541 lines
16 KiB
C#
541 lines
16 KiB
C#
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<SensorManager>*/
|
||
{
|
||
#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<string, Sensor> allSensor = new Dictionary<string, Sensor>();
|
||
//StringBuilder sb = new StringBuilder();
|
||
//private void Start()
|
||
//{
|
||
// sp = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
|
||
// foreach (Sensor s in GetComponentsInChildren<Sensor>())
|
||
// {
|
||
// 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();
|
||
//}
|
||
///// <summary>
|
||
///// 接收字节流
|
||
///// </summary>
|
||
//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);
|
||
// }
|
||
//}
|
||
|
||
///// <summary>
|
||
///// 接收字符串
|
||
///// </summary>
|
||
//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);
|
||
// }
|
||
//}
|
||
///// <summary>
|
||
///// 发送消息
|
||
///// </summary>
|
||
///// <param name="dataSend"></param>
|
||
//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<T>() where T : Sensor
|
||
//{
|
||
// try
|
||
// {
|
||
// string name = typeof(T).Name;
|
||
// if (!allSensor.ContainsKey(name))
|
||
// {
|
||
// allSensor.TryAdd(name, GetComponentInChildren<T>());
|
||
// 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<string, Sensor> allSensor = new Dictionary<string, Sensor>();
|
||
List<string> COMPorts = new List<string>();
|
||
List<SerialPort> serialPorts = new List<SerialPort>();
|
||
List<Thread> dataReceiveThreads = new List<Thread>();
|
||
// 线程运行状态标志(每个串口对应一个标志)
|
||
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<Sensor>())
|
||
{
|
||
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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 字节流接收线程(绑定具体串口索引)
|
||
/// </summary>
|
||
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占用
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 字符串接收线程(绑定具体串口索引)
|
||
/// </summary>
|
||
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占用
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送数据到所有已打开的串口
|
||
/// </summary>
|
||
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}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定类型的传感器
|
||
/// </summary>
|
||
public T GetSensor<T>() where T : Sensor
|
||
{
|
||
try
|
||
{
|
||
string name = typeof(T).Name;
|
||
if (!allSensor.ContainsKey(name))
|
||
{
|
||
T sensor = GetComponentInChildren<T>();
|
||
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 串口打开关闭控制
|
||
/// <summary>
|
||
/// 打开所有串口并启动对应线程
|
||
/// </summary>
|
||
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()}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 关闭所有串口并停止线程(安全终止,避免Abort)
|
||
/// </summary>
|
||
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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取所有Windows串口并排序
|
||
/// </summary>
|
||
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<string>();
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
} |