This commit is contained in:
2025-06-08 10:55:20 +02:00
commit 4b1f79e4d3
625 changed files with 61204 additions and 0 deletions

58
Dockerfile Executable file
View File

@@ -0,0 +1,58 @@
####
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
#
# Before building the container image run:
#
# mvn package
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/rest-client-quickstart-jvm .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/rest-client-quickstart-jvm
#
# If you want to include the debug port into your docker image
# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5050
#
# Then run the container using :
#
# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/rest-client-quickstart-jvm
#
###
#FROM artifactory.intern.gec.io/docker/eclipse-temurin:17-alpine
FROM nexus.alm.oncite.io/docker/ubi9/openjdk-21:1.20
#ARG RUN_JAVA_VERSION=1.3.8
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'
# Install java and the run-java script
# Also set up permissions for user `1001`
#RUN apk add --no-cache curl ca-certificates \
# && mkdir /deployments \
# && chown 1001 /deployments \
# && chmod "g+rwX" /deployments \
# && chown 1001:root /deployments \
# && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \
# && chown 1001 /deployments/run-java.sh \
# && chmod 540 /deployments/run-java.sh \
# && echo "securerandom.source=file:/dev/urandom" >> $JAVA_HOME/lib/security/java.security
# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size.
#ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/
COPY --chown=185 target/quarkus-app/*.jar /deployments/
COPY --chown=185 target/quarkus-app/app/ /deployments/app/
COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/
#EXPOSE 8080 5005
#USER 1001
#ENTRYPOINT [ "/deployments/run-java.sh" ]
EXPOSE 8080
USER 185
ENV JAVA_OPTS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"

2
lombok.config Executable file
View File

@@ -0,0 +1,2 @@
config.stopBubbling = true
lombok.addLombokGeneratedAnnotation = true

370
pom.xml Executable file
View File

@@ -0,0 +1,370 @@
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>io.gec.raw</groupId>
<artifactId>connector</artifactId>
<name>raw.connector</name>
<version>2.0.0-SNAPSHOT</version>
<properties>
<compiler-plugin.version>3.13.0</compiler-plugin.version>
<maven.compiler.parameters>true</maven.compiler.parameters>
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.14.4</quarkus.platform.version>
<surefire-plugin.version>3.5.1</surefire-plugin.version>
<timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyyMMddHHmm</maven.build.timestamp.format>
<lombok.version>1.18.34</lombok.version>
<maven-resources-plugin.version>3.3.1</maven-resources-plugin.version>
<mockito.version>5.14.1</mockito.version>
<sonar.jacoco.reportFile>${project.build.directory}/jacoco.exec</sonar.jacoco.reportFile>
<sonar.coverage.jacoco.xmlReportPaths>${project.build.directory}/site/jacoco/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
<sonar.coverage.exclusions>**/entity/**/*,**/dto/**/*,**/domain/**/*,**/flyway/**/*</sonar.coverage.exclusions>
<mapstruct.version>1.6.2</mapstruct.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- overwrite protobuf version due to high risk CVE-2024-7254 -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.25.5</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-flyway</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-config-yaml</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-cache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-health</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-scheduler</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>${maven-resources-plugin.version}</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
<scope>provided</scope>
</dependency>
<!-- testing -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-mockito</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-security</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jacoco</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<!-- external -->
<dependency>
<groupId>org.snmp4j</groupId>
<artifactId>snmp4j</artifactId>
<version>3.8.2</version>
</dependency>
<dependency>
<groupId>org.snmp4j</groupId>
<artifactId>snmp4j-agent</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.11.1</version>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency>
<groupId>com.hierynomus</groupId>
<artifactId>sshj</artifactId>
<version>0.39.0</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>org.slf4j.impl.MavenSimpleLoggerFactory</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-opentelemetry</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-jdbc</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/entity/**/*</exclude>
<exclude>**/dto/*</exclude>
<exclude>**/domain/*</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>pre-unit-test</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<skip>${skip.tests}</skip>
<append>true</append>
<exclClassLoaders>*QuarkusClassLoader</exclClassLoaders>
<destFile>${sonar.jacoco.reportFile}</destFile>
</configuration>
</execution>
<execution>
<id>pre-integration-test</id>
<goals>
<goal>prepare-agent-integration</goal>
</goals>
<configuration>
<skip>${skip.it}</skip>
<append>true</append>
<exclClassLoaders>*QuarkusClassLoader</exclClassLoaders>
<destFile>${sonar.jacoco.reportFile}</destFile>
</configuration>
</execution>
<execution>
<id>coverage-report</id>
<phase>post-integration-test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<dataFile>${sonar.jacoco.reportFile}</dataFile>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<properties>
<skipITs>false</skipITs>
<quarkus.native.enabled>true</quarkus.native.enabled>
<quarkus.package.jar.enabled>false</quarkus.package.jar.enabled>
</properties>
</profile>
</profiles>
</project>

View File

@@ -0,0 +1,44 @@
package io.gec.raw.connector.access.configworkitem.control;
import io.gec.raw.connector.access.configworkitem.domain.AccessConfig;
import io.gec.raw.connector.access.configworkitem.domain.AccessPinConfig;
import io.gec.raw.connector.access.configworkitem.dto.AccessConfigDTO;
import io.gec.raw.connector.access.connectionworkitem.dto.ConnectionWorkItemValueDTO;
import io.gec.raw.connector.exception.entity.SftpException;
import io.gec.raw.connector.ftp.control.FtpController;
import io.gec.raw.connector.ftp.domain.SftpData;
import io.gec.raw.connector.device.control.DeviceController;
import io.gec.raw.connector.device.entity.Device;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.List;
import java.util.UUID;
@ApplicationScoped
public class AccessConfigController {
@Inject
FtpController ftpController;
@Inject
DeviceController deviceController;
@Inject
AccessConfigProvider accessConfigProvider;
public void sendAccessConfig(AccessConfigDTO accessConfigDTO,
UUID commSvcDeviceId,
List<ConnectionWorkItemValueDTO> accessWorkItemValues) throws SftpException {
Device rootDevice = deviceController.getDeviceByCommSvcId(commSvcDeviceId);
SftpData sftpData = ftpController.createSftpData(accessWorkItemValues, rootDevice.getDeviceURL());
AccessConfig accessConfig = accessConfigProvider.generateAccessConfig(accessConfigDTO, rootDevice.getDescription());
for (AccessPinConfig accessPinConfig : accessConfig.getEmergencyPinConfigs()) {
ftpController.putEmergencyPinFile(sftpData, accessPinConfig);
}
for (AccessPinConfig accessPinConfig : accessConfig.getOneTimePinConfigs()) {
ftpController.putOneTimePinFile(sftpData, accessPinConfig);
}
ftpController.putAccessConfigFile(sftpData, accessConfig.getContent());
}
}

View File

@@ -0,0 +1,114 @@
package io.gec.raw.connector.access.configworkitem.control;
import io.gec.raw.connector.access.configworkitem.domain.AccessCodeEntry;
import io.gec.raw.connector.access.configworkitem.domain.AccessUserRole;
import io.gec.raw.connector.access.configworkitem.dto.AccessCardCodeDTO;
import io.gec.raw.connector.access.configworkitem.dto.CardCodeType;
import io.gec.raw.connector.access.configworkitem.dto.HandleDTO;
import io.gec.raw.connector.access.configworkitem.dto.KeypadDTO;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.*;
@ApplicationScoped
public class AccessConfigConverter {
public List<AccessCodeEntry> convertToAccessCodeEntries(List<KeypadDTO> keypads) {
if (keypads == null) {
return Collections.emptyList();
}
Map<String, List<AccessCardCodeDTO>> accessCardCodesMap = new LinkedHashMap<>();
for (KeypadDTO keypadDTO : keypads) {
addToAccessCardCodesMap(keypadDTO.getHandles(), accessCardCodesMap);
}
Map<CardCodeType, Map<String, AccessCodeEntry>> accessConfigData = new LinkedHashMap<>();
processAccessCardCodes(accessCardCodesMap, accessConfigData);
return accessConfigData.values().stream()
.flatMap(accessCodeEntryMap -> accessCodeEntryMap.values().stream())
.toList();
}
private void addToAccessCardCodesMap(List<HandleDTO> handles, Map<String, List<AccessCardCodeDTO>> accessCardCodesMap) {
if (handles == null) {
return;
}
for (HandleDTO handleDTO : handles) {
List<AccessCardCodeDTO> accessCardCodes = accessCardCodesMap.computeIfAbsent(
handleDTO.getAccessDeviceSerialNr(),
handleSerialNumber -> new ArrayList<>());
accessCardCodes.addAll(handleDTO.getAccessCardCodes());
}
}
private void processAccessCardCodes(Map<String, List<AccessCardCodeDTO>> accessCardCodesMap,
Map<CardCodeType, Map<String, AccessCodeEntry>> accessConfigData) {
for (Map.Entry<String, List<AccessCardCodeDTO>> cardCodeEntry : accessCardCodesMap.entrySet()) {
String handleSerialNumber = cardCodeEntry.getKey();
List<AccessCardCodeDTO> accessCardCodes = cardCodeEntry.getValue();
processAccessCardCodes(accessCardCodes, handleSerialNumber, accessConfigData);
}
}
private void processAccessCardCodes(List<AccessCardCodeDTO> accessCardCodes,
String handleSerialNumber,
Map<CardCodeType, Map<String, AccessCodeEntry>> accessConfigData) {
boolean isEmergencyPinCodeEntryCreated = false;
boolean isOneTimePinCodeEntryCreated = false;
for (AccessCardCodeDTO accessCardCodeDTO : accessCardCodes) {
CardCodeType type = accessCardCodeDTO.getType();
// Additional protection - one handle can have a maximum of one emergency PIN and a maximum of one one-time PIN
// If such protection is not applied before the connector receives this access configuration
// then only the first cardCode (PIN) from the list will be used.
if (!canCreateAccessCodeEntry(type, isEmergencyPinCodeEntryCreated, isOneTimePinCodeEntryCreated)) {
continue;
}
isEmergencyPinCodeEntryCreated |= type == CardCodeType.EMERGENCY_PIN;
isOneTimePinCodeEntryCreated |= type == CardCodeType.ONE_TIME_PIN;
String value = accessCardCodeDTO.getValue();
Map<String, AccessCodeEntry> accessCodeEntries = accessConfigData.computeIfAbsent(type, cardCodeType -> new LinkedHashMap<>());
AccessCodeEntry accessCodeEntry = accessCodeEntries.computeIfAbsent(value, cardCodeValue -> createAccessCodeEntry(accessCardCodeDTO));
accessCodeEntry.addHandleSerialNumber(handleSerialNumber);
}
}
private boolean canCreateAccessCodeEntry(CardCodeType type, boolean isEmergencyPinCodeEntryCreated, boolean isOneTimePinCodeEntryCreated) {
if (type == CardCodeType.EMERGENCY_PIN && isEmergencyPinCodeEntryCreated) {
return false;
}
if (type == CardCodeType.ONE_TIME_PIN && isOneTimePinCodeEntryCreated) {
return false;
}
return true;
}
private AccessCodeEntry createAccessCodeEntry(AccessCardCodeDTO accessCardCodeDTO) {
AccessCodeEntry accessCodeEntry = new AccessCodeEntry();
accessCodeEntry.setType(accessCardCodeDTO.getType());
accessCodeEntry.setValue(accessCardCodeDTO.getValue());
accessCodeEntry.setUserName(accessCardCodeDTO.getUserName());
accessCodeEntry.setAccessUserRole(getAccessUserRole(accessCardCodeDTO.getUserRole()));
accessCodeEntry.setValidationPeriod(accessCardCodeDTO.getValidationPeriod());
accessCodeEntry.setFromDatetime(accessCardCodeDTO.getFromDatetime());
return accessCodeEntry;
}
private AccessUserRole getAccessUserRole(String userRole) {
if (AccessUserRole.ACCESS_ACKNOWLEDGE.getRoleName().equals(userRole)) {
return AccessUserRole.ACCESS_ACKNOWLEDGE;
}
return AccessUserRole.ACCESS_USER;
}
}

View File

@@ -0,0 +1,275 @@
package io.gec.raw.connector.access.configworkitem.control;
import io.gec.raw.connector.access.configworkitem.domain.AccessCodeEntry;
import io.gec.raw.connector.access.configworkitem.domain.AccessConfig;
import io.gec.raw.connector.access.configworkitem.domain.AccessUserRole;
import io.gec.raw.connector.access.configworkitem.dto.*;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.apache.commons.codec.digest.Sha2Crypt;
import org.apache.commons.lang3.StringUtils;
import org.jboss.logging.Logger;
import java.time.Clock;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@ApplicationScoped
public class AccessConfigProvider {
@Inject
AccessConfigConverter accessConfigConverter;
@Inject
Clock clock;
@Inject
Logger logger;
private static final String ACCESS_FILE_HEADER = """
#------------- Access-File CMC-III -----------------
# Name :
# Location :
# Contact :
# IPv4-Address :
""";
private static final String TYPE_CODE_CONTENT_PATTERN = "Key:%s; User:%s; Information:%s; Handle:%s";
private static final String TYPE_CARD_CONTENT_PATTERN = "Crd:%s; User:%s; Information:%s; Handle:%s";
private static final String TYPE_EMERGENCY_PIN_CONTENT_PATTERN = "Key:emgy%s; User:%s; Information:%s; Handle:%s";
private static final String TYPE_ONE_TIME_PIN_CONTENT_PATTERN = "Key:code%s; User:%s; Information:%s; Handle:%s";
private static final String DEVICE_DESCRIPTION = "V3.17.30_5";
// AccessConfigDTO contains Access configuration, deviceDescription is used for switching between different CMC/PDU Firmware versions
public AccessConfig generateAccessConfig(AccessConfigDTO accessConfigDTO, String deviceDescription) {
AccessConfig accessConfig = new AccessConfig();
List<AccessCodeEntry> accessCodeEntries = accessConfigConverter.convertToAccessCodeEntries(accessConfigDTO.getKeypads());
String accessFileContent = prepareAccessConfigFileContent(accessConfigDTO, accessConfig, accessCodeEntries, deviceDescription);
accessConfig.setContent(accessFileContent);
logger.infof("Access config generated for Mgmt Device (ID:'%s')", accessConfigDTO.getMgmtDeviceComponentId());
return accessConfig;
}
private String prepareAccessConfigFileContent(AccessConfigDTO accessConfigDTO,
AccessConfig accessConfig,
List<AccessCodeEntry> accessCodeEntries,
String deviceDescription) {
/*return new StringBuilder()
.append(ACCESS_FILE_HEADER)
.append(generateOptions(accessConfigDTO.getOptions(), deviceDescription))
.append(generateAccessCodeEntries(accessCodeEntries, accessConfig))
.append(generateKeypads(accessConfigDTO.getKeypads())).toString();*/
return new StringBuilder()
.append(ACCESS_FILE_HEADER)
.append(generateOptions(accessConfigDTO.getOptions(), deviceDescription))
.append(generateAccessCodeEntries(accessCodeEntries, accessConfig)).toString();
}
private String generateOptions(OptionsDTO options, String deviceDescription) {
if (options == null) {
return StringUtils.EMPTY;
}
StringBuilder optionsContent = new StringBuilder();
if (deviceDescription.contains(DEVICE_DESCRIPTION)) {
if (options.isFourEyes()) {
optionsContent.append(String.format("4-Eyes:1,%s", options.getFourEyesTimeout())).append("\n");
}
if (options.isTwoFactor()) {
optionsContent.append(String.format("2-Factor:1,%s", options.getTwoFactorTimeout())).append("\n");
}
} else {
if (options.isFourEyes()) {
optionsContent.append(String.format("4-Eyes:%s", options.getFourEyesTimeout())).append("\n");
}
if (options.isTwoFactor()) {
optionsContent.append(String.format("2-Factor:%s", options.getTwoFactorTimeout())).append("\n");
}
}
return optionsContent.toString();
}
private String generateAccessCodeEntries(List<AccessCodeEntry> accessCodeEntries, AccessConfig accessConfig) {
if (accessCodeEntries.isEmpty()) {
return StringUtils.EMPTY;
}
StringBuilder accessCodesContent = new StringBuilder();
for (AccessCodeEntry accessCodeEntry : accessCodeEntries) {
switch (accessCodeEntry.getType()) {
case CODE -> addAccessCodeEntryForCodeType(accessCodesContent, accessCodeEntry);
case CARD -> addAccessCodeEntryForCardType(accessCodesContent, accessCodeEntry);
case EMERGENCY_PIN -> addAccessCodeEntryForEmergencyPinType(accessCodesContent, accessCodeEntry, accessConfig);
case ONE_TIME_PIN -> addAccessCodeEntryForOneTimePinType(accessCodesContent, accessCodeEntry, accessConfig);
}
}
return accessCodesContent.toString();
}
private void addAccessCodeEntryForCodeType(StringBuilder accessCodesContent, AccessCodeEntry accessCodeEntry) {
addAccessCodeEntry(
accessCodesContent,
TYPE_CODE_CONTENT_PATTERN,
accessCodeEntry.getValue(),
accessCodeEntry.getAccessUserRole(),
accessCodeEntry.getUserName(),
accessCodeEntry.getHandleSerialNumbers());
}
private void addAccessCodeEntryForCardType(StringBuilder accessCodesContent, AccessCodeEntry accessCodeEntry) {
// cardCode format in CMC and PDU is 8 digits + 8 leading zeros
final String cardCode = accessCodeEntry.getValue();
String cardCodeValue = cardCode.length() == 8
? "00000000" + cardCode
: cardCode;
addAccessCodeEntry(
accessCodesContent,
TYPE_CARD_CONTENT_PATTERN,
cardCodeValue,
accessCodeEntry.getAccessUserRole(),
accessCodeEntry.getUserName(),
accessCodeEntry.getHandleSerialNumbers());
}
private void addAccessCodeEntryForEmergencyPinType(StringBuilder accessCodesContent, AccessCodeEntry accessCodeEntry, AccessConfig accessConfig) {
for (String handleSerialNumber : accessCodeEntry.getHandleSerialNumbers()) {
final String pinFileContent = generatePinFileContent(accessCodeEntry.getValue());
accessConfig.addEmergencyPinConfig(handleSerialNumber, pinFileContent);
/*addAccessCodeEntry(
accessCodesContent,
TYPE_EMERGENCY_PIN_CONTENT_PATTERN,
handleSerialNumber,
AccessUserRole.ACCESS_USER,
accessCodeEntry.getUserName(),
Set.of(handleSerialNumber));*/
addAccessCodeEntry(
accessCodesContent,
TYPE_EMERGENCY_PIN_CONTENT_PATTERN,
handleSerialNumber,
AccessUserRole.ACCESS_USER,
accessCodeEntry.getUserName(),
accessCodeEntry.getHandleSerialNumbers());
}
}
private void addAccessCodeEntryForOneTimePinType(StringBuilder accessCodesContent, AccessCodeEntry accessCodeEntry, AccessConfig accessConfig) {
for (String handleSerialNumber : accessCodeEntry.getHandleSerialNumbers()) {
OffsetDateTime startDateTime = Optional.ofNullable(accessCodeEntry.getFromDatetime()).orElseGet(() -> OffsetDateTime.now(clock));
long expirationTime = getExpirationTime(accessCodeEntry.getValidationPeriod(), startDateTime);
if (isTimeExpired(expirationTime)) {
continue;
}
final String pinFileContent = generatePinFileContent(
accessCodeEntry.getValue(),
startDateTime.toInstant().toEpochMilli(),
expirationTime);
accessConfig.addOneTimePinConfig(handleSerialNumber, pinFileContent);
/*addAccessCodeEntry(
accessCodesContent,
TYPE_ONE_TIME_PIN_CONTENT_PATTERN,
handleSerialNumber,
AccessUserRole.ACCESS_USER,
accessCodeEntry.getUserName(),
Set.of(handleSerialNumber));*/
addAccessCodeEntry(
accessCodesContent,
TYPE_ONE_TIME_PIN_CONTENT_PATTERN,
handleSerialNumber,
AccessUserRole.ACCESS_USER,
accessCodeEntry.getUserName(),
accessCodeEntry.getHandleSerialNumbers());
}
}
private void addAccessCodeEntry(StringBuilder accessCodesContent,
String contentPattern,
String cardCodeValue,
AccessUserRole accessUserRole,
String userName,
Set<String> handleSerialNumbers) {
if (StringUtils.isNotEmpty(contentPattern) && StringUtils.isNotEmpty(cardCodeValue)) {
String handles = String.join(",", handleSerialNumbers);
accessCodesContent.append(contentPattern.formatted(
cardCodeValue,
accessUserRole.getRoleName(),
userName,
handles
)).append("\n");
}
}
private String generateKeypads(List<KeypadDTO> keypads) {
if (keypads == null) {
return StringUtils.EMPTY;
}
StringBuilder keypadsContent = new StringBuilder();
for (KeypadDTO keypadDTO : keypads) {
List<HandleDTO> handles = keypadDTO.getHandles();
String handleSerialNumbersValue = Optional.ofNullable(handles).stream()
.flatMap(Collection::stream)
.map(HandleDTO::getAccessDeviceSerialNr)
.collect(Collectors.joining(","));
if (!handleSerialNumbersValue.isEmpty()) {
keypadsContent.append(String.format("Keypad:%s; Handle:%s",
keypadDTO.getAccessDeviceSerialNr(),
handleSerialNumbersValue)).append("\n");
}
}
return keypadsContent.toString().trim();
}
private String generatePinFileContent(String accessCardCodeValue) {
return generatePinFileContent(accessCardCodeValue, 0L, -1L);
}
private String generatePinFileContent(String accessCardCodeValue, long startDateTime, long expirationTime) {
long startDateTimeInSeconds = TimeUnit.MILLISECONDS.toSeconds(startDateTime);
long expirationTimeInSeconds = expirationTime >= 0
? TimeUnit.MILLISECONDS.toSeconds(expirationTime)
: -1L; // forever
return Sha2Crypt.sha256Crypt(accessCardCodeValue.getBytes()) + "\t%d\t%d".formatted(startDateTimeInSeconds, expirationTimeInSeconds);
}
private long getExpirationTime(Integer validationPeriod, OffsetDateTime startDateTime) {
if (validationPeriod == null) {
return 0L; // one-time
}
if (validationPeriod == 0) {
return 0L; // one-time
}
if (validationPeriod < 0) {
return -1L; // forever
}
return startDateTime
.plusSeconds(TimeUnit.HOURS.toSeconds(validationPeriod))
.toInstant()
.toEpochMilli();
}
private boolean isTimeExpired(long expirationTime) {
return expirationTime > 0 && Instant.now(clock).toEpochMilli() > expirationTime;
}
}

View File

@@ -0,0 +1,104 @@
package io.gec.raw.connector.access.configworkitem.control;
import io.gec.raw.connector.access.configworkitem.dto.AccessDbDTO;
import io.gec.raw.connector.access.configworkitem.dto.HandleDbDTO;
import io.gec.raw.connector.access.configworkitem.dto.KeypadDbDTO;
import io.gec.raw.connector.access.configworkitem.dto.OptionDbDTO;
import io.gec.raw.connector.access.utils.ParserHelper;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
@ApplicationScoped
public class AccessDbConfigParser {
private static final String REGEX = "'(.*?)'";
private static final String NOT_SUPPORTED_ELEMENT_WITH_INDEX = "Not supported element with index: ";
private final Pattern pattern = Pattern.compile(REGEX, Pattern.MULTILINE);
@Inject
ParserHelper parserHelper;
public AccessDbDTO parseFileContent(Path filePath) throws IOException {
List<HandleDbDTO> handleList = new ArrayList<>();
List<KeypadDbDTO> keypadList = new ArrayList<>();
OptionDbDTO optionDTO = null;
try (BufferedReader br = new BufferedReader(new FileReader(filePath.toFile()))) {
String line;
while ((line = br.readLine()) != null) {
Matcher matcher = pattern.matcher(line);
if (line.contains("INSERT INTO Handles")) {
handleList.add(createHandleDTO(matcher.results()));
} else if (line.contains("INSERT INTO Keypads")) {
keypadList.add(createKeypadDTO(matcher.results()));
} else if (line.contains("INSERT INTO Options")) {
optionDTO = createOptionDTO(matcher.results());
}
}
}
AccessDbDTO accessDbDTO = new AccessDbDTO();
accessDbDTO.setHandles(handleList);
accessDbDTO.setKeypads(keypadList);
accessDbDTO.setOptions(optionDTO);
return accessDbDTO;
}
private OptionDbDTO createOptionDTO(Stream<MatchResult> results) {
OptionDbDTO optionDTO = new OptionDbDTO();
List<MatchResult> matchResultList = results.toList();
for (int i = 0; i < matchResultList.size(); i++) {
switch (i) {
case 0 -> optionDTO.setEyes4(parserHelper.toBoolean(matchResultList.get(i).group(1)));
case 1 -> optionDTO.setWay2(parserHelper.toBoolean(matchResultList.get(i).group(1)));
case 2 -> optionDTO.setTimeout1(Integer.parseInt(matchResultList.get(i).group(1)));
case 3 -> optionDTO.setTimeout2(Integer.parseInt(matchResultList.get(i).group(1)));
default -> throw new IllegalArgumentException(NOT_SUPPORTED_ELEMENT_WITH_INDEX + i + " in Options data");
}
}
return optionDTO;
}
private KeypadDbDTO createKeypadDTO(Stream<MatchResult> results) {
KeypadDbDTO keypadDTO = new KeypadDbDTO();
List<MatchResult> matchResultList = results.toList();
for (int i = 0; i < matchResultList.size(); i++) {
switch (i) {
case 0 -> keypadDTO.setKeypad(matchResultList.get(i).group(1));
case 1 -> keypadDTO.setHandleList(Arrays.asList(matchResultList.get(i).group(1).split(",")));
default -> throw new IllegalArgumentException(NOT_SUPPORTED_ELEMENT_WITH_INDEX + i + " in Keypads data");
}
}
return keypadDTO;
}
private HandleDbDTO createHandleDTO(Stream<MatchResult> results) {
HandleDbDTO handleDTO = new HandleDbDTO();
List<MatchResult> matchResultList = results.toList();
for (int i = 0; i < matchResultList.size(); i++) {
switch (i) {
case 0 -> handleDTO.setType(matchResultList.get(i).group(1));
case 1 -> handleDTO.setCode(matchResultList.get(i).group(1));
case 2 -> handleDTO.setUser(matchResultList.get(i).group(1));
case 3 -> handleDTO.setInformation(matchResultList.get(i).group(1));
case 4 -> handleDTO.setHandleList(Arrays.asList(matchResultList.get(i).group(1).split(",")));
default -> throw new IllegalArgumentException(NOT_SUPPORTED_ELEMENT_WITH_INDEX + i + " in Handles data");
}
}
return handleDTO;
}
}

