Commits

Sebastian Bub committed 2bfdf01

testing and bugfixing for all conditions and for time based analog inputs

Comments (0)

Files changed (7)

 ## Project Status
 
 The project has just started and the author uses it by himself mainly for
-simple output. Bug reports and feature requests are welcome.
+simple output (manually and with cronjobs). If I had to give it a release number, I would say it is a 0.81 release. Bug reports and feature requests are welcome.
 
 
 ## Getting Started
 
 `0 * 16-21 ? * * :: VIRTUALlamp1alreadyOn==0&darknessSensor1in==1&lamp1out=1&VIRTUALlamp1alreadyOn=1`
 
-`0 30 22 ? * * :: VIRTUALlamp1alreadyOn==1&&lamp1out=1&VIRTUALlamp1alreadyOn=0`
+`0 30 22 ? * * :: VIRTUALlamp1alreadyOn==1&lamp1out=1&VIRTUALlamp1alreadyOn=0`
 
 Every minute the virtual variable is checked VIRTUALlamp1alreadyOn. If lamp is not on, the darkness sensor darknessSensor1in is check. If both conditions are true, the lamp lamp1out is turned on and the state is changed. At 22:30, the lamp is turned off and the state is changed.
 
 
 Please also read comments in gpio.conf.
 
-Although it is an analog input port, you must explicitly start reading the value because it may be slow (and blocks your port and your http request). The last read value is saved internally and can be used afterwards with conditions (initial value is -1). Keep in mind that reading may be slow. With manual control, your response may be slow (since reading blocks the port). With automatic control, make sure that the read process has finished or you may check against a previous value.
+Although it is an analog input port, you must explicitly start reading the value (IN) because it may be slow (and blocks your port and your http request). The last read value is saved internally and can be used afterwards with conditions (initial value is -1). Keep in mind that reading may be slow. With manual control, your response may be slow (since reading blocks the port). With automatic control, make sure that the read process has finished or you may check against a previous value.
 
 #### Example:
 
-The following request will read a new value for analogSensor2ti (and blocks until it is read), and will afterwards check if analogSensor2ti > 10000 and may set lamp1out.
+The following request will read a new value for analogSensor2ti (request is blocked until it is read), and will afterwards check if analogSensor2ti > 10000 and may set lamp1out.
 
 `http://raspberrypi:8080/handle?analogSensor2ti=IN&analogSensor2ti>10000&lamp1out=1`
 
 
 `out1=1&analogTI=IN&out2=1` is handled `analogTI=IN&out1=1&out2=1`
 
-`out1=1&in1=IN&out2=1&analogTI=IN&out3=1` is handled `in1=IN&analogTI=IN&out1=1&out2=1&&out3=1`
+`out1=1&in1=IN&out2=1&analogTI=IN&out3=1` is handled `in1=IN&analogTI=IN&out1=1&out2=1&out3=1`
 
 `out1=1&in1==1&out2=1&analogTI=IN&out3=1` (given in1==1 is true) is handled `out1=1&in1==1&analogTI=IN&out2=1&out3=1`
 

gpio.conf.MUST_BE_CHANGED

 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-##############################################################################
+################################################################################
 #
 # simple configuration file to define handling of GPIO ports
 #
 #       2. It is set to the value of DEFAULT.STATE (required).
 #       3. Wait for the time specified in AUTO.TOGGLE (required).
 #       4. It is reconfigured to be an input port.
-#       5. It is read (in a while loop as fast as possible), until a value of (required) !DEFAULT.STATE is returned.
+#       5. It is read (in a while loop as fast as possible), until a value
+#          of (required) not-DEFAULT.STATE is returned.
 #       6. The counter of the while loop is returned (which can be checked with conditions).
 #       7. It stays an input port until next read.
 # - choose a name which does not get you in trouble addressing it
 # - if you do not set the default state, it is defined by the OS (default state
 #   is required on auto toggle and TI direction)
 # - auto toggle interval is specified in ms (requires default state),
