using System; using System.Drawing; using System.Text; using System.Windows.Forms; using System.IO.Ports; using System.IO; using System.Diagnostics; using System.Windows.Forms.DataVisualization.Charting; using System.Runtime.InteropServices; using System.Net; namespace WindowsFormsApp1 { public partial class MainForm : Form { public MainForm() { InitializeComponent(); foreach (var s in chart_Graph.Series) s.XValueType = ChartValueType.DateTime; chart_Graph.ChartAreas[0].AxisX.LabelStyle.Format = "mm:ss"; chart_Graph.ChartAreas[0].AxisX.Minimum = DateTime.Now.ToOADate(); chart_Graph.ChartAreas[0].AxisX.Maximum = DateTime.Now.AddSeconds(trackBar_GraphX_Down.Value).ToOADate(); chart_Graph.ChartAreas[0].AxisX.IntervalAutoMode = IntervalAutoMode.FixedCount; chart_Graph.ChartAreas[0].AxisX.Interval = (new DateTime(1, 1, 1, 0, 0, 1)).ToOADate(); for (int a = 0; a < checkedListBox_Series.Items.Count; a++) checkedListBox_Series.SetItemChecked(a, true); PIDs[0] = numericUpDown_PID_Min; PIDs[1] = numericUpDown_PID_Max; PIDs[2] = numericUpDown_PID_P_Min; PIDs[3] = numericUpDown_PID_P_Max; PIDs[4] = numericUpDown_PID_P_Value; PIDs[5] = numericUpDown_PID_I_Min; PIDs[6] = numericUpDown_PID_I_Max; PIDs[7] = numericUpDown_PID_I_Value; PIDs[8] = numericUpDown_PID_D_Min; PIDs[9] = numericUpDown_PID_D_Max; PIDs[10] = numericUpDown_PID_D_Value; PIDs[11] = numericUpDown_Yaw_P_Min; PIDs[12] = numericUpDown_Yaw_P_Max; PIDs[13] = numericUpDown_Yaw_P_Value; PIDs[14] = numericUpDown_Yaw_I_Min; PIDs[15] = numericUpDown_Yaw_I_Max; PIDs[16] = numericUpDown_Yaw_I_Value; PIDs[17] = numericUpDown_Yaw_D_Min; PIDs[18] = numericUpDown_Yaw_D_Max; PIDs[19] = numericUpDown_Yaw_D_Value; } private byte[] getStructBytes(RecvHead str) { int size = Marshal.SizeOf(str); byte[] arr = new byte[size]; IntPtr ptr = IntPtr.Zero; try { ptr = Marshal.AllocHGlobal(size); Marshal.StructureToPtr(str, ptr, true); Marshal.Copy(ptr, arr, 0, size); } finally { Marshal.FreeHGlobal(ptr); } return arr; } private object fromBytestoStruct(byte[] arr) { RecvHead str = new RecvHead(); int size = Marshal.SizeOf(str); IntPtr ptr = IntPtr.Zero; try { ptr = Marshal.AllocHGlobal(size); Marshal.Copy(arr, 0, ptr, size); str = (RecvHead)Marshal.PtrToStructure(ptr, str.GetType()); } finally { Marshal.FreeHGlobal(ptr); } return str; } private bool CheckCRC8(byte crc1, byte crc2) { bool ret = crc1 == crc2; if (ret) { RecvCounts++; textBox_Count.Text = RecvCounts.ToString(); } else { RecvErrors++; textBox_Error.Text = RecvErrors.ToString(); } RecvMain.Mode = RecvModeEnum.Begin; return ret; } void OperationResult(bool res) { if (res) { RecvCounts++; textBox_Count.Text = RecvCounts.ToString(); } else { RecvErrors++; textBox_Error.Text = RecvErrors.ToString(); } RecvMain.Mode = RecvModeEnum.Begin; } private byte GetCRC8(byte[] arr, int size) { byte crc = 0; for (int a = 0; a < Math.Min(arr.Length, size); a++) crc ^= arr[a]; return crc; } private bool CheckCRC8(byte[] arr, int size, byte crc2) { byte crc1 = GetCRC8(arr, size); return CheckCRC8(crc1, crc2); } private void SendData(RecvModeEnum mode, byte[] data) { if (data == null) data = new byte[0]; RecvHead head = new RecvHead(mode, data.Length, GetCRC8(data, data.Length)); byte[] h = getStructBytes(head); byte[] send = new byte[1 + h.Length + data.Length]; send[0] = 0xAA; Buffer.BlockCopy(h, 0, send, 1, h.Length); Buffer.BlockCopy(data, 0, send, 1 + h.Length, data.Length); serialPort_COM.Write(send, 0, send.Length); } string RecordsFolder = ""; BinaryWriter RecordWriter = null; private object[] PIDs = new object[20]; private const int RecvCount = 512; private byte[] RecvData = new byte[RecvCount]; private int RecvSize = 0; private enum RecvModeEnum : byte { Begin, Head, Titles, Data, Auto, SetPID, GetPID, Done }; private int TimerStop = 0; private int RecvErrors = 0; private int RecvCounts = 0; private DateTime GraphLast = DateTime.Now; private struct RecvHead { public RecvHead(RecvModeEnum mode, int size, byte crc8) { Mode = mode; DataSize = (byte)size; DataCRC8 = crc8; CRC8 = (byte)(((byte)Mode) ^ DataSize ^ DataCRC8); } public RecvModeEnum Mode; public byte DataSize; public byte DataCRC8; public byte CRC8; public bool Check() { return CRC8 == (byte)(((byte)Mode) ^ DataSize ^ DataCRC8); } } private RecvHead RecvMain; private void DrawGraph(byte[] data, int from, int size) { if (checkBox_Track.Checked) { chart_Graph.ChartAreas[0].AxisX.Minimum = DateTime.Now.AddSeconds(-trackBar_GraphX_Down.Value).ToOADate(); chart_Graph.ChartAreas[0].AxisX.Maximum = DateTime.Now.ToOADate(); } else { TimeSpan shift = DateTime.Now.Subtract(GraphLast); if (shift.TotalSeconds > trackBar_GraphX_Down.Value) { GraphLast = DateTime.Now; for (int a = 0; a < chart_Graph.Series.Count; a++) chart_Graph.Series[a].Points.Clear(); chart_Graph.ChartAreas[0].AxisX.Minimum = DateTime.Now.ToOADate(); chart_Graph.ChartAreas[0].AxisX.Maximum = DateTime.Now.AddSeconds(trackBar_GraphX_Down.Value).ToOADate(); } } RecordWriter?.Write(Encoding.UTF8.GetBytes(DateTime.Now.ToOADate().ToString())); short[] lines = new short[size / 2]; Buffer.BlockCopy(data, from, lines, 0, size); for (int a = 0; a < Math.Min(lines.Length, chart_Graph.Series.Count); a++) { chart_Graph.Series[a].Points.AddXY(DateTime.Now, lines[a]); if (checkBox_Track.Checked) { while (true) { if ((DateTime.Now - DateTime.FromOADate(chart_Graph.Series[a].Points[0].XValue)).TotalSeconds > trackBar_GraphX_Down.Value + 1) chart_Graph.Series[a].Points.RemoveAt(0); else break; } } RecordWriter?.Write(Encoding.UTF8.GetBytes("|" + lines[a].ToString())); } RecordWriter?.Write(Encoding.UTF8.GetBytes("\r\n")); RecordWriter?.Flush(); } private void RecvPID(byte[] data) { for (int a = 0; a < 20; a++) { ((NumericUpDown)PIDs[a]).Value = (decimal)BitConverter.ToSingle(data, a * 4); } } private byte[] SendPID() { byte[] send = new byte[20 * 4]; float[] data = new float[20]; for (int a = 0; a < 20; a++) { float v = (float)((NumericUpDown)PIDs[a]).Value; data[a] = v; } Buffer.BlockCopy(data, 0, send, 0, send.Length); return send; } private void button_Connect_Click(object sender, EventArgs e) { if (button_Connect.Text == "Connect") { try { serialPort_COM.PortName = comboBox_Port.Text; } catch { MessageBox.Show("Error to set port", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } try { serialPort_COM.Open(); serialPort_COM.DiscardInBuffer(); SendData(RecvModeEnum.Titles, null); } catch { MessageBox.Show("Error to connect", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } RecvErrors = 0; RecvCounts = 0; textBox_Error.Text = "0"; textBox_Count.Text = "0"; timer_Tick.Enabled = true; button_Connect.Text = "Disonnect"; checkBox_Save_CheckedChanged(null, null); } else { serialPort_COM.Close(); timer_Tick.Enabled = false; button_Connect.Text = "Connect"; panel_PID.Enabled = false; RecordWriter?.Close(); RecordWriter = null; } } private void timer_Tick_Tick(object sender, EventArgs e) { switch (RecvMain.Mode) { case RecvModeEnum.Begin: { TimerStop = 20; byte[] recv = new byte[1]; int len; try { len = serialPort_COM.Read(recv, 0, 1); } catch { break; } if (recv[0] == 0xAA) RecvMain.Mode = RecvModeEnum.Head; break; } case RecvModeEnum.Head: { int size = Marshal.SizeOf(RecvMain); int len; try { len = serialPort_COM.Read(RecvData, RecvSize, size - RecvSize); } catch { break; } RecvSize += len; if (RecvSize < size) break; RecvSize = 0; RecvHead head = (RecvHead)fromBytestoStruct(RecvData); if (!head.Check()) { OperationResult(false); break; } RecvMain = head; break; } case RecvModeEnum.Titles: { int len; try { len = serialPort_COM.Read(RecvData, RecvSize, RecvMain.DataSize - RecvSize); } catch { break; } RecvSize += len; if (RecvSize < RecvMain.DataSize) break; RecvSize = 0; if (!CheckCRC8(RecvData, RecvMain.DataSize, RecvMain.DataCRC8)) break; string titles = Encoding.UTF8.GetString(RecvData, 0, len); string[] tit = titles.Split('|'); chart_Graph.Series.Clear(); checkedListBox_Series.Items.Clear(); foreach (var t in tit) { var s = chart_Graph.Series.Add(t); s.ChartType = SeriesChartType.FastLine; s.XValueType = ChartValueType.DateTime; int i = checkedListBox_Series.Items.Add(t); checkedListBox_Series.SetItemChecked(i, true); } break; } case RecvModeEnum.Data: { int len; try { len = serialPort_COM.Read(RecvData, RecvSize, RecvMain.DataSize - RecvSize); } catch { break; } RecvSize += len; if (RecvSize < RecvMain.DataSize) break; RecvSize = 0; if (!CheckCRC8(RecvData, len, RecvMain.DataCRC8)) break; if (chart_Graph.Series.Count == 0) break; DrawGraph(RecvData, 0, RecvMain.DataSize); break; } case RecvModeEnum.GetPID: { int len; try { len = serialPort_COM.Read(RecvData, RecvSize, RecvMain.DataSize - RecvSize); } catch { break; } RecvSize += len; if (RecvSize < RecvMain.DataSize) break; RecvSize = 0; if (!CheckCRC8(RecvData, len, RecvMain.DataCRC8)) break; RecvPID(RecvData); label_PID_State.Text = "Done"; for (int a = 0; a < 20; a++) ((NumericUpDown)PIDs[a]).BackColor = Color.White; panel_PID.Enabled = true; break; } case RecvModeEnum.Done: { for (int a = 0; a < 20; a++) ((NumericUpDown)PIDs[a]).BackColor = Color.White; label_PID_State.Text = "Done"; RecvMain.Mode = RecvModeEnum.Begin; break; } default: { RecvMain.Mode = RecvModeEnum.Begin; break; } } TimerStop--; if (TimerStop <= 0) { RecvErrors++; textBox_Error.Text = RecvErrors.ToString(); RecvMain.Mode = RecvModeEnum.Begin; } } private void comboBox_Port_DropDown(object sender, EventArgs e) { comboBox_Port.Items.Clear(); foreach (string p in SerialPort.GetPortNames()) comboBox_Port.Items.Add(p); } private void numericUpDown_Min_ValueChanged(object sender, EventArgs e) { trackBar_GraphX_Down.Maximum = (int)numericUpDown_Min.Value; if (sender == numericUpDown_Min) trackBar_GraphX_Down.Value = trackBar_GraphX_Down.Maximum; else foreach (var s in chart_Graph.Series) s.Points.Clear(); GraphLast = DateTime.Now; chart_Graph.ChartAreas[0].AxisX.Minimum = DateTime.Now.ToOADate(); chart_Graph.ChartAreas[0].AxisX.Maximum = DateTime.Now.AddSeconds(trackBar_GraphX_Down.Value).ToOADate(); chart_Graph.ChartAreas[0].AxisX.Interval = (new DateTime(1, 1, 1, 0, 0, 0).AddSeconds(((double)trackBar_GraphX_Down.Value) / 10)).ToOADate(); if (trackBar_GraphX_Down.Value < 10) chart_Graph.ChartAreas[0].AxisX.LabelStyle.Format = "mm:ss.f"; else chart_Graph.ChartAreas[0].AxisX.LabelStyle.Format = "mm:ss"; } private void button_PID_Read_Click(object sender, EventArgs e) { label_PID_State.Text = "Read"; SendData(RecvModeEnum.GetPID, null); } private void button_PID_Write_Click(object sender, EventArgs e) { if (!panel_PID.Enabled) return; label_PID_State.Text = "Write"; byte[] data = SendPID(); SendData(RecvModeEnum.SetPID, data); } private void button_Color_Click(object sender, EventArgs e) { if (colorDialog_Color.ShowDialog() == DialogResult.Cancel) return; if (checkedListBox_Series.SelectedIndex == -1) return; //--- int index = checkedListBox_Series.SelectedIndex; chart_Graph.Series[index].Color = colorDialog_Color.Color; button_Color.BackColor = colorDialog_Color.Color; } private void label_PID_State_TextChanged(object sender, EventArgs e) { if (label_PID_State.Text != "Done") label_PID_State.BackColor = Color.Tomato; else label_PID_State.BackColor = Color.LightGreen; } private void numericUpDown_PID_Min_ValueChanged(object sender, EventArgs e) { ((NumericUpDown)sender).BackColor = Color.Tomato; } private void checkBox_Save_CheckedChanged(object sender, EventArgs e) { if (!checkBox_Save.Checked) { RecordWriter?.Close(); RecordWriter = null; return; } DateTime dt = DateTime.Now; string file = Path.Combine(RecordsFolder, "record=" + dt.ToString("yyyy-MM-dd+HH_mm_ss") + ".tel"); try { RecordWriter = new BinaryWriter(File.OpenWrite(file)); string head = "Telem v1\r\nTime"; foreach (var s in chart_Graph.Series) head += "|" + s.Name; head += "\r\n"; RecordWriter.Write(Encoding.UTF8.GetBytes(head)); RecordWriter.Flush(); } catch { MessageBox.Show("Incorrect save file!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); checkBox_Save.Checked = false; } } private void Form1_Load(object sender, EventArgs e) { try { string folder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); string specificFolder = Path.Combine(folder, "Telem"); //--- RecordsFolder = Path.Combine(specificFolder, "Records"); Directory.CreateDirectory(RecordsFolder); openFileDialog_Open.InitialDirectory = RecordsFolder; } catch { } string[] input = Environment.GetCommandLineArgs(); if (input.Length > 1) { string file = input[1]; byte[] record = File.ReadAllBytes(file); short[] data = new short[record.Length / 2]; Buffer.BlockCopy(record, 0, data, 0, record.Length); for (int a = 0; a < data.Length; a++) { chart_Graph.Series[a % 9].Points.AddXY(a / 9, data[a]); } chart_Graph.ChartAreas[0].AxisX.Maximum = double.NaN; groupBox_Connect.Enabled = false; } } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { RecordWriter?.Close(); RecordWriter = null; } private void openToolStripMenuItem_Click(object sender, EventArgs e) { if (openFileDialog_Open.ShowDialog() != DialogResult.OK) return; Record w = new Record(); w.RecordFile = openFileDialog_Open.FileName; w.Show(); } private void folderToolStripMenuItem_Click(object sender, EventArgs e) { try { Process.Start(RecordsFolder); } catch { MessageBox.Show("No records!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void numericUpDown_Graph_MaxY_ValueChanged(object sender, EventArgs e) { chart_Graph.ChartAreas[0].AxisY.Minimum = (double)numericUpDown_Graph_MinY.Value; chart_Graph.ChartAreas[0].AxisY.Maximum = (double)numericUpDown_Graph_MaxY.Value; trackBar_GraphY_Left.Maximum = (int)numericUpDown_Graph_MaxY.Value; trackBar_GraphY_Left.Minimum = (int)numericUpDown_Graph_MinY.Value; trackBar_GraphY_Right.Maximum = (int)numericUpDown_Graph_MaxY.Value; trackBar_GraphY_Right.Minimum = (int)numericUpDown_Graph_MinY.Value; } private void checkBox_AutoY_CheckedChanged(object sender, EventArgs e) { if (checkBox_AutoY.Checked) { chart_Graph.ChartAreas[0].AxisY.Minimum = double.NaN; chart_Graph.ChartAreas[0].AxisY.Maximum = double.NaN; numericUpDown_Graph_MinY.Enabled = false; numericUpDown_Graph_MaxY.Enabled = false; panel_GraphY.Enabled = false; } else { numericUpDown_Graph_MaxY_ValueChanged(null, null); numericUpDown_Graph_MinY.Enabled = true; numericUpDown_Graph_MaxY.Enabled = true; panel_GraphY.Enabled = true; trackBar_GraphY_Left.Value = trackBar_GraphY_Left.Minimum; trackBar_GraphY_Right.Value = trackBar_GraphY_Right.Maximum; } } private void trackBar_GraphY_Left_Scroll(object sender, EventArgs e) { int min = trackBar_GraphY_Left.Value; int max = trackBar_GraphY_Right.Value; if (min > max) { int sub = min; min = max; max = sub; } if (max == min) max += 1; chart_Graph.ChartAreas[0].AxisY.Minimum = min; chart_Graph.ChartAreas[0].AxisY.Maximum = max; } private void checkedListBox_Series_SelectedIndexChanged(object sender, EventArgs e) { if (checkedListBox_Series.SelectedIndex == -1) return; int index = checkedListBox_Series.SelectedIndex; button_Color.BackColor = chart_Graph.Series[index].Color; //--- for (int a = 0; a < checkedListBox_Series.Items.Count; a++) { chart_Graph.Series[a].Enabled = checkedListBox_Series.GetItemChecked(a); } } } }