I have a timer event that executes the following code. I'm trying to read from a TCP connection for a specific string, but it doesn't seem like the buffer gets updated on each passing timer tick event. The source that I'm getting this data from will send 4 different types of strings in a byte array depending on the current state of the system. They are sent to me on a continuous basis. What appears to be happening is that I read just once and then not again for some reason. I've verified that the source I'm receiving data from is indeed sending me different messages, but I don't seem to be able to "read" them. Just the first time only apparently. I tried using the Array.Clear() method, but I still only seem to get one buffer of data and not something that is continuously updating itself. The point of this timer event is to continuously update a C# Windows Form app to alert someone of a fault. I created the "PartnerClient TCPClient at the top of my program.
I'm hopeful that someone has some advice. Perhaps I need an EndRead, but I have tried this approach. Any advice would help
public void FaultDataTimer_Tick(object sender, EventArgs e)
{
byte[] mRx = new byte[9];
byte[] statusBytes = new byte[9];
string strRecv;
string[] values = { "ULI_Fault", "DynoFault", "ULI_AOkay", "DynoAOkay" };
if (PartnerClient.Connected == true)
{
try
{
PartnerClient.GetStream().BeginRead(mRx, 0, mRx.Length, null, PartnerClient);
}
catch (Exception exc)
{
MessageBox.Show(exc.Message);
}
for (int i = 0; i < 9; i++)
{
statusBytes[i] = mRx[i];
}
strRecv = Encoding.ASCII.GetString(statusBytes);
if (values.Any(strRecv.Contains) || values.Any(strRecv.Contains))
{
if (strRecv == "ULI_Fault")
{
uliPanel.BackColor = Color.Red;
}
else if (strRecv == "DynoFault")
{
dynoPanel.BackColor = Color.Red;
}
else if (strRecv == "ULI_AOkay")
{
uliPanel.BackColor = greenColor;
}
else if (strRecv == "DynoAOkay")
{
dynoPanel.BackColor = greenColor;
}
}
}
Array.Clear(mRx, 0, mRx.Length);
}
Related
I'm creating a UWP program for Raspberry Pi. One of the functions of the program is to send and receive some data from an Arduino.
The problem is when I try sending data to the Arduino rapidly and many times, I end up with System.Runtime.InteropServices.COMException The operation identifier is not valid. originating from DataWriter.DetachStream().
Sending the data rapidly works just fine, up until a certain amount it seems, where I get the exception thrown.
With "rapid", I mean using an auto clicker to click a button to send data each millisecond.
I've not tried sending data slowly many times in a row to reproduce the issue, as this would probably take a long time (seeing it takes about 10-20 seconds with 1ms delay between transmissions.
I've been searching for a solution to this problem for way too many hours, but I can't seem to find any related questions/solutions.
public sealed partial class LightControl : Page
{
int Alpha;
int Red;
int Green;
int Blue;
// This is the handler for the button to send data
private void LightButton_Click(object sender, RoutedEventArgs e)
{
if (!(sender is Button button) || button.Tag == null) return;
string tag = button.Tag.ToString();
Alpha = int.Parse(tag.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
Red = int.Parse(tag.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
Green = int.Parse(tag.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
Blue = int.Parse(tag.Substring(6, 2), System.Globalization.NumberStyles.HexNumber);
SendLightData();
}
public async void SendLightData()
{
await ArduinoHandler.Current.WriteAsync(ArduinoHandler.DataEnum.LightArduino,
ArduinoHandler.DataEnum.Light, Convert.ToByte(LightConstants.LightCommand.LightCommand),
Convert.ToByte(Red), Convert.ToByte(Green), Convert.ToByte(Blue), Convert.ToByte(Alpha),
WriteCancellationTokenSource.Token);
}
}
public class ArduinoHandler
{
// Code for singleton behaviour. Included for completeness
#region Singleton behaviour
private static ArduinoHandler arduinoHandler;
private static Object singletonCreationLock = new Object();
public static ArduinoHandler Current
{
get
{
if (arduinoHandler == null)
{
lock (singletonCreationLock)
{
if (arduinoHandler == null)
{
CreateNewArduinoHandler();
}
}
}
return arduinoHandler;
}
}
public static void CreateNewArduinoHandler()
{
arduinoHandler = new ArduinoHandler();
}
#endregion
private DataWriter dataWriter;
private Object WriteCancelLock = new Object();
public async Task WriteAsync(DataEnum receiver, DataEnum sender,
byte commandByte1, byte dataByte1, byte dataByte2, byte dataByte3,
byte dataByte4, CancellationToken cancellationToken)
{
try
{
dataWriter = new DataWriter(arduinos[receiver].OutputStream);
byte[] buffer;
Task<uint> storeAsyncTask;
lock (WriteCancelLock)
{
buffer = new byte[8];
buffer[0] = Convert.ToByte(receiver);
buffer[1] = Convert.ToByte(sender);
buffer[2] = commandByte1;
buffer[3] = dataByte1;
buffer[4] = dataByte2;
buffer[5] = dataByte3;
buffer[6] = dataByte4;
buffer[7] = Convert.ToByte('\n');
cancellationToken.ThrowIfCancellationRequested();
dataWriter.WriteBytes(buffer);
storeAsyncTask = dataWriter.StoreAsync().AsTask(cancellationToken);
}
uint bytesWritten = await storeAsyncTask;
Debug.Write("\nSent: " + BitConverter.ToString(buffer) + "\n");
}
catch (Exception e)
{
Debug.Write(e.Message);
}
finally
{
dataWriter.DetachStream(); // <--- I've located the exception to originate from here, using the debugger in Visual Studio
dataWriter.Dispose();
}
}
public enum DataEnum
{
Light = 0x01,
Piston = 0x02,
PC = 0x03,
LightArduino = 0x04
}
}
I would expect the Raspberry Pi to send the data to the Arduino, but after a while with rapid data transmission, the exception is thrown.
Update
I tried using a local variable for the dataWriter as suggested below, but this causes strange behavior after a while with rapid data transmission. Just as if it slows down. It is worth noting that I don't get an exception anymore.
Quite hard trying to explain how it behaves, but the Debug.Write logs the message I'm sending (which works fine). However, after a while, it seems to "slow down", and even after I stop clicking, the data is being sent once every second or so. It works completely fine up until this point. So I'm wondering if there is a limit of some sort I'm hitting?
Update 2
I seem to have found a rather "hacky" and weird solution to the problem.
If I use Serial.write() on the Arduino to send the data back to the Raspberry Pi, it seems to have fixed the issue somehow.
If anyone knows how this worked, I'd be very interested to know :)
const int payloadSize = 8;
byte payload[payloadSize]
int numBytes;
// Called each time serial data is available
void serialEvent()
{
numBytes = Serial.available();
if (numBytes == payloadSize)
{
for (int i = 0; i < payloadSize; i++)
{
payload[i] = Serial.read();
Serial.write(payload[i]); // <--- This line fixed the issue for whatever reason
}
}
checkData(); // Function to do something with the data
for (int i = 0; i < payloadSize; i++)
{
payload[i] = None;
}
numBytes = 0;
}
Your problem originates from the fact that you are using a fire-and-forget approach of working with async method. When you call SendLightData() in quick succession, it doesn't wait for the previous WriteAsync operation to complete.
Once the execution reaches the first actual await expression - which is the await storeAsyncTask line, the UI thread is freed up to handle another button click.
This new button click can start executing and overwrite the dataWriter field in the same instance of ArduinoHandler. When the first storeAsyncTask finishes executing, it will actually datach the dataWriter of the second call, not its own. This can lead to multiple different sorts of issues and race conditions.
So you must make sure that it is not possible to click the button before the previous operation actually executes. You could use a boolean flag for that as a simple solution.
private bool _isWorking = false;
public async void SendLightData()
{
if (!_isWorking)
{
try
{
_isWorking = true;
await ArduinoHandler.Current.WriteAsync(ArduinoHandler.DataEnum.LightArduino,
ArduinoHandler.DataEnum.Light, Convert.ToByte(LightConstants.LightCommand.LightCommand),
Convert.ToByte(Red), Convert.ToByte(Green), Convert.ToByte(Blue), Convert.ToByte(Alpha),
WriteCancellationTokenSource.Token);
}
finally
{
_isWorking = false;
}
}
This will ensure that two operations never execute simultaneously.
Other solution could be to not store the data writer as a field and just have it as a local variable. When you avoid all shared state between the calls, you can safely know that there will be no race condition stemming from overwriting.
I made a server-client program with auto connection.
But the program only works if I first start the server application because the client needs to connect.
My goal is for the client to check with a delay of 2 seconds if the server is already created.
I made a while statement for a trial and error cycle
try
{
int a = 1;
while (a == 1)
{
cliente.Connect(IP_End);
if (cliente.Connected)
{
connectRead();
a = 2;
}
else
{
while (!cliente.Connected)
{
int milliseconds = 2000;
Thread.Sleep(milliseconds);
cliente.Connect(IP_End);
MessageBox.Show(text);
if (cliente.Connected)
{
connectRead();
}
}
}
}
}
catch(SocketException se)
{
MessageBox.Show(se.Message);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
the error is :
No connection can be made because the target computer actively refused them 192.168.254.28:100
the method is here :
private void connectRead()
{
STW = new StreamWriter(cliente.GetStream());
STR = new StreamReader(cliente.GetStream());
STW.AutoFlush = true;
backgroundWorker1.RunWorkerAsync();
backgroundWorker1.WorkerSupportsCancellation = true;
}
Based on the code you almost got it right. I believe the "Connect" extension method should update the value within the client. If it does the way I'd do it would be to introduce a separate method that returns a boolean and it would help to simplify the code. For example:
private bool ConnectClient(SomeClient clientToConnect, IPAddress ipToConnectTo, int delay)
{
System.Threading.Thread.Sleep(delay);
clientToConnect.Connect(ipToConnectTo);
return clientToConnect.Connected;
}
try
{
bool successfulConnection;
while (!successfulConnection)
{
successfulConnection = ConnectClient(yourClient, "10.10.10.10", 2000);
}
}
catch
{
// ...
}
Without knowing more about errors or desired functionality, I can tell you will call connectRead(); twice if you hit the second loop. To fix this replace your second loop with this:
while (!cliente.Connected)
{
int milliseconds = 2000;
Thread.Sleep(milliseconds);
cliente.Connect(IP_End);
if (cliente.Connected)
{
connectRead();
a = 2;
}
}
EDIT:
I also took out the MessageBox because that seems unnecessary and will pause the program until you hit Ok on the MessageBox.
EDIT 2:
Question on actively refused connections
Question on determination of open port
I am facing a problem that a thread in my application runs into a deadlock when I want to close the serial port of a gsm terminal. This problem is well known here and here but all advices in these threads didn't help me.
/// <summary>
/// Closes the COM port, prevents reading and writing
/// </summary>
public void Stop()
{
Debug.WriteLine("stop called");
var block = true;
var bgw = new BackgroundWorker
{
WorkerReportsProgress = false,
WorkerSupportsCancellation = false,
};
bgw.DoWork += (s, e) =>
{
if (!CanAccessPort())
return;
try
{
_serialPort.DataReceived -= Read;
GC.ReRegisterForFinalize(_serialPort.BaseStream);
_serialPort.Close();
_isOpen = false;
}
catch (Exception ex)
{
throw new Exception(PORTERROR, ex);
}
};
bgw.RunWorkerCompleted += (s, e) =>
{
Debug.WriteLine("block is set to false =)");
block = false;
};
bgw.RunWorkerAsync();
while (block)
Thread.Sleep(250);
}
The code above runs forever when _serialPort.Close() is executed. As a recommended advice I read about running the close operation in a separate thread. I tried with BackgroundWorker and Thread classes but nothing did work. Using AutoResetEvent as suggested in another thread did not work either. Before closing the port I am sending some commands to it and receive several results but it won't close. When I run a simple command line program that starts the port, reads data and tries to close it, everything works and even without threads.
What could cause the deadlock? I am not doing any GUI related stuff as mentioned in almost all other answers.
DataReceived event handler code here:
/// <summary>
/// Reads input from the COM interface and invokes the corresponding event depending on the input
/// </summary>
private void Read(object sender, SerialDataReceivedEventArgs e)
{
var buffer = new char[1024];
var counter = 0;
_keepRunning = true;
if (_timeout == null)
{
// timeout must be at least 3 seconds because when sending a sms to the terminal the receive notification (+CSDI) can be lost with less timeout
_timeout = new Timer(3000);
_timeout.Elapsed += (s, ev) =>
{
_keepRunning = false;
_timeout.Stop();
};
}
_timeout.Start();
// cancel condition: no more new data for 3 seconds or "OK"/"ERROR" found within the result
while (_keepRunning)
{
var toRead = _serialPort.BytesToRead;
if (toRead == 0)
{
Thread.Sleep(100);
continue;
}
_timeout.Stop();
_timeout.Start();
counter += _serialPort.Read(buffer, counter, toRead);
// ok or error found in result string
var tmp = new string(buffer).Replace("\0", "").Trim();
if (tmp.EndsWith("OK") || tmp.EndsWith("ERROR"))
{
_timeout.Stop();
_keepRunning = false;
}
}
// remove empty array slots from the back
var nullTerminalCounter = 0;
for (var i = buffer.Length - 1; i != 0; i--)
{
if (buffer[i] == '\0')
{
nullTerminalCounter++;
continue;
}
break;
}
Array.Resize(ref buffer, buffer.Length - nullTerminalCounter);
var str = new String(buffer).Trim();
// result must be something different than incoming messages (+CMTI: \"MT\", 25)
if (!((str.StartsWith("+CMTI") || str.StartsWith("+CSDI")) && str.Length < 20))
{
// when an incoming message is received, it does not belong to the command issued, so result has not yet arrived, hence port is still blocked!
_isBlocked = false;
Debug.WriteLine("port is unblocked");
}
var args = new CommandReturnValueReceivedEventArgs
{
ResultString = str
};
OnCommandReturnValueReceived(this, args);
}
if (toRead == 0)
{
Thread.Sleep(100);
continue;
}
This code is the basic source of the deadlock. The rule for SerialPort.Close() is that it can only close the serial port when none of the event handlers for SerialPort are active. Problem is, your DataReceived event handler is almost always active, waiting for data. This was not the intended use for the event. You are supposed to read whatever is available from the serial port, typically appending bytes to a buffer and get out. The event fires again when more bytes are available.
while (_keepRunning)
Looks like you discovered this problem and tried to fix it with the Timer. That doesn't work either, in a very ratty way that's very difficult to debug. A bool variable is not a proper synchronization primitive, like ManualResetEvent. The while() loop will not see the _keepRunning variable turn to false when you target x86 and run the Release build of your program. Which enables the jitter optimizer, it is apt to store the variable in a cpu register. Declaring the variable volatile is required to suppress that optimization.
I suspect, but can't guarantee, that using volatile will solve your problem. And you probably want to set _keepRunning to false before calling Close() so you don't get the timeout delay.
A more structural fix is however indicated. Rewrite the DataReceived event handler so it never loops waiting for data. Given that this appears to be code to talk to a modem, a simple _serialPort.ReadLine() call should be all that's required.
The issues I had went away if I ensured that Open and Close never were called at the same time by any thread. I did this by using a lock.
The essence is the following.
lock(_port)
_port.Open(.....);
...
lock(_port)
_port.Close(.....);
I use this code for receiving scanlines:
StateObject stateobj = (StateObject)ar.AsyncState;
stateobj.workSocket.BeginReceive(new System.AsyncCallback(VideoReceive), stateobj);
UdpClient client = stateobj.workSocket;
IPEndPoint ipendp = new IPEndPoint(IPAddress.Any, 0);
byte[] data = client.EndReceive(ar, ref ipendp);
BinaryReader inputStream = new BinaryReader(new MemoryStream(data));
inputStream.BaseStream.Position = 0;
int currentPart = inputStream.ReadInt32();
if (currentPart == part)
{
int a = 0;
int colum = inputStream.ReadInt32();
for (; a < packets.GetLength(1); a++)
{
packets[colum, a, 2] = inputStream.ReadByte();
packets[colum, a, 1] = inputStream.ReadByte();
packets[colum, a, 0] = inputStream.ReadByte();
}
receiverCheck++;
}
else if (currentPart != part)
{
part++;
mask2.Data = packets;
pictureBox1.BeginInvoke(new MethodInvoker(() => { pictureBox1.Image = mask2.ToBitmap(); }));
int colum = inputStream.ReadInt32();
for (int a = 0; a < packets.GetLength(1); a++)
{
packets[colum, a, 2] = inputStream.ReadByte();
packets[colum, a, 1] = inputStream.ReadByte();
packets[colum, a, 0] = inputStream.ReadByte();
}
}
After all scanlines have been received the image displayed in pictureBox.
This should work, but have a lot lost packets even on localhost (only ~ 95 of 480), so I have striped image. I found a similar problem here.
Answer:
private void OnReceive(object sender, SocketAsyncEventArgs e)
{
TOP:
if (e != null)
{
int length = e.BytesTransferred;
if (length > 0)
{
FireBytesReceivedFrom(Datagram, length, (IPEndPoint)e.RemoteEndPoint);
}
e.Dispose(); // could possibly reuse the args?
}
Socket s = Socket;
if (s != null && RemoteEndPoint != null)
{
e = new SocketAsyncEventArgs();
try
{
e.RemoteEndPoint = RemoteEndPoint;
e.SetBuffer(Datagram, 0, Datagram.Length); // don't allocate a new buffer every time
e.Completed += OnReceive;
// this uses the fast IO completion port stuff made available in .NET 3.5; it's supposedly better than the socket selector or the old Begin/End methods
if (!s.ReceiveFromAsync(e)) // returns synchronously if data is already there
goto TOP; // using GOTO to avoid overflowing the stack
}
catch (ObjectDisposedException)
{
// this is expected after a disconnect
e.Dispose();
Logger.Info("UDP Client Receive was disconnected.");
}
catch (Exception ex)
{
Logger.Error("Unexpected UDP Client Receive disconnect.", ex);
}
}
}
Answer has method FireBytesReceivedFrom(), but I can't find it. How can I use this code? And does this code help?
UDP doesn't guarantee that all packets will be received, it that they will arrive in any particular order. So even if you get this "working" be aware that it could (will probably) fail at some point.
When you call BeginReceive, you are starting an sync read. When data arrives, your event handler will be called, and it is then that you need to call EndReceive. Currently you are calling EndReceive immediately, which is probably why things are going wrong.
Some other notes:
I'd suggest that you don't try to be clever and re-use the buffer, as that could result in you losing data by overwriting data while you are trying to read it. Start off simple and add optimizations like this after you have it working well.
Also, the goto could be causing havoc. You seem to be trying to use it to retry, but this code is running IN the data received event handler. Event handlers should handle the event in the most lightweight way possible and then return, not start looping... especially as the loop here could cause a re-entrant call to the same event handler.
With async comms, you should start a read and exit. When you eventually receive the data (your event handler is called), grab it and start a new async read. Anything more complex than that is likely to cause problems.
The Fire... method you are missing probably just raises (fires) an event to tell clients that the data has arrived. this is the place where you should be grabbing the received data and doing something with it.
If you are using an example to build this code then I suggest you look for a better example. (in any case I would always recommend trying to find 3 examples so you can compare the implementations, as you will usually learn a lot more about something this way)
I am trying to read from several serial ports from sensors through microcontrollers. Each serial port will receive more than 2000 measurements (each measurement is 7 bytes, all in hex). And they are firing at the same time. Right now I am polling from 4 serial ports. Also, I translate each measurement into String and append it to a Stringbuilder. When I finish receiving data, they will be ouput in to a file. The problem is the CPU consumption is very high, ranging from 80% to 100%.
I went though some articles and put Thread.Sleep(100) at the end. It reduces CPU time when there is no data coming. I also put Thread.Sleep at the end of each polling when the BytesToRead is smaller than 100. It only helps to a certain extent.
Can someone suggest a solution to poll from serial port and handle data that I get? Maybe appending every time I get something causes the problem?
//I use separate threads for all sensors
private void SensorThread(SerialPort mySerialPort, int bytesPerMeasurement, TextBox textBox, StringBuilder data)
{
textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = ""; }));
int bytesRead;
int t;
Byte[] dataIn;
while (mySerialPort.IsOpen)
{
try
{
if (mySerialPort.BytesToRead != 0)
{
//trying to read a fix number of bytes
bytesRead = 0;
t = 0;
dataIn = new Byte[bytesPerMeasurement];
t = mySerialPort.Read(dataIn, 0, bytesPerMeasurement);
bytesRead += t;
while (bytesRead != bytesPerMeasurement)
{
t = mySerialPort.Read(dataIn, bytesRead, bytesPerMeasurement - bytesRead);
bytesRead += t;
}
//convert them into hex string
StringBuilder s = new StringBuilder();
foreach (Byte b in dataIn) { s.Append(b.ToString("X") + ","); }
var line = s.ToString();
var lineString = string.Format("{0} ---- {2}",
line,
mySerialPort.BytesToRead);
data.Append(lineString + "\r\n");//append a measurement to a huge Stringbuilder...Need a solution for this.
////use delegate to change UI thread...
textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = line; }));
if (mySerialPort.BytesToRead <= 100) { Thread.Sleep(100); }
}
else{Thread.Sleep(100);}
}
catch (Exception ex)
{
//MessageBox.Show(ex.ToString());
}
}
}
this is not a good way to do it, it far better to work on the DataReceived event.
basically with serial ports there's a 3 stage process that works well.
Receiving the Data from the serial port
Waiting till you have a relevant chunk of data
Interpreting the data
so something like
class DataCollector
{
private readonly Action<List<byte>> _processMeasurement;
private readonly string _port;
private SerialPort _serialPort;
private const int SizeOfMeasurement = 4;
List<byte> Data = new List<byte>();
public DataCollector(string port, Action<List<byte>> processMeasurement)
{
_processMeasurement = processMeasurement;
_serialPort = new SerialPort(port);
_serialPort.DataReceived +=SerialPortDataReceived;
}
private void SerialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
while(_serialPort.BytesToRead > 0)
{
var count = _serialPort.BytesToRead;
var bytes = new byte[count];
_serialPort.Read(bytes, 0, count);
AddBytes(bytes);
}
}
private void AddBytes(byte[] bytes)
{
Data.AddRange(bytes);
while(Data.Count > SizeOfMeasurement)
{
var measurementData = Data.GetRange(0, SizeOfMeasurement);
Data.RemoveRange(0, SizeOfMeasurement);
if (_processMeasurement != null) _processMeasurement(measurementData);
}
}
}
Note: Add Bytes keeps collecting data till you have enough to count as a measurement, or if you get a burst of data, splits it up into seperate measurements.... so you can get 1 byte one time, 2 the next, and 1 more the next, and it will then take that an turn it into a measurement. Most of the time if your micro sends it in a burst, it will come in as one, but sometimes it will get split into 2.
then somewhere you can do
var collector = new DataCollector("COM1", ProcessMeasurement);
and
private void ProcessMeasurement(List<byte> bytes)
{
// this will get called for every measurement, so then
// put stuff into a text box.... or do whatever
}
First of all consider reading Using Stopwatches and Timers in .NET. You can break down any performance issue with this and tell exactly which part of Your code is causing the problem.
Use SerialPort.DataReceived Event to trigger data receiving process.
Separate receiving process and data manipulation process. Store Your data first then process.
Do not edit UI from reading loop.
I guess what you should be doing is adding an event handler to process incoming data:
mySerialPort.DataReceived += new SerialDataReceivedEventHandler(mySerialPort_DataReceived);
This eliminates the need to run a separate thread for each serial port you listen to. Also, each DataReceived handler will be called precisely when there is data available and will consume only as much CPU time as is necessary to process the data, then yield to the application/OS.
If that doesn't solve the CPU usage problem, it means you're doing too much processing. But unless you've got some very fast serial ports I can't imagine the code you've got there will pose a problem.