using Modbus; using Modbus.Device; using Modbus.IO; using System; using System.Collections.Generic; using System.IO; using System.IO.Ports; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace MV485.helper { //设备搜寻(指允许一对一搜寻) public class FindDeviceHelper { //private readonly object _lock = new object(); //搜素时,尝试波特率的优先顺序(13个波特率) //private static List _baudRates = new List //{ // 9600,115200,14400,19200,38400,56000,57600,4800,2400,1200,600,300,110 //}; //private static List _baudRates = new List //{ // 115200, 57600, 38400, 19200, 9600, 14400, 56000, 4800, 2400, 1200, 600, 300, 110 //}; private SerialPort _serialPort; private ModbusSerialMaster _modbusMaster; private ModbusSerialTransport _modbusTransport; private byte[] sentData; //发送到设备的数据 private byte[] recvData; //从设备接收的数据 //指定的串口 private string _portName; //找到设备的事件 //public event Action SearchFinished; public event Action SearchFinished; public event Action SearchDevice; //搜索到的设备 public event Action SearchLog; public event Action SerialConnected; //串口是否已连接 public event Action SerialStatusChanged; //串口连接状态变化 private bool _isStopSearch = false; private string _message; private bool _blSearchOne = false; public int _findCount = 0; public FindDeviceHelper() { //_portName = portName; } //开始搜寻(参数为0时,没有指定波特率从列表中搜寻,否则按照指定的波特率搜寻) public void StartSearch(string portName,string sBaudrates,byte minId,byte maxId,bool blSearchOne) { _blSearchOne = blSearchOne; _isStopSearch = false; _portName = portName; _findCount = 0; _message = $"开始搜索{_portName}"; SearchLog?.Invoke(_message); Logger.Info(_message); //把baudRates转成数组或 string[] parts = sBaudrates.Split(','); byte findAddress = 0; int findBaudRate = 0; foreach(var part in parts) { if (_isStopSearch) break; if (!int.TryParse(part, out int baudRate)) continue; // >0 地址有效 byte address = SearchAddressWithBaudRate(baudRate,minId,maxId); if (address > 0) { findAddress = address; findBaudRate = baudRate; //SearchDevice?.Invoke(_portName, findBaudRate, findAddress); if (blSearchOne && _findCount > 0) { break; //搜索一个设备时,搜索到后,直接退出 } } } //SearchFinished?.Invoke(findAddress > 0, _portName, findBaudRate,findAddress); SearchFinished?.Invoke(_findCount); StopSearch(); Close(); } public void StopSearch() { _message = $"已停止搜索{_portName}"; SearchLog?.Invoke(_message); Logger.Info(_message); _isStopSearch = true; } public void Disponse() { Close(); } //按指定波特率查找 private byte SearchAddressWithBaudRate(int baudRate,byte minId,byte maxId) { //先打开串口 bool blOpen = OpenSerial(baudRate); if (!blOpen) return 0; _message = $"开始搜索波特率: {baudRate}"; Logger.Info(_message); SearchLog?.Invoke(_message); //尝试读取从485设备读取模块ID(即设备ID,站点ID) for(byte i = minId; i <= maxId; i++) { if (_isStopSearch) break; if (!_serialPort.IsOpen) { _isStopSearch = true; _message = $"串口{_portName}已断开,停止搜索。"; SearchLog?.Invoke(_message); SerialStatusChanged?.Invoke(false); Logger.Info(_message); return 0; } bool readResult = ReadAddress(baudRate, i); if (readResult) { _message = $"已匹配设备的连接参数,波特率: {baudRate}, 地址: 0x{i.ToString("X2")}"; SearchLog?.Invoke(_message); Logger.Info(_message); _findCount++; //设备数量+1 SearchDevice?.Invoke(_portName, baudRate, i); if (_blSearchOne) { return i; //找到后返回 } } } _message = $"在波特率{baudRate}下,搜索结束。"; SearchLog?.Invoke(_message); Logger.Info(_message); return 0; } private bool ReadAddress(int baudrate,byte address) { try { _message = $"波特率{baudrate},开始读取设备ID: 0x{address.ToString("X2")}"; SearchLog?.Invoke(_message); //F0模块ID,即站地址 ushort[] slaveAddressRegisters = _modbusMaster.ReadHoldingRegisters(address, 0xF0, 1); printModbusSRData("模块ID"); byte slaveAdress = (byte)(slaveAddressRegisters[0] & 0xFF); return slaveAdress == address; } catch (TimeoutException ex) { //目前测试,波特率不对的话,返回超时错误 _message = $"波特率{baudrate},读取设备ID: 0x{address.ToString("X2")},等待超时"; Logger.Warn(_message); SearchLog?.Invoke(_message); } catch (SlaveException ex) { _message = $"波特率{baudrate},Modbus从站异常: 0x{address.ToString("X2")}, {ex.Message}"; Logger.Warn(_message); SearchLog?.Invoke(_message); } catch (IOException ex) { _message = $"波特率{baudrate},读取设备ID: 0x{address.ToString("X2")}IO错误,{ex.Message}"; Logger.Warn(_message); SearchLog?.Invoke(_message); } catch (Exception ex) { _message = $"波特率{baudrate},读取设备ID: 0x{address.ToString("X2")}错误,{ex.Message}"; Logger.Warn(_message); SearchLog?.Invoke(_message); } return false; } private bool OpenSerial(int baudRate) { Close(); try { //除去波特率外,其它参数默认为不会被修改 SerialPort serialPort = new SerialPort(_portName, baudRate, Parity.None, 8, StopBits.One); serialPort.ReadTimeout = 100; //读取的超时时间应该短一些 serialPort.Open(); _serialPort = serialPort; _modbusMaster = ModbusSerialMaster.CreateRtu(_serialPort); _modbusTransport = (ModbusSerialTransport)_modbusMaster.Transport; _message = $"串口{_portName}连接成功"; SearchLog?.Invoke(_message); Logger.Info(_message); SerialConnected?.Invoke(true); return true; } catch(Exception ex) { _message = $"串口{_portName}连接失败"; SearchLog?.Invoke(_message); SerialConnected?.Invoke(false); _isStopSearch = true; //连接失败停止搜索 Logger.Info(_message); Logger.Error(ex.Message); return false; } } private void Close() { try { _modbusMaster?.Dispose(); _modbusMaster = null; _modbusTransport?.Dispose(); _modbusTransport = null; if (_serialPort != null) { if (_serialPort.IsOpen) { _serialPort.Close(); _message = $"已关闭串口{_portName}"; } _serialPort.Dispose(); _serialPort = null; } } catch (Exception ex) { _message = $"关闭串口{_portName}失败: {ex.Message}"; SearchLog?.Invoke(_message); Logger.Error(_message); } } private void printModbusSRData(string dataName) { sentData = _modbusTransport.GetLastSentData(); recvData = _modbusTransport.GetLastReceivedData(); if (sentData != null && recvData != null) { string sendDataHex = BitConverter.ToString(sentData).Replace("-", " "); string recvDataHex = BitConverter.ToString(recvData).Replace("-", " "); _message = $"{dataName} 发送数据: {sendDataHex}"; SearchLog?.Invoke(_message); Logger.Debug(_message); _message = $"{dataName} 接收数据: {recvDataHex}"; SearchLog?.Invoke(_message); Logger.Debug(_message); } } //----------------------------------------------------- } }