From: hama Date: Wed, 27 Jul 2016 18:51:19 +0000 (+0200) Subject: variable assignment works, comboboxes X-Git-Tag: v0.1~4 X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=a728fc6d3f809f59522050dda81e631a7a00ecd8;p=jpinet variable assignment works, comboboxes * Expression: assignment tested * textfields replaced by NumberField --- diff --git a/src/main/java/de/republib/expr/Expression.java b/src/main/java/de/republib/expr/Expression.java index bd0219a..8b256d3 100644 --- a/src/main/java/de/republib/expr/Expression.java +++ b/src/main/java/de/republib/expr/Expression.java @@ -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. + * + * Note: + * + * + * @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. + * + * Note: + * + * + * @param text + * formula text + * @param defaultValue + * result if the formula contain errors + * @return defaultValue: error occurred
+ * 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 variables = new HashMap<>(); protected Stack 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 index 0000000..7a9e9cb --- /dev/null +++ b/src/main/java/de/republib/expr/ExpressionException.java @@ -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); + } +} diff --git a/src/main/java/de/republib/expr/OpCode.java b/src/main/java/de/republib/expr/OpCode.java index 1985943..65db894 100644 --- a/src/main/java/de/republib/expr/OpCode.java +++ b/src/main/java/de/republib/expr/OpCode.java @@ -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), // diff --git a/src/main/java/de/republib/expr/ParserException.java b/src/main/java/de/republib/expr/ParserException.java index 4d3df7c..bde06d5 100644 --- a/src/main/java/de/republib/expr/ParserException.java +++ b/src/main/java/de/republib/expr/ParserException.java @@ -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; /** diff --git a/src/main/java/de/republib/expr/Scanner.java b/src/main/java/de/republib/expr/Scanner.java index 5fb54ec..756d0ea 100644 --- a/src/main/java/de/republib/expr/Scanner.java +++ b/src/main/java/de/republib/expr/Scanner.java @@ -184,7 +184,7 @@ public class Scanner { op = OpCode.RIGHT_PARENT; break; case '=': - op = OpCode.ASSIGN; + op = OpCode.ASSIGNMENT; break; default: rc = false; diff --git a/src/main/java/de/republib/expr/Variant.java b/src/main/java/de/republib/expr/Variant.java index 9d9c36c..5a02690 100644 --- a/src/main/java/de/republib/expr/Variant.java +++ b/src/main/java/de/republib/expr/Variant.java @@ -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 true: 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 true: 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. * diff --git a/src/main/java/de/republib/expr/VariantException.java b/src/main/java/de/republib/expr/VariantException.java index bf37af5..fbbd342 100644 --- a/src/main/java/de/republib/expr/VariantException.java +++ b/src/main/java/de/republib/expr/VariantException.java @@ -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 index 0000000..65b371a --- /dev/null +++ b/src/main/java/de/republib/gui/NumberField.java @@ -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 { + /** + * + */ + private static final long serialVersionUID = 1L; + private final int maxEntries; + + /** + * Constructor. + */ + public HistoryModel(int maxEntries) { + super(new Vector()); + 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 implements FocusListener { + /** + * + */ + private static final long serialVersionUID = 1L; + private static final Logger logger = LoggerFactory.getLogger(NumberField.class); + Vector 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 + * null 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; + } + +} diff --git a/src/main/java/de/republib/net/TcpClient.java b/src/main/java/de/republib/net/TcpClient.java index 2a8aea6..d47f647 100644 --- a/src/main/java/de/republib/net/TcpClient.java +++ b/src/main/java/de/republib/net/TcpClient.java @@ -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; } } } diff --git a/src/main/java/de/republib/pinet/gui/ControlCenter.java b/src/main/java/de/republib/pinet/gui/ControlCenter.java index 9bfa090..157fb33 100644 --- a/src/main/java/de/republib/pinet/gui/ControlCenter.java +++ b/src/main/java/de/republib/pinet/gui/ControlCenter.java @@ -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 true: 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(); diff --git a/src/main/java/de/republib/pinet/gui/GPIOSettings.java b/src/main/java/de/republib/pinet/gui/GPIOSettings.java index ef2ca04..5f3598c 100644 --- a/src/main/java/de/republib/pinet/gui/GPIOSettings.java +++ b/src/main/java/de/republib/pinet/gui/GPIOSettings.java @@ -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 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(), "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 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(), "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 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 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(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); } } - } /** diff --git a/src/test/java/de/republib/expr/ExpressionTest.java b/src/test/java/de/republib/expr/ExpressionTest.java index 00b7a96..3ea94a1 100644 --- a/src/test/java/de/republib/expr/ExpressionTest.java +++ b/src/test/java/de/republib/expr/ExpressionTest.java @@ -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); + } } diff --git a/src/test/java/de/republib/expr/VariantTest.java b/src/test/java/de/republib/expr/VariantTest.java index 8acdb4d..886bf91 100644 --- a/src/test/java/de/republib/expr/VariantTest.java +++ b/src/test/java/de/republib/expr/VariantTest.java @@ -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);