Skip to content

Commit

Permalink
worky MockGateway
Browse files Browse the repository at this point in the history
  • Loading branch information
supertick committed Dec 9, 2023
1 parent 00e539f commit 18f9a18
Show file tree
Hide file tree
Showing 71 changed files with 5,128 additions and 6,204 deletions.
48 changes: 37 additions & 11 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,38 @@
{
"files.exclude": {
"**/.classpath": true,
"**/.project": true,
"**/.settings": true,
"**/.factorypath": true,
"**/target": true
},
"files.associations": {
"cstddef": "cpp"
{ "files.exclude": {
"**/.classpath": true,
"**/.project": true,
"**/.settings": true,
"**/.factorypath": true,
"**/target": true
},
"files.associations": {
"cstddef": "cpp"
},
"terminal.integrated.scrollback": 9999,
"jest.rootPath": "app",
"jest.jestCommandLine": "npx jest --runInBand --verbose",
"jest.autoRun": "off",
"jest.monitorLongRun": 120000,
"jest.showCoverageOnLoad": false,
"jest.coverageFormatter": "GutterFormatter",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
},
"typescript.tsdk": "./app/node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"editor.tabSize": 2,
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
}

26 changes: 19 additions & 7 deletions src/main/java/org/myrobotlab/arduino/VirtualMsg.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -2019,4 +2027,8 @@ public void setInvoke(boolean b){
invoke = b;
}

public void setSerial(Serial serial) {
this.serial = serial;
}

}
5 changes: 5 additions & 0 deletions src/main/java/org/myrobotlab/codec/ForeignProcessUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ public class ForeignProcessUtils {
* @return Whether name is a valid FQCN
*/
public static boolean isValidJavaClassName(String name) {
// TODO: this is temporary, until proxy java classes
// are proxied in the same way as other services
if (name.equals("Unknown")) {
return false;
}
return JAVA_FQCN_PATTERN.matcher(name).matches();
}

Expand Down
36 changes: 20 additions & 16 deletions src/main/java/org/myrobotlab/framework/Outbox.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -63,11 +62,17 @@ public class Outbox implements Runnable, Serializable {
static public final String PROCESSANDBROADCAST = "PROCESSANDBROADCAST";

protected String name = null;
private transient LinkedList<Message> msgBox = new LinkedList<Message>();

private transient LinkedList<Message> msgBox = new LinkedList<Message>();

private boolean isRunning = false;

private boolean blocking = false;

int maxQueue = 1024;

int initialThreadCount = 1;

transient ArrayList<Thread> outboxThreadPool = new ArrayList<Thread>();

protected Map<String, FilterInterface> filters = new HashMap<>();
Expand Down Expand Up @@ -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
Expand All @@ -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))) {
Expand All @@ -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();
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand All @@ -379,7 +386,4 @@ synchronized public void detach(String service) {
public Map<String, List<MRLListener>> getNotifyList() {
return notifyList;
}



}
124 changes: 67 additions & 57 deletions src/main/java/org/myrobotlab/framework/ProxyFactory.java
Original file line number Diff line number Diff line change
@@ -1,77 +1,87 @@
package org.myrobotlab.framework;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.MethodDelegation;
import org.myrobotlab.framework.interfaces.ServiceInterface;
import static net.bytebuddy.matcher.ElementMatchers.any;

import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.stream.Collectors;

import static net.bytebuddy.matcher.ElementMatchers.any;
import org.myrobotlab.framework.interfaces.ServiceInterface;
import org.myrobotlab.logging.LoggerFactory;
import org.slf4j.Logger;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.MethodDelegation;

/**
* ProxyFactory takes a service description via {@link Registration}
* and uses <a href="https://bytebuddy.net/#/">ByteBuddy</a> to generate
* a new class and instance for it, delegating all method calls to a new instance
* of {@link ProxyInterceptor}. The registration must contain at least the
* fully-qualified name of {@link ServiceInterface} in its {@link Registration#interfaces}
* list. If this name is not present, an {@link IllegalArgumentException} is thrown.
* ProxyFactory takes a service description via {@link Registration} and uses
* <a href="https://bytebuddy.net/#/">ByteBuddy</a> to generate a new class and
* instance for it, delegating all method calls to a new instance of
* {@link ProxyInterceptor}. The registration must contain at least the
* fully-qualified name of {@link ServiceInterface} in its
* {@link Registration#interfaces} list. If this name is not present, an
* {@link IllegalArgumentException} is thrown.
*
* @author AutonomicPerfectionist
*/
public class ProxyFactory {

/**
* Creates a proxy class and instantiates it using the given registration.
* If the registration's {@link Registration#interfaces} list does not contain
* the fully-qualified name of {@link ServiceInterface}, an {@link IllegalArgumentException}
* is thrown. The generated proxy uses the name, id, and interfaces present in
* the registration to create the new service. The proxy
* delegates to a new instance of {@link ProxyInterceptor}.
*
* @param registration The information required to generate a new proxy
* @return A newly-instantiated proxy
* @throws IllegalArgumentException if registration's interfaces list
* does not contain ServiceInterface
*/
public static ServiceInterface createProxyService(Registration registration) {
// TODO add caching support
if (!registration.interfaces.contains(ServiceInterface.class.getName())) {
throw new IllegalArgumentException(
"Registration must list at least ServiceInterface in the interfaces list."
);
}
ByteBuddy buddy = new ByteBuddy();
DynamicType.Builder<?> builder = buddy.subclass(Object.class);
List<Class<?>> interfaceClasses = registration.interfaces.stream().map(i -> {
try {
return Class.forName(i);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Unable to load interface " + i + " for registration " + registration, e);
}
}).collect(Collectors.toList());
transient public final static Logger log = LoggerFactory.getLogger(ProxyFactory.class);

/**
* Creates a proxy class and instantiates it using the given registration. If
* the registration's {@link Registration#interfaces} list does not contain
* the fully-qualified name of {@link ServiceInterface}, an
* {@link IllegalArgumentException} is thrown. The generated proxy uses the
* name, id, and interfaces present in the registration to create the new
* service. The proxy delegates to a new instance of {@link ProxyInterceptor}.
*
* @param registration
* The information required to generate a new proxy
* @return A newly-instantiated proxy
* @throws IllegalArgumentException
* if registration's interfaces list does not
* contain
* ServiceInterface
*/
public static ServiceInterface createProxyService(Registration registration) {

builder = builder.implement(interfaceClasses)
.method(any())
.intercept(MethodDelegation
.withDefaultConfiguration()
.to(new ProxyInterceptor(registration.name, registration.id)));
if (registration.interfaces == null) {
log.info("remote did not provide any interfaces, creating minimal getId and getName from registration data");
} else {
// TODO add caching support
if (!registration.interfaces.contains(ServiceInterface.class.getName())) {
throw new IllegalArgumentException("Registration must list at least ServiceInterface in the interfaces list.");
}
}
ByteBuddy buddy = new ByteBuddy();
DynamicType.Builder<?> builder = buddy.subclass(Object.class);
List<Class<?>> interfaceClasses = registration.interfaces.stream().map(i -> {
try {
return Class.forName(i);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Unable to load interface " + i + " for registration " + registration, e);
}
}).collect(Collectors.toList());

builder = builder.implement(interfaceClasses).method(any()).intercept(MethodDelegation.withDefaultConfiguration()
.to(new ProxyInterceptor(registration.name, registration.id, registration.typeKey)));

Class<?> proxyClass = builder.make()
.load(ProxyFactory.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
try {
// We never defined any constructors so the default no-args is available
return (ServiceInterface) proxyClass.getConstructors()[0].newInstance();
Class<?> proxyClass = builder.make().load(ProxyFactory.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
try {
// We never defined any constructors so the default no-args is available
ServiceInterface si = (ServiceInterface) proxyClass.getConstructors()[0].newInstance();
MethodCache cache = MethodCache.getInstance();
cache.cacheMethodEntries(si.getClass());
return si;

} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
// Really shouldn't happen since we have full control over the
// newly-generated class
throw new RuntimeException(e);
}
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
// Really shouldn't happen since we have full control over the
// newly-generated class
throw new RuntimeException(e);
}
}
}
Loading

0 comments on commit 18f9a18

Please sign in to comment.