SECS/GEM S14F1 单对象查询异常
故事的起因是我们在使用自研的上位机工具发送获取属性请求时,明明在命令行里带上了具体的控制任务ID,满心以为机台会乖乖返回这一个对象的数据,结果它却一股脑地把内存里所有的控制任务全吐了出来。为了弄清到底是谁的责任,我们直接深入到了底层的字节码去抓包对证。抓包结果清清楚楚地显示,我们的工具确实把那个目标ID转换成字节发了出去,这就排除了我们软件半路“丢包”的嫌疑。当时我们差点就断定这是机台驱动偷工减料,没有做过滤功能。
但为了把证据做实,我们换了一个带图形界面的测试软件去做交叉验证,结果却阴差阳错地打破了僵局。第一次用测试软件发包时,因为被界面排版误导,我们错把目标ID填到了“请求的属性名”这个格子里。机台收到后,非常严谨地回了一个空数据,意思是找不到叫这个名字的属性。这个细节突然让我们意识到,机台其实一点也不傻,它的解析能力是很完善的。紧接着,我们把ID填对位置,放进了“对象ID列表”里再次发送,这一次机台完美地只返回了那一个特定的对象。这个结果直接给机台洗清了冤屈,也让我们猛然醒悟:原来是我们自己写代码的时候,把ID塞错了位置。
回过头去查阅我们最初的组包代码和SEMI E39的官方规范,真相终于大白。写代码的时候,因为看到第一个字段叫“对象标识符”,按照普通程序员的直觉,肯定觉得这里就是放ID的正确位置。但这其实是官方规范抛出的一个语义陷阱。在真正的底层逻辑里,这第一个字段主要是用来指代复杂对象的目录层级路径的,对于控制任务这种全局顶层对象,正确的做法是直接留空。而真正用来做精准匹配查找的,是第三个字段,也就是“对象ID列表”。由于我们的代码把这第三个字段和第四个过滤条件字段都留空了,机台就老老实实地按照规范要求,执行了一次类似数据库里的“全表扫描”。明白了这一点,解决办法就变得特别简单优雅,我们完全不需要在业务代码里去写那些别扭的二次过滤逻辑,只需要在底层组包时,把第一个字段清空,然后把目标ID打包成一个单项列表塞进第三个字段里,整个问题就从根源上化解了。
跳出这个具体的Bug,其实这次排查也帮我们彻底理顺了SECS/GEM通讯里最核心的基础语法。在这个协议里,所有的指令都被装进了一个由“流(Stream)”和“功能(Function)”组成的二维坐标系里。你可以把“流”想象成半导体工厂里的各个业务大类或者部门,比如有专门管机台状态的部门,有管异常报警的部门,还有我们这次用到的、专门负责复杂对象管理的部门。而“功能”则是告诉这个部门你要办什么具体的业务。
这里面有一套绝对不可违背的底层交规:发送方主动发起的请求必定是奇数,而接收方给出回应的数字必定是这个奇数加一的偶数。这就好比一问一答,规规矩矩。如果对方给你回复了一个以0结尾的数字,那其实就是一种干脆的拒绝,意思是这个操作它处理不了或者根本不支持。最后,配合上每次发请求时带上的那个用来匹配问答的流水号(系统字节),以及决定“要不要死等机台回复”的等待标志位,这套看似古老的底层规则,就像精密咬合的齿轮一样,确保了每天在产线上飞驰的成千上万条指令绝对不会出现张冠李戴的错乱。