diff --git a/install-oee-domain-jar.bat b/install-oee-domain-jar.bat index fbc66cc..e831863 100644 --- a/install-oee-domain-jar.bat +++ b/install-oee-domain-jar.bat @@ -3,4 +3,4 @@ rmdir /Q /S C:\maven_repo\org\point85\oee-domain call mvn -v call mvn clean package rem install jar in local repo -call mvn install:install-file -Dfile=./target/OEE-Domain-3.10.0.jar -DgroupId=org.point85 -DartifactId=oee-domain -Dversion=3.10.0 -Dpackaging=jar \ No newline at end of file +call mvn install:install-file -Dfile=./target/OEE-Domain-3.10.1.jar -DgroupId=org.point85 -DartifactId=oee-domain -Dversion=3.10.1 -Dpackaging=jar \ No newline at end of file diff --git a/pom.xml b/pom.xml index 12c8689..af181c0 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.point85 oee-domain - 3.10.0 + 3.10.1 jar http://maven.apache.org diff --git a/src/main/java/org/point85/domain/DomainUtils.java b/src/main/java/org/point85/domain/DomainUtils.java index a605bf7..b0b1602 100644 --- a/src/main/java/org/point85/domain/DomainUtils.java +++ b/src/main/java/org/point85/domain/DomainUtils.java @@ -48,8 +48,8 @@ private DomainUtils() { } public static String getVersionInfo() { - return DomainLocalizer.instance().getLangString("version") + " 3.10.0, " - + LocalDate.of(2023, 12, 27).format(DateTimeFormatter.ISO_DATE); + return DomainLocalizer.instance().getLangString("version") + " 3.10.1, " + + LocalDate.of(2024, 1, 10).format(DateTimeFormatter.ISO_DATE); } // format a Duration @@ -251,4 +251,12 @@ public static String gunzip(byte[] data) throws Exception { return new String(bos.toByteArray()); } + + public static String byteArrayToHex(byte[] byteArray) { + StringBuilder sb = new StringBuilder(); + for (byte b : byteArray) { + sb.append(String.format("%02X ", b)); + } + return sb.toString(); + } } diff --git a/src/main/java/org/point85/domain/http/HttpOeeClient.java b/src/main/java/org/point85/domain/http/HttpOeeClient.java index 5f3c133..a23e0a5 100644 --- a/src/main/java/org/point85/domain/http/HttpOeeClient.java +++ b/src/main/java/org/point85/domain/http/HttpOeeClient.java @@ -20,6 +20,9 @@ import com.google.gson.Gson; +/** + * Class for making HTTP GET and PUT requests + */ public class HttpOeeClient { // logger private Logger logger = LoggerFactory.getLogger(HttpOeeClient.class); diff --git a/src/main/java/org/point85/domain/http/HttpSource.java b/src/main/java/org/point85/domain/http/HttpSource.java index 7c1b98b..d80805d 100644 --- a/src/main/java/org/point85/domain/http/HttpSource.java +++ b/src/main/java/org/point85/domain/http/HttpSource.java @@ -10,9 +10,13 @@ import org.point85.domain.collector.DataSourceType; import org.point85.domain.dto.HttpSourceDto; +/** + * The HttpSource class represents an HTTP/HTTPS server as a source of + * application events. + * + */ @Entity @DiscriminatorValue(DataSourceType.HTTP_VALUE) - public class HttpSource extends CollectorDataSource { // overloaded for HTTPS port @Column(name = "END_PATH") diff --git a/src/main/java/org/point85/domain/mqtt/MqttOeeClient.java b/src/main/java/org/point85/domain/mqtt/MqttOeeClient.java index 899dac4..3dc613c 100644 --- a/src/main/java/org/point85/domain/mqtt/MqttOeeClient.java +++ b/src/main/java/org/point85/domain/mqtt/MqttOeeClient.java @@ -4,6 +4,7 @@ import java.io.FileInputStream; import java.net.InetAddress; import java.security.KeyStore; +import java.util.concurrent.atomic.AtomicBoolean; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; @@ -42,6 +43,10 @@ public class MqttOeeClient extends BaseMessagingClient { private static final String TCP_PROTOCOL = "tcp://"; private static final String SSL_PROTOCOL = "ssl://"; + // polling loop for replies + private static final int NUM_TRIES = 20; + private static final int RETRY_WAIT = 500; + // native client private MqttClient mqttClient; @@ -51,20 +56,42 @@ public class MqttOeeClient extends BaseMessagingClient { // connection options private MqttConnectOptions connectionOptions = new MqttConnectOptions(); + // flag for response from publishing a message is available + private AtomicBoolean dataAvailable = new AtomicBoolean(false); + + // the text of the message response + private String messageResponse; + public MqttOeeClient() { connectionOptions.setAutomaticReconnect(true); connectionOptions.setCleanSession(CLEAN_SESSION); connectionOptions.setConnectionTimeout(10); } + /** + * Register listener for replies + * + * @param listener {@link MqttMessageListener} + */ public void registerListener(MqttMessageListener listener) { this.eventListener = listener; } + /** + * Remove the reply listener + */ public void unregisterListener() { this.eventListener = null; } + /** + * Initialize the client + * + * @param hostName Broker name + * @param port Broker port + * @param listener {@link MqttMessageListener} + * @throws Exception Exception + */ public void startUp(String hostName, int port, MqttMessageListener listener) throws Exception { // connect to server connect(hostName, port); @@ -110,6 +137,12 @@ public void connect(String hostName, int port) throws Exception { } } + /** + * Subscribe to message replies + * + * @param qos {@link QualityOfService} + * @throws Exception Exception + */ public void subscribeToEvents(QualityOfService qos) throws Exception { mqttClient.subscribe(EVENT_TOPIC, (topic, msg) -> { String json = new String(msg.getPayload()); @@ -140,6 +173,12 @@ public void subscribeToEvents(QualityOfService qos) throws Exception { } } + /** + * Subscribe to notification messages + * + * @param qos {@link QualityOfService} + * @throws Exception Exception + */ public void subscribeToNotifications(QualityOfService qos) throws Exception { mqttClient.subscribe(STATUS_TOPIC, (topic, msg) -> { String json = new String(msg.getPayload()); @@ -173,9 +212,21 @@ public void subscribeToNotifications(QualityOfService qos) throws Exception { } } + /** + * Publish an ApplicationMessage to the topic with the specified + * QualityOfService + * + * @param topic Topic + * @param message {@link ApplicationMessage} + * @param qos {@link QualityOfService} + * @throws Exception Exception + */ public void publish(String topic, ApplicationMessage message, QualityOfService qos) throws Exception { String text = serialize(message); + publishMessage(topic, text, qos); + } + private void publishMessage(String topic, String text, QualityOfService qos) throws Exception { MqttMessage mqttMessage = new MqttMessage(); mqttMessage.setQos(qos.getQos()); mqttMessage.setRetained(false); @@ -188,6 +239,69 @@ public void publish(String topic, ApplicationMessage message, QualityOfService q } } + /** + * Publish a text message, then wait for a response message. A prior call to + * subscribeToTopic() is required. + * + * @param topic Topic to publish to + * @param text Payload of message + * @param qos {@link QualityOfService} + * @param maxWait Maximum time to wait in seconds + * @return Payload of message response + * @throws Exception Exception + */ + public String publishAndWait(String topic, String text, QualityOfService qos, int maxWait) throws Exception { + dataAvailable.set(false); + messageResponse = null; + + // send the message + publishMessage(topic, text, qos); + + // wait for response + long start = System.currentTimeMillis(); + + for (int i = 0; i < NUM_TRIES; i++) { + Thread.sleep(RETRY_WAIT); + long delta = (System.currentTimeMillis() - start) / 1000; + + if (dataAvailable.get() || delta >= maxWait) { + // response has been received or timed out + break; + } + } + + if (messageResponse == null || messageResponse.isEmpty()) { + logger.warn("No response to publishing the message was received within " + maxWait + " seconds."); + } + return messageResponse; + } + + /** + * Subscribe to this topic + * + * @param topic Topic to subscribe to + * @throws Exception Exception + */ + public void subscribeToTopic(String topic) throws Exception { + mqttClient.subscribe(topic, (theTopic, msg) -> { + messageResponse = new String(msg.getPayload()); + + if (messageResponse != null && !messageResponse.isEmpty()) { + dataAvailable.set(true); + } else { + dataAvailable.set(false); + } + + if (logger.isInfoEnabled()) { + logger.info("MQTT message received, topic: " + theTopic + ", payload:\n\t" + messageResponse); + } + }); + + if (logger.isInfoEnabled()) { + logger.info("Subscribed to topic " + topic); + } + } + /** * Disconnect from the MQTT server * diff --git a/src/main/java/org/point85/domain/schedule/Rotation.java b/src/main/java/org/point85/domain/schedule/Rotation.java index f089011..804df2d 100644 --- a/src/main/java/org/point85/domain/schedule/Rotation.java +++ b/src/main/java/org/point85/domain/schedule/Rotation.java @@ -71,6 +71,10 @@ public class Rotation extends Named implements Comparable { @OneToMany(mappedBy = "rotation", cascade = CascadeType.ALL, orphanRemoval = true) private final List rotationSegments = new ArrayList<>(); + // teams + @OneToMany(mappedBy = "rotation", cascade = CascadeType.ALL, orphanRemoval = true) + private final List teams = new ArrayList<>(); + // list of working and non-working days @Transient private List periods; @@ -215,10 +219,24 @@ public WorkSchedule getWorkSchedule() { return workSchedule; } + /** + * Assign the work schedule to this rotation + * + * @param workSchedule {@link WorkSchedule} + */ public void setWorkSchedule(WorkSchedule workSchedule) { this.workSchedule = workSchedule; } + /** + * Get the rotation's related teams + * + * @return List of {@link Team} + */ + public List getTeams() { + return this.teams; + } + @Override public int compareTo(Rotation other) { return getName().compareTo(other.getName()); diff --git a/src/main/java/org/point85/domain/schedule/RotationSegment.java b/src/main/java/org/point85/domain/schedule/RotationSegment.java index ce7699b..a747924 100644 --- a/src/main/java/org/point85/domain/schedule/RotationSegment.java +++ b/src/main/java/org/point85/domain/schedule/RotationSegment.java @@ -27,12 +27,10 @@ of this software and associated documentation files (the "Software"), to deal import java.util.Objects; import javax.persistence.AttributeOverride; -import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; -import javax.persistence.OneToOne; import javax.persistence.Table; import org.point85.domain.dto.RotationSegmentDto; @@ -61,7 +59,7 @@ public class RotationSegment extends KeyedObject implements Comparable { @OneToMany(mappedBy = "shift", cascade = CascadeType.ALL, orphanRemoval = true) private final List breaks = new ArrayList<>(); + // shifts + @OneToMany(mappedBy = "startingShift", cascade = CascadeType.ALL, orphanRemoval = true) + private final List segments = new ArrayList<>(); + /** * Default constructor */ @@ -88,6 +92,15 @@ public List getBreaks() { return this.breaks; } + /** + * Get the rotations segments for this shift + * + * @return List {@link RotationSegment} + */ + public List getRotationSegments() { + return this.segments; + } + /** * Add a break period to this shift * diff --git a/src/main/java/org/point85/domain/schedule/Team.java b/src/main/java/org/point85/domain/schedule/Team.java index c7ca58b..674483b 100644 --- a/src/main/java/org/point85/domain/schedule/Team.java +++ b/src/main/java/org/point85/domain/schedule/Team.java @@ -32,12 +32,10 @@ of this software and associated documentation files (the "Software"), to deal import java.util.Objects; import javax.persistence.AttributeOverride; -import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; -import javax.persistence.OneToOne; import javax.persistence.Table; import org.point85.domain.DomainUtils; @@ -65,7 +63,7 @@ public class Team extends Named implements Comparable { private LocalDate rotationStart; // shift rotation days - @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) + @ManyToOne @JoinColumn(name = "ROTATION_KEY") private Rotation rotation;