Skip to content

Vertx webxr #1254

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 28 commits into
base: develop
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e07d8b0
wip webxr vertx
supertick Mar 19, 2023
29a86b3
Merge branch 'config-with-peers-and-subscriptions' of github.com:MyRo…
supertick Mar 20, 2023
2332822
cron update
supertick Jul 17, 2023
06783cb
raspi updates
supertick Jul 17, 2023
dad01bc
forgot pindefinition
supertick Jul 17, 2023
4658b20
fixed legacy write and pinmode
supertick Jul 17, 2023
4440167
in progress
supertick Jul 18, 2023
4443dc0
oscope fixes
supertick Jul 18, 2023
370e128
from grog branch
supertick Jul 18, 2023
22faefb
oscope cron and raspi improvements
supertick Jul 18, 2023
bac8ca9
ipscan image and info down to debug
supertick Jul 19, 2023
ea0ab74
adding .gitignore entries
supertick Jul 19, 2023
94534be
Merge branch 'develop' of github.com-myrobotlab:MyRobotLab/myrobotlab…
supertick Jul 19, 2023
00e9611
Merge branch 'cron-update' of github.com-myrobotlab:MyRobotLab/myrobo…
supertick Jul 19, 2023
62ad8b0
Merge branch 'develop' of github.com-myrobotlab:MyRobotLab/myrobotlab…
supertick Jul 20, 2023
f1c691c
generated pom
supertick Jul 20, 2023
bae8c3d
Merge branch 'develop' of github.com-myrobotlab:MyRobotLab/myrobotlab…
supertick Jul 20, 2023
993669c
rename
supertick Jul 22, 2023
b7ddb43
incremental
supertick Jul 22, 2023
754b978
Improved Cron and Cron history
supertick Jul 22, 2023
177c81d
Merge branch 'cron-enhanced' of github.com-myrobotlab:MyRobotLab/myro…
supertick Jul 22, 2023
a8dc963
forgot one
supertick Jul 22, 2023
c2de6bb
Merge branch 'cron-enhanced' of github.com-myrobotlab:MyRobotLab/myro…
supertick Jul 22, 2023
8791647
Teamwork fix of Hd44780
supertick Jul 23, 2023
66b42fb
Merge branch 'cron-enhanced' of github.com-myrobotlab:MyRobotLab/myro…
supertick Jul 23, 2023
f090e48
Merge branch 'develop' of github.com-myrobotlab:MyRobotLab/myrobotlab…
supertick Jul 23, 2023
ccceb78
intermediate
supertick Jul 23, 2023
24cfa91
Merge branch 'develop' of github.com-myrobotlab:MyRobotLab/myrobotlab…
supertick Jul 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -116,4 +116,7 @@ build/
/lastRestart.py
/.factorypath
start.yml
config
src/main/resources/resource/InMoov2
src/main/resources/resource/ProgramAB
*.iml
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -100,7 +100,6 @@ Enjoy the code review, address issues and concern in the code review
Reviewer merges pull request to develop.
Reviewer deletes branch.


