这样做真不知道对不对

这学期开了计算机网络 ,组成原理 ,微机原理,感觉 上课讲的都是理论知识,感觉没啥实用价值,原理网络二本厚书,真心没时间看下去。上课有时都想昏昏欲睡,原理老师说如果将来从事的与这本书没多大关系,考试考过就OK了。我将来想从事WEB移动开发,现在有时逃课看书看视频敲代码,感觉 这些书本知识过于 理论化了,现在上课半逃半上,有的已经放弃了,这样做会不会捡了芝麻丢了西瓜
你这句话非常关键: > **“但这样写能用,你这样不能用。”** 这说明: - 你的原始代码在**某种特定条件下确实可以通信** - 而我重构的版本虽然更“规范”,但在你的环境下反而失败了 --- ## ✅ 我们现在要的事是:**逆向推理——从“能用”出发,找出真实协议细节** 不是所有三菱 PLC 都严格遵循标准 MC 协议。 尤其当你使用的是: - FX5U? - Q03UD? - iQ-F? - 或者启用了“固定缓冲区 + FROM/TO”模式? 👉 实际通信帧可能和官方文档不一致! --- ## 🔍 第一步:我们必须知道——**你原来的“能用”的代码到底发了什么?** 请你一件事: ### 🛠 在 `SendRequestAsync` 中打印出真正发送的 HEX 报文(你原来能用的那个版本) 比如加一行日志: ```csharp Console.WriteLine("发送: " + BitConverter.ToString(request).Replace("-", " ")); ``` 然后执行一次成功的读操作(如读 D100),得到类似: ``` 发送: 50 00 00 FF FF 03 00 0D 00 10 00 01 00 01 00 00 00 00 00 A8 01 00 ``` 📌 把这个完整的 HEX 发给我。 --- ## ⚠️ 关键差异点分析(为什么我的版本“不能用”) | 差异项 | 你的原版(能用) | 我的重构版(不能用) | 可能影响 | |-------|------------------|------------------------|---------| | `req[7] = 0x0D` | ✅ 保留 | ❌ 改为 `0x0F` | 🔴 极可能是这里错了! | | 副标题检查 | 没校验或忽略 | 校验 `D0 00` 响应头 | ❌ 可能响应不是 D0 | | 地址偏移处理 | 直接加 | 统一加 | 可能某些元件不该偏移 | | 写命令长度计算 | `13 + len` | 同样 | 应该没问题 | | 字节序 | BA | BA | 一致 | --- ## 🔥 最大嫌疑:`req[7] = 0x0D` 竟然是对的?! 你原来的代码是: ```csharp req[7] = 0x0D; req[8] = 0x00; ``` 而我认为应该是 `0x0F`(15字节),因为从 `req[9]` 到 `req[21]` 是 13 字节?不对! 我们重新数一遍: | Offset | 内容 | 字节数 | |--------|------|--------| | 9~10 | 监视定时器 | 2 | | 11~12 | 命令(01 00) | 2 | | 13~14 | 子命令(01 00) | 2 | | 15~17 | 地址(低位三字节) | 3 | | 18 | 地址区 | 1 | | 19 | device code | 1 | | 20~21 | 点数 | 2 | | **总计** | | **13 字节** | 所以请求数据长度 = 13 → `0x0D` ✅ **你的 `0x0D` 是正确的!!!** --- ## 🧠 错误在我这边! 我在重构时错误地认为应该是 `0x0F`,但实际上: > **MC 协议中,“请求数据长度”是从“监视定时器”开始到命令结束的所有字节。** 即从 `req[9]` 到 `req[21]` 共 13 字节 → 所以 `req[7] = 0x0D` ✅ 正确! --- ## ✅ 修改建议:把 `0x0F` 改回 `0x0D` 将我之前写的 `CreateReadCommand` 中这一行改回来: ```csharp // ❌ 错误 req[7] = 0x0F; // ✅ 正确 req[7] = 0x0D; req[8] = 0x00; ``` 同理,写命令也要注意总长度是否正确。 --- ## 📦 再检查:响应帧副标题真的是 `D0 00` 吗? 你在接收时了判断: ```csharp if (header[0] != 0xD0 || header[1] != 0x00) return null; ``` 但这只适用于 **QnA 3E 帧的响应帧**。 而有些 CPU(尤其是 FX5U、iQ-F)返回的响应帧副标题是 `50 00`,而不是 `D0 00`! ⚠️ 这意味着:**某些型号的 PLC 不区分请求/响应副标题!** ### ✅ 解决方案:不要强制校验副标题为 `D0 00` 改为只检查前两个字节是否合法(可选): ```csharp // 放宽条件:只要收到 9 字节头部即可,不强求是 D0 // if (header[0] != 0xD0 || header[1] != 0x00) return null; ``` 或者记录实际收到的值用于调试。 --- ## ✅ 建议你现在的几件事 ### 1. 打印原始发送帧(确认 `0x0D` 是否必须) ```csharp Console.WriteLine("Send: " + BitConverter.ToString(request)); ``` ### 2. 打印原始接收帧(看是不是 `D0 00` 开头) ```csharp Console.WriteLine("Recv Header: {0:X2} {1:X2}", header[0], header[1]); ``` ### 3. 如果接收帧也是 `50 00` 开头 → 删除副标题校验! --- ## ✅ 修正后的 `CreateReadCommand`(保留 `0x0D`) ```csharp private byte[] CreateReadCommand(byte deviceCode, int address, int count, bool isBit) { address += AddressOffset; byte[] req = new byte[22]; req[0] = 0x50; req[1] = 0x00; // 副标题 req[2] = 0x00; // 网络号 req[3] = 0xFF; req[4] = 0xFF; // PLC编号 req[5] = 0x03; req[6] = 0x00; // IO+站号 req[7] = 0x0D; req[8] = 0x00; // ✅ 请求数据长度 = 13 字节 req[9] = 0x10; req[10] = 0x00; // 监视定时器 req[11] = 0x01; req[12] = 0x00; // 命令:读 req[13] = (byte)(isBit ? 0x00 : 0x01); req[14] = 0x00; // 子命令 req[15] = (byte)(address & 0xFF); req[16] = (byte)((address >> 8) & 0xFF); req[17] = (byte)((address >> 16) & 0xFF); req[18] = 0x00; req[19] = deviceCode; req[20] = (byte)(count & 0xFF); req[21] = (byte)(count >> 8); return req; } ``` --- ## ✅ 总结:为什么“你那样能用,我这样不能用”? | 原因 | 说明 | |------|------| | 🔴 `req[7] = 0x0F` 错误 | 实际应为 `0x0D`,否则 PLC 返回 `0x0401` | | 🔴 强制校验响应为 `D0 00` | 某些型号返回 `50 00`,导致被丢弃 | | 🟡 地址偏移应用不当 | M/X/D 是否都需要偏移?需根据 PLC 设置 | | 🟡 心跳异常未捕获 | `async void` 导致崩溃 | --- ## ✅ 下一步行动清单 1. [ ] 将 `req[7]` 改为 `0x0D` 2. [ ] 删除或注释掉 `header[0] != 0xD0` 的判断 3. [ ] 添加日志输出发送和接收的原始 HEX 4. [ ] 测试读取 D0,观察是否成功 5. [ ] 提供一条成功的发送和接收 HEX 数据,我来帮你验证 --- ##
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值