Skip to content

Commit

Permalink
feat(plc4j/cbus): added simple cbus implementation to plc-simulator
Browse files Browse the repository at this point in the history
  • Loading branch information
sruehl committed Jul 27, 2022
1 parent 3a131f2 commit 067ad4f
Show file tree
Hide file tree
Showing 11 changed files with 301 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ protected ProtocolStackConfigurer<CBusCommand> getStackConfigurer() {
public static class ByteLengthEstimator implements ToIntFunction<ByteBuf> {
@Override
public int applyAsInt(ByteBuf byteBuf) {
System.err.println("Alarm" + byteBuf);
// TODO: we might need to try multiple times because the ln might not be here in time
for (int i = 0; i < byteBuf.readableBytes(); i++) {
boolean hasOneMore = i + 1 < byteBuf.readableBytes();
Expand Down
10 changes: 10 additions & 0 deletions sandbox/plc-simulator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@
<artifactId>plc4j-driver-s7</artifactId>
<version>0.10.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.plc4x</groupId>
<artifactId>plc4j-driver-c-bus</artifactId>
<version>0.10.0-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>org.apache.plc4x</groupId>
Expand All @@ -74,6 +79,11 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
</dependency>


<!-- Explicitly override the scope to compile to include these -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.apache.plc4x.simulator;

import org.apache.commons.cli.*;
import org.apache.plc4x.simulator.model.Context;
import org.apache.plc4x.simulator.server.ServerModule;
import org.apache.plc4x.simulator.simulation.SimulationModule;
Expand All @@ -37,27 +38,28 @@ public class PlcSimulator {
private final Map<String, ServerModule> serverModules;
private final SimulationModule simulationModule;

private PlcSimulator(String simulationName) {
this(simulationName, Thread.currentThread().getContextClassLoader());
private PlcSimulator(String simulationName, PlcSimulatorConfig config) {
this(simulationName, config, Thread.currentThread().getContextClassLoader());
}

private PlcSimulator(String simulationName, ClassLoader classLoader) {
private PlcSimulator(String simulationName, PlcSimulatorConfig config, ClassLoader classLoader) {
Context context = null;
// Initialize all the simulation modules.
LOGGER.info("Initializing Simulation Modules:");
SimulationModule foundSimulationModule = null;
ServiceLoader<SimulationModule> simulationModuleLoader = ServiceLoader.load(SimulationModule.class, classLoader);
for (SimulationModule curSimulationModule : simulationModuleLoader) {
if(curSimulationModule.getName().equals(simulationName)) {
LOGGER.info(String.format("Initializing simulation module: %s ...", simulationName));
foundSimulationModule = curSimulationModule;
context = curSimulationModule.getContext();
LOGGER.info("Initialized");
if (!curSimulationModule.getName().equals(simulationName)) {
continue;
}
LOGGER.info("Initializing simulation module: {} ...", simulationName);
foundSimulationModule = curSimulationModule;
context = curSimulationModule.getContext();
LOGGER.info("Initialized");
}
// If we couldn't find the simulation module provided, report an error and exit.
if(foundSimulationModule == null) {
LOGGER.info(String.format("Couldn't find simulation module %s", simulationName));
if (foundSimulationModule == null) {
LOGGER.info("Couldn't find simulation module {}", simulationName);
System.exit(1);
}
simulationModule = foundSimulationModule;
Expand All @@ -68,10 +70,11 @@ private PlcSimulator(String simulationName, ClassLoader classLoader) {
serverModules = new TreeMap<>();
ServiceLoader<ServerModule> serverModuleLoader = ServiceLoader.load(ServerModule.class, classLoader);
for (ServerModule serverModule : serverModuleLoader) {
LOGGER.info(String.format("Initializing server module: %s ...", serverModule.getName()));
LOGGER.info("Initializing server module: {} ...", serverModule.getName());
serverModules.put(serverModule.getName(), serverModule);
// Inject the contexts.
serverModule.setContext(context);
serverModule.setConfig(config);
LOGGER.info("Initialized");
}
LOGGER.info("Finished Initializing Server Modules\n");
Expand All @@ -83,13 +86,17 @@ private void stop() {
running = false;
}

private void run() throws Exception {
private void run() {
// Start all server modules.
LOGGER.info("Starting Server Modules:");
for (ServerModule serverModule : serverModules.values()) {
LOGGER.info(String.format("Starting server module: %s ...", serverModule.getName()));
serverModule.start();
LOGGER.info("Started");
LOGGER.info("Starting server module: {}...", serverModule.getName());
try {
serverModule.start();
LOGGER.info("Started");
} catch (Exception e) {
LOGGER.warn("Error starting server module: {}...", serverModule.getName(), e);
}
}
LOGGER.info("Finished Starting Server Modules\n");

Expand All @@ -98,30 +105,57 @@ private void run() throws Exception {
while (running) {
try {
simulationModule.loop();
} catch(Exception e) {
LOGGER.error("Caught error while executing loop() method of " + simulationModule.getName() +
" simulation.", e);
} catch (Exception e) {
LOGGER.error("Caught error while executing loop() method of {} simulation.", simulationModule.getName(), e);
}
// Sleep 100 ms to not run the simulation too eagerly.
TimeUnit.MILLISECONDS.sleep(100);
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
}
} finally {
LOGGER.info("Simulations ended");
// Start all server modules.
for (ServerModule serverModule : serverModules.values()) {
LOGGER.info(String.format("Stopping server module %s ...", serverModule.getName()));
serverModule.stop();
LOGGER.info("Stopped");
LOGGER.info("Stopping server module {} ...", serverModule.getName());
try {
serverModule.stop();
LOGGER.info("Stopped");
} catch (Exception e) {
LOGGER.warn("Error stopping server module {} ...", serverModule.getName());
}

}
}
}

public static void main(String[] args) throws Exception {
final PlcSimulator simulator = new PlcSimulator("Water Tank");
public static void main(String... args) throws Exception {
final PlcSimulator simulator = new PlcSimulator("Water Tank", plcSimulatorConfigFromArgs(args));
// Make sure we stop everything correctly.
Runtime.getRuntime().addShutdownHook(new Thread(simulator::stop));
// Start the simulator.
simulator.run();
}

public static PlcSimulatorConfig plcSimulatorConfigFromArgs(String... args) throws Exception {
PlcSimulatorConfig config = new PlcSimulatorConfig();

// Build options
Options options = new Options();

options.addOption("host", true, "display current time");

// Parse args
CommandLineParser parser = new DefaultParser();
CommandLine cmd = parser.parse(options, args);

// Map options
config.host = cmd.getOptionValue("--host", "localhost");

return config;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.apache.plc4x.simulator;

public class PlcSimulatorConfig {
String host;

public String getHost() {
return host;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@
*/
package org.apache.plc4x.simulator.exceptions;

public class SimulatorExcepiton extends Exception {
public class SimulatorException extends Exception {

public SimulatorExcepiton(String message) {
public SimulatorException(String message) {
super(message);
}

public SimulatorExcepiton(String message, Throwable cause) {
public SimulatorException(String message, Throwable cause) {
super(message, cause);
}

public SimulatorExcepiton(Throwable cause) {
public SimulatorException(Throwable cause) {
super(cause);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
*/
package org.apache.plc4x.simulator.server;

import org.apache.plc4x.simulator.exceptions.SimulatorExcepiton;
import org.apache.plc4x.simulator.PlcSimulatorConfig;
import org.apache.plc4x.simulator.exceptions.SimulatorException;
import org.apache.plc4x.simulator.model.Context;

public interface ServerModule {
Expand All @@ -28,10 +29,11 @@ public interface ServerModule {
*/
String getName();

void setContext(Context contexts);
void setConfig(PlcSimulatorConfig config);

void start() throws SimulatorExcepiton;
void setContext(Context contexts);

void stop() throws SimulatorExcepiton;
void start() throws SimulatorException;

void stop() throws SimulatorException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.plc4x.simulator.server.cbus;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.apache.plc4x.java.cbus.CBusDriver;
import org.apache.plc4x.java.cbus.readwrite.CBusConstants;
import org.apache.plc4x.java.cbus.readwrite.CBusMessage;
import org.apache.plc4x.java.cbus.readwrite.CBusOptions;
import org.apache.plc4x.java.cbus.readwrite.RequestContext;
import org.apache.plc4x.java.spi.connection.GeneratedProtocolMessageCodec;
import org.apache.plc4x.java.spi.generation.ByteOrder;
import org.apache.plc4x.simulator.PlcSimulatorConfig;
import org.apache.plc4x.simulator.exceptions.SimulatorException;
import org.apache.plc4x.simulator.model.Context;
import org.apache.plc4x.simulator.server.ServerModule;
import org.apache.plc4x.simulator.server.cbus.protocol.CBusServerAdapter;

public class CBusServerModule implements ServerModule {

private EventLoopGroup loopGroup;
private EventLoopGroup workerGroup;
private Context context;
private PlcSimulatorConfig config;

@Override
public String getName() {
return "C-BUS";
}

@Override
public void setConfig(PlcSimulatorConfig config) {
this.config = config;
}

@Override
public void setContext(Context context) {
this.context = context;
}


@Override
public void start() throws SimulatorException {
if (loopGroup != null) {
return;
}

try {
loopGroup = new NioEventLoopGroup();
workerGroup = new NioEventLoopGroup();

ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(loopGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel channel) {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new GeneratedProtocolMessageCodec<>(CBusMessage.class,
CBusMessage::staticParse, ByteOrder.BIG_ENDIAN,
new Object[]{false, new RequestContext(false, false, false), new CBusOptions(false, false, false, false, false, false, false, false, false)},
new CBusDriver.ByteLengthEstimator(),
new CBusDriver.CorruptPackageCleaner()));
pipeline.addLast(new CBusServerAdapter(context));
}
}).option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);

bootstrap.bind(config.getHost(), CBusConstants.CBUSTCPDEFAULTPORT).sync();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new SimulatorException(e);
}
}

@Override
public void stop() {
if (workerGroup == null) {
return;
}

workerGroup.shutdownGracefully();
loopGroup.shutdownGracefully();
}

}
Loading

0 comments on commit 067ad4f

Please sign in to comment.