View File

@@ -0,0 +1,28 @@
package io.gec.raw.connector.access.configworkitem.domain;
import io.gec.raw.connector.access.configworkitem.dto.CardCodeType;
import lombok.Getter;
import lombok.Setter;
import java.time.OffsetDateTime;
import java.util.LinkedHashSet;
import java.util.Set;
@Getter
@Setter
public class AccessCodeEntry {
private CardCodeType type;
private String value;
private String userName;
private AccessUserRole accessUserRole;
private Integer validationPeriod;
private OffsetDateTime fromDatetime;
private Set<String> handleSerialNumbers;
public void addHandleSerialNumber(String handleSerialNumber) {
if (handleSerialNumbers == null) {
handleSerialNumbers = new LinkedHashSet<>();
}
handleSerialNumbers.add(handleSerialNumber);
}
}

View File

@@ -0,0 +1,23 @@
package io.gec.raw.connector.access.configworkitem.domain;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Getter
public class AccessConfig {
@Setter
private String content;
private final List<AccessPinConfig> emergencyPinConfigs = new ArrayList<>();
private final List<AccessPinConfig> oneTimePinConfigs = new ArrayList<>();
public void addEmergencyPinConfig(String pinIdentifier, String content) {
emergencyPinConfigs.add(new AccessPinConfig(pinIdentifier, content));
}
public void addOneTimePinConfig(String pinIdentifier, String content) {
oneTimePinConfigs.add(new AccessPinConfig(pinIdentifier, content));
}
}

View File

@@ -0,0 +1,19 @@
package io.gec.raw.connector.access.configworkitem.domain;
import io.gec.raw.connector.access.configworkitem.dto.AccessConfigWorkItemStatus;
import io.gec.raw.connector.job.domain.WorkItemResult;
import lombok.Getter;
@Getter
public class AccessConfigWorkItemResult extends WorkItemResult {
private final AccessConfigWorkItemStatus status;
public AccessConfigWorkItemResult(AccessConfigWorkItemStatus status, String message) {
super(message);
this.status = status;
}
public AccessConfigWorkItemResult(AccessConfigWorkItemStatus status) {
this(status, null);
}
}

View File

@@ -0,0 +1,11 @@
package io.gec.raw.connector.access.configworkitem.domain;
import io.gec.raw.connector.access.configworkitem.dto.AccessConfigWorkItemStatus;
import java.util.UUID;
public record AccessConfigWorkItemStatusWrapper(UUID workItemId, AccessConfigWorkItemStatus status, String message) {
public AccessConfigWorkItemStatusWrapper(UUID workItemId, AccessConfigWorkItemStatus status) {
this(workItemId, status, null);
}
}

View File

@@ -0,0 +1,4 @@
package io.gec.raw.connector.access.configworkitem.domain;
public record AccessPinConfig(String pinIdentifier, String content) {
}

View File

@@ -0,0 +1,15 @@
package io.gec.raw.connector.access.configworkitem.domain;
import lombok.Getter;
@Getter
public enum AccessUserRole {
ACCESS_USER("AccessUser"),
ACCESS_ACKNOWLEDGE("AccessAck");
private final String roleName;
AccessUserRole(String roleName) {
this.roleName = roleName;
}
}

View File

@@ -0,0 +1,17 @@
package io.gec.raw.connector.access.configworkitem.dto;
import java.time.OffsetDateTime;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class AccessCardCodeDTO {
private CardCodeType type;
private String value;
private String userName;
private Integer validationPeriod;
private String userRole;
private OffsetDateTime fromDatetime;
}

View File

@@ -0,0 +1,15 @@
package io.gec.raw.connector.access.configworkitem.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.UUID;
@Getter
@Setter
public class AccessConfigDTO {
private UUID mgmtDeviceComponentId;
private OptionsDTO options;
private List<KeypadDTO> keypads;
}

View File

@@ -0,0 +1,17 @@
package io.gec.raw.connector.access.configworkitem.dto;
import io.gec.raw.connector.access.connectionworkitem.dto.ConnectionWorkItemDTO;
import io.gec.raw.connector.job.domain.WorkItem;
import lombok.Getter;
import lombok.Setter;
import java.util.UUID;
@Getter
@Setter
public class AccessConfigWorkItemDTO extends WorkItem {
private UUID id;
private AccessConfigWorkItemStatus status;
private AccessConfigDTO config;
private ConnectionWorkItemDTO connectionWorkItem;
}

View File

@@ -0,0 +1,5 @@
package io.gec.raw.connector.access.configworkitem.dto;
public enum AccessConfigWorkItemStatus {
CREATED, IN_PROGRESS, QUEUED, SUCCESS, DEVICE_CONFIG_FAILURE
}

View File

@@ -0,0 +1,14 @@
package io.gec.raw.connector.access.configworkitem.dto;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class AccessConfigWorkItemUpdateDTO {
private AccessConfigWorkItemStatus status;
private String message;
}

View File

@@ -0,0 +1,15 @@
package io.gec.raw.connector.access.configworkitem.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Setter
@Getter
public class AccessDbDTO {
List<HandleDbDTO> handles;
List<KeypadDbDTO> keypads;
OptionDbDTO options;
}

View File

@@ -0,0 +1,5 @@
package io.gec.raw.connector.access.configworkitem.dto;
public enum CardCodeType {
CARD, CODE, ONE_TIME_PIN, EMERGENCY_PIN
}

View File

@@ -0,0 +1,13 @@
package io.gec.raw.connector.access.configworkitem.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class HandleDTO {
private String accessDeviceSerialNr;
private List<AccessCardCodeDTO> accessCardCodes;
}

View File

@@ -0,0 +1,17 @@
package io.gec.raw.connector.access.configworkitem.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Setter
@Getter
public class HandleDbDTO {
private String type;
private String code;
private String user;
private String information;
private List<String> handleList;
}

View File

@@ -0,0 +1,13 @@
package io.gec.raw.connector.access.configworkitem.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class KeypadDTO {
private String accessDeviceSerialNr;
private List<HandleDTO> handles;
}

View File

@@ -0,0 +1,14 @@
package io.gec.raw.connector.access.configworkitem.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Setter
@Getter
public class KeypadDbDTO {
private String keypad;
private List<String> handleList;
}

View File

@@ -0,0 +1,14 @@
package io.gec.raw.connector.access.configworkitem.dto;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class OptionDbDTO {
private boolean eyes4;
private boolean way2;
private int timeout1;
private int timeout2;
}

View File

@@ -0,0 +1,13 @@
package io.gec.raw.connector.access.configworkitem.dto;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class OptionsDTO {
private boolean fourEyes;
private boolean twoFactor;
private int fourEyesTimeout;
private int twoFactorTimeout;
}

View File

@@ -0,0 +1,95 @@
package io.gec.raw.connector.access.configworkitem.handler;
import io.gec.raw.connector.access.configworkitem.control.AccessConfigController;
import io.gec.raw.connector.access.configworkitem.domain.AccessConfigWorkItemResult;
import io.gec.raw.connector.access.configworkitem.dto.AccessConfigWorkItemDTO;
import io.gec.raw.connector.access.configworkitem.dto.AccessConfigWorkItemStatus;
import io.gec.raw.connector.access.configworkitem.dto.AccessConfigWorkItemUpdateDTO;
import io.gec.raw.connector.access.configworkitem.domain.AccessConfigWorkItemStatusWrapper;
import io.gec.raw.connector.job.domain.JobResult;
import io.gec.raw.connector.job.domain.WorkItemWrapper;
import io.gec.raw.connector.job.dto.JobAction;
import io.gec.raw.connector.job.dto.JobStatus;
import io.gec.raw.connector.job.dto.JobType;
import io.gec.raw.connector.job.handler.WorkItemHandler;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.control.ActivateRequestContext;
import jakarta.inject.Inject;
import java.util.Optional;
import java.util.UUID;
@ApplicationScoped
public class AccessConfigJobHandler extends WorkItemHandler<AccessConfigWorkItemDTO, AccessConfigWorkItemResult> {
@Inject
AccessConfigController accessConfigController;
@Override
protected Optional<AccessConfigWorkItemDTO> getWorkItem(UUID workItemId) {
return communicationServiceController.fetchAccessConfigWorkItem(workItemId);
}
@Override
protected void startProcessing(WorkItemWrapper<AccessConfigWorkItemDTO> workItemWrapper) {
var accessConfigWorkItemStatus = new AccessConfigWorkItemStatusWrapper(workItemWrapper.workItemId(), AccessConfigWorkItemStatus.IN_PROGRESS);
updateAccessConfigWorkItemStatus(accessConfigWorkItemStatus);
}
@ActivateRequestContext
@Override
protected AccessConfigWorkItemResult processWorkItem(WorkItemWrapper<AccessConfigWorkItemDTO> workItemWrapper) {
try {
logger.infof("Processing AccessConfigWorkItem (ID:'%s') ...", workItemWrapper.workItemId());
AccessConfigWorkItemDTO workItem = workItemWrapper.workItem();
accessConfigController.sendAccessConfig(
workItem.getConfig(),
workItem.getConnectionWorkItem().getDeviceId(),
workItem.getConnectionWorkItem().getConnectionPropertyValues());
return new AccessConfigWorkItemResult(AccessConfigWorkItemStatus.SUCCESS);
} catch (Exception e) {
logger.errorf(e, "Processing AccessConfigWorkItem (ID:'%s') - FAILED", workItemWrapper.workItemId());
return new AccessConfigWorkItemResult(AccessConfigWorkItemStatus.DEVICE_CONFIG_FAILURE, e.getMessage());
}
}
@Override
protected void finishProcessing(WorkItemWrapper<AccessConfigWorkItemDTO> workItemWrapper, AccessConfigWorkItemResult workItemResult) {
var accessConfigWorkItemStatus = new AccessConfigWorkItemStatusWrapper(
workItemWrapper.workItemId(),
workItemResult.getStatus(),
workItemResult.getMessage());
updateAccessConfigWorkItemStatus(accessConfigWorkItemStatus);
}
@Override
protected JobResult mapToJobResult(AccessConfigWorkItemResult workItemResult) {
var workItemStatus = workItemResult.getStatus();
var jobStatus = switch (workItemStatus) {
case SUCCESS -> JobStatus.DONE;
case DEVICE_CONFIG_FAILURE -> JobStatus.FAILED;
default -> throw new IllegalStateException("Mapping to job result failed, unexpected value: " + workItemStatus);
};
return new JobResult(jobStatus, workItemResult.getMessage());
}
@Override
public JobType getJobType() {
return JobType.ACCESS_CONFIG;
}
@Override
public JobAction getJobAction() {
return JobAction.SET;
}
private void updateAccessConfigWorkItemStatus(AccessConfigWorkItemStatusWrapper accessConfigWorkItemStatus) {
var accessConfigWorkItemUpdateDTO = new AccessConfigWorkItemUpdateDTO();
accessConfigWorkItemUpdateDTO.setStatus(accessConfigWorkItemStatus.status());
accessConfigWorkItemUpdateDTO.setMessage(accessConfigWorkItemStatus.message());
communicationServiceController.updateAccessConfigWorkItem(accessConfigWorkItemStatus.workItemId(), accessConfigWorkItemUpdateDTO);
}
}

View File

@@ -0,0 +1,44 @@
package io.gec.raw.connector.access.configworkitem.utils;
public class Bcd {
public String decimalToBcd(long num) {
if (num < 0) throw new IllegalArgumentException(
"The method decimalToBcd doesn't support negative numbers." +
" Invalid argument: " + num);
int digits = 0;
long temp = num;
while (temp != 0) {
digits++;
temp /= 10;
}
int byteLen = digits % 2 == 0 ? digits / 2 : (digits + 1) / 2;
byte[] bcd = new byte[byteLen];
for (int i = 0; i < digits; i++) {
byte tmp = (byte) (num % 10);
if (i % 2 == 0) {
bcd[i / 2] = tmp;
} else {
bcd[i / 2] |= (byte) (tmp << 4);
}
num /= 10;
}
for (int i = 0; i < byteLen / 2; i++) {
byte tmp = bcd[i];
bcd[i] = bcd[byteLen - i - 1];
bcd[byteLen - i - 1] = tmp;
}
return new String(bcd);
}
}

View File

@@ -0,0 +1,19 @@
package io.gec.raw.connector.access.connectionworkitem.domain;
import io.gec.raw.connector.access.connectionworkitem.dto.ConnectionWorkItemStatus;
import io.gec.raw.connector.job.domain.WorkItemResult;
import lombok.Getter;
@Getter
public class ConnectionWorkItemResult extends WorkItemResult {
private final ConnectionWorkItemStatus status;
public ConnectionWorkItemResult(ConnectionWorkItemStatus status, String message) {
super(message);
this.status = status;
}
public ConnectionWorkItemResult(ConnectionWorkItemStatus status) {
this(status, null);
}
}

View File

@@ -0,0 +1,21 @@
package io.gec.raw.connector.access.connectionworkitem.dto;
import io.gec.raw.connector.job.domain.WorkItem;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.UUID;
@Getter
@Setter
public class ConnectionWorkItemDTO extends WorkItem {
private UUID protocolId;
private UUID deviceId;
private ConnectionWorkItemStatus status;
private List<ConnectionWorkItemValueDTO> connectionPropertyValues;
}

View File

@@ -0,0 +1,9 @@
package io.gec.raw.connector.access.connectionworkitem.dto;
public enum ConnectionWorkItemStatus {
CREATED,
IN_PROGRESS,
QUEUED,
SUCCESS,
DEVICE_CONNECTION_FAILURE
}

View File

@@ -0,0 +1,10 @@
package io.gec.raw.connector.access.connectionworkitem.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ConnectionWorkItemUpdateDTO {
private ConnectionWorkItemStatus status;
}

View File

@@ -0,0 +1,16 @@
package io.gec.raw.connector.access.connectionworkitem.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.UUID;
@Getter
@Setter
public class ConnectionWorkItemValueDTO {
private UUID protocolPropertyId;
private String protocolPropertyValue;
}

View File

@@ -0,0 +1,95 @@
package io.gec.raw.connector.access.connectionworkitem.handler;
import io.gec.raw.connector.access.connectionworkitem.domain.ConnectionWorkItemResult;
import io.gec.raw.connector.access.connectionworkitem.dto.ConnectionWorkItemDTO;
import io.gec.raw.connector.access.connectionworkitem.dto.ConnectionWorkItemStatus;
import io.gec.raw.connector.access.connectionworkitem.dto.ConnectionWorkItemUpdateDTO;
import io.gec.raw.connector.exception.entity.SftpException;
import io.gec.raw.connector.ftp.control.FtpController;
import io.gec.raw.connector.job.domain.JobResult;
import io.gec.raw.connector.job.domain.WorkItemWrapper;
import io.gec.raw.connector.job.dto.JobAction;
import io.gec.raw.connector.job.dto.JobStatus;
import io.gec.raw.connector.job.dto.JobType;
import io.gec.raw.connector.job.handler.WorkItemHandler;
import io.gec.raw.connector.device.control.DeviceController;
import io.gec.raw.connector.device.entity.Device;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.control.ActivateRequestContext;
import jakarta.inject.Inject;
import java.util.Optional;
import java.util.UUID;
@ApplicationScoped
public class CheckConnectionJobHandler extends WorkItemHandler<ConnectionWorkItemDTO, ConnectionWorkItemResult> {
@Inject
FtpController ftpController;
@Inject
DeviceController deviceController;
@Override
protected Optional<ConnectionWorkItemDTO> getWorkItem(UUID workItemId) {
return communicationServiceController.fetchAccessConnectionWorkItem(workItemId);
}
@Override
protected void startProcessing(WorkItemWrapper<ConnectionWorkItemDTO> workItemWrapper) {
updateAccessWorkItemStatus(workItemWrapper.workItemId(), ConnectionWorkItemStatus.IN_PROGRESS);
}
@ActivateRequestContext
@Override
protected ConnectionWorkItemResult processWorkItem(WorkItemWrapper<ConnectionWorkItemDTO> workItemWrapper) {
logger.infof("Processing ConnectionWorkItem (ID:'%s') ...", workItemWrapper.workItemId());
ConnectionWorkItemStatus accessWorkItemStatus;
ConnectionWorkItemDTO workItem = workItemWrapper.workItem();
try {
Device rootDevice = deviceController.getDeviceByCommSvcId(workItem.getDeviceId());
accessWorkItemStatus = ftpController.checkSftpConnection(workItem.getConnectionPropertyValues(), rootDevice.getDeviceURL())
? ConnectionWorkItemStatus.SUCCESS
: ConnectionWorkItemStatus.DEVICE_CONNECTION_FAILURE;
} catch (SftpException e) {
accessWorkItemStatus = ConnectionWorkItemStatus.DEVICE_CONNECTION_FAILURE;
logger.errorf(e, "Processing ConnectionWorkItem (ID:'%s') - FAILED", workItemWrapper.workItemId());
}
return new ConnectionWorkItemResult(accessWorkItemStatus);
}
@Override
protected void finishProcessing(WorkItemWrapper<ConnectionWorkItemDTO> workItemWrapper, ConnectionWorkItemResult workItemResult) {
updateAccessWorkItemStatus(workItemWrapper.workItemId(), workItemResult.getStatus());
}
@Override
protected JobResult mapToJobResult(ConnectionWorkItemResult workItemResult) {
var workItemStatus = workItemResult.getStatus();
var jobStatus = switch (workItemStatus) {
case SUCCESS -> JobStatus.DONE;
case DEVICE_CONNECTION_FAILURE -> JobStatus.FAILED;
default -> throw new IllegalStateException("Mapping to job result failed, unexpected value: " + workItemStatus);
};
return new JobResult(jobStatus, workItemResult.getMessage());
}
@Override
public JobType getJobType() {
return JobType.ACCESS_CONNECTION;
}
@Override
public JobAction getJobAction() {
return JobAction.EXECUTE;
}
private void updateAccessWorkItemStatus(UUID workItemId, ConnectionWorkItemStatus accessWorkItemStatus) {
var accessWorkItemUpdateDTO = new ConnectionWorkItemUpdateDTO();
accessWorkItemUpdateDTO.setStatus(accessWorkItemStatus);
communicationServiceController.updateAccessConnectionWorkItem(workItemId, accessWorkItemUpdateDTO);
}
}

View File

