From e07d8b07ec8f2f885dcee5cb3b2197e21bf4c5ef Mon Sep 17 00:00:00 2001 From: supertick Date: Sat, 18 Mar 2023 20:31:28 -0700 Subject: [PATCH 001/164] wip webxr vertx --- .../java/org/myrobotlab/service/WebXr.java | 52 ++++++++++++++++++- .../service/config/WebXrConfig.java | 30 ++++++++++- .../myrobotlab/service/data/Orientation.java | 1 + .../org/myrobotlab/service/data/Pose.java | 22 ++++++++ .../org/myrobotlab/service/data/Position.java | 43 +++++++++++++++ .../org/myrobotlab/vertx/ApiVerticle.java | 3 +- 6 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/myrobotlab/service/data/Pose.java create mode 100644 src/main/java/org/myrobotlab/service/data/Position.java diff --git a/src/main/java/org/myrobotlab/service/WebXr.java b/src/main/java/org/myrobotlab/service/WebXr.java index 80be870ff7..87df997800 100644 --- a/src/main/java/org/myrobotlab/service/WebXr.java +++ b/src/main/java/org/myrobotlab/service/WebXr.java @@ -1,11 +1,15 @@ package org.myrobotlab.service; +import java.util.HashMap; +import java.util.Map; + import org.myrobotlab.framework.Service; import org.myrobotlab.logging.Level; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.logging.LoggingFactory; -import org.myrobotlab.service.config.ServiceConfig; +import org.myrobotlab.math.MapperSimple; import org.myrobotlab.service.config.WebXrConfig; +import org.myrobotlab.service.data.Pose; import org.slf4j.Logger; public class WebXr extends Service { @@ -17,7 +21,50 @@ public class WebXr extends Service { public WebXr(String n, String id) { super(n, id); } + + public Pose publishPose(Pose pose) { + log.warn("publishPose {}", pose); + System.out.println(pose.toString()); + + // process mappings config into joint angles + Map map = new HashMap<>(); + + WebXrConfig c = (WebXrConfig)config; + String path = String.format("%s.orientation.roll", pose.name); + if (c.mappings.containsKey(path)) { + Map mapper = c.mappings.get(path); + for (String name: mapper.keySet()) { + map.put(name, mapper.get(name).calcOutput(pose.orientation.roll)); + } + } + + path = String.format("%s.orientation.pitch", pose.name); + if (c.mappings.containsKey(path)) { + Map mapper = c.mappings.get(path); + for (String name: mapper.keySet()) { + map.put(name, mapper.get(name).calcOutput(pose.orientation.pitch)); + } + } + + path = String.format("%s.orientation.yaw", pose.name); + if (c.mappings.containsKey(path)) { + Map mapper = c.mappings.get(path); + for (String name: mapper.keySet()) { + map.put(name, mapper.get(name).calcOutput(pose.orientation.yaw)); + } + } + + invoke("publishJointAngles", map); + + return pose; + } + + // TODO publishQuaternion + public Map publishJointAngles(Map map){ + return map; + } + public static void main(String[] args) { try { @@ -28,6 +75,9 @@ public static void main(String[] args) { // webgui.setSsl(true); webgui.autoStartBrowser(false); webgui.startService(); + Runtime.start("vertx", "Vertx"); + InMoov2 i01 = (InMoov2)Runtime.start("i01", "InMoov2"); + i01.startPeer("simulator"); } catch (Exception e) { diff --git a/src/main/java/org/myrobotlab/service/config/WebXrConfig.java b/src/main/java/org/myrobotlab/service/config/WebXrConfig.java index ebe4ae4c9e..c97615440b 100644 --- a/src/main/java/org/myrobotlab/service/config/WebXrConfig.java +++ b/src/main/java/org/myrobotlab/service/config/WebXrConfig.java @@ -1,9 +1,37 @@ package org.myrobotlab.service.config; +import java.util.HashMap; +import java.util.Map; + +import org.myrobotlab.math.MapperSimple; + public class WebXrConfig extends ServiceConfig { public Integer port = 8888; public boolean autoStartBrowser = true; - public boolean enableMdns = false; + /** + * range and name mappings for orientation and position + * controller name | servo name | mapping + */ + public Map> mappings = new HashMap<>(); + + public WebXrConfig() { + + Map map = new HashMap<>(); + map.put("i01.head.rollNeck", new MapperSimple(-3.14, 3.14, -90, 270)); + mappings.put("head.orientation.roll", map); + + map = new HashMap<>(); + map.put("i01.head.rothead", new MapperSimple(-3.14, 3.14, -90, 270)); + mappings.put("head.orientation.yaw", map); + + map = new HashMap<>(); + map.put("i01.head.neck", new MapperSimple(-3.14, 3.14, -90, 270)); + mappings.put("head.orientation.pitch", map); + + } + } + + diff --git a/src/main/java/org/myrobotlab/service/data/Orientation.java b/src/main/java/org/myrobotlab/service/data/Orientation.java index b2d5d5658e..b7df4102dc 100644 --- a/src/main/java/org/myrobotlab/service/data/Orientation.java +++ b/src/main/java/org/myrobotlab/service/data/Orientation.java @@ -11,6 +11,7 @@ public class Orientation { public Double roll = null; public Double pitch = null; public Double yaw = null; + public String src = null; // default constructor (values will be null until set) public Orientation() { diff --git a/src/main/java/org/myrobotlab/service/data/Pose.java b/src/main/java/org/myrobotlab/service/data/Pose.java new file mode 100644 index 0000000000..767d9be81d --- /dev/null +++ b/src/main/java/org/myrobotlab/service/data/Pose.java @@ -0,0 +1,22 @@ +package org.myrobotlab.service.data; + +public class Pose { + public String name = null; + public Long ts = null; + public Position position = null; + public Orientation orientation = null; + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(String.format("name:%s", name)); + if (position != null) { + sb.append(String.format(" x:%.2f y:%.2f z:%.2f", position.x, position.y, position.z)); + } + if (orientation != null) { + sb.append(String.format(" roll:%.2f pitch:%.2f yaw:%.2f", orientation.roll, orientation.pitch, orientation.yaw)); + } + return sb.toString(); + } + + +} diff --git a/src/main/java/org/myrobotlab/service/data/Position.java b/src/main/java/org/myrobotlab/service/data/Position.java new file mode 100644 index 0000000000..83fe574a44 --- /dev/null +++ b/src/main/java/org/myrobotlab/service/data/Position.java @@ -0,0 +1,43 @@ +package org.myrobotlab.service.data; + +public class Position { + + public Double x; + public Double y; + public Double z; + public String src; + + public Position(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Position(double x, double y) { + this.x = x; + this.y = y; + } + + public Position(int x, int y, int z) { + this.x = (double) x; + this.y = (double) y; + this.z = (double) z; + } + + public Position(int x, int y) { + this.x = (double) x; + this.y = (double) y; + } + + public Position(float x, float y, float z) { + this.x = (double) x; + this.y = (double) y; + this.z = (double) z; + } + + public Position(float x, float y) { + this.x = (double) x; + this.y = (double) y; + } + +} diff --git a/src/main/java/org/myrobotlab/vertx/ApiVerticle.java b/src/main/java/org/myrobotlab/vertx/ApiVerticle.java index 3f101095d0..6b3ca595c7 100644 --- a/src/main/java/org/myrobotlab/vertx/ApiVerticle.java +++ b/src/main/java/org/myrobotlab/vertx/ApiVerticle.java @@ -55,7 +55,8 @@ public void start() throws Exception { // static file routing //StaticHandler root = StaticHandler.create("src/main/resources/resource/Vertx/app"); - StaticHandler root = StaticHandler.create("src/main/resources/resource/Vertx/app"); + // StaticHandler root = StaticHandler.create("src/main/resources/resource/Vertx/app"); + StaticHandler root = StaticHandler.create("../robotlab-x-app/build/"); root.setCachingEnabled(false); root.setDirectoryListing(true); root.setIndexPage("index.html"); From 325cd4e5706717f6b34f0677e00c84fabf7d3a59 Mon Sep 17 00:00:00 2001 From: grog Date: Sat, 29 Apr 2023 08:00:05 -0700 Subject: [PATCH 002/164] fixed predicates --- src/main/java/org/myrobotlab/service/ProgramAB.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/myrobotlab/service/ProgramAB.java b/src/main/java/org/myrobotlab/service/ProgramAB.java index 38c6009e57..a63e50fef1 100644 --- a/src/main/java/org/myrobotlab/service/ProgramAB.java +++ b/src/main/java/org/myrobotlab/service/ProgramAB.java @@ -609,13 +609,21 @@ public void reloadSession(String userName, String botName) throws IOException { } } + /** + * Get the current session predicates + * @return + */ + public Map getPredicates() { + return getPredicates(currentUserName, currentBotName); + } + /** * Get all current predicates names and their values * for the current session * @return */ - public Map getPredicates() { - Session session = getSession(); + public Map getPredicates(String userName, String botName) { + Session session = getSession(userName, botName); if (session != null) { return session.getPredicates(); } From bc0b42fa75ed9d5bf5fe7f1703c487aade32925c Mon Sep 17 00:00:00 2001 From: grog Date: Sat, 29 Apr 2023 08:22:40 -0700 Subject: [PATCH 003/164] removed translate --- .../org/myrobotlab/service/ProgramAB.java | 27 +++++-------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/myrobotlab/service/ProgramAB.java b/src/main/java/org/myrobotlab/service/ProgramAB.java index a63e50fef1..b1ffd24371 100644 --- a/src/main/java/org/myrobotlab/service/ProgramAB.java +++ b/src/main/java/org/myrobotlab/service/ProgramAB.java @@ -42,7 +42,6 @@ import org.myrobotlab.service.interfaces.SpeechSynthesis; import org.myrobotlab.service.interfaces.TextListener; import org.myrobotlab.service.interfaces.TextPublisher; -import org.myrobotlab.service.interfaces.Translator; import org.myrobotlab.service.interfaces.UtteranceListener; import org.myrobotlab.service.interfaces.UtterancePublisher; import org.slf4j.Logger; @@ -61,7 +60,7 @@ * */ public class ProgramAB extends Service - implements Translator, TextListener, TextPublisher, LocaleProvider, LogPublisher, ProgramABListener, UtterancePublisher, UtteranceListener, ResponsePublisher { + implements TextListener, TextPublisher, LocaleProvider, LogPublisher, ProgramABListener, UtterancePublisher, UtteranceListener, ResponsePublisher { /** * default file name that aiml categories comfing from matching a learnf tag @@ -611,19 +610,20 @@ public void reloadSession(String userName, String botName) throws IOException { /** * Get the current session predicates + * * @return */ public Map getPredicates() { return getPredicates(currentUserName, currentBotName); } - + /** - * Get all current predicates names and their values - * for the current session + * Get all current predicates names and their values for the current session + * * @return */ public Map getPredicates(String userName, String botName) { - Session session = getSession(userName, botName); + Session session = getSession(userName, botName); if (session != null) { return session.getPredicates(); } @@ -1474,19 +1474,4 @@ public Utterance publishUtterance(Utterance utterance) { return utterance; } - @Override - public String translate(String text) { - Response response = getResponse(text); - return response.msg; - } - - @Override - public String translate(String text, String fromLang, String toLang) { - // FIXME implement - same mapping that current inmoov has ? en-US etc .. - // although all of those are localizations - // not languages only the 1st part is "en" in en-US - // return getResponse(text) - return null; - } - } From b1a6e1309c280a5340b30a16602d9da84b5e1429 Mon Sep 17 00:00:00 2001 From: grog Date: Sat, 29 Apr 2023 08:29:47 -0700 Subject: [PATCH 004/164] added to translator interface --- .../myrobotlab/service/interfaces/Translator.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/org/myrobotlab/service/interfaces/Translator.java b/src/main/java/org/myrobotlab/service/interfaces/Translator.java index b2ab803d32..00722cc2ee 100644 --- a/src/main/java/org/myrobotlab/service/interfaces/Translator.java +++ b/src/main/java/org/myrobotlab/service/interfaces/Translator.java @@ -26,4 +26,17 @@ public interface Translator { * @return */ public String translate(String text, String fromLang, String toLang); + + /** + * Set the source language type en, es, fr... + * @param toLang + */ + public void setToLang(String toLang); + + /** + * Set the target language type en, es, fr... + * @param fromLang + */ + public void setFromLang(String fromLang); + } From 363bf3ae2e3d6e04317467aca533cf151ff50928 Mon Sep 17 00:00:00 2001 From: grog Date: Sat, 29 Apr 2023 09:37:16 -0700 Subject: [PATCH 005/164] added to from translate methods --- .../java/org/myrobotlab/service/AzureTranslator.java | 10 ++++++++++ .../org/myrobotlab/service/interfaces/Translator.java | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/myrobotlab/service/AzureTranslator.java b/src/main/java/org/myrobotlab/service/AzureTranslator.java index ab94bf8a70..a4f5c08f39 100644 --- a/src/main/java/org/myrobotlab/service/AzureTranslator.java +++ b/src/main/java/org/myrobotlab/service/AzureTranslator.java @@ -210,4 +210,14 @@ public static void main(String[] args) throws Exception { } } + @Override + public void setToLanguage(String to) { + this.to = to; + } + + @Override + public void setFromLanguage(String from) { + this.from = from; + } + } diff --git a/src/main/java/org/myrobotlab/service/interfaces/Translator.java b/src/main/java/org/myrobotlab/service/interfaces/Translator.java index 00722cc2ee..5222a1f7ec 100644 --- a/src/main/java/org/myrobotlab/service/interfaces/Translator.java +++ b/src/main/java/org/myrobotlab/service/interfaces/Translator.java @@ -31,12 +31,12 @@ public interface Translator { * Set the source language type en, es, fr... * @param toLang */ - public void setToLang(String toLang); + public void setToLanguage(String toLang); /** * Set the target language type en, es, fr... * @param fromLang */ - public void setFromLang(String fromLang); + public void setFromLanguage(String fromLang); } From 4769ca2d87e46769437476fad54cccf473f0ee31 Mon Sep 17 00:00:00 2001 From: grog Date: Sun, 30 Apr 2023 07:20:03 -0700 Subject: [PATCH 006/164] sorted and removed a few not used inmoov methods --- .../java/org/myrobotlab/service/InMoov2.java | 439 +++++++++--------- .../service/config/InMoov2Config.java | 2 +- 2 files changed, 228 insertions(+), 213 deletions(-) diff --git a/src/main/java/org/myrobotlab/service/InMoov2.java b/src/main/java/org/myrobotlab/service/InMoov2.java index 9a1e34b06d..336bb29bf8 100644 --- a/src/main/java/org/myrobotlab/service/InMoov2.java +++ b/src/main/java/org/myrobotlab/service/InMoov2.java @@ -17,6 +17,7 @@ import org.myrobotlab.framework.Registration; import org.myrobotlab.framework.Service; import org.myrobotlab.framework.Status; +import org.myrobotlab.framework.TimeoutException; import org.myrobotlab.framework.interfaces.ServiceInterface; import org.myrobotlab.io.FileIO; import org.myrobotlab.logging.Level; @@ -182,16 +183,16 @@ public static void main(String[] args) { protected List configList; + /** + * Configuration from runtime has started. This is when runtime starts + * processing a configuration set for the first time since inmoov was started + */ + boolean configStarted = false; + String currentConfigurationName = "default"; transient SpeechRecognizer ear; - // - // public void onStartConfig(String configName) { - // log.info("onStartConfig"); - // - // } - transient Tracking eyesTracking; // waiting controable threaded gestures we warn user @@ -225,10 +226,10 @@ public static void main(String[] args) { boolean mute = false; - // transient JMonkeyEngine simulator; - transient OpenCV opencv; + // transient JMonkeyEngine simulator; + transient Pir pir; transient Python python; @@ -245,7 +246,6 @@ public static void main(String[] args) { public InMoov2(String n, String id) { super(n, id); - Runtime.getInstance().attachServiceLifeCycleListener(getName()); } public void addTextListener(TextListener service) { @@ -266,7 +266,7 @@ public ServiceConfig apply(ServiceConfig c) { setLocale(getSupportedLocale(Runtime.getInstance().getLocale().toString())); } - init(); + loadInitScripts(); if (config.loadGestures) { loadGestures(); @@ -284,58 +284,6 @@ public ServiceConfig apply(ServiceConfig c) { return c; } - /** - * execute python scripts in the init directory on startup of the service - * - * @throws IOException - */ - public void init() throws IOException { - invoke("publishEvent", "INIT"); - loadScripts(getResourceDir() + fs + "init"); - } - - /** - * Generalized directory python script loading method - * - * @param directory - * @throws IOException - */ - public void loadScripts(String directory) throws IOException { - File dir = new File(directory); - - if (!dir.exists() || !dir.isDirectory()) { - invoke("publishEvent", "LOAD SCRIPTS ERROR"); - return; - } - - String[] extensions = { "py" }; - - for (String extension : extensions) { - File[] files = dir.listFiles(new FilenameFilter() { - public boolean accept(File dir, String name) { - return name.toLowerCase().endsWith("." + extension); - } - }); - - if (files != null) { - for (File file : files) { - Python p = (Python) Runtime.start("python", "Python"); - if (p != null) { - p.execFile(file.getAbsolutePath()); - } - } - } - } - } - - // FIXME FIXME !!! THIS IS A MESS !! - public void applyConfig() { - super.apply(); - log.error("applyConfig()"); - // always getResponse ! - speak("InMoov apply config"); - } - @Override public void attachTextListener(String name) { addListener("publishText", name); @@ -730,10 +678,6 @@ public InMoov2Hand getRightHand() { return (InMoov2Hand) getPeer("rightHand"); } - public Simulator getSimulator() { - return (Simulator) getPeer("simulator"); - } - /** * matches on language only not variant expands language match to full InMoov2 * bot locale @@ -777,6 +721,15 @@ public void halfSpeed() { sendToPeer("torso", "setSpeed", 20.0, 20.0, 20.0); } + /** + * execute python scripts in the init directory on startup of the service + * + * @throws IOException + */ + public void loadInitScripts() throws IOException { + loadScripts(getResourceDir() + fs + "init"); + } + public boolean isCameraOn() { if (opencv != null) { if (opencv.isCapturing()) { @@ -840,6 +793,40 @@ public boolean loadGestures(String directory) { return true; } + /** + * Generalized directory python script loading method + * + * @param directory + * @throws IOException + */ + public void loadScripts(String directory) throws IOException { + File dir = new File(directory); + + if (!dir.exists() || !dir.isDirectory()) { + invoke("publishEvent", "LOAD SCRIPTS ERROR"); + return; + } + + String[] extensions = { "py" }; + + for (String extension : extensions) { + File[] files = dir.listFiles(new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.toLowerCase().endsWith("." + extension); + } + }); + + if (files != null) { + for (File file : files) { + Python p = (Python) Runtime.start("python", "Python"); + if (p != null) { + p.execFile(file.getAbsolutePath()); + } + } + } + } + } + public void moveArm(String which, Double bicep, Double rotate, Double shoulder, Double omoplate) { invoke("publishMoveArm", which, bicep, rotate, shoulder, omoplate); } @@ -880,21 +867,21 @@ public void moveHead(Integer neck, Integer rothead, Integer rollNeck) { moveHead((double) neck, (double) rothead, null, null, null, (double) rollNeck); } - public void moveHeadBlocking(Double neck, Double rothead) { + public void moveHeadBlocking(Double neck, Double rothead) throws InterruptedException, TimeoutException { moveHeadBlocking(neck, rothead, null); } - public void moveHeadBlocking(Double neck, Double rothead, Double rollNeck) { + public void moveHeadBlocking(Double neck, Double rothead, Double rollNeck) throws InterruptedException, TimeoutException { moveHeadBlocking(neck, rothead, null, null, null, rollNeck); } - public void moveHeadBlocking(Double neck, Double rothead, Double eyeX, Double eyeY, Double jaw) { + public void moveHeadBlocking(Double neck, Double rothead, Double eyeX, Double eyeY, Double jaw) throws InterruptedException, TimeoutException { moveHeadBlocking(neck, rothead, eyeX, eyeY, jaw, null); } - public void moveHeadBlocking(Double neck, Double rothead, Double eyeX, Double eyeY, Double jaw, Double rollNeck) { - // the "right" way - sendToPeer("head", "moveToBlocking", neck, rothead, eyeX, eyeY, jaw, rollNeck); + public void moveHeadBlocking(Double neck, Double rothead, Double eyeX, Double eyeY, Double jaw, Double rollNeck) throws InterruptedException, TimeoutException { + sendBlocking(getPeerName("head"), "moveToBlocking", neck, rothead, eyeX, eyeY, jaw, rollNeck); + log.info("here"); } public void moveLeftArm(Double bicep, Double rotate, Double shoulder, Double omoplate) { @@ -962,7 +949,7 @@ public void onCreated(String fullname) { public void onFinishedConfig(String configName) { log.info("onFinishedConfig"); // invoke("publishEvent", "configFinished"); - invoke("publishEvent", "CONFIG LOADED"); + invoke("publishFinishedConfig", configName); } public void onGestureStatus(Status status) { @@ -988,8 +975,9 @@ public void onJointAngles(Map angleMap) { @Override public void onJoystickInput(JoystickData input) throws Exception { - // TODO Auto-generated method stub - + // TODO timer ? to test and not send an event + // switches to manual control ? + invoke("publishEvent", "joystick"); } public String onNewState(String state) { @@ -1010,6 +998,7 @@ public String onNewState(String state) { } public OpenCVData onOpenCVData(OpenCVData data) { + // FIXME - publish event with or without data ? String file reference return data; } @@ -1024,7 +1013,7 @@ public void onPirOn() { led.blue = 150; led.count = 5; led.interval = 500; - + // FIXME flash on config.flashOnBoot invoke("publishFlash"); // pirOn event vs wake event invoke("publishEvent", "WAKE"); @@ -1053,8 +1042,6 @@ public void onRegistered(Registration registration) { @Override public void onReleased(String name) { - // TODO Auto-generated method stub - } // I THINK THIS IS GOOD (good simple one)- need more info though @@ -1070,6 +1057,16 @@ public boolean onSense(boolean b) { return b; } + /** + * runtime re-publish relay + * + * @param configName + */ + public void onStartConfig(String configName) { + log.info("onStartConfig"); + invoke("publishStartConfig", configName); + } + /** * Part of service life cycle - a new servo has been started * @@ -1079,10 +1076,11 @@ public boolean onSense(boolean b) { */ @Override public void onStarted(String name) { - log.info("{} started", name); + InMoov2Config c = (InMoov2Config) config; + + log.info("onStarted {}", name); try { - InMoov2Config c = (InMoov2Config) config; Runtime runtime = Runtime.getInstance(); log.info("onStarted {}", name); @@ -1092,83 +1090,113 @@ public void onStarted(String name) { // } String peerKey = getPeerKey(name); - if (peerKey != null) { - invoke("publishEvent", "STARTED " + peerKey); - } - - String actualName = getPeerName("ear"); - if (actualName.equals(name)) { - AbstractSpeechRecognizer ear = (AbstractSpeechRecognizer) Runtime.getService(actualName); - ear.attachTextListener(getPeerName("chatBot")); - } - - actualName = getPeerName("mouth"); - if (actualName.equals(name)) { - AbstractSpeechSynthesis mouth = (AbstractSpeechSynthesis) Runtime.getService(actualName); - mouth.attachSpeechListener(getPeerName("ear")); - } - - actualName = getPeerName("chatBot"); - if (actualName.equals(name)) { - ProgramAB chatBot = (ProgramAB) Runtime.getService(actualName); - chatBot.attachTextListener(getPeerName("htmlFilter")); - startPeer("htmlFilter"); - } - - actualName = getPeerName("htmlFilter"); - if (actualName.equals(name)) { - TextPublisher htmlFilter = (TextPublisher) Runtime.getService(actualName); - htmlFilter.attachTextListener(getPeerName("mouth")); - } - - // FIXME - this is a much better way to do it than the code above - // the types are not exposed, its not using a local reference of the - // service - // its remote compatible, and it uses the peers actual name - it does not - // break easily, - // allows user modification and subscribing to topics for extensibility, - // no NPEs uses messaging, and follows mrl pub/sub conventions - above - // code should be - // refactored accordingly. - actualName = getPeerName("head"); - if (actualName.equals(name)) { - addListener("publishMoveHead", actualName); - } - - actualName = getPeerName("torso"); - if (actualName.equals(name)) { - addListener("publishMoveTorso", actualName); - } - - // mapping a channel to a single end point depending on peer - actualName = getPeerName("leftHand"); - if (actualName.equals(name)) { - addListener("publishMoveLeftHand", actualName, "onMoveHand"); - } - - // mapping a channel to a single end point depending on peer - actualName = getPeerName("rightHand"); - if (actualName.equals(name)) { - addListener("publishMoveRightHand", actualName, "onMoveHand"); + if (peerKey == null) { + // service not a peer + return; } - // mapping a channel to a single end point depending on peer - actualName = getPeerName("leftArm"); - if (actualName.equals(name)) { - addListener("publishMoveLeftArm", actualName, "onMoveArm"); + if (runtime.isProcessingConfig() && !configStarted) { + invoke("publishEvent", "CONFIG STARTED " + runtime.getConfigName()); + configStarted = true; } - // mapping a channel to a single end point depending on peer - actualName = getPeerName("rightArm"); - if (actualName.equals(name)) { - addListener("publishMoveRightArm", actualName, "onMoveArm"); + invoke("publishEvent", "STARTED " + peerKey); + + switch (peerKey) { + case "audioPlayer": + break; + case "chatBot": + ProgramAB chatBot = (ProgramAB) Runtime.getService(name); + chatBot.attachTextListener(getPeerName("htmlFilter")); + startPeer("htmlFilter"); + break; + case "controller3": + break; + case "controller4": + break; + case "ear": + AbstractSpeechRecognizer ear = (AbstractSpeechRecognizer) Runtime.getService(name); + ear.attachTextListener(getPeerName("chatBot")); + break; + case "eyeTracking": + break; + case "fsm": + break; + case "gpt3": + break; + case "head": + addListener("publishMoveHead", name); + break; + case "headTracking": + break; + case "htmlFilter": + TextPublisher htmlFilter = (TextPublisher) Runtime.getService(name); + htmlFilter.attachTextListener(getPeerName("mouth")); + break; + case "imageDisplay": + break; + case "leap": + break; + case "left": + break; + case "leftArm": + addListener("publishMoveLeftArm", name, "onMoveArm"); + break; + case "leftHand": + addListener("publishMoveLeftHand", name, "onMoveHand"); + break; + case "mouth": + mouth = (AbstractSpeechSynthesis) Runtime.getService(name); + mouth.attachSpeechListener(getPeerName("ear")); + break; + case "mouthControl": + break; + case "neoPixel": + break; + case "opencv": + subscribeTo(name, "publishOpenCVData"); + break; + case "openni": + break; + case "openWeatherMap": + break; + case "pid": + break; + case "pir": + break; + case "random": + break; + case "right": + break; + case "rightArm": + addListener("publishMoveRightArm", name, "onMoveArm"); + break; + case "rightHand": + addListener("publishMoveRightHand", name, "onMoveHand"); + break; + case "servoMixer": + break; + case "simulator": + break; + case "torso": + addListener("publishMoveTorso", name); + break; + case "ultrasonicRight": + break; + case "ultrasonicLeft": + break; + default: + log.warn("unknown peer %s not hanled in onStarted", peerKey); + break; } + // type processing for Servo ServiceInterface si = Runtime.getService(name); if ("Servo".equals(si.getSimpleName())) { log.info("sending setAutoDisable true to {}", name); - send(name, "setAutoDisable", true); - // ServoControl sc = (ServoControl)Runtime.getService(name); + // send(name, "setAutoDisable", true); + Servo servo = (Servo) Runtime.getService(name); + servo.setAutoDisable(true); } } catch (Exception e) { log.error("onStarted threw", e); @@ -1178,6 +1206,7 @@ public void onStarted(String name) { @Override public void onStopped(String name) { // using release peer for peer releasing + // FIXME - auto remove subscriptions of peers? } @Override @@ -1222,6 +1251,31 @@ public void powerUp() { python.execMethod("power_up"); } + /** + * easy utility to publishMessage + * + * @param name + * @param method + * @param data + */ + public void publish(String name, String method, Object... data) { + Message msg = Message.createMessage(getName(), name, method, data); + invoke("publishMessage", msg); + } + + public String publishStartConfig(String configName) { + info("config %s started", configName); + invoke("publishEvent", "CONFIG STARTED " + configName); + return configName; + } + + public String publishFinishedConfig(String configName) { + info("config %s finished", configName); + invoke("publishEvent", "CONFIG LOADED " + configName); + + return configName; + } + /** * "re"-publishing runtime config list, because I don't want to fix the js * subscribeTo :P @@ -1243,28 +1297,6 @@ public String publishEvent(String event) { return String.format("SYSTEM_EVENT %s", event); } - /** - * A more extensible interface point than publishEvent - * - * @param msg - * @return - */ - public Message publishMessage(Message msg) { - return msg; - } - - /** - * easy utility to publishMessage - * - * @param name - * @param method - * @param data - */ - public void publish(String name, String method, Object... data) { - Message msg = Message.createMessage(getName(), name, method, data); - invoke("publishMessage", msg); - } - /** * used to configure a flashing event - could use configuration to signal * different colors and states @@ -1286,6 +1318,17 @@ public String publishHeartbeat() { return getName(); } + /** + * A more extensible interface point than publishEvent + * FIXME - create interface for this + * + * @param msg + * @return + */ + public Message publishMessage(Message msg) { + return msg; + } + public HashMap publishMoveArm(String which, Double bicep, Double rotate, Double shoulder, Double omoplate) { HashMap map = new HashMap<>(); map.put("bicep", bicep); @@ -1388,7 +1431,7 @@ public String publishText(String text) { public void releasePeer(String peerKey) { super.releasePeer(peerKey); if (peerKey != null) { - invoke("publishEvent","STOPPED " + peerKey); + invoke("publishEvent", "STOPPED " + peerKey); } } @@ -1828,16 +1871,6 @@ public SpeechSynthesis startMouth() { return mouth; } - public void startMouthControl() { - speakBlocking(get("STARTINGMOUTHCONTROL")); - mouthControl = (MouthControl) startPeer("mouthControl"); - InMoov2Head head = getHead(); - if (head != null) { - mouthControl.attach(head.getPeer("jaw")); - } - mouthControl.attach(getPeer("mouth")); - } - // FIXME - universal (good) way of handling all exceptions - ie - reporting // back to the user the problem in a short concise way but have // expandable detail in appropriate places @@ -1857,19 +1890,13 @@ public ServiceInterface startPeer(String peer) { @Override public void startService() { super.startService(); + InMoov2Config c = (InMoov2Config) config; Runtime runtime = Runtime.getInstance(); - // InMoov2 has a huge amount of peers + // get service start and release life cycle events + runtime.attachServiceLifeCycleListener(getName()); - // by default all servos will auto-disable - // Servo.setAutoDisableDefault(true); //until peer servo services for - // InMoov2 have the auto disable behavior, we should keep this - - // same as created in runtime - send asyc message to all - // registered services, this service has started - // find all servos - set them all to autoDisable(true) - // onStarted(name) will handle all future created servos List services = Runtime.getServices(); for (ServiceInterface si : services) { if ("Servo".equals(si.getSimpleName())) { @@ -1877,34 +1904,22 @@ public void startService() { } } - // REALLY NEEDS TO BE CLEANED UP - no direct references - // "publish" scripts which should be executed :( - // python = (Python) startPeer("python"); - // python = (Python) Runtime.start("python", "Python"); <- BAD !!!! - // load(locale.getTag()); WTH ? - // get events of new services and shutdown - Runtime r = Runtime.getInstance(); - subscribe(r.getName(), "shutdown"); - subscribe(r.getName(), "publishConfigList"); - - // FIXME - Framework should auto-magically auto-start peers AFTER - // construction - unless explicitly told not to - // peers to start on construction - // imageDisplay = (ImageDisplay) startPeer("imageDisplay"); - + subscribe("runtime", "shutdown"); + // power up loopback subscription + addListener(getName(), "powerUp"); + + + subscribe("runtime", "publishConfigList"); if (runtime.isProcessingConfig()) { invoke("publishEvent", "configStarted"); } + subscribe("runtime", "publishStartConfig"); + subscribe("runtime", "publishFinishedConfig"); - // power up loopback subscription - addListener(getName(), "powerUp"); - + // chatbot getresponse attached to publishEvent addListener("publishEvent", getPeerName("chatBot"), "getResponse"); - - // for begin and end of processing config ? - // subscribe("runtime", "publishStartConfig"); - subscribe("runtime", "publishFinishedConfig"); + try { // copy config if it doesn't already exist diff --git a/src/main/java/org/myrobotlab/service/config/InMoov2Config.java b/src/main/java/org/myrobotlab/service/config/InMoov2Config.java index ba96d1a2d0..318e289239 100644 --- a/src/main/java/org/myrobotlab/service/config/InMoov2Config.java +++ b/src/main/java/org/myrobotlab/service/config/InMoov2Config.java @@ -103,7 +103,7 @@ public Plan getDefault(Plan plan, String name) { // peers FIXME global opencv addDefaultPeerConfig(plan, name, "audioPlayer", "AudioFile", true); - addDefaultPeerConfig(plan, name, "chatBot", "ProgramAB", false); + addDefaultPeerConfig(plan, name, "chatBot", "ProgramAB", true); addDefaultPeerConfig(plan, name, "controller3", "Arduino", false); addDefaultPeerConfig(plan, name, "controller4", "Arduino", false); addDefaultPeerConfig(plan, name, "ear", "WebkitSpeechRecognition", false); From 265cf1e97317eae58496154446f914ffc7b28a26 Mon Sep 17 00:00:00 2001 From: grog Date: Sun, 30 Apr 2023 07:24:06 -0700 Subject: [PATCH 007/164] sort only --- .../java/org/myrobotlab/service/InMoov2.java | 132 +++++++++--------- .../service/config/InMoov2Config.java | 28 ++-- 2 files changed, 80 insertions(+), 80 deletions(-) diff --git a/src/main/java/org/myrobotlab/service/InMoov2.java b/src/main/java/org/myrobotlab/service/InMoov2.java index 9a1e34b06d..95a34a3bb6 100644 --- a/src/main/java/org/myrobotlab/service/InMoov2.java +++ b/src/main/java/org/myrobotlab/service/InMoov2.java @@ -284,50 +284,6 @@ public ServiceConfig apply(ServiceConfig c) { return c; } - /** - * execute python scripts in the init directory on startup of the service - * - * @throws IOException - */ - public void init() throws IOException { - invoke("publishEvent", "INIT"); - loadScripts(getResourceDir() + fs + "init"); - } - - /** - * Generalized directory python script loading method - * - * @param directory - * @throws IOException - */ - public void loadScripts(String directory) throws IOException { - File dir = new File(directory); - - if (!dir.exists() || !dir.isDirectory()) { - invoke("publishEvent", "LOAD SCRIPTS ERROR"); - return; - } - - String[] extensions = { "py" }; - - for (String extension : extensions) { - File[] files = dir.listFiles(new FilenameFilter() { - public boolean accept(File dir, String name) { - return name.toLowerCase().endsWith("." + extension); - } - }); - - if (files != null) { - for (File file : files) { - Python p = (Python) Runtime.start("python", "Python"); - if (p != null) { - p.execFile(file.getAbsolutePath()); - } - } - } - } - } - // FIXME FIXME !!! THIS IS A MESS !! public void applyConfig() { super.apply(); @@ -777,6 +733,16 @@ public void halfSpeed() { sendToPeer("torso", "setSpeed", 20.0, 20.0, 20.0); } + /** + * execute python scripts in the init directory on startup of the service + * + * @throws IOException + */ + public void init() throws IOException { + invoke("publishEvent", "INIT"); + loadScripts(getResourceDir() + fs + "init"); + } + public boolean isCameraOn() { if (opencv != null) { if (opencv.isCapturing()) { @@ -840,6 +806,40 @@ public boolean loadGestures(String directory) { return true; } + /** + * Generalized directory python script loading method + * + * @param directory + * @throws IOException + */ + public void loadScripts(String directory) throws IOException { + File dir = new File(directory); + + if (!dir.exists() || !dir.isDirectory()) { + invoke("publishEvent", "LOAD SCRIPTS ERROR"); + return; + } + + String[] extensions = { "py" }; + + for (String extension : extensions) { + File[] files = dir.listFiles(new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.toLowerCase().endsWith("." + extension); + } + }); + + if (files != null) { + for (File file : files) { + Python p = (Python) Runtime.start("python", "Python"); + if (p != null) { + p.execFile(file.getAbsolutePath()); + } + } + } + } + } + public void moveArm(String which, Double bicep, Double rotate, Double shoulder, Double omoplate) { invoke("publishMoveArm", which, bicep, rotate, shoulder, omoplate); } @@ -1222,6 +1222,18 @@ public void powerUp() { python.execMethod("power_up"); } + /** + * easy utility to publishMessage + * + * @param name + * @param method + * @param data + */ + public void publish(String name, String method, Object... data) { + Message msg = Message.createMessage(getName(), name, method, data); + invoke("publishMessage", msg); + } + /** * "re"-publishing runtime config list, because I don't want to fix the js * subscribeTo :P @@ -1243,28 +1255,6 @@ public String publishEvent(String event) { return String.format("SYSTEM_EVENT %s", event); } - /** - * A more extensible interface point than publishEvent - * - * @param msg - * @return - */ - public Message publishMessage(Message msg) { - return msg; - } - - /** - * easy utility to publishMessage - * - * @param name - * @param method - * @param data - */ - public void publish(String name, String method, Object... data) { - Message msg = Message.createMessage(getName(), name, method, data); - invoke("publishMessage", msg); - } - /** * used to configure a flashing event - could use configuration to signal * different colors and states @@ -1286,6 +1276,16 @@ public String publishHeartbeat() { return getName(); } + /** + * A more extensible interface point than publishEvent + * + * @param msg + * @return + */ + public Message publishMessage(Message msg) { + return msg; + } + public HashMap publishMoveArm(String which, Double bicep, Double rotate, Double shoulder, Double omoplate) { HashMap map = new HashMap<>(); map.put("bicep", bicep); diff --git a/src/main/java/org/myrobotlab/service/config/InMoov2Config.java b/src/main/java/org/myrobotlab/service/config/InMoov2Config.java index ba96d1a2d0..a7a4dc8e33 100644 --- a/src/main/java/org/myrobotlab/service/config/InMoov2Config.java +++ b/src/main/java/org/myrobotlab/service/config/InMoov2Config.java @@ -45,28 +45,28 @@ public class InMoov2Config extends ServiceConfig { */ public String locale = null; // = "en-US"; - public boolean pirEnableTracking = false; - - /** - * play pir sounds when pir switching states - * sound located in data/InMoov2/sounds/pir-activated.mp3 - * sound located in data/InMoov2/sounds/pir-deactivated.mp3 - */ - public boolean pirPlaySounds = true; - - public boolean pirWakeUp = true; - public boolean neoPixelBootGreen=true; - + public boolean neoPixelDownloadBlue = true; - + public boolean neoPixelErrorRed = true; public boolean neoPixelFlashWhenSpeaking = true; + public boolean openCVFaceRecognizerActivated=true; + public boolean openCVFlipPicture=false; - public boolean openCVFaceRecognizerActivated=true; + public boolean pirEnableTracking = false; + + /** + * play pir sounds when pir switching states + * sound located in data/InMoov2/sounds/pir-activated.mp3 + * sound located in data/InMoov2/sounds/pir-deactivated.mp3 + */ + public boolean pirPlaySounds = true; + + public boolean pirWakeUp = true; public boolean robotCanMoveHeadWhileSpeaking = true; From bb707ac682446702d597fc8cdfcdda731290a48c Mon Sep 17 00:00:00 2001 From: grog Date: Wed, 3 May 2023 06:38:12 -0700 Subject: [PATCH 008/164] moving serviceData.json from data/.myrobotlab to libraries --- .../org/myrobotlab/framework/repo/MavenWrapper.java | 6 ++++-- .../org/myrobotlab/framework/repo/ServiceData.java | 11 +++++------ src/main/java/org/myrobotlab/io/FileIO.java | 2 +- .../org/myrobotlab/service/ServiceInterfaceTest.java | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/myrobotlab/framework/repo/MavenWrapper.java b/src/main/java/org/myrobotlab/framework/repo/MavenWrapper.java index ebfe5ae29a..84b1977992 100644 --- a/src/main/java/org/myrobotlab/framework/repo/MavenWrapper.java +++ b/src/main/java/org/myrobotlab/framework/repo/MavenWrapper.java @@ -280,8 +280,10 @@ public static void main(String[] args) { try { LoggingFactory.init(Level.INFO); - - File cache = new File("./data/.myrobotlab/serviceData.json"); + + File libraries = new File("libraries"); + libraries.mkdir(); + File cache = new File("libraries/serviceData.json"); if (cache.exists()) { log.info("removing servicData.json cache"); cache.delete(); diff --git a/src/main/java/org/myrobotlab/framework/repo/ServiceData.java b/src/main/java/org/myrobotlab/framework/repo/ServiceData.java index 0cc069cdf4..d2734bf023 100644 --- a/src/main/java/org/myrobotlab/framework/repo/ServiceData.java +++ b/src/main/java/org/myrobotlab/framework/repo/ServiceData.java @@ -53,7 +53,7 @@ public class ServiceData implements Serializable { private static final long serialVersionUID = 1L; - static private String serviceDataCacheFileName = FileIO.getCfgDir() + File.separator + "serviceData.json"; + static private String serviceDataCacheFileName = "libraries" + File.separator + "serviceData.json"; /** * clears all overrides. All services shall be using the standard hard co @@ -166,11 +166,11 @@ static public ServiceData getLocalInstance() { // step 3 - if 1 & 2 fail - then we can 'assume' were in develop // time (we'll isJar check and error if not) // - generate it and put it in - // getRoot()/resource/framework/serviceData.json + // libraries/serviceData.json // if we're not in a jar we are in an IDE. - // First check the .myrobotlab/serviceData.json dir. + // First check the libraries/serviceData.json dir. File jsonFile = new File(serviceDataCacheFileName); if (jsonFile.exists()) { // load it and return! @@ -427,9 +427,8 @@ public static void main(String[] args) { File removeExisting = new File(filename); removeExisting.delete(); - // remove .myrobotlab/serviceData.json - // 20190630 - GroG changed uses FileIO.getCfgDir() - removeExisting = new File(FileIO.getCfgDir() + File.separatorChar + "serviceData.json"); + // remove libraries/serviceData.json + removeExisting = new File("libraries" + File.separatorChar + "serviceData.json"); removeExisting.delete(); // THIS IS FOR ANT BUILD - DO NOT CHANGE !!! - BEGIN ---- diff --git a/src/main/java/org/myrobotlab/io/FileIO.java b/src/main/java/org/myrobotlab/io/FileIO.java index beafa401b1..861fc33c17 100644 --- a/src/main/java/org/myrobotlab/io/FileIO.java +++ b/src/main/java/org/myrobotlab/io/FileIO.java @@ -807,7 +807,7 @@ public static void main(String[] args) throws ZipException, IOException { List fileList = getFileList("InMoov", true); log.info("found {} files", fileList.size()); - FileIO.extract("/C:/mrl.test/current/myrobotlab.jar", "/resource/framework/serviceData.json", "C:\\mrl.test\\current\\.myrobotlab\\serviceData.json"); + FileIO.extract("/C:/mrl.test/current/myrobotlab.jar", "/resource/framework/serviceData.json", "C:\\mrl.test\\libraries\\serviceData.json"); copy("dir1", "dir2"); diff --git a/src/test/java/org/myrobotlab/service/ServiceInterfaceTest.java b/src/test/java/org/myrobotlab/service/ServiceInterfaceTest.java index c8813e4d0b..1cb9b3465d 100644 --- a/src/test/java/org/myrobotlab/service/ServiceInterfaceTest.java +++ b/src/test/java/org/myrobotlab/service/ServiceInterfaceTest.java @@ -255,7 +255,7 @@ public final void testAllServices() throws ClassNotFoundException, IOException { public final void testInstallAllServices() throws ClassNotFoundException, ParseException, IOException { // TODO: this probably is going to take longer but it's worth while! ServiceData sd = ServiceData.getLocalInstance();// CodecUtils.fromJson(FileUtils.readFileToString(new - // File("../repo/serviceData.json")), + // File("../libraries/serviceData.json")), // ServiceData.class); for (MetaData st : sd.getServiceTypes()) { if (!st.isAvailable()) { From 20c63fbb8c21b8097201377e10500346c31c7c89 Mon Sep 17 00:00:00 2001 From: grog Date: Wed, 3 May 2023 11:12:32 -0700 Subject: [PATCH 009/164] fixes requested --- .../java/org/myrobotlab/framework/repo/MavenWrapper.java | 4 ++-- .../java/org/myrobotlab/framework/repo/ServiceData.java | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/myrobotlab/framework/repo/MavenWrapper.java b/src/main/java/org/myrobotlab/framework/repo/MavenWrapper.java index 84b1977992..c490e2e6ac 100644 --- a/src/main/java/org/myrobotlab/framework/repo/MavenWrapper.java +++ b/src/main/java/org/myrobotlab/framework/repo/MavenWrapper.java @@ -281,9 +281,9 @@ public static void main(String[] args) { LoggingFactory.init(Level.INFO); - File libraries = new File("libraries"); + File libraries = new File(ServiceData.LIBRARIES); libraries.mkdir(); - File cache = new File("libraries/serviceData.json"); + File cache = new File(ServiceData.LIBRARIES + File.separator + "serviceData.json"); if (cache.exists()) { log.info("removing servicData.json cache"); cache.delete(); diff --git a/src/main/java/org/myrobotlab/framework/repo/ServiceData.java b/src/main/java/org/myrobotlab/framework/repo/ServiceData.java index d2734bf023..39d87b766d 100644 --- a/src/main/java/org/myrobotlab/framework/repo/ServiceData.java +++ b/src/main/java/org/myrobotlab/framework/repo/ServiceData.java @@ -32,12 +32,16 @@ * .myrobotlab directory. * * @author GroG + * + * FIXME - this is really just something that manages MetaData ... should make it the same both for clarity and + * transparency * */ -@Deprecated /* at some point this should all move over to MetaData */ public class ServiceData implements Serializable { static private ServiceData localInstance = null; + + static final public String LIBRARIES = "libraries"; transient public final static Logger log = LoggerFactory.getLogger(ServiceData.class); @@ -53,7 +57,7 @@ public class ServiceData implements Serializable { private static final long serialVersionUID = 1L; - static private String serviceDataCacheFileName = "libraries" + File.separator + "serviceData.json"; + static private String serviceDataCacheFileName = LIBRARIES + File.separator + "serviceData.json"; /** * clears all overrides. All services shall be using the standard hard co From 35812c771770256ef258c3e2a5dec78094811bba Mon Sep 17 00:00:00 2001 From: grog Date: Sun, 7 May 2023 11:09:05 -0700 Subject: [PATCH 010/164] initial state-machine checkin --- .gitignore | 28 + .../org/myrobotlab/framework/CmdConfig.java | 2 +- .../org/myrobotlab/framework/Service.java | 14 +- .../org/myrobotlab/programab/Session.java | 10 +- .../java/org/myrobotlab/service/InMoov2.java | 538 ++++++++++-------- .../org/myrobotlab/service/LocalSpeech.java | 70 ++- .../org/myrobotlab/service/ProgramAB.java | 236 +++----- .../java/org/myrobotlab/service/Python.java | 115 ++-- .../java/org/myrobotlab/service/Runtime.java | 3 + .../org/myrobotlab/service/Wikipedia.java | 2 +- .../service/config/InMoov2Config.java | 116 ++-- .../service/config/ProgramABConfig.java | 31 +- .../service/data/LedDisplayData.java | 12 +- .../resource/Intro/InMoov01_start.py | 8 - .../Python/modules/myrobotlab/README.md | 26 + .../Python/modules/myrobotlab/__init__.py | 8 + .../Python/modules/myrobotlab/myrobotlab.py | 24 + .../WebGui/app/service/js/IntroGui.js | 6 + .../WebGui/app/service/js/ProgramABGui.js | 35 +- .../WebGui/app/service/views/IntroGui.html | 2 +- .../app/service/views/ProgramABGui.html | 9 +- .../ProgramAB/bots/lloyd/aiml/lloyd.aiml | 124 ++-- 22 files changed, 801 insertions(+), 618 deletions(-) delete mode 100644 src/main/resources/resource/Intro/InMoov01_start.py create mode 100644 src/main/resources/resource/Python/modules/myrobotlab/README.md create mode 100644 src/main/resources/resource/Python/modules/myrobotlab/__init__.py create mode 100644 src/main/resources/resource/Python/modules/myrobotlab/myrobotlab.py diff --git a/.gitignore b/.gitignore index 803d7438d8..a8b739189c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,31 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Distribution / packaging +dist/ +build/ +*.egg-info/ +*.egg + +# IDEs and editors +.idea/ +.vscode/ +*.pyc + +# Local development settings +.env +.env.local +.env.*.local + +# Logs and other generated files +*.log +*.out +*.pid +*.sock + + # TODO clean this up ! /.project /.classpath diff --git a/src/main/java/org/myrobotlab/framework/CmdConfig.java b/src/main/java/org/myrobotlab/framework/CmdConfig.java index 5ba9870ec7..f807aaeb81 100644 --- a/src/main/java/org/myrobotlab/framework/CmdConfig.java +++ b/src/main/java/org/myrobotlab/framework/CmdConfig.java @@ -27,6 +27,6 @@ public class CmdConfig { /** * if this startup is enabled */ - public boolean enable = true; + public boolean enable = false; } diff --git a/src/main/java/org/myrobotlab/framework/Service.java b/src/main/java/org/myrobotlab/framework/Service.java index 8aa17d20f3..85144c4b14 100644 --- a/src/main/java/org/myrobotlab/framework/Service.java +++ b/src/main/java/org/myrobotlab/framework/Service.java @@ -1379,6 +1379,10 @@ public boolean isRunning() { * Default load config method, subclasses should override this to support * service specific configuration in the service yaml files. * + * apply is the first function to be called after construction of a service, then startService will be called + * + * construct -> apply -> startService + * */ @Override public ServiceConfig apply(ServiceConfig inConfig) { @@ -1940,11 +1944,11 @@ synchronized public void startService() { } thisThread.start(); isRunning = true; - Runtime runtime = Runtime.getInstance(); - if (runtime != null) { - runtime.invoke("started", getName()); // getFullName()); - removed - // fullname - } + send("runtime", "started", getName()); +// Runtime runtime = Runtime.getInstance(); +// if (runtime != null) { +// runtime.invoke("started", getName()); +// } } else { log.debug("startService request: service {} is already running", name); diff --git a/src/main/java/org/myrobotlab/programab/Session.java b/src/main/java/org/myrobotlab/programab/Session.java index f222ea76d1..b09cb3041f 100644 --- a/src/main/java/org/myrobotlab/programab/Session.java +++ b/src/main/java/org/myrobotlab/programab/Session.java @@ -14,6 +14,7 @@ import org.myrobotlab.io.FileIO; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.service.ProgramAB; +import org.myrobotlab.service.config.ProgramABConfig; import org.slf4j.Logger; /** @@ -82,6 +83,11 @@ private synchronized Chat getChat() { predicatesFile = userPredicates; chat.predicates.getPredicateDefaults(userPredicates.getAbsolutePath()); } + + ProgramABConfig config = (ProgramABConfig)programab.getConfig(); + if (config.startTopic != null){ + chat.predicates.put("topic", config.startTopic); + } } predicates = chat.predicates; return chat; @@ -96,7 +102,9 @@ public void savePredicates() { if (predicate.equals("test")) { log.info("here"); } - sb.append(predicate + ":" + value + "\n"); + if (predicate.startsWith("cfg_")) { + sb.append(predicate + ":" + value + "\n"); + } } File predicates = new File(FileIO.gluePaths(botInfo.path.getAbsolutePath(), String.format("config/%s.predicates.txt", userName))); predicates.getParentFile().mkdirs(); diff --git a/src/main/java/org/myrobotlab/service/InMoov2.java b/src/main/java/org/myrobotlab/service/InMoov2.java index 95a34a3bb6..f75c84868b 100644 --- a/src/main/java/org/myrobotlab/service/InMoov2.java +++ b/src/main/java/org/myrobotlab/service/InMoov2.java @@ -28,6 +28,7 @@ import org.myrobotlab.service.abstracts.AbstractSpeechRecognizer; import org.myrobotlab.service.abstracts.AbstractSpeechSynthesis; import org.myrobotlab.service.config.InMoov2Config; +import org.myrobotlab.service.config.ProgramABConfig; import org.myrobotlab.service.config.ServiceConfig; import org.myrobotlab.service.data.JoystickData; import org.myrobotlab.service.data.LedDisplayData; @@ -93,91 +94,6 @@ public static boolean loadFile(String file) { return true; } - public static void main(String[] args) { - try { - - LoggingFactory.init(Level.ERROR); - // Platform.setVirtual(true); - // Runtime.start("s01", "Servo"); - // Runtime.start("intro", "Intro"); - - // Runtime.startConfig("pr-1213-1"); - - Runtime.main(new String[] {"--log-level", "info", "-s", "webgui", "WebGui", "intro", "Intro", "python", "Python"}); - - boolean done = true; - if (done) { - return; - } - - - WebGui webgui = (WebGui) Runtime.create("webgui", "WebGui"); - // webgui.setSsl(true); - webgui.autoStartBrowser(false); - // webgui.setPort(8888); - webgui.startService(); - - Runtime.start("python", "Python"); - // Runtime.start("ros", "Ros"); - Runtime.start("intro", "Intro"); - // InMoov2 i01 = (InMoov2) Runtime.start("i01", "InMoov2"); - // i01.startPeer("simulator"); - // Runtime.startConfig("i01-05"); - // Runtime.startConfig("pir-01"); - - // Polly polly = (Polly)Runtime.start("i01.mouth", "Polly"); - // i01 = (InMoov2) Runtime.start("i01", "InMoov2"); - - - // polly.speakBlocking("Hi, to be or not to be that is the question, - // wheather to take arms against a see of trouble, and by aposing them end - // them, to sleep, to die"); - // i01.startPeer("mouth"); - // i01.speakBlocking("Hi, to be or not to be that is the question, - // wheather to take arms against a see of trouble, and by aposing them end - // them, to sleep, to die"); - - Runtime.start("python", "Python"); - - // i01.startSimulator(); - Plan plan = Runtime.load("webgui", "WebGui"); - // WebGuiConfig webgui = (WebGuiConfig) plan.get("webgui"); - // webgui.autoStartBrowser = false; - Runtime.startConfig("webgui"); - Runtime.start("webgui", "WebGui"); - - Random random = (Random) Runtime.start("random", "Random"); - - random.addRandom(3000, 8000, "i01", "setLeftArmSpeed", 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0); - random.addRandom(3000, 8000, "i01", "setRightArmSpeed", 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0); - - random.addRandom(3000, 8000, "i01", "moveLeftArm", 0.0, 5.0, 85.0, 95.0, 25.0, 30.0, 10.0, 15.0); - random.addRandom(3000, 8000, "i01", "moveRightArm", 0.0, 5.0, 85.0, 95.0, 25.0, 30.0, 10.0, 15.0); - - random.addRandom(3000, 8000, "i01", "setLeftHandSpeed", 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0); - random.addRandom(3000, 8000, "i01", "setRightHandSpeed", 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0); - - random.addRandom(3000, 8000, "i01", "moveRightHand", 10.0, 160.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0, 130.0, 175.0); - random.addRandom(3000, 8000, "i01", "moveLeftHand", 10.0, 160.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0, 5.0, 40.0); - - random.addRandom(200, 1000, "i01", "setHeadSpeed", 8.0, 20.0, 8.0, 20.0, 8.0, 20.0); - random.addRandom(200, 1000, "i01", "moveHead", 70.0, 110.0, 65.0, 115.0, 70.0, 110.0); - - random.addRandom(200, 1000, "i01", "setTorsoSpeed", 2.0, 5.0, 2.0, 5.0, 2.0, 5.0); - random.addRandom(200, 1000, "i01", "moveTorso", 85.0, 95.0, 88.0, 93.0, 70.0, 110.0); - - random.save(); - -// i01.startChatBot(); -// -// i01.startAll("COM3", "COM4"); - Runtime.start("python", "Python"); - - } catch (Exception e) { - log.error("main threw", e); - } - } - transient ProgramAB chatBot; protected List configList; @@ -208,8 +124,14 @@ public static void main(String[] args) { String lastGestureExecuted; Long lastPirActivityTime; + + LedDisplayData ledBoot = new LedDisplayData(0, 220, 0); - LedDisplayData led = new LedDisplayData(); + LedDisplayData ledPir = new LedDisplayData(); + + LedDisplayData ledHeartBeat = new LedDisplayData(); + + LedDisplayData ledError = new LedDisplayData(220, 0, 0); /** * supported locales @@ -225,8 +147,6 @@ public static void main(String[] args) { boolean mute = false; - // transient JMonkeyEngine simulator; - transient OpenCV opencv; transient Pir pir; @@ -252,7 +172,15 @@ public void addTextListener(TextListener service) { // CORRECT WAY ! - no direct reference - just use the name in a subscription addListener("publishText", service.getName()); } + + public void syncConfigToPredicates() { + + } + public InMoov2Config publishConfig() { + return (InMoov2Config) config; + } + @Override public ServiceConfig apply(ServiceConfig c) { InMoov2Config config = (InMoov2Config) super.apply(c); @@ -266,7 +194,9 @@ public ServiceConfig apply(ServiceConfig c) { setLocale(getSupportedLocale(Runtime.getInstance().getLocale().toString())); } - init(); + if (config.loadInitScripts) { + loadInitScripts(); + } if (config.loadGestures) { loadGestures(); @@ -281,16 +211,19 @@ public ServiceConfig apply(ServiceConfig c) { } catch (Exception e) { error(e); } - return c; + + invoke("publishConfig", c); + + return c; } - // FIXME FIXME !!! THIS IS A MESS !! - public void applyConfig() { - super.apply(); - log.error("applyConfig()"); - // always getResponse ! - speak("InMoov apply config"); - } + // TODO- Hook to get config event published + // public void applyConfig() { + // super.apply(); + // log.error("applyConfig()"); + // // always getResponse ! + // speak("InMoov apply config"); + // } @Override public void attachTextListener(String name) { @@ -559,7 +492,7 @@ public void finishedGesture(String nameOfGesture) { // FIXME - this isn't the callback for fsm - why is it needed here ? public void fire(String event) { - invoke("publishEvent", event); + invoke("publishSystemEvent", event); } public void fullSpeed() { @@ -770,7 +703,7 @@ public void loadGestures() { * @return true/false */ public boolean loadGestures(String directory) { - invoke("publishEvent", "LOAD GESTURES"); + invoke("publishSystemEvent", "LOAD GESTURES"); // iterate over each of the python files in the directory // and load them into the python interpreter. @@ -816,7 +749,7 @@ public void loadScripts(String directory) throws IOException { File dir = new File(directory); if (!dir.exists() || !dir.isDirectory()) { - invoke("publishEvent", "LOAD SCRIPTS ERROR"); + invoke("publishSystemEvent", "LOAD SCRIPTS ERROR"); return; } @@ -934,7 +867,7 @@ public void moveTorsoBlocking(Double topStom, Double midStom, Double lowStom) { public PredicateEvent onChangePredicate(PredicateEvent event) { log.error("onChangePredicate {}", event); if (event.name.equals("topic")) { - invoke("publishEvent", String.format("TOPIC CHANGED TO %s", event.value)); + invoke("publishSystemEvent", String.format("TOPIC CHANGED TO %s", event.value)); } // depending on configuration .... // call python ? @@ -961,8 +894,8 @@ public void onCreated(String fullname) { public void onFinishedConfig(String configName) { log.info("onFinishedConfig"); - // invoke("publishEvent", "configFinished"); - invoke("publishEvent", "CONFIG LOADED"); + // invoke("publishSystemEvent", "configFinished"); + invoke("publishFinishedConfig", configName); } public void onGestureStatus(Status status) { @@ -988,8 +921,9 @@ public void onJointAngles(Map angleMap) { @Override public void onJoystickInput(JoystickData input) throws Exception { - // TODO Auto-generated method stub - + // TODO timer ? to test and not send an event + // switches to manual control ? + invoke("publishSystemEvent", "joystick"); } public String onNewState(String state) { @@ -1018,16 +952,9 @@ public OpenCVData onOpenCVData(OpenCVData data) { * onPirOn flash neopixel */ public void onPirOn() { - led.action = "flash"; - led.red = 50; - led.green = 100; - led.blue = 150; - led.count = 5; - led.interval = 500; - - invoke("publishFlash"); + invoke("publishFlash", new LedDisplayData(0, 0, 220)); // pirOn event vs wake event - invoke("publishEvent", "WAKE"); + invoke("publishSystemEvent", "WAKE"); } // GOOD GOOD GOOD - LOOPBACK - flexible and replacable by python @@ -1063,9 +990,9 @@ public boolean onSense(boolean b) { // setEvent("pir-sense-on" .. also sets it in config ? // config.handledEvents["pir-sense-on"] if (b) { - invoke("publishEvent", "PIR ON"); + invoke("publishSystemEvent", "PIR ON"); } else { - invoke("publishEvent", "PIR OFF"); + invoke("publishSystemEvent", "PIR OFF"); } return b; } @@ -1086,82 +1013,122 @@ public void onStarted(String name) { Runtime runtime = Runtime.getInstance(); log.info("onStarted {}", name); -// BAD IDEA - better to ask for a system report or an error report -// if (runtime.isProcessingConfig()) { -// invoke("publishEvent", "CONFIG STARTED"); -// } + // BAD IDEA - better to ask for a system report or an error report + // if (runtime.isProcessingConfig()) { + // invoke("publishSystemEvent", "CONFIG STARTED"); + // } String peerKey = getPeerKey(name); if (peerKey != null) { invoke("publishEvent", "STARTED " + peerKey); } - String actualName = getPeerName("ear"); - if (actualName.equals(name)) { - AbstractSpeechRecognizer ear = (AbstractSpeechRecognizer) Runtime.getService(actualName); - ear.attachTextListener(getPeerName("chatBot")); - } - - actualName = getPeerName("mouth"); - if (actualName.equals(name)) { - AbstractSpeechSynthesis mouth = (AbstractSpeechSynthesis) Runtime.getService(actualName); - mouth.attachSpeechListener(getPeerName("ear")); - } - - actualName = getPeerName("chatBot"); - if (actualName.equals(name)) { - ProgramAB chatBot = (ProgramAB) Runtime.getService(actualName); - chatBot.attachTextListener(getPeerName("htmlFilter")); - startPeer("htmlFilter"); + if (runtime.isProcessingConfig() && !configStarted) { + invoke("publishSystemEvent", "CONFIG STARTED " + runtime.getConfigName()); + configStarted = true; } - actualName = getPeerName("htmlFilter"); - if (actualName.equals(name)) { - TextPublisher htmlFilter = (TextPublisher) Runtime.getService(actualName); - htmlFilter.attachTextListener(getPeerName("mouth")); - } - - // FIXME - this is a much better way to do it than the code above - // the types are not exposed, its not using a local reference of the - // service - // its remote compatible, and it uses the peers actual name - it does not - // break easily, - // allows user modification and subscribing to topics for extensibility, - // no NPEs uses messaging, and follows mrl pub/sub conventions - above - // code should be - // refactored accordingly. - actualName = getPeerName("head"); - if (actualName.equals(name)) { - addListener("publishMoveHead", actualName); - } - - actualName = getPeerName("torso"); - if (actualName.equals(name)) { - addListener("publishMoveTorso", actualName); - } - - // mapping a channel to a single end point depending on peer - actualName = getPeerName("leftHand"); - if (actualName.equals(name)) { - addListener("publishMoveLeftHand", actualName, "onMoveHand"); - } - - // mapping a channel to a single end point depending on peer - actualName = getPeerName("rightHand"); - if (actualName.equals(name)) { - addListener("publishMoveRightHand", actualName, "onMoveHand"); - } - - // mapping a channel to a single end point depending on peer - actualName = getPeerName("leftArm"); - if (actualName.equals(name)) { - addListener("publishMoveLeftArm", actualName, "onMoveArm"); - } - - // mapping a channel to a single end point depending on peer - actualName = getPeerName("rightArm"); - if (actualName.equals(name)) { - addListener("publishMoveRightArm", actualName, "onMoveArm"); + invoke("publishSystemEvent", "STARTED " + peerKey); + + switch (peerKey) { + case "audioPlayer": + break; + case "chatBot": + ProgramAB chatBot = (ProgramAB) Runtime.getService(name); + chatBot.attachTextListener(getPeerName("htmlFilter")); + ProgramABConfig cfg = (ProgramABConfig) chatBot.getConfig(); + if (cfg.currentBotName == null || cfg.currentBotName.isEmpty()) { + String locale = getLocale().getTag(); + if (locale != null) { + cfg.currentBotName = locale; + } + } + + // synching predicates - is a session available ? + // do you need switch session from ProgramAB ?? + chatBot.onConfig(config); + + // FIXME !!! Do not startPeers !! there is no reason, this is controlled by Config !!! + // startPeer("htmlFilter"); + break; + case "controller3": + break; + case "controller4": + break; + case "ear": + AbstractSpeechRecognizer ear = (AbstractSpeechRecognizer) Runtime.getService(name); + ear.attachTextListener(getPeerName("chatBot")); + break; + case "eyeTracking": + break; + case "fsm": + break; + case "gpt3": + break; + case "head": + addListener("publishMoveHead", name); + break; + case "headTracking": + break; + case "htmlFilter": + TextPublisher htmlFilter = (TextPublisher) Runtime.getService(name); + htmlFilter.attachTextListener(getPeerName("mouth")); + break; + case "imageDisplay": + break; + case "leap": + break; + case "left": + break; + case "leftArm": + addListener("publishMoveLeftArm", name, "onMoveArm"); + break; + case "leftHand": + addListener("publishMoveLeftHand", name, "onMoveHand"); + break; + case "mouth": + mouth = (AbstractSpeechSynthesis) Runtime.getService(name); + mouth.attachSpeechListener(getPeerName("ear")); + break; + case "mouthControl": + break; + case "neoPixel": + break; + case "opencv": + subscribeTo(name, "publishOpenCVData"); + break; + case "openni": + break; + case "openWeatherMap": + break; + case "pid": + break; + case "pir": + break; + case "random": + break; + case "right": + break; + case "rightArm": + addListener("publishMoveRightArm", name, "onMoveArm"); + break; + case "rightHand": + addListener("publishMoveRightHand", name, "onMoveHand"); + break; + case "servoMixer": + break; + case "simulator": + break; + case "torso": + addListener("publishMoveTorso", name); + break; + case "ultrasonicRight": + break; + case "ultrasonicLeft": + break; + default: + log.warn("unknown peer %s not hanled in onStarted", peerKey); + break; } ServiceInterface si = Runtime.getService(name); @@ -1234,6 +1201,29 @@ public void publish(String name, String method, Object... data) { invoke("publishMessage", msg); } + /** + * publishing point for desired sounds to be played + * + * @param filepath + * @return + */ + public String publishPlayAudioFile(String filepath) { + return filepath; + } + + public String publishStartConfig(String configName) { + info("config %s started", configName); + invoke("publishSystemEvent", "CONFIG STARTED " + configName); + return configName; + } + + public String publishFinishedConfig(String configName) { + info("config %s finished", configName); + invoke("publishSystemEvent", "CONFIG LOADED " + configName); + + return configName; + } + /** * "re"-publishing runtime config list, because I don't want to fix the js * subscribeTo :P @@ -1251,8 +1241,8 @@ public List publishConfigList() { * @param event * @return */ - public String publishEvent(String event) { - return String.format("SYSTEM_EVENT %s", event); + public String publishSystemEvent(String event) { + return String.format("SYSTEMEVENT %s", event); } /** @@ -1261,23 +1251,18 @@ public String publishEvent(String event) { * * @return */ - public LedDisplayData publishFlash() { + public LedDisplayData publishFlash(LedDisplayData led) { return led; } public String publishHeartbeat() { - led.action = "flash"; - led.red = 180; - led.green = 10; - led.blue = 30; - led.count = 1; - led.interval = 50; - invoke("publishFlash"); + invoke("publishFlash", new LedDisplayData(150, 0, 0)); return getName(); } /** - * A more extensible interface point than publishEvent + * A more extensible interface point than publishSystemEvent FIXME - create + * interface for this * * @param msg * @return @@ -1388,7 +1373,7 @@ public String publishText(String text) { public void releasePeer(String peerKey) { super.releasePeer(peerKey); if (peerKey != null) { - invoke("publishEvent","STOPPED " + peerKey); + invoke("publishSystemEvent", "STOPPED " + peerKey); } } @@ -1729,11 +1714,7 @@ public ProgramAB startChatBot() { } } chatBot.setPredicate("parameterHowDoYouDo", ""); - try { - chatBot.savePredicates(); - } catch (IOException e) { - log.error("saving predicates threw", e); - } + chatBot.savePredicates(); htmlFilter = (HtmlFilter) startPeer("htmlFilter");// Runtime.start("htmlFilter", // "HtmlFilter"); chatBot.attachTextListener(htmlFilter); @@ -1747,10 +1728,10 @@ public ProgramAB startChatBot() { if (chatBot.getPredicate("default", "firstinit").isEmpty() || chatBot.getPredicate("default", "firstinit").equals("unknown") || chatBot.getPredicate("default", "firstinit").equals("started")) { chatBot.startSession(chatBot.getPredicate("default", "lastUsername")); - invoke("publishEvent", "FIRST INIT"); + invoke("publishSystemEvent", "FIRST INIT"); } else { chatBot.startSession(chatBot.getPredicate("default", "lastUsername")); - invoke("publishEvent", "WAKE UP"); + invoke("publishSystemEvent", "WAKE UP"); } } catch (Exception e) { speak("could not load chatBot"); @@ -1848,12 +1829,6 @@ public OpenCV startOpenCV() { return opencv; } - @Override - public ServiceInterface startPeer(String peer) { - ServiceInterface si = super.startPeer(peer); - return si; - } - @Override public void startService() { super.startService(); @@ -1862,14 +1837,8 @@ public void startService() { // InMoov2 has a huge amount of peers - // by default all servos will auto-disable - // Servo.setAutoDisableDefault(true); //until peer servo services for - // InMoov2 have the auto disable behavior, we should keep this - - // same as created in runtime - send asyc message to all - // registered services, this service has started - // find all servos - set them all to autoDisable(true) - // onStarted(name) will handle all future created servos + // all existing servo service are automatically autoDisabled = true + // TODO should be in config InMoov2Config.servoAutoDisable:true List services = Runtime.getServices(); for (ServiceInterface si : services) { if ("Servo".equals(si.getSimpleName())) { @@ -1877,34 +1846,21 @@ public void startService() { } } - // REALLY NEEDS TO BE CLEANED UP - no direct references - // "publish" scripts which should be executed :( - // python = (Python) startPeer("python"); - // python = (Python) Runtime.start("python", "Python"); <- BAD !!!! - // load(locale.getTag()); WTH ? - + // FIXME - these should be handled by the StateMachine // get events of new services and shutdown - Runtime r = Runtime.getInstance(); - subscribe(r.getName(), "shutdown"); - subscribe(r.getName(), "publishConfigList"); - - // FIXME - Framework should auto-magically auto-start peers AFTER - // construction - unless explicitly told not to - // peers to start on construction - // imageDisplay = (ImageDisplay) startPeer("imageDisplay"); + subscribe("runtime", "shutdown"); + // power up loopback subscription + addListener(getName(), "powerUp"); + // sad, but this is for the InMoov2 UI and the desire to put + // runtime related things there :( + subscribe("runtime", "publishConfigList"); if (runtime.isProcessingConfig()) { - invoke("publishEvent", "configStarted"); + invoke("publishSystemEvent", "configStarted"); } - // power up loopback subscription - addListener(getName(), "powerUp"); - - addListener("publishEvent", getPeerName("chatBot"), "getResponse"); - - // for begin and end of processing config ? - // subscribe("runtime", "publishStartConfig"); - subscribe("runtime", "publishFinishedConfig"); + // chatbot getresponse attached to publishSystemEvent + addListener("publishSystemEvent", getPeerName("chatBot"), "getResponse"); try { // copy config if it doesn't already exist @@ -1946,6 +1902,22 @@ public void startService() { error(e); } runtime.invoke("publishConfigList"); + + if (c.bootUpPlaySound) { + invoke("publishPlayAudioFile", getResourceDir() + fs + "system" + fs + "sounds" + fs + "startupsound.mp3"); + } + + invoke("publishFlash"); + + // process all currently running services + // since registering for them to start won't process + // them + for (String service : Runtime.getServiceNames()) { + onStarted(service); + } + + invoke("publishSystemEvent", "BootUpCompleted"); + } public void startServos() { @@ -2005,7 +1977,7 @@ public void systemCheck() { Platform platform = Runtime.getPlatform(); setPredicate("system version", platform.getVersion()); // ERROR buffer !!! - invoke("publishEvent", "systemCheckFinished"); + invoke("publishSystemEvent", "systemCheckFinished"); } // FIXME - if this is really desired it will drive local references for all @@ -2020,4 +1992,88 @@ public void waitTargetPos() { sendToPeer("torso", "waitTargetPos"); } + public static void main(String[] args) { + try { + + LoggingFactory.init(Level.ERROR); + // Platform.setVirtual(true); + // Runtime.start("s01", "Servo"); + // Runtime.start("intro", "Intro"); + + // Runtime.startConfig("pr-1213-1"); + + Runtime.main(new String[] { "--log-level", "info", "-s", "intro", "Intro", "python", "Python" }); + + // Runtime.start("bot","ProgramAB"); + + WebGui webgui = (WebGui) Runtime.create("webgui", "WebGui"); + // webgui.setSsl(true); + webgui.autoStartBrowser(false); + // webgui.setPort(8888); + webgui.startService(); + + boolean done = true; + if (done) { + return; + } + + Runtime.start("python", "Python"); + // Runtime.start("ros", "Ros"); + Runtime.start("intro", "Intro"); + // InMoov2 i01 = (InMoov2) Runtime.start("i01", "InMoov2"); + // i01.startPeer("simulator"); + // Runtime.startConfig("i01-05"); + // Runtime.startConfig("pir-01"); + + // Polly polly = (Polly)Runtime.start("i01.mouth", "Polly"); + // i01 = (InMoov2) Runtime.start("i01", "InMoov2"); + + // polly.speakBlocking("Hi, to be or not to be that is the question, + // wheather to take arms against a see of trouble, and by aposing them end + // them, to sleep, to die"); + // i01.startPeer("mouth"); + // i01.speakBlocking("Hi, to be or not to be that is the question, + // wheather to take arms against a see of trouble, and by aposing them end + // them, to sleep, to die"); + + Runtime.start("python", "Python"); + + // i01.startSimulator(); + Plan plan = Runtime.load("webgui", "WebGui"); + // WebGuiConfig webgui = (WebGuiConfig) plan.get("webgui"); + // webgui.autoStartBrowser = false; + Runtime.startConfig("webgui"); + Runtime.start("webgui", "WebGui"); + + Random random = (Random) Runtime.start("random", "Random"); + + random.addRandom(3000, 8000, "i01", "setLeftArmSpeed", 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0); + random.addRandom(3000, 8000, "i01", "setRightArmSpeed", 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0); + + random.addRandom(3000, 8000, "i01", "moveLeftArm", 0.0, 5.0, 85.0, 95.0, 25.0, 30.0, 10.0, 15.0); + random.addRandom(3000, 8000, "i01", "moveRightArm", 0.0, 5.0, 85.0, 95.0, 25.0, 30.0, 10.0, 15.0); + + random.addRandom(3000, 8000, "i01", "setLeftHandSpeed", 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0); + random.addRandom(3000, 8000, "i01", "setRightHandSpeed", 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0); + + random.addRandom(3000, 8000, "i01", "moveRightHand", 10.0, 160.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0, 130.0, 175.0); + random.addRandom(3000, 8000, "i01", "moveLeftHand", 10.0, 160.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0, 10.0, 60.0, 5.0, 40.0); + + random.addRandom(200, 1000, "i01", "setHeadSpeed", 8.0, 20.0, 8.0, 20.0, 8.0, 20.0); + random.addRandom(200, 1000, "i01", "moveHead", 70.0, 110.0, 65.0, 115.0, 70.0, 110.0); + + random.addRandom(200, 1000, "i01", "setTorsoSpeed", 2.0, 5.0, 2.0, 5.0, 2.0, 5.0); + random.addRandom(200, 1000, "i01", "moveTorso", 85.0, 95.0, 88.0, 93.0, 70.0, 110.0); + + random.save(); + + // i01.startChatBot(); + // + // i01.startAll("COM3", "COM4"); + Runtime.start("python", "Python"); + + } catch (Exception e) { + log.error("main threw", e); + } + } } diff --git a/src/main/java/org/myrobotlab/service/LocalSpeech.java b/src/main/java/org/myrobotlab/service/LocalSpeech.java index ee95463632..447993cae6 100644 --- a/src/main/java/org/myrobotlab/service/LocalSpeech.java +++ b/src/main/java/org/myrobotlab/service/LocalSpeech.java @@ -207,8 +207,10 @@ public void loadVoices() { String voicesText = null; + // FIXME this is not right - it should be based on speechType not OS + // speechType should be "set" based on OS and user preference if (platform.isWindows()) { - + try { List args = new ArrayList<>(); @@ -270,9 +272,11 @@ public void loadVoices() { addVoice(matcher.group(1).toLowerCase(), "male", matcher.group(2), matcher.group(1).toLowerCase()); } } - } else if (platform.isLinux()) { - addVoice("Linus", "male", "en-US", "festival"); } + // let apply config add and set the voices +// else if (platform.isLinux()) { +// addVoice("Linus", "male", "en-US", "festival"); +// } } public void removeExt(boolean b) { @@ -283,6 +287,11 @@ public void removeExt(boolean b) { * @return setEspeak sets the Linux tts to espeak template */ public boolean setEspeak() { + if (!Runtime.getPlatform().isLinux()) { + error("espeak only supported on Linux"); + return false; + } + LocalSpeechConfig c = (LocalSpeechConfig) config; c.speechType = "Espeak"; voices.clear(); @@ -297,6 +306,11 @@ public boolean setEspeak() { * @return setFestival sets the Linux tts to festival template */ public boolean setFestival() { + if (!Runtime.getPlatform().isLinux()) { + error("festival only supported on Linux"); + return false; + } + LocalSpeechConfig c = (LocalSpeechConfig) config; voices.clear(); addVoice("Linus", "male", "en-US", "festival"); @@ -304,10 +318,6 @@ public boolean setFestival() { removeExt(false); setTtsHack(false); setTtsCommand("echo \"{text}\" | text2wave -o {filename}"); - if (!Runtime.getPlatform().isLinux()) { - error("festival only supported on Linux"); - return false; - } return true; } @@ -317,6 +327,11 @@ public boolean setFestival() { * @return true if successfully switched */ public boolean setPico2Wav() { + if (!Runtime.getPlatform().isLinux()) { + error("pico2wave only supported on Linux"); + return false; + } + LocalSpeechConfig c = (LocalSpeechConfig) config; c.speechType = "Pico2Wav"; removeExt(false); @@ -329,12 +344,13 @@ public boolean setPico2Wav() { addVoice("es-ES", "female", "es-ES", "pico2wav"); addVoice("fr-FR", "female", "fr-FR", "pico2wav"); addVoice("it-IT", "female", "it-IT", "pico2wav"); + + if (voice == null) { + setVoice(getLocale().getTag()); + } setTtsCommand("pico2wave -l {voice_name} -w {filename} \"{text}\" "); - if (!Runtime.getPlatform().isLinux()) { - error("pico2wave only supported on Linux"); - return false; - } + broadcastState(); return true; } @@ -478,10 +494,30 @@ public void setTtsHack(boolean b) { public void setTtsPath(String ttsPath) { this.ttsPath = ttsPath; } + + public boolean isExecutableAvailable(String executableName) { + ProcessBuilder processBuilder = new ProcessBuilder(); + String command = ""; + boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows"); + if (isWindows) { + command = "where " + executableName; + } else { + command = "which " + executableName; + } + processBuilder.command("sh", "-c", command); + try { + Process process = processBuilder.start(); + process.waitFor(); + return process.exitValue() == 0; + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + return false; + } +} @Override public ServiceConfig apply(ServiceConfig config) { - LocalSpeechConfig c = (LocalSpeechConfig) super.apply(config); + LocalSpeechConfig c = (LocalSpeechConfig) config; // setup the default tts per os Platform platform = Runtime.getPlatform(); @@ -491,7 +527,11 @@ public ServiceConfig apply(ServiceConfig config) { } else if (platform.isMac()) { setSay(); } else if (platform.isLinux()) { - setFestival(); + if (isExecutableAvailable("pico2wave")) { + setPico2Wav(); + } else { + setFestival(); + } } else { error("%s unknown platform %s", getName(), platform.getOS()); } @@ -502,6 +542,10 @@ public ServiceConfig apply(ServiceConfig config) { if (c.voice != null) { setVoice(c.voice); } + + // do super stuff + super.apply(c); + return c; } diff --git a/src/main/java/org/myrobotlab/service/ProgramAB.java b/src/main/java/org/myrobotlab/service/ProgramAB.java index 90aa90216e..004f9b165b 100644 --- a/src/main/java/org/myrobotlab/service/ProgramAB.java +++ b/src/main/java/org/myrobotlab/service/ProgramAB.java @@ -19,6 +19,7 @@ import org.alicebot.ab.MagicBooleans; import org.alicebot.ab.ProgramABListener; import org.apache.commons.lang3.StringUtils; +import org.myrobotlab.codec.CodecUtils; import org.myrobotlab.framework.Service; import org.myrobotlab.framework.interfaces.Attachable; import org.myrobotlab.image.Util; @@ -46,6 +47,7 @@ import org.myrobotlab.service.interfaces.UtteranceListener; import org.myrobotlab.service.interfaces.UtterancePublisher; import org.slf4j.Logger; +import org.yaml.snakeyaml.Yaml; /** * Program AB service for MyRobotLab Uses AIML 2.0 to create a ChatBot This is a @@ -76,13 +78,6 @@ public class ProgramAB extends Service */ protected boolean useGlobalSession = false; - /** - * sleep current state of the sleep if globalSession is used true : ProgramAB - * is sleeping and wont respond false : ProgramAB is not sleeping and any - * response requested will be processed - */ - protected boolean sleep = false; - transient public final static Logger log = LoggerFactory.getLogger(ProgramAB.class); /** @@ -95,17 +90,6 @@ public class ProgramAB extends Service */ Map sessions = new TreeMap<>(); - /** - * initial bot name - this bot comes with ProgramAB this will be the result of - * whatever is scanned in the constructor - */ - String currentBotName = null; - - /** - * default user name chatting with the bot - */ - String currentUserName = "human"; - /** * start GoogleSearch (a peer) instead of sraix web service which is down or * problematic much of the time @@ -125,46 +109,6 @@ public class ProgramAB extends Service */ public ProgramAB(String n, String id) { super(n, id); - - // TODO - allow lazy selection of bot - even if it currently doesn't exist - // in the bot map - move scanning to start - - // 1. scan resources .. either "resource/ProgramAB" or - // ../ProgramAB/resource/ProgramAB (for dev) for valid bot directories - - // List resourceBots = scanForBots(getResourceDir()); - // - // if (isDev()) { - // // 2. dev loading "only" dev bots - from dev location - // for (File file : resourceBots) { - // addBotPath(file.getAbsolutePath()); - // } - // } else { - // // 2. runtime loading - // // copy any bot in "resource/ProgramAB/{botName}" not found in - // // "data/ProgramAB/{botName}" - // for (File file : resourceBots) { - // String botName = getBotName(file); - // File dataBotDir = new File(FileIO.gluePaths("data/ProgramAB", botName)); - // if (dataBotDir.exists()) { - // log.info("found data/ProgramAB/{} not copying", botName); - // } else { - // log.info("will copy new data/ProgramAB/{}", botName); - // try { - // FileIO.copy(file, dataBotDir); - // } catch (Exception e) { - // error(e); - // } - // } - // } - // - // // 3. addPath for all bots found in "data/ProgramAB/" - // List dataBots = scanForBots("data/ProgramAB"); - // for (File file : dataBots) { - // addBotPath(file.getAbsolutePath()); - // } - // } - } public String getBotName(File file) { @@ -197,6 +141,7 @@ public List scanForBots(String path) { if (checkIfValid(file)) { info("found %s bot directory", file.getName()); botDirs.add(file); + addBotPath(file.getAbsolutePath()); } } return botDirs; @@ -615,7 +560,8 @@ public void reloadSession(String userName, String botName) throws IOException { * @return */ public Map getPredicates() { - return getPredicates(currentUserName, currentBotName); + ProgramABConfig c = (ProgramABConfig) config; + return getPredicates(c.currentUserName, c.currentBotName); } /** @@ -633,11 +579,8 @@ public Map getPredicates(String userName, String botName) { /** * Save all the predicates for all known sessions. - * - * @throws IOException - * boom */ - public void savePredicates() throws IOException { + public void savePredicates() { for (Session session : sessions.values()) { session.savePredicates(); } @@ -696,7 +639,8 @@ public void removeBotProperty(String botName, String name) { } public Session startSession() throws IOException { - return startSession(currentUserName); + ProgramABConfig c = (ProgramABConfig) config; + return startSession(c.currentUserName); } // FIXME - it should just set the current userName only @@ -817,25 +761,7 @@ public void addCategory(String pattern, String template, String that) { public void addCategory(String pattern, String template) { addCategory(pattern, template, "*"); } - - /** - * writeAndQuit will write brain to disk For learn.aiml is concerned - */ - public void writeAndQuit() { - // write out all bots aiml & save all predicates for all sessions? - for (BotInfo bot : bots.values()) { - if (bot.isActive()) { - try { - savePredicates(); - // important to save learnf.aiml - // bot.writeQuit(); - } catch (IOException e1) { - log.error("saving predicates threw", e1); - } - } - } - } - + /** * Verifies and adds a new path to the search directories for bots * @@ -867,7 +793,7 @@ public String addBotPath(String path) { bots.put(botInfo.name, botInfo); botInfo.img = getBotImage(botInfo.name); - setCurrentBotName(botInfo.name); + // setCurrentBotName(botInfo.name); broadcastState(); } else { error("invalid bot path - a bot must be a directory with a subdirectory named \"aiml\""); @@ -887,13 +813,15 @@ public String setPath(String path) { } public void setCurrentBotName(String botName) { - this.currentBotName = botName; + ProgramABConfig c = (ProgramABConfig) config; + c.currentBotName = botName; invoke("getBotImage", botName); broadcastState(); } public void setCurrentUserName(String currentUserName) { - this.currentUserName = currentUserName; + ProgramABConfig c = (ProgramABConfig) config; + c.currentUserName = currentUserName; broadcastState(); } @@ -906,11 +834,13 @@ public String getSessionKey(String userName, String botName) { } public String getCurrentUserName() { - return currentUserName; + ProgramABConfig c = (ProgramABConfig) config; + return c.currentUserName; } public String getCurrentBotName() { - return currentBotName; + ProgramABConfig c = (ProgramABConfig) config; + return c.currentBotName; } /** @@ -1021,7 +951,7 @@ public void attach(Attachable attachable) { @Override public void stopService() { super.stopService(); - writeAndQuit(); + savePredicates(); } public boolean setPeerSearch(boolean b) { @@ -1041,6 +971,8 @@ public void startService() { logging.setLevel("class org.myrobotlab.programab.MrlSraixHandler", "DEBUG"); logPublisher.start(); + scanForBots(getResourceDir()); + } @Override /* FIXME - just do this once in abstract */ @@ -1091,7 +1023,8 @@ public String publishLog(String msg) { } public BotInfo getBotInfo() { - return getBotInfo(currentBotName); + ProgramABConfig c = (ProgramABConfig) config; + return getBotInfo(c.currentBotName); } /** @@ -1171,79 +1104,43 @@ public void saveAimlFile(String botName, String filename, String data) { @Override public ServiceConfig getConfig() { - ProgramABConfig config = (ProgramABConfig) super.getConfig(); - // REMOVED from overlap with subscriptions - // Set listeners = getAttached("publishText"); - // config.textListeners = listeners.toArray(new String[listeners.size()]); - - // listeners = getAttached("publishUtterance"); - // config.utteranceListeners = listeners.toArray(new - // String[listeners.size()]); - if (config.bots == null) { - config.bots = new ArrayList<>(); + ProgramABConfig c = (ProgramABConfig) super.getConfig(); + if (c.bots == null) { + c.bots = new ArrayList<>(); } - config.bots.clear(); + c.bots.clear(); for (BotInfo bot : bots.values()) { Path pathAbsolute = Paths.get(bot.path.getAbsolutePath()); Path pathBase = Paths.get(System.getProperty("user.dir")); Path pathRelative = pathBase.relativize(pathAbsolute); - config.bots.add(pathRelative.toString()); + c.bots.add(pathRelative.toString()); } - config.currentBotName = currentBotName; - config.currentUserName = currentUserName; - - return config; + return c; } @Override - public ServiceConfig apply(ServiceConfig c) { - ProgramABConfig config = (ProgramABConfig) super.apply(c); - if (config.bots != null && config.bots.size() > 0) { - bots.clear(); - for (String botPath : config.bots) { + public ServiceConfig apply(ServiceConfig config) { + ProgramABConfig c = (ProgramABConfig) super.apply(config); + if (c.bots != null && c.bots.size() > 0) { + // bots.clear(); + for (String botPath : c.bots) { addBotPath(botPath); } } - if (config.botDir != null) { - List botsFromScanning = scanForBots(config.botDir); - for (File file : botsFromScanning) { - addBotPath(file.getAbsolutePath()); - } - } - - if (config.currentBotName != null) { - setCurrentBotName(config.currentBotName); + if (c.currentBotName != null) { + setCurrentBotName(c.currentBotName); } - if (config.currentUserName != null) { - setCurrentUserName(config.currentUserName); + if (c.currentUserName != null) { + setCurrentUserName(c.currentUserName); } - // useGlobalSession = config.useGlobalSession; - - sleep = config.sleep; - - setCurrentSession(currentUserName, currentBotName); - - // REMOVED because of overlap with subscriptions - // if (config.textListeners != null) { - // for (String local : config.textListeners) { - // attachTextListener(local); - // } - // } - // - // if (config.utteranceListeners != null) { - // for (String local : config.utteranceListeners) { - // attachUtteranceListener(local); - // } - // } - - return config; + return c; } public static void main(String args[]) { @@ -1330,11 +1227,16 @@ synchronized public void onChangePredicate(Chat chat, String predicateName, Stri } /** - * Predicate updates are published here. Topic (one of the most important predicate change) is also published - * when it changes. Session is needed to extract current user and bot this is relevant to. - * @param session - session where the predicate change occurred - * @param name - name of predicate - * @param value - new value of predicate + * Predicate updates are published here. Topic (one of the most important + * predicate change) is also published when it changes. Session is needed to + * extract current user and bot this is relevant to. + * + * @param session + * - session where the predicate change occurred + * @param name + * - name of predicate + * @param value + * - new value of predicate * @return */ public PredicateEvent publishPredicate(Session session, String name, String value) { @@ -1344,12 +1246,12 @@ public PredicateEvent publishPredicate(Session session, String name, String valu event.botName = session.botInfo.name; event.name = name; event.value = value; - + if ("topic".equals(name) && value != null && !value.equals(session.currentTopic)) { invoke("publishTopic", new TopicChange(session.userName, session.botInfo.name, value, session.currentTopic)); session.currentTopic = value; } - + return event; } @@ -1406,18 +1308,22 @@ synchronized public void addCategoryToFile(Bot bot, Category c) { * wakes the global session up */ public void wake() { - sleep = false; + ProgramABConfig c = (ProgramABConfig) super.getConfig(); + c.sleep = false; } /** * sleeps the global session */ public void sleep() { - sleep = true; + ProgramABConfig c = (ProgramABConfig) super.getConfig(); + c.sleep = true; } @Override public void onUtterance(Utterance utterance) throws Exception { + + ProgramABConfig c = (ProgramABConfig) super.getConfig(); log.info("Utterance Received " + utterance); @@ -1448,8 +1354,8 @@ public void onUtterance(Utterance utterance) throws Exception { // TODO: don't talk to bots.. it won't go well.. // TODO: the discord api can provide use the list of mentioned users. // for now.. we'll just see if we see Mr. Turing as a substring. - sleep = (sleep || utterance.text.contains("@")) && !utterance.text.contains(botName); - if (!sleep) { + c.sleep = (c.sleep || utterance.text.contains("@")) && !utterance.text.contains(botName); + if (!c.sleep) { shouldIRespond = true; } } @@ -1483,17 +1389,33 @@ public void onUtterance(Utterance utterance) throws Exception { } } } + + /** + * This receiver can take a config published by another service and sync + * predicates from it + * @param cfg + */ + public void onConfig(ServiceConfig cfg) { + Yaml yaml = new Yaml(); + String yml = yaml.dumpAsMap(cfg); + Map cfgMap = yaml.load(yml); + + for (Map.Entry entry : cfgMap.entrySet()) { + if (entry.getValue() == null) { + setPredicate("cfg_" + entry.getKey(), null); + } else { + setPredicate("cfg_" + entry.getKey(), entry.getValue().toString()); + } + } + } @Override public Utterance publishUtterance(Utterance utterance) { return utterance; } - - + public TopicChange publishTopic(TopicChange topicChange) { return topicChange; } - - } diff --git a/src/main/java/org/myrobotlab/service/Python.java b/src/main/java/org/myrobotlab/service/Python.java index 5c6a2718d3..539623f6b7 100644 --- a/src/main/java/org/myrobotlab/service/Python.java +++ b/src/main/java/org/myrobotlab/service/Python.java @@ -270,7 +270,7 @@ public Object get(String pythonRefName) { Map exampleFiles = new TreeMap(); transient LinkedBlockingQueue inputQueue = new LinkedBlockingQueue(); - final transient InputQueue inputQueueThread; + transient InputQueue inputQueueThread; transient PythonInterpreter interp = null; transient Map interpThreads = new HashMap(); @@ -296,52 +296,6 @@ public Object get(String pythonRefName) { public Python(String n, String id) { super(n, id); - - log.info("created python {}", getName()); - - log.info("creating module directory pythonModules"); - new File("pythonModules").mkdir(); - - createPythonInterpreter(); - sleep(250); - - inputQueueThread = new InputQueue(this); - - // I love ServiceData ! - ServiceData sd = ServiceData.getLocalInstance(); - List sdt = sd.getAvailableServiceTypes(); - for (int i = 0; i < sdt.size(); ++i) { - MetaData st = sdt.get(i); - // FIXME - cache in "data" dir Or perhaps it should be pulled into - // resource directory during build time and packaged with jar - String file = String.format("%s/%s.py", st.getSimpleName(), st.getSimpleName()); - exampleFiles.put(st.getSimpleName(), file); - } - - localPythonFiles = getFileListing(); - - attachPythonConsole(); - - String selfReferenceScript = "from time import sleep\nfrom org.myrobotlab.framework import Platform\n" + "from org.myrobotlab.service import Runtime\n" - + "from org.myrobotlab.framework import Service\n" + "from org.myrobotlab.service import Python\n" - + String.format("%s = Runtime.getService(\"%s\")\n\n", CodecUtils.getSafeReferenceName(getName()), getName()) + "Runtime = Runtime.getInstance()\n\n" - + String.format("runtime = Runtime.getInstance()\n") + String.format("myService = Runtime.getService(\"%s\")\n", getName()); - // FIXME !!! myService is SO WRONG it will collide on more than 1 python - // service :( - PyObject compiled = getCompiledMethod("initializePython", selfReferenceScript, interp); - interp.exec(compiled); - - // initialize all the pre-existing service before python was created - Map services = Runtime.getLocalServices(); - for (ServiceInterface service : services.values()) { - if (service.isRunning()) { - onStarted(service.getName()); - } - } - - log.info("starting python {}", getName()); - inputQueueThread.start(); - log.info("started python {}", getName()); } public void newScript() { @@ -427,20 +381,24 @@ synchronized public void createPythonInterpreter() { Properties preprops = System.getProperties(); PythonInterpreter.initialize(preprops, props, new String[0]); - + interp = new PythonInterpreter(); + + addModulePath(getResourceDir() + fs + "modules"); + } public void addModulePath(String path) { PythonConfig c = (PythonConfig) config; if (c.modulePaths != null) { c.modulePaths.add(path); - if (interp != null) { - PySystemState sys = Py.getSystemState(); - sys.path.append(new PyString(path)); - log.info("Python System Path: {}", sys.path); - } } + + if (interp != null) { + PySystemState sys = Py.getSystemState(); + sys.path.append(new PyString(path)); + log.info("Python System Path: {}", sys.path); + } } public String eval(String method) { @@ -871,6 +829,51 @@ public void stopService() { // release the interpeter stop(); } + + public void init() { + + log.info("created python {}", getName()); + createPythonInterpreter(); + sleep(250); + + inputQueueThread = new InputQueue(this); + + // I love ServiceData ! + ServiceData sd = ServiceData.getLocalInstance(); + List sdt = sd.getAvailableServiceTypes(); + for (int i = 0; i < sdt.size(); ++i) { + MetaData st = sdt.get(i); + // FIXME - cache in "data" dir Or perhaps it should be pulled into + // resource directory during build time and packaged with jar + String file = String.format("%s/%s.py", st.getSimpleName(), st.getSimpleName()); + exampleFiles.put(st.getSimpleName(), file); + } + + localPythonFiles = getFileListing(); + + attachPythonConsole(); + + String selfReferenceScript = "from time import sleep\nfrom org.myrobotlab.framework import Platform\n" + "from org.myrobotlab.service import Runtime\n" + + "from org.myrobotlab.framework import Service\n" + "from org.myrobotlab.service import Python\n" + + String.format("%s = Runtime.getService(\"%s\")\n\n", CodecUtils.getSafeReferenceName(getName()), getName()) + "Runtime = Runtime.getInstance()\n\n" + + String.format("runtime = Runtime.getInstance()\n") + String.format("myService = Runtime.getService(\"%s\")\n", getName()); + // FIXME !!! myService is SO WRONG it will collide on more than 1 python + // service :( + PyObject compiled = getCompiledMethod("initializePython", selfReferenceScript, interp); + interp.exec(compiled); + + // initialize all the pre-existing service before python was created + Map services = Runtime.getLocalServices(); + for (ServiceInterface service : services.values()) { + if (service.isRunning()) { + onStarted(service.getName()); + } + } + + log.info("starting python {}", getName()); + inputQueueThread.start(); + log.info("started python {}", getName()); + } public boolean isOpenOnExecute() { return openOnExecute; @@ -919,6 +922,12 @@ public void onStopped(String fullname) { @Override public ServiceConfig apply(ServiceConfig c) { PythonConfig config = (PythonConfig) super.apply(c); + + // apply is the first method called after construction, + // since we offer the capability of executing scripts specified in config + // the interpreter must be configured and created here + init(); + if (config.startScripts != null && config.startScripts.size() > 0) { if (isRunning()) { diff --git a/src/main/java/org/myrobotlab/service/Runtime.java b/src/main/java/org/myrobotlab/service/Runtime.java index 8b00325a44..db0ae38b3a 100644 --- a/src/main/java/org/myrobotlab/service/Runtime.java +++ b/src/main/java/org/myrobotlab/service/Runtime.java @@ -90,6 +90,7 @@ import org.myrobotlab.service.meta.abstracts.MetaData; import org.myrobotlab.string.StringUtil; import org.slf4j.Logger; +import org.yaml.snakeyaml.constructor.ConstructorException; import picocli.CommandLine; @@ -4814,6 +4815,8 @@ public ServiceConfig readServiceConfig(String configName, String name) { if (check.exists()) { try { sc = CodecUtils.readServiceConfig(filename); + } catch (ConstructorException e) { + error("%s invalid %s %s. Please remove it from the file.", name, filename, e.getCause().getMessage()); } catch (IOException e) { error(e); } diff --git a/src/main/java/org/myrobotlab/service/Wikipedia.java b/src/main/java/org/myrobotlab/service/Wikipedia.java index 4842d48a60..6686d35ed6 100644 --- a/src/main/java/org/myrobotlab/service/Wikipedia.java +++ b/src/main/java/org/myrobotlab/service/Wikipedia.java @@ -195,7 +195,7 @@ private SearchResults searchWikipedia(String searchText, Boolean publishText, Bo } } else { - log.info("no response for %s", searchText); + log.info("no response for {}", searchText); } invoke("publishResults", results); diff --git a/src/main/java/org/myrobotlab/service/config/InMoov2Config.java b/src/main/java/org/myrobotlab/service/config/InMoov2Config.java index a7a4dc8e33..060820b5ba 100644 --- a/src/main/java/org/myrobotlab/service/config/InMoov2Config.java +++ b/src/main/java/org/myrobotlab/service/config/InMoov2Config.java @@ -15,83 +15,81 @@ public class InMoov2Config extends ServiceConfig { public int analogPinFromSoundCard = 53; - + public int audioPollsBySeconds = 2; - - public boolean audioSignalProcessing=false; - + + public boolean audioSignalProcessing = false; + public boolean batteryInSystem = false; - - public boolean customSound=false; - + + public boolean bootUpPlaySound = true; + + public boolean bootUpSound = true; + + public boolean customSound = false; + public boolean forceMicroOnIfSleeping = true; - + public boolean healthCheckActivated = false; - + public int healthCheckTimerMs = 60000; - + public boolean heartbeat = false; - /** * idle time measures the time the fsm is in an idle state */ public boolean idleTimer = true; - public boolean loadGestures = true; + public boolean loadGestures = false; + + public boolean loadInitScripts = false; /** * default to null - allow the OS to set it, unless explicilty set */ public String locale = null; // = "en-US"; - public boolean neoPixelBootGreen=true; + public boolean neoPixelBootGreen = true; public boolean neoPixelDownloadBlue = true; public boolean neoPixelErrorRed = true; - + public boolean neoPixelFlashWhenSpeaking = true; - - public boolean openCVFaceRecognizerActivated=true; - - public boolean openCVFlipPicture=false; - + + public boolean openCVFaceRecognizerActivated = true; + + public boolean openCVFlipPicture = false; + public boolean pirEnableTracking = false; - + /** - * play pir sounds when pir switching states - * sound located in data/InMoov2/sounds/pir-activated.mp3 - * sound located in data/InMoov2/sounds/pir-deactivated.mp3 + * play pir sounds when pir switching states sound located in + * data/InMoov2/sounds/pir-activated.mp3 sound located in + * data/InMoov2/sounds/pir-deactivated.mp3 */ public boolean pirPlaySounds = true; - + public boolean pirWakeUp = true; - + public boolean robotCanMoveHeadWhileSpeaking = true; - - + /** * startup and shutdown will pause inmoov - set the speed to this value then * attempt to move to rest */ public double shutdownStartupSpeed = 50; - + /** - * Sleep 5 minutes after last presence detected + * Sleep 5 minutes after last presence detected */ - public int sleepTimeoutMs=300000; + public int sleepTimeoutMs = 300000; - public boolean startBrainOnBoot = true; - - public boolean startMouthOnBoot = true; - - public boolean startupSound = true; + public int trackingTimeoutMs = 10000; - public int trackingTimeoutMs=10000; - public String unlockInsult = "forgive me"; - + public boolean virtual = false; public InMoov2Config() { @@ -101,9 +99,13 @@ public InMoov2Config() { public Plan getDefault(Plan plan, String name) { super.getDefault(plan, name); - // peers FIXME global opencv + // peers which autostart by default addDefaultPeerConfig(plan, name, "audioPlayer", "AudioFile", true); - addDefaultPeerConfig(plan, name, "chatBot", "ProgramAB", false); + addDefaultPeerConfig(plan, name, "htmlFilter", "HtmlFilter", true); + addDefaultPeerConfig(plan, name, "mouth", "MarySpeech", true); + + addDefaultPeerConfig(plan, name, "chatBot", "ProgramAB", true); + addDefaultPeerConfig(plan, name, "controller3", "Arduino", false); addDefaultPeerConfig(plan, name, "controller4", "Arduino", false); addDefaultPeerConfig(plan, name, "ear", "WebkitSpeechRecognition", false); @@ -112,13 +114,11 @@ public Plan getDefault(Plan plan, String name) { addDefaultPeerConfig(plan, name, "gpt3", "Gpt3", false); addDefaultPeerConfig(plan, name, "head", "InMoov2Head", false); addDefaultPeerConfig(plan, name, "headTracking", "Tracking", false); - addDefaultPeerConfig(plan, name, "htmlFilter", "HtmlFilter", false); addDefaultPeerConfig(plan, name, "imageDisplay", "ImageDisplay", false); addDefaultPeerConfig(plan, name, "leap", "LeapMotion", false); addDefaultPeerConfig(plan, name, "left", "Arduino", false); addDefaultPeerConfig(plan, name, "leftArm", "InMoov2Arm", false); addDefaultPeerConfig(plan, name, "leftHand", "InMoov2Hand", false); - addDefaultPeerConfig(plan, name, "mouth", "MarySpeech", false); addDefaultPeerConfig(plan, name, "mouthControl", "MouthControl", false); addDefaultPeerConfig(plan, name, "neoPixel", "NeoPixel", false); addDefaultPeerConfig(plan, name, "opencv", "OpenCV", false); @@ -171,7 +171,20 @@ public Plan getDefault(Plan plan, String name) { chatBot.listeners = new ArrayList<>(); } chatBot.listeners.add(new Listener("publishText", name + ".htmlFilter", "onText")); - chatBot.botDir = "data/ProgramAB"; + chatBot.startTopic = "BootUp"; + chatBot.bots.add("data/ProgramAB/brain"); + chatBot.bots.add("data/ProgramAB/cn-ZH"); + chatBot.bots.add("data/ProgramAB/de-DE"); + chatBot.bots.add("data/ProgramAB/en-US"); + chatBot.bots.add("data/ProgramAB/es-ES"); + chatBot.bots.add("data/ProgramAB/fi-FI"); + chatBot.bots.add("data/ProgramAB/fr-FR"); + chatBot.bots.add("data/ProgramAB/hi-IN"); + chatBot.bots.add("data/ProgramAB/it-IT"); + chatBot.bots.add("data/ProgramAB/nl-NL"); + chatBot.bots.add("data/ProgramAB/pt-PT"); + chatBot.bots.add("data/ProgramAB/ru-RU"); + chatBot.bots.add("data/ProgramAB/tr-TR"); HtmlFilterConfig htmlFilter = (HtmlFilterConfig) plan.get(getPeerName("htmlFilter")); // htmlFilter.textListeners = new String[] { name + ".mouth" }; @@ -182,6 +195,8 @@ public Plan getDefault(Plan plan, String name) { // onStarted // == Peer - mouth ============================= // setup name references to different services + // LocalSpeechConfig mouth = (LocalSpeechConfig) + // plan.get(getPeerName("mouth")); MarySpeechConfig mouth = (MarySpeechConfig) plan.get(getPeerName("mouth")); mouth.speechRecognizers = new String[] { name + ".ear" }; @@ -260,7 +275,8 @@ public Plan getDefault(Plan plan, String name) { simulator.cameraLookAt = name + ".torso.lowStom"; FiniteStateMachineConfig fsm = (FiniteStateMachineConfig) plan.get(getPeerName("fsm")); - // TODO - events easily gotten from InMoov data ?? auto callbacks in python if exists ? + // TODO - events easily gotten from InMoov data ?? auto callbacks in python + // if exists ? fsm.current = "boot"; fsm.transitions.add(new Transition("boot", "configStarted", "applyingConfig")); fsm.transitions.add(new Transition("applyingConfig", "getUserInfo", "getUserInfo")); @@ -270,14 +286,12 @@ public Plan getDefault(Plan plan, String name) { fsm.transitions.add(new Transition("systemCheck", "systemCheckFinished", "awake")); fsm.transitions.add(new Transition("awake", "sleep", "sleeping")); - - PirConfig pir = (PirConfig) plan.get(getPeerName("pir")); pir.pin = "23"; pir.controller = name + ".left"; pir.listeners = new ArrayList<>(); pir.listeners.add(new Listener("publishPirOn", name, "onPirOn")); - + // == Peer - random ============================= RandomConfig random = (RandomConfig) plan.get(getPeerName("random")); random.enabled = false; @@ -386,17 +400,19 @@ public Plan getDefault(Plan plan, String name) { plan.remove(name + ".eyeTracking.controller"); plan.remove(name + ".eyeTracking.controller.serial"); plan.remove(name + ".eyeTracking.cv"); - + // inmoov2 default listeners listeners = new ArrayList<>(); // FIXME - should be getPeerName("neoPixel") listeners.add(new Listener("publishFlash", name + ".neoPixel", "onLedDisplay")); listeners.add(new Listener("publishEvent", name + ".fsm")); - + + listeners.add(new Listener("publishPlayAudioFile", name + ".audioPlayer")); + // remove the auto-added starts in the plan's runtime RuntimConfig.registry plan.removeStartsWith(name + "."); - + // rtConfig.add(name); // <-- adding i01 / not needed return plan; diff --git a/src/main/java/org/myrobotlab/service/config/ProgramABConfig.java b/src/main/java/org/myrobotlab/service/config/ProgramABConfig.java index b0bf8975c0..53ac7747bc 100644 --- a/src/main/java/org/myrobotlab/service/config/ProgramABConfig.java +++ b/src/main/java/org/myrobotlab/service/config/ProgramABConfig.java @@ -7,18 +7,37 @@ public class ProgramABConfig extends ServiceConfig { - public String currentBotName = "Alice"; - public String currentUserName; - public String[] textFilters; - // public String[] textListeners; - // public String[] utteranceListeners; - public String botDir; + /** + * list of bot directories that will be available + */ public List bots = new ArrayList<>(); + + /** + * current sessions bot name, it must match a botname that was scanned + * currently with ProgramAB Alice, Dr.Who, Mr. Turing and Ency + */ + public String currentBotName = "Alice"; /** + * User name currently interacting with the bot. Setting it here will + * default it. + */ + public String currentUserName = "human"; + + /** + * sleep current state of the sleep if globalSession is used true : ProgramAB + * is sleeping and wont respond false : ProgramAB is not sleeping and any + * response requested will be processed * current sleep/wake value */ public boolean sleep = false; + + /** + * topic to start with, if null then topic will be loaded from predicates of + * a new session if available, this means a config/{username}.predicates.txt + * will need to exist with a topic field + */ + public String startTopic = "default"; @Override public Plan getDefault(Plan plan, String name) { diff --git a/src/main/java/org/myrobotlab/service/data/LedDisplayData.java b/src/main/java/org/myrobotlab/service/data/LedDisplayData.java index 92ce88ba38..9971f322b7 100644 --- a/src/main/java/org/myrobotlab/service/data/LedDisplayData.java +++ b/src/main/java/org/myrobotlab/service/data/LedDisplayData.java @@ -9,7 +9,7 @@ */ public class LedDisplayData { - public String action; // fill | flash | play animation | stop | clear + public String action = "flash"; // fill | flash | play animation | stop | clear public int red; public int green; public int blue; @@ -25,4 +25,14 @@ public class LedDisplayData { public long interval = 500; + public LedDisplayData() {}; + + public LedDisplayData(int red, int green, int blue) { + this.red = red; + this.green = green; + this.blue = blue; + + }; + + } diff --git a/src/main/resources/resource/Intro/InMoov01_start.py b/src/main/resources/resource/Intro/InMoov01_start.py deleted file mode 100644 index f03e5038bd..0000000000 --- a/src/main/resources/resource/Intro/InMoov01_start.py +++ /dev/null @@ -1,8 +0,0 @@ -######################################### -# InMoov01_start.py -# categories: inmoov -# more info @: http://myrobotlab.org/service/InMoov -######################################### -# uncomment for virtual hardware -# Platform.setVirtual(True) -i01 = runtime.start('i01', 'InMoov2') \ No newline at end of file diff --git a/src/main/resources/resource/Python/modules/myrobotlab/README.md b/src/main/resources/resource/Python/modules/myrobotlab/README.md new file mode 100644 index 0000000000..236f1c03b1 --- /dev/null +++ b/src/main/resources/resource/Python/modules/myrobotlab/README.md @@ -0,0 +1,26 @@ +# MyRobotLab Python Library + +The MyRobotLab Python library provides a simple way to connect to and control a MyRobotLab service. MyRobotLab is an open-source robotics framework that allows you to create and control robots using a variety of languages and platforms. + +## Installation +Already installed when myrobotlab services are installed + +## Usage + +To use the MyRobotLab Python library, you'll first need to connect to a MyRobotLab service. You can do this using the `connect` function: + +```python +import myrobotlab + +# default will be +myrobotlab.connect() + +# or if connecting to a service running on a different host +myrobotlab.connect('raspi', 25333) +``` + +## Contributing +Contributions to the MyRobotLab Python library are always welcome. To contribute, simply fork the repository, make your changes, and submit a pull request. + +## License +The MyRobotLab Python library is licensed under the MIT License. \ No newline at end of file diff --git a/src/main/resources/resource/Python/modules/myrobotlab/__init__.py b/src/main/resources/resource/Python/modules/myrobotlab/__init__.py new file mode 100644 index 0000000000..687278f19a --- /dev/null +++ b/src/main/resources/resource/Python/modules/myrobotlab/__init__.py @@ -0,0 +1,8 @@ +# inmoov2/__init__.py +# from . import myrobotlab + +"""MyRobotLab - a Python library for robotics.""" + +from .myrobotlab import connect +__version__ = '0.9' +__doc__ = """Open Source Framework for Robotics and Creative Machine Control""" \ No newline at end of file diff --git a/src/main/resources/resource/Python/modules/myrobotlab/myrobotlab.py b/src/main/resources/resource/Python/modules/myrobotlab/myrobotlab.py new file mode 100644 index 0000000000..5aab58a987 --- /dev/null +++ b/src/main/resources/resource/Python/modules/myrobotlab/myrobotlab.py @@ -0,0 +1,24 @@ + +class InMoov2(object): + """Library to support inmoov2 in Python + Intended to be used with Jython or Py4j. + This will be a singleton class. + + """ + + def __init__(self): + self.name = "MyRobotLab" + self.connections = {} + # TODO initialize any singleton state variables here + + def connect(self, host="127.0.0.1", port=25333): + + self.connections[host + ":" + str(port)] = { + "host": host, + "port": port, + } + + print("connecting.....") + +_myrobotlab = InMoov2() +connect = _myrobotlab.connect \ No newline at end of file diff --git a/src/main/resources/resource/WebGui/app/service/js/IntroGui.js b/src/main/resources/resource/WebGui/app/service/js/IntroGui.js index 963741a2b0..ac26166768 100644 --- a/src/main/resources/resource/WebGui/app/service/js/IntroGui.js +++ b/src/main/resources/resource/WebGui/app/service/js/IntroGui.js @@ -64,6 +64,12 @@ angular.module('mrlapp.service.IntroGui', []).controller('IntroGuiCtrl', ['$scop return ret } + $scope.start = function(name, type) { + msg.sendTo('runtime', 'start', name, type) + return ret + } + + // this method initializes subPanels when a new service becomes available this.onRegistered = function(panel) { if (panelNames.has(panel.displayName)) { diff --git a/src/main/resources/resource/WebGui/app/service/js/ProgramABGui.js b/src/main/resources/resource/WebGui/app/service/js/ProgramABGui.js index 4e3b0b3d3e..3172e43d67 100644 --- a/src/main/resources/resource/WebGui/app/service/js/ProgramABGui.js +++ b/src/main/resources/resource/WebGui/app/service/js/ProgramABGui.js @@ -41,9 +41,6 @@ angular.module('mrlapp.service.ProgramABGui', []).controller('ProgramABGuiCtrl', properties: false } - // grab defaults. - $scope.newUserName = $scope.service.currentUserName - $scope.chatLog = [] // start info status @@ -54,7 +51,7 @@ angular.module('mrlapp.service.ProgramABGui', []).controller('ProgramABGuiCtrl', // use another scope var to transfer/merge selection // from user - service.currentSession is always read-only // all service data should never be written to, only read from - $scope.currentUserName = service.currentUserName + $scope.currentUserName = service.config.currentUserName $scope.service = service $scope.currentSessionKey = $scope.getCurrentSessionKey() @@ -124,7 +121,7 @@ angular.module('mrlapp.service.ProgramABGui', []).controller('ProgramABGuiCtrl', var textData = data $scope.chatLog.unshift({ type: 'Bot', - name: $scope.service.currentBotName, + name: $scope.service.config.currentBotName, text: $sce.trustAsHtml(data.msg) }) $scope.lastResponse = textData @@ -169,22 +166,22 @@ angular.module('mrlapp.service.ProgramABGui', []).controller('ProgramABGuiCtrl', $scope.getAimlFile = function(filename) { $scope.aimlFile = filename console.log('getting aiml file ' + filename) - msg.send('getAimlFile', $scope.service.currentBotName, filename) + msg.send('getAimlFile', $scope.service.config.currentBotName, filename) $scope.tabs.selected = 2 } $scope.saveAimlFile = function() { - msg.send("saveAimlFile", $scope.service.currentBotName, $scope.aimlFile, $scope.aimlFileData.data) + msg.send("saveAimlFile", $scope.service.config.currentBotName, $scope.aimlFile, $scope.aimlFileData.data) } $scope.setSessionKey = function() { - msg.send("setCurrentUserName", $scope.service.currentUserName) - msg.send("setCurrentBotName", $scope.service.currentBotName) + msg.send("setCurrentUserName", $scope.service.config.currentUserName) + msg.send("setCurrentBotName", $scope.service.config.currentBotName) } $scope.getBotInfo = function() { if ($scope.service && $scope.service.bots){ - return $scope.service.bots[$scope.service.currentBotName] + return $scope.service.bots[$scope.service.config.currentBotName] } return null } @@ -200,7 +197,7 @@ angular.module('mrlapp.service.ProgramABGui', []).controller('ProgramABGuiCtrl', } $scope.getCurrentSessionKey = function() { - return $scope.service.currentUserName + ' <-> ' + $scope.service.currentBotName + return $scope.service.config.currentUserName + ' <-> ' + $scope.service.config.currentBotName } $scope.test = function(session, utterance) { @@ -208,8 +205,8 @@ angular.module('mrlapp.service.ProgramABGui', []).controller('ProgramABGuiCtrl', } $scope.getSessionResponse = function(utterance) { - console.info("SESSION GET RESPONSE (" + $scope.currentUserName + " " + $scope.service.currentBotName + ")") - $scope.getResponse($scope.currentUserName, $scope.service.currentBotName, utterance) + console.info("SESSION GET RESPONSE (" + $scope.currentUserName + " " + $scope.service.config.currentBotName + ")") + $scope.getResponse($scope.currentUserName, $scope.service.config.currentBotName, utterance) } $scope.getResponse = function(username, botname, utterance) { @@ -254,7 +251,9 @@ angular.module('mrlapp.service.ProgramABGui', []).controller('ProgramABGuiCtrl', $scope.getProperty = function(propName) { try { - return $scope.getBotInfo()['properties'][propName] + if ($scope.getBotInfo() && $scope.getBotInfo()['properties']){ + return $scope.getBotInfo()['properties'][propName] + } } catch (error){ console.warn('getProperty(' + propName + ') not found') return null @@ -277,6 +276,14 @@ angular.module('mrlapp.service.ProgramABGui', []).controller('ProgramABGuiCtrl', console.log('aceChanged') } + $scope.getBotPath = function(e) { + if ($scope.service?.bots[$scope.service?.config?.currentBotName]?.path){ + return $scope.service?.bots[$scope.service?.config.currentBotName].path + } + return null + } + + $scope.getStatusLabel = function(level) { if (level == 'error') { return 'row label col-md-12 label-danger' diff --git a/src/main/resources/resource/WebGui/app/service/views/IntroGui.html b/src/main/resources/resource/WebGui/app/service/views/IntroGui.html index e0935f6dac..cf3b3b6551 100644 --- a/src/main/resources/resource/WebGui/app/service/views/IntroGui.html +++ b/src/main/resources/resource/WebGui/app/service/views/IntroGui.html @@ -27,7 +27,7 @@   -
+
 
diff --git a/src/main/resources/resource/WebGui/app/service/views/ProgramABGui.html b/src/main/resources/resource/WebGui/app/service/views/ProgramABGui.html index 26ff84dd23..8a9d2876c0 100644 --- a/src/main/resources/resource/WebGui/app/service/views/ProgramABGui.html +++ b/src/main/resources/resource/WebGui/app/service/views/ProgramABGui.html @@ -19,11 +19,11 @@ - +
- + @@ -48,8 +48,9 @@ + topic {{predicates['topic']}} @@ -78,7 +79,7 @@

Location

- {{service.config.botDir}} + {{getBotPath()}} diff --git a/src/test/resources/ProgramAB/bots/lloyd/aiml/lloyd.aiml b/src/test/resources/ProgramAB/bots/lloyd/aiml/lloyd.aiml index f23f2f513c..257d55e668 100644 --- a/src/test/resources/ProgramAB/bots/lloyd/aiml/lloyd.aiml +++ b/src/test/resources/ProgramAB/bots/lloyd/aiml/lloyd.aiml @@ -3,10 +3,10 @@ * -