From: Proctor, K. <Kel...@al...> - 2001-10-26 02:27:22
|
Dieter, I have to disagree with a few points you have raised below. I have also remembered another problem that we would face with the RTU transport mode. Cheers, Kelvin > -----Original Message----- > From: Dieter Wimberger [SMTP:wi...@oe...] > Sent: Thursday, October 25, 2001 20:41 > To: jmo...@li... > Subject: [jModbus-devel] Issues discussion continued > > Hi Kelvin, > > First of all thanks regarding the exam, that worked out nicely. I do not > have much > left so it is not such an issue :) > > Now to our issues.... > > Regarding the : and CRLF characters (let's call those tokens FRAME_START > and FRAME_END), > it is very easy to pass them on. In Java a byte is -128 to +127 this is > the main reason > for the byte reading methods to return int (which is actually -2147483648 > to 2147483647). > According to my calculation I would say we have enough encoding space.... > ;) > [Proctor, Kelvin] I believe you have interpreted the way the java API is written incorrectly. I believe the reason the read() method returns an int and not a byte is for efficiency reasons. The JVM spec states that the minimum memory allocation is 4 bytes. Thus a byte is really stored as 4 bytes in the JVM. If you want to do any sort of arithmetic the byte must be converted to an int, the arithmetic takes place and is then converted back to a byte. All this is done within the JVM so we don't see it but it does make the process less efficient. This is why on a platform like the TINI is it recommended to use ints everywhere to save on conversion cycles. I am assuming that you would simply put a null implementation in the block read functions for the ASCIIInputStream (as they use byte arrays and the above method would not be an option). This raises the point that if we are not going to implement these function in a meaningful manner then should we really be extending FilterInputStream. The method you have suggested would force *all* the values to be bye-banged through the I/O stream. I may have made my definition of this unclear the other day. Looping through an array with a for loop etc... is fine, it's when you read information from streams 1 byte at a time that this becomes a problem. The method you have proposed would force us to always do this. For some good information on why byte-banging of streams is *really* bad I recommend having a look at the TINI book by Don Loomis (http://www.ibutton.com/TINI/tinispec.pdf) in section 11.2.1 on page 227. This shows an example where byte-banging can be so bad that TINI will only do about 100 bytes per second and the same program on a PIII Win2K machine only gets 1000 bytes per second. All of section 11.2 on efficient I/O is worth a read. I know I must sound like I'm harping on this point a bit but I would really like the library to still run on a TINI at acceptable speed if possible. I found in my own testing that even printing about 15 System.out.println() statements per packet in debug mode was enough that the TINI could not handle the rate at which Citect wanted to send requests. (Although Citect has a relatively fast sample rate by default). > The LRC Value could be easily extracted at the transport layer, and > checked too, > whether byte banging or not. I also do think that the byte banging is not > such an > issue in our case, it would be interesting to see whether the performance > and use > of resources differ by any means. > > Also note that the message does not perform any kind of computations > despite extracting > the values. It would throw an Exception if something is malformed and this > exception would > end in the transport. > In fact the message instance is nothing else then a Data Model for the > message, > and whether you discard that one or the byte[] is also more a > philosophical discussion :) > The checks would be still performed by the transport.... > [Proctor, Kelvin] I have also remembered a very important point about the RTU mode that is probably significant at this point. In RTU mode the frame separator is a mark of at least 3.5 character times. This is quite a difficult thing to capture properly under the javax.comm API. David MacMahon and I had a discussion on the TINI list some months back about a way to do this but it will rely heavily on using the timeout and receive thresholds and a few other good tricks. This means that (at least on one layer) that the whole frame must be read as a contiguous block The serial of messages that relate to this discussion are below (in order hopefully) http://lists.dalsemi.com/maillists/tini/2001-August/016297.html http://lists.dalsemi.com/maillists/tini/2001-August/016298.html http://lists.dalsemi.com/maillists/tini/2001-August/016303.html http://lists.dalsemi.com/maillists/tini/2001-August/016301.html http://lists.dalsemi.com/maillists/tini/2001-August/016309.html I still think I would probably be happier with a system that looked something like public class ModbusConnection { private ModbusTransport transport private ModbusMessage send_msg; private ModbusMessage receive_msg; private ByteArrayInputStream in; private ByteArrayOutputStream out; public ModbusConnection(ModbusTransport transport) { this.transport = transport; send_msg = new ModbusMessage(); receive_buff = new byte[Modbus.MAX_MESSAGE_SIZE]; receive_msg = new ModbusMessage(receive_buff, 0); in = new ByteArrayInputStream(receive_buff); out = new ByteArrayOutputStream(Modbus.MAX_MESSAGE_SIZE); } public InputStream getInputStream() { return in; } public OutputStream getOutputStream() { return out; } public int getTransactionID() { return receive_msg.getTransactionID(); } public void setTransactionID(int ID) { send_msg.setTransactionID(ID); } // Send the message I have just written down the output stream and // reset ready for the next message public boolen sendMessage() { boolen success; byte[] buffer = out.toByteArray(); send_msg.setBuffer(buffer, buffer.length) success = transport.sendFrame(send_msg); out.reset(); return success; } // Move onto the next receive message public boolean nextMessage() { boolen success; success = transport.receiveFrame(receive_msg); is.reset(); return success; } } public interface ModbusTransport { // Send a frame and report if it worked OK public boolean sendFrame(ModbusMessage msg); // Receive a frame and report if it worked OK public boolean receiveFrame(ModbusMessage msg); } public class ModbusMessage { private byte[] buff; private int length; priavte int transactionID ModbusMessage(byte[] buff, int length) { this.buff = buff; this.length = length; transactionID = 0; } ModbusMessage() { buff = new byte[Modbus.MAX_MESSAGE_SIZE]; int length = 0; transactionID = 0; } public byte[] getBuffer() { return buff; } public void setBuffer(byte[] buff, int length) { this.buff = buff; this.length = length; } // standard get/set for length // standard get/set for transactionID } Much of the code above would be called different names as not to collide with the class you have already created and in some places (such as the ModbusConnection class) it could be integrated into the classes you have created. The system I have proposed above gives you higher level code access to and input stream and an output steam to play with (within the confines of a single frame) so all the function code you have written will work (almost) directly. The above code allows all the transport classes to perform all I/O in terms of block transfer function wherever possible. As the transport can also see the whole frame it can do the checksum checking. The TCP transport would only need to perform 2 read operations per frame. (one to read the fixed header and a second to read the body). The ASCII transport could use a PushbackInputStream to try and read the maximum message size and than scan along till it found the delimeters and push back however many bytes it overread. (This is the type of purpose this class was designed for.) The RTU transport can set the timeout and receive thresholds to appropriate levels and a single read should read a whole frame in one hit. (This idea that David and I came up with is yet to be proven but I'll try it soon.). This is also extra important for sending RTU frames as the whole message *must* be written in a single block read operation so that there won't be any gaps between characters, as that would cause a end of frame mark. > I also agree with you that the way you call things make a change. I > introduced this concept > [Transaction] because I understood from the context of the specs for > ModbusTCP that there > are always sequences of request-response which are tightly coupled. As I > am also doing work with > databases and other data stores, I borrowed the vocabulary from there, as > I thought it > would be a common nomer :) > However, probably it is too much master(client)/ModbusTCP centric....I > don't know. > Would you suggest an alternative name? > [Proctor, Kelvin] I can appreciate that. The best way for a project to get stuffed up is if the requirements change half way. All the little, basic assumptions you made that influence your design suddenly become wrong. > I guess this sheds another light on the proposal you made too. > I will try to make up the Stream to show how it is meant, and probably I > manage to > prepare an interaction diagram to show what exactly happens when we are > reading a > message :) > This could maybe show the knots in our two approaches and indicate a way > to open those. > > Regards, > Dieter > > _______________________________________________ > jModbus-devel mailing list > jMo...@li... > https://lists.sourceforge.net/lists/listinfo/jmodbus-devel Alcoa World Alumina Australia is a trading name of Alcoa of Australia Limited, ACN 004 879 298 |