Multithreading and Serial Ports

Okay .. this will be lengthy, but I need to explain some background first.

This part of my software is designed to sort items passing through a conveyor belt. I use Modbus for conveyor belt. Modbus will open the gate at a specific time so that the element passes through the gate. Elements will pass through specific gates based on weight.

I control the sensor to determine when the item is on the scale. When the sensor is blocked, the element is weighed and sent to the appropriate gate. Timers are set to open / close the gate.

My code will work for this. The problem is that it will not work for multiple items. By this, I mean that when the gate is open, the sensor is not monitored until the gate is closed. Therefore, when item A is on its way to the gate, item B will not be weighted on a scale when it locks the sensor. I could have up to 8 items on the line right away. Here is the code I'm running now:

private void SensorThread_DoWork(object sender, DoWorkEventArgs e) { if (SensorThread.CancellationPending == true) e.Cancel = true; else { ReadSensor(); } } private void SensorThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { //if sensor is blocked if (sensorstatus == 0) { //the timers just start the thread scaleTimer.Start(); } else { sensorTimer.Start(); } } private void ScaleThread_DoWork(object sender, DoWorkEventArgs e) { if (ScaleThread.CancellationPending == true) { e.Cancel = true; } else { ReadScale(); //SaveWeight(); prevgate = gate; gate = DetermineGate(); SetOpenDelay(); SetDuration(); } } private void ScaleThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { //if gate = 0, this means the weight of meat on scale //is not in any weight range. Meat runs off the end. if (gate == 0) { txtStatus.Invoke(new UpdateStatusCallback(UpdateStatus), new object[] { meatweight.ToString() + "lbs is out of range"}); sensorTimer.Start(); } else { //open gate //then close gate } } 

This code works fine, I just need to consider a few elements in a line. Any suggestions????

I also tried the following:

 private void SensorThread_DoWork(object sender, DoWorkEventArgs e) { if (SensorThread.CancellationPending == true) e.Cancel = true; else { ReadSensor(); } } private void SensorThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { sensorTimer.Start(); } private void ScaleThread_DoWork(object sender, DoWorkEventArgs e) { if (ScaleThread.CancellationPending == true) { e.Cancel = true; } else { //sensor blocked if (sensorstatus == 0) { ReadScale(); //SaveWeight(); prevgate = gate; gate = DetermineGate(); SetOpenDelay(); SetDuration(); //if gate = 0, this means the weight of meat on scale //is not in any weight range. Meat runs off the end. if (gate == 0) { txtStatus.Invoke(new UpdateStatusCallback(UpdateStatus), new object[] { meatweight.ToString() + "lbs is out of range"}); } else { //open gate //close gate } } } private void ScaleThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { scaleTimer.Start(); } 

When I did this, I started both threads when the start button was pressed. I get all kinds of exceptions and the program eventually throws a SEHException and crashes. Other errors I get say: "Serial port is already open" or "I / O error."

+6
source share
3 answers

I think you need something like this. I don’t know if locks are needed, but I added them for security since you get errors.

 private void SensorThread_DoWork(object sender, DoWorkEventArgs e) { int sensor = 1; while(!SensorThread.CancellationPending == true) { int newSensor; lock(this) { newSensor = ReadSensor(); } //sensor state changed if(newSensor != sensor) { //sensor was 1 and changed to 0 if(newSensor==0) { scaleTimer.Start(); } sensor = newSensor; } Thread.Sleep(1); } e.Cancel = true; } private void ScaleThread_DoWork(object sender, DoWorkEventArgs e) { //sensor blocked //if (sensorstatus == 0) { lock(this) { ReadScale(); } //SaveWeight(); prevgate = gate; gate = DetermineGate(); lock(this) { SetOpenDelay(); SetDuration(); } //if gate = 0, this means the weight of meat on scale //is not in any weight range. Meat runs off the end. if (gate == 0) { txtStatus.Invoke(new UpdateStatusCallback(UpdateStatus), new object[] { meatweight.ToString() + "lbs is out of range"}); } else { lock(this) { //open gate } lock(this) { //close gate } } } 
+2
source

I would suggest that it is best to create a separate thread for each serial port. This approach will neither require nor prohibit any similarity in the way the ports are handled, avoid any interference between the ports and scale to a reasonable extent (using a thread for each of the 32 ports would be great; using a thread for each of 1000 will be bad). Although you should avoid creating threads that will simply run for a short time and stop working or create a really huge number of threads, using a dedicated stream for each serial port ensures that there is a stream ready to process it when the data comes in.

+1
source

I notice that you do not have loops in the DoWork methods in the stream. That would be a great place to start. The worker thread must be a loop that does not return until CancellationPending is set to true. They will not loop on their own, because you have it in the thread - the thread will work until it is executed, and then exit.

Edited to add: what you think is missing is that you need to separate the code that controls the scale and the code that opens and closes the gate. One way to do this is to have an infinite loop that controls the scale, and when it detects something, it starts a new thread that handles the opening and closing of the gate.

+1
source

Source: https://habr.com/ru/post/911888/


All Articles