diff --git a/.gitignore b/.gitignore index 9483d7b..d62ce0a 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,6 @@ gradlew.bat Development/gradle/wrapper/gradle-wrapper.properties Core/gradle/wrapper/gradle-wrapper.jar Core/gradle/wrapper/gradle-wrapper.jar +/Development/target +/Abstractions/target +/docs/digitaltwin-core-docs/target diff --git a/Core/.gitattributes b/Abstractions/.gitattributes similarity index 100% rename from Core/.gitattributes rename to Abstractions/.gitattributes diff --git a/Core/.gitignore b/Abstractions/.gitignore similarity index 100% rename from Core/.gitignore rename to Abstractions/.gitignore diff --git a/Core/LICENSE b/Abstractions/LICENSE similarity index 100% rename from Core/LICENSE rename to Abstractions/LICENSE diff --git a/Abstractions/README.md b/Abstractions/README.md new file mode 100644 index 0000000..ced7955 --- /dev/null +++ b/Abstractions/README.md @@ -0,0 +1,10 @@ +# ScaleOut Digital Twins™ Abstractions Libraries for Java + +The open source (Apache 2.0 licensed) project for the digitaltwin-abstractions API: + +- [com.scaleoutsoftware.digitaltwin:digitaltwin-abstractions](https://mvnrepository.com/artifact/com.scaleoutsoftware.digitaltwin/digitaltwin-abstractions/3.0.0): Abstract datatypes required for building Digital Twin models. + + +## Documentation +- [Class reference](https://scaleoutsoftware.github.io/JavaDigitalTwinCore/) +- [ScaleOut Digital Twins™ User Guide](https://static.scaleoutsoftware.com/docs/digital_twin_user_guide/index.html) \ No newline at end of file diff --git a/Abstractions/build.bat b/Abstractions/build.bat new file mode 100644 index 0000000..a40786e --- /dev/null +++ b/Abstractions/build.bat @@ -0,0 +1,8 @@ +rem ******************** +rem * Requirements * +rem ******************** +rem * Java JDK 8 +rem * MVN 3.x +SET JAVA_HOME=C:\path\to\java8 +SET MVN_HOME=C:\path\to\maven +call mvn clean package install javadoc:jar source:jar \ No newline at end of file diff --git a/Abstractions/build.sh b/Abstractions/build.sh new file mode 100644 index 0000000..83def59 --- /dev/null +++ b/Abstractions/build.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +export JAVA_HOME="/path/to/java8" +export MVN_HOME="/path/to/maven" + +export PATH="$JAVA_HOME/bin:$MVN_HOME/bin:$PATH" + +mvn clean package install javadoc:jar source:jar \ No newline at end of file diff --git a/Abstractions/pom.xml b/Abstractions/pom.xml new file mode 100644 index 0000000..bbad931 --- /dev/null +++ b/Abstractions/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + com.scaleoutsoftware.digitaltwin + digitaltwin-abstractions + 3.0.0 + 2021 + https://scaleoutsoftware.com + Digital Twin Core API + Core API for building real-time and simulated digital twins. + + + scaleoutdev + ScaleOut Dev + dev@scaleoutsoftware.com + https://scaleoutsoftware.com + ScaleOut Software, Inc. + https://scaleoutsoftware.com + + developer + engineer + + America/Los_Angeles + + + + + Apache License 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + Apache License 2.0 + + + + https://github.com/scaleoutsoftware/JavaDigitalTwinCore + + + UTF-8 + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + none + true + + -Xlint:all + + + + + + \ No newline at end of file diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/AlertMessage.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/AlertMessage.java similarity index 94% rename from Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/AlertMessage.java rename to Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/AlertMessage.java index 4383a90..df63f13 100644 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/AlertMessage.java +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/AlertMessage.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2021 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.scaleoutsoftware.digitaltwin.core; +package com.scaleoutsoftware.digitaltwin.abstractions; import java.util.HashMap; @@ -26,7 +26,7 @@ public class AlertMessage { private final String _message; private final HashMap _optionalTwinInstanceProperties; - private AlertMessage() {_title = _severity = _message = null; _optionalTwinInstanceProperties = null;} + private AlertMessage() { _title = _severity = _message = null; _optionalTwinInstanceProperties = null;} /** * Construct an alert message with a title, severity, and custom message. diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/PersistenceProvider.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/AzureDigitalTwinsProvider.java similarity index 69% rename from Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/PersistenceProvider.java rename to Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/AzureDigitalTwinsProvider.java index f75ad0c..5557f62 100644 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/PersistenceProvider.java +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/AzureDigitalTwinsProvider.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2021 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.scaleoutsoftware.digitaltwin.core; +package com.scaleoutsoftware.digitaltwin.abstractions; import java.util.List; import java.util.Map; @@ -22,7 +22,7 @@ /** * An interface that can be used for persisting/retrieving the state of real-time digital twins. */ -public interface PersistenceProvider { +public interface AzureDigitalTwinsProvider { /** * Returns true if this PersistenceProvider is active, false otherwise. @@ -30,12 +30,6 @@ public interface PersistenceProvider { */ public abstract boolean isActive(); - /** - * Retrieves this persistence providers type. Currently supported provider types: AzureDigitalTwins. - * @return the persistence provider type. - */ - public PersistenceProviderType getProviderType(); - /** * Retrieves a future that when complete will return the instance IDs stored in a container, or an empty list if no instances exist. * @param containerName the container name. @@ -121,42 +115,4 @@ public interface PersistenceProvider { * @return the property or null if the property does not exist. */ public T getProperty(String containerName, String instanceId, String propertyName, Class clazz); - - /** - * Retrieves a future that will complete exceptionally or return void if the RTDT's property was successfully updated. - * Updates a RTDT property for the provided instance id specified by the property name and property value. - * @param instanceId the instance id. - * @param propertyName the property name. - * @param propertyValue the property value. - * @return a future that will complete exceptionally or return void. - */ - public CompletableFuture updateRtdtPropertyAsync(String instanceId, String propertyName, Object propertyValue); - - /** - * Updates a RTDT property for the provided instance id specified by the property name and property value. - * @param instanceId the instance id. - * @param propertyName the property name. - * @param propertyValue the property value. - */ - public void updateRtdtProperty(String instanceId, String propertyName, Object propertyValue); - - /** - * Retrieves a future that will return a property value for a RTDT instance or null if the property doesn't exist. - * @param instanceId the instance id. - * @param propertyName the property name. - * @param clazz the class of the property type - * @param the type of the property to return. - * @return a future that will return a property value for a RTDT instance. - */ - public CompletableFuture getRtdtPropertyAsync(String instanceId, String propertyName, Class clazz); - - /** - * Retrieves a property for a RTDT instance or null if the property does not exist. - * @param instanceId the instance id. - * @param propertyName the property name. - * @param clazz the class of the property type. - * @param the type of the property to return. - * @return the property value. - */ - public T getRtdtProperty(String instanceId, String propertyName, Class clazz); } diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/CacheOperationStatus.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/CacheOperationStatus.java similarity index 91% rename from Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/CacheOperationStatus.java rename to Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/CacheOperationStatus.java index 3498640..820ebf8 100644 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/CacheOperationStatus.java +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/CacheOperationStatus.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2024 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.scaleoutsoftware.digitaltwin.core; +package com.scaleoutsoftware.digitaltwin.abstractions; /** * Status of a cache operation. diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/CacheResult.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/CacheResult.java similarity index 91% rename from Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/CacheResult.java rename to Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/CacheResult.java index f62adf1..5a99d65 100644 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/CacheResult.java +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/CacheResult.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2024 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.scaleoutsoftware.digitaltwin.core; +package com.scaleoutsoftware.digitaltwin.abstractions; /** * Represents a response from a {@link SharedData} operation. diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/MessageFactory.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/CreateResult.java similarity index 57% rename from Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/MessageFactory.java rename to Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/CreateResult.java index 954c08f..6095592 100644 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/MessageFactory.java +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/CreateResult.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2018 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,17 +13,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.scaleoutsoftware.digitaltwin.core; +package com.scaleoutsoftware.digitaltwin.abstractions; /** - * Message list factory retrieves message lists for a MessageProcessor + * Enum indicating the status of a delete operation. */ -public interface MessageFactory { - - /** - * Returns all incoming messages - * @param the type of incoming messages - * @return an iterable of incoming messages - */ - Iterable getIncomingMessages(); +public enum CreateResult { + /** + * The twin instance was successfully created. + */ + Success, + /** + * The twin instance already existed and could not be created. + */ + ObjectExists } diff --git a/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/DeleteResult.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/DeleteResult.java new file mode 100644 index 0000000..a141807 --- /dev/null +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/DeleteResult.java @@ -0,0 +1,30 @@ +/* + Copyright (c) 2026 by ScaleOut Software, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.scaleoutsoftware.digitaltwin.abstractions; + +/** + * Enum indicating the status of a delete operation. + */ +public enum DeleteResult { + /** + * The twin instance was successfully deleted. + */ + Success, + /** + * The target twin instance was not found. + */ + NotFound +} diff --git a/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/DigitalTwinBase.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/DigitalTwinBase.java new file mode 100644 index 0000000..5b54875 --- /dev/null +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/DigitalTwinBase.java @@ -0,0 +1,121 @@ +/* + Copyright (c) 2026 by ScaleOut Software, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.scaleoutsoftware.digitaltwin.abstractions; + +import java.util.Date; +import java.util.HashMap; + +/** + * A real-time digital twin of a data source. The implementation of the real-time DigitalTwin should have a parameterless constructor for + * basic initialization. + */ +public abstract class DigitalTwinBase> { + + /* capitalized to match .NET serialization */ + /** + * The identifier for this twin instance + */ + public String Id = ""; + + /** + * The model this twin instance belongs to. + */ + public String Model = ""; + + /** + * DO NOT MODIFY. This property is managed by the service. Used to send messages to the instances data source. + */ + public int SourceNamespaceAppId; + + /** + * DO NOT MODIFY. This property is managed by the service. Used to store the instances next simulation time; managed by the + * service in case of load balancing or server failures. + */ + public long NextSimulationTimeUnixMsec; + + /** + * Default constructor. + */ + public DigitalTwinBase() {} + + /** + * The identifier of this DigitalTwin. + * @return the identifier of this digital twin + */ + public String getId() { + return Id; + } + + /** + * The model for this DigitalTwin. + * @return the model for this DigitalTwin + */ + public String getModel() { + return Model; + } + + /** + * INTERNAL: DO NOT MODIFY. This property is managed by the service. + * + * Used for sending messages back to the instances data source. + * + * @return the source App ID namespace + */ + public int getSourceAppIdNamespace() { + return SourceNamespaceAppId; + } + + /** + * INTERNAL: DO NOT MODIFY. This property is managed by the service. + * @param sourceAppIdNamespace assign this instance a new source app ID namespace. + */ + public void setSourceAppIdNamespace(int sourceAppIdNamespace) { + SourceNamespaceAppId = sourceAppIdNamespace; + } + + /** + * INTERNAL: DO NOT MODIFY. This property is managed by the service. + * + * Retrieve the next simulation time for this instance. + * + * @return the next simulation time for this instance. + */ + public long getNextSimulationTimeUnixMsec() { + return NextSimulationTimeUnixMsec; + } + + /** + * INTERNAL: DO NOT MODIFY. This property is managed by the service. + * + * Assign this instance the next simulation time when the instance will be run. + * + * @param nextSimulationTimeUnixMsec the next simulation time when the instance will be run. + */ + public void setNextSimulationTimeUnixMsec(long nextSimulationTimeUnixMsec) { + NextSimulationTimeUnixMsec = nextSimulationTimeUnixMsec; + } + + /** + * Initialization method to set the identifier and model for a DigitalTwin instance. Optionally use the + * {@link InitContext} to start a timer. + * @param context the initialization context. + * @throws IllegalStateException if init is called after initialization. + */ + public void init(InitContext context) throws IllegalStateException { + this.Id = context.getId(); + this.Model = context.getModel(); + } +} diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/InitContext.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/InitContext.java similarity index 66% rename from Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/InitContext.java rename to Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/InitContext.java index d05933e..1f7dddd 100644 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/InitContext.java +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/InitContext.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2024 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.scaleoutsoftware.digitaltwin.core; +package com.scaleoutsoftware.digitaltwin.abstractions; import java.time.Duration; @@ -21,7 +21,7 @@ * The InitContext is passed as a parameter to the {@link DigitalTwinBase#init(InitContext)} method of an initializing * digital twin. */ -public abstract class InitContext { +public abstract class InitContext> { /** * Default constructor. @@ -34,11 +34,23 @@ public InitContext() {} * @param interval the timer interval * @param timerType the timer type * @param timerHandler the time handler callback - * @param the type of the digital twin + * @param timerHandlerClass the timer handler callback class * @return returns {@link TimerActionResult#Success} if the timer was started, {@link TimerActionResult#FailedTooManyTimers} * if too many timers exist, or {@link TimerActionResult#FailedInternalError} if an unexpected error occurs. */ - public abstract TimerActionResult startTimer(String timerName, Duration interval, TimerType timerType, TimerHandler timerHandler); + public abstract TimerActionResult startTimer(String timerName, Duration interval, TimerType timerType, TimerHandler timerHandler, Class> timerHandlerClass); + + /** + * Retrieve a {@link SharedData} accessor for this model's shared data. + * @return a {@link SharedData} instance. + */ + public abstract SharedData getSharedModelData(); + + /** + * Retrieve a {@link SharedData} accessor for globally shared data. + * @return a {@link SharedData} instance. + */ + public abstract SharedData getSharedGlobalData(); /** * Get the model-unique Id identifier of the initializing digital twin instance. diff --git a/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/InitSimulationContext.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/InitSimulationContext.java new file mode 100644 index 0000000..df0d5d4 --- /dev/null +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/InitSimulationContext.java @@ -0,0 +1,36 @@ +/* + Copyright (c) 2026 by ScaleOut Software, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.scaleoutsoftware.digitaltwin.abstractions; + +import java.util.Date; + +/** + * The InitSimulationContext is passed as a parameter to the {@link SimulationProcessor#onInitSimulation(InitSimulationContext, DigitalTwinBase, Date)} method of + * digital twin instance when a simulation is initializing. + */ +public interface InitSimulationContext { + /** + * Retrieve a {@link SharedData} accessor for this model's shared data. + * @return a {@link SharedData} instance. + */ + public abstract SharedData getSharedModelData(); + + /** + * Retrieve a {@link SharedData} accessor for globally shared data. + * @return a {@link SharedData} instance. + */ + public abstract SharedData getSharedGlobalData(); +} diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/MessageProcessor.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/MessageProcessor.java similarity index 53% rename from Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/MessageProcessor.java rename to Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/MessageProcessor.java index c1e6a5c..1392203 100644 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/MessageProcessor.java +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/MessageProcessor.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2018 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,16 +13,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.scaleoutsoftware.digitaltwin.core; +package com.scaleoutsoftware.digitaltwin.abstractions; import java.io.Serializable; /** * Processes messages for a real-time digital twin. * @param the real type of the DigitalTwinBase - * @param the type of messages processed by the real-time digital twin */ -public abstract class MessageProcessor extends MessageProcessorBase implements Serializable { +public abstract class MessageProcessor> { /** * Default constructor. @@ -33,24 +32,10 @@ public MessageProcessor() {} * Processes a set of incoming messages and determines whether to update the real-time digital twin. * @param context optional context for processing. * @param stateObject the state object. - * @param incomingMessages the incoming messages. + * @param incomingMessage the incoming message. * @return processing results for updating the state object. * @throws Exception if an exception occurs during processing */ - public abstract ProcessingResult processMessages(ProcessingContext context, T stateObject, Iterable incomingMessages) throws Exception; - - /** - * Helper method to ensure proper typing for user methods. - * @param context the processing context. - * @param twin the digital twin object. - * @param factory the message list factory. - * @return the implementing class's processing result. - * @throws Exception if an exception occurs during processing. - */ - @Override - public ProcessingResult processMessages(ProcessingContext context, T twin, MessageFactory factory) throws Exception { - Iterable incoming = factory.getIncomingMessages(); - return this.processMessages(context, twin, incoming); - } + public abstract ProcessingResult processMessage(ProcessingContext context, T stateObject, byte[] incomingMessage) throws Exception; } diff --git a/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/ProcessingContext.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/ProcessingContext.java new file mode 100644 index 0000000..693e2f2 --- /dev/null +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/ProcessingContext.java @@ -0,0 +1,164 @@ +/* + Copyright (c) 2026 by ScaleOut Software, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.scaleoutsoftware.digitaltwin.abstractions; + +import java.io.Serializable; +import java.time.Duration; +import java.util.Date; +import java.util.concurrent.CompletableFuture; +import java.util.logging.Level; + +/** + * Context object that allows the user to send a message to a DataSource. + * @param the type of the digital twin + */ +public abstract class ProcessingContext> implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * Default constructor. + */ + public ProcessingContext() {} + + /** + *

+ * Sends a message to a data source. This will route messages through the connector back to the data source. + *

+ * + *

+ * if the datasource is simulation instance, then the message will be sent to the simulation model's implementation + * of the {@link MessageProcessor}. + *

