Ding-Abonnements
Abonnements sind Dienste, die Ereignisse erhalten und auf sie antworten. Ein Abonnement enthält eine Quelle, normalerweise ein Ding. Ein Ding kann ein Abonnement für ein Ereignis haben, das mit einer Aktion antwortet. Wenn beispielsweise eine Entität das Ereignis Motor ist überhitzt auslöst, kann sie dieses Ereignis abonnieren, indem sie ein Abonnement des Typs Motor ausschalten auslöst. Dinge können Abonnements von den Dingvorlagen und Dingformen erben, die sie verwenden.
Ein Abonnement ähnelt einem Standarddienst, ist aber explizit mit einem oder mehreren Ereignissen verknüpft. Dies ermöglicht es Ihnen, das Ereignis vom Code zu entkoppeln, der antwortet. Wie bei einem Dienst können Sie benutzerdefinierte Geschäftslogik implementieren, um auf das Ereignis zu reagieren. Sie können die Funktionen des Modells nutzen, wie Senden von E-Mails über ein Mail-Server-Ding, Schreiben in eine Datenbank oder Aufrufen beliebiger Dienste in der Plattform. Ein Abonnement hat im Gegensatz zu einem Dienst keine explizite Rückgabeausgabe. Ein Abonnement kann jedoch jeden anderen Dienst im Modell aufrufen, auf den der Thread-Sicherheitskontext Zugriff hat. Der Thread-Sicherheitskontext eines Abonnements wird auf denselben Thread-Sicherheitskontext des Ereignisses festgelegt, das ausgelöst wurde. Sie können die JavaScript-Bearbeitungsumgebung verwenden, die für das Implementieren von Diensten verwendet wird.
Abonnements haben eine definierte Eingabe. Dies ist das Datenpaket, das vom Ereignis ausgegeben und als Ereignisdaten bezeichnet wird. Wenn die Entität ein definiertes Ereignis abonniert, werden die Ereignisdaten an die Abonnementfunktion übergeben. Die Ereignisdaten werden durch den Data Shape des Ereignisses beschrieben. Innerhalb der Abonnementimplementierung dienen die Daten, die vom Ereignis übergeben werden, als Eingabe für die Skriptfunktion. Wenn eine Entität beispielsweise ein Ding-Datenänderungsereignis abonniert hat, wird die Abonnement-Skriptfunktion aufgerufen. Anschließend wird der Ding-Eigenschaftswert zusammen mit anderen relevanten Daten aus dem Ereignis als Teil der Ereignisdaten an die Funktion übergeben.
Viele Entitäten können durch ein Abonnement dasselbe Ereignis abonnieren. Jede Entität erhält einen Aufruf des Abonnements mit den übergebenen Ereignisdaten. Eine Entität kann jede Aktion aus dem Abonnementskript verwenden, um die Lösungsanforderungen zu erfüllen.
Der Verwendung dieser Technik hat im Vergleich zu einem Dienst, der von einem anderen Dienst aufgerufen wird, unter anderem die folgenden Vorteile:
Ein Ereignis kann von einem oder mehreren Abonnements abonniert werden.
Ereignisse werden basierend auf der Systemaktivität aufgerufen, und es ist keine Benutzerinteraktion erforderlich.
Wenn mehr als ein Ding für ein Ereignis abonniert ist, können Sie ein Abonnement verwenden, anstatt mehrere Dienste zu verketten.
Ein Abonnement kann mehr als ein Ereignis abonnieren, auch von verschiedenen Entitäten.
* 
Ereignis-Trigger und Abonnements werden asynchron ausgeführt. Beispielsweise erhält eine API-Anforderung für die Eigenschaftsaktualisierung eine sofortige Antwort, wenn die Eigenschaftsaktualisierung abgeschlossen ist. Es wird nicht gewartet, bis die nachfolgenden Abonnements, die auf die Datenänderung antworten, abgeschlossen sind.
Mehrere Abonnements
Abonnements haben einen benutzerdefinierten Namen als eindeutige ID. Entitäten können mehrere Abonnements für ein Ereignis für ein Ding haben. Wenn beispielsweise eine Entität das Ereignis Motor ist überhitzt auslöst, kann sie dieses Ereignis mit dem Abonnement Motor ausschalten und dem Abonnement Arbeitsauftrag erstellen abonnieren, um den Motor warten zu lassen. Es können auch beliebig viele andere Abonnements für dieses Ereignis erstellt werden.
Wenn eine Dingvorlage oder Dingform ein Abonnement für ein Ereignis implementiert, können die Dinge, die diese Dingvorlage oder Dingform verwenden, ebenfalls Abonnements für dasselbe Ereignis erstellen, ohne dass beim Auslösen dieser Ereignisse ein Workaround erforderlich ist, um zusätzliche Maßnahmen zu ergreifen.
Verteilte Abonnements
Verteilte Abonnements ermöglichen die Verteilung von Abonnementausführungen auf alle ThingWorx Knoten, wenn ein Ereignis viele Instanzen von Abonnements auslöst. Dies ist beispielsweise der Fall, wenn viele Dinge dasselbe Zeitgeber- oder Scheduler-Ereignis abonnieren. Dies ermöglicht die horizontale Skalierbarkeit der Zeitgeber-/Scheduler-basierten Abonnementausführung über alle ThingWorx Knoten in Hochverfügbarkeitsumgebungen für eine bessere Ressourcenauslastung und Leistung. Dieses Verhalten wird über das Kontrollkästchen Verteilt auf der Registerkarte Abonnementinformationen aktiviert. Wenn das Kontrollkästchen Verteilt deaktiviert ist, werden Abonnements für Zeitgeber und Scheduler auf dem Knoten ausgeführt, worauf das Zeitgeber- oder Scheduler-Ereignis generiert wird. Weitere Informationen zur zugehörigen Konfiguration finden Sie hier:
Informationen zur lokalen Umgebung finden Sie unter SSL/TLS für AKKA konfigurieren.
Informationen zur Docker-Umgebung finden Sie unter Akka TLS-Kommunikation für ThingWorx konfigurieren.
Multi-Ereignis-Abonnements
Die Funktion "Multi-Ereignis-Abonnement" ermöglicht es Kunden, für ein einzelnes Abonnement mehr als ein Ereignis zu abonnieren, selbst aus verschiedenen Dingen. Dies ist hauptsächlich für komplexe Abonnements erforderlich. Beispielsweise können Kunden Abonnements erstellen, die von mehreren verschiedenen Eigenschaftsänderungen ausgelöst werden können, und eine einfache If-Then-Else-Regel für diese Eigenschaftswerte ausführen, um Maßnahmen basierend auf den Ergebnissen zu ergreifen. Ein Beispiel ist unter Anwendungsfall 2 zu finden. Alternativ können Sie ein auf dem Zeitgeberereignis basierendes Abonnement erstellen, um die Zeitfensterregel festzulegen. Ein Beispiel ist unter Anwendungsfall 3 zu finden.
* 
Die Funktion "Multi-Ereignis-Abonnement" wird nur beim Erstellen eines neuen Abonnements angezeigt. Das mit Version ThingWorx 9.4 und höher erstellte Legacy-Abonnement bleibt unverändert und kann nur ein Ereignis abonnieren.
Mit der Möglichkeit von Multi-Ereignis-Abonnements führen wir das Batch-Erfassungskonzept ein, das mehr als ein Ereignis gleichzeitig an ein Abonnement liefert, wobei die Ereignisse auf Grundlage des Zeitstempels aktualisierter Eigenschaften gruppiert werden. Weitere Informationen finden Sie unter Dingeigenschaften. Weitere Informationen zum Remote-Ding finden Sie unter Dienste für Remote-Dinge.
* 
Wenn Sie derzeit entweder die Java-Erweiterungs-API getSubscriptions oder getInstanceSubscriptions verwenden, müssen Sie stattdessen die Option getMultiEventSubscriptions oder getInstanceMultiEventSubscriptions verwenden. getSubscriptions und getInstanceSubscriptions unterstützen keine Abonnementformate von ThingWorx 9.5 und höher. Dies gilt nur für Entitäten, die eine Kombination von Abonnements enthalten, die vor und nach ThingWorx 9.5 erstellt wurden. Die neuen APIs unterstützen das Legacy-Abonnementformat (vor ThingWorx 9.5) und das neue Abonnementformat (ThingWorx 9.5 und höher).
Das +-Zeichen neben der Dropdown-Liste Ereignisse auf der Registerkarte Eingabe des Abonnements aktiviert dieses Verhalten. Die Liste der Ereignisse des Abonnements kann verwaltet werden. Sie können beispielsweise Ereignisse hinzufügen/löschen und Ereignisinformationen bearbeiten. Jedes hinzugefügte Ereignis erhält einen benutzerdefinierten Alias als eindeutigen Identifier. Dieser Alias sollte für den Zugriff auf eventData über JavaScript verwendet werden. Weitere Informationen über den Zugriff auf eventData finden Sie weiter unten.
* 
Wenn Sie mehr als eine Eigenschaft für den gleichen Zeitstempel aktualisieren, sind die neuen Dienste UpdatePropertyValuesBatched und UpdateSubscribedPropertyValuesBatched den Legacy-Diensten UpdatePropertyValues und UpdateSubscribedPropertyValues vorzuziehen. Die neuen Dienste ermöglichen es, dass Multi-Ereignis-Abonnements genau einmal pro Zeitstempel ausgeführt werden und damit die neuen Werte für alle geänderten Eigenschaften in derselben Ausführung empfangen. Auf diese Weise wird die Gesamtzahl der Abonnementausführungen verglichen mit den Legacy-Diensten reduziert, was einen geringeren CPU-Verbrauch zur Folge hat.
Auf eventData zugreifen
Der Zugriff auf eventData-Informationen über JavaScript erfolgt über den Alias des Ereignisses. Ein Entwickler kann nicht länger über event.eventData.newValue auf eventData zugreifen, sondern muss die events[]-Ebene und den eindeutigen Namen des Alias verwenden, um auf bestimmte eventData wie z.B. events["AliasName"].eventData.newValue.value zuzugreifen.
* 
Diese Richtlinie gilt nur für neue Abonnements, die in ThingWorx 9.5 und höher erstellt werden. Alte Abonnements, die vor 9.5 erstellt wurden, können weiterhin über event.eventData.newValue auf eventData zugreifen.
Es ist möglich, dass nur ein Teil der zugeordneten Ereignisse das Abonnement auslöst. Daher kann es zu einem Fehler führen, wenn Sie auf eventData zugreifen, die das Abonnement nicht ausgelöst haben, sodass Sie zuerst prüfen müssen, ob der Zugriff auf eventData definiert ist. Beispiel:
try {
if (events["Me_DataChange_p1"].eventData.newValue.value ==44 {

} catch (error) {
logger.error(" p1 event is not defined " + error);
}
Dies ist ein weiteres Beispiel.
if( events["Me_DataChange_p1"] !== undefined ){
if(events["Me_DataChange_p1"].eventData.newValue.value)==44;{

}
}
Sortierte und Abonnements und Abonnements mit Status
Ein sortiertes Abonnement ist für Anwendungsfälle vorgesehen, die Reihenfolge und Atomarität (Unteilbarkeit) erfordern. Es wird nur beim Erstellen eines neuen Abonnements angezeigt. Abonnements, die mit ThingWorx 9.4 und früher erstellt wurden, bleiben unverändert und können nicht sequentiell ausgeführt werden.
Ohne diese Funktion werden Abonnementausführungen asynchron parallel ausgeführt und können keinen Status haben (Werte können nicht zwischen Ausführungen übertragen werden), was in einigen Anwendungsfällen zu Parallelitätsproblemen und falschen Ergebnissen führen kann, hauptsächlich wenn Abonnementregeln auf vorherigen Werten basieren. Ein sortiertes Abonnement ermöglicht es jedoch, Abonnementausführungen nacheinander, basierend auf der Zeitstempelreihenfolge eines Ereignisses, durchzuführen.
Dieses Verhalten wird über das Kontrollkästchen Ereignisse sequentiell ausführen auf der Registerkarte Abonnementinformationen aktiviert. Außerdem erhält ein Entwickler nach Aktivierung der Option die vollständige Kontrolle über den Inhalt eines dedizierten JSON-Objekts für jedes Abonnement, sodass er vorherige Werte zwischen Abonnementausführungen speichern, Daten sammeln und einfache Aggregationen mit Status durchführen kann. Weitere Informationen über die Verwendung von thisSub.JSONState finden Sie weiter unten.
* 
Sortierte Abonnements erfordern mehr CPU-Ressourcen als unsortierte Abonnements. Daher können das Kennzeichnen aller Abonnements als sortiert Auswirkungen auf den CPU-Verbrauch insgesamt haben und zu einer geringeren Abonnementausführungsrate führen. Für Abonnements, die keinen Status halten müssen und parallel ohne Nebenwirkungen ausgeführt werden können, wird empfohlen, das Kontrollkästchen Ereignisse sequentiell ausführen deaktiviert zu lassen.
thisSub.JSONState verwenden
Auf das Objekt thisSub.JSONState kann wie folgt über JavaScript zugegriffen werden:
Verwenden Sie thisSub.JSONState.X=5 als nativen Wert.
Verwenden Sie JSONObject(), thisSub.JSONstate.myInfoTable = Y.toJSONObject() als Infotable.
Verwenden Sie thisSub.state.myInfoTable.rows[0].value, um darauf zuzugreifen.
Die im JSONState-Objekt gespeicherten Daten sollten sorgfältig verwendet und nur auf die erforderlichen Felder beschränkt werden. Es wird beispielsweise nicht empfohlen, vollständige Infotable-Objekte mit der ToJSONObject()-Methode statt einzelner Felder zu speichern, da dadurch mehr CPU-Speicher beansprucht und mehr Statusspeicher benötigt wird.
Verwenden Sie das JavaScript-delete-Schlüsselwort delete thisSub.JSONState.myInfoTable, um einen Wert aus dem Statusobjekt zu löschen.
Wenn Sie das gesamte Statusobjekt löschen möchte, weisen Sie es einem leeren JSON-{}-Ausdruck zu: thisSub.JSONState = {}.
* 
Damit verhindert wird, dass Statusdaten durch übermäßigen Arbeitsspeicherverbrauch verloren gehen, muss der Entwickler einzelne Statusfelder löschen oder das gesamte thisSub.JSONState-Objekt löschen, sobald es nicht mehr benötigt wird.
thisSub.JSONState wird im Arbeitsspeicher gespeichert und daher beim Herunterfahren von ThingWorx gelöscht. Außerdem wird der Status in folgenden Fällen gelöscht:
Die Entität, die das Abonnement deklariert, wird bearbeitet und gespeichert.
Die Entität, die das Abonnement deklariert, wird deaktiviert.
Das Abonnement wird deaktiviert, z.B. mit dem Dienst DisableSubscription.
Der neue ThingWorx Knoten wird in einem HA-Cluster gestartet/angehalten, und einige Abonnements werden auf einem anderen Knoten ausgeführt.
* 
ThingWorx setzt Speichergrößengrenzwerte für thisSub.JSONState eines einzelnen Abonnements und den gesamten Arbeitsspeicher alle Abonnementsstatus durch. Diese Grenzwerte können in platform-settings.json Configuration Details im Abschnitt "SubscriptionSettings" konfiguriert werden.
Weitere Informationen zum Überwachen des Abonnementstatusspeichers finden Sie unter Abonnementleistung. Informationen zu optimalen Vorgehensweisen für Abonnements finden Sie unter Optimale Vorgehensweisen für das Erstellen von ThingWorx Lösungen auf einen Blick.
Eine Zusammenfassung zur Verwendung von Multi-Ereignis-Abonnements und sortierten Abonnements finden Sie unter Multi-Ereignis-Abonnements in ThingWorx 9.5.
Der folgende Abschnitt mit Anwendungsfällen enthält Codebeispiele, die die Leistungsfähigkeit von Multi-Ereignissen und von Funktionen von sortierten Abonnements und Abonnements mit Status demonstrieren, einschließlich Anwendungsfällen im Hinblick auf die Behandlung von Kollisionen bei Zeitfenster- und Eigenschaftsaktualisierungen.
Anwendungsfälle
Anwendungsfall 1 
// Usecase 1: alert will be triggered if voltage is higher than 118V
// Check if the voltage value exceeds 118 and trigger the appropriate action.
if (events["Me_DataChange_useCase1_Voltage"].eventData.newValue.value > 118){
logger.warn("Use-Case 1 subscription has been triggered");
}
Anwendungsfall 2 
// Usecase 2: alert will be triggered if voltage is higher than 118V and current is lower than 2A

//********************** Default initialization ****************************************
if (thisSub.JSONState.Voltage === undefined)
thisSub.JSONState.Voltage = me.useCase2_Voltage; // default value from VTQ
if (thisSub.JSONState.Current === undefined)
thisSub.JSONState.Current = 0; // default value 0
//*************************************************************************************
//************************* storing the values in JSONState from eventData ***************
var aliasArray = Object.keys(events.dataShape.fields);
for (var i=0; i < aliasArray.length; i ++ ){
if (aliasArray[i] === "Me_DataChange_useCase2_Current")
// Assign the new current value to the state
thisSub.JSONState.Current = events["Me_DataChange_useCase2_Current"].eventData.newValue.value;
if (aliasArray[i] === "Me_DataChange_useCase2_Voltage") {
// Assign the new voltage value to the state
thisSub.JSONState.Voltage = events["Me_DataChange_useCase2_Voltage"].eventData.newValue.value;
}
}
//******************************************************************************************
// Check if both voltage and current meet the conditions - voltage is higher than 118V and current is lower than 2A
if (thisSub.JSONState.Voltage > 118 &&
thisSub.JSONState.Current < 2) {
logger.warn("Use-Case 2 alert !!!, Voltage is: "+thisSub.JSONState.Voltage+" and Current is: "+thisSub.JSONState.Current);
}
else
logger.warn("Use-Case 2 NO alert, Voltage is: "+thisSub.JSONState.Voltage+" and Current is: "+thisSub.JSONState.Current);
Anwendungsfall 3 
// Usecase 3: alert will be triggered if voltage is higher than 118V and it has last more than 3 minutes.
// Timer will tick every X seconds

/*
When the DataChange event causing the subscription to run,
The value of Voltage will be checked:
1. If it would be above 118, timer would start running in case its undefined.
2. If it would be below or equal to 118, timer would back to undefined.
In case time has gotten to 3 minutes (180000MS), subscription would be triggered.
*/
// Getting the name of the event that triggered the subscription
let aliasArray = Object.keys(events.dataShape.fields);
if (aliasArray[0] === "Me_DataChange_useCase3_Voltage") {
// Valtage was changed
// We want to manage thisSub.JSONState.StartTime only. We don't save the Valtage in state

var Voltage = events[aliasArray[0]].eventData.newValue.value;

if( thisSub.JSONState.StartTime === undefined ) {
if( Voltage > 118 ){
thisSub.JSONState.StartTime = Date.now();
logger.warn("Voltage = " + Voltage + " Start time was set");
}
} else{
if( Voltage <= 118 ){
delete thisSub.JSONState.StartTime; // this will set to undefine
logger.warn("Voltage = " + Voltage + " Start time was unset");
}
}
}
else{
// We are in timer event
if( thisSub.JSONState.StartTime !== undefined && Date.now() - thisSub.JSONState.StartTime > 40000 )
logger.warn("Use-Case 3 timer was triggered - Alert !!! thisSub.JSONState.StartTime = " + thisSub.JSONState.StartTime);
else
logger.warn("Use-Case 3 timer was triggered - no Alert. thisSub.JSONState.StartTime = " + thisSub.JSONState.StartTime);
}

Anwendungsfall 4 
// Usecase 4: Input 32 bit integer will be translated into 32 status properties if there is any change,
// and alert on status property potentially will be triggered.

logger.warn("Use-Case 4 subscription has been triggered");
Anwendungsfall 5 
// Usecase 5: Alert will be triggered only on Error code which has severity level 1 or 2,
// where Error severity is defined in a relational table
// Get reference to the data table
var dataTable = Things["useCase5_DataTable"];
// Get the new value of ErrorCode from the event
var ErrorCodeValue = events["Me_DataChange_useCase5_ErrorCode"].eventData.newValue.value;
// Set parameters for querying the data table
var params = {
maxItems: 100, // Maximum number of entries to retrieve
source: undefined, // Filter entries by a specific source if needed
values: undefined, // Filter entries by specific column values if needed
orderBy: undefined // Order the retrieved entries by a specific column if needed
};
// Query the data table entries
var queryResult = dataTable.QueryDataTableEntries(params);
var rows = queryResult.rows;
// Iterate through the rows and check for matching ErrorCode and severity level
for (var i = 0; i < rows.length; i++) {
var thisRow = rows[i];

// Check if the ErrorCode matches the given ErrorCodeValue
if (thisRow.ErrorCode == ErrorCodeValue) {
// Check if the Severity is either 1 or 2
if (thisRow.Severity == 1 || thisRow.Severity == 2) {
// Log a warning message indicating the matched ErrorCode and its Severity
logger.warn("Error code: " + thisRow.ErrorCode + " has a severity of: " + thisRow.Severity + ". Use-Case 5 subscription has been triggered.");
}
}
}
Anwendungsfall 6 
// Usecase 6: Alert will be trigged if sum of product count is less than 10 in past 10 minutes
// Timer will tick every 1 seconds (1000MS)

// Getting the name of the event that triggered the subscription
var alias = events.dataShape.fields;
// Check if the alias matches "Me_DataChange_useCase6_ProductCount"
if (alias == "Me_DataChange_useCase6_ProductCount") {
// Check if the productCountDict exists in the state
if (thisSub.JSONState.productCountDict != undefined) {
// Delete entries older than 10 minutes
deleteOlderThan10Minutes();
// Add a new entry with the current timestamp and the new value from the event
thisSub.JSONState.productCountDict[Date.now().toString()] = events[alias].eventData.newValue.value;
} else {
// Initialize the productCountDict as an empty object and add a new entry
thisSub.JSONState.productCountDict = {};
thisSub.JSONState.productCountDict[Date.now().toString()] = events[alias].eventData.newValue.value;
}
} else {
// Check if the startTime is undefined and set it to the current time
if (thisSub.JSONState.startTime == undefined) {
thisSub.JSONState.startTime = Date.now();
}
// Check if the productCountDict exists in the state
if (thisSub.JSONState.productCountDict != undefined) {
// Delete entries older than 10 minutes
deleteOlderThan10Minutes();
// Check if an alert should be triggered and log a warning message
if (shouldTriggerAlert()) {
logger.warn("Use-Case 6 subscription has been triggered");
}
}
}
// Function to delete entries older than 10 minutes from productCountDict
function deleteOlderThan10Minutes() {
var keys = Object.keys(thisSub.JSONState.productCountDict);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
// Check if the time difference is greater than 10 minutes (600,000 milliseconds)
if (Date.now() - parseInt(key) > 600000) {
delete thisSub.JSONState.productCountDict[key];
}
}
}
// Function to check if an alert should be triggered
function shouldTriggerAlert() {
// Check if the sum of product count is less than 10 and if the time difference is greater than 10 minutes
if (productCountDictSUM() < 10 && Date.now() - thisSub.JSONState.startTime > 600000) {
return true;
} else {
return false;
}
}
// Function to calculate the sum of values in productCountDict
function productCountDictSUM() {
var keys = Object.keys(thisSub.JSONState.productCountDict);
var sum = 0;
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = thisSub.JSONState.productCountDict[key];
sum += value;
}
return sum;
}
Anwendungsfall 7 
// Usecase 7: alert will be triggered if average current (5 minutes window) is higher than 2.5A,
// and it will be checked very 30 seconds.
// Timer will tick every 30 seconds (30000MS)
// Getting the name of the event that triggered the subscription
var alias = events.dataShape.fields;
// Checking if the data alias matches the expected value
if (alias == "Me_DataChange_useCase7_Current") {
// If the data dictionary exists, update it with the new current value
if (thisSub.JSONState.currentDict != undefined) {
deleteOlderThan5Minutes();
thisSub.JSONState.currentDict[Date.now().toString()] = events[alias].eventData.newValue.value;
} else {
// If the data dictionary doesn't exist, create it and set the start time
thisSub.JSONState.currentDict = {};
thisSub.JSONState.currentDict[Date.now().toString()] = events[alias].eventData.newValue.value;
}
} else {
if(thisSub.JSONState.startTime == undefined){
thisSub.JSONState.startTime = Date.now();
}
// If the data alias doesn't match, perform data management and potential alert triggering
if (thisSub.JSONState.currentDict != undefined) {
// Delete entries in the data dictionary older than 5 minutes
deleteOlderThan5Minutes();
// Check if conditions for triggering the alert are met
if (shouldTriggerAlert()) {
logger.warn("Use-Case 7 subscription has been triggered");
}
}
}
// Function to delete data entries older than 5 minutes
function deleteOlderThan5Minutes() {
var keys = Object.keys(thisSub.JSONState.currentDict);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (Date.now() - parseInt(key) > 300000) {
delete thisSub.JSONState.currentDict[key];
}
}
}
// Function to determine if the alert should be triggered
// Only in case 5 minutes has been passed, and AVG is higher then 2.5
function shouldTriggerAlert() {
if (currentDictAVG() > 2.5 && Date.now() - thisSub.JSONState.startTime > 300000) {
return true;
} else {
return false;
}
}
// Function to calculate the average of currentDict
function currentDictAVG() {
var keys = Object.keys(thisSub.JSONState.currentDict);
var sum = 0;
var count = 0;
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = thisSub.JSONState.currentDict[key];
sum += value;
count++;
}
var average = sum / count;
return average;
}
////at any data change i sum the current
////when 30 seconds has been passed i calculate AVG
//
//
//
////change property event:
//// check if there is any value older then 5, if so - delete. (can implement by get keys)
//// add the value to the dict with key of timestamp
////timer event:
//// check if there is any value older then 5, if so - delete. (can implement by get keys)
//// AVG calculation, and condition check. - not only per 30 seconds, but for all the data at 5 minutes
//
//// build function to: change if there is any value older then 5, if so - delete. (can implement by get keys)
//
// ** the first timer event will be the startTime point of the algorithm
Anwendungsfall 8 
// Usecase 8: the alert will be triggered when most recent value is greater than the previous 10 values
// Check if valuesArray state is undefined and initialize it
if (thisSub.JSONState.valuesArray === undefined) {
thisSub.JSONState.valuesArray = {};
}
// Check if valuesArray length is less than 10
let indexCounter = Object.keys(thisSub.JSONState.valuesArray).length;
if ( indexCounter < 3) {
// Add the new value to valuesArray
thisSub.JSONState.valuesArray["i" + indexCounter] = events["Me_DataChange_useCase8_prop"].eventData.newValue.value;
} else {
// The valuesArray is full

var max = 0;
// Find the maximum value in valuesArray
for (let i = 0; i < Object.keys(thisSub.JSONState.valuesArray).length; i++) {
if (thisSub.JSONState.valuesArray["i" + i] > max)
max = thisSub.JSONState.valuesArray["i" + i];
}

// Check if the new value is greater than the maximum value
if (events["Me_DataChange_useCase8_prop"].eventData.newValue.value > max)
logger.warn("Use-Case 8 Alert!!! max is " + events["Me_DataChange_useCase8_prop"].eventData.newValue.value);
// Shift the values in valuesArray by one position
for (var j = 0; j < parseInt((Object.keys(thisSub.JSONState.valuesArray)).length) - 1; j++) {
thisSub.JSONState.valuesArray["i" + j] = thisSub.JSONState.valuesArray["i" + (j + 1)];
}
// Add the new value to valuesArray using the current indexCounter
thisSub.JSONState.valuesArray["i" + (indexCounter - 1)] = events["Me_DataChange_useCase8_prop"].eventData.newValue.value;
}
logger.warn("Use-Case 8 End. valuesArray = " + thisSub.JSONState.valuesArray);
War dies hilfreich?