diff --git a/src/com/dynamicperception/nmxcommandline/main/NMXCommandLine.java b/src/com/dynamicperception/nmxcommandline/main/NMXCommandLine.java index 04e2e9a..4d8acf1 100644 --- a/src/com/dynamicperception/nmxcommandline/main/NMXCommandLine.java +++ b/src/com/dynamicperception/nmxcommandline/main/NMXCommandLine.java @@ -1,6 +1,8 @@ package com.dynamicperception.nmxcommandline.main; +import java.io.File; import java.io.IOException; +import java.io.PrintStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -15,6 +17,7 @@ import com.dynamicperception.nmxcommandline.helpers.Consts; import com.dynamicperception.nmxcommandline.models.Command; import com.dynamicperception.nmxcommandline.models.NMXComs; +import com.dynamicperception.nmxcommandline.models.Command.Names.General; public class NMXCommandLine { @@ -22,8 +25,8 @@ public class NMXCommandLine { private static Serial serial; final static String DELIMITER = " "; //private static long lastTime; - private static String version = "0.2-beta"; - + private static String version = "0.3-beta"; + public static void main(String[] args) { // Create serial object serial = new Serial(); @@ -38,7 +41,7 @@ public static void main(String[] args) { } try{ - serial.openPort(args[0]); + serial.openPort(Integer.parseInt(args[0])); }catch(RuntimeException e){ Console.pln("Invalid port! Either you picked the wrong number or you have the wrong syntax.\n" + "A full one-time execution should look something like this: " @@ -60,6 +63,27 @@ public static void main(String[] args) { // Print help printHelp(); + /* + * Send an arbitrary command to clear initial connection hiccup. Also + * reroute system out to temporary file to avoid printing the error + * this will likely generate. + */ + try { + PrintStream oldOut = System.out; + File tempFile = new File("temp.txt"); + tempFile.createNewFile(); + PrintStream tempOut = new PrintStream(tempFile); + System.setOut(tempOut); + Command.execute(General.GET_FIRMWARE); + System.setOut(oldOut); + tempOut.close(); + tempFile.delete(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + // Enter program loop execute = true; while(execute){ @@ -74,7 +98,7 @@ private static void printTerminalHelp(){ Console.pln("\nIn order to enter the interactive command line tool, run this JAR file without arguments.\n" + "Alternatively, you may run it with the following arguments to execute a single NMX command and then\n" + "immediately disconnect from the controller and close this application:\n\n" - + "\"NMXCommander.jar . \"\n\n" + + "\"NMXCommander.jar [PORT NAME] [COMMAND TYPE].[COMMAND NAME] [DATA or MOTOR # (if required)] [MOTOR DATA (if required)]\"\n\n" + "If you're not familiar with the command types, names, data, etc., open the application in interactive\n" + "mode first and read the extended help there."); } @@ -86,16 +110,32 @@ private static void printHelp(){ Console.pln("\n\n******** NMX Commander " + version + " Overview ********\n\n" + "This command line tool allows you to manually send single instuctions to the NMX controller.\n" + "This is done by giving input with the following syntax:\n\n" - + "Non-motor commands -- \". \"\n" - + "Motor commands -- \"m. \"\n\n" + + "Non-motor commands -- \"[COMMAND TYPE].[COMMAND NAME] [DATA (if required)]\"\n" + + "Motor commands -- \"m.[COMMAND NAME] [MOTOR #] [DATA (if required)]\"\n\n" + "Non-motor command types include \"g\" (general), \"c\" (camera), and \"k\" (key frame)\n" + "When specifying the motor number for motor commands, counting starts at 0 (i.e. valid motor #s are 0, 1, 2)\n\n\n" + + "******** NMX Commander Configuration Commands ********\n\n" + + "* \"outputAddress [ADDR # optional argument]\" -- without optional parameter, reports the current command address. With the\n" + + "optional parameter, sets the address to which the command will be sent. This is 3 by default and should not be changed unless\n" + + "you have manually reassigned the address of one or more of your controllers. !!!Sending commands to an incorrect address will\n" + + "cause you lots of headaches!!!\n\n" + + "* \"commandDetail [0/1 optional argument]\" -- without optional parameter, reports whether commandDetail is enabled. With the\n" + + "optional parameter, sets commandDetail enabled. If true, NMX Commander will print the command name and any additional data\n" + + "output to the NMX\n\n" + + "* \"serialDetail [0/1 optional argument]\" -- without optional parameter, reports whether serialDetail is enabled. With the\n" + + "optional parameter, sets serialDetail enabled. If true, NMX Commander will print the raw packet received from the NMX for\n" + + "each command\n\n\n" + + "* \"responseTimout [TIMEOUT optional argument]\" -- without optional parameter, reports current response timeout in milliseconds\n" + + "With optional parameter, sets timeout length. If you find that you encounter a situation where you are getting many timeouts that\n" + + "are not causing problems (e.g. you don't care about the response contents, but the command seems to be executing properly), you\n" + + "may want to shorten the timout in order to increase command throughput.\n\n\n" + "******** Other Useful Commands ********\n\n" + "* \"help\" -- prints this information again\n\n" - + "* \". -h\" -- prints command-specific help\n\n" - + "* \"list \" -- lists all commands of that type\n\n" - + "* \"find .\" -- returns all commands of that type containing the search term\n\n" - + "* \"runMacro \" -- runs a command macro list from a text file. Type \"runMacro\" without arguments\n" + + "* \"[COMMAND TYPE].[COMMAND NAME] -h\" -- prints command-specific help\n\n" + + "* \"list [COMMAND TYPE]\" -- lists all commands of that type\n\n" + + "* \"find [COMMAND TYPE].[SEARCH TERM]\" -- returns all commands of that type containing the search term\n\n" + + "* \"repeat [N] [COMMAND TYPE].[COMMAND NAME]\" -- repeats the given command N times\n\n" + + "* \"runMacro [PATH]\" -- runs a command macro list from a text file. Type \"runMacro\" without arguments\n" + "for macro file syntax\n\n" + "* \"exit\" -- closes the serial port and exits the application\n\n\n" + "******** Some Important Tips ********\n\n" @@ -110,7 +150,7 @@ private static void printHelp(){ + "BE CAREFUL ABOUT THIS! If you accidentally send your rig two or four times as far as you intended, you can break\n" + "your valueable equipment!\n\n" + "* If you try to send a motor to a position and it either does not move or does not go all the way to where you sent it,\n" - + "try running the command \"m.resetLimits \" to clear any end limits that may be restricting movement."); + + "try running the command \"m.resetLimits [MOTOR #]\" to clear any end limits that may be restricting movement."); } /** @@ -175,14 +215,52 @@ else if(args.get(0).indexOf("//") != -1){ else if(args.get(0).equals("help")){ printHelp(); } + else if(args.get(0).equals("commandDetail")){ + try{ + boolean enabled = args.get(1).equals("0") ? false : true; + Command.setCommandDetail(enabled); + }catch(IndexOutOfBoundsException e){ + System.out.println("Command detail enabled? : " + Command.getCommandDetail()); + } + } + else if(args.get(0).equals("serialDetail")){ + try{ + boolean enabled = args.get(1).equals("0") ? false : true; + NMXComs.setSerialDetail(enabled); + }catch(IndexOutOfBoundsException e){ + System.out.println("Serial detail enabled? : " + NMXComs.getSerialDetail()); + } + } + else if(args.get(0).equals("outputAddress")){ + try{ + int addr = Integer.parseInt(args.get(1)); + Command.setAddr(addr); + }catch(IndexOutOfBoundsException e){ + System.out.println("Current command address: " + Command.getAddr()); + }catch(NumberFormatException e){ + System.out.println("Invalid address. Must be an integer."); + } + + } + else if(args.get(0).equals("responseTimeout")){ + try{ + int timeout = Integer.parseInt(args.get(1)); + NMXComs.setResponseTimeout(timeout); + }catch(IndexOutOfBoundsException e){ + System.out.println("Current timout in milliseconds: " + NMXComs.getResponseTimeout()); + }catch(NumberFormatException e){ + System.out.println("Invalid address. Must be an integer."); + } + + } // Run command list from file else if(args.get(0).equals("runMacro")){ try { if(args.size() == 1){ - Console.pln("\nrunMacro syntax -- \"runMacro \""); + Console.pln("\nrunMacro syntax -- \"runMacro [PATH]\""); Console.pln("Example -> \"runMacro c:\\NMXmacro.txt\"\n"); Console.pln("The text file should have one command on each line with the following syntax:\n\n" - + "\" . \"\n\n" + + "\"[DELAY TIME] [COMMAND TYPE].[COMMAND NAME] [DATA or MOTOR # (if required)] [MOTOR DATA (if required)]\"\n\n" + "The following example enables the camera, sets the focus time to 600ms, trigger time to 100ms, sets home\n" + "for each of the motors (e.g. sets current position to 0), immediately takes an exposure, commands the motors to\n" + "a new position, waits 5000ms, takes another exposure, waits 1000ms, commands the motors back to their original\n" @@ -231,7 +309,7 @@ else if(args.get(0).equals("replayLog")){ // Help request else if(args.get(0).toLowerCase().equals("list")){ if(args.size() < 2){ - Console.pln("List syntax -- \"list \""); + Console.pln("List syntax -- \"list [COMMAND TYPE]\""); Console.pln("Example -> \"list c\" prints the list of valid camera commands"); return; } @@ -239,20 +317,24 @@ else if(args.get(0).toLowerCase().equals("list")){ return; } // Repeat the following command n times. (e.g. "repeat 2 m.getMS 0") - else if(args.get(0).equals("repeat")){ - int count = Integer.parseInt(args.get(1)); - for(int i = 0; i < 2; i++){ - args.remove(0); + else if(args.get(0).equals("repeat")){ + try{ + int count = Integer.parseInt(args.get(1)); + for(int i = 0; i < 2; i++){ + args.remove(0); + } + for(int i = 0; i < count; i++){ + parseCommand(args); + } + }catch(IndexOutOfBoundsException | NumberFormatException e){ + Console.pln("Invalid syntax. Try \"repeat [#] [command]\""); } - for(int i = 0; i < count; i++){ - parseCommand(args); - } return; } // Find command name else if(args.get(0).equals("find")){ if(args.size() < 2){ - Console.pln("Find syntax -- \"find .\""); + Console.pln("Find syntax -- \"find [COMMAND TYPE].[SEARCH TERM]\""); Console.pln("Example -> \"find m.speed\" prints the following:"); List exampleArgs = Arrays.asList("find", "m.speed"); findCommand(exampleArgs); diff --git a/src/com/dynamicperception/nmxcommandline/models/Command.java b/src/com/dynamicperception/nmxcommandline/models/Command.java index 257296d..5115c45 100644 --- a/src/com/dynamicperception/nmxcommandline/models/Command.java +++ b/src/com/dynamicperception/nmxcommandline/models/Command.java @@ -14,7 +14,7 @@ public class Command { private final static int MOTOR_COUNT = 3; private static int addr = 3; private static int currentControllerNum = 0; - private static boolean debug = true; + private static boolean commandDetail = false; private static List generalList = new ArrayList(); private static List motorList = new ArrayList(); private static List cameraList = new ArrayList(); @@ -157,10 +157,6 @@ else if(returnType == Boolean.class){ ret = (T) Void.class.cast(null); } - // Print debug if necessary - if(debug){ - System.out.println("Command: " + thisCommand.name); - } if(ret != null) System.out.println(ret); else @@ -966,14 +962,22 @@ public static Command get(Type type, int command){ throw new UnsupportedOperationException(); } - public static void setDebug(boolean debug){ - Command.debug = debug; + public static void setCommandDetail(boolean enabled){ + Command.commandDetail = enabled; + } + + public static boolean getCommandDetail(){ + return Command.commandDetail; } public static void setAddr(int addr){ Command.addr = addr; } + public static int getAddr(){ + return Command.addr; + } + public static int getControllerNum(){ return currentControllerNum; } @@ -1117,18 +1121,20 @@ private T executeThis(int subAddr, String dataStr, boolean hasData){ data = (int) Math.round(Float.parseFloat(dataStr)); } } - - // Print debug if necessary - if(debug){ - System.out.println("Command: " + this.name + " Input data: " + dataStr); - } + String commandPacket = ""; + // Send the command to the NMX if(hasData){ - NMXComs.cmd(addr, subAddr, this.command, this.dataLength, data); + commandPacket = NMXComs.cmd(addr, subAddr, this.command, this.dataLength, data); } else{ - NMXComs.cmd(addr, subAddr, this.command); + commandPacket = NMXComs.cmd(addr, subAddr, this.command); + } + + // Print debug if necessary + if(commandDetail){ + System.out.println("Command: " + commandPacket + " Output data: " + dataStr); } // Wait for the NMX to clear diff --git a/src/com/dynamicperception/nmxcommandline/models/NMXComs.java b/src/com/dynamicperception/nmxcommandline/models/NMXComs.java index 94397b3..e493085 100644 --- a/src/com/dynamicperception/nmxcommandline/models/NMXComs.java +++ b/src/com/dynamicperception/nmxcommandline/models/NMXComs.java @@ -33,6 +33,7 @@ public class NMXComs { private static boolean responseOn = true; private static String response = ""; private static int responseVal; + private static int responseTimeout = 500; private static int emptyResponseCount = 0; @@ -46,6 +47,11 @@ public static void setSerialDetail(boolean enabled){ serialDetail = enabled; } + public static boolean getSerialDetail(){ + return serialDetail; + } + + /** * Sets any manually configured data that will be added before the main data segment of the * packet. This is used for a few commands that require direction byte before other data @@ -84,6 +90,22 @@ public static boolean isSendingCommand(){ return sendingCommand; } + /** + * Sets the response timeout + * @param timeout response timeout in milliseconds + */ + public static void setResponseTimeout(int timeout){ + responseTimeout = timeout; + } + + /** + * Gets the response timeout + * @return response timeout in milliseconds + */ + public static int getResponseTimeout(){ + return responseTimeout; + } + /** * @return The value associated with the response to the last command sent */ @@ -112,12 +134,12 @@ protected static int getResponseVal(){ * @param command * NMX command number (DEC) */ - public static void cmd(int addr, int subAddr, int command) { - cmd(addr, subAddr, command, 0, 0); + public static String cmd(int addr, int subAddr, int command) { + return cmd(addr, subAddr, command, 0, 0); } - public static void cmd(int addr, int subAddr, int command, boolean getException) throws InterruptedException{ - cmd(addr, subAddr, command, 0, 0, true, true); + public static String cmd(int addr, int subAddr, int command, boolean getException) throws InterruptedException{ + return cmd(addr, subAddr, command, 0, 0, true, true); } @@ -135,17 +157,18 @@ public static void cmd(int addr, int subAddr, int command, boolean getException) * @param data * Data to be included in the packet */ - public static void cmd(int addr, int subAddr, int command, int length, int data) { + public static String cmd(int addr, int subAddr, int command, int length, int data) { try { - cmd(addr, subAddr, command, length, data, true); + return cmd(addr, subAddr, command, length, data, true); } catch (InterruptedException e) { System.out.println("NMX Command interrupted during waiting period"); e.printStackTrace(); + return "ERROR"; } } - public static void cmd(int addr, int subAddr, int command, int length, int data, boolean getResponse, boolean getException) throws InterruptedException{ - cmd(addr, subAddr, command, length, data, getResponse); + public static String cmd(int addr, int subAddr, int command, int length, int data, boolean getResponse, boolean getException) throws InterruptedException{ + return cmd(addr, subAddr, command, length, data, getResponse); } /** @@ -166,7 +189,7 @@ public static void cmd(int addr, int subAddr, int command, int length, int data, * If this parameter is false, the program will not wait for a * response from the NMX */ - public static void cmd(int addr, int _subAddr, int _command, int _length, int _data, boolean getResponse) throws InterruptedException { + public static String cmd(int addr, int _subAddr, int _command, int _length, int _data, boolean getResponse) throws InterruptedException { responseOn = getResponse; @@ -202,6 +225,8 @@ public static void cmd(int addr, int _subAddr, int _command, int _length, int _d // Set command ready flag to trigger command thread sendingCommand = true; + + return commandPacket; } @@ -234,7 +259,7 @@ public static int getEmptyResponseCount(){ private static void parseResponse() { if(serialDetail) - System.out.println("Response before parsing: " + response); + System.out.println("NMX raw response: " + response); if(!responseOn){ responseVal = Consts.ERROR; @@ -281,7 +306,7 @@ private static void parseResponse() { System.out.println("Error parsing data type: " + response); } catch (StringIndexOutOfBoundsException e) { if(serialDetail) - System.out.println("Out of bounds!!!"); + System.out.println("Response value out of bounds!!!"); } responseVal = (int) data; @@ -357,8 +382,7 @@ public void run(){ // Wait for full packet before proceeding long waitStart = System.currentTimeMillis(); // Give up if the first byte isn't received before the start timeout, - // or the packet isn't finished before the final timeout - final int FINAL_TIMEOUT = 90; + // or the packet isn't finished before the final timeout final int DATA_LENGTH_BYTE = 9; while(waitingForPreamble || dataRemaining > 0){ if(serial.available() > 0){ @@ -388,8 +412,9 @@ else if(index > DATA_LENGTH_BYTE){ } // Check for timeouts long curTime = System.currentTimeMillis(); - if(curTime - waitStart > FINAL_TIMEOUT){ + if(curTime - waitStart > responseTimeout){ response = ERROR_STR; + serial.clear(); break; } } @@ -400,6 +425,7 @@ else if(index > DATA_LENGTH_BYTE){ response = response + debug; } } + // Extract the data from the response packet parseResponse();