10-02-04 再说状态

作者描述了当前工作中遇到的技术难题和个人情绪状态,包括HotPlug问题无法解决带来的挫败感及公司近期的大规模变动。文中还提到了对于侥幸心理可能导致严重后果的反思。

今天的状态仍然没有改善!整天都睧睧恶恶的。HotPlug仍然无解,而且投入的时间很短!今天真是什么都没干,唉,为什么会这样了?想回家了。晚上开会,说最近发生了很多升级、改板的事情,全公司的人都调动出去升级改板了!很痛心,很惋惜!报有侥幸心理,就会出现大问题!一定不能有这种思想。中午HF帮Mother买票了,不错,呵呵。

老表,我再说下我的需求,怪我没讲明白。我不是非得让你按照#region ModbusTCP客户端 public class ModbusTCP : IDisposable { #region 字段与属性 private TcpClient tcpClient; //----------------------------------------TCP 客户端实例 private NetworkStream stream; //---------------------------------------网络流,用于收发数据 private volatile bool connected = false; //----------------------------当前连接状态 private readonly object socketLock = new object(); //------------------锁,保证线程安全 private readonly object idLock = new object(); //----------------------ID锁 private ushort transId = 1; //-----------------------------------------Modbus 事务ID,每次递增 string nameClient; //--------------------------------------------------客户端名称 private string currentIp; //-------------------------------------------当前连接IP private int currentPort; //--------------------------------------------当前连接端口 private volatile bool disposed = false; //-----------------------------是否已释放资源 public del LogInfoDisplay; //-------------------------------------------委托变量-信息显示 public InfoMsg info = new InfoMsg("", ""); //---------------------------显示信息 public event Action<string> returnReceiveEvent;//-----------------------数据接收事件 private readonly SemaphoreSlim sendLock = new SemaphoreSlim(1, 1); public bool Connected => connected; //---------------------------------连接状态 //自动重连 private bool autoReconnect = true; //----------------------------------自动重连,默认开启 private int maxReconnectAttempts = 5; //-------------------------------重连次数 public bool AutoReconnect { get => autoReconnect; set { lock (socketLock) autoReconnect = value; } } public int MaxReconnectAttempts { get => maxReconnectAttempts; set { lock (socketLock) maxReconnectAttempts = value; } } public int ReconnectInterval { get; set; } = 5000; //------------------重连间隔 public int Timeout { get; set; } = 5000; //----------------------------超时时间(毫秒) public ByteOrder RegisterByteOrder { get; set; } = ByteOrder.BigEndian; public WordOrder RegisterWordOrder { get; set; } = WordOrder.HighFirst; public int AddressOffset { get; set; } = 0; //-------------------------地址偏移,默认M0对应0(如 D100 实际地址为100) private System.Timers.Timer heartbeatTimer; //-------------------------心跳定时器 private ushort GenerateTransactionId() { lock (idLock) { if (transId == ushort.MaxValue) transId = 1; return transId++; } } #endregion #region 数据类型枚举 /// <summary> /// 读写数据类型 /// 字 = 16位(short),双字 = 32位(int) /// </summary> public enum DataType : short { 字 = 1, 双字 = 2 } /// <summary> /// 寄存器内字节顺序(用于16位数据) /// </summary> public enum ByteOrder { BigEndian, // 高字节在前(AB) LittleEndian // 低字节在前(BA) } /// <summary> /// 定义32位数据在两个16位寄存器中的存储顺序 /// </summary> public enum WordOrder { /// <summary> /// 高字在前,低字在后(如西门子、标准 Modbus) /// 示例:0x12345678 → [0x1234][0x5678] /// </summary> HighFirst = 1, // 高位寄存器在前(ABCD) /// <summary> /// 低字在前,高字在后(如三菱、欧姆龙常见) /// 示例:0x12345678 → [0x5678][0x1234] /// </summary> LowFirst = 2 // 低位寄存器在前(CDAB) } #endregion #region 客户端初始化 /// <summary> /// 客户端初始化 /// </summary> /// <param name="name">客户端名称</param> /// <param name="receiveData">接收数据事件</param> /// <param name="info">消息显示事件</param> /// <param name="addressOffset">地址偏移量(如M0对应0,则为0;若PLC从1开始则设为-1)</param> public void Initialize(string name = "客户端", Action<string> receiveData = null, del info = null,int addressOffset = 0) { nameClient = name; //-----------------------客户端名称 returnReceiveEvent += receiveData; //-------接收数据事件 LogInfoDisplay += info; //------------------消息事件 this.info.msgType = nameClient; //----------log消息类型 this.AddressOffset = addressOffset; //------地址偏移设置 } /// <summary> /// 配置自动重连 /// </summary> /// <param name="enabled">开启重连true/false</param> /// <param name="intervalMs">重连间隔</param> /// <param name="maxAttempts">重连次数</param> public void ConfigureAutoReconnect(bool enabled, int intervalMs = 5000, int maxAttempts = 5) { AutoReconnect = enabled; ReconnectInterval = intervalMs; MaxReconnectAttempts = maxAttempts; } /// <summary> /// 配置并启用心跳功能 /// 通过定时器周期性向指定地址写入0/1交替信号,用于PLC监控连接状态 /// </summary> /// <param name="enabled">是否启用心跳功能,true=启用,false=禁用</param> /// <param name="address">心跳信号写入的PLC地址,如"D750"</param> /// <param name="intervalMs">心跳间隔时间,单位毫秒,默认500ms</param> /// <example> /// // 启用心跳,每500ms向D750地址写入0/1交替信号 /// modbusTCP.ConfigureHeartbeat(true, "D750", 500); /// /// // 禁用心跳功能 /// modbusTCP.ConfigureHeartbeat(false); /// </example> public void ConfigureHeartbeat(bool enabled = true, string address = "D700", int intervalMs = 500) { // 停止并释放之前的心跳定时器,避免多个定时器同时运行 heartbeatTimer?.Stop(); // 停止定时器 heartbeatTimer?.Dispose(); // 释放定时器资源 heartbeatTimer = null; // 如果禁用心跳,直接返回 if (!enabled) return; // 创建新的心跳定时器 heartbeatTimer = new System.Timers.Timer(intervalMs); bool state = false; // 心跳状态标志,用于0/1交替 heartbeatTimer.Elapsed += async (s, e) => { try { if (!connected) return; state = !state; await WriteSingleRegistersAsync(1, address, state ? 1 : 0); } catch (Exception ex) { // 异常必须在 async void 中捕获,否则会崩溃整个程序 Console.WriteLine("[Heartbeat] Exception: " + ex.Message); } }; heartbeatTimer.Start(); } #endregion #region 连接与断开 /// <summary> /// 连接到 Modbus TCP 服务器 /// </summary> /// <param name="ip">PLC IP 地址</param> /// <param name="port">端口,默认 502</param> /// <returns>是否连接成功</returns> public bool Connect(string ip, int port = 502) { lock (socketLock) { if (connected) return true; // 已连接则直接返回 try { tcpClient = new TcpClient(); tcpClient.SendTimeout = Timeout; tcpClient.ReceiveTimeout = Timeout; var result = tcpClient.BeginConnect(IPAddress.Parse(ip), port, null, null); var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(Timeout)); if (!success) { info.msg = $"连接超时 ({ip}:{port})"; LogInfoDisplay?.Invoke(info); return false; } tcpClient.EndConnect(result); stream = tcpClient.GetStream(); connected = true; currentIp = ip; currentPort = port; info.msg = $"连接成功 {ip}:{port}"; LogInfoDisplay?.Invoke(info); return true; } catch (Exception ex) { info.msg = $"连接失败: {ex.Message}"; LogInfoDisplay?.Invoke(info); Disconnect(); HandleReconnection(); return false; } } } /// <summary> /// 断开连接 /// </summary> public void Disconnect() { lock (socketLock) { if (!connected) return; try { connected = false; stream?.Close(); tcpClient?.Close(); stream = null; tcpClient = null; info.msg = "断开连接"; LogInfoDisplay?.Invoke(info); } catch (Exception ex) { info.msg = $"断开异常: {ex.Message}"; LogInfoDisplay?.Invoke(info); } } } /// <summary> /// 处理自动重连逻辑(避免阻塞主线程) /// </summary> private void HandleReconnection() { if (!AutoReconnect || string.IsNullOrEmpty(currentIp)) return; Task.Run(() => { int attempts = 0; bool firstAttempt = true; while (!connected && !disposed && attempts < MaxReconnectAttempts) { Thread.Sleep(ReconnectInterval); if (firstAttempt) { info.msg = $"正在重连 {currentIp}:{currentPort}..."; LogInfoDisplay?.Invoke(info); firstAttempt = false; } try { if (Connect(currentIp, currentPort)) { break; } attempts++; } catch { } } }); } #endregion #region 发送请求 /// <summary> /// 发送 Modbus 请求并等待响应 /// </summary> /// <param name="request">请求报文</param> /// <param name="expectedResponseLength">期望响应长度(用于校验)</param> /// <returns>响应字节数组,失败返回 null</returns> private async Task<byte[]> SendRequestAsync(byte[] request, int expectedResponseLength) { await sendLock.WaitAsync().ConfigureAwait(false); CancellationTokenSource cts = null; try { cts = new CancellationTokenSource(); cts.CancelAfter(Timeout); // 建议 Timeout >= 3000 // 假设 tcpClient 已连接,networkStream = tcpClient.GetStream() if (stream == null || !stream.CanWrite) return null; // 设置事务ID(大端字节序) ushort transactionId = GenerateTransactionId(); // 自增或随机生成 request[0] = (byte)(transactionId >> 8); request[1] = (byte)(transactionId & 0xFF); // 发送请求 await stream.WriteAsync(request, 0, request.Length).ConfigureAwait(false); byte[] header = new byte[6]; // 先读6字节获取 Length int read = await ReadExactAsync(stream, header, 0, 6, cts.Token).ConfigureAwait(false); if (read != 6) return null; // 校验 Protocol ID short protocolId = (short)((header[2] << 8) | header[3]); if (protocolId != 0) { Console.WriteLine($"协议ID错误: {protocolId}"); return null; } // 解析后续长度(第5-6字节) int pduLength = (header[4] << 8) | header[5]; int totalResponseLength = 6 + pduLength; byte[] response = new byte[totalResponseLength]; Array.Copy(header, 0, response, 0, 6); if (pduLength > 0) { read = await ReadExactAsync(stream, response, 6, pduLength, cts.Token).ConfigureAwait(false); if (read != pduLength) return null; if (returnReceiveEvent != null) { string hexString = BitConverter.ToString(response).Replace("-", " "); returnReceiveEvent(hexString); } } // 校验 Transaction ID ushort responseTid = (ushort)((response[0] << 8) | response[1]); if (responseTid != transactionId) { Console.WriteLine("事务ID不匹配"); return null; } // 检查异常码 if (pduLength >= 2 && (response[7] & 0x80) == 0x80) { Console.WriteLine($"Modbus 异常码: 0x{response[8]:X2}"); return null; } // 最终长度校验 if (response.Length < expectedResponseLength) { Console.WriteLine("响应长度不足"); return null; } return response; } catch (TimeoutException) { Console.WriteLine("通信超时:等待响应时间过长"); return null; } catch (Exception ex) { Console.WriteLine("通信异常: " + ex.Message); bool wasConnected = false; lock (socketLock) { wasConnected = connected; connected = false; } if (wasConnected) { info.msg = "连接断开"; LogInfoDisplay?.Invoke(info); } return null; } finally { // 必须手动 Dispose cts?.Dispose(); sendLock.Release(); // ✅ 必须释放! } } /// <summary> /// 确保异步读取指定数量的字节 /// </summary> private async Task<int> ReadExactAsync(NetworkStream stream, byte[] buffer, int offset, int count, CancellationToken ct = default) { int totalRead = 0; while (totalRead < count) { try { int read = await stream.ReadAsync(buffer, offset + totalRead, count - totalRead, ct).ConfigureAwait(false); if (read == 0) { // 关键:对端关闭连接 bool wasConnected = false; lock (socketLock) { wasConnected = connected; connected = false; } if (wasConnected) { info.msg = "连接断开"; LogInfoDisplay?.Invoke(info); } break; // 结束读取 } totalRead += read; } catch (OperationCanceledException) when (!ct.IsCancellationRequested) { throw new TimeoutException("读取超时"); } } return totalRead; } #endregion #region 地址解析 /// <summary> /// 如 PLC 地址从 1 开始编号,则设置 AddressOffset = -1 /// 解析地址字符串如 "D100" -> 实际地址 99(若偏移-1) /// </summary> private ushort ParseAddress(string address) { if (string.IsNullOrEmpty(address) || address.Length < 2) throw new ArgumentException("地址格式错误"); char type = char.ToUpper(address[0]); string numPart = address.Substring(1).Split('.')[0]; // 忽略位索引 if (!int.TryParse(numPart, out int rawAddr)) throw new ArgumentException("地址数值无效"); int baseAddress; switch (type) { case 'D': case 'M': case 'X': case 'Y': baseAddress = 0x0000; break; default: throw new ArgumentException($"不支持的地址类型: {type}"); } int finalAddr = baseAddress + rawAddr + AddressOffset; if (finalAddr < 0 || finalAddr > 65535) throw new ArgumentException("地址超出范围"); return (ushort)finalAddr; } #endregion #region 读取线圈寄存器(M寄存器) /// <summary> /// 读取线圈寄存器(功能码01) /// </summary> /// <param name="ID">客户端ID</param> /// <param name="address">地址</param> /// <param name="length">长度</param> /// <returns>值</returns> public async Task<bool[]> ReadCoilsAsync(byte ID, string address, int length) { if (length <= 0) length = 1; if (length > 2048) length = 2048; ushort addr = ParseAddress(address); int byteCount = (length + 7) / 8; byte[] request = new byte[12]; // 构建 Modbus TCP 请求报文 request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成) request[2] = 0x00; request[3] = 0x00; //----协议ID request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU) request[6] = ID; //-------------------------单元标识符ID request[7] = 0x01; //-----------------------功能码(读线圈) request[8] = (byte)(addr >> 8); //----------起始地址高字节 request[9] = (byte)(addr & 0xFF); //--------起始地址低字节 request[10] = (byte)(length >> 8); //-------数量高字节 request[11] = (byte)(length & 0xFF); //-----数量低字节 byte[] response = await SendRequestAsync(request, 9 + byteCount).ConfigureAwait(false); if (response == null || response.Length < 9 + byteCount) return null; bool[] result = new bool[length]; for (int i = 0; i < length; i++) { int byteIdx = i / 8; int bitIdx = i % 8; result[i] = (response[9 + byteIdx] & (1 << bitIdx)) != 0; } return result; } #endregion #region 读取输入状态 /// <summary> /// 读取输入状态(功能码02) /// </summary> /// <param name="ID">客户端ID</param> /// <param name="address">地址</param> /// <param name="length">长度</param> /// <returns>值</returns> public async Task<bool[]> ReadDiscreteInputsAsync(byte ID, string address, int length) { if (length <= 0) length = 1; if (length > 2048) length = 2048; ushort addr = ParseAddress(address); int byteCount = (length + 7) / 8; byte[] request = new byte[12]; // 构建 Modbus TCP 请求报文 request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成) request[2] = 0x00; request[3] = 0x00; //----协议ID request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU) request[6] = ID; //-------------------------单元标识符ID request[7] = 0x02; //-----------------------功能码(读离散输入) request[8] = (byte)(addr >> 8); //----------起始地址高字节 request[9] = (byte)(addr & 0xFF); //--------起始地址低字节 request[10] = (byte)(length >> 8); //-------数量高字节 request[11] = (byte)(length & 0xFF); //-----数量低字节 byte[] response = await SendRequestAsync(request, 9 + byteCount).ConfigureAwait(false); if (response == null || response.Length < 9 + byteCount) return null; bool[] result = new bool[length]; for (int i = 0; i < length; i++) { int byteIdx = i / 8; int bitIdx = i % 8; result[i] = (response[9 + byteIdx] & (1 << bitIdx)) != 0; } return result; } #endregion #region 读取保持寄存器(D寄存器) /// <summary> /// 读取保持寄存器(功能码03) /// </summary> /// <param name="ID">客户端ID</param> /// <param name="address">地址</param> /// <param name="length">长度</param> /// <param name="type">类型</param> /// <returns>值</returns> public async Task<int[]> ReadHoldingRegistersAsync(byte ID, string address, int length, DataType type = DataType.字) { if (length <= 0) length = 1; if (length > 125) length = 125; ushort addr = ParseAddress(address); ushort regCount = (ushort)(length * (ushort)type); byte[] request = new byte[12]; // 构建 Modbus TCP 请求报文 request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成) request[2] = 0x00; request[3] = 0x00; //----协议ID request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU) request[6] = ID; //-------------------------单元标识符ID request[7] = 0x03; //-----------------------功能码(读保持寄存器) request[8] = (byte)(addr >> 8); //----------起始地址高字节 request[9] = (byte)(addr & 0xFF); //--------起始地址低字节 request[10] = (byte)(regCount >> 8); //-----数量高字节 request[11] = (byte)(regCount & 0xFF); //---数量低字节 byte[] response = await SendRequestAsync(request, 9 + regCount * 2).ConfigureAwait(false); if (response == null || response.Length < 9 + regCount * 2) return null; return ParseRegisterResponse(response, length, type); } /// <summary> /// 解析寄存器响应(16位和32位) /// </summary> private int[] ParseRegisterResponse(byte[] response, int length, DataType type, ByteOrder byteOrder = ByteOrder.BigEndian, WordOrder wordOrder = WordOrder.HighFirst) { int[] result = new int[length]; if (type == DataType.字) { for (int i = 0; i < length; i++) { int dataIndex = 9 + i * 2; if (dataIndex + 1 < response.Length) { ushort value; if (byteOrder == ByteOrder.BigEndian) { value = (ushort)(response[dataIndex] << 8 | response[dataIndex + 1]); // AB } else { value = (ushort)(response[dataIndex] | response[dataIndex + 1] << 8); // BA } result[i] = (short)value; // 直接转换为有符号short } } } else //双字 { for (int i = 0; i < length; i++) { int dataIndex = 9 + i * 4; if (dataIndex + 3 < response.Length) { // Step 1: 先按字节序读两个16位寄存器 ushort reg1, reg2; if (byteOrder == ByteOrder.BigEndian) { reg1 = (ushort)(response[dataIndex] << 8 | response[dataIndex + 1]); reg2 = (ushort)(response[dataIndex + 2] << 8 | response[dataIndex + 3]); } else { reg1 = (ushort)(response[dataIndex + 1] << 8 | response[dataIndex]); reg2 = (ushort)(response[dataIndex + 3] << 8 | response[dataIndex + 2]); } // Step 2: 再按字序组合 uint value; if (wordOrder == WordOrder.HighFirst) { value = ((uint)reg1 << 16) | reg2; } else { value = ((uint)reg2 << 16) | reg1; } result[i] = (int)value; // 自动处理补码 } } } return result; } #endregion #region 读取输入寄存器 /// <summary> /// 读取输入寄存器(功能码04) /// </summary> /// <param name="ID">客户端ID</param> /// <param name="address">地址</param> /// <param name="length">长度</param> /// <param name="type">类型</param> /// <returns>值</returns> public async Task<int[]> ReadInputRegistersAsync(byte ID, string address, int length, DataType type = DataType.字) { if (length <= 0) length = 1; if (length > 125) length = 125; ushort addr = ParseAddress(address); ushort regCount = (ushort)(length * (ushort)type); byte[] request = new byte[12]; // 构建 Modbus TCP 请求报文 request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成) request[2] = 0x00; request[3] = 0x00; //----协议ID request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU) request[6] = ID; //-------------------------单元标识符ID request[7] = 0x04; //-----------------------功能码(读输入寄存器) request[8] = (byte)(addr >> 8); //----------起始地址高字节 request[9] = (byte)(addr & 0xFF); //--------起始地址低字节 request[10] = (byte)(regCount >> 8); //-----数量高字节 request[11] = (byte)(regCount & 0xFF); //---数量低字节 byte[] response = await SendRequestAsync(request, 9 + regCount * 2).ConfigureAwait(false); if (response == null || response.Length < 9 + regCount * 2) return null; return ParseRegisterResponse(response, length, type); } #endregion #region 写入单个线圈寄存器值(M寄存器) /// <summary> /// 写入单个线圈寄存器值(功能码05) /// </summary> /// <param name="ID">客户端ID</param> /// <param name="address">地址</param> /// <param name="value">值</param> public async Task<bool> WriteSingleCoilsAsync(byte ID, string address, bool value) { if (!connected) return false; ushort addr = ParseAddress(address); byte[] request = new byte[12]; // 构建 Modbus TCP 请求报文 request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成) request[2] = 0x00; request[3] = 0x00; //----协议ID request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU) request[6] = ID; //-------------------------单元标识符ID request[7] = 0x05; //-----------------------功能码(写单个线圈) request[8] = (byte)(addr >> 8); //----------起始地址高字节 request[9] = (byte)(addr & 0xFF); //--------起始地址低字节 request[10] = (byte)(value ? 0xFF : 0x00); //数据(FF表示ON,00表示OFF) request[11] = 0x00; //----------------------数据(固定0x00) try { byte[] response = await SendRequestAsync(request, 12).ConfigureAwait(false); return response != null && response.Length >= 12 && response[7] == 0x05 && (response[8] << 8 | response[9]) == addr; } catch { return false; } } #endregion #region 写入单个保持寄存器值(D寄存器) /// <summary> /// 写入单个保持寄存器值(功能码06)(功能码16) /// </summary> /// <param name="ID">设备ID</param> /// <param name="address">地址</param> /// <param name="value">值(16位或32位)</param> /// <param name="type">数据类型(字=16位,双字=32位)</param> /// <returns>是否写入成功</returns> public async Task<bool> WriteSingleRegistersAsync(byte ID, string address, int value, DataType type = DataType.字) { if (!connected) return false; if (type == DataType.字) { return await WriteSingleRegister16(ID, address, (short)value); } else { return await WriteDoubleRegister32(ID, address, (int)value); } } /// <summary> /// 16位写入实现(功能码06) /// </summary> /// <param name="ID"></param> /// <param name="address"></param> /// <param name="value"></param> /// <returns></returns> private async Task<bool> WriteSingleRegister16(byte ID, string address, short value) { ushort addr = ParseAddress(address); ushort uvalue = (ushort)value; byte[] request = new byte[12]; // 构建 Modbus TCP 请求报文 request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成) request[2] = 0x00; request[3] = 0x00; //----协议ID request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU) request[6] = ID; //-------------------------单元标识符ID request[7] = 0x06; //-----------------------功能码(写16位保存寄存器) request[8] = (byte)(addr >> 8); //----------起始地址高字节 request[9] = (byte)(addr & 0xFF); //--------起始地址低字节 request[10] = (byte)(uvalue >> 8); //-------数量高字节 request[11] = (byte)(uvalue & 0xFF); //-----数量低字节 byte[] response = await SendRequestAsync(request, 12).ConfigureAwait(false); return response != null && response.Length >= 12 && response[7] == 0x06 && (response[8] << 8 | response[9]) == addr; } /// <summary> /// 32位写入实现(功能码16) /// </summary> /// <param name="ID"></param> /// <param name="address"></param> /// <param name="value"></param> /// <returns></returns> private async Task<bool> WriteDoubleRegister32(byte ID, string address, int value, WordOrder wordOrder = WordOrder.HighFirst) { short hiWord = (short)(value >> 16); short loWord = (short)(value & 0xFFFF); short[] words = wordOrder == WordOrder.HighFirst ? new[] { hiWord, loWord } : new[] { loWord, hiWord }; return await WriteMultipleRegistersAsync(ID, address, words); } #endregion #region 写入多个线圈寄存器值(M寄存器) /// <summary> /// 写入多个线圈寄存器值(功能码15)- 所有线圈写入相同值 /// </summary> /// <param name="ID">设备ID</param> /// <param name="address">起始地址(如"M100")</param> /// <param name="length">写入的线圈数量</param> /// <param name="value">所有线圈的状态(true=ON, false=OFF)</param> /// <returns>是否写入成功</returns> /// <example> /// WriteMultipleCoils(1, "M100", 2, true); /// // 写入M100=true, M101=true /// WriteMultipleCoils(1, "M100", 5, false); /// // 写入M100~M104全部为false /// </example> public async Task<bool> WriteMultipleCoilsAsync(byte ID, string address, int length, bool value) { if (!connected) return false; bool[] values = new bool[length]; for (int i = 0; i < length; i++) values[i] = value; return await WriteMultipleCoilsAsync(ID, address, values); } /// <summary> /// 写入多个线圈寄存器值(bool数组版本)- 每个线圈独立控制 /// </summary> /// <param name="ID">设备ID</param> /// <param name="address">起始地址</param> /// <param name="values">每个线圈的状态数组</param> /// <returns>是否写入成功</returns> /// <example> /// WriteMultipleCoils(1, "M100", new bool[] { true, false, true }); /// // 写入M100=true, M101=false, M102=true /// </example> public async Task<bool> WriteMultipleCoilsAsync(byte ID, string address, bool[] values) { if (!connected) return false; ushort addr = ParseAddress(address); int byteCount = (values.Length + 7) / 8; byte[] coilBytes = new byte[byteCount]; for (int i = 0; i < values.Length; i++) { if (values[i]) coilBytes[i / 8] |= (byte)(1 << (i % 8)); } byte[] request = new byte[13 + byteCount]; // 构建 Modbus TCP 请求报文 request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成) request[2] = 0x00; request[3] = 0x00; //----协议ID request[4] = (byte)((request.Length - 6) >> 8); request[5] = (byte)((request.Length - 6) & 0xFF); request[6] = ID; //-------------------------单元标识符ID request[7] = 0x0F; //-----------------------功能码(写多线圈寄存器) request[8] = (byte)(addr >> 8); //----------起始地址高字节 request[9] = (byte)(addr & 0xFF); //--------起始地址低字节 request[10] = (byte)(values.Length >> 8); //数量高字节 request[11] = (byte)(values.Length & 0xFF);//数量低字节 request[12] = (byte)byteCount; Array.Copy(coilBytes, 0, request, 13, byteCount); byte[] response = await SendRequestAsync(request, 12).ConfigureAwait(false); return response != null && response.Length >= 12; } #endregion #region 写入多个保持寄存器值(D寄存器) /// <summary> /// 写入多个保持寄存器值(功能码16) - 所有寄存器写入相同值 /// </summary> /// <param name="ID">设备ID</param> /// <param name="address">起始地址(如"D100")</param> /// <param name="length">写入的寄存器数量</param> /// <param name="value">所有寄存器的值</param> /// <param name="type">数据类型(字=16位,双字=32位)</param> /// <returns>是否写入成功</returns> /// <example> /// WriteMultipleRegister(1, "D100", 3, 100); /// // 写入D100=100, D101=100, D102=100 /// WriteMultipleRegister(1, "D200", 2, 123456789, ReadTypeEnum.双字); /// // 写入D200-D201=123456789, D202-D203=123456789 /// </example> public async Task<bool> WriteMultipleRegistersAsync(byte ID, string address, int length, int value, DataType type = DataType.字) { if (!connected) return false; if (type == DataType.字) { short[] arr = new short[length]; for (int i = 0; i < length; i++) arr[i] = (short)value; return await WriteMultipleRegistersAsync(ID, address, arr); } else { short[] arr = new short[length * 2]; for (int i = 0; i < length; i++) { arr[i * 2] = (short)(value >> 16); arr[i * 2 + 1] = (short)(value & 0xFFFF); } return await WriteMultipleRegistersAsync(ID, address, arr); } } /// <summary> /// 写入多个保持寄存器值(功能码16) /// </summary> /// <param name="ID"></param> /// <param name="address"></param> /// <param name="values"></param> /// <returns></returns> private async Task<bool> WriteMultipleRegistersAsync(byte ID, string address, short[] values) { ushort addr = ParseAddress(address); byte[] request = new byte[13 + values.Length * 2]; // 构建 Modbus TCP 请求报文 request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成) request[2] = 0x00; request[3] = 0x00; //----协议ID request[4] = (byte)((request.Length - 6) >> 8); request[5] = (byte)((request.Length - 6) & 0xFF); request[6] = ID; //-------------------------单元标识符ID request[7] = 0x10; //-----------------------功能码(写多保存寄存器) request[8] = (byte)(addr >> 8); //----------起始地址高字节 request[9] = (byte)(addr & 0xFF); //--------起始地址低字节 request[10] = (byte)(values.Length >> 8); //数量高字节 request[11] = (byte)(values.Length & 0xFF);//数量低字节 request[12] = (byte)(values.Length * 2); for (int i = 0; i < values.Length; i++) { request[13 + i * 2] = (byte)(values[i] >> 8); request[13 + i * 2 + 1] = (byte)(values[i] & 0xFF); } byte[] response = await SendRequestAsync(request, 12).ConfigureAwait(false); return response != null && response.Length >= 12 && response[7] == 0x10 && (response[8] << 8 | response[9]) == addr && (response[10] << 8 | response[11]) == values.Length; } #endregion #region 客户端关闭 /// <summary> /// 释放所有资源 /// </summary> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposed) return; disposed = true; if (disposing) { // 释放托管资源 heartbeatTimer?.Dispose(); stream?.Dispose(); tcpClient?.Dispose(); sendLock?.Dispose(); } } #endregion } #endregion这个代码一摸一样。下面我一步一步说明我的要求,你一定要仔细阅读理解。1.MC是三菱专用协议,我们需支持Q、FX5U、iQ-F、iQ-R等常用型号通讯,所以public ByteOrder RegisterByteOrder { get; set; } = ByteOrder.BigEndian;public WordOrder RegisterWordOrder { get; set; } = WordOrder.HighFirst;这个功能可以取消,按三菱标准就行。2.private readonly object idLock = new object(); private ushort transId = 1; 这些MC用不到直接删除。3.客户端初始化保留三个方法不变。4.连接与断开一样保留自动重连,连接方法不变不要用异步,因为我窗口加载的方法是普通方法,我不想改变。5.核心发送可以用异步。6.地址解析正常支持M/D/X/Y/R/ZR/SM等常用的就行。7.读写寄存器和这些方法一样用异步,且功能完整支持读写单个多个,唯一注意的就是支持16位和32位数据读写。写多个线圈的支持WriteMultipleCoils(1, "M100", 2, true);写入M100=true, M101=true;WriteMultipleCoils(1, "M100", new bool[] { true, false, true });写入M100=true, M101=false, M102=true,写多个寄存器的支持WriteMultipleRegister(1, "D100", 3, 100); 写入D100=100, D101=100, D102=100,WriteMultipleRegister(1, "D200", 2, 123456789, ReadTypeEnum.双字); 写入D200-D201=123456789, D202-D203=123456789,就行
10-16
修改之后又有新的报错了 [2025-10-25 09:11:49,997][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:78][__init__] uuid_path -> /tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb [2025-10-25 09:11:49,997][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:79][__init__] uuid_str -> 00148acc-4d1b-4893-9f8d-cf5bc53c25eb [2025-10-25 09:11:50,535][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:100][start_analysis] ============LYLYLY=============== [2025-10-25 09:11:50,536][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:101][start_analysis] ============task_info=============== [2025-10-25 09:11:50,536][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:102][start_analysis] task_info -> {'resultTopic': 'AnalyseTask_EMUI2web', 'topic': 'AnalyseTask_web2EMUI', 'common_info': {'syn_id': '', 'source_description': '', 'analysis_tool': ['Hubble'], 'extended_info': {}, 'happen_time': '', 'field': '', 'field2': '', 'betano': '', 'task_class': '', 'source': 'LogkitWeb', 'version': '', 'dtsno': '', 'eventid': '', 'product': '', 'log_download_list': [[['RemoteLog_commercial_LMR-AL10_LMU-AL10 5.1.0.227(SP12C00E226R7P8)_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg=_PP1758525182974A-001_20250922151306.zip', 'https://aresocean.huawei.com/hubble/hubblesvc/hubbleportal/report/downloadLogByDate?fileName=7ccd3e2f0ae6413da4ca75da15c87571_RemoteLog_commercial_LMR-AL10_LMU-AL10+5.1.0.227%28SP12C00E226R7P8%29_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg%3D_PP1758525182974A-001_20250922151306.zip&date=20251025', '']]], 'errorcode1': '', 'errorcode3': '', 'errorcode2': '', 'errorcode4': '', 'batch_or_not': False, 'logsource': 'iDebug', 'task_uuid': '00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'username': 'HubbleTest', 'logtype': '', 'sn': '', 'beta_com': 'beta'}, 'user_defined_info': {'ReliabilityWebTool': {'eventid': '', 'remark': '', 'chip_platform': '', 'betatype': '', 'times': '', 'project': '', 'eid': '', 'faulttype': '', 'conclusion': ''}, 'Hubble': {'analysis_type': ['analysis'], 'selfvali': '', 'analysis_field': '', 'dev': 'False', 'script_vali': {'fault_name': '湖内稳定与流畅', 'input_interface_demo': '', 'input_interface_standard': '', 'script_name': 'eqr7plLp_stability_script_third_donghu.py', 'script_url': '/opt/samba3/ScriptValidation/20251025/SPPQE9UW_eqr7plLp_stability_script_third_donghu.py', 'fault_en': ''}}}, 'filename': None, 'filepath': None, 'runfilename': None, 'runfilepath': None, 'device': '', 'callingsource': 'LogkitWeb', 'logsource': 'iDebug', 'product': '', 'version': '', 'time': None, 'field': '', 'errorcode1': '', 'errorcode2': '', 'errorcode3': '', 'errorcode4': '', 'logfiles_url': [{'url': 'https://aresocean.huawei.com/hubble/hubblesvc/hubbleportal/report/downloadLogByDate?fileName=7ccd3e2f0ae6413da4ca75da15c87571_RemoteLog_commercial_LMR-AL10_LMU-AL10+5.1.0.227%28SP12C00E226R7P8%29_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg%3D_PP1758525182974A-001_20250922151306.zip&date=20251025', 'name': 'RemoteLog_commercial_LMR-AL10_LMU-AL10 5.1.0.227(SP12C00E226R7P8)_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg=_PP1758525182974A-001_20250922151306.zip'}], 'description': None, 'userinfo': None, 'uuid': '00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'log_consume_date': None, 'uploadfiles': [], 'ocean_dest': '/opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb'} [2025-10-25 09:11:50,536][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:103][start_analysis] ====task_info结束======== [2025-10-25 09:11:50,536][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:105][start_analysis] uuid -> /tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb [2025-10-25 09:11:50,536][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:106][start_analysis] =======uuid结束============== [2025-10-25 09:11:50,678][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/HiviewAutomata.py:102][unpack_task_info] Kafka result topic is : [AnalyseTask_EMUI2web] [2025-10-25 09:11:50,830][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:48][wrapper] 当前函数 [receive_task] 运行时间 0.15 秒 [2025-10-25 09:11:50,971] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:167] [insert_analyse_runtime_data] hubble_runtime_measurement 缺失日志基础信息 type:1 data:{'model': 'receive_task', 's': 1761354710, 'e': 1761354710, 'd': 0} [2025-10-25 09:11:50,971][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/HiviewKafka.py:179][send_msg_to_web] 发送Kafka消息的Topic: [AnalyseTask_EMUI2web], msg_dict: [{'quesid': '00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'resultpath': '/opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'result': -2, 'updatefiles': '', 'error_detail': '', 'processflag': ''}] [2025-10-25 09:11:51,861][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/doorman/LogDownloader.py:215][stream_download] 日志下载完成, 路径名称: [/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/RemoteLog_commercial_LMR-AL10_LMU-AL10 5.1.0.227(SP12C00E226R7P8)_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg=_PP1758525182974A-001_20250922151306.zip] [2025-10-25 09:11:51,865][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/HiviewAutomata.py:706][check_mbb_log] !start check mbb : [{'url': 'https://aresocean.huawei.com/hubble/hubblesvc/hubbleportal/report/downloadLogByDate?fileName=7ccd3e2f0ae6413da4ca75da15c87571_RemoteLog_commercial_LMR-AL10_LMU-AL10+5.1.0.227%28SP12C00E226R7P8%29_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg%3D_PP1758525182974A-001_20250922151306.zip&date=20251025', 'name': 'RemoteLog_commercial_LMR-AL10_LMU-AL10 5.1.0.227(SP12C00E226R7P8)_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg=_PP1758525182974A-001_20250922151306.zip'}] [2025-10-25 09:11:51,866][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/HiviewAutomata.py:718][check_mbb_log] match_pattern : ^[A-Za-z]{1,3}\d+.* ,logfile_fullname : RemoteLog_commercial_LMR-AL10_LMU-AL10 5.1.0.227(SP12C00E226R7P8)_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg=_PP1758525182974A-001_20250922151306.zip [2025-10-25 09:11:51,866][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/HiviewAutomata.py:724][check_mbb_log] !check end, skip mbb [2025-10-25 09:11:51,866][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/HiviewKafka.py:179][send_msg_to_web] 发送Kafka消息的Topic: [AnalyseTask_EMUI2web], msg_dict: [{'quesid': '00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'resultpath': '/opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'result': -3, 'updatefiles': '', 'error_detail': '', 'processflag': ''}] [2025-10-25 09:11:54,627][WARNING][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/doorman/LogUnzipper.py:352][unTAR] 压缩文件[/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/RemoteLog_commercial_LMR-AL10_LMU-AL10 5.1.0.227(SP12C00E226R7P8)_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg=_PP1758525182974A-001_20250922151306/remoteLog/charge-log/charge-log-000.tar/charge-log-000.tar]损坏 [2025-10-25 09:11:55,198][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/HiviewKafka.py:179][send_msg_to_web] 发送Kafka消息的Topic: [AnalyseTask_EMUI2web], msg_dict: [{'quesid': '00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'resultpath': '', 'result': 200, 'updatefiles': '', 'error_detail': '压缩文件[/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/RemoteLog_commercial_LMR-AL10_LMU-AL10 5.1.0.227(SP12C00E226R7P8)_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg=_PP1758525182974A-001_20250922151306/remoteLog/charge-log/charge-log-000.tar/charge-log-000.tar]损坏', 'processflag': ''}] [2025-10-25 09:11:55,781][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/doorman/LogUnzipper.py:69][unzip] 解压完成, 顶层路径:[/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb] [2025-10-25 09:11:55,781][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:48][wrapper] 当前函数 [download_unzip] 运行时间 4.81 秒 [2025-10-25 09:11:55,925] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:167] [insert_analyse_runtime_data] hubble_runtime_measurement 缺失日志基础信息 type:1 data:{'model': 'download_unzip', 's': 1761354710, 'e': 1761354715, 'd': 5} [2025-10-25 09:11:56,013][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/manager/LogLobbyManager.py:41][read_basics] 增加性能工厂habor_data_param参数 [2025-10-25 09:11:56,030][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/HiviewAutomata.py:688][copyFile2ocean] copy2ocean tmp_path:/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb ocean_path:/opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb file_name:loginfo.json [2025-10-25 09:11:56,031][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/HiviewAutomata.py:693][copyFile2ocean] !copying from /tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/loginfo.json to /opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb [2025-10-25 09:11:56,090][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/HiviewAutomata.py:695][copyFile2ocean] copy status:0 [2025-10-25 09:11:56,090][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/HiviewKafka.py:179][send_msg_to_web] 发送Kafka消息的Topic: [AnalyseTask_EMUI2web], msg_dict: [{'quesid': '00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'resultpath': '/opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'result': 100, 'updatefiles': '', 'error_detail': '', 'processflag': ''}] [2025-10-25 09:11:56,435] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_tools/PerformanceCommonAnalyse/fault_scene_script/database.py] [line:470] [create_sqlite_db] 本地db数据创建成功,db路径为:/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/perf_data.db, 表名为:hubble_aa_perf_resource_t [2025-10-25 09:11:56,435][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:171][prepare] perf_data.db创建成功,路径为/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/perf_data.db [2025-10-25 09:11:56,435][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/HiviewAutomata.py:303][extract_operation_info_from_logs] 对用户使用场景及焦点窗口抽取 [2025-10-25 09:11:56,582][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/user_descriptions_info/user_logs_operation.py:815][start_extraction] user_operation_sequence start_extraction... [2025-10-25 09:11:56,583][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/user_descriptions_info/user_logs_operation.py:824][start_extraction] 当前故障时间为:20250922151306 [2025-10-25 09:11:56,583][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/user_descriptions_info/user_logs_operation.py:829][start_extraction] 当前年份为:2025 [2025-10-25 09:11:56,583][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/user_descriptions_info/user_logs_operation.py:69][get_file] ======>获取日志文件 [2025-10-25 09:11:56,610][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/user_descriptions_info/user_logs_operation.py:162][find_log] ======>获取相关日志 [2025-10-25 09:11:56,616][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/user_descriptions_info/user_logs_operation.py:151][get_scene_sequence_by_focus] 通过focus关键字未找到场景数据,通过wm关键字查询场景数据 [2025-10-25 09:11:56,626] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_tools/PerformanceCommonAnalyse/PerfLogExtractorUtils.py] [line:152] [insert_Resources_data] 开始往数据库发送['场景分析资源']数据 [2025-10-25 09:11:56,920][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/HiviewKafka.py:126][producer_batch_send] kafka发送时间:0.2942068576812744 [2025-10-25 09:11:56,922] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_tools/PerformanceCommonAnalyse/PerfLogExtractorUtils.py] [line:165] [insert_Resources_data] ['场景分析资源']发送数据量:1 [2025-10-25 09:11:56,923] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_tools/PerformanceCommonAnalyse/PerfLogExtractorUtils.py] [line:168] [insert_Resources_data] type_id为7_6,需要存入到本地perf_data.db中 [2025-10-25 09:11:56,958] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_tools/PerformanceCommonAnalyse/fault_scene_script/database.py] [line:477] [insert_db_data] type_id:7_6 数据插入本地PerfDB成功 插入行数为:1 [2025-10-25 09:11:56,959] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_tools/PerformanceCommonAnalyse/PerfLogExtractorUtils.py] [line:152] [insert_Resources_data] 开始往数据库发送['场景分析资源']数据 [2025-10-25 09:11:57,165][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/HiviewKafka.py:126][producer_batch_send] kafka发送时间:0.20565104484558105 [2025-10-25 09:11:57,167] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_tools/PerformanceCommonAnalyse/PerfLogExtractorUtils.py] [line:165] [insert_Resources_data] ['场景分析资源']发送数据量:10 [2025-10-25 09:11:57,167] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_tools/PerformanceCommonAnalyse/PerfLogExtractorUtils.py] [line:168] [insert_Resources_data] type_id为7_6,需要存入到本地perf_data.db中 [2025-10-25 09:11:57,214] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_tools/PerformanceCommonAnalyse/fault_scene_script/database.py] [line:477] [insert_db_data] type_id:7_6 数据插入本地PerfDB成功 插入行数为:10 [2025-10-25 09:11:57,214] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_tools/PerformanceCommonAnalyse/PerfLogExtractorUtils.py] [line:152] [insert_Resources_data] 开始往数据库发送['原始日志关键字校验']数据 [2025-10-25 09:11:57,406][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/HiviewKafka.py:126][producer_batch_send] kafka发送时间:0.19133424758911133 [2025-10-25 09:11:57,407] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_tools/PerformanceCommonAnalyse/PerfLogExtractorUtils.py] [line:165] [insert_Resources_data] ['原始日志关键字校验']发送数据量:2 [2025-10-25 09:11:57,407] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_tools/PerformanceCommonAnalyse/PerfLogExtractorUtils.py] [line:185] [insert_Resources_data] type_id为36,不需要存入到本地perf_data.db中 [2025-10-25 09:11:57,407][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/user_descriptions_info/user_logs_operation.py:778][check_if_screen_record_exist] 提前提取录屏信息 [2025-10-25 09:11:57,437][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:175][prepare] 提取用户操作序列耗时:1.0526819229125977 [2025-10-25 09:11:57,437][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskAssembler.py:133][check_extraction_analyse] check_extraction_analyse cur key is SYSTRACE_EXTRACTION_ANALYSE [2025-10-25 09:11:57,440][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/HiviewAutomata.py:688][copyFile2ocean] copy2ocean tmp_path:/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb ocean_path:/opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb file_name:loginfo.json [2025-10-25 09:11:57,441][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/HiviewAutomata.py:693][copyFile2ocean] !copying from /tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/loginfo.json to /opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb [2025-10-25 09:11:57,467][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/HiviewAutomata.py:695][copyFile2ocean] copy status:0 [2025-10-25 09:11:57,468][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/HiviewKafka.py:179][send_msg_to_web] 发送Kafka消息的Topic: [AnalyseTask_EMUI2web], msg_dict: [{'quesid': '00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'resultpath': '/opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'result': 100, 'updatefiles': '', 'error_detail': '', 'processflag': ''}] [2025-10-25 09:11:57,781] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/frontdesk/PluginValidation.py] [line:90] [download_py_script] 按绝对路径拷贝脚本文件[/opt/samba3/ScriptValidation/20251025/SPPQE9UW_eqr7plLp_stability_script_third_donghu.py] [2025-10-25 09:11:57,816] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/frontdesk/PluginValidation.py] [line:98] [download_py_script] 成功拷贝文件[eqr7plLp_stability_script_third_donghu.py], 下载路径[/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_test/eqr7plLp_stability_script_third_donghu.py] [2025-10-25 09:11:57,823][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/frontdesk/LogCheckin.py:132][single_test_register] Reload [<module 'Hubble.scripts.plugins.plugin_test.eqr7plLp_stability_script_third_donghu' from '/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_test/eqr7plLp_stability_script_third_donghu.py'>]... [2025-10-25 09:11:57,825][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/frontdesk/LogCheckin.py:139][single_test_register] 测试Plugin[eqr7plLp_stability_script_third_donghu] is ready. [2025-10-25 09:11:57,825] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_test/eqr7plLp_stability_script_third_donghu.py] [line:430] [plugin_preprocess] 这是预处理函数? [2025-10-25 09:11:57,826][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:48][wrapper] 当前函数 [prepare] 运行时间 1.90 秒 [2025-10-25 09:11:57,974] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:167] [insert_analyse_runtime_data] hubble_runtime_measurement 缺失日志基础信息 type:1 data:{'model': 'prepare', 's': 1761354715, 'e': 1761354717, 'd': 2} [2025-10-25 09:11:58,034] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/manager/LogDeserializeManager.py] [line:114] [deserialize_thplog] /opt/samba3/deserializer/deserialize_util/hostlogpull '/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/RemoteLog_commercial_LMR-AL10_LMU-AL10 5.1.0.227(SP12C00E226R7P8)_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg=_PP1758525182974A-001_20250922151306/remoteLog/thplog/2025.09.22_15.13.06.111_I0_logtofile.thplog' [2025-10-25 09:11:58,378] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/manager/LogDeserializeManager.py] [line:116] [deserialize_thplog] ./hostlogpull status:0 [2025-10-25 09:11:58,405] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/manager/LogDeserializeManager.py] [line:1048] [decrypt_isp_log] strart decrypt_isp_log ! [2025-10-25 09:11:58,430] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/manager/LogDeserializeManager.py] [line:1088] [decrypt_isp_log] end decrypt_isp_log ! [2025-10-25 09:11:58,430] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/manager/LogDeserializeManager.py] [line:981] [deserialize_bt_hilog] start deserialize_bt_hilog ! [2025-10-25 09:11:58,458] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/manager/LogDeserializeManager.py] [line:994] [deserialize_bt_hilog] hilog_file_paths=['/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/RemoteLog_commercial_LMR-AL10_LMU-AL10 5.1.0.227(SP12C00E226R7P8)_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg=_PP1758525182974A-001_20250922151306/remoteLog/hilog/hilog.000.20250922-151306.txt', '/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/RemoteLog_commercial_LMR-AL10_LMU-AL10 5.1.0.227(SP12C00E226R7P8)_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg=_PP1758525182974A-001_20250922151306/remoteLog/hilog/hilog.008.20250914-183914.txt', '/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/RemoteLog_commercial_LMR-AL10_LMU-AL10 5.1.0.227(SP12C00E226R7P8)_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg=_PP1758525182974A-001_20250922151306/remoteLog/hilog/hilog.007.20250831-010813.txt'] [2025-10-25 09:11:58,458] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/manager/LogDeserializeManager.py] [line:995] [deserialize_bt_hilog] bt_file_paths=['/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/RemoteLog_commercial_LMR-AL10_LMU-AL10 5.1.0.227(SP12C00E226R7P8)_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg=_PP1758525182974A-001_20250922151306/remoteLog/bt/btsnoop_hci_20250922_122333.log', '/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/RemoteLog_commercial_LMR-AL10_LMU-AL10 5.1.0.227(SP12C00E226R7P8)_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg=_PP1758525182974A-001_20250922151306/remoteLog/bt/btsnoop_hci_20250922_043716.log'] [2025-10-25 09:11:58,458][ERROR][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/HiviewAutomata.py:170][deserialize] 原始日志文件解析失败! Traceback (most recent call last): File "/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/HiviewAutomata.py", line 168, in deserialize self.log_deserialize_manager.deserialize(self.uuid_path, first) File "/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/manager/LogDeserializeManager.py", line 85, in deserialize self.deserialize_bt_hilog(uuid_path) File "/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/manager/LogDeserializeManager.py", line 1005, in deserialize_bt_hilog shutil.copy(f, dst_hilog + os.sep + hilog_file_name) File "/usr/local/lib/python3.7/shutil.py", line 248, in copy copyfile(src, dst, follow_symlinks=follow_symlinks) File "/usr/local/lib/python3.7/shutil.py", line 121, in copyfile with open(dst, 'wb') as fdst: FileNotFoundError: [Errno 2] No such file or directory: '/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/hci_tool/input/hilog/hilog.000.20250922-151306.txt' [2025-10-25 09:11:58,460][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:48][wrapper] 当前函数 [deserialize] 运行时间 0.49 秒 [2025-10-25 09:11:58,606] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:169] [insert_analyse_runtime_data] 开始往数据库发送 1 数据 [2025-10-25 09:11:58,799] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:177] [insert_analyse_runtime_data] 发送 【1】类型 数据量:1 [2025-10-25 09:11:58,800][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskAssembler.py:185][generate_task_queue] adding task FILTER_HISTORY_LOGS [2025-10-25 09:11:58,800][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskAssembler.py:194][generate_task_queue] adding task ANALYSIS_ONLY [2025-10-25 09:11:58,801][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:48][wrapper] 当前函数 [run_before] 运行时间 0.00 秒 [2025-10-25 09:11:58,949] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:169] [insert_analyse_runtime_data] 开始往数据库发送 1 数据 [2025-10-25 09:11:59,139] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:177] [insert_analyse_runtime_data] 发送 【1】类型 数据量:1 [2025-10-25 09:11:59,140][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/HiviewKafka.py:179][send_msg_to_web] 发送Kafka消息的Topic: [AnalyseTask_EMUI2web], msg_dict: [{'quesid': '00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'resultpath': '/opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'result': -4, 'updatefiles': '', 'error_detail': '', 'processflag': ''}] [2025-10-25 09:11:59,450][DEBUG][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/manager/LogWaiterManager.py:92][run_task_queue] orgfile_path : /opt/samba3/of25/20251025/00148acc-4d1b-4893-9f8d-cf5bc53c25eb [2025-10-25 09:11:59,451][WARNING][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:104][run] 执行任务: [FILTER_HISTORY_LOGS] [2025-10-25 09:11:59,593][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:164][filter_history_logs] start filter_history_logs [2025-10-25 09:11:59,669][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:180][filter_history_logs] end filter_history_logs [2025-10-25 09:11:59,669] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:169] [insert_analyse_runtime_data] 开始往数据库发送 2 数据 [2025-10-25 09:11:59,964] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:177] [insert_analyse_runtime_data] 发送 【2】类型 数据量:1 [2025-10-25 09:11:59,965][WARNING][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:104][run] 执行任务: [ANALYSIS_ONLY] [2025-10-25 09:12:00,256][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/HiviewKafka.py:179][send_msg_to_web] 发送Kafka消息的Topic: [AnalyseTask_EMUI2web], msg_dict: [{'quesid': '00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'resultpath': '/opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'result': -6, 'updatefiles': '', 'error_detail': '', 'processflag': 'start_locateanalysis'}] [2025-10-25 09:12:00,548][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/analysis/LogAnalyst.py:95][start_analysis] 插件[eqr7plLp_stability_script_third_donghu]的单日志分析逻辑开始执行 [2025-10-25 09:12:00,549] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_test/eqr7plLp_stability_script_third_donghu.py] [line:468] [run_plugin_single] 这是测试插件!uuid_log_path=[/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb], log_path=[/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb], param_dict=[{'common_info': {'syn_id': '', 'source_description': '', 'analysis_tool': ['Hubble'], 'extended_info': {}, 'happen_time': '', 'field': 'Unknown', 'field2': '', 'betano': '', 'task_class': '', 'source': 'LogkitWeb', 'version': 'LMR-AL10 LMU-AL10 5.1.0.227(SP12C00E226R7P8)', 'dtsno': '', 'eventid': '', 'product': 'LMR-AL10', 'log_download_list': [[['RemoteLog_commercial_LMR-AL10_LMU-AL10 5.1.0.227(SP12C00E226R7P8)_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg=_PP1758525182974A-001_20250922151306.zip', 'https://aresocean.huawei.com/hubble/hubblesvc/hubbleportal/report/downloadLogByDate?fileName=7ccd3e2f0ae6413da4ca75da15c87571_RemoteLog_commercial_LMR-AL10_LMU-AL10+5.1.0.227%28SP12C00E226R7P8%29_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg%3D_PP1758525182974A-001_20250922151306.zip&date=20251025', '']]], 'errorcode1': '', 'errorcode3': '', 'errorcode2': '', 'errorcode4': '', 'batch_or_not': False, 'logsource': 'iDebug', 'task_uuid': '00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'username': 'HubbleTest', 'logtype': '', 'sn': 'QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg=', 'beta_com': 'beta', 'run_by_auto': True, 'issue_id': '', 'log_source': 'iDebug', 'datetime_str': '20250922151306', 'device': 'PHONE', 'use_scope': '', 'is_pro': True, 'happen_time_unixtime': 0, 'happen_date_sta': 0, 'happen_date_end': 99999999, 'Graphics_path': '/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/Graphics.json'}, 'user_defined_files': '/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/user_defined_files/eqr7plLp_stability_script_third_donghu', 'timeout_info': [], 'faulttag_has_result': False}] [2025-10-25 09:12:00,579][ERROR][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/analysis/LogAnalyst.py:152][start_analysis] 插件[eqr7plLp_stability_script_third_donghu]的分析逻辑失败, ['PluginClass' object has no attribute 'log_str_to_datetime'] Traceback (most recent call last): File "/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/analysis/LogAnalyst.py", line 117, in start_analysis plugin_single_result = plugin_modules[plugin_name].run_plugin_single(uuid_path, log_path, plugin_params) # analysis File "/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_test/eqr7plLp_stability_script_third_donghu.py", line 513, in run_plugin_single app_end_time = self.log_str_to_datetime(app_end) AttributeError: 'PluginClass' object has no attribute 'log_str_to_datetime' [2025-10-25 09:12:00,580] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:142] [insert_task_result_data] 开始往数据库发送0数据 [2025-10-25 09:12:00,871][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/HiviewKafka.py:126][producer_batch_send] kafka发送时间:0.2899346351623535 [2025-10-25 09:12:00,872] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:156] [insert_task_result_data] 发送 【0】类型 数据量:1 [2025-10-25 09:12:00,872] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:119] [insert_fault_analyse_data] 开始往数据库发送analyse_script_result数据 [2025-10-25 09:12:01,377][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/HiviewKafka.py:126][producer_batch_send] kafka发送时间:0.5044019222259521 [2025-10-25 09:12:01,379] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:130] [insert_fault_analyse_data] 发送 【analyse_script_result】类型 数据量:1 [2025-10-25 09:12:01,379] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:169] [insert_analyse_runtime_data] 开始往数据库发送 3 数据 [2025-10-25 09:12:01,673] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:177] [insert_analyse_runtime_data] 发送 【3】类型 数据量:1 [2025-10-25 09:12:01,674][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:214][log_analysis_only] !copying from /tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/loginfo.json to /opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb [2025-10-25 09:12:01,702][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:215][log_analysis_only] copy status: 0 [2025-10-25 09:12:01,703][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:214][log_analysis_only] !copying from /tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/00148acc-4d1b-4893-9f8d-cf5bc53c25eb.log to /opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb [2025-10-25 09:12:01,720][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:215][log_analysis_only] copy status: 0 [2025-10-25 09:12:01,721][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:217][log_analysis_only] Not A File: /tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/RemoteLog_commercial_LMR-AL10_LMU-AL10 5.1.0.227(SP12C00E226R7P8)_QSFtINNtWq7wANLUdZUERMoRCrVElIPcLSCLFXzl9Hg=_PP1758525182974A-001_20250922151306 [2025-10-25 09:12:01,721][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:214][log_analysis_only] !copying from /tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/unzip_status.json to /opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb [2025-10-25 09:12:01,737][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:215][log_analysis_only] copy status: 0 [2025-10-25 09:12:01,737][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:214][log_analysis_only] !copying from /tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/perf_data.db to /opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb [2025-10-25 09:12:01,753][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:215][log_analysis_only] copy status: 0 [2025-10-25 09:12:01,753][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:214][log_analysis_only] !copying from /tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/task_info.json to /opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb [2025-10-25 09:12:01,779][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:215][log_analysis_only] copy status: 0 [2025-10-25 09:12:01,780][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:214][log_analysis_only] !copying from /tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/Graphics.json to /opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb [2025-10-25 09:12:01,793][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:215][log_analysis_only] copy status: 0 [2025-10-25 09:12:01,793][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:217][log_analysis_only] Not A File: /tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/user_defined_files [2025-10-25 09:12:01,793][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:214][log_analysis_only] !copying from /tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/faultdiag_plugins.json to /opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb [2025-10-25 09:12:01,812][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:215][log_analysis_only] copy status: 0 [2025-10-25 09:12:01,893][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/analysis/LogAnalyst.py:203][analysis_end] 已拷贝: [/opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/faultdiag_plugins.json] [2025-10-25 09:12:01,894][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/HiviewKafka.py:179][send_msg_to_web] 发送Kafka消息的Topic: [AnalyseTask_EMUI2web], msg_dict: [{'quesid': '00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'resultpath': '/opt/samba3/20251025/17/00148acc-4d1b-4893-9f8d-cf5bc53c25eb', 'result': 102, 'updatefiles': '', 'error_detail': '', 'processflag': ''}] [2025-10-25 09:12:02,189] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:169] [insert_analyse_runtime_data] 开始往数据库发送 2 数据 [2025-10-25 09:12:02,386] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:177] [insert_analyse_runtime_data] 发送 【2】类型 数据量:1 [2025-10-25 09:12:02,387][WARNING][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:104][run] 执行任务: [COPY_USER_DEFINED_FILES] [2025-10-25 09:12:02,561][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:183][copy_user_defined_files] start user_defined_files [2025-10-25 09:12:02,601][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:194][copy_user_defined_files] copy status: 0 [2025-10-25 09:12:02,601][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:197][copy_user_defined_files] end user_defined_files [2025-10-25 09:12:02,601] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:169] [insert_analyse_runtime_data] 开始往数据库发送 2 数据 [2025-10-25 09:12:02,901] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:177] [insert_analyse_runtime_data] 发送 【2】类型 数据量:1 [2025-10-25 09:12:02,902][WARNING][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:104][run] 执行任务: [RESULT_PROCES_COMMONUTILS] [2025-10-25 09:12:03,047][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:124][result_proces_commonutils] start RESULT_PROCES_COMMONUTILS [2025-10-25 09:12:03,049] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_tools/ResultProcesCommonUtils/ResultProcesCommonUtils.py] [line:28] [start_analysis] ResultProcesCommonUtils start_analysis ... [2025-10-25 09:12:03,049][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/waiter/TaskRunner.py:129][result_proces_commonutils] end RESULT_PROCES_COMMONUTILS [2025-10-25 09:12:03,049] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:169] [insert_analyse_runtime_data] 开始往数据库发送 2 数据 [2025-10-25 09:12:03,347] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:177] [insert_analyse_runtime_data] 发送 【2】类型 数据量:1 [2025-10-25 09:12:03,347][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/manager/LogWaiterManager.py:120][run_task_queue] all tasks finish [2025-10-25 09:12:03,347][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:232][run] 分析任务主题运行结束,进入收尾工作。 [2025-10-25 09:12:03,347][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:48][wrapper] 当前函数 [run] 运行时间 4.21 秒 [2025-10-25 09:12:03,492] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:169] [insert_analyse_runtime_data] 开始往数据库发送 1 数据 [2025-10-25 09:12:03,786] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:177] [insert_analyse_runtime_data] 发送 【1】类型 数据量:1 [2025-10-25 09:12:03,786][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:125][start_analysis] ---------------------------------------------- [2025-10-25 09:12:03,787][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:126][start_analysis] 插件运行耗时:4.647161483764648 [2025-10-25 09:12:03,787][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:127][start_analysis] ---------------------------------------------- [2025-10-25 09:12:03,787] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/logistics/ResultAggregator.py] [line:57] [result_aggregation] {} [2025-10-25 09:12:03,787] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/logistics/ResultAggregator.py] [line:65] [result_aggregation] 执行插件[eqr7plLp_stability_script_third_donghu]的结果汇总功能 [2025-10-25 09:12:03,788] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/scripts/plugins/plugin_test/eqr7plLp_stability_script_third_donghu.py] [line:726] [plugin_result_refinement] 这是分析结果精加工函数 [2025-10-25 09:12:03,788] [ERROR] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/logistics/ResultAggregator.py] [line:34] [get_user_defined_device] 设备类型无法从homepage_info中获取 'only_run_device' [2025-10-25 09:12:03,789][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:48][wrapper] 当前函数 [run_after] 运行时间 0.00 秒 [2025-10-25 09:12:03,930] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:169] [insert_analyse_runtime_data] 开始往数据库发送 1 数据 [2025-10-25 09:12:04,123] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:177] [insert_analyse_runtime_data] 发送 【1】类型 数据量:1 [2025-10-25 09:12:04,123] [ERROR] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/logistics/LogReporter.py] [line:39] [generate_report] 分析结果存入报告生成数据库失败[[Errno 2] No such file or directory: '/tmp/Hubble/LogkitWeb/AnalyseTask_web2EMUI/00148acc-4d1b-4893-9f8d-cf5bc53c25eb/duplicate.json'] [2025-10-25 09:12:04,252] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/logistics/ResultSaver.py] [line:35] [save_final_results] Plugins结果[faultdiag_plugins.json]保存成功 [2025-10-25 09:12:04,259] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/logistics/ResultSaver.py] [line:103] [plugin_result2loginfo] 插件没有一级定界结果 [2025-10-25 09:12:04,259] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/core/service/logistics/ResultSaver.py] [line:139] [refill_loginfo] Refill loginfo OK. [2025-10-25 09:12:04,260][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:247][save] 结果保存成功!(非保存,只做其他修改) [2025-10-25 09:12:04,260][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:48][wrapper] 当前函数 [save] 运行时间 0.14 秒 [2025-10-25 09:12:04,405] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:169] [insert_analyse_runtime_data] 开始往数据库发送 1 数据 [2025-10-25 09:12:04,705] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:177] [insert_analyse_runtime_data] 发送 【1】类型 数据量:1 [2025-10-25 09:12:04,705][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:130][start_analysis] 此次日志分析任务结束。 [2025-10-25 09:12:04,706][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:131][start_analysis] 结果路径是: None [2025-10-25 09:12:04,706][INFO][1:132592204510912][/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/HiviewPlatform.py:48][wrapper] 当前函数 [start_analysis] 运行时间 14.17 秒 [2025-10-25 09:12:04,848] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:169] [insert_analyse_runtime_data] 开始往数据库发送 0 数据 [2025-10-25 09:12:05,146] [INFO] [/usr/src/app/aresdeepmindhubble-6.0/aresdeepmindhubble/Hubble/utils/basic/LogExtractionRecordUtils.py] [line:177] [insert_analyse_runtime_data] 发送 【0】类型 数据量:1 返回完整代码,如果太多生成不了,就只修改错误部分,请跟我说一下需要修改的代码是哪些,再说一下如何修改
10-26
#region ModbusTCP客户端 public class ModbusTCP : IDisposable { #region 字段与属性 private TcpClient tcpClient; //----------------------------------------TCP 客户端实例 private NetworkStream stream; //---------------------------------------网络流,用于收发数据 private volatile bool connected = false; //----------------------------当前连接状态 private readonly object socketLock = new object(); //------------------锁,保证线程安全 private static ushort transId = 1; //----------------------------------Modbus 事务ID,每次递增 string nameClient; //--------------------------------------------------客户端名称 private string currentIp; //-------------------------------------------当前连接IP private int currentPort; //--------------------------------------------当前连接端口 private volatile bool disposed = false; //-----------------------------是否已释放资源 private readonly object responseLock = new object(); //----------------响应锁 public del LogInfoDisplay; //-------------------------------------------委托变量-信息显示 public InfoMsg info = new InfoMsg("", ""); //---------------------------显示信息 public event Action<string> returnReceiveEvent;//-----------------------数据接收事件 //自动重连 private bool autoReconnect = true; //----------------------------------自动重连,默认开启 private int maxReconnectAttempts = 5; //-------------------------------重连次数 public bool Connected => connected; //---------------------------------连接状态 public bool AutoReconnect { get => autoReconnect; set { lock (socketLock) autoReconnect = value; } } public int MaxReconnectAttempts { get => maxReconnectAttempts; set { lock (socketLock) maxReconnectAttempts = value; } } public int ReconnectInterval { get; set; } = 5000; //------------------重连间隔 public int Timeout { get; set; } = 5000; //----------------------------超时时间(毫秒) public int AddressOffset { get; set; } = 0; //-------------------------地址偏移,默认M0对应0(如 D100 实际地址为100) private static readonly object idLock = new object(); private static ushort GenerateTransactionId() { lock (idLock) { return transId++; } } private System.Timers.Timer heartbeatTimer; //-------------------------心跳定时器 #endregion #region 数据类型枚举 /// <summary> /// 读写数据类型 /// 字 = 16位(short),双字 = 32位(int) /// </summary> public enum DataType : short { 字 = 1, 双字 = 2 } /// <summary> /// 寄存器内字节顺序(用于16位数据) /// </summary> public enum ByteOrder { BigEndian, // 高字节在前(AB) LittleEndian // 低字节在前(BA) } /// <summary> /// 定义32位数据在两个16位寄存器中的存储顺序 /// </summary> public enum WordOrder { /// <summary> /// 高字在前,低字在后(如西门子、标准 Modbus) /// 示例:0x12345678 → [0x1234][0x5678] /// </summary> HighFirst = 1, // 高位寄存器在前(ABCD) /// <summary> /// 低字在前,高字在后(如三菱、欧姆龙常见) /// 示例:0x12345678 → [0x5678][0x1234] /// </summary> LowFirst = 2 // 低位寄存器在前(CDAB) } #endregion #region 客户端初始化 /// <summary> /// 客户端初始化 /// </summary> /// <param name="name">客户端名称</param> /// <param name="receiveData">接收数据事件</param> /// <param name="info">消息显示事件</param> /// <param name="addressOffset">地址偏移量(如M0对应0,则为0;若PLC从1开始则设为-1)</param> public void Initialize(string name = "客户端", Action<string> receiveData = null, del info = null,int addressOffset = 0) { nameClient = name; //-----------------------客户端名称 returnReceiveEvent += receiveData; //-------接收数据事件 LogInfoDisplay += info; //------------------消息事件 this.info.msgType = nameClient; //----------log消息类型 this.AddressOffset = addressOffset; //------地址偏移设置 } /// <summary> /// 配置自动重连 /// </summary> /// <param name="enabled">开启重连true/false</param> /// <param name="intervalMs">重连间隔</param> /// <param name="maxAttempts">重连次数</param> public void ConfigureAutoReconnect(bool enabled, int intervalMs = 5000, int maxAttempts = 5) { AutoReconnect = enabled; ReconnectInterval = intervalMs; MaxReconnectAttempts = maxAttempts; } /// <summary> /// 配置并启用心跳功能 /// 通过定时器周期性向指定地址写入0/1交替信号,用于PLC监控连接状态 /// </summary> /// <param name="enabled">是否启用心跳功能,true=启用,false=禁用</param> /// <param name="address">心跳信号写入的PLC地址,如"D750"</param> /// <param name="intervalMs">心跳间隔时间,单位毫秒,默认500ms</param> /// <example> /// // 启用心跳,每500ms向D750地址写入0/1交替信号 /// modbusTCP.ConfigureHeartbeat(true, "D750", 500); /// /// // 禁用心跳功能 /// modbusTCP.ConfigureHeartbeat(false); /// </example> public void ConfigureHeartbeat(bool enabled = true, string address = "D700", int intervalMs = 500) { // 停止并释放之前的心跳定时器,避免多个定时器同时运行 heartbeatTimer?.Stop(); // 停止定时器 heartbeatTimer?.Dispose(); // 释放定时器资源 heartbeatTimer = null; // 如果禁用心跳,直接返回 if (!enabled) return; // 创建新的心跳定时器 heartbeatTimer = new System.Timers.Timer(intervalMs); bool state = false; // 心跳状态标志,用于0/1交替 heartbeatTimer.Elapsed += async (s, e) => { try { if (!connected) return; state = !state; await WriteSingleRegistersAsync(1, address, state ? 1 : 0); } catch (Exception ex) { // 异常必须在 async void 中捕获,否则会崩溃整个程序 Console.WriteLine("[Heartbeat] Exception: " + ex.Message); } }; heartbeatTimer.Start(); } #endregion #region 连接与断开 /// <summary> /// 连接到 Modbus TCP 服务器 /// </summary> /// <param name="ip">PLC IP 地址</param> /// <param name="port">端口,默认 502</param> /// <returns>是否连接成功</returns> public bool Connect(string ip, int port = 502) { lock (socketLock) { if (connected) return true; // 已连接则直接返回 try { tcpClient = new TcpClient(); tcpClient.SendTimeout = Timeout; tcpClient.ReceiveTimeout = Timeout; var result = tcpClient.BeginConnect(IPAddress.Parse(ip), port, null, null); var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(Timeout)); if (!success) { info.msg = $"连接超时 ({ip}:{port})"; LogInfoDisplay?.Invoke(info); return false; } tcpClient.EndConnect(result); stream = tcpClient.GetStream(); connected = true; currentIp = ip; currentPort = port; info.msg = $"连接成功 {ip}:{port}"; LogInfoDisplay?.Invoke(info); return true; } catch (Exception ex) { info.msg = $"连接失败: {ex.Message}"; LogInfoDisplay?.Invoke(info); Disconnect(); HandleReconnection(); return false; } } } /// <summary> /// 断开连接 /// </summary> public void Disconnect() { lock (socketLock) { if (!connected) return; try { connected = false; stream?.Close(); tcpClient?.Close(); stream = null; tcpClient = null; info.msg = "已断开连接"; LogInfoDisplay?.Invoke(info); } catch (Exception ex) { info.msg = $"断开异常: {ex.Message}"; LogInfoDisplay?.Invoke(info); } } } /// <summary> /// 处理自动重连逻辑(避免阻塞主线程) /// </summary> private void HandleReconnection() { if (!AutoReconnect || string.IsNullOrEmpty(currentIp)) return; Task.Run(() => { int attempts = 0; while (!connected && !disposed && attempts < MaxReconnectAttempts) { Thread.Sleep(ReconnectInterval); try { if (Connect(currentIp, currentPort)) { info.msg = "自动重连成功"; LogInfoDisplay?.Invoke(info); break; } attempts++; } catch { } } }); } #endregion #region 发送请求 /// <summary> /// 发送 Modbus 请求并等待响应 /// </summary> /// <param name="request">请求报文</param> /// <param name="expectedResponseLength">期望响应长度(用于校验)</param> /// <returns>响应字节数组,失败返回 null</returns> private async Task<byte[]> SendRequestAsync(byte[] request, int expectedResponseLength) { CancellationTokenSource cts = new CancellationTokenSource(); try { cts.CancelAfter(Timeout); // 建议 Timeout >= 3000 // 假设 tcpClient 已连接,networkStream = tcpClient.GetStream() if (stream == null || !stream.CanWrite) return null; // 设置事务ID(大端字节序) ushort transactionId = GenerateTransactionId(); // 自增或随机生成 request[0] = (byte)(transactionId >> 8); request[1] = (byte)(transactionId & 0xFF); // 发送请求 await stream.WriteAsync(request, 0, request.Length).ConfigureAwait(false); byte[] header = new byte[6]; // 先读6字节获取 Length int read = await ReadExactAsync(stream, header, 0, 6, cts.Token).ConfigureAwait(false); if (read != 6) return null; // 校验 Protocol ID short protocolId = (short)((header[2] << 8) | header[3]); if (protocolId != 0) { Console.WriteLine($"协议ID错误: {protocolId}"); return null; } // 解析后续长度(第5-6字节) int pduLength = (header[4] << 8) | header[5]; int totalResponseLength = 6 + pduLength; byte[] response = new byte[totalResponseLength]; Array.Copy(header, 0, response, 0, 6); if (pduLength > 0) { read = await ReadExactAsync(stream, response, 6, pduLength, cts.Token).ConfigureAwait(false); if (read != pduLength) return null; if (returnReceiveEvent != null) { string hexString = BitConverter.ToString(response).Replace("-", " "); returnReceiveEvent(hexString); } } // 校验 Transaction ID ushort responseTid = (ushort)((response[0] << 8) | response[1]); if (responseTid != transactionId) { Console.WriteLine("事务ID不匹配"); return null; } // 检查异常码 if (pduLength >= 2 && (response[7] & 0x80) == 0x80) { Console.WriteLine($"Modbus 异常码: 0x{response[8]:X2}"); return null; } // 最终长度校验 if (response.Length < expectedResponseLength) { Console.WriteLine("响应长度不足"); return null; } return response; } catch (TimeoutException) { Console.WriteLine("通信超时:等待响应时间过长"); return null; } catch (Exception ex) { Console.WriteLine("通信异常: " + ex.Message); connected = false; return null; } finally { // 必须手动 Dispose cts?.Dispose(); } } /// <summary> /// 确保异步读取指定数量的字节 /// </summary> private async Task<int> ReadExactAsync(NetworkStream stream, byte[] buffer, int offset, int count, CancellationToken ct = default) { int totalRead = 0; while (totalRead < count) { try { int read = await stream.ReadAsync(buffer, offset + totalRead, count - totalRead, ct).ConfigureAwait(false); if (read == 0) break; // 连接关闭 totalRead += read; } catch (OperationCanceledException) when (!ct.IsCancellationRequested) { throw new TimeoutException("读取超时"); } } return totalRead; } #endregion #region 地址解析 /// <summary> /// 如 PLC 地址从 1 开始编号,则设置 AddressOffset = -1 /// 解析地址字符串如 "D100" -> 实际地址 99(若偏移-1) /// </summary> private ushort ParseAddress(string address) { if (string.IsNullOrEmpty(address) || address.Length < 2) throw new ArgumentException("地址格式错误"); char type = char.ToUpper(address[0]); string numPart = address.Substring(1).Split('.')[0]; // 忽略位索引 if (!int.TryParse(numPart, out int rawAddr)) throw new ArgumentException("地址数值无效"); int baseAddress; switch (type) { case 'D': case 'M': case 'X': case 'Y': baseAddress = 0x0000; break; default: throw new ArgumentException($"不支持的地址类型: {type}"); } int finalAddr = baseAddress + rawAddr + AddressOffset; if (finalAddr < 0 || finalAddr > 65535) throw new ArgumentException("地址超出范围"); return (ushort)finalAddr; } #endregion #region 读取线圈寄存器(M寄存器) /// <summary> /// 读取线圈寄存器(功能码01) /// </summary> /// <param name="ID">客户端ID</param> /// <param name="address">地址</param> /// <param name="length">长度</param> /// <returns>值</returns> public async Task<bool[]> ReadCoilsAsync(byte ID, string address, int length) { if (length <= 0) length = 1; if (length > 2048) length = 2048; ushort addr = ParseAddress(address); int byteCount = (length + 7) / 8; byte[] request = new byte[12]; // 构建 Modbus TCP 请求报文 request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成) request[2] = 0x00; request[3] = 0x00; //----协议ID request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU) request[6] = ID; //-------------------------单元标识符ID request[7] = 0x01; //-----------------------功能码(读线圈) request[8] = (byte)(addr >> 8); //----------起始地址高字节 request[9] = (byte)(addr & 0xFF); //--------起始地址低字节 request[10] = (byte)(length >> 8); //-------数量高字节 request[11] = (byte)(length & 0xFF); //-----数量低字节 byte[] response = await SendRequestAsync(request, 9 + byteCount).ConfigureAwait(false); if (response == null || response.Length < 9 + byteCount) return null; bool[] result = new bool[length]; for (int i = 0; i < length; i++) { int byteIdx = i / 8; int bitIdx = i % 8; result[i] = (response[9 + byteIdx] & (1 << bitIdx)) != 0; } return result; } #endregion #region 读取输入状态 /// <summary> /// 读取输入状态(功能码02) /// </summary> /// <param name="ID">客户端ID</param> /// <param name="address">地址</param> /// <param name="length">长度</param> /// <returns>值</returns> public async Task<bool[]> ReadDiscreteInputsAsync(byte ID, string address, int length) { if (length <= 0) length = 1; if (length > 2048) length = 2048; ushort addr = ParseAddress(address); int byteCount = (length + 7) / 8; byte[] request = new byte[12]; // 构建 Modbus TCP 请求报文 request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成) request[2] = 0x00; request[3] = 0x00; //----协议ID request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU) request[6] = ID; //-------------------------单元标识符ID request[7] = 0x02; //-----------------------功能码(读离散输入) request[8] = (byte)(addr >> 8); //----------起始地址高字节 request[9] = (byte)(addr & 0xFF); //--------起始地址低字节 request[10] = (byte)(length >> 8); //-------数量高字节 request[11] = (byte)(length & 0xFF); //-----数量低字节 byte[] response = await SendRequestAsync(request, 9 + byteCount).ConfigureAwait(false); if (response == null || response.Length < 9 + byteCount) return null; bool[] result = new bool[length]; for (int i = 0; i < length; i++) { int byteIdx = i / 8; int bitIdx = i % 8; result[i] = (response[9 + byteIdx] & (1 << bitIdx)) != 0; } return result; } #endregion #region 读取保持寄存器(D寄存器) /// <summary> /// 读取保持寄存器(功能码03) /// </summary> /// <param name="ID">客户端ID</param> /// <param name="address">地址</param> /// <param name="length">长度</param> /// <param name="type">类型</param> /// <returns>值</returns> public async Task<int[]> ReadHoldingRegistersAsync(byte ID, string address, int length, DataType type = DataType.字) { if (length <= 0) length = 1; if (length > 125) length = 125; ushort addr = ParseAddress(address); ushort regCount = (ushort)(length * (ushort)type); byte[] request = new byte[12]; // 构建 Modbus TCP 请求报文 request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成) request[2] = 0x00; request[3] = 0x00; //----协议ID request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU) request[6] = ID; //-------------------------单元标识符ID request[7] = 0x03; //-----------------------功能码(读保持寄存器) request[8] = (byte)(addr >> 8); //----------起始地址高字节 request[9] = (byte)(addr & 0xFF); //--------起始地址低字节 request[10] = (byte)(regCount >> 8); //-----数量高字节 request[11] = (byte)(regCount & 0xFF); //---数量低字节 byte[] response = await SendRequestAsync(request, 9 + regCount * 2).ConfigureAwait(false); if (response == null || response.Length < 9 + regCount * 2) return null; return ParseRegisterResponse(response, length, type); } /// <summary> /// 解析寄存器响应(16位和32位) /// </summary> private int[] ParseRegisterResponse(byte[] response, int length, DataType type, ByteOrder byteOrder = ByteOrder.BigEndian, WordOrder wordOrder = WordOrder.HighFirst) { int[] result = new int[length]; if (type == DataType.字) { for (int i = 0; i < length; i++) { int dataIndex = 9 + i * 2; if (dataIndex + 1 < response.Length) { ushort value; if (byteOrder == ByteOrder.BigEndian) { value = (ushort)(response[dataIndex] << 8 | response[dataIndex + 1]); // AB } else { value = (ushort)(response[dataIndex] | response[dataIndex + 1] << 8); // BA } result[i] = (short)value; // 直接转换为有符号short } } } else //双字 { for (int i = 0; i < length; i++) { int dataIndex = 9 + i * 4; if (dataIndex + 3 < response.Length) { // Step 1: 先按字节序读两个16位寄存器 ushort reg1, reg2; if (byteOrder == ByteOrder.BigEndian) { reg1 = (ushort)(response[dataIndex] << 8 | response[dataIndex + 1]); reg2 = (ushort)(response[dataIndex + 2] << 8 | response[dataIndex + 3]); } else { reg1 = (ushort)(response[dataIndex + 1] << 8 | response[dataIndex]); reg2 = (ushort)(response[dataIndex + 3] << 8 | response[dataIndex + 2]); } // Step 2: 再按字序组合 uint value; if (wordOrder == WordOrder.HighFirst) { value = ((uint)reg1 << 16) | reg2; } else { value = ((uint)reg2 << 16) | reg1; } result[i] = (int)value; // 自动处理补码 } } } return result; } #endregion #region 读取输入寄存器 /// <summary> /// 读取输入寄存器(功能码04) /// </summary> /// <param name="ID">客户端ID</param> /// <param name="address">地址</param> /// <param name="length">长度</param> /// <param name="type">类型</param> /// <returns>值</returns> public async Task<int[]> ReadInputRegistersAsync(byte ID, string address, int length, DataType type = DataType.字) { if (length <= 0) length = 1; if (length > 125) length = 125; ushort addr = ParseAddress(address); ushort regCount = (ushort)(length * (ushort)type); byte[] request = new byte[12]; // 构建 Modbus TCP 请求报文 request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成) request[2] = 0x00; request[3] = 0x00; //----协议ID request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU) request[6] = ID; //-------------------------单元标识符ID request[7] = 0x04; //-----------------------功能码(读输入寄存器) request[8] = (byte)(addr >> 8); //----------起始地址高字节 request[9] = (byte)(addr & 0xFF); //--------起始地址低字节 request[10] = (byte)(regCount >> 8); //-----数量高字节 request[11] = (byte)(regCount & 0xFF); //---数量低字节 byte[] response = await SendRequestAsync(request, 9 + regCount * 2).ConfigureAwait(false); if (response == null || response.Length < 9 + regCount * 2) return null; return ParseRegisterResponse(response, length, type); } #endregion #region 写入单个线圈寄存器值(M寄存器) /// <summary> /// 写入单个线圈寄存器值(功能码05) /// </summary> /// <param name="ID">客户端ID</param> /// <param name="address">地址</param> /// <param name="value">值</param> public async Task<bool> WriteSingleCoilsAsync(byte ID, string address, bool value) { if (!connected) return false; ushort addr = ParseAddress(address); byte[] request = new byte[12]; // 构建 Modbus TCP 请求报文 request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成) request[2] = 0x00; request[3] = 0x00; //----协议ID request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU) request[6] = ID; //-------------------------单元标识符ID request[7] = 0x05; //-----------------------功能码(写单个线圈) request[8] = (byte)(addr >> 8); //----------起始地址高字节 request[9] = (byte)(addr & 0xFF); //--------起始地址低字节 request[10] = (byte)(value ? 0xFF : 0x00); //数据(FF表示ON,00表示OFF) request[11] = 0x00; //----------------------数据(固定0x00) try { byte[] response = await SendRequestAsync(request, 12).ConfigureAwait(false); return response != null && response.Length >= 12 && response[7] == 0x05 && (response[8] << 8 | response[9]) == addr; } catch { return false; } } #endregion #region 写入单个保持寄存器值(D寄存器) /// <summary> /// 写入单个保持寄存器值(功能码06)(功能码16) /// </summary> /// <param name="ID">设备ID</param> /// <param name="address">地址</param> /// <param name="value">值(16位或32位)</param> /// <param name="type">数据类型(字=16位,双字=32位)</param> /// <returns>是否写入成功</returns> public async Task<bool> WriteSingleRegistersAsync(byte ID, string address, int value, DataType type = DataType.字) { if (!connected) return false; if (type == DataType.字) { return await WriteSingleRegister16(ID, address, (short)value); } else { return await WriteDoubleRegister32(ID, address, (int)value); } } /// <summary> /// 16位写入实现(功能码06) /// </summary> /// <param name="ID"></param> /// <param name="address"></param> /// <param name="value"></param> /// <returns></returns> private async Task<bool> WriteSingleRegister16(byte ID, string address, short value) { ushort addr = ParseAddress(address); ushort uvalue = (ushort)value; byte[] request = new byte[12]; // 构建 Modbus TCP 请求报文 request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成) request[2] = 0x00; request[3] = 0x00; //----协议ID request[4] = 0x00; request[5] = 0x06; //----后续长度:6字节(PDU) request[6] = ID; //-------------------------单元标识符ID request[7] = 0x06; //-----------------------功能码(写16位保存寄存器) request[8] = (byte)(addr >> 8); //----------起始地址高字节 request[9] = (byte)(addr & 0xFF); //--------起始地址低字节 request[10] = (byte)(uvalue >> 8); //-------数量高字节 request[11] = (byte)(uvalue & 0xFF); //-----数量低字节 byte[] response = await SendRequestAsync(request, 12).ConfigureAwait(false); return response != null && response.Length >= 12 && response[7] == 0x06 && (response[8] << 8 | response[9]) == addr; } /// <summary> /// 32位写入实现(功能码16) /// </summary> /// <param name="ID"></param> /// <param name="address"></param> /// <param name="value"></param> /// <returns></returns> private async Task<bool> WriteDoubleRegister32(byte ID, string address, int value, WordOrder wordOrder = WordOrder.HighFirst) { short hiWord = (short)(value >> 16); short loWord = (short)(value & 0xFFFF); short[] words = wordOrder == WordOrder.HighFirst ? new[] { hiWord, loWord } : new[] { loWord, hiWord }; return await WriteMultipleRegistersAsync(ID, address, words); } #endregion #region 写入多个线圈寄存器值(M寄存器) /// <summary> /// 写入多个线圈寄存器值(功能码15)- 所有线圈写入相同值 /// </summary> /// <param name="ID">设备ID</param> /// <param name="address">起始地址(如"M100")</param> /// <param name="length">写入的线圈数量</param> /// <param name="value">所有线圈的状态(true=ON, false=OFF)</param> /// <returns>是否写入成功</returns> /// <example> /// WriteMultipleCoils(1, "M100", 2, true); /// // 写入M100=true, M101=true /// WriteMultipleCoils(1, "M100", 5, false); /// // 写入M100~M104全部为false /// </example> public async Task<bool> WriteMultipleCoilsAsync(byte ID, string address, int length, bool value) { if (!connected) return false; bool[] values = new bool[length]; for (int i = 0; i < length; i++) values[i] = value; return await WriteMultipleCoilsAsync(ID, address, values); } /// <summary> /// 写入多个线圈寄存器值(bool数组版本)- 每个线圈独立控制 /// </summary> /// <param name="ID">设备ID</param> /// <param name="address">起始地址</param> /// <param name="values">每个线圈的状态数组</param> /// <returns>是否写入成功</returns> /// <example> /// WriteMultipleCoils(1, "M100", new bool[] { true, false, true }); /// // 写入M100=true, M101=false, M102=true /// </example> public async Task<bool> WriteMultipleCoilsAsync(byte ID, string address, bool[] values) { if (!connected) return false; ushort addr = ParseAddress(address); int byteCount = (values.Length + 7) / 8; byte[] coilBytes = new byte[byteCount]; for (int i = 0; i < values.Length; i++) { if (values[i]) coilBytes[i / 8] |= (byte)(1 << (i % 8)); } byte[] request = new byte[13 + byteCount]; // 构建 Modbus TCP 请求报文 request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成) request[2] = 0x00; request[3] = 0x00; //----协议ID request[4] = (byte)((request.Length - 6) >> 8); request[5] = (byte)((request.Length - 6) & 0xFF); request[6] = ID; //-------------------------单元标识符ID request[7] = 0x0F; //-----------------------功能码(写多线圈寄存器) request[8] = (byte)(addr >> 8); //----------起始地址高字节 request[9] = (byte)(addr & 0xFF); //--------起始地址低字节 request[10] = (byte)(values.Length >> 8); //数量高字节 request[11] = (byte)(values.Length & 0xFF);//数量低字节 request[12] = (byte)byteCount; Array.Copy(coilBytes, 0, request, 13, byteCount); byte[] response = await SendRequestAsync(request, 12).ConfigureAwait(false); return response != null && response.Length >= 12; } #endregion #region 写入多个保持寄存器值(D寄存器) /// <summary> /// 写入多个保持寄存器值(功能码16) - 所有寄存器写入相同值 /// </summary> /// <param name="ID">设备ID</param> /// <param name="address">起始地址(如"D100")</param> /// <param name="length">写入的寄存器数量</param> /// <param name="value">所有寄存器的值</param> /// <param name="type">数据类型(字=16位,双字=32位)</param> /// <returns>是否写入成功</returns> /// <example> /// WriteMultipleRegister(1, "D100", 3, 100); /// // 写入D100=100, D101=100, D102=100 /// WriteMultipleRegister(1, "D200", 2, 123456789, ReadTypeEnum.双字); /// // 写入D200-D201=123456789, D202-D203=123456789 /// </example> public async Task<bool> WriteMultipleRegistersAsync(byte ID, string address, int length, int value, DataType type = DataType.字) { if (!connected) return false; if (type == DataType.字) { short[] arr = new short[length]; for (int i = 0; i < length; i++) arr[i] = (short)value; return await WriteMultipleRegistersAsync(ID, address, arr); } else { short[] arr = new short[length * 2]; for (int i = 0; i < length; i++) { arr[i * 2] = (short)(value >> 16); arr[i * 2 + 1] = (short)(value & 0xFFFF); } return await WriteMultipleRegistersAsync(ID, address, arr); } } /// <summary> /// 写入多个保持寄存器值(功能码16) /// </summary> /// <param name="ID"></param> /// <param name="address"></param> /// <param name="values"></param> /// <returns></returns> private async Task<bool> WriteMultipleRegistersAsync(byte ID, string address, short[] values) { ushort addr = ParseAddress(address); byte[] request = new byte[13 + values.Length * 2]; // 构建 Modbus TCP 请求报文 request[0] = 0; request[1] = 0; //----------事务ID(由 SendRequestAsync 填充或自动生成) request[2] = 0x00; request[3] = 0x00; //----协议ID request[4] = (byte)((request.Length - 6) >> 8); request[5] = (byte)((request.Length - 6) & 0xFF); request[6] = ID; //-------------------------单元标识符ID request[7] = 0x10; //-----------------------功能码(写多保存寄存器) request[8] = (byte)(addr >> 8); //----------起始地址高字节 request[9] = (byte)(addr & 0xFF); //--------起始地址低字节 request[10] = (byte)(values.Length >> 8); //数量高字节 request[11] = (byte)(values.Length & 0xFF);//数量低字节 request[12] = (byte)(values.Length * 2); for (int i = 0; i < values.Length; i++) { request[13 + i * 2] = (byte)(values[i] >> 8); request[13 + i * 2 + 1] = (byte)(values[i] & 0xFF); } byte[] response = await SendRequestAsync(request, 12).ConfigureAwait(false); return response != null && response.Length >= 12 && response[7] == 0x10 && (response[8] << 8 | response[9]) == addr && (response[10] << 8 | response[11]) == values.Length; } #endregion #region 同步支持 /// <summary> /// 读取线圈寄存器(功能码01) /// </summary> public bool[] ReadCoils(byte ID, string address, int length) { return ReadCoilsAsync(ID, address, length).GetAwaiter().GetResult(); } /// <summary> /// 读取输入状态(功能码02) /// </summary> public bool[] ReadDiscreteInputs(byte ID, string address, int length) { return ReadDiscreteInputsAsync(ID, address, length).GetAwaiter().GetResult(); } /// <summary> /// 读取保持寄存器(功能码03) /// </summary> public int[] ReadHoldingRegisters(byte ID, string address, int length, DataType type = DataType.字) { return ReadHoldingRegistersAsync(ID, address, length, type).GetAwaiter().GetResult(); } /// <summary> /// 读取输入寄存器(功能码04) /// </summary> public int[] ReadInputRegisters(byte ID, string address, int length, DataType type = DataType.字) { return ReadInputRegistersAsync(ID, address, length, type).GetAwaiter().GetResult(); } /// <summary> /// 写入单个线圈(功能码05) /// </summary> public bool WriteSingleCoils(byte ID, string address, bool value) { return WriteSingleCoilsAsync(ID, address, value).GetAwaiter().GetResult(); } /// <summary> /// 写入单个寄存器(功能码06 或 16) /// </summary> public bool WriteSingleRegisters(byte ID, string address, int value, DataType type = DataType.字) { return WriteSingleRegistersAsync(ID, address, value, type).GetAwaiter().GetResult(); } /// <summary> /// 写入多个线圈(相同值)(功能码15) /// </summary> public bool WriteMultipleCoils(byte ID, string address, int length, bool value) { return WriteMultipleCoilsAsync(ID, address, length, value).GetAwaiter().GetResult(); } /// <summary> /// 写入多个线圈(数组)(功能码15) /// </summary> public bool WriteMultipleCoils(byte ID, string address, bool[] values) { return WriteMultipleCoilsAsync(ID, address, values).GetAwaiter().GetResult(); } /// <summary> /// 写入多个寄存器(相同值)(功能码16) /// </summary> public bool WriteMultipleRegisters(byte ID, string address, int length, int value, DataType type = DataType.字) { return WriteMultipleRegistersAsync(ID, address, length, value, type).GetAwaiter().GetResult(); } #endregion #region 客户端关闭 /// <summary> /// 释放所有资源 /// </summary> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposed) return; disposed = true; if (disposing) { // 释放托管资源 heartbeatTimer?.Dispose(); stream?.Dispose(); tcpClient?.Dispose(); } } #endregion } #endregion这个代码在执行private void btn_桌盘_Click(object sender, EventArgs e) { automation.modbusTCP.WriteMultipleCoils(1, "M0", 5, true); automation.modbusTCP.WriteSingleCoils(1, "M16", true); //automation.modbusTCP.WriteSingleRegisters(1, "D0", 100); //automation.modbusTCP.WriteMultipleRegisters(1, "D1" , 4, 50); //bool[] result = await automation.modbusTCP.ReadCoilsAsync(1, "M0", 1); }时会导致界面卡死,数据写入不了。但是在执行 private void btn_桌盘_Click(object sender, EventArgs e) { automation.modbusTCP.WriteMultipleCoilsAsync(1, "M0", 5, true); automation.modbusTCP.WriteSingleCoilsAsync(1, "M16", true); //automation.modbusTCP.WriteSingleRegisters(1, "D0", 100); //automation.modbusTCP.WriteMultipleRegisters(1, "D1" , 4, 50); //bool[] result = await automation.modbusTCP.ReadCoilsAsync(1, "M0", 1); }这个代码时,能够写入成功。但是执行private void btn_自动切屏_Click(object sender, EventArgs e) { //automation.modbusTCP.WriteSingleCoilsAsync(1, "M1000", true); bool[] result1 = automation.modbusTCP.ReadCoils(1, "M15", 1); bool[] result = automation.modbusTCP.ReadCoils(1, "M16", 1); if (result != null && result.Length > 0) { bool m16Value = result[0]; bool m15Value = result1[0]; MessageBox.Show("M16 = " + m16Value + "M15 = " + m15Value); } else { MessageBox.Show("读取失败"); } }这个代码,会弹出读取失败。请仔细阅读理解,找出问题
10-14
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值