diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..e3894a0
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,13 @@
+[*.cs]
+
+# CS8600: Converting null literal or possible null value to non-nullable type.
+dotnet_diagnostic.CS8600.severity = none
+
+# CS8618: Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
+dotnet_diagnostic.CS8618.severity = none
+
+# CS8602: Dereference of a possibly null reference.
+dotnet_diagnostic.CS8602.severity = none
+
+# CS8601: Possible null reference assignment.
+dotnet_diagnostic.CS8601.severity = none
diff --git a/Config.cs b/Config.cs
index ad41e09..265152c 100644
--- a/Config.cs
+++ b/Config.cs
@@ -2,6 +2,7 @@
{
internal class Config
{
+ public int mode = 0;
public string sn = "";
public string disk0 = "";
public string disk1 = "";
diff --git a/MainWindow.xaml b/MainWindow.xaml
index 74d4d66..80ce464 100644
--- a/MainWindow.xaml
+++ b/MainWindow.xaml
@@ -6,28 +6,35 @@
xmlns:local="clr-namespace:Topuino_Client_Windows"
mc:Ignorable="d"
Closing="Window_Closing"
- Title="Topuino 客户端" Height="150" Width="400">
+ Title="Topuino 客户端" Height="180" Width="400">
+
-
+
+
+ USB 模式
+ 在线模式
+ 本地模式
+
+
-
+
-
-
+
+
-
+
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
index 83cd4f7..bef53fb 100644
--- a/MainWindow.xaml.cs
+++ b/MainWindow.xaml.cs
@@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Windows.Input;
using System.Drawing;
using System.Windows.Forms;
+using System.Runtime.InteropServices;
using Newtonsoft.Json;
namespace Topuino_Client_Windows
@@ -44,6 +45,7 @@ namespace Topuino_Client_Windows
}
else
{
+ RadioButton_UsbMode.IsChecked = true;
ComboBox_Disk0.SelectedIndex = 0;
ComboBox_Disk1.SelectedIndex = 0;
}
@@ -56,37 +58,71 @@ namespace Topuino_Client_Windows
private NotifyIcon trayIon = new NotifyIcon();
+ private int mode = 0;
private string sn = "";
private List allDrives;
- private DriveInfo? drive0 = null;
- private DriveInfo? drive1 = null;
+ private DriveInfo drive0;
+ private DriveInfo drive1;
- private Config? initConfig = null;
+ private bool ready = false;
private Thread? refreshThread = null;
private ManualResetEvent requestStop = new ManualResetEvent(false);
private ManualResetEvent stopDone = new ManualResetEvent(false);
+ private OnlineConnector? onlineClient = null;
+ private UsbConnector? usbClient = null;
+
private void LoadConfig()
{
try
{
- initConfig = JsonConvert.DeserializeObject(File.ReadAllText("Config.json"));
+ Config? initConfig = JsonConvert.DeserializeObject(File.ReadAllText("Config.json"));
if (initConfig == null)
{
throw new Exception();
}
+ switch (initConfig.mode)
+ {
+ case 0:
+ RadioButton_UsbMode.IsChecked = true;
+ break;
+ case 1:
+ RadioButton_OnlineMode.IsChecked = true;
+ break;
+ case 2:
+ RadioButton_LocalMode.IsChecked = true;
+ break;
+ default:
+ RadioButton_UsbMode.IsChecked = true;
+ break;
+ }
+
// check if drivers missing
+ bool disk0Found = false;
+ bool disk1Fount = false;
foreach (DriveInfo drive in allDrives)
{
- if (drive.Name != initConfig.disk0 && drive.Name != initConfig.disk1)
+ if (drive.Name == initConfig.disk0)
{
- initConfig.disk0 = drive.Name;
- initConfig.disk1 = drive.Name;
- break;
+ disk0Found = true;
}
+ if (drive.Name == initConfig.disk1)
+ {
+ disk1Fount = true;
+ }
+ }
+ if (!disk0Found)
+ {
+ ShowErrorBox("找不到磁盘0,已切换为默认磁盘");
+ initConfig.disk0 = allDrives[0].Name;
+ }
+ if (!disk1Fount)
+ {
+ ShowErrorBox("找不到磁盘1,已切换为默认磁盘");
+ initConfig.disk1 = allDrives[0].Name;
}
for (int i = 0; i < allDrives.Count; i++)
@@ -148,26 +184,78 @@ namespace Topuino_Client_Windows
netBytesReceiveAfter += ni.GetIPStatistics().BytesReceived;
}
-#pragma warning disable CS8602 // Dereference of a possibly null reference.
- Dictionary statusInfo = new Dictionary();
- statusInfo.Add("SN", sn);
- statusInfo.Add("CPU_PERCENT", ((int)cpuCounter.NextValue()).ToString());
- statusInfo.Add("MEM_PERCENT", ((int)ramPercentUsed).ToString());
- statusInfo.Add("DISK_PERCENT", ((int)((double)(drive0.TotalSize - drive0.AvailableFreeSpace) / drive0.TotalSize * 100)).ToString());
- statusInfo.Add("DISK1_PERCENT", ((int)((double)(drive1.TotalSize - drive1.AvailableFreeSpace) / drive1.TotalSize * 100)).ToString());
- statusInfo.Add("DISK_READ_RATE", ((int)diskReadCounter.NextValue()).ToString());
- statusInfo.Add("DISK_WRITE_RATE", ((int)diskWriteCounter.NextValue()).ToString());
- statusInfo.Add("NET_SENT_RATE", ((int)(netBytesSentAfter - netBytesSentBefore)).ToString());
- statusInfo.Add("NET_RECV_RATE", ((int)(netBytesSentAfter - netBytesSentBefore)).ToString());
-#pragma warning restore CS8602 // Dereference of a possibly null reference.
+ MonitorData data = new MonitorData
+ {
+ cpuPercent = (byte)cpuCounter.NextValue(),
+ memPercent = (byte)ramPercentUsed,
+ disk0Percent = (byte)((double)(drive0.TotalSize - drive0.AvailableFreeSpace) / drive0.TotalSize * 100),
+ disk1Percent = (byte)((double)(drive1.TotalSize - drive1.AvailableFreeSpace) / drive1.TotalSize * 100),
+ diskReadRate = (uint)diskReadCounter.NextValue(),
+ diskWriteRate = (uint)diskWriteCounter.NextValue(),
+ netSentRate = (uint)(netBytesSentAfter - netBytesSentBefore),
+ netRecvRate = (uint)(netBytesReceiveAfter - netBytesReceiveBefore),
+ };
- PublicComm connection = new PublicComm();
- connection.Post(statusInfo);
+ switch (mode)
+ {
+ case 0:
+ UsbRun(data);
+ break;
+ case 1:
+ OnlineRun(data);
+ break;
+ case 2:
+ OfflineRun(data);
+ break;
+ default:
+ break;
+ }
}
stopDone.Set();
}
+ private void UsbRun(MonitorData data)
+ {
+ int size = Marshal.SizeOf(data);
+ byte[] bin = new byte[size];
+ IntPtr ptr = IntPtr.Zero;
+ try
+ {
+ ptr = Marshal.AllocHGlobal(size);
+ Marshal.StructureToPtr(data, ptr, true);
+ Marshal.Copy(ptr, bin, 0, size);
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(ptr);
+ }
+
+ usbClient.Send(bin);
+ }
+
+ private void OnlineRun(MonitorData data)
+ {
+ Dictionary statusInfo = new Dictionary();
+ statusInfo.Add("SN", sn);
+ statusInfo.Add("CPU_PERCENT", data.cpuPercent.ToString());
+ statusInfo.Add("MEM_PERCENT", data.memPercent.ToString());
+ statusInfo.Add("DISK_PERCENT", data.disk0Percent.ToString());
+ statusInfo.Add("DISK1_PERCENT", data.disk1Percent.ToString());
+ statusInfo.Add("DISK_READ_RATE", data.diskReadRate.ToString());
+ statusInfo.Add("DISK_WRITE_RATE", data.diskWriteRate.ToString());
+ statusInfo.Add("NET_SENT_RATE", data.netSentRate.ToString());
+ statusInfo.Add("NET_RECV_RATE", data.netRecvRate.ToString());
+
+ onlineClient.Post(statusInfo);
+ }
+
+ private void OfflineRun(MonitorData data)
+ {
+
+ }
+
+
public void ShowErrorBox(string msg)
{
System.Windows.MessageBox.Show(
@@ -181,53 +269,93 @@ namespace Topuino_Client_Windows
private void ApplyConfig()
{
- sn = TextBox_DeviceSn.Text;
- drive0 = ComboBox_Disk0.SelectedItem as DriveInfo;
- drive1 = ComboBox_Disk1.SelectedItem as DriveInfo;
+ try
+ {
+ if (RadioButton_UsbMode.IsChecked == true)
+ {
+ mode = 0;
+ usbClient = new UsbConnector();
+ }
+ else if (RadioButton_OnlineMode.IsChecked == true)
+ {
+ mode = 1;
+ onlineClient = new OnlineConnector();
+ }
+ else if (RadioButton_LocalMode.IsChecked == true)
+ {
+ mode = 2;
+ // TODO
+ }
+ sn = TextBox_DeviceSn.Text;
+
+ drive0 = ComboBox_Disk0.SelectedItem as DriveInfo;
+ drive1 = ComboBox_Disk1.SelectedItem as DriveInfo;
+
+ ready = true;
+ }
+ catch (Exception e)
+ {
+ ready = false;
+ ShowErrorBox(e.Message);
+ }
}
private void StartRun()
{
- Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
+ if (!ready)
+ {
+ return;
+ }
+
+ if (!drive0.IsReady || !drive1.IsReady)
+ {
+ ShowErrorBox("磁盘未就绪");
+ return;
+ }
+
+ refreshThread = new Thread(Run);
+ refreshThread.Start();
+ }
+
+ private void StopRun()
+ {
if (refreshThread != null)
{
requestStop.Set();
stopDone.WaitOne();
}
+ refreshThread = null;
+
requestStop.Reset();
stopDone.Reset();
-
-#pragma warning disable CS8602 // Dereference of a possibly null reference.
- if (!drive0.IsReady || !drive1.IsReady)
- {
- ShowErrorBox("磁盘未就绪");
- return;
- }
-#pragma warning restore CS8602 // Dereference of a possibly null reference.
-
- Mouse.OverrideCursor = null;
-
- refreshThread = new Thread(Run);
- refreshThread.Start();
}
private async void SaveConfig()
{
Config newConfig = new Config();
+ newConfig.mode = mode;
newConfig.sn = sn;
-#pragma warning disable CS8602 // Dereference of a possibly null reference.
newConfig.disk0 = drive0.Name;
newConfig.disk1 = drive1.Name;
await File.WriteAllTextAsync("Config.json", JsonConvert.SerializeObject(newConfig, Formatting.Indented));
-#pragma warning restore CS8602 // Dereference of a possibly null reference.
+ }
+
+ private void ResetConnectors()
+ {
+ usbClient?.Dispose();
+ onlineClient?.Dispose();
}
private void Button_Save_Click(object sender, RoutedEventArgs e)
{
+ Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
+ StopRun();
+ ResetConnectors();
ApplyConfig();
SaveConfig();
StartRun();
+ Mouse.OverrideCursor = null;
}
private void TrayIcon_DoubleClick(object? sender, EventArgs e)
@@ -242,11 +370,8 @@ namespace Topuino_Client_Windows
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
- if (refreshThread != null)
- {
- requestStop.Set();
- stopDone.WaitOne();
- }
+ StopRun();
+ ResetConnectors();
}
}
}
diff --git a/MonitorData.cs b/MonitorData.cs
new file mode 100644
index 0000000..10e3792
--- /dev/null
+++ b/MonitorData.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Runtime.InteropServices;
+
+namespace Topuino_Client_Windows
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ internal struct MonitorData
+ {
+ public byte cpuPercent;
+ public byte memPercent;
+ public byte disk0Percent;
+ public byte disk1Percent;
+ public uint diskReadRate;
+ public uint diskWriteRate;
+ public uint netSentRate;
+ public uint netRecvRate;
+ }
+}
diff --git a/PublicComm.cs b/OnlineConnector.cs
similarity index 72%
rename from PublicComm.cs
rename to OnlineConnector.cs
index e1383a9..17dfe5a 100644
--- a/PublicComm.cs
+++ b/OnlineConnector.cs
@@ -6,12 +6,12 @@ using System.Collections.Generic;
namespace Topuino_Client_Windows
{
- internal class PublicComm
+ internal class OnlineConnector
{
private HttpClient client = new HttpClient();
private int errorCount = 0;
- public async void Post(Dictionary data)
+ internal async void Post(Dictionary data)
{
try
{
@@ -19,7 +19,7 @@ namespace Topuino_Client_Windows
HttpResponseMessage response = await client.PostAsync("https://iot.vvzero.com/topuino/putdata", content);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
- PublicCommResponse? respData = JsonConvert.DeserializeObject(responseBody);
+ OnlineConnectorResponse? respData = JsonConvert.DeserializeObject(responseBody);
if (respData == null || respData.CODE != 0)
{
throw new Exception();
@@ -35,10 +35,15 @@ namespace Topuino_Client_Windows
}
}
}
+
+ internal void Dispose()
+ {
+ client.Dispose();
+ }
}
- internal class PublicCommResponse
+ internal class OnlineConnectorResponse
{
- public int CODE;
+ public int CODE = 0;
}
}
diff --git a/ParamsSample.json b/ParamsSample.json
index 04ef6ed..c1a827c 100644
--- a/ParamsSample.json
+++ b/ParamsSample.json
@@ -1,4 +1,5 @@
{
+ "mode": 0,
"sn": "V0000T0000",
"disk0": "C:\\",
"disk1": "C:\\"
diff --git a/Topuino_Client_Windows.csproj b/Topuino_Client_Windows.csproj
index 95dd634..5d11d59 100644
--- a/Topuino_Client_Windows.csproj
+++ b/Topuino_Client_Windows.csproj
@@ -7,6 +7,7 @@
true
true
Topuino.ico
+ True
@@ -15,6 +16,7 @@
+
diff --git a/Topuino_Client_Windows.sln b/Topuino_Client_Windows.sln
index f9c3c37..7db12a1 100644
--- a/Topuino_Client_Windows.sln
+++ b/Topuino_Client_Windows.sln
@@ -3,7 +3,12 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32526.322
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Topuino_Client_Windows", "Topuino_Client_Windows.csproj", "{53D6F46D-6FE0-4730-B55A-C4233BA001D3}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Topuino_Client_Windows", "Topuino_Client_Windows.csproj", "{53D6F46D-6FE0-4730-B55A-C4233BA001D3}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0B1B2118-B48C-4CA8-800F-3F2ED24C73ED}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/UsbConnector.cs b/UsbConnector.cs
new file mode 100644
index 0000000..2907099
--- /dev/null
+++ b/UsbConnector.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.IO.Ports;
+using System.Threading;
+
+namespace Topuino_Client_Windows
+{
+ internal class UsbConnector
+ {
+ internal UsbConnector()
+ {
+ string[] portNames = SerialPort.GetPortNames();
+
+ foreach (string name in portNames)
+ {
+ SerialPort port = new SerialPort(name, 115200, Parity.None, 8, StopBits.One);
+ if (IsTopuinoPort(port))
+ {
+ topuinoPort = port;
+ return;
+ }
+ }
+ throw new Exception("找不到 Topuino 设备");
+ }
+
+ private SerialPort? topuinoPort = null;
+ private ManualResetEvent portInitReceived = new ManualResetEvent(false);
+ private bool portValid = false;
+
+ internal void Send(byte[] data)
+ {
+ if (topuinoPort == null)
+ {
+ return;
+ }
+
+ byte[] buff = new byte[data.Length + 4];
+ Array.Copy(data, 0, buff, 4, data.Length);
+ buff[0] = 0x66;
+ buff[1] = 0x77;
+ buff[2] = 0xaa;
+ buff[3] = 0xff;
+ topuinoPort.Write(buff, 0, buff.Length);
+ }
+
+ private bool IsTopuinoPort(SerialPort port)
+ {
+ port.DataReceived += PortInitDataReceiver;
+ port.Open();
+ byte[] pingBuff = new byte[4] { 0x19, 0x26, 0x08, 0x17 };
+
+ portValid = false;
+ port.Write(pingBuff, 0, 4);
+ portInitReceived.WaitOne(1000);
+
+ if (portValid)
+ {
+ return true;
+ }
+ port.Close();
+ return false;
+ }
+
+ private void PortInitDataReceiver(object sender, SerialDataReceivedEventArgs e)
+ {
+ SerialPort port = sender as SerialPort;
+
+ byte[] pongBuff = new byte[2];
+ int readCount = port.Read(pongBuff, 0, 2);
+ if (readCount == 2)
+ {
+ if (pongBuff[0] == 0x68 && pongBuff[1] == 0x61)
+ {
+ portValid = true;
+ }
+ }
+ }
+
+ internal void Dispose()
+ {
+ if (topuinoPort != null)
+ {
+ if (topuinoPort.IsOpen)
+ {
+ topuinoPort.Close();
+ }
+ }
+ topuinoPort = null;
+ }
+ }
+}