-#   e.g. if you set this gpio, it will automatically flip to default state
+#   e.g. if you set a gpio, it will automatically flip to default state
 #   after specified time. auto toggle must be smaller than block time
 # - block time is specified in ms, e.g. you can not set the gpio again during
 #   this interval (ensure that user does not "click multiple times")
-# - choose an appropriate block time for every TI port (unpredictable things may happen)
+# - choose an appropriate block time for every TI port (or unpredictable things
+#   may happen depeding on your hardware)
 # - only comment out those ports that you need (as they are set up)
 #   on startup (they are exported in sysfs)
 

src/main/java/de/derbub/rpigpio/SimpleCommand.java

         }
     }
 
-    public static final Pattern CONDITION_OR_ASSIGN_PATTERN = Pattern.compile("[>=<]?=");
+    public static final Pattern CONDITION_OR_ASSIGN_PATTERN = Pattern.compile("^(.*?)(==|!=|>=|>|<=|<|=)(.*)");
     private static Logger log = Logger.getLogger(SimpleCommand.class);
 
     private String key;
         int intOtherValue = Integer.parseInt(otherValue);
         switch (condition) {
             case EQUAL:
-                return (intValue == intOtherValue);
+                return (intOtherValue == intValue);
             case NOT_EQUAL:
-                return (intValue != intOtherValue);
+                return (intOtherValue != intValue);
             case GREATER:
-                return (intValue > intOtherValue);
+                return (intOtherValue > intValue);
             case GREATER_EQUAL:
-                return (intValue >= intOtherValue);
+                return (intOtherValue >= intValue);
             case LESS:
-                return (intValue < intOtherValue);
+                return (intOtherValue < intValue);
             case LESS_EQUAL:
-                return (intValue <= intOtherValue);
+                return (intOtherValue <= intValue);
             default:
                 throw new RuntimeException(this.getClass().getName() + " is not a condition: " + this);
         }
     public static synchronized ArrayList<SimpleCommand> createCommandList(String commandString) {
         ArrayList<SimpleCommand> tmpList = new ArrayList<SimpleCommand>();
         for (String param : commandString.split("&")) {
-            String[] pair;
-            pair = param.split("[>=<]?=");
-            String key;
-            String value = "";
-            String condition = null;
+            log.debug("read in " + param);
+            Matcher m = CONDITION_OR_ASSIGN_PATTERN.matcher(param);
+            m.matches();
+            if(m.groupCount() != 3){
+                log.warn("ignoring command _" + param + "_ Regexp does not match.");
+                continue;
+            }
+            String key, value, condition;
             try {
-                key = URLDecoder.decode(pair[0], "UTF-8");
-                if (pair.length > 1) {
-                    value = URLDecoder.decode(pair[1], "UTF-8");
-                }
-                Matcher m = CONDITION_OR_ASSIGN_PATTERN.matcher(param);
-                if (m.find()) {
-                    condition = m.group();
-                }
+                key = URLDecoder.decode(m.group(1), "UTF-8");
+                condition = m.group(2);
+                value = URLDecoder.decode(m.group(3), "UTF-8");
             } catch (UnsupportedEncodingException e) {
-                log.warn("received an UnsupportedEncodingException on key or value" + param);
-                key = pair[0];
-                value = pair[1];
+                log.warn("received an UnsupportedEncodingException on key or value for " + param + ". Trying to work on it undecoded.");
+                key = m.group(1);
+                condition = m.group(2);
+                value = m.group(3);
             }
-            tmpList.add(new SimpleCommand(key, value, condition));
-            log.debug("createCommandList() add new SimpleCommand(" + key + ", " + value + ", " + condition + ")");
+            SimpleCommand tmpCmd = new SimpleCommand(key, value, condition);
+            tmpList.add(tmpCmd);
+            log.debug("createCommandList() add new SimpleCommand(" + tmpCmd + ")");
         }
         return tmpList;
     }

src/main/java/de/derbub/rpigpio/SingleContext.java

      */
     public synchronized Map<String, String> handleConditionsAndCommands(ArrayList<SimpleCommand> commandList) {
         Map<String, String> returnMap = new HashMap<String, String>();
-        SimpleCommand[] commandArray = commandList.toArray(new SimpleCommand[34]);  // 17 ports and some optional virtual
+        SimpleCommand[] commandArray = commandList.toArray(new SimpleCommand[commandList.size()]);
         SimpleCommand cmd;
         for (int i = 0; i < commandArray.length; i++) {
             cmd = commandArray[i];
                 }
                 // if it is a condition on a real port
                 Map<String, String> singleInputMap = new HashMap<String, String>();
-                singleInputMap.put(cmd.getKey(), Direction.IN.getValue());
+                singleInputMap.put(cmd.getKey(), Direction.CONDITIONAL_INPUT);
                 Map<String, String> gpioInputMap = gpioInterface.handleGpioPorts(checkGpioMapValidity(singleInputMap));
                 if (null != gpioInputMap && !gpioInputMap.isEmpty()) {
                     String requestedValue = gpioInputMap.get(cmd.getKey());
      * and values
      *
      * @param uncheckedMap Map of gpioNames and values.
-     * @return Map of only valied key/value combinations (i.e. if you try to set an input port, it will be removed here)
+     * @return Map of only valid key/value combinations (i.e. if you try to set an input port, it will be removed here)
      */
     private Map<String, String> checkGpioMapValidity(Map<String, String> uncheckedMap) {
         Map<String, ConfiguredGpio> configuredGpioMap = gpioInterface.getConfiguredGpioMap();
             String uncheckedValue = nameValuesUncheckedMap.get(name).trim();
             ConfiguredGpio gpio = configuredGpioMap.get(name);
             if (gpio.getDirection().isInput()
-                && Direction.IN.equals(Direction.getDirectionByString(uncheckedValue))) {
+                && Direction.CONDITIONAL_INPUT.equals(uncheckedValue)) {
                 nameCheckedValuesMap.put(name, Direction.IN.getValue());
             }
             if (gpio.getDirection().isOutput()) {
                 if (("0".equals(uncheckedValue) || "1".equals(uncheckedValue))) {
                     nameCheckedValuesMap.put(name, uncheckedValue);
                 }
-                if (Direction.IN.equals(Direction.getDirectionByString(uncheckedValue))) {
+                if (Direction.CONDITIONAL_INPUT.equals(uncheckedValue)) {
                     nameCheckedValuesMap.put(name, Direction.IN.getValue());
                 }
             }
-            if (gpio.getDirection().isTimeBasedAnalogIn()
-                && Direction.IN.equals(Direction.getDirectionByString(uncheckedValue))) {
-                nameCheckedValuesMap.put(name, Direction.TI.getValue());
+            if (gpio.getDirection().isTimeBasedAnalogIn()) {
+                // TI is internally handled as out (at least on getting the currentOutputPortState)
+                if (Direction.IN.equals(Direction.getDirectionByString(uncheckedValue))) {
+                    nameCheckedValuesMap.put(name, Direction.TI.getValue());
+                }
+                if (Direction.CONDITIONAL_INPUT.equals(uncheckedValue)) {
+                    nameCheckedValuesMap.put(name, Direction.IN.getValue());
+                }
             }
         }
         return nameCheckedValuesMap;

src/main/java/de/derbub/rpigpio/gpio/Direction.java

 package de.derbub.rpigpio.gpio;
 
 /**
- * Class represents the direction of a gpio pin
+ * Class represents the direction of a gpio pin. In and OUT are simple input or output ports.
+ * TI is a "Timebased analog Input port". Read about it in gpio.conf or README.md
  *
  * @author sb
  */
     IN("in"), OUT("out"), TI("ti");
     private String directionString;
 
+    public static final String CONDITIONAL_INPUT="CIN";
+
     private Direction(String directionString) {
         this.directionString = directionString;
     }

src/main/java/de/derbub/rpigpio/gpio/GpioSysFsInterface.java

      *         set or -1 if it failed or the value of IN
      */
     public synchronized Map<String, String> handleGpioPorts(Map<String, String> nameCheckedValuesMap) {
-        log.debug("handleGpioPorts()");
+        log.debug("handleGpioPorts( " + nameCheckedValuesMap.size() + " ports )");
         Map<String, String> returnMap = new HashMap<String, String>();
 
         Map<ConfiguredGpio, String> batchedGpioOutMap = new HashMap<ConfiguredGpio, String>();
         for (String name : nameCheckedValuesMap.keySet()) {
             ConfiguredGpio gpio = configuredGpioMap.get(name);
+            log.debug("handleGpioPorts working on " + gpio + " value: " + nameCheckedValuesMap.get(name));
             if (gpio.getDirection().isInput()) {
                 String readValue = readGpio(gpio);
                 returnMap.put(name, readValue);
             } else {
-                // if it is a condition on a real port
+                // if real port state is checked (output port or TI)
                 if (Direction.IN.equals(Direction.getDirectionByString(nameCheckedValuesMap.get(name)))) {
-                    returnMap.put(name, (null == currentOutputPortState.get(gpio)
+                    returnMap.put(name, (null != currentOutputPortState.get(gpio)
                         ? currentOutputPortState.get(gpio)
                         : Integer.toString(ConfiguredGpio.NO_STATE)));
                 } else {
                     if (gpio.getDirection().isOutput()) {
+                        // save port in map for later writing
                         batchedGpioOutMap.put(gpio, nameCheckedValuesMap.get(name));
                     }
                     if (gpio.getDirection().isTimeBasedAnalogIn()) {
+                        // TI is read (and written to currentOutputPortState)
                         String readValue = readTimeBasedAnalogIn(gpio);
                         returnMap.put(name, readValue);
                     }
     private synchronized String readTimeBasedAnalogIn(ConfiguredGpio gpio) {
         String returnValue = Integer.toString(ConfiguredGpio.NO_STATE);
         Long nowForBlocking = System.currentTimeMillis();
+        log.debug("readTimeBasedAnalogIn for " + gpio);
 
         // 0. Check that it is not blocked.
         if(isBlocked(gpio, nowForBlocking)){
         // 2. It is set to the value of DEFAULT.STATE
         Map<ConfiguredGpio, String> outMap = new HashMap<ConfiguredGpio, String>(1);
         outMap.put(gpio, Integer.toString(gpio.getDefaultState()));
-        Map<String, String> outWrittenMap = writeGpios(outMap, true);
+        Map<String, String> outWrittenMap = writeGpios(outMap, true); // handle as if it is isAutoTogglingThread (blocking is not checked)
         if(Integer.toString(ConfiguredGpio.NO_STATE).equals(outWrittenMap.get(gpio.getUserdefinedName()))){
             log.error("Could not write default state value to " + gpio);
             return returnValue;
         do {
             currentState = readGpio(gpio);
             counter++;
-        } while (!defaultState.equals(currentState));
+        } while (defaultState.equals(currentState));
 
         // 6. The counter of the while loop is returned (which can be checked with conditions).
+        log.debug("setting value to " + counter + " for " + gpio);
         returnValue = Long.toString(counter);
         currentOutputPortState.put(gpio, returnValue);
         return returnValue;
                 log.warn("Gpio is currently blocked (another " + ((blockedTimestamp + gpio.getBlockTime()) - nowForBlocking) + " ms) for " + gpio);
                 isBlocked = true;
             } else {
-                // TODO? warum?
                 blockedMap.put(gpio, nowForBlocking);
             }
         }

src/main/java/de/derbub/rpigpio/web/GpioServlet.java

     @Override
     public void doGet(HttpServletRequest req, HttpServletResponse resp)
             throws ServletException, IOException {
-        log.debug("Get request for " + req.getRequestURI()
+        log.info("Get request for " + req.getRequestURI()
                 + "?" + req.getQueryString() + " from " + req.getRemoteAddr());
 
         // set/get hardware values