diff --git a/example/Templates.xml b/example/Templates.xml
new file mode 100644
index 0000000..47e00f2
--- /dev/null
+++ b/example/Templates.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/exampleModel.xml b/example/exampleModel.xml
index a106838..2b366ed 100644
--- a/example/exampleModel.xml
+++ b/example/exampleModel.xml
@@ -2,145 +2,28 @@
-
-
-
-
-
-
-
-
+
+
+
+
-
+
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -150,20 +33,19 @@
Value="telPlcStarted, telPlcStopped"/>
-
+
-
+
-
-
+
@@ -173,7 +55,7 @@
-
+
@@ -182,7 +64,7 @@
-
+
@@ -190,253 +72,4 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/example/strolch-plc-example-connections.xml b/example/strolch-plc-example-connections.xml
new file mode 100644
index 0000000..9dd3b45
--- /dev/null
+++ b/example/strolch-plc-example-connections.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/strolch-plc-example.csv b/example/strolch-plc-example.csv
new file mode 100644
index 0000000..2d874ca
--- /dev/null
+++ b/example/strolch-plc-example.csv
@@ -0,0 +1,11 @@
+Description,Type,Device,Pin,Resource,Action1,Action2,Connection,DeviceId
+Material Flow,Group,,,,,,,MaterialFlow
+Conveyor 1,Input,1,1,Conveyor01,Occupied,,i2cInput.dev01,
+Conveyor 2,Input,1,2,Conveyor02,Occupied,,i2cInput.dev01,
+Conveyor 3,Input,1,3,Conveyor03,Occupied,,i2cInput.dev01,
+Conveyor 4,Input,1,4,Conveyor04,Occupied,,i2cInput.dev01,
+Conveyor 1,Output,1,1,Conveyor01,MotorOn,MotorOff,i2cOutput.dev01,
+Conveyor 2,Output,1,2,Conveyor02,MotorOn,MotorOff,i2cOutput.dev01,
+Conveyor 3,Output,1,3,Conveyor03,MotorOn,MotorOff,i2cOutput.dev01,
+Conveyor 4,Output,1,4,Conveyor04,MotorOn,MotorOff,i2cOutput.dev01,
+BarcodeReader,BarcodeReader,,,Conveyor03,Barcode,ReadBarcode,barcodeReader01,
\ No newline at end of file
diff --git a/example/strolch-plc-example.xml b/example/strolch-plc-example.xml
new file mode 100644
index 0000000..4a72f46
--- /dev/null
+++ b/example/strolch-plc-example.xml
@@ -0,0 +1,194 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/strolch-plc-core/src/main/java/li/strolch/plc/core/service/plc/ExamplePlcConveyorPlcService.java b/strolch-plc-core/src/main/java/li/strolch/plc/core/service/plc/ExamplePlcConveyorPlcService.java
new file mode 100644
index 0000000..1ff9a1e
--- /dev/null
+++ b/strolch-plc-core/src/main/java/li/strolch/plc/core/service/plc/ExamplePlcConveyorPlcService.java
@@ -0,0 +1,259 @@
+package li.strolch.plc.core.service.plc;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import li.strolch.agent.api.ComponentContainer;
+import li.strolch.persistence.api.StrolchTransaction;
+import li.strolch.plc.core.PlcHandler;
+import li.strolch.plc.core.PlcService;
+import li.strolch.plc.model.PlcAddress;
+
+public class ExamplePlcConveyorPlcService extends PlcService {
+
+ public static final String R_CONVEYOR_01 = "Conveyor01";
+ public static final String R_CONVEYOR_02 = "Conveyor02";
+ public static final String R_CONVEYOR_03 = "Conveyor03";
+ public static final String R_CONVEYOR_04 = "Conveyor04";
+
+ public static final String A_OCCUPIED = "Occupied";
+ public static final String A_MOTOR_ON = "MotorOn";
+ public static final String A_MOTOR_OFF = "MotorOff";
+ public static final String A_BARCODE = "Barcode";
+ public static final String A_READ_BARCODE = "ReadBarcode";
+
+ private AtomicBoolean conveyor1Occupied;
+ private AtomicBoolean conveyor2Occupied;
+ private AtomicBoolean conveyor3Occupied;
+ private AtomicBoolean conveyor4Occupied;
+
+ private AtomicBoolean conveyor1On;
+ private AtomicBoolean conveyor2On;
+ private AtomicBoolean conveyor3On;
+ private AtomicBoolean conveyor4On;
+
+ private AtomicBoolean conveyor1WaitingForTransfer;
+ private AtomicBoolean conveyor2WaitingForTransfer;
+ private AtomicBoolean conveyor3WaitingForTransfer;
+
+ public ExamplePlcConveyorPlcService(ComponentContainer container, PlcHandler plcHandler) {
+ super(container, plcHandler);
+
+ this.conveyor1WaitingForTransfer = new AtomicBoolean(false);
+ this.conveyor2WaitingForTransfer = new AtomicBoolean(false);
+ this.conveyor3WaitingForTransfer = new AtomicBoolean(false);
+ }
+
+ @Override
+ public void handleNotification(PlcAddress address, Object value) {
+
+ String resource = address.resource;
+ String action = address.action;
+ boolean state = (boolean) value;
+
+ switch (resource) {
+
+ case R_CONVEYOR_01:
+
+ if (action.equals(A_OCCUPIED)) {
+ conveyor1Occupied.set(state);
+ handleTransfer(null, R_CONVEYOR_01, R_CONVEYOR_02, //
+ null, conveyor1Occupied, conveyor2Occupied, //
+ null, conveyor1On, conveyor2On, //
+ null, conveyor1WaitingForTransfer);
+ } else {
+ logger.error("Unhandled action " + resource + "-" + action);
+ }
+
+ break;
+
+ case R_CONVEYOR_02:
+
+ if (action.equals(A_OCCUPIED)) {
+ conveyor2Occupied.set(state);
+ handleTransfer(R_CONVEYOR_01, R_CONVEYOR_02, R_CONVEYOR_03, //
+ conveyor1Occupied, conveyor2Occupied, conveyor3Occupied, //
+ conveyor1On, conveyor2On, conveyor3On, //
+ conveyor1WaitingForTransfer, conveyor2WaitingForTransfer);
+ } else {
+ logger.error("Unhandled action " + resource + "-" + action);
+ }
+
+ break;
+
+ case R_CONVEYOR_03:
+
+ if (action.equals(A_OCCUPIED)) {
+ conveyor3Occupied.set(state);
+ handleTransfer(R_CONVEYOR_02, R_CONVEYOR_03, R_CONVEYOR_04, //
+ conveyor2Occupied, conveyor3Occupied, conveyor4Occupied, //
+ conveyor2On, conveyor3On, conveyor4On, //
+ conveyor2WaitingForTransfer, conveyor3WaitingForTransfer);
+ } else {
+ logger.error("Unhandled action " + resource + "-" + action);
+ }
+
+ break;
+
+ case R_CONVEYOR_04:
+
+ if (action.equals(A_OCCUPIED)) {
+ conveyor4Occupied.set(state);
+ handleTransfer(R_CONVEYOR_03, R_CONVEYOR_04, null, //
+ conveyor3Occupied, conveyor4Occupied, null, //
+ conveyor3On, conveyor4On, null, //
+ conveyor3WaitingForTransfer, null);
+ } else {
+ logger.error("Unhandled action " + resource + "-" + action);
+ }
+
+ break;
+ }
+ }
+
+ private void handleTransfer(String previous, String current, String next, //
+ AtomicBoolean previousOccupied, AtomicBoolean currentOccupied, AtomicBoolean nextOccupied, //
+ AtomicBoolean previousOn, AtomicBoolean currentOn, AtomicBoolean nextOn, //
+ AtomicBoolean previousWaitingForTransfer, AtomicBoolean currentWaitingForTransfer) {
+
+ if (currentOccupied.get()) {
+
+ // handle current conveyor is now occupied
+ if (next == null) {
+ if (currentOn.get()) {
+ logger.info(current + " is now occupied without a next conveyor, stopping conveyor");
+ send(current, A_MOTOR_OFF);
+ currentOn.set(false);
+ } else {
+ logger.info(current + " is now occupied, conveyor is off and no next conveyor: transfer complete.");
+ }
+
+ return;
+ }
+
+ if (nextOccupied.get()) {
+ logger.info(current + " is now occupied, next conveyor " + next + " is still occupied, so waiting...");
+ if (currentWaitingForTransfer.get())
+ logger.error("What the hell, current " + current + " is already waiting for a transfer!");
+ currentWaitingForTransfer.set(true);
+ } else {
+ logger.info(
+ current + " is now occupied, next conveyor " + next + " is not occupied, so transferring...");
+
+ if (nextOn.get()) {
+ logger.info("Next conveyor " + next + " is already running, waiting for transfer to complete...");
+ } else {
+ logger.info("Starting " + next + " and waiting for transfer to complete...");
+ send(next, A_MOTOR_ON);
+ nextOn.set(true);
+ }
+
+ if (currentOn.get()) {
+ logger.info(current + " is already running, waiting for transfer to complete...");
+ } else {
+ logger.info("Starting " + current + " and waiting for transfer to complete...");
+ send(current, A_MOTOR_ON);
+ currentOn.set(true);
+ }
+ }
+
+ return;
+ }
+
+ // current conveyor is not occupied anymore
+
+ if (previous == null) {
+
+ // no previous conveyor, so just stop current, if still on
+
+ if (currentOn.get()) {
+ logger.info(current + " is now unoccupied, stopping conveyor");
+ send(current, A_MOTOR_OFF);
+ currentOn.set(false);
+ } else {
+ logger.info(current + " is now unoccupied, conveyor is already off");
+ }
+
+ return;
+ }
+
+ // handle transfer of previous to current
+
+ if (!previousOccupied.get()) {
+ logger.info(previous + " is not occupied, so no transfer required.");
+
+ if (currentOn.get()) {
+ logger.info(current + " is now unoccupied and previous " + previous
+ + " is not occupied, so no transfer required: Stopping conveyor");
+ send(current, A_MOTOR_OFF);
+ currentOn.set(false);
+ } else {
+ logger.info(current + " is now unoccupied and previous " + previous
+ + " is not occupied, and conveyor not running. Nothing else to do");
+ }
+
+ return;
+ }
+
+ // previous is occupied, so transfer to current, but only if previous was waiting
+ if (!previousWaitingForTransfer.get()) {
+ logger.info(previous + " conveyor is not waiting for a transfer. Nothing else to do.");
+ } else {
+
+ logger.info(previous + " conveyor is waiting for a transfer, so starting transfer");
+
+ if (currentOn.get()) {
+ logger.info(current + " is already on, waiting for transfer...");
+ } else {
+ logger.info("Turning " + current + " on for transfer");
+ send(current, A_MOTOR_ON);
+ currentOn.set(true);
+ }
+
+ if (previousOn.get()) {
+ logger.info(previous + " is already on, waiting for transfer...");
+ } else {
+ logger.info("Turning " + previous + " on for transfer");
+ send(previous, A_MOTOR_ON);
+ previousOn.set(true);
+ }
+ }
+ }
+
+ @Override
+ public void register() {
+ this.plcHandler.registerListener(R_CONVEYOR_01, A_OCCUPIED, this);
+ this.plcHandler.registerListener(R_CONVEYOR_02, A_OCCUPIED, this);
+ this.plcHandler.registerListener(R_CONVEYOR_03, A_OCCUPIED, this);
+ this.plcHandler.registerListener(R_CONVEYOR_04, A_OCCUPIED, this);
+
+ this.plcHandler.registerListener(R_CONVEYOR_03, A_BARCODE, this);
+ super.register();
+ }
+
+ @Override
+ public void unregister() {
+ this.plcHandler.unregisterListener(R_CONVEYOR_01, A_OCCUPIED, this);
+ this.plcHandler.unregisterListener(R_CONVEYOR_02, A_OCCUPIED, this);
+ this.plcHandler.unregisterListener(R_CONVEYOR_03, A_OCCUPIED, this);
+ this.plcHandler.unregisterListener(R_CONVEYOR_04, A_OCCUPIED, this);
+
+ this.plcHandler.unregisterListener(R_CONVEYOR_03, A_BARCODE, this);
+ super.unregister();
+ }
+
+ @Override
+ public void start(StrolchTransaction tx) {
+
+ this.conveyor1Occupied = new AtomicBoolean(getAddressState(tx, R_CONVEYOR_01, A_OCCUPIED));
+ this.conveyor2Occupied = new AtomicBoolean(getAddressState(tx, R_CONVEYOR_02, A_OCCUPIED));
+ this.conveyor3Occupied = new AtomicBoolean(getAddressState(tx, R_CONVEYOR_03, A_OCCUPIED));
+ this.conveyor4Occupied = new AtomicBoolean(getAddressState(tx, R_CONVEYOR_04, A_OCCUPIED));
+
+ this.conveyor1On = new AtomicBoolean(getAddressState(tx, R_CONVEYOR_01, A_MOTOR_ON));
+ this.conveyor2On = new AtomicBoolean(getAddressState(tx, R_CONVEYOR_02, A_MOTOR_ON));
+ this.conveyor3On = new AtomicBoolean(getAddressState(tx, R_CONVEYOR_03, A_MOTOR_ON));
+ this.conveyor4On = new AtomicBoolean(getAddressState(tx, R_CONVEYOR_04, A_MOTOR_ON));
+
+ super.start(tx);
+ }
+}