@@ -0,0 +1,225 @@
package io.gec.raw.connector.access.logworkitem.control;
import io.gec.raw.connector.access.logworkitem.dto.AccessLogDTO;
import io.gec.raw.connector.access.logworkitem.dto.AccessLogItemDTO;
import io.gec.raw.connector.access.utils.ParserHelper;
import io.gec.raw.connector.device.control.DeviceController;
import io.gec.raw.connector.device.entity.Device;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Path;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ApplicationScoped
public class AccessLogParser {
private static final String REGEX_CMC =
"(((\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})\\.(\\d)*\\+(\\d{2}):(\\d{2}))(.*))((Device (\\d+))\\s\\(([^\\(]*)\\)[:|\\s](.*))";
private static final String REGEX_CMC_ACCESS_CONFIG_MESSAGE =
"(((\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})\\.(\\d)*\\+(\\d{2}):(\\d{2}))(.*))(Access\\sConfiguration(.*))";
private static final String REGEX_CMC3_V1 = "((\\d{4})-(\\d{2})-(\\d{2})/(\\d{2}):(\\d{2}):(\\d{2}))(.*)((Device (\\d+))\\s\\(([^\\(]*)\\)[:|\\s](.*))";
private static final String REGEX_CMC3_V2 = "((\\d{2})\\.(\\d{2})\\.(\\d{4})/(\\d{2}):(\\d{2}):(\\d{2}))(.*)((Device (\\d+))\\s\\(([^\\(]*)\\)[:|\\s](.*))";
private static final String REGEX_CMC3_ACCESS_CONFIG_MESSAGE_V1 = "((\\d{4})-(\\d{2})-(\\d{2})/(\\d{2}):(\\d{2}):(\\d{2}))(.*)(Configuration(.*))";
private static final String REGEX_CMC3_ACCESS_CONFIG_MESSAGE_V2 = "((\\d{2})\\.(\\d{2})\\.(\\d{4})/(\\d{2}):(\\d{2}):(\\d{2}))(.*)(Configuration(.*))";
private static final int CMC_DATE_TIME_INDEX = 2;
private static final int CMC_DEVICE_NAME_INDEX = 14;
private static final int CMC_DEVICE_NUMBER_INDEX = 15;
private static final int CMC_DEVICE_DESCRIPTION_INDEX = 16;
private static final int CMC_MESSAGE_INDEX = 17;
private static final int CMC_CONFIG_MESSAGE_INDEX = 13;
private static final int CMC3_DATE_TIME_INDEX = 1;
private static final int CMC3_DEVICE_NAME_INDEX = 10;
private static final int CMC3_DEVICE_NUMBER_INDEX = 11;
private static final int CMC3_DEVICE_DESCRIPTION_INDEX = 12;
private static final int CMC3_MESSAGE_INDEX = 13;
private static final int CMC3_CONFIG_MESSAGE_INDEX = 9;
private final Pattern patternCmc = Pattern.compile(REGEX_CMC, Pattern.MULTILINE);
private final Pattern patternAccessConfigCmcMessage = Pattern.compile(REGEX_CMC_ACCESS_CONFIG_MESSAGE, Pattern.MULTILINE);
private final Pattern patternCmc3V1 = Pattern.compile(REGEX_CMC3_V1, Pattern.MULTILINE);
private final Pattern patternCmc3V2 = Pattern.compile(REGEX_CMC3_V2, Pattern.MULTILINE);
private final Pattern patternAccessConfigCmc3MessageV1 = Pattern.compile(REGEX_CMC3_ACCESS_CONFIG_MESSAGE_V1, Pattern.MULTILINE);
private final Pattern patternAccessConfigCmc3MessageV2 = Pattern.compile(REGEX_CMC3_ACCESS_CONFIG_MESSAGE_V2, Pattern.MULTILINE);
@Inject
ParserHelper parserHelper;
@Inject
DeviceController deviceController;
public AccessLogDTO parseCmcFileContent(Path filePath,
OffsetDateTime fromDateTime,
UUID rootDeviceId,
List<Integer> deviceNumberList) throws IOException {
List<AccessLogItemDTO> accessLogItems = new ArrayList<>();
HashMap<Integer, Device> devicesMap = new HashMap<>();
try (BufferedReader br = new BufferedReader(new FileReader(filePath.toFile()))) {
String line;
while ((line = br.readLine()) != null) {
Matcher matcher = patternCmc.matcher(line);
if (matcher.find()) {
OffsetDateTime dateTime = parserHelper.getOffsetDateTimeFromString(matcher.group(CMC_DATE_TIME_INDEX));
if (!dateTime.isBefore(fromDateTime)
&& deviceNumberList.contains(Integer.parseInt(matcher.group(CMC_DEVICE_NUMBER_INDEX)))) {
accessLogItems.add(createAccessLogItemDTO(matcher, false, dateTime, rootDeviceId, devicesMap));
}
} else {
Matcher matcherAccessConfigMessage = patternAccessConfigCmcMessage.matcher(line);
if (matcherAccessConfigMessage.find()) {
OffsetDateTime dateTime = parserHelper.getOffsetDateTimeFromString(matcherAccessConfigMessage.group(CMC_DATE_TIME_INDEX));
if (!dateTime.isBefore(fromDateTime)) {
accessLogItems.add(createAccessLogItemDTOForConfigMessage(matcherAccessConfigMessage, false, dateTime));
}
}
}
}
}
AccessLogDTO accessLogDTO = new AccessLogDTO();
accessLogDTO.setAccessLogItems(accessLogItems);
return accessLogDTO;
}
public AccessLogDTO parseCmc3FileContent(Path filePath,
OffsetDateTime fromDateTime,
UUID rootDeviceId,
List<Integer> deviceNumberList) throws IOException {
List<AccessLogItemDTO> accessLogItems = new ArrayList<>();
HashMap<Integer, Device> devicesMap = new HashMap<>();
String dateTimeFormat = null;
try (BufferedReader br = new BufferedReader(new FileReader(filePath.toFile()))) {
String line;
while ((line = br.readLine()) != null) {
boolean accessLogEntry = false;
Optional<Matcher> matcherOptional = findMatcher(line, patternCmc3V1, patternCmc3V2);
if (matcherOptional.isPresent()) {
accessLogEntry = true;
} else {
matcherOptional = findMatcher(line, patternAccessConfigCmc3MessageV1, patternAccessConfigCmc3MessageV2);
}
if (matcherOptional.isEmpty()) {
continue;
}
Matcher matcher = matcherOptional.get();
dateTimeFormat = dateTimeFormat != null ? dateTimeFormat : getDateTimeFormat(matcher.pattern());
OffsetDateTime dateTime = parserHelper.parseDateTime(matcher.group(CMC3_DATE_TIME_INDEX), dateTimeFormat);
if (accessLogEntry) {
tryToAddAccessLogCmc3(matcher, fromDateTime, dateTime, rootDeviceId, deviceNumberList, devicesMap, accessLogItems);
} else {
tryToAddAccessConfigLogCmc3(matcher, fromDateTime, dateTime, accessLogItems);
}
}
}
AccessLogDTO accessLogDTO = new AccessLogDTO();
accessLogDTO.setAccessLogItems(accessLogItems);
return accessLogDTO;
}
private Optional<Matcher> findMatcher(String line, Pattern... patterns) {
if (patterns == null) {
return Optional.empty();
}
for (Pattern pattern : patterns) {
Matcher matcher = pattern.matcher(line);
if (matcher.matches()) {
return Optional.of(matcher);
}
}
return Optional.empty();
}
private String getDateTimeFormat(Pattern pattern) {
if (patternCmc3V2.equals(pattern) || patternAccessConfigCmc3MessageV2.equals(pattern)) {
return ParserHelper.DATETIME_DAY_MONTH_YEAR_WITH_DOTS;
}
return ParserHelper.DATETIME_YEAR_MONTH_DAY_WITH_HYPHENS;
}
private void tryToAddAccessLogCmc3(Matcher matcher,
OffsetDateTime fromDateTime,
OffsetDateTime currentDateTime,
UUID rootDeviceId,
List<Integer> deviceNumberList,
HashMap<Integer, Device> devicesMap,
List<AccessLogItemDTO> accessLogItems) {
if (currentDateTime.isBefore(fromDateTime)) {
return;
}
if (!deviceNumberList.contains(Integer.parseInt(matcher.group(CMC3_DEVICE_NUMBER_INDEX)))) {
return;
}
accessLogItems.add(createAccessLogItemDTO(matcher, true, currentDateTime, rootDeviceId, devicesMap));
}
private void tryToAddAccessConfigLogCmc3(Matcher matcher,
OffsetDateTime fromDateTime,
OffsetDateTime currentDateTime,
List<AccessLogItemDTO> accessLogItems) {
if (currentDateTime.isBefore(fromDateTime)) {
return;
}
accessLogItems.add(createAccessLogItemDTOForConfigMessage(matcher, true, currentDateTime));
}
private AccessLogItemDTO createAccessLogItemDTOForConfigMessage(Matcher matcher, boolean isCmc3, OffsetDateTime currentDateTime) {
AccessLogItemDTO accessLogItemDTO = new AccessLogItemDTO();
accessLogItemDTO.setDateTime(currentDateTime);
accessLogItemDTO.setMessage(matcher.group(isCmc3 ? CMC3_CONFIG_MESSAGE_INDEX : CMC_CONFIG_MESSAGE_INDEX));
return accessLogItemDTO;
}
private AccessLogItemDTO createAccessLogItemDTO(Matcher matcher,
boolean isCmc3,
OffsetDateTime currentDateTime,
UUID rootDeviceId,
HashMap<Integer, Device> devicesMap) {
AccessLogItemDTO accessLogItemDTO = new AccessLogItemDTO();
accessLogItemDTO.setDateTime(currentDateTime);
accessLogItemDTO.setDeviceName(matcher.group(isCmc3 ? CMC3_DEVICE_NAME_INDEX : CMC_DEVICE_NAME_INDEX));
int deviceNumber = Integer.parseInt(matcher.group(isCmc3 ? CMC3_DEVICE_NUMBER_INDEX : CMC_DEVICE_NUMBER_INDEX));
accessLogItemDTO.setDeviceNumber(deviceNumber);
accessLogItemDTO.setDeviceDescription(
matcher.group(isCmc3 ? CMC3_DEVICE_DESCRIPTION_INDEX : CMC_DEVICE_DESCRIPTION_INDEX));
accessLogItemDTO.setMessage(matcher.group(isCmc3 ? CMC3_MESSAGE_INDEX : CMC_MESSAGE_INDEX));
Device device;
if (devicesMap.containsKey(deviceNumber)) {
device = devicesMap.get(deviceNumber);
} else {
device = deviceController.getDeviceByDeviceIndex(rootDeviceId, deviceNumber);
devicesMap.put(deviceNumber, device);
}
if (device != null) {
accessLogItemDTO.setDeviceId(device.getCommServiceId());
}
return accessLogItemDTO;
}
}

View File

@@ -0,0 +1,19 @@
package io.gec.raw.connector.access.logworkitem.domain;
import io.gec.raw.connector.access.logworkitem.dto.LogWorkItemStatus;
import io.gec.raw.connector.job.domain.WorkItemResult;
import lombok.Getter;
@Getter
public class AccessLogWorkItemResult extends WorkItemResult {
private final LogWorkItemStatus status;
public AccessLogWorkItemResult(LogWorkItemStatus status, String message) {
super(message);
this.status = status;
}
public AccessLogWorkItemResult(LogWorkItemStatus status) {
this(status, null);
}
}

View File

@@ -0,0 +1,11 @@
package io.gec.raw.connector.access.logworkitem.domain;
import io.gec.raw.connector.access.logworkitem.dto.LogWorkItemStatus;
import java.util.UUID;
public record AccessLogWorkItemStatusWrapper(UUID workItemId, LogWorkItemStatus status, String message) {
public AccessLogWorkItemStatusWrapper(UUID workItemId, LogWorkItemStatus status) {
this(workItemId, status, null);
}
}

View File

@@ -0,0 +1,15 @@
package io.gec.raw.connector.access.logworkitem.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.UUID;
@Getter
@Setter
public class AccessLogDTO {
UUID workItemId;
List<AccessLogItemDTO> accessLogItems;
}

View File

@@ -0,0 +1,19 @@
package io.gec.raw.connector.access.logworkitem.dto;
import lombok.Getter;
import lombok.Setter;
import java.time.OffsetDateTime;
import java.util.UUID;
@Getter
@Setter
public class AccessLogItemDTO {
private OffsetDateTime dateTime;
private UUID deviceId;
private String deviceName;
private int deviceNumber;
private String deviceDescription;
private String message;
}

View File

@@ -0,0 +1,18 @@
package io.gec.raw.connector.access.logworkitem.dto;
import io.gec.raw.connector.access.connectionworkitem.dto.ConnectionWorkItemDTO;
import io.gec.raw.connector.job.domain.WorkItem;
import lombok.Getter;
import lombok.Setter;
import java.time.OffsetDateTime;
import java.util.UUID;
@Getter
@Setter
public class LogWorkItemDTO extends WorkItem {
private UUID id;
private ConnectionWorkItemDTO connectionWorkItem;
private LogWorkItemStatus status;
private OffsetDateTime dateFrom;
}

View File

@@ -0,0 +1,10 @@
package io.gec.raw.connector.access.logworkitem.dto;
public enum LogWorkItemStatus {
CREATED,
IN_PROGRESS,
DONE,
DEVICE_ACCESS_FAILURE,
LOG_FETCH_FAILURE,
LOG_PARSE_FAILURE
}

View File

@@ -0,0 +1,12 @@
package io.gec.raw.connector.access.logworkitem.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class LogWorkItemUpdateDTO {
private LogWorkItemStatus status;
private String message;
}

View File

@@ -0,0 +1,120 @@
package io.gec.raw.connector.access.logworkitem.handler;
import io.gec.raw.connector.access.logworkitem.domain.AccessLogWorkItemResult;
import io.gec.raw.connector.access.logworkitem.domain.AccessLogWorkItemStatusWrapper;
import io.gec.raw.connector.access.logworkitem.dto.AccessLogDTO;
import io.gec.raw.connector.access.logworkitem.dto.LogWorkItemDTO;
import io.gec.raw.connector.access.logworkitem.dto.LogWorkItemStatus;
import io.gec.raw.connector.access.logworkitem.dto.LogWorkItemUpdateDTO;
import io.gec.raw.connector.communicationservice.exception.CommunicationServiceException;
import io.gec.raw.connector.exception.entity.SftpException;
import io.gec.raw.connector.ftp.control.FtpController;
import io.gec.raw.connector.job.domain.JobResult;
import io.gec.raw.connector.job.domain.WorkItemWrapper;
import io.gec.raw.connector.job.dto.JobAction;
import io.gec.raw.connector.job.dto.JobStatus;
import io.gec.raw.connector.job.dto.JobType;
import io.gec.raw.connector.job.handler.WorkItemHandler;
import io.gec.raw.connector.device.control.DeviceController;
import io.gec.raw.connector.device.entity.Device;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.control.ActivateRequestContext;
import jakarta.inject.Inject;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@ApplicationScoped
public class AccessLogJobHandler extends WorkItemHandler<LogWorkItemDTO, AccessLogWorkItemResult> {
@Inject
FtpController ftpController;
@Inject
DeviceController deviceController;
@Override
protected Optional<LogWorkItemDTO> getWorkItem(UUID workItemId) {
return communicationServiceController.fetchAccessLogWorkItem(workItemId);
}
@Override
protected void startProcessing(WorkItemWrapper<LogWorkItemDTO> workItemWrapper) {
var accessLogWorkItemStatus = new AccessLogWorkItemStatusWrapper(workItemWrapper.workItemId(), LogWorkItemStatus.IN_PROGRESS);
updateLogWorkItemStatus(accessLogWorkItemStatus);
}
@ActivateRequestContext
@Override
protected AccessLogWorkItemResult processWorkItem(WorkItemWrapper<LogWorkItemDTO> workItemWrapper) {
logger.infof("Processing AccessLogWorkItem (ID:'%s') ...", workItemWrapper.workItemId());
UUID workItemId = workItemWrapper.workItemId();
LogWorkItemDTO workItem = workItemWrapper.workItem();
try {
Device rootDevice = deviceController.getDeviceByCommSvcId(workItem.getConnectionWorkItem().getDeviceId());
List<Integer> deviceNumbers = deviceController.getAccessDeviceIndexesByRootDeviceId(rootDevice.getId());
var sftpData = ftpController.createSftpData(workItem.getConnectionWorkItem().getConnectionPropertyValues(), rootDevice.getDeviceURL());
AccessLogDTO accessLogDTO = ftpController.getLoggingFile(sftpData, workItem.getDateFrom(), rootDevice.getId(), deviceNumbers);
accessLogDTO.setWorkItemId(workItemId);
communicationServiceController.createAccessLogNotifications(accessLogDTO);
return new AccessLogWorkItemResult(LogWorkItemStatus.DONE, getAccessLogResultMessage(accessLogDTO));
} catch (IllegalArgumentException | SftpException e) {
logger.errorf(e, "Processing AccessLogWorkItem (ID:'%s') - FAILED", workItemId);
return new AccessLogWorkItemResult(LogWorkItemStatus.LOG_FETCH_FAILURE, e.getMessage());
} catch (CommunicationServiceException | IOException e) {
logger.errorf(e, "Processing AccessLogWorkItem (ID:'%s') - FAILED", workItemId);
return new AccessLogWorkItemResult(LogWorkItemStatus.LOG_PARSE_FAILURE, e.getMessage());
} catch (Exception e) {
logger.errorf(e, "Processing AccessLogWorkItem (ID:'%s') - FAILED", workItemId);
return new AccessLogWorkItemResult(LogWorkItemStatus.DEVICE_ACCESS_FAILURE, e.getMessage());
}
}
private String getAccessLogResultMessage(AccessLogDTO accessLogDTO) {
//INFO: DO NOT CHANGE the format of message! It is used by frontend and this must be agreed upon before changing it.
return String.format("Access log items found: '%s'", accessLogDTO.getAccessLogItems().size());
}
@Override
protected void finishProcessing(WorkItemWrapper<LogWorkItemDTO> workItemWrapper, AccessLogWorkItemResult workItemResult) {
var accessLogWorkItemStatus = new AccessLogWorkItemStatusWrapper(
workItemWrapper.workItemId(),
workItemResult.getStatus(),
workItemResult.getMessage());
updateLogWorkItemStatus(accessLogWorkItemStatus);
}
@Override
protected JobResult mapToJobResult(AccessLogWorkItemResult workItemResult) {
var workItemStatus = workItemResult.getStatus();
var jobStatus = switch (workItemStatus) {
case DONE -> JobStatus.DONE;
case LOG_PARSE_FAILURE, LOG_FETCH_FAILURE, DEVICE_ACCESS_FAILURE -> JobStatus.FAILED;
default -> throw new IllegalStateException("Mapping to job result failed, unexpected value: " + workItemStatus);
};
return new JobResult(jobStatus, workItemResult.getMessage());
}
@Override
public JobType getJobType() {
return JobType.ACCESS_LOG;
}
@Override
public JobAction getJobAction() {
return JobAction.GET;
}
private void updateLogWorkItemStatus(AccessLogWorkItemStatusWrapper logWorkItemStatus) {
var logWorkItemUpdateDTO = new LogWorkItemUpdateDTO();
logWorkItemUpdateDTO.setStatus(logWorkItemStatus.status());
logWorkItemUpdateDTO.setMessage(logWorkItemStatus.message());
communicationServiceController.updateAccessLogWorkItem(logWorkItemStatus.workItemId(), logWorkItemUpdateDTO);
}
}

View File

@@ -0,0 +1,38 @@
package io.gec.raw.connector.access.utils;
import jakarta.enterprise.context.ApplicationScoped;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
@ApplicationScoped
public class ParserHelper {
public static final String DATETIME_YEAR_MONTH_DAY_WITH_HYPHENS = "yyyy-MM-dd/HH:mm:ss";
public static final String DATETIME_DAY_MONTH_YEAR_WITH_DOTS = "dd.MM.yyyy/HH:mm:ss";
public boolean toBoolean(String str) {
if (str == null) {
return false;
} else if (str.equalsIgnoreCase("true") || str.equals("1")) {
return true;
} else if (str.equalsIgnoreCase("false") || str.equals("0")) {
return false;
} else {
throw new IllegalArgumentException("The String did not match either specified value");
}
}
public OffsetDateTime getOffsetDateTimeFromString(String dateTime) {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
return OffsetDateTime.parse(dateTime, dateTimeFormatter);
}
public OffsetDateTime parseDateTime(String dateTime, String dateTimeFormat) {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateTimeFormat);
LocalDateTime localDateTime = LocalDateTime.parse(dateTime, dateTimeFormatter);
return localDateTime.atOffset(ZoneOffset.UTC);
}
}

View File

@@ -0,0 +1,41 @@
package io.gec.raw.connector.base.entity;
import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreUpdate;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.time.OffsetDateTime;
@Setter
@Getter
@MappedSuperclass
public abstract class BaseEntity implements Serializable {
@Column(
nullable = false,
updatable = false
)
protected OffsetDateTime created;
@Column(
nullable = false
)
protected OffsetDateTime stamp;
protected BaseEntity() {
}
@PrePersist
protected void onCreate() {
this.created = OffsetDateTime.now();
this.stamp = OffsetDateTime.now();
}
@PreUpdate
protected void onUpdate() {
this.stamp = OffsetDateTime.now();
}
}

View File

@@ -0,0 +1,19 @@
package io.gec.raw.connector.base.entity;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.UuidGenerator;
import java.util.UUID;
@Setter
@Getter
@MappedSuperclass
public abstract class SimpleBaseEntity extends BaseEntity {
@Id
@UuidGenerator
protected UUID id;
}

View File

@@ -0,0 +1,17 @@
package io.gec.raw.connector.base.entity;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import lombok.Setter;
import java.util.UUID;
@Setter
@Getter
@MappedSuperclass
public abstract class SimpleBaseEntityWithPredefinedId extends BaseEntity {
@Id
protected UUID id;
}

View File

@@ -0,0 +1,25 @@
package io.gec.raw.connector.base.entity;
import io.gec.raw.connector.componentmode.dto.ComponentMode;
import java.util.UUID;
public interface SubscribableEntity {
UUID getId();
int getSubscriptionInterval();
ComponentMode getMode();
boolean isInitialValueFetched();
default boolean isNotInitialValueFetched() {
return !isInitialValueFetched();
}
default boolean isSubscribed() {
return ComponentMode.OPERATION.equals(getMode()) && getSubscriptionInterval() > 0;
}
}

View File

@@ -0,0 +1,205 @@
package io.gec.raw.connector.communicationservice.control;
import io.gec.raw.connector.access.configworkitem.dto.AccessConfigWorkItemDTO;
import io.gec.raw.connector.access.configworkitem.dto.AccessConfigWorkItemUpdateDTO;
import io.gec.raw.connector.access.connectionworkitem.dto.ConnectionWorkItemDTO;
import io.gec.raw.connector.access.connectionworkitem.dto.ConnectionWorkItemUpdateDTO;
import io.gec.raw.connector.access.logworkitem.dto.AccessLogDTO;
import io.gec.raw.connector.access.logworkitem.dto.LogWorkItemDTO;
import io.gec.raw.connector.access.logworkitem.dto.LogWorkItemUpdateDTO;
import io.gec.raw.connector.communicationservice.exception.CommunicationServiceResponseExceptionMapper;
import io.gec.raw.connector.componentmode.dto.ChangeModeRequestDTO;
import io.gec.raw.connector.componentmode.dto.ComponentMode;
import io.gec.raw.connector.connector.dto.ConnectorRequestDTO;
import io.gec.raw.connector.connector.dto.ConnectorResponseDTO;
import io.gec.raw.connector.device.dto.DeviceDTO;
import io.gec.raw.connector.device.dto.DeviceResponseDTO;
import io.gec.raw.connector.deviceproperty.dto.PropertyValueRequestDTO;
import io.gec.raw.connector.discovery.dto.DiscoveryWorkItemDTO;
import io.gec.raw.connector.discovery.dto.DiscoveryWorkItemUpdateDTO;
import io.gec.raw.connector.driver.dto.DriverWorkItemDTO;
import io.gec.raw.connector.driver.dto.DriverWorkItemUpdateDTO;
import io.gec.raw.connector.firmware.dto.FirmwareFileMetadataDTO;
import io.gec.raw.connector.firmware.dto.FirmwareWorkItemDTO;
import io.gec.raw.connector.firmware.dto.FirmwareWorkItemUpdateRequestDTO;
import io.gec.raw.connector.job.dto.JobDTO;
import io.gec.raw.connector.job.dto.JobStateDTO;
import io.gec.raw.connector.pauseworkitem.dto.PauseWorkItemDTO;
import io.gec.raw.connector.pauseworkitem.dto.PauseWorkItemUpdateDTO;
import io.gec.raw.connector.setworkitem.dto.SetWorkItemDTO;
import io.gec.raw.connector.setworkitem.dto.SetWorkItemUpdateDTO;
import io.gec.raw.connector.variable.dto.UpdateVariableRequestDTO;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import java.util.List;
import java.util.UUID;
@ApplicationScoped
@RegisterRestClient(configKey = "communication-service")
@RegisterProvider(CommunicationServiceResponseExceptionMapper.class)
public interface CommunicationService {
@GET
@Path("/api/resources/jobs")
List<JobDTO> getJobs(
@QueryParam("connectorId") UUID connectorId,
@QueryParam("sinceDateTime") String sinceDateTime
);
@GET
@Path("api/resources/discoveryWorkItems/{discoveryWorkItemId}")
DiscoveryWorkItemDTO getDiscoveryWorkItemById(@PathParam("discoveryWorkItemId") UUID discoveryWorkItemId);
@PUT
@Path("/api/resources/discoveryWorkItems/{id}")
DiscoveryWorkItemDTO updateDiscoveryWorkItem(
@PathParam("id") UUID discoveryWorkItemId,
DiscoveryWorkItemUpdateDTO discoveryWorkItemUpdateDTO
);
@PUT
@Path("/api/resources/discoveryWorkItems/deviceDiscovery/{id}")
Boolean addLatestDeviceDiscoveryAndArchiveOldDeviceDiscovery(
@PathParam("id") UUID discoveryWorkItemId,
UUID deviceId
);
@PUT
@Path("/api/resources/variables/values")
Boolean updateSubscribedVariable(
UpdateVariableRequestDTO updateVariableRequestDTO
);
@POST
@Path("/api/resources/variables/values/collection")
Boolean updateSubscribedVariables(
List<UpdateVariableRequestDTO> updateVariableRequestDTOs
);
@PUT
@Path("/api/resources/deviceProperties/values")
Boolean updateDeviceProperty(
PropertyValueRequestDTO propertyValueRequestDTO
);
@POST
@Path("api/resources/connectors/{connectorId}/status")
Boolean updateConnectorStatus(
@PathParam("connectorId") UUID connectorId
);
@POST
@Path("api/resources/devices")
DeviceResponseDTO createOrUpdateDevicesWithSubDevices(DeviceDTO deviceDTO);
@POST
@Path("/api/resources/connectors")
ConnectorResponseDTO registerConnector(ConnectorRequestDTO dto);
@POST
@Path("api/resources/accessLogs")
void createAccessLogNotifications(AccessLogDTO accessLogDTO);
@GET
@Path("api/resources/connectionWorkItems/{id}")
ConnectionWorkItemDTO getAccessConnectionWorkItem(@PathParam("id") UUID connectionWorkItemId);
@PATCH
@Path("api/resources/connectionWorkItems/{id}")
ConnectionWorkItemDTO updateAccessConnectionWorkItem(@PathParam("id") UUID connectionWorkItemId,
ConnectionWorkItemUpdateDTO connectionWorkItemUpdateDTO);
@PATCH
@Path("api/resources/logWorkItems/{id}")
LogWorkItemDTO updateAccessLogWorkItem(@PathParam("id") UUID logWorkItemId,
LogWorkItemUpdateDTO logWorkItemUpdateDTO);
@GET
@Path("/api/resources/logWorkItems/{id}")
LogWorkItemDTO getAccessLogWorkItem(@PathParam("id") UUID logWorkItemId);
@GET
@Path("/api/resources/accessConfigWorkItems/{id}")
AccessConfigWorkItemDTO getAccessConfigWorkItem(@PathParam("id") UUID accessConfigWorkItemId);
@PATCH
@Path("api/resources/accessConfigWorkItems/{id}")
AccessConfigWorkItemDTO updateAccessConfigWorkItem(@PathParam("id") UUID accessConfigWorkItemId,
AccessConfigWorkItemUpdateDTO accessConfigWorkItemUpdateDTO);
@PATCH
@Path("api/resources/pauseWorkItems/{id}")
PauseWorkItemDTO updatePauseWorkItem(@PathParam("id") UUID pauseWorkItemId,
PauseWorkItemUpdateDTO pauseWorkItemUpdateDTO);
@GET
@Path("/api/resources/pauseWorkItems/{id}")
PauseWorkItemDTO getPauseWorkItem(
@PathParam("id") UUID pauseWorkItemId
);
@GET
@Path("/api/resources/driverWorkItems/{id}")
DriverWorkItemDTO getDriverWorkItem(
@PathParam("id") UUID driverWorkItemId
);
@PATCH
@Path("api/resources/driverWorkItems/{id}")
DriverWorkItemDTO updateDriverWorkItem(@PathParam("id") UUID driverWorkItemId,
DriverWorkItemUpdateDTO driverWorkItemUpdateDTO);
@POST
@Path("api/resources/jobs/{jobId}/states")
JobStateDTO createJobState(@PathParam("jobId") UUID jobId,
JobStateDTO jobStateDTO);
@GET
@Path("/api/resources/setWorkItems/{id}")
SetWorkItemDTO getSetWorkItem(@PathParam("id") UUID setWorkItemId);
@PATCH
@Path("api/resources/setWorkItems/{id}")
void updateSetWorkItem(@PathParam("id") UUID setWorkItemId,
SetWorkItemUpdateDTO setWorkItemUpdateDTO);
@GET
@Path("api/resources/firmwareWorkItems/{id}")
FirmwareWorkItemDTO getFirmwareWorkItem(@PathParam("id") UUID firmwareWorkItemId);
@PATCH
@Path("api/resources/firmwareWorkItems/{id}")
void updateFirmwareWorkItem(@PathParam("id") UUID firmwareWorkItemId,
FirmwareWorkItemUpdateRequestDTO firmwareWorkItemUpdateRequestDTO);
@GET
@Path("api/resources/firmwareWorkItems/{workItemId}/files/{firmwareId}/metadata")
FirmwareFileMetadataDTO getFirmwareFileMetadata(@PathParam("firmwareId") UUID firmwareId,
@PathParam("workItemId") UUID workItemId);
@GET
@Path("api/resources/firmwareWorkItems/{workItemId}/files/{firmwareId}")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
Response getFirmwareFileStream(@PathParam("firmwareId") UUID firmwareId,
@PathParam("workItemId") UUID workItemId);
@PUT
@Path("api/resources/componentModes/")
void changeComponentMode(ChangeModeRequestDTO changeModeRequestDTO);
@PUT
@Path("api/resources/componentModes/{id}")
JobStateDTO changeComponentModeForDeviceTree(@PathParam("id") UUID deviceId, ComponentMode componentMode);
}

