Skip to content

Commit

Permalink
Implement new tests
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Werner <[email protected]>
  • Loading branch information
fhg-dw committed Jun 19, 2024
1 parent 38c81ad commit 972ff2c
Show file tree
Hide file tree
Showing 27 changed files with 941 additions and 229 deletions.
7 changes: 7 additions & 0 deletions conformance-tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
testResults/
build
.gradle/
.idea/
allure-results/
.venv/
testtool-out/
23 changes: 13 additions & 10 deletions conformance-tests/allowed-licenses.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
{
"allowedLicenses": [
{
"moduleLicense": "The Apache License, Version 2.0",
"moduleVersion": "1.12.0",
"moduleName": "com.beanit:asn1bean"
"moduleLicense": "The Apache License, Version 2.0"
},
{
"moduleLicense": "The Apache License, Version 2.0",
"moduleVersion": "1.9.0",
"moduleName": "com.beanit:iec61850bean"
"moduleLicense": "Apache License, Version 2.0"
},
{
"moduleLicense": "Apache License, Version 2.0",
"moduleVersion": "2.16.1",
"moduleName": "commons-io:commons-io"
"moduleLicense": "Apache 2.0"
},
{
"moduleLicense": "The Apache Software License, Version 2.0"
},
{
"comment": "MIT can be included into Apache 2.0 according https://www.apache.org/legal/resolved.html#category-a (31.1.23)",
Expand All @@ -30,6 +27,12 @@
"comment": "uses dual license, we use Eclipse Public License - v 1.0 which can be included into Apache 2.0 according to https://www.apache.org/legal/resolved.html#category-b (31.1.23)",
"moduleVersion": "1.5.6",
"moduleName": "ch.qos.logback:logback-classic"
},
{
"comment": "MIT can be included into Apache 2.0 according https://www.apache.org/legal/resolved.html#category-a (03.06.2024)",
"moduleLicense": "MIT-0",
"moduleVersion": "1.0.4",
"moduleName": "org.reactivestreams:reactive-streams"
}
]
}
}
3 changes: 3 additions & 0 deletions conformance-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@ dependencies {
implementation 'ch.qos.logback:logback-classic:1.5.6'
implementation group: 'org.slf4j', name: 'slf4j-api', version: '2.0.13'

implementation("com.fasterxml.jackson.core:jackson-databind:2.17.1")

implementation group: 'com.beanit', name: 'iec61850bean', version: '1.9.0'
implementation group: 'commons-io', name: 'commons-io', version: '2.16.1'

testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.2'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.2'

implementation("com.hivemq:hivemq-mqtt-client:1.3.3")
}

