{"record":{"id":"a7acd6711abbc0d8","repo":"YunaiV/ruoyi-vue-pro","slug":"error-a7acd6","errorCode":null,"errorMessage":"功能码 {} 不支持写操作","messagePattern":"功能码 (.+?) 不支持写操作","errorType":"validation","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/common/utils/IotModbusTcpClientUtils.java","lineNumber":110,"sourceCode":"     * 写入 Modbus 数据\n     *\n     * @param connection Modbus 连接\n     * @param slaveId    从站地址\n     * @param point      点位配置\n     * @param values     要写入的值\n     * @return 是否成功\n     */\n    public static Future<Boolean> write(IotModbusTcpClientConnectionManager.ModbusConnection connection,\n                                        Integer slaveId,\n                                        IotModbusPointRespDTO point,\n                                        int[] values) {\n        return connection.executeBlocking(tcpConnection -> {\n            try {\n                // 1. 创建请求\n                ModbusRequest request = createWriteRequest(point.getFunctionCode(),\n                        point.getRegisterAddress(), point.getRegisterCount(), values);\n                if (request == null) {\n                    throw new RuntimeException(\"功能码 \" + point.getFunctionCode() + \" 不支持写操作\");\n                }\n                request.setUnitID(slaveId);\n\n                // 2. 执行事务（请求）\n                ModbusTCPTransaction transaction = new ModbusTCPTransaction(tcpConnection);\n                transaction.setRequest(request);\n                transaction.execute();\n                return true;\n            } catch (Exception e) {\n                throw new RuntimeException(String.format(\"Modbus 写入失败 [slaveId=%d, identifier=%s, address=%d]\",\n                        slaveId, point.getIdentifier(), point.getRegisterAddress()), e);\n            }\n        });\n    }\n\n    /**\n     * 创建读取请求\n     */","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/YunaiV/ruoyi-vue-pro/blob/0418084e222612af2fc1141f566af454f9236ab1/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/common/utils/IotModbusTcpClientUtils.java#L92-L128","documentation":"Thrown by write() when createWriteRequest() returns null, which happens for read-only function codes FC_READ_DISCRETE_INPUTS (2) and FC_READ_INPUT_REGISTERS (4). By Modbus specification, discrete inputs and input registers are read-only and cannot be written. The framework's write path deliberately keys off the READ-style function code stored in the point config, and returns null (rather than throwing inside createWriteRequest) for the genuinely read-only types.","triggerScenarios":"A downstream write/command targets a point whose config functionCode is 2 (discrete input) or 4 (input register); the device config incorrectly marks a sensor point as an input register when a holding register was intended.","commonSituations":"Operator configures a sensor point using FC4 (input register) but a rule/scene later tries to calibrate or write it; product model maps an actuator to the wrong Modbus table; misconfigured point type.","solutions":["Change the point's functionCode to a writable type: FC3 (holding register, mapped to write FC6/16) for analog values or FC1 (coil, mapped to write FC5/15) for binary values.","If the value is genuinely read-only, remove or reject the downstream write command at the service layer instead of letting it reach the Modbus write path.","Validate writability before calling write (see defense)."],"exampleFix":"// before: point configured with read-only FC4 (input register), write throws\n\n// after: use FC3 (holding register) so the framework maps it to a write request\npoint.setFunctionCode(3); // holding register — writable","handlingStrategy":"validation","validationCode":"// before writing, check the point's function code is writable\nboolean isWritableFc(Integer fc) {\n    // framework maps FC1 (coil) and FC3 (holding register) to write requests\n    return fc != null && (fc == 1 || fc == 3);\n}\nif (!isWritableFc(point.getFunctionCode())) {\n    return Future.failedFuture(new IllegalStateException(\n        \"点位 \" + point.getIdentifier() + \" 功能码 \" + point.getFunctionCode() + \" 不支持写操作\"));\n}","typeGuard":"boolean isWritablePoint(IotModbusPointRespDTO point) {\n    Integer fc = point.getFunctionCode();\n    return fc != null && (fc == 1 || fc == 3);\n}","tryCatchPattern":null,"preventionTips":["Mark read-only points in the device model and reject write commands at the service layer before they reach Modbus.","Use FC1 (coil) / FC3 (holding register) for any point that must accept writes.","Validate functionCode writability when loading/refreshing device config so misconfiguration is caught early."],"tags":["modbus","iot","config","validation"],"backgroundTag":null,"analyzedSha":"0418084e222612af2fc1141f566af454f9236ab1","analyzedAt":"2026-08-14T00:56:18.412Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}