diff --git a/eraser-base/src/main/jastadd/MachineLearning.jrag b/eraser-base/src/main/jastadd/MachineLearning.jrag index 1a912dbd92455054b358ddfee00e94cb1c71ca43..d92c35ecaffb0b099b95db4618c7d0799d6a9b7a 100644 --- a/eraser-base/src/main/jastadd/MachineLearning.jrag +++ b/eraser-base/src/main/jastadd/MachineLearning.jrag @@ -85,6 +85,9 @@ aspect MachineLearning { * Checks the ML model for all necessary children. * @return true, if everything is alright. false otherwise */ + public abstract boolean MachineLearningModel.check(); + + @Override public boolean InternalMachineLearningModel.check() { boolean good = true; if (getOutputApplication() == null) { @@ -109,6 +112,11 @@ aspect MachineLearning { return good; } + @Override + public boolean ExternalMachineLearningModel.check() { + throw new UnsupportedOperationException("check not available for external ML models (yet)!"); + } + //--- mlKind --- inh String MachineLearningModel.mlKind(); eq MachineLearningRoot.getActivityRecognition().mlKind() = "ActivityRecognition"; diff --git a/eraser-base/src/main/jastadd/MachineLearning.relast b/eraser-base/src/main/jastadd/MachineLearning.relast index 0101465620191b440674f8d17933f3259c20ff42..5bfeaddda253ba37b4d730dfd3638e2c1b1b70d5 100644 --- a/eraser-base/src/main/jastadd/MachineLearning.relast +++ b/eraser-base/src/main/jastadd/MachineLearning.relast @@ -16,8 +16,8 @@ ManualChangeEvent : ChangeEvent ; abstract MachineLearningModel ::= ; ExternalMachineLearningModel : MachineLearningModel ; abstract InternalMachineLearningModel : MachineLearningModel ::= <OutputApplication:DoubleDoubleFunction> ; -rel InternalMachineLearningModel.RelevantItem* -> Item ; -rel InternalMachineLearningModel.TargetItem* -> Item ; +rel InternalMachineLearningModel.RelevantItem* <-> Item.RelevantInMachineLearningModel* ; +rel InternalMachineLearningModel.TargetItem* <-> Item.TargetInMachineLearningModel* ; abstract ItemPreference ::= ; rel ItemPreference.Item -> Item ; 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 829726efc57bec4f931de8c1e0e046a63a9a40eb..386e6f325dc4fde274507902843a096614e09043 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 @@ -6,19 +6,10 @@ import de.tudresden.inf.st.eraser.jastadd.model.Root; import de.tudresden.inf.st.eraser.util.MqttReceiver; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.fusesource.hawtbuf.Buffer; -import org.fusesource.hawtbuf.UTF8Buffer; -import org.fusesource.mqtt.client.*; +import org.fusesource.mqtt.client.QoS; import java.io.IOException; -import java.net.URI; -import java.util.Arrays; -import java.util.Objects; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; /** * Update an imported model by subscribing to MQTT topics. @@ -37,22 +28,22 @@ public class MQTTUpdater implements AutoCloseable { this.delegatee = new MqttReceiver(); } - public MQTTUpdater(Root model) throws IllegalArgumentException { + public MQTTUpdater(Root root) throws IllegalArgumentException { this(); - this.setModel(model); + this.setRoot(root); } /** - * Sets the model to update - * @param model the model to update + * Sets the model root to update + * @param root the model root to update */ - public void setModel(Root model) { - ExternalHost host = model.getMqttRoot().getHost(); + public void setRoot(Root root) { + ExternalHost host = root.getMqttRoot().getHost(); delegatee.setHost(host.getHostName(), host.getPort()); delegatee.setOnMessage((topicString, message) -> - model.getMqttRoot().resolveTopic(topicString).ifPresent(topic -> + root.getMqttRoot().resolveTopic(topicString).ifPresent(topic -> itemUpdate(topic.getItem(), message))); - delegatee.setTopicsForSubscription(model.getMqttRoot().getIncomingPrefix() + "#"); + delegatee.setTopicsForSubscription(root.getMqttRoot().getIncomingPrefix() + "#"); delegatee.setQoSForSubscription(QoS.AT_LEAST_ONCE); } 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 2ede06b3fef46cacc2dc3427ddc488f891b607e2..ee41ce60dcb3c50555095fa054af50e69e25b21b 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 @@ -9,7 +9,6 @@ import spark.Request; import spark.Response; import spark.Spark; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -26,12 +25,12 @@ import java.util.stream.Collectors; public class Application { private static final Logger logger = LogManager.getLogger(Application.class); - private final Root model; + private final Root root; private final Lock lock; private final Condition quitCondition; - private Application(Root model, Lock lock, Condition quitCondition) { - this.model = model; + private Application(Root root, Lock lock, Condition quitCondition) { + this.root = root; this.lock = lock; this.quitCondition = quitCondition; } @@ -43,19 +42,19 @@ public class Application { Spark.path("/activity", () -> { Spark.get("", - (request, response) -> wrapActivityList(model.getMachineLearningRoot().getActivityList()), + (request, response) -> wrapActivityList(root.getMachineLearningRoot().getActivityList()), mapper::writeValueAsString); Spark.get("/current", - (request, response) -> JavaUtils.ifPresentOrElseReturn(model.currentActivity(), + (request, response) -> JavaUtils.ifPresentOrElseReturn(root.currentActivity(), this::wrapActivity, () -> makeError(response, 204, "No activity recognized.")), mapper::writeValueAsString); Spark.put("/current", (request, response) -> { logger.info("request body: '{}', params: '{}', length={}", request.body(), request.params(), request.contentLength()); - if (!model.getMachineLearningRoot().hasActivityRecognition()) { + if (!root.getMachineLearningRoot().hasActivityRecognition()) { return makeError(response, 404, "No activity recognition model found"); } - MachineLearningModel activityRecognition = model.getMachineLearningRoot().getActivityRecognition(); + MachineLearningModel activityRecognition = root.getMachineLearningRoot().getActivityRecognition(); if (activityRecognition.canSetActivity()) { activityRecognition.setActivity(Integer.valueOf(request.body())); return "OK"; @@ -65,7 +64,7 @@ public class Application { }); Spark.get("/:identifier", (request, response) -> - JavaUtils.ifPresentOrElseReturn(model.resolveActivity(paramAsInt(request, "identifier")), + JavaUtils.ifPresentOrElseReturn(root.resolveActivity(paramAsInt(request, "identifier")), this::wrapActivity, () -> makeError(response, 404, "No activity for identifier " + request.params("identifier"))), mapper::writeValueAsString); @@ -73,11 +72,11 @@ public class Application { Spark.path("/events", () -> { Spark.get("", - (request, response) -> wrapChangeEventList(model.getMachineLearningRoot().getChangeEventList()), + (request, response) -> wrapChangeEventList(root.getMachineLearningRoot().getChangeEventList()), mapper::writeValueAsString); Spark.get("/:identifier", (request, response) -> - JavaUtils.ifPresentOrElseReturn(model.resolveChangeEvent(paramAsInt(request, "identifier")), + JavaUtils.ifPresentOrElseReturn(root.resolveChangeEvent(paramAsInt(request, "identifier")), this::wrapChangeEvent, () -> makeError(response, 404, "No event for identifier " + request.params("identifier"))), mapper::writeValueAsString); @@ -86,10 +85,10 @@ public class Application { Spark.path("/model", () -> { Spark.get("/full", (request, response) -> { response.type("text/plain"); - return model.prettyPrint(); + return root.prettyPrint(); }); Spark.get("/items", - (request, response) -> wrapItemList(model.items()), + (request, response) -> wrapItemList(root.getOpenHAB2Model().items()), mapper::writeValueAsString); Spark.put("/items/:identifier/state", (request, response) -> { logger.info("request body: '{}', params: '{}', length={}", request.body(), request.params(), request.contentLength()); @@ -124,13 +123,13 @@ public class Application { } private Object safeItemRoute(Request request, Response response, Function<Item, String> action) { - return JavaUtils.ifPresentOrElseReturn(model.resolveItem(request.params("identifier")), action, + return JavaUtils.ifPresentOrElseReturn(root.getOpenHAB2Model().resolveItem(request.params("identifier")), action, () -> makeError(response, 404, "Item '" + request.body() + "' not found")); } private String makeHistory(Item item, Response response) { response.type("text/plain"); - InfluxAdapter influxAdapter = model.getInfluxRoot().influxAdapter(); + InfluxAdapter influxAdapter = root.getInfluxRoot().influxAdapter(); influxAdapter.disableAsyncQuery(); List<? extends AbstractItemPoint> list; if (item.asColorItem() != null) { diff --git a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/DummyDataCreator.java b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/DummyDataCreator.java index 902f15bbd31fd7480bd413a239526b9576f68015..e548a6bc95012ba72675131395b9956f4a84466f 100644 --- a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/DummyDataCreator.java +++ b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/DummyDataCreator.java @@ -10,21 +10,21 @@ import de.tudresden.inf.st.eraser.util.JavaUtils; */ class DummyDataCreator { - private final Root model; + private final Root root; - DummyDataCreator(Root model) { - this.model = model; + DummyDataCreator(Root root) { + this.root = root; } private void addDummyActivitiesToModel() { - MachineLearningRoot mlRoot = model.getMachineLearningRoot(); + MachineLearningRoot mlRoot = root.getMachineLearningRoot(); mlRoot.addActivity(new Activity(1, "Sitting in armchair")); mlRoot.addActivity(new Activity(2, "Going to sleep")); mlRoot.addActivity(new Activity(3, "Entering house")); } private void addDummyChangeEventsToModel() { - MachineLearningRoot mlRoot = model.getMachineLearningRoot(); + MachineLearningRoot mlRoot = root.getMachineLearningRoot(); Item iris1 = getOrCreateColorItem("iris1", "Hue Iris 1"); Item go1 = getOrCreateColorItem("go1", "Hue Go 1"); Item go2 = getOrCreateColorItem("go2", "Hue Go 2"); @@ -40,7 +40,7 @@ class DummyDataCreator { } private Item getOrCreateColorItem(String name, String label) { - return model.resolveItem(name).orElseGet(() -> { + return root.getOpenHAB2Model().resolveItem(name).orElseGet(() -> { ColorItem result = new ColorItem(); result.setID(name); result.setLabel(label); @@ -53,7 +53,7 @@ class DummyDataCreator { private RecognitionEvent newRecognitionEvent(int identifier, long timestamp, int activityIdentifier, ChangedItem... changedItems) { RecognitionEvent result = new RecognitionEvent(); - JavaUtils.ifPresentOrElse(model.resolveActivity(activityIdentifier), result::setActivity, () -> { throw new RuntimeException("No activity found for identifier " + activityIdentifier); }); + JavaUtils.ifPresentOrElse(root.resolveActivity(activityIdentifier), result::setActivity, () -> { throw new RuntimeException("No activity found for identifier " + activityIdentifier); }); initChangeEvent(result, identifier, timestamp, changedItems); return result; } 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 b207dc5fb16ff63b5601b3bab92a1b33b29c1241..1bf2b06887fb97a4479c4dad1e1a6d05603ffe81 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 @@ -13,10 +13,7 @@ import de.tudresden.inf.st.eraser.feedbackloop.execute.ExecuteImpl; import de.tudresden.inf.st.eraser.feedbackloop.learner.LearnerHelper; import de.tudresden.inf.st.eraser.feedbackloop.learner.LearnerImpl; import de.tudresden.inf.st.eraser.feedbackloop.plan.PlanImpl; -import de.tudresden.inf.st.eraser.jastadd.model.DummyMachineLearningModel; -import de.tudresden.inf.st.eraser.jastadd.model.InfluxAdapter; -import de.tudresden.inf.st.eraser.jastadd.model.NeuralNetworkRoot; -import de.tudresden.inf.st.eraser.jastadd.model.Root; +import de.tudresden.inf.st.eraser.jastadd.model.*; import de.tudresden.inf.st.eraser.openhab2.OpenHab2Importer; import de.tudresden.inf.st.eraser.openhab2.mqtt.MQTTUpdater; import de.tudresden.inf.st.eraser.spark.Application; @@ -87,12 +84,14 @@ public class EraserStarter { logger.info("Starting ERASER"); boolean startRest = settings.rest.use; - Root model; + Root root; + OpenHAB2Model model; switch (settings.initModelWith) { case openhab: OpenHab2Importer importer = new OpenHab2Importer(); try { model = importer.importFrom(new URL(settings.openhab.url)); + root = model.getRoot(); } catch (MalformedURLException e) { logger.error("Could not parse URL {}", settings.openhab.url); logger.catching(e); @@ -104,7 +103,8 @@ public class EraserStarter { case load: default: try { - model = ParserUtils.load(settings.load.realURL()); + root = ParserUtils.load(settings.load.realURL()); + model = root.getOpenHAB2Model(); } catch (IOException | Parser.Exception e) { logger.error("Problems parsing the given file {}", settings.load.file); logger.catching(e); @@ -114,9 +114,10 @@ public class EraserStarter { } // initialize activity recognition + MachineLearningRoot machineLearningRoot = root.getMachineLearningRoot(); if (settings.activity.dummy) { logger.info("Using dummy activity recognition"); - model.getMachineLearningRoot().setActivityRecognition(DummyMachineLearningModel.createDefault()); + machineLearningRoot.setActivityRecognition(DummyMachineLearningModel.createDefault()); } else { logger.error("Reading activity recognition from file is not supported yet!"); // TODO @@ -125,7 +126,7 @@ public class EraserStarter { // initialize preference learning if (settings.preference.dummy) { logger.info("Using dummy preference learning"); - model.getMachineLearningRoot().setPreferenceLearning(DummyMachineLearningModel.createDefault()); + machineLearningRoot.setPreferenceLearning(DummyMachineLearningModel.createDefault()); } else { logger.info("Reading preference learning from file {}", settings.preference.file); Learner learner = new LearnerImpl(); @@ -146,7 +147,7 @@ public class EraserStarter { if (neuralNetwork == null) { logger.error("Could not create preference model, see possible previous errors."); } else { - model.getMachineLearningRoot().setPreferenceLearning(neuralNetwork); + machineLearningRoot.setPreferenceLearning(neuralNetwork); neuralNetwork.setOutputApplication(zeroToThree -> 33 * zeroToThree); JavaUtils.ifPresentOrElse( model.resolveItem(settings.preference.affectedItem), @@ -156,15 +157,15 @@ public class EraserStarter { } else { // loading was not successful logger.warn("Falling back to dummy preference learning"); - model.getMachineLearningRoot().setPreferenceLearning(DummyMachineLearningModel.createDefault()); + machineLearningRoot.setPreferenceLearning(DummyMachineLearningModel.createDefault()); } } - model.getMachineLearningRoot().getPreferenceLearning().connectItems(settings.preference.items); - if (!model.getMachineLearningRoot().getActivityRecognition().check()) { + machineLearningRoot.getPreferenceLearning().connectItems(settings.preference.items); + if (!machineLearningRoot.getActivityRecognition().check()) { logger.fatal("Invalid activity recognition!"); System.exit(1); } - if (!model.getMachineLearningRoot().getPreferenceLearning().check()) { + if (!machineLearningRoot.getPreferenceLearning().check()) { logger.fatal("Invalid preference learning!"); System.exit(1); } @@ -183,9 +184,9 @@ public class EraserStarter { analyze.setPlan(plan); plan.setExecute(execute); - analyze.setKnowledgeBase(model); - plan.setKnowledgeBase(model); - execute.setKnowledgeBase(model); + analyze.setKnowledgeBase(root); + plan.setKnowledgeBase(root); + execute.setKnowledgeBase(root); analyze.startAsThread(1, TimeUnit.SECONDS); } else { @@ -195,7 +196,7 @@ public class EraserStarter { if (settings.mqttUpdate) { logger.info("Starting MQTT updater"); Thread t = new Thread(() -> { - try (MQTTUpdater updater = new MQTTUpdater(model)) { + try (MQTTUpdater updater = new MQTTUpdater(root)) { updater.start(); updater.waitUntilReady(5, TimeUnit.SECONDS); lock.lock(); @@ -215,7 +216,7 @@ public class EraserStarter { // start REST-API in new thread logger.info("Starting REST server"); Thread t = new Thread( - () -> Application.start(settings.rest.port, model, settings.rest.createDummyMLData, lock, quitCondition), + () -> Application.start(settings.rest.port, root, settings.rest.createDummyMLData, lock, quitCondition), "REST-API"); t.setDaemon(true); t.start(); @@ -249,7 +250,7 @@ public class EraserStarter { if (analyze != null) { analyze.stop(); } - InfluxAdapter influxAdapter = model.getInfluxRoot().influxAdapter(); + InfluxAdapter influxAdapter = root.getInfluxRoot().influxAdapter(); if (influxAdapter != null) { try { influxAdapter.close(); 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 1fa433ad425df55e2d9c44b66c396786cddea84e..80d2346312445914306e6c224d1f9de1b766cf6d 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 @@ -61,7 +61,7 @@ public class ExecuteImpl implements Execute { } private void resolveOrLogError(String itemId, Consumer<? super Item> consumer) { - Optional<Item> optionalItem = knowledgeBase.resolveItem(itemId); + Optional<Item> optionalItem = knowledgeBase.getOpenHAB2Model().resolveItem(itemId); if (!optionalItem.isPresent()) { logger.warn("Could not resolve '{}' as an item.", itemId); } 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 19765c1e6df76fb2e2701b3a5eebb2e302728b6e..97c90b4aca7ec11535f96d1f3c184da92a471f44 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 @@ -86,10 +86,10 @@ public class Main { private static Root createModel(ArrayList<Double> normalizedInputs, int inputBiasNumber) { //create KB Model - Root model = Root.createEmptyRoot(); + Root root = Root.createEmptyRoot(); Group group = new Group(); group.setID("Group1"); - model.addGroup(group); + root.getOpenHAB2Model().addGroup(group); NumberItem monthItem = new NumberItem(); monthItem.setState(normalizedInputs.get(0)); @@ -122,7 +122,7 @@ public class Main { group.addItem(dayItem); group.addItem(hourItem); group.addItem(minuteItem); - return model; + return root; } /** @@ -130,7 +130,8 @@ public class Main { */ private static void createBrightnessNetwork(ArrayList<Double> all_weights, int inputBiasNumber, int hiddenlayernumber, int hiddenlayerbias, ArrayList<Double> normalizedInputsandOutput) { - Root model = createModel(normalizedInputsandOutput, inputBiasNumber); + Root root = createModel(normalizedInputsandOutput, inputBiasNumber); + OpenHAB2Model model = root.getOpenHAB2Model(); Item monthItem = model.resolveItem("month").orElseThrow( () -> new RuntimeException("Month not found")); Item dayItem = model.resolveItem("day").orElseThrow( @@ -220,8 +221,8 @@ public class Main { hiddenNeuron.connectTo(output3, weights.get(i + hiddenSum * 3)); } } - model.getMachineLearningRoot().setPreferenceLearning(nn); - System.out.println(model.prettyPrint()); + root.getMachineLearningRoot().setPreferenceLearning(nn); + System.out.println(root.prettyPrint()); List<String> output = new ArrayList<>(); Function<DoubleNumber, String> leafToString = classification -> Double.toString(classification.number);