@Override
    public void loop() {
        // TODO: Do something sensible ;-)
        // Just a POC for now ... to be replaced by a "real" simulation ...
        try {
            // Get the current value for the water tank level.
            Long value = (Long) context.getMemory().get(PROP_WATER_LEVEL);

            // If the input valve is open, add 10.
            if (context.getDigitalInputs().get(NUM_INPUT_VALVE_INPUT)) {
                value += 10;
                value = Math.min(MAX_WATER_LEVEL, value);
            }

            // If the output valve is open, subtract 8 (It's slightly less throughput than the input)
            if (context.getDigitalInputs().get(NUM_OUTPUT_VALVE_INPUT)) {
                value -= 8;
                value = Math.max(0, value);
            }

            // Calculate if the emergency valve should be open
            boolean emergencyValveOpen = value > EMERGENCY_VALVE_WATER_LEVEL;

            // Update the memory.
            context.getMemory().put(PROP_WATER_LEVEL, value);
            // Update the state of the emergency valve.
            context.getDigitalOutputs().set(EMERGENCY_VALVE_OUTPUT, emergencyValveOpen);

            // Should probably be handled by the simulator
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
This is a little simulation that simulates a Water tank.
This tank has a capacity who's "waterLevel" is represented as a Long value.
Water can flow into the tank if the input valve is opened, and it can flow
out of the tank if the output valve is open.
<p>
The capacity of the output is slightly smaller than that of the input, so
opening both valves will result in the tank filling.
<p>
To prevent the tank from bursting, there's an emergency valve which is opened
as soon as the water-level reaches a critical maximum.
plc4j/utils/plc-simulator/src/main/java/org/apache/plc4x/simulator/simulation/watertank/WaterTankSimulationModule.java