2016-04-23 6 views
1

Delphi XE3, Indy 10.5.9.0Delphi Indy IdTCPServer und IdTCPClient. Lesen und Schreiben von Steuerzeichen und Text

Ich erstelle eine Schnittstelle zwischen einem Computer und einem Instrument. Das Gerät verwendet das ASTM-Protokoll.

Ich habe erfolgreich textbasierte Nachrichten zwischen dem Server und dem Client hin und her gesendet. Ich war in der Lage, Steuerzeichen an den Server zu senden und diese zu lesen. Was ich nach 3 Tagen der Suche nicht herausgefunden habe, ist das Schreiben und Lesen von Nachrichten, die eine Mischung aus Steuerzeichen und Text enthalten.

Ich sende ASTM-Protokoll Nachrichten, die Steuerzeichen und Text wie die folgende Zeile erfordern. Alles in spitzen Klammern sind Steuerzeichen. Wenn ich die Nachricht schreibe, stoße ich nicht auf Probleme. Wenn ich es lese, werde ich sowohl Text- als auch Kontrollzeichen erhalten. Mein Code unten ist, wie ich die Steuerzeichen lese. Wie kann ich feststellen, wenn ich das Zeichen erhalte, ob es sich um ein Steuerzeichen handelt und ob es sich um Text in derselben Zeichenfolge aus Steuerzeichen und Textzeichen handelt? Danke an Remy Lebeau und seinen Beiträgen auf dieser Seite, um mich dahin zu bringen, wo ich bin. Er sprach darüber, wie man Puffer verwendet, aber ich konnte nicht sagen, wie man einen Puffer liest, der Steuerzeichen und Textzeichen enthält.

<STX>3O|1|G-13-00017||^^^HPV|R||||||N||||||||||||||O<CR><ETX>D3<CR><LF> 

Ich habe hinzugefügt, um den folgenden Code zu meinen Server-Komponenten onConnect Ereignis, das sollte zu mir erlaubt, Steuerzeichen senden ...

... 
AContext.Connection.IOHandler.DefStringEncoding := TIdTextEncoding.UTF8; 
... 

Mein Server OnExecute Ereignis ...

procedure TTasksForm.IdTCPServer1Execute(AContext: TIdContext); 
var 
    lastline : WideString; 
    lastcmd : WideString ; 
    lastbyte : Byte ; 
begin 

    ServerTrafficMemo.Lines.Add('OnExecute') ; 

    lastline := '' ; 
    lastcmd := '' ; 

    lastbyte := (AContext.Connection.IOHandler.ReadByte) ; 

    if lastbyte = Byte(5) then 
    begin 

    lastcmd := '<ENQ>' ; 

    ServerTrafficMemo.Lines.Add(lastcmd) ; 

    AContext.Connection.IOHandler.WriteLn(lastcmd + ' received') ; 

    end; 

end; 

Antwort

1

Die einzigen Steuerzeichen vorhanden sind STX und ETX, und sie sind beide < 32, so ASCII und UTF-8 werden beide behandeln sie gut. Oder Sie können Indys eigene integrierte 8-Bit-Codierung stattdessen verwenden.

Für diese Art von Daten gibt es verschiedene verschiedene Möglichkeiten, es mit Indy zu lesen. Da der Großteil der Daten textlich ist und die Steuerzeichen nur als Rahmenbegrenzer verwendet werden, wäre der einfachste Weg, IOHandler.ReadLn() oder IOHandler.WaitFor() mit expliziten Abschlusszeichen zu verwenden.

Natürlich gibt es auch andere Optionen, wie das Lesen von Bytes aus dem IOHandler.InputBuffer direkt (was ich denke, ist in dieser Situation Overkill), mit der InputBuffer.IndexOf() Methode zu wissen, wie viele Bytes zu lesen.

Außerdem ist TIdTCPServer eine Multithread-Komponente, deren Ereignisse in Worker-Threads ausgelöst werden, aber Ihr Code greift direkt auf die Benutzeroberfläche zu, die nicht Thread-sicher ist. Sie MÜSSEN mit dem UI-Thread synchronisieren.

Und Sie sollten auch nicht WideString sein. Verwenden Sie stattdessen (Unicode)String.

versuchen, etwas wie folgt aus:

procedure TTasksForm.IdTCPServer1Connect (AContext: TIdContext); 
begin AContext.Connection.IOHandler.DefStringEncoding := Indy8BitEncoding; 
end; 

procedure TTasksForm.IdTCPServer1Execute(AContext: TIdContext); 
var 
    lastline : string; 
    lastcmd : string ; 
    lastbyte : Byte ; 
begin 
    TThread.Synchronize(nil, 
    procedure 
    begin 
     ServerTrafficMemo.Lines.Add('OnExecute') ; 
    end 
); 

    lastbyte := (AContext.Connection.IOHandler.ReadByte); 

    if lastbyte = $5 then 
    begin 
    lastcmd := '<ENQ>' ; 
    TThread.Synchronize(nil, 
     procedure 
     begin 
     ServerTrafficMemo.Lines.Add(lastcmd) ; 
     end 
    ); 
    end 
    else if lastbyte = $2 then 
    begin 
    lastline := #2 + AContext.Connection.IOHandler.ReadLn(#3) + #3; 
    lastline := lastline + AContext.Connection.IOHandler.ReadLn(#13#10) + #13#10; 
    { or: 
    lastline := #2 + AContext.Connection.IOHandler.WaitFor(#3, true, true); 
    lastline := lastline + AContext.Connection.IOHandler.WaitFor(#13#10, true, true); 
    } 
    lastcmd := '<STX>' ; 
    TThread.Synchronize(nil, 
     procedure 
     begin 
     ServerTrafficMemo.Lines.Add(lastcmd) ; 
     end 
    ); 
    end; 
    AContext.Connection.IOHandler.WriteLn(lastcmd + ' received') ; 
end; 
+0

Wow! Ich versuche all die guten Dinge in diesem Beitrag zu verdauen! Warum verwenden Sie $ in 'lastbyte =' Zeilen und # in ReadLn Zeilen? –

+0

'lastbyte' ist ein Byte. '$ 5' ist ein numerischer Wert, äquivalent zu' Byte (5) ', einfach sauberer. 'ReadLn()' und 'WaitFor()' operieren auf Strings, nicht auf Bytes. '# 2' und' # 3' sind Zeichenwerte ('Char (2)' und 'Char (3)'). –

+0

Gibt es eine Möglichkeit, eine Lese- oder Lesezeit "auszulöschen"? –

0

Ich konnte nicht sagen, wie man einen Puffer liest, der Steuerzeichen und Textzeichen

enthielt

Dieses Protokoll verwendet zweifellos ASCII-Zeichenfolgen. Alle Zeichen unter Dezimal 32 sind Steuerzeichen. Diese 32 und höher werden Datenzeichen sein. Siehe http://ascii-table.com/ascii.php

Der Umgang damit als Bytes funktioniert gut. Sie können auch ansistring verwenden, also ASCII plus die oberen 127 Zeichen. In dieser Situation würde ich UTF (any) vermeiden und bei jedem Byte oder anisstring bleiben. Sie müssen die Nachricht auf Zeichenebene steuern, und diese Zeichen sind 8 Bit pro Zeichen ohne Escapezeichen.

Auch see the first example, in the first answer here: