#!/usr/bin/env python3 """Exercise production worker with real sockets/PTYS and deliberately sparse PLC calls.""" import ctypes as C import os,pty,select,socket,struct,subprocess,tempfile,time,threading from pathlib import Path root=Path(__file__).resolve().parents[1] with tempfile.TemporaryDirectory() as td: so=Path(td)/'worker.so' subprocess.run(['cc','-shared','-fPIC','-std=c11','-Wall','-Wextra','-Werror','-O2','-pthread', *[str(root/'src'/f) for f in ['atom_mb.c','atom_rtu.c','atom_server.c','atom_bridge.c','atom_worker.c']],'-o',str(so)],check=True) lib=C.CDLL(str(so));U=C.c_uint32;W=C.c_uint16;B=C.c_uint8 lib.ab_call.argtypes=[U,U,U,W,B,B,W,W,C.POINTER(W),U,C.POINTER(U),U,C.c_uint64,U,U,U];lib.ab_call.restype=U def call(cmd,h=0,ip=0,port=0,f=3,addr=0,q=1,values=(),ct=100,rt=100,retry=10,unit=1): data=(W*2000)(*values);status=(U*24)() result=lib.ab_call(cmd,h,ip,port,unit,f,addr,q,data,2000,status,24,0,ct,rt,retry) return result,list(status),list(data) def until(fn,seconds=1): end=time.monotonic()+seconds while time.monotonic()=100,m assert call(10,ch)[0]==0 print('PASS worker completes TCP before timeout with PLC absent for 250 ms; result retained and timed') # Remote write must survive a stale PLC push before the PLC pulls its result. peer=socket.create_connection(('127.0.0.1',port));peer.settimeout(1) pdu=struct.pack('>BHH',6,0,4321);peer.sendall(struct.pack('>HHHB',1,0,len(pdu)+1,1)+pdu) assert recv_exact(peer,12)[7:]==pdu assert call(35,sh,f=2,q=8,values=[0]*8)[0]==0 assert call(36,sh,f=2,q=8)[2][0]==4321 # After acknowledgment a deliberate application override is permitted. call(35,sh,f=2,q=8,values=[42]*8);assert call(36,sh,f=2,q=8)[2][0]==42 assert call(35,sh,f=2,addr=1,q=8,values=[0]*8)[0]==1 # explicit recreate for remap peer.close();call(2,ch);call(31,sh) print('PASS autonomous TCP slave and lossless remote-write handoff') # Real serial PTY, again no PLC ticks after submission. master,slave=pty.openpty();path=os.ttyname(slave);os.close(slave) r,s,_=call(20,ip=9600,unit=0,f=8,addr=1,q=len(path),ct=0,values=list(path.encode()));assert not r;h=s[0] call(9,h,rt=100);call(3,h);until(lambda:call(5,h)[1][1]==3) call(6,h,unit=1,f=3,q=1) assert select.select([master],[],[],1)[0];request=os.read(master,256);assert len(request)==8 def crc(p): c=65535 for b in p: c^=b for _ in range(8):c=(c>>1)^(0xa001 if c&1 else 0) return c p=bytes([1,3,2,0,77]);os.write(master,p+struct.pack('