Reputation: 15
I am learning how to work with HL7 and IdTCPClient and IdTCPServer. The HL7 message is received and I get an acknowledgement reply from the server only for the first message. But after that, messages are received but no acknowledgement reply is sent. It hangs at AContext.Connection.IOHandler.WriteLn. How can you make the IdTCPServer send acknowledgement replies for every message it receives? Your input is highly appreciated. Here is the server side code onExcute:
procedure THL7DM.IdTCPServer1Execute(AContext: TIdContext);
Function AcknowledgementMessage(HL7_msg: string): string;
var
s: TStrings;
MSA: TMSASegment;
MSH: TMSHSegment;
begin
result := '';
MSH := TMSHSegment.Create(HL7_msg); {HL7 MSH Segment}
MSA := TMSASegment.Create(''); {HL7 MSA Segment}
s := TStringList.Create;
try
MSH.Accept_Acknowledgment_Type_15 := 'AA';
MSA.Acknowledgment_Code_18 := 'AA';
MSH.Sending_Facility_4 := 'AEdge Lab';
MSH.Message_Type_9 := 'ACK';
MSA.Message_Waiting_Number_1827 := DateTimeToStr(now);
s.Text := MSH.ToString + #13 + #10 + 'MSA' + '|' + MSA.ToString;
s.Text := #11 + s.Text + #28 + #13;
result := s.Text;
finally
MSA.Free;
MSH.Free;
s.Free;
end;
end;
var
MsgStrings: TStrings;
s: string;
msg: string;
begin
MsgStrings := TStringList.Create;
s := AContext.Connection.IOHandler.ReadLn(IndyTextEncoding_OSDefault());
try
MsgStrings.Text := StrEscapedToString(s);
Form2.Memo3.Text := TRegEx.Replace(MsgStrings.Text, #11 + '|' + #28, '');
msg := AcknowledgementMessage(Form2.Memo3.Text);
if TRegEx.IsMatch(msg, #28#13) = True then
==> AContext.Connection.IOHandler.WriteLn(StrStringToEscaped(msg),
IndyTextEncoding_OSDefault());
if TRegEx.IsMatch(MsgStrings.Text, #11) = True then
SaveMessageToDatabase(MsgStrings);
finally
MsgStrings.Free;
end;
end;
Here is the Client side sending the message:
procedure TForm2.BitBtn1Click(Sender: TObject);
var
LLine: String;
I: Integer;
s: string;
begin
// wrapping for HL7
LLine := #11 + Memo1.Text + #28 + #13;
if Receiving_System_Accepts_Escaped_Strings then
HL7DM.IdTCPClient1.IOHandler.WriteLn(StrStringToEscaped(LLine),
IndyTextEncoding_OSDefault())
else
HL7DM.IdTCPClient1.IOHandler.WriteLn(LLine, IndyTextEncoding_OSDefault());
if Assigned(ACKReplyHandler) = False then
begin
ACKReplyHandler := TACK_MsgHandlingThread.Create;
//This will handle incoming HL7 ACK replies
end;
end;
The TACK_MsgHandlingThread looks like this:
procedure TACK_MsgHandlingThread.Execute;
begin
HandleACK_Replies;
end;
procedure TACK_MsgHandlingThread.HandleACK_Replies;
var
s: string;
begin
s := (HL7DM.IdTCPClient1.IOHandler.ReadLn(IndyTextEncoding_UTF8));
// ShowMessage(s);
s := StrEscapedToString(s);
s := TRegEx.Replace(s, #11, '');
s := TRegEx.Replace(s, #28#13#10, '');
Form2.Memo4.Clear;
Form2.Memo4.Text := (s);
end;
Upvotes: 1
Views: 525
Reputation: 597971
The only way TIdIOHandler.WriteLn()
can block is if the receiver is not reading data that has been sent, causing its inbound buffer to fill up and stop the sender from sending more data. This is because your TACK_MsgHandlingThread.Execute()
method is reading only 1 incoming reply and then terminating the thread when Execute()
exits, so it stops reading subsequent replies. You need to run the logic of HandleACK_Replies()
in a loop for the lifetime of the thread, calling TIdIOHandler.ReadLn()
for each reply that is sent until the socket is closed and/or the thread is terminated.
Also, IndyTextEncoding_OSDefault
is not portable across machine boundaries. But more importantly, you are using IndyTextEncoding_UTF8
on the client side instead. You need to use the same encoding on both sides or else you risk data loss.
Also, your server is accessing Memo3
, and your client is accessing Memo4
, without syncing with their respective UI threads at all. That is very dangerous. The VCL and FMX frameworks are not thread-safe (most UI frameworks are not), so you MUST synchronize when accessing UI controls from outside of the UI thread.
Upvotes: 1