![]() |
Sending Byte List - Printable Version +- Atozed Forums (https://www.atozed.com/forums) +-- Forum: Indy (https://www.atozed.com/forums/forum-8.html) +--- Forum: Indy General Discussion (https://www.atozed.com/forums/forum-9.html) +--- Thread: Sending Byte List (/thread-600.html) Pages:
1
2
|
Sending Byte List - krshin - 08-22-2018 I need to send an array of bytes to client connected to TIdTCPServer, I try with: Code: procedure TFServer.btnSendClick(Sender: TObject); and I get memory error I try few scenarios with write but same error is here :-( Is there some solution to send bytes, because another site is hardware with firmware who need array of bytes in TCP connection. Thanks for the help and assistance in the advance... RE: Sending Byte List - rlebeau - 08-22-2018 (08-22-2018, 07:19 PM)krshin Wrote: I get memory error I try few scenarios with write but same error is here :-( You need to keep the TIdTCPServer.Contexts list locked while you iterate through it, otherwise clients are free to (dis)connect from/to the server, modifying the contents of the list, while you are accessing it. (08-22-2018, 07:19 PM)krshin Wrote: Is there some solution to send bytes, because another site is hardware with firmware who need array of bytes in TCP connection. To send a byte array, use the TIdIOHandler.Write(TIdBytes) method instead of the TIdIOHandler.WriteLn(string) method. Also, TIdIOHandler.WriteLn() is subject to charset encoding, so you are going to corrupt your bytes sending them as a string anyway. Try this: Code: procedure TFServer.btnSendClick(Sender: TObject); That being said, if your TIdTCPServer.OnExecute event handler also sends any data to clients, then this approach becomes very dangerous, as you risk inter-mixing your bytes with other data that OnExecute may send, corrupting your communications. In which case, the safer approach is to delegate the sending of the bytes to the OnExecute event handler, and let it decide when it is safe to send the bytes. For example: Code: procedure TFServer.bIdTCPServerConnect(AContext: TIdContext); RE: Sending Byte List - krshin - 08-23-2018 Hello rlebeau, Thank you for very detail sample and assistance :-) I have only one problem when I send this array of bytes everything goes fine at the client hardware side, and I need to get some response from that clients over an open channel of communication, so how to get and read this response in this case? Thanks for the help and assistance in the advance... RE: Sending Byte List - rlebeau - 08-23-2018 (08-23-2018, 05:52 AM)krshin Wrote: I have only one problem when I send this array of bytes everything goes fine at the client hardware side, and I need to get some response from that clients over an open channel of communication, so how to get and read this response in this case? If you know a response will arrive quickly (and you are not delegating the send to the OnExecute event, like I described), then you could simply read the response immediately in your btnSendClick() loop using the same IOHandler that you use to send the command, eg: Code: procedure TFServer.btnSendClick(Sender: TObject); However, it is generally best NOT to wait for responses in loops like that, especially loops running in the UI. You should instead read the response in the OnExecute event (especially if you are delegating sends to OnExecute). Now, HOW you read a response from a client's IOHandler depends on the particular format of the response. Since you have not provided any details about the format of the communication protocol you are using, I can't help you with that particular coding. But TIdIOHandler has MANY reading methods available, for things like strings, integers, streams, byte arrays, etc. RE: Sending Byte List - CaptRick - 08-29-2018 OK, this is great. I've been struggling and had arrive at a similar method to send a byte array... thinking I had failed I vasilate between trying Indy & the old reliable Piette's sockets but I want to use Indy- then suddenly my example streamed a response so there is hope. unfortunately I am a struggling newbie to Indy and an intermediate Delphi guy. I created a dynamic array; TxBuffer : TIdBytes; Sized it; SetLength(TxBuffer,6); I loaded it with pointers, I see now I didn't have to do that anyway... I sent the bytes like this but I don't think it's right?: IdTCPClient.IOHandler.Write(@TxBuffer[0],7); I need to receive a byte array. The protocol is a lightweight datalink protocol, framed by simple headers (Start of Frame byte, inverted payload length, payload length, (Payload is a packed record structure of bytes and words, then an array of words) followed by two bytes of the 'Modbus' CRC, which I am calculating fine. Could someone please give me a starting point for the IOHandler to receive a buffer of bytes? It's not big, about 46 bytes right now though it will be bigger. Ultimately it will stream at a fair speed for monitoring the data. RE: Sending Byte List - rlebeau - 08-29-2018 (08-29-2018, 11:43 AM)CaptRick Wrote: I sent the bytes like this but I don't think it's right?: No, it is not. Use this instead, which sends the entire array as-is: Code: IdTCPClient.IOHandler.Write(TxBuffer); Or, you can use this, which lets you specify how many bytes to send (ie, if you had a larger array that you wanted to send a smaller portion of): Code: IdTCPClient.IOHandler.Write(TxBuffer, 7); (08-29-2018, 11:43 AM)CaptRick Wrote: I need to receive a byte array. The protocol is a lightweight datalink protocol, framed by simple headers (Start of Frame byte, inverted payload length, payload length, (Payload is a packed record structure of bytes and words, then an array of words) followed by two bytes of the 'Modbus' CRC, which I am calculating fine. Good. Then you can simply read the header first, then read however many bytes the header says are in the payload. The IOHandler has many methods that would be suitable for this task - ReadByte(), ReadBytes(), ReadInt16/32/64(), etc (and conversely, equivalents for sending data). RE: Sending Byte List - CaptRick - 08-29-2018 Perfect, Thanks rlebeau! IdTCPClient.IOHandler.Write(TxBuffer, 7); worked perfectly, once I fixed the resize to 7 too... and readbytes() worked on the thread component run procedure. I am now able to read all 46 at once, then parse as appropriate. Off and running... RE: Sending Byte List - CaptRick - 08-31-2018 Geez, every time I think I've got it...something doesn't behave like I thought. Indy Sockets so far have already made my life so much cleaner but the closer I get, the further away I seem. I'm starting with the MicroClient example in Delphi 10.2 Tokyo. // File : UClient.pas // Project : MicroServer.dpr // Easy example of TCP Client with indy component : TidTCPSever // // see indy doc: http://www.indyproject.org/sockets/docs/index.en.aspx I've relegated my receive code to the thread onRun event TFClient.IdThreadComponentRun, otherwise I could use it to receive one series at a time, but they prefer firing them at me so I need to receive full buffers of bytes containing strain gauge data in quick intervals. There will be ~175 gauges, currently I'm testing with 11 copies of the same one. If I read 49 bytes at once for my test case it works fine. When I stopped to read the header first last night I found it triggers the onRun event if there is any more data in the buffer, so then I did an experiment reading one byte at a time and displaying it in the TFClient.IdThreadComponentRun onRun event when it fires and sure enough, this will read the entire buffer perfectly. That's cool and could maybe be the way to go, but if I do that I don't know when it's finished and I can process the data. Any way to really know how many bytes have been received in the IOHandler? When I use SizeOf(RxBuffer) it's always 4 so it does a series of 4 till it's exhausted the incoming buffer. So then I thought OK, I just need to read it all. I'm getting garbage in my Uint16's- this could be an endian issue, I'm not certain yet because I don't program full time. Can I use the HostToLittleEndian function from Indy? I have endian changing routines but if it's 'already in there' why do that. When I read it by bytes I get $0B in the first byte (lines 10&11 below) which is 11 sensors if I decode just that byte. Here is the series of bytes I receive currently; 1 - A5 Frame Start 2 - F3 255- Payload length = 243 3 - 0C 12, payload size 4 - A3 WEPACKETID_SENSORRESULTSFIRST_RES 5 - 00 zero 6 - CD 205 7 - 00 Status 8 - 00 Num Te mp sensors 9 - 00 Num Temp sensors 10 - 0B 11, corect Num Strain Sensors 11 - 00 Num Strain Sensors 12 - 00 Temp Sensor index 13 - 00 Temp Sensor index 14 - 00 Temp Sensor Result 15 - 00 Temp Sensor Result 16 - 76 CRC 17 - 7A CRC 18 - A5 Frame start 19 - E4 228 20 - 1B 27 checks out 21 - A4 Sensor results others 22 - 00 zero 23 - CD 205 24 - 00 u16StrainSensorIndex 25 - 00 u16StrainSensorIndex 26 - 73 u16Strain result 27 - 17 28 - 74 u16Strain result 29 - 17 30 - 74 u16Strain result 31 - 17 32 - 74 u16Strain result 33 - 17 34 - 74 u16Strain result 35 - 17 36 - 75 u16Strain result 37 - 17 38 - 75 u16Strain result 39 - 17 40 - 77 u16Strain result 41 - 17 42 - 77 u16Strain result 43 - 17 44 - 77 u16Strain result 45 - 17 46 - 76 u16Strain result 47 - 17 48 - DE CRC 49 - B2 CRC Pretty sure now the garbage part is an endian issue. More later since I have a full time day job too... RE: Sending Byte List - rlebeau - 08-31-2018 (08-31-2018, 10:38 AM)CaptRick Wrote: I've relegated my receive code to the thread onRun event TFClient.IdThreadComponentRun, otherwise I could use it to receive one series at a time, but they prefer firing them at me so I need to receive full buffers of bytes containing strain gauge data in quick intervals. There will be ~175 gauges, currently I'm testing with 11 copies of the same one. If I read 49 bytes at once for my test case it works fine. When I stopped to read the header first last night I found it triggers the onRun event if there is any more data in the buffer, so then I did an experiment reading one byte at a time and displaying it in the TFClient.IdThreadComponentRun onRun event when it fires and sure enough, this will read the entire buffer perfectly. OnRun is a looped event. As soon as it exits, it is triggered again, over and over, for the lifetime of the thread. It is not dependent on socket I/O. (08-31-2018, 10:38 AM)CaptRick Wrote: That's cool and could maybe be the way to go, but if I do that I don't know when it's finished and I can process the data. That is why you should be reading a full message on each triggering of the OnRun event. Read a full header, read its full payload, process, exit. Repeat. (08-31-2018, 10:38 AM)CaptRick Wrote: Any way to really know how many bytes have been received in the IOHandler? Technically yes (see the IOHandler.InputBuffer.Size property), but that is not the way you should be approaching this situation. Your messages have structure to them. Code for that structure. (08-31-2018, 10:38 AM)CaptRick Wrote: When I use SizeOf(RxBuffer) it's always 4 That means your RxBuffer is likely a dynamic array, so it is effectively just a pointer to data stored elsewhere in memory. A pointer has a fixed size (4 bytes when compiling for 32bit). (08-31-2018, 10:38 AM)CaptRick Wrote: I'm getting garbage in my Uint16's- this could be an endian issue, I'm not certain yet Most likely, yes. Most platforms that Delphi supports are little endian systems, but Indy assumes network byte order (big endian) by default, as most network protocols use that endian for cross-platform compatibility. It is a common standard. TIdIOHandler's reading and sending methods for integers have an optional AConvert parameter that is set to True by default, so they perform conversions between network endian and host endian for you. Since you are sending data between TIdTCPServer and TIdTCPClient, things should "just work" out of the box. Unless you are not using TIdIOHandler's integer sending methods, but are instead receiving data from an external source and just forwarding it as-is. In which case, if the UInt16s are using the same endian as the receiver (check the source's documentation), you can set the AConvert parameter to False when reading the UInt16s. (08-31-2018, 10:38 AM)CaptRick Wrote: Can I use the HostToLittleEndian function from Indy? That function is for sending, not for reading. You would have to use LittleEndianToHost() instead. But that is moot if you use the AConvert parameter of TIdIOHandler.ReadUInt16() instead. (08-31-2018, 10:38 AM)CaptRick Wrote: I have endian changing routines but if it's 'already in there' why do that. Exactly, which is why the AConvert parameter exists. (08-31-2018, 10:38 AM)CaptRick Wrote: When I read it by bytes I get $0B in the first byte (lines 10&11 below) which is 11 sensors if I decode just that byte. Bytes 0B 00 is decimal 11 as a (U)Int16 in little endian. On lines 2 and 3, you have "Payload length" and "payload size". Assuming those 2 bytes are actually a single (U)Int16, then bytes F3 0C would be decimal 3315 in little endian. Does that look like a reasonable payload size for your messsaging? RE: Sending Byte List - CaptRick - 08-31-2018 Thanks so much this is so great! 'Looped event' explains a lot... Forgive me for newbie questions, I've excitedly adopted Indy Sockets for a whole 3 days now... What you have said means I am on the right track, and very nearly there already. I coded a read method to read the number of sensors get all the data, no matter how big, including the last CRC. The thing is the inverted payloads and payload sizes only refer to the two records/structures in packetized headers, one is only two bytes the other only 5 with most of the variables just one byte, using the structure to decode it seems irrelevant to me. No, 3315 is not reasonable, the payload bytes are separate bytes with the inverted one being 255 - payloadsize, the other a byte with the size. I will see about the IOHandler.InputBuffer.Size property, though I have the code written it would seem easier to read the buffer in it's entirety then deal with the byte array. the AConvert parameter, well that's brilliant! I've read about so many things that would make life really easy for me. Normally we use Records and pointers to records as a 'magic decoder ring' for the buffer, but on dedicated LAN's we use UDP, not TCP and the records are much more complex usually. I had to deal with endian conversion every time because we were dealing with Motorola processors on the other end, but it was years ago. I'm not actually using the server of this example but receiving from a device. I have really hit the timing hard, but it's still possible I'm reading the wrong two bytes for the number of sensors, which is the payload that counts. they occur as an array immediately following the second structure. I was playing with AConvert suspecting that might be an endian swapping deal by setting it to true, maybe I made it Big endian and gave myself the headache... I thought I undid it later, unless it's set to True by default since I never set it explicitly to False. If that is true I can fix it in 10 minutes or less! I also tried to buy the E-book on Indy, but could not figure out how to pay for and download it. |