229 lines
6.3 KiB
C#
229 lines
6.3 KiB
C#
// 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<SerialPort> serialPorts = new List<SerialPort>();
|
|
private List<Thread> receiveThreads = new List<Thread>();
|
|
|
|
private Dictionary<string, Sensor> allSensor = new Dictionary<string, Sensor>();
|
|
public GameObject sensorView;
|
|
|
|
private void Start()
|
|
{
|
|
// 1. 自动查找并连接所有可用端口
|
|
List<string> connectedPorts = AutoConnectAll();
|
|
|
|
if (connectedPorts.Count == 0)
|
|
{
|
|
Debug.LogError("没有找到可用的串口设备。");
|
|
return;
|
|
}
|
|
|
|
// 2. 初始化传感器
|
|
foreach (Sensor s in GetComponentsInChildren<Sensor>())
|
|
{
|
|
allSensor.TryAdd(s.GetType().Name, s);
|
|
s.Init(this); // 立即初始化
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 自动查找并连接所有可用的串口
|
|
/// </summary>
|
|
/// <returns>成功连接的端口名称列表</returns>
|
|
private List<string> AutoConnectAll()
|
|
{
|
|
List<string> connectedPorts = new List<string>();
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 通用的数据接收线程方法(每个串口一个)
|
|
/// </summary>
|
|
/// <param name="port">当前处理的串口对象</param>
|
|
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占用过高
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 发送数据(广播模式:向所有打开的端口发送)
|
|
/// </summary>
|
|
/// <param name="str"></param>
|
|
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<T>() where T : Sensor
|
|
{
|
|
string name = typeof(T).Name;
|
|
if (!allSensor.ContainsKey(name))
|
|
{
|
|
T comp = GetComponentInChildren<T>();
|
|
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();
|
|
}
|
|
} |