.NET C# reading from serial after NModBus call - c#

I'm issuing a NModBus call (setting a register to 1) to a sensor that then triggers a transmission of binary data from the sensor via the serial port.
As long as I only read/write registers via NModBus, everything works just fine but when I want to read this returning byte flow, it just doesn't work.
I've tried a few different approaches:
Using the same SerialPort
Using two different SerialPort objects (on the same port of course) in case there were trailing buffers
Reading "synchronously" immediately after the NModBus call
Reading using SerialPortDataReceived
Adding timeouts here and there (including adding waiting times on the sensor itself before starting transmission)
Etc.
Depending on the approach, I may read 4 (four) bytes but I think they are the ModBus answer to the write register call. No way to retrieve the rest of the flow (a few hundred bytes). And yes, in case you were wondering, I have verified via a separate tool that such flow is indeed transmitted. :-P
I have exchanged information via serial port in other programs with no particular problems so I was wondering if the fact that I'm using NModBus then standard serial port operations on the same port is generating concerns?
The code - that is supposed to be pretty basic - looks like:
using (SerialPort serialForModBus = new SerialPort(Port))
{
// Basic serial port settings
Open(serialForModBus);
// Create ModBus RTU master
IModbusSerialMaster master = CreateMaster(serialForModBus);
// Write register to trigger reading...
master.WriteMultipleRegisters(SLAVE_ID, SensorModBusRegisters.COMMAND_SEND_LOGS, new ushort[] { (ushort)1 });
}
// Now read...
using (SerialPort serialToReadLogs = new SerialPort(Port))
{
Open(serialToReadLogs);
serialToReadLogs.ReadTimeout = 10000;
// Externalize reading to separate class (that will also do the parsing..)
SensorLogSerialReader logReader = new SensorLogSerialReader();
serialToReadLogs.DataReceived += logReader.SerialPortDataReceived;
}
// In SensorLogSerialReader
public void SerialPortDataReceived(object senderSerialPort, SerialDataReceivedEventArgs e)
{
SerialPort sender = (SerialPort)senderSerialPort;
List<byte> bytes = new List<byte>();
try
{
bool moreToRead = (sender.BytesToRead != 0);
int cur;
while (moreToRead)
{
cur = sender.ReadByte();
bytes.Add((byte)cur);
moreToRead = (sender.BytesToRead != 0);
}
Done = true;
}
catch (...)
}
Any idea? Am I missing something "evident"?
Thank you!
Note: I've seen an post that may be similar but remained unanswered

Related

SerialPort (.Net 4.0) ReadTo method lead to sistematic timeout

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.

IBuffer in UWP for TCP messages

I need to transfer a string over TCP connection. For this I serializable my object(over 10000 line list) in one stroke, without Intended. But large string won't transfer(As I understood due to buffer size). So MSDN, on this page (https://learn.microsoft.com/ru-ru/windows/uwp/networking/sockets) say me to use IBuffer for transfer my divided stroke. Here is a code:
// More efficient way to send packets.
// This way enables the system to do batched sends
IList<IBuffer> packetsToSend = PreparePackets();
var outputStream = stream.OutputStream;
int i = 0;
Task[] pendingTasks = new Tast[packetsToSend.Count];
foreach (IBuffer packet in packetsToSend)
{
pendingTasks[i++] = outputStream.WriteAsync(packet).AsTask();
}
// Now, wait for all of the pending writes to complete
await Task.WaitAll(pendingTasks);
What is the method PraparePackets()? How to prepare packets from my stroke?
Edit: I've found solution with DataReader and DataWriter, which has written in Albahari.(End of 16 chapter).
I've found solution with DataReader and DataWriter, which has written in Albahari.(End of 16 chapter).

C# - Example for Handshaking with SerialPort class

I'm currently working on a project which requires me to communicate with an MBED through serial ports.
I've read the msdn site but I don't understand.
Can anyone teach me or show examples of how to use SerialPort.Handshake (XonXoff or RequestToSend)?
My main request how to set: If C# reads a certain string, then send data.
First of all i think that Handshaking is not what you need. Handshaking is a low level communication, to control when to send data or not. more about handshaking
My main request how to set: If C# reads a certain string, then send
data.
What you need is a continuous data reading or an event, and after certain string is read - to send some data.
Let me find some examples.
void connect(){
_port = new SerialPort();
// set port parameters
_port.DataReceived += portDataReceived; //set the data received event
_port.Open();
}
void portDataReceived(object sender, SerialDataReceivedEventArgs e)
{
string data = _port.ReadExisting();
if(data.Contains("data-start")) //check for the start data
{
CreateReply(); // your reply
}
}
You can read the data in a while loop, but take a note that an infinite loop executed in the main thread will block your program from responding.
void connectAndRead(){
_port = new SerialPort();
// set port parameters
_port.Open();
_stopReading = false;
var dataBuffer = "";
while(!_stopReading)
{
dataBuffer += _serialPort.ReadLine();
if(!dataBuffer.Contains("data-start")) //check for the start data
continue;
CreateReply(); // your reply
_stopReading = true; // exit loop
}
}

