// SensorManager.cs using System; using System.Collections.Generic; using System.IO.Ports; using System.Text; using System.Threading; using UnityEngine; using UnityThreadingUtils; public class SensorManager : MonoBehaviour { public enum DataType { 字符串, 字节流, } #region 串口参数 [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 // 1. 修改:使用列表存储多个串口及相关线程 private List serialPorts = new List(); private List receiveThreads = new List(); private Dictionary allSensor = new Dictionary(); public GameObject sensorView; private void Start() { // 1. 自动查找并连接所有可用端口 List connectedPorts = AutoConnectAll(); if (connectedPorts.Count == 0) { Debug.LogError("没有找到可用的串口设备。"); return; } // 2. 初始化传感器 foreach (Sensor s in GetComponentsInChildren()) { allSensor.TryAdd(s.GetType().Name, s); s.Init(this); // 立即初始化 } } /// /// 自动查找并连接所有可用的串口 /// /// 成功连接的端口名称列表 private List AutoConnectAll() { List connectedPorts = new List(); string[] portNames = SerialPort.GetPortNames(); if (portNames.Length == 0) { Debug.LogWarning("未检测到任何串口硬件。"); return connectedPorts; } foreach (string name in portNames) { try { SerialPort sp = new SerialPort(name, baudRate, parity, dataBits, stopBits); sp.ReadTimeout = 1000; sp.Open(); // 连接成功,加入列表 serialPorts.Add(sp); connectedPorts.Add(name); Debug.Log($"成功连接设备: {name}"); // 2. 为每一个端口启动独立的接收线程 Thread thread = new Thread(() => DataReceiveThread(sp)); thread.IsBackground = true; thread.Start(); receiveThreads.Add(thread); } catch (Exception e) { Debug.LogWarning($"端口 {name} 连接失败: {e.Message}"); } } return connectedPorts; } /// /// 通用的数据接收线程方法(每个串口一个) /// /// 当前处理的串口对象 private void DataReceiveThread(SerialPort port) { while (true) { // 检查端口是否还有效 if (port == null || !port.IsOpen) break; try { if (port.BytesToRead > 0) { string receivedData = ""; // 根据格式解析数据 if (dataType == DataType.字节流) { byte[] buffer = new byte[port.BytesToRead]; port.Read(buffer, 0, buffer.Length); receivedData = Encoding.Default.GetString(buffer); } else // 字符串 { receivedData = port.ReadExisting(); } // 使用调度器将任务抛回主线程 UnityMainThreadDispatcher.Instance().Enqueue(() => { // 3. 关键:将数据和端口名一起分发给所有传感器 // 你可以在这里写逻辑,比如只处理特定端口的数据,或者广播给所有 foreach (var sensor in allSensor.Values) { sensor.ReceiveData(receivedData, port.PortName); } }); } } catch (TimeoutException) { // 超时正常,继续循环 continue; } catch (Exception ex) { Debug.Log($"读取 {port.PortName} 时出错: {ex.Message}"); break; // 读取出错,退出线程循环 } Thread.Sleep(20); // 防止CPU占用过高 } } /// /// 发送数据(广播模式:向所有打开的端口发送) /// /// public void SendFunction(string str) { if (string.IsNullOrEmpty(str)) return; byte[] dataSend = Encoding.ASCII.GetBytes(str); // 遍历所有串口,向每一个发送数据 foreach (var sp in serialPorts) { try { if (sp != null && sp.IsOpen) { sp.Write(dataSend, 0, dataSend.Length); Debug.Log($"向 {sp.PortName} 发送: {str}"); } } catch (Exception e) { Debug.LogError($"发送失败 {sp?.PortName}: {e.Message}"); } } } private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { if (sensorView.activeSelf) { sensorView.SetActive(false); } else { sensorView.SetActive(true); } } } // 获取传感器逻辑保持不变 public T GetSensor() where T : Sensor { string name = typeof(T).Name; if (!allSensor.ContainsKey(name)) { T comp = GetComponentInChildren(); if (comp != null) { allSensor[name] = comp; comp.Init(this); } } return allSensor[name] as T; } // 退出时关闭所有端口 private void OnApplicationQuit() { foreach (var sp in serialPorts) { try { if (sp != null && sp.IsOpen) sp.Close(); sp?.Dispose(); } catch (Exception e) { Debug.Log(e.Message); } } serialPorts.Clear(); receiveThreads.Clear(); } }