View File

@@ -0,0 +1,356 @@
package io.gec.raw.connector.communicationservice.control;
import io.gec.raw.connector.access.configworkitem.dto.AccessConfigWorkItemDTO;
import io.gec.raw.connector.access.configworkitem.dto.AccessConfigWorkItemUpdateDTO;
import io.gec.raw.connector.access.connectionworkitem.dto.ConnectionWorkItemDTO;
import io.gec.raw.connector.access.connectionworkitem.dto.ConnectionWorkItemUpdateDTO;
import io.gec.raw.connector.access.logworkitem.dto.AccessLogDTO;
import io.gec.raw.connector.access.logworkitem.dto.LogWorkItemDTO;
import io.gec.raw.connector.access.logworkitem.dto.LogWorkItemUpdateDTO;
import io.gec.raw.connector.communicationservice.exception.CommunicationServiceException;
import io.gec.raw.connector.componentmode.dto.ChangeModeRequestDTO;
import io.gec.raw.connector.componentmode.dto.ComponentMode;
import io.gec.raw.connector.config.entity.ConnectorConfig;
import io.gec.raw.connector.connector.dto.ConnectorRequestDTO;
import io.gec.raw.connector.connector.dto.ConnectorResponseDTO;
import io.gec.raw.connector.deviceproperty.dto.PropertyValueRequestDTO;
import io.gec.raw.connector.discovery.dto.DiscoveryWorkItemDTO;
import io.gec.raw.connector.discovery.dto.DiscoveryWorkItemUpdateDTO;
import io.gec.raw.connector.driver.dto.DriverWorkItemDTO;
import io.gec.raw.connector.driver.dto.DriverWorkItemUpdateDTO;
import io.gec.raw.connector.firmware.dto.FirmwareFileMetadataDTO;
import io.gec.raw.connector.firmware.dto.FirmwareWorkItemDTO;
import io.gec.raw.connector.firmware.dto.FirmwareWorkItemUpdateRequestDTO;
import io.gec.raw.connector.job.dto.JobDTO;
import io.gec.raw.connector.job.dto.JobStateDTO;
import io.gec.raw.connector.pauseworkitem.dto.PauseWorkItemDTO;
import io.gec.raw.connector.pauseworkitem.dto.PauseWorkItemUpdateDTO;
import io.gec.raw.connector.setworkitem.dto.SetWorkItemDTO;
import io.gec.raw.connector.setworkitem.dto.SetWorkItemUpdateDTO;
import io.gec.raw.connector.variable.dto.UpdateVariableRequestDTO;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.logging.Logger;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@ApplicationScoped
public class CommunicationServiceController {
@Inject
Logger logger;
@Inject
ConnectorConfig connectorConfig;
@Inject
CommunicationServiceRequestErrorHandler communicationServiceRequestErrorHandler;
@Inject
@RestClient
CommunicationService communicationService;
public Optional<ConnectorResponseDTO> registerConnector(ConnectorRequestDTO dto) {
try {
ConnectorResponseDTO result = communicationService.registerConnector(dto);
logger.infof("Comm.Svc. - Connector registered");
return Optional.of(result);
} catch (Exception ex) {
logger.errorf(ex, "Comm.Svc. - register Connector (ID:'%s') - FAILED", connectorConfig.getConnectorId());
}
return Optional.empty();
}
public void updateConnectorStatus() {
try {
communicationService.updateConnectorStatus(connectorConfig.getConnectorId());
logger.infof("Comm.Svc. - Connector status updated");
} catch (Exception ex) {
logger.errorf(ex, "Comm.Svc. - update Connector status - FAILED");
}
}
public List<JobDTO> fetchJobs(String sinceDateTime) {
try {
//INFO: log concerning fetched jobs done in JobController
return communicationService.getJobs(connectorConfig.getConnectorId(), sinceDateTime);
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - fetch Jobs since '%s' - FAILED", sinceDateTime);
}
return Collections.emptyList();
}
public void createJobState(UUID jobId, JobStateDTO jobStateDTO) {
try {
communicationService.createJobState(jobId, jobStateDTO);
logger.infof("Comm.Svc. - Job (ID:'%s') status updated to: '%s'", jobId, jobStateDTO.getStatus());
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - update Job (ID:'%s') status to: '%s' - FAILED", jobId, jobStateDTO.getStatus());
}
}
public Optional<ConnectionWorkItemDTO> fetchAccessConnectionWorkItem(UUID connectionWorkItemId) {
try {
ConnectionWorkItemDTO result = communicationService.getAccessConnectionWorkItem(connectionWorkItemId);
logger.infof("Comm.Svc. - ConnectionWorkItem (ID:'%s') fetched", connectionWorkItemId);
return Optional.of(result);
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - fetch ConnectionWorkItem (ID:'%s') - FAILED", connectionWorkItemId);
}
return Optional.empty();
}
public Optional<LogWorkItemDTO> fetchAccessLogWorkItem(UUID logWorkItemId) {
try {
LogWorkItemDTO result = communicationService.getAccessLogWorkItem(logWorkItemId);
logger.infof("Comm.Svc. - LogWorkItem (ID:'%s') fetched", logWorkItemId);
return Optional.of(result);
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - fetch LogWorkItem (ID:'%s') - FAILED", logWorkItemId);
}
return Optional.empty();
}
public Optional<AccessConfigWorkItemDTO> fetchAccessConfigWorkItem(UUID accessConfigWorkItemId) {
try {
AccessConfigWorkItemDTO result = communicationService.getAccessConfigWorkItem(accessConfigWorkItemId);
logger.infof("Comm.Svc. - AccessConfigWorkItem (ID:'%s') fetched", accessConfigWorkItemId);
return Optional.of(result);
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - fetch AccessConfigWorkItem (ID:'%s') - FAILED", accessConfigWorkItemId);
}
return Optional.empty();
}
public Optional<PauseWorkItemDTO> fetchPauseWorkItem(UUID pauseWorkItemId) {
try {
PauseWorkItemDTO result = communicationService.getPauseWorkItem(pauseWorkItemId);
logger.infof("Comm.Svc. - PauseWorkItem (ID:'%s') fetched", pauseWorkItemId);
return Optional.of(result);
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - fetch PauseWorkItem (ID:'%s') - FAILED", pauseWorkItemId);
}
return Optional.empty();
}
public Optional<DiscoveryWorkItemDTO> fetchDiscoveryWorkItem(UUID id) {
try {
DiscoveryWorkItemDTO result = communicationService.getDiscoveryWorkItemById(id);
logger.infof("Comm.Svc. - DiscoveryWorkItem (ID:'%s') fetched", id);
return Optional.of(result);
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - fetch DiscoveryWorkItem (ID:'%s') - FAILED", id);
}
return Optional.empty();
}
public Optional<DriverWorkItemDTO> fetchDriverWorkItem(UUID id) {
try {
DriverWorkItemDTO result = communicationService.getDriverWorkItem(id);
logger.infof("Comm.Svc. - DriverWorkItem (ID:'%s') fetched", id);
return Optional.of(result);
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - fetch DriverWorkItem (ID:'%s') - FAILED", id);
}
return Optional.empty();
}
public Optional<FirmwareWorkItemDTO> fetchFirmwareWorkItem(UUID id) {
try {
FirmwareWorkItemDTO result = communicationService.getFirmwareWorkItem(id);
logger.infof("Comm.Svc. - FirmwareWorkItem (ID:'%s') fetched", id);
return Optional.of(result);
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - fetch FirmwareWorkItem (ID:'%s') - FAILED", id);
}
return Optional.empty();
}
public FirmwareFileMetadataDTO fetchFirmwareFileMetadata(UUID firmwareId, UUID workItemId) {
try {
FirmwareFileMetadataDTO result = communicationService.getFirmwareFileMetadata(firmwareId, workItemId);
logger.infof("Comm.Svc. - FirmwareFileMetadata (ID:'%s') fetched", firmwareId);
return result;
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - fetch FirmwareFileMetadata (ID:'%s') - FAILED", firmwareId);
throw ex;
}
}
public InputStream fetchFirmwareFileStream(UUID firmwareId, UUID workItemId) {
try {
Response response = communicationService.getFirmwareFileStream(firmwareId, workItemId);
InputStream inputStream = response.readEntity(InputStream.class);
logger.infof("Comm.Svc. - firmware file input stream (ID:'%s') fetched", firmwareId);
return inputStream;
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - fetch firmware file input stream (ID:'%s') - FAILED", firmwareId);
throw ex;
}
}
public void updateAccessConnectionWorkItem(UUID connectionWorkItemId, ConnectionWorkItemUpdateDTO connectionWorkItemUpdateDTO) {
try {
communicationService.updateAccessConnectionWorkItem(connectionWorkItemId, connectionWorkItemUpdateDTO);
logger.infof("Comm.Svc. - ConnectionWorkItem (ID:'%s') updated to status: '%s'", connectionWorkItemId, connectionWorkItemUpdateDTO.getStatus());
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - update ConnectionWorkItem (ID:'%s') status to '%s' - FAILED", connectionWorkItemId, connectionWorkItemUpdateDTO.getStatus());
}
}
public void updateAccessLogWorkItem(UUID logWorkItemId, LogWorkItemUpdateDTO logWorkItemUpdateDTO) {
try {
communicationService.updateAccessLogWorkItem(logWorkItemId, logWorkItemUpdateDTO);
logger.infof("Comm.Svc. - LogWorkItem (ID:'%s') updated to status: '%s'", logWorkItemId, logWorkItemUpdateDTO.getStatus());
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - update LogWorkItem (ID:'%s') status to '%s' - FAILED", logWorkItemId, logWorkItemUpdateDTO.getStatus());
}
}
public void updateAccessConfigWorkItem(UUID accessConfigWorkItemId, AccessConfigWorkItemUpdateDTO accessConfigWorkItemUpdateDTO) {
try {
communicationService.updateAccessConfigWorkItem(accessConfigWorkItemId, accessConfigWorkItemUpdateDTO);
logger.infof("Comm.Svc. - AccessConfigWorkItem (ID:'%s') updated to status: '%s'", accessConfigWorkItemId, accessConfigWorkItemUpdateDTO.getStatus());
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - update AccessConfigWorkItem (ID:'%s') status to '%s' - FAILED", accessConfigWorkItemId, accessConfigWorkItemUpdateDTO.getStatus());
}
}
public void updatePauseWorkItem(UUID pauseWorkItemId, PauseWorkItemUpdateDTO pauseWorkItemUpdateDTO) {
try {
communicationService.updatePauseWorkItem(pauseWorkItemId, pauseWorkItemUpdateDTO);
logger.infof("Comm.Svc. - PauseWorkItem (ID:'%s') updated to status: '%s'", pauseWorkItemId, pauseWorkItemUpdateDTO.getStatus());
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - update PauseWorkItem (ID:'%s') status to '%s' - FAILED", pauseWorkItemId, pauseWorkItemUpdateDTO.getStatus());
}
}
public void updateDiscoveryWorkItem(UUID discoveryWorkItemId, DiscoveryWorkItemUpdateDTO discoveryWorkItemUpdateDTO) {
try {
communicationService.updateDiscoveryWorkItem(discoveryWorkItemId, discoveryWorkItemUpdateDTO);
logger.infof("Comm.Svc. - DiscoveryWorkItem (ID:'%s') updated to status: '%s'", discoveryWorkItemId, discoveryWorkItemUpdateDTO.getDiscoveryStatusEnum());
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - update DiscoveryWorkItem (ID:'%s') status to '%s' - FAILED", discoveryWorkItemId, discoveryWorkItemUpdateDTO.getDiscoveryStatusEnum());
}
}
public void updateDriverWorkItem(UUID driverWorkItemId, DriverWorkItemUpdateDTO driverWorkItemUpdateDTO) {
try {
communicationService.updateDriverWorkItem(driverWorkItemId, driverWorkItemUpdateDTO);
logger.infof("Comm.Svc. - DriverWorkItem (ID:'%s') updated to status: '%s'", driverWorkItemId, driverWorkItemUpdateDTO.getStatus());
} catch (Exception ex) {
logger.errorf(ex, "Comm.Svc. - update DriverWorkItem (ID:'%s') to status '%s' - FAILED", driverWorkItemId, driverWorkItemUpdateDTO.getStatus());
}
}
public void updateFirmwareWorkItem(UUID firmwareWorkItemId, FirmwareWorkItemUpdateRequestDTO firmwareWorkItemUpdateRequestDTO) {
try {
communicationService.updateFirmwareWorkItem(firmwareWorkItemId, firmwareWorkItemUpdateRequestDTO);
logger.infof("Comm.Svc. - FirmwareWorkItem (ID:'%s') updated to status: '%s'", firmwareWorkItemId, firmwareWorkItemUpdateRequestDTO.getStatus());
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - update FirmwareWorkItem (ID:'%s') to status '%s' - FAILED", firmwareWorkItemId, firmwareWorkItemUpdateRequestDTO.getStatus());
}
}
public void createAccessLogNotifications(AccessLogDTO accessLogDTO) throws CommunicationServiceException {
try {
communicationService.createAccessLogNotifications(accessLogDTO);
logger.infof("Comm.Svc. - AccessLog entries for AccessLogWorkItem (ID:'%s') sent (total: %d)", accessLogDTO.getWorkItemId(), accessLogDTO.getAccessLogItems().size());
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - sending AccessLog entries (total: %d) for AccessLogWorkItem (ID:'%s') - FAILED", accessLogDTO.getAccessLogItems().size(),
accessLogDTO.getWorkItemId());
throw ex;
}
}
public void updateVariableValue(UpdateVariableRequestDTO updateVariableRequestDTO) {
try {
communicationService.updateSubscribedVariable(updateVariableRequestDTO);
logger.debugf("Comm.Svc. - VariableValue (ID:'%s') updated to:'%s', quality:'%s'", updateVariableRequestDTO.getVariableId(), updateVariableRequestDTO.getValue(),
updateVariableRequestDTO.getQualityType());
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - Update VariableValue (ID:'%s') to:'%s', quality:'%s' - FAILED", updateVariableRequestDTO.getVariableId(),
updateVariableRequestDTO.getValue(), updateVariableRequestDTO.getQualityType());
}
}
public void updateVariableValues(List<UpdateVariableRequestDTO> variableValuesDTOs) {
try {
logger.debugf("Comm.Svc. - Sending Variable Values: %s)", variableValuesDTOs);
communicationService.updateSubscribedVariables(variableValuesDTOs);
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - Sending Variable Values - FAILED");
logger.errorf("Values sent in failed request: %s", variableValuesDTOs);
communicationServiceRequestErrorHandler.handleErrorOfUpdateSubscribedVariables(variableValuesDTOs);
}
}
public void updateDeviceProperty(PropertyValueRequestDTO propertyValueRequestDTO) {
try {
communicationService.updateDeviceProperty(propertyValueRequestDTO);
logger.debugf("Comm.Svc. - PropertyValue (ID:'%s') updated to:'%s'", propertyValueRequestDTO.getPropertyId(), propertyValueRequestDTO.getValueAsString());
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - Update PropertyValue (ID:'%s') to:'%s' - FAILED", propertyValueRequestDTO.getPropertyId(),
propertyValueRequestDTO.getValueAsString());
}
}
public Optional<SetWorkItemDTO> fetchSetWorkItem(UUID setWorkItemId) {
try {
SetWorkItemDTO result = communicationService.getSetWorkItem(setWorkItemId);
logger.infof("Comm.Svc. - SetWorkItem (ID:'%s') fetched", setWorkItemId);
return Optional.of(result);
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - Fetch SetWorkItem (ID:'%s') - FAILED", setWorkItemId);
}
return Optional.empty();
}
public void updateSetWorkItem(UUID setWorkItemId, SetWorkItemUpdateDTO setWorkItemUpdateDTO) {
try {
communicationService.updateSetWorkItem(setWorkItemId, setWorkItemUpdateDTO);
logger.infof("Comm.Svc. - SetWorkItem (ID:'%s') updated to status: '%s'", setWorkItemId, setWorkItemUpdateDTO.getStatus());
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - Update SetWorkItem (ID:'%s') to status: '%s' - FAILED", setWorkItemId, setWorkItemUpdateDTO.getStatus());
}
}
public void changeComponentMode(ChangeModeRequestDTO changeModeRequestDTO) {
try {
communicationService.changeComponentMode(changeModeRequestDTO);
logger.infof("Comm.Svc. - Updating ComponentMode");
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - Update ComponentMode - FAILED");
}
}
public void changeComponentModeForDeviceTree(UUID deviceId, ComponentMode mode) {
try {
communicationService.changeComponentModeForDeviceTree(deviceId, mode);
logger.infof("Comm.Svc. - Updating ComponentMode for device (ID: '%s') to: '%s')", deviceId, mode);
} catch (CommunicationServiceException ex) {
logger.errorf(ex, "Comm.Svc. - Update ComponentMode for device (ID: '%s') to: '%s - FAILED')", deviceId, mode);
}
}
}

View File

@@ -0,0 +1,24 @@
package io.gec.raw.connector.communicationservice.control;
import io.gec.raw.connector.variable.control.VariableValueCacheHandler;
import io.gec.raw.connector.variable.dto.UpdateVariableRequestDTO;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.jboss.logging.Logger;
import java.util.List;
import java.util.UUID;
@ApplicationScoped
public class CommunicationServiceRequestErrorHandler {
@Inject
Logger logger;
@Inject
VariableValueCacheHandler variableValueCacheHandler;
public void handleErrorOfUpdateSubscribedVariables(List<UpdateVariableRequestDTO> variableValuesDTOs) {
List<UUID> variableIds = variableValuesDTOs.stream().map(UpdateVariableRequestDTO::getVariableId).toList();
logger.infof("Removing from Cache Variable Values which were sent in failed request IDs: %s", variableIds);
variableValueCacheHandler.removeFromCache(variableIds);
}
}

View File

@@ -0,0 +1,24 @@
package io.gec.raw.connector.communicationservice.exception;
public class CommunicationServiceException extends RuntimeException {
private final ModelType model;
private final FailureType failure;
public CommunicationServiceException(ModelType modelType, FailureType failureType, String message) {
super(message);
model = modelType;
failure = failureType;
}
public CommunicationServiceException(FailureType failureType, String message) {
super(message);
model = new ModelType("unknown");
failure = failureType;
}
@Override
public String toString() {
return "CommunicationServiceException{model=%s, failure=%s, msg:%s}".formatted(model, failure, getMessage());
}
}

View File

@@ -0,0 +1,50 @@
package io.gec.raw.connector.communicationservice.exception;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper;
import org.jboss.logging.Logger;
import java.io.IOException;
import java.util.Map;
public class CommunicationServiceResponseExceptionMapper implements ResponseExceptionMapper<CommunicationServiceException> {
@Inject
Logger logger;
@Override
public CommunicationServiceException toThrowable(Response response) {
return extractCommSvcExceptionFromResponse(response);
}
private CommunicationServiceException extractCommSvcExceptionFromResponse(Response response) {
try {
Map<String, String> body = readResponseBody(response);
var model = body.get("object");
var failure = body.get("failure");
var message = body.get("message");
if (message == null) {
message = body.toString();
}
var modelType = new ModelType(model);
var failureType = new FailureType(response.getStatusInfo().toEnum(), failure);
return new CommunicationServiceException(modelType, failureType, message);
} catch (Exception ex) {
var message = "Unable to extract CommunicationService response body as CommunicationServiceException";
logger.warn(message, ex);
return new CommunicationServiceException(new FailureType(response.getStatusInfo().toEnum(), "unknown"),
message);
}
}
private Map<String, String> readResponseBody(Response response) throws IOException {
String json = response.readEntity(String.class);
return new ObjectMapper().readValue(json, new TypeReference<>() {
});
}
}

View File

@@ -0,0 +1,10 @@
package io.gec.raw.connector.communicationservice.exception;
import jakarta.ws.rs.core.Response;
public record FailureType(Response.Status status, String name) {
@Override
public String toString() {
return "FailureType{status=%s, name=%s}".formatted(status, name);
}
}

View File

@@ -0,0 +1,8 @@
package io.gec.raw.connector.communicationservice.exception;
public record ModelType(String name) {
@Override
public String toString() {
return "ModelType{name=%s}".formatted(name);
}
}

View File

@@ -0,0 +1,70 @@
package io.gec.raw.connector.componentmode.control;
import io.gec.raw.connector.device.control.DeviceController;
import io.gec.raw.connector.componentmode.dto.ComponentMode;
import io.gec.raw.connector.device.entity.Device;
import io.gec.raw.connector.deviceproperty.control.DevicePropertyController;
import io.gec.raw.connector.componentmode.dto.ChangeModeRequestDTO;
import io.gec.raw.connector.variable.control.VariableController;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.jboss.logging.Logger;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
@ApplicationScoped
public class ComponentModeController {
@Inject
DeviceController deviceController;
@Inject
VariableController variableController;
@Inject
DevicePropertyController devicePropertyController;
@Inject
Logger logger;
public ChangeModeRequestDTO createChangeModeRequestDTO(UUID commDeviceId) {
ChangeModeRequestDTO changeModeRequestDTO = new ChangeModeRequestDTO();
Device rootDevice = deviceController.getDeviceByCommSvcId(commDeviceId);
logger.infof("Creating ChangeModeRequestDTO for Device ID: '%s'", rootDevice.getId());
createDeviceModeMap(rootDevice, changeModeRequestDTO);
return changeModeRequestDTO;
}
void createDeviceModeMap(Device device, ChangeModeRequestDTO changeModeRequestDTO) {
Objects.requireNonNull(device, "Device is null, cannot create DeviceModeMap.");
logger.debugf("Processing Device (ID:'%s')", device.getId());
createVariableModeMap(device.getId(), changeModeRequestDTO.getVariableModes());
createPropertyModeMap(device.getId(), changeModeRequestDTO.getPropertyModes());
changeModeRequestDTO.getDeviceModes().put(device.getCommServiceId(), device.getMode());
deviceController.getSubDevices(device.getId()).forEach(subDevice -> createDeviceModeMap(subDevice, changeModeRequestDTO));
}
void createVariableModeMap(UUID deviceId, Map<UUID, ComponentMode> variableModesMap) {
var variables = variableController.getVariablesByDeviceId(deviceId);
variables.forEach(variable -> {
variableModesMap.put(variable.getCommServiceId(), variable.getMode());
logger.debugf("Added Variable (ID:'%s') with mode '%s' to VariableModeMap for Device ID: '%s'", variable.getCommServiceId(), variable.getMode(), deviceId);
});
}
private void createPropertyModeMap(UUID deviceId, Map<UUID, ComponentMode> propertyModesMap) {
var properties = devicePropertyController.findByDeviceId(deviceId);
properties.forEach(property -> {
propertyModesMap.put(property.getCommServiceId(), property.getMode());
logger.debugf("Added Property (ID:'%s') with mode '%s' to PropertyModeMap for Device ID: '%s'", property.getCommServiceId(), property.getMode(), deviceId);
});
}
}

View File

@@ -0,0 +1,39 @@
package io.gec.raw.connector.componentmode.control;
import io.gec.raw.connector.communicationservice.control.CommunicationServiceController;
import io.gec.raw.connector.componentmode.dto.ChangeModeRequestDTO;
import io.gec.raw.connector.componentmode.dto.ComponentMode;
import io.gec.raw.connector.device.control.DeviceController;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.UUID;
@ApplicationScoped
public class ComponentModeHandler {
@Inject
ComponentModeController componentModeController;
@Inject
DeviceController deviceController;
@Inject
CommunicationServiceController communicationServiceController;
public void changeComponentModeForDeviceTree(UUID commDeviceId, ComponentMode componentMode) {
deviceController.updateModeByCommSvcId(commDeviceId, componentMode);
communicationServiceController.changeComponentModeForDeviceTree(commDeviceId, componentMode);
}
public void changeComponentMode(UUID commDeviceId, ComponentMode componentMode) {
deviceController.updateModeByCommSvcId(commDeviceId, componentMode);
ChangeModeRequestDTO changeModeRequestDTO = componentModeController.createChangeModeRequestDTO(commDeviceId);
communicationServiceController.changeComponentMode(changeModeRequestDTO);
}
public void restoreComponentMode(UUID commDeviceId) {
deviceController.restoreModeByCommSvcId(commDeviceId);
ChangeModeRequestDTO changeModeRequestDTO = componentModeController.createChangeModeRequestDTO(commDeviceId);
communicationServiceController.changeComponentMode(changeModeRequestDTO);
}
}