The following config should be useful to work directly on WebGui UI and
InMoov2 UI if the repos are checked out at the same level
```yml
36 changes: 30 additions & 6 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -163,10 +163,6 @@
<!-- Duplicate entry for com.squareup.okhttp3-okhttp-3.9.0 skipping -->
<!-- AzureTranslator end -->

<!-- BodyPart begin -->
<!-- Duplicate entry for org.apache.commons-commons-lang3-3.3.2 skipping -->
<!-- BodyPart end -->

<!-- BoofCv begin -->
<dependency>
<groupId>org.boofcv</groupId>
@@ -1237,13 +1233,13 @@
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>cpython-platform</artifactId>
<version>3.11.3-1.5.9</version>
<version>3.10.8-1.5.8</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>cpython</artifactId>
<version>3.11.3-1.5.9</version>
<version>3.10.8-1.5.8</version>
<scope>provided</scope>
</dependency>
<!-- Py4j end -->
@@ -1574,6 +1570,34 @@
</dependency>
<!-- Twitter end -->

<!-- Vertx begin -->
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>4.3.3</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
<version>4.3.3</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Duplicate entry for io.netty-netty-all-4.1.82.Final skipping -->
<!-- Vertx end -->

<!-- VoiceRss begin -->
<!-- Duplicate entry for org.myrobotlab.audio-voice-effects-1.0 skipping -->
<dependency>
2 changes: 1 addition & 1 deletion src/main/java/org/myrobotlab/service/ProgramAB.java
Original file line number Diff line number Diff line change
@@ -794,7 +794,7 @@ public String addBotPath(String path) {

broadcastState();
} else {
error("invalid bot path - a bot must be a directory with a subdirectory named \"aiml\"");
error("invalid bot path %s - a bot must be a directory with a subdirectory named \"aiml\"", path);
return null;
}
return path;
135 changes: 135 additions & 0 deletions src/main/java/org/myrobotlab/service/Vertx.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package org.myrobotlab.service;

import java.util.HashMap;
import java.util.Set;

import org.myrobotlab.framework.Service;
import org.myrobotlab.logging.Level;
import org.myrobotlab.logging.LoggerFactory;
import org.myrobotlab.logging.LoggingFactory;
import org.myrobotlab.vertx.ApiVerticle;
import org.slf4j.Logger;

import io.vertx.core.VertxOptions;

/**
* 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.
*
* 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
*
* @author greg
*
*/
public class Vertx extends Service {

private static final long serialVersionUID = 1L;

private transient io.vertx.core.Vertx vertx = null;

public final static Logger log = LoggerFactory.getLogger(Vertx.class);

public Vertx(String n, String id) {
super(n, id);
}

/**
* deploys a http and websocket verticle on a secure TLS channel with self signed certificate
*/
public void start() {
log.info("starting driver");

/**
* FIXME - might have to revisit this This is a block comment, but takes
* advantage of javadoc pre non-formatting in ide to preserve the code
* formatting
*
* <pre>
*
* final Vertx that = this;
*
* java.lang.Runtime.getRuntime().addShutdownHook(new Thread() {
* public void run() {
* System.out.println("Running Shutdown Hook");
* that.stop();
* }
* });
*
* </pre>
*/

vertx = io.vertx.core.Vertx.vertx(new VertxOptions().setBlockedThreadCheckInterval(100000));
vertx.deployVerticle(new ApiVerticle(this));

}

@Override
public void startService() {
super.startService();
start();
}

@Override
public void stopService() {
super.stopService();
stop();
}


/**
*
*/
public void stop() {
log.info("stopping driver");
Set<String> ids = vertx.deploymentIDs();
for (String id : ids) {
vertx.undeploy(id, (result) -> {
if (result.succeeded()) {
log.info("succeeded");
} else {
log.error("failed");
}
});
}
}

public static class Matrix {
public String name;
public HashMap<String, Float> matrix;

public Matrix() {
};
}

public Matrix publishMatrix(Matrix data) {
// log.info("publishMatrix {}", data.name);
return data;
}

public static void main(String[] args) {
try {

LoggingFactory.init(Level.INFO);

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();

} catch (Exception e) {
log.error("main threw", e);
}
}
}
89 changes: 89 additions & 0 deletions src/main/java/org/myrobotlab/service/WebXR.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package org.myrobotlab.service;

import java.util.HashMap;
import java.util.Map;

import org.myrobotlab.framework.Service;
import org.myrobotlab.logging.Level;
import org.myrobotlab.logging.LoggerFactory;
import org.myrobotlab.logging.LoggingFactory;
import org.myrobotlab.math.MapperSimple;
import org.myrobotlab.service.config.WebXRConfig;
import org.myrobotlab.service.data.Pose;
import org.slf4j.Logger;

public class WebXR extends Service {

private static final long serialVersionUID = 1L;

public final static Logger log = LoggerFactory.getLogger(WebXR.class);

public WebXR(String n, String id) {
super(n, id);
}

public Pose publishPose(Pose pose) {
log.warn("publishPose {}", pose);
System.out.println(pose.toString());

// process mappings config into joint angles
Map<String, Double> map = new HashMap<>();

WebXRConfig c = (WebXRConfig)config;
String path = String.format("%s.orientation.roll", pose.name);
if (c.mappings.containsKey(path)) {
Map<String, MapperSimple> mapper = c.mappings.get(path);
for (String name: mapper.keySet()) {
map.put(name, mapper.get(name).calcOutput(pose.orientation.roll));
}
}

path = String.format("%s.orientation.pitch", pose.name);
if (c.mappings.containsKey(path)) {
Map<String, MapperSimple> mapper = c.mappings.get(path);
for (String name: mapper.keySet()) {
map.put(name, mapper.get(name).calcOutput(pose.orientation.pitch));
}
}

path = String.format("%s.orientation.yaw", pose.name);
if (c.mappings.containsKey(path)) {
Map<String, MapperSimple> mapper = c.mappings.get(path);
for (String name: mapper.keySet()) {
map.put(name, mapper.get(name).calcOutput(pose.orientation.yaw));
}
}

invoke("publishJointAngles", map);

// TODO - publishQuaternion
// invoke("publishQuaternion", map);

return pose;
}


public Map<String, Double> publishJointAngles(Map<String, Double> map){
return map;
}

public static void main(String[] args) {
try {

LoggingFactory.init(Level.INFO);

Runtime.start("webxr", "WebXr");
WebGui webgui = (WebGui) Runtime.create("webgui", "WebGui");
// webgui.setSsl(true);
webgui.autoStartBrowser(false);
webgui.startService();
Runtime.start("vertx", "Vertx");
InMoov2 i01 = (InMoov2)Runtime.start("i01", "InMoov2");
i01.startPeer("simulator");


} catch (Exception e) {
log.error("main threw", e);
}
}
}
9 changes: 9 additions & 0 deletions src/main/java/org/myrobotlab/service/config/VertxConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.myrobotlab.service.config;

public class VertxConfig extends ServiceConfig {

public Integer port = 8443;
public Integer workerCount = 1;
public boolean ssl = true;

}
1 change: 1 addition & 0 deletions src/main/java/org/myrobotlab/service/data/Orientation.java
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ public class Orientation {
public Double roll = null;
public Double pitch = null;
public Double yaw = null;
public String src = null;

// default constructor (values will be null until set)
public Orientation() {
22 changes: 22 additions & 0 deletions src/main/java/org/myrobotlab/service/data/Pose.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.myrobotlab.service.data;

public class Pose {
public String name = null;
public Long ts = null;
public Position position = null;
public Orientation orientation = null;

public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(String.format("name:%s", name));
if (position != null) {
sb.append(String.format(" x:%.2f y:%.2f z:%.2f", position.x, position.y, position.z));
}
if (orientation != null) {
sb.append(String.format(" roll:%.2f pitch:%.2f yaw:%.2f", orientation.roll, orientation.pitch, orientation.yaw));
}
return sb.toString();
}


}
43 changes: 43 additions & 0 deletions src/main/java/org/myrobotlab/service/data/Position.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.myrobotlab.service.data;

public class Position {

public Double x;
public Double y;
public Double z;
public String src;

public Position(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}

public Position(double x, double y) {
this.x = x;
this.y = y;
}

public Position(int x, int y, int z) {
this.x = (double) x;
this.y = (double) y;
this.z = (double) z;
}

public Position(int x, int y) {
this.x = (double) x;
this.y = (double) y;
}

public Position(float x, float y, float z) {
this.x = (double) x;
this.y = (double) y;
this.z = (double) z;
}

public Position(float x, float y) {
this.x = (double) x;
this.y = (double) y;
}

}
33 changes: 33 additions & 0 deletions src/main/java/org/myrobotlab/service/meta/WebXRMeta.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.myrobotlab.service.meta;

import org.myrobotlab.logging.LoggerFactory;
import org.myrobotlab.service.meta.abstracts.MetaData;
import org.slf4j.Logger;

public class WebXRMeta extends MetaData {
private static final long serialVersionUID = 1L;
public final static Logger log = LoggerFactory.getLogger(WebXRMeta.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 WebXRMeta() {

// add a cool description
addDescription("WebXr allows hmi devices to add input and get data back from mrl");

// false will prevent it being seen in the ui
setAvailable(true);

// add it to one or many categories
addCategory("remote","control");

// add a sponsor to this service
// the person who will do maintenance
// setSponsor("GroG");

}

}
105 changes: 105 additions & 0 deletions src/main/java/org/myrobotlab/vertx/ApiVerticle.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package org.myrobotlab.vertx;

import java.lang.reflect.Method;

import org.myrobotlab.codec.CodecUtils;
import org.myrobotlab.framework.MethodCache;
import org.myrobotlab.framework.interfaces.ServiceInterface;
import org.myrobotlab.service.Runtime;
import org.myrobotlab.service.config.VertxConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.http.ServerWebSocket;
import io.vertx.core.net.SelfSignedCertificate;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.CorsHandler;
import io.vertx.ext.web.handler.StaticHandler;

/**
* verticle to handle api requests
*
* @author GroG
*/
public class ApiVerticle extends AbstractVerticle {

public final static Logger log = LoggerFactory.getLogger(ApiVerticle.class);

private Router router;

transient private org.myrobotlab.service.Vertx service;

public ApiVerticle(org.myrobotlab.service.Vertx service) {
super();
this.service = service;
}

@Override
public void start() throws Exception {
// process configuration and create handlers
log.info("starting api verticle");
VertxConfig config = (VertxConfig) service.getConfig();

// create a router
router = Router.router(vertx);

// handle cors requests
router.route().handler(CorsHandler.create("*").allowedMethod(HttpMethod.GET).allowedMethod(HttpMethod.OPTIONS).allowedHeader("Accept").allowedHeader("Authorization")
.allowedHeader("Content-Type"));

// static file routing

//StaticHandler root = StaticHandler.create("src/main/resources/resource/Vertx/app");
// StaticHandler root = StaticHandler.create("src/main/resources/resource/Vertx/app");
StaticHandler root = StaticHandler.create("../robotlab-x-app/build/");
root.setCachingEnabled(false);
root.setDirectoryListing(true);
root.setIndexPage("index.html");
// root.setAllowRootFileSystemAccess(true);
// root.setWebRoot(null);
router.route("/*").handler(root);


// router.get("/health").handler(this::generateHealth);
// router.get("/api/transaction/:customer/:tid").handler(this::handleTransaction);

// create the HTTP server and pass the
// "accept" method to the request handler
HttpServerOptions httpOptions = new HttpServerOptions();

if (config.ssl) {
SelfSignedCertificate certificate = SelfSignedCertificate.create();
httpOptions.setSsl(true);
httpOptions.setKeyCertOptions(certificate.keyCertOptions());
httpOptions.setTrustOptions(certificate.trustOptions());
}
httpOptions.setPort(config.port);


HttpServer server = vertx.createHttpServer(httpOptions);
// TODO - this is where multiple workers would be defined
// .createHttpServer()

// WebSocketHandler webSocketHandler = new WebSocketHandler(service);
// server.webSocketHandler(webSocketHandler);

// FIXME - don't do "long" or "common" processing in the start()
// FIXME - how to do this -> server.webSocketHandler(this::handleWebSocket);
server.webSocketHandler(new WebSocketHandler(service));
server.requestHandler(router);
// start servers
server.listen();
}


@Override
public void stop() throws Exception {
log.info("stopping api verticle");
}

}
93 changes: 93 additions & 0 deletions src/main/java/org/myrobotlab/vertx/WebSocketHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package org.myrobotlab.vertx;

import java.lang.reflect.Method;

import org.myrobotlab.codec.CodecUtils;
import org.myrobotlab.framework.MethodCache;
import org.myrobotlab.framework.interfaces.ServiceInterface;
import org.myrobotlab.logging.LoggerFactory;
import org.myrobotlab.service.Runtime;
import org.slf4j.Logger;

import io.vertx.core.Handler;
import io.vertx.core.http.ServerWebSocket;

/**
*
* TODO - what else besides text messages - websocket binary streams ??? text stream ?
*
* @author GroG
*
*/
public class WebSocketHandler implements Handler<ServerWebSocket> {

public final static Logger log = LoggerFactory.getLogger(WebSocketHandler.class);

transient private org.myrobotlab.service.Vertx service = null;
TextMessageHandler textMessageHandler = null;

public static class TextMessageHandler implements Handler<String> {

org.myrobotlab.service.Vertx service = null;

public TextMessageHandler(org.myrobotlab.service.Vertx service) {
this.service = service;
}

@Override
public void handle(String json) {
log.info("handling {}", json);

Method method;
try {

org.myrobotlab.framework.Message msg = CodecUtils.fromJson(json, org.myrobotlab.framework.Message.class);

Class<?> clazz = Runtime.getClass(msg.name);
if (clazz == null) {
log.error("cannot derive local type from service {}", msg.name);
return;
}

MethodCache cache = MethodCache.getInstance();
Object[] params = cache.getDecodedJsonParameters(clazz, msg.method, msg.data);

method = cache.getMethod(clazz, msg.method, params);
if (method == null) {
service.error("method cache could not find %s.%s(%s)", clazz.getSimpleName(), msg.method, msg.data);
return;
}

ServiceInterface si = Runtime.getService(msg.name);
Object ret = method.invoke(si, params);

// put msg on mrl msg bus :)
// service.in(msg); <- NOT DECODE PARAMS !!

// if ((new Random()).nextInt(100) == 0) {
// ctx.close(); - will close the websocket !!!
// } else {
// ctx.writeTextMessage("ping"); Useful is writing back
// }

} catch (Exception e) {
service.error(e);
}
}
}

public WebSocketHandler(org.myrobotlab.service.Vertx service) {
this.service = service;
this.textMessageHandler = new TextMessageHandler(service);
}

@Override
public void handle(ServerWebSocket event) {

// ctx.writeTextMessage("ping"); FIXME - query ?
// FIXME - thread-safe ? how many connections mapped to objects ?
event.textMessageHandler(new TextMessageHandler(service));

}

}