using Microsoft.Win32; using System; using System.Diagnostics; using System.IO.Ports; using System.Text; using System.Timers; using System.Windows.Forms; namespace MeetingClock { public class MeetingClockManager { private const string MTC = "MTC"; public enum MeetingClockState { NotInitialized, NoComPort, Disconnected, Connected } public MeetingClockState State { get; private set; } = MeetingClockState.NotInitialized; public string Version { get; private set; } public string Port { get { if (this.serialPort != null && this.serialPort.IsOpen) { return this.serialPort.PortName; } return ""; } } public delegate void MeetingClockStateChangeHandler(MeetingClockManager manager, MeetingClockState oldState, MeetingClockState newState); public event MeetingClockStateChangeHandler StateChanged; private System.Timers.Timer startTimer; private System.Timers.Timer checkTimer; private BluetoothManager bluetoothManager; private SerialPort serialPort; public MeetingClockManager(BluetoothManager bluetoothManager) { this.NewSerialPort(); this.startTimer = new System.Timers.Timer(1000); this.startTimer.AutoReset = false; this.startTimer.Elapsed += ConnectTimer_Elapsed; this.checkTimer = new System.Timers.Timer(30000); this.checkTimer.AutoReset = false; this.checkTimer.Elapsed += CheckTimer_Elapsed; this.checkTimer.Start(); this.bluetoothManager = bluetoothManager; this.bluetoothManager.StateChanged += BluetoothManager_StateChanged; } private void NewSerialPort() { lock (this) { if (this.serialPort != null) { try { this.serialPort.Close(); } catch (Exception) { // ignored } } this.serialPort = new SerialPort(); this.serialPort.BaudRate = 9600; this.serialPort.Parity = Parity.None; this.serialPort.DataBits = 8; this.serialPort.StopBits = StopBits.One; this.serialPort.Encoding = Encoding.GetEncoding("iso8859-1"); this.serialPort.ReadTimeout = 5000; this.serialPort.WriteTimeout = 5000; } } private void BluetoothManager_StateChanged(BluetoothManager manager, BluetoothManager.BluetoothState oldState, BluetoothManager.BluetoothState newState) { switch (newState) { case BluetoothManager.BluetoothState.On: this.startTimer.Start(); break; default: this.Disconnect(); break; } } private void ConnectTimer_Elapsed(object sender, ElapsedEventArgs e) { this.Connect(); } private void Connect() { lock (this) { if (this.State == MeetingClockState.Connected && this.serialPort.IsOpen) { Debug.WriteLine("Meeting clock already connected. Not trying to connect again."); return; } string[] portNames = SerialPort.GetPortNames(); if (portNames.Length == 0) { Debug.WriteLine("No COM ports are available at the moment."); this.ChangeState(MeetingClockState.NoComPort); return; } this.ChangeState(MeetingClockState.Disconnected); this.NewSerialPort(); foreach (string portName in portNames) { Debug.WriteLine(String.Format("Trying to connect to {0}", portName)); try { this.serialPort.PortName = portName; this.serialPort.Open(); if (this.Handshake()) { Debug.WriteLine(String.Format("Successfully connected to meeting clock {0} on port {1}", this.Version, portName)); this.Adjust(); this.ScrollText(""); this.NextDate(DateTime.Now.AddMinutes(-5)); this.ChangeState(MeetingClockState.Connected); return; } else { Debug.WriteLine(String.Format("Failed to connect to meeting clock on port {0}. Invalid Handshake.", portName)); } } catch (Exception exception) { Debug.WriteLine(String.Format("Failed to connect to meeting clock on port {0}. {1}", portName, exception.Message)); } this.Disconnect(); } } } private void Disconnect() { lock (this) { if (this.serialPort.IsOpen) { Debug.WriteLine(String.Format("Closing connection on port {0}", this.serialPort.PortName)); try { this.serialPort.Close(); } catch (Exception) { // ignored } } this.ChangeState(MeetingClockState.Disconnected); this.NewSerialPort(); } } private bool Handshake() { string rx = this.SendAndReceive(MTC); if (rx == null) { return false; } if (rx.StartsWith(MTC)) { this.Version = rx.Substring(MTC.Length).Trim(); return true; } return false; } public void Adjust() { this.SendAndReceive(String.Format("ADJ {0}", DateTime.Now.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss"))); } private string SendAndReceive(string tx) { lock (this) { try { if (this.serialPort.IsOpen) { Debug.WriteLine(String.Format(">>> {0}", tx)); this.serialPort.WriteLine(tx); string rx = this.serialPort.ReadLine().Trim(); Debug.WriteLine(String.Format("<<< {0}", rx)); return rx; } } catch (Exception exception) { Debug.WriteLine(String.Format("Failed to send/receiv on port {0}. {1}", this.serialPort.PortName, exception.Message)); this.Disconnect(); } return null; } } private void ChangeState(MeetingClockState newState) { MeetingClockState oldState = this.State; this.State = newState; if (newState != oldState) { Debug.WriteLine("Meeting Clock state change detected. Old state: {0}, new state {1}.", oldState, newState); if (this.StateChanged != null) { this.StateChanged(this, oldState, newState); } } } private void CheckTimer_Elapsed(object sender, ElapsedEventArgs e) { if (this.serialPort.IsOpen) { if (!this.Handshake()) { this.Disconnect(); } } else if (this.bluetoothManager.State == BluetoothManager.BluetoothState.On) { this.Connect(); } this.checkTimer.Start(); } public void Stop() { this.Disconnect(); this.checkTimer.Stop(); this.startTimer.Stop(); } public int? GetTimeInterval() { try { string rx = this.SendAndReceive("GET TI").Substring(7); return int.Parse(rx); } catch (Exception) { return null; } } public void SetTimeInterval(int value) { try { this.SendAndReceive("SET TI " + value); } catch (Exception) { } } public int? GetDateInterval() { try { string rx = this.SendAndReceive("GET DI").Substring(7); return int.Parse(rx); } catch (Exception) { return null; } } public void SetDateInterval(int value) { try { this.SendAndReceive("SET DI " + value); } catch (Exception) { } } public int? GetTemperatureInterval() { try { string rx = this.SendAndReceive("GET CI").Substring(7); return int.Parse(rx); } catch (Exception) { return null; } } public void SetTemperatureInterval(int value) { try { this.SendAndReceive("SET CI " + value); } catch (Exception) { } } public int? GetBrightness() { try { string rx = this.SendAndReceive("GET IN").Substring(7); return int.Parse(rx); } catch (Exception) { return null; } } public int? GetScrollSpeed() { try { string rx = this.SendAndReceive("GET SS").Substring(7); return int.Parse(rx); } catch (Exception) { return null; } } public int? GetScrollWait() { try { string rx = this.SendAndReceive("GET SW").Substring(7); return int.Parse(rx); } catch (Exception) { return null; } } public void SetBrightness(int value) { try { this.SendAndReceive("SET IN " + value); } catch (Exception) { } } public void SetScrollSpeed(int value) { try { this.SendAndReceive("SET SS " + value); } catch (Exception) { } } public void SetScrollWait(int value) { try { this.SendAndReceive("SET SW " + value); } catch (Exception) { } } public void ScrollText(string value) { try { this.SendAndReceive("SCR " + value); } catch (Exception) { } } public bool? GetDateEnabled() { try { string rx = this.SendAndReceive("GET DE").Substring(7); return int.Parse(rx) > 0; } catch (Exception) { return null; } } public void SetDateEnabled(bool value) { try { this.SendAndReceive("SET DE " + (value ? "1" : "0")); } catch (Exception) { } } public bool? GetTemperatureEnabled() { try { string rx = this.SendAndReceive("GET CE").Substring(7); return int.Parse(rx) > 0; } catch (Exception) { return null; } } public void SetTemperatureEnabled(bool value) { try { this.SendAndReceive("SET CE " + (value ? "1" : "0")); } catch (Exception) { } } internal void NextDate(DateTime value) { try { this.SendAndReceive("NXT " + value.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss")); } catch (Exception) { } } } }