PLC Communication Middleware
Modbus TCP PLC communication middleware with four separated layers and a dedicated Celery polling worker
Background
This is the layer of the sorting system that communicates with PLC equipment in real time, driving automatic sorting control and exchanging inbound and outbound data. The existing communication code mixed responsibilities into a single file, and magic numbers with shallow exception handling made on-site debugging hard. Constraints specific to industrial protocols also had to be solved: the 125-register read limit of Modbus TCP, IEEE 754 Float32 weight parsing, and Korean string encoding.
System architecture
- Four-layer structure: Presentation (Celery tasks) to Service (domain logic) to Infrastructure (Modbus manager/repository/service) to Repository (Django ORM).
- Queue and worker isolation: the Celery queue
plc_pollingis bound only to a dedicated PLC worker (concurrency:1), which removes any possibility of Modbus protocol collisions. - Working around Modbus limits: data from up to 64 automatic sorting outlets is read as a 2-chunk split to fit the 125-register limit, and weights are parsed as IEEE 754 Float32.
- Field diagnostic scripts: six separate PLC diagnostic scripts for installation sites cut inspection time.
How it was built
Separating responsibilities. PLC communication code that started as a single file was split inside the PLC communication app into four layers (Presentation / Service / Infrastructure / Repository). Infrastructure itself was further divided into config, exceptions, manager, models, repository, and service so that settings, exceptions, connection state, and the query layer stay apart.
Isolating polling on a dedicated Celery worker. Modbus connections opened from several threads at once tangle responses easily, so polling was moved to a dedicated PLC queue bound to a dedicated worker (concurrency:1). Beat triggers the inlet, outlet, and combined polling tasks every 10 seconds.
Getting past the Modbus 125-register limit. Because the number of registers readable in one call is capped, sorting outlet data is read in two chunks and joined in the application layer. Weight values are read as two 16-bit registers interpreted as an IEEE 754 Float32.
Custom exception hierarchy and automatic reconnection. Layering the exceptions separates network disconnection, slave errors, and timeouts. The manager watches connection state and retries the connection when the network is unstable.
Structure for three PLC vendors. Alongside PyModbus (Modbus TCP), the Pycomm3 (Allen-Bradley) and Pymelsec (Mitsubishi) dependencies are managed together at project level, so a new vendor's PLC only requires adding an adapter.
Results
- Decomposed a single file into a four-layer structure (Presentation / Service / Infrastructure / Repository).
- Worked around the Modbus TCP 125-register limit with a 2-chunk split read, collecting data from up to 64 sorting outlets every 10 seconds.
- Eliminated protocol collisions with a dedicated PLC queue and a dedicated worker (
concurrency:1). - Cut debugging time by adding six PLC diagnostic scripts for field use.
- Operating alongside multiple PLC units at more than 20 sites nationwide.