View File

@@ -0,0 +1,14 @@
package io.gec.raw.connector.componentmode.dto;
import lombok.Getter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@Getter
public class ChangeModeRequestDTO {
private Map<UUID, ComponentMode> deviceModes = new HashMap<>();
private Map<UUID, ComponentMode> variableModes = new HashMap<>();
private Map<UUID, ComponentMode> propertyModes = new HashMap<>();
}

View File

@@ -0,0 +1,14 @@
package io.gec.raw.connector.componentmode.dto;
public enum ComponentMode {
OPERATION,
PAUSE,
SERVICE,
FIRMWARE_UPDATE,
OFFLINE;
public boolean isRestorable() {
return this.equals(OPERATION) || this.equals(PAUSE);
}
}

View File

@@ -0,0 +1,15 @@
package io.gec.raw.connector.config.control;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.Produces;
import java.time.Clock;
@ApplicationScoped
public class AppConfig {
@Produces
@ApplicationScoped
public Clock createClockBean() {
return Clock.systemUTC();
}
}

View File

@@ -0,0 +1,49 @@
package io.gec.raw.connector.config.control;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.logging.Logger;
import java.text.MessageFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
@ApplicationScoped
public class ConnectorConfigController {
private static final String VERSION_PROPERTY = "version";
private static final String VERSION_PROPERTY_FORMAT = "yyyyMMddHHmm";
private static final String TIMESTAMP_FORMAT = "{0,date,dd.MM.yyyy} {0,time,HH:mm}";
@ConfigProperty(name = "quarkus.application.version")
String version;
@Inject
Logger logger;
@Inject
PropertyFileHandler propertyFileHandler;
public String getConnectorVersion() {
return String.format("%s (%s)", version, getTimestamp());
}
String getTimestamp() throws RuntimeException {
return propertyFileHandler.getProperty(VERSION_PROPERTY)
.filter(property -> !property.isBlank())
.map(this::formatTimestamp)
.orElseThrow(() -> new RuntimeException("Version property in version.properties file does not exist or is empty"));
}
private String formatTimestamp(String timestamp) {
try {
SimpleDateFormat inputFormat = new SimpleDateFormat(VERSION_PROPERTY_FORMAT);
inputFormat.setLenient(false);
Date date = inputFormat.parse(timestamp);
return MessageFormat.format(TIMESTAMP_FORMAT, date);
} catch (ParseException e) {
logger.error("There was an error while parsing version from version.properties file, raw version property will be used");
return timestamp;
}
}
}

View File

@@ -0,0 +1,26 @@
package io.gec.raw.connector.config.control;
import jakarta.enterprise.context.ApplicationScoped;
import java.io.InputStream;
import java.util.Optional;
import java.util.Properties;
@ApplicationScoped
public class PropertyFileHandler {
public static final String PROPERTY_FILE_NAME = "version.properties";
private final Properties properties = new Properties();
public PropertyFileHandler() {
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(PROPERTY_FILE_NAME)) {
properties.load(inputStream);
} catch (Exception ex) {
throw new RuntimeException("There was a problem while loading property file", ex);
}
}
public Optional<String> getProperty(String name) {
return Optional.ofNullable(properties.getProperty(name));
}
}

View File

@@ -0,0 +1,11 @@
package io.gec.raw.connector.config.control;
import io.smallrye.config.ConfigMapping;
import java.time.Duration;
@ConfigMapping(prefix = "connector.variable.bulk-value-sending")
public interface VariableBulkValueSendingConfig {
int maxQueueSize();
Duration maxDelayForFirstValue();
Duration maxWaitTimeForNextValue();
}

View File

@@ -0,0 +1,18 @@
package io.gec.raw.connector.config.entity;
import jakarta.enterprise.context.ApplicationScoped;
import lombok.Getter;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import java.util.UUID;
@ApplicationScoped
@Getter
public class ConnectorConfig {
@ConfigProperty(name = "connector.id")
UUID connectorId;
@ConfigProperty(name = "connector.name")
String connectorName;
@ConfigProperty(name = "connector.description")
String connectorDescription;
}

View File

@@ -0,0 +1,50 @@
package io.gec.raw.connector.config.entity;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.Getter;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import java.util.List;
import java.util.Optional;
@ApplicationScoped
@Getter
public class SnmpConfig {
@Inject
SnmpConnectionTypesConfig snmpConnectionTypesConfig;
@ConfigProperty(name = "snmp.message-dispatcher-pool-size")
int messageDispatcherPoolSize;
@ConfigProperty(name = "snmp.scheduled-tasks.pool-size")
int scheduledTasksPoolSize;
@ConfigProperty(name = "snmp.dynamic-table.pool-size")
int dynamicTablePoolSize;
@ConfigProperty(name = "snmp.dynamic-table.default-pooling-interval")
int dynamicTableDefaultPoolingInterval;
@ConfigProperty(name = "snmp.discovery.deprecated-protocols")
Optional<List<String>> discoveryDeprecatedProtocols;
@ConfigProperty(name = "snmp.discovery.root-oid")
String discoveryRootOid;
@ConfigProperty(name = "snmp.discovery.pool-size")
int discoveryPoolSize;
@ConfigProperty(name = "snmp.discovery.initial-get-retries")
int discoveryInitialGetRetries;
@ConfigProperty(name = "snmp.discovery.initial-get-timeout")
Long discoveryInitialGetTimeout;
@ConfigProperty(name = "snmp.discovery.bulk-get-retries")
int discoveryBulkGetRetries;
@ConfigProperty(name = "snmp.discovery.bulk-get-timeout")
Long discoveryBulkGetTimeout;
@ConfigProperty(name = "snmp.discovery.get-retries")
int discoveryGetRetries;
@ConfigProperty(name = "snmp.discovery.get-timeout")
Long discoveryGetTimeout;
@ConfigProperty(name = "snmp.discovery.set-retries")
int discoverySetRetries;
@ConfigProperty(name = "snmp.discovery.set-timeout")
Long discoverySetTimeout;
}

View File

@@ -0,0 +1,18 @@
package io.gec.raw.connector.config.entity;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithParentName;
import java.util.Map;
@ConfigMapping(prefix = "snmp.connection-types")
public interface SnmpConnectionTypesConfig {
@WithParentName
Map<String, Connection> connectionTypes();
interface Connection {
int port();
String protocol();
String hostname();
}
}

View File

@@ -0,0 +1,95 @@
package io.gec.raw.connector.connector.control;
import io.gec.raw.connector.driver.control.DeviceDriverController;
import io.gec.raw.connector.protocol.domain.CommunicationProtocolEnum;
import io.gec.raw.connector.protocol.domain.SnmpVersionEnum;
import io.gec.raw.connector.communicationservice.control.CommunicationServiceController;
import io.gec.raw.connector.config.control.ConnectorConfigController;
import io.gec.raw.connector.config.entity.ConnectorConfig;
import io.gec.raw.connector.connector.dto.ConnectorRequestDTO;
import io.gec.raw.connector.connector.dto.ConnectorRequestDTO.ConnectorRequestDTOBuilder;
import io.gec.raw.connector.connector.dto.ConnectorResponseDTO;
import io.gec.raw.connector.protocol.control.ProtocolMapper;
import io.gec.raw.connector.protocol.control.ProtocolService;
import jakarta.enterprise.context.ApplicationScoped;
import org.jboss.logging.Logger;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@ApplicationScoped
public class ConnectorController {
private final ConnectorConfig connectorConfig;
private final ConnectorConfigController connectorConfigController;
private final CommunicationServiceController communicationServiceController;
private final DeviceDriverController deviceDriverController;
private final ProtocolService protocolService;
private final ConnectorUtils connectorUtils;
private final ProtocolMapper protocolMapper;
private final Logger logger;
public ConnectorController(ConnectorConfig connectorConfig,
ConnectorConfigController connectorConfigController,
CommunicationServiceController communicationServiceController,
DeviceDriverController deviceDriverController,
ProtocolService protocolService,
ConnectorUtils connectorUtils,
ProtocolMapper protocolMapper,
Logger logger) {
this.connectorConfig = connectorConfig;
this.connectorConfigController = connectorConfigController;
this.communicationServiceController = communicationServiceController;
this.deviceDriverController = deviceDriverController;
this.protocolService = protocolService;
this.connectorUtils = connectorUtils;
this.protocolMapper = protocolMapper;
this.logger = logger;
}
public Optional<ConnectorResponseDTO> registerOnServer() {
ConnectorRequestDTO connectorRequestDTO = buildConnector();
var connectorRegisterResult = communicationServiceController.registerConnector(connectorRequestDTO);
if (connectorRegisterResult.isPresent()) {
protocolService.createOrUpdateProtocols(connectorRegisterResult.get().getProtocols());
deviceDriverController.synchronizeDrivers();
return connectorRegisterResult;
}
return Optional.empty();
}
ConnectorRequestDTO buildConnector() {
ConnectorRequestDTOBuilder connectorRequestDTOBuilder = ConnectorRequestDTO.builder();
String version = connectorConfigController.getConnectorVersion();
try {
connectorRequestDTOBuilder
.id(connectorConfig.getConnectorId())
.name(getConnectorName())
.description(connectorConfig.getConnectorDescription())
.ipAddress(InetAddress.getLocalHost().getHostAddress())
.userId(UUID.randomUUID())
.version(version)
.connectorProtocols(protocolMapper.getProtocols(List.of(
SnmpVersionEnum.SNMP_V1,
SnmpVersionEnum.SNMP_V2C,
SnmpVersionEnum.SNMP_V3,
CommunicationProtocolEnum.SFTP)));
} catch (Exception ex) {
logger.error("Error building the connector: " + ex.getMessage());
}
return connectorRequestDTOBuilder.build();
}
private String getConnectorName() throws SocketException, UnknownHostException {
return connectorConfig.getConnectorName().isEmpty()
? connectorUtils.getMacAddress()
: connectorConfig.getConnectorName();
}
}

View File

@@ -0,0 +1,22 @@
package io.gec.raw.connector.connector.control;
import jakarta.enterprise.context.ApplicationScoped;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
@ApplicationScoped
public class ConnectorUtils {
public String getMacAddress() throws SocketException, UnknownHostException {
NetworkInterface network = NetworkInterface.getByInetAddress(InetAddress.getLocalHost());
byte[] mac = network.getHardwareAddress();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mac.length; i++) {
sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : ""));
}
return sb.toString();
}
}

View File

@@ -0,0 +1,27 @@
/*
* (c) 2023 German Edge Cloud GmbH & Co. KG
*/
package io.gec.raw.connector.connector.dto;
import io.gec.raw.connector.protocol.dto.ProtocolDTO;
import lombok.Builder;
import lombok.Value;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Value
@Builder
public class ConnectorRequestDTO {
UUID id;
String name;
String description;
String ipAddress;
UUID userId;
String version;
@Builder.Default
List<ProtocolDTO> connectorProtocols = new ArrayList<>();
}

View File

@@ -0,0 +1,19 @@
package io.gec.raw.connector.connector.dto;
import io.gec.raw.connector.protocol.dto.ProtocolDTO;
import lombok.Builder;
import lombok.Value;
import java.util.List;
import java.util.UUID;
@Builder
@Value
public class ConnectorResponseDTO {
UUID id;
String name;
String description;
String ipAddress;
List<ProtocolDTO> protocols;
}

View File

@@ -0,0 +1,7 @@
package io.gec.raw.connector.connector.dto;
public enum DataTypeEnum {
STRING,
INTEGER;
}

View File

@@ -0,0 +1,10 @@
package io.gec.raw.connector.connector.dto;
public enum RepresentationEnum {
TEXT,
PASSWORD,
DATE,
NUMBER,
IPV4;
}

View File

@@ -0,0 +1,598 @@
package io.gec.raw.connector.device.control;
import io.gec.raw.connector.communicationservice.control.CommunicationService;
import io.gec.raw.connector.componentmode.dto.ComponentMode;
import io.gec.raw.connector.device.domain.DeviceMapping;
import io.gec.raw.connector.device.dto.DeviceDTO;
import io.gec.raw.connector.device.dto.DeviceResponseDTO;
import io.gec.raw.connector.device.entity.Device;
import io.gec.raw.connector.device.enums.RittalDevicePropertyNameEnum;
import io.gec.raw.connector.deviceproperty.control.DevicePropertyController;
import io.gec.raw.connector.deviceproperty.control.DevicePropertyExtractor;
import io.gec.raw.connector.deviceproperty.control.DevicePropertyRepository;
import io.gec.raw.connector.deviceproperty.dto.DevicePropertyDTO;
import io.gec.raw.connector.deviceproperty.entity.DeviceProperty;
import io.gec.raw.connector.variable.control.VariableController;
import io.gec.raw.connector.variable.control.VariableMapper;
import io.gec.raw.connector.variable.dto.VariableDTO;
import io.gec.raw.connector.variable.entity.Variable;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityNotFoundException;
import jakarta.transaction.Transactional;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.logging.Logger;
import java.util.*;
import java.util.stream.Collectors;
@ApplicationScoped
public class DeviceController {
@Inject
EntityManager em;
@Inject
VariableMapper variableMapper;
@Inject
VariableController variableController;
@Inject
DevicePropertyController devicePropertyController;
@Inject
Logger logger;
@Inject
DevicePropertyRepository devicePropertyRepository;
@Inject
@RestClient
CommunicationService communicationService;
@Inject
DeviceRepository deviceRepository;
@Transactional
public Device storeDevice(DeviceMapping deviceMapping, UUID plantId, UUID parentId) throws Exception {
assert deviceMapping != null : "Device mapping is null";
assert plantId != null : "PlantId is null";
logger.infof("Processing Device (index:'%s', name:'%s', description:'%s') for DiscoveryWorkItem (ID:'%s') ...", deviceMapping.getDeviceIndex(), deviceMapping.getName(),
deviceMapping.getDescription(), deviceMapping.getDiscoveryWorkItemId());
Device device;
Optional<String> deviceSerialNumber = findPropertyValueByName(deviceMapping.getProperties(),
RittalDevicePropertyNameEnum.SERIAL_NUMBER.getLabel());
if (deviceSerialNumber.isPresent()) {
device = handleDeviceWithSerialNumber(deviceMapping, plantId, parentId, deviceSerialNumber.get());
} else {
device = handleDeviceWithoutSerialNumber(deviceMapping, plantId, parentId);
}
processSubDevices(deviceMapping, plantId, device.getId());
return device;
}
private void processSubDevices(DeviceMapping device, UUID plantId, UUID parentId) throws Exception {
logger.infof("Processing sub-devices for Device (ID:'%s') - total: %d ", device.getId(), device.getSubDevices().size());
for (DeviceMapping subDevice : device.getSubDevices()) {
storeDevice(subDevice, plantId, parentId);
}
}
public Device getDeviceByCommSvcId(UUID commSvcId) {
return deviceRepository.getDeviceByCommSvcId(commSvcId);
}
public List<Integer> getAccessDeviceIndexesByRootDeviceId(UUID rootDeviceId) {
return deviceRepository.getAccessDeviceIndexes(rootDeviceId);
}
private Device handleDeviceWithSerialNumber(DeviceMapping deviceMapping,
UUID plantId,
UUID parentId,
String serialNumber) {
var optionalDevice = findUniqueDeviceBySerialNumber(serialNumber, deviceMapping.isRoot(), plantId);
return optionalDevice.map(device -> updateDeviceFromDiscovery(device, deviceMapping))
.orElseGet(() -> createDeviceFromDiscovery(deviceMapping, plantId, parentId));
}
Device handleDeviceWithoutSerialNumber(DeviceMapping deviceData,
UUID plantId,
UUID parentId) {
assert parentId != null : "Parent ID is null but expected for devices without serial number.";
logger.infof("Processing device without Serial Number (name:'%s')", deviceData.getName());
Device parentDevice = getDeviceById(parentId);
assert parentDevice != null : "Parent device not found for device without serial number. parentId: " + parentId;
List<Device> subDevices = getDevicesByParentId(parentDevice.getId());
return subDevices.stream()
.filter(device -> device.getDescription().equals(deviceData.getDescription()))
.findFirst()
.filter(device -> device.getPlantId().equals(plantId))
.map(device -> updateDeviceFromDiscovery(device, deviceData))
.orElseGet(() -> createDeviceFromDiscovery(deviceData, plantId, parentDevice.getId()));
}
private Device createDeviceFromDiscovery(DeviceMapping deviceMapping, UUID plantId, UUID parentId) {
assert deviceMapping != null : "Device is null";
assert plantId != null : "PlantId is null";
Device device = toDevice(deviceMapping, plantId, parentId);
em.persist(device);
logger.debugf("Device (ID:'%s', name: '%s') created", device.getId(), deviceMapping.getName());
variableController.createVariables(deviceMapping.getVariables(), device.getId());
devicePropertyController.createProperties(deviceMapping.getProperties(), device.getId());
return device;
}
private Optional<String> findPropertyValueByName(List<? extends DevicePropertyExtractor> properties, String propertyName) {
if (properties == null || properties.isEmpty()) {
return Optional.empty();
}
return properties.stream()
.filter(property -> propertyName.equals(property.getName()))
.findFirst()
.map(DevicePropertyExtractor::getValue);
}
private Device updateDeviceFromDiscovery(Device device, DeviceMapping deviceData) {
assert device != null : "Device is null";
assert deviceData != null : "DeviceData is null";
device = updateDeviceData(device, deviceData);
communicationService.addLatestDeviceDiscoveryAndArchiveOldDeviceDiscovery(
deviceData.getDiscoveryWorkItemId(), device.getId());
logger.debugf("Updated discovery work item ID for device: %s", device.getId());
return device;
}
private Device updateDeviceData(Device device, DeviceMapping deviceData) {
assert device != null : "Device is null";
assert deviceData != null : "DeviceData is null";
logger.debugf("Device (ID:'%s', name:'%s') updated", device.getId(), device.getName());
device.setDiscoveryWorkItemId(deviceData.getDiscoveryWorkItemId());
device.setName(deviceData.getName());
device.setDescription(deviceData.getDescription());
device.setDeviceURL(deviceData.getDeviceURL());
device.setType(deviceData.getType());
device.setDeviceIndex(deviceData.getDeviceIndex());
device.setMode(deviceData.getMode());
variableController.createVariablesIfNotExistByName(deviceData.getVariables(), device.getId());
devicePropertyController.createPropertiesIfNotExistByName(deviceData.getProperties(), device.getId());
return em.merge(device);
}
Optional<Device> findUniqueDeviceBySerialNumber(String serialNumber, boolean isRootDevice, UUID plantId) {
var devicesWithSerialNumber = deviceRepository.findBySerialNumber(serialNumber, isRootDevice, plantId);
if (devicesWithSerialNumber.size() > 1) {
throw new IllegalStateException(
String.format("More than one device found for Serial Number:'%s', isRootDevice:'%s'", serialNumber, isRootDevice));
}
return devicesWithSerialNumber.stream().findFirst();
}
public Device toDevice(DeviceMapping deviceMapping, UUID plantId, UUID parentId) {
assert deviceMapping != null : "DeviceMapping is null";
assert plantId != null : "PlantId is null";
Device device = new Device();
device.setName(deviceMapping.getName());
device.setDescription(deviceMapping.getDescription() != null ? deviceMapping.getDescription() : "");
device.setDiscoveryWorkItemId(deviceMapping.getDiscoveryWorkItemId());
device.setConnectorId(deviceMapping.getConnectorId());
device.setPlantId(plantId);
device.setDeviceURL(deviceMapping.getDeviceURL());
device.setType(deviceMapping.getType());
device.setParentId(parentId);
device.setMode(ComponentMode.PAUSE);
device.setRestoreMode(ComponentMode.PAUSE);
device.setDriverHeaderId(deviceMapping.getDriverHeaderId());
device.setDeviceIndex(deviceMapping.getDeviceIndex());
return device;
}
public Optional<UUID> findDeviceIdByDeviceUrl(String deviceURL) {
return deviceRepository.findDeviceIdByDeviceUrl(deviceURL);
}
/**
* Retrieves a {@link Device} object from the database based on its unique identifier.
* <p>
* This method fetches the device details corresponding to the provided {@code deviceId}. It performs a null check on the input parameter and logs appropriate messages based on
* whether the device is found or not.
*
* @param deviceId The unique identifier of the device to be retrieved. It must not be null.
* @return The {@link Device} object associated with the given {@code deviceId}, or {@code null} if no such device is found or if {@code deviceId} is null.
*/
public Device getDeviceById(UUID deviceId) {
if (deviceId == null) {
logger.error("Device ID is null.");
return null;
}
logger.debug("Start getting Device by ID: " + deviceId);
Optional<Device> device = deviceRepository.findByIdOptional(deviceId);
if (device.isEmpty()) {
logger.debug("No device found for ID: " + deviceId);
return null;
}
logger.debug("Device found for ID: " + deviceId);
return device.get();
}
/**
* Retrieves the root device in the hierarchy starting from the specified device.
* <p>
* This method iteratively traverses up the device hierarchy starting from the given device until it finds the root device. A root device is identified as a device that does
* not have a parent. If the input device or its ID is {@code null}, an {@link IllegalArgumentException} is thrown.
* <p>
* Note: This method operates recursively and may perform multiple database queries to find the root device.
*
* @param device The starting device from which to find the root device. Must not be {@code null}.
* @return The root {@link Device} of the provided device hierarchy.
* @throws IllegalArgumentException if the provided device or its ID is {@code null}.
*/
public Device getRootDevice(Device device) {
if (device == null || device.getId() == null) {
logger.error("#getRootDevice device is empty or Null: ");
throw new IllegalArgumentException("device is empty or Null");
}
logger.debug("1/2 #getRootDevice Start getting root Device for IP ofDevice with Id: " + device.getId());
while (device.getParentId() != null) {
device = deviceRepository.findById(device.getParentId());
}
logger.debug("2/2 #getRootDevice Finished getting root Device for IP ofDevice with Id: " + device.getId() + "Root Device Id: " + device.getId());
return device;
}
/**
* Constructs a device tree for a communication service starting from a specified device ID.
* <p>
* This method retrieves a device from the database using the given {@code deviceId} and then recursively builds a tree structure of all its sub-devices. Each device in the
* tree is converted to a {@code Device} object suitable for communication services. The tree includes the root device and all its hierarchical sub-devices.
*
* @param deviceId The unique identifier of the root device from which to start building the tree. Must not be {@code null}.
* @return A {@code Device} object representing the root of the device tree, including all its sub-devices.
* @throws IllegalArgumentException if the {@code deviceId} is {@code null} or if no device is found with the given ID.
*/
public DeviceDTO getDeviceTreeForCommSvc(UUID deviceId) {
if (deviceId == null) {
throw new IllegalArgumentException("Device ID is required but null was provided");
}
Device deviceFromDB = em.find(Device.class, deviceId);
if (deviceFromDB == null) {
throw new IllegalArgumentException("Device (ID:'%s') not found".formatted(deviceId));
}
List<Device> subDevices = deviceRepository.getSubDevices(deviceFromDB.getId());
DeviceDTO tempDeviceDTO = createCommSvcDeviceDTO(deviceFromDB.getId());
ArrayList<DeviceDTO> subDeviceDTOs = subDevices.stream()
.map(subDevice -> getDeviceTreeForCommSvc(subDevice.getId()))
.collect(Collectors.toCollection(ArrayList::new));
tempDeviceDTO.setSubDevices(subDeviceDTOs);
return tempDeviceDTO;
}
/**
* Creates a {@link DeviceDTO} object for communication services based on a specified root device ID.
* <p>
* This method retrieves an {@link Device} from the database using the provided {@code rootDeviceId} and converts it into a {@link DeviceDTO} object. It also fetches and sets
* the associated variables and properties for the device. Additionally, it retrieves and sets all direct sub-devices of the root device, along with their own sub-devices,
* effectively building a partial device tree.
*
* @param rootDeviceId The unique identifier of the root device to be converted. Must not be {@code null}.
* @return A {@link DeviceDTO} object representing the root device with its variables, properties, and sub-devices.
* @throws IllegalArgumentException if the {@code rootDeviceId} is {@code null} or if no device is found with the given ID.
*/
public DeviceDTO createCommSvcDeviceDTO(UUID rootDeviceId) {
if (rootDeviceId == null) {
throw new IllegalArgumentException("Root device ID must not be null");
}
Device rootDevice = em.find(Device.class, rootDeviceId);
if (rootDevice == null) {
throw new IllegalArgumentException("Root Device (ID:'%s') not found".formatted(rootDeviceId));
}
DeviceDTO rootDeviceDTO = DeviceDTO.from(rootDevice);
List<Variable> variables = variableController.getVariablesByDeviceId(rootDeviceId);
List<DeviceProperty> deviceProperties = devicePropertyRepository.findByDeviceId(rootDeviceId);
List<VariableDTO> variableDTOs = variableMapper.toVariableDTOs(variables);
List<DevicePropertyDTO> devicePropertyDTOs = toDevicePropertyDTOs(deviceProperties);
rootDeviceDTO.setVariables(variableDTOs);
rootDeviceDTO.setProperties(devicePropertyDTOs);
List<DeviceDTO> subDeviceDTOs = getCommSvcDevicesByParentId(rootDeviceId);
rootDeviceDTO.setSubDevices(subDeviceDTOs);
setCommSvcParentIdAndSubDevices(rootDeviceDTO.getSubDevices(), rootDeviceDTO.getId()); // Recursive call for sub-devices
return rootDeviceDTO;
}
public Device getDeviceByDeviceIndex(UUID rootDeviceId, Integer deviceIndex) {
if (deviceIndex == null) {
throw new IllegalArgumentException("Device index required but null was provided");
}
Optional<Device> rootDevice = deviceRepository.findByIdOptional(rootDeviceId);
return rootDevice.map(device -> traverseDeviceTree(device, deviceIndex))
.orElse(null);
}
private Device traverseDeviceTree(Device device, Integer deviceIndex) {
List<Device> subDevices = deviceRepository.getSubDevices(device.getId());
for (Device subDevice : subDevices) {
if (deviceIndex.equals(subDevice.getDeviceIndex())) {
return subDevice;
}
Device result = traverseDeviceTree(subDevice, deviceIndex);
if (Objects.nonNull(result)) {
return result;
}
}
return null;
}
private void setCommSvcParentIdAndSubDevices(List<DeviceDTO> subDeviceDTOs, UUID commSvcParentId) {
for (DeviceDTO subDeviceDTO : subDeviceDTOs) {
subDeviceDTO.setParentId(commSvcParentId);
List<DeviceDTO> subSubDeviceDTOs = getCommSvcDevicesByParentId(subDeviceDTO.getConnectorDeviceId());
subDeviceDTO.setSubDevices(subSubDeviceDTOs);
setCommSvcParentIdAndSubDevices(subDeviceDTO.getSubDevices(), subDeviceDTO.getId());
}
}
@Transactional
public void updateModeByCommSvcId(UUID commSvcId, ComponentMode mode) {
var device = deviceRepository.getDeviceByCommSvcId(commSvcId);
if (device == null) {
throw new EntityNotFoundException("Device (CommSvcID:'%s') not found.".formatted(commSvcId));
}
updateMode(device, mode);
logger.infof("Device (CommSvcID:'%s') mode set to:'%s'", commSvcId, mode);
}
@Transactional
public void updateMode(Device device, ComponentMode mode) {
if (device.getMode().isRestorable()) {
device.setRestoreMode(device.getMode());
}
device.setMode(mode);
em.merge(device);
switch (mode) {
case PAUSE -> {
unsubscribeDeviceProperties(device.getId());
unsubscribeVariables(device.getId());
}
case OPERATION -> {
subscribeDeviceProperties(device.getId());
subscribeVariables(device.getId());
}
case FIRMWARE_UPDATE, OFFLINE -> {
updateModeRecursive(device, mode);
}
}
}
@Transactional
public void restoreModeByCommSvcId(UUID commSvcId) {
var device = deviceRepository.getDeviceByCommSvcId(commSvcId);
restoreModeRecursive(device);
}
private void restoreModeRecursive(Device device) {
restoreMode(device);
deviceRepository.getSubDevices(device.getId())
.forEach(this::restoreModeRecursive);
}
private void updateModeRecursive(Device device, ComponentMode mode) {
updatePropertyMode(device.getId(), mode);
updateVariableMode(device.getId(), mode);
deviceRepository.getSubDevices(device.getId())
.forEach(subDevice -> updateMode(subDevice, mode));
}
public List<Device> getSubDevices(UUID deviceId) {
if (deviceId == null) {
throw new IllegalArgumentException("Device id required but null was provided");
}
return deviceRepository.getSubDevices(deviceId);
}
@Transactional
public void restoreMode(Device device) {
Objects.requireNonNull(device, "Device required to restore mode");
restoreDeviceMode(device);
restoreVariableMode(device.getId());
restorePropertyMode(device.getId());
}
@Transactional
public void restoreDeviceMode(Device device) {
Objects.requireNonNull(device, "Device required to restore mode");
updateModeWithRestoreMode(device);
logger.debugf("Device (ID:'%s') mode restored to:'%s'", device.getId(), device.getMode());
}
private void updateModeWithRestoreMode(Device device) {
if (device != null) {
ComponentMode restoreMode = device.getRestoreMode();
device.setMode(restoreMode);
}
}
private void restoreVariableMode(UUID deviceId) {
for (Variable variable : variableController.getVariablesByDeviceId(deviceId)) {
variableController.restoreMode(variable);
}
}
private void restorePropertyMode(UUID deviceId) {
for (DeviceProperty property : devicePropertyController.findByDeviceId(deviceId)) {
devicePropertyController.restoreMode(property);
}
}
private void unsubscribeVariables(UUID deviceId) {
for (Variable variable : variableController.getVariablesByDeviceId(deviceId)) {
variableController.updateMode(variable, ComponentMode.PAUSE);
}
}
private void unsubscribeDeviceProperties(UUID deviceId) {
for (DeviceProperty property : devicePropertyController.findByDeviceId(deviceId)) {
devicePropertyController.updateMode(property, ComponentMode.PAUSE);
}
}
private void updateVariableMode(UUID deviceId, ComponentMode mode) {
for (Variable variable : variableController.getVariablesByDeviceId(deviceId)) {
variableController.updateMode(variable, mode);
}
}
private void updatePropertyMode(UUID deviceId, ComponentMode mode) {
for (DeviceProperty property : devicePropertyController.findByDeviceId(deviceId)) {
devicePropertyController.updateMode(property, mode);
}
}
private void subscribeVariables(UUID deviceId) {
for (Variable variable : variableController.getVariablesByDeviceId(deviceId)) {
variableController.updateMode(variable, ComponentMode.OPERATION);
}
}
private void subscribeDeviceProperties(UUID deviceId) {
for (DeviceProperty property : devicePropertyController.findByDeviceId(deviceId)) {
devicePropertyController.updateMode(property, ComponentMode.OPERATION);
}
}
private List<DevicePropertyDTO> toDevicePropertyDTOs(List<DeviceProperty> properties) {
return properties.stream()
.map(DevicePropertyDTO::from)
.toList();
}
private List<Device> getDevicesByParentId(UUID parentId) {
if (parentId == null) {
return new ArrayList<>();
}
return deviceRepository.findByParentId(parentId);
}
/**
* Retrieves a list of {@link DeviceDTO} objects that are direct sub-devices of a specified parent device.
* <p>
* This method fetches all {@link Device} entities that have the specified {@code parentId} from the database. Each of these Device entities is then converted into a Device
* object. Additionally, for each Device object, associated variables and properties are fetched and set. The method effectively maps a list of Device entities to a list of
* Device objects, ready for use in various services or business logic.
*
* @param parentId The unique identifier of the parent device whose sub-devices are to be retrieved. Can be {@code null}, in which case an empty list is returned.
* @return A list of {@link DeviceDTO} objects, each representing a sub-device of the specified parent. Returns an empty list if no sub-devices are found or if the
* {@code parentId} is {@code null}.
*/
private List<DeviceDTO> getCommSvcDevicesByParentId(UUID parentId) {
List<Device> subDevices = getDevicesByParentId(parentId);
ArrayList<DeviceDTO> mappedSubdevices = new ArrayList<>();
subDevices.forEach(deviceEntity -> {
DeviceDTO deviceDTO = DeviceDTO.from(deviceEntity);
List<Variable> variables = variableController.getVariablesByDeviceId(deviceEntity.getId());
List<DeviceProperty> properties = devicePropertyRepository.findByDeviceId(deviceEntity.getId());
List<VariableDTO> variableDTOs = variableMapper.toVariableDTOs(variables);
List<DevicePropertyDTO> devicePropertyDTOs = toDevicePropertyDTOs(properties);
deviceDTO.setVariables(variableDTOs);
deviceDTO.setProperties(devicePropertyDTOs);
mappedSubdevices.add(deviceDTO);
});
return mappedSubdevices;
}
@Transactional
public void updateCommSvcId(DeviceResponseDTO deviceResponseDTO) {
updateCommSvcIdHelper(deviceResponseDTO);
deviceResponseDTO.getSubdevices().forEach(this::updateCommSvcId);
}
/**
* Updates the communication service ID of a device and associated variables and properties based on the provided device response.
* <p>
* This method updates the 'commServiceId' field of a device in the database using the ID provided in the {@link DeviceResponseDTO}. It also updates the communication service
* IDs for the device's variables and properties. If no device is updated in the database (indicating no matching device was found), a warning is logged. The method delegates
* the updating of variables and properties to their respective controllers.
*
* @param deviceResponseDTO The {@link DeviceResponseDTO} containing the new communication service ID and identifiers for the device and its variables and properties.
*/
private void updateCommSvcIdHelper(DeviceResponseDTO deviceResponseDTO) {
if (deviceResponseDTO == null) {
logger.error("DeviceResponseDTO is null.");
return;
}
int result = em.createQuery("UPDATE device SET commServiceId = :CommSvcId WHERE id = :ConnectorDeviceId")
.setParameter("CommSvcId", deviceResponseDTO.getId())
.setParameter("ConnectorDeviceId", deviceResponseDTO.getConnectorDeviceId())
.executeUpdate();
if (result == 0) {
logger.warn(
"Update of Device (ID:'%s') to set CommSvcId:'%s' - without success, no row updated".formatted(deviceResponseDTO.getConnectorDeviceId(),
deviceResponseDTO.getId()));
}
variableController.updateCommSvcId(deviceResponseDTO.getVariables());
devicePropertyController.updateCommSvcId(deviceResponseDTO.getProperties());
}
public List<Device> findAllRootDevices() {
return deviceRepository.findAllRootDevices();
}
}

