diff --git a/content/development/maven-archetypes.md b/content/development/maven-archetypes.md
index 930f0ef..140a582 100644
--- a/content/development/maven-archetypes.md
+++ b/content/development/maven-archetypes.md
@@ -6,8 +6,11 @@ weight: 40
## Maven Archetypes
Maven offers archetypes to generate new projects. Strolch offers the following archetypes, to create new projects:
+
* [li.strolch.mvn.archetype.main](/development/main-class-app) for Java main class applications
-* [li.strolch.mvn.archetype.webapp](/development/web-app) for Java Web based applications using REST and Polymer 1.x as the frontend.
+* [li.strolch.mvn.archetype.webapp](/development/web-app) for Java Web based applications using REST and Polymer 1.x as
+ the frontend.
+* [li.strolch.mvn.archetype.plc](/plc/example-set-up) for Strolch PLC projects.
To use the archetypes, clone the archetypes repository and install it locally:
@@ -17,4 +20,4 @@ cd strolch-maven-archetypes
mvn clean install
```
-Then follow one of the next steps to create the type of application you want.
\ No newline at end of file
+Then follow one of the next steps to create the type of application you want.
diff --git a/content/plc/_index.md b/content/plc/_index.md
index b4a3dc1..580c381 100644
--- a/content/plc/_index.md
+++ b/content/plc/_index.md
@@ -19,7 +19,7 @@ description in how to set up your own Strolch based PLC.
Checkout the code at [GitHub](https://github.com/strolch-li/strolch-plc)
-Strolch PLC is also deployed to Maven Central. Current version is 1.2.1.
+Strolch PLC is also deployed to Maven Central. Current version is 1.2.2 and is using Strolch version 1.8.5.
Currently, we have the following topics of discussion:
diff --git a/content/plc/example-set-up.md b/content/plc/example-set-up.md
index ec155eb..dd17c61 100644
--- a/content/plc/example-set-up.md
+++ b/content/plc/example-set-up.md
@@ -20,362 +20,60 @@ business logic and the PLC controls the I/Os.
![Strolch PLC Example](/assets/images/Strolch-Plc-Example.png)
## New Project
-1. First create a new Strolch Web project using the Strolch Maven archetype
-2. Now add the following Maven dependencies:
-```xml
-
-
- 1.8.4
- 1.2.2
-
-
-
-
- li.strolch
- li.strolch.bom
- pom
- ${strolch.version}
- import
-
-
- li.strolch
- strolch-plc-bom
- pom
- ${strolch.plc.version}
- import
-
-
-
-
-
-
- li.strolch
- strolch-plc-core
-
-
- li.strolch
- strolch-plc-rest
-
-
- li.strolch
- strolch-plc-gw-client
-
-
-
+Create a new project using the PLC Strolch Maven Archetype:
+
+```shell
+mvn archetype:generate \
+ -DarchetypeGroupId=li.strolch \
+ -DarchetypeArtifactId=li.strolch.mvn.archetype.plc \
+ -DarchetypeVersion=0.1.0-SNAPSHOT \
+ -DgroupId= \
+ -DartifactId= \
+ -Dversion= \
+ -Dpackage= \
+ -DappName=""
+
```
-3. Add a bower dependency: `"strolch-wc-plc": "strolch-li/strolch-wc-plc#^0.3.20"`
- to `src/main/webapp/bower.json`
+This will create a multi-module project:
- After adding the dependency, run `gulp` in the webapp directory. Gulp should
- have been installed through the instructions from
- the [development page](/development).
+- The `-web` module contains the server code, which handles notifications from the PLC, and can send
+ telegrams to the PLC.
+- The `-plc-web` module contains the PLC code, which connects to the server and communicates with the
+ low-level hardware.
+- The `shared` modules contains classes shared by both projects (e.g. constants, etc.).
-4. Now we need to add the PLC web views to our new project. This is added in
- the `src/main/webapp/app/src/c-app.html` file. Add the following:
+This project already contains a default PLC model in `-plc-web/runtime/data/`.
-```html
-
-
-
+The following sections explains these files:
-
-
+### strolch-plc-example-connections.xml
-
-
-
-
-
-
-
+This file defines the hardware connections. The following connections are implemented:
-// add a new property to the WebSocket path for observing changes on the PLC
-wsObserverPath: {
- type: String,
- value: function () {
- return CustomWeb.baseWsPath + "/plc/observer";
- }
-}
-```
+* li.strolch.plc.core.hw.i2c.RSL366OverHorterI2c
+* li.strolch.plc.core.hw.i2c.PCF8574InputConnection
+* li.strolch.plc.core.hw.gpio.RaspiBcmGpioInputConnection
+* li.strolch.plc.core.hw.gpio.RaspiBcmGpioOutputConnection
+* li.strolch.plc.core.hw.i2c.Multi8BitI2cOutputConnection
+* li.strolch.plc.core.hw.connections.DataLogicScannerConnection
+* li.strolch.plc.core.hw.connections.LoggerOutConnection
+* li.strolch.plc.core.hw.connections.RandomStringConnection
-5. Don't forget to add the PLC Rest classes to your `ResourceConfig`
+See their respective classes for details.
-```java
-@ApplicationPath("rest")
-public class RestfulApplication extends ResourceConfig {
+### strolch-plc-example.csv
- public RestfulApplication() {
+This file maps I/Os to Resources and Actions and is converted into Strolch `Resource` objects using
+the `PlcAddressGenerator` class.
- ...
+In this example we will use simple Raspberry Pi GPIOs. For convenience, and also when sharing I/O definitions with
+external partners, it is easier to use a CSV file to define the I/Os and then use the `PlcAddressGenerator` to generate
+and validate the model.
- // strolch plc services
- packages(PlcConnectionsResource.class.getPackage().getName());
-
- ...
- }
-}
-```
-
-6. Now we need to configure the PLC's runtime by
- modifying `runtime/StrolchConfiguration.xml` and adding the following:
-
-```xml
-
-
-
-
-
- PlcHandler
- li.strolch.plc.core.PlcHandler
- li.strolch.plc.core.DefaultPlcHandler
- RealmHandler
-
-
- li.strolch.plc.core.hw.DefaultPlc
-
-
-
-
-
- PlcServiceInitializer
- li.strolch.plc.core.PlcServiceInitializer
- li.strolch.plc.example.CustomPlcServiceInitializer
- PlcHandler
-
-
-
-
-
-
- PlcGwClientHandler
- li.strolch.plc.gw.client.PlcGwClientHandler
- li.strolch.plc.gw.client.PlcGwClientHandler
- PlcHandler
- PlcServiceInitializer
-
- plc-01
- plc-01
- plc-01
- ws://localhost:8080/agent/websocket/strolch/plc
-
-
-
-
-
-```
-
-7. Now we add the custom classes we just declared.
-
-**PlcServiceInitializer**
-
-```java
-import java.util.ArrayList;
-import java.util.List;
-
-import li.strolch.plc.example.services.*;
-import li.strolch.agent.api.ComponentContainer;
-import li.strolch.plc.core.PlcHandler;
-import li.strolch.plc.core.PlcService;
-import li.strolch.plc.core.PlcServiceInitializer;
-
-public class CustomPlcServiceInitializer extends PlcServiceInitializer {
-
- public CustomPlcServiceInitializer(ComponentContainer container, String componentName) {
- super(container, componentName);
- }
-
- @Override
- protected List getPlcServices(PlcHandler plcHandler) {
- ArrayList plcServices = new ArrayList<>();
-
- StartupPlcService startupPlcService = new StartupPlcService(plcHandler);
- ConveyorPlcService conveyorPlcService = new ConveyorPlcService(plcHandler);
-
- plcServices.add(conveyorPlcService);
- plcServices.add(startupPlcService);
-
- return plcServices;
- }
-}
-```
-
-**PlcPostInitializer**
-```java
-import li.strolch.agent.api.ComponentContainer;
-import li.strolch.plc.core.PlcPostInitializer;
-
-public class CustomPostInitializer extends PlcPostInitializer {
- public CustomPostInitializer(ComponentContainer container, String componentName) {
- super(container, componentName);
- }
-
- // override the initialize(), start(), stop() and destroy() methods as needed
-}
-```
-
-8. In the `CustomPlcServiceInitializer` we added two PlcServices, for which the
- code is missing. The following are simple examples:
-
-**StartupPlcService**
-
-```java
-import li.strolch.persistence.api.StrolchTransaction;
-import li.strolch.plc.core.PlcHandler;
-import li.strolch.plc.core.PlcService;
-
-public class StartupPlcService extends PlcService {
-
- public static final String PLC = "PLC";
- public static final String STARTED = "Started";
- public static final String STOPPED = "Stopped";
-
- public StartupPlcService(PlcHandler plcHandler) {
- super(plcHandler);
- }
-
- @Override
- public void start(StrolchTransaction tx) {
- send(PLC, STARTED);
- super.start(tx);
- }
-
- @Override
- public void stop() {
- send(PLC, STOPPED);
- super.stop();
- }
-}
-```
-
-**ConveyorPlcService**
-
-```java
-
-
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-
-import li.strolch.plc.core.PlcHandler;
-import li.strolch.plc.core.PlcService;
-import li.strolch.plc.model.PlcAddress;
-
-public class ConveyorPlcService extends PlcService {
-
- public static final int BOX_TRANSFER_DURATION = 30;
-
- private static final String R_CONVEYOR_01 = "Conveyor01";
- private static final String A_START_BUTTON = "StartButton";
- private static final String T_MOTOR_ON = "MotorOn";
- private static final String T_MOTOR_OFF = "MotorOff";
- private static final String A_BOX_DETECTED = "BoxDetected";
-
- private boolean motorOn;
- private ScheduledFuture> motorStopTask;
-
- public ConveyorPlcService(PlcHandler plcHandler) {
- super(plcHandler);
- }
-
- @Override
- public void handleNotification(PlcAddress address, Object value) {
- String resource = address.resource;
- String action = address.action;
-
- if (!resource.equals("Conveyor01"))
- throw new IllegalStateException("Unexpected resource " + resource);
-
- boolean active = (boolean) value;
-
- if (action.equals(A_START_BUTTON)) {
-
- if (active) {
- logger.info("Start button pressed. Starting motors...");
- send(R_CONVEYOR_01, T_MOTOR_ON);
- this.motorOn = true;
- scheduleStopTask();
- }
-
- } else if (action.equals(A_BOX_DETECTED)) {
-
- if (active && this.motorOn) {
- logger.info("Container detected, refreshing stop task...");
- scheduleStopTask();
- }
-
- } else {
- logger.info("Unhandled notification " + address.toKeyAddress());
- }
- }
-
- private void scheduleStopTask() {
- if (this.motorStopTask != null)
- this.motorStopTask.cancel(false);
- this.motorStopTask = schedule(this::stopMotor, BOX_TRANSFER_DURATION, TimeUnit.SECONDS);
- }
-
- private void stopMotor() {
- send(R_CONVEYOR_01, T_MOTOR_OFF);
- }
-
- @Override
- public void register() {
- this.plcHandler.register(R_CONVEYOR_01, A_START_BUTTON, this);
- this.plcHandler.register(R_CONVEYOR_01, A_BOX_DETECTED, this);
- super.register();
- }
-
- @Override
- public void unregister() {
- this.plcHandler.unregister(R_CONVEYOR_01, A_START_BUTTON, this);
- this.plcHandler.unregister(R_CONVEYOR_01, A_BOX_DETECTED, this);
- super.unregister();
- }
-}
-```
-
-9. Now the last part is to add the model, i.e. PlcConnections, PlcAddresses and
- PlcTelegrams. To have less configuration files and make it easier to
- reconfigure at runtime, this data is stored in normal Strolch `Resources`.
-
- In this example we will use simple Raspberry Pi GPIOs. For convenience, and
- also when sharing I/O definitions with external partners, it is easier to use
- a CSV file to define the I/Os and then use the `PlcAddressGenerator` to
- generate and validate the model.
-
- For this purpose in this example, we will use one conveyor with 2 inputs and
- 1 output. The CSV file should have the following content:
-
-```csv
-Description,Type,SubType,Device,Pin,Resource,Action1,Action2,Connection,DeviceId
-Material Flow,Group,,,,,,,,MaterialFlow
-Conveyor 1,Input,Pin,,4,Conveyor,Occupied,,raspiBcmGpioInput
-Conveyor 1,Input,Pin,,17,Conveyor,BoxDetected,,raspiBcmGpioInput
-Conveyor 1,Output,Pin,,18,Conveyor,MotorOn,MotorOff,raspiBcmGpioOutput
-```
+For easier handling, use the following Google Sheet as a starting
+point: https://docs.google.com/spreadsheets/d/10fgTfR3FZCVbQ5bbh0xB1u8rLIaw2KEyO45VMv7y5ho/edit?usp=sharing
The CSV headers are as follows:
@@ -392,13 +90,14 @@ The CSV headers are as follows:
Scanner. The actions must be left empty as the keys Barcode (address), On
and Off (telegrams) will be generated.
* SubType →
-* For Input and Output types →
- * DevPin, DevPin0 → Generates the address as `..`.
- DevPin0 decrements the Device and Pin values by one.
- * Pin → Generates the address as `.`.
-* For Virtual types →
- * Boolean
- * String
+ * For Input and Output types →
+ * DevPin, DevPin0 → Generates the address as `..`.
+ DevPin0 decrements the Device and Pin values by one for zero-indexed addressing.
+ * Pin → Generates the address as `.`.
+ * For Virtual types →
+ * Boolean
+ * String
+ * Integer
* Device → Device number
* Pin → The pin number on the device
* Resource → The resource ID with which to notify the agent
@@ -406,134 +105,53 @@ The CSV headers are as follows:
* Action2 → The second action ID if required
* Connection → The ID of the PlcConnection with which this I/O is attached
* DeviceId → For type Group: Set the ID of this PlcLogicalDevice being generated
+* Interted → For boolean inputs or outputs, if true, inverts the value
+* Value → A default value, often used for virtual integer addresses
+* Remote → if true, then the server will be notified of changes to this address
When you use this file as input for the `PlcAddressGenerator`, then it will
-generate PlcLogicalDevice, PlcAddress and PlcTelegram elements:
+generate PlcLogicalDevice, PlcAddress and PlcTelegram elements. See the file `strolch-plc-example.xml` for an example.
-```xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-The PlcLogicalDevice references the PlcAddress and PlcTelegram objects, and is
+The `PlcLogicalDevice` references the `PlcAddress` and `PlcTelegram` objects, and is
then used in the UI for grouping.
-The PlcAddress is used to store the current value and defines the keys with
+The `PlcAddress` is used to store the current value and defines the keys with
which the agent will be notified
-The PlcTelegram is used to store default values to send, for specific keys. E.g.
+The `PlcTelegram` is used to store default values to send, for specific keys. E.g.
The action `On` would send true, and `Off` would send false. This is semantics, and
is defined in each project depending on the hardware.
-10. Copy the
- file [plc-state.xml](https://github.com/strolch-li/strolch-plc/blob/develop/example/plc-state.xml)
- to your runtime and reference it by use of
- a `` element. Modify the `PlcId` to be the
- same as the one you defined in the `StrolchConfiguration.xml`.
+### Running the example
-11. Now that we have a model, the `PlcConnections` are to be defined. In the
- previous example we used a Raspberry Pi's GPIOs. This needs to be defined as
- a `PlcConnection`:
+Once you have both the server and PLC instances running, you can login. The default username and password are `admin`
+/ `admin`.
-```xml
+After logging in to the PLC you should be greeted with the following
+screen:
+![PLC UI](/assets/images/plc.png)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
+And after logging in to the server you should be greeted with the following
+screen:
+![Server UI](/assets/images/plc-server.png)
-See [strolch-plc-example-connections.xml](https://github.com/strolch-li/strolch-plc/blob/develop/example/strolch-plc-example-connections.xml) for further examples.
+If the PLC could connect to the server, then the `PLC Control` icon should be activated. The actions Enable, Disable and
+Stop All send telegrams to the PLC, thus showing how the server would communicate with the PLC.
+### Customization
+#### PLC
+
+Now that the server and PLC are running, we can customize the code. On the PLC side you will want to create
+new `li.strolch.plc.core.PlcService` services by extending the class and then registering the service
+in `CustomPlcServiceInitializer`.
+
+See the example `StartupPlcService`.
+
+#### Server
+
+On the server side, to register for notifications from the PLC, one would
+implement `li.strolch.plc.gw.server.PlcGwService` services and register them on the `PlcHandler` in
+the `PostInitializer` class.
+
+See the example `ModePlcSrvService`.
\ No newline at end of file
diff --git a/static/assets/images/plc-server.png b/static/assets/images/plc-server.png
new file mode 100644
index 0000000..1061530
Binary files /dev/null and b/static/assets/images/plc-server.png differ
diff --git a/static/assets/images/plc.png b/static/assets/images/plc.png
new file mode 100644
index 0000000..afba5b6
Binary files /dev/null and b/static/assets/images/plc.png differ