Summary
RegisterData in services/plc-modbus/backend/models/plc_models.py declares the wrong field names. It still uses the placeholder fields register_101–register_105 instead of the named VFD telemetry fields that plc_connection.py actually produces. Because Pydantic v2 silently drops unknown fields, the named values are stripped from the /api/plc/io response and replaced with the 0 defaults.
Where
- Bug site:
services/plc-modbus/backend/models/plc_models.py:65-73 (class RegisterData)
- Source of truth (correct):
services/plc-modbus/backend/services/plc_connection.py:44-51 (REGISTER_NAMES) and :187-191 (read_io() builds the dict from REGISTER_NAMES)
- Where it's wrapped:
services/plc-modbus/backend/routes/plc.py:51-57 (IOResponse(registers=io_data["registers"], ...))
Root cause
read_io() returns:
register_data = {ItemCount, ConveyorHz, MotorCurrentX10, MotorTempX10, VFDStatus, ErrorCode}
but the model only accepts:
class RegisterData(BaseModel):
ItemCount: int = 0
register_101: int = 0
register_102: int = 0
register_103: int = 0
register_104: int = 0
register_105: int = 0
Pydantic v2 ignores extra fields by default, so ConveyorHz/MotorCurrentX10/MotorTempX10/VFDStatus/ErrorCode are dropped and the response returns register_101..105 = 0.
Impact
GET /api/plc/io returns zeros for all VFD registers (101–105).
services/diagnosis/main.py format_plc_io_for_llm() reads the named fields (ConveyorHz, MotorCurrentX10, etc.) — so the AI diagnosis receives 0 for every VFD value and has no live machine data.
- Blocks V2 (Machine Awareness) end-to-end and any "current state — which assets are running right now" demo.
Confirmed correct elsewhere
plc_connection.py REGISTER_NAMES, mock_plc.py register constants, and diagnosis/main.py all use the named fields. Only RegisterData is stale. (Note: the services/plc-modbus/CLAUDE.md sidecar register table is also stale — still lists 101–105 as "unused" — and should be refreshed as part of the fix.)
Proposed fix
Rename the RegisterData fields to match REGISTER_NAMES:
class RegisterData(BaseModel):
"""Holding registers (100-105) — From A to B scene + VFD telemetry."""
ItemCount: int = 0 # 100: items that reached SensorEnd
ConveyorHz: int = 0 # 101: conveyor VFD frequency (Hz)
MotorCurrentX10: int = 0 # 102: motor current x10 (amps = value/10)
MotorTempX10: int = 0 # 103: motor temperature x10 (degC = value/10)
VFDStatus: int = 0 # 104: 0=idle, 1=running, 2=fault
ErrorCode: int = 0 # 105: 0=none,1=overload,2=overheat,3=sensor-fail,4=jam,7=e-stop
Acceptance criteria
GET /api/plc/io (mock mode) returns non-zero named VFD fields when the mock has VFD activity.
diagnosis receives live VFD values (no longer all 0).
- Existing
plc-modbus test suite (162 tests) stays green; add a regression test asserting named fields survive serialization.
Identified during the FactoryLM ↔ MIRA ↔ Walker DT-2026 alignment analysis (docs/dt-factorylm-alignment.md).
Summary
RegisterDatainservices/plc-modbus/backend/models/plc_models.pydeclares the wrong field names. It still uses the placeholder fieldsregister_101–register_105instead of the named VFD telemetry fields thatplc_connection.pyactually produces. Because Pydantic v2 silently drops unknown fields, the named values are stripped from the/api/plc/ioresponse and replaced with the0defaults.Where
services/plc-modbus/backend/models/plc_models.py:65-73(class RegisterData)services/plc-modbus/backend/services/plc_connection.py:44-51(REGISTER_NAMES) and:187-191(read_io()builds the dict fromREGISTER_NAMES)services/plc-modbus/backend/routes/plc.py:51-57(IOResponse(registers=io_data["registers"], ...))Root cause
read_io()returns:but the model only accepts:
Pydantic v2 ignores extra fields by default, so
ConveyorHz/MotorCurrentX10/MotorTempX10/VFDStatus/ErrorCodeare dropped and the response returnsregister_101..105 = 0.Impact
GET /api/plc/ioreturns zeros for all VFD registers (101–105).services/diagnosis/main.pyformat_plc_io_for_llm()reads the named fields (ConveyorHz,MotorCurrentX10, etc.) — so the AI diagnosis receives 0 for every VFD value and has no live machine data.Confirmed correct elsewhere
plc_connection.pyREGISTER_NAMES,mock_plc.pyregister constants, anddiagnosis/main.pyall use the named fields. OnlyRegisterDatais stale. (Note: theservices/plc-modbus/CLAUDE.mdsidecar register table is also stale — still lists 101–105 as "unused" — and should be refreshed as part of the fix.)Proposed fix
Rename the
RegisterDatafields to matchREGISTER_NAMES:Acceptance criteria
GET /api/plc/io(mock mode) returns non-zero named VFD fields when the mock has VFD activity.diagnosisreceives live VFD values (no longer all 0).plc-modbustest suite (162 tests) stays green; add a regression test asserting named fields survive serialization.Identified during the FactoryLM ↔ MIRA ↔ Walker DT-2026 alignment analysis (
docs/dt-factorylm-alignment.md).