]> gitweb.hamatoma.de Git - jpinet/commitdiff
variable assignment works, comboboxes
authorhama <hama@siduction.net>
Wed, 27 Jul 2016 18:51:19 +0000 (20:51 +0200)
committerhama <hama@siduction.net>
Wed, 27 Jul 2016 18:51:19 +0000 (20:51 +0200)
* Expression: assignment tested
* textfields replaced by NumberField

13 files changed:
src/main/java/de/republib/expr/Expression.java
src/main/java/de/republib/expr/ExpressionException.java [new file with mode: 0644]
src/main/java/de/republib/expr/OpCode.java
src/main/java/de/republib/expr/ParserException.java
src/main/java/de/republib/expr/Scanner.java
src/main/java/de/republib/expr/Variant.java
src/main/java/de/republib/expr/VariantException.java
src/main/java/de/republib/gui/NumberField.java [new file with mode: 0644]
src/main/java/de/republib/net/TcpClient.java
src/main/java/de/republib/pinet/gui/ControlCenter.java
src/main/java/de/republib/pinet/gui/GPIOSettings.java
src/test/java/de/republib/expr/ExpressionTest.java
src/test/java/de/republib/expr/VariantTest.java

index bd0219a215fa5d88fdc9caf9b264248940389f1a..8b256d3937bbb1e88031d87ef17cf16eb7de1dad 100644 (file)
@@ -25,6 +25,72 @@ import de.republib.util.I18N;
  *
  */
 public class Expression {
+       protected static Expression instance = null;
+
+       /**
+        * Evaluate a formula and returns the long value.
+        *
+        * <b>Note</b>:
+        * <ul>
+        * <li>not thread save: uses a singleton instance</li>
+        * <li>Variables defined in the formula live in singleton "forever"</li>
+        * </ul>
+        *
+        * @param text
+        *            formula text
+        * @return the result of the formula as long
+        * @throws ExpressionException
+        */
+       public static long asLong(final String text) throws ExpressionException {
+               final Variant result = Expression.getInstance().expr(text);
+               if (!result.isBasicType(DataType.LONG)) {
+                       throw new VariantException(
+                                       String.format(I18N.tr("Expected type: INTEGER found: %s: %s"), result.getType().name(), text));
+               }
+               return result.getLongValue();
+       }
+
+       /**
+        * Evaluate a formula and returns the long value.
+        *
+        * <b>Note</b>:
+        * <ul>
+        * <li>not thread save: uses a singleton instance</li>
+        * <li>error messages will be suppressed</li>
+        * <li>Variables defined in the formula live in singleton "forever"</li>
+        * </ul>
+        *
+        * @param text
+        *            formula text
+        * @param defaultValue
+        *            result if the formula contain errors
+        * @return <i>defaultValue</i>: error occurred<br>
+        *         otherwise: the result of the formula as long
+        */
+       public static long asLong(final String text, long defaultValue) {
+               long rc = defaultValue;
+               Variant result;
+               try {
+                       result = Expression.getInstance().expr(text);
+                       if (result.isType(DataType.LONG)) {
+                               rc = result.getLongValue();
+                       }
+               } catch (final ParserException e) {
+                       // messages are suppressed
+               }
+               return rc;
+       }
+
+       /**
+        * @return the instance
+        */
+       public static Expression getInstance() {
+               if (Expression.instance == null) {
+                       Expression.instance = new Expression("");
+               }
+               return Expression.instance;
+       }
+
        protected Scanner scanner;
        protected HashMap<String, Variable> variables = new HashMap<>();
        protected Stack<Variant> values = new Stack<>();
@@ -58,7 +124,8 @@ public class Expression {
                }
                this.values.push(value1);
                Token token;
-               while (!(token = this.scanner.nextNotSpaceToken()).isType(TokenType.END_OF_STRING)) {
+               boolean again = true;
+               while (again && !(token = this.scanner.nextNotSpaceToken()).isType(TokenType.END_OF_STRING)) {
                        if (token.isOp(OpCode.RIGHT_PARENT)) {
                                break;
                        }
@@ -70,14 +137,18 @@ public class Expression {
                        final OpCode op = token.getOpCode();
                        Variant value2;
                        final Token tokenOp = token.copy();
-                       if (op == OpCode.ASSIGN) {
-                               if (!value1.isType(DataType.VARIABLE)) {
+                       if (op == OpCode.ASSIGNMENT) {
+                               if (!value1.isRValue()) {
                                        throw new ParserException(I18N.tr("Assignment misses a rvalue (e.g. a variable) on the left side"),
                                                        token);
                                }
                                value2 = expr();
+                               // we may not read the next token!
+                               again = false;
                        } else {
-                               value2 = term();
+                               if ((value2 = term()) != null) {
+                                       value2 = value2.lValue();
+                               }
                        }
                        if (value2 == null) {
                                throw new ParserException(I18N.tr("unexpected end of string"), this.scanner.getLastToken(), false);
@@ -93,7 +164,7 @@ public class Expression {
                        reduceStack();
                }
                value1 = this.values.pop();
-               return value1;
+               return value1.lValue();
        }
 
        /**
@@ -129,6 +200,13 @@ public class Expression {
                return rc;
        }
 
+       /**
+        * @return the scanner
+        */
+       public Scanner getScanner() {
+               return this.scanner;
+       }
+
        /**
         * Finishes the last stored binary operation.
         *
@@ -139,6 +217,12 @@ public class Expression {
                final Variant operand1 = this.values.lastElement();
                final Token op = this.operators.pop();
                try {
+                       final OpCode op2 = op.getOpCode();
+                       if (op2 != OpCode.ASSIGNMENT && operand1.isRValue()) {
+                               // We may not change the variable value, we change it to a
+                               // lvalue:
+                               operand1.redefine(operand1.lValueImmutable());
+                       }
                        operand1.binaryOperation(op.getOpCode(), operand2);
                } catch (final VariantException e) {
                        // transform in a message with position info:
diff --git a/src/main/java/de/republib/expr/ExpressionException.java b/src/main/java/de/republib/expr/ExpressionException.java
new file mode 100644 (file)
index 0000000..7a9e9cb
--- /dev/null
@@ -0,0 +1,22 @@
+/**
+ *
+ */
+package de.republib.expr;
+
+/**
+ * Base class of all exception used in the package de.republib.expr.
+ *
+ * @author hm
+ *
+ */
+public class ExpressionException extends Exception {
+       /**
+        * Constructor.
+        * 
+        * @param message
+        *            describes the reason of the exception
+        */
+       public ExpressionException(final String message) {
+               super(message);
+       }
+}
index 1985943f7555a85ba04b26c8093c919826f7bb1b..65db894b2c028a090f59b7eeacda3c0d49a3abcb 100644 (file)
@@ -1,7 +1,7 @@
 package de.republib.expr;
 
 public enum OpCode {
-       UNDEF(0), ASSIGN(1, false, true), //
+       UNDEF(0), ASSIGNMENT(1, false, true), //
        PLUS(10, true), MINUS(10, true), //
        TIMES(11), DIV(11), MOD(11), //
        POWER(12), //
index 4d3df7c335eb02084d19143df53ddd7a6eff7c5d..bde06d52b65fa6a52dedd46708438f0541af7f32 100644 (file)
@@ -4,12 +4,12 @@
 package de.republib.expr;
 
 /**
- * Handles scanner errors like "unknown token".
+ * Handles scanner and parser errors like "unknown token".
  *
  * @author hm
  *
  */
-public class ParserException extends Exception {
+public class ParserException extends ExpressionException {
        private static final long serialVersionUID = 1L;
 
        /**
index 5fb54ec23df89f53007331a125ec9cd8891c8765..756d0ea88d59c86c382d03c65ab2d750860be2b2 100644 (file)
@@ -184,7 +184,7 @@ public class Scanner {
                        op = OpCode.RIGHT_PARENT;
                        break;
                case '=':
-                       op = OpCode.ASSIGN;
+                       op = OpCode.ASSIGNMENT;
                        break;
                default:
                        rc = false;
index 9d9c36cb7b2b3d0e7a0da2e2b991a5f71ad91d1c..5a02690f028c7e650409c34ee05d73364d006182 100644 (file)
@@ -3,6 +3,9 @@
  */
 package de.republib.expr;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import de.republib.util.I18N;
 
 /**
@@ -11,7 +14,8 @@ import de.republib.util.I18N;
  * @author hm
  *
  */
-public class Variant {
+public class Variant implements Cloneable {
+       private static final Logger logger = LoggerFactory.getLogger(Variant.class);
        protected DataType type;
        protected long longValue = Long.MIN_VALUE;
        protected double doubleValue = Double.NaN;
@@ -95,24 +99,27 @@ public class Variant {
         */
        public long asLong(long defaultValue) {
                long rc = defaultValue;
-               if (this.longValue != Long.MIN_VALUE) {
-                       rc = this.longValue;
+               final Variant lValue = this.lValueImmutable();
+               // Is the cache defined?
+               if (lValue.longValue != Long.MIN_VALUE) {
+                       rc = lValue.longValue;
                } else {
-                       switch (this.type) {
+                       // fill the cache:
+                       switch (lValue.type) {
                        case BOOLEAN:
-                               rc = this.longValue = this.boolValue ? 1L : 0L;
+                               rc = lValue.longValue = lValue.boolValue ? 1L : 0L;
                                break;
                        case DOUBLE:
-                               rc = this.longValue = Math.round(this.doubleValue);
+                               rc = lValue.longValue = Math.round(lValue.doubleValue);
                                break;
                        case LONG:
-                               rc = this.longValue;
+                               rc = lValue.longValue;
                                break;
                        case STRING: {
                                try {
-                                       final Token token = new Scanner(this.stringValue).nextNotSpaceToken();
+                                       final Token token = new Scanner(lValue.stringValue).nextNotSpaceToken();
                                        if (token.isType(TokenType.INTEGER)) {
-                                               rc = this.longValue = token.getLongValue();
+                                               rc = lValue.longValue = token.getLongValue();
                                        }
                                } catch (final ParserException e) {
                                        // do nothing
@@ -120,9 +127,10 @@ public class Variant {
                                break;
                        }
                        case VARIABLE:
-                               rc = this.longValue = this.variableValue.getValue().asLong(defaultValue);
+                               Variant.logger.error("lValue has type VARIABLE: value: " + lValue.toString());
                                break;
                        default:
+                               Variant.logger.error("unknown Variant.type: " + lValue.type.name());
                                break;
                        }
                }
@@ -135,30 +143,31 @@ public class Variant {
         * @return a string representation of the value
         */
        public String asString() {
-               if (this.stringValue == null) {
-                       switch (this.type) {
+               final Variant lValue = lValueImmutable();
+               if (lValue.stringValue == null) {
+                       switch (lValue.type) {
                        case BOOLEAN:
-                               this.stringValue = this.boolValue ? "true" : "false";
+                               lValue.stringValue = lValue.boolValue ? "true" : "false";
                                break;
                        case DOUBLE:
-                               this.stringValue = String.valueOf(this.doubleValue);
+                               lValue.stringValue = String.valueOf(lValue.doubleValue);
                                break;
                        case LONG:
-                               this.stringValue = String.valueOf(this.longValue);
+                               lValue.stringValue = String.valueOf(lValue.longValue);
                                break;
                        case OBJECT:
-                               this.stringValue = this.objectValue == null ? "" : this.objectValue.toString();
+                               lValue.stringValue = lValue.objectValue == null ? "" : lValue.objectValue.toString();
                                break;
                        case VARIABLE:
-                               this.stringValue = this.variableValue.getValue().asString();
+                               Variant.logger.error("lValue has type VARIABLE: value: " + lValue.toString());
                                break;
                        case STRING:
                        default:
-                               this.stringValue = "";
+                               lValue.stringValue = "";
                                break;
                        }
                }
-               return this.stringValue;
+               return lValue.stringValue;
        }
 
        /**
@@ -173,7 +182,7 @@ public class Variant {
         */
        Variant binaryOperation(OpCode op, Variant operand) throws VariantException {
                switch (op) {
-               case ASSIGN:
+               case ASSIGNMENT:
                        if (this.type != DataType.VARIABLE) {
                                throw new VariantException(I18N.tr("Assignment misses a rvalue (e.g. a variable) on the left side"));
                        }
@@ -259,6 +268,21 @@ public class Variant {
                return this;
        }
 
+       /**
+        * Copies the instance.
+        *
+        * @return a flat copy of the instance
+        */
+       public Variant copy() {
+               Variant rc = null;
+               try {
+                       rc = (Variant) this.clone();
+               } catch (final CloneNotSupportedException e) {
+                       Variant.logger.error("cloning failed: ", e);
+               }
+               return rc;
+       }
+
        /**
         * @return the boolValue
         */
@@ -308,6 +332,37 @@ public class Variant {
                return this.variableValue;
        }
 
+       /**
+        * Returns whether the instance has a given type
+        *
+        * It will be tested the data type at the end of the reference chain (if
+        * available), e.g. if the instance is a variable the data type if the
+        * variable is tested (not the type VARIABLE).
+        *
+        * @param type
+        *            data type to test
+        * @return <i>true</i>: the instance's data type has the given type
+        */
+       public boolean isBasicType(DataType type) {
+               Variant value = this;
+               while (value.isType(DataType.VARIABLE)) {
+                       value = this.variableValue.getValue();
+               }
+               final boolean rc = value.isType(type);
+               return rc;
+       }
+
+       /**
+        * Returns whether the instance can be a right side of an assignment, e.g. a
+        * variable.
+        *
+        * @return <i>true</i>: the instance is a rvalue
+        */
+       public boolean isRValue() {
+               final boolean rc = this.type == DataType.VARIABLE;
+               return rc;
+       }
+
        /**
         * Tests whether the data type is equal to a given type.
         *
@@ -319,6 +374,40 @@ public class Variant {
                return this.type == expected;
        }
 
+       /**
+        * Returns the "left value" of the value: variables will be dereferenced.
+        *
+        * @return the value at the end of the reference chain (value of the
+        *         variable of the variable...)
+        */
+       public Variant lValue() {
+               Variant rc = this;
+               while (rc.isType(DataType.VARIABLE)) {
+                       rc = this.variableValue.getValue();
+                       if (!rc.isType(DataType.VARIABLE)) {
+                               rc = rc.copy();
+                               break;
+                       }
+               }
+               return rc;
+       }
+
+       /**
+        * Returns the "left value" of the value: variables will be dereferenced.
+        *
+        * Note: never change the result: it may be the value of a variable.
+        *
+        * @return the value at the end of the reference chain (value of the
+        *         variable of the variable...)
+        */
+       protected Variant lValueImmutable() {
+               Variant rc = this;
+               while (rc.isType(DataType.VARIABLE)) {
+                       rc = this.variableValue.getValue();
+               }
+               return rc;
+       }
+
        /**
         * Finds the largest exponent for a given base.
         *
index bf37af53ee55423b55783367552f4f30f51ad6d4..fbbd34225ecc61ac62fce9b04fb642d083074ab7 100644 (file)
@@ -9,7 +9,7 @@ package de.republib.expr;
  * @author hm
  *
  */
-public class VariantException extends Exception {
+public class VariantException extends ExpressionException {
        private static final long serialVersionUID = 1L;
 
        /**
diff --git a/src/main/java/de/republib/gui/NumberField.java b/src/main/java/de/republib/gui/NumberField.java
new file mode 100644 (file)
index 0000000..65b371a
--- /dev/null
@@ -0,0 +1,143 @@
+/**
+ *
+ */
+package de.republib.gui;
+
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.util.Vector;
+
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JComboBox;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import de.republib.expr.Expression;
+import de.republib.expr.ParserException;
+import de.republib.util.Announcer;
+
+class HistoryModel extends DefaultComboBoxModel<String> {
+       /**
+        *
+        */
+       private static final long serialVersionUID = 1L;
+       private final int maxEntries;
+
+       /**
+        * Constructor.
+        */
+       public HistoryModel(int maxEntries) {
+               super(new Vector<String>());
+               this.maxEntries = maxEntries;
+       }
+
+       /**
+        * Adds an entry to the history.
+        *
+        * Moves the item to the first place if it exists in the list. Limits the
+        * list size.
+        *
+        * @param item
+        *            new item
+        */
+       public void add(String item) {
+               super.removeElement(item);
+               int size;
+               while ((size = super.getSize()) >= this.maxEntries) {
+                       super.removeElementAt(size - 1);
+               }
+               super.insertElementAt(item, 0);
+       }
+}
+
+/**
+ * Implements a field for numbers with formula evaluation and history in
+ * listbox.
+ *
+ * @author hm
+ *
+ */
+public class NumberField extends JComboBox<String> implements FocusListener {
+       /**
+        *
+        */
+       private static final long serialVersionUID = 1L;
+       private static final Logger logger = LoggerFactory.getLogger(NumberField.class);
+       Vector<String> history;
+       protected Expression expression;
+       protected Announcer announcer;
+       long longValue;
+       long defaultValue;
+
+       /**
+        * Constructor.
+        *
+        * @param text
+        *            the first combobox text
+        * @param defaultValue
+        *            the long value if an error occurres
+        * @param maxEntries
+        *            maximal count of list entries
+        * @param expression
+        *            <i>null</i> or the formula interpreter
+        * @param announcer
+        *            displays the error messages
+        */
+       public NumberField(String text, long defaultValue, int maxEntries, Expression expression, Announcer announcer) {
+               super(new HistoryModel(maxEntries));
+               this.expression = expression == null ? new Expression("") : expression;
+               this.announcer = announcer;
+               this.defaultValue = defaultValue;
+               this.setEditable(true);
+               ((HistoryModel) this.getModel()).add(text);
+               // Transform the formula to a long value:
+               focusLost(null);
+               getEditor().getEditorComponent().addFocusListener(this);
+               this.setSelectedItem(text);
+       }
+
+       /*
+        * (non-Javadoc)
+        *
+        * @see java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent)
+        */
+       @Override
+       public void focusGained(FocusEvent e) {
+               NumberField.logger.debug("NumberField: focus gained");
+       }
+
+       /*
+        * (non-Javadoc)
+        *
+        * @see java.awt.event.FocusListener#focusLost(java.awt.event.FocusEvent)
+        */
+       @Override
+       public void focusLost(FocusEvent event) {
+               final String formula = (String) this.getSelectedItem();
+               if (formula != null) {
+                       try {
+                               this.longValue = this.expression.expr(formula).asLong(this.defaultValue);
+                               ((HistoryModel) getModel()).add(formula);
+                       } catch (final ParserException e) {
+                               this.announcer.say(Announcer.ERROR, e.getLocalizedMessage());
+                       }
+               }
+       }
+
+       /**
+        * @return the longValue
+        */
+       public int getIntValue() {
+               return this.longValue > Integer.MAX_VALUE ? Integer.MAX_VALUE
+                               : this.longValue < Integer.MIN_VALUE ? Integer.MIN_VALUE : (int) this.longValue;
+       }
+
+       /**
+        * @return the longValue
+        */
+       public long getLongValue() {
+               return this.longValue;
+       }
+
+}
index 2a8aea629e9256a2d5faaca4c51482d8f3303067..d47f647afb280c819c969879545d6e325f83e6e9 100644 (file)
@@ -3,6 +3,7 @@ package de.republib.net;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
+import java.net.ConnectException;
 import java.net.Socket;
 import java.net.UnknownHostException;
 
@@ -45,10 +46,21 @@ public class TcpClient {
                } catch (final UnknownHostException e) {
                        this.logger.error(String.format("unknown server: %s:%d", host, port));
                } catch (final IOException e) {
-                       this.logger.error(String.format("cannot connect to %s:%d", host, port), e);
+                       if ((e instanceof ConnectException)) {
+                               this.logger.error(String.format("cannot connect to %s:%d", host, port));
+                       } else {
+                               this.logger.error(String.format("cannot connect to %s:%d", host, port), e);
+                       }
                }
        }
 
+       /**
+        * @return the active
+        */
+       public boolean getActive() {
+               return this.active;
+       }
+
        /**
         * @return the host
         */
@@ -83,6 +95,7 @@ public class TcpClient {
                        }
                } catch (final IOException e) {
                        this.logger.error("TcpClient.receive():", e);
+                       this.active = false;
                }
                return buffer;
        }
@@ -112,8 +125,10 @@ public class TcpClient {
                        }
                        try {
                                this.outStream.write(message.getBuffer(), 0, message.getLength());
+                               this.active = false;
                        } catch (final IOException e) {
                                this.logger.error(String.format("send(%s, %d)", message.toUtf8(4), message.getLength()), e);
+                               this.active = false;
                        }
                }
        }
index 9bfa0909edb030ff7d6898c00358289649cb84ab..157fb337dee41769b62bb964dd5e72471e5ae038 100644 (file)
@@ -17,6 +17,7 @@ import javax.swing.JPanel;
 import javax.swing.JTabbedPane;
 import javax.swing.JTextField;
 
+import de.republib.expr.Expression;
 import de.republib.gui.GuiUtils;
 import de.republib.gui.MultiChannelSheet;
 import de.republib.pinet.GpioClient;
@@ -44,6 +45,7 @@ public class ControlCenter implements Announcer, FocusListener {
        private JTextField textFieldColumns;
        private JTabbedPane tabbedPane;
        private int channelCount = 8;
+       private final Expression expression = new Expression("");
 
        /**
         * Constructor.
@@ -116,6 +118,13 @@ public class ControlCenter implements Announcer, FocusListener {
                return this.client;
        }
 
+       /**
+        * @return the expression
+        */
+       public Expression getExpression() {
+               return this.expression;
+       }
+
        /**
         * Populates the main panel with widgets.
         *
@@ -178,6 +187,24 @@ public class ControlCenter implements Announcer, FocusListener {
 
        }
 
+       /**
+        * Tries to establish a TCP connection.
+        *
+        * @return <i>true</i>: the connection is established
+        */
+       public boolean reconnect() {
+               final boolean rc = this.client.getActive();
+               final String host = this.textFieldServer.getText();
+               final int port = GuiUtils.numberOf(this.textFieldPort, 15000, true);
+               if (!rc) {
+                       this.client = new GpioClient(host, port);
+               }
+               if (!rc) {
+                       say(Announcer.ERROR, String.format(I18N.tr("%s:%d is not reachable"), host, port));
+               }
+               return rc;
+       }
+
        public void run() {
                // Display the window.
                this.frame.pack();
index ef2ca04ba599ff89c5b942dc2b7a7751e5571d5a..5f3598cb05ed449685fb922547f02c89bf6f1c43 100644 (file)
@@ -25,7 +25,7 @@ import javax.swing.JTextField;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import de.republib.gui.GuiUtils;
+import de.republib.gui.NumberField;
 import de.republib.pinet.Function;
 import de.republib.pinet.GpioClient;
 import de.republib.pinet.PinNumber;
@@ -105,7 +105,7 @@ public class GPIOSettings extends JPanel {
                add(this.panelOutputBlinkData = new OutputBlinkPanel(this.center, panelPinData));
                this.panelOutputBlinkData.setVisible(false);
                add(this.panelOutputPwmData = new OutputPwmPanel(this.center, panelPinData));
-               this.panelOutputPwmData.setVisible(true);
+               this.panelOutputPwmData.setVisible(false);
                add(this.panelInputData = new InputPanel(this.center, panelPinData));
                this.panelInputData.setVisible(false);
                add(this.panelInputPwmData = new InputPwmPanel(this.center, panelPinData));
@@ -267,7 +267,7 @@ class InputPanel extends JPanel implements ActionListener {
        private static final long serialVersionUID = 1L;
        private final Logger logger = LoggerFactory.getLogger(this.getClass());
        private JComboBox<String> comboChannel;
-       private JTextField textInterval;
+       private NumberField comboInterval;
        private final ControlCenter center;
        private final PinPanel pinData;
        JButton buttonRun;
@@ -290,7 +290,7 @@ class InputPanel extends JPanel implements ActionListener {
                add(this.comboChannel = new JComboBox<String>(), "wrap");
                this.center.addChannelComboBox(this.comboChannel);
                add(new JLabel(I18N.tr("Interval:")));
-               add(this.textInterval = new JTextField("0"), "wrap");
+               add(this.comboInterval = new NumberField("1000", 1000, 16, center.getExpression(), center), "wrap");
                add(this.buttonRun = new JButton(I18N.tr("Start")), "span 2, wrap");
                this.buttonRun.addActionListener(this);
        }
@@ -301,7 +301,7 @@ class InputPanel extends JPanel implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
                if (e.getSource() == this.buttonRun) {
-                       final int interval = GuiUtils.numberOf(this.textInterval, 1000, true);
+                       final int interval = this.comboInterval.getIntValue();
                        final int channel = this.comboChannel.getSelectedIndex();
                        this.logger.info(String.format("channel: %d interval: %d", channel, interval));
                }
@@ -319,8 +319,8 @@ class InputPwmPanel extends JPanel implements ActionListener {
        private static final long serialVersionUID = 1L;
        private final Logger logger = LoggerFactory.getLogger(this.getClass());
        private JComboBox<String> comboChannel;
-       private JTextField textFieldClock;
-       private JTextField textFieldSteps;
+       private NumberField comboClock;
+       private NumberField comboSteps;
        private final ControlCenter center;
        private final PinPanel pinData;
        private JButton buttonRun;
@@ -343,9 +343,9 @@ class InputPwmPanel extends JPanel implements ActionListener {
                add(this.comboChannel = new JComboBox<String>(), "wrap");
                this.center.addChannelComboBox(this.comboChannel);
                add(new JLabel(I18N.tr("Clock:")));
-               add(this.textFieldClock = new JTextField("1000"), "wrap");
+               add(this.comboClock = new NumberField("1000*1000", 1000 * 1000, 16, center.getExpression(), center), "wrap");
                add(new JLabel(I18N.tr("Period:")));
-               add(this.textFieldSteps = new JTextField("1024"), "wrap");
+               add(this.comboSteps = new NumberField("1024", 1024, 16, center.getExpression(), center), "wrap");
                add(this.buttonRun = new JButton(I18N.tr("Start")), "span 2, wrap");
                this.buttonRun.addActionListener(this);
 
@@ -355,8 +355,8 @@ class InputPwmPanel extends JPanel implements ActionListener {
        public void actionPerformed(ActionEvent e) {
                if (e.getSource() == this.buttonRun) {
                        final int channel = this.comboChannel.getSelectedIndex();
-                       final int clock = GuiUtils.numberOf(this.textFieldClock, 1000, true);
-                       final int steps = GuiUtils.numberOf(this.textFieldSteps, 1024, true);
+                       final int clock = this.comboClock.getIntValue();
+                       final int steps = this.comboSteps.getIntValue();
                        this.logger.info(String.format("channel: %d clock: %d steps: %d", channel, clock, steps));
                }
        }
@@ -376,9 +376,9 @@ class OutputBlinkPanel extends JPanel implements ActionListener {
        private static final long serialVersionUID = 1L;
        // private final Logger logger = LoggerFactory.getLogger(OutputData.class);
        private JComboBox<String> comboMode;
-       private JTextField textCount;
-       private JTextField textHigh;
-       private JTextField textLow;
+       private NumberField comboCount;
+       private NumberField comboHigh;
+       private NumberField ComboLow;
        private JButton buttonRun;
        private final ControlCenter center;
        private final PinPanel pinData;
@@ -392,11 +392,11 @@ class OutputBlinkPanel extends JPanel implements ActionListener {
                this.pinData = pinData;
                add(new JLabel(I18N.tr("Blink parameters:")), "span 2, wrap");
                add(new JLabel(I18N.tr("Count:")));
-               add(this.textCount = new JTextField("10"), "wrap");
+               add(this.comboCount = new NumberField("10", 10, 16, center.getExpression(), center), "wrap");
                add(new JLabel(I18N.tr("High:")));
-               add(this.textHigh = new JTextField("500"), "wrap");
+               add(this.comboHigh = new NumberField("500", 500, 16, center.getExpression(), center), "wrap");
                add(new JLabel(I18N.tr("Low:")));
-               add(this.textLow = new JTextField("500"), "wrap");
+               add(this.ComboLow = new NumberField("500", 500, 16, center.getExpression(), center), "wrap");
                add(this.buttonRun = new JButton(I18N.tr("Start")), "span 2");
                this.buttonRun.addActionListener(this);
        }
@@ -412,15 +412,17 @@ class OutputBlinkPanel extends JPanel implements ActionListener {
                if (e.getSource() == this.buttonRun) {
                        final GpioClient client = this.center.getClient();
                        final PinButton current = this.pinData.getCurrentPin();
-                       final int count = GuiUtils.numberOf(this.textCount, 10, true);
-                       final int high = GuiUtils.numberOf(this.textHigh, 500, true);
-                       final int low = GuiUtils.numberOf(this.textLow, 500, true);
-                       this.center.say(Announcer.LOG,
-                                       String.format(I18N.tr("blinking started on pin %d"), current.getPinNumber().getNumber()));
-                       final DynBytes answer = client.blink(current.getPinNumber(), count, high, low);
-                       if (answer.startsWith("OK")) {
+                       final int count = this.comboCount.getIntValue();
+                       final int high = this.comboHigh.getIntValue();
+                       final int low = this.ComboLow.getIntValue();
+                       if (this.center.reconnect()) {
                                this.center.say(Announcer.LOG,
-                                               String.format(I18N.tr("pin %d blinks %d times"), current.getPinNumber().getNumber(), count));
+                                               String.format(I18N.tr("blinking started on pin %d"), current.getPinNumber().getNumber()));
+                               final DynBytes answer = client.blink(current.getPinNumber(), count, high, low);
+                               if (answer.startsWith("OK")) {
+                                       this.center.say(Announcer.LOG, String.format(I18N.tr("pin %d blinks %d times"),
+                                                       current.getPinNumber().getNumber(), count));
+                               }
                        }
                } else if (e.getSource() == this.comboMode) {
 
@@ -443,10 +445,10 @@ class OutputPwmPanel extends JPanel implements ActionListener {
        static String[] modes = { I18N.tr("constant"), I18N.tr("sin"), I18N.tr("falling slope"), I18N.tr("rising slope"),
                        I18N.tr("dual slope") };
        private JComboBox<String> comboMode;
-       private JTextField textFieldPeriod;
-       private JTextField textFieldStartValue;
-       private JTextField textFieldFunctionSteps;
-       private JTextField textFieldCount;
+       private NumberField comboPeriod;
+       private NumberField comboStartValue;
+       private NumberField comboFunctionSteps;
+       private NumberField comboCount;
        private JButton buttonRun;
        private final ControlCenter center;
        private final PinPanel pinData;
@@ -465,20 +467,20 @@ class OutputPwmPanel extends JPanel implements ActionListener {
                this.pinData = pinData;
                add(new JLabel(I18N.tr("Output Parameters:")), "span 2, wrap");
                add(new JLabel(I18N.tr("Clock:")));
-               add(this.textFieldPeriod = new JTextField("10000"), "wrap");
-               this.textFieldPeriod.setToolTipText(I18N.tr("Duration of the pwm signal in microseconds"));
+               add(this.comboPeriod = new NumberField("1000*1000", 1000 * 1000, 16, center.getExpression(), center), "wrap");
+               this.comboPeriod.setToolTipText(I18N.tr("Duration of the pwm signal in microseconds"));
                add(new JLabel(I18N.tr("Start value:")));
-               add(this.textFieldStartValue = new JTextField("0"), "wrap");
-               this.textFieldStartValue.setToolTipText(I18N.tr("First pwm value, multiplied with 1E4, e.g. 223 means 0.0223"));
+               add(this.comboStartValue = new NumberField("0", 0, 16, center.getExpression(), center), "wrap");
+               this.comboStartValue.setToolTipText(I18N.tr("First pwm value, multiplied with 1E4, e.g. 223 means 0.0223"));
                add(new JLabel(I18N.tr("Function:")));
                add(this.comboMode = new JComboBox<String>(OutputPwmPanel.modes), "wrap");
                this.comboMode.setToolTipText(I18N.tr("A periodic function defining the pwm values."));
                add(new JLabel(I18N.tr("Function steps:")));
-               add(this.textFieldFunctionSteps = new JTextField("20"), "wrap");
-               this.textFieldFunctionSteps.setToolTipText(I18N
+               add(this.comboFunctionSteps = new NumberField("20", 20, 16, center.getExpression(), center), "wrap");
+               this.comboFunctionSteps.setToolTipText(I18N
                                .tr("Count of pwm periods building a 'function period': After this time the pwm values are repeated."));
                add(new JLabel(I18N.tr("Count:")));
-               add(this.textFieldCount = new JTextField("2147483647"), "wrap");
+               add(this.comboCount = new NumberField("2**31", Integer.MAX_VALUE, 16, center.getExpression(), center), "wrap");
 
                add(this.buttonRun = new JButton(I18N.tr("Start")), "span 2");
                this.buttonRun.addActionListener(this);
@@ -495,20 +497,21 @@ class OutputPwmPanel extends JPanel implements ActionListener {
                if (e.getSource() == this.buttonRun) {
                        final GpioClient client = this.center.getClient();
                        final PinButton current = this.pinData.getCurrentPin();
-                       final int period = GuiUtils.numberOf(this.textFieldPeriod, 10000, true);
-                       final double startValue = GuiUtils.numberOf(this.textFieldStartValue, 0, true) / 1E4;
+                       final int period = this.comboPeriod.getIntValue();
+                       final double startValue = this.comboStartValue.getIntValue() / 1E4;
                        final int mode = this.comboMode.getSelectedIndex();
                        final Function function = Function.findById(mode);
-                       final int functionSteps = GuiUtils.numberOf(this.textFieldFunctionSteps, 0, true);
-                       final int count = GuiUtils.numberOf(this.textFieldCount, 0, true);
-
-                       this.center.say(Announcer.LOG,
-                                       String.format(I18N.tr("PWM output: period: %d start: %f Mode: %s"), period, startValue, mode));
-                       final DynBytes answer = client.pwmOutput(current.getPinNumber(), period, startValue, function,
-                                       functionSteps, count);
-                       if (answer.startsWith("OK")) {
+                       final int functionSteps = this.comboFunctionSteps.getIntValue();
+                       final int count = this.comboCount.getIntValue();
+                       if (this.center.reconnect()) {
                                this.center.say(Announcer.LOG,
-                                               String.format(I18N.tr("pin %d puts pwm %d times"), current.getPinNumber().getNumber(), count));
+                                               String.format(I18N.tr("PWM output: period: %d start: %f Mode: %s"), period, startValue, mode));
+                               final DynBytes answer = client.pwmOutput(current.getPinNumber(), period, startValue, function,
+                                               functionSteps, count);
+                               if (answer.startsWith("OK")) {
+                                       this.center.say(Announcer.LOG, String.format(I18N.tr("pin %d puts pwm %d times"),
+                                                       current.getPinNumber().getNumber(), count));
+                               }
                        }
                }
        }
@@ -536,7 +539,6 @@ class PinButton extends JButton {
        public static final int MODE_COUNT = PinButton.MODE_FOCUS + 1;
        static ImageIcon[] icons = null;
        private final Logger logger = LoggerFactory.getLogger(PinButton.class);
-       private JLabel labelPin;
        private final PinNumber pinNumber;
        private final String title;
        private String nickname;
@@ -706,14 +708,15 @@ class PinPanel extends JPanel implements ActionListener, FocusListener {
                                this.logger.debug(
                                                "actionPerformed(); changing mode: " + index + " isOutPwm: " + String.valueOf(isOutputPWM));
                                this.currentPin.setMode(index);
-                               this.parent.getGpioSettings().getPanelOutputBlinkData()
-                                               .setVisible(index == PinButton.MODE_OUTPUT_BLINK);
+                               boolean visible = index == PinButton.MODE_OUTPUT_BLINK;
+                               this.parent.getGpioSettings().getPanelOutputBlinkData().setVisible(visible);
                                this.parent.getGpioSettings().getPanelOutputPwmData().setVisible(isOutputPWM);
-                               this.parent.getGpioSettings().getPanelInputData().setVisible(index == PinButton.MODE_INPUT);
-                               this.parent.getGpioSettings().getPanelInputPwmData().setVisible(index == PinButton.MODE_INPUT_PWM);
+                               visible = index == PinButton.MODE_INPUT;
+                               this.parent.getGpioSettings().getPanelInputData().setVisible(visible);
+                               visible = index == PinButton.MODE_INPUT_PWM;
+                               this.parent.getGpioSettings().getPanelInputPwmData().setVisible(visible);
                        }
                }
-
        }
 
        /**
index 00b7a9603304d56783eee831450ea743f27f46ba..3ea94a14a2ae17d6f5171f0af3fe44add15d15e0 100644 (file)
@@ -13,6 +13,21 @@ public class ExpressionTest {
                Assert.assertEquals(result.getLongValue(), 1L);
        }
 
+       @Test
+       void shouldConvertToLong() throws ExpressionException {
+               Expression.getInstance();
+               Assert.assertEquals(Expression.asLong("1+2*3**4"), 163L);
+               // error should throw an exception:
+               try {
+                       Expression.asLong("'x'");
+                       Assert.fail("missing exception");
+               } catch (final ExpressionException e) {
+                       Assert.assertTrue(e.getMessage().indexOf("INTEGER") > 0);
+               }
+               // suppressed exception:
+               Assert.assertEquals(Expression.asLong("'x'", -123L), -123L);
+       }
+
        @Test
        public void shouldFindVariable() {
                final Expression expr = new Expression("");
@@ -53,7 +68,7 @@ public class ExpressionTest {
        }
 
        @Test
-       public void shouldWorkExpr() throws ParserException {
+       public void shouldWorkLongExpr() throws ParserException {
                final Expression expr = new Expression("1 + 2*3");
                Variant result = expr.expr();
                Assert.assertEquals(result.getType(), DataType.LONG);
@@ -124,4 +139,21 @@ public class ExpressionTest {
                Assert.assertEquals(result.getType(), DataType.LONG);
                Assert.assertEquals(result.getLongValue(), 3L);
        }
+
+       @Test
+       public void shouldWorkVariables() throws ExpressionException {
+               // Variables remains in the singleton instance.
+               // assignment.
+               Assert.assertEquals(Expression.asLong("(a=5)"), 5L);
+               Assert.assertEquals(Expression.asLong("a=47"), 47L);
+               Assert.assertEquals(Expression.asLong("a=b=0x47"), 0x47L);
+               Assert.assertEquals(Expression.asLong("(One=1)+(e2=e3=2*3**4)"), 163L);
+               Assert.assertEquals(Expression.asLong("One"), 1L);
+               Assert.assertEquals(Expression.asLong("e2"), 162L);
+               Assert.assertEquals(Expression.asLong("e3"), 162L);
+               Assert.assertEquals(Expression.asLong("One+e2*9-e3"), 1L + 9 * 162 - 162);
+               Assert.assertEquals(Expression.asLong("One"), 1L);
+               Assert.assertEquals(Expression.asLong("e2"), 162L);
+               Assert.assertEquals(Expression.asLong("e3"), 162L);
+       }
 }
index 8acdb4d173e7d49c049d79b90974cb0189b450e3..886bf91a34470267cbb19568c41d912ce39afccf 100644 (file)
@@ -224,6 +224,22 @@ public class VariantTest extends Variant {
                Assert.assertEquals(varString.asString(), "Hi!");
        }
 
+       @Test
+       public void shouldWorkLValue() {
+               final Variable var2 = new Variable("v2");
+               var2.getValue().redefineLong(44L);
+               final Variant val2 = new Variant(var2);
+
+               final Variable var1 = new Variable("v1");
+               var1.getValue().redefine(val2);
+               final Variant val1 = new Variant(var1);
+
+               Assert.assertTrue(val2.isType(DataType.VARIABLE));
+               Assert.assertTrue(val1.isType(DataType.VARIABLE));
+               Assert.assertTrue(val1.getVariableValue().getValue().isType(DataType.VARIABLE));
+               Assert.assertTrue(val1.isBasicType(DataType.LONG));
+       }
+
        @Test
        public void shouldWorkUnaryOpNot() throws VariantException {
                final Variant var1 = new Variant(true);