4. Roundtrip-based communication protocol design
The core of the Communication Server plug-in is three parts: (1) Implementation of communication protocols unrelated to communication methods and business logic; (2) Implementation of communication business logic related to communication methods and business logic; (3) remote communication message queue. Here I will focus on the implementation of communication protocols. The implementation of this communication protocol is more agile.
4.1 basic unit of communication protocol-message
The communication unit of a communication protocol is a message. The following is a protocol written by a hardware development engineer. A message package consists of the leading character, start character, message header, verification code, message body, and end character. The messages sent and received by different communication commands are different.
The communication protocol must be able to send correct messages and parse response message packets. In addition, the hardware can accept messages in byte format, what communication server software can correctly identify is meaningful fields. Therefore, we have designed the following base classes for messages. The smaller unit of a message is a messagepart. It provides the tocontent and tomessage methods for conversion to bytecode and string, respectively. In addition, it also defines the tryparse Method for parsing bytecode into meaningful messagepart objects. Parsemessageexception is defined here. This exception is thrown when message parsing fails. The following are definitions of message headers, message bodies, and Message Base classes. The message base class consists of the prefix, start, header, body, and suffix.
Then, based on the scata 3.0 protocol provided by hardware development engineers, we define the message base class scata30message related to the communication protocol. This message provides the implementation of a default message header, but the message body needs to be further implemented according to the instructions. It is the implementation of most message bodies involved in communication protocols. The message bodies are basically one-to-one, that is, the message bodies and Response Message bodies.
A message body is generally a command sent by the server to hardware. Such a message body must construct all fields and implement the tocontent method to convert the message into bytecode and send it to the hardware; the Response Message is generally a message sent by hardware to the server. It must at least implement the tryparse method and parse the hardware bytecode into meaningful fields for access by the business logic layer.
The following is a message definition.
Using system; using system. collections. generic; using system. LINQ; using system. text; using uishell. commserverservice. utility; using system. componentmodel; namespace uishell. commserverservice. protocol. scata30.message {[description ("read single table")] public class Syntax: scata30messagebody {public byte meterprotocolcategory; Public byte channel; Public byte [] meteraddressbcd; public long meteraddress; internal limit () {} public scata30readmetermessagebody (byte meterprotocol, byte channel, long meteraddress) {meterprotocolcategory = meterprotocol; Channel = channel; meteraddress = meteraddress; meteraddressbcd = protocolutility. meteraddressfromlong (meteraddress, true);} protected override bool tryparsewithoutcheckcode (byte [] bodycontent) {Throw new handle ();} protected override byte [] tocontentwithoutcheckcode () {return New byte [] {meterprotocolcategory, channel }. concat (meteraddressbcd ). toarray ();} public override string tostring () {return string. format ("protocol type = {0}, channel number = {1}, table address = {2}", meterprotocolcategory, channel, meteraddress );}}}
The following describes the implementation of response messages.
Using system; using system. collections. generic; using system. LINQ; using system. text; using uishell. commserverservice. utility; using system. componentmodel; namespace uishell. commserverservice. protocol. scata30.message {[description ("read single table response")] public class scata30readmeterresponsemessagebody: scata30messagebody {public scata30responsestatus responsestatus; // <summary> // table data, similar to reading data from multiple tables. /// </Summary> Public byte [] meterbodycontent; Public partition () {} protected override bool tryparsewithoutcheckcode (byte [] bodycontent) {If (bodycontent = NULL | bodycontent. length = 0) {_ log. error (string. format (uishell. commserverservice. properties. resources. parsemessagebodyfailed, protocolutility. bytestohexstring (bodycontent); Return false;} If (bodycontent. le Ngth = 1) {If (bodycontent [0]! = (Byte) scata30responsestatus. failed) {_ log. error (string. format (uishell. commserverservice. properties. resources. parsemessagebodyfailed, protocolutility. bytestohexstring (bodycontent); Return false;} else {responsestatus = (scata30responsestatus) bodycontent [0];} else {responsestatus = scata30responsestatus. success; meterbodycontent = bodycontent;} return true;} protected override byte [] tocontentwithoutcheckcode () {If (responsestatus = scata30responsestatus. failed) {return New byte [] {(byte) responsestatus };} return meterbodycontent;} public override string tostring () {return string. format ("status = {0}, table data = {1}", enumdescriptionhelper. getdescription (responsestatus), protocolutility. bytestohexstring (meterbodycontent ));}}}
4.2 composition of communication protocols-roundtrip (Round Trip)
The communication process between the communication server and the hardware is implemented by a group of conversations, and each group of conversations is completed in a Q & A manner. We use the roundtripbase type to represent a Q & A conversation. Q & A conversations are divided into active (activeroundtrip) and passive (passiveroundtrip), that is, the server initiates a hardware response, or the hardware initiates a server response. Sometimes a Q & A conversation may be implemented by several groups of subdialogs, which we call compositeroundtripbase ). The basic classes involved in the communication protocol dialog process are designed as follows.
The roundtripbase of a dialog is designed as follows. It consists of priority and timestamp attributes. The start method is provided to indicate the start of a session, as well as oncompleted and onerror events. Roundtripqueue is a dialog queue. It strictly limits the communication protocol to run only one roundtrip at a time and cannot run cross-nodes. This roundtripqueue is thread-safe, because the communication protocol will be accessed by remote communication threads, protocol threads, UI threads, and other threads.
4.3 roundtrip implementation
In this system, we use the scata 3.0 communication protocol. Here we implement two basic classes: scata30activeroundtrip and scata30passiveroundtrip.
In scata30activeroundtrip, in the start method, streamadapter is used to obtain a message from the communication channel. Once the message is successfully parsed, a response message packet is sent. This dialog will be retried several times once an error or timeout occurs. Similarly, scata30passiveroundtrip is also implemented in this way.
Next, we define the following conversation based on the communication protocol.
let's take a look at the implementation of a conversation.
Using system; using system. collections. generic; using system. LINQ; using system. text; using uishell. commserverservice. protocol. scata30.message; using uishell. commserverservice. utility; using system. componentmodel; namespace uishell. commserverservice. protocol. scata30.roundtrip {[description ("")] public class scata30readhistoricalmeterroundtrip: scata30hasnextactiveroundtrip <scata30readhi Success, success> {public datetime historicaldatetime; public success (ushort destinationaddress, ushort destinationzigbeeaddress, datetime timestamp, using protocol): Base (destinationaddress, destinationzigbeeaddress, new scata30message <success> (Cost Meter. Bydate, protocol. masterstationaddress, destinationaddress, 0, datetime. now, new scata30readhistoricalmetermessagebody (timestamp), scata30messagetype. readmeterbydateresponse, protocol) {historicaldatetime = timestamp;} public override void receiveresponsemessages () {base. receiveresponsemessages (); foreach (VAR message in receivedresponsemessages) {If (! Message. body. historicaldatetime. equals (historicaldatetime) {_ log. error (string. format ("read the historical meter content error since the date time mismatched. the require date time is '{0}', return by concentrator is '{1}' ", historicaldatetime. tostring ("yyyy-mm-dd hh: mm: SS"), message. body. historicaldatetime. tostring ("yyyy-mm-dd hh: mm: SS"); // throw new exception ("parse message error since historical date time mismatched. ");}}}}}
4.4 Implementation of communication protocols
The communication protocol implementation class is shown below. Because the communication protocol has nothing to do with the communication mode and business logic, streamadapter and streamprovider are introduced here to shield these contexts. The function of streamadapter is to obtain a message and send a message. streamprovider provides a communication stream for different communication modes.
The following describes the key implementations of the Protocol class. There is a thread in the Protocol class to implement communication with hardware. This thread will continue to run, and then get the roundtrip from the conversation queue. Once the obtained roundtrip is not empty, it will run the roundtrip. Otherwise, the thread enters the sleep state.
Public bool start () {If (_ started) {return true;} fireonstarting (); try {commstreamprovider. start ();} catch (exception ex) {_ log. error ("Start the communication Provider failed. ", ex); Return false;} _ thread = new thread () => {roundtripbase roundtrip; while (! _ Exited) {monitor. Enter (_ queue. syncroot); roundtrip = dequeue (); If (roundtrip! = NULL) {try {Monitor. exit (_ queue. syncroot); onroundtripstartinghandler (this, new roundtripeventargs () {roundtrip = roundtrip}); roundtrip. start ();} catch (threadabortexception) {trace ("the communication thread is terminated. "); Throw;} catch (scata30streamexception ex) // when stream cannot be obtained, exit {_ exited = true; roundtrip. trace ("session failed because the connection has been closed. ");} Catch (exception ex) {string error = geterrormessage (Ex); roundtrip. Trace (string. Format (" session failed because: {0 }. ", Error);} If (! _ Exited) {roundtrip. trace (environment. newline); onroundtripstartedhandler (this, new roundtripeventargs () {roundtrip = roundtrip});} else {// 1 save the currently failed roundtrip to the queue failedroundtrips. enqueue (roundtrip); // 2 save other unprocessed roundtrip do {roundtrip = _ queue. dequeue (); If (roundtrip! = NULL) {failedroundtrips. enqueue (roundtrip) ;}} while (roundtrip! = NULL); // 3 stop the current protocol stop ();} // After the roundtrip is executed, clear the resource roundtrip. dispose ();} else {Monitor. exit (_ queue. syncroot); onidlehandler (this, new roundtripeventargs (); _ autoresetevent. waitone () ;}}}); _ thread. start (); _ started = true; fireonstarted (); Return true ;}
The dialog is executed asynchronously and notified through events. As shown below.
Using system; using system. collections. generic; using system. LINQ; using system. text; using uishell. commserverservice. protocol. scata30.roundtrip; using uishell. commserverservice. protocol. scata30.message; namespace uishell. commserverservice. protocol. scata30 {public partial class scata30protocol {public scata30setconcentratortimeroundtrip setconcentratortime (ushort concentratoraddress, ushort conce Callback, datetime timestamp, eventhandler
The implementation of this communication protocol is very elegant. During the maintenance process, the change of communication instructions and the change of communication methods do not need to modify the Protocol and roundtrip itself, you only need to change the message body, add a new streamprovider, and implement the business logic at the upper layer. The design of communication protocols is described in length and will be further described below.