View File

@@ -0,0 +1,136 @@
package io.gec.raw.connector.device.control;
import io.gec.raw.connector.componentmode.dto.ComponentMode;
import io.gec.raw.connector.device.entity.Device;
import io.gec.raw.connector.device.enums.RittalDeviceConstants;
import io.gec.raw.connector.device.enums.RittalDevicePropertyNameEnum;
import io.gec.raw.connector.device.projection.DeviceParentIdProjection;
import io.gec.raw.connector.device.projection.DeviceURLProjection;
import io.gec.raw.connector.utils.control.IPAddressHelper;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@ApplicationScoped
public class DeviceRepository implements PanacheRepositoryBase<Device, UUID> {
private static final String DEVICE_ID = "deviceId";
@Inject
EntityManager em;
@Inject
IPAddressHelper ipAddressHelper;
@Transactional
public Optional<DeviceURLProjection> findDeviceURLProjection(UUID id) {
return find("id", id)
.project(DeviceURLProjection.class)
.firstResultOptional();
}
@Transactional
public Optional<DeviceParentIdProjection> findDeviceParentIdProjection(UUID id) {
return find("id", id)
.project(DeviceParentIdProjection.class)
.firstResultOptional();
}
@Transactional
public List<UUID> findDeviceIdsByParentId(UUID parentId) {
return Optional.ofNullable(parentId).map(id -> stream("parentId", id)
.map(Device::getId)
.toList())
.orElse(List.of());
}
@Transactional
public Optional<UUID> findDeviceIdByDeviceUrl(String deviceUrl) {
return this.findByDeviceUrl(deviceUrl)
.map(Device::getId);
}
public void subscribe(UUID id) {
setSubscribed(id, true);
}
public void unsubscribe(UUID id) {
setSubscribed(id, false);
}
public void setSubscribed(UUID id, boolean subscribed) {
update("subscribed = ?1 where id = ?2", subscribed, id);
}
@Transactional
public List<Integer> getAccessDeviceIndexes(UUID deviceId) {
return em.createQuery("SELECT d.deviceIndex FROM device d WHERE d.parentId =: deviceId AND d.type =:accessDevOid", Integer.class)
.setParameter("deviceId", deviceId)
.setParameter("accessDevOid", RittalDeviceConstants.ACCESS_DEVICE_TYPE_OID)
.getResultList();
}
@Transactional
public Device getDeviceByCommSvcId(UUID commDeviceId) {
return em.createQuery("SELECT d FROM device d WHERE d.commServiceId =: commDeviceId", Device.class)
.setParameter("commDeviceId", commDeviceId)
.getSingleResult();
}
@Transactional
public Optional<Device> findByDeviceUrl(String deviceUrl) {
return Optional.ofNullable(deviceUrl).flatMap(url -> stream("deviceURL is not null")
.filter(device -> ipAddressHelper.removeHttpFromIpAddress(device.getDeviceURL()).equals(url))
.findFirst());
}
@Transactional
public List<Device> getSubDevices(UUID parentDeviceId) {
return em.createQuery("FROM device WHERE parentId = :deviceId", Device.class)
.setParameter(DEVICE_ID, parentDeviceId)
.getResultList();
}
public List<Device> findBySerialNumber(String serialNumber, boolean isRootDevice, UUID plantId) {
String queryString = """
SELECT d FROM device d
JOIN deviceproperty property ON property.deviceId = d.id
WHERE
property.name = :propertyName AND
property.valueAsString = :propertyStringValue AND
d.plantId = :plantId
""";
if (isRootDevice) {
queryString += " AND d.parentId IS NULL";
} else {
queryString += " AND d.parentId IS NOT NULL";
}
return em.createQuery(queryString, Device.class)
.setParameter("propertyName", RittalDevicePropertyNameEnum.SERIAL_NUMBER.getLabel())
.setParameter("propertyStringValue", serialNumber)
.setParameter("plantId", plantId)
.getResultList();
}
public void updateMode(UUID id, ComponentMode mode) {
update("mode = ?1 where id = ?2", mode, id);
}
public List<Device> findAllRootDevices() {
return stream("parentId is null")
.toList();
}
public List<Device> findByParentId(UUID parentId) {
return find("parentId", parentId)
.list();
}
}

View File

@@ -0,0 +1,153 @@
package io.gec.raw.connector.device.control;
import io.gec.raw.connector.componentmode.dto.ComponentMode;
import io.gec.raw.connector.device.domain.DeviceMapping;
import io.gec.raw.connector.device.domain.DeviceOidValueMap;
import io.gec.raw.connector.device.enums.AccessDeviceTypeEnum;
import io.gec.raw.connector.device.enums.RittalDeviceConstants;
import io.gec.raw.connector.device.enums.RittalDevicePropertyNameEnum;
import io.gec.raw.connector.deviceproperty.entity.DevicePropertyMapping;
import io.gec.raw.connector.variable.entity.Variable;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static io.gec.raw.connector.device.enums.AccessDeviceTypeEnum.ACCESS_HANDLE;
import static io.gec.raw.connector.device.enums.AccessDeviceTypeEnum.ACCESS_KEYPAD;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.partitioningBy;
@ApplicationScoped
public class DeviceStructureService {
private static final String VARIABLE_DOT_SEPARATOR = ".";
private static final Map<String, AccessDeviceTypeEnum> DESCRIPTION_TO_TYPE_MAP = Map.of(
RittalDeviceConstants.ACCESS_HANDLE_DEVICE_NAME, ACCESS_HANDLE,
RittalDeviceConstants.ACCESS_KEYPAD_DEVICE_NAME, ACCESS_KEYPAD
);
@Inject
ExternalDeviceBuilder externalDeviceBuilder;
@Inject
RittalDeviceBuilder rittalDeviceBuilder;
public DeviceMapping createRittalDeviceStructure(DeviceOidValueMap oidValueMap,
UUID connectorId,
UUID discoveryWorkItemId,
String hostname) throws NumberFormatException {
DeviceMapping rootDeviceTree = rittalDeviceBuilder.buildDeviceTree(oidValueMap, connectorId, discoveryWorkItemId, hostname);
rootDeviceTree.getSubDevices().forEach(rootSubDevice -> {
List<DeviceMapping> subDevices = rootSubDevice.getSubDevices();
var variablesPartitionedByDot = rootSubDevice.getVariables().stream()
.collect(partitioningBy(this::variableNameContainsDot));
var variablesWithDotInName = variablesPartitionedByDot.get(true);
Map<String, List<Variable>> variablesByDeviceName = groupByDeviceName(variablesWithDotInName);
List<String> deviceNames = List.copyOf(variablesByDeviceName.keySet());
variablesByDeviceName.forEach((deviceName, variables) -> {
int devicePosition = deviceNames.indexOf(deviceName) + 1;
DeviceMapping deviceMapping = createDeviceMappingFromVariables(deviceName, devicePosition, variables, rootSubDevice, oidValueMap);
subDevices.add(deviceMapping);
});
var variablesWithoutDotInName = variablesPartitionedByDot.get(false);
rootSubDevice.setVariables(variablesWithoutDotInName);
});
return rootDeviceTree;
}
public DeviceMapping createExternalDeviceStructure(DeviceOidValueMap oidValueMap,
UUID connectorId,
UUID dwiId,
UUID driverHeaderId,
String key) throws NumberFormatException {
return externalDeviceBuilder.buildDeviceTree(oidValueMap, connectorId, dwiId, driverHeaderId, key);
}
private boolean variableNameContainsDot(Variable variable) {
return variable.getName().contains(VARIABLE_DOT_SEPARATOR);
}
private LinkedHashMap<String, List<Variable>> groupByDeviceName(List<Variable> variablesWithDotInName) {
return variablesWithDotInName.stream()
.collect(groupingBy(this::getVariableNamePartBeforeDot, LinkedHashMap::new, Collectors.toList()));
}
private String getVariableNamePartBeforeDot(Variable variable) {
return variable.getName().substring(0, variable.getName().indexOf(VARIABLE_DOT_SEPARATOR));
}
private DeviceMapping createDeviceMappingFromVariables(String deviceName, int devicePosition,
List<Variable> variables,
DeviceMapping rootSubDevice,
DeviceOidValueMap oidValueMap) {
List<DevicePropertyMapping> devicePropertyMappings = createSubDeviceProperties(deviceName, devicePosition, variables, rootSubDevice, oidValueMap);
String deviceAlias = toDeviceAlias(devicePropertyMappings, deviceName);
return DeviceMapping.builder()
.name(deviceAlias)
.description(deviceName)
.connectorId(rootSubDevice.getConnectorId())
.discoveryWorkItemId(rootSubDevice.getDiscoveryWorkItemId())
.variables(variables)
.properties(devicePropertyMappings)
.isRoot(false)
.deviceIndex(rootSubDevice.getDeviceIndex()) // INFO: HAVE TO BE SAME AS FOR PARENT DEVICE - used in access log area
.mode(rootSubDevice.getMode())
.build();
}
private List<DevicePropertyMapping> createSubDeviceProperties(String deviceName, int devicePosition,
List<Variable> variables,
DeviceMapping rootSubDevice,
DeviceOidValueMap oidValueMap) {
Optional<DevicePropertyMapping> sensorDeviceAlias = rittalDeviceBuilder.getDeviceAliasPropertyByVariables(variables, oidValueMap);
Optional<DevicePropertyMapping> accessDeviceType = Optional.ofNullable(toAccessDeviceTypeProperty(deviceName, rootSubDevice, oidValueMap));
Optional<DevicePropertyMapping> bus = findPropertyMapping(rootSubDevice.getProperties(), RittalDevicePropertyNameEnum.BUS);
Optional<DevicePropertyMapping> position = createPositionProperty(devicePosition);
return Stream.of(sensorDeviceAlias, accessDeviceType, bus, position)
.flatMap(Optional::stream)
.toList();
}
private DevicePropertyMapping toAccessDeviceTypeProperty(String deviceName, DeviceMapping rootSubDevice, DeviceOidValueMap oidValueMap) {
if (rootSubDevice != null
&& RittalDeviceConstants.ACCESS_DEVICE_TYPE_OID.equals(rootSubDevice.getType())
&& DESCRIPTION_TO_TYPE_MAP.containsKey(deviceName)) {
return rittalDeviceBuilder.createAccessDeviceTypeProperty(oidValueMap, DESCRIPTION_TO_TYPE_MAP.get(deviceName));
}
return null;
}
Optional<DevicePropertyMapping> findPropertyMapping(List<DevicePropertyMapping> devicePropertyMappings, RittalDevicePropertyNameEnum name) {
return devicePropertyMappings.stream()
.filter(propertyMapping -> name.getLabel().equals(propertyMapping.getName()))
.findFirst();
}
private Optional<DevicePropertyMapping> createPositionProperty(int devicePosition) {
return Optional.of(DevicePropertyMapping.builder()
.name(RittalDevicePropertyNameEnum.POSITION.getLabel())
.value(String.valueOf(devicePosition))
.valueDatatype(RittalDevicePropertyNameEnum.POSITION.getDataType().name())
.readOnly(true)
.mode(ComponentMode.PAUSE)
.build());
}
private String toDeviceAlias(List<DevicePropertyMapping> devicePropertyMappings, String deviceName) {
return findPropertyMapping(devicePropertyMappings, RittalDevicePropertyNameEnum.DEVICE_ALIAS)
.map(DevicePropertyMapping::getValue)
.orElse(deviceName);
}
}

View File

@@ -0,0 +1,112 @@
package io.gec.raw.connector.device.control;
import io.gec.raw.connector.driver.entity.DriverVariable;
import io.gec.raw.connector.driver.control.DriverVariableRepository;
import io.gec.raw.connector.variable.entity.Variable;
import io.gec.raw.connector.device.domain.DeviceOidValueMap;
import io.gec.raw.connector.device.entity.AccessTypeEnum;
import io.gec.raw.connector.device.entity.DataTypeEnum;
import io.gec.raw.connector.device.domain.DeviceMapping;
import io.gec.raw.connector.variable.entity.VarTypeEnum;
import io.gec.raw.connector.device.enums.ExternalDevicePropertyEnum;
import io.gec.raw.connector.deviceproperty.entity.DevicePropertyMapping;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.List;
import java.util.UUID;
import static io.gec.raw.connector.deviceproperty.entity.DevicePropertyMapping.createPropertyMapping;
@ApplicationScoped
public class ExternalDeviceBuilder {
public static final String NO_QUALITY_OID = "NO_QUALITY_OID";
private static final String HTTPS_PREFIX = "https://";
@Inject
DriverVariableRepository driverVariableRepository;
public DeviceMapping buildDeviceTree(DeviceOidValueMap oidValueMap, UUID connectorId, UUID discoveryWorkItemId, UUID driverHeaderId, String key)
throws NumberFormatException {
return DeviceMapping.builder()
.name(oidValueMap.getSysName())
.description(oidValueMap.getSysDescr())
.connectorId(connectorId)
.deviceURL(HTTPS_PREFIX + key)
.discoveryWorkItemId(discoveryWorkItemId)
.variables(createVariables(driverHeaderId))
.properties(createProperties(oidValueMap))
.driverHeaderId(driverHeaderId)
.build();
}
private List<Variable> createVariables(UUID driverHeaderId) {
List<DriverVariable> driverVariableList = driverVariableRepository.findByDriverHeaderId(driverHeaderId);
return driverVariableList.stream()
.map(this::toVariable)
.toList();
}
private List<DevicePropertyMapping> createProperties(DeviceOidValueMap oidValueMap) {
return List.of(
createPropertyMapping(ExternalDevicePropertyEnum.DESCRIPTION, oidValueMap.getSysDescr()),
createPropertyMapping(ExternalDevicePropertyEnum.NAME, oidValueMap.getSysName()),
createPropertyMapping(ExternalDevicePropertyEnum.LOCATION, oidValueMap.getSysLocation()),
createPropertyMapping(ExternalDevicePropertyEnum.CONTACT, oidValueMap.getSysContact()),
createPropertyMapping(ExternalDevicePropertyEnum.SERIAL_NUMBER, oidValueMap.getSysDescr())
//TODO add hardcoded properties
);
}
private Variable toVariable(DriverVariable driverVariable) {
return Variable.builder()
.name(driverVariable.getName())
.description(driverVariable.getDescription())
.min(Integer.valueOf(driverVariable.getMin()))
.max(Integer.valueOf(driverVariable.getMax()))
.varType(VarTypeEnum.VALUE)
.varDataType(driverVariable.getDataType())
.snmpSetVarDataType(driverVariable.getDataType())
.scale(Integer.parseInt(driverVariable.getScale()))
.step(Integer.parseInt(driverVariable.getStep()))
.varAccessType(toVarAccessType(driverVariable))
.varQualityOid(NO_QUALITY_OID)
.valueAsIntegerOid(toValueAsIntegerOid(driverVariable))
.valueAsStringOid(toValueAsStringOid(driverVariable))
.subscriptionInterval(Integer.parseInt(driverVariable.getIntervalFactor()))
.driverVariableId(driverVariable.getId())
.functionType(driverVariable.getFunctionType())
.build();
}
private AccessTypeEnum toVarAccessType(DriverVariable driverVariable) {
return driverVariable.getIsReadOnly()
? AccessTypeEnum.READONLY
: AccessTypeEnum.READWRITE;
}
private String toValueAsIntegerOid(DriverVariable driverVariable) {
if (isRepresentedByNumber(driverVariable)) {
return driverVariable.getOid();
}
return "";
}
private String toValueAsStringOid(DriverVariable driverVariable) {
if (!isRepresentedByNumber(driverVariable)) {
return driverVariable.getOid();
}
return "";
}
private boolean isRepresentedByNumber(DriverVariable driverVariable) {
return driverVariable.getDataType().equals(DataTypeEnum.INTEGER)
|| driverVariable.getDataType().equals(DataTypeEnum.FLOAT)
|| driverVariable.getDataType().equals(DataTypeEnum.NUMERIC)
|| driverVariable.getDataType().equals(DataTypeEnum.STATUSINT)
|| driverVariable.getDataType().equals(DataTypeEnum.ANALOGINT);
}
}

