private void Form1_Load(object sender, EventArgs e) { // mostly just initialization is happening here in form_load log_to_file_now = false; m_pSerialPort = new SerialPort(); m_pSerialPort.DataReceived += new SerialDataReceivedEventHandler(m_pSerialPort_DataReceived); foreach (string port in System.IO.Ports.SerialPort.GetPortNames()) { cbPort.Items.Add(port); } // select default UI items cbParity.SelectedIndex = 0; cbStopBits.SelectedIndex = 0; cbDataBits.SelectedIndex = 0; cbPort.SelectedIndex = 0; cbBaudRate.Text = "9600"; cbPollDelay.SelectedText = DEFAULT_POLL_TIME_IN_MS.ToString(); cbPollTimeFormat.SelectedIndex = 0; // default polling time delay poll_time_in_ms = DEFAULT_POLL_TIME_IN_MS; // background var for the channel //current_channel = channel_sel.undefined; // make sure the right things are disabled and enabled change_ui_to_disconnected(); // the lock to synchronize cross thread communications monitor_lock = new object(); thready = new Thread(new ThreadStart(thread_invoke)); thready.Name = "th"; thready.IsBackground = true; thready.Start(); progress_bar_tick = new System.Threading.Timer(progress_bar_tick_run, null, 0, 100); // serial buffer received_buffer = new char[1024]; // semaphore is owned by parent safe_to_access_buffer = new Semaphore(0, 1); safe_to_access_status = new Semaphore(0, 1); // allow anyone access to the semaphore safe_to_access_buffer.Release(); safe_to_access_status.Release(); safe_to_access_status.WaitOne(); current_status = connection_status.unconnected; safe_to_access_status.Release(); clear_buffer(); pbPollCountdown.Value = 100; }
// will change the output of the power supply // 0=off // 1=on private void change_output(int state) { if (state != 0 && state != 1) { Console.WriteLine("Attempting to change to unknown state! (value={0})", state.ToString()); return; } connection_status t = pause_polling_thread(); thread_safe_update_status_bar("Changing output."); // send the change output to x command string s = ""; s = quick_clear_write_read("OUTP:STAT " + (state == 0 ? "OFF" : "ON") + "\n"); current_status = t; if (state == 0) { ui_to_output_off(); // stop the polling thread (pulse below) safe_to_access_status.WaitOne(); current_status = connection_status.connected; safe_to_access_status.Release(); } else if (state == 1) { ui_to_output_on(); // resume the polling thread safe_to_access_status.WaitOne(); current_status = connection_status.identified; safe_to_access_status.Release(); } else { ui_to_output_undefined(); } // clear existing output // the limits, current channel, and ovp can stay thread_safe_current_text_update(UNKNOWN_CURRENT_TEXT); thread_safe_voltage_text_update(UNKNOWN_VOLTAGE_TEXT); thread_safe_power_text_update(UNKNOWN_POWER_TEXT); resume_polling_thread(t); }
private string connection_status_to_string(connection_status c) { switch (c) { case connection_status.connected: return "connected"; case connection_status.done: return "done"; case connection_status.identified: return "identified"; case connection_status.polling: return "polling"; case connection_status.unconnected: return "unconnected"; case connection_status.undefined: default: return "undefined"; } }
// changes state from connected to ID'd, will start polling continuously on success private void btnIdentify_Click(object sender, EventArgs e) { // return if not connected if (btnConnect.Enabled == true) return; // going to do a couple things before letting the user proceed, // but all of these checks need to be successful int init_device = 0; init_device = quick_identify(); if (init_device <= 0) { return; } // failing a limit read isn't fatal, so the return value can be ignored quick_limit_read(); // failling to identify the current channel is not fatal, so the return value can be ignored quick_channel_read(); // failing to read the over voltage protection value is not fatal, so the return value can be ignored quick_ovp_read(); // If the output is currently off, there's no need to start the polling thread yet int output = quick_output_onoff_read(); btnStopStart.Text = (output==0?"Start":"Stop"); // now that the device is known, list the available power modes populate_modes(); // adds available items quick_power_mode_read(); // reads current power mode and selects it in the combo box // and update the max values in the 'set' boxes update_max_values(); // start the polling if things are good to go if (init_device > 0 && output == 1) { safe_to_access_status.WaitOne(); current_status = connection_status.identified; safe_to_access_status.Release(); lock (monitor_lock) { Monitor.Pulse(monitor_lock); } } else { safe_to_access_status.WaitOne(); current_status = connection_status.undefined; safe_to_access_status.Release(); } return; }
private void btnStopStart_Click(object sender, EventArgs e) { UInt64 i = lock_connected_ui(); // the thread is running and needs to be stopped if (btnStopStart.Text == "Stop") { thread_safe_update_status_bar("Stopping pending communications."); // first, stop the polling thread lock (monitor_lock) { // attempt to wait, but if it's already stopped, then timeout if (current_status == connection_status.polling) { Monitor.Wait(monitor_lock); // wait for any pending commands to finish Thread.Sleep(THREAD_AFTER_INTERRUPT_WAIT); } else Monitor.Wait(monitor_lock, THREAD_WAIT_TIMEOUT); safe_to_access_status.WaitOne(); current_status = connection_status.connected; safe_to_access_status.Release(); } thread_safe_update_status_bar("Ready."); btnStopStart.Text = "Start"; } // the thread is stopped and needs to be resumed else if (btnStopStart.Text == "Start") { lock (monitor_lock) { safe_to_access_status.WaitOne(); current_status = connection_status.identified; safe_to_access_status.Release(); Monitor.Pulse(monitor_lock); } btnStopStart.Text = "Stop"; } unlock_connected_ui(i); }
// this is the main part of the application, and it happens mostly in the background. // after connecting, the device needs to be ID'd. After that succeeds, polling will // commence to continuously retrieve the current and voltage values. The user can // choose to "start logging" which will save these polled values to a file in realtime; // no values are stored for playback or anything. void thread_invoke() { int ok_to_go = 0; int do_it_twice = 0; double volt; double curr; bool monitor_response = true; // run this thread until the program ends while (true) { // wait for a device to be found... ok_to_go = 0; lock (monitor_lock) { while (ok_to_go == 0) { //Console.WriteLine("Checking current_status..."); if (current_status == connection_status.polling) { //Console.WriteLine("current_status=polling"); ok_to_go = 1; // true on interrupt, false on timeout if (poll_time_in_ms - SERIAL_RESPONSE_WAIT_TIME > 10) monitor_response = Monitor.Wait(monitor_lock, poll_time_in_ms - SERIAL_RESPONSE_WAIT_TIME); else monitor_response = Monitor.Wait(monitor_lock, poll_time_in_ms); } else if (current_status == connection_status.identified) { //Console.WriteLine("current_status=identified"); // if it's identified, we're about to begin polling safe_to_access_status.WaitOne(); current_status = connection_status.polling; safe_to_access_status.Release(); // true on interrupt, false on timeout if (poll_time_in_ms - SERIAL_RESPONSE_WAIT_TIME > 10) monitor_response = Monitor.Wait(monitor_lock, poll_time_in_ms - SERIAL_RESPONSE_WAIT_TIME); else monitor_response = Monitor.Wait(monitor_lock, poll_time_in_ms); } else { //Console.WriteLine("current_status=else"); // all other cases need to pause this thread indefinitely ok_to_go = 0; Monitor.Wait(monitor_lock); monitor_response = false; } // if this was woken up early, then actually don't do anything if (monitor_response == true) { ok_to_go = 0; } // it's possible that the status changed while inside Monitor.Wait, so make // sure that this thread is still polling safe_to_access_status.WaitOne(); if (current_status != connection_status.polling) { ok_to_go = 0; } safe_to_access_status.Release(); } } //Console.WriteLine("Done with lock -- ok_to_go={0}, monitor_response={1}, current_status={2}", ok_to_go, (monitor_response ? "true" : "false"), connection_status_to_string(current_status)); volt = 0.0; curr = 0.0; // the voltage and current code are practically the same, so those two // are going to be done in a loop. The first time is current, the second // is voltage clear_buffer(); //Console.WriteLine("Sending curr/volt command."); // disconnecting at the wrong time can cause this thread to // attempt to write to a com port that doesn't exist try { if (do_it_twice == 0) { m_pSerialPort.Write("MEAS:CURR?\n"); } else if (do_it_twice == 1) { m_pSerialPort.Write("MEAS:VOLT?\n"); } } catch { } // wait for a response from the device. This can probably be fine tuned a little // based on the baud rate and other factors. Thread.Sleep(SERIAL_RESPONSE_WAIT_TIME); // received buffer is filled in the event handler for the serial port device string s; safe_to_access_buffer.WaitOne(); int len = 0; while (received_buffer[len] != '\0') len++; s = new string(received_buffer, 0, len); safe_to_access_buffer.Release(); // the response is going to be a number in scientific notation double d = new double(); try { d = double.Parse(s); } catch { d = double.NaN; if (ckBaudErrorFix.Checked == true) { // scale the error correcting time based on the baud rate. // lower bauds have to move in larger amounts than higher baud rates int baud = thread_safe_get_baud_rate(); if (baud == 0) baud = 1; int move_factor = (int)(336000/baud); if (move_factor == 0) move_factor = 1; poll_time_in_ms += move_factor; thread_safe_poll_delay_text_update(poll_time_in_ms.ToString()); // static wait period. The power supply needs some time to recover Thread.Sleep(250); } } // update the UI, and set the current and voltage values that will // be written to the log file. if (do_it_twice == 0 && current_status == connection_status.polling) { curr = d; Console.WriteLine("received current response: {0}", d); if (System.Double.IsNaN(d)) { thread_safe_current_text_update(UNKNOWN_CURRENT_TEXT); } else { thread_safe_current_text_update(d.ToString(CURRENT_TEXT_FORMAT)); } } else if (do_it_twice == 1 && current_status == connection_status.polling) { volt = d; Console.WriteLine("received voltage response: {0}", d); if (System.Double.IsNaN(d)) { thread_safe_voltage_text_update(UNKNOWN_VOLTAGE_TEXT); } else { thread_safe_voltage_text_update(d.ToString(VOLTAGE_TEXT_FORMAT)); } } do_it_twice++; if (do_it_twice == 2) do_it_twice = 0; // update power usage if (!Double.IsNaN(curr) && !Double.IsNaN(volt) && current_status == connection_status.polling) { thread_safe_power_text_update((curr * volt).ToString(POWER_TEXT_FORMAT)); } else { thread_safe_power_text_update(UNKNOWN_POWER_TEXT); } // done with both voltage and current // check to see if they need to be saved to a file if (log_to_file_now == true) { StringBuilder sb = new StringBuilder(); sb.Append(DateTime.Now.ToString()); sb.Append(", "); sb.Append(volt.ToString()); sb.Append(", "); sb.AppendLine(curr.ToString()); byte[] ba = Encoding.ASCII.GetBytes(sb.ToString()); file_to_save_to.Write(ba, 0, ba.Length); } // if someone else is trying to halt this thread, allow that to happen now. lock (monitor_lock) { //Console.WriteLine("pulsing monitor_lock. (end of main loop)"); Monitor.Pulse(monitor_lock); } // also, attempt to re-sync the status bar pbPollCountdown.BeginInvoke((ThreadStart)delegate { lock (pbPollCountdown) { pbPollCountdown.Value = 0; } }); } // end of while(true) }
/// <summary> /// Happens when the disconnect button is clicked to disconnect /// the serial port from the device. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnDisconnect_Click(object sender, EventArgs e) { safe_to_access_status.WaitOne(); current_status = connection_status.unconnected; safe_to_access_status.Release(); btnDisconnect.Enabled = false; btnConnect.Enabled = true; status_bar.Text = "Disconnected"; // wait just a bit to see if any current communications will finish Thread.Sleep(300); // I believe closing the port will always trigger an error m_pSerialPort.Close(); change_ui_to_disconnected(); }
private void resume_polling_thread(connection_status c) { // don't let the polling thread read in the response from the previous commands clear_buffer(); // release the polling thread, if it was stopped earlier lock (monitor_lock) { safe_to_access_status.WaitOne(); current_status = c; safe_to_access_status.Release(); //Console.WriteLine("Resuming polling thread, status={0}", connection_status_to_string(c)); if (current_status == connection_status.polling) Monitor.Pulse(monitor_lock); } thread_safe_update_status_bar("Ready"); }
private void btnConnect_Click(object sender, EventArgs e) { try { #region Set Serial Settings m_pSerialPort.BaudRate = Convert.ToInt32(cbBaudRate.Text); m_pSerialPort.DataBits = Convert.ToInt32(cbDataBits.Text); switch (cbParity.Text) { case "None": m_pSerialPort.Parity = Parity.None; break; case "Odd": m_pSerialPort.Parity = Parity.Odd; break; case "Even": m_pSerialPort.Parity = Parity.Even; break; default: break; } switch (cbStopBits.Text) { case "1": m_pSerialPort.StopBits = StopBits.One; break; case "2": m_pSerialPort.StopBits = StopBits.Two; break; default: break; } m_pSerialPort.PortName = cbPort.Text; m_pSerialPort.DtrEnable = true; m_pSerialPort.RtsEnable = true; #endregion m_pSerialPort.Open(); status_bar.Text = "Connected to " + m_pSerialPort.PortName.ToString() + " at " + m_pSerialPort.BaudRate.ToString(); btnConnect.Enabled = false; btnDisconnect.Enabled = true; safe_to_access_status.WaitOne(); current_status = connection_status.connected; safe_to_access_status.Release(); change_ui_to_connected(); } catch(Exception ex) { MessageBox.Show(ex.Message, "COM Port Connection Problem"); safe_to_access_status.WaitOne(); current_status = connection_status.unconnected; safe_to_access_status.Release(); return; } }
private int quick_identify() { int ret_val = 0; try { string s = quick_clear_write_read("*IDN?\n"); // check to make sure this is [probably] the power supply device if (s.IndexOf("Agilent Technologies") >= 0) { current_device = s; if (s.Length > 3) s = s.Substring(0, s.Length - 2); status_bar.Text = "Found device: " + s; // don't enable logging until a device is found change_ui_to_identified(); ret_val = 1; } else { current_device = ""; ret_val = -1; } } catch { MessageBox.Show("Something went wrong getting device id"); safe_to_access_status.WaitOne(); current_status = connection_status.undefined; safe_to_access_status.Release(); ret_val = -2; } return ret_val; }
private connection_status pause_polling_thread() { thread_safe_update_status_bar("Stopping pending communications."); connection_status t = connection_status.undefined; // first, stop the polling thread lock (monitor_lock) { // attempt to wait, but if it's already stopped, then timeout if (current_status == connection_status.polling) { Monitor.Wait(monitor_lock); // wait for any pending commands to finish Thread.Sleep(THREAD_AFTER_INTERRUPT_WAIT); //Console.WriteLine("pausing polling thread (connection_status=polling)"); t = connection_status.polling; } else { Monitor.Wait(monitor_lock, THREAD_WAIT_TIMEOUT); safe_to_access_status.WaitOne(); t = current_status; safe_to_access_status.Release(); //Console.WriteLine("pausing polling thread (connection_status={0})", connection_status_to_string(t)); } safe_to_access_status.WaitOne(); current_status = connection_status.unconnected; safe_to_access_status.Release(); } return t; }
public void SetPlayerConnection(int ID, connection_status status) { ID--; PlayerInfos[ID].status = status; }