This is an automated email from the ASF dual-hosted git repository.

riemer pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to refs/heads/dev by this push:
     new 03de78e2e4 feat: Extend capabilities of machine data simulator (#4013)
03de78e2e4 is described below

commit 03de78e2e4a14bfe4ca1f7771482bb0adde9c430
Author: Dominik Riemer <[email protected]>
AuthorDate: Wed Dec 3 09:39:09 2025 +0100

    feat: Extend capabilities of machine data simulator (#4013)
---
 .../go-client-e2e/adapter/machine.json             |  25 +-
 .../iiot/IIoTAdaptersExtensionModuleExport.java    |   5 +-
 .../simulator/machine/MachineDataSimulator.java    | 121 ++-----
 .../machine/MachineDataSimulatorAdapter.java       |  23 +-
 .../machine/MachineDataSimulatorUtils.java         | 157 ++-------
 .../machine/event/DiagnosticSimulator.java         | 371 +++++++++++++++++++++
 .../simulator/machine/event/EventSimulator.java    |  31 ++
 .../FlowSimulator.java}                            | 102 ++----
 .../simulator/machine/event/PressureSimulator.java |  80 +++++
 .../machine/event/SensorValueSimulator.java        |  26 ++
 .../machine/event/WaterlevelSimulator.java         |  88 +++++
 .../migration/MachineDataSimulatorMigrationV1.java |  56 ++++
 .../documentation.md                               |  32 --
 .../icon.png                                       | Bin 12881 -> 0 bytes
 .../strings.en                                     |  44 ---
 .../documentation.md                               |  34 --
 .../icon.png                                       | Bin 31009 -> 0 bytes
 .../strings.en                                     |  26 --
 .../strings.en                                     |   5 +-
 19 files changed, 771 insertions(+), 455 deletions(-)

diff --git a/streampipes-client-e2e/go-client-e2e/adapter/machine.json 
b/streampipes-client-e2e/go-client-e2e/adapter/machine.json
index d50a60be01..e532bd837e 100644
--- a/streampipes-client-e2e/go-client-e2e/adapter/machine.json
+++ b/streampipes-client-e2e/go-client-e2e/adapter/machine.json
@@ -39,6 +39,23 @@
       "value": "1000",
       "valueSpecification": null
     },
+    {
+      "@class": 
"org.apache.streampipes.model.staticproperty.FreeTextStaticProperty",
+      "description": "The number of sensors to simulate",
+      "internalName": "numberOfSensors",
+      "label": "Number of sensors",
+      "optional": false,
+      "staticPropertyType": "FreeTextStaticProperty",
+      "htmlAllowed": false,
+      "htmlFontFormat": false,
+      "mapsTo": null,
+      "multiLine": false,
+      "placeholdersSupported": false,
+      "requiredDatatype": "http://www.w3.org/2001/XMLSchema#integer";,
+      "requiredDomainProperty": null,
+      "value": "1",
+      "valueSpecification": null
+    },
     {
       "@class": 
"org.apache.streampipes.model.staticproperty.OneOfStaticProperty",
       "description": "Select simulated sensor data to be published",
@@ -65,6 +82,12 @@
           "internalName": null,
           "name": "waterlevel",
           "selected": false
+        },
+        {
+          "elementId": "sp:option:VWXqSG",
+          "internalName": null,
+          "name": "diagnostics",
+          "selected": false
         }
       ]
     }
@@ -222,4 +245,4 @@
   "selectedEndpointUrl": null,
   "streamRules": [],
   "valueRules": []
-}
\ No newline at end of file
+}
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/IIoTAdaptersExtensionModuleExport.java
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/IIoTAdaptersExtensionModuleExport.java
index 5c9dbf874c..4fbdc74ead 100644
--- 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/IIoTAdaptersExtensionModuleExport.java
+++ 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/IIoTAdaptersExtensionModuleExport.java
@@ -21,6 +21,7 @@ package org.apache.streampipes.connect.iiot;
 import org.apache.streampipes.connect.iiot.adapters.oi4.Oi4Adapter;
 import 
org.apache.streampipes.connect.iiot.adapters.oi4.migration.Oi4AdapterMigrationV1;
 import 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.MachineDataSimulatorAdapter;
+import 
org.apache.streampipes.connect.iiot.migration.MachineDataSimulatorMigrationV1;
 import org.apache.streampipes.connect.iiot.protocol.stream.FileReplayAdapter;
 import org.apache.streampipes.connect.iiot.protocol.stream.HttpServerProtocol;
 import org.apache.streampipes.connect.iiot.protocol.stream.HttpStreamProtocol;
@@ -51,6 +52,8 @@ public class IIoTAdaptersExtensionModuleExport implements 
IExtensionModuleExport
 
   @Override
   public List<IModelMigrator<?, ?>> migrators() {
-    return List.of(new Oi4AdapterMigrationV1());
+    return List.of(
+        new Oi4AdapterMigrationV1(),
+        new MachineDataSimulatorMigrationV1());
   }
 }
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulator.java
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulator.java
index ce2a914349..59d200efd8 100644
--- 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulator.java
+++ 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulator.java
@@ -17,14 +17,12 @@
  */
 package org.apache.streampipes.connect.iiot.adapters.simulator.machine;
 
-import org.apache.streampipes.commons.exceptions.connect.AdapterException;
+import 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.event.EventSimulator;
 import org.apache.streampipes.extensions.api.connect.IEventCollector;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.HashMap;
