diff --git a/.gitignore b/.gitignore index 9769c4d36bd07656530e40001472b458a17340d9..36cdad0353f377d56af524afa0d541ae825fd5d2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,7 @@ build/dependencyUpdates/ venv/ */out/ +*/build/ logs/ datasets/ +public/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 918f65ab8164ff97f01d7e37dc1fa86b7bfd3766..ba0ad31208703b6a7300a13bb620c855b7941747 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,10 @@ stages: -- build -- test -- report + - build + - test + - report + - ragdoc_build + - ragdoc_view + - publish variables: # Instruct Testcontainers to use the daemon of DinD. @@ -9,68 +12,114 @@ variables: # # Improve performance with overlayfs. # DOCKER_DRIVER: overlay2 GRADLE_OPTS: "-Dorg.gradle.daemon=false" - TEST_REPORTS: "/builds/OpenLicht/eraser/eraser-base/build/reports/tests/test/" - TEST_LOG: "/builds/OpenLicht/eraser/eraser-base/logs/eraser-test.log" - JACOCO_REPORT: "/builds/OpenLicht/eraser/eraser-base/build/reports/jacoco/test/jacocoTestReport.xml" - TESTCONTAINERS_RYUK_DISABLED: "true" + TEST_REPORTS: "*/build/test-results/**/TEST-*.xml" + TEST_LOG: "eraser-base/logs/eraser-test.log" + JACOCO_REPORT: "*/build/reports/jacoco/all-tests/jacoco*Report.xml" + # settings for influxdb + INFLUXDB_DB: "jastaddHistory" + INFLUXDB_USER: "root" + INFLUXDB_USER_PASSWORD: "root" before_script: - export GRADLE_USER_HOME=`pwd`/.gradle +cache: + paths: + - .gradle/wrapper + - .gradle/caches + build: - image: openjdk:8 - tags: - - docker + image: openjdk:11 stage: build script: - - ./gradlew --console=plain --build-cache assemble - cache: - key: "$CI_COMMIT_REF_NAME" - policy: push + - ./gradlew --console=plain assemble + artifacts: paths: - - build - - .gradle + - "eraser-base/src/gen" test: - image: openjdk:8 + image: openjdk:11 tags: - docker stage: test + services: + - name: "eclipse-mosquitto:1.6.12" + alias: "mqtt" + - name: "influxdb:1.8.4" + alias: "influx" + needs: + - build script: - - ./gradlew --continue --console=plain check jacocoTestReport - cache: - key: "$CI_COMMIT_REF_NAME" - policy: pull - paths: - - build - - .gradle + - ./gradlew --console=plain --info allTests artifacts: when: always paths: - $TEST_LOG - - $TEST_REPORTS - $JACOCO_REPORT + reports: + junit: $TEST_REPORTS coverage: image: python:3.7.1-alpine - tags: - - docker stage: report - dependencies: - - test + needs: + - test script: -# - ./gradlew --continue --console=plain -x test jacocoTestReport - pip install --user untangle - python print-coverage.py coverage: "/Covered (\\d{1,3}\\.\\d{2}%) of instructions for all projects\\./" - cache: - key: "$CI_COMMIT_REF_NAME" - policy: pull - paths: - - build - - .gradle allow_failure: true artifacts: when: always paths: - $JACOCO_REPORT + +ragdoc_build: + image: + name: "git-st.inf.tu-dresden.de:4567/jastadd/ragdoc-builder" + entrypoint: [""] + stage: ragdoc_build + needs: + - build + script: + - JAVA_FILES=$(find eraser-base/src/ -name '*.java') + - /ragdoc-builder/start-builder.sh -excludeGenerated -d data/ $JAVA_FILES + artifacts: + paths: + - "data/" + +ragdoc_view: + image: + name: "git-st.inf.tu-dresden.de:4567/jastadd/ragdoc-view:relations" + entrypoint: [""] + stage: ragdoc_view + needs: + - ragdoc_build + script: + - DATA_DIR=$(pwd -P)/data + - mkdir -p pages/docs/ragdoc + - OUTPUT_DIR=$(pwd -P)/pages/docs/ragdoc + - cd /ragdoc-view + - ( cd src/ && rm -rf data && ln -s $DATA_DIR ) + - /ragdoc-view/build-view.sh --output-path=$OUTPUT_DIR + - ls -lah $OUTPUT_DIR + artifacts: + paths: + - "pages/docs/ragdoc" + +pages: + image: python:3.8-buster + stage: publish + needs: + - test + - ragdoc_view + before_script: + - pip install -U mkdocs mkdocs-macros-plugin mkdocs-git-revision-date-localized-plugin + script: + - cd pages && mkdocs build + only: + - dev + - main + artifacts: + paths: + - "public" diff --git a/.gitmodules b/.gitmodules index e77282f413a7cf57a9ad0b61b13d7397531c013f..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "ragdoc-view"] - path = ragdoc-view - url = ../../jastadd/ragdoc-view/ diff --git a/README.md b/README.md index be3f3b866e5a966523c8fa8f54b086a1ae181049..af4d2c9d8bd6f40b3984c3502c200a0d6115a711 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ There are the following subprojects in this repository: - Base - - **eraser-base**: The core part of the framework containing the [grammar](/../../blob/master/eraser-base/src/main/jastadd/main.relast), various attributes, the [parser](/../../blob/master/eraser-base/src/main/jastadd/eraser.parser) for model specification files, and Java classes for the [openHAB communication](/../../tree/master/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2) via [MQTT](/../../blob/master/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2/mqtt/MQTTUpdater.java) + - **eraser-base**: The core part of the framework containing the [grammar](/../../blob/main/eraser-base/src/main/jastadd/main.relast), various attributes, the [parser](/../../blob/main/eraser-base/src/main/jastadd/eraser.parser) for model specification files, and Java classes for the [openHAB communication](/../../tree/main/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2) via [MQTT](/../../blob/main/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2/mqtt/MQTTUpdater.java) - Utility - **commons.color**: Utilities for converting color spaces (XYZ, RGB, HSB) - **eraser.rest** and **eraser.spark**: REST-API to communicate with the framework @@ -25,15 +25,6 @@ There are the following subprojects in this repository: This project uses Gradle as the build tool. For detailed information, see [setup guidelines](/../../wikis/setup) -## Running the demo - -There is a demo project, as explained [above](#overview) to showcase the framework. -To start it, run `./gradlew :skywriter-hue-integration:run` - -This demo is intended to run with a pre-configured openHAB instance and has some hard-coded settings. -It will first try to connect to an MQTT broker (with a hard-coded IP) to listen on changes of item states. -There is one rule implemented, namely to listen on changes of the item `skywriter_xyz` expecting MQTT messages of the form `XVALUE,YVALUE,ZVALUE`, and to change to state of another item `iris1_item` representing the HSB value of a Hue Iris. - ## Trivia The name *Eraser* is a small pun on the term *eRACR*, which is an extension of *RACR* for event recognition. It was chosen to ease the search for a good repository image as it is based on a [film](https://en.wikipedia.org/wiki/Eraser_(film)). diff --git a/benchmark/build.gradle b/benchmark/build.gradle index dfcc1d42edbaf81885917bd8b31573056ba29dbb..d4f5bd791f9dce5fc138d9f2ecd2fe9673beafde 100644 --- a/benchmark/build.gradle +++ b/benchmark/build.gradle @@ -1,36 +1,14 @@ -apply plugin: 'application' - -dependencies { - compile project(':eraser-base') - compile project(':feedbackloop.learner_backup') - compile project(':datasets') - testImplementation 'junit:junit:4.12' - // https://mvnrepository.com/artifact/org.hamcrest/java-hamcrest - // https://mvnrepository.com/artifact/org.hamcrest/hamcrest-core - testCompile group: 'org.hamcrest', name: 'hamcrest-core', version: '1.3' - implementation group: 'com.opencsv', name: 'opencsv', version: '4.1' - // https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient - compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.0-alpha4' - testCompile group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: '2.11.2' - // https://mvnrepository.com/artifact/org.apache.httpcomponents/fluent-hc - compile group: 'org.apache.httpcomponents', name: 'fluent-hc', version: '4.2.1' - // https://mvnrepository.com/artifact/junit/junit - testCompile group: 'junit', name: 'junit', version: '4.4' - +plugins { + id 'eraser.java-application-conventions' } -run { - mainClassName = 'de.tudresden.inf.st.eraser.benchmark.Main' - standardInput = System.in - if (project.hasProperty("appArgs")) { - args Eval.me(appArgs) - } +dependencies { + implementation project(':eraser-base') + implementation project(':feedbackloop.learner_backup') + implementation group: 'com.opencsv', name: 'opencsv', version: "${openscv_version}" + implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: "${apache_httpcomponents_version}" + implementation group: 'org.apache.httpcomponents', name: 'fluent-hc', version: "${apache_httpcomponents_version}" + testImplementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: "${log4j_version}" } -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} +application.mainClass = 'de.tudresden.inf.st.eraser.benchmark.Main' diff --git a/benchmark/src/main/java/de/tudresden/inf/st/eraser/benchmark/Benchmark.java b/benchmark/src/main/java/de/tudresden/inf/st/eraser/benchmark/Benchmark.java index 2c1d64d5be93238589167e0c474218d3b22a952f..a8a9e95b0b173dddef7eb3f1c9cd524525142ecb 100644 --- a/benchmark/src/main/java/de/tudresden/inf/st/eraser/benchmark/Benchmark.java +++ b/benchmark/src/main/java/de/tudresden/inf/st/eraser/benchmark/Benchmark.java @@ -7,8 +7,11 @@ import org.apache.http.entity.ContentType; import org.apache.http.util.EntityUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; + import java.io.File; +import java.io.FileNotFoundException; import java.io.FileReader; +import java.io.IOException; import java.util.Arrays; public class Benchmark { @@ -38,24 +41,17 @@ public class Benchmark { private static final Logger logger = LogManager.getLogger(Benchmark.class); private static final String ERASER_ITEM_URI = "http://localhost:4567/model/items/"; //TODO ITEM_NAME HAS TO BE DISCUSSED - private static final String[] ACTIVITY_ITEM_NAME = { - "m_accel_x", "m_accel_y", "m_accel_z", "m_rotation_x", - "m_rotation_y", "m_rotation_z", "w_accel_x", "w_accel_y", - "w_accel_z", "w_rotation_x", "w_rotation_y", "w_rotation_z"}; - private static final String[] PREFERENCE_ITEM_NAME = { - "w_brightness" - }; - private static boolean flag2 = true; + private String[] ACTIVITY_ITEM_NAME; + private String[] PREFERENCE_ITEM_NAME; //csv_type is activity or preference Benchmark(String a_csv_file_path, String p_csv_file_path) { this.a_csv_file_path = a_csv_file_path; this.p_csv_file_path = p_csv_file_path; } - void start(){ + int TIME_PERIOD = 1000; String PREFERENCE_URL = "http://localhost:4567/model/items/iris1_item/state"; String ACTIVITY_URL = "http://localhost:4567/activity/current"; - int TIME_PERIOD = 5000; File a_file; File p_file; FileReader a_file_reader; @@ -64,7 +60,6 @@ public class Benchmark { CSVReader p_csv_reader; String[] a_next_record; String[] p_next_record; - boolean flag1; a_file=new File(a_csv_file_path); p_file= new File(p_csv_file_path); try { @@ -72,41 +67,45 @@ public class Benchmark { p_file_reader=new FileReader(p_file); a_csv_reader = new CSVReader(a_file_reader); p_csv_reader = new CSVReader(p_file_reader); - while ((((a_next_record = a_csv_reader.readNext())!= null) && flag2)){ - try{Thread.sleep(TIME_PERIOD);}catch (InterruptedException e){e.printStackTrace();} - String[] values = Arrays.copyOf(a_next_record,12); - setNewValue(values, ACTIVITY_ITEM_NAME,"activity"); - HttpResponse response= Request.Get(ACTIVITY_URL).execute().returnResponse(); - String status = response.getStatusLine().toString(); - if(status.contains("200")){ - flag1 = true; - logger.info("activity should be (read direct from CSV): " + a_next_record[12]); - logger.info(EntityUtils.toString(response.getEntity())); - logger.info("get activity from web server: response 200 ok"); - }else{ - flag1 = false; - flag2 = false; - logger.info("can not get the activity from the web server"); + int i = 0; + while ((a_next_record = a_csv_reader.readNext())!= null){ + if( i==0 ){ + getCSVHeader(Arrays.copyOf(a_next_record,12),"a"); + i++; } - while((((p_next_record = p_csv_reader.readNext()) != null) && flag1)) { - try{Thread.sleep(TIME_PERIOD);}catch (InterruptedException e){e.printStackTrace();} - String[] values1 = Arrays.copyOf(p_next_record,2); - setNewValue(values1, PREFERENCE_ITEM_NAME,"preference"); - HttpResponse response1= Request.Get(PREFERENCE_URL).execute().returnResponse(); - String status1=response1.getStatusLine().toString(); - if(status1.contains("200")){ - flag2 = true; - logger.info("get the iris1_item preference from web server: response 200 ok, value is: "+EntityUtils.toString(response1.getEntity())); - }else {flag2 = false; - logger.info("can not get the iris1_item from the web server");} - break; + else{ + String[] values = Arrays.copyOf(a_next_record,12); + setNewValue(values, ACTIVITY_ITEM_NAME,"activity"); + int j = 0; + while(((p_next_record = p_csv_reader.readNext()) != null)) { + if( j == 0 ){ + getCSVHeader(Arrays.copyOf(p_next_record,2),"p"); + j++; + }else{ + String[] values1 = Arrays.copyOf(p_next_record,2); + setNewValue(values1, PREFERENCE_ITEM_NAME,"preference"); + // wait for 1s + try{Thread.sleep(TIME_PERIOD);}catch (InterruptedException e){e.printStackTrace();} + logger.warn("checking/comparing the result from web server with the CSV"); + HttpResponse response= Request.Get(ACTIVITY_URL).execute().returnResponse(); + HttpResponse response1= Request.Get(PREFERENCE_URL).execute().returnResponse(); + //check response + checkResult(response, a_next_record,null); + checkResult(response1, p_next_record,a_next_record[12]); + logger.warn("checking/comparing finised"); + //logger.info("checking/comparing finished"); + break; + } + } } + } } catch (Exception e){ e.printStackTrace(); } } + private void setNewValue(String[] values, String[] name, String file_typ){ if(file_typ.equals("activity")) { @@ -119,7 +118,7 @@ public class Benchmark { .execute().returnResponse(); String status=httpResponse.getStatusLine().toString(); if(status.contains("200")){ - logger.info("put activity input name: "+name[i]+", value: "+value+"to web server: response 200 ok"); + logger.info("put activity input name: "+ name[i] +", value: "+ value +" to web server: response 200 ok"); }else{ logger.info("can not put activity inputs to rest server"); } @@ -134,13 +133,71 @@ public class Benchmark { HttpResponse httpResponse = Request.Put(uri) .bodyString(values[1], ContentType.TEXT_PLAIN) .execute().returnResponse(); - String put_response=httpResponse.getStatusLine().toString(); - if (put_response.contains("200")){logger.info("put w_brightness to web server: response 200 ok");}else{ + String put_response = httpResponse.getStatusLine().toString(); + if (put_response.contains("200")){logger.info("put preference input name: w_brightness, value : " + + values[1]+" to web server: response 200 ok");} else{ logger.info("can not put w_brightness to rest server"); } }catch (Exception e){ e.printStackTrace(); } } + + } + + private void getCSVHeader(String[] a_values,String csv_typ){ + if (csv_typ.equals("a")){ + ACTIVITY_ITEM_NAME = a_values; + }else{ + PREFERENCE_ITEM_NAME = a_values; + } + } + private void checkResult(HttpResponse response,String[] record, String activity){ + int status = response.getStatusLine().getStatusCode(); + if(status == 200 && record.length == 13){ + logger.info("activity should be (read direct from CSV): " + record[12]); + try { + logger.info("get activity from web server: response 200 ok, value is: "+ + EntityUtils.toString(response.getEntity())); + }catch (IOException e){ + e.printStackTrace(); + } + }else if(status ==200 && record.length == 4){ + logger.info("preference should be (read direct from CSV): " + comparePreferenceOutput(record,activity)); + try { + logger.info("get the iris1_item preference from web server: response 200 ok, value is: " + + EntityUtils.toString(response.getEntity())); + }catch (IOException e){ + e.printStackTrace(); + } + + }else if(status != 200 &&record.length == 13){ + logger.info("can not get the activity from the web server"); + + } + else if(status != 200 && record.length == 4){ + logger.info("can not get the iris1_item from the web server"); + }else { + logger.info("unknown check"); + } + } + + private String comparePreferenceOutput(String[] record, String activity){ + String output = null; + String brightness=record[1]; + File file = new File(p_csv_file_path); + FileReader reader; + CSVReader csv_reader; + try{ + reader = new FileReader(file); + csv_reader=new CSVReader(reader); + String[] next_record; + while ((next_record = csv_reader.readNext())!= null){ + if (next_record[0].equals(activity) && next_record[1].equals(brightness)){ + output = next_record[2]+",100,"+next_record[3]; + } + } + }catch (Exception e){e.printStackTrace();} + return output; } } \ No newline at end of file diff --git a/benchmark/src/main/java/de/tudresden/inf/st/eraser/benchmark/Main.java b/benchmark/src/main/java/de/tudresden/inf/st/eraser/benchmark/Main.java index ac7536cae53749132be31ebe028e5ec0e55c30f4..640ef4e751e888f4e4ee13fe73868c794e3337a4 100644 --- a/benchmark/src/main/java/de/tudresden/inf/st/eraser/benchmark/Main.java +++ b/benchmark/src/main/java/de/tudresden/inf/st/eraser/benchmark/Main.java @@ -5,7 +5,7 @@ public class Main { public static void main(String[] args) { String A_CSV_FILE_PATH = "../datasets/backup/activity_data.csv"; String P_CSV_FILE_PATH = "../datasets/backup/preference_data.csv"; - Benchmark benchmark=new Benchmark(A_CSV_FILE_PATH,P_CSV_FILE_PATH); + Benchmark benchmark = new Benchmark(A_CSV_FILE_PATH,P_CSV_FILE_PATH); benchmark.start(); } } diff --git a/build.gradle b/build.gradle deleted file mode 100644 index f1f3b408f121f388615eca5d85cd3f128128f17c..0000000000000000000000000000000000000000 --- a/build.gradle +++ /dev/null @@ -1,46 +0,0 @@ -plugins { - id "com.github.ben-manes.versions" version "0.20.0" -} - -allprojects { - group = 'de.tudresden.inf.st' - version = '0.1' -} - -subprojects { - apply plugin: 'java' - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - - task packageSources(type: Jar) { - classifier = 'sources' - from sourceSets.main.allSource - } - - artifacts.archives packageSources - configurations { - testArtifacts.extendsFrom testRuntime - } - - task testJar(type: Jar) { - classifier "test" - from sourceSets.test.output - } - - artifacts { - testArtifacts testJar - } - - repositories { - mavenCentral() - } - - dependencies { - compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.11.2' - compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.2' - testCompile group: 'junit', name: 'junit', version: '4.12' - testCompile group: 'org.hamcrest', name: 'hamcrest-junit', version: '2.0.0.0' - } - -} diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..95bfe53999d61b768da82463f837ec9f5254e997 --- /dev/null +++ b/buildSrc/build.gradle @@ -0,0 +1,11 @@ +plugins { + id 'groovy-gradle-plugin' +} + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation 'com.github.ben-manes:gradle-versions-plugin:0.36.0' +} diff --git a/buildSrc/src/.gitignore b/buildSrc/src/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..9f2a078806e1b8600d753f53ee7437b168a1b55b --- /dev/null +++ b/buildSrc/src/.gitignore @@ -0,0 +1,2 @@ +build/ +.gradle/ diff --git a/buildSrc/src/main/groovy/eraser.java-application-conventions.gradle b/buildSrc/src/main/groovy/eraser.java-application-conventions.gradle new file mode 100644 index 0000000000000000000000000000000000000000..fba18dcf1f122aa46fd411481ed8d5072c8cff26 --- /dev/null +++ b/buildSrc/src/main/groovy/eraser.java-application-conventions.gradle @@ -0,0 +1,4 @@ +plugins { + id 'eraser.java-common-conventions' + id 'application' +} diff --git a/buildSrc/src/main/groovy/eraser.java-common-conventions.gradle b/buildSrc/src/main/groovy/eraser.java-common-conventions.gradle new file mode 100644 index 0000000000000000000000000000000000000000..4e7d518113abd5cf84daf9428e4c30db1c02310c --- /dev/null +++ b/buildSrc/src/main/groovy/eraser.java-common-conventions.gradle @@ -0,0 +1,62 @@ +plugins { + id 'java' + id 'idea' + id 'com.github.ben-manes.versions' + id 'jacoco' +} + +repositories { + mavenCentral() + maven { + name "gitlab-maven" + url "https://git-st.inf.tu-dresden.de/api/v4/groups/jastadd/-/packages/maven" + } +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + +dependencies { + implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: "${log4j_version}" + implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: "${log4j_version}" + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: "${jupiter_version}" + testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.18.1' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: "${jupiter_version}" +} + +tasks.named('test') { + useJUnitPlatform { + excludeTags 'mqtt | influx' + } +} + +tasks.withType(JavaCompile) { + options.deprecation = true +} + +task allTests(type: Test, dependsOn: testClasses) { + description = 'Run every test' + group = 'verification' + + useJUnitPlatform { + } +} + +// extension for all Test tasks similar to https://stackoverflow.com/a/57330075/2493208 +jacocoTestReport { + executionData tasks.withType(Test).findAll { it.state.executed } + getExecutionData().setFrom(fileTree(buildDir).include("/jacoco/*.exec")) + + reports { + xml.enabled true + xml.destination(file("${jacoco.reportsDir}/all-tests/jacocoAllTestReport.xml")) + html.enabled false + } +} + +tasks.withType(Test) { + finalizedBy jacocoTestReport +} diff --git a/buildSrc/src/main/groovy/eraser.java-jastadd-conventions.gradle b/buildSrc/src/main/groovy/eraser.java-jastadd-conventions.gradle new file mode 100644 index 0000000000000000000000000000000000000000..6d9382d37a22c7a1451c3ff6d4e3f5063c817766 --- /dev/null +++ b/buildSrc/src/main/groovy/eraser.java-jastadd-conventions.gradle @@ -0,0 +1,9 @@ +plugins { + id 'eraser.java-common-conventions' + id 'java-library' + id 'jastadd' +} + +dependencies { + api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' +} diff --git a/commons.color/build.gradle b/commons.color/build.gradle index 30ce209c40610e390f709d9ec5a36c7324543c23..7339050832911cbad2e2b88d36875b8561a724b7 100644 --- a/commons.color/build.gradle +++ b/commons.color/build.gradle @@ -1,11 +1,8 @@ -dependencies { - compile group: 'org.apache.commons', name: 'commons-math3', version: '3.6.1' +plugins { + id 'eraser.java-application-conventions' + id 'java-library' } -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } +dependencies { + api group: 'org.apache.commons', name: 'commons-math3', version: '3.6.1' } diff --git a/commons.color/src/main/java/de/tudresden/inf/st/eraser/commons/color/ColorUtils.java b/commons.color/src/main/java/de/tudresden/inf/st/eraser/commons/color/ColorUtils.java index 236f5d38120544ab81c9329c40ecc922215ad13e..21d53dab8d2d70da2bc696ffd63bb8b6c3a3efbd 100644 --- a/commons.color/src/main/java/de/tudresden/inf/st/eraser/commons/color/ColorUtils.java +++ b/commons.color/src/main/java/de/tudresden/inf/st/eraser/commons/color/ColorUtils.java @@ -11,16 +11,17 @@ import java.util.function.Consumer; * * @author rschoene - Initial contribution */ +@SuppressWarnings("WeakerAccess") public class ColorUtils { /** * Value class comprising three double values X, Y, Z ranging from 0 to 1. * The values represent coordinates. */ - public static class XYZvalues { + public static class ValuesXYZ { double x, y, z; - public static XYZvalues of(double x, double y, double z) { - XYZvalues result = new XYZvalues(); + public static ValuesXYZ of(double x, double y, double z) { + ValuesXYZ result = new ValuesXYZ(); result.x = x; result.y = y; result.z = z; @@ -32,22 +33,22 @@ public class ColorUtils { * Value class comprising three integral values R, G, B ranging from 0 to 255. * The values represent red, green and blue. */ - public static class RGBvalues { + public static class ValuesRGB { public int red; public int green; public int blue; - public static RGBvalues of(int red, int green, int blue) { - RGBvalues result = new RGBvalues(); + public static ValuesRGB of(int red, int green, int blue) { + ValuesRGB result = new ValuesRGB(); result.red = red; result.green = green; result.blue = blue; return result; } - public static RGBvalues of(int[] rgb) { + public static ValuesRGB of(int[] rgb) { return of(rgb[0], rgb[1], rgb[2]); } /** Set all values to a minimum of zero. */ - public RGBvalues minZero() { + public ValuesRGB minZero() { red = Math.max(0, red); green = Math.max(0, green); blue = Math.max(0, blue); @@ -59,52 +60,52 @@ public class ColorUtils { * Value class comprising three double values H, S, B ranging from 0 to 1. * The values represent hue, saturation and brightness. */ - public static class HSBvalues { + public static class ValuesHSB { public double hue; public double saturation; public double brightness; - public static HSBvalues of(double hue, double saturation, double brightness) { - HSBvalues result = new HSBvalues(); + public static ValuesHSB of(double hue, double saturation, double brightness) { + ValuesHSB result = new ValuesHSB(); result.hue = hue; result.saturation = saturation; result.brightness = brightness; return result; } /** Set all values to a minimum of zero. */ - public HSBvalues minZero() { + public ValuesHSB minZero() { hue = Math.max(0, hue); saturation = Math.max(0, saturation); brightness = Math.max(0, brightness); return this; } - /** Convert to HSB with value range of 0..255 */ - public HSBvalues255 toIntegral() { - return HSBvalues255.of( - (int) Math.round(255 * hue), - (int) Math.round(255 * saturation), - (int) Math.round(255 * brightness) + /** Convert to the same HSB with integral values */ + public ValuesIntegralHSB toIntegral() { + return ValuesIntegralHSB.of( + (int) Math.round(360 * hue), + (int) Math.round(100 * saturation), + (int) Math.round(100 * brightness) ); } } /** - * Value class comprising three integral values H, S, B ranging from 0 to 255. + * Value class comprising three integral values H, S, B ranging from 0 either to 360 (H) or 100 (S and B). * The values represent hue, saturation and brightness. */ - public static class HSBvalues255 { + public static class ValuesIntegralHSB { public int hue; public int saturation; public int brightness; - public static HSBvalues255 of(int hue, int saturation, int brightness) { - HSBvalues255 result = new HSBvalues255(); + public static ValuesIntegralHSB of(int hue, int saturation, int brightness) { + ValuesIntegralHSB result = new ValuesIntegralHSB(); result.hue = hue; result.saturation = saturation; result.brightness = brightness; return result; } - /** Set all values to a minimum of zero, and a maximum of 255. */ - public HSBvalues255 ensureBounds() { - hue = Math.min(Math.max(0, hue), 255); + /** Set all values to a minimum of zero, and a maximum of either 360 (for H) or 100 (for S and B). */ + public ValuesIntegralHSB ensureBounds() { + hue = Math.min(Math.max(0, hue), 360); saturation = Math.min(Math.max(0, saturation), 100); brightness = Math.min(Math.max(0, brightness), 100); return this; @@ -122,15 +123,15 @@ public class ColorUtils { { 0.0134474, -0.1183897, 1.0154096}}; private static RealMatrix mInverted = MatrixUtils.createRealMatrix(matrixData); - public static RGBvalues convertXYtoRGB(XYZvalues values) { + public static ValuesRGB convertXYtoRGB(ValuesXYZ values) { return convertXYtoRGB(values.x, values.y, values.z); } - public static RGBvalues convertXYtoRGB(double x, double y, double z) { + public static ValuesRGB convertXYtoRGB(double x, double y, double z) { RealMatrix xyz = MatrixUtils.createColumnRealMatrix(new double[] {x, y, z}); RealMatrix result = mInverted.multiply(xyz); double[][] rgb = result.getData(); - return RGBvalues.of( + return ValuesRGB.of( (int) Math.round(255 * rgb[0][0]), // red (int) Math.round(255 * rgb[1][0]), // green (int) Math.round(255 * rgb[2][0])); // blue @@ -138,24 +139,24 @@ public class ColorUtils { public static void convertXYtoRGB(double x, double y, double z, Consumer<Integer> setRed, Consumer<Integer> setGreen, Consumer<Integer> setBlue) { - RGBvalues result = convertXYtoRGB(x, y, z); + ValuesRGB result = convertXYtoRGB(x, y, z); setRed.accept(result.red); setGreen.accept(result.green); setBlue.accept(result.blue); } - public static HSBvalues convertRGBtoHSB(RGBvalues values) { + public static ValuesHSB convertRGBtoHSB(ValuesRGB values) { return convertRGBtoHSB(values.red, values.green, values.blue); } - public static HSBvalues convertRGBtoHSB(int red, int green, int blue) { + public static ValuesHSB convertRGBtoHSB(int red, int green, int blue) { float[] hsb = Color.RGBtoHSB(red, green, blue, null); - return HSBvalues.of(hsb[0], hsb[1], hsb[2]); + return ValuesHSB.of(hsb[0], hsb[1], hsb[2]); } public static void convertRGBtoHSB(int red, int green, int blue, Consumer<Double> setHue, Consumer<Double> setSaturation, Consumer<Double> setBrightness) { - HSBvalues values = convertRGBtoHSB(red, green, blue); + ValuesHSB values = convertRGBtoHSB(red, green, blue); setHue.accept(values.hue); setSaturation.accept(values.saturation); setBrightness.accept(values.brightness); diff --git a/eraser-base/build.gradle b/eraser-base/build.gradle index fbe5f76ec7b7097ca4ef1ca48181b4c26eb71433..63dc1cf62d41dbb534eb12133e494ac66379656d 100644 --- a/eraser-base/build.gradle +++ b/eraser-base/build.gradle @@ -1,57 +1,42 @@ -apply plugin: 'jastadd' -apply plugin: 'application' -apply plugin: 'jacoco' -apply plugin: 'idea' -apply plugin: 'distribution' - -dependencies { - compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.8' - compile group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' - compile group: 'org.fusesource.mqtt-client', name: 'mqtt-client', version: '1.15' - compile group: 'org.influxdb', name: 'influxdb-java', version: '2.15' - testCompile group: 'org.testcontainers', name: 'testcontainers', version: '1.11.2' - testCompile group: 'org.testcontainers', name: 'influxdb', version: '1.11.2' - testCompile group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: '2.11.2' -} - buildscript { repositories.mavenLocal() repositories.mavenCentral() dependencies { - classpath group: 'org.jastadd', name: 'jastaddgradle', version: '1.13.2' + classpath group: 'org.jastadd', name: 'jastaddgradle', version: '1.13.3' } } -run { - mainClassName = 'de.tudresden.inf.st.eraser.Main' - standardInput = System.in - if (project.hasProperty("appArgs")) { - args Eval.me(appArgs) - } +plugins { + id 'eraser.java-application-conventions' + id 'eraser.java-jastadd-conventions' } -test { - testLogging { - events "passed", "skipped", "failed" - exceptionFormat "full" - } +dependencies { + jastadd2 "org.jastadd:jastadd:2.3.4" + compileOnly group: 'de.tudresden.inf.st.jastadd', name: 'coverage-generator', version: '0.0.4' + + api group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: "${jackson_version}" + api group: 'org.fusesource.mqtt-client', name: 'mqtt-client', version: '1.16' + implementation group: 'org.influxdb', name: 'influxdb-java', version: '2.20' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: "${jupiter_version}" +// testImplementation group: 'org.testcontainers', name: 'testcontainers', version: '1.15.0' + testImplementation group: 'org.testcontainers', name: 'junit-jupiter', version: '1.15.0' + testImplementation group: 'org.testcontainers', name: 'influxdb', version: '1.15.0' + testImplementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: "${log4j_version}" } -jacocoTestReport { - reports { - xml.enabled true - html.enabled false - } +application { + mainClass = 'de.tudresden.inf.st.eraser.Main' } def relastFiles = fileTree('src/main/jastadd/') { include '**/*.relast' }.toList().toArray() String[] relastArguments = [ "libs/relast.jar", - "--grammarName=./src/main/jastadd/mainGen", + "--grammarName=./src/gen/jastadd/mainGen", "--useJastAddNames", - "--listClass=RefList", "--jastAddList=JastAddList", + "--resolverHelper", "--file" ] task preprocess(type: JavaExec) { @@ -60,15 +45,57 @@ task preprocess(type: JavaExec) { args relastArguments + relastFiles inputs.files relastFiles - outputs.files file("./src/main/jastadd/mainGen.ast"), file("./src/main/jastadd/mainGen.jadd") + outputs.files(file("./src/gen/jastadd/mainGen.ast"), + file("./src/gen/jastadd/mainGen.jadd")) +} + +String[] coverageGenArguments = [ + '--List=JastAddList', + '--printYaml', + '--outputBaseDir=src/gen/jastadd' +] +task generateCoverage(type: JavaExec) { + main = 'org.jastadd.preprocessor.coverage_gen.Main' + classpath = configurations.compileOnly + + args coverageGenArguments + relastFiles } jastadd { configureModuleBuild() - modules "jastadd_modules" + modules { + //noinspection GroovyAssignabilityCheck + module("eraser") { + + java { + basedir "src/" + include "main/**/*.java" + include "gen/**/*.java" + } + + jastadd { + basedir "src/" + include "main/jastadd/**/*.ast" + include "main/jastadd/**/*.jadd" + include "main/jastadd/**/*.jrag" + include "gen/jastadd/**/*.ast" + include "gen/jastadd/**/*.jadd" + include "gen/jastadd/**/*.jrag" + } + + scanner { + include "src/main/jastadd/eraser.flex" + } + + parser { + include "src/main/jastadd/eraser.parser" + } + + } + } module = "eraser" - extraJastAddOptions = ['--List=JastAddList'] + extraJastAddOptions = ["--lineColumnNumbers", "--List=JastAddList"] astPackage = 'de.tudresden.inf.st.eraser.jastadd.model' genDir = 'src/gen/java' @@ -88,36 +115,8 @@ sourceSets.main { } } -javadoc { - // this is only run to get the index file etc. - failOnError = false -} - -String[] arguments = ["libs/rd-builder.jar", "-d", "doc/"] -def allSrcFiles = sourceSets.main.allSource.findAll { it.name.endsWith('java') }.toArray() -def ragdocViewSrcData = '../ragdoc-view/src/data/' - -task ragdoc(type: JavaExec, dependsOn: assemble, overwrite: true) { - group = 'documentation' - description = 'Create ragdoc json documentation files' - main = "-jar" - args arguments + allSrcFiles -} - -task cleanRagdoc(type: Delete) { - group = 'documentation' - delete fileTree(ragdocViewSrcData + '/*') -} - -task copyRagdoc(type: Copy, dependsOn: cleanRagdoc) { - group = 'documentation' - description = 'Copy ragdoc json documentation files to ragdoc-viewer' - from 'doc/' - into ragdocViewSrcData - eachFile { println it.file } -} - generateAst.dependsOn preprocess +generateAst.dependsOn generateCoverage generateAst.inputs.files file("./src/main/jastadd/mainGen.ast"), file("./src/main/jastadd/mainGen.jadd") //compileJava.dependsOn jastadd // diff --git a/eraser-base/jastadd_modules b/eraser-base/jastadd_modules deleted file mode 100644 index 49d87af10ac8b3ecb923d1afda17d074475882c9..0000000000000000000000000000000000000000 --- a/eraser-base/jastadd_modules +++ /dev/null @@ -1,24 +0,0 @@ -module("eraser") { - - java { - basedir "src/" - include "main/**/*.java" - include "gen/**/*.java" - } - - jastadd { - basedir "src/main/jastadd/" - include "**/*.ast" - include "**/*.jadd" - include "**/*.jrag" - } - - scanner { - include "src/main/jastadd/eraser.flex" - } - - parser { - include "src/main/jastadd/eraser.parser" - } - -} diff --git a/eraser-base/libs/relast.jar b/eraser-base/libs/relast.jar index 9b52866a399cfdd0c4fe29c102125beab5d77989..b1a7542048dd1611db7f479307b0285efd8bb1f6 100644 Binary files a/eraser-base/libs/relast.jar and b/eraser-base/libs/relast.jar differ diff --git a/eraser-base/src/main/jastadd/.gitignore b/eraser-base/src/main/jastadd/.gitignore deleted file mode 100644 index c1777578ed3842b5bff56810af8da640367460dd..0000000000000000000000000000000000000000 --- a/eraser-base/src/main/jastadd/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -mainGen.ast -mainGen.jadd diff --git a/eraser-base/src/main/jastadd/AdditionalTypes.jadd b/eraser-base/src/main/jastadd/AdditionalTypes.jadd index 32651117408970af5b3a57f4ddc48e2f7f2a0d8c..8abddb34f36d87d7f889dfd245fe83b017ae4069 100644 --- a/eraser-base/src/main/jastadd/AdditionalTypes.jadd +++ b/eraser-base/src/main/jastadd/AdditionalTypes.jadd @@ -1,16 +1,19 @@ aspect AdditionalTypes { - public class StringList extends beaver.Symbol implements Iterable<String> { - private java.util.Deque<String> delegatee = new java.util.ArrayDeque<>(); + public class ReversedList<T> extends beaver.Symbol implements Iterable<T> { + private java.util.Deque<T> delegatee = new java.util.ArrayDeque<>(); - public java.util.Iterator<String> iterator() { + public java.util.Iterator<T> iterator() { return delegatee.descendingIterator(); } - public void add(String s) { - delegatee.add(s); + public void add(T t) { + delegatee.add(t); } } + public class StringList extends ReversedList<String> {} + public class ItemList extends ReversedList<Item> {} + public class TypedKeyMap<T> extends beaver.Symbol implements Iterable<AbstractMap.SimpleEntry<T, String>> { private java.util.Deque<AbstractMap.SimpleEntry<T, String>> delegatee = new java.util.ArrayDeque<>(); diff --git a/eraser-base/src/main/jastadd/DecisionTree.jrag b/eraser-base/src/main/jastadd/DecisionTree.jrag index f50ca0562a920844e46a838c0f655f97bf804a01..da4352c0a2ed081d550f0ac0ba1f213d54a2f956 100644 --- a/eraser-base/src/main/jastadd/DecisionTree.jrag +++ b/eraser-base/src/main/jastadd/DecisionTree.jrag @@ -3,21 +3,21 @@ aspect DecisionTree { // let DecisionTreeLeaf implement Leaf public class DecisionTreeLeaf implements Leaf { } - //--- classify --- - syn DecisionTreeLeaf DecisionTreeRoot.classify() { - return getRootRule().classify(); + //--- internalClassify --- + syn DecisionTreeLeaf DecisionTreeRoot.internalClassify() { + return getRootRule().internalClassify(); } - syn DecisionTreeLeaf DecisionTreeElement.classify(); + syn DecisionTreeLeaf DecisionTreeElement.internalClassify(); - syn DecisionTreeLeaf DecisionTreeRule.classify(); + syn DecisionTreeLeaf DecisionTreeRule.internalClassify(); - syn DecisionTreeLeaf ItemStateCheckRule.classify() { + syn DecisionTreeLeaf ItemStateCheckRule.internalClassify() { boolean chooseLeft = getItemStateCheck().holds(); - return (chooseLeft ? getLeft() : getRight()).classify(); + return (chooseLeft ? getLeft() : getRight()).internalClassify(); } - syn DecisionTreeLeaf DecisionTreeLeaf.classify() = this; + syn DecisionTreeLeaf DecisionTreeLeaf.internalClassify() = this; //--- holds --- syn boolean ItemStateCheck.holds() = holdsFor(getItem()); @@ -47,20 +47,20 @@ aspect DecisionTree { } //--- computePreferences --- - syn List<ItemPreference> DecisionTreeLeaf.computePreferences() { + syn MachineLearningResult DecisionTreeLeaf.computePreferences() { // iterate over preference of this leaf, and all its parents and ancestors - List<ItemPreference> result = new ArrayList<>(); + MachineLearningResult result = new MachineLearningResult(); Set<Item> seenItems = new HashSet<>(); List<DecisionTreeElement> ancestors = ancestors(); - for (ItemPreference pref : getPreferenceList()) { - result.add(pref); - seenItems.add(pref.getItem()); + for (ItemUpdate update : getPreferenceList()) { + result.addItemUpdate(update); + seenItems.add(update.getItem()); } for (DecisionTreeElement ancestor : ancestors) { - for (ItemPreference pref : ancestor.getPreferenceList()) { - if (!seenItems.contains(pref.getItem())) { - result.add(pref); - seenItems.add(pref.getItem()); + for (ItemUpdate update : ancestor.getPreferenceList()) { + if (!seenItems.contains(update.getItem())) { + result.addItemUpdate(update); + seenItems.add(update.getItem()); } } } diff --git a/eraser-base/src/main/jastadd/DecisionTree.relast b/eraser-base/src/main/jastadd/DecisionTree.relast index 17a1e65c1c6cd12ba2d49eeceba1eb920dae31bd..78271d1f64c22bc889e0f1634c6c21ffe2354cc3 100644 --- a/eraser-base/src/main/jastadd/DecisionTree.relast +++ b/eraser-base/src/main/jastadd/DecisionTree.relast @@ -1,6 +1,6 @@ // ---------------- Decision Tree ------------------------------ DecisionTreeRoot : InternalMachineLearningModel ::= RootRule:DecisionTreeRule ; -abstract DecisionTreeElement ::= Preference:ItemPreference*; +abstract DecisionTreeElement ::= Preference:ItemUpdate*; abstract DecisionTreeRule : DecisionTreeElement ::= Left:DecisionTreeElement Right:DecisionTreeElement <Label:String> ; ItemStateCheckRule : DecisionTreeRule ::= ItemStateCheck ; diff --git a/eraser-base/src/main/jastadd/Expression.jrag b/eraser-base/src/main/jastadd/Expression.jrag index e3d6a6b6b01effb2b014bbe85b7360aea5314a3a..9e1d4f52d57a1aab055a397983578f2e38f356ab 100644 --- a/eraser-base/src/main/jastadd/Expression.jrag +++ b/eraser-base/src/main/jastadd/Expression.jrag @@ -30,4 +30,15 @@ aspect Expression { eq Designator.eval() = getItem().getStateAsDouble(); eq ParenthesizedNumberExpression.eval() = getOperand().eval(); eq NumberLiteralExpression.eval() = getValue(); + + // is-X + syn boolean Expression.isLogicalExpression() = false; + eq LogicalExpression.isLogicalExpression() = true; + syn boolean Expression.isNumberExpression() = false; + eq NumberExpression.isNumberExpression() = true; + // as-X + syn LogicalExpression Expression.asLogicalExpression() = null; + eq LogicalExpression.asLogicalExpression() = this; + syn NumberExpression Expression.asNumberExpression() = null; + eq NumberExpression.asNumberExpression() = this; } diff --git a/eraser-base/src/main/jastadd/ExternalHost.jrag b/eraser-base/src/main/jastadd/ExternalHost.jrag new file mode 100644 index 0000000000000000000000000000000000000000..a72b775358368e67cebe1587e1b5b5c1bb28aae6 --- /dev/null +++ b/eraser-base/src/main/jastadd/ExternalHost.jrag @@ -0,0 +1,6 @@ +aspect ExternalHost { + syn boolean ExternalHost.exists() = getHostName()!=null && !getHostName().isEmpty(); + public ExternalHost ExternalHost.copyFrom(ExternalHost host) { + return setHostName(host.getHostName()).setPort(host.getPort()).setUserName(host.getUserName()).setPassword(host.getPassword()); + } +} \ No newline at end of file diff --git a/eraser-base/src/main/jastadd/InfluxRoot.jrag b/eraser-base/src/main/jastadd/InfluxRoot.jrag new file mode 100644 index 0000000000000000000000000000000000000000..58c6cab74be46d25bf4f0f68945fca51076b3a04 --- /dev/null +++ b/eraser-base/src/main/jastadd/InfluxRoot.jrag @@ -0,0 +1,3 @@ +aspect InfluxRoot { + syn ExternalHost InfluxRoot.getHost() = new ExternalHost(); +} \ No newline at end of file diff --git a/eraser-base/src/main/jastadd/Item.jrag b/eraser-base/src/main/jastadd/Item.jrag index 0b370142cf57c377775294bb6c758dafa7db036f..967cc061d56453edbe95dee5cc4eba3583eb8c5a 100644 --- a/eraser-base/src/main/jastadd/Item.jrag +++ b/eraser-base/src/main/jastadd/Item.jrag @@ -1,10 +1,5 @@ aspect ItemHandling { - protected boolean Item.isFrozen = false; - public void Item.freeze() { isFrozen = true; } - public void Item.unfreeze() { isFrozen = false; } - public final boolean Item.isFrozen() { return isFrozen; } - protected boolean Item.sendState = true; public void Item.disableSendState() { sendState = false; } public void Item.enableSendState() { sendState = true; } @@ -26,6 +21,8 @@ aspect ItemHandling { eq ItemWithDoubleState.getStateAsString() = Double.toString(getState()); eq ItemWithStringState.getStateAsString() = getState(); + syn LastChanged Item.getLastChanged() = new LastChanged(); + //--- getStateAsDouble --- syn double Item.getStateAsDouble(); // TupleHSB and String work like default @@ -142,6 +139,9 @@ aspect ItemHandling { //--- setStateFromBoolean --- public abstract void Item.setStateFromBoolean(boolean value); public void ColorItem.setStateFromBoolean(boolean value) { + if (getState()!=null && getState().getBrightness()!=0 && value) { + return; + } setBrightness(value ? 100 : 0); } public void DateTimeItem.setStateFromBoolean(boolean value) { @@ -254,47 +254,49 @@ aspect ItemHandling { //--- setState(value,shouldSendState) --- public void ItemWithBooleanState.setState(boolean value, boolean shouldSendState) { - if (isFrozen || stateEquals(value)) { return; } + if (stateEquals(value)) { return; } set_state(value); stateUpdated(shouldSendState); } public void ItemWithStringState.setState(String value, boolean shouldSendState) { - if (isFrozen || stateEquals(value)) { return; } + if (stateEquals(value)) { return; } set_state(value); stateUpdated(shouldSendState); } public void ItemWithDoubleState.setState(double value, boolean shouldSendState) { - if (isFrozen || stateEquals(value)) { return; } + if (stateEquals(value)) { return; } set_state(value); stateUpdated(shouldSendState); } public void ColorItem.setState(TupleHSB value, boolean shouldSendState) { - if (isFrozen || stateEquals(value)) { return; } + if (stateEquals(value)) { return; } set_state(value); stateUpdated(shouldSendState); } public void DateTimeItem.setState(Instant value, boolean shouldSendState) { - if (isFrozen || stateEquals(value)) { return; } + if (stateEquals(value)) { return; } set_state(value); stateUpdated(shouldSendState); } + syn ItemObserver Item.getItemObserver() = new ItemObserver(); + //--- stateUpdated --- /** - * Called, whenever the state of an item is updated. Does various things including: + * Called, whenever the state of an item is updated. Does various things (except it has no parent) including: * <ul> * <li>Send the new state via MQTT</li> * <li>Send the new state to Influx DB</li> * <li>Notify the attached {@link ItemObserver}, if any</li> - * <li>Update state of controlled items</li> * </ul> * @param shouldSendState whether to send the new state (currently affects MQTT and Influx) */ protected void Item.stateUpdated(boolean shouldSendState) { + if (getParent() == null) { return; } if (shouldSendState) { try { // sendState() refined in MQTT and Influx aspect @@ -303,14 +305,13 @@ aspect ItemHandling { logger.catching(e); } } - if (hasItemObserver()) { + if (this.getLastChanged().checkStateProcessingTime(relevantFrequencySetting())) { + this.getLastChanged().afterStateChangeProcessed(); getItemObserver().apply(); } - for (Item controlled : getControllingList()) { - doUpdateFor(controlled); - } } + //--- sendState --- protected void Item.sendState() throws Exception { for (MachineLearningModel model : getRelevantInMachineLearningModels()) { @@ -363,6 +364,20 @@ aspect ItemHandling { public void ItemWithDoubleState.setStateToDefault() { this.setState(0.0); } public void ItemWithStringState.setStateToDefault() { this.setState(""); } + //--- is$ItemType --- + syn boolean Item.isColorItem() = false; + syn boolean ColorItem.isColorItem() = true; + syn boolean Item.isItemWithBooleanState() = false; + syn boolean ItemWithBooleanState.isItemWithBooleanState() = true; + syn boolean Item.isItemWithStringState() = false; + syn boolean ItemWithStringState.isItemWithStringState() = true; + syn boolean Item.isItemWithDoubleState() = false; + syn boolean ItemWithDoubleState.isItemWithDoubleState() = true; + syn boolean Item.isDateTimeItem() = false; + syn boolean DateTimeItem.isDateTimeItem() = true; + syn boolean Item.isActivityItem() = false; + syn boolean ActivityItem.isActivityItem() = true; + //--- as$ItemType --- // those attributes will raise a ClassCastException if called on the wrong item type. But what else can we do? syn ColorItem Item.asColorItem() = (ColorItem) this; @@ -376,54 +391,135 @@ aspect ItemHandling { syn DateTimeItem Item.asDateTimeItem() = (DateTimeItem) this; syn DateTimeItem DateTimeItem.asDateTimeItem() = this; - //--- doUpdateFor --- - protected abstract void Item.doUpdateFor(Item controlling); - protected void ItemWithBooleanState.doUpdateFor(Item controlling) { - controlling.setStateFromBoolean(this.getState()); + + //--- State Copy --- + public void Item.addControlling(Item controlledItem) { + getRoot().addRule(createControllerRule(this,controlledItem)); } - protected void ItemWithStringState.doUpdateFor(Item controlling) { - controlling.setStateFromString(this.getState()); + + public void Item.addControlledBy(Item controllerItem) { + getRoot().addRule(createControllerRule(controllerItem,this)); } - protected void ItemWithDoubleState.doUpdateFor(Item controlling) { - controlling.setStateFromDouble(this.getState()); + + public void Item.synchronizeWith(Item item) { + addControlledBy(item); + addControlling(item); } - protected void ColorItem.doUpdateFor(Item controlling) { - controlling.setStateFromColor(this.getState()); + + private Rule Item.createControllerRule(Item controllerItem, Item controlledItem) { + Rule rule = new Rule(); + + ItemStateChangeCondition condition = new ItemStateChangeCondition(); + condition.setItem(controllerItem); + rule.addCondition(condition); + + SetStateFromTriggeringItemAction action = new SetStateFromTriggeringItemAction(); + action.setAffectedItem(controlledItem); + rule.addAction(action); + + rule.activateFor(controllerItem); + + return rule; + } - protected void DateTimeItem.doUpdateFor(Item controlling) { - controlling.setStateFromInstant(this.getState()); + + public void Item.removeControlling(Item controlledItem) { + JastAddList<Rule> rules = getRoot().getRules(); + HashMap<Rule, Item> removeMap = new HashMap<Rule, Item>(); + for (Rule rule : rules) { + // check if rule has one ItemStateChangeCondition with controllerItem + int size = 0; + Condition lastCondition = null; + for (Condition condition : rule.getConditions()) { + size++; + lastCondition = condition; + } + + // check condition pattern + if (size!=1) + continue; + if (!(lastCondition instanceof ItemStateChangeCondition)) + continue; + Item controlling = ((ItemStateChangeCondition) lastCondition).getItem(); + + // check if action has one SetStateFromTriggeringItemAction + size = 0; + Action lastAction = null; + for (Action action : rule.getActions()) { + size++; + lastAction = action; + } + + // check action pattern + if (size!=1) + continue; + if (!(lastAction instanceof SetStateFromTriggeringItemAction)) + continue; + if (((SetStateFromTriggeringItemAction)lastAction).getAffectedItem() != controlledItem) + continue; + + removeMap.put(rule,controlling); + } + for (Rule rule : removeMap.keySet()) { + rule.deactivateFor(removeMap.get(rule)); + int idx = rules.getIndexOfChild(rule); + rules.removeChild(idx); + } + + } + + //--- onReceiveStateChange --- + protected void Item.onReceiveStateChange() { + //Instant lastChange = this. + } + + + + + + //--- copyStateTo --- + protected abstract void Item.copyStateTo(Item stateReceiver); + + protected void ItemWithBooleanState.copyStateTo(Item stateReceiver) { + stateReceiver.setStateFromBoolean(this.getState()); } + protected void ItemWithStringState.copyStateTo(Item stateReceiver) { + stateReceiver.setStateFromString(this.getState()); + } + protected void ItemWithDoubleState.copyStateTo(Item stateReceiver) { + stateReceiver.setStateFromDouble(this.getState()); + } + protected void ColorItem.copyStateTo(Item stateReceiver) { + stateReceiver.setStateFromColor(this.getState()); + } + protected void DateTimeItem.copyStateTo(Item stateReceiver) { + stateReceiver.setStateFromInstant(this.getState()); + } + + + private void ColorItem.setBrightness(int value) { setState(getState().withDifferentBrightness(value)); } - //--- ItemPreference.apply --- - public abstract void ItemPreference.apply(); - public void ItemPreferenceColor.apply() { - logger.debug("Apply color preference {} -> {}", getItem().getID(), getPreferredHSB()); - getItem().setStateFromColor(getPreferredHSB()); - getItem().freeze(); - for (Item controller : getItem().getControlledByList()) { - controller.setStateFromColor(getPreferredHSB()); - } - getItem().unfreeze(); - } - public void ItemPreferenceDouble.apply() { - logger.debug("Apply double preference {} -> {}", getItem().getID(), getPreferredValue()); - getItem().setStateFromDouble(getPreferredValue()); - getItem().freeze(); - for (Item controller : getItem().getControlledByList()) { - controller.setStateFromDouble(getPreferredValue()); - } - getItem().unfreeze(); + //--- ItemUpdate.apply --- + public abstract void ItemUpdate.apply(); + public void ItemUpdateColor.apply() { + getItem().setStateFromColor(getNewHSB()); } + //--- ItemUpdate.apply --- + public void ItemUpdateDouble.apply() { + getItem().setStateFromDouble(getNewValue()); + } + + //--- ItemUpdate.describe --- + syn String ItemUpdate.describe() = getItem().getID() + " -> " + getNewStateAsString(); -// // override Item.init$Children from JastAdd's own ASTNode aspect -// refine ASTNode public void Item.init$Children() { -// refined(); -// setDefaultShouldSendState(true); -// } + //--- ItemUpdate.getNewStateAsString --- + syn String ItemUpdate.getNewStateAsString(); + eq ItemUpdateColor.getNewStateAsString() = getNewHSB().toString(); + eq ItemUpdateDouble.getNewStateAsString() = Double.toString(getNewValue()); } diff --git a/eraser-base/src/main/jastadd/ItemHistory.jrag b/eraser-base/src/main/jastadd/ItemHistory.jrag index 02f226f8d2a7c59ed7ad5d2d383609096af9d049..1c256a56aa32bddda86f65a5ce951afdaf87d206 100644 --- a/eraser-base/src/main/jastadd/ItemHistory.jrag +++ b/eraser-base/src/main/jastadd/ItemHistory.jrag @@ -7,9 +7,11 @@ aspect ItemHistory { public static final InfluxRoot InfluxRoot.createDefault() { InfluxRoot result = new InfluxRoot(); - result.setUser(DEFAULT_USER); - result.setPassword(DEFAULT_PASSWORD); + + result.getHost().setUserName(DEFAULT_USER).setPassword(DEFAULT_PASSWORD); + result.setDbName(DEFAULT_DB_NAME); + return result; } @@ -17,7 +19,7 @@ aspect ItemHistory { cache InfluxRoot.influxAdapter(); syn InfluxAdapter InfluxRoot.influxAdapter() { InfluxAdapter result; - if (hasHost()) { + if (getHost().exists()) { result = new InfluxAdapterImpl(); } else { result = new InfluxAdapterStub(); @@ -101,6 +103,8 @@ aspect ItemHistory { getRoot().getInfluxRoot().influxAdapter().write(pointFromState()); } + + //--- pointFromState --- protected abstract AbstractItemPoint Item.pointFromState(); protected AbstractItemPoint ItemWithBooleanState.pointFromState() { diff --git a/eraser-base/src/main/jastadd/LastChanged.jrag b/eraser-base/src/main/jastadd/LastChanged.jrag new file mode 100644 index 0000000000000000000000000000000000000000..2ac7dfc95e247aa988f9c518b64c687477b9bbb5 --- /dev/null +++ b/eraser-base/src/main/jastadd/LastChanged.jrag @@ -0,0 +1,18 @@ +aspect LastChanged { + public void LastChanged.afterStateChangeProcessed() { + this.setValue(Instant.now()); + } + + public boolean LastChanged.checkStateProcessingTime(FrequencySetting FrequencySetting) { + if (FrequencySetting == null) { + return true; + } + double frequency = FrequencySetting.getEventProcessingFrequency(); + Instant lastStateChange = this.getValue(); + if (lastStateChange == null) { + return true; + } + return lastStateChange.toEpochMilli() + (1 / frequency) * 1000 < Instant.now().toEpochMilli(); + } + +} diff --git a/eraser-base/src/main/jastadd/Logging.jadd b/eraser-base/src/main/jastadd/Logging.jadd index 710bd79a7bce048213ce0d52bb42cae5176b832a..a98a0fd051a917f4e703a8853a2ab4c1889e72b9 100644 --- a/eraser-base/src/main/jastadd/Logging.jadd +++ b/eraser-base/src/main/jastadd/Logging.jadd @@ -1,7 +1,7 @@ aspect Logging { // Base protected org.apache.logging.log4j.Logger Item.logger = org.apache.logging.log4j.LogManager.getLogger(Item.class); - protected org.apache.logging.log4j.Logger ItemPreference.logger = org.apache.logging.log4j.LogManager.getLogger(ItemPreference.class); + protected org.apache.logging.log4j.Logger ItemUpdate.logger = org.apache.logging.log4j.LogManager.getLogger(ItemUpdate.class); // MachineLearning private org.apache.logging.log4j.Logger DummyMachineLearningModel.logger = org.apache.logging.log4j.LogManager.getLogger(DummyMachineLearningModel.class); diff --git a/eraser-base/src/main/jastadd/MachineLearning.jrag b/eraser-base/src/main/jastadd/MachineLearning.jrag index 26dd3fbb319a535fcc9b5350a944754c104159c9..f722856ba47906cc94d7b6b17e5edcaae9735d14 100644 --- a/eraser-base/src/main/jastadd/MachineLearning.jrag +++ b/eraser-base/src/main/jastadd/MachineLearning.jrag @@ -8,10 +8,10 @@ aspect MachineLearning { public interface Leaf { String getLabel(); int getActivityIdentifier(); - List<ItemPreference> computePreferences(); + MachineLearningResult computePreferences(); } - syn Leaf InternalMachineLearningModel.classify(); + syn Leaf InternalMachineLearningModel.internalClassify(); //--- currentActivityName --- syn String Root.currentActivityName() = JavaUtils.ifPresentOrElseReturn( @@ -24,15 +24,15 @@ aspect MachineLearning { syn java.util.Optional<Activity> Root.currentActivity() { return resolveActivity((int) getSmartHomeEntityModel().getActivityItem().getState()); } - private int Root.extractActivityIdentifier(List<ItemPreference> preferences) { - if (preferences.isEmpty()) { + private int Root.extractActivityIdentifier(List<ItemUpdate> updates) { + if (updates.isEmpty()) { return -1; } - return (int) ((ItemPreferenceDouble) preferences.get(0)).getPreferredValue(); + return (int) ((ItemUpdateDouble) updates.get(0)).getNewValue(); } - //--- currentPreferences --- - syn List<ItemPreference> Root.currentPreferences() = getMachineLearningRoot().getPreferenceLearning().getDecoder().classify().getPreferences(); +// //--- currentPreferences --- +// syn List<ItemUpdate> Root.currentPreferences() = getMachineLearningRoot().getPreferenceLearning().getDecoder().classify().getItemUpdates(); //--- canSetActivity --- syn boolean MachineLearningModel.canSetActivity() = false; @@ -45,7 +45,7 @@ aspect MachineLearning { } //--- DummyMachineLearningModel.classify --- - eq DummyMachineLearningModel.classify() { + eq DummyMachineLearningModel.internalClassify() { if (logger.isInfoEnabled() && getItemList().size() > 0) { logger.info("Dummy classification of {}, values of connected items: {}", mlKind(), @@ -125,6 +125,12 @@ aspect MachineLearning { private MachineLearningEncoder ExternalMachineLearningModel.encoder; public void ExternalMachineLearningModel.setEncoder(MachineLearningEncoder encoder) { this.encoder = encoder; + for (Item relevantItem : encoder.getRelevantItems()) { + addRelevantItem(relevantItem); + } + for (Item targetItem : encoder.getTargets()) { + addTargetItem(targetItem); + } } private MachineLearningDecoder ExternalMachineLearningModel.decoder; public void ExternalMachineLearningModel.setDecoder(MachineLearningDecoder decoder) { @@ -161,6 +167,43 @@ aspect MachineLearning { return this.decoder; } + //--- classify --- + public MachineLearningResult MachineLearningModel.classify() { + MachineLearningResult result = getDecoder().classify(); + setLastResult(result); + return result; + } + + //--- equals --- + public boolean MachineLearningResult.equals(Object other) { + if (!(other instanceof MachineLearningResult)) { + return false; + } + MachineLearningResult otherResult = (MachineLearningResult) other; + if (getNumItemUpdate() != otherResult.getNumItemUpdate()) { + return false; + } + for (int i = 0; i < getNumItemUpdate(); i++) { + if (!getItemUpdate(i).equals(otherResult.getItemUpdate(i))) { + return false; + } + } + return true; + } + + public abstract boolean ItemUpdate.equals(Object other); + public boolean ItemUpdateDouble.equals(Object other) { + if (!(other instanceof ItemUpdateDouble)) { + return false; + } + return getItem() == ((ItemUpdateDouble) other).getItem() && getNewValue() == ((ItemUpdateDouble) other).getNewValue(); + } + public boolean ItemUpdateColor.equals(Object other) { + if (!(other instanceof ItemUpdateColor)) { + return false; + } + return getItem() == ((ItemUpdateColor) other).getItem() && getNewHSB() == ((ItemUpdateColor) other).getNewHSB(); + } } aspect ChangeEvents { @@ -171,19 +214,32 @@ aspect ChangeEvents { RecognitionEvent result = new RecognitionEvent(); result.initChangeEvent(); for (Item relevantItem : modelOfRecognition.getRelevantItems()) { - result.addChangedItem(ChangedItem.newFromItem(relevantItem)); + result.addRelevantItem(ChangedItem.newFrom(relevantItem)); + } + if (modelOfRecognition.hasLastResult()) { + for (ItemUpdate preference : modelOfRecognition.getLastResult().getItemUpdates()) { + result.addChangedItem(ChangedItem.newFrom(preference)); + } } return result; } - //--- newFromItem --- - public static ChangedItem ChangedItem.newFromItem(Item source) { + //--- newFrom Item --- + public static ChangedItem ChangedItem.newFrom(Item source) { ChangedItem result = new ChangedItem(); result.setItem(source); result.setNewStateAsString(source.getStateAsString()); return result; } + //--- newFrom ItemUpdate --- + public static ChangedItem ChangedItem.newFrom(ItemUpdate update) { + ChangedItem result = new ChangedItem(); + result.setItem(update.getItem()); + result.setNewStateAsString(update.getNewStateAsString()); + return result; + } + //--- initChangeEvent --- protected void ChangeEvent.initChangeEvent() { this.setCreated(Instant.now()); diff --git a/eraser-base/src/main/jastadd/MachineLearning.relast b/eraser-base/src/main/jastadd/MachineLearning.relast index 906d344b8f123fca5d617b174078dcfa56f7f4c8..3632637bab0056693ee33b9905b92ea9dc6ab39e 100644 --- a/eraser-base/src/main/jastadd/MachineLearning.relast +++ b/eraser-base/src/main/jastadd/MachineLearning.relast @@ -8,12 +8,12 @@ abstract ChangeEvent ::= <Identifier:int> <Created:Instant> ChangedItem* ; ChangedItem ::= <NewStateAsString:String> ; rel ChangedItem.Item -> Item ; -RecognitionEvent : ChangeEvent ; +RecognitionEvent : ChangeEvent ::= RelevantItem:ChangedItem* ; rel RecognitionEvent.Activity -> Activity ; ManualChangeEvent : ChangeEvent ; -abstract MachineLearningModel ::= ; +abstract MachineLearningModel ::= [LastResult:MachineLearningResult] ; rel MachineLearningModel.RelevantItem* <-> Item.RelevantInMachineLearningModel* ; rel MachineLearningModel.TargetItem* <-> Item.TargetInMachineLearningModel* ; @@ -21,8 +21,10 @@ ExternalMachineLearningModel : MachineLearningModel ; abstract InternalMachineLearningModel : MachineLearningModel ::= <OutputApplication:DoubleDoubleFunction> ; -abstract ItemPreference ::= ; -rel ItemPreference.Item -> Item ; +MachineLearningResult ::= ItemUpdate* ; -ItemPreferenceColor : ItemPreference ::= <PreferredHSB:TupleHSB> ; -ItemPreferenceDouble : ItemPreference ::= <PreferredValue:double> ; +abstract ItemUpdate ::= ; +rel ItemUpdate.Item -> Item ; + +ItemUpdateColor : ItemUpdate ::= <NewHSB:TupleHSB> ; +ItemUpdateDouble : ItemUpdate ::= <NewValue:double> ; diff --git a/eraser-base/src/main/jastadd/ModelStatistics.jrag b/eraser-base/src/main/jastadd/ModelStatistics.jrag index 9ed07bd1947dd5c83792f7371d2c209e88ea3329..786f00e4b5b48ee9857d5b4154cee90aa6803880 100644 --- a/eraser-base/src/main/jastadd/ModelStatistics.jrag +++ b/eraser-base/src/main/jastadd/ModelStatistics.jrag @@ -1,19 +1,10 @@ aspect ModelStatistics { - //--- numChannels --- - syn int SmartHomeEntityModel.numChannels() { - int sum = 0; - for (Thing thing : getThingList()) { - sum += thing.getNumChannel(); - } - return sum; - } - //--- description --- syn String SmartHomeEntityModel.description() = "[" + this.getNumThingType() + " thing type(s), " + this.getNumChannelType() + " channel type(s), " - + this.numChannels() + " channel(s), " + + this.getNumChannel() + " channel(s), " + this.getNumThing() + " thing(s), " + this.getNumGroup() + " group(s), " + this.items().size() + " item(s)]"; diff --git a/eraser-base/src/main/jastadd/Navigation.jrag b/eraser-base/src/main/jastadd/Navigation.jrag index 12f520c3077f50291f1781072b4f4e187d4efc00..8281266e2752f40aceba72cefc862956d40395a2 100644 --- a/eraser-base/src/main/jastadd/Navigation.jrag +++ b/eraser-base/src/main/jastadd/Navigation.jrag @@ -7,121 +7,97 @@ aspect Navigation { //--- items --- syn java.util.List<Item> SmartHomeEntityModel.items() { java.util.List<Item> result = new java.util.ArrayList<>(); - addItems(result, getGroupList()); + getGroupList().forEach(group -> result.addAll(group.items())); + result.addAll(unknownGroup().items()); return result; } - - private void SmartHomeEntityModel.addItems(java.util.List<Item> result, JastAddList<Group> groups) { - groups.forEach(group -> group.getItemList().forEach(item -> result.add(item))); + syn java.util.List<Item> Group.items() { + java.util.List<Item> result = new java.util.ArrayList<>(); + getItemList().forEach(item -> result.add(item)); + getGroupList().forEach(subgroup -> result.addAll(subgroup.items())); + return result; } - //--- parameters --- - syn java.util.Set<Parameter> SmartHomeEntityModel.parameters() { - java.util.Set<Parameter> result = new java.util.TreeSet<>(modelElementComparator()); - getThingTypeList().forEach(tt -> tt.getParameterList().forEach(parameter -> result.add(parameter))); + //--- groups --- + syn java.util.List<Group> SmartHomeEntityModel.groups() { + java.util.List<Group> result = new java.util.ArrayList<>(); + getGroupList().forEach(group -> result.addAll(group.groups())); return result; } - - //--- channels --- - syn java.util.Set<Channel> SmartHomeEntityModel.channels() { - java.util.Set<Channel> result = new java.util.TreeSet<>(modelElementComparator()); - getThingList().forEach(thing -> thing.getChannelList().forEach(channel -> result.add(channel))); + syn java.util.List<Group> Group.groups() { + java.util.List<Group> result = new java.util.ArrayList<>(); + getGroupList().forEach(subgroup -> result.addAll(subgroup.groups())); + result.add(this); return result; } - //--- resolveThingType --- - syn java.util.Optional<ThingType> SmartHomeEntityModel.resolveThingType(String thingTypeId) { - for (ThingType thingType : this.getThingTypeList()) { - if (thingType.getID().equals(thingTypeId)) { - return java.util.Optional.of(thingType); - } + //--- enclosingGroup --- + inh Group Group.enclosingGroup(); + inh Group Item.enclosingGroup(); + eq Group.getItem().enclosingGroup() = this; + eq Group.getGroup().enclosingGroup() = this; + eq SmartHomeEntityModel.unknownGroup().enclosingGroup() = null; + eq SmartHomeEntityModel.getGroup().enclosingGroup() = null; + eq SmartHomeEntityModel.getActivityItem().enclosingGroup() = null; + + //--- containingSmartHomeEntityModel --- + inh SmartHomeEntityModel Thing.containingSmartHomeEntityModel(); + inh SmartHomeEntityModel Group.containingSmartHomeEntityModel(); + inh SmartHomeEntityModel Item.containingSmartHomeEntityModel(); + inh SmartHomeEntityModel ThingType.containingSmartHomeEntityModel(); + inh SmartHomeEntityModel Parameter.containingSmartHomeEntityModel(); + inh SmartHomeEntityModel ChannelType.containingSmartHomeEntityModel(); + inh SmartHomeEntityModel Channel.containingSmartHomeEntityModel(); + inh SmartHomeEntityModel ItemCategory.containingSmartHomeEntityModel(); + inh SmartHomeEntityModel ChannelCategory.containingSmartHomeEntityModel(); + eq SmartHomeEntityModel.getChild().containingSmartHomeEntityModel() = this; + + //--- relevantFrequencySetting --- + syn FrequencySetting Group.relevantFrequencySetting() { + // first, use value defined on group itself, if any + if (this.hasFrequencySetting()) { + return this.getFrequencySetting(); } - return java.util.Optional.empty(); - } - //--- resolveChannel --- - syn java.util.Optional<Channel> SmartHomeEntityModel.resolveChannel(String channelId) { - for (Thing thing : this.getThingList()) { - for (Channel channel : thing.getChannelList()) { - if (channel.getID().equals(channelId)) { - return java.util.Optional.of(channel); - } - } + // recursively use enclosing group and use value from there, if any + Group parent = enclosingGroup(); + if (parent != null) { + return parent.relevantFrequencySetting(); } - return java.util.Optional.empty(); - } - //--- resolveChannelType --- - syn java.util.Optional<ChannelType> SmartHomeEntityModel.resolveChannelType(String channelTypeId) { - for (ChannelType channelType : this.getChannelTypeList()) { - if (channelType.getID().equals(channelTypeId)) { - return java.util.Optional.of(channelType); - } - } - return java.util.Optional.empty(); - } + // if top-level group without FrequencySetting + return null; - //--- resolveItem --- - syn java.util.Optional<Item> SmartHomeEntityModel.resolveItem(String itemId) { - if ("activity".equals(itemId)) { - return Optional.of(getActivityItem()); - } - for (Item item : items()) { - if (item.getID().equals(itemId)) { - return java.util.Optional.of(item); - } - } - return java.util.Optional.empty(); } - - //--- resolveGroup --- - syn java.util.Optional<Group> SmartHomeEntityModel.resolveGroup(String groupId) { - for (Group group : this.getGroupList()) { - if (group.getID().equals(groupId)) { - return java.util.Optional.of(group); - } + syn FrequencySetting Item.relevantFrequencySetting() { + // first, use value defined on item itself, if any + if (this.hasFrequencySetting()) { + return this.getFrequencySetting(); } - return java.util.Optional.empty(); - } - //--- resolveMqttTopic --- - syn java.util.Optional<MqttTopic> Root.resolveMqttTopic(String mqttTopicId) { - return this.getMqttRoot().resolveTopic(mqttTopicId); - } - - //--- resolveItemCategory --- - syn java.util.Optional<ItemCategory> SmartHomeEntityModel.resolveItemCategory(String categoryName) { - for (ItemCategory category : getItemCategoryList()) { - if (category.getName().equals(categoryName)) { - return java.util.Optional.of(category); - } + // use enclosing group and use value from there, if any + Group parent = enclosingGroup(); + if (parent != null) { + return parent.relevantFrequencySetting(); } - return java.util.Optional.empty(); - } - //--- resolveActivity --- - syn java.util.Optional<Activity> Root.resolveActivity(int identifier) { - for (Activity activity : getMachineLearningRoot().getActivityList()) { - if (activity.getIdentifier() == identifier) { - return java.util.Optional.of(activity); - } - } - return java.util.Optional.empty(); + // if top-level item without FrequencySetting + return null; } - //--- resolveChangeEvent --- - syn java.util.Optional<ChangeEvent> Root.resolveChangeEvent(int identifier) { - for (ChangeEvent changeEvent : getMachineLearningRoot().getChangeEventList()) { - if (changeEvent.getIdentifier() == identifier) { - return java.util.Optional.of(changeEvent); - } - } - return java.util.Optional.empty(); + //--- parameters --- + syn java.util.Set<Parameter> SmartHomeEntityModel.parameters() { + java.util.Set<Parameter> result = new java.util.TreeSet<>(modelElementComparator()); + getThingTypeList().forEach(tt -> tt.getParameterList().forEach(parameter -> result.add(parameter))); + return result; } - //--- containingThing --- - inh Thing Channel.containingThing(); - eq Thing.getChannel().containingThing() = this; + //--- channels --- + syn java.util.Set<Channel> SmartHomeEntityModel.channels() { + java.util.Set<Channel> result = new java.util.TreeSet<>(modelElementComparator()); + getThingList().forEach(thing -> thing.getChannelList().forEach(channel -> result.add(channel))); + return result; + } //--- containingNeuralNetwork --- inh NeuralNetworkRoot OutputLayer.containingNeuralNetwork(); @@ -133,7 +109,7 @@ aspect Navigation { return Optional.empty(); } Channel channel = this.getChannel(); - Thing thing = channel.containingThing(); + Thing thing = channel.getThing(); return Optional.of(thing); } diff --git a/eraser-base/src/main/jastadd/NeuralNetwork.jrag b/eraser-base/src/main/jastadd/NeuralNetwork.jrag index 999031dd8ad2260fff1db1cdfa2ec69579a706a2..b4614dd89620e334f947b4450ab5a1c7f9c520a0 100644 --- a/eraser-base/src/main/jastadd/NeuralNetwork.jrag +++ b/eraser-base/src/main/jastadd/NeuralNetwork.jrag @@ -18,17 +18,19 @@ aspect NeuralNetwork { public int getActivityIdentifier() { return (int) number; } - public List<ItemPreference> computePreferences() { - return Collections.singletonList(new ItemPreferenceDouble(affectedItem, number)); + public MachineLearningResult computePreferences() { + MachineLearningResult result = new MachineLearningResult(); + result.addItemUpdate(new ItemUpdateDouble(affectedItem, number)); + return result; } } - //--- classify --- - syn DoubleNumber NeuralNetworkRoot.classify() { - return getOutputLayer().classify(); + //--- internalClassify --- + syn DoubleNumber NeuralNetworkRoot.internalClassify() { + return getOutputLayer().internalClassify(); } - syn DoubleNumber OutputLayer.classify() { + syn DoubleNumber OutputLayer.internalClassify() { double[] inputs = new double[getNumOutputNeuron()]; for (int i = 0; i < getNumOutputNeuron(); ++i) { OutputNeuron n = getOutputNeuron(i); diff --git a/eraser-base/src/main/jastadd/Printing.jrag b/eraser-base/src/main/jastadd/Printing.jrag index fec6c4abfb0a9761e4970d3881adeead29a9464a..60e77e3a00a122f60495df3f66216a08accfbdb2 100644 --- a/eraser-base/src/main/jastadd/Printing.jrag +++ b/eraser-base/src/main/jastadd/Printing.jrag @@ -1,9 +1,11 @@ +import java.util.StringJoiner; + aspect Printing { syn String ASTNode.prettyPrint() { throw new UnsupportedOperationException(); } String ASTNode.safeID(ModelElement elem) { return elem == null ? "NULL" : elem.getID(); } - syn String Root.prettyPrint() { + eq Root.prettyPrint() { StringBuilder sb = new StringBuilder(); sb.append(getSmartHomeEntityModel().prettyPrint()); sb.append(getMqttRoot().prettyPrint()); @@ -12,8 +14,8 @@ aspect Printing { return sb.toString(); } -//--- SmartHomeEntityModel.prettyPrint() --- - syn String SmartHomeEntityModel.prettyPrint() { + //--- SmartHomeEntityModel.prettyPrint() --- + eq SmartHomeEntityModel.prettyPrint() { StringBuilder sb = new StringBuilder(); for (Thing t : getThingList()) { sb.append(t.prettyPrint()); @@ -21,9 +23,12 @@ aspect Printing { for (Item i : items()) { sb.append(i.prettyPrint()); } - for (Group g : getGroupList()) { + for (Group g : groups()) { sb.append(g.prettyPrint()); } + if (unknownGroup().getNumItem() > 0 ) { + sb.append(unknownGroup().prettyPrint()); + } for (ThingType tt : getThingTypeList()) { sb.append(tt.prettyPrint()); } @@ -36,31 +41,31 @@ aspect Printing { for (Channel c : channels()) { sb.append(c.prettyPrint()); } + for (FrequencySetting fs : getFrequencySettingList()) { + sb.append(fs.prettyPrint()); + } return sb.toString(); } -//Thing: id="" label="" type="" channels=["CHANNEL_ID", "CHANNEL_ID"] ; - syn String Thing.prettyPrint() { + // Thing: id="" label="" type="" channels=["CHANNEL_ID", "CHANNEL_ID"] ; + eq Thing.prettyPrint() { return new MemberPrinter("Thing") .addRequired("id", getID()) .addNonDefault("label", getLabel()) .addRequired("type", getType(), ThingType::getID) - .addIds("channels", getNumChannel(), getChannelList()) + .addIds("channels", getChannelList()) .build(); } -//ITEM_TYPE Item: id="" label="" state="" category="" topic="" controls=["ITEM_ID", "ITEM_ID"]; - syn String Item.prettyPrint() { + // ITEM_TYPE Item: id="" label="" state="" category="" topic=""; + eq Item.prettyPrint() { return new MemberPrinter(prettyPrintType()) .addRequired("id", getID()) .addNonDefault("label", getLabel()) .addRequired("state", getStateAsString()) .addOptional("category", hasCategory(), () -> getCategory().getName()) .addOptional("topic", hasTopic(), () -> getTopic().getTopicString()) - .addIds("controls", getControllingList()) - .addNodes("metaData", getNumMetaData(), getMetaDataList(), - md -> "\"" + md.getKey() + "\":\"" + md.getValue() + "\"", - MemberPrinter.ListBracketType.CURLY) + .addOptionalPrettyPrint(getMetaData()) .build(); } @@ -78,25 +83,34 @@ aspect Printing { eq SwitchItem.prettyPrintType() = "Switch Item" ; eq ActivityItem.prettyPrintType() = "Activity Item" ; eq DefaultItem.prettyPrintType() = "Item" ; + eq ItemPrototype.prettyPrintType() = "!! prototype not converted !!" ; // special ActivityItem printing. Always omit state. - syn String ActivityItem.prettyPrint() { + eq ActivityItem.prettyPrint() { return new MemberPrinter(prettyPrintType()) .addRequired("id", getID()) .addNonDefault("label", getLabel()) .addOptional("category", hasCategory(), () -> getCategory().getName()) .addOptional("topic", hasTopic(), () -> getTopic().getTopicString()) - .addIds("controls", getControllingList()) - .addNodes("metaData", getNumMetaData(), getMetaDataList(), - md -> "\"" + md.getKey() + "\":\"" + md.getValue() + "\"", - MemberPrinter.ListBracketType.CURLY) + .addOptionalPrettyPrint(getMetaData()) .build(); } + // MetaData: metaData={"key": "value", "key": "value"} + eq MetaData.prettyPrint() { + if (getNumKeyValuePair() == 0) { + return ""; + } + StringJoiner sj = new StringJoiner(", ", " metaData={", "}"); + for (KeyValuePair keyValuePair : getKeyValuePairList()) { + sj.add("\"" + keyValuePair.getKey() + "\":\"" + keyValuePair.getValue() + "\""); + } + return sj.toString(); + } -//Group: id="" groups=["GROUP_ID", "GROUP_ID"] items=["ITEM_ID", "ITEM_ID"] aggregation=AGG; -// AGG either '"agg-name"', or '"agg-name" ("param1", "param2")' - syn String Group.prettyPrint() { + // Group: id="" groups=["GROUP_ID", "GROUP_ID"] items=["ITEM_ID", "ITEM_ID"] aggregation=AGG; + // AGG either '"agg-name"', or '"agg-name" ("param1", "param2")' + eq Group.prettyPrint() { return new MemberPrinter("Group") .addRequired("id", getID()) .addNonDefault("label", getLabel()) @@ -106,7 +120,6 @@ aspect Printing { .build(); } - syn String GroupAggregationFunction.prettyPrint(); eq SimpleGroupAggregationFunction.prettyPrint() { if (getFunctionName() == SimpleGroupAggregationFunctionName.EQUALITY) { return ""; @@ -121,19 +134,19 @@ aspect Printing { return sb.toString(); } -//ThingType: id="" label="" description="" parameters=["PARAM_ID", "PARAM_ID"] channelTypes=["CHANNEL_TYPE_ID", "CHANNEL_TYPE_ID"]; - syn String ThingType.prettyPrint() { + // ThingType: id="" label="" description="" parameters=["PARAM_ID", "PARAM_ID"] channelTypes=["CHANNEL_TYPE_ID", "CHANNEL_TYPE_ID"]; + eq ThingType.prettyPrint() { return new MemberPrinter("ThingType") .addRequired("id", getID()) .addNonDefault("label", getLabel()) .addNonDefault("description", getDescription()) - .addIds("parameters", getNumParameter(), getParameters()) + .addIds("parameters", getParameters()) .addIds("channelTypes", getChannelTypes()) .build(); } -//Parameter: id="" label="" description="" type="" default="" required; - syn String Parameter.prettyPrint() { + // Parameter: id="" label="" description="" type="" default="" required; + eq Parameter.prettyPrint() { return new MemberPrinter("Parameter") .addRequired("id", getID()) .addNonDefault("label", getLabel()) @@ -145,8 +158,8 @@ aspect Printing { .build(); } -//ChannelType: id="" label="" description="" itemType="" category="" readyOnly; - syn String ChannelType.prettyPrint() { + // ChannelType: id="" label="" description="" itemType="" category="" readyOnly; + eq ChannelType.prettyPrint() { return new MemberPrinter("ChannelType") .addRequired("id", getID()) .addNonDefault("label", getLabel()) @@ -157,49 +170,59 @@ aspect Printing { .build(); } + // ChannelCategory syn String DefaultChannelCategory.prettyPrint() = getValue().name(); - syn String SimpleChannelCategory.prettyPrint() = getValue(); + syn String ReferringChannelCategory.prettyPrint() = getChannelCategory().prettyPrint(); -//Channel: id="" type="" links=["ITEM_ID", "ITEM_ID"]; - syn String Channel.prettyPrint() { + // Channel: id="" type="" links=["ITEM_ID", "ITEM_ID"]; + eq Channel.prettyPrint() { return new MemberPrinter("Channel") .addRequired("id", getID()) .addRequired("type", getType(), ChannelType::getID) .addIds("links", getLinkedItems()) .build(); } + + // FrequencySetting: id="" procFrec=""; + eq FrequencySetting.prettyPrint() { + return new MemberPrinter("FrequencySetting") + .addNonDefault("id", getID()) + .addNonDefault("procFreq", String.valueOf(getEventProcessingFrequency())) + .build(); + } -//ExternalHost: "hostName:port" - syn String ExternalHost.prettyPrint(int defaultPort) { - if (getPort() == defaultPort) { - // default port, do not add + // ExternalHost: "hostName:port" + syn String ExternalHost.prettyPrint() { + if (getHostName().contains(":")) { return getHostName(); - } // otherwise specify port - return getHostName() + ":" + getPort(); + } + return getHostName() + (getPort()>0 ? ":" + getPort() : ""); } -//Mqtt: incoming="" outgoing="" host=""; - syn String MqttRoot.prettyPrint() { + // Mqtt: incoming="" outgoing="" host=""; + eq MqttRoot.prettyPrint() { + ExternalHost host = getHost(); return new MemberPrinter("Mqtt") .addNonDefault("incoming", getIncomingPrefix()) .addNonDefault("outgoing", getOutgoingPrefix()) - .addOptional("host", hasHost(), () -> getHost().prettyPrint(DEFAULT_PORT)) + .addOptional("host", host.exists(), () -> host.prettyPrint()) .build(); } -//Influx: user="" password="" dbName="" host="" ; - syn String InfluxRoot.prettyPrint() { + // Influx: user="" password="" dbName="" host="" ; + eq InfluxRoot.prettyPrint() { + ExternalHost host = getHost(); return new MemberPrinter("Influx") - .addNonDefault("user", getUser(), DEFAULT_USER) - .addNonDefault("password", getPassword(), DEFAULT_PASSWORD) + .addNonDefault("user", host.getUserName(), DEFAULT_USER) + .addNonDefault("password", host.getPassword(), DEFAULT_PASSWORD) .addNonDefault("dbName", getDbName(), DEFAULT_DB_NAME) - .addOptional("host", hasHost(), () -> getHost().prettyPrint(DEFAULT_PORT)) + .addOptional("host", host.exists(), () -> host.prettyPrint()) .build(); } -// Activities: { index: "name" } - syn String MachineLearningRoot.prettyPrint() { + // Activities: { index: "name" } + eq MachineLearningRoot.prettyPrint() { return new MemberPrinter("ML") .addNodes("activities", getNumActivity(), getActivityList(), activity -> activity.getIdentifier() + ":\"" + activity.getLabel() + "\"", @@ -207,7 +230,7 @@ aspect Printing { .build(); } -// Expressions + // Expressions syn String ParenthesizedNumberExpression.prettyPrint() = "(" + getOperand().prettyPrint() + ")"; syn String NumberLiteralExpression.prettyPrint() = Double.toString(getValue()); syn String AddExpression.prettyPrint() = "(" + getLeftOperand().prettyPrint() + " + " + getRightOperand().prettyPrint() + ")"; @@ -217,7 +240,7 @@ aspect Printing { syn String PowerExpression.prettyPrint() = "(" + getLeftOperand().prettyPrint() + " ^ " + getRightOperand().prettyPrint() + ")"; syn String ParenthesizedLogicalExpression.prettyPrint() = "(" + getOperand().prettyPrint() + ")"; syn String NotExpression.prettyPrint() = "!" + getOperand().prettyPrint(); - syn String ComparingExpression.prettyPrint() { + eq ComparingExpression.prettyPrint() { switch (getComparator()) { case NotEquals: return "(" + getLeftOperand().prettyPrint() + " != " + getRightOperand().prettyPrint() + ")"; case Equals: return "(" + getLeftOperand().prettyPrint() + " == " + getRightOperand().prettyPrint() + ")"; @@ -232,4 +255,16 @@ aspect Printing { syn String OrExpression.prettyPrint() = "(" + getLeftOperand().prettyPrint() + " | " + getRightOperand().prettyPrint() + ")"; syn String Designator.prettyPrint() = getItem().getID(); + // Rules + eq Rule.prettyPrint() { + return new MemberPrinter("Rule") + .addIds("TriggeringItems", getObserverList().size(), getObserverList(), + io -> io.observedItem().getID()) + .addNodes("Condition", getNumCondition(), getConditionList(), + Condition::toString) + .addNodes("Action", getNumAction(), getActionList(), + Action::toString) + .build(); + } + } diff --git a/eraser-base/src/main/jastadd/Resolving.jrag b/eraser-base/src/main/jastadd/Resolving.jrag new file mode 100644 index 0000000000000000000000000000000000000000..f194a9f72348ede36fc56b6840a16b082880906c --- /dev/null +++ b/eraser-base/src/main/jastadd/Resolving.jrag @@ -0,0 +1,176 @@ +aspect Resolving { + + //--- resolveThingType --- + syn java.util.Optional<ThingType> SmartHomeEntityModel.resolveThingType(String thingTypeId) { + for (ThingType thingType : this.getThingTypeList()) { + if (thingType.getID().equals(thingTypeId)) { + return java.util.Optional.of(thingType); + } + } + return java.util.Optional.empty(); + } + + //--- resolveChannel --- + syn java.util.Optional<Channel> SmartHomeEntityModel.resolveChannel(String channelId) { + for (Channel channel : this.getChannelList()) { + if (channel.getID().equals(channelId)) { + return java.util.Optional.of(channel); + } + } + return java.util.Optional.empty(); + } + + //--- resolveChannelType --- + syn java.util.Optional<ChannelType> SmartHomeEntityModel.resolveChannelType(String channelTypeId) { + for (ChannelType channelType : this.getChannelTypeList()) { + if (channelType.getID().equals(channelTypeId)) { + return java.util.Optional.of(channelType); + } + } + return java.util.Optional.empty(); + } + + //--- resolveDefaultChannelCategory --- + syn java.util.Optional<DefaultChannelCategory> SmartHomeEntityModel.resolveDefaultChannelCategory(String name) { + for (DefaultChannelCategory dcc : this.defaultChannelCategoryList()) { + if (dcc.getValue().name().equals(name)) { + return java.util.Optional.of(dcc); + } + } + return java.util.Optional.empty(); + } + + //--- resolveParameter --- + syn java.util.Optional<Parameter> SmartHomeEntityModel.resolveParameter(String parameterId) { + for (Parameter parameter : this.getParameterList()) { + if (parameter.getID().equals(parameterId)) { + return java.util.Optional.of(parameter); + } + } + return java.util.Optional.empty(); + } + + //--- resolveItem --- + syn java.util.Optional<Item> SmartHomeEntityModel.resolveItem(String itemId) { + if ("activity".equals(itemId)) { + return Optional.of(getActivityItem()); + } + for (Item item : items()) { + if (item.getID().equals(itemId) && !item.isItemPlaceHolder()) { + return java.util.Optional.of(item); + } + } + return java.util.Optional.empty(); + } + + //--- resolveGroup --- + syn java.util.Optional<Group> SmartHomeEntityModel.resolveGroup(String groupId) { + for (Group group : this.getGroupList()) { + if (group.getID().equals(groupId)) { + return java.util.Optional.of(group); + } + } + return java.util.Optional.empty(); + } + + //--- resolveMqttTopic --- + syn java.util.Optional<MqttTopic> Root.resolveMqttTopic(String mqttTopicId) { + return this.getMqttRoot().resolveTopic(mqttTopicId); + } + + //--- resolveItemCategory --- + syn java.util.Optional<ItemCategory> SmartHomeEntityModel.resolveItemCategory(String categoryName) { + for (ItemCategory category : getItemCategoryList()) { + if (category.getName().equals(categoryName)) { + return java.util.Optional.of(category); + } + } + return java.util.Optional.empty(); + } + + //--- resolveActivity --- + syn java.util.Optional<Activity> Root.resolveActivity(int identifier) { + for (Activity activity : getMachineLearningRoot().getActivityList()) { + if (activity.getIdentifier() == identifier) { + return java.util.Optional.of(activity); + } + } + return java.util.Optional.empty(); + } + syn java.util.Optional<Activity> Root.resolveActivity(String label) { + for (Activity activity : getMachineLearningRoot().getActivityList()) { + if (activity.getLabel().equals(label)) { + return java.util.Optional.of(activity); + } + } + return java.util.Optional.empty(); + } + + //--- resolveChangeEvent --- + syn java.util.Optional<ChangeEvent> Root.resolveChangeEvent(int identifier) { + for (ChangeEvent changeEvent : getMachineLearningRoot().getChangeEventList()) { + if (changeEvent.getIdentifier() == identifier) { + return java.util.Optional.of(changeEvent); + } + } + return java.util.Optional.empty(); + } + + syn java.util.Optional<FrequencySetting> SmartHomeEntityModel.resolveFrequencySetting(String performanceId) { + for (FrequencySetting performance : getFrequencySettingList()) { + if (performance.getLabel().equals(performanceId)) { + return java.util.Optional.of(performance); + } + } + return java.util.Optional.empty(); + } + + // implementing resolving for relations + // _._ -> Item + refine RefResolverStubs eq ASTNode.globallyResolveItemByToken(String id) { + return getRoot().getSmartHomeEntityModel().resolveItem(id).orElseThrow(() -> new RuntimeException("Item '" + id + "' not found!")); + } + + // _._ -> FrequencySetting + refine RefResolverStubs eq ASTNode.globallyResolveFrequencySettingByToken(String id) { + return getRoot().getSmartHomeEntityModel().resolveFrequencySetting(id).orElseThrow(() -> new RuntimeException("FrequencySetting '" + id + "' not found!")); + } + + // Thing.Channel* -> Channel + refine RefResolverStubs eq Thing.resolveChannelByToken(String id, int position) { + return containingSmartHomeEntityModel().resolveChannel(id).orElseThrow(() -> new RuntimeException("Channel '" + id + "' not found!")); + } + + // Thing.Type -> ThingType + refine RefResolverStubs eq Thing.resolveTypeByToken(String id) { + return containingSmartHomeEntityModel().resolveThingType(id).orElseThrow(() -> new RuntimeException("ThingType '" + id + "' not found!")); + } + + // ThingType.Parameter* -> Parameter + refine RefResolverStubs eq ThingType.resolveParameterByToken(String id, int position) { + return containingSmartHomeEntityModel().resolveParameter(id).orElseThrow(() -> new RuntimeException("Parameter '" + id + "' not found!")); + } + + // _._ -> ChannelType + refine RefResolverStubs eq ASTNode.globallyResolveChannelTypeByToken(String id) { + return getRoot().getSmartHomeEntityModel().resolveChannelType(id).orElseThrow(() -> new RuntimeException("ChannelType '" + id + "' not found!")); + } + + // ReferringChannelCategory.ChannelCategory -> DefaultChannelCategory + refine RefResolverStubs eq ReferringChannelCategory.resolveChannelCategoryByToken(String id) { + return containingSmartHomeEntityModel().resolveDefaultChannelCategory(id).orElseThrow(() -> new RuntimeException("DefaultChannelCategory '" + id + "' not found!")); + } + + // Item.Topic? <-> MqttTopic.Item* + refine RefResolverStubs eq Item.resolveTopicByToken(String id) { + // not an actual resolving, also adds the new mqtt-topic under mqtt-root + return getRoot().getMqttRoot().getOrCreateMqttTopic(id); + } + + // Item.Category? <-> ItemCategory.Items* + refine RefResolverStubs eq Item.resolveCategoryByToken(String id) { + // not an actual resolving, also adds the new item-category under containing model + return containingSmartHomeEntityModel().getOrCreateCategoryByName(id); + } + +} diff --git a/eraser-base/src/main/jastadd/Rules.jrag b/eraser-base/src/main/jastadd/Rules.jrag index e1e0db7c7248016dd6db7bea0d6579994548cee9..e2acfca41c585dedfc71bd56c0772abcef36d247 100644 --- a/eraser-base/src/main/jastadd/Rules.jrag +++ b/eraser-base/src/main/jastadd/Rules.jrag @@ -24,25 +24,23 @@ aspect Rules { eq Item.getItemObserver().observedItem() = this; public void Rule.activateFor(Item item) { - // 1) Get or create new ItemObserver, and add it to Root - ItemObserver itemObserver; - if (item.hasItemObserver()) { - itemObserver = item.getItemObserver(); - // 1.a) Check if observer already triggers this rule - for (Rule rule : itemObserver.getTriggeredRules()) { - if (rule.equals(this)) { - logger.warn("Rule already activated for item {}. Ignoring.", item); - return; - } + // 1) Get ItemObserver + ItemObserver itemObserver = item.getItemObserver(); + // 1.a) Check if observer already triggers this rule + for (Rule rule : itemObserver.getTriggeredRules()) { + if (rule.equals(this)) { + logger.warn("Rule already activated for item {}. Ignoring.", item); + return; } - } else { - itemObserver = new ItemObserver(); - item.setItemObserver(itemObserver); } - // 2) Link event and itemObserver itemObserver.addTriggeredRule(this); } + public void Rule.deactivateFor(Item item) { + item.getItemObserver().removeTriggeredRule(this); + + } + private static java.util.concurrent.ScheduledExecutorService Rule.executor = java.util.concurrent.Executors.newScheduledThreadPool(4); /** @@ -66,19 +64,12 @@ aspect Rules { return executor.scheduleAtFixedRate(() -> trigger(null), initialDelay, period, unit); } - public void Rule.removeActivationOf(Item item) { - if (item.hasItemObserver()) { - item.getItemObserver().removeTriggeredRule(this); - } else { - // there is no observer yet - logger.warn("Item {} was never activated before.", item); - } - } // --- Condition.holdsFor --- syn boolean Condition.holdsFor(Item item); eq ItemStateCheckCondition.holdsFor(Item item) = getItemStateCheck().holdsFor(item); eq ExpressionCondition.holdsFor(Item item) = getLogicalExpression().eval(); + eq ItemStateChangeCondition.holdsFor(Item item) = getItem() == item; // --- Action.applyFor --- public abstract void Action.applyFor(Item item); @@ -101,7 +92,11 @@ aspect Rules { getAffectedItem().setStateFromString(getNewStateProvider().get()); } public void SetStateFromTriggeringItemAction.applyFor(Item item) { - item.doUpdateFor(getAffectedItem()); + Item target = getAffectedItem(); + if (target==item) { + return; + } + item.copyStateTo(target); } public void SetStateFromItemsAction.applyFor(Item item) { getAffectedItem().setStateFromString(getCombinator().apply(getSourceItems())); @@ -114,3 +109,18 @@ aspect Rules { } } + +aspect StateSyncGroup { + rewrite StateSyncGroup { + to Rule { + Rule rule = new Rule(); + + for (Item item : getTargetItemList()) { + rule.addAction(new SetStateFromTriggeringItemAction(item)); + rule.addObserver(item.getItemObserver()); + } + + return rule; + } + } +} diff --git a/eraser-base/src/main/jastadd/Rules.relast b/eraser-base/src/main/jastadd/Rules.relast index 3385858454a006cda3c052dbc41028684cb17d23..e6c3ab0138742149407d3a6279a958c2cc551ee4 100644 --- a/eraser-base/src/main/jastadd/Rules.relast +++ b/eraser-base/src/main/jastadd/Rules.relast @@ -2,6 +2,8 @@ Rule ::= Condition* Action* ; abstract Condition ; ItemStateCheckCondition : Condition ::= ItemStateCheck ; +ItemStateChangeCondition : Condition ; +rel ItemStateChangeCondition.Item -> Item; ExpressionCondition : Condition ::= LogicalExpression ; abstract Action ; NoopAction : Action ; @@ -27,3 +29,7 @@ MultiplyDoubleToStateAction : SetStateAction ::= <Multiplier:double> ; ItemObserver ::= ; rel ItemObserver.TriggeredRule* <-> Rule.Observer* ; + +FrequencySetting : LabelledModelElement ::= <EventProcessingFrequency:double> ; +StateSyncGroup : Rule ; +rel StateSyncGroup.TargetItem* -> Item; diff --git a/eraser-base/src/main/jastadd/Util.jrag b/eraser-base/src/main/jastadd/Util.jrag index de0dfb9ef17a41f46885ca2432ced96dd5b5c449..5dec6c937eb459ea7a5abbc0b26d395a8dd7ce44 100644 --- a/eraser-base/src/main/jastadd/Util.jrag +++ b/eraser-base/src/main/jastadd/Util.jrag @@ -4,16 +4,13 @@ aspect Util { // public static ExternalHost ExternalHost.createByName(String hostName) { // return new ExternalHost(hostName, 1883); // } - public void MqttRoot.setHostByName(String hostName) { - setHost(ExternalHost.of(hostName, DEFAULT_PORT)); - flushCache(); - } - public void InfluxRoot.setHostByName(String hostName) { - setHost(ExternalHost.of(hostName, DEFAULT_PORT)); + public static ExternalHost ExternalHost.of(String hostName, int defaultPort) { + return ExternalHost.of(hostName,defaultPort,null,null); } - public static ExternalHost ExternalHost.of(String hostName, int defaultPort) { + + public static ExternalHost ExternalHost.of(String hostName, int defaultPort, String username, String password) { String host = hostName; int port = defaultPort; if (hostName.contains(":")) { @@ -21,10 +18,18 @@ aspect Util { host = parts[0]; port = Integer.parseInt(parts[1]); } - return new ExternalHost(host, port); + ExternalHost eh = new ExternalHost(); + eh.setHostName(host); + eh.setPort(port); + if (username!=null) + eh.setUserName(username); + if (password!=null) + eh.setPassword(password); + + return eh; } - syn String ExternalHost.urlAsString() = String.format("http://%s:%s", getHostName(), getPort()); + syn String ExternalHost.urlAsString() = String.format("http://%s", getHostName()).concat(getPort()>0 ? ""+getPort() : ""); public static Root Root.createEmptyRoot() { Root model = new Root(); @@ -34,4 +39,35 @@ aspect Util { model.setMachineLearningRoot(new MachineLearningRoot()); return model; } + /** + * Performs a safe full traversal of the tree using getChild to trigger rewrites + */ + public void ASTNode.doSafeFullTraversal() { + for (int i = 0; i < getNumChild(); i++) { + ASTNode child = getChild(i); + if (child != null) { + child.doSafeFullTraversal(); + } + } + } + + /** + * removes the object from the AST, i.e. removes the reference from its parent to the object + * + * Please note that any intrinsic non-containment relations to the object are not removed. + * @return true, if the object had a parent. + */ + public boolean ASTNode.removeSelf() { + if (getParent() == null) { + return false; + } else { + for (int childIndex = 0; childIndex < getParent().numChildren(); childIndex++) { + if (getParent().getChild(childIndex) == this) { + getParent().removeChild(childIndex); + return true; + } + } + } + throw new RuntimeException("unable to remove child, because it was not contained in its parent!"); + } } diff --git a/eraser-base/src/main/jastadd/eraser.flex b/eraser-base/src/main/jastadd/eraser.flex index bc9b3e8d9a41c9bd4cc51d1879f081b32538f103..54dff35b310e573c8efe700596f57b36b276235a 100644 --- a/eraser-base/src/main/jastadd/eraser.flex +++ b/eraser-base/src/main/jastadd/eraser.flex @@ -57,6 +57,7 @@ Comment = "//" [^\n\r]+ "Influx" { return sym(Terminals.INFLUX); } "ML" { return sym(Terminals.ML); } "Rule" { return sym(Terminals.RULE); } +"SyncState" { return sym(Terminals.SYNC_STATE); } // special items (group already has a token definition) "Activity" { return sym(Terminals.ACTIVITY); } "Color" { return sym(Terminals.COLOR); } @@ -77,7 +78,6 @@ Comment = "//" [^\n\r]+ "channels" { return sym(Terminals.CHANNELS); } "channelTypes" { return sym(Terminals.CHANNEL_TYPES); } "context" { return sym(Terminals.CONTEXT); } -"controls" { return sym(Terminals.CONTROLS); } "dbName" { return sym(Terminals.DB_NAME); } "default" { return sym(Terminals.DEFAULT); } "description" { return sym(Terminals.DESCRIPTION); } @@ -87,6 +87,9 @@ Comment = "//" [^\n\r]+ "incoming" { return sym(Terminals.INCOMING); } "items" { return sym(Terminals.ITEMS); } "itemType" { return sym(Terminals.ITEM_TYPE); } +"FrequencySetting" { return sym(Terminals.FREQUENCY_SETTING); } +"performance" { return sym(Terminals.PERFORMANCE); } +"procFreq" { return sym(Terminals.PROCESS_FREQUENCY); } "label" { return sym(Terminals.LABEL); } "links" { return sym(Terminals.LINKS); } "metaData" { return sym(Terminals.META_DATA); } diff --git a/eraser-base/src/main/jastadd/eraser.parser b/eraser-base/src/main/jastadd/eraser.parser index 5c275fa8c16d8a694608581b08640ae8a9db5451..0da0601d2136e5ed105eddd81751da9d38e848c8 100644 --- a/eraser-base/src/main/jastadd/eraser.parser +++ b/eraser-base/src/main/jastadd/eraser.parser @@ -1,56 +1,137 @@ %header {: package de.tudresden.inf.st.eraser.jastadd.parser; import de.tudresden.inf.st.eraser.jastadd.model.*; -import de.tudresden.inf.st.eraser.jastadd.model.Action; -import de.tudresden.inf.st.eraser.parser.EraserParserHelper; +import de.tudresden.inf.st.eraser.jastadd.model.Action; // explicit import need to distinguish from beaver.Action +import de.tudresden.inf.st.eraser.jastadd.scanner.EraserScanner; +import de.tudresden.inf.st.eraser.util.JavaUtils; import java.util.Map; import java.util.HashMap; :} ; %embed {: - private EraserParserHelper eph = new EraserParserHelper(); private static <T extends ASTNode<?>> void insertZero(JastAddList<T> listNode, T child) { listNode.insertChild(child, 0); } + private static boolean checkUnusedElements = true; + private static Root initialRoot = null; + /** - * Post processing step after parsing a model, to resolve all references within the model. - * @throws java.util.NoSuchElementException if a reference can not be resolved + * Changes the behavior of the parser to check for unused elements. (Default: true) + * @param checkUnusedElements <code>true</code> to check for unused elements, <code>false</code> to skip the check */ - public void resolveReferences() { - eph.resolveReferences(); + public static void setCheckUnusedElements(boolean checkUnusedElements) { + checkUnusedElements = checkUnusedElements; + } + + public static void setNextInitialRoot(Root root) { + initialRoot = root; + } + + private String ensureTrailingSlash(String input) { + return input != null && !input.isEmpty() && !input.endsWith("/") ? input + "/" : input; + } + + private GroupPlaceHolder createGroupPlaceHolder(String id) { + GroupPlaceHolder result = new GroupPlaceHolder(); + result.setID(id); + return result; + } + + private ItemPlaceHolder createItemPlaceHolder(String id) { + ItemPlaceHolder result = new ItemPlaceHolder(); + result.setID(id); + return result; + } + + public Root parseRoot(EraserScanner scanner) throws java.io.IOException, beaver.Parser.Exception { + if (checkUnusedElements) { + //fillUnused(); + } + Root root = (Root) parse(scanner); + root.treeResolveAll(); + root.doSafeFullTraversal(); + + // resolve ItemPlaceHolders + for (Group g : root.getSmartHomeEntityModel().groups()) { + JastAddList<Item> items = g.getItemList(); + for (int i = 0; i < g.getNumItem(); i++) { + Item item = items.getChild(i); + Item realItem = item.realItem(); + if (item != realItem) { + realItem.removeSelf(); + item.removeSelf(); + items.insertChild(realItem, i); + } + } + } + // resolve GroupPlaceHolders + for (Group g : root.getSmartHomeEntityModel().groups()) { + JastAddList<Group> groups = g.getGroupList(); + for (int i = 0; i < g.getNumGroup(); i++) { + Group group = groups.getChild(i); + Group realGroup = group.realGroup(); + if (group != realGroup) { + realGroup.removeSelf(); + group.removeSelf(); + groups.insertChild(realGroup, i); + } + } + } + return root; + } + + public Expression parseExpression(EraserScanner scanner, short alt_goal) throws java.io.IOException, beaver.Parser.Exception { + Expression exp = (Expression) parse(scanner, alt_goal); + + // create some rule containing the expression to make resolving work + Root root = initialRoot != null ? initialRoot : Root.createEmptyRoot(); + initialRoot = null; + Rule rule = new Rule(); + switch (alt_goal) { + case AltGoals.logical_expression: + ExpressionCondition condition = new ExpressionCondition(); + condition.setLogicalExpression(exp.asLogicalExpression()); + rule.addCondition(condition); + break; + case AltGoals.number_expression: + SetStateFromExpression action = new SetStateFromExpression(); + action.setNumberExpression(exp.asNumberExpression()); + rule.addAction(action); + break; + default: + throw new IllegalArgumentException("Wrong goal passed " + alt_goal); + } + root.addRule(rule); + exp.treeResolveAll(); + exp.doSafeFullTraversal(); + return exp; } :} ; -%goal goal; +%goal root; %goal number_expression; %goal logical_expression; -Root goal = - thing.t goal.r {: insertZero(r.getSmartHomeEntityModel().getThingList(), t); return r; :} - | item.i goal.r {: return r; :} - | group.g goal.r {: insertZero(r.getSmartHomeEntityModel().getGroupList(), g); return r; :} - | thing_type.tt goal.r {: insertZero(r.getSmartHomeEntityModel().getThingTypeList(), tt); return r; :} - | parameter goal.r {: return r; :} - | channel_type.ct goal.r {: insertZero(r.getSmartHomeEntityModel().getChannelTypeList(), ct); return r; :} - | channel.c goal.r {: return r; :} - | mqtt_root.mr goal.r {: r.setMqttRoot(mr); return r; :} - | influx_root.ir goal.r {: r.setInfluxRoot(ir); return r; :} - | machine_learning_root.ml goal.r {: r.setMachineLearningRoot(ml); return r; :} - | rule.rule goal.r {: r.addRule(rule); return r; :} - | thing.t {: return eph.createRoot(t); :} - | item.i {: return eph.createRoot(); :} - | group.g {: return eph.createRoot(g); :} - | thing_type.tt {: return eph.createRoot(tt); :} - | parameter {: return eph.createRoot(); :} - | channel_type.ct {: return eph.createRoot(ct); :} - | channel.c {: return eph.createRoot(); :} - | mqtt_root.mr {: return eph.createRoot(mr); :} - | influx_root.ir {: return eph.createRoot(ir); :} - | machine_learning_root.ml {: return eph.createRoot(ml); :} - | rule.rule {: return eph.createRoot(rule); :} - ; +Root root = + thing.t root.r {: insertZero(r.getSmartHomeEntityModel().getThingList(), t); return r; :} + | item.i root.r {: insertZero(r.getSmartHomeEntityModel().unknownGroup().getItemList(), i); return r; :} + | group.g root.r {: insertZero(r.getSmartHomeEntityModel().getGroupList(), g); return r; :} + | state_sync_group.ss root.r {: r.addRule(ss); return r; :} + | thing_type.tt root.r {: insertZero(r.getSmartHomeEntityModel().getThingTypeList(), tt); return r; :} + | parameter.p root.r {: insertZero(r.getSmartHomeEntityModel().getParameterList(), p); return r; :} + | channel_type.ct root.r {: insertZero(r.getSmartHomeEntityModel().getChannelTypeList(), ct); return r; :} + | channel.c root.r {: insertZero(r.getSmartHomeEntityModel().getChannelList(), c); return r; :} + | mqtt_root.mr root.r {: r.setMqttRoot(mr); return r; :} + | influx_root.ir root.r {: r.setInfluxRoot(ir); return r; :} + | machine_learning_root.ml root.r {: r.setMachineLearningRoot(ml); return r; :} + | rule.rule root.r {: r.addRule(rule); return r; :} + | frequency_setting.ip root.r {: insertZero(r.getSmartHomeEntityModel().getFrequencySettingList(), ip); return r; :} + | {: return Root.createEmptyRoot(); :} + ; + +/// Expressions /// %left RB_ROUND; %left MULT, DIV; @@ -61,117 +142,131 @@ Root goal = %left AND; NumberExpression number_expression = - LB_ROUND number_expression.a MULT number_expression.b RB_ROUND {: return new MultExpression(a, b); :} - | LB_ROUND number_expression.a DIV number_expression.b RB_ROUND {: return new DivExpression(a, b); :} - | LB_ROUND number_expression.a PLUS number_expression.b RB_ROUND {: return new AddExpression(a, b); :} - | LB_ROUND number_expression.a MINUS number_expression.b RB_ROUND {: return new SubExpression(a, b); :} - | LB_ROUND number_expression.a POW number_expression.b RB_ROUND {: return new PowerExpression(a, b); :} - | literal_expression.l {: return l; :} - | designator.d {: return d; :} - | LB_ROUND number_expression.e RB_ROUND {: return new ParenthesizedNumberExpression(e); :} + LB_ROUND number_expression.a MULT number_expression.b RB_ROUND {: return new MultExpression(a, b); :} + | LB_ROUND number_expression.a DIV number_expression.b RB_ROUND {: return new DivExpression(a, b); :} + | LB_ROUND number_expression.a PLUS number_expression.b RB_ROUND {: return new AddExpression(a, b); :} + | LB_ROUND number_expression.a MINUS number_expression.b RB_ROUND {: return new SubExpression(a, b); :} + | LB_ROUND number_expression.a POW number_expression.b RB_ROUND {: return new PowerExpression(a, b); :} + | literal_expression.l {: return l; :} + | designator.d {: return d; :} + | LB_ROUND number_expression.e RB_ROUND {: return new ParenthesizedNumberExpression(e); :} ; LogicalExpression logical_expression = - LB_ROUND logical_expression.a AND logical_expression.b RB_ROUND {: return new AndExpression(a, b); :} - | LB_ROUND logical_expression.a OR logical_expression.b RB_ROUND {: return new OrExpression(a, b); :} - | EXCLAMATION logical_expression.e {: return new NotExpression(e); :} - | LB_ROUND logical_expression.e RB_ROUND {: return new ParenthesizedLogicalExpression(e); :} - | LB_ROUND number_expression.a LT number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.LessThan); :} - | LB_ROUND number_expression.a LE number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.LessOrEqualThan); :} - | LB_ROUND number_expression.a EQ number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.Equals); :} - | LB_ROUND number_expression.a NE number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.NotEquals); :} - | LB_ROUND number_expression.a GE number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.GreaterOrEqualThan); :} - | LB_ROUND number_expression.a GT number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.GreaterThan); :} + LB_ROUND logical_expression.a AND logical_expression.b RB_ROUND {: return new AndExpression(a, b); :} + | LB_ROUND logical_expression.a OR logical_expression.b RB_ROUND {: return new OrExpression(a, b); :} + | EXCLAMATION logical_expression.e {: return new NotExpression(e); :} + | LB_ROUND logical_expression.e RB_ROUND {: return new ParenthesizedLogicalExpression(e); :} + | LB_ROUND number_expression.a LT number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.LessThan); :} + | LB_ROUND number_expression.a LE number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.LessOrEqualThan); :} + | LB_ROUND number_expression.a EQ number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.Equals); :} + | LB_ROUND number_expression.a NE number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.NotEquals); :} + | LB_ROUND number_expression.a GE number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.GreaterOrEqualThan); :} + | LB_ROUND number_expression.a GT number_expression.b RB_ROUND {: return new ComparingExpression(a, b, ComparatorType.GreaterThan); :} ; NumberLiteralExpression literal_expression = - INTEGER.n {: return new NumberLiteralExpression(Integer.parseInt(n)); :} - | REAL.n {: return new NumberLiteralExpression(Double.parseDouble(n)); :} + INTEGER.n {: return new NumberLiteralExpression(Integer.parseInt(n)); :} + | REAL.n {: return new NumberLiteralExpression(Double.parseDouble(n)); :} ; Designator designator = - NAME.n {: return eph.createDesignator(n); :} + NAME.n {: Designator result = new Designator(); result.setItem(Item.createRef(n)); return result; :} ; +/// SHEM /// + Thing thing = - THING COLON thing_body.tb SEMICOLON {: return tb; :} + THING COLON thing_body.tb SEMICOLON {: return tb; :} ; // Thing: id="" label="" type="" channels=["CHANNEL_ID", "CHANNEL_ID"] ; Thing thing_body = - ID EQUALS TEXT.n thing_body.t {: return eph.setID(t, n); :} + ID EQUALS TEXT.n thing_body.t {: t.setID(n); return t; :} | LABEL EQUALS TEXT.n thing_body.t {: t.setLabel(n); return t; :} - | TYPE EQUALS TEXT.n thing_body.t {: return eph.addThingType(t, n); :} - | CHANNELS EQUALS string_list.channels thing_body.t {: return eph.setChannels(t, channels); :} - | ID EQUALS TEXT.n {: return eph.setID(new Thing(), n); :} - | LABEL EQUALS TEXT.n {: Thing t = new Thing(); t.setLabel(n); return t; :} - | TYPE EQUALS TEXT.n {: return eph.addThingType(new Thing(), n); :} - | CHANNELS EQUALS string_list.channels {: return eph.setChannels(new Thing(), channels); :} + | TYPE EQUALS TEXT.n thing_body.t {: t.setType(ThingType.createRef(n)); return t; :} + | CHANNELS EQUALS string_list.channels thing_body.t {: channels.forEach(ch -> t.addChannel(Channel.createRef(ch))); return t; :} + | {: return new Thing(); :} ; Item item = - COLOR ITEM COLON item_body.ib SEMICOLON {: return eph.retype(new ColorItem(), ib); :} - | CONTACT ITEM COLON item_body.ib SEMICOLON {: return eph.retype(new ContactItem(), ib); :} - | DATE_TIME ITEM COLON item_body.ib SEMICOLON {: return eph.retype(new DateTimeItem(), ib); :} - | DIMMER ITEM COLON item_body.ib SEMICOLON {: return eph.retype(new DimmerItem(), ib); :} - | IMAGE ITEM COLON item_body.ib SEMICOLON {: return eph.retype(new ImageItem(), ib); :} - | LOCATION ITEM COLON item_body.ib SEMICOLON {: return eph.retype(new LocationItem(), ib); :} - | NUMBER ITEM COLON item_body.ib SEMICOLON {: return eph.retype(new NumberItem(), ib); :} - | PLAYER ITEM COLON item_body.ib SEMICOLON {: return eph.retype(new PlayerItem(), ib); :} - | ROLLER_SHUTTER ITEM COLON item_body.ib SEMICOLON {: return eph.retype(new RollerShutterItem(), ib); :} - | STRING ITEM COLON item_body.ib SEMICOLON {: return eph.retype(new StringItem(), ib); :} - | SWITCH ITEM COLON item_body.ib SEMICOLON {: return eph.retype(new SwitchItem(), ib); :} - | ACTIVITY ITEM COLON item_body.ib SEMICOLON {: return eph.retype(new ActivityItem(), ib); :} - | ITEM COLON item_body.ib SEMICOLON {: return eph.retype(new DefaultItem(), ib); :} - ; - -// ITEM_TYPE Item: id="" label="" state="" category="" topic="" controls=["ITEM_ID"] metaData={"key":"value"} ; -Item item_body = - ID EQUALS TEXT.n item_body.i {: return eph.setID(i, n); :} - | LABEL EQUALS TEXT.n item_body.i {: i.setLabel(n); return i; :} - | STATE EQUALS TEXT.n item_body.i {: i.setStateFromString(n); return i; :} - | TOPIC EQUALS TEXT.n item_body.i {: return eph.setTopic(i, n); :} - | CATEGORY EQUALS TEXT.n item_body.i {: return eph.setCategory(i, n); :} - | CONTROLS EQUALS string_list.controlling item_body.i - {: return eph.setControlling(i, controlling); :} - | META_DATA EQUALS string_map.md item_body.i - {: return eph.setMetaData(i, md); :} - | {: return eph.createItem(); :} - ; - + COLOR ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new ColorItem()); return ib; :} + | CONTACT ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new ContactItem()); return ib; :} + | DATE_TIME ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new DateTimeItem()); return ib; :} + | DIMMER ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new DimmerItem()); return ib; :} + | IMAGE ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new ImageItem()); return ib; :} + | LOCATION ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new LocationItem()); return ib; :} + | NUMBER ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new NumberItem()); return ib; :} + | PLAYER ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new PlayerItem()); return ib; :} + | ROLLER_SHUTTER ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new RollerShutterItem()); return ib; :} + | STRING ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new StringItem()); return ib; :} + | SWITCH ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new SwitchItem()); return ib; :} + | ACTIVITY ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new ActivityItem()); return ib; :} + | ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new DefaultItem()); return ib; :} + ; + +// ITEM_TYPE Item: id="" label="" state="" category="" topic="" performance="" metaData={"key":"value"} ; +ItemPrototype item_body = + ID EQUALS TEXT.n item_body.i {: i.setID(n); return i; :} + | LABEL EQUALS TEXT.n item_body.i {: i.setLabel(n); return i; :} + | STATE EQUALS TEXT.n item_body.i {: i.setStateFromString(n); return i; :} + | TOPIC EQUALS TEXT.n item_body.i {: i.setTopic(MqttTopic.createRef(n)); return i; :} + | CATEGORY EQUALS TEXT.n item_body.i {: i.setCategory(ItemCategory.createRef(n)); return i; :} + | PERFORMANCE EQUALS TEXT.n item_body.i {: i.setFrequencySetting(FrequencySetting.createRef(n)); return i; :} + | META_DATA EQUALS meta_data.md item_body.i {: i.setMetaData(md); return i; :} + | {: ItemPrototype result = new ItemPrototype(); + result.disableSendState(); + return result; :}; Group group = GROUP COLON group_body.gb SEMICOLON {: return gb; :} ; -// Group: id="" groups=["GROUP_ID", "GROUP_ID"] items=["ITEM_ID", "ITEM_ID"] aggregation=""; -// Group: id="" groups=["GROUP_ID", "GROUP_ID"] items=["ITEM_ID", "ITEM_ID"] aggregation="" ("",""); +// Group: id="" groups=["GROUP_ID", "GROUP_ID"] items=["ITEM_ID", "ITEM_ID"] performance="" aggregation=""; +// Group: id="" groups=["GROUP_ID", "GROUP_ID"] items=["ITEM_ID", "ITEM_ID"] performance="" aggregation="" ("",""); Group group_body = - ID EQUALS TEXT.n group_body.g {: return eph.setID(g, n); :} - | LABEL EQUALS TEXT.n group_body.g {: g.setLabel(n); return g; :} - | GROUPS EQUALS string_list.groups group_body.g {: return eph.setSubGroups(g, groups); :} - | ITEMS EQUALS string_list.items group_body.g {: return eph.setItems(g, items); :} - | AGGREGATION EQUALS TEXT.n group_body.g {: return eph.setSimpleAggregationFunction(g, n); :} - | AGGREGATION EQUALS TEXT.n round_string_list.params group_body.g - {: return eph.setParameterizedAggregationFunction(g, n, params); :} + ID EQUALS TEXT.n group_body.g {: g.setID(n); return g; :} + | LABEL EQUALS TEXT.n group_body.g {: g.setLabel(n); return g; :} + | GROUPS EQUALS string_list.groups group_body.g {: groups.forEach(sg -> g.addGroup(createGroupPlaceHolder(sg))); return g; :} + | ITEMS EQUALS string_list.items group_body.g {: items.forEach(i -> g.addItem(createItemPlaceHolder(i))); return g; :} + | AGGREGATION EQUALS TEXT.n group_body.g {: SimpleGroupAggregationFunctionName name = SimpleGroupAggregationFunctionName.valueOf(n.toUpperCase()); + g.setAggregationFunction(new SimpleGroupAggregationFunction(name)); + return g; :} + | AGGREGATION EQUALS TEXT.n round_string_list.p group_body.g {: ParameterizedGroupAggregationFunctionName name = ParameterizedGroupAggregationFunctionName.valueOf(n.toUpperCase()); + String[] params = { "?", "?" }; + java.util.Iterator<String> iterator = p.iterator(); + for (int index = 0; index < 2; index++) { + params[index] = iterator.next(); + } + g.setAggregationFunction(new ParameterizedGroupAggregationFunction(name, params[0], params[1])); + return g; :} + | PERFORMANCE EQUALS TEXT.n group_body.g {: g.setFrequencySetting(FrequencySetting.createRef(n)); return g; :} | {: return new Group(); :} ; + +StateSyncGroup state_sync_group = SYNC_STATE COLON sync_state_body.ssb SEMICOLON {: return ssb; :}; + +// SyncState: items=["ITEM_ID", "ITEM_ID"]; +StateSyncGroup sync_state_body = + ITEMS EQUALS string_list.items sync_state_body.ssb {: for (String itemName : items) { + ssb.addTargetItem(Item.createRef(itemName)); + } + return ssb; :} + | {: return new StateSyncGroup(); :} + ; + ThingType thing_type = THING_TYPE COLON thing_type_body.ttb SEMICOLON {: return ttb; :} ; // ThingType: id="" label="" description="" parameters=[] channelTypes=[]; ThingType thing_type_body = - ID EQUALS TEXT.n thing_type_body.ttb {: return eph.setID(ttb, n); :} - | LABEL EQUALS TEXT.n thing_type_body.ttb {: ttb.setLabel(n); return ttb; :} - | DESCRIPTION EQUALS TEXT.n thing_type_body.ttb {: ttb.setDescription(n); return ttb; :} - | PARAMETERS EQUALS string_list.p thing_type_body.ttb {: return eph.setParameters(ttb, p); :} - | CHANNEL_TYPES EQUALS string_list.c thing_type_body.ttb {: return eph.setChannelTypes(ttb, c); :} - | ID EQUALS TEXT.n {: return eph.setID(new ThingType(), n); :} - | LABEL EQUALS TEXT.n {: ThingType tt = new ThingType(); tt.setLabel(n); return tt; :} - | DESCRIPTION EQUALS TEXT.n {: ThingType tt = new ThingType(); tt.setDescription(n); return tt; :} - | PARAMETERS EQUALS string_list.p {: return eph.setParameters(new ThingType(), p); :} - | CHANNEL_TYPES EQUALS string_list.c {: return eph.setChannelTypes(new ThingType(), c); :} + ID EQUALS TEXT.n thing_type_body.t {: t.setID(n); return t; :} + | LABEL EQUALS TEXT.n thing_type_body.t {: t.setLabel(n); return t; :} + | DESCRIPTION EQUALS TEXT.n thing_type_body.t {: t.setDescription(n); return t; :} + | PARAMETERS EQUALS string_list.ps thing_type_body.t {: ps.forEach(p -> t.addParameter(Parameter.createRef(p))); return t; :} + | CHANNEL_TYPES EQUALS string_list.cts thing_type_body.t {: cts.forEach(ct -> t.addChannelType(ChannelType.createRef(ct))); return t; :} + | {: return new ThingType(); :} ; Parameter parameter = @@ -181,14 +276,14 @@ Parameter parameter = // Parameter: id="" label="" description="" type="" default="" required; // Parameter: id="" label="" description="" type="" ; Parameter parameter_body = - ID EQUALS TEXT.n parameter_body.pb {: return eph.setID(pb, n); :} - | LABEL EQUALS TEXT.n parameter_body.pb {: pb.setLabel(n); return pb; :} - | DESCRIPTION EQUALS TEXT.n parameter_body.pb {: pb.setDescription(n); return pb; :} - | CONTEXT EQUALS TEXT.n parameter_body.pb {: pb.setContext(n); return pb; :} - | TYPE EQUALS TEXT.n parameter_body.pb {: return eph.setParameterValueType(pb, n); :} - | DEFAULT EQUALS TEXT.n parameter_body.pb {: return eph.setDefault(pb, n); :} - | REQUIRED parameter_body.pb {: pb.setRequired(true); return pb; :} - | {: return new Parameter(); :} + ID EQUALS TEXT.n parameter_body.p {: p.setID(n); return p; :} + | LABEL EQUALS TEXT.n parameter_body.p {: p.setLabel(n); return p; :} + | DESCRIPTION EQUALS TEXT.n parameter_body.p {: p.setDescription(n); return p; :} + | CONTEXT EQUALS TEXT.n parameter_body.p {: p.setContext(n); return p; :} + | TYPE EQUALS TEXT.n parameter_body.p {: p.setType(ParameterValueType.valueOf(JavaUtils.toTitleCase(n))); return p; :} + | DEFAULT EQUALS TEXT.n parameter_body.p {: p.setDefaultValue(new ParameterDefaultValue(n)); return p; :} + | REQUIRED parameter_body.p {: p.setRequired(true); return p; :} + | {: return new Parameter(); :} ; ChannelType channel_type = @@ -197,13 +292,21 @@ ChannelType channel_type = // ChannelType: id="" label="" description="" itemType="" category="TEXT" readyOnly; ChannelType channel_type_body = - ID EQUALS TEXT.n channel_type_body.ctb {: return eph.setID(ctb, n); :} - | LABEL EQUALS TEXT.n channel_type_body.ctb {: ctb.setLabel(n); return ctb; :} - | DESCRIPTION EQUALS TEXT.n channel_type_body.ctb {: ctb.setDescription(n); return ctb; :} - | ITEM_TYPE EQUALS TEXT.n channel_type_body.ctb {: return eph.setItemType(ctb, n); :} - | CATEGORY EQUALS TEXT.c channel_type_body.ctb {: return eph.setChannelCategory(ctb, c); :} - | READ_ONLY channel_type_body.ctb {: ctb.setReadOnly(true); return ctb; :} - | {: return new ChannelType(); :} + ID EQUALS TEXT.n channel_type_body.c {: c.setID(n); return c; :} + | LABEL EQUALS TEXT.n channel_type_body.c {: c.setLabel(n); return c; :} + | DESCRIPTION EQUALS TEXT.n channel_type_body.c {: c.setDescription(n); return c; :} + | ITEM_TYPE EQUALS TEXT.n channel_type_body.c {: c.setItemType(ItemType.valueOf(n)); return c; :} + | CATEGORY EQUALS TEXT.n channel_type_body.c {: try { + DefaultChannelCategoryValue.valueOf(n); + ReferringChannelCategory rcc = new ReferringChannelCategory(); + rcc.setChannelCategory(DefaultChannelCategory.createRef(n)); + c.setChannelCategory(rcc); + } catch (IllegalArgumentException e) { + c.setChannelCategory(new SimpleChannelCategory(n)); + } + return c; :} + | READ_ONLY channel_type_body.c {: c.setReadOnly(true); return c; :} + | {: return new ChannelType(); :} ; Channel channel = @@ -212,58 +315,72 @@ Channel channel = // Channel: id="" type="" links=["ITEM_ID", "ITEM_ID"]; Channel channel_body = - ID EQUALS TEXT.n channel_body.c {: return eph.setID(c, n); :} - | TYPE EQUALS TEXT.n channel_body.c {: return eph.setChannelType(c, n); :} - | LINKS EQUALS string_list.links channel_body.c {: return eph.setLinks(c, links); :} + ID EQUALS TEXT.n channel_body.c {: c.setID(n); return c; :} + | TYPE EQUALS TEXT.n channel_body.c {: c.setType(ChannelType.createRef(n)); return c; :} + | LINKS EQUALS string_list.links channel_body.c {: links.forEach(i -> c.addLinkedItem(Item.createRef(i))); return c; :} | {: return new Channel(); :} ; +/// MQTT /// + MqttRoot mqtt_root = MQTT COLON mqtt_root_body.mrb SEMICOLON {: return mrb; :} ; // Mqtt: incoming="" outgoing="" host=""; MqttRoot mqtt_root_body = - INCOMING EQUALS TEXT.n mqtt_root_body.mrb {: mrb.setIncomingPrefix(n); return mrb; :} - | OUTGOING EQUALS TEXT.n mqtt_root_body.mrb {: mrb.setOutgoingPrefix(n); return mrb; :} - | HOST EQUALS TEXT.n mqtt_root_body.mrb {: mrb.setHostByName(n); return mrb; :} - | USER EQUALS TEXT.n mqtt_root_body.mrb {: mrb.setUser(n); return mrb; :} - | PASSWORD EQUALS TEXT.n mqtt_root_body.mrb {: mrb.setPassword(n); return mrb; :} + INCOMING EQUALS TEXT.n mqtt_root_body.mrb {: mrb.setIncomingPrefix(ensureTrailingSlash(n)); return mrb; :} + | OUTGOING EQUALS TEXT.n mqtt_root_body.mrb {: mrb.setOutgoingPrefix(ensureTrailingSlash(n)); return mrb; :} + | HOST EQUALS TEXT.n mqtt_root_body.mrb {: mrb.getHost().setHostName(n); return mrb; :} + | USER EQUALS TEXT.n mqtt_root_body.mrb {: mrb.getHost().setUserName(n); return mrb; :} + | PASSWORD EQUALS TEXT.n mqtt_root_body.mrb {: mrb.getHost().setPassword(n); return mrb; :} | {: return new MqttRoot(); :} ; +/// Influx /// + InfluxRoot influx_root = INFLUX COLON influx_root_body.irb SEMICOLON {: return irb; :} ; // Influx: user="" password="" dbName="" host="" ; InfluxRoot influx_root_body = - USER EQUALS TEXT.n influx_root_body.irb {: irb.setUser(n); return irb; :} - | PASSWORD EQUALS TEXT.n influx_root_body.irb {: irb.setPassword(n); return irb; :} + USER EQUALS TEXT.n influx_root_body.irb {: irb.getHost().setUserName(n); return irb; :} + | PASSWORD EQUALS TEXT.n influx_root_body.irb {: irb.getHost().setPassword(n); return irb; :} | DB_NAME EQUALS TEXT.n influx_root_body.irb {: irb.setDbName(n); return irb; :} - | HOST EQUALS TEXT.n influx_root_body.irb {: irb.setHostByName(n); return irb; :} + | HOST EQUALS TEXT.n influx_root_body.irb {: irb.getHost().setHostName(n);; return irb; :} | {: return InfluxRoot.createDefault(); :} ; -// Machine Learning +/// Machine Learning /// + MachineLearningRoot machine_learning_root = ML COLON machine_learning_root_body.b SEMICOLON {: return b; :} ; // ML: activities={index:"name"} ; MachineLearningRoot machine_learning_root_body = - ACTIVITIES EQUALS integer_map.map machine_learning_root_body.b {: return eph.setActivities(b, map); :} + ACTIVITIES EQUALS integer_map.map machine_learning_root_body.b {: for (java.util.AbstractMap.SimpleEntry<Integer,String> entry : map) { + Activity activity = new Activity(); + activity.setIdentifier(entry.getKey()); + activity.setLabel(entry.getValue()); + b.addActivity(activity); + } + return b; :} | {: return MachineLearningRoot.createDefault(); :} ; // Rule: condition=condition action=a1 action=a2; // (only allow one condition and action for now) Rule rule = - RULE COLON CONDITION EQUALS condition.c action.a SEMICOLON {: return eph.createRule(c, a); :} + RULE COLON CONDITION EQUALS condition.c action.a SEMICOLON {: Rule result = new Rule(); + result.addCondition(c); + result.addAction(a); + return result; :} ; Condition condition = - logical_expression.be {: return new ExpressionCondition(be); :} + logical_expression.be {: return new ExpressionCondition(be); :} ; // TODO implement action cases @@ -292,41 +409,43 @@ StringList round_string_list = ; StringList round_string_list_body = - TEXT.n COMMA round_string_list_body.slb {: slb.add(n); return slb; :} - | TEXT.n - {: - StringList result = new StringList(); - result.add(n); - return result; - :} + TEXT.n COMMA round_string_list_body.slb {: slb.add(n); return slb; :} + | TEXT.n {: StringList result = new StringList(); + result.add(n); + return result; :} ; -StringKeyMap string_map = - LB_CURLY string_map_body.smb RB_CURLY {: return smb; :} - | LB_CURLY RB_CURLY {: return new StringKeyMap(); :} +MetaData meta_data = + LB_CURLY meta_data_body.mdb RB_CURLY {: return mdb; :} ; -StringKeyMap string_map_body = - TEXT.key COLON TEXT.value COMMA string_map_body.smb {: smb.put(key, value); return smb; :} - | TEXT.key COLON TEXT.value - {: - StringKeyMap result = new StringKeyMap(); - result.put(key, value); - return result; - :} +MetaData meta_data_body = + TEXT.key COLON TEXT.value COMMA meta_data_body.mdb {: insertZero(mdb.getKeyValuePairList(), new KeyValuePair(key, value)); return mdb; :} + | TEXT.key COLON TEXT.value {: MetaData mdb = new MetaData(); + insertZero(mdb.getKeyValuePairList(), new KeyValuePair(key, value)); + return mdb; :} + | {: return new MetaData(); :} ; IntegerKeyMap integer_map = - LB_CURLY integer_map_body.imb RB_CURLY {: return imb; :} - | LB_CURLY RB_CURLY {: return new IntegerKeyMap(); :} + LB_CURLY integer_map_body.imb RB_CURLY {: return imb; :} + | LB_CURLY RB_CURLY {: return new IntegerKeyMap(); :} ; IntegerKeyMap integer_map_body = - INTEGER.key COLON TEXT.value COMMA integer_map_body.imb {: imb.put(Integer.parseInt(key), value); return imb; :} - | INTEGER.key COLON TEXT.value - {: - IntegerKeyMap result = new IntegerKeyMap(); - result.put(Integer.parseInt(key), value); - return result; - :} + INTEGER.key COLON TEXT.value COMMA integer_map_body.imb {: imb.put(Integer.parseInt(key), value); return imb; :} + | INTEGER.key COLON TEXT.value {: IntegerKeyMap result = new IntegerKeyMap(); + result.put(Integer.parseInt(key), value); + return result; :} + ; + +FrequencySetting frequency_setting = + FREQUENCY_SETTING COLON frequency_setting_body.ipb SEMICOLON {: return ipb; :} + ; + +// FrequencySetting: id="" procFreq="" persFreq=""; +FrequencySetting frequency_setting_body = + ID EQUALS TEXT.n frequency_setting_body.fs {: fs.setID(n); return fs; :} + | PROCESS_FREQUENCY EQUALS TEXT.n frequency_setting_body.fs {: fs.setEventProcessingFrequency(Double.parseDouble(n)); return fs; :} + | {: return new FrequencySetting(); :} ; diff --git a/eraser-base/src/main/jastadd/main.relast b/eraser-base/src/main/jastadd/main.relast index b9fd3e6ca271b94dd0a59c1f596c3e4cad241d58..98e9bfdb34ac886e3d4ef568b45cb373ae2bb80d 100644 --- a/eraser-base/src/main/jastadd/main.relast +++ b/eraser-base/src/main/jastadd/main.relast @@ -6,7 +6,7 @@ User : LabelledModelElement ; rel Root.CurrentUser? -> User ; // ---------------- Util ------------------------------ -ExternalHost ::= <HostName:String> <Port:int> ; +ExternalHost ::= <HostName:String> <Port:int> <UserName:String> <Password:String>; // ---------------- InfluxDB ------------------------------ -InfluxRoot ::= <User:String> <Password:String> <DbName:String> [Host:ExternalHost] ; +InfluxRoot ::= <DbName:String> /Host:ExternalHost/ ; diff --git a/eraser-base/src/main/jastadd/mqtt.jrag b/eraser-base/src/main/jastadd/mqtt.jrag index 93e11442e9f277455b8b2a62e32375fd091c94e8..95dd53770199b461679960acd542f5ed0486681a 100644 --- a/eraser-base/src/main/jastadd/mqtt.jrag +++ b/eraser-base/src/main/jastadd/mqtt.jrag @@ -33,6 +33,7 @@ aspect MQTT { } } + //--- getIncomingTopic --- syn String MqttTopic.getIncomingTopic() = getMqttRoot().getIncomingPrefix() + getTopicString(); @@ -43,12 +44,12 @@ aspect MQTT { cache MqttRoot.getMqttSender(); syn MQTTSender MqttRoot.getMqttSender() { MQTTSender result; - if (hasHost()) { + if (getHost().exists()) { result = new MQTTSenderImpl(); } else { result = new MQTTSenderStub(); } - return result.setHost(getHost(), getUser(), getPassword()); + return result.setHost(getHost()); } //--- getMqttRoot --- @@ -77,11 +78,20 @@ aspect MQTT { refine SmartHomeEntityModel public void SmartHomeEntityModel.addNewItem(Item item) { refined(item); // update mqtt-topic to new mqtt-root - JavaUtils.ifPresentOrElse( - getRoot().getMqttRoot().resolveTopicSuffix(item.getTopic().getTopicString()), - topic -> item.setTopic(topic), - () -> de.tudresden.inf.st.eraser.util.ParserUtils.createMqttTopic(item, item.getTopic().getTopicString(), getRoot()) - ); + item.setTopic(getRoot().getMqttRoot().getOrCreateMqttTopic(item.getTopic().getTopicString())); } + + + public MqttTopic MqttRoot.getOrCreateMqttTopic(String topicString) { + return resolveTopicSuffix(topicString).orElseGet(() -> { + MqttTopic result = new MqttTopic(); + result.setTopicString(topicString); + addTopic(result); + return result; + }); + } + +syn ExternalHost MqttRoot.getHost() = new ExternalHost(); + } diff --git a/eraser-base/src/main/jastadd/mqtt.relast b/eraser-base/src/main/jastadd/mqtt.relast index 845149403b3576e7503f59eef2a4e59ca1411485..9251c2483448050e25a7bdff4153119b47f212d1 100644 --- a/eraser-base/src/main/jastadd/mqtt.relast +++ b/eraser-base/src/main/jastadd/mqtt.relast @@ -1,4 +1,4 @@ // ---------------- MQTT ------------------------------ -MqttRoot ::= Topic:MqttTopic* <IncomingPrefix:String> <OutgoingPrefix:String> <User:String> <Password:String> [Host:ExternalHost] ; +MqttRoot ::= Topic:MqttTopic* <IncomingPrefix:String> <OutgoingPrefix:String> /Host:ExternalHost/ ; MqttTopic ::= <TopicString:String> ; rel Item.Topic? <-> MqttTopic.Item* ; diff --git a/eraser-base/src/main/jastadd/shem.jrag b/eraser-base/src/main/jastadd/shem.jrag index 4cdc35da0e05c77be4ea4ab58d0f5699f87ae100..f163ebf4824dab8d3c3107a35fe4654b48e6c2ad 100644 --- a/eraser-base/src/main/jastadd/shem.jrag +++ b/eraser-base/src/main/jastadd/shem.jrag @@ -4,10 +4,73 @@ aspect SmartHomeEntityModel { } public void SmartHomeEntityModel.addNewItem(Item item) { - JavaUtils.ifPresentOrElse( - resolveGroup(de.tudresden.inf.st.eraser.util.ParserUtils.UNKNOWN_GROUP_NAME), - group -> group.addItem(item), - () -> de.tudresden.inf.st.eraser.util.ParserUtils.createUnknownGroup(this, Collections.singletonList(item))); + unknownGroup().addItem(item); + } + + public MetaData MetaData.add(String key, String value) { + addKeyValuePair(new KeyValuePair(key, value)); + return this; + } + + syn nta Group SmartHomeEntityModel.unknownGroup() { + return new Group().setID("Unknown"); + } + + syn nta JastAddList<DefaultChannelCategory> SmartHomeEntityModel.defaultChannelCategoryList() { + JastAddList<DefaultChannelCategory> result = new JastAddList<>(); + Arrays.stream(DefaultChannelCategoryValue.values()).sorted().forEach(ccv -> result.add(new DefaultChannelCategory(ccv))); + return result; + } + + rewrite ItemPrototype { + to Item { + Item result = getItemWithCorrectType(); + result.setID(this.getID()); + result.setLabel(this.getLabel()); + result.setMetaData(this.getMetaData()); + if (this.hasTopic()) { + result.setTopic(this.getTopic()); + } + if (this.hasCategory()) { + result.setCategory(this.getCategory()); + } + if (!result.isActivityItem()) { + String state = this.getStateAsString(); + result.disableSendState(); + if (state.isEmpty()) { + result.setStateToDefault(); + } else { + result.setStateFromString(state); + } + result.enableSendState(); + } + return result; + } + } + + // PlaceHolders + // ItemPlaceHolder + syn Item Item.realItem() = this; + eq ItemPlaceHolder.realItem() { + return containingSmartHomeEntityModel().resolveItem(getID()).orElseThrow(() -> new RuntimeException("Item '" + getID() + "' not found!")); + } + eq ItemPlaceHolder.prettyPrintType() = "<placeholder>"; + syn boolean Item.isItemPlaceHolder() = false; + eq ItemPlaceHolder.isItemPlaceHolder() = true; + + // GroupPlaceHolder + syn Group Group.realGroup() = this; + eq GroupPlaceHolder.realGroup() { + return containingSmartHomeEntityModel().resolveGroup(getID()).orElseThrow(() -> new RuntimeException("Group '" + getID() + "' not found!")); + } + + public ItemCategory SmartHomeEntityModel.getOrCreateCategoryByName(String categoryName) { + return this.resolveItemCategory(categoryName).orElseGet(() -> { + ItemCategory result = new ItemCategory(); + result.setName(categoryName); + addItemCategory(result); + return result; + }); } } diff --git a/eraser-base/src/main/jastadd/shem.relast b/eraser-base/src/main/jastadd/shem.relast index 34ad05feba959e8f1b39449ec56ba96e3dc7a14f..aa83375e5ca39d9e61f1dfeba1141bd722d90739 100644 --- a/eraser-base/src/main/jastadd/shem.relast +++ b/eraser-base/src/main/jastadd/shem.relast @@ -1,22 +1,26 @@ // ---------------- openHAB ------------------------------ -SmartHomeEntityModel ::= Thing* Group* ThingType* ChannelType* ChannelCategory* ItemCategory* /ActivityItem:Item/ ; +SmartHomeEntityModel ::= Thing* Group* ThingType* Parameter* ChannelType* Channel* ItemCategory* /ActivityItem:Item/ FrequencySetting*; + abstract ModelElement ::= <ID:String> ; abstract LabelledModelElement : ModelElement ::= <Label:String> ; abstract DescribableModelElement : LabelledModelElement ::= <Description:String> ; -ThingType : DescribableModelElement ::= Parameter* ; +ThingType : DescribableModelElement ::= ; +rel ThingType.Parameter* -> Parameter ; rel ThingType.ChannelType* -> ChannelType ; -Thing : LabelledModelElement ::= Channel* ; +Thing : LabelledModelElement ::= ; +rel Thing.Channel* <-> Channel.Thing ; rel Thing.Type -> ThingType ; -ChannelType : DescribableModelElement ::= <ItemType:ItemType> <ReadOnly:boolean> ; -rel ChannelType.ChannelCategory -> ChannelCategory ; +ChannelType : DescribableModelElement ::= <ItemType:ItemType> <ReadOnly:boolean> ChannelCategory ; abstract ChannelCategory ; -DefaultChannelCategory : ChannelCategory ::= <Value:DefaultChannelCategoryValue> ; +ReferringChannelCategory : ChannelCategory ; +rel ReferringChannelCategory.ChannelCategory -> DefaultChannelCategory ; SimpleChannelCategory : ChannelCategory ::= <Value:String> ; +DefaultChannelCategory ::= <Value:DefaultChannelCategoryValue> ; Channel : ModelElement ::= ; rel Channel.Type -> ChannelType ; @@ -25,9 +29,9 @@ rel Channel.LinkedItem* <-> Item.Channel? ; Parameter : DescribableModelElement ::= <Type:ParameterValueType> [DefaultValue:ParameterDefaultValue] <Context:String> <Required:boolean> ; ParameterDefaultValue ::= <Value:String> ; -abstract Item : LabelledModelElement ::= <_fetched_data:boolean> MetaData:ItemMetaData* [ItemObserver] ; -rel Item.Category? -> ItemCategory ; -rel Item.Controlling* <-> Item.ControlledBy* ; +abstract Item : LabelledModelElement ::= <_fetched_data:boolean> [MetaData] /ItemObserver/ /LastChanged/; +rel Item.Category? <-> ItemCategory.Items* ; +rel Item.FrequencySetting? -> FrequencySetting ; abstract ItemWithBooleanState : Item ::= <_state:boolean> ; abstract ItemWithStringState : Item ::= <_state:String> ; @@ -45,13 +49,22 @@ StringItem : ItemWithStringState ; SwitchItem : ItemWithBooleanState ; DefaultItem : ItemWithStringState ; ActivityItem : ItemWithDoubleState ; +ItemPrototype : ItemWithStringState ::= ItemWithCorrectType:Item ; // only used for parsing +ItemPlaceHolder : ItemWithStringState ; // only used for parsing -ItemMetaData ::= <Key:String> <Value:String> ; +MetaData ::= KeyValuePair* ; +KeyValuePair ::= <Key:String> <Value:String> ; ItemCategory ::= <Name:String> ; +LastChanged ::= <Value:Instant> ; + Group : LabelledModelElement ::= Group* Item* [AggregationFunction:GroupAggregationFunction] ; +rel Group.FrequencySetting? -> FrequencySetting ; +GroupPlaceHolder : Group ; // only used for parsing + abstract GroupAggregationFunction ; SimpleGroupAggregationFunction : GroupAggregationFunction ::= <FunctionName:SimpleGroupAggregationFunctionName> ; ParameterizedGroupAggregationFunction : GroupAggregationFunction ::= <FunctionName:ParameterizedGroupAggregationFunctionName> <Param1:String> <Param2:String> ; + diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/Main.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/Main.java index b73226fa34a99422f316579b8fea591478b97c5a..59cf34a193f7f7d7960a4356b06b9ea9fc8307b8 100644 --- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/Main.java +++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/Main.java @@ -8,7 +8,9 @@ import de.tudresden.inf.st.eraser.openhab2.mqtt.MQTTUpdater; import de.tudresden.inf.st.eraser.util.ParserUtils; import org.apache.logging.log4j.LogManager; -import java.io.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; /** * Main entry point for testing eraser. diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/InternalMachineLearningHandler.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/InternalMachineLearningHandler.java index 75d2a1d8cfc28c0293f1d55cc4e6714a6e37818a..a04db3e06454e47ecccb63b7231b0c11c13ea1d6 100644 --- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/InternalMachineLearningHandler.java +++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/InternalMachineLearningHandler.java @@ -48,8 +48,7 @@ public class InternalMachineLearningHandler implements MachineLearningEncoder, M @Override public MachineLearningResult classify() { - List<ItemPreference> preferences = model.classify().computePreferences(); - return new InternalMachineLearningResult(preferences); + return model.internalClassify().computePreferences(); } @Override diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/InternalMachineLearningResult.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/InternalMachineLearningResult.java deleted file mode 100644 index 087020b383fee89ea839b2056e22072af799a9f3..0000000000000000000000000000000000000000 --- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/InternalMachineLearningResult.java +++ /dev/null @@ -1,21 +0,0 @@ -package de.tudresden.inf.st.eraser.jastadd.model; - -import java.util.List; - -/** - * Result of a classification returned by an internally held machine learning model. - * - * @author rschoene - Initial contribution - */ -public class InternalMachineLearningResult implements MachineLearningResult { - private final List<ItemPreference> preferences; - - InternalMachineLearningResult(List<ItemPreference> preferences) { - this.preferences = preferences; - } - - @Override - public List<ItemPreference> getPreferences() { - return this.preferences; - } -} diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MQTTSender.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MQTTSender.java index 5a8d3bd9788728ee9b746347bb8da5da66e5c67b..34d06fe31302b49a46bf920541ab9d849b70d07f 100644 --- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MQTTSender.java +++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MQTTSender.java @@ -15,17 +15,7 @@ public interface MQTTSender extends AutoCloseable { * Sets the host running the MQTT broker (no username/password set). * @param host host name (IP address or domain name) and port */ - default MQTTSender setHost(ExternalHost host) { - return setHost(host, null, null); - } - - /** - * Sets the host, username and password running the MQTT broker. - * @param host host name (IP address or domain name) and port - * @param username username for authentication - * @param password password for authentication - */ - MQTTSender setHost(ExternalHost host, String username, String password); + MQTTSender setHost(ExternalHost host); /** diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MQTTSenderImpl.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MQTTSenderImpl.java index a181424f24826736fe4b3730ae4bc6390a24be7f..9fecbe6913f798a530c29b45be7af793df56e3b1 100644 --- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MQTTSenderImpl.java +++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MQTTSenderImpl.java @@ -29,11 +29,13 @@ public class MQTTSenderImpl implements MQTTSender { private TimeUnit publishTimeoutUnit; @Override - public MQTTSender setHost(ExternalHost host, String username, String password) { + public MQTTSender setHost(ExternalHost host) { /* The host running the MQTT broker. */ URI hostUri = URI.create("tcp://" + host.getHostName() + ":" + host.getPort()); logger.debug("Host is {}", hostUri); MQTT mqtt = new MQTT(); + String username = host.getUserName(); + String password = host.getPassword(); if (username != null && !username.isEmpty()) { mqtt.setUserName(username); } diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MQTTSenderStub.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MQTTSenderStub.java index 745bfcc3c97d485da1b9f3ac3df3bb2a800e47ff..2694b85f8232bb6cb83e20eef9bcd349fa63d63b 100644 --- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MQTTSenderStub.java +++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MQTTSenderStub.java @@ -20,10 +20,12 @@ public class MQTTSenderStub implements MQTTSender { private Logger logger = LogManager.getLogger(MQTTSenderStub.class); private PublishCallback callback; - public MQTTSender setHost(ExternalHost host, String username, String password) { + @Override + public MQTTSender setHost(ExternalHost host) { return this; } + public void setCallback(PublishCallback callback) { this.callback = callback; } diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MachineLearningHandlerFactory.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MachineLearningHandlerFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..94301a59f82c339db4f266b03e6c78b17a46a670 --- /dev/null +++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MachineLearningHandlerFactory.java @@ -0,0 +1,151 @@ +package de.tudresden.inf.st.eraser.jastadd.model; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.net.URL; +import java.time.Instant; +import java.util.Collections; +import java.util.List; + +/** + * Factory to create new handlers ({@link MachineLearningEncoder} and {@link MachineLearningDecoder}). + * + * <p> + * The protocol to use this class is as follows: + * <ul> + * <li>Set the model using {@link #setKnowledgeBaseRoot(Root) setKnowledgeBaseRoot()}</li> + * <li>Initialize using a config using {@link #initializeFor(MachineLearningHandlerFactoryTarget, URL) initializeFor()}</li> + * <li>Create the models using {@link #createModel()}</li> + * <li>Once finished, shutdown the factory using {@link #shutdown()}</li> + * </ul> + * </p> + * + * @author rschoene - Initial contribution + */ +public abstract class MachineLearningHandlerFactory implements MachineLearningSetRoot { + + protected Root knowledgeBase; + + public enum MachineLearningHandlerFactoryTarget { + ACTIVITY_RECOGNITION, + PREFERENCE_LEARNING + } + + @Override + public void setKnowledgeBaseRoot(Root root) { + this.knowledgeBase = root; + } + + /** + * Initialize this factory for a certain target and a configuration. + * @param target The target for which the model should be used for + * @param configUrl The location of the configuration + * @throws IOException If an error happen during loading the configuration + * @throws ClassNotFoundException If an error happens while applying the configuration + */ + public abstract void initializeFor(MachineLearningHandlerFactoryTarget target, URL configUrl) throws IOException, ClassNotFoundException; + + /** + * Creates just the {@link MachineLearningEncoder}. + * <p><b>Note: </b> {@link #createModel()} should be used instead of this method.</p> + * @return the new encoder + */ + public abstract MachineLearningEncoder createEncoder(); + + /** + * Creates just the {@link MachineLearningDecoder}. + * <p><b>Note: </b> {@link #createModel()} should be used instead of this method.</p> + * @return the new decoder + */ + public abstract MachineLearningDecoder createDecoder(); + + /** + * Creates a new model. + * To create an {@link InternalMachineLearningModel}, this method needs to be overridden. + * @return the created machine learning model + */ + public MachineLearningModel createModel() { + ExternalMachineLearningModel result = new ExternalMachineLearningModel(); + result.setEncoder(createEncoder()); + result.setDecoder(createDecoder()); + return result; + } + + /** + * Shuts down this factory, and free held resources, if any. + * Subclasses should override the default empty implementation, if needed. + */ + public void shutdown() { + // empty by default + } + + /** + * Creates a new factory to be used, if there was an error during configuration of a factory + * @return a new factory logging warning messages upon each invoked method + */ + public static MachineLearningHandlerFactory createErrorFactory() { + return new MachineLearningHandlerFactory() { + private final Logger logger = LogManager.getLogger(MachineLearningHandlerFactory.class); + @Override + public void initializeFor(MachineLearningHandlerFactoryTarget target, URL configUrl) { + logger.warn("initializeFor called for ErrorFactory"); + } + + @Override + public MachineLearningEncoder createEncoder() { + return new MachineLearningEncoder() { + @Override + public void newData(List<Item> changedItems) { + logger.warn("newData called for encoder of ErrorFactory"); + } + + @Override + public List<Item> getTargets() { + logger.warn("getTargets called for encoder of ErrorFactory"); + return Collections.emptyList(); + } + + @Override + public List<Item> getRelevantItems() { + logger.warn("getRelevantItems called for encoder of ErrorFactory"); + return Collections.emptyList(); + } + + @Override + public void triggerTraining() { + logger.warn("triggerTraining called for encoder of ErrorFactory"); + } + + @Override + public void setKnowledgeBaseRoot(Root root) { + logger.warn("setKnowledgeBaseRoot called for encoder of ErrorFactory"); + } + }; + } + + @Override + public MachineLearningDecoder createDecoder() { + return new MachineLearningDecoder() { + @Override + public MachineLearningResult classify() { + logger.warn("classify called for decoder of ErrorFactory"); + return new MachineLearningResult(); + } + + @Override + public Instant lastModelUpdate() { + logger.warn("lastModelUpdate called for decoder of ErrorFactory"); + return null; + } + + @Override + public void setKnowledgeBaseRoot(Root root) { + logger.warn("setKnowledgeBaseRoot called for decoder of ErrorFactory"); + } + }; + } + }; + } +} diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MachineLearningResult.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MachineLearningResult.java deleted file mode 100644 index eebd16d3679c1531cb02d1ba1abd0c1610303668..0000000000000000000000000000000000000000 --- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MachineLearningResult.java +++ /dev/null @@ -1,25 +0,0 @@ -package de.tudresden.inf.st.eraser.jastadd.model; - -import de.tudresden.inf.st.eraser.jastadd.model.ItemPreference; - -import java.util.List; - -/** - * Representation of a classification result using a MachineLearningModel. - * - * @author rschoene - Initial contribution - */ -@SuppressWarnings("unused") -public interface MachineLearningResult { - - // Object rawClass(); - - // double rawConfidence(); - - // can be used for both activity and preferences - /** - * Get the result as a list of item preferences, i.e., new states to be set for those items. - * @return the classification result as item preferences - */ - List<ItemPreference> getPreferences(); -} diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MachineLearningSetRoot.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MachineLearningSetRoot.java index 13fec09c243d0d15c4f5e41a8c8851321c26d5d5..560877404aac87ed4cdb2c2c0cc948ebe68ea8c2 100644 --- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MachineLearningSetRoot.java +++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/MachineLearningSetRoot.java @@ -8,7 +8,7 @@ package de.tudresden.inf.st.eraser.jastadd.model; public interface MachineLearningSetRoot { /** - * Informs this handler of the knowledge base. + * Informs this instance of the knowledge base. * This method is called before any other of the interface methods. * @param root The root node of the knowledge base */ diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/TupleHSB.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/TupleHSB.java index 8e9ee2ad31f8d16ff415bcb7fe82bd34eb87f02a..3caddf85cb769fd27066e6fa232dfbedf0b4c697 100644 --- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/TupleHSB.java +++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/jastadd/model/TupleHSB.java @@ -13,12 +13,16 @@ public class TupleHSB implements Cloneable { private int brightness; public static TupleHSB of(int hue, int saturation, int brightness) { TupleHSB result = new TupleHSB(); - result.hue = hue; - result.saturation = saturation; - result.brightness = brightness; + result.hue = hue % 360; + result.saturation = ensureBetweenZeroAndHundred(saturation); + result.brightness = ensureBetweenZeroAndHundred(brightness); return result; } + private static int ensureBetweenZeroAndHundred(int value) { + return Math.max(0, Math.min(value, 100)); + } + public int getHue() { return hue; } diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2/OpenHab2Importer.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2/OpenHab2Importer.java index d3fcaefa4cf16a7114406ac69f399dc029e1c7a7..bc3dab425186024202d91c3ad659898f6d06b3e0 100644 --- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2/OpenHab2Importer.java +++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2/OpenHab2Importer.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import de.tudresden.inf.st.eraser.jastadd.model.*; import de.tudresden.inf.st.eraser.openhab2.data.*; +import de.tudresden.inf.st.eraser.util.JavaUtils; import de.tudresden.inf.st.eraser.util.ParserUtils; import de.tudresden.inf.st.eraser.util.Tuple; import org.apache.logging.log4j.LogManager; @@ -76,6 +77,9 @@ public class OpenHab2Importer { logger.info("Read a total of {} item(s) including groups.", itemList.length); update(model, itemList); + root.treeResolveAll(); + root.doSafeFullTraversal(); + LinkData[] linkList = mapper.readValue(makeURL(linksUrl, hostAndPort), LinkData[].class); logger.info("Read a total of {} link(s).", linkList.length); update(model, linkList); @@ -113,13 +117,13 @@ public class OpenHab2Importer { private void update(SmartHomeEntityModel model, ChannelTypeData[] channelTypeList) { for (ChannelTypeData channelTypeData : channelTypeList) { ChannelType channelType = new ChannelType(); + model.addChannelType(channelType); channelType.setID(channelTypeData.UID); channelType.setLabel(channelTypeData.label); channelType.setDescription(channelTypeData.description); maybeSetItemType(channelType, channelTypeData.itemType); maybeSetChannelCategory(channelType, channelTypeData.category); maybeSetReadOnly(channelType, channelTypeData.stateDescription); - model.addChannelType(channelType); } } @@ -144,17 +148,16 @@ public class OpenHab2Importer { if (category == null) { return; } - try { - DefaultChannelCategoryValue dccv = DefaultChannelCategoryValue.valueOf(category); - channelType.setChannelCategory(new DefaultChannelCategory(dccv)); - } catch (IllegalArgumentException e) { - // channel category was not found - // store in unresolved and only warn once about it - if (nonDefaultChannelCategories.add(category)) { - logger.warn("Could not find ChannelCategory for '{}'", category); - } - channelType.setChannelCategory(new SimpleChannelCategory(category)); - } + JavaUtils.ifPresentOrElse(channelType.getRoot().getSmartHomeEntityModel().resolveDefaultChannelCategory(category), + dcc -> channelType.setChannelCategory(new ReferringChannelCategory(dcc)), + () -> { + // channel category was not found + // store in unresolved and only warn once about it + if (nonDefaultChannelCategories.add(category)) { + logger.warn("Could not find ChannelCategory for '{}'", category); + } + channelType.setChannelCategory(new SimpleChannelCategory(category)); + }); } private void maybeSetReadOnly(ChannelType channelType, StateDescriptionData stateDescription) { @@ -177,6 +180,8 @@ public class OpenHab2Importer { channel.setID(channelData.uid); ifPresent(model.resolveChannelType(channelData.channelTypeUID), "channelType", channelData, channel::setType); + model.addChannel(channel); + // now set relation thing.addChannel(channel); } } @@ -243,10 +248,12 @@ public class OpenHab2Importer { } item.enableSendState(); if (itemData.metadata != null) { + MetaData metaData = new MetaData(); + item.setMetaData(metaData); for (Map.Entry<String, MetaDataData> entry : itemData.metadata.entrySet()) { logger.debug("Add metadata for namespace {}", entry.getKey()); for (Map.Entry<String, String> metaDataEntry : entry.getValue().config.entrySet()) { - item.addMetaData(new ItemMetaData(metaDataEntry.getKey(), metaDataEntry.getValue())); + metaData.add(metaDataEntry.getKey(), metaDataEntry.getValue()); } } } @@ -278,7 +285,7 @@ public class OpenHab2Importer { } } if (!itemsWithoutGroup.isEmpty()) { - ParserUtils.createUnknownGroup(model, itemsWithoutGroup); + ParserUtils.addToUnknownGroup(model, itemsWithoutGroup); } } diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2/mqtt/MQTTUpdater.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2/mqtt/MQTTUpdater.java index 647114843839aef335b5337cb62e744870d3a0e3..0952d3c333a06256c3a0fb97181854e43f6a077f 100644 --- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2/mqtt/MQTTUpdater.java +++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2/mqtt/MQTTUpdater.java @@ -39,8 +39,8 @@ public class MQTTUpdater implements AutoCloseable { */ public void setRoot(Root root) { ExternalHost host = root.getMqttRoot().getHost(); - delegatee.setHost(host.getHostName(), host.getPort()); - delegatee.setOnMessage((topicString, message) -> + delegatee.setHost(host); + delegatee.setOnMessage((topicString, message)-> root.getMqttRoot().resolveTopic(topicString).ifPresent(topic -> topic.getItems().forEach( item -> itemUpdate(item, message)))); diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/parser/EraserParserHelper.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/parser/EraserParserHelper.java index 795d469bc786cfde73b12f8329956cbb79bb953a..c832b194ac9d7592d7ddb9e3c099b6f19a972f55 100644 --- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/parser/EraserParserHelper.java +++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/parser/EraserParserHelper.java @@ -14,7 +14,8 @@ import java.util.function.BiConsumer; * * @author rschoene - Initial contribution */ -public class EraserParserHelper { +@Deprecated +class EraserParserHelper { private Logger logger = LogManager.getLogger(EraserParserHelper.class); private Map<String, ThingType> thingTypeMap = new HashMap<>(); @@ -24,16 +25,18 @@ public class EraserParserHelper { private Map<String, Parameter> parameterMap = new HashMap<>(); private Map<String, Item> itemMap = new HashMap<>(); private Map<String, Group> groupMap = new HashMap<>(); + private Map<String, FrequencySetting> FrequencySettingMap = new HashMap<>(); private Map<Thing, String> missingThingTypeMap = new HashMap<>(); private Map<Channel, String> missingChannelTypeMap = new HashMap<>(); private Map<Item, String> missingTopicMap = new HashMap<>(); private Map<Item, String> missingItemCategoryMap = new HashMap<>(); + private Map<Designator, String> missingItemForDesignator = new HashMap<>(); private Map<Thing, Iterable<String>> missingChannelListMap = new HashMap<>(); private Map<Channel, Iterable<String>> missingItemLinkListMap = new HashMap<>(); - private Map<Item, Iterable<String>> missingControllingListMap = new HashMap<>(); + private Map<Group, Iterable<String>> missingSubGroupListMap = new HashMap<>(); private Map<Group, Iterable<String>> missingItemListMap = new HashMap<>(); private Map<ThingType, Iterable<String>> missingChannelTypeListMap = new HashMap<>(); @@ -73,6 +76,7 @@ public class EraserParserHelper { // when parsing expressions this.root = EraserParserHelper.initialRoot != null ? EraserParserHelper.initialRoot : createRoot(); } + if (checkUnusedElements) { fillUnused(); } @@ -86,7 +90,7 @@ public class EraserParserHelper { } else { resolve(itemMap, missingItemForDesignator, Designator::setItem); } - missingTopicMap.forEach((topic, parts) -> ParserUtils.createMqttTopic(topic, parts, this.root)); +// missingTopicMap.forEach((item, s) -> item.setMqttTopic(s)); this.root.getMqttRoot().ensureCorrectPrefixes(); resolveList(channelMap, missingChannelListMap, Thing::addChannel); @@ -95,14 +99,18 @@ public class EraserParserHelper { resolveList(itemMap, missingItemListMap, this::addItemToGroup); resolveList(channelTypeMap, missingChannelTypeListMap, ThingType::addChannelType); resolveList(parameterMap, missingParameterListMap, ThingType::addParameter); - resolveList(itemMap, missingControllingListMap, Item::addControlling); + createUnknownGroupIfNecessary(); - createChannelCategories(); +// createChannelCategories(); createItemCategories(); + if (checkUnusedElements) { checkUnusedElements(); } + + this.root.treeResolveAll(); + this.root.doFullTraversal(); } private void addItemToGroup(Group group, Item item) { @@ -132,16 +140,10 @@ public class EraserParserHelper { sortedDanglingItems.add(item); } } - ParserUtils.createUnknownGroup(this.root.getSmartHomeEntityModel(), sortedDanglingItems); + ParserUtils.addToUnknownGroup(this.root.getSmartHomeEntityModel(), sortedDanglingItems); } } - private void createChannelCategories() { - channelCategoryMap.values().stream().sorted(Comparator.comparing(this::ident)).forEach( - cc -> root.getSmartHomeEntityModel().addChannelCategory(cc)); - channelCategoryMap.clear(); - } - private void createItemCategories() { Map<String, ItemCategory> newCategories = new HashMap<>(); missingItemCategoryMap.forEach((item, category) -> @@ -222,6 +224,12 @@ public class EraserParserHelper { return thing; } + public FrequencySetting setID(FrequencySetting FrequencySetting, String id) { + FrequencySetting.setID(id); + FrequencySettingMap.put(id,FrequencySetting); + return FrequencySetting; + } + public ThingType setID(ThingType thingType, String id) { thingType.setID(id); thingTypeMap.put(id, thingType); @@ -235,26 +243,12 @@ public class EraserParserHelper { return ct; } - public ChannelType setChannelCategory(ChannelType ct, String name) { - ChannelCategory result = channelCategoryMap.get(name); - if (result == null) { - try { - DefaultChannelCategoryValue dccv = DefaultChannelCategoryValue.valueOf(name); - result = new DefaultChannelCategory(dccv); - } catch (IllegalArgumentException e) { - result = new SimpleChannelCategory(name); - } - channelCategoryMap.put(name, result); - } - ct.setChannelCategory(result); - return ct; - } - public Channel setChannelType(Channel c, String channelTypeName) { missingChannelTypeMap.put(c, channelTypeName); return c; } + public Channel setLinks(Channel c, StringList linkNames) { missingItemLinkListMap.put(c, linkNames); return c; @@ -285,22 +279,11 @@ public class EraserParserHelper { return item; } - public Item setMetaData(Item item, StringKeyMap metaData) { - for (AbstractMap.SimpleEntry<String, String> entry : metaData) { - item.addMetaData(new ItemMetaData(entry.getKey(), entry.getValue())); - } - return item; - } - - public Item setControlling(Item item, StringList controlling) { - missingControllingListMap.put(item, controlling); - return item; - } - public Item retype(Item itemWithCorrectType, Item prototype) { itemWithCorrectType.setID(prototype.getID()); itemWithCorrectType.setLabel(prototype.getLabel()); - itemWithCorrectType.setMetaDataList(prototype.getMetaDataList()); + itemWithCorrectType.setMetaData(prototype.getMetaData()); + itemWithCorrectType.setFrequencySetting(prototype.getFrequencySetting()); if (!(itemWithCorrectType instanceof ActivityItem)) { String state = prototype.getStateAsString(); itemWithCorrectType.disableSendState(); @@ -313,7 +296,6 @@ public class EraserParserHelper { } moveMissingForRetype(itemWithCorrectType, prototype, missingTopicMap); - moveMissingForRetype(itemWithCorrectType, prototype, missingControllingListMap); moveMissingForRetype(itemWithCorrectType, prototype, missingItemCategoryMap); itemMap.put(prototype.getID(), itemWithCorrectType); @@ -471,6 +453,12 @@ public class EraserParserHelper { return result; } + public Root createRoot(FrequencySetting frequencySetting) { + Root result = createRoot(); + result.getSmartHomeEntityModel().addFrequencySetting(frequencySetting); + return result; + } + //+++ newStuff (to be categorized) +++ public Designator createDesignator(String itemName) { Designator result = new Designator(); diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/MemberPrinter.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/MemberPrinter.java index a6c89a42bca8e978f837ad805ec4278634f3afa4..53acf15253fcfc3a91067ca332ea8e2f2cdf7642 100644 --- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/MemberPrinter.java +++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/MemberPrinter.java @@ -80,9 +80,9 @@ public class MemberPrinter { * @param listOfNodes the list of nodes * @param mapping a function to map a node to a ModelElement */ - private <T extends ASTNode> void concatIds(Iterable<T> listOfNodes, - Function<T, ? extends ModelElement> mapping) { - concatNodes(listOfNodes, t -> mapping.apply(t).getID(), true); + private <T extends ASTNode<?>> void concatIds(Iterable<T> listOfNodes, + Function<T, String> mapping) { + concatNodes(listOfNodes, mapping, true); } /** @@ -91,7 +91,7 @@ public class MemberPrinter { * @param listOfNodes the list of nodes * @param mapping a function to map a node to a String */ - private <T extends ASTNode> void concatNodes(Iterable<T> listOfNodes, + private <T extends ASTNode<?>> void concatNodes(Iterable<T> listOfNodes, Function<T, String> mapping, boolean quote) { boolean first = true; @@ -146,9 +146,9 @@ public class MemberPrinter { * @param mapping A function to map a node to a ModelElement * @return this */ - public <T extends ASTNode> MemberPrinter addIds( + public <T extends ASTNode<?>> MemberPrinter addIds( String name, int count, Iterable<T> listOfNodes, - Function<T, ? extends ModelElement> mapping) { + Function<T, String> mapping) { if (count > 0) { sb.append(' ').append(name).append("=["); concatIds(listOfNodes, mapping); @@ -168,7 +168,7 @@ public class MemberPrinter { * @param <T> The type of all nodes * @return this */ - public <T extends ASTNode> MemberPrinter addNodes(String name, int count, Iterable<T> listOfNodes, + public <T extends ASTNode<?>> MemberPrinter addNodes(String name, int count, Iterable<T> listOfNodes, Function<T, String> mapping) { return addNodes(name, count, listOfNodes, mapping, ListBracketType.SQUARE); } @@ -183,7 +183,7 @@ public class MemberPrinter { * @param bracketType The type of brackets to enclose the list with * @return this */ - public <T extends ASTNode> MemberPrinter addNodes(String name, int count, Iterable<T> listOfNodes, + public <T extends ASTNode<?>> MemberPrinter addNodes(String name, int count, Iterable<T> listOfNodes, Function<T, String> mapping, ListBracketType bracketType) { if (count > 0) { sb.append(' ').append(name).append("=").append(bracketType.begin); @@ -214,7 +214,7 @@ public class MemberPrinter { * @param child The child to append * @return this */ - public MemberPrinter addOptionalPrettyPrint(ASTNode child) { + public MemberPrinter addOptionalPrettyPrint(ASTNode<?> child) { if (child != null) { this.empty = false; sb.append(child.prettyPrint()); diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/MqttReceiver.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/MqttReceiver.java index 5fa9ad6800b3964369d0a1f140db6a4c3e0d164f..161cbaf16025d56ad5a4f9189ecde65203108051 100644 --- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/MqttReceiver.java +++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/MqttReceiver.java @@ -1,5 +1,6 @@ package de.tudresden.inf.st.eraser.util; +import de.tudresden.inf.st.eraser.jastadd.model.ExternalHost; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.fusesource.hawtbuf.Buffer; @@ -28,6 +29,8 @@ public class MqttReceiver implements AutoCloseable { /** The host running the MQTT broker. */ private URI host; + private String username; + private String password; /** The connection to the MQTT broker. */ private CallbackConnection connection; /** Whether we are subscribed to the topics yet */ @@ -49,11 +52,12 @@ public class MqttReceiver implements AutoCloseable { /** * Sets the host to receive messages from */ - public void setHost(String host, int port) { - this.host = URI.create("tcp://" + host + ":" + port); - logger.debug("Host is {}", this.host); + public void setHost(ExternalHost externalHost) { + this.host = URI.create("tcp://" + externalHost.getHostName() + ":" + externalHost.getPort()); + logger.debug("Host is {}", externalHost.getHostName()); } + public void setOnMessage(BiConsumer<String, String> callback) { this.onMessageCallback = callback; } @@ -101,10 +105,11 @@ public class MqttReceiver implements AutoCloseable { Objects.requireNonNull(this.host, "Host need to be set!"); MQTT mqtt = new MQTT(); mqtt.setHost(this.host); + mqtt.setPassword(this.password); + mqtt.setUserName(this.username); connection = mqtt.callbackConnection(); AtomicReference<Throwable> error = new AtomicReference<>(); - connection.listener(new Listener() { - @Override + connection.listener(new ExtendedListener() { public void onConnected() { logger.debug("Connected"); } @@ -115,12 +120,17 @@ public class MqttReceiver implements AutoCloseable { } @Override - public void onPublish(UTF8Buffer topicBuffer, Buffer body, Runnable ack) { - String topicString = topicBuffer.toString(); + public void onPublish(UTF8Buffer topic, Buffer body, Callback<Callback<Void>> ack) { + String topicString = topic.toString(); String message = body.ascii().toString(); // logger.debug("{}: {}", topicString, message); onMessageCallback.accept(topicString, message); - ack.run(); // always acknowledge message + ack.onSuccess(null); // always acknowledge message + } + + @Override + public void onPublish(UTF8Buffer topicBuffer, Buffer body, Runnable ack) { + logger.warn("onPublish should not be called"); } @Override diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/ParserUtils.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/ParserUtils.java index 9970a680fe141f7ce0f1ad3cb0d4a836fd1caf95..5b365efb7b3e184a76396411a659ce624c76a779 100644 --- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/ParserUtils.java +++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/ParserUtils.java @@ -3,10 +3,11 @@ package de.tudresden.inf.st.eraser.util; import beaver.Parser; import beaver.Scanner; import beaver.Symbol; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; import de.tudresden.inf.st.eraser.jastadd.model.*; import de.tudresden.inf.st.eraser.jastadd.parser.EraserParser; import de.tudresden.inf.st.eraser.jastadd.scanner.EraserScanner; -import de.tudresden.inf.st.eraser.parser.EraserParserHelper; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -24,9 +25,11 @@ import java.util.Objects; */ public class ParserUtils { - public static final String UNKNOWN_GROUP_NAME = "Unknown"; private static boolean verboseLoading = false; private static final Logger logger = LogManager.getLogger(ParserUtils.class); + private static final ObjectMapper OBJECT_MAPPER_INSTANCE = new ObjectMapper() + .enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS) + .enable(SerializationFeature.INDENT_OUTPUT); private interface ReaderProvider { Reader provide() throws IOException; @@ -140,8 +143,7 @@ public class ParserUtils { EraserScanner scanner = new EraserScanner(reader); EraserParser parser = new EraserParser(); - Root result = (Root) parser.parse(scanner); - parser.resolveReferences(); + Root result = parser.parseRoot(scanner); reader.close(); return result; } @@ -167,31 +169,14 @@ public class ParserUtils { } /** - * Create well-known group call "Unknown" and add all dangling items to it. + * Add all dangling items to unknown group. * @param model The model to operate on * @param danglingItems A list of items to add to the new group */ - public static void createUnknownGroup(SmartHomeEntityModel model, Collection<Item> danglingItems) { - Group unknownGroup = new Group(); - unknownGroup.setID(UNKNOWN_GROUP_NAME); - model.addGroup(unknownGroup); + public static void addToUnknownGroup(SmartHomeEntityModel model, Collection<Item> danglingItems) { + Group unknownGroup = model.unknownGroup(); danglingItems.forEach(unknownGroup::addItem); - logger.info("Created new {}", unknownGroup.prettyPrint().trim()); - } - - /** - * Create a topic for the given topic name and assign it to the given item. - * @param item The item to which the topic will be assigned to - * @param topicSuffix The full topic name - * @param root The model to operate on - */ - public static void createMqttTopic(Item item, String topicSuffix, Root root) { - item.setTopic(root.getMqttRoot().resolveTopicSuffix(topicSuffix).orElseGet(() -> { - MqttTopic result = new MqttTopic(); - result.setTopicString(topicSuffix); - root.getMqttRoot().addTopic(result); - return result; - })); + logger.info("Updated unknown group {}", unknownGroup.prettyPrint().trim()); } public static NumberExpression parseNumberExpression(String expression_string) throws IOException, Parser.Exception { @@ -211,7 +196,6 @@ public class ParserUtils { } private static Expression parseExpression(String expression_string, short alt_goal, Root root) throws IOException, Parser.Exception { - EraserParserHelper.setInitialRoot(root); StringReader reader = new StringReader(expression_string); if (verboseLoading) { EraserScanner scanner = new EraserScanner(reader); @@ -225,13 +209,12 @@ public class ParserUtils { e.printStackTrace(); } } + EraserParser.setNextInitialRoot(root); reader = new StringReader(expression_string); EraserScanner scanner = new EraserScanner(reader); EraserParser parser = new EraserParser(); - Expression result = (Expression) parser.parse(scanner, alt_goal); - parser.resolveReferences(); + Expression result = parser.parseExpression(scanner, alt_goal); reader.close(); - EraserParserHelper.setInitialRoot(null); return result; } @@ -240,8 +223,7 @@ public class ParserUtils { StringReader reader = new StringReader(definition); EraserScanner scanner = new EraserScanner(reader); EraserParser parser = new EraserParser(); - Root root = (Root) parser.parse(scanner); - parser.resolveReferences(); + Root root = parser.parseRoot(scanner); reader.close(); int size = root.getSmartHomeEntityModel().items().size(); if (size == 0) { @@ -253,4 +235,17 @@ public class ParserUtils { return root.getSmartHomeEntityModel().items().get(0); } + public static <T> T loadFrom(URL location, Class<T> valueType) throws IOException { + return OBJECT_MAPPER_INSTANCE.readValue(location, valueType); + } + + public static <T> void saveTo(T value, URL location) throws IOException { + if (location.getProtocol().equals("file")) { + // write to (local) file directly + OBJECT_MAPPER_INSTANCE.writeValue(new File(location.getFile()), value); + } else { + OBJECT_MAPPER_INSTANCE.writeValue(location.openConnection().getOutputStream(), value); + } + } + } diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/TestUtils.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/TestUtils.java index fd6024bdfd45e2652eb4eaa118eada3dbfc75f23..1e215384019efafe9139a8139d5034e9cc10f864 100644 --- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/TestUtils.java +++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/TestUtils.java @@ -2,6 +2,8 @@ package de.tudresden.inf.st.eraser.util; import de.tudresden.inf.st.eraser.jastadd.model.*; +import java.util.ArrayList; + /** * Helper class to create models used in tests. * @@ -35,6 +37,15 @@ public class TestUtils { return ModelAndItem.of(root.getSmartHomeEntityModel(), item); } + public static SmartHomeEntityModel createModelWithGroup() { + Root root = Root.createEmptyRoot(); + Group group0 = new Group(); + group0.setID("Group0"); + root.getSmartHomeEntityModel().addGroup(group0); + + return root.getSmartHomeEntityModel(); + } + public static NumberItem addItemTo(SmartHomeEntityModel model, double initialValue) { return addItemTo(model, initialValue, false); } diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/ControllingItemTest.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/ControllingItemTest.java index 40d7a97d5575269136f92293c7f2f7317dcc7cbe..e9ebe848d4929f9275abd923f06eebc91949ebe6 100644 --- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/ControllingItemTest.java +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/ControllingItemTest.java @@ -1,13 +1,14 @@ package de.tudresden.inf.st.eraser; +import de.tudresden.inf.st.eraser.jastadd.model.*; import de.tudresden.inf.st.eraser.util.TestUtils; import de.tudresden.inf.st.eraser.util.TestUtils.ModelAndItem; -import de.tudresden.inf.st.eraser.jastadd.model.*; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.function.Consumer; +import static org.junit.jupiter.api.Assertions.*; + /** * Testing the simple rule engine. * @@ -24,12 +25,25 @@ public class ControllingItemTest { NumberItem item2 = TestUtils.addItemTo(mai.model, 4, true); item1.addControlling(item2); - Assert.assertEquals(0, item1.getState(), DELTA); - Assert.assertEquals(4, item2.getState(), DELTA); + assertEquals(0, item1.getState(), DELTA); + assertEquals(4, item2.getState(), DELTA); item1.setState(5); - Assert.assertEquals(5, item1.getState(), DELTA); - Assert.assertEquals("Item was not controlled correctly", 5, item2.getState(), DELTA); + assertEquals(5, item1.getState(), DELTA); + assertEquals(5, item2.getState(), DELTA, "Item was not controlled correctly"); + } + @Test + public void testRemoveControlling() { + ModelAndItem mai = TestUtils.createModelAndItem(0, true); + NumberItem controlledItem = mai.item; + NumberItem controllingItem = TestUtils.addItemTo(mai.model, 4, true); + controllingItem.addControlling(controlledItem); + controllingItem.removeControlling(controlledItem); + + controllingItem.setState(8); + + assertEquals(8, controllingItem.getState(), DELTA); + assertEquals(0, controlledItem.getState(), DELTA, "Item was still controlled although the controlling was removed"); } @Test @@ -43,16 +57,16 @@ public class ControllingItemTest { item1.addControlling(item3); item1.addControlling(item4); - Assert.assertEquals(0, item1.getState(), DELTA); - Assert.assertEquals(3, item2.getState(), DELTA); - Assert.assertEquals(4, item3.getState(), DELTA); - Assert.assertEquals(5, item4.getState(), DELTA); + assertEquals(0, item1.getState(), DELTA); + assertEquals(3, item2.getState(), DELTA); + assertEquals(4, item3.getState(), DELTA); + assertEquals(5, item4.getState(), DELTA); item1.setState(5); - Assert.assertEquals(5, item1.getState(), DELTA); - Assert.assertEquals("Item2 was not controlled correctly", 5, item2.getState(), DELTA); - Assert.assertEquals("Item3 was not controlled correctly", 5, item3.getState(), DELTA); - Assert.assertEquals("Item4 was not controlled correctly", 5, item4.getState(), DELTA); + assertEquals(5, item1.getState(), DELTA); + assertEquals(5, item2.getState(), DELTA, "Item2 was not controlled correctly"); + assertEquals(5, item3.getState(), DELTA, "Item3 was not controlled correctly"); + assertEquals(5, item4.getState(), DELTA, "Item4 was not controlled correctly"); } @Test @@ -80,41 +94,41 @@ public class ControllingItemTest { target.addControlledBy(booleanItem); target.addControlledBy(colorItem); - Assert.assertEquals(0, numberItem.getState(), DELTA); - Assert.assertEquals("0", stringItem.getState()); - Assert.assertFalse(booleanItem.getState()); - Assert.assertEquals(TupleHSB.of(0, 0, 0), colorItem.getState()); - Assert.assertEquals(TupleHSB.of(0, 0, 0), target.getState()); + assertEquals(0, numberItem.getState(), DELTA); + assertEquals("0", stringItem.getState()); + assertFalse(booleanItem.getState()); + assertEquals(TupleHSB.of(0, 0, 0), colorItem.getState()); + assertEquals(TupleHSB.of(0, 0, 0), target.getState()); // number 5 -> set brightness to 5 numberItem.setState(5); - Assert.assertEquals(5, numberItem.getState(), DELTA); - Assert.assertEquals("Item was not controlled correctly", TupleHSB.of(0, 0, 5), target.getState()); + assertEquals(5, numberItem.getState(), DELTA); + assertEquals(TupleHSB.of(0, 0, 5), target.getState(), "Item was not controlled correctly"); // string 30 -> set brightness to 30 stringItem.setState("30"); - Assert.assertEquals("30", stringItem.getState()); - Assert.assertEquals("Item was not controlled correctly", TupleHSB.of(0, 0, 30), target.getState()); + assertEquals("30", stringItem.getState()); + assertEquals(TupleHSB.of(0, 0, 30), target.getState(), "Item was not controlled correctly"); // string 30,20,10 -> set HSB to (30,20,10) stringItem.setState("30,20,10"); - Assert.assertEquals("30,20,10", stringItem.getState()); - Assert.assertEquals("Item was not controlled correctly", TupleHSB.of(30, 20, 10), target.getState()); + assertEquals("30,20,10", stringItem.getState()); + assertEquals(TupleHSB.of(30, 20, 10), target.getState(), "Item was not controlled correctly"); - // boolean true -> set brightness to full (100) + // boolean true -> brightness should still be 10 because it's already turned on booleanItem.setState(true); - Assert.assertTrue(booleanItem.getState()); - Assert.assertEquals("Item was not controlled correctly", TupleHSB.of(30, 20, 100), target.getState()); + assertTrue(booleanItem.getState()); + assertEquals(TupleHSB.of(30, 20, 10), target.getState(), "Item was not controlled correctly"); // color (33,33,33) -> set brightness to (33,33,33) colorItem.setState(TupleHSB.of(33, 33, 33)); - Assert.assertEquals(TupleHSB.of(33, 33, 33), colorItem.getState()); - Assert.assertEquals("Item was not controlled correctly", TupleHSB.of(33, 33, 33), target.getState()); + assertEquals(TupleHSB.of(33, 33, 33), colorItem.getState()); + assertEquals(TupleHSB.of(33, 33, 33), target.getState(), "Item was not controlled correctly"); // number 44 -> set brightness to 44 numberItem.setState(44); - Assert.assertEquals(44, numberItem.getState(), DELTA); - Assert.assertEquals("Item was not controlled correctly", TupleHSB.of(33, 33, 44), target.getState()); + assertEquals(44, numberItem.getState(), DELTA); + assertEquals(TupleHSB.of(33, 33, 44), target.getState(), "Item was not controlled correctly"); } private <T extends Item> T initAndAddItem(Group group, T item, Consumer<T> setState) { @@ -124,4 +138,25 @@ public class ControllingItemTest { item.enableSendState(); return item; } + + + @Test + public void testCircularControlling() { + ModelAndItem mai = TestUtils.createModelAndItem(0, true); + NumberItem item1 = mai.item; + NumberItem item2 = TestUtils.addItemTo(mai.model, 4, true); + item1.addControlling(item2); + item2.addControlling(item1); + + assertEquals(0, item1.getState(), DELTA); + assertEquals(4, item2.getState(), DELTA); + + item1.setState(5); + assertEquals(5, item1.getState(), DELTA, "Item was not controlled correctly"); + assertEquals(5, item2.getState(), DELTA, "Item was not controlled correctly"); + + item2.setState(3); + assertEquals(3, item1.getState(), DELTA, "Item was not controlled correctly"); + assertEquals(3, item2.getState(), DELTA, "Item was not controlled correctly"); + } } diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/DecisionTreeTest.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/DecisionTreeTest.java index fa93e0691d782030d2e45c385f9327a57c9e1ae4..ee4755b32dcc845e18df6c704c8830be081701b6 100644 --- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/DecisionTreeTest.java +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/DecisionTreeTest.java @@ -2,8 +2,9 @@ package de.tudresden.inf.st.eraser; import de.tudresden.inf.st.eraser.jastadd.model.*; import de.tudresden.inf.st.eraser.util.TestUtils; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * Testing the decision tree aspect of the knowledge base. @@ -25,18 +26,18 @@ public class DecisionTreeTest { dtroot.setRootRule(newRule(isLessThanFour, isFourOrGreater, "check item1", check)); // current value is four, so return value should be "four or greater" - Leaf leaf = dtroot.classify(); - Assert.assertEquals(isFourOrGreater, leaf); + Leaf leaf = dtroot.internalClassify(); + assertEquals(isFourOrGreater, leaf); // change value to 5, so return value should still be "four or greater" mai.item.setState(5); - leaf = dtroot.classify(); - Assert.assertEquals(isFourOrGreater, leaf); + leaf = dtroot.internalClassify(); + assertEquals(isFourOrGreater, leaf); // change value to 2, so return value should now be "less than four" mai.item.setState(2); - leaf = dtroot.classify(); - Assert.assertEquals(isLessThanFour, leaf); + leaf = dtroot.internalClassify(); + assertEquals(isLessThanFour, leaf); } @Test @@ -64,18 +65,18 @@ public class DecisionTreeTest { dtroot.setRootRule(newRule(rule25, rule75, "50-item1", check50)); // current value is 20, so return value should be "less than 25" - Leaf leaf = dtroot.classify(); - Assert.assertEquals(isLessThan25, leaf); + Leaf leaf = dtroot.internalClassify(); + assertEquals(isLessThan25, leaf); // change value to 25, so return value should still be "25 or greater" mai.item.setState(25); - leaf = dtroot.classify(); - Assert.assertEquals(is25OrGreater, leaf); + leaf = dtroot.internalClassify(); + assertEquals(is25OrGreater, leaf); // change value to 100, so return value should now be "greater than 75" mai.item.setState(100); - leaf = dtroot.classify(); - Assert.assertEquals(is75OrGreater, leaf); + leaf = dtroot.internalClassify(); + assertEquals(is75OrGreater, leaf); } @Test @@ -139,29 +140,29 @@ public class DecisionTreeTest { for (TestResult result : testResults) { mai.item.setState(Math.round(result.value)); - Leaf leaf = dtroot.classify(); - Assert.assertEquals(result.chooseLeft ? leaf : right, leaf); + Leaf leaf = dtroot.internalClassify(); + assertEquals(result.chooseLeft ? leaf : right, leaf); } } private ItemStateCheckRule newRule(DecisionTreeElement left, DecisionTreeElement right, String label, - ItemStateNumberCheck check, ItemPreference... preferences) { + ItemStateNumberCheck check, ItemUpdate... updates) { ItemStateCheckRule result = new ItemStateCheckRule(); result.setLeft(left); result.setRight(right); result.setLabel(label); result.setItemStateCheck(check); - for (ItemPreference preference : preferences) { - result.addPreference(preference); + for (ItemUpdate update : updates) { + result.addPreference(update); } return result; } - private DecisionTreeLeaf newLeaf(String label, ItemPreference... preferences) { + private DecisionTreeLeaf newLeaf(String label, ItemUpdate... updates) { DecisionTreeLeaf result = new DecisionTreeLeaf(); result.setLabel(label); - for (ItemPreference preference : preferences) { - result.addPreference(preference); + for (ItemUpdate update : updates) { + result.addPreference(update); } return result; } diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/ExpressionEvalTest.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/ExpressionEvalTest.java index 1758931346632a63c9f3297912b1fb7d1430da46..7b14c73467a69294104a382618b0b752207a6dae 100644 --- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/ExpressionEvalTest.java +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/ExpressionEvalTest.java @@ -3,13 +3,12 @@ package de.tudresden.inf.st.eraser; import beaver.Parser; import de.tudresden.inf.st.eraser.jastadd.model.*; import de.tudresden.inf.st.eraser.util.ParserUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.hamcrest.core.IsInstanceOf.instanceOf; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; /** * Test correct evaluation of NumberExpression and LogicalExpression. @@ -21,56 +20,56 @@ public class ExpressionEvalTest { @Test public void plusExpression() throws IOException, Parser.Exception { NumberExpression sut = ParserUtils.parseNumberExpression("(3 + 4)"); - assertThat(sut.eval(), equalTo(7.0)); + assertEquals(7.0, sut.eval()); } @Test public void minusExpression() throws IOException, Parser.Exception { NumberExpression sut = ParserUtils.parseNumberExpression("(4.1 - 12.5)"); - assertThat(sut.eval(), equalTo(-8.4)); + assertEquals(-8.4, sut.eval()); } @Test - public void multExpression() throws IOException, Parser.Exception { + public void mulExpression() throws IOException, Parser.Exception { NumberExpression sut = ParserUtils.parseNumberExpression("(3 * 4)"); - assertThat(sut.eval(), equalTo(12.0)); + assertEquals(12.0, sut.eval()); } @Test public void divExpression() throws IOException, Parser.Exception { NumberExpression sut = ParserUtils.parseNumberExpression("(1.1 / 4.0)"); - assertThat(sut.eval(), equalTo(0.275)); + assertEquals(0.275, sut.eval()); } @Test public void powerExpression() throws IOException, Parser.Exception { NumberExpression sut = ParserUtils.parseNumberExpression("(16 ^ 0.5)"); - assertThat(sut.eval(), equalTo(4.0)); + assertEquals(4.0, sut.eval()); } @Test public void parenthesizedExpression() throws IOException, Parser.Exception { NumberExpression sut = ParserUtils.parseNumberExpression("(3)"); - assertThat(sut.eval(), equalTo(3.0)); + assertEquals(3.0, sut.eval()); } @Test public void complexExpression() throws IOException, Parser.Exception { NumberExpression sut = ParserUtils.parseNumberExpression("((3 + 4) * (1 / (12 - 8)))"); - assertThat(sut.eval(), equalTo(1.75)); + assertEquals(1.75, sut.eval()); MultExpression multExpression = (MultExpression) sut; // 3+4 - assertThat(multExpression.getLeftOperand(), instanceOf(AddExpression.class)); - assertThat(multExpression.getLeftOperand().eval(), equalTo(7.0)); + assertThat(multExpression.getLeftOperand()).isInstanceOf(AddExpression.class); + assertEquals(7.0, multExpression.getLeftOperand().eval()); // 1/(12-8) - assertThat(multExpression.getRightOperand(), instanceOf(DivExpression.class)); + assertThat(multExpression.getRightOperand()).isInstanceOf(DivExpression.class); DivExpression divExpression = (DivExpression) multExpression.getRightOperand(); - assertThat(divExpression.eval(), equalTo(0.25)); + assertEquals(0.25, divExpression.eval()); // 12-8 - assertThat(divExpression.getRightOperand().eval(), equalTo(4.0)); + assertEquals(4.0, divExpression.getRightOperand().eval()); } @Test @@ -78,12 +77,12 @@ public class ExpressionEvalTest { double itemValue = 5.3; Item referenceItem = ParserUtils.parseItem("Number Item: id=\"myItem\" state=\"" + itemValue + "\";"); NumberExpression sut = ParserUtils.parseNumberExpression("(myItem * 3)", referenceItem.getRoot()); - assertThat(sut.eval(), equalTo(itemValue * 3)); + assertEquals(itemValue * 3, sut.eval()); // set item state to new value itemValue = 17; referenceItem.setStateFromDouble(itemValue); - assertThat(sut.eval(), equalTo(itemValue * 3)); + assertEquals(itemValue * 3, sut.eval()); } @Test @@ -112,54 +111,54 @@ public class ExpressionEvalTest { } private void comparingExpression(double left, String actualComparatorString, double right, boolean expectedResult) throws IOException, Parser.Exception { - String expression = String.format("(%s %s %s)", Double.toString(left), actualComparatorString, Double.toString(right)); + String expression = String.format("(%s %s %s)", left, actualComparatorString, right); LogicalExpression sut = ParserUtils.parseLogicalExpression(expression); - assertThat(sut.eval(), equalTo(expectedResult)); + assertEquals(expectedResult, sut.eval()); } @Test public void notExpression() throws IOException, Parser.Exception { LogicalExpression sut = ParserUtils.parseLogicalExpression("!(0==0)"); - assertThat(sut.eval(), equalTo(false)); + assertFalse(sut.eval()); LogicalExpression sut2 = ParserUtils.parseLogicalExpression("!!(0==0)"); - assertThat(sut2.eval(), equalTo(true)); + assertTrue(sut2.eval()); } @Test public void andExpression() throws IOException, Parser.Exception { LogicalExpression sut = ParserUtils.parseLogicalExpression("((0==0) & (0==0))"); - assertThat(sut.eval(), equalTo(true)); + assertTrue(sut.eval()); LogicalExpression sut2 = ParserUtils.parseLogicalExpression("((0==0) & (0==1))"); - assertThat(sut2.eval(), equalTo(false)); + assertFalse(sut2.eval()); LogicalExpression sut3 = ParserUtils.parseLogicalExpression("((0==1) & (0==0))"); - assertThat(sut3.eval(), equalTo(false)); + assertFalse(sut3.eval()); LogicalExpression sut4 = ParserUtils.parseLogicalExpression("((0==1) & (0==1))"); - assertThat(sut4.eval(), equalTo(false)); + assertFalse(sut4.eval()); } @Test public void orExpression() throws IOException, Parser.Exception { LogicalExpression sut = ParserUtils.parseLogicalExpression("((0==0) | (0==0))"); - assertThat(sut.eval(), equalTo(true)); + assertTrue(sut.eval()); LogicalExpression sut2 = ParserUtils.parseLogicalExpression("((0==0) | (0==1))"); - assertThat(sut2.eval(), equalTo(true)); + assertTrue(sut2.eval()); LogicalExpression sut3 = ParserUtils.parseLogicalExpression("((0==1) | (0==0))"); - assertThat(sut3.eval(), equalTo(true)); + assertTrue(sut3.eval()); LogicalExpression sut4 = ParserUtils.parseLogicalExpression("((0==1) | (0==1))"); - assertThat(sut4.eval(), equalTo(false)); + assertFalse(sut4.eval()); } @Test public void parenthesizedLogicalExpression() throws IOException, Parser.Exception { LogicalExpression sut = ParserUtils.parseLogicalExpression("((0==0))"); - assertThat(sut.eval(), equalTo(true)); + assertTrue(sut.eval()); ParenthesizedLogicalExpression parenthesizedLogicalExpression = (ParenthesizedLogicalExpression) sut; - assertThat(parenthesizedLogicalExpression.getOperand().eval(), equalTo(true)); + assertTrue(parenthesizedLogicalExpression.getOperand().eval()); } } diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/ExpressionParserTest.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/ExpressionParserTest.java index a034d3c8f8699046c9c89d2f139f14880a80b704..a1c9f0014bca6d7ddb95e729d2ee725a203719d1 100644 --- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/ExpressionParserTest.java +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/ExpressionParserTest.java @@ -3,14 +3,12 @@ package de.tudresden.inf.st.eraser; import beaver.Parser; import de.tudresden.inf.st.eraser.jastadd.model.*; import de.tudresden.inf.st.eraser.util.ParserUtils; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.hamcrest.core.IsInstanceOf.instanceOf; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * Test correct parsing of NumberExpression and LogicalExpression. @@ -24,125 +22,125 @@ public class ExpressionParserTest { @Test public void plusExpression() throws IOException, Parser.Exception { NumberExpression sut = parseWithRoundTripNumberExpression("(3 + 4)"); - assertThat(sut, instanceOf(AddExpression.class)); + assertThat(sut).isInstanceOf(AddExpression.class); AddExpression addExpression = (AddExpression) sut; - assertThat(addExpression.getLeftOperand(), instanceOf(NumberLiteralExpression.class)); + assertThat(addExpression.getLeftOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression left = (NumberLiteralExpression) addExpression.getLeftOperand(); - assertThat(left.getValue(), equalTo(3.0)); - assertThat(addExpression.getRightOperand(), instanceOf(NumberLiteralExpression.class)); + assertEquals(3.0, left.getValue()); + assertThat(addExpression.getRightOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression right = (NumberLiteralExpression) addExpression.getRightOperand(); - assertThat(right.getValue(), equalTo(4.0)); + assertEquals(4.0, right.getValue()); } @Test public void minusExpression() throws IOException, Parser.Exception { NumberExpression sut = parseWithRoundTripNumberExpression("(12.5 - 4.1)"); - assertThat(sut, instanceOf(SubExpression.class)); + assertThat(sut).isInstanceOf(SubExpression.class); SubExpression subExpression = (SubExpression) sut; - assertThat(subExpression.getLeftOperand(), instanceOf(NumberLiteralExpression.class)); + assertThat(subExpression.getLeftOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression left = (NumberLiteralExpression) subExpression.getLeftOperand(); - assertThat(left.getValue(), equalTo(12.5)); - assertThat(subExpression.getRightOperand(), instanceOf(NumberLiteralExpression.class)); + assertEquals(12.5, left.getValue()); + assertThat(subExpression.getRightOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression right = (NumberLiteralExpression) subExpression.getRightOperand(); - assertThat(right.getValue(), equalTo(4.1)); + assertEquals(4.1, right.getValue()); } @Test public void multExpression() throws IOException, Parser.Exception { NumberExpression sut = parseWithRoundTripNumberExpression("(0 * 0)"); - assertThat(sut, instanceOf(MultExpression.class)); + assertThat(sut).isInstanceOf(MultExpression.class); MultExpression multExpression = (MultExpression) sut; - assertThat(multExpression.getLeftOperand(), instanceOf(NumberLiteralExpression.class)); + assertThat(multExpression.getLeftOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression left = (NumberLiteralExpression) multExpression.getLeftOperand(); - assertThat(left.getValue(), equalTo(0.0)); - assertThat(multExpression.getRightOperand(), instanceOf(NumberLiteralExpression.class)); + assertEquals(0.0, left.getValue()); + assertThat(multExpression.getRightOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression right = (NumberLiteralExpression) multExpression.getRightOperand(); - assertThat(right.getValue(), equalTo(0.0)); + assertEquals(0.0, right.getValue()); } @Test public void divExpression() throws IOException, Parser.Exception { NumberExpression sut = parseWithRoundTripNumberExpression("(1.1 / 0.0)"); - assertThat(sut, instanceOf(DivExpression.class)); + assertThat(sut).isInstanceOf(DivExpression.class); DivExpression divExpression = (DivExpression) sut; - assertThat(divExpression.getLeftOperand(), instanceOf(NumberLiteralExpression.class)); + assertThat(divExpression.getLeftOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression left = (NumberLiteralExpression) divExpression.getLeftOperand(); - assertThat(left.getValue(), equalTo(1.1)); - assertThat(divExpression.getRightOperand(), instanceOf(NumberLiteralExpression.class)); + assertEquals(1.1, left.getValue()); + assertThat(divExpression.getRightOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression right = (NumberLiteralExpression) divExpression.getRightOperand(); - assertThat(right.getValue(), equalTo(0.0)); + assertEquals(0.0, right.getValue()); } @Test public void powerExpression() throws IOException, Parser.Exception { NumberExpression sut = parseWithRoundTripNumberExpression("(3 ^ 0.5)"); - assertThat(sut, instanceOf(PowerExpression.class)); + assertThat(sut).isInstanceOf(PowerExpression.class); PowerExpression powExpression = (PowerExpression) sut; - assertThat(powExpression.getLeftOperand(), instanceOf(NumberLiteralExpression.class)); + assertThat(powExpression.getLeftOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression left = (NumberLiteralExpression) powExpression.getLeftOperand(); - assertThat(left.getValue(), equalTo(3.0)); - assertThat(powExpression.getRightOperand(), instanceOf(NumberLiteralExpression.class)); + assertEquals(3.0, left.getValue()); + assertThat(powExpression.getRightOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression right = (NumberLiteralExpression) powExpression.getRightOperand(); - assertThat(right.getValue(), equalTo(0.5)); + assertEquals(0.5, right.getValue()); } @Test public void parenthesizedExpression() throws IOException, Parser.Exception { NumberExpression sut = parseWithRoundTripNumberExpression("(3)"); - assertThat(sut, instanceOf(ParenthesizedNumberExpression.class)); + assertThat(sut).isInstanceOf(ParenthesizedNumberExpression.class); ParenthesizedNumberExpression parenExpression = (ParenthesizedNumberExpression) sut; - assertThat(parenExpression.getOperand(), instanceOf(NumberLiteralExpression.class)); + assertThat(parenExpression.getOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression left = (NumberLiteralExpression) parenExpression.getOperand(); - assertThat(left.getValue(), equalTo(3.0)); + assertEquals(3.0, left.getValue()); } @Test public void complexExpression() throws IOException, Parser.Exception { NumberExpression sut = parseWithRoundTripNumberExpression("((3 + 4) * (1 / (12 - 8)))"); - assertThat(sut, instanceOf(MultExpression.class)); + assertThat(sut).isInstanceOf(MultExpression.class); MultExpression multExpression = (MultExpression) sut; // 3+4 - assertThat(multExpression.getLeftOperand(), instanceOf(AddExpression.class)); + assertThat(multExpression.getLeftOperand()).isInstanceOf(AddExpression.class); AddExpression addExpression = (AddExpression) multExpression.getLeftOperand(); - assertThat(addExpression.getLeftOperand(), instanceOf(NumberLiteralExpression.class)); + assertThat(addExpression.getLeftOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression left = (NumberLiteralExpression) addExpression.getLeftOperand(); - assertThat(left.getValue(), equalTo(3.0)); - assertThat(addExpression.getRightOperand(), instanceOf(NumberLiteralExpression.class)); + assertEquals(3.0, left.getValue()); + assertThat(addExpression.getRightOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression right = (NumberLiteralExpression) addExpression.getRightOperand(); - assertThat(right.getValue(), equalTo(4.0)); + assertEquals(4.0, right.getValue()); // 1/(12-8) - assertThat(multExpression.getRightOperand(), instanceOf(DivExpression.class)); + assertThat(multExpression.getRightOperand()).isInstanceOf(DivExpression.class); DivExpression divExpression = (DivExpression) multExpression.getRightOperand(); - assertThat(divExpression.getLeftOperand(), instanceOf(NumberLiteralExpression.class)); + assertThat(divExpression.getLeftOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression leftOfDiv = (NumberLiteralExpression) divExpression.getLeftOperand(); - assertThat(leftOfDiv.getValue(), equalTo(1.0)); + assertEquals(1.0, leftOfDiv.getValue()); // 12-8 - assertThat(divExpression.getRightOperand(), instanceOf(SubExpression.class)); + assertThat(divExpression.getRightOperand()).isInstanceOf(SubExpression.class); SubExpression rightofDiv = (SubExpression) divExpression.getRightOperand(); - assertThat(rightofDiv.getLeftOperand(), instanceOf(NumberLiteralExpression.class)); + assertThat(rightofDiv.getLeftOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression leftOfSSub = (NumberLiteralExpression) rightofDiv.getLeftOperand(); - assertThat(leftOfSSub.getValue(), equalTo(12.0)); - assertThat(rightofDiv.getRightOperand(), instanceOf(NumberLiteralExpression.class)); + assertEquals(12.0, leftOfSSub.getValue()); + assertThat(rightofDiv.getRightOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression rightOfSub = (NumberLiteralExpression) rightofDiv.getRightOperand(); - assertThat(rightOfSub.getValue(), equalTo(8.0)); + assertEquals(8.0, rightOfSub.getValue()); } @Test public void expressionWithItem() throws IOException, Parser.Exception { Item referenceItem = ParserUtils.parseItem("Number Item: id=\"myItem\";"); NumberExpression sut = parseWithRoundTripNumberExpression("(myItem * 3)", referenceItem.getRoot()); - assertThat(sut, instanceOf(MultExpression.class)); + assertThat(sut).isInstanceOf(MultExpression.class); MultExpression multExpression = (MultExpression) sut; - assertThat(multExpression.getLeftOperand(), instanceOf(Designator.class)); + assertThat(multExpression.getLeftOperand()).isInstanceOf(Designator.class); Designator left = (Designator) multExpression.getLeftOperand(); - assertThat(left.getItem(), equalTo(referenceItem)); - assertThat(multExpression.getRightOperand(), instanceOf(NumberLiteralExpression.class)); + assertEquals(referenceItem, left.getItem()); + assertThat(multExpression.getRightOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression right = (NumberLiteralExpression) multExpression.getRightOperand(); - assertThat(right.getValue(), equalTo(3.0)); + assertEquals(3.0, right.getValue()); } @Test @@ -156,23 +154,23 @@ public class ExpressionParserTest { } private void comparingExpression(String actualComparatorString, ComparatorType expectedComparatorType, double left, double right) throws IOException, Parser.Exception { - String expression = String.format("(%s %s %s)", Double.toString(left), actualComparatorString, Double.toString(right)); + String expression = String.format("(%s %s %s)", left, actualComparatorString, right); LogicalExpression sut = parseWithRoundTripLogicalExpression(expression); - assertThat(sut, instanceOf(ComparingExpression.class)); + assertThat(sut).isInstanceOf(ComparingExpression.class); ComparingExpression comparingExpression = (ComparingExpression) sut; - assertThat(comparingExpression.getLeftOperand(), instanceOf(NumberLiteralExpression.class)); + assertThat(comparingExpression.getLeftOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression leftExpression = (NumberLiteralExpression) comparingExpression.getLeftOperand(); - assertThat(leftExpression.getValue(), equalTo(left)); - assertThat(comparingExpression.getRightOperand(), instanceOf(NumberLiteralExpression.class)); + assertEquals(left, leftExpression.getValue()); + assertThat(comparingExpression.getRightOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression rightExpression = (NumberLiteralExpression) comparingExpression.getRightOperand(); - assertThat(rightExpression.getValue(), equalTo(right)); - assertThat(comparingExpression.getComparator(), equalTo(expectedComparatorType)); + assertEquals(right, rightExpression.getValue()); + assertEquals(expectedComparatorType, comparingExpression.getComparator()); } @Test public void notExpression() throws IOException, Parser.Exception { LogicalExpression sut = parseWithRoundTripLogicalExpression("!" + TRUE_EXPRESSION); - assertThat(sut, instanceOf(NotExpression.class)); + assertThat(sut).isInstanceOf(NotExpression.class); NotExpression notExpression = (NotExpression) sut; checkZeroEqualsZero(notExpression.getOperand()); } @@ -180,7 +178,7 @@ public class ExpressionParserTest { @Test public void andExpression() throws IOException, Parser.Exception { LogicalExpression sut = parseWithRoundTripLogicalExpression("(" + TRUE_EXPRESSION + " & " + TRUE_EXPRESSION + ")"); - assertThat(sut, instanceOf(AndExpression.class)); + assertThat(sut).isInstanceOf(AndExpression.class); AndExpression notExpression = (AndExpression) sut; checkZeroEqualsZero(notExpression.getLeftOperand()); checkZeroEqualsZero(notExpression.getRightOperand()); @@ -189,7 +187,7 @@ public class ExpressionParserTest { @Test public void orExpression() throws IOException, Parser.Exception { LogicalExpression sut = parseWithRoundTripLogicalExpression("(" + TRUE_EXPRESSION + " | " + TRUE_EXPRESSION + ")"); - assertThat(sut, instanceOf(OrExpression.class)); + assertThat(sut).isInstanceOf(OrExpression.class); OrExpression notExpression = (OrExpression) sut; checkZeroEqualsZero(notExpression.getLeftOperand()); checkZeroEqualsZero(notExpression.getRightOperand()); @@ -198,7 +196,7 @@ public class ExpressionParserTest { @Test public void parenthesizedLogicalExpression() throws IOException, Parser.Exception { LogicalExpression sut = parseWithRoundTripLogicalExpression("(" + TRUE_EXPRESSION + ")"); - assertThat(sut, instanceOf(ParenthesizedLogicalExpression.class)); + assertThat(sut).isInstanceOf(ParenthesizedLogicalExpression.class); ParenthesizedLogicalExpression parenthesizedLogicalExpression = (ParenthesizedLogicalExpression) sut; checkZeroEqualsZero(parenthesizedLogicalExpression.getOperand()); } @@ -212,7 +210,7 @@ public class ExpressionParserTest { String first = sut.prettyPrint(); NumberExpression reParsed = ParserUtils.parseNumberExpression(first, root); String second = reParsed.prettyPrint(); - assertThat(first, equalTo(second)); + assertEquals(second, first); return sut; } @@ -221,18 +219,18 @@ public class ExpressionParserTest { String first = sut.prettyPrint(); LogicalExpression reParsed = ParserUtils.parseLogicalExpression(first); String second = reParsed.prettyPrint(); - assertThat(first, equalTo(second)); + assertEquals(second, first); return sut; } private void checkZeroEqualsZero(LogicalExpression logicalExpression) { - assertThat(logicalExpression, instanceOf(ComparingExpression.class)); + assertThat(logicalExpression).isInstanceOf(ComparingExpression.class); ComparingExpression comparingExpression = (ComparingExpression) logicalExpression; - assertThat(comparingExpression.getLeftOperand(), instanceOf(NumberLiteralExpression.class)); + assertThat(comparingExpression.getLeftOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression leftOfSSub = (NumberLiteralExpression) comparingExpression.getLeftOperand(); - assertThat(leftOfSSub.getValue(), equalTo(0.0)); - assertThat(comparingExpression.getRightOperand(), instanceOf(NumberLiteralExpression.class)); + assertEquals(0.0, leftOfSSub.getValue()); + assertThat(comparingExpression.getRightOperand()).isInstanceOf(NumberLiteralExpression.class); NumberLiteralExpression rightOfSub = (NumberLiteralExpression) comparingExpression.getRightOperand(); - assertThat(rightOfSub.getValue(), equalTo(0.0)); + assertEquals(0.0, rightOfSub.getValue()); } } diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/GenerateCoverageTest.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/GenerateCoverageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..97d76d18d88847ac76c13a62e53cb24fac1f0c50 --- /dev/null +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/GenerateCoverageTest.java @@ -0,0 +1,12 @@ +package de.tudresden.inf.st.eraser; + +import de.tudresden.inf.st.eraser.jastadd.model.ASTNode; +import org.junit.jupiter.api.Test; + +public class GenerateCoverageTest { + + @Test + public void testGeneratedCoverage() { + ASTNode.runCoverageAll(); + } +} diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/InfluxTest.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/InfluxTest.java index 9526a4db04802684261f820e807db35d6a47c24f..a90b4b61ae80dec1e640b4e42170ea877a265301 100644 --- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/InfluxTest.java +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/InfluxTest.java @@ -3,10 +3,10 @@ package de.tudresden.inf.st.eraser; import de.tudresden.inf.st.eraser.jastadd.model.*; import de.tudresden.inf.st.eraser.util.TestUtils; import de.tudresden.inf.st.eraser.util.TestUtils.ModelAndItem; -import org.junit.*; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.testcontainers.containers.InfluxDBContainer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import java.time.Instant; import java.util.ArrayList; @@ -14,75 +14,87 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + /** * Testing item history aspect, and connection to influx db. * * @author rschoene - Initial contribution */ -@RunWith(Parameterized.class) +@Tag("influx") public class InfluxTest { + public static String getInfluxHost() { + if (System.getenv("GITLAB_CI") != null) { + // we are in the CI, so use "influx" as host + return "influx"; + } { + // else assume a locally running influx container + return "localhost"; + } + } + private static final double DELTA = 0.001; - private List<DoubleStatePoint> points = new ArrayList<>(); + private final List<DoubleStatePoint> points = new ArrayList<>(); private static final double firstState = 2.0; private static final double secondState = 4.0; private static final double thirdState = 3.0; private ModelAndItem mai; - @ClassRule - public static InfluxDBContainer influxDbContainer = new InfluxDBContainer() - .withDatabase(InfluxRoot.createDefault().getDbName()) - .withAdmin(InfluxRoot.createDefault().getUser()) - .withAdminPassword(InfluxRoot.createDefault().getPassword()); - - @Test - public void oneItem() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void oneItem(boolean useStub) { + setNewModel(useStub); NumberItem item = mai.item; // set state once item.setState(firstState); - assertSameOrdered(query(item), + assertSameOrdered(query(useStub, item), DoubleStatePoint.of(Instant.ofEpochSecond(1), firstState, item.getID())); // set state again item.setState(secondState); - assertSameOrdered(query(item), + assertSameOrdered(query(useStub, item), DoubleStatePoint.of(Instant.ofEpochSecond(1), firstState, item.getID()), DoubleStatePoint.of(Instant.ofEpochSecond(2), secondState, item.getID())); // set state a third time item.setState(thirdState); - assertSameOrdered(query(item), + assertSameOrdered(query(useStub, item), DoubleStatePoint.of(Instant.ofEpochSecond(1), firstState, item.getID()), DoubleStatePoint.of(Instant.ofEpochSecond(2), secondState, item.getID()), DoubleStatePoint.of(Instant.ofEpochSecond(3), thirdState, item.getID())); } - @Test - public void twoItems() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void twoItems(boolean useStub) { + setNewModel(useStub); NumberItem item1 = mai.item; NumberItem item2 = TestUtils.addItemTo(mai.model, 1.0, true); // set state once for first item item1.setState(firstState); - assertSameOrdered(query(item1, item2), + assertSameOrdered(query(useStub, item1, item2), DoubleStatePoint.of(Instant.ofEpochSecond(1), firstState, item1.getID())); // set state again for first item item1.setState(secondState); - assertSameOrdered(query(item1, item2), + assertSameOrdered(query(useStub, item1, item2), DoubleStatePoint.of(Instant.ofEpochSecond(1), firstState, item1.getID()), DoubleStatePoint.of(Instant.ofEpochSecond(2), secondState, item1.getID())); // set state for second item item2.setState(firstState); - assertSameOrdered(query(item1, item2), + assertSameOrdered(query(useStub, item1, item2), DoubleStatePoint.of(Instant.ofEpochSecond(1), firstState, item1.getID()), DoubleStatePoint.of(Instant.ofEpochSecond(2), secondState, item1.getID()), DoubleStatePoint.of(Instant.ofEpochSecond(3), firstState, item2.getID())); // set third state for first item item1.setState(thirdState); - assertSameOrdered(query(item1, item2), + assertSameOrdered(query(useStub, item1, item2), DoubleStatePoint.of(Instant.ofEpochSecond(1), firstState, item1.getID()), DoubleStatePoint.of(Instant.ofEpochSecond(2), secondState, item1.getID()), DoubleStatePoint.of(Instant.ofEpochSecond(3), firstState, item2.getID()), @@ -91,7 +103,7 @@ public class InfluxTest { // set second and third state for second item item2.setState(secondState); item2.setState(thirdState); - assertSameOrdered(query(item1, item2), + assertSameOrdered(query(useStub, item1, item2), DoubleStatePoint.of(Instant.ofEpochSecond(1), firstState, item1.getID()), DoubleStatePoint.of(Instant.ofEpochSecond(2), secondState, item1.getID()), DoubleStatePoint.of(Instant.ofEpochSecond(3), firstState, item2.getID()), @@ -100,10 +112,12 @@ public class InfluxTest { DoubleStatePoint.of(Instant.ofEpochSecond(6), thirdState, item2.getID())); } - @Test - public void justAdapter() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void justAdapter(boolean useStub) { + setNewModel(useStub); InfluxAdapter influxAdapter = getInfluxRoot().influxAdapter(); - Assert.assertTrue("Adapter not connected", influxAdapter.isConnected()); + assertTrue(influxAdapter.isConnected(), "Adapter not connected"); influxAdapter.deleteDatabase(); // write one point @@ -119,16 +133,16 @@ public class InfluxTest { } else { result = influxAdapter.query(DoubleStatePoint.NAME, id, DoubleStatePoint.class); } - Assert.assertEquals("Did not get one result item", 1, result.size()); - Assert.assertEquals("Wrong class", DoubleStatePoint.class, result.get(0).getClass()); + assertEquals(1, result.size(), "Did not get one result item"); + assertEquals(DoubleStatePoint.class, result.get(0).getClass(), "Wrong class"); DoubleStatePoint point = (DoubleStatePoint) result.get(0); - Assert.assertEquals("Time differs", now, point.getTime()); - Assert.assertEquals("ID differs", id, point.getId()); - Assert.assertEquals("State differs", state, point.getState(), DELTA); + assertEquals(now, point.getTime(), "Time differs"); + assertEquals(id, point.getId(), "ID differs"); + assertEquals(state, point.getState(), DELTA, "State differs"); } private void assertSameOrdered(List<DoubleStatePoint> actual, DoubleStatePoint... expected) { - Assert.assertEquals("Number of items differs!", expected.length, actual.size()); + assertEquals(expected.length, actual.size(), "Number of items differs!"); // sort actual and expected points by time List<DoubleStatePoint> actualOrdered = new ArrayList<>(actual); actualOrdered.sort(Comparator.comparing(AbstractItemPoint::getTime)); @@ -136,19 +150,18 @@ public class InfluxTest { expectedOrdered.sort(Comparator.comparing(AbstractItemPoint::getTime)); // and then compare them for (int i = 0; i < actualOrdered.size(); i++) { - Assert.assertEquals("State of item " + i + " differs!", - expectedOrdered.get(i).getState(), actualOrdered.get(i).getState(), DELTA); - Assert.assertEquals("Id of item " + i + " differs!", - expectedOrdered.get(i).getId(), actualOrdered.get(i).getId()); + assertEquals(expectedOrdered.get(i).getState(), actualOrdered.get(i).getState(), DELTA, + "State of item " + i + " differs!"); + assertEquals(expectedOrdered.get(i).getId(), actualOrdered.get(i).getId(), + "Id of item " + i + " differs!"); } } - private ModelAndItem createModel() { + private ModelAndItem createModel(boolean useStub) { ModelAndItem mai = TestUtils.createModelAndItem(1.0, true); InfluxRoot influxRoot; if (useStub) { influxRoot = InfluxRoot.createDefault(); - influxRoot.setHostOpt(new Opt<>()); // now a SenderStub is being used ((InfluxAdapterStub) influxRoot.influxAdapter()).setCallback( point -> points.add((DoubleStatePoint) point)); @@ -156,11 +169,10 @@ public class InfluxTest { influxRoot = InfluxRoot.createDefault(); // use container running influx influxRoot.setDbName(InfluxTest.class.getSimpleName()); - System.out.println("ports: " + influxDbContainer.getPortBindings() + " url: '" + influxDbContainer.getUrl() + "'"); - influxRoot.setHostByName(influxDbContainer.getUrl().replaceAll("^http://", "")); + influxRoot.getHost().setHostName(getInfluxHost()); } mai.model.getRoot().setInfluxRoot(influxRoot); - Assume.assumeTrue(influxRoot.influxAdapter().isConnected()); + assumeTrue(influxRoot.influxAdapter().isConnected()); influxRoot.influxAdapter().deleteDatabase(); return mai; } @@ -169,7 +181,7 @@ public class InfluxTest { return mai.model.getRoot().getInfluxRoot(); } - private List<DoubleStatePoint> query(ItemWithDoubleState... allItems) { + private List<DoubleStatePoint> query(boolean useStub, ItemWithDoubleState... allItems) { if (useStub) { return points; } else { @@ -181,30 +193,16 @@ public class InfluxTest { } } - @Before - public void setNewModel() { - mai = createModel(); + private void setNewModel(boolean useStub) { + // BeforeEach does not work with parameterized tests :( + mai = createModel(useStub); getInfluxRoot().influxAdapter().disableAsyncQuery(); } - @After + @AfterEach public void closeInfluxAdapter() throws Exception { if (mai != null && mai.model != null) { getInfluxRoot().influxAdapter().close(); } } - - @Parameterized.Parameter - public String name; - - @Parameterized.Parameter(1) - public boolean useStub; - - @Parameterized.Parameters(name = "{0}") - public static Iterable<Object[]> getTests() { - return Arrays.asList(new Object[][] { - {"Impl", false}, - {"Stub", true} - }); - } } diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/ItemTests.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/ItemTests.java index ffda213bea9a359097db8b962f03a48e52502c57..b7074f22763d928b56a8dc180aa08d5e3464d036 100644 --- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/ItemTests.java +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/ItemTests.java @@ -3,11 +3,12 @@ package de.tudresden.inf.st.eraser; import de.tudresden.inf.st.eraser.jastadd.model.*; import de.tudresden.inf.st.eraser.util.TestUtils; import de.tudresden.inf.st.eraser.util.TestUtils.ModelAndItem; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Instant; -import java.util.Date; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Testing wrapper methods of {@link Item}. @@ -21,12 +22,12 @@ public class ItemTests { ItemWithBooleanState sut = createItem(SwitchItem::new); sut.setState(true); - Assert.assertTrue("State 'true' should match 'true'", sut.stateEquals(true)); - Assert.assertFalse("State 'true' should not match 'false'", sut.stateEquals(false)); + assertTrue(sut.stateEquals(true), "State 'true' should match 'true'"); + assertFalse(sut.stateEquals(false), "State 'true' should not match 'false'"); sut.setState(false); - Assert.assertFalse("State 'false' should not match 'true'", sut.stateEquals(true)); - Assert.assertTrue("State 'false' should match 'false'", sut.stateEquals(false)); + assertFalse(sut.stateEquals(true), "State 'false' should not match 'true'"); + assertTrue(sut.stateEquals(false), "State 'false' should match 'false'"); } @Test @@ -34,10 +35,10 @@ public class ItemTests { ItemWithStringState sut = createItem(ImageItem::new); sut.setState("correct"); - Assert.assertTrue("State 'correct' should match 'correct'", - sut.stateEquals("correct")); - Assert.assertFalse("State 'correct' should not match 'something else'", - sut.stateEquals("something else")); + assertTrue(sut.stateEquals("correct"), + "State 'correct' should match 'correct'"); + assertFalse(sut.stateEquals("something else"), + "State 'correct' should not match 'something else'"); } @Test @@ -45,12 +46,12 @@ public class ItemTests { ItemWithDoubleState sut = createItem(NumberItem::new); sut.setState(3.0); - Assert.assertTrue("State '3.0' should match '3.0'", sut.stateEquals(3.0)); - Assert.assertFalse("State '3.0' should not match '7.0'", sut.stateEquals(7.0)); + assertTrue(sut.stateEquals(3.0), "State '3.0' should match '3.0'"); + assertFalse(sut.stateEquals(7.0), "State '3.0' should not match '7.0'"); sut.setState(4.0); - Assert.assertFalse("State '4.0' should not match '3.0'", sut.stateEquals(3.0)); - Assert.assertFalse("State '4.0' should not match '7.0'", sut.stateEquals(7.0)); + assertFalse(sut.stateEquals(3.0), "State '4.0' should not match '3.0'"); + assertFalse(sut.stateEquals(7.0), "State '4.0' should not match '7.0'"); } @Test @@ -58,18 +59,32 @@ public class ItemTests { ColorItem sut = createItem(ColorItem::new); sut.setState(TupleHSB.of(1, 2, 3)); - Assert.assertTrue("State 'TupleHSB(1,2,3)' should match 'TupleHSB(1,2,3)'", - sut.stateEquals(TupleHSB.of(1, 2, 3))); - Assert.assertFalse("State 'TupleHSB(1,2,3)' should not match 'TupleHSB(1,2,4)'", - sut.stateEquals(TupleHSB.of(1, 2, 4))); - Assert.assertFalse("State 'TupleHSB(1,2,3)' should not match 'TupleHSB(9,2,3)'", - sut.stateEquals(TupleHSB.of(9, 2, 3))); - Assert.assertFalse("State 'TupleHSB(1,2,3)' should not match 'TupleHSB(1,17,3)'", - sut.stateEquals(TupleHSB.of(1, 17, 3))); - Assert.assertFalse("State 'TupleHSB(1,2,3)' should not match 'TupleHSB(99,99,3)'", - sut.stateEquals(TupleHSB.of(99, 99, 3))); - Assert.assertFalse("State 'TupleHSB(1,2,3)' should not match 'TupleHSB(5,5,5)'", - sut.stateEquals(TupleHSB.of(5, 5, 5))); + assertTrue(sut.stateEquals(TupleHSB.of(1, 2, 3)), + "State 'TupleHSB(1,2,3)' should match 'TupleHSB(1,2,3)'"); + assertFalse(sut.stateEquals(TupleHSB.of(1, 2, 4)), + "State 'TupleHSB(1,2,3)' should not match 'TupleHSB(1,2,4)'"); + assertFalse(sut.stateEquals(TupleHSB.of(9, 2, 3)), + "State 'TupleHSB(1,2,3)' should not match 'TupleHSB(9,2,3)'"); + assertFalse(sut.stateEquals(TupleHSB.of(1, 17, 3)), + "State 'TupleHSB(1,2,3)' should not match 'TupleHSB(1,17,3)'"); + assertFalse(sut.stateEquals(TupleHSB.of(99, 99, 3)), + "State 'TupleHSB(1,2,3)' should not match 'TupleHSB(99,99,3)'"); + assertFalse(sut.stateEquals(TupleHSB.of(5, 5, 5)), + "State 'TupleHSB(1,2,3)' should not match 'TupleHSB(5,5,5)'"); + + sut.setState(TupleHSB.of(347, 80, 95)); + assertTrue(sut.stateEquals(TupleHSB.of(347, 80, 95)), + "State 'TupleHSB(357,80,95)' should match 'TupleHSB(357,80,95)'"); + assertFalse(sut.stateEquals(TupleHSB.of(1, 2, 4)), + "State 'TupleHSB(357,80,95)' should not match 'TupleHSB(1,2,4)'"); + assertFalse(sut.stateEquals(TupleHSB.of(9, 2, 3)), + "State 'TupleHSB(357,80,95)' should not match 'TupleHSB(9,2,3)'"); + assertFalse(sut.stateEquals(TupleHSB.of(1, 17, 3)), + "State 'TupleHSB(357,80,95)' should not match 'TupleHSB(1,17,3)'"); + assertFalse(sut.stateEquals(TupleHSB.of(99, 99, 3)), + "State 'TupleHSB(357,80,95)' should not match 'TupleHSB(99,99,3)'"); + assertFalse(sut.stateEquals(TupleHSB.of(5, 5, 5)), + "State 'TupleHSB(357,80,95)' should not match 'TupleHSB(5,5,5)'"); } @Test @@ -77,10 +92,10 @@ public class ItemTests { DateTimeItem sut = createItem(DateTimeItem::new); sut.setState(Instant.ofEpochMilli(1543415826)); - Assert.assertTrue("State 'Date(1543415826)' should match 'Date(1543415826)'", - sut.stateEquals(Instant.ofEpochMilli(1543415826))); - Assert.assertFalse("State 'Date(1543415826)' should not match 'Date(4)'", - sut.stateEquals(Instant.ofEpochMilli(4))); + assertTrue(sut.stateEquals(Instant.ofEpochMilli(1543415826)), + "State 'Date(1543415826)' should match 'Date(1543415826)'"); + assertFalse(sut.stateEquals(Instant.ofEpochMilli(4)), + "State 'Date(1543415826)' should not match 'Date(4)'"); } @FunctionalInterface diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/MarshallingTests.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/MarshallingTests.java index abcbd203be180e81b766bc48aa66cdb7778efae7..2184860192791bc358fdc2987679098366b930db 100644 --- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/MarshallingTests.java +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/MarshallingTests.java @@ -1,58 +1,42 @@ package de.tudresden.inf.st.eraser; -import de.tudresden.inf.st.eraser.jastadd_test.core.*; -import de.tudresden.inf.st.eraser.parser.EraserParserHelper; +import de.tudresden.inf.st.eraser.jastadd.parser.EraserParser; +import de.tudresden.inf.st.eraser.jastadd_test.core.TestConfiguration; +import de.tudresden.inf.st.eraser.jastadd_test.core.TestProperties; +import de.tudresden.inf.st.eraser.jastadd_test.core.TestRunner; +import de.tudresden.inf.st.eraser.jastadd_test.core.Util; import de.tudresden.inf.st.eraser.util.ParserUtils; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; /** * Testing round-trip parsing-printing-comparing. * * @author rschoene - Initial contribution */ -//@RunWith(ParallelParameterized.class) -@RunWith(Parameterized.class) public class MarshallingTests { - private static final TestProperties properties = new TestProperties(); - static { - properties.put("jastadd3", "false"); - properties.put("options", "indent=tab"); - properties.setTestRoot("src/test/resources/tests"); -// properties.exclude(Tests.FAILING); -// properties.exclude(Tests.UNSTABLE); - } - - private final TestConfiguration unitTest; - - @BeforeClass - public static void setupParser() { - EraserParserHelper.setCheckUnusedElements(false); - ParserUtils.setVerboseLoading(false); - } - - /** - * Construct a new JastAdd test - * @param unitTest The test to run. - */ - public MarshallingTests(TestConfiguration unitTest) { - this.unitTest = unitTest; - } - /** * Run the JastAdd test + * @param unitTest The test to run. */ - @Test - public void runTest() throws Exception { - TestRunner.runTest(unitTest, properties); + @ParameterizedTest + @MethodSource("getTests") + public void runTest(TestConfiguration unitTest) throws Exception { + EraserParser.setCheckUnusedElements(false); + ParserUtils.setVerboseLoading(false); + TestRunner.runTest(unitTest); } - @SuppressWarnings("javadoc") - @Parameterized.Parameters(name = "{0}") - public static Iterable<Object[]> getTests() { + public static List<TestConfiguration> getTests() { + TestProperties properties = new TestProperties(); + properties.put("jastadd3", "false"); + properties.put("options", "indent=tab"); + properties.setTestRoot("src/test/resources/tests"); +// properties.exclude(Tests.FAILING); +// properties.exclude(Tests.UNSTABLE); return Util.getTests(properties); } diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/MqttTests.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/MqttTests.java index 24a4f0b29b03fa7d20144b409ad989703b9b16cc..7bfe873c38cfa72d907ab98d0eb28f2ba4edc195 100644 --- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/MqttTests.java +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/MqttTests.java @@ -5,14 +5,9 @@ import de.tudresden.inf.st.eraser.util.MqttReceiver; import de.tudresden.inf.st.eraser.util.TestUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.junit.Assert; -import org.junit.Assume; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.strategy.Wait; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import java.io.IOException; import java.util.ArrayList; @@ -21,16 +16,29 @@ import java.util.List; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; -import static org.hamcrest.core.IsCollectionContaining.hasItem; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; /** * Test for everything related to MQTT. * * @author rschoene - Initial contribution */ -@RunWith(Parameterized.class) +@Tag("mqtt") public class MqttTests { + public static String getMqttHost() { + if (System.getenv("GITLAB_CI") != null) { + // we are in the CI, so use "mqtt" as host + return "mqtt"; + } { + // else assume a locally running mqtt broker + return "localhost"; + } + } + private static final String outgoingPrefix = "out"; private static final String firstPart = "a"; private static final String alternativeFirstPart = "x"; @@ -39,68 +47,68 @@ public class MqttTests { private static final double secondState = 2.0; private static final double thirdState = 3.0; - private List<String> messages = new ArrayList<>(); - private static Logger logger = LogManager.getLogger(MqttTests.class); + private final List<String> messages = new ArrayList<>(); + private static final Logger logger = LogManager.getLogger(MqttTests.class); - @ClassRule - public static GenericContainer mqttBroker = new GenericContainer<>("eclipse-mosquitto:1.5") - .withExposedPorts(1883); - - @Test - public void resolve1() { - ModelItemAndTwoTopics modelAB = createAB(); + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void resolve1(boolean useStub) { + ModelItemAndTwoTopics modelAB = createAB(useStub); MqttRoot sut = modelAB.model.getRoot().getMqttRoot(); // incoming mqtt topic might be "inc/a/" or "inc/a/b" - Assert.assertTrue(sut.resolveTopic("inc/a").isPresent()); - Assert.assertEquals("Could not resolve a.", modelAB.firstTopic, sut.resolveTopic("inc/a").get()); - Assert.assertTrue(sut.resolveTopic("inc/a/b").isPresent()); - Assert.assertEquals("Could not resolve a/b.", modelAB.secondTopic, sut.resolveTopic("inc/a/b").get()); + assertTrue(sut.resolveTopic("inc/a").isPresent()); + assertEquals(modelAB.firstTopic, sut.resolveTopic("inc/a").get(), "Could not resolve a."); + assertTrue(sut.resolveTopic("inc/a/b").isPresent()); + assertEquals(modelAB.secondTopic, sut.resolveTopic("inc/a/b").get(), "Could not resolve a/b."); } - @Test - public void brokerConnected() throws Exception { - ModelItemAndTwoTopics modelAB = createAB(); + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void brokerConnected(boolean useStub) throws Exception { + ModelItemAndTwoTopics modelAB = createAB(useStub); MqttRoot sut = modelAB.model.getRoot().getMqttRoot(); // MqttRoot mqttRoot = new MqttRoot(); // mqttRoot.setHostByName("localhost"); // MQTTSender sender = new MQTTSenderImpl().setHost(mqttRoot.getHost()); MQTTSender sender = sut.getMqttSender(); - Assume.assumeTrue("Broker is not connected", sender.isConnected()); -// Assert.assertTrue(sender.isConnected()); + assumeTrue(sender.isConnected(), "Broker is not connected"); +// assertTrue(sender.isConnected()); sender.publish("test", "me"); } - @Test - public void itemUpdateSend1() throws Exception { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void itemUpdateSend1(boolean useStub) throws Exception { String expectedTopic = outgoingPrefix + "/" + firstPart + "/" + secondPart; - ModelItemAndTwoTopics modelAB = createAB(); + ModelItemAndTwoTopics modelAB = createAB(useStub); assertSenderConnected(modelAB); NumberItem sut = modelAB.item; sut.setTopic(modelAB.secondTopic); - createMqttReceiver(expectedTopic); + createMqttReceiver(useStub, expectedTopic); sut.setState(firstState); assertTimeoutEquals(2, 1, messages::size); - Assert.assertEquals(Double.toString(firstState), messages.get(0)); + assertEquals(Double.toString(firstState), messages.get(0)); sut.setState(secondState); assertTimeoutEquals(2, 2, messages::size); - Assert.assertEquals(Double.toString(secondState), messages.get(1)); + assertEquals(Double.toString(secondState), messages.get(1)); } - @Test - public void itemUpdateSend2() throws Exception { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void itemUpdateSend2(boolean useStub) throws Exception { String expectedTopic1 = outgoingPrefix + "/" + firstPart + "/" + secondPart; String expectedTopic2 = outgoingPrefix + "/" + alternativeFirstPart + "/" + secondPart; - ModelItemAndTwoTopics modelAB = createAB(); + ModelItemAndTwoTopics modelAB = createAB(useStub); assertSenderConnected(modelAB); NumberItem item1 = modelAB.item; @@ -111,28 +119,27 @@ public class MqttTests { NumberItem item2 = TestUtils.addItemTo(modelAB.model, 0, true); item2.setTopic(alternativeB); - createMqttReceiver(expectedTopic1, expectedTopic2); + createMqttReceiver(useStub, expectedTopic1, expectedTopic2); item1.setState(firstState); item2.setState(firstState); assertTimeoutEquals(2, 2, messages::size); - Assert.assertEquals(Double.toString(firstState), messages.get(0)); - Assert.assertEquals(Double.toString(firstState), messages.get(1)); + assertEquals(Double.toString(firstState), messages.get(0)); + assertEquals(Double.toString(firstState), messages.get(1)); item1.setState(secondState); item2.setState(thirdState); assertTimeoutEquals(3, 4, messages::size); // TODO actually this does not test, whether the topic was correct for each state - Assert.assertThat(messages, hasItem(Double.toString(secondState))); - Assert.assertThat(messages, hasItem(Double.toString(thirdState))); + assertThat(messages).contains(Double.toString(secondState)); + assertThat(messages).contains(Double.toString(thirdState)); } private void assertSenderConnected(ModelItemAndTwoTopics modelAB) { MqttRoot mqttRoot = modelAB.model.getRoot().getMqttRoot(); - mqttBroker.waitingFor(Wait.forHealthcheck()); if (!mqttRoot.getMqttSender().isConnected()) { try { Thread.sleep(1000); @@ -140,40 +147,39 @@ public class MqttTests { logger.catching(e); } } - Assert.assertTrue("Broker is not connected", mqttRoot.getMqttSender().isConnected()); + assertTrue(mqttRoot.getMqttSender().isConnected(), "Broker is not connected"); } - private void createMqttReceiver(String... expectedTopics) throws IOException { + private void createMqttReceiver(boolean useStub, String... expectedTopics) throws IOException { if (useStub) { // do not need receiver, as messages are directly written by MqttSenderStub and callback + messages.clear(); return; } MqttReceiver receiver = new MqttReceiver(); List<String> expectedTopicList = Arrays.asList(expectedTopics); -// receiver.setHost("localhost", 1883); - receiver.setHost(mqttBroker.getContainerIpAddress(), mqttBroker.getFirstMappedPort()); + receiver.setHost(ExternalHost.of(getMqttHost(),1883)); receiver.setTopicsForSubscription(expectedTopics); receiver.setOnMessage((topic, message) -> { - Assert.assertThat(expectedTopicList, hasItem(topic)); + assertThat(expectedTopicList).contains(topic); messages.add(message); }); receiver.start(); receiver.waitUntilReady(2, TimeUnit.SECONDS); } - private ModelItemAndTwoTopics createAB() { + private ModelItemAndTwoTopics createAB(boolean useStub) { TestUtils.ModelAndItem mai = TestUtils.createModelAndItem(0, true); SmartHomeEntityModel model = mai.model; MqttRoot mqttRoot = new MqttRoot(); mqttRoot.setIncomingPrefix("inc"); mqttRoot.setOutgoingPrefix(outgoingPrefix); if (useStub) { - mqttRoot.setHostOpt(new Opt<>()); + // now a SenderStub is being used ((MQTTSenderStub) mqttRoot.getMqttSender()).setCallback(((topic, message, qos) -> messages.add(message))); } else { -// mqttRoot.setHostByName("localhost"); - mqttRoot.setHost(ExternalHost.of(mqttBroker.getContainerIpAddress(), mqttBroker.getFirstMappedPort())); + mqttRoot.getHost().setHostName(getMqttHost()).setPort(1883); } MqttTopic a = createAndAddMqttTopic(mqttRoot, firstPart); MqttTopic ab = createAndAddMqttTopic(mqttRoot, firstPart + "/" + secondPart); @@ -211,27 +217,15 @@ public class MqttTests { } long targetEndTime = System.nanoTime() + TimeUnit.SECONDS.toNanos(seconds); while (System.nanoTime() < targetEndTime) { + // this is indeed busy waiting in favour of new dependencies handling it + //noinspection BusyWait Thread.sleep(100); if (expected == actualProvider.get()) { break; } } // final assessment, throw exception if not matched. Or pass, if previously matched. - Assert.assertEquals(expected, actualProvider.get()); - } - - @Parameterized.Parameter - public String name; - - @Parameterized.Parameter(1) - public boolean useStub; - - @Parameterized.Parameters(name = "{0}") - public static Iterable<Object[]> getTests() { - return Arrays.asList(new Object[][] { - {"Impl", false}, - {"Stub", true} - }); + assertEquals(expected, actualProvider.get()); } } diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/NeuralNetworkTest.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/NeuralNetworkTest.java index 0eb593fd0fc6c3cb3401f315afe33d550e4b3658..185f80749afc7061bf37030b556edd02a957d23a 100644 --- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/NeuralNetworkTest.java +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/NeuralNetworkTest.java @@ -3,8 +3,9 @@ package de.tudresden.inf.st.eraser; import de.tudresden.inf.st.eraser.jastadd.model.*; import de.tudresden.inf.st.eraser.util.TestUtils; import de.tudresden.inf.st.eraser.util.TestUtils.ModelAndItem; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * Testing the neuronal network aspect of the knowledge base. @@ -50,22 +51,22 @@ public class NeuralNetworkTest { neuralNetworkRoot.setOutputLayer(outputLayer); // Current value is 1, so return value is 1 * 4 * 0.6 = 2.4, 1 * 4 * 0.4 = 1.6, both are less than 4. so 0. - Leaf leaf = neuralNetworkRoot.classify(); + Leaf leaf = neuralNetworkRoot.internalClassify(); assertLeafEqual(0, leaf); // Current value is 2, so return value is 2 * 4 * 0.6 = 4.8, 2 * 4 * 0.4 = 3.2, first is less than 4. so 1. mai.item.setState(2); - leaf = neuralNetworkRoot.classify(); + leaf = neuralNetworkRoot.internalClassify(); assertLeafEqual(1, leaf); // Current value is 5, so return value is 5 * 4 * 0.6 = 12, 5 * 4 * 0.4 = 8, both are greater than 4. so 3. mai.item.setState(5); - leaf = neuralNetworkRoot.classify(); + leaf = neuralNetworkRoot.internalClassify(); assertLeafEqual(3, leaf); } private void assertLeafEqual(int expected, Leaf actualLeaf) { - Assert.assertEquals(Double.toString(expected), actualLeaf.getLabel()); + assertEquals(Double.toString(expected), actualLeaf.getLabel()); } private Double combineBinaryValues(double[] outputs) { diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/OpenHabImporterTest.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/OpenHabImporterTest.java index ba45dfa3d2381c7a4778eea8314074401df25c93..6daa513b7d87fdae87d514c586da61b9ad8993e7 100644 --- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/OpenHabImporterTest.java +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/OpenHabImporterTest.java @@ -1,35 +1,31 @@ package de.tudresden.inf.st.eraser; import de.tudresden.inf.st.eraser.jastadd.model.SmartHomeEntityModel; -import de.tudresden.inf.st.eraser.jastadd.model.Root; import de.tudresden.inf.st.eraser.jastadd_test.core.*; import de.tudresden.inf.st.eraser.openhab2.OpenHab2Importer; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; -import java.io.File; -import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; +import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Properties; +import java.util.List; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Testing import from openHAB using {@link OpenHab2Importer}. * * @author rschoene - Initial contribution */ -@RunWith(ParallelParameterized.class) public class OpenHabImporterTest { private static final String HOST = "localhost"; static class OpenHab2ImporterFromFile extends OpenHab2Importer { - private String directory; + private final String directory; OpenHab2ImporterFromFile(String directory) { this.directory = directory; @@ -54,8 +50,13 @@ public class OpenHabImporterTest { } private URL makeFileUrl(String fileName, String emptyFileName) throws MalformedURLException { - Path path = Paths.get(directory, fileName); - if (!path.toFile().exists() || !path.toFile().isFile()) { + Path path = null; + try { + path = Paths.get(directory, fileName); + } catch (InvalidPathException e) { + // use empty file + } + if (path == null || !path.toFile().exists() || !path.toFile().isFile()) { path = Paths.get(directory, "..", emptyFileName); } return path.toUri().toURL(); @@ -63,7 +64,7 @@ public class OpenHabImporterTest { } static class OpenHabImportRunner extends TestRunner { - public static void runTest(TestConfiguration config, Properties jastaddProperties) { + public static void runTest(TestConfiguration config) { Result expected = config.getExpected(); @@ -93,35 +94,22 @@ public class OpenHabImporterTest { } } - private static final TestProperties properties = new TestProperties(); - static { - properties.put("jastadd3", "false"); - properties.setTestRoot("src/test/resources/openhabtest"); -// properties.exclude(Tests.FAILING); -// properties.exclude(Tests.UNSTABLE); - } - - private final TestConfiguration unitTest; - - /** - * Construct a new OpenHabImporterTest - * @param unitTest The test to run. - */ - public OpenHabImporterTest(TestConfiguration unitTest) { - this.unitTest = unitTest; - } - /** * Run the OpenHabImporterTest + * @param unitTest The test to run. */ - @Test - public void runTest() { - OpenHabImportRunner.runTest(unitTest, properties); + @ParameterizedTest + @MethodSource("getTests") + public void runTest(TestConfiguration unitTest) { + OpenHabImportRunner.runTest(unitTest); } - @SuppressWarnings("javadoc") - @Parameterized.Parameters(name = "{0}") - public static Iterable<Object[]> getTests() { + public static List<TestConfiguration> getTests() { + TestProperties properties = new TestProperties(); + properties.put("jastadd3", "false"); + properties.setTestRoot("src/test/resources/openhabtest"); +// properties.exclude(Tests.FAILING); +// properties.exclude(Tests.UNSTABLE); return Util.getTests(properties); } } diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/RulesTest.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/RulesTest.java index fc01bb76bb022cfa0a5ff25113d9fc2957c0c3c0..a8c1f727a599a1f529173428dee1d11fa5974156 100644 --- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/RulesTest.java +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/RulesTest.java @@ -2,15 +2,17 @@ package de.tudresden.inf.st.eraser; import beaver.Parser; import de.tudresden.inf.st.eraser.jastadd.model.*; +import de.tudresden.inf.st.eraser.jastadd.model.Action; import de.tudresden.inf.st.eraser.util.ParserUtils; import de.tudresden.inf.st.eraser.util.TestUtils; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Disabled; +import org.testcontainers.shaded.com.google.common.collect.ImmutableList; import java.io.IOException; -import java.util.Arrays; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ScheduledFuture; @@ -18,17 +20,20 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.StreamSupport; +import static de.tudresden.inf.st.eraser.util.TestUtils.getDefaultGroup; +import static org.assertj.core.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.*; + /** * Testing the simple rule engine. * * @author rschoene - Initial contribution */ -@RunWith(Parameterized.class) public class RulesTest { private static final double DELTA = 0.01d; - class CountingAction extends NoopAction { + static class CountingAction extends NoopAction { final Map<Item, AtomicInteger> counters = new HashMap<>(); CountingAction() { @@ -65,16 +70,16 @@ public class RulesTest { root.addRule(rule); rule.activateFor(item); - Assert.assertEquals(m("Counter not initialized correctly"), 0, counter.get(item)); + assertEquals(0, counter.get(item), "Counter not initialized correctly"); setState(item, 5); - Assert.assertEquals(m("Change of item state should trigger the rule"), 1, counter.get(item)); + assertEquals(1, counter.get(item), "Change of item state should trigger the rule"); setState(item, 5); - Assert.assertEquals(m("Set state to same value should not trigger the rule"), 1, counter.get(item)); + assertEquals(1, counter.get(item), "Set state to same value should not trigger the rule"); setState(item, 3); - Assert.assertEquals(m("Change of item state should trigger the rule"), 2, counter.get(item)); + assertEquals(2, counter.get(item), "Change of item state should trigger the rule"); } @Test @@ -89,12 +94,12 @@ public class RulesTest { rule.activateFor(item); rule.activateFor(item); - Assert.assertEquals(m("Rule was not activated exactly once (observer count)"), 1, rule.getObserverList().size()); + assertEquals(1, rule.getObserverList().size(), "Rule was not activated exactly once (observer count)"); int ruleCount = 0; for (ItemObserver observer : rule.getObserverList()) { ruleCount += observer.getTriggeredRuleList().size(); } - Assert.assertEquals(m("Rule was not activated exactly once (rule count)"), 1, ruleCount); + assertEquals(1, ruleCount, "Rule was not activated exactly once (rule count)"); } @Test @@ -116,20 +121,20 @@ public class RulesTest { ruleA.activateFor(item); ruleB.activateFor(item); - Assert.assertEquals(m("First counter not initialized correctly"), 0, counter1.get(item)); - Assert.assertEquals(m("Second counter not initialized correctly"), 0, counter2.get(item)); + assertEquals(0, counter1.get(item), "First counter not initialized correctly"); + assertEquals(0, counter2.get(item), "Second counter not initialized correctly"); setState(item, 5); - Assert.assertEquals(m("Change of item state should trigger the first rule"), 1, counter1.get(item)); - Assert.assertEquals(m("Change of item state should trigger the second rule"), 1, counter2.get(item)); + assertEquals(1, counter1.get(item), "Change of item state should trigger the first rule"); + assertEquals(1, counter2.get(item), "Change of item state should trigger the second rule"); setState(item, 5); - Assert.assertEquals(m("Set state to same value should not trigger the first rule"), 1, counter1.get(item)); - Assert.assertEquals(m("Set state to same value should not trigger the second rule"), 1, counter2.get(item)); + assertEquals(1, counter1.get(item), "Set state to same value should not trigger the first rule"); + assertEquals(1, counter2.get(item), "Set state to same value should not trigger the second rule"); setState(item, 3); - Assert.assertEquals(m("Change of item state should trigger the first rule"), 2, counter1.get(item)); - Assert.assertEquals(m("Change of item state should trigger the second rule"), 2, counter2.get(item)); + assertEquals(2, counter1.get(item), "Change of item state should trigger the first rule"); + assertEquals(2, counter2.get(item), "Change of item state should trigger the second rule"); } @Test @@ -137,7 +142,7 @@ public class RulesTest { TestUtils.ModelAndItem modelAndItem = createModelAndItem(4); Root root = modelAndItem.model.getRoot(); NumberItem item1 = modelAndItem.item; - NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, useUpdatingItem); + NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, true); Rule rule = new Rule(); CountingAction counter = new CountingAction(); @@ -148,47 +153,43 @@ public class RulesTest { rule.activateFor(item2); // Items: 4, 4. Expected counters: 0, 0. - Assert.assertEquals(m("Counter not initialized correctly for first item"), 0, counter.get(item1)); - Assert.assertEquals(m("Counter not initialized correctly for second item"), 0, counter.get(item2)); + assertEquals(0, counter.get(item1), "Counter not initialized correctly for first item"); + assertEquals(0, counter.get(item2), "Counter not initialized correctly for second item"); // Items: 5, 4. Expected counters: 1, 0. setState(item1, 5); - Assert.assertEquals(m("Change of first item state should trigger the rule once"), 1, counter.get(item1)); - Assert.assertEquals(m("Change of first item state should not trigger the rule for second item"), 0, counter.get(item2)); + assertEquals(1, counter.get(item1), "Change of first item state should trigger the rule once"); + assertEquals(0, counter.get(item2), "Change of first item state should not trigger the rule for second item"); // Items: 5, 4. Expected counters: 1, 0. setState(item1, 5); - Assert.assertEquals(m("Set state of first item to same value should not trigger the rule"), - 1, counter.get(item1)); - Assert.assertEquals(m("Change of first item state should not trigger the rule for second item"), 0, counter.get(item2)); + assertEquals(1, counter.get(item1), "Set state of first item to same value should not trigger the rule"); + assertEquals(0, counter.get(item2), "Change of first item state should not trigger the rule for second item"); // Items: 5, 3. Expected counters: 1, 1. setState(item2, 3); - Assert.assertEquals(m("Change of second item state should trigger the rule"), 1, counter.get(item2)); - Assert.assertEquals(m("Change of second item state should not trigger the rule for first item"), 1, counter.get(item1)); + assertEquals(1, counter.get(item2), "Change of second item state should trigger the rule"); + assertEquals(1, counter.get(item1), "Change of second item state should not trigger the rule for first item"); // Items: 5, 3. Expected counters: 1, 1. setState(item1, 5); - Assert.assertEquals(m("Set state of first item to same value should still not trigger the rule"), - 1, counter.get(item1)); - Assert.assertEquals(m("Change of first item state should still not trigger the rule for second item"), 1, counter.get(item2)); + assertEquals(1, counter.get(item1), "Set state of first item to same value should still not trigger the rule"); + assertEquals(1, counter.get(item2), "Change of first item state should still not trigger the rule for second item"); // Items: 6, 3. Expected counters: 2, 1. setState(item1, 6); - Assert.assertEquals(m("Set state of first item should trigger the rule"), - 2, counter.get(item1)); - Assert.assertEquals(m("Change of first item state should still not trigger the rule for second item"), 1, counter.get(item2)); + assertEquals(2, counter.get(item1), "Set state of first item should trigger the rule"); + assertEquals(1, counter.get(item2), "Change of first item state should still not trigger the rule for second item"); // Items: 6, 3. Expected counters: 2, 1. setState(item2, 3); - Assert.assertEquals(m("Set state of second item to same value should not trigger the rule"), - 1, counter.get(item2)); - Assert.assertEquals(m("Change of second item state should not trigger the rule for first item"), 2, counter.get(item1)); + assertEquals(1, counter.get(item2), "Set state of second item to same value should not trigger the rule"); + assertEquals(2, counter.get(item1), "Change of second item state should not trigger the rule for first item"); // Items: 6, 0. Expected counters: 2, 2. setState(item2, 0); - Assert.assertEquals(m("Change of second item state should trigger the rule"), 2, counter.get(item2)); - Assert.assertEquals(m("Change of second item state should not trigger the rule for first item"), 2, counter.get(item1)); + assertEquals(2, counter.get(item2), "Change of second item state should trigger the rule"); + assertEquals(2, counter.get(item1), "Change of second item state should not trigger the rule for first item"); } @Test @@ -204,15 +205,15 @@ public class RulesTest { rule.activateFor(item); - Assert.assertEquals(m("Counter not initialized correctly"), 0, counter.get(item)); + assertEquals(0, counter.get(item), "Counter not initialized correctly"); setState(item, 5); - Assert.assertEquals(m("Change of item state should trigger the rule"), 1, counter.get(item)); + assertEquals(1, counter.get(item), "Change of item state should trigger the rule"); - rule.removeActivationOf(item); + rule.deactivateFor(item); setState(item, 3); - Assert.assertEquals(m("Change of item state should not change the counter anymore"), 1, counter.get(item)); + assertEquals(1, counter.get(item), "Change of item state should not change the counter anymore"); } @Test @@ -231,24 +232,273 @@ public class RulesTest { root.addRule(rule); rule.activateFor(item); - Assert.assertEquals(m("Counter not initialized correctly"), 0, counter.get(item)); + assertEquals(0, counter.get(item), "Counter not initialized correctly"); setState(item, 3); - Assert.assertEquals(m("Change of item to 3 should not trigger the rule, check1 violated"), 0, counter.get(item)); + assertEquals(0, counter.get(item), "Change of item to 3 should not trigger the rule, check1 violated"); setState(item, 4); - Assert.assertEquals(m("Change of item to 4 should trigger the rule"), 1, counter.get(item)); + assertEquals(1, counter.get(item), "Change of item to 4 should trigger the rule"); setState(item, 5); - Assert.assertEquals(m("Change of item to 5 should trigger the rule"), 2, counter.get(item)); + assertEquals(2, counter.get(item), "Change of item to 5 should trigger the rule"); setState(item, 6); - Assert.assertEquals(m("Change of item to 6 should not trigger the rule, check2 violated"), 2, counter.get(item)); + assertEquals(2, counter.get(item), "Change of item to 6 should not trigger the rule, check2 violated"); setState(item, 7); - Assert.assertEquals(m("Change of item to 7 should not trigger the rule, check2 violated"), 2, counter.get(item)); + assertEquals(2, counter.get(item), "Change of item to 7 should not trigger the rule, check2 violated"); + } + + + @Test + public void testStateSyncGroupRewriteStructure() { + // init StateSyncGroup + StateSyncGroup group = new StateSyncGroup(); + + ArrayList<Item> items = new ArrayList<>(); + + // create model and an item + TestUtils.ModelAndItem mai = TestUtils.createModelAndItem(0, true); + NumberItem item0 = mai.item; + group.addTargetItem(item0); + items.add(item0); + + // init more items + for (int i=1;i<=4;i++) { + NumberItem item = TestUtils.addItemTo(mai.model, i, true); + item.setID("item"+i); + group.addTargetItem(item); + items.add(item); + } + + // add StateSyncGroup as rule to root + mai.model.getRoot().addRule(group); + + + + // trace rewritten rule and its actions + Rule rewrittenRule = mai.model.getRoot().getRules().getChild(0); + + + ImmutableList<Action> actions = ImmutableList.copyOf(rewrittenRule.getActionList().iterator()); + ImmutableList<ItemObserver> observers = ImmutableList.copyOf(rewrittenRule.getObservers().iterator()); + + // check general structure + assertEquals(ImmutableList.copyOf(rewrittenRule.getConditionList().iterator()).size(), 0); + assertEquals(actions.size(), 5); + + // check actions and observers + for (int i=0;i<items.size();i++) { + assertTrue(actions.get(i) instanceof SetStateFromTriggeringItemAction); + assertTrue(observers.contains(items.get(i).getItemObserver())); + } + + + + } + + private static void addItemToModel(SmartHomeEntityModel model, Item item) { + getDefaultGroup(model).addItem(item); + } + + @Test + public void testColorItemStateSyncGroup() { + StateSyncGroup group = new StateSyncGroup(); + + //init model and 3 items + SmartHomeEntityModel model = TestUtils.createModelWithGroup(); + + ColorItem colorItem1 = new ColorItem(); + addItemToModel(model,colorItem1); + group.addTargetItem(colorItem1); + + ColorItem colorItem2 = new ColorItem(); + addItemToModel(model,colorItem2); + group.addTargetItem(colorItem2); + + ColorItem colorItem3 = new ColorItem(); + addItemToModel(model,colorItem3); + group.addTargetItem(colorItem3); + + + // add StateSyncGroup as rule to root and trigger rewrite + model.getRoot().addRule(group); + model.getRoot().doSafeFullTraversal(); + + + colorItem1.setState(TupleHSB.parse("0,0,100")); + assertEquals(colorItem1.getState(),TupleHSB.parse("0,0,100")); + assertEquals(colorItem2.getState(),TupleHSB.parse("0,0,100")); + assertEquals(colorItem3.getState(),TupleHSB.parse("0,0,100")); + + colorItem3.setState(TupleHSB.parse("0,0,7")); + assertEquals(colorItem1.getState(),TupleHSB.parse("0,0,7")); + assertEquals(colorItem2.getState(),TupleHSB.parse("0,0,7")); + assertEquals(colorItem3.getState(),TupleHSB.parse("0,0,7")); + + + } + + @Test + public void testDateTimeItemStateSyncGroup() { + StateSyncGroup group = new StateSyncGroup(); + + //init model and 3 items + SmartHomeEntityModel model = TestUtils.createModelWithGroup(); + + DateTimeItem dateTimeItem1 = new DateTimeItem(); + addItemToModel(model,dateTimeItem1); + group.addTargetItem(dateTimeItem1); + + DateTimeItem dateTimeItem2 = new DateTimeItem(); + addItemToModel(model,dateTimeItem2); + group.addTargetItem(dateTimeItem2); + + DateTimeItem dateTimeItem3 = new DateTimeItem(); + addItemToModel(model,dateTimeItem3); + group.addTargetItem(dateTimeItem3); + + + // add StateSyncGroup as rule to root and trigger rewrite + model.getRoot().addRule(group); + model.getRoot().doSafeFullTraversal(); + + Instant i1 = Instant.now(); + dateTimeItem1.setState(i1); + assertEquals(dateTimeItem1.getState(),i1); + assertEquals(dateTimeItem2.getState(),i1); + assertEquals(dateTimeItem3.getState(),i1); + + Instant i2 = Instant.now(); + dateTimeItem3.setState(i2); + assertEquals(dateTimeItem1.getState(),i2); + assertEquals(dateTimeItem2.getState(),i2); + assertEquals(dateTimeItem3.getState(),i2); + } + + /** + * Also for DimmerItem, RollerShutterItem, ActivityItem + */ + @Test + public void testDoubleStateItemStateSyncGroup() { + StateSyncGroup group = new StateSyncGroup(); + + //init model and 3 items + SmartHomeEntityModel model = TestUtils.createModelWithGroup(); + + NumberItem numberItem1 = new NumberItem(); + addItemToModel(model,numberItem1); + group.addTargetItem(numberItem1); + + NumberItem numberItem2 = new NumberItem(); + addItemToModel(model,numberItem2); + group.addTargetItem(numberItem2); + + NumberItem numberItem3 = new NumberItem(); + addItemToModel(model,numberItem3); + group.addTargetItem(numberItem3); + + + // add StateSyncGroup as rule to root and trigger rewrite + model.getRoot().addRule(group); + model.getRoot().doSafeFullTraversal(); + + + numberItem1.setState(123); + assertEquals(numberItem1.getState(),123); + assertEquals(numberItem2.getState(),123); + assertEquals(numberItem3.getState(),123); + + numberItem2.setState(42); + assertEquals(numberItem1.getState(),42); + assertEquals(numberItem2.getState(),42); + assertEquals(numberItem3.getState(),42); } + /** + * Also for ImageItem, LocationItem, PlayerItem, DefaultItem + */ + @Test + public void testStringStateItemStateSyncGroup() { + StateSyncGroup group = new StateSyncGroup(); + + //init model and 3 items + SmartHomeEntityModel model = TestUtils.createModelWithGroup(); + + StringItem stringItem1 = new StringItem(); + addItemToModel(model,stringItem1); + group.addTargetItem(stringItem1); + + StringItem stringItem2 = new StringItem(); + addItemToModel(model,stringItem2); + group.addTargetItem(stringItem2); + + StringItem stringItem3 = new StringItem(); + addItemToModel(model,stringItem3); + group.addTargetItem(stringItem3); + + + // add StateSyncGroup as rule to root and trigger rewrite + model.getRoot().addRule(group); + model.getRoot().doSafeFullTraversal(); + + + stringItem1.setState("123"); + assertEquals(stringItem1.getState(),"123"); + assertEquals(stringItem2.getState(),"123"); + assertEquals(stringItem3.getState(),"123"); + + stringItem2.setState("Hermes"); + assertEquals(stringItem1.getState(),"Hermes"); + assertEquals(stringItem2.getState(),"Hermes"); + assertEquals(stringItem3.getState(),"Hermes"); + + + } + + + /** + * Also for ContactItem + */ + @Test + public void testBooleanStateItemStateSyncGroup() { + StateSyncGroup group = new StateSyncGroup(); + + //init model and 3 items + SmartHomeEntityModel model = TestUtils.createModelWithGroup(); + + SwitchItem switchItem1 = new SwitchItem(); + addItemToModel(model,switchItem1); + group.addTargetItem(switchItem1); + + SwitchItem switchItem2 = new SwitchItem(); + addItemToModel(model,switchItem2); + group.addTargetItem(switchItem2); + + SwitchItem switchItem3 = new SwitchItem(); + addItemToModel(model,switchItem3); + group.addTargetItem(switchItem3); + + + // add StateSyncGroup as rule to root and trigger rewrite + model.getRoot().addRule(group); + model.getRoot().doSafeFullTraversal(); + + + switchItem3.setState(false); + assertFalse(switchItem1.getState()); + assertFalse(switchItem2.getState()); + assertFalse(switchItem3.getState()); + + switchItem1.setState(true); + assertTrue(switchItem1.getState()); + assertTrue(switchItem2.getState()); + assertTrue(switchItem3.getState()); + + } + + @Test public void testTwoActions() { TestUtils.ModelAndItem modelAndItem = createModelAndItem(2); @@ -263,16 +513,16 @@ public class RulesTest { root.addRule(rule); rule.activateFor(item); - Assert.assertEquals(m("First counter not initialized correctly"), 0, counter1.get(item)); - Assert.assertEquals(m("Second counter not initialized correctly"), 0, counter2.get(item)); + assertEquals(0, counter1.get(item), "First counter not initialized correctly"); + assertEquals(0, counter2.get(item), "Second counter not initialized correctly"); setState(item, 3); - Assert.assertEquals(m("Change of item state should trigger the rule for first counter"), 1, counter1.get(item)); - Assert.assertEquals(m("Change of item state should trigger the rule for second counter"), 1, counter2.get(item)); + assertEquals(1, counter1.get(item), "Change of item state should trigger the rule for first counter"); + assertEquals(1, counter2.get(item), "Change of item state should trigger the rule for second counter"); setState(item, 3); - Assert.assertEquals(m("Change of item to same state should not trigger the rule for first counter"), 1, counter1.get(item)); - Assert.assertEquals(m("Change of item to same state should not trigger the rule for second counter"), 1, counter2.get(item)); + assertEquals(1, counter1.get(item), "Change of item to same state should not trigger the rule for first counter"); + assertEquals(1, counter2.get(item), "Change of item to same state should not trigger the rule for second counter"); } @Test @@ -280,7 +530,7 @@ public class RulesTest { TestUtils.ModelAndItem modelAndItem = createModelAndItem(2); Root root = modelAndItem.model.getRoot(); NumberItem item = modelAndItem.item; - NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, useUpdatingItem); + NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, true); Rule ruleA = new Rule(); CountingAction counter1 = new CountingAction(); @@ -297,25 +547,25 @@ public class RulesTest { ruleA.activateFor(item); ruleB.activateFor(item2); - Assert.assertEquals(m("First counter not initialized correctly for first item"), 0, counter1.get(item)); - Assert.assertEquals(m("First counter not initialized correctly for second item"), 0, counter1.get(item2)); - Assert.assertEquals(m("Second counter not initialized correctly for first item"), 0, counter2.get(item)); - Assert.assertEquals(m("Second counter not initialized correctly for second item"), 0, counter2.get(item2)); + assertEquals(0, counter1.get(item), "First counter not initialized correctly for first item"); + assertEquals(0, counter1.get(item2), "First counter not initialized correctly for second item"); + assertEquals(0, counter2.get(item), "Second counter not initialized correctly for first item"); + assertEquals(0, counter2.get(item2), "Second counter not initialized correctly for second item"); setState(item, 3); - Assert.assertEquals(m("Change of first item state should trigger the first rule"), 1, counter1.get(item)); - Assert.assertEquals(m("Change of first item state should trigger the second rule"), 1, counter2.get(item)); - Assert.assertEquals(m("Change of first item state should not trigger the second rule for second item"), 0, counter2.get(item2)); + assertEquals(1, counter1.get(item), "Change of first item state should trigger the first rule"); + assertEquals(1, counter2.get(item), "Change of first item state should trigger the second rule"); + assertEquals(0, counter2.get(item2), "Change of first item state should not trigger the second rule for second item"); setState(item, 3); - Assert.assertEquals(m("Change of item to same state should not trigger the first rule"), 1, counter1.get(item)); - Assert.assertEquals(m("Change of item to same state should not trigger the second rule"), 1, counter2.get(item)); - Assert.assertEquals(m("Change of first item state should not trigger the second rule for second item"), 0, counter2.get(item2)); + assertEquals(1, counter1.get(item), "Change of item to same state should not trigger the first rule"); + assertEquals(1, counter2.get(item), "Change of item to same state should not trigger the second rule"); + assertEquals(0, counter2.get(item2), "Change of first item state should not trigger the second rule for second item"); setState(item2, 7); - Assert.assertEquals(m("Change of second item state should not trigger the first rule"), 1, counter1.get(item)); - Assert.assertEquals(m("Change of second item state should not trigger the second rule for the first item"), 1, counter2.get(item)); - Assert.assertEquals(m("Change of second item state should trigger the second rule for the second item"), 1, counter2.get(item2)); + assertEquals(1, counter1.get(item), "Change of second item state should not trigger the first rule"); + assertEquals(1, counter2.get(item), "Change of second item state should not trigger the second rule for the first item"); + assertEquals(1, counter2.get(item2), "Change of second item state should trigger the second rule for the second item"); } @Test @@ -323,7 +573,7 @@ public class RulesTest { TestUtils.ModelAndItem modelAndItem = createModelAndItem(3); Root root = modelAndItem.model.getRoot(); NumberItem item = modelAndItem.item; - NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, useUpdatingItem); + NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, true); Rule rule = new Rule(); rule.addAction(new SetStateFromConstantStringAction(item2, "5")); @@ -332,16 +582,14 @@ public class RulesTest { root.addRule(rule); rule.activateFor(item); - Assert.assertEquals(m("Affected item not initialized correctly"), - 4, item2.asItemWithDoubleState().getState(), DELTA); + assertEquals(4, item2.asItemWithDoubleState().getState(), DELTA, "Affected item not initialized correctly"); setState(item, 25); - Assert.assertEquals(m("Change of item state should trigger the rule"), 1, counter.get(item)); - Assert.assertEquals(m("Change of item state should set the state of the affected item"), - 5, item2.asItemWithDoubleState().getState(), DELTA); + assertEquals(1, counter.get(item), "Change of item state should trigger the rule"); + assertEquals(5, item2.asItemWithDoubleState().getState(), DELTA, "Change of item state should set the state of the affected item"); } - class ValuedStateProvider implements NewStateProvider { + static class ValuedStateProvider implements NewStateProvider { int value; @Override public String get() { @@ -355,7 +603,7 @@ public class RulesTest { TestUtils.ModelAndItem modelAndItem = createModelAndItem(0); Root root = modelAndItem.model.getRoot(); NumberItem item = modelAndItem.item; - NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 3, useUpdatingItem); + NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 3, true); Rule rule = new Rule(); rule.addAction(new SetStateFromLambdaAction(item2, provider)); @@ -364,32 +612,27 @@ public class RulesTest { root.addRule(rule); rule.activateFor(item); - Assert.assertEquals(m("Affected item not initialized correctly"), - 3, item2.asItemWithDoubleState().getState(), DELTA); + assertEquals(3, item2.asItemWithDoubleState().getState(), DELTA, "Affected item not initialized correctly"); provider.value = 4; setState(item, 1); - Assert.assertEquals(m("Change of item state should trigger the rule"), 1, counter.get(item)); - Assert.assertEquals(m("Change of item state should set the state of the affected item"), - 4, item2.asItemWithDoubleState().getState(), DELTA); + assertEquals(1, counter.get(item), "Change of item state should trigger the rule"); + assertEquals(4, item2.asItemWithDoubleState().getState(), DELTA, "Change of item state should set the state of the affected item"); provider.value = 4; setState(item, 2); - Assert.assertEquals(m("Change of item state should trigger the rule"), 2, counter.get(item)); - Assert.assertEquals(m("Change of item state should set the state of the affected item"), - 4, item2.asItemWithDoubleState().getState(), DELTA); + assertEquals(2, counter.get(item), "Change of item state should trigger the rule"); + assertEquals(4, item2.asItemWithDoubleState().getState(), DELTA, "Change of item state should set the state of the affected item"); provider.value = 5; setState(item, 2); - Assert.assertEquals(m("Change of item to same state should not trigger the rule"), 2, counter.get(item)); - Assert.assertEquals(m("Change of item to same state should not set the state of the affected item"), - 4, item2.asItemWithDoubleState().getState(), DELTA); + assertEquals(2, counter.get(item), "Change of item to same state should not trigger the rule"); + assertEquals(4, item2.asItemWithDoubleState().getState(), DELTA, "Change of item to same state should not set the state of the affected item"); provider.value = 5; setState(item, 3); - Assert.assertEquals(m("Change of item state should trigger the rule"), 3, counter.get(item)); - Assert.assertEquals(m("Change of item state should set the state of the affected item"), - 5, item2.asItemWithDoubleState().getState(), DELTA); + assertEquals(3, counter.get(item), "Change of item state should trigger the rule"); + assertEquals(5, item2.asItemWithDoubleState().getState(), DELTA, "Change of item state should set the state of the affected item"); } @Test @@ -406,20 +649,20 @@ public class RulesTest { root.addRule(rule); rule.activateFor(item); - Assert.assertEquals(m("Counter not initialized correctly"), 0, counter.get(item)); - Assert.assertEquals(m("Affected item not initialized correctly"), "0", item2.getState()); + assertEquals(0, counter.get(item), "Counter not initialized correctly"); + assertEquals("0", item2.getState(), "Affected item not initialized correctly"); setState(item, 5); - Assert.assertEquals(m("Change of item state should trigger the rule"), 1, counter.get(item)); - Assert.assertEquals(m("Change of item state should set the state of the affected item"), "5.0", item2.getState()); + assertEquals(1, counter.get(item), "Change of item state should trigger the rule"); + assertEquals("5.0", item2.getState(), "Change of item state should set the state of the affected item"); setState(item, 5); - Assert.assertEquals(m("Change of item state should not trigger the rule"), 1, counter.get(item)); - Assert.assertEquals(m("Change of item state should not set the state of the affected item"), "5.0", item2.getState()); + assertEquals(1, counter.get(item), "Change of item state should not trigger the rule"); + assertEquals("5.0", item2.getState(), "Change of item state should not set the state of the affected item"); setState(item, 7); - Assert.assertEquals(m("Change of item state should trigger the rule"), 2, counter.get(item)); - Assert.assertEquals(m("Change of item state should set the state of the affected item"), "7.0", item2.getState()); + assertEquals(2, counter.get(item), "Change of item state should trigger the rule"); + assertEquals("7.0", item2.getState(), "Change of item state should set the state of the affected item"); } @Test @@ -427,7 +670,7 @@ public class RulesTest { TestUtils.ModelAndItem modelAndItem = createModelAndItem(3); Root root = modelAndItem.model.getRoot(); NumberItem item = modelAndItem.item; - NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, useUpdatingItem); + NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, true); StringItem affectedItem = addStringItem(root.getSmartHomeEntityModel(), "1"); Rule rule = new Rule(); @@ -444,49 +687,42 @@ public class RulesTest { root.addRule(rule); rule.activateFor(item); - Assert.assertEquals(m("Counter not initialized correctly"), 0, counter.get(item)); - Assert.assertEquals(m("Affected item not initialized correctly"), "1", - affectedItem.getState()); + assertEquals(0, counter.get(item), "Counter not initialized correctly"); + assertEquals("1", affectedItem.getState(), "Affected item not initialized correctly"); // 5 + 4 = 9 setState(item, 5); - Assert.assertEquals(m("Change of item state should trigger the rule"), 1, counter.get(item)); - Assert.assertEquals(m("Change of item state should set the state of the affected item"), - "9", affectedItem.getState()); + assertEquals(1, counter.get(item), "Change of item state should trigger the rule"); + assertEquals("9", affectedItem.getState(), "Change of item state should set the state of the affected item"); // still 5 + 4 = 9, as rule does not trigger for item2 setState(item2, 5); - Assert.assertEquals(m("Change of item2 state should not trigger the rule"), 1, counter.get(item)); - Assert.assertEquals(m("Change of item2 state should not set the state of the affected item"), - "9", affectedItem.getState()); + assertEquals(1, counter.get(item), "Change of item2 state should not trigger the rule"); + assertEquals("9", affectedItem.getState(), "Change of item2 state should not set the state of the affected item"); // still 5 + 4 = 9, as rule should not trigger setState(item, 5); - Assert.assertEquals(m("Change of item to same state should not trigger the rule"), 1, counter.get(item)); - Assert.assertEquals(m("Change of item to same state should not set the state of the affected item"), - "9", affectedItem.getState()); + assertEquals(1, counter.get(item), "Change of item to same state should not trigger the rule"); + assertEquals("9", affectedItem.getState(), "Change of item to same state should not set the state of the affected item"); // 7 + 5 = 12 setState(item, 7); - Assert.assertEquals(m("Change of item state should trigger the rule"), 2, counter.get(item)); - Assert.assertEquals(m("Change of item state should set the state of the affected item"), - "12", affectedItem.getState()); + assertEquals(2, counter.get(item), "Change of item state should trigger the rule"); + assertEquals("12", affectedItem.getState(), "Change of item state should set the state of the affected item"); // add new item to sum - NumberItem item3 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), -4, useUpdatingItem); + NumberItem item3 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), -4, true); action.addSourceItem(item3); // still 7 + 5 = 12, as rule should not trigger setState(item, 7); - Assert.assertEquals(m("Change of item to same state should not trigger the rule"), 2, counter.get(item)); - Assert.assertEquals(m("Change of item to same state should not set the state of the affected item"), - "12", affectedItem.getState()); + assertEquals(2, counter.get(item), "Change of item to same state should not trigger the rule"); + assertEquals("12", affectedItem.getState(), "Change of item to same state should not set the state of the affected item"); // 8 + 5 - 4 = 9 setState(item, 8); - Assert.assertEquals(m("Change of item state should trigger the rule"), 3, counter.get(item)); - Assert.assertEquals(m("Change of item state should set the state of the affected item"), - "9", affectedItem.getState()); + assertEquals(3, counter.get(item), "Change of item state should trigger the rule"); + assertEquals("9", affectedItem.getState(), "Change of item state should set the state of the affected item"); } @Test @@ -494,7 +730,7 @@ public class RulesTest { TestUtils.ModelAndItem modelAndItem = createModelAndItem(3); Root root = modelAndItem.model.getRoot(); NumberItem item = modelAndItem.item; - NumberItem affectedItem = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, useUpdatingItem); + NumberItem affectedItem = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, true); Rule rule = new Rule(); rule.addAction(new AddDoubleToStateAction(affectedItem, 2)); @@ -503,27 +739,23 @@ public class RulesTest { root.addRule(rule); rule.activateFor(item); - Assert.assertEquals(m("Counter not initialized correctly"), 0, counter.get(item)); - Assert.assertEquals(m("Affected item not initialized correctly"), - 4, affectedItem.getState(), DELTA); + assertEquals(0, counter.get(item), "Counter not initialized correctly"); + assertEquals(4, affectedItem.getState(), DELTA, "Affected item not initialized correctly"); // 4 + 2 = 6 setState(item, 5); - Assert.assertEquals(m("Change of item state should trigger the rule"), 1, counter.get(item)); - Assert.assertEquals(m("Change of item state should set the state of the affected item"), - 6, affectedItem.getState(), DELTA); + assertEquals(1, counter.get(item), "Change of item state should trigger the rule"); + assertEquals(6, affectedItem.getState(), DELTA, "Change of item state should set the state of the affected item"); // still 6 setState(item, 5); - Assert.assertEquals(m("Change of item to same state should not trigger the rule"), 1, counter.get(item)); - Assert.assertEquals(m("Change of item to same state should not set the state of the affected item"), - 6, affectedItem.getState(), DELTA); + assertEquals(1, counter.get(item), "Change of item to same state should not trigger the rule"); + assertEquals(6, affectedItem.getState(), DELTA, "Change of item to same state should not set the state of the affected item"); // 6 + 2 = 8 setState(item, -2); - Assert.assertEquals(m("Change of item state should trigger the rule"), 2, counter.get(item)); - Assert.assertEquals(m("Change of item state should set the state of the affected item"), - 8, affectedItem.getState(), DELTA); + assertEquals(2, counter.get(item), "Change of item state should trigger the rule"); + assertEquals(8, affectedItem.getState(), DELTA, "Change of item state should set the state of the affected item"); } @Test @@ -531,7 +763,7 @@ public class RulesTest { TestUtils.ModelAndItem modelAndItem = createModelAndItem(3); Root root = modelAndItem.model.getRoot(); NumberItem item = modelAndItem.item; - NumberItem affectedItem = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, useUpdatingItem); + NumberItem affectedItem = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, true); Rule rule = new Rule(); rule.addAction(new MultiplyDoubleToStateAction(affectedItem, 2)); @@ -540,27 +772,23 @@ public class RulesTest { root.addRule(rule); rule.activateFor(item); - Assert.assertEquals(m("Counter not initialized correctly"), 0, counter.get(item)); - Assert.assertEquals(m("Affected item not initialized correctly"), - 4, affectedItem.getState(), DELTA); + assertEquals(0, counter.get(item), "Counter not initialized correctly"); + assertEquals(4, affectedItem.getState(), DELTA, "Affected item not initialized correctly"); // 4 * 2 = 8 setState(item, 5); - Assert.assertEquals(m("Change of item state should trigger the rule"), 1, counter.get(item)); - Assert.assertEquals(m("Change of item state should set the state of the affected item"), - 8, affectedItem.getState(), DELTA); + assertEquals(1, counter.get(item), "Change of item state should trigger the rule"); + assertEquals(8, affectedItem.getState(), DELTA, "Change of item state should set the state of the affected item"); // still 8 setState(item, 5); - Assert.assertEquals(m("Change of item to same state should not trigger the rule"), 1, counter.get(item)); - Assert.assertEquals(m("Change of item to same state should not set the state of the affected item"), - 8, affectedItem.getState(), DELTA); + assertEquals(1, counter.get(item), "Change of item to same state should not trigger the rule"); + assertEquals(8, affectedItem.getState(), DELTA, "Change of item to same state should not set the state of the affected item"); // 8 * 2 = 16 setState(item, 0); - Assert.assertEquals(m("Change of item state should trigger the rule"), 2, counter.get(item)); - Assert.assertEquals(m("Change of item state should set the state of the affected item"), - 16, affectedItem.getState(), DELTA); + assertEquals(2, counter.get(item), "Change of item state should trigger the rule"); + assertEquals(16, affectedItem.getState(), DELTA, "Change of item state should set the state of the affected item"); } @Test @@ -568,8 +796,8 @@ public class RulesTest { TestUtils.ModelAndItem modelAndItem = createModelAndItem(3); Root root = modelAndItem.model.getRoot(); NumberItem item1 = modelAndItem.item; - NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, useUpdatingItem); - NumberItem affectedItem = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 5, useUpdatingItem); + NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, true); + NumberItem affectedItem = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 5, true); Rule ruleA = new Rule(); ruleA.addAction(new AddDoubleToStateAction(affectedItem, 2)); @@ -588,34 +816,30 @@ public class RulesTest { ruleA.activateFor(item1); ruleB.activateFor(item2); - Assert.assertEquals(m("CounterA not initialized correctly for first item"), 0, counterA.get(item1)); - Assert.assertEquals(m("CounterA not initialized correctly for second item"), 0, counterA.get(item2)); - Assert.assertEquals(m("CounterB not initialized correctly for first item"), 0, counterB.get(item1)); - Assert.assertEquals(m("CounterB not initialized correctly for second item"), 0, counterB.get(item2)); - Assert.assertEquals(m("Affected item not initialized correctly"), - 5, affectedItem.getState(), DELTA); + assertEquals(0, counterA.get(item1), "CounterA not initialized correctly for first item"); + assertEquals(0, counterA.get(item2), "CounterA not initialized correctly for second item"); + assertEquals(0, counterB.get(item1), "CounterB not initialized correctly for first item"); + assertEquals(0, counterB.get(item2), "CounterB not initialized correctly for second item"); + assertEquals(5, affectedItem.getState(), DELTA, "Affected item not initialized correctly"); // First, 5 + 2 = 7. Then, 7 * 3 = 21 setState(item1, 5); - Assert.assertEquals(m("Change of item state should trigger the ruleA"), 1, counterA.get(item1)); - Assert.assertEquals(m("Change of item state should also trigger the ruleB"), 1, counterB.get(item1)); - Assert.assertEquals(m("Change of item state should set the state of the affected item"), - 21, affectedItem.getState(), DELTA); + assertEquals(1, counterA.get(item1), "Change of item state should trigger the ruleA"); + assertEquals(1, counterB.get(item1), "Change of item state should also trigger the ruleB"); + assertEquals(21, affectedItem.getState(), DELTA, "Change of item state should set the state of the affected item"); // still 21 setState(item1, 5); - Assert.assertEquals(m("Change of item to same state should not trigger the ruleA"), 1, counterA.get(item1)); - Assert.assertEquals(m("Change of item to same state should not trigger the ruleB"), 1, counterB.get(item1)); - Assert.assertEquals(m("Change of item to same state should not set the state of the affected item"), - 21, affectedItem.getState(), DELTA); + assertEquals(1, counterA.get(item1), "Change of item to same state should not trigger the ruleA"); + assertEquals(1, counterB.get(item1), "Change of item to same state should not trigger the ruleB"); + assertEquals(21, affectedItem.getState(), DELTA, "Change of item to same state should not set the state of the affected item"); // Only, 21 * 3 = 63 setState(item2, 1); - Assert.assertEquals(m("Change of second item state should not trigger the ruleA"), 1, counterA.get(item1)); - Assert.assertEquals(m("Change of second item state should not trigger the ruleB for first item"), 1, counterB.get(item1)); - Assert.assertEquals(m("Change of second item state should trigger the ruleB for second item"), 1, counterB.get(item2)); - Assert.assertEquals(m("Change of second item state should set the state of the affected item"), - 63, affectedItem.getState(), DELTA); + assertEquals(1, counterA.get(item1), "Change of second item state should not trigger the ruleA"); + assertEquals(1, counterB.get(item1), "Change of second item state should not trigger the ruleB for first item"); + assertEquals(1, counterB.get(item2), "Change of second item state should trigger the ruleB for second item"); + assertEquals(63, affectedItem.getState(), DELTA, "Change of second item state should set the state of the affected item"); } @Test @@ -623,8 +847,8 @@ public class RulesTest { TestUtils.ModelAndItem modelAndItem = createModelAndItem(3); Root root = modelAndItem.model.getRoot(); NumberItem item1 = modelAndItem.item; - NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, useUpdatingItem); - NumberItem affectedItem = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 5, useUpdatingItem); + NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, true); + NumberItem affectedItem = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 5, true); Rule rule = new Rule(); SetStateFromExpression action = new SetStateFromExpression(); @@ -638,63 +862,87 @@ public class RulesTest { root.addRule(rule); rule.activateFor(item1); - Assert.assertEquals(m("Counter not initialized correctly"), 0, counter.get(item1)); - Assert.assertEquals(m("Second item not initialized correctly"), - 4, item2.getState(), DELTA); - Assert.assertEquals(m("Affected item not initialized correctly"), - 5, affectedItem.getState(), DELTA); + assertEquals(0, counter.get(item1), "Counter not initialized correctly"); + assertEquals(4, item2.getState(), DELTA, "Second item not initialized correctly"); + assertEquals(5, affectedItem.getState(), DELTA, "Affected item not initialized correctly"); // 5 + 4 = 9 setState(item1, 5); - Assert.assertEquals(m("Change of item state should trigger the rule"), 1, counter.get(item1)); - Assert.assertEquals(m("Change of item state should set the state of the affected item"), - 9, affectedItem.getState(), DELTA); + assertEquals(1, counter.get(item1), "Change of item state should trigger the rule"); + assertEquals(9, affectedItem.getState(), DELTA, "Change of item state should set the state of the affected item"); // still 9 setState(item1, 5); - Assert.assertEquals(m("Change of item to same state should not trigger the rule"), 1, counter.get(item1)); - Assert.assertEquals(m("Change of item to same state should not set the state of the affected item"), - 9, affectedItem.getState(), DELTA); + assertEquals(1, counter.get(item1), "Change of item to same state should not trigger the rule"); + assertEquals(9, affectedItem.getState(), DELTA, "Change of item to same state should not set the state of the affected item"); // still 9 (changes of item2 do not trigger the rule) setState(item2, 1); - Assert.assertEquals(m("Change of second item to same state should not trigger the rule"), 1, counter.get(item1)); - Assert.assertEquals(m("Change of second item to same state should not set the state of the affected item"), - 9, affectedItem.getState(), DELTA); + assertEquals(1, counter.get(item1), "Change of second item to same state should not trigger the rule"); + assertEquals(9, affectedItem.getState(), DELTA, "Change of second item to same state should not set the state of the affected item"); // 0 + 1 = 1 setState(item1, 0); - Assert.assertEquals(m("Change of item state should trigger the rule"), 2, counter.get(item1)); - Assert.assertEquals(m("Change of item state should set the state of the affected item"), - 1, affectedItem.getState(), DELTA); + assertEquals(2, counter.get(item1), "Change of item state should trigger the rule"); + assertEquals(1, affectedItem.getState(), DELTA, "Change of item state should set the state of the affected item"); } + @Disabled("Not working reliably, need to be made more robust") @Test public void testCronJobRule() { Rule rule = new Rule(); CountingAction counter = new CountingAction(); rule.addAction(counter); - Assert.assertEquals(m("Counter not initialized correctly"), 0, counter.get(null)); + assertEquals(0, counter.get(null), "Counter not initialized correctly"); ScheduledFuture f = rule.activateEvery(50, TimeUnit.MILLISECONDS); waitMillis(160); - Assert.assertTrue("Rule cron job could not be cancelled", f.cancel(true)); + assertTrue(f.cancel(true), "Rule cron job could not be cancelled"); // ----------------------- 160ms ----------------------- // + -- 50ms -- + -- 50ms -- + -- 50 ms -- + -- 10 ms -- // 4 times executed (+), with no initial delay - Assert.assertEquals(m("Rule was not executed four times"), 4, counter.get(null)); + assertEquals(4, counter.get(null), "Rule was not executed four times"); counter.reset(); - Assert.assertEquals(m("Counter not reset correctly"), 0, counter.get(null)); + assertEquals(0, counter.get(null), "Counter not reset correctly"); f = rule.activateEvery(80, 50, TimeUnit.MILLISECONDS); waitMillis(150); - Assert.assertTrue("Rule cron job could not be cancelled", f.cancel(true)); + assertTrue(f.cancel(true), "Rule cron job could not be cancelled"); // ------------------ 150ms ------------------ // ----- 80ms ----- + -- 50ms -- + -- 20 ms -- // 2 times executed (+), with given delay - Assert.assertEquals(m("Rule was not executed two times"), 2, counter.get(null)); + assertEquals(2, counter.get(null), "Rule was not executed two times"); + } + + @Test + public void testFrequencySetting() { + + TestUtils.ModelAndItem mai = createModelAndItem(0); + NumberItem numberItem = mai.item; + + FrequencySetting frequencySetting = new FrequencySetting(); + frequencySetting.setEventProcessingFrequency(10); + numberItem.setFrequencySetting(frequencySetting); + + Rule rule = new Rule(); + CountingAction counter = new CountingAction(); + rule.addAction(counter); + rule.activateFor(numberItem); + numberItem.setState(1); + numberItem.setState(2); + assertEquals(1, counter.get(numberItem), "Action was triggered although FrequencySetting too small"); + counter.reset(); + waitMillis(100); + numberItem.setState(3); + assertEquals(1, counter.get(numberItem), "Action wasn't triggered although frequency FrequencySetting is small enough"); + counter.reset(); + numberItem.setState(4); + numberItem.setState(5); + assertEquals(0, counter.get(numberItem), "Action was triggered although FrequencySetting too small"); + counter.reset(); + } private static void waitMillis(int millis) { @@ -702,17 +950,13 @@ public class RulesTest { Thread.sleep(millis); } catch (InterruptedException e) { e.printStackTrace(); - Assert.fail("Sleeping was interrupted!"); + fail("Sleeping was interrupted!"); } } - private String m(String message) { - return message + " (Using " + name + ")"; - } - private StringItem addStringItem(SmartHomeEntityModel model, String initialValue) { StringItem item = new StringItem(); - Group group = TestUtils.getDefaultGroup(model); + Group group = getDefaultGroup(model); item.setID("item" + group.getNumItem()); item.setState(initialValue, false); group.addItem(item); @@ -720,28 +964,11 @@ public class RulesTest { } private TestUtils.ModelAndItem createModelAndItem(long initialValue) { - return TestUtils.createModelAndItem(initialValue, useUpdatingItem); + return TestUtils.createModelAndItem(initialValue, true); } private void setState(NumberItem item, long newState) { item.setState(newState); - if (!useUpdatingItem && item.hasItemObserver()) { - item.getItemObserver().apply(); - } - } - - @Parameterized.Parameter - public String name; - - @Parameterized.Parameter(1) - public boolean useUpdatingItem; - - @Parameterized.Parameters(name = "{0}") - public static Iterable<Object[]> getTests() { - return Arrays.asList(new Object[][] { -// {"Normal Item", false}, - {"Updating Item", true} - }); } } diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/ParallelParameterized.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/ParallelParameterized.java deleted file mode 100644 index 2d335313a0115e22b18968af59ebfdc9a07407ff..0000000000000000000000000000000000000000 --- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/ParallelParameterized.java +++ /dev/null @@ -1,78 +0,0 @@ -/* Copyright (c) 2005-2015, The JastAdd Team - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package de.tudresden.inf.st.eraser.jastadd_test.core; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -import org.junit.runners.Parameterized; -import org.junit.runners.model.RunnerScheduler; - -/** - * Runs parameterized tests in parallel - */ -public class ParallelParameterized extends Parameterized { - - private static final int NUM_THREADS = 16; - - /** - * Constructor - * @param klass - * @throws Throwable - */ - public ParallelParameterized(Class<?> klass) throws Throwable { - super(klass); - setScheduler(new ThreadPoolScheduler()); - } - - private static class ThreadPoolScheduler implements RunnerScheduler { - private final ExecutorService executor; - - public ThreadPoolScheduler() { - executor = Executors.newFixedThreadPool(NUM_THREADS); - } - - @Override - public void finished() { - executor.shutdown(); - try { - executor.awaitTermination(10, TimeUnit.MINUTES); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - @Override - public void schedule(Runnable test) { - executor.submit(test); - } - } -} diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/TestConfiguration.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/TestConfiguration.java index df9495aaaddac0ca310b319c07a8b614c067d493..860efad6e30f530a7e23dacc4c8c43cc7c3ab864 100644 --- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/TestConfiguration.java +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/TestConfiguration.java @@ -135,12 +135,10 @@ public class TestConfiguration { File[] files = dir.listFiles(); if (files == null) return; for (File file: files) { - if (!file.isDirectory()) { - file.delete(); - } else { + if (file.isDirectory()) { cleanDirectory(file); - file.delete(); } + file.delete(); } } diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/TestProperties.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/TestProperties.java index 1b8099c99948940e0c376e59b843221005471b53..5ee91322f00e961a46d4e77955c08cde8d76a638 100644 --- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/TestProperties.java +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/TestProperties.java @@ -136,7 +136,7 @@ public class TestProperties extends Properties { private static void addPaths(Collection<String> list, String pathList) { String[] items = pathList.split(","); for (String item : items) { - item = item.trim().replace('\\', '/'); + item = Util.platformIndependentPath(item.trim()); if (!item.isEmpty()) { list.add(item); } diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/TestRunner.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/TestRunner.java index 1b4ba7616039ba915162919f5717a593a44972f6..c7ade8111b0f140bd3a1413f9783bef41839eba8 100644 --- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/TestRunner.java +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/TestRunner.java @@ -30,18 +30,16 @@ package de.tudresden.inf.st.eraser.jastadd_test.core; import beaver.Parser; -import de.tudresden.inf.st.eraser.jastadd.model.SmartHomeEntityModel; import de.tudresden.inf.st.eraser.jastadd.model.Root; import de.tudresden.inf.st.eraser.util.ParserUtils; import java.io.File; import java.io.IOException; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.util.Properties; -import static org.junit.Assert.fail; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; /** * Utility methods for running JastAdd unit tests. @@ -50,7 +48,7 @@ import static org.junit.Assert.assertEquals; public class TestRunner { private static int TEST_TIMEOUT = 5000; - private static String SYS_LINE_SEP = System.getProperty("line.separator"); + private static final String SYS_LINE_SEP = System.getProperty("line.separator"); static { // Set up test timeout. @@ -60,9 +58,8 @@ public class TestRunner { /** * Run test with given JastAdd configuration. * @param config test case specific configuration - * @param jastaddProperties global test configuration */ - public static void runTest(TestConfiguration config, Properties jastaddProperties) + public static void runTest(TestConfiguration config) throws Exception { Result expected = config.expected; @@ -129,9 +126,10 @@ public class TestRunner { try { File expected = expectedJastAddErrorOutput(testDir); File actual = new File(tmpDir, "jastadd.err"); - assertEquals("Error output files differ", + assertEquals( readFileToString(expected), - readFileToString(actual)); + readFileToString(actual), + "Error output files differ"); } catch (IOException e) { fail("IOException occurred while comparing JastAdd error output: " + e.getMessage()); } @@ -167,7 +165,7 @@ public class TestRunner { fail("Missing file: " + expectedFileLocation); } String expected = readFileToString(expectedOutput); - assertEquals("Output differs!", expected, actual); + assertEquals(expected, actual, "Output differs!"); } catch (IOException e) { fail("IOException occurred while comparing output: " + e.getMessage()); } @@ -189,7 +187,7 @@ public class TestRunner { if (!file.isFile()) { return ""; } - String content = new String(Files.readAllBytes(file.toPath()), Charset.forName("UTF-8")); + String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); return normalizeText(content); } @@ -197,7 +195,7 @@ public class TestRunner { * Normalize line endings and replace back-slashes with slashes. * This is used to avoid insignificant platform differences from * altering test results. - * @param text + * @param text text to normalize * @return normalized text string */ private static String normalizeText(String text) { diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/TupleHSBTest.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/TupleHSBTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6976a52dd32ef2bbe7e5e5451afc64fcbd5deeaa --- /dev/null +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/TupleHSBTest.java @@ -0,0 +1,109 @@ +package de.tudresden.inf.st.eraser.jastadd_test.core; + +import de.tudresden.inf.st.eraser.jastadd.model.TupleHSB; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +/** + * Testing the class {@link de.tudresden.inf.st.eraser.jastadd.model.TupleHSB}. + * + * @author rschoene - Initial contribution + */ +public class TupleHSBTest { + + @Test + public void testBounds() { + checkWithinBounds(TupleHSB.of(1,2,3)); + checkWithinBounds(TupleHSB.of(340,2,3)); + checkWithinBounds(TupleHSB.of(999,2,3)); + checkWithinBounds(TupleHSB.of(11,200,3)); + checkWithinBounds(TupleHSB.of(240,200,3)); + checkWithinBounds(TupleHSB.of(899,200,3)); + checkWithinBounds(TupleHSB.of(21,2,300)); + checkWithinBounds(TupleHSB.of(140,2,300)); + checkWithinBounds(TupleHSB.of(799,2,300)); + } + + @Test + public void testEquals() { + TupleHSB one = TupleHSB.of(1,2,3); + TupleHSB two = TupleHSB.of(1,2,3); + TupleHSB three = TupleHSB.of(99,99,99); + assertEquals(one, two); + assertNotEquals(one, three); + assertNotEquals(two, three); + + TupleHSB oneEqualModuloHue = TupleHSB.of(361,2,3); + assertEquals(one, oneEqualModuloHue); + + TupleHSB bigSB = TupleHSB.of(50,100,100); + TupleHSB biggerS = TupleHSB.of(50,123,100); + TupleHSB biggerB = TupleHSB.of(50,123,6484); + assertEquals(bigSB, biggerS); + assertEquals(bigSB, biggerB); + } + + @Test + public void testWithDifferent() { + TupleHSB one = TupleHSB.of(1,2,3); + TupleHSB oneDifferentHue = one.withDifferentHue(43); + TupleHSB oneDifferentSaturation = one.withDifferentSaturation(43); + TupleHSB oneDifferentBrightness = one.withDifferentBrightness(43); + TupleHSB oneEqualModuloHue = one.withDifferentHue(721); + + assertNotEquals(one, oneDifferentHue); + assertNotEquals(one, oneDifferentSaturation); + assertNotEquals(one, oneDifferentBrightness); + assertNotEquals(oneDifferentHue, oneDifferentSaturation); + assertNotEquals(oneDifferentHue, oneDifferentBrightness); + assertNotEquals(oneDifferentSaturation, oneDifferentBrightness); + + assertEquals(one, oneEqualModuloHue); + } + + @Test + public void testPrint() { + TupleHSB one = TupleHSB.of(1,2,3); + String expectedForOne = "1,2,3"; + assertEquals(expectedForOne, one.toString()); + + TupleHSB two = TupleHSB.of(341,92,555); + String expectedForTwo = "341,92,100"; + assertEquals(expectedForTwo, two.toString()); + } + + @Test + public void testParse() { + String one = "3,2,1"; + TupleHSB expectedForOne = TupleHSB.of(3, 2, 1); + assertEquals(expectedForOne, TupleHSB.parse(one)); + + String two = "399,201,17"; + TupleHSB expectedForTwo = TupleHSB.of(39, 100, 17); + assertEquals(expectedForTwo, TupleHSB.parse(two)); + } + + @Test + public void testClone() { + TupleHSB one = TupleHSB.of(361,2,3); + TupleHSB two = TupleHSB.of(50,123,100); + TupleHSB clone = one.clone(); + + assertEquals(one, clone); + assertNotEquals(one, two); + assertNotEquals(clone, two); + } + + private void checkWithinBounds(TupleHSB hsb) { + assertThat(hsb.getHue()).isGreaterThanOrEqualTo(0); + assertThat(hsb.getHue()).isLessThanOrEqualTo(360); + assertThat(hsb.getSaturation()).isGreaterThanOrEqualTo(0); + assertThat(hsb.getSaturation()).isLessThanOrEqualTo(100); + assertThat(hsb.getBrightness()).isGreaterThanOrEqualTo(0); + assertThat(hsb.getBrightness()).isLessThanOrEqualTo(100); + } + +} diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/Util.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/Util.java index 117b8d75546692f311b44cb59c76d0b70efcc7a1..7fb7320f9127648a1ee2526ea5878b11360cc603 100644 --- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/Util.java +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/jastadd_test/core/Util.java @@ -38,16 +38,20 @@ import java.util.*; */ public class Util { + public static String platformIndependentPath(String regularPath) { + return regularPath.replace('\\', '/'); + } + // public static final String TEST_ROOT = "src/test/resources/tests"; // public static final String TEST_ROOT = "tests"; /** * Find all test directories - * @param testRoot - * @param tests - * @param excludes + * @param testRoot root directory of tests + * @param tests list of test configurations to add + * @param excludes paths to exclude */ - private static void addChildTestDirs(File testRoot, Collection<Object[]> tests, + private static void addChildTestDirs(File testRoot, List<TestConfiguration> tests, Collection<String> excludes) { if (testRoot == null) { return; @@ -58,12 +62,12 @@ public class Util { return; } for (File child: files) { - addTestDir(testRoot.getPath(), child, tests, excludes); + addTestDir(platformIndependentPath(testRoot.getPath()), child, tests, excludes); } } private static void addTestDir(String testRoot, File dir, - Collection<Object[]> tests, Collection<String> excludes) { + List<TestConfiguration> tests, Collection<String> excludes) { if (dir.isDirectory()) { String path = dir.getPath().replace(File.separatorChar, '/'); @@ -89,23 +93,23 @@ public class Util { /** * Add separate test for each option set. */ - private static void addUnitTests(String testRoot, Collection<Object[]> tests, String path, File dir) { + private static void addUnitTests(String testRoot, List<TestConfiguration> tests, String path, File dir) { Properties testProperties = Util.getTestProperties(dir); String optionsProperty = testProperties.getProperty("options", ""); String extraOptions = testProperties.getProperty("extraoptions", ""); - String options[] = optionsProperty.split("\\|", -1); + String[] options = optionsProperty.split("\\|", -1); int index = 1; for (String option : options) { TestOptions u = new TestOptions((option + " " + extraOptions).trim(), options.length > 1, index); TestConfiguration config = new TestConfiguration(testRoot, path, u); - tests.add(new Object[] { config }); + tests.add(config); index++; } } private static void addByPattern(File root, String pattern, - Collection<Object[]> tests, Collection<String> excludes) { + List<TestConfiguration> tests, Collection<String> excludes) { if (pattern.isEmpty()) { addTestDir(root.getPath(), root, tests, excludes); } else { @@ -116,7 +120,7 @@ public class Util { rest = ""; } else { part = pattern.substring(0, index); - rest = pattern.substring(index+1, pattern.length()); + rest = pattern.substring(index + 1); } if (part.indexOf('*') == -1) { addByPattern(new File(root, part), rest, tests, excludes); @@ -168,8 +172,8 @@ public class Util { * @param properties * @return A collection of String arrays containing the test directories */ - public static Iterable<Object[]> getTests(TestProperties properties) { - List<Object[]> tests = new LinkedList<>(); + public static List<TestConfiguration> getTests(TestProperties properties) { + List<TestConfiguration> tests = new ArrayList<>(); Collection<String> includes = properties.includes(); Collection<String> excludes = properties.excludes(); @@ -178,16 +182,16 @@ public class Util { addTestDir(properties.getTestRoot(), new File(properties.getTestRoot()), tests, excludes); } else { for (String include: includes) { - addByPattern(new File(properties.getTestRoot()), include.replace('\\', '/'), tests, excludes); + addByPattern(new File(properties.getTestRoot()), platformIndependentPath(include), tests, excludes); } } - for (Object[] test : tests) { - ((TestConfiguration) test[0]).addOptions(properties.getProperty("extraoptions", "")); + for (TestConfiguration test : tests) { + test.addOptions(properties.getProperty("extraoptions", "")); } // Sort the tests lexicographically. - tests.sort(Comparator.comparing(a -> a[0].toString())); + tests.sort(Comparator.comparing(TestConfiguration::toString)); return tests; } @@ -201,7 +205,6 @@ public class Util { FileInputStream in = new FileInputStream(propertiesFile); properties.load(in); in.close(); - } catch (FileNotFoundException e) { } catch (IOException e) { e.printStackTrace(); } @@ -231,7 +234,6 @@ public class Util { break; } } - } catch (FileNotFoundException e) { } catch (IOException e) { e.printStackTrace(); } diff --git a/eraser-base/src/test/resources/tests/ppc2/input.eraser b/eraser-base/src/test/resources/tests/ppc2/input.eraser index d53a7f32e5ed4a0f1ccf394458f9b5e3d786e25a..36fddab19dc562bbed3fe437c147016f64ef7eee 100644 --- a/eraser-base/src/test/resources/tests/ppc2/input.eraser +++ b/eraser-base/src/test/resources/tests/ppc2/input.eraser @@ -4,4 +4,5 @@ Group: id="my-group" items=["iris1_item"] ; ThingType: id="skywriter-hat" label="SkyWriterHAT" description="SkyWriterHAT Gesture Recognition" parameters=["brokername"] channelTypes=["flick-type"] ; ChannelType: id="flick-type" itemType="String" label="Last Flick" description="Last Flick detected (and its direction)" category="Motion" readOnly ; Parameter: id="brokername" type="text" required label="Broker Name" description="Name of the broker as defined in the <broker>.url in services/mqtt.cfg. See the MQTT Binding for more information on how to configure MQTT broker connections." context="service" default="mosquitto" ; +FrequencySetting: id="ip1" procFreq="10.0"; Influx: host="localhost" ; diff --git a/eraser-base/src/test/resources/tests/ppc2/output.eraser b/eraser-base/src/test/resources/tests/ppc2/output.eraser index e7371849cfb16b0f0228c800666b4145b0437544..9fec0c1fd2611dd05a1e7872899aecd21936ff89 100644 --- a/eraser-base/src/test/resources/tests/ppc2/output.eraser +++ b/eraser-base/src/test/resources/tests/ppc2/output.eraser @@ -3,5 +3,6 @@ Group: id="my-group" items=["iris1_item"] ; ThingType: id="skywriter-hat" label="SkyWriterHAT" description="SkyWriterHAT Gesture Recognition" parameters=["brokername"] channelTypes=["flick-type"] ; ChannelType: id="flick-type" label="Last Flick" description="Last Flick detected (and its direction)" itemType="String" category="Motion" readOnly ; Parameter: id="brokername" label="Broker Name" description="Name of the broker as defined in the <broker>.url in services/mqtt.cfg. See the MQTT Binding for more information on how to configure MQTT broker connections." type="Text" context="service" default="mosquitto" required ; +FrequencySetting: id="ip1" procFreq="10.0" ; Mqtt: incoming="ppc2/" outgoing="oh2/in/" host="localhost" ; Influx: host="localhost" ; diff --git a/eraser-base/src/test/resources/tests/ppc4/input.eraser b/eraser-base/src/test/resources/tests/ppc4/input.eraser index 8687f881951aa3bc8766e209a87432a235e2a72a..5d7c299b5b794316c4dce44e97f242e5a3435388 100644 --- a/eraser-base/src/test/resources/tests/ppc4/input.eraser +++ b/eraser-base/src/test/resources/tests/ppc4/input.eraser @@ -9,14 +9,14 @@ Group: id="my-empty-group" ; Color Item : id="color1" label="a Color Item" state="1,2,3" topic="item/hsb/color1/state"; DateTime Item : id="datetime1" label="a DateTime Item" state="1543415826" topic="item/date/datetime1/state"; Contact Item : id="contact1" label="a Contact Item" state="CLOSED" topic="item/bool/contact1/state"; -Dimmer Item : id="dimmer1" label="a Dimmer Item" state="123" topic="item/double/dimmer1/state" controls=["color1", "datetime1"]; -Image Item : id="image1" label="an Image Item" state="def" topic="item/str/image1/state" controls=[]; +Dimmer Item : id="dimmer1" label="a Dimmer Item" state="123" topic="item/double/dimmer1/state"; +Image Item : id="image1" label="an Image Item" state="def" topic="item/str/image1/state" ; Location Item : id="location1" label="a Location Item" state="ghi" topic="item/str/location1/state"; -Number Item : id="number1" label="a Number Item" state="456" topic="item/double/number1/state" controls=["string1"]; +Number Item : id="number1" label="a Number Item" state="456" topic="item/double/number1/state" ; Player Item : id="player1" label="a Player Item" state="jkl" topic="item/str/player1/state"; RollerShutter Item : id="rollerShutter1" label="a RollerShutter Item" state="0" topic="item/str/rs1/state"; Activity Item: id="activity"; String Item : id="string1" label="a String Item" state="mno" topic="item/str/string1/state"; -Switch Item : id="switch1" label="a Switch Item" state="true" topic="item/bool/switch1/state" controls=["rollerShutter1"]; +Switch Item : id="switch1" label="a Switch Item" state="true" topic="item/bool/switch1/state"; Item : id="default1" label="a Default Item" state="pqr" topic="item/str/default1/state"; Influx: host="localhost" ; diff --git a/eraser-base/src/test/resources/tests/ppc4/output.eraser b/eraser-base/src/test/resources/tests/ppc4/output.eraser index d4934300c2f7e3c0c8be65bf4f4932bbc4b7be52..4f22e2e26a56180066d1370a6dda13d695d16b3e 100644 --- a/eraser-base/src/test/resources/tests/ppc4/output.eraser +++ b/eraser-base/src/test/resources/tests/ppc4/output.eraser @@ -4,13 +4,13 @@ Image Item: id="image1" label="an Image Item" state="def" topic="item/str/image1 Location Item: id="location1" label="a Location Item" state="ghi" topic="item/str/location1/state" ; DateTime Item: id="datetime1" label="a DateTime Item" state="1970-01-18T20:43:35.826Z" topic="item/date/datetime1/state" ; Item: id="default1" label="a Default Item" state="pqr" topic="item/str/default1/state" ; -Dimmer Item: id="dimmer1" label="a Dimmer Item" state="123.0" topic="item/double/dimmer1/state" controls=["color1", "datetime1"] ; +Dimmer Item: id="dimmer1" label="a Dimmer Item" state="123.0" topic="item/double/dimmer1/state" ; Player Item: id="player1" label="a Player Item" state="jkl" topic="item/str/player1/state" ; -Number Item: id="number1" label="a Number Item" state="456.0" topic="item/double/number1/state" controls=["string1"] ; +Number Item: id="number1" label="a Number Item" state="456.0" topic="item/double/number1/state" ; RollerShutter Item: id="rollerShutter1" label="a RollerShutter Item" state="0.0" topic="item/str/rs1/state" ; Activity Item: id="activity" ; String Item: id="string1" label="a String Item" state="mno" topic="item/str/string1/state" ; -Switch Item: id="switch1" label="a Switch Item" state="ON" topic="item/bool/switch1/state" controls=["rollerShutter1"] ; +Switch Item: id="switch1" label="a Switch Item" state="ON" topic="item/bool/switch1/state" ; Group: id="my-first-group" items=["color1", "contact1", "image1", "location1"] aggregation="AND" ("ON", "OFF") ; Group: id="my-second-group" items=["datetime1", "default1"] ; Group: id="my-third-group" items=["dimmer1", "player1"] ; diff --git a/eraser-base/src/test/resources/tests/ppc5/input.eraser b/eraser-base/src/test/resources/tests/ppc5/input.eraser index 21cc14bc796965aae71d2bca119f60b0cdf640c5..194cc284c08bf3c0f8c44a50dec9d22d8d3aedc5 100644 --- a/eraser-base/src/test/resources/tests/ppc5/input.eraser +++ b/eraser-base/src/test/resources/tests/ppc5/input.eraser @@ -11,7 +11,7 @@ Group: items=["min-item", "max-item"] id="max-group" groups=["min-group"] aggreg Number Item: id="min-item" ; // state will be set to default value Switch Item: topic="items/max" - controls=["min-item"] id="max-item" state="true" label="Item with all members set" category="not used" + id="max-item" state="true" label="Item with all members set" category="not used" metaData={"one":"true", "zero":"false"} ; // Parameters will get sorted alphabetically in output diff --git a/eraser-base/src/test/resources/tests/ppc5/output.eraser b/eraser-base/src/test/resources/tests/ppc5/output.eraser index 5498db37c271084736a1be692b318f92ab2c5b5e..82738395874c5951e61078f4fb386998d1d86829 100644 --- a/eraser-base/src/test/resources/tests/ppc5/output.eraser +++ b/eraser-base/src/test/resources/tests/ppc5/output.eraser @@ -1,7 +1,7 @@ Thing: id="min-thing" type="min-thing-type" ; Thing: id="max-thing" label="Max Thing" type="max-thing-type" channels=["min-channel", "max-channel"] ; Number Item: id="min-item" state="0.0" ; -Switch Item: id="max-item" label="Item with all members set" state="ON" category="not used" topic="items/max" controls=["min-item"] metaData={"one":"true", "zero":"false"} ; +Switch Item: id="max-item" label="Item with all members set" state="ON" category="not used" topic="items/max" metaData={"one":"true", "zero":"false"} ; Group: id="min-group" ; Group: id="max-group" groups=["min-group"] items=["min-item", "max-item"] aggregation="AND" ("one", "two") ; ThingType: id="min-thing-type" ; diff --git a/eraser.rest/.gitignore b/eraser.rest/.gitignore deleted file mode 100644 index 70b583e34c3316bcd77c807e2d6b85db5e7d49f6..0000000000000000000000000000000000000000 --- a/eraser.rest/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/build/ -/bin/ -logs/ diff --git a/eraser.rest/build.gradle b/eraser.rest/build.gradle deleted file mode 100644 index 0ac26261047eacbcc2b97709e7a5b4c1bc927b3b..0000000000000000000000000000000000000000 --- a/eraser.rest/build.gradle +++ /dev/null @@ -1,28 +0,0 @@ -buildscript { - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:2.1.2.RELEASE") - } -} - -plugins { - id 'io.franzbecker.gradle-lombok' version '1.14' -} - -apply plugin: 'org.springframework.boot' -apply plugin: 'io.spring.dependency-management' - -dependencies { - compile project(':eraser-base') - compile 'org.springframework.boot:spring-boot-starter-web' - compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.9.2' - compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2' - testCompile 'org.springframework.boot:spring-boot-starter-test' -} - -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} diff --git a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/Activity.java b/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/Activity.java deleted file mode 100644 index 56da46d31f83e86c1bcd39446305d9588232bd1e..0000000000000000000000000000000000000000 --- a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/Activity.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.tudresden.inf.st.eraser.rest; - -import io.swagger.annotations.ApiModelProperty; -import lombok.Data; - -/** - * A recognized activity resource. - * - * @author rschoene - Initial contribution - */ -@Data(staticConstructor = "of") -public class Activity { - @ApiModelProperty(notes = "Some identifier of this activity") - public final int identifier; - @ApiModelProperty(notes = "Name of the activity") - public final String description; -} diff --git a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/ActivityController.java b/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/ActivityController.java deleted file mode 100644 index 0766b2bb963a0fc0dd66a61f4937dcd844fc3e04..0000000000000000000000000000000000000000 --- a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/ActivityController.java +++ /dev/null @@ -1,58 +0,0 @@ -package de.tudresden.inf.st.eraser.rest; - -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Spring boot controller specifying routes to REST API for activities. - * - * @author rschoene - Initial contribution - */ -@RestController -@RequestMapping("/activity") -@Api(value = "activity-data", description = "Activity data") -public class ActivityController { - - private List<Activity> dummyListOfActivities = new ArrayList<>(); - private Map<Integer, Activity> dummyMapOfActivities = new HashMap<>(); - private int currentActivityIndex = 0; - - public ActivityController() { - add(Activity.of(1, "Sitting in armchair")); - add(Activity.of(2, "Going to sleep")); - add(Activity.of(3, "Entering house")); - } - - public void add(Activity activity) { - dummyListOfActivities.add(activity); - dummyMapOfActivities.put(activity.getIdentifier(), activity); - } - - @ApiOperation(value = "Get all events in long form", response = List.class) - @GetMapping(value = "", produces = "application/json") - public List<Activity> getAllActivities() { - return dummyListOfActivities; - } - - @ApiOperation(value = "Get detailed information of one event", response = Activity.class) - @GetMapping(value = "/current", produces = "application/json") - public Activity getCurrentActivity() { - return dummyListOfActivities.get(currentActivityIndex); - } - - @ApiOperation(value = "Get detailed information of one event", response = Activity.class) - @GetMapping(value = "/{identifier}", produces = "application/json") - //@RequestParam(value = "identifier") - public Activity getActivity(@PathVariable int identifier) { - return dummyMapOfActivities.get(identifier); - } -} diff --git a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/Application.java b/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/Application.java deleted file mode 100644 index 66ca9dc3e5166cf98384d0a5b7149b9bad6b4553..0000000000000000000000000000000000000000 --- a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/Application.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.tudresden.inf.st.eraser.rest; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * The main class to start the rest service. - * - * @author rschoene - Initial contribution - */ -@SpringBootApplication -public class Application { - - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } -} diff --git a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/ChangedItem.java b/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/ChangedItem.java deleted file mode 100644 index 09f06872fe6010f7b1e98a49139ee2a3097c6a9e..0000000000000000000000000000000000000000 --- a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/ChangedItem.java +++ /dev/null @@ -1,19 +0,0 @@ -package de.tudresden.inf.st.eraser.rest; - -import io.swagger.annotations.ApiModelProperty; -import lombok.Data; - -/** - * One changed item and its new state. - * - * @author rschoene - Initial contribution - */ -@Data -public class ChangedItem { - @ApiModelProperty(notes = "The name of the changed item") - public final String name; - @ApiModelProperty(notes = "The new state of the item") - public final Object state; - @ApiModelProperty(notes = "The label of the changed item") - public final String label; -} diff --git a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/Event.java b/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/Event.java deleted file mode 100644 index dda21e0c73d85ad386ba4c7d375e0335bfd8d3d2..0000000000000000000000000000000000000000 --- a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/Event.java +++ /dev/null @@ -1,23 +0,0 @@ -package de.tudresden.inf.st.eraser.rest; - -import io.swagger.annotations.ApiModelProperty; -import lombok.AllArgsConstructor; -import lombok.Data; - -import java.util.List; - -/** - * Some system change event. - * - * @author rschoene - Initial contribution - */ -@Data -@AllArgsConstructor -public abstract class Event { - @ApiModelProperty(notes = "Time when this event happened") - public final long timestamp; - @ApiModelProperty(notes = "Some identifier for the event") - public final int identifier; - @ApiModelProperty(notes = "A list of items changed due to this event") - public final List<ChangedItem> changedItems; -} diff --git a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/EventController.java b/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/EventController.java deleted file mode 100644 index 5b6407998969b53f0a57c6de73986bb7fedd4903..0000000000000000000000000000000000000000 --- a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/EventController.java +++ /dev/null @@ -1,55 +0,0 @@ -package de.tudresden.inf.st.eraser.rest; - -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.*; - -/** - * Spring boot controller specifying routes to REST API for events. - * - * @author rschoene - Initial contribution - */ -@RestController("events") -@RequestMapping("/event") -@Api(value = "event-data", description = "Recent events (recognitions or manual changes)") -public class EventController { - - private List<Event> dummyListOfEvents = new ArrayList<>(); - private Map<Integer, Event> dummyMapOfEvents = new HashMap<>(); - - public EventController() { - add(new RecognitionEvent(1547637740, 1, Arrays.asList( - new ChangedItem("iris1", "green", "Hue Iris 1"), - new ChangedItem("go1", "green", "Hue Go 1")), 1, "Sitting in armchair")); - add(new RecognitionEvent(1547637750, 2, Collections.emptyList(), - 1, "Sitting in armchair")); - add(new RecognitionEvent(1547623460, 4, Collections.singletonList( - new ChangedItem("go2", "off", "Hue Go 2")), 1, "Going to sleep")); - add(new ManualChangeEvent(1501146256, 5, Arrays.asList( - new ChangedItem("iris1", "green", "Hue Iris 1"), - new ChangedItem("go1", "red", "Hue Go 1"), - new ChangedItem("go2", "#EE7F00", "Hue Go 2")))); - } - - public void add(Event event) { - dummyListOfEvents.add(event); - dummyMapOfEvents.put(event.getIdentifier(), event); - } - - @ApiOperation(value = "Get all events in long form", response = List.class) - @GetMapping(value = "", produces = "application/json") - public List<Event> getAllEvents() { - return dummyListOfEvents; - } - - @ApiOperation(value = "Get detailed information of one event", response = Event.class) - @GetMapping(value = "/{identifier}", produces = "application/json") - public Event getEvent(@PathVariable int identifier) { - return dummyMapOfEvents.get(identifier); - } -} diff --git a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/ManualChangeEvent.java b/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/ManualChangeEvent.java deleted file mode 100644 index 69bf77692f9d1a013d092d8c661c0d1633f55f69..0000000000000000000000000000000000000000 --- a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/ManualChangeEvent.java +++ /dev/null @@ -1,23 +0,0 @@ -package de.tudresden.inf.st.eraser.rest; - -import io.swagger.annotations.ApiModelProperty; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.util.List; - -/** - * Manual change of items in the system by an user. - * - * @author rschoene - Initial contribution - */ -@EqualsAndHashCode(callSuper = true) -@Data -public class ManualChangeEvent extends Event { - @ApiModelProperty(notes = "The type of the event") - public final String type = "manual"; - - public ManualChangeEvent(long timestamp, int identifier, List<ChangedItem> changedItems) { - super(timestamp, identifier, changedItems); - } -} diff --git a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/RecognitionEvent.java b/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/RecognitionEvent.java deleted file mode 100644 index 24ed98e01585504300cec39273ec06145cbc9350..0000000000000000000000000000000000000000 --- a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/RecognitionEvent.java +++ /dev/null @@ -1,30 +0,0 @@ -package de.tudresden.inf.st.eraser.rest; - -import io.swagger.annotations.ApiModelProperty; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.util.List; - -/** - * Change of items automatically done by the system. - * - * @author rschoene - Initial contribution - */ -@EqualsAndHashCode(callSuper = true) -@Data -public class RecognitionEvent extends Event { - @ApiModelProperty(notes = "The identifier of the activity causing this event") - public final int activity; - @ApiModelProperty(notes = "The description of the activity") - public final String description; - @ApiModelProperty(notes = "The type of the event") - public final String type = "recognition"; - - public RecognitionEvent(long timestamp, int identifier, List<ChangedItem> changedItems, int activity, String description) { - super(timestamp, identifier, changedItems); - this.activity = activity; - this.description = description; - } - -} diff --git a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/SwaggerConfig.java b/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/SwaggerConfig.java deleted file mode 100644 index fff9d9d36e1e966784f289efb91e9a92dcd82c43..0000000000000000000000000000000000000000 --- a/eraser.rest/src/main/java/de/tudresden/inf/st/eraser/rest/SwaggerConfig.java +++ /dev/null @@ -1,46 +0,0 @@ -package de.tudresden.inf.st.eraser.rest; - -import com.google.common.base.Predicates; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import springfox.documentation.builders.ApiInfoBuilder; -import springfox.documentation.builders.PathSelectors; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.service.ApiInfo; -import springfox.documentation.service.Contact; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger2.annotations.EnableSwagger2; - -/** - * Configuration class to enable swagger. - * - * @author rschoene - Initial contribution - */ -@Configuration -@EnableSwagger2 -public class SwaggerConfig { - @SuppressWarnings("Guava") - @Bean - public Docket api() { - return new Docket(DocumentationType.SWAGGER_2) - .select() - .apis(Predicates.not(RequestHandlerSelectors.basePackage("org.springframework"))) - .paths(PathSelectors.any()) - .build() - .apiInfo(metaData()); - } - - private ApiInfo metaData() { - return new ApiInfoBuilder() - .title("OpenLicht Knowledge-Base REST API") - .description("\"OpenLicht-REST-Server to get recent recognitions, manual settings and activities\"") - .version("1.0.0") - .license("MIT License") - .licenseUrl("https://opensource.org/licenses/MIT") - .contact(new Contact("René Schöne", - "http://tu-dresden.de/die_tu_dresden/fakultaeten/fakultaet_informatik/smt/st/mitarbeiter?person=375", - "rene.schoene@tu-dresden.de")) - .build(); - } -} diff --git a/eraser.spark/build.gradle b/eraser.spark/build.gradle index 5a577d15400aab9d58ac2fec4fae8589b0995020..127435058c97e8edd94d75fc6f3cfd07720d15cf 100644 --- a/eraser.spark/build.gradle +++ b/eraser.spark/build.gradle @@ -1,23 +1,12 @@ plugins { - id 'application' - id 'io.franzbecker.gradle-lombok' version '3.0.0' + id 'eraser.java-application-conventions' + id 'io.franzbecker.gradle-lombok' version "${gradle_lombok_version}" } dependencies { - compile project(':eraser-base') - compile group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: '2.11.2' - compile group: 'com.sparkjava', name: 'spark-core', version: '2.9.0' + implementation project(':eraser-base') + implementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: "${log4j_version}" + implementation group: 'com.sparkjava', name: 'spark-core', version: '2.9.3' } -run { - mainClassName = 'de.tudresden.inf.st.eraser.spark.Application' - standardInput = System.in -} - -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} +application.mainClass = 'de.tudresden.inf.st.eraser.spark.Application' diff --git a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/Application.java b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/Application.java index f79a2df3d49fbc0fe882a356526e63eb6aa23c52..1e793fb89c91b7b5b496fa027825ae2a634d7602 100644 --- a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/Application.java +++ b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/Application.java @@ -1,5 +1,6 @@ package de.tudresden.inf.st.eraser.spark; +import beaver.Parser; import com.fasterxml.jackson.databind.ObjectMapper; import de.tudresden.inf.st.eraser.jastadd.model.*; import de.tudresden.inf.st.eraser.util.JavaUtils; @@ -10,6 +11,7 @@ import spark.Request; import spark.Response; import spark.Spark; +import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -110,10 +112,17 @@ public class Application { mapper::writeValueAsString); Spark.path("/:identifier", () -> { + //--- PUT /model/items/:identifier --- Spark.put("", (request, response) -> { - SmartHomeEntityModel SmartHomeEntityModel = root.getSmartHomeEntityModel(); - Item item = ParserUtils.parseItem(request.body()); - if (!SmartHomeEntityModel.resolveItem(item.getID()).isPresent()) { + SmartHomeEntityModel smartHomeEntityModel = root.getSmartHomeEntityModel(); + Item item; + try { + item = ParserUtils.parseItem(request.body()); + } catch (IllegalArgumentException | IOException | Parser.Exception e) { + logger.catching(e); + return makeError(response, 400, "Could not create item. Error message: " + e.getMessage()); + } + if (!smartHomeEntityModel.resolveItem(item.getID()).isPresent()) { root.getSmartHomeEntityModel().addNewItem(item); response.status(201); return "OK"; @@ -151,6 +160,15 @@ public class Application { }); }); + Spark.put("/feedback/:identifier/newDefault", + (request, response) -> { + logger.info("request body: '{}', params: '{}', length={}", request.body(), request.params(), request.contentLength()); + return safeItemRoute(request, response, item -> { + logger.info("Would set for {} new default '{}'", item.getID(), request.body()); + return "OK"; + }); + }); + //--- POST /system/exit --- Spark.post("/system/exit", (request, response) -> { try { @@ -223,16 +241,14 @@ public class Application { return SimpleItem.of(item.getID(), item.getLabel(), item.getTopic() != null ? item.getTopic().getTopicString() : null, - item.isFrozen(), item.isSendState(), - item.getControllingList().stream().map(Item::getID).collect(Collectors.toList()), - wrapMetaData(item.getMetaDataList())); + wrapMetaData(item.getMetaData())); } - private Map<String, String> wrapMetaData(JastAddList<ItemMetaData> itemMetaDataList) { + private Map<String, String> wrapMetaData(MetaData metaData) { Map<String, String> result = new HashMap<>(); - for (ItemMetaData metaData : itemMetaDataList) { - result.put(metaData.getKey(), metaData.getValue()); + for (KeyValuePair keyValuePair : metaData.getKeyValuePairList()) { + result.put(keyValuePair.getKey(), keyValuePair.getValue()); } return result; } diff --git a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleChangeEvent.java b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleChangeEvent.java index 6a1c1a91bc3d8cdc28d44b07b766168e645786bf..740719aafa7bf85e918377a06c8ae09807234853 100644 --- a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleChangeEvent.java +++ b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleChangeEvent.java @@ -14,7 +14,7 @@ import java.util.List; @Data @AllArgsConstructor public abstract class SimpleChangeEvent { - public final Instant created; + public final long timestamp; public final int identifier; public final List<SimpleChangedItem> changed_items; } diff --git a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleItem.java b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleItem.java index 2eac624367215c547ef4cfe0058fdcce337062fa..dd55f9b4dee0710b98865e2109d17ffbc48bcc37 100644 --- a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleItem.java +++ b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleItem.java @@ -15,8 +15,6 @@ public class SimpleItem { public final String ID; public final String label; public final String topic; - public final boolean frozen; public final boolean sendState; - public final List<String> controlling; public final Map<String, String> metaData; } diff --git a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleManualChangeEvent.java b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleManualChangeEvent.java index 685436cd376b993403db98d4a6192b4de94975d5..a21bd562b206a8df58e26339e2e0d0e2e7f59912 100644 --- a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleManualChangeEvent.java +++ b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleManualChangeEvent.java @@ -20,7 +20,7 @@ public class SimpleManualChangeEvent extends SimpleChangeEvent { public final String type = "manual"; public SimpleManualChangeEvent(Instant created, int identifier, List<SimpleChangedItem> changedItems) { - super(created, identifier, changedItems); + super(created.getEpochSecond(), identifier, changedItems); } static SimpleManualChangeEvent createFrom(ManualChangeEvent event) { diff --git a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleRecognitionEvent.java b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleRecognitionEvent.java index 0a3821e4fc49d9fa2b2d0e75717da5477c0a3fa7..80955665a46357984e4d1df3b616e94005665a1c 100644 --- a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleRecognitionEvent.java +++ b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/SimpleRecognitionEvent.java @@ -19,17 +19,23 @@ import java.util.stream.Collectors; public class SimpleRecognitionEvent extends SimpleChangeEvent { public final int activity; public final String description; + public final List<SimpleChangedItem> relevant_items; public final String type = "recognition"; - public SimpleRecognitionEvent(Instant created, int identifier, List<SimpleChangedItem> changedItems, int activity, String description) { - super(created, identifier, changedItems); + public SimpleRecognitionEvent(Instant created, int identifier, List<SimpleChangedItem> changedItems, List<SimpleChangedItem> relevantItems, int activity, String description) { + super(created.getEpochSecond(), identifier, changedItems); this.activity = activity; this.description = description; + this.relevant_items = relevantItems; } static SimpleRecognitionEvent createFrom(RecognitionEvent event) { return new SimpleRecognitionEvent(event.getCreated(), event.getIdentifier(), - JavaUtils.toStream(event.getChangedItems()).map(SimpleChangedItem::createFrom).collect(Collectors.toList()), + JavaUtils.toStream(event.getChangedItems()) + .filter(changedItem -> !changedItem.getItem().getID().isEmpty()) + .map(SimpleChangedItem::createFrom) + .collect(Collectors.toList()), + JavaUtils.toStream(event.getRelevantItems()).map(SimpleChangedItem::createFrom).collect(Collectors.toList()), event.getActivity().getIdentifier(), event.getActivity().getLabel()); } diff --git a/eraser.starter/build.gradle b/eraser.starter/build.gradle index 186e75a1e448ab9f0e61c5f8df2d3ac94da2f1f9..26a0364217deda509bf80593c9f10f976d84e696 100644 --- a/eraser.starter/build.gradle +++ b/eraser.starter/build.gradle @@ -1,37 +1,51 @@ plugins { - id 'java' - id 'application' + id 'eraser.java-application-conventions' id 'distribution' id 'io.github.http-builder-ng.http-plugin' version '0.1.1' } -repositories { - mavenCentral() +dependencies { + implementation project(':eraser-base') + implementation project(':eraser.spark') + implementation project(':feedbackloop.api') + implementation project(':feedbackloop.analyze') + implementation project(':feedbackloop.plan') + implementation project(':feedbackloop.execute') + implementation project(':feedbackloop.learner_backup') + implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: "${jackson_version}" + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: "${jackson_version}" + implementation group: 'net.sourceforge.argparse4j', name: 'argparse4j', version: '0.8.1' + // implementation project(':feedbackloop.learner') + } -sourceCompatibility = 1.8 +def versionFile = 'src/main/resources/eraser.properties' +def oldProps = new Properties() -dependencies { - compile project(':eraser-base') - compile project(':eraser.spark') - compile project(':feedbackloop.api') - compile project(':feedbackloop.analyze') - compile project(':feedbackloop.plan') - compile project(':feedbackloop.execute') - compile project(':feedbackloop.learner_backup') - compile project(':datasets') - compile group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.9.8' - compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.8' - compile group: 'net.sourceforge.argparse4j', name: 'argparse4j', version: '0.8.1' - // compile project(':feedbackloop.learner') +try { + file(versionFile).withInputStream { stream -> oldProps.load(stream) } + version = oldProps['version'] +} catch (e) { + // this happens, if either the properties file is not present, or cannot be read from + throw new GradleException("File ${versionFile} not found or unreadable. Aborting.", e) +} +task printVersion() { + doLast { + println(version) + } } -run { - mainClassName = 'de.tudresden.inf.st.eraser.starter.EraserStarter' - standardInput = System.in +task newVersion() { + doFirst { + def props = new Properties() + props['version'] = value + props.store(file(versionFile).newWriter(), null) + } } +application.mainClass = 'de.tudresden.inf.st.eraser.starter.EraserStarter' + import io.github.httpbuilderng.http.HttpTask task shutdown (type: HttpTask) { @@ -46,23 +60,6 @@ task shutdown (type: HttpTask) { } } -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} - -//distributions { -// main { -// contents { -// from { -// 'src/main/resources/starter.eraser' -// } -// } -// } -//} applicationDistribution.from("src/main/resources") { include "starter.eraser" } diff --git a/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/DummyMachineLearningHandlerFactory.java b/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/DummyMachineLearningHandlerFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..f72cda5f2fc2c1fa4b2016d62f29b70925419c09 --- /dev/null +++ b/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/DummyMachineLearningHandlerFactory.java @@ -0,0 +1,37 @@ +package de.tudresden.inf.st.eraser.starter; + +import de.tudresden.inf.st.eraser.jastadd.model.*; + +import java.net.URL; + +/** + * Factory to create dummy handlers. + * + * @author rschoene - Initial contribution + */ +public class DummyMachineLearningHandlerFactory extends MachineLearningHandlerFactory { + + private final DummyMachineLearningModel model = DummyMachineLearningModel.createDefault(); + + @Override + public void initializeFor(MachineLearningHandlerFactoryTarget target, URL configUrl) { + // not needed + } + + @Override + public MachineLearningEncoder createEncoder() { + // not needed + return null; + } + + @Override + public MachineLearningDecoder createDecoder() { + // not needed + return null; + } + + @Override + public MachineLearningModel createModel() { + return model; + } +} diff --git a/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/EraserStarter.java b/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/EraserStarter.java index 4d171f8b53d707cca9c07d12721a6586fbfaaf66..83815a11615b3b2c1c8f851772c9a84208e4f316 100644 --- a/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/EraserStarter.java +++ b/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/EraserStarter.java @@ -1,6 +1,7 @@ package de.tudresden.inf.st.eraser.starter; import beaver.Parser; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import de.tudresden.inf.st.eraser.feedbackloop.analyze.AnalyzeImpl; @@ -8,8 +9,6 @@ import de.tudresden.inf.st.eraser.feedbackloop.api.Analyze; import de.tudresden.inf.st.eraser.feedbackloop.api.Execute; import de.tudresden.inf.st.eraser.feedbackloop.api.Plan; import de.tudresden.inf.st.eraser.feedbackloop.execute.ExecuteImpl; -import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.Learner; -import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.MachineLearningImpl; import de.tudresden.inf.st.eraser.feedbackloop.plan.PlanImpl; import de.tudresden.inf.st.eraser.jastadd.model.*; import de.tudresden.inf.st.eraser.openhab2.OpenHab2Importer; @@ -25,15 +24,19 @@ import org.apache.logging.log4j.Logger; import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; -import java.util.ArrayList; -import java.util.List; +import java.util.MissingResourceException; +import java.util.ResourceBundle; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import static de.tudresden.inf.st.eraser.jastadd.model.MachineLearningHandlerFactory.MachineLearningHandlerFactoryTarget.ACTIVITY_RECOGNITION; +import static de.tudresden.inf.st.eraser.jastadd.model.MachineLearningHandlerFactory.MachineLearningHandlerFactoryTarget.PREFERENCE_LEARNING; + /** * This Starter combines and starts all modules. This includes: * @@ -52,8 +55,12 @@ public class EraserStarter { private static final Logger logger = LogManager.getLogger(EraserStarter.class); - @SuppressWarnings("ResultOfMethodCallIgnored") public static void main(String[] args) { + EraserStarter starter = new EraserStarter(); + starter.run(args); + } + + public void run(String[] args) { ArgumentParser parser = ArgumentParsers.newFor("eraser").build() .defaultHelp(true) .description("Starts the knowledge-base of OpenLicht"); @@ -68,6 +75,7 @@ public class EraserStarter { System.exit(1); } ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); File settingsFile = new File(commandLineOptions.configFile); Setting settings; try { @@ -78,7 +86,7 @@ public class EraserStarter { System.exit(1); return; } - logger.info("Starting ERASER"); + logger.info("Starting ERASER " + readVersion()); boolean startRest = settings.rest.use; Root root; @@ -101,7 +109,7 @@ public class EraserStarter { default: try { root = ParserUtils.load(settings.load.realURL()); - model = root.getSmartHomeEntityModel(); + root.getSmartHomeEntityModel(); } catch (IOException | Parser.Exception e) { logger.error("Problems parsing the given file {}", settings.load.file); logger.catching(e); @@ -110,101 +118,15 @@ public class EraserStarter { } } - // initialize backup learner - Learner learner = new Learner(); + MachineLearningHandlerFactory activityFactory = createFactory(ACTIVITY_RECOGNITION, settings.activity, root); + MachineLearningHandlerFactory preferenceFactory = createFactory(PREFERENCE_LEARNING, settings.preference, root); // initialize activity recognition MachineLearningRoot machineLearningRoot = root.getMachineLearningRoot(); - if (settings.activity.dummy) { - logger.info("Using dummy activity recognition, ignoring other settings for this"); - machineLearningRoot.setActivityRecognition(DummyMachineLearningModel.createDefault()); - } else { - MachineLearningImpl handler = new MachineLearningImpl(learner, MachineLearningImpl.GOAL_ACTIVITY_PHONE_AND_WATCH); - handler.setKnowledgeBaseRoot(root); - logger.info("Reading activity recognition from csv file {}", settings.activity.file); - handler.initActivities(settings.activity.realURL().getFile()); - ExternalMachineLearningModel machineLearningModel = new ExternalMachineLearningModel(); - machineLearningModel.setEncoder(handler); - machineLearningModel.setDecoder(handler); - root.getMachineLearningRoot().setActivityRecognition(machineLearningModel); + root.getMachineLearningRoot().setActivityRecognition(activityFactory.createModel()); + root.getMachineLearningRoot().setPreferenceLearning(preferenceFactory.createModel()); - //Begin the Integration - Item item1=model.resolveItem("m_accel_x").get(); - Item item2=model.resolveItem("m_accel_y").get(); - Item item3=model.resolveItem("m_accel_z").get(); - Item item4=model.resolveItem("m_rotation_x").get(); - Item item5=model.resolveItem("m_rotation_y").get(); - Item item6=model.resolveItem("m_rotation_z").get(); - Item item7=model.resolveItem("w_accel_x").get(); - Item item8=model.resolveItem("w_accel_y").get(); - Item item9=model.resolveItem("w_accel_z").get(); - Item item10=model.resolveItem("w_rotation_x").get(); - Item item11=model.resolveItem("w_rotation_y").get(); - Item item12=model.resolveItem("w_rotation_z").get(); - //0.2717419,8.698134,4.471172,0.043741,0.515962,0.854318,1.8818425,4.9320555,8.145074,0.2374878,-0.032836914,0.3381958,working - item1.setStateFromString("0.2717419"); - item2.setStateFromString("8.698134"); - item3.setStateFromString("4.471172"); - item4.setStateFromString("0.043741"); - item5.setStateFromString("0.515962"); - item6.setStateFromString("0.854318"); - item7.setStateFromString("1.8818425"); - item8.setStateFromString("4.9320555"); - item9.setStateFromString("8.145074"); - item10.setStateFromString("0.2374878"); - item11.setStateFromString("-0.032836914"); - item12.setStateFromString("0.3381958"); - - ArrayList<Item> newData=new ArrayList<>(); - newData.add(item1); - newData.add(item2); - newData.add(item3); - newData.add(item4); - newData.add(item5); - newData.add(item6); - newData.add(item7); - newData.add(item8); - newData.add(item9); - newData.add(item10); - newData.add(item11); - newData.add(item12); - handler.newData(newData); - List<ItemPreference> preference=handler.classify().getPreferences(); - for(ItemPreference preference1 : preference){ - preference1.apply(); - } - } - - // initialize preference learning - if (settings.preference.dummy) { - logger.info("Using dummy preference learning, ignoring other settings for this"); - machineLearningRoot.setPreferenceLearning(DummyMachineLearningModel.createDefault()); - } else { - logger.info("Reading preference learning from csv file {}", settings.preference.file); - MachineLearningImpl handler = new MachineLearningImpl(learner, MachineLearningImpl.GOAL_PREFERENCE_BRIGHTNESS_IRIS); - handler.setKnowledgeBaseRoot(root); - handler.initPreferences(settings.preference.realURL().getFile()); - ExternalMachineLearningModel machineLearningModel = new ExternalMachineLearningModel(); - machineLearningModel.setEncoder(handler); - machineLearningModel.setDecoder(handler); - root.getMachineLearningRoot().setPreferenceLearning(machineLearningModel); - //working,medium,240,70 - Item activity_item = model.resolveItem("activity").get(); - String activity=activity_item.getStateAsString(); - activity_item.setStateFromString(activity); - Item brightness_item = model.resolveItem("w_brightness").get(); - brightness_item.setStateFromString("medium"); - ArrayList<Item> newData1 = new ArrayList<>(); - newData1.add(activity_item); - newData1.add(brightness_item); - handler.newData(newData1); - List<ItemPreference> preference=handler.classify().getPreferences(); - for(ItemPreference preference1 : preference){ - preference1.apply(); - } - } - - machineLearningRoot.getPreferenceLearning().connectItems(settings.preference.items); +// machineLearningRoot.getPreferenceLearning().connectItems(settings.preference.items); if (!machineLearningRoot.getActivityRecognition().check()) { logger.fatal("Invalid activity recognition!"); System.exit(1); @@ -279,6 +201,7 @@ public class EraserStarter { logger.info("No REST server this time"); System.out.println("Hit [Enter] to exit"); try { + //noinspection ResultOfMethodCallIgnored System.in.read(); } catch (IOException e) { e.printStackTrace(); @@ -294,6 +217,8 @@ public class EraserStarter { if (analyze != null) { analyze.stop(); } + activityFactory.shutdown(); + preferenceFactory.shutdown(); InfluxAdapter influxAdapter = root.getInfluxRoot().influxAdapter(); if (influxAdapter != null) { try { @@ -304,4 +229,44 @@ public class EraserStarter { } logger.info("I'm done here."); } + + private MachineLearningHandlerFactory createFactory(MachineLearningHandlerFactory.MachineLearningHandlerFactoryTarget target, Setting.MLContainer config, Root root) { + MachineLearningHandlerFactory factory; + String niceTargetName = target.toString().toLowerCase().replace("_", " "); + if (config.dummy || config.factory == null) { + logger.info("Using dummy {}, ignoring other settings for this", niceTargetName); + factory = new DummyMachineLearningHandlerFactory(); + } else { + try { + Class<? extends MachineLearningHandlerFactory> clazz = Class.forName(config.factory) + .asSubclass(MachineLearningHandlerFactory.class); + factory = clazz.getDeclaredConstructor().newInstance(); + factory.setKnowledgeBaseRoot(root); + factory.initializeFor(target, config.realURL()); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IOException | + NoSuchMethodException | InvocationTargetException e) { + logger.error("Could not instantiate machine learning factory for {} with class '{}'.", + niceTargetName, config); + logger.catching(e); + if (config.abortOnError) { + logger.fatal("Aborting now as specified in config."); + System.exit(1); + return null; + } + logger.warn("Using error factory for {}.", niceTargetName); + factory = MachineLearningHandlerFactory.createErrorFactory(); + } + } + return factory; + } + + private String readVersion() { + try { + ResourceBundle resources = ResourceBundle.getBundle("eraser"); + return resources.getString("version"); + } catch (MissingResourceException e) { + return "version ?"; + } + } + } diff --git a/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/Setting.java b/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/Setting.java index 09af126b0a66f54aa2b6c88fb04bda9cb7218cb3..cd157af8c44065f10386ea48765da6e0d64c450b 100644 --- a/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/Setting.java +++ b/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/Setting.java @@ -6,7 +6,6 @@ import org.apache.logging.log4j.Logger; import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Paths; -import java.util.List; /** * Setting bean. @@ -43,14 +42,13 @@ class Setting { } } public class MLContainer extends FileContainer { + /** Factory class to instantiate machine learning handlers */ + public String factory; /** Use dummy model in which the current activity is directly editable. Default: false. */ public boolean dummy = false; /** Model id. Default: 1.*/ public int id = 1; - /** Items to connect to inputs */ - public List<String> items; - /** Item to change with classification result */ - public String affectedItem; + public boolean abortOnError = false; } public class OpenHabContainer { /** The URL from which to import and at which openHAB is running */ diff --git a/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/TestMachineLearningHandlerFactory.java b/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/TestMachineLearningHandlerFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..d6946f471d0ed94f7b2c979f3faa7c44c77775d6 --- /dev/null +++ b/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/TestMachineLearningHandlerFactory.java @@ -0,0 +1,68 @@ +package de.tudresden.inf.st.eraser.starter; + +import de.tudresden.inf.st.eraser.jastadd.model.*; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Properties; + +/** + * Testing preference setting + * + * @author rschoene - Initial contribution + */ +public class TestMachineLearningHandlerFactory extends MachineLearningHandlerFactory { + + private static final Logger logger = LogManager.getLogger(TestMachineLearningHandlerFactory.class); + private double offset = 0; + private double multiplier = 1; + + @Override + public void initializeFor(MachineLearningHandlerFactoryTarget target, URL configUrl) { + if (target != MachineLearningHandlerFactoryTarget.PREFERENCE_LEARNING) { + logger.error("Test Factory only made for preference learning, not {}", target); + } + // treat given file as properties file + try (InputStream input = configUrl.openStream()) { + Properties prop = new Properties(); + prop.load(input); + offset = Double.parseDouble(prop.getProperty("offset", "0")); + multiplier = Double.parseDouble(prop.getProperty("multiplier", "1")); + } catch (IOException | IllegalArgumentException e) { + logger.catching(e); + } + } + + @Override + public MachineLearningEncoder createEncoder() { + return null; + } + + @Override + public MachineLearningDecoder createDecoder() { + return null; + } + + @Override + public MachineLearningModel createModel() { + DummyMachineLearningModel result = DummyMachineLearningModel.createDefault(); + for (Item item : knowledgeBase.getSmartHomeEntityModel().items()) { + if (item.isColorItem()) { + ItemUpdateDouble update = new ItemUpdateDouble() { + // dirty hack to override value based on activity during runtime + @Override + public double getNewValue() { + Activity activity = knowledgeBase.currentActivity().orElse(knowledgeBase.getMachineLearningRoot().getActivity(0)); + return offset + activity.getIdentifier() * multiplier; + } + }; + update.setItem(item); + result.getCurrent().addPreference(update); + } + } + return result; + } +} diff --git a/eraser.starter/src/main/resources/activity_data.csv b/eraser.starter/src/main/resources/activity_data.csv new file mode 120000 index 0000000000000000000000000000000000000000..b758a4eaa4e22197d06dffbc2b4f9ec71975864c --- /dev/null +++ b/eraser.starter/src/main/resources/activity_data.csv @@ -0,0 +1 @@ +../../../../feedbackloop.learner_backup/src/test/resources/activity_data.csv \ No newline at end of file diff --git a/eraser.starter/src/main/resources/activity_definition.json b/eraser.starter/src/main/resources/activity_definition.json new file mode 120000 index 0000000000000000000000000000000000000000..9a98b24cb3f4613dd3c12d3b925cc910bb8e2649 --- /dev/null +++ b/eraser.starter/src/main/resources/activity_definition.json @@ -0,0 +1 @@ +../../../../feedbackloop.learner_backup/src/main/resources/activity_definition.json \ No newline at end of file diff --git a/eraser.starter/src/main/resources/activity_network.eg b/eraser.starter/src/main/resources/activity_network.eg new file mode 120000 index 0000000000000000000000000000000000000000..3c37b3277cf3e3c02e142185a6290957e89ff3f2 --- /dev/null +++ b/eraser.starter/src/main/resources/activity_network.eg @@ -0,0 +1 @@ +../../../../feedbackloop.learner_backup/src/main/resources/activity_network.eg \ No newline at end of file diff --git a/eraser.starter/src/main/resources/activity_normalizer.json b/eraser.starter/src/main/resources/activity_normalizer.json new file mode 120000 index 0000000000000000000000000000000000000000..edc204d0a71571d0d0ebd00dce92fe086790022a --- /dev/null +++ b/eraser.starter/src/main/resources/activity_normalizer.json @@ -0,0 +1 @@ +../../../../feedbackloop.learner_backup/src/main/resources/activity_normalizer.json \ No newline at end of file diff --git a/eraser.starter/src/main/resources/eraser.properties b/eraser.starter/src/main/resources/eraser.properties new file mode 100644 index 0000000000000000000000000000000000000000..8066d3403a20424c785a5cb8c8df599d7a449db8 --- /dev/null +++ b/eraser.starter/src/main/resources/eraser.properties @@ -0,0 +1,2 @@ +#Sun Jun 20 11:43:25 CEST 2021 +version=1.0.0 diff --git a/eraser.starter/src/main/resources/loaded_learner_activity_phone_and_watch.json b/eraser.starter/src/main/resources/loaded_learner_activity_phone_and_watch.json new file mode 120000 index 0000000000000000000000000000000000000000..152f4382ec5290554625453e65d69a6daf85f670 --- /dev/null +++ b/eraser.starter/src/main/resources/loaded_learner_activity_phone_and_watch.json @@ -0,0 +1 @@ +../../../../feedbackloop.learner_backup/src/main/resources/loaded_learner_activity_phone_and_watch.json \ No newline at end of file diff --git a/eraser.starter/src/main/resources/loaded_learner_preferences_brightness_iris.json b/eraser.starter/src/main/resources/loaded_learner_preferences_brightness_iris.json new file mode 120000 index 0000000000000000000000000000000000000000..154e90403181cb5c1f39ccd3d64d022027941edf --- /dev/null +++ b/eraser.starter/src/main/resources/loaded_learner_preferences_brightness_iris.json @@ -0,0 +1 @@ +../../../../feedbackloop.learner_backup/src/main/resources/loaded_learner_preferences_brightness_iris.json \ No newline at end of file diff --git a/eraser.starter/src/main/resources/preference_definition.json b/eraser.starter/src/main/resources/preference_definition.json new file mode 120000 index 0000000000000000000000000000000000000000..a8dfc4259c79da2cc8b55991b99865e35db4aadc --- /dev/null +++ b/eraser.starter/src/main/resources/preference_definition.json @@ -0,0 +1 @@ +../../../../feedbackloop.learner_backup/src/main/resources/preference_definition.json \ No newline at end of file diff --git a/eraser.starter/src/main/resources/preference_network.eg b/eraser.starter/src/main/resources/preference_network.eg new file mode 120000 index 0000000000000000000000000000000000000000..c3ee89df72cc3a31eab2eca329051fff3bc82c0e --- /dev/null +++ b/eraser.starter/src/main/resources/preference_network.eg @@ -0,0 +1 @@ +../../../../feedbackloop.learner_backup/src/main/resources/preference_network.eg \ No newline at end of file diff --git a/eraser.starter/src/main/resources/preference_normalizer.json b/eraser.starter/src/main/resources/preference_normalizer.json new file mode 120000 index 0000000000000000000000000000000000000000..3ff4cdb3131f44077c83e8774f2e91d96deb1409 --- /dev/null +++ b/eraser.starter/src/main/resources/preference_normalizer.json @@ -0,0 +1 @@ +../../../../feedbackloop.learner_backup/src/main/resources/preference_normalizer.json \ No newline at end of file diff --git a/eraser.starter/src/main/resources/starter.eraser b/eraser.starter/src/main/resources/starter.eraser index a8dc0c5731c9f5ce106c55aa4d2a87fc8f85df4f..42580e822d3cefe1463f96a7e8adf3cb4f4a8ce9 100644 --- a/eraser.starter/src/main/resources/starter.eraser +++ b/eraser.starter/src/main/resources/starter.eraser @@ -1,4 +1,5 @@ Color Item: id="iris1_item" label="Iris 1" state="121,88,68" topic="iris1_item"; +Color Item: id="go1_item" label="Go 1" state="120,80,60" topic="go1_item"; Number Item: id="datetime_month" label="Month" state="1" topic="datetime_month"; Number Item: id="datetime_day" label="Day" state="31" topic="datetime_day"; Number Item: id="datetime_hour" label="Hour" state="13" topic="datetime_hour"; @@ -17,7 +18,7 @@ String Item: id="w_accel_y" label="" state="4.9320555" topic="w_accel_y"; String Item: id="w_accel_z" label="" state="8.145074" topic="w_accel_z"; String Item: id="w_rotation_x" label="" state="0.2374878" topic="w_rotation_x"; String Item: id="w_rotation_y" label="" state="-0.032836914" topic="w_rotation_y"; -String Item: id="w_rotation_z" label="" state="0.3381958" topic="w_rotation_y"; +String Item: id="w_rotation_z" label="" state="0.3381958" topic="w_rotation_z"; String Item: id="w_brightness" label="" state="bright" topic="w_brightness"; //String Item: id="activity" label="" state="lying" topic="activity"; Activity Item: id="activity" topic="activity"; @@ -28,7 +29,8 @@ Group: id="Datetime" items=["datetime_month", "datetime_day", "datetime_hour", " Mqtt: incoming="oh2/out/" outgoing="oh2/in/" host="localhost:1883" ; //Mqtt: incoming="oh2/out/" outgoing="oh2/in/" host="192.168.1.250" ; -Influx: host="172.22.1.152" ; +//Influx: host="172.22.1.152" ; + //"working", "walking", "dancing", "lying", "getting up", "reading" ML: activities={ 0: "working", diff --git a/eraser.starter/src/main/resources/testHandler.properties b/eraser.starter/src/main/resources/testHandler.properties new file mode 100644 index 0000000000000000000000000000000000000000..98a5f0e153b69773798b53d8a4b37aa9a5993ba6 --- /dev/null +++ b/eraser.starter/src/main/resources/testHandler.properties @@ -0,0 +1,2 @@ +offset=4 +multiplier=10 diff --git a/eraser.starter/starter-setting.yaml b/eraser.starter/starter-setting.yaml index e8f438cf3eeb22d71d44c675e7328e61ba612562..49909e91e4fe5c0e73e3a3f3ca8ffa175f496e72 100644 --- a/eraser.starter/starter-setting.yaml +++ b/eraser.starter/starter-setting.yaml @@ -24,31 +24,29 @@ load: # Model for activity recognition. If dummy is true, then the file parameter is ignored. activity: - # File to read in. Expected format = csv /Users/boqi/eraser/datasets - file: ../datasets/backup/activity_data.csv + factory: de.tudresden.inf.st.eraser.feedbackloop.learner_backup.MachineLearningHandlerFactoryImpl + # File to read in. Expected format depends on factory + file: src/main/resources/loaded_learner_activity_phone_and_watch.json external: true # Use dummy model in which the current activity is directly editable. Default: false. dummy: false # Model id. Default: 1. id: 1 - # Item to change with classification result - affectedItem: activity + abortOnError: true # Model for preference learning. If dummy is true, then the file parameter is ignored. preference: - # File to read in. Expected format = csv - file: ../datasets/backup/preference_data.csv +# factory: de.tudresden.inf.st.eraser.starter.TestMachineLearningHandlerFactory + factory: de.tudresden.inf.st.eraser.feedbackloop.learner_backup.MachineLearningHandlerFactoryImpl + # File to read in. Expected format depends on factory +# file: src/main/resources/testHandler.properties + file: src/main/resources/loaded_learner_preferences_brightness_iris.json external: true # Use dummy model in which the current activity is directly editable. Default: false. - dummy: false + dummy: true # Model id. Default: 1. id: 1 - # Items to connect to inputs - items: - - activity - - w_brightness - # Item to change with classification result - affectedItem: iris1_item + abortOnError: true # Initialize the knowledge base by importing data from openHAB. openhab: diff --git a/feedbackloop.analyze/build.gradle b/feedbackloop.analyze/build.gradle index 361f5c1429190208b8a70cbb889bf6c1cbd56993..6fb8173284f0268085bf53a8a0b6dcae071496d2 100644 --- a/feedbackloop.analyze/build.gradle +++ b/feedbackloop.analyze/build.gradle @@ -1,12 +1,8 @@ -dependencies { - compile project(':eraser-base') - compile project(':feedbackloop.api') +plugins { + id 'eraser.java-common-conventions' } -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } +dependencies { + implementation project(':eraser-base') + implementation project(':feedbackloop.api') } diff --git a/feedbackloop.analyze/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/analyze/AnalyzeImpl.java b/feedbackloop.analyze/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/analyze/AnalyzeImpl.java index 5e9f8cfb5476ae25219cebd704e17e2e2b78cb9d..eecdc8fb749c0bc4778b6aeba2c7b870a6529c02 100644 --- a/feedbackloop.analyze/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/analyze/AnalyzeImpl.java +++ b/feedbackloop.analyze/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/analyze/AnalyzeImpl.java @@ -3,6 +3,8 @@ package de.tudresden.inf.st.eraser.feedbackloop.analyze; import de.tudresden.inf.st.eraser.feedbackloop.api.Analyze; import de.tudresden.inf.st.eraser.feedbackloop.api.Plan; import de.tudresden.inf.st.eraser.jastadd.model.Activity; +import de.tudresden.inf.st.eraser.jastadd.model.ItemUpdate; +import de.tudresden.inf.st.eraser.jastadd.model.MachineLearningResult; import de.tudresden.inf.st.eraser.jastadd.model.Root; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -17,10 +19,12 @@ public class AnalyzeImpl implements Analyze { private Root knowledgeBase; private Plan plan; private Activity mostRecentActivity; + private MachineLearningResult mostRecentPreferences; private Logger logger = LogManager.getLogger(AnalyzeImpl.class); public AnalyzeImpl() { this.mostRecentActivity = null; + this.mostRecentPreferences = null; } @Override @@ -40,17 +44,32 @@ public class AnalyzeImpl implements Analyze { @Override public void analyzeLatestChanges() { + MachineLearningResult recognitionResult = knowledgeBase.getMachineLearningRoot().getActivityRecognition().classify(); + recognitionResult.getItemUpdates().forEach(ItemUpdate::apply); knowledgeBase.currentActivity().ifPresent(activity -> { + MachineLearningResult newMLResult = knowledgeBase.getMachineLearningRoot().getPreferenceLearning().classify(); + // check if activity has changed if (!activity.equals(mostRecentActivity)) { // new! inform plan! logger.info("Found new activity '{}'", activity.getLabel()); - mostRecentActivity = activity; try { informPlan(activity); } catch (Exception e) { logger.catching(e); } + } else { + // if no change, also check, if preferences have changed + if (!newMLResult.equals(mostRecentPreferences)) { + logger.info("Preferences have changed for same activity '{}'", activity.getLabel()); + try { + informPlan(activity); + } catch (Exception e) { + logger.catching(e); + } + } } + mostRecentActivity = activity; + mostRecentPreferences = newMLResult; }); } } diff --git a/feedbackloop.api/build.gradle b/feedbackloop.api/build.gradle index ba194086c7c858a3435af764a8783f38231d2c69..3e04a280611187df9be8e81ece074a9352dc2c13 100644 --- a/feedbackloop.api/build.gradle +++ b/feedbackloop.api/build.gradle @@ -1,17 +1,10 @@ plugins { - id 'io.franzbecker.gradle-lombok' version '3.0.0' + id 'eraser.java-common-conventions' + id 'io.franzbecker.gradle-lombok' version "${gradle_lombok_version}" } dependencies { - compile project(':eraser-base') + implementation project(':eraser-base') compile project(':commons.color') compile group: 'org.encog', name: 'encog-core', version: '3.4' } - -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} diff --git a/feedbackloop.api/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/api/Execute.java b/feedbackloop.api/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/api/Execute.java index c7ecb57d535b832d59d76d9840813a6d8a72ca2d..8e824e83a0f9b73331d9629a7aa06bb97114ea98 100644 --- a/feedbackloop.api/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/api/Execute.java +++ b/feedbackloop.api/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/api/Execute.java @@ -1,11 +1,10 @@ package de.tudresden.inf.st.eraser.feedbackloop.api; -import de.tudresden.inf.st.eraser.commons.color.ColorUtils.RGBvalues; -import de.tudresden.inf.st.eraser.jastadd.model.ItemPreference; +import de.tudresden.inf.st.eraser.commons.color.ColorUtils.ValuesRGB; +import de.tudresden.inf.st.eraser.jastadd.model.ItemUpdate; import de.tudresden.inf.st.eraser.jastadd.model.Root; import de.tudresden.inf.st.eraser.util.Tuple; -import java.util.List; import java.util.Map; /** @@ -22,15 +21,8 @@ public interface Execute { void setKnowledgeBase(Root knowledgeBase); /** - * <b>Deprecated</b>: Use {@link #updateItems(List)} instead. - * @param brightnessAndRgbForItems Map, keys are item names, values are RGB and brightness values + * Updates items according to given updates + * @param updates tuples containing item and its new HSB value */ - @Deprecated - void updateItems(Map<String, Tuple<Integer, RGBvalues>> brightnessAndRgbForItems); - - /** - * Updates items according to given preferences - * @param preferences tuples containing item and its new HSB value - */ - void updateItems(List<ItemPreference> preferences); + void updateItems(Iterable<ItemUpdate> updates); } diff --git a/feedbackloop.api/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/api/Learner.java b/feedbackloop.api/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/api/Learner.java index a08a6fc2a444b4a30fd4f234acfd4449c075d444..0d92fbbd0ef22abb47cc16c2ba568a0ca02116ec 100644 --- a/feedbackloop.api/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/api/Learner.java +++ b/feedbackloop.api/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/api/Learner.java @@ -118,9 +118,6 @@ public interface Learner { * */ EncogModel getTrainedModel(int modelID); - @Deprecated - EncogModel getTrainedModel(URL url, int modelID); - /** * * Method for getting normalizer of a model for a specific column/input. diff --git a/feedbackloop.api/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/api/Plan.java b/feedbackloop.api/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/api/Plan.java index 8c237d06d64684d6386e1bc8de5a73146d7a1485..5155a62bf8d2eebe012fdb7dbf6f478818d115a1 100644 --- a/feedbackloop.api/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/api/Plan.java +++ b/feedbackloop.api/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/api/Plan.java @@ -1,11 +1,9 @@ package de.tudresden.inf.st.eraser.feedbackloop.api; import de.tudresden.inf.st.eraser.jastadd.model.Activity; -import de.tudresden.inf.st.eraser.jastadd.model.ItemPreference; +import de.tudresden.inf.st.eraser.jastadd.model.ItemUpdate; import de.tudresden.inf.st.eraser.jastadd.model.Root; -import java.util.List; - /** * Third phase in the MAPE feedback loop, planning reconfiguration actions to adapt to recent changes. * @@ -21,7 +19,7 @@ public interface Plan { void planToMatchPreferences(Activity activity); - default void informExecute(List<ItemPreference> preferences) { - getExecute().updateItems(preferences); + default void informExecute(Iterable<ItemUpdate> updates) { + getExecute().updateItems(updates); } } diff --git a/feedbackloop.execute/build.gradle b/feedbackloop.execute/build.gradle index 361f5c1429190208b8a70cbb889bf6c1cbd56993..6fb8173284f0268085bf53a8a0b6dcae071496d2 100644 --- a/feedbackloop.execute/build.gradle +++ b/feedbackloop.execute/build.gradle @@ -1,12 +1,8 @@ -dependencies { - compile project(':eraser-base') - compile project(':feedbackloop.api') +plugins { + id 'eraser.java-common-conventions' } -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } +dependencies { + implementation project(':eraser-base') + implementation project(':feedbackloop.api') } diff --git a/feedbackloop.execute/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/execute/ExecuteImpl.java b/feedbackloop.execute/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/execute/ExecuteImpl.java index 489956ca9807110328b740b77fe4412e0f00fda3..fd2ab95d6792ea57ccae539c2ff6cc6f5d081d97 100644 --- a/feedbackloop.execute/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/execute/ExecuteImpl.java +++ b/feedbackloop.execute/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/execute/ExecuteImpl.java @@ -1,16 +1,8 @@ package de.tudresden.inf.st.eraser.feedbackloop.execute; -import de.tudresden.inf.st.eraser.commons.color.ColorUtils; -import de.tudresden.inf.st.eraser.commons.color.ColorUtils.HSBvalues255; -import de.tudresden.inf.st.eraser.commons.color.ColorUtils.RGBvalues; import de.tudresden.inf.st.eraser.feedbackloop.api.Execute; -import de.tudresden.inf.st.eraser.jastadd.model.*; -import de.tudresden.inf.st.eraser.util.Tuple; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.util.*; -import java.util.function.Consumer; +import de.tudresden.inf.st.eraser.jastadd.model.ItemUpdate; +import de.tudresden.inf.st.eraser.jastadd.model.Root; /** * Reference implementation for Execute. @@ -19,52 +11,17 @@ import java.util.function.Consumer; */ public class ExecuteImpl implements Execute { - private Logger logger = LogManager.getLogger(ExecuteImpl.class); - private Root knowledgeBase; +// private Root knowledgeBase; @Override public void setKnowledgeBase(Root knowledgeBase) { - this.knowledgeBase = knowledgeBase; - } - - @Override - public void updateItems(Map<String, Tuple<Integer, RGBvalues>> brightnessAndRgbForItems) { - List<ItemPreference> preferences = new ArrayList<>(); - for (Map.Entry<String, Tuple<Integer, RGBvalues>> entry : brightnessAndRgbForItems.entrySet()) { - String itemId = entry.getKey(); - resolveOrLogError(itemId, item -> { - if (entry.getValue() == null) { - return; - } - Integer brightness = entry.getValue().x; - RGBvalues rgb = entry.getValue().y; - HSBvalues255 hsb; - if (rgb != null) { - // also set rgb values - hsb = ColorUtils.convertRGBtoHSB(rgb).toIntegral(); - hsb.brightness = brightness; - } else { - hsb = HSBvalues255.of(0, 100, brightness); - } - hsb.ensureBounds(); - preferences.add(new ItemPreferenceColor(item, TupleHSB.of(hsb.hue, hsb.saturation, hsb.brightness))); - }); - } - updateItems(preferences); +// this.knowledgeBase = knowledgeBase; } @Override - public void updateItems(List<ItemPreference> preferences) { - for (ItemPreference preference : preferences) { + public void updateItems(Iterable<ItemUpdate> updates) { + for (ItemUpdate preference : updates) { preference.apply(); } } - - private void resolveOrLogError(String itemId, Consumer<? super Item> consumer) { - Optional<Item> optionalItem = knowledgeBase.getSmartHomeEntityModel().resolveItem(itemId); - if (!optionalItem.isPresent()) { - logger.warn("Could not resolve '{}' as an item.", itemId); - } - optionalItem.ifPresent(consumer); - } } diff --git a/feedbackloop.execute/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/execute/ExecuteImplTest.java b/feedbackloop.execute/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/execute/ExecuteImplTest.java index ff915252454a236e885f504858afe7914f30aec5..3b0501e31c1f7fa4ca849ff3b087aa9361f4666e 100644 --- a/feedbackloop.execute/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/execute/ExecuteImplTest.java +++ b/feedbackloop.execute/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/execute/ExecuteImplTest.java @@ -4,11 +4,12 @@ import de.tudresden.inf.st.eraser.feedbackloop.api.Execute; import de.tudresden.inf.st.eraser.jastadd.model.*; import de.tudresden.inf.st.eraser.util.TestUtils; import de.tudresden.inf.st.eraser.util.TestUtils.ModelAndItem; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Collections; +import static org.junit.jupiter.api.Assertions.*; + /** * Test updating items within the Execute component. * @@ -30,19 +31,19 @@ public class ExecuteImplTest { lamp.enableSendState(); TestUtils.getDefaultGroup(model).addItem(lamp); - numberItem.addControlling(lamp); + numberItem.synchronizeWith(lamp); Execute execute = new ExecuteImpl(); execute.setKnowledgeBase(model.getRoot()); - Assert.assertEquals(0, numberItem.getState(), DELTA); - Assert.assertEquals(TupleHSB.of(0, 0, 0), lamp.getState()); + assertEquals(0, numberItem.getState(), DELTA); + assertEquals(TupleHSB.of(0, 0, 0), lamp.getState()); - ItemPreference preference = new ItemPreferenceColor(lamp, TupleHSB.of(1, 2, 3)); + ItemUpdate preference = new ItemUpdateColor(lamp, TupleHSB.of(1, 2, 3)); execute.updateItems(Collections.singletonList(preference)); - Assert.assertEquals(3, numberItem.getState(), DELTA); - Assert.assertEquals(TupleHSB.of(1, 2, 3), lamp.getState()); + assertEquals(3, numberItem.getState(), DELTA); + assertEquals(TupleHSB.of(1, 2, 3), lamp.getState()); } @Test @@ -61,19 +62,19 @@ public class ExecuteImplTest { lamp.enableSendState(); TestUtils.getDefaultGroup(model).addItem(lamp); - button.addControlling(lamp); + button.synchronizeWith(lamp); Execute execute = new ExecuteImpl(); execute.setKnowledgeBase(model.getRoot()); - Assert.assertFalse(button.getState()); - Assert.assertEquals(TupleHSB.of(0, 0, 0), lamp.getState()); + assertFalse(button.getState()); + assertEquals(TupleHSB.of(0, 0, 0), lamp.getState()); - ItemPreference preference = new ItemPreferenceColor(lamp, TupleHSB.of(1, 2, 3)); + ItemUpdate preference = new ItemUpdateColor(lamp, TupleHSB.of(1, 2, 3)); execute.updateItems(Collections.singletonList(preference)); - Assert.assertTrue(button.getState()); - Assert.assertEquals(TupleHSB.of(1, 2, 3), lamp.getState()); + assertTrue(button.getState()); + assertEquals(TupleHSB.of(1, 2, 3), lamp.getState()); } @Test @@ -102,28 +103,28 @@ public class ExecuteImplTest { lamp.enableSendState(); TestUtils.getDefaultGroup(model).addItem(lamp); - lamp.addControlledBy(numberItem); - lamp.addControlledBy(stringItem); - lamp.addControlledBy(booleanItem); - lamp.addControlledBy(colorItem); + lamp.synchronizeWith(numberItem); + lamp.synchronizeWith(stringItem); + lamp.synchronizeWith(booleanItem); + lamp.synchronizeWith(colorItem); Execute execute = new ExecuteImpl(); execute.setKnowledgeBase(model.getRoot()); - Assert.assertEquals(0, numberItem.getState(), DELTA); - Assert.assertEquals("0", stringItem.getState()); - Assert.assertFalse(booleanItem.getState()); - Assert.assertEquals(TupleHSB.of(0, 0, 0), colorItem.getState()); - Assert.assertEquals(TupleHSB.of(0, 0, 0), lamp.getState()); + assertEquals(0, numberItem.getState(), DELTA); + assertEquals("0", stringItem.getState()); + assertFalse(booleanItem.getState()); + assertEquals(TupleHSB.of(0, 0, 0), colorItem.getState()); + assertEquals(TupleHSB.of(0, 0, 0), lamp.getState()); - ItemPreference preference = new ItemPreferenceColor(lamp, TupleHSB.of(1, 2, 3)); + ItemUpdate preference = new ItemUpdateColor(lamp, TupleHSB.of(1, 2, 3)); execute.updateItems(Collections.singletonList(preference)); - Assert.assertEquals(3, numberItem.getState(), DELTA); - Assert.assertEquals("1,2,3", stringItem.getState()); - Assert.assertTrue(booleanItem.getState()); - Assert.assertEquals(TupleHSB.of(1, 2, 3), colorItem.getState()); - Assert.assertEquals(TupleHSB.of(1, 2, 3), lamp.getState()); + assertEquals(3, numberItem.getState(), DELTA); + assertEquals("1,2,3", stringItem.getState()); + assertTrue(booleanItem.getState()); + assertEquals(TupleHSB.of(1, 2, 3), colorItem.getState()); + assertEquals(TupleHSB.of(1, 2, 3), lamp.getState()); } } diff --git a/feedbackloop.learner/build.gradle b/feedbackloop.learner/build.gradle index 3ad295bf3c112d55e31fb6d185cef21ead1da66d..270a1146ccb06a983472c29b54a8ce510649ce9b 100644 --- a/feedbackloop.learner/build.gradle +++ b/feedbackloop.learner/build.gradle @@ -1,23 +1,11 @@ -apply plugin: 'application' - -dependencies { - compile project(':eraser-base') - compile project(':feedbackloop.api') - compile group: 'org.encog', name: 'encog-core', version: '3.4' +plugins { + id 'eraser.java-application-conventions' } -run { - mainClassName = 'de.tudresden.inf.st.eraser.feedbackloop.learner.Main' - standardInput = System.in - if (project.hasProperty("appArgs")) { - args Eval.me(appArgs) - } +dependencies { + implementation project(':eraser-base') + implementation project(':feedbackloop.api') + implementation group: 'org.encog', name: 'encog-core', version: '3.4' } -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} +application.mainClass = 'de.tudresden.inf.st.eraser.feedbackloop.learner.Main' diff --git a/feedbackloop.learner/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner/LearnerImpl.java b/feedbackloop.learner/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner/LearnerImpl.java index 83edf6b7f195d9be6420369d4590955c42449735..5c2489a393a7aa4cefa55c1383b69327059ead75 100644 --- a/feedbackloop.learner/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner/LearnerImpl.java +++ b/feedbackloop.learner/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner/LearnerImpl.java @@ -174,11 +174,6 @@ public class LearnerImpl implements Learner { return fillModel(modelID); } - @Override - public EncogModel getTrainedModel(URL url, int modelID) { - return fillModel(modelID); - } - private EncogModel fillModel(int modelID) { EncogModel encogModel = new EncogModel("NN"); BasicNetwork nn = models.get(modelID).getNetwork(); diff --git a/feedbackloop.learner/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner/Main.java b/feedbackloop.learner/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner/Main.java index 0d4983c47112462224aa5595942aa23cf738ad8b..d1d56afaa351eecb9c7b79464a7c5ad25e648e71 100644 --- a/feedbackloop.learner/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner/Main.java +++ b/feedbackloop.learner/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner/Main.java @@ -226,7 +226,7 @@ public class Main { List<String> output = new ArrayList<>(); Function<DoubleNumber, String> leafToString = classification -> Double.toString(classification.number); - Function<NeuralNetworkRoot, DoubleNumber> classify = NeuralNetworkRoot::classify; + Function<NeuralNetworkRoot, DoubleNumber> classify = NeuralNetworkRoot::internalClassify; DoubleNumber classification = classify.apply(nn); output.add(leafToString.apply(classification)); System.out.println(output); diff --git a/feedbackloop.learner_backup/build.gradle b/feedbackloop.learner_backup/build.gradle index 6199fa8da5b905d3d5d4cb02d6c6858218f3ffdd..3cc29f284c90d32499bb2fae65ee82693d52bf39 100644 --- a/feedbackloop.learner_backup/build.gradle +++ b/feedbackloop.learner_backup/build.gradle @@ -1,34 +1,19 @@ -apply plugin: 'application' +plugins { + id 'eraser.java-application-conventions' + id 'io.franzbecker.gradle-lombok' version "${gradle_lombok_version}" +} dependencies { - compile project(':eraser-base') - compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.8' - compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.11.2' - compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.2' - testCompile group: 'junit', name: 'junit', version: '4.12' - testCompile group: 'org.hamcrest', name: 'hamcrest-junit', version: '2.0.0.0' - compile group: 'org.encog', name: 'encog-core', version: '3.4' - implementation group: 'com.opencsv', name: 'opencsv', version: '4.1' - implementation group: 'commons-io', name: 'commons-io', version: '2.5' - implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.8' - implementation group: 'org.apache.httpcomponents', name: 'fluent-hc', version: '4.5.8' + implementation project(':eraser-base') + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: "${jackson_version}" + implementation group: 'org.encog', name: 'encog-core', version: '3.4' + implementation group: 'com.opencsv', name: 'opencsv', version: '5.3' + implementation group: 'commons-io', name: 'commons-io', version: '2.8.0' + implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: "${apache_httpcomponents_version}" + implementation group: 'org.apache.httpcomponents', name: 'fluent-hc', version: "${apache_httpcomponents_version}" // https://mvnrepository.com/artifact/org.apache.spark/spark-mllib //runtime group: 'org.apache.spark', name: 'spark-mllib_2.10', version: '1.3.0' - compile group: 'com.sparkjava', name: 'spark-core', version: '2.9.0' -} - -run { - mainClassName = 'de.tudresden.inf.st.eraser.feedbackloop.learner_backup.Main' - standardInput = System.in - if (project.hasProperty("appArgs")) { - args Eval.me(appArgs) - } +// compile group: 'com.sparkjava', name: 'spark-core', version: '2.9.0' } -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} +application.mainClass = 'de.tudresden.inf.st.eraser.feedbackloop.learner_backup.Main' diff --git a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/CsvTransfer.java b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/CsvTransfer.java deleted file mode 100644 index 1c08268d59dd4003149e551d2e3e60895d30e3f6..0000000000000000000000000000000000000000 --- a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/CsvTransfer.java +++ /dev/null @@ -1,60 +0,0 @@ -package de.tudresden.inf.st.eraser.feedbackloop.learner_backup; - -import java.io.*; -import java.util.*; - -import com.opencsv.CSVReader; -import com.opencsv.CSVWriter; - - -public class CsvTransfer { - public static void main(String[] args){ - Learner learner=new Learner(); - learner.train("datasets/backup/activity_data.csv","datasets/backup/preference_data.csv"); - } - - //learner.train("datasets/backup/activity_data.csv","datasets/backup/preference_data.csv"); - /**private static final String CSV_FILE_PATH - = "datasets/backup/activity_data_example.csv"; - private static final String OUTPUT_FILE_PATH - = "datasets/backup/activity_data.csv"; - public static void main(String[] args) - { - addDataToCSV(CSV_FILE_PATH, OUTPUT_FILE_PATH); - } - public static void addDataToCSV(String input, String output) - { - File input_file = new File(input); - File output_file = new File(output); - try { - // create FileWriter object with file as parameter - FileReader reader = new FileReader(input_file); - CSVReader csv_reader = new CSVReader(reader); - String[] nextRecord; - FileWriter writer = new FileWriter(output_file); - CSVWriter csv_writer = new CSVWriter(writer, ',', - CSVWriter.NO_QUOTE_CHARACTER, - CSVWriter.DEFAULT_ESCAPE_CHARACTER, - CSVWriter.DEFAULT_LINE_END); - List<String[]> data = new ArrayList<String[]>(); - - while ((nextRecord = csv_reader.readNext()) != null) { - data.add(nextRecord); - - for (String cell : nextRecord) { - System.out.print(cell + "\t"); - } - System.out.println(); - } - csv_writer.writeAll(data); - writer.close(); - csv_reader.close(); - } - catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - }*/ -} - - diff --git a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/DummyPreference.java b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/DummyPreference.java index 59f3ad9f43fd11eab315e145b1780d4152b82181..58eaf48b045f44a4d20f18f2441cccafc98077c9 100644 --- a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/DummyPreference.java +++ b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/DummyPreference.java @@ -9,25 +9,19 @@ import java.util.Random; public class DummyPreference { private static String activity; //Activity: walking, reading, working, dancing, lying, getting up - private static String watch_brightness; //dark: <45; dimmer 45-70; bright >70; - private static String light_color_openhab_H; //red 7; green 120; blue 240; yellow 60; sky blue 180; purple 300; - private static String brightness_output; //1-100**/ private static Random random = new Random(); public static void main(String[] args) { creator(); } - static void creator(){ - - - try{ + private static void creator(){ + try { FileWriter writer = new FileWriter("datasets/backup/preference_data.csv",true); CSVWriter csv_writer = new CSVWriter(writer, ',', CSVWriter.NO_QUOTE_CHARACTER, CSVWriter.DEFAULT_ESCAPE_CHARACTER, CSVWriter.DEFAULT_LINE_END); - //activity="walking" green activity ="walking"; @@ -40,33 +34,46 @@ public class DummyPreference { csv_writer.writeAll(generator("getting up","yellow")); csv_writer.close(); writer.close(); - }catch (IOException e){e.printStackTrace();} + } catch (IOException e) { + e.printStackTrace(); + } } - static List<String[]> generator(String activity_input, String color){ - List<String[]> data = new ArrayList<String[]>(); + + /** + * Generate random data + * @param activity_input input for activity + * @param color red 7; green 120; blue 240; yellow 60; sky blue 180; purple 300; + * @return generated data + */ + private static List<String[]> generator(String activity_input, String color){ + List<String[]> data = new ArrayList<>(); + //dark: <45; dimmer 45-70; bright >70; + //1-100**/ + String brightness_output; + String watch_brightness; activity = activity_input; - light_color_openhab_H =color; + // //100 walking with different lighting intensity - for (int i=0; i<100; i++){ + for (int i=0; i<100; i++) { String[] add_data = new String[4]; int brightness = random.nextInt(3000); System.out.println(brightness); - if (brightness<45){ + if (brightness<45) { watch_brightness = "dark"; brightness_output ="100"; - }else if(45<=brightness && brightness<200){ + } else if(brightness < 200) { watch_brightness = "dimmer"; brightness_output ="40"; - }else if( 200<=brightness && brightness<1000){ + } else if(brightness < 1000) { watch_brightness = "medium"; brightness_output ="70"; - }else{ + } else { watch_brightness = "bright"; brightness_output ="0"; } add_data[0] = activity; add_data[1] = watch_brightness; - add_data[2] = light_color_openhab_H; + add_data[2] = color; add_data[3] = brightness_output; data.add(add_data); } diff --git a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/Learner.java b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/Learner.java index 5d8021d8faf7519d9ec15305f0752cfbcd742a0c..6954c37d27237cacb539760e3268db03aa7f84c1 100644 --- a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/Learner.java +++ b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/Learner.java @@ -1,191 +1,153 @@ package de.tudresden.inf.st.eraser.feedbackloop.learner_backup; -import java.io.File; -import java.io.IOException; -//import java.util.ArrayList; -//import com.sun.javafx.tools.packager.Log; +import com.fasterxml.jackson.databind.ObjectMapper; +import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.data.LearnerSettings; +import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.data.SimpleColumnDefinition; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -//import org.encog.ConsoleStatusReportable; +import org.encog.ConsoleStatusReportable; import org.encog.Encog; -import org.encog.ml.MLClassification; -//import org.encog.ml.MLRegression; import org.encog.ml.data.MLData; import org.encog.ml.data.versatile.NormalizationHelper; import org.encog.ml.data.versatile.VersatileMLDataSet; import org.encog.ml.data.versatile.columns.ColumnDefinition; -import org.encog.ml.data.versatile.columns.ColumnType; import org.encog.ml.data.versatile.sources.CSVDataSource; import org.encog.ml.data.versatile.sources.VersatileDataSource; -import org.encog.ml.factory.MLMethodFactory; import org.encog.ml.model.EncogModel; import org.encog.neural.networks.BasicNetwork; +import org.encog.persist.EncogDirectoryPersistence; import org.encog.util.csv.CSVFormat; -import static org.encog.persist.EncogDirectoryPersistence.*; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +/** + * Internal class for Neural Networks using Encog. + * Expects either a CSV file with data points to learn from, + * or serialized versions of a trained network and its normalization helpers. + * + * @author rschoene - Initial contribution + */ public class Learner { - /** - * intial train - * */ - private File save_activity_model_file; - private File save_preference_model_file; - private File csv_file; - private VersatileMLDataSet a_data; - private VersatileMLDataSet p_data; - private EncogModel a_model; - private EncogModel p_model; - private NormalizationHelper activity_helper; - private NormalizationHelper preference_helper; - private MLClassification a_best_method; - private MLClassification p_best_method; - private final Logger logger = LogManager.getLogger(Learner.class); + private File modelFile; + private NormalizationHelper normalizationHelper; + private BasicNetwork network; + private static final Logger logger = LogManager.getLogger(Learner.class); - public Learner() { - try { - save_activity_model_file = File.createTempFile("activity_model", "eg"); - } catch (IOException e) { - // use local alternative - save_activity_model_file = new File("activity_model.eq"); - } - try { - save_preference_model_file = File.createTempFile("preference_model", "eg"); - } catch (IOException e) { - // use local alternative - save_preference_model_file = new File("preference_model.eg"); - } - save_activity_model_file.deleteOnExit(); - save_preference_model_file.deleteOnExit(); - } + private final LearnerSettings settings; - private void activityDataAnalyser(String activity_csv_url){ - VersatileDataSource a_souce; - String csv_url_activity; - csv_url_activity = activity_csv_url; - this.csv_file = new File(csv_url_activity); - a_souce = new CSVDataSource(csv_file,false,CSVFormat.DECIMAL_POINT); - a_data = new VersatileMLDataSet(a_souce); - String[] activity_inputs={"m_accel_x","m_accel_y", "m_accel_z", - "m_rotation_x","m_rotation_y", "m_rotation_z", - "w_accel_x","w_accel_y", "w_accel_z", - "w_rotation_x","w_rotation_y","w_rotation_z" - }; - for(int i=0; i < activity_inputs.length; i++){ - a_data.defineSourceColumn(activity_inputs[i], i, ColumnType.continuous); - } - ColumnDefinition outputColumn = a_data.defineSourceColumn("labels", 12, ColumnType.nominal); - a_data.defineSingleOutputOthersInput(outputColumn); - a_data.analyze(); - a_model = new EncogModel(a_data); - a_model.selectMethod(a_data, MLMethodFactory.TYPE_FEEDFORWARD); - a_data.normalize(); - activity_helper = a_data.getNormHelper(); - } + /** + * Creates a new learner object using the configuration at the given URL. + * @param configURL the location of the configuration + * @throws IOException if an error occurs in {@link ObjectMapper#readValue(URL, Class)} + */ + Learner(URL configURL) throws IOException { + this(LearnerSettings.loadFrom(configURL)); + } - private void preferenceDataAnalyser(String preference_csv_url){ - VersatileDataSource p_source; - String csv_url_preference; - csv_url_preference = preference_csv_url; - this.csv_file = new File(csv_url_preference); - p_source = new CSVDataSource(csv_file,false,CSVFormat.DECIMAL_POINT); - p_data = new VersatileMLDataSet(p_source); - p_data.defineSourceColumn("activity", 0, ColumnType.nominal); - p_data.defineSourceColumn("w_brightness", 1, ColumnType.nominal); - ColumnDefinition outputColumn1 = p_data.defineSourceColumn("label1", 2, ColumnType.continuous); - ColumnDefinition outputColumn2 = p_data.defineSourceColumn("label2", 3, ColumnType.continuous); - ColumnDefinition[] outputs = new ColumnDefinition[2]; - outputs[0] = outputColumn1; - outputs[1] = outputColumn2; - p_data.defineMultipleOutputsOthersInput(outputs); - p_data.analyze(); - p_model = new EncogModel(p_data); - p_model.selectMethod(p_data, MLMethodFactory.TYPE_FEEDFORWARD); - p_data.normalize(); - preference_helper = p_data.getNormHelper(); + Learner(LearnerSettings settings) { + this.settings = settings; + try { + modelFile = File.createTempFile(settings.name + "_model", "eg"); + } catch (IOException e) { + // use local alternative + modelFile = new File(settings.name + "_model.eg"); } + modelFile.deleteOnExit(); + } - void train(String activity_url,String preference_url){ - activity_train(activity_url); - preference_train(preference_url); - Encog.getInstance().shutdown(); +// /** +// * Begin training using the training set specified in settings. +// * @throws MalformedURLException if the location of the training set in the settings is malformed +// */ +// void train() throws MalformedURLException { +// URL location = new File(settings.initialDataFile).toURI().toURL(); +// train(location); +// } + /** + * Begin training with the given initial training set. + * @param location the location of the training set + */ + void train(URL location, String csvFormatString) { + logger.info("Training for {} begins using {}", settings.name, location); + VersatileDataSource source; + File csvFile = new File(location.getFile()); + CSVFormat csvFormat; + switch (csvFormatString) { + case "DECIMAL_POINT": csvFormat = CSVFormat.DECIMAL_POINT; break; + case "DECIMAL_COMMA": csvFormat = CSVFormat.DECIMAL_COMMA; break; + default: + logger.warn("Unknown CSV format, using default decimal point"); + csvFormat = CSVFormat.DECIMAL_POINT; } - - void activity_train(String activity_csv_url){ - logger.info("Activity training is beginning ... ..."); - activityDataAnalyser(activity_csv_url); - a_model.holdBackValidation(0.3, true, 1001); - a_model.selectTrainingType(a_data); - a_best_method = (MLClassification)a_model.crossvalidate(5, true); - saveEncogModel(save_activity_model_file); - logger.info("Activity training is finished ... ..."); - Encog.getInstance().shutdown(); + source = new CSVDataSource(csvFile, true, csvFormat); + VersatileMLDataSet data = new VersatileMLDataSet(source); + List<ColumnDefinition> targets = new ArrayList<>(); + final int inputSize = settings.columns.size(); + for (int index = 0; index < inputSize; index++) { + SimpleColumnDefinition columnDefinition = settings.columns.get(index); + ColumnDefinition sourceColumn = data.defineSourceColumn(columnDefinition.name, index, columnDefinition.type); + if (columnDefinition.kind == SimpleColumnDefinition.ColumnKind.target) { + targets.add(sourceColumn); + } } - void preference_train(String prefence_csv_url){ - logger.info("Preference training is beginning ... ..."); - preferenceDataAnalyser(prefence_csv_url); - p_model.holdBackValidation(0.3, true, 1001); - p_model.selectTrainingType(p_data); - p_best_method = (MLClassification)p_model.crossvalidate(5, true); - saveEncogModel(save_preference_model_file); - logger.info("Preference training is finished ... ..."); - Encog.getInstance().shutdown(); + if (targets.isEmpty()) { + logger.warn("No targets specified for {}!", settings.name); } - - String[] predictor(String[] new_data){ - String[] preference_data = new String[2]; - String[] result = new String[3]; - String[] activity_data= new String[12]; - for(int i=0; i<new_data.length;i++){ - activity_data[i]=new_data[i]; - } - result[0] = activity_predictor(activity_data); - preference_data[0]=result[0]; - preference_data[1]=new_data[12]; - result[1] = preference_predictor(preference_data)[0]; - result[2] = preference_predictor(preference_data)[1]; - Encog.getInstance().shutdown(); - return result; + if (targets.size() == 1) { + data.defineSingleOutputOthersInput(targets.get(0)); + } else { + data.defineMultipleOutputsOthersInput(targets.toArray(new ColumnDefinition[0])); } - - String activity_predictor(String[] new_data){ - logger.info("Activity predicting ... ..."); - String activity_result; - activityDataAnalyser("../datasets/backup/activity_data.csv"); - BasicNetwork activity_method = (BasicNetwork) loadObject(save_activity_model_file); - MLData input = activity_helper.allocateInputVector(); - activity_helper.normalizeInputVector(new_data,input.getData(),false); - MLData output = activity_method.compute(input); - activity_result = activity_helper.denormalizeOutputVectorToString(output)[0]; - Encog.getInstance().shutdown(); - logger.debug("Activity Predictor result is: {}",activity_result); - return activity_result; + data.analyze(); + EncogModel model = new EncogModel(data); + if (settings.verboseTraining) { + model.setReport(new ConsoleStatusReportable()); } + model.selectMethod(data, settings.trainingMethod); + data.normalize(); + normalizationHelper = data.getNormHelper(); + model.holdBackValidation(settings.validationPercent, settings.shuffleForValidation, settings.validationSeed); + model.selectTrainingType(data); + network = (BasicNetwork) model.crossvalidate(settings.validationFolds, settings.shuffleForValidation); + EncogDirectoryPersistence.saveObject(modelFile, network); + logger.info("Training for {} finished", settings.name); + } - String[] preference_predictor(String[] new_data){ - logger.info("Activity predicting ... ..."); - String[] preference_result; - preference_result = new String[2]; - preferenceDataAnalyser("../datasets/backup/preference_data.csv"); - BasicNetwork preference_method = (BasicNetwork)loadObject(save_preference_model_file); - MLData input = preference_helper.allocateInputVector(); - preference_helper.normalizeInputVector(new_data, input.getData(),false); - MLData output = preference_method.compute(input); - preference_result[0] = preference_helper.denormalizeOutputVectorToString(output)[0]; - preference_result[1] = preference_helper.denormalizeOutputVectorToString(output)[1]; - Encog.getInstance().shutdown(); - logger.debug("Preference Predictor result is, Color: {}",Math.round(Float.valueOf(preference_result[0]))); - logger.debug("Preference Predictor result is, Brightness: {} ",Math.round(Float.valueOf(preference_result[1]))); - return preference_result; - } - private void saveEncogModel(File modelFile){ - if (modelFile.equals(save_activity_model_file)) { - saveObject(modelFile, this.a_best_method); - } else { - saveObject(modelFile, this.p_best_method); - } + String[] predictor(String[] newData) { + String[] result; + MLData input = normalizationHelper.allocateInputVector(); + normalizationHelper.normalizeInputVector(newData, input.getData(), false); + MLData output = network.compute(input); + result = normalizationHelper.denormalizeOutputVectorToString(output); + logger.debug("Result prediction for {} applied on {} is {}:", settings.name, newData, result); + return result; + } - } -} + void load(URL networkLocation, URL normalizationHelperLocation, boolean useJsonFormat) + throws IOException, ClassNotFoundException { + this.network = (BasicNetwork) EncogDirectoryPersistence.loadObject(networkLocation.openStream()); + this.normalizationHelper = LearnerPersistenceUtils.loadNormalizationHelperFrom( + normalizationHelperLocation, useJsonFormat); + } + void shutdown() { + Encog.getInstance().shutdown(); + } + public void save() { + EncogDirectoryPersistence.saveObject(new File(settings.name + "_network.eg"), this.network); + try { + LearnerPersistenceUtils.saveNormalizationHelper(this.normalizationHelper, + new File(settings.name + "_normalizer.json").toURI().toURL(), true); + } catch (IOException e) { + logger.catching(e); + } + } +} diff --git a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/LearnerGoal.java b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/LearnerGoal.java new file mode 100644 index 0000000000000000000000000000000000000000..3c3bb624695de79a1dce060508b9b52d23ff7046 --- /dev/null +++ b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/LearnerGoal.java @@ -0,0 +1,11 @@ +package de.tudresden.inf.st.eraser.feedbackloop.learner_backup; + +/** + * Goal of the Learner. + * + * @author rschoene - Initial contribution + */ +public enum LearnerGoal { + ACTIVITY_PHONE_AND_WATCH, + PREFERENCE_BRIGHTNESS_IRIS +} diff --git a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/LearnerKind.java b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/LearnerKind.java new file mode 100644 index 0000000000000000000000000000000000000000..cbd9f047a3d12c1da42f3e69d3e79111f6c2c4c8 --- /dev/null +++ b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/LearnerKind.java @@ -0,0 +1,13 @@ +package de.tudresden.inf.st.eraser.feedbackloop.learner_backup; + +/** + * Kind of Learner. + * + * @author rschoene - Initial contribution + */ +public enum LearnerKind { + /** The standard learner, getting a CSV for initial training */ + normal, + /** A learner loaded a pre-trained model, getting a BasicNetwork and a NormalizationHelper */ + loaded +} diff --git a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/LearnerPersistenceUtils.java b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/LearnerPersistenceUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..882fac6d48f804bd5e3c98be01dd4daf6d389cea --- /dev/null +++ b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/LearnerPersistenceUtils.java @@ -0,0 +1,190 @@ +package de.tudresden.inf.st.eraser.feedbackloop.learner_backup; + +import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.data.NormalizationHelperData; +import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.data.SimpleColumnDefinition; +import de.tudresden.inf.st.eraser.util.ParserUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.encog.EncogError; +import org.encog.ml.data.versatile.NormalizationHelper; +import org.encog.ml.data.versatile.columns.ColumnDefinition; +import org.encog.ml.data.versatile.columns.ColumnType; +import org.encog.ml.data.versatile.missing.MeanMissingHandler; +import org.encog.ml.data.versatile.normalizers.Normalizer; +import org.encog.ml.data.versatile.normalizers.RangeNormalizer; +import org.encog.ml.data.versatile.normalizers.strategies.BasicNormalizationStrategy; +import org.encog.ml.data.versatile.normalizers.strategies.NormalizationStrategy; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Handles objects storing to and loading from files. + * + * @author rschoene - Initial contribution + */ +public class LearnerPersistenceUtils { + + private static final Logger logger = LogManager.getLogger(LearnerPersistenceUtils.class); + + static NormalizationHelper loadNormalizationHelperFrom(URL location, boolean useJsonFormat) throws IOException, ClassNotFoundException { + NormalizationHelper result; + if (useJsonFormat) { + NormalizationHelperData data = NormalizationHelperData.loadFrom(location); + result = new NormalizationHelper(); + result.setSourceColumns(mapSimpleColumnDefs(data.sourceColumns)); + result.setInputColumns(mapSimpleColumnDefs(data.inputColumns)); + result.setOutputColumns(mapSimpleColumnDefs(data.outputColumns)); + result.setNormStrategy(new BasicNormalizationStrategy( + data.inputLow, data.inputHigh, data.outputLow, data.outputHigh)); + data.unknownValues.forEach(result::defineUnknownValue); + data.missingToMean.forEach((name, mean) -> findColumnDef(name, result).ifPresent( + columnDefinition -> result.defineMissingHandler(columnDefinition, new MeanMissingHandler()))); + } else { + URLConnection connection = location.openConnection(); + ObjectInputStream in = new ObjectInputStream(connection.getInputStream()); + result = (NormalizationHelper) in.readObject(); + } + return result; + } + + private static Optional<ColumnDefinition> findColumnDef(String name, NormalizationHelper result) { + List<ColumnDefinition> allDefs = new ArrayList<>(); + allDefs.addAll(result.getSourceColumns()); + allDefs.addAll(result.getInputColumns()); + allDefs.addAll(result.getOutputColumns()); + for(ColumnDefinition colDef : allDefs) { + if (colDef.getName().equals(name)) { + return Optional.of(colDef); + } + } + logger.warn("Could not find column with name '{}'", name); + return Optional.empty(); + } + + private static List<ColumnDefinition> mapSimpleColumnDefs(List<SimpleColumnDefinition> columnDefinitions) { + return columnDefinitions.stream() + .map(colDef -> { + ColumnDefinition result = new ColumnDefinition(colDef.name, colDef.type); + result.setLow(colDef.low); + result.setHigh(colDef.high); + result.setSd(colDef.sd); + result.setMean(colDef.mean); + result.setCount(colDef.count); + result.setIndex(colDef.index); + colDef.classes.forEach(result::defineClass); + return result; + }) + .collect(Collectors.toList()); + } + + private static List<SimpleColumnDefinition> mapColumnDefs(List<ColumnDefinition> columnDefinitions) { + return columnDefinitions.stream() + .map(colDef -> { + SimpleColumnDefinition result = new SimpleColumnDefinition(); + result.name = colDef.getName(); + result.type = colDef.getDataType(); + result.low = colDef.getLow(); + result.high = colDef.getHigh(); + result.sd = colDef.getSd(); + result.mean = colDef.getMean(); + result.count = colDef.getCount(); + result.index = colDef.getIndex(); + result.classes = colDef.getClasses(); + return result; + }) + .collect(Collectors.toList()); + } + + static void saveNormalizationHelper(NormalizationHelper normalizationHelper, URL targetLocation, boolean useJsonFormat) throws IOException { + ObjectOutputStream out; + if (targetLocation.getProtocol().equals("file")) { + // we want to write to a file, thus can not use URL directly (FileURLConnection does not support output) + out = new ObjectOutputStream(new FileOutputStream(targetLocation.getFile())); + } else { + URLConnection connection = targetLocation.openConnection(); + connection.setDoOutput(true); + + out = new ObjectOutputStream(connection.getOutputStream()); + } + if (useJsonFormat) { + NormalizationHelperData data = new NormalizationHelperData(); + data.sourceColumns = mapColumnDefs(normalizationHelper.getSourceColumns()); + data.inputColumns = mapColumnDefs(normalizationHelper.getInputColumns()); + data.outputColumns = mapColumnDefs(normalizationHelper.getOutputColumns()); + NormalizationStrategy normStrategy = normalizationHelper.getNormStrategy(); + if (normStrategy instanceof BasicNormalizationStrategy) { + BasicNormalizationStrategy basicNormStrategy = (BasicNormalizationStrategy) normStrategy; + double[] lowAndHigh = extractLowAndHigh(basicNormStrategy.getInputNormalizers().get(ColumnType.continuous)); + data.inputLow = lowAndHigh[0]; + data.inputHigh = lowAndHigh[1]; + lowAndHigh = extractLowAndHigh(basicNormStrategy.getOutputNormalizers().get(ColumnType.continuous)); + data.outputLow = lowAndHigh[0]; + data.outputHigh = lowAndHigh[1]; + } else { + logger.warn("Unknown normalization strategy, can not serialize {}", normStrategy); + } + data.unknownValues = normalizationHelper.getUnknownValues(); + testForMissingHandlers(normalizationHelper, data); + ParserUtils.saveTo(data, targetLocation); + } else { + out.writeObject(normalizationHelper); + } + out.close(); + } + + private static void testForMissingHandlers(NormalizationHelper normalizationHelper, NormalizationHelperData data) { + List<ColumnDefinition> allDefs = new ArrayList<>(); + allDefs.addAll(normalizationHelper.getSourceColumns()); + allDefs.addAll(normalizationHelper.getInputColumns()); + allDefs.addAll(normalizationHelper.getOutputColumns()); + String firstUnknown; + if (normalizationHelper.getUnknownValues().isEmpty()) { + normalizationHelper.defineUnknownValue(null); + firstUnknown = null; + } else { + firstUnknown = normalizationHelper.getUnknownValues().get(0); + } + double[] buffer = new double[1]; + for (ColumnDefinition colDef : allDefs) { + try { + normalizationHelper.normalizeToVector(colDef, 0, buffer, false, firstUnknown); + // there was a missing handler defined + data.missingToMean.put(colDef.getName(), colDef.getMean()); + } catch (EncogError e) { + // missing handler is not defined, so move on + } + } + } + + private static double[] extractLowAndHigh(Normalizer norm) { + if (norm instanceof RangeNormalizer) { + /* reconstruct low and high from the normalizer, as we know the normalization formula is + result = ((value - col.low) / (col.high - col.low)) * (normHigh - normLow) + normLow + if the first part of the product is zero, we get normLow + if the first part of the product is one, we get normHigh + */ + double[] buffer = new double[2]; + ColumnDefinition colDef = new ColumnDefinition("dummy", ColumnType.continuous); + colDef.setLow(0); + colDef.setHigh(1); + // now we have (value / 1) as first part of the product + norm.normalizeColumn(colDef, 0, buffer, 0); + norm.normalizeColumn(colDef, 1, buffer, 1); + return buffer; + } else { + logger.error("Unknown normalizer, can not serialize {}", norm); + return new double[2]; + } + + } + +} diff --git a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/MachineLearningHandlerFactoryImpl.java b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/MachineLearningHandlerFactoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..27e3b6c6aa1393804faeb24eeaf22d14754862a6 --- /dev/null +++ b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/MachineLearningHandlerFactoryImpl.java @@ -0,0 +1,55 @@ +package de.tudresden.inf.st.eraser.feedbackloop.learner_backup; + +import de.tudresden.inf.st.eraser.jastadd.model.MachineLearningDecoder; +import de.tudresden.inf.st.eraser.jastadd.model.MachineLearningEncoder; +import de.tudresden.inf.st.eraser.jastadd.model.MachineLearningHandlerFactory; + +import java.io.IOException; +import java.net.URL; + +import static de.tudresden.inf.st.eraser.feedbackloop.learner_backup.LearnerGoal.ACTIVITY_PHONE_AND_WATCH; +import static de.tudresden.inf.st.eraser.feedbackloop.learner_backup.LearnerGoal.PREFERENCE_BRIGHTNESS_IRIS; + +/** + * Factory to create handlers of type {@link MachineLearningHandlerFactoryImpl}. + * + * @author rschoene - Initial contribution + */ +public class MachineLearningHandlerFactoryImpl extends MachineLearningHandlerFactory { + + private MachineLearningImpl handler; + + @Override + public void initializeFor(MachineLearningHandlerFactoryTarget target, URL configUrl) throws IOException, ClassNotFoundException { + switch (target) { + case ACTIVITY_RECOGNITION: + handler = new MachineLearningImpl(ACTIVITY_PHONE_AND_WATCH, configUrl); + handler.setKnowledgeBaseRoot(knowledgeBase); + break; + case PREFERENCE_LEARNING: + handler = new MachineLearningImpl(PREFERENCE_BRIGHTNESS_IRIS, configUrl); + handler.setKnowledgeBaseRoot(knowledgeBase); + break; + default: + throw new UnsupportedOperationException("Target " + target + " is not supported"); + } + handler.startTraining(); + } + + @Override + public MachineLearningEncoder createEncoder() { + return handler; + } + + @Override + public MachineLearningDecoder createDecoder() { + return handler; + } + + @Override + public void shutdown() { + if (handler != null) { + handler.shutdown(); + } + } +} diff --git a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/MachineLearningImpl.java b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/MachineLearningImpl.java index f2540105d7dd6dbf3f037fa19868ff1c8d2749a8..b1f0062b0065e7fabb8a175c49c7d76ba1e4fcb0 100644 --- a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/MachineLearningImpl.java +++ b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/MachineLearningImpl.java @@ -1,70 +1,63 @@ package de.tudresden.inf.st.eraser.feedbackloop.learner_backup; - - -//import com.sun.javafx.tools.packager.Log; +import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.data.LearnerScenarioDefinition; import de.tudresden.inf.st.eraser.jastadd.model.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.io.IOException; +import java.net.URL; import java.time.Instant; import java.util.*; import java.util.stream.Collectors; - public class MachineLearningImpl implements MachineLearningDecoder, MachineLearningEncoder { +// public static final int GOAL_ACTIVITY_PHONE_AND_WATCH = 1; +// public static final int GOAL_PREFERENCE_BRIGHTNESS_IRIS = 2; + + private final Logger logger = LogManager.getLogger(MachineLearningImpl.class); + private final Learner learner; + private final LearnerGoal goal; + private final LearnerScenarioDefinition scenarioDefinition; + private List<Item> target_item_list; private List<Item> relevant_item_list; private String[] preference_result; + private String[] a_new_data = new String[12]; + private String[] p_new_data = new String[2]; + + private int a_length = 0; + private boolean empty; + private Root root; - private final Logger logger = LogManager.getLogger(MachineLearningImpl.class); - private final Learner learner; - private final int goal; - - public static final int GOAL_ACTIVITY_PHONE_AND_WATCH = 1; - public static final int GOAL_PREFERENCE_BRIGHTNESS_IRIS = 2; - public String activity_result; - private String[] activites= new String[]{"working", "walking", "dancing", "lying", "getting up", "reading"}; - public MachineLearningImpl(Learner learner, int goal) { - this.learner = learner; + private String activity_result; + private Instant lastModelUpdate; + + /** + * Create a new MachineLearning handler. + * @param goal The goal of the learner + * @param configURL The URL for the {@link LearnerScenarioDefinition} + * @throws IOException If an error occurs during initialization of the Learner + */ + public MachineLearningImpl(LearnerGoal goal, URL configURL) throws IOException { + scenarioDefinition = LearnerScenarioDefinition.loadFrom(configURL); + URL learnerSettingsURL = scenarioDefinition.getDefinitionFileAsURL(); + this.learner = new Learner(learnerSettingsURL); this.goal = goal; } @Override public void setKnowledgeBaseRoot(Root root) { this.root = root; - updateItems(); - } - - private void updateItems() { - SmartHomeEntityModel model = root.getSmartHomeEntityModel(); + SmartHomeEntityModel model = this.root.getSmartHomeEntityModel(); List<String> targetItemNames, relevantItemNames; switch (this.goal) { - case GOAL_ACTIVITY_PHONE_AND_WATCH: - targetItemNames = Collections.singletonList("activity"); - relevantItemNames = Arrays.asList( - "m_accel_x", - "m_accel_y", - "m_accel_z", - "m_rotation_x", - "m_rotation_y", - "m_rotation_z", - "w_accel_x", - "w_accel_y", - "w_accel_z", - "w_rotation_x", - "w_rotation_y", - "w_rotation_z" - ); - break; - case GOAL_PREFERENCE_BRIGHTNESS_IRIS: - targetItemNames = Collections.singletonList("iris1_item"); - relevantItemNames = Arrays.asList( - "activity", - "w_brightness" - ); + case ACTIVITY_PHONE_AND_WATCH: + case PREFERENCE_BRIGHTNESS_IRIS: + targetItemNames = scenarioDefinition.targetItemNames; + relevantItemNames = scenarioDefinition.relevantItemNames; break; default: logger.error("Unknown goal value ({}) set", this.goal); @@ -93,46 +86,53 @@ public class MachineLearningImpl implements MachineLearningDecoder, MachineLearn @Override public void newData(List<Item> changedItems) { - /* String topic = changedItems.get(0).getTopic().toString(); - if(topic.equals("oh2/samsung/items1")){ - new_data[0]=changedItems.get(0).influxMeasurementName(); - } - model.getSmartHomeEntityModel().items(); - Item iris_item=model.getSmartHomeEntityModel().resolveItem("iris_item").get(); - iris_item.getStateAsString();*/ /* FIXME either save state of unchanged items here (if only changed items are reported) <- pull model or let knowledge base pass all relevant items <- push model */ - if(this.goal==GOAL_ACTIVITY_PHONE_AND_WATCH){ - String[] new_data = new String[12]; - for (int i =0; i< new_data.length; i++){ - new_data[i] ="0"; - } - for(Item item: changedItems){ - int i = 0; - for(Item item1: relevant_item_list){ - if(item.getTopic().toString().equals(item1.getTopic().toString())){ - new_data[i]=item.getStateAsString(); + switch (this.goal) { + case ACTIVITY_PHONE_AND_WATCH: + for (Item item : changedItems) { + if (item.getID().equals("m_accel_x")) { + a_length = 0; + } + int i = 0; + for (Item item1 : relevant_item_list) { + if (item.getTopic().toString().equals(item1.getTopic().toString())) { + this.a_new_data[i] = item.getStateAsString(); + } + i++; } - i++; + a_length++; } - } - this.activity_result = learner.activity_predictor(new_data); - - }else if(this.goal==GOAL_PREFERENCE_BRIGHTNESS_IRIS){ - String[] new_data = new String[2]; - for(Item item: changedItems){ - if(root.getSmartHomeEntityModel().getActivityItem().equals(item)) - { - String test=item.getStateAsString(); - int index = Math.round(Float.valueOf(test)); - new_data[0]=activites[index]; + if (a_length == 12) { + for (String value : a_new_data) { + if (value == null) { + empty = true; + break; + } + } + if (!empty) { + this.activity_result = learner.predictor(a_new_data)[0]; + } + a_length = 0; + Arrays.fill(this.a_new_data, null); } - if(item.getID().equals("w_brightness")){ - new_data[1]=item.getStateAsString(); + break; + case PREFERENCE_BRIGHTNESS_IRIS: + for (Item item : changedItems) { + if (root.getSmartHomeEntityModel().getActivityItem().equals(item)) { + String test = item.getStateAsString(); + int index = Math.round(Float.parseFloat(test)); + this.p_new_data[0] = root.getMachineLearningRoot().getActivity(index).getLabel(); + } + if (item.getID().equals("w_brightness")) { + this.p_new_data[1] = item.getStateAsString(); + } } - } - this.preference_result=learner.preference_predictor(new_data); + this.preference_result = learner.predictor(this.p_new_data); + break; + default: + logger.error("Unknown goal value ({}) set in newData", this.goal); } } @@ -153,59 +153,53 @@ public class MachineLearningImpl implements MachineLearningDecoder, MachineLearn @Override public Instant lastModelUpdate() { - return null; + return this.lastModelUpdate; } @Override public MachineLearningResult classify() { + MachineLearningResult result = new MachineLearningResult(); switch (this.goal) { - case GOAL_ACTIVITY_PHONE_AND_WATCH: - String activityStringValue = activity_result; - Item activityItem = resolve(this.root.getSmartHomeEntityModel(), "activity"); - //activityItem.setStateFromString(activityStringValue); - // FIXME how to translate activityStringValue to a number? or should activity item state better be a String? - for (int i=0; i< activites.length;i++){ - if(activites[i].equals(activityStringValue)){ - activityItem.setStateFromString(String.valueOf(i)); - } + case ACTIVITY_PHONE_AND_WATCH: + if (activity_result == null) { + return result; } - logger.debug("Classify would return activity: {}", activityStringValue); - ItemPreference classifiedActivity = new ItemPreferenceDouble(activityItem, 0); - return new MachineLearningResultImpl(classifiedActivity); - case GOAL_PREFERENCE_BRIGHTNESS_IRIS: -// String[] preference = {result[1], result[2]}; - // FIXME what is the meaning of result[1] and result[2] + Item activityItem = resolve(this.root.getSmartHomeEntityModel(), "activity"); + this.root.resolveActivity(activity_result).ifPresent( + activity -> result.addItemUpdate(new ItemUpdateDouble(activityItem, activity.getIdentifier()))); + break; + case PREFERENCE_BRIGHTNESS_IRIS: Item iris1 = resolve(this.root.getSmartHomeEntityModel(), "iris1_item"); int color = 0; int brightness = 0; - if (preference_result != null){ - color = Math.round(Float.valueOf(preference_result[0])); - brightness = Math.round(Float.valueOf(preference_result[1])); + if (preference_result != null) { + color = Math.round(Float.parseFloat(preference_result[0])); + brightness = Math.round(Float.parseFloat(preference_result[1])); } - - ItemPreference classifiedPreference = new ItemPreferenceColor(iris1, TupleHSB.of(color, 100, brightness)); - return new MachineLearningResultImpl(classifiedPreference); + result.addItemUpdate(new ItemUpdateColor(iris1, TupleHSB.of(color, 100, brightness))); + break; default: logger.error("Unknown goal value ({}) set in classify", this.goal); - return new EmptyMachineLearningResult(); } + return result; } - public void initActivities(String filenameOfCsv) { - logger.debug(filenameOfCsv); - learner.activity_train(filenameOfCsv); - } - - public void initPreferences(String filenameOfCsv) { - learner.preference_train(filenameOfCsv); + void startTraining() throws IOException, ClassNotFoundException { + switch (this.scenarioDefinition.kind) { + case normal: + learner.train(this.scenarioDefinition.getDataFilesAsURL().get(0), this.scenarioDefinition.csvFormat); + break; + case loaded: + List<URL> dataFilesAsURL = this.scenarioDefinition.getDataFilesAsURL(); + learner.load(dataFilesAsURL.get(0), dataFilesAsURL.get(1), true); + } + this.lastModelUpdate = Instant.now(); } - class EmptyMachineLearningResult implements MachineLearningResult { - - @Override - public List<ItemPreference> getPreferences() { - return Collections.emptyList(); + void shutdown() { + if (this.scenarioDefinition.saveModels) { + learner.save(); } + learner.shutdown(); } - } diff --git a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/MachineLearningResultImpl.java b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/MachineLearningResultImpl.java deleted file mode 100644 index 61d483a566255d51ce424d23adb014df1eec8109..0000000000000000000000000000000000000000 --- a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/MachineLearningResultImpl.java +++ /dev/null @@ -1,26 +0,0 @@ -package de.tudresden.inf.st.eraser.feedbackloop.learner_backup; - -import de.tudresden.inf.st.eraser.jastadd.model.ItemPreference; -import de.tudresden.inf.st.eraser.jastadd.model.MachineLearningResult; - -import java.util.Collections; -import java.util.List; - -/** - * TODO: Add description. - * - * @author rschoene - Initial contribution - */ -public class MachineLearningResultImpl implements MachineLearningResult { - - private final ItemPreference preference; - - MachineLearningResultImpl(ItemPreference preference) { - this.preference = preference; - } - - @Override - public List<ItemPreference> getPreferences() { - return Collections.singletonList(preference); - } -} diff --git a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/Main.java b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/Main.java index 2580847d5ace8e832bde76159dad2e85974115a2..c1958a1255ef28b67d0ae925abea7deb73b5fae0 100644 --- a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/Main.java +++ b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/Main.java @@ -1,163 +1,163 @@ package de.tudresden.inf.st.eraser.feedbackloop.learner_backup; +import com.fasterxml.jackson.databind.ObjectMapper; +import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.data.LearnerScenarioDefinition; +import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.data.LearnerSettings; +import de.tudresden.inf.st.eraser.jastadd.model.*; +import de.tudresden.inf.st.eraser.util.ParserUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.encog.ml.data.versatile.NormalizationHelper; import org.encog.util.csv.CSVFormat; import org.encog.util.csv.ReadCSV; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; -public class Main { - - public static void main(String[] args) { - /** - * new data from KB - * */ - //ReaderCSV reader = new ReaderCSV("datasets/backup/activity_data.csv","preference"); - //reader.updater(); - Learner learner=new Learner(); - //learner.preference_train("../datasets/backup/preference_data.csv"); - //learner.train("datasets/backup/activity_data.csv","datasets/backup/preference_data.csv"); - //0.5793968,1.2126632,-4.6244006,-0.030779,0.801127,0.590978,-3.1411927,-0.93373865,-0.31124622,-0.35992432,0.33746338,-0.79608154,dancing - /**String[] new_data = new String[12]; - new_data[0]="0.5793968"; - new_data[1]="1.2126632"; - new_data[2]="-4.6244006"; - new_data[3]="-0.030779"; - new_data[4]="0.801127"; - new_data[5]="0.590978"; - new_data[6]="-3.1411927"; - new_data[7]="-0.93373865"; - new_data[8]="-0.31124622"; - new_data[9]="-0.35992432"; - new_data[10]="0.33746338"; - new_data[11]="-0.79608154"; +import static de.tudresden.inf.st.eraser.jastadd.model.MachineLearningHandlerFactory.MachineLearningHandlerFactoryTarget.ACTIVITY_RECOGNITION; - String result =learner.activity_predictor(new_data); - System.out.println(result); +@SuppressWarnings("unused") +public class Main { - List k=new ArrayList(); + private static final Logger logger = LogManager.getLogger(Main.class); - k.add(new_data);*/ + public static void main(String[] args) { +// ReaderCSV reader = new ReaderCSV("datasets/backup/activity_data.csv","preference"); +// reader.updater(); +// Learner learner=new Learner(); +// learner.preference_train("../datasets/backup/preference_data.csv"); +// learner.train("datasets/backup/activity_data.csv","datasets/backup/preference_data.csv"); +// activity_validation_learner(); +// testSettings(); +// testLoadNormalizationHelper(); + testLearnerWithDatasetFromMunich(); + } - //learner.preference_train("datasets/backup/preference_data.csv"); - //learner.train("datasets/backup/activity_data.csv","datasets/backup/preference_data.csv"); - //walking,medium,120,70 - //reading,bright,180,0 + private static void testLearnerWithDatasetFromMunich() { + MachineLearningHandlerFactory factory = new MachineLearningHandlerFactoryImpl(); + try { + URL configURL = Paths.get("src", "main", "resources", "activity_definition-2019-oct-28.json").toUri().toURL(); + LearnerScenarioDefinition scenarioDefinition = LearnerScenarioDefinition.loadFrom(configURL); + Root model = createRootWithItemsFrom(scenarioDefinition); + factory.setKnowledgeBaseRoot(model); + factory.initializeFor(ACTIVITY_RECOGNITION, configURL); + MachineLearningModel mlModel = factory.createModel(); + List<Item> changedItems = new ArrayList<>(); // TODO + mlModel.getEncoder().newData(changedItems); + MachineLearningResult result = mlModel.getDecoder().classify(); + } catch (IOException | ClassNotFoundException e) { + logger.catching(e); + } + } - //activity_validation_learner(); - //0.10654198,8.6574335,4.414908,0.040269,0.516884,0.853285,1.2066777,-1.1444284,9.648633,1.2207031E-4,-0.055358887,0.5834961 working - String[] new_data = new String[12]; - new_data[0]="0.010773907"; - new_data[1]="8.610746"; - new_data[2]="4.4963107"; - new_data[3]="0.047136"; - new_data[4]="0.515427"; - new_data[5]="0.852877"; - new_data[6]="0.9720459"; - new_data[7]="-1.3694834"; - new_data[8]="9.696517"; - new_data[9]="-0.0056152344"; - new_data[10]="-0.049438477"; - new_data[11]="0.5576782"; - //0.010773907,8.610746,4.4963107,0.047136,0.515427,0.852877,0.9720459,-1.3694834,9.696517,-0.0056152344,-0.049438477,0.5576782 working - //0.9999999988939648,-0.9995820798966354,-0.9999999999999997,-0.9999988062118802,-0.9974031940544938,-1.0 + private static Root createRootWithItemsFrom(LearnerScenarioDefinition scenarioDefinition) { + Root result = Root.createEmptyRoot(); + ParserUtils.addToUnknownGroup( + result.getSmartHomeEntityModel(), + scenarioDefinition.relevantItemNames.stream().map(Main::createItem).collect(Collectors.toList())); + return result; + } - //String result =learner.activity_predictor(new_data); - //System.out.println(result); + private static Item createItem(String itemName) { + Item item; + if (itemName.contains("OpenClose")) { + // contact item + item = new ContactItem(); + } else if (itemName.contains("Fibaro")) { + // boolean item + item = new SwitchItem(); + } else { + // double item + item = new NumberItem(); + } + item.setID(itemName); + return item; + } - /**String[] new_data_1 =new String[12]; - //-2.6252422,8.619126,-2.7030537,0.552147,0.5078,0.450302,-8.1881695,-1.2641385,0.038307227,-0.34222412,0.49102783,-0.016540527,walking - new_data_1[0]="-2.6252422"; - new_data_1[1]="8.619126"; - new_data_1[2]="-2.7030537"; - new_data_1[3]="0.552147"; - new_data_1[4]="0.5078"; - new_data_1[5]="0.450302"; - new_data_1[6]="-8.1881695"; - new_data_1[7]="-1.2641385"; - new_data_1[8]="0.038307227"; - new_data_1[9]="-0.34222412"; - new_data_1[10]="0.49102783"; - new_data_1[11]="-0.016540527"; - String result1 =learner.activity_predictor(new_data_1); - System.out.println(result1); - /** - * learner.train(activity_csv_url, preference_data_url) - * learner.predictor get the result from predictor for new data - * */ - /**String[] new_data_2 = new String[12]; - //-6.5565214,5.717354,5.6658783,0.185591,0.464146,0.413321,-20.580557,3.8498764,-0.4261679,0.7647095,-0.4713745,0.23999023,dancing - new_data_2[0]="-6.5565214"; - new_data_2[1]="5.717354"; - new_data_2[2]="5.6658783"; - new_data_2[3]="0.185591"; - new_data_2[4]="0.464146"; - new_data_2[5]="0.413321"; - new_data_2[6]="-20.580557"; - new_data_2[7]="3.8498764"; - new_data_2[8]="-0.4261679"; - new_data_2[9]="0.7647095"; - new_data_2[10]="-0.4713745"; - new_data_2[11]="0.23999023"; + private static void testLoadNormalizationHelper() { + try { + URL binaryURL = Paths.get("src", "test", "resources", "activity_normalizer.bin").toUri().toURL(); + NormalizationHelper normalizer = LearnerPersistenceUtils.loadNormalizationHelperFrom(binaryURL, false); + System.out.println("loaded"); + } catch (IOException | ClassNotFoundException e) { + logger.catching(e); + } + } - String[] new_data_3 = new String[12]; - new_data_3[0]="-5.3881507"; - new_data_3[1]="0.25378537"; - new_data_3[2]="7.69257"; - new_data_3[3]="-0.122974"; - new_data_3[4]="0.247411"; - new_data_3[5]="0.439031"; - new_data_3[6]="4.9224787"; - new_data_3[7]="-10.601525"; - new_data_3[8]="-4.927267"; - new_data_3[9]="0.7946167"; - new_data_3[10]="0.35272217"; - new_data_3[11]="0.16192627"; - //"-5.3881507","0.25378537","7.69257","-0.122974","0.247411","0.439031","4.9224787","-10.601525","-4.927267","0.7946167","0.35272217","0.16192627","lying" - Learner learner=new Learner(); - //learner.train("datasets/backup/activity_data.csv", "datasets/preference_data.csv"); - String[] result = learner.predictor(new_data_3); - System.out.println("activity is:" + result[0]); - //System.out.println("perference is: "+ result[1]);**/ + private static void testSettings() { + ObjectMapper mapper = new ObjectMapper(); + File settingsFile = Paths.get("src", "main", "resources", "activity_definition.json").toFile(); + LearnerSettings settings; + try { + settings = mapper.readValue(settingsFile, LearnerSettings.class); + } catch (IOException e) { + logger.catching(e); + return; + } + System.out.println("settings.name = " + settings.name); + System.out.println("settings.columns = " + settings.columns + .stream() + .map(col -> "(" + col.kind + ": " + col.name + "," + col.type + ")") + .collect(Collectors.joining(";"))); } - public static void activity_validation_learner(){ - ReadCSV csv = new ReadCSV("datasets/backup/activity_data.csv", false, CSVFormat.DECIMAL_POINT); - String[] line = new String[12]; - Learner learner=new Learner(); - int wrong=0; - int right=0; - while(csv.next()) { - StringBuilder result = new StringBuilder(); - line[0] = csv.get(0); - line[1] = csv.get(1); - line[2] = csv.get(2); - line[3] = csv.get(3); - line[4] = csv.get(4); - line[5] = csv.get(5); - line[6] = csv.get(6); - line[7] = csv.get(7); - line[8] = csv.get(8); - line[9] = csv.get(9); - line[10] = csv.get(10); - line[11] = csv.get(11); - String correct = csv.get(12); - String irisChosen = learner.predictor(line)[0]; - result.append(Arrays.toString(line)); - result.append(" -> predicted: "); - result.append(irisChosen); - result.append("(correct: "); - result.append(correct); - result.append(")"); - if (irisChosen.equals(correct)!=true){ - System.out.println(correct); - System.out.println(irisChosen); - ++wrong; - }else{ - ++right; + + private static void activity_validation_learner() throws IOException { + ReadCSV csv = new ReadCSV("../datasets/backup/activity_data.csv", true, CSVFormat.DECIMAL_POINT); + String[] line = new String[11]; + Learner learner = new Learner(new ObjectMapper().readValue( + Paths.get("src", "main", "resources", "activity_definition.json").toFile(), + LearnerSettings.class)); + learner.train(Paths.get("src", "test", "activity_data.csv").toUri().toURL(), "DECIMAL_POINT"); +// learner.preference_train("../datasets/backup/preference_data.csv"); + int wrong = 0; + int right = 0; + int i = 0; + while (csv.next()) { + if (i == 0) { + i++; + } else { + StringBuilder result = new StringBuilder(); + line[0] = csv.get(0); + line[1] = csv.get(1); + line[2] = csv.get(2); + line[3] = csv.get(3); + line[4] = csv.get(4); + line[5] = csv.get(5); + line[6] = csv.get(6); + line[7] = csv.get(7); + line[8] = csv.get(8); + line[9] = csv.get(9); + line[10] = csv.get(10); + //line[11] = csv.get(11); + String correct = csv.get(11); + String irisChosen = learner.predictor(line)[0]; + result.append(Arrays.toString(line)); + result.append(" -> predicted: "); + result.append(irisChosen); + result.append("(correct: "); + result.append(correct); + result.append(")"); + if (!irisChosen.equals(correct)) { + System.out.println(correct); + System.out.println(irisChosen); + ++wrong; + } else { + ++right; + } + System.out.println(result.toString()); } - System.out.println(result.toString()); + System.out.println("wrong number" + wrong); + System.out.println("right number" + right); } - System.out.println("wrong number"+wrong); - System.out.println("right number"+right); + + learner.shutdown(); //double validation = (double(right))/(double(wrong+right)); //System.out.println("%.2f"+validation); } diff --git a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/ReaderCSV.java b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/ReaderCSV.java index 76c1ce791b00843befc9ed8b9d0f1941532ac386..ee3b5aceed331350341b22a5ad5cda52408d8539 100644 --- a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/ReaderCSV.java +++ b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop.learner_backup/ReaderCSV.java @@ -1,25 +1,21 @@ package de.tudresden.inf.st.eraser.feedbackloop.learner_backup; -import java.awt.*; -import java.io.FileReader; -import java.io.*; -import java.util.Arrays; - import com.opencsv.CSVReader; -import org.apache.http.client.fluent.Request; import org.apache.http.HttpResponse; +import org.apache.http.client.fluent.Request; import org.apache.http.entity.ContentType; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.ReentrantLock; + +import java.io.File; +import java.io.FileReader; +import java.util.Arrays; public class ReaderCSV{ //read every 5 s from csv //activity CSV - /** + /* * Col 1: smartphone acceleration x * Col 2: smartphone acceleration y * Col 3: smartphone acceleration z @@ -33,7 +29,7 @@ public class ReaderCSV{ * Col 11: watch rotation y * Col 12: watch rotation z/*/ //preference CSV - /** + /* * Col 1: Activity * Col 2: watch brightness range "bright, medium, dimmer, dark"*/ @@ -113,4 +109,4 @@ public class ReaderCSV{ } } -} \ No newline at end of file +} diff --git a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/data/LearnerScenarioDefinition.java b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/data/LearnerScenarioDefinition.java new file mode 100644 index 0000000000000000000000000000000000000000..5d7d511be2f28b0e73eaabea1218246db4471f5e --- /dev/null +++ b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/data/LearnerScenarioDefinition.java @@ -0,0 +1,69 @@ +package de.tudresden.inf.st.eraser.feedbackloop.learner_backup.data; + +import com.fasterxml.jackson.annotation.JsonInclude; +import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.LearnerKind; +import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.MachineLearningImpl; +import de.tudresden.inf.st.eraser.util.ParserUtils; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Settings to initialize {@link MachineLearningImpl}. + * + * @author rschoene - Initial contribution + */ +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class LearnerScenarioDefinition { + public List<String> relevantItemNames; + public List<String> targetItemNames; + public Map<String, String> nonTrivialOutputMappings = new HashMap<>(); + public String definitionFile; + public String csvFormat = "DECIMAL_POINT"; + public List<String> dataFiles; + public LearnerKind kind = LearnerKind.normal; + /** Save models at shutdown */ + public boolean saveModels = false; + public transient URL myBaseLocation; + + public static LearnerScenarioDefinition loadFrom(URL location) throws IOException { + LearnerScenarioDefinition result = ParserUtils.loadFrom(location, LearnerScenarioDefinition.class); + result.myBaseLocation = new URL(location.getProtocol(), location.getHost(), location.getPort(), new File(location.getFile()).getParent(), null); + return result; + } + + public URL getDefinitionFileAsURL() throws MalformedURLException { + return filenameToURL(definitionFile); + } + + public List<URL> getDataFilesAsURL() throws MalformedURLException { + List<URL> result = new ArrayList<>(); + for (String dataFile : dataFiles) { + URL url = filenameToURL(dataFile); + result.add(url); + } + return result; + } + + private URL filenameToURL(URL base, String filename) throws MalformedURLException { + // construct new URL with same content except for file part + String newFilePart; + if (filename.startsWith("/")) { + newFilePart = filename; + } else { + newFilePart = (base.getFile().endsWith("/") ? base.getFile() : base.getFile() + "/" ) + filename; + } + return new URL(base.getProtocol(), base.getHost(), base.getPort(), newFilePart, null); + } + + private URL filenameToURL(String filename) throws MalformedURLException { + return filenameToURL(myBaseLocation, filename); + } +} diff --git a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/data/LearnerSettings.java b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/data/LearnerSettings.java new file mode 100644 index 0000000000000000000000000000000000000000..ff1b56367db8aae66e7d559f603886a7847f9572 --- /dev/null +++ b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/data/LearnerSettings.java @@ -0,0 +1,41 @@ +package de.tudresden.inf.st.eraser.feedbackloop.learner_backup.data; + +import com.fasterxml.jackson.annotation.JsonInclude; +import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.Learner; +import de.tudresden.inf.st.eraser.util.ParserUtils; +import org.encog.ml.factory.MLMethodFactory; +import org.encog.ml.model.EncogModel; + +import java.io.IOException; +import java.net.URL; +import java.util.List; + +/** + * Settings to initialize the {@link Learner}. + * + * @author rschoene - Initial contribution + */ +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class LearnerSettings { + /** Description what this learner is about to learn */ + public String name; + /** All available columns (of the CSV used for training, may be set to be ignored) */ + public List<SimpleColumnDefinition> columns; + /** Whether to be verbose while training */ + public boolean verboseTraining = false; + /** Training method */ + public String trainingMethod = MLMethodFactory.TYPE_FEEDFORWARD; + /** Training parameter. Used in {@link EncogModel#holdBackValidation(double, boolean, int)} */ + public double validationPercent = 0.3; + /** Training parameter. Used in {@link EncogModel#holdBackValidation(double, boolean, int)} + * and {@link EncogModel#crossvalidate(int, boolean)} */ + public boolean shuffleForValidation = true; + /** Training parameter. Used in {@link EncogModel#holdBackValidation(double, boolean, int)} */ + public int validationSeed = 1001; + /** Training parameter. Used in {@link EncogModel#crossvalidate(int, boolean)} */ + public int validationFolds = 5; + + public static LearnerSettings loadFrom(URL location) throws IOException { + return ParserUtils.loadFrom(location, LearnerSettings.class); + } +} diff --git a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/data/NormalizationHelperData.java b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/data/NormalizationHelperData.java new file mode 100644 index 0000000000000000000000000000000000000000..1f89a300015b50de605482ae74dea7f31847e315 --- /dev/null +++ b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/data/NormalizationHelperData.java @@ -0,0 +1,33 @@ +package de.tudresden.inf.st.eraser.feedbackloop.learner_backup.data; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Settings to initialize {@link org.encog.ml.data.versatile.NormalizationHelper}. + * + * @author rschoene - Initial contribution + */ +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class NormalizationHelperData { + public List<SimpleColumnDefinition> sourceColumns; + public List<SimpleColumnDefinition> inputColumns; + public List<SimpleColumnDefinition> outputColumns; + public double inputLow; + public double inputHigh; + public double outputLow; + public double outputHigh; + public List<String> unknownValues = new ArrayList<>(); + public Map<String, Double> missingToMean = new HashMap<>(); + + public static NormalizationHelperData loadFrom(URL location) throws IOException { + return new ObjectMapper().readValue(location, NormalizationHelperData.class); + } +} diff --git a/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/data/SimpleColumnDefinition.java b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/data/SimpleColumnDefinition.java new file mode 100644 index 0000000000000000000000000000000000000000..e8d1f905fae2b53fd417d0f812a5c852cff20597 --- /dev/null +++ b/feedbackloop.learner_backup/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/data/SimpleColumnDefinition.java @@ -0,0 +1,34 @@ +package de.tudresden.inf.st.eraser.feedbackloop.learner_backup.data; + +import com.fasterxml.jackson.annotation.JsonInclude; +import org.encog.ml.data.versatile.columns.ColumnType; + +import java.util.ArrayList; +import java.util.List; + +/** + * Simple, serializable representation of a {@link org.encog.ml.data.versatile.columns.ColumnDefinition}. + * + * @author rschoene - Initial contribution + */ +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class SimpleColumnDefinition { + public String name; + /** {@link ColumnType} is either: nominal, ordinal, continuous, or ignore */ + public ColumnType type; + public int count = -1; + public int index = -1; + public double low = Double.NaN; + public double high = Double.NaN; + public double mean = Double.NaN; + public double sd = Double.NaN; + public List<String> classes = new ArrayList<>(); + /** {@link ColumnKind} is either input, target, or ignored */ + public ColumnKind kind; + + public enum ColumnKind { + input, + target, + ignored + } +} diff --git a/feedbackloop.learner_backup/src/main/resources/2019-oct-28-activity_definition.json b/feedbackloop.learner_backup/src/main/resources/2019-oct-28-activity_definition.json new file mode 100644 index 0000000000000000000000000000000000000000..1078c7fd6d309f2541ecf899d7ff9ce9df5d6048 --- /dev/null +++ b/feedbackloop.learner_backup/src/main/resources/2019-oct-28-activity_definition.json @@ -0,0 +1,41 @@ +{ + "name": "activity", + "columns": [ + { "kind": "input", "name": "Date", "type": "ignore" }, + { "kind": "input", "name": "time", "type": "ignore" }, + { "kind": "target", "name": "activity_label", "type": "nominal" }, + { "kind": "input", "name": "grideye_room_window", "type": "ignore" }, + { "kind": "input", "name": "grideye_room_door", "type": "ignore" }, + { "kind": "input", "name": "Fibaro_2_work_motion", "type": "nominal" }, + { "kind": "input", "name": "H2_weather_temp", "type": "ignore" }, + { "kind": "input", "name": "H2_weather_pressure", "type": "ignore" }, + { "kind": "input", "name": "H2_weather_hum", "type": "ignore" }, + { "kind": "input", "name": "Fibaro_4_TV_motion", "type": "nominal" }, + { "kind": "input", "name": "Fibaro_3_door_motion", "type": "nominal" }, + { "kind": "input", "name": "Fibaro_11_OpenClose", "type": "nominal" }, + { "kind": "input", "name": "Aeotec_8_room_Temp", "type": "ignore" }, + { "kind": "input", "name": "Aeotec_8_room_lum", "type": "ignore" }, + { "kind": "input", "name": "Aeotec_8_room_Hum", "type": "ignore" }, + { "kind": "input", "name": "Aeotec_8_room_uv", "type": "ignore" }, + { "kind": "input", "name": "Fibaro_5_room_Temp", "type": "ignore" }, + { "kind": "input", "name": "Fibaro_5_room_lum", "type": "ignore" }, + { "kind": "input", "name": "Fibaro_3_door_lum", "type": "ignore" }, + { "kind": "input", "name": "Fibaro_3_door_Temp", "type": "ignore" }, + { "kind": "input", "name": "Fibaro_2_work_lum", "type": "ignore" }, + { "kind": "input", "name": "Fibaro_4_TV_Temp", "type": "ignore" }, + { "kind": "input", "name": "Fibaro_4_TV_lum", "type": "ignore" }, + { "kind": "input", "name": "Fibaro_2_work_Temp", "type": "ignore" }, + { "kind": "input", "name": "TV_OnOff", "type": "ignore" }, + { "kind": "input", "name": "DPS310_pressure_room", "type": "ignore" }, + { "kind": "input", "name": "DPS310_temp_room", "type": "ignore" }, + { "kind": "input", "name": "DPS310_altitude_room", "type": "ignore" }, + { "kind": "input", "name": "work_device_online_state", "type": "nominal" }, + { "kind": "input", "name": "Fibaro_15_OpenClose", "type": "ignore" }, + { "kind": "input", "name": "GridEye_room_window_x", "type": "continuous" }, + { "kind": "input", "name": "GridEye_room_door_y", "type": "continuous" }, + { "kind": "input", "name": "GridEye_room_door_x", "type": "continuous" }, + { "kind": "input", "name": "GridEye_room_window_y", "type": "continuous"} + ], + "verboseTraining": true, + "validationFolds": 3 +} diff --git a/feedbackloop.learner_backup/src/main/resources/2019-oct-28-activity_network.eg b/feedbackloop.learner_backup/src/main/resources/2019-oct-28-activity_network.eg new file mode 100644 index 0000000000000000000000000000000000000000..f2cf7839a7bfb7630f8ecfd20ba337f5e1775fbb --- /dev/null +++ b/feedbackloop.learner_backup/src/main/resources/2019-oct-28-activity_network.eg @@ -0,0 +1,24 @@ +encog,BasicNetwork,java,3.4.0,1,1574428376334 +[BASIC] +[BASIC:PARAMS] +[BASIC:NETWORK] +beginTraining=0 +connectionLimit=0 +contextTargetOffset=0,0,0 +contextTargetSize=0,0,0 +endTraining=2 +hasContext=f +inputCount=19 +layerCounts=7,16,20 +layerFeedCounts=7,15,19 +layerContextCount=0,0,0 +layerIndex=0,7,23 +output=-0.340836329,-0.3423459838,-0.9971504865,-0.994969938,-1,-0.9999999999,-0.3279989961,-0.9797933007,-0.9984924393,-0.9999897248,-0.9998031211,-0.9898578199,0.9998409154,0.9972211116,0.9805206206,-0.9932796274,-0.9544652313,0.9968045856,-0.9445173438,-0.9995702614,-0.9977451063,0.9999910694,1,-1,1,-1,-1,-1,1,1,-1,-1,-1,1,-1,-1,-1,1,0.9921568627,0.3833992095,0.1529411765,-1,1 +outputCount=7 +weightIndex=0,112,412 +weights=-0.404137706,-0.2885181231,0.9668734481,0.0062269301,1.0128231886,0.0089469213,0.4585189612,-0.0894394019,-0.0797318623,-0.3468553546,-1.2300755961,-0.3468429248,-0.5145871857,-0.2848383941,-0.3264209087,0.5741982601,0.27127421,-0.5434136125,0.5485149158,0.5003078803,-0.2698965768,0.7983927357,-0.0333252064,0.3447572796,-0.1882659481,0.7247422012,0.3064477151,-1.0138871821,0.1416051,0.6583373536,0.2705332613,-1.1855603499,0.9929388464,0.5796871274,-0.3399669435,0.8569628254,0.2075255413,-0.5600042384,-0.4138292438,0.6538720983,-0.6923520436,-0.3319059439,-0.4537568575,-0.0025677392,-0.6905085845,0.9053047001,-0.6162521138,-0.3960775091,-0.4368636028,-0.9001436588,1.0750416574,0.7660161008,-0.5990507184,-0.2425415361,0.0274320766,-0.3558952255,-0.71936614,0.5830552943,-0.171895279,0.1974145518,0.7948972407,1.0487310214,-0.566601431,0.1010319343,0.172866604,3.0551601629,3.0000824008,0.3866019399,2.7196342543,-13.0154758675,-6.9544569017,4.3141833144,-2.4103093678,0.2125104309,0.7730445211,-7.4019442181,0.099425633,-10.1082331563,-3.2519692,-4.1723085126,1.1032763871,1.434494168,1.0655828482,2.259228432,0.4309682109,0.8421555229,-1.2418354028,-0.7857195285,-0.1460260544,5.7534066269,-2.0174021556,0.9996651657,-3.1600904683,-3.926442431,-0.780245191,-2.6582836523,-0.3842677407,0.8001100617,0.0932911402,0.8260885108,0.3243592641,0.929464689,0.0688818482,0.2795366272,-0.8677541061,0.1766944912,-0.5401835409,-0.1872442079,0.7241379909,-0.4751260736,-0.7012144045,0.6693119708,0.9182400855,-0.8851888016,0.4365092184,0.2855724153,-0.5807331971,-0.5545832569,-0.4287869651,-0.4256322973,-0.1423483588,0.3741760439,0.0626357057,-0.2969373002,0.1952954195,0.3347612998,-0.319608364,-0.0253925855,-0.25709248,0.0673034925,-0.0089497179,1.0365875369,-0.3911295373,-0.077826642,0.5508385817,-0.3285681225,0.5657971033,-0.2066801958,0.1686295498,0.2070775065,0.9912375579,-0.4753575264,-0.2726487663,0.2664021737,1.3515632261,0.4126524074,0.1782886902,-0.0285599614,-0.0603748382,-0.0374828481,-0.0914111075,-0.268271809,0.0852087324,0.5126836126,0.2546340519,0.2746173051,2.817153954,0.7249724018,-0.6981285839,-0.4985126373,0.8604969349,0.2037431513,2.7264477402,1.2330380603,-0.0167592164,-0.144188394,-0.7301846959,0.4337688818,0.1298617096,-0.4470460904,-0.1921802707,-4.1597800015,-0.4025492346,0.0229041912,1.306807566,0.3525340482,0.6127139965,0.5393598738,0.6929584822,0.7056334137,1.1994301985,0.4496289046,0.2941309986,-0.2056291639,1.251880871,-0.010770356,-0.9810071383,0.0064674917,-0.0176386919,-0.048016511,-0.0107097517,0.0753283256,0.7404769463,-0.5929286192,-0.5078274499,0.4259262815,0.143754059,0.1359612229,0.2541697513,0.2510065262,0.6709506571,-0.148350937,1.1900545736,0.749742294,-0.6287601463,0.6900848995,-0.2690878318,0.0001816587,0.0455003469,0.0309224531,-0.0076091205,-1.0006871788,0.2681613331,0.1833383853,-0.5172206492,0.6892488375,-0.7451485095,0.4276582331,0.3742194161,0.4014722088,1.0994959368,0.847787554,0.7638290785,-0.7914432342,-0.4420336184,-0.9158573706,2.6389325839,0.0887550998,-0.0569580742,-0.0392462129,-0.0239489707,0.1418880057,1.0896267359,0.9904016473,-1.0607560941,1.6784823518,-0.9433720747,0.0257586137,0.4714302624,0.4166775721,-0.6066347851,1.6444284094,0.9257977982,-1.0381297318,-1.7311006544,1.1774799465,1.877862961,-0.2855399619,0.0755531811,0.0424919604,0.2678753077,0.1398908597,0.8214292233,0.9009702295,0.1469390864,0.4637993899,-0.2313547534,0.1448407563,1.0392025508,0.9881708464,-0.7406734936,0.4983164436,0.2939713585,-0.1343176987,-1.112507143,0.3883191946,0.1738987071,-0.07073642,0.050403588,0.0765184646,-0.0005174206,0.8847534729,0.7436968164,-1.0771556764,0.1100227689,-0.0614329979,-0.1622374928,-0.1525778093,-0.6077259941,-0.619774004,0.3603468196,-0.2009073954,-0.3733319916,-0.0788296869,0.6074303689,-0.4126291585,-0.9028665297,-0.0099997932,-0.0001654564,0.0148504563,-0.0231406351,0.5377204482,-0.5777914567,-0.1781865678,1.1415129984,0.2834037016,-0.5539063514,-0.4515762597,0.1404508438,0.1187112233,0.6532769069,-1.2829520787,-0.5799936288,-0.2116382272,0.2306003109,1.0953413393,-0.3323463029,0.0225181859,0.0589642565,0.0426297362,-0.040109034,0.3269351263,-0.5321326898,0.9175722208,-0.6271480872,-0.7459771441,0.0158692044,0.0311943145,-0.5266229614,-0.5225480734,-0.330023217,-0.5138457028,-0.0246451645,-0.2378591164,-0.8257893027,-0.1187365978,-0.8009636381,0.0004696224,0.0525540439,0.015912768,0.0008598419,-0.8381211463,-0.4135458004,0.0319505462,0.4168831222,-1.6027457971,-0.726313219,-0.3371409293,-1.942373089,-1.9681611004,0.3375429492,0.0455411676,0.2193321201,0.5576102386,0.2043673413,0.5864664222,-1.9789533345,0.0541247202,0.0236840469,-0.0086069514,-0.0407470423,-0.4357306331,1.9243856045,-1.3208328316,-0.0954191506,0.0808587123,0.3690795603,0.1095208965,-0.0382497008,-0.0336668021,-0.2167623054,0.0806856928,-0.6457893128,-0.6880786727,0.1245176541,-0.2603043844,-0.8839399611,-0.0815347261,-0.202848777,0.1924881481,-0.1531643391,-0.1819825028,-0.7487125691,-1.0617607253,-0.0682555272,0.1203110094,-0.0307641525,-0.6370047961,0.3619059939,0.3858454328,-0.1968004594,-0.4804011029,-0.3394977591,-0.0261051038,0.1742859157,0.5756254766,-2.5706321037,0.0797072295,0.3687297587,0.0907303422,-0.0206407321,0.3037126261,-0.9534375329,0.8626140311,0.2956554426,0.2553834042,-0.8520628339,-0.8594849437,-0.0600253851,-0.0390563524,-1.4805598205,-0.2333029476,-0.8993510452,-0.9759839461,0.0934807631,-1.6299950666,0.5668144115,0.0068186589,-0.0746488856,0.0060924025,-0.0051572226,1.0449041956 +biasActivation=0,1,1 +[BASIC:ACTIVATION] +"org.encog.engine.network.activation.ActivationTANH" +"org.encog.engine.network.activation.ActivationTANH" +"org.encog.engine.network.activation.ActivationLinear" diff --git a/feedbackloop.learner_backup/src/main/resources/2019-oct-28-activity_normalizer.json b/feedbackloop.learner_backup/src/main/resources/2019-oct-28-activity_normalizer.json new file mode 100644 index 0000000000000000000000000000000000000000..a3be23ca034d201f72abdbe2dccadeaec3352c39 --- /dev/null +++ b/feedbackloop.learner_backup/src/main/resources/2019-oct-28-activity_normalizer.json @@ -0,0 +1,238 @@ +{ + "sourceColumns" : [ { + "name" : "Date", + "type" : "ignore", + "index" : 0 + }, { + "name" : "time", + "type" : "ignore", + "index" : 1 + }, { + "name" : "activity_label", + "type" : "nominal", + "index" : 2, + "classes" : [ "DoorClosedPOut", "DoorOpenedPOut", "DoorClosedPIn", "Working", "Reading", "TVWatching", "DoorOpenedPIn" ] + }, { + "name" : "grideye_room_window", + "type" : "ignore", + "index" : 3 + }, { + "name" : "grideye_room_door", + "type" : "ignore", + "index" : 4 + }, { + "name" : "Fibaro_2_work_motion", + "type" : "nominal", + "index" : 5, + "classes" : [ "ON", "OFF", "\\N" ] + }, { + "name" : "H2_weather_temp", + "type" : "ignore", + "index" : 6 + }, { + "name" : "H2_weather_pressure", + "type" : "ignore", + "index" : 7 + }, { + "name" : "H2_weather_hum", + "type" : "ignore", + "index" : 8 + }, { + "name" : "Fibaro_4_TV_motion", + "type" : "nominal", + "index" : 9, + "classes" : [ "\\N", "ON", "OFF" ] + }, { + "name" : "Fibaro_3_door_motion", + "type" : "nominal", + "index" : 10, + "classes" : [ "OFF", "ON", "\\N" ] + }, { + "name" : "Fibaro_11_OpenClose", + "type" : "nominal", + "index" : 11, + "classes" : [ "CLOSED", "OPEN", "\\N" ] + }, { + "name" : "Aeotec_8_room_Temp", + "type" : "ignore", + "index" : 12 + }, { + "name" : "Aeotec_8_room_lum", + "type" : "ignore", + "index" : 13 + }, { + "name" : "Aeotec_8_room_Hum", + "type" : "ignore", + "index" : 14 + }, { + "name" : "Aeotec_8_room_uv", + "type" : "ignore", + "index" : 15 + }, { + "name" : "Fibaro_5_room_Temp", + "type" : "ignore", + "index" : 16 + }, { + "name" : "Fibaro_5_room_lum", + "type" : "ignore", + "index" : 17 + }, { + "name" : "Fibaro_3_door_lum", + "type" : "ignore", + "index" : 18 + }, { + "name" : "Fibaro_3_door_Temp", + "type" : "ignore", + "index" : 19 + }, { + "name" : "Fibaro_2_work_lum", + "type" : "ignore", + "index" : 20 + }, { + "name" : "Fibaro_4_TV_Temp", + "type" : "ignore", + "index" : 21 + }, { + "name" : "Fibaro_4_TV_lum", + "type" : "ignore", + "index" : 22 + }, { + "name" : "Fibaro_2_work_Temp", + "type" : "ignore", + "index" : 23 + }, { + "name" : "TV_OnOff", + "type" : "ignore", + "index" : 24 + }, { + "name" : "DPS310_pressure_room", + "type" : "ignore", + "index" : 25 + }, { + "name" : "DPS310_temp_room", + "type" : "ignore", + "index" : 26 + }, { + "name" : "DPS310_altitude_room", + "type" : "ignore", + "index" : 27 + }, { + "name" : "work_device_online_state", + "type" : "nominal", + "index" : 28, + "classes" : [ "ON", "OFF", "\\N" ] + }, { + "name" : "Fibaro_15_OpenClose", + "type" : "ignore", + "index" : 29 + }, { + "name" : "GridEye_room_window_x", + "type" : "continuous", + "count" : 195108, + "index" : 30, + "low" : 0.0, + "high" : 255.0, + "mean" : 187.7175666810177, + "sd" : 84.78936592617109 + }, { + "name" : "GridEye_room_door_y", + "type" : "continuous", + "count" : 195108, + "index" : 31, + "low" : 0.0, + "high" : 253.0, + "mean" : 180.47390163396682, + "sd" : 45.67432497413589 + }, { + "name" : "GridEye_room_door_x", + "type" : "continuous", + "count" : 195108, + "index" : 32, + "low" : 0.0, + "high" : 255.0, + "mean" : 159.02686717100272, + "sd" : 53.62353439276967 + }, { + "name" : "GridEye_room_window_y", + "type" : "continuous", + "count" : 195108, + "index" : 33, + "low" : 0.0, + "high" : 252.0, + "mean" : 123.94642966972138, + "sd" : 81.4972896448954 + } ], + "inputColumns" : [ { + "name" : "Fibaro_2_work_motion", + "type" : "nominal", + "index" : 5, + "classes" : [ "ON", "OFF", "\\N" ] + }, { + "name" : "Fibaro_4_TV_motion", + "type" : "nominal", + "index" : 9, + "classes" : [ "\\N", "ON", "OFF" ] + }, { + "name" : "Fibaro_3_door_motion", + "type" : "nominal", + "index" : 10, + "classes" : [ "OFF", "ON", "\\N" ] + }, { + "name" : "Fibaro_11_OpenClose", + "type" : "nominal", + "index" : 11, + "classes" : [ "CLOSED", "OPEN", "\\N" ] + }, { + "name" : "work_device_online_state", + "type" : "nominal", + "index" : 28, + "classes" : [ "ON", "OFF", "\\N" ] + }, { + "name" : "GridEye_room_window_x", + "type" : "continuous", + "count" : 195108, + "index" : 30, + "low" : 0.0, + "high" : 255.0, + "mean" : 187.7175666810177, + "sd" : 84.78936592617109 + }, { + "name" : "GridEye_room_door_y", + "type" : "continuous", + "count" : 195108, + "index" : 31, + "low" : 0.0, + "high" : 253.0, + "mean" : 180.47390163396682, + "sd" : 45.67432497413589 + }, { + "name" : "GridEye_room_door_x", + "type" : "continuous", + "count" : 195108, + "index" : 32, + "low" : 0.0, + "high" : 255.0, + "mean" : 159.02686717100272, + "sd" : 53.62353439276967 + }, { + "name" : "GridEye_room_window_y", + "type" : "continuous", + "count" : 195108, + "index" : 33, + "low" : 0.0, + "high" : 252.0, + "mean" : 123.94642966972138, + "sd" : 81.4972896448954 + } ], + "outputColumns" : [ { + "name" : "activity_label", + "type" : "nominal", + "index" : 2, + "classes" : [ "DoorClosedPOut", "DoorOpenedPOut", "DoorClosedPIn", "Working", "Reading", "TVWatching", "DoorOpenedPIn" ] + } ], + "inputLow" : -1.0, + "inputHigh" : 1.0, + "outputLow" : -1.0, + "outputHigh" : 1.0, + "unknownValues" : [ null ] +} \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/main/resources/2019-oct-28-learner.json b/feedbackloop.learner_backup/src/main/resources/2019-oct-28-learner.json new file mode 100644 index 0000000000000000000000000000000000000000..2b7359f39b3caf204da311bd03e4ac6333879595 --- /dev/null +++ b/feedbackloop.learner_backup/src/main/resources/2019-oct-28-learner.json @@ -0,0 +1,25 @@ +{ + "relevantItemNames": [ + "Fibaro_2_work_motion", + "Fibaro_4_TV_motion", + "Fibaro_3_door_motion", + "Fibaro_11_OpenClose", + "work_device_online_state", + "GridEye_room_window_x", + "GridEye_room_door_y", + "GridEye_room_door_x", + "GridEye_room_window_y" + ], + "targetItemNames": [ + "activity" + ], + "nonTrivialOutputMappings": { + "activity_label": "activity" + }, + "definitionFile": "./2019-oct-28-activity_definition.json", + "dataFiles": [ + "activity_data/28_08_2019_H14_14/result_all_items_EVERYTHING.csv" + ], + "csvFormat": "DECIMAL_COMMA", + "saveModels": true +} diff --git a/feedbackloop.learner_backup/src/main/resources/2019-oct-28-loaded_learner.json b/feedbackloop.learner_backup/src/main/resources/2019-oct-28-loaded_learner.json new file mode 100644 index 0000000000000000000000000000000000000000..f476f3571dc5283d0f1c0eccc90c18bd833c9479 --- /dev/null +++ b/feedbackloop.learner_backup/src/main/resources/2019-oct-28-loaded_learner.json @@ -0,0 +1,26 @@ +{ + "relevantItemNames": [ + "Fibaro_2_work_motion", + "Fibaro_4_TV_motion", + "Fibaro_3_door_motion", + "Fibaro_11_OpenClose", + "work_device_online_state", + "GridEye_room_window_x", + "GridEye_room_door_y", + "GridEye_room_door_x", + "GridEye_room_window_y" + ], + "targetItemNames": [ + "activity" + ], + "nonTrivialOutputMappings": { + "activity_label": "activity" + }, + "definitionFile": "./2019-oct-28-activity_definition.json", + "dataFiles": [ + "./2019-oct-28-activity_network.eg", + "./2019-oct-28-activity_normalizer.json" + ], + "csvFormat": "DECIMAL_COMMA", + "kind": "loaded" +} diff --git a/feedbackloop.learner_backup/src/main/resources/activity_data.csv b/feedbackloop.learner_backup/src/main/resources/activity_data.csv new file mode 100644 index 0000000000000000000000000000000000000000..c32f9706f812e54cc6dc58a1876c806503530d9e --- /dev/null +++ b/feedbackloop.learner_backup/src/main/resources/activity_data.csv @@ -0,0 +1,369 @@ +0.2717419,8.698134,4.471172,0.043741,0.515962,0.854318,1.8818425,4.9320555,8.145074,0.2374878,-0.032836914,0.3381958,working +0.2813187,8.604761,4.55856,0.041658,0.510887,0.857407,1.8818425,4.9320555,8.145074,0.2374878,-0.032836914,0.3381958,working +0.23822308,8.622718,4.406528,0.03991,0.512086,0.856781,1.8818425,4.9320555,8.145074,0.2374878,-0.032836914,0.3381958,working +0.11731588,8.619126,4.4783545,0.038642,0.513641,0.855887,2.0590134,5.7125654,6.4547677,0.2374878,-0.032836914,0.3381958,working +0.24181437,8.669404,4.486734,0.04219,0.515633,0.854455,2.0829554,4.6112323,7.302315,0.30133057,0.005004883,0.3652954,working +0.19632454,8.66701,4.4556093,0.041381,0.51905,0.852327,0.21547815,-1.0773908,9.294291,0.0087890625,-0.002319336,0.5358887,working +0.23343466,8.623915,4.4328647,0.042787,0.516075,0.853984,0.7757214,3.490746,9.370906,0.10900879,0.048583984,0.53826904,working +0.18555063,8.669404,4.437653,0.043233,0.516444,0.853727,1.1109096,2.1978772,9.299079,0.056274414,0.01751709,0.55133057,working +0.21547815,8.698134,4.4125137,0.043405,0.515751,0.854099,0.36391866,-0.8283938,9.763555,-0.009521484,-0.016784668,0.5675049,working +0.3890578,8.66701,4.4520183,0.039945,0.519914,0.851807,1.1875241,-1.8243817,7.465121,0.04333496,-0.003479004,0.5588989,working +0.12808979,8.670602,4.3634324,0.039883,0.521765,0.850612,1.1875241,-1.8243817,7.465121,0.04333496,-0.003479004,0.5588989,working +0.10654198,8.6574335,4.414908,0.040269,0.516884,0.853285,1.2066777,-1.1444284,9.648633,1.2207031E-4,-0.055358887,0.5834961,working +0.08140286,8.76637,4.1539397,0.039312,0.527554,0.84693,0.44532153,-2.1452048,9.543288,-0.05847168,-0.039367676,0.52716064,working +0.17956513,8.698134,4.1814733,0.041489,0.52054,0.850801,0.6368576,-0.6512229,9.998186,0.034851074,-0.028747559,0.53570557,working +0.02633622,8.651448,4.3873744,0.040486,0.520068,0.850974,1.508347,0.19632454,9.749189,0.08312988,-0.020568848,0.57818604,working +0.010773907,8.610746,4.4963107,0.047136,0.515427,0.852877,0.9720459,-1.3694834,9.696517,-0.0056152344,-0.049438477,0.5576782,working +0.21906945,8.929175,3.6032736,0.047929,0.528586,0.845337,1.043872,-0.59855044,10.160992,0.0053100586,-0.024353027,0.52856445,working +-0.15801731,8.755595,4.423288,0.048114,0.519962,0.849552,1.1923125,-0.08140286,9.849746,0.052856445,-0.003540039,0.5180054,working +0.05865794,8.665813,4.332308,0.045692,0.518734,0.850569,2.5905263,0.90021986,9.873688,0.12976074,-0.029785156,0.532959,working +0.06584055,8.605958,4.477157,0.048499,0.515082,0.852584,2.327164,2.0398598,8.001422,0.20056152,0.003479004,0.48242188,working +0.05865794,8.631097,4.3861775,0.046089,0.516823,0.851908,2.0111294,1.2354081,9.006987,0.16241455,-0.02746582,0.46368408,working +-0.017956512,8.627506,4.4675803,0.047409,0.513421,0.853688,1.1779473,-0.51235914,9.773131,0.057434082,-0.011291504,0.5081787,working +-0.18794483,8.659827,4.4783545,0.041528,0.515836,0.852265,0.9959879,0.7469909,9.672575,0.07373047,-0.002319336,0.5019531,working +-0.027533319,8.591593,4.620809,0.046683,0.510911,0.854784,3.0550013,3.6487634,8.48984,0.25952148,-0.010253906,0.43432617,working +-0.08140286,8.716091,4.332308,0.042494,0.517638,0.85112,1.3455414,-0.49320555,9.89763,0.034423828,-0.038757324,0.4852295,working +-0.22146365,8.836999,4.1036615,0.038773,0.531266,0.842643,2.2266076,3.2369606,8.465898,0.26757812,0.02154541,0.43115234,working +1.0761937,11.745954,2.4648306,0.526819,0.526535,0.468877,-9.270349,-2.4372973,1.0390835,-0.7081909,-0.08135986,0.6744995,walking +-1.8710686,8.711303,-1.5227122,0.54441,0.510167,0.46156,-9.270349,-2.4372973,1.0390835,-0.7081909,-0.08135986,0.6744995,walking +-0.63087213,9.64983,-2.3463178,0.218222,0.734833,0.617411,-11.353305,-1.388637,0.16759412,-0.6591797,-0.101745605,0.7449341,walking +-0.10055647,9.488221,0.008379706,0.520795,-0.531387,-0.481526,-8.963891,-0.51235914,-0.3878607,-0.51483154,-0.0703125,0.83361816,walking +2.2026656,10.09156,-3.4476504,0.512742,-0.552492,-0.477334,-15.552734,-0.7517793,0.48362875,-0.6300659,-0.072631836,0.772522,walking +0.09816227,9.627085,-1.8674773,0.54604,-0.514798,-0.454878,-13.747506,-2.2984335,-2.5139117,-0.34100342,-0.17474365,0.42633057,walking +-0.7781156,8.385692,-3.1112652,0.50674,-0.550405,-0.502163,-10.745177,-2.27928,1.2785037,-0.03930664,-0.30413818,0.26208496,walking +-0.18674773,9.277532,-1.1683705,0.548857,-0.509167,-0.470289,-9.150639,1.6232687,0.16280572,0.030029297,-0.38269043,0.19390869,walking +-0.6823475,10.573992,-2.553416,0.679861,-0.341986,-0.292999,-7.52737,-2.1452048,0.9672575,0.3192749,-0.3623047,0.18859863,walking +-0.2765303,11.451467,-0.30645782,0.563436,0.508963,0.451556,-8.384495,-0.80924016,-0.7134721,0.16137695,-0.67700195,0.019042969,walking +1.2916719,9.548077,-1.3000516,0.51573,0.538749,0.509015,-7.28795,-1.0630256,0.23463176,0.26348877,-0.6868286,-0.1529541,walking +0.8379706,10.741586,0.45489833,0.54087,0.51788,0.4762,-9.370906,-0.48362875,1.7286136,0.18725586,-0.7562256,-0.11633301,walking +0.59495914,9.346964,-1.6639702,0.502446,0.561492,0.502803,-9.605537,-2.5905263,0.05746084,0.13793945,-0.74884033,-0.1798706,walking +0.93014735,10.562021,-0.16519992,0.523761,0.542328,0.462923,-9.481039,-2.4277205,-0.7230489,0.42114258,-0.3991089,-0.73480225,walking +0.08738836,9.709685,-1.5191209,0.562041,0.534199,0.433607,-12.689269,-1.6472107,0.34476504,0.7036743,0.074035645,-0.63220215,walking +0.18076223,9.118318,-1.3084313,0.254447,-0.716063,-0.601747,-8.485051,-2.4133554,-1.3599066,0.15490723,0.4364624,-0.1907959,walking +-0.18435353,9.961076,-1.3263878,0.261971,-0.708253,-0.606287,-10.0508585,-1.0199299,0.8331822,0.113220215,0.6170654,-0.04260254,walking +-0.688333,8.623915,-0.7649474,0.467896,-0.599728,-0.506172,-6.7803793,-0.521936,0.2681506,-0.20965576,0.58239746,-0.020568848,walking +-0.22744916,9.506178,-1.1575965,0.49718,-0.585651,-0.491387,-9.394848,-2.0494366,-0.33997664,-0.18896484,0.69592285,0.20074463,walking +-1.1348516,10.6877165,-0.9516952,0.519502,-0.571514,-0.466647,-8.714894,-0.7278373,-0.8810662,-0.18896484,0.69592285,0.20074463,walking +0.8882488,10.172963,-0.6715736,0.52846,-0.55123,-0.467232,-9.56723,-0.80445176,-0.4357447,-0.22363281,0.6533203,0.2144165,walking +-0.8511387,8.486248,-1.1384429,0.491758,-0.577708,-0.488183,-11.305421,-4.060566,-1.0055647,-0.18109131,0.64508057,0.1262207,walking +0.023942016,11.11987,1.3946224,0.51358,-0.536144,-0.482607,3.8546648,5.104438,6.799533,0.1015625,-0.27069092,-0.4434204,walking +-0.5470751,9.082404,-1.6232687,0.42116,-0.616129,-0.547717,-8.441956,-1.6424223,0.320823,-0.20501709,0.6883545,0.33581543,walking +-1.4808137,9.829395,-4.5178585,0.00118,0.776792,0.629414,-9.768343,-3.2609026,0.272939,-0.20495605,0.69610596,0.3265381,walking +-1.4257472,9.561244,-0.8643068,0.450442,0.612177,0.47736,-9.768343,-3.2609026,0.272939,-0.20495605,0.69610596,0.3265381,walking +-1.5466543,11.438298,-2.468422,0.496624,0.573871,0.452744,-9.768343,-3.2609026,0.272939,-0.20495605,0.69610596,0.3265381,walking +-2.6252422,8.619126,-2.7030537,0.552147,0.5078,0.450302,-8.1881695,-1.2641385,0.038307227,-0.34222412,0.49102783,-0.016540527,walking +-0.21308395,9.40203,-2.1691468,0.599439,0.476026,0.396271,-6.339846,-1.3503298,-0.033518825,-0.35205078,0.44171143,-0.16430664,walking +-0.08140286,8.789114,-2.3690627,0.593409,0.464892,0.410283,-8.432379,-4.2999864,1.4700398,-0.24761963,0.57470703,0.45324707,walking +-0.8559271,9.961076,-0.90021986,0.555447,0.518125,0.43304,-9.509769,-2.006341,-2.1499932,0.13470459,0.65411377,0.0036010742,walking +-0.2801216,8.352173,-0.33638534,0.558818,0.514925,0.445665,-7.8290396,-3.596091,-0.79008657,0.16339111,0.60461426,-0.7661743,walking +-0.08738836,9.898827,-1.9524715,0.508351,0.559775,0.486663,-10.352529,-2.0590134,0.22505496,0.59747314,0.28381348,-0.71154785,walking +2.1930888,7.1550717,-3.1411927,0.192016,0.739159,0.626426,-8.556877,-1.412579,0.78529817,0.6008911,0.26306152,-0.72491455,walking +-0.92895025,11.055226,-0.23822308,0.493986,-0.554274,-0.504623,-13.939042,-2.0590134,1.3934253,0.64434814,0.144104,-0.7451172,walking +0.21188685,8.973468,-1.0235212,0.550447,-0.505433,-0.448153,-13.024457,-2.873042,1.1875241,0.5584717,0.3137207,-0.7282715,walking +-0.24779987,12.925097,-3.788824,0.53726,-0.52702,-0.453356,-11.741165,-2.5570073,1.3982137,0.6694946,0.21734619,-0.6765747,walking +-2.1547816,13.4183035,-6.009446,0.323083,-0.661886,-0.619246,-8.001422,-2.4995465,1.6376339,0.4973755,0.46984863,-0.6560669,walking +-1.2593501,8.961497,-1.7465701,0.196693,-0.740158,-0.62706,-12.808979,-2.5761611,0.10055647,0.5979004,0.381958,-0.6482544,walking +-0.1328782,9.147048,-2.9053638,0.022181,0.777414,0.627034,-7.6710224,-1.5753847,-0.320823,0.4567871,0.2935791,-0.8391113,walking +0.7338228,9.568427,-6.634333,0.393491,0.730379,0.484185,-11.674128,0.17238252,1.7142484,0.7868042,0.18414307,-0.5524292,walking +-1.9716251,8.117541,-4.0761285,0.469076,0.718339,0.409424,-11.674128,0.17238252,1.7142484,0.7868042,0.18414307,-0.5524292,walking +0.32561144,9.016563,3.6092591,-0.018518,-0.561261,-0.827409,-12.650962,-2.0111294,0.5746084,0.78063965,0.17840576,-0.39233398,walking +0.090979666,9.075222,3.503914,0.010144,0.564181,0.825586,-8.2169,-0.18195933,0.24899697,0.74591064,0.1239624,-0.62054443,walking +0.19991584,9.054871,3.7086184,-0.012561,-0.560571,-0.82801,-9.385271,-4.0462008,-1.5562311,0.256958,0.36480713,-0.27624512,walking +0.21667525,9.060856,3.6415808,0.012755,0.558465,0.829429,-9.710882,-0.7757214,3.6870706,0.14190674,0.45703125,-0.21124268,walking +0.22744916,9.060856,3.645172,0.012841,0.558664,0.829293,-10.529699,-3.1316159,-0.272939,-0.29626465,0.49450684,0.17242432,walking +0.19512744,9.060856,3.6140475,0.01255,0.557529,0.830062,-9.509769,-0.8283938,0.9959879,-0.1918335,0.40795898,-0.080078125,walking +0.1328782,9.076419,3.5685577,-0.010446,-0.561843,-0.827177,-9.572019,-2.4947581,-1.6136919,0.13330078,0.41436768,-0.2218628,walking +-1.5095441,11.198878,-1.8387469,0.261779,-0.680799,-0.637011,-1.958457,-0.10055647,4.4675803,-0.04296875,-0.19110107,-0.9154053,dancing +-0.05267244,-5.6156,2.4037786,0.352709,-0.731645,-0.528224,18.703505,12.378023,14.5088625,-0.5602417,-0.23596191,-0.7937622,dancing +0.07062895,-1.5753847,-0.81761986,0.339088,-0.677723,-0.589453,18.703505,12.378023,14.5088625,-0.5602417,-0.23596191,-0.7937622,dancing +2.2732944,3.4225113,-2.6755204,0.118016,0.877354,0.45141,-12.794614,11.975797,11.530476,-0.47833252,-0.43554688,-0.7303467,dancing +-4.5561657,13.062764,-2.8646624,0.381358,-0.792455,-0.433683,-12.794614,11.975797,11.530476,-0.371521,-0.3826294,-0.53393555,dancing +4.5980644,-4.882974,2.19668,0.276273,-0.733631,-0.604661,-12.794614,11.975797,11.530476,-0.371521,-0.3826294,-0.53393555,dancing +-1.8016368,11.6442,-5.7664347,0.219061,-0.663912,-0.707763,-1.8626889,-9.198523,-16.357185,-0.7172241,-0.3291626,-0.37512207,dancing +-1.7992426,27.91759,-1.6220716,-0.082716,0.809359,0.578657,-3.1411927,-0.93373865,-0.31124622,-0.35992432,0.33746338,-0.79608154,dancing +0.5793968,1.2126632,-4.6244006,-0.030779,0.801127,0.590978,-3.1411927,-0.93373865,-0.31124622,-0.35992432,0.33746338,-0.79608154,dancing +0.48123455,35.582626,-8.6299,0.514118,0.515082,0.556824,1.2880806,3.8355112,2.3894134,-0.4446411,-0.15338135,-0.85528564,dancing +3.2692823,-1.8004397,-0.7769185,0.514916,0.374323,0.534814,-2.9400797,-4.3047748,-3.7732618,-0.40649414,-0.029296875,-0.8713379,dancing +2.4863784,-4.0258503,0.1352724,0.543841,-0.486144,-0.546646,-12.861651,-5.33907,-3.265691,-0.34906006,0.54486084,-0.69177246,dancing +0.39145198,8.514978,-7.3430166,0.520221,-0.546999,-0.566261,-10.213664,-8.327033,1.3072342,-0.07220459,0.12145996,-0.6904297,dancing +-4.309563,26.964697,-0.6811504,0.555781,-0.51674,-0.545712,-7.431602,3.940856,14.346057,-0.18426514,0.19262695,-0.8668823,dancing +-5.368997,26.76957,-7.6746135,0.573843,-0.547145,-0.523433,-12.871228,-8.714894,10.793061,-0.087768555,0.31750488,-0.8586426,dancing +-3.2513258,10.072407,-9.166202,0.561235,-0.526834,-0.535287,-7.393295,-1.9249382,-6.129156,-0.55218506,0.30267334,-0.60998535,dancing +-1.0989386,8.825027,-1.2078748,0.494579,-0.582964,-0.569548,-7.393295,-1.9249382,-6.129156,-0.55218506,0.30267334,-0.60998535,dancing +-1.4999673,9.7803135,-5.5521536,0.461249,-0.606122,-0.597372,-7.393295,-1.9249382,-6.129156,-0.55218506,0.30267334,-0.60998535,dancing +-4.3993454,13.245921,-4.8722005,0.505116,-0.582591,-0.566936,-8.111555,-4.3718123,-3.0789435,-0.10809326,0.3184204,-0.6734009,dancing +-2.7748797,12.2080345,-7.835025,0.412957,-0.585669,-0.636403,-8.111555,-4.3718123,-3.0789435,-0.10809326,0.3184204,-0.6734009,dancing +-0.9995792,16.71871,-6.7013707,0.363318,-0.656258,-0.61907,-8.111555,-4.3718123,-3.0789435,-0.10809326,0.3184204,-0.6734009,dancing +-6.169858,26.574442,-2.6120741,0.446443,0.638707,0.565161,-25.617958,-7.728483,6.229713,-0.18395996,0.6801758,0.0079956055,dancing +3.4823663,-2.4756045,-2.802413,0.631561,0.453375,0.3784,5.9807158,-0.56982,6.612785,-0.5132446,0.11016846,-0.84606934,dancing +0.19632454,-0.35913026,1.483208,0.637244,0.476061,0.351114,-2.7341783,1.8435353,-4.3526587,-0.50219727,0.082214355,-0.8566284,dancing +3.6008794,5.6778493,-4.009091,0.637278,0.483459,0.382699,-13.038822,-9.873688,3.86903,-0.30413818,0.54803467,-0.48510742,dancing +2.6862943,2.0003555,-2.4121583,0.420848,-0.549301,-0.628842,5.559336,-8.508993,7.0581064,0.47381592,0.25909424,-0.46484375,dancing +-0.12449849,25.859772,-6.409278,0.444236,0.699106,0.454563,-8.408437,-7.28795,-3.0933087,0.8930054,0.19561768,-0.31585693,dancing +1.4915876,3.345897,3.9971197,0.471195,0.545347,0.476621,-8.408437,-7.28795,-3.0933087,0.8930054,0.19561768,-0.31585693,dancing +-6.293159,17.597382,-3.558981,0.51224,0.55481,0.428982,-5.2241483,-10.91756,-4.7884035,0.8930054,0.19561768,-0.31585693,dancing +-0.9816227,8.230068,-5.3127337,0.362911,-0.699678,-0.583309,-17.152061,-12.521675,1.9201498,0.5169678,-0.24389648,-0.71276855,dancing +1.5526398,8.35337,-1.8387469,0.421293,-0.607088,-0.605932,-6.4978633,8.327033,3.86903,0.9624634,-0.013122559,-0.2140503,dancing +2.8167782,9.326612,-3.752911,0.216582,0.808251,0.528818,-11.228806,-10.730812,-6.2967505,0.8629761,-0.12542725,-0.012512207,dancing +-3.5003228,9.1255,0.077811554,0.340029,0.607215,0.516562,-1.661576,5.559336,9.792285,0.6323242,0.07775879,-0.52948,dancing +-6.82946,7.5082164,3.2094274,0.311356,0.579539,0.380038,-12.976573,-3.0597897,-3.596091,0.7932739,-0.4605713,0.19799805,dancing +-6.5565214,5.717354,5.6658783,0.185591,0.464146,0.413321,-20.580557,3.8498764,-0.4261679,0.7647095,-0.4713745,0.23999023,dancing +-0.093373865,5.5102553,6.8689647,0.253226,0.141213,0.429836,-22.682667,-7.1059904,-6.631939,0.83862305,-0.53723145,-0.048095703,dancing +-0.02873042,5.6898203,7.9870567,0.261946,0.140491,0.440384,-14.781801,4.036624,3.1603463,0.82159424,-0.46600342,-0.3283081,dancing +0.9911995,5.329493,7.752425,0.259802,0.132426,0.445474,-9.816227,8.015787,-11.880029,0.41210938,0.028564453,-0.8978882,dancing +0.9911995,5.329493,7.752425,0.259802,0.132426,0.445474,35.975273,-13.852851,-0.81881696,0.15344238,0.34429932,-0.8980713,dancing +1.8866309,5.0721164,7.60997,0.258374,0.13855,0.457876,-4.343082,6.9623384,-5.425261,0.5442505,-0.30700684,-0.7702637,dancing +1.8866309,5.0721164,7.60997,0.258374,0.13855,0.457876,-4.43885,-6.0285997,-7.4842744,0.62213135,-0.49639893,-0.57073975,dancing +1.8866309,5.0721164,7.60997,0.258374,0.13855,0.457876,-2.858677,-5.7987566,-6.8091097,-0.5181885,-0.24584961,-0.33776855,dancing +1.2940661,5.0960584,7.704541,0.252565,0.146955,0.465843,-10.146626,-0.56982,1.8195933,-0.29083252,-0.015625,-0.39385986,dancing +1.2940661,5.0960584,7.704541,0.252565,0.146955,0.465843,-14.130578,-14.020445,5.544971,0.028686523,0.005493164,-0.94610596,dancing +1.2940661,5.0960584,7.704541,0.252565,0.146955,0.465843,-0.62728083,-1.0390835,-0.6464345,0.01763916,-0.029052734,-0.96429443,dancing +-0.62129533,5.445612,8.038532,0.246227,0.138322,0.452051,0.5554548,1.5275006,-4.2185836,-0.27026367,-0.5075073,-0.7207031,dancing +-0.62129533,5.445612,8.038532,0.246227,0.138322,0.452051,-10.429143,-2.0685902,-4.7788267,-0.1798706,-0.34783936,-0.91845703,dancing +-0.62129533,5.445612,8.038532,0.246227,0.138322,0.452051,-34.993652,-0.6607997,-3.3566709,0.14996338,0.07019043,-0.5793457,dancing +-1.2545617,2.8526914,9.387665,0.200543,0.135893,0.410051,-9.988609,-5.473145,0.2633622,-0.07659912,0.23321533,-0.5091553,dancing +-1.2545617,2.8526914,9.387665,0.200543,0.135893,0.410051,-37.52193,11.377247,0.0,-0.5796509,0.2685547,-0.3302002,dancing +-5.3486466,4.1635165,10.986992,0.177058,0.151285,0.376394,-26.522966,11.042058,6.775591,0.3161621,-0.24066162,-0.6817627,dancing +-1.3850456,4.0773253,9.271546,0.181064,0.078282,0.317588,-7.153875,-4.6782703,-3.3279405,-0.6854248,-0.4020996,-0.6055298,dancing +0.19871874,2.9364884,9.163807,0.182012,0.063067,0.334532,-7.153875,-4.6782703,-3.3279405,-0.6854248,-0.4020996,-0.6055298,dancing +-0.45609543,3.3566709,8.334216,0.185752,0.072793,0.364497,7.4268136,-4.3957543,-0.2825158,-0.49023438,-0.60131836,-0.59381104,dancing +-0.82480246,4.06296,9.226056,0.163756,0.171797,0.438793,-23.266851,-6.7947445,-24.181437,-0.19897461,-0.46099854,-0.64434814,dancing +-0.7733272,3.810372,8.857349,0.150553,0.173511,0.465805,-23.266851,-6.7947445,-24.181437,0.10510254,-0.565979,-0.81585693,dancing +-1.3467385,4.226963,8.487445,0.144853,0.167943,0.469881,-12.722788,-11.554418,-6.4499793,-0.34527588,-0.11248779,-0.9309082,dancing +-1.2916719,3.9552212,8.185776,0.129444,0.189504,0.47238,-12.722788,-11.554418,-6.4499793,-0.34527588,-0.11248779,-0.9309082,dancing +-2.1428106,1.1192893,8.376115,0.081862,0.190805,0.454552,-7.517793,-6.2249246,-12.588713,-0.43927002,-0.20568848,-0.8723755,dancing +-0.545878,2.268506,8.230068,0.064164,0.196495,0.435339,-16.213533,-18.239029,29.783869,0.4555664,-0.20837402,-0.8644409,dancing +-0.545878,1.7525556,6.8557963,0.108653,0.169757,0.457975,-16.213533,-18.239029,29.783869,0.4555664,-0.20837402,-0.8644409,dancing +-1.0031705,1.6496049,9.600749,0.164427,0.091194,0.481398,-4.783615,-2.2649148,-5.7843914,-0.09460449,-0.28289795,-0.9168701,dancing +0.58298814,2.610877,9.801862,0.177686,0.056519,0.443821,-16.146496,-38.264133,21.619642,0.28320312,-0.12322998,-0.92407227,dancing +0.7769185,2.5558102,9.7084875,0.177548,0.021465,0.421464,-12.976573,-2.2361844,-12.0093155,0.24395752,-0.09832764,-0.9067383,dancing +0.61411273,2.8203697,8.512584,0.175761,-0.006402,0.396688,-10.658986,-23.669079,12.938266,0.8140869,-0.20648193,-0.49694824,dancing +0.7015011,3.028665,8.292317,0.193947,-0.012867,0.351924,-19.780895,-32.307358,25.005043,0.87231445,-0.18859863,-0.38006592,dancing +1.6591818,3.040636,9.016563,0.183839,-0.017427,0.343706,-5.2145715,1.3455414,1.7286136,0.74017334,0.33502197,-0.05407715,dancing +1.7705121,3.335123,9.296685,0.17411,-0.021688,0.338191,-19.9437,-57.436897,13.22557,0.94873047,0.014221191,-0.25457764,dancing +0.9959879,3.321955,9.203311,0.169907,-0.021603,0.328727,-23.841461,-51.566315,18.727446,0.9489746,0.04437256,-0.17047119,dancing +1.7908629,3.2860417,9.175778,0.162457,-0.019116,0.328205,-3.1364043,1.7669208,1.1444284,0.7150879,0.40179443,-0.17193604,dancing +2.0590134,2.219425,8.789114,0.154137,-0.02028,0.338588,-3.7972038,-0.9911995,-0.37349546,0.65319824,0.4500122,-0.2597046,dancing +0.8834604,3.1136594,9.155427,0.136216,-0.019107,0.313152,-2.2984335,-5.6551046,-9.524135,0.5107422,0.5120239,-0.46636963,dancing +1.0163386,1.8854338,9.555259,0.120914,-0.030812,0.298579,-9.691729,-5.554548,1.1013328,0.91864014,-0.030639648,-0.012817383,dancing +1.0079589,1.8100165,9.533711,0.108573,-0.041892,0.286177,-8.992621,-5.602432,2.6862943,0.92559814,-0.26239014,0.014038086,dancing +1.0079589,1.8100165,9.533711,0.108573,-0.041892,0.286177,-1.0295067,-0.34955344,1.1971009,0.6725464,0.17822266,-0.704895,dancing +2.078167,1.0642227,9.43914,0.085724,-0.074001,0.251005,9.251195,8.858546,-6.493075,0.43548584,0.62939453,-0.42596436,dancing +1.7956513,1.4999673,9.661801,0.085047,-0.07941,0.249626,5.9807158,34.648888,20.09214,0.48736572,0.45159912,-0.29840088,dancing +1.8686744,1.7956513,9.903615,0.095007,-0.076599,0.250747,-0.866701,13.9438305,13.512875,0.51031494,0.40283203,-0.38781738,dancing +1.8686744,1.7956513,9.903615,0.095007,-0.076599,0.250747,7.029376,38.16836,8.255207,0.237854,0.40441895,-0.4666748,dancing +1.4401124,1.9476831,8.74123,0.11099,-0.061563,0.259082,7.029376,38.16836,8.255207,0.237854,0.40441895,-0.4666748,dancing +1.4401124,1.9476831,8.74123,0.11099,-0.061563,0.259082,-8.350976,25.033773,8.178593,0.13439941,-0.10437012,-0.6239624,dancing +1.4401124,1.9476831,8.74123,0.11099,-0.061563,0.259082,-26.072857,-32.71437,26.508602,0.095703125,-0.4041748,-0.8901367,dancing +1.9632454,2.018312,8.313866,0.127513,-0.038076,0.259911,-23.611618,-39.442078,9.32781,-0.39178467,-0.1159668,-0.7161865,dancing +1.9632454,2.018312,8.313866,0.127513,-0.038076,0.259911,5.085284,-2.5713725,-2.0254946,-0.26672363,-0.5751343,-0.7036743,dancing +0.60932434,3.0681696,9.052477,0.131991,-0.040674,0.246552,5.085284,-2.5713725,-2.0254946,-0.26672363,-0.5751343,-0.7036743,dancing +0.60932434,3.0681696,9.052477,0.131991,-0.040674,0.246552,20.03468,24.880545,20.781672,-0.01727295,-0.61883545,-0.5725708,dancing +0.60932434,3.0681696,9.052477,0.131991,-0.040674,0.246552,-30.391996,-23.621195,-8.815451,-0.819397,-0.37023926,-0.4265747,dancing +1.2485762,2.5833437,10.242394,0.133648,-0.039705,0.240514,9.193734,17.228676,13.867216,-0.18328857,-0.8397827,-0.44335938,dancing +0.5279215,3.6966474,8.180987,0.131563,-0.038393,0.260185,-18.928558,0.9816227,5.1235914,-0.37872314,-0.8082886,-0.45074463,dancing +-0.18435353,2.6312277,10.752359,0.129343,-0.058427,0.246766,45.80108,30.669724,5.133168,0.066223145,0.19677734,-0.58691406,dancing +0.36750996,4.045004,7.961918,0.157725,-0.01845,0.269816,45.844173,29.257145,1.8291701,0.068725586,0.21868896,-0.59124756,dancing +0.89303726,4.1742907,7.603985,0.205324,0.023198,0.278158,45.844173,29.257145,1.8291701,0.068725586,0.21868896,-0.59124756,dancing +0.019153614,5.0374002,8.525752,0.219525,-0.022294,0.221374,32.407913,17.774553,0.2873042,0.125,0.24633789,-0.6143799,dancing +-0.4321534,5.793968,7.854179,0.229523,-0.022137,0.194461,-23.54458,-8.623915,-1.9488802,0.27172852,-0.0010375977,-0.82891846,dancing +-1.6292542,6.754043,9.066842,0.263673,0.048986,0.172589,-15.016433,-1.8914193,3.739743,0.65216064,0.24005127,-0.44763184,dancing +-0.49081135,6.030994,9.817424,0.268913,0.07796,0.179818,24.837448,30.837318,16.112978,0.5783081,0.38201904,-0.30615234,dancing +-0.08619126,6.1100025,6.56131,0.310198,0.037761,0.16616,-15.275007,-3.122039,10.601525,0.023925781,-0.6676636,-0.72424316,dancing +-0.6847417,6.6079965,6.578069,0.332459,0.024177,0.126753,-32.752678,-1.6807296,-7.240066,-0.11669922,-0.29608154,-0.92437744,dancing +-0.89543146,7.0724716,7.289147,0.337729,0.014951,0.078682,22.060175,6.6606693,-5.391742,-0.2277832,-0.43133545,-0.788147,dancing +-0.5506664,6.6427126,6.7947445,0.340486,0.011528,0.070576,0.5315128,3.3087866,12.061988,0.1798706,-0.7470093,-0.63500977,dancing +-0.037110128,6.4739213,6.2644286,0.353686,0.004541,0.061581,1.0151415,1.2737153,8.805874,0.074279785,-0.78570557,-0.5996704,dancing +-0.6871359,6.7348895,6.752846,0.355992,0.006114,0.028282,0.9864111,-0.4357447,7.675811,-0.029907227,0.05633545,-0.97631836,dancing +0.7781156,0.62608373,13.4422455,0.109537,-0.017382,0.502022,7.422025,5.7508726,-0.4357447,0.4711914,0.31848145,-0.57958984,lying +1.067814,1.0450691,9.329007,0.069728,-0.027419,0.479298,3.1794999,-4.5298295,2.336741,0.93481445,0.28710938,-0.050476074,lying +1.0295067,0.93732995,9.343372,0.069772,-0.027098,0.478409,-0.63206923,-10.333375,0.6607997,0.8916626,0.05255127,0.02935791,lying +1.0905589,0.89423436,9.528923,0.068825,-0.027936,0.478198,-0.8714894,-10.299856,1.0965444,0.6711426,0.023498535,-0.107910156,lying +-0.034715924,0.8750807,9.471462,0.050216,-0.022737,0.47624,2.303222,-10.812215,0.8283938,0.6668091,0.022766113,-0.10839844,lying +0.92775315,0.82360536,9.508572,0.100342,-0.034871,0.493959,7.551312,-8.355764,7.1059904,0.5140381,0.25543213,0.29107666,lying +1.2150574,1.0666169,9.757569,0.079846,-0.02702,0.486489,7.1873937,-2.1691468,2.6479871,0.5656128,0.23803711,0.42132568,lying +1.270124,1.2413936,9.423578,0.085681,-0.033578,0.482435,7.3549876,-10.132261,-3.572149,0.71087646,0.12390137,0.34332275,lying +1.2354081,1.1647792,9.36492,0.085307,-0.034933,0.482168,5.3007627,-7.0245876,4.0174704,0.5527344,0.15118408,0.33569336,lying +1.7525556,2.005144,6.588843,0.082507,-0.001699,0.511765,4.232949,-7.5225816,-2.9161377,0.67077637,0.40789795,0.21044922,lying +1.922544,1.2425907,8.761581,0.066092,-0.016159,0.517305,4.232949,-7.5225816,-2.9161377,0.67077637,0.40789795,0.21044922,lying +0.3172317,-0.41060558,10.044873,0.033376,-0.008348,0.513688,9.457097,-10.299856,-5.3725886,0.67077637,0.40789795,0.21044922,lying +-13.785813,-1.9165585,7.9930425,-0.248631,0.355075,0.407095,9.457097,-10.299856,-5.3725886,0.6039429,0.4309082,0.3334961,lying +-5.4061074,-0.47046062,8.224083,-0.137367,0.24702,0.442219,6.114791,-10.481815,-3.371036,0.6013794,0.335083,0.20037842,lying +-5.3881507,0.25378537,7.69257,-0.122974,0.247411,0.439031,4.9224787,-10.601525,-4.927267,0.7946167,0.35272217,0.16192627,lying +-2.2158337,-1.5490485,8.249222,-0.039781,-0.0025,0.49737,2.777274,-2.4756045,-0.7422025,0.48950195,0.41723633,0.2475586,lying +0.89303726,1.6603789,11.309011,0.043675,0.021625,0.501122,12.181698,-3.7588966,2.3319526,0.41363525,0.42785645,0.15966797,lying +4.092888,0.6548142,11.572374,0.148896,-0.06608,0.423493,3.6535518,-9.174581,0.5410896,0.56378174,0.13085938,0.08392334,lying +4.9524064,2.9053638,6.775591,0.157319,-0.08853,0.485426,-2.72939,0.1436521,9.14585,0.115478516,-0.1104126,0.04650879,lying +1.3611037,0.36511576,10.160992,0.109675,-0.119889,0.537302,-0.08619126,-4.8602295,7.5082164,0.2835083,-0.10949707,0.026611328,lying +2.542642,0.48123455,8.775947,0.090738,-0.064833,0.538508,-0.08619126,-4.8602295,7.5082164,0.2835083,-0.10949707,0.026611328,lying +1.2354081,0.8331822,9.369708,0.062437,-0.030422,0.540841,-1.0390835,-6.1387334,7.335834,0.31292725,-0.050720215,0.061523438,lying +1.0007763,0.8702923,9.41041,0.061722,-0.018469,0.540189,0.8619126,-6.4068837,7.651869,0.3065796,-0.026733398,0.085876465,lying +0.9828198,0.91578215,9.32781,0.062559,-0.016057,0.539085,0.20111294,-5.78918,8.279149,0.272583,-0.02557373,0.08099365,lying +0.90381116,0.6631939,9.522937,0.055965,-0.027886,0.536139,4.7979803,-7.6135616,-7.8912888,0.7335205,0.4847412,0.1340332,lying +0.8319851,0.81881696,9.482236,0.052538,-0.025674,0.535042,4.7979803,-7.6135616,-7.8912888,0.7335205,0.4847412,0.1340332,lying +1.1707647,0.78769237,9.234436,0.057437,-0.030304,0.535808,5.386954,-8.183381,7.2592196,0.39520264,0.2668457,-0.05029297,lying +0.6332663,0.7541735,9.598354,0.056614,-0.028472,0.536826,5.386954,-8.183381,7.2592196,0.39520264,0.2668457,-0.05029297,lying +0.8583213,0.58777654,9.500193,0.049152,-0.024994,0.538068,2.5187001,-0.8475474,8.42759,0.24157715,0.23150635,0.036376953,lying +0.8858546,0.60573304,9.479841,0.049036,-0.024114,0.53807,5.707777,-8.652645,7.651869,0.3416748,0.26013184,0.0068359375,lying +0.8595184,0.6572084,9.441534,0.053508,-0.021819,0.538046,5.080496,-7.192182,5.2959743,0.38079834,0.28118896,0.014709473,lying +0.81163436,0.63087213,9.417592,0.049244,-0.020966,0.538258,5.171476,-7.345411,5.0613422,0.39465332,0.28356934,0.016540527,lying +0.80445176,0.676362,9.507375,0.052132,-0.025991,0.537735,5.171476,-7.345411,5.0613422,0.39465332,0.28356934,0.016540527,lying +3.705027,6.211756,6.081272,0.356814,0.06642,0.569911,-0.92416185,-2.384625,8.930372,0.019104004,-0.083984375,0.032104492,lying +-1.7429788,9.594764,-3.9264908,0.599053,0.489213,0.356771,-1.6376339,-0.521936,9.011775,-0.09851074,-0.09906006,0.01361084,lying +1.804031,6.775591,6.495469,0.358314,0.125564,0.496893,3.98874,4.213795,7.2113357,-0.4078369,0.07171631,-0.27856445,getting up +0.7565677,7.321469,5.853823,0.385424,0.166314,0.485998,3.98874,4.213795,7.2113357,-0.4078369,0.07171631,-0.27856445,getting up +-0.51116204,11.372458,-1.721431,0.551661,0.315508,0.400456,-1.1779473,-12.981361,-1.2737153,-0.008178711,0.651001,-0.75683594,getting up +-0.044292733,10.01734,-2.3319526,0.60771,0.386845,0.366552,-1.1779473,-12.981361,-1.2737153,-0.008178711,0.651001,-0.75683594,getting up +0.047884032,8.288726,-5.097255,0.667071,0.518986,0.325706,-3.53863,-9.212888,-1.733402,0.11566162,0.6757202,-0.7201538,getting up +0.78170687,7.4375877,-7.711724,0.729606,0.524644,0.281481,-3.145981,-9.653421,-1.0965444,0.10369873,0.6723633,-0.72540283,getting up +0.7314286,7.952341,-6.0800753,0.724444,0.514214,0.306981,-3.0550013,-9.308656,-0.047884032,0.10662842,0.6362915,-0.7576904,getting up +1.0642227,7.839813,-6.0585275,0.719183,0.510162,0.315935,-2.5474305,-8.580819,1.4460979,0.10662842,0.6362915,-0.7576904,getting up +1.1563994,9.003395,-4.086902,0.679054,0.488935,0.36551,-5.329493,-8.010999,1.3072342,0.25878906,0.5993042,-0.74658203,getting up +0.8678981,9.075222,-4.358644,0.679095,0.478453,0.368605,-6.373365,-9.217676,-0.641646,0.34362793,0.61724854,-0.6595459,getting up +-2.4013844,9.002198,1.472434,0.471214,0.442282,0.390479,-2.6862943,-0.79487497,9.212888,0.1642456,-0.03149414,-0.97473145,getting up +-1.9596541,7.3849154,5.662287,0.318185,0.351412,0.431604,-2.6862943,-0.79487497,9.212888,0.1642456,-0.03149414,-0.97473145,getting up +-2.5067291,5.7748146,6.3793507,0.250726,0.297848,0.445006,-2.6862943,-0.79487497,9.212888,0.1642456,-0.03149414,-0.97473145,getting up +-2.6048915,4.1706996,7.156269,0.175619,0.239031,0.455325,-2.8921957,-1.3838485,8.954314,0.16912842,-0.022399902,-0.97161865,getting up +0.45250413,1.0618285,9.482236,0.053052,0.00286,0.452231,-2.9017725,-1.1971009,9.222465,0.16912842,-0.022399902,-0.97161865,getting up +-3.9755719,2.445677,9.829395,0.087005,0.11265,0.448801,5.803545,7.3645644,-2.0638018,0.168396,-0.022338867,-0.97283936,getting up +-0.80924016,8.744822,4.5801077,0.408321,0.294136,0.43778,-4.453215,4.960786,12.354081,0.12347412,0.13775635,-0.7609863,getting up +-0.2837129,9.783905,-1.5370775,0.612064,0.41741,0.367859,-7.9152308,-8.145074,4.3861775,0.029846191,0.517395,-0.6730957,getting up +-0.5303157,9.660604,-2.7928362,0.650772,0.436042,0.332186,-4.2281604,-7.627927,5.985504,-0.20056152,0.18762207,-0.91448975,getting up +-0.39743748,9.652225,-2.8491,0.664107,0.438354,0.320754,-4.501099,-3.083732,7.6375036,0.20495605,0.15795898,-0.8401489,getting up +0.7015011,1.0821792,9.532514,0.069863,-0.014044,0.437128,7.9487495,5.315128,1.2210429,-0.3390503,-0.5656738,-0.69989014,getting up +0.7529764,1.0666169,9.522937,0.06915,-0.014728,0.43757,7.9487495,5.315128,1.2210429,-0.3390503,-0.5656738,-0.69989014,getting up +0.7410054,1.009156,9.451111,0.066262,-0.016157,0.437731,7.9487495,5.315128,1.2210429,-0.3390503,-0.5656738,-0.69989014,getting up +0.7098808,0.9576807,9.455899,0.064499,-0.017663,0.438077,7.9487495,5.315128,1.2210429,-0.3390503,-0.5656738,-0.69989014,getting up +0.5303157,1.0199299,9.560047,0.05964,-0.007167,0.445017,7.9487495,5.315128,1.2210429,-0.3390503,-0.5656738,-0.69989014,getting up +0.34835634,1.0175357,9.540894,0.058427,-0.005304,0.452726,7.9487495,5.315128,1.2210429,-0.3390503,-0.5656738,-0.69989014,getting up +0.36990416,1.4556746,9.358934,0.068614,9.66E-4,0.45346,7.9487495,5.315128,1.2210429,-0.3390503,-0.5656738,-0.69989014,getting up +-3.9025488,2.3906105,9.835381,0.101116,0.100239,0.436387,6.133945,7.4603324,-3.016694,-0.36853027,-0.63287354,-0.65545654,getting up +-3.429694,4.3658266,7.8745294,0.185402,0.204235,0.42461,6.133945,7.4603324,-3.016694,-0.36853027,-0.63287354,-0.65545654,getting up +-1.4915876,8.674192,3.9240966,0.436043,0.334635,0.423488,4.6351743,2.801216,8.925584,-0.08874512,-0.20245361,-0.798584,getting up +-0.5590461,9.657013,-3.466804,0.654102,0.438002,0.32678,1.7190368,2.2984335,11.48738,-0.023803711,-0.11303711,-0.7922363,getting up +-0.33159694,9.408015,-3.0083144,0.660632,0.438496,0.323987,2.906561,3.3806129,0.33997664,-0.20080566,0.13085938,-0.74224854,getting up +-0.6536171,9.713276,-2.019509,0.63986,0.431322,0.337232,2.906561,3.3806129,0.33997664,-0.20080566,0.13085938,-0.74224854,getting up +-0.45729253,9.628283,-1.7154455,0.625291,0.423314,0.346692,-1.9440918,-6.301539,4.903325,-0.27905273,0.54156494,-0.5644531,getting up +-0.80325466,9.676167,-2.147599,0.638433,0.440608,0.326434,-3.3135753,-11.650186,2.767697,-0.23632812,0.56103516,-0.60369873,getting up +-3.4584243,5.5629277,-11.432313,0.745335,0.555948,0.158843,-0.0047884034,-0.61291564,8.322245,0.028930664,0.048828125,-0.90875244,getting up +-0.60214174,8.731653,-8.986636,0.759024,0.528473,0.211023,6.4739213,-7.015011,4.381389,-0.38146973,0.55023193,-0.72198486,getting up +-0.049081136,6.603208,-4.7500963,0.72917,0.473651,0.244622,3.739743,-5.8945246,4.2904096,-0.25476074,0.4000244,-0.8187866,getting up +-1.0139444,9.616311,-1.3455414,0.607241,0.431619,0.333201,-7.9583263,-5.032612,4.429273,-0.119628906,0.545105,-0.7716675,getting up +-1.3491327,9.82341,0.17118542,0.58999,0.368623,0.30736,1.685518,-2.1308396,8.585608,-0.038757324,0.05456543,-0.86224365,getting up +-0.08140286,9.385271,-1.4616601,0.587527,0.42082,0.374104,-3.8402996,-7.302315,12.708423,-0.038757324,0.05456543,-0.86224365,getting up +-1.2988545,10.250774,-1.210269,0.163023,0.730719,0.622134,2.2601264,-0.7517793,8.925584,-0.019226074,0.094055176,-0.84399414,getting up +-0.35553896,9.932345,-2.2984335,-0.051033,0.758208,0.649904,2.2601264,-0.7517793,8.925584,-0.06555176,-0.16235352,-0.87646484,getting up +0.05865794,9.607931,-1.271321,0.13732,-0.719487,-0.673311,2.8778305,2.8682537,8.465898,-0.018066406,-0.31695557,-0.8586426,getting up +-1.4891934,9.762358,-1.8303672,0.207792,-0.719784,-0.65417,3.3518825,3.2944214,8.66701,-0.045776367,-0.32348633,-0.86376953,getting up +-1.3144168,9.7563715,-1.3431472,0.206839,-0.717338,-0.655592,3.3518825,3.2944214,8.66701,-0.045776367,-0.32348633,-0.86376953,getting up +-1.2401965,9.7803135,-1.292869,0.21009,-0.716625,-0.654731,-9.409213,-2.4851813,-0.48362875,-0.63067627,0.35913086,0.52178955,getting up +-1.2162545,9.840169,-1.2425907,0.210742,-0.71313,-0.657956,-9.409213,-2.4851813,-0.48362875,-0.63067627,0.35913086,0.52178955,getting up +-1.2378023,9.872491,-1.1935096,0.211178,-0.709722,-0.661237,-9.409213,-2.4851813,-0.48362875,-0.63067627,0.35913086,0.52178955,getting up +-1.198298,9.781511,-1.0151415,0.211256,-0.708644,-0.662221,-9.409213,-2.4851813,-0.48362875,-0.63067627,0.35913086,0.52178955,getting up +-1.1911154,9.720459,-1.0055647,0.211676,-0.706189,-0.66437,-9.409213,-2.4851813,-0.48362875,-0.63067627,0.35913086,0.52178955,getting up +-1.1671734,9.870096,-1.151611,0.211967,-0.708288,-0.662024,-9.409213,-2.4851813,-0.48362875,-0.63067627,0.35913086,0.52178955,getting up +-1.2114661,9.759963,-1.0977415,0.212862,-0.708956,-0.660991,-9.409213,-2.4851813,-0.48362875,-0.63067627,0.35913086,0.52178955,getting up +0.7146692,1.3934253,9.340978,0.089651,-0.00312,0.417283,3.9743748,7.73806,5.6407394,-0.67645264,0.33605957,0.5355835,getting up +0.82599956,1.3455414,9.48463,0.087991,-0.00456,0.416977,2.0973208,1.4173675,8.949526,-0.79937744,0.18200684,0.55792236,getting up +0.6584055,1.5945383,9.496601,0.092038,-0.00257,0.416586,2.0973208,1.4173675,8.949526,-0.79937744,0.18200684,0.55792236,getting up +1.328782,2.9963434,11.656171,0.148971,0.007997,0.423309,2.2170308,2.15957,9.174581,0.1026001,0.14648438,0.9837036,getting up +-1.3611037,8.628703,4.1359835,0.438745,0.229936,0.319773,-15.069105,-25.7664,2.0350714,0.08215332,-0.4119873,0.8272705,getting up +-4.045004,9.002198,1.3275849,0.491764,0.397139,0.263512,-8.968679,-22.567745,0.92895025,0.09820557,-0.48492432,0.78845215,getting up +-3.3542767,9.003395,-1.6184803,0.529851,0.51857,0.276504,-4.903325,-7.61835,-3.098097,0.045166016,-0.80059814,0.5270996,getting up +-2.5366566,8.859743,-3.7912183,0.545344,0.563223,0.349472,-4.903325,-7.61835,-3.098097,0.045166016,-0.80059814,0.5270996,getting up +0.29448682,9.293094,-4.8973393,0.629883,0.535572,0.366506,-5.2049947,-5.9663506,-2.0398598,-0.012268066,-0.8010864,0.5418701,getting up +-1.6484078,8.002619,-5.0182467,0.607939,0.6292,0.303782,-7.469909,-6.4068837,-0.17717093,0.066833496,-0.6807251,0.3007202,getting up +-1.3563153,8.2037325,-2.079364,0.538547,0.561007,0.388253,-9.849746,-6.0046577,0.34955344,-0.057861328,-0.68237305,0.25274658,getting up +-0.04070143,9.927557,-1.5753847,0.626124,0.426154,0.347018,-6.861782,-8.949526,5.659893,-0.11682129,-0.40185547,0.8427124,getting up +-0.5554548,9.822212,-1.6783354,0.614296,0.428075,0.354046,2.873042,2.6288335,8.350976,0.057556152,0.1303711,0.98083496,getting up +-1.246182,9.847352,-0.5434838,0.587803,0.410959,0.341078,4.5537715,2.3463178,8.1642275,0.12390137,0.16320801,0.9719238,getting up +-2.15957,7.85777,-7.620744,0.764364,0.48603,0.164172,2.5713725,2.944868,8.844181,0.112854004,0.21234131,0.96325684,getting up +-0.81761986,8.972271,-1.769315,0.650518,0.397484,0.293039,-2.4516625,-0.92416185,9.734824,0.018554688,-0.036621094,0.70654297,getting up +-0.9876082,9.7240505,-0.521936,0.597225,0.390318,0.332846,-5.544971,-4.975151,7.40766,-6.1035156E-5,-0.12957764,0.6951904,getting up +-2.8311436,9.345766,-1.7788919,0.323092,-0.672183,-0.643092,-11.932701,-2.930503,0.7613561,0.37469482,-0.60479736,-0.35321045,getting up +0.5674258,10.207679,1.5729905,0.26551,-0.69882,-0.653699,-11.932701,-2.930503,0.7613561,0.37469482,-0.60479736,-0.35321045,getting up +-2.825158,9.901221,-6.5708866,0.212012,-0.720192,-0.656629,-11.932701,-2.930503,0.7613561,0.37469482,-0.60479736,-0.35321045,getting up +-5.1116204,7.4148426,-0.60573304,0.216464,-0.724514,-0.648422,-11.932701,-2.930503,0.7613561,0.37469482,-0.60479736,-0.35321045,getting up +0.051475335,9.0476885,3.6942532,0.524853,0.176888,0.27445,3.8833952,-8.441956,4.7165775,0.5126953,-0.41912842,-0.5494385,reading +0.12808979,8.9842415,3.7900212,0.523027,0.174178,0.273199,4.951209,-9.241618,-6.885724,0.6368408,0.5133667,-0.082336426,reading +0.06703765,8.965088,3.6499605,0.523948,0.172769,0.272169,4.951209,-9.241618,-6.885724,0.6368408,0.5133667,-0.082336426,reading +0.045489833,9.010578,3.8175547,0.523228,0.172727,0.271831,4.7500963,-8.7053175,1.0869676,0.46624756,0.45837402,-0.20721436,reading +0.06943185,9.036914,3.776853,0.523728,0.172254,0.271803,5.779603,-8.145074,-3.6966474,0.6072998,0.5275879,-0.047851562,reading +0.034715924,9.011775,3.7122097,0.528377,0.161318,0.252932,5.9902925,-8.240842,-4.7548847,0.65093994,0.5335083,-0.018737793,reading +0.06943185,9.009381,3.7313633,0.503882,0.228535,0.357397,5.2720323,-7.6949644,2.767697,0.4074707,0.42651367,-0.20239258,reading +0.22505496,8.893262,3.8031895,0.482578,0.25763,0.418032,5.497087,-9.639056,-2.5713725,0.5736084,0.5039673,-0.097229004,reading +0.1352724,9.010578,3.8295257,0.483855,0.259558,0.416577,6.6846113,-6.6893997,-2.1643584,0.4557495,0.62420654,-0.16363525,reading +0.08020575,8.9842415,3.7900212,0.483776,0.261572,0.41761,6.0621185,-7.0006456,-4.0701427,0.4835205,0.66693115,-0.14245605,reading +0.016759412,8.956709,3.9025488,0.489382,0.264482,0.413744,6.3685765,-7.1634517,-3.9983168,0.4979248,0.6590576,-0.14666748,reading +0.46447513,9.993398,2.9735985,0.565463,0.263197,0.325919,5.415684,-7.6614456,-5.0182467,0.5946655,0.635437,-0.07513428,reading +0.19153613,8.400057,4.8973393,0.455782,0.198238,0.385135,6.114791,-7.7332716,-2.906561,0.51586914,0.62298584,-0.12536621,reading +0.24181437,8.479065,4.8386817,0.45569,0.196415,0.383573,6.0046577,-7.6710224,-1.043872,0.4593506,0.60339355,-0.15960693,reading +0.2645593,8.3868885,4.911705,0.455563,0.195669,0.378565,6.5457473,-7.752425,-2.0733786,0.53564453,0.6374512,-0.020568848,reading +0.24899697,8.364143,4.909311,0.456406,0.195833,0.377804,6.325481,-8.528147,-1.388637,0.49041748,0.607666,-0.10461426,reading +0.25139117,8.350976,4.9464207,0.454331,0.194655,0.378158,6.483498,-8.00621,-1.2066777,0.46258545,0.5890503,-0.1472168,reading +0.23223756,8.367735,5.0254292,0.453797,0.192742,0.377,6.919243,-6.9000893,-3.5098996,0.51330566,0.6300049,-0.051452637,reading +0.44891283,8.289924,5.040992,0.468045,0.150584,0.303261,7.469909,-6.7803793,-2.7820623,0.5115967,0.6220093,-0.022644043,reading +0.31124622,8.354567,5.00867,0.475315,0.126131,0.260557,7.469909,-6.7803793,-2.7820623,0.5115967,0.6220093,-0.022644043,reading +0.557849,8.383297,5.044583,0.485259,0.081805,0.185081,7.350199,-5.674258,-2.873042,0.57373047,0.5710449,0.06640625,reading +0.45489833,8.331821,4.9404354,0.480499,0.117909,0.244601,5.257667,-9.32781,0.81402856,0.5319214,0.446167,-0.09173584,reading +0.22984336,8.49343,4.8243165,0.467962,0.167377,0.332289,5.2241483,-9.892841,-0.6655881,0.4977417,0.5111084,-0.18249512,reading +0.25857377,8.425196,4.847061,0.456957,0.186617,0.369656,5.434838,-9.241618,-0.18195933,0.4727173,0.5373535,-0.19946289,reading +0.29807812,8.499416,4.691438,0.46124,0.187768,0.361762,7.809886,-8.6334915,-1.1492168,0.44396973,0.5876465,-0.14300537,reading +0.5422867,8.53892,4.5920787,0.470563,0.186151,0.356845,5.185841,-5.363012,2.3463178,0.17828369,0.5204468,-0.12023926,reading +0.108936176,8.610746,4.4687777,0.473924,0.189529,0.353294,5.185841,-5.363012,2.3463178,0.17828369,0.5204468,-0.12023926,reading +0.41299978,8.645462,4.4005427,0.48497,0.183085,0.348634,8.2169,-6.019023,-2.432509,0.3779297,0.67663574,-0.032714844,reading +0.37588966,8.62152,4.5513773,0.483387,0.183852,0.34884,8.025364,-5.8466406,-1.0055647,0.3779297,0.67663574,-0.032714844,reading +0.2837129,8.623915,4.5441947,0.479515,0.189044,0.356414,7.704541,-6.4978633,-1.364695,0.38781738,0.64263916,-0.079833984,reading +0.35434186,8.644265,4.437653,0.475872,0.203589,0.378237,8.020576,-5.746084,-0.49320555,0.32574463,0.6552124,-0.1182251,reading +0.38307226,8.644265,4.426879,0.479259,0.193537,0.361896,8.336611,-6.0142345,-1.1875241,0.34692383,0.66522217,-0.06640625,reading +0.30885202,8.656237,4.4328647,0.480626,0.196025,0.360675,7.771579,-6.95755,-2.7629087,0.4222412,0.6610718,-0.058654785,reading +0.39743748,8.664616,4.459201,0.482108,0.190997,0.359816,7.771579,-6.95755,-2.7629087,0.4222412,0.6610718,-0.058654785,reading +0.32680854,8.652645,4.5358152,0.480967,0.191436,0.359034,8.236053,-5.1810527,-1.3311762,0.35473633,0.6616821,-0.054382324,reading +0.39983168,8.619126,4.4603977,0.480877,0.189432,0.358086,8.236053,-5.1810527,-1.3311762,0.35473633,0.6616821,-0.054382324,reading +0.31124622,8.639477,4.4675803,0.480206,0.193002,0.358359,7.824251,-4.1371803,-4.687847,0.387146,0.7139282,-0.012817383,reading +0.40462008,8.608353,4.485537,0.482988,0.190831,0.356566,7.335834,-3.4237084,-6.0525417,0.47314453,0.72094727,0.05432129,reading +0.36990416,8.639477,4.5202527,0.481752,0.190689,0.356889,7.872135,-5.00867,-4.55856,0.548645,0.7122803,0.122802734,reading +0.46088383,8.717288,4.4807487,0.481568,0.189589,0.35649,7.5465236,-6.3829417,0.18674773,0.34552002,0.6011963,-0.16253662,reading +0.4333505,8.64666,4.5513773,0.480823,0.189283,0.356862,7.5465236,-6.3829417,0.18674773,0.34552002,0.6011963,-0.16253662,reading +0.2717419,8.622718,4.423288,0.478732,0.193337,0.357637,7.038953,-7.3166804,-0.91458505,0.4180298,0.6279297,-0.11242676,reading +0.22146365,8.611943,4.453215,0.47913,0.193361,0.356561,8.24563,-5.3821654,-3.6296098,0.48815918,0.6576538,0.07128906,reading +0.21308395,8.669404,4.5238442,0.479402,0.190629,0.353405,7.7093296,-6.397307,2.030283,0.3267212,0.6272583,-0.1003418,reading +0.20470424,8.603564,4.578911,0.4785,0.190347,0.35378,8.269572,-6.301539,-1.9871874,0.36401367,0.6604614,-0.061462402,reading +0.21068975,8.65025,4.4807487,0.480444,0.185876,0.351681,8.269572,-6.301539,-1.9871874,0.36401367,0.6604614,-0.061462402,reading +0.32561144,8.652645,4.759673,0.4795,0.189287,0.35033,8.010999,-2.4229321,-1.8770541,0.19390869,0.604126,-0.1749878,reading +0.40581718,8.622718,4.4005427,0.484372,0.183214,0.344614,8.010999,-2.4229321,-1.8770541,0.19390869,0.604126,-0.1749878,reading +0.4225766,8.644265,4.453215,0.491422,0.170771,0.329321,8.010999,-2.4229321,-1.8770541,0.19390869,0.604126,-0.1749878,reading +0.30047232,8.616732,4.5082817,0.508734,0.029415,0.088808,8.365341,-6.8330517,-0.7661445,0.4449463,0.5628662,0.008117676,reading +0.38307226,8.470686,4.823119,0.505399,0.003563,0.039837,8.101978,-4.3287168,-3.821146,0.62957764,0.53759766,0.21777344,reading +0.30765492,8.507795,4.6782703,0.505728,-0.006423,0.021294,8.111555,-3.9695864,-5.57849,0.75024414,0.42193604,0.28771973,reading +0.2633622,8.694544,4.408922,0.520918,-0.013196,0.007811,8.135497,-5.094861,-3.3901896,0.6911621,0.43804932,0.26019287,reading +0.23463176,8.668207,4.3742065,0.521188,-0.014667,0.007118,8.135497,-5.094861,-3.3901896,0.6911621,0.43804932,0.26019287,reading +0.1604115,8.700529,4.498705,0.521065,-0.012916,0.009618,8.135497,-5.094861,-3.3901896,0.6911621,0.43804932,0.26019287,reading +0.25498247,8.746018,4.2102036,0.427971,0.295937,0.538508,7.4268136,-3.5194764,-5.315128,0.4312744,0.7504883,0.06402588,reading +0.46926352,8.799889,4.2437224,0.355216,0.371939,0.669981,7.9966335,-4.4196963,-4.6351743,0.19732666,0.829895,-0.12286377,reading +0.22744916,8.631097,3.679888,0.307022,0.415636,0.723846,6.339846,-8.384495,-0.31124622,-0.0017089844,0.7738037,-0.48883057,reading +0.2837129,8.944737,4.1862617,0.328889,0.430042,0.691728,6.3302693,-8.992621,-1.3024458,-0.026672363,0.729126,-0.54486084,reading +-0.90261406,9.451111,0.664391,0.41849,0.560944,0.532071,6.8809357,-8.700529,-0.6368576,-0.041748047,0.70129395,-0.5609131,reading +-2.0949266,5.5461683,-4.320337,0.532796,0.687411,0.338894,7.350199,-7.0724716,0.9816227,-0.05670166,0.6612549,-0.5805054,reading +-0.6775591,10.178948,-1.2785037,0.481581,0.567819,0.459069,6.5840545,-9.121908,0.770933,0.01727295,0.7211304,-0.48657227,reading +-0.47405192,9.61272,2.1583729,0.431698,0.44596,0.54865,8.0924015,-7.7907324,-2.4660277,0.13513184,0.8187866,-0.307312,reading +-0.5793968,9.610326,2.112883,0.424366,0.449894,0.549505,3.6727054,-7.144298,-2.9640217,0.16827393,0.77459717,-0.36462402,reading +-0.40222588,9.55047,2.1320367,0.430287,0.449814,0.549681,6.421249,-7.608773,-2.1068976,0.101745605,0.7675781,-0.4169922,reading +-0.4297592,9.606734,2.12246,0.426088,0.452766,0.552639,6.421249,-7.608773,-2.1068976,0.101745605,0.7675781,-0.4169922,reading +-0.36391866,9.574412,2.2038627,0.424412,0.453767,0.556486,6.397307,-8.312668,-2.3175871,0.09124756,0.7698364,-0.4336548,reading +-0.41180268,9.563639,2.2266076,0.42385,0.452525,0.556342,6.3350577,-7.9104424,-6.1530986,0.10070801,0.7746582,-0.4111328,reading +-0.10175357,9.544485,2.4301147,0.386091,0.48693,0.611933,6.7324953,-6.7133417,-4.247314,0.11340332,0.8291626,-0.30474854,reading +-0.19871874,9.470264,2.0638018,0.333603,0.521783,0.665704,7.091625,-6.1387334,-4.4005427,0.13031006,0.8366089,-0.26818848,reading +-0.21667525,9.563639,2.1428106,0.356477,0.510714,0.640012,6.8426285,-6.612785,-4.6160207,0.17901611,0.8216553,-0.24829102,reading +-0.22266077,8.842984,-4.537012,0.356985,0.765486,0.472036,3.0071173,-8.690952,-2.801216,0.2524414,0.791687,-0.47131348,reading +-1.3934253,9.868899,-1.4269443,0.324254,0.724224,0.54202,3.7014358,-7.824251,-0.038307227,-0.06591797,0.6723633,-0.69799805,reading +-0.40102878,9.528923,-1.316811,0.311577,0.703091,0.560119,6.143522,-5.2768207,3.8977604,-0.13586426,0.536499,-0.78045654,reading +-0.30885202,10.247183,-1.7669208,0.30072,0.703998,0.560515,2.815581,-0.23463176,8.66701,-0.1743164,-0.044921875,-0.9202881,reading +-1.4652514,9.01776,3.4009635,0.26317,0.521783,0.69711,-0.6751649,-5.291186,-8.714894,-0.37243652,0.88983154,-0.23016357,reading +-1.6124948,9.473856,3.21661,0.259809,0.520139,0.694731,-0.6751649,-5.291186,-8.714894,-0.37243652,0.88983154,-0.23016357,reading +-1.553837,9.440337,3.3387144,0.258307,0.524715,0.685753,-0.6751649,-5.291186,-8.714894,-0.37243652,0.88983154,-0.23016357,reading +-0.58298814,9.3421755,2.7593174,0.262307,0.523689,0.684083,-0.6751649,-5.291186,-8.714894,-0.37243652,0.88983154,-0.23016357,reading +-0.9576807,9.220071,3.0992942,0.263974,0.522447,0.683864,-0.6751649,-5.291186,-8.714894,-0.37243652,0.88983154,-0.23016357,reading +-0.8619126,8.913613,3.0322564,0.268156,0.520667,0.683479,-0.6751649,-5.291186,-8.714894,-0.37243652,0.88983154,-0.23016357,reading +-0.88944596,9.701305,2.9616275,0.272036,0.519901,0.681376,-0.6751649,-5.291186,-8.714894,-0.37243652,0.88983154,-0.23016357,reading +-0.5231331,8.980651,3.1483753,0.274279,0.520218,0.680266,-0.6751649,-5.291186,-8.714894,-0.37243652,0.88983154,-0.23016357,reading diff --git a/feedbackloop.learner_backup/src/main/resources/activity_definition.json b/feedbackloop.learner_backup/src/main/resources/activity_definition.json new file mode 100644 index 0000000000000000000000000000000000000000..9c4a9ea5f122755989b89bcc6fb1393e5321b941 --- /dev/null +++ b/feedbackloop.learner_backup/src/main/resources/activity_definition.json @@ -0,0 +1,18 @@ +{ + "name": "activity", + "columns": [ + { "kind": "input", "name": "m_accel_x", "type":"continuous" }, + { "kind": "input", "name": "m_accel_y", "type": "continuous" }, + { "kind": "input", "name": "m_accel_z", "type": "continuous" }, + { "kind": "input", "name": "m_rotation_x", "type": "continuous" }, + { "kind": "input", "name": "m_rotation_y", "type": "continuous" }, + { "kind": "input", "name": "m_rotation_z", "type": "continuous" }, + { "kind": "input", "name": "w_accel_x", "type": "continuous" }, + { "kind": "input", "name": "w_accel_y", "type": "continuous" }, + { "kind": "input", "name": "w_accel_z", "type": "continuous" }, + { "kind": "input", "name": "w_rotation_x", "type": "continuous" }, + { "kind": "input", "name": "w_rotation_y", "type": "continuous" }, + { "kind": "input", "name": "w_rotation_z", "type": "continuous" }, + { "kind": "target", "name": "labels", "type": "nominal" } + ] +} diff --git a/feedbackloop.learner_backup/src/main/resources/activity_network.eg b/feedbackloop.learner_backup/src/main/resources/activity_network.eg new file mode 100644 index 0000000000000000000000000000000000000000..fe2db897c735129ebcb8d841a141d1a41eb77646 --- /dev/null +++ b/feedbackloop.learner_backup/src/main/resources/activity_network.eg @@ -0,0 +1,24 @@ +encog,BasicNetwork,java,3.4.0,1,1573056726663 +[BASIC] +[BASIC:PARAMS] +[BASIC:NETWORK] +beginTraining=0 +connectionLimit=0 +contextTargetOffset=0,0,0 +contextTargetSize=0,0,0 +endTraining=2 +hasContext=f +inputCount=12 +layerCounts=6,20,13 +layerFeedCounts=6,19,12 +layerContextCount=0,0,0 +layerIndex=0,6,26 +output=-0.9999999998,-0.9998262405,-0.9932703249,-0.9999999975,-0.9963323982,0.9952233689,0.8950485885,-0.2471548675,-0.7134368163,-0.9998203621,-0.9999109929,0.9999771142,0.3960535735,0.9999686648,0.9999999913,-0.2732796415,-0.9032063845,0.2081346278,0.898536287,-0.9971402052,-0.6828243854,-0.9184217676,-0.9998798168,-0.1932547113,0.9936549563,1,0.4286079092,-0.264797324,-0.1866788711,0.1060429716,0.7912779246,0.6472232094,0.0475589101,0.0911549832,0.0406388653,-0.2327875517,0.5914319716,-0.8001432392,1 +outputCount=6 +weightIndex=0,120,367 +weights=-1.7088652862,14.2752997314,0.740715275,1.2708204681,-0.4036569286,0.5001665546,0.0510627805,-2.9066310901,-0.2229013934,0.9396399688,0.9183598904,0.1416988855,0.8605749633,0.3905960813,-4.0459063587,-0.0170059908,2.4624246765,-2.9285166906,-0.7918081248,-2.0136574195,0.1478608406,2.5631843207,-0.9042402055,-0.500729481,-0.4630983795,0.0549798828,-0.0977590272,-0.9530129648,-1.5263691622,2.7301976123,-1.4797516391,2.6986317319,2.0146594579,-0.1235918148,1.4572757618,3.1693321102,-0.4065944987,0.3409007327,-2.2486394978,-0.6060336368,1.8345153211,-0.16153149,3.2683859226,2.3278377242,0.6143746876,2.8760864787,-2.480894825,0.2254095198,-1.2581358145,-1.4160792407,1.7234292992,-0.6619454512,-0.3098164934,0.8935333972,0.572682518,-2.982461729,-0.8738574393,1.1012797036,-0.3932676143,-0.256108697,0.2758586826,2.2127488985,-0.1462069945,0.7379583108,1.7224441339,-1.2712399722,1.2290939449,-0.5113844261,3.4698304855,0.058031942,2.1958337137,-0.6861100881,-0.1289956756,3.2646590042,0.1194102889,1.0482716301,-0.3478084366,5.595088101,-1.800527516,-0.682039906,-0.8045783014,-0.31729294,-2.6348978495,-1.084771508,-1.8333889808,-2.1761151382,-0.0652946441,-0.196028085,0.6854932987,0.659761725,0.6996173621,-2.2696984236,-2.4985596864,-0.5664655703,1.9285987078,-2.1306999458,2.3762958334,-0.8974550018,-0.2328747935,-0.8344445737,0.0680257304,1.8940723951,-0.7222739165,1.2567665333,0.1537928557,0.5751549779,-0.0984708659,-1.9990615202,-0.5808651773,8.0012792546,0.6393304473,0.7872214157,2.3118540239,0.5645897035,-8.9168604976,-0.9494985541,-0.8077446719,0.4791333039,0.3655111935,-0.5896824972,0.7857840522,0.5238654196,-0.5514168427,1.7253022698,-1.5421428536,-1.6870718373,-1.41449094,-0.7453084143,-6.6371806823,-0.7906882244,2.8593694737,-1.5765304381,0.5432602673,0.67080304,0.4251434242,0.0682657046,-0.7209840053,0.5518314996,0.8094369702,0.1802793127,-0.1158826004,0.6344079775,-0.2737678288,-0.4980923143,0.1636091351,-0.9606778805,0.6562119197,-2.8917841429,-0.1210997433,-0.0391425506,0.2407236017,-0.1423270818,0.9681781252,-0.5598600307,-0.388628032,0.4014909677,-1.1192832575,1.2193571411,-0.3060765699,-0.1835597226,11.2443097204,-3.5664946066,-1.1492000058,0.2621528218,0.1678764461,0.1902651754,-3.2994277105,-11.02227939,-0.3893786806,-0.3400217232,0.8098044232,-0.9646277798,0.6470744963,-0.8634790397,0.5891430856,0.8349596657,-3.849727641,-1.4122175376,-6.9665054499,-0.4201546219,-2.0311154576,0.4919752675,-0.0940479522,0.4895338525,-0.5202666164,0.910913153,-0.7950927495,-0.5850007689,4.5770693733,2.4301725095,1.0306331342,-2.4907175407,1.2308782405,3.4864102012,0.3399583016,1.0982556011,-1.4094635012,0.0699311184,0.0492202838,-1.1756280277,1.302794241,0.6520771703,-0.6896449718,-0.7280381619,3.8654669365,2.1228029568,0.1757444382,-0.1413681423,1.0716643312,0.3849145532,0.5342981861,0.4640418476,-0.6646148594,-1.5554107056,-1.4624700127,1.13554699,1.1198715255,0.3540672649,1.0675049395,1.2352620786,-0.2614184979,1.2342286673,-1.0549762137,1.5991555107,0.7355069849,0.0606401695,-0.5497146072,1.8999576856,5.9377575427,7.5338358234,-0.0865799559,-3.0694638269,-3.0437525145,-2.704859121,0.9283397874,1.5269139975,-0.0995107467,0.9282047722,-1.2856460052,-0.0307327173,4.2613097335,0.257577656,0.5685645817,-0.0169828568,-0.5204521728,0.7168015356,-0.5859561953,0.2708517822,1.2065386892,-1.3601695201,0.1620367717,-0.6470949599,0.7873135349,1.1024505063,0.1579793327,-0.0017665327,2.822972495,0.0160673118,-0.4465177278,1.8980963961,-0.3773971979,0.943726363,-0.5213528238,-0.3897857227,-0.8630032021,2.5201910192,-0.575253653,0.8907255859,-0.7195215087,-0.5382433047,2.2851094383,-1.5540647847,0.2960692828,0.665211973,0.1405813785,0.1105460028,0.8068504487,2.0277954136,-1.7509079195,1.1171814188,0.129101145,0.5965653003,-2.0007574537,0.2396412696,-0.8020062738,-0.5588785126,1.0502688477,-0.8620666979,-0.6130290006,-0.7871503186,-0.6196680129,0.7774902198,-0.429619143,-1.1356565182,0.7263590549,-1.9679598636,-0.7285879264,2.7402886665,0.930229153,-0.664069337,0.6429741916,-1.3094505505,0.7978709596,-0.3499112457,-3.8104849889,0.5466548215,-2.2820640634,-1.0103239143,-16.004145782,8.7519010794,1.2152734978,-2.5156034018,-1.5446133472,-1.1614820056,-0.2661163326,0.0583080828,-0.3356454252,-1.3513237137,-0.6317676477,-0.2050401396,0.4081601902,-0.7397933013,0.3498065895,-0.581329787,2.0349560655,-0.7880680183,1.7382312616,0.3776435593,0.4898207616,0.4497504489,-1.1493424244,-0.3073277469,-1.9244321011,-0.0127378365,2.7008386292,-2.280812537,-0.9230709045,0.2856600067,-1.7975208467,3.097515774,0.123435157,0.3851379119,-0.8956612407,0.3849468804,-0.3434920946,0.2850125373,-0.1755239343,0.0828670202,0.8446469853,-0.1201831136,-0.1576389798,0.3703280934,-0.1546663502,-1.0571186475,1.334800809,-1.1064211025,-0.5226992813,1.555613798,0.7408293695,1.1076573982,-1.1994299372,0.6734668239,1.0278022031,-0.9930018696,-0.7971144873,0.3036302406,0.8814128057 +biasActivation=0,1,1 +[BASIC:ACTIVATION] +"org.encog.engine.network.activation.ActivationTANH" +"org.encog.engine.network.activation.ActivationTANH" +"org.encog.engine.network.activation.ActivationLinear" diff --git a/feedbackloop.learner_backup/src/main/resources/activity_normalizer.json b/feedbackloop.learner_backup/src/main/resources/activity_normalizer.json new file mode 100644 index 0000000000000000000000000000000000000000..696d9bdd1d5f54f0800989002820c9c788d14fc0 --- /dev/null +++ b/feedbackloop.learner_backup/src/main/resources/activity_normalizer.json @@ -0,0 +1,236 @@ +{ + "sourceColumns" : [ { + "name" : "m_accel_x", + "type" : "continuous", + "count" : 368, + "index" : 0, + "low" : -13.785813, + "high" : 4.9524064, + "mean" : -0.19340335027717379, + "sd" : 1.7490953819438357 + }, { + "name" : "m_accel_y", + "type" : "continuous", + "count" : 368, + "index" : 1, + "low" : -5.6156, + "high" : 35.582626, + "mean" : 7.109900721222825, + "sd" : 4.5921221593489046 + }, { + "name" : "m_accel_z", + "type" : "continuous", + "count" : 368, + "index" : 2, + "low" : -11.432313, + "high" : 13.4422455, + "mean" : 3.2416352282336933, + "sd" : 5.027733939700574 + }, { + "name" : "m_rotation_x", + "type" : "continuous", + "count" : 368, + "index" : 3, + "low" : -0.248631, + "high" : 0.764364, + "mean" : 0.3137794211956523, + "sd" : 0.21140628451422136 + }, { + "name" : "m_rotation_y", + "type" : "continuous", + "count" : 368, + "index" : 4, + "low" : -0.792455, + "high" : 0.877354, + "mean" : 0.1370161440217391, + "sd" : 0.39550889574156256 + }, { + "name" : "m_rotation_z", + "type" : "continuous", + "count" : 368, + "index" : 5, + "low" : -0.82801, + "high" : 0.857407, + "mean" : 0.2879983125, + "sd" : 0.40760406682498107 + }, { + "name" : "w_accel_x", + "type" : "continuous", + "count" : 368, + "index" : 6, + "low" : -37.52193, + "high" : 45.844173, + "mean" : -1.3088346191668498, + "sd" : 10.449275409586559 + }, { + "name" : "w_accel_y", + "type" : "continuous", + "count" : 368, + "index" : 7, + "low" : -57.436897, + "high" : 38.16836, + "mean" : -3.0629256689130435, + "sd" : 9.400961004585001 + }, { + "name" : "w_accel_z", + "type" : "continuous", + "count" : 368, + "index" : 8, + "low" : -24.181437, + "high" : 29.783869, + "mean" : 1.7380082924809785, + "sd" : 6.801019490227344 + }, { + "name" : "w_rotation_x", + "type" : "continuous", + "count" : 368, + "index" : 9, + "low" : -0.819397, + "high" : 0.9624634, + "mean" : 0.13214409923764678, + "sd" : 0.40673713347253454 + }, { + "name" : "w_rotation_y", + "type" : "continuous", + "count" : 368, + "index" : 10, + "low" : -0.8397827, + "high" : 0.88983154, + "mean" : 0.16942944796820666, + "sd" : 0.42887071522273607 + }, { + "name" : "w_rotation_z", + "type" : "continuous", + "count" : 368, + "index" : 11, + "low" : -0.97631836, + "high" : 0.9837036, + "mean" : -0.22766295774130452, + "sd" : 0.48663553612477467 + }, { + "name" : "labels", + "type" : "nominal", + "index" : 12, + "classes" : [ "working", "walking", "dancing", "lying", "getting up", "reading" ] + } ], + "inputColumns" : [ { + "name" : "m_accel_x", + "type" : "continuous", + "count" : 368, + "index" : 0, + "low" : -13.785813, + "high" : 4.9524064, + "mean" : -0.19340335027717379, + "sd" : 1.7490953819438357 + }, { + "name" : "m_accel_y", + "type" : "continuous", + "count" : 368, + "index" : 1, + "low" : -5.6156, + "high" : 35.582626, + "mean" : 7.109900721222825, + "sd" : 4.5921221593489046 + }, { + "name" : "m_accel_z", + "type" : "continuous", + "count" : 368, + "index" : 2, + "low" : -11.432313, + "high" : 13.4422455, + "mean" : 3.2416352282336933, + "sd" : 5.027733939700574 + }, { + "name" : "m_rotation_x", + "type" : "continuous", + "count" : 368, + "index" : 3, + "low" : -0.248631, + "high" : 0.764364, + "mean" : 0.3137794211956523, + "sd" : 0.21140628451422136 + }, { + "name" : "m_rotation_y", + "type" : "continuous", + "count" : 368, + "index" : 4, + "low" : -0.792455, + "high" : 0.877354, + "mean" : 0.1370161440217391, + "sd" : 0.39550889574156256 + }, { + "name" : "m_rotation_z", + "type" : "continuous", + "count" : 368, + "index" : 5, + "low" : -0.82801, + "high" : 0.857407, + "mean" : 0.2879983125, + "sd" : 0.40760406682498107 + }, { + "name" : "w_accel_x", + "type" : "continuous", + "count" : 368, + "index" : 6, + "low" : -37.52193, + "high" : 45.844173, + "mean" : -1.3088346191668498, + "sd" : 10.449275409586559 + }, { + "name" : "w_accel_y", + "type" : "continuous", + "count" : 368, + "index" : 7, + "low" : -57.436897, + "high" : 38.16836, + "mean" : -3.0629256689130435, + "sd" : 9.400961004585001 + }, { + "name" : "w_accel_z", + "type" : "continuous", + "count" : 368, + "index" : 8, + "low" : -24.181437, + "high" : 29.783869, + "mean" : 1.7380082924809785, + "sd" : 6.801019490227344 + }, { + "name" : "w_rotation_x", + "type" : "continuous", + "count" : 368, + "index" : 9, + "low" : -0.819397, + "high" : 0.9624634, + "mean" : 0.13214409923764678, + "sd" : 0.40673713347253454 + }, { + "name" : "w_rotation_y", + "type" : "continuous", + "count" : 368, + "index" : 10, + "low" : -0.8397827, + "high" : 0.88983154, + "mean" : 0.16942944796820666, + "sd" : 0.42887071522273607 + }, { + "name" : "w_rotation_z", + "type" : "continuous", + "count" : 368, + "index" : 11, + "low" : -0.97631836, + "high" : 0.9837036, + "mean" : -0.22766295774130452, + "sd" : 0.48663553612477467 + } ], + "outputColumns" : [ { + "name" : "labels", + "type" : "nominal", + "index" : 12, + "classes" : [ "working", "walking", "dancing", "lying", "getting up", "reading" ] + } ], + "inputLow" : -1.0, + "inputHigh" : 1.0, + "outputLow" : -1.0, + "outputHigh" : 1.0, + "unknownValues" : [ null ] +} \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/main/resources/learner_activity_phone_and_watch.json b/feedbackloop.learner_backup/src/main/resources/learner_activity_phone_and_watch.json new file mode 100644 index 0000000000000000000000000000000000000000..189a3c95f81e4c2cc959c3f01b2941d6577ad5af --- /dev/null +++ b/feedbackloop.learner_backup/src/main/resources/learner_activity_phone_and_watch.json @@ -0,0 +1,27 @@ +{ + "relevantItemNames": [ + "m_accel_x", + "m_accel_y", + "m_accel_z", + "m_rotation_x", + "m_rotation_y", + "m_rotation_z", + "w_accel_x", + "w_accel_y", + "w_accel_z", + "w_rotation_x", + "w_rotation_y", + "w_rotation_z" + ], + "targetItemNames": [ + "activity" + ], + "nonTrivialOutputMappings": { + "labels": "activity" + }, + "definitionFile": "./activity_definition.json", + "dataFiles": [ + "./activity_data.csv" + ], + "saveModels": false +} diff --git a/feedbackloop.learner_backup/src/main/resources/learner_preferences_brightness_iris.json b/feedbackloop.learner_backup/src/main/resources/learner_preferences_brightness_iris.json new file mode 100644 index 0000000000000000000000000000000000000000..b035fe9295552f7d54bbf401b97c5d5aa26b3274 --- /dev/null +++ b/feedbackloop.learner_backup/src/main/resources/learner_preferences_brightness_iris.json @@ -0,0 +1,14 @@ +{ + "relevantItemNames": [ + "activity", + "w_brightness" + ], + "targetItemNames": [ + "iris1_item" + ], + "definitionFile": "./preference_definition.json", + "dataFiles": [ + "./preference_data.csv" + ], + "saveModels": false +} diff --git a/feedbackloop.learner_backup/src/main/resources/loaded_learner_activity_phone_and_watch.json b/feedbackloop.learner_backup/src/main/resources/loaded_learner_activity_phone_and_watch.json new file mode 100644 index 0000000000000000000000000000000000000000..ea4d62637270f40906dec2479a4229d42a271eca --- /dev/null +++ b/feedbackloop.learner_backup/src/main/resources/loaded_learner_activity_phone_and_watch.json @@ -0,0 +1,28 @@ +{ + "relevantItemNames": [ + "m_accel_x", + "m_accel_y", + "m_accel_z", + "m_rotation_x", + "m_rotation_y", + "m_rotation_z", + "w_accel_x", + "w_accel_y", + "w_accel_z", + "w_rotation_x", + "w_rotation_y", + "w_rotation_z" + ], + "targetItemNames": [ + "activity" + ], + "nonTrivialOutputMappings": { + "labels": "activity" + }, + "definitionFile": "./activity_definition.json", + "dataFiles": [ + "./activity_network.eg", + "./activity_normalizer.json" + ], + "kind": "loaded" +} diff --git a/feedbackloop.learner_backup/src/main/resources/loaded_learner_preferences_brightness_iris.json b/feedbackloop.learner_backup/src/main/resources/loaded_learner_preferences_brightness_iris.json new file mode 100644 index 0000000000000000000000000000000000000000..f06dd1e5d0bba5cc76957f133cc66afa32ec92a7 --- /dev/null +++ b/feedbackloop.learner_backup/src/main/resources/loaded_learner_preferences_brightness_iris.json @@ -0,0 +1,15 @@ +{ + "relevantItemNames": [ + "activity", + "w_brightness" + ], + "targetItemNames": [ + "iris1_item" + ], + "definitionFile": "./preference_definition.json", + "dataFiles": [ + "./preference_network.eg", + "./preference_normalizer.json" + ], + "kind": "loaded" +} diff --git a/feedbackloop.learner_backup/src/main/resources/preference_data.csv b/feedbackloop.learner_backup/src/main/resources/preference_data.csv new file mode 100644 index 0000000000000000000000000000000000000000..264467a975f3de3001a57bc9902ec5d9308db23f --- /dev/null +++ b/feedbackloop.learner_backup/src/main/resources/preference_data.csv @@ -0,0 +1,600 @@ +walking,medium,120,70 +walking,bright,120,0 +walking,medium,120,70 +walking,bright,120,0 +walking,bright,120,0 +walking,medium,120,70 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,medium,120,70 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,medium,120,70 +walking,medium,120,70 +walking,dimmer,120,40 +walking,medium,120,70 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,medium,120,70 +walking,bright,120,0 +walking,bright,120,0 +walking,medium,120,70 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,medium,120,70 +walking,dark,120,100 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,medium,120,70 +walking,bright,120,0 +walking,medium,120,70 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,medium,120,70 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,medium,120,70 +walking,dimmer,120,40 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,medium,120,70 +walking,bright,120,0 +walking,dark,120,100 +walking,medium,120,70 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,medium,120,70 +walking,medium,120,70 +walking,medium,120,70 +walking,medium,120,70 +walking,bright,120,0 +walking,medium,120,70 +walking,medium,120,70 +walking,bright,120,0 +walking,dark,120,100 +walking,medium,120,70 +walking,bright,120,0 +walking,dimmer,120,40 +walking,medium,120,70 +walking,medium,120,70 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,medium,120,70 +walking,bright,120,0 +walking,bright,120,0 +walking,medium,120,70 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,medium,120,70 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +walking,medium,120,70 +walking,medium,120,70 +walking,bright,120,0 +walking,medium,120,70 +walking,bright,120,0 +walking,bright,120,0 +walking,bright,120,0 +reading,medium,180,70 +reading,medium,180,70 +reading,medium,180,70 +reading,bright,180,0 +reading,medium,180,70 +reading,bright,180,0 +reading,medium,180,70 +reading,bright,180,0 +reading,medium,180,70 +reading,bright,180,0 +reading,bright,180,0 +reading,medium,180,70 +reading,bright,180,0 +reading,bright,180,0 +reading,medium,180,70 +reading,bright,180,0 +reading,bright,180,0 +reading,medium,180,70 +reading,bright,180,0 +reading,dark,180,100 +reading,medium,180,70 +reading,medium,180,70 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,dimmer,180,40 +reading,medium,180,70 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,dimmer,180,40 +reading,bright,180,0 +reading,medium,180,70 +reading,bright,180,0 +reading,bright,180,0 +reading,medium,180,70 +reading,medium,180,70 +reading,bright,180,0 +reading,bright,180,0 +reading,dimmer,180,40 +reading,bright,180,0 +reading,dark,180,100 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,medium,180,70 +reading,medium,180,70 +reading,dimmer,180,40 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,medium,180,70 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,dimmer,180,40 +reading,bright,180,0 +reading,dimmer,180,40 +reading,medium,180,70 +reading,dimmer,180,40 +reading,bright,180,0 +reading,medium,180,70 +reading,bright,180,0 +reading,medium,180,70 +reading,dimmer,180,40 +reading,bright,180,0 +reading,dark,180,100 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,medium,180,70 +reading,bright,180,0 +reading,bright,180,0 +reading,medium,180,70 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,medium,180,70 +reading,bright,180,0 +reading,medium,180,70 +reading,dimmer,180,40 +reading,medium,180,70 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,bright,180,0 +reading,medium,180,70 +working,medium,240,70 +working,medium,240,70 +working,medium,240,70 +working,medium,240,70 +working,dark,240,100 +working,bright,240,0 +working,bright,240,0 +working,medium,240,70 +working,medium,240,70 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,medium,240,70 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,medium,240,70 +working,bright,240,0 +working,medium,240,70 +working,bright,240,0 +working,bright,240,0 +working,medium,240,70 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,medium,240,70 +working,bright,240,0 +working,medium,240,70 +working,bright,240,0 +working,medium,240,70 +working,bright,240,0 +working,bright,240,0 +working,medium,240,70 +working,medium,240,70 +working,bright,240,0 +working,medium,240,70 +working,bright,240,0 +working,dimmer,240,40 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,medium,240,70 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,medium,240,70 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,medium,240,70 +working,medium,240,70 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,medium,240,70 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,medium,240,70 +working,medium,240,70 +working,bright,240,0 +working,bright,240,0 +working,medium,240,70 +working,medium,240,70 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,medium,240,70 +working,medium,240,70 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,medium,240,70 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,bright,240,0 +working,medium,240,70 +working,medium,240,70 +dancing,medium,300,70 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,dimmer,300,40 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,dimmer,300,40 +dancing,dimmer,300,40 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,medium,300,70 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,medium,300,70 +dancing,dimmer,300,40 +dancing,bright,300,0 +dancing,bright,300,0 +dancing,medium,300,70 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,medium,7,70 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,dimmer,7,40 +lying,bright,7,0 +lying,bright,7,0 +lying,dark,7,100 +lying,bright,7,0 +lying,dark,7,100 +lying,dimmer,7,40 +lying,medium,7,70 +lying,bright,7,0 +lying,medium,7,70 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,medium,7,70 +lying,medium,7,70 +lying,bright,7,0 +lying,bright,7,0 +lying,medium,7,70 +lying,medium,7,70 +lying,bright,7,0 +lying,dimmer,7,40 +lying,bright,7,0 +lying,bright,7,0 +lying,medium,7,70 +lying,bright,7,0 +lying,bright,7,0 +lying,medium,7,70 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,medium,7,70 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,dimmer,7,40 +lying,medium,7,70 +lying,medium,7,70 +lying,dark,7,100 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,medium,7,70 +lying,bright,7,0 +lying,dimmer,7,40 +lying,bright,7,0 +lying,medium,7,70 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,dimmer,7,40 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,medium,7,70 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,dimmer,7,40 +lying,medium,7,70 +lying,medium,7,70 +lying,dark,7,100 +lying,medium,7,70 +lying,medium,7,70 +lying,bright,7,0 +lying,medium,7,70 +lying,medium,7,70 +lying,bright,7,0 +lying,bright,7,0 +lying,medium,7,70 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,bright,7,0 +lying,medium,7,70 +lying,bright,7,0 +lying,medium,7,70 +lying,medium,7,70 +lying,medium,7,70 +lying,medium,7,70 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,dimmer,60,40 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,dimmer,60,40 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,dark,60,100 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,dimmer,60,40 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,dimmer,60,40 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,dimmer,60,40 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,bright,60,0 +getting up,medium,60,70 +getting up,medium,60,70 diff --git a/feedbackloop.learner_backup/src/main/resources/preference_definition.json b/feedbackloop.learner_backup/src/main/resources/preference_definition.json new file mode 100644 index 0000000000000000000000000000000000000000..6ac043cd466c2d0705c6c399a57f7731b0bb10d1 --- /dev/null +++ b/feedbackloop.learner_backup/src/main/resources/preference_definition.json @@ -0,0 +1,9 @@ +{ + "name": "preference", + "columns": [ + { "kind": "input", "name": "activity", "type":"nominal" }, + { "kind": "input", "name": "w_brightness", "type": "nominal" }, + { "kind": "target", "name": "label1", "type": "continuous" }, + { "kind": "target", "name": "label2", "type": "continuous" } + ] +} diff --git a/feedbackloop.learner_backup/src/main/resources/preference_network.eg b/feedbackloop.learner_backup/src/main/resources/preference_network.eg new file mode 100644 index 0000000000000000000000000000000000000000..1587e1da370b0124f809132166145f4741384a76 --- /dev/null +++ b/feedbackloop.learner_backup/src/main/resources/preference_network.eg @@ -0,0 +1,24 @@ +encog,BasicNetwork,java,3.4.0,1,1573117410995 +[BASIC] +[BASIC:PARAMS] +[BASIC:NETWORK] +beginTraining=0 +connectionLimit=0 +contextTargetOffset=0,0,0 +contextTargetSize=0,0,0 +endTraining=2 +hasContext=f +inputCount=10 +layerCounts=2,7,11 +layerFeedCounts=2,6,10 +layerContextCount=0,0,0 +layerIndex=0,2,9 +output=-0.6381859116,0.3999935955,-0.6540824672,0.7389201853,0.9938086675,0.9744616654,-0.9998339089,0.911158752,1,-1,-1,-1,-1,-1,1,-1,1,-1,-1,1 +outputCount=2 +weightIndex=0,14,80 +weights=-2.2442844715,0.360914345,0.1013390668,-0.6391682758,1.3438935674,-0.4978456338,-0.170321037,0.8003732757,0.915248595,2.0407802432,-1.8826965793,-0.0195990503,-0.0125650557,0.0691792866,-0.4505245774,-0.3256543307,-0.4666017567,-0.5881366551,1.9781769307,-0.3349721024,-0.8605579705,-0.1100200458,0.1373966455,0.3000984192,-0.6132118127,-0.5722441964,0.4528791857,-0.5797529708,1.5866346514,-0.4129251382,0.4931087639,-0.9064649265,-0.0032546631,0.7438195148,-0.4720461064,0.2981426389,0.1522369118,0.7132860409,-0.5736723943,0.021228877,0.4853956744,0.5589259044,-1.3403586258,2.2111834668,-2.5310840076,2.7268975262,-0.2288535369,-0.5730428823,0.456874404,-0.8039689625,0.2972606181,0.6557258144,0.4426663626,0.7848014523,0.5849130255,-0.8301874694,-1.0571770329,0.076641765,-1.3871133692,0.3709447004,-0.4032415216,3.9107091517,-0.6455690937,-0.8192705336,0.2195543966,0.8971750301,0.3284791954,1.815129938,-0.5670301457,1.4246698687,-0.8056742788,0.1254233733,-0.1005919109,-0.6978992274,2.0372389285,0.2320539506,0.2233571532,0.7194574942,-0.7761286713,-0.6049784996 +biasActivation=0,1,1 +[BASIC:ACTIVATION] +"org.encog.engine.network.activation.ActivationTANH" +"org.encog.engine.network.activation.ActivationTANH" +"org.encog.engine.network.activation.ActivationLinear" diff --git a/feedbackloop.learner_backup/src/main/resources/preference_normalizer.json b/feedbackloop.learner_backup/src/main/resources/preference_normalizer.json new file mode 100644 index 0000000000000000000000000000000000000000..f606c0beee7af46f172904c428ca20e8ff1cadcd --- /dev/null +++ b/feedbackloop.learner_backup/src/main/resources/preference_normalizer.json @@ -0,0 +1,66 @@ +{ + "sourceColumns" : [ { + "name" : "activity", + "type" : "nominal", + "index" : 0, + "classes" : [ "walking", "reading", "working", "dancing", "lying", "getting up" ] + }, { + "name" : "w_brightness", + "type" : "nominal", + "index" : 1, + "classes" : [ "bright", "medium", "dimmer", "dark" ] + }, { + "name" : "label1", + "type" : "continuous", + "count" : 599, + "index" : 2, + "low" : 7.0, + "high" : 300.0, + "mean" : 151.2186978297162, + "sd" : 100.8570150267567 + }, { + "name" : "label2", + "type" : "continuous", + "count" : 599, + "index" : 3, + "low" : 0.0, + "high" : 100.0, + "mean" : 24.273789649415694, + "sd" : 33.34594053873994 + } ], + "inputColumns" : [ { + "name" : "activity", + "type" : "nominal", + "index" : 0, + "classes" : [ "walking", "reading", "working", "dancing", "lying", "getting up" ] + }, { + "name" : "w_brightness", + "type" : "nominal", + "index" : 1, + "classes" : [ "bright", "medium", "dimmer", "dark" ] + } ], + "outputColumns" : [ { + "name" : "label1", + "type" : "continuous", + "count" : 599, + "index" : 2, + "low" : 7.0, + "high" : 300.0, + "mean" : 151.2186978297162, + "sd" : 100.8570150267567 + }, { + "name" : "label2", + "type" : "continuous", + "count" : 599, + "index" : 3, + "low" : 0.0, + "high" : 100.0, + "mean" : 24.273789649415694, + "sd" : 33.34594053873994 + } ], + "inputLow" : -1.0, + "inputHigh" : 1.0, + "outputLow" : -1.0, + "outputHigh" : 1.0, + "unknownValues" : [ null ] +} \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerSubjectUnderTest.java b/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerSubjectUnderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3a42297c790140535eb08e337879b76e26ac4713 --- /dev/null +++ b/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerSubjectUnderTest.java @@ -0,0 +1,37 @@ +package de.tudresden.inf.st.eraser.feedbackloop.learner_backup; + +import de.tudresden.inf.st.eraser.jastadd.model.MachineLearningDecoder; +import de.tudresden.inf.st.eraser.jastadd.model.MachineLearningEncoder; +import de.tudresden.inf.st.eraser.jastadd.model.MachineLearningHandlerFactory; +import de.tudresden.inf.st.eraser.jastadd.model.Root; + +import java.io.IOException; +import java.net.URL; + +/** + * Data object for the test. + * + * @author rschoene - Initial contribution + */ +class LearnerSubjectUnderTest { + MachineLearningEncoder encoder; + MachineLearningDecoder decoder; + Root root; + private MachineLearningHandlerFactoryImpl factory; + + void init(LearnerTestSettings settings) { + root = LearnerTestUtils.createKnowledgeBase(settings); + factory = new MachineLearningHandlerFactoryImpl(); + factory.setKnowledgeBaseRoot(root); + } + + void initFor(MachineLearningHandlerFactory.MachineLearningHandlerFactoryTarget factoryTarget, URL configURL) throws IOException, ClassNotFoundException { + factory.initializeFor(factoryTarget, configURL); + encoder = factory.createEncoder(); + decoder = factory.createDecoder(); + } + + void shutdown() { + factory.shutdown(); + } +} diff --git a/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerTest.java b/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..51e2df578ab77fd2cc25868ccb9779b04cb76051 --- /dev/null +++ b/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerTest.java @@ -0,0 +1,129 @@ +package de.tudresden.inf.st.eraser.feedbackloop.learner_backup; + +import de.tudresden.inf.st.eraser.jastadd.model.Item; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static de.tudresden.inf.st.eraser.jastadd.model.MachineLearningHandlerFactory.MachineLearningHandlerFactoryTarget.ACTIVITY_RECOGNITION; +import static de.tudresden.inf.st.eraser.jastadd.model.MachineLearningHandlerFactory.MachineLearningHandlerFactoryTarget.PREFERENCE_LEARNING; + +/** + * Testing the learner. + * + * @author rschoene - Initial contribution + */ +public class LearnerTest { + + private static final Path base = Paths.get("src", "test", "resources"); + private static URL ACTIVITY_CONFIG; + private static URL LOADED_ACTIVITY_CONFIG; + private static URL ACTIVITY_DATA; + private static URL PREFERENCE_CONFIG; + private static URL LOADED_PREFERENCE_CONFIG; + private static URL PREFERENCE_DATA; + private static URL OCT_ACTIVITY_CONFIG; + private static URL OCT_LOADED_ACTIVITY_CONFIG; + private static URL OCT_ACTIVITY_DATA; + private LearnerSubjectUnderTest sut; + + @BeforeAll + public static void setData() throws MalformedURLException { + ACTIVITY_CONFIG = resolveFromBaseURL("learner_activity_phone_and_watch.json"); + LOADED_ACTIVITY_CONFIG = resolveFromBaseURL("loaded_learner_activity_phone_and_watch.json"); + ACTIVITY_DATA = resolveFromBaseURL("activity_data.csv"); + PREFERENCE_CONFIG = resolveFromBaseURL("learner_preferences_brightness_iris.json"); + LOADED_PREFERENCE_CONFIG = resolveFromBaseURL("loaded_learner_preferences_brightness_iris.json"); + PREFERENCE_DATA = resolveFromBaseURL("preference_data.csv"); + + OCT_ACTIVITY_CONFIG = resolveFromBaseURL("2019-oct-28-learner.json"); + OCT_LOADED_ACTIVITY_CONFIG = resolveFromBaseURL("2019-oct-28-loaded_learner.json"); + OCT_ACTIVITY_DATA = resolveFromBaseURL("activity_data/28_08_2019_H14_14/result_all_items_EVERYTHING.csv"); + } + + private static URL resolveFromBaseURL(String filename) throws MalformedURLException { + return base.resolve(filename).toUri().toURL(); + } + + @BeforeEach + public void initLearner() { + sut = new LearnerSubjectUnderTest(); + } + + @Test + public void testActivities() throws IOException { + LearnerTestUtils.testLearner(sut, settingsActivities()); + } + + @Disabled("Was ignored before") + @Test + public void testPreferences() throws IOException { + LearnerTestUtils.testLearner(sut, settingsPreferences()); + } + + @Test + public void testLoadedActivities() throws IOException { + LearnerTestUtils.testLearner(sut, settingsActivities() + .setConfigURL(LOADED_ACTIVITY_CONFIG)); + } + + @Test + public void testLoadedPreferences() throws IOException { + LearnerTestUtils.testLearner(sut, settingsPreferences() + .setConfigURL(LOADED_PREFERENCE_CONFIG)); + } + + @Disabled("takes longer than 10min") + @Test + public void testOctoberActivities() throws IOException { + LearnerTestUtils.testLearner(sut, settingsActivities() + .setConfigURL(OCT_ACTIVITY_CONFIG) + .setDataURL(OCT_ACTIVITY_DATA)); + } + + @Disabled("not working currently") + @Test + public void testLoadedOctoberActivities() throws IOException { + LearnerTestUtils.testLearner(sut, settingsActivities() + .setConfigURL(OCT_LOADED_ACTIVITY_CONFIG) + .setDataURL(OCT_ACTIVITY_DATA) + .setVerbose(true)); + } + + private LearnerTestSettings settingsActivities() throws IOException { + return new LearnerTestSettings() + .setConfigURL(ACTIVITY_CONFIG) + .setDataURL(ACTIVITY_DATA) + .setOutputItemProvider(() -> sut.root.getSmartHomeEntityModel().getActivityItem()) + .setStateOfOutputItem(item -> sut.root.currentActivityName()) + .setFactoryTarget(ACTIVITY_RECOGNITION) + .setSingleUpdateList(false); + } + + private LearnerTestSettings settingsPreferences() throws IOException { + return new LearnerTestSettings() + .setConfigURL(PREFERENCE_CONFIG) + .setDataURL(PREFERENCE_DATA) + .setExpectedOutput(LearnerTestUtils::decodeOutput) + .putSpecialInputHandler("activity", (item, value) -> item.setStateFromLong( + sut.root.resolveActivity(value) + .orElseThrow(() -> new AssertionError("Activity " + value + " not found")) + .getIdentifier())) + .setOutputItemProvider(() -> sut.root.getSmartHomeEntityModel() + .resolveItem(LearnerTestConstants.PREFERENCE_OUTPUT_ITEM_NAME) + .orElseThrow(() -> new AssertionError( + "Item " + LearnerTestConstants.PREFERENCE_OUTPUT_ITEM_NAME + " not found"))) + .setStateOfOutputItem(Item::getStateAsString) + .setCheckUpdate(LearnerTestUtils::colorSimilar) + .setFactoryTarget(PREFERENCE_LEARNING) + .setSingleUpdateList(true); + } + +} diff --git a/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerTestConstants.java b/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerTestConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..5ba440384c37dcd67f006b67a6a41dcb2eb7d94b --- /dev/null +++ b/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerTestConstants.java @@ -0,0 +1,33 @@ +package de.tudresden.inf.st.eraser.feedbackloop.learner_backup; + +/** + * Constants for the Learner tests. + * + * @author rschoene - Initial contribution + */ +public interface LearnerTestConstants { + /** Minimal accuracy (correct / total classifications) */ + double MIN_ACCURACY = 0.8; + /** Maximum difference when comparing colors */ + double MAX_COLOR_DIFFERENCE = 0.2; + /** Weights for difference (in order: Hue, Saturation, Brightness) when comparing colors */ + double[] COLOR_WEIGHTS = new double[]{0.8/360, 0.1/100, 0.1/100}; + /** Name of the item which is targeted by preference learning, in test data */ + String PREFERENCE_OUTPUT_ITEM_NAME = "iris1_item"; + /** Labels of activities, in test data */ + String[] ACTIVITY_NAMES = new String[]{ + "working", + "walking", + "dancing", + "lying", + "getting up", + "reading", + "DoorClosedPIn", + "DoorClosedPOut", + "DoorOpenedPIn", + "DoorOpenedPOut", + "Reading", + "TVWatching", + "Working", + }; +} diff --git a/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerTestSettings.java b/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerTestSettings.java new file mode 100644 index 0000000000000000000000000000000000000000..c016f604711caf87c6b38360ed1211403b988146 --- /dev/null +++ b/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerTestSettings.java @@ -0,0 +1,55 @@ +package de.tudresden.inf.st.eraser.feedbackloop.learner_backup; + +import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.LearnerTestUtils.CheckUpdate; +import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.data.LearnerScenarioDefinition; +import de.tudresden.inf.st.eraser.jastadd.model.Item; +import de.tudresden.inf.st.eraser.jastadd.model.MachineLearningHandlerFactory; +import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.IOException; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; + +@Data +@Accessors(chain = true) +class LearnerTestSettings { + private URL configURL; + private URL dataURL; + private Function<List<String>, String> expectedOutput = targetValues -> targetValues.get(0); + private final Map<String, BiConsumer<Item, String>> specialInputHandler = new HashMap<>(); + private Supplier<Item> outputItemProvider; + private Function<Item, String> stateOfOutputItem; + private CheckUpdate checkUpdate = String::equals; + private MachineLearningHandlerFactory.MachineLearningHandlerFactoryTarget factoryTarget; + private boolean singleUpdateList; + private boolean verbose = false; + + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private transient LearnerScenarioDefinition scenarioSettings; + + @SuppressWarnings("SameParameterValue") + LearnerTestSettings putSpecialInputHandler(String itemName, BiConsumer<Item, String> handler) { + specialInputHandler.put(itemName, handler); + return this; + } + + LearnerTestSettings setConfigURL(URL configURL) throws IOException { + scenarioSettings = LearnerScenarioDefinition.loadFrom(configURL); + this.configURL = configURL; + return this; + } + + LearnerScenarioDefinition getScenarioSettings() { + return scenarioSettings; + } +} diff --git a/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerTestUtils.java b/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerTestUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..50d235f4b736ea0cb2d29f951004a977f61b449f --- /dev/null +++ b/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerTestUtils.java @@ -0,0 +1,227 @@ +package de.tudresden.inf.st.eraser.feedbackloop.learner_backup; + +import com.opencsv.CSVParserBuilder; +import com.opencsv.CSVReader; +import com.opencsv.CSVReaderBuilder; +import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.data.LearnerScenarioDefinition; +import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.data.LearnerSettings; +import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.data.SimpleColumnDefinition; +import de.tudresden.inf.st.eraser.jastadd.model.*; +import de.tudresden.inf.st.eraser.util.ParserUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.encog.ml.data.versatile.columns.ColumnType; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +import static de.tudresden.inf.st.eraser.feedbackloop.learner_backup.LearnerTestConstants.COLOR_WEIGHTS; +import static de.tudresden.inf.st.eraser.feedbackloop.learner_backup.LearnerTestConstants.MAX_COLOR_DIFFERENCE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Utility methods to keep test code clean. + * + * @author rschoene - Initial contribution + */ +public class LearnerTestUtils { + + private static final Logger logger = LogManager.getLogger(LearnerTestUtils.class); + + static Root createKnowledgeBase(LearnerTestSettings settings) { + Root result = Root.createEmptyRoot(); + Group group = new Group(); + result.getSmartHomeEntityModel().addGroup(group); + // init items + Stream.concat(settings.getScenarioSettings().relevantItemNames.stream(), + settings.getScenarioSettings().targetItemNames.stream()) + .distinct().forEach( + itemName -> { + if (itemName.equals("activity")) return; + Item item = createItem(itemName); + group.addItem(item); + item.setTopic(result.getMqttRoot().getOrCreateMqttTopic(itemName)); + } + ); + // init activities + for (int i = 0; i < LearnerTestConstants.ACTIVITY_NAMES.length; i++) { + result.getMachineLearningRoot().addActivity(new Activity(i, LearnerTestConstants.ACTIVITY_NAMES[i])); + } + return result; + } + + private static Item createItem(String itemName) { + Item item; + if (itemName.contains("OpenClose")) { + // contact item + item = new ContactItem(); + } else if (itemName.contains("Fibaro")) { + // boolean item + item = new SwitchItem(); + } else { + switch (itemName) { + case "work_device_online_state": + item = new SwitchItem(); + break; + case LearnerTestConstants.PREFERENCE_OUTPUT_ITEM_NAME: + item = new ColorItem(); + break; + case "w_brightness": + item = new StringItem(); + break; + default: item = new NumberItem(); + } + } + item.setID(itemName); + return item; + } + + static void testLearner(LearnerSubjectUnderTest sut, LearnerTestSettings settings) { + sut.init(settings); + // maybe use factory.createModel() here instead + // go through same csv as for training and test some of the values + int correct = 0, wrong = 0; + try(InputStream is = settings.getDataURL().openStream(); + Reader reader = new InputStreamReader(is); + CSVReader csvreader = createCSVReader(reader, settings)) { + sut.initFor(settings.getFactoryTarget(), settings.getConfigURL()); + int index = 0; + LearnerScenarioDefinition scenarioSettings = settings.getScenarioSettings(); + LearnerSettings definition = LearnerSettings.loadFrom(scenarioSettings.getDefinitionFileAsURL()); + List<String> targetValues; + for (String[] line : csvreader) { + // only check every 10th line, push an update for every input column + if (++index % 10 == 0) { + // Attention: Not every column might be relevant + targetValues = new ArrayList<>(); + int lineSize = line.length; + int inputSize = scenarioSettings.relevantItemNames.size(); + List<Item> itemsToUpdate = new ArrayList<>(inputSize); + for (int i = 0; i < lineSize; i++) { + SimpleColumnDefinition column = definition.columns.get(i); + switch (column.kind) { + case input: + // do nothing + break; + case target: + targetValues.add(line[i]); + continue; + case ignored: + continue; + } + if (column.type == ColumnType.ignore) { + continue; + } + // use itemName == name of column (or a non-trivial mapping, if any) + String itemName = scenarioSettings.nonTrivialOutputMappings.getOrDefault(column.name, column.name); + Item item = sut.root.getSmartHomeEntityModel().resolveItem(itemName) + .orElseThrow(() -> new AssertionError("Item " + itemName + " not found")); + if (settings.getSpecialInputHandler().containsKey(itemName)) { + if (settings.isVerbose()) { + logger.debug("Setting {} {} using special handler and value '{}' (column {})", + item, item.getID(), line[i], i); + } + settings.getSpecialInputHandler().get(itemName).accept(item, line[i]); + } else { + if (settings.isVerbose()) { + logger.debug("Setting {} {} using '{}' (column {})", item, item.getID(), line[i], i); + } + item.setStateFromString(line[i]); + } + itemsToUpdate.add(item); + } + if (settings.isSingleUpdateList()) { + sut.encoder.newData(itemsToUpdate); + } else { + itemsToUpdate.forEach(item -> sut.encoder.newData(Collections.singletonList(item))); + } + MachineLearningResult result = sut.decoder.classify(); + // check if only one item is to be updated + assertEquals(1, result.getNumItemUpdate(), "Not one item update!"); + ItemUpdate update = result.getItemUpdate(0); + // check that the output item is to be updated + assertEquals(settings.getOutputItemProvider().get(), update.getItem(), + "Output item not to be updated!"); + update.apply(); + // check if the correct new state was set + assertThat(targetValues).isNotEmpty(); + String expected = settings.getExpectedOutput().apply(targetValues); + String actual = settings.getStateOfOutputItem().apply(update.getItem()); + if (settings.getCheckUpdate().assertEquals(expected, actual)) { + correct++; + } else { + wrong++; + if (settings.isVerbose()) { + logger.debug("Result not equal, expected '{}' but was '{}'", expected, actual); + } + } + } // end if index % 10 == 0 + } // end for + } catch (IOException | ClassNotFoundException e) { + throw new AssertionError(e); + } finally { + sut.shutdown(); + } + assertThat(correct + wrong).isGreaterThan(0); + double accuracy = correct * 1.0 / (correct + wrong); + logger.info("Accuracy: {}", accuracy); + assertThat(accuracy).isGreaterThan(LearnerTestConstants.MIN_ACCURACY); + } + + private static CSVReader createCSVReader(Reader reader, LearnerTestSettings settings) { + char separator; + switch(settings.getScenarioSettings().csvFormat) { + case "DECIMAL_POINT": separator=','; break; + case "DECIMAL_COMMA": separator=';'; break; + default: + logger.warn("Unknown CSV format, using default comma as separator"); + separator=','; + } + return new CSVReaderBuilder(reader) + .withCSVParser(new CSVParserBuilder().withSeparator(separator).build()) + .build(); + } + + @FunctionalInterface + public interface CheckUpdate { + boolean assertEquals(String expected, String actual); + } + + static String decodeOutput(List<String> targetValues) { + int color = Integer.parseInt(targetValues.get(0)); + int brightness = Integer.parseInt(targetValues.get(1)); + return TupleHSB.of(color, 100, brightness).toString(); + } + + private static int hueDistance(int hue1, int hue2) { + int d = Math.abs(hue1 - hue2); + return d > 180 ? 360 - d : d; + } + + /** + * Compares two colours given as strings of the form "HUE,SATURATION,BRIGHTNESS" + * @param expected the expected colour + * @param actual the computed, actual colour + * @return <code>true</code>, if both a colours are similar + */ + static boolean colorSimilar(String expected, String actual) { + TupleHSB expectedTuple = TupleHSB.parse(expected); + TupleHSB actualTuple = TupleHSB.parse(actual); + int diffHue = hueDistance(expectedTuple.getHue(), actualTuple.getHue()); + int diffSaturation = Math.abs(expectedTuple.getSaturation() - actualTuple.getSaturation()); + int diffBrightness = Math.abs(expectedTuple.getBrightness() - actualTuple.getBrightness()); + double total = diffHue * COLOR_WEIGHTS[0] + + diffSaturation * COLOR_WEIGHTS[1] + + diffBrightness * COLOR_WEIGHTS[2]; +// logger.debug("Diff expected {} and actual {}: H={} + S={} + B={} -> {} < {} ?", expected, actual, +// diffHue, diffSaturation, diffBrightness, total, MAX_COLOR_DIFFERENCE); + return total < MAX_COLOR_DIFFERENCE; + } +} diff --git a/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerTestUtilsTest.java b/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerTestUtilsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f1267de0e9f42fe8f86f33108aae7b66012876bc --- /dev/null +++ b/feedbackloop.learner_backup/src/test/java/de/tudresden/inf/st/eraser/feedbackloop/learner_backup/LearnerTestUtilsTest.java @@ -0,0 +1,92 @@ +package de.tudresden.inf.st.eraser.feedbackloop.learner_backup; + +import de.tudresden.inf.st.eraser.jastadd.model.TupleHSB; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for the utility methods. + * + * @author rschoene - Initial contribution + */ +public class LearnerTestUtilsTest { + + @Test + public void testColorSimilar() { + Map<String, TupleHSB> colors = new HashMap<>(); + + // reddish target colors + colors.put("pink", TupleHSB.of(350, 100, 82)); + colors.put("orangeRed", TupleHSB.of(16, 100, 45)); + colors.put("lightPink", TupleHSB.of(351, 100, 80)); + colors.put("darkSalmon", TupleHSB.of(15, 71, 67)); + colors.put("lightCoral", TupleHSB.of(0, 78, 63)); + colors.put("darkRed", TupleHSB.of(0, 100, 16)); + colors.put("indianRed", TupleHSB.of(0, 53, 49)); + colors.put("lavenderBlush", TupleHSB.of(340, 100, 95)); + colors.put("lavender", TupleHSB.of(240, 66, 90)); + String[] targetColors = new String[]{"pink", "orangeRed", "lightPink", "darkSalmon", "lightCoral", + "darkRed", "indianRed", "lavenderBlush", "lavender"}; + + // reference colors + colors.put("blue", TupleHSB.of(240, 100, 11)); + colors.put("blueViolet", TupleHSB.of(271, 75, 36)); + colors.put("magenta", TupleHSB.of(300, 100, 41)); + colors.put("purple", TupleHSB.of(300, 100, 20)); + colors.put("red", TupleHSB.of(0, 100, 29)); + colors.put("tomato", TupleHSB.of(9, 100, 55)); + colors.put("orange", TupleHSB.of(39, 100, 67)); + colors.put("yellow", TupleHSB.of(60, 100, 88)); + colors.put("yellowGreen", TupleHSB.of(80, 60, 67)); + colors.put("green", TupleHSB.of(120, 100, 29)); + colors.put("springGreen", TupleHSB.of(150, 100, 64)); + colors.put("cyan", TupleHSB.of(180, 100, 69)); + colors.put("ivory", TupleHSB.of(60, 100, 98)); + + String[] referenceColors = new String[]{"blue", "blueViolet", "magenta", "purple", "red", "tomato", + "orange", "yellow", "yellowGreen", "green", "springGreen", "cyan", "ivory"}; + + /* Code to help producing similarity matrix */ +// for (String target : targetColors) { +// String tmp = ""; +// for (String reference : referenceColors) { +// tmp += assertColorSimilar(colors, target, reference) ? "x" : " "; +// tmp += ","; +// } +// System.out.println( "***" + target + ": " + tmp); +// } + + String[] similarityMatrix = new String[]{ + "blue, blueViolet, magenta, purple, red, tomato, orange, yellow, yellowGreen, green, springGreen, cyan, ivory", // <- reference colors + " , , x , x , x , x , x , x , , , , , x ", // pink + " , , x , x , x , x , x , x , , , , , x ", // orangeRed + " , , x , x , x , x , x , x , , , , , x ", // lightPink + " , , , , x , x , x , x , x , , , , x ", // darkSalmon + " , , x , x , x , x , x , x , x , , , , x ", // lightCoral + " , , x , x , x , x , x , , , , , , ", // darkRed + " , , x , , x , x , x , , , , , , ", // indianRed + " , , x , x , x , x , x , x , , , , , x ", // lavenderBlush + " x , x , , , , , , , , , , x , "}; // lavender + + for (int targetIndex = 0; targetIndex < targetColors.length; targetIndex++) { + String target = targetColors[targetIndex]; + String[] expectedValues = similarityMatrix[targetIndex + 1].split(","); + for (int referenceIndex = 0; referenceIndex < referenceColors.length; referenceIndex++) { + String reference = referenceColors[referenceIndex]; + boolean expectedToBeSimilar = expectedValues[referenceIndex].contains("x"); + String message = String.format("%s iss%s expected to be similar to %s, but %s!", + target, expectedToBeSimilar ? "" : " not", + reference, expectedToBeSimilar ? "differs" : "it was"); + assertEquals(expectedToBeSimilar, + LearnerTestUtils.colorSimilar( + colors.get(reference).toString(), + colors.get(target).toString()), + message); + } + } + } +} diff --git a/feedbackloop.learner_backup/src/test/resources/2019-oct-28-activity_definition.json b/feedbackloop.learner_backup/src/test/resources/2019-oct-28-activity_definition.json new file mode 120000 index 0000000000000000000000000000000000000000..de4c267d5685e494fef742dfab57c4c8fb4241d8 --- /dev/null +++ b/feedbackloop.learner_backup/src/test/resources/2019-oct-28-activity_definition.json @@ -0,0 +1 @@ +../../../src/main/resources/2019-oct-28-activity_definition.json \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/test/resources/2019-oct-28-activity_network.eg b/feedbackloop.learner_backup/src/test/resources/2019-oct-28-activity_network.eg new file mode 120000 index 0000000000000000000000000000000000000000..cc1061a8dcd82c6c56f95a71cd03eaea194c6223 --- /dev/null +++ b/feedbackloop.learner_backup/src/test/resources/2019-oct-28-activity_network.eg @@ -0,0 +1 @@ +../../../src/main/resources/2019-oct-28-activity_network.eg \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/test/resources/2019-oct-28-activity_normalizer.json b/feedbackloop.learner_backup/src/test/resources/2019-oct-28-activity_normalizer.json new file mode 120000 index 0000000000000000000000000000000000000000..155f7b9fb29dab79e193c4691dc0e825172086bd --- /dev/null +++ b/feedbackloop.learner_backup/src/test/resources/2019-oct-28-activity_normalizer.json @@ -0,0 +1 @@ +../../../src/main/resources/2019-oct-28-activity_normalizer.json \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/test/resources/2019-oct-28-learner.json b/feedbackloop.learner_backup/src/test/resources/2019-oct-28-learner.json new file mode 120000 index 0000000000000000000000000000000000000000..dff51597d8d463305dbbcb3a125428415935dc8e --- /dev/null +++ b/feedbackloop.learner_backup/src/test/resources/2019-oct-28-learner.json @@ -0,0 +1 @@ +../../../src/main/resources/2019-oct-28-learner.json \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/test/resources/2019-oct-28-loaded_learner.json b/feedbackloop.learner_backup/src/test/resources/2019-oct-28-loaded_learner.json new file mode 120000 index 0000000000000000000000000000000000000000..9fb3eb166164d7030b978f57c70177058b4c80df --- /dev/null +++ b/feedbackloop.learner_backup/src/test/resources/2019-oct-28-loaded_learner.json @@ -0,0 +1 @@ +../../../src/main/resources/2019-oct-28-loaded_learner.json \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/test/resources/activity_data.csv b/feedbackloop.learner_backup/src/test/resources/activity_data.csv new file mode 120000 index 0000000000000000000000000000000000000000..997328a124244ce64ca257785ae7ac4bd29e18e2 --- /dev/null +++ b/feedbackloop.learner_backup/src/test/resources/activity_data.csv @@ -0,0 +1 @@ +../../../src/main/resources/activity_data.csv \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/test/resources/activity_definition.json b/feedbackloop.learner_backup/src/test/resources/activity_definition.json new file mode 120000 index 0000000000000000000000000000000000000000..c6b66b3d812b86097acfc6e296135872f37490ac --- /dev/null +++ b/feedbackloop.learner_backup/src/test/resources/activity_definition.json @@ -0,0 +1 @@ +../../../src/main/resources/activity_definition.json \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/test/resources/activity_network.eg b/feedbackloop.learner_backup/src/test/resources/activity_network.eg new file mode 120000 index 0000000000000000000000000000000000000000..9fb9ad041a1482c382dc8937be69554d540a46d4 --- /dev/null +++ b/feedbackloop.learner_backup/src/test/resources/activity_network.eg @@ -0,0 +1 @@ +../../../src/main/resources/activity_network.eg \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/test/resources/activity_normalizer.json b/feedbackloop.learner_backup/src/test/resources/activity_normalizer.json new file mode 120000 index 0000000000000000000000000000000000000000..eb750afe0f3ac6d934b4ec646736ed10d17cde1d --- /dev/null +++ b/feedbackloop.learner_backup/src/test/resources/activity_normalizer.json @@ -0,0 +1 @@ +../../../src/main/resources/activity_normalizer.json \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/test/resources/learner_activity_phone_and_watch.json b/feedbackloop.learner_backup/src/test/resources/learner_activity_phone_and_watch.json new file mode 120000 index 0000000000000000000000000000000000000000..b0544decd42f5ac2b3fb8d92019ebba4300d634f --- /dev/null +++ b/feedbackloop.learner_backup/src/test/resources/learner_activity_phone_and_watch.json @@ -0,0 +1 @@ +../../../src/main/resources/learner_activity_phone_and_watch.json \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/test/resources/learner_preferences_brightness_iris.json b/feedbackloop.learner_backup/src/test/resources/learner_preferences_brightness_iris.json new file mode 120000 index 0000000000000000000000000000000000000000..ce4f8226e2ea28143aed36222776651c66c6d093 --- /dev/null +++ b/feedbackloop.learner_backup/src/test/resources/learner_preferences_brightness_iris.json @@ -0,0 +1 @@ +../../../src/main/resources/learner_preferences_brightness_iris.json \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/test/resources/loaded_learner_activity_phone_and_watch.json b/feedbackloop.learner_backup/src/test/resources/loaded_learner_activity_phone_and_watch.json new file mode 120000 index 0000000000000000000000000000000000000000..6264f533f48488fecb13fb7650c5d0f0333d89f8 --- /dev/null +++ b/feedbackloop.learner_backup/src/test/resources/loaded_learner_activity_phone_and_watch.json @@ -0,0 +1 @@ +../../../src/main/resources/loaded_learner_activity_phone_and_watch.json \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/test/resources/loaded_learner_preferences_brightness_iris.json b/feedbackloop.learner_backup/src/test/resources/loaded_learner_preferences_brightness_iris.json new file mode 120000 index 0000000000000000000000000000000000000000..eae6f0e77a2d2566e64a000f1f3c7319eb43c755 --- /dev/null +++ b/feedbackloop.learner_backup/src/test/resources/loaded_learner_preferences_brightness_iris.json @@ -0,0 +1 @@ +../../../src/main/resources/loaded_learner_preferences_brightness_iris.json \ No newline at end of file diff --git a/eraser.rest/src/main/resources/log4j2.xml b/feedbackloop.learner_backup/src/test/resources/log4j2-test.xml similarity index 52% rename from eraser.rest/src/main/resources/log4j2.xml rename to feedbackloop.learner_backup/src/test/resources/log4j2-test.xml index 867ec439d0a32dcb5f8b3e2d0c7485d7d8da418c..8e963f0161f4fedbfc0ea28884cce0d22b42186a 100644 --- a/eraser.rest/src/main/resources/log4j2.xml +++ b/feedbackloop.learner_backup/src/test/resources/log4j2-test.xml @@ -4,8 +4,8 @@ <Console name="Console"> <PatternLayout pattern="%highlight{%d{HH:mm:ss.SSS} %-5level} %c{1.} - %msg%n"/> </Console> - <RollingFile name="RollingFile" fileName="logs/eraser.log" - filePattern="logs/eraser-%i.log"> + <RollingFile name="RollingFile" fileName="logs/eraser-test.log" + filePattern="logs/eraser-test-%i.log"> <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n"/> <Policies> <OnStartupTriggeringPolicy/> @@ -18,5 +18,13 @@ <AppenderRef ref="Console"/> <AppenderRef ref="RollingFile"/> </Root> + <Logger name="de.tudresden.inf.st.eraser.openhab2.mqtt" level="DEBUG" additivity="false"> + <Appender-ref ref="Console"/> + </Logger> + <!-- Stubs reduce noise--> + <Logger name="de.tudresden.inf.st.eraser.jastadd.model.InfluxAdapterStub" level="WARN" additivity="false"> + </Logger> + <Logger name="de.tudresden.inf.st.eraser.jastadd.model.MQTTSenderStub" level="WARN" additivity="false"> + </Logger> </Loggers> </Configuration> diff --git a/feedbackloop.learner_backup/src/test/resources/preference_data.csv b/feedbackloop.learner_backup/src/test/resources/preference_data.csv new file mode 120000 index 0000000000000000000000000000000000000000..2a30bc618d90a2e2ce6c6445b241b3734fdfe46c --- /dev/null +++ b/feedbackloop.learner_backup/src/test/resources/preference_data.csv @@ -0,0 +1 @@ +../../../src/main/resources/preference_data.csv \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/test/resources/preference_definition.json b/feedbackloop.learner_backup/src/test/resources/preference_definition.json new file mode 120000 index 0000000000000000000000000000000000000000..0f88f9356a1a5055d8d892afa6844c47660798e5 --- /dev/null +++ b/feedbackloop.learner_backup/src/test/resources/preference_definition.json @@ -0,0 +1 @@ +../../../src/main/resources/preference_definition.json \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/test/resources/preference_network.eg b/feedbackloop.learner_backup/src/test/resources/preference_network.eg new file mode 120000 index 0000000000000000000000000000000000000000..29979f04ddec79c371de61cfce01a679692614d8 --- /dev/null +++ b/feedbackloop.learner_backup/src/test/resources/preference_network.eg @@ -0,0 +1 @@ +../../../src/main/resources/preference_network.eg \ No newline at end of file diff --git a/feedbackloop.learner_backup/src/test/resources/preference_normalizer.json b/feedbackloop.learner_backup/src/test/resources/preference_normalizer.json new file mode 120000 index 0000000000000000000000000000000000000000..d72f3f68b5e6b37e1ace422429ec8e8a605d47f9 --- /dev/null +++ b/feedbackloop.learner_backup/src/test/resources/preference_normalizer.json @@ -0,0 +1 @@ +../../../src/main/resources/preference_normalizer.json \ No newline at end of file diff --git a/feedbackloop.main/build.gradle b/feedbackloop.main/build.gradle index 7e66daa3efddb2ef62735abc2fe6e57bab91d053..c1ff89c5aebae853e7d2716cb6b6f1b4b6c2bfd2 100644 --- a/feedbackloop.main/build.gradle +++ b/feedbackloop.main/build.gradle @@ -1,26 +1,14 @@ -apply plugin: 'application' - -dependencies { - compile project(':eraser-base') - compile project(':feedbackloop.api') - compile project(':feedbackloop.monitor') - compile project(':feedbackloop.analyze') - compile project(':feedbackloop.plan') - compile project(':feedbackloop.execute') +plugins { + id 'eraser.java-application-conventions' } -run { - mainClassName = 'de.tudresden.inf.st.eraser.feedbackloop.main.Main' - standardInput = System.in - if (project.hasProperty("appArgs")) { - args Eval.me(appArgs) - } +dependencies { + implementation project(':eraser-base') + implementation project(':feedbackloop.api') + implementation project(':feedbackloop.monitor') + implementation project(':feedbackloop.analyze') + implementation project(':feedbackloop.plan') + implementation project(':feedbackloop.execute') } -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} +application.mainClass = 'de.tudresden.inf.st.eraser.feedbackloop.main.Main' diff --git a/feedbackloop.monitor/build.gradle b/feedbackloop.monitor/build.gradle index e3f4c6e283e32e73a0a54ffe1f03d019c81c6d8a..7b20211d09ea803af8c249af518cf9ae038d3036 100644 --- a/feedbackloop.monitor/build.gradle +++ b/feedbackloop.monitor/build.gradle @@ -1,22 +1,10 @@ -apply plugin: 'application' - -dependencies { - compile project(':eraser-base') - compile project(':feedbackloop.api') +plugins { + id 'eraser.java-application-conventions' } -run { - mainClassName = 'de.tudresden.inf.st.eraser.feedbackloop.monitor.Main' - standardInput = System.in - if (project.hasProperty("appArgs")) { - args Eval.me(appArgs) - } +dependencies { + implementation project(':eraser-base') + implementation project(':feedbackloop.api') } -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} +application.mainClass = 'de.tudresden.inf.st.eraser.feedbackloop.monitor.Main' diff --git a/feedbackloop.plan/build.gradle b/feedbackloop.plan/build.gradle index 13b20cf6cff29796338f0ac62dd71723fd5ff467..f975ca22ecedf94fa809f412f5c1fd8bb46a4a11 100644 --- a/feedbackloop.plan/build.gradle +++ b/feedbackloop.plan/build.gradle @@ -1,22 +1,10 @@ -apply plugin: 'application' +plugins { + id 'eraser.java-application-conventions' +} dependencies { - compile project(':eraser-base') + implementation project(':eraser-base') compile project(':feedbackloop.api') } -run { - mainClassName = 'de.tudresden.inf.st.eraser.feedbackloop.plan.Main' - standardInput = System.in - if (project.hasProperty("appArgs")) { - args Eval.me(appArgs) - } -} - -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} +application.mainClass = 'de.tudresden.inf.st.eraser.feedbackloop.plan.Main' diff --git a/feedbackloop.plan/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/plan/PlanImpl.java b/feedbackloop.plan/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/plan/PlanImpl.java index e7f256b2928dfbbdd35e48b2dbf5b1797eee727d..7beb399d70cf437b0e34668d4fe1e0dd570cea73 100644 --- a/feedbackloop.plan/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/plan/PlanImpl.java +++ b/feedbackloop.plan/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/plan/PlanImpl.java @@ -6,8 +6,6 @@ import de.tudresden.inf.st.eraser.jastadd.model.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.List; - /** * Reference implementation for Plan. * @@ -37,14 +35,17 @@ public class PlanImpl implements Plan { @Override public void planToMatchPreferences(Activity activity) { logger.info("Plan got new activity [{}]: {}", activity.getIdentifier(), activity.getLabel()); - List<ItemPreference> preferences = knowledgeBase.currentPreferences(); - knowledgeBase.getMachineLearningRoot().addChangeEvent(createRecognitionEvent(activity)); - informExecute(preferences); + MachineLearningResult mlResult = knowledgeBase.getMachineLearningRoot().getPreferenceLearning().getLastResult(); + knowledgeBase.getMachineLearningRoot().addChangeEvent(createRecognitionEvent(activity, mlResult.getItemUpdates())); + informExecute(mlResult.getItemUpdates()); } - private ChangeEvent createRecognitionEvent(Activity activity) { + private ChangeEvent createRecognitionEvent(Activity activity, Iterable<ItemUpdate> updates) { RecognitionEvent result = RecognitionEvent.createRecognitionEvent(knowledgeBase.getMachineLearningRoot().getActivityRecognition()); result.setActivity(activity); + for (ItemUpdate update : updates) { + result.addChangedItem(ChangedItem.newFrom(update)); + } return result; } } diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000000000000000000000000000000000000..7e890a083c617c251c1a24316ea846144a7f08d9 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,6 @@ +jackson_version = 2.12.0-rc2 +apache_httpcomponents_version = 4.5.13 +log4j_version = 2.14.0 +gradle_lombok_version = 4.0.0 +openscv_version = 5.3 +jupiter_version = 5.7.0 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 01b8bf6b1f99cad9213fc495b33ad5bbab8efd20..457aad0d98108420a977756b7145c93c8910b076 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 030ec1f8a437a05e86702c10632c66340d98ba0f..4d9ca1649142b0c20144adce78e2472e2da01c30 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Tue May 07 14:40:56 CEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip diff --git a/gradlew b/gradlew index cccdd3d517fc5249beaefa600691cf150f2fa3e6..af6708ff229fda75da4f7cc4da4747217bac4d53 100755 --- a/gradlew +++ b/gradlew @@ -28,7 +28,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" diff --git a/gradlew.bat b/gradlew.bat index e95643d6a2ca62258464e83c72f5156dc941c609..0f8d5937c4ad18feb44a19e55ad1e37cc159260f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome diff --git a/influx_test/.gitignore b/influx_test/.gitignore deleted file mode 100644 index 70b583e34c3316bcd77c807e2d6b85db5e7d49f6..0000000000000000000000000000000000000000 --- a/influx_test/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/build/ -/bin/ -logs/ diff --git a/influx_test/build.gradle b/influx_test/build.gradle deleted file mode 100644 index 50fdeee4c477c0d43ad4ce73436630531a423a32..0000000000000000000000000000000000000000 --- a/influx_test/build.gradle +++ /dev/null @@ -1,22 +0,0 @@ -apply plugin: 'application' - -dependencies { - compile project(':eraser-base') - compile group: 'org.influxdb', name: 'influxdb-java', version: '2.15' -} - -run { - mainClassName = 'de.tudresden.inf.st.eraser.influx_test.Main' - standardInput = System.in - if (project.hasProperty("appArgs")) { - args Eval.me(appArgs) - } -} - -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} diff --git a/influx_test/src/main/java/de/tudresden/inf/st/eraser/influx_test/Main.java b/influx_test/src/main/java/de/tudresden/inf/st/eraser/influx_test/Main.java deleted file mode 100644 index ed4bbdfae50f5ad3909c3badfd5bf7f948c7a1ac..0000000000000000000000000000000000000000 --- a/influx_test/src/main/java/de/tudresden/inf/st/eraser/influx_test/Main.java +++ /dev/null @@ -1,128 +0,0 @@ -package de.tudresden.inf.st.eraser.influx_test; - -import org.influxdb.InfluxDB; -import org.influxdb.InfluxDBFactory; -import org.influxdb.annotation.Column; -import org.influxdb.annotation.Measurement; -import org.influxdb.dto.Point; -import org.influxdb.dto.Pong; -import org.influxdb.dto.Query; -import org.influxdb.impl.InfluxDBResultMapper; - -import java.time.Instant; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -@SuppressWarnings("deprecation") -public class Main { - - @Measurement(name = "ItemD") - public static class ItemWithDoubleStatePoint { - @Column(name = "time") - private Instant time; - - @Column(name = "state") - private Double state; - - @Column(name = "id", tag = true) - private String id; - - static ItemWithDoubleStatePoint of(double state, String id) { - return of(Instant.now(), state, id); - } - - static ItemWithDoubleStatePoint of(Instant time, double state, String id) { - ItemWithDoubleStatePoint point = new ItemWithDoubleStatePoint(); - point.time = time; - point.state = state; - point.id = id; - return point; - } - - Point build() { - return Point.measurement("ItemD") - .time(time.toEpochMilli(), TimeUnit.MILLISECONDS) - .addField("state", state) - .tag("id", id) - .build(); - } - - @Override - public String toString() { - return "ItemD [" + id + "@" + time + ": " + state + "]"; - } - } - - // InfluxDB connections settings - private static final String host = "172.22.1.152"; - private static final int port = 8086; - private static final String user = "root"; - private static final String password = "root"; - private static final String dbName = "jastaddHistoryMain"; - - public static void main(String[] args) { - testInflux(); - } - - private static void testInflux() { - // see https://github.com/influxdata/influxdb-java - String url = String.format("http://%s:%s", host, port); - InfluxDB influxDB = InfluxDBFactory.connect(url, user, password); - Pong response = influxDB.ping(); - if (response.getVersion().equalsIgnoreCase("unknown")) { - System.err.println("Error pinging server"); - return; - } - - if (databaseExists(influxDB)) { - deleteDatabase(influxDB); - } - createDatabase(influxDB); - influxDB.setDatabase(dbName); - createDefaultRetentionPolicy(influxDB); - InfluxDBResultMapper resultMapper = new InfluxDBResultMapper(); - ItemWithDoubleStatePoint point; - Query q; - List<ItemWithDoubleStatePoint> result; - - // add one measurement - point = ItemWithDoubleStatePoint.of(0.3, "iris1_item"); - influxDB.write(point.build()); - - // read all measurements - q = new Query("SELECT id, state FROM ItemD WHERE id = 'iris1_item'", dbName); - result = resultMapper.toPOJO(influxDB.query(q), ItemWithDoubleStatePoint.class); - System.out.println(result); - - // add another measurement - point = ItemWithDoubleStatePoint.of(0.4, "iris1_item"); - influxDB.write(point.build()); - - // and read all measurements, should be two now - q = new Query("SELECT id, state FROM ItemD WHERE id = 'iris1_item'", dbName); - result = resultMapper.toPOJO(influxDB.query(q), ItemWithDoubleStatePoint.class); - System.out.println(result); - } - - private static boolean databaseExists(InfluxDB influxDB) { -// Query query = new Query("SHOW DATABASES", dbName); - return influxDB.databaseExists(dbName); - } - - private static void deleteDatabase(InfluxDB influxDB) { - influxDB.deleteDatabase(dbName); -// influxDB.query(Query.encode("CREATE DATABASE \"" + dbName + "\"")); - } - - private static void createDatabase(InfluxDB influxDB) { - influxDB.createDatabase(dbName); -// influxDB.query(Query.encode("CREATE DATABASE \"" + dbName + "\"")); - } - - private static void createDefaultRetentionPolicy(InfluxDB influxDB) { - String rpName = "aRetentionPolicy"; - influxDB.createRetentionPolicy(rpName, dbName, "30d", "30m", 2, true); - influxDB.setRetentionPolicy(rpName); - } -} diff --git a/influx_test/src/main/resources/log4j2.xml b/influx_test/src/main/resources/log4j2.xml deleted file mode 100644 index 867ec439d0a32dcb5f8b3e2d0c7485d7d8da418c..0000000000000000000000000000000000000000 --- a/influx_test/src/main/resources/log4j2.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<Configuration> - <Appenders> - <Console name="Console"> - <PatternLayout pattern="%highlight{%d{HH:mm:ss.SSS} %-5level} %c{1.} - %msg%n"/> - </Console> - <RollingFile name="RollingFile" fileName="logs/eraser.log" - filePattern="logs/eraser-%i.log"> - <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n"/> - <Policies> - <OnStartupTriggeringPolicy/> - </Policies> - <DefaultRolloverStrategy max="20"/> - </RollingFile> - </Appenders> - <Loggers> - <Root level="debug"> - <AppenderRef ref="Console"/> - <AppenderRef ref="RollingFile"/> - </Root> - </Loggers> -</Configuration> diff --git a/integration/build.gradle b/integration/build.gradle index 219d9424d1b27114d05fc74ff704f6e7ce02937b..2b68d31294f3fa08506e73d568b66ecbb11adb63 100644 --- a/integration/build.gradle +++ b/integration/build.gradle @@ -1,14 +1,10 @@ -apply plugin: 'application' - -run { - mainClassName = 'de.tudresden.inf.st.eraser.integration.IntegrationMain' - standardInput = System.in - if (project.hasProperty("appArgs")) { - args Eval.me(appArgs) - } +plugins { + id 'eraser.java-application-conventions' } dependencies { - compile project(':eraser-base') - compile project(':openhab-mock') + implementation project(':eraser-base') + implementation group: 'com.opencsv', name: 'opencsv', version: "${openscv_version}" } + +application.mainClass = 'de.tudresden.inf.st.eraser.integration.IntegrationMain' diff --git a/integration/src/main/java/de/tudresden/inf/st/eraser/integration/IntegrationMain.java b/integration/src/main/java/de/tudresden/inf/st/eraser/integration/IntegrationMain.java index 05884c80d68bf27980465038960da9d5fe7adf66..3d16a465d8bb2e67e47b4a7ae3fea0f3122791db 100644 --- a/integration/src/main/java/de/tudresden/inf/st/eraser/integration/IntegrationMain.java +++ b/integration/src/main/java/de/tudresden/inf/st/eraser/integration/IntegrationMain.java @@ -1,12 +1,15 @@ package de.tudresden.inf.st.eraser.integration; +import com.opencsv.CSVParser; +import com.opencsv.CSVParserBuilder; import com.opencsv.CSVReader; +import com.opencsv.CSVReaderBuilder; import de.tudresden.inf.st.eraser.Main; import de.tudresden.inf.st.eraser.deserializer.ASTNodeDeserializer; import de.tudresden.inf.st.eraser.jastadd.model.*; import de.tudresden.inf.st.eraser.openhab2.mqtt.MQTTUpdater; -import de.tudresden.inf.st.eraser.openhab_mock.MockMain; import de.tudresden.inf.st.eraser.serializer.JsonSerializer; +import org.fusesource.mqtt.client.QoS; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -41,14 +44,20 @@ public class IntegrationMain { // read in csv String filename = "/data1.csv"; // Path csvContent = Paths.get("src", "main", "resources", filename); - InputStream inputStream = MockMain.class.getResourceAsStream(filename); + InputStream inputStream = IntegrationMain.class.getResourceAsStream(filename); String host = "localhost"; MqttRoot mqttRoot = new MqttRoot(); - mqttRoot.setHostByName(host); + mqttRoot.getHost().setHostName(host); // columns: time,topic,qos,message try (InputStreamReader inputStreamReader = new InputStreamReader(inputStream); - CSVReader reader = new CSVReader(inputStreamReader, ',', '"', 1); - MQTTSender sender = mqttRoot.getMqttSender()) { + CSVReader reader = new CSVReaderBuilder(inputStreamReader) + .withCSVParser(new CSVParserBuilder() + .withSeparator(',') + .withQuoteChar('"') + .build()) + .withSkipLines(1) + .build(); + MQTTSender sender = mqttRoot.getMqttSender()){ if (!sender.isConnected()) { String msg = "MQTT sender is not connected, aborting to avoid waits at publish calls"; logger.error(msg); @@ -64,13 +73,13 @@ public class IntegrationMain { String message = line[3]; // TODO replay messages in real time, i.e., with delay between messages try { - sender.publish(topic, message, MockMain.getQoSEnum(qos)); + sender.publish(topic, message, getQoSEnum(qos)); } catch (Exception e) { // abort the whole operation throw new RuntimeException(e); } }); - } catch (Exception e) { + } catch(Exception e){ e.printStackTrace(); } }); @@ -81,7 +90,7 @@ public class IntegrationMain { // Root model = importFromLocalFile(); logger.debug("Got model: {}", model.getSmartHomeEntityModel().description()); MqttRoot mqttRoot = new MqttRoot(); - mqttRoot.setHostByName("localhost"); + mqttRoot.getHost().setHostName("localhost"); mqttRoot.setIncomingPrefix("oh2/out/"); MqttTopic irisStateTopic = new MqttTopic(); irisStateTopic.setTopicString("iris1_item/state"); @@ -126,4 +135,17 @@ public class IntegrationMain { return ASTNodeDeserializer.read(inputStream); } + public static QoS getQoSEnum(String qos) { + switch (qos) { + case "0": + return QoS.AT_MOST_ONCE; + case "1": + return QoS.AT_LEAST_ONCE; + case "2": + return QoS.EXACTLY_ONCE; + default: + throw new IllegalArgumentException("Invalid QoS: " + qos); + } + } + } diff --git a/integration_test/build.gradle b/integration_test/build.gradle index 2d905f3f6d29a82267538d94376175d6556787d7..7e3f7749884765aa9d3ff7137a65bda9c5a345fc 100644 --- a/integration_test/build.gradle +++ b/integration_test/build.gradle @@ -1,24 +1,12 @@ -apply plugin: 'application' - -dependencies { - testCompile project(':eraser-base') - testCompile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.8' - testCompile group: 'org.apache.httpcomponents', name: 'fluent-hc', version: '4.5.8' - testCompile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.8' +plugins { + id 'eraser.java-application-conventions' } -run { - mainClassName = 'de.tudresden.inf.st.eraser.integration_test.Main' - standardInput = System.in - if (project.hasProperty("appArgs")) { - args Eval.me(appArgs) - } +dependencies { + implementation project(':eraser-base') + testImplementation group: 'org.apache.httpcomponents', name: 'httpclient', version: "${apache_httpcomponents_version}" + testImplementation group: 'org.apache.httpcomponents', name: 'fluent-hc', version: "${apache_httpcomponents_version}" + testImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: "${jackson_version}" } -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} +application.mainClass = 'de.tudresden.inf.st.eraser.integration_test.Main' diff --git a/learner_test/.gitignore b/learner_test/.gitignore deleted file mode 100644 index 84c048a73cc2e5dd24f807669eb99b0ce3123195..0000000000000000000000000000000000000000 --- a/learner_test/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build/ diff --git a/learner_test/build.gradle b/learner_test/build.gradle deleted file mode 100644 index 64ba25868ab02f58ed90c006f52cfb3e9563c411..0000000000000000000000000000000000000000 --- a/learner_test/build.gradle +++ /dev/null @@ -1,35 +0,0 @@ -repositories { - mavenCentral() -} - -sourceCompatibility = 1.8 - -apply plugin: 'java' -apply plugin: 'application' - -dependencies { - compile project(':eraser-base') - compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.8.8.1' - compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.10.0' - compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.10.0' - testCompile group: 'junit', name: 'junit', version: '4.12' - testCompile group: 'org.hamcrest', name: 'hamcrest-junit', version: '1.0.0.0' - - compile 'org.encog:encog-core:3.4' -} - -run { - mainClassName = 'de.tudresden.inf.st.eraser.learner_test.Main' - standardInput = System.in - if (project.hasProperty("appArgs")) { - args Eval.me(appArgs) - } -} - -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} diff --git a/learner_test/src/main/java/de/tudresden/inf/st/eraser/learner_test/Main.java b/learner_test/src/main/java/de/tudresden/inf/st/eraser/learner_test/Main.java deleted file mode 100644 index 62f00cb624f4dbae09c84bca772d09318f5c9964..0000000000000000000000000000000000000000 --- a/learner_test/src/main/java/de/tudresden/inf/st/eraser/learner_test/Main.java +++ /dev/null @@ -1,112 +0,0 @@ -package de.tudresden.inf.st.eraser.learner_test; -import org.encog.Encog; -import org.encog.ml.MLClassification; -import org.encog.ml.data.MLData; -import org.encog.persist.EncogDirectoryPersistence; -import org.encog.util.csv.CSVFormat; -import org.encog.util.csv.ReadCSV; -import org.encog.util.simple.EncogUtility; - -import org.encog.ml.data.versatile.NormalizationHelper; -import org.encog.ml.data.versatile.VersatileMLDataSet; -import org.encog.ml.data.versatile.columns.ColumnDefinition; -import org.encog.ml.data.versatile.columns.ColumnType; -import org.encog.ml.data.versatile.sources.VersatileDataSource; -import org.encog.ml.data.versatile.sources.CSVDataSource; -import org.encog.ml.factory.MLMethodFactory; -import org.encog.ml.model.EncogModel; -import org.encog.ConsoleStatusReportable; -import org.encog.ml.MLRegression; -import java.io.File; -import java.util.Arrays; -import static org.encog.persist.EncogDirectoryPersistence.*; - -public class Main { - - public static void main(String[] args) { - //mapping the data into model - String savefile = "src/main/java/de/tudresden/inf/st/eraser/learner_test/save_model.eg"; - String File = "src/main/java/de/tudresden/inf/st/eraser/learner_test/preference_data.csv"; - File file = new File(File); - VersatileDataSource source = new CSVDataSource(file, false, CSVFormat.DECIMAL_POINT); - VersatileMLDataSet data = new VersatileMLDataSet(source); - data.defineSourceColumn("monat", 0, ColumnType.continuous); - data.defineSourceColumn("day", 1, ColumnType.continuous); - data.defineSourceColumn("hour", 2, ColumnType.continuous); - data.defineSourceColumn("minute", 3, ColumnType.continuous); - ColumnDefinition outputColumn = data.defineSourceColumn("labels", 4, ColumnType.continuous); - data.defineSingleOutputOthersInput(outputColumn); - data.analyze(); - System.out.println("get data "); - EncogModel model = new EncogModel(data); - model.selectMethod(data, MLMethodFactory.TYPE_FEEDFORWARD); - //model.setReport(new ConsoleStatusReportable()); - data.normalize(); - NormalizationHelper helper = data.getNormHelper(); - System.out.println(helper.toString()); - model.holdBackValidation(0.3, true, 1001); - model.selectTrainingType(data); - MLRegression bestMethod = (MLRegression)model.crossvalidate(5, true); - MLClassification bestMethodtest=(MLClassification)model.crossvalidate(5,true); - /**System.out.println( "Training error: " + EncogUtility.calculateRegressionError(bestMethod, model.getTrainingDataset())); - System.out.println( "testTraining error: " + EncogUtility.calculateClassificationError(bestMethodtest, model.getTrainingDataset())); - System.out.println( "Validation error: " + EncogUtility.calculateRegressionError(bestMethod, model.getValidationDataset())); - System.out.println( "testValidation error: " + EncogUtility.calculateClassificationError(bestMethodtest, model.getValidationDataset())); - - System.out.println(helper.getClass()); - System.out.println(helper.toString()); - System.out.println("Final model: " + bestMethod); - System.out.println("Final testmodel: " + bestMethodtest);**/ - //NormalizationHelper helper = data.getNormHelper(); - - //test - String helperstr=helper.toString(); - String [] split=helperstr.split(";"); - String [] finalStr = split[split.length-1].replace("]","").replace("[",""). - split(","); - System.out.println(helper); - - // save network... - //to delete - saveObject(new File(savefile), bestMethodtest); - ReadCSV csv = new ReadCSV(File, false, CSVFormat.DECIMAL_POINT); - String[] line = new String[4]; - MLData input = helper.allocateInputVector(); - System.out.println("input test---------------"); - System.out.println(input); - while(csv.next()) { - StringBuilder result = new StringBuilder(); - line[0] = csv.get(0); - line[1] = csv.get(1); - line[2] = csv.get(2); - line[3] = csv.get(3); - String correct = csv.get(4); - helper.normalizeInputVector(line,input.getData(),false); - - MLData output = bestMethod.compute(input); - System.out.println("inputs:"); - System.out.println(input); - System.out.println("outputs:"); - System.out.println(output); - String brightnessChosen = helper.denormalizeOutputVectorToString(output)[0]; - - result.append(Arrays.toString(line)); - result.append(" -> predicted: "); - result.append(brightnessChosen); - result.append("(correct: "); - result.append(correct); - result.append(")"); - System.out.println(result.toString()); - break; - - } - // Delete data file and shut down. - //File.delete(); - Encog.getInstance().shutdown(); - /**Training error: 0.299928703107046 - testTraining error: 0.9931740614334471 - Validation error: 0.41277024952020763 - testValidation error: 0.992*/ - - } -} diff --git a/learner_test/src/main/java/de/tudresden/inf/st/eraser/learner_test/final_data.csv b/learner_test/src/main/java/de/tudresden/inf/st/eraser/learner_test/final_data.csv deleted file mode 100644 index a1e263194d3ee4f95c64c3eff3b5123fb2246cf5..0000000000000000000000000000000000000000 --- a/learner_test/src/main/java/de/tudresden/inf/st/eraser/learner_test/final_data.csv +++ /dev/null @@ -1,418 +0,0 @@ -7,20,12,13,2 -7,20,14,40,1 -7,20,14,40,2 -7,21,13,2,2 -7,21,13,2,2 -7,21,14,23,2 -7,21,14,23,2 -7,21,15,41,2 -7,21,16,54,2 -7,21,16,54,2 -7,21,17,45,3 -7,22,12,28,3 -7,22,15,35,2 -7,22,15,35,2 -7,22,18,59,3 -7,22,18,59,3 -7,23,12,32,2 -7,23,12,32,2 -7,23,16,7,2 -7,23,16,7,2 -7,23,16,7,2 -7,23,16,7,2 -7,23,16,7,2 -7,24,12,4,0 -7,24,12,4,0 -7,24,12,4,1 -7,24,14,38,2 -7,24,14,38,2 -7,24,18,54,3 -7,25,12,31,0 -7,25,12,32,1 -7,25,12,32,1 -7,25,15,6,3 -7,25,18,56,3 -7,26,13,41,2 -7,26,19,14,3 -7,27,11,39,2 -7,27,11,39,3 -7,27,11,46,3 -7,27,11,46,2 -7,27,13,8,2 -7,27,13,8,2 -7,27,13,9,2 -7,27,13,45,2 -7,27,13,45,2 -7,27,15,38,3 -7,28,12,12,2 -7,28,12,13,2 -7,28,12,41,2 -7,28,12,41,2 -7,28,12,41,2 -7,28,14,0,1 -7,28,14,0,2 -7,28,15,21,3 -7,28,18,56,3 -7,29,10,9,1 -7,29,10,9,1 -7,29,10,9,1 -7,29,11,54,0 -7,29,11,54,0 -7,29,11,54,0 -7,29,11,54,1 -7,29,14,10,2 -7,29,16,44,2 -7,29,16,44,2 -7,30,16,7,3 -7,30,18,45,3 -7,31,13,2,0 -7,31,13,2,1 -7,31,13,3,1 -7,31,13,3,1 -7,31,13,3,1 -7,31,18,39,3 -8,1,12,22,0 -8,1,12,22,1 -8,1,14,20,2 -8,1,14,20,2 -8,1,14,20,2 -8,1,15,55,3 -8,1,18,31,3 -8,1,18,37,3 -8,1,18,37,3 -8,1,19,2,3 -8,1,19,2,3 -8,1,20,5,3 -8,2,10,9,2 -8,2,10,9,1 -8,2,10,9,2 -8,2,10,9,2 -8,2,13,58,2 -8,2,13,58,2 -8,2,15,44,3 -8,2,15,44,3 -8,2,15,44,3 -8,2,17,21,3 -8,2,17,21,3 -8,2,17,21,3 -8,3,13,31,1 -8,3,13,31,2 -8,3,13,32,2 -8,3,16,43,3 -8,4,13,20,1 -8,4,13,20,2 -8,4,18,27,3 -8,5,13,37,2 -8,5,13,37,2 -8,5,18,33,3 -8,6,11,24,3 -8,6,11,24,3 -8,6,11,24,3 -8,6,13,50,3 -8,7,13,4,2 -8,7,13,4,2 -8,7,14,56,3 -8,8,12,13,2 -8,8,12,13,2 -8,8,15,51,2 -8,8,15,51,2 -8,8,15,51,3 -8,9,13,32,2 -8,9,13,32,2 -8,9,13,32,2 -8,9,15,8,2 -8,9,15,8,2 -8,9,15,8,2 -8,9,16,19,2 -8,10,11,32,0 -8,10,11,32,1 -8,10,11,32,1 -8,10,13,13,1 -8,10,13,13,1 -8,10,13,13,2 -8,10,16,42,3 -8,10,16,42,3 -8,11,14,6,2 -8,11,14,7,2 -8,11,18,54,3 -8,11,18,54,3 -8,11,18,54,3 -8,12,12,27,1 -8,12,12,27,1 -8,12,12,28,1 -8,12,13,53,2 -8,12,13,53,2 -8,12,13,53,2 -8,12,15,21,3 -8,13,13,16,1 -8,13,13,16,1 -8,13,13,16,1 -8,13,14,14,2 -8,13,14,14,2 -8,13,16,11,3 -8,13,17,18,3 -8,14,13,7,1 -8,14,13,7,1 -8,14,13,7,1 -8,14,13,7,1 -8,14,13,7,2 -8,14,13,7,2 -8,14,15,6,3 -8,15,14,5,2 -8,15,14,5,2 -8,15,14,6,2 -8,15,14,6,2 -8,15,16,41,3 -8,15,16,41,3 -8,15,17,30,3 -8,16,13,40,2 -8,16,13,40,2 -8,16,17,52,3 -8,16,17,53,3 -8,17,13,34,1 -8,17,13,35,2 -8,17,14,7,2 -8,17,19,2,3 -8,18,10,21,3 -8,18,11,14,2 -8,18,11,14,2 -8,18,11,14,2 -8,18,11,14,2 -8,18,14,25,2 -8,18,14,25,3 -8,18,14,25,2 -8,18,18,18,3 -8,18,18,19,3 -8,19,18,33,3 -8,19,18,33,3 -8,19,18,33,3 -8,19,18,33,3 -8,20,14,28,2 -8,20,14,28,2 -8,20,14,28,2 -8,20,14,28,2 -8,20,17,8,3 -8,20,18,22,3 -8,21,11,24,1 -8,21,11,24,1 -8,21,11,24,1 -8,21,15,34,3 -8,21,18,55,3 -8,22,12,3,1 -8,22,12,4,2 -8,22,12,4,2 -8,22,13,51,2 -8,22,13,51,2 -8,22,13,51,2 -8,22,18,12,3 -8,22,18,12,3 -8,22,18,12,3 -8,22,18,12,3 -8,22,18,40,3 -8,22,18,40,3 -8,23,13,42,1 -8,23,13,42,1 -8,23,17,32,3 -8,23,19,28,3 -8,23,20,27,3 -8,23,20,27,3 -8,23,21,49,3 -8,24,14,0,2 -8,24,14,0,2 -8,24,14,0,2 -8,24,14,0,2 -8,24,15,4,3 -8,24,15,4,3 -8,24,16,2,3 -8,24,16,3,3 -8,24,16,37,3 -8,24,17,9,3 -8,24,17,14,3 -8,25,13,34,1 -8,25,13,34,1 -8,25,13,34,1 -8,25,13,34,1 -8,25,13,34,1 -8,25,15,1,3 -8,25,17,58,3 -8,26,10,29,0 -8,26,10,29,0 -8,26,10,29,0 -8,26,10,29,0 -8,26,10,29,0 -8,26,16,42,3 -8,26,16,42,3 -8,26,18,41,3 -8,26,18,41,3 -8,27,13,41,2 -8,27,13,41,2 -8,27,13,41,2 -8,27,13,41,2 -8,27,17,42,3 -8,28,11,9,1 -8,28,11,9,1 -8,28,12,14,0 -8,28,12,14,1 -8,28,12,14,0 -8,28,15,3,2 -8,28,15,3,2 -8,28,16,31,3 -8,28,17,40,3 -8,29,14,44,3 -8,29,17,25,3 -8,30,12,5,0 -8,30,12,5,0 -8,30,12,5,0 -8,30,13,32,1 -8,30,13,32,1 -8,30,13,56,2 -8,30,14,23,2 -8,30,14,23,2 -8,30,14,23,2 -8,30,14,23,2 -8,30,14,41,2 -8,30,14,41,2 -8,30,14,41,2 -8,30,15,50,3 -8,30,17,0,3 -8,30,18,59,3 -8,30,18,59,3 -8,31,14,31,2 -8,31,14,31,2 -8,31,14,31,2 -8,31,17,59,3 -8,31,18,0,3 -9,1,16,13,3 -9,1,16,13,3 -9,1,16,13,3 -9,1,17,41,3 -9,2,13,44,1 -9,2,13,44,1 -9,2,13,44,1 -9,2,14,49,2 -9,2,14,49,2 -9,2,14,49,2 -9,2,16,6,3 -9,2,16,6,3 -9,2,17,2,3 -9,3,16,9,3 -9,3,17,35,3 -9,3,17,36,3 -9,4,12,57,1 -9,4,12,57,1 -9,4,15,8,3 -9,4,15,34,3 -9,4,16,26,3 -9,4,16,26,3 -9,4,18,37,3 -9,4,18,37,3 -9,4,18,37,3 -9,6,11,18,0 -9,6,11,18,0 -9,6,12,54,1 -9,6,12,54,1 -9,6,14,21,2 -9,6,14,21,2 -9,6,19,20,3 -9,7,11,50,0 -9,7,14,17,2 -9,7,14,57,3 -9,7,14,57,3 -9,7,16,56,3 -9,7,16,56,3 -9,7,16,56,3 -9,7,16,56,3 -9,7,18,38,3 -9,7,18,38,3 -9,8,11,4,2 -9,8,11,4,2 -9,8,11,13,0 -9,8,11,13,0 -9,8,11,13,0 -9,8,11,13,0 -9,8,11,13,0 -9,8,11,14,0 -9,8,11,14,1 -9,8,11,14,1 -9,8,12,1,0 -9,8,12,1,0 -9,8,12,1,0 -9,8,12,1,0 -9,8,12,1,0 -9,8,12,1,1 -9,8,12,36,0 -9,8,12,36,0 -9,8,12,36,0 -9,8,12,36,0 -9,8,12,36,0 -9,8,13,37,1 -9,8,13,37,1 -9,8,13,37,1 -9,8,14,20,2 -9,8,14,20,2 -9,8,18,20,3 -9,9,12,47,1 -9,9,12,47,2 -9,9,12,47,2 -9,9,19,5,3 -9,10,13,15,1 -9,10,13,15,1 -9,10,13,15,0 -9,10,16,49,3 -9,10,19,6,3 -9,10,21,5,3 -9,11,14,16,2 -9,11,14,16,2 -9,11,14,16,2 -9,11,18,41,3 -9,12,14,43,2 -9,12,14,43,2 -9,12,14,43,2 -9,12,16,14,3 -9,12,17,12,3 -9,12,17,12,2 -9,12,17,12,3 -9,12,17,12,2 -9,12,20,44,3 -9,13,19,52,3 -9,14,14,39,2 -9,14,14,39,2 -9,14,15,14,3 -9,14,17,29,3 -9,14,17,29,3 -9,14,17,29,3 -9,15,11,41,1 -9,15,11,41,1 -9,15,13,4,1 -9,15,14,3,1 -9,15,14,3,2 -9,16,12,36,1 -9,16,12,36,1 -9,16,12,36,1 -9,16,12,36,1 -9,16,12,48,1 -9,16,12,48,1 -9,16,13,51,1 -9,16,13,51,2 -9,16,13,51,1 -9,16,15,13,3 -9,16,15,14,3 -9,16,15,14,3 -9,17,10,27,0 -9,17,10,27,0 -9,17,11,10,0 -9,17,11,10,0 -9,17,11,10,0 -9,17,12,43,1 -9,17,12,43,1 -9,17,12,43,1 -9,17,13,32,1 -9,17,13,32,1 -9,17,14,5,1 -9,17,14,5,2 -9,17,14,6,2 -9,17,15,7,3 -9,17,15,49,3 -9,17,15,49,3 -9,17,18,12,3 -9,17,18,13,3 diff --git a/learner_test/src/main/java/de/tudresden/inf/st/eraser/learner_test/save_model.eg b/learner_test/src/main/java/de/tudresden/inf/st/eraser/learner_test/save_model.eg deleted file mode 100644 index 47c37ff28ef95b7b22a2df05ec050c3ffeb53a30..0000000000000000000000000000000000000000 --- a/learner_test/src/main/java/de/tudresden/inf/st/eraser/learner_test/save_model.eg +++ /dev/null @@ -1,24 +0,0 @@ -encog,BasicNetwork,java,3.4.0,1,1554196571101 -[BASIC] -[BASIC:PARAMS] -[BASIC:NETWORK] -beginTraining=0 -connectionLimit=0 -contextTargetOffset=0,0,0 -contextTargetSize=0,0,0 -endTraining=2 -hasContext=f -inputCount=4 -layerCounts=1,8,5 -layerFeedCounts=1,7,4 -layerContextCount=0,0,0 -layerIndex=0,1,9 -output=0.2537517424,0.3154675575,-0.8739039638,-0.4408848221,-0.8484433638,-0.999915299,-0.6964984771,-0.208278439,1,0,0,-0.4545454545,0.3559322034,1 -outputCount=1 -weightIndex=0,8,43 -weights=0.5976774048,-0.7925906525,0.7127327881,-0.9611660362,0.8031350986,-0.7286657218,1.0990482817,-0.5985785536,-0.0783115433,0.575612931,1.1267500918,1.7184744034,0.2271044512,-1.0525796764,0.0900869671,1.1492323512,0.6141715555,-1.0455927965,-0.0925453451,0.2471651431,2.3634316872,0.3939369257,0.4607437082,-0.1435186798,0.8428535365,-0.0848896791,-0.070602589,-1.2640263565,2.4899996734,-0.2185394776,10.3421332361,-0.1650898311,-0.2750133571,-0.79680959,-0.8051139953,0.8219933747,-0.0727160299,-0.4609522002,-1.0410685492,-0.5354063412,0.3028724456,-0.6835374219,0.169591233 -biasActivation=0,1,1 -[BASIC:ACTIVATION] -"org.encog.engine.network.activation.ActivationTANH" -"org.encog.engine.network.activation.ActivationTANH" -"org.encog.engine.network.activation.ActivationLinear" diff --git a/learner_test/src/main/java/de/tudresden/inf/st/eraser/learner_test/save_model_test.eg b/learner_test/src/main/java/de/tudresden/inf/st/eraser/learner_test/save_model_test.eg deleted file mode 100644 index 62fe6421d95e164aa14e123b950e831e37a5f23c..0000000000000000000000000000000000000000 --- a/learner_test/src/main/java/de/tudresden/inf/st/eraser/learner_test/save_model_test.eg +++ /dev/null @@ -1,24 +0,0 @@ -encog,BasicNetwork,java,3.4.0,1,1548158734516 -[BASIC] -[BASIC:PARAMS] -[BASIC:NETWORK] -beginTraining=0 -connectionLimit=0 -contextTargetOffset=0,0,0 -contextTargetSize=0,0,0 -endTraining=2 -hasContext=f -inputCount=4 -layerCounts=4,8,5 -layerFeedCounts=4,7,4 -layerContextCount=0,0,0 -layerIndex=0,4,12 -output=0.6991387348,-0.8711034513,-0.996886038,-0.832747291,-0.0935682806,-0.9996163977,0.5399150265,0.9411173394,-0.5084989975,0.4850010791,0.9999999957,1,0,-0.6666666667,-0.4545454545,0.6949152542,1 -outputCount=4 -weightIndex=0,32,67 -weights=-2.6901880743,0.6512821123,-1.2270002115,1.63124668,0.1982387305,-0.2994789552,1.5833040739,-0.9450411677,2.0541422847,-0.718279397,-1.1761952241,0.5028631512,0.0690323612,-1.496141565,-0.1955149568,-0.7453976822,-0.3691141073,0.9854755554,2.2113850088,-1.5216550292,0.9652087936,-1.3028209693,-1.3346156171,0.4142247818,1.0821207364,0.1987534858,0.6202881884,-0.2940331887,-1.4643282498,2.6960334656,-0.0167663298,-2.9907087565,0.3469960227,-0.0441249736,-2.5998575813,-0.7106361301,-0.8111809962,2.2216158678,-0.5482762437,-1.7996398291,-3.6734127565,-2.9102547958,0.4845401914,0.3760471288,-0.0124987546,0.3784047483,0.5860932613,-0.2682876707,0.7429004186,-7.559247176,-3.4421363532,1.1989747484,-2.3340717496,-1.4740773042,-0.7795788072,-1.8241693655,-0.630132295,-0.8191869009,-0.4060569987,-1.0997423162,-0.5495165849,0.1407829068,-2.2964930412,0.0798893221,-19.5271913755,2.0474187009,-0.2622671892 -biasActivation=0,1,1 -[BASIC:ACTIVATION] -"org.encog.engine.network.activation.ActivationTANH" -"org.encog.engine.network.activation.ActivationTANH" -"org.encog.engine.network.activation.ActivationLinear" diff --git a/learner_test/src/main/resources/log4j2.xml b/learner_test/src/main/resources/log4j2.xml deleted file mode 100644 index 0594576fac98ba859e411597c90c8e3d989378bd..0000000000000000000000000000000000000000 --- a/learner_test/src/main/resources/log4j2.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<Configuration> - <Appenders> - <Console name="Console"> - <PatternLayout pattern="%highlight{%d{HH:mm:ss.SSS} %-5level} %c{1.} - %msg%n"/> - </Console> - <RollingFile name="RollingFile" fileName="logs/jastadd-mquat.log" - filePattern="logs/jastadd-mquat-%i.log"> - <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n"/> - <Policies> - <OnStartupTriggeringPolicy/> - </Policies> - <DefaultRolloverStrategy max="20"/> - </RollingFile> - </Appenders> - <Loggers> - <Root level="debug"> - <AppenderRef ref="Console"/> - <AppenderRef ref="RollingFile"/> - </Root> - </Loggers> -</Configuration> diff --git a/ml_test/.gitignore b/ml_test/.gitignore deleted file mode 100644 index 70b583e34c3316bcd77c807e2d6b85db5e7d49f6..0000000000000000000000000000000000000000 --- a/ml_test/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/build/ -/bin/ -logs/ diff --git a/ml_test/build.gradle b/ml_test/build.gradle deleted file mode 100644 index a9e198eb94876643269b680a0de267e69724100f..0000000000000000000000000000000000000000 --- a/ml_test/build.gradle +++ /dev/null @@ -1,21 +0,0 @@ -apply plugin: 'application' - -dependencies { - compile project(':eraser-base') -} - -run { - mainClassName = 'de.tudresden.inf.st.eraser.ml_test.Main' - standardInput = System.in - if (project.hasProperty("appArgs")) { - args Eval.me(appArgs) - } -} - -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} diff --git a/ml_test/src/main/java/de/tudresden/inf/st/eraser/ml_test/Main.java b/ml_test/src/main/java/de/tudresden/inf/st/eraser/ml_test/Main.java deleted file mode 100644 index 75fd6d205d96cfe43bdd27176c21ac7e7155473a..0000000000000000000000000000000000000000 --- a/ml_test/src/main/java/de/tudresden/inf/st/eraser/ml_test/Main.java +++ /dev/null @@ -1,195 +0,0 @@ -package de.tudresden.inf.st.eraser.ml_test; - -import de.tudresden.inf.st.eraser.jastadd.model.*; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Random; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import java.util.stream.Collectors; - -public class Main { - - private static final Logger logger = LogManager.getLogger(Main.class); - - public static void main(String[] args) { - logger.info("Hello World!"); - createAndTestBrightnessNetwork(); - createAndTestColorNetwork(); - } - - private static Root createModel() { - Root root = Root.createEmptyRoot(); - Group group = new Group(); - group.setID("Group1"); - root.getSmartHomeEntityModel().addGroup(group); - Item activityItem = newItem("activity", "Recognized activity", false, 8); - Item brightnessItem = newItem("brightness", "Measured brightness", false, 5); - group.addItem(activityItem); - group.addItem(brightnessItem); - return root; - } - - private static NumberItem newItem(String id, String label, boolean defaultSendState, int initialState) { - NumberItem item = new NumberItem(); - item.setID(id); - item.setLabel(label); - if (defaultSendState) { - item.enableSendState(); - } else { - item.disableSendState(); - } - item.setState(initialState); - return item; - } - - private static final int REPETITIONS = 20; - private static final boolean CHANGE_WEIGHTS_IN_BETWEEN = true; - private static final Random random = new Random(0); - private static void classifyTimed( - NeuralNetworkRoot nn, - Function<NeuralNetworkRoot, DoubleNumber> classify, - Function<DoubleNumber, String> leafToString) { - List<String> results = new ArrayList<>(); - List<Long> times = new ArrayList<>(); - for (int i = 0; i < REPETITIONS; i++) { - long before = System.nanoTime(); - DoubleNumber classification = classify.apply(nn); - long diff = System.nanoTime() - before; - results.add(leafToString.apply(classification)); - times.add(TimeUnit.NANOSECONDS.toMillis(diff)); - if (CHANGE_WEIGHTS_IN_BETWEEN) { - HiddenNeuron hiddenNeuron = nn.getHiddenNeuron(random.nextInt(nn.getNumHiddenNeuron())); - NeuronConnection connection = hiddenNeuron.getOutput(random.nextInt(hiddenNeuron.getNumOutput())); - connection.setWeight(hiddenNeuron.getNumOutput() * random.nextDouble()); - nn.flushTreeCache(); - } - } - logger.info("Classification results: {}", results); - logger.info("Took {}ms", times.stream().map(l -> Long.toString(l)).collect(Collectors.joining("ms, "))); - logger.info("Took on average: {}ms", - Arrays.stream(times.toArray(new Long[0])).mapToLong(l -> l).average().orElse(-1)); - logger.info("Took on median: {}ms", - Arrays.stream(times.toArray(new Long[0])).mapToLong(l -> l).sorted() - .skip((REPETITIONS-1)/2).limit(2-REPETITIONS%2).average().orElse(Double.NaN)); - } - - private static class PreparationResult { - OutputLayer outputLayer; - DoubleArrayDoubleFunction sigmoid; - InputNeuron activity; - InputNeuron brightness; - NeuralNetworkRoot nn; - HiddenNeuron[] hiddenNeurons; - - PreparationResult(OutputLayer outputLayer, DoubleArrayDoubleFunction sigmoid, InputNeuron activity, - InputNeuron brightness, NeuralNetworkRoot nn, HiddenNeuron[] hiddenNeurons) { - this.outputLayer = outputLayer; - this.sigmoid = sigmoid; - this.activity = activity; - this.brightness = brightness; - this.nn = nn; - this.hiddenNeurons = hiddenNeurons; - } - } - - private static PreparationResult prepareNetwork() { - Root root = createModel(); - Item activityItem = root.getSmartHomeEntityModel().resolveItem("activity").orElseThrow( - () -> new RuntimeException("Activity not found")); - Item brightnessItem = root.getSmartHomeEntityModel().resolveItem("brightness").orElseThrow( - () -> new RuntimeException("Brightness not found")); - NeuralNetworkRoot nn = new NeuralNetworkRoot(); - - DoubleArrayDoubleFunction sigmoid = inputs -> Math.signum(Arrays.stream(inputs).sum()); - - // input layer (2 neurons) - InputNeuron activity = new InputNeuron(); - activity.setItem(activityItem); - InputNeuron brightness = new InputNeuron(); - brightness.setItem(brightnessItem); - nn.addInputNeuron(activity); - nn.addInputNeuron(brightness); - - OutputLayer outputLayer = new OutputLayer(); - nn.setOutputLayer(outputLayer); - - // hidden layer (10 neurons) - HiddenNeuron[] hiddenNeurons = new HiddenNeuron[10]; - for (int hiddenIndex = 0; hiddenIndex < hiddenNeurons.length; hiddenIndex++) { - HiddenNeuron hiddenNeuron = new HiddenNeuron(); - hiddenNeuron.setActivationFormula(sigmoid); - nn.addHiddenNeuron(hiddenNeuron); - activity.connectTo(hiddenNeuron, 1.0/2.0); - brightness.connectTo(hiddenNeuron, 1.0/2.0); - } - root.getMachineLearningRoot().setPreferenceLearning(nn); - - return new PreparationResult(outputLayer, sigmoid, activity, brightness, nn, hiddenNeurons); - } - - /** - * Purpose: Create a neural network with 3 layers (2 + 10 + 1 neurons) - * Sigmoid function for all layers, combinator of output is identity function - */ - private static void createAndTestBrightnessNetwork() { - /* - - Helligkeit NN: - - arbeitet momentan mit Zonen und nicht mit einzelnen Lampen - - 3 Layers - - Input Layer hat Neuronen (Aktivitätsnummer, Wert vom Helligkeitssensor) - - Hidden Layer hat 10 Neuronen - - Output Layer hat 1 Neuron ( Helligkeitswert) - - Aktivierungsfunktion: Sigmoidfunktion <- selbe für alle Layers - */ - PreparationResult pr = prepareNetwork(); - OutputNeuron output = new OutputNeuron(); - output.setLabel("Brightness_Output"); - output.setActivationFormula(pr.sigmoid); - pr.outputLayer.addOutputNeuron(output); - // we just have one output neuron, thus use IdentityFunction - pr.outputLayer.setCombinator(inputs -> inputs[0]); - - for (HiddenNeuron hiddenNeuron : pr.hiddenNeurons) { - hiddenNeuron.connectTo(output, 1.0/pr.hiddenNeurons.length); - } - - classifyTimed(pr.nn, NeuralNetworkRoot::classify, - classification -> Double.toString(classification.number)); - } - - /** - * Purpose: Create a neural network with 3 layers (2 + 6 + 3 neurons) - * Sigmoid function for all layers, combinator creates RGB value in hex form - */ - private static void createAndTestColorNetwork() { - PreparationResult pr = prepareNetwork(); - for (int i = 0; i < 3; i++) { - OutputNeuron output = new OutputNeuron(); - output.setLabel("Brightness_Output_" + i); - output.setActivationFormula(inputs -> Arrays.stream(inputs).sum()); - pr.outputLayer.addOutputNeuron(output); - } - // we have three output neurons, combine them to a double value (representing RGB) - pr.outputLayer.setCombinator(inputs -> 65536 * Math.ceil(255.0 * inputs[0]) + 256 * Math.ceil(255.0 * inputs[1]) + Math.ceil(255.0 * inputs[0])); - - for (HiddenNeuron hiddenNeuron : pr.hiddenNeurons) { - for (int outputIndex = 0; outputIndex < pr.outputLayer.getNumOutputNeuron(); outputIndex++) { - hiddenNeuron.connectTo(pr.outputLayer.getOutputNeuron(outputIndex), random.nextDouble() * 1.0/pr.hiddenNeurons.length); - } - } - - classifyTimed(pr.nn, NeuralNetworkRoot::classify, - classification -> Double.toHexString(classification.number)); - -// long before = System.nanoTime(); -// DoubleNumber classification = nn.classify(); -// long diff = System.nanoTime() - before; -// logger.info("Classification: {}", ); -// logger.debug("Took {}ms", TimeUnit.NANOSECONDS.toMillis(diff)); - } -} diff --git a/ml_test/src/main/resources/log4j2.xml b/ml_test/src/main/resources/log4j2.xml deleted file mode 100644 index 686c2a889038bd7e7d89928939edfd09a5f15a94..0000000000000000000000000000000000000000 --- a/ml_test/src/main/resources/log4j2.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<Configuration> - <Appenders> - <Console name="Console"> - <PatternLayout pattern="%highlight{%d{HH:mm:ss.SSS} %-5level} %c{1.} - %msg%n"/> - </Console> - <RollingFile name="RollingFile" fileName="logs/eraser.log" - filePattern="logs/eraser-%i.log"> - <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n"/> - <Policies> - <OnStartupTriggeringPolicy/> - </Policies> - <DefaultRolloverStrategy max="20"/> - </RollingFile> - </Appenders> - <Loggers> - <Root level="info"> - <AppenderRef ref="Console"/> - <AppenderRef ref="RollingFile"/> - </Root> - </Loggers> -</Configuration> diff --git a/ml_test_boqi/.gitignore b/ml_test_boqi/.gitignore deleted file mode 100644 index 70b583e34c3316bcd77c807e2d6b85db5e7d49f6..0000000000000000000000000000000000000000 --- a/ml_test_boqi/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/build/ -/bin/ -logs/ diff --git a/ml_test_boqi/build.gradle b/ml_test_boqi/build.gradle deleted file mode 100644 index d07b5930b7d3c75216b36bb9f5d9fa08b5d9d2c0..0000000000000000000000000000000000000000 --- a/ml_test_boqi/build.gradle +++ /dev/null @@ -1,33 +0,0 @@ -repositories { - mavenCentral() -} - -sourceCompatibility = 1.8 - -apply plugin: 'java' -apply plugin: 'application' - -dependencies { - compile project(':eraser-base') - compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.8' - compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.11.1' - compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.1' - testCompile group: 'junit', name: 'junit', version: '4.12' - testCompile group: 'org.hamcrest', name: 'hamcrest-junit', version: '2.0.0.0' -} - -run { - mainClassName = 'de.tudresden.inf.st.eraser.ml_test_boqi.Main' - standardInput = System.in - if (project.hasProperty("appArgs")) { - args Eval.me(appArgs) - } -} - -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} diff --git a/ml_test_boqi/src/main/java/de/tudresden/inf/st/eraser/ml_test_boqi/Main.java b/ml_test_boqi/src/main/java/de/tudresden/inf/st/eraser/ml_test_boqi/Main.java deleted file mode 100644 index f42663472e3a986c82df86e4bf89d0f9ff706441..0000000000000000000000000000000000000000 --- a/ml_test_boqi/src/main/java/de/tudresden/inf/st/eraser/ml_test_boqi/Main.java +++ /dev/null @@ -1,219 +0,0 @@ -package de.tudresden.inf.st.eraser.ml_test_boqi; - -import de.tudresden.inf.st.eraser.jastadd.model.*; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import java.util.stream.Collectors; -import org.apache.commons.math3.stat.StatUtils; -import de.tudresden.inf.st.eraser.jastadd.model.Item; - - -public class Main { - - private static final Logger logger = LogManager.getLogger(Main.class); - - public static void main(String[] args) { - - logger.info("Hello World!"); - createAndTestBrightnessNetwork(); - } - private static Root createModel() { - Root model = Root.createEmptyRoot(); - Group group = new Group(); - group.setID("Group1"); - model.addGroup(group); - - // inputs items muss normalize 1.0, 0.06666666666666665, 0.4545454545454546, -0.5593220338983051, 1(bias) - - NumberItem monthItem = new NumberItem(); - monthItem.setState(-1.0); - monthItem.setID("month"); - monthItem.setLabel("datetime-month"); - - NumberItem dayItem = new NumberItem(); - dayItem.setState(0.2666666666666666); - dayItem.setID("day"); - dayItem.setLabel("datetime-day"); - - NumberItem hourItem = new NumberItem(); - hourItem.setState(-0.6363636363636364); - hourItem.setID("hour"); - hourItem.setLabel("datetime-hour"); - - NumberItem minuteItem = new NumberItem(); - minuteItem.setState(-0.5593220338983051); - minuteItem.setID("minute"); - minuteItem.setLabel("datetime-minute"); - - NumberItem biasItem = new NumberItem(); - biasItem.setState(1); - biasItem.setID("bias"); - biasItem.setLabel("bias"); - - group.addItem(monthItem); - group.addItem(dayItem); - group.addItem(hourItem); - group.addItem(minuteItem); - group.addItem(biasItem); - return model; - } - private static final int REPETITIONS = 1; - private static void classifyTimed( - NeuralNetworkRoot nn, - Function<NeuralNetworkRoot, DoubleNumber> classify, - Function<DoubleNumber, String> leafToString) { - List<String> results = new ArrayList<>(); - List<Long> times = new ArrayList<>(); - long before = System.nanoTime(); - DoubleNumber classification = classify.apply(nn); - long diff = System.nanoTime() - before; - results.add(leafToString.apply(classification)); - times.add(TimeUnit.NANOSECONDS.toMillis(diff)); - logger.info("Classification results: {}", results); - logger.info("Took {}ms", String.join("ms, ", times.stream().map(l -> Long.toString(l)).collect(Collectors.toList()))); - logger.info("Took on average: {}ms", - Arrays.stream(times.toArray(new Long[0])).mapToLong(l -> l).average().orElse(-1)); - logger.info("Took on median: {}ms", - Arrays.stream(times.toArray(new Long[0])).mapToLong(l -> l).sorted() - .skip((REPETITIONS - 1) / 2).limit(2 - REPETITIONS % 2).average().orElse(Double.NaN)); - } - - /** - * Purpose: Create a neural network with 3 layers (5 + 8 + 4 neurons) - */ - private static void createAndTestBrightnessNetwork() { - Root model = createModel(); - Item monthItem = model.resolveItem("month").orElseThrow( - () -> new RuntimeException("Month not found")); - Item dayItem = model.resolveItem("day").orElseThrow( - () -> new RuntimeException("Day not found")); - Item hourItem = model.resolveItem("hour").orElseThrow( - () -> new RuntimeException("Hour not found")); - Item minuteItem = model.resolveItem("minute").orElseThrow( - () -> new RuntimeException("Minute not found")); - Item biasItem = model.resolveItem("bias").orElseThrow( - () -> new RuntimeException("Bias not found")); - - NeuralNetworkRoot nn = new NeuralNetworkRoot(); - - DoubleArrayDoubleFunction sigmoid = inputs -> Math.signum(Arrays.stream(inputs).sum()); - DoubleArrayDoubleFunction tanh= inputs ->Math.tanh(Arrays.stream(inputs).sum()); - DoubleArrayDoubleFunction function_one= inputs->function_one(); - - //Weights outputs from learner Module - ArrayList<Double> weights= new ArrayList<Double>(Arrays.asList( - -4.8288886204,0.6723236931,2.1451097188,-0.8551053267,-0.7858304445,4.1369566727,-3.3096691918, - -0.2190980261,2.6871317298,1.2272772167,-2.5292510941,-1.2860407542,-4.2280191541,1.004752063, - 0.8345207039,0.0123185817,-0.5921808915,0.0967336988,-0.305892589,0.5572392781,-0.7190098073, - -1.6247354373,0.4589248822,-0.0269816271,2.2208040852,-3.6281085698,0.2204999381,4.7263701556, - -4.8348948698,0.231141867,8.7120706018,-1.4912707741,0.9482851705,0.1377551973,-6.6525856465, - -1.321197315,-2.7369948929,17.664289214,-3.1279212743,-0.8245974167,-1.4251924355,0.8370511414, - 2.0841638143,-0.210152817,-1.9414132298,-1.7973688846,-2.1977997794,-3.6046836685,-3.3403186721, - -6.1556924635,-2.8952903587,-1.0773989561,0.2300429028,-0.2184650371,0.0297181797,0.5709092417, - 1.3960358442,-3.1577981239,0.0423944625,-17.8143314027,-1.4439317172,-0.5137688896,1.0166045804, - 0.3059149818,1.0938282764,0.6203368549,0.702449827)); - // input layer - InputNeuron month = new InputNeuron(); - month.setItem(monthItem); - InputNeuron day = new InputNeuron(); - day.setItem(dayItem); - InputNeuron hour = new InputNeuron(); - hour.setItem(hourItem); - InputNeuron minute = new InputNeuron(); - minute.setItem(minuteItem); - InputNeuron bias = new InputNeuron(); - bias.setItem(biasItem); - - nn.addInputNeuron(month); - nn.addInputNeuron(day); - nn.addInputNeuron(hour); - nn.addInputNeuron(minute); - nn.addInputNeuron(bias); - - // output layer - OutputLayer outputLayer = new OutputLayer(); - OutputNeuron output0 = new OutputNeuron(); - output0.setActivationFormula(tanh); - OutputNeuron output1 = new OutputNeuron(); - output1.setActivationFormula(tanh); - OutputNeuron output2 = new OutputNeuron(); - output2.setActivationFormula(tanh); - OutputNeuron output3 = new OutputNeuron(); - output3.setActivationFormula(tanh); - - outputLayer.addOutputNeuron(output0); - outputLayer.addOutputNeuron(output1); - outputLayer.addOutputNeuron(output2); - outputLayer.addOutputNeuron(output3); - - outputLayer.setCombinator(inputs->predictor(inputs)); - nn.setOutputLayer(outputLayer); - - // hidden layer - HiddenNeuron[] hiddenNeurons = new HiddenNeuron[8]; - for (int i = 0; i < (hiddenNeurons.length); i++) { - - if (i==7){ - HiddenNeuron hiddenNeuron = new HiddenNeuron(); - hiddenNeuron.setActivationFormula(function_one); - hiddenNeurons[i] = hiddenNeuron; - nn.addHiddenNeuron(hiddenNeuron); - bias.connectTo(hiddenNeuron,1.0); - hiddenNeuron.connectTo(output0, weights.get(i)); - hiddenNeuron.connectTo(output1, weights.get(i+8)); - hiddenNeuron.connectTo(output2, weights.get(i+8*2)); - hiddenNeuron.connectTo(output3, weights.get(i+8*3)); - } - else{ - HiddenNeuron hiddenNeuron = new HiddenNeuron(); - hiddenNeuron.setActivationFormula(tanh); - hiddenNeurons[i] = hiddenNeuron; - nn.addHiddenNeuron(hiddenNeuron); - - month.connectTo(hiddenNeuron, weights.get((hiddenNeurons.length*4)+i*5)); - day.connectTo(hiddenNeuron, weights.get((hiddenNeurons.length*4+1)+i*5)); - hour.connectTo(hiddenNeuron, weights.get((hiddenNeurons.length*4+2)+i*5)); - minute.connectTo(hiddenNeuron, weights.get((hiddenNeurons.length*4+3)+i*5)); - bias.connectTo(hiddenNeuron,weights.get((hiddenNeurons.length*4+4)+i*5)); - hiddenNeuron.connectTo(output0, weights.get(i)); - hiddenNeuron.connectTo(output1, weights.get(i+8)); - hiddenNeuron.connectTo(output2, weights.get(i+8*2)); - hiddenNeuron.connectTo(output3, weights.get(i+8*3));} - } - - model.getMachineLearningRoot().setPreferenceLearning(nn); - System.out.println(model.prettyPrint()); - - classifyTimed(nn, NeuralNetworkRoot::classify, - classification -> Double.toString(classification.number)); - } - private static double function_one() { - return 1.0; - } - private static double predictor(double[] inputs) { - int index=0; - double maxinput=StatUtils.max(inputs); - System.out.println(inputs); - for (int i = 0; i < inputs.length; i++) - { - if (inputs[i] == maxinput){ - index=i; - } - } - //outputs from learner - ArrayList<Double> outputs= new ArrayList<Double>(Arrays.asList(2.0,1.0,3.0,0.0)); - double output=outputs.get(index); - return output; - } -} - -//inputs: -//[BasicMLData:-1.0,0.2666666666666666,-0.6363636363636364,-0.5593220338983051] -//outputs: -//[BasicMLData:-0.9151867668336432,-0.1568555041251098,-0.9786996639280675,-0.9436628188408074] -//[7, 20, 12, 13] -> predicted: 1(correct: 2) \ No newline at end of file diff --git a/ml_test_boqi/src/main/resources/log4j2.xml b/ml_test_boqi/src/main/resources/log4j2.xml deleted file mode 100644 index 0594576fac98ba859e411597c90c8e3d989378bd..0000000000000000000000000000000000000000 --- a/ml_test_boqi/src/main/resources/log4j2.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<Configuration> - <Appenders> - <Console name="Console"> - <PatternLayout pattern="%highlight{%d{HH:mm:ss.SSS} %-5level} %c{1.} - %msg%n"/> - </Console> - <RollingFile name="RollingFile" fileName="logs/jastadd-mquat.log" - filePattern="logs/jastadd-mquat-%i.log"> - <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n"/> - <Policies> - <OnStartupTriggeringPolicy/> - </Policies> - <DefaultRolloverStrategy max="20"/> - </RollingFile> - </Appenders> - <Loggers> - <Root level="debug"> - <AppenderRef ref="Console"/> - <AppenderRef ref="RollingFile"/> - </Root> - </Loggers> -</Configuration> diff --git a/org.openhab.action.machinelearn/.gitignore b/org.openhab.action.machinelearn/.gitignore deleted file mode 100644 index 64c2056c6f34fbf226e041a575bdd73dff75df1f..0000000000000000000000000000000000000000 --- a/org.openhab.action.machinelearn/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -build/ -/bin/ -logs/ diff --git a/org.openhab.action.machinelearn/META-INF/MANIFEST.MF b/org.openhab.action.machinelearn/META-INF/MANIFEST.MF deleted file mode 100644 index 81418c31f3f28f76f0ff7cf3c7848a838cd8c2ec..0000000000000000000000000000000000000000 --- a/org.openhab.action.machinelearn/META-INF/MANIFEST.MF +++ /dev/null @@ -1,23 +0,0 @@ -Manifest-Version: 1.0 -Private-Package: org.openhab.action.machinelearn.internal -Ignore-Package: org.openhab.action.machinelearn.internal -Bundle-License: http://www.eclipse.org/legal/epl-v10.html -Bundle-Name: openHAB Machine Learning Action -Bundle-SymbolicName: org.openhab.action.machinelearn -Bundle-Vendor: openHAB.org -Bundle-Version: 1.11.0.qualifier -Bundle-Activator: org.openhab.action.machinelearn.internal.MachineLearnActivator -Bundle-ManifestVersion: 2 -Bundle-Description: This is the Machine Learning action of the open Home Aut - omation Bus (openHAB) -Import-Package: org.openhab.core.scriptengine.action, - org.osgi.framework, - org.osgi.service.cm, - org.osgi.service.component, - org.slf4j -Bundle-DocURL: http://www.openhab.org -Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Service-Component: OSGI-INF/action.xml -Bundle-ClassPath: ., - lib/weka.jar -Bundle-ActivationPolicy: lazy diff --git a/org.openhab.action.machinelearn/OSGI-INF/action.xml b/org.openhab.action.machinelearn/OSGI-INF/action.xml deleted file mode 100644 index d3d60bb2711d68bad6c7e223f01f5fe50fe3fd4c..0000000000000000000000000000000000000000 --- a/org.openhab.action.machinelearn/OSGI-INF/action.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - - Copyright (c) 2010-2016 by the respective copyright holders. - - All rights reserved. This program and the accompanying materials - are made available under the terms of the Eclipse Public License v1.0 - which accompanies this distribution, and is available at - http://www.eclipse.org/legal/epl-v10.html - ---> -<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="activate" deactivate="deactivate" immediate="true" name="org.openhab.action.machinelearn.action"> - <implementation class="org.openhab.action.machinelearn.internal.MachineLearnActionService" /> - - <service> - <provide interface="org.openhab.core.scriptengine.action.ActionService" /> - <provide interface="org.osgi.service.cm.ManagedService" /> - </service> - - <property name="service.pid" type="String" value="org.openhab.machinelearn" /> -</scr:component> diff --git a/org.openhab.action.machinelearn/README.md b/org.openhab.action.machinelearn/README.md deleted file mode 100644 index 805bce7b3f4f8ca24bb4c1a560f74d4ba0a8731b..0000000000000000000000000000000000000000 --- a/org.openhab.action.machinelearn/README.md +++ /dev/null @@ -1,111 +0,0 @@ -# OpenHAB machine learning action plugin - -## Installation - -Copy a .jar file containing the plugin into the OpenHAB's `addons` directory. -The action will then provide several methods to use ML functionality. - -## How does this work? - -The plugin has a collection of machine learning models, of which only one is -active at a time. New models will be created if incoming _labeled_ instances -have a set of attributes different from what the current models have, but these -models will be inactive, until a user-triggered re-evaluation is performed. The -model with the best evaluation score will be made active. Incoming _labeled_ -instances also form a _training window_, which can be used for retraining the -existing models (which can be useful to fight concept drift). - -## A minimal example - -```java -// Read the docs about ARFF in the internet -// Internally, this creates one model trained on the dataset -buildFromDataset('/path/to/dataset.arff') - - -// Assemble an instance that has to be predicted, somehow... -Map<String, Double> instance = assembleInstance() - - -// Get the prediction (class index if dataset was for classification -// or the regression value if dataset was for regression). -double result = classify(instance) - - -// If anotherInstance is labeled, it will be added to the training window -// of the created model, but the modeled will not be retrained. -result = classify(labeledInstance) - - -// Retrain all models (only one, currently) on their training windows. -retrain() - - -// If such instance is labeled, a new model with an appropriate featureset -// will be silently created, and trained on the instance. The returned -// prediction, however, will be made by a currently active model (the one that -// was built from dataset) by ignoring new features and setting the values of -// missing features to some value (zero or mean). -result = classify(instanceWithMissingFeature_orNewFeature) - - -// Both models now make predictions, but only the result of a currently active -// model is returned -result = classify(someUnlabeledInstance) - - -// The recent performance of all models is compared, and the one with the best -// score is made active. -evaluate() -``` - -## Available public methods - -* `void saveModel(String path); void loadModel(String path)` - -Save the set of predictive models that resulted as a work of this plugin or load -the previously saved model set. - -* `void buildFromDataset(String path)` - -If `path` contains an ARFF formated dataset, where the attribute to be predicted -is named `label`, then the plugin will erase everything and create the set with -one model, built from that dataset and set that model is active. - -* `double classify(Map<String, Double> row)` - -Pass a dictionary with an input instance (names of attributes mapped to their -values in the instance). All models will make predictions for this instance, but -only the prediction made by the currently active model will be returned. If none -of the models is active, then an exception indicating that issue will be thrown. - -If an input instance has a set of attributes, that none of the trained models -has, then the existing models will still try to predict that instance. If the -instance lacks some attributes, that are present in the models, then the -models will replace the resulting missing value with zero. If the instance has -attributes that are not present in models, these will be ignored by models. - -If such instance has a label, though, then a new model will be created and -trained on this single instance. The new model will be inactive until a user -demands re-evaluation. For such models, every incoming labeled instance will be -added to their training window, and the model will be retrained every time the -window grows, until the training window is filled. After that retraining happens -only on user's demand. - -Labeled instances will be added to the head of the training window of all the -models, removing the instance on the tail if the window is full. - -* `void evaluate()` - -The models will store their recent prediction history of labeled instances, thus -they are aware of their recent performance. Calling `evaluate` will set the -model with the best recent history as an active one. - -* `void retrain()` - -Retrain all existing models on their training windows. Reset the recent history -of all models. - -* `void reset()` - -Erase the current model set. diff --git a/org.openhab.action.machinelearn/build.gradle b/org.openhab.action.machinelearn/build.gradle deleted file mode 100644 index 7360d4c8b932a5805e9bd29f21960d3c67b9851e..0000000000000000000000000000000000000000 --- a/org.openhab.action.machinelearn/build.gradle +++ /dev/null @@ -1,16 +0,0 @@ -dependencies { - compile files('lib/weka.jar') - compile project(':stub.org.openhab.core.scriptengine.action') - compile group: 'org.osgi', name: 'org.osgi.framework', version: '1.9.0' - compile group: 'org.osgi', name: 'org.osgi.service.cm', version: '1.6.0' - compile group: 'org.osgi', name: 'org.osgi.service.component', version: '1.4.0' - compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25' -} - -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} diff --git a/org.openhab.action.machinelearn/lib/weka.jar b/org.openhab.action.machinelearn/lib/weka.jar deleted file mode 100644 index a8d1fdb042d7d495dcb9ce966f427be87ad7b572..0000000000000000000000000000000000000000 Binary files a/org.openhab.action.machinelearn/lib/weka.jar and /dev/null differ diff --git a/org.openhab.action.machinelearn/src/main/java/org/openhab/action/machinelearn/internal/MLUnit.java b/org.openhab.action.machinelearn/src/main/java/org/openhab/action/machinelearn/internal/MLUnit.java deleted file mode 100644 index 884bd2c8bc4a32269c49cbc3809ba32b372b9e96..0000000000000000000000000000000000000000 --- a/org.openhab.action.machinelearn/src/main/java/org/openhab/action/machinelearn/internal/MLUnit.java +++ /dev/null @@ -1,133 +0,0 @@ -package org.openhab.action.machinelearn.internal; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import weka.classifiers.Classifier; -import weka.core.Attribute; -import weka.core.DenseInstance; -import weka.core.Instance; -import weka.core.Instances; -import weka.core.converters.ConverterUtils.DataSource; - -//Machine Learning Model class, stores classifier and data on which it was trained -class MLUnit implements java.io.Serializable { - - private static final long serialVersionUID = -4328733681884286806L; - private static final double alpha = 0.1; - private Classifier cls; - private Instances data; - - // How much data-history to store - private int capacity; - // How important is this classifier - private double weight; - private double history = 1.0; - - protected MLUnit(String dataset, Class<? extends Classifier> clsc) throws Exception { - data = DataSource.read(dataset); - capacity = data.numInstances(); - data.setClass(data.attribute("label")); - weight = 1.0; - cls = clsc.newInstance(); - build(); - } - - protected MLUnit(Set<String> attNames, Class<? extends Classifier> clsc, int window) throws Exception { - - // Initialize data entity (see Weka documentation) - ArrayList<Attribute> atts = new ArrayList<Attribute>(); - int classIndex = -1; - for (String name : attNames) { - if (name.equals("label")) { - classIndex = atts.size(); - } - atts.add(new Attribute(name)); - } - // Throw exception if no "label" field found - if (classIndex == -1) { - throw new Exception(attNames.toString()); - } - this.data = new Instances("thinkaboutit", atts, 0); - this.data.setClassIndex(classIndex); - - // Create classifier - this.cls = clsc.newInstance(); - this.capacity = window; - this.weight = 0.0; - } - - private void build() throws Exception { - // When there is data - retrain (or train) - cls.buildClassifier(data); - } - - protected Set<String> getAttributeSet() { - // Return set of attributes this model contains - Set<String> tmp = new HashSet<String>(); - for (Attribute a : Collections.list(data.enumerateAttributes())) { - tmp.add(a.name()); - } - tmp.add("label"); - return tmp; - } - - protected void setWeight(double w) { - this.weight = w; - } - - protected double getWeight() { - return this.weight; - } - - protected double getHistory() { - return this.history; - } - - protected boolean isMatch(Set<String> keys) { - // Check if you can use this model for incoming data - return keys.equals(getAttributeSet()); - } - - protected double incoming(Map<String, Double> row) throws Exception { - - // Fill in new data row - double[] values = new double[data.numAttributes()]; - - for (int i = 0; i < values.length; i++) { - Double num = row.get(data.attribute(i).name()); - values[i] = num == null ? 0 : num.doubleValue(); - } - - Instance inst = new DenseInstance(1.0, values); - inst.setDataset(data); - double clsResult = cls.classifyInstance(inst); - - // If data was labeled add it to the model - if (row.get("label") != null) { - - data.add(inst); - double[] distro = cls.distributionForInstance(inst); - - if (distro.length == 1) { - history = (1 - alpha) * history + alpha * Math.pow(clsResult - row.get("label"), 2); - } else { - history = (1 - alpha) * history + alpha * (clsResult == row.get("label") ? 0.0 : 1.0); - } - if (data.numInstances() < capacity) { - // Retrain on every instance until capacity is reached - // after that only on demand (e.g. sensor malfunction, concept - // drift) - build(); - } else { - data.delete(0); - } - } - - // Classify and return result - return clsResult; - } -} diff --git a/org.openhab.action.machinelearn/src/main/java/org/openhab/action/machinelearn/internal/MachineLearn.java b/org.openhab.action.machinelearn/src/main/java/org/openhab/action/machinelearn/internal/MachineLearn.java deleted file mode 100644 index f7920ab2ce839e1936ed18e462827f457d24fa62..0000000000000000000000000000000000000000 --- a/org.openhab.action.machinelearn/src/main/java/org/openhab/action/machinelearn/internal/MachineLearn.java +++ /dev/null @@ -1,191 +0,0 @@ -/** - * Copyright (c) 2010-2016 by the respective copyright holders. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.openhab.action.machinelearn.internal; - -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.openhab.core.scriptengine.action.ActionDoc; -import org.openhab.core.scriptengine.action.ParamDoc; - -import weka.classifiers.functions.MultilayerPerceptron; -import weka.classifiers.trees.J48; - -/** - * This class provides static methods that can be used to trigger - * machine learning algorithms - * - * @author Pavel Lutskov - * @since 1.9.0 - */ -public class MachineLearn { - - // This is the model (list of classifiers, usually one or two) - private static List<MLUnit> clfs = new ArrayList<MLUnit>(); - - // Debug string for stack traces - private static String dbg = ""; - - // Debug string for program path examination - private static String dbg2 = ""; - - // Method to trigger calculation - @ActionDoc(text = "Run regression on given data", returns = "regression result") - public static synchronized double classify( - @ParamDoc(name = "row", text = "instance for classification") Map<String, Double> row) throws Exception { - - // If there is no model for this data-point, then create a new one - if (!existsMatch(row.keySet())) { - dbg2 += ":no match found:"; - try { - dbg2 += ":try to create new unit:"; - MLUnit cls = new MLUnit(row.keySet(), MultilayerPerceptron.class, 500); - clfs.add(cls); - } catch (Exception e) { - dbg2 += ":fail to create new unit:"; - handle(e); - } - } - - List<Double> results = new ArrayList<Double>(); - double fullWeight = 0.0; - - // Try classification on existing models (might be extended to ensemble learning) - for (MLUnit cls : clfs) { - try { - dbg2 += ":try classification:"; - results.add(cls.incoming(row) * cls.getWeight()); - fullWeight += cls.getWeight(); - } catch (Exception e) { - dbg2 += ":failed classification:"; - results.add(0.0); - handle(e); - } - } - if (fullWeight == 0.0) { - throw new Exception("Classification of the instance is impossible."); - } - - // Result is weighted sum of outputs from all models in list (in this version active model has weight 1, and - // others 0) - dbg2 += ":end results:"; - double result = 0.0; - for (double d : results) { - result += d; - } - // If there is no model evaluated as good, then return 0 - return fullWeight == 0.0 ? 0.0 : result / fullWeight; - } - - // Run evaluation to activate/deactivate present models - @ActionDoc(text = "Evaluate model on labeled data and see which model is the best") - public static synchronized void evaluate() { - double best = Double.POSITIVE_INFINITY; - - MLUnit winner = null; - for (MLUnit cls : clfs) { - if (cls.getHistory() < best) { - best = cls.getHistory(); - winner = cls; - } - } - if (winner != null) { - for (MLUnit cls : clfs) { - cls.setWeight(0.0); - } - winner.setWeight(1.0); - } - } - - // Understand if there exists an appropriate model for incoming data-point - @ActionDoc(text = "Private method to realize that new model is necessary") - private static boolean existsMatch(Set<String> keys) { - boolean does = false; - for (MLUnit cls : clfs) { - dbg2 += ":checking unit for match::"; - dbg2 += cls.getAttributeSet(); - dbg2 += ":" + keys; - dbg2 += ":end checking unit:"; - does = cls.isMatch(keys) || does; - } - return does; - } - - // Store latest stack trace into dbg string - @ActionDoc(text = "Private method to handle occuring exceptions") - private static void handle(Exception e) { - e.printStackTrace(); - StringWriter sw = new StringWriter(); - e.printStackTrace(new PrintWriter(sw)); - dbg = sw.toString(); - } - - // Method to retrieve debug info from rules - @ActionDoc(text = "Get some debug info", returns = "Debug string with the latest stack trace") - public static String getDebug() { - String tmp = dbg; - dbg = ""; - return tmp; - } - - @ActionDoc(text = "Get more debug info", returns = "More debug info") - public static String getDebu2() { - String tmp = dbg2; - dbg2 = ""; - return tmp; - } - - @ActionDoc(text = "Reset if something went wrong") - public static void reset() { - clfs = new ArrayList<MLUnit>(); - dbg = ""; - dbg2 = ""; - } - - @ActionDoc(text = "Build model from dataset") - public static void buildFromDataset(String path) { - clfs = new ArrayList<>(); - try { - clfs.add(new MLUnit(path, J48.class)); - } catch (Exception e) { - dbg2 += ":failed loading:"; - handle(e); - } - } - - @ActionDoc(text = "Save model to disk") - public static void saveModel(String path) { - try (FileOutputStream fos = new FileOutputStream(path); ObjectOutputStream oos = new ObjectOutputStream(fos)) { - dbg2 += ":saving model to " + path + ":"; - oos.writeObject(clfs); - } catch (Exception e) { - dbg2 += ":failed saving:"; - handle(e); - } - } - - @ActionDoc(text = "Load previously stored model") - public static synchronized void loadModel(String path) { - try (FileInputStream fis = new FileInputStream(path); ObjectInputStream ois = new ObjectInputStream(fis)) { - dbg2 += ":loading model from " + path + ":"; - clfs = (ArrayList<MLUnit>) ois.readObject(); - } catch (Exception e) { - dbg2 += ":failed loading:"; - handle(e); - } - } -} diff --git a/org.openhab.action.machinelearn/src/main/java/org/openhab/action/machinelearn/internal/MachineLearnActionService.java b/org.openhab.action.machinelearn/src/main/java/org/openhab/action/machinelearn/internal/MachineLearnActionService.java deleted file mode 100644 index 80ef9bb07fd782db187f606462a35414b3d09463..0000000000000000000000000000000000000000 --- a/org.openhab.action.machinelearn/src/main/java/org/openhab/action/machinelearn/internal/MachineLearnActionService.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) 2010-2016 by the respective copyright holders. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.openhab.action.machinelearn.internal; - -import java.util.Dictionary; - -import org.openhab.core.scriptengine.action.ActionService; -import org.osgi.service.cm.ConfigurationException; -import org.osgi.service.cm.ManagedService; - -/** - * This class registers an OSGi service for the Mail action. - * - * @author Kai Kreuzer - * @since 1.3.0 - */ -public class MachineLearnActionService implements ActionService, ManagedService { - - /** - * Indicates whether this action is properly configured which means all - * necessary configurations are set. This flag can be checked by the - * action methods before executing code. - */ - /* default */ static boolean isProperlyConfigured = false; - - public MachineLearnActionService() { - } - - public void activate() { - } - - public void deactivate() { - // deallocate Resources here that are no longer needed and - // should be reset when activating this binding again - } - - @Override - public String getActionClassName() { - return MachineLearn.class.getCanonicalName(); - } - - @Override - public Class<?> getActionClass() { - return MachineLearn.class; - } - - @Override - @SuppressWarnings("rawtypes") - public void updated(Dictionary config) throws ConfigurationException { - - } -} diff --git a/org.openhab.action.machinelearn/src/main/java/org/openhab/action/machinelearn/internal/MachineLearnActivator.java b/org.openhab.action.machinelearn/src/main/java/org/openhab/action/machinelearn/internal/MachineLearnActivator.java deleted file mode 100644 index 53a0798b6c233eb2b23a12837b50d668858ee711..0000000000000000000000000000000000000000 --- a/org.openhab.action.machinelearn/src/main/java/org/openhab/action/machinelearn/internal/MachineLearnActivator.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) 2010-2016 by the respective copyright holders. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.openhab.action.machinelearn.internal; - -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Extension of the default OSGi bundle activator - * - * @author Kai Kreuzer - * @since 1.3.0 - */ -public final class MachineLearnActivator implements BundleActivator { - - private static Logger logger = LoggerFactory.getLogger(MachineLearnActivator.class); - - private static BundleContext context; - - /** - * Called whenever the OSGi framework starts our bundle - */ - @Override - public void start(BundleContext bc) throws Exception { - context = bc; - logger.debug("Machine Learning action has been started."); - } - - /** - * Called whenever the OSGi framework stops our bundle - */ - @Override - public void stop(BundleContext bc) throws Exception { - context = null; - logger.debug("Machine Learning action has been stopped."); - } - - /** - * Returns the bundle context of this bundle - * - * @return the bundle context - */ - public static BundleContext getContext() { - return context; - } - -} diff --git a/org.openhab.action.machinelearn/src/main/resources/readme.txt b/org.openhab.action.machinelearn/src/main/resources/readme.txt deleted file mode 100644 index 98698c670dc399dac76f7a173160545eba8f01c1..0000000000000000000000000000000000000000 --- a/org.openhab.action.machinelearn/src/main/resources/readme.txt +++ /dev/null @@ -1 +0,0 @@ -Bundle resources go in here! \ No newline at end of file diff --git a/org.openlicht.action.reinforcementlearning/.gitignore b/org.openlicht.action.reinforcementlearning/.gitignore deleted file mode 100644 index 64c2056c6f34fbf226e041a575bdd73dff75df1f..0000000000000000000000000000000000000000 --- a/org.openlicht.action.reinforcementlearning/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -build/ -/bin/ -logs/ diff --git a/org.openlicht.action.reinforcementlearning/META-INF/MANIFEST.MF b/org.openlicht.action.reinforcementlearning/META-INF/MANIFEST.MF deleted file mode 100644 index f7583bb18d636fec33238e5fd571732ef171cf48..0000000000000000000000000000000000000000 --- a/org.openlicht.action.reinforcementlearning/META-INF/MANIFEST.MF +++ /dev/null @@ -1,27 +0,0 @@ -Manifest-Version: 1.0 -Private-Package: org.openhab.action.helloworld.internal -Ignore-Package: org.openhab.action.helloworld.internal -Bundle-License: http://www.eclipse.org/legal/epl-v10.html -Bundle-Name: openLicht Reinforcement Learning Action -Bundle-SymbolicName: org.openlicht.action.reinforcementlearning -Bundle-Version: 1.9.0.qualifier -Bundle-Activator: org.openlicht.action.reinforcementlearning.internal.MainActivator -Bundle-ManifestVersion: 2 -Bundle-Description: This is the Hello World action of the open Home Aut - omation Bus (openHAB) -Import-Package: org.apache.commons.lang, - org.eclipse.jdt.annotation, - org.openhab.core.library.types, - org.openhab.core.scriptengine.action, - org.osgi.framework, - org.osgi.service.cm, - org.osgi.service.component, - org.slf4j -Bundle-DocURL: http://www.openhab.org -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Service-Component: OSGI-INF/action.xml -Bundle-ClassPath: ., - lib/encog-core-3.4.jar -Bundle-ActivationPolicy: lazy -Automatic-Module-Name: org.openlicht.action.reinforcementlearning -Require-Bundle: org.eclipse.smarthome.core diff --git a/org.openlicht.action.reinforcementlearning/OSGI-INF/action.xml b/org.openlicht.action.reinforcementlearning/OSGI-INF/action.xml deleted file mode 100644 index ded8de6584af4ef69343ea2d29339b258eb26fba..0000000000000000000000000000000000000000 --- a/org.openlicht.action.reinforcementlearning/OSGI-INF/action.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - - Copyright (c) 2010-2016 by the respective copyright holders. - - All rights reserved. This program and the accompanying materials - are made available under the terms of the Eclipse Public License v1.0 - which accompanies this distribution, and is available at - http://www.eclipse.org/legal/epl-v10.html - ---> -<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="activate" deactivate="deactivate" immediate="true" name="org.openlicht.action.reinforcementlearning.action"> - <implementation class="org.openlicht.action.reinforcementlearning.internal.MainActionService" /> - - <service> - <provide interface="org.openhab.core.scriptengine.action.ActionService" /> - <provide interface="org.osgi.service.cm.ManagedService" /> - </service> - - <property name="service.pid" type="String" value="org.openhab.helloworld" /> - -</scr:component> diff --git a/org.openlicht.action.reinforcementlearning/build.gradle b/org.openlicht.action.reinforcementlearning/build.gradle deleted file mode 100644 index 572c87ea95295ba93b68e1f8bae5bed2dcc0149d..0000000000000000000000000000000000000000 --- a/org.openlicht.action.reinforcementlearning/build.gradle +++ /dev/null @@ -1,17 +0,0 @@ -dependencies { - compile files('lib/encog-core-3.4.jar') - compile project(':stub.org.openhab.core.scriptengine.action') - compile group: 'org.eclipse.jdt', name: 'org.eclipse.jdt.annotation', version: '2.2.200' - compile group: 'org.osgi', name: 'org.osgi.framework', version: '1.9.0' - compile group: 'org.osgi', name: 'org.osgi.service.cm', version: '1.6.0' - compile group: 'org.osgi', name: 'org.osgi.service.component', version: '1.4.0' - compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25' -} - -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} diff --git a/org.openlicht.action.reinforcementlearning/lib/encog-core-3.4.jar b/org.openlicht.action.reinforcementlearning/lib/encog-core-3.4.jar deleted file mode 100644 index e78ab98f8f50dfb7855b472ba33d4c09f4fe6218..0000000000000000000000000000000000000000 Binary files a/org.openlicht.action.reinforcementlearning/lib/encog-core-3.4.jar and /dev/null differ diff --git a/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/Main.java b/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/Main.java deleted file mode 100644 index 900fcfe6b14a4377be5cc2b405df22b0dd2daa86..0000000000000000000000000000000000000000 --- a/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/Main.java +++ /dev/null @@ -1,288 +0,0 @@ - -/** - * Copyright (c) 2010-2016 by the respective copyright holders. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.openlicht.action.reinforcementlearning.internal; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.util.HashMap; - -import org.openhab.core.scriptengine.action.ActionDoc; -import org.openhab.core.scriptengine.action.ParamDoc; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class provides static methods that can be used in automation rules - * for using Reinforcement Learning methods and conect to OpenHab. - * - * @author David Morales Rodríguez - * - * - */ -public class Main { - - private static int N_LAMPS = 3; - private static int N_VALUES = 3; - private static ReinforcementLearningAlgorithm rl_algorithm = null; - private static Logger logger = LoggerFactory.getLogger(Main.class); - - // method to change string to double. Used to get naturalLight (home I/O uses "," for float numbers) - private static double changeStringToDouble(String string_number) { - double number; - int endIndex = string_number.indexOf(","); - if (endIndex > 0) { - number = Double.parseDouble(string_number.substring(0, endIndex)); - if (endIndex < string_number.length() - 1) { - String decimal_string = string_number.substring(endIndex + 1, string_number.length()); - double decimal = Double.parseDouble(decimal_string); - decimal = decimal / (Math.pow(10, decimal_string.length())); - number = number + decimal; - - } - - } else { - number = Double.parseDouble(string_number); - } - return number; - - } - - // Method to init the model - @ActionDoc(text = "Test Method for building the model") - public static synchronized void initModel() throws IOException { - - logger.warn("Initializing model."); - - if (rl_algorithm == null) { - trainAndSave(); - - } else { - logger.warn("model was already initialized."); - - } - - } - - // Method to init the model loading it - @ActionDoc(text = "Test Method for building the model") - public static synchronized void initModel(@ParamDoc(name = "f_nnRgb") String f_nnRgb, - @ParamDoc(name = "f_nnBright") String f_nnBright) throws IOException { - - logger.warn("Initializing model."); - - if (rl_algorithm == null) { - rl_algorithm = new ReinforcementLearningAlgorithm(N_LAMPS, N_VALUES, f_nnRgb, f_nnBright); - - } else { - logger.warn("model was already initialized."); - - } - } - - // Method to save the model in the following files - // file for nn rgb: "\\openhab-2.3.0\\datasets\\RGB_NN" - // file for nn bright: "\\openhab-2.3.0\\datasets\\Bright_NN" - @ActionDoc(text = "Test Method for saving the model") - public static synchronized void saveModel() throws IOException { - logger.warn("saving model."); - rl_algorithm.saveModel(); - logger.warn("model was saved"); - - } - - // Method to get the plan for the current state(activity, natural light) - @ActionDoc(text = "Test Method for getting the plan", returns = "plan") - public static synchronized HashMap<String, Double> planBright(@ParamDoc(name = "activity") int activity, - @ParamDoc(name = "natural_light") String natural_light) throws IOException { - - if (rl_algorithm == null) { - initModel(); - } - - // natural light from 0 to 10 in HOME IO, from 0 to 100 for us - double light = changeStringToDouble(natural_light) * 10; - State state = new State(activity, light); - - logger.warn("getting plan for activity: " + Integer.toString(activity) + " natural light: " - + Double.toString(light)); - - // get plan using RL - double aux_plan = rl_algorithm.planBright(state); - - // construct hashMap to return - HashMap<String, Double> plan = new HashMap<String, Double>(); - - plan.put("lamp" + Integer.toString(activity), aux_plan); - - return plan; - } - - // training method, input: activity, natural light, ideal_value - @ActionDoc(text = "Test Method for training") - public static synchronized void trainBright(@ParamDoc(name = "activity") int activity, - @ParamDoc(name = "natural_light") String natural_light, @ParamDoc(name = "ideal_light") double ideal_light) - throws IOException { - - if (rl_algorithm == null) { - initModel(); - } - - logger.warn("training algorithm"); - double ideal = ideal_light; - // natural light from 0 to 10 in HOME IO, from 0 to 100 for us - State state = new State(activity, changeStringToDouble(natural_light) * 10); - rl_algorithm.trainAlgorithmBright(state, ideal); - - } - - // method for training the model using datasets and then save the model - private static void trainAndSave() { - - rl_algorithm = new ReinforcementLearningAlgorithm(N_LAMPS, N_VALUES); - - String dataset = "\\openhab-2.3.0\\datasets\\dataset.txt"; - String output = "\\openhab-2.3.0\\datasets\\output.txt"; - - String line; - // read dataset to train our model - try { - FileReader f = new FileReader(dataset); - BufferedReader b = new BufferedReader(f); - - logger.warn("reading dataset."); - FileWriter f_output = new FileWriter(output); - BufferedWriter bw = new BufferedWriter(f_output); - - // we don´t need the first line (header) - line = b.readLine(); - - // read data - double lamps_values[] = new double[N_VALUES]; - // double[] planRGB; - double planBright; - int contador = 0; - while ((line = b.readLine()) != null) { - ++contador; - int index = line.indexOf(" "); - int activity = Integer.parseInt((line.substring(0, index))); - index = index + 1; - int n_index = line.indexOf(" ", index); - double natural_light = Double.parseDouble(line.substring(index, n_index)); - double ideal_bright; - index = n_index; - State state = new State(activity, natural_light); - - index = index + 2; - int last_index = index; - - for (int j = 0; j < N_VALUES; j++) { - - if (j == 2) { - index = line.indexOf("]", index); - } else { - index = line.indexOf(",", index); - } - - lamps_values[j] = Integer.parseInt((line.substring(last_index, index))); - index = index + 2; - last_index = index; - } - - index = line.length(); - ideal_bright = Double.parseDouble(line.substring(last_index, index)); - // before training - - logger.warn("Before Training"); - // planRGB = rl_algorithm.planRGB(state); - planBright = rl_algorithm.planBright(state); - logger.warn("natural light " + natural_light + " activity: " + activity + " light plan: " - + Double.toString(planBright)); - logger.warn("user values: " + Double.toString(ideal_bright)); - - // training the algorithm - // rl_algorithm.trainAlgorithmRGB(state, lamps_values); - rl_algorithm.trainAlgorithmBright(state, ideal_bright); - logger.warn("After Training"); - planBright = rl_algorithm.planBright(state); - logger.warn("natural light " + natural_light + " activity: " + activity + " light plan: " - + Double.toString(planBright)); - logger.warn("user values: " + Double.toString(ideal_bright)); - - } - bw.close(); - f_output.close(); - b.close(); - f.close(); - ; - - } catch (Exception e) { - logger.error("Exception occurred during execution: {}", e.getMessage(), e); - } - rl_algorithm.saveModel(); - - } - - /* - * @ActionDoc(text = "Test Method for training", returns = "algorithm trained") - * public static synchronized void trainRGB(@ParamDoc(name = "activity") int activity, - * - * @ParamDoc(name = "bright") String bright, @ParamDoc(name = "configuration") HSBType configuration) - * throws IOException { - * double[] users_lamps_values = new double[3]; - * - * Color color = new Color(configuration.getRGB()); - * users_lamps_values[0] = color.getRed(); - * users_lamps_values[1] = color.getGreen(); - * users_lamps_values[2] = color.getBlue(); - * - * logger.warn("lamps values given by user: {} {} {}", Double.toString(users_lamps_values[0]), - * Double.toString(users_lamps_values[1]), Double.toString(users_lamps_values[2])); - * if (rl_algorithm == null) { - * initModel(); - * } - * logger.warn("training algorithm"); - * - * State state = new State(activity, Double.parseDouble(bright)*10); - * rl_algorithm.trainAlgorithmRGB(state, users_lamps_values); - * - * } - * - * - * - * @ActionDoc(text = "Test Method for getting the plan", returns = "plan") - * public static synchronized HashMap<String, HSBType> planRGB() throws IOException { - * - * if (rl_algorithm == null) { - * initModel(); - * } - * logger.warn("getting plan"); - * HashMap<String, HSBType> plan = new HashMap<String, HSBType>(); - * - * double[] lamps_values = rl_algorithm.planRGB(state); - * - * for (int i = 0; i < N_LAMPS; i++) { - * int red = (int) Math.round(lamps_values[i][0]); - * int green = (int) Math.round(lamps_values[i][1]); - * int blue = (int) Math.round(lamps_values[i][2]); - * logger.warn("lamps values given by plan function: {} {} {}", red, green, blue); - * HSBType configuration = HSBType.fromRGB(red, green, blue); - * configuration.toRGB(); - * plan.put("lamp" + Integer.toString(i), configuration); - * - * } - * return plan; - * - * } - */ - -} diff --git a/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/MainActionService.java b/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/MainActionService.java deleted file mode 100644 index d29584aedca854d07e321fc23c41f6e845105c8b..0000000000000000000000000000000000000000 --- a/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/MainActionService.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) 2010-2016 by the respective copyright holders. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.openlicht.action.reinforcementlearning.internal; - -import java.util.Dictionary; - -import org.openhab.core.scriptengine.action.ActionService; -import org.osgi.service.cm.ConfigurationException; -import org.osgi.service.cm.ManagedService; - -/** - * This class registers an OSGi service for the Main action. - * - * @author David Morales Rodríguez - */ -public class MainActionService implements ActionService, ManagedService { - - /** - * Indicates whether this action is properly configured which means all - * necessary configurations are set. This flag can be checked by the - * action methods before executing code. - */ - /* default */ static boolean isProperlyConfigured = false; - - public MainActionService() { - } - - public void activate() { - } - - public void deactivate() { - // deallocate Resources here that are no longer needed and - // should be reset when activating this binding again - } - - @Override - public String getActionClassName() { - return Main.class.getCanonicalName(); - } - - @Override - public Class<?> getActionClass() { - return Main.class; - } - - @Override - @SuppressWarnings("rawtypes") - public void updated(Dictionary config) throws ConfigurationException { - - } - -} diff --git a/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/MainActivator.java b/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/MainActivator.java deleted file mode 100644 index 98b2c764bae1e1ad790b44baeb3ab1c2529f44bd..0000000000000000000000000000000000000000 --- a/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/MainActivator.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2010-2016 by the respective copyright holders. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.openlicht.action.reinforcementlearning.internal; - -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Extension of the default OSGi bundle activator - * - * @author David Morales Rodríguez - */ -public final class MainActivator implements BundleActivator { - - private static Logger logger = LoggerFactory.getLogger(MainActivator.class); - - private static BundleContext context; - - /** - * Called whenever the OSGi framework starts our bundle - */ - @Override - public void start(BundleContext bc) throws Exception { - context = bc; - logger.debug("Main action has been started."); - } - - /** - * Called whenever the OSGi framework stops our bundle - */ - @Override - public void stop(BundleContext bc) throws Exception { - context = null; - logger.debug("Main action has been stopped."); - } - - /** - * Returns the bundle context of this bundle - * - * @return the bundle context - */ - public static BundleContext getContext() { - return context; - } - -} diff --git a/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/Model.java b/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/Model.java deleted file mode 100644 index 9da77cadfcd9672a93cc92ba8d442b4b8504985c..0000000000000000000000000000000000000000 --- a/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/Model.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.openlicht.action.reinforcementlearning.internal; - -import java.util.Random; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -//abstract class Model -public abstract class Model { - protected int N_VALUES; - protected int N_LAMPS; - protected Random rnd = new Random(); - protected static Logger logger = LoggerFactory.getLogger(Model.class); - - public Model(int N_LAMPS, int N_VALUES) { - this.N_LAMPS = N_LAMPS; - this.N_VALUES = N_VALUES; - rnd.setSeed(System.currentTimeMillis()); - } - - // method to validate lamps values (RGB) - protected double[] validateLampsValues(double[] l_values) { - for (int i = 0; i < this.N_VALUES; ++i) { - if (l_values[i] > 255) { - l_values[i] = 255; - } - - if (l_values[i] < 0) { - l_values[i] = 0; - } - - } - return l_values; - } - - // method to validate bright value - protected double validateBrightValue(double bright_value) { - - if (bright_value > 100) { - bright_value = 100; - } - - if (bright_value < 0) { - bright_value = 0; - } - - return bright_value; - } - - // training the model for the current state using the values given by the user - public abstract void trainBright(State state, double bright); - - // training the model for the current state using the values given by the user - public abstract void trainRGB(State state, double[] user_lamps_values); - - // get output (lams_values (RGB)) using exploitation - public abstract double[] getPlanRGB(State state); - - // get output (lams_values (Bright)) using exploitation - public abstract double getPlanBright(State state); - - // method to save the model - public abstract void saveModel(); - -} diff --git a/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/MultilayerPerceptron_Model.java b/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/MultilayerPerceptron_Model.java deleted file mode 100644 index 5ee2ed452a837a84d28e3c9887facb1bb9b5a5d0..0000000000000000000000000000000000000000 --- a/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/MultilayerPerceptron_Model.java +++ /dev/null @@ -1,236 +0,0 @@ -package org.openlicht.action.reinforcementlearning.internal; - -import java.io.File; - -import org.encog.engine.network.activation.ActivationSigmoid; -import org.encog.ml.data.MLDataSet; -import org.encog.ml.data.basic.BasicMLDataSet; -import org.encog.ml.train.MLTrain; -import org.encog.neural.networks.BasicNetwork; -import org.encog.neural.networks.layers.BasicLayer; -import org.encog.neural.networks.training.propagation.back.Backpropagation; -import org.encog.persist.EncogDirectoryPersistence; - -//This class implement a Rl model using two Neural networks build using encog -public class MultilayerPerceptron_Model extends Model { - - // first NN to get rgb configuration - private BasicNetwork RGB_network; - // second NN to get bright configuration - private BasicNetwork Bright_network; - - // constructor - public MultilayerPerceptron_Model(int N_LAMPS, int N_VALUES) { - - super(N_LAMPS, N_VALUES); - - // NN to predict RGB values - this.RGB_network = new BasicNetwork(); - - // no activation function,true->bias neurons, input layer has 2 neurons (activity, natural_light) - this.RGB_network.addLayer(new BasicLayer(null, true, 2)); - - // Sigmoid activation function,true->bias neurons, hidden layer has 6 neurons - this.RGB_network.addLayer(new BasicLayer(new ActivationSigmoid(), true, 6)); - - // Sigmoid activation function,false->bias neurons, output layer has 3 neurons (RGB values) - this.RGB_network.addLayer(new BasicLayer(new ActivationSigmoid(), false, 3)); - this.RGB_network.getStructure().finalizeStructure(); - this.RGB_network.reset(); - - // NN to predict bright - this.Bright_network = new BasicNetwork(); - - // no activation function,true->bias neurons, input layer has 2 neurons (activity, natural_light) - this.Bright_network.addLayer(new BasicLayer(null, true, 2)); - - // Sigmoid activation function,true->bias neurons, hidden layer has 10 neurons - this.Bright_network.addLayer(new BasicLayer(new ActivationSigmoid(), true, 10)); - - // Sigmoid activation function,false->bias neurons, output layer has 1 neurons (Bright value) - this.Bright_network.addLayer(new BasicLayer(new ActivationSigmoid(), false, 1)); - this.Bright_network.getStructure().finalizeStructure(); - this.Bright_network.reset(); - - } - - // constructor with files to initialize NNs - public MultilayerPerceptron_Model(int N_LAMPS, int N_VALUES, String file_NNrgb, String file_NNbright) { - - super(N_LAMPS, N_VALUES); - logger.warn("trying to load nn"); - this.RGB_network = (BasicNetwork) EncogDirectoryPersistence.loadObject(new File(file_NNrgb)); - this.Bright_network = (BasicNetwork) EncogDirectoryPersistence.loadObject(new File(file_NNbright)); - logger.warn("nn initialized"); - - } - - // method to save the model - @Override - public void saveModel() { - - // save model - EncogDirectoryPersistence.saveObject(new File("\\openhab-2.3.0\\datasets\\RGB_NN"), this.RGB_network); - - EncogDirectoryPersistence.saveObject(new File("\\openhab-2.3.0\\datasets\\Bright_NN"), this.Bright_network); - - } - - /////////////////////////////////////// aux - /////////////////////////////////////// functions//////////////////////////////////////////////////////////////// - - // function to normalize values. NN get values for a sigmoid function - private double normalizeActivityPlanRGB(int activity) { - return (activity - 2) * 1; - - } - - // function to normalize values. NN get values for a sigmoid function - private double normalizeNatural_lightPlanRGB(double natural_light) { - return ((natural_light - 50) / 8); - } - - // function to normalize values. NN get values for a sigmoid function - private double normalizeActivityPlanBright(int activity) { - return (activity - 2) * 1; - - } - - // function to normalize values. NN get values for a sigmoid function - private double normalizeNatural_lightPlanBright(double natural_light) { - return ((natural_light - 50) / 8); - } - - ///////////////////////////////// train methods///////////////////////////////////////////////////////////////// - - // train model for bright prediction - @Override - public void trainBright(State state, double ideal_bright) { - double[][] IDEAL = new double[1][1]; - // scale for the NN - IDEAL[0][0] = ideal_bright / 100; - - // normalize values to use sigmod functions - double activity_lamp = this.normalizeActivityPlanBright(state.getActivity()); - double natural_light = this.normalizeNatural_lightPlanBright(state.getNatural_light()); - - double[][] INPUT = new double[1][2]; - INPUT[0][0] = activity_lamp; - INPUT[0][1] = natural_light; - - // training using backpropagation - MLDataSet trainingSet = new BasicMLDataSet(INPUT, IDEAL); - // learning rate of 3.5 and a momentum of 0.3 - MLTrain train = new Backpropagation(this.Bright_network, trainingSet, 3.5, 0.3); - int epoch = 1; - do { - train.iteration(); - // System.out.println("Epoch #" + epoch + "Error:" + train.getError()); - epoch++; - } while (train.getError() > 0.005); - - train.finishTraining(); - - // test the neural network - // System.out.println("Bright Neural Network Results:"); - /* - * for (MLDataPair pair : trainingSet) { - * final MLData output = this.Bright_network.compute(pair.getInput()); - * System.out.println( - * "INPUT= [" + pair.getInput().getData(0) + ", " + pair.getInput().getData(1) + "], actual= [" - * + output.getData(0) * 100 + "] ,ideal= [" + pair.getIdeal().getData(0) * 100 + "]"); - * } - */ - } - - @Override - public void trainRGB(State state, double[] user_lamps_values) { - - double[][] IDEAL = new double[1][3]; - - // from RGB to %R %G %B - for (int i = 0; i < this.N_VALUES; i++) { - IDEAL[0][i] = user_lamps_values[i] / 255; - } - - double activity_lamp = this.normalizeActivityPlanRGB(state.getActivity()); - double natural_light = this.normalizeNatural_lightPlanRGB(state.getNatural_light()); - double[][] INPUT = new double[1][2]; - INPUT[0][0] = activity_lamp; - INPUT[0][1] = natural_light; - - // training using backpropagation - MLDataSet trainingSet = new BasicMLDataSet(INPUT, IDEAL); - // learning rate of 3.5 and a momentum of 0.3 - MLTrain train = new Backpropagation(this.RGB_network, trainingSet, 3.5, 0.3); - int epoch = 1; - do { - train.iteration(); - System.out.println("Epoch #" + epoch + "Error:" + train.getError()); - epoch++; - } while (train.getError() > 0.005); - - train.finishTraining(); - - // test the neural network - /* - * System.out.println("RGB Neural Network Results:"); - * for (MLDataPair pair : trainingSet) { - * final MLData output = this.RGB_network.compute(pair.getInput()); - * System.out.println("INPUT= [" + pair.getInput().getData(0) + ", " + pair.getInput().getData(1) - * + "], actual= [" + output.getData(0) * 255 + ", " + output.getData(1) * 255 + ", " - * + output.getData(2) * 255 + "] ,ideal= [" + pair.getIdeal().getData(0) * 255 + ", " - * + pair.getIdeal().getData(1) * 255 + ", " + pair.getIdeal().getData(2) * 255 + "]"); - * } - */ - } - - ///////////////////////////////////////////// exploitation - ///////////////////////////////////////////// methods/////////////////////////////////////////////////////////////////// - - // exploitation method for RGB values - @Override - public double[] getPlanRGB(State state) { - - double[] output = new double[this.N_VALUES]; - - // input must be an array, network.compute(input,output)... - double[] input = new double[2]; - input[0] = this.normalizeActivityPlanRGB(state.getActivity()); - input[1] = this.normalizeNatural_lightPlanBright(state.getNatural_light()); - - // get output - this.RGB_network.compute(input, output); - - // to RGB - for (int i = 0; i < this.N_VALUES; i++) { - output[i] = output[i] * 255; - } - - return output; - - } - - // exploitation method for Bright values - @Override - public double getPlanBright(State state) { - - double aux[] = new double[1]; - double output; - - // input must be an array, network.compute(input,output)... - double[] input = new double[2]; - input[0] = this.normalizeActivityPlanBright(state.getActivity()); - input[1] = this.normalizeNatural_lightPlanBright(state.getNatural_light()); - - // get output - this.Bright_network.compute(input, aux); - - // to bright - output = validateBrightValue(100 * aux[0]); - - return output; - - } - -} diff --git a/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/RLAgent.java b/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/RLAgent.java deleted file mode 100644 index dd78a6052a3eb48ae56c5eff470432396afd2769..0000000000000000000000000000000000000000 --- a/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/RLAgent.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.openlicht.action.reinforcementlearning.internal; - -import java.util.Random; - -/*This class defines a RL agent*/ -public class RLAgent { - private Random rnd = new Random(); - - // constant to do exploration - private static final double EPSILON = 0; - - // variables that define our environment - // n_lamps: n° lamps - // n_values: n° values for each lamp - private int N_LAMPS; - private int N_VALUES; - - private MultilayerPerceptron_Model model; - - // method to save the model - public void saveModel() { - this.model.saveModel(); - } - - // contructor withoud initialized NN files - public RLAgent(int n_lamps, int n_values) { - rnd.setSeed(System.currentTimeMillis()); - this.N_LAMPS = n_lamps; - this.N_VALUES = n_values; - model = new MultilayerPerceptron_Model(this.N_LAMPS, this.N_VALUES); - } - - // constructor with initialized NN files - public RLAgent(int n_lamps, int n_values, String file_NNrgb, String file_NNbright) { - rnd.setSeed(System.currentTimeMillis()); - this.N_LAMPS = n_lamps; - this.N_VALUES = n_values; - model = new MultilayerPerceptron_Model(this.N_LAMPS, this.N_VALUES, file_NNrgb, file_NNbright); - } - - // method call from reinforcementLearning class to train the agent - // last_state: last state given - // ideal bright values - public void trainingAlgorithmBright(State state, double ideal_bright) { - - // TRAIN THE SYSTEM - model.trainBright(state, ideal_bright); - } - - // method call from reinforcementLearning class to train the agent - // last_state: last state given - // lamps_values: lamps values the algorithm gave back - public void trainingAlgorithmRGB(State state, double[] user_lamps_values) { - - // TRAIN THE SYSTEM - model.trainRGB(state, user_lamps_values); - } - - // method call from reinforcementLearning class to get a configuration for the lamps (RGB) - public double[] getPlanRGB(State state) { - - if (decide_if_exploration()) {// do exploration - // not implemented - return null; - } else {// do exploitation - return model.getPlanRGB(state); - } - - } - - // method call from reinforcementLearning class to get a configuration for the lamps (RGB) - public double getPlanBright(State state) { - - if (decide_if_exploration()) {// do exploration - // not implemented - return -1; - } else {// do exploitation - - return model.getPlanBright(state); - } - - } - - // method to decide if we do exploration - private boolean decide_if_exploration() { - - boolean exploration = false; - if (rnd.nextFloat() < EPSILON) { - exploration = true; - } - - return exploration; - - } - -} diff --git a/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/ReinforcementLearningAlgorithm.java b/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/ReinforcementLearningAlgorithm.java deleted file mode 100644 index ffa9ad2163642e48a377f320573bed0dff34b3f5..0000000000000000000000000000000000000000 --- a/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/ReinforcementLearningAlgorithm.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.openlicht.action.reinforcementlearning.internal; - -/*This class implement a reinforcement learning algorithm */ -public class ReinforcementLearningAlgorithm { - - // reinforcement learning agent - private RLAgent agent; - private int N_LAMPS; - private int N_VALUES; - - // constructor without initialized NN files - public ReinforcementLearningAlgorithm(int n_lamps, int n_values) { - - this.N_LAMPS = n_lamps; - this.N_VALUES = n_values; - - agent = new RLAgent(N_LAMPS, N_VALUES); - } - - // constructor with initialized NN files - public ReinforcementLearningAlgorithm(int n_lamps, int n_values, String file_NNrgb, String file_NNbright) { - - this.N_LAMPS = n_lamps; - this.N_VALUES = n_values; - - agent = new RLAgent(N_LAMPS, N_VALUES, file_NNrgb, file_NNbright); - } - - // we get the feedback from the user and we train our algorithm with it - public void trainAlgorithmRGB(State state, double[] users_lamps_values) { - this.agent.trainingAlgorithmRGB(state, users_lamps_values); - } - - // we get the feedback from the user and we train our algorithm with it - public void trainAlgorithmBright(State state, double ideal_bright) { - this.agent.trainingAlgorithmBright(state, ideal_bright); - } - - // get plan (RGB) - public double[] planRGB(State state) { - return this.agent.getPlanRGB(state); - - } - - // get plan (Bright) - public double planBright(State state) { - return this.agent.getPlanBright(state); - - } - - // method to save the model - public void saveModel() { - this.agent.saveModel(); - } -} \ No newline at end of file diff --git a/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/State.java b/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/State.java deleted file mode 100644 index 5b0e00b93f22bfef8a4616680dfe92fb6ebc0966..0000000000000000000000000000000000000000 --- a/org.openlicht.action.reinforcementlearning/src/main/java/org/openlicht/action/reinforcementlearning/internal/State.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.openlicht.action.reinforcementlearning.internal; - -//class to define the state(activity, natural light). -public class State { - // user´s activity - private int activity; - private double natural_light; - - public State(int activity, double natural_light) { - this.natural_light = natural_light; - this.activity = activity; - - } - - public int getActivity() { - return this.activity; - - } - - public double getNatural_light() { - return this.natural_light; - } -} \ No newline at end of file diff --git a/org.openlicht.action.reinforcementlearning/src/main/resources/readme.txt b/org.openlicht.action.reinforcementlearning/src/main/resources/readme.txt deleted file mode 100644 index 98698c670dc399dac76f7a173160545eba8f01c1..0000000000000000000000000000000000000000 --- a/org.openlicht.action.reinforcementlearning/src/main/resources/readme.txt +++ /dev/null @@ -1 +0,0 @@ -Bundle resources go in here! \ No newline at end of file diff --git a/pages/.gitignore b/pages/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..14338f39f89e2a45e3473a6597e640aa9458f9a8 --- /dev/null +++ b/pages/.gitignore @@ -0,0 +1,2 @@ +/docs/ragdoc/ +__pycache__ diff --git a/pages/custom_theme/footer.html b/pages/custom_theme/footer.html new file mode 100644 index 0000000000000000000000000000000000000000..2d6c164695b3a1ed19711d3497caf142a8416ebb --- /dev/null +++ b/pages/custom_theme/footer.html @@ -0,0 +1,11 @@ +{% block footer %} +<p>{% if config.copyright %} +<small>{{ config.copyright }}<br></small> +{% endif %} +<hr> +Built with <a href="https://www.mkdocs.org/">MkDocs</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>. +{% if page and page.meta and page.meta.git_revision_date_localized %} +<small><br><i>Last updated {{ page.meta.git_revision_date_localized }}</i></small> +{% endif %} +</p> +{% endblock %} diff --git a/pages/docs/DSL.md b/pages/docs/DSL.md new file mode 100644 index 0000000000000000000000000000000000000000..7ac5c9f9b6c5dde3768c40a68e804be9fa5c6d50 --- /dev/null +++ b/pages/docs/DSL.md @@ -0,0 +1,100 @@ +# The eraser-DSL + +Find a general description in the [Model description](Model-description) page, and the parser ([dev](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/eraser-base/src/main/jastadd/eraser.parser), [main](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/main/eraser-base/src/main/jastadd/eraser.parser)) and printing ([dev](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/eraser-base/src/main/jastadd/Printing.jrag), [main](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/main/eraser-base/src/main/jastadd/Printing.jrag)). + +The DSL is line-based, so on each line, one model element will be defined. +All shown specifications for one element are optional and can be reordered, but at least `id` must be present to identify the element. +References to other elements are done by using their IDs as strings, and can be forward references. +The following elements can be described (in the main branch if not otherwise noted): + +## ChannelType + +``` +ChannelType: id="" label="" description="" itemType="" category="" readyOnly; +``` + +- The item type has to be one of the [item names defined for Eclipse Smart Home](https://www.eclipse.org/smarthome/documentation/concepts/items.html) +- The category can either be one of the [categories defined for Eclipse Smart Home](https://www.eclipse.org/smarthome/documentation/concepts/categories.html#channel-categories), or a custom one. +- Both item type and category are defined as enums ([dev](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/eraser-base/src/main/jastadd/enums.jadd), [main](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/main/eraser-base/src/main/jastadd/enums.jadd)) +- The flag `readOnly` is currently without any effect + +## Channel + +``` +Channel: id="" type="" links=["ITEM_ID", "ITEM_ID"]; +``` + +- `type` must reference a [ChannelType](#channeltype), and must be set +- `links` must reference [items](#item) + +## Group + +``` +Group: id="" groups=["GROUP_ID", "GROUP_ID"] items=["ITEM_ID", "ITEM_ID"] aggregation=AGG; +``` + +- `groups` must reference other [groups](#group) +- `items` must reference [items](#item) +- `AGG` in `aggregation` can either be one of `EQUALITY`, `AVG`, `MAX`, `MIN`, `SUM`; or one of `AND`, `OR`, `NAND`, `NOR`, followed by a list of arguments surrounded by round brackets, e.g., `aggregation = AVG` or `aggregation = AND ("off", "on")`. The semantics are described in the [openHAB docs](https://www.openhab.org/docs/configuration/items.html#group-type-and-state) + +## Item + +``` +ITEM_TYPE Item: id="" label="" state="" category="" topic="" controls=["ITEM_ID", "ITEM_ID"] metaData={"key":"value"}; +``` + +- `ITEM_TYPE` can be one of the following: `Color`, `Contact`, `DateTime`, `Dimmer`, `Image`, `Location`, `Number`, `Player`, `RollerShutter`, `String`, `Switch`. If left out, the item will behave as if `String` was used. +- `state` is given as a string and will be interpreted depending on the type of the item +- `topic` implicitely defines a [MQTT](mqtt) topic which is used to update the item in both directions. The full topic is prefixed with `incoming`-prefix if receiving updates from openHAB, and `outgoing`-prefix if sending updates to openHAB +- `category` is currently not used +- `controls` is only available on `dev`. It defines connection of one item controls the state of another item as the state of the first item changes. +- `metaData` contains key-value-pairs comprising its meta data + +## Parameter + +``` +Parameter: id="" label="" description="" type="" default="" required; +``` + +- `type` has to be one of the following: `Boolean`, `Decimal`, `Integer`, `Text` +- `default` is currently an arbitrary String, thus it is not checked whether it matches the type + +## ThingType + +``` +ThingType: id="" label="" description="" parameters=["PARAM_ID", "PARAM_ID"] channelTypes=["CHANNEL_TYPE_ID", "CHANNEL_TYPE_ID"]; +``` + +- `parameters` must reference [parameters](#parameter) +- `channelTypes` must reference [channel types](#channeltype) + +## Thing + +``` +Thing: id="" label="" type="" channels=["CHANNEL_ID", "CHANNEL_ID"] ; +``` + +- `type` must reference a [thing type](#thingtype), and must be set +- `channels` must reference [channels](#channel) + +## Mqtt + +``` +Mqtt: incoming="" outgoing="" host=""; +``` + +- MQTT is used to communicate/synchronize with openHAB +- `incoming` defines the prefix for received MQTT messages, see [topic description in item](#item). Defaults to an empty string, which should be avoided. +- `outgoing` defines the prefix for sent MQTT messages, see [topic description in item](#item). Defaults to an empty string, which should be avoided. +- `host` defines the URL of the host running an MQTT broker. The port will be set to `1883` and cannot be changed. + +## Influx + +``` +Influx: user="" password="" dbName="" host="" ; +``` + +- Influx is used to store historical item states +- `user` and `password` define connection credentials +- `dbName` defines the name of the database to use +- `host` defines the URL of the host running an InfluxDB. The port will be set to `8086` and cannot be changed. diff --git a/pages/docs/Inner-workings.md b/pages/docs/Inner-workings.md new file mode 100644 index 0000000000000000000000000000000000000000..ebbb861f8a380b26abe079584c28f263165d1110 --- /dev/null +++ b/pages/docs/Inner-workings.md @@ -0,0 +1,106 @@ +# Inner workings + +Please also refer to the [API documentation](ragdoc/index.html) + +## openHAB synchronization via MQTT + +MQTTRoot contains `incomingPrefix`, `outgoingPrefix` and `host` tokens. + +Items have a MQTT-topic associated, both from which they get updates via MQTT and publish changes of their state via MQTT. + +Eraser will subscribe to `incomingPrefix/#`, i.e., every update of topics starting with `incomingPrefix` is listened to. +Also, if the a new host is set (`MQTTRoot.Host`), the connection is re-established. +Listening means, that upon a new message, the topic string is resolved to a non-terminal MQTT-topic using `Root.resolveTopic`. This topic non-terminal has a reference to its item, which in turn has its state updated with the body of the MQTT message. Each different item type has its own way to set its state from a string (`Item.setStateFromString`). + +Whenever a state of an item is set from within the KB, this change will be published via MQTT, if `shouldSendState` is set to true, or the default setting (`DefaultShouldSendState`) is set to true. +The topic will be prefix with `outgoingPrefix`. Every item type has its own way to print its state to a string (`Item.getStateAsString`), which is used as a MQTT message body. + + +## Item state history via InfluxDB + +Connecting to Influx is done with the Java binding `org.influxdb:influxdb-java`. To represent the data to send and receive, `ItemPoints` are used defined in the `ItemHistoryPoints.jrag` aspect file. +There is one class for each item type capturing the specifics for translating item states to data points. + +A pretty cool way to add new behavior when setting the item state is to use JastAdd's refinement: + +``` +refine MQTT public void Item.sendState() throws Exception { + refined(); + getRoot().getInfluxRoot().influxAdapter().write(pointFromState()); +} +``` + +This adds writing a new data point to influx after the former behavior (which includes sending the update via MQTT, as described above). +However, those two are intended to be independent of each other. + +For retrieving the history, an asynchronous approach was chosen and is described in the following. The sequence diagram shows the important methods called, where ItemType denotes a specific subclass of Item, e.g., `ColorItem` or `ItemWithDoubleState`, and Item denotes the general, common superclass `Item`. + + + +To get the history, `getHistory` is called on a specific item returning a list of datapoints of a matching type, e.g., a list of `DoubleStatePoint` for an `ItemWithDoubleState`. + +The code in `getHistory` is actuall the same for every type, namely: `return proxied(this::_history);` +This calls `proxied` (shown in yellow above) which first asynchronously updates the history and returns the current value using `_history()` (shown in orange). +For updating the history, first `historyUpdating` is checked, and if appropriate, the current InfluxAdapter is used to invoke `asyncQuery` and use the return value to set a new `history_token`. +This token contains the history, and is returned by the `_history` attribute. If data was never fetched before, an empty list is returned. + +## Rules + +For events in rules to trigger, `ItemObserver` objects are used. The following figure shows three rules (A, B, C) triggered by two different items (`item1` und `item2`): + + + +To get those wirings, the following code can be used: + +```java +Root model = createModel(); +NumberedItem item1 = createNumberedItem(); +ColorItem item2 = createColorItem(); +addToModel(model, item1); +addToModel(model, item2); + +Rule rA = new Rule(); +ItemStateNumberCheck check1 = new ItemStateNumberCheck(ComparatorType.GreaterOrEqualThan, 4); +ItemStateNumberCheck check2 = new ItemStateNumberCheck(ComparatorType.LessThan, 6); +rA.addCondition(new ItemStateCheckCondition(check1)); +rA.addCondition(new ItemStateCheckCondition(check2)); +rA.setAction(new LambdaAction(item -> System.out.println("A was triggered by " + item.getLabel()))); +rA.setAction(new LambdaAction(item -> item.disableSendState())); +rA.activateFor(item1); + +Rule rB = new Rule(); +Rule rC = new Rule(); +// .. conditions, actions of B and C +rB.activateFor(item1); +rB.activateFor(item2); +rC.activateFor(item1); +``` + +Changes to `item1` trigger all three rules, whereas changing `item2` only triggers the second rule B. +Triggering a rule works as follows: + + + +When the state of `item1` is changed, its observer checks, whether the new change is different to the previous change. If that is the case, it triggers all associated rules, i.e, it calls `rule.trigger()` for every rule in the `TriggeredRuleList`. +The rule checks all conditions (if any), and if all hold, executes all actions (if any). + +In this shown case, `item1` was changed, thus all rules `rA`, `rB`, and `rC` are triggered, if the state has changed. The full sequence send for the two latter events is not shown above for brevity. + +### Condition Types + +Currently only one condition is supported, which compares the state of an ItemWithDoubleState with a given constant. It borrows its functionality from `ItemStateCheck`, which has a comparator (`>=`, `>`, `=`, `!=`, `<`, `<=`) and a double constant. + +### Action Types + +| Name | Children | Description | +|------|----------|-------------| +| Action | _none_ | Abstract super class for all actions | +| LambdaAction | <ul><li>`<Lambda:Action2EditConsumer>` An arbitrary lambda consuming an `Item`</li></ul> | Does arbitrary stuff, use only if none of the classes below fit. | +| TriggerRuleAction | <ul><li>`rel Rule -> Rule` Relation to the rule to trigger</li></ul> | Triggers another rule passing the same item to it. | +| _SetStateAction_ (abstract) | <ul><li>`rel AffectedItem -> Item` The item to change the state of</li></ul> | Abstract class for changing the state of one affected item. | +| SetStateFromConstantStringAction | <ul><li>`rel AffectedItem -> Item` (from SetStateAction)</li><li>`<NewState:String>` The new state to set</li></ul> | Sets the given constant as the new state for the affected item | +| SetStateFromLambdaAction | <ul><li>`rel AffectedItem -> Item` (from SetStateAction)</li><li>`<NewStateProvider:NewStateProvider>` A lambda to get the new state</li></ul> | Sets the given variable as the new state for the affected item | +| SetStateFromTriggeringItemAction | <ul><li>`rel AffectedItem -> Item` (from SetStateAction)</li></ul> | Sets the state of the triggering item as the new state for the affected item | +| SetStateFromItemsAction | <ul><li>`rel AffectedItem -> Item` (from SetStateAction)</li><li>`<Combinator:ItemsToStringFunction>` A function taking a list of items and returning a string</li><li>`rel SourceItem* -> Item` A list of input items</li></ul> | Uses the combinator to combine all input items to a new state, which is set for the affected item | +| AddDoubleToStateAction | <ul><li>`rel AffectedItem -> Item` (from SetStateAction)</li><li>`<Increment:double>` A constant value to add</li></ul> | Increments the state of the affected item by the given constant increment | +| MultiplyDoubleToStateAction | <ul><li>`rel AffectedItem -> Item` (from SetStateAction)</li><li>`<Multiplier:double>` A constant value to multipy</li></ul> | Multiplies the state of the affected item by the given constant multiplier | diff --git a/pages/docs/Learner.md b/pages/docs/Learner.md new file mode 100644 index 0000000000000000000000000000000000000000..89a7ed477b766a2c26906aa6e9a5a7b7c14ea9a7 --- /dev/null +++ b/pages/docs/Learner.md @@ -0,0 +1,102 @@ +# Learner + +_This document is only available in German_ + +Der Learner ist ein Interface zur Encog Bibliothek. Er dient nur dazu Modelle zu traineren und zu Laufzeit des Systems anzupassen. Im Folgenden zeigen wir Beispiele wie der Learner zu Verwenden ist. + +## Training mit externen Datensatz + +```java +learner.setCsvFolderPath("/csvs"); + +ArrayList<Integer> targetColumns = new ArrayList<>(); +targetColumns.add(5); +targetColumns.add(7); + +int modelID = 1; + +learner.loadDataSet("test1", targetColumns, modelID); + +ArrayList<Integer> inputMaxes = new ArrayList<>(); +inputMaxes.add(100); // e.g., first Columns is light intensity sensor with max value of 100 +// ... + +ArrayList<Integer> inputMins = new ArrayList<>(); +inputMins.add(0); // e.g., first Columns is light intensity sensor with min value of 0 +// ... + +ArrayList<Integer> inputMaxes = new ArrayList<>(); +targetMaxes.add(255); // e.g., first output value is R value of lamp with max value of 255 +// ... + +ArrayList<Integer> inputMins = new ArrayList<>(); +targetMins.add(0); // e.g., first output value is R value of lamp with min value of 0 +// ... + +int inputCount = 5; +int outputCount = 2; +int hiddenCount = 1; +int hiddenNeuronCount = 4; + +learner.train(inputCount, outputCount, hiddenCount, hiddenNeuronCount, imodelID, inputMaxes, inputMins, targetMaxes, targetMins); + +Model model = learner.getTrainedModel(modelID); +``` + +## Training mit Daten von interner Datenbank + +```java +ArrayList<Integer> targetColumns = new ArrayList<>(); +targetColumns.add(5); +targetColumns.add(7); + +int modelID = 1; + +ArrayList<Integer> inputMaxes = new ArrayList<>(); +inputMaxes.add(100); // e.g., first Columns is light intensity sensor with max value of 100 +// ... + +ArrayList<Integer> inputMins = new ArrayList<>(); +inputMins.add(0); // e.g., first Columns is light intensity sensor with min value of 0 +// ... + +ArrayList<Integer> inputMaxes = new ArrayList<>(); +targetMaxes.add(255); // e.g., first output value is R value of lamp with max value of 255 +// ... + +ArrayList<Integer> inputMins = new ArrayList<>(); +targetMins.add(0); // e.g., first output value is R value of lamp with min value of 0 +... + +int inputCount = 5; +int outputCount = 2; +int hiddenCount = 1; +int hiddenNeuronCount = 4; + +Table table = database.getData("query"); // Database Klasse, Methode und Table existieren nicht, nur ein Beispiel + +double [][] data = table.toArray(); // Methode und Table existieren nicht, nur ein Beispiel + +learner.train(data, inputCount, outputCount, hiddenCount, hiddenNeuronCount, imodelID, inputMaxes, inputMins, targetMaxes, targetMins, targetColumns); + +Model model = learner.getTrainedModel(modelID); +``` + +## Retraining + +```java +ArrayList<Integer> targetColumns = new ArrayList<>(); +targetColumns.add(5); +targetColumns.add(7); + +int modelID = 1; + +data[][] dataVector = database.getNewestVector(); // Database Klasse und Methode existieren nicht nur ein Beispiel + +learner.reTrain(dataVector, targetColumns, modelID); +``` + +## Inference + +Bei der Nutzung der von Machine Learning muss in vielen Fällen die Einagbe und Ausgaben normalisiert bzw. denormalisiert werden. +Die Normalisierer können vom Learner angefragt werden mit getNormalizerInput und getNormalizerTar angefragt werden. diff --git a/pages/docs/MachineLearning.md b/pages/docs/MachineLearning.md new file mode 100644 index 0000000000000000000000000000000000000000..ff7f416cc127077db0553211655f215341e4252b --- /dev/null +++ b/pages/docs/MachineLearning.md @@ -0,0 +1,227 @@ +# Machine Learning Basics + +The relevant parts of the grammar ([dev](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/eraser-base/src/main/jastadd/main.relast) | [main](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/eraser-base/src/main/jastadd/main.relast)) are the following (updated on Feburary, 11th 2019): + +## General information machine learning + +``` +abstract MachineLearningModel ::= <OutputApplication:DoubleDoubleFunction> ; +abstract ItemPreference ::= ; +ItemPreferenceColor : ItemPreference ::= <PreferredHSB:TupleHSB> ; +ItemPreferenceDouble : ItemPreference ::= <PreferredValue:double> ; +rel ItemPreference.Item -> Item ; + +// neural network +NeuralNetworkRoot : MachineLearningModel ::= InputNeuron* HiddenNeuron* OutputLayer ; +OutputLayer ::= OutputNeuron* <Combinator:DoubleArrayDoubleFunction> ; +abstract Neuron ::= Output:NeuronConnection* ; +NeuronConnection ::= <Weight:double> ; +InputNeuron : Neuron ; +HiddenNeuron : Neuron ::= <ActivationFormula:DoubleArrayDoubleFunction> ; +OutputNeuron : HiddenNeuron ::= <Label:String> ; +rel NeuronConnection.Neuron <-> Neuron.Input* ; +rel InputNeuron.Item -> Item ; +rel OutputLayer.AffectedItem -> Item ; + +// decision tree +DecisionTreeRoot : MachineLearningModel ::= RootRule:DecisionTreeRule ; +abstract DecisionTreeElement ::= Preference:ItemPreference*; +abstract DecisionTreeRule : DecisionTreeElement ::= Left:DecisionTreeElement Right:DecisionTreeElement <Label:String> ; +ItemStateCheckRule : DecisionTreeRule ::= ItemStateCheck ; +abstract ItemStateCheck ::= <Comparator:ComparatorType> ; +ItemStateNumberCheck : ItemStateCheck ::= <Value:double> ; +ItemStateStringCheck : ItemStateCheck ::= <Value:String> ; +DecisionTreeLeaf : DecisionTreeElement ::= <ActivityIdentifier:int> <Label:String> ; + +// dummy model +DummyMachineLearningModel : MachineLearningModel ::= <Current:DecisionTreeLeaf> ; +``` + +A machine learning model implements a classification task, thus it has an attribute `classify` returning a general class `Leaf` whose type depends to the specific model. +This leaf represents the result of a classification, and comprises a double value and a set of `ItemPreference`s. +Such a double value can be scaled using the `OutputApplication`, which is a function mapping one double value to another. +If the neural network only outputs values from 0 to 3, but a range from 0 to 100 is needed, the OutputApplication function can multiply the result with 33. + +## Neural networks + +A neural network is, generally speaken, a set of neurons connected through weighted connections. +Those neurons are typically put into layers such that connections of neurons are only possible between neurons in adjecent layers. +Currently in `eraser`, only the output layer is explicitely modelled. + +Neurons are either Input-, Hidden-, or OutputNeurons. +InputNeurons typically form the first (implicit) layer and are connected to Items, thus get their state from the state of the connected Item. +HiddenNeurons are between input and output, and have an individual `ActivationFormula` taking all input values from the neuron and return its state. +OutputNeurons also have an activation formula, and are children of the `OutputLayer`. +In this output layer, a `Combinator` is defined, which merges the values of all output neurons into one double value as result of the whole neural network. + +### Construction of a neural network + +Most of the code snippets in this section are taken from [NeuralNetworkTest in the `dev` branch](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/eraser-base/src/test/java/de/tudresden/inf/st/eraser/NeuralNetworkTest.java). +To construct a neural network, first the root element needs to be created using `createEmpty`, which sets the output application function to the identity function: + +``` +NeuralNetworkRoot neuralNetworkRoot = NeuralNetworkRoot.createEmpty() +``` + +Then all neurons are created using the matching type, e.g.: + +```java +InputNeuron inputNeuron = new InputNeuron(); +HiddenNeuron hiddenNeuron = new HiddenNeuron(); +hiddenNeuron.setActivationFormula(inputs -> 4 * inputs[0]); +OutputNeuron outputNeuron1 = new OutputNeuron(); +outputNeuron1.setLabel("first"); +outputNeuron1.setActivationFormula(inputs -> inputs[0] > 4 ? 1d : 0d); +``` + +Afterwards, the connections are set using the convenience method `connectTo` taking the target neuron and a weight: + +```java +inputNeuron.connectTo(hiddenNeuron, 1); +hiddenNeuron.connectTo(outputNeuron1, 0.4); +``` + +As an important next step, the output layer and all neurons are added to the model: + +```java +neuralNetworkRoot.addInputNeuron(inputNeuron); +neuralNetworkRoot.addHiddenNeuron(hiddenNeuron); +OutputLayer outputLayer = new OutputLayer(); +outputLayer.addOutputNeuron(outputNeuron1); +neuralNetworkRoot.setOutputLayer(outputLayer); +``` + +Connect the items to the neural network. +This will assign the first item in the list to the first input neuron, the second to the second, and so on. + +```java +Item item; +neuralNetworkRoot.connectItems(Arrays.asList(item)); +``` + +The combinator function is set using a lambda expression: + +```java +outputLayer.setCombinator(outputs -> { + int n = 0; + for (double d : outputs) { + n = (n << 1) | (d == 0 ? 0 : 1); + } + return (double) n; +}); +``` + +Finally, the affected item needs to be set in the output layer: + +```java +Item affectedItem; +outputLayer.setAffectedItem(affectedItem); +``` + +To verify, that the network was correctly built, the `check` method can be used. +It will check the various formulas (output, combinator, activation), valid connections, set weights and connected items. +The return value is `true` if no errors were found, and `false` otherwise. +In case of any warnings or errors, they are printed out using the logger. + +```java +boolean everythingValid = neuralNetworkRoot.check(); +``` + +### Using a neural network + +Once a model has been built, it can be used either to recognize activities, or to learn preferences: + +```java +Root model; +// either recognize activities, ... +model.getMachineLearningRoot().setActivityRecognition(neuralNetworkRoot); +// ... or learn preferences +model.getMachineLearningRoot().setPreferenceLearning(neuralNetworkRoot); +``` + +Once add the complete model, the classification can be invoked: + +```java +Leaf leaf = neuralNetworkRoot.classify(); +``` + +Depending on the use case, the leaf can either be interpreted as an activity, or as an item preference. +For an activity, the classification result is used as an acitivty identifier: + +```java +int identifier = leaf.getActivityIdentifier(); +Optional<Activity> activity = model.resolveActivity(identifier); +``` + +Alternatively, the result can be a preference as written in the following. +In case of a neural network, currently only one item will be affected (the one set as AffectedItem in the output layer). + +```java +List<ItemPreference> preferences = leaf.computePreferences(); +for (ItemPreference p : preferences) { + p.apply(); +} +``` + +## Decision Trees + +A decision tree is a tree of decisions. +Every decision rule refers to an item and compares the state of this item to a given constant. +The leaf of the tree don't contain any decisions anymore, instead they capture the classification result. + +### Construction of a decision tree + +Like above, most of the code was taken from the according test, in this case the [DecisionTreeTest](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/eraser-base/src/test/java/de/tudresden/inf/st/eraser/DecisionTreeTest.java). + +To begin with, construct a new `DecisionTreeRoot`. + +```java +DecisionTreeRoot dtroot = new DecisionTreeRoot(); +``` + +Next, create your first decision. +In this case, the rule will decided whether the state of the referenced item is below or above 4. +Instead of the leafs, other rules could be inserted referencing the same or another Item. + +```java +DecisionTreeLeaf isLessThanFour = new DecisionTreeLeaf(); +DecisionTreeLeaf isFourOrGreater = new DecisionTreeLeaf(); +ItemStateNumberCheck check = new ItemStateNumberCheck(ComparatorType.LessThan, 4f); +JastAddList<ItemPreference> preferences = new JastAddList(); +dtroot.setRootRule(new Rule(preferences, isLessThanFour, isFourOrGreater, "check item1", check)); +``` + +For every leaf, its label, the resulting activity identifier and item preferences have to be set. + +```java +Item affectedItem; +isLessThanFour.setLabel("less than four"); +isLessThanFour.setActivityIdentifier(1); +isLessThanFour.addItemPreference(new ItemPreferenceDouble(2, affectedItem)); +isFourOrGreater.setLabel("four or greater"); +isFourOrGreater.setActivityIdentifier(3); +isFourOrGreater.addItemPreference(new ItemPreferenceColor(new TupleHSB(1, 2, 3), affectedItem)); +``` + +The automatic connection of items to all elements of a decision tree is currently not supported. +Instead the items can only be set manually. + +```java +Item item; +try { + DecisionTreeRoot.connectItems(Arrays.asList(item)); +} catch(UnsupportedOperationException e) { + check.setItem(item); +} +``` + +### Using a decision tree + +The result of the classification is the leaf at the end of the "decision path". +If the item has for example a state of 3, the leaf `isLessThanFour` will be returned. + +```java +Leaf leaf = dtroot.classify(); +``` + +A `DecisionTreeLeaf` directly has an activity identifier and item preferences attached to it, thus can be used in the same way as described [above](#using-a-neural-network). diff --git a/pages/docs/Model-description.md b/pages/docs/Model-description.md new file mode 100644 index 0000000000000000000000000000000000000000..58d867236afe194910e58916e10a92a074637f3f --- /dev/null +++ b/pages/docs/Model-description.md @@ -0,0 +1,67 @@ +# Model description + +The latest version of the model: [main](https://git-st.inf.tu-dresden.de/rschoene/eraser/blob/main/eraser-base/src/main/jastadd/main.relast) | [dev](https://git-st.inf.tu-dresden.de/rschoene/eraser/blob/dev/eraser-base/src/main/jastadd/main.relast) + +Below, all parts of the model are described in more detail. Each part resides in a separate grammar file. + +## Main + +`Root ::= SmartHomeEntityModel User* MqttRoot InfluxRoot MachineLearningRoot Rule* Location* ;` + +- `SmartHomeEntityModel`: Smart home entities +- `User*`: Users in the system (work in progress) +- `MqttRoot`: Everything needed for MQTT (communication with openHAB) +- `InfluxRoot`: Everything needed for Influx (time series database for item state history) +- `MachineLearningModelRoot`: The learned models for activity recognition and preference resolving +- `Rule*`: Self-made ECA rules +- `Location*`: Locations associated with users, activities, preferences (work in progress) + +## Smart Home Entity Model + +`SmartHomeEntityModel ::= Thing* Group* ThingType* Parameter* ChannelType* Channel* ItemCategory* /ActivityItem:Item/ FrequencySetting* ;` + +The structure is mostly extracted from openHAB. There are some abstract superclasses to share common token, such as `ID`, `label` or `description`. Some differences to openHAB include: + +- Links are not modelled explicitly, but instead the items are directly referenced within channels +- Item type was reified instead of a token describing the type. This has the advantage, that the state can be used in a more type-safe way. On the downside, more code is required in some instances, such as for item history, and there is no easy `getState` method. + +Explanations for the other types: + +- **Things** represent physical entities, such as a Hue lamp, a complex (or simple) sensor, a monitor, a webservice. They are typed and described by ThingTypes. +- **ThingTypes** describe the blueprint for a Thing, like a class does for objects in OO languages. They also have **ChannelTypes** describing possible capabilities. +- **Items** are the capabilities of the things, e.g., the color temperature of a lamp, the measured temperature value of a sensor, a method of a webservice (the last one is probably a bad design). They have a type as described above. +- Things have **Channels** (one for each ChannelType of their ThingType) linking their effective capabilities to items via **Links**, such that those items represent the current value of this capability. A channel has a **ChannelCategory** describing the capability, e.g., `BatteryLevel`, `Switch`, `Presence` +- **Groups** contain other groups, and other items + +## MQTT + +- Topics are a flat list, referencing items to be updated upon a message in this topic +- All topics have one common prefix to subscribe to topics, and another common prefix to publish topics + +## Influx + +Basically, here are just some tokens containing information for connection to Influx, such as username, password, host + +## Machine Learning + +Handling of machine learning models currently undergoes a change. + +There is a distinction between internal and external models, both implement an encoder interface to get data into those models, and decoder interface to extract data from the models. +There are two internal models which are supported: decision trees and neural networks. + +Decision tree are modelled straight-forward, there are inner nodes and leaves. Inner nodes reference an item and check a certain condition for this item to decide whether to follow the left or the right path of the inner node. The classification is equal to the leaf node at the end of the path. + +Neural networks are also straight-forward, but probably inefficient for bigger networks in the current implementation. Every neuron is a non-terminal which has ingoing and outgoing connections (using bidirectional relations), input neurons reference an item and propagate its state, hidden and output neurons have an activation function, bias neurons always return a constant value. The output layer contains all output neurons and has a combinator function to calculate the classification result. + +## Rules + +Simple ECA rules can be modelled. A rule has a list of conditions and a list of actions. An event can be triggered by some items (via their ItemObserver), i.e., whenever an item changes its state to a different value. Conditions use a logical expression, which can contain simple mathematical expressions, comparisons, and simple logical operations. Actions are organized currently by their purpose, but are wll be simplified to reference only affected items and a number expression to calculate the new value for those items. + +### StateSyncGroup + +#### What does it do? +Item states can be synchronized through StateSyncGroups. Once an item belongs to such a group, all of its state changes will be applied and then sent to other items in that group. +The behavior of the other items in the group following the order to change the state is up to the item type. This is how state transfers between different item types can be managed. For example if a ColorItem (currently at `1%` brightness) and a SwitchItem (currently at value `false`) are in a StateSyncGroup and the SwitchItem is changing to `true` then the ColorItem won't change to 100%. Instead, it keeps its old value (`1%`). + +#### How does it work? +The StateSyncGroup as an entity does only exist while parsing. After resolving all references, it is rewritten (using JastAdd rewrites). As a result, an ECA rule emerges that has conditions and actions whose functionality correspond to the explained behavior in the previous paragraph. diff --git a/pages/docs/config.md b/pages/docs/config.md new file mode 100644 index 0000000000000000000000000000000000000000..ce4ae0f28a7b816a7ba03a6766c7e8f0c0feccc3 --- /dev/null +++ b/pages/docs/config.md @@ -0,0 +1,75 @@ +# Configuration + +This page describes the main configuration options when starter eraser. +Whenever a file is specified here, it will be a combination of `file` and `external`. With `external` set to `true`, the given path is used to access the file system, using the directory of the `eraser.starter` module as current directory for relative paths. If `external` is set to `false` the file is looked up in the bundled JAR file. + +## Starting configuration `starter-settings.yaml` + +In this file, everything is being configured, i.e., which components will be started, and how they are set up. +It has global options (mostly boolean) and sections for components, both declared on the top-level. + +### Global options + +| Name | Type | Description | Default | +|-----------------|---------|--------------------------------------------------------------------|---------| +| `useMAPE` | Boolean | Start the feedback loop | True | +| `sharedLearner` | Boolean | Use the same learner instance for activity and preference learning | False | +| `mqttUpdate` | Boolean | Get updates from openHAB into the knowledge base | True | +| `initModelWith` | Choice | Method to initialize model. Possible values: "load", "openhab" | "load" | + +### Section `rest` + +This section configures the REST server providing an API to interact with eraser. + +| Name | Type | Description | Default | +|---------------------|---------|------------------------------------------------------------------------------------------|---------| +| `use` | Boolean | Start the REST server | True | +| `port` | Integer | Port of the REST server | 4567 | +| `createDummyMLData` | Boolean | Add some dummy data for activities and events. Only effective when using the REST server | False | + +### Section `load` + +This section defines how to initialize eraser when using the [option `initModelWith`](#global-options) with the value "load". + +| Name | Type | Description | Default | +|------------|----------|-------------------------------------------------------------------|---------| +| `file` | Filename | File to read in, expected format: `.eraser` | _None_ | +| `external` | Boolean | False: Use file bundled in the JAR. True: Use file in filesystem. | False | + +### Sections `activity` and `preference` + +These section have equivalent options and define the activity recogntion taking place in the Analyze phase, and the preference learning triggered in the Plan phase respectively. + +| Name | Type | Description | Default | +|------------|----------|--------------------------------------------------------------------|---------| +| `file` | Filename | File to read in, expected format: `.eraser` | _None_ | +| `external` | Boolean | False: Use file bundled in the JAR. True: Use file in filesystem. | False | +| `dummy` | Boolean | Use dummy model in which the current activity is directly editable | false | +| `id` | Integer | Model id. Currently unused | 1 | + + +### Section `openhab` + +This section configures the communication with openHAB. + +| Name | Type | Description | Default | +|---------------------|--------|--------------------------------------------------------------|---------| +| `url` | String | The URL from which to import and at which openHAB is running | _None_ | +| `metadataNamespace` | String | The metadata namespace used for items | _None_ | + +## Configuring start of eraser + +To build eraser, execute + +```bash +./gradlew :eraser.starter:installDist +``` + +This will create bundled JARs and a script to launch the starter. Both will be located in `eraser.starter/build/install/eraser.starter/`. +To start, change to this directory and invoke + +```bash +./bin/eraser.starter -f starter-setting.yaml +``` + +Beforehand, the configuration has to be changed according to your needs. diff --git a/pages/docs/contributing.md b/pages/docs/contributing.md new file mode 100644 index 0000000000000000000000000000000000000000..bddb5e917a41b77195e915faec5f1652c68cd6f8 --- /dev/null +++ b/pages/docs/contributing.md @@ -0,0 +1,95 @@ +# Contributing + +Please also refer to the [API documentation](ragdoc/index.html) + +Steps to create the initial multi-project setup from a single-project setup + +- create a new Gradle project named `eraser` using IntelliJ +- manually create subdirectory `eraser-base`, move `src/` to this directory +- change `build.gradle` to + +```Groovy +allprojects { + group = 'de.tudresden.inf.st' + version = '1.0.0-SNAPSHOT' +} + +subprojects { + apply plugin: 'java' + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + + task packageSources(type: Jar) { + classifier = 'sources' + from sourceSets.main.allSource + } + + artifacts.archives packageSources + configurations { + testArtifacts.extendsFrom testRuntime + } + + task testJar(type: Jar) { + classifier "test" + from sourceSets.test.output + } + + artifacts { + testArtifacts testJar + } +} +``` +- create new `eraser-base/build.gradle` with the following content + +```Groovy +repositories { + mavenCentral() +} + +apply plugin: 'jastadd' + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' +} + +buildscript { + repositories.mavenLocal() + repositories.mavenCentral() + dependencies { + classpath 'org.jastadd:jastaddgradle:1.12.2' + } +} + +jastadd { + configureModuleBuild() + modules "jastadd_modules" + + module = "expressions" + + astPackage = 'de.tudresden.inf.st.eraser.jastadd.model' + genDir = 'src/gen/java' + + buildInfoDir = 'src/gen-res' +} +``` +- create new directory `eraser-base/src/main/jastadd` and put in JADD and JRAG files +- create new `eraser-base/jastadd_modules` with the following content: + +```Groovy +module("expressions") { + + java { + basedir "src/main/java/" + include "**/*.java" + } + + jastadd { + basedir "src/main/jastadd/" + include "**/*.ast" + include "**/*.jadd" + include "**/*.jrag" + } +} +``` +- optionally create a `.gitignore` to not commit `.idea` and `.gradle` directories +- optionally create a Main Java class diff --git a/pages/docs/img/config-eraser-binding.png b/pages/docs/img/config-eraser-binding.png new file mode 100644 index 0000000000000000000000000000000000000000..be402279d557960dab5e89dabba12a39ef2b22f9 Binary files /dev/null and b/pages/docs/img/config-eraser-binding.png differ diff --git a/pages/docs/img/config-mqtt-binding.png b/pages/docs/img/config-mqtt-binding.png new file mode 100644 index 0000000000000000000000000000000000000000..2157fe69d8efb03ab80540eca8023900d3496b2a Binary files /dev/null and b/pages/docs/img/config-mqtt-binding.png differ diff --git a/pages/docs/img/item-history.png b/pages/docs/img/item-history.png new file mode 100644 index 0000000000000000000000000000000000000000..ebf497bb55fe9babe73b4e9694605139db19da5b Binary files /dev/null and b/pages/docs/img/item-history.png differ diff --git a/pages/docs/img/item-history.uxf b/pages/docs/img/item-history.uxf new file mode 100644 index 0000000000000000000000000000000000000000..98efa99a8668269eb18628f00c0419210599aed3 --- /dev/null +++ b/pages/docs/img/item-history.uxf @@ -0,0 +1,239 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<diagram program="umlet" version="14.3.0"> + <zoom_level>10</zoom_level> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>50</x> + <y>100</y> + <w>150</w> + <h>30</h> + </coordinates> + <panel_attributes>_:Caller-Thread_</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>370</x> + <y>100</y> + <w>150</w> + <h>30</h> + </coordinates> + <panel_attributes>_:Updater-Thread_</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>110</x> + <y>120</y> + <w>30</w> + <h>80</h> + </coordinates> + <panel_attributes>lt=.</panel_attributes> + <additional_attributes>10.0;10.0;10.0;60.0</additional_attributes> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>110</x> + <y>180</y> + <w>20</w> + <h>340</h> + </coordinates> + <panel_attributes> +layer=-1</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>120</x> + <y>180</y> + <w>210</w> + <h>60</h> + </coordinates> + <panel_attributes>lt=<<- +TypedItem.getHistory</panel_attributes> + <additional_attributes>10.0;40.0;40.0;40.0;40.0;10.0;10.0;10.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>120</x> + <y>230</y> + <w>150</w> + <h>60</h> + </coordinates> + <panel_attributes>lt=<<- +Item.proxied</panel_attributes> + <additional_attributes>20.0;40.0;40.0;40.0;40.0;10.0;10.0;10.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>430</x> + <y>120</y> + <w>30</w> + <h>290</h> + </coordinates> + <panel_attributes>lt=.</panel_attributes> + <additional_attributes>10.0;10.0;10.0;270.0</additional_attributes> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>430</x> + <y>390</y> + <w>20</w> + <h>60</h> + </coordinates> + <panel_attributes/> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>130</x> + <y>370</y> + <w>320</w> + <h>40</h> + </coordinates> + <panel_attributes>lt=<- +InfluxAdapter.asyncQuery</panel_attributes> + <additional_attributes>300.0;20.0;10.0;20.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>130</x> + <y>280</y> + <w>310</w> + <h>60</h> + </coordinates> + <panel_attributes>lt=<<- +ItemType.influxMeasurementName</panel_attributes> + <additional_attributes>10.0;40.0;40.0;40.0;40.0;10.0;10.0;10.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>130</x> + <y>320</y> + <w>200</w> + <h>60</h> + </coordinates> + <panel_attributes>lt=<<- +ItemType.pointClass</panel_attributes> + <additional_attributes>10.0;40.0;40.0;40.0;40.0;10.0;10.0;10.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>440</x> + <y>390</y> + <w>220</w> + <h>60</h> + </coordinates> + <panel_attributes>lt=<<- +Item.set_history_token</panel_attributes> + <additional_attributes>10.0;40.0;40.0;40.0;40.0;10.0;10.0;10.0</additional_attributes> + </element> + <element> + <id>UMLSpecialState</id> + <coordinates> + <x>430</x> + <y>460</y> + <w>20</w> + <h>20</h> + </coordinates> + <panel_attributes>type=termination</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>430</x> + <y>440</y> + <w>30</w> + <h>40</h> + </coordinates> + <panel_attributes>lt=.</panel_attributes> + <additional_attributes>10.0;10.0;10.0;20.0</additional_attributes> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>120</x> + <y>270</y> + <w>20</w> + <h>240</h> + </coordinates> + <panel_attributes> +layer=3 +bg=yellow</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLNote</id> + <coordinates> + <x>290</x> + <y>220</y> + <w>120</w> + <h>60</h> + </coordinates> + <panel_attributes>/_history/ +overloaded for +each Itemtype</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>250</x> + <y>230</y> + <w>60</w> + <h>40</h> + </coordinates> + <panel_attributes>lt=.</panel_attributes> + <additional_attributes>40.0;10.0;10.0;20.0</additional_attributes> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>130</x> + <y>440</y> + <w>20</w> + <h>60</h> + </coordinates> + <panel_attributes> +layer=5 +bg=orange</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>130</x> + <y>400</y> + <w>210</w> + <h>60</h> + </coordinates> + <panel_attributes>lt=<<- +syn ItemType._history</panel_attributes> + <additional_attributes>20.0;40.0;40.0;40.0;40.0;10.0;10.0;10.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>140</x> + <y>450</y> + <w>280</w> + <h>60</h> + </coordinates> + <panel_attributes>lt=<<- +Item.fetchHistory(history_token)</panel_attributes> + <additional_attributes>10.0;40.0;30.0;40.0;30.0;10.0;10.0;10.0</additional_attributes> + </element> +</diagram> diff --git a/pages/docs/img/rules-object.png b/pages/docs/img/rules-object.png new file mode 100644 index 0000000000000000000000000000000000000000..6db9a497ad78cc8403790a48aeb601f6db8e20ab Binary files /dev/null and b/pages/docs/img/rules-object.png differ diff --git a/pages/docs/img/rules-object.uxf b/pages/docs/img/rules-object.uxf new file mode 100644 index 0000000000000000000000000000000000000000..e4018519eea968565cb4ad5201ea97a9d9767085 --- /dev/null +++ b/pages/docs/img/rules-object.uxf @@ -0,0 +1,361 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<diagram program="umlet" version="14.3.0"> + <zoom_level>10</zoom_level> + <element> + <id>UMLClass</id> + <coordinates> + <x>360</x> + <y>90</y> + <w>160</w> + <h>50</h> + </coordinates> + <panel_attributes>item1:NumberItem +-- +state: Double +</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>750</x> + <y>90</y> + <w>160</w> + <h>50</h> + </coordinates> + <panel_attributes>item2:ColorItem +-- +state: TupleHSB</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>750</x> + <y>180</y> + <w>160</w> + <h>30</h> + </coordinates> + <panel_attributes>io2:ItemObserver +bg=orange</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>360</x> + <y>180</y> + <w>160</w> + <h>30</h> + </coordinates> + <panel_attributes>io1:ItemObserver +bg=yellow</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>430</x> + <y>130</y> + <w>30</w> + <h>70</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>10.0;10.0;10.0;50.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>810</x> + <y>130</y> + <w>30</w> + <h>70</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>10.0;10.0;10.0;50.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>350</x> + <y>200</y> + <w>60</w> + <h>180</h> + </coordinates> + <panel_attributes>lt=<- +</panel_attributes> + <additional_attributes>10.0;160.0;40.0;160.0;40.0;10.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>420</x> + <y>200</y> + <w>100</w> + <h>170</h> + </coordinates> + <panel_attributes>lt=<- +</panel_attributes> + <additional_attributes>80.0;150.0;80.0;130.0;10.0;130.0;10.0;10.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>480</x> + <y>60</y> + <w>470</w> + <h>320</h> + </coordinates> + <panel_attributes>lt=<- +</panel_attributes> + <additional_attributes>390.0;300.0;450.0;300.0;450.0;20.0;190.0;20.0;190.0;190.0;10.0;190.0;10.0;150.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>610</x> + <y>200</y> + <w>230</w> + <h>190</h> + </coordinates> + <panel_attributes>lt=<- +</panel_attributes> + <additional_attributes>10.0;170.0;50.0;170.0;50.0;80.0;210.0;80.0;210.0;10.0</additional_attributes> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>200</x> + <y>350</y> + <w>160</w> + <h>30</h> + </coordinates> + <panel_attributes>rA:Rule +bg=yellow</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>220</x> + <y>480</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>aA1:Action</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>230</x> + <y>440</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>cA2:Condition</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>210</x> + <y>370</y> + <w>40</w> + <h>100</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>10.0;10.0;10.0;80.0;20.0;80.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>320</x> + <y>370</y> + <w>50</w> + <h>140</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>30.0;10.0;30.0;120.0;10.0;120.0</additional_attributes> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>460</x> + <y>350</y> + <w>160</w> + <h>30</h> + </coordinates> + <panel_attributes>rB:Rule +</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>710</x> + <y>350</y> + <w>160</w> + <h>30</h> + </coordinates> + <panel_attributes>rC:Rule +bg=yellow</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>540</x> + <y>350</y> + <w>80</w> + <h>30</h> + </coordinates> + <panel_attributes>bg=orange +fg=white</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>460</x> + <y>350</y> + <w>90</w> + <h>30</h> + </coordinates> + <panel_attributes>bg=yellow +fg=white</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>230</x> + <y>400</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>cA1:Condition</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>210</x> + <y>370</y> + <w>40</w> + <h>60</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>10.0;10.0;10.0;40.0;20.0;40.0</additional_attributes> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>220</x> + <y>520</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>aA2:Action</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>320</x> + <y>370</y> + <w>50</w> + <h>180</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>30.0;10.0;30.0;160.0;10.0;160.0</additional_attributes> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>470</x> + <y>480</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>aA1:Action</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>570</x> + <y>370</y> + <w>50</w> + <h>140</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>30.0;10.0;30.0;120.0;10.0;120.0</additional_attributes> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>480</x> + <y>400</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>cB1:Condition</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>460</x> + <y>370</y> + <w>40</w> + <h>60</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>10.0;10.0;10.0;40.0;20.0;40.0</additional_attributes> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>470</x> + <y>520</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>aA2:Action</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>570</x> + <y>370</y> + <w>50</w> + <h>180</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>30.0;10.0;30.0;160.0;10.0;160.0</additional_attributes> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>720</x> + <y>420</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>aA1:Action</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>820</x> + <y>370</y> + <w>50</w> + <h>80</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>30.0;10.0;30.0;60.0;10.0;60.0</additional_attributes> + </element> +</diagram> diff --git a/pages/docs/img/rules-sequence.png b/pages/docs/img/rules-sequence.png new file mode 100644 index 0000000000000000000000000000000000000000..57020da1ca87d0249966613da6a49db97d6bb9ef Binary files /dev/null and b/pages/docs/img/rules-sequence.png differ diff --git a/pages/docs/img/rules-sequence.uxf b/pages/docs/img/rules-sequence.uxf new file mode 100644 index 0000000000000000000000000000000000000000..3cbe6465412002d8192be55664f72c923a3ae748 --- /dev/null +++ b/pages/docs/img/rules-sequence.uxf @@ -0,0 +1,482 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<diagram program="umlet" version="14.3.0"> + <zoom_level>10</zoom_level> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>240</x> + <y>280</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>_item1:Item_</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>280</x> + <y>340</y> + <w>20</w> + <h>90</h> + </coordinates> + <panel_attributes/> + <additional_attributes/> + </element> + <element> + <id>UMLActor</id> + <coordinates> + <x>120</x> + <y>330</y> + <w>60</w> + <h>110</h> + </coordinates> + <panel_attributes>User</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>170</x> + <y>340</y> + <w>130</w> + <h>40</h> + </coordinates> + <panel_attributes>lt=<<- +setState</panel_attributes> + <additional_attributes>110.0;20.0;10.0;20.0</additional_attributes> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>420</x> + <y>410</y> + <w>20</w> + <h>100</h> + </coordinates> + <panel_attributes/> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>420</x> + <y>300</y> + <w>30</w> + <h>130</h> + </coordinates> + <panel_attributes>lt=.</panel_attributes> + <additional_attributes>10.0;10.0;10.0;110.0</additional_attributes> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>360</x> + <y>280</y> + <w>150</w> + <h>30</h> + </coordinates> + <panel_attributes>_io1:ItemObserver_ +bg=yellow</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>290</x> + <y>400</y> + <w>150</w> + <h>40</h> + </coordinates> + <panel_attributes>lt=<<<- +apply</panel_attributes> + <additional_attributes>130.0;20.0;10.0;20.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>290</x> + <y>350</y> + <w>130</w> + <h>60</h> + </coordinates> + <panel_attributes>lt=<<<- +sendState0</panel_attributes> + <additional_attributes>10.0;40.0;30.0;40.0;30.0;10.0;10.0;10.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>430</x> + <y>420</y> + <w>130</w> + <h>60</h> + </coordinates> + <panel_attributes>lt=<<<- +check if +stateEquals</panel_attributes> + <additional_attributes>10.0;40.0;30.0;40.0;30.0;10.0;10.0;10.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>430</x> + <y>470</y> + <w>190</w> + <h>40</h> + </coordinates> + <panel_attributes>lt=<<<- +trigger</panel_attributes> + <additional_attributes>170.0;20.0;10.0;20.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>600</x> + <y>300</y> + <w>30</w> + <h>210</h> + </coordinates> + <panel_attributes>lt=.</panel_attributes> + <additional_attributes>10.0;10.0;10.0;190.0</additional_attributes> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>600</x> + <y>490</y> + <w>20</w> + <h>280</h> + </coordinates> + <panel_attributes> +layer=-1</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>560</x> + <y>280</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>_rA:Rule_ +bg=yellow</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>710</x> + <y>280</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>_cA1:Condition_</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>750</x> + <y>570</y> + <w>20</w> + <h>50</h> + </coordinates> + <panel_attributes/> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>750</x> + <y>300</y> + <w>30</w> + <h>290</h> + </coordinates> + <panel_attributes>lt=.</panel_attributes> + <additional_attributes>10.0;10.0;10.0;270.0</additional_attributes> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>830</x> + <y>280</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>_aA1:Action_</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>870</x> + <y>690</y> + <w>20</w> + <h>20</h> + </coordinates> + <panel_attributes/> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>870</x> + <y>300</y> + <w>30</w> + <h>410</h> + </coordinates> + <panel_attributes>lt=.</panel_attributes> + <additional_attributes>10.0;10.0;10.0;390.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>610</x> + <y>560</y> + <w>160</w> + <h>40</h> + </coordinates> + <panel_attributes>lt=<<<- +holdsFor(item1)</panel_attributes> + <additional_attributes>140.0;20.0;10.0;20.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>280</x> + <y>420</y> + <w>30</w> + <h>360</h> + </coordinates> + <panel_attributes>lt=.</panel_attributes> + <additional_attributes>10.0;10.0;10.0;340.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>420</x> + <y>500</y> + <w>30</w> + <h>210</h> + </coordinates> + <panel_attributes>lt=.</panel_attributes> + <additional_attributes>10.0;10.0;10.0;190.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>610</x> + <y>600</y> + <w>160</w> + <h>30</h> + </coordinates> + <panel_attributes>lt=<.</panel_attributes> + <additional_attributes>10.0;10.0;140.0;10.0</additional_attributes> + </element> + <element> + <id>UMLFrame</id> + <coordinates> + <x>590</x> + <y>630</y> + <w>310</w> + <h>130</h> + </coordinates> + <panel_attributes>alt +layer=1 +bg=white +-- +[if all conditions hold] + + + +-. +[else] +</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>280</x> + <y>300</y> + <w>30</w> + <h>60</h> + </coordinates> + <panel_attributes>lt=.</panel_attributes> + <additional_attributes>10.0;10.0;10.0;40.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>610</x> + <y>680</y> + <w>280</w> + <h>40</h> + </coordinates> + <panel_attributes>lt=<<<- +applyFor(item1) +layer=2</panel_attributes> + <additional_attributes>260.0;20.0;10.0;20.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>430</x> + <y>700</y> + <w>190</w> + <h>30</h> + </coordinates> + <panel_attributes>lt=<.</panel_attributes> + <additional_attributes>10.0;10.0;170.0;10.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>430</x> + <y>760</y> + <w>190</w> + <h>30</h> + </coordinates> + <panel_attributes>lt=<.</panel_attributes> + <additional_attributes>10.0;10.0;170.0;10.0</additional_attributes> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>990</x> + <y>760</y> + <w>20</w> + <h>50</h> + </coordinates> + <panel_attributes/> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>990</x> + <y>300</y> + <w>30</w> + <h>480</h> + </coordinates> + <panel_attributes>lt=.</panel_attributes> + <additional_attributes>10.0;10.0;10.0;460.0</additional_attributes> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>950</x> + <y>280</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>_rB:Rule_ + +</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>430</x> + <y>780</y> + <w>580</w> + <h>40</h> + </coordinates> + <panel_attributes>lt=<<<- +trigger</panel_attributes> + <additional_attributes>560.0;20.0;10.0;20.0</additional_attributes> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>420</x> + <y>690</y> + <w>20</w> + <h>120</h> + </coordinates> + <panel_attributes/> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>290</x> + <y>800</y> + <w>150</w> + <h>30</h> + </coordinates> + <panel_attributes>lt=<.</panel_attributes> + <additional_attributes>10.0;10.0;130.0;10.0</additional_attributes> + </element> + <element> + <id>UMLGeneric</id> + <coordinates> + <x>280</x> + <y>760</y> + <w>20</w> + <h>60</h> + </coordinates> + <panel_attributes/> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>280</x> + <y>810</y> + <w>30</w> + <h>50</h> + </coordinates> + <panel_attributes>lt=.</panel_attributes> + <additional_attributes>10.0;10.0;10.0;30.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>420</x> + <y>800</y> + <w>30</w> + <h>60</h> + </coordinates> + <panel_attributes>lt=.</panel_attributes> + <additional_attributes>10.0;10.0;10.0;40.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>600</x> + <y>760</y> + <w>30</w> + <h>100</h> + </coordinates> + <panel_attributes>lt=.</panel_attributes> + <additional_attributes>10.0;10.0;10.0;80.0</additional_attributes> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>1000</x> + <y>280</y> + <w>60</w> + <h>30</h> + </coordinates> + <panel_attributes>bg=orange +fg=white</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>950</x> + <y>280</y> + <w>60</w> + <h>30</h> + </coordinates> + <panel_attributes>bg=yellow +fg=white</panel_attributes> + <additional_attributes/> + </element> +</diagram> diff --git a/pages/docs/img/rules.uxf b/pages/docs/img/rules.uxf new file mode 100644 index 0000000000000000000000000000000000000000..fb5fd373e77d85290ec7e64f0ce01c35763ef982 --- /dev/null +++ b/pages/docs/img/rules.uxf @@ -0,0 +1,382 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<diagram program="umlet" version="14.3.0"> + <zoom_level>10</zoom_level> + <element> + <id>UMLClass</id> + <coordinates> + <x>230</x> + <y>30</y> + <w>160</w> + <h>50</h> + </coordinates> + <panel_attributes>item1:NumberItem +-- +state: Double +</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>620</x> + <y>30</y> + <w>160</w> + <h>50</h> + </coordinates> + <panel_attributes>item2:ColorItem +-- +state: TupleHSB</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>620</x> + <y>120</y> + <w>160</w> + <h>30</h> + </coordinates> + <panel_attributes>io2:ItemObserver +bg=orange</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>230</x> + <y>120</y> + <w>160</w> + <h>30</h> + </coordinates> + <panel_attributes>io1:ItemObserver +bg=yellow</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>300</x> + <y>70</y> + <w>30</w> + <h>70</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>10.0;10.0;10.0;50.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>680</x> + <y>70</y> + <w>30</w> + <h>70</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>10.0;10.0;10.0;50.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>220</x> + <y>140</y> + <w>60</w> + <h>230</h> + </coordinates> + <panel_attributes>lt=<- +</panel_attributes> + <additional_attributes>10.0;210.0;40.0;210.0;40.0;10.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>290</x> + <y>140</y> + <w>240</w> + <h>230</h> + </coordinates> + <panel_attributes>lt=<- +</panel_attributes> + <additional_attributes>200.0;210.0;220.0;210.0;220.0;130.0;10.0;130.0;10.0;10.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>350</x> + <y>0</y> + <w>470</w> + <h>370</h> + </coordinates> + <panel_attributes>lt=<- +</panel_attributes> + <additional_attributes>390.0;350.0;450.0;350.0;450.0;20.0;190.0;20.0;190.0;190.0;10.0;190.0;10.0;150.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>480</x> + <y>140</y> + <w>230</w> + <h>270</h> + </coordinates> + <panel_attributes>lt=<- +</panel_attributes> + <additional_attributes>10.0;250.0;50.0;250.0;50.0;80.0;210.0;80.0;210.0;10.0</additional_attributes> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>70</x> + <y>290</y> + <w>160</w> + <h>30</h> + </coordinates> + <panel_attributes>rA:Rule</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>120</x> + <y>420</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>aA:Action</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>120</x> + <y>380</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>cA:Condition</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>120</x> + <y>340</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>e1A:Event +bg=yellow</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>80</x> + <y>310</y> + <w>60</w> + <h>60</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>10.0;10.0;10.0;40.0;40.0;40.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>80</x> + <y>310</y> + <w>60</w> + <h>100</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>10.0;10.0;10.0;80.0;40.0;80.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>80</x> + <y>310</y> + <w>60</w> + <h>140</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>10.0;10.0;10.0;120.0;40.0;120.0</additional_attributes> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>330</x> + <y>290</y> + <w>160</w> + <h>30</h> + </coordinates> + <panel_attributes>rB:Rule</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>380</x> + <y>460</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>aB:Action</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>380</x> + <y>420</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>cB:Condition</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>380</x> + <y>340</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>e1B:Event +bg=yellow</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>340</x> + <y>310</y> + <w>60</w> + <h>60</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>10.0;10.0;10.0;40.0;40.0;40.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>340</x> + <y>310</y> + <w>60</w> + <h>140</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>10.0;10.0;10.0;120.0;40.0;120.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>340</x> + <y>310</y> + <w>60</w> + <h>180</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>10.0;10.0;10.0;160.0;40.0;160.0</additional_attributes> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>580</x> + <y>290</y> + <w>160</w> + <h>30</h> + </coordinates> + <panel_attributes>rC:Rule</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>630</x> + <y>420</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>aC:Action</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>630</x> + <y>380</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>cC:Condition</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>630</x> + <y>340</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>e1C:Event +bg=yellow</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>590</x> + <y>310</y> + <w>60</w> + <h>60</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>10.0;10.0;10.0;40.0;40.0;40.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>590</x> + <y>310</y> + <w>60</w> + <h>100</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>10.0;10.0;10.0;80.0;40.0;80.0</additional_attributes> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>590</x> + <y>310</y> + <w>60</w> + <h>140</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>10.0;10.0;10.0;120.0;40.0;120.0</additional_attributes> + </element> + <element> + <id>UMLClass</id> + <coordinates> + <x>380</x> + <y>380</y> + <w>110</w> + <h>30</h> + </coordinates> + <panel_attributes>e2B:Event +bg=orange</panel_attributes> + <additional_attributes/> + </element> + <element> + <id>Relation</id> + <coordinates> + <x>340</x> + <y>310</y> + <w>60</w> + <h>100</h> + </coordinates> + <panel_attributes>lt=<<<<<-</panel_attributes> + <additional_attributes>10.0;10.0;10.0;80.0;40.0;80.0</additional_attributes> + </element> +</diagram> diff --git a/pages/docs/img/swagger-ui.png b/pages/docs/img/swagger-ui.png new file mode 100644 index 0000000000000000000000000000000000000000..f0f5fc77e8648341e092dbff465aa988a3f7805a Binary files /dev/null and b/pages/docs/img/swagger-ui.png differ diff --git a/pages/docs/index.md b/pages/docs/index.md new file mode 100644 index 0000000000000000000000000000000000000000..afb6bbd8ac2d638ef8cb057419b499d4433a7fa2 --- /dev/null +++ b/pages/docs/index.md @@ -0,0 +1,11 @@ +# Documentation of Eraser + +First, setup eraser following the README in the main repository: [dev](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/dev/README.md) | [main](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/blob/main/README.md) +To configure the system, read the [configuration page](config). + +For an overview of the model, check out the [Model description](Model-description). +If you need more details, dive into the [inner workings](Inner-workings) (now with colorful images!), [insights for working with the model](working) the [description of machine learning](MachineLearning) (with code examples) or the [description of Learner implementation](Learner) (only in German). +To be able to read in models, a [dedicated DSL](DSL) must be used. + +Further, the updated [configuration settings regarding MQTT 1.x and 2.x](mqtt) as well as the [Setup guide to connect to openHAB](openhab-binding) (only in German) are found in this wiki. +For contributing, please see [contribution guideline](contributing). diff --git a/pages/docs/mqtt.md b/pages/docs/mqtt.md new file mode 100644 index 0000000000000000000000000000000000000000..70782f601d634e9c9439266c393b53de72b6305d --- /dev/null +++ b/pages/docs/mqtt.md @@ -0,0 +1,89 @@ +# OpenHAB Bindings + +## openHAB 2.0-2.3 and MQTT 1.x + +In the old openHAB versions, there used to be the MQTT event bus, which publishes every change of the state of an item to a channel dedicated to this item, and receives updates for the state analogously. + +The configuration file needed is located in `/etc/openhab2/services/mqtt-eventbus.cfg` with the following content: + +```bash +broker=mosquitto_local +statePublishTopic=oh2/out/${item}/state +commandSubscribeTopic=oh2/in/${item}/state +``` + +If you have an item named `iris1_item`, then every change (e.g., using the PaperUI) will be published to `oh2/out/iris1_item/state`. +And whenever a message is published to `oh2/in/iris1_item/state`, its payload will be used to update the state of the item `iris1_item`. + +## openHAB 2.4+ and MQTT 2.x + +In the more recent versions of openHAB, the old MQTT binding was ported to the new architecture. +However, the event bus feature [was dropped in favour of simple rules](https://community.openhab.org/t/roadmap-mqtt-binding/70657/12). + +Now the following setup is required, involving one new channel, one new group and two new rules. + +### A new MQTT channel to receive updates + +To enable a trigger for all new MQTT messages, a new channel has to be created in the Thing of the respective broker. +Navigate to the Thing of the broker in the PaperUI, and click the "+" near "Channels" to create a new one using the following settings: + +- Channel type: "Publish trigger" (will change the editable fields) +- Channel Id: Choose any appropriate name and stick to it. In the following, `receiveEverythingChannel` will be used. +- Label: Pick a description, like "Receive everything". +- MQTT Topic: This is the topic to subscribe to, and has to match the `outgoing` value in the MQTT settings of Eraser. The default in most examples is to use `oh2/in/#`. +- Payload condition: Is left empty. +- Separator character: The character to separate the last topic and the payload. In the following, "#" is used (not to be confused with the `#` used for the topic above, which matches any topic with the given prefix) + +### A new group for relevant items + +To enable a trigger to publish new states, a new group has to be created with all relevant items as children. +Navigate to Configuration > Items in the PaperUI. If this is not visible, disable the "Simple Mode" found at Configuration > System > Item Linking. This enables the manual editing of items and their links to channels. + +Create a new group with the following settings: + +- Name: Choose any appropriate name and stick to it. In the following, `PublishAllToMqttGroup` will be used. +- Label: Pick a description, like "Items whose state will be published to MQTT". +- Category: Is left empty. +- Type: `Group` +- Base type: `None` (Other types would enable an aggregation function, which is not needed here) + +### A rule to process received updates + +Add the following rule to your rule file in `./etc/openhab2/rules/`: + +```java +rule "Receive all" +when + Channel "mqtt:systemBroker:embedded-mqtt-broker:receiveEverythingChannel" triggered +then + // The receivedEvent String contains unneeded elements like the mqtt topic, we only need everything after the "/" as this is were item name an + // logInfo("one.rules", "receivedEvent: {}", receivedEvent.toString()) + val payload = receivedEvent.toString.split(" ", 3).get(2) + val parts = payload.split("/") + // logInfo("one.rules", "parts: {}", parts) + val lastTopicPartAndRealPayload = parts.get(parts.length - 1) + // logInfo("one.rules", "lastTopicPartAndRealPayload: {}", lastTopicPartAndRealPayload) + val topicAndPayloadTokens = lastTopicPartAndRealPayload.split("#", 2) + // logInfo("one.rules", "topicAndPayloadTokens: {}", topicAndPayloadTokens) + sendCommand(topicAndPayloadTokens.get(0), topicAndPayloadTokens.get(1)); +end +``` + +Note the matching channel id for an system broker with the id `embedded-mqtt-broker`. If you use a normal broker with the id `myMQTTBroker`, it would be `mqtt:broker:myMQTTBroker:receiveEverythingChannel` instead. +The rule processes the received information, which is a String containing the topic and payload, separated by the formerly chosen Separator character `#`. +Lastly, it updates the item matching the last part of the topic with the payload. + +### A rule to publish updates + +```java +rule "Publish all" +when + Member of PublishAllToMqttGroup changed +then + val actions = getActions("mqtt","mqtt:systemBroker:embedded-mqtt-broker") + actions.publishMQTT("oh2/out/" + triggeringItem.name, triggeringItem.state.toString); +end +``` + +Note the matching group name `PublishAllToMqttGroup`, the matching name of the system broker `embedded-mqtt-broker` (see [above](#a-rule-to-process-received-updates) for normal broker), and the correct topic to publish to. +The topic has to match the `incoming` value in the MQTT settings of Eraser. The default in most examples is to use `oh2/out/`. diff --git a/pages/docs/openhab-binding.md b/pages/docs/openhab-binding.md new file mode 100644 index 0000000000000000000000000000000000000000..7be215a07fc2c16486f3836862d67cdd6b0297f6 --- /dev/null +++ b/pages/docs/openhab-binding.md @@ -0,0 +1,157 @@ +# Setup für Knowledge-Base + +_This document is only available in German_ + +## Eraser-Seite + +Grundlage zum Starten der Knowledge-Base ist die Datei `eraser.starter-1.0.0-SNAPSHOT.zip`, welche die eraser-Module, deren Abhängigkeiten sowie ein Start-Skript enthält. + +Zusätzlich ist eine Konfigurationsdatei im YAML-Format notwendig. Die Standard-Konfiguration ist folgende (mit den Standard-Werten in Kommentaren): + +```yaml +# Settings for Eraser. + +# Start the feedback loop. Default: true. +useMAPE: true + +rest: + # Start the REST server. Default: true. + use: true + # Port of the REST server. Default: 4567. + port: 4567 + # Add some dummy data for activities and events. Only effective when using the REST server. Default: false. + createDummyMLData: false + +# Initialize the knowledge base with a file. +# OpenHAB synchronization is done if MQTT url is set in the processed file +load: + # File to read in. Expected format = eraser +# # Option 1: Use built-in file +# file: starter.eraser +# external: false + # Option 2: Use external file + file: starter.eraser + external: true + +# Model for activity recognition. If dummy is true, then the file parameter is ignored. +activity: + # File to read in. Expected format = eg + file: activity.eg + # Use dummy model in which the current activity is directly editable. Default: false. + dummy: true + # Model id. Default: 1. + id: 1 + +# Model for preference learning. If dummy is true, then the file parameter is ignored. +preference: + # File to read in. Expected format = eg + file: preference.eg + # Use dummy model in which the current activity is directly editable. Default: false. + dummy: true + # Model id. Default: 1. + id: 1 + # Items to connect to inputs + items: + - datetime_month + - datetime_day + - datetime_hour + - datetime_minute + - bias + - activity + # Item to change with classification result + affectedItem: iris1_item + +# Initialize the knowledge base by importing data from openHAB. +openhab: + # The URL from which to import and at which openHAB is running + url: "127.0.0.1:8080" + # The metadata namespace for items + metadataNamespace: fuenf + +# Get updates from openHAB into the knowledge base. Default: true. +mqttUpdate: true + +# Method to initialize model. Possible values: "load", "openhab". Default: "load". +initModelWith: load +``` + +Anzupassen ist folgendes: + +- referenzierte Dateien (initiales Modell `load.file = starter.eraser`, ML-Modell für Aktivitätserkennung `activity.file = activity.eg` und ML-Modell für Präferenzen `preference.file = preference.eg`) +- referenzierte Items für die Präferenzen (`preference.items` und `preference.affectedItem`) +- URL von openHAB, inklusive Port. Optional der Namespace für Metadaten. + +Die in der Konfigurations-Datei referenzierte Modell-Datei `starter.eraser` enthält das initiale Modell in der dafür vorgesehenen [DSL](https://git-st.inf.tu-dresden.de/OpenLicht/eraser/wikis/DSL). Ein Beispiel-Modell sieht wie folgt aus: + +``` +Color Item: id="iris1_item" label="Iris 1" state="121,88,68" topic="iris1_item"; +Number Item: id="datetime_month" label="Month" state="1" topic="datetime_month"; +Number Item: id="datetime_day" label="Day" state="31" topic="datetime_day"; +Number Item: id="datetime_hour" label="Hour" state="13" topic="datetime_hour"; +Number Item: id="datetime_minute" label="Minute" state="37" topic="datetime_minute"; +Number Item: id="bias" label="bias item" state="1" ; +Activity Item: id="activity" ; + +Group: id="Lights" items=["iris1_item"]; +Group: id="Datetime" items=["datetime_month", "datetime_day", "datetime_hour", "datetime_minute"]; + +Mqtt: incoming="oh2/out/" outgoing="oh2/in/" host="localhost:2883" ; + +Influx: host="172.22.1.152" ; + +ML: activities={ + 0: "Open door in empty room", + 1: "Door closed in empty room", + 2: "Open door with person in room", + 3: "Door closed with person in room", + 4: "Working", + 5: "Watch TV", + 6: "Reading", + 7: "Listening to music", + 8: "Going to sleep", + 9: "Wake up" +} ; +``` + +Im Statement zu `Mqtt` muss die Verbindung zum MQTT-Broker, der auch im openHAB verwendet wird, angegeben werden. Gegebenfalls muss eine Port-Weiterleitung eingerichtet werden. +Das Statement zu Influx ist optional. + +Zum Starter der Knowledge-Base ist die Konfigurationsdatei anzugeben (hier im Beispiel `./starter-setting.yaml`): + +```bash +./eraser.starter-1.0.0-SNAPSHOT/bin/eraser.starter -f starter-setting.yaml +``` + +Zum Beenden sollte (wie auch beim Starten im Log zu lesen) ein POST-Request auf `localhost:3467/system/exit` gesendet werden. Das ist auch über die [Swagger-UI](#swagger-ui-fur-die-rest-api-der-knowledge-base) möglich. + +## openHAB-Seite + +Grundlage auf der openHAB-Seite ist ein installiertes MQTT-Binding in Version 2, und das Eraser-Binding in Version `2.3.0.201904121612`. + +In der PaperUI muss ein MQTT-Broker-Thing angelegt werden, entweder ein SystemBroker oder ein lokaler Broker, wie im Beispiel gezeigt: + + + +Zusätzlich muss ein Eraser-Thing angelegt werden, welches den Namen des oben genannten Brokers in den Einstellung gesetzt hat (hier im Beispiel `local-mqtt-broker`). +Wahlweise können Updates von allen Items (`Publish All` aktiviert), oder nur von Items in einer Gruppe (`Publish All` deaktiviert und `Publish Group` ausgewählt) an Eraser gesendet werden. + +Weiterhin ist zu beachten, dass die MQTT-Topics hier und bei der Eraser-Konfiguration übereinstimmen, d.h. + +- `openhab.Base-Topic == eraser.outgoing` (im Beispiel `oh2/in`) +- `openhab.Outgoing-Topic == eraser.incoming` (im Beispiel `oh2/out`) + + + +## Swagger-UI für die REST-API der Knowledge-Base + +Zur einfacheren Verwendung der REST-API wurde eine Swagger-UI in Python entwickelt, da das verwendete leichtgewichtige REST-Framework ([SparkJava](http://sparkjava.com/)) dies nicht unterstützt. +Zum Starten wird das Python-Skript gestartet, welches auf `localhost:5000` läuft und nach `localhost:4567` (Standard-Einstellung in der Knowledge-Base unter `rest.port`) weiterleitet. + +```bash +python forward.py +``` + +Abhängigkeiten sind `flask`, `flask_restplus` und `requests`. +Nach Starten sieht die Oberfläche aktuell wie folgt aus: + + diff --git a/pages/docs/setup.md b/pages/docs/setup.md new file mode 100644 index 0000000000000000000000000000000000000000..7d293e437494ab5aef8b93b18f952b6a423495bd --- /dev/null +++ b/pages/docs/setup.md @@ -0,0 +1,17 @@ +# Setup and Building eraser + +This project uses Gradle as the build tool. Please use the provided Gradle wrappers instead of an installed one. + +- Clone the repository, either using https (`https://git-st.inf.tu-dresden.de/OpenLicht/eraser.git`) or ssh (`git@git-st.inf.tu-dresden.de:OpenLicht/eraser.git`) +- (Optional) Build all subprojects: `./gradlew build` + +## Troubleshooting + +### Lombok + +Some of the modules use [Lombok](https://projectlombok.org/). It has to be set up according to your platform, e.g., for IntelliJ the [plugin has to be installed](https://projectlombok.org/setup/intellij). +Afterwards, the annotation processor has to be enabled for the project (which sould be warned about by IntelliJ). + +### Java Version + +All submodules use Java `1.8`. Please ensure using an appropriate compiler. diff --git a/pages/docs/working.md b/pages/docs/working.md new file mode 100644 index 0000000000000000000000000000000000000000..ec9110a2e1157adedac16177ef6cf45116ec850f --- /dev/null +++ b/pages/docs/working.md @@ -0,0 +1,18 @@ +# Working with the model + +The root is mostly the entry point to all needed information, and can be obtained from any element in the model using `getRoot()`. +From there, one has mostly "descend" into the relevant part of the model, e.g., if information is needed about items, then `getOpenHAB2Model()` yields this part of the model. + +There are various attributes to resolve model entities by their ID, e.g., `resolveThing(String thingName)`, `resolveItem(String itemName)`. Those return an `java.util.Optional` with the respective type, e.g., `Optional<Item>` to represent non-matched IDs. + +## OpenHAB specifics + +As described in the [description of the model](Model-description), item type is reified, so all items with a double state are of type `ItemWithDoubleState` and only this type has a `getState` returning its state (as a double value). +There is a convenience way to get the state as a double, or String value using `getStateAsDouble` and `getStateAsString` defined on `Item` and implement in a meaningful way by the different subclasses. +Analogously, state can be set from different type, e.g., from a String, or a boolean value. + +Synchronisation between eraser and openHAB itself currently includes only states of items. Therefore, any structural changes (e.g., addition or removal of an item) or change of other attributes such as description of items or things, is **not tracked nor supported**. + +## Documentation + +When working and especially when extending eraser, it is highly recommended to use the [API documentation](ragdoc/index.html) as a reference manual. diff --git a/pages/main.py b/pages/main.py new file mode 100644 index 0000000000000000000000000000000000000000..c3da017406f772426a95f1754057951b8d66ff5b --- /dev/null +++ b/pages/main.py @@ -0,0 +1,25 @@ +eraserVersionFileName = '../eraser.starter/src/main/resources/eraser.properties' + + +def get_version(): + with open(eraserVersionFileName) as eraserVersionFile: + versionFileContent = eraserVersionFile.read() + return versionFileContent[versionFileContent.rindex('version=') + 8:].strip() + + +def define_env(env): + """ + This is the hook for defining variables, macros and filters + + - variables: the dictionary that contains the environment variables + - macro: a decorator function, to declare a macro. + """ + env.conf['site_name'] = 'Documentation of Eraser ' + get_version() + + @env.macro + def eraser_version(): + return get_version() + + +if __name__ == '__main__': + print(get_version()) diff --git a/pages/mkdocs.yml b/pages/mkdocs.yml new file mode 100644 index 0000000000000000000000000000000000000000..e8b9ac7fe4dd3d3cc52be9581baf02305f5c429f --- /dev/null +++ b/pages/mkdocs.yml @@ -0,0 +1,27 @@ +site_name: Documentation of Eraser +nav: + - setup.md + - config.md + - Model-description.md + - Inner-workings.md + - working.md + - MachineLearning.md + - Learner.md + - DSL.md + - mqtt.md + - openhab-binding.md + - contributing.md + - API documentation: ragdoc/index.html +theme: + name: readthedocs + custom_dir: custom_theme/ +plugins: + - search + - git-revision-date-localized: + type: datetime + timezone: Europe/Berlin + locale: en + fallback_to_build_date: True + - macros +repo_url: https://git-st.inf.tu-dresden.de/OpenLicht/eraser +site_dir: ../public diff --git a/print-coverage.py b/print-coverage.py index 30ee86794382a8e055ce3fa0e0746cd872981b34..951043a82b101393a0486c342a12c7b619f4338e 100644 --- a/print-coverage.py +++ b/print-coverage.py @@ -1,8 +1,16 @@ +import glob import os import untangle -print('Current path: ' + os.path.abspath(os.curdir)) -obj = untangle.parse('eraser-base/build/reports/jacoco/test/jacocoTestReport.xml') -instructions = [o for o in obj.report.counter if o['type'] == 'INSTRUCTION'][0] -missed, covered = int(instructions['missed']), int(instructions['covered']) + +print(f'Current path: {os.path.abspath(os.curdir)}') +missed, covered = 0, 0 +for f in glob.iglob(os.getenv('JACOCO_REPORT')): + print(f'Checking {f}') + obj = untangle.parse(f) + instructions = [o for o in obj.report.counter if o['type'] == 'INSTRUCTION'][0] + missed += int(instructions['missed']) + covered += int(instructions['covered']) # print missed / (missed + covered) -print('Covered %.2f%% of instructions for all projects.' % (missed * 100.0 / (missed + covered))) +if missed == covered == 0: + covered = 1 +print('Covered %.2f%% of instructions for all projects.' % (covered * 100.0 / (missed + covered))) diff --git a/project-template/build.gradle b/project-template/build.gradle index 3bae16225ca01a6fea919d36be3a383c6a62507f..4c719170028a0bc82e806ef5dd56fc08cba627e2 100644 --- a/project-template/build.gradle +++ b/project-template/build.gradle @@ -1,21 +1,9 @@ -apply plugin: 'application' - -dependencies { - compile project(':eraser-base') +plugins { + id 'eraser.java-application-conventions' } -run { - mainClassName = 'de.tudresden.inf.st.eraser.projectName.Main' - standardInput = System.in - if (project.hasProperty("appArgs")) { - args Eval.me(appArgs) - } +dependencies { + implementation project(':eraser-base') } -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} +application.mainClass diff --git a/ragdoc-view b/ragdoc-view deleted file mode 160000 index 4bb8afb1bc3dc6b346e04e72c5896568b2e9b192..0000000000000000000000000000000000000000 --- a/ragdoc-view +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4bb8afb1bc3dc6b346e04e72c5896568b2e9b192 diff --git a/settings.gradle b/settings.gradle index 0a118ec458b406dfae388bf1218c2abd4bfd356b..15d204b21982b9226a4785fb5dc66381c81198b8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,24 +1,22 @@ +pluginManagement { + plugins { + id 'org.jastadd' version '1.13.3' + } +} + rootProject.name = 'eraser' include ':eraser-base' -include 'openhab-mock' include 'integration' include ':benchmark' include ':commons.color' -include ':skywriter-hue-integration' -include ':org.openhab.action.machinelearn' -include ':org.openlicht.action.reinforcementlearning' -include ':stub.org.openhab.core.scriptengine.action' include ':feedbackloop.analyze' include ':feedbackloop.plan' include ':feedbackloop.execute' include ':feedbackloop.api' include ':feedbackloop.main' -include ':ml_test' include ':feedbackloop.monitor' include ':feedbackloop.learner' -include ':influx_test' include ':eraser.spark' include ':eraser.starter' include ':feedbackloop.learner_backup' -include ':datasets' diff --git a/skywriter-hue-integration/build.gradle b/skywriter-hue-integration/build.gradle index 410a39b582bd8ea9316289b95c1f7f0083de6576..77d03a565cbd09eda8fd17563c92b7207c9487db 100644 --- a/skywriter-hue-integration/build.gradle +++ b/skywriter-hue-integration/build.gradle @@ -1,22 +1,10 @@ -apply plugin: 'application' - -dependencies { - compile project(':eraser-base') - compile project(':commons.color') +plugins { + id 'eraser.java-application-conventions' } -run { - mainClassName = 'de.tudresden.inf.st.eraser.skywriter_hue_integration.Main' - standardInput = System.in - if (project.hasProperty("appArgs")) { - args Eval.me(appArgs) - } +dependencies { + implementation project(':eraser-base') + implementation project(':commons.color') } -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} +application.mainClass = 'de.tudresden.inf.st.eraser.skywriter_hue_integration.Main' diff --git a/skywriter-hue-integration/src/main/java/de/tudresden/inf/st/eraser/skywriter_hue_integration/Main.java b/skywriter-hue-integration/src/main/java/de/tudresden/inf/st/eraser/skywriter_hue_integration/Main.java index 43e229c16c23e88440e693f1cc7a62f871d08fdb..04c3ffe472c46bbf8411e1c1ce8f53c43166d940 100644 --- a/skywriter-hue-integration/src/main/java/de/tudresden/inf/st/eraser/skywriter_hue_integration/Main.java +++ b/skywriter-hue-integration/src/main/java/de/tudresden/inf/st/eraser/skywriter_hue_integration/Main.java @@ -2,7 +2,7 @@ package de.tudresden.inf.st.eraser.skywriter_hue_integration; import beaver.Parser; import de.tudresden.inf.st.eraser.commons.color.ColorUtils; -import de.tudresden.inf.st.eraser.commons.color.ColorUtils.RGBvalues; +import de.tudresden.inf.st.eraser.commons.color.ColorUtils.ValuesRGB; import de.tudresden.inf.st.eraser.jastadd.model.*; import de.tudresden.inf.st.eraser.openhab2.mqtt.MQTTUpdater; import de.tudresden.inf.st.eraser.util.ParserUtils; @@ -147,10 +147,10 @@ public class Main { } private static void updateStateHSB(Item skywriter1_x, Item skywriter1_y, Item irisItem) { - RGBvalues rgb = ColorUtils.convertXYtoRGB(Double.parseDouble(skywriter1_x.getStateAsString()), + ValuesRGB rgb = ColorUtils.convertXYtoRGB(Double.parseDouble(skywriter1_x.getStateAsString()), Double.parseDouble(skywriter1_y.getStateAsString()), 1.0); // irisItem.setState(String.format("%d,%d,%d", rgb.red, rgb.green, rgb.blue)); - ColorUtils.HSBvalues255 hsb = ColorUtils.convertRGBtoHSB(rgb).toIntegral().ensureBounds(); + ColorUtils.ValuesIntegralHSB hsb = ColorUtils.convertRGBtoHSB(rgb).toIntegral().ensureBounds(); irisItem.setStateFromString(String.format("%d,%d,%d", hsb.hue, hsb.saturation, hsb.brightness)); } @@ -161,7 +161,7 @@ public class Main { double xyzToken = Double.parseDouble(tokens[i]); rgbArray[i] = (int) Math.round(255 * xyzToken); } - ColorUtils.HSBvalues255 hsb = ColorUtils.convertRGBtoHSB(RGBvalues.of(rgbArray)) + ColorUtils.ValuesIntegralHSB hsb = ColorUtils.convertRGBtoHSB(ValuesRGB.of(rgbArray)) .toIntegral().ensureBounds(); return String.format("%d,%d,%d", hsb.hue, hsb.saturation, hsb.brightness); } diff --git a/stub.org.openhab.core.scriptengine.action/.gitignore b/stub.org.openhab.core.scriptengine.action/.gitignore deleted file mode 100644 index 70b583e34c3316bcd77c807e2d6b85db5e7d49f6..0000000000000000000000000000000000000000 --- a/stub.org.openhab.core.scriptengine.action/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/build/ -/bin/ -logs/ diff --git a/stub.org.openhab.core.scriptengine.action/build.gradle b/stub.org.openhab.core.scriptengine.action/build.gradle deleted file mode 100644 index 128c3aa9c93c8dc9dd4aff8a245930bfb21b555a..0000000000000000000000000000000000000000 --- a/stub.org.openhab.core.scriptengine.action/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -sourceSets { - main { - java { - srcDir 'src/main/java' - } - } -} diff --git a/stub.org.openhab.core.scriptengine.action/src/main/java/org/openhab/core/scriptengine/action/ActionDoc.java b/stub.org.openhab.core.scriptengine.action/src/main/java/org/openhab/core/scriptengine/action/ActionDoc.java deleted file mode 100644 index 02434e332e6759ef1e2f3ceb0d43786888a3a3e2..0000000000000000000000000000000000000000 --- a/stub.org.openhab.core.scriptengine.action/src/main/java/org/openhab/core/scriptengine/action/ActionDoc.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.openhab.core.scriptengine.action; - -import java.lang.annotation.*; - -/** - * Stub to make projects build using ActionDoc. - * - * @author rschoene - Initial contribution - */ -@Target(ElementType.METHOD) -@Inherited -@Retention(RetentionPolicy.RUNTIME) -public @interface ActionDoc { - String text(); - - String returns() default ""; -} diff --git a/stub.org.openhab.core.scriptengine.action/src/main/java/org/openhab/core/scriptengine/action/ActionService.java b/stub.org.openhab.core.scriptengine.action/src/main/java/org/openhab/core/scriptengine/action/ActionService.java deleted file mode 100644 index 877f7b8091527b28a198e1f84027d92611198f51..0000000000000000000000000000000000000000 --- a/stub.org.openhab.core.scriptengine.action/src/main/java/org/openhab/core/scriptengine/action/ActionService.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.openhab.core.scriptengine.action; - -/** - * Stub to make projects build using ActionService. - * - * @author rschoene - Initial contribution - */ -public interface ActionService { - - /** - * returns the FQCN of the action class. - * - * @return the FQCN of the action class - */ - String getActionClassName(); - - /** - * Returns the action class itself - * - * @return the action class - */ - Class<?> getActionClass(); - -} diff --git a/stub.org.openhab.core.scriptengine.action/src/main/java/org/openhab/core/scriptengine/action/ParamDoc.java b/stub.org.openhab.core.scriptengine.action/src/main/java/org/openhab/core/scriptengine/action/ParamDoc.java deleted file mode 100644 index e9134a198d4c84f1cb76a663ccaeea2ce522242e..0000000000000000000000000000000000000000 --- a/stub.org.openhab.core.scriptengine.action/src/main/java/org/openhab/core/scriptengine/action/ParamDoc.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.openhab.core.scriptengine.action; - -import java.lang.annotation.*; - -/** - * Stub to make projects build using ParamDoc. - * - * @author rschoene - Initial contribution - */ -@Target(ElementType.PARAMETER) -@Inherited -@Retention(RetentionPolicy.RUNTIME) -public @interface ParamDoc { - String name(); - - String text() default ""; -} diff --git a/stub.org.openhab.core.scriptengine.action/src/main/resources/log4j2.xml b/stub.org.openhab.core.scriptengine.action/src/main/resources/log4j2.xml deleted file mode 100644 index 867ec439d0a32dcb5f8b3e2d0c7485d7d8da418c..0000000000000000000000000000000000000000 --- a/stub.org.openhab.core.scriptengine.action/src/main/resources/log4j2.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<Configuration> - <Appenders> - <Console name="Console"> - <PatternLayout pattern="%highlight{%d{HH:mm:ss.SSS} %-5level} %c{1.} - %msg%n"/> - </Console> - <RollingFile name="RollingFile" fileName="logs/eraser.log" - filePattern="logs/eraser-%i.log"> - <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n"/> - <Policies> - <OnStartupTriggeringPolicy/> - </Policies> - <DefaultRolloverStrategy max="20"/> - </RollingFile> - </Appenders> - <Loggers> - <Root level="debug"> - <AppenderRef ref="Console"/> - <AppenderRef ref="RollingFile"/> - </Root> - </Loggers> -</Configuration>