# AB CIP integration-test fixture — ab_server (libplctag). # # One service per family. All bind :44818 on the host; only one runs at a # time. Commands mirror the CLI args AbServerProfile.cs constructs for the # native-binary path. # # Usage: # docker compose --profile controllogix up # docker compose --profile compactlogix up # docker compose --profile micro800 up # docker compose --profile guardlogix up services: controllogix: profiles: ["controllogix"] build: context: . dockerfile: Dockerfile image: otopcua-ab-server:libplctag-release container_name: otopcua-ab-server-controllogix restart: "no" ports: - "44818:44818" command: [ "ab_server", "--plc=ControlLogix", "--path=1,0", "--port=44818", "--tag=TestDINT:DINT[1]", "--tag=TestREAL:REAL[1]", "--tag=TestBOOL:BOOL[1]", "--tag=TestSINT:SINT[1]", "--tag=TestString:STRING[1]", "--tag=TestArray:DINT[16]" ] compactlogix: profiles: ["compactlogix"] image: otopcua-ab-server:libplctag-release build: context: . dockerfile: Dockerfile container_name: otopcua-ab-server-compactlogix restart: "no" ports: - "44818:44818" # ab_server doesn't distinguish CompactLogix from ControlLogix — no # dedicated --plc mode. Driver-side ConnectionSize cap is enforced # separately (see AbServerProfile.CompactLogix Notes). command: [ "ab_server", "--plc=ControlLogix", "--path=1,0", "--port=44818", "--tag=TestDINT:DINT[1]", "--tag=TestREAL:REAL[1]", "--tag=TestBOOL:BOOL[1]" ] micro800: profiles: ["micro800"] image: otopcua-ab-server:libplctag-release build: context: . dockerfile: Dockerfile container_name: otopcua-ab-server-micro800 restart: "no" ports: - "44818:44818" # ab_server does have a Micro800 plc mode (unconnected-only, empty path). command: [ "ab_server", "--plc=Micro800", "--port=44818", "--tag=TestDINT:DINT[1]", "--tag=TestREAL:REAL[1]" ] guardlogix: profiles: ["guardlogix"] image: otopcua-ab-server:libplctag-release build: context: . dockerfile: Dockerfile container_name: otopcua-ab-server-guardlogix restart: "no" ports: - "44818:44818" # ab_server has no safety subsystem — _S suffix triggers driver-side # classification only. command: [ "ab_server", "--plc=ControlLogix", "--path=1,0", "--port=44818", "--tag=TestDINT:DINT[1]", "--tag=SafetyDINT_S:DINT[1]" ] # ---- PR abcip-5.1 — paired-fixture for HSBY role probing ------------------ # The "paired" profile spins up two ab_server instances (controllogix-primary # on :44818, controllogix-secondary on :44819) plus a stub hsby-mux sidecar # that flips a role bit on demand. The mux is a placeholder — it does NOT # currently inject role bits because ab_server has no WallClockTime.SyncStatus # tag concept. PR abcip-5.2 follow-up will land: # 1. A patched ab_server image (or a separate Python TCP shim) that exposes # a writable WallClockTime.SyncStatus DINT per chassis. # 2. A real hsby-mux REST endpoint (POST /flip {"active": "primary"}) that # writes 1 to the chosen chassis + 0 to the other. # For now the services exist so the compose file documents the topology + the # AbCipHsbyRoleProberTests integration test has a place to land its # [AbServerFact] without breaking the pre-5.1 ab_server profiles. controllogix-primary: profiles: ["paired"] image: otopcua-ab-server:libplctag-release build: context: . dockerfile: Dockerfile container_name: otopcua-ab-server-controllogix-primary restart: "no" ports: - "44818:44818" command: [ "ab_server", "--plc=ControlLogix", "--path=1,0", "--port=44818", "--tag=TestDINT:DINT[1]", # Stand-in for WallClockTime.SyncStatus until the patched image lands. "--tag=SyncStatus:DINT[1]" ] controllogix-secondary: profiles: ["paired"] image: otopcua-ab-server:libplctag-release build: context: . dockerfile: Dockerfile container_name: otopcua-ab-server-controllogix-secondary restart: "no" ports: - "44819:44818" command: [ "ab_server", "--plc=ControlLogix", "--path=1,0", "--port=44818", "--tag=TestDINT:DINT[1]", "--tag=SyncStatus:DINT[1]" ] # Stub hsby-mux — placeholder. Today's image is a tiny Python script that # exposes a /health endpoint + nothing else. PR abcip-5.2 will replace this # with a real role-flip endpoint that writes SyncStatus on either chassis. hsby-mux: profiles: ["paired"] image: python:3.12-alpine container_name: otopcua-ab-hsby-mux restart: "no" ports: - "8080:8080" command: - sh - -c - | python -c " import http.server, socketserver class H(http.server.BaseHTTPRequestHandler): def do_GET(s): s.send_response(200); s.send_header('Content-Type','text/plain'); s.end_headers() s.wfile.write(b'hsby-mux stub - PR abcip-5.2 follow-up will wire role flips') socketserver.TCPServer(('', 8080), H).serve_forever() " depends_on: - controllogix-primary - controllogix-secondary