Compare commits

...

2 Commits

Author SHA1 Message Date
Robert von Burg 5e2e139fcf
[Minor] Code cleanup 2024-04-12 13:20:48 +02:00
Robert von Burg d9f6f9daca
[New] ReadState is now configurable with configuration parameter plcSupportsReadState
If this parameter is missing, it is considered true, as this is the previous behaviour
2024-04-12 10:56:46 +02:00
43 changed files with 1161 additions and 1159 deletions

View File

@ -212,7 +212,7 @@ public class DefaultPlcHandler extends StrolchComponent implements PlcHandler, P
if (this.globalListener != null) if (this.globalListener != null)
this.plc.setGlobalListener(this.globalListener); this.plc.setGlobalListener(this.globalListener);
logger.info("Reconfigured PLC with " + this.plcAddresses.size() + " addresses"); logger.info("Reconfigured PLC with {} addresses", this.plcAddresses.size());
return true; return true;
} catch (Exception e) { } catch (Exception e) {
@ -239,7 +239,7 @@ public class DefaultPlcHandler extends StrolchComponent implements PlcHandler, P
if (tx.getConfiguration().hasParameter(PARAM_VERBOSE)) { if (tx.getConfiguration().hasParameter(PARAM_VERBOSE)) {
boolean verboseOverride = tx.getConfiguration().getBoolean(PARAM_VERBOSE); boolean verboseOverride = tx.getConfiguration().getBoolean(PARAM_VERBOSE);
logger.info("Overriding XML verbose property from configuration resource to " + verboseOverride); logger.info("Overriding XML verbose property from configuration resource to {}", verboseOverride);
this.verbose = verboseOverride; this.verbose = verboseOverride;
} }
@ -257,8 +257,8 @@ public class DefaultPlcHandler extends StrolchComponent implements PlcHandler, P
try { try {
getContainer().getPrivilegeHandler().validateSystemSession(this.ctx); getContainer().getPrivilegeHandler().validateSystemSession(this.ctx);
} catch (Exception e) { } catch (Exception e) {
logger.error("PrivilegeContext for session " + this.ctx.getCertificate().getSessionId() + logger.error("PrivilegeContext for session {} is not valid, reopening.",
" is not valid, reopening.", e); this.ctx.getCertificate().getSessionId(), e);
this.ctx = getContainer().getPrivilegeHandler().openAgentSystemUserContext(); this.ctx = getContainer().getPrivilegeHandler().openAgentSystemUserContext();
} }
} }
@ -284,7 +284,7 @@ public class DefaultPlcHandler extends StrolchComponent implements PlcHandler, P
public void unregister(String resource, String action, PlcListener listener) { public void unregister(String resource, String action, PlcListener listener) {
PlcAddress plcAddress = this.plcAddresses.getElement(resource, action); PlcAddress plcAddress = this.plcAddresses.getElement(resource, action);
if (plcAddress == null) { if (plcAddress == null) {
logger.warn("No PlcAddress exists for " + resource + "-" + action); logger.warn("No PlcAddress exists for {}-{}", resource, action);
} else { } else {
this.plc.unregister(plcAddress, listener); this.plc.unregister(plcAddress, listener);
} }
@ -353,7 +353,7 @@ public class DefaultPlcHandler extends StrolchComponent implements PlcHandler, P
String addressId = this.addressesToResourceId.get(address); String addressId = this.addressesToResourceId.get(address);
if (addressId == null) { if (addressId == null) {
logger.error("No PlcAddress mapping for " + address); logger.error("No PlcAddress mapping for {}", address);
return; return;
} }
@ -377,11 +377,11 @@ public class DefaultPlcHandler extends StrolchComponent implements PlcHandler, P
tx.update(addressRes); tx.update(addressRes);
tx.commitOnClose(); tx.commitOnClose();
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to update PlcAddress " + addressId + " with new value " + value, e); logger.error("Failed to update PlcAddress {} with new value {}", addressId, value, e);
} }
if (this.verbose && (nanoTime() - s > MILLISECONDS.toNanos(SILENT_THRESHOLD))) if (this.verbose && (nanoTime() - s > MILLISECONDS.toNanos(SILENT_THRESHOLD)))
logger.info("async update " + address.toKey() + " took " + (formatNanoDuration(nanoTime() - s))); logger.info("async update {} took {}", address.toKey(), formatNanoDuration(nanoTime() - s));
} }
private void updateConnectionState(String id, ConnectionState state, String stateMsg) { private void updateConnectionState(String id, ConnectionState state, String stateMsg) {
@ -405,11 +405,11 @@ public class DefaultPlcHandler extends StrolchComponent implements PlcHandler, P
tx.commitOnClose(); tx.commitOnClose();
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to update state for connection " + id, e); logger.error("Failed to update state for connection {}", id, e);
} }
if (this.verbose) if (this.verbose)
logger.info("updateConnectionState took " + (formatNanoDuration(nanoTime() - s))); logger.info("updateConnectionState took {}", formatNanoDuration(nanoTime() - s));
} }
@Override @Override

View File