jar {
Expand Down
1 change: 0 additions & 1 deletion conformance-tests/checkLicense.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/bin/bash

# Skript to check proper licensing setup

./gradlew clean checkLicense
Expand Down
2 changes: 0 additions & 2 deletions conformance-tests/conformance-tests/.gitignore

This file was deleted.

9 changes: 9 additions & 0 deletions conformance-tests/run-all-tests.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
#!/bin/bash

./gradlew checkLicense

echo TODO: start the below as screen sessions, stop after tests!
echo "cd ../deployments/n61850-smqtt-ubuntu2004/ && docker-compose up"
echo "cd ../hedera-61850-gateway/ && docker-compose up"
echo mosquitto_sub -t fledge/south-schedule -i schedule-subscriber
echo mosquitto_sub -t fledge/south-command -i cmd-subscriber

./gradlew test
echo "All tests run. Please see the logs in testResults/ directory for details"
3 changes: 0 additions & 3 deletions conformance-tests/run-demo-tests.sh

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

package org.openmuc.fnn.steuerbox;

import com.fasterxml.jackson.databind.ObjectMapper;

import javax.xml.parsers.DocumentBuilderFactory;

/**
Expand All @@ -22,7 +24,13 @@ public class Context {

private static final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

private static final ObjectMapper objectMapper = new ObjectMapper();

public static DocumentBuilderFactory getDocumentBuilderFactory() {
return factory;
}

public static ObjectMapper getObjectMapper() {
return objectMapper;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
package org.openmuc.fnn.steuerbox.models;

import com.beanit.iec61850bean.ServiceError;
import org.openmuc.fnn.steuerbox.IEC61850Utility;
import org.openmuc.fnn.steuerbox.scheduling.ScheduleDefinitions;
import org.openmuc.fnn.steuerbox.scheduling.ScheduleType;
import org.openmuc.fnn.steuerbox.testutils.IEC61850Utility;

import java.io.IOException;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.openmuc.fnn.steuerbox.mqtt;

import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static org.openmuc.fnn.steuerbox.mqtt.ParsingUtil.parseMqttMessages;

/**
* A command payload representing a control signal intended for the EMS.
* <p>
* Only relevant parameters will be parsed, most of the JSON fields are FLEDGE defaults and not relevant for the
* ReLevENT use cases.
*/
public class Command {
public Command(long epochSecond, double controlValue) {
this.epochSecond = epochSecond;
this.controlValue = controlValue;
}

public final long epochSecond;
public final double controlValue;

public static Command fromJsonString(String command)
throws ParserConfigurationException, IOException, SAXException {
List<Map.Entry<Instant, Double>> entries = parseMqttMessages(command);
if (entries.size() != 1) {
throw new IllegalArgumentException(
"Expected exactly 1 element to be returned. Are you trying to parse a schedule as a command?");
}
Map.Entry<Instant, Double> instantDoubleEntry = entries.get(0);
return new Command(instantDoubleEntry.getKey().getEpochSecond(), instantDoubleEntry.getValue());
}

@Override
public String toString() {
return "Command{" + "epochSecond=" + epochSecond + "(equivalent to " + Instant.ofEpochSecond(epochSecond)
+ "), controlValue=" + controlValue + '}';
}

@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Command command = (Command) o;
return epochSecond == command.epochSecond && Double.compare(controlValue, command.controlValue) == 0;
}

@Override
public int hashCode() {
return Objects.hash(epochSecond, controlValue);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.openmuc.fnn.steuerbox.mqtt;

import com.fasterxml.jackson.databind.JsonNode;
import org.openmuc.fnn.steuerbox.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Duration;
import java.time.Instant;
import java.util.AbstractMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* Utilities to parse JSON messages
*/
public class ParsingUtil {

private final static Logger log = LoggerFactory.getLogger(ParsingUtil.class);

private ParsingUtil() {
// prevent calling constructor: utility class with no members
}

/**
* Deserialize a schedule for communication with HEDERA-61850-interface.
* <p>
* Will use a switch to not communicate the request to HEDERA but instead immediately forward it to the IEC 61850
* FLEDGE scheduling server
*/
public static String scheduleToJson(List values, Duration interval, Instant start) {
ScheduleResolution scheduleResolution = ScheduleResolution.from(interval);
return String.format("{\"skipHedera\":true," //
+ "\"direction\":\"IMPORT\","//
+ "\"start\":{\"seconds\":%s,\"nanos\":%s},"//
+ "\"resolution\":\"%s\"," //
+ "\"values\":[%s]}", start.getEpochSecond(), start.getNano(), scheduleResolution.name(),
values.stream().map(Object::toString).collect(Collectors.joining(",")));
}

/**
* Utility to parse command or schedule parameters from a JSON message (command and schedules are very alike: a
* schedule is like an array of commands)
*/
static List<Map.Entry<Instant, Double>> parseMqttMessages(String commandOrScheduleJson) {
List<Map.Entry<Instant, Double>> entities = new LinkedList<>();
try {
Iterator<JsonNode> parameters = Context.getObjectMapper()
.readTree(commandOrScheduleJson)
.get("parameters")
.elements();
while (parameters.hasNext()) {
JsonNode parameter = parameters.next();
JsonNode apcTypNode = parameter.get("value").get("GTIC").get("ApcTyp");
JsonNode tNode = apcTypNode.get("t");
long epochSeconds = tNode.get("SecondSinceEpoch").longValue();
double controlValue = apcTypNode.get("ctlVal").asDouble();
long nanos = 0; // could be created from FractionOfSecond, but testing does not make sense on that base..
log.trace("Ignoring FractionOfSecond, testing on that base does not make sense");
entities.add(new AbstractMap.SimpleEntry<>(Instant.ofEpochSecond(epochSeconds, nanos), controlValue));
}
return entities;
} catch (Exception e) {
throw new RuntimeException(
"Unable deserialize command from '" + commandOrScheduleJson + "': Required elements not found.", e);
}
}
}
Loading

0 comments on commit 972ff2c

Please sign in to comment.