+ * + * @param payload the message (as a serialized JSON string) + * @return the sending result + */ + public abstract CompletableFuture sendToDataSource(byte[] payload); + + /** + *

+ * This method sends a serialized JSON message to a real-time digital twin + *

+ * + *

+ * Note, the message contents must be serialized so that the registered message type + * of the digital twin model will be sufficient to deserialize the message. + *

+ * @param model the model of the digital twin + * @param id the id of the digital twin + * @param payload the serialized JSON message + * @return the sending result + */ + public abstract CompletableFuture sendToDigitalTwin(String model, String id, byte[] payload); + + /** + *

+ * This method sends an alert message to configured systems. + *

+ * + *

+ * If the message cannot be sent then the returned CompletableFuture will complete exceptionally. + *

+ * @param alert the alert message. + * @return the sending result. + */ + public abstract CompletableFuture sendAlert(AlertMessage alert); + + /** + * Returns an {@link AzureDigitalTwinsProvider} or null if no AzureDigitalTwinsProvider configuration can be found. + * @return a {@link AzureDigitalTwinsProvider} or null. + */ + public abstract AzureDigitalTwinsProvider getAzureDigitalTwinsProvider(); + + /** + * Retrieve the unique Identifier for a DataSource (matches the Device/Datasource/Real-time twin ID) + * @return the digital twin id + */ + public abstract String getDataSourceId(); + + /** + * Retrieve the model for a DigitalTwin (matches the model of a Device/Datasource/real-time twin) + * @return the digital twin model + */ + public abstract String getDigitalTwinModel(); + + /** + * Log a message to the real-time digital twin UI. + * + * Note: the only supported severity levels are: INFO, WARN, and SEVERE + * + * @param severity the severity of the log message + * @param message the message to log + * @return A future that will complete successfully with no result or exceptionally. + */ + public abstract CompletableFuture logMessage(Level severity, String message); + + /** + * Delete the target real-time twin instance. + * @param targetTwinModel the model of the real-time twin instance + * @param targetTwinId the id of the real-time twin instance + * @return a completable future that will complete with a {@link DeleteResult} indicating the status of the operation, or + * exceptionally indicating that an error occurred. + */ + public abstract CompletableFuture removeRealTimeTwin(String targetTwinModel, String targetTwinId); + + /** + * Starts a new timer for the digital twin + * @param timerName the timer name + * @param interval the timer interval + * @param timerType the timer type + * @param timerHandler the time handler callback + * @param timerHandlerClass the timer handler callback class + * @return returns {@link TimerActionResult#Success} if the timer was started, {@link TimerActionResult#FailedTooManyTimers} + * if too many timers exist, or {@link TimerActionResult#FailedInternalError} if an unexpected error occurs. + */ + public abstract TimerActionResult startTimer(String timerName, Duration interval, TimerType timerType, TimerHandler timerHandler, Class> timerHandlerClass); + + /** + * Stops the specified timer. + * @param timerName the timer name. + * @return returns {@link TimerActionResult#Success} if the timer was stopped, {@link TimerActionResult#FailedNoSuchTimer} + * if no timer exists with that name, or {@link TimerActionResult#FailedInternalError} if an unexpected error occurs. + */ + public abstract TimerActionResult stopTimer(String timerName); + + /** + * Retrieves the current time. If the model (simulation or real-time) is running inside of a simulation then the + * simulation time will be returned. + * + * @return The current time (real time, or simulation if running under simulation). + */ + public abstract Date getCurrentTime(); + + /** + * Retrieve the running {@link SimulationController} or null if no simulation is running. + * @return the {@link SimulationController} or null if no simulation is running. + */ + public abstract SimulationController getSimulationController(); + + /** + * Retrieve a {@link SharedData} accessor for this model's shared data. + * @return a {@link SharedData} instance. + */ + public abstract SharedData getSharedModelData(); + + /** + * Retrieve a {@link SharedData} accessor for globally shared data. + * @return a {@link SharedData} instance. + */ + public abstract SharedData getSharedGlobalData(); + +} diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/ProcessingResult.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/ProcessingResult.java similarity index 72% rename from Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/ProcessingResult.java rename to Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/ProcessingResult.java index 45b2ed6..0465ce3 100644 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/ProcessingResult.java +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/ProcessingResult.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2018 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,10 +13,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.scaleoutsoftware.digitaltwin.core; +package com.scaleoutsoftware.digitaltwin.abstractions; /** - * The result from a message processor which indicates to update the state object or to ignore + * The result from a message processor which indicates to update the twin instance, not update the twin instance, or + * remove the twin instance. */ public enum ProcessingResult { /** @@ -26,5 +27,9 @@ public enum ProcessingResult { /** * Do not update the digital twin. */ - NoUpdate + NoUpdate, + /** + * The twin instance should be removed after processing. + */ + Remove } diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/SendingResult.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/SendingResult.java similarity index 86% rename from Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/SendingResult.java rename to Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/SendingResult.java index 94ea82f..dfd1f01 100644 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/SendingResult.java +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/SendingResult.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2018 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,10 +13,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.scaleoutsoftware.digitaltwin.core; +package com.scaleoutsoftware.digitaltwin.abstractions; /** - * Marks a message as Delivered or not Delivered + * Marks a message as Handled, Enqueued, or Not Handled */ public enum SendingResult { /** diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/SharedData.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/SharedData.java similarity index 75% rename from Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/SharedData.java rename to Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/SharedData.java index f091158..088b2f4 100644 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/SharedData.java +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/SharedData.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2024 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,7 +13,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.scaleoutsoftware.digitaltwin.core; +package com.scaleoutsoftware.digitaltwin.abstractions; + +import java.util.concurrent.CompletableFuture; /** * SharedData is used to access a model's, or globally, shared cache. @@ -24,7 +26,7 @@ public interface SharedData { * @param key the key mapping to a value. * @return A cache result. */ - public CacheResult get(String key); + public CompletableFuture get(String key); /** * Put a new key/value mapping into the cache. @@ -32,18 +34,18 @@ public interface SharedData { * @param value the value. * @return a cache result. */ - public CacheResult put(String key, byte[] value); + public CompletableFuture put(String key, byte[] value); /** * Remove a key/value mapping from the cache. * @param key the key mapping to a value. * @return a cache result. */ - public CacheResult remove(String key); + public CompletableFuture remove(String key); /** * Clear the shared data cache. * @return a cache result. */ - public CacheResult clear(); + public CompletableFuture clear(); } diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/SimulationController.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/SimulationController.java similarity index 58% rename from Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/SimulationController.java rename to Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/SimulationController.java index f796807..5f2f103 100644 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/SimulationController.java +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/SimulationController.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2022 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,10 +13,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.scaleoutsoftware.digitaltwin.core; +package com.scaleoutsoftware.digitaltwin.abstractions; import java.time.Duration; import java.util.Date; +import java.util.concurrent.CompletableFuture; /** * The SimulationController interface is used to interact with the running DigitalTwin simulation. @@ -31,6 +32,18 @@ public interface SimulationController { */ Duration getSimulationTimeIncrement(); + /** + * + */ + + /** + *

+ * Retrieves the simulation start time. + *

+ * @return the simulation start time. + */ + Date getSimulationStartTime(); + /** *

* Delay simulation processing for this DigitalTwin instance for a duration of time. @@ -60,10 +73,9 @@ public interface SimulationController { *

* * @param duration the duration to delay. - * @return {@link SendingResult#Handled} if the delay was processed or {@link SendingResult#NotHandled} * if the delay was not processed. */ - SendingResult delay(Duration duration); + void delay(Duration duration); /** *

@@ -74,34 +86,21 @@ public interface SimulationController { * Simulation processing will be delayed until this instance is run with {@link SimulationController#runThisInstance()}. *

* - * @return {@link SendingResult#Handled} if the delay was processed or {@link SendingResult#NotHandled} * if the delay was not processed. */ - SendingResult delayIndefinitely(); + void delayIndefinitely(); /** *

* Asynchronously send a JSON serialized message to a DigitalTwin instance that will be processed by the DigitalTwin - * models {@link MessageProcessor#processMessages(ProcessingContext, DigitalTwinBase, Iterable)} method. + * models {@link MessageProcessor#processMessage(ProcessingContext, DigitalTwinBase, byte[])} method. *

* @param modelName the model to send the messages too. * @param telemetryMessage a blob representing a JSON serialized messages. * @return {@link SendingResult#Handled} if the messages were processed, {@link SendingResult#Enqueued} if * the messages are in process of being handled, or {@link SendingResult#NotHandled} if the delay was not processed. */ - SendingResult emitTelemetry(String modelName, byte[] telemetryMessage); - - /** - *

- * Asynchronously send a JSON serializable message to a DigitalTwin instance that will be processed by the DigitalTwin - * models {@link MessageProcessor#processMessages(ProcessingContext, DigitalTwinBase, Iterable)} method. - *

- * @param modelName the model to send the messages too. - * @param jsonSerializableMessage an object message that is JSON serializable. - * @return {@link SendingResult#Handled} if the messages were processed, {@link SendingResult#Enqueued} if - * the messages are in process of being handled, or {@link SendingResult#NotHandled} if the delay was not processed. - */ - SendingResult emitTelemetry(String modelName, Object jsonSerializableMessage); + CompletableFuture emitTelemetry(String modelName, byte[] telemetryMessage); /** * Create a new digital twin instance for simulation processing. @@ -112,37 +111,7 @@ public interface SimulationController { * is in process of being created, or {@link SendingResult#NotHandled} if the instance could not be created. * @param the type of the digital twin to create. */ - SendingResult createInstance(String modelName, String instanceId, T base); - - /** - * Create a new digital twin instance for simulation processing from a persistence store. - * - * The twin instance will be loaded via model name and id from a persistence store. - * - * If no instance can be found, then an exception will be thrown and no instance will be created. - * - * @param model The model name. - * @param id the instance id. - * @return {@link SendingResult#Handled} if the instance was created, {@link SendingResult#Enqueued} if the instance - * is in process of being created, or {@link SendingResult#NotHandled} if the instance could not be created. - */ - SendingResult createInstanceFromPersistenceStore(String model, String id); - - /** - * The twin instance will be loaded via model name and id from a persistence store. - * - * The twin instance will be loaded via model name and id from a persistence store. - * - * If no instance can be found, then the default parameter instance will be used. - * - * @param model the model name. - * @param id the instance id. - * @param def the default instance to create. - * @return {@link SendingResult#Handled} if the instance was created, {@link SendingResult#Enqueued} if the instance - * * is in process of being created, or {@link SendingResult#NotHandled} if the instance could not be created. - * @param the type of the digital twin to create. - */ - SendingResult createInstanceFromPersistenceStore(String model, String id, T def); + > CompletableFuture createInstance(String modelName, String instanceId, T base); /** * Delete and remove a digital twin instance from simulation processing. @@ -151,20 +120,19 @@ public interface SimulationController { * @return {@link SendingResult#Handled} if the instance was deleted, {@link SendingResult#Enqueued} if the instance * is in process of being deleted, or {@link SendingResult#NotHandled} if the instance could not be deleted. */ - SendingResult deleteInstance(String modelName, String instanceId); + CompletableFuture deleteInstance(String modelName, String instanceId); /** * Delete and remove this digital twin instance from simulation processing. * @return this local request will always return {@link SendingResult#Handled}. */ - SendingResult deleteThisInstance(); + CompletableFuture deleteThisInstance(); /** * Run this instance during this simulation step. The instance will be run using the models {@link SimulationProcessor#processModel(ProcessingContext, DigitalTwinBase, Date)} * implementation. * - * This will cause the simulation sub-system to run this instance regardless of the instances current - * {@link DigitalTwinBase#NextSimulationTime}. + * This will cause the simulation sub-system to run this instance during the current simulation step. */ void runThisInstance(); @@ -174,5 +142,4 @@ public interface SimulationController { */ SimulationStatus stopSimulation(); - } diff --git a/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/SimulationProcessor.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/SimulationProcessor.java new file mode 100644 index 0000000..331705f --- /dev/null +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/SimulationProcessor.java @@ -0,0 +1,65 @@ +/* + Copyright (c) 2026 by ScaleOut Software, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.scaleoutsoftware.digitaltwin.abstractions; + +import java.io.Serializable; +import java.util.Date; + +/** + * Processes simulation events for a digital twin. + * @param the type of the digital twin. + */ +public abstract class SimulationProcessor> { + /** + * Default constructor. + */ + public SimulationProcessor() {} + + /** + * Processes simulation events for a real-time digital twin. + * @param context the processing context. + * @param instance the digital twin instance. + * @param epoch the current time of the simulation. + * @return {@link ProcessingResult#UpdateDigitalTwin} to update the digital twin, or + * {@link ProcessingResult#NoUpdate} to ignore the changes. + */ + public abstract ProcessingResult processModel(ProcessingContext context, T instance, Date epoch); + + /** + *

+ * Optional method that is called per-instance when a simulation is started. Default behavior is a no-op. + *

+ * + *

+ * onInitSimulation can be used when internal digital twin starting state is set outside the context of a digital twins init method and may be changed + * between simulation runs. + *

+ *
    + *
  • Set variables in global or shared data.
  • + *
  • Run a simulation.
  • + *
  • onInitSimulation is called (per-instance) and digital twin instances set internal state based on the values in shared data.
  • + *
  • Complete simulation and evaluate the result.
  • + *
+ * + * @param context The simulation init context. + * @param instance The digital twin instance. + * @param epoch the simulation start time. + * @return {@link ProcessingResult#UpdateDigitalTwin} or {@link ProcessingResult#NoUpdate}. Default behavior: {@link ProcessingResult#NoUpdate}. + */ + public ProcessingResult onInitSimulation(InitSimulationContext context, T instance, Date epoch) { + return ProcessingResult.NoUpdate; + } +} diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/SimulationStatus.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/SimulationStatus.java similarity index 92% rename from Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/SimulationStatus.java rename to Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/SimulationStatus.java index fcb0f52..11e6d0b 100644 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/SimulationStatus.java +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/SimulationStatus.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.scaleoutsoftware.digitaltwin.core; +package com.scaleoutsoftware.digitaltwin.abstractions; /** * The status of a simulation. diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/TimerActionResult.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/TimerActionResult.java similarity index 95% rename from Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/TimerActionResult.java rename to Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/TimerActionResult.java index f326046..df1f203 100644 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/TimerActionResult.java +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/TimerActionResult.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2022 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.scaleoutsoftware.digitaltwin.core; +package com.scaleoutsoftware.digitaltwin.abstractions; /** * The result of a timer action. diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/TimerHandler.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/TimerHandler.java similarity index 84% rename from Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/TimerHandler.java rename to Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/TimerHandler.java index 60b82aa..337ca61 100644 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/TimerHandler.java +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/TimerHandler.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2022 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,13 +13,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.scaleoutsoftware.digitaltwin.core; +package com.scaleoutsoftware.digitaltwin.abstractions; /** * Callback to a handle a timer message for a {@link DigitalTwinBase}. * @param the type of the {@link DigitalTwinBase}. */ -public interface TimerHandler { +public interface TimerHandler> { /** * Callback to handle a timer message. @@ -29,5 +29,5 @@ public interface TimerHandler { * @return {@link ProcessingResult#UpdateDigitalTwin} to update the digital twin instance or * {@link ProcessingResult#NoUpdate} to leave the instance state as is. */ - public ProcessingResult onTimedMessage(String timerName, T instance, ProcessingContext ctx); + public ProcessingResult onTimedMessage(String timerName, T instance, ProcessingContext ctx); } diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/TimerMetadata.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/TimerMetadata.java similarity index 59% rename from Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/TimerMetadata.java rename to Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/TimerMetadata.java index dbd0758..120e09e 100644 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/TimerMetadata.java +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/TimerMetadata.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2022 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,41 +13,30 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.scaleoutsoftware.digitaltwin.core; - -import java.lang.reflect.InvocationTargetException; -import java.time.Duration; +package com.scaleoutsoftware.digitaltwin.abstractions; /** * Metadata class for a timer. * @param the type of the {@link DigitalTwinBase} implementation. */ -public class TimerMetadata { - final String timerHandler; - final TimerType timerType; - final long timerIntervalMs; - final int timerId; +public class TimerMetadata> { + final TimerHandler handlerClass; + final TimerType timerType; + final long timerIntervalMs; + final int timerSlot; /** * Constructs a timer metadata. - * @param handler the timer handler. + * @param handlerClass the timer handler. * @param timerType the timer type. * @param timerIntervalMs the timer interval. - * @param timerIdx the timer index. + * @param timerSlot the timer index. */ - public TimerMetadata(TimerHandler handler, TimerType timerType, long timerIntervalMs, int timerIdx) { - this.timerHandler = handler.getClass().getName(); + public TimerMetadata(TimerHandler handlerClass, TimerType timerType, long timerIntervalMs, int timerSlot) { + this.handlerClass = handlerClass; this.timerType = timerType; this.timerIntervalMs = timerIntervalMs; - this.timerId = timerIdx; - } - - /** - * Retrieves the timer handler class name. - * @return the timer handler class name. - */ - public String getTimerHandlerClass() { - return timerHandler; + this.timerSlot = timerSlot; } /** @@ -70,7 +59,11 @@ public long getTimerIntervalMs() { * Retrieves the timer ID. * @return the timer ID. */ - public int getTimerId() { - return timerId; + public int getTimerSlot() { + return timerSlot; + } + + public TimerHandler getHandler() { + return handlerClass; } } diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/TimerType.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/TimerType.java similarity index 88% rename from Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/TimerType.java rename to Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/TimerType.java index 41fee8c..e7ef13e 100644 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/TimerType.java +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/TimerType.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2022 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.scaleoutsoftware.digitaltwin.core; +package com.scaleoutsoftware.digitaltwin.abstractions; /** * Enum representation of the available timer types diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/package-info.java b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/package-info.java similarity index 85% rename from Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/package-info.java rename to Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/package-info.java index ee0bf8c..956efa9 100644 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/package-info.java +++ b/Abstractions/src/main/java/com/scaleoutsoftware/digitaltwin/abstractions/package-info.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2018 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,5 +17,5 @@ /** * Digital twin model API - Create a digital twin model. */ -package com.scaleoutsoftware.digitaltwin.core; +package com.scaleoutsoftware.digitaltwin.abstractions; diff --git a/Core/README.md b/Core/README.md deleted file mode 100644 index 5d35706..0000000 --- a/Core/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# ScaleOut Digital Twins™ Core Libraries for Java - -The open source (Apache 2.0 licensed) project for the Core API: - -- [com.scaleout.digitaltwin.core](https://repo.scaleoutsoftware.com/#artifact/com.scaleoutsoftware.digitaltwin/core): Core datatypes required for building Digital Twin models. - - -## Documentation -- [Class reference](https://scaleoutsoftware.github.io/JavaDigitalTwinCore/) -- [ScaleOut Digital Twins™ User Guide](https://static.scaleoutsoftware.com/docs/digital_twin_user_guide/index.html) \ No newline at end of file diff --git a/Core/build.gradle b/Core/build.gradle deleted file mode 100644 index 5d8de5b..0000000 --- a/Core/build.gradle +++ /dev/null @@ -1,33 +0,0 @@ -plugins { - id 'java' -} - -group 'com.scaleoutsoftware.digitaltwin' -version '3.0.9' - -sourceCompatibility = JavaVersion.VERSION_12 - -repositories { - mavenCentral() -} - -dependencies { - testImplementation group: 'junit', name: 'junit', version: '4.12' -} - -configurations { - archives -} - -task myJavadocs(type: Javadoc) { - source = sourceSets.main.allJava -} - -jar { - manifest { - attributes ('Implementation-Title': project.name, - 'Implementation-Version': project.version) - } -} - - diff --git a/Core/settings.gradle b/Core/settings.gradle deleted file mode 100644 index 1bdee66..0000000 --- a/Core/settings.gradle +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = 'digitaltwin-core' - diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/AlertProviderConfiguration.java b/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/AlertProviderConfiguration.java deleted file mode 100644 index 88bd238..0000000 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/AlertProviderConfiguration.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - Copyright (c) 2021 by ScaleOut Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.scaleoutsoftware.digitaltwin.core; - -import java.io.Serializable; - -/** - * Configuration for an alert provider. - */ -public class AlertProviderConfiguration implements Serializable { - /** - * The alert provider type. - */ - final String alertProviderType; - /** - * The required url for the alert provider type. - */ - final String url; - /** - * The required integration key for the alert provider type. - */ - final String integrationKey; - /** - * The required routing key for the alert provider type. - */ - final String routingKey; - /** - * The name of this alert provider. - */ - final String name; - /** - * The entity ID of this alert provider type. - */ - final String entityId; - - private AlertProviderConfiguration() {alertProviderType = url = integrationKey = routingKey = name = entityId = null;} - - /** - * Construct an alert provider configuration. - * @param alertProviderType the alert provider type. - * @param url the alert provider URL where alerts should be posted. - * @param integrationKey the integration key. - * @param routingKey the routing key. - * @param name the name of the alert provider. - * @param entityId the entity Id. - */ - public AlertProviderConfiguration(String alertProviderType, String url, String integrationKey, String routingKey, String name, String entityId) { - this.alertProviderType = alertProviderType; - this.url = url; - this.integrationKey = integrationKey; - this.routingKey = routingKey; - this.name = name; - this.entityId = entityId; - } - - /** - * Retrieve the alert provider type for this configuration. - * @return the alert provider type. - */ - public String getAlertProviderType() { - return alertProviderType; - } - - /** - * Retrieve the URL for this alert provider configuration. - * @return the URL for this alert provider configuration. - */ - public String getURL() { - return url; - } - - /** - * Retrieve the integration key for this alert provider configuration. - * @return the integration key for this alert provider configuration. - */ - public String getIntegrationKey() { - return integrationKey; - } - - /** - * Retrieve the routing key for this alert provider configuration. - * @return the routing key for this alert provider configuration. - */ - public String getRoutingKey() { - return routingKey; - } - - /** - * Retrieve the name of this alert provider configuration. - * @return the name of this alert provider configuration. - */ - public String getName() { - return name; - } - - /** - * Retrieve the entity ID for this alert provider configuration. - * @return the entity ID for this alert provider configuration. - */ - public String getEntityId() { - return entityId; - } - - @Override - public String toString() { - return "AlertProviderConfiguration{" + - "alertProviderType=" + alertProviderType + '\'' + - ", URL='" + url + '\'' + - ", IntegrationKey='" + integrationKey + '\'' + - ", RoutingKey='" + routingKey + '\'' + - ", Name='" + name + '\'' + - ", EntityId='" + entityId + '\'' + - '}'; - } -} diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/DigitalTwinBase.java b/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/DigitalTwinBase.java deleted file mode 100644 index d8082ca..0000000 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/DigitalTwinBase.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - Copyright (c) 2018 by ScaleOut Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.scaleoutsoftware.digitaltwin.core; - -import java.util.Date; -import java.util.HashMap; - -/** - * A real-time digital twin of a data source. The implementation of the real-time DigitalTwin should have a parameterless constructor for - * basic initialization. - */ -public abstract class DigitalTwinBase { - - /* capitalized to match .NET serialization */ - /** - * The identifier for this twin instance - */ - public String Id = ""; - - /** - * The model this twin instance belongs to. - */ - public String Model = ""; - - /** - * The timer handlers for this twin instance. - */ - public HashMap TimerHandlers = new HashMap<>(); - - /** - * Note: Simulation only. The next time in milliseconds that this Digital Twin instance will be passed to {@link SimulationProcessor#processModel(ProcessingContext, DigitalTwinBase, Date)}. - */ - public long NextSimulationTime = 0L; - - /** - * Default constructor. - */ - public DigitalTwinBase() {} - - /** - * Retrieve the next simulation time in milliseconds. - * @return the next simulation time in milliseconds. - */ - public long getNextSimulationTimeMs() { - return NextSimulationTime; - } - - /** - * Set the next simulation time in milliseconds. - * @param nextSimulationTime set the next simulation time. - */ - public void setNextSimulationTime(long nextSimulationTime) { - NextSimulationTime = nextSimulationTime; - } - - /** - * The identifier of this DigitalTwin. - * @return the identifier of this digital twin - */ - public String getId() { - return Id; - } - - /** - * The model for this DigitalTwin. - * @return the model for this DigitalTwin - */ - public String getModel() { - return Model; - } - - /** - * Initialization method to set the identifier and model for a DigitalTwin instance. Optionally use the - * {@link InitContext} to start a timer. - * @param context the initialization context. - * @throws IllegalStateException if init is called after initialization. - */ - public void init(InitContext context) throws IllegalStateException { - this.Id = context.getId(); - this.Model = context.getModel(); - } -} diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/DigitalTwinTimerMessage.java b/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/DigitalTwinTimerMessage.java deleted file mode 100644 index 9b591c8..0000000 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/DigitalTwinTimerMessage.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - Copyright (c) 2022 by ScaleOut Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.scaleoutsoftware.digitaltwin.core; - -/** - * A message sent to a digital twin instance's message processor. - */ -public class DigitalTwinTimerMessage { - - private String _modelName; - private String _twinId; - private int _timerId; - private String _timerName; - private TimerType _timerType; - - /** - * Construct a digital twin timer message. - * @param modelName the digital twin model name. - * @param twinId the digital twin instance ID - * @param timerId the timer ID - * @param timerName the timer name - * @param timerType the timer type - */ - public DigitalTwinTimerMessage(String modelName, String twinId, int timerId, String timerName, TimerType timerType) { - _modelName = modelName; - _twinId = twinId; - _timerId = timerId; - _timerName = timerName; - _timerType = timerType; - } - - /** - * Retrieve the digital twin model name. - * @return the digital twin model name. - */ - public String getModelName() { - return _modelName; - } - - /** - * Retrieve the digital twin ID. - * @return the digital twin ID. - */ - public String getTwinId() { - return _twinId; - } - - /** - * Retrieve the timer ID. - * @return the timer ID. - */ - public int getTimerId() { - return _timerId; - } - - /** - * Retrieve the timer name. - * @return the timer name. - */ - public String getTimerName() { - return _timerName; - } - - /** - * Retrieve the {@link TimerType}. - * @return the {@link TimerType} - */ - public TimerType getTimerType() { - return _timerType; - } -} diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/MessageProcessorBase.java b/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/MessageProcessorBase.java deleted file mode 100644 index 68a49c6..0000000 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/MessageProcessorBase.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - Copyright (c) 2018 by ScaleOut Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.scaleoutsoftware.digitaltwin.core; - -/** - * Base class for the MessageProcessor to help with typing. - * @param the type of the DigitalTwin - */ -public abstract class MessageProcessorBase { - - /** - * Default constructor. - */ - public MessageProcessorBase() {} - - /** - * Helper method to ensure proper typing for the user methods. - * @param context the processing context - * @param twin the real-time digital twin instance - * @param messageListFactory the message list factory - * @return the implementing class's processing result - * @throws Exception if an exception occurs during processing - */ - public abstract ProcessingResult processMessages(ProcessingContext context, T twin, MessageFactory messageListFactory) throws Exception; -} diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/ModelSchema.java b/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/ModelSchema.java deleted file mode 100644 index a2fe2f7..0000000 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/ModelSchema.java +++ /dev/null @@ -1,541 +0,0 @@ -/* - Copyright (c) 2018 by ScaleOut Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.scaleoutsoftware.digitaltwin.core; - -import java.util.List; - -/** - * The ModelSchema class is used as a Java object representation of the model.json schema file used for deploying a - * digital twin model to the real-time digital twin cloud service. - */ -public class ModelSchema { - private final String modelType; - private final String messageProcessorType; - private final String simulationProcessorType; - private final String messageType; - private final String assemblyName; - private final String azureDigitalTwinModelName; - private final String persistenceProvider; - private final boolean enablePersistence; - private final boolean enableSimulationSupport; - private final boolean enableMessageRecording; - private final List alertProviders; - - private ModelSchema() { - modelType = messageProcessorType = simulationProcessorType = messageType = assemblyName = azureDigitalTwinModelName = persistenceProvider = null; - enablePersistence = false; - enableSimulationSupport = false; - enableMessageRecording = false; - alertProviders = null; - } - - /** - * Creates a model schema from a digital twin class, a message processor class, and a message class. - * @param dtClass the digital twin class implementation. - * @param mpClass the message processor class implementation. - * @param msgClass a JSON serializable message class. - */ - public ModelSchema( - String dtClass, - String mpClass, - String msgClass) { - if( (dtClass == null || dtClass.isEmpty()) || - (mpClass == null || mpClass.isEmpty()) || - (msgClass == null || msgClass.isEmpty()) - ) { - throw new IllegalArgumentException(String.format("Expected value for dtClass, mpClass, and msgClass; actual values: %s, %s, %s", - (dtClass == null ? "null dtClass" : dtClass), - (mpClass == null ? "null mpClass" : mpClass), - (msgClass == null ? "null mpClass" : msgClass) - )); - } - modelType = dtClass; - messageProcessorType = mpClass; - simulationProcessorType = null; - enableSimulationSupport = false; - messageType = msgClass; - assemblyName = "NOT_USED_BY_JAVA_MODELS"; - alertProviders = null; - azureDigitalTwinModelName = null; - enablePersistence = false; - enableMessageRecording = false; - persistenceProvider = null; - } - - /** - * Creates a model schema from a digital twin class, a message processor class, and a message class. - * @param dtClass the digital twin class implementation. - * @param mpClass the message processor class implementation. - * @param msgClass a JSON serializable message class. - * @param emr enable message recording for this model. - */ - public ModelSchema( - String dtClass, - String mpClass, - String msgClass, - boolean emr) { - if( (dtClass == null || dtClass.isEmpty()) || - (mpClass == null || mpClass.isEmpty()) || - (msgClass == null || msgClass.isEmpty()) - ) { - throw new IllegalArgumentException(String.format("Expected value for dtClass, mpClass, and msgClass; actual values: %s, %s, %s", - (dtClass == null ? "null dtClass" : dtClass), - (mpClass == null ? "null mpClass" : mpClass), - (msgClass == null ? "null mpClass" : msgClass) - )); - } - modelType = dtClass; - messageProcessorType = mpClass; - simulationProcessorType = null; - enableSimulationSupport = false; - messageType = msgClass; - assemblyName = "NOT_USED_BY_JAVA_MODELS"; - alertProviders = null; - azureDigitalTwinModelName = null; - enablePersistence = false; - enableMessageRecording = emr; - persistenceProvider = null; - } - - /** - * Creates a model schema from a digital twin class, a message processor class, a message class, and - * alert provider configurations. - * @param dtClass the digital twin class implementation. - * @param mpClass the message processor class implementation. - * @param msgClass a JSON serializable message class. - * @param alertingProviders the alerting provider configurations. - */ - public ModelSchema( - String dtClass, - String mpClass, - String msgClass, - List alertingProviders) { - if( (dtClass == null || dtClass.isEmpty()) || - (mpClass == null || mpClass.isEmpty()) || - (msgClass == null || msgClass.isEmpty()) - ) { - throw new IllegalArgumentException(String.format("Expected value for dtClass, mpClass, and msgClass; actual values: %s, %s, %s", - (dtClass == null ? "null dtClass" : dtClass), - (mpClass == null ? "null mpClass" : mpClass), - (msgClass == null ? "null mpClass" : msgClass) - )); - } - modelType = dtClass; - messageProcessorType = mpClass; - simulationProcessorType = null; - enableSimulationSupport = false; - messageType = msgClass; - assemblyName = "NOT_USED_BY_JAVA_MODELS"; - azureDigitalTwinModelName = null; - enablePersistence = false; - enableMessageRecording = false; - persistenceProvider = null; - alertProviders = alertingProviders; - } - - /** - * Creates a model schema from a digital twin class, a message processor class, a message class, and - * alert provider configurations. - * @param dtClass the digital twin class implementation. - * @param mpClass the message processor class implementation. - * @param msgClass a JSON serializable message class. - * @param spClass the simulation processor class implementation. - * @param alertingProviders the alerting provider configurations. - */ - public ModelSchema( - String dtClass, - String mpClass, - String msgClass, - String spClass, - List alertingProviders) { - if( (dtClass == null || dtClass.isEmpty()) || - (mpClass == null || mpClass.isEmpty()) || - (msgClass == null || msgClass.isEmpty()) || - (spClass == null || spClass.isEmpty()) - ) { - throw new IllegalArgumentException(String.format("Expected value for dtClass, mpClass, and msgClass; actual values: %s, %s, %s", - (dtClass == null ? "null dtClass" : dtClass), - (mpClass == null ? "null mpClass" : mpClass), - (msgClass == null ? "null mpClass" : msgClass), - (spClass == null ? "null mpClass" : msgClass) - )); - } - modelType = dtClass; - messageProcessorType = mpClass; - simulationProcessorType = spClass; - enableSimulationSupport = true; - messageType = msgClass; - assemblyName = "NOT_USED_BY_JAVA_MODELS"; - azureDigitalTwinModelName = null; - enablePersistence = false; - persistenceProvider = null; - enableMessageRecording = false; - alertProviders = alertingProviders; - } - - /** - * Creates a model schema from a digital twin class, a message processor class, a message class, and - * alert provider configurations. - * @param dtClass the digital twin class implementation. - * @param mpClass the message processor class implementation. - * @param msgClass a JSON serializable message class. - * @param spClass the simulation processor class implementation. - * @param alertingProviders the alerting provider configurations. - * @param emr enable message recording for this model. - */ - public ModelSchema( - String dtClass, - String mpClass, - String msgClass, - String spClass, - List alertingProviders, - boolean emr) { - if( (dtClass == null || dtClass.isEmpty()) || - (mpClass == null || mpClass.isEmpty()) || - (msgClass == null || msgClass.isEmpty()) || - (spClass == null || spClass.isEmpty()) - ) { - throw new IllegalArgumentException(String.format("Expected value for dtClass, mpClass, and msgClass; actual values: %s, %s, %s", - (dtClass == null ? "null dtClass" : dtClass), - (mpClass == null ? "null mpClass" : mpClass), - (msgClass == null ? "null mpClass" : msgClass), - (spClass == null ? "null mpClass" : msgClass) - )); - } - modelType = dtClass; - messageProcessorType = mpClass; - simulationProcessorType = spClass; - enableSimulationSupport = true; - messageType = msgClass; - assemblyName = "NOT_USED_BY_JAVA_MODELS"; - azureDigitalTwinModelName = null; - enablePersistence = false; - persistenceProvider = null; - enableMessageRecording = emr; - alertProviders = alertingProviders; - } - - /** - * Creates a model schema from a digital twin class, a message processor class, a message class, and - * alert provider configurations. - * @param dtClass the digital twin class implementation. - * @param mpClass the message processor class implementation. - * @param msgClass a JSON serializable message class. - * @param adtName the Azure Digital Twin model name. - * @param persistenceType the persistence provider type. - * @param alertingProviders the alerting provider configurations. - */ - public ModelSchema( - String dtClass, - String mpClass, - String msgClass, - String adtName, - PersistenceProviderType persistenceType, - List alertingProviders) { - if( (dtClass == null || dtClass.isEmpty()) || - (mpClass == null || mpClass.isEmpty()) || - (msgClass == null || msgClass.isEmpty()) - ) { - throw new IllegalArgumentException(String.format("Expected value for dtClass, mpClass, and msgClass; actual values: %s, %s, %s", - (dtClass == null ? "null dtClass" : dtClass), - (mpClass == null ? "null mpClass" : mpClass), - (msgClass == null ? "null mpClass" : msgClass) - )); - } - modelType = dtClass; - messageProcessorType = mpClass; - simulationProcessorType = null; - enableSimulationSupport = false; - messageType = msgClass; - enableMessageRecording = false; - assemblyName = "NOT_USED_BY_JAVA_MODELS"; - persistenceProvider = persistenceType.name(); - switch (persistenceType) { - case AzureDigitalTwinsService: - azureDigitalTwinModelName = adtName; - enablePersistence = true; - break; - case SQLite: - case SQLServer: - case DynamoDb: - case CosmosDb: - enablePersistence = true; - azureDigitalTwinModelName = null; - break; - default: - azureDigitalTwinModelName = null; - enablePersistence = false; - break; - } - alertProviders = alertingProviders; - } - - /** - * Creates a model schema from a digital twin class, a message processor class, a message class, and - * alert provider configurations. - * @param dtClass the digital twin class implementation. - * @param mpClass the message processor class implementation. - * @param msgClass a JSON serializable message class. - * @param adtName the Azure Digital Twin model name. - * @param persistenceType the persistence provider type. - * @param alertingProviders the alerting provider configurations. - * @param emr enable message recording for this model. - */ - public ModelSchema( - String dtClass, - String mpClass, - String msgClass, - String adtName, - PersistenceProviderType persistenceType, - List alertingProviders, - boolean emr) { - if( (dtClass == null || dtClass.isEmpty()) || - (mpClass == null || mpClass.isEmpty()) || - (msgClass == null || msgClass.isEmpty()) - ) { - throw new IllegalArgumentException(String.format("Expected value for dtClass, mpClass, and msgClass; actual values: %s, %s, %s", - (dtClass == null ? "null dtClass" : dtClass), - (mpClass == null ? "null mpClass" : mpClass), - (msgClass == null ? "null mpClass" : msgClass) - )); - } - modelType = dtClass; - messageProcessorType = mpClass; - simulationProcessorType = null; - enableSimulationSupport = false; - messageType = msgClass; - enableMessageRecording = emr; - assemblyName = "NOT_USED_BY_JAVA_MODELS"; - persistenceProvider = persistenceType.name(); - switch (persistenceType) { - case AzureDigitalTwinsService: - azureDigitalTwinModelName = adtName; - enablePersistence = true; - break; - case SQLite: - case SQLServer: - case DynamoDb: - enablePersistence = true; - azureDigitalTwinModelName = null; - break; - default: - azureDigitalTwinModelName = null; - enablePersistence = false; - break; - } - alertProviders = alertingProviders; - } - - /** - * Creates a model schema from a digital twin class, a message processor class, a message class, - * a simulation processor class, an Azure Digital Twin Model name class, a persistence provider type, - * and an alert provider configuration. - * - * @param dtClass the digital twin class implementation. - * @param mpClass the message processor class implementation. - * @param msgClass a JSON serializable message class. - * @param simulationProcessorClass the simulation processor class implementation. - * @param adtName the Azure Digital Twin model name. - * @param persistenceType the persistence provider type. - * @param alertingProviders the alerting provider configurations. - */ - public ModelSchema( - String dtClass, - String mpClass, - String msgClass, - String simulationProcessorClass, - String adtName, - PersistenceProviderType persistenceType, - List alertingProviders) { - if( (dtClass == null || dtClass.isEmpty()) || - (mpClass == null || mpClass.isEmpty()) || - (msgClass == null || msgClass.isEmpty()) - ) { - throw new IllegalArgumentException(String.format("Expected value for dtClass, mpClass, and msgClass; actual values: %s, %s, %s", - (dtClass == null ? "null dtClass" : dtClass), - (mpClass == null ? "null mpClass" : mpClass), - (msgClass == null ? "null mpClass" : msgClass) - )); - } - modelType = dtClass; - messageProcessorType = mpClass; - simulationProcessorType = simulationProcessorClass; - enableSimulationSupport = true; - enableMessageRecording = false; - messageType = msgClass; - assemblyName = "NOT_USED_BY_JAVA_MODELS"; - persistenceProvider = persistenceType.name(); - switch (persistenceType) { - case AzureDigitalTwinsService: - azureDigitalTwinModelName = adtName; - enablePersistence = true; - break; - case SQLite: - case SQLServer: - case DynamoDb: - enablePersistence = true; - azureDigitalTwinModelName = null; - break; - default: - azureDigitalTwinModelName = null; - enablePersistence = false; - break; - } - alertProviders = alertingProviders; - } - - /** - * Creates a model schema from a digital twin class, a message processor class, a message class, - * a simulation processor class, an Azure Digital Twin Model name class, a persistence provider type, - * and an alert provider configuration. - * - * @param dtClass the digital twin class implementation. - * @param mpClass the message processor class implementation. - * @param msgClass a JSON serializable message class. - * @param simulationProcessorClass the simulation processor class implementation. - * @param adtName the Azure Digital Twin model name. - * @param persistenceType the persistence provider type. - * @param alertingProviders the alerting provider configurations. - * @param emr enable message recording for this model. - */ - public ModelSchema( - String dtClass, - String mpClass, - String msgClass, - String simulationProcessorClass, - String adtName, - PersistenceProviderType persistenceType, - List alertingProviders, - boolean emr) { - if( (dtClass == null || dtClass.isEmpty()) || - (mpClass == null || mpClass.isEmpty()) || - (msgClass == null || msgClass.isEmpty()) - ) { - throw new IllegalArgumentException(String.format("Expected value for dtClass, mpClass, and msgClass; actual values: %s, %s, %s", - (dtClass == null ? "null dtClass" : dtClass), - (mpClass == null ? "null mpClass" : mpClass), - (msgClass == null ? "null mpClass" : msgClass) - )); - } - modelType = dtClass; - messageProcessorType = mpClass; - simulationProcessorType = simulationProcessorClass; - enableSimulationSupport = true; - enableMessageRecording = emr; - messageType = msgClass; - assemblyName = "NOT_USED_BY_JAVA_MODELS"; - persistenceProvider = persistenceType.name(); - switch (persistenceType) { - case AzureDigitalTwinsService: - azureDigitalTwinModelName = adtName; - enablePersistence = true; - break; - case SQLite: - case SQLServer: - case DynamoDb: - case CosmosDb: - enablePersistence = true; - azureDigitalTwinModelName = null; - break; - default: - azureDigitalTwinModelName = null; - enablePersistence = false; - break; - } - alertProviders = alertingProviders; - } - - /** - * Retrieve the digital twin model type (a {@link DigitalTwinBase} implementation). - * @return the model type. - */ - public String getModelType() { - return modelType; - } - - /** - * Retrieve the message type (JSON serializable message implementation). - * @return the message type. - */ - public String getMessageType() { - return messageType; - } - - /** - * Retrieve the message processor type (a {@link MessageProcessor} implementation). - * @return the message processor type. - */ - public String getMessageProcessorType() { - return messageProcessorType; - } - - /** - * Retrieve the simulation processor type (a {@link SimulationProcessor} implementation). - * @return the simulation processor type. - */ - public String getSimulationProcessorType() { - return simulationProcessorType; - } - - /** - * NOT USED BY JAVA MODEL SCHEMA - * @return NOT USED BY JAVA MODEL SCHEMA - */ - public String getAssemblyName() { - return assemblyName; - } - - /** - * Retrieve the alert provider configurations. - * @return the alert provider configurations. - */ - public List getAlertProviders() {return alertProviders; } - - /** - * Retrieve the Azure Digital Twin model name. - * @return the Azure Digital Twin model name. - */ - public String getAzureDigitalTwinModelName() { - return azureDigitalTwinModelName; - } - - /** - * Retrieve persistence status. True if persistence is enabled, false otherwise. - * @return True if persistence is enabled, false otherwise. - */ - public boolean persistenceEnabled() { return enablePersistence; } - - /** - * Retrieve simulation support enabled status. True if simulation support is enabled, false otherwise. - * @return True if simulation support is enabled, false otherwise. - */ - public boolean simulationSupportEnabled() {return enableSimulationSupport;} - - /** - * Retrieve the persistence provider type. - * @return the persistence provider type. - */ - public PersistenceProviderType getPersistenceProvider() { return PersistenceProviderType.fromString(persistenceProvider); } - - /** - * Retrieves the message recording enabled status. True if this model should persist messages when message recording is active, - * false otherwise. - * @return True if message recording is enabled, false otherwise. - */ - public boolean messageRecordingEnabled() { - return enableMessageRecording; - } -} diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/PersistenceProviderType.java b/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/PersistenceProviderType.java deleted file mode 100644 index 17681a2..0000000 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/PersistenceProviderType.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - Copyright (c) 2021 by ScaleOut Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.scaleoutsoftware.digitaltwin.core; - -import java.io.Serializable; - -/** - * Available {@link PersistenceProvider} types. - */ -public enum PersistenceProviderType implements Serializable { - - /** - * Enum for the Azure Digital Twin service. - */ - AzureDigitalTwinsService("AzureDigitalTwinsService", 1), - /** - * Enum for CosmosDB - */ - CosmosDb("Azure Cosmos DB", 6), - - /** - * Enum for DynamoDB - */ - DynamoDb("DynamoDB", 5), - /** - * Enum for SQLite - */ - SQLite("SQLite", 4), - /** - * Enum for SQLServer - */ - SQLServer("SQLServer", 3), - - /** - * Enum for an unconfigured PersistenceProvider - */ - Unconfigured("", 0); - - private String _name; - private int _value; - PersistenceProviderType(String name, int ordinal) { - _name = name; - _value = ordinal; - } - - /** - * Return the PersistenceProviderType from a string value. We do not rely on Java's naming - * because the string values need to be cross-platform and the names may be different in each language. - * @param name the enums name. - * @return the associated PersistenceProviderType, or null if no association exists. - */ - public static PersistenceProviderType fromString(String name) { - if(name != null && !name.isEmpty() && !name.isBlank()) { - switch(name) { - case "AzureDigitalTwinsService": - return AzureDigitalTwinsService; - case "SQLite": - return SQLite; - case "SQLServer": - return SQLServer; - case "DynamoDB": - return DynamoDb; - case "Azure Cosmos DB": - return CosmosDb; - default: - return null; - } - } else { - return null; - } - } - - /** - * Return the PersistenceProviderType from an ordinal value. We do not rely on Java's ordering - * because the ordinal values need to be cross-platform, and the values may be ordered differently. - * @param ordinal the enums ordinal value. - * @return the associated PersistenceProviderType, or null if no association exists. - */ - public static PersistenceProviderType fromOrdinal(int ordinal) { - switch(ordinal) { - case 1: - return AzureDigitalTwinsService; - case 3: - return SQLServer; - case 4: - return SQLite; - case 5: - return DynamoDb; - case 6: - return CosmosDb; - default: - return null; - } - } -} diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/ProcessingContext.java b/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/ProcessingContext.java deleted file mode 100644 index 9803b63..0000000 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/ProcessingContext.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - Copyright (c) 2018 by ScaleOut Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.scaleoutsoftware.digitaltwin.core; - -import java.io.Serializable; -import java.time.Duration; -import java.util.Date; -import java.util.List; -import java.util.logging.Level; - -/** - * Context object that allows the user to send a message to a DataSource. - */ -public abstract class ProcessingContext implements Serializable { - - /** - * Default constructor. - */ - public ProcessingContext() {} - - /** - *

- * Sends a message to a data source. This will route messages through the connector back to the data source. - *

- * - *

- * if the datasource is simulation instance, then the message will be sent to the simulation model's implementation - * of the {@link MessageProcessor}. - *

- * - * @param payload the message (as a serialized JSON string) - * @return the sending result - */ - public abstract SendingResult sendToDataSource(byte[] payload); - - /** - *

- * Sends a message to a data source. This will route messages through the connector back to the data source. - *

- * - *

- * if the datasource is simulation instance, then the message will be sent to the simulation model's implementation - * of the {@link MessageProcessor}. - *

- * - * @param jsonSerializableMessage a JSON serializable message. - * @return the sending result - */ - public abstract SendingResult sendToDataSource(Object jsonSerializableMessage); - - /** - *

- * Sends a list of messages to a data source. This will route messages through the connector back to the data source. - *

- * - *

- * if the datasource is simulation instance, then the message will be sent to the simulation model's implementation - * of the {@link MessageProcessor#processMessages(ProcessingContext, DigitalTwinBase, Iterable)}. - *

- * - * @param jsonSerializableMessages a list of JSON serializable messages. - * @return the sending result - */ - public abstract SendingResult sendToDataSource(List jsonSerializableMessages); - - /** - *

- * This method sends a serialized JSON message to a real-time digital twin - *

- * - *

- * Note, the message contents must be serialized so that the registered message type - * of the digital twin model will be sufficient to deserialize the message. - *

- * @param model the model of the digital twin - * @param id the id of the digital twin - * @param payload the serialized JSON message - * @return the sending result - */ - public abstract SendingResult sendToDigitalTwin(String model, String id, byte[] payload); - - /** - *

- * This method sends a serialized JSON message to a real-time digital twin - *

- * - *

- * Note, the message contents must be serialized so that the registered message type - * of the digital twin model will be sufficient to deserialize the message. - *

- * @param model the model of the digital twin - * @param id the id of the digital twin - * @param jsonSerializableMessage a JSON serializable message object - * @return the sending result - */ - public abstract SendingResult sendToDigitalTwin(String model, String id, Object jsonSerializableMessage); - - /** - *

- * This method sends a JSON message to a real-time digital twin - *

- * - *

- * Note, the message contents must be serialized so that the registered message type - * of the digital twin model will be sufficient to deserialize the message. - *

- * - * @param model the model of the digital twin - * @param id the id of the digital twin - * @param payload the JSON message - * @return the sending result - */ - public abstract SendingResult sendToDigitalTwin(String model, String id, String payload); - - /** - *

- * This method sends a list of serialized JSON message to a real-time digital twin - *

- * - *

- * Note, the message contents must be serialized so that the registered message type - * of the digital twin model will be sufficient to deserialize the message. - *

- * - * @param model the model of the digital twin - * @param id the id of the digital twin - * @param payload the JSON message - * @return the sending result - */ - public abstract SendingResult sendToDigitalTwin(String model, String id, List payload); - - - /** - *

- * This method sends an alert message to supported systems. See "TODO: Link to docs" for more details on supported systems. - *

- * - *

- * When a model is deployed, an optional alerting provider configuration can be supplied. The provider name corresponds - * to the name of the configured alerting provider. For example, if an alerting provider configuration is called - * "SREPod1", then the processing context will send an alert message to the alerting provider configured with the name - * "SREPod1". - *

- * - *

- * If the message cannot be sent then {@link SendingResult#NotHandled} will be returned. If the message is sent - * and in process of sending then {@link SendingResult#Enqueued} will be returned. Once the message is successfully sent - * then the returned enum will be changed to {@link SendingResult#Handled}. - *

- * @param alertingProviderName the alerting provider name. Note, must match a valid configuration. - * @param alert the alert message. - * @return the sending result. - */ - public abstract SendingResult sendAlert(String alertingProviderName, AlertMessage alert); - - /** - * Returns the configured persistence provider or null if no persistence provider configuration can be found. - * @return a PersistenceProvider . - */ - public abstract PersistenceProvider getPersistenceProvider(); - - /** - * Retrieve the unique Identifier for a DataSource (matches the Device/Datasource/Real-time twin ID) - * @return the digital twin id - */ - public abstract String getDataSourceId(); - - /** - * Retrieve the model for a DigitalTwin (matches the model of a Device/Datasource/real-time twin) - * @return the digital twin model - */ - public abstract String getDigitalTwinModel(); - - /** - * Logs a message to the real-time digital twin cloud service. - * - * Note: the only supported severity levels are: INFO, WARN, and SEVERE - * - * @param severity the severity of the log message - * @param message the message to log - */ - public abstract void logMessage(Level severity, String message); - - /** - * Starts a new timer for the digital twin - * @param timerName the timer name - * @param interval the timer interval - * @param timerType the timer type - * @param timerHandler the time handler callback - * @param the type of the digital twin - * @return returns {@link TimerActionResult#Success} if the timer was started, {@link TimerActionResult#FailedTooManyTimers} - * if too many timers exist, or {@link TimerActionResult#FailedInternalError} if an unexpected error occurs. - */ - public abstract TimerActionResult startTimer(String timerName, Duration interval, TimerType timerType, TimerHandler timerHandler); - - /** - * Stops the specified timer. - * @param timerName the timer name. - * @return returns {@link TimerActionResult#Success} if the timer was stopped, {@link TimerActionResult#FailedNoSuchTimer} - * if no timer exists with that name, or {@link TimerActionResult#FailedInternalError} if an unexpected error occurs. - */ - public abstract TimerActionResult stopTimer(String timerName); - - /** - * Retrieves the current time. If the model (simulation or real-time) is running inside of a simulation then the - * simulation time will be returned. - * - * @return The current time (real time, or simulation if running under simulation). - */ - public abstract Date getCurrentTime(); - - /** - * Retrieve the running {@link SimulationController} or null if no simulation is running. - * @return the {@link SimulationController} or null if no simulation is running. - */ - public abstract SimulationController getSimulationController(); - - /** - * Retrieve a {@link SharedData} accessor for this model's shared data. - * @return a {@link SharedData} instance. - */ - public abstract SharedData getSharedModelData(); - - /** - * Retrieve a {@link SharedData} accessor for globally shared data. - * @return a {@link SharedData} instance. - */ - public abstract SharedData getSharedGlobalData(); - -} diff --git a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/SimulationProcessor.java b/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/SimulationProcessor.java deleted file mode 100644 index 9dda706..0000000 --- a/Core/src/main/java/com/scaleoutsoftware/digitaltwin/core/SimulationProcessor.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - Copyright (c) 2022 by ScaleOut Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.scaleoutsoftware.digitaltwin.core; - -import java.io.Serializable; -import java.util.Date; - -/** - * Processes simulation events for a digital twin. - * @param the type of the digital twin. - */ -public abstract class SimulationProcessor implements Serializable { - - /** - * Default constructor. - */ - public SimulationProcessor() {} - - /** - * Processes simulation events for a real-time digital twin. - * @param context the processing context. - * @param instance the digital twin instance. - * @param epoch the current time of the simulation. - * @return {@link ProcessingResult#UpdateDigitalTwin} to update the digital twin, or - * {@link ProcessingResult#NoUpdate} to ignore the changes. - */ - public abstract ProcessingResult processModel(ProcessingContext context, T instance, Date epoch); -} diff --git a/Development/README.md b/Development/README.md index a48dbe3..93648f8 100644 --- a/Development/README.md +++ b/Development/README.md @@ -2,7 +2,7 @@ The open source (Apache 2.0 licensed) project for the Workbench API: -- [com.scaleout.digitaltwin.development](https://repo.scaleoutsoftware.com/#artifact/com.scaleoutsoftware.digitaltwin/development): A lightweight [development/testing framework](https://static.scaleoutsoftware.com/docs/digital_twin_user_guide/software_toolkit/dt_builder/java_api/workbench.html) for running Digital Twin models. +- [com.scaleoutsoftware.digitaltwin:digitaltwin-development](https://mvnrepository.com/artifact/com.scaleoutsoftware.digitaltwin/digitaltwin-development/3.0.0): A lightweight [development/testing framework](https://static.scaleoutsoftware.com/docs/digital_twin_user_guide/software_toolkit/dt_builder/java_api/workbench.html) for running Digital Twin models. ## Documentation - [Class reference](https://scaleoutsoftware.github.io/JavaDigitalTwinCore/) diff --git a/Development/build.bat b/Development/build.bat new file mode 100644 index 0000000..a40786e --- /dev/null +++ b/Development/build.bat @@ -0,0 +1,8 @@ +rem ******************** +rem * Requirements * +rem ******************** +rem * Java JDK 8 +rem * MVN 3.x +SET JAVA_HOME=C:\path\to\java8 +SET MVN_HOME=C:\path\to\maven +call mvn clean package install javadoc:jar source:jar \ No newline at end of file diff --git a/Development/build.gradle b/Development/build.gradle deleted file mode 100644 index d3afa74..0000000 --- a/Development/build.gradle +++ /dev/null @@ -1,27 +0,0 @@ -plugins { - id 'java' - id 'java-library' -} - -group 'com.scaleoutsoftware.digitaltwin' -version '3.0.7' - -sourceCompatibility = JavaVersion.VERSION_12 - -repositories { - mavenCentral() - maven { - url "https://repo.scaleoutsoftware.com/repository/external/" - } -} - -dependencies { - implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.20.0' - testImplementation group: 'junit', name: 'junit', version: '4.12' - implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.5' - // public build configuration - implementation group: 'com.scaleoutsoftware.digitaltwin', name: 'core', version: '3.0.7' - - // local build configuration - //implementation fileTree(dir: '..\\Core\\build\\libs\\', include: '*.jar') -} diff --git a/Development/build.sh b/Development/build.sh new file mode 100644 index 0000000..83def59 --- /dev/null +++ b/Development/build.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +export JAVA_HOME="/path/to/java8" +export MVN_HOME="/path/to/maven" + +export PATH="$JAVA_HOME/bin:$MVN_HOME/bin:$PATH" + +mvn clean package install javadoc:jar source:jar \ No newline at end of file diff --git a/Development/pom.xml b/Development/pom.xml new file mode 100644 index 0000000..3903d52 --- /dev/null +++ b/Development/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + + com.scaleoutsoftware.digitaltwin + digitaltwin-development + 3.0.0 + 2021 + https://scaleoutsoftware.com + Digital Twin Core API + Development/Workbench API for testing ScaleOut real-time and simulated digital twins. + + + scaleoutdev + ScaleOut Dev + dev@scaleoutsoftware.com + https://scaleoutsoftware.com + ScaleOut Software, Inc. + https://scaleoutsoftware.com + + developer + engineer + + America/Los_Angeles + + + + + Apache License 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + Apache License 2.0 + + + + https://github.com/scaleoutsoftware/JavaDigitalTwinCore + + + 8 + 8 + UTF-8 + + + + com.google.code.gson + gson + 2.14.0 + + + org.apache.logging.log4j + log4j-api + 2.25.4 + + + org.apache.logging.log4j + log4j-core + 2.25.4 + + + com.scaleoutsoftware.digitaltwin + digitaltwin-abstractions + 3.0.0 + + + junit + junit + 4.13.2 + test + + + + \ No newline at end of file diff --git a/Development/settings.gradle b/Development/settings.gradle deleted file mode 100644 index e652c08..0000000 --- a/Development/settings.gradle +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = 'digitaltwin-development' - diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/Constants.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/Constants.java index 4b9361b..0e69eab 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/Constants.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/Constants.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/LogMessage.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/LogMessage.java index 23e5775..8990d1d 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/LogMessage.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/LogMessage.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/ProxyState.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/ProxyState.java index 9875074..2b97438 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/ProxyState.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/ProxyState.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationEvent.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationEvent.java index 19e739b..7bc8af7 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationEvent.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationEvent.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ */ package com.scaleoutsoftware.digitaltwin.development; -import com.scaleoutsoftware.digitaltwin.core.ProcessingContext; +import com.scaleoutsoftware.digitaltwin.abstractions.ProcessingContext; import java.util.Date; @@ -37,6 +37,10 @@ abstract class SimulationEvent implements Comparable { abstract void setProxyState(ProxyState newState); + abstract void handleResetNextSimulationTime(); + + abstract void simulationInit(Date simulationStartTime); + long getPriority() { return _priority; } @@ -56,7 +60,7 @@ void setNextSimulationTime(long nextSimulationTime) { handleResetNextSimulationTime(); } - abstract void handleResetNextSimulationTime(); + @Override public int compareTo(SimulationEvent other) { diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationEventResult.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationEventResult.java index ea490af..deb5d75 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationEventResult.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationEventResult.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationEventTimerImpl.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationEventTimerImpl.java index 864b861..6824686 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationEventTimerImpl.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationEventTimerImpl.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,9 +15,9 @@ */ package com.scaleoutsoftware.digitaltwin.development; -import com.scaleoutsoftware.digitaltwin.core.DigitalTwinBase; -import com.scaleoutsoftware.digitaltwin.core.ProcessingContext; -import com.scaleoutsoftware.digitaltwin.core.TimerHandler; +import com.scaleoutsoftware.digitaltwin.abstractions.DigitalTwinBase; +import com.scaleoutsoftware.digitaltwin.abstractions.ProcessingContext; +import com.scaleoutsoftware.digitaltwin.abstractions.TimerHandler; import java.util.Date; @@ -55,4 +55,9 @@ void setProxyState(ProxyState newState) { @Override void handleResetNextSimulationTime() { } + + @Override + void simulationInit(Date simulationStartTime) { + + } } diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationEventTwinImpl.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationEventTwinImpl.java index 9d1f9a7..337102e 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationEventTwinImpl.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationEventTwinImpl.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,22 +15,23 @@ */ package com.scaleoutsoftware.digitaltwin.development; -import com.scaleoutsoftware.digitaltwin.core.DigitalTwinBase; -import com.scaleoutsoftware.digitaltwin.core.ProcessingContext; -import com.scaleoutsoftware.digitaltwin.core.SimulationProcessor; +import com.scaleoutsoftware.digitaltwin.abstractions.*; import java.nio.charset.StandardCharsets; import java.util.Date; -import java.util.Objects; class SimulationEventTwinImpl extends SimulationEvent { SimulationProcessor _processor; TwinProxy _proxy; + SharedData _modelSharedData; + SharedData _globalSharedData; - SimulationEventTwinImpl(long priority, TwinProxy proxy, SimulationProcessor processor) { + SimulationEventTwinImpl(long priority, TwinProxy proxy, SimulationProcessor processor, SharedData modelSharedData, SharedData globalSharedData) { super(proxy.getInstance().Model, proxy.getInstance().Id, priority); - _proxy = proxy; - _processor = processor; + _proxy = proxy; + _processor = processor; + _modelSharedData = modelSharedData; + _globalSharedData = globalSharedData; } @Override @@ -39,8 +40,11 @@ SimulationEventResult processSimulationEvent(ProcessingContext context, Date cur DigitalTwinBase base = _proxy.getInstance(); synchronized (_proxy) { WorkbenchProcessingContext wpc = (WorkbenchProcessingContext)context; - wpc.resetInstance(base); - _processor.processModel(wpc, base, currentTime); + wpc.resetProxy(_proxy); + ProcessingResult result = _processor.processModel(wpc, base, currentTime); + if(result == ProcessingResult.Remove) { + _proxy.setProxyState(ProxyState.Removed); + } _proxy.setInstance(base); } } catch (Exception e) { @@ -61,9 +65,16 @@ void setProxyState(ProxyState newState) { @Override void handleResetNextSimulationTime() { - DigitalTwinBase base = _proxy.getInstance(); - base.NextSimulationTime = _nextSimulationTime; - _proxy.setInstance(base); + } + + @Override + void simulationInit(Date simulationStartTime) { + InitSimulationContext context = new WorkbenchInitSimulationContext(_globalSharedData, _modelSharedData); + synchronized (_proxy) { + DigitalTwinBase base = _proxy.getInstance(); + _processor.onInitSimulation(context, base, simulationStartTime); + _proxy.setInstance(base); + } } @Override diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationScheduler.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationScheduler.java index 3deaeca..1c2ad82 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationScheduler.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationScheduler.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ */ package com.scaleoutsoftware.digitaltwin.development; -import com.scaleoutsoftware.digitaltwin.core.*; +import com.scaleoutsoftware.digitaltwin.abstractions.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -29,37 +29,40 @@ class SimulationScheduler { static AtomicInteger PROCESSED = new AtomicInteger(0); static AtomicInteger QUEUED = new AtomicInteger(0); static AtomicInteger SENT = new AtomicInteger(0); - private static final int NUM_SIMULATION_WORKERS = Runtime.getRuntime().availableProcessors(); - private final List _workers = new ArrayList<>(NUM_SIMULATION_WORKERS); - private final ExecutorService _simulationService = Executors.newFixedThreadPool(NUM_SIMULATION_WORKERS, new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r, "SimulationWorker"); - t.setName(t.getName()+"-"+t.getId()); - t.setDaemon(true); - return t; - } - }); + private final List _workers; + private final ExecutorService _simulationService; private final String _modelName; private final SimulationProcessor _simulationProcessor; private final Logger _logger = LogManager.getLogger(SimulationScheduler.class); private long _curSimulationTime; + private Date _simulationStartTime; private boolean _isActive; public SimulationScheduler(String modelName, Class digitalTwinClass, SimulationProcessor modelProcessor, - TwinExecutionEngine executor) { + TwinExecutionEngine executor, + int numWorkers) { _modelName = modelName; _simulationProcessor = modelProcessor; - for(int i = 0; i < NUM_SIMULATION_WORKERS; i++) { + _workers = new ArrayList<>(numWorkers); + _simulationService = Executors.newFixedThreadPool(numWorkers, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r, "SimulationWorker"); + t.setName(t.getName()+"-"+t.getId()); + t.setDaemon(true); + return t; + } + }); + for(int i = 0; i < numWorkers; i++) { _workers.add(new SimulationWorker(i, _modelName, _simulationProcessor, digitalTwinClass, executor, this)); } } - // -------------- public methods ---------------- - public SimulationStep runSimulation(SimulationStepArgs runSimulationEventArgs) { + // -------------- package private methods ---------------- + SimulationStep runSimulation(SimulationStepArgs runSimulationEventArgs) { _logger.info("Received run simulation event with args: " + runSimulationEventArgs.getCurSimulationTime() + " iterationSize: " + runSimulationEventArgs.getIterationSize() + " flags: " + runSimulationEventArgs.getSimulationFlags() ); long current = System.currentTimeMillis(); SimulationStep ret; @@ -71,12 +74,18 @@ public SimulationStep runSimulation(SimulationStepArgs runSimulationEventArgs) { worker.shutdown(); } return new SimulationStep(SimulationStatus.UserRequested,_curSimulationTime); + } + if(runSimulationEventArgs.getSimulationFlags() == WorkbenchSimulationFlags.Start) { + _logger.info("Starting simulation; initializing instances."); + for(SimulationWorker worker : _workers) { + worker.initSimulation(new Date(runSimulationEventArgs.getCurSimulationTime())); + } + return new SimulationStep(SimulationStatus.Running,_curSimulationTime); } else { ret = runSimulationStep(runSimulationEventArgs); } _logger.info(String.format("runSim complete in %s ms... returning next: %s", (System.currentTimeMillis()-current), ret)); - _logger.info(String.format("Queued: %s Processed: %s Sent: %s", QUEUED.getAndSet(0), PROCESSED.getAndSet(0), SENT.getAndSet(0))); return ret; } @@ -91,6 +100,16 @@ void setStatus(boolean active) { _isActive = active; } + Date getSimulationStartTime() { + return _simulationStartTime; + } + + void setSimulationStartTime(Date simulationStartTime) { + _simulationStartTime = simulationStartTime; + } + + + // -------------- private methods ---------------- private SimulationStep runSimulationStep(SimulationStepArgs args) { long currentTimeMs = System.currentTimeMillis(); @@ -152,7 +171,7 @@ void runThisInstance(String model, String id) throws WorkbenchException { } private int findSlotId(String id) { - return (int)((Constants.getHash(id.getBytes(StandardCharsets.UTF_8))) % (long)NUM_SIMULATION_WORKERS); + return (int)((Constants.getHash(id.getBytes(StandardCharsets.UTF_8))) % (long)_workers.size()); } diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationStep.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationStep.java index 3913423..6d9489c 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationStep.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationStep.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ */ package com.scaleoutsoftware.digitaltwin.development; -import com.scaleoutsoftware.digitaltwin.core.SimulationStatus; +import com.scaleoutsoftware.digitaltwin.abstractions.SimulationStatus; /** * The simulation step class encases the metadata for a completed interval of a simulation. diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationStepArgs.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationStepArgs.java index cfe6774..76ab354 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationStepArgs.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationStepArgs.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationTime.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationTime.java index 021eaa5..7bbe1b5 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationTime.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationTime.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationWorker.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationWorker.java index 14ab16f..6abfe08 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationWorker.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/SimulationWorker.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ */ package com.scaleoutsoftware.digitaltwin.development; -import com.scaleoutsoftware.digitaltwin.core.*; +import com.scaleoutsoftware.digitaltwin.abstractions.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -66,7 +66,7 @@ public void shutdown() { } public void addTwinToQueue(TwinProxy proxy) { - SimulationEvent event = new SimulationEventTwinImpl(_curSimulationTime, proxy, _simulationProcessor); + SimulationEvent event = new SimulationEventTwinImpl(_curSimulationTime, proxy, _simulationProcessor, new WorkbenchSharedData(_twinExecutionEngine.getGlobalSharedData()), new WorkbenchSharedData(_twinExecutionEngine.getModelData(_modelName))); _timeOrderedQueue.add(event); _events.put(String.format("%s%s",event.getModel(),event.getId()), event); } @@ -89,14 +89,23 @@ public void stopTimer(String model, String id, String timerName) { _events.remove(String.format("%s%s",event.getModel(),event.getId())); } + void initSimulation(Date startTime) { + for(SimulationEvent event : _events.values()) { + event.simulationInit(startTime); + } + } + public void runThisInstance(String model, String id) throws WorkbenchException { - SimulationEvent event = _events.get(String.format("%s%s",model,id)); + SimulationEvent event = _events.remove(String.format("%s%s",model,id)); if(event == null) { TwinProxy proxy = _twinExecutionEngine.getTwinProxy(model, id); - event = new SimulationEventTwinImpl(_curSimulationTime, proxy, _simulationProcessor); + event = new SimulationEventTwinImpl(_curSimulationTime, proxy, _simulationProcessor, new WorkbenchSharedData(_twinExecutionEngine.getGlobalSharedData()), new WorkbenchSharedData(_twinExecutionEngine.getModelData(_modelName))); + } else { + _timeOrderedQueue.remove(event); } WorkbenchSimulationController simulationController = new WorkbenchSimulationController(_twinExecutionEngine, _simulationScheduler); WorkbenchProcessingContext processingContext = new WorkbenchProcessingContext(_twinExecutionEngine, simulationController); + processingContext.reset(model, id, null); Date date = new Date(); date.setTime(_curSimulationTime); event.processSimulationEvent(processingContext, date); @@ -116,8 +125,10 @@ public void runThisInstance(String model, String id) throws WorkbenchException { event.setPriority(_curSimulationTime + _simulationInterval); event.setNextSimulationTime(_curSimulationTime + _simulationInterval); } - _events.put(String.format("%s%s",model,id), event); - _timeOrderedQueue.add(event); + if(!simulationController.deleted() && !(event.getProxyState() == ProxyState.Removed)) { + _events.put(String.format("%s%s",model,id), event); + _timeOrderedQueue.add(event); + } } @Override @@ -143,7 +154,7 @@ public SimulationStep call() throws Exception { if(next != null) { if(next.getProxyState() == ProxyState.Active) { processed++; - nextQueueTm = next.getPriority(); + nextQueueTm = next.getPriority(); if(next.getPriority() <= simulationTime.getCurrentSimulationTime()) { simulationController.reset(_modelName, next.getId()); processingContext.reset(_modelName, next.getId(), null); @@ -179,7 +190,7 @@ public SimulationStep call() throws Exception { result = ProcessingResult.NoUpdate; } if(!simulationController.enqueue()) { - // the user called "runThisInstance" -- the work item has already been reenqued for the + // the user called "runThisInstance" -- the work item has already been re-enqueued for the // current time slice. addToBuffer = false; } @@ -189,7 +200,7 @@ public SimulationStep call() throws Exception { } keepProcessing = false; } - if(!simulationController.deleted() && addToBuffer) { + if(!simulationController.deleted() && addToBuffer && !(next.getProxyState() == ProxyState.Removed)) { buffer.add(next); } } diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/TwinExecutionEngine.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/TwinExecutionEngine.java index 0896623..4733898 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/TwinExecutionEngine.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/TwinExecutionEngine.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,24 +16,24 @@ package com.scaleoutsoftware.digitaltwin.development; import com.google.gson.Gson; -import com.scaleoutsoftware.digitaltwin.core.*; +import com.scaleoutsoftware.digitaltwin.abstractions.*; import java.io.Closeable; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; +import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; class TwinExecutionEngine implements Closeable { private List _modelNames; - private ConcurrentHashMap> _digitalTwins; - private ConcurrentHashMap _messageProcessors; - private ConcurrentHashMap _simulationProcessors; - private ConcurrentHashMap> _messageProcessorValueTypes; + private ConcurrentHashMap>> _digitalTwins; + private ConcurrentHashMap> _messageProcessors; + private ConcurrentHashMap> _simulationProcessors; private ConcurrentHashMap> _modelInstances; - private ConcurrentHashMap> _alertProviders; + private ConcurrentHashMap _alertProviders; private ConcurrentHashMap> _modelsSharedData; private HashMap _globalSharedData; private Workbench _workbench; @@ -48,34 +48,31 @@ class TwinExecutionEngine implements Closeable { } void init( ) { - _modelNames = new LinkedList<>(); - _digitalTwins = new ConcurrentHashMap<>(); - _messageProcessors = new ConcurrentHashMap<>(); - _simulationProcessors = new ConcurrentHashMap<>(); - _messageProcessorValueTypes = new ConcurrentHashMap<>(); - _modelInstances = new ConcurrentHashMap<>(); - _modelsSharedData = new ConcurrentHashMap<>(); - _globalSharedData = new HashMap<>(); - _alertProviders = new ConcurrentHashMap<>(); - _simulationSchedulers = new ConcurrentHashMap<>(); - _realTimeTimers = new ConcurrentHashMap<>(); - _gson = new Gson(); + _modelNames = new LinkedList<>(); + _digitalTwins = new ConcurrentHashMap<>(); + _messageProcessors = new ConcurrentHashMap<>(); + _simulationProcessors = new ConcurrentHashMap<>(); + _modelInstances = new ConcurrentHashMap<>(); + _modelsSharedData = new ConcurrentHashMap<>(); + _globalSharedData = new HashMap<>(); + _alertProviders = new ConcurrentHashMap<>(); + _simulationSchedulers = new ConcurrentHashMap<>(); + _realTimeTimers = new ConcurrentHashMap<>(); + _gson = new Gson(); } - void addDigitalTwin(String digitalTwinModelName, MessageProcessor digitalTwinMessageProcessor, Class dtType, Class messageClass) { + void addDigitalTwin(String digitalTwinModelName, MessageProcessor digitalTwinMessageProcessor, Class dtType) { _modelNames.add(digitalTwinModelName); _digitalTwins.put(digitalTwinModelName, dtType); _messageProcessors.put(digitalTwinModelName, digitalTwinMessageProcessor); - _messageProcessorValueTypes.put(digitalTwinModelName, messageClass); } - void addDigitalTwin(String digitalTwinModelName, MessageProcessor digitalTwinMessageProcessor, SimulationProcessor simulationProcessor, Class dtType, Class messageClass) { + void addDigitalTwin(String digitalTwinModelName, MessageProcessor digitalTwinMessageProcessor, SimulationProcessor simulationProcessor, Class dtType, int numWorkers) { _modelNames.add(digitalTwinModelName); _digitalTwins.put(digitalTwinModelName, dtType); _messageProcessors.put(digitalTwinModelName, digitalTwinMessageProcessor); _simulationProcessors.put(digitalTwinModelName, simulationProcessor); - _messageProcessorValueTypes.put(digitalTwinModelName, messageClass); - _simulationSchedulers.put(digitalTwinModelName, new SimulationScheduler(digitalTwinModelName, dtType, simulationProcessor, this)); + _simulationSchedulers.put(digitalTwinModelName, new SimulationScheduler(digitalTwinModelName, dtType, simulationProcessor, this, numWorkers)); } void addTimer(String modelName, String id, String timerName, TimerType type, Duration interval, TimerHandler handler) { @@ -110,10 +107,8 @@ void stopTimer(String modelName, String id, String timerName) { } } - void addAlertProvider(String modelName, AlertProviderConfiguration configuration) { - ConcurrentHashMap configMap = new ConcurrentHashMap<>(); - configMap.put(configuration.getName(), configuration); - _alertProviders.put(modelName, configMap); + void addAlertProvider(String modelName, String configuration) { + _alertProviders.put(modelName, configuration); } void updateTwin(String model, String id, TwinProxy proxy) { @@ -140,8 +135,8 @@ List runningModels() { return _modelNames; } - HashMap getTwinInstances(String model) { - HashMap ret = new HashMap<>(); + HashMap> getTwinInstances(String model) { + HashMap> ret = new HashMap<>(); ConcurrentHashMap instances = _modelInstances.get(model); if(instances!= null) { for(Map.Entry entry : instances.entrySet()) { @@ -151,8 +146,8 @@ HashMap getTwinInstances(String model) { return ret; } - DigitalTwinBase getTwinInstance(String model, String id) { - DigitalTwinBase ret = null; + DigitalTwinBase getTwinInstance(String model, String id) { + DigitalTwinBase ret = null; ConcurrentHashMap instances = _modelInstances.get(model); if(instances != null) { TwinProxy proxy = instances.get(id); @@ -173,35 +168,9 @@ TwinProxy getTwinProxy(String model, String id) { return proxy; } - String generateModelSchema(String model) throws WorkbenchException { - if(_digitalTwins.get(model) != null) { - ModelSchema schema; - if(_simulationProcessors.get(model) != null) { - schema = new ModelSchema( - _digitalTwins.get(model).getName(), - _messageProcessors.get(model).getClass().getName(), - _messageProcessorValueTypes.get(model).getName(), - _simulationProcessors.get(model).getClass().getName(), - List.copyOf(_alertProviders.get(model) == null ? Collections.emptyList() : _alertProviders.get(model).values())); - } else { - schema = new ModelSchema( - _digitalTwins.get(model).getName(), - _messageProcessors.get(model).getClass().getName(), - _messageProcessorValueTypes.get(model).getName(), - List.copyOf(_alertProviders.get(model) == null ? Collections.emptyList() : _alertProviders.get(model).values())); - } - - Gson gson = new Gson(); - String modelSchemaJson = gson.toJson(schema, ModelSchema.class); - return modelSchemaJson; - } else { - throw new WorkbenchException("Model has not been added to this workbench."); - } - } - - boolean hasAlertProviderConfiguration(String model, String alertProviderName) { + boolean hasAlertProviderConfiguration(String model) { if(hasModel(model)) { - return _alertProviders.containsKey(model) && _alertProviders.getOrDefault(model, new ConcurrentHashMap<>()).containsKey(alertProviderName); + return _alertProviders.containsKey(model); } else { return false; } @@ -212,30 +181,14 @@ boolean hasModel(String modelName) { return _modelNames.contains(modelName); } - SendingResult sendToSource(String source, String model, String id, String msg) throws WorkbenchException { + SendingResult sendToSource(String source, String model, String id, byte[] msg) throws WorkbenchException { if(_modelNames.contains(source)) { - String toSend = String.format("[%s]", msg); - run(source, id, null, toSend); + run(source, id, null, msg); return SendingResult.Handled; } else { ConcurrentHashMap> messagesByModel = _workbench.SOURCE_MESSAGES.getOrDefault(model, new ConcurrentHashMap<>()); List messages = messagesByModel.getOrDefault(id, new LinkedList<>()); - messages.add(msg); - messagesByModel.put(id, messages); - _workbench.SOURCE_MESSAGES.put(model, messagesByModel); - return SendingResult.Handled; - } - } - - SendingResult sendToSource(String source, String model, String id, List jsonSerializableMessage) throws WorkbenchException { - if(_modelNames.contains(source)) { - run(source, id, null, jsonSerializableMessage); - return SendingResult.Handled; - } else { - String msg = _gson.toJson(jsonSerializableMessage); - ConcurrentHashMap> messagesByModel = _workbench.SOURCE_MESSAGES.getOrDefault(model, new ConcurrentHashMap>()); - List messages = messagesByModel.getOrDefault(id, new LinkedList<>()); - messages.add(msg); + messages.add(new String(msg, StandardCharsets.UTF_8)); messagesByModel.put(id, messages); _workbench.SOURCE_MESSAGES.put(model, messagesByModel); return SendingResult.Handled; @@ -282,22 +235,22 @@ public void logMessage(String model, LogMessage message) { _workbench.LOGGED_MESSAGES.put(model, prev); } - public void recordAlertMessage(String model, String alertProvider, AlertMessage message) { + public void recordAlertMessage(String model, AlertMessage message) { ConcurrentHashMap> perModelMessages = _workbench.ALERT_MESSAGES.getOrDefault(model, new ConcurrentHashMap<>()); - ConcurrentLinkedQueue perApMessages = perModelMessages.getOrDefault(alertProvider, new ConcurrentLinkedQueue<>()); + ConcurrentLinkedQueue perApMessages = perModelMessages.getOrDefault(_alertProviders.get(model), new ConcurrentLinkedQueue<>()); perApMessages.add(message); - perModelMessages.put(alertProvider, perApMessages); + perModelMessages.put(_alertProviders.get(model), perApMessages); _workbench.ALERT_MESSAGES.put(model, perModelMessages); } public void createInstance(String modelName, String id, DigitalTwinBase instance) { - TwinProxy proxy = new TwinProxy(instance); + TwinProxy proxy = new TwinProxy(instance, new HashMap<>()); ConcurrentHashMap modelInstances = _modelInstances.get(modelName); if(modelInstances == null) { modelInstances = new ConcurrentHashMap<>(); } modelInstances.put(id, proxy); - InitContext initContext = new WorkbenchInitContext(this, instance, modelName, id); + InitContext initContext = new WorkbenchInitContext<>(this, proxy, modelName, id); instance.init(initContext); SimulationScheduler scheduler = _simulationSchedulers.get(modelName); if(scheduler != null) { @@ -315,7 +268,15 @@ public void deleteSimulationInstance(String modelName, String id) { _modelInstances.put(modelName, modelInstances); } - ProcessingResult run(String model, String id, String source, String serializedList) throws WorkbenchException { + public CompletableFuture deleteRealTimeInstance(String modelName, String id) { + ConcurrentHashMap modelInstances = _modelInstances.get(modelName); + TwinProxy proxy = modelInstances.remove(id); + proxy.setProxyState(ProxyState.Removed); + _modelInstances.put(modelName, modelInstances); + return CompletableFuture.completedFuture(DeleteResult.Success); + } + + ProcessingResult run(String model, String id, String source, byte[] message) throws WorkbenchException { try { ConcurrentHashMap twinInstances = _modelInstances.get(model); if(twinInstances == null) { @@ -329,62 +290,9 @@ ProcessingResult run(String model, String id, String source, String serializedLi throw new WorkbenchException(String.format("DigitalTwin model \"%s\" does not exist on this workbench.", model)); } instance = dtClazz.getConstructor().newInstance(); - InitContext initContext = new WorkbenchInitContext(this, instance, model, id); - instance.init(initContext); - proxy = new TwinProxy(instance); - SimulationScheduler scheduler = _simulationSchedulers.get(model); - if(scheduler != null) { - proxy.setProxyState(ProxyState.Active); - scheduler.addInstance(proxy); - } - } else { - instance = proxy.getInstance(); - } - MessageProcessor mp = _messageProcessors.get(model); - HashMap sharedData = _modelsSharedData.get(model); - if(sharedData == null) sharedData = new HashMap<>(); - _modelsSharedData.put(model, sharedData); - SimulationController simulationController = null; - SimulationScheduler scheduler = _simulationSchedulers.get(model); - if(scheduler != null) { - simulationController = new WorkbenchSimulationController(this, scheduler); - } - WorkbenchProcessingContext context = new WorkbenchProcessingContext(_workbench._twinExecutionEngine, sharedData, _globalSharedData, simulationController); - context.reset(model, id, source, instance); - ProcessingResult res = mp.processMessages(context, instance, new WorkbenchMessageListFactory(serializedList, _messageProcessorValueTypes.get(model))); - if(context.forceSave()) res = ProcessingResult.UpdateDigitalTwin; - switch(res) { - case UpdateDigitalTwin: - proxy.setInstance(instance); - twinInstances.put(id, proxy); - _modelInstances.put(model, twinInstances); - break; - case NoUpdate: - break; - default: - break; - } - return res; - } catch (Exception e) { - throw new WorkbenchException("Exception thrown while running message processor.", e); - } - } - - ProcessingResult run(String model, String id, String source, List messages) throws WorkbenchException { - try { - ConcurrentHashMap twinInstances = _modelInstances.get(model); - if(twinInstances == null) { - twinInstances = new ConcurrentHashMap<>(); - } - TwinProxy proxy = twinInstances.get(id); - DigitalTwinBase instance = null; - if(proxy == null) { - Class dtClazz = _digitalTwins.get(model); - if(dtClazz == null) return ProcessingResult.NoUpdate; - instance = dtClazz.getConstructor().newInstance(); - InitContext initContext = new WorkbenchInitContext(this, instance, model, id); + InitContext initContext = new WorkbenchInitContext(this, proxy, model, id); instance.init(initContext); - proxy = new TwinProxy(instance); + proxy = new TwinProxy(instance, new HashMap<>()); SimulationScheduler scheduler = _simulationSchedulers.get(model); if(scheduler != null) { proxy.setProxyState(ProxyState.Active); @@ -403,11 +311,12 @@ ProcessingResult run(String model, String id, String source, List messag simulationController = new WorkbenchSimulationController(this, scheduler); } WorkbenchProcessingContext context = new WorkbenchProcessingContext(_workbench._twinExecutionEngine, sharedData, _globalSharedData, simulationController); - context.reset(model, id, source, instance); + context.reset(model, id, source, proxy); if(simulationController != null) { simulationController.reset(model, id); } - ProcessingResult res = mp.processMessages(context, instance, new WorkbenchMessageListFactory(messages, _messageProcessorValueTypes.get(model))); + + ProcessingResult res = mp.processMessage(context, instance, message); if(context.forceSave()) res = ProcessingResult.UpdateDigitalTwin; switch(res) { case UpdateDigitalTwin: @@ -417,12 +326,15 @@ ProcessingResult run(String model, String id, String source, List messag break; case NoUpdate: break; + case Remove: + twinInstances.remove(id); + _modelInstances.put(model, twinInstances); default: break; } return res; } catch (Exception e) { - throw new WorkbenchException(e.getMessage(), e); + throw new WorkbenchException("Exception thrown while running message processor.", e); } } diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/TwinProxy.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/TwinProxy.java index 0839071..9d5cda4 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/TwinProxy.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/TwinProxy.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,14 +15,21 @@ */ package com.scaleoutsoftware.digitaltwin.development; -import com.scaleoutsoftware.digitaltwin.core.DigitalTwinBase; +import com.scaleoutsoftware.digitaltwin.abstractions.DigitalTwinBase; +import com.scaleoutsoftware.digitaltwin.abstractions.TimerMetadata; + +import java.util.HashMap; class TwinProxy { - private DigitalTwinBase _instance; - private ProxyState _state; - public TwinProxy(DigitalTwinBase instance) { - _instance = instance; - _state = ProxyState.Unspecified; + private DigitalTwinBase _instance; + private ProxyState _state; + private long _nextSimulationTime; + private HashMap> _timerHandlers; + public TwinProxy(DigitalTwinBase instance, HashMap> timerHandlers) { + _instance = instance; + _timerHandlers = timerHandlers; + _state = ProxyState.Unspecified; + _nextSimulationTime = 0L; } void setProxyState(ProxyState state) { @@ -41,6 +48,10 @@ public void setInstance(DigitalTwinBase instance) { _instance = instance; } + HashMap> getTimerHandlers() { + return _timerHandlers; + } + @Override public String toString() { return "TwinProxy{" + diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/Util.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/Util.java new file mode 100644 index 0000000..582c09a --- /dev/null +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/Util.java @@ -0,0 +1,47 @@ +/* + Copyright (c) 2026 by ScaleOut Software, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.scaleoutsoftware.digitaltwin.development; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +class Util { + public static List copyOf(Collection source) { + if (source == null) { + throw new NullPointerException("source"); + } + + List copy = new ArrayList<>(source.size()); + + for (T item : source) { + if (item == null) { + throw new NullPointerException("List contains null"); + } + copy.add(item); + } + + return Collections.unmodifiableList(copy); + } + + public static CompletableFuture failedFuture(Throwable t) { + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(t); + return future; + } +} diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/Workbench.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/Workbench.java index 3448b55..281efe1 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/Workbench.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/Workbench.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ */ package com.scaleoutsoftware.digitaltwin.development; -import com.scaleoutsoftware.digitaltwin.core.*; +import com.scaleoutsoftware.digitaltwin.abstractions.*; import java.io.*; import java.util.*; @@ -259,6 +259,7 @@ public class Workbench implements AutoCloseable { private long _now, _next; private SimulationStep _result = null; private boolean _simulationStarted = false; + private int _numWorkers = Runtime.getRuntime().availableProcessors(); /** @@ -268,6 +269,16 @@ public Workbench() { _twinExecutionEngine = new TwinExecutionEngine(this); } + /** + * Instantiate the workbench. + * @param numSimulationWorkers the number of simulation workers to use. Default is {@link Runtime#availableProcessors()}. + */ + public Workbench(int numSimulationWorkers) { + _twinExecutionEngine = new TwinExecutionEngine(this); + if(_numWorkers <= 0) throw new IllegalArgumentException("numSimulationWorkers must be greater-than 0."); + _numWorkers = numSimulationWorkers; + } + /** * Adds a real-time digital twin model to the workbench. @@ -275,20 +286,16 @@ public Workbench() { * @param modelName the name of the model. * @param digitalTwinMessageProcessor the model's {@link MessageProcessor} implementation. Must be marked as {@link Serializable}. * @param dtType the model's {@link DigitalTwinBase} implementation. - * @param messageClass the model's message type. * @param the type of the digital twin. - * @param the type of the message. * @throws WorkbenchException if any of the parameters are null or the model does not pass validation (the message * processor must be serializable, and the digital twin implementation must have a parameterless constructor). */ - public void addRealTimeModel(String modelName, MessageProcessor digitalTwinMessageProcessor, Class dtType, Class messageClass) throws WorkbenchException { - if(modelName == null || modelName.isBlank() || modelName.isEmpty() || digitalTwinMessageProcessor == null || dtType == null || messageClass == null) { - String errorMessage = String.format("modelName null: %b messageProcessor null: %b dtType null: %b messageType null: %b",modelName == null, digitalTwinMessageProcessor == null, dtType == null, messageClass == null); + public > void addRealTimeModel(String modelName, MessageProcessor digitalTwinMessageProcessor, Class dtType) throws WorkbenchException { + if(modelName == null || modelName.isEmpty() || digitalTwinMessageProcessor == null || dtType == null) { + String errorMessage = String.format("modelName null: %b messageProcessor null: %b dtType null: %b",modelName == null, digitalTwinMessageProcessor == null, dtType == null); throw new WorkbenchException(new IllegalArgumentException("All parameters required. Found null parameter.\n" + errorMessage)); } - - validate(digitalTwinMessageProcessor, dtType); - _twinExecutionEngine.addDigitalTwin(modelName, digitalTwinMessageProcessor, dtType, messageClass); + _twinExecutionEngine.addDigitalTwin(modelName, digitalTwinMessageProcessor, dtType); } /** @@ -298,20 +305,16 @@ public void addRealTimeModel(String modelName, Mes * @param digitalTwinMessageProcessor the model's {@link MessageProcessor} implementation. Must be marked as {@link Serializable}. * @param simulationProcessor the model's {@link SimulationProcessor} implementation. Must be marked as {@link Serializable}. * @param dtType the model's {@link DigitalTwinBase} implementation. - * @param messageClass the model's message type. * @param the type of the digital twin. - * @param the type of the message. * @throws WorkbenchException if any of the parameters are null or the model does not pass validation (the message * processor must be serializable, and the digital twin implementation must have a parameterless constructor). */ - public void addSimulationModel(String modelName, MessageProcessor digitalTwinMessageProcessor, SimulationProcessor simulationProcessor, Class dtType, Class messageClass) throws WorkbenchException { - if(modelName == null || modelName.isBlank() || modelName.isEmpty() || digitalTwinMessageProcessor == null || simulationProcessor == null || dtType == null || messageClass == null) { - String errorMessage = String.format("modelName null: %b messageProcessor null: %b simulationProcessor null: %b dtType null: %b messageType null: %b",modelName == null, digitalTwinMessageProcessor == null, simulationProcessor == null, dtType == null, messageClass == null); + public > void addSimulationModel(String modelName, MessageProcessor digitalTwinMessageProcessor, SimulationProcessor simulationProcessor, Class dtType) throws WorkbenchException { + if(modelName == null || modelName.isEmpty() || digitalTwinMessageProcessor == null || simulationProcessor == null || dtType == null) { + String errorMessage = String.format("modelName null: %b messageProcessor null: %b simulationProcessor null: %b dtType null: %b",modelName == null, digitalTwinMessageProcessor == null, simulationProcessor == null, dtType == null); throw new WorkbenchException(new IllegalArgumentException("All parameters required. Found null parameter.\n" + errorMessage)); } - - validate(digitalTwinMessageProcessor, dtType); - _twinExecutionEngine.addDigitalTwin(modelName, digitalTwinMessageProcessor, simulationProcessor, dtType, messageClass); + _twinExecutionEngine.addDigitalTwin(modelName, digitalTwinMessageProcessor, simulationProcessor, dtType, _numWorkers); } /** @@ -322,9 +325,10 @@ public void addSimulationModel(String modelName, M * @param modelName the instances model. * @param id the instance identifier. * @param instance the real-time or simulation instance. + * @param the type of the Digital Twin * @throws WorkbenchException If the model does not exist or if a simulation is already running. */ - public void addInstance(String modelName, String id, DigitalTwinBase instance) throws WorkbenchException { + public > void addInstance(String modelName, String id, V instance) throws WorkbenchException { if(_simulationStarted) throw new WorkbenchException("Cannot add new instance while simulation is active."); if(!_twinExecutionEngine.hasModel(modelName)) throw new WorkbenchException("The model does not exist on this workbench."); _twinExecutionEngine.createInstance(modelName, id, instance); @@ -336,13 +340,13 @@ public void addInstance(String modelName, String id, DigitalTwinBase instance) t * {@link Workbench#initializeSimulation(long, long, long)} has been called. * * @param modelName the instances model. - * @param configuration the alert provider configuration. + * @param alertProviderName the alert provider configuration. * @throws WorkbenchException If the model does not exist or if a simulation is already running. */ - public void addAlertProvider(String modelName, AlertProviderConfiguration configuration) throws WorkbenchException { + public void addAlertProvider(String modelName, String alertProviderName) throws WorkbenchException { if(_simulationStarted) throw new WorkbenchException("Cannot add new alert provider while simulation is active."); if(!_twinExecutionEngine.hasModel(modelName)) throw new WorkbenchException("The model does not exist on this workbench."); - _twinExecutionEngine.addAlertProvider(modelName, configuration); + _twinExecutionEngine.addAlertProvider(modelName, alertProviderName); } /** @@ -363,6 +367,8 @@ public SimulationStep runSimulation(long startTime, long endTime, double speedup SimulationStep ret, result = null; SimulationStepArgs args; long now, curTime, start, end, deltaTm, delta, wait, numItv; + args = new SimulationStepArgs(startTime, interval, WorkbenchSimulationFlags.Start); + _twinExecutionEngine.runSimulationStep(args); SimulationStatus status = SimulationStatus.Running; now = curTime = startTime; while(status == SimulationStatus.Running && @@ -374,7 +380,7 @@ public SimulationStep runSimulation(long startTime, long endTime, double speedup end = System.currentTimeMillis(); delta = result.getTime() - curTime; numItv = delta/interval; - numItv = delta%numItv != 0 ? numItv+1 : numItv; + numItv = numItv > 0 ? delta%numItv != 0 ? numItv+1 : numItv : numItv; deltaTm = end-start; wait = deltaTm >= interval ? 0L : (long)((interval-deltaTm)/speedup); status = result.getStatus(); @@ -437,7 +443,7 @@ public SimulationStep step() throws WorkbenchException { _result = _twinExecutionEngine.runSimulationStep(args); delta = _result.getTime() - _curTime; numItv = delta/_interval; - numItv = delta%numItv != 0 ? numItv+1 : numItv; + numItv = numItv > 0 ? delta%numItv != 0 ? numItv+1 : numItv : numItv+1; _curTime = _curTime+(numItv*_interval); _next = _curTime; return new SimulationStep(_result.getStatus(), _now); @@ -480,7 +486,7 @@ public Date peek() throws WorkbenchException { * @return the instances associated with the parameter model * @throws WorkbenchException if an exception occurs while retrieving digital twin instances for the parameter modelName */ - public HashMap getInstances(String modelName) throws WorkbenchException { + public HashMap> getInstances(String modelName) throws WorkbenchException { if(_twinExecutionEngine.getTwinInstances(modelName) == null) throw new WorkbenchException(String.format("No instances for model %s.", modelName)); return _twinExecutionEngine.getTwinInstances(modelName); @@ -531,73 +537,50 @@ public List getLoggedMessages(String model, long timestamp) { */ public List getAlertMessages(String model, String alertProvider) throws WorkbenchException { if(!_twinExecutionEngine.hasModel(model)) throw new WorkbenchException(String.format("No registered model with name %s found.", model)); - if(!_twinExecutionEngine.hasAlertProviderConfiguration(model, alertProvider)) throw new WorkbenchException(String.format("No alert provider configuration, registered for model %s, for %s found.", model, alertProvider)); + if(!_twinExecutionEngine.hasAlertProviderConfiguration(model)) throw new WorkbenchException(String.format("No alert provider configuration, registered for model %s, for %s found.", model, alertProvider)); ConcurrentHashMap> perModelMessages = ALERT_MESSAGES.getOrDefault(model, new ConcurrentHashMap<>()); return Arrays.asList(perModelMessages.get(alertProvider).toArray(new AlertMessage[0])); } /** - * Generates a ModelSchema for the defined model - * - * @param modelName the digital twin model's name to generate a schema. - * @return a JSON string of the model's schema - * @throws WorkbenchException if an exception occurs while generating a model schema. + * Retrieve the {@link SharedData} for a model. + * @param model the model name. + * @return the {@link SharedData} for a model. + * @throws WorkbenchException if an exception occurs while creating the working shared data. */ - public String generateModelSchema(String modelName) throws WorkbenchException { - if(_twinExecutionEngine.runningModels().contains(modelName)) { - return _twinExecutionEngine.generateModelSchema(modelName); + public SharedData getSharedModelData(String model) throws WorkbenchException { + if(_twinExecutionEngine.hasModel(model)) { + return new WorkbenchSharedData(_twinExecutionEngine.getModelData(model)); + } else { + throw new WorkbenchException("Workbench does not contain model " + model); } - throw new WorkbenchException("Model is not loaded; cannot generate model schema."); } /** - * Generates a ModelSchema for the parameter modelName and writes the schema to a file on the file system. If the - * parameter outputDirectory is null the file will be written to the working directory of the JVM. - * - * @param modelName the name of the digital twin model - * @param outputDirectory the directory to write the file to, or null to write the file to the current working directory. - * @return the full file path of the model.json schema file - * @throws WorkbenchException if an exception occurs while generating a model schema. + * Retrieve the global {@link SharedData}. + * @return the global {@link SharedData} of this workbench. + * @throws WorkbenchException if an exception occurs while creating the working shared data. */ - public String generateModelSchema(String modelName, String outputDirectory) throws WorkbenchException { - if(modelName == null || modelName.isEmpty()) { - throw new WorkbenchException("Required parameters: modelName.", new IllegalArgumentException()); - } - - outputDirectory = outputDirectory == null ? System.getProperty("user.dir") : outputDirectory; - - if(_twinExecutionEngine.runningModels().contains(modelName)) { - try { - String filePath = String.format("%s\\model.json", outputDirectory); - FileWriter fileWriter = new FileWriter(filePath); - fileWriter.write(_twinExecutionEngine.generateModelSchema(modelName)); - fileWriter.flush(); - fileWriter.close(); - return filePath; - } catch (IOException e) { - throw new WorkbenchException("Could not write file to file system.", e); - } - - } - throw new WorkbenchException("Model is not loaded; cannot generate model schema."); + public SharedData getSharedGlobalData() throws WorkbenchException { + return new WorkbenchSharedData(_twinExecutionEngine.getGlobalSharedData()); } /** * Send a list of messages to a real-time or simulation model. * @param modelName The model name. * @param id the instance id. - * @param messages the messages to send. + * @param message the message to send. * @return {@link SendingResult#Handled} unless an exception is thrown. - * @throws WorkbenchException if model name, id, or messages are null. Also thrown if the model's {@link MessageProcessor#processMessages(ProcessingContext, DigitalTwinBase, Iterable)} + * @throws WorkbenchException if model name, id, or messages are null. Also thrown if the model's {@link MessageProcessor#processMessage(ProcessingContext, DigitalTwinBase, byte[])} * throws an exception. */ - public SendingResult send(String modelName, String id, List messages) throws WorkbenchException { - if(modelName == null || id == null || messages == null) { + public SendingResult send(String modelName, String id, byte[] message) throws WorkbenchException { + if(modelName == null || id == null) { throw new WorkbenchException("ModelName, Id, and messages are required."); } if(_twinExecutionEngine.hasModel(modelName) && !_simulationStarted) { - _twinExecutionEngine.run(modelName, id, null, messages); + _twinExecutionEngine.run(modelName, id, null, message); } else { String msg = _twinExecutionEngine.hasModel(modelName) ? String.format("Cannot send message to %s. Simulation is active.", modelName) : String.format("Cannot send message to %s. Model does not exist.", modelName); throw new WorkbenchException(msg); @@ -605,110 +588,31 @@ public SendingResult send(String modelName, String id, List messages) th return SendingResult.Handled; } -// /** -// * Sends a single JSON serialized UTF-8 string message to a digital twin. -// * -// * @param modelName the name of the digital twin model -// * @param id the ID of the digital twin model -// * @param jsonSerializedMessage the serialized JSON UTF-8 string message -// * @return the sending result -// * @throws WorkbenchException if an exception is thrown by the twin or an error occurs while processing. -// */ -// public SendingResult send(String modelName, String id, String jsonSerializedMessage) throws WorkbenchException { -// List msgs = new ArrayList<>(); -// msgs.add(jsonSerializedMessage); -// return send(modelName, id, msgs); -// } -// -// /** -// * -// * @param modelName the name of the digital twin model -// * @param id the ID of the digital twin model -// * @param jsonSerializedMessages a serialized list of JSON UTF-8 string messages -// * @return the sending result -// * @throws WorkbenchException if an exception occurs while sending a message -// */ -// public SendingResult send(String modelName, String id, List jsonSerializedMessages) throws WorkbenchException { -// return sendMessage(modelName, id, jsonSerializedMessages) != null ? SendingResult.Handled : SendingResult.NotHandled; -// } - - ProcessingResult sendMessage(String model, String id, List jsonMessages) throws WorkbenchException { - if(_simulationStarted) throw new WorkbenchException("Cannot send message; simulation is active."); - StringBuilder serializedListBuilder = new StringBuilder(String.format("[%s", jsonMessages.remove(0))); - for(String msg : jsonMessages) { - serializedListBuilder.append(","); - serializedListBuilder.append(msg); - - } - serializedListBuilder.append("]"); - return _twinExecutionEngine.run(model, id, null, serializedListBuilder.toString()); + /** + * Add a key/value pair to {@link SharedData} for a model. + * @param modelName the model name. + * @param key the key. + * @param value the value. + */ + public void addSharedModelData(String modelName, String key, byte[] value) { + _twinExecutionEngine.getModelData(modelName).put(key, value); } + /** + * Add a key/value pair to the global {@link SharedData}. + * @param key the key. + * @param value the value. + */ + public void addGlobalModelData(String key, byte[] value) { + _twinExecutionEngine.getGlobalSharedData().put(key, value); + } - static void validate(MessageProcessor digitalTwinMessageProcessor, Class dtType) throws WorkbenchException { - WorkbenchException mee = null; - - ByteArrayOutputStream baos = null; - ObjectOutputStream oos = null; - - ByteArrayInputStream bais = null; - ObjectInputStream ois = null; - boolean serialized = false; - try { - // serialize MessageProcessor - baos = new ByteArrayOutputStream(); - oos = new ObjectOutputStream(baos); - oos.writeObject(digitalTwinMessageProcessor); - - byte[] serializedMP = baos.toByteArray(); - serialized = true; - - bais = new ByteArrayInputStream(serializedMP); - ois = new ObjectInputStream(bais); - MessageProcessor incoming = (MessageProcessor) ois.readObject(); - } catch (Exception all) { - if(serialized) - throw new WorkbenchException("Could not deserialize MessageProcessor instance.", all); - else - throw new WorkbenchException("Could not serialize MessageProcessor instance", all); - } finally { - try { - if(baos != null) { - baos.flush(); - baos.close(); - } - if(oos != null) { - oos.flush(); - oos.close(); - } - - if(bais != null) { - bais.close(); - } - if(ois != null) { - ois.close(); - } - } catch(Exception any) {} // best effort to cleanup - } - - try { - Class mpType = digitalTwinMessageProcessor.getClass(); - // instantiate TwinInstance - MessageProcessor instance = mpType.getConstructor().newInstance(); - } catch (Exception e) { - throw new WorkbenchException("Could not instantiate MessageProcessor instance. Default constructor required.", e); - } - + @Override + public void close() { try { - // instantiate TwinInstance - DigitalTwinBase instance = dtType.getConstructor().newInstance(); + _twinExecutionEngine.close(); } catch (Exception e) { - throw new WorkbenchException("Could not instantiate DigitalTwin instance. Default constructor required.", e); } - } - @Override - public void close() throws Exception { - _twinExecutionEngine.close(); } } diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchException.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchException.java index 27beb77..f529ac0 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchException.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchException.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2025 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ * A Workbench exception indicates that a real-time or simulated twin caused an exception. */ public class WorkbenchException extends Exception { - + private static final long serialVersionUID = 1L; /** * The string message for this workbench exception. */ diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchInitContext.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchInitContext.java index 29e6a48..be7a84e 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchInitContext.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchInitContext.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,26 +15,36 @@ */ package com.scaleoutsoftware.digitaltwin.development; -import com.scaleoutsoftware.digitaltwin.core.*; +import com.scaleoutsoftware.digitaltwin.abstractions.*; import java.time.Duration; -class WorkbenchInitContext extends InitContext { +class WorkbenchInitContext> extends InitContext { TwinExecutionEngine _twinExecutionEngine; - DigitalTwinBase _instance; + TwinProxy _proxy; String _model; String _id; - WorkbenchInitContext(TwinExecutionEngine twinExecutionEngine, DigitalTwinBase instance, String model, String id) { + WorkbenchInitContext(TwinExecutionEngine twinExecutionEngine, TwinProxy proxy, String model, String id) { _twinExecutionEngine = twinExecutionEngine; - _instance = instance; + _proxy = proxy; _model = model; _id = id; } @Override - public TimerActionResult startTimer(String timerName, Duration duration, TimerType timerType, TimerHandler timerHandler) { - return WorkbenchTimerService.startTimer(_twinExecutionEngine, (T)_instance, _model, _id, timerName, duration, timerType, timerHandler); + public TimerActionResult startTimer(String timerName, Duration duration, TimerType timerType, TimerHandler timerHandler, Class> aClass) { + return WorkbenchTimerService.startTimer(_twinExecutionEngine, _proxy, _model, _id, timerName, duration, timerType, timerHandler, aClass); + } + + @Override + public SharedData getSharedModelData() { + return new WorkbenchSharedData(_twinExecutionEngine.getModelData(_model)); + } + + @Override + public SharedData getSharedGlobalData() { + return new WorkbenchSharedData(_twinExecutionEngine.getGlobalSharedData()); } @Override diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchInitSimulationContext.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchInitSimulationContext.java new file mode 100644 index 0000000..4526ae3 --- /dev/null +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchInitSimulationContext.java @@ -0,0 +1,39 @@ +/* + Copyright (c) 2026 by ScaleOut Software, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.scaleoutsoftware.digitaltwin.development; + +import com.scaleoutsoftware.digitaltwin.abstractions.InitSimulationContext; +import com.scaleoutsoftware.digitaltwin.abstractions.SharedData; + +class WorkbenchInitSimulationContext implements InitSimulationContext { + SharedData _globalData; + SharedData _modelData; + + WorkbenchInitSimulationContext(SharedData globalData, SharedData modelData) { + _globalData = globalData; + _modelData = modelData; + } + + @Override + public SharedData getSharedModelData() { + return _modelData; + } + + @Override + public SharedData getSharedGlobalData() { + return _globalData; + } +} diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchMessageListFactory.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchMessageListFactory.java deleted file mode 100644 index 9bedd7d..0000000 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchMessageListFactory.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - Copyright (c) 2023 by ScaleOut Software, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.scaleoutsoftware.digitaltwin.development; - -import com.google.gson.Gson; -import com.scaleoutsoftware.digitaltwin.core.MessageFactory; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.List; - -class WorkbenchMessageListFactory implements MessageFactory { - private String _serializedJsonList; - private List _messages; - private Class _type; - - WorkbenchMessageListFactory(String serializedJson, Class type) { - _serializedJsonList = serializedJson; - _messages = null; - _type = type; - } - - WorkbenchMessageListFactory(List messages, Class type) { - _serializedJsonList = null; - _messages = messages; - _type = type; - } - - @Override - public Iterable getIncomingMessages() { - if (_messages != null) { - return (Iterable) _messages; - } else { - Gson gson = new Gson(); - return gson.fromJson(_serializedJsonList, getTypedList(_type)); - } - } - - private Type getTypedList(final Class paramClass) { - return new ParameterizedType() { - public Type[] getActualTypeArguments() { - return new Type[]{paramClass}; - } - - public Type getRawType() { - return List.class; - } - - public Type getOwnerType() { - return null; - } - }; - } -} diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchProcessingContext.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchProcessingContext.java index d4478d7..f542d0d 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchProcessingContext.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchProcessingContext.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2025 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,21 +15,19 @@ */ package com.scaleoutsoftware.digitaltwin.development; -import com.google.gson.Gson; +import com.scaleoutsoftware.digitaltwin.abstractions.*; -import com.scaleoutsoftware.digitaltwin.core.*; - -import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.logging.Level; -class WorkbenchProcessingContext extends ProcessingContext { +class WorkbenchProcessingContext> extends ProcessingContext { final TwinExecutionEngine _twinExecutionEngine; String _model; String _id; String _source; - DigitalTwinBase _twinInstance; + TwinProxy _proxy; SimulationController _controller; HashMap _modelData; HashMap _globalData; @@ -47,12 +45,14 @@ class WorkbenchProcessingContext extends ProcessingContext { _controller = controller; } - void reset(String model, String id, String source, DigitalTwinBase instance) { + void reset(String model, String id, String source, TwinProxy proxy) { _model = model; _id = id; - _twinInstance = instance; + _proxy = proxy; _forceSave = false; _source = source; + _modelData = _twinExecutionEngine.getModelData(model); + _globalData = _twinExecutionEngine.getGlobalSharedData(); } void reset(String model, String id, String source) { @@ -64,8 +64,8 @@ void reset(String model, String id, String source) { _globalData = _twinExecutionEngine.getGlobalSharedData(); } - void resetInstance(DigitalTwinBase instance) { - _twinInstance = instance; + void resetProxy(TwinProxy proxy) { + _proxy = proxy; } boolean forceSave() { @@ -73,100 +73,35 @@ boolean forceSave() { } @Override - public SendingResult sendToDataSource(byte[] bytes) { - try { - return _twinExecutionEngine.sendToSource(_source, _model, _id, new String(bytes, StandardCharsets.UTF_8)); - } catch (WorkbenchException e) { - return SendingResult.NotHandled; - } - } - - @Override - public SendingResult sendToDataSource(Object jsonSerializableMessage) { + public CompletableFuture sendToDataSource(byte[] message) { try { - List jsonSerializableMessages = new LinkedList<>(); - jsonSerializableMessages.add(jsonSerializableMessage); - return _twinExecutionEngine.sendToSource(_source, _model, _id, jsonSerializableMessages); + return CompletableFuture.completedFuture(_twinExecutionEngine.sendToSource(_source, _model, _id, message)); } catch (WorkbenchException e) { - return SendingResult.NotHandled; + return CompletableFuture.completedFuture(SendingResult.NotHandled); } } @Override - public SendingResult sendToDataSource(List list) { + public CompletableFuture sendToDigitalTwin(String model, String id, byte[] message) { try { - return _twinExecutionEngine.sendToSource(_source, _model, _id, list); + return CompletableFuture.completedFuture(_twinExecutionEngine.run(model, id,null, message) != null ? SendingResult.Handled : SendingResult.NotHandled); } catch (WorkbenchException e) { - return SendingResult.NotHandled; + return CompletableFuture.completedFuture(SendingResult.NotHandled); } } @Override - public SendingResult sendToDigitalTwin(String model, String id, byte[] bytes) { - List msgs = new LinkedList<>(); - msgs.add(bytes); - return sendToDigitalTwin(model, id, msgs); - } - - @Override - public SendingResult sendToDigitalTwin(String model, String id, Object jsonSerializableMessage) { - try { - if(_twinExecutionEngine.getTwinInstance(model, id) != null) { - List jsonSerializableMessages = new LinkedList<>(); - jsonSerializableMessages.add(jsonSerializableMessage); - return _twinExecutionEngine.run(model, id, null, jsonSerializableMessages) != null ? SendingResult.Handled : SendingResult.NotHandled; - } else { - return SendingResult.NotHandled; - } - } catch (WorkbenchException e) { - return SendingResult.NotHandled; - } - } - - @Override - public SendingResult sendToDigitalTwin(String model, String id, String msg) { - return sendToDigitalTwin(model, id, msg.getBytes(StandardCharsets.UTF_8)); - } - - @Override - public SendingResult sendToDigitalTwin(String model, String id, List list) { - if( (model == null || model.isEmpty()) || - (id == null || id.isEmpty()) || - (list == null || list.isEmpty())) { - return SendingResult.NotHandled; - } - Gson gson = new Gson(); - List msgs = new LinkedList<>(); - for(byte[] serialMsg : list) { - msgs.add(new String(serialMsg, StandardCharsets.UTF_8)); - } - String json = gson.toJson(msgs); - try { - if(_twinExecutionEngine.getTwinInstance(model, id) != null) { - return _twinExecutionEngine.run(model, id, null, json) != null ? SendingResult.Handled : SendingResult.NotHandled; - } else { - return SendingResult.NotHandled; - } - - } catch (WorkbenchException e) { - return SendingResult.NotHandled; - } - } - - @Override - public SendingResult sendAlert(String alertProviderName, AlertMessage alertMessage) { - if(alertProviderName.isBlank() || alertProviderName.isEmpty() || alertMessage == null) return SendingResult.NotHandled; - else if (!_twinExecutionEngine.hasAlertProviderConfiguration(_model, alertProviderName)) return SendingResult.NotHandled; + public CompletableFuture sendAlert(AlertMessage alertMessage) { + if(alertMessage == null) return Util.failedFuture(new IllegalArgumentException("Alert message was null.")); + else if (!_twinExecutionEngine.hasAlertProviderConfiguration(_model)) return Util.failedFuture(new IllegalStateException("No alert provider configuration available to model.")); else { - if(_twinExecutionEngine.hasAlertProviderConfiguration(_model, alertProviderName)) { - _twinExecutionEngine.recordAlertMessage(_model, alertProviderName, alertMessage); - } - return SendingResult.Handled; + _twinExecutionEngine.recordAlertMessage(_model, alertMessage); + return CompletableFuture.completedFuture(null); } } @Override - public PersistenceProvider getPersistenceProvider() { + public AzureDigitalTwinsProvider getAzureDigitalTwinsProvider() { return null; } @@ -181,13 +116,19 @@ public String getDigitalTwinModel() { } @Override - public void logMessage(Level level, String msg) { + public CompletableFuture logMessage(Level level, String msg) { _twinExecutionEngine.logMessage(_model, new LogMessage(level, msg)); + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture removeRealTimeTwin(String targetModelName, String targetInstanceId) { + return _twinExecutionEngine.deleteRealTimeInstance(targetModelName, targetInstanceId); } @Override - public TimerActionResult startTimer(String timerName, Duration interval, TimerType timerType, TimerHandler timerHandler) { - TimerActionResult ret = WorkbenchTimerService.startTimer(_twinExecutionEngine, (T)_twinInstance, _model, _id, timerName, interval, timerType, timerHandler); + public TimerActionResult startTimer(String timerName, Duration interval, TimerType timerType, TimerHandler timerHandler, Class> aClass) { + TimerActionResult ret = WorkbenchTimerService.startTimer(_twinExecutionEngine, _proxy, _model, _id, timerName, interval, timerType, timerHandler, aClass); if(ret != TimerActionResult.Success) { _forceSave = false; } else { @@ -198,7 +139,7 @@ public TimerActionResult startTimer(String timerName @Override public TimerActionResult stopTimer(String timerName) { - TimerActionResult ret = WorkbenchTimerService.stopTimer(_twinExecutionEngine, _twinInstance, _model, _id, timerName); + TimerActionResult ret = WorkbenchTimerService.stopTimer(_twinExecutionEngine, _proxy, _model, _id, timerName); if(ret != TimerActionResult.Success) { _forceSave = false; } else { diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchSharedData.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchSharedData.java index ffee258..a6ea640 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchSharedData.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchSharedData.java @@ -1,20 +1,36 @@ +/* + Copyright (c) 2026 by ScaleOut Software, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ package com.scaleoutsoftware.digitaltwin.development; -import com.scaleoutsoftware.digitaltwin.core.CacheOperationStatus; -import com.scaleoutsoftware.digitaltwin.core.CacheResult; -import com.scaleoutsoftware.digitaltwin.core.SharedData; +import com.scaleoutsoftware.digitaltwin.abstractions.CacheOperationStatus; +import com.scaleoutsoftware.digitaltwin.abstractions.CacheResult; +import com.scaleoutsoftware.digitaltwin.abstractions.SharedData; import java.util.HashMap; +import java.util.concurrent.CompletableFuture; -public class WorkbenchSharedData implements SharedData { +class WorkbenchSharedData implements SharedData { private final HashMap data; public WorkbenchSharedData(HashMap shared) { data = shared; } @Override - public CacheResult get(String s) { - return new CacheResult() { + public CompletableFuture get(String s) { + return CompletableFuture.completedFuture(new CacheResult() { @Override public String getKey() { return s; @@ -29,13 +45,14 @@ public byte[] getValue() { public CacheOperationStatus getStatus() { return data.containsKey(s) ? CacheOperationStatus.ObjectRetrieved : CacheOperationStatus.ObjectDoesNotExist; } - }; + }); + } @Override - public CacheResult put(String s, byte[] bytes) { + public CompletableFuture put(String s, byte[] bytes) { data.put(s, bytes); - return new CacheResult() { + return CompletableFuture.completedFuture(new CacheResult() { @Override public String getKey() { return s; @@ -50,13 +67,13 @@ public byte[] getValue() { public CacheOperationStatus getStatus() { return CacheOperationStatus.ObjectPut; } - }; + }); } @Override - public CacheResult remove(String s) { + public CompletableFuture remove(String s) { byte[] v = data.remove(s); - return new CacheResult() { + return CompletableFuture.completedFuture(new CacheResult() { @Override public String getKey() { return s; @@ -71,13 +88,13 @@ public byte[] getValue() { public CacheOperationStatus getStatus() { return v == null ? CacheOperationStatus.ObjectDoesNotExist : CacheOperationStatus.ObjectRemoved; } - }; + }); } @Override - public CacheResult clear() { + public CompletableFuture clear() { data.clear(); - return new CacheResult() { + return CompletableFuture.completedFuture(new CacheResult() { @Override public String getKey() { return null; @@ -92,6 +109,6 @@ public byte[] getValue() { public CacheOperationStatus getStatus() { return CacheOperationStatus.CacheCleared; } - }; + }); } } diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchSimulationController.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchSimulationController.java index a76a580..33e261d 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchSimulationController.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchSimulationController.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,12 +15,14 @@ */ package com.scaleoutsoftware.digitaltwin.development; -import com.scaleoutsoftware.digitaltwin.core.*; +import com.scaleoutsoftware.digitaltwin.abstractions.*; import java.nio.charset.StandardCharsets; import java.time.Duration; +import java.util.Date; import java.util.LinkedList; import java.util.List; +import java.util.concurrent.CompletableFuture; class WorkbenchSimulationController implements SimulationController { TwinExecutionEngine _engine; @@ -44,88 +46,55 @@ public Duration getSimulationTimeIncrement() { } @Override - public SendingResult delay(Duration duration) { - _requestedDelay = duration.toMillis(); - _delayRequested = true; - return SendingResult.Handled; + public Date getSimulationStartTime() { + return _scheduler.getSimulationStartTime(); } @Override - public SendingResult delayIndefinitely() { - _requestedDelay = 0x0000e677d21fdbffL; + public void delay(Duration duration) { + _requestedDelay = duration.toMillis(); _delayRequested = true; - return SendingResult.Handled; } @Override - public SendingResult emitTelemetry(String modelName, byte[] bytes) { - try { - _engine.run(modelName, _id, _modelName, String.format("[%s]", new String(bytes, StandardCharsets.UTF_8))); - return SendingResult.Handled; - } catch (WorkbenchException e) { - e.printStackTrace(); - return SendingResult.NotHandled; - } + public void delayIndefinitely() { + _requestedDelay = 0x0000e677d21fdbffL; + _delayRequested = true; } @Override - public SendingResult emitTelemetry(String modelName, Object jsonSerializableMessage) { + public CompletableFuture emitTelemetry(String modelName, byte[] message) { try { - if(_engine.hasModel(modelName)) { - List jsonSerializableMessages = new LinkedList<>(); - jsonSerializableMessages.add(jsonSerializableMessage); - _engine.run(modelName, _id, _modelName, jsonSerializableMessages); - return SendingResult.Handled; - } else { - return SendingResult.NotHandled; - } - + _engine.run(modelName, _id, _modelName, message); + return CompletableFuture.completedFuture(SendingResult.Handled); } catch (WorkbenchException e) { - e.printStackTrace(); - return SendingResult.NotHandled; + return CompletableFuture.completedFuture(SendingResult.NotHandled); } } @Override - public SendingResult createInstance(String modelName, String id, T instance) { + public > CompletableFuture createInstance(String modelName, String id, T instance) { try { _engine.createInstance(modelName, id, instance); - return SendingResult.Handled; + return CompletableFuture.completedFuture(CreateResult.Success); } catch (Exception e) { - e.printStackTrace(); - return SendingResult.NotHandled; - } - } - - @Override - public SendingResult createInstanceFromPersistenceStore(String modelName, String id) { - try { - throw new NoSuchMethodException("Not available on the workbench."); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - - @Override - public SendingResult createInstanceFromPersistenceStore(String modelName, String id, T defaultInstance) { - try { - throw new NoSuchMethodException("Not available on the workbench."); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(e); + return future; } } @Override - public SendingResult deleteInstance(String modelName, String id) { + public CompletableFuture deleteInstance(String modelName, String id) { _engine.deleteSimulationInstance(modelName, id); - return SendingResult.Handled; + return CompletableFuture.completedFuture(DeleteResult.Success); } @Override - public SendingResult deleteThisInstance() { + public CompletableFuture deleteThisInstance() { _engine.deleteSimulationInstance(_modelName, _id); _deleted = true; - return SendingResult.Handled; + return CompletableFuture.completedFuture(DeleteResult.Success); } @Override diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchSimulationFlags.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchSimulationFlags.java index 8910290..b8da409 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchSimulationFlags.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchSimulationFlags.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package com.scaleoutsoftware.digitaltwin.development; enum WorkbenchSimulationFlags { + // Start the simulation + Start, // Run the simulation Run, // Stop the simulation diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchTimerService.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchTimerService.java index 1c99c4f..8666fe7 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchTimerService.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchTimerService.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,31 +15,31 @@ */ package com.scaleoutsoftware.digitaltwin.development; -import com.scaleoutsoftware.digitaltwin.core.*; +import com.scaleoutsoftware.digitaltwin.abstractions.*; import java.time.Duration; class WorkbenchTimerService { - static TimerActionResult startTimer(TwinExecutionEngine twinExecutionEngine, T instance, String model, String id, String timerName, Duration interval, TimerType timerType, TimerHandler timerHandler) { - if(timerName == null || timerName.isBlank() || timerName.isEmpty() || interval == null || + static > TimerActionResult startTimer(TwinExecutionEngine twinExecutionEngine, TwinProxy proxy, String model, String id, String timerName, Duration interval, TimerType timerType, TimerHandler timerHandler, Class> handlerClass) { + if(timerName == null || timerName.isEmpty() || interval == null || interval.isZero() || interval.isNegative() || timerType == null || timerHandler == null) { String msg = String.format("Empty, blank, zero, or null parameter provided: timerName %s interval %s timerType %s timerHandler %s", timerName, interval, timerType, timerHandler); throw new IllegalArgumentException(msg); } - if (instance.TimerHandlers.size() >= Constants.MAX_TIMER_COUNT) // all timer slots are occupied + if (proxy.getTimerHandlers().size() >= Constants.MAX_TIMER_COUNT) // all timer slots are occupied return TimerActionResult.FailedTooManyTimers; - if(instance.TimerHandlers.containsKey(timerName)) return TimerActionResult.FailedTimerAlreadyExists; + if(proxy.getTimerHandlers().containsKey(timerName)) return TimerActionResult.FailedTimerAlreadyExists; int timerId = -1; boolean[] taken = new boolean[Constants.MAX_TIMER_COUNT]; // List of all timer Ids - for (TimerMetadata md : instance.TimerHandlers.values()) { - taken[md.getTimerId()] = true; + for (TimerMetadata md : proxy.getTimerHandlers().values()) { + taken[md.getTimerSlot()] = true; } for(int i = 0; i < taken.length; i++) { @@ -52,13 +52,13 @@ static TimerActionResult startTimer(TwinExecutionEng twinExecutionEngine.addTimer(model, id, timerName, timerType, interval, timerHandler); TimerMetadata metadata = new TimerMetadata<>(timerHandler, timerType, interval.toMillis(), timerId); - instance.TimerHandlers.put(timerName, metadata); + proxy.getTimerHandlers().put(timerName, metadata); return TimerActionResult.Success; } - static TimerActionResult stopTimer(TwinExecutionEngine twinExecutionEngine, T instance, String model, String id, String timerName) { + static > TimerActionResult stopTimer(TwinExecutionEngine twinExecutionEngine, TwinProxy proxy, String model, String id, String timerName) { twinExecutionEngine.stopTimer(model, id, timerName); - instance.TimerHandlers.remove(timerName); + proxy.getTimerHandlers().remove(timerName); return TimerActionResult.Success; } diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchTimerTask.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchTimerTask.java index def85a3..e725b63 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchTimerTask.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/WorkbenchTimerTask.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ */ package com.scaleoutsoftware.digitaltwin.development; -import com.scaleoutsoftware.digitaltwin.core.*; +import com.scaleoutsoftware.digitaltwin.abstractions.*; import java.time.Duration; import java.util.TimerTask; @@ -45,7 +45,7 @@ class WorkbenchTimerTask extends TimerTask { public void run() { DigitalTwinBase instance = _proxy.getInstance(); WorkbenchProcessingContext context = new WorkbenchProcessingContext(_engine, null); - context.reset(_modelName, _id, null, instance); + context.reset(_modelName, _id, null, _proxy); ProcessingResult result; synchronized (instance) { result = _handler.onTimedMessage(_timerName, instance, context); diff --git a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/package-info.java b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/package-info.java index 5738555..61ac790 100644 --- a/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/package-info.java +++ b/Development/src/main/java/com/scaleoutsoftware/digitaltwin/development/package-info.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Development/src/test/java/com/scaleoutsoftware/digitaltwin/development/TestWorkbench.java b/Development/src/test/java/com/scaleoutsoftware/digitaltwin/development/TestWorkbench.java index 702e5e3..c349f15 100644 --- a/Development/src/test/java/com/scaleoutsoftware/digitaltwin/development/TestWorkbench.java +++ b/Development/src/test/java/com/scaleoutsoftware/digitaltwin/development/TestWorkbench.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2023 by ScaleOut Software, Inc. + Copyright (c) 2026 by ScaleOut Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ package com.scaleoutsoftware.digitaltwin.development; import com.google.gson.Gson; -import com.scaleoutsoftware.digitaltwin.core.*; +import com.scaleoutsoftware.digitaltwin.abstractions.*; import org.junit.Assert; import org.junit.Test; @@ -24,11 +24,12 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.*; +import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; public class TestWorkbench { - public static class SimpleDigitalTwin extends DigitalTwinBase { + public static class SimpleDigitalTwin extends DigitalTwinBase { private String _stringProp; public SimpleDigitalTwin() {} public SimpleDigitalTwin(String stringProp) { @@ -46,15 +47,14 @@ public SimpleMessage(String s, int i) { } } - public static class SimpleMessageProcessor extends MessageProcessor implements Serializable { + public static class SimpleMessageProcessor extends MessageProcessor implements Serializable { public SimpleMessageProcessor() {} @Override - public ProcessingResult processMessages(ProcessingContext processingContext, SimpleDigitalTwin instance, Iterable messages) { + public ProcessingResult processMessage(ProcessingContext processingContext, SimpleDigitalTwin instance, byte[] message) throws ExecutionException, InterruptedException { Gson gson = new Gson(); Date currentTime = processingContext.getCurrentTime(); - PersistenceProvider provider = processingContext.getPersistenceProvider(); if(processingContext.getDigitalTwinModel().compareTo(instance.getModel()) != 0) { throw new IllegalStateException(String.format("context.getModel and instance.getModel difer. %s:%s", processingContext.getDigitalTwinModel(), instance.getModel())); } @@ -62,81 +62,74 @@ public ProcessingResult processMessages(ProcessingContext processingContext, Sim throw new IllegalStateException(String.format("context.getModel and instance.getModel difer. %s:%s", processingContext.getDigitalTwinModel(), instance.getModel())); } + SimpleMessage msg = gson.fromJson(new String(message, StandardCharsets.UTF_8), SimpleMessage.class); + if(instance.getModel().compareTo("SimSimple") == 0) {// if this is a simulation model... - for(SimpleMessage msg : messages) { - System.out.println(msg.stringChange); - } + System.out.println(msg.stringChange); } else { - for(SimpleMessage msg : messages) { - switch (msg.stringChange) { - case "SendToSource": - processingContext.sendToDataSource(new SimpleMessage("Hello from data source!", 10)); - break; - case "SendToTwin": - String jsonMsg = gson.toJson(msg); - byte[] bytes = jsonMsg.getBytes(StandardCharsets.UTF_8); - processingContext.sendToDigitalTwin(msg.payload == null ? msg.payload : "model", msg.intChange+"",bytes); - break; - case "LogMessage": - processingContext.logMessage(Level.INFO, msg.payload); - break; - case "StartTimer": - processingContext.startTimer("timer", Duration.ofMillis(1000), TimerType.Recurring, - new TimerHandler() { - @Override - public ProcessingResult onTimedMessage(String s, DigitalTwinBase digitalTwinBase, ProcessingContext processingContext) { - System.out.println("Hello from real-time timer."); - return ProcessingResult.UpdateDigitalTwin; - } - }); - break; - case "StopTimer": - processingContext.stopTimer("timer"); - break; - case "SharedData": - SharedData sharedData = processingContext.getSharedModelData(); - CacheResult result = sharedData.put("Hello", "Some string...".getBytes(StandardCharsets.UTF_8)); - if(result.getStatus() == CacheOperationStatus.ObjectPut) { - System.out.println("Successfully stored object in model storage."); - } - result = sharedData.get("Hello"); - if(result.getStatus() == CacheOperationStatus.ObjectRetrieved) { - System.out.println("Successfully retrieved " + new String(result.getValue(), StandardCharsets.UTF_8) + " from model storage."); - } - result = sharedData.remove("Hello"); - if(result.getStatus() == CacheOperationStatus.ObjectRemoved) { - System.out.println("Successfully removed " + new String(result.getValue(), StandardCharsets.UTF_8) + " from model storage."); - } - sharedData = processingContext.getSharedGlobalData(); - result = sharedData.put("Hello", "Some string...".getBytes(StandardCharsets.UTF_8)); - if(result.getStatus() == CacheOperationStatus.ObjectPut) { - System.out.println("Successfully stored object in global storage."); - } - result = sharedData.get("Hello"); - if(result.getStatus() == CacheOperationStatus.ObjectRetrieved) { - System.out.println("Successfully retrieved " + new String(result.getValue(), StandardCharsets.UTF_8) + " from global storage."); - } - result = sharedData.remove("Hello"); - if(result.getStatus() == CacheOperationStatus.ObjectRemoved) { - System.out.println("Successfully removed " + new String(result.getValue(), StandardCharsets.UTF_8) + " from global storage."); - } - break; - case "WakeUp": - SimulationController controller = processingContext.getSimulationController(); - instance._stringProp = "WakeUp"; - System.out.println("Calling run this twin..."); - controller.runThisInstance(); - break; - default: - break; - } + switch (msg.stringChange) { + case "SendToSource": + processingContext.sendToDataSource(gson.toJson(new SimpleMessage("Hello from data source!", 10)).getBytes(StandardCharsets.UTF_8)); + break; + case "SendToTwin": + String jsonMsg = gson.toJson(msg); + byte[] bytes = jsonMsg.getBytes(StandardCharsets.UTF_8); + processingContext.sendToDigitalTwin(msg.payload == null ? msg.payload : "model", msg.intChange+"",bytes); + break; + case "LogMessage": + processingContext.logMessage(Level.INFO, msg.payload); + break; + case "StartTimer": + processingContext.startTimer("timer", Duration.ofMillis(1000), TimerType.Recurring, new SimpleTimer(), SimpleTimer.class); + break; + case "StopTimer": + processingContext.stopTimer("timer"); + break; + case "SharedData": + SharedData sharedData = processingContext.getSharedModelData(); + CacheResult result = sharedData.put("Hello", "Some string...".getBytes(StandardCharsets.UTF_8)).get(); + if(result.getStatus() == CacheOperationStatus.ObjectPut) { + System.out.println("Successfully stored object in model storage."); + } + result = sharedData.get("Hello").get(); + if(result.getStatus() == CacheOperationStatus.ObjectRetrieved) { + System.out.println("Successfully retrieved " + new String(result.getValue(), StandardCharsets.UTF_8) + " from model storage."); + } + result = sharedData.remove("Hello").get(); + if(result.getStatus() == CacheOperationStatus.ObjectRemoved) { + System.out.println("Successfully removed " + new String(result.getValue(), StandardCharsets.UTF_8) + " from model storage."); + } + result = sharedData.put("modelTest", "assert".getBytes(StandardCharsets.UTF_8)).get(); + sharedData = processingContext.getSharedGlobalData(); + result = sharedData.put("Hello", "Some string...".getBytes(StandardCharsets.UTF_8)).get(); + if(result.getStatus() == CacheOperationStatus.ObjectPut) { + System.out.println("Successfully stored object in global storage."); + } + result = sharedData.get("Hello").get(); + if(result.getStatus() == CacheOperationStatus.ObjectRetrieved) { + System.out.println("Successfully retrieved " + new String(result.getValue(), StandardCharsets.UTF_8) + " from global storage."); + } + result = sharedData.remove("Hello").get(); + if(result.getStatus() == CacheOperationStatus.ObjectRemoved) { + System.out.println("Successfully removed " + new String(result.getValue(), StandardCharsets.UTF_8) + " from global storage."); + } + result = sharedData.put("globalTest", "assert".getBytes(StandardCharsets.UTF_8)).get(); + break; + case "WakeUp": + SimulationController controller = processingContext.getSimulationController(); + instance._stringProp = "WakeUp"; + System.out.println("Calling run this twin..."); + controller.runThisInstance(); + break; + default: + break; } } return ProcessingResult.UpdateDigitalTwin; } } - public static class RealTimeCar extends DigitalTwinBase { + public static class RealTimeCar extends DigitalTwinBase { private int _tirePressure; public RealTimeCar() { _tirePressure=0; } public RealTimeCar(int startingTirePressure) { @@ -163,22 +156,22 @@ public int getPressureChange() { } } - public static class RealTimeCarMessageProcessor extends MessageProcessor implements Serializable { + public static class RealTimeCarMessageProcessor extends MessageProcessor implements Serializable { final int TIRE_PRESSURE_FULL = 100; @Override - public ProcessingResult processMessages(ProcessingContext processingContext, RealTimeCar car, Iterable messages) throws Exception { + public ProcessingResult processMessage(ProcessingContext processingContext, RealTimeCar car, byte[] message) throws Exception { + Gson gson = new Gson(); + TirePressureMessage msg = gson.fromJson(new String(message, StandardCharsets.UTF_8), TirePressureMessage.class); // apply the updates from the messages - for(TirePressureMessage message : messages) { - car.incrementTirePressure(message.getPressureChange()); - } + car.incrementTirePressure(msg.getPressureChange()); if(car.getTirePressure() > TIRE_PRESSURE_FULL) { - processingContext.sendToDataSource(new TirePressureMessage(car.getTirePressure())); + processingContext.sendToDataSource(gson.toJson(new TirePressureMessage(car.getTirePressure())).getBytes(StandardCharsets.UTF_8)); } return ProcessingResult.UpdateDigitalTwin; } } - public static class SimulationPump extends DigitalTwinBase { + public static class SimulationPump extends DigitalTwinBase { private double _tirePressureChange; private boolean _tirePressureReached = false; public SimulationPump() {} @@ -199,9 +192,9 @@ public boolean isTireFull() { } } - public static class SimulatedPumpMessageProcessor extends MessageProcessor implements Serializable { + public static class SimulatedPumpMessageProcessor extends MessageProcessor implements Serializable { @Override - public ProcessingResult processMessages(ProcessingContext processingContext, SimulationPump simCar, Iterable messages) throws Exception { + public ProcessingResult processMessage(ProcessingContext processingContext, SimulationPump simCar, byte[] message) throws Exception { // apply the updates from the messages simCar.setTirePressureReached(); return ProcessingResult.UpdateDigitalTwin; @@ -216,25 +209,31 @@ public ProcessingResult processModel(ProcessingContext processingContext, Simula controller.deleteThisInstance(); } else { int change = (int) (100 * simPump.getTirePressureChange()); - controller.emitTelemetry("RealTimeCar", new TirePressureMessage(change)); + Gson gson = new Gson(); + controller.emitTelemetry("RealTimeCar", gson.toJson(new TirePressureMessage(change)).getBytes(StandardCharsets.UTF_8)); } return ProcessingResult.UpdateDigitalTwin; } } + public static class SimpleTimer implements TimerHandler { + + @Override + public ProcessingResult onTimedMessage(String s, SimpleDigitalTwin simpleDigitalTwin, ProcessingContext processingContext) { + System.out.println("timer called!"); + return ProcessingResult.UpdateDigitalTwin; + } + } + public static class SimpleSimProcessor extends SimulationProcessor implements Serializable { private Gson _gson = new Gson(); private AtomicInteger timesInvoked = new AtomicInteger(0); - private boolean _useJson; private String _modelIdToMessage; private String _instanceIdToMessage; - public SimpleSimProcessor(boolean json) { - _useJson = json; - } + public SimpleSimProcessor() {} public SimpleSimProcessor(String modelIdToMessage, String instanceIdToMessage) { - _useJson = false; _modelIdToMessage = modelIdToMessage; _instanceIdToMessage = instanceIdToMessage; } @@ -244,7 +243,7 @@ public int getTimesInvoked() { } @Override - public ProcessingResult processModel(ProcessingContext processingContext, SimpleDigitalTwin simpleDigitalTwin, Date date) { + public ProcessingResult processModel(ProcessingContext processingContext, SimpleDigitalTwin simpleDigitalTwin, Date date) { timesInvoked.addAndGet(1); SimulationController controller = processingContext.getSimulationController(); if(simpleDigitalTwin.getId().compareTo("stop") == 0) { @@ -254,13 +253,8 @@ public ProcessingResult processModel(ProcessingContext processingContext, Simple controller.delay(Duration.ofSeconds(600)); return ProcessingResult.UpdateDigitalTwin; } else if (simpleDigitalTwin.getId().contains("timer")) { - processingContext.startTimer("timer", Duration.ofMillis(1000), TimerType.OneTime, new TimerHandler<>() { - @Override - public ProcessingResult onTimedMessage(String s, DigitalTwinBase digitalTwinBase, ProcessingContext processingContext) { - System.out.println("timer called!"); - return ProcessingResult.UpdateDigitalTwin; - } - }); + Class timerHandlerClass = SimpleTimer.class; + TimerActionResult timer = processingContext.startTimer("timer", Duration.ofMillis(1000), TimerType.OneTime, new SimpleTimer(), timerHandlerClass); return ProcessingResult.UpdateDigitalTwin; } else if (simpleDigitalTwin.getId().contains("log")) { processingContext.logMessage(Level.INFO, simpleDigitalTwin._stringProp); @@ -268,7 +262,7 @@ public ProcessingResult onTimedMessage(String s, DigitalTwinBase digitalTwinBase processingContext.logMessage(Level.SEVERE, simpleDigitalTwin._stringProp); return ProcessingResult.UpdateDigitalTwin; } else if (simpleDigitalTwin.getId().contains("alert")) { - processingContext.sendAlert("alert", new AlertMessage(simpleDigitalTwin.getId(), simpleDigitalTwin.getId(), simpleDigitalTwin._stringProp)); + processingContext.sendAlert(new AlertMessage(simpleDigitalTwin.getId(), simpleDigitalTwin.getId(), simpleDigitalTwin._stringProp)); return ProcessingResult.UpdateDigitalTwin; } else if (simpleDigitalTwin.getId().contains("sleeper")) { if(simpleDigitalTwin._stringProp.compareTo("WakeUp") == 0) { @@ -280,17 +274,24 @@ public ProcessingResult onTimedMessage(String s, DigitalTwinBase digitalTwinBase return ProcessingResult.UpdateDigitalTwin; } else if (simpleDigitalTwin.getId().contains("waker")) { System.out.println("Waking up sleeper..."); - processingContext.sendToDigitalTwin(_modelIdToMessage, _instanceIdToMessage, new SimpleMessage("WakeUp", 23)); + Gson gson = new Gson(); + processingContext.sendToDigitalTwin(_modelIdToMessage, _instanceIdToMessage, gson.toJson(new SimpleMessage("WakeUp", 23)).getBytes(StandardCharsets.UTF_8)); + return ProcessingResult.UpdateDigitalTwin; + } else if (simpleDigitalTwin.getId().compareTo("initSimulation") == 0) { return ProcessingResult.UpdateDigitalTwin; } long delay = Long.parseLong(simpleDigitalTwin.getId()); controller.delay(Duration.ofSeconds(delay)); - if(_useJson) { - byte[] msg = _gson.toJson(new SimpleMessage("SendToSource", 23)).getBytes(StandardCharsets.UTF_8); - controller.emitTelemetry("Simple", msg); - } else { - SimpleMessage telemetry = new SimpleMessage("SendToSource", 23); - controller.emitTelemetry("Simple", telemetry); + SimpleMessage telemetry = new SimpleMessage("SendToSource", 23); + Gson gson = new Gson(); + controller.emitTelemetry("Simple", gson.toJson(telemetry).getBytes(StandardCharsets.UTF_8)); + return ProcessingResult.UpdateDigitalTwin; + } + + @Override + public ProcessingResult onInitSimulation(InitSimulationContext ctx, SimpleDigitalTwin simpleDigitalTwin, Date date) { + if(simpleDigitalTwin.getId().compareTo("initSimulation") == 0) { + timesInvoked.set(1000); } return ProcessingResult.UpdateDigitalTwin; } @@ -301,8 +302,8 @@ public void TestWorkbenchNoInstances() throws WorkbenchException { Gson gson = new Gson(); SimulationStep result; try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class, SimpleMessage.class); - workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), new SimpleSimProcessor(false), SimpleDigitalTwin.class, SimpleMessage.class); + workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class); + workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), new SimpleSimProcessor(), SimpleDigitalTwin.class); result = workbench.runSimulation(System.currentTimeMillis(), System.currentTimeMillis() + 60000, 1, 1000); Assert.assertSame(SimulationStatus.NoRemainingWork, result.getStatus()); @@ -313,14 +314,14 @@ public void TestWorkbenchNoInstances() throws WorkbenchException { @Test public void TestWorkbenchOnlySimulationInstances() throws WorkbenchException { - SimpleSimProcessor processor = new SimpleSimProcessor(false); + SimpleSimProcessor processor = new SimpleSimProcessor(); Gson gson = new Gson(); SimulationStep result; try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class, SimpleMessage.class); - workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class, SimpleMessage.class); + workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class); + workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class); - for (int i = 0; i < 1; i++) { + for (int i = 1; i < 2; i++) { DigitalTwinBase instance = new SimpleDigitalTwin("hello" + i); workbench.addInstance("SimSimple", "" + i, instance); } @@ -340,8 +341,8 @@ public void TestWorkbenchSample() throws WorkbenchException { long expectedStop; SimulationStep step; try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("RealTimeCar", new RealTimeCarMessageProcessor(), RealTimeCar.class, TirePressureMessage.class); - workbench.addSimulationModel("SimPump", new SimulatedPumpMessageProcessor(), new PumpSimulationProcessor(), SimulationPump.class, TirePressureMessage.class); + workbench.addRealTimeModel("RealTimeCar", new RealTimeCarMessageProcessor(), RealTimeCar.class); + workbench.addSimulationModel("SimPump", new SimulatedPumpMessageProcessor(), new PumpSimulationProcessor(), SimulationPump.class); workbench.addInstance("SimPump", "23", new SimulationPump(0.29d)); long start = System.currentTimeMillis(); @@ -351,7 +352,7 @@ public void TestWorkbenchSample() throws WorkbenchException { while (step.getStatus() == SimulationStatus.Running) { step = workbench.step(); - HashMap realTimeCars = workbench.getInstances("RealTimeCar"); + HashMap> realTimeCars = workbench.getInstances("RealTimeCar"); RealTimeCar rtCar = (RealTimeCar) realTimeCars.get("23"); System.out.println("rtCar: " + rtCar.getTirePressure()); } @@ -367,8 +368,8 @@ public void TestWorkbenchSampleRun() throws WorkbenchException { long expectedStop; SimulationStep step; try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("RealTimeCar", new RealTimeCarMessageProcessor(), RealTimeCar.class, TirePressureMessage.class); - workbench.addSimulationModel("SimModel", new SimulatedPumpMessageProcessor(), new PumpSimulationProcessor(), SimulationPump.class, TirePressureMessage.class); + workbench.addRealTimeModel("RealTimeCar", new RealTimeCarMessageProcessor(), RealTimeCar.class); + workbench.addSimulationModel("SimModel", new SimulatedPumpMessageProcessor(), new PumpSimulationProcessor(), SimulationPump.class); workbench.addInstance("SimModel", "23", new SimulationPump(0.29d)); long start = System.currentTimeMillis(); @@ -384,12 +385,12 @@ public void TestWorkbenchSampleRun() throws WorkbenchException { @Test public void TestWorkbenchSpeedup() throws WorkbenchException { - SimpleSimProcessor processor = new SimpleSimProcessor(false); + SimpleSimProcessor processor = new SimpleSimProcessor(); Gson gson = new Gson(); SimulationStep result; try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class, SimpleMessage.class); - workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class, SimpleMessage.class); + workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class); + workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class); for (int i = 0; i < 1000; i++) { DigitalTwinBase instance = new SimpleDigitalTwin("hello" + i); @@ -397,9 +398,12 @@ public void TestWorkbenchSpeedup() throws WorkbenchException { } long start = System.currentTimeMillis(); - result = workbench.runSimulation(System.currentTimeMillis(), System.currentTimeMillis() + 60000, 100, 1000); + long stop = start + 60000; + result = workbench.runSimulation(System.currentTimeMillis(), stop, 100, 1000); Assert.assertSame(SimulationStatus.EndTimeReached, result.getStatus()); - Assert.assertEquals(1308, processor.getTimesInvoked()); + + // each id (0-999) delays for it's id in seconds + Assert.assertEquals(1249, processor.getTimesInvoked()); } catch (Exception e) { throw new RuntimeException(e); } @@ -411,12 +415,12 @@ public void TestWorkbenchDebug() throws WorkbenchException{ int numInstance = 10; int numItterations = 0; for(int i = 0; i < 20; i ++) { - SimpleSimProcessor processor = new SimpleSimProcessor(i >= 10); + SimpleSimProcessor processor = new SimpleSimProcessor(); SimulationStep result; long start; try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class, SimpleMessage.class); - workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class, SimpleMessage.class); + workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class); + workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class); for (int twinCount = 0; twinCount < 1000; twinCount++) { DigitalTwinBase instance = new SimpleDigitalTwin("hello" + twinCount); @@ -433,7 +437,8 @@ public void TestWorkbenchDebug() throws WorkbenchException{ long stop = System.currentTimeMillis(); System.out.println("RunTime: " + (stop-start)); Assert.assertSame(SimulationStatus.EndTimeReached, result.getStatus()); - Assert.assertEquals(1308, processor.getTimesInvoked()); + // each id (0-999) delays for it's id in seconds, processor.getTimesInvoked()); + Assert.assertEquals(1249, processor.getTimesInvoked()); } catch (Exception e) { throw new RuntimeException(e); } @@ -443,18 +448,17 @@ public void TestWorkbenchDebug() throws WorkbenchException{ @Test public void testWorkbenchDebugOnlySimulationInstances() throws WorkbenchException { - SimpleSimProcessor processor = new SimpleSimProcessor(false); + SimpleSimProcessor processor = new SimpleSimProcessor(); Gson gson = new Gson(); long stopTimeMs; SimulationStep step; try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class, SimpleMessage.class); - workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class, SimpleMessage.class); + workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class); + workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class); - for (int i = 0; i < 1; i++) { - DigitalTwinBase instance = new SimpleDigitalTwin("hello" + i); - workbench.addInstance("SimSimple", "" + i, instance); - } + + DigitalTwinBase instance = new SimpleDigitalTwin("hello" + 1); + workbench.addInstance("SimSimple", "" + 1, instance); long startTimeMs = System.currentTimeMillis(); stopTimeMs = startTimeMs + 60000; @@ -478,12 +482,12 @@ public void TestWorkbenchStop() throws WorkbenchException{ Gson gson = new Gson(); int numInstance = 10; int numItterations = 0; - SimpleSimProcessor processor = new SimpleSimProcessor(false); + SimpleSimProcessor processor = new SimpleSimProcessor(); SimulationStep result; long start; try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class, SimpleMessage.class); - workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class, SimpleMessage.class); + workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class); + workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class); for (int twinCount = 0; twinCount < 1000; twinCount++) { DigitalTwinBase instance = new SimpleDigitalTwin("hello" + twinCount); @@ -511,12 +515,12 @@ public void TestWorkbenchHugeDelays() throws WorkbenchException{ Gson gson = new Gson(); int numInstance = 10; int numItterations = 0; - SimpleSimProcessor processor = new SimpleSimProcessor(false); + SimpleSimProcessor processor = new SimpleSimProcessor(); SimulationStep result; long start; try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class, SimpleMessage.class); - workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class, SimpleMessage.class); + workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class); + workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class); for (int twinCount = 0; twinCount < 1000; twinCount++) { DigitalTwinBase instance = new SimpleDigitalTwin("delay" + twinCount); @@ -543,12 +547,12 @@ public void TestWorkbenchTimer() throws WorkbenchException{ Gson gson = new Gson(); int numInstance = 10; int numItterations = 0; - SimpleSimProcessor processor = new SimpleSimProcessor(false); + SimpleSimProcessor processor = new SimpleSimProcessor(); SimulationStep result; long start; try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class, SimpleMessage.class); - workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class, SimpleMessage.class); + workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class); + workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class); for (int twinCount = 0; twinCount < 1; twinCount++) { DigitalTwinBase instance = new SimpleDigitalTwin("timer" + twinCount); @@ -575,23 +579,20 @@ public void TestWorkbenchRealtimeTimer() throws WorkbenchException, InterruptedE Gson gson = new Gson(); int numInstance = 10; int numItterations = 0; - SimpleSimProcessor processor = new SimpleSimProcessor(false); + SimpleSimProcessor processor = new SimpleSimProcessor(); try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class, SimpleMessage.class); + workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class); for (int twinCount = 0; twinCount < 1; twinCount++) { DigitalTwinBase instance = new SimpleDigitalTwin("timer" + twinCount); workbench.addInstance("Simple", "timer" + twinCount, instance); } - - List messages = new LinkedList<>(); - messages.add(new SimpleMessage("StartTimer", 0)); - workbench.send("Simple", "timer0", messages); + byte[] message = gson.toJson(new SimpleMessage("StartTimer", 0)).getBytes(StandardCharsets.UTF_8); + workbench.send("Simple", "timer0", message); Thread.sleep(3000); - messages = new LinkedList<>(); - messages.add(new SimpleMessage("StopTimer", 0)); - workbench.send("Simple", "timer0", messages); + message = gson.toJson(new SimpleMessage("StopTimer", 0)).getBytes(StandardCharsets.UTF_8); + workbench.send("Simple", "timer0", message); Thread.sleep(2000); } catch (Exception e) { throw new RuntimeException(e); @@ -604,18 +605,16 @@ public void TestWorkbenchRealtimeTimerScope() throws WorkbenchException, Interru Gson gson = new Gson(); int numInstance = 10; int numItterations = 0; - SimpleSimProcessor processor = new SimpleSimProcessor(false); + SimpleSimProcessor processor = new SimpleSimProcessor(); try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class, SimpleMessage.class); + workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class); for(int twinCount = 0; twinCount < 1; twinCount++) { DigitalTwinBase instance = new SimpleDigitalTwin("timer"+twinCount); workbench.addInstance("Simple", "timer" + twinCount, instance); } - - List messages = new LinkedList<>(); - messages.add(new SimpleMessage("StartTimer", 0)); - workbench.send("Simple", "timer0", messages); + byte[] message = gson.toJson(new SimpleMessage("StartTimer", 0)).getBytes(StandardCharsets.UTF_8); + workbench.send("Simple", "timer0", message); Thread.sleep(3000); } catch (Exception e) { Assert.fail(); @@ -625,7 +624,7 @@ public void TestWorkbenchRealtimeTimerScope() throws WorkbenchException, Interru @Test public void TestWorkbenchSimulationLogMessage() throws WorkbenchException { - SimpleSimProcessor processor = new SimpleSimProcessor(false); + SimpleSimProcessor processor = new SimpleSimProcessor(); Gson gson = new Gson(); String logMessageContent; long exp; @@ -633,8 +632,8 @@ public void TestWorkbenchSimulationLogMessage() throws WorkbenchException { long stop; List messages; try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class, SimpleMessage.class); - workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class, SimpleMessage.class); + workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class); + workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class); logMessageContent = "this is a log message"; for (int i = 0; i < 1; i++) { @@ -685,13 +684,13 @@ public void TestWorkbenchSimulationLogMessage() throws WorkbenchException { @Test public void TestWorkbenchSimulationAlertMessage() throws WorkbenchException { - SimpleSimProcessor processor = new SimpleSimProcessor(false); + SimpleSimProcessor processor = new SimpleSimProcessor(); Gson gson = new Gson(); SimulationStep result; try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class, SimpleMessage.class); - workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class, SimpleMessage.class); - workbench.addAlertProvider("SimSimple", new AlertProviderConfiguration("test", "www.url.com", "integrationkey", "routingKey", "alert", "entityId")); + workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class); + workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class); + workbench.addAlertProvider("SimSimple", "test"); String alertMessageContent = "this is an alert message"; String id = "alert"; @@ -706,7 +705,7 @@ public void TestWorkbenchSimulationAlertMessage() throws WorkbenchException { Assert.assertSame(SimulationStatus.EndTimeReached, result.getStatus()); Assert.assertEquals(stop, result.getTime()); Assert.assertEquals(exp, processor.getTimesInvoked()); - List messages = workbench.getAlertMessages("SimSimple", "alert"); + List messages = workbench.getAlertMessages("SimSimple", "test"); Assert.assertEquals(messages.size(), exp); for(AlertMessage msg : messages) { Assert.assertSame(msg.getMessage(), alertMessageContent); @@ -722,7 +721,7 @@ public void TestWorkbenchSimulationAlertMessage() throws WorkbenchException { @Test(expected = WorkbenchException.class) public void TestWorkbenchException() throws Exception { try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("Simple", null, SimpleDigitalTwin.class, SimpleMessage.class); + workbench.addRealTimeModel("Simple", null, SimpleDigitalTwin.class); } catch (Exception e) { throw e; } @@ -730,17 +729,16 @@ public void TestWorkbenchException() throws Exception { @Test public void TestWorkbenchPeek() throws Exception { - SimpleSimProcessor processor = new SimpleSimProcessor(false); + SimpleSimProcessor processor = new SimpleSimProcessor(); long stopTimeMs; SimulationStep step; try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class, SimpleMessage.class); - workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class, SimpleMessage.class); + workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class); + workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class); - for (int i = 0; i < 1; i++) { - DigitalTwinBase instance = new SimpleDigitalTwin("hello" + i); - workbench.addInstance("SimSimple", "" + i, instance); - } + + DigitalTwinBase instance = new SimpleDigitalTwin("hello" + 1); + workbench.addInstance("SimSimple", "" + 1, instance); long startTimeMs = System.currentTimeMillis(); stopTimeMs = startTimeMs + 60000; @@ -764,56 +762,65 @@ public void TestWorkbenchPeek() throws Exception { } @Test - public void TestWorkbenchGenerateModelSchema() throws Exception { - SimpleSimProcessor processor = new SimpleSimProcessor(false); - try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class, SimpleMessage.class); - workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class, SimpleMessage.class); - String schemaAsJson = workbench.generateModelSchema("Simple"); - Assert.assertEquals(schemaAsJson, "{\"modelType\":\"com.scaleoutsoftware.digitaltwin.development.TestWorkbench$SimpleDigitalTwin\",\"messageProcessorType\":\"com.scaleoutsoftware.digitaltwin.development.TestWorkbench$SimpleMessageProcessor\",\"messageType\":\"com.scaleoutsoftware.digitaltwin.development.TestWorkbench$SimpleMessage\",\"assemblyName\":\"NOT_USED_BY_JAVA_MODELS\",\"enablePersistence\":false,\"enableSimulationSupport\":false,\"alertProviders\":[]}"); - String dir = workbench.generateModelSchema("SimSimple", System.getProperty("user.dir")); - Assert.assertEquals(String.format("%s\\model.json", System.getProperty("user.dir")), dir); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Test (expected = WorkbenchException.class) - public void TestWorkbenchGenerateModelSchemaExceptionally() throws Exception { - SimpleSimProcessor processor = new SimpleSimProcessor(false); + public void TestWorkbenchSharedData() throws Exception { + Gson gson = new Gson(); try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class, SimpleMessage.class); - workbench.addSimulationModel("SimSimple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class, SimpleMessage.class); - String schemaAsJson = workbench.generateModelSchema(""); + workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class); + byte[] message = gson.toJson(new SimpleMessage("SharedData", 29)).getBytes(StandardCharsets.UTF_8); + workbench.send("Simple", "23", message); + CacheResult result = workbench.getSharedModelData("Simple").get("modelTest").get(); + Assert.assertEquals(CacheOperationStatus.ObjectRetrieved, result.getStatus()); + Assert.assertEquals("modelTest", result.getKey()); + Assert.assertEquals("assert", new String(result.getValue(), StandardCharsets.UTF_8)); + result = workbench.getSharedGlobalData().get("globalTest").get(); + Assert.assertEquals(CacheOperationStatus.ObjectRetrieved, result.getStatus()); + Assert.assertEquals("globalTest", result.getKey()); + Assert.assertEquals("assert", new String(result.getValue(), StandardCharsets.UTF_8)); } catch (Exception e) { throw e; } } @Test - public void TestWorkbenchSharedData() throws Exception { + public void TestWorkbenchRunThisInstance() throws Exception { try (Workbench workbench = new Workbench()) { - workbench.addRealTimeModel("Simple", new SimpleMessageProcessor(), SimpleDigitalTwin.class, SimpleMessage.class); - LinkedList messages = new LinkedList<>(); - messages.add(new SimpleMessage("SharedData", 29)); - workbench.send("Simple", "23", messages); + workbench.addSimulationModel("Simple", new SimpleMessageProcessor(), new SimpleSimProcessor("Simple2", "sleeper"), SimpleDigitalTwin.class); + workbench.addSimulationModel("Simple2", new SimpleMessageProcessor(), new SimpleSimProcessor(), SimpleDigitalTwin.class); + + workbench.addInstance("Simple", "waker", new SimpleDigitalTwin("waker")); + workbench.addInstance("Simple2", "sleeper", new SimpleDigitalTwin("sleeper")); + long startTimeMs = System.currentTimeMillis(); + long stopTimeMs = startTimeMs + 15000L; + long step = 1000L; + workbench.runSimulation(startTimeMs, stopTimeMs, 1, step); + SimpleDigitalTwin waker = (SimpleDigitalTwin) workbench.getInstances("Simple").get("waker"); + SimpleDigitalTwin sleeper = (SimpleDigitalTwin) workbench.getInstances("Simple2").get("sleeper"); + Assert.assertNotNull(waker); + Assert.assertNotNull(sleeper); + Assert.assertEquals("waker", waker._stringProp); + Assert.assertEquals("asleep", sleeper._stringProp); } catch (Exception e) { throw e; } } @Test - public void TestWorkbenchRunThisInstance() throws Exception { + public void TestWorkbenchInitSimulation() throws Exception { try (Workbench workbench = new Workbench()) { - workbench.addSimulationModel("Simple", new SimpleMessageProcessor(), new SimpleSimProcessor("Simple2", "sleeper"), SimpleDigitalTwin.class, SimpleMessage.class); - workbench.addSimulationModel("Simple2", new SimpleMessageProcessor(), new SimpleSimProcessor(false), SimpleDigitalTwin.class, SimpleMessage.class); + SimpleSimProcessor processor = new SimpleSimProcessor(); + workbench.addSimulationModel("Simple", new SimpleMessageProcessor(), processor, SimpleDigitalTwin.class); - workbench.addInstance("Simple", "waker", new SimpleDigitalTwin("waker")); - workbench.addInstance("Simple2", "sleeper", new SimpleDigitalTwin("sleeper")); + workbench.addInstance("Simple", "initSimulation", new SimpleDigitalTwin("waker")); + + long simRuntimeMs = 15000L; long startTimeMs = System.currentTimeMillis(); - long stopTimeMs = startTimeMs + 15000L; + long stopTimeMs = startTimeMs + simRuntimeMs; + int speedup = 1; long step = 1000L; - workbench.runSimulation(startTimeMs, stopTimeMs, 1, step); + long expectedInvokes = simRuntimeMs/1000L; + int initSimExpectedInvokes = 1000; + workbench.runSimulation(startTimeMs, stopTimeMs, speedup, step); + Assert.assertEquals(expectedInvokes + initSimExpectedInvokes, processor.timesInvoked.get()); } catch (Exception e) { throw e; } diff --git a/README.md b/README.md index d64df82..778cce5 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ This repository contains the open source (Apache 2.0 licensed) projects for the following artifacts: -- [com.scaleout.digitaltwin.core](https://repo.scaleoutsoftware.com/#artifact/com.scaleoutsoftware.digitaltwin/core): Core datatypes required for building Digital Twin models. -- [com.scaleout.digitaltwin.development](https://repo.scaleoutsoftware.com/#artifact/com.scaleoutsoftware.digitaltwin/development): A lightweight [development/testing framework](https://static.scaleoutsoftware.com/docs/digital_twin_user_guide/software_toolkit/dt_builder/java_api/workbench.html) for running Digital Twin models. +- [com.scaleoutsoftware.digitaltwin:digitaltwin-abstractions](https://mvnrepository.com/artifact/com.scaleoutsoftware.digitaltwin/digitaltwin-abstractions/3.0.0): Abstract datatypes required for building Digital Twin models. +- [com.scaleoutsoftware.digitaltwin:digitaltwin-development](https://mvnrepository.com/artifact/com.scaleoutsoftware.digitaltwin/digitaltwin-development/3.0.0): A lightweight [development/testing framework](https://static.scaleoutsoftware.com/docs/digital_twin_user_guide/software_toolkit/dt_builder/java_api/workbench.html) for running Digital Twin models. ## Documentation - [Class reference](https://scaleoutsoftware.github.io/JavaDigitalTwinCore/) diff --git a/docs/allclasses-index.html b/docs/allclasses-index.html index bddcce0..86c7271 100644 --- a/docs/allclasses-index.html +++ b/docs/allclasses-index.html @@ -1,25 +1,21 @@ - -All Classes and Interfaces (digitaltwin-core-docs 3.0.4 API) + +All Classes and Interfaces (DigitalTwinAPI 3.0.0 API) - + + - - + -