-import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
 public class MachineDataSimulator implements Runnable {
@@ -32,19 +30,22 @@ public class MachineDataSimulator implements Runnable {
   private final IEventCollector collector;
 
   private final Integer waitTimeMs;
-  private final String selectedSimulatorOption;
+  private final Integer numberOfSensors;
+  private final EventSimulator simulator;
 
   private Boolean running;
 
   private static final Logger LOG = 
LoggerFactory.getLogger(MachineDataSimulator.class);
 
-  public MachineDataSimulator(IEventCollector collector,
+  public MachineDataSimulator(EventSimulator simulator,
+                              IEventCollector collector,
                               Integer waitTimeMs,
-                              String selectedSimulatorOption) {
+                              int numberOfSensors) {
+    this.simulator = simulator;
     this.collector = collector;
     this.waitTimeMs = waitTimeMs;
-    this.selectedSimulatorOption = selectedSimulatorOption;
     this.running = true;
+    this.numberOfSensors = numberOfSensors;
   }
 
   @Override
@@ -53,108 +54,42 @@ public class MachineDataSimulator implements Runnable {
     long startTimeMs = System.currentTimeMillis();
 
     while (running) {
-      Map<String, Object> event = new HashMap<>();
       long currentTimeMs = System.currentTimeMillis();
       long timeDeltaMs = currentTimeMs - startTimeMs;
 
-      switch (this.selectedSimulatorOption) {
-        case "flowrate":
-          // 0 - 30s
-          if (timeDeltaMs > 0 && timeDeltaMs <= 30000) {
-            event = buildFlowrateEvent(0);
-          } else if (timeDeltaMs > 30000 && timeDeltaMs <= 60000) {
-            // 30s - 60s
-            event = buildFlowrateEvent(1);
-          } else {
-            // > 60s
-            // reset start time to start over again
-            startTimeMs = currentTimeMs;
-          }
-          break;
-        case "pressure":
-          // 0 - 30s
-          if (timeDeltaMs > 0 && timeDeltaMs <= 30000) {
-            event = buildPressureEvent(0);
-          } else if (timeDeltaMs > 30000 && timeDeltaMs <= 60000) {
-            // 30s - 60s
-            event = buildPressureEvent(1);
-          } else {
-            // > 60s
-            // reset start time to start over again
-            startTimeMs = currentTimeMs;
-          }
-          break;
-        case "waterlevel":
-          if (timeDeltaMs > 0 && timeDeltaMs <= 30000) {
-            // 0 - 30s
-            event = buildWaterlevelEvent(0);
-          } else if (timeDeltaMs > 30000 && timeDeltaMs <= 60000) {
-            // 30s - 60s
-            event = buildWaterlevelEvent(1);
-          } else {
-            // > 60s
-            // reset start time to start over again
-            startTimeMs = currentTimeMs;
-          }
-          break;
-        default:
-          try {
-            throw new AdapterException("resource not found");
-          } catch (AdapterException e) {
-            throw new RuntimeException(e);
-          }
+      Integer simulationPhase = null;
+
+      if (timeDeltaMs > 0 && timeDeltaMs <= 30000) {
+        simulationPhase = 0;
+      } else if (timeDeltaMs > 30000 && timeDeltaMs <= 60000) {
+        simulationPhase = 1;
+      } else {
+        startTimeMs = currentTimeMs; // reset
       }
 
-      if (!event.keySet().isEmpty()) {
-        collector.collect(event);
+      if (simulationPhase != null) {
+        long timestamp = System.currentTimeMillis();
+
+        for (int sensorIndex = 1; sensorIndex <= numberOfSensors; 
sensorIndex++) {
+          var event = simulator.buildEvent(simulationPhase, sensorIndex, 
timestamp);
+
+          if (!event.isEmpty()) {
+            collector.collect(event);
+          }
+        }
       }
 
       try {
         TimeUnit.MILLISECONDS.sleep(waitTimeMs);
       } catch (InterruptedException e) {
         LOG.error("Machine simulator thread interrupted", e);
+        Thread.currentThread().interrupt();
+        return;
       }
     }
   }
 
-  private Map<String, Object> buildFlowrateEvent(int simulationPhase) {
-    Map<String, Object> event = new HashMap<>();
-
-    event.put("timestamp", System.currentTimeMillis());
-    event.put("sensorId", "flowrate02");
-    event.put("mass_flow", randomDoubleBetween(0, 10));
-    event.put("volume_flow", randomDoubleBetween(0, 10));
-    event.put("temperature", simulationPhase == 0 ? randomDoubleBetween(40, 
50) : randomDoubleBetween(80, 100));
-    event.put("density", randomDoubleBetween(40, 50));
-    event.put("sensor_fault_flags", simulationPhase != 0);
-
-    return event;
-  }
-
-  private Map<String, Object> buildPressureEvent(int simulationPhase) {
-    Map<String, Object> event = new HashMap<>();
-
-    event.put("timestamp", System.currentTimeMillis());
-    event.put("sensorId", "pressure01");
-    event.put("pressure", simulationPhase == 0 ? randomDoubleBetween(10, 40) : 
randomDoubleBetween(40, 70));
-
-    return event;
-  }
-
-  private Map<String, Object> buildWaterlevelEvent(int simulationPhase) {
-    Map<String, Object> event = new HashMap<>();
-
-    event.put("timestamp", System.currentTimeMillis());
-    event.put("sensorId", "level01");
-    event.put("level", simulationPhase == 0 ? randomDoubleBetween(20, 30) : 
randomDoubleBetween(60, 80));
-    event.put("overflow", simulationPhase != 0);
 
-    return event;
-  }
-
-  private double randomDoubleBetween(int min, int max) {
-    return Math.random() * (max - min + 1) + min;
-  }
 
   public void setRunning(Boolean running) {
     this.running = running;
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulatorAdapter.java
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulatorAdapter.java
index fe737e31e6..5440d19e86 100644
--- 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulatorAdapter.java
+++ 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulatorAdapter.java
@@ -25,7 +25,6 @@ import 
org.apache.streampipes.extensions.api.connect.StreamPipesAdapter;
 import 
org.apache.streampipes.extensions.api.connect.context.IAdapterGuessSchemaContext;
 import 
org.apache.streampipes.extensions.api.connect.context.IAdapterRuntimeContext;
 import 
org.apache.streampipes.extensions.api.extractor.IAdapterParameterExtractor;
-import org.apache.streampipes.model.AdapterType;
 import org.apache.streampipes.model.connect.guess.GuessSchema;
 import org.apache.streampipes.model.extensions.ExtensionAssetType;
 import org.apache.streampipes.sdk.builder.adapter.AdapterConfigurationBuilder;
@@ -35,41 +34,45 @@ import org.apache.streampipes.sdk.helpers.Options;
 
 public class MachineDataSimulatorAdapter implements StreamPipesAdapter {
 
-  private static final String ID = 
"org.apache.streampipes.connect.iiot.adapters.simulator.machine";
+  public static final String ID = 
"org.apache.streampipes.connect.iiot.adapters.simulator.machine";
   private static final String WAIT_TIME_MS = "wait-time-ms";
   private static final String SELECTED_SIMULATOR_OPTION = 
"selected-simulator-option";
+  public static final String NUMBER_OF_SENSORS = "numberOfSensors";
 
   private MachineDataSimulator machineDataSimulator;
 
   @Override
   public IAdapterConfiguration declareConfig() {
-    return AdapterConfigurationBuilder.create(ID, 0, 
MachineDataSimulatorAdapter::new)
+    return AdapterConfigurationBuilder.create(ID, 1, 
MachineDataSimulatorAdapter::new)
         .withAssets(ExtensionAssetType.DOCUMENTATION, ExtensionAssetType.ICON)
         .withLocales(Locales.EN)
-        .withCategory(AdapterType.Debugging)
         .requiredIntegerParameter(Labels.withId(WAIT_TIME_MS), 1000)
+        .requiredIntegerParameter(Labels.withId(NUMBER_OF_SENSORS), 1)
         
.requiredSingleValueSelection(Labels.withId(SELECTED_SIMULATOR_OPTION), 
Options.from(
-            "flowrate", "pressure", "waterlevel"))
+            "flowrate", "pressure", "waterlevel", "diagnostics"))
         .buildConfiguration();
   }
 
   @Override
   public void onAdapterStarted(IAdapterParameterExtractor extractor,
                                IEventCollector collector,
-                               IAdapterRuntimeContext adapterRuntimeContext)
-      throws AdapterException {
+                               IAdapterRuntimeContext adapterRuntimeContext) 
throws AdapterException {
     var ex = extractor.getStaticPropertyExtractor();
 
     var waitTimeMs = ex.singleValueParameter(WAIT_TIME_MS, Integer.class);
+    var numberOfSensors = ex.singleValueParameter(NUMBER_OF_SENSORS, 
Integer.class);
     var selectedSimulatorOption = 
ex.selectedSingleValue(SELECTED_SIMULATOR_OPTION, String.class);
-    this.machineDataSimulator = new MachineDataSimulator(collector, 
waitTimeMs, selectedSimulatorOption);
+    var simulator = 
MachineDataSimulatorUtils.getSimulator(selectedSimulatorOption);
+    this.machineDataSimulator = new MachineDataSimulator(
+        simulator, collector, waitTimeMs, numberOfSensors
+    );
     Thread thread = new Thread(this.machineDataSimulator);
     thread.start();
   }
 
   @Override
   public void onAdapterStopped(IAdapterParameterExtractor extractor,
-                               IAdapterRuntimeContext adapterRuntimeContext) 
throws AdapterException {
+                               IAdapterRuntimeContext adapterRuntimeContext) {
     this.machineDataSimulator.setRunning(false);
   }
 
@@ -79,6 +82,6 @@ public class MachineDataSimulatorAdapter implements 
StreamPipesAdapter {
                                        IAdapterGuessSchemaContext 
adapterGuessSchemaContext) throws AdapterException {
     var ex = extractor.getStaticPropertyExtractor();
     var selectedSimulatorOption = 
ex.selectedSingleValue(SELECTED_SIMULATOR_OPTION, String.class);
-    return MachineDataSimulatorUtils.getSchema(selectedSimulatorOption);
+    return 
MachineDataSimulatorUtils.getSimulator(selectedSimulatorOption).getSchema();
   }
 }
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulatorUtils.java
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulatorUtils.java
index 1a3a27865d..c795164102 100644
--- 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulatorUtils.java
+++ 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulatorUtils.java
@@ -18,16 +18,12 @@
 package org.apache.streampipes.connect.iiot.adapters.simulator.machine;
 
 import org.apache.streampipes.commons.exceptions.connect.AdapterException;
-import org.apache.streampipes.model.connect.guess.GuessSchema;
-import org.apache.streampipes.model.schema.PropertyScope;
-import org.apache.streampipes.sdk.builder.PrimitivePropertyBuilder;
-import org.apache.streampipes.sdk.builder.adapter.GuessSchemaBuilder;
-import org.apache.streampipes.sdk.utils.Datatypes;
-import org.apache.streampipes.vocabulary.SO;
-
-import java.net.URI;
-
-import static 
org.apache.streampipes.sdk.helpers.EpProperties.timestampProperty;
+import 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.event.DiagnosticSimulator;
+import 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.event.EventSimulator;
+import 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.event.FlowSimulator;
+import 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.event.PressureSimulator;
+import 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.event.SensorValueSimulator;
+import 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.event.WaterlevelSimulator;
 
 public class MachineDataSimulatorUtils {
 
@@ -35,133 +31,18 @@ public class MachineDataSimulatorUtils {
   public static final String NS = 
"https://streampipes.org/vocabulary/examples/watertank/v1/";;
   public static final String HAS_SENSOR_ID = NS + "hasSensorId";
 
-  private static final String TIMESTAMP = "timestamp";
-  private static final String SENSOR_ID = "sensorId";
-  private static final String MASS_FLOW = "mass_flow";
-  private static final String TEMPERATURE = "temperature";
-
-  public static GuessSchema getSchema(String selectedSimulatorOption) throws 
AdapterException {
-    switch (selectedSimulatorOption) {
-      case "flowrate":
-        return getFlowrateSchema();
-      case "pressure":
-        return getPressureSchema();
-      case "waterlevel":
-        return getWaterlevelSchema();
-      default:
-        throw new AdapterException("resource not found");
-    }
-  }
-
-  private static GuessSchema getWaterlevelSchema() {
-    return GuessSchemaBuilder.create()
-        .property(timestampProperty(TIMESTAMP))
-        .sample(TIMESTAMP, System.currentTimeMillis())
-        .property(PrimitivePropertyBuilder
-            .create(Datatypes.String, SENSOR_ID)
-            .label("Sensor ID")
-            .description("The ID of the sensor")
-            .semanticType(HAS_SENSOR_ID)
-            .scope(PropertyScope.DIMENSION_PROPERTY)
-            .build())
-        .sample(SENSOR_ID, "sensor01")
-        .property(PrimitivePropertyBuilder
-            .create(Datatypes.Float, "level")
-            .label("Water Level")
-            .description("Denotes the current water level in the container")
-            .semanticType(SO.NUMBER)
-            .scope(PropertyScope.MEASUREMENT_PROPERTY)
-            .build())
-        .sample("level", 5.25f)
-        .property(PrimitivePropertyBuilder
-            .create(Datatypes.Boolean, "overflow")
-            .label("Overflow")
-            .description("Indicates whether the tank overflows")
-            .semanticType(SO.NUMBER)
-            .scope(PropertyScope.MEASUREMENT_PROPERTY)
-            .build())
-        .sample("overflow", true)
-        .build();
-  }
-
-  private static GuessSchema getPressureSchema() {
-    return GuessSchemaBuilder.create()
-        .property(timestampProperty(TIMESTAMP))
-        .sample(TIMESTAMP, System.currentTimeMillis())
-        .property(PrimitivePropertyBuilder
-            .create(Datatypes.String, SENSOR_ID)
-            .label("Sensor ID")
-            .description("The ID of the sensor")
-            .semanticType(HAS_SENSOR_ID)
-            .scope(PropertyScope.DIMENSION_PROPERTY)
-            .build())
-        .sample(SENSOR_ID, "sensor01")
-        .property(PrimitivePropertyBuilder
-            .create(Datatypes.Float, "pressure")
-            .label("Pressure")
-            .description("Denotes the current pressure in the pressure tank")
-            .semanticType(SO.NUMBER)
-            .valueSpecification(0.0f, 100.0f, 0.5f)
-            .scope(PropertyScope.MEASUREMENT_PROPERTY)
-            .build())
-        .sample("pressure", 85.22f)
-        .build();
-  }
-
-  public static GuessSchema getFlowrateSchema() {
-    return GuessSchemaBuilder.create()
-        .property(timestampProperty(TIMESTAMP))
-        .sample(TIMESTAMP, System.currentTimeMillis())
-        .property(PrimitivePropertyBuilder
-            .create(Datatypes.String, SENSOR_ID)
-            .label("Sensor ID")
-            .description("The ID of the sensor")
-            .semanticType(HAS_SENSOR_ID)
-            .scope(PropertyScope.DIMENSION_PROPERTY)
-            .build())
-        .sample(SENSOR_ID, "sensor01")
-        .property(PrimitivePropertyBuilder
-            .create(Datatypes.Float, MASS_FLOW)
-            .label("Mass Flow")
-            .description("Denotes the current mass flow in the sensor")
-            .semanticType(SO.NUMBER)
-            .scope(PropertyScope.MEASUREMENT_PROPERTY)
-            .build())
-        .sample(MASS_FLOW, 5.76f)
-        .property(PrimitivePropertyBuilder
-            .create(Datatypes.Float, "volume_flow")
-            .label("Volume Flow")
-            .description("Denotes the current volume flow")
-            .semanticType(SO.NUMBER)
-            .scope(PropertyScope.MEASUREMENT_PROPERTY)
-            .build())
-        .sample("volume_flow", 3.34f)
-        .property(PrimitivePropertyBuilder
-            .create(Datatypes.Float, TEMPERATURE)
-            .label("Temperature")
-            .description("Denotes the current temperature in degrees celsius")
-            .semanticType(SO.NUMBER)
-            .scope(PropertyScope.MEASUREMENT_PROPERTY)
-            
.measurementUnit(URI.create("http://qudt.org/vocab/unit#DegreeCelsius";))
-            .valueSpecification(0.0f, 100.0f, 0.1f)
-            .build())
-        .sample(TEMPERATURE, 33.221f)
-        .property(PrimitivePropertyBuilder
-            .create(Datatypes.Float, "density")
-            .label("Density")
-            .description("Denotes the current density of the fluid")
-            .semanticType(SO.NUMBER)
-            .scope(PropertyScope.MEASUREMENT_PROPERTY)
-            .build())
-        .sample("density", 5.0f)
-        .property(PrimitivePropertyBuilder
-            .create(Datatypes.Boolean, "sensor_fault_flags")
-            .label("Sensor Fault Flags")
-            .description("Any fault flags of the sensors")
-            .semanticType(SO.BOOLEAN)
-            .scope(PropertyScope.MEASUREMENT_PROPERTY)
-            .build())
-        .sample("sensor_fault_flags", true)
-        .build();
+  public static final String TIMESTAMP = "timestamp";
+  public static final String SENSOR_ID = "sensorId";
+  public static final String MASS_FLOW = "mass_flow";
+  public static final String TEMPERATURE = "temperature";
+
+  public static EventSimulator getSimulator(String selectedSimulatorOption) 
throws AdapterException {
+    return switch (selectedSimulatorOption) {
+      case "flowrate" -> new FlowSimulator(new SensorValueSimulator());
+      case "pressure" -> new PressureSimulator(new SensorValueSimulator());
+      case "waterlevel" -> new WaterlevelSimulator(new SensorValueSimulator());
+      case "diagnostics" -> new DiagnosticSimulator(new 
SensorValueSimulator());
+      default -> throw new AdapterException("resource not found");
+    };
   }
 }
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/DiagnosticSimulator.java
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/DiagnosticSimulator.java
new file mode 100644
index 0000000000..e54d6d516d
--- /dev/null
+++ 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/DiagnosticSimulator.java
@@ -0,0 +1,371 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    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 org.apache.streampipes.connect.iiot.adapters.simulator.machine.event;
+
+import org.apache.streampipes.model.connect.guess.GuessSchema;
+import org.apache.streampipes.model.schema.EventPropertyNested;
+import org.apache.streampipes.sdk.builder.NestedPropertyBuilder;
+import org.apache.streampipes.sdk.builder.PrimitivePropertyBuilder;
+import org.apache.streampipes.sdk.builder.adapter.GuessSchemaBuilder;
+import org.apache.streampipes.sdk.helpers.EpProperties;
+import org.apache.streampipes.sdk.helpers.Labels;
+import org.apache.streampipes.sdk.utils.Datatypes;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.MachineDataSimulatorUtils.TIMESTAMP;
+import static 
org.apache.streampipes.sdk.helpers.EpProperties.timestampProperty;
+
+public class DiagnosticSimulator implements EventSimulator {
+
+  private static final String SENSOR_ID = "sensorId";
+  private static final String SENSOR_TYPE = "sensorType";
+  private static final String PHASE = "phase";
+  private static final String ACTIVE = "active";
+  private static final String TAGS = "tags";
+  private static final String METRICS = "metrics";
+  private static final String RECENT_SAMPLES = "recentSamples";
+  private static final String VOLUME_FLOW = "volume_flow";
+  private static final String TEMPERATURE = "temperature";
+  private static final String ACTIVE_ALARMS = "activeAlarms";
+  private static final String OVERALL_STATE = "overallState";
+  private static final String STATUS = "status";
+  private static final String OFFSET_MS = "offsetMs";
+
+  private final SensorValueSimulator valueSimulator;
+
+  public DiagnosticSimulator(SensorValueSimulator valueSimulator) {
+    this.valueSimulator = valueSimulator;
+  }
+
+  @Override
+  public Map<String, Object> buildEvent(int simulationPhase, int sensorIndex, 
long timestamp) {
+    Map<String, Object> event = new HashMap<>();
+
+    event.put(TIMESTAMP, timestamp);
+    event.put(SENSOR_ID, String.format("sensor%02d", sensorIndex));
+    event.put(SENSOR_TYPE, "diagnostic");
+    event.put(PHASE, simulationPhase);
+    event.put(ACTIVE, Boolean.TRUE);
+
+    List<String> tags = new ArrayList<>();
+    tags.add("diagnostic");
+    tags.add(simulationPhase == 0 ? "normal" : "alarm");
+    event.put(TAGS, tags);
+
+    Map<String, Object> metrics = new HashMap<>();
+
+    double volumeFlowValue = valueSimulator.randomDoubleBetween(0, 10);
+    double temperatureValue = (simulationPhase == 0)
+        ? valueSimulator.randomDoubleBetween(40, 50)
+        : valueSimulator.randomDoubleBetween(80, 100);
+
+    metrics.put(VOLUME_FLOW,
+        buildMetricBlock("l/s", volumeFlowValue, 0.0, 10.0, 1.0, 9.0));
+    metrics.put(TEMPERATURE,
+        buildMetricBlock("°C", temperatureValue,
+            (simulationPhase == 0 ? 30.0 : 70.0),
+            (simulationPhase == 0 ? 60.0 : 120.0),
+            (simulationPhase == 0 ? 35.0 : 75.0),
+            (simulationPhase == 0 ? 55.0 : 110.0)));
+
+
+    event.put(METRICS, metrics);
+
+    List<Map<String, Object>> samples = new ArrayList<>();
+    for (int i = 0; i < 3; i++) {
+      Map<String, Object> sample = new HashMap<>();
+      sample.put(OFFSET_MS, -i * 1000);
+      sample.put(VOLUME_FLOW, volumeFlowValue + (i - 1) * 0.1);
+      sample.put(TEMPERATURE, temperatureValue + (i - 1) * 0.2);
+      samples.add(sample);
+    }
+    event.put(RECENT_SAMPLES, samples);
+
+    Map<String, Object> status = new HashMap<>();
+    status.put(OVERALL_STATE, simulationPhase == 0 ? "OK" : "ALARM");
+
+    List<String> activeAlarms = new ArrayList<>();
+    if (simulationPhase != 0) {
+      if (temperatureValue > 80) {
+        activeAlarms.add("HIGH_TEMPERATURE");
+      }
+    }
+    status.put(ACTIVE_ALARMS, activeAlarms);
+
+    event.put(STATUS, status);
+    return event;
+  }
+
+  @Override
+  public GuessSchema getSchema() {
+    return GuessSchemaBuilder.create()
+        .property(timestampProperty(TIMESTAMP))
+        .sample(TIMESTAMP, System.currentTimeMillis())
+        .property(
+            PrimitivePropertyBuilder
+                .create(Datatypes.String, SENSOR_ID)
+                .label("Sensor ID")
+                .description("The ID of the sensor")
+                .build()
+        )
+        .sample(SENSOR_ID, "sensor01")
+        .property(
+            PrimitivePropertyBuilder
+                .create(Datatypes.String, SENSOR_TYPE)
+                .label("Sensor Type")
+                .description("The type of the sensor")
+                .build()
+        )
+        .sample(SENSOR_TYPE, "diagnostic")
+        .property(
+            PrimitivePropertyBuilder.create(Datatypes.Integer, PHASE)
+                .label("Phase")
+                .description("The current simulation phase")
+                .build()
+        )
+        .sample(PHASE, 0)
+        .property(
+            PrimitivePropertyBuilder.create(Datatypes.Boolean, ACTIVE)
+                .label("Active")
+                .description("Indicates whether the sensor is active")
+                .build()
+        )
+        .sample(ACTIVE, true)
+        .property(
+            EpProperties.listStringEp(
+                Labels.from(TAGS, "Tags", "Tags associated with the sensor"),
+                TAGS,
+                null)
+        )
+        .sample(TAGS, List.of("diagnostic", "normal"))
+        .property(
+            NestedPropertyBuilder.create(METRICS)
+                .withEventProperty(
+                    makeMetricsSchema(VOLUME_FLOW)
+                )
+                .withEventProperty(
+                    makeMetricsSchema(TEMPERATURE)
+                )
+                .build()
+        )
+        .sample(METRICS, Map.of(
+            VOLUME_FLOW, Map.of("unit", "l/s", "value", 5.0, "thresholds",
+                Map.of("min", 0.0, "max", 10.0, "warningMin", 1.0, 
"warningMax", 9.0),
+                "recentValues", List.of(4.75, 5.0, 5.25),
+                "samples", List.of(
+                    Map.of("index", 0, "sampleValue", 4.9, "quality", "GOOD"),
+                    Map.of("index", 1, "sampleValue", 5.0, "quality", "FAIR"),
+                    Map.of("index", 2, "sampleValue", 5.1, "quality", "POOR")
+                )
+            ),
+            TEMPERATURE, Map.of("unit", "°C", "value", 45.0, "thresholds",
+                Map.of("min", 30.0, "max", 60.0, "warningMin", 35.0, 
"warningMax", 55.0),
+                "recentValues", List.of(42.75, 45.0, 47.25),
+                "samples", List.of(
+                    Map.of("index", 0, "sampleValue", 44.8, "quality", "GOOD"),
+                    Map.of("index", 1, "sampleValue", 45.0, "quality", "FAIR"),
+                    Map.of("index", 2, "sampleValue", 45.2, "quality", "POOR")
+                )
+            )
+        ))
+        .property(
+            EpProperties.listNestedEp(
+                Labels.from(RECENT_SAMPLES, "Recent Samples", "Recent sensor 
samples"),
+                RECENT_SAMPLES,
+                List.of(
+                    PrimitivePropertyBuilder
+                        .create(Datatypes.Integer, OFFSET_MS)
+                        .label("Offset (ms)")
+                        .description("Offset in milliseconds from the event 
timestamp")
+                        .build(),
+                    PrimitivePropertyBuilder
+                        .create(Datatypes.Double, VOLUME_FLOW)
+                        .label("Volume Flow")
+                        .description("Volume flow value")
+                        .build(),
+                    PrimitivePropertyBuilder
+                        .create(Datatypes.Double, TEMPERATURE)
+                        .label("Temperature")
+                        .description("Temperature value")
+                        .build()
+                )
+            )
+        )
+        .sample(RECENT_SAMPLES, List.of(
+            Map.of(
+                OFFSET_MS, -0,
+                VOLUME_FLOW, 5.0,
+                TEMPERATURE, 45.0
+            ),
+            Map.of(
+                OFFSET_MS, -1000,
+                VOLUME_FLOW, 4.9,
+                TEMPERATURE, 44.8
+            ),
+            Map.of(
+                OFFSET_MS, -2000,
+                VOLUME_FLOW, 5.1,
+                TEMPERATURE, 45.2
+        )))
+        .property(
+            NestedPropertyBuilder.create("status")
+                .withEventProperty(
+                    PrimitivePropertyBuilder
+                        .create(Datatypes.String, OVERALL_STATE)
+                        .label("Overall State")
+                        .description("Overall state of the sensor")
+                        .build()
+                )
+                .withEventProperty(
+                    EpProperties.listStringEp(
+                        Labels.from(ACTIVE_ALARMS, "Active Alarms", "List of 
active alarms"),
+                        ACTIVE_ALARMS,
+                        null)
+                )
+                .build()
+        )
+        .sample("status", Map.of(
+            OVERALL_STATE, "OK",
+            ACTIVE_ALARMS, List.of()
+        ))
+        .build();
+  }
+
+  private EventPropertyNested makeMetricsSchema(String metricsName) {
+    return NestedPropertyBuilder
+        .create(metricsName)
+        .withEventProperty(
+            PrimitivePropertyBuilder
+                .create(Datatypes.String, "unit")
+                .label("Unit")
+                .description("Unit of the volume flow")
+                .build()
+        )
+        .withEventProperty(
+            PrimitivePropertyBuilder
+                .create(Datatypes.Double, "value")
+                .label("Value")
+                .description("Current value of the volume flow")
+                .build()
+        )
+        .withEventProperty(
+            NestedPropertyBuilder
+                .create("thresholds")
+                .withEventProperty(
+                    PrimitivePropertyBuilder
+                        .create(Datatypes.Double, "min")
+                        .label("Min")
+                        .description("Minimum threshold")
+                        .build()
+                )
+                .withEventProperty(
+                    PrimitivePropertyBuilder
+                        .create(Datatypes.Double, "max")
+                        .label("Max")
+                        .description("Maximum threshold")
+                        .build()
+                )
+                .withEventProperty(
+                    PrimitivePropertyBuilder
+                        .create(Datatypes.Double, "warningMin")
+                        .label("Warning Min")
+                        .description("Warning minimum threshold")
+                        .build()
+                )
+                .withEventProperty(
+                    PrimitivePropertyBuilder
+                        .create(Datatypes.Double, "warningMax")
+                        .label("Warning Max")
+                        .description("Warning maximum threshold")
+                        .build()
+                )
+                .build())
+        .withEventProperty(
+            EpProperties.listDoubleEp(
+                Labels.from("recentValues", "Recent Values", "Recent volume 
flow values"),
+                "recentValues",
+                null
+            )
+        )
+        .withEventProperty(
+            EpProperties.listNestedEp(
+                Labels.from("samples", "Samples", "Recent volume flow 
samples"),
+                "samples",
+                List.of(
+                    PrimitivePropertyBuilder
+                        .create(Datatypes.Integer, "index")
+                        .label("Index")
+                        .description("Sample index")
+                        .build(),
+                    PrimitivePropertyBuilder
+                        .create(Datatypes.Double, "sampleValue")
+                        .label("Sample Value")
+                        .description("Value of the sample")
+                        .build(),
+                    PrimitivePropertyBuilder
+                        .create(Datatypes.String, "quality")
+                        .label("Quality")
+                        .description("Quality of the sample")
+                        .build()
+                )
+            )
+        )
+        .build();
+  }
+
+  private Map<String, Object> buildMetricBlock(String unit,
+                                               double value,
+                                               double min,
+                                               double max,
+                                               double warningMin,
+                                               double warningMax) {
+    Map<String, Object> metric = new HashMap<>();
+    metric.put("unit", unit);
+    metric.put("value", value);
+
+    Map<String, Object> thresholds = new HashMap<>();
+    thresholds.put("min", min);
+    thresholds.put("max", max);
+    thresholds.put("warningMin", warningMin);
+    thresholds.put("warningMax", warningMax);
+    metric.put("thresholds", thresholds);
+
+    List<Double> recentValues = new ArrayList<>();
+    recentValues.add(value * 0.95);
+    recentValues.add(value);
+    recentValues.add(value * 1.05);
+    metric.put("recentValues", recentValues);
+
+    List<Map<String, Object>> samples = new ArrayList<>();
+    for (int i = 0; i < 3; i++) {
+      Map<String, Object> sample = new HashMap<>();
+      sample.put("index", i);
+      sample.put("sampleValue", value + (i - 1) * 0.1);
+      sample.put("quality", i == 0 ? "GOOD" : (i == 1 ? "FAIR" : "POOR"));
+      samples.add(sample);
+    }
+    metric.put("samples", samples);
+
+    return metric;
+  }
+}
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/EventSimulator.java
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/EventSimulator.java
new file mode 100644
index 0000000000..6db71843b8
--- /dev/null
+++ 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/EventSimulator.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    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 org.apache.streampipes.connect.iiot.adapters.simulator.machine.event;
+
+import org.apache.streampipes.model.connect.guess.GuessSchema;
+
+import java.util.Map;
+
+public interface EventSimulator {
+
+  Map<String, Object> buildEvent(int simulationPhase, int sensorIndex, long 
timestamp);
+
+  GuessSchema getSchema();
+
+}
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulatorUtils.java
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/FlowSimulator.java
similarity index 54%
copy from 
streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulatorUtils.java
copy to 
streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/FlowSimulator.java
index 1a3a27865d..9a4adfc726 100644
--- 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/MachineDataSimulatorUtils.java
+++ 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/FlowSimulator.java
@@ -15,9 +15,9 @@
  * limitations under the License.
  *
  */
-package org.apache.streampipes.connect.iiot.adapters.simulator.machine;
 
-import org.apache.streampipes.commons.exceptions.connect.AdapterException;
+package org.apache.streampipes.connect.iiot.adapters.simulator.machine.event;
+
 import org.apache.streampipes.model.connect.guess.GuessSchema;
 import org.apache.streampipes.model.schema.PropertyScope;
 import org.apache.streampipes.sdk.builder.PrimitivePropertyBuilder;
@@ -26,89 +26,41 @@ import org.apache.streampipes.sdk.utils.Datatypes;
 import org.apache.streampipes.vocabulary.SO;
 
 import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
 
+import static 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.MachineDataSimulatorUtils.HAS_SENSOR_ID;
+import static 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.MachineDataSimulatorUtils.MASS_FLOW;
+import static 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.MachineDataSimulatorUtils.SENSOR_ID;
+import static 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.MachineDataSimulatorUtils.TEMPERATURE;
+import static 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.MachineDataSimulatorUtils.TIMESTAMP;
 import static 
org.apache.streampipes.sdk.helpers.EpProperties.timestampProperty;
 
-public class MachineDataSimulatorUtils {
-
-  // Vocabulary
-  public static final String NS = 
"https://streampipes.org/vocabulary/examples/watertank/v1/";;
-  public static final String HAS_SENSOR_ID = NS + "hasSensorId";
+public class FlowSimulator implements EventSimulator {
 
-  private static final String TIMESTAMP = "timestamp";
-  private static final String SENSOR_ID = "sensorId";
-  private static final String MASS_FLOW = "mass_flow";
-  private static final String TEMPERATURE = "temperature";
+  private final SensorValueSimulator valueSimulator;
 
-  public static GuessSchema getSchema(String selectedSimulatorOption) throws 
AdapterException {
-    switch (selectedSimulatorOption) {
-      case "flowrate":
-        return getFlowrateSchema();
-      case "pressure":
-        return getPressureSchema();
-      case "waterlevel":
-        return getWaterlevelSchema();
-      default:
-        throw new AdapterException("resource not found");
-    }
+  public FlowSimulator(SensorValueSimulator valueSimulator) {
+    this.valueSimulator = valueSimulator;
   }
 
-  private static GuessSchema getWaterlevelSchema() {
-    return GuessSchemaBuilder.create()
-        .property(timestampProperty(TIMESTAMP))
-        .sample(TIMESTAMP, System.currentTimeMillis())
-        .property(PrimitivePropertyBuilder
-            .create(Datatypes.String, SENSOR_ID)
-            .label("Sensor ID")
-            .description("The ID of the sensor")
-            .semanticType(HAS_SENSOR_ID)
-            .scope(PropertyScope.DIMENSION_PROPERTY)
-            .build())
-        .sample(SENSOR_ID, "sensor01")
-        .property(PrimitivePropertyBuilder
-            .create(Datatypes.Float, "level")
-            .label("Water Level")
-            .description("Denotes the current water level in the container")
-            .semanticType(SO.NUMBER)
-            .scope(PropertyScope.MEASUREMENT_PROPERTY)
-            .build())
-        .sample("level", 5.25f)
-        .property(PrimitivePropertyBuilder
-            .create(Datatypes.Boolean, "overflow")
-            .label("Overflow")
-            .description("Indicates whether the tank overflows")
-            .semanticType(SO.NUMBER)
-            .scope(PropertyScope.MEASUREMENT_PROPERTY)
-            .build())
-        .sample("overflow", true)
-        .build();
-  }
+  @Override
+  public Map<String, Object> buildEvent(int simulationPhase, int sensorIndex, 
long timestamp) {
+    Map<String, Object> event = new HashMap<>();
 
-  private static GuessSchema getPressureSchema() {
-    return GuessSchemaBuilder.create()
-        .property(timestampProperty(TIMESTAMP))
-        .sample(TIMESTAMP, System.currentTimeMillis())
-        .property(PrimitivePropertyBuilder
-            .create(Datatypes.String, SENSOR_ID)
-            .label("Sensor ID")
-            .description("The ID of the sensor")
-            .semanticType(HAS_SENSOR_ID)
-            .scope(PropertyScope.DIMENSION_PROPERTY)
-            .build())
-        .sample(SENSOR_ID, "sensor01")
-        .property(PrimitivePropertyBuilder
-            .create(Datatypes.Float, "pressure")
-            .label("Pressure")
-            .description("Denotes the current pressure in the pressure tank")
-            .semanticType(SO.NUMBER)
-            .valueSpecification(0.0f, 100.0f, 0.5f)
-            .scope(PropertyScope.MEASUREMENT_PROPERTY)
-            .build())
-        .sample("pressure", 85.22f)
-        .build();
+    event.put("timestamp", timestamp);
+    event.put("sensorId", String.format("flowrate%02d", sensorIndex));
+    event.put("mass_flow", valueSimulator.randomDoubleBetween(0, 10));
+    event.put("volume_flow", valueSimulator.randomDoubleBetween(0, 10));
+    event.put("temperature",
+        simulationPhase == 0 ? valueSimulator.randomDoubleBetween(40, 50) : 
valueSimulator.randomDoubleBetween(80, 100));
+    event.put("density", valueSimulator.randomDoubleBetween(40, 50));
+    event.put("sensor_fault_flags", simulationPhase != 0);
+
+    return event;
   }
 
-  public static GuessSchema getFlowrateSchema() {
+  public GuessSchema getSchema() {
     return GuessSchemaBuilder.create()
         .property(timestampProperty(TIMESTAMP))
         .sample(TIMESTAMP, System.currentTimeMillis())
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/PressureSimulator.java
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/PressureSimulator.java
new file mode 100644
index 0000000000..c9d8255855
--- /dev/null
+++ 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/PressureSimulator.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    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 org.apache.streampipes.connect.iiot.adapters.simulator.machine.event;
+
+import org.apache.streampipes.model.connect.guess.GuessSchema;
+import org.apache.streampipes.model.schema.PropertyScope;
+import org.apache.streampipes.sdk.builder.PrimitivePropertyBuilder;
+import org.apache.streampipes.sdk.builder.adapter.GuessSchemaBuilder;
+import org.apache.streampipes.sdk.utils.Datatypes;
+import org.apache.streampipes.vocabulary.SO;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.MachineDataSimulatorUtils.HAS_SENSOR_ID;
+import static 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.MachineDataSimulatorUtils.SENSOR_ID;
+import static 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.MachineDataSimulatorUtils.TIMESTAMP;
+import static 
org.apache.streampipes.sdk.helpers.EpProperties.timestampProperty;
+
+public class PressureSimulator implements EventSimulator {
+
+  private final SensorValueSimulator valueSimulator;
+
+  public PressureSimulator(SensorValueSimulator valueSimulator) {
+    this.valueSimulator = valueSimulator;
+  }
+
+  @Override
+  public Map<String, Object> buildEvent(int simulationPhase, int sensorIndex, 
long timestamp) {
+    Map<String, Object> event = new HashMap<>();
+
+    event.put("timestamp", timestamp);
+    event.put("sensorId", String.format("pressure%02d", sensorIndex));
+    event.put("pressure",
+        simulationPhase == 0 ? valueSimulator.randomDoubleBetween(10, 40) : 
valueSimulator.randomDoubleBetween(40, 70));
+
+    return event;
+  }
+
+  @Override
+  public GuessSchema getSchema() {
+    return GuessSchemaBuilder.create()
+        .property(timestampProperty(TIMESTAMP))
+        .sample(TIMESTAMP, System.currentTimeMillis())
+        .property(PrimitivePropertyBuilder
+            .create(Datatypes.String, SENSOR_ID)
+            .label("Sensor ID")
+            .description("The ID of the sensor")
+            .semanticType(HAS_SENSOR_ID)
+            .scope(PropertyScope.DIMENSION_PROPERTY)
+            .build())
+        .sample(SENSOR_ID, "sensor01")
+        .property(PrimitivePropertyBuilder
+            .create(Datatypes.Float, "pressure")
+            .label("Pressure")
+            .description("Denotes the current pressure in the pressure tank")
+            .semanticType(SO.NUMBER)
+            .valueSpecification(0.0f, 100.0f, 0.5f)
+            .scope(PropertyScope.MEASUREMENT_PROPERTY)
+            .build())
+        .sample("pressure", 85.22f)
+        .build();
+  }
+}
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/SensorValueSimulator.java
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/SensorValueSimulator.java
new file mode 100644
index 0000000000..912de2f3de
--- /dev/null
+++ 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/SensorValueSimulator.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    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 org.apache.streampipes.connect.iiot.adapters.simulator.machine.event;
+
+public class SensorValueSimulator {
+
+  public double randomDoubleBetween(int min, int max) {
+    return Math.random() * (max - min + 1) + min;
+  }
+}
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/WaterlevelSimulator.java
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/WaterlevelSimulator.java
new file mode 100644
index 0000000000..50798aa38a
--- /dev/null
+++ 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/simulator/machine/event/WaterlevelSimulator.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    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 org.apache.streampipes.connect.iiot.adapters.simulator.machine.event;
+
+import org.apache.streampipes.model.connect.guess.GuessSchema;
+import org.apache.streampipes.model.schema.PropertyScope;
+import org.apache.streampipes.sdk.builder.PrimitivePropertyBuilder;
+import org.apache.streampipes.sdk.builder.adapter.GuessSchemaBuilder;
+import org.apache.streampipes.sdk.utils.Datatypes;
+import org.apache.streampipes.vocabulary.SO;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.MachineDataSimulatorUtils.HAS_SENSOR_ID;
+import static 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.MachineDataSimulatorUtils.SENSOR_ID;
+import static 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.MachineDataSimulatorUtils.TIMESTAMP;
+import static 
org.apache.streampipes.sdk.helpers.EpProperties.timestampProperty;
+
+public class WaterlevelSimulator implements EventSimulator {
+
+  private final SensorValueSimulator valueSimulator;
+
+  public WaterlevelSimulator(SensorValueSimulator valueSimulator) {
+    this.valueSimulator = valueSimulator;
+  }
+
+  @Override
+  public Map<String, Object> buildEvent(int simulationPhase, int sensorIndex, 
long timestamp) {
+    Map<String, Object> event = new HashMap<>();
+
+    event.put("timestamp", timestamp);
+    event.put("sensorId", String.format("level%02d", sensorIndex));
+    event.put("level",
+        simulationPhase == 0 ? valueSimulator.randomDoubleBetween(20, 30) : 
valueSimulator.randomDoubleBetween(60, 80));
+    event.put("overflow", simulationPhase != 0);
+
+    return event;
+  }
+
+  @Override
+  public GuessSchema getSchema() {
+    return GuessSchemaBuilder.create()
+        .property(timestampProperty(TIMESTAMP))
+        .sample(TIMESTAMP, System.currentTimeMillis())
+        .property(PrimitivePropertyBuilder
+            .create(Datatypes.String, SENSOR_ID)
+            .label("Sensor ID")
+            .description("The ID of the sensor")
+            .semanticType(HAS_SENSOR_ID)
+            .scope(PropertyScope.DIMENSION_PROPERTY)
+            .build())
+        .sample(SENSOR_ID, "sensor01")
+        .property(PrimitivePropertyBuilder
+            .create(Datatypes.Float, "level")
+            .label("Water Level")
+            .description("Denotes the current water level in the container")
+            .semanticType(SO.NUMBER)
+            .scope(PropertyScope.MEASUREMENT_PROPERTY)
+            .build())
+        .sample("level", 5.25f)
+        .property(PrimitivePropertyBuilder
+            .create(Datatypes.Boolean, "overflow")
+            .label("Overflow")
+            .description("Indicates whether the tank overflows")
+            .semanticType(SO.NUMBER)
+            .scope(PropertyScope.MEASUREMENT_PROPERTY)
+            .build())
+        .sample("overflow", true)
+        .build();
+  }
+}
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/migration/MachineDataSimulatorMigrationV1.java
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/migration/MachineDataSimulatorMigrationV1.java
new file mode 100644
index 0000000000..08ffbbc14b
--- /dev/null
+++ 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/migration/MachineDataSimulatorMigrationV1.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    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 org.apache.streampipes.connect.iiot.migration;
+
+import 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.MachineDataSimulatorAdapter;
+import 
org.apache.streampipes.extensions.api.extractor.IStaticPropertyExtractor;
+import org.apache.streampipes.extensions.api.migration.IAdapterMigrator;
+import org.apache.streampipes.model.connect.adapter.AdapterDescription;
+import org.apache.streampipes.model.extensions.svcdiscovery.SpServiceTagPrefix;
+import org.apache.streampipes.model.migration.MigrationResult;
+import org.apache.streampipes.model.migration.ModelMigratorConfig;
+import org.apache.streampipes.model.staticproperty.OneOfStaticProperty;
+import org.apache.streampipes.model.staticproperty.Option;
+import org.apache.streampipes.sdk.StaticProperties;
+import org.apache.streampipes.sdk.helpers.Labels;
+
+import static 
org.apache.streampipes.connect.iiot.adapters.simulator.machine.MachineDataSimulatorAdapter.NUMBER_OF_SENSORS;
+
+public class MachineDataSimulatorMigrationV1 implements IAdapterMigrator {
+  @Override
+  public ModelMigratorConfig config() {
+    return new ModelMigratorConfig(
+        MachineDataSimulatorAdapter.ID,
+        SpServiceTagPrefix.ADAPTER,
+        0,
+        1
+    );
+  }
+
+  @Override
+  public MigrationResult<AdapterDescription> migrate(AdapterDescription 
element,
+                                                     IStaticPropertyExtractor 
extractor) throws RuntimeException {
+    element.getConfig().add(1, 
StaticProperties.integerFreeTextProperty(Labels.withId(NUMBER_OF_SENSORS), 1));
+    var sensorTypes = element.getConfig().get(2);
+    if (sensorTypes instanceof OneOfStaticProperty oneOfStaticProperty) {
+      oneOfStaticProperty.getOptions().add(new Option("diagnostics"));
+    }
+    return MigrationResult.success(element);
+  }
+}
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2.file/documentation.md
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2.file/documentation.md
deleted file mode 100644
index a03969e8fa..0000000000
--- 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2.file/documentation.md
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You under the Apache License, Version 2.0
-  ~ (the "License"); you may not use this file except in compliance with
-  ~ the License.  You may obtain a copy of the License at
-  ~
-  ~    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.
-  ~
-  -->
-
-## File (Stream)
-
-<p align="center"> 
-    <img src="icon.png" width="150px;" class="pe-image-documentation"/>
-</p>
-
-***
-
-## Description
-
-Continuously streams the content from a file
-
-***
-
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2.file/icon.png
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2.file/icon.png
deleted file mode 100644
index 2b3a037857..0000000000
Binary files 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2.file/icon.png
 and /dev/null differ
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2.file/strings.en
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2.file/strings.en
deleted file mode 100644
index 8847039ef7..0000000000
--- 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2.file/strings.en
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#    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.
-#
-
-
-org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2.file.title=File
 Stream
-org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2.file.description=Continuously
 streams the content from a file.
-
-filePath.title=File
-filePath.description=File Path
-
-replaceTimestamp.title=Use current time
-replaceTimestamp.description=Replace Event Time with Current Timestamp
-
-speed.title=Replay Speed
-speed.description=
-
-speedUp.title=Speed Up
-speedUp.description=original = 1; speedup 2x = 2; half speed  =  0.5
-
-replayOnce.title=Replay Once
-replayOnce.description='yes' file is only replayed once, 'no' the file is 
replayed till adapter is stopped
-
-keepOriginalTime.title=Keep original time
-keepOriginalTime.description=
-
-speedUpFactor.title=Speed Up Factor
-speedUpFactor.description=
-
-fastest.title=Fastest (Ignore original time)
-fastest.description=
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2/documentation.md
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2/documentation.md
deleted file mode 100644
index ec3ed056bb..0000000000
--- 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2/documentation.md
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You under the Apache License, Version 2.0
-  ~ (the "License"); you may not use this file except in compliance with
-  ~ the License.  You may obtain a copy of the License at
-  ~
-  ~    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.
-  ~
-  -->
-
-## Machine Data Simulator
-
-<p align="center"> 
-    <img src="icon.png" width="150px;" class="pe-image-documentation"/>
-</p>
-
-***
-
-## Description
-
-Publishes various simulated machine sensor data in a configurable time 
interval (in milliseconds).
-Sensors are:
-* flowrate
-* pressure
-* waterlevel
-***
\ No newline at end of file
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2/icon.png
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2/icon.png
deleted file mode 100644
index 33c09c451c..0000000000
Binary files 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2/icon.png
 and /dev/null differ
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2/strings.en
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2/strings.en
deleted file mode 100644
index 21b8edc4c8..0000000000
--- 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2/strings.en
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#    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.
-#
-
-
-org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2.title=Machine
 Data Simulator
-org.apache.streampipes.connect.iiot.adapters.simulator.machine.v2.description=Publishes
 various simulated machine sensor data
-
-wait-time-ms.title=Wait Time (MS)
-wait-time-ms.description=The time to wait between two events in milliseconds
-
-selected-simulator-option.title=Select sensor
-selected-simulator-option.description=Select simulated sensor data to be 
published
\ No newline at end of file
diff --git 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine/strings.en
 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine/strings.en
index f45e9d25bb..2ebb381be1 100644
--- 
a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine/strings.en
+++ 
b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/resources/org.apache.streampipes.connect.iiot.adapters.simulator.machine/strings.en
@@ -23,4 +23,7 @@ wait-time-ms.title=Wait Time (MS)
 wait-time-ms.description=The time to wait between two events in milliseconds
 
 selected-simulator-option.title=Select sensor
-selected-simulator-option.description=Select simulated sensor data to be 
published
\ No newline at end of file
+selected-simulator-option.description=Select simulated sensor data to be 
published
+
+numberOfSensors.title=Number of sensors
+numberOfSensors.description=The number of sensors to simulate

Reply via email to