using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Data.SqlTypes; using System.IO.Ports; using System.Linq; using System.Text; using System.Threading; //using UnityEditor.PackageManager.UI; using UnityEngine; using UnityThreadingUtils; using ZXKFramework; public class SensorManager : MonoBehaviour /*MonoSingleton*/ { #region 修改前逻辑 //public enum DataType //{ // 字符串, // 字节流, //} //#region 串口参数,主要修改串口名与波特率 //[Header("串口名")] //public string portName = "COM10"; //[Header("波特率")] //public int baudRate = 115200; //[Header("奇偶校验")] //private Parity parity = Parity.None; //[Header("数据位")] //private int dataBits = 8; //[Header("停止位")] //private StopBits stopBits = StopBits.One; //SerialPort sp = null; //Thread dataReceiveThread; //[Header("接收数据格式")] //public DataType dataType; //#endregion //private Dictionary allSensor = new Dictionary(); //StringBuilder sb = new StringBuilder(); //private void Start() //{ // sp = new SerialPort(portName, baudRate, parity, dataBits, stopBits); // foreach (Sensor s in GetComponentsInChildren()) // { // allSensor.TryAdd(s.GetType().Name, s); // } // switch (dataType) // { // case DataType.字节流: // dataReceiveThread = new Thread(new ThreadStart(DataReceiveBytesThread)); // break; // case DataType.字符串: // dataReceiveThread = new Thread(new ThreadStart(DataReceiveStrThread)); // break; // } // OpenPort(); //} ///// ///// 接收字节流 ///// //private void DataReceiveBytesThread() //{ // while (true) // { // if (sp != null && sp.IsOpen) // { // try // { // if (sp.BytesToRead > 0) // { // byte[] buffer = new byte[sp.BytesToRead]; // sp.Read(buffer, 0, sp.BytesToRead); // string receivedData = Encoding.Default.GetString(buffer); // //Debug.Log(receivedData); // UnityMainThreadDispatcher.Instance().Enqueue(() => // { // foreach (var s in allSensor) // { // s.Value.ReceiveData(receivedData); // } // }); // } // } // catch (Exception) // { // Debug.Log("消息接收失败"); // } // } // Thread.Sleep(20); // } //} ///// ///// 接收字符串 ///// //private void DataReceiveStrThread() //{ // while (true) // { // if (sp != null && sp.IsOpen) // { // try // { // if (sp.BytesToRead > 0) // { // sb.Append(sp.ReadExisting()); // //this.ColorLog(GDLog.LogColorState.Blue, sp.ReadLine()); // UnityMainThreadDispatcher.Instance().Enqueue(() => // { // foreach (var s in allSensor) // { // s.Value.ReceiveData(sb.ToString()); // } // sb.Clear(); // }); // } // } // catch (Exception) // { // Debug.Log("消息接收失败"); // } // } // Thread.Sleep(20); // } //} ///// ///// 发送消息 ///// ///// //public void SendFunction(string str) //{ // try // { // byte[] dataSend = Encoding.ASCII.GetBytes(str); // if (sp != null && sp.IsOpen) // { // if (dataSend != null && dataSend.Length > 0) // { // sp.Write(dataSend, 0, dataSend.Length); // Debug.Log("发送消息成功:" + str); // } // } // } // catch (Exception) // { // Debug.Log("发送消息失败:" + str); // } //} ////获取指定传感器 //public T GetSensor() where T : Sensor //{ // try // { // string name = typeof(T).Name; // if (!allSensor.ContainsKey(name)) // { // allSensor.TryAdd(name, GetComponentInChildren()); // allSensor[name].Init(this); // } // //Debug.Log("传感器获取成功"); // return allSensor[name] as T; // } // catch (Exception) // { // Debug.Log("传感器获取失败"); // return null; // } //} //#region 串口开启关闭相关 ////打开串口 //public void OpenPort() //{ // try // { // if (!sp.IsOpen) // { // sp.Open(); // dataReceiveThread.Start(); // Debug.Log("串口打开成功"); // } // } // catch (Exception) // { // Debug.Log("串口打开失败"); // } //} ////关闭串口 //public void ClosePort() //{ // try // { // sp.Close(); // dataReceiveThread.Abort(); // Debug.Log("串口关闭"); // } // catch (Exception) // { // Debug.Log("串口关闭失败"); // } //} //#endregion //#region Unity 退出相关 //private void OnApplicationQuit() //{ // ClosePort(); //} //private void OnDisable() //{ // this.Log("为什么关闭了:" + gameObject.name); // ClosePort(); //} //#endregion #endregion public enum DataType { 字符串, 字节流, } #region 串口参数 //[Header("端口名(多串口时自动获取)")] //public string portName = "COM3"; [Header("波特率")] public int baudRate = 115200; [Header("奇偶校验")] private Parity parity = Parity.None; [Header("数据位")] private int dataBits = 8; [Header("停止位")] private StopBits stopBits = StopBits.One; [Header("接收数据格式")] public DataType dataType; #endregion private Dictionary allSensor = new Dictionary(); public GameObject canvas; List COMPorts = new List(); List serialPorts = new List(); List dataReceiveThreads = new List(); // 线程运行状态标志(每个串口对应一个标志) private bool[] isThreadRunning; private void Start() { COMPorts = GetAllWindowsCOMPorts().ToList(); // 初始化所有串口 for (int i = 0; i < COMPorts.Count; i++) { SerialPort sp = new SerialPort(COMPorts[i], baudRate, parity, dataBits, stopBits); serialPorts.Add(sp); } // 初始化传感器 foreach (Sensor s in GetComponentsInChildren()) { allSensor.TryAdd(s.GetType().Name, s); } // 初始化线程(绑定具体串口索引,避免索引混乱) isThreadRunning = new bool[COMPorts.Count]; // 初始化标志位数组 for (int i = 0; i < COMPorts.Count; i++) { isThreadRunning[i] = true; // 默认启动线程 int portIndex = i; // 捕获当前索引(避免闭包陷阱) Thread thread; // 根据数据类型创建对应线程,并绑定端口索引 switch (dataType) { case DataType.字节流: thread = new Thread(() => DataReceiveBytesThread(portIndex)); break; case DataType.字符串: thread = new Thread(() => DataReceiveStrThread(portIndex)); break; default: thread = null; break; } if (thread != null) { dataReceiveThreads.Add(thread); } } OpenPort(); } private void Update() { if (Input.GetKeyDown(KeyCode.BackQuote)) { canvas?.SetActive(!canvas.activeInHierarchy); } if (Input.GetKeyDown(KeyCode.Escape)) { canvas?.SetActive(false); } } /// /// 字节流接收线程(绑定具体串口索引) /// private void DataReceiveBytesThread(int portIndex) { // 循环条件:线程标志位为true,且索引有效 while (isThreadRunning[portIndex] && portIndex < serialPorts.Count) { SerialPort sp = serialPorts[portIndex]; if (sp != null && sp.IsOpen) { try { if (sp.BytesToRead > 0) { byte[] buffer = new byte[sp.BytesToRead]; sp.Read(buffer, 0, buffer.Length); string receivedData = Encoding.Default.GetString(buffer); //Debug.Log($"串口 {sp.PortName} 接收字节流:{receivedData}"); // 主线程处理数据 UnityMainThreadDispatcher.Instance().Enqueue(() => { foreach (var pair in allSensor) { pair.Value.ReceiveData(receivedData, sp); } }); } } catch (Exception ex) { Debug.Log($"串口 {sp.PortName} 字节流接收错误:{ex.Message}"); } } Thread.Sleep(10); // 降低CPU占用 } } /// /// 字符串接收线程(绑定具体串口索引) /// private void DataReceiveStrThread(int portIndex) { // 每个线程独立的StringBuilder(避免多线程冲突) StringBuilder sb = new StringBuilder(); // 循环条件:线程标志位为true,且索引有效 while (isThreadRunning[portIndex] && portIndex < serialPorts.Count) { SerialPort sp = serialPorts[portIndex]; if (sp != null && sp.IsOpen) { try { if (sp.BytesToRead > 0) { sb.Append(sp.ReadExisting()); string receivedData = sb.ToString(); UnityMainThreadDispatcher.Instance().Enqueue(() => { foreach (var s in allSensor) { try { //Debug.LogError(s.Value.name); s.Value.ReceiveData(receivedData, sp); } catch (Exception ex) { Debug.LogError($"传感器处理数据错误:{ex.Message}"); } } sb.Clear(); }); } } catch (Exception ex) { Debug.Log($"串口 {sp.PortName} 字符串接收错误:{ex.Message}"); } } Thread.Sleep(10); // 降低CPU占用 } } /// /// 发送数据到所有已打开的串口 /// public void SendFunction(string str) { try { byte[] dataSend = Encoding.ASCII.GetBytes(str); for (int i = 0; i < serialPorts.Count; i++) { SerialPort sp = serialPorts[i]; if (sp != null && sp.IsOpen && dataSend != null && dataSend.Length > 0) { sp.Write(dataSend, 0, dataSend.Length); Debug.Log($"向串口 {sp.PortName} 发送成功:{str}"); } } } catch (Exception ex) { Debug.Log($"发送数据失败:{ex.Message},数据:{str}"); } } /// /// 获取指定类型的传感器 /// public T GetSensor() where T : Sensor { try { string name = typeof(T).Name; if (!allSensor.ContainsKey(name)) { T sensor = GetComponentInChildren(); if (sensor != null) { allSensor.TryAdd(name, sensor); sensor.Init(this); } } Debug.Log($"获取传感器 {typeof(T).Name} 成功"); return allSensor[name] as T; } catch (Exception ex) { Debug.Log($"获取传感器 {typeof(T).Name} 失败:{ex.Message}"); return null; } } #region 串口打开关闭控制 /// /// 打开所有串口并启动对应线程 /// public void OpenPort() { try { Debug.Log("正在打开串口..."); for (int i = 0; i < serialPorts.Count; i++) { SerialPort sp = serialPorts[i]; if (sp != null && !sp.IsOpen) { sp.Open(); Debug.Log($"串口 {sp.PortName} 打开成功"); } // 启动对应线程(检查索引有效性) if (i < dataReceiveThreads.Count && dataReceiveThreads[i] != null && !dataReceiveThreads[i].IsAlive) { dataReceiveThreads[i].Start(); } } } catch (Exception ex) { Debug.Log($"串口打开失败:{ex.ToString()}"); } } /// /// 关闭所有串口并停止线程(安全终止,避免Abort) /// public void ClosePort() { for (int i = 0; i < serialPorts.Count; i++) { // 1. 停止线程(通过标志位) if (i < isThreadRunning.Length) { isThreadRunning[i] = false; // 通知线程退出循环 } // 等待线程终止(最多1秒) if (i < dataReceiveThreads.Count && dataReceiveThreads[i] != null && dataReceiveThreads[i].IsAlive) { dataReceiveThreads[i].Join(1000); dataReceiveThreads[i] = null; } // 2. 关闭串口 SerialPort sp = serialPorts[i]; if (sp != null && sp.IsOpen) { try { sp.Close(); Debug.Log($"串口 {sp.PortName} 关闭成功"); } catch (Exception ex) { Debug.Log($"串口 {sp.PortName} 关闭失败:{ex.ToString()}"); } serialPorts[i] = null; } } } #endregion #region Unity 生命周期 private void OnApplicationQuit() { ClosePort(); } private void OnDisable() { ClosePort(); } /// /// 获取所有Windows串口并排序 /// public string[] GetAllWindowsCOMPorts() { try { var ports = SerialPort.GetPortNames(); // 按COM端口号排序 Array.Sort(ports, (a, b) => { if (int.TryParse(a.Replace("COM", ""), out int numA) && int.TryParse(b.Replace("COM", ""), out int numB)) { return numA.CompareTo(numB); } return string.Compare(a, b); }); return ports; } catch (Exception e) { Debug.LogError($"获取串口失败:{e.Message}\n{e.StackTrace}"); return Array.Empty(); } } #endregion }