[Major] Updated PLC example setup doc
This commit is contained in:
parent
846548f01c
commit
b525b0f5e4
|
@ -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.
|
||||
Then follow one of the next steps to create the type of application you want.
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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
|
||||
<project>
|
||||
<properties>
|
||||
<strolch.version>1.8.4</strolch.version>
|
||||
<strolch.plc.version>1.2.2</strolch.plc.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependency>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>li.strolch.bom</artifactId>
|
||||
<type>pom</type>
|
||||
<version>${strolch.version}</version>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch-plc-bom</artifactId>
|
||||
<type>pom</type>
|
||||
<version>${strolch.plc.version}</version>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<!-- PLC -->
|
||||
<dependency>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch-plc-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch-plc-rest</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch-plc-gw-client</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
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=<my.groupid> \
|
||||
-DartifactId=<my-artifactId> \
|
||||
-Dversion=<my.version> \
|
||||
-Dpackage=<my.package> \
|
||||
-DappName="<my app name>"
|
||||
|
||||
```
|
||||
|
||||
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 `<my-artifactId>-web` module contains the server code, which handles notifications from the PLC, and can send
|
||||
telegrams to the PLC.
|
||||
- The `<my-artifactId>-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 `<my-artifactId>-plc-web/runtime/data/`.
|
||||
|
||||
```html
|
||||
<!-- HTML Imports -->
|
||||
<link rel="import" href="../bower_components/strolch-wc-plc/strolch-wc-plc-connections.html">
|
||||
<link rel="import" href="../bower_components/strolch-wc-plc/strolch-wc-plc-logical-devices.html">
|
||||
The following sections explains these files:
|
||||
|
||||
<!-- Change default-page to plcLogicalDevices -->
|
||||
<c-app-routing id="appRouting"
|
||||
login-page="login"
|
||||
default-page="plcLogicalDevices"
|
||||
auth-valid="[[authTokenValid]]"
|
||||
page="{{page}}"
|
||||
route-tail="{{routeTail}}"
|
||||
use-hash-as-path></c-app-routing>
|
||||
### strolch-plc-example-connections.xml
|
||||
|
||||
<!-- Add the new pages in the iron-pages element: -->
|
||||
<template is="dom-if" if="[[equal(page, 'plcConnections')]]" restamp>
|
||||
<strolch-wc-plc-connections id="plcConnections"
|
||||
base-path="../"
|
||||
base-rest-path="[[baseRestPath]]"
|
||||
route="{{subroute}}"></strolch-wc-plc-connections>
|
||||
</template>
|
||||
<template is="dom-if" if="[[equal(page, 'plcLogicalDevices')]]" restamp>
|
||||
<strolch-wc-plc-logical-devices id="plcLogicalDevices"
|
||||
base-path="../"
|
||||
base-rest-path="[[baseRestPath]]"
|
||||
base-ws-path="[[baseWsPath]]"
|
||||
route="{{subroute}}"></strolch-wc-plc-logical-devices>
|
||||
</template>
|
||||
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
|
||||
|
||||
<StrolchConfiguration>
|
||||
<env id="dev">
|
||||
<!--
|
||||
This component configures the PlcHandler by
|
||||
loading the PlcConnections, PlcAddresses and PlcTelegrams
|
||||
-->
|
||||
<Component>
|
||||
<name>PlcHandler</name>
|
||||
<api>li.strolch.plc.core.PlcHandler</api>
|
||||
<impl>li.strolch.plc.core.DefaultPlcHandler</impl>
|
||||
<depends>RealmHandler</depends>
|
||||
<Properties>
|
||||
<!-- The component handling the low level connections -->
|
||||
<plcClass>li.strolch.plc.core.hw.DefaultPlc</plcClass>
|
||||
</Properties>
|
||||
</Component>
|
||||
|
||||
<!--
|
||||
This component handles registrations of the PlcServices, i.e. your PLC business logic
|
||||
-->
|
||||
<Component>
|
||||
<name>PlcServiceInitializer</name>
|
||||
<api>li.strolch.plc.core.PlcServiceInitializer</api>
|
||||
<impl>li.strolch.plc.example.CustomPlcServiceInitializer</impl>
|
||||
<depends>PlcHandler</depends>
|
||||
<Properties>
|
||||
</Properties>
|
||||
</Component>
|
||||
|
||||
<!--
|
||||
This component notifies a Strolch agent of changes on the PLC
|
||||
only if you have a Strolch server with a configured
|
||||
li.strolch.plc.gw.server.PlcServerWebSocketEndpoint ready to accept connections
|
||||
-->
|
||||
<Component>
|
||||
<name>PlcGwClientHandler</name>
|
||||
<api>li.strolch.plc.gw.client.PlcGwClientHandler</api>
|
||||
<impl>li.strolch.plc.gw.client.PlcGwClientHandler</impl>
|
||||
<depends>PlcHandler</depends>
|
||||
<depends>PlcServiceInitializer</depends>
|
||||
<Properties>
|
||||
<plcId>plc-01</plcId>
|
||||
<gwUsername>plc-01</gwUsername>
|
||||
<gwPassword>plc-01</gwPassword>
|
||||
<gwServerUrl>ws://localhost:8080/agent/websocket/strolch/plc
|
||||
</gwServerUrl>
|
||||
</Properties>
|
||||
</Component>
|
||||
</env>
|
||||
</StrolchConfiguration>
|
||||
```
|
||||
|
||||
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<PlcService> getPlcServices(PlcHandler plcHandler) {
|
||||
ArrayList<PlcService> 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 `<Connection>.<Device>.<Pin>`.
|
||||
DevPin0 decrements the Device and Pin values by one.
|
||||
* Pin → Generates the address as `<Connection>.<Pin>`.
|
||||
* For Virtual types →
|
||||
* Boolean
|
||||
* String
|
||||
* For Input and Output types →
|
||||
* DevPin, DevPin0 → Generates the address as `<Connection>.<Device>.<Pin>`.
|
||||
DevPin0 decrements the Device and Pin values by one for zero-indexed addressing.
|
||||
* Pin → Generates the address as `<Connection>.<Pin>`.
|
||||
* 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
|
||||
<StrolchModel>
|
||||
<Resource Id="D_MaterialFlow" Name="MaterialFlow" Type="PlcLogicalDevice">
|
||||
<ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
|
||||
<Parameter Id="description" Name="Description" Type="String"
|
||||
Value="Material Flow"/>
|
||||
<Parameter Id="group" Name="Group" Type="String"
|
||||
Value="01 Material Flow"/>
|
||||
<Parameter Id="index" Name="Index" Type="Integer" Value="10"/>
|
||||
</ParameterBag>
|
||||
<ParameterBag Id="relations" Name="Relations" Type="Relations">
|
||||
<Parameter Id="addresses" Name="Addresses" Type="StringList"
|
||||
Interpretation="Resource-Ref" Uom="PlcAddress"
|
||||
Value="A_Conveyor-Occupied, A_Conveyor-BoxDetected, A_Conveyor-MotorOn"/>
|
||||
<Parameter Id="telegrams" Name="Telegrams" Type="StringList"
|
||||
Interpretation="Resource-Ref" Uom="PlcTelegram"
|
||||
Value="T_Conveyor-MotorOn, T_Conveyor-MotorOff"/>
|
||||
</ParameterBag>
|
||||
</Resource>
|
||||
|
||||
<Resource Id="A_Conveyor-Occupied" Name="Conveyor - Occupied"
|
||||
Type="PlcAddress">
|
||||
<ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
|
||||
<Parameter Id="description" Name="Description" Type="String"
|
||||
Index="5"
|
||||
Value="Conveyor 1"/>
|
||||
<Parameter Id="address" Name="HW Address" Type="String"
|
||||
Interpretation="PlcConnection" Index="10"
|
||||
Value="raspiBcmGpioInput.4"/>
|
||||
<Parameter Id="resource" Name="Resource ID for PlcAddress"
|
||||
Type="String"
|
||||
Index="20" Value="Conveyor"/>
|
||||
<Parameter Id="action" Name="Action ID for PlcAddress" Type="String"
|
||||
Index="30" Value="Occupied"/>
|
||||
<Parameter Id="index" Name="Index" Type="Integer" Index="40"
|
||||
Value="10"/>
|
||||
<Parameter Id="value" Name="Value" Type="Boolean" Index="100"
|
||||
Value="false"/>
|
||||
</ParameterBag>
|
||||
</Resource>
|
||||
|
||||
<Resource Id="T_Conveyor-MotorOn" Name="Conveyor - MotorOn"
|
||||
Type="PlcTelegram">
|
||||
<ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
|
||||
<Parameter Id="description" Name="Description" Type="String"
|
||||
Index="5"
|
||||
Value="Conveyor 1"/>
|
||||
<Parameter Id="address" Name="HW Address" Type="String"
|
||||
Interpretation="PlcConnection" Index="10"
|
||||
Value="raspiBcmGpioOutput.18"/>
|
||||
<Parameter Id="resource" Name="Resource ID for PlcAddress"
|
||||
Type="String"
|
||||
Index="20" Value="Conveyor"/>
|
||||
<Parameter Id="action" Name="Action ID for PlcAddress" Type="String"
|
||||
Index="30" Value="MotorOn"/>
|
||||
<Parameter Id="index" Name="Index" Type="Integer" Index="40"
|
||||
Value="10"/>
|
||||
<Parameter Id="value" Name="Value" Type="Boolean" Index="100"
|
||||
Value="true"/>
|
||||
</ParameterBag>
|
||||
</Resource>
|
||||
</StrolchModel>
|
||||
```
|
||||
|
||||
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 `<IncludeFile file="plc-state.xml" />` 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)
|
||||
|
||||
<StrolchModel>
|
||||
<Resource Id="raspiBcmGpioOutput" Name="Raspi BCM GPIO Output"
|
||||
Type="PlcConnection">
|
||||
<ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
|
||||
<Parameter Id="className" Name="Connection Class" Type="String"
|
||||
Value="li.strolch.plc.core.hw.gpio.RaspiBcmGpioOutputConnection"/>
|
||||
<Parameter Id="state" Name="Connection State" Type="String"
|
||||
Interpretation="Enumeration" Uom="ConnectionState"
|
||||
Value="Disconnected"/>
|
||||
<Parameter Id="stateMsg" Name="Connection State Msg" Type="String"
|
||||
Interpretation="Enumeration" Uom="ConnectionState"
|
||||
Value=""/>
|
||||
<Parameter Id="inverted" Name="Inverted" Type="Boolean"
|
||||
Value="false"/>
|
||||
<Parameter Id="bcmOutputPins" Name="BCM Output Pins"
|
||||
Type="IntegerList" Value="27"/>
|
||||
</ParameterBag>
|
||||
</Resource>
|
||||
<Resource Id="raspiBcmGpioInput" Name="Raspi BCM GPIO Input"
|
||||
Type="PlcConnection">
|
||||
<ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
|
||||
<Parameter Id="className" Name="Connection Class" Type="String"
|
||||
Value="li.strolch.plc.core.hw.gpio.RaspiBcmGpioInputConnection"/>
|
||||
<Parameter Id="state" Name="Connection State" Type="String"
|
||||
Interpretation="Enumeration" Uom="ConnectionState"
|
||||
Value="Disconnected"/>
|
||||
<Parameter Id="stateMsg" Name="Connection State Msg" Type="String"
|
||||
Interpretation="Enumeration" Uom="ConnectionState"
|
||||
Value=""/>
|
||||
<Parameter Id="inverted" Name="Inverted" Type="Boolean"
|
||||
Value="true"/>
|
||||
<Parameter Id="bcmInputPins" Name="BCM Input Pins"
|
||||
Type="IntegerList" Value="4"/>
|
||||
</ParameterBag>
|
||||
</Resource>
|
||||
</StrolchModel>
|
||||
```
|
||||
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`.
|
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
Loading…
Reference in New Issue