I have an class which should send/receive data in packet form. This class contains an event handler which runs when new data is available to be read from the physical medium.
In the event handler I read the data from the medium and parse the available data for complete packets. Once a packet is identified an event is raised to pass the new packet to subscribers. The subscribers to this event decide if they want to use the packet or not.
So far so good... Now to my question. While the scenario above works to distribute the incoming data to a multitude of subscribers and place further processing logic further up in the application it leaves me with a problem:
Sometimes the class will receive a data packet which is just a reply (think ACK/FAIL) to another packet which was sent by the class.
How would I implement a method to send something which would wait for such a confirmation while not breaking the aforementioned concept of handling incoming raw data in the event handler?
Some pseudo code to illustrate the problem:
class CommCenter
{
public event NewPacketAvailable;
private void _newRawDataAvailable_EventHandler
{
// read incoming data from physical medium
// parse incoming data for packet structure
// the received packet may either contain a reply to
// a previously sent message or unrelated
// if packet found raise NewPacketAvailable event
// so that subscribers can handle the packet
}
public void SendMessage(DataPacket packet, Int32 timeoutInMilliseconds)
{
// put message on the physical medium
// wait for a packet confirming the previously sent packet
// How would I have to do this??
// the packet confirmation would arrive in _newRawDataAvailable_EventHandler
}
}
Maybe has a good idea or even implemented such a logic before. I'm a bit confused at the moment what to do. Glad for any help or pointers in the right direction ;-)
Okay, this is how it works:
In the event handler, read the data from the medium. Check if it's data or just a confirmation of a previous packet (ACK).
If it is data, pass it on.
If it is ACK, put it into a special Queue for ACKs.
In the SendMessage method iterate over this ACK Queue after sending your message until:
Timeout occurs
The confirmation for your sent packet arrives
That's it.
Related
I'm taking data from a serial instrument for plotting on a chart. The data stream is 230 kbps, and the serial pipeline is less than 50% full, data arrives about 100 kbps and actually doesn't vary really and rate or quantity.
Having used just a serial terminal program, like Teraterm, on the same computer; I can capture data and prove that both the source of the data as well as the test reception method are fine and I see no errors to the captured data.
The Windows Forms application I'm developing loses data. I've reduced it from receiving, capturing (in parallel), parsing, and plotting, to just receiving and capturing. And have found that I still see lost data in the capture.
I'm not a long experienced Windows person, so therefore may not know of better ways to accomplish the same functions. Here are the actions I'm taking to perform receive actions:
I'm using a System.IO.Ports.SerialPort class.
I modify the .DataReceived event via:
+= new SerialDataReceivedEventHandler(comPort_DataReceive);
I then call the open() method.
Note: I may be doing something incorrect here, I never clear the .DataReceived event with a -= at any point, instead each time I open, the event is added yet again. Nevertheless, these problems occur even when I've only talked to the port once.
Here's my code for the data receive function. RxString is a string.
private void comPort_DataReceive(object sender, SerialDataReceivedEventArgs e)
{
RxString = comPort.ReadExisting();
this.Invoke(new EventHandler(ParseData));
}
private void ParseData(object sender, EventArgs e)
{
// Save to capture file, if capture is enabled
if ((WriteToFileEnabled == true) && (WriteToFileName != null))
{
writeFileHandle.Write(RxString);
}
return;
// Previously would parse and plot data
}
So, how would persons execute a receive in this situation to get this data without losing it?
Follow on questions are things like: How big is the buffer for serial receive, or do I need to worry about that if I have a reasonably responsive application? Flow control is irrelevant, the remote device is going to send data no matter what, so it would be up to my computer to take that data and process it or ignore it. But how would I know if I've lost data or experienced framing errors and stuff? (I ask that last one without having searched much on the SerialPort class structure, sorry.)
Lets assume that your device is sending messages that are 85 bytes in length. The DataReceive event handler may or may not fire once to receive those 85 bytes. Since it might fire more than once your code must account for that. The DataReceive event handler should read the bytes available and append them to a buffer that is processed later.
Also, only one of the events raised by the SerialPort class can execute at a time. In the example assume the handler has to fire three times to receive the 85 bytes. While processing the first part the other two can't execute. If while processing the first part one of the other events, PinChanged or ErrorReceived, are needed they can't be executed either.
My first two experiences with the SerialPort class were a 9600 bps terminal and a 1 Mbps bluetooth device. What worked for the slower did not work for the faster, but when I figured out how to get the faster to work the slower could use the same methodology.
My methodology:
Before opening the serial port I start two other background threads that run in a do loop. The first one(Receive) reads all available bytes from the serial port, adds them to a buffer, and signals the second thread on every read. The second one(Protocol) determines if a full message has arrived, does any byte to string conversion, updates the UI, etc. Depending on the application I may start a third thread that handles errors and pin changes. All of these threads are throttled by a Threading AutoResetEvent.
My DataReceive event handler has one line in it, a Set on the AutoResetEvent that is throttling Receive.
A VB example of this can be found here SerialPort Methodology. Since adopting this methodology I have not had any of the problems that seem to plague other SerialPort users and have used it successfully with speeds up to 2Mbps.
My Context
I have a TCP networking program that sends large objects that have been serialized and encoded into base64 over a connection. I wrote a client library and a server library, and they both use NetworkStream's Begin/EndReadandBegin/EndWrite. Here's the (very much simplified version of the) code I'm using:
For the server:
var Server = new TcpServer(/* network stuffs */);
Server.Connect();
Server.OnClientConnect += new ClientConnectEventHandler(Server_OnClientConnect);
void Server_OnClientConnect()
{
LargeObject obj = CalculateLotsOfBoringStuff();
Server.Write(obj.SerializeAndEncodeBase64());
}
Then the client:
var Client = new TcpClient(/* more network stuffs */);
Client.Connect();
Client.OnMessageFromServer += new MessageEventHandler(Client_OnMessageFromServer);
void Client_OnMessageFromServer(MessageEventArgs mea)
{
DoSomethingWithLargeObject(mea.Data.DecodeBase64AndDeserialize());
}
The client library has a callback method for NetworkStream.BeginRead which triggers the event OnMessageFromServer that passes the data as a string through MessageEventArgs.
My Problem
When receiving large amounts of data through BeginRead/EndRead, however, it appears to be fragmented over multiple messages. E.G. pretend this is a long message:
"This is a really long message except not because it's for explanatory purposes."
If that really were a long message, Client_OnMessageFromServer might be called... say three times with fragmented parts of the "long message":
"This is a really long messa"
"ge except not because it's for explanatory purpos"
"es."
Soooooooo.... takes deep breath
What would be the best way to have everything sent through one Begin/EndWrite to be received in one call to Client_OnMessageFromServer?
You can't. On TCP, how things arrive is not necessarily the same as how they were sent. It the job of your code to know what constitutes a complete message, and if necessary to buffer incoming data until you have a complete message (taking care not to discard the start of the next message I the process).
In text protocols, this usually means "spot the newline / nul-char". For binary, it usually means "read the length-header in the preamble the the message".
TCP is a stream protocol, and has no fixed message boundaries. This means you can receive part of a message or the end of one and the beginning of another.
There are two ways to solve this:
Alter your protocol to add end-of-message markers. This way you continuously receive until you find the special marker. This can however lead that you have a buffer containing the end of one message and the beginning of another which is why I recommend the next way.
Alter protocol to first send the length of the message. Then you will know exactly how long the message is, and can count down while receiving so you won't read the beginning of the next message.
I originally had a race condition when sending data, the issue was that I was allowing multiple SocketAsyncEventArgs to be used to send data, but the first packet didn't send fully before the 2nd packet, this is because I have it so if the data doesn't fit in the buffer it loops until all the data is sent, and the first packet was larger than the second packet which is tiny, so the second packet was being sent and reached to the client before the first packet.
I have solved this by assigning 1 SocketAyncEventArgs to an open connection to be used for sending data and used a Semaphore to limit the access to it, and make the SocketAsyncEventArgs call back once it completed.
Now this works fine because all data is sent, calls back when its complete ready for the next send. The issue with this is, its causing blocking when I want to send data randomly to the open connection, and when there is a lot of data sending its going to block my threads.
I am looking for a work around to this, I thought of having a Queue which when data is requested to be sent, it simply adds the packet to the Queue and then 1 SocketAsyncEventArgs simply loops to send that data.
But how can I do this efficiently whilst still being scalable? I want to avoid blocking as much as I can whilst sending my packets in the order they are requested to be sent in.
Appreciate any help!
If the data needs to be kept in order, and you don't want to block, then you need to add a queue. The way I do this is by tracking, on my state object, whether we already have an active send async-loop in process for that connection. After enqueue (which obviously must be synchronized), just check what is in-progress:
public void PromptToSend(NetContext context)
{
if(Interlocked.CompareExchange(ref writerCount, 1, 0) == 0)
{ // then **we** are the writer
context.Handler.StartSending(this);
}
}
Here writerCount is the count of write-loops (which should be exactly 1 or 0) on the connection; if there aren't any, we start one.
My StartSending tries to read from that connection's queue; if it can do so, it does the usual SendAsync etc:
if (!connection.Socket.SendAsync(args)) SendCompleted(args);
(note that SendCompleted here is for the "sync" case; it would have got to SendCompleted via the event-model for the "async" case). SendCompleted repeats this "dequeue, try send async" step, obviously.
The only thing left is to make sure that when we try to dequeue, we note the lack of action if we find nothing more to do:
if (bufferedLength == 0)
{ // nothing to do; report this worker as inactive
Interlocked.Exchange(ref writerCount, 0);
return 0;
}
Make sense?
I have an assignment where I need to load some data like user (pouzivatel) and some int(stav odberu) through link modem with the serial port and store it in my local database. I know how to load data, send data over the serial port, but I need to make it happen in a structure on the image.
First I dial the telephone number of the device with AT command, btw this is working, but I do not know now how to stop and wait for SOH+adresa objektu (SOH+some string about address). Then send data about confirmation (ACK) and wait for new data to come.
The wait sequence is my biggest problem. How do I stop and wait for data being received.
Using the component and utilizing its DataReceived event as suggested in the comments would probably solve your problem easy and effectively. But you may have been looking for something more low-level to do it yourself.
If you want/need to do it in-line without any fancy event based system that would assume you are already in some message queue based environment like WinForms, you could do something like this.
while (true)
{
// check for new data
...
// if you got some, respond to it
...
if (someConditionThatTellsYouYouAreDoneOrSupposedToTerminate) break;
System.Threading.Thread.Sleep(50);
}
I am using a serial port to communicate with a remote diagnostics device.
The length of the response from the remote device varies depending upon the command but is known ahead of time. So, currently I send the command and wait for the required number of response bytes to be received.
I subscribe to the 'SerialPort.DataReceived' event whenever I'm not actively soliciting data. The handler for this event simply dumps any 'unsolicited' received data to a log (unsolicited data is typically only received if the remote device restarts unexpectedly, etc).
In some cases I want to send commands at a rate of about 60Hz.
My question is whether it's best to unsubscribe/subscribe to the 'SerialPort.DataReceived' event every time I call my 'SendCommand' method to actively solicit data, or should I leave the event subscription alone and just toggle a boolean 'TransferInProgress' flag that the DataReceived handler can use to ignore incoming data when I'm actively soliciting it?
Here's the current implementation:
public virtual bool SendCommand(byte[] command, ref byte[] response) {
try {
TransferInProgress = true;
OnTransferStarted();
// temporarily unsubscribe since we're actively soliciting data
_port.DataReceived -=
new SerialDataReceivedEventHandler(SerialPort_DataReceived);
_port.DiscardInBuffer();
_port.Write(command, 0, command.Length);
OnCommandSent(command);
// read the requested number of response bytes
int responseBytesRead = 0;
while (responseBytesRead < response.Length) {
responseBytesRead +=
_port.Read(response, responseBytesRead, (response.Length - responseBytesRead));
}
OnCommandResponseReceived(response);
return true;
}
catch (Exception ex) {
OnCommandSendFailed(ex.Message);
return false;
}
finally {
_port.DataReceived +=
new SerialDataReceivedEventHandler(SerialPort_DataReceived);
OnTransferComplete();
TransferInProgress = false;
}
}
-Trevor
Have you thought about handling all of your data reception in one place? You could treat the commands you send as fire and forget, parsing the data received for the responses. If the responses do not have an identifying header and the ONLY way you know how to parse them is by knowing which command you sent and the length of the response, then you could keep track of the commands sent in a queue. The way that would work, is that in your Data Received handler you would check the queue of commands you're waiting on a response for, and then parse the data received like you do now.
Long story short, I would recommend handling all incoming data in one place.
My opinion if I'm understanding correctly would be to simply handle all your receiving data in the DataReceived handler or you have one other options.
If the data received between actual request isn't much you could just read the buffer and log it before transmitting your request. The serial driver receive buffer may be enough to store a small amount of data. Then send the request and read in just the response. This will probable be the easier method and simpler code.
I normally toggle a boolean. With subscribing/unsubscribing you run the risk of subscribing to the same event more than once. For instance in your code if OnTransferStarted() throws an exception you will subscribe twice to the DataReceived event.