Apache PLC4X S7批量读取异常:请求与返回项数量不匹配
关于PLC4X读取snap7 Python模拟器标签数量限制的问题
我是PLC4X及编程领域新手,现使用Java 17(Spring Boot 2.5.0)编写服务读取本地搭建的snap7 Python模拟器数据,目标是实现每秒读取约1000个标签或尽可能快的读取速度。
我使用的是PLC4X 0.12.0版本,按照文档操作后,单次请求中标签数量在18个以内可正常读取,但当添加超过18个tagAddresses时,应用会挂起并抛出如下异常:
o.a.p.j.s.r.protocol.S7ProtocolLogic : org.apache.plc4x.java.api.exceptions.PlcProtocolException: The number of requested items doesn't match the number of returned items
通过将标签地址列表拆分成批量处理可解决该问题,但我想了解是否是自身操作有误,或是该限制为设计意图,亦或是模拟器存在问题(我仅修改了部分可读取的值)。
我的代码如下:
@Component public class S7Poller { private static final Logger LOG = LoggerFactory.getLogger(S7Poller.class); private PlcConnection connection; private static final LinkedHashMap<String, String> tagAddresses = new LinkedHashMap<>(); @PostConstruct public void init() { try { connection = new DefaultPlcDriverManager() .getConnection("s7://localhost:1102?local-rack=0&local-slot=1"); } catch (PlcConnectionException e) { e.printStackTrace(); System.err.println("Error connecting to the PLC: " + e.getMessage()); } } @Scheduled(cron = "${cron}") public void readPLC() { if (connection != null) { try { PlcReadRequest.Builder readRequest = connection.readRequestBuilder(); readRequest.addTagAddress("boolean1", "%DB1.DBX0:0:BOOL"); readRequest.addTagAddress("boolean2", "%DB1.DBX0:1:BOOL"); readRequest.addTagAddress("boolean3", "%DB1.DBX0:2:BOOL"); readRequest.addTagAddress("boolean4", "%DB1.DBX0:3:BOOL"); readRequest.addTagAddress("boolean5", "%DB1.DBX0:4:BOOL"); readRequest.addTagAddress("boolean6", "%DB1.DBX0:5:BOOL"); readRequest.addTagAddress("boolean7", "%DB1.DBX0:6:BOOL"); readRequest.addTagAddress("boolean8", "%DB1.DBX0:7:BOOL"); readRequest.addTagAddress("sint1", "%DB1.DBB10:SINT"); readRequest.addTagAddress("sint2", "%DB1.DBB11:SINT"); readRequest.addTagAddress("sint3", "%DB1.DBB12:SINT"); readRequest.addTagAddress("sint4", "%DB1.DBB13:SINT"); readRequest.addTagAddress("usint1", "%DB1.DBB20:USINT"); readRequest.addTagAddress("usint2", "%DB1.DBB21:USINT"); readRequest.addTagAddress("int1", "%DB1.DBW30:INT"); readRequest.addTagAddress("int2", "%DB1.DBW32:INT"); readRequest.addTagAddress("int3", "%DB1.DBW34:INT"); readRequest.addTagAddress("int4", "%DB1.DBW36:INT"); readRequest.addTagAddress("int5", "%DB1.DBW38:INT"); readRequest.addTagAddress("real1", "%DB1.DBD60:REAL"); readRequest.addTagAddress("real2", "%DB1.DBD76:REAL"); readRequest.addTagAddress("real3", "%DB1.DBD92:REAL"); readRequest.addTagAddress("string1", "%DB1.DBX100:STRING"); PlcReadRequest rr = readRequest.build(); PlcReadResponse response = rr.execute().get(); logResponses(response); } catch (ExecutionException | InterruptedException e) { e.printStackTrace(); throw new RuntimeException(e); } } } private void logResponses(PlcReadResponse response) { LOG.info("boolean1: " + response.getBoolean("boolean1")); LOG.info("boolean2: " + response.getBoolean("boolean2")); LOG.info("boolean3: " + response.getBoolean("boolean3")); LOG.info("boolean4: " + response.getBoolean("boolean4")); LOG.info("boolean5: " + response.getBoolean("boolean5")); LOG.info("boolean6: " + response.getBoolean("boolean6")); LOG.info("boolean7: " + response.getBoolean("boolean7")); LOG.info("boolean8: " + response.getBoolean("boolean8")); LOG.info("sint1: " + response.getInteger("sint1")); LOG.info("sint2: " + response.getInteger("sint2")); LOG.info("sint3: " + response.getInteger("sint3")); LOG.info("sint4: " + response.getInteger("sint4")); LOG.info("usint1: " + response.getInteger("usint1")); LOG.info("usint2: " + response.getInteger("usint2")); LOG.info("int1: " + response.getInteger("int1")); LOG.info("int2: " + response.getInteger("int2")); LOG.info("int3: " + response.getInteger("int3")); LOG.info("int4: " + response.getInteger("int4")); LOG.info("int5: " + response.getInteger("int5")); LOG.info("real1: " + response.getFloat("real1")); LOG.info("real2: " + response.getFloat("real2")); LOG.info("real3: " + response.getFloat("real3")); LOG.info("string1: " + response.getString("string1")); } }
可能的原因分析
- 模拟器局限性:snap7 Python模拟器并非真实PLC设备,内部处理逻辑可能存在单次请求的标签数量上限,无法正确响应超过阈值的批量读取请求。
- PLC4X旧版本缺陷:你使用的PLC4X 0.12.0是较旧的版本,可能存在批量请求解析的bug,导致请求项和响应项数量不匹配。
- 标签地址离散性影响:请求的标签分散在DB块的不同偏移位置,PLC4X会将这类请求拆分为多个底层S7协议请求,模拟器可能无法正确合并这些请求的响应结果。
解决建议
- 升级PLC4X版本:更换为PLC4X的最新稳定版本,新版本通常会修复协议解析和批量请求处理的兼容性问题。
- 优化标签读取策略:将同一连续地址段的标签合并读取,比如把DB1.DBX0的8个BOOL合并为
%DB1.DBB0:BYTE读取,再在代码中拆分每个位,减少请求项数量的同时提升读取效率。 - 验证真实PLC:如果条件允许,用真实S7 PLC测试相同的批量读取请求,确认问题是否来源于模拟器。
- 设置合理批量阈值:通过测试找到模拟器能稳定处理的最大单次标签数量,以此为阈值自动拆分请求列表,平衡读取速度和稳定性。
内容的提问来源于stack exchange,提问作者deleteJavascript
相关产品推荐
相关产品推荐

