Using this example I created two classes used to read a device on a serial port. The device is a laser which I can control (turn system/lasers on/off, read stats etc.) using commands like SYST:STAT?. Most of the commands immediately send back a response that I can read using the _serialPort.ReadLine().
However, the command SYST:STAT ON that turns on the system is one of the commands that takes about ten seconds. This is where the makers of the laser decided it's nice to send back a message that says: system ready. I can read this response asynchonously using the NewSerialDataRecieved event. In here, I read every line like this:
private void OnDataReceived(object sender, SerialDataEventArgs e)
{
_dataReceived += Encoding.ASCII.GetString(e.Data);
// Loop as long as there's full lines available
while (_dataReceived.Length > 0 && _dataReceived.Contains("\r\n"))
{
int delimiterIndex = _dataReceived.IndexOf("\r\n", StringComparison.Ordinal) + 2;
string line = _dataReceived.Substring(0, delimiterIndex); // Get first line from data
_dataReceived = _dataReceived.Substring(delimiterIndex, _dataReceived.Length - line.Length); // Remove first line from data
line = line.Substring(0, line.Length - 2);
TryParseSystemReadyResponse(line);
TryParseEnteringStandbyModeResponse(line);
TryParseTemperatureResponse(line);
OnResponseReceived?.Invoke(line);
}
}
The method reads the incoming data, stores it, and then parses the data line by line. This works perfectly for my asynchronous, well defined responses. Here's the catch: most of the responses are formatted like '0 0' or '0 1'. This means there's no way to parse them like that. This is why I read them directly after sending my command to the serial device, like this:
public void UpdateLaserState()
{
// Get the selected laser
_serialPortManager.SendCommand("LSR:NSEL?");
int selectedLaser = _serialPortManager.ReadIntegerResponse();
// Do stuff with the laser number
OnSystemStateChange?.Invoke();
}
ReadIntegerResponse is a method that does a ReadLine, strips the response of the preceding '0 ' and then parses the remaining string to int. Up to now, communication works as well as I would expect. Messages that can be synchronous are being read instantly and asynchronous messages can be parsed by my OnDataReceived method.
Here comes the problem. I need to read the temperature of the laser system about twice a second. My solution was to create a thread that reads the temperature, like this:
public Laser(){
_temperatureThread = new Thread(TemperatureTask);
_temperatureThread.Start();
_temperatureThreadRunning = true;
}
public void Dispose()
{
_temperatureThreadRunning = false;
_temperatureThread.Join();
}
private void TemperatureTask()
{
while (_temperatureThreadRunning)
{
this.UpdateTemperatures();
Thread.Sleep(TemperatureTaskSleep);
}
}
public void UpdateTemperatures()
{
_serialPortManager.SendCommand("SYST:TEMP?");
//string line = _serialPortManager.ReadResponse();
//TryParseTemperatureResponse(line);
}
This gives problems as UpdateTemperatures sends responses very often on a different thread. I resolved a lot of problems by implementing a lock. However, my responses often get mixed up. I think the following is happening:
Main thread Temperature thread
-----------------------------------------
Sends command |
| Sends command
Reads response |
| Reads response
This means the threads are reading eachothers responses. There is no good way to identify responses like '0 0' as belonging to a certain command.
Is there a way to prevent this from happening?
P.S. the ReadResponse methods of the _serialPortManager all look like this (except for parsing the response):
public string ReadResponse()
{
string response = SafeReadLine();
return StripResponse(response);
}
private string SafeReadLine()
{
lock (_serialPortLock)
{
string response = _serialPort.ReadLine();
return StripResponse(response);
}
}
Related
I am writing an application that needs to write messages to a USB HID device and read responses. For this purpose, I'm using USBHIDDRIVER.dll (https://www.leitner-fischer.com/2007/08/03/hid-usb-driver-library/ )
Now it works fine when writing many of the message types - i.e. short ones.
However, there is one type of message where I have to write a .hex file containing about 70,000 lines. The protocol requires that each line needs to be written individually and sent in a packet containing other information (start, end byte, checksum)
However I'm encountering problems with this.
I've tried something like this:
private byte[] _responseBytes;
private ManualResetEvent _readComplete;
public byte[][] WriteMessage(byte[][] message)
{
byte[][] devResponse = new List<byte[]>();
_readComplete = new ManualResetEvent(false);
for (int i = 0; i < message.Length; i++)
{
var usbHid = new USBInterface("myvid", "mypid");
usbHid.Connect();
usbHid.enableUsbBufferEvent(UsbHidReadEvent);
if (!usbHid.write(message)) {
throw new Exception ("Write Failed");
}
usbHid.startRead();
if (!_readComplete.WaitOne(10000)) {
usbHid.stopRead();
throw new Exception ("Timeout waiting for read");
}
usbHid.stopRead();
_readComplete.Reset();
devResponse.Add(_responseBytes.ToArray());
usbHid = null;
}
return devResponse;
}
private void ReadEvent()
{
if (_readComplete!= null)
{
_readComplete.Set();
}
_microHidReadBytes = (byte[])((ListWithEvent)sender)[0];
}
This appears to work. In WireShark I can see the messages going back and forth. However as you can see it's creating an instance of the USBInterface class every iteration. This seems very clunky and I can see in the TaskManager, it starts to eat up a lot of memory - current run has it above 1GB and eventually it falls over with an OutOfMemory exception. It is also very slow. Current run is not complete after about 15 mins, although I've seen another application do the same job in less than one minute.
However, if I move the creation and connection of the USBInterface out of the loop as in...
var usbHid = new USBInterface("myvid", "mypid");
usbHid.Connect();
usbHid.enableUsbBufferEvent(UsbHidReadEvent);
for (int i = 0; i < message.Length; i++)
{
if (!usbHid.write(message)) {
throw new Exception ("Write Failed");
}
usbHid.startRead();
if (!_readComplete.WaitOne(10000)) {
usbHid.stopRead();
throw new Exception ("Timeout waiting for read");
}
usbHid.stopRead();
_readComplete.Reset();
devResponse.Add(_responseBytes.ToArray());
}
usbHid = null;
... now what happens is it only allows me to do one write! I write the data, read the response and when it comes around the loop to write the second message, the application just hangs in the write() function and never returns. (Doesn't even time out)
What is the correct way to do this kind of thing?
(BTW I know it's adding a lot of data to that devResponse object but this is not the source of the issue - if I remove it, it still consumes an awful lot of memory)
UPDATE
I've found that if I don't enable reading, I can do multiple writes without having to create a new USBInterface1 object with each iteration. This is an improvement but I'd still like to be able to read each response. (I can see they are still sent down in Wireshark)
I am making a program that starts a child process and communicates via anonymous pipes. When I read from a pipe, the program hangs at the first ReadLine() call as seen in the following code method:
// Reads messages sent from module's process via anonymous pipe
internal string[] ReadPipe() {
try {
Log.Verbose("Checking read pipe");
// Check for sync message and return null if there is no message to receive
string pipeMessage = _pipeInReader.ReadLine(); // HANGS ON THIS LINE
if(pipeMessage == null || !pipeMessage.StartsWith("SYNC")) {
Log.Verbose("No message found in pipe");
return null;
}
// Return array of message lines
Log.Verbose("Received message from module {ModuleName}", _fileInfo.Name);
List<string> pipeMessageLines = new();
do {
pipeMessage = _pipeInReader.ReadLine();
pipeMessageLines.Add(pipeMessage);
Log.Debug(pipeMessage);
} while(pipeMessage != null && !pipeMessage.StartsWith("END"));
return pipeMessageLines.ToArray();
} catch(Exception e) {
Log.Error(e.ToString());
return null;
}
}
The code I am using to write to the pipe is the following:
// Sends a message to module's process via anonymous pipe
public static void WritePipe(string message) {
try {
Log.Verbose("Sending \"{Message}\" to kit pipe", message);
// Send sync message and wait for module process to receive it
Log.Verbose("Waiting for pipe drain");
_pipeOutWriter.Write("SYNC");
_pipeOut.WaitForPipeDrain();
// Send the specified message
Log.Verbose("Pipe drained. Sending message");
_pipeOutWriter.Write(message);
_pipeOutWriter.Write("END");
} catch(Exception e) {
Log.Error(e.ToString());
}
}
Why does it hang at that ReadLine() line?
Thanks in advance!
Without a proper minimal, reproducible example, it's impossible to say for sure. However, one glaring problem with your code is that when you write to the _pipeOutWriter object, you don't call Flush(). Assuming that's a TextWriter, by default it's going to buffer the data until the internal buffer is full, and not send anything to the underlying Stream until then.
By calling Flush(), you force it to flush its internal buffer and send the data right away.
If that does not address your question, please edit the question to improve it, making sure to provide a minimal, reproducible example, and any additional details about what you've tried so far to fix the problem and what specifically you need help with.
I want to create a TCP listener for my .NET Core project. I'm using Kestrel and configured a new ConnectionHandler for this via
kestrelServerOptions.ListenLocalhost(5000, builder =>
{
builder.UseConnectionHandler<MyTCPConnectionHandler>();
});
So what I have so far is
internal class MyTCPConnectionHandler : ConnectionHandler
{
public override async Task OnConnectedAsync(ConnectionContext connection)
{
IDuplexPipe pipe = connection.Transport;
PipeReader pipeReader = pipe.Input;
while (true)
{
ReadResult readResult = await pipeReader.ReadAsync();
ReadOnlySequence<byte> readResultBuffer = readResult.Buffer;
foreach (ReadOnlyMemory<byte> segment in readResultBuffer)
{
// read the current message
string messageSegment = Encoding.UTF8.GetString(segment.Span);
// send back an echo
await pipe.Output.WriteAsync(segment);
}
if (readResult.IsCompleted)
{
break;
}
pipeReader.AdvanceTo(readResultBuffer.Start, readResultBuffer.End);
}
}
}
When sending messages from a TCP client to the server application the code works fine. The line await pipe.Output.WriteAsync(segment); is acting like an echo for now.
Some questions come up
What response should I send back to the client so that it does not run into a timeout?
When should I send back the response? When readResult.IsCompleted returns true?
How should I change the code to fetch the whole message sent by the client? Should I store each messageSegment in a List<string> and join it to a single string when readResult.IsCompleted returns true?
that is entirely protocol dependent; in many cases, you're fine to do nothing; in others, there will be specific "ping"/"pong" frames to send if you just want to say "I'm still here"
the "when" is entirely protocol dependent; waiting for readResult.IsCompleted means that you're waiting for the inbound socket to be marked as closed, which means you won't send anything until the client closes their outbound socket; for single-shot protocols, that might be fine; but in most cases, you'll want to look for a single inbound frame, and reply to that frame (and repeat)
it sounds like you might indeed be writing a one-shot channel, i.e. the client only sends one thing to the server, and after that: the server only sends one thing to the client; in that case, you do something like:
while (true)
{
var readResult = await pipeReader.ReadAsync();
if (readResult.IsCompleted)
{
// TODO: not shown; process readResult.Buffer
// tell the pipe that we consumed everything, and exit
pipeReader.AdvanceTo(readResultBuffer.End, readResultBuffer.End);
break;
}
else
{
// wait for the client to close their outbound; tell
// the pipe that we couldn't consume anything
pipeReader.AdvanceTo(readResultBuffer.Start, readResultBuffer.End);
}
As for:
Should I store each messageSegment in a List<string> and join it to a single string when
The first thing to consider here is that it is not necessarily the case that each buffer segment contains an exact number of characters. Since you are using UTF-8, which is a multi-byte encoding, a segment might contain fractions of characters at the start and end, so: decoding it is a bit more involved than that.
Because of this, it is common to check IsSingleSegment on the buffer; if this is true, you can just use simple code:
if (buffer.IsSingleSegment)
{
string message = Encoding.UTF8.GetString(s.FirstSpan);
DoSomethingWith(message);
}
else
{
// ... more complex
}
The discontiguous buffer case is much harder; basically, you have two choices here:
linearize the segments into a contiguous buffer, probably leasing an oversized buffer from ArrayPool<byte>.Shared, and use UTF8.GetString on the correct portion of the leased buffer
use the GetDecoder() API on the encoding, and use that to populate a new string, which on older frameworks means overwriting a newly allocated string, or in newer frameworks means using the string.Create API
Frankly, "1" is much simpler. For example (untested):
public static string GetString(in this ReadOnlySequence<byte> payload,
Encoding encoding = null)
{
encoding ??= Encoding.UTF8;
return payload.IsSingleSegment ? encoding.GetString(payload.FirstSpan)
: GetStringSlow(payload, encoding);
static string GetStringSlow(in ReadOnlySequence<byte> payload, Encoding encoding)
{
// linearize
int length = checked((int)payload.Length);
var oversized = ArrayPool<byte>.Shared.Rent(length);
try
{
payload.CopyTo(oversized);
return encoding.GetString(oversized, 0, length);
}
finally
{
ArrayPool<byte>.Shared.Return(oversized);
}
}
}
In my scenario there are one or two serial port. If only one port is configured, that port can be dedicated to a PLC or to a Scale.
The PLC communicate only when user push some button on the machine, the Scale instead constantly poll the measured that has weighted. If only one port is configured there are no issue.
When two port are configured the communication protocol change a bit, if user push the PLC's weight button PLC send a weight request. If that request is listened on DataReceived event the Scale port is opened and the polled value is readen.
Now i have to admit that the legacy code that i will paste here is not the best code possible, and that is bad that it use the same DataReceived event for both the ports; but that code had work for quite a lot (about six years). Recently the Scale has been changed and the program had stoped working properly (only in the two port configuration) raising a sistematic timeout exception. I have sniffed the traffic and nothing change with protocol or data format.
Here the problematic piece of code :
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
lock (_myLockSerialPort)
{
string source = "No Data" ;
try
{
if (sender == null)
return;
SerialPort genericPort = (SerialPort)sender;
if (genericPort.Equals(ScalePort))
{
if (genericPort.BytesToRead > 0)
{
source = "Scale" ;
string datas = string.Empty;
datas = genericPort.ReadTo(_currentConfiguration.ReadToForPese);
ReadDataFromScale(genericPort, datas);
}
}
else
{
if (genericPort.BytesToRead > 0)
{
source = "PLC" ;
string datas = string.Empty;
datas = genericPort.ReadTo(_currentConfiguration.ReadToForPlc);
ReadDataFromPlc(genericPort, datas);
}
}
}
catch (TimeoutException timeoutException)
{
//Handle Timeout
}
catch (Exception err)
{
//Handle Other Errors
}
}
}
The question is if there is something in the ReadTo method or in the polling interval that can explain that behaviour.
I would underline that nothing in the data exchange format has changed, so the character used to terminate the communication are still in place (and present in the sniffed traffic).
NO, NO, NO!!! If you had a text file normally you would use ReadLine() method to read one line at a time. With ports or sockets data is coming at you at real time and the program is probably running faster than the data so you will not get a full line at one time. The serial uart in the PC has only a small read buffer (could be as small as 8 bytes). Windows is using timers to move the serial port data into a stream. Your program is reading the windows stream. So the actual data received is a combination of how fast the sender is sending data, the latency of the UART, the latency of Windows, and the latency of the Net Library. It is not a configuration issue. It is a real time issue.
I would recommend using two SerialPort classes, one for your scale and one for the PLC in which they would have their own DataReceived events. As others have suggested, you should append characters until you see your end character, then parse the string with your methods you wrote. Below is an example where Line Feed (LF) is my end character I'm looking for.
StringBuilder sbScale = new StringBuilder();
StringBuilder sbPLC = new StringBuilder();
char LF = (char)10;
private void serialPortScale_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string Data = serialPortScale.ReadExisting();
foreach (char c in Data)
{
if (c == LF)
{
sbScale.Append(c);
CurrentLine = sbScale.ToString();
sbScale.Clear();
ReadDataFromScale(CurrentLine);
}
else
{
sbScale.Append(c);
}
}
}
private void serialPortPLC_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string Data = serialPortPLC.ReadExisting();
foreach (char c in Data)
{
if (c == LF)
{
sbPLC.Append(c);
CurrentLine = sbPLC.ToString();
sbPLC.Clear();
ReadDataFromPlc(CurrentLine);
}
else
{
sbPLC.Append(c);
}
}
}
You could try to implement that into one DataReceived event, but it can be messy. I think have dedicated classes to each device is much cleaner and easier to read, especially if someone has to take over the code some day after you. Also I clear the string builder immediately, because the DataReceived event is threaded, so it can fire while you're already in there.
I'm trying to refactor some ultra-complex legacy code that sends data from a handheld device to an app running on a PC, to which the handheld device is connected.
There is a "conversation" that goes on between the two apps that follows a protocol; the server (the app running on the PC) responds based on what the client tells it, and vice versa. Actually, the "conversation" can be seen about two thirds of the way down here.
Anyway, my problem is: how can I let the client wait for the server to respond without interrupting it, or thinking it's not going to respond and failing to continue? This is what I have right now:
public class FileXferLegacy : IFileXfer
{
private SerialPort cereal;
private String lastDataReceived;
private String receivedData;
. . .
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// This method will be called when there is data waiting in the port's buffer
try
{
receivedData += cereal.ReadLine();
lastDataReceived = receivedData;
ExceptionLoggingService.Instance.WriteLog(String.Format("Received {0} in FileXferLegacy.SendDataContentsAsXML", receivedData));
}
catch (Exception ex)
{
//MessageBox.Show(ex.Message);
}
}
#region IFileFetchSend Members
. . .
public void SendDataContentsAsXML(string destinationPath, string data)
{
byte[] stuff;
ExceptionLoggingService.Instance.WriteLog("Reached
FileXferLegacy.SendDataContentsAsXML");
cereal.Open();
stuff = System.Text.UTF8Encoding.UTF8.GetBytes("PING" + "\n");
cereal.Write(stuff, 0, stuff.Length);
if (lastDataReceived.Contains("PING")) // Expecting "PING|ACKNOWLEDGE|"
{
stuff =
System.Text.UTF8Encoding.UTF8.GetBytes("LOGIN|foo|000003|LOC_HOST|PPP_PEER|1.4.0.42|bar" + "\n");
// TODO: replace this test data with dynamic data
cereal.Write(stuff, 0, stuff.Length);
}
if (lastDataReceived.Contains("JOIN|LEVEL")) // Expecting something like "JOIN|LEVEL|1
SETTING|ALT_ID|FALSE"
{
stuff = System.Text.UTF8Encoding.UTF8.GetBytes("HHTCOMMAND|GETHHTSUPDATE|");
cereal.Write(stuff, 0, stuff.Length);
}
. . .
String lastResponse = lastDataReceived; // Expecting something like
"RESULT|FILECOMPLETE|INV_000003_whatever(not identical to what was sent earlier!).XML"
// Parse out and do something with the filename ("INV_000003_whatever(not identical to
what was sent earlier!).XML" above)
}
As you can see, the client/handheld sends a string; it then reads "lastDataReceived" which is assigned in the DataReceived method. But what if there has been a delay, and "lastDataReceived" is null? What do I need to do to force a delay (without going to an extreme that would cause the app to appear slothlike in its slowness)? Or what is the way this should be done, if I'm totally off base?
A typical approach is to use a reader thread that pulls bytes off the port with blocking reads (though it can be done with async notification instead) and, once detecting that an entire message has been delivered, it either:
Puts them into a blocking queue (with consumer blocking on calls to dequeue until either a msg is added or a timeout reached
or
Notifies a listener with an event that contains the message.
Which of those two depends a lot on the consumer of those messages. Your code above would benefit from #1, though if the consumer is the UI thread then you should look at #2.
The protocol seems to be half-duplex so rewriting it with synchronous calls to Write/Readline seems to be the simplest way to handle it.