I am interfacing with a device and sending and receiving around 2000 bytes from the device using c#. I find that if I click my connect button and just let it run, the process completes 80% of the time, and 20% of the time a read time out occurs. This in and of itself is a problem.
Additionally, if I move my windows around at all while it is reading data a read timeout occurs. I thought it may have been a priority issue, so I tried messing with the priority of the background worker and interface threads, but no luck.
Has anyone had this happen to them before? And advice is appreciated. Below is what code is relevant, if you think you need more code let me know and I will post it as well. Thanks in advance.
Where the functions are called from:
private void connectButton_Click(object sender, RoutedEventArgs e) //Connect button clicked
{
if (!Global.isConnected)
{
agentradioread.connect((string)portsOpen.SelectedValue, this);
form.deviceConnecting(this);
}
else
{
agentradioread.disconnect();
form.deviceDisconnect(this);
}
}
private void loadSettings_Click(object sender, RoutedEventArgs e) //Loads settings
{
if (Global.isConnected)
{
agentradioread.readValues(this);
}
}
private void sendSettings_Click(object sender, RoutedEventArgs e) //Sends settings
{
if (Global.isConnected)
{
agentradioread.readTime();
string minutes = (Create.sysval[10].ToString().Length == 1) ? "0" + Create.sysval[10].ToString() : Create.sysval[10].ToString();
string date = Create.sysval[11].ToString() + "/" + Create.sysval[12].ToString() + "/20" + Create.sysval[13].ToString();
string time = Create.sysval[9].ToString() + ":" + minutes;
MessageBoxResult result = MessageBox.Show("The current device date and time is: " + date + " " + time + "\r\nIs this time correct?", "Date & Time Verification", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes) //Time is correct
{
Create.sysval[9] = 255;
}
else //Time is incorrect
{
dateTimeChange dateTime = new dateTimeChange();
dateTime.ShowDialog();
}
agentradioread.sendValues(this);
}
}
The functions called:
public void connect(string selectedPort, agentRadio agentR) //Connects device and reads values
{
agentSerial = new SerialPort(selectedPort, 9600, Parity.None, 8, StopBits.One);
connectWorker = new BackgroundWorker();
connectWorker.WorkerReportsProgress = true;
connectWorker.DoWork += new DoWorkEventHandler(connectWorker_DoWork);
connectWorker.ProgressChanged += new ProgressChangedEventHandler(connectWorker_ProgressChanged);
connectWorker.RunWorkerAsync(agentR);
}
public void readTime() //Reads time into sysval locations
{
agentSerial.DiscardInBuffer();
agentSerial.DiscardOutBuffer();
agentSerial.Write(Global.TIME, 0, Global.TIME.Length); //begin send values
loadTime();
}
public void readValues(agentRadio agentR) //Read values
{
readWorker = new BackgroundWorker();
readWorker.WorkerReportsProgress = true;
readWorker.DoWork += new DoWorkEventHandler(readWorker_DoWork);
readWorker.ProgressChanged += new ProgressChangedEventHandler(readWorker_ProgressChanged);
agentSerial.DiscardInBuffer();
agentSerial.DiscardOutBuffer();
readWorker.RunWorkerAsync(agentR);
}
public void sendValues(agentRadio agentR) //Send values
{
sendWorker = new BackgroundWorker();
sendWorker.WorkerReportsProgress = true;
sendWorker.DoWork += new DoWorkEventHandler(sendWorker_DoWork);
sendWorker.ProgressChanged += new ProgressChangedEventHandler(sendWorker_ProgressChanged);
agentSerial.DiscardInBuffer();
agentSerial.DiscardOutBuffer();
sendWorker.RunWorkerAsync(agentR);
}
public void restoreDevice(agentRadio agentR) //Restore device to defaults
{
agentSerial.DiscardInBuffer();
agentSerial.DiscardOutBuffer();
agentSerial.Write(Global.RESTORE, 0, Global.RESTORE.Length); //device restore command
MessageBox.Show(agentSerial.ReadByte().ToString());
try
{
Global.restoring = true;
readValues(agentR);
}
catch
{
MessageBox.Show("Your device was restored, but a read time out occured. You need to go to the connect tab and reconnect to the device, and your settings will update.", "Device Restore Notice", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
public void disconnect() //Disconnects device
{
if (Global.isConnected == true)
{
try
{
agentSerial.DiscardInBuffer();
agentSerial.DiscardOutBuffer();
agentSerial.Write(Global.EXIT_PROGRAM, 0, Global.EXIT_PROGRAM.Length);
}
catch
{
//leave option to reConnect
}
agentSerial.Close();
Global.isConnected = false;
}
}
/*Methods that cannot be called from elsewhere in the application*/
private void connectWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) //Updates progressbar while device is connecting
{
ProgressBar progressBar = e.UserState as ProgressBar;
App.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Send,
new Action(
delegate()
{ progressBar.Value = e.ProgressPercentage; }));
}
private void connectWorker_DoWork(object sender, DoWorkEventArgs e) //Connects and read values
{
startRead((agentRadio)e.Argument);
}
private void readWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) //Alerts user when values are loaded
{
ProgressBar progressBar = e.UserState as ProgressBar;
App.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Send,
new Action(
delegate()
{ progressBar.Value = e.ProgressPercentage; }));
}
private void readWorker_DoWork(object sender, DoWorkEventArgs e) //Reads values
{
agentRadio agentR = (agentRadio)e.Argument;
App.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Send,
new Action(
delegate()
{ form.deviceReading(agentR); }));
agentSerial.Write(Global.READ_VALUES, 0, Global.READ_VALUES.Length); //begin read values
byte key = (byte)agentSerial.ReadByte();
if (Global.START_COMMAND == key) //Verify continue key
{
object progressBarObject = new object();
progressBarObject = agentR.connectProgress;
for (int i = 1; i < 2247; i++) //Loop through values, calling read function and updating progress bar
{
int progress = (int)(((float)i / 2246.0) * 100);
readWorker.ReportProgress(progress, progressBarObject);
try
{
readData(i, agentR);
agentSerial.Write(Global.GO_AHEAD, 0, Global.GO_AHEAD.Length);
agentSerial.DiscardInBuffer();
agentSerial.DiscardOutBuffer();
}
catch
{
break;
}
}
}
else //Key failed and displays error
{
App.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Send,
new Action(
delegate()
{ form.deviceDisconnect(agentR); }));
MessageBox.Show("Connection Failed, Invalid Key");
}
}
private void sendWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) //Alerts user when values are sent
{
ProgressBar progressBar = e.UserState as ProgressBar;
App.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Send,
new Action(
delegate()
{ progressBar.Value = e.ProgressPercentage; }));
}
private void sendWorker_DoWork(object sender, DoWorkEventArgs e) //Sends values
{
agentRadio agentR = (agentRadio)e.Argument;
App.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Send,
new Action(
delegate()
{ form.deviceSending(agentR); }));
agentSerial.Write(Global.SEND_VALUES, 0, Global.SEND_VALUES.Length); //begin send values
byte key = (byte)agentSerial.ReadByte();
if (Global.START_COMMAND == key) //Verify continue key
{
object progressBarObject = new object();
progressBarObject = agentR.connectProgress;
for (int i = 1; i < 659; i++) //Loops and sends data to device
{
int progress = (int)(((float)i / 658.0) * 100);
sendWorker.ReportProgress(progress, progressBarObject);
try
{
byte result = sendData(i, agentR);
if (result != Global.GO_AHEAD[0])
{
MessageBox.Show("Fatal Error");
break;
}
agentSerial.DiscardInBuffer();
agentSerial.DiscardOutBuffer();
}
catch
{
break;
}
}
}
else //Key failed and displays error
{
App.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Send,
new Action(
delegate()
{ form.deviceDisconnect(agentR); }));
MessageBox.Show("Connection Failed, Invalid Key");
}
}
private void loadTime() //Loads device time into sysval locations
{
byte key = (byte)agentSerial.ReadByte();
if (Global.START_COMMAND == key) //Verify continue key
{
for (int i = 0; i < 5; i++)
{
try
{
byte result = (byte)agentSerial.ReadByte();
Create.sysval[9 + i] = result;
}
catch
{
Create.sysval[9 + i] = 0;
}
}
agentSerial.ReadByte();
}
agentSerial.DiscardInBuffer();
agentSerial.DiscardOutBuffer();
}
private string sendInitial() //Sends a ? and returns 7 bytes of data
{
string handshake = "";
agentSerial.Write("?");
for (int i = 0; i < 7; i++)
{
try
{
handshake += agentSerial.ReadByte().ToString();
}
catch
{
break;
}
}
return handshake;
}
private void startRead(agentRadio agentR) //Initializes data send and receive
{
System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Highest;
App.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Send,
new Action(
delegate()
{ agentR.connectProgress.IsIndeterminate = true; }));
agentSerial.ReadTimeout = 5000;
agentSerial.Open();
agentSerial.DiscardInBuffer();
agentSerial.DiscardOutBuffer();
string handshake = sendInitial();
if (handshake == Global.AGENT_RADIO) //Result matches agent radio version
{
agentSerial.Write(Global.READ_VALUES, 0, Global.READ_VALUES.Length); //begin read values
byte key = (byte)agentSerial.ReadByte();
if (Global.START_COMMAND == key) //Verify continue key
{
App.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Send,
new Action(
delegate()
{ agentR.connectProgress.IsIndeterminate = false; }));
object progressBarObject = new object();
progressBarObject = agentR.connectProgress;
for (int i = 1; i < 2247; i++) //Loop through values, calling read function and updating progress bar
{
int progress = (int)(((float)i / 2246.0) * 100);
connectWorker.ReportProgress(progress, progressBarObject);
try
{
readData(i, agentR);
agentSerial.Write(Global.GO_AHEAD, 0, Global.GO_AHEAD.Length);
agentSerial.DiscardInBuffer();
agentSerial.DiscardOutBuffer();
}
catch
{
break;
}
}
}
else //Key failed and displays error
{
agentSerial.Close();
App.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Send,
new Action(
delegate()
{ form.deviceDisconnect(agentR); }));
MessageBox.Show("Connection Failed, Invalid Key");
}
}
else //Result did not match an agent radio version
{
agentSerial.Close(); //Closes port
if (attempt < 3) //Attempt to connect to device 5 times
{
attempt++;
startRead(agentR);
}
else //Displays error to user if connection failed
{
App.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Send,
new Action(
delegate()
{ form.deviceDisconnect(agentR); }));
MessageBox.Show("Connection Failed, Not Agent Radio Version");
}
}
}
private void readData(int iteration, agentRadio agentR) //Reads data for current iteration
{
byte value = 0; //Current value
try
{
value = (byte)agentSerial.ReadByte(); //Reads byte
}
catch
{
agentSerial.Close();
App.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Send,
new Action(
delegate()
{ form.deviceDisconnect(agentR); }));
MessageBox.Show("Connection Failed, Read Timeout"); //Displays message if read timeout occured
}
if (iteration > 0 && iteration < 385) //read schedule
{
double pos = (iteration - 1) / 48;
int i = (int)Math.Floor(pos);
int j = (iteration - 1) - (i * 48);
Create.schedule[i, j] = value;
}
if (iteration > 384 && iteration < 1285) //read alarm history
{
double pos = (iteration - 385) / 9;
int i = (int)Math.Floor(pos);
int j = (iteration - 385) - (i * 9);
Create.alarms[i, j] = value;
}
if (iteration > 1284 && iteration < 1345) //read error log
{
double pos = (iteration - 1285) / 6;
int i = (int)Math.Floor(pos);
int j = (iteration - 1285) - (i * 6);
Create.errors[i, j] = value;
}
if (iteration > 1344 && iteration < 1945) //read voltage history
{
double pos = (iteration - 1345) / 6;
int i = (int)Math.Floor(pos);
int j = (iteration - 1345) - (i * 6);
Create.voltage[i, j] = value;
}
if (iteration > 1944 && iteration < 1973) //read holidays
{
Create.holidays[iteration - 1945] = value;
}
if (iteration > 1972 && iteration < 2168) //read message sequences
{
double pos = (iteration - 1973) / 15;
int i = (int)Math.Floor(pos);
int j = (iteration - 1973) - (i * 15);
Create.messages[i, j] = value;
}
if (iteration > 2167 && iteration < 2196) //read message info
{
Create.recordings[iteration - 2168] = value;
}
if (iteration > 2195 && iteration < 2246) //read sysval
{
Create.sysval[iteration - 2196] = value;
}
if (iteration == 2246 && value == Global.FINISH_COMMAND)
{
if (Global.restoring)
{
App.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Send,
new Action(
delegate()
{ form.deviceConnect(agentR); }));
MessageBox.Show("Your device has been restored, and all settings from the device have been received.", "Device Restore Succesful", MessageBoxButton.OK, MessageBoxImage.Information);
}
else
{
App.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Send,
new Action(
delegate()
{ form.deviceConnect(agentR); }));
Global.isConnected = true;
MessageBox.Show("Your device is connected, and all settings from the device have been received.", "Data Transfer Succesful", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
}
private byte sendData(int iteration, agentRadio agentR) //Sends data for current iteration
{
byte value = 0; //For return later
if (iteration > 0 && iteration < 51) //sysval
{
byte[] toWrite = new byte[1];
toWrite[0] = Create.sysval[iteration - 1];
agentSerial.Write(toWrite, 0, 1);
value = (byte)agentSerial.ReadByte();
}
if (iteration > 50 && iteration < 79) //holiday
{
byte[] toWrite = new byte[1];
toWrite[0] = Create.holidays[iteration - 51];
agentSerial.Write(toWrite, 0, 1);
value = (byte)agentSerial.ReadByte();
}
if (iteration > 78 && iteration < 463) //schedule
{
double pos = (iteration - 79) / 48;
int i = (int)Math.Floor(pos);
int j = (iteration - 79) - (i * 48);
byte[] toWrite = new byte[1];
toWrite[0] = Create.schedule[i, j];
agentSerial.Write(toWrite, 0, 1);
value = (byte)agentSerial.ReadByte();
}
if (iteration > 462 && iteration < 658) //message sequence
{
double pos = (iteration - 463) / 15;
int i = (int)Math.Floor(pos);
int j = (iteration - 463) - (i * 15);
byte[] toWrite = new byte[1];
toWrite[0] = Create.messages[i, j];
agentSerial.Write(toWrite, 0, 1);
value = (byte)agentSerial.ReadByte();
}
if (iteration == 658 && ((byte)agentSerial.ReadByte() == Global.FINISH_COMMAND)) //Last iteration should get finish command
{
App.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Send,
new Action(
delegate()
{ form.deviceConnect(agentR); }));
MessageBox.Show("Your settings have been transfered succesfully to the device.", "Successful Transfer", MessageBoxButton.OK, MessageBoxImage.Information);
value = 17; //Simulates GO_AHEAD character for last iteration of loop
}
return value; //Return character off port, should be GO_AHEAD
}
}
Form class:
public string[] populateAvailablePorts() //Returns string array of open ports
{
String[] portsAvailable = SerialPort.GetPortNames();
return portsAvailable;
}
public void deviceConnect(agentRadio agentR) //Adjusts display for when device is disconnected
{
agentR.portsOpen.IsEnabled = false;
agentR.connectButton.Content = "Disconnect";
agentR.connectButton.IsEnabled = true;
agentR.connectProgress.Value = 100;
agentR.loadSettingsConnect.Content = "Load Settings";
agentR.sendSettingsConnect.Content = "Send Settings";
agentR.loadSettingsConnect.IsEnabled = true;
agentR.sendSettingsConnect.IsEnabled = true;
agentR.DeviceRestore.IsEnabled = true;
agentR.VoltageStack.Children.Clear();
agentR.ErrorStack.Children.Clear();
agentR.Events.Children.Clear();
agentR.DeviceRestore.IsEnabled = true;
display.setDisplay(agentR);
}
public void deviceConnecting(agentRadio agentR) //Adjusts display for when device is disconnected
{
agentR.portsOpen.IsEnabled = false;
agentR.connectButton.Content = "Connecting...";
agentR.connectButton.IsEnabled = false;
agentR.loadSettingsConnect.IsEnabled = false;
agentR.sendSettingsConnect.IsEnabled = false;
agentR.DeviceRestore.IsEnabled = false;
}
public void deviceReading(agentRadio agentR) //Adjusts display for when device is disconnected
{
agentR.connectButton.IsEnabled = false;
agentR.loadSettingsConnect.Content = "Reading Data...";
agentR.loadSettingsConnect.IsEnabled = false;
agentR.sendSettingsConnect.IsEnabled = false;
agentR.DeviceRestore.IsEnabled = false;
}
public void deviceSending(agentRadio agentR) //Adjusts display for when device is disconnected
{
agentR.connectButton.IsEnabled = false;
agentR.sendSettingsConnect.Content = "Sending Data...";
agentR.loadSettingsConnect.IsEnabled = false;
agentR.sendSettingsConnect.IsEnabled = false;
agentR.DeviceRestore.IsEnabled = false;
}
public void deviceDisconnect(agentRadio agentR) //Adjusts display for when device is diconnected
{
agentR.portsOpen.IsEnabled = true;
agentR.connectButton.IsEnabled = true;
agentR.connectButton.Content = "Connect";
agentR.loadSettingsConnect.Content = "Load Settings";
agentR.sendSettingsConnect.Content = "Send Settings";
agentR.connectProgress.Value = 0;
agentR.connectProgress.IsIndeterminate = false;
agentR.loadSettingsConnect.IsEnabled = false;
agentR.sendSettingsConnect.IsEnabled = false;
agentR.DeviceRestore.IsEnabled = false;
}
Don't use the ProgressBar object in your DoWork handler. The ProgressChanged handler (readWorker_ProgressChanged) should get everything it needs to report progress itself.
Also, why are you dispatching the setting of IsIndeterminate?
Also. the ProgressChanged handler will always be called on the UI thread (unless you invoke it directly yourself, there's no need to dispatch anything in readWorker_ProgressChanged.
I would also avoid manually dispatching back to the form for things like deviceReading and deviceDisconnect. That's what the progress event is for.
The reason I would avoid this is because you are sending back to the UI thread for these operations--this means your background thread is blocked until the UI can invoke these actions. If the UI is busy redrawing the screen or handling menu selections, it can't deal with this action. This halts your background work until the UI thread can handle it, thus timing out the serial communications.