View File

@@ -0,0 +1,354 @@
package io.gec.raw.connector.device.control;
import io.gec.raw.connector.componentmode.dto.ComponentMode;
import io.gec.raw.connector.device.domain.DeviceMapping;
import io.gec.raw.connector.device.domain.DeviceOidValueMap;
import io.gec.raw.connector.device.entity.AccessTypeEnum;
import io.gec.raw.connector.device.entity.DataTypeEnum;
import io.gec.raw.connector.device.enums.*;
import io.gec.raw.connector.deviceproperty.control.DeviceTypeProvider;
import io.gec.raw.connector.deviceproperty.entity.DevicePropertyMapping;
import io.gec.raw.connector.utils.control.RittalVariableConfigList;
import io.gec.raw.connector.utils.control.ValueScalingHelper;
import io.gec.raw.connector.variable.control.VariableConstraintParser;
import io.gec.raw.connector.variable.entity.VarTypeEnum;
import io.gec.raw.connector.variable.entity.Variable;
import jakarta.annotation.Nullable;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import static io.gec.raw.connector.device.enums.VariableFieldName.*;
import static io.gec.raw.connector.utils.control.RittalVariableConfigList.excludedVariableNameList;
import static java.lang.Integer.parseInt;
@ApplicationScoped
public class RittalDeviceBuilder {
@Inject
VariableConstraintParser constraintParser;
@Inject
ValueScalingHelper scalingHelper;
@Inject
DeviceTypeProvider deviceTypeProvider;
public DeviceMapping buildDeviceTree(DeviceOidValueMap oidValueMap, UUID connectorId, UUID discoveryWorkItemId, String hostname)
throws NumberFormatException {
DeviceMapping rootDevice = DeviceMapping.builder()
.deviceURL(RittalDeviceConstants.HTTPS_PREFIX + hostname)
.name(oidValueMap.getSysName())
.description(oidValueMap.getSysDescr())
.connectorId(connectorId)
.discoveryWorkItemId(discoveryWorkItemId)
.properties(buildRootDeviceProperties(oidValueMap, hostname))
.mode(ComponentMode.PAUSE)
.isRoot(true)
.build();
int numberOfDevices = parseInt((oidValueMap.get(".1.3.6.1.4.1.2606.7.4.1.1.2.0").toString()));
var deviceIndexes = getDeviceIndexes(oidValueMap, numberOfDevices);
for (int subDevNumber = 0; subDevNumber < numberOfDevices; subDevNumber++) {
rootDevice.getSubDevices().add(buildSubDevice(oidValueMap, deviceIndexes[subDevNumber], connectorId, discoveryWorkItemId));
}
return rootDevice;
}
int[] getDeviceIndexes(DeviceOidValueMap oidValueMap, int numberOfDevices) {
int deviceCounter = 1;
int deviceResultCounter = 0;
int[] result = new int[numberOfDevices];
while (deviceResultCounter < numberOfDevices && deviceCounter <= numberOfDevices) {
var searchString = oidValueMap.get(String.format(".1.3.6.1.4.1.2606.7.4.1.2.1.2.%s", deviceCounter));
if (searchString != null) {
result[deviceResultCounter++] = deviceCounter;
}
deviceCounter++;
}
return result;
}
DeviceMapping buildSubDevice(DeviceOidValueMap oidValueMap, int subDevNumber, UUID connectorId, UUID discoveryWorkItemId) {
var deviceName = oidValueMap.get(RittalDevicePropertyNameEnum.NAME.getIndexedOid(subDevNumber)).toString();
var deviceAlias = oidValueMap.get(RittalDevicePropertyNameEnum.DEVICE_ALIAS.getIndexedOid(subDevNumber)).toString();
DeviceMapping subDevice = DeviceMapping.builder()
.description(deviceName)
.type(oidValueMap.get(RittalDevicePropertyNameEnum.TYPE.getIndexedOid(subDevNumber)).toString())
.name(deviceAlias.isEmpty() ? deviceName : deviceAlias)
.properties(buildSubDeviceProperties(subDevNumber, oidValueMap))
.connectorId(connectorId)
.discoveryWorkItemId(discoveryWorkItemId)
.isRoot(false)
.mode(ComponentMode.PAUSE)
.deviceIndex(subDevNumber)
.build();
int numberSnmpVars = parseInt(oidValueMap.get(RittalDevicePropertyNameEnum.NUMBER_OF_VARIABLES.getIndexedOid(subDevNumber)).toString());
if (numberSnmpVars > 0) {
subDevice.setVariables(buildVariables(numberSnmpVars, subDevNumber, oidValueMap));
}
return subDevice;
}
List<DevicePropertyMapping> buildRootDeviceProperties(DeviceOidValueMap oidValueMap, String hostname) {
List<DevicePropertyMapping> properties = Stream.of(
RittalDevicePropertyNameEnum.ROOT_SERIAL_NUMBER,
RittalDevicePropertyNameEnum.ROOT_HARDWARE_VERSION,
RittalDevicePropertyNameEnum.ROOT_SOFTWARE_VERSION,
RittalDevicePropertyNameEnum.ROOT_PRODUCTION_CODE,
RittalDevicePropertyNameEnum.ROOT_UNIT_TYPE
).map(property -> createPropertyMapping(property, oidValueMap, null))
.collect(Collectors.toList());
properties.add(createAccessDeviceTypeProperty(oidValueMap, AccessDeviceTypeEnum.ACCESS_MANAGEMENT_DEVICE));
properties.add(createIpAddressProperty(hostname));
return properties;
}
List<Variable> buildVariables(int numberSnmpVars, int subDeviceIndex, DeviceOidValueMap oidValueMap) {
return IntStream.rangeClosed(1, numberSnmpVars)
.mapToObj(varIndex -> buildVariableFromSnmpVar(varIndex, subDeviceIndex, oidValueMap))
.filter(variable -> !isVariableExcluded(variable.getName()))
.toList();
}
List<DevicePropertyMapping> buildSubDeviceProperties(int subDevNumber, DeviceOidValueMap oidValueMap) {
List<DevicePropertyMapping> properties = Stream.of(
RittalDevicePropertyNameEnum.DEVICE_ALIAS,
RittalDevicePropertyNameEnum.NAME,
RittalDevicePropertyNameEnum.TYPE,
RittalDevicePropertyNameEnum.NODE_ID,
RittalDevicePropertyNameEnum.STATUS,
RittalDevicePropertyNameEnum.MODEL_NUMBER,
RittalDevicePropertyNameEnum.LOCATION,
RittalDevicePropertyNameEnum.BUS,
RittalDevicePropertyNameEnum.POSITION,
RittalDevicePropertyNameEnum.SOFTWARE_VERSION,
RittalDevicePropertyNameEnum.HARDWARE_VERSION,
RittalDevicePropertyNameEnum.SERIAL_NUMBER,
RittalDevicePropertyNameEnum.NUMBER_OF_VARIABLES,
RittalDevicePropertyNameEnum.STATUS_TEXT)
.map(property -> createPropertyMapping(property, oidValueMap, subDevNumber))
.collect(Collectors.toList());
getAccessDeviceTypeProperty(oidValueMap, subDevNumber).ifPresent(properties::add);
return properties;
}
Optional<DevicePropertyMapping> getAccessDeviceTypeProperty(DeviceOidValueMap oidValueMap, Integer deviceIndex) {
var typeValue = oidValueMap.get(RittalDevicePropertyNameEnum.TYPE.getIndexedOid(deviceIndex)).toString();
if (RittalDeviceConstants.ACCESS_DEVICE_TYPE_OID.equals(typeValue)) {
var accessDeviceTypeProperty = createAccessDeviceTypeProperty(oidValueMap, AccessDeviceTypeEnum.ACCESS_DEVICE);
return Optional.of(accessDeviceTypeProperty);
}
return Optional.empty();
}
public DevicePropertyMapping createAccessDeviceTypeProperty(DeviceOidValueMap oidValueMap, AccessDeviceTypeEnum accessDeviceType) {
var accessDeviceTypeProperty = createPropertyMapping(RittalDevicePropertyNameEnum.ACCESS_DEVICE_TYPE, oidValueMap, null);
accessDeviceTypeProperty.setValue(accessDeviceType.name());
return accessDeviceTypeProperty;
}
DevicePropertyMapping createIpAddressProperty(String hostname) {
return createPropertyMapping(RittalDevicePropertyNameEnum.ROOT_IP_ADDRESS, hostname);
}
DevicePropertyMapping createPropertyMapping(RittalDevicePropertyNameEnum rittalDevicePropertyNameEnum, String value) {
return createPropertyMapping(
rittalDevicePropertyNameEnum.getLabel(),
null,
value,
rittalDevicePropertyNameEnum.getDataType().name(),
rittalDevicePropertyNameEnum.isReadOnly());
}
DevicePropertyMapping createPropertyMapping(RittalDevicePropertyNameEnum rittalDevicePropertyEnum, DeviceOidValueMap oidValueMap, @Nullable Integer deviceIndex) {
var oid = deviceIndex == null ? rittalDevicePropertyEnum.getBaseOid() : rittalDevicePropertyEnum.getIndexedOid(deviceIndex);
var value = Optional.ofNullable(oidValueMap.get(oid))
.orElse(RittalDeviceConstants.PROPERTY_NO_VALUE)
.toString();
if (rittalDevicePropertyEnum == RittalDevicePropertyNameEnum.TYPE) {
value = deviceTypeProvider.findByOid(value);
}
return createPropertyMapping(rittalDevicePropertyEnum.getLabel(), oid, value, rittalDevicePropertyEnum.getDataType().name(), rittalDevicePropertyEnum.isReadOnly());
}
private DevicePropertyMapping createPropertyMapping(String name, String oid, String value, String valueDataType, boolean readOnly) {
return DevicePropertyMapping.builder()
.oid(oid)
.name(name)
.value(value)
.valueDatatype(valueDataType)
.readOnly(readOnly)
.mode(ComponentMode.PAUSE)
.build();
}
Optional<DevicePropertyMapping> getDeviceAliasPropertyByVariables(List<Variable> sensorVariables,
DeviceOidValueMap oidValueMap) {
return sensorVariables.stream()
.filter(variable -> variable.getName().toLowerCase().endsWith(RittalDeviceConstants.VARIABLE_DESC_NAME))
.findFirst()
.map(descNameVariable -> toDeviceAliasProperty(oidValueMap, descNameVariable));
}
private DevicePropertyMapping toDeviceAliasProperty(DeviceOidValueMap oidValueMap, Variable v) {
return DevicePropertyMapping.builder()
.oid(v.getValueAsStringOid())
.name(RittalDevicePropertyNameEnum.DEVICE_ALIAS.getLabel())
.value(String.valueOf(oidValueMap.getOrDefault(v.getValueAsStringOid(), RittalDeviceConstants.PROPERTY_NO_VALUE)))
.valueDatatype(PropertyClassType.STRING.name())
.mode(ComponentMode.PAUSE)
.readOnly(false)
.build();
}
private boolean isVariableExcluded(String variableName) {
return excludedVariableNameList.stream()
.anyMatch(excludedVariableName ->
variableName.toLowerCase().endsWith(excludedVariableName)
);
}
private Variable buildVariableFromSnmpVar(int varStartIndex, int subDeviceIndex, DeviceOidValueMap oidValueMap) {
VarTypeEnum varType = VarTypeEnum.findByNumber(parseInt(getOidValue(oidValueMap, VARTYPE, subDeviceIndex, varStartIndex)[1]));
DataTypeEnum varDataType = DataTypeEnum.findByNumber(parseInt(getOidValue(oidValueMap, DATATYPE, subDeviceIndex, varStartIndex)[1]));
AccessTypeEnum varAccessType = AccessTypeEnum.findByNumber(parseInt(getOidValue(oidValueMap, ACCESS, subDeviceIndex, varStartIndex)[1]));
Variable variable = new Variable();
variable.setName(getOidValue(oidValueMap, NAME, subDeviceIndex, varStartIndex)[1]);
variable.setVarType(varType);
variable.setUnit(getOidValue(oidValueMap, UNIT, subDeviceIndex, varStartIndex)[1]);
variable.setVarDataType(varDataType);
variable.setSnmpSetVarDataType(varDataType);
variable.setScale(getOidValue(oidValueMap, SCALE, subDeviceIndex, varStartIndex)[1]);
variable.setConstraints(constraintParser.parseConstraints(getOidValue(oidValueMap, CONSTRAINTS, subDeviceIndex, varStartIndex)[1]));
variable.setStep(getOidValue(oidValueMap, STEPS, subDeviceIndex, varStartIndex)[1]);
variable.setMode(ComponentMode.PAUSE);
variable.setVarAccessType(varAccessType);
//INFO: String and Int Values
String[] stringResult = getOidValue(oidValueMap, VALUESTRING, subDeviceIndex, varStartIndex);
String[] integerResult = getOidValue(oidValueMap, VALUEINT, subDeviceIndex, varStartIndex);
variable.setValueAsStringOid(stringResult[0]);
variable.setValueAsIntegerOid(integerResult[0]);
//INFO: Quality
var qualityResult = getOidValue(oidValueMap, QUALITY, subDeviceIndex, varStartIndex);
variable.setVarQualityOid(qualityResult[0]);
variable.setSubscriptionInterval(getInterval(variable));
variable.setVarDataType(getType(variable));
variable.setFunctionType(getFunctionType(variable));
if (variable.getSnmpSetVarDataType() == DataTypeEnum.INTEGER) {
var minMaxMap = constraintParser.parseMinMaxConstraint(variable.getConstraints());
if (minMaxMap.get("min") != null) {
variable.setMin(scalingHelper.scaleNumericValueFromDevice(minMaxMap.get("min"), variable.getScale()));
}
if (minMaxMap.get("max") != null) {
variable.setMax(scalingHelper.scaleNumericValueFromDevice(minMaxMap.get("max"), variable.getScale()));
}
}
//INFO: If min max Null Ignore
return variable;
}
// Intervals
/*
* 5 minutes for SetPtHigh and SetPtLow for Alarm and Warnings.
5 minutes for DescName, Hysteresis, Category, Offset
5 minutes for Circuit, Socket Type, Grouping
10 secs for Values, Usage, Signal, Position
30 secs for Status, Relay
once : Size
*/
int getInterval(Variable variable) {
var tempName = variable.getName();
if (tempName.contains("Value") || tempName.contains("Usage") ||
tempName.contains("Signal") || tempName.contains("Position")) {
return 10;
} else if (tempName.contains("Status") || tempName.contains("Relay")) {
return 30;
} else if (tempName.contains("Size")) {
return 1800;
}
return 300;
}
FunctionTypeEnum getFunctionType(Variable variable) {
String variableName = variable.getName().toLowerCase();
if (variableName.endsWith(RittalDeviceConstants.VARIABLE_STATUS)) {
return FunctionTypeEnum.STATE;
}
Optional<FunctionTypeEnum> trapFunctionType = RittalVariableConfigList.contains_FunctionTypeMap.entrySet().stream()
.filter(entry -> entry.getKey().stream().anyMatch(variableName::contains))
.map(Map.Entry::getValue)
.findFirst();
if (trapFunctionType.isPresent()) {
return trapFunctionType.get();
}
Optional<FunctionTypeEnum> specificFunctionType = RittalVariableConfigList.endsWith_specificFunctionTypeMap.entrySet().stream()
.filter(entry -> entry.getKey().stream().anyMatch(variableName::endsWith))
.map(Map.Entry::getValue)
.findFirst();
if (specificFunctionType.isPresent()) {
return specificFunctionType.get();
}
Optional<FunctionTypeEnum> generalFunctionType = RittalVariableConfigList.endsWith_generalFunctionTypeMap.entrySet().stream()
.filter(entry -> entry.getKey().stream().anyMatch(variableName::endsWith))
.map(Map.Entry::getValue)
.findFirst();
if (generalFunctionType.isPresent()) {
return generalFunctionType.get();
}
return FunctionTypeEnum.VALUE;
}
DataTypeEnum getType(Variable variable) {
if (variable.getName().toLowerCase().endsWith(RittalDeviceConstants.VARIABLE_STATUS)) {
return DataTypeEnum.STATUSINT;
} else if (variable.getVarDataType() == DataTypeEnum.INTEGER && (variable.getScale() > 1 || variable.getScale() < 0)) {
return DataTypeEnum.FLOAT;
} else if (variable.getVarDataType() == DataTypeEnum.INTEGER) {
return DataTypeEnum.NUMERIC;
} else if (variable.getVarDataType() == DataTypeEnum.ENUM) {
//TODO mabe Change to dedicated type
return DataTypeEnum.STATUSINT; //INFO: Cant be Numeric because of scaling
} else {
return DataTypeEnum.STRING;
}
}
String[] getOidValue(DeviceOidValueMap oidValueMap, VariableFieldName variableFieldName, int subDeviceIndex, int varStartIndex) {
String oid = String.format(".1.3.6.1.4.1.2606.7.4.2.2.1.%s.%s.%s", variableFieldName.getValue(), subDeviceIndex, varStartIndex);
Object value = oidValueMap.get(oid);
if (value == null) {
throw new NoSuchElementException("Can't find value in oidValueMap for OID: " + oid);
}
return new String[] {oid, value.toString()};
}
}

View File

@@ -0,0 +1,40 @@
package io.gec.raw.connector.device.domain;
import io.gec.raw.connector.componentmode.dto.ComponentMode;
import io.gec.raw.connector.deviceproperty.entity.DevicePropertyMapping;
import io.gec.raw.connector.variable.entity.Variable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeviceMapping {
private String description;
private String name;
private String type;
private String parentId;
private String id;
private String deviceURL;
private UUID connectorId;
private UUID discoveryWorkItemId;
private boolean isRoot;
private UUID driverHeaderId;
private ComponentMode mode;
@Builder.Default
private Integer deviceIndex = 0;
@Builder.Default
private List<DeviceMapping> subDevices = new ArrayList<>();
@Builder.Default
private List<Variable> variables = new ArrayList<>();
@Builder.Default
private List<DevicePropertyMapping> properties = new ArrayList<>();
}

View File

@@ -0,0 +1,103 @@
package io.gec.raw.connector.device.domain;
import io.gec.raw.connector.device.enums.ExternalDeviceConstants;
import java.util.HashMap;
/**
* @author Frank.Pruefer
*/
public class DeviceOidValueMap extends HashMap<String, Object> {
private String sysDescr;
private String sysObjectId;
private String sysUpTime;
private String sysContact;
private String sysName;
private String sysLocation;
private String sysServices;
private String enterprise;
/**
* Set an oid as a string and the corresponding variable value
*
* @param oid the oid
* @param value the value
*/
@Override
public Object put(String oid, Object value) {
switch (oid) {
case ExternalDeviceConstants.OID_DESCRIPTION:
sysDescr = value.toString();
break;
case ExternalDeviceConstants.OID_SYS_OBJECT_ID:
sysObjectId = value.toString();
break;
case ExternalDeviceConstants.OID_SYS_UP_TIME:
sysUpTime = value.toString();
break;
case ExternalDeviceConstants.OID_SYS_CONTACT:
sysContact = value.toString();
break;
case ExternalDeviceConstants.OID_SYS_NAME:
sysName = value.toString();
break;
case ExternalDeviceConstants.OID_SYS_LOCATION:
sysLocation = value.toString();
break;
case ExternalDeviceConstants.OID_SYS_SERVICES:
sysServices = value.toString();
break;
default:
if (!oid.startsWith(ExternalDeviceConstants.OID_MIB_2_PREFIX)) {
try {
if (getEnterprise().isEmpty()) {
enterprise = oid.split("\\.")[7];
}
} catch (Exception e) {
// ignore this exception when enterprise can't be determined from oid string
}
return super.put(oid, value);
}
}
return null;
}
/**
* @return true, if all oids for a default device are set (from sysDesc to sysServices)
*/
public boolean isComplete() {
return sysDescr != null && sysObjectId != null && sysContact != null && sysName != null
&& sysLocation != null;
}
public String getEnterprise() {
return enterprise == null ? "" : enterprise;
}
public String getSysDescr() {
return sysDescr == null ? "" : sysDescr;
}
public String getSysObjectId() {
return sysObjectId == null ? "" : sysObjectId;
}
public String getSysUpTime() {
return sysUpTime == null ? "" : sysUpTime;
}
public String getSysContact() {
return sysContact == null ? "" : sysContact;
}
public String getSysName() {
return sysName == null ? "" : sysName;
}
public String getSysLocation() {
return sysLocation == null ? "" : sysLocation;
}
}

View File

@@ -0,0 +1,59 @@
/*
* (c) 2023 German Edge Cloud GmbH & Co. KG
*
*/
package io.gec.raw.connector.device.dto;
import io.gec.raw.connector.componentmode.dto.ComponentMode;
import io.gec.raw.connector.variable.dto.VariableDTO;
import io.gec.raw.connector.device.entity.Device;
import io.gec.raw.connector.deviceproperty.dto.DevicePropertyDTO;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.UUID;
@Getter
@Setter
@Builder
public class DeviceDTO {
private UUID id;
private UUID connectorId;
private UUID connectorDeviceId;
private UUID discoveryWorkitemId;
private String name;
private String description;
private String type;
private UUID parentId;
private String deviceUrl;
private UUID plantId;
private String assignedToProject;
private UUID commSvcId;
private UUID driverHeaderId;
private ComponentMode mode;
private List<VariableDTO> variables;
private List<DevicePropertyDTO> properties;
private List<UUID> communicationProtocolIds;
private List<DeviceDTO> subDevices;
public static DeviceDTO from(Device entity) {
return DeviceDTO.builder()
.id(entity.getCommServiceId())
.connectorId(entity.getConnectorId())
.connectorDeviceId(entity.getId())
.discoveryWorkitemId(entity.getDiscoveryWorkItemId())
.name(entity.getName())
.description(entity.getDescription())
.type(entity.getType())
.parentId(entity.getParentId())
.deviceUrl(entity.getDeviceURL())
.plantId(entity.getPlantId())
.mode(entity.getMode())
.driverHeaderId(entity.getDriverHeaderId())
.build();
}
}

View File

@@ -0,0 +1,15 @@
package io.gec.raw.connector.device.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.UUID;
@Getter
@Setter
public class DevicePropertyResponseDTO {
private UUID id;
private UUID deviceId;
private UUID connectorPropertyId;
}

View File

@@ -0,0 +1,20 @@
package io.gec.raw.connector.device.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.UUID;
@Getter
@Setter
public class DeviceResponseDTO {
private UUID id;
private UUID connectorId;
private UUID connectorDeviceId;
private UUID parentId;
private List<DeviceResponseDTO> subdevices;
private List<VariableResponseDTO> variables;
private List<DevicePropertyResponseDTO> properties;
}

View File

@@ -0,0 +1,17 @@
package io.gec.raw.connector.device.dto;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.UUID;
@Getter
@Setter
@Builder
public class VariableResponseDTO {
private UUID id;
private UUID deviceId;
private UUID connectorVariableId;
}

View File

@@ -0,0 +1,31 @@
package io.gec.raw.connector.device.entity;
import lombok.Getter;
@Getter
public enum AccessTypeEnum {
// CMC3 - Variable access types
NONE("NONE", 1),
READONLY("READONLY", 2),
READWRITE("READWRITE", 3),
READWRITESWITCH("READWRITESWITCH", 4),
READWRITEEXTENDED("READWRITEEXTENDED", 5);
private final String ordinal;
private final Integer number;
AccessTypeEnum(String ordinal, Integer number) {
this.ordinal = ordinal;
this.number = number;
}
public static AccessTypeEnum findByNumber(Integer number) {
for (AccessTypeEnum accessType : AccessTypeEnum.values()) {
if (accessType.getNumber().equals(number)) {
return accessType;
}
}
return null;
}
}

View File

@@ -0,0 +1,31 @@
package io.gec.raw.connector.device.entity;
import lombok.Getter;
@Getter
public enum DataTypeEnum {
// CMC3 - Variables Datatype enum
INTEGER(2),
STRING(3),
ENUM(4),
ANALOGINT(5),
STATUSINT(6),
NUMERIC(7),
FLOAT(8);
private final Integer number;
DataTypeEnum(Integer number) {
this.number = number;
}
public static DataTypeEnum findByNumber(Integer number) {
for (DataTypeEnum dataType : DataTypeEnum.values()) {
if (dataType.getNumber().equals(number)) {
return dataType;
}
}
return null;
}
}

View File

