From 689ae2a8baa1ca4874a508a7ae5a39f57f369cef Mon Sep 17 00:00:00 2001 From: grog Date: Sun, 5 Nov 2023 07:18:08 -0800 Subject: [PATCH 1/2] fixed npe in onPirOn --- src/main/java/org/myrobotlab/service/InMoov2.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/myrobotlab/service/InMoov2.java b/src/main/java/org/myrobotlab/service/InMoov2.java index 247d8d37db..bd794195b8 100644 --- a/src/main/java/org/myrobotlab/service/InMoov2.java +++ b/src/main/java/org/myrobotlab/service/InMoov2.java @@ -927,10 +927,13 @@ public void onPirOn() { led.interval = 500; // FIXME flash on config.flashOnBoot invoke("publishFlash"); - String botState = chatBot.getPredicate("botState"); - if ("sleeping".equals(botState)) { - invoke("publishEvent", "WAKE"); - } + ProgramAB chatBot = (ProgramAB)getPeer("chatBot"); + if (chatBot != null) { + String botState = chatBot.getPredicate("botState"); + if ("sleeping".equals(botState)) { + invoke("publishEvent", "WAKE"); + } + } } // GOOD GOOD GOOD - LOOPBACK - flexible and replacable by python From b521675fed268c3c22f1cd7a6cd09e921d65fb33 Mon Sep 17 00:00:00 2001 From: grog Date: Sat, 11 Nov 2023 08:48:50 -0800 Subject: [PATCH 2/2] random service update --- .../org/myrobotlab/service/JMonkeyEngine.java | 2 +- .../java/org/myrobotlab/service/Random.java | 262 ++++++++++-------- .../myrobotlab/service/meta/RandomMeta.java | 5 +- src/main/resources/resource/Random/Random.py | 54 +++- .../WebGui/app/service/views/RandomGui.html | 12 +- .../org/myrobotlab/service/RandomTest.java | 18 +- 6 files changed, 212 insertions(+), 141 deletions(-) diff --git a/src/main/java/org/myrobotlab/service/JMonkeyEngine.java b/src/main/java/org/myrobotlab/service/JMonkeyEngine.java index fbc5ef5e99..a22156778e 100644 --- a/src/main/java/org/myrobotlab/service/JMonkeyEngine.java +++ b/src/main/java/org/myrobotlab/service/JMonkeyEngine.java @@ -2157,7 +2157,7 @@ public void simpleInitApp() { new File(getDataDir()).mkdirs(); new File(getResourceDir()).mkdirs(); - // assetManager.registerLocator("./", FileLocator.class); + assetManager.registerLocator("./", FileLocator.class); assetManager.registerLocator(getDataDir(), FileLocator.class); assetManager.registerLocator(assetsDir, FileLocator.class); assetManager.registerLocator(modelsDir, FileLocator.class); diff --git a/src/main/java/org/myrobotlab/service/Random.java b/src/main/java/org/myrobotlab/service/Random.java index dca027aa3a..71ca99616f 100644 --- a/src/main/java/org/myrobotlab/service/Random.java +++ b/src/main/java/org/myrobotlab/service/Random.java @@ -32,6 +32,10 @@ public class Random extends Service { public final static Logger log = LoggerFactory.getLogger(Random.class); + transient private RandomProcessor processor = null; + + transient private final Object lock = new Object(); + /** * * RandomMessage is used to contain the ranges of values and intervals for @@ -39,14 +43,15 @@ public class Random extends Service { * */ static public class RandomMessage { + public String taskName; public String name; public String method; public Range[] data; public boolean enabled = true; public long minIntervalMs; public long maxIntervalMs; - public long interval; public boolean oneShot = false; + public transient long nextProcessTimeTs = 0; public RandomMessage() { } @@ -103,6 +108,23 @@ public double getRandom(double min, double max) { return min + (Math.random() * (max - min)); } + public void addRandom(String taskName, long minIntervalMs, long maxIntervalMs, String name, String method, Integer... values) { + addRandom(taskName, minIntervalMs, maxIntervalMs, name, method, toRanges((Object[]) values)); + } + + public void addRandom(String taskName, long minIntervalMs, long maxIntervalMs, String name, String method, Double... values) { + addRandom(taskName, minIntervalMs, maxIntervalMs, name, method, toRanges((Object[]) values)); + } + + // FIXME - test this + public void addRandom(String taskName, long minIntervalMs, long maxIntervalMs, String name, String method) { + addRandom(taskName, minIntervalMs, maxIntervalMs, name, method, toRanges((Object[]) null)); + } + + public void addRandom(String taskName, long minIntervalMs, long maxIntervalMs, String name, String method, String... params) { + addRandom(taskName, minIntervalMs, maxIntervalMs, name, method, setRange((Object[]) params)); + } + public void addRandom(long minIntervalMs, long maxIntervalMs, String name, String method, Integer... values) { addRandom(minIntervalMs, maxIntervalMs, name, method, toRanges((Object[]) values)); } @@ -170,74 +192,105 @@ public void removeAll() { } public void addRandom(long minIntervalMs, long maxIntervalMs, String name, String method, Range... ranges) { + String taskName = String.format("%s.%s", name, method); + addRandom(taskName, minIntervalMs, maxIntervalMs, name, method, ranges); + } - RandomMessage msg = new RandomMessage(); - msg.name = name; - msg.method = method; - msg.minIntervalMs = minIntervalMs; - msg.maxIntervalMs = maxIntervalMs; - msg.data = ranges; - - String key = String.format("%s.%s", name, method); - randomData.put(key, msg); - - msg.interval = getRandom(minIntervalMs, maxIntervalMs); - log.info("add random message {} in {} ms", key, msg.interval); - if (enabled) { - // only if global enabled is enabled do we start the task - addTask(key, 0, msg.interval, "process", key); - } + public void addRandom(String taskName, long minIntervalMs, long maxIntervalMs, String name, String method, Range... ranges) { + + RandomMessage data = new RandomMessage(); + data.name = name; + data.method = method; + data.minIntervalMs = minIntervalMs; + data.maxIntervalMs = maxIntervalMs; + data.data = ranges; + data.enabled = true; + + randomData.put(taskName, data); + + log.info("add random message {} in {} to {} ms", taskName, data.minIntervalMs, data.maxIntervalMs); broadcastState(); } - public void process(String key) { - // if (!enabled) { - // return; - // } + private class RandomProcessor extends Thread { - RandomMessage msg = randomData.get(key); - if (msg == null || !msg.enabled) { - return; + public RandomProcessor(String name) { + super(name); } - Message m = Message.createMessage(getName(), msg.name, msg.method, null); - if (msg.data != null) { - List data = new ArrayList<>(); - - for (int i = 0; i < msg.data.length; ++i) { - Object o = msg.data[i]; - if (o instanceof Range) { - Range range = (Range) o; - Object param = null; - - if (range.set != null) { - int rand = getRandom(0, range.set.size() - 1); - param = range.set.get(rand); - } else if (range.min instanceof Double) { - param = getRandom((Double) range.min, (Double) range.max); - } else if (range.min instanceof Long) { - param = getRandom((Long) range.min, (Long) range.max); - } else if (range.min instanceof Integer) { - param = getRandom((Integer) range.min, (Integer) range.max); + public void run() { + while (enabled) { + try { + // minimal interval time for processor to check + // and see if any random event needs processing + + sleep(200); + for (String key : randomData.keySet()) { + + long now = System.currentTimeMillis(); + + RandomMessage randomEntry = randomData.get(key); + if (!randomEntry.enabled) { + continue; + } + + // first time set + if (randomEntry.nextProcessTimeTs == 0) { + randomEntry.nextProcessTimeTs = now + getRandom((Long) randomEntry.minIntervalMs, (Long) randomEntry.maxIntervalMs); + } + + if (now < randomEntry.nextProcessTimeTs) { + // this entry isn't ready + continue; + } + + Message m = Message.createMessage(getName(), randomEntry.name, randomEntry.method, null); + if (randomEntry.data != null) { + List data = new ArrayList<>(); + + for (int i = 0; i < randomEntry.data.length; ++i) { + Object o = randomEntry.data[i]; + if (o instanceof Range) { + Range range = (Range) o; + Object param = null; + + if (range.set != null) { + int rand = getRandom(0, range.set.size() - 1); + param = range.set.get(rand); + } else if (range.min instanceof Double) { + param = getRandom((Double) range.min, (Double) range.max); + } else if (range.min instanceof Long) { + param = getRandom((Long) range.min, (Long) range.max); + } else if (range.min instanceof Integer) { + param = getRandom((Integer) range.min, (Integer) range.max); + } + + data.add(param); + } + } + m.data = data.toArray(); + } + m.sendingMethod = "process"; + log.debug("random msg @ {} ms {}", now - randomEntry.nextProcessTimeTs, m); + out(m); + + // auto-disable oneshot + if (randomEntry.oneShot) { + randomEntry.enabled = false; + } + + // reset next processing time + randomEntry.nextProcessTimeTs = now + getRandom((Long) randomEntry.minIntervalMs, (Long) randomEntry.maxIntervalMs); + } - data.add(param); + } catch (Exception e) { + error(e); } - } - m.data = data.toArray(); - } - m.sendingMethod = "process"; - log.info("random msg @ {} ms {}", msg.interval, m); - out(m); - - purgeTask(key); - if (!msg.oneShot) { - msg.interval = getRandom(msg.minIntervalMs, msg.maxIntervalMs); - // must re-schedule unless one shot - if (enabled) { - // only if global enabled is enabled do we start the task - addTask(key, 0, msg.interval, "process", key); - } + + } // while (enabled) { + + log.info("Random {}-processor terminating", getName()); } } @@ -280,12 +333,12 @@ public RandomConfig apply(RandomConfig c) { return c; } + @Deprecated /* use remove(String key) */ public RandomMessage remove(String name, String method) { return remove(String.format("%s.%s", name, method)); } public RandomMessage remove(String key) { - purgeTask(key); return randomData.remove(key); } @@ -294,79 +347,49 @@ public Set getKeySet() { } public void disable(String key) { - // exact match - if (key.contains(".")) { - RandomMessage msg = randomData.get(key); - if (msg == null) { - log.warn("cannot disable random event with key {}", key); - return; - } - randomData.get(key).enabled = false; - purgeTask(key); + + if (!randomData.containsKey(key)) { + error("disable cannot find key %s", key); return; } - // must be name - disable "all" for this service - for (RandomMessage msg : randomData.values()) { - if (msg.name.equals(key)) { - msg.enabled = false; - purgeTask(String.format("%s.%s", msg.name, msg.method)); - } - } + + randomData.get(key).enabled = false; } public void enable(String key) { - // exact match - if (key.contains(".")) { - RandomMessage msg = randomData.get(key); - if (msg == null) { - log.warn("cannot enable random event with key {}", key); - return; - } - randomData.get(key).enabled = true; - if (enabled) { - // only if global enabled is enabled do we start the task - addTask(key, 0, msg.interval, "process", key); - } + if (!randomData.containsKey(key)) { + error("disable cannot find key %s", key); return; } - // must be name - disable "all" for this service - String name = key; - for (RandomMessage msg : randomData.values()) { - if (msg.name.equals(name)) { - msg.enabled = true; - String fullKey = String.format("%s.%s", msg.name, msg.method); - if (enabled) { - // only if global enabled is enabled do we start the task - addTask(fullKey, 0, msg.interval, "process", fullKey); - } - } - } + randomData.get(key).enabled = true; } public void disable() { - // remove all timed attempts of processing random - // events - purgeTasks(); - enabled = false; - broadcastState(); + synchronized (lock) { + enabled = false; + processor = null; + broadcastState(); + } } public void enable() { - for (RandomMessage msg : randomData.values()) { - // re-enable tasks which were previously enabled - if (msg.enabled == true) { - String fullKey = String.format("%s.%s", msg.name, msg.method); - addTask(fullKey, 0, msg.interval, "process", fullKey); + synchronized (lock) { + enabled = true; + if (processor == null) { + processor = new RandomProcessor(String.format("%s-processor", getName())); + processor.start(); + // wait until thread starts + sleep(200); + } else { + info("%s already enabled"); } + broadcastState(); } - - enabled = true; - broadcastState(); } public void purge() { randomData.clear(); - purgeTasks(); + broadcastState(); } public Set getMethodsFromName(String serviceName) { @@ -395,13 +418,22 @@ public List methodQuery(String serviceName, String methodName) { } return MethodCache.getInstance().query(si.getClass().getCanonicalName(), methodName); } + + public Map getRandomEvents(){ + return randomData; + } + + public RandomMessage getRandomEvent(String key) { + return randomData.get(key); + } public static void main(String[] args) { try { LoggingFactory.init(Level.INFO); - + Runtime.setConfig("dev"); Runtime.start("c1", "Clock"); + Runtime.start("python", "Python"); Random random = (Random) Runtime.start("random", "Random"); diff --git a/src/main/java/org/myrobotlab/service/meta/RandomMeta.java b/src/main/java/org/myrobotlab/service/meta/RandomMeta.java index c18ebfa9a7..f7765982c2 100644 --- a/src/main/java/org/myrobotlab/service/meta/RandomMeta.java +++ b/src/main/java/org/myrobotlab/service/meta/RandomMeta.java @@ -17,13 +17,10 @@ public RandomMeta() { // add a cool description addDescription("provides a service for random message generation"); - // false will prevent it being seen in the ui - setAvailable(true); - // add dependencies if necessary // addDependency("com.twelvemonkeys.common", "common-lang", "3.1.1"); - setAvailable(false); + setAvailable(true); // add it to one or many categories addCategory("general"); diff --git a/src/main/resources/resource/Random/Random.py b/src/main/resources/resource/Random/Random.py index 7d5fa86f9b..84c79ab17a 100644 --- a/src/main/resources/resource/Random/Random.py +++ b/src/main/resources/resource/Random/Random.py @@ -7,34 +7,63 @@ ######################################### # start the service -python = runtime.start("python","Python") -random = runtime.start("random","Random") -clock = runtime.start("clock","Clock") +python = runtime.start("python", "Python") +random = runtime.start("random", "Random") +clock = runtime.start("clock", "Clock") + + +def happy(): + print("i am happy") + + +def sad(): + print("i am sad") + + +def angry(): + print("i am angry") + + +# add a named random task +random.addRandom("random emotion", 1000, 2000, "python", "exec", "happy()", "sad()", "angry()") + # enable random events random.enable() -# roll the dice every 1 to 2 seonds -random.addRandom(1000, 2000, "python", "roll_dice", random.intRange(1, 6)) + + def roll_dice(value): print("roll_dice " + str(value)) + +# roll the dice every 1 to 2 seonds +random.addRandom(1000, 2000, "python", "roll_dice", random.intRange(1, 6)) + + # add a complex dice -random.addRandom(1000, 2000, "python", "roll_complex_dice", random.doubleRange(1, 6)) def roll_complex_dice(value): print("roll_complex_dice " + str(value)) -# roll the dice every 1 to 2 seonds -random.addRandom(1000, 2000, "python", "random_color", random.setRange("red","green","blue","yellow")) +# roll the complex dice every 1 to 2 seonds +random.addRandom(1000, 2000, "python", "roll_complex_dice", random.doubleRange(1, 6)) + + def random_color(value): print("random_color " + str(value)) +# roll the dice every 1 to 2 seonds +random.addRandom(1000, 2000, "python", "random_color", random.setRange("red", "green", "blue", "yellow")) + + # do a complex multi parameter, multi-type method -random.addRandom(1000, 2000, "python", "kitchen_sink", random.intRange(1, 6), random.doubleRange(1, 6), random.setRange("red","green","blue","yellow"), random.setRange("bob","jane","fred","mary")) def kitchen_sink(dice, complex_dice, colors, names): print("kitchen_sink ", dice, complex_dice, colors, names) + +random.addRandom(1000, 2000, "python", "kitchen_sink", random.intRange(1, 6), random.doubleRange(1, 6), random.setRange("red","green","blue","yellow"), random.setRange("bob","jane","fred","mary")) + # set the interval on a clock between 1000 and 8000 # if you look in the UI you can see the clock interval changing random.addRandom(200, 500, "clock", "setInterval", random.intRange(1000, 8000)) @@ -43,13 +72,8 @@ def kitchen_sink(dice, complex_dice, colors, names): random.addRandom(200, 500, "clock", "startClock") random.addRandom(200, 500, "clock", "stopClock") - -# run it all for 8 seconds -sleep(8) - # disable single random event generator - must be explicit with name.method key random.disable("python.roll_dice") -sleep(8) # you know longer should see the python.roll_dice event firing - since it was explicitly disabled @@ -61,4 +85,4 @@ def kitchen_sink(dice, complex_dice, colors, names): # random.enable() # stop events and clear all random event ata -# random.purge() \ No newline at end of file +# random.purge() diff --git a/src/main/resources/resource/WebGui/app/service/views/RandomGui.html b/src/main/resources/resource/WebGui/app/service/views/RandomGui.html index 34c57ea2ca..b49a6365a0 100644 --- a/src/main/resources/resource/WebGui/app/service/views/RandomGui.html +++ b/src/main/resources/resource/WebGui/app/service/views/RandomGui.html @@ -12,10 +12,16 @@

( - + [ + {{param.min}} - {{param.max}} - {{item}} - + + + {{item}} + , + ] + , + ) every {{value.minIntervalMs}} ms to {{value.maxIntervalMs}} ms diff --git a/src/test/java/org/myrobotlab/service/RandomTest.java b/src/test/java/org/myrobotlab/service/RandomTest.java index 450ffa04da..cf0d85f94e 100644 --- a/src/test/java/org/myrobotlab/service/RandomTest.java +++ b/src/test/java/org/myrobotlab/service/RandomTest.java @@ -3,7 +3,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.util.Map; + import org.myrobotlab.framework.Service; +import org.myrobotlab.service.Random.RandomMessage; public class RandomTest extends AbstractServiceTest { @@ -25,8 +28,9 @@ public void testService() throws Exception { assertTrue("set interval 1000 base value", 1000 == clock.getInterval()); random.addRandom(0, 200, "clock", "setInterval", 5000, 10000); + random.enable(); - sleep(200); + sleep(300); assertTrue("should have method", random.getKeySet().contains("clock.setInterval")); @@ -45,13 +49,13 @@ public void testService() throws Exception { assertTrue("clock should be started 1", clock.isClockRunning()); // disable all of a services random events - random.disable("clock"); + random.disable("clock.startClock"); clock.stopClock(); sleep(200); assertTrue("clock should not be started", !clock.isClockRunning()); // enable all of a service's random events - random.enable("clock"); + random.enable("clock.startClock"); sleep(200); assertTrue("clock should be started 2", clock.isClockRunning()); @@ -77,7 +81,15 @@ public void testService() throws Exception { assertTrue("clock should not be started", !clock.isClockRunning()); assertTrue(String.format("random method 3 should be %d => 5000 values", clock.getInterval()), 5000 <= clock.getInterval()); assertTrue(String.format("random method 3 should be %d <= 10000 values",clock.getInterval()) , clock.getInterval() <= 10000); + + clock.stopClock(); + random.purge(); + Map events = random.getRandomEvents(); + assertTrue(events.size() == 0); + + random.addRandom("named task", 200, 500, "clock", "setInterval", 100, 1000, 10); + clock.releaseService(); random.releaseService();