diff --git a/.github/workflows/build.yml b/.github/build.yml similarity index 94% rename from .github/workflows/build.yml rename to .github/build.yml index c8d608bb4c..b510c17054 100644 --- a/.github/workflows/build.yml +++ b/.github/build.yml @@ -1,3 +1,5 @@ +# This file must be in the .github/workflows folder for git actions to work +# but currently is disabled, since builds are being done by jenkins name: Java CI on: @@ -82,3 +84,4 @@ jobs: tag_name: ${{ steps.version.outputs.version }} generate_release_notes: true body_path: ./release-template.md + diff --git a/Jenkinsfile b/Jenkinsfile index a0e8bed48e..7e85304616 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -6,7 +6,6 @@ * Cancel all jobs - Jenkins.instance.queue.clear() ***********************************************************************************/ - pipeline { // https://plugins.jenkins.io/agent-server-parameter/ // agent { label params['agent-name'] } @@ -51,43 +50,25 @@ pipeline { echo "VERSION ${VERSION}" echo "BUILD_NUMBER ${BUILD_NUMBER}" - - print params['agent-name'] // print System.properties['os.name'].toLowerCase() - access to java object requires permission changes script { - if (isUnix()) { sh ''' - echo isUnix true git --version java -version mvn -version ''' - echo sh(script: 'env|sort', returnStdout: true) - } else { - bat ''' - echo isUnix false - git --version - java -version - mvn -version - set - ''' } } - } } // stage build stage('compile') { steps { script { - if (isUnix()) { sh ''' mvn -Dbuild.number=${BUILD_NUMBER} -DskipTests -q clean compile ''' - } else { - bat(/"${MAVEN_HOME}\bin\mvn" -Dbuild.number=${BUILD_NUMBER} -DskipTests -q clean compile /) - } } } } // stage compile @@ -98,19 +79,12 @@ pipeline { } steps { script { - // TODO - integration tests ! - if (isUnix()) { sh ''' mvn test -Dtest=org.myrobotlab.framework.DependencyTest -q ''' - } else { - bat ''' - mvn test -Dtest=org.myrobotlab.framework.DependencyTest -q - ''' - } } } - } // stage verify + } // stage dependencies stage('verify') { when { @@ -118,16 +92,9 @@ pipeline { } steps { script { - // TODO - integration tests ! - if (isUnix()) { sh ''' mvn -Dfile.encoding=UTF-8 -DargLine="-Xmx1024m" verify --fail-fast -q ''' - } else { - bat ''' - mvn -Dfile.encoding=UTF-8 -DargLine="-Xmx1024m" verify --fail-fast -q - ''' - } } } } // stage verify @@ -135,54 +102,42 @@ pipeline { stage('package') { steps { script { - if (isUnix()) { sh ''' mvn -Dbuild.number=${BUILD_NUMBER} -DskipTests -q package ''' - } else { - bat(/"${MAVEN_HOME}\bin\mvn" -Dbuild.number=${BUILD_NUMBER} -DskipTests -q package /) - } } } - } // stage compile + } // stage package stage('javadoc') { - // when { - // // expression { params.javadoc == 'true' } - // expression { env.BRANCH_NAME == 'master' || env.BRANCH_NAME == 'develop' } - // } + when { + // expression { params.javadoc == 'true' } + expression { env.BRANCH_NAME == 'master' || env.BRANCH_NAME == 'develop' } + } steps { - script { - if (isUnix()) { sh ''' mvn -q javadoc:javadoc ''' - } else { - bat ''' - mvn -q javadoc:javadoc - ''' - } } - } } // stage javadoc - stage('archive-min') { - when { - expression { env.BRANCH_NAME != 'master' && env.BRANCH_NAME != 'develop' } - } + stage('archive') { + // when { + // expression { env.BRANCH_NAME != 'master' && env.BRANCH_NAME != 'develop' } + // } steps { - archiveArtifacts 'target/myrobotlab.jar, target/surefire-reports/*, target/*.exec' + archiveArtifacts 'target/myrobotlab.jar, target/surefire-reports/**, target/*.exec, target/site/**' } } - stage('archive-javadocs') { - when { - expression { env.BRANCH_NAME == 'master' || env.BRANCH_NAME == 'develop' } - } - steps { - archiveArtifacts 'target/myrobotlab.zip, target/surefire-reports/*, target/*.exec, target/site/**' - } - } + // stage('archive-javadocs') { + // when { + // expression { env.BRANCH_NAME == 'master' || env.BRANCH_NAME == 'develop' } + // } + // steps { + // archiveArtifacts 'target/myrobotlab.zip, target/surefire-reports/*, target/*.exec, target/site/**' + // } + // } // stage('jacoco') { // steps { @@ -192,9 +147,10 @@ pipeline { // } stage('publish-github') { - when { expression { env.BRANCH_NAME == 'master' || env.BRANCH_NAME == 'develop' } } + // when { expression { env.BRANCH_NAME == 'master' || env.BRANCH_NAME == 'develop' } } + when { expression { env.BRANCH_NAME == 'master'} } steps { - withCredentials([string(credentialsId: 'github-token-2', variable: 'token')]) { // var name "token" is set in cred config and is case senstive + withCredentials([string(credentialsId: 'supertick-github-token', variable: 'token')]) { // var name "token" is set in cred config and is case senstive echo "publishing ${VERSION_PREFIX}.${BUILD_NUMBER}" echo "version ${VERSION}" // for security - your supposed to make it non-interpretive single quotes and let the OS process the interpolation @@ -217,5 +173,12 @@ pipeline { } } + always { + // publish junit + junit 'target/surefire-reports/**/*.xml' + // Publish JaCoCo coverage report + jacoco(execPattern: '**/target/jacoco.exec') + } + } // post } // pipeline diff --git a/pom.xml b/pom.xml index 66429a97ae..1ddd46462c 100644 --- a/pom.xml +++ b/pom.xml @@ -2025,9 +2025,8 @@ **/integration/* - **/OpenCV* - + diff --git a/src/main/java/SampleAsyncCallback.java b/src/main/java/SampleAsyncCallback.java deleted file mode 100644 index dde262aae1..0000000000 --- a/src/main/java/SampleAsyncCallback.java +++ /dev/null @@ -1,511 +0,0 @@ - -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ - -import org.eclipse.paho.client.mqttv3.IMqttActionListener; -import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; -import org.eclipse.paho.client.mqttv3.IMqttToken; -import org.eclipse.paho.client.mqttv3.MqttAsyncClient; -import org.eclipse.paho.client.mqttv3.MqttCallback; -import org.eclipse.paho.client.mqttv3.MqttConnectOptions; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; - -/** - * A sample application that demonstrates how to use the MQTT v3 Client api in - * non-blocking callback/notification mode. - * - * It can be run from the command line in one of two modes: - as a publisher, - * sending a single message to a topic on the server - as a subscriber, - * listening for messages from the server - * - * There are three versions of the sample that implement the same features but - * do so using using different programming styles: - *
    - *
  1. Sample (this one) which uses the API which blocks until the operation - * completes
  2. - *
  3. SampleAsyncWait shows how to use the asynchronous API with waiters that - * block until an action completes
  4. - *
  5. SampleAsyncCallBack shows how to use the asynchronous API where events - * are used to notify the application when an action completes - *
  6. - *
- * - * If the application is run with the -h parameter then info is displayed that - * describes all of the options / parameters. - */ -public class SampleAsyncCallback implements MqttCallback { - - int state = BEGIN; - - static final int BEGIN = 0; - static final int CONNECTED = 1; - static final int PUBLISHED = 2; - static final int SUBSCRIBED = 3; - static final int DISCONNECTED = 4; - static final int FINISH = 5; - static final int ERROR = 6; - static final int DISCONNECT = 7; - - // Private instance variables - MqttAsyncClient client; - String brokerUrl; - private boolean quietMode; - private MqttConnectOptions conOpt; - private boolean clean; - Throwable ex = null; - Object waiter = new Object(); - boolean donext = false; - private String password; - private String userName; - - /** - * Constructs an instance of the sample client wrapper - * - * @param brokerUrl - * the url to connect to - * @param clientId - * the client id to connect with - * @param cleanSession - * clear state at end of connection or not (durable or non-durable - * subscriptions) - * @param quietMode - * whether debug should be printed to standard out - * @param userName - * the username to connect with - * @param password - * the password for the user - * @throws MqttException - * if an error happens - */ - public SampleAsyncCallback(String brokerUrl, String clientId, boolean cleanSession, boolean quietMode, String userName, String password) throws MqttException { - this.brokerUrl = brokerUrl; - this.quietMode = quietMode; - this.clean = cleanSession; - this.password = password; - this.userName = userName; - - try { - // Construct the object that contains connection parameters - // such as cleansession and LWAT - conOpt = new MqttConnectOptions(); - conOpt.setCleanSession(clean); - if (password != null) { - conOpt.setPassword(this.password.toCharArray()); - } - if (userName != null) { - conOpt.setUserName(this.userName); - } - - // Construct the MqttClient instance - client = new MqttAsyncClient(this.brokerUrl, clientId); - - // Set this wrapper as the callback handler - client.setCallback(this); - - } catch (MqttException e) { - e.printStackTrace(); - log("Unable to set up client: " + e.toString()); - System.exit(1); - } - } - - /** - * Publish / send a message to an MQTT server - * - * @param topicName - * the name of the topic to publish to - * @param qos - * the quality of service to delivery the message at (0,1,2) - * @param payload - * the set of bytes to send to the MQTT server - * @throws Throwable - * t - */ - public void publish(String topicName, int qos, byte[] payload) throws Throwable { - // Use a state machine to decide which step to do next. State change occurs - // when a notification is received that an MQTT action has completed - while (state != FINISH) { - switch (state) { - case BEGIN: - // Connect using a non blocking connect - MqttConnector con = new MqttConnector(); - con.doConnect(); - break; - case CONNECTED: - // Publish using a non blocking publisher - Publisher pub = new Publisher(); - pub.doPublish(topicName, qos, payload); - break; - case PUBLISHED: - state = DISCONNECT; - donext = true; - break; - case DISCONNECT: - Disconnector disc = new Disconnector(); - disc.doDisconnect(); - break; - case ERROR: - throw ex; - case DISCONNECTED: - state = FINISH; - donext = true; - break; - } - - waitForStateChange(10000); - } - } - - /** - * Wait for a maximum amount of time for a state change event to occur - * - * @param maxTTW - * maximum time to wait in milliseconds - * @throws MqttException - */ - private void waitForStateChange(int maxTTW) throws MqttException { - synchronized (waiter) { - if (!donext) { - try { - waiter.wait(maxTTW); - } catch (InterruptedException e) { - log("timed out"); - e.printStackTrace(); - } - - if (ex != null) { - throw (MqttException) ex; - } - } - donext = false; - } - } - - /** - * Subscribe to a topic on an MQTT server Once subscribed this method waits - * for the messages to arrive from the server that match the subscription. It - * continues listening for messages until the enter key is pressed. - * - * @param topicName - * to subscribe to (can be wild carded) - * @param qos - * the maximum quality of service to receive messages at for this - * subscription - * @throws Throwable - * t - */ - public void subscribe(String topicName, int qos) throws Throwable { - // Use a state machine to decide which step to do next. State change occurs - // when a notification is received that an MQTT action has completed - while (state != FINISH) { - switch (state) { - case BEGIN: - // Connect using a non blocking connect - MqttConnector con = new MqttConnector(); - con.doConnect(); - break; - case CONNECTED: - // Subscribe using a non blocking subscribe - Subscriber sub = new Subscriber(); - sub.doSubscribe(topicName, qos); - break; - case SUBSCRIBED: - return; - case DISCONNECT: - Disconnector disc = new Disconnector(); - disc.doDisconnect(); - break; - case ERROR: - throw ex; - case DISCONNECTED: - state = FINISH; - donext = true; - break; - } - waitForStateChange(10000); - } - } - - /** - * Utility method to handle logging. If 'quietMode' is set, this method does - * nothing - * - * @param message - * the message to log - */ - void log(String message) { - if (!quietMode) { - System.out.println(message); - } - } - - /****************************************************************/ - /* Methods to implement the MqttCallback interface */ - /****************************************************************/ - - /** - * @see MqttCallback#connectionLost(Throwable) - */ - @Override - public void connectionLost(Throwable cause) { - // Called when the connection to the server has been lost. - // An application may choose to implement reconnection - // logic at this point. This sample simply exits. - log("Connection to " + brokerUrl + " lost!" + cause); - System.exit(1); - } - - /** - * @see MqttCallback#deliveryComplete(IMqttDeliveryToken) - */ - @Override - public void deliveryComplete(IMqttDeliveryToken token) { - // Called when a message has been delivered to the - // server. The token passed in here is the same one - // that was returned from the original call to publish. - // This allows applications to perform asynchronous - // delivery without blocking until delivery completes. - // - // This sample demonstrates asynchronous deliver, registering - // a callback to be notified on each call to publish. - // - // The deliveryComplete method will also be called if - // the callback is set on the client - // - log("Delivery complete callback: Publish Completed " + token.getTopics()); - } - - /** - * @see MqttCallback#messageArrived(String, MqttMessage) - */ - @Override - public void messageArrived(String topic, MqttMessage message) throws MqttException { - // Called when a message arrives from the server that matches any - // subscription made by the client - System.out.println("Time:\t" + System.currentTimeMillis() + " Topic:\t" + topic + " Message:\t" + new String(message.getPayload()) + " QoS:\t" + message.getQos()); - } - - /****************************************************************/ - /* End of MqttCallback methods */ - /****************************************************************/ - static void printHelp() { - System.out.println("Syntax:\n\n" + " Sample [-h] [-a publish|subscribe] [-t ] [-m ]\n" - + " [-s 0|1|2] -b ] [-p ] [-i ]\n\n" + " -h Print this help text and quit\n" - + " -q Quiet mode (default is false)\n" + " -a Perform the relevant action (default is publish)\n" + " -t Publish/subscribe to instead of the default\n" - + " (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" + " -m Use instead of the default\n" - + " (\"Message from MQTTv3 Java client\")\n" + " -s Use this QoS instead of the default (2)\n" - + " -b Use this name/IP address instead of the default (localhost)\n" + " -p Use this port instead of the default (1883)\n\n" - + " -i Use this client ID instead of SampleJavaV3_\n" + " -c Connect to the server with a clean session (default is false)\n" - + " \n\n Security Options \n" + " -u Username \n" + " -z Password \n" + " \n\n SSL Options \n" + " -v SSL enabled; true - (default is false) " - + " -k Use this JKS format key store to verify the client\n" + " -w Passpharse to verify certificates in the keys store\n" - + " -r Use this JKS format keystore to verify the server\n" + " If javax.net.ssl properties have been set only the -v flag needs to be set\n" - + "Delimit strings containing spaces with \"\"\n\n" + "Publishers transmit a single message then disconnect from the server.\n" - + "Subscribers remain connected to the server and receive appropriate\n" + "messages until is pressed.\n\n"); - } - - /** - * Connect in a non blocking way and then sit back and wait to be notified - * that the action has completed. - */ - public class MqttConnector { - - public MqttConnector() { - } - - public void doConnect() { - // Connect to the server - // Get a token and setup an asynchronous listener on the token which - // will be notified once the connect completes - log("Connecting to " + brokerUrl + " with client ID " + client.getClientId()); - - IMqttActionListener conListener = new IMqttActionListener() { - @Override - public void onSuccess(IMqttToken asyncActionToken) { - log("Connected"); - state = CONNECTED; - carryOn(); - } - - @Override - public void onFailure(IMqttToken asyncActionToken, Throwable exception) { - ex = exception; - state = ERROR; - log("connect failed" + exception); - carryOn(); - } - - public void carryOn() { - synchronized (waiter) { - donext = true; - waiter.notifyAll(); - } - } - }; - - try { - // Connect using a non blocking connect - client.connect(conOpt, "Connect sample context", conListener); - } catch (MqttException e) { - // If though it is a non blocking connect an exception can be - // thrown if validation of parms fails or other checks such - // as already connected fail. - state = ERROR; - donext = true; - ex = e; - } - } - } - - /** - * Publish in a non blocking way and then sit back and wait to be notified - * that the action has completed. - */ - public class Publisher { - public void doPublish(String topicName, int qos, byte[] payload) { - // Send / publish a message to the server - // Get a token and setup an asynchronous listener on the token which - // will be notified once the message has been delivered - MqttMessage message = new MqttMessage(payload); - message.setQos(qos); - - log("Publishing at: " + System.currentTimeMillis() + " to topic \"" + topicName + "\" qos " + qos); - - // Setup a listener object to be notified when the publish completes. - // - IMqttActionListener pubListener = new IMqttActionListener() { - @Override - public void onSuccess(IMqttToken asyncActionToken) { - log("Publish Completed"); - state = PUBLISHED; - carryOn(); - } - - @Override - public void onFailure(IMqttToken asyncActionToken, Throwable exception) { - ex = exception; - state = ERROR; - log("Publish failed" + exception); - carryOn(); - } - - public void carryOn() { - synchronized (waiter) { - donext = true; - waiter.notifyAll(); - } - } - }; - - try { - // Publish the message - client.publish(topicName, message, "Pub sample context", pubListener); - } catch (MqttException e) { - state = ERROR; - donext = true; - ex = e; - } - } - } - - /** - * Subscribe in a non blocking way and then sit back and wait to be notified - * that the action has completed. - */ - public class Subscriber { - public void doSubscribe(String topicName, int qos) { - // Make a subscription - // Get a token and setup an asynchronous listener on the token which - // will be notified once the subscription is in place. - log("Subscribing to topic \"" + topicName + "\" qos " + qos); - - IMqttActionListener subListener = new IMqttActionListener() { - @Override - public void onSuccess(IMqttToken asyncActionToken) { - log("Subscribe Completed"); - state = SUBSCRIBED; - carryOn(); - } - - @Override - public void onFailure(IMqttToken asyncActionToken, Throwable exception) { - ex = exception; - state = ERROR; - log("Subscribe failed" + exception); - carryOn(); - } - - public void carryOn() { - synchronized (waiter) { - donext = true; - waiter.notifyAll(); - } - } - }; - - try { - client.subscribe(topicName, qos, "Subscribe sample context", subListener); - } catch (MqttException e) { - state = ERROR; - donext = true; - ex = e; - } - } - } - - /** - * Disconnect in a non blocking way and then sit back and wait to be notified - * that the action has completed. - */ - public class Disconnector { - public void doDisconnect() { - // Disconnect the client - log("Disconnecting"); - - IMqttActionListener discListener = new IMqttActionListener() { - @Override - public void onSuccess(IMqttToken asyncActionToken) { - log("Disconnect Completed"); - state = DISCONNECTED; - carryOn(); - } - - @Override - public void onFailure(IMqttToken asyncActionToken, Throwable exception) { - ex = exception; - state = ERROR; - log("Disconnect failed" + exception); - carryOn(); - } - - public void carryOn() { - synchronized (waiter) { - donext = true; - waiter.notifyAll(); - } - } - }; - - try { - client.disconnect("Disconnect sample context", discListener); - } catch (MqttException e) { - state = ERROR; - donext = true; - ex = e; - } - } - } -} \ No newline at end of file diff --git a/src/main/java/org/myrobotlab/arduino/VirtualMsg.java b/src/main/java/org/myrobotlab/arduino/VirtualMsg.java index cc29874af4..d2cc3ddaf7 100644 --- a/src/main/java/org/myrobotlab/arduino/VirtualMsg.java +++ b/src/main/java/org/myrobotlab/arduino/VirtualMsg.java @@ -3,6 +3,8 @@ import java.io.ByteArrayOutputStream; import java.io.OutputStream; +import java.io.IOException; +import org.myrobotlab.service.Serial; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; @@ -44,8 +46,11 @@ b16 int int (short) 2 bytes -32,768 to 32,767 import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.logging.LoggingFactory; +import org.myrobotlab.service.VirtualArduino; import java.io.FileOutputStream; +import java.util.Arrays; +import org.myrobotlab.service.interfaces.MrlCommPublisher; import org.myrobotlab.service.Runtime; import org.myrobotlab.service.Servo; import org.myrobotlab.service.interfaces.SerialDevice; @@ -1589,7 +1594,9 @@ public void onBytes(byte[] bytes) { msgSize = 0; Arrays.fill(ioCmd, 0); // FIXME - optimize - remove // warn(String.format("Arduino->MRL error - bad magic number %d - %d rx errors", newByte, ++errorServiceToHardwareRxCnt)); - log.warn("Arduino->MRL error - bad magic number {} - {} rx errors", newByte, ++errorServiceToHardwareRxCnt); + if (!arduino.isConnecting()){ + log.warn("Arduino->MRL error - bad magic number {} - {} rx errors", newByte, ++errorServiceToHardwareRxCnt); + } } continue; } else if (byteCount.get() == 2) { @@ -1622,7 +1629,10 @@ public void onBytes(byte[] bytes) { } if (!clearToSend) { - log.warn("NOT CLEAR TO SEND! resetting parser!"); + if (!arduino.isConnecting()) { + // we're connecting, so we're going to ignore the message. + log.warn("NOT CLEAR TO SEND! resetting parser!"); + } // We opened the port, and we got some data that isn't a Begin message. // so, I think we need to reset the parser and continue processing bytes... // there will be errors until the next magic byte is seen. @@ -1877,7 +1887,7 @@ public void enableAcks(boolean b){ } public void waitForAck(){ - if (!ackEnabled) { + if (!ackEnabled || serial == null || !serial.isConnected()) { return; } // if there's a pending message, we need to wait for the ack to be received. @@ -1910,10 +1920,8 @@ public int getMethod(){ public void add(int value) { - // this explodes - sendBufferSize increases forever ... duh index not valid - // is this suppose to be round robin buffer ? - // sendBuffer[sendBufferSize] = (value & 0xFF); - // sendBufferSize += 1; + sendBuffer[sendBufferSize] = (value & 0xFF); + sendBufferSize += 1; } public int[] getBuffer() { @@ -2019,4 +2027,8 @@ public void setInvoke(boolean b){ invoke = b; } + public void setSerial(Serial serial) { + this.serial = serial; + } + } diff --git a/src/main/java/org/myrobotlab/audio/AudioProcessor.java b/src/main/java/org/myrobotlab/audio/AudioProcessor.java index 46bc11d136..fb5236daef 100644 --- a/src/main/java/org/myrobotlab/audio/AudioProcessor.java +++ b/src/main/java/org/myrobotlab/audio/AudioProcessor.java @@ -23,7 +23,6 @@ import org.myrobotlab.service.data.AudioData; import org.slf4j.Logger; -// FIXME - make runnable public class AudioProcessor extends Thread { static transient Logger log = LoggerFactory.getLogger(AudioProcessor.class); diff --git a/src/main/java/org/myrobotlab/audio/CaptureThread.java b/src/main/java/org/myrobotlab/audio/CaptureThread.java deleted file mode 100644 index cab25ede5d..0000000000 --- a/src/main/java/org/myrobotlab/audio/CaptureThread.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.myrobotlab.audio; - -import java.io.ByteArrayOutputStream; -import java.io.OutputStream; - -import javax.sound.sampled.SourceDataLine; -import javax.sound.sampled.TargetDataLine; - -import org.myrobotlab.logging.LoggerFactory; -import org.myrobotlab.logging.Logging; -import org.myrobotlab.service.AudioFile; -import org.slf4j.Logger; - -class CaptureThread extends Thread { - // An arbitrary-size temporary holding - // buffer - static Logger log = LoggerFactory.getLogger(AudioFile.class); - - byte buffer[] = new byte[10000]; - OutputStream out = null; - - TargetDataLine audioLine; - SourceDataLine sourceDataLine; - - boolean done = false; - - public CaptureThread(TargetDataLine audioLine) { - this(audioLine, null); - } - - public CaptureThread(TargetDataLine audioLine, OutputStream out) { - this.audioLine = audioLine; - if (out == null) { - out = new ByteArrayOutputStream(); - ; - } - this.out = out; - } - - @Override - public void run() { - out = new ByteArrayOutputStream(); - try {// Loop until stopCapture is set - // by another thread that - // services the Stop button. - while (!done) { - // Read data from the internal - // buffer of the data line. - int cnt = audioLine.read(buffer, 0, buffer.length); - if (cnt > 0) { - // Save data in output stream - // object. - out.write(buffer, 0, cnt); - } // end if - } // end while - audioLine.close(); - out.close(); - } catch (Exception e) { - Logging.logError(e); - } finally { - if (out != null) { - try { - out.close(); - } catch (Exception e) { - } - } - } - }// end run - - public void setBufferLength(int size) { - buffer = new byte[size]; - } - - public void stopCapture() { - this.interrupt(); - } - - public OutputStream getOuputStream() { - return out; - } -} \ No newline at end of file diff --git a/src/main/java/org/myrobotlab/audio/MRLSoundAudioDevice.java b/src/main/java/org/myrobotlab/audio/MRLSoundAudioDevice.java deleted file mode 100644 index b6fd2b5666..0000000000 --- a/src/main/java/org/myrobotlab/audio/MRLSoundAudioDevice.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.myrobotlab.audio; - -//import be.tarsos.dsp.AudioEvent; -//import be.tarsos.dsp.AudioProcessor; -import javazoom.jl.decoder.JavaLayerException; -import javazoom.jl.player.JavaSoundAudioDevice; - -public class MRLSoundAudioDevice extends JavaSoundAudioDevice { - - // private List audioProcessors = null; - - private float gain = 1.0F; - - @Override - public void write(short[] paramArrayOfShort, int paramInt1, int paramInt2) throws JavaLayerException { - - if (gain == 1.0) { - // default behavior - super.write(paramArrayOfShort, paramInt1, paramInt2); - } else { - // so some digital signal processing!!! woot! - short[] volumeAdjusted = new short[paramArrayOfShort.length]; - for (int i = 0; i < paramArrayOfShort.length; i++) { - // Multiplication is volume control! amplify the signal by the gain - // EEK that's a lot of type casting! - volumeAdjusted[i] = (short) ((paramArrayOfShort[i]) * gain); - } - // pass the volume adjusted array to the underlying audio device - super.write(volumeAdjusted, paramInt1, paramInt2); - - } - } - - public float getGain() { - return gain; - } - - /** - * A value typically between 0.0 to 1.0. (Values larger than 1.0 may clip the - * original signal) - * - * @param gain - * - the gain to apply. This is multiplied by the underlying audio - * signal. - */ - public void setGain(float gain) { - this.gain = gain; - } - - // public void setLineGain(float gain) - // { - // this.getSourceLineInfo() - // if (source != null) - // { - // FloatControl volControl = (FloatControl) - // source.getControl(FloatControl.Type.MASTER_GAIN); - // float newGain = Math.min(Math.max(gain, volControl.getMinimum()), - // volControl.getMaximum()); - // - // volControl.setValue(newGain); - // } - // } - -} diff --git a/src/main/java/org/myrobotlab/client/DrupalNameProvider.java b/src/main/java/org/myrobotlab/client/DrupalNameProvider.java deleted file mode 100644 index b9ca47e0d5..0000000000 --- a/src/main/java/org/myrobotlab/client/DrupalNameProvider.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.myrobotlab.client; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.Statement; - -import org.myrobotlab.logging.LoggerFactory; -import org.myrobotlab.logging.Logging; -import org.myrobotlab.service.Shoutbox.NameProvider; -import org.slf4j.Logger; - -public class DrupalNameProvider implements NameProvider { - public static final Logger log = LoggerFactory.getLogger(DrupalNameProvider.class); - private Connection conn = null; - - @Override - public String getName(String ip) { - log.info("==DrupalNameProvider.getName({})==", ip); - try { - if (this.conn == null) { - Class.forName("com.mysql.jdbc.Driver"); - log.info("attempting to connect to mysql"); - this.conn = DriverManager.getConnection("jdbc:mysql://localhost/myrobotlab", "root", ""); - if (this.conn == null) { - log.error("could not connect"); - return ip; - } - } - String sql = String.format("SELECT users.name, sessions.uid, sessions.hostname FROM myrobotlab.users " + " INNER JOIN myrobotlab.sessions ON sessions.uid=users.uid " - + " WHERE sessions.hostname = '%s' " + " ORDER BY sessions.uid DESC", ip); - - Statement statement = this.conn.createStatement(); - log.info("executing query"); - ResultSet records = statement.executeQuery(sql); - - String user = null; - while (records.next()) { - user = records.getString("name"); - log.info("found [{}] for ip {}", user, ip); - if ((user == null) || (user.trim().length() == 0 || user.trim().equals(""))) { - log.info("user null or blank skipping"); - continue; - } else { - log.info("found user [{}]", user); - return user; - } - } - log.info("no not blank records found returning ip [{}]", ip); - return ip; - } catch (Exception e) { - Logging.logError(e); - } - return ip; - } -} diff --git a/src/main/java/org/myrobotlab/cmdline/CcmdParam.java b/src/main/java/org/myrobotlab/cmdline/CcmdParam.java deleted file mode 100644 index 15c69aea72..0000000000 --- a/src/main/java/org/myrobotlab/cmdline/CcmdParam.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * - * @author grog (at) myrobotlab.org - * - * This file is part of MyRobotLab (http://myrobotlab.org). - * - * MyRobotLab is free software: you can redistribute it and/or modify - * it under the terms of the Apache License 2.0 as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version (subject to the "Classpath" exception - * as provided in the LICENSE.txt file that accompanied this code). - * - * MyRobotLab is distributed in the hope that it will be useful or fun, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Apache License 2.0 for more details. - * - * All libraries in thirdParty bundle are subject to their own license - * requirements - please refer to http://myrobotlab.org/libraries for - * details. - * - * Enjoy ! - * - * */ - -package org.myrobotlab.cmdline; - -import java.util.ArrayList; - -public class CcmdParam { - - public ArrayList m_strings = new ArrayList(); - - public CcmdParam() { - } - -} diff --git a/src/main/java/org/myrobotlab/cmdline/CmdLine.java b/src/main/java/org/myrobotlab/cmdline/CmdLine.java deleted file mode 100644 index 9d764e0a07..0000000000 --- a/src/main/java/org/myrobotlab/cmdline/CmdLine.java +++ /dev/null @@ -1,190 +0,0 @@ -/** - * - * @author grog (at) myrobotlab.org - * - * This file is part of MyRobotLab (http://myrobotlab.org). - * - * MyRobotLab is free software: you can redistribute it and/or modify - * it under the terms of the Apache License 2.0 as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version (subject to the "Classpath" exception - * as provided in the LICENSE.txt file that accompanied this code). - * - * MyRobotLab is distributed in the hope that it will be useful or fun, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Apache License 2.0 for more details. - * - * All libraries in thirdParty bundle are subject to their own license - * requirements - please refer to http://myrobotlab.org/libraries for - * details. - * - * Enjoy ! - * - * */ - -package org.myrobotlab.cmdline; - -import java.util.ArrayList; -import java.util.HashMap; - -public class CmdLine extends HashMap { - - private static final long serialVersionUID = 1560723637806853945L; - - private String[] args = null; - - public static void main(String[] args) { - - CmdLine cmdline = new CmdLine(args); - - if (cmdline.containsKey("-test")) { - String service = cmdline.getSafeArgument("-service", 0, ""); - System.out.println("Service: " + service); - } - } - - public CmdLine(String[] args) { - splitLine(args); - } - - public String getArgument(final String pSwitch, int iIdx) { - if (hasSwitch(pSwitch)) { - if (containsKey(pSwitch)) { - if (get(pSwitch).m_strings.size() > iIdx) { - return get(pSwitch).m_strings.get(iIdx); - } - } - } - - // throw (int)0; - - return ""; - - } - - public int getArgumentCount(final String pSwitch) { - int iArgumentCount = -1; - - if (hasSwitch(pSwitch)) { - if (containsKey(pSwitch)) { - iArgumentCount = get(pSwitch).m_strings.size(); - } - } - - return iArgumentCount; - } - - public ArrayList getArgumentList(final String pSwitch) { - return get(pSwitch).m_strings; - } - - public String getSafeArgument(final String pSwitch, int iIdx, final String pDefault) { - String sRet = new String(""); - - if (pDefault != null) { - sRet = pDefault; - } - - if (!hasSwitch(pSwitch)) - return sRet; - - String r = getArgument(pSwitch, iIdx); - if ((r == null || r.length() == 0) && (pDefault != null && pDefault.length() != 0)) { - return pDefault; - } else { - return r; - } - } - - public boolean hasSwitch(final String pSwitch) { - return containsKey(pSwitch); - } - - public boolean isSwitch(final String pParam) { - if (pParam == null) { - return false; - } - if (pParam.length() <= 1) { - return false; - } - - if (pParam.charAt(0) == '-') { - boolean ret = true; - - // allow negative numbers as arguments. - // ie., don't count them as switches - ret &= !Character.isDigit(pParam.charAt(1)); - - // if we have a space then the param was escaped - // if its escaped e.g. -agent "-test -logLevel WARN" then its not a - // flag - ret &= !pParam.contains(" "); - return ret; - } else { - return false; - } - - } - - public int splitLine(String[] args) { - // HashMap a = new HashMap(); - // a.put(arg0, arg1) - this.args = args; - String curParam = new String(); - for (int i = 0; i < args.length; ++i) { - if (isSwitch(args[i])) { - curParam = args[i]; - String arg = ""; - - // look at next input string to see if it's a switch or an - // argument - if (i + 1 < args.length) { - if (!isSwitch(args[i + 1])) { - // it's an argument, not a switch - arg = args[i + 1]; - - // skip to next - i++; - } else { - arg = ""; - } - } - - // add it - CcmdParam cmd = new CcmdParam(); - - // only add non-empty args - if (arg != "") { - cmd.m_strings.add(arg); - } - - // add the CCmdParam to 'this' - put(curParam, cmd); - } else { - // it's not a new switch, so it must be more stuff for the last - // switch - // ...let's add it - // get an iterator for the current param - if (containsKey(curParam)) { - // (*theIterator).second.m_strings.push_back(argv[i]); - get(curParam).m_strings.add(args[i]); - } else { - // ?? - } - } - } - return 5; - - } - - @Override - public String toString() { - StringBuffer ret = new StringBuffer(); - for (int i = 0; i < args.length; ++i) { - ret.append("\"" + args[i] + "\"").append(" "); - } - - return ret.toString(); - } -} diff --git a/src/main/java/org/myrobotlab/codec/CodecUtils.java b/src/main/java/org/myrobotlab/codec/CodecUtils.java index ba528b7a10..eb20b2191b 100644 --- a/src/main/java/org/myrobotlab/codec/CodecUtils.java +++ b/src/main/java/org/myrobotlab/codec/CodecUtils.java @@ -1685,4 +1685,10 @@ public static int[] hexToRGB(String hexValue) { } return rgb; } + + public static String hashcodeToHex(int hashCode) { + String hexString = Long.toHexString(hashCode).toUpperCase(); + return String.format("%6s", hexString).replace(' ', '0').substring(0, 6); + } + } diff --git a/src/main/java/org/myrobotlab/codec/Recorder.java b/src/main/java/org/myrobotlab/codec/Recorder.java deleted file mode 100644 index 66df1319f8..0000000000 --- a/src/main/java/org/myrobotlab/codec/Recorder.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.myrobotlab.codec; - -import java.io.IOException; - -import org.myrobotlab.framework.Message; -import org.myrobotlab.framework.interfaces.NameProvider; - -public interface Recorder { - - public abstract void start(NameProvider service) throws IOException; - - public abstract void stop() throws IOException; - - public abstract void write(Message msg) throws IOException; - -} \ No newline at end of file diff --git a/src/main/java/org/myrobotlab/codec/RecorderPythonFile.java b/src/main/java/org/myrobotlab/codec/RecorderPythonFile.java deleted file mode 100644 index ed93be7aea..0000000000 --- a/src/main/java/org/myrobotlab/codec/RecorderPythonFile.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.myrobotlab.codec; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.text.SimpleDateFormat; -import java.util.Date; - -import org.myrobotlab.framework.Message; -import org.myrobotlab.framework.interfaces.NameProvider; -import org.myrobotlab.service.Runtime; - -public class RecorderPythonFile implements Recorder { - - boolean isRecording = false; - - private transient OutputStream recordingPython; - - /* - * (non-Javadoc) - * - * @see org.myrobotlab.codec.Recorder#write(org.myrobotlab.framework.Message) - */ - @Override - public void write(Message msg) throws IOException { - // python - Object[] data = msg.data; - String msgName = (msg.getName().equals(Runtime.getInstance().getName())) ? "runtime" : msg.getName(); - recordingPython.write(String.format("%s.%s(", msgName, msg.method).getBytes()); - if (data != null) { - for (int i = 0; i < data.length; ++i) { - Object d = data[i]; - if (d.getClass() == Integer.class || d.getClass() == Float.class || d.getClass() == Boolean.class || d.getClass() == Double.class || d.getClass() == Short.class - || d.getClass() == Short.class) { - recordingPython.write(d.toString().getBytes()); - - // FIXME Character probably blows up - } else if (d.getClass() == String.class || d.getClass() == Character.class) { - recordingPython.write(String.format("\"%s\"", d).getBytes()); - } else { - recordingPython.write("object".getBytes()); - } - if (i < data.length - 1) { - recordingPython.write(",".getBytes()); - } - } - } - recordingPython.write(")\n".getBytes()); - recordingPython.flush(); - } - - @Override - public void start(NameProvider service) throws FileNotFoundException { - SimpleDateFormat TSFormatter = new SimpleDateFormat("yyyyMMddHHmmssSSS"); - String cfgDir = String.format("%s%s.myrobotlab", System.getProperty("user.dir"), File.separator); - String filenamePython = String.format("%s/%s_%s.py", cfgDir, service.getName(), TSFormatter.format(new Date())); - - // log.info(String.format("started recording %s to file %s", getName(), - // filename)); - - recordingPython = new BufferedOutputStream(new FileOutputStream(filenamePython), 8 * 1024); - - } - - public static void main(String[] args) { - - } - - @Override - public void stop() throws IOException { - // TODO Auto-generated method stub - - } - -} diff --git a/src/main/java/org/myrobotlab/codec/RecorderXmlFile.java b/src/main/java/org/myrobotlab/codec/RecorderXmlFile.java deleted file mode 100644 index 4ae6a98077..0000000000 --- a/src/main/java/org/myrobotlab/codec/RecorderXmlFile.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.myrobotlab.codec; - -import java.io.IOException; - -import org.myrobotlab.framework.Message; -import org.myrobotlab.framework.interfaces.NameProvider; - -public class RecorderXmlFile implements Recorder { - - @Override - public void write(Message msg) throws IOException { - // Object[] data = msg.data; - // String msgName = (msg.name.equals(Runtime.getInstance().getName())) ? - // "runtime" : msg.name; - // TODO implement - - } - - @Override - public void start(NameProvider service) throws IOException { - // TODO Auto-generated method stub - - } - - @Override - public void stop() throws IOException { - // TODO Auto-generated method stub - - } - -} diff --git a/src/main/java/org/myrobotlab/framework/CmdOptions.java b/src/main/java/org/myrobotlab/framework/CmdOptions.java index 996d1b0556..f0eb00c0e7 100644 --- a/src/main/java/org/myrobotlab/framework/CmdOptions.java +++ b/src/main/java/org/myrobotlab/framework/CmdOptions.java @@ -45,7 +45,7 @@ static boolean contains(List l, String flag) { // launcher @Option(names = { "-c", - "--config" }, description = "Specify a configuration set to start. The config set is a directory which has all the necessary configuration files. It loads runtime.yml first, and subsequent service configuration files will then load. \n example: --config data/config/my-config-dir") + "--config" }, fallbackValue="default", description = "Specify a configuration set to start. The config set is a directory which has all the necessary configuration files. It loads runtime.yml first, and subsequent service configuration files will then load. \n example: --config data/config/my-config-dir") public String config = null; @Option(names = { diff --git a/src/main/java/org/myrobotlab/framework/Config.java b/src/main/java/org/myrobotlab/framework/Config.java deleted file mode 100644 index 80456356d3..0000000000 --- a/src/main/java/org/myrobotlab/framework/Config.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.myrobotlab.framework; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -@Retention(RUNTIME) -@Target({ TYPE, METHOD }) -public @interface Config { - boolean required() default true; - - int order() default 50; - - String description() default ""; -} diff --git a/src/main/java/org/myrobotlab/framework/Index.java b/src/main/java/org/myrobotlab/framework/Index.java deleted file mode 100644 index 3e0f9e3525..0000000000 --- a/src/main/java/org/myrobotlab/framework/Index.java +++ /dev/null @@ -1,292 +0,0 @@ -package org.myrobotlab.framework; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Set; - -import org.myrobotlab.logging.Level; -import org.myrobotlab.logging.LoggerFactory; -import org.myrobotlab.logging.LoggingFactory; -import org.slf4j.Logger; - -public class Index { - - public final static Logger log = LoggerFactory.getLogger(IndexNode.class); - - private IndexNode root = new IndexNode(); - - public static void main(String[] args) throws IOException { - LoggingFactory.init(Level.DEBUG); - - Index reservations = new Index(); - - ArrayList ret = reservations.crawlForDataStartingWith("RightArm"); - log.info("size {} from query of RightArm", ret.size()); - log.info(reservations.toString()); - log.info("here"); - - // reservations.put("arm.database.config", new - // ServiceReservation("Rotate","Servo","a servo")); - /* - * reservations.put("global.database.qa.server", "Db2udb04"); - * reservations.put("global.database.qa.port", "50005"); - * reservations.put("global.database.qa.schema", "TESTDB2"); - * reservations.put("global.database.qa.userid", "odcad01"); - * reservations.put("global.database.qa.password", "3dglasez"); - * - * reservations.put("global.database.dev.server", "Db2udb03"); - * reservations.put("global.database.dev.port", "50004"); - * reservations.put("global.database.dev.schema", "FIM"); - * reservations.put("global.database.dev.userid", "odcad00"); - * reservations.put("global.database.dev.password", "3dglases"); - */ - /* - * - * String test = reservations.get("global.database.dev.userid", "zod"); - * log.info(test); - * - * String dbConfigKey = reservations.get("global.database.config"); - * IndexNode dbConfigNode = - * reservations.getNode(String.format("global.database.%s" ,dbConfigKey)); - * HashMap dbConfig = dbConfigNode.getBranches(); - * - * log.info(dbConfig.get("server").getValue().toString()); - * log.info(dbConfig.get("port").getValue().toString()); - * log.info(dbConfig.get("schema").getValue().toString()); - * log.info(dbConfig.get("userid").getValue().toString()); - * log.info(dbConfig.get("password").getValue().toString()); - * - * // reservations.put("global.database.config","qa"); dbConfigKey = - * reservations.get("global.database.config"); dbConfigNode = reservations - * .getNode(String.format("global.database.%s",dbConfigKey)); dbConfig = - * dbConfigNode.getBranches(); - * - * log.info(dbConfig.get("server").getValue().toString()); - * log.info(dbConfig.get("port").getValue().toString()); - * log.info(dbConfig.get("schema").getValue().toString()); - * log.info(dbConfig.get("userid").getValue().toString()); - * log.info(dbConfig.get("password").getValue().toString()); - */ - HashMap hash = new HashMap(); - ArrayList array = new ArrayList(); - hash.put("hash1", "hashValue1"); - hash.put("hash2", "hashValue2"); - hash.put("hash3", "hashValue3"); - array.add("array1"); - array.add("array2"); - array.add("array3"); - array.add("array4"); - // config.put("arrayList", array); - // config.put("hashMap", hash); - - // File outfile = new File("out.xml"); - - // FileInputStream in = new FileInputStream(outfile); - // Index config2 = new Index(); - // config2.loadFromXML(in); - } - - public Index() { - /* - * if (properties == null) { properties = new Properties(); } - */ - } - - public void buildTree(String key, T value) { - root.buildTree(root, key, value); - } - - /* - * - * - * public void load() { load((String) null); } - * - * public void load(String path) { load(path, null); } - * - * /* public void load(String path, String propFileName) { if (path == null) { - * path = System.getProperty("user.dir"); } - * - * String configPath = propFileName; - * - * try { Properties properties = new Properties(); log.info( - * "loading config file " + configPath); properties.load(new - * FileInputStream(configPath)); - * - * for (String key : properties.stringPropertyNames()) { root.buildTree(root, - * key, properties.getProperty(key)); //IndexNode.buildTree(root, key, - * properties.getProperty(key)); } } catch (Exception e) { log.error( - * "config did not load from location " + configPath); - * Logging.logException(e); } } - */ - - public void clear() { - root.clear(); - } - - /* - * public static Index getInstance() { return getInstance((String) null); } - * - * public static Index getInstance(String path) { if (instance == null) { - * log.debug("new config"); instance = new Index(); } if (path != null) { - * load(path); } return instance; } - */ - - public ArrayList crawlForDataStartingWith(String inkey) { - return crawlForDataStartingWith(inkey, 20); - } - - public ArrayList crawlForDataStartingWith(String inkey, int maxItemsToReturn) { - - // TODO - start with a TreeMap ? - ArrayList ret = new ArrayList(); - - // get the limb in the tree which is keyed for this inkey - return root.crawlForData(ret, maxItemsToReturn, inkey); - - } - - public ArrayList flatten() { - return flatten(null); - } - - /* FULL IndexNode + key Set>> */ - public ArrayList flatten(String key) { - ArrayList ret = new ArrayList(); - return root.crawlForData(ret, 0, key); - } - - // --------------- config / property extensions begin -------------- - public T get(String name) { - return get(name, null); - } - - public T get(String name, T defaultValue) { - - IndexNode n = root.getNode(name); - if (n == null) { - return defaultValue; - } else { - return n.getValue(defaultValue); - } - } - - public Set getKeySetFromNode(String key) { - - IndexNode n = root.getNode(key); - if (n != null) { - return n.getBranches().keySet(); - } - - return null; - } - - public IndexNode getNode(String key) { - return root.getNode(key); - } - - /** - * find a node key, by browsing the whole tree.. ( wanted to use - * crawlForDataStartingWith but seem nok ) - * - * @param parent - * - parent node key - * @param key - * - this key - * @return - result node found - */ - public String findNode(String parent, String key) { - - Set childs = null; - if (parent == null) { - childs = getRootNode().getBranches().keySet(); - } else { - if (getNode(parent) != null) { - childs = getNode(parent).getBranches().keySet(); - } else { - return null; - } - } - - for (String entrie : childs) { - String nextNode = ""; - if (!(parent == null)) { - nextNode = parent + "." + entrie; - } else { - nextNode = entrie; - } - - // log.info(key + " key " + nextNode); - if (nextNode.toLowerCase().contains(key.toLowerCase())) { - return nextNode; - } - String nodeIterated = findNode(nextNode, key); - if (nodeIterated != null) { - return nodeIterated; - } - - } - - return null; - - } - - public String findNode(String key) { - return findNode(null, key); - } - - public IndexNode getOrCreateNode(String key) { - IndexNode node = getNode(key); - if (node == null) { - // IndexNode newNode = new IndexNode(); - return root.putNode(key, null); - } - return node; - } - - public IndexNode getRootNode() { - return root; - } - - /** - * check if selected node have leafs and return them as key list - * - * @param node - * - node to get leafs from - * @return all leaves - */ - public ArrayList getLeafs(String node) { - - ArrayList leafs = new ArrayList(); - log.debug("getNodeLeafs for {}", node); - if (root.getNode(node) != null) { - - Set entries = root.getNode(node).getBranches().keySet(); - // log.info(entries+"entries"); - - for (String key : entries) { - if (!(root.getNode(node + "." + key) == null) && root.getNode(node + "." + key).size() == 0) { - - leafs.add(node + "." + key); - } - - } - } - return leafs; - } - - public Enumeration propertyNames() { - return root.propertyNames(); - } - - public IndexNode put(String name, T value) { - return root.putNode(name, value); - } - - @Override - public String toString() { - return root.toString(); - } - -} diff --git a/src/main/java/org/myrobotlab/framework/IndexNode.java b/src/main/java/org/myrobotlab/framework/IndexNode.java deleted file mode 100644 index 1d9b95385b..0000000000 --- a/src/main/java/org/myrobotlab/framework/IndexNode.java +++ /dev/null @@ -1,213 +0,0 @@ -package org.myrobotlab.framework; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -import java.util.Vector; - -import org.myrobotlab.logging.LoggerFactory; -import org.slf4j.Logger; - -public class IndexNode { - - public final static Logger log = LoggerFactory.getLogger(IndexNode.class); - private HashMap> branches = new HashMap>(); - private T value; - - public IndexNode() { - } - - public IndexNode(T value) { - // leaf value - this.value = value; - } - - public void buildTree(IndexNode node, String key, T value) { - // if there is a "." - then we are a branch - not a leaf - if (key.indexOf(".") > 0) { - // if (key.length() > 0) { - // build branches of tree - String branch = key.substring(0, key.indexOf(".")); - String theRest = key.substring(key.indexOf(".") + 1); - log.info("adding branch {}", branch); - if (!node.branches.containsKey(branch)) { - // log.debug(String.format("adding branch %s", branch)); - IndexNode twig = new IndexNode(); - node.branches.put(branch, twig); - } - buildTree(node.getNode(branch), theRest, value); - } else { - // add a leaf - log.debug("adding leaf {}={}", key, value); - node.branches.put(key, new IndexNode(value)); - } - } - - public void clear() { - branches.clear(); - } - - public boolean containsKey(String key) { - IndexNode node = getNode(key); - return node != null; - } - - // http://stackoverflow.com/questions/2319538/most-concise-way-to-convert-a-setstring-to-a-liststring - // FIXME not the most effecient - should simply return the Set>> from each branches !!! - // good thing you put the key in the data node :P (lame) - // limitCount = 0 means no limit - public ArrayList crawlForData(ArrayList data, int limitCount, String key) { - - if (value != null) { - // found some data add it - data.add(value); - } - if (key != null && key.contains(".")) { - String subkey = key.substring(0, 1); - String climb = key.substring(1); - IndexNode t = branches.get(subkey); - if (t == null) { - return data; // nothing here - return what was passed in - } else { - t.crawlForData(data, limitCount, climb); // climb some more - } - } else { - // no key at this point - just get data - if (limitCount < 1 || data.size() < limitCount) { - // still under our data limit - // look for more data - by crawling up children - for (String k : branches.keySet()) { - // return crawlForData(data, limitCount, - // String.format("%s%s",key,k)); - // FIXME - data structure change will do concurrent mod erro - IndexNode n = branches.get(k); - n.crawlForData(data, limitCount, ""); - } - } - - } - - return data; - } - - public HashMap> getBranches() { - return branches; - } - - public IndexNode getNode(String key) { - if (key == null) { - return this; - } - IndexNode target = null; - if (key.contains(".")) { - String subkey = key.substring(0, key.indexOf(".")); - String climb = key.substring(key.indexOf(".") + 1); - IndexNode t = branches.get(subkey); - if (t == null) { - return null; - } else { - return t.getNode(climb); - } - } else { - target = branches.get(key); - } - - return target; - - } - - public T getValue() { - return value; - } - - public T getValue(T defaultValue) { - return (value == null) ? defaultValue : value; - } - - public Enumeration propertyNames() { - Vector n = new Vector(branches.keySet()); - return n.elements(); - } - - public Enumeration propertySortedNames() { - List n = new Vector(branches.keySet()); - Collections.sort(n); - return ((Vector) n).elements(); - } - - public IndexNode putNode(String key, T value) { - if (key == null) { - setValue(value); - return this; - } - - if (key.contains(".")) { - // if (key.length() > 1) { - // find the last node we from which - // we need to start building branches - // String subkey = key.substring(0, 1); - // String climb = key.substring(1); - String subkey = key.substring(0, key.indexOf(".")); - String climb = key.substring(key.indexOf(".") + 1); - - IndexNode t = branches.get(subkey); - if (t == null) { - // no branch - we need to build it - IndexNode twig = new IndexNode(); - branches.put(subkey, twig); - return twig.putNode(climb, value); - } else { - return t.putNode(climb, value); - } - } else { - IndexNode targetNode = branches.get(key); - if (targetNode == null) { - return branches.put(key, new IndexNode(value)); - } else { - targetNode.setValue(value); - return targetNode; - } - } - - // return destination; - } - - public void setValue(T value) { - this.value = value; - } - - public int size() { - return branches.size(); - } - - @Override - public String toString() { - return toString(null); - } - - public String toString(String contextPath) { - StringBuffer sb = new StringBuffer(); - - if (contextPath != null && getValue() != null) { - sb.append(contextPath); - sb.append("="); - sb.append(getValue()); - sb.append("\n"); - } - - for (String key : branches.keySet()) { - - if (contextPath != null) { - sb.append(branches.get(key).toString(contextPath + "." + key)); - } else { - sb.append(branches.get(key).toString(key)); - } - } - - return sb.toString(); - } - -} diff --git a/src/main/java/org/myrobotlab/framework/Outbox.java b/src/main/java/org/myrobotlab/framework/Outbox.java index 01921add9f..cdc1d3f5c0 100644 --- a/src/main/java/org/myrobotlab/framework/Outbox.java +++ b/src/main/java/org/myrobotlab/framework/Outbox.java @@ -43,7 +43,6 @@ import org.myrobotlab.service.interfaces.Gateway; import org.slf4j.Logger; - /* * Outbox is a message based thread which sends messages based on addListener lists and current * queue status. It is only aware of the Service directory, addListener lists, and operators. @@ -63,11 +62,17 @@ public class Outbox implements Runnable, Serializable { static public final String PROCESSANDBROADCAST = "PROCESSANDBROADCAST"; protected String name = null; - private transient LinkedList msgBox = new LinkedList(); + + private transient LinkedList msgBox = new LinkedList(); + private boolean isRunning = false; + private boolean blocking = false; + int maxQueue = 1024; + int initialThreadCount = 1; + transient ArrayList outboxThreadPool = new ArrayList(); protected Map filters = new HashMap<>(); @@ -216,10 +221,10 @@ public void run() { MRLListener listener = subList.get(i); msg.setName(listener.callbackName); msg.method = listener.callbackMethod; - + if (!isFiltered(msg)) { - send(msg); - } + send(msg); + } // must make new for internal queues // otherwise you'll change the name on @@ -228,21 +233,22 @@ public void run() { } } else { if (log.isDebugEnabled()) { - log.debug("{}/{}({}) notifyList is empty", msg.getName(), msg.method, CodecUtils.getParameterSignature(msg.data)); + log.debug("{}/{}({}) notifyList is empty", msg.getName(), msg.method, + CodecUtils.getParameterSignature(msg.data)); } continue; } } // while (isRunning) } - + public FilterInterface addFilter(String name, String method, FilterInterface filter) { return filters.put(String.format("%s.%s", CodecUtils.getFullName(name), method), filter); } - + public FilterInterface removeFilter(String name, String method) { return filters.remove(String.format("%s.%s", CodecUtils.getFullName(name), method)); } - + public boolean isFiltered(Message msg) { String fullname = CodecUtils.getFullName(msg.name); if (filters.size() == 0 || !filters.containsKey(String.format("%s.%s", fullname, msg.method))) { @@ -258,7 +264,7 @@ public int size() { public void start() { for (int i = outboxThreadPool.size(); i < initialThreadCount; ++i) { - Thread t = new Thread(this, name + "_outbox_" + i); + Thread t = new Thread(this, CodecUtils.getShortName(name) + "_outbox_" + i); outboxThreadPool.add(t); t.start(); } @@ -317,7 +323,8 @@ final public void send(final Message msg) { // ? ServiceInterface sw = Runtime.getService(msg.getName()); if (sw == null && autoClean) { - log.warn("could not find service {} to process {} from sender {} - tearing down route", msg.getName(), msg.method, msg.sender); + log.warn("could not find service {} to process {} from sender {} - tearing down route", msg.getName(), + msg.method, msg.sender); ServiceInterface sender = Runtime.getService(msg.sender); if (sender != null) { sender.removeListener(msg.sendingMethod, msg.getName(), msg.method); @@ -358,8 +365,8 @@ public void reset() { /** * Safe detach for single subscriber * - * @param name - * the name of the listener to detach + * @param service + * the name of the listener to detach * */ synchronized public void detach(String service) { @@ -379,7 +386,4 @@ synchronized public void detach(String service) { public Map> getNotifyList() { return notifyList; } - - - } diff --git a/src/main/java/org/myrobotlab/framework/Plan.java b/src/main/java/org/myrobotlab/framework/Plan.java index 92f48b066e..ab89e2d2bb 100644 --- a/src/main/java/org/myrobotlab/framework/Plan.java +++ b/src/main/java/org/myrobotlab/framework/Plan.java @@ -96,7 +96,7 @@ public void clear() { */ public ServiceConfig remove(String service) { RuntimeConfig rtConfig = (RuntimeConfig) config.get("runtime"); - rtConfig.registry.remove(service); + rtConfig.remove(service); return config.remove(service); } @@ -142,21 +142,5 @@ public void addRegistry(String service) { runtime.add(service); } - /** - * good to prune trees of peers from starting - expecially if the peers - * require re-configuring - * - * @param startsWith - * - removes RuntimeConfig.registry all services that start with - * input - */ - public void removeStartsWith(String startsWith) { - RuntimeConfig runtime = (RuntimeConfig) config.get("runtime"); - if (runtime == null) { - log.error("removeRegistry - runtime null !"); - return; - } - runtime.removeStartsWith(startsWith); - } } diff --git a/src/main/java/org/myrobotlab/framework/Service.java b/src/main/java/org/myrobotlab/framework/Service.java index 8d4dcc7ab8..4519d0bfbb 100644 --- a/src/main/java/org/myrobotlab/framework/Service.java +++ b/src/main/java/org/myrobotlab/framework/Service.java @@ -141,9 +141,9 @@ public abstract class Service implements Runnable, Seri transient protected Thread thisThread = null; - transient protected Inbox inbox = null; + final transient protected Inbox inbox; - protected Outbox outbox = null; + final protected Outbox outbox; protected String serviceVersion = null; @@ -1473,7 +1473,6 @@ public void setPeerConfigValue(String peerKey, String fieldname, Object value) t // broadcast change invoke("getPeerConfig", peerKey); - Runtime.getPlan().put(peerName, sc); Runtime runtime = Runtime.getInstance(); runtime.broadcastState(); } @@ -1724,8 +1723,6 @@ final public void run() { @Override public boolean save() { Runtime runtime = Runtime.getInstance(); - // save all services ... weird notation - should have explicit - // saveAllServices return runtime.saveService(runtime.getConfigName(), getName(), null); } @@ -1994,30 +1991,10 @@ synchronized public ServiceInterface startPeer(String peerKey) { error("startPeer could not find peerKey of %s in %s", peerKey, getName()); return null; } - - ServiceInterface si = Runtime.getService(peer.name); - if (si != null) { - // so this peer is already started, but are we responsible for - // all subpeers ? - return si; - } - - // request to modify the plan's runtime to start all service that match - // actualName.* - Plan plan = Runtime.getPlan(); - ServiceConfig sc = plan.get(peer.name); - - if (sc == null) { - log.info("no current plan for peer {} - since this is a peer request we can make a plan", peer.name); - // error("plan.get(%s) == null", actualName); - Runtime.load(peer.name, peer.type); - sc = plan.get(peer.name); - } - + // start peer requested - Runtime.start(peer.name, sc.type); broadcastState(); - return Runtime.getService(peer.name); + return Runtime.start(peer.name); } /** @@ -2032,8 +2009,13 @@ synchronized public ServiceInterface startPeer(String peerKey) { synchronized public void releasePeer(String peerKey) { if (getConfig() != null && getConfig().getPeer(peerKey) != null) { - Peer peer = getConfig().getPeer(peerKey); - ServiceConfig sc = Runtime.getPlan().get(peer.name); + ServiceConfig sc = null; + String peerName = getPeerName(peerKey); + ServiceInterface si = Runtime.getService(peerName); + if (si != null) { + sc = si.getConfig(); + } + // peer recursive if (sc != null && sc.getPeers() != null) { for (String subPeerKey : sc.getPeers().keySet()) { @@ -2043,9 +2025,7 @@ synchronized public void releasePeer(String peerKey) { } } } - Plan plan = Runtime.getPlan(); - plan.removeRegistry(peer.name); - Runtime.release(peer.name); + Runtime.release(peerName); broadcastState(); } else { error("%s.releasePeer(%s) does not exist", getName(), peerKey); @@ -2827,7 +2807,7 @@ protected void registerForInterfaceChange(Class clazz) { } final public Plan getDefault() { - return ServiceConfig.getDefault(Runtime.getPlan(), getName(), this.getClass().getSimpleName()); + return ServiceConfig.getDefault(new Plan("runtime"), getName(), this.getClass().getSimpleName()); } @Override @@ -2848,9 +2828,6 @@ public void apply() { return; } - // updating plan - FIXME remove plan - Runtime.getPlan().put(getName(), sc); - // applying config to self apply((T) sc); } @@ -2869,8 +2846,6 @@ public void applyPeerConfig(String peerKey, ServiceConfig config) { public

void applyPeerConfig(String peerKey, P config, StaticType> configServiceType) { String peerName = getPeerName(peerKey); - Runtime.getPlan().put(peerName, config); - // meh - templating is not very helpful here ConfigurableService

si = Runtime.getService(peerName, configServiceType); if (si != null) { @@ -2891,7 +2866,7 @@ public void setPeerName(String key, String fullName) { String oldName = peer.name; peer.name = fullName; // update plan ? - ServiceConfig.getDefault(Runtime.getPlan(), peer.name, peer.type); + ServiceConfig.getDefault(new Plan("runtime"), peer.name, peer.type); // FIXME - determine if only updating the Plan in memory is enough, // should we also make or update a config file - if the config path is set? info("updated %s name to %s", oldName, peer.name); @@ -2915,26 +2890,10 @@ public Map> getNotifyList() { */ public void updatePeerType(String key, String peerType) { - // MAKE NOTE ! - CONFIG IS DIFFERENT THAN PLAN !!!! MODIFY BOTH ???!? - - // get current plan - Plan plan = Runtime.getPlan(); - - // get self - ServiceConfig sc = plan.get(getName()); - if (sc != null) { - sc.putPeerType(key, String.format("%s.%s", getName(), key), peerType); - } - Peer peer = getConfig().getPeer(key); peer.type = peerType; - // not Needed - // config.putPeerType(key, String.format("%s.%s", key, getName()), - // peerType); - plan.remove(peer.name); - // FIXME - rename putDefault - ServiceConfig.getDefault(Runtime.getPlan(), peer.name, peerType); + ServiceConfig.getDefault(new Plan("runtime"), peer.name, peerType); Runtime runtime = Runtime.getInstance(); String configName = runtime.getConfigName(); // Seems a bit invasive - but yml file overrides everything diff --git a/src/main/java/org/myrobotlab/framework/CmdConfig.java b/src/main/java/org/myrobotlab/framework/StartYml.java similarity index 82% rename from src/main/java/org/myrobotlab/framework/CmdConfig.java rename to src/main/java/org/myrobotlab/framework/StartYml.java index f807aaeb81..c8bfb25a44 100644 --- a/src/main/java/org/myrobotlab/framework/CmdConfig.java +++ b/src/main/java/org/myrobotlab/framework/StartYml.java @@ -8,17 +8,12 @@ * @author GroG * */ -public class CmdConfig { +public class StartYml { /** * instance id of myrobotlab, default will be dynamically generated */ public String id; - - /** - * {configRoot}/{configName} - */ - public String configRoot = "data/config"; - + /** * configuration set to start under /data/config/{configName} */ diff --git a/src/main/java/org/myrobotlab/framework/ToolTip.java b/src/main/java/org/myrobotlab/framework/ToolTip.java deleted file mode 100644 index 21d79b9bdb..0000000000 --- a/src/main/java/org/myrobotlab/framework/ToolTip.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * - */ -package org.myrobotlab.framework; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * @author gperry - * - */ - -@Retention(RetentionPolicy.RUNTIME) -public @interface ToolTip { - public String value(); -} diff --git a/src/main/java/org/myrobotlab/framework/TypeConverter.java b/src/main/java/org/myrobotlab/framework/TypeConverter.java deleted file mode 100644 index 051ae50e34..0000000000 --- a/src/main/java/org/myrobotlab/framework/TypeConverter.java +++ /dev/null @@ -1,187 +0,0 @@ -package org.myrobotlab.framework; - -import java.io.IOException; -import java.lang.reflect.Method; -import java.util.HashMap; - -import org.myrobotlab.codec.CodecUtils; -import org.myrobotlab.logging.Level; -import org.myrobotlab.logging.LoggerFactory; -import org.myrobotlab.logging.Logging; -import org.myrobotlab.logging.LoggingFactory; -import org.slf4j.Logger; - -/** - * JSON TypeConverter - used in general REST api to convert url JSON parameters - * appropriately to hard types for method invoking used in WebGui and Cli - * - * @author GroG - * - */ -public class TypeConverter { - - public final static Logger log = LoggerFactory.getLogger(TypeConverter.class); - - // Possible Optimization -> pointers to known method signatures - - // optimization so that once a - // method's signature is processed and - // known conversion exists - it is saved - static public HashMap knownMethodSignatureConverters = new HashMap(); - - // pointers to conversion methods - // static public HashMap conversions = new HashMap(); - - /** - * this method tries to get the appropriate 'Typed parameter array for a - * specific method It "converts" parameters of strings into typed parameters - * which can then be used to reflectively invoke the appropriate method - * - * @param clazz - * c - * @param method - * m - * @param stringParams - * p - * @return object array - * @throws IOException - * e - * - */ - static public Object[] getTypedParamsFromJson(Class clazz, String method, String[] stringParams) throws IOException { - - // try { - - Method[] methods = clazz.getMethods(); - for (int i = 0; i < methods.length; ++i) { - Method m = methods[i]; - Class[] types = m.getParameterTypes(); - // TODO optimize getting name ??? why didn't Java reflect api - // use a HashMap ??? - if (method.equals(m.getName()) && stringParams.length == types.length) { - log.debug("method with same ordinal of params found {}.{} - building new converter", method, stringParams.length); - - try { - Object[] newGSONTypedParamters = new Object[stringParams.length]; - - for (int j = 0; j < types.length; ++j) { - Class pType = types[j]; - String param = stringParams[j]; - - log.debug("attempting conversion into {} from inbound data {}", pType.getSimpleName(), stringParams[j]); - if (pType == String.class) { - // escape quotes - param = param.replaceAll("\"", "\\\""); - // add quotes - param = String.format("\"%s\"", param); - } - newGSONTypedParamters[j] = CodecUtils.fromJson(param, pType); - - } - - log.debug("successfully converted all types"); - return newGSONTypedParamters; - - } catch (Exception e) { - // Logging.logException(e); - log.warn("could not match type from inbound data"); - continue; - } - - } // if name and ordinal match - } // through each method - - String error = String.format("could not find or convert %s", method); - log.error(error); - - /* - * } catch (Exception e) { Logging.logError(e); } - * - * return null; - */ - throw new IOException(error); - - } - - public static void main(String[] args) { - - try { - LoggingFactory.init(Level.DEBUG); - - /* - * FIXME PUT IN JUNIT TEST !! - * org.myrobotlab.service.Runtime.createAndStart("clock", "Clock"); - * - * ServiceInterface si = - * org.myrobotlab.service.Runtime.getService("clock"); - * - * - * String stringParams[] = new String[] { "13", "1" }; String method = - * "digitalWrite"; Class clazz = si.getClass(); - * - * Object[] params = getTypedParamsFromJson(clazz, method, stringParams); - * - * si.invoke(method, params); - * - * log.info("here"); - * - * Object[] params2 = getTypedParamsFromJson(clazz, method, stringParams); - * log.info("here"); - */ - } catch (Exception e) { - Logging.logError(e); - } - } - - static public boolean StringToBoolean(String in) { - return Boolean.parseBoolean(in); - } - - // -------- primitive boxed types conversion begin ------------ - static public byte StringToByte(String in) { - return Byte.parseByte(in); - } - - static public char StringToChar(String in) { - return in.charAt(0); - } - - static public double StringToDouble(String in) { - return Double.parseDouble(in); - } - - static public float StringToFloat(String in) { - return Float.parseFloat(in); - } - - static public int StringToInteger(String in) { - return Integer.parseInt(in); - } - - static public long StringToLong(String in) { - return Long.parseLong(in); - } - - // -------- primitive boxed types conversion end ------------ - - static public short StringToShort(String in) { - return Short.parseShort(in); - } - - /* - * static public Object[] convert(String[] stringParams, Method[] converter) { - * try { Object[] newTypedParams = new Object[stringParams.length]; for (int i - * = 0; i < stringParams.length; ++i) { // static calls on conversion - - * probably not thread safe newTypedParams[0] = converter[i].invoke(null, - * stringParams[i]); } - * - * return newTypedParams; } catch (Exception e) { Logging.logException(e); } - * - * return null; } - */ - - static public String StringToString(String in) { - return in; - } - -} diff --git a/src/main/java/org/myrobotlab/fsm/api/Event.java b/src/main/java/org/myrobotlab/fsm/api/Event.java deleted file mode 100644 index f622a5544c..0000000000 --- a/src/main/java/org/myrobotlab/fsm/api/Event.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.myrobotlab.fsm.api; - -import java.util.Date; - -import org.myrobotlab.fsm.util.Utils; - -/** - * Abstract class for events to which a FSM should react and make transitions. - * - * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - */ -public abstract class Event { - - protected String name; - protected long timestamp; - - protected Event() { - this.name = Utils.DEFAULT_EVENT_NAME; - timestamp = System.currentTimeMillis(); - } - - protected Event(final String name) { - this.name = name; - timestamp = System.currentTimeMillis(); - } - - public String getName() { - return name; - } - - public long getTimestamp() { - return timestamp; - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder(); - sb.append("Event"); - sb.append("{name='").append(name).append('\''); - sb.append(", timestamp=").append(new Date(timestamp)); - sb.append('}'); - return sb.toString(); - } - - public abstract String getId(); - -} diff --git a/src/main/java/org/myrobotlab/fsm/api/EventHandler.java b/src/main/java/org/myrobotlab/fsm/api/EventHandler.java deleted file mode 100644 index 44059cb3f1..0000000000 --- a/src/main/java/org/myrobotlab/fsm/api/EventHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.myrobotlab.fsm.api; - -/** - * Abstraction for actions to perform when an event is triggered. - * - * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - */ -public interface EventHandler { - - /** - * Action method to execute when an event occurs. - * - * @param event - * the triggered event - * @throws Exception - * thrown if a problem occurs during action performing - */ - void handleEvent(Event event) throws Exception; - -} diff --git a/src/main/java/org/myrobotlab/fsm/api/FiniteStateMachine.java b/src/main/java/org/myrobotlab/fsm/api/FiniteStateMachine.java deleted file mode 100644 index 2f16ef3cb6..0000000000 --- a/src/main/java/org/myrobotlab/fsm/api/FiniteStateMachine.java +++ /dev/null @@ -1,100 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.myrobotlab.fsm.api; - -import java.util.List; -import java.util.Set; - -/** - * FSM interface. This is the main abstraction for a finite state machine. - * - * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - */ -public interface FiniteStateMachine { - - /** - * Return current FSM state. - * - * @return current FSM state - */ - State getCurrentState(); - - /** - * Return FSM initial state. - * - * @return FSM initial state - */ - - // State getInitialState(); - - /** - * Return FSM final states. - * - * @return FSM final states - */ - Set getFinalStates(); - - /** - * Return FSM registered states. - * - * @return FSM registered states - */ - Set getStates(); - - /** - * Return FSM registered transitions. - * - * @return FSM registered transitions - */ - Set getTransitions(); - - /** - * Return the last triggered event. - * - * @return the last triggered event - */ - Event getLastEvent(); - - /** - * Return the last transition made. - * - * @return the last transition made - */ - Transition getLastTransition(); - - /** - * Fire an event. According to event type, the FSM will make the right - * transition. - * - * @param event - * to fire - * @return The next FSM state defined by the transition to make - * @throws FiniteStateMachineException - * thrown if an exception occurs during event handling - */ - // State fire(Event event) throws FiniteStateMachineException; - - List fire(Event event) throws FiniteStateMachineException; - -} diff --git a/src/main/java/org/myrobotlab/fsm/api/FiniteStateMachineException.java b/src/main/java/org/myrobotlab/fsm/api/FiniteStateMachineException.java deleted file mode 100644 index 19079c3ca1..0000000000 --- a/src/main/java/org/myrobotlab/fsm/api/FiniteStateMachineException.java +++ /dev/null @@ -1,92 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.myrobotlab.fsm.api; - -/** - * Exception thrown if a problem occurs during event handling. This class gives - * access to the {@link Transition} and {@link Event} related to the exception. - * - * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - */ -public class FiniteStateMachineException extends Exception { - - /** - * The transition where the exception occurred. - */ - private Transition transition; - - /** - * The event triggered when the exception occurred. - */ - private Event event; - - /** - * The root cause of the exception. - */ - private Throwable cause; - - /** - * Create a new {@link FiniteStateMachineException}. - * - * @param transition - * where the exception occurred - * @param event - * triggered when the exception occurred - * @param cause - * root cause of the exception - */ - public FiniteStateMachineException(final Transition transition, final Event event, final Throwable cause) { - this.transition = transition; - this.event = event; - this.cause = cause; - } - - /** - * Get the transition where the exception occurred. - * - * @return the transition where the exception occurred. - */ - public Transition getTransition() { - return transition; - } - - /** - * Get the event triggered when the exception occurred. - * - * @return the event triggered when the exception occurred. - */ - public Event getEvent() { - return event; - } - - /** - * Get the root cause of the exception. - * - * @return the root cause of the exception - */ - @Override - public Throwable getCause() { - return cause; - } -} diff --git a/src/main/java/org/myrobotlab/fsm/api/SimpleEvent.java b/src/main/java/org/myrobotlab/fsm/api/SimpleEvent.java deleted file mode 100644 index 2e1d36f4ce..0000000000 --- a/src/main/java/org/myrobotlab/fsm/api/SimpleEvent.java +++ /dev/null @@ -1,92 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.myrobotlab.fsm.api; - -import java.util.Date; - -import org.myrobotlab.fsm.util.Utils; - -/** - * general event class for invoking transitions - * - */ -public class SimpleEvent extends Event { - @Deprecated - String source; - private Transition transition; - // private State lastState; - - public SimpleEvent(String name) { - this.name = name; - timestamp = System.currentTimeMillis(); - } - - public SimpleEvent() { - this.name = Utils.DEFAULT_EVENT_NAME; - timestamp = System.currentTimeMillis(); - } - - public SimpleEvent(String fsmName, String eventName) { - this.source = fsmName; - this.name = eventName; - } - - @Override - public String getName() { - return name; - } - - @Override - public long getTimestamp() { - return timestamp; - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder(); - sb.append("Event"); - sb.append("{name='").append(name).append('\''); - sb.append(", timestamp=").append(new Date(timestamp)); - sb.append('}'); - return sb.toString(); - } - - @Override - public String getId() { - return name; - } - - public String getSource() { - return source; - } - - public void setTransition(Transition transition) { - this.transition = transition; - } - - public Transition getTransition() { - return transition; - } - -} diff --git a/src/main/java/org/myrobotlab/fsm/api/State.java b/src/main/java/org/myrobotlab/fsm/api/State.java deleted file mode 100644 index 050422ee4f..0000000000 --- a/src/main/java/org/myrobotlab/fsm/api/State.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.myrobotlab.fsm.api; - -// FIXME - should be an interface in (api) - -/** - * A class representing a FSM state. States have unique names within a - * FSM instance - * - * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - */ -public class State { - - private String id; - transient private Transition lastTransition = null; - - /** - * Create a new {@link State}. - * - * @param id - * of the state - */ - public State(final String id) { - this.id = id; - } - - /** - * Get state name. - * - * @return state name - */ - public String getName() { - return id; - } - - /* - * States have unique name within a Easy States FSM instance - */ - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null) - return false; - return id.equals(o.toString()); - } - - @Override - public int hashCode() { - return id.hashCode(); - } - - @Override - public String toString() { - return id; - } - - public void setLastTransition(Transition transition) { - lastTransition = transition; - } - - public Transition getLastTransition() { - return lastTransition; - } - - public void setId(String name) { - this.id = name; - } - -} diff --git a/src/main/java/org/myrobotlab/fsm/api/StateHandler.java b/src/main/java/org/myrobotlab/fsm/api/StateHandler.java deleted file mode 100644 index 0c8651c983..0000000000 --- a/src/main/java/org/myrobotlab/fsm/api/StateHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.myrobotlab.fsm.api; - -/** - * Abstraction for actions to perform when an event is triggered. - * - * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - */ -public interface StateHandler { - - /** - * Action method to execute when an event occurs. - * - * @param event - * the triggered event - * @throws Exception - * thrown if a problem occurs during action performing - */ - void handleState(State event) throws Exception; - -} diff --git a/src/main/java/org/myrobotlab/fsm/api/Transition.java b/src/main/java/org/myrobotlab/fsm/api/Transition.java deleted file mode 100644 index 6dc175a95b..0000000000 --- a/src/main/java/org/myrobotlab/fsm/api/Transition.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.myrobotlab.fsm.api; - -/** - * Abstraction for a FSM transition. Transitions are unique according to - * source state and triggering event type. - * - * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - */ -public interface Transition { - - /** - * Return transition name. - * - * @return transition name - */ - String getName(); - - /** - * Return transition source state. - * - * @return transition source state - */ - State getSourceState(); - - /** - * Return transition target state. - * - * @return transition target state - */ - State getTargetState(); - - /** - * Return fired event type upon which the transition should be made. - * - * @return Event type class - */ - // Class getEventType(); - - /** - * Return event handler to execute when an event is fired. - * - * @return transition event handler - */ - EventHandler getEventHandler(); - - StateHandler getNewStateHandler(); - - String getId(); - -} diff --git a/src/main/java/org/myrobotlab/fsm/api/package-info.java b/src/main/java/org/myrobotlab/fsm/api/package-info.java deleted file mode 100644 index c8147d471e..0000000000 --- a/src/main/java/org/myrobotlab/fsm/api/package-info.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/** - * This package contains the public APIs. - */ -package org.myrobotlab.fsm.api; diff --git a/src/main/java/org/myrobotlab/fsm/core/SimpleTransition.java b/src/main/java/org/myrobotlab/fsm/core/SimpleTransition.java deleted file mode 100644 index b03f6cf640..0000000000 --- a/src/main/java/org/myrobotlab/fsm/core/SimpleTransition.java +++ /dev/null @@ -1,143 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.myrobotlab.fsm.core; - -import org.myrobotlab.fsm.api.EventHandler; -import org.myrobotlab.fsm.api.State; -import org.myrobotlab.fsm.api.StateHandler; -import org.myrobotlab.fsm.api.Transition; -import org.myrobotlab.fsm.util.Utils; - -public class SimpleTransition implements Transition { - - private String id; - private State sourceState; - private State targetState; - // private Class eventType; - transient private EventHandler eventHandler; - transient private StateHandler newStateHandler; - // private String source; - - public SimpleTransition() { - id = Utils.DEFAULT_TRANSITION_NAME; - } - - @Override - public State getSourceState() { - return sourceState; - } - - public void setSourceState(State sourceState) { - this.sourceState = sourceState; - } - - @Override - public State getTargetState() { - return targetState; - } - - public void setTargetState(State targetState) { - this.targetState = targetState; - } - - @Override - public String getName() { - return id; - } - - public void setName(String name) { - this.id = name; - } - - @Override - public EventHandler getEventHandler() { - return eventHandler; - } - - public void setEventHandler(EventHandler eventHandler) { - this.eventHandler = eventHandler; - } - - public void setNewStateHandler(StateHandler stateHandler) { - this.newStateHandler = stateHandler; - } - - /* - * Transitions are unique according to source state and triggering event type - */ - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null) - return false; - - SimpleTransition that = (SimpleTransition) o; - - return id.equals(that.id) && sourceState.equals(that.sourceState); - - } - - @Override - public int hashCode() { - int result = sourceState.hashCode(); - // result = 31 * result + eventType.hashCode(); - result = 31 * result + id.hashCode(); - return result; - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder(); - sb.append(sourceState.getName()); - sb.append("-("); - sb.append(getId()); - sb.append(")->"); - sb.append(targetState.getName()); - /* - * sb.append("Transition"); sb.append("{name='").append(id).append('\''); - * sb.append(", sourceState=").append(sourceState.getId()); - * sb.append(", targetState=").append(targetState.getId()); - * sb.append(", id=").append(id); if (eventHandler != null) { - * sb.append(", eventHandler=").append(eventHandler.getClass().getName()); } - * sb.append('}'); - */ - return sb.toString(); - } - - @Override - public String getId() { - return id; - } - - @Override - public StateHandler getNewStateHandler() { - return newStateHandler; - } - - public void setId(String id) { - this.id = id; - } -} diff --git a/src/main/java/org/myrobotlab/fsm/core/package-info.java b/src/main/java/org/myrobotlab/fsm/core/package-info.java deleted file mode 100644 index ebd89a46bc..0000000000 --- a/src/main/java/org/myrobotlab/fsm/core/package-info.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/** - * This package contains the core implementation of finite state machine. - */ -package org.myrobotlab.fsm.core; diff --git a/src/main/java/org/myrobotlab/fsm/util/Utils.java b/src/main/java/org/myrobotlab/fsm/util/Utils.java deleted file mode 100644 index e7f489c577..0000000000 --- a/src/main/java/org/myrobotlab/fsm/util/Utils.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.myrobotlab.fsm.util; - -import java.util.Set; - -import org.myrobotlab.fsm.api.State; - -/** - * Constants and utilities class. - * - * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - */ -public final class Utils { - - private Utils() { - } - - /** - * Default event name. - */ - public static final String DEFAULT_EVENT_NAME = "event"; - - /** - * Default transition name. - */ - public static final String DEFAULT_TRANSITION_NAME = "transition"; - - /** - * Utility method to print states names as string. - * - * @param states - * the states set to dump - * @return string concatenation of states names - */ - public static String dumpFSMStates(final Set states) { - StringBuilder result = new StringBuilder(); - for (State state : states) { - result.append(state.getName()).append(";"); - } - return result.toString(); - } - -} diff --git a/src/main/java/org/myrobotlab/fsm/util/package-info.java b/src/main/java/org/myrobotlab/fsm/util/package-info.java deleted file mode 100644 index 88374162d4..0000000000 --- a/src/main/java/org/myrobotlab/fsm/util/package-info.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * The MIT License - * - * Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/** - * This package contains utility classes. - */ -package org.myrobotlab.fsm.util; diff --git a/src/main/java/org/myrobotlab/genetic/Chromosome.java b/src/main/java/org/myrobotlab/genetic/Chromosome.java deleted file mode 100644 index 1d070a6712..0000000000 --- a/src/main/java/org/myrobotlab/genetic/Chromosome.java +++ /dev/null @@ -1,106 +0,0 @@ -package org.myrobotlab.genetic; - -import java.util.ArrayList; -import java.util.Random; - -public class Chromosome { - String genome; - double fitness; - ArrayList decodedGenome; - - Chromosome(int genomeSize) { - Random rand = new Random(); - genome = new String(); - for (int i = 0; i < genomeSize; i++) { - int randomValue = rand.nextInt(2); - String catValue = ((Integer) randomValue).toString(); - genome += catValue; - } - } - - public Chromosome(Chromosome chromosome) { - // TODO Auto-generated constructor stub - this.decodedGenome = new ArrayList(chromosome.decodedGenome); - this.genome = new String(chromosome.genome); - } - - public Chromosome() { - // TODO Auto-generated constructor stub - } - - /** - * @return the genome - */ - public String getGenome() { - return genome; - } - - /** - * @return the fitness - */ - public double getFitness() { - return fitness; - } - - /** - * @return the decodedGenome - */ - public ArrayList getDecodedGenome() { - return decodedGenome; - } - - /** - * @param genome - * the genome to set - */ - public void setGenome(String genome) { - this.genome = genome; - } - - /** - * @param d - * the fitness to set - */ - public void setFitness(double d) { - this.fitness = d; - } - - /** - * @param decodedGenome - * the decodedGenome to set - */ - public void setDecodedGenome(ArrayList decodedGenome) { - this.decodedGenome = decodedGenome; - } - - public Chromosome recombine(Chromosome c, double recombinationRate) { - // TODO Auto-generated method stub - Random rand = new Random(); - Chromosome chromosome = new Chromosome(); - if (rand.nextDouble() < recombinationRate) { - int randomNumber = rand.nextInt(genome.length() - 1); - chromosome.genome = genome.substring(0, randomNumber + 1) + c.genome.substring(randomNumber + 1); - } else { - chromosome.genome = genome; - } - return chromosome; - } - - public Chromosome mutate(double mutationRate) { - // TODO Auto-generated method stub - Random rand = new Random(); - String newGenome = new String(); - for (int i = 0; i < genome.length(); i++) { - if (rand.nextDouble() < mutationRate) { - if (genome.charAt(i) == '1') - newGenome += "0"; - else - newGenome += "1"; - } else - newGenome += genome.substring(i, i + 1); - } - Chromosome ret = new Chromosome(); - ret.setGenome(newGenome); - return ret; - } -} \ No newline at end of file diff --git a/src/main/java/org/myrobotlab/genetic/Genetic.java b/src/main/java/org/myrobotlab/genetic/Genetic.java deleted file mode 100644 index 422af67747..0000000000 --- a/src/main/java/org/myrobotlab/genetic/Genetic.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * - */ -package org.myrobotlab.genetic; - -import java.util.ArrayList; - -/** - * @author Christian/Calamity - * - */ -public interface Genetic { - void calcFitness(ArrayList chromosomes); - - void decode(ArrayList chromosomes); - -} diff --git a/src/main/java/org/myrobotlab/genetic/GeneticAlgorithm.java b/src/main/java/org/myrobotlab/genetic/GeneticAlgorithm.java deleted file mode 100644 index 34efd004a2..0000000000 --- a/src/main/java/org/myrobotlab/genetic/GeneticAlgorithm.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.myrobotlab.genetic; - -import java.util.ArrayList; -import java.util.Random; - -/** - * Genetic Algorithms implementation - * - * This implementation of the Genetic algorithms describe on this site - * http://www.ai-junkie.com/ga/intro/gat1.html - * - * The principle is simple, you have a bank of chromosomes that contains a serie - * of bits that can be decoded by your application Each chromosomes are then - * check to see how they can solve what you try to intend, and get a score - * (fitness) to express how good they solves it. Chromosomes are then picks at - * random from the pool, with chromosomes weighted by their fitness, then - * recombine (swap part of their bits serie) and mutate (changing random bits in - * their serie) and put into a new pool of chromosome (generation). The - * algorithm run a number of generation and try to improve his best fitting - * chromosome. - * - * Usage: implements Genetic interface in the class you want to use the - * algorithm for the decode (how you convert the serie of bits to your data) and - * the calcFitness (give a score to your chromosome) methods Instanciate - * GeneticAlgorithm with it's config parameters populationSize: number of - * chromosome in the pool genomeSize: the number of gene in your chromosome (the - * number of data you want to use) geneSize: the number of byte you use for each - * gene (data) recombinationRate: Is the chance that two chromosomes get mixed - * together, 0.7 is usually a good start mutationRate: Is the chance that a - * giving bit get modified, 0.001 is usually a good start call - * doGeneration(number of generation) to get the best fitting chromosome - * - * The parameters are very empiric. They will influence how quick and precise - * the chromosome will evolve toward your best solution. - * - * @author Christian/Calamity - * - */ - -public class GeneticAlgorithm { - - double recombinationRate = 0.7; - double mutationRate = 0.001; - int populationPoolSize = 100; - ArrayList chromosomes = new ArrayList(); - int geneSize = 8; - private Genetic geneticClass; - - public GeneticAlgorithm(Genetic geneticClass, int populationSize, int genomeSize, int geneSize, double recombinationRate, double mutationRate) { - // TODO Auto-generated constructor stub - populationPoolSize = populationSize; - this.recombinationRate = recombinationRate; - this.mutationRate = mutationRate; - this.geneSize = geneSize; - this.geneticClass = geneticClass; - for (int i = 0; i < populationSize; i++) { - Chromosome chromo = new Chromosome(genomeSize * geneSize); - chromosomes.add(chromo); - } - } - - public GeneticAlgorithm(Genetic geneticClass, int genomeSize, GeneticParameters param) { - populationPoolSize = param.getGeneticPoolSize(); - recombinationRate = param.getGeneticRecombinationRate(); - mutationRate = param.getGeneticMutationRate(); - geneSize = genomeSize; - this.geneticClass = geneticClass; - for (int i = 0; i < populationPoolSize; i++) { - Chromosome chromo = new Chromosome(genomeSize * geneSize); - chromosomes.add(chromo); - } - } - - public Chromosome doGeneration(int generation) { - // decode the genes - // calculate the fitness of the pool - Chromosome bestFit = chromosomes.get(0); - for (int i = 0; i < generation; i++) { - geneticClass.decode(chromosomes); - geneticClass.calcFitness(chromosomes); - Double totalFitness = 0.0; - for (Chromosome chromosome : chromosomes) { - totalFitness += chromosome.fitness; - if (bestFit != null && chromosome.fitness > bestFit.fitness) { - bestFit = chromosome; - } - } - if (i != generation - 1) { // last iteration, no need to mutate - ArrayList newPool = new ArrayList(); - newPool.add(bestFit); - while (newPool.size() < populationPoolSize) { - Chromosome c1 = new Chromosome(RandomWheel(chromosomes, totalFitness)); - Chromosome c2 = new Chromosome(RandomWheel(chromosomes, totalFitness)); - newPool.add(c1.recombine(c2, recombinationRate).mutate(mutationRate)); - newPool.add(c2.recombine(c1, recombinationRate).mutate(mutationRate)); - } - chromosomes = newPool; - } - } - return bestFit; - } - - private Chromosome RandomWheel(ArrayList chromosomes, Double totalFitness) { - Random rand = new Random(); - double randomNumber = rand.nextDouble() * totalFitness; - Double fitnessCount = 0.0; - for (Chromosome chromosome : chromosomes) { - fitnessCount += chromosome.fitness; - if (randomNumber <= fitnessCount) { - return chromosome; - } - } - return null; - } - - public void setGeneticClass(Genetic geneticClass) { - this.geneticClass = geneticClass; - } - -} diff --git a/src/main/java/org/myrobotlab/genetic/GeneticParameters.java b/src/main/java/org/myrobotlab/genetic/GeneticParameters.java deleted file mode 100644 index 7163d2a316..0000000000 --- a/src/main/java/org/myrobotlab/genetic/GeneticParameters.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * - */ -package org.myrobotlab.genetic; - -/** - * @author Christian - * - */ -public class GeneticParameters { - private int geneticPoolSize = 200; - private double geneticMutationRate = 0.01; - private double geneticRecombinationRate = 0.7; - private int geneticGeneration = 300; - private boolean geneticComputeSimulation = false; - - /** - * @return the geneticComputeSimulation - */ - public boolean getGeneticComputeSimulation() { - return geneticComputeSimulation; - } - - /** - * @param geneticComputeSimulation - * the geneticComputeSimulation to set - */ - public void setGeneticComputeSimulation(boolean geneticComputeSimulation) { - this.geneticComputeSimulation = geneticComputeSimulation; - } - - /** - * @return the geneticPoolSize - */ - public int getGeneticPoolSize() { - return geneticPoolSize; - } - - /** - * @param geneticPoolSize - * the geneticPoolSize to set - */ - public void setGeneticPoolSize(int geneticPoolSize) { - this.geneticPoolSize = geneticPoolSize; - } - - /** - * @return the geneticMutationRate - */ - public double getGeneticMutationRate() { - return geneticMutationRate; - } - - /** - * @param geneticMutationRate - * the geneticMutationRate to set - */ - public void setGeneticMutationRate(double geneticMutationRate) { - this.geneticMutationRate = geneticMutationRate; - } - - /** - * @return the geneticRecombinationRate - */ - public double getGeneticRecombinationRate() { - return geneticRecombinationRate; - } - - /** - * @param geneticRecombinationRate - * the geneticRecombinationRate to set - */ - public void setGeneticRecombinationRate(double geneticRecombinationRate) { - this.geneticRecombinationRate = geneticRecombinationRate; - } - - /** - * @return the geneticGeneration - */ - public int getGeneticGeneration() { - return geneticGeneration; - } - - /** - * @param geneticGeneration - * the geneticGeneration to set - */ - public void setGeneticGeneration(int geneticGeneration) { - this.geneticGeneration = geneticGeneration; - } -} diff --git a/src/main/java/org/myrobotlab/image/Util.java b/src/main/java/org/myrobotlab/image/Util.java index ff79c78387..2af64df713 100644 --- a/src/main/java/org/myrobotlab/image/Util.java +++ b/src/main/java/org/myrobotlab/image/Util.java @@ -609,11 +609,7 @@ public static FloatPointer arrayListToFloatPointer(ArrayList confidences) * for display inline in html. * * @param bytes - * input bytes - * @return a string - * @throws IOException - * boom - * + * @return */ public static String bytesToBase64Jpg(byte[] bytes) { // diff --git a/src/main/java/org/myrobotlab/io/FileIO.java b/src/main/java/org/myrobotlab/io/FileIO.java index 80d665e101..2cdad66af2 100644 --- a/src/main/java/org/myrobotlab/io/FileIO.java +++ b/src/main/java/org/myrobotlab/io/FileIO.java @@ -58,14 +58,12 @@ import java.util.zip.ZipException; import org.apache.commons.io.Charsets; -import org.myrobotlab.cmdline.CmdLine; import org.myrobotlab.framework.Platform; import org.myrobotlab.framework.Service; import org.myrobotlab.logging.Level; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.logging.Logging; import org.myrobotlab.logging.LoggingFactory; -import org.myrobotlab.service.Python; import org.myrobotlab.service.Runtime; import org.slf4j.Logger; @@ -824,19 +822,6 @@ public static void main(String[] args) throws ZipException, IOException { result = "\\a\\" + File.separator + "b\\"; // assert /a/b/ - /* - * URI ?? full circle URL url = new - * URL("jar:file:/C:/Program%20Files/test.jar!/foo/bar"); JarURLConnection - * connection = (JarURLConnection) url.openConnection(); File file = new - * File(connection.getJarFileURL().toURI()) - * - * getResource ! takes string - returns url URL url = - * FileIO.class.getResource("/com"); => - * jar:file:/C:/mrlDevelop/repo/org.alicebot.ab/0.0.6.26/Ab.jar!/com - * - * - */ - try { // TODO - matrix of all file listing / url listings @@ -846,8 +831,6 @@ public static void main(String[] args) throws ZipException, IOException { // TODO - various other url path combos // TODO - make a jar - test it - CmdLine cmdLine = new CmdLine(args); - log.info("=== jar info begin ==="); log.info("source url [{}]", FileIO.class.getProtectionDomain().getCodeSource().getLocation()); log.info("source uri [{}]", FileIO.class.getProtectionDomain().getCodeSource().getLocation().toURI()); @@ -860,33 +843,6 @@ public static void main(String[] args) throws ZipException, IOException { URL url = FileIO.class.getResource("/com"); log.info("{}", url); - // File test = new File(url.toURI()); - // log.info("{}", test.exists()); - // File test = new File("/C:/") - - // === jar info begin === - // source url - // [file:/C:/mrlDevelop/myrobotlab/dist/current/develop/myrobotlab.jar] - // source uri - // [file:/C:/mrlDevelop/myrobotlab/dist/current/develop/myrobotlab.jar] - // source path - // [/C:/mrlDevelop/myrobotlab/dist/current/develop/myrobotlab.jar] - // === jar info end === - // getRoot - // [/C:/mrlDevelop/myrobotlab/dist/current/develop/myrobotlab.jar] - // file:/c:/windows exists true - // file:///c:/windows exists true - // - // - // final URL jarUrl = new - // URL("jar:file:/C:/proj/parser/jar/parser.jar!/test.xml"); - // final JarURLConnection connection = (JarURLConnection) - // jarUrl.openConnection(); - // final URL url = connection.getJarFileURL(); - // - // System.out.println(url.getFile()); - // - List services = getServiceList(); log.info("{}", services.size()); @@ -894,155 +850,12 @@ public static void main(String[] args) throws ZipException, IOException { File f = new File(uri); log.info("{} exists {}", uri, f.exists()); - // uri = new URI("file://c:/windows"); - // f = new File(uri); - // log.info("{} exists {}", uri, f.exists()); - // throws - java.lang.IllegalArgumentException: URI has an authority - // component - uri = new URI("file:///c:/windows"); f = new File(uri); log.info("{} exists {}", uri, f.exists()); - /* - * URI uri = new URI("file://c:/windows"); File f = new File(uri); - * log.info("{} exists {}", uri, f.exists()); - */ - - // info part - - // test examples root - . ./ / <-- absolute - String root = cmdLine.getSafeArgument("-root", 0, "dist/current/develop/myrobotlab.jar"); - String src = cmdLine.getSafeArgument("-src", 0, "resource/Python/examples"); - String dst = cmdLine.getSafeArgument("-dst", 0, "test2"); - log.info("dst arg: {}", dst); - // - List files = listResourceContents("Python/examples"); - log.info("listInternalContents /Python/examples size {}", files.size()); - - List urls = null; - - log.info("findPackageContents resource/Python/examples"); - // urls = listContents(root, gluePaths(Service.getResourceRoot(), - // "/Python/examples")); - - log.info("findPackageContents resource/Python/examples {}", urls.size()); - - /* - * for (int i = 0; i < urls.size(); ++i) { File test = new - * File(urls.get(i).getPath()); String x = FileIO.toString(test); - * log.info("{}", test); } - */ - - urls = listContents(getRoot(), Service.getResourceDir(Python.class, "examples")); - log.info("findPackageContents {}/Python/examples {}", Service.getResourceDir(Python.class, "examples"), urls.size()); - - urls = listContents(src); - log.info("findPackageContents {} {}", src, urls.size()); - - urls = listContents(root, "org/myrobotlab/service"); - - // urls = getPackageContent("org.myrobotlab.service"); - // log.info("listResourceContents {} {}", src, urls.size()); - - // DOOD ! - listResourceContents findPackagContent getPackageContent - - // copy requirements - // FIXME - don't use package names for consistency - these are all - // file manipulations - use file notation - - // deravations for root="/c:/.../bin/ or /c:/.../myrobotlab.jar!" - // extract root="/c:/.../bin or /c:/.../myrobotlab.jar" src="/ or /* - // or blank or null or ./" dst="/ or /* or blank or null or ./" - // the contents of (bin or myrobotlab.jar) will be extracted in the - // current directory - // root bounds test .. root="/" src="/" dst="?" - // more testing spaces, special characters, UTF-8 - - // log.info("extract test"); - // extractResources(true); - // extract(src, dst); - - // inJar - // extract ("/resource", "test"); -> .\bin\resource\Python\examples - // does not exist what the hell? - // extract(/C:/mrlDevelop/myrobotlab/dist/current/develop/myrobotlab.jar, - // /, test) - - /* - * final URL jarUrl = new - * URL("jar:file:/C:/mrl/myrobotlab/dist/myrobotlab.jar!/resource"); final - * JarURLConnection connection = (JarURLConnection) - * jarUrl.openConnection(); final URL url = connection.getJarFileURL(); - * - * System.out.println(url.getFile()); - */ - log.info("isJar : {}", isJar()); - // File[] files = getPackageContent("org.myrobotlab.service"); - - // extract("develop/myrobotlab.jar", "resource/version.txt", - // "./version.txt"); - - // extract("/C:/mrl/myrobotlab/dist/myrobotlab.jar", "resource", - // ""); - // extract("dist/myrobotlab.jar", "resource", ""); - // extractResources(); - /* - * // extract directory to a non existent directory // result should be - * test7 extract("dist/myrobotlab.jar", "resource/AdafruitMotorShield/*", - * "test66"); - * - * // file to file extract("dist/myrobotlab.jar", "module.properties", - * "module.txt"); - * - * // file to file extract("dist/myrobotlab.jar", - * "resource/ACEduinoMotorShield.png", "ACEduinoMotorShield.png"); - * - * // file to file extract("dist/myrobotlab.jar", - * "resource/ACEduinoMotorShield.png", - * "test2/deeper/ACEduinoMotorShield.png"); - * - * // extract directory to a non existent directory // result should be - * test7 extract("dist/myrobotlab.jar", "resource/*", "test7"); - * - * // extract directory to a non existent directory // result should be - * test8/testdeeper/(contents of resource) extract("dist/myrobotlab.jar", - * "resource/", "test8/testdeeper"); - * - * // extract directory to a non existent directory // result should be - * test3/deep/deeper/resource extract("dist/myrobotlab.jar", "resource", - * "test3/deep/deeper"); - * - * String t = "this is a test"; FileIO.savePartFile("save.txt", - * t.getBytes()); byte[] data = FileIO.loadPartFile("save.txt", 10000); if - * (data != null) { log.info(new String(data)); } - */ - - /* - * String data = resourceToString("version.txt"); data = - * resourceToString("framework/ivychain.xml"); data = - * resourceToString("framework/serviceData.xml"); - * - * byte[] ba = resourceToByteArray("version.txt"); ba = - * resourceToByteArray("framework/version.txt"); ba = - * resourceToByteArray("framework/serviceData.xml"); - * - * String hello = resourceToString("blah.txt"); - * - * copyResource("mrl_logo.jpg", "mrl_logo.jpg"); - * - * byte[] b = resourceToByteArray("mrl_logo.jpg"); - * - * File[] files = getPackageContent(""); - * - * log.info(getBinaryPath()); - * - * log.info("{}", b); - * - * log.info("done"); - */ } catch (Exception e) { Logging.logError(e); } @@ -1574,9 +1387,10 @@ public static String getExt(final String filename) { } return null; } - + /** * validate a directory exists + * * @param dir * @return */ @@ -1589,9 +1403,10 @@ public static boolean checkDir(String dir) { } return false; } - + /** * validate a file exists + * * @param filename * @return */ @@ -1605,7 +1420,6 @@ public static boolean checkFile(String filename) { return false; } - /** * flips all \ to / or / to \ depending on OS * @@ -1625,5 +1439,4 @@ public static String normalize(String dirPath) { } } - } diff --git a/src/main/java/org/myrobotlab/jme3/InMoov3DApp.java b/src/main/java/org/myrobotlab/jme3/InMoov3DApp.java deleted file mode 100644 index 9123f0723a..0000000000 --- a/src/main/java/org/myrobotlab/jme3/InMoov3DApp.java +++ /dev/null @@ -1,871 +0,0 @@ -package org.myrobotlab.jme3; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Queue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; - -import org.myrobotlab.framework.Service; -import org.myrobotlab.kinematics.CollisionItem; -import org.myrobotlab.kinematics.Map3DPoint; -import org.myrobotlab.kinematics.Point; -import org.myrobotlab.logging.LoggerFactory; -import org.myrobotlab.math.MapperLinear; -import org.myrobotlab.math.interfaces.Mapper; -import org.myrobotlab.service.interfaces.ServoControl; -import org.myrobotlab.service.interfaces.ServoEvent; -import org.slf4j.Logger; - -import com.jme3.app.SimpleApplication; -import com.jme3.asset.plugins.FileLocator; -import com.jme3.font.BitmapFont; -import com.jme3.font.BitmapText; -import com.jme3.input.KeyInput; -import com.jme3.input.MouseInput; -import com.jme3.input.controls.AnalogListener; -import com.jme3.input.controls.KeyTrigger; -import com.jme3.input.controls.MouseAxisTrigger; -import com.jme3.input.controls.MouseButtonTrigger; -import com.jme3.light.DirectionalLight; -import com.jme3.material.Material; -import com.jme3.math.ColorRGBA; -import com.jme3.math.FastMath; -import com.jme3.math.Vector3f; -import com.jme3.scene.Geometry; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; -import com.jme3.scene.shape.Box; -import com.jme3.scene.shape.Cylinder; -import com.jme3.texture.Texture2D; -import com.jme3.ui.Picture; - -/** - * @author Christian version 1.0.3 - */ -public class InMoov3DApp extends SimpleApplication implements IntegratedMovementInterface { - - transient public final static Logger log = LoggerFactory.getLogger(InMoov3DApp.class); - - private transient HashMap nodes = new HashMap(); - private Queue eventQueue = new ConcurrentLinkedQueue(); - private transient HashMap servoToNode = new HashMap(); - private HashMap maps = new HashMap<>(); - private transient Service service = null; - private transient Queue nodeQueue = new ConcurrentLinkedQueue(); - private transient Queue bitmapTextQueue = new ConcurrentLinkedQueue(); - private transient Queue pictureQueue = new ConcurrentLinkedQueue(); - private transient Queue batteryQueue = new ConcurrentLinkedQueue(); - - private HashMap shapes = new HashMap(); - private boolean updateCollisionItem; - private Queue pointQueue = new ConcurrentLinkedQueue(); - private transient Node point; - private transient ArrayList collisionItems = new ArrayList(); - public String BackGroundColor; - public ColorRGBA BackGroundColorRgba = ColorRGBA.Gray; - - // poc monitor update text fields - public boolean VinmoovMonitorActivated = false; - public boolean leftArduinoConnected = false; - public boolean rightArduinoConnected = false; - protected String onRecognizedText = ""; - protected BitmapText leftArduino; - protected BitmapText rightArduino; - protected BitmapText onRecognized; - protected Picture microOn; - protected Picture microOff; - protected Picture battery[] = new Picture[101]; - protected Texture2D textureBat[] = new Texture2D[101]; - private long startUpdateTs; - private long deltaMs; - private long sleepMs; - - public void setLeftArduinoConnected(boolean param) { - leftArduinoConnected = param; - bitmapTextQueue.add(leftArduino); - } - - public void setRightArduinoConnected(boolean param) { - rightArduinoConnected = param; - bitmapTextQueue.add(rightArduino); - } - - public void setMicro(boolean param) { - if (param) { - pictureQueue.add(microOn); - } else { - pictureQueue.add(microOff); - } - } - - public void onRecognized(String text) { - onRecognizedText = text; - bitmapTextQueue.add(onRecognized); - } - - public void setBatteryLevel(Integer level) { - - if (level >= 80) { - level = 100; - } - if (level >= 60 && level < 80) { - level = 80; - } - if (level >= 40 && level < 60) { - level = 60; - } - if (level >= 20 && level < 40) { - level = 40; - } - if (level >= 10 && level < 20) { - level = 20; - } - if (level >= 0 && level < 10) { - level = 0; - } - - if (!(level >= 0) && !(level <= 100)) { - level = 100; - } - - batteryQueue.add(battery[level]); - - } - // end monitor - - @Override - public void simpleInitApp() { - assetManager.registerLocator("InMoov/jm3/assets", FileLocator.class); - - inputManager.setCursorVisible(true); - flyCam.setEnabled(false); - cam.setLocation(new Vector3f(0f, 0f, 900f)); - - inputManager.addMapping("MouseClickL", new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); - inputManager.addListener(analogListener, "MouseClickL"); - inputManager.addMapping("MouseClickR", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT)); - inputManager.addListener(analogListener, "MouseClickR"); - inputManager.addMapping("MMouseUp", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false)); - inputManager.addListener(analogListener, "MMouseUp"); - inputManager.addMapping("MMouseDown", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true)); - inputManager.addListener(analogListener, "MMouseDown"); - inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A), new KeyTrigger(KeyInput.KEY_LEFT)); // A - // and - // left - // arrow - inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D), new KeyTrigger(KeyInput.KEY_RIGHT)); // D - // and - // right - // arrow - inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W), new KeyTrigger(KeyInput.KEY_UP)); // A - // and - // left - // arrow - inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S), new KeyTrigger(KeyInput.KEY_DOWN)); // D - // and - // right - // arrow - inputManager.addMapping("ZoomIn", new KeyTrigger(KeyInput.KEY_E)); - inputManager.addMapping("ZoomOut", new KeyTrigger(KeyInput.KEY_Q)); - inputManager.addListener(analogListener, new String[] { "Left", "Right", "Up", "Down", "ZoomIn", "ZoomOut" }); - // no worky - inputManager.addMapping("FullScreen", new KeyTrigger(KeyInput.KEY_F)); - inputManager.addListener(analogListener, "FullScreen"); - - switch (BackGroundColor) { - case "Gray": - BackGroundColorRgba = ColorRGBA.Gray; - break; - case "Black": - BackGroundColorRgba = ColorRGBA.Black; - break; - case "White": - BackGroundColorRgba = ColorRGBA.White; - break; - case "DarkGray": - BackGroundColorRgba = ColorRGBA.DarkGray; - break; - case "LightGray": - BackGroundColorRgba = ColorRGBA.LightGray; - break; - case "Red": - BackGroundColorRgba = ColorRGBA.Red; - break; - case "Green": - BackGroundColorRgba = ColorRGBA.Green; - break; - case "Blue": - BackGroundColorRgba = ColorRGBA.Blue; - break; - case "Magenta": - BackGroundColorRgba = ColorRGBA.Magenta; - break; - case "Cyan": - BackGroundColorRgba = ColorRGBA.Cyan; - break; - case "Orange": - BackGroundColorRgba = ColorRGBA.Orange; - break; - case "Yellow": - BackGroundColorRgba = ColorRGBA.Yellow; - break; - case "Brown": - BackGroundColorRgba = ColorRGBA.Brown; - break; - case "Pink": - BackGroundColorRgba = ColorRGBA.Yellow; - break; - default: - BackGroundColorRgba = ColorRGBA.Gray; - } - - viewPort.setBackgroundColor(BackGroundColorRgba); - - DirectionalLight sun = new DirectionalLight(); - sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f)); - rootNode.addLight(sun); - rootNode.scale(.5f); - rootNode.setLocalTranslation(0, -200, 0); - - Node node = new Node("ltorso"); - rootNode.attachChild(node); - Spatial spatial = assetManager.loadModel("Models/ltorso.j3o"); - spatial.setName("ltorso"); - node.attachChild(spatial); - node.setLocalTranslation(new Vector3f(0, 0, 0)); - Vector3f rotationMask = Vector3f.UNIT_X.mult(1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - nodes.put("ltorso", node); - - node = new Node("mtorso"); - Node parentNode = nodes.get("ltorso"); - parentNode.attachChild(node); - spatial = assetManager.loadModel("Models/mtorso.j3o"); - spatial.setName("mtorso"); - node.attachChild(spatial); - node.setLocalTranslation(new Vector3f(0, 0, 0)); - rotationMask = Vector3f.UNIT_Y.mult(-1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - nodes.put("mtorso", node); - maps.put("mtorso", new MapperLinear(0, 180, 120, 60)); - - node = new Node("ttorso"); - parentNode = nodes.get("mtorso"); - parentNode.attachChild(node); - spatial = assetManager.loadModel("Models/ttorso1.j3o"); - spatial.setName("mtorso"); - node.attachChild(spatial); - node.setLocalTranslation(new Vector3f(0, 105, 10)); - rotationMask = Vector3f.UNIT_Z.mult(1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - nodes.put("ttorso", node); - maps.put("ttorso", new MapperLinear(0, 180, 80, 100)); - - node = new Node("rightS"); - parentNode = nodes.get("ttorso"); - parentNode.attachChild(node); - node.setLocalTranslation(new Vector3f(0, 300, 0)); - rotationMask = Vector3f.UNIT_Z.mult(1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - nodes.put("rightS", node); - - node = new Node("Romoplate"); - parentNode = nodes.get("rightS"); - parentNode.attachChild(node); - spatial = assetManager.loadModel("Models/Romoplate1.j3o"); - spatial.setName("Romoplate"); - node.attachChild(spatial); - node.setLocalTranslation(new Vector3f(-143, 0, -17)); - rotationMask = Vector3f.UNIT_Z.mult(-1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - Vector3f angle = rotationMask.mult((float) Math.toRadians(6)); - node.rotate(angle.x, angle.y, angle.z); - nodes.put("Romoplate", node); - maps.put("Romoplate", new MapperLinear(0, 180, 10, 70)); - - node = new Node("Rshoulder"); - parentNode = nodes.get("Romoplate"); - parentNode.attachChild(node); - spatial = assetManager.loadModel("Models/Rshoulder1.j3o"); - spatial.setName("Rshoulder"); - node.attachChild(spatial); - node.setLocalTranslation(new Vector3f(-23, -45, 0)); - rotationMask = Vector3f.UNIT_X.mult(-1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - angle = rotationMask.mult((float) Math.toRadians(-2)); - node.rotate(angle.x, angle.y, angle.z); - nodes.put("Rshoulder", node); - maps.put("Rshoulder", new MapperLinear(0, 180, 0, 180)); - - node = new Node("Rrotate"); - parentNode = nodes.get("Rshoulder"); - parentNode.attachChild(node); - spatial = assetManager.loadModel("Models/rotate1.j3o"); - spatial.setName("Rrotate"); - node.attachChild(spatial); - node.setLocalTranslation(new Vector3f(-57, -55, 8)); - rotationMask = Vector3f.UNIT_Y.mult(-1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - nodes.put("Rrotate", node); - maps.put("Rrotate", new MapperLinear(0, 180, 40, 180)); - - node = new Node("Rbicep"); - parentNode = nodes.get("Rrotate"); - parentNode.attachChild(node); - spatial = assetManager.loadModel("Models/Rbicep1.j3o"); - spatial.setName("Rbicep"); - node.attachChild(spatial); - node.setLocalTranslation(new Vector3f(5, -225, -32)); - rotationMask = Vector3f.UNIT_X.mult(-1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - angle = rotationMask.mult((float) Math.toRadians(30)); - node.rotate(angle.x, angle.y, angle.z); - // node.rotateUpTo(angle); - nodes.put("Rbicep", node); - maps.put("Rbicep", new MapperLinear(0, 180, 5, 60)); - - node = new Node("leftS"); - parentNode = nodes.get("ttorso"); - parentNode.attachChild(node); - node.setLocalTranslation(new Vector3f(0, 300, 0)); - rotationMask = Vector3f.UNIT_Z.mult(1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - nodes.put("leftS", node); - - node = new Node("omoplate"); - parentNode = nodes.get("leftS"); - parentNode.attachChild(node); - spatial = assetManager.loadModel("Models/Lomoplate1.j3o"); - spatial.setName("omoplate"); - node.attachChild(spatial); - node.setLocalTranslation(new Vector3f(143, 0, -15)); - rotationMask = Vector3f.UNIT_Z.mult(1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - angle = rotationMask.mult((float) Math.toRadians(4)); - node.rotate(angle.x, angle.y, angle.z); - nodes.put("omoplate", node); - maps.put("omoplate", new MapperLinear(0, 180, 10, 70)); - - node = new Node("shoulder"); - parentNode = nodes.get("omoplate"); - parentNode.attachChild(node); - spatial = assetManager.loadModel("Models/Lshoulder.j3o"); - spatial.setName("shoulder"); - node.attachChild(spatial); - node.setLocalTranslation(new Vector3f(17, -45, 5)); - rotationMask = Vector3f.UNIT_X.mult(-1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - nodes.put("shoulder", node); - maps.put("shoulder", new MapperLinear(0, 180, 0, 180)); - - node = new Node("rotate"); - parentNode = nodes.get("shoulder"); - parentNode.attachChild(node); - spatial = assetManager.loadModel("Models/rotate1.j3o"); - spatial.setName("rotate"); - node.attachChild(spatial); - node.setLocalTranslation(new Vector3f(65, -58, -3)); - rotationMask = Vector3f.UNIT_Y.mult(1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - nodes.put("rotate", node); - maps.put("rotate", new MapperLinear(0, 180, 40, 180)); - - node = new Node("bicep"); - parentNode = nodes.get("rotate"); - parentNode.attachChild(node); - spatial = assetManager.loadModel("Models/Lbicep.j3o"); - spatial.setName("bicep"); - node.attachChild(spatial); - node.setLocalTranslation(new Vector3f(-14, -223, -28)); - rotationMask = Vector3f.UNIT_X.mult(-1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - angle = rotationMask.mult((float) Math.toRadians(27)); - node.rotate(angle.x, angle.y, angle.z); - nodes.put("bicep", node); - maps.put("bicep", new MapperLinear(0, 180, 5, 60)); - - node = new Node("RWrist"); - parentNode = nodes.get("Rbicep"); - parentNode.attachChild(node); - spatial = assetManager.loadModel("Models/RWristFinger.j3o"); - spatial.setName("RWrist"); - node.attachChild(spatial); - node.setLocalTranslation(new Vector3f(15, -290, -10)); - rotationMask = Vector3f.UNIT_Y.mult(-1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - angle = rotationMask.mult((float) Math.toRadians(-90)); - node.rotate(angle.x, angle.y, angle.z); - nodes.put("RWrist", node); - maps.put("RWrist", new MapperLinear(0, 180, 130, 40)); - - node = new Node("LWrist"); - parentNode = nodes.get("bicep"); - parentNode.attachChild(node); - spatial = assetManager.loadModel("Models/LWristFinger.j3o"); - spatial.setName("LWrist"); - node.attachChild(spatial); - node.setLocalTranslation(new Vector3f(0, -290, -20)); - rotationMask = Vector3f.UNIT_Y.mult(1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - angle = rotationMask.mult((float) Math.toRadians(-90)); - node.rotate(angle.x, angle.y, angle.z); - nodes.put("LWrist", node); - maps.put("LWrist", new MapperLinear(0, 180, 40, 130)); - - node = new Node("neck"); - parentNode = nodes.get("ttorso"); - parentNode.attachChild(node); - spatial = assetManager.loadModel("Models/neck.j3o"); - spatial.setName("neck"); - node.attachChild(spatial); - node.setLocalTranslation(new Vector3f(0, 452.5f, -45)); - rotationMask = Vector3f.UNIT_X.mult(-1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - nodes.put("neck", node); - maps.put("neck", new MapperLinear(0, 180, 60, 110)); - - node = new Node("rollNeck"); - parentNode = nodes.get("neck"); - parentNode.attachChild(node); - node.setLocalTranslation(new Vector3f(0, 0, 0)); - rotationMask = Vector3f.UNIT_Z.mult(1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - angle = rotationMask.mult((float) Math.toRadians(2)); - node.rotate(angle.x, angle.y, angle.z); - nodes.put("rollNeck", node); - maps.put("rollNeck", new MapperLinear(0, 180, 60, 115)); - - node = new Node("head"); - parentNode = nodes.get("rollNeck"); - parentNode.attachChild(node); - spatial = assetManager.loadModel("Models/head.j3o"); - spatial.setName("head"); - node.attachChild(spatial); - node.setLocalTranslation(new Vector3f(0, 10f, 20)); - rotationMask = Vector3f.UNIT_Y.mult(-1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - nodes.put("head", node); - maps.put("head", new MapperLinear(0, 180, 150, 30)); - - node = new Node("jaw"); - parentNode = nodes.get("head"); - parentNode.attachChild(node); - spatial = assetManager.loadModel("Models/jaw.j3o"); - spatial.setName("jaw"); - node.attachChild(spatial); - node.setLocalTranslation(new Vector3f(-5, 63f, -50)); - rotationMask = Vector3f.UNIT_X.mult(1); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - node.setUserData("currentAngle", 0); - angle = rotationMask.mult((float) Math.toRadians(0)); - node.rotate(angle.x, angle.y, angle.z); - nodes.put("jaw", node); - maps.put("jaw", new MapperLinear(0, 180, -10, 5)); - - // poc monitor declaration - - Texture2D textureBackGround = (Texture2D) assetManager.loadTexture("/resource/InMoov/monitor/monitor_back.png"); - Picture BackGround = new Picture("/resource/InMoov/monitor/monitor_back.png"); - BackGround.setTexture(assetManager, textureBackGround, true); - BackGround.setWidth(settings.getWidth()); - BackGround.setHeight(settings.getHeight()); - BackGround.setLocalTranslation(0.0F, 0.0F, 0.0F); - - double widthCoef = 1920F / settings.getWidth(); - double heightCoef = 1080F / settings.getHeight(); - - BitmapFont font = assetManager.loadFont("Interface/Fonts/Default.fnt"); - leftArduino = new BitmapText(font, false); - leftArduino.setLocalTranslation(0.0F, 100F, 0.0F); - leftArduino.setText(""); - rightArduino = new BitmapText(font, false); - rightArduino.setLocalTranslation(0.0F, 140F, 0.0F); - rightArduino.setText(""); - onRecognized = new BitmapText(font, false); - onRecognized.setLocalTranslation((settings.getWidth() / 2) - 180, settings.getHeight() - 20, 0.0F); - onRecognized.setText("Listening..."); - - Texture2D texture = (Texture2D) assetManager.loadTexture("/resource/InMoov/monitor/microOn.png"); - microOn = new Picture("/resource/InMoov/monitor/microOn.png"); - microOn.setTexture(assetManager, texture, true); - microOn.setWidth(Math.round(texture.getImage().getWidth() / widthCoef)); - microOn.setHeight(Math.round(texture.getImage().getHeight() / heightCoef)); - microOn.setLocalTranslation(10F, settings.getHeight() - 50, 0.0F); - - Texture2D textureOff = (Texture2D) assetManager.loadTexture("/resource/InMoov/monitor/microOff.png"); - microOff = new Picture("/resource/InMoov/monitor/microOff.png"); - microOff.setTexture(assetManager, textureOff, true); - microOff.setWidth(Math.round(textureOff.getImage().getWidth() / widthCoef)); - microOff.setHeight(Math.round(textureOff.getImage().getHeight() / heightCoef)); - microOff.setLocalTranslation(10F, settings.getHeight() - 50, 0.0F); - - for (int i = 0; i <= 100; i += 20) { - textureBat[i] = (Texture2D) assetManager.loadTexture("/resource/InMoov/monitor/bat_" + i + ".png"); - battery[i] = new Picture("/resource/InMoov/monitor/bat_" + i + ".png"); - battery[i].setTexture(assetManager, textureBat[i], true); - battery[i].setWidth(Math.round(textureBat[i].getImage().getWidth() / widthCoef)); - battery[i].setHeight(Math.round(textureBat[i].getImage().getHeight() / heightCoef)); - battery[i].setLocalTranslation(Math.round(settings.getWidth() - (textureBat[i].getImage().getWidth() / widthCoef)), - Math.round(settings.getHeight() - (textureBat[i].getImage().getHeight() / heightCoef)), 0.0F); - } - - if (VinmoovMonitorActivated) { - guiNode.attachChild(BackGround); - guiNode.attachChild(onRecognized); - guiNode.attachChild(battery[100]); - guiNode.attachChild(microOn); - } - - // end monitor - - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - mat.setColor("Color", ColorRGBA.Green); - Cylinder c = new Cylinder(4, 10, 5, 10, true, false); - Geometry geom = new Geometry("Cylinder", c); - geom.setMaterial(mat); - point = new Node("point"); - point.attachChild(geom); - rootNode.attachChild(point); - - if (service != null) { - synchronized (service) { - service.notifyAll(); - } - } - ; - - } - - /* - * - * @param name : name of the part - * - * @param modelPath : path leading the the 3dmesh (null for no model) - * - * @param modelScale : model will be scale to this parameter - * - * @param hookTo : attach this part to the hook part (null to hook to the - * root) - * - * @param relativePosition : position relative to the hook part - * - * @param rotationMask : set Vector3f.UNIT_X, Vector3f.UNIT_Y, - * Vector3f.UNIT_Z) for the axe of rotation - * - * @param initialAngle : initial angle of rotation of the part (in radian) - */ - - @Override - public void updatePosition(ServoEvent event) { - eventQueue.add(event); - } - - @Override - public void simpleUpdate(float tpf) { - // start the clock on how much time we will take - startUpdateTs = System.currentTimeMillis(); - - if (updateCollisionItem) { - for (Node node : collisionItems) { - if (node.getUserData("collisionItem") != null) { - node.removeFromParent(); - node.updateGeometricState(); - } - } - collisionItems.clear(); - } - - while (eventQueue.size() > 0) { - ServoEvent event = eventQueue.remove(); - if (servoToNode.containsKey(event.name)) { - Node node = servoToNode.get(event.name); - Vector3f rotMask = new Vector3f((float) node.getUserData("rotationMask_x"), (float) node.getUserData("rotationMask_y"), (float) node.getUserData("rotationMask_z")); - float currentAngle = (float) node.getUserData("currentAngle"); - Mapper map = maps.get(node.getName()); - float rotation = (float) ((map.calcOutput(event.pos)) * Math.PI / 180 - currentAngle * Math.PI / 180); - Vector3f angle = rotMask.mult(rotation); - node.rotate(angle.x, angle.y, angle.z); - node.setUserData("currentAngle", (map.calcOutput(event.pos))); - servoToNode.put(event.name, node); - nodes.put(node.getName(), node); - } - } - while (pointQueue.size() > 0) { - Point p = pointQueue.remove(); - point.setLocalTranslation((float) p.getX(), (float) p.getZ(), (float) p.getY()); - } - - if (VinmoovMonitorActivated) { - - while (pictureQueue.size() > 0) { - - Picture picture = pictureQueue.remove(); - // rootNode.updateGeometricState(); - - guiNode.detachChild(microOff); - guiNode.detachChild(microOn); - guiNode.attachChild(picture); - microOff.updateGeometricState(); - microOn.updateGeometricState(); - - } - - while (batteryQueue.size() > 0) { - - Picture picture = batteryQueue.remove(); - // rootNode.updateGeometricState(); - for (int i = 0; i <= 100; i += 20) { - guiNode.detachChild(battery[i]); - } - - // picture = new Picture("/resource/InMoov/monitor/bat_80.png"); - - guiNode.attachChild(picture); - for (int i = 0; i <= 100; i += 20) { - battery[i].updateGeometricState(); - } - - } - - while (bitmapTextQueue.size() > 0) { - Node bitmap = bitmapTextQueue.remove(); - String leftIndicator = "NOK"; - String rightIndicator = "NOK"; - if (leftArduinoConnected) { - leftIndicator = "OK"; - } - if (rightArduinoConnected) { - rightIndicator = "OK"; - } - // rootNode.updateGeometricState(); - // leftArduino.setText("Left Arduino : "+leftIndicator); - // rightArduino.setText("Right Arduino : "+rightIndicator); - onRecognized.setText(onRecognizedText); - guiNode.detachChild(bitmap); - guiNode.attachChild(bitmap); - bitmap.updateGeometricState(); - - } - - } - - while (nodeQueue.size() > 0) { - Node node = nodeQueue.remove(); - Node hookNode = nodes.get(node.getUserData("hookTo")); - if (hookNode == null) { - hookNode = rootNode; - } - hookNode.attachChild(node); - if (node.getUserData("collisionItem") != null) { - collisionItems.add(node); - } - Spatial x = hookNode.getChild(node.getName()); - if (x != null) { - rootNode.updateGeometricState(); - } - - } - - // To achieve ~30 fps, the thread will need to sleep for 33ms otherwise the - // update thread races through this function without pause to generating - // 300+ fps. - // If this update takes deltaMs to process then we will subtract that from - // the initial 33ms, - // to make total time spent in this method as close to 33ms as possible. - - deltaMs = System.currentTimeMillis() - startUpdateTs; - sleepMs = 33 - deltaMs; - try { - Thread.sleep(sleepMs); - } catch (Exception e) { - } - } - - // FIXME - race condition, if this method is called before JME is fully - // initialized :( - // the result is no servos are successfully added - public void addServo(String partName, ServoControl servo) { - if (nodes.containsKey(partName)) { - Node node = nodes.get(partName); - Mapper map = maps.get(partName); - map.setMinMax(servo.getMin(), servo.getMax()); - double angle = -map.calcOutput(servo.getRest()) + map.calcOutput(servo.getCurrentInputPos()); - angle *= Math.PI / 180; - Vector3f rotMask = new Vector3f((float) node.getUserData("rotationMask_x"), (float) node.getUserData("rotationMask_y"), (float) node.getUserData("rotationMask_z")); - Vector3f rotAngle = rotMask.mult((float) angle); - node.rotate(rotAngle.x, rotAngle.y, rotAngle.z); - node.setUserData("currentAngle", map.calcOutput(servo.getCurrentInputPos())); - nodes.put(partName, node); - servoToNode.put(servo.getName(), node); - maps.put(partName, map); - } else { - log.info(partName + " is not a valid part name for VinMoov"); - } - } - - @Override - public void setService(Service service) { - this.service = service; - } - - private AnalogListener analogListener = new AnalogListener() { - @Override - public void onAnalog(String name, float keyPressed, float tpf) { - if (name.equals("MouseClickL")) { - // rotate+= keyPressed; - rootNode.rotate(0, -keyPressed, 0); - // Log.info(rotate); - } else if (name.equals("MouseClickR")) { - // rotate+= keyPressed; - rootNode.rotate(0, keyPressed, 0); - // Log.info(rotate); - } else if (name.equals("MMouseUp") || name.equals("ZoomIn")) { - rootNode.setLocalScale(rootNode.getLocalScale().mult(1.01f)); - } else if (name.equals("MMouseDown") || name.equals("ZoomOut")) { - rootNode.setLocalScale(rootNode.getLocalScale().mult(0.99f)); - } else if (name.equals("Up")) { - rootNode.move(0, keyPressed * 100, 0); - } else if (name.equals("Down")) { - rootNode.move(0, -keyPressed * 100, 0); - } else if (name.equals("Left")) { - rootNode.move(-keyPressed * 100, 0, 0); - } else if (name.equals("Right")) { - rootNode.move(keyPressed * 100, 0, 0); - } - // seem no worky - else if (name.equals("FullScreen")) { - if (settings.isFullscreen()) { - settings.setFullscreen(false); - } else { - settings.setFullscreen(true); - } - } - } - }; - - @Override - public void addObject(CollisionItem item) { - if (!item.isRender()) { - return; - } - if (item.isFromKinect()) { - Node pivot = new Node(item.getName()); - for (Map3DPoint p : item.cloudMap.values()) { - Box b = new Box(4f, 4f, 4f); - Geometry geo = new Geometry("Box", b); - Vector3f pos = new Vector3f((float) p.point.getX(), (float) p.point.getZ(), (float) p.point.getY()); - geo.setLocalTranslation(pos); - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - mat.setColor("Color", ColorRGBA.Red); - geo.setMaterial(mat); - pivot.attachChild(geo); - } - pivot.setUserData("HookTo", null); - pivot.setUserData("collisionItem", "1"); - nodeQueue.add(pivot); - } else { - Vector3f ori = new Vector3f((float) item.getOrigin().getX(), (float) item.getOrigin().getZ(), (float) item.getOrigin().getY()); - Vector3f end = new Vector3f((float) item.getEnd().getX(), (float) item.getEnd().getZ(), (float) item.getEnd().getY()); - Cylinder c = new Cylinder(8, 50, (float) item.getRadius(), (float) item.getLength(), true, false); - Geometry geom = new Geometry("Cylinder", c); - shapes.put(item.getName(), geom); - geom.setLocalTranslation(FastMath.interpolateLinear(0.5f, ori, end)); - geom.lookAt(end, Vector3f.UNIT_Y); - // geom.scale(0.5f); - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - if (item.isFromKinect()) { - mat.setColor("Color", ColorRGBA.Red); - } else { - mat.setColor("Color", ColorRGBA.Blue); - } - geom.setMaterial(mat); - Node pivot = new Node(item.getName()); - pivot.attachChild(geom); - pivot.setUserData("HookTo", null); - pivot.setUserData("collisionItem", "1"); - nodeQueue.add(pivot); - } - } - - @Override - public void addObject(ConcurrentHashMap items) { - updateCollisionItem = true; - for (CollisionItem item : items.values()) { - addObject(item); - } - updateCollisionItem = false; - } - - @Override - public void addPoint(Point point) { - pointQueue.add(point); - } - - public void setMinMaxAngles(String partName, double min, double max) { - if (maps.containsKey(partName)) { - Mapper map = maps.get(partName); - map = new MapperLinear(map.getMinX(), map.getMaxX(), min, max); - maps.put(partName, map); - } else { - log.info("No part named " + partName + " found"); - } - } - -} \ No newline at end of file diff --git a/src/main/java/org/myrobotlab/jme3/IntegratedMovementInterface.java b/src/main/java/org/myrobotlab/jme3/IntegratedMovementInterface.java deleted file mode 100644 index 9c6dd0ad31..0000000000 --- a/src/main/java/org/myrobotlab/jme3/IntegratedMovementInterface.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * - */ -package org.myrobotlab.jme3; - -import java.util.concurrent.ConcurrentHashMap; - -import org.myrobotlab.framework.Service; -import org.myrobotlab.kinematics.CollisionItem; -import org.myrobotlab.kinematics.Point; -import org.myrobotlab.service.interfaces.ServoEvent; - -import com.jme3.system.AppSettings; - -/** - * @author chris - * - */ -public interface IntegratedMovementInterface { - public void updatePosition(ServoEvent event); - - public void setService(Service service); - - public void addObject(CollisionItem item); - - public void addObject(ConcurrentHashMap items); - - public void addPoint(Point point); - - public void setSettings(AppSettings settings); - - public void setShowSettings(boolean b); - - public void setPauseOnLostFocus(boolean b); - - public void start(); -} diff --git a/src/main/java/org/myrobotlab/kinematics/Action.java b/src/main/java/org/myrobotlab/kinematics/Action.java new file mode 100644 index 0000000000..4f3385ee1e --- /dev/null +++ b/src/main/java/org/myrobotlab/kinematics/Action.java @@ -0,0 +1,58 @@ +package org.myrobotlab.kinematics; + +import java.util.Map; + +public class Action { + // consider enum type + enum Type { + moveTo, speak, sleep, message + } + + /** + * type determines how to handle the value depending on what is desired ... + */ + public String type = "moveTo"; // moveTo | speak | sleep | Message + + /** + * delay type when type is Delay, String when type is Text + */ + public Object value; + + /** + * if set wait this many millis before next action blocking delay + */ + public Long waitMs = null; + + /** + * blocks if true - and will wait for this action to complete before going to + * the next action + */ + public boolean willBlock = false; + + @Override + public String toString() { + return String.format("part %s %s", type, (value != null) ? value.toString() : null); + } + + public static Action createMoveToAction(Map> moves) { + Action action = new Action(); + action.type = "moveTo"; + // TODO - check validity of moves + action.value = moves; + return action; + } + + public static Action createSleepAction(double sleep) { + Action action = new Action(); + action.type = "sleep"; + action.value = sleep; + return action; + } + + public static Action createSpeakAction(Map speechCommand) { + Action action = new Action(); + action.type = "speak"; + action.value = speechCommand; + return action; + } +} diff --git a/src/main/java/org/myrobotlab/kinematics/Gesture.java b/src/main/java/org/myrobotlab/kinematics/Gesture.java index bea0aae688..dc5d136a09 100644 --- a/src/main/java/org/myrobotlab/kinematics/Gesture.java +++ b/src/main/java/org/myrobotlab/kinematics/Gesture.java @@ -9,26 +9,11 @@ public class Gesture { /** * sequence of poses and offset times */ - protected List parts = new ArrayList<>(); + public List actions = new ArrayList<>(); - protected boolean repeat = false; - - public List getParts(){ - return parts; - } - - public boolean getRepeat() { - return repeat; - } - - public void setParts(List parts){ - this.parts = parts; - } - - public void setRepeat(boolean repeat) { - this.repeat = repeat; - } - - + /** + * loop this set of actions + */ + public boolean repeat = false; } diff --git a/src/main/java/org/myrobotlab/kinematics/GesturePart.java b/src/main/java/org/myrobotlab/kinematics/GesturePart.java deleted file mode 100644 index 232d0f6b59..0000000000 --- a/src/main/java/org/myrobotlab/kinematics/GesturePart.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.myrobotlab.kinematics; - -public class GesturePart { - - /** - * name of pose - */ - public String name; - - /** - * type determines how to handle the value - * depending on what is desired ... - */ - public String type; // Pose | Text | Delay | Message - - /** - * delay type when type is Delay, String when type is Text - */ - public Object value; - - /** - * if blocking true will wait until sequence part finished - */ - public boolean blocking = false; - - - @Override - public String toString() { - return String.format("part %s %s %s", name, type, (value != null)?value.toString():null); - } -} diff --git a/src/main/java/org/myrobotlab/kinematics/GravityCenter.java b/src/main/java/org/myrobotlab/kinematics/GravityCenter.java deleted file mode 100644 index f7fa8e8791..0000000000 --- a/src/main/java/org/myrobotlab/kinematics/GravityCenter.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.myrobotlab.kinematics; - -import java.util.HashMap; - -import org.myrobotlab.service.IntegratedMovement; - -/** - * This class will compute the center of gravity of the links composing a robot - * - * @author chris - * - */ -public class GravityCenter extends Thread { - - private HashMap masses = new HashMap(); - private HashMap cogs = new HashMap(); - private transient IntegratedMovement service; - private Point cog; - private Point cogTarget = new Point(0, 0, 0, 0, 0, 0); - private int maxDistanceToCog = 25; - - public GravityCenter(IntegratedMovement im) { - super("GravityCenter"); - service = im; - } - - /** - * Set the mass and center of mass of a link - * - * @param name - * name - * @param mass - * mass - * @param centerOfMass - * (0.0 - 1.0) representing where the center of mass is located, from - * the origin point. If you don't know, it's safe to put 0.5 - */ - public void setLinkMass(String name, double mass, double centerOfMass) { - masses.put(name, mass); - cogs.put(name, centerOfMass); - } - - public synchronized Point computeCoG(CollisionDectection cd) { - if (cd == null) { - cd = service.collisionItems; - } - double totalMass = 0; - for (double mass : masses.values()) { - totalMass += mass; - } - cog = new Point(0, 0, 0, 0, 0, 0); - for (CollisionItem ci : cd.getItems().values()) { - if (cogs.containsKey(ci.getName())) { - Point icog = ci.getEnd().subtract(ci.getOrigin()).unitVector(1).multiplyXYZ(cogs.get(ci.getName())).multiplyXYZ(ci.getLength()).add(ci.getOrigin()); - double m = masses.get(ci.getName()) / totalMass; - Point ic = icog.multiplyXYZ(m); - Point c = cog.add(ic); - cog = c; - } - } - // Log.info(cog.toString()+"gc"); - if (cog.getZ() <= 0.1) { - int x = 0; - - } - return cog; - - } - - public Point getCoG() { - return cog; - } - - public Point getCoGTarget() { - return cogTarget; - } - - public double getMaxDistanceToCog() { - return maxDistanceToCog; - } - - public void setCoGTarget(double x, double y, double z) { - cogTarget = new Point(x, y, z, 0, 0, 0); - } -} diff --git a/src/main/java/org/myrobotlab/kinematics/IMEngine.java b/src/main/java/org/myrobotlab/kinematics/IMEngine.java deleted file mode 100644 index d27252d425..0000000000 --- a/src/main/java/org/myrobotlab/kinematics/IMEngine.java +++ /dev/null @@ -1,883 +0,0 @@ -/** - * - */ -package org.myrobotlab.kinematics; - -import java.util.ArrayList; - -import org.myrobotlab.genetic.Chromosome; -import org.myrobotlab.genetic.Genetic; -import org.myrobotlab.genetic.GeneticAlgorithm; -import org.myrobotlab.kinematics.CollisionDectection.CollisionResults; -import org.myrobotlab.logging.LoggerFactory; -import org.myrobotlab.math.MapperLinear; -import org.myrobotlab.math.MathUtils; -import org.myrobotlab.math.interfaces.Mapper; -import org.myrobotlab.service.IntegratedMovement; -import org.myrobotlab.service.IntegratedMovement.ObjectPointLocation; -import org.myrobotlab.service.interfaces.ServoEvent; -import org.slf4j.Logger; - -/** - * @author Christian - * - */ -public class IMEngine extends Thread implements Genetic { - public final static Logger log = LoggerFactory.getLogger(IMEngine.class); - - DHRobotArm arm, computeArm; - public Point target = null; - private double maxDistance = 5.0; - private Matrix inputMatrix = null; - private transient IntegratedMovement service = null; - private boolean noUpdatePosition = false; - private boolean holdTargetEnabled = false; - private int tryCount = 0;; - private Point oldTarget = null; - private double timeToWait; - private long lastTimeUpdate; - private int Ai = IntegratedMovement.Ai.AVOID_COLLISION.value; - - public class MoveInfo { - Point offset = null; - CollisionItem targetItem = null; - ObjectPointLocation objectLocation = null; - DHLink lastLink = null; - public String arm; - public String lastLinkName; - } - - MoveInfo moveInfo = null; - private CalcFitnessType calcFitnessType; - private int cogRetry; - private String lastDHLink; - - private enum CalcFitnessType { - POSITION, COG; - } - - public IMEngine(String name, IntegratedMovement IM) { - super(name); - arm = new DHRobotArm(); - arm.name = name; - service = IM; - } - - public IMEngine(String name, DHRobotArm arm, IntegratedMovement integratedMovement) { - super(name); - this.arm = arm; - service = integratedMovement; - } - - public DHRobotArm getDHRobotArm() { - return arm; - } - - public void setDHRobotArm(DHRobotArm dhArm) { - arm = dhArm; - } - - @Override - public void run() { - while (true) { - Point currentPosition = arm.getPalmPosition(lastDHLink); - if (AiActive(IntegratedMovement.Ai.AVOID_COLLISION)) { - Point avoidPoint = checkCollision(arm, service.collisionItems); - if (avoidPoint != null) { - Point previousTarget = target; - target = avoidPoint; - move(); - cogRetry = 0; - target = previousTarget; - } - } - if (target == null && !isWaitingForServo() && AiActive(IntegratedMovement.Ai.KEEP_BALANCE)) { - target = checkCoG(); - if (target != null) { - // move(); - target = null; - } - } - if (target != null && currentPosition - .distanceTo(target) > maxDistance /** && !isWaitingForServo() **/ - ) { - log.info("distance to target {}", currentPosition.distanceTo(target)); - log.info(currentPosition.toString()); - move(lastDHLink); - cogRetry = 0; - continue; - } - if (target != null && currentPosition.distanceTo(target) < maxDistance && !AiActive(IntegratedMovement.Ai.HOLD_POSITION) && !isWaitingForServo()) { - Point cog = service.cog.computeCoG(null); - if (AiActive(IntegratedMovement.Ai.KEEP_BALANCE) && cog.distanceTo(service.cog.getCoGTarget()) > service.cog.getMaxDistanceToCog()) { - - } else { - target = null; - moveInfo = null; - } - } - } - } - - private Point checkCoG() { - Point cog = service.cog.computeCoG(null); - double deltaDegree = 0.1; - if (cogRetry > 10) { - return null; - } - cog.setZ(0.0); - // cog.setY(0.0); - // if (cog.getZ()==0.0) return null; - Point cogTarget = service.cog.getCoGTarget(); - if (cog.distanceTo(cogTarget) > service.cog.getMaxDistanceToCog()) { - int maxIter = 100; - computeArm = new DHRobotArm(arm); - CollisionDectection cd = new CollisionDectection(service.collisionItems); - for (int i = 0; i < maxIter; i++) { - for (DHLink link : computeArm.getLinks()) { - /* - * if (link.getState() != Servo.SERVO_EVENT_STOPPED) { - * link.addPositionValue(link.getTargetPos()); continue; } - */ - } - for (int j = computeArm.getNumLinks() - 1; j >= 0; j--) { - // for (int j = 0; j < computeArm.getNumLinks(); j++) { - /* - * if (computeArm.getLink(j).getState() != Servo.SERVO_EVENT_STOPPED) - * { continue; } - */ - Point cogIni = service.cog.computeCoG(cd); - cogIni.setZ(0.0); - // cogIni.setY(0.0); - computeArm.getLink(j).incrRotate(MathUtils.degToRad(deltaDegree)); - double[][] jp = computeArm.createJointPositionMap(); - for (int k = computeArm.getNumLinks() - 1; k >= j; k--) { - // for (int k = j; k < computeArm.getNumLinks(); k++) { - CollisionItem ci = new CollisionItem(new Point(jp[k][0], jp[k][1], jp[k][2], 0, 0, 0), new Point(jp[k + 1][0], jp[k + 1][1], jp[k + 1][2], 0, 0, 0), - computeArm.getLink(k).getName()); - if (k != computeArm.getNumLinks() - 1) { - ci.addIgnore(computeArm.getLink(k + 1).getName()); - } - cd.addItem(ci); - } - Point deltaCoG = service.cog.computeCoG(cd); - deltaCoG.setZ(0.0); - // deltaCoG.setY(0.0); - if (cogTarget.distanceTo(cogIni) > cogTarget.distanceTo(deltaCoG)) { - if (cogTarget.distanceTo(deltaCoG) < service.cog.getMaxDistanceToCog() * 0.8) { - publishAngles(); - cogRetry = 0; - return computeArm.getPalmPosition(); - } - continue; - } - computeArm.getLink(j).incrRotate(MathUtils.degToRad(-2 * deltaDegree)); - jp = computeArm.createJointPositionMap(); - for (int k = computeArm.getNumLinks() - 1; k >= j; k--) { - CollisionItem ci = new CollisionItem(new Point(jp[k][0], jp[k][1], jp[k][2], 0, 0, 0), new Point(jp[k + 1][0], jp[k + 1][1], jp[k + 1][2], 0, 0, 0), - computeArm.getLink(k).getName()); - if (k != computeArm.getNumLinks() - 1) { - ci.addIgnore(computeArm.getLink(k + 1).getName()); - } - cd.addItem(ci); - } - deltaCoG = service.cog.computeCoG(cd); - deltaCoG.setZ(0.0); - // deltaCoG.setY(0.0); - if (cogTarget.distanceTo(cogIni) > cogTarget.distanceTo(deltaCoG)) { - if (cogTarget.distanceTo(deltaCoG) < service.cog.getMaxDistanceToCog() * 0.8) { - publishAngles(); - cogRetry = 0; - return computeArm.getPalmPosition(); - } - continue; - } - computeArm.getLink(j).incrRotate(MathUtils.degToRad(deltaDegree)); - } - } - // int geneticPoolSize=100; - // double geneticRecombinationRate = 0.7; - // double geneticMutationRate = 0.01; - // int geneticGeneration = 50; - // calcFitnessType = CalcFitnessType.POSITION; - // GeneticAlgorithm GA = new GeneticAlgorithm(this, geneticPoolSize, - // arm.getNumLinks(), 12, geneticRecombinationRate, - // geneticMutationRate ); - // Chromosome bestFit = GA.doGeneration(geneticGeneration); // this is the - // number of time the chromosome pool will be - // recombined and mutate - // for (int i = 0; i < computeArm.getNumLinks(); i++) { - // if (bestFit.getDecodedGenome().get(i) != null) { - // DHLink link = computeArm.getLink(i); - // double degrees = link.getPositionValueDeg(); - // double deltaDegree = java.lang.Math.abs(degrees - - // (double)bestFit.getDecodedGenome().get(i)); - // if (degrees > ((double)bestFit.getDecodedGenome().get(i))) { - // degrees -= deltaDegree; - // } - // else if (degrees < ((double)bestFit.getDecodedGenome().get(i))) { - // degrees += deltaDegree; - // } - // link.addPositionValue( degrees); - // } - // } - // double[][] jp = computeArm.createJointPositionMap(); - // for (int k = 0; k < computeArm.getNumLinks(); k++) { - // CollisionItem ci = new CollisionItem(new Point(jp[k][0], jp[k][1], - // jp[k][2], 0 , 0, 0), new Point(jp[k+1][0], jp[k+1][1], - // jp[k+1][2], 0, 0, 0), computeArm.getLink(k).getName()); - // if (k != computeArm.getNumLinks()-1) { - // ci.addIgnore(computeArm.getLink(k+1).getName()); - // } - // cd.addItem(ci); - // } - // Point deltaCoG = service.cog.computeCoG(cd); - // deltaCoG.setZ(0.0); - // //deltaCoG.setY(0.0); - // if (deltaCoG.distanceTo(service.cog.getCoGTarget()) < - // service.cog.getMaxDistanceToCog()) { - // Point pos = computeArm.getPalmPosition(); - // log.info("Moving to " + getName() + pos.toString()); - // //publishAngles(); - // return computeArm.getPalmPosition(); - // } - return computeArm.getPalmPosition(); - // cogRetry++; - } - return null; - } - - private boolean isWaitingForServo() { - if (System.currentTimeMillis() > lastTimeUpdate + timeToWait) { - return false; - } - // return true; - return false; - } - - private boolean AiActive(IntegratedMovement.Ai ai) { - if ((Ai & ai.value) > 0) { - return true; - } - return false; - } - - private void move() { - move(null); - } - - private void move(String lastDHLink) { - // noUpdatePosition = true; - if (inputMatrix != null) { - target = rotateAndTranslate(target); - } - if (++tryCount > 500 && oldTarget != null) { - target = oldTarget; - } - boolean success = moveToGoal(target, lastDHLink); - // log.info("Moving to {}", arm.getPalmPosition()); - // target = null; - if (success) { - publishAngles(); - } - // noUpdatePosition = false; - } - - private boolean moveToGoal(Point goal) { - return moveToGoal(goal, null); - } - - private boolean moveToGoal(Point goal, String lastDHLink) { - // we know where we are.. we know where we want to go. - int numSteps = 0; - double iterStep = 0.01; - double errorThreshold = 0.5; - int maxIterations = 10000; - int geneticPoolSize = 100; - double geneticRecombinationRate = 0.7; - double geneticMutationRate = 0.01; - int geneticGeneration = 50; - computeArm = new DHRobotArm(arm); - // what's the current point - while (true) { - // checkCollision(arm,service.collisionItems); - numSteps++; - if (numSteps >= maxIterations) { - // if (numSteps >= maxIterations) return true; - // log.info(computeArm.getPalmPosition().toString() + "genetic"); - calcFitnessType = CalcFitnessType.POSITION; - GeneticAlgorithm GA = new GeneticAlgorithm(this, geneticPoolSize, arm.getNumLinks(), 12, geneticRecombinationRate, geneticMutationRate); - Chromosome bestFit = GA.doGeneration(geneticGeneration); // this is the - // number of - // time the - // chromosome - // pool will be - // recombined - // and mutate - for (int i = 0; i < computeArm.getNumLinks(); i++) { - if (bestFit.getDecodedGenome().get(i) != null) { - DHLink link = computeArm.getLink(i); - double degrees = link.getPositionValueDeg(); - double deltaDegree = java.lang.Math.abs(degrees - (double) bestFit.getDecodedGenome().get(i)); - if (degrees > ((double) bestFit.getDecodedGenome().get(i))) { - degrees -= deltaDegree; - } else if (degrees < ((double) bestFit.getDecodedGenome().get(i))) { - degrees += deltaDegree; - } - link.addPositionValue(degrees); - } - if (computeArm.getLink(i).getName().equals(lastDHLink)) { - break; - } - } - return true; - } - Point currentPos = computeArm.getPalmPosition(lastDHLink); - // log.debug("Current Position " + currentPos); - // vector to destination - Point deltaPoint = goal.subtract(currentPos); - Matrix dP = new Matrix(3, 1); - dP.elements[0][0] = deltaPoint.getX(); - dP.elements[1][0] = deltaPoint.getY(); - dP.elements[2][0] = deltaPoint.getZ(); - // scale a vector towards the goal by the increment step. - dP = dP.multiply(iterStep); - Matrix dTheta = null; - // try { - Matrix jInverse = computeArm.getJInverse(); - dTheta = jInverse.multiply(dP); - // } - // catch (NullPointerException e){ - if (dTheta == null) { - dTheta = new Matrix(computeArm.getNumLinks(), 1); - for (int i = 0; i < computeArm.getNumLinks(); i++) { - dTheta.elements[i][0] = 0.000001; - } - } - - // log.info("delta Theta + " + dTheta); - double maxTimeToMove = 0; - for (int i = 0; i < dTheta.getNumRows(); i++) { - DHLink link = computeArm.getLink(i); - if (link.hasServo) { - // update joint positions! move towards the goal! - double d = dTheta.elements[i][0]; - // incr rotate needs to be min/max aware here! - computeArm.getLink(i).incrRotate(d); - double timeToMove = Math.abs(d / link.getVelocity()); - if (timeToMove > maxTimeToMove) { - maxTimeToMove = timeToMove; - } - } - if (computeArm.getLink(i).getName().equals(lastDHLink)) { - break; - } - } - // delta point represents the direction we need to move in order to - // get there. - // we should figure out how to scale the steps. - - if (deltaPoint.magnitude() < errorThreshold) { - break; - } - } - return true; - - } - - private Point checkCollision(DHRobotArm arm, CollisionDectection cd) { - DHRobotArm checkArm = new DHRobotArm(arm); - double time = 0.0; - double timePerLoop = 0.1; - CollisionResults collisionResult = null; - CollisionDectection ccd = new CollisionDectection(cd); - while (time <= 2.0) { - // rotate the checkArm by timePerLoop - for (DHLink link : checkArm.getLinks()) { - if (link.hasServo) { - double delta = link.getVelocity() * timePerLoop; - double maxDelta = Math.abs(link.getTargetPos() - link.getPositionValueDeg()); - delta = Math.toRadians(Math.min(delta, maxDelta)); - if (link.getTargetPos() < link.getCurrentPos()) { - delta *= -1; - } - link.incrRotate(delta); - } - } - double[][] jp = checkArm.createJointPositionMap(); - // send data to the collision detector class - for (int i = 0; i < checkArm.getNumLinks(); i++) { - CollisionItem ci = new CollisionItem(new Point(jp[i][0], jp[i][1], jp[i][2], 0, 0, 0), new Point(jp[i + 1][0], jp[i + 1][1], jp[i + 1][2], 0, 0, 0), - checkArm.getLink(i).getName()); - if (i != checkArm.getNumLinks() - 1) { - ci.addIgnore(checkArm.getLink(i + 1).getName()); - } - ccd.addItem(ci); - if (time == 0.0) { - cd.addItem(ci); - } - } - collisionResult = ccd.runTest(); - if (collisionResult.haveCollision) { - break; - } - time += timePerLoop; - } - if (collisionResult.haveCollision) { - // log.info("collision detected"); - CollisionItem ci = null; - int itemIndex = 0; - for (DHLink l : checkArm.getLinks()) { - boolean foundIt = false; - for (itemIndex = 0; itemIndex < 2; itemIndex++) { - if (l.getName().equals(collisionResult.collisionItems[itemIndex].getName())) { - ci = collisionResult.collisionItems[itemIndex]; - foundIt = true; - break; - } - } - if (foundIt) - break; // we have the item to watch - } - if (ci == null) { - // log.info("Collision between static item {} and {} detected", - // collisionResult.collisionItems[0].getName(), - // collisionResult.collisionItems[1].getName()); - return null; - } - Point armPos = checkArm.getPalmPosition(); - Point newPos = checkArm.getPalmPosition(); - Point vCollItem = collisionResult.collisionPoints[itemIndex].subtract(collisionResult.collisionPoints[1 - itemIndex]); - // if (vCollItem.magnitude() > 100){ // scale vector so the avoiding point - // is not too far - vCollItem = vCollItem.unitVector(100); - // } - newPos = newPos.add(vCollItem); - Point ori = collisionResult.collisionItems[1 - itemIndex].getOrigin(); - Point end = collisionResult.collisionItems[1 - itemIndex].getEnd(); - Point colPoint = collisionResult.collisionPoints[1 - itemIndex]; - if (collisionResult.collisionLocation[1 - itemIndex] > 0.0 || collisionResult.collisionLocation[1 - itemIndex] < 1.0) { // collision - // on - // the - // side - // of - // item - Point vToEndOfObject; - if (collisionResult.collisionLocation[1 - itemIndex] < 0.5) { // collision - // near - // the - // origin - vToEndOfObject = ori.subtract(colPoint); - // newPos = newPos.add(ori).subtract(colPoint); - } else { // collision near the end - // newPos = newPos.add(end).subtract(colPoint); - vToEndOfObject = end.subtract(colPoint); - } - vToEndOfObject = vToEndOfObject.unitVector(100); - newPos = newPos.add(vToEndOfObject); - } - // move away of the part - // double length = - // collisionResult.collisionItems[1-itemIndex].getLength(); - // double ratio = collisionResult.collisionItems[itemIndex].getRadius() / - // length; - // double[] vector = - // collisionResult.collisionItems[1-itemIndex].getVector(); - // for (int i=0; i<3; i++){ - // vector[i] *= ratio; - // } - // if (collisionResult.collisionLocation[1-itemIndex] < 0.5) { //collision - // near the origin - // newPos.setX(newPos.getX() - vector[0]); - // newPos.setY(newPos.getY() - vector[1]); - // newPos.setZ(newPos.getZ() - vector[2]); - // } - // else { - // newPos.setX(newPos.getX() + vector[0]); - // newPos.setY(newPos.getY() + vector[1]); - // newPos.setZ(newPos.getZ() + vector[2]); - // } - // add a vector end point move toward the collision point - Point vtocollpoint = armPos.subtract(colPoint); - vtocollpoint = vtocollpoint.unitVector(100); - newPos = newPos.add(vtocollpoint); - // double distance = newPos.distanceTo(arm.getPalmPosition()); - // if (distance > 100) { - // Point vtonewPos = arm.getPalmPosition().subtract(newPos); - // vtonewPos = vtonewPos.multiplyXYZ(100/distance); - // newPos = arm.getPalmPosition().add(vtonewPos); - // } - log.info("Avoiding position toward ", newPos.toString()); - return newPos; - } - return null; - } - - private void publishAngles() { - timeToWait = 0; - for (DHLink link : computeArm.getLinks()) { - double timeToMove = 0; - if (link.hasServo && link.getVelocity() > 0) { - timeToMove = Math.abs(link.getCurrentPos() - link.getPositionValueDeg()) / link.getVelocity() * 1000; - } - if (timeToMove > timeToWait) { - timeToWait = timeToMove; - } - service.sendAngles(link.getName(), link.getPositionValueDeg()); - lastTimeUpdate = System.currentTimeMillis(); - // if (link.hasServo) waitForServo ++; - } - } - - public void moveTo(Point point) { - moveTo(point, null); - } - - public void moveTo(Point point, String lastDHLink) { - target = point; - this.lastDHLink = lastDHLink; - oldTarget = arm.getPalmPosition(lastDHLink); - tryCount = 0; - } - - /** - * @return the maxDistance - */ - public double getMaxDistance() { - return maxDistance; - } - - /** - * @param maxDistance - * the maxDistance to set - */ - public void setMaxDistance(double maxDistance) { - this.maxDistance = maxDistance; - } - - public void setInputMatrix(Matrix inputMatrix) { - this.inputMatrix = inputMatrix; - - } - - public Point rotateAndTranslate(Point pIn) { - - Matrix m = new Matrix(4, 1); - m.elements[0][0] = pIn.getX(); - m.elements[1][0] = pIn.getY(); - m.elements[2][0] = pIn.getZ(); - m.elements[3][0] = 1; - Matrix pOM = inputMatrix.multiply(m); - - // TODO: compute the roll pitch yaw - double roll = 0; - double pitch = 0; - double yaw = 0; - - Point pOut = new Point(pOM.elements[0][0], pOM.elements[1][0], pOM.elements[2][0], roll, pitch, yaw); - return pOut; - } - - public void updateLinksPosition(ServoEvent data) { - if (noUpdatePosition) - return; - for (DHLink l : arm.getLinks()) { - if (l.getName().equals(data.name)) { - l.addPositionValue(data.pos); - // l.setState(data.state); - // l.setVelocity(data.speed); - // l.setTargetPos(data.targetPos); - l.setCurrentPos(data.pos); - } - } - - } - - public void holdTarget(boolean holdEnabled) { - this.holdTargetEnabled = holdEnabled; - - } - - @Override - public void calcFitness(ArrayList chromosomes) { - for (Chromosome chromosome : chromosomes) { - DHRobotArm newArm = new DHRobotArm(); - double fitnessMult = 1; - double fitnessTime = 0; - for (int i = 0; i < computeArm.getNumLinks(); i++) { - // copy the value of the currentArm - DHLink newLink = new DHLink(computeArm.getLink(i)); - if (chromosome.getDecodedGenome().get(i) != null) { - newLink.addPositionValue((double) chromosome.getDecodedGenome().get(i)); - double delta = computeArm.getLink(i).getPositionValueDeg() - (double) chromosome.getDecodedGenome().get(i); - double timeOfMove = Math.abs(delta / newLink.getVelocity()); - if (timeOfMove > fitnessTime) { - fitnessTime = timeOfMove; - } - } - newArm.addLink(newLink); - } - Point potLocation = newArm.getPalmPosition(); - if (calcFitnessType == CalcFitnessType.POSITION) { - if (target == null) - return; - Double distance = potLocation.distanceTo(target); - if (fitnessTime < 0.1) { - fitnessTime = 0.1; - } - // fitness is the score showing how close the results is to the target - // position - Double fitness = (fitnessMult / distance * 1000);// + - // (1/fitnessTime*.01); - if (fitness < 0) - fitness *= -1; - chromosome.setFitness(fitness); - } else if (calcFitnessType == CalcFitnessType.COG) { - // compute the COG of this potiental arm - CollisionDectection cd = new CollisionDectection(service.collisionItems); - DHRobotArm checkArm = new DHRobotArm(arm); - for (int i = 0; i < checkArm.getNumLinks(); i++) { - if (chromosome.getDecodedGenome().get(i) != null) { - DHLink link = checkArm.getLink(i); - double degrees = link.getPositionValueDeg(); - double deltaDegree = java.lang.Math.abs(degrees - (double) chromosome.getDecodedGenome().get(i)); - if (degrees > ((double) chromosome.getDecodedGenome().get(i))) { - degrees -= deltaDegree; - } else if (degrees < ((double) chromosome.getDecodedGenome().get(i))) { - degrees += deltaDegree; - } - link.addPositionValue(degrees); - } - } - double[][] jp = checkArm.createJointPositionMap(); - // send data to the collision detector class - for (int i = 0; i < checkArm.getNumLinks(); i++) { - CollisionItem ci = new CollisionItem(new Point(jp[i][0], jp[i][1], jp[i][2], 0, 0, 0), new Point(jp[i + 1][0], jp[i + 1][1], jp[i + 1][2], 0, 0, 0), - checkArm.getLink(i).getName()); - if (i != checkArm.getNumLinks() - 1) { - ci.addIgnore(checkArm.getLink(i + 1).getName()); - } - cd.addItem(ci); - } - Point cog = service.cog.computeCoG(cd); - // project the COG point to the X/Y plane - cog.setZ(0.0); - // cog.setY(0.0); - // find a value that put the COG into the target area while minimizing - // deltaCoG - Double fitness = 0.0; - double deltaCog = cog.distanceTo(service.cog.getCoGTarget()); - if (deltaCog == 0) { - fitness = 999999999.0; - } else { - fitness = 1 / potLocation.distanceTo(cog); - fitness = 1 / deltaCog; - if (deltaCog <= service.cog.getMaxDistanceToCog()) { - fitness *= 100; - } - } - if (arm.getPalmPosition().distanceTo(potLocation) > 10) { - fitness = 0.01; - } - fitness = Math.abs(fitness) * 100; - chromosome.setFitness(fitness); - } - } - return; - } - - @Override - public void decode(ArrayList chromosomes) { - for (Chromosome chromosome : chromosomes) { - int pos = 0; - ArrayList decodedGenome = new ArrayList(); - for (DHLink link : computeArm.getLinks()) { - if (!link.hasServo) { - decodedGenome.add(null); - continue; - } - // if (link.getState() == Servo.SERVO_EVENT_STARTED) { - // decodedGenome.add(link.getTargetPos()); - // continue; - // } - Mapper map = null; - if (link.servoMin == link.servoMax) { - decodedGenome.add(link.servoMin); - continue; - } else { - map = new MapperLinear(0.0, 8191.0, link.servoMin, link.servoMax); - } - Double value = 0.0; - for (int i = pos; i < chromosome.getGenome().length() && i < pos + 13; i++) { - if (chromosome.getGenome().charAt(i) == '1') - value += 1 << i - pos; - } - pos += 13; - value = map.calcOutput(value); - if (value.isNaN()) { - value = link.getPositionValueDeg(); - } - // if (value < MathUtils.radToDeg(link.getMin()-link.getInitialTheta())) - // value = link.getPositionValueDeg(); - // if (value > MathUtils.radToDeg(link.getMax()-link.getInitialTheta())) - // value = link.getPositionValueDeg(); - decodedGenome.add(value); - } - chromosome.setDecodedGenome(decodedGenome); - } - } - - public double[][] createJointPositionMap() { - return arm.createJointPositionMap(); - } - - public void moveTo(CollisionItem item, ObjectPointLocation location, String lastDHLink) { - moveInfo = new MoveInfo(); - moveInfo.targetItem = item; - moveInfo.objectLocation = location; - moveInfo.lastLinkName = lastDHLink; - this.lastDHLink = lastDHLink; - if (moveInfo.targetItem == null) { - log.info("no items named ", item.getName(), "found"); - moveInfo = null; - return; - } - target = moveToObject(); - service.getJmeApp().addPoint(target); - } - - private Point moveToObject() { - double safety = 10.0; - Point[] point = new Point[2]; - if (moveInfo.lastLinkName == null) { - moveInfo.lastLink = arm.getLink(arm.getNumLinks() - 1); - } else { - for (DHLink link : arm.getLinks()) { - if (link.getName() == moveInfo.lastLinkName) { - moveInfo.lastLink = link; - break; - } - } - } - CollisionItem lastLinkItem = service.collisionItems.getItem(moveInfo.lastLink.getName()); - service.collisionItems.addIgnore(moveInfo.targetItem.name, lastLinkItem.name); - Double[] vector = new Double[3]; - boolean addRadius = false; - switch (moveInfo.objectLocation) { - case ORIGIN_CENTER: { - point[0] = moveInfo.targetItem.getOrigin(); - Point v = moveInfo.targetItem.getOrigin().subtract(moveInfo.targetItem.getEnd()); - v = v.unitVector(safety); - point[0] = point[0].add(v); - break; - } - case END_CENTER: { - point[0] = moveInfo.targetItem.getEnd(); - Point v = moveInfo.targetItem.getEnd().subtract(moveInfo.targetItem.getOrigin()); - v = v.unitVector(safety); - point[0] = point[0].add(v); - break; - } - case CLOSEST_POINT: { - point = service.collisionItems.getClosestPoint(moveInfo.targetItem, lastLinkItem, new Double[2], vector); - addRadius = true; - break; - } - case ORIGIN_SIDE: { - point[0] = moveInfo.targetItem.getOrigin(); - Point v = moveInfo.targetItem.getOrigin().subtract(moveInfo.targetItem.getEnd()); - v = v.unitVector(safety); - point[0] = point[0].add(v); - addRadius = true; - break; - } - case END_SIDE: { - point[0] = moveInfo.targetItem.getEnd(); - Point v = moveInfo.targetItem.getEnd().subtract(moveInfo.targetItem.getOrigin()); - v = v.unitVector(safety); - point[0] = point[0].add(v); - addRadius = true; - break; - } - case CENTER_SIDE: - case LEFT_SIDE: - case RIGHT_SIDE: { - point = service.collisionItems.getClosestPoint(moveInfo.targetItem, lastLinkItem, new Double[] { 0.5, 0.5 }, vector); - addRadius = true; - break; - } - case CENTER: { - point = service.collisionItems.getClosestPoint(moveInfo.targetItem, lastLinkItem, new Double[] { 0.5, 0.5 }, vector); - break; - } - } - if (addRadius) { - double[] vectori = moveInfo.targetItem.getVector(); - double[] vectorT = moveInfo.targetItem.getVectorT(); - Point side0 = new Point(point[0].getX() + vectorT[0], point[0].getY() + vectorT[1], point[0].getZ() + vectorT[2], 0, 0, 0); - Point v = new Point(vectorT[0], vectorT[1], vectorT[2], 0, 0, 0); - v = v.unitVector(safety); - side0 = side0.add(v); - Point pointF = side0; - Point curPos = arm.getPalmPosition(moveInfo.lastLinkName); - double d = Math.pow((side0.getX() - curPos.getX()), 2) + Math.pow((side0.getY() - curPos.getY()), 2) + Math.pow((side0.getZ() - curPos.getZ()), 2); - double currentx = side0.getX(); - for (int i = 0; i < 360; i += 10) { - double L = vectori[0] * vectori[0] + vectori[1] * vectori[1] + vectori[2] * vectori[2]; - double x = ((moveInfo.targetItem.getOrigin().getX() * (Math.pow(vectori[1], 2) + Math.pow(vectori[2], 2)) - - vectori[0] * (moveInfo.targetItem.getOrigin().getY() * vectori[1] + moveInfo.targetItem.getOrigin().getZ() * vectori[2] - vectori[0] * side0.getX() - - vectori[1] * side0.getY() - vectori[2] * side0.getZ())) - * (1 - Math.cos(MathUtils.degToRad(i))) + L * side0.getX() * Math.cos(MathUtils.degToRad(i)) - + Math.sqrt(L) * (-moveInfo.targetItem.getOrigin().getZ() * vectori[1] + moveInfo.targetItem.getOrigin().getY() * vectori[2] - vectori[2] * side0.getY() - + vectori[1] * side0.getZ()) * Math.sin(MathUtils.degToRad(i))) - / L; - double y = ((moveInfo.targetItem.getOrigin().getY() * (Math.pow(vectori[0], 2) + Math.pow(vectori[2], 2)) - - vectori[1] * (moveInfo.targetItem.getOrigin().getX() * vectori[0] + moveInfo.targetItem.getOrigin().getZ() * vectori[2] - vectori[0] * side0.getX() - - vectori[1] * side0.getY() - vectori[2] * side0.getZ())) - * (1 - Math.cos(MathUtils.degToRad(i))) + L * side0.getY() * Math.cos(MathUtils.degToRad(i)) - + Math.sqrt(L) * (moveInfo.targetItem.getOrigin().getZ() * vectori[0] - moveInfo.targetItem.getOrigin().getX() * vectori[2] + vectori[2] * side0.getX() - - vectori[0] * side0.getZ()) * Math.sin(MathUtils.degToRad(i))) - / L; - double z = ((moveInfo.targetItem.getOrigin().getZ() * (Math.pow(vectori[0], 2) + Math.pow(vectori[1], 2)) - - vectori[2] * (moveInfo.targetItem.getOrigin().getX() * vectori[0] + moveInfo.targetItem.getOrigin().getY() * vectori[1] - vectori[0] * side0.getX() - - vectori[1] * side0.getY() - vectori[2] * side0.getZ())) - * (1 - Math.cos(MathUtils.degToRad(i))) + L * side0.getZ() * Math.cos(MathUtils.degToRad(i)) - + Math.sqrt(L) * (-moveInfo.targetItem.getOrigin().getY() * vectori[0] + moveInfo.targetItem.getOrigin().getX() * vectori[1] - vectori[1] * side0.getX() - + vectori[0] * side0.getY()) * Math.sin(MathUtils.degToRad(i))) - / L; - Point check = new Point(x, y, z, 0, 0, 0); - double dt = Math.pow((check.getX() - curPos.getX()), 2) + Math.pow((check.getY() - curPos.getY()), 2) + Math.pow((check.getZ() - curPos.getZ()), 2); - if (moveInfo.objectLocation.equals(ObjectPointLocation.RIGHT_SIDE)) { - if (check.getX() < currentx) { - pointF = check; - currentx = check.getX(); - } - } else if (moveInfo.objectLocation.equals(ObjectPointLocation.LEFT_SIDE)) { - if (check.getX() > currentx) { - pointF = check; - currentx = check.getX(); - } - } else if (dt < d) { - pointF = check; - d = dt; - } - } - point[0] = pointF; - } - - Point moveToPoint = point[0]; - log.info("Moving to point ", moveToPoint); - return moveToPoint; - } - - public void setAi(IntegratedMovement.Ai ai) { - this.Ai |= ai.value; - } - - public void removeAi(IntegratedMovement.Ai ai) { - if ((Ai & ai.value) > 0) { - Ai -= ai.value; - } - } - -} diff --git a/src/main/java/org/myrobotlab/kinematics/TestJmeIMModel.java b/src/main/java/org/myrobotlab/kinematics/TestJmeIMModel.java deleted file mode 100644 index b902e49fe4..0000000000 --- a/src/main/java/org/myrobotlab/kinematics/TestJmeIMModel.java +++ /dev/null @@ -1,325 +0,0 @@ -package org.myrobotlab.kinematics; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Queue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; - -import org.myrobotlab.framework.Service; -import org.myrobotlab.jme3.IntegratedMovementInterface; -import org.myrobotlab.service.interfaces.ServoEvent; - -import com.jme3.app.SimpleApplication; -import com.jme3.asset.plugins.FileLocator; -import com.jme3.input.KeyInput; -import com.jme3.input.MouseInput; -import com.jme3.input.controls.ActionListener; -import com.jme3.input.controls.AnalogListener; -import com.jme3.input.controls.KeyTrigger; -import com.jme3.input.controls.MouseAxisTrigger; -import com.jme3.input.controls.MouseButtonTrigger; -import com.jme3.light.DirectionalLight; -import com.jme3.material.Material; -import com.jme3.math.ColorRGBA; -import com.jme3.math.FastMath; -import com.jme3.math.Vector3f; -import com.jme3.scene.Geometry; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; -import com.jme3.scene.shape.Box; -import com.jme3.scene.shape.Cylinder; - -/** - * @author Christian - * - */ -public class TestJmeIMModel extends SimpleApplication implements IntegratedMovementInterface { - private transient HashMap nodes = new HashMap(); - private Queue eventQueue = new ConcurrentLinkedQueue(); - private transient Queue nodeQueue = new ConcurrentLinkedQueue(); - private Queue pointQueue = new ConcurrentLinkedQueue(); - private transient ArrayList collisionItems = new ArrayList(); - private boolean ready = false; - private transient Service service; - private transient Node point; - - public static void main(String[] args) { - TestJmeIMModel app = new TestJmeIMModel(); - app.start(); - } - - @Override - public void simpleInitApp() { - assetManager.registerLocator("inmoov/jm3/assets", FileLocator.class); - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - mat.setColor("Color", ColorRGBA.Green); - viewPort.setBackgroundColor(ColorRGBA.Gray); - inputManager.setCursorVisible(true); - flyCam.setEnabled(false); - Node node = new Node("cam"); - node.setLocalTranslation(0, 300, 0); - rootNode.attachChild(node); - // ChaseCamera chaseCam = new ChaseCamera(cam, node, inputManager); - // chaseCam.setDefaultDistance(900); - // chaseCam.setMaxDistance(2000); - // chaseCam.setDefaultHorizontalRotation((float)Math.toRadians(90)); - // chaseCam.setZoomSensitivity(10); - DirectionalLight sun = new DirectionalLight(); - sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f)); - rootNode.addLight(sun); - cam.setLocation(new Vector3f(0f, 0f, 900f)); - rootNode.scale(.40f); - rootNode.setLocalTranslation(0, -200, 0); - Cylinder c = new Cylinder(8, 50, 5, 10, true, false); - Geometry geom = new Geometry("Cylinder", c); - geom.setMaterial(mat); - point = new Node("point"); - point.attachChild(geom); - rootNode.attachChild(point); - ready = true; - if (service != null) { - synchronized (service) { - service.notifyAll(); - } - } - inputManager.addMapping("MouseClickL", new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); - inputManager.addListener(analogListener, "MouseClickL"); - inputManager.addMapping("MouseClickR", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT)); - inputManager.addListener(analogListener, "MouseClickR"); - inputManager.addMapping("MMouseUp", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false)); - inputManager.addListener(analogListener, "MMouseUp"); - inputManager.addMapping("MMouseDown", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true)); - inputManager.addListener(analogListener, "MMouseDown"); - inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A), new KeyTrigger(KeyInput.KEY_LEFT)); // A - // and - // left - // arrow - inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D), new KeyTrigger(KeyInput.KEY_RIGHT)); // D - // and - // right - // arrow - inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W), new KeyTrigger(KeyInput.KEY_UP)); // A - // and - // left - // arrow - inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S), new KeyTrigger(KeyInput.KEY_DOWN)); // D - // and - // right - // arrow - inputManager.addListener(analogListener, new String[] { "Left", "Right", "Up", "Down" }); - } - - /** - * - * @param name - * : name of the part - * @param modelPath - * : path leading the the 3dmesh (null for no model) - * @param modelScale - * : model will be scale to this parameter - * @param hookTo - * : attach this part to the hook part (null to hook to the root) - * @param relativePosition - * : position relative to the hook part - * @param rotationMask - * : set Vector3f.UNIT_X, Vector3f.UNIT_Y, Vector3f.UNIT_Z) for the - * axe of rotation - * @param initialAngle - * : initial angle of rotation of the part (in radian) - */ - public void addPart(String name, String modelPath, float modelScale, String hookTo, Vector3f relativePosition, Vector3f rotationMask, float initialAngle) { - Node node = new Node(name); - if (hookTo != null) { - // Node hookNode = nodes.get(hookTo); - // hookNode.attachChild(node); - node.setUserData("hookTo", hookTo); - } else { - // rootNode.attachChild(node); - node.setUserData("hookTo", "rootNode"); - } - if (modelPath != null) { - Spatial spatial = assetManager.loadModel(modelPath); - spatial.scale(modelScale); - spatial.setName(name); - node.attachChild(spatial); - } - node.setLocalTranslation(relativePosition); - Vector3f angle = rotationMask.mult(initialAngle); - node.rotate(angle.x, angle.y, angle.z); - node.setUserData("rotationMask_x", rotationMask.x); - node.setUserData("rotationMask_y", rotationMask.y); - node.setUserData("rotationMask_z", rotationMask.z); - float pos = 0.0f; - node.setUserData("currentAngle", pos); - nodes.put(name, node); - nodeQueue.add(node); - } - - @Override - public void updatePosition(ServoEvent event) { - eventQueue.add(event); - } - - @Override - public void simpleUpdate(float tpf) { - if (updateCollisionItem) { - for (Node node : collisionItems) { - if (node.getUserData("collisionItem") != null) { - node.removeFromParent(); - node.updateGeometricState(); - } - } - collisionItems.clear(); - } - while (nodeQueue.size() > 0) { - Node node = nodeQueue.remove(); - Node hookNode = nodes.get(node.getUserData("hookTo")); - if (hookNode == null) { - hookNode = rootNode; - } - Spatial x = hookNode.getChild(node.getName()); - if (x != null) { - rootNode.updateGeometricState(); - } - hookNode.attachChild(node); - if (node.getUserData("collisionItem") != null) { - collisionItems.add(node); - } - } - while (eventQueue.size() > 0) { - ServoEvent event = eventQueue.remove(); - if (nodes.containsKey(event.name)) { - Node node = nodes.get(event.name); - Vector3f rotMask = new Vector3f((float) node.getUserData("rotationMask_x"), (float) node.getUserData("rotationMask_y"), (float) node.getUserData("rotationMask_z")); - float currentAngle = (float) node.getUserData("currentAngle"); - double rotation = (event.pos - currentAngle) * Math.PI / 180; - Vector3f angle = rotMask.mult((float) rotation); - node.rotate(angle.x, angle.y, angle.z); - node.setUserData("currentAngle", event.pos.floatValue()); - nodes.put(event.name, node); - } - - } - while (pointQueue.size() > 0) { - Point p = pointQueue.remove(); - point.setLocalTranslation((float) p.getX(), (float) p.getZ(), (float) p.getY()); - } - } - - public boolean isReady() { - return ready; - } - - @Override - public void setService(Service integratedMovement) { - service = integratedMovement; - - } - - public ActionListener actionListener = new ActionListener() { - @Override - public void onAction(String name, boolean keyPressed, float tpf) { - if (name.equals("MouseClickL")) { - // rotate+= keyPressed; - rootNode.rotate(0, 1, 0); - // Log.info(rotate); - } - // if (name.equals("Rotate")) { - // Vector3f camloc = cam.getLocation(); - // camloc.x += 10; - // cam.setLocation(camloc); - // } - /** TODO: test for mapping names and implement actions */ - } - }; - - private AnalogListener analogListener = new AnalogListener() { - @Override - public void onAnalog(String name, float keyPressed, float tpf) { - if (name.equals("MouseClickL")) { - // rotate+= keyPressed; - rootNode.rotate(0, -keyPressed, 0); - // Log.info(rotate); - } else if (name.equals("MouseClickR")) { - // rotate+= keyPressed; - rootNode.rotate(0, keyPressed, 0); - // Log.info(rotate); - } else if (name.equals("MMouseUp")) { - rootNode.setLocalScale(rootNode.getLocalScale().mult(1.05f)); - } else if (name.equals("MMouseDown")) { - rootNode.setLocalScale(rootNode.getLocalScale().mult(0.95f)); - } else if (name.equals("Up")) { - rootNode.move(0, keyPressed * 100, 0); - } else if (name.equals("Down")) { - rootNode.move(0, -keyPressed * 100, 0); - } else if (name.equals("Left")) { - rootNode.move(-keyPressed * 100, 0, 0); - } else if (name.equals("Right")) { - rootNode.move(keyPressed * 100, 0, 0); - } - } - }; - private HashMap shapes = new HashMap(); - private boolean updateCollisionItem = false; - - @Override - public void addObject(CollisionItem item) { - if (!item.isRender()) { - return; - } - if (item.isFromKinect()) { - Node pivot = new Node(item.getName()); - for (Map3DPoint p : item.cloudMap.values()) { - Box b = new Box(4f, 4f, 4f); - Geometry geo = new Geometry("Box", b); - Vector3f pos = new Vector3f((float) p.point.getX(), (float) p.point.getZ(), (float) p.point.getY()); - geo.setLocalTranslation(pos); - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - mat.setColor("Color", ColorRGBA.Red); - geo.setMaterial(mat); - pivot.attachChild(geo); - } - pivot.setUserData("HookTo", null); - pivot.setUserData("collisionItem", "1"); - nodeQueue.add(pivot); - } else { - Vector3f ori = new Vector3f((float) item.getOrigin().getX(), (float) item.getOrigin().getZ(), (float) item.getOrigin().getY()); - Vector3f end = new Vector3f((float) item.getEnd().getX(), (float) item.getEnd().getZ(), (float) item.getEnd().getY()); - Cylinder c = new Cylinder(8, 50, (float) item.getRadius(), (float) item.getLength(), true, false); - Geometry geom = new Geometry("Cylinder", c); - shapes.put(item.name, geom); - geom.setLocalTranslation(FastMath.interpolateLinear(0.5f, ori, end)); - geom.lookAt(end, Vector3f.UNIT_Y); - // geom.scale(0.5f); - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - if (item.fromKinect) { - mat.setColor("Color", ColorRGBA.Red); - } else { - mat.setColor("Color", ColorRGBA.Blue); - } - geom.setMaterial(mat); - Node pivot = new Node(item.getName()); - pivot.attachChild(geom); - pivot.setUserData("HookTo", null); - pivot.setUserData("collisionItem", "1"); - nodeQueue.add(pivot); - } - } - - @Override - public void addObject(ConcurrentHashMap items) { - updateCollisionItem = true; - for (CollisionItem item : items.values()) { - addObject(item); - } - updateCollisionItem = false; - } - - @Override - public void addPoint(Point point) { - pointQueue.add(point); - - } - -} diff --git a/src/main/java/org/myrobotlab/kinematics/TestJmeIntegratedMovement.java b/src/main/java/org/myrobotlab/kinematics/TestJmeIntegratedMovement.java deleted file mode 100644 index 8efd71af35..0000000000 --- a/src/main/java/org/myrobotlab/kinematics/TestJmeIntegratedMovement.java +++ /dev/null @@ -1,145 +0,0 @@ -/** - * - */ -package org.myrobotlab.kinematics; - -import java.util.HashMap; -import java.util.concurrent.ConcurrentHashMap; - -import com.jme3.app.SimpleApplication; -import com.jme3.material.Material; -import com.jme3.math.ColorRGBA; -import com.jme3.math.FastMath; -import com.jme3.math.Vector3f; -import com.jme3.scene.Geometry; -import com.jme3.scene.Node; -import com.jme3.scene.shape.Box; -import com.jme3.scene.shape.Cylinder; - -/** - * @author Christian - * - */ -public class TestJmeIntegratedMovement extends SimpleApplication { - private ConcurrentHashMap objects; - private Vector3f camLocation; - private int camXDir = 1; - private int camZDir = -1; - private HashMap shapes = new HashMap(); - private boolean updateShape = false; - - @Override - public void simpleInitApp() { - // if (true) { - // Cylinder c= new Cylinder(8,50,50,10,true,false); - // Geometry geom = new Geometry("Cylinder",c); - // //Vector3f ori = new Vector3f((float)ci.getOrigin().getX(), - // (float)ci.getOrigin().getZ(), (float)ci.getOrigin().getY()); - // //Vector3f end = new Vector3f((float)ci.getEnd().getX(), - // (float)ci.getEnd().getZ(), (float)ci.getEnd().getY()); - // //geom.setLocalTranslation(FastMath.interpolateLinear(0.5f, ori, end)); - // //geom.lookAt(end, Vector3f.UNIT_Y); - // Material mat = new - // Material(assetManager,"Common/MatDefs/Misc/Unshaded.j3md"); - // mat.setColor("Color", ColorRGBA.Blue); - // geom.setMaterial(mat); - // Node pivot = new Node("pivot"); - // rootNode.attachChild(pivot); - // pivot.attachChild(geom); - // - // } - for (CollisionItem ci : objects.values()) { - Vector3f ori = new Vector3f((float) ci.getOrigin().getX() / 2, (float) ci.getOrigin().getZ() / 2, (float) ci.getOrigin().getY() / 2); - Vector3f end = new Vector3f((float) ci.getEnd().getX() / 2, (float) ci.getEnd().getZ() / 2, (float) ci.getEnd().getY() / 2); - if (!ci.fromKinect) { - Cylinder c = new Cylinder(8, 50, (float) ci.getRadius() / 2, (float) ci.getLength() / 2, true, false); - Geometry geom = new Geometry("Cylinder", c); - shapes.put(ci.name, geom); - geom.setLocalTranslation(FastMath.interpolateLinear(0.5f, ori, end)); - geom.lookAt(end, Vector3f.UNIT_Y); - // geom.scale(0.5f); - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - if (ci.fromKinect) { - mat.setColor("Color", ColorRGBA.Red); - } else { - mat.setColor("Color", ColorRGBA.Blue); - } - geom.setMaterial(mat); - Node pivot = new Node("pivot"); - rootNode.attachChild(pivot); - pivot.attachChild(geom); - // pivot.rotate(0.4f, 0.4f, 0.0f); - } else { - Node item = new Node("item"); - for (Map3DPoint p : ci.cloudMap.values()) { - Box b = new Box(1.5f, 1.5f, 1.5f); - Geometry geo = new Geometry("Box", b); - Vector3f pos = new Vector3f((float) p.point.getX() / 2, (float) p.point.getZ() / 2, (float) p.point.getY() / 2); - geo.setLocalTranslation(pos); - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - mat.setColor("Color", ColorRGBA.Red); - geo.setMaterial(mat); - item.attachChild(geo); - - } - rootNode.attachChild(item); - } - } - this.cam.setLocation(new Vector3f(0f, 0f, 1000f)); - // this.cam.lookAtDirection(new Vector3f(0,0,0), new Vector3f(0,0,0)); - } - - public static void main(String[] args) { - TestJmeIntegratedMovement app = new TestJmeIntegratedMovement(); - app.start(); - } - - public void setObjects(ConcurrentHashMap collisionObject) { - objects = collisionObject; - - } - - @Override - public void simpleUpdate(float tpf) { - Vector3f camLoc = cam.getLocation(); - if (camLoc.x >= 1000) { - camXDir = -1; - } - if (camLoc.x <= -1000) { - camXDir = 1; - } - if (camLoc.z >= 1000) { - camZDir = -1; - } - if (camLoc.z <= -1000) { - camZDir = 1; - } - // camLoc.add(2*tpf*camXDir, 0, 2*tpf*camZDir); - camLoc.x += 50 * tpf * camXDir; - camLoc.z += 50 * tpf * camZDir; - // this.cam.setLocation(camLoc); - // cam.lookAtDirection(new Vector3f(camLoc.x*-1,0,camLoc.z*-1), - // cam.getUp()); - if (updateShape) { - for (CollisionItem ci : objects.values()) { - if (!ci.isFromKinect()) { - Vector3f ori = new Vector3f((float) ci.getOrigin().getX() / 2, (float) ci.getOrigin().getZ() / 2, (float) ci.getOrigin().getY() / 2); - Vector3f end = new Vector3f((float) ci.getEnd().getX() / 2, (float) ci.getEnd().getZ() / 2, (float) ci.getEnd().getY() / 2); - Geometry geom = shapes.get(ci.name); - geom.setLocalTranslation(FastMath.interpolateLinear(0.5f, ori, end)); - geom.lookAt(end, Vector3f.UNIT_Y); - // geom.scale(0.5f); - shapes.put(ci.name, geom); - } - } - updateShape = false; - } - } - - public void updateObjects(ConcurrentHashMap concurrentHashMap) { - objects = concurrentHashMap; - updateShape = true; - // Log.info("data updated",System.currentTimeMillis()); - } - -} diff --git a/src/main/java/org/myrobotlab/math/MapperSimple.java b/src/main/java/org/myrobotlab/math/MapperSimple.java index 3d5ff80960..a25d983c08 100644 --- a/src/main/java/org/myrobotlab/math/MapperSimple.java +++ b/src/main/java/org/myrobotlab/math/MapperSimple.java @@ -15,7 +15,7 @@ * The original Arduino map() method this was inspired from has no such values. * This was an attempt to "clean up" exposing the map to config. Otherwise, * users setting config would be asking the same questions regarding the useless - * fields Inverted & Clip + * fields Inverted and Clip * * * @author GroG diff --git a/src/main/java/org/myrobotlab/memory/BaseCache.java b/src/main/java/org/myrobotlab/memory/BaseCache.java deleted file mode 100644 index 00d00a6bd6..0000000000 --- a/src/main/java/org/myrobotlab/memory/BaseCache.java +++ /dev/null @@ -1,442 +0,0 @@ -/** - * Base cache that should be used by all Cache implementations. - */ -package org.myrobotlab.memory; - -import org.myrobotlab.reflection.Reflector; - -/** - * Base class that contains the public facing methods. - * - * @author SwedaKonsult - * - */ -public abstract class BaseCache implements ManagedCache { - - private static final boolean DEFAULT_BOOL = false; - private static final byte DEFAULT_BYTE = 0; - private static final double DEFAULT_DOUBLE = 0.0d; - private static final float DEFAULT_FLOAT = 0.0f; - private static final int DEFAULT_INT = 0; - private static final short DEFAULT_SHORT = 0; - - /** - * Internal method for BaseCache to actually add items to the implementing - * cache. - * - * @param name - * - add class name - * @param value - * - value - */ - protected abstract void addToCache(String name, Object value); - - @Override - public void clear() { - clearCache(); - } - - protected abstract void clearCache(); - - /** - * Internal method for BaseCache to actually check if the name exists in the - * implementing cache. - * - * @param name - * @return - */ - protected abstract boolean contains(String name); - - /** - * Expire an item in the cache. - */ - @Override - public void expire(String name) { - expireItem(name); - } - - protected abstract void expireItem(String name); - - /** - * Get a value from the cache. - * - * @param name - * the name of the value to retrieve - * @return null if the name does not exist or if the type could not be cast to - * T - */ - @Override - @SuppressWarnings("unchecked") - public T get(String name, Class cls) { - if (name == null || !contains(name)) { - if (!Reflector.isPrimitive(cls)) { - return null; - } - return (T) Reflector.getPrimitive(cls); - } - Object value = getFromCache(name); - - if (value != null && cls.isInstance(value)) { - return (T) value; - } - if (!Reflector.isPrimitive(cls)) { - return null; - } - // else return whichever primitive they're asking for - if (cls.isAssignableFrom(Integer.class)) { - return (T) new Integer(0); - } - if (cls.isAssignableFrom(Byte.class)) { - return (T) new Byte(Byte.MIN_VALUE); - } - if (cls.isAssignableFrom(Short.class)) { - return (T) new Short(Short.MIN_VALUE); - } - if (cls.isAssignableFrom(Double.class)) { - return (T) new Double(0d); - } - if (cls.isAssignableFrom(Float.class)) { - return (T) new Float(0f); - } - if (cls.isAssignableFrom(Long.class)) { - return (T) new Long(0l); - } - if (cls.isAssignableFrom(Boolean.class)) { - return (T) Boolean.FALSE; - } - return (T) new Character('\u0000'); - } - - /* - * Get a boolean primitive value from the cache. Tests for: Boolean, Integer, - * Byte, Short, String (parseBoolean) - * - * @return false if nothing is found or the cached value is not a boolean - * value - */ - public boolean getBool(String name) { - if (name == null || !contains(name)) { - return DEFAULT_BOOL; - } - Object value = getFromCache(name); - if (value == null) { - return DEFAULT_BOOL; - } - if (value instanceof Boolean) { - boolean b = (Boolean) value; - return b; - } - if (value instanceof Integer) { - int i = (Integer) value; - return i != 0; - } - if (value instanceof Byte) { - byte b = (Byte) value; - return b != 0; - } - if (value instanceof Short) { - short s = (Short) value; - return s != 0; - } - if (!(value instanceof String)) { - return DEFAULT_BOOL; - } - return parseWithDefault((String) value, DEFAULT_BOOL); - } - - /* - * Get an byte primitive value from the cache. Tests for: Byte, Short, String - * (parseByte) - * - * @return 0 if nothing is found or the cached value was not an byte value - */ - public byte getByte(String name) { - if (name == null || !contains(name)) { - return DEFAULT_BYTE; - } - Object value = getFromCache(name); - if (value == null) { - return DEFAULT_BYTE; - } - if (value instanceof Byte) { - byte b = (Byte) value; - return b; - } - if (value instanceof Short) { - short s = (Short) value; - return (byte) s; - } - if (!(value instanceof String)) { - return DEFAULT_BYTE; - } - return parseWithDefault((String) value, DEFAULT_BYTE); - } - - /* - * Get an double primitive value from the cache. Tests for: Double, Float, - * Integer, Byte, Short, String (parseDouble) - * - * @return 0.0d if nothing is found or the cached value was not an double - * value - */ - public double getDouble(String name) { - if (name == null || !contains(name)) { - return DEFAULT_DOUBLE; - } - Object value = getFromCache(name); - if (value == null) { - return DEFAULT_DOUBLE; - } - if (value instanceof Double) { - double d = (Double) value; - return d; - } - if (value instanceof Float) { - float f = (Float) value; - return f; - } - if (value instanceof Integer) { - int f = (Integer) value; - return f; - } - if (value instanceof Byte) { - byte b = (Byte) value; - return b; - } - if (value instanceof Short) { - short s = (Short) value; - return s; - } - if (!(value instanceof String)) { - return DEFAULT_DOUBLE; - } - return parseWithDefault((String) value, DEFAULT_DOUBLE); - } - - /* - * Get an float primitive value from the cache. Tests for: Float, Integer, - * Byte, Short, String (parseDouble) - * - * @return 0.0f if nothing is found or the cached value was not an float value - */ - public float getFloat(String name) { - if (name == null || !contains(name)) { - return DEFAULT_FLOAT; - } - Object value = getFromCache(name); - if (value == null) { - return DEFAULT_FLOAT; - } - if (value instanceof Float) { - float f = (Float) value; - return f; - } - if (value instanceof Byte) { - byte b = (Byte) value; - return b; - } - if (value instanceof Integer) { - int b = (Integer) value; - return b; - } - if (value instanceof Short) { - short s = (Short) value; - return s; - } - if (!(value instanceof String)) { - return DEFAULT_FLOAT; - } - return parseWithDefault((String) value, DEFAULT_FLOAT); - } - - /** - * Internal method for BaseCache to actually retrieve items from the - * implementing cache. - * - * @param name - */ - protected abstract Object getFromCache(String name); - - /* - * Get an int primitive value from the cache. Tests for: Integer, Byte, Short, - * String (parseInt) - * - * @return 0 if nothing is found or the cached value was not an integer value - */ - public int getInt(String name) { - if (name == null || !contains(name)) { - return DEFAULT_INT; - } - Object value = getFromCache(name); - if (value == null) { - return DEFAULT_INT; - } - if (value instanceof Integer) { - int i = (Integer) value; - return i; - } - if (value instanceof Byte) { - byte b = (Byte) value; - return b; - } - if (value instanceof Short) { - short s = (Short) value; - return s; - } - if (!(value instanceof String)) { - return DEFAULT_INT; - } - return parseWithDefault((String) value, DEFAULT_INT); - } - - /* - * Get an short primitive value from the cache. Tests for: Short, Byte, String - * (parseShort) - * - * @return 0 if nothing is found or the cached value was not an integer value - */ - public short getShort(String name) { - if (name == null || !contains(name)) { - return DEFAULT_SHORT; - } - Object value = getFromCache(name); - if (value == null) { - return DEFAULT_SHORT; - } - if (value instanceof Short) { - short s = (Short) value; - return s; - } - if (value instanceof Byte) { - byte b = (Byte) value; - return b; - } - if (!(value instanceof String)) { - return DEFAULT_SHORT; - } - return parseWithDefault((String) value, DEFAULT_SHORT); - } - - /** - * Try to parse a boolean. - * - * @param value - * @param defaultBool - * return value if the string cannot be parsed into a boolean - * @return - */ - private boolean parseWithDefault(String value, boolean defaultBool) { - return Boolean.parseBoolean(value); - } - - /** - * Try to parse a byte. - * - * @param value - * @param defaultByte - * return value if the string cannot be parsed into an byte - * @return - */ - private byte parseWithDefault(String value, byte defaultByte) { - try { - return Byte.parseByte(value); - } catch (NumberFormatException e) { - } - return defaultByte; - } - - /** - * Try to parse a double. - * - * @param value - * @param defaultDouble - * return value if the string cannot be parsed into an double - * @return - */ - private double parseWithDefault(String value, double defaultDouble) { - try { - return Double.parseDouble(value); - } catch (NumberFormatException e) { - } - return defaultDouble; - } - - /** - * Try to parse a float. - * - * @param value - * @param defaultFloat - * return value if the string cannot be parsed into an float - * @return - */ - private float parseWithDefault(String value, float defaultFloat) { - try { - return Float.parseFloat(value); - } catch (NumberFormatException e) { - } - return defaultFloat; - } - - /** - * Try to parse an integer. - * - * @param value - * @param defaultInt - * return value if the string cannot be parsed into an integer - * @return - */ - private int parseWithDefault(String value, int defaultInt) { - try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { - } - return defaultInt; - } - - /** - * Try to parse a short. - * - * @param value - * @param defaultShort - * return value if the string cannot be parsed into an short - * @return - */ - private short parseWithDefault(String value, short defaultShort) { - try { - return Short.parseShort(value); - } catch (NumberFormatException e) { - } - return defaultShort; - } - - /** - * Add a value to the cache. - * - * @param name - * cannot be null or empty - * - */ - @Override - public void put(String name, Object value) { - if (name == null || name.isEmpty()) { - return; - } - addToCache(name, value); - } - - /** - * Internal method for BaseCache to actually remove items from the - * implementing cache. - * - * @param name - * class name - */ - protected abstract void removeFromCache(String name); - - @Override - public void timeout() { - timeoutCache(); - } - - protected abstract void timeoutCache(); -} diff --git a/src/main/java/org/myrobotlab/memory/Cache.java b/src/main/java/org/myrobotlab/memory/Cache.java deleted file mode 100644 index 8c957285fc..0000000000 --- a/src/main/java/org/myrobotlab/memory/Cache.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Cache interface - */ -package org.myrobotlab.memory; - -/** - * Interface for a single cache. Should be retrieved from CacheManager. - * - * @author SwedaKonsult - * - */ -public interface Cache { - - /** - * Expire an item in the cache. - * - * @param name - * the name of the item - */ - void expire(String name); - - /* - * Get a value. - */ - T get(String name, Class c); - - /** - * Cache a value. - * - * @param name - * the name of the item - * @param value - * the value of the item - */ - void put(String name, Object value); -} diff --git a/src/main/java/org/myrobotlab/memory/CacheFactory.java b/src/main/java/org/myrobotlab/memory/CacheFactory.java deleted file mode 100644 index 7df1354479..0000000000 --- a/src/main/java/org/myrobotlab/memory/CacheFactory.java +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Factory for creating caches. - */ -package org.myrobotlab.memory; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import org.myrobotlab.reflection.Reflector; - -/** - * Creates a cache based on configuration for the specified package and class. - * Singleton. - * - * @author SwedaKonsult - * - */ -public class CacheFactory { - /** - * Keep this as a singleton - */ - private final static CacheFactory me = new CacheFactory(); - - /** - * The default initial size of the Cache created. - */ - private final static int DEFAULT_INITIAL_SIZE = 10; - - /** - * Cache the caches. - */ - private final ConcurrentMap caches; - - /* - * Get a handle to this factory. - */ - public static CacheFactory getFactory() { - return me; - } - - /** - * Private constructor. - */ - private CacheFactory() { - // TODO need to load configuration about caches - caches = new ConcurrentHashMap(); - } - - /* - * Create a cache using a specific class. This assumes that the constructor - * does not take any parameters. - * - * @param forClass the cache class that should be used - */ - public Cache createCache(Class forClass) { - if (forClass == null) { - return createDefaultCache(); - } - Cache cache = getExistingCache(forClass); - if (cache != null) { - return cache; - } - cache = Reflector. getNewInstance(forClass, new Object[0]); - caches.put(createKey(forClass), cache); - return cache; - } - - /** - * Create a default cache type. - * - * @return LocalCache - */ - private Cache createDefaultCache() { - return new LocalCache(DEFAULT_INITIAL_SIZE); - } - - /** - * Build up the key used for caching. - * - * @param forClass - * @return - */ - private String createKey(Class forClass) { - return forClass.getCanonicalName(); - } - - /** - * Get a key. - * - * @param forClass - * @return - */ - private Cache getExistingCache(Class forClass) { - String key = createKey(forClass); - if (caches.containsKey(key)) { - return caches.get(key); - } - key = forClass.getPackage().getName(); - if (caches.containsKey(key)) { - return caches.get(key); - } - key = forClass.getName(); - if (caches.containsKey(key)) { - return caches.get(key); - } - return null; - } -} diff --git a/src/main/java/org/myrobotlab/memory/CacheManager.java b/src/main/java/org/myrobotlab/memory/CacheManager.java deleted file mode 100644 index 1d5cd21cdd..0000000000 --- a/src/main/java/org/myrobotlab/memory/CacheManager.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Cache manager - */ -package org.myrobotlab.memory; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -/** - * Manager that should be in charge of caches so that we have a way to clear - * them out and keep them under control. - * - * @author SwedaKonsult - * - */ -public class CacheManager { - /** - * Handle to myself as a singleton. - */ - private final static CacheManager me; - - /** - * All caches that we're currently managing. - */ - private final ConcurrentMap caches; - /** - * Keep track of how often each cache is supposed to time out. - */ - private final ConcurrentMap cacheTimeouts; - - static { - me = new CacheManager(); - } - - /* - * Get a handle to this singleton. - */ - public static CacheManager getInstance() { - return me; - } - - /** - * Singleton constructor. - */ - private CacheManager() { - // start off with 10 - caches = new ConcurrentHashMap(10); - cacheTimeouts = new ConcurrentHashMap(10); - } - - /** - * Add a new cache to the list of caches. - * - * @param name - * the name used to reference the cache - * @param cache - * the cache to add - * @param timeoutInterval - * the interval in ms of how long items in this cache should be kept - * before releasing them - */ - public void addCache(String name, ManagedCache cache, int timeoutInterval) { - caches.put(name, cache); - cacheTimeouts.put(name, timeoutInterval); - } - - /* - * Get a handle to one of the caches. - */ - public Cache getCache(String name) { - if (!caches.containsKey(name)) { - return null; - } - return caches.get(name); - } -} diff --git a/src/main/java/org/myrobotlab/memory/Generalizer.java b/src/main/java/org/myrobotlab/memory/Generalizer.java deleted file mode 100644 index b14fc0ad1c..0000000000 --- a/src/main/java/org/myrobotlab/memory/Generalizer.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.myrobotlab.memory; - -import java.util.HashMap; - -/** - * @author GroG - * - * a class which generalizes amount, colors, or other attributes - * - */ -public class Generalizer { - - // context - general expression formula - // no context - 100 = many - // 3 = a few - // 2 = a couple - // 1 = one - - // large medium small - HashMap context = new HashMap(); - - // a thing - - // colors from rgb values - -} diff --git a/src/main/java/org/myrobotlab/memory/LocalCache.java b/src/main/java/org/myrobotlab/memory/LocalCache.java deleted file mode 100644 index 0ff7c7be42..0000000000 --- a/src/main/java/org/myrobotlab/memory/LocalCache.java +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Cache class that can be used by any code. - */ -package org.myrobotlab.memory; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -/** - * Implementation of the Cache interface that stores information in local - * memory. - * - * @author SwedaKonsult - * - */ -public class LocalCache extends BaseCache { - /** - * Default concurrency level - grabbed from ConcurrentHashMap. - */ - public static final int DEFAULT_CONCURRENCY_LEVEL = 16; - /** - * Default load factor - grabbed from ConcurrentHashMap. - */ - public static final float DEFAULT_LOAD_FACTOR = 0.75f; - /** - * The cache of this instance. - */ - private final ConcurrentMap items; - private final ConcurrentMap itemTimeouts; - private final long timeout; - private long nextTimeout; - private final boolean useTimeout; - - /** - * Constructor. Default load factor (0.75) and concurrencyLevel (16). Default - * timeout: 0 (never). - * - * @param initialCapacity - * the initial capacity. The implementation performs internal sizing - * to accommodate this many elements. - * @throws IllegalArgumentException - * if the initial capacity of elements is negative. - */ - public LocalCache(int initialCapacity) { - this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL, 0); - } - - /** - * Constructor. - * - * @param initialSize - * the initial capacity. The implementation performs internal sizing - * to accommodate this many elements. - * @param loadFactor - * the load factor threshold, used to control resizing. Resizing may - * be performed when the average number of elements per bin exceeds - * this threshold. - * @param concurrencyLevel - * the estimated number of concurrently updating threads. The - * implementation performs internal sizing to try to accommodate this - * many threads. - * @param timeout - * amount of time in ms after which an item should time out. - * @throws IllegalArgumentException - * if the initial capacity is negative or the load factor or - * concurrencyLevel are non-positive. - */ - public LocalCache(int initialSize, float loadFactor, int concurrencyLevel, int timeout) { - items = new ConcurrentHashMap(initialSize, loadFactor, concurrencyLevel); - itemTimeouts = new ConcurrentHashMap(initialSize, loadFactor, concurrencyLevel); - this.timeout = timeout; - nextTimeout = 0l; - useTimeout = this.timeout > 0l; - } - - @Override - protected void addToCache(String name, Object value) { - items.put(name, value); - // only set a timeout for the item if we want to timeout things in this - // cache - if (timeout > 0l) { - itemTimeouts.put(name, System.currentTimeMillis() + timeout); - } - } - - @Override - protected void clearCache() { - items.clear(); - // only need to clear things out if they've been used - if (useTimeout) { - itemTimeouts.clear(); - nextTimeout = 0l; - } - } - - @Override - protected boolean contains(String name) { - return items.containsKey(name); - } - - @Override - protected void expireItem(String name) { - if (name == null || name.isEmpty() || !itemTimeouts.containsKey(name) || !useTimeout) { - return; - } - // time it out - itemTimeouts.put(name, 0l); - // make sure it's clear that something needs to be timed out - nextTimeout = 0l; - } - - @Override - protected Object getFromCache(String name) { - if (!items.containsKey(name)) { - return null; - } - return items.get(name); - } - - @Override - protected void removeFromCache(String name) { - if (!items.containsKey(name)) { - return; - } - // TODO is this needed in order to make sure that the handle is removed? - items.put(name, null); - items.remove(name); - // only remove it if we're using timeouts - if (useTimeout) { - itemTimeouts.remove(name); - } - } - - @Override - protected void timeoutCache() { - if (!useTimeout || (nextTimeout > 0l && nextTimeout > System.currentTimeMillis())) { - // nothing to time out right now - return; - } - // TODO loop through itemTimeouts in order to see if one of them needs - // to be timed out - } -} diff --git a/src/main/java/org/myrobotlab/memory/ManagedCache.java b/src/main/java/org/myrobotlab/memory/ManagedCache.java deleted file mode 100644 index 8567434641..0000000000 --- a/src/main/java/org/myrobotlab/memory/ManagedCache.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Internal Cache that allows for managing. - */ -package org.myrobotlab.memory; - -/** - * Interface for use by the CacheManager. Contains additional methods that are - * required for cache management. - * - * @author SwedaKonsult - * - */ -public interface ManagedCache extends Cache { - /** - * Clear all values from the cache. - */ - void clear(); - - /** - * Update the timeout for name so that it is cleaned up the next time - * timeout() is called. - * - * @param name - * the name - */ - @Override - void expire(String name); - - /** - * Clear out any values that should be timed out. - */ - void timeout(); -} diff --git a/src/main/java/org/myrobotlab/memory/Memory.java b/src/main/java/org/myrobotlab/memory/Memory.java deleted file mode 100644 index fd55b0b457..0000000000 --- a/src/main/java/org/myrobotlab/memory/Memory.java +++ /dev/null @@ -1,148 +0,0 @@ -package org.myrobotlab.memory; - -import java.util.HashMap; -import java.util.Map; - -import org.myrobotlab.codec.CodecUtils; -import org.myrobotlab.logging.LoggerFactory; -import org.myrobotlab.logging.Logging; -import org.slf4j.Logger; - -public class Memory { - - public final static Logger log = LoggerFactory.getLogger(Memory.class.getCanonicalName()); - - Node root = new Node("/"); // test for new Node("") & new Node(".") - - boolean autoBuildPaths = true; - - // private ArrayList listeners = new - // ArrayList(); - MemoryChangeListener listener = null; - - public void addMemoryChangeListener(MemoryChangeListener listener) { - if (this.listener == null) { - this.listener = listener; - } else { - log.error("too many listeners"); - // bad form - shouldn't follow rxtx ;p - } - } - - // TODO - move these into Memory ??? - public void crawlAndPublish() { - crawlAndPublish("", root); - } - - // TODO - move these into Memory ??? - public void crawlAndPublish(String parentPath, Node currentNode) { - log.info("{}.{}", parentPath, currentNode.getName()); - - // publish recursively - HashMap objects = currentNode.getNodes(); - for (Map.Entry o : objects.entrySet()) { - Object value = o.getValue(); - if (value.getClass() == Node.class) { - Node node = (Node) value; - - String newPath = String.format("%s/%s", parentPath, node.getName()); - if (listener != null) { // FIXME - don't publish root - design - // problem ! - listener.publish(parentPath, node); - } - - crawlAndPublish(newPath, node); - } - } - } - - public Object get(String path) { - if (path == null || path == "") // TODO - test for .getNode(null) !!! - { - return root; - } else { - // return root.getNode(path); - return root.get(path); - } - } - - public Node getNode(String path) { - return (Node) get(path); - } - - public Node getRoot() { - return root; - } - - // TODO - optimization put reference in of parents ??? - public Object put(String parentPath, Node node) { - // FIXME - A CLUE the root node name is null ! - - Object object = root.get(parentPath); - - /* - * AUTOBUILD - WOULD BE NICE if (object == null && autoBuildPaths) { int - * pos0 = 0; int pos1 = parentPath.indexOf("/"); while (pos1 != -1) { String - * parentKey = parentPath.substring(pos0, pos1); pos0 = pos1; pos1 = - * parentPath.indexOf("/", pos0 + 1); String childKey = - * parentPath.substring(pos0 + 1, pos1); log.info("adding {} -> new node {}" - * , parentKey, childKey); put(parentKey, new Node(childKey)); if (listener - * != null) { listener.onPut(null, node); // <-- ? needs full path? } - * - * } - * - * } - */ - - if (object == null) { - log.error("could not add node {} to path {}", node.getName(), parentPath); - return null; - } - - Class c = object.getClass(); - Object ret = null; - if (c == Node.class) { - Node parent = (Node) object; - ret = parent.getNodes().put(node.getName(), node); - } else if (c == HashMap.class) { - // it must be data right ? - HashMap data = (HashMap) object; - ret = data.put(node.getName(), node); - } else { - log.error("wtf ??? - something besides node or hashmap !!!"); - } - if (listener != null) { - listener.onPut(parentPath, node); - } - return ret; - } - - public Object putNode(String path, String nodeName) { - Object o = root.get(path); - Class c = o.getClass(); - if (c == Node.class) { - Node newNode = new Node(nodeName); - Node node = (Node) o; - return node.put(newNode); - } else { - log.error("path {} is not to a Node", path); - return null; - } - - } - - public String toJSON() { - return CodecUtils.toJson(this); - } - - public void toJSONFile(String string) { - try { - - CodecUtils.toJsonFile(this, string); - - } catch (Exception e) { - Logging.logError(e); - } - } - -} diff --git a/src/main/java/org/myrobotlab/memory/MemoryChangeListener.java b/src/main/java/org/myrobotlab/memory/MemoryChangeListener.java deleted file mode 100644 index 69fd1d2420..0000000000 --- a/src/main/java/org/myrobotlab/memory/MemoryChangeListener.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.myrobotlab.memory; - -public interface MemoryChangeListener { - - // public void onAdd(String parentPath, Node node); - public void onPut(String parentPath, Node node); - - public void publish(String path, Node node); - -} diff --git a/src/main/java/org/myrobotlab/memory/Node.java b/src/main/java/org/myrobotlab/memory/Node.java deleted file mode 100644 index 2dcfddb9b8..0000000000 --- a/src/main/java/org/myrobotlab/memory/Node.java +++ /dev/null @@ -1,164 +0,0 @@ -package org.myrobotlab.memory; - -import java.io.Serializable; -import java.util.HashMap; - -import org.myrobotlab.codec.CodecUtils; -import org.myrobotlab.logging.Level; -import org.myrobotlab.logging.LoggerFactory; -import org.myrobotlab.logging.Logging; -import org.myrobotlab.logging.LoggingFactory; -import org.slf4j.Logger; - -/** - * @author GroG a "very" generalized memory node - potentially used to grow - * associations with other nodes uses the concept of attributes and - * free-form associations - * - */ -public class Node implements Serializable { - - /** - * NodeContext is used with messaging to publish updates of nodes. A location - * parentPath is needed since the node is not aware of its location. - */ - public static class NodeContext { - public String parentPath; - public Node node; - - public NodeContext(String parentPath, Node node) { - this.parentPath = parentPath; - this.node = node; - } - } - - private static final long serialVersionUID = 1L; - - public final static Logger log = LoggerFactory.getLogger(Node.class.getCanonicalName()); - - private String name; - - public double feelingIndex = 0; - - public double timestamp = System.currentTimeMillis(); - - private HashMap data = new HashMap(); - - public static void main(String[] args) { - try { - LoggingFactory.init(Level.INFO); - - Node root = new Node("root"); - root.put("key1", "value1"); - root.put("key2", "value2"); - Node node2 = new Node("node2"); - root.put("node2", node2); - - node2.put("subkey1", "subValue"); - node2.put("subIntKey", 5); - Node node3 = new Node("node3"); - node3.put("subkey1", "value3"); - node2.put("node3", node3); - Node node4 = root.getNode("root/node2/node3"); - log.info("{}", node4.get("subkey1")); - - // Node node5 = root.getNode("root/node2/node5"); - - // SerializableImage img = new SerializableImage(ImageIO.read(new - // File("opencv.4084.jpg")), "myImage"); - // node2.put("img", img); - - CodecUtils.toJsonFile(root, "node.json"); - log.info("here"); - } catch (Exception e) { - Logging.logError(e); - } - } - - public Node(String name) { - this.name = name; - } - - /* - * the most important method get is the effective "search" method of memory. - * It has an XPath like syntax the "/" means "data of" a node, so when the - * path is /k1/k2/ - would mean get the hashmap of k2 /k1/k2 - means get the - * key value of k2 in the hashmap of k1 - * - * other examples /background /background/position/x /background/position/y - * /foreground /known/ball/red /known/ball/yellow /known/cup /unknown/object1 - * /positions/x/ <map> /positions/y/ <map> /positions/time/ - * <map> /tracking - * - * @return - an object found at the given path - */ - public Object get(String path) { - if (path == "") { - return this; - } - if (path == "/") { - return this.data; - } - - int pos0 = path.indexOf('/'); - if (pos0 != -1) { - int pos1 = path.indexOf('/', pos0 + 1); - String subpath; - String remaining; - if (pos1 != -1) { - subpath = path.substring(pos0 + 1, pos1); - remaining = path.substring(pos0 + 1); - } else { - subpath = path.substring(pos0 + 1); - remaining = subpath; - } - - if (data.containsKey(subpath)) { - Object o = data.get(subpath); - Class c = o.getClass(); - if (c == Node.class) { - return ((Node) o).get(remaining); - } else { - return o; - } - } - } else if (data.containsKey(path)) { - return data.get(path); - } - - if (path.equals(name)) { - return this; - } - - return null; - - } - - public String getName() { - return name; - } - - /* - * a convienent cast method to get a node - */ - public Node getNode(String path) { - return (Node) get(path); - } - - public HashMap getNodes() { - return data; - } - - public Node put(Node node) { - return (Node) data.put(node.getName(), node); - } - - public Object put(String key, Object value) { - return data.put(key, value); - } - - public int size() { - return data.size(); - } - -} diff --git a/src/main/java/org/myrobotlab/memory/NodeDeprecate.java b/src/main/java/org/myrobotlab/memory/NodeDeprecate.java deleted file mode 100644 index 024fa3e322..0000000000 --- a/src/main/java/org/myrobotlab/memory/NodeDeprecate.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.myrobotlab.memory; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Date; - -import org.myrobotlab.image.KinectImageNode; - -public class NodeDeprecate implements Serializable { - - private static final long serialVersionUID = 1L; - public int ID = 0; - public Date timestamp = null; - public String word = null; - public ArrayList imageData = null; - - public NodeDeprecate() { - } -} diff --git a/src/main/java/org/myrobotlab/net/Mail.java b/src/main/java/org/myrobotlab/net/Mail.java index 74be1a3955..fa1384fe2a 100644 --- a/src/main/java/org/myrobotlab/net/Mail.java +++ b/src/main/java/org/myrobotlab/net/Mail.java @@ -164,7 +164,7 @@ public void setEmailServer(String host, Integer port) { * This will work with gmail but an "app password" will need to be set up on * the sending account. * - * "Create & use App Passwords" + * "Create and use App Passwords" * https://support.google.com/mail/answer/185833?hl=en * * @param host diff --git a/src/main/java/org/myrobotlab/net/SslUtil.java b/src/main/java/org/myrobotlab/net/SslUtil.java index d0c3cf6bd2..368fe77c49 100644 --- a/src/main/java/org/myrobotlab/net/SslUtil.java +++ b/src/main/java/org/myrobotlab/net/SslUtil.java @@ -19,18 +19,14 @@ public class SslUtil { * Returns a SSLSocketFactory if provided with a x509 cert, and key file. The * caCrtFile is the certificate authority root and inpassword is the protected * password of the cert "if suppied". If unprotected leave null or empty "". + * @param sslCaFilePath - ca root + * @param sslCertFilePath - certificate file (pem) + * @param sslKeyFilePath - private key + * @param sslPassword - password if key/cert are protected by a password * - * @param caCrtFile - * - ca root - * @param crtFile - * - certificate file (pem) - * @param keyFile - * - private key - * @param inpassword - * - password if key/cert are protected by a password * @return the ssl socket factory * @throws Exception - * boom + * */ public static SSLSocketFactory getSocketFactory(String sslCaFilePath, String sslCertFilePath, String sslKeyFilePath, String sslPassword) throws Exception { diff --git a/src/main/java/org/myrobotlab/programab/BotInfo.java b/src/main/java/org/myrobotlab/programab/BotInfo.java index 7e8e416cbf..852e77cbf0 100644 --- a/src/main/java/org/myrobotlab/programab/BotInfo.java +++ b/src/main/java/org/myrobotlab/programab/BotInfo.java @@ -12,6 +12,7 @@ import org.alicebot.ab.Bot; import org.myrobotlab.io.FileIO; import org.myrobotlab.logging.LoggerFactory; +import org.myrobotlab.programab.handlers.sraix.MrlSraixHandler; import org.myrobotlab.service.ProgramAB; import org.slf4j.Logger; @@ -23,7 +24,7 @@ public class BotInfo { transient public final static Logger log = LoggerFactory.getLogger(BotInfo.class); - public String name; + public String botType; public File path; public Properties properties = new Properties(); private transient Bot bot; @@ -36,17 +37,17 @@ public class BotInfo { public String img; public BotInfo(ProgramAB programab, File path) { - this.name = path.getName(); + this.botType = path.getName(); this.path = path; this.programab = programab; - programab.info("found bot %s", name); + programab.info("found bot %s", botType); try { FileInputStream fis = new FileInputStream(FileIO.gluePaths(path.getAbsolutePath(), "manifest.txt")); properties.load(new InputStreamReader(fis, Charset.forName("UTF-8"))); fis.close(); log.info("loaded properties"); } catch (FileNotFoundException e) { - programab.warn("bot %s does not have a manifest.txt", name); + programab.warn("bot %s does not have a manifest.txt", botType); } catch (Exception e) { log.error("BotInfo threw", e); } @@ -62,13 +63,13 @@ public synchronized Bot getBot() { if (bot == null) { // lazy loading of bot - created on the first use if (properties.containsKey("locale")) { - bot = new Bot(name, path.getAbsolutePath(), java.util.Locale.forLanguageTag((String) properties.get("locale"))); + bot = new Bot(botType, path.getAbsolutePath(), java.util.Locale.forLanguageTag((String) properties.get("locale"))); bot.listener = programab; } else { if (programab.getLocaleTag() == null) { - bot = new Bot(name, path.getAbsolutePath()); + bot = new Bot(botType, path.getAbsolutePath()); } else { - bot = new Bot(name, path.getAbsolutePath(), java.util.Locale.forLanguageTag(programab.getLocaleTag())); + bot = new Bot(botType, path.getAbsolutePath(), java.util.Locale.forLanguageTag(programab.getLocaleTag())); } bot.listener = programab; } @@ -131,7 +132,7 @@ public void removeProperty(String name2) { @Override public String toString() { - return String.format("%s - %s", name, path); + return String.format("%s - %s", botType, path); } } diff --git a/src/main/java/org/myrobotlab/programab/MrlSraixHandler.java b/src/main/java/org/myrobotlab/programab/MrlSraixHandler.java deleted file mode 100755 index d1b2808b2e..0000000000 --- a/src/main/java/org/myrobotlab/programab/MrlSraixHandler.java +++ /dev/null @@ -1,211 +0,0 @@ -package org.myrobotlab.programab; - -import java.util.ArrayList; -import java.util.Locale; -import java.util.regex.Matcher; - -import org.alicebot.ab.Chat; -import org.alicebot.ab.Sraix; -import org.alicebot.ab.SraixHandler; -import org.myrobotlab.codec.CodecUtils; -import org.myrobotlab.framework.Message; -import org.myrobotlab.framework.interfaces.ServiceInterface; -import org.myrobotlab.logging.LoggerFactory; -import org.myrobotlab.service.ProgramAB; -import org.myrobotlab.service.Runtime; -import org.myrobotlab.service.data.SearchResults; -import org.myrobotlab.service.interfaces.SearchPublisher; -import org.myrobotlab.string.StringUtil; -// import org.nd4j.shade.jackson.dataformat.xml.XmlMapper; -import org.slf4j.Logger; - -import com.fasterxml.jackson.dataformat.xml.XmlMapper; - - -public class MrlSraixHandler implements SraixHandler { - transient public final static Logger log = LoggerFactory.getLogger(MrlSraixHandler.class); - - private ProgramAB programab = null; - - public MrlSraixHandler() { - - } - - public MrlSraixHandler(ProgramAB programab) { - this.programab = programab; - } - - @Override - public String sraix(Chat chatSession, String input, String defaultResponse, String hint, String host, String botid, String apiKey, String limit, Locale locale) { - log.debug("MRL Sraix handler! Input {}", input); - - // FIXME - "list of AIs in priority order to attempt to handle request - // best synopsis of sraix I've found - https://gist.github.com/onlurking/f6431e672cfa202c09a7c7cf92ac8a8b - try { - XmlMapper xmlMapper = new XmlMapper(); - Oob oob = xmlMapper.readValue(input, Oob.class); - StringBuilder responseText = new StringBuilder(); - if (oob.mrljson != null) { - Message[] msgs = CodecUtils.fromJson(oob.mrljson, Message[].class); - for (Message msg: msgs) { - msg.sender = programab.getName(); - msg.sendingMethod = "sraix"; - // buffered asynchronous - use invoke synchronous - // programab.in(msg); - // invoking to keep it synchronous - ServiceInterface si = Runtime.getService(msg.getName()); - Object ret = si.invoke(msg.method, msg.data); - if (ret != null) { - responseText.append(ret.toString()); - } - } - return responseText.toString(); - } - log.info("found oob {}", oob); - } catch (Exception e) { - // programab.error("threw on input %s", input); - } - - // the INPUT has the string we care about. if this is an OOB tag, let's - // evaluate it and return the result. - if (containsOOB(input)) { - String response = processInlineOOB(input); - return response; - } else if (programab != null && programab.getPeer("search") != null) { - try { - SearchPublisher search = (SearchPublisher) programab.getPeer("search"); - if (search != null) { - SearchResults results = search.search(input); - String searchResponse = results.getTextAndImages(); - - if (searchResponse == null || searchResponse.length() == 0) { - Session session = programab.getSession(); - // TODO - perhaps more rich codes for details of failure - // Response r = session.getResponse("SRAIXFAILED_WIKIPEDIA " + input); - Response r = session.getResponse("SRAIXFAILED " + input); - return r.msg; - } - return searchResponse; - } else { - // TODO - perhaps more rich codes for details of failure - // Response r = programab.getResponse("SRAIXFAILED_WIKIPEDIA_NOT_AVAILABLE"); - Session session = programab.getSession(); - Response r = session.getResponse("SRAIXFAILED " + input); - return r.msg; - } - - } catch (Exception e) { - return "sorry, I cannot search now " + e.getMessage(); - } - } else { - // fall back to default behavior of pannous / pandorabots? - // TODO: expose pandora bots here if botid is set? - // TODO: enable call out to an official MRL hosted NLU service/ knowedge - // service. - - String response = Sraix.sraixPannous(input, hint, chatSession, locale); - if (StringUtil.isEmpty(response)) { - return defaultResponse; - } else { - // clean up the response a bit. - response = cleanPannousResponse(response); - return response; - } - } - } - - private String cleanPannousResponse(String response) { - String clean = response.replaceAll("\\(Answers.com\\)", "").trim(); - return clean; - } - - private boolean containsOOB(String text) { - Matcher oobMatcher = OOBPayload.oobPattern.matcher(text); - return oobMatcher.matches(); - } - - // TODO override it inside programAB to share methods and publish OOB - private String processInlineOOB(String text) { - // Find any oob tags - StringBuilder responseBuilder = new StringBuilder(); - ArrayList payloads = new ArrayList(); - Matcher oobMatcher = OOBPayload.oobPattern.matcher(text); - int start = 0; - while (oobMatcher.find()) { - // We found some OOB text. - // assume only one OOB in the text? - // everything from the start to the end of this - responseBuilder.append(text.substring(start, oobMatcher.start())); - // update the end to be - // next segment is from the end of this one to the start of the next one. - start = oobMatcher.end(); - String oobPayload = oobMatcher.group(0); - Matcher mrlMatcher = OOBPayload.mrlPattern.matcher(oobPayload); - while (mrlMatcher.find()) { - String mrlPayload = mrlMatcher.group(0); - OOBPayload payload = parseOOB(mrlPayload); - Object result = invokeOOBPayloads(payloads, mrlPayload, payload); - if (result != null && result.getClass().isArray()) { - Object[] objects = (Object[]) result; - for (Object o : objects) { - responseBuilder.append(o.toString() + " "); - } - } else { - - if (result != null) { - responseBuilder.append(result); - } - } - log.info("OOB PROCESSING RESULT: {}", result); - } - } - // append the last part. (assume the start is set to the end of the last - // match.. - // or zero if no matches found. - responseBuilder.append(text.substring(start)); - return responseBuilder.toString(); - } - - private Object invokeOOBPayloads(ArrayList payloads, String mrlPayload, OOBPayload payload) { - payloads.add(payload); - // grab service and invoke method. - ServiceInterface s = Runtime.getService(payload.getServiceName()); - if (s == null) { - log.warn("Service name in OOB/MRL tag unknown. {}", mrlPayload); - return null; - } - Object result = null; - if (payload.getParams() != null) { - result = s.invoke(payload.getMethodName(), payload.getParams().toArray()); - } else { - result = s.invoke(payload.getMethodName()); - } - return result; - } - - private OOBPayload parseOOB(String oobPayload) { - - // TODO: fix the damn double encoding issue. - // we have user entered text in the service/method and params values. - // grab the service - Matcher serviceMatcher = OOBPayload.servicePattern.matcher(oobPayload); - serviceMatcher.find(); - String serviceName = serviceMatcher.group(1); - - Matcher methodMatcher = OOBPayload.methodPattern.matcher(oobPayload); - methodMatcher.find(); - String methodName = methodMatcher.group(1); - - Matcher paramMatcher = OOBPayload.paramPattern.matcher(oobPayload); - ArrayList params = new ArrayList(); - while (paramMatcher.find()) { - // We found some OOB text. - // assume only one OOB in the text? - String param = paramMatcher.group(1); - params.add(param); - } - OOBPayload payload = new OOBPayload(serviceName, methodName, params); - return payload; - - } -} diff --git a/src/main/java/org/myrobotlab/programab/OOBPayload.java b/src/main/java/org/myrobotlab/programab/OOBPayload.java deleted file mode 100644 index c9984a6929..0000000000 --- a/src/main/java/org/myrobotlab/programab/OOBPayload.java +++ /dev/null @@ -1,176 +0,0 @@ -package org.myrobotlab.programab; - -import java.util.ArrayList; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.commons.lang3.StringUtils; -import org.myrobotlab.framework.Message; -import org.myrobotlab.framework.interfaces.ServiceInterface; -import org.myrobotlab.logging.LoggerFactory; -import org.myrobotlab.service.ProgramAB; -import org.myrobotlab.service.Runtime; -import org.slf4j.Logger; - -public class OOBPayload { - - transient public final static Logger log = LoggerFactory.getLogger(OOBPayload.class); - // TODO: something better than regex to parse the xml. (Problem is that the - // service/method/param values - // could end up double encoded ... So we had to switch to hamd crafting the - // aiml for the oob/mrl tag. - public transient static final Pattern oobPattern = Pattern.compile(".*?", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE); - public transient static final Pattern mrlPattern = Pattern.compile(".*?", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE); - public transient static final Pattern servicePattern = Pattern.compile("(.*?)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE); - public transient static final Pattern methodPattern = Pattern.compile("(.*?)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE); - public transient static final Pattern paramPattern = Pattern.compile("(.*?)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE); - - private String serviceName; - private String methodName; - private ArrayList params; - - public OOBPayload() { - // TODO: remove the default constructor - }; - - public OOBPayload(String serviceName, String methodName, ArrayList params) { - this.serviceName = serviceName; - this.methodName = methodName; - this.params = params; - } - - public String getMethodName() { - return methodName; - } - - public ArrayList getParams() { - return params; - } - - public String getServiceName() { - return serviceName; - } - - public void setMethodName(String methodName) { - this.methodName = methodName; - } - - public void setParams(ArrayList params) { - this.params = params; - } - - public void setServiceName(String serviceName) { - this.serviceName = serviceName; - } - - public static String asOOBTag(OOBPayload payload) { - // TODO: this isn't really safe as XML/AIML.. but we don't want to end up - // double encoding things like - // the important tags... So, for now, it's just wrapped in the tags. - StringBuilder oobBuilder = new StringBuilder(); - oobBuilder.append(""); - oobBuilder.append(""); - oobBuilder.append(""); - oobBuilder.append(payload.getServiceName()); - oobBuilder.append(""); - oobBuilder.append(""); - oobBuilder.append(payload.getMethodName()); - oobBuilder.append(""); - for (String param : payload.params) { - oobBuilder.append(""); - // TODO: this could be problematic if the param contains XML chars that - // are not AIML ... - oobBuilder.append(param); - oobBuilder.append(""); - } - oobBuilder.append(""); - oobBuilder.append(""); - return oobBuilder.toString(); - } - - public static String asBlockingOOBTag(OOBPayload oobTag) { - return "" + OOBPayload.asOOBTag(oobTag) + ""; - } - - public static OOBPayload fromString(String oobPayload) { - - // TODO: fix the damn double encoding issue. - // we have user entered text in the service/method - // and params values. - // grab the service - - Matcher serviceMatcher = servicePattern.matcher(oobPayload); - serviceMatcher.find(); - String serviceName = serviceMatcher.group(1); - - Matcher methodMatcher = methodPattern.matcher(oobPayload); - methodMatcher.find(); - String methodName = methodMatcher.group(1); - - Matcher paramMatcher = paramPattern.matcher(oobPayload); - ArrayList params = new ArrayList(); - while (paramMatcher.find()) { - // We found some OOB text. - // assume only one OOB in the text? - String param = paramMatcher.group(1); - params.add(param); - } - OOBPayload payload = new OOBPayload(serviceName, methodName, params); - // log.info(payload.toString()); - return payload; - } - - public static boolean invokeOOBPayload(OOBPayload payload, String sender, boolean blocking) { - ServiceInterface s = Runtime.getService(payload.getServiceName()); - // the service must exist and the method name must be set. - if (s == null || StringUtils.isEmpty(payload.getMethodName())) { - return false; - } - - if (!blocking) { - s.in(Message.createMessage(sender, payload.getServiceName(), payload.getMethodName(), payload.getParams().toArray())); - // non-blocking.. fire and forget! - return true; - } - - // TODO: should you be able to be synchronous for this - // execution? - Object result = null; - if (payload.getParams() != null) { - result = s.invoke(payload.getMethodName(), payload.getParams().toArray()); - } else { - result = s.invoke(payload.getMethodName()); - } - log.info("OOB PROCESSING RESULT: {}", result); - return true; - } - - public static ArrayList extractOOBPayloads(String text, ProgramAB programAB) { - ArrayList payloads = new ArrayList(); - Matcher oobMatcher = OOBPayload.oobPattern.matcher(text); - while (oobMatcher.find()) { - // We found some OOB text. - // assume only one OOB in the text? - String oobPayload = oobMatcher.group(0); - Matcher mrlMatcher = OOBPayload.mrlPattern.matcher(oobPayload); - while (mrlMatcher.find()) { - String mrlPayload = mrlMatcher.group(0); - OOBPayload payload = OOBPayload.fromString(mrlPayload); - payloads.add(payload); - // TODO: maybe we dont' want this? - // Notifiy endpoints - programAB.invoke("publishOOBText", mrlPayload); - // grab service and invoke method. - - } - } - return payloads; - } - - public static String removeOOBFromString(String res) { - Matcher matcher = OOBPayload.oobPattern.matcher(res); - res = matcher.replaceAll(""); - return res; - } - -} diff --git a/src/main/java/org/myrobotlab/programab/Oob.java b/src/main/java/org/myrobotlab/programab/Oob.java deleted file mode 100644 index d6c8d2bf81..0000000000 --- a/src/main/java/org/myrobotlab/programab/Oob.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.myrobotlab.programab; - -public class Oob { - - public class Mrl { - public String service; - public String method; - public Object[] param; - } - - public String mrljson; - public String mrl; -} - diff --git a/src/main/java/org/myrobotlab/programab/PredicateEvent.java b/src/main/java/org/myrobotlab/programab/PredicateEvent.java deleted file mode 100644 index bf256fa143..0000000000 --- a/src/main/java/org/myrobotlab/programab/PredicateEvent.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.myrobotlab.programab; - -/** - * Pojo for state change of one of ProgramAB's state info - * @author GroG - * - */ -public class PredicateEvent { - /** - * unique identifier for the session user & bot - */ - public String id; - /** - * name of the predicate changed - */ - public String name; - - // public String previousValue; - - /** - * new value - */ - public String value; - public String botName; - public String userName; - - @Override - public String toString() { - return String.format("%s %s=%s", id, name, value); - } -} diff --git a/src/main/java/org/myrobotlab/programab/Response.java b/src/main/java/org/myrobotlab/programab/Response.java index 128ae8cfc0..a2802196b7 100644 --- a/src/main/java/org/myrobotlab/programab/Response.java +++ b/src/main/java/org/myrobotlab/programab/Response.java @@ -3,6 +3,8 @@ import java.util.Date; import java.util.List; +import org.myrobotlab.programab.models.Mrl; + /** * FIXME - this class should become a more generalized AI response data object * in org.myrobotlab.data so that other AI systems (and search engines) can fill @@ -29,12 +31,14 @@ public class Response { /** * filtered oob data */ - public List payloads; + public List payloads; - public Response(String userName, String botName, String msg, List payloads) { + public Response(String userName, String botName, String msg, List payloads) { this.botName = botName; this.userName = userName; this.msg = msg; + + // what is this for ? this.payloads = payloads; } @@ -48,7 +52,7 @@ public String toString() { str.append("Msg:" + msg + ", "); str.append("Payloads:["); if (payloads != null) { - for (OOBPayload payload : payloads) { + for (Mrl payload : payloads) { str.append(payload.toString() + ", "); } } diff --git a/src/main/java/org/myrobotlab/programab/Session.java b/src/main/java/org/myrobotlab/programab/Session.java index a234310e57..c529044592 100644 --- a/src/main/java/org/myrobotlab/programab/Session.java +++ b/src/main/java/org/myrobotlab/programab/Session.java @@ -3,8 +3,8 @@ import java.io.File; import java.io.FileWriter; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.Date; +import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.TreeSet; @@ -13,6 +13,10 @@ import org.alicebot.ab.Predicates; import org.myrobotlab.io.FileIO; import org.myrobotlab.logging.LoggerFactory; +import org.myrobotlab.programab.handlers.oob.OobProcessor; +import org.myrobotlab.programab.models.Event; +import org.myrobotlab.programab.models.Mrl; +import org.myrobotlab.programab.models.Template; import org.myrobotlab.service.ProgramAB; import org.myrobotlab.service.config.ProgramABConfig; import org.slf4j.Logger; @@ -27,25 +31,55 @@ public class Session { transient public final static Logger log = LoggerFactory.getLogger(ProgramAB.class); - public String userName; - public boolean processOOB = true; + /** + * name of the user that owns this session + */ + public String username; + + /** + * last time the bot responded + */ public Date lastResponseTime = null; + + /** + * bot will prompt users if enabled trolling is true after + * maxConversationDelay has passed + */ public boolean enableTrolling = false; - // Number of milliseconds before the robot starts talking on its own. + + /** + * Number of milliseconds before the robot starts talking on its own. + */ public int maxConversationDelay = 5000; - // FIXME - could be transient ?? - transient public BotInfo botInfo; + /** + * general bot information + */ + public transient BotInfo botInfo; + + /** + * interface to program-ab + */ public transient Chat chat; - transient ProgramAB programab; + /** + * service that manages this session + */ + private transient ProgramAB programab; + /** + * current file associated with this user and session + */ public File predicatesFile; - // public Map predicates = new TreeMap<>(); - public Predicates predicates = null; + /** + * predicate data associated with this session + */ + protected Predicates predicates = null; - // current topic of this session + /** + * current topic of this session + */ public String currentTopic = null; /** @@ -61,35 +95,37 @@ public class Session { */ public Session(ProgramAB programab, String userName, BotInfo botInfo) { this.programab = programab; - this.userName = userName; + this.username = userName; this.botInfo = botInfo; + this.chat = loadChat(); + predicates = chat.predicates; + + Event event = new Event(programab.getName(), userName, null, null); + programab.invoke("publishSession", event); + + ProgramABConfig config = programab.getConfig(); + if (config.startTopic != null) { + chat.predicates.put("topic", config.startTopic); + } + + this.maxConversationDelay = config.maxConversationDelay; + this.enableTrolling = config.enableTrolling; } - /** - * lazy loading chat - * - * task to save predicates and getting responses will eventually call getBot - * we don't want initialization to create 2 when only one is needed - * - * @return - */ private synchronized Chat getChat() { - if (chat == null) { - chat = new Chat(botInfo.getBot()); - // loading predefined predicates - if they exist - File userPredicates = new File(FileIO.gluePaths(botInfo.path.getAbsolutePath(), String.format("config/%s.predicates.txt", userName))); - if (userPredicates.exists()) { - predicatesFile = userPredicates; - chat.predicates.getPredicateDefaults(userPredicates.getAbsolutePath()); - } - - ProgramABConfig config = (ProgramABConfig)programab.getConfig(); - if (config.startTopic != null){ - chat.predicates.put("topic", config.startTopic); - } + return chat; + } + + private Chat loadChat() { + Chat chat = new Chat(botInfo.getBot()); + // loading predefined predicates - if they exist + File userPredicates = new File(FileIO.gluePaths(botInfo.path.getAbsolutePath(), String.format("config/%s.predicates.txt", username))); + if (userPredicates.exists()) { + predicatesFile = userPredicates; + chat.predicates.getPredicateDefaults(userPredicates.getAbsolutePath()); } - predicates = chat.predicates; + return chat; } @@ -103,9 +139,9 @@ public void savePredicates() { sb.append(predicate + ":" + value + "\n"); } } - File predicates = new File(FileIO.gluePaths(botInfo.path.getAbsolutePath(), String.format("config/%s.predicates.txt", userName))); + File predicates = new File(FileIO.gluePaths(botInfo.path.getAbsolutePath(), String.format("config/%s.predicates.txt", username))); predicates.getParentFile().mkdirs(); - log.info("Bot : {} User : {} Predicates Filename : {} ", botInfo.name, userName, predicates); + log.info("bot : {} user : {} saving predicates filename : {} ", botInfo.botType, username, predicates); try { FileWriter writer = new FileWriter(predicates, StandardCharsets.UTF_8); writer.write(sb.toString()); @@ -118,6 +154,7 @@ public void savePredicates() { /** * Get all current predicate names and values + * * @return */ public Map getPredicates() { @@ -127,38 +164,28 @@ public Map getPredicates() { } public Response getResponse(String inText) { + try { + String returnText = getChat().multisentenceRespond(inText); + String xml = String.format("", returnText); + Template template = XmlParser.parseTemplate(xml); - String text = getChat().multisentenceRespond(inText); - - // Find any oob tags - ArrayList oobTags = OOBPayload.extractOOBPayloads(text, programab); + OobProcessor handler = OobProcessor.getInstance(programab); + handler.process(template.oob, true); // block by default - // invoke them all if configured to do so - if (processOOB) { - for (OOBPayload payload : oobTags) { - // assumption is this is non blocking invoking! - boolean oobRes = OOBPayload.invokeOOBPayload(payload, programab.getName(), false); - if (!oobRes) { - // there was a failure invoking - log.warn("Failed to invoke OOB/MRL tag : {}", OOBPayload.asOOBTag(payload)); - } - } - } - - // strip any oob tags if found - if (oobTags.size() > 0) { - text = OOBPayload.removeOOBFromString(text).trim(); + List mrl = template.oob != null ? template.oob.mrl : null; + // returned all text inside template but outside oob + Response response = new Response(username, botInfo.botType, template.text, mrl); + return response; + } catch (Exception e) { + programab.error(e); } - - Response response = new Response(userName, botInfo.name, text, oobTags); - return response; - + return new Response(username, botInfo.botType, "", null); } public Chat reload() { botInfo.reload(); - chat = null; - return getChat(); + chat = loadChat(); + return chat; } public void remove(String predicateName) { @@ -173,4 +200,12 @@ public String getPredicate(String predicateName) { return getChat().predicates.get(predicateName); } + public String getUsername() { + return username; + } + + public Object getBotType() { + return botInfo.botType; + } + } diff --git a/src/main/java/org/myrobotlab/programab/XmlParser.java b/src/main/java/org/myrobotlab/programab/XmlParser.java new file mode 100644 index 0000000000..51051af7db --- /dev/null +++ b/src/main/java/org/myrobotlab/programab/XmlParser.java @@ -0,0 +1,31 @@ +package org.myrobotlab.programab; + +import org.myrobotlab.programab.models.Sraix; +import org.myrobotlab.programab.models.Template; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; + +/** + * Thread safe fasterjackson xml parser. + * + * @author GroG + * + */ +public class XmlParser { + + public static Template parseTemplate(String xml) throws JsonMappingException, JsonProcessingException { + ThreadLocal xmlMapperThreadLocal = ThreadLocal.withInitial(XmlMapper::new); + XmlMapper xmlMapper = xmlMapperThreadLocal.get(); + Template template = xmlMapper.readValue(xml, Template.class); + return template; + } + + public static Sraix parseSraix(String xml) throws JsonMappingException, JsonProcessingException { + ThreadLocal xmlMapperThreadLocal = ThreadLocal.withInitial(XmlMapper::new); + XmlMapper xmlMapper = xmlMapperThreadLocal.get(); + return xmlMapper.readValue(xml, Sraix.class); + } + +} diff --git a/src/main/java/org/myrobotlab/programab/handlers/oob/OobProcessor.java b/src/main/java/org/myrobotlab/programab/handlers/oob/OobProcessor.java new file mode 100644 index 0000000000..a34e5aa347 --- /dev/null +++ b/src/main/java/org/myrobotlab/programab/handlers/oob/OobProcessor.java @@ -0,0 +1,93 @@ +package org.myrobotlab.programab.handlers.oob; + +import java.util.List; + +import org.myrobotlab.codec.CodecUtils; +import org.myrobotlab.framework.Message; +import org.myrobotlab.programab.models.Mrl; +import org.myrobotlab.programab.models.Oob; +import org.myrobotlab.service.ProgramAB; + +public class OobProcessor { + + private static OobProcessor instance; + private transient ProgramAB programab; + protected int maxBlockTime = 2000; + + private OobProcessor() { + } + + public static OobProcessor getInstance(ProgramAB programab) { + if (instance == null) { + instance = new OobProcessor(); + instance.programab = programab; + } + return instance; + } + + public Message toMsg(Mrl mrl) { + Object[] data = null; + if (mrl.params != null) { + data = new Object[mrl.params.size()]; + for (int i = 0; i < data.length; ++i) { + data[i] = mrl.params.get(i).trim(); + } + } + String service = mrl.service == null?null:mrl.service.trim(); + return Message.createMessage(programab.getName(), service, mrl.method.trim(), data); + } + + public String process(Oob oob, boolean block) { + StringBuilder sb = new StringBuilder(); + + // FIXME dynamic way of registering oobs + if (oob != null) { + // Process + if (oob.mrl != null) { + List mrls = oob.mrl; + for (Mrl mrl : mrls) { + if (!block) { + // programab.out(toMsg(mrl)); + programab.info("sending without blocking %s", toMsg(mrl)); + programab.send(toMsg(mrl)); + } else { + try { + programab.info("sendingBlocking without blocking %s", toMsg(mrl)); + Object o = programab.sendBlocking(toMsg(mrl), maxBlockTime); + if (o != null) { + sb.append(o); + } + } catch (Exception e) { + programab.error(e); + } + } + } + } // for each mrl + } + + // Process + if (oob != null && oob.mrljson != null) { + + Message[] msgs = CodecUtils.fromJson(oob.mrljson, Message[].class); + if (msgs != null) { + for (Message msg : msgs) { + + if (!block) { + programab.send(msg); + } else { + try { + Object o = programab.sendBlocking(msg, maxBlockTime); + if (o != null) { + sb.append(o); + } + } catch (Exception e) { + programab.error(e); + } + } + } // for each msg + } + } + + return sb.toString(); + } +} diff --git a/src/main/java/org/myrobotlab/programab/handlers/sraix/MrlSraixHandler.java b/src/main/java/org/myrobotlab/programab/handlers/sraix/MrlSraixHandler.java new file mode 100755 index 0000000000..6f26dbdefd --- /dev/null +++ b/src/main/java/org/myrobotlab/programab/handlers/sraix/MrlSraixHandler.java @@ -0,0 +1,58 @@ +package org.myrobotlab.programab.handlers.sraix; + +import java.util.Locale; + +import org.alicebot.ab.Chat; +import org.alicebot.ab.SraixHandler; +import org.myrobotlab.logging.LoggerFactory; +import org.myrobotlab.programab.XmlParser; +import org.myrobotlab.programab.handlers.oob.OobProcessor; +import org.myrobotlab.programab.models.Sraix; +import org.myrobotlab.service.ProgramAB; +import org.myrobotlab.service.data.SearchResults; +import org.myrobotlab.service.interfaces.SearchPublisher; +import org.slf4j.Logger; + +public class MrlSraixHandler implements SraixHandler { + transient public final static Logger log = LoggerFactory.getLogger(MrlSraixHandler.class); + + private ProgramAB programab = null; + + public MrlSraixHandler(ProgramAB programab) { + this.programab = programab; + } + + @Override + public String sraix(Chat chatSession, String input, String defaultResponse, String hint, String host, String botid, String apiKey, String limit, Locale locale) { + try { + log.debug("MRL Sraix handler! Input {}", input); + String xml = String.format("%s", input); + // Template template = XmlParser.parseTemplate(xml); + Sraix sraix = XmlParser.parseSraix(xml); + + if (sraix.oob != null) { + OobProcessor handler = OobProcessor.getInstance(programab); + String ret = handler.process(sraix.oob, true); // block by default + return ret; + } else if (sraix.search != null) { + log.info("search now"); + SearchPublisher search = (SearchPublisher)programab.startPeer("search"); + // if my default "search" peer key has a name .. use it ? + if (search != null) { + SearchResults results = search.search(sraix.search); + // return results.getTextAndImages(); + return results.getHtml(); + } else { + log.warn("no default search configured"); + } + } + } catch (Exception e) { + programab.error(e); + } + if (defaultResponse != null) { + return defaultResponse; + } + return ""; + } + +} diff --git a/src/main/java/org/myrobotlab/programab/models/Event.java b/src/main/java/org/myrobotlab/programab/models/Event.java new file mode 100644 index 0000000000..93e85c4086 --- /dev/null +++ b/src/main/java/org/myrobotlab/programab/models/Event.java @@ -0,0 +1,65 @@ +package org.myrobotlab.programab.models; + +/** + * Pojo for state change of one of ProgramAB's state info + * @author GroG + * + */ +public class Event { + /** + * the botName in this state change - typically + * current session botName + */ + public String botname; + /** + * unique identifier for the session user & bot + */ + public String id; + + /** + * name of the predicate changed + */ + public String name; + + /** + * service this topic change came from + */ + public String src; + + /** + * new topic or state name in this transition + */ + public String topic; + + /** + * timestamp + */ + public long ts = System.currentTimeMillis(); + + /** + * the user name in this state change - usually + * current session userName + */ + public String user; + + /** + * new value + */ + public String value; + + public Event() { + } + + public Event(String src, String userName, String botName, String topic) { + this.src = src; + this.user = userName; + this.botname = botName; + this.topic = topic; + } + + + @Override + public String toString() { + return String.format("%s %s=%s", id, name, value); + } +} diff --git a/src/main/java/org/myrobotlab/programab/models/Mrl.java b/src/main/java/org/myrobotlab/programab/models/Mrl.java new file mode 100644 index 0000000000..04c1bf79bb --- /dev/null +++ b/src/main/java/org/myrobotlab/programab/models/Mrl.java @@ -0,0 +1,14 @@ +package org.myrobotlab.programab.models; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; + +public class Mrl { + public String service; + public String method; + @JacksonXmlElementWrapper(useWrapping = false) + @JsonProperty("param") + public List params; +} \ No newline at end of file diff --git a/src/main/java/org/myrobotlab/programab/models/Oob.java b/src/main/java/org/myrobotlab/programab/models/Oob.java new file mode 100644 index 0000000000..833bab5a0f --- /dev/null +++ b/src/main/java/org/myrobotlab/programab/models/Oob.java @@ -0,0 +1,14 @@ +package org.myrobotlab.programab.models; + +import java.util.List; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; + +public class Oob { + + public String mrljson; + + @JacksonXmlElementWrapper(useWrapping = false) + public List mrl; +} + diff --git a/src/main/java/org/myrobotlab/programab/models/Sraix.java b/src/main/java/org/myrobotlab/programab/models/Sraix.java new file mode 100644 index 0000000000..99b0639cb6 --- /dev/null +++ b/src/main/java/org/myrobotlab/programab/models/Sraix.java @@ -0,0 +1,10 @@ +package org.myrobotlab.programab.models; + +// FIXME add attributes and internal tags +public class Sraix { + + public String search; + + public Oob oob; + +} diff --git a/src/main/java/org/myrobotlab/programab/models/Template.java b/src/main/java/org/myrobotlab/programab/models/Template.java new file mode 100644 index 0000000000..91f8e5de51 --- /dev/null +++ b/src/main/java/org/myrobotlab/programab/models/Template.java @@ -0,0 +1,51 @@ +package org.myrobotlab.programab.models; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText; + +//@JacksonXmlRootElement(localName = "template") +//@JsonIgnoreProperties(ignoreUnknown = true) +@JsonIgnoreProperties(ignoreUnknown = true) +public class Template { + // @JacksonXmlElementWrapper(useWrapping = false) + + @JacksonXmlProperty(localName = "template") + + @JacksonXmlText + public String text; + + +public Oob oob; + +// @JsonProperty("ignorable") +// public List oob; +// +// public List getOob() { +// return oob; +// } +// +// public void setOob(List oob) { +// this.oob = oob; +// } + + public static void main(String[] args) { + + try { + + // String xml = ""; + // String xml = ""; + String xml = ""; + + XmlMapper xmlMapper = new XmlMapper(); + Template template = xmlMapper.readValue(xml, Template.class); + + System.out.println(template); + + } catch(Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/org/myrobotlab/reflection/Locator.java b/src/main/java/org/myrobotlab/reflection/Locator.java deleted file mode 100644 index 6d8c6b5036..0000000000 --- a/src/main/java/org/myrobotlab/reflection/Locator.java +++ /dev/null @@ -1,115 +0,0 @@ -/** - * @author SwedaKonsult - * - * This file is part of MyRobotLab (http://myrobotlab.org). - * - * MyRobotLab is free software: you can redistribute it and/or modify - * it under the terms of the Apache License 2.0 as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version (subject to the "Classpath" exception - * as provided in the LICENSE.txt file that accompanied this code). - * - * MyRobotLab is distributed in the hope that it will be useful or fun, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Apache License 2.0 for more details. - * - * All libraries in thirdParty bundle are subject to their own license - * requirements - please refer to http://myrobotlab.org/libraries for - * details. - * - * Enjoy ! - * - */ -package org.myrobotlab.reflection; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; - -/** - * Class that allows for finding classes in files or packages. - * - * @author SwedaKonsult - * - */ -public class Locator { - /** - * Recursive method used to find all classes in a given directory and subdirs. - * - * @param directory - * Directory to start searching in - * @param packageName - * The package name for classes found inside the base directory - * @return The classes - */ - public static List> findClasses(File directory, String packageName) { - List> classes = new ArrayList>(); - try { - if (!directory.exists()) { - return classes; - } - } catch (SecurityException e) { - // in case we don't have read access to the directory - return classes; - } - File[] files = directory.listFiles(); - Class clazz; - String fileName; - for (File file : files) { - fileName = file.getName(); - // don't include hidden files - if (fileName.startsWith(".")) { - continue; - } - if (file.isDirectory()) { - classes.addAll(findClasses(file, String.format("%s.%s", packageName, fileName))); - continue; - } - if (!fileName.endsWith(".class")) { - continue; - } - try { - clazz = Class.forName(String.format("%s.%s", packageName, fileName.substring(0, fileName.length() - 6))); - } catch (ClassNotFoundException e) { - continue; - } catch (ExceptionInInitializerError e) { - continue; - } catch (LinkageError e) { - continue; - } - classes.add(clazz); - } - return classes; - } - - /** - * Scans all classes accessible from the context class loader which belong to - * the given package and subpackages. - * - * @param packageName - * The package to scan - * @return Located Classes - * @throws IOException - * if the path based on the packageName is not valid - */ - public static List> getClasses(String packageName) throws IOException { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - assert classLoader != null; - String path = packageName.replace('.', '/'); - Enumeration resources = classLoader.getResources(path); - List dirs = new ArrayList(); - while (resources.hasMoreElements()) { - URL resource = resources.nextElement(); - dirs.add(new File(resource.getFile())); - } - ArrayList> classes = new ArrayList>(); - for (File directory : dirs) { - classes.addAll(findClasses(directory, packageName)); - } - return classes; - } -} diff --git a/src/main/java/org/myrobotlab/reflection/Reflector.java b/src/main/java/org/myrobotlab/reflection/Reflector.java index c0a368522e..09b9d5e098 100644 --- a/src/main/java/org/myrobotlab/reflection/Reflector.java +++ b/src/main/java/org/myrobotlab/reflection/Reflector.java @@ -18,13 +18,8 @@ */ public class Reflector { - // private static HashMap cache = new HashMap(); - static final Logger log = LoggerFactory.getLogger(Reflector.class); - // final static String getSignature - /** * Allow for checking if a boxed primitive is being used. */ diff --git a/src/main/java/org/myrobotlab/runtime/ProcParser.java b/src/main/java/org/myrobotlab/runtime/ProcParser.java deleted file mode 100644 index 1c8fd711e8..0000000000 --- a/src/main/java/org/myrobotlab/runtime/ProcParser.java +++ /dev/null @@ -1,368 +0,0 @@ -package org.myrobotlab.runtime; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; - -import org.myrobotlab.codec.CodecUtils; -import org.myrobotlab.logging.LoggerFactory; -import org.myrobotlab.logging.Logging; -import org.myrobotlab.logging.LoggingFactory; -import org.slf4j.Logger; - -public class ProcParser { - - static public final String CPU = "CPU"; - - static public final String MEMORY = "MEMORY"; - - static public final String DISK = "DISK"; - - static public final String NETWORK = "NETWORK"; - - /* - * The pid of the process - not thread safe - */ - static private int processPid = -1; - - /* - * The line which the number of cpu cores is located in /proc/cpuinfo in - * kernel 2.6.32-34-generic. - */ - public static final int cpucoresline = 12; - - /* - * Constant access path string. Those with 'pid' before are in /proc/[pid]/; - * Those with 'net' are in /proc/net/. Those without are directly in /proc/. - */ - public static final String pidStatmPath = "/proc/#/statm"; - - public static final String pidStatPath = "/proc/#/stat"; - - public static final String statPath = "/proc/stat"; - - public static final String cpuinfoPath = "/proc/cpuinfo"; - - public static final String meminfoPath = "/proc/meminfo"; - - public static final String netdevPath = "/proc/net/dev"; - - public static final String partitionsPath = "/proc/partitions"; - public static final String diskstatsPath = "/proc/diskstats"; - public static final String EMPTY = ""; - public static final String COLON = ":"; - public static final String SPACE = " "; - public static final String SHARP = "#"; - public static final String LINE_SEPARATOR = "line.separator"; - - public final static Logger log = LoggerFactory.getLogger(ProcParser.class); - - public static Integer getArmInstructionVersion() { - String[] tempData = null; - String[] tempFile = null; - - Integer ret = 6; - - // Parse /proc/cpuinfo to obtain how many cores the CPU has. - String cpuInfo = getContents(cpuinfoPath); - if (cpuInfo != null) { - tempFile = cpuInfo.split(System.getProperty(LINE_SEPARATOR)); - - for (String line : tempFile) { - if (line.contains("Processor")) { - tempData = line.split(COLON); - break; - } - } - if (tempData == null) { - log.error("proc data not found - not a Linux system?"); - return null; - } - - if (tempData.length == 2) { - String idata = tempData[1]; - int pos0 = idata.indexOf("ARMv"); - if (pos0 > 0) { - String vdata = idata.substring(pos0 + 4, pos0 + 5); - try { - ret = Integer.parseInt(vdata); - } catch (Exception e) { - Logging.logError(e); - } - } - - } - } - - return ret; - } - - /** - * Fetch the entire contents of a text file, and return it in a String. This - * style of implementation does not throw Exceptions to the caller. - * - * @param path - * is a file which already exists and can be read. - * @throws IOException - */ - static private synchronized String getContents(String path) { - // ...checks on aFile are elided - StringBuilder contents = new StringBuilder(); - - try { - // use buffering, reading one line at a time - // FileReader always assumes default encoding is OK! - BufferedReader input = new BufferedReader(new FileReader(new File(path))); - try { - String line = null; // not declared within while loop - /* - * readLine is a bit quirky : it returns the content of a line MINUS the - * newline. it returns null only for the END of the stream. it returns - * an empty String if two newlines appear in a row. - */ - while ((line = input.readLine()) != null) { - contents.append(line); - contents.append(System.getProperty(LINE_SEPARATOR)); - } - } finally { - input.close(); - - } - } catch (IOException e) { - Logging.logError(e); - } - - return contents.toString(); - } - - public static ArrayList getCpuUsage() { - BufferedReader br = null; - ArrayList data = new ArrayList(); - String[] tempData; - try { - int numberOfCores = getNumberofCores(); - // Parse /proc/stat file and fill the member values list - // We gonna parse de first line (total) and each line corresponding - // to - // one core - // Line example: cpu0 311689 2102 654770 6755602 32431 38 4127 0 0 0 - br = getStream(statPath); - // read a dummy line just for skip the total cpu line - br.readLine(); - for (int core = 1; core <= numberOfCores; core++) { - data.add(String.valueOf(core)); - tempData = br.readLine().split(SPACE); - // Adds the first 9 fields. - for (int field = 1; field < 10; field++) { - data.add(tempData[field]); - } - } - br.close(); - } catch (IOException e) { - Logging.logError(e); - } - return data; - } - - public static ArrayList getDiskUsage() { - ArrayList partitionData = getPartitionUsage(); - ArrayList data = new ArrayList(); - String[] tempData = null; - String[] tempFile = null; - - tempFile = getContents(diskstatsPath).split(System.getProperty(LINE_SEPARATOR)); - ArrayList tempPart = getPartitionNames(partitionData); - // Parse /proc/diskstats to obtain disk statistics - - for (String line : tempFile) { - for (String partition : tempPart) { - if (line.contains(SPACE + partition + SPACE)) { - // split(SPACE); - tempData = line.split(SPACE); - // adds the rest of the disk statistics - data.addAll(Arrays.asList(tempData)); - data.removeAll(Collections.singleton(EMPTY)); - } - } - } - - return data; - } - - /** - * @param _processPid - * the process id - * @return memory usage information. Files: /proc/[pid]/statm /proc/[pid]/stat - * - */ - public static ArrayList getMemoryUsage(int _processPid) { - BufferedReader br = null; - ArrayList data = new ArrayList(); - String[] tempData = null; - try { - // Parse /proc/[pid]/statm file and fill the member values list with - // its contents (all) - tempData = getContents(pidStatmPath.replace(SHARP, String.valueOf(_processPid))).trim().split(SPACE); - data.addAll(Arrays.asList(tempData)); - // Parse /proc/[pid]/stat file and fill the member values list just - // with values 22, 23 and 24 (vsize, resident set size and resident - // set size limit). - tempData = getContents(pidStatPath.replace(SHARP, String.valueOf(_processPid))).trim().split(SPACE); - data.add(tempData[22]); - data.add(tempData[23]); - data.add(tempData[24]); - // Parse /proc/meminfo file for the system memory information. - br = getStream(meminfoPath); - for (int i = 0; i < 4; i++) { - tempData = br.readLine().trim().split(SPACE); - for (String s : tempData) { - if (s.length() != 0 && CodecUtils.tryParseInt(s)) { - data.add(s); - } - } - } - br.close(); - } catch (IOException e) { - Logging.logError(e); - } - return data; - } - - public static ArrayList getNetworkUsage() { - ArrayList data = new ArrayList(); - String[] tempData = null; - String[] tempFile = null; - - tempFile = getContents(netdevPath).split(System.getProperty(LINE_SEPARATOR)); - // Skip the first two lines (headers) - for (int i = 2; i < tempFile.length; i++) { - // Parse /proc/net/dev to obtain network statistics. - // Line e.g.: - // lo: 4852 43 0 0 0 0 0 0 4852 43 0 0 0 0 0 0 - tempData = tempFile[i].replace(COLON, SPACE).split(SPACE); - data.addAll(Arrays.asList(tempData)); - data.removeAll(Collections.singleton(EMPTY)); - } - return data; - } - - /** - * DEPRECATE - why do this? Java can do this? - * - * @return int - * @throws FileNotFoundException - * e - * @throws IOException - * e - * @throws NumberFormatException - * e - */ - public static int getNumberofCores() throws FileNotFoundException, IOException, NumberFormatException { - String[] tempData = null; - String[] tempFile = null; - - // Parse /proc/cpuinfo to obtain how many cores the CPU has. - tempFile = getContents(cpuinfoPath).split(System.getProperty(LINE_SEPARATOR)); - for (String line : tempFile) { - if (line.contains("cpu cores")) { - tempData = line.split(COLON); - break; - } - } - return Integer.parseInt(tempData[1].trim()); - } - - /* - * Create a list with the partitions name to be used to find their statistics - * in /proc/diskstats file - */ - public static ArrayList getPartitionNames(ArrayList data) { - - ArrayList partitionsName = new ArrayList(); - for (String string : data) { - if (!CodecUtils.tryParseInt(string)) { - partitionsName.add(string); - } - } - return partitionsName; - } - - public static ArrayList getPartitionUsage() { - ArrayList data = new ArrayList(); - String[] tempData = null; - String[] tempFile = null; - - tempFile = getContents(partitionsPath).split(System.getProperty(LINE_SEPARATOR)); - - // parse the disk partitions - for (int i = 2; i < tempFile.length; i++) { - tempData = tempFile[i].split(SPACE); - data.addAll(Arrays.asList(tempData)); - data.removeAll(Collections.singleton(EMPTY)); - } - return data; - } - - /** - * Opens a stream from a existing file and return it. This style of - * implementation does not throw Exceptions to the caller. - * - * @param path - * is a file which already exists and can be read. - * @throws IOException - */ - private synchronized static BufferedReader getStream(String _path) throws IOException { - BufferedReader br = null; - File file = new File(_path); - FileReader fileReader = null; - try { - fileReader = new FileReader(file); - br = new BufferedReader(fileReader); - - } catch (IOException e) { - Logging.logError(e); - } - return br; - } - - /* - * Gathers the usage statistic from the /proc file system for CPU, Memory, - * Disk and Network - */ - static public ArrayList getUsage(String uType) { - if ((uType == null) || (processPid < 0)) { - throw new IllegalArgumentException(); - } - ArrayList usageData = null; - String type = uType.toUpperCase(); - - if (type.equals("CPU")) { - usageData = getCpuUsage(); - } else if (type.equals("MEMORY")) { - usageData = getMemoryUsage(processPid); - } else if (type.equals("DISK")) { - usageData = getDiskUsage(); - } else if (type.equals("NETWORK")) { - usageData = getNetworkUsage(); - } - return usageData; - } - - public static void main(String[] args) { - LoggingFactory.init("DEBUG"); - - Integer v = ProcParser.getArmInstructionVersion(); - log.info("{}", v); - log.info("{}", v); - } - - public int setPid(int pid) { - processPid = pid; - return pid; - } -}// end ProcInfoParser diff --git a/src/main/java/org/myrobotlab/service/Adafruit16CServoDriver.java b/src/main/java/org/myrobotlab/service/Adafruit16CServoDriver.java index e47e8dce3e..cf804f607c 100644 --- a/src/main/java/org/myrobotlab/service/Adafruit16CServoDriver.java +++ b/src/main/java/org/myrobotlab/service/Adafruit16CServoDriver.java @@ -360,7 +360,11 @@ public void setPWM(String pinLabel, Integer pulseWidthOn, Integer pulseWidthOff) byte[] buffer = { (byte) (PCA9685_LED0_ON_L + (pin * 4)), (byte) (pulseWidthOn & 0xff), (byte) (pulseWidthOn >> 8), (byte) (pulseWidthOff & 0xff), (byte) (pulseWidthOff >> 8) }; log.debug("Writing pin {}, pulesWidthOn {}, pulseWidthOff {}", pin, pulseWidthOn, pulseWidthOff); - controller.i2cWrite(this, Integer.parseInt(deviceBus), Integer.decode(deviceAddress), buffer, buffer.length); + if (controller != null) { + controller.i2cWrite(this, Integer.parseInt(deviceBus), Integer.decode(deviceAddress), buffer, buffer.length); + } else { + error("controller not set!"); + } } /** diff --git a/src/main/java/org/myrobotlab/service/Arduino.java b/src/main/java/org/myrobotlab/service/Arduino.java index 38135bad41..327676d91f 100644 --- a/src/main/java/org/myrobotlab/service/Arduino.java +++ b/src/main/java/org/myrobotlab/service/Arduino.java @@ -839,41 +839,6 @@ public void enableAck(boolean enabled) { msg.enableAcks(enabled); } - transient BoardInfoPoller poller = new BoardInfoPoller(); - - public class BoardInfoPoller implements Runnable { - boolean running = false; - Thread thread = null; - - @Override - public void run() { - try { - running = true; - while (running) { - sendBoardInfoRequest(); - sleep(1000); - } - } catch (Exception e) { - log.info("board info stopping {}", e.getMessage()); - } - thread = null; - running = false; - } - - public void start() { - if (thread == null) { - thread = new Thread(this, "boardInfoPoller"); - thread.start(); - } - } - - public void stop() { - if (thread != null) { - thread.interrupt(); - } - } - } - // TODO - remove // MrlComm now constantantly sends a stream of BoardInfo // > enableBoardInfo/bool enabled - no point to this @@ -1875,8 +1840,6 @@ public void record() throws Exception { public void releaseService() { super.releaseService(); - poller.stop(); - // SHUTDOWN ACKING - use case - port no longer exists if (msg != null) { msg.enableAck(false); @@ -2354,23 +2317,17 @@ public ArduinoConfig getConfig() { return config; } + + public void startService() { + super.startService(); + if (config.connect && config.port != null && !isConnected()) { + connect(config.port); + } + } @Override public ArduinoConfig apply(ArduinoConfig c) { super.apply(c); - - if (msg == null) { - serial = (Serial) startPeer("serial"); - if (serial == null) { - log.error("serial is null"); - } - msg.setSerial(serial); - serial.addByteListener(this); - } else { - // TODO: figure out why this gets called so often. - log.info("Init serial we already have a msg class."); - } - if (config.connect && config.port != null) { connect(config.port); } diff --git a/src/main/java/org/myrobotlab/service/Emoji.java b/src/main/java/org/myrobotlab/service/Emoji.java index 64930f2923..b64573eaa6 100644 --- a/src/main/java/org/myrobotlab/service/Emoji.java +++ b/src/main/java/org/myrobotlab/service/Emoji.java @@ -1,6 +1,7 @@ package org.myrobotlab.service; import java.io.File; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashSet; @@ -8,19 +9,17 @@ import java.util.Map; import java.util.Set; +import org.myrobotlab.codec.CodecUtils; import org.myrobotlab.framework.Service; -import org.myrobotlab.fsm.api.Event; -import org.myrobotlab.fsm.api.EventHandler; -import org.myrobotlab.fsm.api.SimpleEvent; -import org.myrobotlab.fsm.api.State; -import org.myrobotlab.fsm.core.SimpleTransition; import org.myrobotlab.io.FileIO; import org.myrobotlab.logging.Level; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.logging.LoggingFactory; +import org.myrobotlab.service.FiniteStateMachine.StateChange; import org.myrobotlab.service.config.EmojiConfig; import org.myrobotlab.service.data.ImageData; import org.myrobotlab.service.interfaces.ImagePublisher; +import org.myrobotlab.service.interfaces.StateChangeHandler; import org.myrobotlab.service.interfaces.TextListener; import org.myrobotlab.service.interfaces.TextPublisher; import org.slf4j.Logger; @@ -28,17 +27,15 @@ // emotionListener // Links // - http://googleemotionalindex.com/ -public class Emoji extends Service implements TextListener, EventHandler, ImagePublisher { +public class Emoji extends Service implements TextListener, StateChangeHandler, ImagePublisher { private static final long serialVersionUID = 1L; public final static Logger log = LoggerFactory.getLogger(Emoji.class); - // transient ImageDisplay display = null; - transient HttpClient http = null; - State lastState = null; + String lastState = null; final Set validSize = new HashSet<>(); @@ -87,11 +84,20 @@ public void addEmojiMap() { } + /** + * Map a reference of some text like "grinning face" to a unicode reference. + * + * @param keyword - words desired + * @param unicode - emoji reference + */ public void addEmoji(String keyword, String unicode) { log.info("emoji {}:{}", keyword, unicode); ((EmojiConfig) config).map.put(keyword, unicode); } + /** + * clears all mappings + */ public void clearEmojis() { ((EmojiConfig) config).map.clear(); } @@ -99,17 +105,43 @@ public void clearEmojis() { @Override public void startService() { super.startService(); - - // FIXME - send default fullscreen always on top minAutoSize to display ? - // display = (ImageDisplay) startPeer("display"); http = (HttpClient) startPeer("http"); - addEmojiMap(); } - public void display(String source) { + /** + * Returns a base 64 string representation of the emoji image + * + * @param source - text or unicode refrence + * @return - base64 png + * @throws IOException + */ + public String getBase64Image(String source) throws IOException { + ImageData img = getImageData(source); + String ret = CodecUtils.toBase64(FileIO.toByteArray(new File(img.src))); + return ret; + } + + /** + * Returns filename and other info + * + * @param source - keyword or unicode reference + * @return ImageData + */ + public ImageData getImageData(String source) { + try { + if (source == null) { + error("emoji source cannot be null"); + return null; + } + + boolean isUnicode = source.toUpperCase().startsWith("U+"); + if (isUnicode) { + source = source.toUpperCase(); + } + String cacheDir = getEmojiCacheDir(); log.info("display source {} fullscreen {}", source); @@ -123,12 +155,23 @@ public void display(String source) { Map map = ((EmojiConfig) config).map; // check for keyword - if (map.containsKey(source)) { - String unicodeFileName = cacheDir + File.separator + map.get(source) + ".png"; + if (map.containsKey(source) || isUnicode) { + String unicodeFileName = null; + if (isUnicode) { + unicodeFileName = cacheDir + File.separator + source + ".png"; + } else { + unicodeFileName = cacheDir + File.separator + map.get(source) + ".png"; + } if (!new File(unicodeFileName).exists()) { try { - String fetchCode = map.get(source).replace("+", "").toLowerCase(); - String url = ((EmojiConfig) config).emojiSourceUrlTemplate.replace("{size}", "" + getSize()).replace("{code}", fetchCode).replace("{CODE}", source); + String fetchCode = null; + if (isUnicode) { + fetchCode = source.replace("+", "").toLowerCase(); + } else { + fetchCode = map.get(source).replace("+", "").toLowerCase(); + } + String url = ((EmojiConfig) config).emojiSourceUrlTemplate.replace("{size}", "" + getSize()) + .replace("{code}", fetchCode).replace("{CODE}", source); byte[] bytes = http.getBytes(url); FileIO.toFile(unicodeFileName, bytes); } catch (Exception e) { @@ -164,11 +207,18 @@ public void display(String source) { img.src = filename; img.source = getName(); - invoke("publishImage", img); + return img; } catch (Exception e) { log.error("displayFullScreen threw", e); } + return null; + } + + public void display(String source) { + ImageData img = getImageData(source); + invoke("publishImage", img); + invoke("publishDisplay", img); } public int getSize() { @@ -192,16 +242,13 @@ public void onText(String text) { // FIXME - publish events if desired... @Override - public void handleEvent(Event event) throws Exception { + public void handleStateChange(StateChange event) { log.info("handleEvent {}", event); - SimpleEvent se = (SimpleEvent) event; - SimpleTransition transition = (SimpleTransition) se.getTransition(); EmojiData emoji = new EmojiData(); - emoji.name = transition.getTargetState().getName(); + emoji.name = event.state; emoji.unicode = ((EmojiConfig) config).map.get(emoji.name); invoke("publishEmoji", emoji); - display(transition.getTargetState().getName()); - // emotionalState.getCurrentState().getName(); + display(event.state); } public void publishEmoji(EmojiData emoji) { @@ -224,12 +271,17 @@ public static void main(String[] args) { // scan text for emotional words - addEmotionWordPair(happy 1f609) ... LoggingFactory.init(Level.WARN); - Runtime.startConfig("emoji-display-2"); + // Runtime.startConfig("emoji-display-2"); // Runtime.startConfig("emoji-display-1"); // Runtime.saveConfig("emoji-display-2"); Emoji emoji = (Emoji) Runtime.start("emoji", "Emoji"); + + String imgTag = String.format("", emoji.getBase64Image("U+1F98D")); + + FileIO.toFile("emoji.html", String.format("%s", imgTag)); + ImageDisplay display = (ImageDisplay) Runtime.start("display", "ImageDisplay"); emoji.attachImageListener(display); @@ -362,6 +414,11 @@ public static void main(String[] args) { } } + public String getImageFile(String emoji) { + ImageData data = getImageData(emoji); + return data.src; + } + @Override public void attachTextPublisher(TextPublisher service) { if (service == null) { diff --git a/src/main/java/org/myrobotlab/service/FiniteStateMachine.java b/src/main/java/org/myrobotlab/service/FiniteStateMachine.java index d04436f1e3..7a290bc459 100644 --- a/src/main/java/org/myrobotlab/service/FiniteStateMachine.java +++ b/src/main/java/org/myrobotlab/service/FiniteStateMachine.java @@ -12,6 +12,7 @@ import org.myrobotlab.framework.Service; import org.myrobotlab.framework.interfaces.MessageListener; import org.myrobotlab.framework.interfaces.ServiceInterface; +import org.myrobotlab.generics.SlidingWindowList; import org.myrobotlab.logging.Level; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.logging.LoggingFactory; @@ -51,7 +52,7 @@ public class FiniteStateMachine extends Service { /** * state history of fsm */ - protected List history = new ArrayList<>(); + protected List history = new SlidingWindowList<>(100); // TODO - .from("A").to("B").on(Messages.ANY) // TODO - .from("A").to("B").on(Messages.EMPTY) @@ -65,17 +66,27 @@ public class Tuple { } public class StateChange { - public String last; - public String current; + /** + * timestamp + */ + public long ts = System.currentTimeMillis(); + + /** + * current new state + */ + public String state; + + /** + * event which activated new state + */ public String event; - public StateChange(String last, String current, String event) { - this.last = last; - this.current = current; + public StateChange(String current, String event) { + this.state = current; this.event = event; } public String toString() { - return String.format("%s --%s--> %s", last, event, current); + return String.format("%s --%s--> %s", last, event, state); } } @@ -113,11 +124,8 @@ public String getNext(String key) { public void init() { stateMachine.init(); State state = stateMachine.getCurrent(); - if (history.size() > 100) { - history.remove(0); - } if (state != null) { - history.add(state.getName()); + history.add(new StateChange(state.getName(), String.format("%s.setCurrent", getName()))); } } @@ -194,8 +202,9 @@ public void fire(String event) { log.info("fired event ({}) -> ({}) moves to ({})", event, last == null ? null : last.getName(), current == null ? null : current.getName()); if (last != null && !last.equals(current)) { - invoke("publishStateChange", new StateChange(last.getName(), current.getName(), event)); - history.add(current.getName()); + StateChange stateChange = new StateChange(current.getName(), event); + invoke("publishStateChange", stateChange); + history.add(stateChange); } } catch (Exception e) { log.error("fire threw", e); @@ -247,7 +256,7 @@ public StateChange publishStateChange(StateChange stateChange) { for (String listener : messageListeners) { ServiceInterface service = Runtime.getService(listener); if (service != null) { - org.myrobotlab.framework.Message msg = org.myrobotlab.framework.Message.createMessage(getName(), listener, CodecUtils.getCallbackTopicName(stateChange.current), null); + org.myrobotlab.framework.Message msg = org.myrobotlab.framework.Message.createMessage(getName(), listener, CodecUtils.getCallbackTopicName(stateChange.state), null); service.in(msg); } } @@ -419,7 +428,7 @@ public void setCurrent(String state) { stateMachine.setCurrent(state); current = stateMachine.getCurrent(); if (last != null && !last.equals(current)) { - invoke("publishStateChange", new StateChange(last.getName(), current.getName(), null)); + invoke("publishStateChange", new StateChange(current.getName(), String.format("%s.setCurrent", getName()))); } } catch (Exception e) { log.error("setCurrent threw", e); @@ -427,4 +436,12 @@ public void setCurrent(String state) { } } + public String getPreviousState() { + if (history.size() == 0) { + return null; + } else { + return history.get(history.size() - 2).state; + } + } + } \ No newline at end of file diff --git a/src/main/java/org/myrobotlab/service/Gpt3.java b/src/main/java/org/myrobotlab/service/Gpt3.java index 82d9ad90d2..93e4404b3c 100644 --- a/src/main/java/org/myrobotlab/service/Gpt3.java +++ b/src/main/java/org/myrobotlab/service/Gpt3.java @@ -48,6 +48,7 @@ * @author GroG * */ +@Deprecated /* use OpenAI service */ public class Gpt3 extends Service implements TextListener, TextPublisher, UtterancePublisher, UtteranceListener, ResponsePublisher { private static final long serialVersionUID = 1L; diff --git a/src/main/java/org/myrobotlab/service/ImageDisplay.java b/src/main/java/org/myrobotlab/service/ImageDisplay.java index d115227158..d921e2f270 100644 --- a/src/main/java/org/myrobotlab/service/ImageDisplay.java +++ b/src/main/java/org/myrobotlab/service/ImageDisplay.java @@ -669,7 +669,7 @@ public void run() { public void startService() { super.startService(); if (GraphicsEnvironment.isHeadless()) { - log.warn("in headless mode - %s will not display images", getName()); + log.info("in headless mode - {} will not display images", getName()); return; } } diff --git a/src/main/java/org/myrobotlab/service/InMoov2.java b/src/main/java/org/myrobotlab/service/InMoov2.java index 7ef89d2465..a8ff2d94aa 100644 --- a/src/main/java/org/myrobotlab/service/InMoov2.java +++ b/src/main/java/org/myrobotlab/service/InMoov2.java @@ -3,14 +3,19 @@ import java.io.File; import java.io.FilenameFilter; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; +import java.util.TreeMap; import java.util.TreeSet; +import java.util.concurrent.locks.ReentrantLock; import org.apache.commons.io.FilenameUtils; +import org.myrobotlab.codec.CodecUtils; import org.myrobotlab.framework.Message; import org.myrobotlab.framework.Plan; import org.myrobotlab.framework.Platform; @@ -24,29 +29,32 @@ import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.logging.LoggingFactory; import org.myrobotlab.opencv.OpenCVData; -import org.myrobotlab.programab.PredicateEvent; import org.myrobotlab.programab.Response; +import org.myrobotlab.programab.models.Event; +import org.myrobotlab.service.Log.LogEntry; import org.myrobotlab.service.abstracts.AbstractSpeechRecognizer; import org.myrobotlab.service.abstracts.AbstractSpeechSynthesis; import org.myrobotlab.service.config.InMoov2Config; import org.myrobotlab.service.config.OpenCVConfig; import org.myrobotlab.service.config.SpeechSynthesisConfig; import org.myrobotlab.service.data.JoystickData; -import org.myrobotlab.service.data.LedDisplayData; import org.myrobotlab.service.data.Locale; +import org.myrobotlab.service.data.SensorData; import org.myrobotlab.service.interfaces.IKJointAngleListener; import org.myrobotlab.service.interfaces.JoystickListener; import org.myrobotlab.service.interfaces.LocaleProvider; import org.myrobotlab.service.interfaces.ServiceLifeCycleListener; import org.myrobotlab.service.interfaces.ServoControl; import org.myrobotlab.service.interfaces.Simulator; +import org.myrobotlab.service.interfaces.SpeechListener; import org.myrobotlab.service.interfaces.SpeechRecognizer; import org.myrobotlab.service.interfaces.SpeechSynthesis; import org.myrobotlab.service.interfaces.TextListener; import org.myrobotlab.service.interfaces.TextPublisher; import org.slf4j.Logger; -public class InMoov2 extends Service implements ServiceLifeCycleListener, TextListener, TextPublisher, JoystickListener, LocaleProvider, IKJointAngleListener { +public class InMoov2 extends Service + implements ServiceLifeCycleListener, SpeechListener, TextListener, TextPublisher, JoystickListener, LocaleProvider, IKJointAngleListener { public final static Logger log = LoggerFactory.getLogger(InMoov2.class); @@ -56,6 +64,11 @@ public class InMoov2 extends Service implements ServiceLifeCycleL static String speechRecognizer = "WebkitSpeechRecognition"; + /** + * number of times waited in boot state + */ + protected int bootCount = 0; + /** * This method will load a python file into the python interpreter. * @@ -66,6 +79,7 @@ public class InMoov2 extends Service implements ServiceLifeCycleL @Deprecated /* use execScript - this doesn't handle resources correctly */ public static boolean loadFile(String file) { File f = new File(file); + // FIXME cannot be casting to Python Python p = (Python) Runtime.getService("python"); log.info("Loading Python file {}", f.getAbsolutePath()); if (p == null) { @@ -95,6 +109,11 @@ public static boolean loadFile(String file) { return true; } + /** + * the config that was processed before booting, if there was one. + */ + protected String bootedConfig = null; + protected transient ProgramAB chatBot; protected List configList; @@ -109,12 +128,28 @@ public static boolean loadFile(String file) { protected transient SpeechRecognizer ear; + protected List errors = new ArrayList<>(); + + /** + * The finite state machine is core to managing state of InMoov2. There is + * very little benefit gained in having the interactions pub/sub. Therefore, + * there will be a direct reference to the fsm. If different state graph is + * needed, then the fsm can provide that service. + */ + private transient FiniteStateMachine fsm = null; // waiting controable threaded gestures we warn user protected boolean gestureAlreadyStarted = false; protected Set gestures = new TreeSet(); + /** + * Prevents actions or events from happening when InMoov2 is first booted + */ + protected boolean hasBooted = false; + + protected boolean isPirOn = false; + protected transient HtmlFilter htmlFilter; protected transient ImageDisplay imageDisplay; @@ -138,10 +173,24 @@ public static boolean loadFile(String file) { protected transient Python python; + protected long stateLastIdleTime = System.currentTimeMillis(); + + protected long stateLastRandomTime = System.currentTimeMillis(); + protected String voiceSelected; + /** + * Generalized memory, used for normalizing data from different services into + * one centralized place. Not the same as configuration, as this definition is + * owned by what the user needs vs configuration is what the service needs and + * understands. Python, ProgramAB and the InMoov2 service can all normalize + * their data here with one way or two way bindings + */ + protected Map memory = new TreeMap<>(); + public InMoov2(String n, String id) { super(n, id); + locales = Locale.getLocaleMap("en-US", "fr-FR", "es-ES", "de-DE", "nl-NL", "ru-RU", "hi-IN", "it-IT", "fi-FI", "pt-PT", "tr-TR"); } // should be removed in favor of general listeners @@ -155,7 +204,7 @@ public InMoov2Config apply(InMoov2Config c) { super.apply(c); try { - locales = Locale.getLocaleMap("en-US", "fr-FR", "es-ES", "de-DE", "nl-NL", "ru-RU", "hi-IN", "it-IT", "fi-FI", "pt-PT", "tr-TR"); + Runtime.start("python"); if (c.locale != null) { setLocale(c.locale); @@ -163,11 +212,11 @@ public InMoov2Config apply(InMoov2Config c) { setLocale(getSupportedLocale(Runtime.getInstance().getLocale().toString())); } - loadAppsScripts(); - - loadInitScripts(); - if (c.loadGestures) { + loadAppsScripts(); + + loadInitScripts(); + loadGestures(); } @@ -183,8 +232,6 @@ public InMoov2Config apply(InMoov2Config c) { return c; } - - @Override public void attachTextListener(String name) { addListener("publishText", name); @@ -383,14 +430,8 @@ public void enable() { * @param pythonCode * @return */ - public boolean exec(String pythonCode) { - try { - Python p = (Python) Runtime.start("python", "Python"); - return p.exec(pythonCode, true); - } catch (Exception e) { - error("unable to execute script %s", pythonCode); - return false; - } + public void exec(String pythonCode) { + send("python", "exec", pythonCode); } /** @@ -407,6 +448,7 @@ public String execGesture(String gesture) { subscribe("python", "publishStatus", this.getName(), "onGestureStatus"); startedGesture(gesture); lastGestureExecuted = gesture; + // FIXME cannot be casting to Python Python python = (Python) Runtime.getService("python"); if (python == null) { error("python service not started"); @@ -415,11 +457,25 @@ public String execGesture(String gesture) { return python.evalAndWait(gesture); } + /** + * Possible pub/sub way to interface with python - no blocking though + * + * @param code + * @return + */ + public String publishPython(String code) { + return code; + } + /** * FIXME - I think there was lots of confusion of executing resources or just * a file on the file system ... "execScript" I would expect to be just a file * on the file system. * + * FIXME - this is a mess - the UI uses this function exhaustively, it should + * not ! it should be appropriately named to execResource or execResourcefile + * + * * If resource semantics are needed there should be a execResourceScript which * adds the context and calls the underlying execScript "which only" executes * a filesystem file :P @@ -428,15 +484,9 @@ public String execGesture(String gesture) { * execute a resource script * @return success or failure */ - public boolean execScript(String someScriptName) { - try { - Python p = (Python) Runtime.start("python", "Python"); - String script = getResourceAsString(someScriptName); - return p.exec(script, true); - } catch (Exception e) { - error("unable to execute script %s", someScriptName); - return false; - } + public void execScript(String someScriptName) { + String script = getResourceAsString(someScriptName); + send("python", "exec", script); } public void finishedGesture() { @@ -455,7 +505,23 @@ 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); + // systemEvent(event); + fsm.fire(event); + } + + /** + * used to configure a flashing event - could use configuration to signal + * different colors and states + * + * @return + */ + public void flash() { + invoke("publishFlash", "default"); + } + + public String flash(String name) { + invoke("publishFlash", name); + return name; } public void fullSpeed() { @@ -467,13 +533,40 @@ public void fullSpeed() { sendToPeer("torso", "fullSpeed"); } - // FIXME - remove all of this form of localization + /** + * Generalized memory get/set will probably need to save at some point as well + * + * @param key + * @return + */ public String get(String key) { - String ret = localize(key); - if (ret != null) { - return ret; + Object ret = memory.get(key); + if (ret == null) { + return null; } - return "not yet translated"; + return ret.toString(); + } + + /** + * Generalized memory setter + * + * @param key + * @param data + * @return + */ + public Object set(String key, Object data) { + return memory.put(key, data); + } + + /** + * rebroadcasted from chatBot whenever predicates change + * + * @param predicate + * @return + */ + public Event publishPredicate(Event predicate) { + predicate.src = getName(); + return predicate; } public InMoov2Arm getArm(String side) { @@ -502,30 +595,32 @@ public InMoov2Head getHead() { * @return the timestamp of the last activity time. */ public Long getLastActivityTime() { - try { - - Long lastActivityTime = 0L; - - Long head = (Long) sendToPeerBlocking("head", "getLastActivityTime", getName()); - Long leftArm = (Long) sendToPeerBlocking("leftArm", "getLastActivityTime", getName()); - Long rightArm = (Long) sendToPeerBlocking("rightArm", "getLastActivityTime", getName()); - Long leftHand = (Long) sendToPeerBlocking("leftHand", "getLastActivityTime", getName()); - Long rightHand = (Long) sendToPeerBlocking("rightHand", "getLastActivityTime", getName()); - Long torso = (Long) sendToPeerBlocking("torso", "getLastActivityTime", getName()); - - lastActivityTime = Math.max(head, leftArm); - lastActivityTime = Math.max(lastActivityTime, rightArm); - lastActivityTime = Math.max(lastActivityTime, leftHand); - lastActivityTime = Math.max(lastActivityTime, rightHand); - lastActivityTime = Math.max(lastActivityTime, torso); - - return lastActivityTime; - - } catch (Exception e) { - error(e); - return null; + Long head = (InMoov2Head) getPeer("head") != null ? ((InMoov2Head) getPeer("head")).getLastActivityTime() : null; + Long leftArm = (InMoov2Arm) getPeer("leftArm") != null ? ((InMoov2Arm) getPeer("leftArm")).getLastActivityTime() : null; + Long rightArm = (InMoov2Arm) getPeer("rightArm") != null ? ((InMoov2Arm) getPeer("rightArm")).getLastActivityTime() : null; + Long leftHand = (InMoov2Hand) getPeer("leftHand") != null ? ((InMoov2Hand) getPeer("leftHand")).getLastActivityTime() : null; + Long rightHand = (InMoov2Hand) getPeer("rightHand") != null ? ((InMoov2Hand) getPeer("rightHand")).getLastActivityTime() : null; + Long torso = (InMoov2Torso) getPeer("torso") != null ? ((InMoov2Torso) getPeer("torso")).getLastActivityTime() : null; + + Long lastActivityTime = null; + + if (head != null || leftArm != null || rightArm != null || leftHand != null || rightHand != null || torso != null) { + lastActivityTime = 0L; + if (head != null) + lastActivityTime = Math.max(lastActivityTime, head); + if (leftArm != null) + lastActivityTime = Math.max(lastActivityTime, leftArm); + if (rightArm != null) + lastActivityTime = Math.max(lastActivityTime, rightArm); + if (leftHand != null) + lastActivityTime = Math.max(lastActivityTime, leftHand); + if (rightHand != null) + lastActivityTime = Math.max(lastActivityTime, rightHand); + if (torso != null) + lastActivityTime = Math.max(lastActivityTime, torso); } + return lastActivityTime; } public InMoov2Arm getLeftArm() { @@ -574,6 +669,13 @@ public InMoov2Hand getRightHand() { return (InMoov2Hand) getPeer("rightHand"); } + public String getState() { + if (fsm == null) { + return null; + } + return fsm.getCurrent(); + } + /** * matches on language only not variant expands language match to full InMoov2 * bot locale @@ -604,10 +706,6 @@ public InMoov2Torso getTorso() { return (InMoov2Torso) getPeer("torso"); } - public InMoov2Config getTypedConfig() { - return (InMoov2Config) config; - } - public void halfSpeed() { sendToPeer("head", "setSpeed", 25.0, 25.0, 25.0, 25.0, 100.0, 25.0); sendToPeer("rightHand", "setSpeed", 30.0, 30.0, 30.0, 30.0, 30.0, 30.0); @@ -648,7 +746,7 @@ public void loadAppsScripts() throws IOException { loadScripts(getResourceDir() + fs + "gestures/InMoovApps/Rock_Paper_Scissors"); loadScripts(getResourceDir() + fs + "gestures/InMoovApps/Kids_WordsGame"); } - + public void loadGestures() { loadGestures(getResourceDir() + fs + "gestures"); } @@ -663,7 +761,7 @@ public void loadGestures() { * @return true/false */ public boolean loadGestures(String directory) { - invoke("publishEvent", "LOAD GESTURES"); + systemEvent("LOAD GESTURES"); // iterate over each of the python files in the directory // and load them into the python interpreter. @@ -693,7 +791,7 @@ public boolean loadGestures(String directory) { info("%s Gestures loaded, %s Gestures with error", totalLoaded, totalError); broadcastState(); if (totalError > 0) { - invoke("publishEvent", "GESTURE_ERROR"); + systemEvent("GESTURE_ERROR"); return false; } return true; @@ -709,7 +807,7 @@ public void loadScripts(String directory) throws IOException { File dir = new File(directory); if (!dir.exists() || !dir.isDirectory()) { - invoke("publishEvent", "LOAD SCRIPTS ERROR"); + systemEvent("LOAD SCRIPTS ERROR"); return; } @@ -724,17 +822,24 @@ public boolean accept(File dir, String name) { if (files != null) { for (File file : files) { - Python p = (Python) Runtime.start("python", "Python"); - if (p != null) { - p.execFile(file.getAbsolutePath()); - } + send("python", "execFile", file.getAbsolutePath()); } } } } public void moveArm(String which, Double bicep, Double rotate, Double shoulder, Double omoplate) { - invoke("publishMoveArm", which, bicep, rotate, shoulder, omoplate); + HashMap map = new HashMap<>(); + Optional.ofNullable(bicep).ifPresent(value -> map.put("bicep", value)); + Optional.ofNullable(rotate).ifPresent(value -> map.put("rotate", value)); + Optional.ofNullable(shoulder).ifPresent(value -> map.put("shoulder", value)); + Optional.ofNullable(omoplate).ifPresent(value -> map.put("omoplate", value)); + + if ("left".equals(which)) { + invoke("publishMoveLeftArm", map); + } else { + invoke("publishMoveRightArm", map); + } } public void moveEyelids(Double eyelidleftPos, Double eyelidrightPos) { @@ -750,7 +855,19 @@ public void moveHand(String which, Double thumb, Double index, Double majeure, D } public void moveHand(String which, Double thumb, Double index, Double majeure, Double ringFinger, Double pinky, Double wrist) { - invoke("publishMoveHand", which, thumb, index, majeure, ringFinger, pinky, wrist); + HashMap map = new HashMap<>(); + Optional.ofNullable(thumb).ifPresent(value -> map.put("thumb", value)); + Optional.ofNullable(index).ifPresent(value -> map.put("index", value)); + Optional.ofNullable(majeure).ifPresent(value -> map.put("majeure", value)); + Optional.ofNullable(ringFinger).ifPresent(value -> map.put("ringFinger", value)); + Optional.ofNullable(pinky).ifPresent(value -> map.put("pinky", value)); + Optional.ofNullable(wrist).ifPresent(value -> map.put("wrist", value)); + + if ("left".equals(which)) { + invoke("publishMoveLeftHand", map); + } else { + invoke("publishMoveRightHand", map); + } } public void moveHead(Double neck, Double rothead) { @@ -766,13 +883,24 @@ public void moveHead(Double neck, Double rothead, Double eyeX, Double eyeY, Doub } public void moveHead(Double neck, Double rothead, Double eyeX, Double eyeY, Double jaw, Double rollNeck) { - invoke("publishMoveHead", neck, rothead, eyeX, eyeY, jaw, rollNeck); + HashMap map = new HashMap<>(); + Optional.ofNullable(neck).ifPresent(value -> map.put("neck", value)); + Optional.ofNullable(rothead).ifPresent(value -> map.put("rothead", value)); + Optional.ofNullable(eyeX).ifPresent(value -> map.put("eyeX", value)); + Optional.ofNullable(eyeY).ifPresent(value -> map.put("eyeY", value)); + Optional.ofNullable(jaw).ifPresent(value -> map.put("jaw", value)); + Optional.ofNullable(rollNeck).ifPresent(value -> map.put("rollNeck", value)); + invoke("publishMoveHead", map); } public void moveHead(Integer neck, Integer rothead, Integer rollNeck) { moveHead((double) neck, (double) rothead, null, null, null, (double) rollNeck); } + public void moveHeadBlocking(Integer neck, Integer rothead) { + moveHeadBlocking((double) neck, (double) rothead, null); + } + public void moveHeadBlocking(Double neck, Double rothead) { moveHeadBlocking(neck, rothead, null); } @@ -818,8 +946,11 @@ public void moveRightHand(Integer thumb, Integer index, Integer majeure, Integer } public void moveTorso(Double topStom, Double midStom, Double lowStom) { - // the "right" way - invoke("publishMoveTorso", topStom, midStom, lowStom); + HashMap map = new HashMap<>(); + Optional.ofNullable(topStom).ifPresent(value -> map.put("topStom", value)); + Optional.ofNullable(midStom).ifPresent(value -> map.put("midStom", value)); + Optional.ofNullable(lowStom).ifPresent(value -> map.put("lowStom", value)); + invoke("publishMoveTorso", map); } public void moveTorsoBlocking(Double topStom, Double midStom, Double lowStom) { @@ -827,10 +958,10 @@ public void moveTorsoBlocking(Double topStom, Double midStom, Double lowStom) { sendToPeer("torso", "moveToBlocking", topStom, midStom, lowStom); } - public PredicateEvent onChangePredicate(PredicateEvent event) { + public Event onChangePredicate(Event event) { log.error("onChangePredicate {}", event); if (event.name.equals("topic")) { - invoke("publishEvent", String.format("TOPIC CHANGED TO %s", event.value)); + systemEvent(String.format("TOPIC CHANGED TO %s", event.value)); } // depending on configuration .... // call python ? @@ -839,6 +970,12 @@ public PredicateEvent onChangePredicate(PredicateEvent event) { return event; } + public void onConfigFinished(String configName) { + log.info("onConfigFinished"); + configStarted = false; + invoke("publishBoot"); + } + /** * comes in from runtime which owns the config list * @@ -857,8 +994,8 @@ public void onCreated(String fullname) { public void onFinishedConfig(String configName) { log.info("onFinishedConfig"); - // invoke("publishEvent", "configFinished"); - invoke("publishFinishedConfig", configName); + // systemEvent("configFinished"); + invoke("publishConfigFinished", configName); } public void onGestureStatus(Status status) { @@ -870,6 +1007,169 @@ public void onGestureStatus(Status status) { unsubscribe("python", "publishStatus", this.getName(), "onGestureStatus"); } + protected long heartbeatCount = 0; + + /** + * Allows or prevents sensor input from processing. Initial boot prevents + * sensor data from interfering with booting. + */ + protected boolean allowSensorInput = false; + + /** + * A generalized recurring event which can perform checks and various other + * methods or tasks. Heartbeats will not start until after boot stage. + */ + public void onHeartbeat(String name) { + try { + heartbeatCount++; + // heartbeats can start before config is + // done processing - so the following should + // not be dependent on config + Runtime runtime = Runtime.getInstance(); + + // no longer having problems with synchronization of other + // services - boot is immutable - no scripts can run until it is done + // and it will run InMoov2.py + if (fsm == null || "boot".equals(getState())) { + // need to keep trying to boot + boot(); + log.warn("not ready to leave boot - runtime still processing config"); + return; + } + // prepare report + if (!hasBooted) { + log.info("boot hasn't completed, will not process heartbeat"); + return; + } + + Long lastActivityTime = getLastActivityTime(); + + // FIXME lastActivityTime != 0 is bogus - the value should be null if + // never set + if (config.stateIdleInterval != null && lastActivityTime != null && lastActivityTime != 0 + && lastActivityTime + (config.stateIdleInterval * 1000) < System.currentTimeMillis()) { + stateLastIdleTime = lastActivityTime; + } + + // will fire an idle event if there has been no activity + if (System.currentTimeMillis() > stateLastIdleTime + (config.stateIdleInterval * 1000)) { + fire("idle"); + stateLastIdleTime = System.currentTimeMillis(); + } + + // interval event firing + if (config.stateRandomInterval != null && System.currentTimeMillis() > stateLastRandomTime + (config.stateRandomInterval * 1000)) { + // fire("random"); + stateLastRandomTime = System.currentTimeMillis(); + } + + // FIXME publishInactivit(long time) + // FIXME publishLastActivity + + if (config.flashOnPir) { + Pir pir = (Pir) getPeer("pir"); + if (pir != null && pir.isActive()) { + flash("pir"); + } + } + + } catch (Exception e) { + error(e); + } + + // if (config.pirOnFlash && isPeerStarted("pir") && isPirOn) { + // flash("pir"); + // } + + if (config.batteryInSystem) { + double batteryLevel = Runtime.getBatteryLevel(); + invoke("publishBatteryLevel", batteryLevel); + // FIXME - thresholding should always have old value or state + // so we don't pump endless errors + if (batteryLevel < 5) { + error("battery level < 5 percent"); + // systemEvent(BATTERY ERROR) + } else if (batteryLevel < 10) { + warn("battery level < 10 percent"); + // systemEvent(BATTERY WARN) + } + } + + // flash error until errors are cleared + if (config.flashOnErrors) { + if (errors.size() > 0) { + invoke("publishFlash", "error"); + } else { + // invoke("publishFlash", "heartbeat"); + } + } + + // has processed system events + // if led report ... + if (isPeerStarted("neoPixel") && !isSpeaking) { + // FIXME - publishLedMatrix + // FIXME direct type is bad :( + NeoPixel neo = (NeoPixel) getPeer("neoPixel"); + if (neo != null) { + // neo.clearPixelSet(); + + String state = getState(); + if (state != null) { + // log.info("hashcode {} {}", state, state.hashCode()); + // base bottom blue square + String color = CodecUtils.hashcodeToHex(state.hashCode()); + log.info("hashcode {} {} {}", state, color, state.hashCode()); + neo.setPixel(134, color); + neo.setPixel(135, color); + neo.setPixel(136, color); + neo.setPixel(137, color); + } + + if (heartbeatCount % 2 == 0) { + // top heartbeat square off + neo.setPixel(132, 0, 0, 0); + neo.setPixel(133, 0, 0, 0); + neo.setPixel(138, 0, 0, 0); + neo.setPixel(139, 0, 0, 0); + } else { + // top heartbeat square on + neo.setPixel(132, 12, 180, 212); + neo.setPixel(133, 12, 180, 212); + neo.setPixel(138, 12, 180, 212); + neo.setPixel(139, 12, 180, 212); + } + + // FIXME anywhere exposing type is bad + Pir pir = (Pir) getPeer("pir"); + if (pir != null && pir.isActive()) { + neo.setPixel(130, 225, 254, 0); + neo.setPixel(131, 225, 254, 0); + neo.setPixel(140, 225, 254, 0); + neo.setPixel(141, 225, 254, 0); + } else { + neo.setPixel(130, 0, 0, 0); + neo.setPixel(131, 0, 0, 0); + neo.setPixel(140, 0, 0, 0); + neo.setPixel(141, 0, 0, 0); + + } + neo.writeMatrix(); + } + } + + } + + public void onInactivity() { + log.info("onInactivity"); + + // powerDown ? + + } + + /** + * Central hub of input motion control. Potentially, all input from joysticks, + * quest2 controllers and headset, or any IK service could be sent here + */ @Override public void onJointAngles(Map angleMap) { log.debug("onJointAngles {}", angleMap); @@ -887,7 +1187,65 @@ public void onJointAngles(Map angleMap) { public void onJoystickInput(JoystickData input) throws Exception { // TODO timer ? to test and not send an event // switches to manual control ? - invoke("publishEvent", "joystick"); + systemEvent("joystick"); + } + + /** + * Centralized logging system will have all logging from all services, + * including lower level logs that do not propegate as statuses + * + * @param log + * - flushed log from Log service + */ + public void onLogEvents(List log) { + // scan for warn or errors + for (LogEntry entry : log) { + if ("ERROR".equals(entry.level) && errors.size() < 100) { + errors.add(entry); + } + } + } + + public void onMoveHead(Map map) { + InMoov2Head head = (InMoov2Head) getPeer("head"); + if (head != null) { + head.onMove(map); + } + } + + public void onMoveLeftArm(Map map) { + InMoov2Arm leftArm = (InMoov2Arm) getPeer("leftArm"); + if (leftArm != null) { + leftArm.onMove(map); + } + } + + public void onMoveLeftHand(Map map) { + InMoov2Hand leftHand = (InMoov2Hand) getPeer("leftHand"); + if (leftHand != null) { + leftHand.onMove(map); + } + } + + public void onMoveRightArm(Map map) { + InMoov2Arm rightArm = (InMoov2Arm) getPeer("rightArm"); + if (rightArm != null) { + rightArm.onMove(map); + } + } + + public void onMoveRightHand(Map map) { + InMoov2Hand rightHand = (InMoov2Hand) getPeer("rightHand"); + if (rightHand != null) { + rightHand.onMove(map); + } + } + + public void onMoveTorso(Map map) { + InMoov2Torso torso = (InMoov2Torso) getPeer("torso"); + if (torso != null) { + torso.onMove(map); + } } public String onNewState(String state) { @@ -911,16 +1269,16 @@ public OpenCVData onOpenCVData(OpenCVData data) { // FIXME - publish event with or without data ? String file reference return data; } - + /** * onPeak volume callback TODO - maybe make it variable with volume ? * * @param volume */ public void onPeak(double volume) { - if (config.neoPixelFlashWhenSpeaking && !configStarted) { + if (config.neoPixelFlashWhenSpeaking && !"boot".equals(getState())) { if (volume > 0.5) { - invoke("publishSpeakingFlash", "speaking"); + invoke("publishSpeakingFlash", "speaking"); } } } @@ -930,14 +1288,25 @@ public void onPeak(double volume) { * onPirOn flash neopixel */ public void onPirOn() { - // FIXME flash on config.flashOnBoot - invoke("publishFlash", "pir"); - ProgramAB chatBot = (ProgramAB)getPeer("chatBot"); - if (chatBot != null) { - String botState = chatBot.getPredicate("botState"); - if ("sleeping".equals(botState)) { - invoke("publishEvent", "WAKE"); - } + isPirOn = true; + // one advantage of re-publishing from inmoov - getName parameter can be + // added + if (allowSensorInput && !"boot".equals(getState())) { + SensorData pir = new SensorData(getName(), "Pir", isPirOn); + invoke("publishSensorData", pir); + } + } + + /** + * Pir off callback - FIXME NEEDS WORK + */ + public void onPirOff() { + isPirOn = false; + // one advantage of re-publishing from inmoov - getName parameter can be + // added + if (allowSensorInput && !"boot".equals(getState())) { + SensorData pir = new SensorData(getName(), "Pir", isPirOn); + invoke("publishSensorData", pir); } } @@ -972,9 +1341,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"); + systemEvent("PIR ON"); } else { - invoke("publishEvent", "PIR OFF"); + systemEvent("PIR OFF"); } return b; } @@ -986,7 +1355,7 @@ public boolean onSense(boolean b) { */ public void onStartConfig(String configName) { log.info("onStartConfig"); - invoke("publishStartConfig", configName); + invoke("publishConfigStarted", configName); } /** @@ -998,8 +1367,6 @@ public void onStartConfig(String configName) { */ @Override public void onStarted(String name) { - InMoov2Config c = (InMoov2Config) config; - log.info("onStarted {}", name); try { @@ -1008,7 +1375,7 @@ public void onStarted(String name) { // BAD IDEA - better to ask for a system report or an error report // if (runtime.isProcessingConfig()) { - // invoke("publishEvent", "CONFIG STARTED"); + // systemEvent("CONFIG STARTED"); // } String peerKey = getPeerKey(name); @@ -1018,11 +1385,11 @@ public void onStarted(String name) { } if (runtime.isProcessingConfig() && !configStarted) { - invoke("publishEvent", "CONFIG STARTED " + runtime.getConfigName()); + systemEvent("CONFIG STARTED " + runtime.getConfigName()); configStarted = true; } - invoke("publishEvent", "STARTED " + peerKey); + systemEvent("STARTED " + peerKey); switch (peerKey) { case "audioPlayer": @@ -1127,6 +1494,7 @@ public void onStarted(String name) { @Override public void onStopped(String name) { + log.info("service {} has stopped"); // using release peer for peer releasing // FIXME - auto remove subscriptions of peers? } @@ -1143,6 +1511,9 @@ public void onText(String text) { // TODO FIX/CHECK this, migrate from python land public void powerDown() { + // publishFlash(maxInactivityTimeSeconds, maxInactivityTimeSeconds, + // maxInactivityTimeSeconds, maxInactivityTimeSeconds, + // maxInactivityTimeSeconds, maxInactivityTimeSeconds) rest(); purgeTasks(); @@ -1185,15 +1556,209 @@ public void publish(String name, String method, Object... data) { invoke("publishMessage", msg); } - public String publishStartConfig(String configName) { + public String publishConfigStarted(String configName) { info("config %s started", configName); - invoke("publishEvent", "CONFIG STARTED " + configName); + systemEvent("CONFIG STARTED " + configName); return configName; } - public String publishFinishedConfig(String configName) { + public double publishBatteryLevel(double d) { + return d; + } + + /** + * At boot all services specified through configuration have started, or if no + * configuration has started minimally the InMoov2 service has started. During + * the processing of config and starting other services data will have + * accumulated, and at boot, some of data may now be inspected and processed + * in a synchronous single threaded way. With reporting after startup, vs + * during, other peer services are not needed (e.g. audioPlayer is no longer + * needed to be started "before" InMoov2 because when boot is called + * everything that is wanted has been started. + * + */ + public void boot() { + try { + // if ! ready + // FIXME don't overwrite if exists + Runtime runtime = Runtime.getInstance(); + runtime.saveDefault("InMoovDefault", getName(), "InMoov2", true); + + bootCount++; + log.info("boot count {}", bootCount); + + // thinking you shouldn't "boot" twice ? + if (hasBooted) { + log.warn("will not boot again"); + return; + } + + if (runtime.isProcessingConfig()) { + log.warn("runtime still processing config set {}, waiting ....", runtime.getConfigName()); + return; + } + + ServiceInterface python = Runtime.getService("python"); + if (python == null || !python.isReady()) { + log.warn("python not ready, waiting ...."); + return; + } + + // TODO - MAKE BOOT REPORT !!!! deliver it on a heartbeat + + // get service start and release life cycle events + // FIXME reduce to onStart + runtime.attachServiceLifeCycleListener(getName()); + + List services = Runtime.getServices(); + for (ServiceInterface si : services) { + if ("Servo".equals(si.getSimpleName())) { + send(si.getFullName(), "setAutoDisable", true); + } + } + + // get events of new services and shutdown + subscribe("runtime", "shutdown"); + // power up loopback subscription + addListener(getName(), "powerUp"); + + subscribe("runtime", "publishConfigList"); + if (runtime.isProcessingConfig()) { + systemEvent("configStarted"); + } + + // subscribe to config processing events + // runtime callbacks publish the same a local + // subscribe("runtime", "publishConfigStarted", "publishConfigStarted"); + subscribe("runtime", "publishConfigFinished", "publishConfigFinished"); + + // chatbot getresponse attached to publishEvent + addListener("publishEvent", getPeerName("chatBot"), "getResponse"); + + // copy config if it doesn't already exist + String resourceBotDir = FileIO.gluePaths(getResourceDir(), "config"); + List files = FileIO.getFileList(resourceBotDir); + for (File f : files) { + String botDir = "data/config/" + f.getName(); + File bDir = new File(botDir); + if (bDir.exists() || !f.isDirectory()) { + log.info("skipping data/config/{}", botDir); + } else { + log.info("will copy new data/config/{}", botDir); + try { + FileIO.copy(f.getAbsolutePath(), botDir); + } catch (Exception e) { + error(e); + } + } + } + runtime.invoke("publishConfigList"); + // FIXME - reduce the number of these + if (config.loadAppsScripts) { + loadAppsScripts(); + } + + if (config.loadInitScripts) { + loadInitScripts(); + } + + if (config.loadGestures) { + loadGestures(); + } + + execScript("InMoov2.py"); + log.info("here"); + + for (ServiceInterface si : services) { + if ("Servo".equals(si.getSimpleName())) { + send(si.getFullName(), "setAutoDisable", true); + } + } + + // FIXME - any interesting state changes or config stuff + // needs to be reported (once?) in onHeartbeat + + // FIXME - standardize multi-config examples should be available + // moved from startService to allow more simple control + // FIXME standard FileIO copyIfNotExists(src, dst) + // try { + // // copy config if it doesn't already exist + // String resourceBotDir = FileIO.gluePaths(getResourceDir(), "config"); + // List files = FileIO.getFileList(resourceBotDir); + // for (File f : files) { + // String botDir = "data/config/" + f.getName(); + // File bDir = new File(botDir); + // if (bDir.exists() || !f.isDirectory()) { + // log.info("skipping data/config/{}", botDir); + // } else { + // log.info("will copy new data/config/{}", botDir); + // try { + // FileIO.copy(f.getAbsolutePath(), botDir); + // } catch (Exception e) { + // error(e); + // } + // } + // } + // } catch (Exception e) { + // error(e); + // } + + // FIXME - find good way of running an animation "through" a state + if (config.neoPixelBootGreen && getPeer("neoPixel") != null) { + NeoPixel neoPixel = (NeoPixel) getPeer("neoPixel"); + if (neoPixel != null) { + // invoke("publishPlayAnimation", config.bootAnimation); + } + } + + if (config.startupSound && getPeer("audioPlayer") != null) { + ((AudioFile) getPeer("audioPlayer")).playBlocking(FileIO.gluePaths(getResourceDir(), "/system/sounds/startupsound.mp3")); + } + + if (config.systemEventsOnBoot) { + // reporting on all services and config started + if (bootedConfig != null) { + // configuration was processed before booting + systemEvent("CONFIG LOADED %s", bootedConfig); + } + } + + // FIXME - important to do invoke & fsm needs to be consistent order + + // if speaking then turn off animation + + // publish all the errors + + // switch off animations + + // start heartbeat + // say starting heartbeat + if (config.heartbeat) { + startHeartbeat(); + } else { + stopHeartbeat(); + } + + // say finished booting + fire("start"); + + // if (getPeer("mouth") != null) { + // AbstractSpeechSynthesis mouth = + // (AbstractSpeechSynthesis)getPeer("mouth"); + // mouth.setMute(wasMute); + // } + } catch (Exception e) { + hasBooted = false; + error(e); + } + + hasBooted = true; + + } + + public String publishConfigFinished(String configName) { info("config %s finished", configName); - invoke("publishEvent", "CONFIG LOADED " + configName); + systemEvent("CONFIG LOADED " + configName); return configName; } @@ -1208,6 +1773,28 @@ public List publishConfigList() { return configList; } + /** + * publishes a name of an animation, off/on control will be done through + * AudioListener interface + * + * @param name + * @return + */ + public String publishAnimation(String name) { + return name; + } + + /** + * publishes a name for NeoPixel.onFlash to consume, in a seperate channel to + * potentially be used by "speaking only" leds + * + * @param name + * @return + */ + public String publishSpeakingFlash(String name) { + return name; + } + /** * event publisher for the fsm - although other services potentially can * consume and filter this event channel @@ -1220,119 +1807,138 @@ public String publishEvent(String event) { } /** - * used to configure a flashing event - could use configuration to signal - * different colors and states + * publishes a name for NeoPixel.onFlash to consume * + * @param botType * @return */ public String publishFlash(String flashName) { return flashName; } + /** + * FSM is regularly driven by the heartbeat + * + * @return + */ public String publishHeartbeat() { - invoke("publishFlash", "heartbeat"); return getName(); } /** - * A more extensible interface point than publishEvent FIXME - create - * interface for this + *
+   * 
+   * Typically rebroadcast message from ProgramAB mrljson in aiml
+   * 
+   * The oob syntax is:
+   *  <oob>
+   *    <mrljson>
+   *        [{method:on_new_user, data:[{"name":"<star/>"}]}]
+   *    </mrljson>
+   * </oob>
+   * 
+   * 
* * @param msg * @return */ public Message publishMessage(Message msg) { + msg.sender = getName(); return msg; } - public HashMap publishMoveArm(String which, Double bicep, Double rotate, Double shoulder, Double omoplate) { - HashMap map = new HashMap<>(); - map.put("bicep", bicep); - map.put("rotate", rotate); - map.put("shoulder", shoulder); - map.put("omoplate", omoplate); - if ("left".equals(which)) { - invoke("publishMoveLeftArm", bicep, rotate, shoulder, omoplate); - } else { - invoke("publishMoveRightArm", bicep, rotate, shoulder, omoplate); - } + public Map publishMoveHead(Map map) { return map; } - public HashMap publishMoveHand(String which, Double thumb, Double index, Double majeure, Double ringFinger, Double pinky, Double wrist) { - HashMap map = new HashMap<>(); - map.put("which", which); - map.put("thumb", thumb); - map.put("index", index); - map.put("majeure", majeure); - map.put("ringFinger", ringFinger); - map.put("pinky", pinky); - map.put("wrist", wrist); - if ("left".equals(which)) { - invoke("publishMoveLeftHand", thumb, index, majeure, ringFinger, pinky, wrist); - } else { - invoke("publishMoveRightHand", thumb, index, majeure, ringFinger, pinky, wrist); - } + public Map publishMoveLeftArm(Map map) { return map; } - public HashMap publishMoveHead(Double neck, Double rothead, Double eyeX, Double eyeY, Double jaw, Double rollNeck) { - HashMap map = new HashMap<>(); - map.put("neck", neck); - map.put("rothead", rothead); - map.put("eyeX", eyeX); - map.put("eyeY", eyeY); - map.put("jaw", jaw); - map.put("rollNeck", rollNeck); + public Map publishMoveLeftHand(Map map) { return map; } - public HashMap publishMoveLeftArm(Double bicep, Double rotate, Double shoulder, Double omoplate) { - HashMap map = new HashMap<>(); - map.put("bicep", bicep); - map.put("rotate", rotate); - map.put("shoulder", shoulder); - map.put("omoplate", omoplate); + public Map publishMoveRightArm(Map map) { return map; } - public HashMap publishMoveLeftHand(Double thumb, Double index, Double majeure, Double ringFinger, Double pinky, Double wrist) { - HashMap map = new HashMap<>(); - map.put("thumb", thumb); - map.put("index", index); - map.put("majeure", majeure); - map.put("ringFinger", ringFinger); - map.put("pinky", pinky); - map.put("wrist", wrist); + public Map publishMoveRightHand(Map map) { return map; } - public HashMap publishMoveRightArm(Double bicep, Double rotate, Double shoulder, Double omoplate) { - HashMap map = new HashMap<>(); - map.put("bicep", bicep); - map.put("rotate", rotate); - map.put("shoulder", shoulder); - map.put("omoplate", omoplate); + public Map publishMoveTorso(Map map) { return map; } - public HashMap publishMoveRightHand(Double thumb, Double index, Double majeure, Double ringFinger, Double pinky, Double wrist) { - HashMap map = new HashMap<>(); - map.put("thumb", thumb); - map.put("index", index); - map.put("majeure", majeure); - map.put("ringFinger", ringFinger); - map.put("pinky", pinky); - map.put("wrist", wrist); - return map; + public String publishPlayAudioFile(String filename) { + return filename; } - public HashMap publishMoveTorso(Double topStom, Double midStom, Double lowStom) { - HashMap map = new HashMap<>(); - map.put("topStom", topStom); - map.put("midStom", midStom); - map.put("lowStom", lowStom); - return map; + public String publishPlayAnimation(String animation) { + return animation; + } + + /** + * stop animation event + */ + public void publishStopAnimation() { + } + + /** + * initial state - updated on any state change + */ + String state = "boot"; + + public class InMoov2State { + public long ts = System.currentTimeMillis(); + public String src; + public String state; + public String event; + } + + /** + * The integration between the FiniteStateMachine (fsm) and the InMoov2 + * service and potentially other services (Python, ProgramAB) happens here. + * + * After boot all state changes get published here. + * + * Some InMoov2 service methods will be called here for "default + * implemenation" of states. If a user doesn't want to have that default + * implementation, they can change it by changing the definition of the state + * machine, and have a new state which will call a Python inmoov2 library + * callback. Overriding, appending, or completely transforming the behavior is + * all easily accomplished by managing the fsm and python inmoov2 library + * callbacks. + * + * Python inmoov2 callbacks ProgramAB topic switching + * + * Depending on config: + * + * + * @param stateChange + * @return + */ + public InMoov2State publishStateChange(FiniteStateMachine.StateChange stateChange) { + log.info("publishStateChange {}", stateChange); + InMoov2State state = new InMoov2State(); + state.src = getName(); + state.state = stateChange.state; + state.event = stateChange.event; + return state; + } + + /** + * event publisher for the fsm - although other services potentially can + * consume and filter this event channel + * + * @param event + * @return + */ + public String publishSystemEvent(String event) { + // well, it turned out underscore was a goofy selection, as underscore in + // aiml is wildcard ... duh + return String.format("SYSTEM_EVENT %s", event); } /** @@ -1343,11 +1949,21 @@ public String publishText(String text) { return text; } + /** + * default this will come from idle after some configurable time period + */ + public void random() { + Random random = (Random) getPeer("random"); + if (random != null) { + random.enable(); + } + } + @Override public void releasePeer(String peerKey) { super.releasePeer(peerKey); if (peerKey != null) { - invoke("publishEvent", "STOPPED " + peerKey); + systemEvent("STOPPED " + peerKey); } } @@ -1520,7 +2136,7 @@ public void setOpenCV(OpenCV opencv) { } public boolean setPirPlaySounds(boolean b) { - getTypedConfig().pirPlaySounds = b; + config.pirPlaySounds = b; return b; } @@ -1575,11 +2191,6 @@ public void setVoice(String name) { } } - // ----------------------------------------------------------------------------- - // These are methods added that were in InMoov1 that we no longer had in - // InMoov2. - // From original InMoov1 so we don't loose the - public void sleeping() { log.error("sleeping"); } @@ -1589,7 +2200,7 @@ public void speak(String toSpeak) { } public void speakAlert(String toSpeak) { - invoke("publishEvent", "ALERT"); + systemEvent("ALERT"); speakBlocking(toSpeak); } @@ -1626,91 +2237,7 @@ public void speakBlocking(String format, Object... args) { } } - public void startAll() throws Exception { - startAll(null, null); - } - - public void startAll(String leftPort, String rightPort) throws Exception { - startMouth(); - startChatBot(); - - // startHeadTracking(); - // startEyesTracking(); - // startOpenCV(); - startEar(); - - startServos(); - // startMouthControl(head.jaw, mouth); - - speakBlocking(get("STARTINGSEQUENCE")); - } - - public void startBrain() { - startChatBot(); - } - - public ProgramAB startChatBot() { - - try { - chatBot = (ProgramAB) startPeer("chatBot"); - - if (locale != null) { - chatBot.setCurrentBotName(locale.getTag()); - } - - // FIXME remove get en.properties stuff - speakBlocking(get("CHATBOTACTIVATED")); - - chatBot.attachTextPublisher(ear); - - // this.attach(chatBot); FIXME - attach as a TextPublisher - then - // re-publish - // FIXME - deal with language - // speakBlocking(get("CHATBOTACTIVATED")); - chatBot.repetitionCount(10); - // chatBot.setPath(getResourceDir() + fs + "chatbot"); - // chatBot.setPath(getDataDir() + "ProgramAB"); - chatBot.startSession("default", locale.getTag()); - // reset some parameters to default... - chatBot.setPredicate("topic", "default"); - chatBot.setPredicate("questionfirstinit", ""); - chatBot.setPredicate("tmpname", ""); - chatBot.setPredicate("null", ""); - // load last user session - if (!chatBot.getPredicate("name").isEmpty()) { - if (chatBot.getPredicate("lastUsername").isEmpty() || chatBot.getPredicate("lastUsername").equals("unknown") || chatBot.getPredicate("lastUsername").equals("default")) { - chatBot.setPredicate("lastUsername", chatBot.getPredicate("name")); - } - } - chatBot.setPredicate("parameterHowDoYouDo", ""); - chatBot.savePredicates(); - htmlFilter = (HtmlFilter) startPeer("htmlFilter");// Runtime.start("htmlFilter", - // "HtmlFilter"); - chatBot.attachTextListener(htmlFilter); - htmlFilter.attachTextListener((TextListener) getPeer("mouth")); - chatBot.attachTextListener(this); - // start session based on last recognized person - // if (!chatBot.getPredicate("default", "lastUsername").isEmpty() && - // !chatBot.getPredicate("default", "lastUsername").equals("unknown")) { - // chatBot.startSession(chatBot.getPredicate("lastUsername")); - // } - 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"); - } else { - chatBot.startSession(chatBot.getPredicate("default", "lastUsername")); - invoke("publishEvent", "WAKE UP"); - } - } catch (Exception e) { - speak("could not load chatBot"); - error(e.getMessage()); - speak(e.getMessage()); - } - broadcastState(); - return chatBot; - } - + @Deprecated /* this needs to be removed - runtime and config handle this */ public SpeechRecognizer startEar() { ear = (SpeechRecognizer) startPeer("ear"); @@ -1734,8 +2261,57 @@ public void startedGesture(String nameOfGesture) { } } + public class Heart implements Runnable { + private final ReentrantLock lock = new ReentrantLock(); + private Thread thread; + + @Override + public void run() { + if (lock.tryLock()) { + try { + while (!Thread.currentThread().isInterrupted()) { + invoke("publishHeartbeat"); + Thread.sleep(config.heartbeatInterval); + } + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + } finally { + lock.unlock(); + log.info("heart stopping"); + thread = null; + } + } + } + + public void stop() { + if (thread != null) { + thread.interrupt(); + config.heartbeat = false; + } else { + log.info("heart already stopped"); + } + } + + public void start() { + if (thread == null) { + log.info("starting heart"); + thread = new Thread(this, String.format("%s-heart", getName())); + thread.start(); + config.heartbeat = true; + } else { + log.info("heart already started"); + } + } + } + + private transient final Heart heart = new Heart(); + + protected boolean heartBeating = false; + + protected boolean isSpeaking = false; + public void startHeartbeat() { - addTask(1000, "publishHeartbeat"); + heart.start(); } // TODO - general objective "might" be to reduce peers down to something @@ -1796,59 +2372,19 @@ public ServiceInterface startPeer(String peer) { @Override public void startService() { - super.startService(); - - InMoov2Config c = (InMoov2Config) config; - Runtime runtime = Runtime.getInstance(); - - // get service start and release life cycle events - runtime.attachServiceLifeCycleListener(getName()); - - List services = Runtime.getServices(); - for (ServiceInterface si : services) { - if ("Servo".equals(si.getSimpleName())) { - send(si.getFullName(), "setAutoDisable", true); - } - } - // get events of new services and shutdown - subscribe("runtime", "shutdown"); - // power up loopback subscription - addListener(getName(), "powerUp"); + try { - subscribe("runtime", "publishConfigList"); - if (runtime.isProcessingConfig()) { - invoke("publishEvent", "configStarted"); - } - subscribe("runtime", "publishStartConfig"); - subscribe("runtime", "publishFinishedConfig"); + super.startService(); - // chatbot getresponse attached to publishEvent - addListener("publishEvent", getPeerName("chatBot"), "getResponse"); + // core part of InMoov + fsm = (FiniteStateMachine) startPeer("fsm"); + fsm.init(); - try { - // copy config if it doesn't already exist - String resourceBotDir = FileIO.gluePaths(getResourceDir(), "config"); - List files = FileIO.getFileList(resourceBotDir); - for (File f : files) { - String botDir = "data/config/" + f.getName(); - File bDir = new File(botDir); - if (bDir.exists() || !f.isDirectory()) { - log.info("skipping data/config/{}", botDir); - } else { - log.info("will copy new data/config/{}", botDir); - try { - FileIO.copy(f.getAbsolutePath(), botDir); - } catch (Exception e) { - error(e); - } - } - } } catch (Exception e) { error(e); } - runtime.invoke("publishConfigList"); } public void startServos() { @@ -1876,12 +2412,13 @@ public void stop() { } public void stopGesture() { + // FIXME cannot be casting to Python Python p = (Python) Runtime.getService("python"); p.stop(); } public void stopHeartbeat() { - purgeTask("publishHeartbeat"); + heart.stop(); } public void stopNeopixelAnimation() { @@ -1908,7 +2445,21 @@ public void systemCheck() { Platform platform = Runtime.getPlatform(); setPredicate("system version", platform.getVersion()); // ERROR buffer !!! - invoke("publishEvent", "systemCheckFinished"); + systemEvent("systemCheckFinished"); + } + + public String systemEvent(String format, Object... args) { + if (format == null) { + return null; + } + String event = null; + if (args == null) { + event = format; + } else { + event = String.format(format, args); + } + invoke("publishEvent", event); + return event; } // FIXME - if this is really desired it will drive local references for all @@ -1922,44 +2473,121 @@ public void waitTargetPos() { sendToPeer("leftArm", "waitTargetPos"); sendToPeer("torso", "waitTargetPos"); } - + public boolean setSpeechType(String speechType) { - + if (speechType == null) { error("cannot change speech type to null"); return false; } - + if (!speechType.contains(".")) { speechType = "org.myrobotlab.service." + speechType; } - + Runtime runtime = Runtime.getInstance(); String peerName = getName() + ".mouth"; Plan plan = runtime.getDefault(peerName, speechType); try { - SpeechSynthesisConfig mouth = (SpeechSynthesisConfig)plan.get(peerName); + SpeechSynthesisConfig mouth = (SpeechSynthesisConfig) plan.get(peerName); mouth.speechRecognizers = new String[] { getName() + ".ear" }; savePeerConfig("mouth", plan.get(peerName)); - + if (isPeerStarted("mouth")) { - // restart + // restart releasePeer("mouth"); startPeer("mouth"); } - - } catch(Exception e) { + + } catch (Exception e) { error("could not create config for %s", speechType); return false; } - + return true; - + // updatePeerType("mouth" /* getPeerName("mouth") */, speechType); // return speechType; } + public void closeRightHand() { + + // if InMoov2Hand.close/open is used directly + // it prevents user's interception of the data + // and forces InMoov2Hand type to be used :( + // pub/sub is the way + + // hardcoded, but if necessary can be put in config + HashMap map = new HashMap<>(); + map.put("thumb", 130.0); + map.put("index", 180.0); + map.put("majeure", 180.0); + map.put("ringFinger", 180.0); + map.put("pinky", 180.0); + invoke("publishMoveRightHand", map); + + } + + public void openRightHand() { + // if InMoov2Hand.close/open is used directly + // it prevents user's interception of the data + // and forces InMoov2Hand type to be used :( + // pub/sub is the way + + // hardcoded, but if necessary can be put in config + HashMap map = new HashMap<>(); + map.put("thumb", 0.0); + map.put("index", 0.0); + map.put("majeure", 0.0); + map.put("ringFinger", 0.0); + map.put("pinky", 0.0); + invoke("publishMoveRightHand", map); + } + + public void closeLeftHand() { + + // if InMoov2Hand.close/open is used directly + // it prevents user's interception of the data + // and forces InMoov2Hand type to be used :( + // pub/sub is the way + + // hardcoded, but if necessary can be put in config + HashMap map = new HashMap<>(); + map.put("thumb", 130.0); + map.put("index", 180.0); + map.put("majeure", 180.0); + map.put("ringFinger", 180.0); + map.put("pinky", 180.0); + invoke("publishMoveLeftHand", map); + + } + + public void openLeftHand() { + // if InMoov2Hand.close/open is used directly + // it prevents user's interception of the data + // and forces InMoov2Hand type to be used :( + // pub/sub is the way + + // hardcoded, but if necessary can be put in config + HashMap map = new HashMap<>(); + map.put("thumb", 0.0); + map.put("index", 0.0); + map.put("majeure", 0.0); + map.put("ringFinger", 0.0); + map.put("pinky", 0.0); + invoke("publishMoveLeftHand", map); + } + + public void openHands() { + openLeftHand(); + openRightHand(); + } + + public void closeHands() { + closeLeftHand(); + closeRightHand(); + } public static void main(String[] args) { try { @@ -1968,32 +2596,32 @@ public static void main(String[] args) { // Platform.setVirtual(true); // Runtime.start("s01", "Servo"); // Runtime.start("intro", "Intro"); - + Runtime runtime = Runtime.getInstance(); + runtime.saveDefault("default-i01", "i01", "InMoov2", true); Runtime.startConfig("dev"); - + + boolean done = true; + if (done) { + return; + } + WebGui webgui = (WebGui) Runtime.create("webgui", "WebGui"); // webgui.setSsl(true); webgui.autoStartBrowser(false); // webgui.setPort(8888); webgui.startService(); - InMoov2 i01 = (InMoov2)Runtime.start("i01","InMoov2"); - - boolean done = true; - if (done) { - return; - } - + InMoov2 i01 = (InMoov2) Runtime.start("i01", "InMoov2"); - OpenCVConfig ocvConfig = i01.getPeerConfig("opencv", new StaticType<>() {}); + OpenCVConfig ocvConfig = i01.getPeerConfig("opencv", new StaticType<>() { + }); ocvConfig.flip = true; i01.setPeerConfigValue("opencv", "flip", true); // i01.savePeerConfig("", null); - - // Runtime.startConfig("default"); - - // Runtime.main(new String[] { "--log-level", "info", "-s", "webgui", "WebGui", "intro", "Intro", "python", "Python" }); + // Runtime.startConfig("default"); + // Runtime.main(new String[] { "--log-level", "info", "-s", "webgui", + // "WebGui", "intro", "Intro", "python", "Python" }); Runtime.start("python", "Python"); // Runtime.start("ros", "Ros"); @@ -2055,5 +2683,36 @@ public static void main(String[] args) { } } + // FIXME - rebroadcast these + + @Override + public void onStartSpeaking(String utterance) { + isSpeaking = true; + } + + @Override + public void onEndSpeaking(String utterance) { + isSpeaking = false; + } + + /** + * Rebroadcasted topic change with source changed to this robot, python will + * consume it. + * + * @param topicChange + * @return the topic change + */ + public Event publishTopic(Event topicChange) { + topicChange.src = getName(); + return topicChange; + } + + // predicate change ? rebroadcasted ? + // FIXME if it went chatBot.publishSession -> InMoov2.onSession (if getState() + // != boot) InMoov2.publishSession + public Event publishSession(Event newSession) { + newSession.src = getName(); + return newSession; + } } diff --git a/src/main/java/org/myrobotlab/service/IntegratedMovement.java b/src/main/java/org/myrobotlab/service/IntegratedMovement.java deleted file mode 100644 index cdbb59ef36..0000000000 --- a/src/main/java/org/myrobotlab/service/IntegratedMovement.java +++ /dev/null @@ -1,883 +0,0 @@ -package org.myrobotlab.service; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.myrobotlab.framework.Service; -import org.myrobotlab.genetic.GeneticParameters; -import org.myrobotlab.jme3.IntegratedMovementInterface; -import org.myrobotlab.kinematics.CollisionDectection; -import org.myrobotlab.kinematics.CollisionItem; -import org.myrobotlab.kinematics.DHLink; -import org.myrobotlab.kinematics.DHLinkType; -import org.myrobotlab.kinematics.DHRobotArm; -import org.myrobotlab.kinematics.GravityCenter; -import org.myrobotlab.kinematics.IMEngine; -import org.myrobotlab.kinematics.Map3D; -import org.myrobotlab.kinematics.Map3DPoint; -import org.myrobotlab.kinematics.Matrix; -import org.myrobotlab.kinematics.Point; -import org.myrobotlab.kinematics.PositionData; -import org.myrobotlab.kinematics.TestJmeIMModel; -import org.myrobotlab.logging.Level; -import org.myrobotlab.logging.LoggerFactory; -import org.myrobotlab.logging.LoggingFactory; -import org.myrobotlab.math.MapperLinear; -import org.myrobotlab.math.MathUtils; -import org.myrobotlab.math.interfaces.Mapper; -import org.myrobotlab.openni.OpenNiData; -import org.myrobotlab.service.config.ServiceConfig; -import org.myrobotlab.service.interfaces.IKJointAnglePublisher; -import org.myrobotlab.service.interfaces.ServoControl; -import org.myrobotlab.service.interfaces.ServoEvent; -import org.slf4j.Logger; - -import com.jme3.math.Vector3f; -import com.jme3.system.AppSettings; - -/** - * - * IntegratedMovement - This class provides a 3D based inverse kinematics - * implementation that allows you to specify the robot arm geometry based on DH - * Parameters. The work is based on InversedKinematics3D by kwatters with - * different computation and goal, including collision detection and - * moveToObject - * - * Rotation and Orientation information is not currently supported. (but should - * be easy to add) - * - * @author Christian/Calamity - * - */ -public class IntegratedMovement extends Service implements IKJointAnglePublisher { - - private static final long serialVersionUID = 1L; - public final static Logger log = LoggerFactory.getLogger(IntegratedMovement.class); - - // private HashMap arms = new HashMap(); - private transient HashMap engines = new HashMap(); - - private Matrix inputMatrix = null; - - public transient CollisionDectection collisionItems = new CollisionDectection(); - - transient GeneticParameters geneticParameters = new GeneticParameters(); - - public enum ObjectPointLocation { - ORIGIN_CENTER(0x01, "Center Origin"), ORIGIN_SIDE(0x02, "Side Origin"), END_SIDE(0x04, "Side End"), END_CENTER(0x05, "Center End"), CLOSEST_POINT(0x06, - "Closest Point"), CENTER(0x07, "Center"), CENTER_SIDE(0x08, "Side Center"), LEFT_SIDE(0x09, "Left Side"), RIGHT_SIDE(0x0A, "Right Side"); - - public int value; - public String location; - - private ObjectPointLocation(int value, String location) { - this.value = value; - this.location = location; - } - } - - public enum Ai { - HOLD_POSITION(0x01, "Hold Position"), AVOID_COLLISION(0x02, "Avoid Collision"), KEEP_BALANCE(0x04, "Keep Balance"); - - public int value; - public String text; - - private Ai(int value, String text) { - this.value = value; - this.text = text; - } - } - - private transient OpenNi openni = null; - - /** - * @return the openni - */ - public OpenNi getOpenni() { - return openni; - } - - /** - * @param openni - * the openni to set - */ - public void setOpenni(OpenNi openni) { - this.openni = openni; - } - - private transient Map3D map3d = new Map3D(); - private String kinectName = "kinect"; - private boolean ProcessKinectData = false; - - private transient IntegratedMovementInterface jmeApp = null; - - private HashMap maps = new HashMap<>(); - public transient GravityCenter cog = new GravityCenter(this); - - /** - * @return the jmeApp - */ - public IntegratedMovementInterface getJmeApp() { - return jmeApp; - } - - public IntegratedMovement(String n, String id) { - super(n, id); - } - - public Point currentPosition(String arm) { - if (engines.containsKey(arm)) { - return getArm(arm).getPalmPosition(); - } - log.info("IK service have no data for {}", arm); - return new Point(0, 0, 0, 0, 0, 0); - } - - public void moveTo(String arm, double x, double y, double z) { - moveTo(arm, x, y, z, null); - } - - public void moveTo(String arm, double x, double y, double z, String lastDHLink) { - moveTo(arm, new Point(x, y, z, 0, 0, 0), lastDHLink); - } - - public void moveTo(String arm, Point point) { - moveTo(arm, point, null); - } - - public void moveTo(String arm, Point point, String lastDHLink) { - if (engines.containsKey(arm)) { - engines.get(arm).moveTo(point, lastDHLink); - } else { - log.info("unknow arm {}", arm); - } - jmeApp.addPoint(point); - } - - /** - * This create a rotation and translation matrix that will be applied on the - * "moveTo" call. - * - * @param dx - * - x axis translation - * @param dy - * - y axis translation - * @param dz - * - z axis translation - * @param roll - * - rotation about z (in degrees) - * @param pitch - * - rotation about x (in degrees) - * @param yaw - * - rotation about y (in degrees) - * @return the matrix - */ - public Matrix createInputMatrix(double dx, double dy, double dz, double roll, double pitch, double yaw) { - roll = MathUtils.degToRad(roll); - pitch = MathUtils.degToRad(pitch); - yaw = MathUtils.degToRad(yaw); - Matrix trMatrix = Matrix.translation(dx, dy, dz); - Matrix rotMatrix = Matrix.zRotation(roll).multiply(Matrix.yRotation(yaw).multiply(Matrix.xRotation(pitch))); - inputMatrix = trMatrix.multiply(rotMatrix); - for (IMEngine engine : engines.values()) { - engine.setInputMatrix(inputMatrix); - } - return inputMatrix; - } - - public double[][] createJointPositionMap(String arm) { - return engines.get(arm).createJointPositionMap(); - // return createJointPositionMap(getArm(arm)); - } - - public DHRobotArm getArm(String arm) { - if (engines.containsKey(arm)) { - return engines.get(arm).getDHRobotArm(); - } else { - log.error("Unknow DHRobotArm {}", arm); - DHRobotArm newArm = new DHRobotArm(); - newArm.name = arm; - return newArm; - } - } - - public void addArm(String name, DHRobotArm currentArm) { - IMEngine newEngine = new IMEngine(name, currentArm, this); - engines.put(name, newEngine); - } - - public static void main(String[] args) throws Exception { - LoggingFactory.init(Level.INFO); - - Runtime.createAndStart("python", "Python"); - Runtime.createAndStart("gui", "SwingGui"); - IntegratedMovement ik = (IntegratedMovement) Runtime.start("ik", "IntegratedMovement"); - Arduino arduino = (Arduino) Runtime.start("arduino", "Arduino"); - arduino.connect("COM22"); - arduino.setDebug(true); - // define and attach servo - // map is set so servo accept angle as input, output where - // they need to go so that their part they where attach to - // move by the input degree - Servo mtorso = (Servo) Runtime.start("mtorso", "Servo"); - mtorso.setPin(26); - mtorso.setPosition(90); - mtorso.attach(arduino); - mtorso.map(15.0, 165.0, 148.0, 38.0); - // mtorso.map(89.9,90.1,93.1,92.9); - mtorso.setRest(90.0); - // #mtorso.setMinMax(35,150); - mtorso.setVelocity(13.0); - mtorso.moveTo(90.0); - Servo ttorso = (Servo) Runtime.start("ttorso", "Servo"); - ttorso.setPin(7); - ttorso.setPosition(90); - ttorso.attach(arduino); - ttorso.map(80.0, 100.0, 92.0, 118.0); - // ttorso.setInverted(False) - // #ttorso.setMinMax(85,125) - ttorso.setVelocity(13.0); - ttorso.moveTo(90.0); - Servo omoplate = (Servo) Runtime.start("omoplate", "Servo"); - omoplate.setPin(11); - omoplate.setPosition(10); - omoplate.attach(arduino); - omoplate.map(10.0, 70.0, 10.0, 70.0); - omoplate.setVelocity(15.0); - // #omoplate.setMinMax(10,70) - omoplate.moveTo(10.0); - Servo Romoplate = (Servo) Runtime.start("Romoplate", "Servo"); - Romoplate.setPin(31); - Romoplate.setPosition(10); - Romoplate.attach(arduino); - Romoplate.map(10.0, 70.0, 10.0, 70.0); - Romoplate.setVelocity(15.0); - // #omoplate.setMinMax(10,70) - Romoplate.moveTo(10.0); - Servo shoulder = (Servo) Runtime.start("shoulder", "Servo"); - shoulder.setPin(26); - shoulder.setPosition(30); - shoulder.attach(arduino); - shoulder.map(0.0, 180.0, 0.0, 180.0); - // #shoulder.setMinMax(0,180) - shoulder.setVelocity(14.0); - shoulder.moveTo(30.0); - Servo Rshoulder = (Servo) Runtime.start("Rshoulder", "Servo"); - Rshoulder.setPin(6); - Rshoulder.setPosition(30); - Rshoulder.attach(arduino); - Rshoulder.map(0.0, 180.0, 0.0, 180.0); - // #shoulder.setMinMax(0,180) - Rshoulder.setVelocity(14.0); - Rshoulder.moveTo(30.0); - Servo rotate = (Servo) Runtime.start("rotate", "Servo"); - rotate.setPin(9); - rotate.setPosition(90); - rotate.attach(arduino); - rotate.map(46.0, 160.0, 46.0, 160.0); - // #rotate.setMinMax(46,180) - rotate.setVelocity(18.0); - rotate.moveTo(90.0); - Servo Rrotate = (Servo) Runtime.start("Rrotate", "Servo"); - Rrotate.setPin(29); - Rrotate.setPosition(90); - Rrotate.attach(arduino); - Rrotate.map(46.0, 160.0, 46.0, 160.0); - // #rotate.setMinMax(46,180) - Rrotate.setVelocity(18.0); - Rrotate.moveTo(90.0); - Servo bicep = (Servo) Runtime.start("bicep", "Servo"); - bicep.setPin(8); - bicep.setPosition(10); - bicep.attach(arduino); - bicep.map(5.0, 60.0, 5.0, 80.0); - bicep.setVelocity(26.0); - // #bicep.setMinMax(5,90) - bicep.moveTo(10.0); - Servo Rbicep = (Servo) Runtime.start("Rbicep", "Servo"); - Rbicep.setPin(28); - Rbicep.setPosition(10); - Rbicep.attach(arduino); - Rbicep.map(5.0, 60.0, 5.0, 80.0); - Rbicep.setVelocity(26.0); - // #bicep.setMinMax(5,90) - Rbicep.moveTo(10.0); - Servo wrist = (Servo) Runtime.start("wrist", "Servo"); - wrist.setPin(7); - wrist.setPosition(90); - wrist.attach(arduino); - // #wrist.map(45,135,45,135) - wrist.map(0.0, 180.0, 0.0, 180.0); - wrist.setVelocity(26.0); - // #bicep.setMinMax(5,90) - wrist.moveTo(90.0); - Servo Rwrist = (Servo) Runtime.start("Rwrist", "Servo"); - Rwrist.setPin(27); - Rwrist.setPosition(90); - Rwrist.attach(arduino); - // #wrist.map(45,135,45,135) - wrist.map(0.0, 180.0, 0.0, 180.0); - Rwrist.setVelocity(26.0); - // #bicep.setMinMax(5,90) - Rwrist.moveTo(90.0); - // Servo finger = (Servo) Runtime.start("finger","Servo"); - // finger.attach(arduino,18,90); - // finger.map(89.999,90.001,89.999,90.001); - // finger.setVelocity(26); - // //#bicep.setMinMax(5,90) - // finger.moveTo(90); - // Servo Rfinger = (Servo) Runtime.start("Rfinger","Servo"); - // Rfinger.attach(arduino,38,90); - // Rfinger.map(89.999,90.001,89.999,90.001); - // Rfinger.setVelocity(26); - // //#bicep.setMinMax(5,90) - // Rfinger.moveTo(90); - - // #define the DH parameters for the ik service - ik.setNewDHRobotArm("leftArm"); - ik.setDHLink("leftArm", mtorso, 113, 90, 0, -90); - // ik.setDHLink("rightArm",ttorso,0,90+65.6,346,0); - ik.setDHLink("leftArm", ttorso, 0, 180, 292, 90); - ik.setDHLink("leftArm", "rightS", 143, 180, 0, 90); - ik.setDHLink("leftArm", omoplate, 0, -5.6, 45, -90); - ik.setDHLink("leftArm", shoulder, 77, -30 + 90, 0, 90); - ik.setDHLink("leftArm", rotate, 284, 90, 40, 90); - ik.setDHLink("leftArm", bicep, 0, -7 + 24.4 + 90, 300, 0); - ik.setDHLink("leftArm", wrist, 0, -90, 0, 0); - ik.setDHLinkType("wrist", DHLinkType.REVOLUTE_ALPHA); - ik.setDHLink("leftArm", "wristup", 0, -5, 110, 0); - ik.setDHLink("leftArm", "wristdown", 0, 0, 105, 45); - ik.setDHLink("leftArm", "finger", 5, -90, 5, 0); - - ik.startEngine("leftArm"); - - ik.setNewDHRobotArm("rightArm"); - ik.setDHLink("rightArm", mtorso, 113, 90, 0, -90); - ik.setDHLink("rightArm", ttorso, 0, 180, 292, 90); - // ik.setDHLink("leftArm",ttorso,0,180,297.5,90); - ik.setDHLink("rightArm", "leftS", -143, 180, 0, -90); - ik.setDHLink("rightArm", Romoplate, 0, -5.6, 45, 90); - ik.setDHLink("rightArm", Rshoulder, -77, -30 + 90, 0, -90); - ik.setDHLink("rightArm", Rrotate, -284, 90, 40, -90); - ik.setDHLink("rightArm", Rbicep, 0, -7 + 24.4 + 90, 300, 0); - //////////// //#ik.setDHLink(wrist,00,-90,200,0) - ik.setDHLink("rightArm", Rwrist, 00, -90, 0, 0); - ik.setDHLinkType("Rwrist", DHLinkType.REVOLUTE_ALPHA); - ik.setDHLink("rightArm", "Rwristup", 0, 5, 110, 0); - ik.setDHLink("rightArm", "Rwristdown", 0, 0, 105, -45); - ik.setDHLink("rightArm", "Rfinger", 5, 90, 5, 0); - ik.startEngine("rightArm"); - - ik.setNewDHRobotArm("kinect"); - ik.setDHLink("kinect", mtorso, 113, 90, 0, -90); - ik.setDHLink("kinect", ttorso, 0, 90 + 90, 110, -90); - ik.setDHLink("kinect", "camera", 0, 90, 10, 90); - // - ik.startEngine("kinect"); - - // #define object, each dh link are set as an object, but the - // #start point and end point will be update by the ik service, but still - // need - // #a name and a radius - // #static object need a start point, an end point, a name and a radius - ik.clearObject(); - ik.addObject(0.0, 0.0, 0.0, 0.0, 0.0, -150.0, "base", 150.0, false); - ik.addObject("mtorso", 150.0); - ik.addObject("ttorso", 10.0); - ik.addObject("omoplate", 10.0); - ik.addObject("Romoplate", 10.0); - ik.addObject("shoulder", 50.0); - ik.addObject("Rshoulder", 50.0); - ik.addObject("rotate", 50.0); - ik.addObject("Rrotate", 50.0); - ik.addObject("bicep", 60.0); - ik.addObject("Rbicep", 60.0); - ik.addObject("wrist", 10.0); - ik.addObject("Rwrist", 70.0); - ik.addObject("leftS", 10); - ik.addObject("rightS", 10); - ik.addObject("wristup", 70); - ik.addObject("wristdown", 70); - ik.objectAddIgnore("bicep", "wristup"); - ik.addObject("Rwristup", 70); - ik.addObject("Rwristdown", 70); - ik.objectAddIgnore("Rbicep", "Rwristup"); - ik.objectAddIgnore("leftS", "rightS"); - ik.objectAddIgnore("omoplate", "rotate"); - ik.objectAddIgnore("Romoplate", "Rrotate"); - ik.objectAddIgnore("rightS", "shoulder"); - ik.objectAddIgnore("leftS", "Rshoulder"); - sleep(1000); - // ik.addObject("Rfinger",10.0); - // ik.addObject(-1000.0,400, 0, 1000, 425, 00, "obstacle",40, true); - // #ik.addObject(360,540,117,360, 550,107,"cymbal",200) - // #ik.addObject(90,530,-180,300,545,-181,"bell", 25) - // ik.addObject(170,640,-70,170,720,-250,"tom",150,true); - ik.addObject(0, 700, 300, 0, 700, 150, "beer", 30, true); - - // print ik.currentPosition(); - - // #setting ik parameters for the computing - - // #move to a position - // ik.moveTo("leftArm",260,410,-120); - // ik.moveTo(280,190,-345); - // #ik.moveTo("cymbal",ik.ObjectPointLocation.ORIGIN_SIDE, 0,0,5) - // #mtorso.moveTo(45) - log.info(ik.currentPosition("leftArm").toString()); - log.info(ik.currentPosition("rightArm").toString()); - // shoulder.moveTo(90); - // sleep(1000); - // log.info(ik.currentPosition("leftArm").toString()); - - // print "kinect Position" + str(ik.currentPosition("kinect")); - - // ik.holdTarget("leftArm", true); - ik.visualize(); - ((TestJmeIMModel) ik.jmeApp).addPart("ltorso", "Models/ltorso.j3o", 1, null, new Vector3f(0, 0, 0), Vector3f.UNIT_X.mult(1), (float) Math.toRadians(0)); - ((TestJmeIMModel) ik.jmeApp).addPart("mtorso", "Models/mtorso.j3o", 1f, null, new Vector3f(0, 0, 0), Vector3f.UNIT_Y.mult(-1), (float) Math.toRadians(-90)); - ((TestJmeIMModel) ik.jmeApp).addPart("ttorso", "Models/ttorso1.j3o", 1f, "mtorso", new Vector3f(0, 105f, 10), Vector3f.UNIT_Z, (float) Math.toRadians(-90)); - ((TestJmeIMModel) ik.jmeApp).addPart("rightS", null, 1f, "ttorso", new Vector3f(0, 300f, 0), Vector3f.UNIT_Z, (float) Math.toRadians(0)); - ((TestJmeIMModel) ik.jmeApp).addPart("Romoplate", "Models/Romoplate1.j3o", 1f, "rightS", new Vector3f(-143f, 0, -17), Vector3f.UNIT_Z.mult(-1), (float) Math.toRadians(-4)); - ((TestJmeIMModel) ik.jmeApp).addPart("Rshoulder", "Models/Rshoulder1.j3o", 1f, "Romoplate", new Vector3f(-23, -45f, 0), Vector3f.UNIT_X.mult(-1), (float) Math.toRadians(-32)); - ((TestJmeIMModel) ik.jmeApp).addPart("Rrotate", "Models/rotate1.j3o", 1f, "Rshoulder", new Vector3f(-57, -55, 8), Vector3f.UNIT_Y.mult(-1), (float) Math.toRadians(-90)); - ((TestJmeIMModel) ik.jmeApp).addPart("Rbicep", "Models/Rbicep1.j3o", 1f, "Rrotate", new Vector3f(5, -225, -32), Vector3f.UNIT_X.mult(-1), (float) Math.toRadians(20)); - ((TestJmeIMModel) ik.jmeApp).addPart("leftS", null, 1f, "ttorso", new Vector3f(0, 300f, 0), Vector3f.UNIT_Z, (float) Math.toRadians(0)); - ((TestJmeIMModel) ik.jmeApp).addPart("omoplate", "Models/Lomoplate1.j3o", 1f, "leftS", new Vector3f(143f, 0, -15), Vector3f.UNIT_Z.mult(1), (float) Math.toRadians(-6)); - ((TestJmeIMModel) ik.jmeApp).addPart("shoulder", "Models/Lshoulder.j3o", 1f, "omoplate", new Vector3f(17, -45f, 5), Vector3f.UNIT_X.mult(-1), (float) Math.toRadians(-30)); - ((TestJmeIMModel) ik.jmeApp).addPart("rotate", "Models/rotate1.j3o", 1f, "shoulder", new Vector3f(65, -58, -3), Vector3f.UNIT_Y.mult(1), (float) Math.toRadians(-90)); - ((TestJmeIMModel) ik.jmeApp).addPart("bicep", "Models/Lbicep.j3o", 1f, "rotate", new Vector3f(-14, -223, -28), Vector3f.UNIT_X.mult(-1), (float) Math.toRadians(17)); - ((TestJmeIMModel) ik.jmeApp).addPart("Rwrist", "Models/RWristFinger.j3o", 1f, "Rbicep", new Vector3f(15, -290, -10), Vector3f.UNIT_Y.mult(-1), (float) Math.toRadians(180)); - ((TestJmeIMModel) ik.jmeApp).addPart("wrist", "Models/LWristFinger.j3o", 1f, "bicep", new Vector3f(0, -290, -20), Vector3f.UNIT_Y.mult(1), (float) Math.toRadians(180)); - ((TestJmeIMModel) ik.jmeApp).addPart("neck", "Models/neck.j3o", 1f, "ttorso", new Vector3f(0, 452.5f, -45), Vector3f.UNIT_X.mult(-1), (float) Math.toRadians(0)); - ((TestJmeIMModel) ik.jmeApp).addPart("neckroll", null, 1f, "neck", new Vector3f(0, 0, 0), Vector3f.UNIT_Z.mult(1), (float) Math.toRadians(2)); - ((TestJmeIMModel) ik.jmeApp).addPart("head", "Models/head.j3o", 1f, "neckroll", new Vector3f(0, 10, 20), Vector3f.UNIT_Y.mult(-1), (float) Math.toRadians(0)); - ((TestJmeIMModel) ik.jmeApp).addPart("jaw", "Models/jaw.j3o", 1f, "head", new Vector3f(-5, 63, -50), Vector3f.UNIT_X.mult(-1), (float) Math.toRadians(0)); - // ((TestJmeIMModel) ik.jmeApp).addPart("finger", null, 10f, "wrist", new - // Vector3f(0,205,0), Vector3f.UNIT_X.mult(-1), - // (float)Math.toRadians(0)); - - // TODO add the object that can collide with the model - // ik.jmeApp.addObject(); - - // need to move a little so the position update - mtorso.moveTo(91.0); - ttorso.moveTo(91.0); - Romoplate.moveTo(11.0); - Rshoulder.moveTo(31.0); - Rrotate.moveTo(91.0); - Rbicep.moveTo(6.0); - omoplate.moveTo(11.0); - shoulder.moveTo(31.0); - rotate.moveTo(91.0); - bicep.moveTo(6.0); - wrist.moveTo(91.0); - Rwrist.moveTo(91.0); - - mtorso.moveTo(90.0); - ttorso.moveTo(90.0); - Romoplate.moveTo(10.0); - Rshoulder.moveTo(30.0); - Rrotate.moveTo(90.0); - Rbicep.moveTo(5.0); - omoplate.moveTo(10.0); - shoulder.moveTo(30.0); - rotate.moveTo(90.0); - bicep.moveTo(5.0); - wrist.moveTo(90.0); - Rwrist.moveTo(90.0); - - // sleep(3000); - // double[][] jp = ik.createJointPositionMap("leftArm"); - // Point x = new - // Point(jp[jp.length-2][0],jp[jp.length-2][1],jp[jp.length-2][2],0,0,0); - // Point y = new - // Point(jp[jp.length-1][0],jp[jp.length-1][1],jp[jp.length-1][2],0,0,0); - // ik.addObject(x,y,"finger",100.0, false); - - // ik.startOpenNI(); - // ik.processKinectData(); - - ik.cog = new GravityCenter(ik); - ik.cog.setLinkMass("mtorso", 2.832, 0.5); - ik.cog.setLinkMass("ttorso", 5.774, 0.5); - ik.cog.setLinkMass("omoplate", 0.739, 0.5); - ik.cog.setLinkMass("Romoplate", 0.739, 0.5); - ik.cog.setLinkMass("rotate", 0.715, 0.5754); - ik.cog.setLinkMass("Rrotate", 0.715, 0.5754); - ik.cog.setLinkMass("shoulder", 0.513, 0.5); - ik.cog.setLinkMass("Rshoulder", 0.513, 0.5); - ik.cog.setLinkMass("bicep", 0.940, 0.4559); - ik.cog.setLinkMass("Rbicep", 0.940, 0.4559); - ik.cog.setLinkMass("wrist", 0.176, 0.7474); - ik.cog.setLinkMass("Rwrist", 0.176, 0.7474); - - // ik.setAi("rightArm", Ai.KEEP_BALANCE); - // ik.setAi("leftArm", Ai.KEEP_BALANCE); - ik.removeAi("kinect", Ai.AVOID_COLLISION); - - } - - public void startEngine(String arm) { - getEngine(arm).start(); - addTask("publishPosition-" + arm, 1000, 0, "publishPosition", arm); - } - - public Thread getEngine(String arm) { - if (engines.containsKey(arm)) { - return engines.get(arm); - } else { - log.info("no engines found {}", arm); - return null; - } - } - - @Override - public Map publishJointAngles(Map angleMap) { - return angleMap; - } - - public double[][] publishJointPositions(double[][] jointPositionMap) { - return jointPositionMap; - } - - public void setDHLink(String arm, String name, double d, double theta, double r, double alpha) { - if (engines.containsKey(arm)) { - DHLink dhLink = new DHLink(name, d, r, MathUtils.degToRad(theta), MathUtils.degToRad(alpha)); - IMEngine engine = engines.get(arm); - DHRobotArm dhArm = engine.getDHRobotArm(); - dhArm.addLink(dhLink); - engine.setDHRobotArm(dhArm); - engines.put(arm, engine); - } else { - log.error("Unknow DH arm {}", arm); - } - } - - public void setDHLink(String arm, ServoControl servo, double d, double theta, double r, double alpha) { - // TODO: kw verify the servo max here should actually be minX maxX .. not - // these values here. - setDHLink(arm, servo, d, theta, r, alpha, servo.getMin(), servo.getMax()); - } - - public void setDHLink(String arm, ServoControl servo, double d, double theta, double r, double alpha, double minAngle, double maxAngle) { - if (engines.containsKey(arm)) { - IMEngine engine = engines.get(arm); - DHLink dhLink = new DHLink(servo.getName(), d, r, MathUtils.degToRad(theta), MathUtils.degToRad(alpha)); - // servo.addIKServoEventListener(this); - dhLink.addPositionValue(servo.getCurrentInputPos()); - // TODO: kw: review that these should actually be the input minX / maxX .. - // not the output mapping values here. - dhLink.setMin(MathUtils.degToRad(theta + servo.getMin())); - dhLink.setMax(MathUtils.degToRad(theta + servo.getMax())); - // dhLink.setState(Servo.SERVO_EVENT_STOPPED); - dhLink.setVelocity(servo.getSpeed()); - dhLink.setTargetPos(servo.getCurrentInputPos()); - dhLink.servoMin = minAngle;// servo.getMinInput(); - dhLink.servoMax = maxAngle;// servo.getMaxInput(); - dhLink.hasServo = true; - DHRobotArm dhArm = engine.getDHRobotArm(); - dhArm.addLink(dhLink); - engine.setDHRobotArm(dhArm); - engines.put(arm, engine); - // servo.subscribe(getName(), "publishAngles", servo.getName(), - // "onIMAngles"); - Mapper map = new MapperLinear(servo.getMin(), servo.getMax(), minAngle, maxAngle); - maps.put(servo.getName(), map); - } else { - log.error("Unknow DH arm {}", arm); - } - } - - public void setNewDHRobotArm(String name) { - IMEngine engine = new IMEngine(name, this); - engine.setInputMatrix(inputMatrix); - engines.put(name, engine); - - } - - public String addObject(double oX, double oY, double oZ, double eX, double eY, double eZ, String name, double radius, boolean render) { - return addObject(new Point(oX, oY, oZ, 0, 0, 0), new Point(eX, eY, eZ, 0, 0, 0), name, radius, render); - } - - public String addObject(Point origin, Point end, String name, double radius) { - return addObject(origin, end, name, radius, false); - } - - public String addObject(Point origin, Point end, String name, double radius, boolean render) { - CollisionItem item = new CollisionItem(origin, end, name, radius, render); - collisionItems.addItem(item); - if (jmeApp != null) { - jmeApp.addObject(item); - } - broadcastState(); - return item.getName(); - } - - public String addObject(String name, double radius, boolean render) { - return addObject(new Point(0, 0, 0, 0, 0, 0), new Point(0, 0, 0, 0, 0, 0), name, radius, render); - } - - public String addObject(String name, double radius) { - return addObject(name, radius, false); - } - - public String addObject(HashMap cloudMap) { - CollisionItem item = new CollisionItem(cloudMap); - collisionItems.addItem(item); - return item.getName(); - } - - public void clearObject() { - collisionItems.clearItem(); - } - - public void removeObject(String name) { - collisionItems.removeObject(name); - } - - public void objectAddIgnore(String object1, String object2) { - collisionItems.addIgnore(object1, object2); - } - - public void objectRemoveIgnore(String object1, String object2) { - collisionItems.removeIgnore(object1, object2); - } - - public void onIKServoEvent(ServoEvent data) { - Mapper map = maps.get(data.name); - // data.pos = map.calcOutput(data.pos); - // data.targetPos = map.calcOutput(data.targetPos); - for (IMEngine e : engines.values()) { - e.updateLinksPosition(data); - } - if (openni != null) { - map3d.updateKinectPosition(currentPosition(kinectName)); - } - if (jmeApp != null) { - // jmeApp.updateObjects(collisionItems.getItems()); - jmeApp.updatePosition(data); - } - } - - public void moveTo(String armName, String objectName, ObjectPointLocation location, String lastDHLink) { - engines.get(armName).moveTo(collisionItems.getItem(objectName), location, lastDHLink); - } - - public void moveTo(String armName, String objectName, ObjectPointLocation location) { - moveTo(armName, objectName, location, null); - } - - public void stopMoving() { - for (IMEngine engine : engines.values()) { - engine.target = null; - } - } - - public OpenNi startOpenNI() throws Exception { - if (openni == null) { - openni = (OpenNi) startPeer("openni"); - openni.start3DData(); - map3d.updateKinectPosition(currentPosition(kinectName)); - // this.subscribe(openni.getName(), "publishOpenNIData", this.getName(), - // "onOpenNiData"); - } - return openni; - } - - public void onOpenNiData(OpenNiData data) throws InterruptedException { - if (ProcessKinectData) { - ProcessKinectData = false; - long a = System.currentTimeMillis(); - log.info("start {}", a); - map3d.processDepthMap(data); - removeKinectObject(); - ArrayList> object = map3d.getObject(); - for (int i = 0; i < object.size(); i++) { - addObject(object.get(i)); - } - if (jmeApp != null) { - jmeApp.addObject(getCollisionObject()); - } - long b = System.currentTimeMillis(); - log.info("end {} - {} - {}", b, b - a, this.inbox.size()); - broadcastState(); - } - } - - private void removeKinectObject() throws InterruptedException { - collisionItems.removeKinectObject(); - - } - - public void processKinectData() throws InterruptedException { - ProcessKinectData = true; - onOpenNiData(openni.get3DData()); - } - - public void setKinectName(String kinectName) { - this.kinectName = kinectName; - } - - public ConcurrentHashMap getCollisionObject() { - return collisionItems.getItems(); - } - - public ObjectPointLocation[] getEnumLocationValue() { - return ObjectPointLocation.values(); - } - - public Collection getArms() { - return engines.values(); - } - - public void visualize() throws InterruptedException { - if (jmeApp != null) { - log.info("JmeApp already started"); - return; - } - jmeApp = new TestJmeIMModel(); - // jmeApp.setObjects(getCollisionObject()); - // jmeApp.setShowSettings(false); - AppSettings settings = new AppSettings(true); - settings.setResolution(800, 600); - // settings.setUseInput(false); - jmeApp.setSettings(settings); - jmeApp.setShowSettings(false); - jmeApp.setPauseOnLostFocus(false); - jmeApp.setService(this); - jmeApp.start(); - // need to wait for jmeApp to be ready or the models won't load - synchronized (this) { - wait(5000); - } - // add the existing objects - jmeApp.addObject(collisionItems.getItems()); - } - - public synchronized void sendAngles(String name, double positionValueDeg) { - double pos = 0; - if (maps.containsKey(name)) { - pos = maps.get(name).calcInput(positionValueDeg); - } - invoke("publishAngles", name, pos); - } - - public Object[] publishAngles(String name, double positionValueDeg) { - Object[] retval = new Object[] { name, positionValueDeg }; - return retval; - } - - public void holdTarget(String arm, boolean holdEnabled) { - engines.get(arm).holdTarget(holdEnabled); - } - - public PositionData publishPosition(PositionData position) { - return position; - } - - public void publishPosition(String armName) { - invoke("publishPosition", new PositionData(armName, currentPosition(armName))); - } - - public void setJmeApp(IntegratedMovementInterface jmeApp) { - this.jmeApp = jmeApp; - } - - public void setMinMaxAngles(String partName, double min, double max) { - if (maps.containsKey(partName)) { - Mapper map = maps.get(partName); - map = new MapperLinear(map.getMinX(), map.getMaxX(), min, max); - maps.put(partName, map); - for (IMEngine engine : engines.values()) { - for (DHLink link : engine.getDHRobotArm().getLinks()) { - if (link.getName().equals(partName)) { - link.servoMin = min; - link.servoMax = max; - link.setMin(link.getInitialTheta() + Math.toRadians(min)); - link.setMax(link.getInitialTheta() + Math.toRadians(max)); - } - } - } - } else { - log.info("No part named {} found", partName); - } - } - - public void setAi(Ai ai) { - for (IMEngine engine : engines.values()) { - engine.setAi(ai); - } - } - - public void setAi(String ai) { - for (Ai a : Ai.values()) { - if (a.text.equals(ai)) { - setAi(a); - return; - } - } - log.info("Ai {} not found", ai); - } - - public void removeAi(Ai ai) { - for (IMEngine engine : engines.values()) { - engine.removeAi(ai); - } - } - - public void setAi(String armName, Ai ai) { - if (engines.containsKey(armName)) { - engines.get(armName).setAi(ai); - } - } - - public void removeAi(String armName, Ai ai) { - if (engines.containsKey(armName)) { - engines.get(armName).removeAi(ai); - } - } - - public Double getAngleWithObject(String armName, String objectName) { - CollisionItem ci = collisionItems.getItem(objectName); - Vector3f vo = new Vector3f((float) ci.getOrigin().getX(), (float) ci.getOrigin().getY(), (float) ci.getOrigin().getZ()); - Vector3f ve = new Vector3f((float) ci.getEnd().getX(), (float) ci.getEnd().getY(), (float) ci.getEnd().getZ()); - Vector3f vci = vo.subtract(ve); - Point armvector = engines.get(armName).getDHRobotArm().getVector(); - Vector3f va = new Vector3f((float) armvector.getX(), (float) armvector.getY(), (float) armvector.getZ()); - float angle = va.dot(vci); - double div = Math.sqrt(Math.pow(vci.x, 2) + Math.pow(vci.y, 2) + Math.pow(vci.z, 2)) * Math.sqrt(Math.pow(va.x, 2) + Math.pow(va.y, 2) + Math.pow(va.z, 2)); - return MathUtils.radToDeg(Math.acos(angle / div)); - // return (double) va.angleBetween(vci); - } - - public Double getAngleWithAxis(String objectName, String axis) { - CollisionItem ci = collisionItems.getItem(objectName); - Vector3f vo = new Vector3f((float) ci.getOrigin().getX(), (float) ci.getOrigin().getY(), (float) ci.getOrigin().getZ()); - Vector3f ve = new Vector3f((float) ci.getEnd().getX(), (float) ci.getEnd().getY(), (float) ci.getEnd().getZ()); - Vector3f vci = vo.subtract(ve); - Vector3f va = null; - if (axis.equals("x")) { - va = Vector3f.UNIT_X; - } else if (axis.equals("y")) { - va = Vector3f.UNIT_Y; - } else if (axis.equals("z")) { - va = Vector3f.UNIT_Z; - } - return MathUtils.radToDeg(va.angleBetween(vci)); - } - - public void setDHLinkType(String name, DHLinkType type) { - for (IMEngine engine : engines.values()) { - for (DHLink link : engine.getDHRobotArm().getLinks()) { - if (link.getName().equals(name)) { - link.setType(type); - } - } - } - } -} diff --git a/src/main/java/org/myrobotlab/service/Intro.java b/src/main/java/org/myrobotlab/service/Intro.java index ad64524e3c..3e7764bf46 100644 --- a/src/main/java/org/myrobotlab/service/Intro.java +++ b/src/main/java/org/myrobotlab/service/Intro.java @@ -62,7 +62,7 @@ public Object get(String key) { /** * @param introScriptName - * execute an Intro resource script + * execute an Intro resource script */ public void execScript(String introScriptName) { try { @@ -78,7 +78,7 @@ public void execScript(String introScriptName) { * This method will load a python file into the python interpreter. * * @param file - * the python file to load + * the python file to load * @return true/false */ @Deprecated @@ -127,7 +127,6 @@ public static void main(String[] args) { // Runtime.start("intro", "Intro"); // Runtime.release("python"); - // Runtime.releaseAll(); Runtime.start("python", "Python"); // Runtime.start("mega", "Arduino"); // Runtime.start("ada", "Adafruit16CServoDriver"); @@ -168,7 +167,7 @@ public static void main(String[] args) { DiscordBot bot = (DiscordBot) Runtime.start("bot", "DiscordBot"); ProgramAB brain = (ProgramAB) Runtime.start("brain", "ProgramAB"); - brain.setCurrentBotName("Alice"); + brain.setBotType("Alice"); bot.connect(); brain.attach(bot); bot.attach(brain); diff --git a/src/main/java/org/myrobotlab/service/Keyboard.java b/src/main/java/org/myrobotlab/service/Keyboard.java index 9b53cfbe39..f399b77b41 100644 --- a/src/main/java/org/myrobotlab/service/Keyboard.java +++ b/src/main/java/org/myrobotlab/service/Keyboard.java @@ -103,7 +103,7 @@ public void nativeMouseWheelMoved(NativeMouseWheelEvent me) { public Keyboard(String n, String id) { super(n, id); if (Service.isHeadless()) { - log.warn("the Keyboard service requires a DISPLAY to function correctly"); + log.info("the Keyboard service requires a DISPLAY to function correctly"); keyboard = null; mouseEvent = null; return; diff --git a/src/main/java/org/myrobotlab/service/KeyboardSim.java b/src/main/java/org/myrobotlab/service/KeyboardSim.java index b9052a0541..9e3e36ccfa 100644 --- a/src/main/java/org/myrobotlab/service/KeyboardSim.java +++ b/src/main/java/org/myrobotlab/service/KeyboardSim.java @@ -27,7 +27,7 @@ public KeyboardSim(String n, String id) { if (!GraphicsEnvironment.isHeadless()) { robot = new Robot(); } else { - log.warn("headless environment - but KeyboardSim requires a display"); + log.info("headless environment - but KeyboardSim requires a display"); } } catch (Exception e) { log.error("could not create java.awt.Robot", e); diff --git a/src/main/java/org/myrobotlab/service/Lloyd.java b/src/main/java/org/myrobotlab/service/Lloyd.java index ca1dc561ff..4a5aeaf7b3 100755 --- a/src/main/java/org/myrobotlab/service/Lloyd.java +++ b/src/main/java/org/myrobotlab/service/Lloyd.java @@ -23,7 +23,7 @@ import org.myrobotlab.logging.LoggingFactory; import org.myrobotlab.opencv.OpenCVFilterDL4JTransfer; import org.myrobotlab.opencv.OpenCVFilterLloyd; -import org.myrobotlab.programab.OOBPayload; +import org.myrobotlab.programab.models.Oob; import org.myrobotlab.service.config.ServiceConfig; import org.myrobotlab.service.interfaces.SpeechRecognizer; import org.myrobotlab.service.interfaces.SpeechSynthesis; @@ -143,7 +143,7 @@ public void startMouth() { public void startBrain() throws IOException { brain = (ProgramAB) Runtime.start("brain", "ProgramAB"); // TODO: setup the AIML / chat bot directory for all of this. - brain.startSession("ProgramAB", "person", "lloyd"); + brain.setSession("person", "lloyd"); } public void initializeBrain() { @@ -240,13 +240,15 @@ public void tellMeAboutLookup() { } public void createKnowledgeLookup(String pattern, String fieldName, String prefix, String suffix) { - OOBPayload oobTag = createSolrFieldSearchOOB(fieldName); + Oob oobTag = createSolrFieldSearchOOB(fieldName); // TODO: handle (in the template) a zero hit result ?) - String template = prefix + OOBPayload.asBlockingOOBTag(oobTag) + suffix; - brain.addCategory(pattern, template); + + // FIXME +// String template = prefix + OOBPayload.asBlockingOOBTag(oobTag) + suffix; +// brain.addCategory(pattern, template); } - private OOBPayload createSolrFieldSearchOOB(String fieldName) { + private Oob createSolrFieldSearchOOB(String fieldName) { String serviceName = "cloudMemory"; // TODO: make this something that's completely abstracted out from here. String methodName = "fetchFirstResultSentence"; @@ -257,8 +259,10 @@ private OOBPayload createSolrFieldSearchOOB(String fieldName) { // +has_infobox:true"); params.add("infobox_name_ss: title: text: +" + fieldName + ":* +has_infobox:true"); params.add(fieldName); - OOBPayload oobTag = new OOBPayload(serviceName, methodName, params); - return oobTag; +// OOBPayload oobTag = new OOBPayload(serviceName, methodName, params); +// return oobTag; + // FIXME + return null; } public void startMemory() { diff --git a/src/main/java/org/myrobotlab/service/LocalSpeech.java b/src/main/java/org/myrobotlab/service/LocalSpeech.java index 292198f3f6..3d2194c896 100644 --- a/src/main/java/org/myrobotlab/service/LocalSpeech.java +++ b/src/main/java/org/myrobotlab/service/LocalSpeech.java @@ -359,9 +359,8 @@ public boolean setPico2Wav() { * Typically double quotes should be filtered out of the command as creating * the text to speech process command can be broken by double quotes * - * @param filter - * chars to filter. - * + * @param target + * @param replace */ public void addFilter(String target, String replace) { LocalSpeechConfig c = (LocalSpeechConfig) config; diff --git a/src/main/java/org/myrobotlab/service/Log.java b/src/main/java/org/myrobotlab/service/Log.java index bdef4384cb..b550cbf651 100644 --- a/src/main/java/org/myrobotlab/service/Log.java +++ b/src/main/java/org/myrobotlab/service/Log.java @@ -105,6 +105,8 @@ public String toString() { */ long minIntervalMs = 1000; + private String previousLogLevel; + public Log(String n, String id) { super(n, id); getLogLevel(); @@ -265,6 +267,7 @@ public void setRootLogLevel(String level) { public LogConfig apply(LogConfig c) { super.apply(c); + previousLogLevel = getLogLevel(); if (c.level != null) { setRootLogLevel(c.level); } @@ -317,6 +320,9 @@ public void stopLogging() { public void stopService() { super.stopService(); stopLogging(); + if (previousLogLevel != null) { + Runtime.setLogLevel(previousLogLevel); + } } public static void main(String[] args) { diff --git a/src/main/java/org/myrobotlab/service/MockGateway.java b/src/main/java/org/myrobotlab/service/MockGateway.java index 0e532f28cc..7a1f2577c4 100644 --- a/src/main/java/org/myrobotlab/service/MockGateway.java +++ b/src/main/java/org/myrobotlab/service/MockGateway.java @@ -55,17 +55,17 @@ public RemoteService(MockGateway gateway, String fullname) { this.gateway = gateway; } - public void handle(Message msg) { + public void handle(Message msg) { if (msg.method.equals("getMethodMap")) { Map emptyMap = new HashMap<>(); // emptyMap = Runtime.getMethodMap("clock"); - Message returnMsg = Message.createMessage(name, msg.sender, "onMethodMap", emptyMap); + Message returnMsg = Message.createMessage(name, msg.sender, "onMethodMap", emptyMap); gateway.send(returnMsg); } - + } } - + @Override public void send(Message msg) { super.send(msg); @@ -109,32 +109,32 @@ public void addConnection(String id) { @Override public void sendRemote(Message msg) throws Exception { log.info("mock gateway got a sendRemote {}", msg); - + String key = String.format("%s.%s", msg.name, msg.method); - + BlockingQueue q = null; if (!sendQueues.containsKey(key)) { - q = new LinkedBlockingQueue<>(); + q = new LinkedBlockingQueue<>(); sendQueues.put(key, q); } else { q = sendQueues.get(key); } - + q.add(msg); invoke("publishMessageEvent", msg); msgs.add(msg); - + // verify the msg can be serialized String json = CodecUtils.toJson(msg); log.debug("sendRemote {}", json); - + if (!remoteServices.containsKey(msg.name)) { error("got remote message from %s - and do not have its client !!!", msg.name); return; - } + } remoteServices.get(msg.name).handle(msg); } - + public void clear() { sendQueues.clear(); } @@ -147,14 +147,12 @@ public int size() { public boolean isLocal(Message msg) { return Runtime.getInstance().isLocal(msg); } - public void sendWithDelay(String name, String method, Object... data) { Message msg = Message.createMessage(method, name, method, data); addTask(UUID.randomUUID().toString(), true, 0, 0, "send", new Object[] { msg }); } - /** * Send an asynchronous message so waiting for a callback can be done easily * with inline code e.g. @@ -182,13 +180,13 @@ public void sendWithDelay(long wait, String name, String method, Object... data) public Message waitForMsg(String name, String callback, long maxTimeWaitMs) throws TimeoutException { try { String fullName = getFullRemoteName(name); - + String key = String.format("%s.%s", fullName, callback); - - if (!sendQueues.containsKey(key)) { + + if (!sendQueues.containsKey(key)) { sendQueues.put(key, new LinkedBlockingQueue<>()); } - + Message msg = sendQueues.get(key).poll(maxTimeWaitMs, TimeUnit.MILLISECONDS); if (msg == null) { String timeout = String.format("waited %dms for %s.%s", maxTimeWaitMs, name, callback); @@ -241,7 +239,7 @@ public void startService() { public void registerRemoteService(String remoteServiceName) { registerRemoteService(remoteServiceName, null); } - + public String getFullRemoteName(String name) { if (!name.contains("@")) { return String.format("%s@%s", name, remoteId); @@ -264,7 +262,6 @@ public void registerRemoteService(String remoteServiceName, ArrayList in // Runtime.register(remoteId, remoteServiceName, "mock:mock", interfaces); Runtime.register(remoteId, remoteServiceName, "Unknown", interfaces); - } /** @@ -319,12 +316,12 @@ public static void main(String[] args) { public Message getMsg(String name, String callback) { String fullName = getFullRemoteName(name); - + String key = String.format("%s.%s", fullName, callback); if (!sendQueues.containsKey(key)) { return null; } - + try { Message msg = sendQueues.get(key).poll(0, TimeUnit.MILLISECONDS); return msg; diff --git a/src/main/java/org/myrobotlab/service/NeoPixel.java b/src/main/java/org/myrobotlab/service/NeoPixel.java index 61c15e1f64..21a05cbdc5 100644 --- a/src/main/java/org/myrobotlab/service/NeoPixel.java +++ b/src/main/java/org/myrobotlab/service/NeoPixel.java @@ -927,7 +927,6 @@ public void setPixel(int address, int red, int green, int blue, int white) { * @param green * @param blue * @param white - * @param delayMs */ public void setPixel(String matrixName, Integer pixelSetIndex, int address, int red, int green, int blue, int white) { // get and update memory cache diff --git a/src/main/java/org/myrobotlab/service/NovelAI.java b/src/main/java/org/myrobotlab/service/NovelAI.java index e7581d980a..6ebddb9038 100644 --- a/src/main/java/org/myrobotlab/service/NovelAI.java +++ b/src/main/java/org/myrobotlab/service/NovelAI.java @@ -30,26 +30,6 @@ public NovelAI(String n, String id) { client = new OkHttpClient(); } - /** - * The methods apply and getConfig can be used, if more complex configuration - * handling is needed. By default, the framework takes care of most of it, - * including subscription handling. - * - *
-  @Override
-  public ServiceConfig apply(ServiceConfig c) {
-    super.apply(c)
-    return c;
-  }
-  
-  @Override
-  public ServiceConfig getConfig() {
-    super.getConfig()
-    return config;
-  }
-   * 
- **/ - public static void main(String[] args) { try { diff --git a/src/main/java/org/myrobotlab/service/OpenAI.java b/src/main/java/org/myrobotlab/service/OpenAI.java new file mode 100644 index 0000000000..6a616635e6 --- /dev/null +++ b/src/main/java/org/myrobotlab/service/OpenAI.java @@ -0,0 +1,299 @@ +package org.myrobotlab.service; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.myrobotlab.codec.CodecUtils; +import org.myrobotlab.framework.Service; +import org.myrobotlab.framework.StaticType; +import org.myrobotlab.framework.Status; +import org.myrobotlab.framework.interfaces.Attachable; +import org.myrobotlab.logging.Level; +import org.myrobotlab.logging.LoggerFactory; +import org.myrobotlab.logging.LoggingFactory; +import org.myrobotlab.programab.Response; +import org.myrobotlab.service.config.OpenAIConfig; +import org.myrobotlab.service.data.Utterance; +import org.myrobotlab.service.interfaces.ResponsePublisher; +import org.myrobotlab.service.interfaces.TextListener; +import org.myrobotlab.service.interfaces.TextPublisher; +import org.myrobotlab.service.interfaces.UtteranceListener; +import org.myrobotlab.service.interfaces.UtterancePublisher; +import org.slf4j.Logger; + +/** + * https://beta.openai.com/account/api-keys + * + * https://beta.openai.com/playground + * + * https://beta.openai.com/examples + * + *
+     * curl https://api.openai.com/v1/completions \
+      -H "Content-Type: application/json" \
+      -H "Authorization: Bearer $OPENAI_API_KEY" \
+      -d '{
+      "model": "text-ada-001",
+      "prompt": "why is the sun so bright ?\n\nThe sun is bright because it is a star. It is born on theentlement, or radiance, which is the product of the surface area of the sun's clouds and the surface area of the sun's atmosphere.",
+      "temperature": 0.7,
+      "max_tokens": 256,
+      "top_p": 1,
+      "frequency_penalty": 0,
+      "presence_penalty": 0
+    }'
+ * 
+ * 
+ * + * @author GroG + * + */ +public class OpenAI extends Service implements TextListener, TextPublisher, UtterancePublisher, UtteranceListener, ResponsePublisher { + + private static final long serialVersionUID = 1L; + + public final static Logger log = LoggerFactory.getLogger(OpenAI.class); + + protected String currentChannel; + + private String currentBotName; + + private String currentChannelName; + + private String currentChannelType; + + public OpenAI(String n, String id) { + super(n, id); + } + + public Response getResponse(String text) { + + try { + + OpenAIConfig c = (OpenAIConfig) config; + + invoke("publishRequest", text); + + if (text == null || text.trim().length() == 0) { + log.info("emtpy text, not responding"); + return null; + } + + String responseText = ""; + + if (c.sleepWord != null && text.contains(c.sleepWord) && !c.sleeping) { + sleep(); + responseText = "Ok, I will go to sleep"; + } + + if (c.prefix != null) { + text = c.prefix + " " + text; + } + + if (!c.sleeping) { + + // chat completions + String json = + "{\r\n" + + " \"model\": \""+ c.engine +"\",\r\n" + + " \"messages\": [{\"role\": \"user\", \"content\": \""+ text +"\"}],\r\n" + + " \"temperature\": 0.7\r\n" + + " }"; + +// completions +// String json = +// "{\r\n" + " \"model\": \"" + c.engine + "\",\r\n" + " \"prompt\": \"" + text + "\",\r\n" + " \"temperature\": " + c.temperature + ",\r\n" + " \"max_tokens\": " +// + c.maxTokens + ",\r\n" + " \"top_p\": 1,\r\n" + " \"frequency_penalty\": 0,\r\n" + " \"presence_penalty\": 0\r\n" + "}"; + + + HttpClient http = (HttpClient) startPeer("http"); + + String msg = http.postJson(c.token, c.url, json); + + Map payload = CodecUtils.fromJson(msg, new StaticType<>() {}); + + @SuppressWarnings({ "unchecked", "rawtypes" }) + Map errors = (Map)payload.get("error"); + if (errors != null) { + error((String)errors.get("message")); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) + List choices = (List) payload.get("choices"); + if (choices != null && choices.size() > 0) { + @SuppressWarnings({ "unchecked", "rawtypes" }) + Map textObject = (Map) choices.get(0); + responseText = (String) textObject.get("text"); + if (responseText == null) { + // /chat/completions + @SuppressWarnings({ "unchecked", "rawtypes" }) + Map content = (Map)textObject.get("message"); + // role=assistant + responseText = (String)content.get("content"); + } + + + } else { + warn("no response for %s", text); + } + + } else { + log.info("sleeping waiting for wake word \"{}\"", c.wakeWord); + } + + if (c.wakeWord != null && text.contains(c.wakeWord) && c.sleeping) { + responseText = "Hello, I am awake"; + wake(); + } + + Response response = new Response("friend", getName(), responseText, null); + Utterance utterance = new Utterance(); + utterance.username = getName(); + utterance.text = responseText; + utterance.isBot = true; + utterance.channel = currentChannel; + utterance.channelType = currentChannelType; + utterance.channelBotName = currentBotName; + utterance.channelName = currentChannelName; + if (responseText != null && responseText.length() > 0) { + invoke("publishUtterance", utterance); + invoke("publishResponse", response); + invoke("publishText", responseText); + } + + return response; + + } catch (IOException e) { + error(e); + } + return null; + } + + /** + * Overridden error to also publish the errors + * probably would be a better solution to self subscribe to errors and + * have the subscriptions publish utterances/responses/text + */ + @Override + public Status error(String error) { + Status status = super.error(error); + invoke("publishText", error); + Response response = new Response("friend", getName(), error, null); + Utterance utterance = new Utterance(); + utterance.text = error; + invoke("publishUtterance", utterance); + invoke("publishResponse", response); + return status; + } + + public String publishRequest(String text) { + return text; + } + + public void setToken(String token) { + OpenAIConfig c = (OpenAIConfig) config; + c.token = token; + } + + public String setEngine(String engine) { + OpenAIConfig c = (OpenAIConfig) config; + c.engine = engine; + return engine; + } + + @Override + public void onUtterance(Utterance utterance) throws Exception { + currentChannelType = utterance.channelType; + currentChannel = utterance.channel; + currentBotName = utterance.channelBotName; + currentChannelName = utterance.channelName; + // prevent bots going off the rails + if (utterance.isBot) { + log.info("Not responding to bots."); + return; + } + getResponse(utterance.text); + } + + @Override + public Utterance publishUtterance(Utterance utterance) { + return utterance; + } + + @Override + public String publishText(String text) { + return text; + } + + @Override + public void onText(String text) throws IOException { + getResponse(text); + } + + @Override + public Response publishResponse(Response response) { + return response; + } + + /** + * wakes the global session up + */ + public void wake() { + OpenAIConfig c = (OpenAIConfig) config; + log.info("wake now"); + c.sleeping = false; + } + + /** + * sleeps the global session + */ + public void sleep() { + OpenAIConfig c = (OpenAIConfig) config; + log.info("sleeping now"); + c.sleeping = true; + } + + @Override + public void attach(Attachable attachable) { + + /* + * if (attachable instanceof ResponseListener) { // this one is done + * correctly attachResponseListener(attachable.getName()); } else + */ + if (attachable instanceof TextPublisher) { + attachTextPublisher((TextPublisher) attachable); + } else if (attachable instanceof TextListener) { + addListener("publishText", attachable.getName(), "onText"); + } else if (attachable instanceof UtteranceListener) { + attachUtteranceListener(attachable.getName()); + } else { + log.error("don't know how to attach a {}", attachable.getName()); + } + } + + public static void main(String[] args) { + try { + + LoggingFactory.init(Level.INFO); + + // Runtime runtime = Runtime.getInstance(); + // Runtime.startConfig("openai-01"); + + WebGui webgui = (WebGui) Runtime.create("webgui", "WebGui"); + webgui.autoStartBrowser(false); + webgui.startService(); + + /* + * OpenAI i01_chatBot = (OpenAI) Runtime.start("i01.chatBot", "OpenAI"); + * + * bot.attach("i01.chatBot"); i01_chatBot.attach("bot"); + * + * i01_chatBot.getResponse("hi, how are you?"); + * + * Runtime.start("webgui", "WebGui"); + */ + + } catch (Exception e) { + log.error("main threw", e); + } + } +} diff --git a/src/main/java/org/myrobotlab/service/OpenCV.java b/src/main/java/org/myrobotlab/service/OpenCV.java index ff093721bd..b5a6364dca 100644 --- a/src/main/java/org/myrobotlab/service/OpenCV.java +++ b/src/main/java/org/myrobotlab/service/OpenCV.java @@ -55,6 +55,7 @@ import javax.imageio.ImageIO; import javax.imageio.stream.MemoryCacheImageOutputStream; +import org.atmosphere.config.managed.Invoker; import org.bytedeco.javacv.CanvasFrame; import org.bytedeco.javacv.FFmpegFrameRecorder; import org.bytedeco.javacv.Frame; diff --git a/src/main/java/org/myrobotlab/service/Pir.java b/src/main/java/org/myrobotlab/service/Pir.java index eea710f585..5cb6a80a95 100644 --- a/src/main/java/org/myrobotlab/service/Pir.java +++ b/src/main/java/org/myrobotlab/service/Pir.java @@ -312,9 +312,8 @@ public Long getLastChangeTs() { * "selecting" the controller's name, returns the possible list of pins to * attach. * + * @param pinArrayControl * @return - * @throws InterruptedException - * @throws TimeoutException */ @SuppressWarnings("unchecked") public List getPinList(String pinArrayControl) { diff --git a/src/main/java/org/myrobotlab/service/ProgramAB.java b/src/main/java/org/myrobotlab/service/ProgramAB.java index db06861aaa..fdde56b35b 100644 --- a/src/main/java/org/myrobotlab/service/ProgramAB.java +++ b/src/main/java/org/myrobotlab/service/ProgramAB.java @@ -16,11 +16,12 @@ import org.alicebot.ab.Bot; import org.alicebot.ab.Category; import org.alicebot.ab.Chat; -import org.alicebot.ab.MagicBooleans; import org.alicebot.ab.ProgramABListener; import org.apache.commons.lang3.StringUtils; +import org.myrobotlab.framework.Message; import org.myrobotlab.framework.Service; import org.myrobotlab.framework.interfaces.Attachable; +import org.myrobotlab.generics.SlidingWindowList; import org.myrobotlab.image.Util; import org.myrobotlab.io.FileIO; import org.myrobotlab.logging.LoggerFactory; @@ -28,13 +29,12 @@ import org.myrobotlab.logging.LoggingFactory; import org.myrobotlab.logging.SimpleLogPublisher; import org.myrobotlab.programab.BotInfo; -import org.myrobotlab.programab.PredicateEvent; import org.myrobotlab.programab.Response; import org.myrobotlab.programab.Session; +import org.myrobotlab.programab.models.Event; import org.myrobotlab.service.config.ProgramABConfig; import org.myrobotlab.service.config.ServiceConfig; import org.myrobotlab.service.data.Locale; -import org.myrobotlab.service.data.TopicChange; import org.myrobotlab.service.data.Utterance; import org.myrobotlab.service.interfaces.LocaleProvider; import org.myrobotlab.service.interfaces.LogPublisher; @@ -72,6 +72,11 @@ public class ProgramAB extends Service private static final long serialVersionUID = 1L; + /** + * history of topic changes + */ + protected List topicHistory = new SlidingWindowList<>(100); + /** * useGlobalSession true will allow the sleep member to control session focus */ @@ -85,7 +90,7 @@ public class ProgramAB extends Service Map bots = new TreeMap<>(); /** - * Mapping a bot to a userName and chat session + * Mapping a bot to a username and chat session */ Map sessions = new TreeMap<>(); @@ -140,7 +145,7 @@ public List scanForBots(String path) { if (checkIfValid(file)) { info("found %s bot directory", file.getName()); botDirs.add(file); - addBotPath(file.getAbsolutePath()); + addBot(file.getAbsolutePath()); } } return botDirs; @@ -183,7 +188,7 @@ public void addTextPublisher(TextPublisher service) { } public int getMaxConversationDelay() { - return getCurrentSession().maxConversationDelay; + return getConfig().maxConversationDelay; } /** @@ -196,105 +201,101 @@ public int getMaxConversationDelay() { * */ public Response getResponse(String text) { - return getResponse(getCurrentUserName(), text); + return getResponse(getUsername(), text); } /** * This method has the side effect of switching which bot your are currently * chatting with. * - * @param userName + * @param username * - the query string to the bot brain * @param text * - the user that is sending the query * @return the response for a user from a bot given the input text. */ - public Response getResponse(String userName, String text) { - return getResponse(userName, getCurrentBotName(), text); + public Response getResponse(String username, String text) { + return getResponse(username, getBotType(), text); } /** * Full get response method . Using this method will update the current * user/bot name if different from the current session. * - * @param userName + * @param username * username - * @param botName + * @param botType * bot name * @param text * utterace * @return programab response to utterance * */ - public Response getResponse(String userName, String botName, String text) { - return getResponse(userName, botName, text, true); + public Response getResponse(String username, String botType, String text) { + return getResponse(username, botType, text, true); } /** * Gets a response and optionally update if this is the current bot session * that's active globally. * - * @param userName - * username - * @param botName - * botname + * @param username + * - user request a response + * + * @param botType + * - bot type providing the response + * * @param text - * utterance + * - query * * @param updateCurrentSession - * (specify if the currentbot/currentuser name should be updated in - * the programab service.) - * @return the response + * - switch the current focus, so that the current session is the + * username and bot type in the parameter, publishSession will + * publish the new session if different * - * TODO - no one cares about starting sessions, starting a new session - * could be as simple as providing a different username, or botname in - * getResponse and a necessary session could be created + * @return the response * */ - public Response getResponse(String userName, String botName, String text, boolean updateCurrentSession) { - Session session = getSession(userName, botName); + public Response getResponse(String username, String botType, String text, boolean updateCurrentSession) { + Session session = getSession(username, botType); // if a session with this user and bot does not exist // attempt to create it if (session == null) { - session = startSession(userName, botName); + session = startSession(username, botType, updateCurrentSession); if (session == null) { - error("username or bot name not valid %s %s", userName, botName); + error("username or bot name not valid %s %s", username, botType); return null; } } - // update the current session if we want to change which bot is at - // attention. - if (updateCurrentSession) { - setCurrentUserName(userName); - setCurrentBotName(botName); + if (updateCurrentSession && (!getUsername().equals(username) || !getBotType().equals(botType))) { + setUsername(username); + setBotType(botType); } - // Get the actual bots aiml based response for this session log.info("getResponse({})", text); Response response = session.getResponse(text); - // EEK! clean up the API! - invoke("publishRequest", text); // publisher used by uis + invoke("publishRequest", text); invoke("publishResponse", response); invoke("publishText", response.msg); return response; } - private Bot getBot(String botName) { - return bots.get(botName).getBot(); + private Bot getBot(String botType) { + return bots.get(botType).getBot(); } - private BotInfo getBotInfo(String botName) { - if (botName == null) { + private BotInfo getBotInfo(String botType) { + if (botType == null) { error("getBotinfo(null) not valid"); return null; } - BotInfo botInfo = bots.get(botName); + BotInfo botInfo = bots.get(botType); if (botInfo == null) { - error("botInfo(%s) is null", botName); + error("botInfo(%s) is null", botType); return null; } @@ -312,12 +313,24 @@ public void repetitionCount(int val) { org.alicebot.ab.MagicNumbers.repetition_count = val; } + /** + * get the "current" session if it exists + * + * @return + */ public Session getSession() { - return getSession(getCurrentUserName(), getCurrentBotName()); + return getSession(getUsername(), getBotType()); } - public Session getSession(String userName, String botName) { - String sessionKey = getSessionKey(userName, botName); + /** + * get a specific user & botType session + * + * @param user + * @param botType + * @return + */ + public Session getSession(String user, String botType) { + String sessionKey = getSessionKey(user, botType); if (sessions.containsKey(sessionKey)) { return sessions.get(sessionKey); } else { @@ -325,12 +338,27 @@ public Session getSession(String userName, String botName) { } } - public void removePredicate(String userName, String predicateName) { - removePredicate(userName, getCurrentBotName(), predicateName); + /** + * remove a specific user and current bot types predicate + */ + public void removePredicate(String user, String predicateName) { + removePredicate(user, getBotType(), predicateName); } - public void removePredicate(String userName, String botName, String predicateName) { - getSession(userName, botName).remove(predicateName); + /** + * remove an explicit user and botType's predicate + * + * @param user + * @param botType + * @param name + */ + public void removePredicate(String user, String botType, String name) { + Session session = getSession(user, botType); + if (session != null) { + session.remove(name); + } else { + error("could not remove predicate %s from session %s<->%s session does not exist", user, botType, name); + } } /** @@ -342,8 +370,15 @@ public void removePredicate(String userName, String botName, String predicateNam * value to add to the set */ public void addToSet(String setName, String setValue) { + if (setName == null || setValue == null) { + error("addToSet(%s,%s) cannot have name or value null", setName, setValue); + return; + } + setName = setName.toLowerCase().trim(); + setValue = setValue.trim(); + // add to the set for the bot. - Bot bot = getBot(getCurrentBotName()); + Bot bot = getBot(getBotType()); AIMLSet updateSet = bot.setMap.get(setName); setValue = setValue.toUpperCase().trim(); if (updateSet != null) { @@ -371,8 +406,17 @@ public void addToSet(String setName, String setValue) { * - the value */ public void addToMap(String mapName, String key, String value) { + + if (mapName == null || key == null || value == null) { + error("addToMap(%s,%s,%s) mapname, key or value cannot be null", mapName, key, value); + return; + } + mapName = mapName.toLowerCase().trim(); + key = key.toUpperCase().trim(); + + // add an entry to the map. - Bot bot = getBot(getCurrentBotName()); + Bot bot = getBot(getBotType()); AIMLMap updateMap = bot.mapMap.get(mapName); key = key.toUpperCase().trim(); if (updateMap != null) { @@ -388,41 +432,87 @@ public void addToMap(String mapName, String key, String value) { } } - public void setPredicate(String predicateName, String predicateValue) { - setPredicate(getCurrentUserName(), predicateName, predicateValue); + public void setPredicate(String name, String value) { + setPredicate(getUsername(), name, value); } - public void setPredicate(String userName, String predicateName, String predicateValue) { - setPredicate(userName, getCurrentBotName(), predicateName, predicateValue); + /** + * Sets a specific user and current bot predicate to a value. Useful when + * setting predicate values of a session, when the user previously was an + * unknown human to a new or previously known user. + * + * @param username + * @param name + * @param value + */ + public void setPredicate(String username, String name, String value) { + setPredicate(username, getBotType(), name, value); } - public void setPredicate(String userName, String botName, String predicateName, String predicateValue) { - Session session = getSession(userName, botName); + /** + * Sets a predicate for a session keyed by username and bottype. If the + * session does not currently exist, it will make a new session for that user. + * + * @param username + * @param botType + * @param name + * @param value + */ + public void setPredicate(String username, String botType, String name, String value) { + Session session = getSession(username, botType); if (session != null) { - session.setPredicate(predicateName, predicateValue); + session.setPredicate(name, value); + } else { + // attempt to create a session if it doesn't exist + session = startSession(username, botType, false); + if (session != null) { + session.setPredicate(name, value); + } else { + error("could not create session"); + } } } - @Deprecated - public void unsetPredicate(String userName, String predicateName) { - removePredicate(userName, getCurrentBotName(), predicateName); + @Deprecated /* use removePredicate */ + public void unsetPredicate(String username, String predicateName) { + removePredicate(username, getBotType(), predicateName); } + /** + * Get a predicate's value for the current session + * + * @param predicateName + * @return + */ public String getPredicate(String predicateName) { - return getPredicate(getCurrentUserName(), predicateName); + return getPredicate(getUsername(), predicateName); } - public String getPredicate(String userName, String predicateName) { - return getPredicate(userName, getCurrentBotName(), predicateName); + /** + * get a specified users's predicate value for the current botType session + * + * @param username + * @param predicateName + * @return + */ + public String getPredicate(String username, String predicateName) { + return getPredicate(username, getBotType(), predicateName); } - public String getPredicate(String userName, String botName, String predicateName) { - Session s = getSession(userName, botName); + /** + * With a session key, get a specific predicate value + * + * @param username + * @param botType + * @param predicateName + * @return + */ + public String getPredicate(String username, String botType, String predicateName) { + Session s = getSession(username, botType); if (s == null) { - // If that session doesn't currently exist, let's start it. - s = startSession(userName, botName); + s = startSession(username, botType, false); if (s == null) { - log.warn("Error starting programAB session between bot {} and user {}", userName, botName); + log.warn("Error starting programAB session between bot {} and user {}", username, botType); return null; } } @@ -432,8 +522,8 @@ public String getPredicate(String userName, String botName, String predicateName /** * Only respond if the last response was longer than delay ms ago * - * @param userName - * - current userName + * @param username + * - current username * @param text * - text to get a response * @param delay @@ -442,11 +532,11 @@ public String getPredicate(String userName, String botName, String predicateName * @throws IOException * boom */ - public Response troll(String userName, String text, Long delay) throws IOException { - Session session = getSession(userName, getCurrentBotName()); + public Response troll(String username, String text, Long delay) throws IOException { + Session session = getSession(username, getBotType()); long delta = System.currentTimeMillis() - session.lastResponseTime.getTime(); if (delta > delay) { - return getResponse(userName, text); + return getResponse(username, text); } else { log.info("Skipping response, minimum delay since previous response not reached."); return null; @@ -461,13 +551,13 @@ public boolean isEnableAutoConversation() { * Return a list of all patterns that the current AIML Bot knows to match * against. * - * @param botName + * @param botType * the bots name from which to return it's patterns. * @return a list of all patterns loaded into the aiml brain */ - public ArrayList listPatterns(String botName) { + public ArrayList listPatterns(String botType) { ArrayList patterns = new ArrayList(); - Bot bot = getBot(botName); + Bot bot = getBot(botType); for (Category c : bot.brain.getCategories()) { patterns.add(c.getPattern()); } @@ -508,6 +598,9 @@ public Response publishResponse(Response response) { @Override public String publishText(String text) { + if (text == null || text.length() == 0) { + return ""; + } // TODO: this should not be done here. // clean up whitespaces & cariage return text = text.replaceAll("\\n", " "); @@ -535,21 +628,21 @@ public String publishOOBText(String oobText) { /** * This method will close the current bot, and reload it from AIML It then - * will then re-establish only the session associated with userName. + * will then re-establish only the session associated with username. * - * @param userName + * @param username * username for the session - * @param botName + * @param botType * the bot name being chatted with * @throws IOException * boom * */ - public void reloadSession(String userName, String botName) throws IOException { - Session session = getSession(userName, botName); + public void reloadSession(String username, String botType) throws IOException { + Session session = getSession(username, botType); if (session != null) { session.reload(); - info("reloaded session %s <-> %s ", userName, botName); + info("reloaded session %s <-> %s ", username, botType); } } @@ -559,7 +652,7 @@ public void reloadSession(String userName, String botName) throws IOException { * @return */ public Map getPredicates() { - return getPredicates(config.currentUserName, config.currentBotName); + return getPredicates(config.username, config.botType); } /** @@ -567,8 +660,8 @@ public Map getPredicates() { * * @return */ - public Map getPredicates(String userName, String botName) { - Session session = getSession(userName, botName); + public Map getPredicates(String username, String botType) { + Session session = getSession(username, botType); if (session != null) { return session.getPredicates(); } @@ -585,15 +678,15 @@ public void savePredicates() { } public void setEnableAutoConversation(boolean enableAutoConversation) { - getSession().enableTrolling = enableAutoConversation; + getConfig().enableTrolling = enableAutoConversation; } - public void setMaxConversationDelay(int maxConversationDelay) { - getSession().maxConversationDelay = maxConversationDelay; + public boolean getEnableAutoConversation() { + return getConfig().enableTrolling; } - public void setProcessOOB(boolean processOOB) { - getSession().processOOB = processOOB; + public void setMaxConversationDelay(int maxConversationDelay) { + getConfig().maxConversationDelay = maxConversationDelay; } /** @@ -605,128 +698,105 @@ public void setProcessOOB(boolean processOOB) { * value to set for current bot/session */ public void setBotProperty(String name, String value) { - setBotProperty(getCurrentBotName(), name, value); + setBotProperty(getBotType(), name, value); } /** * set a bot property - the result will be serialized to config/properties.txt * - * @param botName + * @param botType * bot name * @param name * bot property name * @param value * value to set the property too */ - public void setBotProperty(String botName, String name, String value) { - info("setting %s property %s:%s", getCurrentBotName(), name, value); - BotInfo botInfo = getBotInfo(botName); + public void setBotProperty(String botType, String name, String value) { + info("setting %s property %s:%s", getBotType(), name, value); + BotInfo botInfo = getBotInfo(botType); name = name.trim(); value = value.trim(); botInfo.setProperty(name, value); } public void removeBotProperty(String name) { - removeBotProperty(getCurrentBotName(), name); + removeBotProperty(getBotType(), name); } - public void removeBotProperty(String botName, String name) { - info("removing %s property %s", getCurrentBotName(), name); - BotInfo botInfo = getBotInfo(botName); + public void removeBotProperty(String botType, String name) { + info("removing %s property %s", getBotType(), name); + BotInfo botInfo = getBotInfo(botType); botInfo.removeProperty(name); } - public Session startSession() throws IOException { - return startSession(config.currentUserName); - } - - // FIXME - it should just set the current userName only - public Session startSession(String userName) throws IOException { - return startSession(userName, getCurrentBotName()); - } - - public Session startSession(String userName, String botName) { - return startSession(null, userName, botName, MagicBooleans.defaultLocale); - } - - @Deprecated /* path included for legacy */ - public Session startSession(String path, String userName, String botName) { - return startSession(path, userName, botName, MagicBooleans.defaultLocale); + /** + * Setting a session is only setting a key, to the active user and bot, its + * not starting a session, which is a different process done threw + * startSession. + * + * Sets username and botType. The session will be started if it can be when a + * getResponse is processed. "Active" session is just where the session key + * exists and is currently set via username and botType + * + * @param username + * @param botType + * @return + */ + public void setSession(String username, String botType) { + // replacing "focus" so + // current name and bottype is the + // one that will be used + setUsername(username); + setBotType(botType); } /** * Load the AIML 2.0 Bot config and start a chat session. This must be called - * after the service is created. + * after the service is created. If the session does not exist it will be + * created. If the session does exist then that session will be used. * - * @param path - * - the path to the ProgramAB directory where the bots aiml and - * config reside - * @param userName + * config.username and config.botType will be set in memory the specified + * values. The "current" session will be this session. + * + * @param username * - The new user name - * @param botName + * @param botType * - The name of the bot to load. (example: alice2) - * @param locale - * - The locale of the bot to ensure the aiml is loaded (mostly for - * Japanese support) FIXME - local is defined in the bot, - * specifically config/mrl.properties * - * reasons to deprecate: - * - * 1. I question the need to expose this externally at all - if the - * user uses getResponse(username, botname, text) then a session can - * be auto-started - there is really no reason not to auto-start. - * - * 2. path is completely invalid here - * - * 3. Locale is completely invalid - it is now part of the bot - * description in mrl.properties and shouldn't be defined externally, - * unles its pulled from Runtime * @return the session that is started */ - public Session startSession(String path, String userName, String botName, java.util.Locale locale) { - - /* - * not wanted or needed if (path != null) { addBotPath(path); } - */ - - Session session = getSession(userName, botName); + public Session startSession(String username, String botType, boolean setAsCurrent) { - if (session != null) { - log.info("session {} already exists - will use it", getSessionKey(userName, botName)); - setCurrentSession(userName, botName); - return session; + if (username == null || botType == null) { + error("username nor bot type can be null"); + return null; } - // create a new session - log.info("creating new sessions"); - BotInfo botInfo = getBotInfo(botName); - if (botInfo == null) { - error("cannot create session %s is not a valid botName", botName); + if (!bots.containsKey(botType)) { + error("bot type %s is not valid, list of possible types are %s", botType, bots.keySet()); return null; } - session = new Session(this, userName, botInfo); - sessions.put(getSessionKey(userName, botName), session); + if (setAsCurrent) { + // really sets the key of the active session username <-> botType + // but next getResponse will use this session + setSession(username, botType); + } - log.info("Started session for bot botName:{} , userName:{}", botName, userName); - setCurrentSession(userName, botName); - return session; - } + String sessionKey = getSessionKey(username, botType); + if (sessions.containsKey(sessionKey)) { + log.info("session exists returning existing"); + return sessions.get(sessionKey); + } - /** - * setting the current session is equivalent to setting current user name and - * current bot name - * - * @param userName - * username - * @param botName - * botname - * - */ - public void setCurrentSession(String userName, String botName) { - setCurrentUserName(userName); - setCurrentBotName(botName); + log.info("creating new session {}<->{} replacing {}", username, botType, setAsCurrent); + BotInfo botInfo = getBotInfo(botType); + Session session = new Session(this, username, botInfo); + sessions.put(sessionKey, session); + + // get session + return getSession(); } /** @@ -736,7 +806,7 @@ public void setCurrentSession(String userName, String botName) { * @param c */ public void addCategory(Category c) { - Bot bot = getBot(getCurrentBotName()); + Bot bot = getBot(getBotType()); bot.brain.addCategory(c); } @@ -758,16 +828,18 @@ public void addCategory(String pattern, String template, String that) { public void addCategory(String pattern, String template) { addCategory(pattern, template, "*"); } - + /** - * Verifies and adds a new path to the search directories for bots + * Verifies and adds a new path to the search directories for bots. Bots of + * aiml live in directories which represent their "type" The directory names + * must be unique. * * @param path * the path to add a bot from * @return the path if successful. o/w null * */ - public String addBotPath(String path) { + public String addBot(String path) { // verify the path is valid File botPath = new File(path); File verifyAiml = new File(FileIO.gluePaths(path, "aiml")); @@ -783,12 +855,12 @@ public String addBotPath(String path) { BotInfo botInfo = new BotInfo(this, botPath); - // key'ing on "path" probably would be better and only displaying "name" - // then there would be no put/collisions only duplicate names - // (preferrable) + if (bots.containsKey(botInfo.botType)) { + log.info("replacing bot %s with new bot definition", botInfo.botType); + } - bots.put(botInfo.name, botInfo); - botInfo.img = getBotImage(botInfo.name); + bots.put(botInfo.botType, botInfo); + botInfo.img = getBotImage(botInfo.botType); broadcastState(); } else { @@ -798,41 +870,79 @@ public String addBotPath(String path) { return path; } - @Deprecated /* for legacy - use addBotsDir */ - public String setPath(String path) { - // This method is not good, because it doesn't take the full path - // from input and there is a buried "hardcoded" value which no one knows - // about - addBotsDir(path + File.separator + "bots"); - - return path; + @Deprecated /* use setBotType */ + public void setCurrentBotName(String botType) { + setBotType(botType); } - public void setCurrentBotName(String botName) { - config.currentBotName = botName; - invoke("getBotImage", botName); - broadcastState(); + /** + * Sets the current bot type to a set of aiml folders previously added via + * configuration or through the addBot(path) function. + * + * You can get a list of possible configured bot types through the method + * getBots() + * + * @param botType + */ + public void setBotType(String botType) { + if (botType == null) { + error("bot type cannot be null"); + return; + } + + if (bots.size() == 0) { + error("bot paths must be set before a bot type is set"); + } + + if (!bots.containsKey(botType)) { + error("cannot set bot %s, no valid type found, possible values are %s", botType, bots.keySet()); + return; + } + String prev = config.botType; + config.botType = botType; + if (!botType.equals(prev)) { + invoke("getBotImage", botType); + broadcastState(); + } } - public void setCurrentUserName(String currentUserName) { - config.currentUserName = currentUserName; - broadcastState(); + public void setUsername(String username) { + if (username == null) { + error("username cannot be null"); + return; + } + String prev = config.username; + config.username = username; + if (!username.equals(prev)) { + broadcastState(); + } } - public Session getCurrentSession() { - return sessions.get(getSessionKey(getCurrentUserName(), getCurrentBotName())); + public String getSessionKey(String username, String botType) { + return String.format("%s <-> %s", username, botType); } - public String getSessionKey(String userName, String botName) { - return String.format("%s <-> %s", userName, botName); + /** + * Simple preferred way to get the user's name + * + * @return + */ + public String getUsername() { + return config.username; } + @Deprecated /* of course it will be "current" - use getUser() */ public String getCurrentUserName() { - return config.currentUserName; + return getUsername(); } + @Deprecated /* use getBotType() */ public String getCurrentBotName() { - return config.currentBotName; + return getBotType(); + } + + public String getBotType() { + return config.botType; } /** @@ -930,17 +1040,44 @@ public boolean setPeerSearch(boolean b) { @Override public void startService() { - super.startService(); + try { + super.startService(); - logPublisher = new SimpleLogPublisher(this); - logPublisher.filterClasses(new String[] { "org.alicebot.ab.Graphmaster", "org.alicebot.ab.MagicBooleans", "class org.myrobotlab.programab.MrlSraixHandler" }); - Logging logging = LoggingFactory.getInstance(); - logging.setLevel("org.alicebot.ab.Graphmaster", "DEBUG"); - logging.setLevel("org.alicebot.ab.MagicBooleans", "DEBUG"); - logging.setLevel("class org.myrobotlab.programab.MrlSraixHandler", "DEBUG"); - logPublisher.start(); + // scan for bots + if (config.botDir != null) { + scanForBots(config.botDir); + } + + // explicitly setting bots overrides scans + if (config.bots != null && config.bots.size() > 0) { + for (String botPath : config.bots) { + addBot(botPath); + } + } - scanForBots(getResourceDir()); + if (config.username != null) { + setUsername(config.username); + } + + if (config.botType != null) { + setBotType(config.botType); + } + + if (config.startTopic != null) { + setTopic(config.startTopic); + } + + logPublisher = new SimpleLogPublisher(this); + logPublisher.filterClasses(new String[] { "org.alicebot.ab.Graphmaster", "org.alicebot.ab.MagicBooleans", "class org.myrobotlab.programab.MrlSraixHandler" }); + Logging logging = LoggingFactory.getInstance(); + logging.setLevel("org.alicebot.ab.Graphmaster", "DEBUG"); + logging.setLevel("org.alicebot.ab.MagicBooleans", "DEBUG"); + logging.setLevel("class org.myrobotlab.programab.MrlSraixHandler", "DEBUG"); + logPublisher.start(); + + } catch (Exception e) { + error(e); + } } @@ -992,7 +1129,7 @@ public String publishLog(String msg) { } public BotInfo getBotInfo() { - return getBotInfo(config.currentBotName); + return getBotInfo(config.botType); } /** @@ -1002,21 +1139,25 @@ public BotInfo getBotInfo() { * boom * */ - public void reload() throws IOException { - reloadSession(getCurrentUserName(), getCurrentBotName()); + public void reload() { + try { + reloadSession(getUsername(), getBotType()); + } catch (Exception e) { + error(e); + } } public String getBotImage() { - return getBotImage(getCurrentBotName()); + return getBotImage(getBotType()); } - public String getBotImage(String botName) { + public String getBotImage(String botType) { BotInfo botInfo = null; String path = null; try { - botInfo = getBotInfo(botName); + botInfo = getBotInfo(botType); if (botInfo != null) { path = FileIO.gluePaths(botInfo.path.getAbsolutePath(), "bot.png"); File check = new File(path); @@ -1026,16 +1167,16 @@ public String getBotImage(String botName) { } } catch (Exception e) { - info("image for %s cannot be found %s", botName, e.getMessage()); + info("image for %s cannot be found %s", botType, e.getMessage()); } return getResourceImage("default.png"); } - public String getAimlFile(String botName, String name) { - BotInfo botInfo = getBotInfo(botName); + public String getAimlFile(String botType, String name) { + BotInfo botInfo = getBotInfo(botType); if (botInfo == null) { - error("cannot get bot %s", botName); + error("cannot get bot %s", botType); return null; } @@ -1053,10 +1194,10 @@ public String getAimlFile(String botName, String name) { return ret; } - public void saveAimlFile(String botName, String filename, String data) { - BotInfo botInfo = getBotInfo(botName); + public void saveAimlFile(String botType, String filename, String data) { + BotInfo botInfo = getBotInfo(botType); if (botInfo == null) { - error("cannot get bot %s", botName); + error("cannot get bot %s", botType); return; } @@ -1090,44 +1231,10 @@ public ProgramABConfig getConfig() { return config; } - @Override - public ProgramABConfig apply(ProgramABConfig c) { - super.apply(c); - if (c.bots != null && c.bots.size() > 0) { - // bots.clear(); - for (String botPath : c.bots) { - addBotPath(botPath); - } - } - - if (c.botDir == null) { - c.botDir = getResourceDir(); - } - - List botsFromScanning = scanForBots(c.botDir); - for (File file : botsFromScanning) { - addBotPath(file.getAbsolutePath()); - } - - if (c.currentUserName != null) { - setCurrentUserName(c.currentUserName); - } - - if (c.currentBotName != null) { - setCurrentBotName(c.currentBotName); - } - - if (c.startTopic != null) { - setTopic(c.startTopic); - } - - - return c; - } - public static void main(String args[]) { try { LoggingFactory.init("INFO"); + Runtime.startConfig("dev"); // Runtime.start("gui", "SwingGui"); Runtime runtime = Runtime.getInstance(); @@ -1164,7 +1271,7 @@ public static void main(String args[]) { } } - public void addBotsDir(String path) { + public void addBots(String path) { if (path == null) { error("set path can not be null"); @@ -1182,14 +1289,14 @@ public void addBotsDir(String path) { if (check.exists() && check.isDirectory()) { log.info("found %d possible bot directories", check.listFiles().length); for (File f : check.listFiles()) { - addBotPath(f.getAbsolutePath()); + addBot(f.getAbsolutePath()); } } } @Override - synchronized public void onChangePredicate(Chat chat, String predicateName, String result) { - log.info("{} on predicate change {}={}", chat.bot.name, predicateName, result); + synchronized public void onChangePredicate(Chat chat, String predicateName, String value) { + log.info("{} on predicate change {}={}", chat.bot.name, predicateName, value); // a little janky because program-ab doesn't know the predicate filename, // because it does know the "user" @@ -1200,7 +1307,7 @@ synchronized public void onChangePredicate(Chat chat, String predicateName, Stri for (Session s : sessions.values()) { if (s.chat == chat) { // found session saving predicates - invoke("publishPredicate", s, predicateName, result); + invoke("publishPredicate", s, predicateName, value); s.savePredicates(); return; } @@ -1221,17 +1328,19 @@ synchronized public void onChangePredicate(Chat chat, String predicateName, Stri * - new value of predicate * @return */ - public PredicateEvent publishPredicate(Session session, String name, String value) { - PredicateEvent event = new PredicateEvent(); - event.id = String.format("%s<->%s", session.userName, session.botInfo.name); - event.userName = session.userName; - event.botName = session.botInfo.name; + public Event publishPredicate(Session session, String name, String value) { + Event event = new Event(); + event.id = String.format("%s<->%s", session.username, session.botInfo.botType); + event.user = session.username; + event.botname = session.botInfo.botType; 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)); + Event topicChange = new Event(getName(), session.username, session.botInfo.botType, value); + invoke("publishTopic", topicChange); session.currentTopic = value; + topicHistory.add(topicChange); } return event; @@ -1302,14 +1411,14 @@ public void sleep() { @Override public void onUtterance(Utterance utterance) throws Exception { - - log.info("Utterance Received " + utterance); + + log.info("utterance received {}", utterance); boolean talkToBots = false; // TODO: reconcile having different name between the discord bot username // and the programab bot name. Mr. Turing is not actually Alice.. and vice // versa. - String botName = utterance.channelBotName; + String botType = utterance.channelBotName; // prevent bots going off the rails if (utterance.isBot && talkToBots) { @@ -1318,7 +1427,7 @@ public void onUtterance(Utterance utterance) throws Exception { } // Don't talk to myself, though I should be a bot.. - if (utterance.username.contentEquals(botName)) { + if (utterance.username != null && utterance.username.contentEquals(botType)) { log.info("Don't talk to myself."); return; } @@ -1332,7 +1441,7 @@ 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. - config.sleep = (config.sleep || utterance.text.contains("@")) && !utterance.text.contains(botName); + config.sleep = (config.sleep || utterance.text.contains("@")) && !utterance.text.contains(botType); if (!config.sleep) { shouldIRespond = true; } @@ -1346,7 +1455,7 @@ public void onUtterance(Utterance utterance) throws Exception { String utteranceDisp = utterance.text; // let's strip the @+botname from the beginning of the utterance i guess. // Strip the botname from the utterance passed to programab. - utteranceDisp = utteranceDisp.replace("@" + botName, ""); + utteranceDisp = utteranceDisp.replace("@" + botType, ""); Response resp = getResponse(utterance.username, utteranceDisp); if (resp != null && !StringUtils.isEmpty(resp.msg)) { // Ok.. now what? respond to the user ... @@ -1367,17 +1476,18 @@ 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(); + 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); @@ -1385,7 +1495,7 @@ public void onConfig(ServiceConfig cfg) { setPredicate("cfg_" + entry.getKey(), entry.getValue().toString()); } } - + invoke("getPredicates"); } @@ -1394,24 +1504,76 @@ public Utterance publishUtterance(Utterance utterance) { return utterance; } - public TopicChange publishTopic(TopicChange topicChange) { + /** + * New topic published when it changes + * + * @param topicChange + * @return + */ + public Event publishTopic(Event topicChange) { return topicChange; } - public String getTopic() { - return getPredicate(getCurrentUserName(), "topic"); + public String getTopic() { + return getPredicate(getUsername(), "topic"); } - - public String getTopic(String username) { + + public String getTopic(String username) { return getPredicate(username, "topic"); } - - public void setTopic(String username, String topic) { + + public void setTopic(String username, String topic) { setPredicate(username, "topic", topic); } - - public void setTopic(String topic) { - setPredicate(getCurrentUserName(), "topic", topic); + + public void setTopic(String topic) { + setPredicate(getUsername(), "topic", topic); + } + + /** + * Published when a new session is created + * + * @param session + * @return + */ + public Event publishSession(Event session) { + return session; + } + + /** + * clear all sessions + */ + public void clear() { + log.info("clearing sessions"); + sessions.clear(); + } + + /** + *
+   * A mechanism to publish a message directly from aiml.
+   * The subscriber can interpret the message and do something with it.
+   * In the case of InMoov for example, the unaddressed messages are processed
+   * as python method calls. This remove direct addressing from the aiml!
+   * And allows a great amount of flexibility on how the messages are
+   * interpreted, without polluting the aiml or ProgramAB.
+   * 
+   * The oob syntax is:
+   *  <oob>
+   *    <mrljson>
+   *        [{method:on_new_user, data:[{"name":"<star/>"}]}]
+   *    </mrljson>
+   * </oob>
+   * 
+   * 
+   * Full typed parameters are supported without conversions.
+   * 
+   * 
+ * + * @param msg + * @return + */ + public Message publishMessage(Message msg) { + return msg; } } diff --git a/src/main/java/org/myrobotlab/service/Py4j.java b/src/main/java/org/myrobotlab/service/Py4j.java index 909270e77c..66859f4bdf 100644 --- a/src/main/java/org/myrobotlab/service/Py4j.java +++ b/src/main/java/org/myrobotlab/service/Py4j.java @@ -155,6 +155,8 @@ public void run() { public Py4j(String n, String id) { super(n, id); + // ready when connected + ready = false; } /** @@ -199,7 +201,7 @@ public void connectionStarted(Py4JServerConnection gatewayConnection) { try { log.info("connectionStarted {}", gatewayConnection.toString()); clients.put(getClientKey(gatewayConnection), new Py4jClient()); - + ready = true; info("connection started"); invoke("getClients"); } catch (Exception e) { @@ -220,6 +222,7 @@ public void connectionStopped(Py4JServerConnection gatewayConnection) { * @param code The Python code to execute in the interpreter. */ public void exec(String code) { + log.info(String.format("exec %s", code)); try { if (handler != null) { handler.exec(code); @@ -340,6 +343,7 @@ public boolean preProcessHook(Message msg) { String json = CodecUtils.toJson(msg); // handler.invoke(msg.method, json); + log.info(String.format("handler %s", json)); handler.send(json); } else { error("preProcessHook handler is null"); @@ -566,7 +570,6 @@ public void stopService() { * * @param scriptName * @param code - * @return */ public void updateScript(String scriptName, String code) { if (openedScripts.containsKey(scriptName)) { @@ -606,6 +609,7 @@ public void connect(String uri) throws Exception { public void sendRemote(Message msg) throws Exception { log.info("sendRemote"); String jsonMsg = CodecUtils.toJson(msg); + log.info(String.format("sendRemote %s")); handler.send(jsonMsg); } diff --git a/src/main/java/org/myrobotlab/service/Python.java b/src/main/java/org/myrobotlab/service/Python.java index e8c2fee02e..c17a905f23 100644 --- a/src/main/java/org/myrobotlab/service/Python.java +++ b/src/main/java/org/myrobotlab/service/Python.java @@ -180,7 +180,7 @@ public void run() { public final static transient Logger log = LoggerFactory.getLogger(Python.class); // TODO this needs to be moved into an actual cache if it is to be used // Cache of compile python code - private static final transient HashMap objectCache = new HashMap(); + private final transient HashMap objectCache = new HashMap(); private static final long serialVersionUID = 1L; @@ -200,7 +200,7 @@ public void run() { * @param interp * @return */ - private static synchronized PyObject getCompiledMethod(String name, String code, PythonInterpreter interp) { + private synchronized PyObject getCompiledMethod(String name, String code, PythonInterpreter interp) { // TODO change this from a synchronized method to a few blocks to // improve concurrent performance if (objectCache.containsKey(name)) { @@ -334,7 +334,7 @@ public void closeScript(String file) { /** * append more Python to the current script * - * @param data + * @param code * the code to append * @return the resulting concatenation */ @@ -759,7 +759,6 @@ public void saveScript(String scriptName, String code) throws IOException { * upserts a script in memory * @param file * @param code - * @return */ public void updateScript(String file, String code) { if (openedScripts.containsKey(file)) { @@ -889,8 +888,6 @@ public void init() { 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" @@ -900,6 +897,8 @@ public void init() { PyObject compiled = getCompiledMethod("initializePython", selfReferenceScript, interp); interp.exec(compiled); + attachPythonConsole(); + // initialize all the pre-existing service before python was created Map services = Runtime.getLocalServices(); for (ServiceInterface service : services.values()) { diff --git a/src/main/java/org/myrobotlab/service/Random.java b/src/main/java/org/myrobotlab/service/Random.java index 567431d0c3..8a9e1875fb 100644 --- a/src/main/java/org/myrobotlab/service/Random.java +++ b/src/main/java/org/myrobotlab/service/Random.java @@ -107,6 +107,10 @@ public long getRandom(long min, long max) { public double getRandom(double min, double max) { return min + (Math.random() * (max - min)); } + + public RandomMessage getTask(String taskName) { + return randomData.get(taskName); + } public void addRandom(String taskName, long minIntervalMs, long maxIntervalMs, String name, String method, Integer... values) { addRandom(taskName, minIntervalMs, maxIntervalMs, name, method, toRanges((Object[]) values)); @@ -224,7 +228,7 @@ public void run() { // minimal interval time for processor to check // and see if any random event needs processing - sleep(200); + sleep(config.rate); for (String key : randomData.keySet()) { long now = System.currentTimeMillis(); @@ -349,6 +353,9 @@ public RandomMessage remove(String name, String method) { } public RandomMessage remove(String key) { + if (!randomData.containsKey(key)) { + error("key %s does not exist"); + } return randomData.remove(key); } @@ -436,6 +443,16 @@ public Map getRandomEvents(){ public RandomMessage getRandomEvent(String key) { return randomData.get(key); } + + /** + * disables all the individual tasks + */ + public void disableAll() { + for (RandomMessage data : randomData.values()) { + data.enabled = false; + } + broadcastState(); + } public static void main(String[] args) { try { diff --git a/src/main/java/org/myrobotlab/service/RasPi.java b/src/main/java/org/myrobotlab/service/RasPi.java index ea6e33444f..7ce96ce599 100644 --- a/src/main/java/org/myrobotlab/service/RasPi.java +++ b/src/main/java/org/myrobotlab/service/RasPi.java @@ -709,14 +709,19 @@ public void startService() { log.info("Initiating i2c"); I2CFactory.getInstance(Integer.parseInt(bus)); - log.info("i2c initiated on bus {}", bus); - addTask(1000, "scan"); - log.info("read task initialized"); - addTask(1000, "read"); + if (config.pollOnStart) { + log.info("i2c initiated on bus {}", bus); + addTask(1000, "scan"); + + log.info("read task initialized"); + addTask(1000, "read"); + } } catch (IOException e) { log.error("i2c initiation failed", e); + } catch (UnsupportedOperationException er) { + log.warn("invalid operation initializing i2c - is platform not raspberry pi?"); } catch (Exception e) { // an error in the constructor won't get broadcast - so we need Runtime to // do it diff --git a/src/main/java/org/myrobotlab/service/Runtime.java b/src/main/java/org/myrobotlab/service/Runtime.java index 5a86472a6b..c1e5e95557 100644 --- a/src/main/java/org/myrobotlab/service/Runtime.java +++ b/src/main/java/org/myrobotlab/service/Runtime.java @@ -18,12 +18,16 @@ import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; @@ -32,7 +36,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Queue; import java.util.Random; import java.util.Set; import java.util.TimeZone; @@ -44,7 +47,6 @@ import org.myrobotlab.codec.CodecUtils; import org.myrobotlab.codec.CodecUtils.ApiDescription; import org.myrobotlab.codec.ForeignProcessUtils; -import org.myrobotlab.framework.CmdConfig; import org.myrobotlab.framework.CmdOptions; import org.myrobotlab.framework.DescribeQuery; import org.myrobotlab.framework.DescribeResults; @@ -61,6 +63,7 @@ import org.myrobotlab.framework.Registration; import org.myrobotlab.framework.Service; import org.myrobotlab.framework.ServiceReservation; +import org.myrobotlab.framework.StartYml; import org.myrobotlab.framework.StaticType; import org.myrobotlab.framework.Status; import org.myrobotlab.framework.interfaces.ConfigurableService; @@ -70,7 +73,6 @@ import org.myrobotlab.framework.repo.IvyWrapper; import org.myrobotlab.framework.repo.Repo; import org.myrobotlab.framework.repo.ServiceData; -import org.myrobotlab.framework.repo.ServiceDependency; import org.myrobotlab.io.FileIO; import org.myrobotlab.logging.AppenderType; import org.myrobotlab.logging.LoggerFactory; @@ -134,15 +136,7 @@ public class Runtime extends Service implements MessageListener, * a registry of all services regardless of which environment they came from - * each must have a unique name */ - static private final Map registry = new TreeMap<>(); - - /** - * A plan is a request to runtime to change the system. Typically its to ask - * to start and configure new services. The master plan is an accumulation of - * all these requests. - */ - @Deprecated /* use the filesystem only no memory plan */ - protected final Plan masterPlan = new Plan("runtime"); + static private Map registry = new TreeMap<>(); /** * thread for non-blocking install of services @@ -178,22 +172,14 @@ public class Runtime extends Service implements MessageListener, "org.myrobotlab.service.interfaces.ServiceLifeCycleListener", "org.myrobotlab.framework.interfaces.StatePublisher")); protected final Set serviceTypes = new HashSet<>(); - + /** * The directory name currently being used for config. This is NOT full path * name. It cannot be null, it cannot have "/" or "\" in the name - it has to * be a valid file name for the OS. It's defaulted to "default". Changed often */ - @Deprecated /* use startyml.config */ protected String configName = "default"; - /** - * default parent path of configPath - rarely changed FIXME - don't make it - * static ! - */ - @Deprecated /* use startYml.configRoot */ - static protected String CONFIG_ROOT = "data" + fs + "config"; - /** * State variable reporting if runtime is currently starting services from * config. If true you can find which config from runtime.getConfigName() @@ -231,9 +217,15 @@ public class Runtime extends Service implements MessageListener, static private final String RUNTIME_NAME = "runtime"; + /** + * user's data directory + */ static public final String DATA_DIR = "data"; - static private boolean autoAcceptLicense = true; // at the moment + /** + * default parent path of configPath static ! + */ + final static protected String ROOT_CONFIG_DIR = DATA_DIR + fs + "config"; /** * number of services created by this runtime @@ -256,7 +248,7 @@ public class Runtime extends Service implements MessageListener, /** * command line configuration */ - static CmdConfig startYml = new CmdConfig(); + static StartYml startYml = new StartYml(); /** * the platform (local instance) for this runtime. It must be a non-static as @@ -385,20 +377,23 @@ static public ServiceInterface create(String name) { static public synchronized ServiceInterface create(String name, String type) { try { - Runtime runtime = Runtime.getInstance(); ServiceInterface si = Runtime.getService(name); if (si != null) { return si; } // FIXME remove configName from loadService - Runtime.load(name, type); + Plan plan = Runtime.load(name, type); Runtime.check(name, type); // at this point - the plan should be loaded, now its time to create the // children peers // and parent service - createServicesFromPlan(Runtime.getPlan(), null, name); - return Runtime.getService(name); + createServicesFromPlan(plan, null, name); + si = Runtime.getService(name); + if (si == null) { + Runtime.getInstance().error("coult not create %s of type %s", name, type); + } + return si; } catch (Exception e) { runtime.error(e); } @@ -440,13 +435,13 @@ synchronized private static Map createServicesFromPlan sc.state = "CREATING"; ServiceInterface si = createService(service, sc.type, null); sc.state = "CREATED"; - // process the base listeners/subscription of ServiceConfig + // process the base listeners/subscription of ServiceConfig si.addConfigListeners(sc); if (si instanceof ConfigurableService) { try { - ((ConfigurableService)si).apply(sc); - } catch(Exception e) { - Runtime.getInstance().error("could not apply config of type %s to service %s, using default config", sc.type, si.getName(), sc.type); + ((ConfigurableService) si).apply(sc); + } catch (Exception e) { + Runtime.getInstance().error("could not apply config of type %s to service %s, using default config", sc.type, si.getName(), sc.type); } } createdServices.put(service, si); @@ -537,12 +532,11 @@ static public ServiceInterface createAndStart(String name, String type) { } /** - * creates and starts service from a cmd line object + * creates and starts services from a cmd line object * * @param services * - services to be created */ - @Deprecated /* who uses this ? */ public final static void createAndStartServices(List services) { if (services == null) { @@ -629,7 +623,6 @@ public void setAutoStart(boolean autoStart) throws IOException { startYml.id = getId(); startYml.enable = autoStart; startYml.config = configName; - startYml.configRoot = CONFIG_ROOT; FileIO.toFile("start.yml", CodecUtils.toYaml(startYml)); invoke("getStartYml"); } @@ -684,7 +677,13 @@ static private synchronized ServiceInterface createService(String name, String t fullName = String.format("%s@%s", name, inId); if (type == null) { - ServiceConfig sc = runtime.masterPlan.get(name); + ServiceConfig sc; + try { + sc = CodecUtils.readServiceConfig(runtime.getConfigName() + fs + name + ".yml"); + } catch (IOException e) { + runtime.error("could not find type for service %s", name); + return null; + } if (sc != null) { log.info("found type for {} in plan", name); type = sc.type; @@ -896,8 +895,8 @@ public static Runtime getInstance() { // a bit backwards - it loads after it been created // but its necessary because you need an runtime instance before you // load - - File cfgRoot = new File(CONFIG_ROOT); + + File cfgRoot = new File(ROOT_CONFIG_DIR); cfgRoot.mkdirs(); if (startYml.enable) { Runtime.load("runtime", "Runtime"); @@ -1158,7 +1157,8 @@ public static Map getRegistry() { } public static ServiceInterface getService(String inName) { - return getService(inName, new StaticType<>() {}); + return getService(inName, new StaticType<>() { + }); } public static > S getConfigurableService(String inName, StaticType serviceType) { @@ -1194,20 +1194,20 @@ public static S getService(String inName, StaticTyp * */ static public String[] getServiceNames() { - List si = getServices(); - String[] ret = new String[si.size()]; - for (int i = 0; i < ret.length; ++i) { - ServiceInterface s = si.get(i); - - if (isLocal(s.getFullName())) { - ret[i] = s.getName(); + Set ret = registry.keySet(); + String[] services = new String[ret.size()]; + + String localId = Platform.getLocalInstance().getId(); + int cnt = 0; + for (String fullname : ret) { + if (fullname.endsWith(String.format("@%s", localId))) { + services[cnt] = CodecUtils.getShortName(fullname); } else { - ret[i] = s.getFullName(); + services[cnt] = fullname; } - - // ret[i] = s.getFullName(); + ++cnt; } - return ret; + return services; } // Is it a good idea to modify all regex inputs? For example, if the pattern @@ -1367,7 +1367,7 @@ public static synchronized List getServicesFromInterface(Class * * @return */ - static public CmdConfig getStartYml() { + static public StartYml getStartYml() { return startYml; } @@ -1740,10 +1740,11 @@ public static synchronized Registration register(Registration registration) { return registration; } -// if (!ForeignProcessUtils.isValidTypeKey(registration.getTypeKey())) { -// log.error("Invalid type key being registered: " + registration.getTypeKey()); -// return null; -// } + // if (!ForeignProcessUtils.isValidTypeKey(registration.getTypeKey())) { + // log.error("Invalid type key being registered: " + + // registration.getTypeKey()); + // return null; + // } log.info("{}@{} registering at {} of type {}", registration.getName(), registration.getId(), Platform.getLocalInstance().getId(), registration.getTypeKey()); @@ -1783,6 +1784,17 @@ public static synchronized Registration register(Registration registration) { return null; } + // FIXME - probably some more clear definition about the requirements + // of remote + // service registration + // In general, there should be very few requirements if any, besides + // providing a + // name, and the proxy + // interface should be responsible for creating a minimal + // interpretation + // (ServiceInterface) for the remote + // service + // Class[] interfaces = registration.interfaces.stream().map(i -> { // try { // return Class.forName(i); @@ -1796,8 +1808,13 @@ public static synchronized Registration register(Registration registration) { // Proxy.newProxyInstance(Runtime.class.getClassLoader(), interfaces, // new ProxyServiceInvocationHandler(registration.getName(), // registration.getId())); - registration.service = ProxyFactory.createProxyService(registration); - log.info("Created proxy: " + registration.service); + try { + registration.service = ProxyFactory.createProxyService(registration); + log.info("Created proxy: " + registration.service); + } catch (Exception e) { + // at the moment preventing throw + Runtime.getInstance().error(e); + } } } @@ -1892,6 +1909,12 @@ public synchronized static boolean releaseService(String inName) { String name = getFullName(inName); + String id = CodecUtils.getId(name); + if (!id.equals(Platform.getLocalInstance().getId())) { + log.warn("will only release local services - %s is remote", name); + return false; + } + log.info("releasing service {}", name); if (!registry.containsKey(name)) { @@ -1912,49 +1935,23 @@ public synchronized static boolean releaseService(String inName) { if (si.isLocal()) { si.purgeTasks(); si.stopService(); - Plan plan = Runtime.getPlan(); - ServiceConfig sc = plan.get(inName); - if (sc == null) { - log.debug("service config not available for {}", inName); - } else { - sc.state = "STOPPED"; - } } else { if (runtime != null) { runtime.send(name, "releaseService"); } } - // FOR remote this isn't correct - it should wait for - // a message from the other runtime to say that its released - unregister(name); - Plan plan = Runtime.getPlan(); - ServiceConfig sc = plan.get(inName); - if (sc != null) { - sc.state = "RELEASED"; - // FIXME - TODO RELEASE PEERS ! which is any inName.* !!! - - // iterate through peers - // if (sc.autoStartPeers) { - // // get peers from meta data - // MetaData md = MetaData.get(sc.type); - // Map peers = md.getPeers(); - // log.info("auto start peers and {} of type {} has {} peers", inName, - // sc.type, peers.size()); - // // RECURSE ! - if we found peers and autoStartPeers is true - we start - // // all - // // the children up - // for (String peer : peers.keySet()) { - // // get actual Name - // String actualPeerName = getPeerName(peer, sc, peers, inName); - // if (actualPeerName != null && isStarted(actualPeerName) && - // si.autoStartedPeersContains(actualPeerName)) { - // release(actualPeerName); - // } - // } - // } + // recursive peer release + Map peers = si.getPeers(); + if (peers != null) { + for (Peer peer : peers.values()) { + release(peer.name); + } } + // FOR remote this isn't correct - it should wait for + // a message from the other runtime to say that its released + unregister(name); return true; } @@ -2005,11 +2002,19 @@ synchronized public static void unregister(String inName) { // FIXME - release autostarted peers ? - // last step - remove from registry - registry.remove(name); + // last step - remove from registry by making new registry + // thread safe way + Map removedService = new TreeMap<>(); + for (String key : registry.keySet()) { + if (!name.equals(key)) { + removedService.put(key, registry.get(key)); + } + } + registry = removedService; + // and config RuntimeConfig c = (RuntimeConfig) Runtime.getInstance().config; - c.registry.remove(CodecUtils.getShortName(name)); + c.remove(CodecUtils.getShortName(name)); log.info("released {}", name); } @@ -2100,7 +2105,7 @@ public void run() { * @param releaseRuntime * Whether the Runtime should also be released */ - static private void processRelease(boolean releaseRuntime) { + synchronized static private void processRelease(boolean releaseRuntime) { // reverse release to order of creation Collection local = getLocalServices().values(); @@ -2130,15 +2135,15 @@ static private void processRelease(boolean releaseRuntime) { // clean up remote ... the contract should // probably be just remove their references - do not // ask for them to be released remotely .. - for (String remoteService : registry.keySet()) { - if (!remoteService.equals(runtime.getFullName())) { - registry.remove(remoteService); - } - } + // in thread safe way - if (runtime != null && releaseRuntime) { + if (releaseRuntime && runtime != null) { runtime.releaseService(); - runtime = null; + } else { + // put runtime in new registry + Runtime.getInstance(); + registry = new TreeMap<>(); + registry.put(runtime.getFullName(), registry.get(runtime.getFullName())); } } @@ -2207,7 +2212,7 @@ public Integer publishShutdown(Integer seconds) { public List publishConfigList() { configList = new ArrayList<>(); - File configDirFile = new File(CONFIG_ROOT); + File configDirFile = new File(ROOT_CONFIG_DIR); if (!configDirFile.exists() || !configDirFile.isDirectory()) { error("%s config root does not exist", configDirFile.getAbsolutePath()); return configList; @@ -2261,35 +2266,7 @@ public static void releaseAllServicesExcept(HashSet saveMe) { * */ static public void release(String fullName) { - if (fullName == null) { - log.error("release(null)"); - return; - } - - ServiceInterface si = Runtime.getService(fullName); - if (si == null) { - log.info("{} already released", fullName); - return; - } - String shortName = si.getName(); - - // check for peers !! check in config or check in Plan ?!?!? - Map peers = si.getPeers(); - if (peers != null) { - for (String peerKey : peers.keySet()) { - Peer peer = peers.get(peerKey); - if (peer.autoStart) { - release(peer.name); - } - } - } - - // important to call service.releaseService because - // many are derived that take care of additional thread - // cleanup - Plan plan = Runtime.getPlan(); - plan.remove(shortName); - si.releaseService(); + releaseService(fullName); } /** @@ -2456,7 +2433,7 @@ public void connect(String url) { // runtime@{id} // subscribe to "describe" MRLListener listener = new MRLListener("describe", getFullName(), "onDescribe"); - Message msg = Message.createMessage(getFullName(), "runtime", "addListener", listener); + Message msg = Message.createMessage(getFullName(), "runtime", "addListener", listener); client2.send(CodecUtils.toJsonMsg(msg)); // send describe @@ -2578,15 +2555,16 @@ public void addRoute(String remoteId, String uuid, int metric) { * Start Runtime with the specified config * * @param configName + * * The name of the config file - * @return The Runtime singleton */ static public void startConfig(String configName) { setConfig(configName); Runtime runtime = Runtime.getInstance(); runtime.processingConfig = true; // multiple inbox threads not available - runtime.invoke("publishStartConfig", configName); - RuntimeConfig rtConfig = runtime.readServiceConfig(runtime.getConfigName(), "runtime", new StaticType<>() {}); + runtime.invoke("publishConfigStarted", configName); + RuntimeConfig rtConfig = runtime.readServiceConfig(runtime.getConfigName(), "runtime", new StaticType<>() { + }); if (rtConfig == null) { runtime.error("cannot find %s%s%s", runtime.getConfigName(), fs, "runtime.yml"); return; @@ -2602,7 +2580,7 @@ static public void startConfig(String configName) { } // has to be loaded - File file = new File(Runtime.CONFIG_ROOT + fs + runtime.getConfigName() + fs + service + ".yml"); + File file = new File(Runtime.ROOT_CONFIG_DIR + fs + runtime.getConfigName() + fs + service + ".yml"); if (!file.exists()) { runtime.error("cannot read file %s - skipping", file.getPath()); continue; @@ -2613,25 +2591,25 @@ static public void startConfig(String configName) { if (sc == null) { continue; } - runtime.loadService(Runtime.getPlan(), service, sc.type, true, 0); + runtime.loadService(new Plan("runtime"), service, sc.type, true, 0); } catch (Exception e) { runtime.error(e); } } // for all newly created services start them - Map created = Runtime.createServicesFromPlan(Runtime.getPlan(), null, null); + Map created = Runtime.createServicesFromPlan(new Plan("runtime"), null, null); for (ServiceInterface si : created.values()) { si.startService(); } runtime.processingConfig = false; // multiple inbox threads not available - runtime.invoke("publishFinishedConfig", configName); + runtime.invoke("publishConfigFinished", configName); } - public String publishStartConfig(String configName) { - log.info("publishStartConfig {}", configName); + public String publishConfigStarted(String configName) { + log.info("publishConfigStarted {}", configName); // Make Note: done inline, because the thread actually doing the config // processing // would need to be finished with it before this thread could be invoked @@ -2640,8 +2618,8 @@ public String publishStartConfig(String configName) { return configName; } - public String publishFinishedConfig(String configName) { - log.info("publishFinishedConfig {}", configName); + public String publishConfigFinished(String configName) { + log.info("publishConfigFinished {}", configName); // Make Note: done inline, because the thread actually doing the config // processing // would need to be finished with it before this thread could be invoked @@ -2662,7 +2640,6 @@ public String publishFinishedConfig(String configName) { synchronized static public ServiceInterface start(String name, String type) { try { - ServiceInterface requestedService = Runtime.getService(name); if (requestedService != null) { log.info("requested service already exists"); @@ -2674,10 +2651,9 @@ synchronized static public ServiceInterface start(String name, String type) { return requestedService; } - Runtime.load(name, type); - Runtime.savePlan(null); - // FIXME - does some order need to be maintained - Map services = createServicesFromPlan(Runtime.getPlan(), null, name); + Plan plan = Runtime.load(name, type); + + Map services = createServicesFromPlan(plan, null, name); if (services == null) { Runtime.getInstance().error("cannot create instance of %s with type %s given current configuration", name, type); @@ -2733,15 +2709,23 @@ synchronized static public ServiceInterface start(String name, String type) { } /** - * sing parameter name info supplied - potentially all information regarding - * this service coulde be found in on the filesystem or in the plan + * single parameter name info supplied - potentially all information regarding + * this service could be found in on the filesystem or in the plan * * @param name * @return */ synchronized static public ServiceInterface start(String name) { - Runtime.load(name, null); - Map services = createServicesFromPlan(Runtime.getPlan(), null, name); + if (Runtime.getService(name) != null) { + // already exists + ServiceInterface si = Runtime.getService(name); + if (!si.isRunning()) { + si.startService(); + } + return si; + } + Plan plan = Runtime.load(name, null); + Map services = createServicesFromPlan(plan, null, name); // FIXME - order ? for (ServiceInterface service : services.values()) { service.startService(); @@ -2752,7 +2736,7 @@ synchronized static public ServiceInterface start(String name) { synchronized public static Plan load(String name, String type) { try { Runtime runtime = Runtime.getInstance(); - return runtime.loadService(Runtime.getPlan(), name, type, true, 0); + return runtime.loadService(new Plan("runtime"), name, type, true, 0); } catch (IOException e) { runtime.error(e); } @@ -2781,21 +2765,6 @@ public Runtime(String n, String id) { // fist and only time.... runtime = this; repo = (IvyWrapper) Repo.getInstance(LIBRARIES, "IvyWrapper"); - - // resolve serviceData MetaTypes for the repo - - for (MetaData metaData : serviceData.getServiceTypes()) { - if (metaData.getSimpleName().equals("OpenCV")) { - log.warn("here"); - } - Set deps = repo.getUnfulfilledDependencies(metaData.getType()); - if (deps.size() == 0) { - metaData.installed = true; - } else { - log.warn("{} not installed", metaData.getSimpleName()); - } - } - } } @@ -3384,13 +3353,16 @@ static public void disableLogging() { */ @Override public void releaseService() { - super.releaseService(); if (runtime != null) { + runtime.purgeTasks(); + runtime.stopService(); runtime.stopInteractiveMode(); - runtime.getRepo().removeStatusPublishers(); + registry = new TreeMap<>(); + } + synchronized (INSTANCE_LOCK) { + runtime = null; } - runtime = null; } /** @@ -3541,63 +3513,63 @@ static public String execute(String program, List args, String workingDi List command = new ArrayList<>(); command.add(program); if (args != null) { - command.addAll(args); + command.addAll(args); } ProcessBuilder builder = new ProcessBuilder(command); if (workingDir != null) { - builder.directory(new File(workingDir)); + builder.directory(new File(workingDir)); } Map environment = builder.environment(); if (additionalEnv != null) { - environment.putAll(additionalEnv); + environment.putAll(additionalEnv); } StringBuilder outputBuilder = new StringBuilder(); try { - Process handle = builder.start(); + Process handle = builder.start(); - InputStream stdErr = handle.getErrorStream(); - InputStream stdOut = handle.getInputStream(); + InputStream stdErr = handle.getErrorStream(); + InputStream stdOut = handle.getInputStream(); - // Read the output streams in separate threads to avoid potential blocking - Thread stdErrThread = new Thread(() -> readStream(stdErr, outputBuilder)); - stdErrThread.start(); + // Read the output streams in separate threads to avoid potential blocking + Thread stdErrThread = new Thread(() -> readStream(stdErr, outputBuilder)); + stdErrThread.start(); - Thread stdOutThread = new Thread(() -> readStream(stdOut, outputBuilder)); - stdOutThread.start(); + Thread stdOutThread = new Thread(() -> readStream(stdOut, outputBuilder)); + stdOutThread.start(); - if (block) { - int exitValue = handle.waitFor(); - outputBuilder.append("Exit Value: ").append(exitValue); - log.info("Command exited with exit value: {}", exitValue); - } else { - log.info("Command started"); - } + if (block) { + int exitValue = handle.waitFor(); + outputBuilder.append("Exit Value: ").append(exitValue); + log.info("Command exited with exit value: {}", exitValue); + } else { + log.info("Command started"); + } - return outputBuilder.toString(); + return outputBuilder.toString(); } catch (IOException e) { - log.error("Error executing command", e); - return e.getMessage(); + log.error("Error executing command", e); + return e.getMessage(); } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - log.error("Command execution interrupted", e); - return e.getMessage(); + Thread.currentThread().interrupt(); + log.error("Command execution interrupted", e); + return e.getMessage(); } -} + } -private static void readStream(InputStream inputStream, StringBuilder outputBuilder) { + private static void readStream(InputStream inputStream, StringBuilder outputBuilder) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { - String line; - while ((line = reader.readLine()) != null) { - outputBuilder.append(line).append(System.lineSeparator()); - } + String line; + while ((line = reader.readLine()) != null) { + outputBuilder.append(line).append(System.lineSeparator()); + } } catch (IOException e) { - log.error("Error reading process output", e); + log.error("Error reading process output", e); } -} + } /** * Get the current battery level of the computer this MRL instance is running @@ -3738,19 +3710,6 @@ public static Process exec(String... cmd) throws IOException { return p; } - /** - * Re-execute {@link Runtime#main(String[])} with the specified arguments and - * then return the Runtime singleton instance. - * - * @param args2 - * An array of the arguments to be passed to main() - * @return The Runtime singleton - */ - public static Runtime getInstance(String[] args2) { - Runtime.main(args2); - return Runtime.getInstance(); - } - /** * Get all the options passed on the command line when MyRobotLab is executed. * @@ -3954,7 +3913,7 @@ public List getServiceTypes() { } return filteredTypes; } - + /** * Register a connection route from one instance to this one. * @@ -3981,22 +3940,6 @@ public void addConnection(String uuid, String id, Connection connection) { addRoute(id, uuid, 10); } -// @Override -// public ServiceConfig getFilteredConfig() { -// RuntimeConfig sc = (RuntimeConfig) super.getFilteredConfig(); -// Set removeList = new HashSet<>(); -// for (Listener listener : sc.listeners) { -// if (listener.callback.equals("onReleased") || listener.callback.equals("onStarted") || listener.callback.equals("onRegistered") || listener.callback.equals("onStopped") -// || listener.callback.equals("onCreated")) { -// removeList.add(listener); -// } -// } -// for (Listener remove : removeList) { -// sc.listeners.remove(remove); -// } -// return sc; -// } - /** * Unregister all connections that a specified client has made. * @@ -4341,27 +4284,6 @@ public String stopped(String name) { return name; } - public static Plan getPlan() { - Runtime runtime = Runtime.getInstance(); - return runtime.getLocalPlan(); - } - - public Plan getLocalPlan() { - return masterPlan; - } - - /** - * Clear the {@link #masterPlan}. - */ - static public void clearPlan() { - Runtime runtime = Runtime.getInstance(); - runtime.masterPlan.clear(); - runtime.masterPlan.put("runtime", new RuntimeConfig()); - // unset config path - runtime.configName = null; - runtime.publishConfigList(); - } - /** * Wrapper for {@link ServiceData#getMetaData(String, String)} * @@ -4552,10 +4474,18 @@ public static void main(String[] args) { return; } - File checkStart = new File("start.yml"); - if (checkStart.exists()) { + // start.yml file is required, if not pre-existing + // is created immediately. It contains static information + // which needs to be available before a Runtime is created + File checkStartYml = new File("start.yml"); + if (!checkStartYml.exists()) { + // save default + startYml = new StartYml(); + String defaultStartFile = CodecUtils.toYaml(startYml); + FileIO.toFile("start.yml", defaultStartFile); + } else { String yml = FileIO.toString("start.yml"); - startYml = CodecUtils.fromYaml(yml, CmdConfig.class); + startYml = CodecUtils.fromYaml(yml, StartYml.class); } // id always required - precedence @@ -4563,31 +4493,19 @@ public static void main(String[] args) { // if in start.yml it will be used // if supplied by the command line it will be used // command line has the highest precedence - if (options.id == null) { - if (startYml == null || startYml.id == null || !startYml.enable) { - options.id = NameGenerator.getName(); - } else { - options.id = startYml.id; - } - } - - // String id = (options.fromLauncherx) ? options.id : - // String.format("%s-launcher", options.id); - String id = options.id; - // fix paths Platform platform = Platform.getLocalInstance(); - platform.setId(id); - - // save an output of our cmd options - File dataDir = new File(Runtime.DATA_DIR); - if (!dataDir.exists()) { - dataDir.mkdirs(); - } - - File configRoot = new File(Runtime.CONFIG_ROOT); - if (!configRoot.exists()) { - configRoot.mkdirs(); + if (options.id != null) { + platform.setId(options.id); + } else if (startYml.id != null) { + platform.setId(startYml.id); + } else { + // no id set - should be first + // time mrl is started + String id = NameGenerator.getName(); + platform.setId(id); + startYml.id = id; + FileIO.toFile("start.yml", CodecUtils.toYaml(startYml)); } if (options.virtual) { @@ -4610,18 +4528,21 @@ public static void main(String[] args) { return; } - if (options.configRoot != null || (startYml != null && startYml.configRoot != null && startYml.enable)) { - CONFIG_ROOT = (startYml != null && startYml.configRoot != null) ? startYml.configRoot : options.configRoot; - } - // if a you specify a config file it becomes the "base" of configuration // inline flags will still override values - if (options.config != null || (startYml != null && startYml.config != null && startYml.enable)) { + if (options.config != null) { // if this is a valid config, it will load - Runtime.getInstance(); + setConfig(options.config); + } else { + // required directory to load any service + setConfig(startYml.config); } - createAndStartServices(options.services); + if (startYml.enable) { + Runtime.startConfig(startYml.config); + } else { + createAndStartServices(options.services); + } if (options.invoke != null) { invokeCommands(options.invoke); @@ -4749,45 +4670,56 @@ static public ServiceInterface loadAndStart(String name, String type) { * @return * @throws IOException */ - synchronized private Plan loadService(Plan plan, String name, String type, boolean start, int level) throws IOException { - - log.info("loading - {} {} {}", name, type, level); + synchronized public Plan loadService(Plan plan, String name, String type, boolean start, int level) throws IOException { if (plan == null) { log.error("plan required to load a system"); return null; } + log.info("loading - {} {} {}", name, type, level); + // from recursive memory definition ServiceConfig sc = plan.get(name); - // if there isn't some form of definition in the plan - // get a "default" service config and put it in the plan - if (sc == null) { - if (type == null) { - log.error("cannot get Java def with type == null"); + // HIGHEST PRIORITY - OVERRIDE WITH FILE + String configPath = runtime.getConfigPath(); + String configFile = configPath + fs + name + ".yml"; + + // PRIORITY #1 + // find if a current yml config file exists - highest priority + log.debug("priority #1 user's yml override {} ", configFile); + ServiceConfig fileSc = readServiceConfig(Runtime.getInstance().getConfigName(), name); + if (fileSc != null) { + // if definition exists in file form, it overrides current memory one + sc = fileSc; + } else if (sc != null) { + // if memory config is available but not file + // we save it + String yml = CodecUtils.toYaml(sc); + FileIO.toFile(configFile, yml); + } + + // special conflict case - type is specified, but its not the same as + // file version - in that case specified parameter type wins and overwrites + // config. User can force type by supplying one as a parameter, however, the + // recursive + // call other peer types will have name/file.yml definition precedence + if ((type != null && sc != null && !type.equals(sc.type) && level == 0) || (sc == null)) { + if (sc != null) { + warn("type %s overwriting type %s specified in %s.yml file", type, sc.type, name); } - log.info("getting default Java definition {} {}", name, type); - - // TODO !!!! - switch on Runtime var useDefaults = true/false ServiceConfig.getDefault(plan, name, type); sc = plan.get(name); - } - // HIGHEST PRIORITY - OVERRIDE WITH FILE - String configPath = runtime.getConfigName(); - if (configPath != null) { - log.info("priority #1 user's yml override {}", configPath + fs + name + ".yml"); - // PRIORITY #1 - // find if a current yml config file exists - highest priority - ServiceConfig fileSc = readServiceConfig(configPath, name); - if (fileSc != null) { - // if definition exists in file form, it overrides current memory one - sc = fileSc; - } + // create new file if it didn't exist or overwrite it if new type is + // required + String yml = CodecUtils.toYaml(sc); + FileIO.toFile(configFile, yml); } if (sc == null && type == null) { log.error("no local config and unknown type"); + return plan; } // finalize @@ -4822,12 +4754,14 @@ synchronized private Plan loadService(Plan plan, String name, String type, boole } public ServiceConfig readServiceConfig(String name) { - return readServiceConfig(name, new StaticType<>() {}); + return readServiceConfig(name, new StaticType<>() { + }); } - + /** - * read a service's configuration, in the context - * of current config set name or default + * read a service's configuration, in the context of current config set name + * or default + * * @param name * @return */ @@ -4836,7 +4770,8 @@ public C readServiceConfig(String name, StaticType } public ServiceConfig readServiceConfig(String configName, String name) { - return readServiceConfig(configName, name, new StaticType<>() {}); + return readServiceConfig(configName, name, new StaticType<>() { + }); } /** @@ -4859,7 +4794,7 @@ public C readServiceConfig(String configName, String n return null; } - String filename = CONFIG_ROOT + fs + configName + fs + name + ".yml"; + String filename = ROOT_CONFIG_DIR + fs + configName + fs + name + ".yml"; File check = new File(filename); C sc = null; if (check.exists()) { @@ -4878,7 +4813,6 @@ public String publishConfigLoaded(String name) { return name; } - public String setAllIds(String id) { Platform.getLocalInstance().setId(id); for (ServiceInterface si : getServices()) { @@ -4887,12 +4821,11 @@ public String setAllIds(String id) { return id; } - @Override public RuntimeConfig apply(RuntimeConfig c) { super.apply(c); config = c; - + setLocale(config.locale); if (config.id != null) { @@ -4952,7 +4885,7 @@ static public void releaseConfig(String configName) { */ static public void releaseConfigPath(String configPath) { try { - String filename = CONFIG_ROOT + fs + Runtime.getInstance().getConfigName() + fs + "runtime.yml"; + String filename = ROOT_CONFIG_DIR + fs + Runtime.getInstance().getConfigName() + fs + "runtime.yml"; String releaseData = FileIO.toString(new File(filename)); RuntimeConfig config = CodecUtils.fromYaml(releaseData, RuntimeConfig.class); List registry = config.getRegistry(); @@ -4969,7 +4902,7 @@ static public void releaseConfigPath(String configPath) { } public static String getConfigRoot() { - return CONFIG_ROOT; + return ROOT_CONFIG_DIR; } /** @@ -5013,10 +4946,7 @@ public boolean saveService(String configName, String serviceName, String filenam setConfig(configName); - String configPath = CONFIG_ROOT + fs + configName; - - File dir = new File(configPath); - dir.mkdirs(); + String configPath = ROOT_CONFIG_DIR + fs + configName; // save running services Set servicesToSave = new HashSet<>(); @@ -5025,7 +4955,6 @@ public boolean saveService(String configName, String serviceName, String filenam if (startYml.enable) { startYml.id = getId(); startYml.config = configName; - startYml.configRoot = CONFIG_ROOT; FileIO.toFile("start.yml", CodecUtils.toYaml(startYml)); } @@ -5065,6 +4994,14 @@ public String setConfigName(String name) { if (name != null) { configName = name.trim(); } + + // for the moment the best way is to mandate + // a dir is created when a new config name is set + // because loading service are required to save config + // before starting + File configDir = new File(ROOT_CONFIG_DIR + fs + name); + configDir.mkdirs(); + invoke("publishConfigList"); return name; } @@ -5087,22 +5024,15 @@ public boolean isProcessingConfig() { * @return configName */ public static String setConfig(String configName) { + + File configDir = new File(ROOT_CONFIG_DIR + fs + configName); + configDir.mkdirs(); + Runtime runtime = Runtime.getInstance(); runtime.setConfigName(configName); return configName; } - /** - * sets the root of all config, where root + fs + configName exists - * - * @param root - * @return - */ - public static String setConfigRoot(String root) { - CONFIG_ROOT = root; - return root; - } - // FIXME - move this to service and add default (no servicename) method // signature @Deprecated /* @@ -5150,44 +5080,6 @@ public void registerForInterfaceChange(String targetedInterface) { invoke("publishInterfaceToNames"); } - /** - * save the current "plan" to the filesystem this will give the user the - * opportunity to tweak defaults before starting - * - * @param configName - */ - static public void savePlan(String configName) { - Runtime runtime = Runtime.getInstance(); - Runtime.setConfig(configName); - Runtime.getInstance().savePlanInternal(runtime.getConfigName()); - } - - private void savePlanInternal(String configName) { - - if (configName == null) { - info("cannot save plan config name is null"); - return; - } - - File configDirectory = new File(CONFIG_ROOT + fs + configName); - configDirectory.mkdirs(); - - for (String s : masterPlan.keySet()) { - String filename = CONFIG_ROOT + fs + configName + fs + s + ".yml"; - File check = new File(filename); - if (check.exists()) { - continue; - } - String data = CodecUtils.toYaml(masterPlan.get(s)); - try { - FileIO.toFile(filename, data.getBytes()); - } catch (Exception e) { - error(e); - } - info("saved %s config to %s", getName(), filename); - } - } - /** * Published whenever a new service type definition if found * @@ -5281,8 +5173,7 @@ public void loadFile(String path) { } String name = f.getName().substring(0, f.getName().length() - 4); ServiceConfig sc = CodecUtils.readServiceConfig(path); - loadService(Runtime.getPlan(), name, sc.type, true, 0); - masterPlan.put(name, sc); + loadService(new Plan("runtime"), name, sc.type, true, 0); } catch (Exception e) { error("loadFile requirese"); } @@ -5303,7 +5194,7 @@ final public Plan saveDefault(String name, String type, boolean fullPlan) { final public Plan saveDefault(String configName, String name, String type, boolean fullPlan) { Plan plan = ServiceConfig.getDefault(new Plan(name), name, type); - String configPath = CONFIG_ROOT + fs + configName; + String configPath = ROOT_CONFIG_DIR + fs + configName; if (!fullPlan) { try { @@ -5346,12 +5237,13 @@ public void saveAllDefaults(String configPath, boolean fullPlan) { } } + /** + * Get current runtime's config path + * + * @return + */ public String getConfigPath() { - if (configName == null) { - error("config name is not set"); - return null; - } - return CONFIG_ROOT + fs + configName; + return ROOT_CONFIG_DIR + fs + configName; } @Override @@ -5362,4 +5254,126 @@ public RuntimeConfig getConfig() { return config; } + /** + * Gets a {serviceName}.yml file config from configName directory + * + * @param configName + * @param serviceName + * @return ServiceConfig + */ + public ServiceConfig getConfig(String configName, String serviceName) { + return readServiceConfig(configName, serviceName); + } + + /** + * Get a {serviceName}.yml file in the current config directory + * + * @param serviceName + * @return + */ + public ServiceConfig getConfig(String serviceName) { + return readServiceConfig(serviceName); + } + + /** + * Save a config with a new Config + * + * @param name + * @param serviceConfig + * @throws IOException + */ + public static void saveConfig(String name, ServiceConfig serviceConfig) throws IOException { + String file = Runtime.ROOT_CONFIG_DIR + fs + runtime.getConfigName() + fs + name + ".yml"; + FileIO.toFile(file, CodecUtils.toYaml(serviceConfig)); + } + + /** + * get the service's peer config + * + * @param serviceName + * @param peerKey + * @return + */ + public ServiceConfig getPeerConfig(String serviceName, String peerKey) { + ServiceConfig sc = runtime.getConfig(serviceName); + if (sc == null) { + return null; + } + Peer peer = sc.getPeer(peerKey); + return runtime.getConfig(peer.name); + } + + /** + * Switches a service's .yml type definition while replacing the set of + * listeners to preserver subscriptions. Useful when switching services that + * support the same interface like SpeechSynthesis services etc. + * + * @param serviceName + * @param type + * @return + * @throws IOException + */ + public boolean changeType(String serviceName, String type) { + try { + ServiceConfig sc = getConfig(serviceName); + if (sc == null) { + error("could not find %s config", serviceName); + return false; + } + // get target + Plan targetPlan = getDefault(serviceName, type); + if (targetPlan == null || targetPlan.get(serviceName) == null) { + error("%s null", type); + return false; + } + ServiceConfig target = targetPlan.get(serviceName); + // replacing listeners + target.listeners = sc.listeners; + saveConfig(serviceName, target); + return true; + } catch (Exception e) { + error("could not save %s of type %s", serviceName, type); + return false; + } + } + + /** + * Get a peer's config + * + * @param sericeName + * @param peerKey + * @return + */ + public ServiceConfig getPeer(String sericeName, String peerKey) { + ServiceConfig sc = getConfig(sericeName); + if (sc == null) { + return null; + } + Peer peer = sc.getPeer(peerKey); + if (peer == null) { + return null; + } + return getConfig(peer.name); + } + + /** + * Removes a config set and all its files + * + * @param string + */ + public static void removeConfig(String configName) { + try { + log.info("removeing config"); + + File check = new File(ROOT_CONFIG_DIR + fs + configName); + + if (check.exists()) { + Path pathToBeDeleted = Paths.get(check.getAbsolutePath()); + Files.walk(pathToBeDeleted).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); + } + } catch (Exception e) { + log.error("removeConfig threw", e); + } + } + } diff --git a/src/main/java/org/myrobotlab/service/Security.java b/src/main/java/org/myrobotlab/service/Security.java index 0219c7ca53..979c2afdab 100644 --- a/src/main/java/org/myrobotlab/service/Security.java +++ b/src/main/java/org/myrobotlab/service/Security.java @@ -141,7 +141,6 @@ public static Security getInstance() { public static void main(String[] args) throws Exception { // LoggingFactory.init(Level.INFO); - Runtime.getInstance(args); Runtime.start("gui", "SwingGui"); // Security security = Security.getInstance(); diff --git a/src/main/java/org/myrobotlab/service/Serial.java b/src/main/java/org/myrobotlab/service/Serial.java index af16f3522a..ca48f2f7cc 100644 --- a/src/main/java/org/myrobotlab/service/Serial.java +++ b/src/main/java/org/myrobotlab/service/Serial.java @@ -1291,6 +1291,19 @@ public void startTcpServer(Integer port) throws IOException { public void stopTcpServer() throws IOException { tcpSerialHub.stop(); } + + public void startService() { + super.startService(); + if (config.port != null) { + try { + if (!isConnected()) { + connect(config.port); + } + } catch (Exception e) { + log.error("load connecting threw", e); + } + } + } @Override public SerialConfig getConfig() { @@ -1300,21 +1313,6 @@ public SerialConfig getConfig() { return config; } - @Override - public SerialConfig apply(SerialConfig c) { - super.apply(c); - - if (c.port != null) { - try { - if (isConnected()) { - connect(c.port); - } - } catch (Exception e) { - log.error("load connecting threw", e); - } - } - return c; - } public static void main(String[] args) { diff --git a/src/main/java/org/myrobotlab/service/ServoMixer.java b/src/main/java/org/myrobotlab/service/ServoMixer.java index dc6c3f7127..f6cfabd2a8 100755 --- a/src/main/java/org/myrobotlab/service/ServoMixer.java +++ b/src/main/java/org/myrobotlab/service/ServoMixer.java @@ -1,11 +1,14 @@ package org.myrobotlab.service; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -16,8 +19,8 @@ import org.myrobotlab.framework.interfaces.Attachable; import org.myrobotlab.framework.interfaces.ServiceInterface; import org.myrobotlab.io.FileIO; +import org.myrobotlab.kinematics.Action; import org.myrobotlab.kinematics.Gesture; -import org.myrobotlab.kinematics.GesturePart; import org.myrobotlab.kinematics.Pose; import org.myrobotlab.kinematics.PoseMove; import org.myrobotlab.logging.LoggerFactory; @@ -26,6 +29,7 @@ import org.myrobotlab.service.interfaces.SelectListener; import org.myrobotlab.service.interfaces.ServiceLifeCycleListener; import org.myrobotlab.service.interfaces.ServoControl; +import org.myrobotlab.service.interfaces.SpeechSynthesis; import org.slf4j.Logger; /** @@ -39,12 +43,11 @@ public class ServoMixer extends Service implements ServiceLifeCycleListener, SelectListener { /** - * The Player plays a requested gesture, which is a sequence of Poses. - * Poses can be positions, delays, or speech. It publishes when it - * starts a gesture and when its finished with a gesture. All - * poses within the gesture are published as well. This potentially will - * provide some measure of safety if servos should not accept other input - * when doing a gesture. + * The Player plays a requested gesture, which is a sequence of Poses. Poses + * can be positions, delays, or speech. It publishes when it starts a gesture + * and when its finished with a gesture. All poses within the gesture are + * published as well. This potentially will provide some measure of safety if + * servos should not accept other input when doing a gesture. * */ public class Player implements Runnable { @@ -55,44 +58,38 @@ public class Player implements Runnable { transient private ExecutorService executor; private void play() { - if (runningGesture.getParts() != null) { + if (runningGesture.actions != null) { invoke("publishGestureStarted", gestureName); - for (int i = 0; i < runningGesture.getParts().size(); ++i) { + for (int i = 0; i < runningGesture.actions.size(); ++i) { if (!running) { break; } - GesturePart sp = runningGesture.getParts().get(i); - invoke("publishPlayingGesturePart", sp); - invoke("publishPlayingGesturePartIndex", i); - switch(sp.type) { - case "Pose":{ - Pose pose = getPose(sp.name); - if (pose == null) { - warn("Pose %s not found", sp.name); - continue; - } - // move to positions - moveToPose(sp.name, pose, sp.blocking); - // TODO if stopped, get and stop all servos - + Action action = runningGesture.actions.get(i); + invoke("publishPlayingAction", action); + invoke("publishPlayingActionIndex", i); + switch (action.type) { + case "moveTo": { + Map> moves = (Map) action.value; + for (String servoName : moves.keySet()) { + Map move = moves.get(servoName); + moveTo(servoName, move); + } } - break; - case "Delay":{ - sleep((Integer)sp.value); + break; + case "sleep": { + sleep(Math.round((double) action.value * 1000)); } - break; - case "Speech":{ - invoke("publishText", (String)sp.value); + break; + case "speak": { + speak((Map) action.value); } - break; - default:{ - error("do not know how to handle gesture part of type %s", sp.type); + break; + default: { + error("do not know how to handle gesture part of type %s", action.type); } } } // poses - invoke("publishGestureStopped", gestureName); - } running = false; } @@ -101,7 +98,7 @@ private void play() { public void run() { try { running = true; - if (runningGesture.getRepeat()) { + if (runningGesture.repeat) { while (running) { play(); } @@ -130,32 +127,38 @@ public void stop() { invoke("publishGestureStopped", gestureName); } } - + public final static Logger log = LoggerFactory.getLogger(InMoov2.class); - - + private static final long serialVersionUID = 1L; - /** * Set of servo names kept in sync with current registry */ protected Set allServos = new TreeSet<>(); - + + // TODO selected servos + + /** + * current gesture being edited + */ + protected Gesture currentGesture = null; /** * gesture player */ final protected transient Player player = new Player(); - + protected String currentEditGestureName = null; + public ServoMixer(String n, String id) { super(n, id); } /** - * Explicitly saving a new gesture file. This will error if the - * file already exists. The gesture part moves will be empty. + * Explicitly saving a new gesture file. This will error if the file already + * exists. The gesture part moves will be empty. + * * @param filename * @return */ @@ -165,16 +168,22 @@ public String addNewGestureFile(String filename) { return null; } if (!filename.toLowerCase().endsWith(".yml")) { - filename += ".yml"; + filename += ".yml"; } if (FileIO.checkFile(filename)) { error("file %s already exists", filename); return null; } - saveGesture(filename, new Gesture()); + saveGesture(filename, new Gesture()); return filename; } + public void saveGesture(String name) { + // FIXME - warn if overwrite + // FIXME - don't warn if opened + saveGesture(name, currentGesture); + } + /** * general interface attach */ @@ -190,14 +199,14 @@ public void attach(Attachable attachable) { */ @Override public void attach(String name) { - ServoMixerConfig c = (ServoMixerConfig)config; + // FIXME - check type in registry, describe, or query ... make sure Servo // type.. // else return error - should be type checking ServiceInterface si = Runtime.getService(name); if (si != null & "Servo".equals(si.getSimpleName())) { Servo servo = (Servo) Runtime.getService(name); - if (c.autoDisable) { + if (config.autoDisable) { servo.setAutoDisable(true); } allServos.add(name); @@ -218,17 +227,37 @@ public void attachServo(Servo servo) { } public Gesture getGesture(String name) { - ServoMixerConfig c = (ServoMixerConfig)config; + Gesture gesture = null; + try { - if (!FileIO.checkDir(c.gesturesDir)) { - error("invalid poses directory %s", c.gesturesDir); + // if name is null or currentEditGestureName return current gesture + if (name == null || name.equals(currentEditGestureName)) { + gesture = currentGesture; + return gesture; + } + + // switching gestures + + if (!FileIO.checkDir(config.gesturesDir)) { + error("invalid poses directory %s", config.gesturesDir); return null; } - String filename = new File(c.gesturesDir).getAbsolutePath() + File.separator + name + ".yml"; - log.info("Loading Pose name {}", filename); + String filename = new File(config.gesturesDir).getAbsolutePath() + File.separator + name + ".yml"; + log.info("loading gesture {}", filename); gesture = CodecUtils.fromYaml(FileIO.toString(filename), Gesture.class); + + // FIXME - replacing current gesture .. if DIRTY edits - warn !!! + if (currentEditGestureName != null) { + saveGesture(currentEditGestureName); + } + + currentGesture = gesture; + currentEditGestureName = name; + return gesture; + } catch (FileNotFoundException e) { + info("file %s.yml not found", name); } catch (Exception e) { error(e); } @@ -236,15 +265,14 @@ public Gesture getGesture(String name) { } public List getGestureFiles() { - ServoMixerConfig c = (ServoMixerConfig)config; List files = new ArrayList<>(); - if (!FileIO.checkDir(c.gesturesDir)) { - error("gestures %s directory does not exist", c.gesturesDir); + if (!FileIO.checkDir(config.gesturesDir)) { + error("gestures %s directory does not exist", config.gesturesDir); return files; } - File dir = new File(c.gesturesDir); + File dir = new File(config.gesturesDir); File[] all = dir.listFiles(); Set sorted = new TreeSet<>(); for (File f : all) { @@ -268,11 +296,10 @@ public List getGestureFiles() { * @return the loaded pose object */ public Pose getPose(String name) { - + try { - ServoMixerConfig c = (ServoMixerConfig)config; - String filename = new File(c.posesDir).getAbsolutePath() + File.separator + name + ".yml"; + String filename = new File(config.posesDir).getAbsolutePath() + File.separator + name + ".yml"; log.info("loading pose name {}", filename); String yml = FileIO.toString(filename); return CodecUtils.fromYaml(yml, Pose.class); @@ -284,36 +311,8 @@ public Pose getPose(String name) { return null; } - public List getPoseFiles() { - ServoMixerConfig c = (ServoMixerConfig)config; - - List files = new ArrayList<>(); - if (!FileIO.checkDir(c.posesDir)) { - return files; - } - - File dir = new File(c.posesDir); - - if (!dir.exists() || !dir.isDirectory()) { - error("%s not a valid directory", c.posesDir); - return files; - } - File[] all = dir.listFiles(); - Set sorted = new TreeSet<>(); - for (File f : all) { - if (f.getName().toLowerCase().endsWith(".yml")) { - sorted.add(f.getName().substring(0, f.getName().lastIndexOf("."))); - } - } - for (String s : sorted) { - files.add(s); - } - return files; - } - public String getPosesDirectory() { - ServoMixerConfig c = (ServoMixerConfig)config; - return c.posesDir; + return config.posesDir; } public List listAllServos() { @@ -336,12 +335,12 @@ public void moveToPose(String name) throws IOException { public void moveToPose(String name, Pose p, boolean blocking) { try { - + if (p.getMoves() == null) { error("no moves within pose file %s", name); return; } - + for (String sc : p.getMoves().keySet()) { PoseMove pm = p.getMoves().get(sc); ServoControl servo = (ServoControl) Runtime.getService(sc); @@ -368,7 +367,7 @@ public void moveToPose(String name, Pose p, boolean blocking) { public void onCreated(String name) { } - @Override + @Override /* FIXME have a simple onStarted onRelease */ public void onRegistered(Registration registration) { } @@ -381,8 +380,8 @@ public void onReleased(String name) { } /** - * handles incoming selected events and publishes - * search events + * handles incoming selected events and publishes search events + * * @param selected */ @Override @@ -415,6 +414,7 @@ public void playGesture(String name) { /** * When a gesture starts its name will be published + * * @param name * @return */ @@ -424,6 +424,7 @@ public String publishGestureStarted(String name) { /** * When a gesture stops its name will be published + * * @param name * @return */ @@ -432,20 +433,22 @@ public String publishGestureStopped(String name) { } /** - * Current gesture part being processed by player + * Current gesture part being processed by player + * * @param sp * @return */ - public GesturePart publishPlayingGesturePart(GesturePart sp) { + public Action publishPlayingAction(Action sp) { return sp; } /** * Current index of gesture being played + * * @param i * @return */ - public int publishPlayingGesturePartIndex(int i) { + public int publishPlayingActionIndex(int i) { return i; } @@ -458,8 +461,9 @@ public String publishStopPose(String name) { } /** - * Speech publishes here - a SpeechSynthesis service could subscribe, - * or ProgramAB + * Speech publishes here - a SpeechSynthesis service could subscribe, or + * ProgramAB + * * @param text * @return */ @@ -468,17 +472,19 @@ public String publishText(String text) { } public void removeGesture(String name) { - ServoMixerConfig c = (ServoMixerConfig)config; try { - if (!FileIO.checkDir(c.gesturesDir)) { - error("invalid poses directory %s", c.gesturesDir); + if (!FileIO.checkDir(config.gesturesDir)) { + error("invalid poses directory %s", config.gesturesDir); return; } - String filename = new File(c.gesturesDir).getAbsolutePath() + File.separator + name + ".yml"; + String filename = new File(config.gesturesDir).getAbsolutePath() + File.separator + name + ".yml"; File del = new File(filename); if (del.exists()) { - del.delete(); + if (del.delete()) { + currentEditGestureName = null; + currentGesture = null; + } } invoke("getGestureFiles"); } catch (Exception e) { @@ -486,30 +492,60 @@ public void removeGesture(String name) { } } - public void removePose(String name) { + public void moveActionUp(int index) { + if (currentGesture == null) { + error("cannot move gesture not set"); + return; + } + + List list = currentGesture.actions; + + if (index > 0 && index < list.size()) { + Action action = list.remove(index); + list.add(index - 1, action); + } else { + error("index out of range or at the beginning of the list."); + } + invoke("getGesture"); + } + + public void moveActionDown(int index) { + if (currentGesture == null) { + error("cannot move: gesture not set"); + return; + } + + List list = currentGesture.actions; + + if (index >= 0 && index < list.size() - 1) { + Action action = list.remove(index); + list.add(index + 1, action); + } else { + error("index out of range or at the end of the list."); + } + invoke("getGesture"); + } + + public void removeActionFromGesture(int index) { try { - ServoMixerConfig c = (ServoMixerConfig)config; - if (!FileIO.checkDir(c.posesDir)) { - error("invalid poses directory %s", c.posesDir); + Gesture gesture = getGesture(); + if (gesture == null) { + error("gesture not set"); return; } - String filename = new File(c.posesDir).getAbsolutePath() + File.separator + name + ".yml"; - File del = new File(filename); - if (del.exists()) { - del.delete(); - } else { - error("could not delete file %s", filename); + if (gesture.actions.size() != 0 && index < gesture.actions.size()) { + gesture.actions.remove(index); } - invoke("getPoseFiles"); + } catch (Exception e) { error(e); } } public void rest() { - for(String servo : allServos) { - Servo s = (Servo)Runtime.getService(servo); + for (String servo : allServos) { + Servo s = (Servo) Runtime.getService(servo); s.rest(); } } @@ -520,13 +556,12 @@ public void rest() { * * @param filename * the filename to save the gesture as - * @param json - * the json to save + * @param gesture + * the gesture to save */ public void saveGesture(String filename, Gesture gesture) { try { - ServoMixerConfig c = (ServoMixerConfig)config; - + if (filename == null) { error("save gesture file name cannot be null"); return; @@ -542,62 +577,23 @@ public void saveGesture(String filename, Gesture gesture) { } // Gesture seq = CodecUtils.fromJson(json, Gesture.class); - + if (gesture != null) { - String path = c.gesturesDir + fs + filename; + String path = config.gesturesDir + fs + filename; FileOutputStream fos = new FileOutputStream(path); fos.write(CodecUtils.toYaml(gesture).getBytes()); fos.close(); - } + } + info("saved gesture %s", filename); invoke("getGestureFiles"); } catch (Exception e) { error(e); } } - - /** - * Save a {name}.yml file to the current poses directory. - * - * @param name - * name to save pose as - * @throws IOException - * boom - * - */ - public void savePose(String name) throws IOException { - savePose(name, allServos); - } - - public void savePose(String name, Set servos) throws IOException { - ServoMixerConfig c = (ServoMixerConfig)config; - - if (servos == null) { - log.error("cannot save %s null servos"); - return; - } - - Pose pose = new Pose(); - - for (String servo : servos) { - ServoControl sc = (ServoControl)Runtime.getService(servo); - if (sc == null) { - error("servo %s null", name); - continue; - } else { - pose.getMoves().put(CodecUtils.getShortName(servo), new PoseMove(sc.getCurrentInputPos(), sc.getSpeed())); - } - } - log.info("saving pose name {}", name); - - String filename = c.posesDir + File.separator + name + ".yml"; - FileIO.toFile(filename, CodecUtils.toYaml(pose).getBytes()); - invoke("getPoseFiles"); - } - /** - * publishing a search to allow UI searching based on other - * service selections + * publishing a search to allow UI searching based on other service selections + * * @param servos * @return */ @@ -606,8 +602,8 @@ public String search(String servos) { } public void setAutoDisable(boolean b) { - ServoMixerConfig c = (ServoMixerConfig)config; - c.autoDisable = b; + + config.autoDisable = b; if (b) { List servos = Runtime.getServiceNamesFromInterface(Servo.class); for (String name : servos) { @@ -624,33 +620,29 @@ public void setAutoDisable(boolean b) { } public void setPosesDirectory(String posesDirectory) { - ServoMixerConfig c = (ServoMixerConfig)config; File dir = new File(posesDirectory); if (!dir.exists()) { dir.mkdirs(); } - c.posesDir = posesDirectory; - invoke("getPoseFiles"); + config.posesDir = posesDirectory; broadcastState(); } @Override public void startService() { try { - ServoMixerConfig c = (ServoMixerConfig)config; - - new File(c.posesDir).mkdirs(); - new File(c.gesturesDir).mkdirs(); + new File(config.posesDir).mkdirs(); + new File(config.gesturesDir).mkdirs(); Runtime.getInstance().attachServiceLifeCycleListener(getName()); - + List all = Runtime.getServiceNamesFromInterface(ServoControl.class); for (String sc : all) { attach(sc); } - File poseDirectory = new File(c.posesDir); + File poseDirectory = new File(config.posesDir); if (!poseDirectory.exists()) { poseDirectory.mkdirs(); } @@ -666,7 +658,7 @@ public void startService() { public void stop() { player.stop(); } - + @Override public void stopService() { super.stopService(); @@ -676,7 +668,7 @@ public void stopService() { public static void main(String[] args) throws Exception { try { - Runtime.main(new String[] { "--id", "admin"}); + Runtime.main(new String[] { "--id", "admin" }); LoggingFactory.init("INFO"); Runtime.start("i01.head.rothead", "Servo"); @@ -686,9 +678,182 @@ public static void main(String[] args) throws Exception { webgui.startService(); Python python = (Python) Runtime.start("python", "Python"); ServoMixer mixer = (ServoMixer) Runtime.start("mixer", "ServoMixer"); + mixer.addNewGestureFile("test"); + Gesture gesture = mixer.getGesture("test"); + String gestureName = "aaa"; + String servoName = "i01.head.rothead"; + Double position = 90.0; + Double speed = null; + + Map> moves = new TreeMap<>(); + + Map poseMove1 = new TreeMap<>(); + poseMove1.put("position", 90.0); + + Map poseMove2 = new TreeMap<>(); + poseMove2.put("position", 90); + poseMove2.put("speed", 35.0); + + moves.put("i01.head.rothead", poseMove1); + moves.put("i01.head.neck", poseMove2); + + mixer.openGesture(gestureName); + mixer.addMoveToAction(moves); // autofill delay from keyframe ? + mixer.saveGesture(); + // mixer.save(gestureName); + + // mixer.setPose("test", ) } catch (Exception e) { log.error("main threw", e); } } + + public void saveGesture() { + // FIXME check if exists - ask overwrite + saveGesture(currentEditGestureName, currentGesture); + } + + public Gesture openGesture(String name) { + if (currentGesture != null) { + warn("replacing current gesture"); + // prompt user return null etc. + } + + Gesture gesture = getGesture(name); + if (gesture == null) { + // gesture not found make new + gesture = new Gesture(); + } + + currentEditGestureName = name; + currentGesture = gesture; + + return currentGesture; + } + + public void addSpeakAction(Map speechCommand) { + addSpeakAction(speechCommand, null); + } + + public void addSpeakAction(Map speechCommand, Integer index) { + addAction(Action.createSpeakAction(speechCommand), index); + } + + + public void addMoveToAction(List servos) { + addMoveToAction(servos, null); + } + + /** + * list of servos to add to an action - they're current speed and input + * positions will be added at index + * + * @param servos + * @param index + */ + public void addMoveToAction(List servos, Integer index) { + Map> moves = new TreeMap<>(); + for (String servoName : servos) { + ServoControl sc = (ServoControl)Runtime.getService(servoName); + Map posAndSpeed = new TreeMap<>(); + posAndSpeed.put("position", sc.getCurrentInputPos()); + posAndSpeed.put("speed", sc.getSpeed()); + moves.put(servoName, posAndSpeed); + } + addAction(Action.createMoveToAction(moves), index); + } + + public void addMoveToAction(Map> moves) { + addMoveToAction(moves, null); + } + + public void addMoveToAction(Map> moves, Integer index) { + addAction(Action.createMoveToAction(moves), index); + } + + public void addAction(Action action, Integer index) { + if (currentGesture == null) { + error("current gesture not set"); + return; + } + if (index != null) { + if (currentGesture.actions.size() == 0) { + currentGesture.actions.add(action); + } else { + currentGesture.actions.add(index, action); + } + } else { + currentGesture.actions.add(action); + } + } + + public void addSleepAction(double sleep) { + addSleepAction(sleep, null); + } + + public void addSleepAction(double sleep, Integer index) { + addAction(Action.createSleepAction(sleep), index); + } + + public Gesture getGesture() { + return currentGesture; + } + + private void moveTo(String servoName, Map move) { + ServoControl servo = (ServoControl) Runtime.getService(servoName); + if (servo == null) { + warn("servo (%s) cannot move to pose because it does not exist", servoName); + return; + } + + Object speed = move.get("speed"); + if (speed != null) { + if (speed instanceof Integer) { + servo.setSpeed((Integer) speed); + } else if (speed instanceof Double) { + servo.setSpeed((Double) speed); + } + } + + Boolean blocking = (Boolean) move.get("blocking"); + Object position = move.get("position"); + if (blocking != null && blocking) { + if (position instanceof Integer) { + servo.moveToBlocking((Integer) position); + } else if (position instanceof Double) { + servo.moveToBlocking((Double) position); + } + } else { + if (position instanceof Integer) { + servo.moveTo((Integer) position); + } else if (position instanceof Double) { + servo.moveTo((Double) position); + } + } + } + + private void speak(Map speechPart) { + String mouthName = (String) speechPart.get("mouth"); + SpeechSynthesis mouth = (SpeechSynthesis) Runtime.getService(mouthName); + if (mouthName == null || mouth == null) { + error("speech synthesis service name missing"); + return; + } + try { + // makes it harder to block + // FIXME if blocking send(mouthName, "speak") + // TODO - show multiple SpeechSynthesis select like Servos + Boolean blocking = (Boolean) speechPart.get("blocking"); + if (blocking != null && blocking) { + mouth.speakBlocking((String) speechPart.get("text")); + } else { + mouth.speak((String) speechPart.get("text")); + } + } catch (Exception e) { + error(e); + } + + } + } diff --git a/src/main/java/org/myrobotlab/service/Shoutbox.java b/src/main/java/org/myrobotlab/service/Shoutbox.java index a567ae073e..e063de93a1 100644 --- a/src/main/java/org/myrobotlab/service/Shoutbox.java +++ b/src/main/java/org/myrobotlab/service/Shoutbox.java @@ -453,7 +453,7 @@ public void startChatBot() throws IOException { return; } chatbot = (ProgramAB) Runtime.start("chatbot", "ProgramAB"); - chatbot.startSession("ProgramAB", "alice2"); + chatbot.setSession("ProgramAB", "alice2"); chatbot.addResponseListener(this); } diff --git a/src/main/java/org/myrobotlab/service/SlackBot.java b/src/main/java/org/myrobotlab/service/SlackBot.java index 990fc21855..7ab10832f8 100755 --- a/src/main/java/org/myrobotlab/service/SlackBot.java +++ b/src/main/java/org/myrobotlab/service/SlackBot.java @@ -139,7 +139,7 @@ public static void main(String[] args) throws IOException, Exception { // Let's see about getting a programAB instance setup and attached to the // slack bot ProgramAB chatBot = (ProgramAB) Runtime.start("chatBot", "ProgramAB"); - chatBot.setCurrentBotName("Mr. Turing"); + chatBot.setBotType("Mr. Turing"); slackBot.attachUtteranceListener(chatBot.getName()); chatBot.attachUtteranceListener(slackBot.getName()); // Tell the slack bot to connect diff --git a/src/main/java/org/myrobotlab/service/Vertx.java b/src/main/java/org/myrobotlab/service/Vertx.java index 09cd1132ad..03c5694895 100644 --- a/src/main/java/org/myrobotlab/service/Vertx.java +++ b/src/main/java/org/myrobotlab/service/Vertx.java @@ -22,12 +22,16 @@ /** * Vertx gateway - used to support a http and websocket gateway for myrobotlab. - * Write business logic in Verticles. Also, try not to write any logic besides initialization inside start() method. + * Write business logic in Verticles. Also, try not to write any logic besides + * initialization inside start() method. * - * It currently does not utilize the Vertx event bus - which is pretty much the most important part of Vertx. - * TODO: take advantage of publishing on the event bus + * It currently does not utilize the Vertx event bus - which is pretty much the + * most important part of Vertx. TODO: take advantage of publishing on the event + * bus * - * @see https://medium.com/@pvub/https-medium-com-pvub-vert-x-workers-6a8df9b2b9ee + * vertx + * worker * * @author GroG * @@ -45,7 +49,8 @@ public Vertx(String n, String id) { } /** - * deploys a http and websocket verticle on a secure TLS channel with self signed certificate + * deploys a http and websocket verticle on a secure TLS channel with self + * signed certificate */ public void start() { log.info("starting driver"); @@ -73,13 +78,13 @@ public void start() { vertx.deployVerticle(new ApiVerticle(this)); } - + @Override public void startService() { super.startService(); start(); } - + @Override public void stopService() { super.stopService(); @@ -111,16 +116,16 @@ public static void main(String[] args) { Vertx vertx = (Vertx) Runtime.start("vertx", "Vertx"); vertx.start(); - InMoov2 i01 = (InMoov2)Runtime.start("i01", "InMoov2"); - // i01.startSimulator(); - JMonkeyEngine jme = (JMonkeyEngine)i01.startPeer("simulator"); -// Runtime.start("python", "Python"); -// - WebGui webgui = (WebGui) Runtime.create("webgui", "WebGui"); - // webgui.setSsl(true); - webgui.autoStartBrowser(false); - webgui.setPort(8888); - webgui.startService(); + InMoov2 i01 = (InMoov2) Runtime.start("i01", "InMoov2"); + // i01.startSimulator(); + JMonkeyEngine jme = (JMonkeyEngine) i01.startPeer("simulator"); + // Runtime.start("python", "Python"); + // + WebGui webgui = (WebGui) Runtime.create("webgui", "WebGui"); + // webgui.setSsl(true); + webgui.autoStartBrowser(false); + webgui.setPort(8888); + webgui.startService(); } catch (Exception e) { log.error("main threw", e); @@ -128,36 +133,47 @@ public static void main(String[] args) { } // FIXME - refactor for bare minimum - - @Override /* FIXME "Gateway" is server/service oriented not connecting thing - remove this */ + + @Override /* + * FIXME "Gateway" is server/service oriented not connecting thing - + * remove this + */ public void connect(String uri) throws URISyntaxException { // TODO Auto-generated method stub } - - @Override /* FIXME not much point of these - as they are all consistently using Runtime's centralized connection info */ + + @Override /* + * FIXME not much point of these - as they are all consistently + * using Runtime's centralized connection info + */ public List getClientIds() { return Runtime.getInstance().getConnectionUuids(getName()); } - @Override /* FIXME not much point of these - as they are all consistently using Runtime's centralized connection info */ + @Override /* + * FIXME not much point of these - as they are all consistently + * using Runtime's centralized connection info + */ public Map getClients() { return Runtime.getInstance().getConnections(getName()); } - - @Override /* FIXME this is the one and probably "only" relevant method for Gateway - perhaps a handle(Connection c) */ + @Override /* + * FIXME this is the one and probably "only" relevant method for + * Gateway - perhaps a handle(Connection c) + */ public void sendRemote(Message msg) throws Exception { log.info("sendRemote {}", msg.toString()); // FIXME MUST BE DIRECT THREAD FROM BROADCAST NOT OUTBOX !!! msg.addHop(getId()); Map clients = getClients(); - for(Connection c: clients.values()) { + for (Connection c : clients.values()) { try { - ServerWebSocket socket = (ServerWebSocket)c.get("websocket"); - String json = CodecUtils.toJsonMsg(msg); - socket.writeTextMessage(json); - } catch(Exception e) { + ServerWebSocket socket = (ServerWebSocket) c.get("websocket"); + String json = CodecUtils.toJsonMsg(msg); + socket.writeTextMessage(json); + } catch (Exception e) { error(e); } } @@ -166,8 +182,9 @@ public void sendRemote(Message msg) throws Exception { @Override public boolean isLocal(Message msg) { - return Runtime.getInstance().isLocal(msg); } - + return Runtime.getInstance().isLocal(msg); + } + public io.vertx.core.Vertx getVertx() { return vertx; } diff --git a/src/main/java/org/myrobotlab/service/WebGui.java b/src/main/java/org/myrobotlab/service/WebGui.java index d007befe6e..39b5603658 100644 --- a/src/main/java/org/myrobotlab/service/WebGui.java +++ b/src/main/java/org/myrobotlab/service/WebGui.java @@ -62,7 +62,8 @@ * services are already APIs - perhaps a data API - same as service without the * message wrapper */ -public class WebGui extends Service implements AuthorizationProvider, Gateway, Handler, ServiceLifeCycleListener { +public class WebGui extends Service + implements AuthorizationProvider, Gateway, Handler, ServiceLifeCycleListener { public static class LiveVideoStreamHandler implements Handler { @@ -89,7 +90,7 @@ public void handle(AtmosphereResource r) { } } } - + private final transient IncomingMsgQueue inMsgQueue = new IncomingMsgQueue(); public static class Panel { @@ -127,7 +128,7 @@ public Panel(String name, int x, int y, int z) { * needed to get the api key to select the appropriate api processor * * @param uri - * u + * u * @return api key * */ @@ -270,9 +271,9 @@ public boolean getAutoStartBrowser() { * String broadcast to specific client * * @param uuid - * u + * u * @param str - * s + * s * */ public void broadcast(String uuid, String str) { @@ -314,7 +315,9 @@ public Config.Builder getNettosphereConfig() { // cert.privateKey()).build(); SelfSignedCertificate selfSignedCertificate = new SelfSignedCertificate(); - SslContext context = SslContextBuilder.forServer(selfSignedCertificate.certificate(), selfSignedCertificate.privateKey()).sslProvider(SslProvider.JDK) + SslContext context = SslContextBuilder + .forServer(selfSignedCertificate.certificate(), selfSignedCertificate.privateKey()) + .sslProvider(SslProvider.JDK) .clientAuth(ClientAuth.NONE).build(); configBuilder.sslContext(context); @@ -493,7 +496,8 @@ public void handle(AtmosphereResource r) { } else if ((bodyData != null) && log.isDebugEnabled()) { logData = bodyData; } - log.debug("-->{} {} {} - [{}] from connection {}", (newPersistentConnection) ? "new" : "", request.getMethod(), request.getRequestURI(), logData, uuid); + log.debug("-->{} {} {} - [{}] from connection {}", (newPersistentConnection) ? "new" : "", request.getMethod(), + request.getRequestURI(), logData, uuid); } // important persistent connections will have associated routes ... @@ -570,7 +574,8 @@ public void handle(AtmosphereResource r) { } if (msg.containsHop(getId())) { - log.error("{} dumping duplicate hop msg to avoid cyclical from {} --to--> {}.{}", getName(), msg.sender, msg.name, msg.method); + log.error("{} dumping duplicate hop msg to avoid cyclical from {} --to--> {}.{}", getName(), msg.sender, + msg.name, msg.method); return; } @@ -914,7 +919,7 @@ public void run() { * remotely control UI * * @param panel - * - the panel which has been moved or resized + * - the panel which has been moved or resized */ public void savePanel(Panel panel) { if (panel.name == null) { @@ -1072,7 +1077,7 @@ public void startService() { public void stop() { if (nettosphere != null) { - log.warn("==== nettosphere STOPPING ===="); + log.info("==== nettosphere STOPPING ===="); // done so a thread "from" webgui can stop itself :P // Must not be called from a I/O-Thread to prevent deadlocks! new Thread() { @@ -1081,7 +1086,7 @@ public void run() { nettosphere.framework().removeAllAtmosphereHandler(); nettosphere.stop(); nettosphere = null; - log.warn("==== nettosphere STOPPED ===="); + log.info("==== nettosphere STOPPED ===="); } }.start(); } @@ -1101,7 +1106,7 @@ public void releaseService() { * Default (false) is to use the CDN * * @param useLocalResources - * - true uses local resources fals uses cdn + * - true uses local resources fals uses cdn */ public void useLocalResources(boolean useLocalResources) { this.useLocalResources = useLocalResources; @@ -1161,7 +1166,7 @@ public WebGuiConfig getConfig() { public WebGuiConfig apply(WebGuiConfig c) { super.apply(c); - + if (c.port != null && (port != null && c.port.intValue() != port.intValue())) { setPort(c.port); } @@ -1177,15 +1182,15 @@ public static void main(String[] args) { try { - // Platform.setVirtual(true); - - Runtime.startConfig("dev"); - + Runtime.main(new String[] { "--log-level", "info", "-s", "webgui", "WebGui", "intro", "Intro", "python", "Python" }); boolean done = true; if (done) { return; } - + + // Platform.setVirtual(true); + // Runtime.main(new String[] { "--log-level", "info", "-s", "webgui", "WebGui", "intro", "Intro", "python", "Python", "-c", "dev" }); + // Runtime.startConfig("dev"); // Runtime.start("python", "Python"); // Arduino arduino = (Arduino)Runtime.start("arduino", "Arduino"); @@ -1196,11 +1201,13 @@ public static void main(String[] args) { // webgui.setSsl(true); webgui.startService(); + + Runtime.start("python", "Python"); + // Runtime.start("intro", "Intro"); // Runtime.start("i01", "InMoov2"); - // Runtime.start("i01", "InMoov2"); // Runtime.start("python", "Python"); // Runtime.start("i01", "InMoov2"); @@ -1239,7 +1246,8 @@ public static void main(String[] args) { arduino.connect("/dev/ttyACM0"); for (int i = 0; i < 1000; ++i) { - webgui.display("https://i.kinja-img.com/gawker-media/image/upload/c_scale,f_auto,fl_progressive,q_80,w_800/pytutcxcrfjvuhz2jipa.jpg"); + webgui.display( + "https://i.kinja-img.com/gawker-media/image/upload/c_scale,f_auto,fl_progressive,q_80,w_800/pytutcxcrfjvuhz2jipa.jpg"); } // Runtime.setLogLevel("ERROR"); @@ -1303,5 +1311,4 @@ public void onStopped(String name) { public void onReleased(String name) { } - } \ No newline at end of file diff --git a/src/main/java/org/myrobotlab/service/WorkE.java b/src/main/java/org/myrobotlab/service/WorkE.java index 9bede4d64b..0bd3c553cc 100644 --- a/src/main/java/org/myrobotlab/service/WorkE.java +++ b/src/main/java/org/myrobotlab/service/WorkE.java @@ -26,7 +26,8 @@ import org.myrobotlab.service.interfaces.TextPublisher; import org.slf4j.Logger; -public class WorkE extends Service implements StatusListener, TextPublisher, SpeechSynthesisControl, SpeechSynthesisControlPublisher, JoystickListener { +public class WorkE extends Service implements StatusListener, TextPublisher, SpeechSynthesisControl, + SpeechSynthesisControlPublisher, JoystickListener { public final static Logger log = LoggerFactory.getLogger(WorkE.class); @@ -231,13 +232,13 @@ public void attach(Attachable attachable) { * TODO - make a brain interface ?? * * @param service - * the programab for the brain + * the programab for the brain * */ public void attachBrain(ProgramAB service) { brain = service.getName(); speak("attaching brain"); - send(brain, "addBotPath", getResourcePath("bot/WorkE")); + send(brain, "addBot", getResourcePath("bot/WorkE")); send(brain, "setCurrentBotName", "WorkE"); send(brain, "setCurrentUserName", "master"); @@ -558,7 +559,7 @@ public void publishLeftMotorStop() { * ChassiControl interface * * @param pwr - * power + * power * @return the power * */ @@ -572,7 +573,7 @@ public double publishMotorLeftMove(double pwr) { * ChassiControl interface * * @param pwr - * power + * power * @return the power * */ @@ -706,7 +707,7 @@ public void setBrain(String name) { * must NOT end in bots - is its parent folder * * @param path - * path + * path * @return the brainPath * */ diff --git a/src/main/java/org/myrobotlab/service/_TemplateService.java b/src/main/java/org/myrobotlab/service/_TemplateService.java index 61798b307e..fd9c5d5d5c 100644 --- a/src/main/java/org/myrobotlab/service/_TemplateService.java +++ b/src/main/java/org/myrobotlab/service/_TemplateService.java @@ -17,24 +17,6 @@ public _TemplateService(String n, String id) { super(n, id); } - /** - * The methods apply and getConfig can be used, if more complex configuration handling is needed. - * By default, the framework takes care of most of it, including subscription handling. - *
-  @Override
-  public ServiceConfig apply(ServiceConfig c) {
-    super.apply(c)
-    return c;
-  }
-
-  @Override
-  public ServiceConfig getConfig() {
-    super.getConfig()
-    return config;
-  }
-  
- **/ - public static void main(String[] args) { try { diff --git a/src/main/java/org/myrobotlab/service/abstracts/AbstractServo.java b/src/main/java/org/myrobotlab/service/abstracts/AbstractServo.java index 5c57cef155..39aab3550b 100644 --- a/src/main/java/org/myrobotlab/service/abstracts/AbstractServo.java +++ b/src/main/java/org/myrobotlab/service/abstracts/AbstractServo.java @@ -6,7 +6,6 @@ import java.util.Set; import org.myrobotlab.codec.CodecUtils; -import org.myrobotlab.framework.Config; import org.myrobotlab.framework.Registration; import org.myrobotlab.framework.Service; import org.myrobotlab.framework.interfaces.Attachable; @@ -611,7 +610,7 @@ public Double moveTo(Double newPos) { * controller or calling moveTo when blocking is in process */ if (newPos == null) { - log.info("{} will not move to null position - not moving", getName()); + log.debug("{} will not move to null position - not moving", getName()); return newPos; } @@ -861,7 +860,6 @@ public void setMinMax(double minXY, double maxXY) { } @Override - @Config // default - if pin is different - output servo.setPin() public void setPin(Integer pin) { if (pin == null) { log.info("{}.setPin(null) as pin is not a valid pin value", pin); @@ -1160,7 +1158,7 @@ public C apply(C c) { if (c.synced != null) { syncedServos.clear(); - Collections.addAll(syncedServos, c.synced); + Collections.addAll(syncedServos, c.synced); } // rest = c.rest; diff --git a/src/main/java/org/myrobotlab/service/config/EmojiConfig.java b/src/main/java/org/myrobotlab/service/config/EmojiConfig.java index e2d6cbbdcc..f2d427655f 100644 --- a/src/main/java/org/myrobotlab/service/config/EmojiConfig.java +++ b/src/main/java/org/myrobotlab/service/config/EmojiConfig.java @@ -7,6 +7,9 @@ public class EmojiConfig extends ServiceConfig { + /** + * emoji repo + */ public String emojiSourceUrlTemplate = "https://raw.githubusercontent.com/googlefonts/noto-emoji/main/png/{size}/emoji_{code}.png"; /** @@ -14,7 +17,10 @@ public class EmojiConfig extends ServiceConfig { */ public Map map = new HashMap(); - public int size = 512; + /** + * repo has 32, 72, 128, and 512 + */ + public int size = 72; @Override public Plan getDefault(Plan plan, String name) { diff --git a/src/main/java/org/myrobotlab/service/config/InMoov2Config.java b/src/main/java/org/myrobotlab/service/config/InMoov2Config.java index af6c68c7bf..860ecb32b5 100644 --- a/src/main/java/org/myrobotlab/service/config/InMoov2Config.java +++ b/src/main/java/org/myrobotlab/service/config/InMoov2Config.java @@ -1,406 +1,536 @@ package org.myrobotlab.service.config; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; import org.myrobotlab.framework.Plan; import org.myrobotlab.jme3.UserDataConfig; import org.myrobotlab.math.MapperLinear; +import org.myrobotlab.math.MapperSimple; import org.myrobotlab.service.Pid.PidData; import org.myrobotlab.service.Runtime; import org.myrobotlab.service.config.FiniteStateMachineConfig.Transition; import org.myrobotlab.service.config.RandomConfig.RandomMessageConfig; +/** + * InMoov2Config - configuration for InMoov2 service + * - this is a "default" configuration + * If its configuration which will directly affect another service the naming + * pattern should be {peerName}{propertyName} + * e.g. neoPixelErrorRed + * + * FIXME make a color map that can be overridden + * + * @author GroG + * + */ public class InMoov2Config extends ServiceConfig { - public int analogPinFromSoundCard = 53; - - public int audioPollsBySeconds = 2; - - public boolean audioSignalProcessing=false; - - public boolean batteryInSystem = false; - - 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; - - /** - * default to null - allow the OS to set it, unless explicilty set - */ - public String locale = null; // = "en-US"; - - public boolean neoPixelBootGreen=true; - - public boolean neoPixelDownloadBlue = true; - - public boolean neoPixelErrorRed = true; - - public boolean neoPixelFlashWhenSpeaking = 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; - - - /** - * 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 - */ - public int sleepTimeoutMs=300000; - - public boolean startupSound = true; - - public int trackingTimeoutMs=10000; - - public String unlockInsult = "forgive me"; - - public boolean virtual = false; - - public InMoov2Config() { - } - - @Override - public Plan getDefault(Plan plan, String name) { - super.getDefault(plan, name); - - // peers FIXME global opencv - addDefaultPeerConfig(plan, name, "audioPlayer", "AudioFile", 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); - addDefaultPeerConfig(plan, name, "eyeTracking", "Tracking", false); - addDefaultPeerConfig(plan, name, "fsm", "FiniteStateMachine", false); - addDefaultPeerConfig(plan, name, "gpt3", "Gpt3", false); - addDefaultPeerConfig(plan, name, "head", "InMoov2Head", false); - addDefaultPeerConfig(plan, name, "headTracking", "Tracking", false); - addDefaultPeerConfig(plan, name, "htmlFilter", "HtmlFilter", true); - 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); - addDefaultPeerConfig(plan, name, "openni", "OpenNi", false); - addDefaultPeerConfig(plan, name, "openWeatherMap", "OpenWeatherMap", false); - addDefaultPeerConfig(plan, name, "pid", "Pid", false); - addDefaultPeerConfig(plan, name, "pir", "Pir", false); - addDefaultPeerConfig(plan, name, "random", "Random", false); - addDefaultPeerConfig(plan, name, "right", "Arduino", false); - addDefaultPeerConfig(plan, name, "rightArm", "InMoov2Arm", false); - addDefaultPeerConfig(plan, name, "rightHand", "InMoov2Hand", false); - addDefaultPeerConfig(plan, name, "servoMixer", "ServoMixer", false); - addDefaultPeerConfig(plan, name, "simulator", "JMonkeyEngine", false); - addDefaultPeerConfig(plan, name, "torso", "InMoov2Torso", false); - addDefaultPeerConfig(plan, name, "ultrasonicRight", "UltrasonicSensor", false); - addDefaultPeerConfig(plan, name, "ultrasonicLeft", "UltrasonicSensor", false); - - MouthControlConfig mouthControl = (MouthControlConfig) plan.get(getPeerName("mouthControl")); - - // setup name references to different services - mouthControl.jaw = name + ".head.jaw"; - String i01Name = name; - int index = name.indexOf("."); - if (index > 0) { - i01Name = name.substring(0, name.indexOf(".")); + /** + * When the healthCheck is operating, it will check the battery level. + * If the battery level is < 5% it will publishFlash with red at regular + * interval + */ + public boolean batteryInSystem = false; + + public boolean customSound = false; + + public boolean forceMicroOnIfSleeping = true; + + public boolean healthCheckActivated = false; + + public int healthCheckTimerMs = 60000; + + /** + * Single heartbeat to drive InMoov2 .. it can check status, healthbeat, + * and fire events to the FSM. + * Checks battery level and sends a heartbeat flash on publishHeartbeat + * and onHeartbeat at a regular interval + */ + public boolean heartbeat = true; + + /** + * interval heath check processes in milliseconds + */ + public long heartbeatInterval = 3000; + + /** + * loads all python gesture files in the gesture directory + */ + public boolean loadGestures = true; + + /** + * executes all scripts in the init directory on startup + */ + public boolean loadInitScripts = true; + + /** + * default to null - allow the OS to set it, unless explicilty set + */ + public String locale = null; // = "en-US"; + + public boolean neoPixelBootGreen = true; + + public boolean neoPixelDownloadBlue = true; + + public boolean neoPixelErrorRed = true; + + public boolean neoPixelFlashWhenSpeaking = 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; + + /** + * 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 + */ + public int sleepTimeoutMs = 300000; + + public boolean startupSound = true; + + public int trackingTimeoutMs = 10000; + + public String unlockInsult = "forgive me"; + + public boolean virtual = false; + + public String bootAnimation = "Theater Chase"; + + public boolean flashOnErrors = true; + + public boolean flashOnPir; + + public boolean loadAppsScripts = true; + + public boolean systemEventsOnBoot = true; + + public Integer stateRandomInterval = null; + + public Integer stateIdleInterval = null; + + public InMoov2Config() { } - mouthControl.mouth = i01Name + ".mouth"; - - ProgramABConfig chatBot = (ProgramABConfig) plan.get(getPeerName("chatBot")); - Runtime runtime = Runtime.getInstance(); - String[] bots = new String[] { "cn-ZH", "en-US", "fi-FI", "hi-IN", "nl-NL", "ru-RU", "de-DE", "es-ES", "fr-FR", "it-IT", "pt-PT", "tr-TR" }; - String tag = runtime.getLocaleTag(); - if (tag != null) { - String[] tagparts = tag.split("-"); - String lang = tagparts[0]; - for (String b : bots) { - if (b.startsWith(lang)) { - chatBot.currentBotName = b; - break; + @Override + public Plan getDefault(Plan plan, String name) { + super.getDefault(plan, name); + + // FIXME define global peers named "python" "webgui" etc... + + // peers FIXME global opencv + addDefaultPeerConfig(plan, name, "audioPlayer", "AudioFile", 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); + addDefaultPeerConfig(plan, name, "eyeTracking", "Tracking", false); + addDefaultPeerConfig(plan, name, "fsm", "FiniteStateMachine", false); + addDefaultPeerConfig(plan, name, "log", "Log", false); + addDefaultPeerConfig(plan, name, "gpt3", "Gpt3", false); + addDefaultPeerConfig(plan, name, "head", "InMoov2Head", false); + addDefaultPeerConfig(plan, name, "headTracking", "Tracking", false); + addDefaultPeerConfig(plan, name, "htmlFilter", "HtmlFilter", true); + 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, "mouth.audioFile", "AudioFile", false); + addDefaultPeerConfig(plan, name, "mouthControl", "MouthControl", false); + addDefaultPeerConfig(plan, name, "neoPixel", "NeoPixel", false); + addDefaultPeerConfig(plan, name, "opencv", "OpenCV", false); + addDefaultPeerConfig(plan, name, "openni", "OpenNi", false); + addDefaultPeerConfig(plan, name, "openWeatherMap", "OpenWeatherMap", false); + addDefaultPeerConfig(plan, name, "pid", "Pid", false); + addDefaultPeerConfig(plan, name, "pir", "Pir", false); + addDefaultPeerConfig(plan, name, "random", "Random", false); + addDefaultPeerConfig(plan, name, "right", "Arduino", false); + addDefaultPeerConfig(plan, name, "rightArm", "InMoov2Arm", false); + addDefaultPeerConfig(plan, name, "rightHand", "InMoov2Hand", false); + addDefaultPeerConfig(plan, name, "servoMixer", "ServoMixer", false); + addDefaultPeerConfig(plan, name, "simulator", "JMonkeyEngine", false); + addDefaultPeerConfig(plan, name, "torso", "InMoov2Torso", false); + addDefaultPeerConfig(plan, name, "ultrasonicRight", "UltrasonicSensor", false); + addDefaultPeerConfig(plan, name, "ultrasonicLeft", "UltrasonicSensor", false); + addDefaultPeerConfig(plan, name, "vertx", "Vertx", false); + addDefaultPeerConfig(plan, name, "webxr", "WebXR", false); + + WebXRConfig webxr = (WebXRConfig) plan.get(getPeerName("webxr")); + + Map map = new HashMap<>(); + MapperSimple mapper = new MapperSimple(-0.5, 0.5, 0, 180); + map.put("i01.head.neck", mapper); + webxr.controllerMappings.put("head.orientation.pitch", map); + + map = new HashMap<>(); + mapper = new MapperSimple(-0.5, 0.5, 0, 180); + map.put("i01.head.rothead", mapper); + webxr.controllerMappings.put("head.orientation.yaw", map); + + map = new HashMap<>(); + mapper = new MapperSimple(-0.5, 0.5, 0, 180); + map.put("i01.head.roll", mapper); + webxr.controllerMappings.put("head.orientation.roll", map); + + MouthControlConfig mouthControl = (MouthControlConfig) plan.get(getPeerName("mouthControl")); + + // setup name references to different services + mouthControl.jaw = name + ".head.jaw"; + String i01Name = name; + int index = name.indexOf("."); + if (index > 0) { + i01Name = name.substring(0, name.indexOf(".")); } - } - } - - chatBot.currentUserName = "human"; - - // chatBot.textListeners = new String[] { name + ".htmlFilter" }; - if (chatBot.listeners == null) { - chatBot.listeners = new ArrayList<>(); + + mouthControl.mouth = i01Name + ".mouth"; + + ProgramABConfig chatBot = (ProgramABConfig) plan.get(getPeerName("chatBot")); + chatBot.listeners = new ArrayList<>(); + + Runtime runtime = Runtime.getInstance(); + String[] bots = new String[] { "cn-ZH", "en-US", "fi-FI", "hi-IN", "nl-NL", "ru-RU", "de-DE", "es-ES", "fr-FR", + "it-IT", "pt-PT", "tr-TR" }; + String tag = runtime.getLocaleTag(); + if (tag != null) { + String[] tagparts = tag.split("-"); + String lang = tagparts[0]; + for (String b : bots) { + if (b.startsWith(lang)) { + chatBot.botType = b; + break; + } + } + } + + chatBot.username = "human"; + + // chatBot.textListeners = new String[] { name + ".htmlFilter" }; + if (chatBot.listeners == null) { + chatBot.listeners = new ArrayList<>(); + } + chatBot.listeners.add(new Listener("publishText", name + ".htmlFilter", "onText")); + + HtmlFilterConfig htmlFilter = (HtmlFilterConfig) plan.get(getPeerName("htmlFilter")); + // htmlFilter.textListeners = new String[] { name + ".mouth" }; + htmlFilter.listeners = new ArrayList<>(); + htmlFilter.listeners.add(new Listener("publishText", name + ".mouth", "onText")); + + // FIXME - turns out subscriptions like this are not needed if they are in + // onStarted + // == Peer - mouth ============================= + // setup name references to different services + MarySpeechConfig mouth = (MarySpeechConfig) plan.get(getPeerName("mouth")); + mouth.voice = "Mark"; + mouth.speechRecognizers = new String[] { name + ".ear" }; + + // == Peer - servoMixer ============================= + // setup name references to different services + ServoMixerConfig servoMixer = (ServoMixerConfig) plan.get(getPeerName("servoMixer")); + servoMixer.listeners = new ArrayList<>(); + servoMixer.listeners.add(new Listener("publishText", name + ".mouth", "onText")); + // servoMixer.listeners.add(new Listener("publishText", name + ".chatBot", + // "onText")); + + // == Peer - ear ============================= + // setup name references to different services + WebkitSpeechRecognitionConfig ear = (WebkitSpeechRecognitionConfig) plan.get(getPeerName("ear")); + ear.listeners = new ArrayList<>(); + ear.listeners.add(new Listener("publishText", name + ".chatBot", "onText")); + ear.listening = true; + // remove, should only need ServiceConfig.listeners + ear.textListeners = new String[] { name + ".chatBot" }; + + JMonkeyEngineConfig simulator = (JMonkeyEngineConfig) plan.get(getPeerName("simulator")); + + simulator.multiMapped.put(name + ".leftHand.index", + new String[] { name + ".leftHand.index", name + ".leftHand.index2", name + ".leftHand.index3" }); + simulator.multiMapped.put(name + ".leftHand.majeure", + new String[] { name + ".leftHand.majeure", name + ".leftHand.majeure2", name + ".leftHand.majeure3" }); + simulator.multiMapped.put(name + ".leftHand.pinky", + new String[] { name + ".leftHand.pinky", name + ".leftHand.pinky2", name + ".leftHand.pinky3" }); + simulator.multiMapped.put(name + ".leftHand.ringFinger", + new String[] { name + ".leftHand.ringFinger", name + ".leftHand.ringFinger2", + name + ".leftHand.ringFinger3" }); + simulator.multiMapped.put(name + ".leftHand.thumb", + new String[] { name + ".leftHand.thumb1", name + ".leftHand.thumb2", name + ".leftHand.thumb3" }); + + simulator.multiMapped.put(name + ".rightHand.index", + new String[] { name + ".rightHand.index", name + ".rightHand.index2", name + ".rightHand.index3" }); + simulator.multiMapped.put(name + ".rightHand.majeure", + new String[] { name + ".rightHand.majeure", name + ".rightHand.majeure2", + name + ".rightHand.majeure3" }); + simulator.multiMapped.put(name + ".rightHand.pinky", + new String[] { name + ".rightHand.pinky", name + ".rightHand.pinky2", name + ".rightHand.pinky3" }); + simulator.multiMapped.put(name + ".rightHand.ringFinger", new String[] { name + ".rightHand.ringFinger", + name + ".rightHand.ringFinger2", name + ".rightHand.ringFinger3" }); + simulator.multiMapped.put(name + ".rightHand.thumb", + new String[] { name + ".rightHand.thumb1", name + ".rightHand.thumb2", name + ".rightHand.thumb3" }); + + // simulator.nodes.put("camera", new UserData()); + simulator.nodes.put(name + ".head.jaw", + new UserDataConfig(new MapperLinear(0.0, 180.0, -5.0, 80.0, true, false), "x")); + simulator.nodes.put(name + ".head.neck", + new UserDataConfig(new MapperLinear(0.0, 180.0, 20.0, -20.0, true, false), "x")); + simulator.nodes.put(name + ".head.rothead", new UserDataConfig(null, "y")); + simulator.nodes.put(name + ".head.rollNeck", + new UserDataConfig(new MapperLinear(0.0, 180.0, 30.0, -30.0, true, false), "z")); + simulator.nodes.put(name + ".head.eyeY", + new UserDataConfig(new MapperLinear(0.0, 180.0, 40.0, 140.0, true, false), "x")); + simulator.nodes.put(name + ".head.eyeX", + new UserDataConfig(new MapperLinear(0.0, 180.0, -10.0, 70.0, true, false), "y")); + simulator.nodes.put(name + ".torso.topStom", + new UserDataConfig(new MapperLinear(0.0, 180.0, -30.0, 30.0, true, false), "z")); + simulator.nodes.put(name + ".torso.midStom", + new UserDataConfig(new MapperLinear(0.0, 180.0, 50.0, 130.0, true, false), "y")); + simulator.nodes.put(name + ".torso.lowStom", + new UserDataConfig(new MapperLinear(0.0, 180.0, -30.0, 30.0, true, false), "x")); + simulator.nodes.put(name + ".rightArm.bicep", + new UserDataConfig(new MapperLinear(0.0, 180.0, 0.0, -150.0, true, false), "x")); + simulator.nodes.put(name + ".leftArm.bicep", + new UserDataConfig(new MapperLinear(0.0, 180.0, 0.0, -150.0, true, false), "x")); + simulator.nodes.put(name + ".rightArm.shoulder", + new UserDataConfig(new MapperLinear(0.0, 180.0, 30.0, -150.0, true, false), "x")); + simulator.nodes.put(name + ".leftArm.shoulder", + new UserDataConfig(new MapperLinear(0.0, 180.0, 30.0, -150.0, true, false), "x")); + simulator.nodes.put(name + ".rightArm.rotate", + new UserDataConfig(new MapperLinear(0.0, 180.0, 80.0, -80.0, true, false), "y")); + simulator.nodes.put(name + ".leftArm.rotate", + new UserDataConfig(new MapperLinear(0.0, 180.0, -80.0, 80.0, true, false), "y")); + simulator.nodes.put(name + ".rightArm.omoplate", + new UserDataConfig(new MapperLinear(0.0, 180.0, 10.0, -180.0, true, false), "z")); + simulator.nodes.put(name + ".leftArm.omoplate", + new UserDataConfig(new MapperLinear(0.0, 180.0, -10.0, 180.0, true, false), "z")); + simulator.nodes.put(name + ".rightHand.wrist", + new UserDataConfig(new MapperLinear(0.0, 180.0, -20.0, 60.0, true, false), "y")); + simulator.nodes.put(name + ".leftHand.wrist", + new UserDataConfig(new MapperLinear(0.0, 180.0, 20.0, -60.0, true, false), "y")); + simulator.nodes.put(name + ".leftHand.thumb1", + new UserDataConfig(new MapperLinear(0.0, 180.0, -30.0, -100.0, true, false), "y")); + simulator.nodes.put(name + ".leftHand.thumb2", + new UserDataConfig(new MapperLinear(0.0, 180.0, 80.0, 20.0, true, false), "x")); + simulator.nodes.put(name + ".leftHand.thumb3", + new UserDataConfig(new MapperLinear(0.0, 180.0, 80.0, 20.0, true, false), "x")); + simulator.nodes.put(name + ".leftHand.index", + new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); + simulator.nodes.put(name + ".leftHand.index2", + new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); + simulator.nodes.put(name + ".leftHand.index3", + new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); + simulator.nodes.put(name + ".leftHand.majeure", + new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); + simulator.nodes.put(name + ".leftHand.majeure2", + new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); + simulator.nodes.put(name + ".leftHand.majeure3", + new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); + simulator.nodes.put(name + ".leftHand.ringFinger", + new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); + simulator.nodes.put(name + ".leftHand.ringFinger2", + new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); + simulator.nodes.put(name + ".leftHand.ringFinger3", + new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); + simulator.nodes.put(name + ".leftHand.pinky", + new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); + simulator.nodes.put(name + ".leftHand.pinky2", + new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); + simulator.nodes.put(name + ".leftHand.pinky3", + new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); + simulator.nodes.put(name + ".rightHand.thumb1", + new UserDataConfig(new MapperLinear(0.0, 180.0, 30.0, 110.0, true, false), "y")); + simulator.nodes.put(name + ".rightHand.thumb2", + new UserDataConfig(new MapperLinear(0.0, 180.0, -100.0, -150.0, true, false), "x")); + simulator.nodes.put(name + ".rightHand.thumb3", + new UserDataConfig(new MapperLinear(0.0, 180.0, -100.0, -160.0, true, false), "x")); + simulator.nodes.put(name + ".rightHand.index", + new UserDataConfig(new MapperLinear(0.0, 180.0, 65.0, -10.0, true, false), "x")); + simulator.nodes.put(name + ".rightHand.index2", + new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x")); + simulator.nodes.put(name + ".rightHand.index3", + new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x")); + simulator.nodes.put(name + ".rightHand.majeure", + new UserDataConfig(new MapperLinear(0.0, 180.0, 65.0, -10.0, true, false), "x")); + simulator.nodes.put(name + ".rightHand.majeure2", + new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x")); + simulator.nodes.put(name + ".rightHand.majeure3", + new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x")); + simulator.nodes.put(name + ".rightHand.ringFinger", + new UserDataConfig(new MapperLinear(0.0, 180.0, 65.0, -10.0, true, false), "x")); + simulator.nodes.put(name + ".rightHand.ringFinger2", + new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x")); + simulator.nodes.put(name + ".rightHand.ringFinger3", + new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x")); + simulator.nodes.put(name + ".rightHand.pinky", + new UserDataConfig(new MapperLinear(0.0, 180.0, 65.0, -10.0, true, false), "x")); + simulator.nodes.put(name + ".rightHand.pinky2", + new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x")); + simulator.nodes.put(name + ".rightHand.pinky3", + new UserDataConfig(new MapperLinear(0.0, 180.0, 60.0, -10.0, true, false), "x")); + 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 ? + fsm.current = "boot"; + fsm.transitions.add(new Transition("boot", "configStarted", "applyingConfig")); + fsm.transitions.add(new Transition("applyingConfig", "getUserInfo", "getUserInfo")); + fsm.transitions.add(new Transition("applyingConfig", "systemCheck", "systemCheck")); + fsm.transitions.add(new Transition("applyingConfig", "wake", "awake")); + fsm.transitions.add(new Transition("getUserInfo", "systemCheck", "systemCheck")); + 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 = "D23"; + 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; + + // setup name references to different services + RandomMessageConfig rm = new RandomMessageConfig(name, "setLeftArmSpeed", 3000, 8000, 8.0, 25.0, 8.0, 25.0, 8.0, + 25.0, 8.0, 25.0); + random.randomMessages.put(name + ".setLeftArmSpeed", rm); + + rm = new RandomMessageConfig(name, "setRightArmSpeed", 3000, 8000, 8, 25, 8, 25, 8, 25, 8, 25); + random.randomMessages.put(name + ".setRightArmSpeed", rm); + + rm = new RandomMessageConfig(name, "moveLeftArm", 000, 8000, 0, 5, 85, 95, 25, 30, 10, 15); + random.randomMessages.put(name + ".moveLeftArm", rm); + + rm = new RandomMessageConfig(name, "moveRightArm", 3000, 8000, 0, 5, 85, 95, 25, 30, 10, 15); + random.randomMessages.put(name + ".moveRightArm", rm); + + rm = new RandomMessageConfig(name, "setLeftHandSpeed", 3000, 8000, 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.randomMessages.put(name + ".setLeftHandSpeed", rm); + + rm = new RandomMessageConfig(name, "setRightHandSpeed", 3000, 8000, 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.randomMessages.put(name + ".setRightHandSpeed", rm); + + rm = new RandomMessageConfig(name, "moveLeftHand", 3000, 8000, 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.randomMessages.put(name + ".moveLeftHand", rm); + + rm = new RandomMessageConfig(name, "moveRightHand", 3000, 8000, 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.randomMessages.put(name + ".moveRightHand", rm); + + rm = new RandomMessageConfig(name, "setHeadSpeed", 3000, 8000, 8.0, 20.0, 8.0, 20.0, 8.0, 20.0); + random.randomMessages.put(name + ".setHeadSpeed", rm); + + rm = new RandomMessageConfig(name, "moveHead", 3000, 8000, 70, 110, 65, 115, 70, 110); + random.randomMessages.put(name + ".moveHead", rm); + + rm = new RandomMessageConfig(name, "setTorsoSpeed", 3000, 8000, 2.0, 5.0, 2.0, 5.0, 2.0, 5.0); + random.randomMessages.put(name + ".setTorsoSpeed", rm); + + rm = new RandomMessageConfig(name, "moveTorso", 3000, 8000, 85, 95, 88, 93, 70, 110); + random.randomMessages.put(name + ".moveTorso", rm); + + // == Peer - headTracking ============================= + TrackingConfig headTracking = (TrackingConfig) plan.get(getPeerName("headTracking")); + + // setup name references to different services + headTracking.getPeer("tilt").name = name + ".head.neck"; + headTracking.getPeer("pan").name = name + ".head.rothead"; + headTracking.getPeer("cv").name = name + ".opencv"; + headTracking.getPeer("pid").name = name + ".pid"; + headTracking.getPeer("controller").name = name + ".left"; + + // == Peer - eyeTracking ============================= + TrackingConfig eyeTracking = (TrackingConfig) plan.get(getPeerName("eyeTracking")); + + // setup name references to different services + eyeTracking.getPeer("tilt").name = name + ".head.eyeY"; + eyeTracking.getPeer("pan").name = name + ".head.eyeX"; + eyeTracking.getPeer("cv").name = name + ".opencv"; + eyeTracking.getPeer("pid").name = name + ".pid"; + eyeTracking.getPeer("controller").name = name + ".left"; + + // == Peer - pid ============================= + PidConfig pid = (PidConfig) plan.get(getPeerName("pid")); + + PidData tiltPid = new PidData(); + tiltPid.ki = -0.001; + tiltPid.kp = -40.0; + tiltPid.inverted = true; + pid.data.put(headTracking.getPeer("tilt").name, tiltPid); + + PidData panPid = new PidData(); + panPid.ki = 0.001; + panPid.kp = 40.0; + pid.data.put(headTracking.getPeer("pan").name, panPid); + + PidData eyeTiltPid = new PidData(); + eyeTiltPid.ki = -0.001; + eyeTiltPid.kp = -30.0; + eyeTiltPid.inverted = true; + pid.data.put(eyeTracking.getPeer("tilt").name, eyeTiltPid); + + PidData eyePanPid = new PidData(); + eyePanPid.ki = 0.001; + eyePanPid.kp = 30.0; + pid.data.put(eyeTracking.getPeer("pan").name, eyePanPid); + + NeoPixelConfig neoPixel = (NeoPixelConfig) plan.get(getPeerName("neoPixel")); + neoPixel.pin = 2; + // neoPixel.controller = String.format("%s.left", name); + neoPixel.controller = String.format("%s.controller3", name); + neoPixel.red = 12; + neoPixel.green = 180; + neoPixel.blue = 212; + neoPixel.pixelCount = 16; + neoPixel.currentAnimation = "Ironman"; + + // remove undesired defaults from our default + plan.remove(name + ".headTracking.tilt"); + plan.remove(name + ".headTracking.pan"); + plan.remove(name + ".headTracking.pid"); + plan.remove(name + ".headTracking.controller"); + plan.remove(name + ".headTracking.controller.serial"); + plan.remove(name + ".headTracking.cv"); + + plan.remove(name + ".eyeTracking.tilt"); + plan.remove(name + ".eyeTracking.pan"); + plan.remove(name + ".eyeTracking.pid"); + 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")); + + // remove the auto-added starts in the plan's runtime RuntimConfig.registry + + // rtConfig.add(name); // <-- adding i01 / not needed + + return plan; } - chatBot.listeners.add(new Listener("publishText", name + ".htmlFilter", "onText")); - - HtmlFilterConfig htmlFilter = (HtmlFilterConfig) plan.get(getPeerName("htmlFilter")); - // htmlFilter.textListeners = new String[] { name + ".mouth" }; - htmlFilter.listeners = new ArrayList<>(); - htmlFilter.listeners.add(new Listener("publishText", name + ".mouth", "onText")); - - // FIXME - turns out subscriptions like this are not needed if they are in - // onStarted - // == Peer - mouth ============================= - // setup name references to different services - MarySpeechConfig mouth = (MarySpeechConfig) plan.get(getPeerName("mouth")); - mouth.voice = "Mark"; - mouth.speechRecognizers = new String[] { name + ".ear" }; - - // == Peer - servoMixer ============================= - // setup name references to different services - ServoMixerConfig servoMixer = (ServoMixerConfig) plan.get(getPeerName("servoMixer")); - servoMixer.listeners = new ArrayList<>(); - servoMixer.listeners.add(new Listener("publishText", name + ".mouth", "onText")); - //servoMixer.listeners.add(new Listener("publishText", name + ".chatBot", "onText")); - - // == Peer - ear ============================= - // setup name references to different services - WebkitSpeechRecognitionConfig ear = (WebkitSpeechRecognitionConfig) plan.get(getPeerName("ear")); - ear.listeners = new ArrayList<>(); - ear.listeners.add(new Listener("publishText", name + ".chatBot", "onText")); - ear.listening = true; - // remove, should only need ServiceConfig.listeners - ear.textListeners = new String[]{name + ".chatBot"}; - - JMonkeyEngineConfig simulator = (JMonkeyEngineConfig) plan.get(getPeerName("simulator")); - - simulator.multiMapped.put(name + ".leftHand.index", new String[] { name + ".leftHand.index", name + ".leftHand.index2", name + ".leftHand.index3" }); - simulator.multiMapped.put(name + ".leftHand.majeure", new String[] { name + ".leftHand.majeure", name + ".leftHand.majeure2", name + ".leftHand.majeure3" }); - simulator.multiMapped.put(name + ".leftHand.pinky", new String[] { name + ".leftHand.pinky", name + ".leftHand.pinky2", name + ".leftHand.pinky3" }); - simulator.multiMapped.put(name + ".leftHand.ringFinger", new String[] { name + ".leftHand.ringFinger", name + ".leftHand.ringFinger2", name + ".leftHand.ringFinger3" }); - simulator.multiMapped.put(name + ".leftHand.thumb", new String[] { name + ".leftHand.thumb1", name + ".leftHand.thumb2", name + ".leftHand.thumb3" }); - - simulator.multiMapped.put(name + ".rightHand.index", new String[] { name + ".rightHand.index", name + ".rightHand.index2", name + ".rightHand.index3" }); - simulator.multiMapped.put(name + ".rightHand.majeure", new String[] { name + ".rightHand.majeure", name + ".rightHand.majeure2", name + ".rightHand.majeure3" }); - simulator.multiMapped.put(name + ".rightHand.pinky", new String[] { name + ".rightHand.pinky", name + ".rightHand.pinky2", name + ".rightHand.pinky3" }); - simulator.multiMapped.put(name + ".rightHand.ringFinger", new String[] { name + ".rightHand.ringFinger", name + ".rightHand.ringFinger2", name + ".rightHand.ringFinger3" }); - simulator.multiMapped.put(name + ".rightHand.thumb", new String[] { name + ".rightHand.thumb1", name + ".rightHand.thumb2", name + ".rightHand.thumb3" }); - - // simulator.nodes.put("camera", new UserData()); - simulator.nodes.put(name + ".head.jaw", new UserDataConfig(new MapperLinear(0.0, 180.0, -5.0, 80.0, true, false), "x")); - simulator.nodes.put(name + ".head.neck", new UserDataConfig(new MapperLinear(0.0, 180.0, 20.0, -20.0, true, false), "x")); - simulator.nodes.put(name + ".head.rothead", new UserDataConfig(null, "y")); - simulator.nodes.put(name + ".head.rollNeck", new UserDataConfig(new MapperLinear(0.0, 180.0, 30.0, -30.0, true, false), "z")); - simulator.nodes.put(name + ".head.eyeY", new UserDataConfig(new MapperLinear(0.0, 180.0, 40.0, 140.0, true, false), "x")); - simulator.nodes.put(name + ".head.eyeX", new UserDataConfig(new MapperLinear(0.0, 180.0, -10.0, 70.0, true, false), "y")); - simulator.nodes.put(name + ".torso.topStom", new UserDataConfig(new MapperLinear(0.0, 180.0, -30.0, 30.0, true, false), "z")); - simulator.nodes.put(name + ".torso.midStom", new UserDataConfig(new MapperLinear(0.0, 180.0, 50.0, 130.0, true, false), "y")); - simulator.nodes.put(name + ".torso.lowStom", new UserDataConfig(new MapperLinear(0.0, 180.0, -30.0, 30.0, true, false), "x")); - simulator.nodes.put(name + ".rightArm.bicep", new UserDataConfig(new MapperLinear(0.0, 180.0, 0.0, -150.0, true, false), "x")); - simulator.nodes.put(name + ".leftArm.bicep", new UserDataConfig(new MapperLinear(0.0, 180.0, 0.0, -150.0, true, false), "x")); - simulator.nodes.put(name + ".rightArm.shoulder", new UserDataConfig(new MapperLinear(0.0, 180.0, 30.0, -150.0, true, false), "x")); - simulator.nodes.put(name + ".leftArm.shoulder", new UserDataConfig(new MapperLinear(0.0, 180.0, 30.0, -150.0, true, false), "x")); - simulator.nodes.put(name + ".rightArm.rotate", new UserDataConfig(new MapperLinear(0.0, 180.0, 80.0, -80.0, true, false), "y")); - simulator.nodes.put(name + ".leftArm.rotate", new UserDataConfig(new MapperLinear(0.0, 180.0, -80.0, 80.0, true, false), "y")); - simulator.nodes.put(name + ".rightArm.omoplate", new UserDataConfig(new MapperLinear(0.0, 180.0, 10.0, -180.0, true, false), "z")); - simulator.nodes.put(name + ".leftArm.omoplate", new UserDataConfig(new MapperLinear(0.0, 180.0, -10.0, 180.0, true, false), "z")); - simulator.nodes.put(name + ".rightHand.wrist", new UserDataConfig(new MapperLinear(0.0, 180.0, -20.0, 60.0, true, false), "y")); - simulator.nodes.put(name + ".leftHand.wrist", new UserDataConfig(new MapperLinear(0.0, 180.0, 20.0, -60.0, true, false), "y")); - simulator.nodes.put(name + ".leftHand.thumb1", new UserDataConfig(new MapperLinear(0.0, 180.0, -30.0, -100.0, true, false), "y")); - simulator.nodes.put(name + ".leftHand.thumb2", new UserDataConfig(new MapperLinear(0.0, 180.0, 80.0, 20.0, true, false), "x")); - simulator.nodes.put(name + ".leftHand.thumb3", new UserDataConfig(new MapperLinear(0.0, 180.0, 80.0, 20.0, true, false), "x")); - simulator.nodes.put(name + ".leftHand.index", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); - simulator.nodes.put(name + ".leftHand.index2", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); - simulator.nodes.put(name + ".leftHand.index3", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); - simulator.nodes.put(name + ".leftHand.majeure", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); - simulator.nodes.put(name + ".leftHand.majeure2", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); - simulator.nodes.put(name + ".leftHand.majeure3", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); - simulator.nodes.put(name + ".leftHand.ringFinger", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); - simulator.nodes.put(name + ".leftHand.ringFinger2", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); - simulator.nodes.put(name + ".leftHand.ringFinger3", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); - simulator.nodes.put(name + ".leftHand.pinky", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); - simulator.nodes.put(name + ".leftHand.pinky2", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); - simulator.nodes.put(name + ".leftHand.pinky3", new UserDataConfig(new MapperLinear(0.0, 180.0, -110.0, -179.0, true, false), "x")); - simulator.nodes.put(name + ".rightHand.thumb1", new UserDataConfig(new MapperLinear(0.0, 180.0, 30.0, 110.0, true, false), "y")); - simulator.nodes.put(name + ".rightHand.thumb2", new UserDataConfig(new MapperLinear(0.0, 180.0, -100.0, -150.0, true, false), "x")); - simulator.nodes.put(name + ".rightHand.thumb3", new UserDataConfig(new MapperLinear(0.0, 180.0, -100.0, -160.0, true, false), "x")); - simulator.nodes.put(name + ".rightHand.index", new UserDataConfig(new MapperLinear(0.0, 180.0, 65.0, -10.0, true, false), "x")); - simulator.nodes.put(name + ".rightHand.index2", new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x")); - simulator.nodes.put(name + ".rightHand.index3", new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x")); - simulator.nodes.put(name + ".rightHand.majeure", new UserDataConfig(new MapperLinear(0.0, 180.0, 65.0, -10.0, true, false), "x")); - simulator.nodes.put(name + ".rightHand.majeure2", new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x")); - simulator.nodes.put(name + ".rightHand.majeure3", new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x")); - simulator.nodes.put(name + ".rightHand.ringFinger", new UserDataConfig(new MapperLinear(0.0, 180.0, 65.0, -10.0, true, false), "x")); - simulator.nodes.put(name + ".rightHand.ringFinger2", new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x")); - simulator.nodes.put(name + ".rightHand.ringFinger3", new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x")); - simulator.nodes.put(name + ".rightHand.pinky", new UserDataConfig(new MapperLinear(0.0, 180.0, 65.0, -10.0, true, false), "x")); - simulator.nodes.put(name + ".rightHand.pinky2", new UserDataConfig(new MapperLinear(0.0, 180.0, 70.0, -10.0, true, false), "x")); - simulator.nodes.put(name + ".rightHand.pinky3", new UserDataConfig(new MapperLinear(0.0, 180.0, 60.0, -10.0, true, false), "x")); - 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 ? - fsm.current = "boot"; - fsm.transitions.add(new Transition("boot", "configStarted", "applyingConfig")); - fsm.transitions.add(new Transition("applyingConfig", "getUserInfo", "getUserInfo")); - fsm.transitions.add(new Transition("applyingConfig", "systemCheck", "systemCheck")); - fsm.transitions.add(new Transition("applyingConfig", "wake", "awake")); - fsm.transitions.add(new Transition("getUserInfo", "systemCheck", "systemCheck")); - 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; - - // setup name references to different services - RandomMessageConfig rm = new RandomMessageConfig(name, "setLeftArmSpeed", 3000, 8000, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0); - random.randomMessages.put(name + ".setLeftArmSpeed", rm); - - rm = new RandomMessageConfig(name, "setRightArmSpeed", 3000, 8000, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0, 8.0, 25.0); - random.randomMessages.put(name + ".setRightArmSpeed", rm); - - rm = new RandomMessageConfig(name, "moveLeftArm", 000, 8000, 0.0, 5.0, 85.0, 95.0, 25.0, 30.0, 10.0, 15.0); - random.randomMessages.put(name + ".moveLeftArm", rm); - - rm = new RandomMessageConfig(name, "moveRightArm", 3000, 8000, 0.0, 5.0, 85.0, 95.0, 25.0, 30.0, 10.0, 15.0); - random.randomMessages.put(name + ".moveRightArm", rm); - - rm = new RandomMessageConfig(name, "setLeftHandSpeed", 3000, 8000, 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.randomMessages.put(name + ".setLeftHandSpeed", rm); - - rm = new RandomMessageConfig(name, "setRightHandSpeed", 3000, 8000, 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.randomMessages.put(name + ".setRightHandSpeed", rm); - - rm = new RandomMessageConfig(name, "moveLeftHand", 3000, 8000, 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.randomMessages.put(name + ".moveLeftHand", rm); - - rm = new RandomMessageConfig(name, "moveRightHand", 3000, 8000, 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.randomMessages.put(name + ".moveRightHand", rm); - - rm = new RandomMessageConfig(name, "setHeadSpeed",3000, 8000, 8.0, 20.0, 8.0, 20.0, 8.0, 20.0); - random.randomMessages.put(name + ".setHeadSpeed", rm); - - rm = new RandomMessageConfig(name, "moveHead", 3000, 8000, 70.0, 110.0, 65.0, 115.0, 70.0, 110.0); - random.randomMessages.put(name + ".moveHead", rm); - - rm = new RandomMessageConfig(name , "setTorsoSpeed", 3000, 8000, 2.0, 5.0, 2.0, 5.0, 2.0, 5.0); - random.randomMessages.put(name + ".setTorsoSpeed", rm); - - rm = new RandomMessageConfig(name, "moveTorso", 3000, 8000, 85.0, 95.0, 88.0, 93.0, 70.0, 110.0); - random.randomMessages.put(name + ".moveTorso", rm); - - // == Peer - headTracking ============================= - TrackingConfig headTracking = (TrackingConfig) plan.get(getPeerName("headTracking")); - - // setup name references to different services - headTracking.getPeer("tilt").name = name + ".head.neck"; - headTracking.getPeer("pan").name = name + ".head.rothead"; - headTracking.getPeer("cv").name = name + ".opencv"; - headTracking.getPeer("pid").name = name + ".pid"; - headTracking.getPeer("controller").name = name + ".left"; - - // == Peer - eyeTracking ============================= - TrackingConfig eyeTracking = (TrackingConfig) plan.get(getPeerName("eyeTracking")); - - // setup name references to different services - eyeTracking.getPeer("tilt").name = name + ".head.eyeY"; - eyeTracking.getPeer("pan").name = name + ".head.eyeX"; - eyeTracking.getPeer("cv").name = name + ".opencv"; - eyeTracking.getPeer("pid").name = name + ".pid"; - eyeTracking.getPeer("controller").name = name + ".left"; - - // == Peer - pid ============================= - PidConfig pid = (PidConfig) plan.get(getPeerName("pid")); - - PidData tiltPid = new PidData(); - tiltPid.ki = -0.001; - tiltPid.kp = -40.0; - tiltPid.inverted = true; - pid.data.put(headTracking.getPeer("tilt").name, tiltPid); - - PidData panPid = new PidData(); - panPid.ki = 0.001; - panPid.kp = 40.0; - pid.data.put(headTracking.getPeer("pan").name, panPid); - - PidData eyeTiltPid = new PidData(); - eyeTiltPid.ki = -0.001; - eyeTiltPid.kp = -30.0; - eyeTiltPid.inverted = true; - pid.data.put(eyeTracking.getPeer("tilt").name, eyeTiltPid); - - PidData eyePanPid = new PidData(); - eyePanPid.ki = 0.001; - eyePanPid.kp = 30.0; - pid.data.put(eyeTracking.getPeer("pan").name, eyePanPid); - - NeoPixelConfig neoPixel = (NeoPixelConfig) plan.get(getPeerName("neoPixel")); - neoPixel.pin = 2; - // neoPixel.controller = String.format("%s.left", name); - neoPixel.controller = String.format("%s.controller3", name); - neoPixel.red = 12; - neoPixel.green = 180; - neoPixel.blue = 212; - neoPixel.pixelCount = 16; - neoPixel.currentAnimation = "Ironman"; - - // remove undesired defaults from our default - plan.remove(name + ".headTracking.tilt"); - plan.remove(name + ".headTracking.pan"); - plan.remove(name + ".headTracking.pid"); - plan.remove(name + ".headTracking.controller"); - plan.remove(name + ".headTracking.controller.serial"); - plan.remove(name + ".headTracking.cv"); - - plan.remove(name + ".eyeTracking.tilt"); - plan.remove(name + ".eyeTracking.pan"); - plan.remove(name + ".eyeTracking.pid"); - 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")); - - // 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/IntegratedMovementConfig.java b/src/main/java/org/myrobotlab/service/config/IntegratedMovementConfig.java deleted file mode 100644 index b4975688ce..0000000000 --- a/src/main/java/org/myrobotlab/service/config/IntegratedMovementConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.myrobotlab.service.config; - -import org.myrobotlab.framework.Plan; - -public class IntegratedMovementConfig extends ServiceConfig { - - @Override - public Plan getDefault(Plan plan, String name) { - super.getDefault(plan, name); - addDefaultPeerConfig(plan, name, "openni", "OpenNi"); - return plan; - } - -} diff --git a/src/main/java/org/myrobotlab/service/config/MqttBrokerConfig.java b/src/main/java/org/myrobotlab/service/config/MqttBrokerConfig.java index 510ab6d6fe..b2b66c5cc1 100644 --- a/src/main/java/org/myrobotlab/service/config/MqttBrokerConfig.java +++ b/src/main/java/org/myrobotlab/service/config/MqttBrokerConfig.java @@ -6,6 +6,6 @@ public class MqttBrokerConfig extends ServiceConfig { public String password; public String address = "0.0.0.0"; public Integer mqttPort = 1883; - public Integer wsPort = 8080; + public Integer wsPort = 8081; } diff --git a/src/main/java/org/myrobotlab/service/config/NeoPixelConfig.java b/src/main/java/org/myrobotlab/service/config/NeoPixelConfig.java index c343d0dddf..f8cb8deac0 100644 --- a/src/main/java/org/myrobotlab/service/config/NeoPixelConfig.java +++ b/src/main/java/org/myrobotlab/service/config/NeoPixelConfig.java @@ -8,11 +8,11 @@ public class NeoPixelConfig extends ServiceConfig { /** - * when attached to an audio file service the animation to be - * played when audio is playing + * when attached to an audio file service the animation to be played when + * audio is playing */ public String audioAnimation = "Ironman"; - + /** * pin number of controller */ @@ -60,8 +60,6 @@ public class NeoPixelConfig extends ServiceConfig { * initial fill */ public boolean fill = false; - - /** * Map of predefined led flashes, defined here in configuration. Another @@ -99,11 +97,9 @@ public static class Flash { * time this flash remains off */ public long timeOff = 500; - - - public Flash() { + + public Flash() { } - public Flash(int red, int green, int blue, long timeOn, long timeOff) { this.red = red; diff --git a/src/main/java/org/myrobotlab/service/config/OpenAIConfig.java b/src/main/java/org/myrobotlab/service/config/OpenAIConfig.java new file mode 100644 index 0000000000..94da640265 --- /dev/null +++ b/src/main/java/org/myrobotlab/service/config/OpenAIConfig.java @@ -0,0 +1,35 @@ +package org.myrobotlab.service.config; + +import org.myrobotlab.framework.Plan; + +public class OpenAIConfig extends ServiceConfig { + + public String currentUserName; + + /** + * current sleep/wake value + */ + public boolean sleeping = false; + + public int maxTokens = 256; + public float temperature = 0.7f; + public String url = "https://api.openai.com/v1/chat/completions"; + public String token = null; + public String engine = "gpt-3.5-turbo"; // "text-davinci-003" + public String wakeWord = "wake"; + public String sleepWord = "sleep"; + /** + * static prefix to send to gpt3 + * e.g. " talk like a pirate when responding, " + */ + public String prefix = null; + + @Override + public Plan getDefault(Plan plan, String name) { + super.getDefault(plan, name); + // http peer to retrieve emojis + addDefaultPeerConfig(plan, name, "http", "HttpClient"); + 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 ce9ae14033..0012b6864b 100644 --- a/src/main/java/org/myrobotlab/service/config/ProgramABConfig.java +++ b/src/main/java/org/myrobotlab/service/config/ProgramABConfig.java @@ -6,14 +6,11 @@ import org.myrobotlab.framework.Plan; public class ProgramABConfig extends ServiceConfig { - - @Deprecated /* unused text filters */ - public String[] textFilters; - + /** - * a directory ProgramAB will scan for new bots + * a directory ProgramAB will scan for new bots on startup */ - public String botDir; + public String botDir = "resource/ProgramAB/"; /** * explicit bot directories @@ -24,13 +21,13 @@ public class ProgramABConfig extends ServiceConfig { * 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"; + public String botType = "Alice"; /** * User name currently interacting with the bot. Setting it here will * default it. */ - public String currentUserName = "human"; + public String username = "human"; /** * sleep current state of the sleep if globalSession is used true : ProgramAB @@ -45,7 +42,19 @@ public class ProgramABConfig extends ServiceConfig { * a new session if available, this means a config/{username}.predicates.txt * will need to exist with a topic field */ - public String startTopic = "unknown"; + public String startTopic = null; + + /** + * bot will prompt users if enabled trolling is true + * after maxConversationDelay has passed + */ + public boolean enableTrolling = false; + + + /** + * Number of milliseconds before the robot starts talking on its own. + */ + public int maxConversationDelay = 5000; @Override public Plan getDefault(Plan plan, String name) { diff --git a/src/main/java/org/myrobotlab/service/config/RandomConfig.java b/src/main/java/org/myrobotlab/service/config/RandomConfig.java index 81a35fd3f1..23b639882d 100644 --- a/src/main/java/org/myrobotlab/service/config/RandomConfig.java +++ b/src/main/java/org/myrobotlab/service/config/RandomConfig.java @@ -63,7 +63,18 @@ public RandomMessageConfig(String service, String method, long minIntervalMs, lo } + /** + * enable or disables all random tasks + */ public boolean enabled = true; + + /** + * map of random data for messaage creation + */ public Map randomMessages = new HashMap<>(); + /** + * rate at which each random data enqueued is sampled and evaluated + */ + public long rate = 100L; } diff --git a/src/main/java/org/myrobotlab/service/config/RasPiConfig.java b/src/main/java/org/myrobotlab/service/config/RasPiConfig.java index f10c2a1fc3..e676dc474b 100644 --- a/src/main/java/org/myrobotlab/service/config/RasPiConfig.java +++ b/src/main/java/org/myrobotlab/service/config/RasPiConfig.java @@ -8,6 +8,8 @@ public class RasPiConfig extends ServiceConfig { */ public int pollRateHz = 1; + public boolean pollOnStart = false; + // TODO - config which starts pins in a mode (read/write) and if write a value 0/1 } diff --git a/src/main/java/org/myrobotlab/service/data/Script2.java b/src/main/java/org/myrobotlab/service/data/Script2.java deleted file mode 100644 index ff5cbf1144..0000000000 --- a/src/main/java/org/myrobotlab/service/data/Script2.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.myrobotlab.service.data; - -import java.io.Serializable; - -public class Script2 implements Serializable { - static final long serialVersionUID = 1L; - /** - * unique location & key of the script e.g. /mrl/scripts/myScript.py - */ - public String file; - /** - * actual code/contents of the script - */ - public String code; - - public Script2(String file, String script) { - this.file = file; - this.code = script; - } - -} diff --git a/src/main/java/org/myrobotlab/service/data/SearchResults.java b/src/main/java/org/myrobotlab/service/data/SearchResults.java index 3981693c71..8a24b94674 100644 --- a/src/main/java/org/myrobotlab/service/data/SearchResults.java +++ b/src/main/java/org/myrobotlab/service/data/SearchResults.java @@ -32,6 +32,23 @@ public String getText() { } return sb.toString(); } + + // FIXME - probably should not do this - but leave it up to the service + // that created it and keep this data object more simple + public String getHtml() { + StringBuilder sb = new StringBuilder(); + for (String t : text) { + sb.append(t); + } + + for (ImageData img : images) { + sb.append(""); + sb.append("\n"); + } + return sb.toString(); + } public String getTextAndImages() { StringBuilder sb = new StringBuilder(); diff --git a/src/main/java/org/myrobotlab/service/data/SensorData.java b/src/main/java/org/myrobotlab/service/data/SensorData.java index 363553b68a..7a922cebaa 100644 --- a/src/main/java/org/myrobotlab/service/data/SensorData.java +++ b/src/main/java/org/myrobotlab/service/data/SensorData.java @@ -2,17 +2,47 @@ import java.io.Serializable; +/** + * A generalized bucket for simplified channels to hold + * any type of sensor information and its source. + * + * @author GroG + * + */ public class SensorData implements Serializable { private static final long serialVersionUID = 1L; - Object data; + + /** + * timestamp + */ + public long ts = System.currentTimeMillis(); + + /** + * Type of string data so downstream receivers can + * interpret data field correctly, typically the + * name of the type of service which generated it. + */ + public String type; + + /** + * Service name where data came from. + */ + public String src; + + /** + * data of sensor + */ + public Object data; - public SensorData(Object data) { - this.data = data; + public SensorData() { } - - public Object getData() { - return data; + + public SensorData(String src, String type, Object data) { + this.src = src; + this.type = type; + this.data = data; } + } diff --git a/src/main/java/org/myrobotlab/service/data/TopicChange.java b/src/main/java/org/myrobotlab/service/data/TopicChange.java deleted file mode 100644 index af25c05f06..0000000000 --- a/src/main/java/org/myrobotlab/service/data/TopicChange.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.myrobotlab.service.data; - -/** - * Purpose of this class is to be a simple data POJO - * for ProgramAB topic changes. This will be useful to - * interface the state machine implemented in AIML where - * topic changes represent changes of state. - * - * @author GroG - * - */ -public class TopicChange { - - /** - * previous topic or state in this state transition - */ - public String oldTopic; - - /** - * new topic or state name in this transition - */ - public String newTopic; - - /** - * the user name in this state change - usually - * current session userName - */ - public String userName; - - /** - * the botName in this state change - typically - * current session botName - */ - public String botName; - - public TopicChange(String userName, String botName, String newTopic, String oldTopic) { - this.userName = userName; - this.botName = botName; - this.newTopic = newTopic; - this.oldTopic = oldTopic; - } - - -} diff --git a/src/main/java/org/myrobotlab/service/interfaces/MotorControl.java b/src/main/java/org/myrobotlab/service/interfaces/MotorControl.java index d965d847a3..ddbae382f7 100644 --- a/src/main/java/org/myrobotlab/service/interfaces/MotorControl.java +++ b/src/main/java/org/myrobotlab/service/interfaces/MotorControl.java @@ -28,7 +28,6 @@ import org.myrobotlab.framework.interfaces.Attachable; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.sensor.EncoderPublisher; -import org.myrobotlab.service.abstracts.AbstractMotor; import org.slf4j.Logger; public interface MotorControl extends RelativePositionControl, AnalogListener, Attachable { @@ -67,7 +66,6 @@ default void attachMotorController(MotorController controller) throws Exception * the need of typed references * * @param controller - * @throws Exception */ default void detachMotorController(MotorController controller){ if (controller == null) { @@ -81,7 +79,6 @@ default void detachMotorController(MotorController controller){ /** * Implementation of detachMotorController * @param controller - * @throws Exception */ void detachMotorController(String controller); diff --git a/src/main/java/org/myrobotlab/service/interfaces/ServoControl.java b/src/main/java/org/myrobotlab/service/interfaces/ServoControl.java index 1a38dcaaf0..1e140ce167 100644 --- a/src/main/java/org/myrobotlab/service/interfaces/ServoControl.java +++ b/src/main/java/org/myrobotlab/service/interfaces/ServoControl.java @@ -25,7 +25,6 @@ package org.myrobotlab.service.interfaces; -import org.myrobotlab.framework.Config; import org.myrobotlab.framework.interfaces.Attachable; import org.myrobotlab.framework.interfaces.StateSaver; import org.myrobotlab.math.interfaces.Mapper; @@ -55,7 +54,6 @@ public interface ServoControl extends AbsolutePositionControl, EncoderListener, /** * enable the PWM pulses/power to the servo */ - @Config void enable(); /** @@ -351,7 +349,7 @@ public interface ServoControl extends AbsolutePositionControl, EncoderListener, /** * synchronizing servos together e.g. leftEye.sync("rightEye") * - * @param servo + * @param name * name that's being synched e.g. master.synch("slave") */ void sync(String name); diff --git a/src/main/java/org/myrobotlab/service/interfaces/StateChangeHandler.java b/src/main/java/org/myrobotlab/service/interfaces/StateChangeHandler.java new file mode 100644 index 0000000000..524201e971 --- /dev/null +++ b/src/main/java/org/myrobotlab/service/interfaces/StateChangeHandler.java @@ -0,0 +1,9 @@ +package org.myrobotlab.service.interfaces; + +import org.myrobotlab.service.FiniteStateMachine.StateChange; + +public interface StateChangeHandler { + + public void handleStateChange(StateChange event); + +} diff --git a/src/main/java/org/myrobotlab/service/interfaces/StateListener.java b/src/main/java/org/myrobotlab/service/interfaces/StateListener.java deleted file mode 100644 index d3573af093..0000000000 --- a/src/main/java/org/myrobotlab/service/interfaces/StateListener.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.myrobotlab.service.interfaces; - -import org.myrobotlab.fsm.api.State; - -public interface StateListener { - - void onState(State state); - -} diff --git a/src/main/java/org/myrobotlab/service/interfaces/StatePublisher.java b/src/main/java/org/myrobotlab/service/interfaces/StatePublisher.java deleted file mode 100644 index c630f18408..0000000000 --- a/src/main/java/org/myrobotlab/service/interfaces/StatePublisher.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.myrobotlab.service.interfaces; - -import org.myrobotlab.fsm.api.State; - -public interface StatePublisher { - - State publishState(State state); - -} diff --git a/src/main/java/org/myrobotlab/service/meta/JoystickMeta.java b/src/main/java/org/myrobotlab/service/meta/JoystickMeta.java index 9a8d6107a8..ca544c8523 100644 --- a/src/main/java/org/myrobotlab/service/meta/JoystickMeta.java +++ b/src/main/java/org/myrobotlab/service/meta/JoystickMeta.java @@ -28,11 +28,6 @@ public JoystickMeta() { log.info("adding jinput native dependencies"); addDependency("jinput-natives", "jinput-natives", "2.0.7", "zip"); } - // addDependency("net.java.jinput", "jinput-platform", "2.0.7"); - // addArtifact("net.java.jinput", "natives-windows"); - // addArtifact("net.java.jinput", "natives-linux"); - // addArtifact("") - } } diff --git a/src/main/java/org/myrobotlab/service/meta/OpenAIMeta.java b/src/main/java/org/myrobotlab/service/meta/OpenAIMeta.java new file mode 100644 index 0000000000..71a72786aa --- /dev/null +++ b/src/main/java/org/myrobotlab/service/meta/OpenAIMeta.java @@ -0,0 +1,32 @@ +package org.myrobotlab.service.meta; + +import org.myrobotlab.logging.LoggerFactory; +import org.myrobotlab.service.meta.abstracts.MetaData; +import org.slf4j.Logger; + +public class OpenAIMeta extends MetaData { + private static final long serialVersionUID = 1L; + public final static Logger log = LoggerFactory.getLogger(OpenAIMeta.class); + + /** + * This class is contains all the meta data details of a service. It's peers, + * dependencies, and all other meta data related to the service. + * + */ + public OpenAIMeta() { + + // add a cool description + addDescription("OpenAI api interface"); + + // add it to one or many categories + addCategory("AI"); + + + + setCloudService(true); + + // setSponsor("GroG"); + + } + +} diff --git a/src/main/java/org/myrobotlab/service/meta/ProgramABMeta.java b/src/main/java/org/myrobotlab/service/meta/ProgramABMeta.java index 7430f362dd..94ce962ed2 100644 --- a/src/main/java/org/myrobotlab/service/meta/ProgramABMeta.java +++ b/src/main/java/org/myrobotlab/service/meta/ProgramABMeta.java @@ -32,7 +32,8 @@ public ProgramABMeta() { // TODO: This is for CJK support in ProgramAB move this into the published // POM for ProgramAB so they are pulled in transiently. addDependency("org.apache.lucene", "lucene-analysis-common", "9.4.2"); - addDependency("org.apache.lucene", "lucene-analysis-kuromoji", "9.4.2"); + addDependency("org.apache.lucene", "lucene-analysis-kuromoji", "9.4.2"); + addDependency("org.openjdk.nashorn", "nashorn-core", "15.4"); addCategory("ai", "control"); } diff --git a/src/main/java/org/myrobotlab/system/InMoovSystemTest.java b/src/main/java/org/myrobotlab/system/InMoovSystemTest.java deleted file mode 100755 index 88db56d16d..0000000000 --- a/src/main/java/org/myrobotlab/system/InMoovSystemTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.myrobotlab.system; - -import java.io.IOException; - -import org.junit.Ignore; -import org.junit.Test; -import org.myrobotlab.logging.LoggingFactory; -import org.myrobotlab.service.Python; -import org.myrobotlab.service.Runtime; - -/** - * InMoov system test. given an inmoov script location, it will load that - * script. This is useful for developers to be able to launch the inmoov scripts - * from inside of an IDE like eclipse or other. - * - */ -@Ignore -public class InMoovSystemTest { - - private String inmoovScript = "./InMoov/InMoov.py"; - - @Test - public void testInMoovBuild() throws IOException { - - // setup some logging.. etc. - LoggingFactory.init("INFO"); - // make sure the inmoov zip is installed locally - // Runtime.install(); - - Python python = (Python) Runtime.createAndStart("python", "Python"); - python.execFile(inmoovScript); - System.err.println("Press any key to exit."); - System.out.flush(); - System.in.read(); - System.err.println("Ahhhh... the any key!"); - System.err.flush(); - } -} diff --git a/src/main/resources/resource/Arduino/MrlComm/Adafruit_NeoPixel.cpp b/src/main/resources/resource/Arduino/MrlComm/Adafruit_NeoPixel.cpp index fe4e89e8a2..f2881a5356 100644 --- a/src/main/resources/resource/Arduino/MrlComm/Adafruit_NeoPixel.cpp +++ b/src/main/resources/resource/Arduino/MrlComm/Adafruit_NeoPixel.cpp @@ -46,7 +46,7 @@ #include "Adafruit_NeoPixel.h" #if defined(TARGET_LPC1768) - #include +#include #endif #if defined(NRF52) || defined(NRF52_SERIES) @@ -59,9 +59,9 @@ #if defined(ARDUINO_ARCH_NRF52840) #if defined __has_include -# if __has_include () -# include -# endif +#if __has_include() +#include +#endif #endif #endif @@ -77,11 +77,21 @@ pixel. @return Adafruit_NeoPixel object. Call the begin() function before use. */ -Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, uint16_t p, neoPixelType t) : - begun(false), brightness(0), pixels(NULL), endTime(0) { +Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, int16_t p, neoPixelType t) + : begun(false), brightness(0), pixels(NULL), endTime(0) { updateType(t); updateLength(n); setPin(p); +#if defined(ARDUINO_ARCH_RP2040) + // Find a free SM on one of the PIO's + sm = pio_claim_unused_sm(pio, false); // don't panic + // Try pio1 if SM not found + if (sm < 0) { + pio = pio1; + sm = pio_claim_unused_sm(pio, true); // panic if no SM is free + } + init = true; +#endif } /*! @@ -94,12 +104,13 @@ Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, uint16_t p, neoPixelType t) : 'new' keyword with the first constructor syntax (length, pin, type). */ -Adafruit_NeoPixel::Adafruit_NeoPixel() : +Adafruit_NeoPixel::Adafruit_NeoPixel() + : #if defined(NEO_KHZ400) - is800KHz(true), + is800KHz(true), #endif - begun(false), numLEDs(0), numBytes(0), pin(-1), brightness(0), pixels(NULL), - rOffset(1), gOffset(0), bOffset(2), wOffset(1), endTime(0) { + begun(false), numLEDs(0), numBytes(0), pin(-1), brightness(0), + pixels(NULL), rOffset(1), gOffset(0), bOffset(2), wOffset(1), endTime(0) { } /*! @@ -107,14 +118,15 @@ Adafruit_NeoPixel::Adafruit_NeoPixel() : */ Adafruit_NeoPixel::~Adafruit_NeoPixel() { free(pixels); - if(pin >= 0) pinMode(pin, INPUT); + if (pin >= 0) + pinMode(pin, INPUT); } /*! @brief Configure NeoPixel pin for output. */ void Adafruit_NeoPixel::begin(void) { - if(pin >= 0) { + if (pin >= 0) { pinMode(pin, OUTPUT); digitalWrite(pin, LOW); } @@ -136,7 +148,7 @@ void Adafruit_NeoPixel::updateLength(uint16_t n) { // Allocate new data -- note: ALL PIXELS ARE CLEARED numBytes = n * ((wOffset == rOffset) ? 3 : 4); - if((pixels = (uint8_t *)malloc(numBytes))) { + if ((pixels = (uint8_t *)malloc(numBytes))) { memset(pixels, 0, numBytes); numLEDs = n; } else { @@ -167,41 +179,71 @@ void Adafruit_NeoPixel::updateType(neoPixelType t) { wOffset = (t >> 6) & 0b11; // See notes in header file rOffset = (t >> 4) & 0b11; // regarding R/G/B/W offsets gOffset = (t >> 2) & 0b11; - bOffset = t & 0b11; + bOffset = t & 0b11; #if defined(NEO_KHZ400) - is800KHz = (t < 256); // 400 KHz flag is 1<<8 + is800KHz = (t < 256); // 400 KHz flag is 1<<8 #endif // If bytes-per-pixel has changed (and pixel data was previously // allocated), re-allocate to new size. Will clear any data. - if(pixels) { + if (pixels) { bool newThreeBytesPerPixel = (wOffset == rOffset); - if(newThreeBytesPerPixel != oldThreeBytesPerPixel) updateLength(numLEDs); + if (newThreeBytesPerPixel != oldThreeBytesPerPixel) + updateLength(numLEDs); } } +// RP2040 specific driver #if defined(ARDUINO_ARCH_RP2040) -extern "C" void rp2040Show( - uint16_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t type); +void Adafruit_NeoPixel::rp2040Init(uint8_t pin, bool is800KHz) +{ + uint offset = pio_add_program(pio, &ws2812_program); + + if (is800KHz) + { + // 800kHz, 8 bit transfers + ws2812_program_init(pio, sm, offset, pin, 800000, 8); + } + else + { + // 400kHz, 8 bit transfers + ws2812_program_init(pio, sm, offset, pin, 400000, 8); + } +} +// Not a user API +void Adafruit_NeoPixel::rp2040Show(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz) +{ + if (this->init) + { + // On first pass through initialise the PIO + rp2040Init(pin, is800KHz); + this->init = false; + } + + while(numBytes--) + // Bits for transmission must be shifted to top 8 bits + pio_sm_put_blocking(pio, sm, ((uint32_t)*pixels++)<< 24); +} + #endif #if defined(ESP8266) // ESP8266 show() is external to enforce ICACHE_RAM_ATTR execution -extern "C" void ICACHE_RAM_ATTR espShow( - uint16_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t type); +extern "C" IRAM_ATTR void espShow(uint16_t pin, uint8_t *pixels, + uint32_t numBytes, uint8_t type); #elif defined(ESP32) -extern "C" void espShow( - uint16_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t type); +extern "C" void espShow(uint16_t pin, uint8_t *pixels, uint32_t numBytes, + uint8_t type); #endif // ESP8266 -#if defined(K210) +#if defined(K210) #define KENDRYTE_K210 1 #endif #if defined(KENDRYTE_K210) -extern "C" void k210Show( - uint8_t pin, uint8_t *pixels, uint32_t numBytes, boolean is800KHz); -#endif //KENDRYTE_K210 +extern "C" void k210Show(uint8_t pin, uint8_t *pixels, uint32_t numBytes, + boolean is800KHz); +#endif // KENDRYTE_K210 /*! @brief Transmit pixel data in RAM to NeoPixels. @note On most architectures, interrupts are temporarily disabled in @@ -215,7 +257,8 @@ extern "C" void k210Show( */ void Adafruit_NeoPixel::show(void) { - if(!pixels) return; + if (!pixels) + return; // Data latch = 300+ microsecond pause in the output stream. Rather than // put a delay at the end of the function, the ending time is noted and @@ -223,36 +266,36 @@ void Adafruit_NeoPixel::show(void) { // subsequent round of data until the latch time has elapsed. This // allows the mainline code to start generating the next frame of data // rather than stalling for the latch. - while(!canShow()); - // endTime is a private member (rather than global var) so that multiple - // instances on different pins can be quickly issued in succession (each - // instance doesn't delay the next). - - // In order to make this code runtime-configurable to work with any pin, - // SBI/CBI instructions are eschewed in favor of full PORT writes via the - // OUT or ST instructions. It relies on two facts: that peripheral - // functions (such as PWM) take precedence on output pins, so our PORT- - // wide writes won't interfere, and that interrupts are globally disabled - // while data is being issued to the LEDs, so no other code will be - // accessing the PORT. The code takes an initial 'snapshot' of the PORT - // state, computes 'pin high' and 'pin low' values, and writes these back - // to the PORT register as needed. - - // NRF52 may use PWM + DMA (if available), may not need to disable interrupt -#if !( defined(NRF52) || defined(NRF52_SERIES) ) + while (!canShow()) + ; + // endTime is a private member (rather than global var) so that multiple + // instances on different pins can be quickly issued in succession (each + // instance doesn't delay the next). + + // In order to make this code runtime-configurable to work with any pin, + // SBI/CBI instructions are eschewed in favor of full PORT writes via the + // OUT or ST instructions. It relies on two facts: that peripheral + // functions (such as PWM) take precedence on output pins, so our PORT- + // wide writes won't interfere, and that interrupts are globally disabled + // while data is being issued to the LEDs, so no other code will be + // accessing the PORT. The code takes an initial 'snapshot' of the PORT + // state, computes 'pin high' and 'pin low' values, and writes these back + // to the PORT register as needed. + + // NRF52 may use PWM + DMA (if available), may not need to disable interrupt + // ESP32 may not disable interrupts because espShow() uses RMT which tries to acquire locks +#if !(defined(NRF52) || defined(NRF52_SERIES) || defined(ESP32)) noInterrupts(); // Need 100% focus on instruction timing #endif #if defined(__AVR__) -// AVR MCUs -- ATmega & ATtiny (no XMEGA) --------------------------------- + // AVR MCUs -- ATmega & ATtiny (no XMEGA) --------------------------------- - volatile uint16_t - i = numBytes; // Loop counter - volatile uint8_t - *ptr = pixels, // Pointer to next byte - b = *ptr++, // Current byte value - hi, // PORT w/output bit set high - lo; // PORT w/output bit set low + volatile uint16_t i = numBytes; // Loop counter + volatile uint8_t *ptr = pixels, // Pointer to next byte + b = *ptr++, // Current byte value + hi, // PORT w/output bit set high + lo; // PORT w/output bit set low // Hand-tuned assembly code issues data to the LED drivers at a specific // rate. There's separate code for different CPU speeds (8, 12, 16 MHz) @@ -272,10 +315,10 @@ void Adafruit_NeoPixel::show(void) { #if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled - if(is800KHz) { + if (is800KHz) { #endif - volatile uint8_t n1, n2 = 0; // First, next bits out + volatile uint8_t n1, n2 = 0; // First, next bits out // Squeezing an 800 KHz stream out of an 8 MHz chip requires code // specific to each PORT register. @@ -286,14 +329,15 @@ void Adafruit_NeoPixel::show(void) { // PORTD OUTPUT ---------------------------------------------------- #if defined(PORTD) - #if defined(PORTB) || defined(PORTC) || defined(PORTF) - if(port == &PORTD) { - #endif // defined(PORTB/C/F) +#if defined(PORTB) || defined(PORTC) || defined(PORTF) + if (port == &PORTD) { +#endif // defined(PORTB/C/F) - hi = PORTD | pinMask; + hi = PORTD | pinMask; lo = PORTD & ~pinMask; n1 = lo; - if(b & 0x80) n1 = hi; + if (b & 0x80) + n1 = hi; // Dirty trick: RJMPs proceeding to the next instruction are used // to delay two clock cycles in one instruction word (rather than @@ -302,360 +346,618 @@ void Adafruit_NeoPixel::show(void) { // relative branch. asm volatile( - "headD:" "\n\t" // Clk Pseudocode - // Bit 7: - "out %[port] , %[hi]" "\n\t" // 1 PORT = hi - "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo - "out %[port] , %[n1]" "\n\t" // 1 PORT = n1 - "rjmp .+0" "\n\t" // 2 nop nop - "sbrc %[byte] , 6" "\n\t" // 1-2 if(b & 0x40) - "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi - "out %[port] , %[lo]" "\n\t" // 1 PORT = lo - "rjmp .+0" "\n\t" // 2 nop nop - // Bit 6: - "out %[port] , %[hi]" "\n\t" // 1 PORT = hi - "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo - "out %[port] , %[n2]" "\n\t" // 1 PORT = n2 - "rjmp .+0" "\n\t" // 2 nop nop - "sbrc %[byte] , 5" "\n\t" // 1-2 if(b & 0x20) - "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi - "out %[port] , %[lo]" "\n\t" // 1 PORT = lo - "rjmp .+0" "\n\t" // 2 nop nop - // Bit 5: - "out %[port] , %[hi]" "\n\t" // 1 PORT = hi - "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo - "out %[port] , %[n1]" "\n\t" // 1 PORT = n1 - "rjmp .+0" "\n\t" // 2 nop nop - "sbrc %[byte] , 4" "\n\t" // 1-2 if(b & 0x10) - "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi - "out %[port] , %[lo]" "\n\t" // 1 PORT = lo - "rjmp .+0" "\n\t" // 2 nop nop - // Bit 4: - "out %[port] , %[hi]" "\n\t" // 1 PORT = hi - "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo - "out %[port] , %[n2]" "\n\t" // 1 PORT = n2 - "rjmp .+0" "\n\t" // 2 nop nop - "sbrc %[byte] , 3" "\n\t" // 1-2 if(b & 0x08) - "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi - "out %[port] , %[lo]" "\n\t" // 1 PORT = lo - "rjmp .+0" "\n\t" // 2 nop nop - // Bit 3: - "out %[port] , %[hi]" "\n\t" // 1 PORT = hi - "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo - "out %[port] , %[n1]" "\n\t" // 1 PORT = n1 - "rjmp .+0" "\n\t" // 2 nop nop - "sbrc %[byte] , 2" "\n\t" // 1-2 if(b & 0x04) - "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi - "out %[port] , %[lo]" "\n\t" // 1 PORT = lo - "rjmp .+0" "\n\t" // 2 nop nop - // Bit 2: - "out %[port] , %[hi]" "\n\t" // 1 PORT = hi - "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo - "out %[port] , %[n2]" "\n\t" // 1 PORT = n2 - "rjmp .+0" "\n\t" // 2 nop nop - "sbrc %[byte] , 1" "\n\t" // 1-2 if(b & 0x02) - "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi - "out %[port] , %[lo]" "\n\t" // 1 PORT = lo - "rjmp .+0" "\n\t" // 2 nop nop - // Bit 1: - "out %[port] , %[hi]" "\n\t" // 1 PORT = hi - "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo - "out %[port] , %[n1]" "\n\t" // 1 PORT = n1 - "rjmp .+0" "\n\t" // 2 nop nop - "sbrc %[byte] , 0" "\n\t" // 1-2 if(b & 0x01) - "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi - "out %[port] , %[lo]" "\n\t" // 1 PORT = lo - "sbiw %[count], 1" "\n\t" // 2 i-- (don't act on Z flag yet) - // Bit 0: - "out %[port] , %[hi]" "\n\t" // 1 PORT = hi - "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo - "out %[port] , %[n2]" "\n\t" // 1 PORT = n2 - "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ - "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80) - "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi - "out %[port] , %[lo]" "\n\t" // 1 PORT = lo - "brne headD" "\n" // 2 while(i) (Z flag set above) - : [byte] "+r" (b), - [n1] "+r" (n1), - [n2] "+r" (n2), - [count] "+w" (i) - : [port] "I" (_SFR_IO_ADDR(PORTD)), - [ptr] "e" (ptr), - [hi] "r" (hi), - [lo] "r" (lo)); - - #if defined(PORTB) || defined(PORTC) || defined(PORTF) + "headD:" + "\n\t" // Clk Pseudocode + // Bit 7: + "out %[port] , %[hi]" + "\n\t" // 1 PORT = hi + "mov %[n2] , %[lo]" + "\n\t" // 1 n2 = lo + "out %[port] , %[n1]" + "\n\t" // 1 PORT = n1 + "rjmp .+0" + "\n\t" // 2 nop nop + "sbrc %[byte] , 6" + "\n\t" // 1-2 if(b & 0x40) + "mov %[n2] , %[hi]" + "\n\t" // 0-1 n2 = hi + "out %[port] , %[lo]" + "\n\t" // 1 PORT = lo + "rjmp .+0" + "\n\t" // 2 nop nop + // Bit 6: + "out %[port] , %[hi]" + "\n\t" // 1 PORT = hi + "mov %[n1] , %[lo]" + "\n\t" // 1 n1 = lo + "out %[port] , %[n2]" + "\n\t" // 1 PORT = n2 + "rjmp .+0" + "\n\t" // 2 nop nop + "sbrc %[byte] , 5" + "\n\t" // 1-2 if(b & 0x20) + "mov %[n1] , %[hi]" + "\n\t" // 0-1 n1 = hi + "out %[port] , %[lo]" + "\n\t" // 1 PORT = lo + "rjmp .+0" + "\n\t" // 2 nop nop + // Bit 5: + "out %[port] , %[hi]" + "\n\t" // 1 PORT = hi + "mov %[n2] , %[lo]" + "\n\t" // 1 n2 = lo + "out %[port] , %[n1]" + "\n\t" // 1 PORT = n1 + "rjmp .+0" + "\n\t" // 2 nop nop + "sbrc %[byte] , 4" + "\n\t" // 1-2 if(b & 0x10) + "mov %[n2] , %[hi]" + "\n\t" // 0-1 n2 = hi + "out %[port] , %[lo]" + "\n\t" // 1 PORT = lo + "rjmp .+0" + "\n\t" // 2 nop nop + // Bit 4: + "out %[port] , %[hi]" + "\n\t" // 1 PORT = hi + "mov %[n1] , %[lo]" + "\n\t" // 1 n1 = lo + "out %[port] , %[n2]" + "\n\t" // 1 PORT = n2 + "rjmp .+0" + "\n\t" // 2 nop nop + "sbrc %[byte] , 3" + "\n\t" // 1-2 if(b & 0x08) + "mov %[n1] , %[hi]" + "\n\t" // 0-1 n1 = hi + "out %[port] , %[lo]" + "\n\t" // 1 PORT = lo + "rjmp .+0" + "\n\t" // 2 nop nop + // Bit 3: + "out %[port] , %[hi]" + "\n\t" // 1 PORT = hi + "mov %[n2] , %[lo]" + "\n\t" // 1 n2 = lo + "out %[port] , %[n1]" + "\n\t" // 1 PORT = n1 + "rjmp .+0" + "\n\t" // 2 nop nop + "sbrc %[byte] , 2" + "\n\t" // 1-2 if(b & 0x04) + "mov %[n2] , %[hi]" + "\n\t" // 0-1 n2 = hi + "out %[port] , %[lo]" + "\n\t" // 1 PORT = lo + "rjmp .+0" + "\n\t" // 2 nop nop + // Bit 2: + "out %[port] , %[hi]" + "\n\t" // 1 PORT = hi + "mov %[n1] , %[lo]" + "\n\t" // 1 n1 = lo + "out %[port] , %[n2]" + "\n\t" // 1 PORT = n2 + "rjmp .+0" + "\n\t" // 2 nop nop + "sbrc %[byte] , 1" + "\n\t" // 1-2 if(b & 0x02) + "mov %[n1] , %[hi]" + "\n\t" // 0-1 n1 = hi + "out %[port] , %[lo]" + "\n\t" // 1 PORT = lo + "rjmp .+0" + "\n\t" // 2 nop nop + // Bit 1: + "out %[port] , %[hi]" + "\n\t" // 1 PORT = hi + "mov %[n2] , %[lo]" + "\n\t" // 1 n2 = lo + "out %[port] , %[n1]" + "\n\t" // 1 PORT = n1 + "rjmp .+0" + "\n\t" // 2 nop nop + "sbrc %[byte] , 0" + "\n\t" // 1-2 if(b & 0x01) + "mov %[n2] , %[hi]" + "\n\t" // 0-1 n2 = hi + "out %[port] , %[lo]" + "\n\t" // 1 PORT = lo + "sbiw %[count], 1" + "\n\t" // 2 i-- (don't act on Z flag yet) + // Bit 0: + "out %[port] , %[hi]" + "\n\t" // 1 PORT = hi + "mov %[n1] , %[lo]" + "\n\t" // 1 n1 = lo + "out %[port] , %[n2]" + "\n\t" // 1 PORT = n2 + "ld %[byte] , %a[ptr]+" + "\n\t" // 2 b = *ptr++ + "sbrc %[byte] , 7" + "\n\t" // 1-2 if(b & 0x80) + "mov %[n1] , %[hi]" + "\n\t" // 0-1 n1 = hi + "out %[port] , %[lo]" + "\n\t" // 1 PORT = lo + "brne headD" + "\n" // 2 while(i) (Z flag set above) + : [byte] "+r"(b), [n1] "+r"(n1), [n2] "+r"(n2), [count] "+w"(i) + : [port] "I"(_SFR_IO_ADDR(PORTD)), [ptr] "e"(ptr), [hi] "r"(hi), + [lo] "r"(lo)); + +#if defined(PORTB) || defined(PORTC) || defined(PORTF) } else // other PORT(s) - #endif // defined(PORTB/C/F) +#endif // defined(PORTB/C/F) #endif // defined(PORTD) // PORTB OUTPUT ---------------------------------------------------- #if defined(PORTB) - #if defined(PORTD) || defined(PORTC) || defined(PORTF) - if(port == &PORTB) { - #endif // defined(PORTD/C/F) +#if defined(PORTD) || defined(PORTC) || defined(PORTF) + if (port == &PORTB) { +#endif // defined(PORTD/C/F) // Same as above, just switched to PORTB and stripped of comments. - hi = PORTB | pinMask; + hi = PORTB | pinMask; lo = PORTB & ~pinMask; n1 = lo; - if(b & 0x80) n1 = hi; + if (b & 0x80) + n1 = hi; asm volatile( - "headB:" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n2] , %[lo]" "\n\t" - "out %[port] , %[n1]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 6" "\n\t" - "mov %[n2] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n1] , %[lo]" "\n\t" - "out %[port] , %[n2]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 5" "\n\t" - "mov %[n1] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n2] , %[lo]" "\n\t" - "out %[port] , %[n1]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 4" "\n\t" - "mov %[n2] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n1] , %[lo]" "\n\t" - "out %[port] , %[n2]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 3" "\n\t" - "mov %[n1] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n2] , %[lo]" "\n\t" - "out %[port] , %[n1]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 2" "\n\t" - "mov %[n2] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n1] , %[lo]" "\n\t" - "out %[port] , %[n2]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 1" "\n\t" - "mov %[n1] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n2] , %[lo]" "\n\t" - "out %[port] , %[n1]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 0" "\n\t" - "mov %[n2] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "sbiw %[count], 1" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n1] , %[lo]" "\n\t" - "out %[port] , %[n2]" "\n\t" - "ld %[byte] , %a[ptr]+" "\n\t" - "sbrc %[byte] , 7" "\n\t" - "mov %[n1] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "brne headB" "\n" - : [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i) - : [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi), - [lo] "r" (lo)); - - #if defined(PORTD) || defined(PORTC) || defined(PORTF) + "headB:" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n2] , %[lo]" + "\n\t" + "out %[port] , %[n1]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 6" + "\n\t" + "mov %[n2] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n1] , %[lo]" + "\n\t" + "out %[port] , %[n2]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 5" + "\n\t" + "mov %[n1] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n2] , %[lo]" + "\n\t" + "out %[port] , %[n1]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 4" + "\n\t" + "mov %[n2] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n1] , %[lo]" + "\n\t" + "out %[port] , %[n2]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 3" + "\n\t" + "mov %[n1] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n2] , %[lo]" + "\n\t" + "out %[port] , %[n1]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 2" + "\n\t" + "mov %[n2] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n1] , %[lo]" + "\n\t" + "out %[port] , %[n2]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 1" + "\n\t" + "mov %[n1] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n2] , %[lo]" + "\n\t" + "out %[port] , %[n1]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 0" + "\n\t" + "mov %[n2] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "sbiw %[count], 1" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n1] , %[lo]" + "\n\t" + "out %[port] , %[n2]" + "\n\t" + "ld %[byte] , %a[ptr]+" + "\n\t" + "sbrc %[byte] , 7" + "\n\t" + "mov %[n1] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "brne headB" + "\n" + : [byte] "+r"(b), [n1] "+r"(n1), [n2] "+r"(n2), [count] "+w"(i) + : [port] "I"(_SFR_IO_ADDR(PORTB)), [ptr] "e"(ptr), [hi] "r"(hi), + [lo] "r"(lo)); + +#if defined(PORTD) || defined(PORTC) || defined(PORTF) } - #endif - #if defined(PORTC) || defined(PORTF) +#endif +#if defined(PORTC) || defined(PORTF) else - #endif // defined(PORTC/F) +#endif // defined(PORTC/F) #endif // defined(PORTB) // PORTC OUTPUT ---------------------------------------------------- #if defined(PORTC) - #if defined(PORTD) || defined(PORTB) || defined(PORTF) - if(port == &PORTC) { - #endif // defined(PORTD/B/F) +#if defined(PORTD) || defined(PORTB) || defined(PORTF) + if (port == &PORTC) { +#endif // defined(PORTD/B/F) // Same as above, just switched to PORTC and stripped of comments. - hi = PORTC | pinMask; + hi = PORTC | pinMask; lo = PORTC & ~pinMask; n1 = lo; - if(b & 0x80) n1 = hi; + if (b & 0x80) + n1 = hi; asm volatile( - "headC:" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n2] , %[lo]" "\n\t" - "out %[port] , %[n1]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 6" "\n\t" - "mov %[n2] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n1] , %[lo]" "\n\t" - "out %[port] , %[n2]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 5" "\n\t" - "mov %[n1] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n2] , %[lo]" "\n\t" - "out %[port] , %[n1]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 4" "\n\t" - "mov %[n2] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n1] , %[lo]" "\n\t" - "out %[port] , %[n2]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 3" "\n\t" - "mov %[n1] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n2] , %[lo]" "\n\t" - "out %[port] , %[n1]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 2" "\n\t" - "mov %[n2] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n1] , %[lo]" "\n\t" - "out %[port] , %[n2]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 1" "\n\t" - "mov %[n1] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n2] , %[lo]" "\n\t" - "out %[port] , %[n1]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 0" "\n\t" - "mov %[n2] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "sbiw %[count], 1" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n1] , %[lo]" "\n\t" - "out %[port] , %[n2]" "\n\t" - "ld %[byte] , %a[ptr]+" "\n\t" - "sbrc %[byte] , 7" "\n\t" - "mov %[n1] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "brne headC" "\n" - : [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i) - : [port] "I" (_SFR_IO_ADDR(PORTC)), [ptr] "e" (ptr), [hi] "r" (hi), - [lo] "r" (lo)); - - #if defined(PORTD) || defined(PORTB) || defined(PORTF) + "headC:" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n2] , %[lo]" + "\n\t" + "out %[port] , %[n1]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 6" + "\n\t" + "mov %[n2] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n1] , %[lo]" + "\n\t" + "out %[port] , %[n2]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 5" + "\n\t" + "mov %[n1] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n2] , %[lo]" + "\n\t" + "out %[port] , %[n1]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 4" + "\n\t" + "mov %[n2] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n1] , %[lo]" + "\n\t" + "out %[port] , %[n2]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 3" + "\n\t" + "mov %[n1] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n2] , %[lo]" + "\n\t" + "out %[port] , %[n1]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 2" + "\n\t" + "mov %[n2] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n1] , %[lo]" + "\n\t" + "out %[port] , %[n2]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 1" + "\n\t" + "mov %[n1] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n2] , %[lo]" + "\n\t" + "out %[port] , %[n1]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 0" + "\n\t" + "mov %[n2] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "sbiw %[count], 1" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n1] , %[lo]" + "\n\t" + "out %[port] , %[n2]" + "\n\t" + "ld %[byte] , %a[ptr]+" + "\n\t" + "sbrc %[byte] , 7" + "\n\t" + "mov %[n1] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "brne headC" + "\n" + : [byte] "+r"(b), [n1] "+r"(n1), [n2] "+r"(n2), [count] "+w"(i) + : [port] "I"(_SFR_IO_ADDR(PORTC)), [ptr] "e"(ptr), [hi] "r"(hi), + [lo] "r"(lo)); + +#if defined(PORTD) || defined(PORTB) || defined(PORTF) } - #endif // defined(PORTD/B/F) - #if defined(PORTF) +#endif // defined(PORTD/B/F) +#if defined(PORTF) else - #endif +#endif #endif // defined(PORTC) // PORTF OUTPUT ---------------------------------------------------- #if defined(PORTF) - #if defined(PORTD) || defined(PORTB) || defined(PORTC) - if(port == &PORTF) { - #endif // defined(PORTD/B/C) +#if defined(PORTD) || defined(PORTB) || defined(PORTC) + if (port == &PORTF) { +#endif // defined(PORTD/B/C) - hi = PORTF | pinMask; + hi = PORTF | pinMask; lo = PORTF & ~pinMask; n1 = lo; - if(b & 0x80) n1 = hi; + if (b & 0x80) + n1 = hi; asm volatile( - "headF:" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n2] , %[lo]" "\n\t" - "out %[port] , %[n1]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 6" "\n\t" - "mov %[n2] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n1] , %[lo]" "\n\t" - "out %[port] , %[n2]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 5" "\n\t" - "mov %[n1] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n2] , %[lo]" "\n\t" - "out %[port] , %[n1]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 4" "\n\t" - "mov %[n2] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n1] , %[lo]" "\n\t" - "out %[port] , %[n2]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 3" "\n\t" - "mov %[n1] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n2] , %[lo]" "\n\t" - "out %[port] , %[n1]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 2" "\n\t" - "mov %[n2] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n1] , %[lo]" "\n\t" - "out %[port] , %[n2]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 1" "\n\t" - "mov %[n1] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "rjmp .+0" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n2] , %[lo]" "\n\t" - "out %[port] , %[n1]" "\n\t" - "rjmp .+0" "\n\t" - "sbrc %[byte] , 0" "\n\t" - "mov %[n2] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "sbiw %[count], 1" "\n\t" - "out %[port] , %[hi]" "\n\t" - "mov %[n1] , %[lo]" "\n\t" - "out %[port] , %[n2]" "\n\t" - "ld %[byte] , %a[ptr]+" "\n\t" - "sbrc %[byte] , 7" "\n\t" - "mov %[n1] , %[hi]" "\n\t" - "out %[port] , %[lo]" "\n\t" - "brne headF" "\n" - : [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i) - : [port] "I" (_SFR_IO_ADDR(PORTF)), [ptr] "e" (ptr), [hi] "r" (hi), - [lo] "r" (lo)); - - #if defined(PORTD) || defined(PORTB) || defined(PORTC) + "headF:" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n2] , %[lo]" + "\n\t" + "out %[port] , %[n1]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 6" + "\n\t" + "mov %[n2] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n1] , %[lo]" + "\n\t" + "out %[port] , %[n2]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 5" + "\n\t" + "mov %[n1] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n2] , %[lo]" + "\n\t" + "out %[port] , %[n1]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 4" + "\n\t" + "mov %[n2] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n1] , %[lo]" + "\n\t" + "out %[port] , %[n2]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 3" + "\n\t" + "mov %[n1] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n2] , %[lo]" + "\n\t" + "out %[port] , %[n1]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 2" + "\n\t" + "mov %[n2] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n1] , %[lo]" + "\n\t" + "out %[port] , %[n2]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 1" + "\n\t" + "mov %[n1] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "rjmp .+0" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n2] , %[lo]" + "\n\t" + "out %[port] , %[n1]" + "\n\t" + "rjmp .+0" + "\n\t" + "sbrc %[byte] , 0" + "\n\t" + "mov %[n2] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "sbiw %[count], 1" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "mov %[n1] , %[lo]" + "\n\t" + "out %[port] , %[n2]" + "\n\t" + "ld %[byte] , %a[ptr]+" + "\n\t" + "sbrc %[byte] , 7" + "\n\t" + "mov %[n1] , %[hi]" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "brne headF" + "\n" + : [byte] "+r"(b), [n1] "+r"(n1), [n2] "+r"(n2), [count] "+w"(i) + : [port] "I"(_SFR_IO_ADDR(PORTF)), [ptr] "e"(ptr), [hi] "r"(hi), + [lo] "r"(lo)); + +#if defined(PORTD) || defined(PORTB) || defined(PORTC) } - #endif // defined(PORTD/B/C) +#endif // defined(PORTD/B/C) #endif // defined(PORTF) #if defined(NEO_KHZ400) @@ -673,41 +975,56 @@ void Adafruit_NeoPixel::show(void) { volatile uint8_t next, bit; - hi = *port | pinMask; - lo = *port & ~pinMask; + hi = *port | pinMask; + lo = *port & ~pinMask; next = lo; - bit = 8; - - asm volatile( - "head20:" "\n\t" // Clk Pseudocode (T = 0) - "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) - "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128) - "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4) - "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 6) - "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7) - "dec %[bit]" "\n\t" // 1 bit-- (T = 8) - "breq nextbyte20" "\n\t" // 1-2 if(bit == 0) - "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10) - "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12) - "rjmp .+0" "\n\t" // 2 nop nop (T = 14) - "rjmp .+0" "\n\t" // 2 nop nop (T = 16) - "rjmp .+0" "\n\t" // 2 nop nop (T = 18) - "rjmp head20" "\n\t" // 2 -> head20 (next bit out) - "nextbyte20:" "\n\t" // (T = 10) - "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12) - "nop" "\n\t" // 1 nop (T = 13) - "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 14) - "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 16) - "sbiw %[count], 1" "\n\t" // 2 i-- (T = 18) - "brne head20" "\n" // 2 if(i != 0) -> (next byte) - : [port] "+e" (port), - [byte] "+r" (b), - [bit] "+r" (bit), - [next] "+r" (next), - [count] "+w" (i) - : [hi] "r" (hi), - [lo] "r" (lo), - [ptr] "e" (ptr)); + bit = 8; + + asm volatile("head20:" + "\n\t" // Clk Pseudocode (T = 0) + "st %a[port], %[hi]" + "\n\t" // 2 PORT = hi (T = 2) + "sbrc %[byte] , 7" + "\n\t" // 1-2 if(b & 128) + "mov %[next], %[hi]" + "\n\t" // 0-1 next = hi (T = 4) + "st %a[port], %[next]" + "\n\t" // 2 PORT = next (T = 6) + "mov %[next] , %[lo]" + "\n\t" // 1 next = lo (T = 7) + "dec %[bit]" + "\n\t" // 1 bit-- (T = 8) + "breq nextbyte20" + "\n\t" // 1-2 if(bit == 0) + "rol %[byte]" + "\n\t" // 1 b <<= 1 (T = 10) + "st %a[port], %[lo]" + "\n\t" // 2 PORT = lo (T = 12) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 14) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 16) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 18) + "rjmp head20" + "\n\t" // 2 -> head20 (next bit out) + "nextbyte20:" + "\n\t" // (T = 10) + "st %a[port], %[lo]" + "\n\t" // 2 PORT = lo (T = 12) + "nop" + "\n\t" // 1 nop (T = 13) + "ldi %[bit] , 8" + "\n\t" // 1 bit = 8 (T = 14) + "ld %[byte] , %a[ptr]+" + "\n\t" // 2 b = *ptr++ (T = 16) + "sbiw %[count], 1" + "\n\t" // 2 i-- (T = 18) + "brne head20" + "\n" // 2 if(i != 0) -> (next byte) + : [port] "+e"(port), [byte] "+r"(b), [bit] "+r"(bit), + [next] "+r"(next), [count] "+w"(i) + : [hi] "r"(hi), [lo] "r"(lo), [ptr] "e"(ptr)); } #endif // NEO_KHZ400 @@ -715,7 +1032,7 @@ void Adafruit_NeoPixel::show(void) { #elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled - if(is800KHz) { + if (is800KHz) { #endif // In the 12 MHz case, an optimized 800 KHz datastream (no dead time @@ -730,253 +1047,397 @@ void Adafruit_NeoPixel::show(void) { // PORTD OUTPUT ---------------------------------------------------- #if defined(PORTD) - #if defined(PORTB) || defined(PORTC) || defined(PORTF) - if(port == &PORTD) { - #endif // defined(PORTB/C/F) +#if defined(PORTB) || defined(PORTC) || defined(PORTF) + if (port == &PORTD) { +#endif // defined(PORTB/C/F) - hi = PORTD | pinMask; - lo = PORTD & ~pinMask; + hi = PORTD | pinMask; + lo = PORTD & ~pinMask; next = lo; - if(b & 0x80) next = hi; + if (b & 0x80) + next = hi; // Don't "optimize" the OUT calls into the bitTime subroutine; // we're exploiting the RCALL and RET as 3- and 4-cycle NOPs! - asm volatile( - "headD:" "\n\t" // (T = 0) - "out %[port], %[hi]" "\n\t" // (T = 1) - "rcall bitTimeD" "\n\t" // Bit 7 (T = 15) - "out %[port], %[hi]" "\n\t" - "rcall bitTimeD" "\n\t" // Bit 6 - "out %[port], %[hi]" "\n\t" - "rcall bitTimeD" "\n\t" // Bit 5 - "out %[port], %[hi]" "\n\t" - "rcall bitTimeD" "\n\t" // Bit 4 - "out %[port], %[hi]" "\n\t" - "rcall bitTimeD" "\n\t" // Bit 3 - "out %[port], %[hi]" "\n\t" - "rcall bitTimeD" "\n\t" // Bit 2 - "out %[port], %[hi]" "\n\t" - "rcall bitTimeD" "\n\t" // Bit 1 - // Bit 0: - "out %[port] , %[hi]" "\n\t" // 1 PORT = hi (T = 1) - "rjmp .+0" "\n\t" // 2 nop nop (T = 3) - "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 5) - "out %[port] , %[next]" "\n\t" // 1 PORT = next (T = 6) - "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7) - "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80) (T = 8) - "mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 9) - "nop" "\n\t" // 1 (T = 10) - "out %[port] , %[lo]" "\n\t" // 1 PORT = lo (T = 11) - "sbiw %[count], 1" "\n\t" // 2 i-- (T = 13) - "brne headD" "\n\t" // 2 if(i != 0) -> (next byte) - "rjmp doneD" "\n\t" - "bitTimeD:" "\n\t" // nop nop nop (T = 4) - "out %[port], %[next]" "\n\t" // 1 PORT = next (T = 5) - "mov %[next], %[lo]" "\n\t" // 1 next = lo (T = 6) - "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 7) - "sbrc %[byte], 7" "\n\t" // 1-2 if(b & 0x80) (T = 8) - "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 9) - "nop" "\n\t" // 1 (T = 10) - "out %[port], %[lo]" "\n\t" // 1 PORT = lo (T = 11) - "ret" "\n\t" // 4 nop nop nop nop (T = 15) - "doneD:" "\n" - : [byte] "+r" (b), - [next] "+r" (next), - [count] "+w" (i) - : [port] "I" (_SFR_IO_ADDR(PORTD)), - [ptr] "e" (ptr), - [hi] "r" (hi), - [lo] "r" (lo)); - - #if defined(PORTB) || defined(PORTC) || defined(PORTF) + asm volatile("headD:" + "\n\t" // (T = 0) + "out %[port], %[hi]" + "\n\t" // (T = 1) + "rcall bitTimeD" + "\n\t" // Bit 7 (T = 15) + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeD" + "\n\t" // Bit 6 + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeD" + "\n\t" // Bit 5 + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeD" + "\n\t" // Bit 4 + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeD" + "\n\t" // Bit 3 + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeD" + "\n\t" // Bit 2 + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeD" + "\n\t" // Bit 1 + // Bit 0: + "out %[port] , %[hi]" + "\n\t" // 1 PORT = hi (T = 1) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 3) + "ld %[byte] , %a[ptr]+" + "\n\t" // 2 b = *ptr++ (T = 5) + "out %[port] , %[next]" + "\n\t" // 1 PORT = next (T = 6) + "mov %[next] , %[lo]" + "\n\t" // 1 next = lo (T = 7) + "sbrc %[byte] , 7" + "\n\t" // 1-2 if(b & 0x80) (T = 8) + "mov %[next] , %[hi]" + "\n\t" // 0-1 next = hi (T = 9) + "nop" + "\n\t" // 1 (T = 10) + "out %[port] , %[lo]" + "\n\t" // 1 PORT = lo (T = 11) + "sbiw %[count], 1" + "\n\t" // 2 i-- (T = 13) + "brne headD" + "\n\t" // 2 if(i != 0) -> (next byte) + "rjmp doneD" + "\n\t" + "bitTimeD:" + "\n\t" // nop nop nop (T = 4) + "out %[port], %[next]" + "\n\t" // 1 PORT = next (T = 5) + "mov %[next], %[lo]" + "\n\t" // 1 next = lo (T = 6) + "rol %[byte]" + "\n\t" // 1 b <<= 1 (T = 7) + "sbrc %[byte], 7" + "\n\t" // 1-2 if(b & 0x80) (T = 8) + "mov %[next], %[hi]" + "\n\t" // 0-1 next = hi (T = 9) + "nop" + "\n\t" // 1 (T = 10) + "out %[port], %[lo]" + "\n\t" // 1 PORT = lo (T = 11) + "ret" + "\n\t" // 4 nop nop nop nop (T = 15) + "doneD:" + "\n" + : [byte] "+r"(b), [next] "+r"(next), [count] "+w"(i) + : [port] "I"(_SFR_IO_ADDR(PORTD)), [ptr] "e"(ptr), + [hi] "r"(hi), [lo] "r"(lo)); + +#if defined(PORTB) || defined(PORTC) || defined(PORTF) } else // other PORT(s) - #endif // defined(PORTB/C/F) +#endif // defined(PORTB/C/F) #endif // defined(PORTD) // PORTB OUTPUT ---------------------------------------------------- #if defined(PORTB) - #if defined(PORTD) || defined(PORTC) || defined(PORTF) - if(port == &PORTB) { - #endif // defined(PORTD/C/F) +#if defined(PORTD) || defined(PORTC) || defined(PORTF) + if (port == &PORTB) { +#endif // defined(PORTD/C/F) - hi = PORTB | pinMask; - lo = PORTB & ~pinMask; + hi = PORTB | pinMask; + lo = PORTB & ~pinMask; next = lo; - if(b & 0x80) next = hi; + if (b & 0x80) + next = hi; // Same as above, just set for PORTB & stripped of comments - asm volatile( - "headB:" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeB" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeB" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeB" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeB" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeB" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeB" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeB" "\n\t" - "out %[port] , %[hi]" "\n\t" - "rjmp .+0" "\n\t" - "ld %[byte] , %a[ptr]+" "\n\t" - "out %[port] , %[next]" "\n\t" - "mov %[next] , %[lo]" "\n\t" - "sbrc %[byte] , 7" "\n\t" - "mov %[next] , %[hi]" "\n\t" - "nop" "\n\t" - "out %[port] , %[lo]" "\n\t" - "sbiw %[count], 1" "\n\t" - "brne headB" "\n\t" - "rjmp doneB" "\n\t" - "bitTimeB:" "\n\t" - "out %[port], %[next]" "\n\t" - "mov %[next], %[lo]" "\n\t" - "rol %[byte]" "\n\t" - "sbrc %[byte], 7" "\n\t" - "mov %[next], %[hi]" "\n\t" - "nop" "\n\t" - "out %[port], %[lo]" "\n\t" - "ret" "\n\t" - "doneB:" "\n" - : [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i) - : [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi), - [lo] "r" (lo)); - - #if defined(PORTD) || defined(PORTC) || defined(PORTF) + asm volatile("headB:" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeB" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeB" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeB" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeB" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeB" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeB" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeB" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "rjmp .+0" + "\n\t" + "ld %[byte] , %a[ptr]+" + "\n\t" + "out %[port] , %[next]" + "\n\t" + "mov %[next] , %[lo]" + "\n\t" + "sbrc %[byte] , 7" + "\n\t" + "mov %[next] , %[hi]" + "\n\t" + "nop" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "sbiw %[count], 1" + "\n\t" + "brne headB" + "\n\t" + "rjmp doneB" + "\n\t" + "bitTimeB:" + "\n\t" + "out %[port], %[next]" + "\n\t" + "mov %[next], %[lo]" + "\n\t" + "rol %[byte]" + "\n\t" + "sbrc %[byte], 7" + "\n\t" + "mov %[next], %[hi]" + "\n\t" + "nop" + "\n\t" + "out %[port], %[lo]" + "\n\t" + "ret" + "\n\t" + "doneB:" + "\n" + : [byte] "+r"(b), [next] "+r"(next), [count] "+w"(i) + : [port] "I"(_SFR_IO_ADDR(PORTB)), [ptr] "e"(ptr), + [hi] "r"(hi), [lo] "r"(lo)); + +#if defined(PORTD) || defined(PORTC) || defined(PORTF) } - #endif - #if defined(PORTC) || defined(PORTF) +#endif +#if defined(PORTC) || defined(PORTF) else - #endif // defined(PORTC/F) +#endif // defined(PORTC/F) #endif // defined(PORTB) // PORTC OUTPUT ---------------------------------------------------- #if defined(PORTC) - #if defined(PORTD) || defined(PORTB) || defined(PORTF) - if(port == &PORTC) { - #endif // defined(PORTD/B/F) +#if defined(PORTD) || defined(PORTB) || defined(PORTF) + if (port == &PORTC) { +#endif // defined(PORTD/B/F) - hi = PORTC | pinMask; - lo = PORTC & ~pinMask; + hi = PORTC | pinMask; + lo = PORTC & ~pinMask; next = lo; - if(b & 0x80) next = hi; + if (b & 0x80) + next = hi; // Same as above, just set for PORTC & stripped of comments - asm volatile( - "headC:" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeC" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeC" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeC" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeC" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeC" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeC" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeC" "\n\t" - "out %[port] , %[hi]" "\n\t" - "rjmp .+0" "\n\t" - "ld %[byte] , %a[ptr]+" "\n\t" - "out %[port] , %[next]" "\n\t" - "mov %[next] , %[lo]" "\n\t" - "sbrc %[byte] , 7" "\n\t" - "mov %[next] , %[hi]" "\n\t" - "nop" "\n\t" - "out %[port] , %[lo]" "\n\t" - "sbiw %[count], 1" "\n\t" - "brne headC" "\n\t" - "rjmp doneC" "\n\t" - "bitTimeC:" "\n\t" - "out %[port], %[next]" "\n\t" - "mov %[next], %[lo]" "\n\t" - "rol %[byte]" "\n\t" - "sbrc %[byte], 7" "\n\t" - "mov %[next], %[hi]" "\n\t" - "nop" "\n\t" - "out %[port], %[lo]" "\n\t" - "ret" "\n\t" - "doneC:" "\n" - : [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i) - : [port] "I" (_SFR_IO_ADDR(PORTC)), [ptr] "e" (ptr), [hi] "r" (hi), - [lo] "r" (lo)); - - #if defined(PORTD) || defined(PORTB) || defined(PORTF) + asm volatile("headC:" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeC" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeC" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeC" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeC" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeC" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeC" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeC" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "rjmp .+0" + "\n\t" + "ld %[byte] , %a[ptr]+" + "\n\t" + "out %[port] , %[next]" + "\n\t" + "mov %[next] , %[lo]" + "\n\t" + "sbrc %[byte] , 7" + "\n\t" + "mov %[next] , %[hi]" + "\n\t" + "nop" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "sbiw %[count], 1" + "\n\t" + "brne headC" + "\n\t" + "rjmp doneC" + "\n\t" + "bitTimeC:" + "\n\t" + "out %[port], %[next]" + "\n\t" + "mov %[next], %[lo]" + "\n\t" + "rol %[byte]" + "\n\t" + "sbrc %[byte], 7" + "\n\t" + "mov %[next], %[hi]" + "\n\t" + "nop" + "\n\t" + "out %[port], %[lo]" + "\n\t" + "ret" + "\n\t" + "doneC:" + "\n" + : [byte] "+r"(b), [next] "+r"(next), [count] "+w"(i) + : [port] "I"(_SFR_IO_ADDR(PORTC)), [ptr] "e"(ptr), + [hi] "r"(hi), [lo] "r"(lo)); + +#if defined(PORTD) || defined(PORTB) || defined(PORTF) } - #endif // defined(PORTD/B/F) - #if defined(PORTF) +#endif // defined(PORTD/B/F) +#if defined(PORTF) else - #endif +#endif #endif // defined(PORTC) // PORTF OUTPUT ---------------------------------------------------- #if defined(PORTF) - #if defined(PORTD) || defined(PORTB) || defined(PORTC) - if(port == &PORTF) { - #endif // defined(PORTD/B/C) +#if defined(PORTD) || defined(PORTB) || defined(PORTC) + if (port == &PORTF) { +#endif // defined(PORTD/B/C) - hi = PORTF | pinMask; - lo = PORTF & ~pinMask; + hi = PORTF | pinMask; + lo = PORTF & ~pinMask; next = lo; - if(b & 0x80) next = hi; + if (b & 0x80) + next = hi; // Same as above, just set for PORTF & stripped of comments - asm volatile( - "headF:" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeC" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeC" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeC" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeC" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeC" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeC" "\n\t" - "out %[port], %[hi]" "\n\t" - "rcall bitTimeC" "\n\t" - "out %[port] , %[hi]" "\n\t" - "rjmp .+0" "\n\t" - "ld %[byte] , %a[ptr]+" "\n\t" - "out %[port] , %[next]" "\n\t" - "mov %[next] , %[lo]" "\n\t" - "sbrc %[byte] , 7" "\n\t" - "mov %[next] , %[hi]" "\n\t" - "nop" "\n\t" - "out %[port] , %[lo]" "\n\t" - "sbiw %[count], 1" "\n\t" - "brne headF" "\n\t" - "rjmp doneC" "\n\t" - "bitTimeC:" "\n\t" - "out %[port], %[next]" "\n\t" - "mov %[next], %[lo]" "\n\t" - "rol %[byte]" "\n\t" - "sbrc %[byte], 7" "\n\t" - "mov %[next], %[hi]" "\n\t" - "nop" "\n\t" - "out %[port], %[lo]" "\n\t" - "ret" "\n\t" - "doneC:" "\n" - : [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i) - : [port] "I" (_SFR_IO_ADDR(PORTF)), [ptr] "e" (ptr), [hi] "r" (hi), - [lo] "r" (lo)); - - #if defined(PORTD) || defined(PORTB) || defined(PORTC) + asm volatile("headF:" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeC" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeC" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeC" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeC" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeC" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeC" + "\n\t" + "out %[port], %[hi]" + "\n\t" + "rcall bitTimeC" + "\n\t" + "out %[port] , %[hi]" + "\n\t" + "rjmp .+0" + "\n\t" + "ld %[byte] , %a[ptr]+" + "\n\t" + "out %[port] , %[next]" + "\n\t" + "mov %[next] , %[lo]" + "\n\t" + "sbrc %[byte] , 7" + "\n\t" + "mov %[next] , %[hi]" + "\n\t" + "nop" + "\n\t" + "out %[port] , %[lo]" + "\n\t" + "sbiw %[count], 1" + "\n\t" + "brne headF" + "\n\t" + "rjmp doneC" + "\n\t" + "bitTimeC:" + "\n\t" + "out %[port], %[next]" + "\n\t" + "mov %[next], %[lo]" + "\n\t" + "rol %[byte]" + "\n\t" + "sbrc %[byte], 7" + "\n\t" + "mov %[next], %[hi]" + "\n\t" + "nop" + "\n\t" + "out %[port], %[lo]" + "\n\t" + "ret" + "\n\t" + "doneC:" + "\n" + : [byte] "+r"(b), [next] "+r"(next), [count] "+w"(i) + : [port] "I"(_SFR_IO_ADDR(PORTF)), [ptr] "e"(ptr), + [hi] "r"(hi), [lo] "r"(lo)); + +#if defined(PORTD) || defined(PORTB) || defined(PORTC) } - #endif // defined(PORTD/B/C) +#endif // defined(PORTD/B/C) #endif // defined(PORTF) #if defined(NEO_KHZ400) @@ -987,45 +1448,64 @@ void Adafruit_NeoPixel::show(void) { volatile uint8_t next, bit; - hi = *port | pinMask; - lo = *port & ~pinMask; + hi = *port | pinMask; + lo = *port & ~pinMask; next = lo; - bit = 8; - - asm volatile( - "head30:" "\n\t" // Clk Pseudocode (T = 0) - "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) - "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128) - "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4) - "rjmp .+0" "\n\t" // 2 nop nop (T = 6) - "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 8) - "rjmp .+0" "\n\t" // 2 nop nop (T = 10) - "rjmp .+0" "\n\t" // 2 nop nop (T = 12) - "rjmp .+0" "\n\t" // 2 nop nop (T = 14) - "nop" "\n\t" // 1 nop (T = 15) - "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 17) - "rjmp .+0" "\n\t" // 2 nop nop (T = 19) - "dec %[bit]" "\n\t" // 1 bit-- (T = 20) - "breq nextbyte30" "\n\t" // 1-2 if(bit == 0) - "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 22) - "rjmp .+0" "\n\t" // 2 nop nop (T = 24) - "rjmp .+0" "\n\t" // 2 nop nop (T = 26) - "rjmp .+0" "\n\t" // 2 nop nop (T = 28) - "rjmp head30" "\n\t" // 2 -> head30 (next bit out) - "nextbyte30:" "\n\t" // (T = 22) - "nop" "\n\t" // 1 nop (T = 23) - "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 24) - "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 26) - "sbiw %[count], 1" "\n\t" // 2 i-- (T = 28) - "brne head30" "\n" // 1-2 if(i != 0) -> (next byte) - : [port] "+e" (port), - [byte] "+r" (b), - [bit] "+r" (bit), - [next] "+r" (next), - [count] "+w" (i) - : [hi] "r" (hi), - [lo] "r" (lo), - [ptr] "e" (ptr)); + bit = 8; + + asm volatile("head30:" + "\n\t" // Clk Pseudocode (T = 0) + "st %a[port], %[hi]" + "\n\t" // 2 PORT = hi (T = 2) + "sbrc %[byte] , 7" + "\n\t" // 1-2 if(b & 128) + "mov %[next], %[hi]" + "\n\t" // 0-1 next = hi (T = 4) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 6) + "st %a[port], %[next]" + "\n\t" // 2 PORT = next (T = 8) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 10) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 12) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 14) + "nop" + "\n\t" // 1 nop (T = 15) + "st %a[port], %[lo]" + "\n\t" // 2 PORT = lo (T = 17) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 19) + "dec %[bit]" + "\n\t" // 1 bit-- (T = 20) + "breq nextbyte30" + "\n\t" // 1-2 if(bit == 0) + "rol %[byte]" + "\n\t" // 1 b <<= 1 (T = 22) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 24) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 26) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 28) + "rjmp head30" + "\n\t" // 2 -> head30 (next bit out) + "nextbyte30:" + "\n\t" // (T = 22) + "nop" + "\n\t" // 1 nop (T = 23) + "ldi %[bit] , 8" + "\n\t" // 1 bit = 8 (T = 24) + "ld %[byte] , %a[ptr]+" + "\n\t" // 2 b = *ptr++ (T = 26) + "sbiw %[count], 1" + "\n\t" // 2 i-- (T = 28) + "brne head30" + "\n" // 1-2 if(i != 0) -> (next byte) + : [port] "+e"(port), [byte] "+r"(b), [bit] "+r"(bit), + [next] "+r"(next), [count] "+w"(i) + : [hi] "r"(hi), [lo] "r"(lo), [ptr] "e"(ptr)); } #endif // NEO_KHZ400 @@ -1033,7 +1513,7 @@ void Adafruit_NeoPixel::show(void) { #elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000L) #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled - if(is800KHz) { + if (is800KHz) { #endif // WS2811 and WS2812 have different hi/lo duty cycles; this is @@ -1044,42 +1524,58 @@ void Adafruit_NeoPixel::show(void) { volatile uint8_t next, bit; - hi = *port | pinMask; - lo = *port & ~pinMask; + hi = *port | pinMask; + lo = *port & ~pinMask; next = lo; - bit = 8; - - asm volatile( - "head20:" "\n\t" // Clk Pseudocode (T = 0) - "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) - "sbrc %[byte], 7" "\n\t" // 1-2 if(b & 128) - "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4) - "dec %[bit]" "\n\t" // 1 bit-- (T = 5) - "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 7) - "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 8) - "breq nextbyte20" "\n\t" // 1-2 if(bit == 0) (from dec above) - "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10) - "rjmp .+0" "\n\t" // 2 nop nop (T = 12) - "nop" "\n\t" // 1 nop (T = 13) - "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15) - "nop" "\n\t" // 1 nop (T = 16) - "rjmp .+0" "\n\t" // 2 nop nop (T = 18) - "rjmp head20" "\n\t" // 2 -> head20 (next bit out) - "nextbyte20:" "\n\t" // (T = 10) - "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 11) - "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 13) - "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15) - "nop" "\n\t" // 1 nop (T = 16) - "sbiw %[count], 1" "\n\t" // 2 i-- (T = 18) - "brne head20" "\n" // 2 if(i != 0) -> (next byte) - : [port] "+e" (port), - [byte] "+r" (b), - [bit] "+r" (bit), - [next] "+r" (next), - [count] "+w" (i) - : [ptr] "e" (ptr), - [hi] "r" (hi), - [lo] "r" (lo)); + bit = 8; + + asm volatile("head20:" + "\n\t" // Clk Pseudocode (T = 0) + "st %a[port], %[hi]" + "\n\t" // 2 PORT = hi (T = 2) + "sbrc %[byte], 7" + "\n\t" // 1-2 if(b & 128) + "mov %[next], %[hi]" + "\n\t" // 0-1 next = hi (T = 4) + "dec %[bit]" + "\n\t" // 1 bit-- (T = 5) + "st %a[port], %[next]" + "\n\t" // 2 PORT = next (T = 7) + "mov %[next] , %[lo]" + "\n\t" // 1 next = lo (T = 8) + "breq nextbyte20" + "\n\t" // 1-2 if(bit == 0) (from dec above) + "rol %[byte]" + "\n\t" // 1 b <<= 1 (T = 10) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 12) + "nop" + "\n\t" // 1 nop (T = 13) + "st %a[port], %[lo]" + "\n\t" // 2 PORT = lo (T = 15) + "nop" + "\n\t" // 1 nop (T = 16) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 18) + "rjmp head20" + "\n\t" // 2 -> head20 (next bit out) + "nextbyte20:" + "\n\t" // (T = 10) + "ldi %[bit] , 8" + "\n\t" // 1 bit = 8 (T = 11) + "ld %[byte] , %a[ptr]+" + "\n\t" // 2 b = *ptr++ (T = 13) + "st %a[port], %[lo]" + "\n\t" // 2 PORT = lo (T = 15) + "nop" + "\n\t" // 1 nop (T = 16) + "sbiw %[count], 1" + "\n\t" // 2 i-- (T = 18) + "brne head20" + "\n" // 2 if(i != 0) -> (next byte) + : [port] "+e"(port), [byte] "+r"(b), [bit] "+r"(bit), + [next] "+r"(next), [count] "+w"(i) + : [ptr] "e"(ptr), [hi] "r"(hi), [lo] "r"(lo)); #if defined(NEO_KHZ400) } else { // 400 KHz @@ -1091,273 +1587,368 @@ void Adafruit_NeoPixel::show(void) { volatile uint8_t next, bit; - hi = *port | pinMask; - lo = *port & ~pinMask; + hi = *port | pinMask; + lo = *port & ~pinMask; next = lo; - bit = 8; - - asm volatile( - "head40:" "\n\t" // Clk Pseudocode (T = 0) - "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) - "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128) - "mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 4) - "rjmp .+0" "\n\t" // 2 nop nop (T = 6) - "rjmp .+0" "\n\t" // 2 nop nop (T = 8) - "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 10) - "rjmp .+0" "\n\t" // 2 nop nop (T = 12) - "rjmp .+0" "\n\t" // 2 nop nop (T = 14) - "rjmp .+0" "\n\t" // 2 nop nop (T = 16) - "rjmp .+0" "\n\t" // 2 nop nop (T = 18) - "rjmp .+0" "\n\t" // 2 nop nop (T = 20) - "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 22) - "nop" "\n\t" // 1 nop (T = 23) - "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 24) - "dec %[bit]" "\n\t" // 1 bit-- (T = 25) - "breq nextbyte40" "\n\t" // 1-2 if(bit == 0) - "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 27) - "nop" "\n\t" // 1 nop (T = 28) - "rjmp .+0" "\n\t" // 2 nop nop (T = 30) - "rjmp .+0" "\n\t" // 2 nop nop (T = 32) - "rjmp .+0" "\n\t" // 2 nop nop (T = 34) - "rjmp .+0" "\n\t" // 2 nop nop (T = 36) - "rjmp .+0" "\n\t" // 2 nop nop (T = 38) - "rjmp head40" "\n\t" // 2 -> head40 (next bit out) - "nextbyte40:" "\n\t" // (T = 27) - "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 28) - "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 30) - "rjmp .+0" "\n\t" // 2 nop nop (T = 32) - "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 34) - "rjmp .+0" "\n\t" // 2 nop nop (T = 36) - "sbiw %[count], 1" "\n\t" // 2 i-- (T = 38) - "brne head40" "\n" // 1-2 if(i != 0) -> (next byte) - : [port] "+e" (port), - [byte] "+r" (b), - [bit] "+r" (bit), - [next] "+r" (next), - [count] "+w" (i) - : [ptr] "e" (ptr), - [hi] "r" (hi), - [lo] "r" (lo)); + bit = 8; + + asm volatile("head40:" + "\n\t" // Clk Pseudocode (T = 0) + "st %a[port], %[hi]" + "\n\t" // 2 PORT = hi (T = 2) + "sbrc %[byte] , 7" + "\n\t" // 1-2 if(b & 128) + "mov %[next] , %[hi]" + "\n\t" // 0-1 next = hi (T = 4) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 6) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 8) + "st %a[port], %[next]" + "\n\t" // 2 PORT = next (T = 10) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 12) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 14) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 16) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 18) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 20) + "st %a[port], %[lo]" + "\n\t" // 2 PORT = lo (T = 22) + "nop" + "\n\t" // 1 nop (T = 23) + "mov %[next] , %[lo]" + "\n\t" // 1 next = lo (T = 24) + "dec %[bit]" + "\n\t" // 1 bit-- (T = 25) + "breq nextbyte40" + "\n\t" // 1-2 if(bit == 0) + "rol %[byte]" + "\n\t" // 1 b <<= 1 (T = 27) + "nop" + "\n\t" // 1 nop (T = 28) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 30) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 32) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 34) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 36) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 38) + "rjmp head40" + "\n\t" // 2 -> head40 (next bit out) + "nextbyte40:" + "\n\t" // (T = 27) + "ldi %[bit] , 8" + "\n\t" // 1 bit = 8 (T = 28) + "ld %[byte] , %a[ptr]+" + "\n\t" // 2 b = *ptr++ (T = 30) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 32) + "st %a[port], %[lo]" + "\n\t" // 2 PORT = lo (T = 34) + "rjmp .+0" + "\n\t" // 2 nop nop (T = 36) + "sbiw %[count], 1" + "\n\t" // 2 i-- (T = 38) + "brne head40" + "\n" // 1-2 if(i != 0) -> (next byte) + : [port] "+e"(port), [byte] "+r"(b), [bit] "+r"(bit), + [next] "+r"(next), [count] "+w"(i) + : [ptr] "e"(ptr), [hi] "r"(hi), [lo] "r"(lo)); } #endif // NEO_KHZ400 #else - #error "CPU SPEED NOT SUPPORTED" +#error "CPU SPEED NOT SUPPORTED" #endif // end F_CPU ifdefs on __AVR__ -// END AVR ---------------------------------------------------------------- - + // END AVR ---------------------------------------------------------------- #elif defined(__arm__) -// ARM MCUs -- Teensy 3.0, 3.1, LC, Arduino Due, RP2040 ------------------- + // ARM MCUs -- Teensy 3.0, 3.1, LC, Arduino Due, RP2040 ------------------- #if defined(ARDUINO_ARCH_RP2040) // Use PIO rp2040Show(pin, pixels, numBytes, is800KHz); -#elif defined(TEENSYDUINO) && defined(KINETISK) // Teensy 3.0, 3.1, 3.2, 3.5, 3.6 -#define CYCLES_800_T0H (F_CPU / 4000000) -#define CYCLES_800_T1H (F_CPU / 1250000) -#define CYCLES_800 (F_CPU / 800000) -#define CYCLES_400_T0H (F_CPU / 2000000) -#define CYCLES_400_T1H (F_CPU / 833333) -#define CYCLES_400 (F_CPU / 400000) - - uint8_t *p = pixels, - *end = p + numBytes, pix, mask; - volatile uint8_t *set = portSetRegister(pin), - *clr = portClearRegister(pin); - uint32_t cyc; - - ARM_DEMCR |= ARM_DEMCR_TRCENA; +#elif defined(TEENSYDUINO) && \ + defined(KINETISK) // Teensy 3.0, 3.1, 3.2, 3.5, 3.6 +#define CYCLES_800_T0H (F_CPU / 4000000) +#define CYCLES_800_T1H (F_CPU / 1250000) +#define CYCLES_800 (F_CPU / 800000) +#define CYCLES_400_T0H (F_CPU / 2000000) +#define CYCLES_400_T1H (F_CPU / 833333) +#define CYCLES_400 (F_CPU / 400000) + + uint8_t *p = pixels, *end = p + numBytes, pix, mask; + volatile uint8_t *set = portSetRegister(pin), *clr = portClearRegister(pin); + uint32_t cyc; + + ARM_DEMCR |= ARM_DEMCR_TRCENA; ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled - if(is800KHz) { + if (is800KHz) { #endif cyc = ARM_DWT_CYCCNT + CYCLES_800; - while(p < end) { + while (p < end) { pix = *p++; - for(mask = 0x80; mask; mask >>= 1) { - while(ARM_DWT_CYCCNT - cyc < CYCLES_800); - cyc = ARM_DWT_CYCCNT; + for (mask = 0x80; mask; mask >>= 1) { + while (ARM_DWT_CYCCNT - cyc < CYCLES_800) + ; + cyc = ARM_DWT_CYCCNT; *set = 1; - if(pix & mask) { - while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T1H); + if (pix & mask) { + while (ARM_DWT_CYCCNT - cyc < CYCLES_800_T1H) + ; } else { - while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T0H); + while (ARM_DWT_CYCCNT - cyc < CYCLES_800_T0H) + ; } *clr = 1; } } - while(ARM_DWT_CYCCNT - cyc < CYCLES_800); + while (ARM_DWT_CYCCNT - cyc < CYCLES_800) + ; #if defined(NEO_KHZ400) } else { // 400 kHz bitstream cyc = ARM_DWT_CYCCNT + CYCLES_400; - while(p < end) { + while (p < end) { pix = *p++; - for(mask = 0x80; mask; mask >>= 1) { - while(ARM_DWT_CYCCNT - cyc < CYCLES_400); - cyc = ARM_DWT_CYCCNT; + for (mask = 0x80; mask; mask >>= 1) { + while (ARM_DWT_CYCCNT - cyc < CYCLES_400) + ; + cyc = ARM_DWT_CYCCNT; *set = 1; - if(pix & mask) { - while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T1H); + if (pix & mask) { + while (ARM_DWT_CYCCNT - cyc < CYCLES_400_T1H) + ; } else { - while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T0H); + while (ARM_DWT_CYCCNT - cyc < CYCLES_400_T0H) + ; } *clr = 1; } } - while(ARM_DWT_CYCCNT - cyc < CYCLES_400); + while (ARM_DWT_CYCCNT - cyc < CYCLES_400) + ; } #endif // NEO_KHZ400 #elif defined(TEENSYDUINO) && (defined(__IMXRT1052__) || defined(__IMXRT1062__)) -#define CYCLES_800_T0H (F_CPU_ACTUAL / 4000000) -#define CYCLES_800_T1H (F_CPU_ACTUAL / 1250000) -#define CYCLES_800 (F_CPU_ACTUAL / 800000) -#define CYCLES_400_T0H (F_CPU_ACTUAL / 2000000) -#define CYCLES_400_T1H (F_CPU_ACTUAL / 833333) -#define CYCLES_400 (F_CPU_ACTUAL / 400000) - - uint8_t *p = pixels, - *end = p + numBytes, pix, mask; - volatile uint32_t *set = portSetRegister(pin), - *clr = portClearRegister(pin); - uint32_t cyc, - msk = digitalPinToBitMask(pin); - - ARM_DEMCR |= ARM_DEMCR_TRCENA; +#define CYCLES_800_T0H (F_CPU_ACTUAL / 4000000) +#define CYCLES_800_T1H (F_CPU_ACTUAL / 1250000) +#define CYCLES_800 (F_CPU_ACTUAL / 800000) +#define CYCLES_400_T0H (F_CPU_ACTUAL / 2000000) +#define CYCLES_400_T1H (F_CPU_ACTUAL / 833333) +#define CYCLES_400 (F_CPU_ACTUAL / 400000) + + uint8_t *p = pixels, *end = p + numBytes, pix, mask; + volatile uint32_t *set = portSetRegister(pin), *clr = portClearRegister(pin); + uint32_t cyc, msk = digitalPinToBitMask(pin); + + ARM_DEMCR |= ARM_DEMCR_TRCENA; ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled - if(is800KHz) { + if (is800KHz) { #endif cyc = ARM_DWT_CYCCNT + CYCLES_800; - while(p < end) { + while (p < end) { pix = *p++; - for(mask = 0x80; mask; mask >>= 1) { - while(ARM_DWT_CYCCNT - cyc < CYCLES_800); - cyc = ARM_DWT_CYCCNT; + for (mask = 0x80; mask; mask >>= 1) { + while (ARM_DWT_CYCCNT - cyc < CYCLES_800) + ; + cyc = ARM_DWT_CYCCNT; *set = msk; - if(pix & mask) { - while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T1H); + if (pix & mask) { + while (ARM_DWT_CYCCNT - cyc < CYCLES_800_T1H) + ; } else { - while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T0H); + while (ARM_DWT_CYCCNT - cyc < CYCLES_800_T0H) + ; } *clr = msk; } } - while(ARM_DWT_CYCCNT - cyc < CYCLES_800); + while (ARM_DWT_CYCCNT - cyc < CYCLES_800) + ; #if defined(NEO_KHZ400) } else { // 400 kHz bitstream cyc = ARM_DWT_CYCCNT + CYCLES_400; - while(p < end) { + while (p < end) { pix = *p++; - for(mask = 0x80; mask; mask >>= 1) { - while(ARM_DWT_CYCCNT - cyc < CYCLES_400); - cyc = ARM_DWT_CYCCNT; + for (mask = 0x80; mask; mask >>= 1) { + while (ARM_DWT_CYCCNT - cyc < CYCLES_400) + ; + cyc = ARM_DWT_CYCCNT; *set = msk; - if(pix & mask) { - while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T1H); + if (pix & mask) { + while (ARM_DWT_CYCCNT - cyc < CYCLES_400_T1H) + ; } else { - while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T0H); + while (ARM_DWT_CYCCNT - cyc < CYCLES_400_T0H) + ; } *clr = msk; } } - while(ARM_DWT_CYCCNT - cyc < CYCLES_400); + while (ARM_DWT_CYCCNT - cyc < CYCLES_400) + ; } #endif // NEO_KHZ400 #elif defined(TEENSYDUINO) && defined(__MKL26Z64__) // Teensy-LC #if F_CPU == 48000000 - uint8_t *p = pixels, - pix, count, dly, - bitmask = digitalPinToBitMask(pin); + uint8_t *p = pixels, pix, count, dly, bitmask = digitalPinToBitMask(pin); volatile uint8_t *reg = portSetRegister(pin); - uint32_t num = numBytes; - asm volatile( - "L%=_begin:" "\n\t" - "ldrb %[pix], [%[p], #0]" "\n\t" - "lsl %[pix], #24" "\n\t" - "movs %[count], #7" "\n\t" - "L%=_loop:" "\n\t" - "lsl %[pix], #1" "\n\t" - "bcs L%=_loop_one" "\n\t" - "L%=_loop_zero:" "\n\t" - "strb %[bitmask], [%[reg], #0]" "\n\t" - "movs %[dly], #4" "\n\t" - "L%=_loop_delay_T0H:" "\n\t" - "sub %[dly], #1" "\n\t" - "bne L%=_loop_delay_T0H" "\n\t" - "strb %[bitmask], [%[reg], #4]" "\n\t" - "movs %[dly], #13" "\n\t" - "L%=_loop_delay_T0L:" "\n\t" - "sub %[dly], #1" "\n\t" - "bne L%=_loop_delay_T0L" "\n\t" - "b L%=_next" "\n\t" - "L%=_loop_one:" "\n\t" - "strb %[bitmask], [%[reg], #0]" "\n\t" - "movs %[dly], #13" "\n\t" - "L%=_loop_delay_T1H:" "\n\t" - "sub %[dly], #1" "\n\t" - "bne L%=_loop_delay_T1H" "\n\t" - "strb %[bitmask], [%[reg], #4]" "\n\t" - "movs %[dly], #4" "\n\t" - "L%=_loop_delay_T1L:" "\n\t" - "sub %[dly], #1" "\n\t" - "bne L%=_loop_delay_T1L" "\n\t" - "nop" "\n\t" - "L%=_next:" "\n\t" - "sub %[count], #1" "\n\t" - "bne L%=_loop" "\n\t" - "lsl %[pix], #1" "\n\t" - "bcs L%=_last_one" "\n\t" - "L%=_last_zero:" "\n\t" - "strb %[bitmask], [%[reg], #0]" "\n\t" - "movs %[dly], #4" "\n\t" - "L%=_last_delay_T0H:" "\n\t" - "sub %[dly], #1" "\n\t" - "bne L%=_last_delay_T0H" "\n\t" - "strb %[bitmask], [%[reg], #4]" "\n\t" - "movs %[dly], #10" "\n\t" - "L%=_last_delay_T0L:" "\n\t" - "sub %[dly], #1" "\n\t" - "bne L%=_last_delay_T0L" "\n\t" - "b L%=_repeat" "\n\t" - "L%=_last_one:" "\n\t" - "strb %[bitmask], [%[reg], #0]" "\n\t" - "movs %[dly], #13" "\n\t" - "L%=_last_delay_T1H:" "\n\t" - "sub %[dly], #1" "\n\t" - "bne L%=_last_delay_T1H" "\n\t" - "strb %[bitmask], [%[reg], #4]" "\n\t" - "movs %[dly], #1" "\n\t" - "L%=_last_delay_T1L:" "\n\t" - "sub %[dly], #1" "\n\t" - "bne L%=_last_delay_T1L" "\n\t" - "nop" "\n\t" - "L%=_repeat:" "\n\t" - "add %[p], #1" "\n\t" - "sub %[num], #1" "\n\t" - "bne L%=_begin" "\n\t" - "L%=_done:" "\n\t" - : [p] "+r" (p), - [pix] "=&r" (pix), - [count] "=&r" (count), - [dly] "=&r" (dly), - [num] "+r" (num) - : [bitmask] "r" (bitmask), - [reg] "r" (reg) - ); + uint32_t num = numBytes; + asm volatile("L%=_begin:" + "\n\t" + "ldrb %[pix], [%[p], #0]" + "\n\t" + "lsl %[pix], #24" + "\n\t" + "movs %[count], #7" + "\n\t" + "L%=_loop:" + "\n\t" + "lsl %[pix], #1" + "\n\t" + "bcs L%=_loop_one" + "\n\t" + "L%=_loop_zero:" + "\n\t" + "strb %[bitmask], [%[reg], #0]" + "\n\t" + "movs %[dly], #4" + "\n\t" + "L%=_loop_delay_T0H:" + "\n\t" + "sub %[dly], #1" + "\n\t" + "bne L%=_loop_delay_T0H" + "\n\t" + "strb %[bitmask], [%[reg], #4]" + "\n\t" + "movs %[dly], #13" + "\n\t" + "L%=_loop_delay_T0L:" + "\n\t" + "sub %[dly], #1" + "\n\t" + "bne L%=_loop_delay_T0L" + "\n\t" + "b L%=_next" + "\n\t" + "L%=_loop_one:" + "\n\t" + "strb %[bitmask], [%[reg], #0]" + "\n\t" + "movs %[dly], #13" + "\n\t" + "L%=_loop_delay_T1H:" + "\n\t" + "sub %[dly], #1" + "\n\t" + "bne L%=_loop_delay_T1H" + "\n\t" + "strb %[bitmask], [%[reg], #4]" + "\n\t" + "movs %[dly], #4" + "\n\t" + "L%=_loop_delay_T1L:" + "\n\t" + "sub %[dly], #1" + "\n\t" + "bne L%=_loop_delay_T1L" + "\n\t" + "nop" + "\n\t" + "L%=_next:" + "\n\t" + "sub %[count], #1" + "\n\t" + "bne L%=_loop" + "\n\t" + "lsl %[pix], #1" + "\n\t" + "bcs L%=_last_one" + "\n\t" + "L%=_last_zero:" + "\n\t" + "strb %[bitmask], [%[reg], #0]" + "\n\t" + "movs %[dly], #4" + "\n\t" + "L%=_last_delay_T0H:" + "\n\t" + "sub %[dly], #1" + "\n\t" + "bne L%=_last_delay_T0H" + "\n\t" + "strb %[bitmask], [%[reg], #4]" + "\n\t" + "movs %[dly], #10" + "\n\t" + "L%=_last_delay_T0L:" + "\n\t" + "sub %[dly], #1" + "\n\t" + "bne L%=_last_delay_T0L" + "\n\t" + "b L%=_repeat" + "\n\t" + "L%=_last_one:" + "\n\t" + "strb %[bitmask], [%[reg], #0]" + "\n\t" + "movs %[dly], #13" + "\n\t" + "L%=_last_delay_T1H:" + "\n\t" + "sub %[dly], #1" + "\n\t" + "bne L%=_last_delay_T1H" + "\n\t" + "strb %[bitmask], [%[reg], #4]" + "\n\t" + "movs %[dly], #1" + "\n\t" + "L%=_last_delay_T1L:" + "\n\t" + "sub %[dly], #1" + "\n\t" + "bne L%=_last_delay_T1L" + "\n\t" + "nop" + "\n\t" + "L%=_repeat:" + "\n\t" + "add %[p], #1" + "\n\t" + "sub %[num], #1" + "\n\t" + "bne L%=_begin" + "\n\t" + "L%=_done:" + "\n\t" + : [p] "+r"(p), [pix] "=&r"(pix), [count] "=&r"(count), + [dly] "=&r"(dly), [num] "+r"(num) + : [bitmask] "r"(bitmask), [reg] "r"(reg)); #else #error "Sorry, only 48 MHz is supported, please set Tools > CPU Speed to 48 MHz" #endif // F_CPU == 48000000 -// Begin of support for nRF52 based boards ------------------------- + // Begin of support for nRF52 based boards ------------------------- #elif defined(NRF52) || defined(NRF52_SERIES) // [[[Begin of the Neopixel NRF52 EasyDMA implementation @@ -1386,16 +1977,16 @@ void Adafruit_NeoPixel::show(void) { //#define MAGIC_T1H 12UL | (0x8000) // 0.75us // WS2812B (rev B) timing is 0.4 and 0.8 us -#define MAGIC_T0H 6UL | (0x8000) // 0.375us -#define MAGIC_T1H 13UL | (0x8000) // 0.8125us +#define MAGIC_T0H 6UL | (0x8000) // 0.375us +#define MAGIC_T1H 13UL | (0x8000) // 0.8125us // WS2811 (400 khz) timing is 0.5 and 1.2 -#define MAGIC_T0H_400KHz 8UL | (0x8000) // 0.5us -#define MAGIC_T1H_400KHz 19UL | (0x8000) // 1.1875us +#define MAGIC_T0H_400KHz 8UL | (0x8000) // 0.5us +#define MAGIC_T1H_400KHz 19UL | (0x8000) // 1.1875us // For 400Khz, we double value of CTOPVAL -#define CTOPVAL 20UL // 1.25us -#define CTOPVAL_400KHz 40UL // 2.5us +#define CTOPVAL 20UL // 1.25us +#define CTOPVAL_400KHz 40UL // 2.5us // ---------- END Constants for the EasyDMA implementation ------------- // @@ -1407,14 +1998,14 @@ void Adafruit_NeoPixel::show(void) { // The number of cycles was hand picked and is guaranteed to be 100% // organic to preserve freshness and high accuracy. // ---------- BEGIN Constants for cycle counter implementation --------- -#define CYCLES_800_T0H 18 // ~0.36 uS -#define CYCLES_800_T1H 41 // ~0.76 uS -#define CYCLES_800 71 // ~1.25 uS +#define CYCLES_800_T0H 18 // ~0.36 uS +#define CYCLES_800_T1H 41 // ~0.76 uS +#define CYCLES_800 71 // ~1.25 uS -#define CYCLES_400_T0H 26 // ~0.50 uS -#define CYCLES_400_T1H 70 // ~1.26 uS -#define CYCLES_400 156 // ~2.50 uS -// ---------- END of Constants for cycle counter implementation -------- +#define CYCLES_400_T0H 26 // ~0.50 uS +#define CYCLES_400_T1H 70 // ~1.26 uS +#define CYCLES_400 156 // ~2.50 uS + // ---------- END of Constants for cycle counter implementation -------- // To support both the SoftDevice + Neopixels we use the EasyDMA // feature from the NRF25. However this technique implies to @@ -1426,55 +2017,60 @@ void Adafruit_NeoPixel::show(void) { // // If there is not enough memory, we will fall back to cycle counter // using DWT - uint32_t pattern_size = numBytes*8*sizeof(uint16_t)+2*sizeof(uint16_t); - uint16_t* pixels_pattern = NULL; + uint32_t pattern_size = + numBytes * 8 * sizeof(uint16_t) + 2 * sizeof(uint16_t); + uint16_t *pixels_pattern = NULL; - NRF_PWM_Type* pwm = NULL; + NRF_PWM_Type *pwm = NULL; // Try to find a free PWM device, which is not enabled // and has no connected pins - NRF_PWM_Type* PWM[] = { - NRF_PWM0, NRF_PWM1, NRF_PWM2 + NRF_PWM_Type *PWM[] = { + NRF_PWM0, + NRF_PWM1, + NRF_PWM2 #if defined(NRF_PWM3) - ,NRF_PWM3 + , + NRF_PWM3 #endif }; - for(unsigned int device = 0; device < (sizeof(PWM)/sizeof(PWM[0])); device++) { - if( (PWM[device]->ENABLE == 0) && + for (unsigned int device = 0; device < (sizeof(PWM) / sizeof(PWM[0])); + device++) { + if ((PWM[device]->ENABLE == 0) && (PWM[device]->PSEL.OUT[0] & PWM_PSEL_OUT_CONNECT_Msk) && (PWM[device]->PSEL.OUT[1] & PWM_PSEL_OUT_CONNECT_Msk) && (PWM[device]->PSEL.OUT[2] & PWM_PSEL_OUT_CONNECT_Msk) && - (PWM[device]->PSEL.OUT[3] & PWM_PSEL_OUT_CONNECT_Msk) - ) { + (PWM[device]->PSEL.OUT[3] & PWM_PSEL_OUT_CONNECT_Msk)) { pwm = PWM[device]; break; } } // only malloc if there is PWM device available - if ( pwm != NULL ) { - #if defined(ARDUINO_NRF52_ADAFRUIT) // use thread-safe malloc - pixels_pattern = (uint16_t *) rtos_malloc(pattern_size); - #else - pixels_pattern = (uint16_t *) malloc(pattern_size); - #endif + if (pwm != NULL) { +#if defined(ARDUINO_NRF52_ADAFRUIT) // use thread-safe malloc + pixels_pattern = (uint16_t *)rtos_malloc(pattern_size); +#else + pixels_pattern = (uint16_t *)malloc(pattern_size); +#endif } // Use the identified device to choose the implementation // If a PWM device is available use DMA - if( (pixels_pattern != NULL) && (pwm != NULL) ) { + if ((pixels_pattern != NULL) && (pwm != NULL)) { uint16_t pos = 0; // bit position - for(uint16_t n=0; n0; mask >>= 1) { - #if defined(NEO_KHZ400) - if( !is800KHz ) { - pixels_pattern[pos] = (pix & mask) ? MAGIC_T1H_400KHz : MAGIC_T0H_400KHz; - }else - #endif + for (uint8_t mask = 0x80; mask > 0; mask >>= 1) { +#if defined(NEO_KHZ400) + if (!is800KHz) { + pixels_pattern[pos] = + (pix & mask) ? MAGIC_T1H_400KHz : MAGIC_T0H_400KHz; + } else +#endif { pixels_pattern[pos] = (pix & mask) ? MAGIC_T1H : MAGIC_T0H; } @@ -1491,15 +2087,16 @@ void Adafruit_NeoPixel::show(void) { pwm->MODE = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos); // Set the PWM to use the 16MHz clock - pwm->PRESCALER = (PWM_PRESCALER_PRESCALER_DIV_1 << PWM_PRESCALER_PRESCALER_Pos); + pwm->PRESCALER = + (PWM_PRESCALER_PRESCALER_DIV_1 << PWM_PRESCALER_PRESCALER_Pos); // Setting of the maximum count // but keeping it on 16Mhz allows for more granularity just // in case someone wants to do more fine-tuning of the timing. #if defined(NEO_KHZ400) - if( !is800KHz ) { + if (!is800KHz) { pwm->COUNTERTOP = (CTOPVAL_400KHz << PWM_COUNTERTOP_COUNTERTOP_Pos); - }else + } else #endif { pwm->COUNTERTOP = (CTOPVAL << PWM_COUNTERTOP_COUNTERTOP_Pos); @@ -1518,10 +2115,10 @@ void Adafruit_NeoPixel::show(void) { pwm->SEQ[0].PTR = (uint32_t)(pixels_pattern) << PWM_SEQ_PTR_PTR_Pos; // Calculation of the number of steps loaded from memory. - pwm->SEQ[0].CNT = (pattern_size/sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos; + pwm->SEQ[0].CNT = (pattern_size / sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos; // The following settings are ignored with the current config. - pwm->SEQ[0].REFRESH = 0; + pwm->SEQ[0].REFRESH = 0; pwm->SEQ[0].ENDDELAY = 0; // The Neopixel implementation is a blocking algorithm. DMA @@ -1529,29 +2126,28 @@ void Adafruit_NeoPixel::show(void) { // operation we enable the interruption for the end of sequence // and block the execution thread until the event flag is set by // the peripheral. -// pwm->INTEN |= (PWM_INTEN_SEQEND0_Enabled<INTEN |= (PWM_INTEN_SEQEND0_Enabled<PSEL.OUT[0] = g_APinDescription[pin].name; - #else +#else pwm->PSEL.OUT[0] = g_ADigitalPinMap[pin]; - #endif +#endif // Enable the PWM pwm->ENABLE = 1; // After all of this and many hours of reading the documentation // we are ready to start the sequence... - pwm->EVENTS_SEQEND[0] = 0; + pwm->EVENTS_SEQEND[0] = 0; pwm->TASKS_SEQSTART[0] = 1; // But we have to wait for the flag to be set. - while(!pwm->EVENTS_SEQEND[0]) - { - #if defined(ARDUINO_NRF52_ADAFRUIT) || defined(ARDUINO_ARCH_NRF52840) + while (!pwm->EVENTS_SEQEND[0]) { +#if defined(ARDUINO_NRF52_ADAFRUIT) || defined(ARDUINO_ARCH_NRF52840) yield(); - #endif +#endif } // Before leave we clear the flag for the event. @@ -1565,40 +2161,39 @@ void Adafruit_NeoPixel::show(void) { pwm->PSEL.OUT[0] = 0xFFFFFFFFUL; - #if defined(ARDUINO_NRF52_ADAFRUIT) // use thread-safe free - rtos_free(pixels_pattern); - #else - free(pixels_pattern); - #endif - }// End of DMA implementation +#if defined(ARDUINO_NRF52_ADAFRUIT) // use thread-safe free + rtos_free(pixels_pattern); +#else + free(pixels_pattern); +#endif + } // End of DMA implementation // --------------------------------------------------------------------- - else{ -#ifndef ARDUINO_ARCH_NRF52840 - // Fall back to DWT - #if defined(ARDUINO_NRF52_ADAFRUIT) - // Bluefruit Feather 52 uses freeRTOS - // Critical Section is used since it does not block SoftDevice execution - taskENTER_CRITICAL(); - #elif defined(NRF52_DISABLE_INT) - // If you are using the Bluetooth SoftDevice we advise you to not disable - // the interrupts. Disabling the interrupts even for short periods of time - // causes the SoftDevice to stop working. - // Disable the interrupts only in cases where you need high performance for - // the LEDs and if you are not using the EasyDMA feature. - __disable_irq(); - #endif - - NRF_GPIO_Type* nrf_port = (NRF_GPIO_Type*) digitalPinToPort(pin); + else { +#ifndef ARDUINO_ARCH_NRF52840 +// Fall back to DWT +#if defined(ARDUINO_NRF52_ADAFRUIT) + // Bluefruit Feather 52 uses freeRTOS + // Critical Section is used since it does not block SoftDevice execution + taskENTER_CRITICAL(); +#elif defined(NRF52_DISABLE_INT) + // If you are using the Bluetooth SoftDevice we advise you to not disable + // the interrupts. Disabling the interrupts even for short periods of time + // causes the SoftDevice to stop working. + // Disable the interrupts only in cases where you need high performance for + // the LEDs and if you are not using the EasyDMA feature. + __disable_irq(); +#endif + + NRF_GPIO_Type *nrf_port = (NRF_GPIO_Type *)digitalPinToPort(pin); uint32_t pinMask = digitalPinToBitMask(pin); - uint32_t CYCLES_X00 = CYCLES_800; + uint32_t CYCLES_X00 = CYCLES_800; uint32_t CYCLES_X00_T1H = CYCLES_800_T1H; uint32_t CYCLES_X00_T0H = CYCLES_800_T0H; #if defined(NEO_KHZ400) - if( !is800KHz ) - { - CYCLES_X00 = CYCLES_400; + if (!is800KHz) { + CYCLES_X00 = CYCLES_400; CYCLES_X00_T1H = CYCLES_400_T1H; CYCLES_X00_T0H = CYCLES_400_T0H; } @@ -1609,36 +2204,39 @@ void Adafruit_NeoPixel::show(void) { DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; // Tries to re-send the frame if is interrupted by the SoftDevice. - while(1) { + while (1) { uint8_t *p = pixels; uint32_t cycStart = DWT->CYCCNT; uint32_t cyc = 0; - for(uint16_t n=0; n>= 1) { - while(DWT->CYCCNT - cyc < CYCLES_X00); - cyc = DWT->CYCCNT; + for (uint8_t mask = 0x80; mask; mask >>= 1) { + while (DWT->CYCCNT - cyc < CYCLES_X00) + ; + cyc = DWT->CYCCNT; nrf_port->OUTSET |= pinMask; - if(pix & mask) { - while(DWT->CYCCNT - cyc < CYCLES_X00_T1H); + if (pix & mask) { + while (DWT->CYCCNT - cyc < CYCLES_X00_T1H) + ; } else { - while(DWT->CYCCNT - cyc < CYCLES_X00_T0H); + while (DWT->CYCCNT - cyc < CYCLES_X00_T0H) + ; } nrf_port->OUTCLR |= pinMask; } } - while(DWT->CYCCNT - cyc < CYCLES_X00); - + while (DWT->CYCCNT - cyc < CYCLES_X00) + ; // If total time longer than 25%, resend the whole data. // Since we are likely to be interrupted by SoftDevice - if ( (DWT->CYCCNT - cycStart) < ( 8*numBytes*((CYCLES_X00*5)/4) ) ) { + if ((DWT->CYCCNT - cycStart) < (8 * numBytes * ((CYCLES_X00 * 5) / 4))) { break; } @@ -1646,40 +2244,44 @@ void Adafruit_NeoPixel::show(void) { delayMicroseconds(300); } - // Enable interrupts again - #if defined(ARDUINO_NRF52_ADAFRUIT) - taskEXIT_CRITICAL(); - #elif defined(NRF52_DISABLE_INT) - __enable_irq(); - #endif +// Enable interrupts again +#if defined(ARDUINO_NRF52_ADAFRUIT) + taskEXIT_CRITICAL(); +#elif defined(NRF52_DISABLE_INT) + __enable_irq(); +#endif #endif } -// END of NRF52 implementation + // END of NRF52 implementation -#elif defined (__SAMD21E17A__) || defined(__SAMD21G18A__) || defined(__SAMD21E18A__) || defined(__SAMD21J18A__) // Arduino Zero, Gemma/Trinket M0, SODAQ Autonomo and others +#elif defined(__SAMD21E17A__) || defined(__SAMD21G18A__) || \ + defined(__SAMD21E18A__) || defined(__SAMD21J18A__) || \ + defined (__SAMD11C14A__) + // Arduino Zero, Gemma/Trinket M0, SODAQ Autonomo + // and others // Tried this with a timer/counter, couldn't quite get adequate // resolution. So yay, you get a load of goofball NOPs... - uint8_t *ptr, *end, p, bitMask, portNum; - uint32_t pinMask; + uint8_t *ptr, *end, p, bitMask, portNum; + uint32_t pinMask; - portNum = g_APinDescription[pin].ulPort; - pinMask = 1ul << g_APinDescription[pin].ulPin; - ptr = pixels; - end = ptr + numBytes; - p = *ptr++; - bitMask = 0x80; + portNum = g_APinDescription[pin].ulPort; + pinMask = 1ul << g_APinDescription[pin].ulPin; + ptr = pixels; + end = ptr + numBytes; + p = *ptr++; + bitMask = 0x80; volatile uint32_t *set = &(PORT->Group[portNum].OUTSET.reg), *clr = &(PORT->Group[portNum].OUTCLR.reg); #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled - if(is800KHz) { + if (is800KHz) { #endif - for(;;) { + for (;;) { *set = pinMask; asm("nop; nop; nop; nop; nop; nop; nop; nop;"); - if(p & bitMask) { + if (p & bitMask) { asm("nop; nop; nop; nop; nop; nop; nop; nop;" "nop; nop; nop; nop; nop; nop; nop; nop;" "nop; nop; nop; nop;"); @@ -1690,20 +2292,21 @@ void Adafruit_NeoPixel::show(void) { "nop; nop; nop; nop; nop; nop; nop; nop;" "nop; nop; nop; nop;"); } - if(bitMask >>= 1) { + if (bitMask >>= 1) { asm("nop; nop; nop; nop; nop; nop; nop; nop; nop;"); } else { - if(ptr >= end) break; - p = *ptr++; + if (ptr >= end) + break; + p = *ptr++; bitMask = 0x80; } } #if defined(NEO_KHZ400) } else { // 400 KHz bitstream - for(;;) { + for (;;) { *set = pinMask; asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;"); - if(p & bitMask) { + if (p & bitMask) { asm("nop; nop; nop; nop; nop; nop; nop; nop;" "nop; nop; nop; nop; nop; nop; nop; nop;" "nop; nop; nop; nop; nop; nop; nop; nop;" @@ -1720,29 +2323,195 @@ void Adafruit_NeoPixel::show(void) { "nop; nop; nop; nop; nop; nop; nop; nop;" "nop; nop; nop; nop; nop; nop; nop; nop;" "nop; nop; nop; nop; nop; nop; nop; nop;"); - if(bitMask >>= 1) { + if (bitMask >>= 1) { asm("nop; nop; nop; nop; nop; nop; nop;"); } else { - if(ptr >= end) break; - p = *ptr++; + if (ptr >= end) + break; + p = *ptr++; bitMask = 0x80; } } } #endif -#elif defined (__SAMD51__) // M4 +//---- +#elif defined(XMC1100_XMC2GO) || defined(XMC1100_H_BRIDGE2GO) || defined(XMC1100_Boot_Kit) || defined(XMC1300_Boot_Kit) + + // XMC1100/1200/1300 with ARM Cortex M0 are running with 32MHz, XMC1400 runs with 48MHz so may not work + // Tried this with a timer/counter, couldn't quite get adequate + // resolution. So yay, you get a load of goofball NOPs... - uint8_t *ptr, *end, p, bitMask, portNum, bit; + uint8_t *ptr, *end, p, bitMask, portNum; uint32_t pinMask; - portNum = g_APinDescription[pin].ulPort; - pinMask = 1ul << g_APinDescription[pin].ulPin; ptr = pixels; end = ptr + numBytes; p = *ptr++; bitMask = 0x80; + XMC_GPIO_PORT_t* XMC_port = mapping_port_pin[ pin ].port; + uint8_t XMC_pin = mapping_port_pin[ pin ].pin; + + uint32_t omrhigh = (uint32_t)XMC_GPIO_OUTPUT_LEVEL_HIGH << XMC_pin; + uint32_t omrlow = (uint32_t)XMC_GPIO_OUTPUT_LEVEL_LOW << XMC_pin; + +#ifdef NEO_KHZ400 // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + for(;;) { + XMC_port->OMR = omrhigh; + asm("nop; nop; nop; nop;"); + if(p & bitMask) { + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop;"); + XMC_port->OMR = omrlow; + } else { + XMC_port->OMR = omrlow; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop;"); + } + if(bitMask >>= 1) { + asm("nop; nop; nop; nop; nop;"); + } else { + if(ptr >= end) break; + p = *ptr++; + bitMask = 0x80; + } + } +#ifdef NEO_KHZ400 // untested code + } else { // 400 KHz bitstream + for(;;) { + XMC_port->OMR = omrhigh; + asm("nop; nop; nop; nop; nop;"); + if(p & bitMask) { + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop;"); + XMC_port->OMR = omrlow; + } else { + XMC_port->OMR = omrlow; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop;"); + } + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;"); + if(bitMask >>= 1) { + asm("nop; nop; nop;"); + } else { + if(ptr >= end) break; + p = *ptr++; + bitMask = 0x80; + } + } + } + +#endif +//---- + +//---- +#elif defined(XMC4700_Relax_Kit) || defined(XMC4800_Relax_Kit) + +// XMC4700 and XMC4800 with ARM Cortex M4 are running with 144MHz +// Tried this with a timer/counter, couldn't quite get adequate +// resolution. So yay, you get a load of goofball NOPs... + +uint8_t *ptr, *end, p, bitMask, portNum; +uint32_t pinMask; + +ptr = pixels; +end = ptr + numBytes; +p = *ptr++; +bitMask = 0x80; + +XMC_GPIO_PORT_t* XMC_port = mapping_port_pin[ pin ].port; +uint8_t XMC_pin = mapping_port_pin[ pin ].pin; + +uint32_t omrhigh = (uint32_t)XMC_GPIO_OUTPUT_LEVEL_HIGH << XMC_pin; +uint32_t omrlow = (uint32_t)XMC_GPIO_OUTPUT_LEVEL_LOW << XMC_pin; + +#ifdef NEO_KHZ400 // 800 KHz check needed only if 400 KHz support enabled +if(is800KHz) { +#endif + + for(;;) { + XMC_port->OMR = omrhigh; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop;"); + if(p & bitMask) { + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;"); + XMC_port->OMR = omrlow; + } else { + XMC_port->OMR = omrlow; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;"); + } + if(bitMask >>= 1) { + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;"); + } else { + if(ptr >= end) break; + p = *ptr++; + bitMask = 0x80; + } + } + + +#ifdef NEO_KHZ400 + } else { // 400 KHz bitstream + // ToDo! + } +#endif +//---- + +#elif defined(__SAMD51__) // M4 + + uint8_t *ptr, *end, p, bitMask, portNum, bit; + uint32_t pinMask; + + portNum = g_APinDescription[pin].ulPort; + pinMask = 1ul << g_APinDescription[pin].ulPin; + ptr = pixels; + end = ptr + numBytes; + p = *ptr++; + bitMask = 0x80; + volatile uint32_t *set = &(PORT->Group[portNum].OUTSET.reg), *clr = &(PORT->Group[portNum].OUTCLR.reg); @@ -1762,65 +2531,67 @@ void Adafruit_NeoPixel::show(void) { // seems to work just well enough. When finished, the SysTick // peripheral is set back to its original state. - uint32_t t0, t1, top, ticks, - saveLoad = SysTick->LOAD, saveVal = SysTick->VAL; + uint32_t t0, t1, top, ticks, saveLoad = SysTick->LOAD, saveVal = SysTick->VAL; #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled - if(is800KHz) { + if (is800KHz) { #endif - top = (uint32_t)(F_CPU * 0.00000125); // Bit hi + lo = 1.25 uS - t0 = top - (uint32_t)(F_CPU * 0.00000040); // 0 = 0.4 uS hi - t1 = top - (uint32_t)(F_CPU * 0.00000080); // 1 = 0.8 uS hi + top = (uint32_t)(F_CPU * 0.00000125); // Bit hi + lo = 1.25 uS + t0 = top - (uint32_t)(F_CPU * 0.00000040); // 0 = 0.4 uS hi + t1 = top - (uint32_t)(F_CPU * 0.00000080); // 1 = 0.8 uS hi #if defined(NEO_KHZ400) - } else { // 400 KHz bitstream - top = (uint32_t)(F_CPU * 0.00000250); // Bit hi + lo = 2.5 uS - t0 = top - (uint32_t)(F_CPU * 0.00000050); // 0 = 0.5 uS hi - t1 = top - (uint32_t)(F_CPU * 0.00000120); // 1 = 1.2 uS hi + } else { // 400 KHz bitstream + top = (uint32_t)(F_CPU * 0.00000250); // Bit hi + lo = 2.5 uS + t0 = top - (uint32_t)(F_CPU * 0.00000050); // 0 = 0.5 uS hi + t1 = top - (uint32_t)(F_CPU * 0.00000120); // 1 = 1.2 uS hi } #endif - SysTick->LOAD = top; // Config SysTick for NeoPixel bit freq - SysTick->VAL = top; // Set to start value (counts down) - (void)SysTick->VAL; // Dummy read helps sync up 1st bit + SysTick->LOAD = top; // Config SysTick for NeoPixel bit freq + SysTick->VAL = top; // Set to start value (counts down) + (void)SysTick->VAL; // Dummy read helps sync up 1st bit - for(;;) { - *set = pinMask; // Set output high + for (;;) { + *set = pinMask; // Set output high ticks = (p & bitMask) ? t1 : t0; // SysTick threshold, - while(SysTick->VAL > ticks); // wait for it - *clr = pinMask; // Set output low - if(!(bitMask >>= 1)) { // Next bit for this byte...done? - if(ptr >= end) break; // If last byte sent, exit loop - p = *ptr++; // Fetch next byte - bitMask = 0x80; // Reset bitmask + while (SysTick->VAL > ticks) + ; // wait for it + *clr = pinMask; // Set output low + if (!(bitMask >>= 1)) { // Next bit for this byte...done? + if (ptr >= end) + break; // If last byte sent, exit loop + p = *ptr++; // Fetch next byte + bitMask = 0x80; // Reset bitmask } - while(SysTick->VAL <= ticks); // Wait for rollover to 'top' + while (SysTick->VAL <= ticks) + ; // Wait for rollover to 'top' } - SysTick->LOAD = saveLoad; // Restore SysTick rollover to 1 ms - SysTick->VAL = saveVal; // Restore SysTick value + SysTick->LOAD = saveLoad; // Restore SysTick rollover to 1 ms + SysTick->VAL = saveVal; // Restore SysTick value -#elif defined (ARDUINO_STM32_FEATHER) // FEATHER WICED (120MHz) +#elif defined(ARDUINO_STM32_FEATHER) // FEATHER WICED (120MHz) // Tried this with a timer/counter, couldn't quite get adequate // resolution. So yay, you get a load of goofball NOPs... - uint8_t *ptr, *end, p, bitMask; - uint32_t pinMask; + uint8_t *ptr, *end, p, bitMask; + uint32_t pinMask; - pinMask = BIT(PIN_MAP[pin].gpio_bit); - ptr = pixels; - end = ptr + numBytes; - p = *ptr++; - bitMask = 0x80; + pinMask = BIT(PIN_MAP[pin].gpio_bit); + ptr = pixels; + end = ptr + numBytes; + p = *ptr++; + bitMask = 0x80; volatile uint16_t *set = &(PIN_MAP[pin].gpio_device->regs->BSRRL); volatile uint16_t *clr = &(PIN_MAP[pin].gpio_device->regs->BSRRH); #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled - if(is800KHz) { + if (is800KHz) { #endif - for(;;) { - if(p & bitMask) { // ONE + for (;;) { + if (p & bitMask) { // ONE // High 800ns *set = pinMask; asm("nop; nop; nop; nop; nop; nop; nop; nop;" @@ -1866,12 +2637,13 @@ void Adafruit_NeoPixel::show(void) { "nop; nop; nop; nop; nop; nop; nop; nop;" "nop; nop; nop; nop;"); } - if(bitMask >>= 1) { + if (bitMask >>= 1) { // Move on to the next pixel asm("nop;"); } else { - if(ptr >= end) break; - p = *ptr++; + if (ptr >= end) + break; + p = *ptr++; bitMask = 0x80; } } @@ -1882,17 +2654,17 @@ void Adafruit_NeoPixel::show(void) { #endif #elif defined(TARGET_LPC1768) - uint8_t *ptr, *end, p, bitMask; - ptr = pixels; - end = ptr + numBytes; - p = *ptr++; - bitMask = 0x80; + uint8_t *ptr, *end, p, bitMask; + ptr = pixels; + end = ptr + numBytes; + p = *ptr++; + bitMask = 0x80; #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled - if(is800KHz) { + if (is800KHz) { #endif - for(;;) { - if(p & bitMask) { + for (;;) { + if (p & bitMask) { // data ONE high // min: 550 typ: 700 max: 5,500 gpio_set(pin); @@ -1910,12 +2682,13 @@ void Adafruit_NeoPixel::show(void) { gpio_clear(pin); time::delay_ns(450); } - if(bitMask >>= 1) { + if (bitMask >>= 1) { // Move on to the next pixel asm("nop;"); } else { - if(ptr >= end) break; - p = *ptr++; + if (ptr >= end) + break; + p = *ptr++; bitMask = 0x80; } } @@ -1925,204 +2698,229 @@ void Adafruit_NeoPixel::show(void) { } #endif #elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ARDUINO_CORE_STM32) - uint8_t *p = pixels, *end = p + numBytes, - pix = *p++, mask = 0x80; - uint32_t cyc; + uint8_t *p = pixels, *end = p + numBytes, pix = *p++, mask = 0x80; + uint32_t cyc; uint32_t saveLoad = SysTick->LOAD, saveVal = SysTick->VAL; #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled - if(is800KHz) { + if (is800KHz) { #endif - uint32_t top = (F_CPU / 800000); // 1.25µs - uint32_t t0 = top - (F_CPU / 2500000); // 0.4µs - uint32_t t1 = top - (F_CPU / 1250000); // 0.8µs + uint32_t top = (F_CPU / 800000); // 1.25µs + uint32_t t0 = top - (F_CPU / 2500000); // 0.4µs + uint32_t t1 = top - (F_CPU / 1250000); // 0.8µs SysTick->LOAD = top - 1; // Config SysTick for NeoPixel bit freq - SysTick->VAL = 0; // Set to start value - for(;;) { + SysTick->VAL = 0; // Set to start value + for (;;) { LL_GPIO_SetOutputPin(gpioPort, gpioPin); cyc = (pix & mask) ? t1 : t0; - while(SysTick->VAL > cyc); + while (SysTick->VAL > cyc) + ; LL_GPIO_ResetOutputPin(gpioPort, gpioPin); - if(!(mask >>= 1)) { - if(p >= end) break; - pix = *p++; + if (!(mask >>= 1)) { + if (p >= end) + break; + pix = *p++; mask = 0x80; } - while(SysTick->VAL <= cyc); + while (SysTick->VAL <= cyc) + ; } #if defined(NEO_KHZ400) - } else { // 400 kHz bitstream - uint32_t top = (F_CPU / 400000); // 2.5µs - uint32_t t0 = top - (F_CPU / 2000000); // 0.5µs - uint32_t t1 = top - (F_CPU / 833333); // 1.2µs + } else { // 400 kHz bitstream + uint32_t top = (F_CPU / 400000); // 2.5µs + uint32_t t0 = top - (F_CPU / 2000000); // 0.5µs + uint32_t t1 = top - (F_CPU / 833333); // 1.2µs SysTick->LOAD = top - 1; // Config SysTick for NeoPixel bit freq - SysTick->VAL = 0; // Set to start value - for(;;) { + SysTick->VAL = 0; // Set to start value + for (;;) { LL_GPIO_SetOutputPin(gpioPort, gpioPin); cyc = (pix & mask) ? t1 : t0; - while(SysTick->VAL > cyc); + while (SysTick->VAL > cyc) + ; LL_GPIO_ResetOutputPin(gpioPort, gpioPin); - if(!(mask >>= 1)) { - if(p >= end) break; - pix = *p++; + if (!(mask >>= 1)) { + if (p >= end) + break; + pix = *p++; mask = 0x80; } - while(SysTick->VAL <= cyc); + while (SysTick->VAL <= cyc) + ; } } #endif // NEO_KHZ400 - SysTick->LOAD = saveLoad; // Restore SysTick rollover to 1 ms - SysTick->VAL = saveVal; // Restore SysTick value -#elif defined (NRF51) - uint8_t *p = pixels, - pix, count, mask; - int32_t num = numBytes; - unsigned int bitmask = ( 1 << g_ADigitalPinMap[pin] ); -// https://github.com/sandeepmistry/arduino-nRF5/blob/dc53980c8bac27898fca90d8ecb268e11111edc1/variants/BBCmicrobit/variant.cpp + SysTick->LOAD = saveLoad; // Restore SysTick rollover to 1 ms + SysTick->VAL = saveVal; // Restore SysTick value +#elif defined(NRF51) + uint8_t *p = pixels, pix, count, mask; + int32_t num = numBytes; + unsigned int bitmask = (1 << g_ADigitalPinMap[pin]); + // https://github.com/sandeepmistry/arduino-nRF5/blob/dc53980c8bac27898fca90d8ecb268e11111edc1/variants/BBCmicrobit/variant.cpp - volatile unsigned int *reg = (unsigned int *) (0x50000000UL + 0x508); + volatile unsigned int *reg = (unsigned int *)(0x50000000UL + 0x508); -// https://github.com/sandeepmistry/arduino-nRF5/blob/dc53980c8bac27898fca90d8ecb268e11111edc1/cores/nRF5/SDK/components/device/nrf51.h -// http://www.iot-programmer.com/index.php/books/27-micro-bit-iot-in-c/chapters-micro-bit-iot-in-c/47-micro-bit-iot-in-c-fast-memory-mapped-gpio?showall=1 -// https://github.com/Microsoft/pxt-neopixel/blob/master/sendbuffer.asm + // https://github.com/sandeepmistry/arduino-nRF5/blob/dc53980c8bac27898fca90d8ecb268e11111edc1/cores/nRF5/SDK/components/device/nrf51.h + // http://www.iot-programmer.com/index.php/books/27-micro-bit-iot-in-c/chapters-micro-bit-iot-in-c/47-micro-bit-iot-in-c-fast-memory-mapped-gpio?showall=1 + // https://github.com/Microsoft/pxt-neopixel/blob/master/sendbuffer.asm asm volatile( - // "cpsid i" ; disable irq - - // b .start - "b L%=_start" "\n\t" - // .nextbit: ; C0 - "L%=_nextbit:" "\n\t" //; C0 - // str r1, [r3, #0] ; pin := hi C2 - "strb %[bitmask], [%[reg], #0]" "\n\t" //; pin := hi C2 - // tst r6, r0 ; C3 - "tst %[mask], %[pix]" "\n\t"// ; C3 - // bne .islate ; C4 - "bne L%=_islate" "\n\t" //; C4 - // str r1, [r2, #0] ; pin := lo C6 - "strb %[bitmask], [%[reg], #4]" "\n\t" //; pin := lo C6 - // .islate: - "L%=_islate:" "\n\t" - // lsrs r6, r6, #1 ; r6 >>= 1 C7 - "lsr %[mask], %[mask], #1" "\n\t" //; r6 >>= 1 C7 - // bne .justbit ; C8 - "bne L%=_justbit" "\n\t" //; C8 - - // ; not just a bit - need new byte - // adds r4, #1 ; r4++ C9 - "add %[p], #1" "\n\t" //; r4++ C9 - // subs r5, #1 ; r5-- C10 - "sub %[num], #1" "\n\t" //; r5-- C10 - // bcc .stop ; if (r5<0) goto .stop C11 - "bcc L%=_stop" "\n\t" //; if (r5<0) goto .stop C11 - // .start: - "L%=_start:" - // movs r6, #0x80 ; reset mask C12 - "movs %[mask], #0x80" "\n\t" //; reset mask C12 - // nop ; C13 - "nop" "\n\t" //; C13 - - // .common: ; C13 - "L%=_common:" "\n\t" //; C13 - // str r1, [r2, #0] ; pin := lo C15 - "strb %[bitmask], [%[reg], #4]" "\n\t" //; pin := lo C15 - // ; always re-load byte - it just fits with the cycles better this way - // ldrb r0, [r4, #0] ; r0 := *r4 C17 - "ldrb %[pix], [%[p], #0]" "\n\t" //; r0 := *r4 C17 - // b .nextbit ; C20 - "b L%=_nextbit" "\n\t" //; C20 - - // .justbit: ; C10 - "L%=_justbit:" "\n\t" //; C10 - // ; no nops, branch taken is already 3 cycles - // b .common ; C13 - "b L%=_common" "\n\t" //; C13 - - // .stop: - "L%=_stop:" "\n\t" - // str r1, [r2, #0] ; pin := lo - "strb %[bitmask], [%[reg], #4]" "\n\t" //; pin := lo - // cpsie i ; enable irq - - : [p] "+r" (p), - [pix] "=&r" (pix), - [count] "=&r" (count), - [mask] "=&r" (mask), - [num] "+r" (num) - : [bitmask] "r" (bitmask), - [reg] "r" (reg) - ); + // "cpsid i" ; disable irq + + // b .start + "b L%=_start" + "\n\t" + // .nextbit: ; C0 + "L%=_nextbit:" + "\n\t" //; C0 + // str r1, [r3, #0] ; pin := hi C2 + "strb %[bitmask], [%[reg], #0]" + "\n\t" //; pin := hi C2 + // tst r6, r0 ; C3 + "tst %[mask], %[pix]" + "\n\t" // ; C3 + // bne .islate ; C4 + "bne L%=_islate" + "\n\t" //; C4 + // str r1, [r2, #0] ; pin := lo C6 + "strb %[bitmask], [%[reg], #4]" + "\n\t" //; pin := lo C6 + // .islate: + "L%=_islate:" + "\n\t" + // lsrs r6, r6, #1 ; r6 >>= 1 C7 + "lsr %[mask], %[mask], #1" + "\n\t" //; r6 >>= 1 C7 + // bne .justbit ; C8 + "bne L%=_justbit" + "\n\t" //; C8 + + // ; not just a bit - need new byte + // adds r4, #1 ; r4++ C9 + "add %[p], #1" + "\n\t" //; r4++ C9 + // subs r5, #1 ; r5-- C10 + "sub %[num], #1" + "\n\t" //; r5-- C10 + // bcc .stop ; if (r5<0) goto .stop C11 + "bcc L%=_stop" + "\n\t" //; if (r5<0) goto .stop C11 + // .start: + "L%=_start:" + // movs r6, #0x80 ; reset mask C12 + "movs %[mask], #0x80" + "\n\t" //; reset mask C12 + // nop ; C13 + "nop" + "\n\t" //; C13 + + // .common: ; C13 + "L%=_common:" + "\n\t" //; C13 + // str r1, [r2, #0] ; pin := lo C15 + "strb %[bitmask], [%[reg], #4]" + "\n\t" //; pin := lo C15 + // ; always re-load byte - it just fits with the cycles better this way + // ldrb r0, [r4, #0] ; r0 := *r4 C17 + "ldrb %[pix], [%[p], #0]" + "\n\t" //; r0 := *r4 C17 + // b .nextbit ; C20 + "b L%=_nextbit" + "\n\t" //; C20 + + // .justbit: ; C10 + "L%=_justbit:" + "\n\t" //; C10 + // ; no nops, branch taken is already 3 cycles + // b .common ; C13 + "b L%=_common" + "\n\t" //; C13 + + // .stop: + "L%=_stop:" + "\n\t" + // str r1, [r2, #0] ; pin := lo + "strb %[bitmask], [%[reg], #4]" + "\n\t" //; pin := lo + // cpsie i ; enable irq + + : [p] "+r"(p), [pix] "=&r"(pix), [count] "=&r"(count), [mask] "=&r"(mask), + [num] "+r"(num) + : [bitmask] "r"(bitmask), [reg] "r"(reg)); #elif defined(__SAM3X8E__) // Arduino Due - #define SCALE VARIANT_MCK / 2UL / 1000000UL - #define INST (2UL * F_CPU / VARIANT_MCK) - #define TIME_800_0 ((int)(0.40 * SCALE + 0.5) - (5 * INST)) - #define TIME_800_1 ((int)(0.80 * SCALE + 0.5) - (5 * INST)) - #define PERIOD_800 ((int)(1.25 * SCALE + 0.5) - (5 * INST)) - #define TIME_400_0 ((int)(0.50 * SCALE + 0.5) - (5 * INST)) - #define TIME_400_1 ((int)(1.20 * SCALE + 0.5) - (5 * INST)) - #define PERIOD_400 ((int)(2.50 * SCALE + 0.5) - (5 * INST)) - - int pinMask, time0, time1, period, t; - Pio *port; +#define SCALE VARIANT_MCK / 2UL / 1000000UL +#define INST (2UL * F_CPU / VARIANT_MCK) +#define TIME_800_0 ((int)(0.40 * SCALE + 0.5) - (5 * INST)) +#define TIME_800_1 ((int)(0.80 * SCALE + 0.5) - (5 * INST)) +#define PERIOD_800 ((int)(1.25 * SCALE + 0.5) - (5 * INST)) +#define TIME_400_0 ((int)(0.50 * SCALE + 0.5) - (5 * INST)) +#define TIME_400_1 ((int)(1.20 * SCALE + 0.5) - (5 * INST)) +#define PERIOD_400 ((int)(2.50 * SCALE + 0.5) - (5 * INST)) + + int pinMask, time0, time1, period, t; + Pio *port; volatile WoReg *portSet, *portClear, *timeValue, *timeReset; - uint8_t *p, *end, pix, mask; + uint8_t *p, *end, pix, mask; pmc_set_writeprotect(false); pmc_enable_periph_clk((uint32_t)TC3_IRQn); TC_Configure(TC1, 0, - TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1); + TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1); TC_Start(TC1, 0); - pinMask = g_APinDescription[pin].ulPin; // Don't 'optimize' these into - port = g_APinDescription[pin].pPort; // declarations above. Want to - portSet = &(port->PIO_SODR); // burn a few cycles after - portClear = &(port->PIO_CODR); // starting timer to minimize - timeValue = &(TC1->TC_CHANNEL[0].TC_CV); // the initial 'while'. + pinMask = g_APinDescription[pin].ulPin; // Don't 'optimize' these into + port = g_APinDescription[pin].pPort; // declarations above. Want to + portSet = &(port->PIO_SODR); // burn a few cycles after + portClear = &(port->PIO_CODR); // starting timer to minimize + timeValue = &(TC1->TC_CHANNEL[0].TC_CV); // the initial 'while'. timeReset = &(TC1->TC_CHANNEL[0].TC_CCR); - p = pixels; - end = p + numBytes; - pix = *p++; - mask = 0x80; + p = pixels; + end = p + numBytes; + pix = *p++; + mask = 0x80; #if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled - if(is800KHz) { + if (is800KHz) { #endif - time0 = TIME_800_0; - time1 = TIME_800_1; + time0 = TIME_800_0; + time1 = TIME_800_1; period = PERIOD_800; #if defined(NEO_KHZ400) } else { // 400 KHz bitstream - time0 = TIME_400_0; - time1 = TIME_400_1; + time0 = TIME_400_0; + time1 = TIME_400_1; period = PERIOD_400; } #endif - for(t = time0;; t = time0) { - if(pix & mask) t = time1; - while(*timeValue < (unsigned)period); - *portSet = pinMask; + for (t = time0;; t = time0) { + if (pix & mask) + t = time1; + while (*timeValue < (unsigned)period) + ; + *portSet = pinMask; *timeReset = TC_CCR_CLKEN | TC_CCR_SWTRG; - while(*timeValue < (unsigned)t); + while (*timeValue < (unsigned)t) + ; *portClear = pinMask; - if(!(mask >>= 1)) { // This 'inside-out' loop logic utilizes - if(p >= end) break; // idle time to minimize inter-byte delays. + if (!(mask >>= 1)) { // This 'inside-out' loop logic utilizes + if (p >= end) + break; // idle time to minimize inter-byte delays. pix = *p++; mask = 0x80; } } - while(*timeValue < (unsigned)period); // Wait for last bit + while (*timeValue < (unsigned)period) + ; // Wait for last bit TC_Stop(TC1, 0); #endif // end Due -// END ARM ---------------------------------------------------------------- - + // END ARM ---------------------------------------------------------------- #elif defined(ESP8266) || defined(ESP32) -// ESP8266 ---------------------------------------------------------------- + // ESP8266 ---------------------------------------------------------------- // ESP8266 show() is external to enforce ICACHE_RAM_ATTR execution espShow(pin, pixels, numBytes, is800KHz); @@ -2130,94 +2928,97 @@ void Adafruit_NeoPixel::show(void) { #elif defined(KENDRYTE_K210) k210Show(pin, pixels, numBytes, is800KHz); - -#elif defined(__ARDUINO_ARC__) -// Arduino 101 ----------------------------------------------------------- +#elif defined(__ARDUINO_ARC__) -#define NOPx7 { __builtin_arc_nop(); \ - __builtin_arc_nop(); __builtin_arc_nop(); \ - __builtin_arc_nop(); __builtin_arc_nop(); \ - __builtin_arc_nop(); __builtin_arc_nop(); } + // Arduino 101 ----------------------------------------------------------- + +#define NOPx7 \ + { \ + __builtin_arc_nop(); \ + __builtin_arc_nop(); \ + __builtin_arc_nop(); \ + __builtin_arc_nop(); \ + __builtin_arc_nop(); \ + __builtin_arc_nop(); \ + __builtin_arc_nop(); \ + } PinDescription *pindesc = &g_APinDescription[pin]; - register uint32_t loop = 8 * numBytes; // one loop to handle all bytes and all bits + register uint32_t loop = + 8 * numBytes; // one loop to handle all bytes and all bits register uint8_t *p = pixels; - register uint32_t currByte = (uint32_t) (*p); + register uint32_t currByte = (uint32_t)(*p); register uint32_t currBit = 0x80 & currByte; register uint32_t bitCounter = 0; register uint32_t first = 1; - // The loop is unusual. Very first iteration puts all the way LOW to the wire - - // constant LOW does not affect NEOPIXEL, so there is no visible effect displayed. - // During that very first iteration CPU caches instructions in the loop. - // Because of the caching process, "CPU slows down". NEOPIXEL pulse is very time sensitive - // that's why we let the CPU cache first and we start regular pulse from 2nd iteration + // The loop is unusual. Very first iteration puts all the way LOW to the wire + // - constant LOW does not affect NEOPIXEL, so there is no visible effect + // displayed. During that very first iteration CPU caches instructions in the + // loop. Because of the caching process, "CPU slows down". NEOPIXEL pulse is + // very time sensitive that's why we let the CPU cache first and we start + // regular pulse from 2nd iteration if (pindesc->ulGPIOType == SS_GPIO) { register uint32_t reg = pindesc->ulGPIOBase + SS_GPIO_SWPORTA_DR; uint32_t reg_val = __builtin_arc_lr((volatile uint32_t)reg); register uint32_t reg_bit_high = reg_val | (1 << pindesc->ulGPIOId); - register uint32_t reg_bit_low = reg_val & ~(1 << pindesc->ulGPIOId); + register uint32_t reg_bit_low = reg_val & ~(1 << pindesc->ulGPIOId); loop += 1; // include first, special iteration - while(loop--) { - if(!first) { + while (loop--) { + if (!first) { currByte <<= 1; bitCounter++; } // 1 is >550ns high and >450ns low; 0 is 200..500ns high and >450ns low - __builtin_arc_sr(first ? reg_bit_low : reg_bit_high, (volatile uint32_t)reg); - if(currBit) { // ~400ns HIGH (740ns overall) - NOPx7 - NOPx7 + __builtin_arc_sr(first ? reg_bit_low : reg_bit_high, + (volatile uint32_t)reg); + if (currBit) { // ~400ns HIGH (740ns overall) + NOPx7 NOPx7 } // ~340ns HIGH - NOPx7 - __builtin_arc_nop(); + NOPx7 __builtin_arc_nop(); // 820ns LOW; per spec, max allowed low here is 5000ns */ __builtin_arc_sr(reg_bit_low, (volatile uint32_t)reg); - NOPx7 - NOPx7 + NOPx7 NOPx7 - if(bitCounter >= 8) { + if (bitCounter >= 8) { bitCounter = 0; - currByte = (uint32_t) (*++p); + currByte = (uint32_t)(*++p); } currBit = 0x80 & currByte; first = 0; } - } else if(pindesc->ulGPIOType == SOC_GPIO) { + } else if (pindesc->ulGPIOType == SOC_GPIO) { register uint32_t reg = pindesc->ulGPIOBase + SOC_GPIO_SWPORTA_DR; uint32_t reg_val = MMIO_REG_VAL(reg); register uint32_t reg_bit_high = reg_val | (1 << pindesc->ulGPIOId); - register uint32_t reg_bit_low = reg_val & ~(1 << pindesc->ulGPIOId); + register uint32_t reg_bit_low = reg_val & ~(1 << pindesc->ulGPIOId); loop += 1; // include first, special iteration - while(loop--) { - if(!first) { + while (loop--) { + if (!first) { currByte <<= 1; bitCounter++; } MMIO_REG_VAL(reg) = first ? reg_bit_low : reg_bit_high; - if(currBit) { // ~430ns HIGH (740ns overall) - NOPx7 - NOPx7 - __builtin_arc_nop(); + if (currBit) { // ~430ns HIGH (740ns overall) + NOPx7 NOPx7 __builtin_arc_nop(); } // ~310ns HIGH NOPx7 - // 850ns LOW; per spec, max allowed low here is 5000ns */ - MMIO_REG_VAL(reg) = reg_bit_low; - NOPx7 - NOPx7 + // 850ns LOW; per spec, max allowed low here is 5000ns */ + MMIO_REG_VAL(reg) = reg_bit_low; + NOPx7 NOPx7 - if(bitCounter >= 8) { + if (bitCounter >= 8) { bitCounter = 0; - currByte = (uint32_t) (*++p); + currByte = (uint32_t)(*++p); } currBit = 0x80 & currByte; @@ -2229,10 +3030,9 @@ void Adafruit_NeoPixel::show(void) { #error Architecture not supported #endif + // END ARCHITECTURE SELECT ------------------------------------------------ -// END ARCHITECTURE SELECT ------------------------------------------------ - -#if !( defined(NRF52) || defined(NRF52_SERIES) ) +#if !(defined(NRF52) || defined(NRF52_SERIES) || defined(ESP32)) interrupts(); #endif @@ -2244,15 +3044,16 @@ void Adafruit_NeoPixel::show(void) { if any, is set to INPUT and the new pin is set to OUTPUT. @param p Arduino pin number (-1 = no pin). */ -void Adafruit_NeoPixel::setPin(uint16_t p) { - if(begun && (pin >= 0)) pinMode(pin, INPUT); +void Adafruit_NeoPixel::setPin(int16_t p) { + if (begun && (pin >= 0)) + pinMode(pin, INPUT); // Disable existing out pin pin = p; - if(begun) { + if (begun) { pinMode(p, OUTPUT); digitalWrite(p, LOW); } #if defined(__AVR__) - port = portOutputRegister(digitalPinToPort(p)); + port = portOutputRegister(digitalPinToPort(p)); pinMask = digitalPinToBitMask(p); #endif #if defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ARDUINO_CORE_STM32) @@ -2269,23 +3070,23 @@ void Adafruit_NeoPixel::setPin(uint16_t p) { @param g Green brightness, 0 = minimum (off), 255 = maximum. @param b Blue brightness, 0 = minimum (off), 255 = maximum. */ -void Adafruit_NeoPixel::setPixelColor( - uint16_t n, uint8_t r, uint8_t g, uint8_t b) { +void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint8_t r, uint8_t g, + uint8_t b) { - if(n < numLEDs) { - if(brightness) { // See notes in setBrightness() + if (n < numLEDs) { + if (brightness) { // See notes in setBrightness() r = (r * brightness) >> 8; g = (g * brightness) >> 8; b = (b * brightness) >> 8; } uint8_t *p; - if(wOffset == rOffset) { // Is an RGB-type strip - p = &pixels[n * 3]; // 3 bytes per pixel - } else { // Is a WRGB-type strip - p = &pixels[n * 4]; // 4 bytes per pixel - p[wOffset] = 0; // But only R,G,B passed -- set W to 0 + if (wOffset == rOffset) { // Is an RGB-type strip + p = &pixels[n * 3]; // 3 bytes per pixel + } else { // Is a WRGB-type strip + p = &pixels[n * 4]; // 4 bytes per pixel + p[wOffset] = 0; // But only R,G,B passed -- set W to 0 } - p[rOffset] = r; // R,G,B always stored + p[rOffset] = r; // R,G,B always stored p[gOffset] = g; p[bOffset] = b; } @@ -2301,24 +3102,24 @@ void Adafruit_NeoPixel::setPixelColor( @param w White brightness, 0 = minimum (off), 255 = maximum, ignored if using RGB pixels. */ -void Adafruit_NeoPixel::setPixelColor( - uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w) { +void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint8_t r, uint8_t g, + uint8_t b, uint8_t w) { - if(n < numLEDs) { - if(brightness) { // See notes in setBrightness() + if (n < numLEDs) { + if (brightness) { // See notes in setBrightness() r = (r * brightness) >> 8; g = (g * brightness) >> 8; b = (b * brightness) >> 8; w = (w * brightness) >> 8; } uint8_t *p; - if(wOffset == rOffset) { // Is an RGB-type strip - p = &pixels[n * 3]; // 3 bytes per pixel (ignore W) - } else { // Is a WRGB-type strip - p = &pixels[n * 4]; // 4 bytes per pixel - p[wOffset] = w; // Store W + if (wOffset == rOffset) { // Is an RGB-type strip + p = &pixels[n * 3]; // 3 bytes per pixel (ignore W) + } else { // Is a WRGB-type strip + p = &pixels[n * 4]; // 4 bytes per pixel + p[wOffset] = w; // Store W } - p[rOffset] = r; // Store R,G,B + p[rOffset] = r; // Store R,G,B p[gOffset] = g; p[bOffset] = b; } @@ -2332,17 +3133,14 @@ void Adafruit_NeoPixel::setPixelColor( and least significant byte is blue. */ void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint32_t c) { - if(n < numLEDs) { - uint8_t *p, - r = (uint8_t)(c >> 16), - g = (uint8_t)(c >> 8), - b = (uint8_t)c; - if(brightness) { // See notes in setBrightness() + if (n < numLEDs) { + uint8_t *p, r = (uint8_t)(c >> 16), g = (uint8_t)(c >> 8), b = (uint8_t)c; + if (brightness) { // See notes in setBrightness() r = (r * brightness) >> 8; g = (g * brightness) >> 8; b = (b * brightness) >> 8; } - if(wOffset == rOffset) { + if (wOffset == rOffset) { p = &pixels[n * 3]; } else { p = &pixels[n * 4]; @@ -2369,21 +3167,22 @@ void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint32_t c) { void Adafruit_NeoPixel::fill(uint32_t c, uint16_t first, uint16_t count) { uint16_t i, end; - if(first >= numLEDs) { + if (first >= numLEDs) { return; // If first LED is past end of strip, nothing to do } // Calculate the index ONE AFTER the last pixel to fill - if(count == 0) { + if (count == 0) { // Fill to end of strip end = numLEDs; } else { // Ensure that the loop won't go past the last pixel end = first + count; - if(end > numLEDs) end = numLEDs; + if (end > numLEDs) + end = numLEDs; } - for(i = first; i < end; i++) { + for (i = first; i < end; i++) { this->setPixelColor(i, c); } } @@ -2446,45 +3245,45 @@ uint32_t Adafruit_NeoPixel::ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) { // the constants below are not the multiples of 256 you might expect. // Convert hue to R,G,B (nested ifs faster than divide+mod+switch): - if(hue < 510) { // Red to Green-1 + if (hue < 510) { // Red to Green-1 b = 0; - if(hue < 255) { // Red to Yellow-1 + if (hue < 255) { // Red to Yellow-1 r = 255; - g = hue; // g = 0 to 254 - } else { // Yellow to Green-1 - r = 510 - hue; // r = 255 to 1 + g = hue; // g = 0 to 254 + } else { // Yellow to Green-1 + r = 510 - hue; // r = 255 to 1 g = 255; } - } else if(hue < 1020) { // Green to Blue-1 + } else if (hue < 1020) { // Green to Blue-1 r = 0; - if(hue < 765) { // Green to Cyan-1 + if (hue < 765) { // Green to Cyan-1 g = 255; - b = hue - 510; // b = 0 to 254 - } else { // Cyan to Blue-1 - g = 1020 - hue; // g = 255 to 1 + b = hue - 510; // b = 0 to 254 + } else { // Cyan to Blue-1 + g = 1020 - hue; // g = 255 to 1 b = 255; } - } else if(hue < 1530) { // Blue to Red-1 + } else if (hue < 1530) { // Blue to Red-1 g = 0; - if(hue < 1275) { // Blue to Magenta-1 - r = hue - 1020; // r = 0 to 254 + if (hue < 1275) { // Blue to Magenta-1 + r = hue - 1020; // r = 0 to 254 b = 255; - } else { // Magenta to Red-1 + } else { // Magenta to Red-1 r = 255; - b = 1530 - hue; // b = 255 to 1 + b = 1530 - hue; // b = 255 to 1 } - } else { // Last 0.5 Red (quicker than % operator) + } else { // Last 0.5 Red (quicker than % operator) r = 255; g = b = 0; } // Apply saturation and value to R,G,B, pack into 32-bit result: - uint32_t v1 = 1 + val; // 1 to 256; allows >>8 instead of /255 - uint16_t s1 = 1 + sat; // 1 to 256; same reason - uint8_t s2 = 255 - sat; // 255 to 0 + uint32_t v1 = 1 + val; // 1 to 256; allows >>8 instead of /255 + uint16_t s1 = 1 + sat; // 1 to 256; same reason + uint8_t s2 = 255 - sat; // 255 to 0 return ((((((r * s1) >> 8) + s2) * v1) & 0xff00) << 8) | - (((((g * s1) >> 8) + s2) * v1) & 0xff00) | - ( ((((b * s1) >> 8) + s2) * v1) >> 8); + (((((g * s1) >> 8) + s2) * v1) & 0xff00) | + (((((b * s1) >> 8) + s2) * v1) >> 8); } /*! @@ -2499,44 +3298,41 @@ uint32_t Adafruit_NeoPixel::ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) { This gets more pronounced at lower brightness levels. */ uint32_t Adafruit_NeoPixel::getPixelColor(uint16_t n) const { - if(n >= numLEDs) return 0; // Out of bounds, return no color. + if (n >= numLEDs) + return 0; // Out of bounds, return no color. uint8_t *p; - if(wOffset == rOffset) { // Is RGB-type device + if (wOffset == rOffset) { // Is RGB-type device p = &pixels[n * 3]; - if(brightness) { + if (brightness) { // Stored color was decimated by setBrightness(). Returned value // attempts to scale back to an approximation of the original 24-bit // value used when setting the pixel color, but there will always be // some error -- those bits are simply gone. Issue is most // pronounced at low brightness levels. return (((uint32_t)(p[rOffset] << 8) / brightness) << 16) | - (((uint32_t)(p[gOffset] << 8) / brightness) << 8) | - ( (uint32_t)(p[bOffset] << 8) / brightness ); + (((uint32_t)(p[gOffset] << 8) / brightness) << 8) | + ((uint32_t)(p[bOffset] << 8) / brightness); } else { // No brightness adjustment has been made -- return 'raw' color - return ((uint32_t)p[rOffset] << 16) | - ((uint32_t)p[gOffset] << 8) | - (uint32_t)p[bOffset]; + return ((uint32_t)p[rOffset] << 16) | ((uint32_t)p[gOffset] << 8) | + (uint32_t)p[bOffset]; } - } else { // Is RGBW-type device + } else { // Is RGBW-type device p = &pixels[n * 4]; - if(brightness) { // Return scaled color + if (brightness) { // Return scaled color return (((uint32_t)(p[wOffset] << 8) / brightness) << 24) | (((uint32_t)(p[rOffset] << 8) / brightness) << 16) | - (((uint32_t)(p[gOffset] << 8) / brightness) << 8) | - ( (uint32_t)(p[bOffset] << 8) / brightness ); + (((uint32_t)(p[gOffset] << 8) / brightness) << 8) | + ((uint32_t)(p[bOffset] << 8) / brightness); } else { // Return raw color - return ((uint32_t)p[wOffset] << 24) | - ((uint32_t)p[rOffset] << 16) | - ((uint32_t)p[gOffset] << 8) | - (uint32_t)p[bOffset]; + return ((uint32_t)p[wOffset] << 24) | ((uint32_t)p[rOffset] << 16) | + ((uint32_t)p[gOffset] << 8) | (uint32_t)p[bOffset]; } } } - /*! @brief Adjust output brightness. Does not immediately affect what's currently displayed on the LEDs. The next call to show() will @@ -2560,7 +3356,7 @@ void Adafruit_NeoPixel::setBrightness(uint8_t b) { // (color values are interpreted literally; no scaling), 1 = min // brightness (off), 255 = just below max brightness. uint8_t newBrightness = b + 1; - if(newBrightness != brightness) { // Compare against prior value + if (newBrightness != brightness) { // Compare against prior value // Brightness has changed -- re-scale existing data in RAM, // This process is potentially "lossy," especially when increasing // brightness. The tight timing in the WS2811/WS2812 code means there @@ -2571,15 +3367,17 @@ void Adafruit_NeoPixel::setBrightness(uint8_t b) { // the limited number of steps (quantization) in the old data will be // quite visible in the re-scaled version. For a non-destructive // change, you'll need to re-render the full strip data. C'est la vie. - uint8_t c, - *ptr = pixels, - oldBrightness = brightness - 1; // De-wrap old brightness value + uint8_t c, *ptr = pixels, + oldBrightness = brightness - 1; // De-wrap old brightness value uint16_t scale; - if(oldBrightness == 0) scale = 0; // Avoid /0 - else if(b == 255) scale = 65535 / oldBrightness; - else scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness; - for(uint16_t i=0; i> 8; } brightness = newBrightness; @@ -2590,16 +3388,12 @@ void Adafruit_NeoPixel::setBrightness(uint8_t b) { @brief Retrieve the last-set brightness value for the strip. @return Brightness value: 0 = minimum (off), 255 = maximum. */ -uint8_t Adafruit_NeoPixel::getBrightness(void) const { - return brightness - 1; -} +uint8_t Adafruit_NeoPixel::getBrightness(void) const { return brightness - 1; } /*! @brief Fill the whole NeoPixel strip with 0 / black / off. */ -void Adafruit_NeoPixel::clear(void) { - memset(pixels, 0, numBytes); -} +void Adafruit_NeoPixel::clear(void) { memset(pixels, 0, numBytes); } // A 32-bit variant of gamma8() that applies the same function // to all components of a packed RGB or WRGB value. @@ -2613,6 +3407,68 @@ uint32_t Adafruit_NeoPixel::gamma32(uint32_t x) { // someone's storing information in the unused most significant byte // of an RGB value, but this seems exceedingly rare and if it's // encountered in reality they can mask values going in or coming out. - for(uint8_t i=0; i<4; i++) y[i] = gamma8(y[i]); + for (uint8_t i = 0; i < 4; i++) + y[i] = gamma8(y[i]); return x; // Packed 32-bit return } + +/*! + @brief Fill NeoPixel strip with one or more cycles of hues. + Everyone loves the rainbow swirl so much, now it's canon! + @param first_hue Hue of first pixel, 0-65535, representing one full + cycle of the color wheel. Each subsequent pixel will + be offset to complete one or more cycles over the + length of the strip. + @param reps Number of cycles of the color wheel over the length + of the strip. Default is 1. Negative values can be + used to reverse the hue order. + @param saturation Saturation (optional), 0-255 = gray to pure hue, + default = 255. + @param brightness Brightness/value (optional), 0-255 = off to max, + default = 255. This is distinct and in combination + with any configured global strip brightness. + @param gammify If true (default), apply gamma correction to colors + for better appearance. +*/ +void Adafruit_NeoPixel::rainbow(uint16_t first_hue, int8_t reps, + uint8_t saturation, uint8_t brightness, bool gammify) { + for (uint16_t i=0; i= 100) - #include - #else - #include - #include - #endif +#if (ARDUINO >= 100) +#include +#else +#include +#include +#endif + +#ifdef USE_TINYUSB // For Serial when selecting TinyUSB +#include +#endif + #endif #ifdef TARGET_LPC1768 - #include +#include +#endif + +#if defined(ARDUINO_ARCH_RP2040) +#include +#include "hardware/pio.h" +#include "hardware/clocks.h" +#include "rp2040_pio.h" #endif // The order of primary colors in the NeoPixel data stream can vary among @@ -75,43 +87,43 @@ // 0bRRRRGGBB for RGB // RGB NeoPixel permutations; white and red offsets are always same -// Offset: W R G B -#define NEO_RGB ((0<<6) | (0<<4) | (1<<2) | (2)) ///< Transmit as R,G,B -#define NEO_RBG ((0<<6) | (0<<4) | (2<<2) | (1)) ///< Transmit as R,B,G -#define NEO_GRB ((1<<6) | (1<<4) | (0<<2) | (2)) ///< Transmit as G,R,B -#define NEO_GBR ((2<<6) | (2<<4) | (0<<2) | (1)) ///< Transmit as G,B,R -#define NEO_BRG ((1<<6) | (1<<4) | (2<<2) | (0)) ///< Transmit as B,R,G -#define NEO_BGR ((2<<6) | (2<<4) | (1<<2) | (0)) ///< Transmit as B,G,R +// Offset: W R G B +#define NEO_RGB ((0 << 6) | (0 << 4) | (1 << 2) | (2)) ///< Transmit as R,G,B +#define NEO_RBG ((0 << 6) | (0 << 4) | (2 << 2) | (1)) ///< Transmit as R,B,G +#define NEO_GRB ((1 << 6) | (1 << 4) | (0 << 2) | (2)) ///< Transmit as G,R,B +#define NEO_GBR ((2 << 6) | (2 << 4) | (0 << 2) | (1)) ///< Transmit as G,B,R +#define NEO_BRG ((1 << 6) | (1 << 4) | (2 << 2) | (0)) ///< Transmit as B,R,G +#define NEO_BGR ((2 << 6) | (2 << 4) | (1 << 2) | (0)) ///< Transmit as B,G,R // RGBW NeoPixel permutations; all 4 offsets are distinct // Offset: W R G B -#define NEO_WRGB ((0<<6) | (1<<4) | (2<<2) | (3)) ///< Transmit as W,R,G,B -#define NEO_WRBG ((0<<6) | (1<<4) | (3<<2) | (2)) ///< Transmit as W,R,B,G -#define NEO_WGRB ((0<<6) | (2<<4) | (1<<2) | (3)) ///< Transmit as W,G,R,B -#define NEO_WGBR ((0<<6) | (3<<4) | (1<<2) | (2)) ///< Transmit as W,G,B,R -#define NEO_WBRG ((0<<6) | (2<<4) | (3<<2) | (1)) ///< Transmit as W,B,R,G -#define NEO_WBGR ((0<<6) | (3<<4) | (2<<2) | (1)) ///< Transmit as W,B,G,R +#define NEO_WRGB ((0 << 6) | (1 << 4) | (2 << 2) | (3)) ///< Transmit as W,R,G,B +#define NEO_WRBG ((0 << 6) | (1 << 4) | (3 << 2) | (2)) ///< Transmit as W,R,B,G +#define NEO_WGRB ((0 << 6) | (2 << 4) | (1 << 2) | (3)) ///< Transmit as W,G,R,B +#define NEO_WGBR ((0 << 6) | (3 << 4) | (1 << 2) | (2)) ///< Transmit as W,G,B,R +#define NEO_WBRG ((0 << 6) | (2 << 4) | (3 << 2) | (1)) ///< Transmit as W,B,R,G +#define NEO_WBGR ((0 << 6) | (3 << 4) | (2 << 2) | (1)) ///< Transmit as W,B,G,R -#define NEO_RWGB ((1<<6) | (0<<4) | (2<<2) | (3)) ///< Transmit as R,W,G,B -#define NEO_RWBG ((1<<6) | (0<<4) | (3<<2) | (2)) ///< Transmit as R,W,B,G -#define NEO_RGWB ((2<<6) | (0<<4) | (1<<2) | (3)) ///< Transmit as R,G,W,B -#define NEO_RGBW ((3<<6) | (0<<4) | (1<<2) | (2)) ///< Transmit as R,G,B,W -#define NEO_RBWG ((2<<6) | (0<<4) | (3<<2) | (1)) ///< Transmit as R,B,W,G -#define NEO_RBGW ((3<<6) | (0<<4) | (2<<2) | (1)) ///< Transmit as R,B,G,W +#define NEO_RWGB ((1 << 6) | (0 << 4) | (2 << 2) | (3)) ///< Transmit as R,W,G,B +#define NEO_RWBG ((1 << 6) | (0 << 4) | (3 << 2) | (2)) ///< Transmit as R,W,B,G +#define NEO_RGWB ((2 << 6) | (0 << 4) | (1 << 2) | (3)) ///< Transmit as R,G,W,B +#define NEO_RGBW ((3 << 6) | (0 << 4) | (1 << 2) | (2)) ///< Transmit as R,G,B,W +#define NEO_RBWG ((2 << 6) | (0 << 4) | (3 << 2) | (1)) ///< Transmit as R,B,W,G +#define NEO_RBGW ((3 << 6) | (0 << 4) | (2 << 2) | (1)) ///< Transmit as R,B,G,W -#define NEO_GWRB ((1<<6) | (2<<4) | (0<<2) | (3)) ///< Transmit as G,W,R,B -#define NEO_GWBR ((1<<6) | (3<<4) | (0<<2) | (2)) ///< Transmit as G,W,B,R -#define NEO_GRWB ((2<<6) | (1<<4) | (0<<2) | (3)) ///< Transmit as G,R,W,B -#define NEO_GRBW ((3<<6) | (1<<4) | (0<<2) | (2)) ///< Transmit as G,R,B,W -#define NEO_GBWR ((2<<6) | (3<<4) | (0<<2) | (1)) ///< Transmit as G,B,W,R -#define NEO_GBRW ((3<<6) | (2<<4) | (0<<2) | (1)) ///< Transmit as G,B,R,W +#define NEO_GWRB ((1 << 6) | (2 << 4) | (0 << 2) | (3)) ///< Transmit as G,W,R,B +#define NEO_GWBR ((1 << 6) | (3 << 4) | (0 << 2) | (2)) ///< Transmit as G,W,B,R +#define NEO_GRWB ((2 << 6) | (1 << 4) | (0 << 2) | (3)) ///< Transmit as G,R,W,B +#define NEO_GRBW ((3 << 6) | (1 << 4) | (0 << 2) | (2)) ///< Transmit as G,R,B,W +#define NEO_GBWR ((2 << 6) | (3 << 4) | (0 << 2) | (1)) ///< Transmit as G,B,W,R +#define NEO_GBRW ((3 << 6) | (2 << 4) | (0 << 2) | (1)) ///< Transmit as G,B,R,W -#define NEO_BWRG ((1<<6) | (2<<4) | (3<<2) | (0)) ///< Transmit as B,W,R,G -#define NEO_BWGR ((1<<6) | (3<<4) | (2<<2) | (0)) ///< Transmit as B,W,G,R -#define NEO_BRWG ((2<<6) | (1<<4) | (3<<2) | (0)) ///< Transmit as B,R,W,G -#define NEO_BRGW ((3<<6) | (1<<4) | (2<<2) | (0)) ///< Transmit as B,R,G,W -#define NEO_BGWR ((2<<6) | (3<<4) | (1<<2) | (0)) ///< Transmit as B,G,W,R -#define NEO_BGRW ((3<<6) | (2<<4) | (1<<2) | (0)) ///< Transmit as B,G,R,W +#define NEO_BWRG ((1 << 6) | (2 << 4) | (3 << 2) | (0)) ///< Transmit as B,W,R,G +#define NEO_BWGR ((1 << 6) | (3 << 4) | (2 << 2) | (0)) ///< Transmit as B,W,G,R +#define NEO_BRWG ((2 << 6) | (1 << 4) | (3 << 2) | (0)) ///< Transmit as B,R,W,G +#define NEO_BRGW ((3 << 6) | (1 << 4) | (2 << 2) | (0)) ///< Transmit as B,R,G,W +#define NEO_BGWR ((2 << 6) | (3 << 4) | (1 << 2) | (0)) ///< Transmit as B,G,W,R +#define NEO_BGRW ((3 << 6) | (2 << 4) | (1 << 2) | (0)) ///< Transmit as B,G,R,W // Add NEO_KHZ400 to the color order value to indicate a 400 KHz device. // All but the earliest v1 NeoPixels expect an 800 KHz data stream, this is @@ -134,7 +146,7 @@ #ifdef NEO_KHZ400 typedef uint16_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor #else -typedef uint8_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor +typedef uint8_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor #endif // These two tables are declared outside the Adafruit_NeoPixel class @@ -149,22 +161,24 @@ for x in range(256): if x&15 == 15: print */ static const uint8_t PROGMEM _NeoPixelSineTable[256] = { - 128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173, - 176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215, - 218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244, - 245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255, - 255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246, - 245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220, - 218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179, - 176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131, - 128,124,121,118,115,112,109,106,103,100, 97, 93, 90, 88, 85, 82, - 79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, - 37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, - 10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, - 10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, - 37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76, - 79, 82, 85, 88, 90, 93, 97,100,103,106,109,112,115,118,121,124}; + 128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162, 165, 167, 170, + 173, 176, 179, 182, 185, 188, 190, 193, 196, 198, 201, 203, 206, 208, 211, + 213, 215, 218, 220, 222, 224, 226, 228, 230, 232, 234, 235, 237, 238, 240, + 241, 243, 244, 245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254, + 254, 255, 255, 255, 255, 255, 255, 255, 254, 254, 254, 253, 253, 252, 251, + 250, 250, 249, 248, 246, 245, 244, 243, 241, 240, 238, 237, 235, 234, 232, + 230, 228, 226, 224, 222, 220, 218, 215, 213, 211, 208, 206, 203, 201, 198, + 196, 193, 190, 188, 185, 182, 179, 176, 173, 170, 167, 165, 162, 158, 155, + 152, 149, 146, 143, 140, 137, 134, 131, 128, 124, 121, 118, 115, 112, 109, + 106, 103, 100, 97, 93, 90, 88, 85, 82, 79, 76, 73, 70, 67, 65, + 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, 37, 35, 33, 31, 29, + 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, 10, 9, 7, 6, + 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, 10, 11, + 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, 37, + 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76, + 79, 82, 85, 88, 90, 93, 97, 100, 103, 106, 109, 112, 115, 118, 121, + 124}; /* Similar to above, but for an 8-bit gamma-correction table. Copy & paste this snippet into a Python REPL to regenerate: @@ -175,49 +189,49 @@ for x in range(256): if x&15 == 15: print */ static const uint8_t PROGMEM _NeoPixelGammaTable[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, - 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, - 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, - 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, - 20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29, - 30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42, - 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - 58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, 70, 71, 72, 73, 75, - 76, 77, 78, 80, 81, 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96, - 97, 99,100,102,103,105,106,108,109,111,112,114,115,117,119,120, - 122,124,125,127,129,130,132,134,136,137,139,141,143,145,146,148, - 150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180, - 182,184,186,188,191,193,195,197,199,202,204,206,209,211,213,215, - 218,220,223,225,227,230,232,235,237,240,242,245,247,250,252,255}; + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, + 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, + 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, + 25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34, 34, 35, + 36, 37, 38, 38, 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, 80, 81, + 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 97, 99, 100, 102, + 103, 105, 106, 108, 109, 111, 112, 114, 115, 117, 119, 120, 122, 124, 125, + 127, 129, 130, 132, 134, 136, 137, 139, 141, 143, 145, 146, 148, 150, 152, + 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, + 184, 186, 188, 191, 193, 195, 197, 199, 202, 204, 206, 209, 211, 213, 215, + 218, 220, 223, 225, 227, 230, 232, 235, 237, 240, 242, 245, 247, 250, 252, + 255}; -/*! +/*! @brief Class that stores state and functions for interacting with Adafruit NeoPixels and compatible devices. */ class Adafruit_NeoPixel { - public: - +public: // Constructor: number of LEDs, pin number, LED type - Adafruit_NeoPixel(uint16_t n, uint16_t pin=6, - neoPixelType type=NEO_GRB + NEO_KHZ800); + Adafruit_NeoPixel(uint16_t n, int16_t pin = 6, + neoPixelType type = NEO_GRB + NEO_KHZ800); Adafruit_NeoPixel(void); ~Adafruit_NeoPixel(); - void begin(void); - void show(void); - void setPin(uint16_t p); - void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b); - void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, - uint8_t w); - void setPixelColor(uint16_t n, uint32_t c); - void fill(uint32_t c=0, uint16_t first=0, uint16_t count=0); - void setBrightness(uint8_t); - void clear(void); - void updateLength(uint16_t n); - void updateType(neoPixelType t); + void begin(void); + void show(void); + void setPin(int16_t p); + void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b); + void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w); + void setPixelColor(uint16_t n, uint32_t c); + void fill(uint32_t c = 0, uint16_t first = 0, uint16_t count = 0); + void setBrightness(uint8_t); + void clear(void); + void updateLength(uint16_t n); + void updateType(neoPixelType t); /*! @brief Check whether a call to show() will start sending data immediately or will 'block' for a required interval. NeoPixels @@ -232,10 +246,26 @@ class Adafruit_NeoPixel { if show() would block (meaning some idle time is available). */ bool canShow(void) { - if (endTime > micros()) { - endTime = micros(); + // It's normal and possible for endTime to exceed micros() if the + // 32-bit clock counter has rolled over (about every 70 minutes). + // Since both are uint32_t, a negative delta correctly maps back to + // positive space, and it would seem like the subtraction below would + // suffice. But a problem arises if code invokes show() very + // infrequently...the micros() counter may roll over MULTIPLE times in + // that interval, the delta calculation is no longer correct and the + // next update may stall for a very long time. The check below resets + // the latch counter if a rollover has occurred. This can cause an + // extra delay of up to 300 microseconds in the rare case where a + // show() call happens precisely around the rollover, but that's + // neither likely nor especially harmful, vs. other code that might + // stall for 30+ minutes, or having to document and frequently remind + // and/or provide tech support explaining an unintuitive need for + // show() calls at least once an hour. + uint32_t now = micros(); + if (endTime > now) { + endTime = now; } - return (micros() - endTime) >= 300L; + return (now - endTime) >= 300L; } /*! @brief Get a pointer directly to the NeoPixel data buffer in RAM. @@ -251,19 +281,19 @@ class Adafruit_NeoPixel { writes past the ends of the buffer. Great power, great responsibility and all that. */ - uint8_t *getPixels(void) const { return pixels; }; - uint8_t getBrightness(void) const; + uint8_t *getPixels(void) const { return pixels; }; + uint8_t getBrightness(void) const; /*! @brief Retrieve the pin number used for NeoPixel data output. @return Arduino pin number (-1 if not set). */ - int16_t getPin(void) const { return pin; }; + int16_t getPin(void) const { return pin; }; /*! @brief Return the number of pixels in an Adafruit_NeoPixel strip object. @return Pixel count (0 if not set). */ - uint16_t numPixels(void) const { return numLEDs; } - uint32_t getPixelColor(uint16_t n) const; + uint16_t numPixels(void) const { return numLEDs; } + uint32_t getPixelColor(uint16_t n) const; /*! @brief An 8-bit integer sine wave function, not directly compatible with standard trigonometric units like radians or degrees. @@ -276,7 +306,7 @@ class Adafruit_NeoPixel { a signed int8_t, but you'll most likely want unsigned as this output is often used for pixel brightness in animation effects. */ - static uint8_t sine8(uint8_t x) { + static uint8_t sine8(uint8_t x) { return pgm_read_byte(&_NeoPixelSineTable[x]); // 0-255 in, 0-255 out } /*! @@ -290,7 +320,7 @@ class Adafruit_NeoPixel { NeoPixels in average tasks. If you need finer control you'll need to provide your own gamma-correction function instead. */ - static uint8_t gamma8(uint8_t x) { + static uint8_t gamma8(uint8_t x) { return pgm_read_byte(&_NeoPixelGammaTable[x]); // 0-255 in, 0-255 out } /*! @@ -304,8 +334,8 @@ class Adafruit_NeoPixel { function. Packed RGB format is predictable, regardless of LED strand color order. */ - static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) { - return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; + static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) { + return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; } /*! @brief Convert separate red, green, blue and white values into a @@ -319,10 +349,10 @@ class Adafruit_NeoPixel { function. Packed WRGB format is predictable, regardless of LED strand color order. */ - static uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { - return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; + static uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { + return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; } - static uint32_t ColorHSV(uint16_t hue, uint8_t sat=255, uint8_t val=255); + static uint32_t ColorHSV(uint16_t hue, uint8_t sat = 255, uint8_t val = 255); /*! @brief A gamma-correction function for 32-bit packed RGB or WRGB colors. Makes color transitions appear more perceptially @@ -335,32 +365,48 @@ class Adafruit_NeoPixel { control you'll need to provide your own gamma-correction function instead. */ - static uint32_t gamma32(uint32_t x); + static uint32_t gamma32(uint32_t x); + + void rainbow(uint16_t first_hue = 0, int8_t reps = 1, + uint8_t saturation = 255, uint8_t brightness = 255, + bool gammify = true); - protected: + static neoPixelType str2order(const char *v); -#ifdef NEO_KHZ400 // If 400 KHz NeoPixel support enabled... - bool is800KHz; ///< true if 800 KHz pixels +private: +#if defined(ARDUINO_ARCH_RP2040) + void rp2040Init(uint8_t pin, bool is800KHz); + void rp2040Show(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz); #endif - bool begun; ///< true if begin() previously called - uint16_t numLEDs; ///< Number of RGB LEDs in strip - uint16_t numBytes; ///< Size of 'pixels' buffer below - int16_t pin; ///< Output pin number (-1 if not yet set) - uint8_t brightness; ///< Strip brightness 0-255 (stored as +1) - uint8_t *pixels; ///< Holds LED color values (3 or 4 bytes each) - uint8_t rOffset; ///< Red index within each 3- or 4-byte pixel - uint8_t gOffset; ///< Index of green byte - uint8_t bOffset; ///< Index of blue byte - uint8_t wOffset; ///< Index of white (==rOffset if no white) - uint32_t endTime; ///< Latch timing reference + +protected: +#ifdef NEO_KHZ400 // If 400 KHz NeoPixel support enabled... + bool is800KHz; ///< true if 800 KHz pixels +#endif + bool begun; ///< true if begin() previously called + uint16_t numLEDs; ///< Number of RGB LEDs in strip + uint16_t numBytes; ///< Size of 'pixels' buffer below + int16_t pin; ///< Output pin number (-1 if not yet set) + uint8_t brightness; ///< Strip brightness 0-255 (stored as +1) + uint8_t *pixels; ///< Holds LED color values (3 or 4 bytes each) + uint8_t rOffset; ///< Red index within each 3- or 4-byte pixel + uint8_t gOffset; ///< Index of green byte + uint8_t bOffset; ///< Index of blue byte + uint8_t wOffset; ///< Index of white (==rOffset if no white) + uint32_t endTime; ///< Latch timing reference #ifdef __AVR__ - volatile uint8_t *port; ///< Output PORT register - uint8_t pinMask; ///< Output PORT bitmask + volatile uint8_t *port; ///< Output PORT register + uint8_t pinMask; ///< Output PORT bitmask #endif #if defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ARDUINO_CORE_STM32) - GPIO_TypeDef *gpioPort; ///< Output GPIO PORT - uint32_t gpioPin; ///< Output GPIO PIN + GPIO_TypeDef *gpioPort; ///< Output GPIO PORT + uint32_t gpioPin; ///< Output GPIO PIN +#endif +#if defined(ARDUINO_ARCH_RP2040) + PIO pio = pio0; + int sm = 0; + bool init = true; #endif }; -#endif // ADAFRUIT_NEOPIXEL_H +#endif // ADAFRUIT_NEOPIXEL_H \ No newline at end of file diff --git a/src/main/resources/resource/Arduino/MrlComm/MrlComm.ino b/src/main/resources/resource/Arduino/MrlComm/MrlComm.ino index dbe6cd8e6f..7568dd625f 100644 --- a/src/main/resources/resource/Arduino/MrlComm/MrlComm.ino +++ b/src/main/resources/resource/Arduino/MrlComm/MrlComm.ino @@ -56,6 +56,8 @@ #include #endif +#define SERIAL_BUFFER_SIZE 256 + /*********************************************************************** * GLOBAL VARIABLES * TODO - work on reducing globals and pass as parameters diff --git a/src/main/resources/resource/Arduino/MrlComm/MrlNeopixel.cpp b/src/main/resources/resource/Arduino/MrlComm/MrlNeopixel.cpp index 4371f6a1f1..380e9d07f0 100755 --- a/src/main/resources/resource/Arduino/MrlComm/MrlNeopixel.cpp +++ b/src/main/resources/resource/Arduino/MrlComm/MrlNeopixel.cpp @@ -60,7 +60,7 @@ void MrlNeopixel::colorWipe() void MrlNeopixel::scanner() { - if (y == strip->numPixels() - 1) + if (y >= strip->numPixels() - 1) { z = 0; } @@ -158,7 +158,7 @@ void MrlNeopixel::theaterChaseRainbow() void MrlNeopixel::writeMatrix(byte bufferSize, const byte *buffer) { - if (!strip) + if (!strip || runAnimation) { return; } diff --git a/src/main/resources/resource/Py4j/Py4j.py b/src/main/resources/resource/Py4j/Py4j.py index 3c75cd94c5..271885c564 100644 --- a/src/main/resources/resource/Py4j/Py4j.py +++ b/src/main/resources/resource/Py4j/Py4j.py @@ -18,6 +18,10 @@ from py4j.java_collections import JavaObject, JavaClass from py4j.java_gateway import JavaGateway, CallbackServerParameters, GatewayParameters +# REQUIRED TO SET FIELDS ! +# from py4j.java_gateway import set_field + +# rename java_object proxy or service? class Service(ABC): def __init__(self, name): @@ -31,9 +35,9 @@ def __str__(self): # Delegate string representation to the underlying Java object return str(self.java_object) - def subscribe(self, event): + def subscribe(self, topic, method): print("subscribe") - self.java_object.subscribe(event) + self.java_object.subscribe(topic, method) @abstractmethod def getType(self): @@ -54,7 +58,7 @@ def onFlash(self): class InMoov2(Service): def __init__(self, name): super().__init__(name) - self.subscribe('onStateChange') + self.subscribe(name, 'onStateChange') def getType(self): return "InMoov2" @@ -104,7 +108,7 @@ def __init__(self): sys.stderr = self self.gateway = JavaGateway(callback_server_parameters=CallbackServerParameters(), python_server_entry_point=self, - gateway_parameters=GatewayParameters(auto_convert=True)) + gateway_parameters=GatewayParameters(auto_convert=True, auto_field=True)) self.runtime = self.gateway.jvm.org.myrobotlab.service.Runtime.getInstance() # FIXME - REMOVE THIS - DO NOT SET ANY GLOBALS !!!! runtime = self.runtime @@ -113,6 +117,7 @@ def __init__(self): def construct_runtime(self): """ + FIXME - remove this method Constructs a new Runtime instance and returns it. """ jvm_runtime = self.gateway.jvm.org.myrobotlab.service.Runtime.getInstance() diff --git a/src/main/resources/resource/WebGui/app/.eslintrc.js b/src/main/resources/resource/WebGui/app/.eslintrc.js new file mode 100644 index 0000000000..fced5bb66e --- /dev/null +++ b/src/main/resources/resource/WebGui/app/.eslintrc.js @@ -0,0 +1,79 @@ +const path = require("path") + +module.exports = { + env: { + browser: true, + es2021: true, + amd: true, + node: true, + jest: true, + }, + extends: [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:@tanstack/eslint-plugin-query/recommended", + "plugin:testing-library/react", + "plugin:jest-dom/recommended", + ], + ignorePatterns: ["src/models/", "src/target/"], + overrides: [ + { + files: ["*.ts", "*.tsx"], + parser: "@typescript-eslint/parser", + extends: [ + "standard-with-typescript", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking", + ], + parserOptions: { + project: [path.resolve(__dirname, "./tsconfig.json"), path.resolve(__dirname, "./tsconfig.vite.json")], + }, + rules: { + quotes: "off", + "@typescript-eslint/quotes": "off", + "@typescript-eslint/comma-dangle": "off", + "@typescript-eslint/triple-slash-reference": "warn", + "@typescript-eslint/space-before-function-paren": "warn", + "@typescript-eslint/explicit-function-return-type": "warn", + "@typescript-eslint/no-namespace": "warn", + "@typescript-eslint/member-delimiter-style": "off", + }, + }, + ], + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + }, + plugins: ["react", "@tanstack/query", "testing-library", "jest-dom"], + rules: { + "react/display-name": "warn", + "react/prop-types": "off", // switch to typescript instead + "no-unused-vars": "warn", + "react/jsx-key": "warn", + "no-undef": "error", + "no-unsafe-optional-chaining": "warn", + "no-empty-pattern": "warn", + "no-fallthrough": "warn", + "no-case-declarations": "warn", + "no-redeclare": "warn", + "no-prototype-builtins": "warn", + "no-useless-escape": "warn", + "react/no-unescaped-entities": "warn", + "react/jsx-no-target-blank": "warn", + "react/jsx-no-undef": "error", + "react/no-unused-prop-types": "warn", + "react/no-unused-state": "warn", + "react/no-unsafe": "warn", + "react/react-in-jsx-scope": "off", + "no-restricted-syntax": "error", + quotes: "off", + "@typescript-eslint/quotes": "off", + "multiline-ternary": "off", + "testing-library/no-wait-for-multiple-assertions": "warn", + "testing-library/prefer-screen-queries": "warn", + "testing-library/no-node-access": "warn", + "testing-library/no-unnecessary-act": "warn", + "testing-library/no-wait-for-side-effects": "warn", + "testing-library/await-async-utils": "warn", + }, +} diff --git a/src/main/resources/resource/WebGui/app/.prettierrc b/src/main/resources/resource/WebGui/app/.prettierrc new file mode 100644 index 0000000000..1a9b410a7f --- /dev/null +++ b/src/main/resources/resource/WebGui/app/.prettierrc @@ -0,0 +1,6 @@ +{ + "tabWidth": 2, + "useTabs": false, + "printWidth": 120, + "semi": false +} diff --git a/src/main/resources/resource/WebGui/app/mrl.js b/src/main/resources/resource/WebGui/app/mrl.js index 81cfef0b0e..93594a09c5 100644 --- a/src/main/resources/resource/WebGui/app/mrl.js +++ b/src/main/resources/resource/WebGui/app/mrl.js @@ -323,11 +323,20 @@ angular.module('mrlapp.mrl', []).provider('mrl', [function() { let simpleTypeName = _self.getSimpleName(registration.typeKey) + // FIXME - currently handles all unknown types through kludgy test + if (!simpleTypeName || simpleTypeName.includes(':') || simpleTypeName == 'Unknown'){ + simpleTypeName = "Unknown"; + registration.typeKey = "Unknown" + } + serviceTypes[simpleTypeName] = registration.typeKey // initial de-serialization of state let service = JSON.parse(registration.state) registry[fullname] = service + if (simpleTypeName == "Unknown"){ + service.simpleName = "Unknown" + } // now add a panel - with the function it registered // _self.addServicePanel(service) @@ -558,6 +567,10 @@ angular.module('mrlapp.mrl', []).provider('mrl', [function() { } } else { + if (!service.name){ + console.error('uh oh') + } + if (service.name.includes('@')) { return service.name } else { @@ -960,7 +973,7 @@ angular.module('mrlapp.mrl', []).provider('mrl', [function() { let createPanel = function(fullname, type, x, y, width, height, zIndex, data) { let displayName = fullname.endsWith(_self.remoteId)?_self.getShortName(fullname):fullname - console.error('createPanel', _self.remoteId, displayName) + console.info('createPanel', _self.remoteId, displayName) let panel = { simpleName: _self.getSimpleName(type), name: fullname, @@ -1198,6 +1211,7 @@ angular.module('mrlapp.mrl', []).provider('mrl', [function() { if (!tabsViewCtrl || !_self.getService(serviceName)) { console.error('tabsViewCtrl is null - cannot changeTab') } else { + console.info("changeTab !", serviceName) tabsViewCtrl.changeTab(serviceName) history.push(serviceName) } @@ -1235,6 +1249,7 @@ angular.module('mrlapp.mrl', []).provider('mrl', [function() { var deferred = $q.defer() if (!msgInterfaces.hasOwnProperty(name)) { //console.log(name + ' getMsgInterface ') + msgInterfaces[name] = { "name": name, "temp": {}, @@ -1409,6 +1424,7 @@ angular.module('mrlapp.mrl', []).provider('mrl', [function() { // - name + '@' + _self.id) _self.subscribeToServiceMethod(msgInterfaces[name].onMsg, name, 'getMethodMap') msgInterfaces[name].getMethodMap() + // deferred.resolve("yay") return deferred.promise }, getPlatform: function() { diff --git a/src/main/resources/resource/WebGui/app/service/js/MockGatewayGui.js b/src/main/resources/resource/WebGui/app/service/js/MockGatewayGui.js index 16bfef1ef3..572b992e91 100644 --- a/src/main/resources/resource/WebGui/app/service/js/MockGatewayGui.js +++ b/src/main/resources/resource/WebGui/app/service/js/MockGatewayGui.js @@ -1,39 +1,42 @@ -angular.module("mrlapp.service.MockGatewayGui", []).controller("MockGatewayGuiCtrl", ["$scope", "mrl", function($scope, mrl) { +angular.module("mrlapp.service.MockGatewayGui", []).controller("MockGatewayGuiCtrl", [ + "$scope", + "mrl", + function ($scope, mrl) { console.info("MockGatewayGuiCtrl") var _self = this var msg = this.msg // GOOD TEMPLATE TO FOLLOW - this.updateState = function(service) { - $scope.service = service + this.updateState = function (service) { + $scope.service = service } - $scope.msgToRemote = function(msg) { - if ($scope.service) { - return msg.name.endsWith($scope.service.remoteId) - } else { - return false - } + $scope.msgToRemote = function (msg) { + if ($scope.service) { + return msg.name.endsWith($scope.service.remoteId) + } else { + return false + } } - this.onMsg = function(inMsg) { - let data = inMsg.data[0] - switch (inMsg.method) { + this.onMsg = function (inMsg) { + let data = inMsg.data[0] + switch (inMsg.method) { case "onState": - _self.updateState(data) - $scope.$apply() - break + _self.updateState(data) + $scope.$apply() + break case "onMessageEvent": - $scope.service.msgs.push(data) - $scope.$apply() - break + $scope.service.msgs.push(data) + $scope.$apply() + break default: - console.error("ERROR - unhandled method " + $scope.name + " " + inMsg.method) - break - } + console.error("ERROR - unhandled method " + $scope.name + " " + inMsg.method) + break + } } msg.subscribe("publishMessageEvent") msg.subscribe(this) -} -, ]) + }, +]) 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 38211728ff..0e34121c1c 100644 --- a/src/main/resources/resource/WebGui/app/service/js/ProgramABGui.js +++ b/src/main/resources/resource/WebGui/app/service/js/ProgramABGui.js @@ -1,6 +1,12 @@ -angular.module('mrlapp.service.ProgramABGui', []).controller('ProgramABGuiCtrl', ['$scope', '$compile', 'mrl', '$uibModal', '$sce', function($scope, $compile, mrl, $uibModal, $sce) { +angular.module("mrlapp.service.ProgramABGui", []).controller("ProgramABGuiCtrl", [ + "$scope", + "$compile", + "mrl", + "$uibModal", + "$sce", + function ($scope, $compile, mrl, $uibModal, $sce) { // $modal ???? - console.info('ProgramABGuiCtrl') + console.info("ProgramABGuiCtrl") // grab the self and message var _self = this var startDialog = null @@ -8,8 +14,8 @@ angular.module('mrlapp.service.ProgramABGui', []).controller('ProgramABGuiCtrl', // use $scope only when the variable // needs to interract with the display - $scope.currentUserName = '' - $scope.utterance = '' + $scope.currentUserName = "" + $scope.utterance = "" $scope.currentSessionKey = null $scope.status = null $scope.predicates = [] @@ -18,10 +24,10 @@ angular.module('mrlapp.service.ProgramABGui', []).controller('ProgramABGuiCtrl', $scope.aimlEditor = null $scope.tabs = { - "selected": 1 + selected: 1, } $scope.tabsRight = { - "selected": 1 + selected: 1, } // active tab index @@ -29,7 +35,7 @@ angular.module('mrlapp.service.ProgramABGui', []).controller('ProgramABGuiCtrl', $scope.aimlFile = "blah \n blah " $scope.aimlFileData = { - "data": "HELLO THERE !!!" + data: "HELLO THERE !!!", } $scope.lastResponse @@ -38,7 +44,7 @@ angular.module('mrlapp.service.ProgramABGui', []).controller('ProgramABGuiCtrl', // be put in an object to be effectively modified // when $sope is used $scope.edit = { - properties: false + properties: false, } $scope.chatLog = [] @@ -47,269 +53,263 @@ angular.module('mrlapp.service.ProgramABGui', []).controller('ProgramABGuiCtrl', $scope.log = [] // following the template. - this.updateState = function(service) { - // 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.config.currentUserName - $scope.service = service - $scope.currentSessionKey = $scope.getCurrentSessionKey() - - /* + this.updateState = function (service) { + // 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.config.username + $scope.service = service + $scope.currentSessionKey = $scope.getCurrentSessionKey() + + /* for (let bot in $scope.service.sessions){ for (let username in $scope.service.sessions[bot]){ console.info(username) } } */ - } - this.onMsg = function(inMsg) { - // console.info("ProgramABGui.onMsg(" + inMsg.method + ')') - let data = inMsg.data[0] - - switch (inMsg.method) { - - case 'onStatus': - $scope.status = data; - $scope.$apply() - break - - case 'onBotImage': - $scope.currentBotImage = data - $scope.$apply() - break - - case 'onState': - _self.updateState(data) - $scope.$apply() - break - - case 'onTopic': - $scope.service.currentTopic = data - $scope.$apply() - break - - case 'onAimlFile': - $scope.aimlFileData.data = data - $scope.$apply() - break - - case 'onPredicates': - $scope.predicates = data - $scope.$apply() - break - - case 'onPredicate': - $scope.predicates[data.name] = data.value - $scope.$apply() - break - - - case 'onRequest': - var textData = data - $scope.chatLog.unshift({ - type: 'User', - name: $scope.currentUserName, - text: $sce.trustAsHtml(textData) - }) - console.info('onRequest', textData) - $scope.$apply() - break - case 'onResponse': - var textData = data - $scope.chatLog.unshift({ - type: 'Bot', - name: $scope.service.config.currentBotName, - text: $sce.trustAsHtml(data.msg) - }) - $scope.lastResponse = textData - $scope.$apply() - break - case 'onLog': - var textData = data - let filename = null - parts = textData.split(" ") - if (parts.length > 2 && parts[1] == "Matched:") { - filename = parts[parts.length - 1] - textData = textData.substr(0, textData.lastIndexOf(' ') + 1) - // pos0 = textData.lastIndexOf(' ') + 1 - // url = textData.substr(0,pos0) + '' - // url = textData.substr(0,pos0) + '' - // textData = url - } - - $scope.log.unshift({ - 'name': '', - 'text': textData, - 'filename': filename - }) - - $scope.$apply() - break - case 'onOOBText': - var textData = data - $scope.chatLog.unshift({ - name: " > oob <", - text: $sce.trustAsHtml(textData) - }) - console.info('currResponse', textData) - $scope.$apply() - break + this.onMsg = function (inMsg) { + // console.info("ProgramABGui.onMsg(" + inMsg.method + ')') + let data = inMsg.data[0] + + switch (inMsg.method) { + case "onStatus": + $scope.status = data + $scope.$apply() + break + + case "onBotImage": + $scope.currentBotImage = data + $scope.$apply() + break + + case "onState": + _self.updateState(data) + $scope.$apply() + break + + case "onTopic": + $scope.service.currentTopic = data + $scope.$apply() + break + + case "onAimlFile": + $scope.aimlFileData.data = data + $scope.$apply() + break + + case "onPredicates": + $scope.predicates = data + $scope.$apply() + break + + case "onPredicate": + $scope.predicates[data.name] = data.value + $scope.$apply() + break + + case "onRequest": + var textData = data + $scope.chatLog.unshift({ + type: "User", + name: $scope.currentUserName, + text: $sce.trustAsHtml(textData), + }) + console.info("onRequest", textData) + $scope.$apply() + break + case "onResponse": + var textData = data + $scope.chatLog.unshift({ + type: "Bot", + name: $scope.service.config.botType, + text: $sce.trustAsHtml(data.msg), + }) + $scope.lastResponse = textData + $scope.$apply() + break + case "onLog": + var textData = data + let filename = null + parts = textData.split(" ") + if (parts.length > 2 && parts[1] == "Matched:") { + filename = parts[parts.length - 1] + textData = textData.substr(0, textData.lastIndexOf(" ") + 1) + // pos0 = textData.lastIndexOf(' ') + 1 + // url = textData.substr(0,pos0) + '' + // url = textData.substr(0,pos0) + '' + // textData = url + } + + $scope.log.unshift({ + name: "", + text: textData, + filename: filename, + }) + + $scope.$apply() + break + case "onOOBText": + var textData = data + $scope.chatLog.unshift({ + name: " > oob <", + text: $sce.trustAsHtml(textData), + }) + console.info("currResponse", textData) + $scope.$apply() + break default: - console.error("ERROR - unhandled method " + $scope.name + " " + inMsg.method) - break - } + console.error("ERROR - unhandled method " + $scope.name + " " + inMsg.method) + break + } } - $scope.getAimlFile = function(filename) { - $scope.aimlFile = filename - console.log('getting aiml file ' + filename) - msg.send('getAimlFile', $scope.service.config.currentBotName, filename) - $scope.tabs.selected = 2 + $scope.getAimlFile = function (filename) { + $scope.aimlFile = filename + console.log("getting aiml file " + filename) + msg.send("getAimlFile", $scope.service.config.botType, filename) + $scope.tabs.selected = 2 } - $scope.saveAimlFile = function() { - msg.send("saveAimlFile", $scope.service.config.currentBotName, $scope.aimlFile, $scope.aimlFileData.data) + $scope.saveAimlFile = function () { + msg.send("saveAimlFile", $scope.service.config.botType, $scope.aimlFile, $scope.aimlFileData.data) } - $scope.setSessionKey = function() { - msg.send("setCurrentUserName", $scope.service.config.currentUserName) - msg.send("setCurrentBotName", $scope.service.config.currentBotName) + $scope.setSessionKey = function () { + msg.send("setCurrentUserName", $scope.service.config.username) + msg.send("setCurrentBotName", $scope.service.config.botType) } - $scope.getBotInfo = function() { - if ($scope.service && $scope.service.bots){ - return $scope.service.bots[$scope.service.config.currentBotName] - } - return null + $scope.getBotInfo = function () { + if ($scope.service && $scope.service.bots) { + return $scope.service.bots[$scope.service.config.botType] + } + return null } - $scope.getCurrentSession = function() { - if (!$scope.service.sessions){ - return null - } - if ($scope.getCurrentSessionKey()in $scope.service.sessions) { - return $scope.service.sessions[$scope.getCurrentSessionKey()] - } + $scope.getCurrentSession = function () { + if (!$scope.service.sessions) { return null + } + if ($scope.getCurrentSessionKey() in $scope.service.sessions) { + return $scope.service.sessions[$scope.getCurrentSessionKey()] + } + return null } - $scope.getCurrentSessionKey = function() { - return $scope.service.config.currentUserName + ' <-> ' + $scope.service.config.currentBotName + $scope.getCurrentSessionKey = function () { + return $scope.service.config.username + " <-> " + $scope.service.config.botType } - $scope.test = function(session, utterance) { - msg.send("getCategories", "hello") + $scope.test = function (session, utterance) { + msg.send("getCategories", "hello") } - $scope.getSessionResponse = function(utterance) { - console.info("SESSION GET RESPONSE (" + $scope.currentUserName + " " + $scope.service.config.currentBotName + ")") - $scope.getResponse($scope.currentUserName, $scope.service.config.currentBotName, utterance) + $scope.getSessionResponse = function (utterance) { + console.info("SESSION GET RESPONSE (" + $scope.currentUserName + " " + $scope.service.config.botType + ")") + $scope.getResponse($scope.currentUserName, $scope.service.config.botType, utterance) } - $scope.getResponse = function(username, botname, utterance) { - console.info("USER BOT RESPONSE (" + username + " " + botname + ")") - msg.send("getResponse", username, botname, utterance) - $scope.utterance = "" + $scope.getResponse = function (username, botname, utterance) { + console.info("USER BOT RESPONSE (" + username + " " + botname + ")") + msg.send("getResponse", username, botname, utterance) + $scope.utterance = "" } - $scope.startDialog = function() { - startDialog = $uibModal.open({ - templateUrl: "startDialog.html", - scope: $scope, - controller: function($scope) { - $scope.cancel = function() { - startDialog.dismiss() - } - - } - }) + $scope.startDialog = function () { + startDialog = $uibModal.open({ + templateUrl: "startDialog.html", + scope: $scope, + controller: function ($scope) { + $scope.cancel = function () { + startDialog.dismiss() + } + }, + }) } - $scope.startSession = function(username, botname) { - $scope.currentUserName = username - $scope.chatLog.unshift("Reload Session for Bot " + botname) - $scope.startSessionLabel = 'Reload Session' - msg.send("startSession", username, botname) - startDialog.dismiss() + $scope.startSession = function (username, botname) { + $scope.currentUserName = username + $scope.chatLog.unshift("Reload Session for Bot " + botname) + $scope.startSessionLabel = "Reload Session" + msg.send("startSession", username, botname) + startDialog.dismiss() } - $scope.savePredicates = function() { - $scope.service = mrl.getService($scope.service.name) - mrl.sendTo($scope.service.name, "savePredicates") - // FIXME !!!! lame + $scope.savePredicates = function () { + $scope.service = mrl.getService($scope.service.name) + mrl.sendTo($scope.service.name, "savePredicates") + // FIXME !!!! lame } - $scope.getProperties = function() { - if (!$scope.getBotInfo()){ - return null - } - return $scope.getBotInfo()['properties'] + $scope.getProperties = function () { + if (!$scope.getBotInfo()) { + return null + } + return $scope.getBotInfo()["properties"] } - $scope.getProperty = function(propName) { - try { - if ($scope.getBotInfo() && $scope.getBotInfo()['properties']){ - return $scope.getBotInfo()['properties'][propName] - } - } catch (error){ - console.warn('getProperty(' + propName + ') not found') - return null + $scope.getProperty = function (propName) { + try { + if ($scope.getBotInfo() && $scope.getBotInfo()["properties"]) { + return $scope.getBotInfo()["properties"][propName] } + } catch (error) { + console.warn("getProperty(" + propName + ") not found") + return null + } } - $scope.removeBotProperty = function(propName) { - delete $scope.getBotInfo()['properties'][propName] - msg.send("removeBotProperty", propName) + $scope.removeBotProperty = function (propName) { + delete $scope.getBotInfo()["properties"][propName] + msg.send("removeBotProperty", propName) } - $scope.aceLoaded = function(_editor) { - // _editor.setReadOnly(true); - $scope.aimlEditor = _editor - console.log('aceLoaded') + $scope.aceLoaded = function (_editor) { + // _editor.setReadOnly(true); + $scope.aimlEditor = _editor + console.log("aceLoaded") } - $scope.aceChanged = function(e) { - // - console.log('aceChanged') + $scope.aceChanged = function (e) { + // + console.log("aceChanged") } - $scope.getBotPath = function(e) { - if ($scope.service?.bots && $scope.service?.bots[$scope.service?.config?.currentBotName]?.path){ - return $scope.service?.bots[$scope.service?.config.currentBotName].path - } - return null + $scope.getBotPath = function (e) { + if ($scope.service?.bots && $scope.service?.bots[$scope.service?.config?.currentBotName]?.path) { + return $scope.service?.bots[$scope.service?.config.botType].path + } + return null } - - - $scope.getStatusLabel = function(level) { - if (level == 'error') { - return 'row label col-md-12 label-danger' - } - if (level == 'warn') { - return 'row label col-md-12 label-warning' - } - return 'row label col-md-12 label-info' + $scope.getStatusLabel = function (level) { + if (level == "error") { + return "row label col-md-12 label-danger" + } + if (level == "warn") { + return "row label col-md-12 label-warning" + } + + return "row label col-md-12 label-info" } // subscribe to the response from programab. - msg.subscribe('publishTopic') - msg.subscribe('publishRequest') - msg.subscribe('publishResponse') - msg.subscribe('publishLog') - msg.subscribe('publishOOBText') - msg.subscribe('getPredicates') - msg.subscribe('publishPredicate') - msg.subscribe('getAimlFile') + msg.subscribe("publishTopic") + msg.subscribe("publishRequest") + msg.subscribe("publishResponse") + msg.subscribe("publishLog") + msg.subscribe("publishOOBText") + msg.subscribe("getPredicates") + msg.subscribe("publishPredicate") + msg.subscribe("getAimlFile") + msg.send("getPredicates") - msg.send('getPredicates') - msg.subscribe(this) -} + }, ]) /* .filter('orderObjectBy', function() { diff --git a/src/main/resources/resource/WebGui/app/service/js/ServoGui.js b/src/main/resources/resource/WebGui/app/service/js/ServoGui.js index 2175f81481..21fec7e0df 100644 --- a/src/main/resources/resource/WebGui/app/service/js/ServoGui.js +++ b/src/main/resources/resource/WebGui/app/service/js/ServoGui.js @@ -245,6 +245,10 @@ angular.module('mrlapp.service.ServoGui', []).controller('ServoGuiCtrl', ['$scop msg.send('setIdleTimeout', idleTime * 1000) } + $scope.toggleServoSpeedBar = function (service) { + service.speedBar = !service.speedBar + } + $scope.map = function() { if ($scope.lockInputOutput) { diff --git a/src/main/resources/resource/WebGui/app/service/js/ServoMixerGui.js b/src/main/resources/resource/WebGui/app/service/js/ServoMixerGui.js index 40d1a258de..6cc8aea714 100644 --- a/src/main/resources/resource/WebGui/app/service/js/ServoMixerGui.js +++ b/src/main/resources/resource/WebGui/app/service/js/ServoMixerGui.js @@ -1,17 +1,22 @@ -angular.module('mrlapp.service.ServoMixerGui', []).controller('ServoMixerGuiCtrl', ['$scope', 'mrl', function($scope, mrl) { - console.info('ServoMixerGuiCtrl') +angular.module("mrlapp.service.ServoMixerGui", []).controller("ServoMixerGuiCtrl", [ + "$scope", + "mrl", + function ($scope, mrl) { + console.info("ServoMixerGuiCtrl") var _self = this var msg = this.msg var globalPoseIndex = 0 $scope.minView = true - $scope.delay = 3 // initial + $scope.sleep = 3 // initial $scope.showGestureSave = false + $scope.speakBlocking = true + $scope.mouth = "mouth" $scope.searchServo = { - displayName: null + displayName: null, } - + $scope.servos = [] $scope.sliders = [] // list of current pose files @@ -19,329 +24,316 @@ angular.module('mrlapp.service.ServoMixerGui', []).controller('ServoMixerGuiCtrl $scope.gestureFiles = [] $scope.state = { - // gestureIndex is a string representation from $index :( dumb - 'gestureIndex': "0", - 'selectedPose': null, - 'selectedGestureFile': null, - 'selectedGesture': null, - 'playingPose': null, - 'currentGesture':{ - 'parts':[] - } + // gestureIndex is a string representation from $index :( dumb + gestureIndex: "0", + selectedPose: null, + selectedGestureFile: null, + selectedGesture: null, + playingPose: null, + currentGesture: { + parts: [], + }, } - $scope.options = []; - angular.forEach("a:alpha,b:beta,d:delta,g:gamma,e:eta,E:epsilon,o:omega,z:zeta".split(','), function(val) { - var parts = val.split(":"); - $scope.options.push({ - name: parts[0], - value: parts[1] - }); - }); - // unique id for new poses added to gesture let id = 0 // FIXME - this should be done in a base class or in framework - $scope.mrl = mrl; + $scope.mrl = mrl // sublist object of servo panels - changes based onRegistered and onReleased events $scope.subPanels = {} // GOOD TEMPLATE TO FOLLOW - this.updateState = function(service) { - // do the update - $scope.service = service + this.updateState = function (service) { + // do the update + $scope.service = service } - $scope.toggle = function(servo) { - $scope.sliders[servo].tracking = !$scope.sliders[servo].tracking + $scope.toggle = function (servo) { + $scope.sliders[servo].tracking = !$scope.sliders[servo].tracking } - _self.onSliderChange = function(servoName) { - if (!$scope.sliders[servoName].tracking) { - msg.sendTo(servoName, 'moveTo', $scope.sliders[servoName].value) - } + _self.onSliderChange = function (servoName) { + if (!$scope.sliders[servoName].tracking) { + msg.sendTo(servoName, "moveTo", $scope.sliders[servoName].value) + } } - $scope.setSearchServo = function(text) { - $scope.searchServo.displayName = text + $scope.setSearchServo = function (text) { + $scope.searchServo.displayName = text } this.updateState($scope.service) - this.onMsg = function(inMsg) { - var data = inMsg.data[0]; - switch (inMsg.method) { - case 'onStatus': - console.log(inMsg) - break - case 'onState': - _self.updateState(data) - $scope.$apply() - break - case 'onSearch': - $scope.searchServo.displayName = data - $scope.searchServos(data) - // sets pose name from selected - $scope.state.selectedPose = data + "_" + globalPoseIndex++ - $scope.$apply() - break - case 'onPlayingGesturePart': - // FIXME rename - if (data.type != 'Delay'){ - $scope.state.playingPose = data - } else { - $scope.state.playingPose.value = data.value/1000 - } - $scope.$apply() - break - case 'onPlayingGesturePartIndex': - // FIXME rename - $scope.state.gestureIndex = data + "" - $scope.state.playingPoseIndex = data - $scope.$apply() - break - case 'onStopPose': - // $scope.state.playingPose = ' ' - $scope.$apply() - break - case 'onServoEvent': - $scope.sliders[data.name].value = data.pos; - $scope.$apply() - break - case 'onPoseFiles': - $scope.poseFiles = data - $scope.$apply() - if (data && data.length > 0) { - $scope.state.selectedPose = data[data.length - 1] - } - break - case 'onGesture': - $scope.state.currentGesture = data - $scope.$apply() - break - case 'onGestureFiles': - $scope.gestureFiles = data - if (!$scope.state.selectedGestureFile && $scope.gestureFiles && $scope.gestureFiles.length > 0){ - $scope.state.selectedGestureFile = $scope.gestureFiles[0] - msg.send('getGesture', $scope.state.selectedGestureFile) - } - $scope.$apply() - break - case 'onListAllServos': - // servos sliders are either in "tracking" or "control" state - // "tracking" they are moving from callback position info published by servos - // "control" they are sending control messages to the servos - $scope.servos = data - for (var servo of $scope.servos) { - // dynamically build sliders - $scope.sliders[servo.name] = { - value: 0, - tracking: false, - options: { - id: servo.name, - floor: 0, - ceil: 180, - onStart: function(id) { - console.info('ServoMixer.onStart') - }, - onChange: function(id) { - _self.onSliderChange(id) - }, - /* + this.onMsg = function (inMsg) { + var data = inMsg.data[0] + switch (inMsg.method) { + case "onStatus": + console.log(inMsg) + break + case "onState": + _self.updateState(data) + $scope.$apply() + break + case "onSearch": + $scope.searchServo.displayName = data + $scope.searchServos(data) + // sets pose name from selected + $scope.state.selectedPose = data + "_" + globalPoseIndex++ + $scope.$apply() + break + case "onPlayingAction": + // FIXME rename + if (data.type != "Delay") { + $scope.state.playingPose = data + } else { + $scope.state.playingPose.value = data.value / 1000 + } + $scope.$apply() + break + case "onPlayingActionIndex": + // FIXME rename + $scope.state.gestureIndex = data + "" + $scope.state.playingPoseIndex = data + $scope.$apply() + break + case "onStopPose": + // $scope.state.playingPose = ' ' + $scope.$apply() + break + case "onServoEvent": + $scope.sliders[data.name].value = data.pos + $scope.$apply() + break + case "onPoseFiles": + $scope.poseFiles = data + $scope.$apply() + if (data && data.length > 0) { + $scope.state.selectedPose = data[data.length - 1] + } + break + case "onGesture": + $scope.state.currentGesture = data + $scope.$apply() + break + case "onGestureFiles": + $scope.gestureFiles = data + if (!$scope.state.selectedGestureFile && $scope.gestureFiles && $scope.gestureFiles.length > 0) { + $scope.state.selectedGestureFile = $scope.gestureFiles[0] + msg.send("getGesture", $scope.state.selectedGestureFile) + } + $scope.$apply() + break + case "onListAllServos": + // servos sliders are either in "tracking" or "control" state + // "tracking" they are moving from callback position info published by servos + // "control" they are sending control messages to the servos + $scope.servos = data + for (var servo of $scope.servos) { + // dynamically build sliders + $scope.sliders[servo.name] = { + value: 0, + tracking: false, + options: { + id: servo.name, + floor: 0, + ceil: 180, + onStart: function (id) { + console.info("ServoMixer.onStart") + }, + onChange: function (id) { + _self.onSliderChange(id) + }, + /* onChange: function() { if (!this.tracking) { // if not tracking then control msg.sendTo(servo, 'moveToX', sliders[servo].value) } },*/ - onEnd: function(id) {} - } - } - // dynamically add callback subscriptions - // these are "intermediate" subscriptions in that they - // don't send a subscribe down to service .. yet - // that must already be in place (and is in the case of Servo.publishServoEvent) - // FIXME .. servo.getName() == servo.getFullName() :( - needs to be done in framework - msg.subscribeTo(_self, servo.name + '@' + servo.id, 'publishServoEvent') - + onEnd: function (id) {}, + }, } - $scope.$apply() - break + // dynamically add callback subscriptions + // these are "intermediate" subscriptions in that they + // don't send a subscribe down to service .. yet + // that must already be in place (and is in the case of Servo.publishServoEvent) + // FIXME .. servo.getName() == servo.getFullName() :( - needs to be done in framework + msg.subscribeTo(_self, servo.name + "@" + servo.id, "publishServoEvent") + } + $scope.$apply() + break default: - console.error("ERROR - unhandled method " + $scope.name + " " + inMsg.method) - break - } + console.error("ERROR - unhandled method " + $scope.name + " " + inMsg.method) + break + } } - - $scope.addPoseToGesture = function() { - // get pos entry - let pose = { - 'name': $scope.state.selectedPose, - 'type': 'Pose', - 'blocking': false - } - - $scope.state.currentGesture.parts.splice(parseInt($scope.state.gestureIndex) + 1, 0, pose) - } - - $scope.removePoseFromGesture = function() { - $scope.state.currentGesture.parts.splice($scope.state.gestureIndex, 1) + $scope.addMoveToAction = function () { + let moves = {} + let hasSelected = false + if ($scope.servos) { + $scope.servos.forEach((servo) => { + if (servo.selected) { + moves[mrl.getShortName(servo.name)] = { + position: servo.currentInputPos, + speed: servo.speed, + } + hasSelected = true + } + }) + } + + if (hasSelected) { + msg.send("addMoveToAction", moves, parseInt($scope.state.gestureIndex) + 1) + msg.send("getGesture") + $scope.state.gestureIndex = parseInt($scope.state.gestureIndex) + 1 + "" + } else { + mrl.send("error", "no selected servos") + } } - move = function(arr, fromIndex, toIndex) { - var element = arr[fromIndex]; - arr.splice(fromIndex, 1); - arr.splice(toIndex, 0, element); - // stupid ass conversion back to string for list 'select' - $scope.state.gestureIndex = toIndex + '' + $scope.removeActionFromGesture = function () { + msg.send("removeActionFromGesture", parseInt($scope.state.gestureIndex)) + msg.send("getGesture") } - $scope.moveUpPoseInGesture = function() { - move($scope.state.currentGesture.parts, $scope.state.gestureIndex, parseInt($scope.state.gestureIndex) - 1) + $scope.moveActionUp = function () { + msg.send("moveActionUp", parseInt($scope.state.gestureIndex)) + // hilariously dumb need to subtract it and then convert it to a string + $scope.state.gestureIndex = parseInt($scope.state.gestureIndex) - 1 + "" } - $scope.moveDownPoseInGesture = function() { - move($scope.state.currentGesture.parts, $scope.state.gestureIndex, parseInt($scope.state.gestureIndex) + 1) + $scope.moveActionDown = function () { + msg.send("moveActionDown", parseInt($scope.state.gestureIndex)) + // hilariously dumb need to subtract it and then convert it to a string + $scope.state.gestureIndex = parseInt($scope.state.gestureIndex) + 1 + "" } - $scope.searchServos = function(searchText) { - var result = {} - angular.forEach($scope.subPanels, function(value, key) { - if (!searchText || mrl.getShortName(key).indexOf(searchText) != -1) { - result[key] = value; - } - }) - return result + $scope.searchServos = function (searchText) { + var result = {} + angular.forEach($scope.subPanels, function (value, key) { + if (!searchText || mrl.getShortName(key).indexOf(searchText) != -1) { + result[key] = value + } + }) + return result } - $scope.savePose = function(pose) { - msg.send('savePose', pose); + $scope.savePose = function (pose) { + msg.send("savePose", pose) } // this method initializes subPanels when a new service becomes available - this.onRegistered = function(panel) { - if (panel.simpleName == 'Servo') { - $scope.subPanels[panel.name] = panel - } + this.onRegistered = function (panel) { + if (panel.simpleName == "Servo") { + $scope.subPanels[panel.name] = panel + } } // this method removes subPanels references from released service - this.onReleased = function(panelName) { - delete $scope.subPanels[panelName] - console.info('here') + this.onReleased = function (panelName) { + delete $scope.subPanels[panelName] + console.info("here") } - $scope.step = function(){ - let index = parseInt($scope.state.gestureIndex) - let part = $scope.state.currentGesture.parts[index] - if (part.type === 'Pose'){ - msg.send('moveToPose', part.name) - } - index++ - $scope.state.gestureIndex = index + "" - + $scope.step = function () { + let index = parseInt($scope.state.gestureIndex) + let part = $scope.state.currentGesture.parts[index] + if (part.type === "Pose") { + msg.send("moveToPose", part.name) + } + index++ + $scope.state.gestureIndex = index + "" } - // initialize all services which have panel references in Intro + // initialize all services which have panel references in Intro let servicePanelList = mrl.getPanelList() for (let index = 0; index < servicePanelList.length; ++index) { - this.onRegistered(servicePanelList[index]) + this.onRegistered(servicePanelList[index]) } - $scope.saveGesture = function(gestureName) { - // gestureName = $scope.state.selectedGestureFile - $scope.state.currentGesture.name = gestureName - if ($scope.gestureFiles.includes(gestureName)){ - // saving current file - msg.send('saveGesture', gestureName, $scope.state.currentGesture) - } else { - // saving new file - blankGesture = { - parts:[], - repeat: false - } - msg.send('saveGesture', gestureName, blankGesture) - } - + $scope.saveGesture = function (gestureName) { + msg.send("saveGesture", gestureName) } - $scope.addDelay = function(seconds){ + $scope.addSleep = function (seconds) { + let value = parseFloat(seconds) - let value = parseFloat(seconds) - - if (Number.isNaN(value)){ - console.error(seconds, "is not a valid number for delay") - return - } - - let delay = { - 'name': 'delay', - 'type': 'Delay', - 'value': value * 1000, - 'blocking': true - } - - $scope.state.currentGesture.parts.splice(parseInt($scope.state.gestureIndex) + 1, 0, delay) + if (Number.isNaN(value)) { + console.error(seconds, "is not a valid number for sleep") + return + } + + msg.send("addSleepAction", seconds) + msg.send("getGesture") } - $scope.playGesture = function(gesture) { - if (gesture){ - msg.send('playGesture', gesture) - } else { - console.warn('gesture empty') - } + $scope.playGesture = function (gesture) { + if (gesture) { + msg.send("playGesture", gesture) + } else { + console.warn("gesture empty") + } } - $scope.removeGesture = function(gesture) { - if (gesture){ - msg.send('removeGesture', gesture) - } else { - console.warn('removeGesture empty') - } + $scope.removeGesture = function (gesture) { + if (gesture) { + msg.send("removeGesture", gesture) + } else { + console.warn("removeGesture empty") + } } - $scope.displayValue = function(pose) { - // !pose.value || Number.isNaN(pose.value)?'':pose.value/1000 - if (pose.type == 'Delay'){ - return pose.value/1000 - } else { - return pose.value - } + $scope.displayAction = function (action) { + if (action.type == "sleep") { + return `sleep ${action.value}` + } else if (action.type == "speak") { + return `speak ${action.value.text}` + } else if (action.type == "moveTo") { + let ret = "move" + Object.keys(action.value).forEach((key) => { + ret += ` ${key} ${action.value[key].position}` + if (action.value[key].speed) { + ret += ` ${action.value[key].speed}` + } + }) + return ret + } else { + return action.value + } } - $scope.speak = function() { - - let delay = { - 'name': 'speech', - 'type': 'Speech', - 'value': $scope.text, - 'blocking': true - } - - $scope.state.currentGesture.parts.splice(parseInt($scope.state.gestureIndex) + 1, 0, delay) + $scope.speak = function () { + speechPart = { + mouth: $scope.mouth, + blocking: $scope.speakBlocking, + text: $scope.text, + } + msg.send("addSpeakAction", speechPart, parseInt($scope.state.gestureIndex) + 1) + msg.send("getGesture") + } + + $scope.toggleServoSpeedBar = function (service) { + service.speedBar = !service.speedBar } + msg.subscribe("getGesture") + msg.subscribe("getGestureFiles") + msg.subscribe("listAllServos") + msg.subscribe("search") - msg.subscribe('getPoseFiles') - msg.subscribe('getGesture') - msg.subscribe('getGestureFiles') - msg.subscribe('listAllServos') - msg.subscribe('search') - - msg.send('listAllServos') - msg.send('getPoseFiles'); - msg.send('getGestureFiles'); + msg.send("listAllServos") + msg.send("getGestureFiles") - msg.subscribe("publishPlayingGesturePart") - msg.subscribe("publishPlayingGesturePartIndex") + msg.subscribe("publishPlayingAction") + msg.subscribe("publishPlayingActionIndex") msg.subscribe("publishStopPose") mrl.subscribeToRegistered(this.onRegistered) mrl.subscribeToReleased(this.onReleased) msg.subscribe(this) -} + }, ]) diff --git a/src/main/resources/resource/WebGui/app/service/serviceCtrlDirective.js b/src/main/resources/resource/WebGui/app/service/serviceCtrlDirective.js index dd9f039a5e..5b87a60400 100644 --- a/src/main/resources/resource/WebGui/app/service/serviceCtrlDirective.js +++ b/src/main/resources/resource/WebGui/app/service/serviceCtrlDirective.js @@ -1,65 +1,98 @@ -angular.module('mrlapp.service').directive('serviceCtrlDirective', ['$compile', 'mrl', function($compile, mrl) { - return { +angular + .module("mrlapp.service") + .directive("serviceCtrlDirective", [ + "$compile", + "mrl", + function ($compile, mrl) { + return { scope: { - //"=" -> binding to items in parent-scope specified by attribute - //"@" -> using passed attribute - panel: '=panel' + //"=" -> binding to items in parent-scope specified by attribute + //"@" -> using passed attribute + panel: "=panel", }, - link: function(scope, elem, attr) { - scope.service = mrl.getService(scope.panel.name); - scope.panelconfig = {}; + link: function (scope, elem, attr) { + scope.service = mrl.getService(scope.panel.name) + scope.panelconfig = {} + console.error("WHAT THE HELL CHROME !!!!") - // ACTUAL SCOPE IS CREATED IN serviceCtrlNext DIRECTIVE !!!! - //prepare dynamic controller injection - var html = '
'; - var watch = scope.$watch(function() { - return scope.panel.templatestatus; - }, function() { - if (scope.panel.templatestatus && scope.panel.templatestatus == 'loaded') { - watch(); - console.info('deps loaded, start ctrl', scope.panel.name); - mrl.createMsgInterface(scope.panel.name).then(function(msg_) { - console.info('==== msgInterface received', scope.panel.name); - scope.panel.msg_ = msg_; - scope.msginterface = msg_; - scope.msgmethods = msg_.temp.msg; - scope.mrl = mrl - elem.html(html).show(); - console.info("elem.contents") - // console.info(elem.contents()) - - $compile(elem.contents())(scope); - - }, function(msg_) { - console.log('msgInterface-meh!'); - }); - } - }); - } - }; -} -]).directive('serviceCtrlNext', ['mrl', function(mrl) { - //dynamic controller - return { + // ACTUAL SCOPE IS CREATED IN serviceCtrlNext DIRECTIVE !!!! + //prepare dynamic controller injection + var html = + "
" + var watch = scope.$watch( + function () { + return scope.panel.templatestatus + }, + function () { + if (scope.panel.templatestatus && scope.panel.templatestatus == "loaded") { + watch() + console.info("deps loaded, start ctrl", scope.panel.name) + mrl + .createMsgInterface(scope.panel.name) + .then(function (msg_) { + console.info("==== msgInterface received", scope.panel.name) + scope.panel.msg_ = msg_ + scope.msginterface = msg_ + scope.msgmethods = msg_.temp.msg + scope.mrl = mrl + elem.html(html).show() + console.info("elem.contents") + // console.info(elem.contents()) + + $compile(elem.contents())(scope) + }) + .catch(function (errorMessage) { + // This block will execute if the promise is rejected + console.error(errorMessage) + // Handle error + }) + .finally(function () { + // This block will execute after either resolve or reject + console.log("Operation complete") + }) + } + } + ) // end of watch() + }, // link + } + }, + ]) + .directive("serviceCtrlNext", [ + "mrl", + function (mrl) { + //dynamic controller + console.error("WHAT THE HELL CHROME !") + return { scope: { - msg: '=msgmethods', - name: '=', - service: '=', - //Does it make sense to give him an instance of itself that may be outdated in just a bit? Or let it fetch it's instance himself`? - size: '=', - mrl:'=mrl' + msg: "=msgmethods", + name: "=", + service: "=", + //Does it make sense to give him an instance of itself that may be outdated in just a bit? Or let it fetch it's instance himself`? + size: "=", + mrl: "=mrl", }, bindToController: { - panelconfig: '=', - msg: '=msginterface' + panelconfig: "=", + msg: "=msginterface", }, controller: "@", controllerAs: "guictrl", name: "controllerName", - link: function(scope, elem, attr) { - console.log(scope.name, '==== serviceCtrlNext-link'); - mrl.controllerscope(scope.name, scope); - } - }; -} -]); + link: function (scope, elem, attr) { + console.log(scope.name, "==== serviceCtrlNext-link") + mrl.controllerscope(scope.name, scope) + }, + } + }, + ]) diff --git a/src/main/resources/resource/WebGui/app/service/views/FiniteStateMachineGui.html b/src/main/resources/resource/WebGui/app/service/views/FiniteStateMachineGui.html index 067ee7624c..b61f5af303 100644 --- a/src/main/resources/resource/WebGui/app/service/views/FiniteStateMachineGui.html +++ b/src/main/resources/resource/WebGui/app/service/views/FiniteStateMachineGui.html @@ -54,5 +54,22 @@

Last Event {{event}} Current State: {{current}}

+ + + + + + + + + + + + + + + +
TimestampStateEvent
{{ item.ts }}{{ item.state }}{{ item.event }}
+ diff --git a/src/main/resources/resource/WebGui/app/service/views/MockGatewayGui.html b/src/main/resources/resource/WebGui/app/service/views/MockGatewayGui.html index 07cd7f8e40..e1f874a842 100644 --- a/src/main/resources/resource/WebGui/app/service/views/MockGatewayGui.html +++ b/src/main/resources/resource/WebGui/app/service/views/MockGatewayGui.html @@ -1,28 +1,36 @@
- +
+

+ {{service.id}} {{service.remoteId}} +

-

{{service.id}} {{service.remoteId}}

- - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + -
tssendernamemethoddata
tssendernamemethoddata
{{msg.msgId}}{{msg.sender}} {{msg.name}}{{msg.method}}{{msg.data}}
{{msg.msgId}}{{msg.sender}} + + {{msg.name}}{{msg.method}}{{msg.data}}
+
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 8a9d2876c0..c8863b5fa5 100644 --- a/src/main/resources/resource/WebGui/app/service/views/ProgramABGui.html +++ b/src/main/resources/resource/WebGui/app/service/views/ProgramABGui.html @@ -19,17 +19,17 @@ - +
- + - - + + - + @@ -41,7 +41,7 @@
- +
diff --git a/src/main/resources/resource/WebGui/app/service/views/ServoGui.html b/src/main/resources/resource/WebGui/app/service/views/ServoGui.html index fc0cb1faee..54bb6911d3 100644 --- a/src/main/resources/resource/WebGui/app/service/views/ServoGui.html +++ b/src/main/resources/resource/WebGui/app/service/views/ServoGui.html @@ -1,208 +1,380 @@ -
- - -
-
- + + +
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
ControllerPin  Invert  AutoDisableTimeOut = {{service.idleTimeout/1000}} sEnable
+ + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + +
SweepTrackSpeed + +
+
+
+ + ENERGIZED +
+
- -
-
- - - - - - - - - - - - - - - - - - - -
ControllerPin  Invert  AutoDisableTimeOut = {{service.idleTimeout/1000}} sEnable
- - - - - - - - - - - - - - - - - - - -
+ +
+   + + Limits
+ + + + + + {{speedDisplay}}°/s   + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ {{service.targetPos}}º +
+
+
+ {{service.currentInputPos.toFixed(0)}}º +
+
+
+ {{service.rest}}º +
+
+ +
+
- - - - -
-
- - - - - - - - - - - - - - - - - -
SweepTrackSpeed - -
-
-
- - ENERGIZED - - - -
-
-
- -
  - - Limits
- - - - - - {{speedDisplay}}°/s   - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- {{service.targetPos}}º -
-
-
- {{service.currentInputPos.toFixed(0)}}º -
-
-
- {{service.rest}}º -
-
- -
-
-
- -
 
 
- -
 
- -
Min {{service.mapper.minX}} - InputMax {{service.mapper.maxX}} -
- - -
Min {{service.mapper.minY}} - - Output Lock - - Max {{service.mapper.maxY}} -
- - -
-
- -
- -
- - + +
 
 
+ +
 
+ +
Min {{service.mapper.minX}}InputMax {{service.mapper.maxX}}
+ + +
Min {{service.mapper.minY}} + Output Lock + + Max {{service.mapper.maxY}}
+ + +
+
+ +
+ +
+ +
- {{service.name}}{{service.targetPos}}º + {{service.name}}{{service.targetPos}}º@{{speedDisplay}}°/s + + + + + speed + - - + +
- - diff --git a/src/main/resources/resource/WebGui/app/service/views/ServoMixerGui.html b/src/main/resources/resource/WebGui/app/service/views/ServoMixerGui.html index 506af5198c..eb4fff00cc 100644 --- a/src/main/resources/resource/WebGui/app/service/views/ServoMixerGui.html +++ b/src/main/resources/resource/WebGui/app/service/views/ServoMixerGui.html @@ -1,15 +1,14 @@ -
-

{{state.selectedGestureFile}} {{state.playingPose.name}}

+

{{state.selectedGestureFile}} {{state.playingPose.name}} {{state.gestureIndex}}

- - - + + + - + -
Directory {{service.config.gesturesDir}}
Directory {{service.config.gesturesDir}}
+ Gesture -
+
- - - - + + - - -
- Content + + +
+ Actions
- - - - + - - - + + + + + +
- - - - - - -
- - - - - -
+ + + + + + +
+ + + + + +
- - -
- Pose - - - - - - - - +
@@ -144,6 +120,7 @@

{{state.selectedGestureFile}} {{state.playingPose.name}}


+
@@ -152,3 +129,5 @@

{{state.selectedGestureFile}} {{state.playingPose.name}}

+ +here {{servos}} diff --git a/src/main/resources/resource/framework/pom.xml.template b/src/main/resources/resource/framework/pom.xml.template index ca584c5ba0..76aa1d8b2d 100644 --- a/src/main/resources/resource/framework/pom.xml.template +++ b/src/main/resources/resource/framework/pom.xml.template @@ -331,7 +331,7 @@ maven-surefire-plugin org.apache.maven.plugins - 2.18 + 3.2.2 ${argLine} -Djava.library.path=libraries/native -Djna.library.path=libraries/native @@ -340,11 +340,11 @@ **/integration/* - - **/OpenCV* + - - + + @@ -425,7 +425,7 @@ org.apache.maven.plugins maven-surefire-report-plugin - 2.18 + 3.2.2 org.apache.maven.plugins diff --git a/src/test/java/org/myrobotlab/arduino/ArduinoServoConfigTest.java b/src/test/java/org/myrobotlab/arduino/ArduinoServoConfigTest.java index 60764244b7..86260ce947 100755 --- a/src/test/java/org/myrobotlab/arduino/ArduinoServoConfigTest.java +++ b/src/test/java/org/myrobotlab/arduino/ArduinoServoConfigTest.java @@ -1,4 +1,5 @@ package org.myrobotlab.arduino; + import java.io.IOException; import java.text.ParseException; @@ -28,7 +29,6 @@ @Ignore public class ArduinoServoConfigTest { - @Test public void generateSimpleConfigs() throws Exception { @@ -102,10 +102,6 @@ public void loadInMoov2Configs() throws IOException { System.out.println("Loaded..."); waitOnAnyKey(); - // Runtime.releaseAll(); - // i02 = (InMoov2)Runtime.createAndStart("i02", "InMoov2"); - // i02.load("testconfig.yml"); - // TODO: do something. } // @Test diff --git a/src/test/java/org/myrobotlab/codec/ClassUtilTest.java b/src/test/java/org/myrobotlab/codec/ClassUtilTest.java new file mode 100644 index 0000000000..9480eb065f --- /dev/null +++ b/src/test/java/org/myrobotlab/codec/ClassUtilTest.java @@ -0,0 +1,500 @@ +package org.myrobotlab.codec; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import org.junit.Test; +import org.myrobotlab.framework.Service; +import org.myrobotlab.framework.interfaces.Attachable; +import org.myrobotlab.math.interfaces.Mapper; +import org.myrobotlab.sensor.EncoderData; +import org.myrobotlab.service.config.ServiceConfig; +import org.myrobotlab.service.interfaces.EncoderControl; +import org.myrobotlab.service.interfaces.ServoControl; +import org.myrobotlab.service.interfaces.ServoController; +import org.myrobotlab.service.interfaces.TextPublisher; + +public class ClassUtilTest implements ServoControl, TextPublisher { + + /** + * because these 2 interfaces inherit multiple interfaces the total is currently 8 + */ + int numIntefaces = 8; + + @Test + public void testGetInterfacesString() throws ClassNotFoundException { + Set interfaces = ClassUtil.getInterfaces(this.getClass().getCanonicalName()); + assertEquals("8 itnerfaces", 8, interfaces.size()); + } + + @Test + public void testGetInterfacesClassOfQSetOfString() throws ClassNotFoundException { + Set filter = new HashSet<>(); + filter.add("org.myrobotlab.service.interfaces.TextPublisher"); + Set interfaces = ClassUtil.getInterfaces(this.getClass(), filter); + assertEquals("7 itnerfaces", 7, interfaces.size()); + } + + @Test + public void testGetInterfacesStringSetOfString() throws ClassNotFoundException { + Set filter = new HashSet<>(); + filter.add("org.myrobotlab.service.interfaces.TextPublisher"); + Set interfaces = ClassUtil.getInterfaces(this.getClass().getCanonicalName(), filter); + assertEquals("7 itnerfaces", 7, interfaces.size()); + } + + @Override + public Double moveTo(Integer newPos) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Double moveTo(Double newPos) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Double moveToBlocking(Integer newPos) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Double moveToBlocking(Integer newPos, Long timeoutMs) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void onEncoderData(EncoderData data) { + // TODO Auto-generated method stub + + } + + @Override + public void attach(Attachable service) throws Exception { + // TODO Auto-generated method stub + + } + + @Override + public void addListener(String localTopic, String otherService, String callback) { + // TODO Auto-generated method stub + + } + + @Override + public void addListener(String localTopic, String otherService) { + // TODO Auto-generated method stub + + } + + @Override + public void removeListener(String localTopic, String otherService, String callback) { + // TODO Auto-generated method stub + + } + + @Override + public void removeListener(String localTopic, String otherService) { + // TODO Auto-generated method stub + + } + + @Override + public void attach(String serviceName) throws Exception { + // TODO Auto-generated method stub + + } + + @Override + public void detach(Attachable service) { + // TODO Auto-generated method stub + + } + + @Override + public void detach(String serviceName) { + // TODO Auto-generated method stub + + } + + @Override + public void detach() { + // TODO Auto-generated method stub + + } + + @Override + public Set getAttached() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set getAttached(String publishingPoint) { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isAttached(Attachable instance) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isAttached(String name) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isLocal() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean hasInterface(String interfaze) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean hasInterface(Class interfaze) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isType(Class clazz) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isType(String clazz) { + // TODO Auto-generated method stub + return false; + } + + @Override + public String getName() { + // TODO Auto-generated method stub + return null; + } + + @Override + public ServiceConfig load() throws IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean save() { + // TODO Auto-generated method stub + return false; + } + + @Override + public Service publishState() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void broadcastState() { + // TODO Auto-generated method stub + + } + + @Override + public void attach(ServoController listener) { + // TODO Auto-generated method stub + + } + + @Override + public void detach(ServoController listener) { + // TODO Auto-generated method stub + + } + + @Override + public void disable() { + // TODO Auto-generated method stub + + } + + @Override + public void enable() { + // TODO Auto-generated method stub + + } + + @Override + public boolean isAutoDisable() { + // TODO Auto-generated method stub + return false; + } + + @Override + public String getController() { + // TODO Auto-generated method stub + return null; + } + + @Override + public EncoderControl getEncoder() { + // TODO Auto-generated method stub + return null; + } + + @Override + public long getLastActivityTime() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public Mapper getMapper() { + // TODO Auto-generated method stub + return null; + } + + @Override + public double getMax() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public double getMin() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public String getPin() { + // TODO Auto-generated method stub + return null; + } + + @Override + public double getCurrentInputPos() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public double getCurrentOutputPos() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public double getRest() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public Double getSpeed() { + // TODO Auto-generated method stub + return null; + } + + @Override + public double getTargetOutput() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public double getTargetPos() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean isBlocking() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isEnabled() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isInverted() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isMoving() { + // TODO Auto-generated method stub + return false; + } + + @Override + public void map(double minX, double maxX, double minY, double maxY) { + // TODO Auto-generated method stub + + } + + @Override + public Double moveToBlocking(Double pos) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Double moveToBlocking(Double pos, Long timeoutMs) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void rest() { + // TODO Auto-generated method stub + + } + + @Override + public void setAutoDisable(boolean autoDisable) { + // TODO Auto-generated method stub + + } + + @Override + public void setInverted(boolean invert) { + // TODO Auto-generated method stub + + } + + @Override + public void setMapper(Mapper m) { + // TODO Auto-generated method stub + + } + + @Override + public void setMinMax(double minXY, double maxXY) { + // TODO Auto-generated method stub + + } + + @Override + public void setMinMaxOutput(double minY, double maxY) { + // TODO Auto-generated method stub + + } + + @Override + public void setPin(Integer pin) { + // TODO Auto-generated method stub + + } + + @Override + public void setPin(String pin) { + // TODO Auto-generated method stub + + } + + @Override + public void setPosition(double pos) { + // TODO Auto-generated method stub + + } + + @Override + public void setRest(double rest) { + // TODO Auto-generated method stub + + } + + @Override + public void setSpeed(Integer degreesPerSecond) { + // TODO Auto-generated method stub + + } + + @Override + public void setSpeed(Double degreesPerSecond) { + // TODO Auto-generated method stub + + } + + @Override + public void stop() { + // TODO Auto-generated method stub + + } + + @Override + public void sync(ServoControl sc) { + // TODO Auto-generated method stub + + } + + @Override + public void sync(String name) { + // TODO Auto-generated method stub + + } + + @Override + public void unsync(String name) { + // TODO Auto-generated method stub + + } + + @Override + public void unsync(ServoControl sc) { + // TODO Auto-generated method stub + + } + + @Override + public void waitTargetPos() { + // TODO Auto-generated method stub + + } + + @Override + public void writeMicroseconds(int uS) { + // TODO Auto-generated method stub + + } + + @Override + public void attachServoController(String sc) { + // TODO Auto-generated method stub + + } + + @Override + public void fullSpeed() { + // TODO Auto-generated method stub + + } + + @Override + public String publishText(String text) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/test/java/org/myrobotlab/framework/ConfigTest.java b/src/test/java/org/myrobotlab/framework/ConfigTest.java index 80a6665c37..8ed7668f39 100644 --- a/src/test/java/org/myrobotlab/framework/ConfigTest.java +++ b/src/test/java/org/myrobotlab/framework/ConfigTest.java @@ -11,25 +11,60 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Comparator; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; -import org.myrobotlab.codec.CodecUtils; -import org.myrobotlab.io.FileIO; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.service.Clock; -import org.myrobotlab.service.Pid.PidData; +import org.myrobotlab.service.InMoov2Head; +import org.myrobotlab.service.LocalSpeech; import org.myrobotlab.service.Runtime; -import org.myrobotlab.service.Tracking; -import org.myrobotlab.service.WebGui; +import org.myrobotlab.service.Servo; import org.myrobotlab.service.config.ClockConfig; -import org.myrobotlab.service.config.PidConfig; +import org.myrobotlab.service.config.InMoov2Config; +import org.myrobotlab.service.config.LocalSpeechConfig; +import org.myrobotlab.service.config.MarySpeechConfig; +import org.myrobotlab.service.config.OpenCVConfig; +import org.myrobotlab.service.config.PollyConfig; +import org.myrobotlab.service.config.ServiceConfig.Listener; import org.myrobotlab.service.config.ServoConfig; import org.myrobotlab.service.config.TrackingConfig; import org.myrobotlab.test.AbstractTest; import org.slf4j.Logger; public class ConfigTest extends AbstractTest { + + + @BeforeClass + public static void setUpBeforeClass() { + System.out.println("Runs before any test method in the class"); + } + + @AfterClass + public static void tearDownAfterClass() { + System.out.println("Runs after all test methods in the class"); + } + + @Before /* before each test */ + public void setUp() throws IOException { + // remove all services - also resets config name to DEFAULT effectively + Runtime.releaseAll(true, true); + // clean our config directory + Runtime.removeConfig(CONFIG_NAME); + // set our config + Runtime.setConfig(CONFIG_NAME); + } + + @After + public void tearDown() { + System.out.println("Runs after each test method"); + } + // --- config set related --- // setConfigPath(fullpath) @@ -55,349 +90,186 @@ public class ConfigTest extends AbstractTest { final String CONFIG_PATH = "data" + File.separator + "config" + File.separator + CONFIG_NAME; - public void removeConfigData() throws IOException { - File check = new File("data" + File.separator + "config" + File.separator + CONFIG_NAME); - - if (check.exists()) { - Path pathToBeDeleted = Paths.get(check.getAbsolutePath()); - Files.walk(pathToBeDeleted).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); - } - - } @Test - public void configTest() throws Exception { + public void testStartNoConfig() throws Exception { Runtime runtime = Runtime.getInstance(); - Plan plan = null; - - Runtime.clearPlan(); - removeConfigData(); - Runtime.setConfig(CONFIG_NAME); - - // load default config TODO test Runtime.useDefaults(false) also - Runtime.load("eyeTracking", "Tracking"); - plan = Runtime.load("headTracking", "Tracking"); - Runtime.load("cv", "OpenCV"); - - TrackingConfig eyeTracking = (TrackingConfig) plan.get("eyeTracking"); - TrackingConfig headTracking = (TrackingConfig) plan.get("headTracking"); - - Runtime.load("pid", "Pid"); - PidConfig pid = (PidConfig) plan.get("pid"); + assertNotNull(runtime); - // TODO - check defaults -// eyeTrackingc.getPeer("cv").autoStart = false; -// headTrackingc.getPeer("cv").autoStart = false; - - // map both names to single cv - eyeTracking.getPeer("cv").name = "cv"; - headTracking.getPeer("cv").name = "cv"; - - eyeTracking.getPeer("pid").name = "pid"; - headTracking.getPeer("pid").name = "pid"; - - eyeTracking.getPeer("controller").name = "left"; - headTracking.getPeer("controller").name = "left"; - - eyeTracking.getPeer("pan").name = "eyeX"; - headTracking.getPeer("pan").name = "rothead"; + // complete teardown, release runtime, block + Runtime.releaseAll(true, true); - eyeTracking.getPeer("tilt").name = "eyeY"; - headTracking.getPeer("tilt").name = "neck"; + String[] names = Runtime.getServiceNames(); + assertEquals("complete teardown should be 0", 0, names.length); - Runtime.load("eyeY", "Servo"); - Runtime.load("eyeX", "Servo"); - Runtime.load("neck", "Servo"); - Runtime.load("rothead", "Servo"); - ServoConfig eyeY = (ServoConfig)plan.get("eyeY"); - ServoConfig eyeX = (ServoConfig)plan.get("eyeX"); - ServoConfig neck = (ServoConfig)plan.get("neck"); - ServoConfig rothead = (ServoConfig)plan.get("rothead"); - eyeY.pin = "24"; - eyeX.pin = "22"; - neck.pin = "12"; - rothead.pin = "13"; - - eyeY.autoDisable = true; - eyeX.autoDisable = true; - neck.autoDisable = true; - rothead.autoDisable = true; - - eyeX.minIn = 60.0; - eyeY.minIn = 60.0; - neck.minIn = 20.0; - rothead.minIn = 20.0; - - eyeX.maxIn = 120.0; - eyeY.maxIn = 120.0; - neck.maxIn = 160.0; - rothead.maxIn = 160.0; + // nothing to start - should be empty config + Runtime.startConfig(CONFIG_NAME); - eyeX.minOut = 60.0; - eyeY.minOut = 60.0; - neck.minOut = 20.0; - rothead.minOut = 20.0; - - eyeX.maxOut = 120.0; - eyeY.maxOut = 120.0; - neck.maxOut = 160.0; - rothead.maxOut = 160.0; + // starting an empty config automatically needs a runtime, and runtime + // by default starts the singleton security service + names = Runtime.getServiceNames(); + assertEquals("complete teardown should be 2 after trying to start a config runtime and security", 2, names.length); + + } + + @Test + public void testSwitchingPeer() throws IOException { + + Runtime runtime = Runtime.getInstance(); + assertNotNull(runtime); + // loading a composite service should save default config + // to the current config directory + Plan plan = Runtime.load("eyeTracking", "Tracking"); + assertNotNull(plan); - eyeY.controller = "left"; - eyeX.controller = "left"; - neck.controller = "left"; - rothead.controller = "left"; + // load eyeTracking.yml config - verify default state + TrackingConfig eyeTracking = (TrackingConfig)runtime.getConfig(CONFIG_NAME, "eyeTracking"); + TrackingConfig defaultTracking = new TrackingConfig(); + assertEquals("eyeTracking.yml values should be the same as default", defaultTracking.enabled, eyeTracking.enabled); + assertEquals("eyeTracking.yml type should be the same as default", defaultTracking.type, eyeTracking.type); + + eyeTracking = (TrackingConfig)runtime.getConfig("eyeTracking"); + assertEquals("eyeTracking.yml values should be the same as default", defaultTracking.enabled, eyeTracking.enabled); + assertEquals("eyeTracking.yml type should be the same as default", defaultTracking.type, eyeTracking.type); - PidData pidData = new PidData(); - pidData.ki = 0.001; - pidData.kp = 30.0; - pid.data.put("eyeX", pidData); - - pidData = new PidData(); - pidData.ki = 0.001; - pidData.kp = 30.0; - pid.data.put("eyeY", pidData); - - // test Runtime.useDefaults(true/false) + // load single opencv + OpenCVConfig cv = (OpenCVConfig)Runtime.load("cv", "OpenCV").get("cv"); + // default capturing is false + assertFalse(cv.capturing); + + // save as true + cv.capturing = true; + Runtime.saveConfig("cv", cv); + Runtime.load("pid", "Pid"); + eyeTracking = (TrackingConfig)runtime.getConfig("eyeTracking"); - // prune the children we don't want - plan.remove("eyeTracking.cv"); - plan.remove("headTracking.cv"); + eyeTracking.getPeer("cv").name = "cv"; + Runtime.saveConfig("eyeTracking", eyeTracking); + + // verify the peer was updated to cv + eyeTracking = (TrackingConfig)runtime.getConfig("eyeTracking"); + cv = (OpenCVConfig)runtime.getPeerConfig("eyeTracking","cv"); + // from previous save + assertTrue(cv.capturing); - plan.remove("eyeTracking.pan"); - plan.remove("headTracking.pan"); + } + + @Test + public void testChangeType() throws IOException { + Runtime runtime = Runtime.getInstance(); + Runtime.load("mouth", "MarySpeech"); + MarySpeechConfig mouth = (MarySpeechConfig)runtime.getConfig("mouth"); + mouth.listeners = new ArrayList(); + mouth.listeners.add(new Listener("publishStartSpeaking", "fakeListener")); + Runtime.saveConfig("mouth", mouth); + MarySpeechConfig mary = (MarySpeechConfig)runtime.getConfig("mouth"); + assertNotNull(mary); + assertEquals(1, mary.listeners.size()); + // save it + runtime.changeType("mouth", "LocalSpeech"); + LocalSpeechConfig local = (LocalSpeechConfig)runtime.getConfig("mouth"); + assertEquals("must have the listener", 1, local.listeners.size()); + assertTrue(local.listeners.get(0).listener.equals("fakeListener")); + } - plan.remove("eyeTracking.tilt"); - plan.remove("headTracking.tilt"); + @Test + public void testInitialLoad() { + Runtime runtime = Runtime.getInstance(); + Runtime.load("service", "Clock"); + ClockConfig clock = (ClockConfig)runtime.getConfig("service"); + assertNotNull(clock); + // replace load + Runtime.load("service", "Tracking"); + TrackingConfig tracking = (TrackingConfig)runtime.getConfig("service"); + assertNotNull(tracking); + } + + @Test + public void testChangePeerName() throws IOException { + Runtime runtime = Runtime.getInstance(); + Plan plan = Runtime.load("pollyMouth", "Polly"); + PollyConfig polly = (PollyConfig)plan.get("pollyMouth"); + Runtime.load("i01", "InMoov2"); + InMoov2Config i01 = (InMoov2Config)runtime.getConfig("i01"); + // default + MarySpeechConfig mary = (MarySpeechConfig)runtime.getPeer("i01", "mouth"); + assertNotNull(mary); + polly.listeners = mary.listeners; + Runtime.saveConfig("pollyMouth", polly); + Peer peer = i01.getPeers().get("mouth"); + peer.name = "pollyMouth"; + Runtime.saveConfig("i01", i01); + // switch to pollyMouth + PollyConfig p = (PollyConfig)runtime.getPeer("i01", "mouth"); - plan.remove("eyeTracking.pid"); - plan.remove("headTracking.pid"); + // FIXME - was going to test moving of subscriptions, however, unfortunately + // SpeechSynthesis services use a "recognizers" data instead of just simple subscriptions + // This should be fixed in the future to use standard subscriptions - plan.remove("eyeTracking.controller"); - plan.remove("headTracking.controller"); + } + + @Test + public void testSimpleServiceStart() { + Clock clock = (Clock)Runtime.start("track", "Clock"); + clock.startClock(); + clock.releaseService(); + // better be a tracking service + LocalSpeech track = (LocalSpeech)Runtime.start("track", "LocalSpeech"); + assertNotNull(track); + track.releaseService(); + // better be a clock + clock = (Clock)Runtime.create("track", "Clock"); + log.info("start"); + } - plan.remove("eyeTracking.controller.serial"); - plan.remove("headTracking.controller.serial"); + @Test + public void testPeers() { + InMoov2Head head = (InMoov2Head)Runtime.start("track", "InMoov2Head"); + Servo neck = (Servo)Runtime.getService("track.neck"); + assertNotNull(neck); + head.releaseService(); + assertNull(Runtime.getService("track.neck")); - // FIXME - THANKFULLY THIS DID NOT WORK AT ALL - // ServiceReservation sr = plan.getPeers().get("eyeTracking").get("cv"); - // sr.actualName = "cv"; - // - // sr = plan.getPeers().get("headTracking").get("cv"); - // sr.actualName = "cv"; - - // set a communal opencv source -// eyeTrackingc.cv = "cv"; -// headTrackingc.cv = "cv"; - - // assertEquals("2x tracking with merged opencv expecting 13 (7 + 7 - 2 + 1) services", 14, plan.size()); - // save the plan - Runtime.savePlan(CONFIG_NAME); - // clear the in memory plan - Runtime.clearPlan(); - - Runtime.setAllVirtual(false); + } + + @Test + public void testSaveApply() throws IOException { + Runtime runtime = Runtime.getInstance(); + Servo neck = (Servo)Runtime.start("neck", "Servo"); + ServoConfig config = neck.getConfig(); - // start the plan - Runtime.startConfig(CONFIG_NAME); - - assertNotNull(Runtime.getService("cv")); - assertNotNull(Runtime.getService("eyeTracking")); - assertNotNull(Runtime.getService("headTracking")); - - WebGui webgui = (WebGui) Runtime.create("webgui", "WebGui"); - webgui.autoStartBrowser(false); - webgui.startService(); + // Where config is "different" than member variables it + // takes an apply(config) of the config to make the service + // update its member variables, vs changing config and + // immediately getting the service behavior change. + config.idleTimeout = 5000; + // the fact this takes and additional method to process + // i think is legacy and should be changed for Servo to use + // its config "directly" + neck.apply(config); + neck.save(); + neck.releaseService(); + neck = (Servo)Runtime.start("neck", "Servo"); + assertTrue("preserved value", 5000 == neck.getConfig().idleTimeout); + + Servo servo = (Servo)Runtime.start("servo", "Servo"); + config = (ServoConfig)Runtime.load("default", "Servo").get("default"); + assertNull(config.idleTimeout); + config.idleTimeout = 7000; + Runtime.saveConfig("servo", config); + servo.apply(); + assertTrue(servo.getConfig().idleTimeout == 7000); - assertNull(Runtime.getService("eyeTracking.cv")); - assertNull(Runtime.getService("headTracking.cv")); - - Runtime.releaseConfig(CONFIG_NAME); - assertNull(Runtime.getService("cv")); - assertNull(Runtime.getService("eyeTracking")); - assertNull(Runtime.getService("headTracking")); - - // given unknown plan and config - // when i clear the plan - // its clear with no config or peers - - // clear plan - Runtime.clearPlan(); - removeConfigData(); - - plan = Runtime.getPlan(); - assertEquals("cleared plan should be 0", 1, plan.size()); - // assertEquals("cleared plan peers be 0", 0, plan.getPeers().size()); - - // simple single plan - // load is attempt to load from file - if not available load from memory - Runtime.clearPlan(); - Runtime.load("c1", "Clock"); - plan = Runtime.getPlan(); - ClockConfig clock = (ClockConfig) plan.get("c1"); - clock.interval = 3555; - assertNotNull(clock); - assertEquals(2, plan.size()); - - // FIXME - use case test individual services being saved - test setting - // configPath to non default - - // given using static runtime method saveConfig - // when called with a valid config name - // then creates /data/config/{configName} - Runtime.setConfig(CONFIG_NAME); - Clock c2 = (Clock) Runtime.start("c2", "Clock"); - c2.setInterval(5000); - c2.save(); + config.idleTimeout = 8000; + servo.apply(config); + assertTrue(servo.getIdleTimeout() == 8000); + servo.apply(); + assertTrue("filesystem servo.yml applied", servo.getIdleTimeout() == 7000); - File check = new File( runtime.getConfigPath() + File.separator + "c2.yml"); - assertTrue(check.exists()); - ClockConfig clockConfig = CodecUtils.fromYaml(FileIO.toString(runtime.getConfigPath() + File.separator + "c2.yml"), ClockConfig.class); - assertTrue(clockConfig.interval == 5000); - - // given the system is new and no previous definition exists - // when i load a default service config - // then a valid plan will exist - - Runtime.savePlan(CONFIG_NAME); - check = new File(CONFIG_PATH + File.separator + "c1.yml"); - assertTrue(check.exists()); - clockConfig = CodecUtils.fromYaml(FileIO.toString(CONFIG_PATH + File.separator + "c1.yml"), ClockConfig.class); - assertTrue(clockConfig.interval == 3555); - - Clock c1 = (Clock) Runtime.start("c1"); - assertTrue(c1.getInterval() == 3555); - c1.setInterval(3333); - c1.save(); - - clockConfig = CodecUtils.fromYaml(FileIO.toString(CONFIG_PATH + File.separator + "c1.yml"), ClockConfig.class); - assertTrue(clockConfig.interval == 3333); - - c1.setInterval(4444); - assertTrue(c1.getInterval() == 4444); - - c1.apply(); - assertTrue(c1.getInterval() == 3333); - - clockConfig = (ClockConfig) Runtime.getPlan().get("c1"); - assertTrue(clockConfig.interval == 3333); - - // FIXME - use case - when a parent is not created, but some of the children - // are ! - - // FIXME - use case Runtime.start('myservice') - - // use case - start 2 services with overlapping peers - remove one - // .. the shared services - // should last until "both" services are released - - // given i have a composite service - // when i load it - // then all services will configured by its default will be loaded - Runtime.clearPlan(); - plan = Runtime.load("track", "Tracking"); - TrackingConfig track = (TrackingConfig) plan.get("track"); - assertNotNull(track); - assertEquals("tracking is 1 service and currently has 6 subservices", 8, plan.size()); - - // Create creates it does not start - there is no starting of sub systems if - // you are creating ! - Tracking tracking = (Tracking) Runtime.create("track", "Tracking"); - assertNotNull(tracking); - tracking.startService(); - - Runtime.start("track", "Tracking"); - - // FIXME !!! - check to make sure a composite config like Tracking - // can point a peer to a different actual name e.g. opencv vs - // tracking.opencv - - // FIXME - release and verify release of plan - - ServoConfig track_pan = (ServoConfig) plan.get("track.pan"); - assertNotNull(track_pan); - - if (!Runtime.saveConfig(null)) { - // good should not be able to save to null - // TODO : round robin buffer and index into - Status error = runtime.getLastError(); - log.info(error.toString()); - } - - assertFalse(Runtime.saveConfig(null)); - - // given i have a valid plan - // then i save the plan - // the files exist - Runtime.savePlan(CONFIG_NAME); - - check = new File(CONFIG_PATH); - - assertTrue(check.exists()); - assertTrue(check.isDirectory()); - - check = new File(Runtime.getInstance().getConfigPath() + File.separator + "track.yml"); - assertTrue(check.exists()); - check = new File(Runtime.getInstance().getConfigPath() + File.separator + "track.cv.yml"); - assertTrue(check.exists()); - - // TODO - check file details - // for (File f : configFiles) { - // - // } - - // TODO -verify that config yml files do not affect a Runtime.start() - - // remove the config data - removeConfigData(); - - // given a valid plan - // when Runtime.start() - // the defined services all start - FIXME - they better not be pulling in - // filesystem data ! - - Runtime.saveConfig(CONFIG_NAME); - - check = new File("data" + File.separator + "config" + File.separator + CONFIG_NAME); - - assertTrue(check.exists()); - assertTrue(check.isDirectory()); - - // given a Clock config exists and matches c1.yml - // when I load c1 - // then the c1.yml gets loaded into the plan - - // given i have a config file called c1 on the file system "THAT IS" a - // LocalSpeech ! - // when I Runtime.start( c1, Clock) - // a clock is loaded - - // given i have a valid plan - // when there is no yml file data - // then i can start a service from the plan - - // load a simple plan - no config set specified - name and type supplied - // load default embedded Clock meta data from code to memory - - // validate default config in plan - // for (String s : plan.keySet()) { - // assertEquals("c1", s); - // ClockConfig cc = (ClockConfig) plan.get("c1"); - // assertNotNull(cc); - // assertEquals("Clock", cc.type); - // } - - // TODO - saveAll vs runtime.save() - - // Runtime.start("webgui", "WebGui"); - - // FIXME implement & test these - // Runtime.loadConfigPath(CONFIG_NAME); - // Runtime.start(); - } + } \ No newline at end of file diff --git a/src/test/java/org/myrobotlab/framework/ServiceLifeCycleTest.java b/src/test/java/org/myrobotlab/framework/ServiceLifeCycleTest.java index ebde72cf82..a563abf1ff 100644 --- a/src/test/java/org/myrobotlab/framework/ServiceLifeCycleTest.java +++ b/src/test/java/org/myrobotlab/framework/ServiceLifeCycleTest.java @@ -6,8 +6,10 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import java.io.IOException; import java.util.List; +import org.junit.Before; import org.junit.Test; import org.myrobotlab.framework.interfaces.ServiceInterface; import org.myrobotlab.logging.LoggerFactory; @@ -29,26 +31,25 @@ public class ServiceLifeCycleTest extends AbstractTest { public final static Logger log = LoggerFactory.getLogger(ServiceLifeCycleTest.class); + + @Before /* before each test */ + public void setUp() throws IOException { + // remove all services - also resets config name to DEFAULT effectively + Runtime.releaseAll(true, true); + // clean our config directory + Runtime.removeConfig("ServiceLifeCycleTest"); + // set our config + Runtime.setConfig("ServiceLifeCycleTest"); + } + @Test public void serviceLifeCycleTest() throws Exception { - // clear plan - Runtime.clearPlan(); // load a simple plan Runtime.load("c1", "Clock"); - Plan plan = Runtime.getPlan(); - // 1 clock and 1 runtime - assertEquals(2, plan.size()); - - // clear plan - Runtime.clearPlan(); - - plan = Runtime.getPlan(); - assertEquals(1, plan.size()); - - plan = Runtime.load("controller", "Arduino"); + Plan plan = Runtime.load("controller", "Arduino"); ArduinoConfig ac = (ArduinoConfig) plan.get("controller"); assertFalse(ac.connect); // 1 arduino 1 serial @@ -66,8 +67,6 @@ public void serviceLifeCycleTest() throws Exception { assertNull(Runtime.getService("controller")); assertNull(Runtime.getService("controller.serial")); - Runtime.clearPlan(); - /** * use case - load a default config - modify it substantially then start the * service, with worky peers @@ -97,7 +96,6 @@ public void serviceLifeCycleTest() throws Exception { Runtime.release("track"); assertNull(Runtime.getService("track.controller")); - Runtime.clearPlan(); plan = Runtime.load("i02", "InMoov2"); log.info("plan has {} services", plan.size()); @@ -119,9 +117,8 @@ public void serviceLifeCycleTest() throws Exception { i02.startPeer("eyeTracking"); assertEquals("i02.eyeTracking", i02.getPeerName("eyeTracking")); - - Runtime.load("webgui", "WebGui"); - WebGuiConfig webgui = (WebGuiConfig) plan.get("webgui"); + + WebGuiConfig webgui = (WebGuiConfig)Runtime.load("webgui", "WebGui").get("webgui"); webgui.autoStartBrowser = false; // start it up Runtime.startConfig("webgui"); @@ -137,7 +134,6 @@ public void serviceLifeCycleTest() throws Exception { LocalSpeech mouth = (LocalSpeech) i02.getPeer("mouth"); assertNotNull(mouth); - // FIXME i02.releasePeers() log.info("done"); diff --git a/src/test/java/org/myrobotlab/integration/InMoovScriptTest.java b/src/test/java/org/myrobotlab/integration/InMoovScriptTest.java index 4bb777d6b0..cb3ab8d399 100644 --- a/src/test/java/org/myrobotlab/integration/InMoovScriptTest.java +++ b/src/test/java/org/myrobotlab/integration/InMoovScriptTest.java @@ -18,7 +18,7 @@ /** * * This test was intended to test variations of the inmoov scripts. - * It's ignored now until we can have some similar test coverage + * It's ignored now until we can have some similar test coverage * with the InMoov2 implementatoin. */ @Ignore @@ -98,7 +98,7 @@ public void testInMoovScript() throws IOException { // test virtual inmoov // tear down - Runtime.releaseAll(); + Runtime.releaseAll(true, true); } } diff --git a/src/test/java/org/myrobotlab/opencv/AbstractOpenCVFilterTest.java b/src/test/java/org/myrobotlab/opencv/AbstractOpenCVFilterTest.java index cd3ee4452b..8e6f60b7af 100755 --- a/src/test/java/org/myrobotlab/opencv/AbstractOpenCVFilterTest.java +++ b/src/test/java/org/myrobotlab/opencv/AbstractOpenCVFilterTest.java @@ -36,7 +36,7 @@ public abstract class AbstractOpenCVFilterTest extends AbstractTest { public int numFrames = 0; private CloseableFrameConverter converter1 = new CloseableFrameConverter(); private CloseableFrameConverter converter2 = new CloseableFrameConverter(); - + @Test public void testFilter() throws InterruptedException { List filters = createFilters(); @@ -44,18 +44,19 @@ public void testFilter() throws InterruptedException { List inputs = createTestImages(); numFrames = inputs.size(); OpenCV opencv = (OpenCV) Runtime.start("opencv", "OpenCV"); - + for (OpenCVFilter filter : filters) { // Verify that the filters can be serialized! String json = CodecUtils.toJson(filter); assertNotNull(json); filter.setOpenCV(opencv); } - + for (IplImage input : inputs) { frameIndex++; long now = System.currentTimeMillis(); - // create the OpenCVData object that will run with this image through the pipeline. + // create the OpenCVData object that will run with this image through the + // pipeline. OpenCVData data = new OpenCVData(CVSERVICENAME, now, frameIndex, converter1.toFrame(input)); for (OpenCVFilter filter : filters) { if (debug) { @@ -86,7 +87,6 @@ public void testFilter() throws InterruptedException { Runtime.release("opencv"); // other stuff that comes along with runtime to shutdown. Runtime.release("security"); - // Runtime.releaseAll(); } // clean up the other runtime stuffs } @@ -105,7 +105,8 @@ public List createTestImages() { return images; } - private IplImage processTestImage(OpenCVFilter filter, OpenCVData data, IplImage input, int frameIndex) throws InterruptedException { + private IplImage processTestImage(OpenCVFilter filter, OpenCVData data, IplImage input, int frameIndex) + throws InterruptedException { filter.setData(data); // call process on the filter with the input image. long start = System.currentTimeMillis(); @@ -117,8 +118,7 @@ private IplImage processTestImage(OpenCVFilter filter, OpenCVData data, IplImage filter.displayEnabled = true; // verify that processDisplay doesn't blow up! BufferedImage bi = filter.processDisplay(); - - + if (debug) { IplImage displayVal = converter2.toImage(bi); outputImage = filter.show(displayVal, "Filter " + filter.name + " Output Image " + frameIndex); diff --git a/src/test/java/org/myrobotlab/process/LauncherTest.java b/src/test/java/org/myrobotlab/process/LauncherTest.java index 2c3745575e..fe275a3053 100644 --- a/src/test/java/org/myrobotlab/process/LauncherTest.java +++ b/src/test/java/org/myrobotlab/process/LauncherTest.java @@ -9,6 +9,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; import org.myrobotlab.framework.CmdOptions; import org.myrobotlab.logging.LoggerFactory; @@ -17,6 +18,8 @@ import picocli.CommandLine; + +@Ignore public class LauncherTest extends AbstractTest { public final static Logger log = LoggerFactory.getLogger(LauncherTest.class); @@ -46,7 +49,7 @@ static public String toString(String[] args) { @Test public void test() throws IllegalArgumentException, IllegalAccessException, IOException, URISyntaxException, InterruptedException, ParseException { - Launcher.main(new String[] {"-s", "runtime", "Runtime" }); + Launcher.main(new String[] { "-s", "runtime", "Runtime" }); String help = Launcher.mainHelp(); diff --git a/src/test/java/org/myrobotlab/programab/OOBPayloadTest.java b/src/test/java/org/myrobotlab/programab/OOBPayloadTest.java deleted file mode 100644 index 3761bc5c29..0000000000 --- a/src/test/java/org/myrobotlab/programab/OOBPayloadTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.myrobotlab.programab; - -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; - -import org.junit.Test; -import org.myrobotlab.test.AbstractTest; - -public class OOBPayloadTest extends AbstractTest { - - @Test - public void basicTestOOBPayload() { - // test the getters/setters of oob object. - OOBPayload payload = new OOBPayload(); - payload.setMethodName("foo"); - ArrayList params = new ArrayList(); - payload.setParams(params); - payload.setServiceName("fooservice"); - - assertEquals(payload.getMethodName(), "foo"); - assertEquals(payload.getParams(), params); - assertEquals(payload.getServiceName(), "fooservice"); - - OOBPayload pl2 = new OOBPayload("boo2", "fooo2", params); - assertEquals(pl2.getMethodName(), "fooo2"); - assertEquals(pl2.getParams(), params); - assertEquals(pl2.getServiceName(), "boo2"); - - } - -} diff --git a/src/test/java/org/myrobotlab/service/ArduinoMotorPotTest.java b/src/test/java/org/myrobotlab/service/ArduinoMotorPotTest.java index 3493779590..ffe514e72c 100644 --- a/src/test/java/org/myrobotlab/service/ArduinoMotorPotTest.java +++ b/src/test/java/org/myrobotlab/service/ArduinoMotorPotTest.java @@ -74,7 +74,7 @@ private String join(ArrayList list, String joinChar) { public void onSensorData(SensorData event) { // about we downsample this call? - int[] data = (int[]) event.getData(); + int[] data = (int[]) event.data; count++; int value = data[0]; log.info("Data: {}", data); diff --git a/src/test/java/org/myrobotlab/service/ArduinoTest.java b/src/test/java/org/myrobotlab/service/ArduinoTest.java index 5dff17f399..7a6422d29d 100644 --- a/src/test/java/org/myrobotlab/service/ArduinoTest.java +++ b/src/test/java/org/myrobotlab/service/ArduinoTest.java @@ -20,7 +20,6 @@ import org.myrobotlab.service.data.DeviceMapping; import org.myrobotlab.service.interfaces.PinArrayListener; import org.myrobotlab.service.interfaces.PinDefinition; -import org.myrobotlab.service.interfaces.PinListener; import org.myrobotlab.service.interfaces.SerialDevice; import org.myrobotlab.test.AbstractTest; import org.slf4j.Logger; @@ -218,7 +217,7 @@ public final void pinArrayTest() { sleep(200); arduino01.reset(); // Wait for reset to complete - sleep(100); + sleep(200); assertTrue("did not get pin array data D10", catcher.containsPinArrayFromPin(arduino01.getPin(10).getPinName())); assertTrue("did not get pin array data D12", catcher.containsPinArrayFromPin(arduino01.getPin(12).getPinName())); @@ -471,8 +470,10 @@ public void testPin() throws Exception { arduino01.attachPinListener(catcher); arduino01.enablePin(pin.getAddress()); - sleep(100); + sleep(200); arduino01.disablePin(pin.getAddress()); + assertNotNull("should not be null", catcher.pinData); + assertNotNull("should not be null2", catcher.pinData.pin); assertTrue("did not receive pin data int", catcher.pinData.pin.equals(analogPin)); catcher.pinData = null; diff --git a/src/test/java/org/myrobotlab/service/ClockTest.java b/src/test/java/org/myrobotlab/service/ClockTest.java index 83de45d7fa..5a229ef89f 100644 --- a/src/test/java/org/myrobotlab/service/ClockTest.java +++ b/src/test/java/org/myrobotlab/service/ClockTest.java @@ -91,7 +91,7 @@ public void testService() throws Exception { clock.restartClock(); // wait now for the event - Service.sleep(1100); + Service.sleep(1500); assertNotNull("should have event now", gateway.getMsg("mocker", "onEpoch")); clock.restartClock(); Service.sleep(500); diff --git a/src/test/java/org/myrobotlab/service/HarryTest.java b/src/test/java/org/myrobotlab/service/HarryTest.java index 29527a0845..b635ea2a6b 100755 --- a/src/test/java/org/myrobotlab/service/HarryTest.java +++ b/src/test/java/org/myrobotlab/service/HarryTest.java @@ -144,7 +144,8 @@ public void testDynamic() throws SolrServerException, IOException, InterruptedEx solr.startEmbedded(); createAIML(); ProgramAB harry = (ProgramAB) Runtime.start("harry", "ProgramAB"); - harry.startSession("testbots", "username", "test"); + harry.setBotType("testbots"); + harry.startSession("username", "test", true); goLearnStuff(solr, harry); @@ -166,7 +167,8 @@ public void testHarry() throws Exception { solr.startEmbedded(); createAIML(); ProgramAB harry = (ProgramAB) Runtime.start("harry", "ProgramAB"); - harry.startSession("testbots", "username", "test"); + harry.setSession("username", "test"); + // harry.startSession("username", "test"); // start the opencv service with the yolo filter. OpenCV cv = (OpenCV) Runtime.start("cv", "OpenCV"); diff --git a/src/test/java/org/myrobotlab/service/ProgramABTest.java b/src/test/java/org/myrobotlab/service/ProgramABTest.java index b6b7aa494c..674b689bb6 100644 --- a/src/test/java/org/myrobotlab/service/ProgramABTest.java +++ b/src/test/java/org/myrobotlab/service/ProgramABTest.java @@ -1,99 +1,150 @@ package org.myrobotlab.service; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.List; import java.util.Map; -import org.apache.commons.io.FileUtils; import org.junit.After; import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; -import org.junit.Rule; +import org.junit.BeforeClass; import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.myrobotlab.framework.Service; +import org.myrobotlab.framework.Message; import org.myrobotlab.io.FileIO; import org.myrobotlab.logging.LoggerFactory; +import org.myrobotlab.programab.BotInfo; import org.myrobotlab.programab.Response; +import org.myrobotlab.programab.Session; import org.myrobotlab.service.data.Locale; +import org.myrobotlab.service.data.Utterance; import org.slf4j.Logger; -/** - * ProgramABTest - This is the unit test to validate that the ProgramAB service continues - * to behave as expected. - * - * It tests the behavior of some reference sets of aiml. - * - * There are 2 AIML sets used in this unit test - * lloyd and pikachu. - * - * The lloyd aiml set is used to test most functionality, - * the pikachu aiml set is used to validate that japanese tokenization is working - * - */ -public class ProgramABTest extends AbstractServiceTest { +public class ProgramABTest { public final static Logger log = LoggerFactory.getLogger(ProgramABTest.class); - - private String path = null; - private ProgramAB testService; - - // the botname and test username to use (for most of the test suite) - private String botname = "lloyd"; - private String username = "testUser"; - // The aiml sets will be modified in this test, to avoid the junit annotation makes sure this will - // be cleaned up after test execution. - // watch for open file handles which will prevent these directories from being deleted. - @Rule - public TemporaryFolder testFolder = new TemporaryFolder(); - // the location of the test bot folders containing the aiml an dother configs. + static protected final String PIKACHU = "pikachu"; + + static protected final String LLOYD = "lloyd"; + private String testResources = "src/test/resources/ProgramAB"; - - public Service createService() { - - // let's do a file copy to the temp folder. - try { - FileUtils.copyDirectory(new File(testResources), testFolder.getRoot()); - log.info("Using JUnit temp folder: {}", testFolder); - } catch (IOException e) { - log.error("create temp folder threw", e); - return null; - } - - try { - // LoggingFactory.init("INFO"); - log.info("Setting up the Program AB Service ########################################"); - // Load the service under test - // a test robot - // TODO: this should probably be created by Runtime, - // OOB tags might not know what the service name is ?! - testService = (ProgramAB) Runtime.start(botname, "ProgramAB"); - // Important, tell this service to use the test folder as it's path for loading bots. - testService.setPath(testFolder.getRoot().getAbsolutePath()); - // start the service. - testService.startService(); - // load the bot brain for the chat with the user - testService.startSession(username, botname); - } catch (Exception e) { - log.error("createService threw", e); + + static private ProgramAB lloyd; + + static private ProgramAB pikachu; + + private String username = "testUser"; + + // This method runs once before any test method in the class + @BeforeClass + public static void setUpClass() { + System.out.println("BeforeClass - Runs once before any test method"); + lloyd = (ProgramAB) Runtime.start(LLOYD, "ProgramAB"); + pikachu = (ProgramAB) Runtime.start(PIKACHU, "ProgramAB"); + + // very first inits - all should work ! + assertTrue("4+ standard", lloyd.getBots().size() >= 4); + // should require very little to start ! - this is a requirement ! + Response response = lloyd.getResponse("Hi"); + + // expect Alice's aiml processed + assertTrue(response.msg.startsWith("Hi")); + + Session session = lloyd.getSession(); + assertEquals("default user should be human", "human", session.getUsername()); + assertEquals("default botType should be Alice", "Alice", session.getBotType()); + + } + + // This method runs once after all test methods in the class have been + // executed + @AfterClass + public static void tearDownClass() { + Runtime.release(LLOYD); + Runtime.release(PIKACHU); + } + + // This method runs before each test method + @Before + public void setUp() { + System.out.println("Before - Runs before each test method"); + // Perform setup tasks specific to each test method + + // add a couple test bots + List bots = lloyd.scanForBots(testResources + "/bots"); + assertTrue("2+ test bots", bots.size() >= 2); + assertTrue("6+ bots total", lloyd.getBots().size() >= 6); + + pikachu.scanForBots(testResources + "/bots"); + + // validate newly created programab can by default start a session + Session session = lloyd.getSession(); + assertNotNull(session); + + lloyd.setBotType("lloyd"); + assertEquals("lloyd", lloyd.getBotType()); + + // validate error is called when invalid bot type set + + // load the bot brain for the chat with the user + lloyd.setSession(username, LLOYD); + assertEquals(username, lloyd.getUsername()); + // clean out any aimlif the bot that might + // have been saved in a previous test run! + String aimlIFPath = testResources + "/bots/" + LLOYD + "/aimlif"; + File aimlIFPathF = new File(aimlIFPath); + if (aimlIFPathF.isDirectory()) { + for (File f : aimlIFPathF.listFiles()) { + // if there's a file here. + log.info("Deleting pre-existing AIMLIF files : {}", f.getAbsolutePath()); + f.delete(); + } } - return testService; } - + + @Test + public void testOnUtterance() throws Exception { + + MockGateway gateway = (MockGateway) Runtime.start("gateway", "MockGateway"); + lloyd = (ProgramAB)Runtime.start("lloyd", "ProgramAB"); + lloyd.addListener("publishUtterance", "mocker@mockId"); + + Utterance utterance = new Utterance(); + utterance.username = "human"; + utterance.text = "HELLO"; + utterance.channelBotName = "Mr.Turing"; + + gateway.sendWithDelay("lloyd", "onUtterance", utterance); + Message msg = gateway.waitForMsg("mocker", "onUtterance", 50); + assertNotNull(msg); + assertTrue( ((Utterance) msg.data[0]).text.startsWith("Hi")); + } + public void addCategoryTest() throws IOException { - testService.addCategory("BOOG", "HOWDY"); - Response resp = testService.getResponse(username, "BOOG"); + lloyd.addCategory("BOOG", "HOWDY"); + Response resp = lloyd.getResponse(username, "BOOG"); assertTrue(resp.msg.equals("HOWDY")); } - public void listPatternsTest() { - ArrayList res = testService.listPatterns(botname); + @Test + public void testAddCategoryTest() throws IOException { + lloyd.addCategory("ABCDEF", "ABCDEF"); + // String username = lloyd.getUsername(); + // username, + Response resp = lloyd.getResponse("ABCDEF"); + assertTrue(resp.msg.equals("ABCDEF")); + } + + @Test + public void testListPatterns() { + ArrayList res = lloyd.listPatterns(LLOYD); assertTrue(res.size() > 0); } @@ -102,118 +153,92 @@ public void listPatternsTest() { // stuff // @Test public void pannousTest() throws IOException { - Response resp = testService.getResponse(username, "SHOW ME INMOOV"); + Response resp = lloyd.getResponse(username, "SHOW ME INMOOV"); // System.out.println(resp); boolean contains = resp.msg.contains("http"); assertTrue(contains); } - @Before - public void setUp() { - // TODO: set the location for the temp folder via : - // System.getProperty("java.io.tmpdir") - // LoggingFactory.init("INFO"); - // testFolder.getRoot().getAbsolutePath() - try { - this.path = testFolder.getRoot().getAbsolutePath() + File.separator + "ProgramAB"; - FileIO.copy(testResources, path); - } catch (IOException e) { - log.warn("Error extracting resources for test. {}", testResources); - Assert.assertNotNull(e); - } - } - - public void sraixOOBTest() throws IOException { - // Response resp = testService.getResponse(username, "MRLSRAIX"); - // System.out.println(resp); - // boolean contains = resp.msg.contains("foobar"); - // assertTrue(contains); - Response resp = testService.getResponse(username, "OOBMRLSRAIX"); - // System.out.println(resp); + @Test + public void testSraixOOB() throws IOException { + Response resp = lloyd.getResponse(username, "OOBMRLSRAIX"); boolean contains = resp.msg.contains("You are talking to lloyd"); assertTrue(contains); } - public void sraixTest() throws IOException { + @Test + public void testSraix() throws IOException { if (Runtime.hasInternet()) { - Response resp = testService.getResponse(username, "MRLSRAIX"); - //Response resp = testService.getResponse(username, "Why is the sky blue?"); - // System.out.println(resp); - // System.out.println(resp); + Response resp = lloyd.getResponse(username, "MRLSRAIX"); boolean contains = resp.msg.contains("information"); assertTrue(contains); } } + @Test public void testAddEntryToSetAndMaps() throws IOException { // TODO: This does NOT work yet! - Response resp = testService.getResponse(username, "Add Jabba to the starwarsnames set"); + Response resp = lloyd.getResponse(username, "Add Jabba to the starwarsnames SET"); assertEquals("Ok...", resp.msg); - resp = testService.getResponse(username, "Add jabba equals Jabba the Hut to the starwars map"); + resp = lloyd.getResponse(username, "Add jabba equals Jabba the Hut to the starwars MAP"); assertEquals("Ok...", resp.msg); - resp = testService.getResponse(username, "DO YOU LIKE Jabba?"); + resp = lloyd.getResponse(username, "DO YOU LIKE Jabba?"); assertEquals("Jabba the Hut is awesome.", resp.msg); // TODO : re-enable this one? // now test creating a new set. - resp = testService.getResponse(username, "Add bourbon to the whiskey set"); + resp = lloyd.getResponse(username, "Add bourbon to the whiskey SET"); assertEquals("Ok...", resp.msg); - resp = testService.getResponse(username, "NEWSETTEST bourbon"); + resp = lloyd.getResponse(username, "NEWSETTEST bourbon"); // assertEquals("bourbon is a whiskey", resp.msg); } @Test - public void testJapanese() throws IOException, InterruptedException { - ProgramAB pikachu = (ProgramAB) Runtime.start("pikachu", "ProgramAB"); - pikachu.setPath(path); - // pikachu the service. - pikachu.startService(); + public void testJapanese() throws IOException { + pikachu.scanForBots(testResources + "/bots"); + pikachu.setBotType("pikachu"); + // setting Japanese locality + pikachu.setLocale("ja"); // load the bot brain for the chat with the user - pikachu.startSession(path, username, "pikachu", new java.util.Locale("ja")); + pikachu.setSession(username, PIKACHU); Response resp = pikachu.getResponse("私はケビンです"); assertEquals("あなたに会えてよかったケビン", resp.msg); - // Release the service we just created - pikachu.releaseService(); + Runtime.release(PIKACHU); } + @Test public void testLearn() throws IOException { - // Response resp1 = testService.getResponse(session, "SET FOO BAR"); - // System.out.println(resp1.msg); - Response resp = testService.getResponse(username, "LEARN AAA IS BBB"); - // System.out.println(resp.msg); - resp = testService.getResponse(username, "WHAT IS AAA"); + Response resp = lloyd.getResponse(username, "LEARN AAA IS BBB"); + resp = lloyd.getResponse(username, "WHAT IS AAA"); assertEquals("BBB", resp.msg); } @Test public void testMultiSession() throws IOException { ProgramAB lloyd = (ProgramAB) Runtime.start("lloyd", "ProgramAB"); - lloyd.setPath(path); - // pikachu the service. - lloyd.startService(); + lloyd.setBotType("lloyd"); // load the bot brain for the chat with the user - lloyd.startSession(path, "user1", "lloyd"); + lloyd.setSession("user1", "lloyd"); Response res = lloyd.getResponse("My name is Kevin"); System.out.println(res); - lloyd.startSession(path, "user2", "lloyd"); + lloyd.setSession("user2", "lloyd"); res = lloyd.getResponse("My name is Grog"); System.out.println(res); - lloyd.startSession(path, "user1", "lloyd"); + lloyd.setSession("user1", "lloyd"); Response respA = lloyd.getResponse("What is my name?"); System.out.println(respA); - lloyd.startSession(path, "user2", "lloyd"); + lloyd.setSession("user2", "lloyd"); Response respB = lloyd.getResponse("What is my name?"); System.out.println(respB); - - assertEquals("Kevin", respA.msg); - assertEquals("Grog", respB.msg); - - // release this service. - lloyd.releaseService(); - + lloyd.setSession(username, LLOYD); + assertEquals("Kevin.", respA.msg); + assertEquals("Grog.", respB.msg); } + @Test public void testOOBTags() throws Exception { - Response resp = testService.getResponse(username, "OOB TEST"); + + ProgramAB lloyd = (ProgramAB) Runtime.start("lloyd", "ProgramAB"); + Response resp = lloyd.getResponse(username, "OOB TEST"); assertEquals("OOB Tag Test", resp.msg); // TODO figure a mock object that can wait on a callback to let us know the @@ -221,164 +246,151 @@ public void testOOBTags() throws Exception { // wait up to 5 seconds for python service to start long maxWait = 6000; int i = 0; - Python python = (Python)Runtime.start("python", "Python"); - while (Runtime.getService("python") == null) { + + while (Runtime.getService("oobclock") == null) { Thread.sleep(100); - log.info("Waiting for python to start..."); + log.info("waiting for oobclock to start..."); i++; if (i > maxWait) { - Assert.assertFalse("Took too long to process OOB tag", i > maxWait); + Assert.assertFalse("took too long to process OOB tag", i > maxWait); } } - Assert.assertNotNull(Runtime.getService("python")); - - python.releaseService(); - + Assert.assertNotNull(Runtime.getService("oobclock")); + Runtime.release("oobclock"); } + @Test public void testPredicates() { // test removing the predicate if it exists - testService.setPredicate(username, "name", "foo1"); - String name = testService.getPredicate(username, "name"); + lloyd.setPredicate(username, "name", "foo1"); + String name = lloyd.getPredicate(username, "name"); // validate it's set properly assertEquals("foo1", name); - testService.removePredicate(username, "name"); + lloyd.removePredicate(username, "name"); // validate the predicate doesn't exist - name = testService.getPredicate(username, "name"); + name = lloyd.getPredicate(username, "name"); // TODO: is this valid? one would expect it would return null. assertEquals("unknown", name); // set a predicate - testService.setPredicate(username, "name", "foo2"); - name = testService.getPredicate(username, "name"); + lloyd.setPredicate(username, "name", "foo2"); + name = lloyd.getPredicate(username, "name"); // validate it's set properly assertEquals("foo2", name); } + @Test public void testProgramAB() throws Exception { // a response - Response resp = testService.getResponse(username, "UNIT TEST PATTERN"); + Response resp = lloyd.getResponse(username, "UNIT TEST PATTERN"); // System.out.println(resp.msg); assertEquals("Unit Test Pattern Passed", resp.msg); } + @Test public void testSavePredicates() throws IOException { long uniqueVal = System.currentTimeMillis(); String testValue = String.valueOf(uniqueVal); - Response resp = testService.getResponse(username, "SET FOO " + testValue); + Response resp = lloyd.getResponse(username, "SET FOO " + testValue); assertEquals(testValue, resp.msg); - testService.savePredicates(); - testService.reloadSession(username, botname); - resp = testService.getResponse(username, "GET FOO"); + lloyd.savePredicates(); + lloyd.reloadSession(username, LLOYD); + resp = lloyd.getResponse(username, "GET FOO"); assertEquals("FOO IS " + testValue, resp.msg); } - @Override - public void testService() throws Exception { - // run each of the test methods. - testProgramAB(); - testOOBTags(); - testSavePredicates(); - testPredicates(); - testLearn(); - testSets(); - testSetsAndMaps(); - testAddEntryToSetAndMaps(); - testTopicCategories(); - umlautTest(); - listPatternsTest(); - // This following test is known to be busted.. - // pannousTest(); - addCategoryTest(); - sraixOOBTest(); - sraixTest(); // this should call out to wikipedia for info about Claude Shannon. - // on pannous bots - testUppercase(); - } - + @Test public void testUppercase() { // test a category where the aiml tag is uppercased. - Response resp = testService.getResponse(username, "UPPERCASE"); + Response resp = lloyd.getResponse(username, "UPPERCASE"); assertEquals("Passed", resp.msg); } + @Test public void testSets() throws IOException { - Response resp = testService.getResponse(username, "SETTEST CAT"); + Response resp = lloyd.getResponse(username, "SETTEST CAT"); assertEquals("An Animal.", resp.msg); - resp = testService.getResponse(username, "SETTEST MOUSE"); + resp = lloyd.getResponse(username, "SETTEST MOUSE"); assertEquals("An Animal.", resp.msg); - resp = testService.getResponse(username, "SETTEST DOG"); + resp = lloyd.getResponse(username, "SETTEST DOG"); // System.out.println(resp.msg); assertEquals("An Animal.", resp.msg); } + @Test public void testSetsAndMaps() throws IOException { - Response resp = testService.getResponse(username, "DO YOU LIKE Leah?"); + Response resp = lloyd.getResponse(username, "DO YOU LIKE Leah?"); assertEquals("Princess Leia Organa is awesome.", resp.msg); - resp = testService.getResponse(username, "DO YOU LIKE Princess Leah?"); + resp = lloyd.getResponse(username, "DO YOU LIKE Princess Leah?"); assertEquals("Princess Leia Organa is awesome.", resp.msg); } + @Test public void testTopicCategories() throws IOException { + lloyd.removePredicate(username, "topic"); + String topic = lloyd.getTopic(); + assertEquals("unknown", topic); // Top level definition - Response resp = testService.getResponse(username, "TESTTOPICTEST"); + Response resp = lloyd.getResponse(username, "TESTTOPICTEST"); assertEquals("TOPIC IS unknown", resp.msg); - resp = testService.getResponse(username, "SET TOPIC TEST"); - resp = testService.getResponse(username, "TESTTOPICTEST"); + resp = lloyd.getResponse(username, "SET TOPIC TEST"); + resp = lloyd.getResponse(username, "TESTTOPICTEST"); assertEquals("TEST TOPIC RESPONSE", resp.msg); // maybe we can still fallback to non-topic responses. - resp = testService.getResponse(username, "HI"); + resp = lloyd.getResponse(username, "HI"); assertEquals("Hello user!", resp.msg); // TODO: how the heck do we unset a predicate from AIML? - testService.unsetPredicate(username, "topic"); - resp = testService.getResponse(username, "TESTTOPICTEST"); + lloyd.removePredicate(username, "topic"); + resp = lloyd.getResponse(username, "TESTTOPICTEST"); assertEquals("TOPIC IS unknown", resp.msg); } - public void umlautTest() throws IOException { - Response resp = testService.getResponse(username, "Lars Ümlaüt"); + @Test + public void testUmlaut() throws IOException { + Response resp = lloyd.getResponse(username, "Lars Ümlaüt"); + // @GroG says - "this is not working" assertEquals("He's a character from Guitar Hero!", resp.msg); } @Test public void testLocales() { - // have locales - ProgramAB pikachu = (ProgramAB)Runtime.start("pikachu", "ProgramAB"); - // lloyd.setPath(path); - pikachu.addBotsDir(path + File.separator + "bots"); - pikachu.setCurrentBotName("pikachu"); - Map locales = pikachu.getLocales(); - assertTrue(locales.size() > 0); + ProgramAB lloyd = (ProgramAB) Runtime.start("pikachu", "ProgramAB"); + lloyd.addBots(testResources + "/" + "bots"); + lloyd.setBotType("pikachu"); + Map locales = lloyd.getLocales(); assertTrue(locales.containsKey("ja")); // release the service we created in this method. pikachu.releaseService(); } @Test - public void testReload() { - // FIXME - TODO - // reload bot creates a new bot leaves old references :( - // verify reload - /* - * Preferably with default bot ProgramAB lloyd = - * (ProgramAB)Runtime.start("lloyd", "ProgramAB"); // did not work because - * lloyd is lame // lloyd.getResponse("my name is george"); Response - * response = lloyd.getResponse("what is my name?"); - * - * BotInfo botInfo = lloyd.getBotInfo(); Bot oldBot = botInfo.getBot(); - * lloyd.reload(); Bot newBotInfo = botInfo.getBot(); - * assertNotEquals(oldBot, newBotInfo); - * - * response = lloyd.getResponse("what is my name?"); - * assertTrue(response.msg.contains("george")); - */ + public void testReload() throws IOException { + lloyd.getResponse("my name is george"); + Response response = lloyd.getResponse("what is my name?"); + + BotInfo botInfo = lloyd.getBotInfo(); + + String newFile = botInfo.path.getAbsolutePath() + File.separator + "aiml" + File.separator + "newFileCategory.aiml"; + String newFileCategory = "RELOAD"; + FileIO.toFile(newFile, newFileCategory); + + lloyd.reload(); + + response = lloyd.getResponse("RELOAD"); + assertTrue(response.msg.contains("I have reloaded")); + + response = lloyd.getResponse("what is my name?"); + assertTrue(response.msg.contains("george")); + + // clean out file + new File(newFile).delete(); + } @Test public void testDefaultSession() throws IOException { // minimal startup - create the service get a response ProgramAB lloyd = (ProgramAB) Runtime.start("lloyd", "ProgramAB"); - lloyd.setPath(path); - lloyd.setCurrentBotName("lloyd"); + lloyd.setBotType("lloyd"); assertTrue(lloyd.getBots().size() > 0); // test for a response Response response = lloyd.getResponse("Hello"); @@ -394,7 +406,7 @@ public void testDefaultSession() throws IOException { // TODO - tests // ProgramAB starts - it should find its own bot info's // set username = default - // set botname = what is available if NOT set + // set LLOYD = what is available if NOT set // getResponse() -> if current session doesn't exist - get bot // if current bot doesn't exist - attempt to activate it // test - absolute minimal setup and getResponse ... 2 lines ? 1? diff --git a/src/test/java/org/myrobotlab/service/ProgramABTest2.java b/src/test/java/org/myrobotlab/service/ProgramABTest2.java new file mode 100644 index 0000000000..717f4295d6 --- /dev/null +++ b/src/test/java/org/myrobotlab/service/ProgramABTest2.java @@ -0,0 +1,106 @@ +package org.myrobotlab.service; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.myrobotlab.logging.LoggerFactory; +import org.myrobotlab.programab.Session; +import org.slf4j.Logger; + +public class ProgramABTest2 { + + transient public final static Logger log = LoggerFactory.getLogger(ProgramABTest2.class); + + private ProgramAB programab; + public static final String LLOYD = "lloyd"; + public static final String PIKACHU = "pikachu"; + public static final String TEST_RESOURCE_ROOT = "./src/test/resources"; + public static final String BOTS_DIR = TEST_RESOURCE_ROOT + "/ProgramAB/bots"; + public static final String LLOYD_BOTDIR = BOTS_DIR + "/" + LLOYD; + public static final String PIKACHU_BOTDIR = BOTS_DIR + "/" + PIKACHU; + + @Before + public void setUp() { + programab = (ProgramAB) Runtime.start("lloyd", "ProgramAB"); + + // add 2 bot path references + programab.addBot(LLOYD_BOTDIR); + programab.addBot(PIKACHU_BOTDIR); + List botsTypes = programab.getBots(); + assertEquals("should have 6 bots now 4 (automatic) from resource and 2 from test", 6, botsTypes.size()); + + // by default there should be a valid user and bot type + String user = programab.getUsername(); + String botType = programab.getBotType(); + + assertEquals("human is the first default user", "human", user); + // if valid directories there should be "some" default bot + assertNotNull(botType); + + } + + @Test + public void testSetBotType() { + programab.setBotType(LLOYD); + assertEquals("x is the first default user", LLOYD, programab.getBotType()); + + programab.setBotType("bogus"); + assertEquals("should not be able to set to invalid botType", LLOYD, programab.getBotType()); + + } + + @Test + public void testScanForBots() { + List botFolders = programab.scanForBots(BOTS_DIR); + assertEquals("should be 2 folderes", 2, botFolders.size()); + } + + @Test + public void testGetMaxConversationDelay() { + programab.clear(); + // should work for no sessions - previously threw npe + programab.setMaxConversationDelay(7000); + int maxDelay = programab.getMaxConversationDelay(); + assertEquals(7000, maxDelay); + programab.setMaxConversationDelay(5000); + maxDelay = programab.getMaxConversationDelay(); + assertEquals(5000, maxDelay); + programab.setEnableAutoConversation(true); + assertTrue(programab.getEnableAutoConversation()); + programab.setEnableAutoConversation(true); + programab.setEnableAutoConversation(false); + assertFalse(programab.getEnableAutoConversation()); + programab.getSession(); + } + + @Test + public void testPredicateFun() { + + } + + @Test + public void testCheckIfValid_WhenAimlDirectoryExists_ReturnsTrue() { + boolean isValid = programab.checkIfValid(new File(LLOYD_BOTDIR)); + assertTrue(isValid); + } + + @Test + public void testCheckIfValid_WhenAimlDirectoryDoesNotExist_ReturnsFalse() { + boolean isValid = programab.checkIfValid(new File(".")); + assertFalse(isValid); + } + + @Test + public void testSession() { + Session session = programab.getSession(); + log.info(String.format("%s", session)); + } + +} diff --git a/src/test/java/org/myrobotlab/service/PythonTest.java b/src/test/java/org/myrobotlab/service/PythonTest.java index 97a3f2fe0c..b0998fb7b7 100644 --- a/src/test/java/org/myrobotlab/service/PythonTest.java +++ b/src/test/java/org/myrobotlab/service/PythonTest.java @@ -3,13 +3,25 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.io.IOException; import java.util.Map; +import org.junit.Before; import org.myrobotlab.framework.Service; import org.python.core.PyInteger; public class PythonTest extends AbstractServiceTest { + @Before /* before each test */ + public void setUp() throws IOException { + // remove all services - also resets config name to DEFAULT effectively + Runtime.releaseAll(true, true); + // clean our config directory + Runtime.removeConfig("PythonTest"); + // set our config + Runtime.setConfig("PythonTest"); + } + @Override public Service createService() { return (Service) Runtime.start("python", "Python"); diff --git a/src/test/java/org/myrobotlab/service/RandomTest.java b/src/test/java/org/myrobotlab/service/RandomTest.java index 52338855eb..4feef62e03 100644 --- a/src/test/java/org/myrobotlab/service/RandomTest.java +++ b/src/test/java/org/myrobotlab/service/RandomTest.java @@ -3,8 +3,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.io.IOException; import java.util.Map; +import org.junit.Before; import org.myrobotlab.framework.Service; import org.myrobotlab.service.Random.RandomMessage; @@ -17,6 +19,17 @@ public class RandomTest extends AbstractServiceTest { public Service createService() throws Exception { return (Service) Runtime.start("random", "Random"); } + + @Before /* before each test */ + public void setUp() throws IOException { + // remove all services - also resets config name to DEFAULT effectively + Runtime.releaseAll(true, true); + // clean our config directory + Runtime.removeConfig("RandomTest"); + // set our config + Runtime.setConfig("RandomTest"); + } + @Override public void testService() throws Exception { @@ -37,13 +50,12 @@ public void testService() throws Exception { assertTrue(String.format("random method 1 should be %d => 5000 values", clock.getInterval()), 5000 <= clock.getInterval()); assertTrue(String.format("random method 1 should be %d <= 10000 values",clock.getInterval()) , clock.getInterval() <= 10000); - random.remove("clock", "setInterval"); + random.remove("clock.setInterval"); assertTrue("should not have method", !random.getKeySet().contains("clock.setInterval")); random.addRandom(0, 200, "clock", "setInterval", 5000, 10000); random.addRandom(0, 200, "clock", "startClock"); - // random.addRandom(0, 200, "clock", "stopClock"); sleep(500); assertTrue("clock should be started 1", clock.isClockRunning()); @@ -51,12 +63,12 @@ public void testService() throws Exception { // disable all of a services random events random.disable("clock.startClock"); clock.stopClock(); - sleep(200); - assertTrue("clock should not be started", !clock.isClockRunning()); + sleep(250); + assertTrue("clock should not be started 1", !clock.isClockRunning()); // enable all of a service's random events random.enable("clock.startClock"); - sleep(200); + sleep(250); assertTrue("clock should be started 2", clock.isClockRunning()); // disable one method - leave other enabled @@ -64,7 +76,7 @@ public void testService() throws Exception { clock.stopClock(); clock.setInterval(999999); sleep(200); - assertTrue("clock should not be started", !clock.isClockRunning()); + assertTrue("clock should not be started 3", !clock.isClockRunning()); assertTrue(String.format("random method 2 should be %d => 5000 values", clock.getInterval()), 5000 <= clock.getInterval()); assertTrue(String.format("random method 2 should be %d <= 10000 values",clock.getInterval()) , clock.getInterval() <= 10000); @@ -72,13 +84,13 @@ public void testService() throws Exception { random.disable(); sleep(200); clock.setInterval(999999); - assertTrue("clock should not be started", !clock.isClockRunning()); + assertTrue("clock should not be started 4", !clock.isClockRunning()); assertEquals(999999, (long)clock.getInterval()); // re-enable all that were previously enabled but not explicitly disabled ones random.enable(); sleep(1000); - assertTrue("clock should not be started", !clock.isClockRunning()); + assertTrue("clock should not be started 5", !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); diff --git a/src/test/java/org/myrobotlab/service/RuntimeProcessTest.java b/src/test/java/org/myrobotlab/service/RuntimeProcessTest.java index 07e1775110..54032c3972 100644 --- a/src/test/java/org/myrobotlab/service/RuntimeProcessTest.java +++ b/src/test/java/org/myrobotlab/service/RuntimeProcessTest.java @@ -31,7 +31,7 @@ public boolean contains(ByteArrayOutputStream out, String str) { @Test public void cliTest() throws Exception { - // from ,to null=runtime, data + // from ,to null=runtime, data String cwd = null; Message msg = CodecUtils.pathToMsg(getName(), "ls"); assertEquals("runtime", msg.getName()); @@ -73,31 +73,39 @@ public void cliTest() throws Exception { Runtime.releaseAll(false, true); String[] services = Runtime.getServiceNames(); - assertTrue(String.format("releasedAll(false) should have 1 remaining runtime services are %s", Arrays.toString(services)), services.length == 1); - + assertTrue( + String.format("releasedAll(false) should have 1 remaining runtime services are %s", Arrays.toString(services)), + services.length == 1); + // releasing "self" test runtime.releaseService(); services = Runtime.getServiceNames(); - assertTrue(String.format("releasedAll(false) should have 0 remaining runtime services are %s", Arrays.toString(services)), services.length == 0); - + assertTrue( + String.format("releasedAll(false) should have 0 remaining runtime services are %s", Arrays.toString(services)), + services.length == 0); + // testing re-entrant - runtime = Runtime.getInstance(); - assertTrue("testing re-entrant - expecting runtime service", Arrays.toString(Runtime.getServiceNames()).contains("runtime")); - - // removing all - Runtime.releaseAll(); + assertTrue("testing re-entrant - expecting runtime service", + Arrays.toString(Runtime.getServiceNames()).contains("runtime")); + + // removing all + Runtime.releaseAll(true, true); sleep(100); // better be 0 services = Runtime.getServiceNames(); - assertTrue(String.format("releasedAll(false) should have 0 remaining runtime services are %s", Arrays.toString(services)), services.length == 0); + assertTrue( + String.format("releasedAll(false) should have 0 remaining runtime services are %s", Arrays.toString(services)), + services.length == 0); // better be re-entrant runtime = Runtime.getInstance(); services = Runtime.getServiceNames(); - assertTrue(String.format("releasedAll(false) should have new runtime services are %s", Arrays.toString(services)), services.length > 0); - assertTrue("testing re-entrant again - expecting runtime service", Arrays.toString(Runtime.getServiceNames()).contains("runtime")); - + assertTrue(String.format("releasedAll(false) should have new runtime services are %s", Arrays.toString(services)), + services.length > 0); + assertTrue("testing re-entrant again - expecting runtime service", + Arrays.toString(Runtime.getServiceNames()).contains("runtime")); /** * cli diff --git a/src/test/java/org/myrobotlab/service/RuntimeTest.java b/src/test/java/org/myrobotlab/service/RuntimeTest.java index 50c6c03268..f612770657 100644 --- a/src/test/java/org/myrobotlab/service/RuntimeTest.java +++ b/src/test/java/org/myrobotlab/service/RuntimeTest.java @@ -3,6 +3,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.io.IOException; import java.util.Date; import java.util.List; import java.util.Map; @@ -24,11 +25,18 @@ public class RuntimeTest extends AbstractTest { public final static Logger log = LoggerFactory.getLogger(RuntimeTest.class); - @Before - public void setUp() { - // LoggingFactory.init("WARN"); + + @Before /* before each test */ + public void setUp() throws IOException { + // remove all services - also resets config name to DEFAULT effectively + Runtime.releaseAll(true, true); + // clean our config directory + Runtime.removeConfig("RuntimeTest"); + // set our config + Runtime.setConfig("RuntimeTest"); } + @Test public void testGetExternalIPAddress() throws Exception { if (hasInternet()) { @@ -57,14 +65,30 @@ public void testGetLocalHardwareAddresses() { @Test public void registerRemoteService() { - Registration registration = new Registration("remoteId", "clock", "Clock"); + Registration registration = new Registration("remoteId", "clock1", "Clock"); Runtime.register(registration); - Clock clock = (Clock) Runtime.getService("clock@remoteId"); - Assert.assertNotNull(clock); + String[] services = Runtime.getServiceNames(); + + boolean found = false; + for (String service: services) { + if (service.equals("clock1@remoteId")) { + found = true; + } + } + + if (!found) { + throw new RuntimeException("could not find clock1@remoteId"); + } + + // FIXME - don't do this, + // this should be proxied or we should just send messages + +// Clock clock = (Clock) Runtime.getService("clock1@remoteId"); +// Assert.assertNotNull(clock); // cleanup - Runtime.release("clock@remoteId"); + Runtime.release("clock1@remoteId"); } @Test diff --git a/src/test/java/org/myrobotlab/service/ServiceInterfaceTest.java b/src/test/java/org/myrobotlab/service/ServiceInterfaceTest.java index 440ebeb59e..806985daa6 100644 --- a/src/test/java/org/myrobotlab/service/ServiceInterfaceTest.java +++ b/src/test/java/org/myrobotlab/service/ServiceInterfaceTest.java @@ -122,7 +122,7 @@ public final void testAllServices() throws ClassNotFoundException, IOException { blacklist.add("Solr"); blacklist.add("Proxy"); // interesting idea - but no worky blacklist.add("Sphinx"); - blacklist.add("SwingGui"); + // This one just takes so darn long. blacklist.add("Deeplearning4j"); blacklist.add("OculusDiy"); diff --git a/src/test/java/org/myrobotlab/service/ServiceSmokeTest.java b/src/test/java/org/myrobotlab/service/ServiceSmokeTest.java index 13d1529570..55089cb620 100755 --- a/src/test/java/org/myrobotlab/service/ServiceSmokeTest.java +++ b/src/test/java/org/myrobotlab/service/ServiceSmokeTest.java @@ -95,7 +95,12 @@ public void testAllServiceSerialization() { } - Runtime.releaseAll(); + // releases all service including runtime + Runtime.releaseAll(true, true); + + // starts new service + Runtime.start("clock", "Clock"); + Runtime.setAllVirtual(true); log.info("Done with tests.."); diff --git a/src/test/java/org/myrobotlab/service/SolrTest.java b/src/test/java/org/myrobotlab/service/SolrTest.java index f236615198..5669aee004 100755 --- a/src/test/java/org/myrobotlab/service/SolrTest.java +++ b/src/test/java/org/myrobotlab/service/SolrTest.java @@ -20,11 +20,12 @@ import org.myrobotlab.image.Util; import org.myrobotlab.programab.Response; -// @Ignore +@Ignore public class SolrTest extends AbstractServiceTest { - // @Test + + @Ignore public void testImageStoreFetch() throws SolrServerException, IOException { - Solr solr = (Solr) Runtime.createAndStart("solr", "Solr"); + Solr solr = (Solr) Runtime.start("solr", "Solr"); String solrHome = SolrTest.testFolder.getRoot().getAbsolutePath(); solr.startEmbedded(solrHome); solr.deleteEmbeddedIndex(); @@ -55,7 +56,8 @@ private SolrInputDocument makeImageDoc(Solr solr, String docId, IplImage image) } private IplImage loadDefaultImage() { - String path = "src" + File.separator + "test" + File.separator + "resources" + File.separator + "OpenCV" + File.separator + "lena.png"; + String path = "src" + File.separator + "test" + File.separator + "resources" + File.separator + "OpenCV" + + File.separator + "lena.png"; IplImage image = cvLoadImage(path); return image; } @@ -69,9 +71,8 @@ public Service createService() { @Override public void testService() throws Exception { - // LoggingFactory.init("INFO"); please do not do this - Solr solr = (Solr) service; - // String solrHome = SolrTest.testFolder.getRoot().getAbsolutePath(); + Solr solr = (Solr) Runtime.start("solr", "Solr"); + solr.startEmbedded(); solr.deleteEmbeddedIndex(); solr.addDocument(makeTestDoc("doc_1")); @@ -95,7 +96,7 @@ public void testService() throws Exception { Document mrlDoc = new Document("doc_3"); mrlDoc.setField("title", "Mrl Rocks!"); // Object o = Arrays.asList(1.0f, 2.5f, 3.7f, 4.1f); - //Object o2 = Arrays.asList(makeVector(384)); + // Object o2 = Arrays.asList(makeVector(384)); mrlDoc.setField("vector", makeVector(384)); solr.onDocument(mrlDoc); @@ -128,16 +129,14 @@ public void testService() throws Exception { query = new SolrQuery("username:joe"); resp = solr.search(query); Assert.assertEquals(1, resp.getResults().getNumFound()); - - + // let's search for our vector ArrayList v = makeVector(384); - String queryVec = vecToString(v); - - - query = new SolrQuery("{!knn f=vector topK=10}"+queryVec); + String queryVec = vecToString(v); + + query = new SolrQuery("{!knn f=vector topK=10}" + queryVec); resp = solr.search(query); - + // System.out.println(resp); Assert.assertEquals(1, resp.getResults().getNumFound()); @@ -147,23 +146,24 @@ private String vecToString(ArrayList v) { // TODO Auto-generated method stub StringBuilder sb = new StringBuilder(); sb.append("["); - for (int i = 0 ; i < v.size(); i++) { + for (int i = 0; i < v.size(); i++) { sb.append(v.get(i)); - if (i != v.size()-1) { + if (i != v.size() - 1) { sb.append(","); } - } + } sb.append("]"); return sb.toString(); } - private ArrayList makeVector (int length) { + private ArrayList makeVector(int length) { ArrayList result = new ArrayList(); - for (int i = 0 ; i < length; i++) { - result.add( 0.5f ); + for (int i = 0; i < length; i++) { + result.add(0.5f); } return result; } + private SolrInputDocument makeTestDoc(String docId) { // TODO Auto-generated method stub SolrInputDocument doc = new SolrInputDocument(); diff --git a/src/test/java/org/myrobotlab/service/UltrasonicSensorTest.java b/src/test/java/org/myrobotlab/service/UltrasonicSensorTest.java index 9dfe3b0dce..dbe063d5fa 100644 --- a/src/test/java/org/myrobotlab/service/UltrasonicSensorTest.java +++ b/src/test/java/org/myrobotlab/service/UltrasonicSensorTest.java @@ -56,7 +56,7 @@ public void testUltrasonicSensor() throws TimeoutException { // 1 hz ultra.setRate(1); ultra.startRanging(); - Message msg = gateway.waitForMsg("mocker@mockId", "onRange", 1000); + Message msg = gateway.waitForMsg("mocker@mockId", "onRange", 1500); Double r = (Double)msg.data[0]; assertNotNull(r); ultra.stopRanging(); diff --git a/src/test/java/org/myrobotlab/test/AbstractTest.java b/src/test/java/org/myrobotlab/test/AbstractTest.java index 2ebf3d0634..c4fc33b83d 100644 --- a/src/test/java/org/myrobotlab/test/AbstractTest.java +++ b/src/test/java/org/myrobotlab/test/AbstractTest.java @@ -30,8 +30,6 @@ public class AbstractTest { static private boolean logWarnTestHeader = false; - private static boolean releaseRemainingServices = true; - private static boolean releaseRemainingThreads = false; protected transient Queue queue = new LinkedBlockingQueue<>(); @@ -42,9 +40,9 @@ public class AbstractTest { @Rule public final TestName testName = new TestName(); - + static public String simpleName; - + private static boolean lineFeedFooter = true; public String getSimpleName() { @@ -100,7 +98,6 @@ public static void setUpAbstractTest() throws Exception { threadSetStart = Thread.getAllStackTraces().keySet(); } installAll(); - Runtime.clearPlan(); } static public List getThreadNames() { @@ -123,9 +120,7 @@ public static void sleep(long sleepTimeMs) { public static void tearDownAbstractTest() throws Exception { log.info("tearDownAbstractTest"); - if (releaseRemainingServices) { - releaseServices(); - } + releaseServices(); if (logWarnTestHeader) { log.warn("=========== finished test {} ===========", simpleName); @@ -150,19 +145,23 @@ static protected void installAll() { */ public static void releaseServices() { - log.info("end of test - id {} remaining services {}", Platform.getLocalInstance().getId(), Arrays.toString(Runtime.getServiceNames())); - + log.info("end of test - id {} remaining services {}", Platform.getLocalInstance().getId(), + Arrays.toString(Runtime.getServiceNames())); + // release all including runtime - be careful of default runtime.yml - // Runtime.releaseAll(true, true); - Runtime.releaseAll(); + Runtime.releaseAll(true, true); + // wait for draining threads sleep(100); + // resets runtime with fresh new instance + Runtime.getInstance(); // check threads - kill stragglers // Set stragglers = new HashSet(); Set threadSetEnd = Thread.getAllStackTraces().keySet(); Set threadsRemaining = new TreeSet<>(); for (Thread thread : threadSetEnd) { - if (!threadSetStart.contains(thread) && !"runtime_outbox_0".equals(thread.getName()) && !"runtime".equals(thread.getName())) { + if (!threadSetStart.contains(thread) && !"runtime_outbox_0".equals(thread.getName()) + && !"runtime".equals(thread.getName())) { if (releaseRemainingThreads) { log.warn("interrupting thread {}", thread.getName()); thread.interrupt(); @@ -179,8 +178,10 @@ public static void releaseServices() { if (threadsRemaining.size() > 0) { log.info("{} straggling threads remain [{}]", threadsRemaining.size(), String.join(",", threadsRemaining)); } - - // log.warn("end of test - id {} remaining services after release {}", Platform.getLocalInstance().getId(), Arrays.toString(Runtime.getServiceNames())); + + // log.warn("end of test - id {} remaining services after release {}", + // Platform.getLocalInstance().getId(), + // Arrays.toString(Runtime.getServiceNames())); } public AbstractTest() { diff --git a/src/test/resources/ProgramAB/bots/lloyd/aiml/lloyd.aiml b/src/test/resources/ProgramAB/bots/lloyd/aiml/lloyd.aiml index 257d55e668..9a40847137 100644 --- a/src/test/resources/ProgramAB/bots/lloyd/aiml/lloyd.aiml +++ b/src/test/resources/ProgramAB/bots/lloyd/aiml/lloyd.aiml @@ -1,167 +1,273 @@ - - -* -