Pcap.Net PacketCommunicator.Dispose() close my application without any error

I am using Pcap.Net take Pcap File and transmit all it's packet through my machine Network Adapter.
So in order to do that i am using the code example Sending packets using Send Buffer:
class Program
{
static void Main(string[] args)
{
string file = #"C:\file_1.pcap";
string file2 = #"C:\file_2.pcap";
// Retrieve the device list from the local machine
IList<LivePacketDevice> allDevices = LivePacketDevice.AllLocalMachine;
// Take the selected adapter
PacketDevice selectedOutputDevice = allDevices[1];
SendPackets(selectedOutputDevice, file);
SendPackets(selectedOutputDevice, file2);
}
static void SendPackets(PacketDevice selectedOutputDevice, string file)
{
// Retrieve the length of the capture file
long capLength = new FileInfo(file).Length;
// Chek if the timestamps must be respected
bool isSync = false;
// Open the capture file
OfflinePacketDevice selectedInputDevice = new OfflinePacketDevice(file);
using (PacketCommunicator inputCommunicator = selectedInputDevice.Open(65536, PacketDeviceOpenAttributes.Promiscuous, 1000))
{
using (PacketCommunicator outputCommunicator = selectedOutputDevice.Open(100, PacketDeviceOpenAttributes.Promiscuous, 1000))
{
// Allocate a send buffer
using (PacketSendBuffer sendBuffer = new PacketSendBuffer((uint)capLength))
{
// Fill the buffer with the packets from the file
Packet packet;
while (inputCommunicator.ReceivePacket(out packet) == PacketCommunicatorReceiveResult.Ok)
{
//outputCommunicator.SendPacket(packet);
sendBuffer.Enqueue(packet);
}
// Transmit the queue
outputCommunicator.Transmit(sendBuffer, isSync);
inputCommunicator.Dispose();
}
outputCommunicator.Dispose();
}
//inputCommunicator.Dispose();
}
}
}
In order to send packet Pcap.Net offers 2 ways:
Send buffer.
Send each packet using SendPacket().
Now after finish to send my 2 files (like in my example) i want to use the Dispose() to free resources.
when using the first option all works fine and this finish to handle my 2 Pcap files.
When using the second option SendPacket() (currently in my code example this is as a comments) after the first file finish my application is closing and not reach to the second file.
I try it also in Console Application and in WPF and in both cases same result.
With UI (WPF) my application GUI just close without any error.
Any suggestions what could cause this ?
When you use the using keyword it means you implicitly call Dispose() at the end of the scope.
If you call Dispose() explicitly as well, it means you call Dispose() twice on the same instance, which is likely to crash your program.

How can I simulate SerialPort interactions for testing?