@ -28,7 +28,7 @@ class PlcConfigurator {
Map<PlcAddress, String> addressesToResourceId) throws Exception { Map<PlcAddress, String> addressesToResourceId) throws Exception {
// instantiate Plc // instantiate Plc
logger.info("Configuring PLC " + plcClassName + "..."); logger.info("Configuring PLC {}...", plcClassName);
Plc plc = ClassHelper.instantiateClass(plcClassName); Plc plc = ClassHelper.instantiateClass(plcClassName);
// instantiate all PlcConnections // instantiate all PlcConnections
@ -44,10 +44,10 @@ class PlcConfigurator {
// first all addresses // first all addresses
for (Resource resource : logicalDevices) { for (Resource resource : logicalDevices) {
logger.info("Configuring PlcAddresses for PlcLogicalDevice " + resource.getId() + "..."); logger.info("Configuring PlcAddresses for PlcLogicalDevice {}...", resource.getId());
List<Resource> addresses = tx.getResourcesByRelation(resource, PARAM_ADDRESSES, true); List<Resource> addresses = tx.getResourcesByRelation(resource, PARAM_ADDRESSES, true);
if (addresses.isEmpty()) { if (addresses.isEmpty()) {
logger.warn("\tNo PlcAddresses for " + resource.getId()); logger.warn("\tNo PlcAddresses for {}", resource.getId());
} else { } else {
for (Resource addressRes : addresses) { for (Resource addressRes : addresses) {
buildPlcAddress(plc, plcAddresses, addressesToResourceId, plcAddressesByHwAddress, addressRes); buildPlcAddress(plc, plcAddresses, addressesToResourceId, plcAddressesByHwAddress, addressRes);
@ -57,10 +57,10 @@ class PlcConfigurator {
// now telegrams // now telegrams
for (Resource logicalDevice : logicalDevices) { for (Resource logicalDevice : logicalDevices) {
logger.info("Configuring PlcTelegrams for PlcLogicalDevice " + logicalDevice.getId() + "..."); logger.info("Configuring PlcTelegrams for PlcLogicalDevice {}...", logicalDevice.getId());
List<Resource> telegrams = tx.getResourcesByRelation(logicalDevice, PARAM_TELEGRAMS, true); List<Resource> telegrams = tx.getResourcesByRelation(logicalDevice, PARAM_TELEGRAMS, true);
if (telegrams.isEmpty()) { if (telegrams.isEmpty()) {
logger.warn("\tNo PlcTelegrams for " + logicalDevice.getId()); logger.warn("\tNo PlcTelegrams for {}", logicalDevice.getId());
} else { } else {
for (Resource telegramRes : telegrams) { for (Resource telegramRes : telegrams) {
buildTelegramPlcAddress(plcAddresses, plcTelegrams, addressesToResourceId, plcAddressesByHwAddress, buildTelegramPlcAddress(plcAddresses, plcTelegrams, addressesToResourceId, plcAddressesByHwAddress,
@ -74,9 +74,9 @@ class PlcConfigurator {
private static void configureConnection(Plc plc, Resource connection) throws Exception { private static void configureConnection(Plc plc, Resource connection) throws Exception {
String className = connection.getParameter(BAG_PARAMETERS, PARAM_CLASS_NAME, true).getValue(); String className = connection.getParameter(BAG_PARAMETERS, PARAM_CLASS_NAME, true).getValue();
logger.info("Configuring PLC Connection " + className + "..."); logger.info("Configuring PLC Connection {}...", className);
PlcConnection plcConnection = ClassHelper.instantiateClass(className, PlcConnection plcConnection = ClassHelper.instantiateClass(className, new Class<?>[]{Plc.class, String.class},
new Class<?>[] { Plc.class, String.class }, new Object[] { plc, connection.getId() }); new Object[]{plc, connection.getId()});
plcConnection.initialize(connection.getParameterBag(BAG_PARAMETERS, true).toObjectMap()); plcConnection.initialize(connection.getParameterBag(BAG_PARAMETERS, true).toObjectMap());
plc.addConnection(plcConnection); plc.addConnection(plcConnection);
} }
@ -121,14 +121,18 @@ class PlcConfigurator {
telegramRes.getLocator() + " is referencing non-existing address " + address); telegramRes.getLocator() + " is referencing non-existing address " + address);
if (valueP.getValueType() != existingAddress.valueType) { if (valueP.getValueType() != existingAddress.valueType) {
throw new IllegalStateException( throw new IllegalStateException(telegramRes.getLocator()
telegramRes.getLocator() + " has valueType " + valueP.getValueType() + " but address " + " has valueType "
+ existingAddress.address + " has type " + existingAddress.valueType); + valueP.getValueType()
+ " but address "
+ existingAddress.address
+ " has type "
+ existingAddress.valueType);
} }
PlcAddress telegramAddress = new PlcAddress(PlcAddressType.Telegram, resource, action, address, PlcAddress telegramAddress = new PlcAddress(PlcAddressType.Telegram, resource, action, address,
valueP.getValueType(), valueP.getValue(), false, remote); valueP.getValueType(), valueP.getValue(), false, remote);
logger.info("Adding " + telegramAddress + "..."); logger.info("Adding {}...", telegramAddress);
PlcAddress replaced = plcTelegrams.addElement(resource, action, telegramAddress); PlcAddress replaced = plcTelegrams.addElement(resource, action, telegramAddress);
if (replaced != null) if (replaced != null)
@ -139,9 +143,12 @@ class PlcConfigurator {
PlcAddress plcAddress = plcAddresses.getElement(existingAddress.resource, existingAddress.action); PlcAddress plcAddress = plcAddresses.getElement(existingAddress.resource, existingAddress.action);
if (plcAddress == null) if (plcAddress == null)
throw new IllegalStateException( throw new IllegalStateException("PlcAddress for "
"PlcAddress for " + resource + "-" + action + " does not exist, so can not connect PlcTelegram " + resource
+ telegramAddress); + "-"
+ action
+ " does not exist, so can not connect PlcTelegram "
+ telegramAddress);
String addressId = addressesToResourceId.get(plcAddress); String addressId = addressesToResourceId.get(plcAddress);
if (addressId == null) if (addressId == null)
throw new IllegalStateException( throw new IllegalStateException(

View File

@ -10,6 +10,8 @@ import li.strolch.policy.ReloadPrivilegeHandlerJob;
import li.strolch.runtime.configuration.RuntimeConfiguration; import li.strolch.runtime.configuration.RuntimeConfiguration;
import li.strolch.utils.helper.ExceptionHelper; import li.strolch.utils.helper.ExceptionHelper;
import static java.text.MessageFormat.format;
public class PlcPostInitializer extends SimplePostInitializer { public class PlcPostInitializer extends SimplePostInitializer {
public PlcPostInitializer(ComponentContainer container, String componentName) { public PlcPostInitializer(ComponentContainer container, String componentName) {
@ -49,8 +51,9 @@ public class PlcPostInitializer extends SimplePostInitializer {
protected void notifyStart() { protected void notifyStart() {
if (!(getConfiguration().getBoolean("notifyStart", Boolean.FALSE) && getContainer() if (!(
.hasComponent(MailHandler.class))) getConfiguration().getBoolean("notifyStart", Boolean.FALSE) && getContainer().hasComponent(
MailHandler.class)))
return; return;
String recipients = getConfiguration().getString("notifyStartRecipients", ""); String recipients = getConfiguration().getString("notifyStartRecipients", "");
@ -61,20 +64,18 @@ public class PlcPostInitializer extends SimplePostInitializer {
StrolchAgent agent = getContainer().getAgent(); StrolchAgent agent = getContainer().getAgent();
RuntimeConfiguration runtimeConfiguration = agent.getStrolchConfiguration().getRuntimeConfiguration(); RuntimeConfiguration runtimeConfiguration = agent.getStrolchConfiguration().getRuntimeConfiguration();
String subject = runtimeConfiguration.getApplicationName() + ":" + runtimeConfiguration.getEnvironment() String subject = format("{0}:{1} Startup Complete!", runtimeConfiguration.getApplicationName(),
+ " Startup Complete!"; runtimeConfiguration.getEnvironment());
String body = "Dear User\n\n" // String body = format(
+ "The " + getConfiguration().getRuntimeConfiguration().getApplicationName() "Dear User\n\nThe {0} Server has just completed startup with version {1}\n\n\tYour Server.",
+ " Server has just completed startup with version " // getConfiguration().getRuntimeConfiguration().getApplicationName(),
+ agent.getVersion().getAppVersion().getArtifactVersion() // agent.getVersion().getAppVersion().getArtifactVersion());
+ "\n\n" //
+ "\tYour Server.";
try { try {
getContainer().getComponent(MailHandler.class).sendMailAsync(subject, body, recipients); getContainer().getComponent(MailHandler.class).sendMailAsync(subject, body, recipients);
} catch (Exception e) { } catch (Exception e) {
logger.error("Notifying of server startup failed: " + ExceptionHelper.getRootCause(e), e); logger.error("Notifying of server startup failed: {}", ExceptionHelper.getRootCause(e), e);
} }
} }
} }

View File

@ -296,7 +296,7 @@ public abstract class PlcService implements PlcListener {
* @param locator the locator of the message * @param locator the locator of the message
*/ */
protected void disableMsg(Locator locator) { protected void disableMsg(Locator locator) {
logger.info("Disabling message for locator " + locator); logger.info("Disabling message for locator {}", locator);
this.plcHandler.disableMsg(locator); this.plcHandler.disableMsg(locator);
} }
@ -544,6 +544,6 @@ public abstract class PlcService implements PlcListener {
* @param e the exception which occurred * @param e the exception which occurred
*/ */
protected void handleFailedAsync(Exception e) { protected void handleFailedAsync(Exception e) {
logger.error("Failed to execute " + getClass().getSimpleName(), e); logger.error("Failed to execute {}", getClass().getSimpleName(), e);
} }
} }

View File

@ -31,12 +31,12 @@ public abstract class PlcServiceInitializer extends StrolchComponent {
try { try {
plcService.stop(); plcService.stop();
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to stop PlcService " + plcService.getClass().getName(), e); logger.error("Failed to stop PlcService {}", plcService.getClass().getName(), e);
} }
try { try {
plcService.unregister(); plcService.unregister();
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to unregister PlcService " + plcService.getClass().getName(), e); logger.error("Failed to unregister PlcService {}", plcService.getClass().getName(), e);
} }
}); });
super.stop(); super.stop();
@ -45,7 +45,7 @@ public abstract class PlcServiceInitializer extends StrolchComponent {
protected void startPlcServices() { protected void startPlcServices() {
PlcHandler plcHandler = getComponent(PlcHandler.class); PlcHandler plcHandler = getComponent(PlcHandler.class);
if (plcHandler.getPlcState() != PlcState.Started) { if (plcHandler.getPlcState() != PlcState.Started) {
logger.error("Can not start PlcServices as PlcState is " + plcHandler.getPlcState()); logger.error("Can not start PlcServices as PlcState is {}", plcHandler.getPlcState());
return; return;
} }
@ -54,7 +54,7 @@ public abstract class PlcServiceInitializer extends StrolchComponent {
try { try {
plcService.register(); plcService.register();
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to register PlcService " + plcService.getClass().getName(), e); logger.error("Failed to register PlcService {}", plcService.getClass().getName(), e);
} }
} }
@ -68,7 +68,7 @@ public abstract class PlcServiceInitializer extends StrolchComponent {
try { try {
plcService.start(tx); plcService.start(tx);
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to register PlcService " + plcService.getClass().getName(), e); logger.error("Failed to register PlcService {}", plcService.getClass().getName(), e);
} }
} }
} }

View File

@ -1,12 +1,5 @@
package li.strolch.plc.core.hw; package li.strolch.plc.core.hw;
import static java.util.stream.Collectors.toSet;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.stream.Stream;
import li.strolch.plc.model.PlcAddress; import li.strolch.plc.model.PlcAddress;
import li.strolch.plc.model.PlcAddressKey; import li.strolch.plc.model.PlcAddressKey;
import li.strolch.plc.model.PlcAddressType; import li.strolch.plc.model.PlcAddressType;
@ -15,6 +8,13 @@ import li.strolch.utils.collections.MapOfLists;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toSet;
public class DefaultPlc implements Plc { public class DefaultPlc implements Plc {
private static final Logger logger = LoggerFactory.getLogger(DefaultPlc.class); private static final Logger logger = LoggerFactory.getLogger(DefaultPlc.class);
@ -79,15 +79,15 @@ public class DefaultPlc implements Plc {
@Override @Override
public void register(PlcAddress address, PlcListener listener) { public void register(PlcAddress address, PlcListener listener) {
this.listeners.addElement(address, listener); this.listeners.addElement(address, listener);
logger.info(address.toKeyAddress() + ": " + listener.getClass().getSimpleName()); logger.info("{}: {}", address.toKeyAddress(), listener.getClass().getSimpleName());
} }
@Override @Override
public void unregister(PlcAddress address, PlcListener listener) { public void unregister(PlcAddress address, PlcListener listener) {
if (this.listeners.removeElement(address, listener)) { if (this.listeners.removeElement(address, listener)) {
logger.info(address + ": " + listener.getClass().getName()); logger.info("{}: {}", address, listener.getClass().getName());
} else { } else {
logger.warn("Listener not registered with key " + address.toKeyAddress() + ": " + logger.warn("Listener not registered with key {}: {}", address.toKeyAddress(),
listener.getClass().getSimpleName()); listener.getClass().getSimpleName());
} }
} }
@ -105,7 +105,7 @@ public class DefaultPlc implements Plc {
private void doNotify(String address, Object value) { private void doNotify(String address, Object value) {
PlcAddress plcAddress = this.notificationMappings.get(address); PlcAddress plcAddress = this.notificationMappings.get(address);
if (plcAddress == null) { if (plcAddress == null) {
logger.warn("No mapping to PlcAddress for hwAddress " + address); logger.warn("No mapping to PlcAddress for hwAddress {}", address);
return; return;
} }
@ -113,7 +113,7 @@ public class DefaultPlc implements Plc {
if (value instanceof Boolean) if (value instanceof Boolean)
value = !((boolean) value); value = !((boolean) value);
else else
logger.error(plcAddress + " is marked as inverted, but the value is not a boolean, but a " + logger.error("{} is marked as inverted, but the value is not a boolean, but a {}", plcAddress,
value.getClass()); value.getClass());
} }
@ -130,11 +130,11 @@ public class DefaultPlc implements Plc {
for (PlcListener listener : listeners) { for (PlcListener listener : listeners) {
try { try {
if (this.verbose) if (this.verbose)
logger.info("Notifying " + plcAddress.toKey() + ": " + value + " @ " + listener); logger.info("Notifying {}: {} @ {}", plcAddress.toKey(), value, listener);
listener.handleNotification(plcAddress, value); listener.handleNotification(plcAddress, value);
} catch (Exception e) { } catch (Exception e) {
if (catchExceptions) { if (catchExceptions) {
logger.error("Failed to notify listener " + listener + " for address " + plcAddress, e); logger.error("Failed to notify listener {} for address {}", listener, plcAddress, e);
} else { } else {
throw e; throw e;
} }
@ -157,7 +157,7 @@ public class DefaultPlc implements Plc {
logger.error("Interrupted!"); logger.error("Interrupted!");
} catch (Exception e) { } catch (Exception e) {
if (task != null) if (task != null)
logger.error("Failed to perform notification for " + task.address + ": " + task.value, e); logger.error("Failed to perform notification for {}: {}", task.address, task.value, e);
else else
logger.error("Failed to get notification task", e); logger.error("Failed to get notification task", e);
} }
@ -209,10 +209,10 @@ public class DefaultPlc implements Plc {
public void addConnection(PlcConnection connection) { public void addConnection(PlcConnection connection) {
this.connections.put(connection.getId(), connection); this.connections.put(connection.getId(), connection);
Set<String> addresses = connection.getAddresses(); Set<String> addresses = connection.getAddresses();
logger.info("Adding connection " + connection.getId() + " " + connection.getClass().getName() + " with " + logger.info("Adding connection {} {} with {} addresses...", connection.getId(), connection.getClass().getName(),
addresses.size() + " addresses..."); addresses.size());
for (String address : addresses) { for (String address : addresses) {
logger.info(" Adding " + address + "..."); logger.info(" Adding {}...", address);
this.connectionsByAddress.put(address, connection); this.connectionsByAddress.put(address, connection);
} }
} }
@ -268,23 +268,13 @@ public class DefaultPlc implements Plc {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Replaced mapping for address " + address.address + " for key " + replaced + " with " + address); "Replaced mapping for address " + address.address + " for key " + replaced + " with " + address);
logger.info("Registered " + address); logger.info("Registered {}", address);
} }
private void validateVirtualAddress(PlcAddress address) { private void validateVirtualAddress(PlcAddress address) {
switch (address.address) {
if (address.address.equals(VIRTUAL_BOOLEAN) || address.address.equals(VIRTUAL_BOOLEAN + ".")) { case VIRTUAL_BOOLEAN, VIRTUAL_BOOLEAN + ".", VIRTUAL_STRING, VIRTUAL_STRING + ".", VIRTUAL_INTEGER,
throw new IllegalStateException( VIRTUAL_INTEGER + "." -> throw new IllegalStateException(
"Virtual address " + address.address + " is missing sub component for " + address);
}
if (address.address.equals(VIRTUAL_STRING) || address.address.equals(VIRTUAL_STRING + ".")) {
throw new IllegalStateException(
"Virtual address " + address.address + " is missing sub component for " + address);
}
if (address.address.equals(VIRTUAL_INTEGER) || address.address.equals(VIRTUAL_INTEGER + ".")) {
throw new IllegalStateException(
"Virtual address " + address.address + " is missing sub component for " + address); "Virtual address " + address.address + " is missing sub component for " + address);
} }
} }

View File

@ -11,10 +11,8 @@ public interface PlcListener {
/** /**
* Notifies the listener of the new value at the given address * Notifies the listener of the new value at the given address
* *
* @param address * @param address the address at which the event was detected
* the address at which the event was detected * @param value the new value at the address
* @param value
* the new value at the address
*/ */
void handleNotification(PlcAddress address, Object value); void handleNotification(PlcAddress address, Object value);
} }

View File

@ -1,7 +1,7 @@
package li.strolch.plc.core.hw.connections; package li.strolch.plc.core.hw.connections;
import static li.strolch.plc.model.PlcConstants.PARAM_SIMULATED; import li.strolch.plc.core.hw.Plc;
import static li.strolch.utils.helper.ExceptionHelper.getExceptionMessageWithCauses; import li.strolch.utils.helper.AsciiHelper;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -15,8 +15,9 @@ import java.util.Set;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import li.strolch.plc.core.hw.Plc; import static java.text.MessageFormat.format;
import li.strolch.utils.helper.AsciiHelper; import static li.strolch.plc.model.PlcConstants.PARAM_SIMULATED;
import static li.strolch.utils.helper.ExceptionHelper.getExceptionMessageWithCauses;
public class DataLogicScannerConnection extends SimplePlcConnection { public class DataLogicScannerConnection extends SimplePlcConnection {
@ -64,13 +65,13 @@ public class DataLogicScannerConnection extends SimplePlcConnection {
this.addresses.add(this.addressTrigger); this.addresses.add(this.addressTrigger);
this.addresses.add(this.addressBarcode); this.addresses.add(this.addressBarcode);
logger.info("Configured DataLogic Scanner connection to " + this.address + ":" + this.port); logger.info("Configured DataLogic Scanner connection to {}:{}", this.address, this.port);
} }
@Override @Override
public boolean connect() { public boolean connect() {
if (this.simulated) { if (this.simulated) {
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!"); logger.warn("{}: Running SIMULATED, NOT CONNECTING!", this.id);
return super.connect(); return super.connect();
} }
@ -80,7 +81,7 @@ public class DataLogicScannerConnection extends SimplePlcConnection {
try { try {
this.socket = new Socket(this.address, this.port); this.socket = new Socket(this.address, this.port);
this.socket.setSoTimeout((int) TimeUnit.SECONDS.toMillis(this.readTimeout)); this.socket.setSoTimeout((int) TimeUnit.SECONDS.toMillis(this.readTimeout));
logger.info("Connected DataLogic Scanner connection to " + this.address + ":" + this.port); logger.info("Connected DataLogic Scanner connection to {}:{}", this.address, this.port);
this.read = true; this.read = true;
this.readTask = this.plc.getExecutorPool().getSingleThreadExecutor(this.id).submit(this::read); this.readTask = this.plc.getExecutorPool().getSingleThreadExecutor(this.id).submit(this::read);
@ -98,7 +99,7 @@ public class DataLogicScannerConnection extends SimplePlcConnection {
@Override @Override
public void disconnect() { public void disconnect() {
if (this.simulated) { if (this.simulated) {
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!"); logger.warn("{}: Running SIMULATED, NOT CONNECTING!", this.id);
super.disconnect(); super.disconnect();
return; return;
} }
@ -114,7 +115,7 @@ public class DataLogicScannerConnection extends SimplePlcConnection {
} }
if (this.socket != null) { if (this.socket != null) {
logger.warn("Closing socket to " + this.address + ":" + this.port); logger.warn("Closing socket to {}:{}", this.address, this.port);
try { try {
this.socket.shutdownInput(); this.socket.shutdownInput();
this.socket.shutdownOutput(); this.socket.shutdownOutput();
@ -150,7 +151,7 @@ public class DataLogicScannerConnection extends SimplePlcConnection {
throw new IllegalStateException("Illegal Address " + address); throw new IllegalStateException("Illegal Address " + address);
if (this.simulated) { if (this.simulated) {
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!"); logger.warn("{}: Running SIMULATED, NOT CONNECTING!", this.id);
return; return;
} }
@ -170,8 +171,8 @@ public class DataLogicScannerConnection extends SimplePlcConnection {
} catch (IOException e) { } catch (IOException e) {
handleBrokenConnection( handleBrokenConnection(
"Failed to handle address " + address + " for " + this.address + ":" + this.port + ": " format("Failed to handle address {0} for {1}:{2}: {3}", address, this.address, this.port,
+ getExceptionMessageWithCauses(e), e); getExceptionMessageWithCauses(e)), e);
throw new IllegalStateException( throw new IllegalStateException(
"Failed to handle address " + address + " for " + this.address + ":" + this.port, e); "Failed to handle address " + address + " for " + this.address + ":" + this.port, e);
@ -180,7 +181,7 @@ public class DataLogicScannerConnection extends SimplePlcConnection {
private void read() { private void read() {
logger.info("Reading from DataLogic Scanner at " + this.address + ":" + this.port + "..."); logger.info("Reading from DataLogic Scanner at {}:{}...", this.address, this.port);
while (this.read) { while (this.read) {
try { try {
@ -215,7 +216,7 @@ public class DataLogicScannerConnection extends SimplePlcConnection {
} }
String barcode = sb.toString(); String barcode = sb.toString();
logger.info("Received barcode " + barcode); logger.info("Received barcode {}", barcode);
notify(this.addressBarcode, barcode); notify(this.addressBarcode, barcode);
} }
@ -226,27 +227,27 @@ public class DataLogicScannerConnection extends SimplePlcConnection {
try { try {
sendStopTrigger(); sendStopTrigger();
} catch (IOException ex) { } catch (IOException ex) {
logger.error("Failed to send stop during timeout exception: " + ex.getMessage()); logger.error("Failed to send stop during timeout exception: {}", ex.getMessage());
} }
internalDisconnect(); internalDisconnect();
handleBrokenConnection( handleBrokenConnection(
"Timeout while reading from scanner at " + this.address + ":" + this.port + ": " format("Timeout while reading from scanner at {0}:{1}: {2}", this.address, this.port,
+ getExceptionMessageWithCauses(e), e); getExceptionMessageWithCauses(e)), e);
} else { } else {
logger.warn("Timeout while reading from scanner at " + this.address + ":" + this.port logger.warn("Timeout while reading from scanner at {}:{}. Disconnected.", this.address,
+ ". Disconnected."); this.port);
notify(this.addressBarcode, NO_CONNECTION); notify(this.addressBarcode, NO_CONNECTION);
disconnect(); disconnect();
} }
} else { } else {
notify(this.addressBarcode, NO_CONNECTION); notify(this.addressBarcode, NO_CONNECTION);
internalDisconnect(); internalDisconnect();
handleBrokenConnection("Failed to connect to " + this.address + ":" + this.port + ": " handleBrokenConnection(format("Failed to connect to {0}:{1}: {2}", this.address, this.port,
+ getExceptionMessageWithCauses(e), e); getExceptionMessageWithCauses(e)), e);
} }
} }
} }
logger.info("Stopped reading from " + this.address + ":" + this.port); logger.info("Stopped reading from {}:{}", this.address, this.port);
} }
} }

View File

@ -11,6 +11,6 @@ public class LoggerOutConnection extends SimplePlcConnection {
@Override @Override
public void send(String address, Object value) { public void send(String address, Object value) {
assertConnected(); assertConnected();
logger.info(address + " -> " + value); logger.info("{} -> {}", address, value);
} }
} }

View File

@ -15,11 +15,11 @@ public class RandomStringConnection extends SimplePlcConnection {
@Override @Override
public void send(String address, Object value) { public void send(String address, Object value) {
assertConnected(); assertConnected();
PlcConnection.logger.info("Sending " + address + " => " + value); PlcConnection.logger.info("Sending {} => {}", address, value);
byte[] data = new byte[8]; byte[] data = new byte[8];
new SecureRandom().nextBytes(data); new SecureRandom().nextBytes(data);
String newValue = StringHelper.toHexString(data); String newValue = StringHelper.toHexString(data);
PlcConnection.logger.info("Generated random value " + newValue); PlcConnection.logger.info("Generated random value {}", newValue);
this.plc.syncNotify(address, newValue); this.plc.syncNotify(address, newValue);
} }
} }

View File

@ -19,12 +19,12 @@ public abstract class SimplePlcConnection extends PlcConnection {
@Override @Override
public void initialize(Map<String, Object> parameters) throws Exception { public void initialize(Map<String, Object> parameters) throws Exception {
logger.info("Configured " + getClass().getSimpleName() + " " + this.id); logger.info("Configured {} {}", getClass().getSimpleName(), this.id);
} }
@Override @Override
public boolean connect() { public boolean connect() {
logger.info(this.id + ": Is now connected."); logger.info("{}: Is now connected.", this.id);
if (this.simulated) if (this.simulated)
logger.info("Running SIMULATED"); logger.info("Running SIMULATED");
this.connectionState = ConnectionState.Connected; this.connectionState = ConnectionState.Connected;
@ -35,7 +35,7 @@ public abstract class SimplePlcConnection extends PlcConnection {
@Override @Override
public void disconnect() { public void disconnect() {
logger.info(this.id + ": Is now disconnected."); logger.info("{}: Is now disconnected.", this.id);
this.connectionState = ConnectionState.Disconnected; this.connectionState = ConnectionState.Disconnected;
this.connectionStateMsg = "-"; this.connectionStateMsg = "-";
this.plc.notifyConnectionStateChanged(this); this.plc.notifyConnectionStateChanged(this);

View File

@ -18,7 +18,6 @@ import li.strolch.plc.core.hw.connections.SimplePlcConnection;
public class RaspiBcmGpioInputConnection extends SimplePlcConnection { public class RaspiBcmGpioInputConnection extends SimplePlcConnection {
private boolean verbose; private boolean verbose;
private List<Integer> inputBcmAddresses;
private Map<String, Pin> pinsByAddress; private Map<String, Pin> pinsByAddress;
private Map<GpioPin, String> addressesByPin; private Map<GpioPin, String> addressesByPin;
@ -33,18 +32,16 @@ public class RaspiBcmGpioInputConnection extends SimplePlcConnection {
public void initialize(Map<String, Object> parameters) { public void initialize(Map<String, Object> parameters) {
this.simulated = parameters.containsKey(PARAM_SIMULATED) && (boolean) parameters.get(PARAM_SIMULATED); this.simulated = parameters.containsKey(PARAM_SIMULATED) && (boolean) parameters.get(PARAM_SIMULATED);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked") List<Integer> bcmInputPins = (List<Integer>) parameters.get("bcmInputPins");
List<Integer> bcmInputPins = (List<Integer>) parameters.get("bcmInputPins");
this.inputBcmAddresses = bcmInputPins;
this.pinsByAddress = new HashMap<>(); this.pinsByAddress = new HashMap<>();
for (Integer address : this.inputBcmAddresses) { for (Integer address : bcmInputPins) {
Pin pin = RaspiBcmPin.getPinByAddress(address); Pin pin = RaspiBcmPin.getPinByAddress(address);
if (pin == null) if (pin == null)
throw new IllegalArgumentException("RaspiBcmPin " + address + " does not exist!"); throw new IllegalArgumentException("RaspiBcmPin " + address + " does not exist!");
String key = this.id + "." + address; String key = this.id + "." + address;
this.pinsByAddress.put(key, pin); this.pinsByAddress.put(key, pin);
logger.info("Registered address " + key + " for RaspiBcmPin " + pin); logger.info("Registered address {} for RaspiBcmPin {}", key, pin);
} }
if (parameters.containsKey("pinPullResistance")) { if (parameters.containsKey("pinPullResistance")) {
@ -56,14 +53,14 @@ public class RaspiBcmGpioInputConnection extends SimplePlcConnection {
this.verbose = parameters.containsKey("verbose") && (Boolean) parameters.get("verbose"); this.verbose = parameters.containsKey("verbose") && (Boolean) parameters.get("verbose");
this.inverted = parameters.containsKey("inverted") && (boolean) parameters.get("inverted"); this.inverted = parameters.containsKey("inverted") && (boolean) parameters.get("inverted");
logger.info("Configured Raspi BCM GPIO Input for Pins " + this.inputBcmAddresses.stream().map(Object::toString) logger.info("Configured Raspi BCM GPIO Input for Pins {}",
.collect(joining(", "))); bcmInputPins.stream().map(Object::toString).collect(joining(", ")));
} }
@Override @Override
public boolean connect() { public boolean connect() {
if (this.simulated) { if (this.simulated) {
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!"); logger.warn("{}: Running SIMULATED, NOT CONNECTING!", this.id);
return super.connect(); return super.connect();
} }
@ -82,7 +79,7 @@ public class RaspiBcmGpioInputConnection extends SimplePlcConnection {
inputPin.addListener((GpioPinListenerDigital) this::handleInterrupt); inputPin.addListener((GpioPinListenerDigital) this::handleInterrupt);
this.addressesByPin.put(inputPin, address); this.addressesByPin.put(inputPin, address);
logger.info("Provisioned input pin " + inputPin + " for address " + address); logger.info("Provisioned input pin {} for address {}", inputPin, address);
} }
return super.connect(); return super.connect();
@ -95,19 +92,19 @@ public class RaspiBcmGpioInputConnection extends SimplePlcConnection {
private void handleInterrupt(GpioPinDigitalStateChangeEvent event) { private void handleInterrupt(GpioPinDigitalStateChangeEvent event) {
if (this.verbose) if (this.verbose)
logger.info(event.getPin() + " " + event.getState() + " " + event.getEdge()); logger.info("{} {} {}", event.getPin(), event.getState(), event.getEdge());
String address = this.addressesByPin.get(event.getPin()); String address = this.addressesByPin.get(event.getPin());
PinState state = event.getState(); PinState state = event.getState();
if (this.verbose) if (this.verbose)
logger.info(address + " has new state " + state); logger.info("{} has new state {}", address, state);
notify(address, this.inverted ? state.isLow() : state.isHigh()); notify(address, this.inverted ? state.isLow() : state.isHigh());
} }
@Override @Override
public void disconnect() { public void disconnect() {
if (this.simulated) { if (this.simulated) {
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!"); logger.warn("{}: Running SIMULATED, NOT CONNECTING!", this.id);
super.disconnect(); super.disconnect();
return; return;
} }
@ -119,7 +116,7 @@ public class RaspiBcmGpioInputConnection extends SimplePlcConnection {
} }
this.addressesByPin.clear(); this.addressesByPin.clear();
} catch (Error e) { } catch (Error e) {
logger.error("Failed to disconnect " + this.id, e); logger.error("Failed to disconnect {}", this.id, e);
} }
super.disconnect(); super.disconnect();

View File

@ -16,7 +16,6 @@ import li.strolch.plc.core.hw.connections.SimplePlcConnection;
public class RaspiBcmGpioOutputConnection extends SimplePlcConnection { public class RaspiBcmGpioOutputConnection extends SimplePlcConnection {
private boolean verbose; private boolean verbose;
private List<Integer> outputBcmAddresses;
private Map<String, Pin> pinsByAddress; private Map<String, Pin> pinsByAddress;
private Map<String, GpioPinDigitalOutput> gpioPinsByAddress; private Map<String, GpioPinDigitalOutput> gpioPinsByAddress;
private boolean inverted; private boolean inverted;
@ -29,25 +28,22 @@ public class RaspiBcmGpioOutputConnection extends SimplePlcConnection {
public void initialize(Map<String, Object> parameters) { public void initialize(Map<String, Object> parameters) {
this.simulated = parameters.containsKey(PARAM_SIMULATED) && (boolean) parameters.get(PARAM_SIMULATED); this.simulated = parameters.containsKey(PARAM_SIMULATED) && (boolean) parameters.get(PARAM_SIMULATED);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked") List<Integer> bcmOutputPins = (List<Integer>) parameters.get("bcmOutputPins");
List<Integer> bcmOutputPins = (List<Integer>) parameters.get("bcmOutputPins");
this.outputBcmAddresses = bcmOutputPins;
this.pinsByAddress = new HashMap<>(); this.pinsByAddress = new HashMap<>();
for (Integer address : this.outputBcmAddresses) { for (Integer address : bcmOutputPins) {
Pin pin = RaspiBcmPin.getPinByAddress(address); Pin pin = RaspiBcmPin.getPinByAddress(address);
if (pin == null) if (pin == null)
throw new IllegalArgumentException("RaspiBcmPin " + address + " does not exist!"); throw new IllegalArgumentException("RaspiBcmPin " + address + " does not exist!");
String key = this.id + "." + address; String key = this.id + "." + address;
this.pinsByAddress.put(key, pin); this.pinsByAddress.put(key, pin);
logger.info("Registered address " + key + " for RaspiBcmPin " + pin); logger.info("Registered address {} for RaspiBcmPin {}", key, pin);
} }
this.verbose = parameters.containsKey("verbose") && (Boolean) parameters.get("verbose"); this.verbose = parameters.containsKey("verbose") && (Boolean) parameters.get("verbose");
this.inverted = parameters.containsKey("inverted") && (boolean) parameters.get("inverted"); this.inverted = parameters.containsKey("inverted") && (boolean) parameters.get("inverted");
logger.info( logger.info("Configured Raspi BCM GPIO Output for Pins {}",
"Configured Raspi BCM GPIO Output for Pins " + this.outputBcmAddresses.stream().map(Object::toString) bcmOutputPins.stream().map(Object::toString).collect(joining(", ")));
.collect(joining(", ")));
} }
@Override @Override
@ -65,7 +61,7 @@ public class RaspiBcmGpioOutputConnection extends SimplePlcConnection {
Pin pin = this.pinsByAddress.get(address); Pin pin = this.pinsByAddress.get(address);
GpioPinDigitalOutput outputPin = gpioController.provisionDigitalOutputPin(pin); GpioPinDigitalOutput outputPin = gpioController.provisionDigitalOutputPin(pin);
this.gpioPinsByAddress.put(address, outputPin); this.gpioPinsByAddress.put(address, outputPin);
logger.info("Provisioned output pin " + outputPin + " for address " + address); logger.info("Provisioned output pin {} for address {}", outputPin, address);
} }
return super.connect(); return super.connect();
@ -79,7 +75,7 @@ public class RaspiBcmGpioOutputConnection extends SimplePlcConnection {
@Override @Override
public void disconnect() { public void disconnect() {
if (this.simulated) { if (this.simulated) {
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!"); logger.warn("{}: Running SIMULATED, NOT CONNECTING!", this.id);
super.disconnect(); super.disconnect();
return; return;
} }
@ -91,7 +87,7 @@ public class RaspiBcmGpioOutputConnection extends SimplePlcConnection {
} }
this.gpioPinsByAddress.clear(); this.gpioPinsByAddress.clear();
} catch (Error e) { } catch (Error e) {
logger.error("Failed to disconnect " + this.id, e); logger.error("Failed to disconnect {}", this.id, e);
} }
super.disconnect(); super.disconnect();
@ -100,7 +96,7 @@ public class RaspiBcmGpioOutputConnection extends SimplePlcConnection {
@Override @Override
public void send(String address, Object value) { public void send(String address, Object value) {
if (this.simulated) { if (this.simulated) {
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!"); logger.warn("{}: Running SIMULATED, NOT CONNECTING!", this.id);
return; return;
} }
@ -114,7 +110,7 @@ public class RaspiBcmGpioOutputConnection extends SimplePlcConnection {
PinState newState = high ? PinState.HIGH : PinState.LOW; PinState newState = high ? PinState.HIGH : PinState.LOW;
if (this.verbose) if (this.verbose)
logger.info("Setting pin " + outputPin + " to new state " + newState); logger.info("Setting pin {} to new state {}", outputPin, newState);
outputPin.setState(newState); outputPin.setState(newState);
} }

View File

@ -1,15 +1,15 @@
package li.strolch.plc.core.hw.i2c; package li.strolch.plc.core.hw.i2c;
import static li.strolch.utils.helper.StringHelper.toHexString;
import static li.strolch.utils.helper.StringHelper.toPrettyHexString;
import java.io.IOException;
import com.pi4j.io.i2c.I2CDevice; import com.pi4j.io.i2c.I2CDevice;
import li.strolch.utils.communication.PacketObserver; import li.strolch.utils.communication.PacketObserver;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException;
import static li.strolch.utils.helper.StringHelper.toHexString;
import static li.strolch.utils.helper.StringHelper.toPrettyHexString;
public class LoggingI2cDevice { public class LoggingI2cDevice {
private static final Logger logger = LoggerFactory.getLogger(LoggingI2cDevice.class); private static final Logger logger = LoggerFactory.getLogger(LoggingI2cDevice.class);
@ -45,12 +45,12 @@ public class LoggingI2cDevice {
sleepIfNecessary(); sleepIfNecessary();
if (log) if (log)
logger.info(this.i2cAddressS + ": Writing: " + toHexString(data)); logger.info("{}: Writing: {}", this.i2cAddressS, toHexString(data));
this.i2cDevice.write(data); this.i2cDevice.write(data);
if (this.packetObserver != null) if (this.packetObserver != null)
this.packetObserver.notifySent(new byte[] { data }); this.packetObserver.notifySent(new byte[]{data});
this.lastWriteNanos = System.nanoTime(); this.lastWriteNanos = System.nanoTime();
} }
@ -58,7 +58,7 @@ public class LoggingI2cDevice {
sleepIfNecessary(); sleepIfNecessary();
if (log) if (log)
logger.info(this.i2cAddressS + ": Writing: " + toPrettyHexString(buffer)); logger.info("{}: Writing: {}", this.i2cAddressS, toPrettyHexString(buffer));
this.i2cDevice.write(buffer); this.i2cDevice.write(buffer);
@ -68,7 +68,7 @@ public class LoggingI2cDevice {
} }
public void write(boolean log, int address, byte b) throws IOException, InterruptedException { public void write(boolean log, int address, byte b) throws IOException, InterruptedException {
write(log, new byte[] { (byte) address, b }); write(log, new byte[]{(byte) address, b});
} }
public void write(boolean log, int address, byte[] buffer) throws IOException, InterruptedException { public void write(boolean log, int address, byte[] buffer) throws IOException, InterruptedException {
@ -82,7 +82,7 @@ public class LoggingI2cDevice {
sleepIfNecessary(); sleepIfNecessary();
if (log) if (log)
logger.info(this.i2cAddressS + ": Writing: " + toPrettyHexString(writeBuffer)); logger.info("{}: Writing: {}", this.i2cAddressS, toPrettyHexString(writeBuffer));
int read = this.i2cDevice.read(writeBuffer, 0, writeBuffer.length, readBuffer, 0, readBuffer.length); int read = this.i2cDevice.read(writeBuffer, 0, writeBuffer.length, readBuffer, 0, readBuffer.length);
if (read != readBuffer.length) if (read != readBuffer.length)
@ -95,31 +95,31 @@ public class LoggingI2cDevice {
this.lastWriteNanos = System.nanoTime(); this.lastWriteNanos = System.nanoTime();
if (log) if (log)
logger.info(this.i2cAddressS + ": Read: " + toPrettyHexString(readBuffer)); logger.info("{}: Read: {}", this.i2cAddressS, toPrettyHexString(readBuffer));
} }
public int read(boolean log) throws IOException { public int read(boolean log) throws IOException {
int read = this.i2cDevice.read(); int read = this.i2cDevice.read();
if (log) if (log)
logger.info(this.i2cAddressS + ": Read: " + toHexString((byte) read)); logger.info("{}: Read: {}", this.i2cAddressS, toHexString((byte) read));
if (this.packetObserver != null) if (this.packetObserver != null)
this.packetObserver.notifyReceived(new byte[] { (byte) read }); this.packetObserver.notifyReceived(new byte[]{(byte) read});
return read; return read;
} }
public int read(boolean log, byte address) throws IOException { public int read(boolean log, byte address) throws IOException {
if (log) if (log)
logger.info(this.i2cAddressS + ": Writing: " + toHexString(address)); logger.info("{}: Writing: {}", this.i2cAddressS, toHexString(address));
int read = this.i2cDevice.read(address); int read = this.i2cDevice.read(address);
if (log) if (log)
logger.info(this.i2cAddressS + ": Read: " + toHexString((byte) read)); logger.info("{}: Read: {}", this.i2cAddressS, toHexString((byte) read));
if (this.packetObserver != null) { if (this.packetObserver != null) {
this.packetObserver.notifySent(new byte[] { address }); this.packetObserver.notifySent(new byte[]{address});
this.packetObserver.notifyReceived(new byte[] { (byte) read }); this.packetObserver.notifyReceived(new byte[]{(byte) read});
} }
return read; return read;
@ -127,31 +127,31 @@ public class LoggingI2cDevice {
public void read(boolean log, byte[] buffer) throws IOException { public void read(boolean log, byte[] buffer) throws IOException {
if (log) if (log)
logger.info(this.i2cAddressS + ": Reading: " + buffer.length + " bytes from last set address..."); logger.info("{}: Reading: {} bytes from last set address...", this.i2cAddressS, buffer.length);
int read = this.i2cDevice.read(buffer, 0, buffer.length); int read = this.i2cDevice.read(buffer, 0, buffer.length);
if (read != buffer.length) if (read != buffer.length)
throw new IllegalStateException("Expected to read " + buffer.length + " bytes, but read " + read); throw new IllegalStateException("Expected to read " + buffer.length + " bytes, but read " + read);
if (log) if (log)
logger.info(this.i2cAddressS + ": Read: " + toPrettyHexString(buffer)); logger.info("{}: Read: {}", this.i2cAddressS, toPrettyHexString(buffer));
if (this.packetObserver != null) if (this.packetObserver != null)
this.packetObserver.notifyReceived(new byte[] { (byte) read }); this.packetObserver.notifyReceived(new byte[]{(byte) read});
} }
public void read(boolean log, byte address, byte[] buffer) throws IOException { public void read(boolean log, byte address, byte[] buffer) throws IOException {
if (log) if (log)
logger.info( logger.info("{}: Reading: {} bytes from address {}", this.i2cAddressS, buffer.length,
this.i2cAddressS + ": Reading: " + buffer.length + " bytes from address " + toHexString(address)); toHexString(address));
int read = this.i2cDevice.read(address, buffer, 0, buffer.length); int read = this.i2cDevice.read(address, buffer, 0, buffer.length);
if (read != buffer.length) if (read != buffer.length)
throw new IllegalStateException("Expected to read " + buffer.length + " bytes, but read " + read); throw new IllegalStateException("Expected to read " + buffer.length + " bytes, but read " + read);
if (log) if (log)
logger.info(this.i2cAddressS + ": Read: " + toPrettyHexString(buffer)); logger.info("{}: Read: {}", this.i2cAddressS, toPrettyHexString(buffer));
if (this.packetObserver != null) { if (this.packetObserver != null) {
this.packetObserver.notifySent(new byte[] { address }); this.packetObserver.notifySent(new byte[]{address});
this.packetObserver.notifyReceived(buffer); this.packetObserver.notifyReceived(buffer);
} }
} }
@ -180,6 +180,6 @@ public class LoggingI2cDevice {
this.ioWait = ioWait; this.ioWait = ioWait;
this.ioWaitNanos = ioWaitNanos; this.ioWaitNanos = ioWaitNanos;
logger.info("Using " + ioWait + " ms and " + ioWaitNanos + " ns for write sleep"); logger.info("Using {} ms and {} ns for write sleep", ioWait, ioWaitNanos);
} }
} }

View File

@ -41,7 +41,8 @@ public abstract class Multi8BitI2cOutputConnection extends SimplePlcConnection {
public abstract String getName(); public abstract String getName();
public String getDescription() { public String getDescription() {
return "I2C Output " + getName() + " @ " + byteStream(this.addresses).map(b -> "0x" + toHexString(b)) return "I2C Output " + getName() + " @ " + byteStream(this.addresses)
.map(b -> "0x" + toHexString(b))
.collect(joining(", ")); .collect(joining(", "));
} }
@ -65,12 +66,11 @@ public abstract class Multi8BitI2cOutputConnection extends SimplePlcConnection {
this.inverted = parameters.containsKey("inverted") && (boolean) parameters.get("inverted"); this.inverted = parameters.containsKey("inverted") && (boolean) parameters.get("inverted");
this.reversed = parameters.containsKey("reversed") && (boolean) parameters.get("reversed"); this.reversed = parameters.containsKey("reversed") && (boolean) parameters.get("reversed");
logger.info("inverted: " + this.inverted); logger.info("inverted: {}", this.inverted);
logger.info("reversed: " + this.reversed); logger.info("reversed: {}", this.reversed);
logger.info("nrOfBits: " + this.nrOfBits); logger.info("nrOfBits: {}", this.nrOfBits);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked") List<Integer> addressList = (List<Integer>) parameters.get("addresses");
List<Integer> addressList = (List<Integer>) parameters.get("addresses");
this.addresses = new byte[addressList.size()]; this.addresses = new byte[addressList.size()];
for (int i = 0; i < addressList.size(); i++) { for (int i = 0; i < addressList.size(); i++) {
this.addresses[i] = addressList.get(i).byteValue(); this.addresses[i] = addressList.get(i).byteValue();
@ -79,26 +79,26 @@ public abstract class Multi8BitI2cOutputConnection extends SimplePlcConnection {
Map<String, int[]> positionsByAddress = new HashMap<>(); Map<String, int[]> positionsByAddress = new HashMap<>();
for (int i = 0; i < this.addresses.length; i++) { for (int i = 0; i < this.addresses.length; i++) {
for (int j = 0; j < this.nrOfBits; j++) for (int j = 0; j < this.nrOfBits; j++)
positionsByAddress.put(this.id + "." + i + "." + j, new int[] { i, j }); positionsByAddress.put(this.id + "." + i + "." + j, new int[]{i, j});
} }
this.positionsByAddress = Collections.unmodifiableMap(positionsByAddress); this.positionsByAddress = Collections.unmodifiableMap(positionsByAddress);
logger.info("Configured " + getDescription()); logger.info("Configured {}", getDescription());
} }
@Override @Override
public boolean connect() { public boolean connect() {
if (this.simulated) { if (this.simulated) {
logger.warn(getName() + ": " + this.id + ": Running SIMULATED, NOT CONNECTING!"); logger.warn("{}: {}: Running SIMULATED, NOT CONNECTING!", getName(), this.id);
return super.connect(); return super.connect();
} }
if (isConnected()) { if (isConnected()) {
logger.warn(getName() + ": " + this.id + ": Already connected"); logger.warn("{}: {}: Already connected", getName(), this.id);
return true; return true;
} }
logger.info(getName() + ": " + this.id + ": Connecting..."); logger.info("{}: {}: Connecting...", getName(), this.id);
// initialize // initialize
try { try {
@ -114,7 +114,7 @@ public abstract class Multi8BitI2cOutputConnection extends SimplePlcConnection {
} }
if (setup()) { if (setup()) {
logger.info("Successfully connected " + this.outputDevices.length + " devices as " + getDescription()); logger.info("Successfully connected {} devices as {}", this.outputDevices.length, getDescription());
return super.connect(); return super.connect();
} }
@ -136,7 +136,7 @@ public abstract class Multi8BitI2cOutputConnection extends SimplePlcConnection {
byte address = this.addresses[index]; byte address = this.addresses[index];
I2CDevice outputDevice = devices[index]; I2CDevice outputDevice = devices[index];
ok &= setup(address, index, outputDevice); ok &= setup(address, index, outputDevice);
logger.info("Connected " + getDescription(address)); logger.info("Connected {}", getDescription(address));
} }
if (ok) if (ok)
@ -150,7 +150,7 @@ public abstract class Multi8BitI2cOutputConnection extends SimplePlcConnection {
@Override @Override
public void disconnect() { public void disconnect() {
if (this.simulated) { if (this.simulated) {
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!"); logger.warn("{}: Running SIMULATED, NOT CONNECTING!", this.id);
super.disconnect(); super.disconnect();
return; return;
} }
@ -169,7 +169,7 @@ public abstract class Multi8BitI2cOutputConnection extends SimplePlcConnection {
@Override @Override
public void send(String address, Object value) { public void send(String address, Object value) {
if (this.simulated) { if (this.simulated) {
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!"); logger.warn("{}: Running SIMULATED, NOT CONNECTING!", this.id);
return; return;
} }

View File

@ -1,19 +1,5 @@
package li.strolch.plc.core.hw.i2c; package li.strolch.plc.core.hw.i2c;
import static com.pi4j.wiringpi.Gpio.HIGH;
import static com.pi4j.wiringpi.Gpio.LOW;
import static li.strolch.plc.model.PlcConstants.PARAM_SIMULATED;
import static li.strolch.utils.helper.ByteHelper.asBinary;
import static li.strolch.utils.helper.ByteHelper.isBitSet;
import static li.strolch.utils.helper.ExceptionHelper.getExceptionMessageWithCauses;
import static li.strolch.utils.helper.StringHelper.toHexString;
import static li.strolch.utils.helper.StringHelper.toPrettyHexString;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import com.pi4j.io.gpio.*; import com.pi4j.io.gpio.*;
import com.pi4j.io.gpio.event.GpioPinDigitalStateChangeEvent; import com.pi4j.io.gpio.event.GpioPinDigitalStateChangeEvent;
import com.pi4j.io.gpio.event.GpioPinListenerDigital; import com.pi4j.io.gpio.event.GpioPinListenerDigital;
@ -27,6 +13,21 @@ import li.strolch.plc.core.hw.gpio.PlcGpioController;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import static com.pi4j.wiringpi.Gpio.HIGH;
import static com.pi4j.wiringpi.Gpio.LOW;
import static java.text.MessageFormat.format;
import static li.strolch.plc.model.PlcConstants.PARAM_SIMULATED;
import static li.strolch.utils.helper.ByteHelper.asBinary;
import static li.strolch.utils.helper.ByteHelper.isBitSet;
import static li.strolch.utils.helper.ExceptionHelper.getExceptionMessageWithCauses;
import static li.strolch.utils.helper.StringHelper.toHexString;
import static li.strolch.utils.helper.StringHelper.toPrettyHexString;
public class PCF8574InputConnection extends SimplePlcConnection { public class PCF8574InputConnection extends SimplePlcConnection {
private static final Logger logger = LoggerFactory.getLogger(PCF8574InputConnection.class); private static final Logger logger = LoggerFactory.getLogger(PCF8574InputConnection.class);
@ -71,8 +72,7 @@ public class PCF8574InputConnection extends SimplePlcConnection {
this.i2cBusNr = (int) parameters.get("i2cBus"); this.i2cBusNr = (int) parameters.get("i2cBus");
this.inverted = parameters.containsKey("inverted") && (boolean) parameters.get("inverted"); this.inverted = parameters.containsKey("inverted") && (boolean) parameters.get("inverted");
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked") List<Integer> addressList = (List<Integer>) parameters.get("addresses");
List<Integer> addressList = (List<Integer>) parameters.get("addresses");
this.addresses = new byte[addressList.size()]; this.addresses = new byte[addressList.size()];
for (int i = 0; i < addressList.size(); i++) { for (int i = 0; i < addressList.size(); i++) {
this.addresses[i] = addressList.get(i).byteValue(); this.addresses[i] = addressList.get(i).byteValue();
@ -81,36 +81,35 @@ public class PCF8574InputConnection extends SimplePlcConnection {
Map<String, int[]> positionsByAddress = new HashMap<>(); Map<String, int[]> positionsByAddress = new HashMap<>();
for (int i = 0; i < this.addresses.length; i++) { for (int i = 0; i < this.addresses.length; i++) {
for (int j = 0; j < 8; j++) for (int j = 0; j < 8; j++)
positionsByAddress.put(this.id + "." + i + "." + j, new int[] { i, j }); positionsByAddress.put(this.id + "." + i + "." + j, new int[]{i, j});
} }
this.positionsByAddress = Collections.unmodifiableMap(positionsByAddress); this.positionsByAddress = Collections.unmodifiableMap(positionsByAddress);
this.interruptResistance = PinPullResistance.valueOf((String) parameters.get("interruptPinPullResistance")); this.interruptResistance = PinPullResistance.valueOf((String) parameters.get("interruptPinPullResistance"));
this.interruptBcmPinAddress = (Integer) parameters.get("interruptBcmPinAddress"); this.interruptBcmPinAddress = (Integer) parameters.get("interruptBcmPinAddress");
this.interruptChangeState = PinState.valueOf((String) parameters.get("interruptChangeState")); this.interruptChangeState = PinState.valueOf((String) parameters.get("interruptChangeState"));
this.enableInterruptFix = this.enableInterruptFix = parameters.containsKey("enableInterruptFix") && (Boolean) parameters.get(
parameters.containsKey("enableInterruptFix") && (Boolean) parameters.get("enableInterruptFix"); "enableInterruptFix");
logger.info( logger.info("Configured {} as PCF8574 Input on I2C addresses 0x {} on BCM Pin interrupt trigger {}", this.id,
"Configured " + this.id + " as PCF8574 Input on I2C addresses 0x " + toPrettyHexString(this.addresses) toPrettyHexString(this.addresses), this.interruptBcmPinAddress);
+ " on BCM Pin interrupt trigger " + this.interruptBcmPinAddress);
if (this.verbose) if (this.verbose)
logger.info("Verbose enabled for connection " + this.id); logger.info("Verbose enabled for connection {}", this.id);
} }
@Override @Override
public boolean connect() { public boolean connect() {
if (this.simulated) { if (this.simulated) {
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!"); logger.warn("{}: Running SIMULATED, NOT CONNECTING!", this.id);
return super.connect(); return super.connect();
} }
if (isConnected()) { if (isConnected()) {
logger.warn(this.id + ": Already connected"); logger.warn("{}: Already connected", this.id);
return true; return true;
} }
logger.info(this.id + ": Connecting..."); logger.info("{}: Connecting...", this.id);
// initialize // initialize
try { try {
@ -119,8 +118,8 @@ public class PCF8574InputConnection extends SimplePlcConnection {
this.inputDevices = new I2CDevice[this.addresses.length]; this.inputDevices = new I2CDevice[this.addresses.length];
for (int i = 0; i < this.addresses.length; i++) { for (int i = 0; i < this.addresses.length; i++) {
this.inputDevices[i] = i2cBus.getDevice(this.addresses[i]); this.inputDevices[i] = i2cBus.getDevice(this.addresses[i]);
logger.info("Connected to I2C Device " + this.id + " at 0x" + toHexString(this.addresses[i]) logger.info("Connected to I2C Device {} at 0x{} on I2C Bus {}", this.id, toHexString(this.addresses[i]),
+ " on I2C Bus " + this.i2cBusNr); this.i2cBusNr);
} }
} catch (Throwable e) { } catch (Throwable e) {
@ -132,8 +131,9 @@ public class PCF8574InputConnection extends SimplePlcConnection {
boolean ok = readInitialState(); boolean ok = readInitialState();
if (!ok) { if (!ok) {
handleBrokenConnection("Failed to read initial values from I2C Bus " + this.i2cBusNr + " and addresses 0x " handleBrokenConnection(
+ toPrettyHexString(this.addresses), null); format("Failed to read initial values from I2C Bus {0} and addresses 0x {1}", this.i2cBusNr,
toPrettyHexString(this.addresses)), null);
} }
// register interrupt listener // register interrupt listener
@ -148,15 +148,17 @@ public class PCF8574InputConnection extends SimplePlcConnection {
if (gpioController.getProvisionedPins().stream().map(GpioPin::getPin).anyMatch(interruptPin::equals)) if (gpioController.getProvisionedPins().stream().map(GpioPin::getPin).anyMatch(interruptPin::equals))
throw new IllegalStateException("Pin " + interruptPin + " is already provisioned!"); throw new IllegalStateException("Pin " + interruptPin + " is already provisioned!");
this.interruptGpioPin = gpioController.provisionDigitalInputPin(interruptPin, this.interruptResistance); this.interruptGpioPin = gpioController.provisionDigitalInputPin(interruptPin, this.interruptResistance);
logger.info("Provisioned GPIO Input pin " + this.interruptGpioPin + " with PinPullResistance " logger.info("Provisioned GPIO Input pin {} with PinPullResistance {}", this.interruptGpioPin,
+ this.interruptResistance); this.interruptResistance);
this.interruptGpioPin.removeAllListeners(); this.interruptGpioPin.removeAllListeners();
this.interruptGpioPin.addListener((GpioPinListenerDigital) this::handleInterrupt); this.interruptGpioPin.addListener((GpioPinListenerDigital) this::handleInterrupt);
logger.info("Registered GPIO interrupt handler for BCM " + interruptPin); logger.info("Registered GPIO interrupt handler for BCM {}", interruptPin);
if (this.enableInterruptFix) { if (this.enableInterruptFix) {
this.interruptFixTask = this.plc.getExecutorPool().getScheduledExecutor("InterruptFix") this.interruptFixTask = this.plc
.getExecutorPool()
.getScheduledExecutor("InterruptFix")
.scheduleWithFixedDelay(this::checkInterruptPin, 1, 1, TimeUnit.SECONDS); .scheduleWithFixedDelay(this::checkInterruptPin, 1, 1, TimeUnit.SECONDS);
logger.info("Enabled Interrupt Fix Task."); logger.info("Enabled Interrupt Fix Task.");
} }
@ -164,8 +166,9 @@ public class PCF8574InputConnection extends SimplePlcConnection {
return ok && super.connect(); return ok && super.connect();
} catch (Throwable e) { } catch (Throwable e) {
handleBrokenConnection("Failed to register GPIO listener for BCM pin " + this.interruptBcmPinAddress + ": " handleBrokenConnection(
+ getExceptionMessageWithCauses(e), e); format("Failed to register GPIO listener for BCM pin {0}: {1}", this.interruptBcmPinAddress,
getExceptionMessageWithCauses(e)), e);
return false; return false;
} }
@ -175,7 +178,7 @@ public class PCF8574InputConnection extends SimplePlcConnection {
public void disconnect() { public void disconnect() {
if (this.simulated) { if (this.simulated) {
super.disconnect(); super.disconnect();
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!"); logger.warn("{}: Running SIMULATED, NOT CONNECTING!", this.id);
return; return;
} }
@ -186,9 +189,9 @@ public class PCF8574InputConnection extends SimplePlcConnection {
try { try {
this.interruptGpioPin.removeAllListeners(); this.interruptGpioPin.removeAllListeners();
PlcGpioController.getInstance().unprovisionPin(this.interruptGpioPin); PlcGpioController.getInstance().unprovisionPin(this.interruptGpioPin);
logger.info("Provisioned GPIO Input pin " + this.interruptGpioPin); logger.info("Provisioned GPIO Input pin {}", this.interruptGpioPin);
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to unprovision pin " + this.interruptGpioPin, e); logger.error("Failed to unprovision pin {}", this.interruptGpioPin, e);
} }
} }
@ -207,8 +210,9 @@ public class PCF8574InputConnection extends SimplePlcConnection {
if ((this.interruptChangeState == PinState.HIGH && currentState == HIGH) // if ((this.interruptChangeState == PinState.HIGH && currentState == HIGH) //
|| (this.interruptChangeState == PinState.LOW && currentState == LOW)) { || (this.interruptChangeState == PinState.LOW && currentState == LOW)) {
logger.error("Missed interrupt for pin " + this.interruptGpioPin + " as current state is " + currentState logger.error(
+ " and expected change state is " + this.interruptChangeState + ", forcing update..."); "Missed interrupt for pin {} as current state is {} and expected change state is {}, forcing update...",
this.interruptGpioPin, currentState, this.interruptChangeState);
try { try {
handleNewState("interruptFix"); handleNewState("interruptFix");
@ -217,13 +221,13 @@ public class PCF8574InputConnection extends SimplePlcConnection {
} }
this.interruptFixes++; this.interruptFixes++;
logger.error("Performed " + this.interruptFixes + " interrupt fixes."); logger.error("Performed {} interrupt fixes.", this.interruptFixes);
} }
} }
private void handleInterrupt(GpioPinDigitalStateChangeEvent event) { private void handleInterrupt(GpioPinDigitalStateChangeEvent event) {
if (this.verbose) if (this.verbose)
logger.info(event.getPin() + " " + event.getState() + " " + event.getEdge()); logger.info("{} {} {}", event.getPin(), event.getState(), event.getEdge());
try { try {
if (event.getState() == this.interruptChangeState) if (event.getState() == this.interruptChangeState)
@ -238,16 +242,15 @@ public class PCF8574InputConnection extends SimplePlcConnection {
for (int i = 0; i < this.inputDevices.length; i++) { for (int i = 0; i < this.inputDevices.length; i++) {
I2CDevice i2CDevice = this.inputDevices[i]; I2CDevice i2CDevice = this.inputDevices[i];
if (i2CDevice == null) { if (i2CDevice == null) {
logger.warn("Ignoring invalid I2C Device 0x" + toHexString(this.addresses[i]) + " " + ctx); logger.warn("Ignoring invalid I2C Device 0x{} {}", toHexString(this.addresses[i]), ctx);
continue; continue;
} }
byte data = (byte) i2CDevice.read(); byte data = (byte) i2CDevice.read();
if (this.verbose) if (this.verbose)
logger.info( logger.info("{} at 0x{} has new state {} {}", this.id, toHexString((byte) i2CDevice.getAddress()),
this.id + " at 0x" + toHexString((byte) i2CDevice.getAddress()) + " has new state " + asBinary( asBinary(data), ctx);
data) + " " + ctx);
for (int j = 0; j < 8; j++) { for (int j = 0; j < 8; j++) {
boolean newState = isBitSet(data, j); boolean newState = isBitSet(data, j);
@ -257,9 +260,8 @@ public class PCF8574InputConnection extends SimplePlcConnection {
if (this.states[i][j] != newState) { if (this.states[i][j] != newState) {
this.states[i][j] = newState; this.states[i][j] = newState;
String address = this.id + "." + i + "." + j; String address = this.id + "." + i + "." + j;
logger.info("Detected " + address + " = " + (newState ? 1 : 0) + (this.inverted ? logger.info("Detected {} = {}{}{} {}", address, newState ? 1 : 0,
" (inverted) " : this.inverted ? " (inverted) " : " (normal) ", asBinary(data), ctx);
" (normal) ") + asBinary(data) + " " + ctx);
this.plc.queueNotify(address, newState); this.plc.queueNotify(address, newState);
} }
} }
@ -278,9 +280,8 @@ public class PCF8574InputConnection extends SimplePlcConnection {
I2CDevice i2CDevice = this.inputDevices[i]; I2CDevice i2CDevice = this.inputDevices[i];
try { try {
byte data = (byte) i2CDevice.read(); byte data = (byte) i2CDevice.read();
logger.info( logger.info("Initial Value for {} at 0x{} is {}", this.id, toHexString(this.addresses[i]),
"Initial Value for " + this.id + " at 0x" + toHexString(this.addresses[i]) + " is " + asBinary( asBinary(data));
data));
this.states[i] = new boolean[8]; this.states[i] = new boolean[8];
for (int j = 0; j < 8; j++) { for (int j = 0; j < 8; j++) {
@ -295,8 +296,8 @@ public class PCF8574InputConnection extends SimplePlcConnection {
} catch (Exception e) { } catch (Exception e) {
ok = false; ok = false;
this.inputDevices[i] = null; this.inputDevices[i] = null;
logger.error("Failed to read initial state for " + this.id + " at 0x" + toHexString( logger.error("Failed to read initial state for {} at 0x{}", this.id,
(byte) i2CDevice.getAddress()), e); toHexString((byte) i2CDevice.getAddress()), e);
} }
} }

View File

@ -40,17 +40,17 @@ public class PCF8574OutputConnection extends Multi8BitI2cOutputConnection {
this.states[index] = (byte) 0xff; this.states[index] = (byte) 0xff;
try { try {
i2cDev.write(this.states[index]); i2cDev.write(this.states[index]);
logger.info(this.id + ": set initial value to " + asBinary((byte) 0xff) + " for " + getDescription( logger.info("{}: set initial value to {} for {}", this.id, asBinary((byte) 0xff),
address)); getDescription(address));
} catch (Exception e) { } catch (Exception e) {
ok = false; ok = false;
logger.error(this.id + ": Failed to set initial value to " + asBinary((byte) 0xff) + " for " logger.error("{}: Failed to set initial value to {} for {}", this.id, asBinary((byte) 0xff),
+ getDescription(address), e); getDescription(address), e);
} }
} else { } else {
this.states[index] = (byte) i2cDev.read(); this.states[index] = (byte) i2cDev.read();
logger.info( logger.info("{}: Initial value is {} for {}", this.id, asBinary(this.states[index]),
this.id + ": Initial value is " + asBinary(this.states[index]) + " for " + getDescription(address)); getDescription(address));
} }
return ok; return ok;
@ -64,8 +64,7 @@ public class PCF8574OutputConnection extends Multi8BitI2cOutputConnection {
else else
newState = setBit(this.states[device], pin); newState = setBit(this.states[device], pin);
logger.info("Setting " + this.id + "." + device + "." + pin + " = " + (high ? 0 : 1) + " (" + asBinary(newState) logger.info("Setting {}.{}.{} = {} ({})", this.id, device, pin, high ? 0 : 1, asBinary(newState));
+ ")");
outputDevice.write(newState); outputDevice.write(newState);
this.states[device] = newState; this.states[device] = newState;

View File

@ -1,13 +1,5 @@
package li.strolch.plc.core.hw.i2c; package li.strolch.plc.core.hw.i2c;
import static li.strolch.plc.model.PlcConstants.PARAM_SIMULATED;
import static li.strolch.utils.helper.ExceptionHelper.getExceptionMessageWithCauses;
import static li.strolch.utils.helper.StringHelper.toHexString;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.Callable;
import com.pi4j.io.i2c.I2CFactory; import com.pi4j.io.i2c.I2CFactory;
import com.pi4j.io.i2c.impl.I2CBusImpl; import com.pi4j.io.i2c.impl.I2CBusImpl;
import li.strolch.plc.core.hw.Plc; import li.strolch.plc.core.hw.Plc;
@ -15,6 +7,15 @@ import li.strolch.plc.core.hw.connections.SimplePlcConnection;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.Callable;
import static java.text.MessageFormat.format;
import static li.strolch.plc.model.PlcConstants.PARAM_SIMULATED;
import static li.strolch.utils.helper.ExceptionHelper.getExceptionMessageWithCauses;
import static li.strolch.utils.helper.StringHelper.toHexString;
public class RSL366OverHorterI2c extends SimplePlcConnection { public class RSL366OverHorterI2c extends SimplePlcConnection {
// https://www.horter.de/doku/i2c-hs-433MHz_Beschreibung.pdf // https://www.horter.de/doku/i2c-hs-433MHz_Beschreibung.pdf
@ -84,11 +85,11 @@ public class RSL366OverHorterI2c extends SimplePlcConnection {
Map<String, byte[]> positionsByAddress = new HashMap<>(); Map<String, byte[]> positionsByAddress = new HashMap<>();
for (byte i = 1; i < 5; i++) { for (byte i = 1; i < 5; i++) {
for (byte j = 1; j < 5; j++) for (byte j = 1; j < 5; j++)
positionsByAddress.put(this.id + "." + i + "." + j, new byte[] { i, j }); positionsByAddress.put(this.id + "." + i + "." + j, new byte[]{i, j});
} }
this.positionsByAddress = Collections.unmodifiableMap(positionsByAddress); this.positionsByAddress = Collections.unmodifiableMap(positionsByAddress);
logger.info("Configured RSL366 over Horter I2c on address 0x" + toHexString(this.address)); logger.info("Configured RSL366 over Horter I2c on address 0x{}", toHexString(this.address));
} }
public <T> T runBusLockedDeviceAction(final Callable<T> action) throws IOException { public <T> T runBusLockedDeviceAction(final Callable<T> action) throws IOException {
@ -98,16 +99,16 @@ public class RSL366OverHorterI2c extends SimplePlcConnection {
@Override @Override
public synchronized boolean connect() { public synchronized boolean connect() {
if (this.simulated) { if (this.simulated) {
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!"); logger.warn("{}: Running SIMULATED, NOT CONNECTING!", this.id);
return super.connect(); return super.connect();
} }
if (isConnected()) { if (isConnected()) {
logger.warn(this.id + ": Already connected"); logger.warn("{}: Already connected", this.id);
return true; return true;
} }
logger.info(this.id + ": Connecting..."); logger.info("{}: Connecting...", this.id);
try { try {
if (this.i2cBus == null) { if (this.i2cBus == null) {
@ -119,18 +120,18 @@ public class RSL366OverHorterI2c extends SimplePlcConnection {
byte[] status = runBusLockedDeviceAction(this::configure); byte[] status = runBusLockedDeviceAction(this::configure);
String version = status[ADDR_INFO_VER_MAJOR] + "." + status[ADDR_INFO_VER_MINOR]; String version = status[ADDR_INFO_VER_MAJOR] + "." + status[ADDR_INFO_VER_MINOR];
logger.info("Connected to 433MHz RSL366 over HorterI2C version " + version + " supporting " logger.info("Connected to 433MHz RSL366 over HorterI2C version {} supporting {} protocols", version,
+ status[ADDR_INFO_NR_OF_KNOWN_PROTOCOLS] + " protocols"); status[ADDR_INFO_NR_OF_KNOWN_PROTOCOLS]);
logger.info("Connected to I2C device at address 0x" + toHexString(this.address) + " on I2C Bus " logger.info("Connected to I2C device at address 0x{} on I2C Bus {}", toHexString(this.address),
+ this.i2cBusNr); this.i2cBusNr);
return super.connect(); return super.connect();
} catch (Throwable e) { } catch (Throwable e) {
handleBrokenConnection( handleBrokenConnection(
"Failed to connect to 433MHz RSL366 over HorterI2C at address 0x" + toHexString(this.address) format("Failed to connect to 433MHz RSL366 over HorterI2C at address 0x{0} on I2C Bus {1}: {2}",
+ " on I2C Bus " + this.i2cBusNr + ": " + getExceptionMessageWithCauses(e), e); toHexString(this.address), this.i2cBusNr, getExceptionMessageWithCauses(e)), e);
return false; return false;
} }
@ -144,7 +145,7 @@ public class RSL366OverHorterI2c extends SimplePlcConnection {
@Override @Override
public synchronized void send(String address, Object value) { public synchronized void send(String address, Object value) {
if (this.simulated) { if (this.simulated) {
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!"); logger.warn("{}: Running SIMULATED, NOT CONNECTING!", this.id);
return; return;
} }
@ -165,8 +166,8 @@ public class RSL366OverHorterI2c extends SimplePlcConnection {
} catch (Exception e) { } catch (Exception e) {
if (e instanceof IllegalStateException) if (e instanceof IllegalStateException)
throw (IllegalStateException) e; throw (IllegalStateException) e;
String msg = "Failed to send " + (on ? "on" : "off") + " to system " + system + " device " + device String msg = format("Failed to send {0} to system {1} device {2} at address 0x{3} on I2C Bus {4}",
+ " at address 0x" + toHexString(this.address) + " on I2C Bus " + this.i2cBusNr; on ? "on" : "off", system, device, toHexString(this.address), this.i2cBusNr);
handleBrokenConnection(msg + ": " + getExceptionMessageWithCauses(e), e); handleBrokenConnection(msg + ": " + getExceptionMessageWithCauses(e), e);
throw new IllegalStateException(msg, e); throw new IllegalStateException(msg, e);
} }
@ -175,7 +176,7 @@ public class RSL366OverHorterI2c extends SimplePlcConnection {
private byte[] configure() throws IOException, InterruptedException { private byte[] configure() throws IOException, InterruptedException {
logger.info("Configuring..."); logger.info("Configuring...");
byte[] data = { CONF_PROTOCOL, repeats }; byte[] data = {CONF_PROTOCOL, repeats};
this.dev.write(this.verbose, ADDR_REG_CONF_CODE, data); this.dev.write(this.verbose, ADDR_REG_CONF_CODE, data);
Thread.sleep(20L); Thread.sleep(20L);
@ -191,14 +192,14 @@ public class RSL366OverHorterI2c extends SimplePlcConnection {
if (status[ADDR_INFO_REPEATS] != repeats) if (status[ADDR_INFO_REPEATS] != repeats)
throw new IllegalStateException("Repeats could not bet set to " + repeats); throw new IllegalStateException("Repeats could not bet set to " + repeats);
logger.info("Configured with protocol " + CONF_PROTOCOL + " and " + repeats + " repeats."); logger.info("Configured with protocol " + CONF_PROTOCOL + " and {} repeats.", repeats);
return status; return status;
} }
private void setState(byte system, byte device, boolean state) throws Exception { private void setState(byte system, byte device, boolean state) throws Exception {
logger.info("System: " + toHexString(system)); logger.info("System: {}", toHexString(system));
logger.info("Device: " + toHexString(device)); logger.info("Device: {}", toHexString(device));
byte[] status = readInfo(false); byte[] status = readInfo(false);
if (isDeviceTransmitting(status)) { if (isDeviceTransmitting(status)) {
@ -233,8 +234,7 @@ public class RSL366OverHorterI2c extends SimplePlcConnection {
} }
showInfoRegister(status); showInfoRegister(status);
logger.info("Successfully sent state change to " + (state ? "on" : "off") + " for device " + system + ", " logger.info("Successfully sent state change to {} for device {}, {}", state ? "on" : "off", system, device);
+ device);
} }
private void waitForDeviceIdle() throws Exception { private void waitForDeviceIdle() throws Exception {
@ -269,12 +269,12 @@ public class RSL366OverHorterI2c extends SimplePlcConnection {
} }
private static void showInfoRegister(byte[] status) { private static void showInfoRegister(byte[] status) {
logger.info(" Pointer : " + toHexString(status[ADDR_INFO_PTR])); logger.info(" Pointer : {}", toHexString(status[ADDR_INFO_PTR]));
logger.info(" Status : " + toHexString(status[ADDR_INFO_STATUS]) + " " + parseStatus( logger.info(" Status : {} {}", toHexString(status[ADDR_INFO_STATUS]),
status[ADDR_INFO_STATUS])); parseStatus(status[ADDR_INFO_STATUS]));
logger.info(" TX : " + toHexString(status[ADDR_INFO_TRANSMITTING])); logger.info(" TX : {}", toHexString(status[ADDR_INFO_TRANSMITTING]));
logger.info(" Protocol : " + toHexString(status[ADDR_INFO_PROTOCOL])); logger.info(" Protocol : {}", toHexString(status[ADDR_INFO_PROTOCOL]));
logger.info(" Repeats : " + toHexString(status[ADDR_INFO_REPEATS])); logger.info(" Repeats : {}", toHexString(status[ADDR_INFO_REPEATS]));
} }
private static boolean isDeviceTransmitting(byte[] status) { private static boolean isDeviceTransmitting(byte[] status) {

View File

@ -1,13 +1,13 @@
package li.strolch.plc.core.hw.i2c; package li.strolch.plc.core.hw.i2c;
import static li.strolch.utils.helper.ByteHelper.*;
import static li.strolch.utils.helper.StringHelper.toHexString;
import java.io.IOException;
import com.pi4j.io.i2c.I2CDevice; import com.pi4j.io.i2c.I2CDevice;
import li.strolch.plc.core.hw.Plc; import li.strolch.plc.core.hw.Plc;
import java.io.IOException;
import static li.strolch.utils.helper.ByteHelper.*;
import static li.strolch.utils.helper.StringHelper.toHexString;
public class TCA9534OutputConnection extends Multi8BitI2cOutputConnection { public class TCA9534OutputConnection extends Multi8BitI2cOutputConnection {
private static final byte TCA9534_REG_ADDR_OUT_PORT = 0x01; private static final byte TCA9534_REG_ADDR_OUT_PORT = 0x01;
@ -44,8 +44,8 @@ public class TCA9534OutputConnection extends Multi8BitI2cOutputConnection {
"Failed to read configuration from address 0x" + toHexString(TCA9534_REG_ADDR_CFG)); "Failed to read configuration from address 0x" + toHexString(TCA9534_REG_ADDR_CFG));
if (config != 0x00) { if (config != 0x00) {
logger.warn(getDescription(address) + " is not configured as OUTPUT, setting register 0x" + toHexString( logger.warn("{} is not configured as OUTPUT, setting register 0x{} to 0x00", getDescription(address),
TCA9534_REG_ADDR_CFG) + " to 0x00"); toHexString(TCA9534_REG_ADDR_CFG));
i2cDev.write(TCA9534_REG_ADDR_OUT_PORT, (byte) 0x00); i2cDev.write(TCA9534_REG_ADDR_OUT_PORT, (byte) 0x00);
i2cDev.write(TCA9534_REG_ADDR_CFG, (byte) 0x00); i2cDev.write(TCA9534_REG_ADDR_CFG, (byte) 0x00);
} }
@ -56,11 +56,10 @@ public class TCA9534OutputConnection extends Multi8BitI2cOutputConnection {
this.states[index] = (byte) 0x00; this.states[index] = (byte) 0x00;
try { try {
i2cDev.write(TCA9534_REG_ADDR_OUT_PORT, this.states[index]); i2cDev.write(TCA9534_REG_ADDR_OUT_PORT, this.states[index]);
logger.info("Set initial value to " + asBinary((byte) 0x00) + " for " + getDescription(address)); logger.info("Set initial value to {} for {}", asBinary((byte) 0x00), getDescription(address));
} catch (Exception e) { } catch (Exception e) {
ok = false; ok = false;
logger.error( logger.error("Failed to set initial value to {} for {}", asBinary((byte) 0x00), getDescription(address),
"Failed to set initial value to " + asBinary((byte) 0x00) + " for " + getDescription(address),
e); e);
} }
} else { } else {
@ -70,7 +69,7 @@ public class TCA9534OutputConnection extends Multi8BitI2cOutputConnection {
currentState = reverse(currentState); currentState = reverse(currentState);
this.states[index] = currentState; this.states[index] = currentState;
logger.info("Initial value is " + asBinary(this.states[index]) + " for " + getDescription(address)); logger.info("Initial value is {} for {}", asBinary(this.states[index]), getDescription(address));
} }
return ok; return ok;
@ -87,8 +86,8 @@ public class TCA9534OutputConnection extends Multi8BitI2cOutputConnection {
byte writeState = this.reversed ? reverse(newState) : newState; byte writeState = this.reversed ? reverse(newState) : newState;
if (this.verbose) if (this.verbose)
logger.info("Setting " + getDescription((byte) outputDevice.getAddress()) + " to new state " + asBinary( logger.info("Setting {} to new state {}", getDescription((byte) outputDevice.getAddress()),
writeState)); asBinary(writeState));
outputDevice.write(TCA9534_REG_ADDR_OUT_PORT, writeState); outputDevice.write(TCA9534_REG_ADDR_OUT_PORT, writeState);
this.states[device] = newState; this.states[device] = newState;

View File

@ -36,10 +36,10 @@ public class SendPlcAddressActionService extends AbstractService<JsonServiceArgu
if (jsonObject.has(PARAM_VALUE)) { if (jsonObject.has(PARAM_VALUE)) {
String valueS = jsonObject.get(PARAM_VALUE).getAsString(); String valueS = jsonObject.get(PARAM_VALUE).getAsString();
Object value = plcAddress.valueType.parseValue(valueS); Object value = plcAddress.valueType.parseValue(valueS);
logger.info("PLC Send " + resource + "-" + action + " with " + valueS); logger.info("PLC Send {}-{} with {}", resource, action, valueS);
plcHandler.send(resource, action, value); plcHandler.send(resource, action, value);
} else { } else {
logger.info("PLC Send " + resource + "-" + action + " with default value " + plcAddress.defaultValue); logger.info("PLC Send {}-{} with default value {}", resource, action, plcAddress.defaultValue);
plcHandler.send(resource, action); plcHandler.send(resource, action);
} }
} else if (addressType == PlcAddressType.Notification) { } else if (addressType == PlcAddressType.Notification) {
@ -49,7 +49,7 @@ public class SendPlcAddressActionService extends AbstractService<JsonServiceArgu
String valueS = jsonObject.get(PARAM_VALUE).getAsString(); String valueS = jsonObject.get(PARAM_VALUE).getAsString();
Object value = plcAddress.valueType.parseValue(valueS); Object value = plcAddress.valueType.parseValue(valueS);
logger.info("PLC Notification " + resource + "-" + action + " with " + valueS); logger.info("PLC Notification {}-{} with {}", resource, action, valueS);
plcHandler.notify(resource, action, value); plcHandler.notify(resource, action, value);
} else { } else {

View File

@ -30,24 +30,23 @@ public class SetPlcStateService extends AbstractService<StringMapArgument, Servi
PlcServiceInitializer plcServiceInitializer = getComponent(PlcServiceInitializer.class); PlcServiceInitializer plcServiceInitializer = getComponent(PlcServiceInitializer.class);
switch (newState) { switch (newState) {
case Stopped: case Stopped -> {
if (plcHandler.getPlcState() == PlcState.Stopped) if (plcHandler.getPlcState() == PlcState.Stopped)
return ServiceResult.error("Already stopped"); return ServiceResult.error("Already stopped");
plcServiceInitializer.stop(); plcServiceInitializer.stop();
plcHandler.stopPlc(); plcHandler.stopPlc();
break; }
case Started: case Started -> {
if (plcHandler.getPlcState() == PlcState.Started) if (plcHandler.getPlcState() == PlcState.Started)
return ServiceResult.error("Already started"); return ServiceResult.error("Already started");
plcHandler.startPlc(); plcHandler.startPlc();
plcServiceInitializer.start(); plcServiceInitializer.start();
break; }
case Configured: case Configured -> {
if (!plcHandler.reconfigurePlc()) if (!plcHandler.reconfigurePlc())
return ServiceResult.error(plcHandler.getPlcStateMsg()); return ServiceResult.error(plcHandler.getPlcStateMsg());
break; }
default: default -> throw new IllegalArgumentException("Can not switch to state " + newState);
throw new IllegalArgumentException("Can not switch to state " + newState);
} }
return ServiceResult.success(); return ServiceResult.success();

View File

@ -30,9 +30,9 @@ public class ExamplePlcConveyorPlcService extends PlcService {
private AtomicBoolean conveyor3On; private AtomicBoolean conveyor3On;
private AtomicBoolean conveyor4On; private AtomicBoolean conveyor4On;
private AtomicBoolean conveyor1WaitingForTransfer; private final AtomicBoolean conveyor1WaitingForTransfer;
private AtomicBoolean conveyor2WaitingForTransfer; private final AtomicBoolean conveyor2WaitingForTransfer;
private AtomicBoolean conveyor3WaitingForTransfer; private final AtomicBoolean conveyor3WaitingForTransfer;
public ExamplePlcConveyorPlcService(PlcHandler plcHandler) { public ExamplePlcConveyorPlcService(PlcHandler plcHandler) {
super(plcHandler); super(plcHandler);
@ -50,62 +50,54 @@ public class ExamplePlcConveyorPlcService extends PlcService {
boolean state = (boolean) value; boolean state = (boolean) value;
switch (resource) { switch (resource) {
case R_CONVEYOR_01 -> {
case R_CONVEYOR_01: if (action.equals(A_OCCUPIED)) {
conveyor1Occupied.set(state);
if (action.equals(A_OCCUPIED)) { handleTransfer(null, R_CONVEYOR_01, R_CONVEYOR_02, //
conveyor1Occupied.set(state); null, conveyor1Occupied, conveyor2Occupied, //
handleTransfer(null, R_CONVEYOR_01, R_CONVEYOR_02, // null, conveyor1On, conveyor2On, //
null, conveyor1Occupied, conveyor2Occupied, // null, conveyor1WaitingForTransfer);
null, conveyor1On, conveyor2On, // } else {
null, conveyor1WaitingForTransfer); logger.error("Unhandled action {}-{}", resource, action);
} else { }
logger.error("Unhandled action " + resource + "-" + action);
} }
case R_CONVEYOR_02 -> {
break; if (action.equals(A_OCCUPIED)) {
conveyor2Occupied.set(state);
case R_CONVEYOR_02: handleTransfer(R_CONVEYOR_01, R_CONVEYOR_02, R_CONVEYOR_03, //
conveyor1Occupied, conveyor2Occupied, conveyor3Occupied, //
if (action.equals(A_OCCUPIED)) { conveyor1On, conveyor2On, conveyor3On, //
conveyor2Occupied.set(state); conveyor1WaitingForTransfer, conveyor2WaitingForTransfer);
handleTransfer(R_CONVEYOR_01, R_CONVEYOR_02, R_CONVEYOR_03, // } else {
conveyor1Occupied, conveyor2Occupied, conveyor3Occupied, // logger.error("Unhandled action {}-{}", resource, action);
conveyor1On, conveyor2On, conveyor3On, // }
conveyor1WaitingForTransfer, conveyor2WaitingForTransfer);
} else {
logger.error("Unhandled action " + resource + "-" + action);
} }
case R_CONVEYOR_03 -> {
break; if (action.equals(A_OCCUPIED)) {
conveyor3Occupied.set(state);
case R_CONVEYOR_03: handleTransfer(R_CONVEYOR_02, R_CONVEYOR_03, R_CONVEYOR_04, //
conveyor2Occupied, conveyor3Occupied, conveyor4Occupied, //
if (action.equals(A_OCCUPIED)) { conveyor2On, conveyor3On, conveyor4On, //
conveyor3Occupied.set(state); conveyor2WaitingForTransfer, conveyor3WaitingForTransfer);
handleTransfer(R_CONVEYOR_02, R_CONVEYOR_03, R_CONVEYOR_04, // } else {
conveyor2Occupied, conveyor3Occupied, conveyor4Occupied, // logger.error("Unhandled action {}-{}", resource, action);
conveyor2On, conveyor3On, conveyor4On, // }
conveyor2WaitingForTransfer, conveyor3WaitingForTransfer);
} else {
logger.error("Unhandled action " + resource + "-" + action);
} }
case R_CONVEYOR_04 -> {
break; if (action.equals(A_OCCUPIED)) {
conveyor4Occupied.set(state);
case R_CONVEYOR_04: handleTransfer(R_CONVEYOR_03, R_CONVEYOR_04, null, //
conveyor3Occupied, conveyor4Occupied, null, //
if (action.equals(A_OCCUPIED)) { conveyor3On, conveyor4On, null, //
conveyor4Occupied.set(state); conveyor3WaitingForTransfer, null);
handleTransfer(R_CONVEYOR_03, R_CONVEYOR_04, null, // } else {
conveyor3Occupied, conveyor4Occupied, null, // logger.error("Unhandled action {}-{}", resource, action);
conveyor3On, conveyor4On, null, // }
conveyor3WaitingForTransfer, null);
} else {
logger.error("Unhandled action " + resource + "-" + action);
} }
break;
} }
} }
@ -119,37 +111,37 @@ public class ExamplePlcConveyorPlcService extends PlcService {
// handle current conveyor is now occupied // handle current conveyor is now occupied
if (next == null) { if (next == null) {
if (currentOn.get()) { if (currentOn.get()) {
logger.info(current + " is now occupied without a next conveyor, stopping conveyor"); logger.info("{} is now occupied without a next conveyor, stopping conveyor", current);
send(current, A_MOTOR_OFF); send(current, A_MOTOR_OFF);
currentOn.set(false); currentOn.set(false);
} else { } else {
logger.info(current + " is now occupied, conveyor is off and no next conveyor: transfer complete."); logger.info("{} is now occupied, conveyor is off and no next conveyor: transfer complete.",
current);
} }
return; return;
} }
if (nextOccupied.get()) { if (nextOccupied.get()) {
logger.info(current + " is now occupied, next conveyor " + next + " is still occupied, so waiting..."); logger.info("{} is now occupied, next conveyor {} is still occupied, so waiting...", current, next);
if (currentWaitingForTransfer.get()) if (currentWaitingForTransfer.get())
logger.error("What the hell, current " + current + " is already waiting for a transfer!"); logger.error("What the hell, current {} is already waiting for a transfer!", current);
currentWaitingForTransfer.set(true); currentWaitingForTransfer.set(true);
} else { } else {
logger.info( logger.info("{} is now occupied, next conveyor {} is not occupied, so transferring...", current, next);
current + " is now occupied, next conveyor " + next + " is not occupied, so transferring...");
if (nextOn.get()) { if (nextOn.get()) {
logger.info("Next conveyor " + next + " is already running, waiting for transfer to complete..."); logger.info("Next conveyor {} is already running, waiting for transfer to complete...", next);
} else { } else {
logger.info("Starting " + next + " and waiting for transfer to complete..."); logger.info("Starting {} and waiting for transfer to complete...", next);
send(next, A_MOTOR_ON); send(next, A_MOTOR_ON);
nextOn.set(true); nextOn.set(true);
} }
if (currentOn.get()) { if (currentOn.get()) {
logger.info(current + " is already running, waiting for transfer to complete..."); logger.info("{} is already running, waiting for transfer to complete...", current);
} else { } else {
logger.info("Starting " + current + " and waiting for transfer to complete..."); logger.info("Starting {} and waiting for transfer to complete...", current);
send(current, A_MOTOR_ON); send(current, A_MOTOR_ON);
currentOn.set(true); currentOn.set(true);
} }
@ -165,11 +157,11 @@ public class ExamplePlcConveyorPlcService extends PlcService {
// no previous conveyor, so just stop current, if still on // no previous conveyor, so just stop current, if still on
if (currentOn.get()) { if (currentOn.get()) {
logger.info(current + " is now unoccupied, stopping conveyor"); logger.info("{} is now unoccupied, stopping conveyor", current);
send(current, A_MOTOR_OFF); send(current, A_MOTOR_OFF);
currentOn.set(false); currentOn.set(false);
} else { } else {
logger.info(current + " is now unoccupied, conveyor is already off"); logger.info("{} is now unoccupied, conveyor is already off", current);
} }
return; return;
@ -178,16 +170,18 @@ public class ExamplePlcConveyorPlcService extends PlcService {
// handle transfer of previous to current // handle transfer of previous to current
if (!previousOccupied.get()) { if (!previousOccupied.get()) {
logger.info(previous + " is not occupied, so no transfer required."); logger.info("{} is not occupied, so no transfer required.", previous);
if (currentOn.get()) { if (currentOn.get()) {
logger.info(current + " is now unoccupied and previous " + previous logger.info(
+ " is not occupied, so no transfer required: Stopping conveyor"); "{} is now unoccupied and previous {} is not occupied, so no transfer required: Stopping conveyor",
current, previous);
send(current, A_MOTOR_OFF); send(current, A_MOTOR_OFF);
currentOn.set(false); currentOn.set(false);
} else { } else {
logger.info(current + " is now unoccupied and previous " + previous logger.info(
+ " is not occupied, and conveyor not running. Nothing else to do"); "{} is now unoccupied and previous {} is not occupied, and conveyor not running. Nothing else to do",
current, previous);
} }
return; return;
@ -195,23 +189,23 @@ public class ExamplePlcConveyorPlcService extends PlcService {
// previous is occupied, so transfer to current, but only if previous was waiting // previous is occupied, so transfer to current, but only if previous was waiting
if (!previousWaitingForTransfer.get()) { if (!previousWaitingForTransfer.get()) {
logger.info(previous + " conveyor is not waiting for a transfer. Nothing else to do."); logger.info("{} conveyor is not waiting for a transfer. Nothing else to do.", previous);
} else { } else {
logger.info(previous + " conveyor is waiting for a transfer, so starting transfer"); logger.info("{} conveyor is waiting for a transfer, so starting transfer", previous);
if (currentOn.get()) { if (currentOn.get()) {
logger.info(current + " is already on, waiting for transfer..."); logger.info("{} is already on, waiting for transfer...", current);
} else { } else {
logger.info("Turning " + current + " on for transfer"); logger.info("Turning {} on for transfer", current);
send(current, A_MOTOR_ON); send(current, A_MOTOR_ON);
currentOn.set(true); currentOn.set(true);
} }
if (previousOn.get()) { if (previousOn.get()) {
logger.info(previous + " is already on, waiting for transfer..."); logger.info("{} is already on, waiting for transfer...", previous);
} else { } else {
logger.info("Turning " + previous + " on for transfer"); logger.info("Turning {} on for transfer", previous);
send(previous, A_MOTOR_ON); send(previous, A_MOTOR_ON);
previousOn.set(true); previousOn.set(true);
} }

View File

@ -90,11 +90,11 @@ public class PlcHandlerTest {
assertFalse(value.get()); assertFalse(value.get());
plcHandler.register("PLC", "Running", (address, v) -> { plcHandler.register("PLC", "Running", (address, v) -> {
logger.error("Setting " + address + " to " + v); logger.error("Setting {} to {}", address, v);
value.set((Boolean) v); value.set((Boolean) v);
}); });
plcHandler.register("PLC", "NotRunning", (address, v) -> { plcHandler.register("PLC", "NotRunning", (address, v) -> {
logger.error("Setting " + address + " to " + v); logger.error("Setting {} to {}", address, v);
value.set((Boolean) v); value.set((Boolean) v);
}); });

View File

@ -35,7 +35,7 @@ public class DataLogicScannerConnectionTest {
logger.info("Connecting to scanner..."); logger.info("Connecting to scanner...");
scanner.connect(); scanner.connect();
logger.info("Connected to scanner " + scanner.getId()); logger.info("Connected to scanner {}", scanner.getId());
logger.info("Sending trigger..."); logger.info("Sending trigger...");
scanner.send("test.trigger", true); scanner.send("test.trigger", true);
logger.info("Trigger sent."); logger.info("Trigger sent.");

View File

@ -1,15 +1,14 @@
package li.strolch.plc.core.hw.i2c; package li.strolch.plc.core.hw.i2c;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.text.MessageFormat;
import com.pi4j.io.i2c.I2CBus; import com.pi4j.io.i2c.I2CBus;
import com.pi4j.io.i2c.I2CDevice; import com.pi4j.io.i2c.I2CDevice;
import com.pi4j.io.i2c.I2CFactory; import com.pi4j.io.i2c.I2CFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/** /**
* <p>Compile:</p> * <p>Compile:</p>
* <code>javac -cp pi4j-core-1.4-SNAPSHOT.jar:. RSL366OverHorterI2cTest.java</code> * <code>javac -cp pi4j-core-1.4-SNAPSHOT.jar:. RSL366OverHorterI2cTest.java</code>
@ -51,7 +50,7 @@ public class RSL366OverHorterI2cTest {
static final byte STATUS_BAD_PTR = 0x08; static final byte STATUS_BAD_PTR = 0x08;
static final byte STATUS_CONF_TOO_MUCH_DATA = 0x09; static final byte STATUS_CONF_TOO_MUCH_DATA = 0x09;
static byte[] systemValues = new byte[] { 0, 0, 0, 0 }; static byte[] systemValues = new byte[]{0, 0, 0, 0};
static byte system; static byte system;
static byte device; static byte device;
@ -70,8 +69,11 @@ public class RSL366OverHorterI2cTest {
byte[] status = configure(); byte[] status = configure();
String version = status[ADDR_INFO_VER_MAJOR] + "." + status[ADDR_INFO_VER_MINOR]; String version = status[ADDR_INFO_VER_MAJOR] + "." + status[ADDR_INFO_VER_MINOR];
System.out.println("Connected to Horter I2C to 433MHz version " + version + " supporting " System.out.println("Connected to Horter I2C to 433MHz version "
+ status[ADDR_INFO_NR_OF_KNOWN_PROTOCOLS] + " 433MHz protocols"); + version
+ " supporting "
+ status[ADDR_INFO_NR_OF_KNOWN_PROTOCOLS]
+ " 433MHz protocols");
System.out.println(); System.out.println();
readCodes(input); readCodes(input);
@ -86,21 +88,11 @@ public class RSL366OverHorterI2cTest {
String action = input.readLine(); String action = input.readLine();
switch (action) { switch (action) {
case "o": case "o" -> setState(system, device, true);
setState(system, device, true); case "f" -> setState(system, device, false);
break; case "c" -> configure();
case "f": case "e" -> readCodes(input);
setState(system, device, false); case "x" -> run = false;
break;
case "c":
configure();
break;
case "e":
readCodes(input);
break;
case "x":
run = false;
break;
} }
} catch (Exception e) { } catch (Exception e) {
@ -116,7 +108,7 @@ public class RSL366OverHorterI2cTest {
byte protocol = 2; byte protocol = 2;
byte repeats = 1; byte repeats = 1;
System.out.println("Configuring..."); System.out.println("Configuring...");
byte[] data = { protocol, repeats }; byte[] data = {protocol, repeats};
System.out.println("=> " + toHexString(ADDR_REG_CONF_CODE) + " " + toHexString(data)); System.out.println("=> " + toHexString(ADDR_REG_CONF_CODE) + " " + toHexString(data));
dev.write(ADDR_REG_CONF_CODE, data); dev.write(ADDR_REG_CONF_CODE, data);
Thread.sleep(50L); Thread.sleep(50L);
@ -164,14 +156,19 @@ public class RSL366OverHorterI2cTest {
throw new IllegalStateException( throw new IllegalStateException(
"DeviceCode is invalid after sending deviceCode: " + parseStatus(status[ADDR_INFO_STATUS])); "DeviceCode is invalid after sending deviceCode: " + parseStatus(status[ADDR_INFO_STATUS]));
if (!isDeviceTransmitting(status)) if (!isDeviceTransmitting(status))
throw new IllegalStateException( throw new IllegalStateException("Device is not transmitting after sending "
"Device is not transmitting after sending " + toHexString(system) + "." + toHexString(value) + toHexString(system)
+ "..."); + "."
+ toHexString(value)
+ "...");
showInfoRegister(status); showInfoRegister(status);
System.out.println( System.out.println("Successfully sent state change to "
"Successfully sent state change to " + (state ? "on" : "off") + " for device " + system + ", " + (state ? "on" : "off")
+ device); + " for device "
+ system
+ ", "
+ device);
} }
private static void waitForDeviceIdle() throws Exception { private static void waitForDeviceIdle() throws Exception {
@ -229,30 +226,19 @@ public class RSL366OverHorterI2cTest {
} }
private static String parseStatus(byte status) { private static String parseStatus(byte status) {
switch (status) { return switch (status) {
case STATUS_OK: case STATUS_OK -> "OK";
return "OK"; case STATUS_SYS_TOO_MUCH_DATA -> "Too much SystemCode data";
case STATUS_SYS_TOO_MUCH_DATA: case STATUS_SYS_MISSING_DATA -> "SystemCode missing data";
return "Too much SystemCode data"; case STATUS_SYS_INVALID_DATA -> "Invalid SystemCode";
case STATUS_SYS_MISSING_DATA: case STATUS_SYS_MISSING -> "SystemCode Missing";
return "SystemCode missing data"; case STATUS_DEV_TOO_MUCH_DATA -> "Too much device data";
case STATUS_SYS_INVALID_DATA: case STATUS_DEV_INVALID_DATA -> "DeviceCode invalid";
return "Invalid SystemCode"; case STATUS_PROTO_UNKNOWN -> "Invalid protocol";
case STATUS_SYS_MISSING: case STATUS_BAD_PTR -> "Bad pointer";
return "SystemCode Missing"; case STATUS_CONF_TOO_MUCH_DATA -> "Too much config data";
case STATUS_DEV_TOO_MUCH_DATA: default -> "Unknown status " + toHexString(status);
return "Too much device data"; };
case STATUS_DEV_INVALID_DATA:
return "DeviceCode invalid";
case STATUS_PROTO_UNKNOWN:
return "Invalid protocol";
case STATUS_BAD_PTR:
return "Bad pointer";
case STATUS_CONF_TOO_MUCH_DATA:
return "Too much config data";
default:
return "Unknown status " + toHexString(status);
}
} }
private static void readCodes(BufferedReader input) { private static void readCodes(BufferedReader input) {
@ -288,44 +274,25 @@ public class RSL366OverHorterI2cTest {
} }
public static String toHexString(byte[] raw, int offset, int length) throws RuntimeException { public static String toHexString(byte[] raw, int offset, int length) throws RuntimeException {
try { byte[] hex = new byte[2 * length];
byte[] hex = new byte[2 * length]; int index = 0;
int index = 0;
int pos = offset; int pos = offset;
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
byte b = raw[pos]; byte b = raw[pos];
int v = b & 0xFF; int v = b & 0xFF;
hex[index++] = HEX_CHAR_TABLE[v >>> 4]; hex[index++] = HEX_CHAR_TABLE[v >>> 4];
hex[index++] = HEX_CHAR_TABLE[v & 0xF]; hex[index++] = HEX_CHAR_TABLE[v & 0xF];
pos++; pos++;
}
return new String(hex, "ASCII");
} catch (UnsupportedEncodingException e) {
String msg = MessageFormat
.format("Something went wrong while converting to HEX: {0}", e.getMessage());
throw new RuntimeException(msg, e);
} }
return new String(hex, StandardCharsets.US_ASCII);
} }
private static final byte[] HEX_CHAR_TABLE = { (byte) '0', private static final byte[] HEX_CHAR_TABLE = {(byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4',
(byte) '1', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd',
(byte) '2', (byte) 'e', (byte) 'f'};
(byte) '3',
(byte) '4',
(byte) '5',
(byte) '6',
(byte) '7',
(byte) '8',
(byte) '9',
(byte) 'a',
(byte) 'b',
(byte) 'c',
(byte) 'd',
(byte) 'e',
(byte) 'f' };
public static String asBinary(byte b) { public static String asBinary(byte b) {

View File

@ -1,33 +1,14 @@
package li.strolch.plc.gw.client; package li.strolch.plc.gw.client;
import static java.net.NetworkInterface.getByInetAddress;
import static li.strolch.model.Tags.Json.*;
import static li.strolch.plc.core.DefaultPlcHandler.SILENT_THRESHOLD;
import static li.strolch.plc.model.ModelHelper.valueToJson;
import static li.strolch.plc.model.PlcConstants.*;
import static li.strolch.runtime.StrolchConstants.DEFAULT_REALM;
import static li.strolch.utils.helper.ExceptionHelper.*;
import static li.strolch.utils.helper.NetworkHelper.formatMacAddress;
import static li.strolch.utils.helper.StringHelper.isEmpty;
import java.io.IOException;
import java.net.SocketException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import jakarta.websocket.*; import jakarta.websocket.*;
import jakarta.websocket.CloseReason.CloseCodes; import jakarta.websocket.CloseReason.CloseCodes;
import li.strolch.agent.api.*; import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.StrolchComponent;
import li.strolch.agent.api.StrolchRealm;
import li.strolch.agent.api.VersionQueryResult;
import li.strolch.model.Locator; import li.strolch.model.Locator;
import li.strolch.model.Resource; import li.strolch.model.Resource;
import li.strolch.model.log.LogMessage; import li.strolch.model.log.LogMessage;
@ -42,6 +23,29 @@ import li.strolch.utils.CheckedRunnable;
import li.strolch.utils.helper.NetworkHelper; import li.strolch.utils.helper.NetworkHelper;
import org.glassfish.tyrus.client.ClientManager; import org.glassfish.tyrus.client.ClientManager;
import java.io.IOException;
import java.net.SocketException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import static java.net.NetworkInterface.getByInetAddress;
import static li.strolch.model.Tags.Json.*;
import static li.strolch.plc.core.DefaultPlcHandler.SILENT_THRESHOLD;
import static li.strolch.plc.model.ModelHelper.valueToJson;
import static li.strolch.plc.model.PlcConstants.*;
import static li.strolch.runtime.StrolchConstants.DEFAULT_REALM;
import static li.strolch.utils.helper.ExceptionHelper.*;
import static li.strolch.utils.helper.NetworkHelper.formatMacAddress;
import static li.strolch.utils.helper.StringHelper.isEmpty;
public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcListener { public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcListener {
public static final String PLC = "PLC"; public static final String PLC = "PLC";
@ -142,7 +146,7 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
try { try {
this.gwSession.close(new CloseReason(CloseCodes.GOING_AWAY, "Shutting down")); this.gwSession.close(new CloseReason(CloseCodes.GOING_AWAY, "Shutting down"));
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to close server session: " + e.getMessage()); logger.error("Failed to close server session: {}", e.getMessage());
} }
} }
@ -166,7 +170,7 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
private void connectToServer() { private void connectToServer() {
// connect to Server // connect to Server
logger.info("Connecting to Server at " + this.gwServerUrl + "..."); logger.info("Connecting to Server at {}...", this.gwServerUrl);
try { try {
this.gwClient = ClientManager.createClient(); this.gwClient = ClientManager.createClient();
this.gwSession = this.gwClient.connectToServer(new PlcGwClientEndpoint(this), new URI(this.gwServerUrl)); this.gwSession = this.gwClient.connectToServer(new PlcGwClientEndpoint(this), new URI(this.gwServerUrl));
@ -178,20 +182,23 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
} }
if (rootCause.getMessage() != null && rootCause.getMessage().contains("Connection refused")) { if (rootCause.getMessage() != null && rootCause.getMessage().contains("Connection refused")) {
logger.error( logger.error("Connection refused to connect to server. Will try to connect again in "
"Connection refused to connect to server. Will try to connect again in " + RETRY_DELAY + "s: " + + RETRY_DELAY
getExceptionMessageWithCauses(e)); + "s: {}", getExceptionMessageWithCauses(e));
} else if (rootCause.getMessage() != null && } else if (rootCause.getMessage() != null && rootCause
rootCause.getMessage().contains("Response code was not 101: 404.")) { .getMessage()
logger.error("Connection failed with 404 error code. Is URL " + this.gwServerUrl + " correct?"); .contains("Response code was not 101: 404.")) {
logger.error("Connection failed with 404 error code. Is URL {} correct?", this.gwServerUrl);
logger.error("Server not yet ready with 404 error. Will try again in " + RETRY_DELAY + "s"); logger.error("Server not yet ready with 404 error. Will try again in " + RETRY_DELAY + "s");
} else { } else {
logger.error("Failed to connect to server! Will try to connect again in " + RETRY_DELAY + "s", e); logger.error("Failed to connect to server! Will try to connect again in " + RETRY_DELAY + "s", e);
} }
closeBrokenGwSessionUpdateState("Failed to connect to server", closeBrokenGwSessionUpdateState("Failed to connect to server",
"Connection refused to connect to server. Will try to connect again in " + RETRY_DELAY + "s: " + "Connection refused to connect to server. Will try to connect again in "
getExceptionMessageWithCauses(e)); + RETRY_DELAY
+ "s: "
+ getExceptionMessageWithCauses(e));
delayConnect(RETRY_DELAY, TimeUnit.SECONDS); delayConnect(RETRY_DELAY, TimeUnit.SECONDS);
return; return;
} }
@ -225,7 +232,7 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
return; return;
} }
logger.info(this.gwSession.getId() + ": Connected to Server."); logger.info("{}: Connected to Server.", this.gwSession.getId());
// schedule the heart beat timer // schedule the heart beat timer
if (this.serverConnectFuture != null) if (this.serverConnectFuture != null)
@ -250,7 +257,7 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
} }
private void closeGwSession(String msg) { private void closeGwSession(String msg) {
logger.info("Closing GW session: " + msg); logger.info("Closing GW session: {}", msg);
this.authenticated = false; this.authenticated = false;
if (this.serverConnectFuture != null) if (this.serverConnectFuture != null)
@ -260,7 +267,7 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
try { try {
this.gwSession.close(new CloseReason(CloseCodes.UNEXPECTED_CONDITION, msg)); this.gwSession.close(new CloseReason(CloseCodes.UNEXPECTED_CONDITION, msg));
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to close server session due to " + e.getMessage()); logger.error("Failed to close server session due to {}", e.getMessage());
} }
} }
@ -284,7 +291,7 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
private boolean tryPingServer() { private boolean tryPingServer() {
try { try {
logger.info(this.gwSession.getId() + ": Pinging Server..."); logger.info("{}: Pinging Server...", this.gwSession.getId());
this.gwSession.getBasicRemote().sendPong(ByteBuffer.wrap(this.plcId.getBytes())); this.gwSession.getBasicRemote().sendPong(ByteBuffer.wrap(this.plcId.getBytes()));
long lastUpdate = System.currentTimeMillis() - this.lastSystemStateNotification; long lastUpdate = System.currentTimeMillis() - this.lastSystemStateNotification;
@ -304,7 +311,7 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
return false; return false;
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to send Ping to Server, closing server session due to: " + getExceptionMessage(e)); logger.error("Failed to send Ping to Server, closing server session due to: {}", getExceptionMessage(e));
return true; return true;
} }
} }
@ -326,7 +333,7 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
sendDataToClient(messageJ); sendDataToClient(messageJ);
if (this.verbose) if (this.verbose)
logger.info("Sent msg " + message.getLocator() + " to server"); logger.info("Sent msg {} to server", message.getLocator());
}); });
} }
@ -342,7 +349,7 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
sendDataToClient(messageJ); sendDataToClient(messageJ);
if (this.verbose) if (this.verbose)
logger.info("Sent msg " + locator + " to server"); logger.info("Sent disable msg {} to server", locator);
}); });
} }
@ -362,7 +369,7 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
sendDataToClient(notificationJ); sendDataToClient(notificationJ);
if (this.verbose) if (this.verbose)
logger.info("Sent notification for " + plcAddress.toKey() + " to server"); logger.info("Sent notification for {} to server", plcAddress.toKey());
}); });
} }
@ -377,14 +384,11 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
try { try {
runAsAgent(ctx -> { runAsAgent(ctx -> {
if (MSG_TYPE_AUTHENTICATION.equals(messageType)) { switch (messageType) {
handleAuthResponse(ctx, jsonObject); case MSG_TYPE_AUTHENTICATION -> handleAuthResponse(ctx, jsonObject);
} else if (MSG_TYPE_PLC_TELEGRAM.equals(messageType)) { case MSG_TYPE_PLC_TELEGRAM -> async(() -> handleTelegram(jsonObject));
async(() -> handleTelegram(jsonObject)); case MSG_TYPE_PLC_GET_ADDRESS_STATE -> async(() -> handleGetAddressState(ctx, jsonObject));
} else if (MSG_TYPE_PLC_GET_ADDRESS_STATE.equals(messageType)) { case null, default -> logger.error("Unhandled message type {}", messageType);
async(() -> handleGetAddressState(ctx, jsonObject));
} else {
logger.error("Unhandled message type " + messageType);
} }
}); });
} catch (Exception e) { } catch (Exception e) {
@ -406,7 +410,7 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
telegramJ.addProperty(PARAM_STATE, PlcResponseState.Done.name()); telegramJ.addProperty(PARAM_STATE, PlcResponseState.Done.name());
telegramJ.addProperty(PARAM_STATE_MSG, ""); telegramJ.addProperty(PARAM_STATE_MSG, "");
logger.info("Sent address state for " + plcAddress.toKey() + " = " + value + " to server"); logger.info("Sent address state for {} = {} to server", plcAddress.toKey(), value);
} catch (Exception e) { } catch (Exception e) {
handleFailedTelegram(telegramJ, plcAddress, e); handleFailedTelegram(telegramJ, plcAddress, e);
@ -439,21 +443,28 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
sendDataToClient(telegramJ); sendDataToClient(telegramJ);
if (this.verbose) if (this.verbose)
logger.info("Sent Telegram response for " + (plcAddress == null ? "unknown" : plcAddress.toKey()) + logger.info("Sent Telegram response for {} to server", plcAddress == null ? "unknown" : plcAddress.toKey());
" to server");
} }
private void handleAuthResponse(PrivilegeContext ctx, JsonObject response) { private void handleAuthResponse(PrivilegeContext ctx, JsonObject response) {
if (!response.has(PARAM_STATE) || !response.has(PARAM_STATE_MSG) || !response.has(PARAM_AUTH_TOKEN)) { if (!response.has(PARAM_STATE) || !response.has(PARAM_STATE_MSG) || !response.has(PARAM_AUTH_TOKEN)) {
closeBrokenGwSessionUpdateState(ctx, "Auth failed!", closeBrokenGwSessionUpdateState(ctx, "Auth failed!", "Failed to authenticated with Server: At least one of "
"Failed to authenticated with Server: At least one of " + PARAM_STATE + ", " + PARAM_STATE_MSG + + PARAM_STATE
", " + PARAM_AUTH_TOKEN + " params is missing on Auth Response"); + ", "
+ PARAM_STATE_MSG
+ ", "
+ PARAM_AUTH_TOKEN
+ " params is missing on Auth Response");
throw new IllegalStateException( throw new IllegalStateException("Failed to authenticated with Server: At least one of "
"Failed to authenticated with Server: At least one of " + PARAM_STATE + ", " + PARAM_STATE_MSG + + PARAM_STATE
", " + PARAM_AUTH_TOKEN + " params is missing on Auth Response"); + ", "
+ PARAM_STATE_MSG
+ ", "
+ PARAM_AUTH_TOKEN
+ " params is missing on Auth Response");
} }
if (PlcResponseState.valueOf(response.get(PARAM_STATE).getAsString()) != PlcResponseState.Sent) { if (PlcResponseState.valueOf(response.get(PARAM_STATE).getAsString()) != PlcResponseState.Sent) {
@ -468,7 +479,7 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
"Missing auth token on AUTH response!"); "Missing auth token on AUTH response!");
throw new IllegalStateException("Missing auth token on AUTH response!"); throw new IllegalStateException("Missing auth token on AUTH response!");
} }
logger.info(this.gwSession.getId() + ": Successfully authenticated with Server!"); logger.info("{}: Successfully authenticated with Server!", this.gwSession.getId());
saveServerConnectionState(ctx, ConnectionState.Connected, ""); saveServerConnectionState(ctx, ConnectionState.Connected, "");
notifyPlcConnectionState(ConnectionState.Connected); notifyPlcConnectionState(ConnectionState.Connected);
@ -483,29 +494,29 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
} }
public void onWsPong(PongMessage message, Session session) { public void onWsPong(PongMessage message, Session session) {
logger.info(session.getId() + ": Received pong " + message.toString()); logger.info("{}: Received pong {}", session.getId(), message.toString());
} }
public void onWsOpen(Session session) { public void onWsOpen(Session session) {
logger.info(session.getId() + ": New Session"); logger.info("{}: New Session", session.getId());
} }
public void onWsClose(Session session, CloseReason closeReason) { public void onWsClose(Session session, CloseReason closeReason) {
this.authenticated = false; this.authenticated = false;
logger.info("Session closed with ID " + session.getId() + " due to " + closeReason.getCloseCode() + " " + logger.info("Session closed with ID {} due to {} {}. Reconnecting in " + RETRY_DELAY + "s.", session.getId(),
closeReason.getReasonPhrase() + ". Reconnecting in " + RETRY_DELAY + "s."); closeReason.getCloseCode(), closeReason.getReasonPhrase());
if (this.gwSession != null) { if (this.gwSession != null) {
closeBrokenGwSessionUpdateState(closeReason.getReasonPhrase(), closeBrokenGwSessionUpdateState(closeReason.getReasonPhrase(),
"Session closed with ID " + session.getId() + " due to " + closeReason.getCloseCode() + " " + MessageFormat.format("Session closed with ID {0} due to {1} {2}. Reconnecting in {3}s.",
closeReason.getReasonPhrase() + ". Reconnecting in " + RETRY_DELAY + "s."); session.getId(), closeReason.getCloseCode(), closeReason.getReasonPhrase(), RETRY_DELAY));
} }
delayConnect(RETRY_DELAY, TimeUnit.SECONDS); delayConnect(RETRY_DELAY, TimeUnit.SECONDS);
} }
public void onWsError(Session session, Throwable throwable) { public void onWsError(Session session, Throwable throwable) {
logger.error(session.getId() + ": Received error: " + throwable.getMessage(), throwable); logger.error("{}: Received error: {}", session.getId(), throwable.getMessage(), throwable);
} }
@SuppressWarnings("SynchronizeOnNonFinalField") @SuppressWarnings("SynchronizeOnNonFinalField")
@ -561,7 +572,8 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
private void saveServerConnectionState(PrivilegeContext ctx, ConnectionState state, String stateMsg) { private void saveServerConnectionState(PrivilegeContext ctx, ConnectionState state, String stateMsg) {
StrolchRealm realm = getContainer().getRealm(ctx.getCertificate()); StrolchRealm realm = getContainer().getRealm(ctx.getCertificate());
try (StrolchTransaction tx = realm.openTx(ctx.getCertificate(), "saveServerConnectionState", false) try (StrolchTransaction tx = realm
.openTx(ctx.getCertificate(), "saveServerConnectionState", false)
.silentThreshold(SILENT_THRESHOLD, TimeUnit.MILLISECONDS)) { .silentThreshold(SILENT_THRESHOLD, TimeUnit.MILLISECONDS)) {
Resource plc = tx.getResourceBy(TYPE_PLC, this.plcId, true); Resource plc = tx.getResourceBy(TYPE_PLC, this.plcId, true);
@ -582,24 +594,26 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
VersionQueryResult versionQueryResult = getContainer().getAgent().getVersion(); VersionQueryResult versionQueryResult = getContainer().getAgent().getVersion();
this.versions.add(AGENT_VERSION, versionQueryResult.getAgentVersion().toJson(true)); this.versions.add(AGENT_VERSION, versionQueryResult.getAgentVersion().toJson(true));
this.versions.add(APP_VERSION, versionQueryResult.getAppVersion().toJson(true)); this.versions.add(APP_VERSION, versionQueryResult.getAppVersion().toJson(true));
this.versions.add(COMPONENT_VERSIONS, this.versions.add(COMPONENT_VERSIONS, versionQueryResult
versionQueryResult.getComponentVersions().stream().map(v -> v.toJson(true)) .getComponentVersions()
.collect(JsonArray::new, JsonArray::add, JsonArray::addAll)); .stream()
.map(v -> v.toJson(true))
.collect(JsonArray::new, JsonArray::add, JsonArray::addAll));
} }
return this.versions; return this.versions;
} }
public JsonArray getIpAddresses() { public JsonArray getIpAddresses() {
if (this.ipAddresses == null || this.ipAddresses.isEmpty() || if (this.ipAddresses == null || this.ipAddresses.isEmpty() || (
(System.currentTimeMillis() - this.ipAddressesUpdateTime > 10000L)) { System.currentTimeMillis() - this.ipAddressesUpdateTime > 10000L)) {
try { try {
this.ipAddresses = NetworkHelper.findInet4Addresses().stream().map(add -> { this.ipAddresses = NetworkHelper.findInet4Addresses().stream().map(add -> {
String mac; String mac;
try { try {
mac = formatMacAddress(getByInetAddress(add).getHardwareAddress()); mac = formatMacAddress(getByInetAddress(add).getHardwareAddress());
} catch (SocketException e) { } catch (SocketException e) {
logger.error("Failed to get HW address for " + add.getHostAddress(), e); logger.error("Failed to get HW address for {}", add.getHostAddress(), e);
mac = "(unknown)"; mac = "(unknown)";
} }
@ -645,12 +659,12 @@ public class PlcGwClientHandler extends StrolchComponent implements GlobalPlcLis
private static void handleFailedTelegram(JsonObject telegramJ, PlcAddress plcAddress, Exception e) { private static void handleFailedTelegram(JsonObject telegramJ, PlcAddress plcAddress, Exception e) {
if (plcAddress == null) { if (plcAddress == null) {
logger.error("Failed to handle telegram: " + telegramJ, e); logger.error("Failed to handle telegram: {}", telegramJ, e);
telegramJ.addProperty(PARAM_STATE, PlcResponseState.Failed.name()); telegramJ.addProperty(PARAM_STATE, PlcResponseState.Failed.name());
telegramJ.addProperty(PARAM_STATE_MSG, telegramJ.addProperty(PARAM_STATE_MSG,
"Could not evaluate PlcAddress: " + getExceptionMessage(getRootCause(e), false)); "Could not evaluate PlcAddress: " + getExceptionMessage(getRootCause(e), false));
} else { } else {
logger.error("Failed to execute telegram: " + plcAddress.toKeyAddress(), e); logger.error("Failed to execute telegram: {}", plcAddress.toKeyAddress(), e);
telegramJ.addProperty(PARAM_STATE, PlcResponseState.Failed.name()); telegramJ.addProperty(PARAM_STATE, PlcResponseState.Failed.name());
telegramJ.addProperty(PARAM_STATE_MSG, telegramJ.addProperty(PARAM_STATE_MSG,
"Failed to perform " + plcAddress.toKey() + ": " + getExceptionMessage(getRootCause(e), false)); "Failed to perform " + plcAddress.toKey() + ": " + getExceptionMessage(getRootCause(e), false));

View File

@ -1,20 +1,5 @@
package li.strolch.plc.gw.server; package li.strolch.plc.gw.server;
import static java.util.stream.Collectors.toSet;
import static li.strolch.plc.model.ModelHelper.jsonToValue;
import static li.strolch.plc.model.ModelHelper.valueToJson;
import static li.strolch.plc.model.PlcConstants.*;
import static li.strolch.utils.collections.SynchronizedCollections.synchronizedMapOfLists;
import static li.strolch.utils.helper.ExceptionHelper.getExceptionMessageWithCauses;
import static li.strolch.websocket.WebSocketRemoteIp.get;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive; import com.google.gson.JsonPrimitive;
@ -34,13 +19,29 @@ import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.model.Certificate; import li.strolch.privilege.model.Certificate;
import li.strolch.privilege.model.Usage; import li.strolch.privilege.model.Usage;
import li.strolch.privilege.model.UserRep; import li.strolch.privilege.model.UserRep;
import li.strolch.runtime.sessions.StrolchSessionHandler;
import li.strolch.runtime.configuration.ComponentConfiguration; import li.strolch.runtime.configuration.ComponentConfiguration;
import li.strolch.runtime.privilege.PrivilegedRunnable; import li.strolch.runtime.privilege.PrivilegedRunnable;
import li.strolch.runtime.privilege.PrivilegedRunnableWithResult; import li.strolch.runtime.privilege.PrivilegedRunnableWithResult;
import li.strolch.runtime.sessions.StrolchSessionHandler;
import li.strolch.utils.collections.MapOfLists; import li.strolch.utils.collections.MapOfLists;
import li.strolch.utils.dbc.DBC; import li.strolch.utils.dbc.DBC;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import static java.text.MessageFormat.format;
import static java.util.stream.Collectors.toSet;
import static li.strolch.plc.model.ModelHelper.jsonToValue;
import static li.strolch.plc.model.ModelHelper.valueToJson;
import static li.strolch.plc.model.PlcConstants.*;
import static li.strolch.utils.collections.SynchronizedCollections.synchronizedMapOfLists;
import static li.strolch.utils.helper.ExceptionHelper.getExceptionMessageWithCauses;
import static li.strolch.websocket.WebSocketRemoteIp.get;
public class PlcGwServerHandler extends StrolchComponent { public class PlcGwServerHandler extends StrolchComponent {
public static final String MSG_DISCONNECTED_TIMED_OUT = "Disconnected / Timed out"; public static final String MSG_DISCONNECTED_TIMED_OUT = "Disconnected / Timed out";
@ -77,7 +78,8 @@ public class PlcGwServerHandler extends StrolchComponent {
this.runAsUser = configuration.getString("runAsUser", "plc-server"); this.runAsUser = configuration.getString("runAsUser", "plc-server");
this.realm = getContainer().getRealmNames().iterator().next(); this.realm = getContainer().getRealmNames().iterator().next();
this.plcIds = runAsAgentWithResult(ctx -> getContainer().getPrivilegeHandler() this.plcIds = runAsAgentWithResult(ctx -> getContainer()
.getPrivilegeHandler()
.getPrivilegeHandler() .getPrivilegeHandler()
.getUsers(ctx.getCertificate()) .getUsers(ctx.getCertificate())
.stream() // .stream() //
@ -96,7 +98,8 @@ public class PlcGwServerHandler extends StrolchComponent {
@Override @Override
public void start() throws Exception { public void start() throws Exception {
this.clearDeadConnectionsTask = getAgent().getScheduledExecutor(getName()) this.clearDeadConnectionsTask = getAgent()
.getScheduledExecutor(getName())
.scheduleWithFixedDelay(this::clearDeadConnections, 10, 10, TimeUnit.SECONDS); .scheduleWithFixedDelay(this::clearDeadConnections, 10, 10, TimeUnit.SECONDS);
super.start(); super.start();
} }
@ -128,18 +131,14 @@ public class PlcGwServerHandler extends StrolchComponent {
public void register(String plcId, PlcAddressKey addressKey, PlcNotificationListener listener) { public void register(String plcId, PlcAddressKey addressKey, PlcNotificationListener listener) {
DBC.PRE.assertNotNull("addressKey must not be null", addressKey); DBC.PRE.assertNotNull("addressKey must not be null", addressKey);
DBC.PRE.assertNotEmpty("plcId must not be empty", plcId); DBC.PRE.assertNotEmpty("plcId must not be empty", plcId);
MapOfLists<PlcAddressKey, PlcNotificationListener> plcListeners = this.plcAddressListenersByPlcId.get(plcId); MapOfLists<PlcAddressKey, PlcNotificationListener> plcListeners
if (plcListeners == null) { = this.plcAddressListenersByPlcId.computeIfAbsent(plcId, k -> new MapOfLists<>());
plcListeners = new MapOfLists<>();
this.plcAddressListenersByPlcId.put(plcId, plcListeners);
}
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (plcListeners) { synchronized (plcListeners) {
plcListeners.addElement(addressKey, listener); plcListeners.addElement(addressKey, listener);
} }
logger.info("Registered listener on plc " + plcId + " key " + addressKey + ": " + listener); logger.info("Registered listener on plc {} key {}: {}", plcId, addressKey, listener);
} }
public void unregister(String plcId, PlcAddressKey addressKey, PlcNotificationListener listener) { public void unregister(String plcId, PlcAddressKey addressKey, PlcNotificationListener listener) {
@ -149,12 +148,11 @@ public class PlcGwServerHandler extends StrolchComponent {
if (plcListeners == null) if (plcListeners == null)
return; return;
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (plcListeners) { synchronized (plcListeners) {
plcListeners.removeElement(addressKey, listener); plcListeners.removeElement(addressKey, listener);
} }
logger.info("Unregistered listener from plc " + plcId + " key " + addressKey + ": " + listener); logger.info("Unregistered listener from plc {} key {}: {}", plcId, addressKey, listener);
} }
public void run(PrivilegedRunnable runnable) throws Exception { public void run(PrivilegedRunnable runnable) throws Exception {
@ -214,9 +212,9 @@ public class PlcGwServerHandler extends StrolchComponent {
PlcAddressResponseListener listener) { PlcAddressResponseListener listener) {
if (valueJ == null) if (valueJ == null)
logger.info("Sending " + plcAddressKey + " to " + plcSession.plcId + "..."); logger.info("Sending {} to {}...", plcAddressKey, plcSession.plcId);
else else
logger.info("Sending " + plcAddressKey + " = " + valueJ + " to " + plcSession.plcId + "..."); logger.info("Sending {} = {} to {}...", plcAddressKey, valueJ, plcSession.plcId);
PlcAddressResponse plcResponse = new PlcAddressResponse(plcSession.plcId, plcAddressKey); PlcAddressResponse plcResponse = new PlcAddressResponse(plcSession.plcId, plcAddressKey);
plcResponse.setListener(() -> listener.handleResponse(plcResponse)); plcResponse.setListener(() -> listener.handleResponse(plcResponse));
@ -227,22 +225,22 @@ public class PlcGwServerHandler extends StrolchComponent {
sendDataToClient(data, plcSession.session); sendDataToClient(data, plcSession.session);
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to send " + plcAddressKey + " to PLC " + plcSession.plcId, e); logger.error("Failed to send {} to PLC {}", plcAddressKey, plcSession.plcId, e);
plcResponse.setState(PlcResponseState.Failed); plcResponse.setState(PlcResponseState.Failed);
plcResponse.setStateMsg("Failed to send " + plcAddressKey + " to PLC " + plcSession.plcId + ": " plcResponse.setStateMsg(format("Failed to send {0} to PLC {1}: {2}", plcAddressKey, plcSession.plcId,
+ getExceptionMessageWithCauses(e)); getExceptionMessageWithCauses(e)));
try { try {
listener.handleResponse(plcResponse); listener.handleResponse(plcResponse);
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Failed to notify listener " + listener, ex); logger.error("Failed to notify listener {}", listener, ex);
} }
} }
} }
private void asyncGetAddressState(PlcSession plcSession, PlcAddressKey plcAddressKey, private void asyncGetAddressState(PlcSession plcSession, PlcAddressKey plcAddressKey,
PlcAddressResponseValueListener listener) { PlcAddressResponseValueListener listener) {
logger.info("Requesting value for address " + plcAddressKey + " from PLC " + plcSession.plcId + "..."); logger.info("Requesting value for address {} from PLC {}...", plcAddressKey, plcSession.plcId);
PlcAddressValueResponse plcResponse = new PlcAddressValueResponse(plcSession.plcId, plcAddressKey); PlcAddressValueResponse plcResponse = new PlcAddressValueResponse(plcSession.plcId, plcAddressKey);
plcResponse.setListener(() -> listener.handleResponse(plcResponse)); plcResponse.setListener(() -> listener.handleResponse(plcResponse));
@ -253,28 +251,23 @@ public class PlcGwServerHandler extends StrolchComponent {
sendDataToClient(data, plcSession.session); sendDataToClient(data, plcSession.session);
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to get address state for " + plcAddressKey + " from PLC " + plcSession.plcId, e); logger.error("Failed to get address state for {} from PLC {}", plcAddressKey, plcSession.plcId, e);
plcResponse.setState(PlcResponseState.Failed); plcResponse.setState(PlcResponseState.Failed);
plcResponse.setStateMsg( plcResponse.setStateMsg(
"Failed to get address state for " + plcAddressKey + " from PLC " + plcSession.plcId + ": " format("Failed to get address state for {0} from PLC {1}: {2}", plcAddressKey, plcSession.plcId,
+ getExceptionMessageWithCauses(e)); getExceptionMessageWithCauses(e)));
try { try {
listener.handleResponse(plcResponse); listener.handleResponse(plcResponse);
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Failed to notify listener " + listener, ex); logger.error("Failed to notify listener {}", listener, ex);
} }
} }
} }
private static JsonObject buildJsonTelegram(String plcId, PlcAddressKey plcAddressKey, JsonPrimitive valueJ, private static JsonObject buildJsonTelegram(String plcId, PlcAddressKey plcAddressKey, JsonPrimitive valueJ,
PlcAddressResponse plcResponse) { PlcAddressResponse plcResponse) {
JsonObject jsonObject = new JsonObject(); JsonObject jsonObject = buildJson(plcId, plcAddressKey, plcResponse, MSG_TYPE_PLC_TELEGRAM);
jsonObject.addProperty(PARAM_SEQUENCE_ID, plcResponse.getSequenceId());
jsonObject.addProperty(PARAM_MESSAGE_TYPE, MSG_TYPE_PLC_TELEGRAM);
jsonObject.addProperty(PARAM_PLC_ID, plcId);
jsonObject.addProperty(PARAM_RESOURCE, plcAddressKey.resource);
jsonObject.addProperty(PARAM_ACTION, plcAddressKey.action);
if (valueJ != null) if (valueJ != null)
jsonObject.add(PARAM_VALUE, valueJ); jsonObject.add(PARAM_VALUE, valueJ);
return jsonObject; return jsonObject;
@ -282,9 +275,14 @@ public class PlcGwServerHandler extends StrolchComponent {
private static JsonObject buildJsonGetAddressStateTelegram(String plcId, PlcAddressKey plcAddressKey, private static JsonObject buildJsonGetAddressStateTelegram(String plcId, PlcAddressKey plcAddressKey,
PlcAddressResponse plcResponse) { PlcAddressResponse plcResponse) {
return buildJson(plcId, plcAddressKey, plcResponse, MSG_TYPE_PLC_GET_ADDRESS_STATE);
}
private static JsonObject buildJson(String plcId, PlcAddressKey plcAddressKey, PlcAddressResponse plcResponse,
String msgTypePlcGetAddressState) {
JsonObject jsonObject = new JsonObject(); JsonObject jsonObject = new JsonObject();
jsonObject.addProperty(PARAM_SEQUENCE_ID, plcResponse.getSequenceId()); jsonObject.addProperty(PARAM_SEQUENCE_ID, plcResponse.getSequenceId());
jsonObject.addProperty(PARAM_MESSAGE_TYPE, MSG_TYPE_PLC_GET_ADDRESS_STATE); jsonObject.addProperty(PARAM_MESSAGE_TYPE, msgTypePlcGetAddressState);
jsonObject.addProperty(PARAM_PLC_ID, plcId); jsonObject.addProperty(PARAM_PLC_ID, plcId);
jsonObject.addProperty(PARAM_RESOURCE, plcAddressKey.resource); jsonObject.addProperty(PARAM_RESOURCE, plcAddressKey.resource);
jsonObject.addProperty(PARAM_ACTION, plcAddressKey.action); jsonObject.addProperty(PARAM_ACTION, plcAddressKey.action);
@ -335,21 +333,21 @@ public class PlcGwServerHandler extends StrolchComponent {
String messageType = jsonObject.get(PARAM_MESSAGE_TYPE).getAsString(); String messageType = jsonObject.get(PARAM_MESSAGE_TYPE).getAsString();
switch (messageType) { switch (messageType) {
case MSG_TYPE_AUTHENTICATION -> handleAuth(session, jsonObject); case MSG_TYPE_AUTHENTICATION -> handleAuth(session, jsonObject);
case MSG_TYPE_PLC_NOTIFICATION -> handleNotification(assertPlcAuthed(plcId, session.getId()), jsonObject); case MSG_TYPE_PLC_NOTIFICATION -> handleNotification(assertPlcAuthed(plcId, session.getId()), jsonObject);
case MSG_TYPE_PLC_TELEGRAM -> handleTelegramResponse(assertPlcAuthed(plcId, session.getId()), jsonObject); case MSG_TYPE_PLC_TELEGRAM -> handleTelegramResponse(assertPlcAuthed(plcId, session.getId()), jsonObject);
case MSG_TYPE_PLC_GET_ADDRESS_STATE -> case MSG_TYPE_PLC_GET_ADDRESS_STATE ->
handleGetAddressStateResponse(assertPlcAuthed(plcId, session.getId()), jsonObject); handleGetAddressStateResponse(assertPlcAuthed(plcId, session.getId()), jsonObject);
case MSG_TYPE_STATE_NOTIFICATION -> handleStateMsg(assertPlcAuthed(plcId, session.getId()), jsonObject); case MSG_TYPE_STATE_NOTIFICATION -> handleStateMsg(assertPlcAuthed(plcId, session.getId()), jsonObject);
case MSG_TYPE_MESSAGE -> { case MSG_TYPE_MESSAGE -> {
assertPlcAuthed(plcId, session.getId()); assertPlcAuthed(plcId, session.getId());
handleMessage(jsonObject); handleMessage(jsonObject);
} }
case MSG_TYPE_DISABLE_MESSAGE -> { case MSG_TYPE_DISABLE_MESSAGE -> {
assertPlcAuthed(plcId, session.getId()); assertPlcAuthed(plcId, session.getId());
handleDisableMessage(jsonObject); handleDisableMessage(jsonObject);
} }
default -> logger.error(plcId + ": Unhandled message type " + messageType); default -> logger.error("{}: Unhandled message type {}", plcId, messageType);
} }
} }
@ -369,21 +367,20 @@ public class PlcGwServerHandler extends StrolchComponent {
else else
value = valueJ.getAsString(); value = valueJ.getAsString();
logger.info(plcSession.plcId + ": Received notification for " + addressKey.toKey() + ": " + value); logger.info("{}: Received notification for {}: {}", plcSession.plcId, addressKey.toKey(), value);
MapOfLists<PlcAddressKey, PlcNotificationListener> plcListeners = this.plcAddressListenersByPlcId.get( MapOfLists<PlcAddressKey, PlcNotificationListener> plcListeners = this.plcAddressListenersByPlcId.get(
plcSession.plcId); plcSession.plcId);
if (plcListeners == null) { if (plcListeners == null) {
logger.warn(plcSession.plcId + ": No listeners for PLC " + plcSession.plcId); logger.warn("{}: No listeners for PLC {}", plcSession.plcId, plcSession.plcId);
return; return;
} }
List<PlcNotificationListener> listeners; List<PlcNotificationListener> listeners;
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (plcListeners) { synchronized (plcListeners) {
listeners = plcListeners.getList(addressKey); listeners = plcListeners.getList(addressKey);
if (listeners == null) { if (listeners == null) {
logger.warn(plcSession.plcId + ": No listeners for " + addressKey.toKey()); logger.warn("{}: No listeners for {}", plcSession.plcId, addressKey.toKey());
return; return;
} }
} }
@ -393,8 +390,8 @@ public class PlcGwServerHandler extends StrolchComponent {
try { try {
listener.handleNotification(addressKey, value); listener.handleNotification(addressKey, value);
} catch (Exception e) { } catch (Exception e) {
logger.error( logger.error("{}: Failed to notify listener {} for {}", plcSession.plcId, listener, addressKey.toKey(),
plcSession.plcId + ": Failed to notify listener " + listener + " for " + addressKey.toKey(), e); e);
} }
} }
} }
@ -403,7 +400,7 @@ public class PlcGwServerHandler extends StrolchComponent {
long sequenceId = responseJ.get(PARAM_SEQUENCE_ID).getAsLong(); long sequenceId = responseJ.get(PARAM_SEQUENCE_ID).getAsLong();
PlcResponse plcResponse = this.plcResponses.remove(sequenceId); PlcResponse plcResponse = this.plcResponses.remove(sequenceId);
if (plcResponse == null) { if (plcResponse == null) {
logger.error(plcSession.plcId + ": PlcResponse does not exist for sequenceId " + sequenceId); logger.error("{}: PlcResponse does not exist for sequenceId {}", plcSession.plcId, sequenceId);
return; return;
} }
@ -415,8 +412,7 @@ public class PlcGwServerHandler extends StrolchComponent {
try { try {
plcResponse.getListener().run(); plcResponse.getListener().run();
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to notify listener " + plcResponse.getListener() + " for response of " + plcResponse, logger.error("Failed to notify listener {} for response of {}", plcResponse.getListener(), plcResponse, e);
e);
} }
} }
@ -424,7 +420,8 @@ public class PlcGwServerHandler extends StrolchComponent {
long sequenceId = responseJ.get(PARAM_SEQUENCE_ID).getAsLong(); long sequenceId = responseJ.get(PARAM_SEQUENCE_ID).getAsLong();
PlcResponse response = this.plcResponses.remove(sequenceId); PlcResponse response = this.plcResponses.remove(sequenceId);
if (response == null) { if (response == null) {
logger.error(plcSession.plcId + ": PlcResponse does not exist for sequenceId " + sequenceId); logger.error("{}: PlcResponse does not exist for GetAddressState message with sequenceId {}",
plcSession.plcId, sequenceId);
return; return;
} }
@ -441,19 +438,18 @@ public class PlcGwServerHandler extends StrolchComponent {
try { try {
plcResponse.getListener().run(); plcResponse.getListener().run();
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to notify listener " + plcResponse.getListener() + " for response of " + plcResponse, logger.error("Failed to notify listener {} for response of {}", plcResponse.getListener(), plcResponse, e);
e);
} }
} }
private void handleMessage(JsonObject jsonObject) { private void handleMessage(JsonObject jsonObject) {
JsonObject msgJ = jsonObject.get(PARAM_MESSAGE).getAsJsonObject(); JsonObject msgJ = jsonObject.get(PARAM_MESSAGE).getAsJsonObject();
LogMessage logMessage = LogMessage.fromJson(msgJ); LogMessage logMessage = LogMessage.fromJson(msgJ);
logger.info("Received message " + logMessage.getLocator()); logger.info("Received message {}", logMessage.getLocator());
if (!logMessage.getRealm().equals(this.realm)) if (!logMessage.getRealm().equals(this.realm))
throw new IllegalStateException( throw new IllegalStateException(
"Unexpected realm in message " + logMessage.getId() + " " + logMessage.getLocator() + " " format("Unexpected realm in message {0} {1} {2}", logMessage.getId(), logMessage.getLocator(),
+ logMessage.getMessage()); logMessage.getMessage()));
OperationsLog log = getComponent(OperationsLog.class); OperationsLog log = getComponent(OperationsLog.class);
log.updateState(logMessage.getRealm(), logMessage.getLocator(), LogMessageState.Inactive); log.updateState(logMessage.getRealm(), logMessage.getLocator(), LogMessageState.Inactive);
@ -466,7 +462,7 @@ public class PlcGwServerHandler extends StrolchComponent {
if (!realm.equals(this.realm)) if (!realm.equals(this.realm))
throw new IllegalStateException("Unexpected realm in disable message action for message " + locator); throw new IllegalStateException("Unexpected realm in disable message action for message " + locator);
logger.info("Received disable for messages with locator " + locator); logger.info("Received disable for messages with locator {}", locator);
OperationsLog operationsLog = getComponent(OperationsLog.class); OperationsLog operationsLog = getComponent(OperationsLog.class);
operationsLog.updateState(realm, locator, LogMessageState.Inactive); operationsLog.updateState(realm, locator, LogMessageState.Inactive);
} }
@ -475,8 +471,8 @@ public class PlcGwServerHandler extends StrolchComponent {
String sessionId = session.getId(); String sessionId = session.getId();
if (!authJ.has(PARAM_PLC_ID) || !authJ.has(PARAM_USERNAME) || !authJ.has(PARAM_PASSWORD)) if (!authJ.has(PARAM_PLC_ID) || !authJ.has(PARAM_USERNAME) || !authJ.has(PARAM_PASSWORD))
throw new IllegalStateException( throw new IllegalStateException(
sessionId + ": Auth Json is missing one of " + PARAM_PLC_ID + ", " + PARAM_USERNAME + ", " format("{0}: Auth Json is missing one of {1}, {2}, {3}: {4}", sessionId, PARAM_PLC_ID,
+ PARAM_PASSWORD + ": " + authJ); PARAM_USERNAME, PARAM_PASSWORD, authJ));
String plcId = authJ.get(PARAM_PLC_ID).getAsString(); String plcId = authJ.get(PARAM_PLC_ID).getAsString();
String username = authJ.get(PARAM_USERNAME).getAsString(); String username = authJ.get(PARAM_USERNAME).getAsString();
@ -519,21 +515,21 @@ public class PlcGwServerHandler extends StrolchComponent {
private void sendAuthResponse(PlcSession plcSession, JsonObject jsonObject) { private void sendAuthResponse(PlcSession plcSession, JsonObject jsonObject) {
try { try {
sendDataToClient(jsonObject.toString(), plcSession.session); sendDataToClient(jsonObject.toString(), plcSession.session);
logger.info(plcSession.plcId + ": Sent " + MSG_TYPE_AUTHENTICATION + " response on Session " logger.info("{}: Sent " + MSG_TYPE_AUTHENTICATION + " response on Session {}", plcSession.plcId,
+ plcSession.session.getId()); plcSession.session.getId());
} catch (Exception e) { } catch (Exception e) {
logger.error(plcSession.plcId + ": Failed to send data to PLC", e); logger.error("{}: Failed to send data to PLC", plcSession.plcId, e);
try { try {
plcSession.session.close( plcSession.session.close(
new CloseReason(CloseReason.CloseCodes.CLOSED_ABNORMALLY, "Failed to send auth response")); new CloseReason(CloseReason.CloseCodes.CLOSED_ABNORMALLY, "Failed to send auth response"));
} catch (IOException ex) { } catch (IOException ex) {
logger.error(plcSession.plcId + ": Faild to close session to PLC"); logger.error("{}: Faild to close session to PLC", plcSession.plcId);
} }
} }
} }
public void onWsOpen(Session session) { public void onWsOpen(Session session) {
logger.info(session.getId() + ": New Session"); logger.info("{}: New Session", session.getId());
} }
public void onWsPong(PongMessage message, Session session) { public void onWsPong(PongMessage message, Session session) {
@ -546,8 +542,8 @@ public class PlcGwServerHandler extends StrolchComponent {
PlcSession existingPlcSession = this.plcSessionsByPlcId.put(plcId, plcSession); PlcSession existingPlcSession = this.plcSessionsByPlcId.put(plcId, plcSession);
if (existingPlcSession != null) { if (existingPlcSession != null) {
logger.error("Old PLC session found for plc " + plcId + " under SessionId " logger.error("Old PLC session found for plc {} under SessionId {}. Closing that session.", plcId,
+ existingPlcSession.session.getId() + ". Closing that session."); existingPlcSession.session.getId());
this.plcSessionsBySessionId.remove(existingPlcSession.session.getId()); this.plcSessionsBySessionId.remove(existingPlcSession.session.getId());
try { try {
@ -556,12 +552,12 @@ public class PlcGwServerHandler extends StrolchComponent {
new CloseReason(CloseReason.CloseCodes.NOT_CONSISTENT, "Stale session")); new CloseReason(CloseReason.CloseCodes.NOT_CONSISTENT, "Stale session"));
} }
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to close session " + existingPlcSession.session.getId(), e); logger.error("Failed to close session {}", existingPlcSession.session.getId(), e);
} }
} }
this.plcSessionsBySessionId.put(session.getId(), plcSession); this.plcSessionsBySessionId.put(session.getId(), plcSession);
logger.info("PLC connected with ID " + plcId + " and SessionId " + plcSession.session.getId()); logger.info("PLC connected with ID {} and SessionId {}", plcId, plcSession.session.getId());
} }
if (plcSession.certificate != null) { if (plcSession.certificate != null) {
@ -570,14 +566,14 @@ public class PlcGwServerHandler extends StrolchComponent {
sessionHandler.validate(plcSession.certificate); sessionHandler.validate(plcSession.certificate);
plcSession.lastUpdate = System.currentTimeMillis(); plcSession.lastUpdate = System.currentTimeMillis();
logger.info("PLC " + plcId + " with SessionId " + session.getId() + " is still alive on certificate " logger.info("PLC {} with SessionId {} is still alive on certificate {}", plcId, session.getId(),
+ plcSession.certificate.getSessionId()); plcSession.certificate.getSessionId());
this.plcStateHandler.handleStillConnected(plcSession); this.plcStateHandler.handleStillConnected(plcSession);
} catch (StrolchNotAuthenticatedException e) { } catch (StrolchNotAuthenticatedException e) {
logger.error("PLC session " + session.getId() + " is not authenticated anymore for plc " + plcId logger.error("PLC session {} is not authenticated anymore for plc {}. Closing session.",
+ ". Closing session."); session.getId(), plcId);
this.plcSessionsBySessionId.remove(plcId); this.plcSessionsBySessionId.remove(plcId);
PlcSession registeredSession = this.plcSessionsByPlcId.get(plcId); PlcSession registeredSession = this.plcSessionsByPlcId.get(plcId);
@ -590,7 +586,7 @@ public class PlcGwServerHandler extends StrolchComponent {
session.close(new CloseReason(CloseReason.CloseCodes.NOT_CONSISTENT, "Stale session")); session.close(new CloseReason(CloseReason.CloseCodes.NOT_CONSISTENT, "Stale session"));
} }
} catch (Exception e1) { } catch (Exception e1) {
logger.error("Failed to close session " + session.getId(), e1); logger.error("Failed to close session {}", session.getId(), e1);
} }
} }
} }
@ -599,14 +595,14 @@ public class PlcGwServerHandler extends StrolchComponent {
private void clearDeadConnections() { private void clearDeadConnections() {
// find all sessions which are timed out // find all sessions which are timed out
List<PlcSession> expiredSessions = this.plcSessionsBySessionId.values() List<PlcSession> expiredSessions = this.plcSessionsBySessionId
.values()
.stream() .stream()
.filter(this::hasExpired) .filter(this::hasExpired)
.toList(); .toList();
for (PlcSession plcSession : expiredSessions) { for (PlcSession plcSession : expiredSessions) {
logger.warn("Session " + plcSession.session.getId() + " has expired for PLC " + plcSession.plcId logger.warn("Session {} has expired for PLC {}. Closing.", plcSession.session.getId(), plcSession.plcId);
+ ". Closing.");
// close the session // close the session
try { try {
@ -615,13 +611,13 @@ public class PlcGwServerHandler extends StrolchComponent {
new CloseReason(CloseReason.CloseCodes.CLOSED_ABNORMALLY, "Session expired!")); new CloseReason(CloseReason.CloseCodes.CLOSED_ABNORMALLY, "Session expired!"));
} }
} catch (IOException e) { } catch (IOException e) {
logger.error("Closing session lead to exception: " + getExceptionMessageWithCauses(e)); logger.error("Closing session lead to exception: {}", getExceptionMessageWithCauses(e));
} }
// invalidate the certificate // invalidate the certificate
if (plcSession.certificate != null) { if (plcSession.certificate != null) {
logger.warn("Invalidating old Session " + plcSession.session.getId() + " for PLC " + plcSession.plcId logger.warn("Invalidating old Session {} for PLC {} with certificate {}", plcSession.session.getId(),
+ " with certificate " + plcSession.certificate.getSessionId()); plcSession.plcId, plcSession.certificate.getSessionId());
StrolchSessionHandler sessionHandler = getContainer().getComponent(StrolchSessionHandler.class); StrolchSessionHandler sessionHandler = getContainer().getComponent(StrolchSessionHandler.class);
sessionHandler.invalidate(plcSession.certificate); sessionHandler.invalidate(plcSession.certificate);
} }
@ -645,22 +641,22 @@ public class PlcGwServerHandler extends StrolchComponent {
PlcSession plcSession = this.plcSessionsBySessionId.remove(session.getId()); PlcSession plcSession = this.plcSessionsBySessionId.remove(session.getId());
if (plcSession == null) { if (plcSession == null) {
logger.warn(session.getId() + ": Connection to session " + session.getId() + " is lost due to " logger.warn("{}: Connection to session {} is lost due to {} {}", session.getId(), session.getId(),
+ closeReason.getCloseCode() + " " + closeReason.getReasonPhrase()); closeReason.getCloseCode(), closeReason.getReasonPhrase());
return; return;
} }
this.plcSessionsByPlcId.remove(plcSession.plcId); this.plcSessionsByPlcId.remove(plcSession.plcId);
String reason = closeReason.getCloseCode() + " " + closeReason.getReasonPhrase(); String reason = closeReason.getCloseCode() + " " + closeReason.getReasonPhrase();
logger.warn(session.getId() + ": Connection to PLC " + plcSession.plcId + " is lost due to " + reason); logger.warn("{}: Connection to PLC {} is lost due to {}", session.getId(), plcSession.plcId, reason);
if (plcSession.certificate != null) { if (plcSession.certificate != null) {
StrolchSessionHandler sessionHandler = getContainer().getComponent(StrolchSessionHandler.class); StrolchSessionHandler sessionHandler = getContainer().getComponent(StrolchSessionHandler.class);
try { try {
sessionHandler.invalidate(plcSession.certificate); sessionHandler.invalidate(plcSession.certificate);
} catch (Exception e) { } catch (Exception e) {
logger.error(session.getId() + ": Failed to invalidate session for plc " + plcSession.plcId, e); logger.error("{}: Failed to invalidate session for plc {}", session.getId(), plcSession.plcId, e);
} }
this.plcStateHandler.handlePlcState(plcSession, ConnectionState.Disconnected, reason, null); this.plcStateHandler.handlePlcState(plcSession, ConnectionState.Disconnected, reason, null);
@ -675,7 +671,7 @@ public class PlcGwServerHandler extends StrolchComponent {
private void notifyObserversOfConnectionLost(String plcId) { private void notifyObserversOfConnectionLost(String plcId) {
logger.info("Notifying observers of connection lost to plc " + plcId + "..."); logger.info("Notifying observers of connection lost to plc {}...", plcId);
// first notify and remove any response observers for disconnected PLCs // first notify and remove any response observers for disconnected PLCs
List<PlcResponse> keySet = new ArrayList<>(this.plcResponses.values()); List<PlcResponse> keySet = new ArrayList<>(this.plcResponses.values());
@ -687,12 +683,11 @@ public class PlcGwServerHandler extends StrolchComponent {
plcResponse.setStateMsg(MSG_DISCONNECTED_TIMED_OUT); plcResponse.setStateMsg(MSG_DISCONNECTED_TIMED_OUT);
plcResponse.setState(PlcResponseState.Failed); plcResponse.setState(PlcResponseState.Failed);
try { try {
logger.warn("Notifying PlcResponse listener " + plcResponse + " of connection lost!"); logger.warn("Notifying PlcResponse listener {} of connection lost!", plcResponse);
plcResponse.getListener().run(); plcResponse.getListener().run();
} catch (Exception e) { } catch (Exception e) {
logger.error( logger.error("Failed to notify PlcResponse listener {} of connection lost to PLC {}", plcResponse,
"Failed to notify PlcResponse listener " + plcResponse + " of connection lost to PLC " + plcId, plcId, e);
e);
} }
} }
@ -710,12 +705,11 @@ public class PlcGwServerHandler extends StrolchComponent {
List<PlcNotificationListener> listenersCopy = new ArrayList<>(listeners); List<PlcNotificationListener> listenersCopy = new ArrayList<>(listeners);
for (PlcNotificationListener listener : listenersCopy) { for (PlcNotificationListener listener : listenersCopy) {
logger.warn("Notifying PlcNotificationListener " + addressKey + " with " + listener logger.warn("Notifying PlcNotificationListener {} with {} of connection lost!", addressKey, listener);
+ " of connection lost!");
try { try {
listener.handleConnectionLost(); listener.handleConnectionLost();
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to notify listener " + listener + " of connection lost for PLC " + plcId, e); logger.error("Failed to notify listener {} of connection lost for PLC {}", listener, plcId, e);
} }
} }
} }
@ -730,14 +724,14 @@ public class PlcGwServerHandler extends StrolchComponent {
try { try {
listener.handleConnectionState(plcId, connectionState); listener.handleConnectionState(plcId, connectionState);
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to notify listener " + listener + " of new connection state " + connectionState logger.error("Failed to notify listener {} of new connection state {} for PLC {}", listener,
+ " for PLC " + plcId, e); connectionState, plcId, e);
} }
} }
} }
public void onWsError(Session session, Throwable throwable) { public void onWsError(Session session, Throwable throwable) {
logger.error(session.getId() + ": Error: " + throwable.getMessage(), throwable); logger.error("{}: Error: {}", session.getId(), throwable.getMessage(), throwable);
} }
private PlcSession getPlcSession(String plcId) { private PlcSession getPlcSession(String plcId) {

View File

@ -162,12 +162,13 @@ public abstract class PlcGwService implements PlcNotificationListener, PlcAddres
} }
private void handleFailedRunnable(String runnable, Exception e) { private void handleFailedRunnable(String runnable, Exception e) {
logger.error("Runnable " + runnable + " failed!", e); logger.error("Runnable {} failed!", runnable, e);
if (hasOperationsLogs()) { if (hasOperationsLogs()) {
getOperationsLogs().addMessage( getOperationsLogs().addMessage(
new LogMessage(this.container.getRealmNames().iterator().next(), SYSTEM_USER_AGENT, new LogMessage(this.container.getRealmNames().iterator().next(), SYSTEM_USER_AGENT,
Resource.locatorFor(TYPE_PLC, this.plcId), LogSeverity.Exception, Resource.locatorFor(TYPE_PLC, this.plcId), LogSeverity.Exception,
LogMessageState.Information, PlcGwSrvI18n.bundle, "systemAction.failed").withException(e) LogMessageState.Information, PlcGwSrvI18n.bundle, "systemAction.failed")
.withException(e)
.value("action", runnable)); .value("action", runnable));
} }
} }
@ -175,8 +176,7 @@ public abstract class PlcGwService implements PlcNotificationListener, PlcAddres
/** /**
* Executes the given consumer in a read-only transaction * Executes the given consumer in a read-only transaction
* *
* @param consumer * @param consumer the consumer to run in a read-only transaction
* the consumer to run in a read-only transaction
*/ */
protected <T> T runReadOnlyTx(CheckedBiFunction<PrivilegeContext, StrolchTransaction, T> consumer) { protected <T> T runReadOnlyTx(CheckedBiFunction<PrivilegeContext, StrolchTransaction, T> consumer) {
return run(ctx -> { return run(ctx -> {
@ -193,8 +193,7 @@ public abstract class PlcGwService implements PlcNotificationListener, PlcAddres
* when the runnable is completed and the TX is dirty, i.e. {@link StrolchTransaction#needsCommit()} returns * when the runnable is completed and the TX is dirty, i.e. {@link StrolchTransaction#needsCommit()} returns
* true</p> * true</p>
* *
* @param consumer * @param consumer the consumer to run in a writeable transaction
* the consumer to run in a writeable transaction
*/ */
protected <T> T runWritableTx(CheckedBiFunction<PrivilegeContext, StrolchTransaction, T> consumer) { protected <T> T runWritableTx(CheckedBiFunction<PrivilegeContext, StrolchTransaction, T> consumer) {
return run(ctx -> { return run(ctx -> {
@ -232,7 +231,8 @@ public abstract class PlcGwService implements PlcNotificationListener, PlcAddres
protected ScheduledFuture<?> scheduleAtFixedRate(PrivilegedRunnable runnable, long initialDelay, long period, protected ScheduledFuture<?> scheduleAtFixedRate(PrivilegedRunnable runnable, long initialDelay, long period,
TimeUnit delayUnit) { TimeUnit delayUnit) {
return this.container.getAgent() return this.container
.getAgent()
.getScheduledExecutor(PlcGwService.class.getSimpleName()) .getScheduledExecutor(PlcGwService.class.getSimpleName())
.scheduleAtFixedRate(() -> { .scheduleAtFixedRate(() -> {
try { try {
@ -245,7 +245,8 @@ public abstract class PlcGwService implements PlcNotificationListener, PlcAddres
protected ScheduledFuture<?> scheduleWithFixedDelay(PrivilegedRunnable runnable, long initialDelay, long period, protected ScheduledFuture<?> scheduleWithFixedDelay(PrivilegedRunnable runnable, long initialDelay, long period,
TimeUnit delayUnit) { TimeUnit delayUnit) {
return this.container.getAgent() return this.container
.getAgent()
.getScheduledExecutor(PlcGwService.class.getSimpleName()) .getScheduledExecutor(PlcGwService.class.getSimpleName())
.scheduleWithFixedDelay(() -> { .scheduleWithFixedDelay(() -> {
try { try {
@ -257,6 +258,6 @@ public abstract class PlcGwService implements PlcNotificationListener, PlcAddres
} }
protected void handleFailedSchedule(Exception e) { protected void handleFailedSchedule(Exception e) {
logger.error("Failed to execute " + getClass().getSimpleName(), e); logger.error("Failed to execute {}", getClass().getSimpleName(), e);
} }
} }

View File

@ -103,9 +103,8 @@ public class PlcStateHandler {
plc.setString(PARAM_CONNECTION_STATE_MSG, connectionStateMsg); plc.setString(PARAM_CONNECTION_STATE_MSG, connectionStateMsg);
tx.update(plc); tx.update(plc);
logger.info( logger.info("Updated connection state for PLC {} to {}{}", plc.getId(), connectionState,
"Updated connection state for PLC " + plc.getId() + " to " + connectionState + (isEmpty( isEmpty(connectionStateMsg) ? "" : ": " + connectionStateMsg);
connectionStateMsg) ? "" : ": " + connectionStateMsg));
} }
if (stateJ != null) { if (stateJ != null) {
@ -145,7 +144,7 @@ public class PlcStateHandler {
} }
private void saveGatewayIpAddresses(StrolchTransaction tx, Resource plc, JsonArray ipAddresses) { private void saveGatewayIpAddresses(StrolchTransaction tx, Resource plc, JsonArray ipAddresses) {
if (ipAddresses.size() == 0) if (ipAddresses.isEmpty())
return; return;
// update local IPs // update local IPs
@ -225,20 +224,20 @@ public class PlcStateHandler {
} }
private Resource buildNewPlc(PlcGwServerHandler.PlcSession plcSession, StrolchTransaction tx) { private Resource buildNewPlc(PlcGwServerHandler.PlcSession plcSession, StrolchTransaction tx) {
return new ResourceBuilder(plcSession.plcId, plcSession.plcId, TYPE_PLC) // return new ResourceBuilder(plcSession.plcId, plcSession.plcId, TYPE_PLC)
.defaultBag() // .defaultBag()
.string(PARAM_CONNECTION_STATE, buildParamName(PARAM_CONNECTION_STATE)) .string(PARAM_CONNECTION_STATE, buildParamName(PARAM_CONNECTION_STATE))
.enumeration(ConnectionState.Disconnected) .enumeration(ConnectionState.Disconnected)
.end() // .end()
.string(PARAM_CONNECTION_STATE_MSG, buildParamName(PARAM_CONNECTION_STATE_MSG)) .string(PARAM_CONNECTION_STATE_MSG, buildParamName(PARAM_CONNECTION_STATE_MSG))
.end() // .end()
.stringList(PARAM_LOCAL_IP, buildParamName(PARAM_LOCAL_IP)) .stringList(PARAM_LOCAL_IP, buildParamName(PARAM_LOCAL_IP))
.end() // .end()
.endBag() // .endBag()
.build(); .build();
} }
@ -246,18 +245,16 @@ public class PlcStateHandler {
if (systemState == null) if (systemState == null)
return; return;
new ResourceSystemStateFromJson().compactStates() // new ResourceSystemStateFromJson().compactStates()
// os // os
.withSystemLoadAverageState() // .withSystemLoadAverageState()
// memory // memory
.withMemoryRounding(DataUnit.MegaBytes) // .withMemoryRounding(DataUnit.MegaBytes).withFreePhysicalMemorySizeState()
.withFreePhysicalMemorySizeState() //
// storage // storage
.withStorageSpaceRounding(DataUnit.GigaBytes) // .withStorageSpaceRounding(DataUnit.GigaBytes).withFreeSpaceState()
.withFreeSpaceState() //
.fillElement(systemState, gateway); .fillElement(systemState, gateway);
} }

View File

@ -1,10 +1,15 @@
package li.strolch.plc.gw.server; package li.strolch.plc.gw.server;
import li.strolch.model.Resource;
import li.strolch.plc.model.PlcAddressKey; import li.strolch.plc.model.PlcAddressKey;
import li.strolch.plc.model.PlcAddressValueResponse; import li.strolch.plc.model.PlcAddressValueResponse;
import static li.strolch.plc.model.PlcConstants.PARAM_PLC_SUPPORTS_READ_STATE;
public abstract class ReadStatePlcGwService extends PlcGwService implements PlcConnectionStateListener { public abstract class ReadStatePlcGwService extends PlcGwService implements PlcConnectionStateListener {
protected boolean plcSupportsReadState;
public ReadStatePlcGwService(String plcId, PlcGwServerHandler plcHandler) { public ReadStatePlcGwService(String plcId, PlcGwServerHandler plcHandler) {
super(plcId, plcHandler); super(plcId, plcHandler);
} }
@ -12,14 +17,28 @@ public abstract class ReadStatePlcGwService extends PlcGwService implements PlcC
protected void handleGetState(PlcAddressValueResponse response) { protected void handleGetState(PlcAddressValueResponse response) {
PlcAddressKey addressKey = response.getPlcAddressKey(); PlcAddressKey addressKey = response.getPlcAddressKey();
if (response.isFailed()) { if (response.isFailed()) {
logger.error("Failed to read value for address " + addressKey + ": " + response.getStateMsg()); logger.error("Failed to read value for address {}: {}", addressKey, response.getStateMsg());
} else { } else {
storeAddressState(addressKey, response.getValue()); storeAddressState(addressKey, response.getValue());
} }
} }
@Override
public void register() {
this.plcSupportsReadState = runReadOnlyTx((ctx, tx) -> {
Resource configuration = tx.getConfiguration();
return !configuration.hasParameter(PARAM_PLC_SUPPORTS_READ_STATE) || configuration.getBoolean(
PARAM_PLC_SUPPORTS_READ_STATE);
});
super.register();
}
protected void readState(String resource, String action) { protected void readState(String resource, String action) {
this.plcHandler.asyncGetAddressState(keyFor(resource, action), this.plcId, this::handleGetState); if (!this.plcSupportsReadState)
logger.warn("Not reading state for resource {}: {} as PLC does not support this feature!", resource,
action);
else
this.plcHandler.asyncGetAddressState(keyFor(resource, action), this.plcId, this::handleGetState);
} }
protected abstract void storeAddressState(PlcAddressKey addressKey, Object value); protected abstract void storeAddressState(PlcAddressKey addressKey, Object value);

View File

@ -1,29 +1,26 @@
package li.strolch.plc.gw.server.policy.execution; package li.strolch.plc.gw.server.policy.execution;
import static li.strolch.model.StrolchModelConstants.BAG_PARAMETERS;
import static li.strolch.model.log.LogMessageState.*;
import static li.strolch.plc.gw.server.PlcGwSrvI18n.*;
import static li.strolch.plc.model.PlcConstants.PARAM_PLC_ID;
import static li.strolch.runtime.StrolchConstants.SYSTEM_USER_AGENT;
import java.util.HashSet;
import java.util.Set;
import li.strolch.execution.ExecutionHandler;
import li.strolch.execution.policy.SimpleExecution; import li.strolch.execution.policy.SimpleExecution;
import li.strolch.model.activity.Action; import li.strolch.model.activity.Action;
import li.strolch.model.log.LogMessage; import li.strolch.model.log.LogMessage;
import li.strolch.model.log.LogMessageState;
import li.strolch.model.log.LogSeverity; import li.strolch.model.log.LogSeverity;
import li.strolch.model.parameter.StringParameter; import li.strolch.model.parameter.StringParameter;
import li.strolch.persistence.api.StrolchTransaction; import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.plc.gw.server.PlcAddressResponseListener; import li.strolch.plc.gw.server.PlcAddressResponseListener;
import li.strolch.plc.gw.server.PlcGwServerHandler; import li.strolch.plc.gw.server.PlcGwServerHandler;
import li.strolch.plc.gw.server.PlcGwSrvI18n;
import li.strolch.plc.gw.server.PlcNotificationListener; import li.strolch.plc.gw.server.PlcNotificationListener;
import li.strolch.plc.model.PlcAddressKey; import li.strolch.plc.model.PlcAddressKey;
import li.strolch.plc.model.PlcAddressResponse; import li.strolch.plc.model.PlcAddressResponse;
import java.util.HashSet;
import java.util.Set;
import static li.strolch.model.StrolchModelConstants.BAG_PARAMETERS;
import static li.strolch.model.log.LogMessageState.Information;
import static li.strolch.plc.gw.server.PlcGwSrvI18n.bundle;
import static li.strolch.plc.model.PlcConstants.PARAM_PLC_ID;
import static li.strolch.runtime.StrolchConstants.SYSTEM_USER_AGENT;
public abstract class PlcExecutionPolicy extends SimpleExecution public abstract class PlcExecutionPolicy extends SimpleExecution
implements PlcNotificationListener, PlcAddressResponseListener { implements PlcNotificationListener, PlcAddressResponseListener {
@ -85,12 +82,10 @@ public abstract class PlcExecutionPolicy extends SimpleExecution
} }
protected boolean assertResponse(PlcAddressResponse response) { protected boolean assertResponse(PlcAddressResponse response) {
if (response.getState().isFailed()) { if (!response.getState().isFailed())
toError(msgFailedToSendMessage(response)); return true;
return false; toError(msgFailedToSendMessage(response));
} return false;
return true;
} }
protected void send(PlcAddressKey key, boolean value) { protected void send(PlcAddressKey key, boolean value) {

View File

@ -1,7 +1,5 @@
package li.strolch.plc.gw.server.service; package li.strolch.plc.gw.server.service;
import static li.strolch.plc.model.PlcConstants.*;
import li.strolch.persistence.api.StrolchTransaction; import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.plc.model.PlcAddressResponse; import li.strolch.plc.model.PlcAddressResponse;
import li.strolch.service.StringMapArgument; import li.strolch.service.StringMapArgument;
@ -9,6 +7,8 @@ import li.strolch.service.api.AbstractService;
import li.strolch.service.api.ServiceResult; import li.strolch.service.api.ServiceResult;
import li.strolch.utils.dbc.DBC; import li.strolch.utils.dbc.DBC;
import static li.strolch.plc.model.PlcConstants.*;
public class SendPlcTelegramService extends AbstractService<StringMapArgument, ServiceResult> { public class SendPlcTelegramService extends AbstractService<StringMapArgument, ServiceResult> {
@Override @Override

View File

@ -5,14 +5,13 @@ import com.google.gson.JsonPrimitive;
public class ModelHelper { public class ModelHelper {
public static JsonPrimitive valueToJson(Object value) { public static JsonPrimitive valueToJson(Object value) {
if (value instanceof Boolean) return switch (value) {
return new JsonPrimitive((Boolean) value); case Boolean b -> new JsonPrimitive(b);
else if (value instanceof Number) case Number number -> new JsonPrimitive(number);
return new JsonPrimitive((Number) value); case String s -> new JsonPrimitive(s);
else if (value instanceof String) case null, default -> throw new IllegalArgumentException(
return new JsonPrimitive((String) value); "Unhandled value type " + (value == null ? "(null)" : value.getClass().getName()));
throw new IllegalArgumentException( };
"Unhandled value type " + (value == null ? "(null)" : value.getClass().getName()));
} }
public static Object jsonToValue(JsonPrimitive valueJ) { public static Object jsonToValue(JsonPrimitive valueJ) {

View File

@ -1,11 +1,14 @@
package li.strolch.plc.model; package li.strolch.plc.model;
import java.util.Objects;
import li.strolch.model.StrolchValueType; import li.strolch.model.StrolchValueType;
import java.util.Objects;
import static java.text.MessageFormat.format;
/** /**
* <p>This represents the address on the PLC. Addresses always consists of a resource and action field. This address includes:</p> * <p>This represents the address on the PLC. Addresses always consists of a resource and action field. This address
* includes:</p>
* <ul> * <ul>
* <li>the type defined as {@link PlcAddressType}</li> * <li>the type defined as {@link PlcAddressType}</li>
* <li>remote flag, denoting of this address should be sent to a remote listener</li> * <li>remote flag, denoting of this address should be sent to a remote listener</li>
@ -87,8 +90,8 @@ public class PlcAddress {
@Override @Override
public String toString() { public String toString() {
return this.type + " " + this.resource + "-" + this.action + " " + this.valueType.getType() + " @ " return format("{0} {1}-{2} {3} @ {4}", this.type, this.resource, this.action, this.valueType.getType(),
+ this.address; this.address);
} }
public String toKey() { public String toKey() {

View File

@ -1,5 +1,7 @@
package li.strolch.plc.model; package li.strolch.plc.model;
import static java.text.MessageFormat.format;
public class PlcAddressResponse extends PlcResponse { public class PlcAddressResponse extends PlcResponse {
private final PlcAddressKey plcAddressKey; private final PlcAddressKey plcAddressKey;
@ -21,7 +23,7 @@ public class PlcAddressResponse extends PlcResponse {
@Override @Override
public String toString() { public String toString() {
return "PlcAddressResponse{" + "plcId='" + plcId + '\'' + ", plcAddressKey=" + plcAddressKey + ", sequenceId=" return format("PlcAddressResponse'{'plcId=''{0}'', plcAddressKey={1}, sequenceId={2}, state={3}'}'", plcId,
+ sequenceId + ", state=" + state + '}'; plcAddressKey, sequenceId, state);
} }
} }

View File

@ -1,5 +1,7 @@
package li.strolch.plc.model; package li.strolch.plc.model;
import static java.text.MessageFormat.format;
public class PlcAddressValueResponse extends PlcAddressResponse { public class PlcAddressValueResponse extends PlcAddressResponse {
private Object value; private Object value;
@ -40,7 +42,8 @@ public class PlcAddressValueResponse extends PlcAddressResponse {
@Override @Override
public String toString() { public String toString() {
return "PlcAddressValueResponse{" + "plcId='" + plcId + '\'' + ", sequenceId=" + sequenceId + ", state=" + state return format(
+ ", stateMsg='" + stateMsg + '\'' + ", value=" + value + '}'; "PlcAddressValueResponse'{'plcId=''{0}'', sequenceId={1}, state={2}, stateMsg=''{3}'', value={4}'}'",
plcId, sequenceId, state, stateMsg, value);
} }
} }

View File

@ -54,6 +54,8 @@ public class PlcConstants {
public static final String PARAM_REALM = "realm"; public static final String PARAM_REALM = "realm";
public static final String PARAM_SIMULATED = "simulated"; public static final String PARAM_SIMULATED = "simulated";
public static final String PARAM_PLC_SUPPORTS_READ_STATE = "plcSupportsReadState";
public static final String INTERPRETATION_NOTIFICATION = "Notification"; public static final String INTERPRETATION_NOTIFICATION = "Notification";
public static final String INTERPRETATION_TELEGRAM = "Telegram"; public static final String INTERPRETATION_TELEGRAM = "Telegram";

View File

@ -38,11 +38,11 @@ public class PlcConnectionsResource {
Paging<JsonObject> paging; Paging<JsonObject> paging;
try (StrolchTransaction tx = RestfulStrolchComponent.getInstance().openTx(cert, getContext())) { try (StrolchTransaction tx = RestfulStrolchComponent.getInstance().openTx(cert, getContext())) {
paging = new PlcConnectionSearch() // paging = new PlcConnectionSearch()
.stringQuery(query) // .stringQuery(query)
.search(tx) // .search(tx)
.orderByName() // .orderByName()
.visitor(plcConnectionToJson()) // .visitor(plcConnectionToJson())
.toPaging(offset, limit); .toPaging(offset, limit);
} }

View File

@ -1,14 +1,5 @@
package li.strolch.plc.rest; package li.strolch.plc.rest;
import static java.util.Comparator.comparing;
import static li.strolch.plc.model.PlcConstants.*;
import static li.strolch.plc.rest.PlcModelVisitor.*;
import static li.strolch.rest.StrolchRestfulConstants.DATA;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
@ -27,6 +18,15 @@ import li.strolch.rest.StrolchRestfulConstants;
import li.strolch.rest.helper.ResponseUtil; import li.strolch.rest.helper.ResponseUtil;
import li.strolch.utils.collections.MapOfLists; import li.strolch.utils.collections.MapOfLists;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import static java.util.Comparator.comparing;
import static li.strolch.plc.model.PlcConstants.*;
import static li.strolch.plc.rest.PlcModelVisitor.*;
import static li.strolch.rest.StrolchRestfulConstants.DATA;
@Path("plc/logicalDevices") @Path("plc/logicalDevices")
public class PlcLogicalDevicesResource { public class PlcLogicalDevicesResource {
@ -44,12 +44,11 @@ public class PlcLogicalDevicesResource {
JsonArray dataJ = new JsonArray(); JsonArray dataJ = new JsonArray();
try (StrolchTransaction tx = RestfulStrolchComponent.getInstance().openTx(cert, getContext())) { try (StrolchTransaction tx = RestfulStrolchComponent.getInstance().openTx(cert, getContext())) {
MapOfLists<String, Resource> devicesByGroup = new PlcLogicalDeviceSearch() // MapOfLists<String, Resource> devicesByGroup = new PlcLogicalDeviceSearch()
.stringQuery(query) // .stringQuery(query)
.search(tx) // .search(tx)
.orderBy(comparing((Resource o) -> o.getParameter(PARAM_INDEX).getValue())) // .orderBy(comparing((Resource o) -> o.getParameter(PARAM_INDEX).getValue()))
.toMapOfLists(r -> r.hasParameter(PARAM_GROUP) ? .toMapOfLists(r -> r.hasParameter(PARAM_GROUP) ? r.getParameter(PARAM_GROUP).getValueAsString() :
r.getParameter(PARAM_GROUP).getValueAsString() :
"default"); "default");
Set<String> groups = new TreeSet<>(devicesByGroup.keySet()); Set<String> groups = new TreeSet<>(devicesByGroup.keySet());
@ -57,7 +56,8 @@ public class PlcLogicalDevicesResource {
List<Resource> devices = devicesByGroup.getList(group); List<Resource> devices = devicesByGroup.getList(group);
JsonObject groupJ = new JsonObject(); JsonObject groupJ = new JsonObject();
groupJ.addProperty(Tags.Json.NAME, group); groupJ.addProperty(Tags.Json.NAME, group);
groupJ.add(DATA, devices.stream() groupJ.add(DATA, devices
.stream()
.map(e -> e.accept(plcLogicalDeviceToJson())) .map(e -> e.accept(plcLogicalDeviceToJson()))
.collect(JsonArray::new, JsonArray::add, JsonArray::addAll)); .collect(JsonArray::new, JsonArray::add, JsonArray::addAll));
@ -81,7 +81,8 @@ public class PlcLogicalDevicesResource {
Resource plcLogicalDevice = tx.getResourceBy(TYPE_PLC_LOGICAL_DEVICE, id, true); Resource plcLogicalDevice = tx.getResourceBy(TYPE_PLC_LOGICAL_DEVICE, id, true);
tx.assertHasPrivilege(Operation.GET, plcLogicalDevice); tx.assertHasPrivilege(Operation.GET, plcLogicalDevice);
dataJ = tx.getResourcesByRelation(plcLogicalDevice, PARAM_ADDRESSES, true) dataJ = tx
.getResourcesByRelation(plcLogicalDevice, PARAM_ADDRESSES, true)
.stream() .stream()
.map(e -> e.accept(plcAddressToJson(simple))) .map(e -> e.accept(plcAddressToJson(simple)))
.collect(JsonArray::new, JsonArray::add, JsonArray::addAll); .collect(JsonArray::new, JsonArray::add, JsonArray::addAll);
@ -103,7 +104,8 @@ public class PlcLogicalDevicesResource {
Resource plcLogicalDevice = tx.getResourceBy(TYPE_PLC_LOGICAL_DEVICE, id, true); Resource plcLogicalDevice = tx.getResourceBy(TYPE_PLC_LOGICAL_DEVICE, id, true);
tx.assertHasPrivilege(Operation.GET, plcLogicalDevice); tx.assertHasPrivilege(Operation.GET, plcLogicalDevice);
dataJ = tx.getResourcesByRelation(plcLogicalDevice, PARAM_ADDRESSES, true) dataJ = tx
.getResourcesByRelation(plcLogicalDevice, PARAM_ADDRESSES, true)
.stream() .stream()
.map(e -> e.accept(plcAddressToJson(simple))) .map(e -> e.accept(plcAddressToJson(simple)))
.collect(JsonArray::new, JsonArray::add, JsonArray::addAll); .collect(JsonArray::new, JsonArray::add, JsonArray::addAll);
@ -125,7 +127,8 @@ public class PlcLogicalDevicesResource {
Resource plcLogicalDevice = tx.getResourceBy(TYPE_PLC_LOGICAL_DEVICE, id, true); Resource plcLogicalDevice = tx.getResourceBy(TYPE_PLC_LOGICAL_DEVICE, id, true);
tx.assertHasPrivilege(Operation.GET, plcLogicalDevice); tx.assertHasPrivilege(Operation.GET, plcLogicalDevice);
dataJ = tx.getResourcesByRelation(plcLogicalDevice, PARAM_TELEGRAMS, true) dataJ = tx
.getResourcesByRelation(plcLogicalDevice, PARAM_TELEGRAMS, true)
.stream() .stream()
.map(e -> e.accept(plcTelegramToJson(simple))) .map(e -> e.accept(plcTelegramToJson(simple)))
.collect(JsonArray::new, JsonArray::add, JsonArray::addAll); .collect(JsonArray::new, JsonArray::add, JsonArray::addAll);

View File

@ -27,16 +27,22 @@ public class PlcModelVisitor {
// add the custom parameters with keys for the id, name and value, so we can show them on the UI // add the custom parameters with keys for the id, name and value, so we can show them on the UI
JsonArray parametersJ = new JsonArray(); JsonArray parametersJ = new JsonArray();
connectionR.getParameterBag(BAG_PARAMETERS, true).getParameters().stream() connectionR
.getParameterBag(BAG_PARAMETERS, true)
.getParameters()
.stream()
.sorted(comparing(Parameter::getIndex)) .sorted(comparing(Parameter::getIndex))
.filter(p -> !(p.getId().equals(PARAM_STATE) || p.getId().equals(PARAM_STATE_MSG) || p.getId() .filter(p -> !(
.equals(PARAM_CLASS_NAME))).forEach(parameter -> { p.getId().equals(PARAM_STATE) || p.getId().equals(PARAM_STATE_MSG) || p
JsonObject paramJ = new JsonObject(); .getId()
paramJ.addProperty(Tags.Json.ID, parameter.getId()); .equals(PARAM_CLASS_NAME)))
paramJ.addProperty(Tags.Json.NAME, parameter.getName()); .forEach(parameter -> {
paramJ.addProperty(Tags.Json.VALUE, parameter.getValueAsString()); JsonObject paramJ = new JsonObject();
parametersJ.add(paramJ); paramJ.addProperty(Tags.Json.ID, parameter.getId());
}); paramJ.addProperty(Tags.Json.NAME, parameter.getName());
paramJ.addProperty(Tags.Json.VALUE, parameter.getValueAsString());
parametersJ.add(paramJ);
});
connectionJ.add(BAG_PARAMETERS, parametersJ); connectionJ.add(BAG_PARAMETERS, parametersJ);
}); });
} }

View File

@ -1,8 +1,5 @@
package li.strolch.plc.rest; package li.strolch.plc.rest;
import static li.strolch.plc.model.PlcConstants.*;
import static li.strolch.rest.StrolchRestfulConstants.DATA;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.ws.rs.*; import jakarta.ws.rs.*;
@ -18,6 +15,10 @@ import li.strolch.rest.helper.ResponseUtil;
import li.strolch.service.StringMapArgument; import li.strolch.service.StringMapArgument;
import li.strolch.service.api.ServiceHandler; import li.strolch.service.api.ServiceHandler;
import li.strolch.service.api.ServiceResult; import li.strolch.service.api.ServiceResult;
import li.strolch.utils.dbc.DBC;
import static li.strolch.plc.model.PlcConstants.*;
import static li.strolch.rest.StrolchRestfulConstants.DATA;
@Path("plc") @Path("plc")
public class PlcResource { public class PlcResource {
@ -27,6 +28,8 @@ public class PlcResource {
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response getState(@Context HttpServletRequest request) { public Response getState(@Context HttpServletRequest request) {
PlcHandler plcHandler = RestfulStrolchComponent.getInstance().getComponent(PlcHandler.class); PlcHandler plcHandler = RestfulStrolchComponent.getInstance().getComponent(PlcHandler.class);
Certificate cert = (Certificate) request.getAttribute(StrolchRestfulConstants.STROLCH_CERTIFICATE);
DBC.PRE.assertNotNull("No certificate available!", cert);
JsonObject jsonObject = new JsonObject(); JsonObject jsonObject = new JsonObject();
jsonObject.addProperty(PARAM_STATE, plcHandler.getPlcState().name()); jsonObject.addProperty(PARAM_STATE, plcHandler.getPlcState().name());

View File

@ -1,19 +1,5 @@
package li.strolch.plc.core.util; package li.strolch.plc.core.util;
import static java.lang.Integer.parseInt;
import static java.nio.charset.StandardCharsets.UTF_8;
import static li.strolch.model.xml.StrolchXmlHelper.parseToMap;
import static li.strolch.plc.model.PlcConstants.*;
import static li.strolch.utils.helper.StringHelper.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import li.strolch.model.Resource; import li.strolch.model.Resource;
import li.strolch.model.StrolchRootElement; import li.strolch.model.StrolchRootElement;
import li.strolch.model.StrolchValueType; import li.strolch.model.StrolchValueType;
@ -29,6 +15,20 @@ import org.apache.commons.csv.CSVRecord;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static java.lang.Integer.parseInt;
import static java.nio.charset.StandardCharsets.UTF_8;
import static li.strolch.model.xml.StrolchXmlHelper.parseToMap;
import static li.strolch.plc.model.PlcConstants.*;
import static li.strolch.utils.helper.StringHelper.*;
public class PlcAddressGenerator { public class PlcAddressGenerator {
private static final Logger logger = LoggerFactory.getLogger(PlcAddressGenerator.class); private static final Logger logger = LoggerFactory.getLogger(PlcAddressGenerator.class);
@ -36,7 +36,8 @@ public class PlcAddressGenerator {
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
if (args.length != 3) if (args.length != 3)
throw new IllegalStateException("Usage: java " + PlcAddressGenerator.class.getName() throw new IllegalStateException("Usage: java "
+ PlcAddressGenerator.class.getName()
+ " <templates.xml> <import.csv> <export.csv>"); + " <templates.xml> <import.csv> <export.csv>");
File templatesF = new File(args[0]); File templatesF = new File(args[0]);
@ -79,9 +80,9 @@ public class PlcAddressGenerator {
Map<String, Resource> exportList = new LinkedHashMap<>(); Map<String, Resource> exportList = new LinkedHashMap<>();
CSVFormat csvFormat = CSVFormat.DEFAULT.withFirstRecordAsHeader(); CSVFormat csvFormat = CSVFormat.DEFAULT.builder().setHeader().setSkipHeaderRecord(true).build();
try (InputStream in = new FileInputStream(importFile); try (InputStream in = new FileInputStream(importFile);
CSVParser parser = CSVParser.parse(in, UTF_8, csvFormat)) { CSVParser parser = CSVParser.parse(in, UTF_8, csvFormat)) {
int groupNr = 0; int groupNr = 0;
int groupIndex = 10; int groupIndex = 10;
@ -94,7 +95,7 @@ public class PlcAddressGenerator {
String type = record.get("Type"); String type = record.get("Type");
if (type.isEmpty()) { if (type.isEmpty()) {
logger.info("Ignoring empty type for " + record); logger.info("Ignoring empty type for {}", record);
continue; continue;
} }
@ -107,145 +108,41 @@ public class PlcAddressGenerator {
String keyName = resource + " - " + action1; String keyName = resource + " - " + action1;
switch (type) { switch (type) {
case "Group" -> { case "Group" -> {
groupNr++; groupNr++;
addressIndex = 10; addressIndex = 10;
telegramIndex = 10; telegramIndex = 10;
String deviceId = record.get("DeviceId").trim(); String deviceId = record.get("DeviceId").trim();
if (isEmpty(deviceId)) if (isEmpty(deviceId))
throw new IllegalStateException("No device for new group: " + record); throw new IllegalStateException("No device for new group: " + record);
logicalDevice = logicalDeviceT.getClone(); logicalDevice = logicalDeviceT.getClone();
logicalDevice.setId("D_" + deviceId); logicalDevice.setId("D_" + deviceId);
logicalDevice.setName(deviceId); logicalDevice.setName(deviceId);
String groupNrS = normalizeLength(Integer.toString(groupNr), 2, true, '0'); String groupNrS = normalizeLength(Integer.toString(groupNr), 2, true, '0');
logicalDevice.setString(PARAM_DESCRIPTION, description); logicalDevice.setString(PARAM_DESCRIPTION, description);
logicalDevice.setString(PARAM_GROUP, groupNrS + " " + description); logicalDevice.setString(PARAM_GROUP, groupNrS + " " + description);
logicalDevice.setInteger(PARAM_INDEX, groupIndex); logicalDevice.setInteger(PARAM_INDEX, groupIndex);
add(exportList, logicalDevice); add(exportList, logicalDevice);
groupIndex += 10; groupIndex += 10;
logger.info("Added PlcLogicalDevice " + logicalDevice.getId()); logger.info("Added PlcLogicalDevice {}", logicalDevice.getId());
}
case "Input" -> {
if (isEmpty(resource))
throw new IllegalStateException("resource missing for: " + record);
if (isEmpty(action1))
throw new IllegalStateException("action1 missing for: " + record);
if (isEmpty(connection))
throw new IllegalStateException("connection missing for: " + record);
if (logicalDevice == null)
throw new IllegalStateException(
"No PlcLogicalDevice exists for address with keys " + resource + "-" + action1);
String subType = record.get("SubType").trim();
if (isEmpty(connection))
throw new IllegalStateException("SubType missing for: " + record);
String address = evaluateAddress(subType, record, connection);
Resource addressR = addressT.getClone();
addressR.setId("A_" + key);
addressR.setName(keyName);
addressR.setString(PARAM_DESCRIPTION, description);
addressR.setString(PARAM_ADDRESS, address);
addressR.setString(PARAM_RESOURCE, resource);
addressR.setString(PARAM_ACTION, action1);
if (record.isSet("Inverted"))
addressR.setBoolean(PARAM_INVERTED, Boolean.parseBoolean(record.get("Inverted").trim()));
if (record.isSet("Remote"))
addressR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim()));
addressR.setInteger(PARAM_INDEX, addressIndex);
addressIndex += 10;
BooleanParameter valueP = new BooleanParameter(PARAM_VALUE, "Value", false);
valueP.setIndex(100);
addressR.addParameter(valueP);
add(exportList, addressR);
logicalDevice.addRelation(PARAM_ADDRESSES, addressR);
logger.info(
"Added Boolean PlcAddress " + addressR.getId() + " " + addressR.getName() + " for address "
+ address);
}
case "Output" -> {
if (isEmpty(resource))
throw new IllegalStateException("resource missing for: " + record);
if (isEmpty(action1))
throw new IllegalStateException("action1 missing for: " + record);
if (isEmpty(connection))
throw new IllegalStateException("connection missing for: " + record);
if (logicalDevice == null)
throw new IllegalStateException(
"No PlcLogicalDevice exists for address with keys " + resource + "-" + action1);
String subType = record.get("SubType").trim();
if (isEmpty(connection))
throw new IllegalStateException("SubType missing for: " + record);
String address = evaluateAddress(subType, record, connection);
Resource telegramR;
BooleanParameter valueP;
// action1 value
telegramR = telegramT.getClone();
telegramR.setId("T_" + key);
telegramR.setName(keyName);
telegramR.setString(PARAM_DESCRIPTION, description);
telegramR.setString(PARAM_ADDRESS, address);
telegramR.setString(PARAM_RESOURCE, resource);
telegramR.setString(PARAM_ACTION, action1);
if (record.isSet("Remote"))
telegramR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim()));
telegramR.setInteger(PARAM_INDEX, telegramIndex);
telegramIndex += 10;
valueP = new BooleanParameter(PARAM_VALUE, "Value", true);
valueP.setIndex(100);
telegramR.addParameter(valueP);
add(exportList, telegramR);
logicalDevice.addRelation(PARAM_TELEGRAMS, telegramR);
logger.info("Added Boolean PlcTelegram " + telegramR.getId() + " " + telegramR.getName()
+ " for address " + address);
// action2 value
if (record.isSet("Action2") && isNotEmpty(record.get("Action2").trim())) {
String action2 = record.get("Action2").trim();
telegramR = telegramT.getClone();
telegramR.setId("T_" + resource + "-" + action2);
telegramR.setName(resource + " - " + action2);
telegramR.setString(PARAM_DESCRIPTION, description);
telegramR.setString(PARAM_ADDRESS, address);
telegramR.setString(PARAM_RESOURCE, resource);
telegramR.setString(PARAM_ACTION, action2);
if (record.isSet("Remote"))
telegramR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim()));
telegramR.setInteger(PARAM_INDEX, telegramIndex);
telegramIndex += 10;
valueP = new BooleanParameter(PARAM_VALUE, "Value", false);
valueP.setIndex(100);
telegramR.addParameter(valueP);
add(exportList, telegramR);
logicalDevice.addRelation(PARAM_TELEGRAMS, telegramR);
logger.info("Added Boolean PlcTelegram " + telegramR.getId() + " " + telegramR.getName()
+ " for address " + address);
} }
case "Input" -> {
// validate address exists for this address if (isEmpty(resource))
if (exportList.values().stream().filter(e -> e.getType().equals(TYPE_PLC_ADDRESS)) throw new IllegalStateException("resource missing for: " + record);
.noneMatch(e -> e.getString(PARAM_ADDRESS).equals(address))) { if (isEmpty(action1))
throw new IllegalStateException("action1 missing for: " + record);
if (isEmpty(connection))
throw new IllegalStateException("connection missing for: " + record);
if (logicalDevice == null)
throw new IllegalStateException(
"No PlcLogicalDevice exists for address with keys " + resource + "-" + action1);
String subType = record.get("SubType").trim();
if (isEmpty(connection))
throw new IllegalStateException("SubType missing for: " + record);
String address = evaluateAddress(subType, record, connection);
Resource addressR = addressT.getClone(); Resource addressR = addressT.getClone();
addressR.setId("A_" + key); addressR.setId("A_" + key);
@ -255,76 +152,52 @@ public class PlcAddressGenerator {
addressR.setString(PARAM_ADDRESS, address); addressR.setString(PARAM_ADDRESS, address);
addressR.setString(PARAM_RESOURCE, resource); addressR.setString(PARAM_RESOURCE, resource);
addressR.setString(PARAM_ACTION, action1); addressR.setString(PARAM_ACTION, action1);
if (record.isSet("Inverted"))
addressR.setBoolean(PARAM_INVERTED, Boolean.parseBoolean(record.get("Inverted").trim()));
if (record.isSet("Remote")) if (record.isSet("Remote"))
addressR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim())); addressR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim()));
addressR.setInteger(PARAM_INDEX, addressIndex); addressR.setInteger(PARAM_INDEX, addressIndex);
addressIndex += 10; addressIndex += 10;
valueP = new BooleanParameter(PARAM_VALUE, "Value", false); BooleanParameter valueP = new BooleanParameter(PARAM_VALUE, "Value", false);
valueP.setIndex(100); valueP.setIndex(100);
addressR.addParameter(valueP); addressR.addParameter(valueP);
add(exportList, addressR); add(exportList, addressR);
logicalDevice.addRelation(PARAM_ADDRESSES, addressR); logicalDevice.addRelation(PARAM_ADDRESSES, addressR);
logger.info("Added missing Boolean PlcAddress " + addressR.getId() + " " + addressR.getName() logger.info("Added Boolean PlcAddress {} {} for address {}", addressR.getId(),
+ " for address " + address); addressR.getName(), address);
} }
} case "Output" -> {
case "Virtual" -> {
if (isEmpty(resource)) if (isEmpty(resource))
throw new IllegalStateException("resource missing for: " + record); throw new IllegalStateException("resource missing for: " + record);
if (isEmpty(action1)) if (isEmpty(action1))
throw new IllegalStateException("action1 missing for: " + record); throw new IllegalStateException("action1 missing for: " + record);
if (isEmpty(connection)) if (isEmpty(connection))
throw new IllegalStateException("connection missing for: " + record); throw new IllegalStateException("connection missing for: " + record);
if (logicalDevice == null) if (logicalDevice == null)
throw new IllegalStateException( throw new IllegalStateException(
"No PlcLogicalDevice exists for address with keys " + resource + "-" + action1); "No PlcLogicalDevice exists for address with keys " + resource + "-" + action1);
String subType = record.get("SubType").trim(); String subType = record.get("SubType").trim();
if (isEmpty(connection)) if (isEmpty(connection))
throw new IllegalStateException("SubType missing for: " + record); throw new IllegalStateException("SubType missing for: " + record);
Resource telegramR; String address = evaluateAddress(subType, record, connection);
String value = record.isSet("Value") ? record.get("Value") : null; Resource telegramR;
BooleanParameter valueP;
switch (subType) { // action1 value
case "Boolean" -> {
// address for virtual boolean
Resource addressR = addressT.getClone();
addressR.setId("A_" + key);
addressR.setName(keyName);
addressR.setString(PARAM_DESCRIPTION, description);
addressR.setString(PARAM_ADDRESS, connection);
addressR.setString(PARAM_RESOURCE, resource);
addressR.setString(PARAM_ACTION, action1);
if (record.isSet("Remote"))
addressR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim()));
addressR.setInteger(PARAM_INDEX, addressIndex);
addressIndex += 10;
Parameter<?> valueP = new BooleanParameter(PARAM_VALUE, "Value", false);
valueP.setIndex(100);
addressR.addParameter(valueP);
add(exportList, addressR);
logicalDevice.addRelation(PARAM_ADDRESSES, addressR);
logger.info("Added Virtual Boolean PlcAddress " + addressR.getId() + " " + addressR.getName()
+ " for connection " + connection);
// telegram for action1
telegramR = telegramT.getClone(); telegramR = telegramT.getClone();
telegramR.setId("T_" + key); telegramR.setId("T_" + key);
telegramR.setName(keyName); telegramR.setName(keyName);
telegramR.setString(PARAM_DESCRIPTION, description); telegramR.setString(PARAM_DESCRIPTION, description);
telegramR.setString(PARAM_ADDRESS, connection); telegramR.setString(PARAM_ADDRESS, address);
telegramR.setString(PARAM_RESOURCE, resource); telegramR.setString(PARAM_RESOURCE, resource);
telegramR.setString(PARAM_ACTION, action1); telegramR.setString(PARAM_ACTION, action1);
if (record.isSet("Remote")) if (record.isSet("Remote"))
@ -339,20 +212,19 @@ public class PlcAddressGenerator {
add(exportList, telegramR); add(exportList, telegramR);
logicalDevice.addRelation(PARAM_TELEGRAMS, telegramR); logicalDevice.addRelation(PARAM_TELEGRAMS, telegramR);
logger.info("Added Virtual Boolean PlcTelegram " + telegramR.getId() + " " + telegramR.getName() logger.info("Added Boolean PlcTelegram {} {} for address {}", telegramR.getId(),
+ " for connection " + connection); telegramR.getName(), address);
// telegram for action2 // action2 value
if (record.isSet("Action2") && isNotEmpty(record.get("Action2").trim())) { if (record.isSet("Action2") && isNotEmpty(record.get("Action2").trim())) {
String action2 = record.get("Action2").trim(); String action2 = record.get("Action2").trim();
key = resource + "-" + action2;
keyName = resource + " - " + action2;
telegramR = telegramT.getClone(); telegramR = telegramT.getClone();
telegramR.setId("T_" + key); telegramR.setId("T_" + resource + "-" + action2);
telegramR.setName(keyName); telegramR.setName(resource + " - " + action2);
telegramR.setString(PARAM_DESCRIPTION, description); telegramR.setString(PARAM_DESCRIPTION, description);
telegramR.setString(PARAM_ADDRESS, connection); telegramR.setString(PARAM_ADDRESS, address);
telegramR.setString(PARAM_RESOURCE, resource); telegramR.setString(PARAM_RESOURCE, resource);
telegramR.setString(PARAM_ACTION, action2); telegramR.setString(PARAM_ACTION, action2);
if (record.isSet("Remote")) if (record.isSet("Remote"))
@ -367,238 +239,378 @@ public class PlcAddressGenerator {
add(exportList, telegramR); add(exportList, telegramR);
logicalDevice.addRelation(PARAM_TELEGRAMS, telegramR); logicalDevice.addRelation(PARAM_TELEGRAMS, telegramR);
logger.info( logger.info("Added Boolean PlcTelegram {} {} for address {}", telegramR.getId(),
"Added Virtual Boolean PlcTelegram " + telegramR.getId() + " " + telegramR.getName() telegramR.getName(), address);
+ " for connection " + connection); }
// validate address exists for this address
if (exportList
.values()
.stream()
.filter(e -> e.getType().equals(TYPE_PLC_ADDRESS))
.noneMatch(e -> e.getString(PARAM_ADDRESS).equals(address))) {
Resource addressR = addressT.getClone();
addressR.setId("A_" + key);
addressR.setName(keyName);
addressR.setString(PARAM_DESCRIPTION, description);
addressR.setString(PARAM_ADDRESS, address);
addressR.setString(PARAM_RESOURCE, resource);
addressR.setString(PARAM_ACTION, action1);
if (record.isSet("Remote"))
addressR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim()));
addressR.setInteger(PARAM_INDEX, addressIndex);
addressIndex += 10;
valueP = new BooleanParameter(PARAM_VALUE, "Value", false);
valueP.setIndex(100);
addressR.addParameter(valueP);
add(exportList, addressR);
logicalDevice.addRelation(PARAM_ADDRESSES, addressR);
logger.info("Added missing Boolean PlcAddress {} {} for address {}", addressR.getId(),
addressR.getName(), address);
} }
} }
case "String" -> { case "Virtual" -> {
// address for virtual string if (isEmpty(resource))
Resource addressR = addressT.getClone(); throw new IllegalStateException("resource missing for: " + record);
if (isEmpty(action1))
throw new IllegalStateException("action1 missing for: " + record);
if (isEmpty(connection))
throw new IllegalStateException("connection missing for: " + record);
if (logicalDevice == null)
throw new IllegalStateException(
"No PlcLogicalDevice exists for address with keys " + resource + "-" + action1);
String subType = record.get("SubType").trim();
if (isEmpty(connection))
throw new IllegalStateException("SubType missing for: " + record);
Resource telegramR;
String value = record.isSet("Value") ? record.get("Value") : null;
switch (subType) {
case "Boolean" -> {
// address for virtual boolean
Resource addressR = addressT.getClone();
addressR.setId("A_" + key);
addressR.setName(keyName);
addressR.setString(PARAM_DESCRIPTION, description);
addressR.setString(PARAM_ADDRESS, connection);
addressR.setString(PARAM_RESOURCE, resource);
addressR.setString(PARAM_ACTION, action1);
if (record.isSet("Remote"))
addressR.setBoolean(PARAM_REMOTE,
Boolean.parseBoolean(record.get("Remote").trim()));
addressR.setInteger(PARAM_INDEX, addressIndex);
addressIndex += 10;
Parameter<?> valueP = new BooleanParameter(PARAM_VALUE, "Value", false);
valueP.setIndex(100);
addressR.addParameter(valueP);
add(exportList, addressR);
logicalDevice.addRelation(PARAM_ADDRESSES, addressR);
logger.info("Added Virtual Boolean PlcAddress {} {} for connection {}",
addressR.getId(), addressR.getName(), connection);
// telegram for action1
telegramR = telegramT.getClone();
telegramR.setId("T_" + key);
telegramR.setName(keyName);
telegramR.setString(PARAM_DESCRIPTION, description);
telegramR.setString(PARAM_ADDRESS, connection);
telegramR.setString(PARAM_RESOURCE, resource);
telegramR.setString(PARAM_ACTION, action1);
if (record.isSet("Remote"))
telegramR.setBoolean(PARAM_REMOTE,
Boolean.parseBoolean(record.get("Remote").trim()));
telegramR.setInteger(PARAM_INDEX, telegramIndex);
telegramIndex += 10;
valueP = new BooleanParameter(PARAM_VALUE, "Value", true);
valueP.setIndex(100);
telegramR.addParameter(valueP);
add(exportList, telegramR);
logicalDevice.addRelation(PARAM_TELEGRAMS, telegramR);
logger.info("Added Virtual Boolean PlcTelegram {} {} for connection {}",
telegramR.getId(), telegramR.getName(), connection);
// telegram for action2
if (record.isSet("Action2") && isNotEmpty(record.get("Action2").trim())) {
String action2 = record.get("Action2").trim();
key = resource + "-" + action2;
keyName = resource + " - " + action2;
telegramR = telegramT.getClone();
telegramR.setId("T_" + key);
telegramR.setName(keyName);
telegramR.setString(PARAM_DESCRIPTION, description);
telegramR.setString(PARAM_ADDRESS, connection);
telegramR.setString(PARAM_RESOURCE, resource);
telegramR.setString(PARAM_ACTION, action2);
if (record.isSet("Remote"))
telegramR.setBoolean(PARAM_REMOTE,
Boolean.parseBoolean(record.get("Remote").trim()));
telegramR.setInteger(PARAM_INDEX, telegramIndex);
telegramIndex += 10;
valueP = new BooleanParameter(PARAM_VALUE, "Value", false);
valueP.setIndex(100);
telegramR.addParameter(valueP);
add(exportList, telegramR);
logicalDevice.addRelation(PARAM_TELEGRAMS, telegramR);
logger.info("Added Virtual Boolean PlcTelegram {} {} for connection {}",
telegramR.getId(), telegramR.getName(), connection);
}
}
case "String" -> {
// address for virtual string
Resource addressR = addressT.getClone();
addressR.setId("A_" + key);
addressR.setName(keyName);
addressR.setString(PARAM_DESCRIPTION, description);
addressR.setString(PARAM_ADDRESS, connection);
addressR.setString(PARAM_RESOURCE, resource);
addressR.setString(PARAM_ACTION, action1);
if (record.isSet("Remote"))
addressR.setBoolean(PARAM_REMOTE,
Boolean.parseBoolean(record.get("Remote").trim()));
addressR.setInteger(PARAM_INDEX, addressIndex);
addressIndex += 10;
Parameter<?> valueP = new StringParameter(PARAM_VALUE, "Value",
value == null ? "" : value);
valueP.setIndex(100);
addressR.addParameter(valueP);
add(exportList, addressR);
logicalDevice.addRelation(PARAM_ADDRESSES, addressR);
logger.info("Added Virtual String PlcAddress {} {} for connection {}", addressR.getId(),
addressR.getName(), connection);
// telegram for action1
telegramR = telegramT.getClone();
telegramR.setId("T_" + key);
telegramR.setName(keyName);
telegramR.setString(PARAM_DESCRIPTION, description);
telegramR.setString(PARAM_ADDRESS, connection);
telegramR.setString(PARAM_RESOURCE, resource);
telegramR.setString(PARAM_ACTION, action1);
if (record.isSet("Remote"))
telegramR.setBoolean(PARAM_REMOTE,
Boolean.parseBoolean(record.get("Remote").trim()));
telegramR.setInteger(PARAM_INDEX, telegramIndex);
telegramIndex += 10;
valueP = new StringParameter(PARAM_VALUE, "Value", "");
valueP.setIndex(100);
telegramR.addParameter(valueP);
add(exportList, telegramR);
logicalDevice.addRelation(PARAM_TELEGRAMS, telegramR);
logger.info("Added Virtual String PlcTelegram {} {} for connection {}",
telegramR.getId(), telegramR.getName(), connection);
}
case "Integer" -> {
// address for virtual integer
Resource addressR = addressT.getClone();
addressR.setId("A_" + key);
addressR.setName(keyName);
addressR.setString(PARAM_DESCRIPTION, description);
addressR.setString(PARAM_ADDRESS, connection);
addressR.setString(PARAM_RESOURCE, resource);
addressR.setString(PARAM_ACTION, action1);
if (record.isSet("Remote"))
addressR.setBoolean(PARAM_REMOTE,
Boolean.parseBoolean(record.get("Remote").trim()));
addressR.setInteger(PARAM_INDEX, addressIndex);
addressIndex += 10;
Parameter<?> valueP = new IntegerParameter(PARAM_VALUE, "Value",
value == null ? 0 : parseInt(record.get("Value")));
valueP.setIndex(100);
addressR.addParameter(valueP);
add(exportList, addressR);
logicalDevice.addRelation(PARAM_ADDRESSES, addressR);
logger.info("Added Virtual Integer PlcAddress {} {} for connection {}",
addressR.getId(), addressR.getName(), connection);
// telegram for action1
telegramR = telegramT.getClone();
telegramR.setId("T_" + key);
telegramR.setName(keyName);
telegramR.setString(PARAM_DESCRIPTION, description);
telegramR.setString(PARAM_ADDRESS, connection);
telegramR.setString(PARAM_RESOURCE, resource);
telegramR.setString(PARAM_ACTION, action1);
if (record.isSet("Remote"))
telegramR.setBoolean(PARAM_REMOTE,
Boolean.parseBoolean(record.get("Remote").trim()));
telegramR.setInteger(PARAM_INDEX, telegramIndex);
telegramIndex += 10;
valueP = new IntegerParameter(PARAM_VALUE, "Value",
value == null ? 0 : parseInt(record.get("Value")));
valueP.setIndex(100);
telegramR.addParameter(valueP);
add(exportList, telegramR);
logicalDevice.addRelation(PARAM_TELEGRAMS, telegramR);
logger.info("Added Virtual Integer PlcTelegram {} {} for connection {}",
telegramR.getId(), telegramR.getName(), connection);
}
default -> throw new IllegalArgumentException(
"Unhandled virtual connection " + connection + " for " + resource + "-" + action1);
}
}
case "DataLogicScanner" -> {
if (isEmpty(resource))
throw new IllegalStateException("resource missing for: " + record);
if (logicalDevice == null)
throw new IllegalStateException(
"No PlcLogicalDevice exists for address with keys " + resource + "-" + action1);
Resource addressR;
Resource telegramR;
// address for barcode
key = resource + "-Barcode";
keyName = resource + " - Barcode";
addressR = addressT.getClone();
addressR.setId("A_" + key); addressR.setId("A_" + key);
addressR.setName(keyName); addressR.setName(keyName);
addressR.setString(PARAM_DESCRIPTION, description); addressR.setString(PARAM_DESCRIPTION, description);
addressR.setString(PARAM_ADDRESS, connection); addressR.setString(PARAM_ADDRESS, connection + ".barcode");
addressR.setString(PARAM_RESOURCE, resource); addressR.setString(PARAM_RESOURCE, resource);
addressR.setString(PARAM_ACTION, action1); addressR.setString(PARAM_ACTION, "Barcode");
if (record.isSet("Remote")) if (record.isSet("Remote"))
addressR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim())); addressR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim()));
addressR.setInteger(PARAM_INDEX, addressIndex); addressR.setInteger(PARAM_INDEX, addressIndex);
addressIndex += 10; addressIndex += 10;
Parameter<?> valueP = new StringParameter(PARAM_VALUE, "Value", value == null ? "" : value); StringParameter valueP = new StringParameter(PARAM_VALUE, "Value", "");
valueP.setIndex(100); valueP.setIndex(100);
addressR.addParameter(valueP); addressR.addParameter(valueP);
add(exportList, addressR); add(exportList, addressR);
logicalDevice.addRelation(PARAM_ADDRESSES, addressR); logicalDevice.addRelation(PARAM_ADDRESSES, addressR);
logger.info("Added Virtual String PlcAddress " + addressR.getId() + " " + addressR.getName() logger.info("Added DataLogicScanner PlcAddress {} {} for connection {}", addressR.getId(),
+ " for connection " + connection); addressR.getName(), connection);
// telegram for action1 // address for on
telegramR = telegramT.getClone(); key = resource + "-On";
telegramR.setId("T_" + key); keyName = resource + " - On";
telegramR.setName(keyName); addressR = addressT.getClone();
telegramR.setString(PARAM_DESCRIPTION, description);
telegramR.setString(PARAM_ADDRESS, connection);
telegramR.setString(PARAM_RESOURCE, resource);
telegramR.setString(PARAM_ACTION, action1);
if (record.isSet("Remote"))
telegramR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim()));
telegramR.setInteger(PARAM_INDEX, telegramIndex);
telegramIndex += 10;
valueP = new StringParameter(PARAM_VALUE, "Value", "");
valueP.setIndex(100);
telegramR.addParameter(valueP);
add(exportList, telegramR);
logicalDevice.addRelation(PARAM_TELEGRAMS, telegramR);
logger.info("Added Virtual String PlcTelegram " + telegramR.getId() + " " + telegramR.getName()
+ " for connection " + connection);
}
case "Integer" -> {
// address for virtual integer
Resource addressR = addressT.getClone();
addressR.setId("A_" + key); addressR.setId("A_" + key);
addressR.setName(keyName); addressR.setName(keyName);
addressR.setString(PARAM_DESCRIPTION, description); addressR.setString(PARAM_DESCRIPTION, description);
addressR.setString(PARAM_ADDRESS, connection); addressR.setString(PARAM_ADDRESS, connection + ".trigger");
addressR.setString(PARAM_RESOURCE, resource); addressR.setString(PARAM_RESOURCE, resource);
addressR.setString(PARAM_ACTION, action1); addressR.setString(PARAM_ACTION, "On");
if (record.isSet("Remote")) if (record.isSet("Remote"))
addressR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim())); addressR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim()));
addressR.setInteger(PARAM_INDEX, addressIndex); addressR.setInteger(PARAM_INDEX, addressIndex);
addressIndex += 10; addressIndex += 10;
Parameter<?> valueP = new IntegerParameter(PARAM_VALUE, "Value", BooleanParameter booleanValueP = new BooleanParameter(PARAM_VALUE, "Value", false);
value == null ? 0 : parseInt(record.get("Value"))); booleanValueP.setIndex(100);
valueP.setIndex(100); addressR.addParameter(booleanValueP);
addressR.addParameter(valueP);
add(exportList, addressR); add(exportList, addressR);
logicalDevice.addRelation(PARAM_ADDRESSES, addressR); logicalDevice.addRelation(PARAM_ADDRESSES, addressR);
logger.info("Added Virtual Integer PlcAddress " + addressR.getId() + " " + addressR.getName() logger.info("Added DataLogicScanner PlcAddress {} {} for connection {}", addressR.getId(),
+ " for connection " + connection); addressR.getName(), connection);
// telegram for action1 // telegram for trigger on
telegramR = telegramT.getClone(); telegramR = telegramT.getClone();
telegramR.setId("T_" + key); telegramR.setId("T_" + key);
telegramR.setName(keyName); telegramR.setName(keyName);
telegramR.setString(PARAM_DESCRIPTION, description); telegramR.setString(PARAM_DESCRIPTION, description);
telegramR.setString(PARAM_ADDRESS, connection); telegramR.setString(PARAM_ADDRESS, connection + ".trigger");
telegramR.setString(PARAM_RESOURCE, resource); telegramR.setString(PARAM_RESOURCE, resource);
telegramR.setString(PARAM_ACTION, action1); telegramR.setString(PARAM_ACTION, "On");
if (record.isSet("Remote")) if (record.isSet("Remote"))
telegramR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim())); telegramR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim()));
telegramR.setInteger(PARAM_INDEX, telegramIndex); telegramR.setInteger(PARAM_INDEX, telegramIndex);
telegramIndex += 10; telegramIndex += 10;
valueP = new IntegerParameter(PARAM_VALUE, "Value", booleanValueP = new BooleanParameter(PARAM_VALUE, "Value", true);
value == null ? 0 : parseInt(record.get("Value"))); booleanValueP.setIndex(100);
valueP.setIndex(100); telegramR.addParameter(booleanValueP);
telegramR.addParameter(valueP);
add(exportList, telegramR); add(exportList, telegramR);
logicalDevice.addRelation(PARAM_TELEGRAMS, telegramR); logicalDevice.addRelation(PARAM_TELEGRAMS, telegramR);
logger.info("Added Virtual Integer PlcTelegram " + telegramR.getId() + " " + telegramR.getName() logger.info("Added DataLogicScanner PlcTelegram {} {} for connection {}", telegramR.getId(),
+ " for connection " + connection); telegramR.getName(), connection);
// telegram for trigger off
key = resource + "-Off";
keyName = resource + " - Off";
telegramR = telegramT.getClone();
telegramR.setId("T_" + key);
telegramR.setName(keyName);
telegramR.setString(PARAM_DESCRIPTION, description);
telegramR.setString(PARAM_ADDRESS, connection + ".trigger");
telegramR.setString(PARAM_RESOURCE, resource);
telegramR.setString(PARAM_ACTION, "Off");
if (record.isSet("Remote"))
telegramR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim()));
telegramR.setInteger(PARAM_INDEX, telegramIndex);
telegramIndex += 10;
booleanValueP = new BooleanParameter(PARAM_VALUE, "Value", false);
booleanValueP.setIndex(100);
telegramR.addParameter(booleanValueP);
add(exportList, telegramR);
logicalDevice.addRelation(PARAM_TELEGRAMS, telegramR);
logger.info("Added DataLogicScanner PlcTelegram {} {} for connection {}", telegramR.getId(),
telegramR.getName(), connection);
} }
default -> throw new IllegalArgumentException( default -> throw new IllegalStateException("Unhandled type " + type + " for " + record);
"Unhandled virtual connection " + connection + " for " + resource + "-" + action1);
}
}
case "DataLogicScanner" -> {
if (isEmpty(resource))
throw new IllegalStateException("resource missing for: " + record);
if (logicalDevice == null)
throw new IllegalStateException(
"No PlcLogicalDevice exists for address with keys " + resource + "-" + action1);
Resource addressR;
Resource telegramR;
// address for barcode
key = resource + "-Barcode";
keyName = resource + " - Barcode";
addressR = addressT.getClone();
addressR.setId("A_" + key);
addressR.setName(keyName);
addressR.setString(PARAM_DESCRIPTION, description);
addressR.setString(PARAM_ADDRESS, connection + ".barcode");
addressR.setString(PARAM_RESOURCE, resource);
addressR.setString(PARAM_ACTION, "Barcode");
if (record.isSet("Remote"))
addressR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim()));
addressR.setInteger(PARAM_INDEX, addressIndex);
addressIndex += 10;
StringParameter valueP = new StringParameter(PARAM_VALUE, "Value", "");
valueP.setIndex(100);
addressR.addParameter(valueP);
add(exportList, addressR);
logicalDevice.addRelation(PARAM_ADDRESSES, addressR);
logger.info("Added DataLogicScanner PlcAddress " + addressR.getId() + " " + addressR.getName()
+ " for connection " + connection);
// address for on
key = resource + "-On";
keyName = resource + " - On";
addressR = addressT.getClone();
addressR.setId("A_" + key);
addressR.setName(keyName);
addressR.setString(PARAM_DESCRIPTION, description);
addressR.setString(PARAM_ADDRESS, connection + ".trigger");
addressR.setString(PARAM_RESOURCE, resource);
addressR.setString(PARAM_ACTION, "On");
if (record.isSet("Remote"))
addressR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim()));
addressR.setInteger(PARAM_INDEX, addressIndex);
addressIndex += 10;
BooleanParameter booleanValueP = new BooleanParameter(PARAM_VALUE, "Value", false);
booleanValueP.setIndex(100);
addressR.addParameter(booleanValueP);
add(exportList, addressR);
logicalDevice.addRelation(PARAM_ADDRESSES, addressR);
logger.info("Added DataLogicScanner PlcAddress " + addressR.getId() + " " + addressR.getName()
+ " for connection " + connection);
// telegram for trigger on
telegramR = telegramT.getClone();
telegramR.setId("T_" + key);
telegramR.setName(keyName);
telegramR.setString(PARAM_DESCRIPTION, description);
telegramR.setString(PARAM_ADDRESS, connection + ".trigger");
telegramR.setString(PARAM_RESOURCE, resource);
telegramR.setString(PARAM_ACTION, "On");
if (record.isSet("Remote"))
telegramR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim()));
telegramR.setInteger(PARAM_INDEX, telegramIndex);
telegramIndex += 10;
booleanValueP = new BooleanParameter(PARAM_VALUE, "Value", true);
booleanValueP.setIndex(100);
telegramR.addParameter(booleanValueP);
add(exportList, telegramR);
logicalDevice.addRelation(PARAM_TELEGRAMS, telegramR);
logger.info("Added DataLogicScanner PlcTelegram " + telegramR.getId() + " " + telegramR.getName()
+ " for connection " + connection);
// telegram for trigger off
key = resource + "-Off";
keyName = resource + " - Off";
telegramR = telegramT.getClone();
telegramR.setId("T_" + key);
telegramR.setName(keyName);
telegramR.setString(PARAM_DESCRIPTION, description);
telegramR.setString(PARAM_ADDRESS, connection + ".trigger");
telegramR.setString(PARAM_RESOURCE, resource);
telegramR.setString(PARAM_ACTION, "Off");
if (record.isSet("Remote"))
telegramR.setBoolean(PARAM_REMOTE, Boolean.parseBoolean(record.get("Remote").trim()));
telegramR.setInteger(PARAM_INDEX, telegramIndex);
telegramIndex += 10;
booleanValueP = new BooleanParameter(PARAM_VALUE, "Value", false);
booleanValueP.setIndex(100);
telegramR.addParameter(booleanValueP);
add(exportList, telegramR);
logicalDevice.addRelation(PARAM_TELEGRAMS, telegramR);
logger.info("Added DataLogicScanner PlcTelegram " + telegramR.getId() + " " + telegramR.getName()
+ " for connection " + connection);
}
default -> throw new IllegalStateException("Unhandled type " + type + " for " + record);
} }
} }
} }
// validate // validate
boolean valid = true; boolean valid = true;
MapOfLists<String, Resource> elementsByAddress = exportList.values().stream() MapOfLists<String, Resource> elementsByAddress = exportList
.values()
.stream()
.filter(e -> !e.getType().equals(TYPE_PLC_LOGICAL_DEVICE)) .filter(e -> !e.getType().equals(TYPE_PLC_LOGICAL_DEVICE))
.collect(MapOfLists::new, (m, e) -> m.addElement(e.getString(PARAM_ADDRESS), e), MapOfLists::addAll); .collect(MapOfLists::new, (m, e) -> m.addElement(e.getString(PARAM_ADDRESS), e), MapOfLists::addAll);
for (String address : elementsByAddress.keySet()) { for (String address : elementsByAddress.keySet()) {
@ -608,21 +620,21 @@ public class PlcAddressGenerator {
List<Resource> addresses = elements.stream().filter(e -> e.getType().equals(TYPE_PLC_ADDRESS)).toList(); List<Resource> addresses = elements.stream().filter(e -> e.getType().equals(TYPE_PLC_ADDRESS)).toList();
if (addresses.size() > 1) { if (addresses.size() > 1) {
logger.warn("Multiple elements with address " + address); logger.warn("Multiple elements with address {}", address);
for (Resource o : elements) { for (Resource o : elements) {
logger.warn("\t" + o.getId() + " " + o.getName()); logger.warn("\t{} {}", o.getId(), o.getName());
} }
valid = false; valid = false;
} }
List<Resource> telegrams = elements.stream().filter(e -> e.getType().equals(TYPE_PLC_TELEGRAM)).toList(); List<Resource> telegrams = elements.stream().filter(e -> e.getType().equals(TYPE_PLC_TELEGRAM)).toList();
StrolchValueType valueType = addresses.get(0).getParameter(PARAM_VALUE, true).getValueType(); StrolchValueType valueType = addresses.getFirst().getParameter(PARAM_VALUE, true).getValueType();
if (valueType == StrolchValueType.BOOLEAN) { if (valueType == StrolchValueType.BOOLEAN) {
if (telegrams.size() != 2) { if (telegrams.size() != 2) {
logger.error("Expected to have 2 telegrams for Boolean address " + address + ", but there are: " logger.error("Expected to have 2 telegrams for Boolean address {}, but there are: {}", address,
+ telegrams.size()); telegrams.size());
for (Resource telegram : telegrams) { for (Resource telegram : telegrams) {
logger.error("\t" + telegram.getId() + " " + telegram.getName()); logger.error("\t{} {}", telegram.getId(), telegram.getName());
} }
valid = false; valid = false;
} else { } else {
@ -632,26 +644,26 @@ public class PlcAddressGenerator {
boolean value2 = telegram2.getBoolean(PARAM_VALUE); boolean value2 = telegram2.getBoolean(PARAM_VALUE);
if (!value1 || value2) { if (!value1 || value2) {
logger.error("Unexpected values for telegrams: "); logger.error("Unexpected values for telegrams: ");
logger.error("\t" + value1 + " for " + telegram1.getId() + " " + telegram1.getName()); logger.error("\t{} for {} {}", value1, telegram1.getId(), telegram1.getName());
logger.error("\t" + value2 + " for " + telegram2.getId() + " " + telegram2.getName()); logger.error("\t{} for {} {}", value2, telegram2.getId(), telegram2.getName());
valid = false; valid = false;
} }
} }
} else { } else {
logger.warn("No validation available for value type " + valueType + " and address " + address); logger.warn("No validation available for value type {} and address {}", valueType, address);
for (Resource element : elements) { for (Resource element : elements) {
logger.warn("\t" + element.getId() + " " + element.getName()); logger.warn("\t{} {}", element.getId(), element.getName());
} }
} }
} }
if (valid) if (valid)
logger.info("Validation ok of " + elementsByAddress.size() + " addresses"); logger.info("Validation ok of {} addresses", elementsByAddress.size());
else else
logger.error("At least one address is invalid!"); logger.error("At least one address is invalid!");
StrolchXmlHelper.writeToFile(exportFile, exportList.values()); StrolchXmlHelper.writeToFile(exportFile, exportList.values());
logger.info("Wrote " + exportList.size() + " elements to " + exportFile); logger.info("Wrote {} elements to {}", exportList.size(), exportFile);
} }
private String evaluateAddress(String subType, CSVRecord record, String connection) { private String evaluateAddress(String subType, CSVRecord record, String connection) {

View File

@ -13,6 +13,6 @@ public class PlcAddressGeneratorTest {
String importFile = "../example/data/strolch-plc-example.csv"; String importFile = "../example/data/strolch-plc-example.csv";
String exportFile = "../example/data/strolch-plc-example.xml"; String exportFile = "../example/data/strolch-plc-example.xml";
PlcAddressGenerator.main(new String[] { templatesFile, importFile, exportFile }); PlcAddressGenerator.main(new String[]{templatesFile, importFile, exportFile});
} }
} }