package org.myrobotlab.service; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.myrobotlab.framework.Message; import org.myrobotlab.framework.Service; import org.myrobotlab.framework.ServiceType; import org.myrobotlab.io.FileIO; import org.myrobotlab.logging.Level; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.logging.Logging; import org.myrobotlab.logging.LoggingFactory; import org.myrobotlab.serial.PortQueue; import org.myrobotlab.service.interfaces.SerialDataListener; import org.slf4j.Logger; /** * * VirtualDevice - This is a virtual serial port device that can be used to * redirect serial data over a network for example. Blender service requires * this so the serial commands to an inmoov and be pumped over the network to * blender, rather than over the serial port to an actual arduino. * */ public class VirtualDevice extends Service implements SerialDataListener { private static final long serialVersionUID = 1L; public final static Logger log = LoggerFactory.getLogger(VirtualDevice.class); // transient Serial uart; transient HashMap<String, Serial> uarts = new HashMap<String, Serial>(); transient Python logic; transient BlockingQueue<Message> msgs = new LinkedBlockingQueue<Message>(); public VirtualDevice(String n) { super(n); // uart = (Serial)createPeer("uart"); logic = (Python) createPeer("logic"); } public void startService() { super.startService(); // uart = (Serial)startPeer("uart"); logic = (Python) startPeer("logic"); // uart.addByteListener(this); } public Python getLogic() { return logic; } public Serial getUart(String portName) { return uarts.get(portName); } public HashMap<String, Serial> getUarts() { return uarts; } public void createVirtualSerial(String portName) throws IOException { // first create and connect on the virtual UART side connectVirtualUart(portName, null); // return uart.connectVirtualNullModem(portName); } public void createVirtualArduino(String portName) throws IOException { createVirtualSerial(portName); String newCode = FileIO.resourceToString("VirtualDevice/Arduino.py"); log.info(newCode); logic.openScript("Arduino.py", newCode); logic.execAndWait(); } /** * for a virtual UART to create a unopened port available for connection. it * will connect itself to one end of the twisted buffer pair * * @param portName * @param myPort * @throws IOException */ /* * public String connectVirtualNullModem(String newPortName) throws * IOException { * * BlockingQueue<Integer> left = new LinkedBlockingQueue<Integer>(); * BlockingQueue<Integer> right = new LinkedBlockingQueue<Integer>(); * * // create other end of null modem cable // which MRL Services can connect * to Port newPort = new PortQueue(newPortName, right, left); * ports.put(newPortName, newPort); * * // add our virtual port String uartPortName = String.format("%s_uart", * newPortName); PortQueue vPort = new PortQueue(uartPortName, left, right); * connectPort(vPort, this); * * info(String.format("created virtual null modem cable %s <--> %s", * newPortName, uartPortName)); return newPortName; } */ /** * connecting to a virtual UART allows a Serial service to interface with a * mocked hardware. To do this a Serial service creates 2 stream ports and * twists the virtual cable between them. * * A virtual port is half a virtual pipe, and if unconnected - typically is * not very interesting... * * @param listener * @return * @throws IOException */ public Serial connectVirtualUart(String myPort, String uartPort) throws IOException { // get port names if (myPort == null) { myPort = getName(); } if (uartPort == null) { uartPort = String.format("%s_uart", myPort); } BlockingQueue<Integer> left = new LinkedBlockingQueue<Integer>(); BlockingQueue<Integer> right = new LinkedBlockingQueue<Integer>(); /* * if (listener != null) { listeners.put(listener.getName(), listener); } */ ; // connectPort(vPort, this); // create & connect virtual uart Serial uart = (Serial) Runtime.start(uartPort, "Serial"); // add our virtual port PortQueue vPort = new PortQueue(myPort, left, right); Serial.ports.put(myPort, vPort); PortQueue uPort = new PortQueue(uartPort, right, left); uart.connectPort(uPort, uart); // add the uart connected to my port uarts.put(myPort, uart); log.info(String.format("connectToVirtualUart - creating uart %s <--> %s", myPort, uartPort)); return uart; } public Serial createVirtualUart() throws IOException { return connectVirtualUart(null, null); } @Override public Integer onByte(Integer b) throws IOException { log.info("{}.onByte {}", getName(), b); return null; } @Override public String onConnect(String portName) { log.info("{}.onConnect {}", getName(), portName); return portName; } @Override public String onDisconnect(String portName) { log.info("{}.onDisconnect {}", getName(), portName); return portName; } /** * preProcessHook is used to intercept messages and process or route them * before being processed/invoked in the Service. * * @throws * * @see * org.myrobotlab.framework.Service#preProcessHook(org.myrobotlab. * framework.Message) */ @Override public boolean preProcessHook(Message msg) { try { msgs.put(msg); // log.info(String.format("%d msg %s ", msgs.size(), msg)); } catch (Exception e) { Logging.logError(e); } return false; } public void clear() { // data.clear(); msgs.clear(); } public BlockingQueue<Message> getMsgs() { return msgs; } public Message getMsg(long timeout) throws InterruptedException { Message msg = msgs.poll(timeout, TimeUnit.MILLISECONDS); return msg; } public ArrayList<Message> waitForMsgs(int count) throws InterruptedException, IOException { return waitForMsgs(count, 1000, 100); } public ArrayList<Message> waitForMsgs(int count, int timeout) throws InterruptedException, IOException { return waitForMsgs(count, timeout, 100); } public ArrayList<Message> waitForMsgs(int count, int timeout, int pollInterval) throws InterruptedException, IOException { ArrayList<Message> ret = new ArrayList<Message>(); long start = System.currentTimeMillis(); long now = start; while (ret.size() < count) { now = System.currentTimeMillis(); Message msg = msgs.poll(pollInterval, TimeUnit.MILLISECONDS); if (msg != null) { ret.add(msg); } if (now - start > timeout) { String error = String.format("waited %d ms received %d messages expecting %d in less than %d ms", now - start, ret.size(), count, timeout); log.error(error); throw new IOException(error); } } log.info(String.format("returned %d msgs in %s ms", ret.size(), now - start)); return ret; } /** * This static method returns all the details of the class without it having * to be constructed. It has description, categories, dependencies, and peer * definitions. * * @return ServiceType - returns all the data * */ static public ServiceType getMetaData() { ServiceType meta = new ServiceType(VirtualDevice.class.getCanonicalName()); meta.addDescription("A service which can create virtual devices, like the virtual Arduino"); meta.addCategory("testing"); // put peer definitions in meta.addPeer("uart", "Serial", "uart"); meta.addPeer("logic", "Python", "logic to implement"); return meta; } public static void main(String[] args) { LoggingFactory.init(Level.INFO); try { String portName = "vport"; Arduino arduino = (Arduino) Runtime.start("arduino", "Arduino"); // Serial serial = arduino.getSerial(); VirtualDevice virtual = (VirtualDevice) Runtime.start("virtual", "VirtualDevice"); virtual.createVirtualArduino(portName); // Runtime.start("webgui", "WebGui"); /* * boolean done = true; if (done){ return; } */ // Python logic = virtual.getLogic(); /* * Serial uart = virtual.getUART(); uart.setCodec("arduino"); Codec codec * = uart.getRXCodec(); codec.setTimeout(1000); uart.setTimeout(100); // * don't want to hang when decoding results... */ arduino.setBoardMega(); arduino.connect(portName); // Runtime.start("gui", "GUIService"); Runtime.start("webgui", "WebGui"); } catch (Exception e) { Logging.logError(e); } } }