@@ -0,0 +1,52 @@
package io.gec.raw.connector.device.entity;
import io.gec.raw.connector.base.entity.SimpleBaseEntity;
import io.gec.raw.connector.componentmode.dto.ComponentMode;
import jakarta.enterprise.context.Dependent;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.util.UUID;
@Dependent
@Entity(name = "device")
@Table(name = "device", schema = "connector_svc")
@Getter
@Setter
public class Device extends SimpleBaseEntity {
private String name;
private String description;
private String type;
@Column(name = "parent_id")
private UUID parentId;
private String deviceURL;
@Column(name = "plant_id")
private UUID plantId;
@Column(name = "connector_id")
private UUID connectorId;
@Column(name = "comm_svc_id")
private UUID commServiceId;
@Column(name = "discoveryWorkitem_id")
private UUID discoveryWorkItemId;
@Column(name = "driver_header_id")
private UUID driverHeaderId;
@Column(name = "device_index", nullable = false)
private Integer deviceIndex;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private ComponentMode mode;
@Enumerated(EnumType.STRING)
@Column(name = "restore_mode", nullable = false)
private ComponentMode restoreMode;
}

View File

@@ -0,0 +1,8 @@
package io.gec.raw.connector.device.enums;
public enum AccessDeviceTypeEnum {
ACCESS_DEVICE,
ACCESS_MANAGEMENT_DEVICE,
ACCESS_HANDLE,
ACCESS_KEYPAD,
}

View File

@@ -0,0 +1,15 @@
package io.gec.raw.connector.device.enums;
public class ExternalDeviceConstants {
private ExternalDeviceConstants() {}
public static final String OID_MIB_2_PREFIX = ".1.3.6.1.2.1.";
public static final String OID_DESCRIPTION = ".1.3.6.1.2.1.1.1.0";
public static final String OID_SYS_OBJECT_ID = ".1.3.6.1.2.1.1.2.0";
public static final String OID_SYS_UP_TIME = ".1.3.6.1.2.1.1.3.0";
public static final String OID_SYS_CONTACT = ".1.3.6.1.2.1.1.4.0";
public static final String OID_SYS_NAME = ".1.3.6.1.2.1.1.5.0";
public static final String OID_SYS_LOCATION = ".1.3.6.1.2.1.1.6.0";
public static final String OID_SYS_SERVICES = ".1.3.6.1.2.1.1.7.0";
}

View File

@@ -0,0 +1,21 @@
package io.gec.raw.connector.device.enums;
import io.gec.raw.connector.device.entity.DataTypeEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum ExternalDevicePropertyEnum {
DESCRIPTION("Description", ExternalDeviceConstants.OID_DESCRIPTION, DataTypeEnum.STRING, true),
NAME("Name", ExternalDeviceConstants.OID_SYS_NAME, DataTypeEnum.STRING, true),
LOCATION("Location", ExternalDeviceConstants.OID_SYS_LOCATION, DataTypeEnum.STRING, true),
CONTACT("Contact", ExternalDeviceConstants.OID_SYS_CONTACT, DataTypeEnum.STRING, true),
SERIAL_NUMBER("Serial number", ExternalDeviceConstants.OID_DESCRIPTION, DataTypeEnum.STRING, true);
private final String name;
private final String oid;
private final DataTypeEnum dataType;
private boolean readOnly;
}

View File

@@ -0,0 +1,12 @@
package io.gec.raw.connector.device.enums;
public enum FunctionTypeEnum {
UNDEFINED,
STATE,
VALUE,
SETTING,
CONFIGURATION,
SERVICE,
EXECUTE
}

View File

@@ -0,0 +1,5 @@
package io.gec.raw.connector.device.enums;
public enum PropertyClassType {
GROUP, INT, FLOAT, BOOL, STRING, URL, ENUM, IP_V4, IP_V6
}

View File

@@ -0,0 +1,98 @@
package io.gec.raw.connector.device.enums;
public class RittalDeviceConstants {
public static final String VARIABLE_DESC_NAME = ".descname";
//Excluded VariableNames
public static final String VARIABLE_KEY_CODE = ".keycode";
public static final String VARIABLE_KEY_COMMAND = ".keycommand";
public static final String VARIABLE_CATEGORY = ".category";
//Status functionType
public static final String VARIABLE_STATUS = ".status";
//setting functionType
public static final String VARIABLE_SETPT = ".setpt";
public static final String VARIABLE_OFFSET = ".offset";
public static final String VARIABLE_SENSITIVITY = ".sensitivity";
public static final String VARIABLE_GROUPING = ".grouping";
public static final String VARIABLE_HYSTERESIS = ".hysteresis";
public static final String VARIABLE_SETPOINT = ".setpoint";
public static final String VARIABLE_AVERAGE = ".average";
public static final String VARIABLE_LOGIC = ".logic";
public static final String VARIABLE_START = ".start";
public static final String VARIABLE_END = ".end";
public static final String VARIABLE_UNIT = ".unit";
public static final String VARIABLE_FACTOR = "factor";
public static final String VARIABLE_POWER_ON_DELAY = ".power on delay";
public static final String VARIABLE_MIN_VALUE = ".min.value";
public static final String VARIABLE_MAX_VALUE = ".max.value";
public static final String VARIABLE_MANUAL_VALUE = ".manual.value";
public static final String VARIABLE_CONFIG = ".config";
public static final String VARIABLE_GENERAL_ON_DELAY = ".general.on delay";
public static final String VARIABLE_GENERAL_ERROR_DELAY = ".general.error delay";
public static final String VARIABLE_ACTIVE_CUSTOM_ENERGY_VALUE = ".activecustomenergy.value";
//Execute functionType
public static final String VARIABLE_DELAY = ".delay";
public static final String VARIABLE_LOGIN_DELAY = "login.delay";
public static final String VARIABLE_RELAY = ".relay";
public static final String VARIABLE_COMMAND = ".command";
public static final String VARIABLE_ATTEMPTS = ".attempts";
public static final String VARIABLE_RED = ".red";
public static final String VARIABLE_GREEN = ".green";
public static final String VARIABLE_BLUE = ".blue";
public static final String VARIABLE_USER_COUNT = ".user count";
public static final String VARIABLE_GENERAL_GROUPING = ".general.grouping";
//Configuration functionType
public static final String VARIABLE_DESCNAME = ".descname";
public static final String VARIABLE_TYPE = ".type";
public static final String VARIABLE_CIRCUIT = ".circuit";
public static final String VARIABLE_SOCKET_TYPE = ".socket type";
public static final String VARIABLE_MODE = ".mode";
public static final String VARIABLE_LED_COMMAND = "led.command";
public static final String VARIABLE_SEQUENCE_MODE = ".sequence mode";
public static final String VARIABLE_CUSTOM_VALUE = "custom.value";
public static final String VARIABLE_ERROR_INFO = ".error info";
public static final String VARIABLE_POSITION = "position";
public static final String VARIABLE_CUSTOM_RUNTIME_VALUE = "custom.runtime.value";
public static final String VARIABLE_MESSAGE = "message";
public static final String VARIABLE_GENERAL_SEQUENCE_MODE = ".general.sequence mode";
//Service functionType
public static final String VARIABLE_SERVICE = "service";
public static final String VARIABLE_TRAP_RECEIVER = "trapreceiver";
public static final String VARIABLE_TRAP_ENABLE = "trapenable";
//other Constrains
public static final String PROPERTY_NO_VALUE = "No value";
public static final String HTTPS_PREFIX = "https://";
public static final String ACCESS_DEVICE_TYPE_OID = "1.3.6.1.4.1.2606.7.7.4.6144";
public static final String ACCESS_HANDLE_DEVICE_NAME = "Handle";
public static final String ACCESS_KEYPAD_DEVICE_NAME = "KeyPad";
public static final String OID_DEVICE_ALIAS = ".1.3.6.1.4.1.2606.7.4.1.2.1.3";
public static final String OID_NAME = ".1.3.6.1.4.1.2606.7.4.1.2.1.2";
public static final String OID_TYPE = ".1.3.6.1.4.1.2606.7.4.1.2.1.4";
public static final String OID_NODE_ID = ".1.3.6.1.4.1.2606.7.4.1.2.1.5";
public static final String OID_STATUS = ".1.3.6.1.4.1.2606.7.4.1.2.1.6";
public static final String OID_ORDER_NUMBER = ".1.3.6.1.4.1.2606.7.4.1.2.1.7";
public static final String OID_LOCATION = ".1.3.6.1.4.1.2606.7.4.1.2.1.8";
public static final String OID_BUS = ".1.3.6.1.4.1.2606.7.4.1.2.1.9";
public static final String OID_POSITION = ".1.3.6.1.4.1.2606.7.4.1.2.1.10";
public static final String OID_SOFTWARE_VERSION = ".1.3.6.1.4.1.2606.7.4.1.2.1.11";
public static final String OID_HARDWARE_VERSION = ".1.3.6.1.4.1.2606.7.4.1.2.1.12";
public static final String OID_SERIAL_NUMBER = ".1.3.6.1.4.1.2606.7.4.1.2.1.13";
public static final String OID_NUMBER_OF_VARIABLES = ".1.3.6.1.4.1.2606.7.4.1.2.1.17";
public static final String OID_STATUS_TEXT = ".1.3.6.1.4.1.2606.7.4.1.2.1.19";
public static final String OID_ROOT_SERIAL_NUMBER = ".1.3.6.1.4.1.2606.7.2.6.0";
public static final String OID_ROOT_HARDWARE_VERSION = ".1.3.6.1.4.1.2606.7.2.3.0";
public static final String OID_ROOT_SOFTWARE_VERSION = ".1.3.6.1.4.1.2606.7.2.4.0";
public static final String OID_ROOT_PRODUCTION_CODE = ".1.3.6.1.4.1.2606.7.2.7.0";
public static final String OID_ROOT_UNIT_TYPE = ".1.3.6.1.4.1.2606.7.2.8.0";
}

View File

@@ -0,0 +1,47 @@
package io.gec.raw.connector.device.enums;
import io.gec.raw.connector.device.entity.DataTypeEnum;
import lombok.Getter;
@Getter
public enum RittalDevicePropertyNameEnum {
DEVICE_ALIAS("Device alias", RittalDeviceConstants.OID_DEVICE_ALIAS, DataTypeEnum.STRING, false), //INFO: DO NOT CHANGE -> Property is used as DisplayName in the UI
ACCESS_DEVICE_TYPE("Access device type", null, DataTypeEnum.ENUM, true), //INFO: DO NOT CHANGE -> Property is used in Core.Svc
NAME("Name", RittalDeviceConstants.OID_NAME, DataTypeEnum.STRING, true),
TYPE("Type", RittalDeviceConstants.OID_TYPE, DataTypeEnum.STRING, true),
NODE_ID("Node Id", RittalDeviceConstants.OID_NODE_ID, DataTypeEnum.INTEGER, true),
STATUS("Status", RittalDeviceConstants.OID_STATUS, DataTypeEnum.STRING, true),
MODEL_NUMBER("Model number", RittalDeviceConstants.OID_ORDER_NUMBER, DataTypeEnum.STRING, true),
LOCATION("Location", RittalDeviceConstants.OID_LOCATION, DataTypeEnum.STRING, false),
BUS("BUS", RittalDeviceConstants.OID_BUS, DataTypeEnum.STRING, true),
POSITION("Position", RittalDeviceConstants.OID_POSITION, DataTypeEnum.INTEGER, true),
SOFTWARE_VERSION("Software version", RittalDeviceConstants.OID_SOFTWARE_VERSION, DataTypeEnum.STRING, true),
HARDWARE_VERSION("Hardware version", RittalDeviceConstants.OID_HARDWARE_VERSION, DataTypeEnum.STRING, true),
SERIAL_NUMBER("Serial number", RittalDeviceConstants.OID_SERIAL_NUMBER, DataTypeEnum.STRING, true),
NUMBER_OF_VARIABLES("Number of Variables", RittalDeviceConstants.OID_NUMBER_OF_VARIABLES, DataTypeEnum.INTEGER, true),
STATUS_TEXT("Statustext", RittalDeviceConstants.OID_STATUS_TEXT, DataTypeEnum.STRING, true),
// ROOT_DEVICE
ROOT_SERIAL_NUMBER("Serial number", RittalDeviceConstants.OID_ROOT_SERIAL_NUMBER, DataTypeEnum.STRING, true),
ROOT_HARDWARE_VERSION("Hardware version", RittalDeviceConstants.OID_ROOT_HARDWARE_VERSION, DataTypeEnum.STRING, true),
ROOT_SOFTWARE_VERSION("Software version", RittalDeviceConstants.OID_ROOT_SOFTWARE_VERSION, DataTypeEnum.STRING, true),
ROOT_PRODUCTION_CODE("Production Code", RittalDeviceConstants.OID_ROOT_PRODUCTION_CODE, DataTypeEnum.STRING, true),
ROOT_UNIT_TYPE("UnitType", RittalDeviceConstants.OID_ROOT_UNIT_TYPE, DataTypeEnum.STRING, true),
ROOT_IP_ADDRESS("IP address", null, DataTypeEnum.STRING, true);
private final String label;
private final String baseOid;
private final DataTypeEnum dataType;
private final boolean readOnly;
RittalDevicePropertyNameEnum(String label, String baseOid, DataTypeEnum dataType, boolean readOnly) {
this.label = label;
this.baseOid = baseOid;
this.dataType = dataType;
this.readOnly = readOnly;
}
public String getIndexedOid(int index) {
return this.baseOid + "." + index;
}
}

View File

@@ -0,0 +1,36 @@
/*
*
* * (c) 2022 German Edge Cloud GmbH & Co. KG
* *
*
*/
package io.gec.raw.connector.device.enums;
import lombok.Getter;
@Getter
public enum VariableFieldName {
NAME("Name", 3),
VARTYPE("VarType", 4),
UNIT("Unit", 5),
DATATYPE("Datatype", 6),
SCALE("Scale", 7),
CONSTRAINTS("Constraints", 8),
STEPS("Steps", 9),
VALUESTRING("ValueString", 10),
VALUEINT("ValueInt", 11),
LASTCHANGE("LastChange", 12),
ACCESS("Access", 13),
QUALITY("Quality", 14),
ENTPHYSICALINDEX("EntPhysicalIndex", 15);
private final String text;
private final int value;
VariableFieldName(String text, int value) {
this.text = text;
this.value = value;
}
}

View File

@@ -0,0 +1,16 @@
package io.gec.raw.connector.device.projection;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.UUID;
@RegisterForReflection
@AllArgsConstructor
@Getter
public class DeviceParentIdProjection {
private UUID id;
private UUID parentId;
}

View File

@@ -0,0 +1,13 @@
package io.gec.raw.connector.device.projection;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor;
import lombok.Getter;
@RegisterForReflection
@AllArgsConstructor
@Getter
public class DeviceURLProjection {
private String deviceURL;
}

View File

@@ -0,0 +1,55 @@
package io.gec.raw.connector.deviceproperty.control;
import io.gec.raw.connector.communicationservice.control.CommunicationServiceController;
import io.gec.raw.connector.deviceproperty.dto.PropertyValueRequestDTO;
import io.gec.raw.connector.statistic.control.StatisticCollectorController;
import io.gec.raw.connector.statistic.domain.StatisticCategory;
import io.quarkus.cache.Cache;
import io.quarkus.cache.CacheKey;
import io.quarkus.cache.CacheName;
import io.quarkus.cache.CacheResult;
import io.quarkus.cache.CaffeineCache;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.jboss.logging.Logger;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@ApplicationScoped
public class DevicePropertyCachedService {
@CacheName("device-properties")
Cache cache;
@Inject
CommunicationServiceController communicationServiceController;
@Inject
Logger logger;
@Inject
StatisticCollectorController statisticCollectorController;
@CacheResult(cacheName = "device-properties")
public Object getCachedValue(@CacheKey UUID key) {
Object value = cache.as(CaffeineCache.class).getIfPresent(key).getNow(null);
logger.debugv("Getting variable from cache, key: {0}, value: {1}", key, value);
return value;
}
public void updateDeviceProperty(@CacheKey UUID key, PropertyValueRequestDTO propertyValueRequestDTO) {
PropertyValueRequestDTO cachedPropertyValueRequestDTO = (PropertyValueRequestDTO) getCachedValue(key);
if (cachedPropertyValueRequestDTO == null || !cachedPropertyValueRequestDTO.equals(propertyValueRequestDTO)) {
long start = System.currentTimeMillis();
communicationServiceController.updateDeviceProperty(propertyValueRequestDTO);
long end = System.currentTimeMillis();
StatisticCategory statisticCategory = StatisticCategory.of(this.getClass().getSimpleName(),
"PropertyValueRequestDTO");
statisticCollectorController.add(statisticCategory, start, end, end);
cache.as(CaffeineCache.class).put(key, CompletableFuture.completedFuture(propertyValueRequestDTO));
return;
}
//INFO: Cache update always because Items could be removed automatically after expiration
cache.as(CaffeineCache.class).put(key, CompletableFuture.completedFuture(propertyValueRequestDTO));
logger.debugv("{0} added to cache: key: {1}, value: {2}", propertyValueRequestDTO.getClass().getSimpleName(), key, propertyValueRequestDTO);
}
}

View File

@@ -0,0 +1,152 @@
package io.gec.raw.connector.deviceproperty.control;
import io.gec.raw.connector.componentmode.dto.ComponentMode;
import io.gec.raw.connector.device.dto.DevicePropertyResponseDTO;
import io.gec.raw.connector.deviceproperty.entity.DeviceProperty;
import io.gec.raw.connector.deviceproperty.entity.DevicePropertyMapping;
import io.gec.raw.connector.deviceproperty.exception.DevicePropertyNotFoundException;
import io.gec.raw.connector.deviceproperty.projection.DevicePropertyNameProjection;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.persistence.EntityNotFoundException;
import jakarta.transaction.Transactional;
import org.jboss.logging.Logger;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
@ApplicationScoped
public class DevicePropertyController {
@Inject
Logger logger;
@Inject
DevicePropertyRepository devicePropertyRepository;
@Inject
DevicePropertyMapper devicePropertyMapper;
@Inject
DevicePropertyTaskScheduler devicePropertyTaskScheduler;
@Transactional
public void createProperties(List<DevicePropertyMapping> properties, UUID deviceId) {
if (properties != null) {
for (DevicePropertyMapping property : properties) {
DeviceProperty tempProperty = devicePropertyMapper.toDeviceProperty(deviceId, property);
devicePropertyRepository.persist(tempProperty);
}
}
}
@Transactional
public void createPropertiesIfNotExistByName(List<DevicePropertyMapping> properties, UUID deviceId) {
var devicePropertyNames = devicePropertyRepository.findNamesByDeviceId(deviceId).stream()
.map(DevicePropertyNameProjection::getName)
.toList();
var newDeviceProperties = properties.stream()
.filter(property -> !devicePropertyNames.contains(property.getName()))
.toList();
createProperties(newDeviceProperties, deviceId);
}
public DeviceProperty findById(UUID id) {
return devicePropertyRepository.findByIdOptional(id)
.orElseThrow(() -> new DevicePropertyNotFoundException(id));
}
@Transactional
public List<DeviceProperty> findByDeviceId(UUID deviceId) throws EntityNotFoundException {
return devicePropertyRepository.findByDeviceId(deviceId);
}
public Optional<DeviceProperty> findByName(List<DeviceProperty> properties, String name) {
return properties.stream()
.filter(property -> property.getName().equalsIgnoreCase(name))
.findFirst();
}
public DeviceProperty findPropertyByCommSvcId(UUID commSvcId) {
return devicePropertyRepository.findByCommSvcId(commSvcId);
}
public List<DeviceProperty> findSubscribedDeviceProperties() {
return devicePropertyRepository.findWithNonNullValueOidByMode(ComponentMode.OPERATION);
}
public List<DeviceProperty> findSubscribableDevicePropertiesUpdatedAfter(OffsetDateTime stamp) {
return devicePropertyRepository.findWithNonNullValueOidAfterStamp(stamp);
}
@Transactional
public void updatePropertyInterval(UUID propertyId, String intervalValue) {
validateSubmittedId(propertyId);
DeviceProperty deviceProperty = devicePropertyRepository.findById(propertyId);
var interval = parseIntervalStringToIntValue(deviceProperty, intervalValue);
deviceProperty.setSubscriptionInterval(interval);
logger.debugf("DeviceProperty (ID:'%s') interval changed to '%s'", propertyId, intervalValue);
}
@Transactional
public void updateMode(DeviceProperty deviceProperty, ComponentMode mode) {
ComponentMode oldMode = deviceProperty.getMode();
if (ComponentMode.PAUSE.equals(mode) && deviceProperty.isNotInitialValueFetched() && Objects.nonNull(deviceProperty.getValueChangeOid())) {
devicePropertyTaskScheduler.executeTask(deviceProperty);
deviceProperty.setInitialValueFetched(true);
}
if (deviceProperty.getMode().isRestorable()) {
deviceProperty.setRestoreMode(oldMode);
}
deviceProperty.setMode(mode);
logger.debugf("Property (ID:'%s') mode set to:'%s'", deviceProperty.getId(), mode);
}
@Transactional
public void updateCommSvcId(List<DevicePropertyResponseDTO> properties) {
properties.forEach(this::updatePropertyCommSvcId);
}
private void updatePropertyCommSvcId(DevicePropertyResponseDTO property) {
Optional<DeviceProperty> toUpdate = devicePropertyRepository.findByIdOptional(property.getConnectorPropertyId());
toUpdate.ifPresentOrElse(deviceProperty -> deviceProperty.setCommServiceId(property.getId()),
() -> logger.warn("Could not store com_svc_id for " + property.getConnectorPropertyId()));
}
@Transactional
public void restoreMode(DeviceProperty property) {
Objects.requireNonNull(property, "Property required to restore mode");
updateModeWithRestoreMode(property);
logger.debugf("Property (ID:'%s') mode restored to:'%s'", property.getId(), property.getMode());
}
public void updateModeWithRestoreMode(DeviceProperty DeviceProperty) {
if (DeviceProperty != null) {
ComponentMode restoreMode = DeviceProperty.getRestoreMode();
DeviceProperty.setMode(restoreMode);
}
}
private void validateSubmittedId(UUID propertyId) {
if (propertyId == null) {
throw new IllegalStateException("Update Interval Failed, Property ID is missing");
}
}
@Transactional
public void updateDeviceProperty(DeviceProperty deviceProperty) {
devicePropertyRepository.update(deviceProperty);
}
private Integer parseIntervalStringToIntValue(DeviceProperty deviceProperty, String value) {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new NumberFormatException("Invalid interval provided Property (ID:'%s'), interval:'%s'".formatted(deviceProperty.getId(), value));
}
}
}

View File

@@ -0,0 +1,8 @@
package io.gec.raw.connector.deviceproperty.control;
public interface DevicePropertyExtractor {
String getName();
String getValue();
}

View File

@@ -0,0 +1,66 @@
package io.gec.raw.connector.deviceproperty.control;
import io.gec.raw.connector.deviceproperty.dto.PropertyValueRequestDTO;
import io.gec.raw.connector.deviceproperty.entity.DeviceProperty;
import io.gec.raw.connector.variable.dto.VarQuality;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import org.jboss.logging.Logger;
import java.util.Objects;
@ApplicationScoped
public class DevicePropertyHandler {
@Inject
DevicePropertyCachedService cachedService;
@Inject
Logger logger;
@Transactional
public void handleDevicePropertyValue(DeviceProperty deviceProperty, String devicePropertyValue) {
validateDeviceProperty(deviceProperty);
logUpdate("START", deviceProperty, "handle value");
updateDeviceProperty(deviceProperty, devicePropertyValue);
deviceProperty.setInitialValueFetched(true);
}
void validateDeviceProperty(DeviceProperty deviceProperty) {
if (Objects.isNull(deviceProperty)) {
String message = "DeviceProperty is required but not provided";
throw new IllegalArgumentException(message);
}
if (Objects.isNull(deviceProperty.getId())) {
String message = "id is required but not provided";
throw new IllegalArgumentException(message);
}
if (deviceProperty.getSubscriptionInterval() == 0) {
String message = "subscriptionInterval is required to be other than 0";
throw new IllegalArgumentException(message);
}
}
private void updateDeviceProperty(DeviceProperty deviceProperty, String value) {
PropertyValueRequestDTO propertyValueRequestDTO = PropertyValueRequestDTO.builder()
.propertyId(deviceProperty.getId())
.commSvcId(deviceProperty.getCommServiceId())
.varQuality(value.isEmpty() ? VarQuality.WARNINGNOVALUE : VarQuality.OK)
.valueAsString(value)
.build();
logUpdate("RESULT", deviceProperty, "value=%s. quality=%s".formatted(value, propertyValueRequestDTO.getVarQuality()));
cachedService.updateDeviceProperty(deviceProperty.getId(), propertyValueRequestDTO);
}
private void logUpdate(String action, DeviceProperty deviceProperty, String message) {
logger.debugf("[%s] DeviceProperty(ID=%s, mode=%s, initialValueFetched=%s, interval=%s) - %s",
action,
deviceProperty.getId(),
deviceProperty.getMode(),
deviceProperty.isInitialValueFetched(),
deviceProperty.getSubscriptionInterval(),
message
);
}
}

Some files were not shown because too many files have changed in this diff Show More