EnergyEfficiencyManagement/Assets/Zion/Scripts/ModbusRTUController.cs

317 lines
9.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.IO.Ports;
using System.Threading;
using UnityEngine;
using System.Collections.Generic;
/// <summary>
/// Modbus RTU 控制脚本
/// </summary>
public class ModbusMultiDeviceController : MonoBehaviour
{
[System.Serializable]
public class DeviceInfo
{
public string deviceName; // 设备名称
public ushort registerAddress; // 寄存器地址
public bool isOn; // 当前状态
}
[Header("串口配置")]
public string portName = "COM3";
public int baudRate = 9600;
public Parity parity = Parity.None;
public int dataBits = 8;
public StopBits stopBits = StopBits.One;
[Header("设备列表")]
public List<DeviceInfo> devices = new List<DeviceInfo>()
{
new DeviceInfo { deviceName = "第一路红色管路", registerAddress = 0x0000, isOn = false },
new DeviceInfo { deviceName = "第二路绿色管路", registerAddress = 0x0001, isOn = false },
new DeviceInfo { deviceName = "第三路蓝色管路", registerAddress = 0x0002, isOn = false },
new DeviceInfo { deviceName = "第四路罐体", registerAddress = 0x0003, isOn = false },
new DeviceInfo { deviceName = "第五路照明", registerAddress = 0x0004, isOn = false },
new DeviceInfo { deviceName = "第六路第二个罐亮", registerAddress = 0x0005, isOn = false },
new DeviceInfo { deviceName = "第七路第二个汽轮机工作", registerAddress = 0x0006, isOn = false }
};
private SerialPort serialPort;
private byte deviceAddress = 0x01; // 设备地址
private byte functionCode = 0x05; // 功能码:写单个线圈
// 初始化串口
public bool OpenPort()
{
if (serialPort != null && serialPort.IsOpen)
{
Debug.LogWarning("串口已经打开。");
return true;
}
try
{
serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
serialPort.ReadTimeout = 500;
serialPort.WriteTimeout = 500;
serialPort.Open();
Debug.Log($"串口 {portName} 打开成功,波特率:{baudRate}");
return true;
}
catch (System.Exception ex)
{
Debug.LogError($"打开串口失败: {ex.Message}");
return false;
}
}
// 计算 Modbus RTU 的 CRC16 校验码 (低位在前)
public static byte[] CalculateCRC(byte[] data, int length)
{
ushort crc = 0xFFFF;
for (int i = 0; i < length; i++)
{
crc ^= data[i];
for (int j = 0; j < 8; j++)
{
if ((crc & 0x0001) != 0)
{
crc >>= 1;
crc ^= 0xA001;
}
else
{
crc >>= 1;
}
}
}
return new byte[] { (byte)(crc & 0xFF), (byte)((crc >> 8) & 0xFF) };
}
// 构建完整指令帧
public byte[] BuildCommand(ushort registerAddress, bool turnOn)
{
ushort value = turnOn ? (ushort)0xFF00 : (ushort)0x0000;
byte[] frame = new byte[8];
frame[0] = deviceAddress;
frame[1] = functionCode;
frame[2] = (byte)(registerAddress >> 8);
frame[3] = (byte)(registerAddress & 0xFF);
frame[4] = (byte)(value >> 8);
frame[5] = (byte)(value & 0xFF);
byte[] crc = CalculateCRC(frame, 6);
frame[6] = crc[0];
frame[7] = crc[1];
return frame;
}
// 发送指令到指定设备
public bool SendCommand(int deviceIndex, bool turnOn)
{
if (deviceIndex < 0 || deviceIndex >= devices.Count)
{
Debug.LogError($"设备索引 {deviceIndex} 无效有效范围0-{devices.Count - 1}");
return false;
}
if (serialPort == null || !serialPort.IsOpen)
{
Debug.LogError("串口未打开,请先调用 OpenPort()");
return false;
}
DeviceInfo device = devices[deviceIndex];
byte[] command = BuildCommand(device.registerAddress, turnOn);
try
{
serialPort.DiscardInBuffer();
serialPort.Write(command, 0, command.Length);
Debug.Log($"[{device.deviceName}] 发送指令: {(turnOn ? "" : "")} - {ByteArrayToString(command)}");
// 等待设备响应
Thread.Sleep(50);
// 读取响应
int bytesToRead = serialPort.BytesToRead;
if (bytesToRead > 0)
{
byte[] response = new byte[bytesToRead];
int bytesRead = serialPort.Read(response, 0, bytesToRead);
// 验证响应
if (bytesRead == command.Length && ByteArraysEqual(command, response))
{
device.isOn = turnOn;
Debug.Log($"[{device.deviceName}] {(turnOn ? "" : "")} 成功");
return true;
}
else
{
Debug.LogWarning($"[{device.deviceName}] 响应验证失败");
return false;
}
}
else
{
Debug.LogWarning($"[{device.deviceName}] 设备无响应");
return false;
}
}
catch (System.Exception ex)
{
Debug.LogError($"[{device.deviceName}] 通信错误: {ex.Message}");
return false;
}
}
// 通过设备名称控制
public bool SendCommandByName(string deviceName, bool turnOn)
{
int deviceIndex = devices.FindIndex(d => d.deviceName == deviceName);
if (deviceIndex == -1)
{
Debug.LogError($"未找到设备: {deviceName}");
return false;
}
return SendCommand(deviceIndex, turnOn);
}
// 控制所有设备
public void TurnAllOn()
{
Debug.Log("打开所有设备...");
for (int i = 0; i < devices.Count; i++)
{
SendCommand(i, true);
Thread.Sleep(100); // 设备间间隔
}
}
public void TurnAllOff()
{
Debug.Log("关闭所有设备...");
for (int i = devices.Count - 1; i >= 0; i--)
{
SendCommand(i, false);
Thread.Sleep(100); // 设备间间隔
}
}
// 切换设备状态
public void ToggleDevice(int deviceIndex)
{
if (deviceIndex >= 0 && deviceIndex < devices.Count)
{
DeviceInfo device = devices[deviceIndex];
SendCommand(deviceIndex, !device.isOn);
}
}
// 获取设备状态
public bool GetDeviceStatus(int deviceIndex)
{
if (deviceIndex >= 0 && deviceIndex < devices.Count)
{
return devices[deviceIndex].isOn;
}
return false;
}
public bool GetDeviceStatusByName(string deviceName)
{
DeviceInfo device = devices.Find(d => d.deviceName == deviceName);
return device != null ? device.isOn : false;
}
// 工具方法
private string ByteArrayToString(byte[] bytes)
{
return System.BitConverter.ToString(bytes).Replace("-", " ");
}
private bool ByteArraysEqual(byte[] a, byte[] b)
{
if (a.Length != b.Length) return false;
for (int i = 0; i < a.Length; i++)
{
if (a[i] != b[i]) return false;
}
return true;
}
// 测试功能 - 按顺序控制所有设备
public void TestAllDevices()
{
if (!OpenPort())
{
Debug.LogError("无法打开串口,测试中止");
return;
}
Debug.Log("开始测试所有设备...");
StartCoroutine(TestSequence());
}
private System.Collections.IEnumerator TestSequence()
{
// 打开所有设备
for (int i = 0; i < devices.Count; i++)
{
SendCommand(i, true);
yield return new WaitForSeconds(1.0f); // 等待1秒
}
yield return new WaitForSeconds(2.0f); // 等待2秒
// 关闭所有设备
for (int i = devices.Count - 1; i >= 0; i--)
{
SendCommand(i, false);
yield return new WaitForSeconds(1.0f); // 等待1秒
}
Debug.Log("设备测试完成");
}
// 直接控制特定设备(快捷方法)
public void ControlRedPipe(bool turnOn) => SendCommandByName("第一路红色管路", turnOn);
public void ControlGreenPipe(bool turnOn) => SendCommandByName("第二路绿色管路", turnOn);
public void ControlBluePipe(bool turnOn) => SendCommandByName("第三路蓝色管路", turnOn);
public void ControlTank(bool turnOn) => SendCommandByName("第四路罐体", turnOn);
public void ControlLighting(bool turnOn) => SendCommandByName("第五路照明", turnOn);
public void ControlSecondTankLight(bool turnOn) => SendCommandByName("第六路第二个罐亮", turnOn);
public void ControlSecondTurbine(bool turnOn) => SendCommandByName("第七路第二个汽轮机工作", turnOn);
// 关闭串口
public void ClosePort()
{
if (serialPort != null && serialPort.IsOpen)
{
TurnAllOff(); // 先关闭所有设备
Thread.Sleep(500);
serialPort.Close();
Debug.Log("串口已关闭");
}
}
// Unity 生命周期
private void Start()
{
// 自动打开串口
OpenPort();
}
private void OnDestroy()
{
ClosePort();
}
private void OnApplicationQuit()
{
ClosePort();
}
}