I have a program that reports the size of a folder structure over Serial COM every X seconds. It also has the ability to report a list of all the files within the folder structure, when a user sends a command also over Serial COM.
As an example: A user is getting the size info every 10 seconds, at some point they want to know the list of files. So they send a “1” over the COM port and the program then starts reporting the files back.
The issue I am having is that two functions can’t write to the COM Port at the same time and so it starts throwing exceptions.
What I would like to do is have both functions wait until the other is finished. Here is my code.
This is the simple fucntion for writing to the COM Port:
private void ComWrite(string msg)
{
ComPort.Write(msg);
}
I call this from these two fucntions:
This one reports the file names:
private void GetFileNames()
{
fileNames = Directory.GetFiles(textBox1.Text, "*.wav", SearchOption.AllDirectories);
for (int i = 0; i < fileNames.Length; i++)
{
ComWrite((fileNames[i] + "\r\n"));
}
}
This one is on a timer and reports the folder size:
public void OnTimedEvent(object source, ElapsedEventArgs elapsed)
{
folderSize = DirSize(new DirectoryInfo(textBox1.Text)) / 1000000;
string labelText = folderSize.ToString() + "Mb";
label3.Text = labelText;
if (checkBox1.Checked)
{
try
{
ComWrite(labelText + "\r\n");
label9.Text = labelText;
}
catch (Exception)
{
MessageBox.Show("Please Open COMPORT before sending command");
}
}
}
How can I implement asynchronous functions, or some other method of stopping them from falling over one another?
EDIT: Requested Code.
Here is setup the COM Port on form load.
private void Form1_Load(object sender, EventArgs e)
{
label5.Text = "Idle";
ComPort.BaudRate = Convert.ToInt32("9600");
ComPort.DataBits = Convert.ToInt16("8");
ComPort.StopBits = (StopBits)Enum.Parse(typeof(StopBits), "One");
ComPort.Handshake = (Handshake)Enum.Parse(typeof(Handshake), "None");
ComPort.Parity = (Parity)Enum.Parse(typeof(Parity), "None");
}
And you open the COM Port you select via the combobox with a button click here:
private void btnPortState_Click(object sender, EventArgs e)
{
try
{
if (btnPortState.Text == "COMPort Closed / Click to Open")
{
btnPortState.BackColor = Color.Green;
btnPortState.Text = "COMPort Open / Click To Close";
ComPort.PortName = Convert.ToString(cboPorts.Text);
ComPort.Open();
}
else if (btnPortState.Text == "COMPort Open / Click To Close")
{
btnPortState.Text = "COMPort Closed / Click to Open";
btnPortState.BackColor = Color.Firebrick;
ComPort.Close();
}
}
catch (Exception)
{
MessageBox.Show("You must select a COMPORT before opening it.");
}
}
I think what you want is a mutual exclusion for any transaction made to your com port.
If the problem is that 2 different threads can start to write to the comport at the same time this can be solved by using a semaphore.
In your case I am guessing that it wouldn't be good if the directory size would be transmitted while you are sending the file names. So you can't simply synchronize inside ComWrite
.
Instead, I would propose the following:
In Class:
//semaphore so only 1 Thread at a time can pass through
private static Semaphore semaphore = new Semaphore(1, 1); //(1,1) -> see comments
In OnTimedEvent
:
if (checkBox1.Checked)
{
try
{
semaphore.WaitOne(); //Increases semaphore counter
ComWrite(labelText + "\r\n");
semaphore.Release();
label9.Text = labelText;
}
(...)
And
private void GetFileNames()
{
semaphore.WaitOne();
(...) //writing the file names to ComPort
semaphore.Release();
}
Semaphores are kind of working like a counter. If the counter reaches its max count no more threads can pass by the Wait
call and are waiting for their turn. releasing the semaphore basically tells the counter to decrease and waiting threads are now allowed to enter.