package org.myrobotlab.codec.serial;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.TreeMap;
import org.myrobotlab.codec.CodecUtils;
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.service.Arduino;
import org.myrobotlab.service.Runtime;
import org.slf4j.Logger;
public class ArduinoBindingsGenerator {
public transient final static Logger log = LoggerFactory.getLogger(ArduinoBindingsGenerator.class);
static TreeMap<String, Method> sorted = new TreeMap<String, Method>();
static TreeMap<String, Integer> sensorTypes = new TreeMap<String, Integer>();
static TreeMap<String, Integer> errorTypes = new TreeMap<String, Integer>();
static StringBuilder inoTemplate = new StringBuilder("///// INO GENERATED DEFINITION BEGIN //////\n");
static StringBuilder pythonTemplate = new StringBuilder("##### PYTHON GENERATED DEFINITION BEGIN ######\n");
static StringBuilder javaStaticImports = new StringBuilder("\t///// java static import definition - DO NOT MODIFY - Begin //////\n");
static StringBuilder javaDefines = new StringBuilder("\t///// java ByteToMethod generated definition - DO NOT MODIFY - Begin //////\n");
static StringBuilder javaBindingsInit = new StringBuilder();
static StringBuilder javaFunctionToString = new StringBuilder();
/**
* called for each method - java method is the "source" of reflected data so
* all method context code should be generated in this function
*
* @param key
* @param index
*/
public static void createBindingsFor(String key, int index) {
Method method = sorted.get(key);
StringBuilder msb = new StringBuilder(method.getName());
Class<?>[] params = method.getParameterTypes();
for (int j = 0; j < params.length; ++j) {
msb.append(String.format(" %s", params[j].getSimpleName()));
}
String methodSignatureComment = String.format("// {%s} \n", msb.toString());
String pythonSignatureComment = String.format(" # {%s} \n", msb.toString());
inoTemplate.append(methodSignatureComment);
pythonTemplate.append(pythonSignatureComment);
String underscore = CodecUtils.toUnderScore(method.getName());
inoTemplate.append(String.format("#define %s\t\t%d\n", underscore, index));
pythonTemplate.append(String.format(" %s = %d\n\n", underscore, index));
javaStaticImports.append(String.format("\timport static org.myrobotlab.codec.serial.ArduinoMsgCodec.%s;\n", underscore));
javaDefines.append(String.format("\t%s", methodSignatureComment));
javaDefines.append(String.format("\tpublic final static int %s =\t\t%d;\n\n", underscore, index));
javaBindingsInit.append(String.format("\t\tbyteToMethod.put(%s,\"%s\");\n", underscore, method.getName()));
javaBindingsInit.append(String.format("\t\tmethodToByte.put(\"%s\",%s);\n\n", method.getName(), underscore));
javaFunctionToString.append(String.format("\tcase ArduinoMsgCodec.%s:{\n\t\treturn \"%s\";\n\n\t}\n", underscore ,underscore));
}
/**)
* master method which gets source material for generating msg bindings - the
* source is the Arduino class itself - its method should have a near 1 to 1
* relation to the msgs sent to and from MRLComm.ino These msgs are examined
* and msg bindings are created
*
* @throws IOException
*/
public static void generateDefinitions() throws IOException {
HashMap<String, String> snr = new HashMap<String, String>();
Arduino arduino = (Arduino) Runtime.create("arduino", "Arduino");
Method[] m = arduino.getDeclaredMethods();
for (int i = 0; i < m.length; ++i) {
// String signature = Encoder.getMethodSignature(m[i]);
Method method = m[i];
StringBuilder msb = new StringBuilder(m[i].getName());
Class<?>[] params = method.getParameterTypes();
for (int j = 0; j < params.length; ++j) {
msb.append(String.format(" %s", params[j].toString()));
}
// log.info(String.format("[%s]", msb.toString())); // hmmm
// "class someclass" :(
// get rid of overloads by taking the method with maximum
// complexity(ish) :)
if (sorted.containsKey(method.getName())) {
// overloaded - who has more parameters
Method complex = (sorted.get(method.getName()).getParameterTypes().length < method.getParameterTypes().length) ? method : sorted.get(method.getName());
sorted.put(method.getName(), complex);
} else {
sorted.put(method.getName(), method);
}
// FIXME non Arduino service method protocol defitions added
// like errorTypes
// and sensorTypes
}
HashSet<String> exclude = new HashSet<String>();
// filter out methods which do not get relayed
// from Arduino to MRLComm or Back
// these are all methods 'only' used in Java-Land
// exclude.add("addCustomMsgListener");
exclude.add("connect");
exclude.add("disconnect");
exclude.add("getDescription");
exclude.add("createPinList");
exclude.add("getDeviceId");
exclude.add("setController");
exclude.add("getController");
exclude.add("getMrlDeviceType");
exclude.add("getServoIndex");
exclude.add("getBoardType");
exclude.add("getCategories");
exclude.add("getPeers");
exclude.add("getPinList");
exclude.add("getSerial");
exclude.add("getStepperData");// WTF
exclude.add("isConnected");
exclude.add("main");
exclude.add("onByte");
exclude.add("onCustomMsg");
exclude.add("releaseService");
exclude.add("sendMsg");
exclude.add("setBoardType");
exclude.add("startService");
exclude.add("test");
exclude.add("attach");
exclude.add("detach");
exclude.add("getPin");
exclude.add("setMode");
exclude.add("uploadSketch");
exclude.add("publishStepperEvent");
exclude.add("setStepperSpeed");
exclude.add("stepperAttach");
// additionally force getversion and publishMRLCommError
// so that mis-matches of version are quickly reported...
exclude.add("getVersion");
exclude.add("publishVersion");
exclude.add("publishMRLCommError");
// except.add("E");
// getter & setters
exclude.add("setRXFormatter");
exclude.add("setTXFormatter");
exclude.add("getRXFormatter");
exclude.add("getTXFormatter");
exclude.add("getRXCodecKey");
exclude.add("getTXCodecKey");
exclude.add("connectVirtualUART");
exclude.add("getSketch");
exclude.add("onConnect");
exclude.add("onDisconnect");
exclude.add("setBoard");
exclude.add("setSketch");
// stuff which never gets down to the uC
exclude.add("getDataSinkType");
exclude.add("getSensorConfig");
exclude.add("getSensorType");
exclude.add("update");
exclude.add("stopService");
exclude.add("refresh");
exclude.add("pinNameToAddress");
exclude.add("refreshVersion");
exclude.add("getPortName");
exclude.add("setBoardMega");
exclude.add("setBoardUno");
exclude.add("createVirtual");
exclude.add("getMetaData");
exclude.add("processMessage");
exclude.add("enabledHeartbeat");
int index = 0;
// forcing 3 methods to be always the same index publishMRLCommError 0, getVersion 2 publishVersion 3
// that way you can get a meaningful error if you have the wrong version or mrlcomm
++index;
createBindingsFor("publishMRLCommError", index);
++index;
createBindingsFor("getVersion", index);
++index;
createBindingsFor("publishVersion", index);
++index;
for (String key : sorted.keySet()) {
if (exclude.contains(key)) {
continue;
}
createBindingsFor(key, index);
++index;
// log.info(); // hmmm "class someclass" :(
}
inoTemplate.append("///// INO GENERATED DEFINITION END //////\n");
pythonTemplate.append("##### PYTHON GENERATED DEFINITION END #####\n");
snr.put("<%=javaStaticImports%>", javaStaticImports.toString());
snr.put("<%=java.defines%>", javaDefines.toString());
snr.put("<%=java.bindings.init%>", javaBindingsInit.toString());
snr.put("<%=javaFunctionToString%>", javaFunctionToString.toString());
snr.put("<%=mrlcomm.defines%>", inoTemplate.toString());
snr.put("<%=python.defines%>", pythonTemplate.toString());
log.info(inoTemplate.toString());
log.info(javaBindingsInit.toString());
// file template filtering
String java = FileIO.toString("src/resource/generate/ArduinoMsgCodec.java.template");
String python = FileIO.toString("src/resource/generate/pythonTemplate.txt");
String MRLComm = FileIO.toString("src/resource/generate/ArduinoMsgCodec.h.template");
// merge data with template
for (String key : snr.keySet()) {
log.info(snr.get(key));
java = java.replace(key, snr.get(key));
python = python.replace(key, snr.get(key));
MRLComm = MRLComm.replace(key, snr.get(key));
}
long ts = System.currentTimeMillis();
FileIO.toFile(String.format("ArduinoMsgCodec.%d.py", ts), python);
FileIO.toFile("src/org/myrobotlab/codec/serial/" + String.format("ArduinoMsgCodec.java", ts), java);
FileIO.toFile(String.format("src/resource/Arduino/MRLComm/ArduinoMsgCodec.h", ts), MRLComm);
// String ret = String.format("%s\n\n%s", ino.toString(),
// java.toString());
// log.info(ret);
// to define (upper with underscore)
Runtime.exit();
}
public static void main(String[] args) {
try {
LoggingFactory.init(Level.INFO);
String t = "testMethod";
// camelback to underscore
/*
* String regex = "[A-Z\\d]"; String replacement = "$1_";
* log.info(t.replaceAll(regex, replacement)); log.info(t);
*/
log.info(CodecUtils.toUnderScore(t));
log.info(CodecUtils.toCamelCase(CodecUtils.toUnderScore(t)));
ArduinoBindingsGenerator.generateDefinitions();
} catch (Exception e) {
Logging.logError(e);
}
}
}