Dear folks The following code is supposed to send a text string to a client that is listening on a set port and ip address. The connection works fine. The problem is that nothing seems to be sent. {code} function TFCSelectForm.SendNitroMessage(NitroRequest: String): String; var sStrm: TMemoryStream; begin try sStrm := TMemoryStream.Create; try sStrm.WriteBuffer(Pointer(NitroRequest)^,Length(NitroRequest)); // sStrm shows as () in the debugger IdTCPClient1.IOHandler.Write(sStrm, 0, False); sStrm.Position := 0; finally sStrm.Free; end; except DpsTimer.Enabled := False; Label1.Caption := 'Paymark is offline.'; end; end; NitroRequest certainly has data in it. But it appears to "disappear" when it is added to the stream. In that the sStrm shows as '()' when I check it using the debugger. I'm blowed if I can see what I'm doing wrong. Just one other thing - when I check the Position value it shows as 13. Which is the length of the string that is supposed to be sent. So there must be something there? Thanks Alan Edited by: Alan Jeffery on Aug 21, 2013 6:54 PM Edited by: Alan Jeffery on Aug 21, 2013 7:10 PM
![]() |
0 |
![]() |
Alan wrote: > sStrm.WriteBuffer(Pointer(NitroRequest)^,Length(NitroRequest)); In XE2, Delphi's String type is Unicode, but your code is assuming it is Ansi instead (WriteBuffer() operates on bytes, not characters). If your receiver can receive Unicode, then do this instead: {code:delphi} sStrm.WriteBuffer(Pointer(NitroRequest)^, Length(NitroRequest) * SizeOf(Char)); {code} However, if your receiver is expecting Ansi instead (which is most likely), then you need to convert the Unicode data to Ansi. You can either: 1) change your NitroRequest parameter to an AnsiString and let the RTL handle the conversion for you when you pass a Unicode string to your function: {code:delphi} function TFCSelectForm.SendNitroMessage(NitroRequest: AnsiString): String; {code} 2) use a TStringStream instead of a TMemoryStream, and let the RTL convert the Unicode string to bytes for you: {code:delphi} function TFCSelectForm.SendNitroMessage(NitroRequest: String): String; var sStrm: TStringStream; begin try sStrm := TStringStream.Create(NitroRequest, TEncoding.Default); // or any TEncoding you want try IdTCPClient1.IOHandler.Write(sStrm, 0, False); finally sStrm.Free; end; except DpsTimer.Enabled := False; Label1.Caption := 'Paymark is offline.'; end; end; {code} 3) use Indy's WriteStringToStream() function and let Indy convert the Unicode string to bytes for you: {code:delphi} function TFCSelectForm.SendNitroMessage(NitroRequest: String): String; var sStrm: TMemoryStream; begin try sStrm := TMemoryStream.Create; try WriteStringToStream(sStrm, NitroRequest, TIdTextEncoding.Default); // or any TIdTextEncoding you want IdTCPClient1.IOHandler.Write(sStrm, 0, False); finally sStrm.Free; end; except DpsTimer.Enabled := False; Label1.Caption := 'Paymark is offline.'; end; end; {code} 4) don't use a TStream at all. The IOHandler can convert and write the Unicode string directly: {code:delphi} function TFCSelectForm.SendNitroMessage(NitroRequest: String): String; begin try IdTCPClient1.IOHandler.Write(NitroRequest, TIdTextEncoding.Default); // or any TIdTextEncoding you want except DpsTimer.Enabled := False; Label1.Caption := 'Paymark is offline.'; end; end; {code} -- Remy Lebeau (TeamB)
![]() |
0 |
![]() |
> {quote:title=Remy Lebeau (TeamB) wrote:}{quote} > 4) don't use a TStream at all. The IOHandler can convert and write the Unicode > string directly: > > {code:delphi} > function TFCSelectForm.SendNitroMessage(NitroRequest: String): String; > begin > try > IdTCPClient1.IOHandler.Write(NitroRequest, TIdTextEncoding.Default); > // or any TIdTextEncoding you want > except > DpsTimer.Enabled := False; > Label1.Caption := 'Paymark is offline.'; > end; > end; > {code} > > -- Remy Thanks for that. It worked. The problem now is that the response isn't what I expected: Here is the code waiting for the response: {code} const cStart = '#'; var I : Integer; sStr : String; begin sStr := ''; Result := ''; I := 0; try IdTCPClient1.IOHandler.WaitFor(cStart, True, False, Indy8BitEncoding,5000); except // DPSTimer.Enabled := False; Exit; end; repeat // I gets the value of 'S002' on the first respinse from the client, instead of '0060' I := StrToInt(IdTCPClient1.IOHandler.ReadString(4)); IdTCPClient1.IOHandler.CheckForDisconnect(True, True); IdTCPClient1.IOHandler.CheckForDataOnSource(IdTimeoutDefault); until False; if I <> 0 then begin sStr := IdTCPClient1.IOHandler.ReadString(I, Indy8BitEncoding); if Length(sStr) <> 0 then begin // just in case the response differed - clear the buffer // not sure this is a good idea. //IdTCPClient1.IOHandler.DiscardAll; Result := sStr; end; end; {code} Here is the response - from a test application that came with the SDK; [09:20:33:348]Send: #0013G0000000 [09:20:33:569]Recv: #0060S00220PROCESSING NOW 000000000 [09:20:35:944]Recv: #0060S00220ACCEPTED 000001006 [09:20:35:964]Recv: #00073L [09:20:35:965]Send: #00073 The ReadString(4) should get the '0060'. Instead it's getting the 'S002'. I don't understand why that could happen. In practice the software doesn't need to do anything with the "PROCESSING NOW" and "ACCEPTED" responses. It does need to respond to the following: [09:20:36:066]Recv: #02273R*-----Training-----* In Training TERMINAL 00901350 TIME 28FEB 20:58 TRAN 000019 MERCHANT LOGON ACCEPTED *----Test Only-----* [09:20:36:070]Send: #00073 And then wait for the following: [09:20:40:593]Recv: #0090G2100ACCEPTED 0090135010009013050 280270205831000019I5100 NITRO 0102000 That one says the process was successful. I only need to deal with the stuff up to the end of 'ACCEPTED'. But, as I said, I'm stymied with the ReadString(4) getting the wrong values. I can't figure out how to work around that because it shouldn't happen. As far as I can figure it. Thanks Alan > Remy Lebeau (TeamB) Edited by: Alan Jeffery on Aug 22, 2013 2:31 PM Edited by: Alan Jeffery on Aug 22, 2013 2:32 PM Edited by: Alan Jeffery on Aug 22, 2013 2:33 PM Edited by: Alan Jeffery on Aug 22, 2013 2:34 PM
![]() |
0 |
![]() |
Alan wrote: > I gets the value of 'S002' on the first respinse from the client, instead of '0060' You have altered the code I gave you earlier. You changed it to introduce an endless repeat loop that reads 4 bytes at a time until a socket disconnect/error occurs. Why? On the first iteration of the loop, it would read '0060', then the next iteration would read 'S002', then '20PR', then 'OCES', then 'SING', then ' NOW', then ' 000', then '0000', then '00#0', and so on. See the problem? All you need to do is wait for the start, determine the length, read that many bytes. Done. Do not loop. Do not call CheckForDisconnect(). Do not call CheckForDataOnSource(). Do not call DiscardAll(). Given the new samples you have shown, the first 4 bytes following the '#' are NOT consistently a message length (unless you are truncating the data you are logging - please don't do that!). That is contradicting what you claimed earlier: #0013G0000000 Message *is* 13 characters long... #0060S00220PROCESSING NOW 000000000 Message *is not* 60 characters long! #0060S00220ACCEPTED 000001006 Message *is not* 60 characters long! #00073L Message *is* 13 characters long... #00073 Message *is not* 7 characters long! #02273R*-----Training-----* In Training TERMINAL 00901350 TIME 28FEB 20:58 TRAN 000019 MERCHANT LOGON ACCEPTED *----Test Only-----* Message *is not* 227 characters long! #00073 Message *is not* 7 characters long! #0090G2100ACCEPTED 0090135010009013050 280270205831000019I5100 NITRO 0102000 Message *is not* 90 characters long! So now we are back to square one again - DISCOVERING THE TRUE FORMAT OF THE MESSAGES!!! It should not be taking THIS LONG to find out what the actual format of the messages is, either from documentation or asking the vendor directly. I did not receive any private message from you earlier, so please post the documentation to the Attachments forum. -- Remy Lebeau (TeamB)
![]() |
0 |
![]() |
> {quote:title=Remy Lebeau (TeamB) wrote:}{quote} > Alan wrote: > > > I gets the value of 'S002' on the first respinse from the client, instead > of '0060' > > You have altered the code I gave you earlier. You changed it to introduce > an endless repeat loop that reads 4 bytes at a time until a socket disconnect/error > occurs. Why? On the first iteration of the loop, it would read '0060', > then the next iteration would read 'S002', then '20PR', then 'OCES', then > 'SING', then ' NOW', then ' 000', then '0000', then '00#0', and so on. See > the problem? > Remy I just spotted that. And have removed the loop. Still not working correctly. > All you need to do is wait for the start, determine the length, read that > many bytes. Done. Do not loop. Do not call CheckForDisconnect(). Do not > call CheckForDataOnSource(). Do not call DiscardAll(). > > Given the new samples you have shown, the first 4 bytes following the '#' > are NOT consistently a message length (unless you are truncating the data > you are logging - please don't do that!). That is contradicting what you > claimed earlier: > > #0013G0000000 > > Message *is* 13 characters long... > > #0060S00220PROCESSING NOW 000000000 > > Message *is not* 60 characters long! > > #0060S00220ACCEPTED 000001006 > > Message *is not* 60 characters long! > > #00073L > > Message *is* 13 characters long... > > #00073 > > Message *is not* 7 characters long! > > #02273R*-----Training-----* > In Training > TERMINAL 00901350 > TIME 28FEB 20:58 > TRAN 000019 > MERCHANT LOGON > ACCEPTED > > *----Test Only-----* > > Message *is not* 227 characters long! > > #00073 > > Message *is not* 7 characters long! > > #0090G2100ACCEPTED 0090135010009013050 280270205831000019I5100 NITRO 0102000 > > Message *is not* 90 characters long! > > So now we are back to square one again - DISCOVERING THE TRUE FORMAT OF THE > MESSAGES!!! It should not be taking THIS LONG to find out what the actual > format of the messages is, either from documentation or asking the vendor > directly. > Gack. Sorry about that. The cutnpaste did truncate. I didn't see it. The responses are as long as the "length" field says. I have been dealing directly with the vendor's developer's. It's been like getting blood out of a stone. And the documentation is wrong in certain places. For example it doesn't mention those 'S' responses. I have a more recent copy. I'll attach that. The problem I'm having now is that the 'S' responses are being dealt with but one response is simply getting the wrong string. In fact it's getting the beginning of the next message. The response is the #00073L message above. Which I have to respond to with a #00073 message to trigger a receipt. But the client isn't waiting for that. It's immediately giving me the final ACCEPTED message. Although the 7 is being read correctly the ReadString is getting '3L#0090'. If I try I - 4 the connection hangs - which isn't a surprise. I can't find an "Attachments" forum. Where is it? Ok found it. Thread created. With latest documentation. It still doesn't show the format of display messages. However the vendor's folks say those messages can be ignored. Which is what I've set things up to do. In that the procedure that works out what response is required ignores those messages with with 'S' in them. Thanks Alan > I did not receive any private message from you earlier, so please post the > documentation to the Attachments forum. > > -- > Remy Lebeau (TeamB) Edited by: Alan Jeffery on Aug 22, 2013 4:56 PM Edited by: Alan Jeffery on Aug 22, 2013 4:57 PM Edited by: Alan Jeffery on Aug 22, 2013 4:57 PM Edited by: Alan Jeffery on Aug 22, 2013 5:03 PM
![]() |
0 |
![]() |
Alan wrote: > Gack. Sorry about that. The cutnpaste did truncate. I didn't see > it. The responses are as long as the "length" field says. OK, then that does match what is in the documentation (thanks for that), and the documentation does confirm that the length value includes the '#' and length fields, so calling ReadString(MsgLen-5) is the corrct way to go after calling ReadString(4) to get the message length. > I have been dealing directly with the vendor's developer's. It's been > like getting blood out of a stone. And the documentation is wrong in > certain places. That is fine, as long as the information about the message length is accurate. That is the most important thing right now. > For example it doesn't mention those 'S' responses. Actually, it does. They are Display events. See section 3.12 of the documentation. So, given this sequence: [09:20:33:348]Send: #0013G0000000 [09:20:33:569]Recv: #0060S00220PROCESSING NOW 000000000 [09:20:35:944]Recv: #0060S00220ACCEPTED 000001006 [09:20:35:964]Recv: #00073L [09:20:35:965]Send: #00073 You requested a Logon (but I don't see a cooresponding response, unless you omitted it), then you received 2 Display events, then you received a Receipt event saying "Logon receipt about to be printed", which you then replied to. And given this sequence: [09:20:36:066]Recv: #02273R*-----Training-----* In Training TERMINAL 00901350 TIME 28FEB 20:58 TRAN 000019 MERCHANT LOGON ACCEPTED *----Test Only-----* [09:20:36:070]Send: #00073 [09:20:40:593]Recv: #0090G2100ACCEPTED 0090135010009013050 280270205831000019I5100 NITRO 0102000 You received a Receipt event saying "Receipt present for POS to print" with additional data, which you replied to, and then you received a Logon response. > The problem I'm having now is that the 'S' responses are being dealt > with but one response is simply getting the wrong string. In fact > it's getting the beginning of the next message. <snip> > Although the 7 is being read correctly the ReadString is getting '3L#0090'. That is because you are passing the full message length (7) to ReadString() instead of subtracting 5 for the bytes you have already read. 1 byte for the '#', 4 bytes for the length field. That goes back to a comment in my earlier example: {code:delphi} Result := IdTCPClient1.IOHandler.ReadString(MsgLen-5, Indy8BitEncoding); // assuming the length value includes the starting '#' and 4 byte of the length... {code} Which the documentation confirms is true: {quote} Length of message **including start and length fields** {quote} > If I try I - 4 the connection hangs - which isn't a surprise. Use I - 5 instead. > It still doesn't show the format of display messages. Yes, it does, in section 3.12. [09:20:33:569]Recv: #0060S00220PROCESSING NOW 000000000 Breaks done to this (assuming your newsreader truncated white space when posting): Start = '#' Length = '0060' Command Code = 'S' SubCode = '0' Num Lines = '02' Line Length = '20' Text = 'PROCESSING NOW' Cancel = '0' Accept = '0' Decline = '0' Authorise = '0' Input Data = '0' OK = '0' Reserved = '0' Reserved = '0' Graphic Code = '0' And: [09:20:35:944]Recv: #0060S00220ACCEPTED 000001006 Breaks done to this: Start = '#' Length = '0060' Command Code = 'S' SubCode = '0' Num Lines = '02' Line Length = '20' Text = 'ACCEPTED' Cancel = '0' Accept = '0' Decline = '0' Authorise = '0' Input Data = '0' OK = '1' Reserved = '0' Reserved = '0' Graphic Code = '6' -- Remy Lebeau (TeamB)
![]() |
0 |
![]() |
> {quote:title=Remy Lebeau (TeamB) wrote:}{quote} > Alan wrote: > > > Gack. Sorry about that. The cutnpaste did truncate. I didn't see > > it. The responses are as long as the "length" field says. > > OK, then that does match what is in the documentation (thanks for that), > and the documentation does confirm that the length value includes the '#' > and length fields, so calling ReadString(MsgLen-5) is the corrct way to go > after calling ReadString(4) to get the message length. > > > I have been dealing directly with the vendor's developer's. It's been > > like getting blood out of a stone. And the documentation is wrong in > > certain places. > > That is fine, as long as the information about the message length is accurate. > That is the most important thing right now. > > > For example it doesn't mention those 'S' responses. > > Actually, it does. They are Display events. See section 3.12 of the documentation. Yes, I see that. The previous version didn't. Didn't mention them, at all. > > So, given this sequence: > > [09:20:33:348]Send: #0013G0000000 > [09:20:33:569]Recv: #0060S00220PROCESSING NOW 000000000 > [09:20:35:944]Recv: #0060S00220ACCEPTED 000001006 > [09:20:35:964]Recv: #00073L > [09:20:35:965]Send: #00073 > > You requested a Logon (but I don't see a cooresponding response, unless you > omitted it), then you received 2 Display events, then you received a Receipt > event saying "Logon receipt about to be printed", which you then replied to. > > And given this sequence: > > [09:20:36:066]Recv: #02273R*-----Training-----* > In Training > TERMINAL 00901350 > TIME 28FEB 20:58 > TRAN 000019 > MERCHANT LOGON > ACCEPTED > > *----Test Only-----* > > [09:20:36:070]Send: #00073 > [09:20:40:593]Recv: #0090G2100ACCEPTED 0090135010009013050 280270205831000019I5100 > NITRO 0102000 > > You received a Receipt event saying "Receipt present for POS to print" with > additional data, which you replied to, and then you received a Logon response. > > > The problem I'm having now is that the 'S' responses are being dealt > > with but one response is simply getting the wrong string. In fact > > it's getting the beginning of the next message. > <snip> > > Although the 7 is being read correctly the ReadString is getting '3L#0090'. > > That is because you are passing the full message length (7) to ReadString() > instead of subtracting 5 for the bytes you have already read. 1 byte for > the '#', 4 bytes for the length field. That goes back to a comment in my > earlier example: > > {code:delphi} > Result := IdTCPClient1.IOHandler.ReadString(MsgLen-5, Indy8BitEncoding); > // assuming the length value includes the starting '#' and 4 byte of the > length... > {code} > > Which the documentation confirms is true: > > {quote} > Length of message **including start and length fields** > {quote} > > > If I try I - 4 the connection hangs - which isn't a surprise. > > Use I - 5 instead. Yep, that fixed it. Sometimes I wonder about my math. I had forgotten the '#' had already gone. > > > It still doesn't show the format of display messages. > > Yes, it does, in section 3.12. > > [09:20:33:569]Recv: #0060S00220PROCESSING NOW 000000000 > > Breaks done to this (assuming your newsreader truncated white space when > posting): > > Start = '#' > Length = '0060' > Command Code = 'S' > SubCode = '0' > Num Lines = '02' > Line Length = '20' > Text = 'PROCESSING NOW' > Cancel = '0' > Accept = '0' > Decline = '0' > Authorise = '0' > Input Data = '0' > OK = '0' > Reserved = '0' > Reserved = '0' > Graphic Code = '0' > > And: > > [09:20:35:944]Recv: #0060S00220ACCEPTED 000001006 > > Breaks done to this: > > Start = '#' > Length = '0060' > Command Code = 'S' > SubCode = '0' > Num Lines = '02' > Line Length = '20' > Text = 'ACCEPTED' > Cancel = '0' > Accept = '0' > Decline = '0' > Authorise = '0' > Input Data = '0' > OK = '1' > Reserved = '0' > Reserved = '0' > Graphic Code = '6' > > -- Yes, that is correct. As I said, the display messages can be ignored. But I do need to trap the '3' messages and they require an acknowledgement as '#00073'. Now I have another strange thing. I'm sending a "Set dialog" message. This configures the dialog form of the Nitro client. It sends this message: #00612500 0 Center 1Dialog Title And gets back this one: #00082 1 just about immediately. But the IOHandler.WaitFor is timing out. I thought it was the data I was sending the client. But it is correct. I disabled the timer when the '00' 'ACCEPTED' message was received. And enabled it again when the Set Dialog message was sent. Timeout is set at 5000 seeing as the responses from the Nitro client can be kind of slow. Alan > Remy Lebeau (TeamB)
![]() |
0 |
![]() |
Alan wrote: > It sends this message: #00612500 0 Center 1Dialog Title > > And gets back this one: #00082 1 just about immediately. So far so good. > But the IOHandler.WaitFor is timing out. I thought it was the data I > was sending the client. But it is correct. That means either the '#' of the response is not appearing in the InputBuffer to begin with, or the reading of the previous message likely got it by accident. Either way seems odd, assuming no other thread is reading from the same socket and thus grabbing data that your reading code is expecting. -- Remy Lebeau (TeamB)
![]() |
0 |
![]() |
Alan wrote: > It sends this message: #00612500 0 Center 1Dialog Title > > And gets back this one: #00082 1 just about immediately. So far so good. > But the IOHandler.WaitFor is timing out. I thought it was the data I > was sending the client. But it is correct. That means either the '#' of the response is not appearing in the InputBuffer to begin with, or the reading of the previous message likely got it by accident. Either way seems odd, assuming no other thread is reading from the same socket and thus grabbing data that your reading code is expecting. -- Remy Lebeau (TeamB)
![]() |
0 |
![]() |