C
C#17mo ago
FacePalmGamer

✅ Correct way to pass data between threads?

I have an event on a simple Winform reading from the serial port. The issue is I would like to output the data onto a winform component, but because its on an even it happens on a different thread and I get the error Cross-thread operation not valid: Control 'textBoxRecieve' accessed from a thread other than the thread it was created on. so what is the correct way to pass this data? I know I could do something like a channel do make a cross thread communication system, but that seems excessive and I figure there is an easier way I do now know
public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
textBoxRecieve.Text = textBoxRecieve.Text + Environment.NewLine + ((SerialPort)sender).ReadExisting();
}
public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
textBoxRecieve.Text = textBoxRecieve.Text + Environment.NewLine + ((SerialPort)sender).ReadExisting();
}
That was the function running and erroring. I was just trying to make a simple ECHO test debugListen.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler); and that is how I was setting up the event
51 Replies
sibber
sibber17mo ago
gui framework usually provide a dispatcher for winforms thats Control.Invoke
FacePalmGamer
FacePalmGamerOP17mo ago
Am i changing my event to be called from the invoker or am I adding the invoke method to my handler function? not really sure how this works so just trying to understand
sibber
sibber17mo ago
either would work but id probably do that from the handler
FacePalmGamer
FacePalmGamerOP17mo ago
i was gonna say changing the handler function seems easier
private delegate void DELEGATE(string data);

public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
Delegate del = new DELEGATE(ProcessData);
this.Invoke(del, (((SerialPort)sender).ReadExisting()));
}

private void ProcessData(string data)
{
textBoxRecieve.Text = textBoxRecieve.Text + Environment.NewLine + data;
}
private delegate void DELEGATE(string data);

public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
Delegate del = new DELEGATE(ProcessData);
this.Invoke(del, (((SerialPort)sender).ReadExisting()));
}

private void ProcessData(string data)
{
textBoxRecieve.Text = textBoxRecieve.Text + Environment.NewLine + data;
}
This works. Thank you. Posting code incase anyone else finds this later I do have a question. how does the Invoke method work? Is it telling the main thread to pause what its doing and run this code now? or is it some other way?
cap5lut
cap5lut17mo ago
the ui thread has something like an internal list for delegates, which it will process on each iteration. basically something like
while (isRunning)
{
processInvocations();
processEvents();
renderUI();
}
while (isRunning)
{
processInvocations();
processEvents();
renderUI();
}
btw u dont need the delegate, u can simply use Action<T> for that and u should prepare the data in ur handler and only pass that then:
public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
if (sender is SerialPort port)
{
string data = port.ReadExisting();
this.Invoke(ProcessData, data);
}
}
public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
if (sender is SerialPort port)
{
string data = port.ReadExisting();
this.Invoke(ProcessData, data);
}
}
FacePalmGamer
FacePalmGamerOP17mo ago
that makes sense. is there a reason to do the if() instead of just casting it? shouldnt it always be a SerialPort
cap5lut
cap5lut17mo ago
hmmm, actually in this case probably not, as it should crash if the sender isnt a SerialPort to show its a bug, i guess or do the pattern matching and throw a more meaningful exception in the else branch
FacePalmGamer
FacePalmGamerOP17mo ago
also im guessing I should prepare data before i use it since its on its own thread already?
cap5lut
cap5lut17mo ago
yeah, u want to do the minimum amount of work on the ui thread else it might freeze if there is too much going on
FacePalmGamer
FacePalmGamerOP17mo ago
ok this is a bit off topic, but im not sure if there is a better way to do this. Is there a better way to identify data coming from the serial port? Currently im just sending formated strings and parsing them
public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
string data = ((SerialPort)sender).ReadExisting();
string dataG = data;
string keyItem = data.Substring(0, data.IndexOf(' '));
data = data.Substring(data.IndexOf(' ') + 1);
string keyType = data.Substring(0, data.IndexOf(' '));
data = data.Substring(data.IndexOf(' ') + 1);
double value = double.Parse(data);
switch (keyItem)
{
case "mobile1":
if (keyType == "lat")
RCRGroundStation.mobilePosition[1].Item1 = value;
else if (keyType == "lon")
RCRGroundStation.mobilePosition[1].Item2 = value;
break;
case "baseStation":
if (keyType == "lat")
RCRGroundStation.basePosition.Item1 = value;
else if (keyType == "lon")
RCRGroundStation.basePosition.Item2 = value;
break;
case "rocket":
if (keyType == "lat")
RCRGroundStation.rocketPosition.Item1 = value;
else if (keyType == "lon")
RCRGroundStation.rocketPosition.Item2 = value;
break;
}
this.Invoke(ProcessData, dataG);
}

