
Part Production Cell — MES & OEE
OpenPLC, Ignition Perspective and SQLite: order in events, KPIs and downtime causes.
This project started with a simple sorting line. A part reaches the infeed sensor, gets a type, then either moves to a side stream through a pusher or continues to the exit. The real task was not the conveyor animation but a sequence that stays understandable.
First I split ownership inside the PLC. Ladder owns mode, startup, permissives, final commands, and common fault state. Structured Text owns the part cycle: it latches the type, waits for feedback, handles timeouts, and updates counters.
Then I connected the loop: operator action → PLC logic → command → model feedback → HMI state. This is a desktop learning simulation, not a production or safety-certified system.

Ladder owns mode, Run latch, permissives, final commands, and common fault state. Structured Text owns part state, timeouts, and counters. That keeps a final command from receiving writes from two logic blocks.

At PE_Infeed, the PLC latches type A or B. It does not accept the next part until the active cycle is complete.
For type A, the cycle follows 10 → 20 → 30 → 0: travel to divert, extend feedback, return home, then increment Type A/Total.
No required PE or pusher feedback: the sequence moves to state 90 and removes automatic commands. Reset returns to idle; it does not start another cycle.
The ST block takes the latched part type through infeed, travel to divert, pusher extension, return home, and completion. Every wait has its own timeout and fault transition.

At PE_Infeed, the state machine stores type A or B. It keeps using that value, so a changing external signal cannot reroute a part halfway through its cycle.
Ladder creates commands and permissives. ST does not write equipment directly; it provides sequence state, fault, and counters. The final command has one writer.
If a sensor or Pusher_Extended does not arrive in time, the state machine enters state 90. Automatic commands are removed. A new cycle needs Reset and a new Start.
CASE Sequence_State OF
0:
IF Mode_Auto_Active AND Run_Active
AND Pusher_Home AND PE_Infeed THEN
Active_Part_Type_A := Part_Type_A;
IF Part_Type_A THEN
Sequence_State := 10;
ELSE
Sequence_State := 50;
END_IF;
END_IF;
END_CASE;
10:
IF Divert_Edge.Q THEN
Sequence_State := 20;
ELSIF T_To_Divert.Q THEN
Fault_Jam := TRUE;
Sequence_State := 90;
END_IF;
20:
IF Pusher_Extended THEN
Sequence_State := 30;
ELSIF T_Extend.Q THEN
Fault_Pusher := TRUE;
Sequence_State := 90;
END_IF;
90:
IF Reset_Pulse THEN
Fault_Jam := FALSE;
Fault_Pusher := FALSE;
Active_Part_Type_A := FALSE;
Sequence_State := 0;
END_IF;
Perspective keeps Auto, Start/Stop/Reset, sensor status, pusher feedback, fault, and three counters on one screen. The Gateway Timer models only inputs and feedback. Commands, faults, and counts stay in the PLC.

The screen holds commands, line status, conveyor, three PE sensors, pusher feedback, fault cause, and separate counters.
A 200 ms Gateway Timer alternates A/B. For A it produces PE_Infeed → PE_Divert → Extended → Home with margin inside the PLC's 2 s watchdog.
In the test cycle, type A changed Total/A/B from 10/4/6 to 11/5/6. Then type B changed 11/5/6 to 12/5/7. No active fault appeared.
Conclusions:

OpenPLC, Ignition Perspective and SQLite: order in events, KPIs and downtime causes.

OpenPLC, Modbus TCP, and Ignition: from ladder logic to a verifiable operator scenario.

Enterprise monitoring for a nuclear plant: system states, engineering constraints, and 3D storage logic.