I'm about to start developing a small app (C#) that communicates with a PLC and a testing unit via Serial Ports - this is my first venture into this area.
In essence, I am going to send the PLC a signal to start an operation, and then I am going to wait for the result of that operation from the test unit (which will be independently communicating with the PLC) to return a ASCII string.
Depending on the content of that string, I may want to listen to a signal from the PLC...
It's all new to me, so at the moment, I'm just researching System.IO.Ports.SerialPort; digression: are there third part products out there than simplify interaction with the Serial Port, or are the built-in classes as good as you will get? I'm thinking of ease of use as opposed to better features.
However, it will be a few weeks before the hardware is available for development and testing, so I was wondering how I could simulate communication to/from the serial port so that I can start developing my app?
[I don't yet know how the PLC and the PC are due to communicate - I understand it will be binary rather than text, but at the moment, that is all I know.]
Abstract away your serial port comms behind an interface so that you can code your app against the interface and then test with a 'fake' implementation. When you've got the hardware for the real thing, you can code up the 'real' implementation of the interface and swap out the fake one.
So for example, you'd have an interface
public interface ISerialComms
{
void SendMessage(string message)
}
and you'd code your app against that interface using a fake implementation:
public class FakeSerialComms : ISerialComms
{
public void SendMessage(string message)
{
//some implementation
}
}
Hope that helps!
I've had some success in the past using com0com.
There are two pieces of software that I have found invaluable while doing serial port work.
Free Serial Port Monitor
http://www.serial-port-monitor.com
Despite the cheesy name, it is actually quite useful. Note that you should have it stop listening to your port if you go to unplug a USB-to-Serial converter. Otherwise it can crash (well... wait indefinitely on exit, which is annoying). It doesn't have to put itself in the middle of a serial connection to sniff data. It monitors the IO using the Win32 API.
Franson Serial Port Tools
http://franson.com/serialtools/
Or.. any loopback software really. There are lots out there. This allows you to send data and receive it within software. If you end up doing any GPS work, Franson also has a nice GPS simulator so you don't have to sit outside the whole time to debug code.
Finally, if you have had enough with the built-in serial class and its horrendous shortcomings, then you need a replacement, and going straight to the Win32 API will take forever.
CommStudio
I have found CommStudio to be absolutely rock solid. Quite frankly, after spending 5 months researching and buying other options, it is the only one that works perfectly with removable USB adapters. All of the other solutions have issues when the device is plugged back in. You can download their free "Express" version here: http://www.componentsource.com/products/commstudio/downloads.html?rv=42917
I have wrote an article on this topic using Virtual Serial Port Driver 9.0 standard using Microsoft SerialPort Class (Sytem.IO.Ports), it is of course possible to use any other comm port tool.
In the software I create 2 virtual ports COM1 and COM2.
I use COM1 to emulate as data sender.
I use COM2 to receive what ever being send from COM1.
This is helpful if you are developing Embedded or IoT solution.
Emulator (in this example as random accelerometer)
private static bool _continue;
private static SerialPort _serialPort;
public static void Main()
{
var stringComparer = StringComparer.OrdinalIgnoreCase;
var readThread = new Thread(Read);
_serialPort = new SerialPort
{
PortName = "COM1",
ReadTimeout = 500,
WriteTimeout = 500
};
_serialPort.Open();
_continue = true;
readThread.Start();
while (_continue)
{
var x = ValueGenerator();
var y = ValueGenerator();
var z = ValueGenerator();
var message = $"x:{x};y:{y};z:{z}";
if (stringComparer.Equals("quit", message))
{
_continue = false;
}
else
{
_serialPort.WriteLine(message);
Thread.Sleep(200);
}
}
readThread.Join();
_serialPort.Close();
}
public static double ValueGenerator()
{
const int range = 1;
var random = new Random();
return random.NextDouble() * range;
}
public static void Read()
{
while (_continue)
{
try
{
var message = _serialPort.ReadLine();
Console.WriteLine(message);
}
catch (TimeoutException) { }
}
}
And my data receiver is almost similar
private static bool _continue;
private static SerialPort _serialPort;
public static void Main()
{
var stringComparer = StringComparer.OrdinalIgnoreCase;
var readThread = new Thread(Read);
_serialPort = new SerialPort
{
PortName = "COM2",
ReadTimeout = 500,
WriteTimeout = 500
};
_serialPort.Open();
_continue = true;
readThread.Start();
while (_continue)
{
var message = Console.ReadLine();
if (stringComparer.Equals("quit", message))
{
_continue = false;
}
else
{
_serialPort.WriteLine(message);
}
}
readThread.Join();
_serialPort.Close();
}
public static void Read()
{
while (_continue)
{
try
{
var message = _serialPort.ReadLine();
Console.WriteLine(message);
}
catch (TimeoutException) { }
}
}
Disclaimer: the link of this guideline refer to my personal web site.
I like David's answer above but if your looking to do integration tests and actually test your serial port communication I have used and application called ViN soft virtual serial cable in the past to basically create 2 serial ports on your machine that are connected by a virtual cable.
Also if you have a serial port on your development machine you could use it to connect to another machine that has a serial port and write an application that will basically simulate the communication of the PLC.
I would prefer to use a combination of both David's method and this method to ensure proper testing.
There is another resource out there that emulates serial ports for windows if anyone else is still looking for decent serial debugging tools.
The 32-bit version is free and seems pretty decent. It's called Virtual Serial Ports Emulator.
Very old but still might be useful to some. Instead of relying on COM interaction, just use the SerialPort.BaseStream to communicate with the port. This allows you to simply use a standard stream interface for communication, in other words, doesn't matter if you use serial ports, TCP connections, or even file streams. Perfect for simulation.

Categories