private void ProcessData(string data)
{
textBoxRecieve.Text = textBoxRecieve.Text + data;

main.UpdateCords();
}
public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
string data = ((SerialPort)sender).ReadExisting();
string dataG = data;
string keyItem = data.Substring(0, data.IndexOf(' '));
data = data.Substring(data.IndexOf(' ') + 1);
string keyType = data.Substring(0, data.IndexOf(' '));
data = data.Substring(data.IndexOf(' ') + 1);
double value = double.Parse(data);
switch (keyItem)
{
case "mobile1":
if (keyType == "lat")
RCRGroundStation.mobilePosition[1].Item1 = value;
else if (keyType == "lon")
RCRGroundStation.mobilePosition[1].Item2 = value;
break;
case "baseStation":
if (keyType == "lat")
RCRGroundStation.basePosition.Item1 = value;
else if (keyType == "lon")
RCRGroundStation.basePosition.Item2 = value;
break;
case "rocket":
if (keyType == "lat")
RCRGroundStation.rocketPosition.Item1 = value;
else if (keyType == "lon")
RCRGroundStation.rocketPosition.Item2 = value;
break;
}
this.Invoke(ProcessData, dataG);
}

private void ProcessData(string data)
{
textBoxRecieve.Text = textBoxRecieve.Text + data;

main.UpdateCords();
}
btw thanks for all the help this makes alot more sense now. Thought i would ask this question though since there might be a better way to do communication
cap5lut
cap5lut17mo ago
tbh i have no clue about how serial ports work, the first thing that catches my eye is the question if the data is always one complete message, or if it could also be multiple messages at once or even fragmented messages or maybe its fragmented because the connection died such cases arent handled at all right now
FacePalmGamer
FacePalmGamerOP17mo ago
thats very true. but that would be handled on the slave device This is for a system of pi picos talking with each other making a tracking system for a rocket. this UI is for a ground station so that handling would be for the pico attached to it to group the message, but you are right I should still add a catch incase a message fragments
Omnissiah
Omnissiah17mo ago
serial port doesn't have the concept of message, it's just data although there are some settings that can influence this
cap5lut
cap5lut17mo ago
the parsing itself could be done via regex as well, would be a bit cleaner
FacePalmGamer
FacePalmGamerOP17mo ago
I have done next to 0 work with regex. I know it exists and i have used it in path of exile(a game) and thats it
cap5lut
cap5lut17mo ago
so its just stream based, so it it can be multiple and/or partial data at once
FacePalmGamer
FacePalmGamerOP17mo ago
yeah I have a virtual serial port rn to test with
FacePalmGamer
FacePalmGamerOP17mo ago
It is just sending raw bytes
FacePalmGamer
FacePalmGamerOP17mo ago
as it recieves them it puts them in the next port (in the case of my virtual com)
cap5lut
cap5lut17mo ago
do the picos send some kind of marker for an end of a message?
Omnissiah
Omnissiah17mo ago
yes, from the pov of c# it's the usual stream reader, so you say read 2000 bytes or even read 1 byte and when it's ready it returns that to you
FacePalmGamer
FacePalmGamerOP17mo ago
no clue I am getting my picos tomorrow to start testing. I was planning on adding a separation byte incase i send longer messages that way i can just data.split('~')
Omnissiah
Omnissiah17mo ago
you could search if pico offers flow control settings or something but in the end who cares
FacePalmGamer
FacePalmGamerOP17mo ago
yeah thats too much work for a system that maybe will recieve 100 messages a second i dont think i will be overflowing a pico or the serial communication line
Omnissiah
Omnissiah17mo ago
what 1000/sec? 1000 bytes/sec you mean
FacePalmGamer
FacePalmGamerOP17mo ago
extra 0 100/sec it could get around 20 or 30 updates from each tracker a second each tracker is sending 3 doubles so ~800 bytes its using 115200 baud so its good for 14400 bytes
Omnissiah
Omnissiah17mo ago
baud is bit/s, not byte
FacePalmGamer
FacePalmGamerOP17mo ago
ah im still well over
Omnissiah
Omnissiah17mo ago
eh you know it's still relatively busy for a serial
cap5lut
cap5lut17mo ago
u know this would be a lot easier if the protocol would be binary instead of text based 😂
FacePalmGamer
FacePalmGamerOP17mo ago
thats what testing is for.
Omnissiah
Omnissiah17mo ago
are you sure? binary could be a mess text is somewhat easier
FacePalmGamer
FacePalmGamerOP17mo ago
this guy just wants to use bit banging for everything
cap5lut
cap5lut17mo ago
why would it be a mess? i would say its a lot easier, the only real trap would be difference in endianness, which can be easily covered
FacePalmGamer
FacePalmGamerOP17mo ago
I believe on the lower level serial is just hex so its not too different. its just readable
Omnissiah
Omnissiah17mo ago
text is easier because human understandable and debuggable almost every protocol i met was text based
FacePalmGamer
FacePalmGamerOP17mo ago
SPI is not i believe
Omnissiah
Omnissiah17mo ago
probably it's like that because it's meant to be operated by a console or somethings
FacePalmGamer
FacePalmGamerOP17mo ago
i2c is also only hex i believe but i dont know enough about them to advocate one way or another
Omnissiah
Omnissiah17mo ago
iot for home automation is text
FacePalmGamer
FacePalmGamerOP17mo ago
what protocol does home automation use?
Omnissiah
Omnissiah17mo ago
openwebnet is one of them
FacePalmGamer
FacePalmGamerOP17mo ago
well it makes sense. Alot of that data is probably outputting to the user so sending strings and chars vs hex makes sense
Omnissiah
Omnissiah17mo ago
i don't exactly know what made them take this route
FacePalmGamer
FacePalmGamerOP17mo ago
well this derailed quite a bit form the original question. Thanks for all the help, I'll look into different ways to parse and setup my communication network
cap5lut
cap5lut17mo ago
good luck 😄 also please dont forget to $close the thread (if u have questions later it would be better to open a new thread anyway)
MODiX
MODiX17mo ago
Use the /close command to mark a forum thread as answered
Omnissiah
Omnissiah17mo ago
to me if you have that amout of updates you could use a queue (buffer) between serial and ui just that
FacePalmGamer
FacePalmGamerOP17mo ago
wouldnt really be a queue. because I want the newest information as soon as possible. If im getting too many inputs I just throwout the ones i miss will do If i need to throttle it. i just ignore inputs read the newest ones and disregard the rest
Omnissiah
Omnissiah17mo ago
sure sure, i was just thinking on how to do this in a non async/await architecture you would still need a timeout for the response from the form/controls
FacePalmGamer
FacePalmGamerOP17mo ago
ah i see. Well the input for this is a pi pico, the pico only runs C so it will be bare bones. I believe any modern laptop should be able to handle the through put of a pi, but i could be wrong the pi is the place where i except a bottleneck to be if there even is one Aight ima close this now. I have to go to work. Ty for the help
Want results from more Discord servers?
Add your server