<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
+ <modelVersion>4.0.0</modelVersion>
- <groupId>de.republlb</groupId>
- <artifactId>pinet</artifactId>
- <packaging>jar</packaging>
- <version>1.0-SNAPSHOT</version>
+ <groupId>de.republlb</groupId>
+ <artifactId>pinet</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-SNAPSHOT</version>
- <name>Controlling a Raspberry Pi</name>
- <url>https://sourcefourge.net</url>
+ <name>Controlling a Raspberry Pi</name>
+ <url>https://sourcefourge.net</url>
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
- <logback.version>1.1.7</logback.version>
- <slf4j.version>1.7.21</slf4j.version>
- </properties>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+ <logback.version>1.1.7</logback.version>
+ <slf4j.version>1.7.21</slf4j.version>
+ </properties>
- <dependencies>
- <!-- logging -->
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>${slf4j.version}</version>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- <version>${logback.version}</version>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-core</artifactId>
- <version>${logback.version}</version>
- </dependency>
- <dependency>
- <groupId>org.testng</groupId>
- <artifactId>testng</artifactId>
- <version>6.9.8</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
+ <dependencies>
+ <!-- logging -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <version>${logback.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <version>${logback.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ <version>6.9.8</version>
+ <scope>test</scope>
+ </dependency>
+ <!-- https://mvnrepository.com/artifact/com.miglayout/miglayout-swing -->
+ <dependency>
+ <groupId>com.miglayout</groupId>
+ <artifactId>miglayout-swing</artifactId>
+ <version>4.2</version>
+ </dependency>
- <build>
- <defaultGoal>install</defaultGoal>
+ </dependencies>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>3.5.1</version>
- <configuration>
- <source>1.7</source>
- <target>1.7</target>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-resources-plugin</artifactId>
- <version>2.6</version>
- <configuration>
- <encoding>UTF-8</encoding>
- </configuration>
- </plugin>
+ <build>
+ <defaultGoal>install</defaultGoal>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <version>2.12.4</version>
- <configuration>
- <parallel>methods</parallel>
- <threadCount>4</threadCount>
- </configuration>
- </plugin>
- <!-- Allows the example to be run via 'mvn compile exec:java' -->
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>exec-maven-plugin</artifactId>
- <version>1.4.0</version>
- <configuration>
- <mainClass>de.republlb.MainApp</mainClass>
- <includePluginDependencies>false</includePluginDependencies>
- </configuration>
- </plugin>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.5.1</version>
+ <configuration>
+ <source>1.7</source>
+ <target>1.7</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>2.6</version>
+ <configuration>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
- </plugins>
- </build>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.12.4</version>
+ <configuration>
+ <parallel>methods</parallel>
+ <threadCount>4</threadCount>
+ </configuration>
+ </plugin>
+ <!-- Allows the example to be run via 'mvn compile exec:java' -->
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.4.0</version>
+ <configuration>
+ <mainClass>de.republlb.MainApp</mainClass>
+ <includePluginDependencies>false</includePluginDependencies>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
</project>
--- /dev/null
+package de.republib.expr;
+/**
+ *
+ */
+
+/**
+ * Enumerates the data types which characterizes the storage of a value.
+ *
+ * @author hm
+ *
+ */
+public enum DataType {
+ UNDEF, BOOLEAN, LONG, DOUBLE, STRING, OBJECT, VARIABLE
+}
--- /dev/null
+/**
+ *
+ */
+package de.republib.expr;
+
+import java.util.HashMap;
+import java.util.Stack;
+
+import de.republib.util.I18N;
+
+/**
+ * Implements a parser for arithmetic expressions (formulas).
+ *
+ * <pre>
+ * Syntax:
+ * <expr> ::= <term> {<operator> <operand}*
+ * <term> ::= '(' <expr> ')' | <constant> | <variable>
+ * <constant> ::= <number>
+ * <operator> ::= '+' | '-' | '*' | '/' | '%' | '**' | '='
+ * </pre>
+ *
+ * @author hm
+ *
+ */
+public class Expression {
+ protected Scanner scanner;
+ protected HashMap<String, Variable> variables = new HashMap<>();
+ protected Stack<Variant> values = new Stack<>();
+ protected Stack<OpCode> operators = new Stack<>();
+
+ /**
+ * Constructor.
+ *
+ * @param text
+ * the text with the formula
+ */
+ public Expression(String text) {
+ this.scanner = new Scanner(text);
+ }
+
+ /**
+ * Calculates the value of the expression.
+ *
+ * <pre>
+ * <expr> ::= <term> { <operator> <term> }*
+ * </pre>
+ *
+ * @return the value of the expression stored in a token
+ * @throws ParserException
+ * @throws VariantException
+ */
+ public Variant expr() throws ParserException {
+ final Variant rc = term();
+ if (rc == null) {
+ throw new ParserException(I18N.tr("unexpected end of string"), this.scanner.getLastToken(), false);
+ }
+ Token token;
+ final boolean again = true;
+ while (again && !(token = this.scanner.nextNotSpaceToken()).isType(TokenType.END_OF_STRING)) {
+ if (!token.isType(TokenType.OP)) {
+ throw new ParserException(
+ String.format(I18N.tr("operator expected (like '+', '-'...). Found: %s"), token.getType()),
+ token);
+ }
+ final OpCode op = token.getOpCode();
+ Variant value2;
+ final Token tokenOp = token.copy();
+ if (op == OpCode.ASSIGN) {
+ if (!rc.isType(DataType.VARIABLE)) {
+ throw new ParserException(I18N.tr("Assignment misses a rvalue (e.g. a variable) on the left side"),
+ token);
+ }
+ value2 = expr();
+ } else {
+ value2 = term();
+ }
+ try {
+ rc.binaryOperation(op, value2);
+ } catch (final VariantException e) {
+ // transform in a message with position info:
+ throw new ParserException(e.getMessage(), tokenOp);
+ }
+ }
+ return rc;
+ }
+
+ /**
+ * Returns the variable given by the name.
+ *
+ * If the variable with this name does not exist it will be created.
+ *
+ * @param id
+ * variable name
+ * @return the variable
+ */
+ public Variable findVariable(String id) {
+ Variable rc = this.variables.getOrDefault(id, null);
+ if (rc == null) {
+ this.variables.put(id, rc = new Variable(id));
+ }
+ return rc;
+ }
+
+ /**
+ * Sets a new text for parsing.
+ *
+ * @param text
+ * the formula text
+ * @return the instance (for chaining)
+ */
+ public Expression reset(String text) {
+ this.scanner.reset(text);
+ return this;
+ }
+
+ /**
+ * Calculates the value of the expression.
+ *
+ * <pre>
+ * <term> ::= '(' <expr> ')' | <constant> | <variable> | <sign> <term>
+ * <sign> ::= '+' | '-'
+ * </pre>
+ *
+ * @return the value of the term
+ * @throws ParserException
+ * @throws VariantException
+ */
+ public Variant term() throws ParserException {
+ Variant rc = null;
+ Token token;
+ boolean isNegative = false;
+ do {
+ token = this.scanner.nextNotSpaceToken();
+ if (token.isOp(OpCode.MINUS)) {
+ isNegative = !isNegative;
+ }
+ } while (token.isType(TokenType.OP) && token.getOpCode().isUnary());
+
+ switch (token.getType()) {
+ case ID:
+ rc = new Variant(findVariable(token.getString()));
+ break;
+ case OP:
+ switch (token.getOpCode()) {
+ case LEFT_PARENTH:
+ rc = expr();
+ if (!this.scanner.getLastToken().isOp(OpCode.RIGHT_PARENT)) {
+ throw new ParserException(I18N.tr("missing ')'"), token, false);
+ }
+ break;
+ case RIGHT_PARENT:
+ break;
+ default:
+ throw new ParserException(String.format(I18N.tr("operand (e.g. constant) expected. Found operator %s"),
+ token.getOpCode().name()), token);
+ }
+ break;
+ case STRING:
+ rc = new Variant(token.getString());
+ break;
+ case INTEGER:
+ rc = new Variant(token.getLongValue());
+ break;
+ case END_OF_STRING:
+ default:
+ break;
+ }
+ if (isNegative) {
+ if (rc.isType(DataType.LONG)) {
+ rc.setLongValue(-rc.getLongValue());
+ } else if (rc.isType(DataType.DOUBLE)) {
+ rc.setDoubleValue(-rc.getDoubleValue());
+ }
+ }
+ return rc;
+ }
+
+}
--- /dev/null
+package de.republib.expr;
+
+public enum OpCode {
+ UNDEF(0), ASSIGN(1, false, true), //
+ PLUS(10, true), MINUS(10, true), //
+ TIMES(11), DIV(11), MOD(11), //
+ POWER(12), //
+ LEFT_PARENTH(20), RIGHT_PARENT(20), //
+ NOT(0, true);
+
+ private int priority;
+ private boolean rightAssociative;
+ private boolean unary;
+
+ /**
+ * Constructor.
+ *
+ * @param priority
+ * priority of the operator. Higher priority binds stronger.
+ */
+ private OpCode(int priority) {
+ this.priority = priority;
+ this.rightAssociative = false;
+ this.unary = false;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param priority
+ * priority of the operator. Higher priority binds stronger.
+ * @param isUnary
+ * <i>true</i>: the operator is an unary operator. May be a
+ * binary operator too
+ */
+ private OpCode(int priority, boolean isUnary) {
+ this.priority = priority;
+ this.rightAssociative = false;
+ this.unary = isUnary;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param priority
+ * priority of the operator. Higher priority binds stronger.
+ * @param isUnary
+ * <i>true</i>: the operator is an unary operator. May be a
+ * binary operator too
+ * @param rightAssociative
+ * <i>true</i>: the right term must be evaluated before the
+ * operation can be done. Typical example is assignment '='
+ */
+ private OpCode(int priority, boolean isUnary, boolean rightAssociative) {
+ this.priority = priority;
+ this.rightAssociative = rightAssociative;
+ this.unary = isUnary;
+ }
+
+ /**
+ * @return the priority
+ */
+ public int getPriority() {
+ return this.priority;
+ }
+
+ /**
+ * @return the rightAssociative
+ */
+ public boolean isRightAssociative() {
+ return this.rightAssociative;
+ }
+
+ public boolean isUnary() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+}
--- /dev/null
+/**
+ *
+ */
+package de.republib.expr;
+
+/**
+ * Handles scanner errors like "unknown token".
+ *
+ * @author hm
+ *
+ */
+public class ParserException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs the message of the exception.
+ *
+ * @param message
+ * message describing the error
+ * @param token
+ * additional information about the context
+ * @param startPosition
+ * <i>true</i>: the start position is the interesting point<br>
+ * <i>false</i>: the positon behind the token is the interesting
+ * point
+ * @return the message with position information
+ */
+ private static String buildMessage(String message, Token token, boolean startPosition) {
+ String rc;
+ if (startPosition) {
+ rc = String.format("%d-%d ==> [%s]: %s", token.getLineNo(), token.getPosition(), token.toString(), message);
+ } else {
+ rc = String.format("%d-%d [%s] <==: %s", token.getLineNo(), token.getPosition(), token.toString(), message);
+ }
+ return rc;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param message
+ * message describing the error
+ * @param token
+ * additional information about the context
+ */
+ public ParserException(String message, Token token) {
+ super(ParserException.buildMessage(message, token, true));
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param message
+ * message describing the error
+ * @param token
+ * additional information about the context
+ * @param startPosition
+ * <i>true</i>: the start position is the interesting point<br>
+ * <i>false</i>: the positon behind the token is the interesting
+ * point
+ */
+ public ParserException(String message, Token token, boolean startPosition) {
+ super(ParserException.buildMessage(message, token, startPosition));
+ }
+}
--- /dev/null
+/**
+ *
+ */
+package de.republib.expr;
+
+import de.republib.util.I18N;
+
+/**
+ * Divides a text into tokens to parse formulas.
+ *
+ * @author hm
+ *
+ */
+public class Scanner {
+ int position = 0;
+ int lineNo = 1;
+ String text;
+ Token[] tokens = new Token[3];
+ int indexToken = 0;
+ Token lastToken = null;
+ StringBuilder string = new StringBuilder(8096);
+
+ /**
+ * Constructor.
+ *
+ * @param text
+ * the text to parse
+ */
+ public Scanner(final String text) {
+ this.text = text;
+ for (int ix = 0; ix < this.tokens.length; ix++) {
+ this.tokens[ix] = new Token();
+ }
+ }
+
+ /**
+ * @return the lastToken
+ */
+ public Token getLastToken() {
+ return this.lastToken;
+ }
+
+ /**
+ * Handles a escaped character of a string, e.g. '\n'.
+ *
+ * @param buffer
+ * the character will appended to this buffer
+ */
+ private void handleEscapedChar(StringBuilder buffer) {
+ char cc;
+ int value;
+ int maxLength;
+ switch (cc = this.text.charAt(this.position++)) {
+ case 'b':
+ buffer.append('\b');
+ break;
+ case 'f':
+ buffer.append('\f');
+ break;
+ case 't':
+ buffer.append('\t');
+ break;
+ case 'n':
+ buffer.append('\n');
+ break;
+ case 'r':
+ buffer.append('\r');
+ break;
+ case '0': {
+ value = 0;
+ maxLength = 3;
+ while (maxLength-- > 0 && this.position < this.text.length()
+ && (cc = this.text.charAt(this.position++)) >= '0' && cc < '9') {
+ value = value * 8 + cc - '0';
+ }
+ buffer.append((char) value);
+ break;
+ }
+ case 'X':
+ case 'x':
+ value = 0;
+ maxLength = 2;
+ while (maxLength-- > 0 && this.position < this.text.length()
+ && ((cc = this.text.charAt(this.position)) >= '0' && cc < '9' || cc >= 'a' && cc <= 'f'
+ || cc >= 'A' && cc <= 'F')) {
+ ++this.position;
+ final int digit = cc <= '9' ? cc - '0' : cc <= 'F' ? cc - 'A' + 10 : cc - 'a' + 10;
+ value = value * 16 + digit;
+ }
+ buffer.append((char) value);
+ break;
+ case 'U':
+ case 'u':
+ value = 0;
+ maxLength = 4;
+ while (maxLength-- > 0 && this.position < this.text.length()
+ && ((cc = this.text.charAt(this.position++)) >= '0' && cc < '9' || cc >= 'a' && cc <= 'f'
+ || cc >= 'A' && cc <= 'F')) {
+ final int digit = cc <= '9' ? cc - '0' : cc <= 'F' ? cc - 'A' + 10 : cc - 'a' + 10;
+ value = value * 16 + digit;
+ }
+ buffer.append((char) value);
+ break;
+ case '\\':
+ default:
+ buffer.append(cc);
+ break;
+ }
+ }
+
+ private boolean handleOp(char cc, Token token) {
+ OpCode op = OpCode.UNDEF;
+ final int start = this.position - 1;
+ boolean rc = true;
+ switch (cc) {
+ case '+':
+ op = OpCode.PLUS;
+ break;
+ case '-':
+ op = OpCode.MINUS;
+ break;
+ case '*':
+ if (this.position < this.text.length() && this.text.charAt(this.position) == '*') {
+ op = OpCode.POWER;
+ this.position++;
+ } else {
+ op = OpCode.TIMES;
+ }
+ break;
+ case '/':
+ op = OpCode.DIV;
+ break;
+ case '%':
+ op = OpCode.MOD;
+ break;
+ case '(':
+ op = OpCode.LEFT_PARENTH;
+ break;
+ case ')':
+ op = OpCode.RIGHT_PARENT;
+ break;
+ default:
+ rc = false;
+ break;
+ }
+ if (rc) {
+ token.setOp(this.text.substring(start, this.position), op);
+ }
+ return rc;
+ }
+
+ /**
+ * Returns the next token which is not a SPACE token.
+ *
+ * @return the next token
+ * @throws ParserException
+ */
+ public Token nextNotSpaceToken() throws ParserException {
+ Token token;
+ while ((token = nextToken()).isType(TokenType.SPACE)) {
+ // do nothing
+ }
+ return token;
+ }
+
+ /**
+ * Scans the next token.
+ *
+ * @return the next token
+ * @throws ParserException
+ */
+ public Token nextToken() throws ParserException {
+ final Token token = this.tokens[this.indexToken++];
+ final int tokenStart = this.position;
+ if (this.indexToken >= this.tokens.length) {
+ this.indexToken = 0;
+ }
+ token.setPosition(this.position);
+ token.setLineNo(this.lineNo);
+ if (this.position >= this.text.length()) {
+ token.setEndOfString();
+ } else {
+ char cc = this.text.charAt(this.position++);
+ if (Character.isWhitespace(cc)) {
+ if (cc == '\n') {
+ ++this.lineNo;
+ }
+ while (this.position < this.text.length()
+ && Character.isWhitespace(cc = this.text.charAt(this.position))) {
+ if (cc == '\n') {
+ ++this.lineNo;
+ }
+ ++this.position;
+ }
+ token.setSpace(this.text.substring(tokenStart, this.position));
+ } else if (Character.isDigit(cc)) {
+ long value = cc - '0';
+ long digit;
+ if (this.position < this.text.length() && (cc = this.text.charAt(this.position)) == 'x' || cc == 'X') {
+ ++this.position;
+ while (this.position < this.text.length()
+ && (Character.isDigit(cc = this.text.charAt(this.position)) || cc >= 'A' && cc <= 'F'
+ || cc >= 'a' && cc <= 'f')) {
+ this.position++;
+ digit = cc <= '9' ? cc - '0' : cc <= 'F' ? cc - 'A' + 10 : cc - 'a' + 10;
+ value = 16 * value + digit;
+ }
+ } else {
+ while (this.position < this.text.length()
+ && Character.isDigit(cc = this.text.charAt(this.position))) {
+ this.position++;
+ value = 10 * value + cc - '0';
+ }
+ }
+ token.setLong(value);
+ } else if (cc == '\'' || cc == '"') {
+ this.string.setLength(0);
+ final char delimiter = cc;
+ while (this.position < this.text.length() && (cc = this.text.charAt(this.position++)) != delimiter) {
+ if (cc != '\\') {
+ this.string.append(cc);
+ } else {
+ handleEscapedChar(this.string);
+ }
+ }
+ token.setString(this.string.toString(), this.text.substring(tokenStart, this.position), delimiter);
+ } else if (Character.isJavaIdentifierStart(cc)) {
+ while (this.position < this.text.length()
+ && Character.isJavaIdentifierPart(cc = this.text.charAt(this.position))) {
+ ++this.position;
+ }
+ token.setId(this.text.substring(tokenStart, this.position));
+ } else if (!handleOp(cc, token)) {
+ token.setUnknown("" + cc);
+ throw new ParserException(I18N.tr("unknown token"), token);
+ }
+ }
+ return token;
+ }
+
+ /**
+ * Resets the instance with a new text and position 0.
+ *
+ * @param text
+ * the input text to scan
+ * @return the instance (for chaining)
+ */
+ public Scanner reset(String text) {
+ this.text = text;
+ this.position = 0;
+ this.lineNo = 1;
+ return this;
+ }
+}
--- /dev/null
+/**
+ *
+ */
+package de.republib.expr;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Describes a syntactical element of a formula.
+ *
+ * @author hm
+ *
+ */
+public class Token {
+ static Logger logger = LoggerFactory.getLogger(Token.class);
+ protected TokenType type = TokenType.UNDEF;
+ protected long longValue = 0;
+ protected String string = null;
+ /// string in printable form, with delimiters and escaped characters,
+ /// e.g."x\\n"
+ protected String rawString = null;
+ protected char delimiter = '\0';
+ protected OpCode opCode = OpCode.UNDEF;
+ protected int position;
+ protected int lineNo;
+
+ /**
+ * Constructor.
+ */
+ public Token() {
+
+ }
+
+ /**
+ * Returns a flat copy of the instance.
+ *
+ * @return a copy of th instance
+ */
+ public Token copy() {
+ Token rc = null;
+ try {
+ rc = (Token) clone();
+ } catch (final CloneNotSupportedException e) {
+ Token.logger.error("Token.copy()", e);
+ }
+ return rc;
+ }
+
+ /**
+ * @return the delimiter
+ */
+ public char getDelimiter() {
+ return this.delimiter;
+ }
+
+ /**
+ * @return the lineNo
+ */
+ public int getLineNo() {
+ return this.lineNo;
+ }
+
+ /**
+ * @return the longValue
+ */
+ public long getLongValue() {
+ return this.longValue;
+ }
+
+ /**
+ * @return the opCode
+ */
+ public OpCode getOpCode() {
+ return this.opCode;
+ }
+
+ /**
+ * @return the position
+ */
+ public int getPosition() {
+ return this.position;
+ }
+
+ /**
+ * @return the rawString
+ */
+ public String getRawString() {
+ return this.rawString;
+ }
+
+ /**
+ * @return the string
+ */
+ public String getString() {
+ return this.string;
+ }
+
+ /**
+ * @return the type
+ */
+ public TokenType getType() {
+ return this.type;
+ }
+
+ /**
+ * Tests whether the token has a given type.
+ *
+ * @param type
+ * token type to compare
+ * @return <i>true</i>the type matches
+ */
+ public boolean isType(TokenType type) {
+ return this.type == type;
+ }
+
+ /**
+ * Tests whether the token is a given operator.
+ *
+ * @param opCode
+ * operator to compare
+ * @return <i>true</i>: the token is an operator and has the given opcode
+ */
+ public boolean isOp(OpCode opCode) {
+ final boolean rc = this.type == TokenType.OP && this.opCode == opCode;
+ return rc;
+ }
+
+ /**
+ * Sets the token as identifier.
+ *
+ * @param id
+ * name of the identifier
+ * @param delimiter
+ * @return the instance (for chaining)
+ */
+ public Token setEndOfString() {
+ this.type = TokenType.END_OF_STRING;
+ return this;
+ }
+
+ /**
+ * Sets the token as identifier.
+ *
+ * @param id
+ * name of the identifier
+ * @param delimiter
+ * @return the instance (for chaining)
+ */
+ public Token setId(String id) {
+ this.type = TokenType.ID;
+ this.string = id;
+ return this;
+ }
+
+ /**
+ * @param lineNo
+ * the lineNo to set
+ */
+ public void setLineNo(int lineNo) {
+ this.lineNo = lineNo;
+ }
+
+ /**
+ * Sets the token as long constant.
+ *
+ * @param value
+ * the long value
+ * @return the instance (for chaining)
+ */
+ public Token setLong(long value) {
+ this.type = TokenType.INTEGER;
+ this.longValue = value;
+ return this;
+ }
+
+ /**
+ * Sets the token as operator.
+ *
+ * @param string
+ * the string of the operator, e.g. "+"
+ * @param op
+ * the operator like "+"
+ * @return the instance (for chaining)
+ */
+ public Token setOp(String string, OpCode op) {
+ this.type = TokenType.OP;
+ this.string = string;
+ this.opCode = op;
+ return this;
+ }
+
+ /**
+ * @param position
+ * the position to set
+ */
+ public void setPosition(int position) {
+ this.position = position;
+ }
+
+ /**
+ * Sets the token as space sequence.
+ *
+ * @param spaces
+ * the space sequence
+ * @return the instance (for chaining)
+ */
+ public Token setSpace(String spaces) {
+ this.type = TokenType.SPACE;
+ this.string = spaces;
+ return this;
+ }
+
+ /**
+ * Sets the token as string.
+ *
+ * @param spaces
+ * the space sequence
+ * @return the instance (for chaining)
+ */
+ public Token setString(String string, String rawString, char delimiter) {
+ this.type = TokenType.STRING;
+ this.string = string;
+ this.rawString = rawString;
+ this.delimiter = delimiter;
+ return this;
+ }
+
+ /**
+ * Sets the token as space sequence.
+ *
+ * @param spaces
+ * the space sequence
+ * @return the instance (for chaining)
+ */
+ public Token setUnknown(String info) {
+ this.type = TokenType.UNKNOWN;
+ this.string = info;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ String rc = null;
+ switch (this.type) {
+ case END_OF_STRING:
+ rc = "EOS";
+ break;
+ case INTEGER:
+ rc = String.valueOf(this.getLongValue());
+ break;
+ case STRING:
+ rc = this.getRawString();
+ break;
+ case UNKNOWN:
+ case ID:
+ case OP:
+ rc = this.getString();
+ break;
+ case SPACE:
+ rc = "<spaces>";
+ break;
+ default:
+ rc = "?";
+ break;
+ }
+ return rc;
+ }
+}
--- /dev/null
+/**
+ *
+ */
+package de.republib.expr;
+
+/**
+ * @author hm
+ *
+ */
+public enum TokenType {
+ UNDEF, INTEGER, OP, ID, STRING, SPACE, END_OF_STRING, UNKNOWN
+}
--- /dev/null
+/**
+ *
+ */
+package de.republib.expr;
+
+/**
+ * Manages a variable.
+ *
+ * A variable is a name and a value.
+ *
+ * @author hm
+ *
+ */
+public class Variable {
+ protected String id;
+ protected Variant value;
+
+ /**
+ * Constructor.
+ *
+ * @param id
+ * name of the variable
+ */
+ public Variable(String id) {
+ this.id = id;
+ this.value = new Variant("");
+ }
+
+ /**
+ * Assigns a new value to the instance.
+ *
+ * @param value
+ */
+ public void assign(Variant value) {
+ this.value.redefine(value);
+ }
+
+ /**
+ * @return the id
+ */
+ public String getId() {
+ return this.id;
+ }
+
+ /**
+ * @return the value
+ */
+ public Variant getValue() {
+ return this.value;
+ }
+}
--- /dev/null
+/**
+ *
+ */
+package de.republib.expr;
+
+import de.republib.util.I18N;
+
+/**
+ * Implements a container for a value of different data types.
+ *
+ * @author hm
+ *
+ */
+public class Variant {
+ protected DataType type;
+ protected long longValue = Long.MIN_VALUE;
+ protected double doubleValue = Double.NaN;
+ protected Boolean boolValue = null;
+ protected String stringValue;
+ protected Object objectValue;
+ protected Variable variableValue;
+
+ /**
+ * Constructor.
+ *
+ * @param value
+ * the instance becomes the type BOOLEAN with this value
+ */
+ public Variant(boolean value) {
+ this.boolValue = value;
+ this.type = DataType.BOOLEAN;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param longValue
+ * the instance becomes the type INTEGER with this value
+ */
+ public Variant(double value) {
+ this.doubleValue = value;
+ this.type = DataType.DOUBLE;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param value
+ * the instance becomes the type INTEGER with this value
+ */
+ public Variant(long value) {
+ this.longValue = value;
+ this.type = DataType.LONG;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param value
+ * the instance becomes the type OBJECT with this value
+ */
+ public Variant(Object value) {
+ this.objectValue = value;
+ this.type = DataType.STRING;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param string
+ * the instance becomes the type STRING with this value
+ */
+ public Variant(String string) {
+ this.stringValue = string;
+ this.type = DataType.STRING;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param value
+ * the instance becomes the type VARIABLE with this value
+ */
+ public Variant(Variable value) {
+ this.variableValue = value;
+ this.type = DataType.VARIABLE;
+ }
+
+ /**
+ * Returns the value as long integer.
+ *
+ * @param defaultValue
+ * return code for unexpected states
+ * @return a long representation of the value or the default value
+ */
+ public long asLong(long defaultValue) {
+ long rc = defaultValue;
+ if (this.longValue != Long.MIN_VALUE) {
+ rc = this.longValue;
+ } else {
+ switch (this.type) {
+ case BOOLEAN:
+ rc = this.longValue = this.boolValue ? 1L : 0L;
+ break;
+ case DOUBLE:
+ rc = this.longValue = Math.round(this.doubleValue);
+ break;
+ case LONG:
+ rc = this.longValue;
+ break;
+ case STRING: {
+ try {
+ final Token token = new Scanner(this.stringValue).nextNotSpaceToken();
+ if (token.isType(TokenType.INTEGER)) {
+ rc = this.longValue = token.getLongValue();
+ }
+ } catch (final ParserException e) {
+ // do nothing
+ }
+ break;
+ }
+ case VARIABLE:
+ rc = this.longValue = this.variableValue.getValue().asLong(defaultValue);
+ break;
+ default:
+ break;
+ }
+ }
+ return rc;
+ }
+
+ /**
+ * Returns the value as string.
+ *
+ * @return a string representation of the value
+ */
+ public String asString() {
+ if (this.stringValue == null) {
+ switch (this.type) {
+ case BOOLEAN:
+ this.stringValue = this.boolValue ? "true" : "false";
+ break;
+ case DOUBLE:
+ this.stringValue = String.valueOf(this.doubleValue);
+ break;
+ case LONG:
+ this.stringValue = String.valueOf(this.longValue);
+ break;
+ case OBJECT:
+ this.stringValue = this.objectValue == null ? "" : this.objectValue.toString();
+ break;
+ case VARIABLE:
+ this.stringValue = this.variableValue.getValue().asString();
+ break;
+ case STRING:
+ default:
+ this.stringValue = "";
+ break;
+ }
+ }
+ return this.stringValue;
+ }
+
+ /**
+ * @return the boolValue
+ */
+ public Boolean getBoolValue() {
+ return this.boolValue;
+ }
+
+ /**
+ * @return the doubleValue
+ */
+ public double getDoubleValue() {
+ return this.doubleValue;
+ }
+
+ /**
+ * @return the longValue
+ */
+ public long getLongValue() {
+ return this.longValue;
+ }
+
+ /**
+ * @return the objectValue
+ */
+ public Object getObjectValue() {
+ return this.objectValue;
+ }
+
+ /**
+ * @return the string
+ */
+ public String getStringValue() {
+ return this.stringValue;
+ }
+
+ /**
+ * @return the type
+ */
+ public DataType getType() {
+ return this.type;
+ }
+
+ /**
+ * @return the variableValue
+ */
+ public Variable getVariableValue() {
+ return this.variableValue;
+ }
+
+ /**
+ * Tests whether the data type is equal to a given type.
+ *
+ * @param expected
+ * the type to compare
+ * @return <i>true</i>: the type is the expected
+ */
+ public boolean isType(DataType expected) {
+ return this.type == expected;
+ }
+
+ /**
+ * Does an operation with another value.
+ *
+ * @param op
+ * the operation to do
+ * @param operand
+ * the other value
+ * @return the instance (for chaining)
+ * @throws VariantException
+ */
+ Variant binaryOperation(OpCode op, Variant operand) throws VariantException {
+ switch (op) {
+ case ASSIGN:
+ if (this.type != DataType.VARIABLE) {
+ throw new VariantException(I18N.tr("Assignment misses a rvalue (e.g. a variable) on the left side"));
+ }
+ this.variableValue.assign(operand);
+ break;
+ case DIV:
+ case MOD:
+ if (this.type == DataType.LONG) {
+ final long op2 = operand.asLong(0);
+ if (op2 == 0L) {
+ throw new VariantException(I18N.tr("division by 0"));
+ } else {
+ if (op == OpCode.MOD) {
+ this.longValue %= op2;
+ } else {
+ this.longValue /= op2;
+ }
+ }
+ } else {
+ wrongDataType(this, op, operand);
+ }
+ break;
+ case MINUS:
+ if (this.type == DataType.LONG) {
+ final long op2 = operand.asLong(0);
+ this.longValue -= op2;
+ } else {
+ wrongDataType(this, op, operand);
+ }
+ break;
+ case PLUS:
+ if (this.type == DataType.LONG) {
+ final long op2 = operand.asLong(0);
+ this.longValue += op2;
+ } else if (this.type == DataType.STRING) {
+ this.stringValue += operand.asString();
+ } else {
+ wrongDataType(this, op, operand);
+ }
+ break;
+ case POWER:
+ if (this.type == DataType.LONG) {
+ long op2 = operand.asLong(0);
+ if (op2 < 0) {
+ throw new VariantException(I18N.tr("exponent < 0: ") + operand.asString());
+ } else if (op2 == 0) {
+ this.longValue = 1;
+ } else if (op2 > 100) {
+ throw new VariantException(I18N.tr("exponent too large: ") + operand.asString());
+ } else {
+ long value = this.longValue;
+ while (op2-- > 1) {
+ value *= this.longValue;
+ }
+ this.longValue = value;
+ }
+ } else {
+ wrongDataType(this, op, operand);
+ }
+ break;
+ case TIMES:
+ if (this.type == DataType.LONG) {
+ final long op2 = operand.asLong(0);
+ this.longValue *= op2;
+ } else if (this.type == DataType.STRING) {
+ long factor = operand.asLong(0);
+ if (factor > 100000) {
+ throw new VariantException(I18N.tr("string multiplier too large: ") + factor);
+ } else {
+ final StringBuilder buffer = new StringBuilder((int) factor * this.stringValue.length());
+ while (factor-- > 0) {
+ buffer.append(this.stringValue);
+ }
+ this.stringValue = buffer.toString();
+ }
+ } else {
+ wrongDataType(this, op, operand);
+ }
+ break;
+ default:
+ throw new VariantException(String.format(I18N.tr("unexpected operation %s"), op.name()));
+ }
+ return this;
+ }
+
+ /**
+ * Takes the datat ype from another instance.
+ *
+ * @param source
+ * @return the instance (for chaining)
+ */
+ public Variant redefine(Variant source) {
+ switch (source.getType()) {
+ case BOOLEAN:
+ redefineBool(source.getBoolValue());
+ break;
+ case DOUBLE:
+ redefineDouble(source.getDoubleValue());
+ break;
+ case LONG:
+ redefineLong(source.getLongValue());
+ break;
+ case OBJECT:
+ redefineObject(source.getObjectValue());
+ break;
+ case STRING:
+ redefineString(source.getStringValue());
+ break;
+ case VARIABLE:
+ redefineVariable(source.getVariableValue());
+ break;
+ default:
+ break;
+ }
+ return this;
+ }
+
+ /**
+ * Change the data type to boolean.
+ *
+ * @param value
+ * the boolean value
+ * @return the instance (for chaining)
+ */
+ public Variant redefineBool(Boolean value) {
+ this.type = DataType.BOOLEAN;
+ this.stringValue = null;
+ this.longValue = Long.MIN_VALUE;
+ this.boolValue = value;
+ this.doubleValue = Double.NaN;
+ this.objectValue = null;
+ this.variableValue = null;
+ return this;
+ }
+
+ /**
+ * Change the data type to double.
+ *
+ * @param value
+ * the double value
+ * @return the instance (for chaining)
+ */
+ public Variant redefineDouble(double value) {
+ this.type = DataType.DOUBLE;
+ this.stringValue = null;
+ this.longValue = Long.MIN_VALUE;
+ this.boolValue = null;
+ this.doubleValue = value;
+ this.objectValue = null;
+ this.variableValue = null;
+ return this;
+ }
+
+ /**
+ * Change the data type to integer.
+ *
+ * @param value
+ * the value value
+ * @return the instance (for chaining)
+ */
+ public Variant redefineLong(long value) {
+ this.type = DataType.LONG;
+ this.stringValue = null;
+ this.longValue = value;
+ this.boolValue = null;
+ this.doubleValue = Double.NaN;
+ this.objectValue = null;
+ this.variableValue = null;
+ return this;
+ }
+
+ /**
+ * Change the data type to string.
+ *
+ * @param value
+ * the string value
+ * @return the instance (for chaining)
+ */
+ public Variant redefineObject(Object value) {
+ this.type = DataType.OBJECT;
+ this.stringValue = null;
+ this.longValue = Long.MIN_VALUE;
+ this.boolValue = null;
+ this.doubleValue = Double.NaN;
+ this.objectValue = value;
+ this.variableValue = null;
+ return this;
+ }
+
+ /**
+ * Change the data type to string.
+ *
+ * @param value
+ * the string value
+ * @return the instance (for chaining)
+ */
+ public Variant redefineString(String value) {
+ this.type = DataType.STRING;
+ this.stringValue = value;
+ this.longValue = Long.MIN_VALUE;
+ this.boolValue = null;
+ this.doubleValue = Double.NaN;
+ this.objectValue = null;
+ this.variableValue = null;
+ return this;
+ }
+
+ /**
+ * Change the data type to VARIABLE.
+ *
+ * @param value
+ * the variable
+ * @return the instance (for chaining)
+ */
+ public Variant redefineVariable(Variable value) {
+ this.type = DataType.VARIABLE;
+ this.stringValue = null;
+ this.longValue = Long.MIN_VALUE;
+ this.boolValue = null;
+ this.doubleValue = Double.NaN;
+ this.objectValue = null;
+ this.variableValue = value;
+ return this;
+ }
+
+ /**
+ * @param boolValue
+ * the boolValue to set
+ */
+ public void setBoolValue(boolean boolValue) {
+ this.boolValue = boolValue;
+ }
+
+ /**
+ * @param boolValue
+ * the boolValue to set
+ */
+ public void setBoolValue(Boolean boolValue) {
+ this.boolValue = boolValue;
+ }
+
+ /**
+ * @param doubleValue
+ * the doubleValue to set
+ */
+ public void setDoubleValue(double doubleValue) {
+ this.doubleValue = doubleValue;
+ }
+
+ /**
+ * @param longValue
+ * the longValue to set
+ */
+ public void setLongValue(long longValue) {
+ this.longValue = longValue;
+ }
+
+ /**
+ * @param objectValue
+ * the objectValue to set
+ */
+ public void setObjectValue(Object objectValue) {
+ this.objectValue = objectValue;
+ }
+
+ /**
+ * @param string
+ * the string to set
+ */
+ public void setStringValue(String string) {
+ this.stringValue = string;
+ }
+
+ /**
+ * @param type
+ * the type to set
+ */
+ public void setType(DataType type) {
+ this.type = type;
+ }
+
+ /**
+ * @param variableValue
+ * the variableValue to set
+ */
+ public void setVariableValue(Variable variableValue) {
+ this.variableValue = variableValue;
+ }
+
+ /**
+ * Informs about a wrong combination of data types and/or operator.
+ *
+ * @param op1
+ * first operand
+ * @param op
+ * operator
+ * @param op2
+ * second operand
+ * @throws VariantException
+ */
+ protected void wrongDataType(Variant op1, OpCode op, Variant op2) throws VariantException {
+ throw new VariantException(String.format(I18N.tr("wrong operators (%s - %s) for operator %s"),
+ op1.getType().name(), op2.getType().name(), op.name()));
+ }
+}
--- /dev/null
+/**
+ *
+ */
+package de.republib.expr;
+
+/**
+ * Handles errors in <i>Variant</i>
+ *
+ * @author hm
+ *
+ */
+public class VariantException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * @param message
+ * describes the reason of the error
+ */
+ VariantException(String message) {
+ super(message);
+ }
+}
graphics.setColor(Channel.colorLegend);
final int x = this.x0 + this.width / 2;
final int y = this.y0 + this.parent.getFontSizeLegend() + 5;
- final IPairData data = this.curves.get(0);
- graphics.drawString(data.getName(), x, y);
- String text = String.format("(%.1g, %.1g)", data.getX(0), data.getY(0));
- graphics.drawString(text, this.x0 + 5, this.y0 + this.getHeight() - this.parent.getFontSizeLegend());
- final int lastIx = data.getSteps() - 1;
- text = String.format("(%.1g, %.1g)", data.getX(lastIx), data.getY(lastIx));
- final int width = graphics.getFontMetrics().stringWidth(text);
- graphics.drawString(text, this.x0 + this.width - width - 5, y);
+ if (this.curves.size() > 0) {
+ final IPairData data = this.curves.get(0);
+ graphics.drawString(data.getName(), x, y);
+ String text = String.format("(%.1g, %.1g)", data.getX(0), data.getY(0));
+ graphics.drawString(text, this.x0 + 5, this.y0 + this.getHeight() - this.parent.getFontSizeLegend());
+ final int lastIx = data.getSteps() - 1;
+ text = String.format("(%.1g, %.1g)", data.getX(lastIx), data.getY(lastIx));
+ final int width = graphics.getFontMetrics().stringWidth(text);
+ graphics.drawString(text, this.x0 + this.width - width - 5, y);
+ }
}
/**
--- /dev/null
+/**
+ *
+ */
+package de.republib.gui;
+
+import javax.swing.JTextField;
+
+/**
+ * Helper functions for Graphical User Interface elements.
+ *
+ * Only static methods.
+ *
+ * @author hm
+ *
+ */
+public class GuiUtils {
+ /**
+ * Gets an integer from a text field.
+ *
+ * @param field
+ * the field holding the integer
+ * @param defaultValue
+ * return value if the field does not contain an integer
+ * @param repair
+ * <i>true</i>If the field value is not an integer the default
+ * value is set as field value
+ * @return <i>defaultValue</i> the field value has not a correct integer<br>
+ * the value of the field as integer
+ */
+ public static int numberOf(JTextField field, int defaultValue, boolean repair) {
+ int rc = defaultValue;
+ try {
+ rc = Integer.parseInt(field.getText());
+ } catch (final NumberFormatException e) {
+ if (repair) {
+ field.setText(String.valueOf(defaultValue));
+ }
+ }
+ return rc;
+ }
+}
*/
private static final long serialVersionUID = 1L;
- private final int columnCount = 2;
- private final int rowCount = 4;
+ private int columnCount = 2;
+ private int rowCount = 4;
private final ArrayList<Channel> channels = new ArrayList<>();
private final int widthGridLine = 1;
private final Color colorGridLine = Color.BLACK;
}
/**
- * @param channelCount
- * the channelCount to set
+ * @param rows
+ * count of rows
+ * @param columns
+ * count of columns
*/
- public void setChannelCount(int channelCount) {
+ public void setChannelCount(int rows, int columns) {
boolean changed = false;
- while (this.channels.size() > channelCount) {
- this.channels.remove(this.channels.size() - 1);
- changed = true;
- }
- while (this.channels.size() < channelCount) {
- this.channels.add(new Channel(this.channels.size(), this));
- changed = true;
- }
- if (changed) {
- resizeChannels();
+ if (rows > 0 && columns > 0) {
+ final int channelCount = rows * columns;
+ this.rowCount = rows;
+ this.columnCount = columns;
+ while (this.channels.size() > channelCount) {
+ this.channels.remove(this.channels.size() - 1);
+ changed = true;
+ }
+ while (this.channels.size() < channelCount) {
+ this.channels.add(new Channel(this.channels.size(), this));
+ changed = true;
+ }
+ if (changed) {
+ resizeChannels();
+ }
}
}
buffer.setLength(0 + 0);
length = this.inStream.read(buffer.getBuffer(), 0, buffer.getCapacity());
buffer.setLength(length);
- if (this.logger.isDebugEnabled()) {
+ if (buffer.startsWith("ERR")) {
+ this.logger.error("received: " + buffer.toUtf8());
+ } else if (this.logger.isDebugEnabled()) {
this.logger.debug(String.format("received: %s[%d]", buffer.toUtf8(4), length));
}
} catch (final IOException e) {
--- /dev/null
+/**
+ *
+ */
+package de.republib.pinet;
+
+/**
+ * @author hm
+ *
+ */
+public enum Function {
+ CONSTANT(0, 'c'), SIN(1, 's'), FALLING_SLOPE(3, 'f'), RISING_SLOPE(2, 'r'), DUAL_SLOPE(4, 'd');
+
+ /**
+ * Search a function by its id.
+ *
+ * @param id
+ * id to search
+ * @return null: not found<br>
+ * otherwise: the function with the id
+ */
+ public static Function findById(int id) {
+ Function rc = null;
+ for (final Function func : Function.values()) {
+ if (func.getId() == id) {
+ rc = func;
+ }
+ }
+ return rc;
+ }
+
+ private int id;
+
+ private byte code;
+
+ /**
+ * Constructor.
+ *
+ * @param id
+ * a current number
+ * @param code
+ * the code used in the TCP requests
+ */
+ private Function(int id, char code) {
+ this.id = id;
+ this.code = (byte) code;
+ }
+
+ /**
+ * @return the code
+ */
+ public byte getCode() {
+ return this.code;
+ }
+
+ /**
+ * @return the id
+ */
+ public int getId() {
+ return this.id;
+ }
+}
}
/**
- * Toggles an output pin between high and low.
+ * Lets the server toggle an output pin between high and low.
*
* @param pin
* pin for output
rc = sendAndReceive(this.buffer);
return rc;
}
+
+ /*
+ */
+ /**
+ * Lets the server put Pulse Code Modulation signals to a given pin.
+ *
+ * <pre>
+ * Syntax: OPWM[pin][clock][steps][startvalue][function][functionSteps]<br>
+ * [pin]: 1 byte. Numbering like <i>PinNumber</i>.<br>
+ * [clock]: 4 byte little endian. duration of a time slice in micro seconds.<br>
+ * [steps]: 2 byte little endian, number of time slices for one period.
+ * [startvalue]: 2 byte little endian, first value when starting the sequence.
+ * [function]: 's': sin, 'r': rising slope, 'f': falling slope, 'd': dual slope
+ * [functionSteps]: 2 byte little endian, number of PWM periods defining one function period
+ * [count]: 4 byte little endian.number of function periods
+ * </pre>
+ *
+ * @param pin
+ * pin for output
+ * @param clock
+ * minimal time slice of the pwm period in microseconds
+ * @param steps
+ * number of time slices
+ * @param startValue
+ * value of the first PWM period (in steps)
+ * @param function
+ * function controlling the pwm values
+ * @param functionSteps
+ * number of steps
+ * @param count
+ * number of function periods
+ * @return the answer from the server
+ */
+ public DynBytes pwmOutput(PinNumber pin, int period, double startValue, Function function, int functionSteps,
+ int count) {
+ DynBytes rc = null;
+ if (this.logger.isDebugEnabled()) {
+ this.logger
+ .debug(String.format("output pwm: pin: %d period: %d start: %f function: %s f.steps: %d count: %d",
+ pin.getNumber(), period, startValue, function.name(), functionSteps, count));
+ }
+ this.buffer.setLength(0).append("OPWM");
+ this.buffer.append((byte) pin.getNumber());
+ this.buffer.appendLittleEndian(period, 4);
+ final int value = Math.max(0, Math.min(10000, (int) Math.round(startValue * 1E4)));
+ this.buffer.appendLittleEndian(value, 2);
+ this.buffer.append(function.getCode());
+ this.buffer.appendLittleEndian(functionSteps, 2);
+ this.buffer.appendLittleEndian(count, 4);
+ rc = sendAndReceive(this.buffer);
+ return rc;
+ }
+
}
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.util.LinkedList;
+import java.util.List;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
+import de.republib.gui.GuiUtils;
import de.republib.gui.MultiChannelSheet;
import de.republib.pinet.GpioClient;
import de.republib.util.Announcer;
/**
* Created by hm on 03.07.16.
*/
-public class ControlCenter implements Announcer {
+public class ControlCenter implements Announcer, FocusListener {
private GpioClient client = null;
private JFrame frame;
-
+ private final List<JComboBox<String>> channelComboBoxes = new LinkedList<>();
private JPanel panelCenter;
private GPIOSettings panelGPIOSettings;
private JPanel panelLog;
private JLabel labelStatusLine;
- private MultiChannelSheet panelData;
+ private MultiChannelSheet panelMultiChannels;
private JPanel panelHead;
private JTextField textFieldServer;
private JTextField textFieldPort;
+ private JTextField textFieldRows;
+ private JTextField textFieldColumns;
private JTabbedPane tabbedPane;
+ private int channelCount = 8;
/**
* Constructor.
}
+ /**
+ * Adds a combobox with channels in the list to the internal list.
+ *
+ * @param comboBox
+ * combobox to add
+ */
+ public void addChannelComboBox(JComboBox<String> comboBox) {
+ this.channelComboBoxes.add(comboBox);
+ fillChannelCombo(comboBox, this.channelCount);
+ }
+
+ /**
+ * Builds the list of a given channel combo box.
+ *
+ * @param combobox
+ * combo box to modify
+ * @param count
+ * number of channels
+ */
+ public void fillChannelCombo(JComboBox<String> combobox, int count) {
+ final String[] names = new String[count];
+ for (int ix = 0; ix < count; ix++) {
+ names[ix] = String.valueOf(ix);
+ }
+ combobox.setModel(new DefaultComboBoxModel<>(names));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent)
+ */
+ @Override
+ public void focusGained(FocusEvent e) {
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.awt.event.FocusListener#focusLost(java.awt.event.FocusEvent)
+ */
+ @Override
+ public void focusLost(FocusEvent e) {
+ if (e.getSource() == this.textFieldRows || e.getSource() == this.textFieldColumns) {
+ setChannelCount(GuiUtils.numberOf(this.textFieldRows, 4, true),
+ GuiUtils.numberOf(this.textFieldColumns, 2, true));
+ }
+
+ }
+
/**
* @return the client
*/
return this.client;
}
+ /**
+ * Populates the main panel with widgets.
+ *
+ * @param host
+ * the host shown in the text field
+ * @param port
+ * the port shown in the text field
+ */
public void populate(String host, int port) {
this.frame = new JFrame(I18N.tr("Pinet Control Center"));
this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.panelHead.add(this.textFieldServer = new JTextField(host));
this.panelHead.add(new JLabel(I18N.tr("Port:")));
this.panelHead.add(this.textFieldPort = new JTextField(String.valueOf(port)));
+ this.panelHead.add(new JLabel(I18N.tr("Rows:")));
+ this.panelHead.add(this.textFieldRows = new JTextField(String.valueOf(4)));
+ this.textFieldRows.addFocusListener(this);
+ this.panelHead.add(new JLabel(I18N.tr("Column:")));
+ this.panelHead.add(this.textFieldColumns = new JTextField(String.valueOf(2)));
+ this.textFieldColumns.addFocusListener(this);
this.panelCenter.add(this.tabbedPane = new JTabbedPane());
this.tabbedPane.addTab(I18N.tr("GPIO settings"), this.panelGPIOSettings = new GPIOSettings(this));
this.panelGPIOSettings.populate();
- this.tabbedPane.addTab(I18N.tr("Diagram"), this.panelData = new MultiChannelSheet());
+ this.tabbedPane.addTab(I18N.tr("Diagram"), this.panelMultiChannels = new MultiChannelSheet());
populateCurves();
this.tabbedPane.addTab(I18N.tr("Log"), this.panelLog = new JPanel());
}
FunctionPairData function = new FunctionPairData(MathFunction.X, 0.0, 10.0, 790);
function.setFactor(9.0);
function.setOffset(1.0);
- this.panelData.addChannel(function);
+ this.panelMultiChannels.addChannel(function);
function = new FunctionPairData(MathFunction.SIN, 0.0, 10.0, 790);
function.setFactor(4.0);
function.setOffset(5.0);
- this.panelData.addChannel(function);
+ this.panelMultiChannels.addChannel(function);
function = new FunctionPairData(MathFunction.COS, 0.0, 10.0, 790);
function.setFactor(4.0);
function.setOffset(5.0);
- this.panelData.addChannel(function);
+ this.panelMultiChannels.addChannel(function);
function = new FunctionPairData(MathFunction.LOG, 0.01, 10.0, 790);
- this.panelData.addChannel(function);
+ this.panelMultiChannels.addChannel(function);
function = new FunctionPairData(MathFunction.EXP, -5.0, 5.0, 790);
- this.panelData.addChannel(function);
+ this.panelMultiChannels.addChannel(function);
function = new FunctionPairData(MathFunction.X2, -1, 2, 790);
- this.panelData.addChannel(function);
+ this.panelMultiChannels.addChannel(function);
function = new FunctionPairData(MathFunction.TAN, 0.0, 1.0, 790);
- this.panelData.addChannel(function);
+ this.panelMultiChannels.addChannel(function);
}
this.labelStatusLine.setText(prefix + message);
}
}
+
+ /**
+ * Sets a new channel count.
+ *
+ * @param rows
+ * number of rows of the multi channel sheet
+ * @param columns
+ * number of columns of the multi channel sheet
+ */
+ public void setChannelCount(int rows, int columns) {
+ this.channelCount = rows * columns;
+ for (final JComboBox<String> combo : this.channelComboBoxes) {
+ fillChannelCombo(combo, this.channelCount);
+ }
+ this.panelMultiChannels.setChannelCount(rows, columns);
+ this.panelMultiChannels.repaint();
+ }
}
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.GridLayout;
+import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.io.IOException;
+import java.net.URL;
+import javax.imageio.ImageIO;
+import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import de.republib.gui.GuiUtils;
+import de.republib.pinet.Function;
import de.republib.pinet.GpioClient;
import de.republib.pinet.PinNumber;
import de.republib.util.Announcer;
import de.republib.util.DynBytes;
import de.republib.util.I18N;
+import net.miginfocom.swing.MigLayout;
/**
* Manages the register page "GPIO Settings".
// private final Logger logger =
// LoggerFactory.getLogger(GPIOSettings.class);
private GPIOTable gpioTable;
- private OutputData panelOutputData;
- private InputData panelInputData;
+ private OutputBlinkPanel panelOutputBlinkData;
+ private OutputPwmPanel panelOutputPwmData;
+ private InputPanel panelInputData;
+ private InputPwmPanel panelInputPwmData;
private final ControlCenter center;
/**
/**
* @return the panelInputData
*/
- public InputData getPanelInputData() {
+ public InputPanel getPanelInputData() {
return this.panelInputData;
}
+ /**
+ * @return the panelInputPwmData
+ */
+ public InputPwmPanel getPanelInputPwmData() {
+ return this.panelInputPwmData;
+ }
+
+ /**
+ * @return the panelOutputData
+ */
+ public OutputBlinkPanel getPanelOutputBlinkData() {
+ return this.panelOutputBlinkData;
+ }
+
/**
* @return the panelOutputData
*/
- public OutputData getPanelOutputData() {
- return this.panelOutputData;
+ public OutputPwmPanel getPanelOutputPwmData() {
+ return this.panelOutputPwmData;
}
+ /**
+ * Populates the panel with the widgets.
+ */
public void populate() {
this.gpioTable = new GPIOTable(this);
add(this.gpioTable);
- final PinData panelPinData = new PinData(this.gpioTable);
+ final PinPanel panelPinData = new PinPanel(this.gpioTable);
this.gpioTable.setPanelPinData(panelPinData);
add(panelPinData);
- add(this.panelOutputData = new OutputData(this.center, panelPinData));
- this.panelOutputData.setVisible(false);
- add(this.panelInputData = new InputData(this.center, panelPinData));
- this.panelOutputData.setVisible(false);
+ add(this.panelOutputBlinkData = new OutputBlinkPanel(this.center, panelPinData));
+ this.panelOutputBlinkData.setVisible(false);
+ add(this.panelOutputPwmData = new OutputPwmPanel(this.center, panelPinData));
+ this.panelOutputPwmData.setVisible(true);
+ add(this.panelInputData = new InputPanel(this.center, panelPinData));
+ this.panelInputData.setVisible(false);
+ add(this.panelInputPwmData = new InputPwmPanel(this.center, panelPinData));
+ this.panelInputPwmData.setVisible(false);
}
}
new JLabel(I18N.tr("Pin")), new JLabel(I18N.tr("Name")) };
private final Object[][] cells;
- PinData panelPinData;
+ PinPanel panelPinData;
GPIOSettings gpioSettings;
/**
public GPIOTable(GPIOSettings parent) {
super(new GridLayout(21, 4));
this.gpioSettings = parent;
- this.pinButtons[0] = new PinButton(0, I18N.tr("+3.3V"), null);
- this.pinButtons[1] = new PinButton(1, I18N.tr("+5V"), null);
+ final String TEXT_GROUND = "GND";
+ final String TEXT_3V = "+3.3V";
+ final String TEXT_5V = "+5.0V";
+ this.pinButtons[0] = new PinButton(0, I18N.tr(TEXT_3V), null);
+ this.pinButtons[1] = new PinButton(1, I18N.tr(TEXT_5V), null);
this.pinButtons[2] = new PinButton(2, "SDA1", PinNumber.PIN_RPi2_03);
- this.pinButtons[3] = new PinButton(3, I18N.tr("+5V"), null);
+ this.pinButtons[3] = new PinButton(3, I18N.tr(TEXT_5V), null);
this.pinButtons[4] = new PinButton(4, "SCL1", PinNumber.PIN_RPi2_05);
- this.pinButtons[5] = new PinButton(5, I18N.tr("GND"), null);
+ this.pinButtons[5] = new PinButton(5, I18N.tr(TEXT_GROUND), null);
this.pinButtons[6] = new PinButton(6, "GCLK", PinNumber.PIN_RPi2_07);
this.pinButtons[7] = new PinButton(7, "TXD0", PinNumber.PIN_RPi2_08);
- this.pinButtons[8] = new PinButton(8, I18N.tr("GND"), null);
+ this.pinButtons[8] = new PinButton(8, I18N.tr(TEXT_GROUND), null);
this.pinButtons[9] = new PinButton(9, "RXD0", PinNumber.PIN_RPi2_10);
this.pinButtons[10] = new PinButton(10, "GEN0", PinNumber.PIN_RPi2_11);
this.pinButtons[11] = new PinButton(11, "GEN1", PinNumber.PIN_RPi2_12);
this.pinButtons[12] = new PinButton(12, "GEN2", PinNumber.PIN_RPi2_13);
- this.pinButtons[13] = new PinButton(13, I18N.tr("GND"), null);
+ this.pinButtons[13] = new PinButton(13, I18N.tr(TEXT_GROUND), null);
this.pinButtons[14] = new PinButton(14, "GEN3", PinNumber.PIN_RPi2_15);
this.pinButtons[15] = new PinButton(15, "GEN4", PinNumber.PIN_RPi2_16);
- this.pinButtons[16] = new PinButton(16, I18N.tr("+3.3V"), null);
+ this.pinButtons[16] = new PinButton(16, I18N.tr(TEXT_3V), null);
this.pinButtons[17] = new PinButton(17, "GEN5", PinNumber.PIN_RPi2_18);
this.pinButtons[18] = new PinButton(18, "SPI_MOSI", PinNumber.PIN_RPi2_19);
- this.pinButtons[19] = new PinButton(19, I18N.tr("GND"), null);
+ this.pinButtons[19] = new PinButton(19, I18N.tr(TEXT_GROUND), null);
this.pinButtons[20] = new PinButton(20, "SPI_MISO", PinNumber.PIN_RPi2_21);
this.pinButtons[21] = new PinButton(21, "GEN6", PinNumber.PIN_RPi2_22);
this.pinButtons[22] = new PinButton(22, "SPI_SCLK", PinNumber.PIN_RPi2_23);
this.pinButtons[23] = new PinButton(23, "SPI_CE0", PinNumber.PIN_RPi2_24);
- this.pinButtons[24] = new PinButton(24, I18N.tr("GND"), null);
+ this.pinButtons[24] = new PinButton(24, I18N.tr(TEXT_GROUND), null);
this.pinButtons[25] = new PinButton(25, "SPI_CE1", PinNumber.PIN_RPi2_26);
this.pinButtons[26] = new PinButton(26, "I2C_ID_SC", PinNumber.PIN_RPi2_27);
this.pinButtons[27] = new PinButton(27, "I2C_ID_SD", PinNumber.PIN_RPi2_28);
this.pinButtons[28] = new PinButton(28, null, PinNumber.PIN_RPi2_29);
- this.pinButtons[29] = new PinButton(29, I18N.tr("GND"), null);
+ this.pinButtons[29] = new PinButton(29, I18N.tr(TEXT_GROUND), null);
this.pinButtons[30] = new PinButton(30, null, PinNumber.PIN_RPi2_31);
this.pinButtons[31] = new PinButton(31, null, PinNumber.PIN_RPi2_32);
this.pinButtons[32] = new PinButton(32, null, PinNumber.PIN_RPi2_33);
- this.pinButtons[33] = new PinButton(33, I18N.tr("GND"), null);
+ this.pinButtons[33] = new PinButton(33, I18N.tr(TEXT_GROUND), null);
this.pinButtons[34] = new PinButton(34, null, PinNumber.PIN_RPi2_35);
this.pinButtons[35] = new PinButton(35, null, PinNumber.PIN_RPi2_36);
this.pinButtons[36] = new PinButton(36, null, PinNumber.PIN_RPi2_37);
this.pinButtons[37] = new PinButton(37, null, PinNumber.PIN_RPi2_38);
- this.pinButtons[38] = new PinButton(38, I18N.tr("GND"), null);
+ this.pinButtons[38] = new PinButton(38, I18N.tr(TEXT_GROUND), null);
this.pinButtons[39] = new PinButton(39, null, PinNumber.PIN_RPi2_40);
for (final PinButton pinButton : this.pinButtons) {
pinButton.addActionListener(this);
* @param panelPinData
* the panelPinData to set
*/
- public void setPanelPinData(PinData panelPinData) {
+ public void setPanelPinData(PinPanel panelPinData) {
this.panelPinData = panelPinData;
}
+
+ /**
+ * Updates the widgets related to a given pin button.
+ *
+ * @param button
+ * the button which should be updated
+ */
+ public void updateButton(PinButton button) {
+ final int row = button.getIndex() / 2;
+ if (button.getIndex() % 2 == 0) {
+ ((JLabel) this.cells[row][0]).setText(button.getNickname());
+ } else {
+ ((JLabel) this.cells[row][3]).setText(button.getNickname());
+ }
+ }
+
}
-class InputData extends JPanel {
+class InputPanel extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;
- // private final Logger logger = LoggerFactory.getLogger(OutputData.class);
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
private JComboBox<String> comboChannel;
- private JTextField textChannel;
+ private JTextField textInterval;
private final ControlCenter center;
- private final PinData pinData;
+ private final PinPanel pinData;
+ JButton buttonRun;
/**
* Constructor.
+ *
+ * @param center
+ * the control center
+ * @param pinData
+ * the parent panel
*/
- public InputData(ControlCenter center, PinData pinData) {
- super(new GridLayout(0, 2));
+ public InputPanel(ControlCenter center, PinPanel pinData) {
+ super(new MigLayout("fillx", "[left]rel[grow,fill]", "[]7[]"));
this.center = center;
this.pinData = pinData;
+ add(new JLabel(I18N.tr("Input parameters:")), "span 2, wrap");
+
add(new JLabel(I18N.tr("Channel:")));
- add(this.textChannel = new JTextField("0"));
+ add(this.comboChannel = new JComboBox<String>(), "wrap");
+ this.center.addChannelComboBox(this.comboChannel);
add(new JLabel(I18N.tr("Interval:")));
- add(this.textChannel = new JTextField("0"));
- add(new JLabel());
- add(new JLabel());
- add(new JLabel());
- add(new JLabel());
- add(new JLabel());
- add(new JLabel());
- add(new JLabel());
- add(new JLabel());
+ add(this.textInterval = new JTextField("0"), "wrap");
+ add(this.buttonRun = new JButton(I18N.tr("Start")), "span 2, wrap");
+ this.buttonRun.addActionListener(this);
}
+
+ /*
+ *
+ */
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (e.getSource() == this.buttonRun) {
+ final int interval = GuiUtils.numberOf(this.textInterval, 1000, true);
+ final int channel = this.comboChannel.getSelectedIndex();
+ this.logger.info(String.format("channel: %d interval: %d", channel, interval));
+ }
+ }
+
+}
+
+/**
+ * Manages the panel for input with Pulse Width Modulation.
+ *
+ * @author hm
+ *
+ */
+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 final ControlCenter center;
+ private final PinPanel pinData;
+ private JButton buttonRun;
+
+ /**
+ * Constructor.
+ *
+ * @param center
+ * the control center
+ * @param pinData
+ * the parent panel
+ */
+ public InputPwmPanel(ControlCenter center, PinPanel pinData) {
+ super(new MigLayout("fillx", "[left]rel[grow,fill]", "[]7[]"));
+ this.center = center;
+ this.pinData = pinData;
+ add(new JLabel(I18N.tr("Input parameters:")), "span 2, wrap");
+
+ add(new JLabel(I18N.tr("Channel:")));
+ 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(new JLabel(I18N.tr("Period:")));
+ add(this.textFieldSteps = new JTextField("1024"), "wrap");
+ add(this.buttonRun = new JButton(I18N.tr("Start")), "span 2, wrap");
+ this.buttonRun.addActionListener(this);
+
+ }
+
+ @Override
+ 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);
+ this.logger.info(String.format("channel: %d clock: %d steps: %d", channel, clock, steps));
+ }
+ }
+
}
/**
* @author hm
*
*/
-class OutputData extends JPanel implements ActionListener {
+class OutputBlinkPanel extends JPanel implements ActionListener {
/**
*
*/
private static final long serialVersionUID = 1L;
- private final static String[] modes = { I18N.tr("Blink") };
// private final Logger logger = LoggerFactory.getLogger(OutputData.class);
private JComboBox<String> comboMode;
private JTextField textCount;
private JTextField textLow;
private JButton buttonRun;
private final ControlCenter center;
- private final PinData pinData;
+ private final PinPanel pinData;
/**
* Constructor.
*/
- public OutputData(ControlCenter center, PinData pinData) {
- super(new GridLayout(0, 2));
+ public OutputBlinkPanel(ControlCenter center, PinPanel pinData) {
+ super(new MigLayout("fillx", "[left]rel[grow,fill]", "[]7[]"));
this.center = center;
this.pinData = pinData;
- add(new JLabel(I18N.tr("Mode:")));
- add(this.comboMode = new JComboBox<>(OutputData.modes));
+ add(new JLabel(I18N.tr("Blink parameters:")), "span 2, wrap");
add(new JLabel(I18N.tr("Count:")));
- add(this.textCount = new JTextField("10"));
+ add(this.textCount = new JTextField("10"), "wrap");
add(new JLabel(I18N.tr("High:")));
- add(this.textHigh = new JTextField("500"));
+ add(this.textHigh = new JTextField("500"), "wrap");
add(new JLabel(I18N.tr("Low:")));
- add(this.textLow = new JTextField("500"));
- add(new JLabel(""));
- add(this.buttonRun = new JButton(I18N.tr("Start")));
- this.comboMode.addActionListener(this);
+ add(this.textLow = new JTextField("500"), "wrap");
+ add(this.buttonRun = new JButton(I18N.tr("Start")), "span 2");
this.buttonRun.addActionListener(this);
}
if (e.getSource() == this.buttonRun) {
final GpioClient client = this.center.getClient();
final PinButton current = this.pinData.getCurrentPin();
- final int count = numberOf(this.textCount, 10);
- final int high = numberOf(this.textHigh, 500);
- final int low = numberOf(this.textLow, 500);
+ 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()));
+ 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(), count));
+ String.format(I18N.tr("pin %d blinks %d times"), current.getPinNumber().getNumber(), count));
}
} else if (e.getSource() == this.comboMode) {
}
}
+}
+
+/**
+ * Manages the panel for output with Pulse Width Modulation.
+ *
+ * @author hm
+ *
+ */
+class OutputPwmPanel extends JPanel implements ActionListener {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ // private final Logger logger = LoggerFactory.getLogger(OutputData.class);
+ 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 JButton buttonRun;
+ private final ControlCenter center;
+ private final PinPanel pinData;
/**
- * Gets a number from a text field.
+ * Constructor.
*
- * @param text
- * the text field
- * @param defaultValue
- * the return value for invalid field values
- * @return <i>defaultValue</i>: invalid field value<br>
- * otherwise: the value of the field as integer
- */
- private int numberOf(JTextField text, int defaultValue) {
- int rc = defaultValue;
- try {
- rc = Integer.parseInt(text.getText());
- } catch (final NumberFormatException e) {
- this.center.say(Announcer.ERROR,
- String.format(I18N.tr("not a number: '%s' used instead: %d"), text.getText(), rc));
- text.setText(String.valueOf(rc));
+ * @param center
+ * the control center
+ * @param pinData
+ * the parent panel
+ */
+ public OutputPwmPanel(ControlCenter center, PinPanel pinData) {
+ super(new MigLayout("fillx", "[left]rel[grow,fill]", "[]7[]"));
+ this.center = center;
+ 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(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(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
+ .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.buttonRun = new JButton(I18N.tr("Start")), "span 2");
+ this.buttonRun.addActionListener(this);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
+ */
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ 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 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")) {
+ this.center.say(Announcer.LOG,
+ String.format(I18N.tr("pin %d puts pwm %d times"), current.getPinNumber().getNumber(), count));
+ }
}
- return rc;
}
}
+/**
+ * Manages the properties of a GPIO pin button.
+ *
+ * This button can be used to set the properties of a pin.
+ *
+ * @author hm
+ *
+ */
class PinButton extends JButton {
/**
*
private static final long serialVersionUID = 1L;
public static final int MODE_UNDEF = 0;
public static final int MODE_INPUT = 1;
- public static final int MODE_OUTPUT = 2;
+ public static final int MODE_INPUT_PWM = 2;
+ public static final int MODE_OUTPUT_BLINK = 3;
+ public static final int MODE_OUTPUT_PWM = 4;
+ public static final int MODE_FOCUS = 5;
+ 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;
private int mode = PinButton.MODE_UNDEF;
- int index;
+ private final int index;
/**
* Constructor.
* otherwise: the pin number
*/
public PinButton(int index, String title, PinNumber pinNumber) {
- super(pinNumber == null ? title : String.valueOf(index + 1));
+ super(pinNumber == null ? title : String.format("P%02d", index + 1));
+ if (PinButton.icons == null) {
+ PinButton.icons = new ImageIcon[PinButton.MODE_COUNT];
+ PinButton.icons[PinButton.MODE_UNDEF] = null;
+ PinButton.icons[PinButton.MODE_INPUT] = null;
+ PinButton.icons[PinButton.MODE_INPUT_PWM] = null;
+ PinButton.icons[PinButton.MODE_OUTPUT_BLINK] = null;
+ PinButton.icons[PinButton.MODE_OUTPUT_PWM] = null;
+ try {
+ final URL url = getClass().getResource("/icons/input16.png");
+ final Image image = url == null ? null : ImageIO.read(url);
+ ImageIcon icon = image == null ? null : new ImageIcon(image);
+ PinButton.icons[PinButton.MODE_INPUT] = icon;
+ PinButton.icons[PinButton.MODE_INPUT_PWM] = icon;
+ icon = new ImageIcon(ImageIO.read(getClass().getResource("/icons/output16.png")));
+ PinButton.icons[PinButton.MODE_OUTPUT_BLINK] = icon;
+ PinButton.icons[PinButton.MODE_OUTPUT_PWM] = icon;
+ icon = new ImageIcon(ImageIO.read(getClass().getResource("/icons/undef16.png")));
+ PinButton.icons[PinButton.MODE_UNDEF] = icon;
+ icon = new ImageIcon(ImageIO.read(getClass().getResource("/icons/focus16.png")));
+ PinButton.icons[PinButton.MODE_FOCUS] = icon;
+ } catch (final IOException e) {
+ this.logger.error("cannot load icon", e);
+ }
+
+ }
this.index = index;
this.title = title == null ? pinNumber.getName() : title;
this.pinNumber = pinNumber;
+ setIcon(PinButton.icons[this.mode]);
}
/**
return this.mode;
}
+ /**
+ * @return the nickname
+ */
+ public String getNickname() {
+ return this.nickname == null || this.nickname.isEmpty() ? this.title : this.nickname;
+ }
+
/**
* @return the pinNumber
*/
* the mode to set
*/
public void setMode(int mode) {
- this.mode = mode;
+ if (mode != PinButton.MODE_FOCUS) {
+ this.mode = mode;
+ }
+ setIcon(PinButton.icons[this.mode]);
+ }
+
+ /**
+ * @param nickname
+ * the nickname to set
+ */
+ public void setNickname(String nickname) {
+ this.nickname = nickname;
}
}
* @author hm
*
*/
-class PinData extends JPanel implements ActionListener {
+class PinPanel extends JPanel implements ActionListener, FocusListener {
/**
*
*/
private static final long serialVersionUID = 1L;
- private final Logger logger = LoggerFactory.getLogger(PinData.class);
+ private final Logger logger = LoggerFactory.getLogger(PinPanel.class);
private JLabel labelPin;
private JLabel labelInternalNo;
private JLabel labelTitle;
+ private JTextField textFieldName;
private JComboBox<String> comboMode;
- private final String[] modes = { I18N.tr("undefined"), I18N.tr("input"), I18N.tr("output") };
+ private final String[] modes = { I18N.tr("undefined"), I18N.tr("input"), I18N.tr("input-pwm"),
+ I18N.tr("output-blink"), I18N.tr("output-pwm") };
private PinButton currentPin = null;
private final GPIOTable parent;
* @param table
* info about the pins
*/
- public PinData(GPIOTable table) {
- super(new GridLayout(0, 2));
+ public PinPanel(GPIOTable table) {
+ super(new MigLayout("fillx", "[left]rel[grow,fill]", "[]7[]"));
this.parent = table;
- add(new JLabel(I18N.tr("Pin No:")));
- add(this.labelPin = new JLabel());
- add(new JLabel(I18N.tr("GPIO number:")));
- add(this.labelInternalNo = new JLabel());
+ add(new JLabel(I18N.tr("Pin description:")), "span 2, wrap");
+ add(new JLabel(I18N.tr("Pin no:")));
+ add(this.labelPin = new JLabel(), "wrap");
+ add(new JLabel(I18N.tr("GPIO id:")));
+ add(this.labelInternalNo = new JLabel(), "wrap");
add(new JLabel(I18N.tr("Title:")));
- add(this.labelTitle = new JLabel());
+ add(this.labelTitle = new JLabel(), "wrap");
+ add(new JLabel(I18N.tr("Name:")));
+ add(this.textFieldName = new JTextField(), "wrap");
+ this.textFieldName.addFocusListener(this);
add(new JLabel(I18N.tr("Mode:")));
add(this.comboMode = new JComboBox<>(this.modes));
this.comboMode.addActionListener(this);
if (event.getSource() == this.comboMode) {
final int index = this.comboMode.getSelectedIndex();
if (this.currentPin.getMode() != index) {
- this.logger.debug("actionPerformed(); changing mode: " + index);
+ final boolean isOutputPWM = index == PinButton.MODE_OUTPUT_PWM;
+ this.logger.debug(
+ "actionPerformed(); changing mode: " + index + " isOutPwm: " + String.valueOf(isOutputPWM));
this.currentPin.setMode(index);
- this.parent.getGpioSettings().getPanelOutputData().setVisible(index == PinButton.MODE_OUTPUT);
+ this.parent.getGpioSettings().getPanelOutputBlinkData()
+ .setVisible(index == PinButton.MODE_OUTPUT_BLINK);
+ 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);
}
}
* properties of the pin
*/
public void fillData(PinButton button) {
+ if (this.currentPin != null) {
+ // change the icon:
+ this.currentPin.setMode(this.currentPin.getMode());
+ }
this.currentPin = button;
final PinNumber number = button.getPinNumber();
this.labelPin.setText(String.valueOf(button.getIndex() + 1));
this.labelInternalNo.setText(number == null ? "-" : String.valueOf(number.getNumber()));
this.labelTitle.setText(button.getTitle());
this.comboMode.setVisible(number != null);
- final int index = button.getMode();
- this.comboMode.setSelectedIndex(index);
- final OutputData output = this.parent.getGpioSettings().getPanelOutputData();
+ final int mode = button.getMode();
+ this.comboMode.setSelectedIndex(mode);
+ final OutputBlinkPanel output = this.parent.getGpioSettings().getPanelOutputBlinkData();
if (output != null) {
- output.setVisible(index == PinButton.MODE_OUTPUT);
+ output.setVisible(mode == PinButton.MODE_OUTPUT_BLINK);
+ }
+ final OutputPwmPanel outputPwm = this.parent.getGpioSettings().getPanelOutputPwmData();
+ if (outputPwm != null) {
+ outputPwm.setVisible(mode == PinButton.MODE_OUTPUT_PWM);
}
- final InputData input = this.parent.getGpioSettings().getPanelInputData();
+ final InputPanel input = this.parent.getGpioSettings().getPanelInputData();
if (input != null) {
- input.setVisible(index == PinButton.MODE_INPUT);
+ input.setVisible(mode == PinButton.MODE_INPUT);
+ }
+ final InputPwmPanel inputPwm = this.parent.getGpioSettings().getPanelInputPwmData();
+ if (inputPwm != null) {
+ inputPwm.setVisible(mode == PinButton.MODE_INPUT);
+ }
+ button.setMode(PinButton.MODE_FOCUS);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent)
+ */
+ @Override
+ public void focusGained(FocusEvent e) {
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.awt.event.FocusListener#focusLost(java.awt.event.FocusEvent)
+ */
+ @Override
+ public void focusLost(FocusEvent e) {
+ if (e.getSource() == this.textFieldName && this.currentPin != null) {
+ final String nickname = this.textFieldName.getText();
+ this.currentPin.setNickname(nickname);
+ this.parent.updateButton(this.currentPin);
}
}
}
return rc;
}
+
+ /**
+ * Evaluate a arithmetic expression.
+ *
+ * @param expr
+ * a formula with '+', '-', '*', '/', "**" and numbers but no
+ * parentheses
+ * @return the result
+ */
+ long numberOf(final String expr) throws Exception {
+ long rc = 0;
+ final String[] terms = expr.split("[-+]");
+ if (terms.length == 1) {
+ rc = Long.parseLong(expr);
+ } else {
+ for (final String term : terms) {
+ final String[] factors = term.split("[*/]");
+ if (factors.length == 1) {
+
+ } else {
+ for (final String factor : factors) {
+ final long rc2 = 1;
+ final String[] parts = factor.split("\\*\\*");
+ }
+ }
+ }
+ }
+ return rc;
+ }
}
--- /dev/null
+package de.republib.expr;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class ExpressionParserTest {
+
+ @Test
+ public void shouldConstruct() throws ParserException {
+ final Expression expr = new Expression("1");
+ final Variant result = expr.expr();
+ Assert.assertEquals(result.getType(), DataType.LONG);
+ Assert.assertEquals(result.getLongValue(), 1L);
+ }
+
+ @Test
+ public void shouldFindVariable() {
+ final Expression expr = new Expression("");
+ final String name = "aLong";
+ final Variable var = expr.findVariable(name);
+ var.assign(new Variant(47L));
+ Assert.assertEquals(expr.findVariable(name).getValue().getType(), DataType.LONG);
+ Assert.assertEquals(expr.findVariable(name).getValue().getLongValue(), 47L);
+ }
+
+ @Test
+ public void shouldWorkExpr() throws ParserException {
+ final Expression expr = new Expression("1 + 2*3");
+ Variant result = expr.expr();
+ Assert.assertEquals(result.getType(), DataType.LONG);
+ Assert.assertEquals(result.getLongValue(), 163L);
+
+ result = expr.reset("2 + 2*3**4 - 15 / 3 % 4").expr();
+ Assert.assertEquals(result.getType(), DataType.LONG);
+ Assert.assertEquals(result.getLongValue(), 163L);
+ }
+
+ @Test
+ public void shouldWorkTerm() throws ParserException {
+ final Expression expr = new Expression("2");
+
+ Variant result = expr.term();
+ Assert.assertEquals(result.getType(), DataType.LONG);
+ Assert.assertEquals(result.getLongValue(), 2L);
+
+ result = expr.reset("-2").term();
+ Assert.assertEquals(result.getType(), DataType.LONG);
+ Assert.assertEquals(result.getLongValue(), -2L);
+
+ result = expr.reset("'x'").term();
+ Assert.assertEquals(result.getType(), DataType.STRING);
+ Assert.assertEquals(result.getStringValue(), "x");
+
+ result = expr.reset("(44)").term();
+ Assert.assertEquals(result.getType(), DataType.LONG);
+ Assert.assertEquals(result.getLongValue(), 44L);
+ }
+}
--- /dev/null
+package de.republib.expr;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import de.republib.expr.ParserException;
+import de.republib.expr.Scanner;
+import de.republib.expr.Token;
+import de.republib.expr.TokenType;
+
+public class ScannerTest {
+ @Test
+ public void shouldConstruct() {
+ final Scanner scanner = new Scanner("");
+ Assert.assertNotNull(scanner);
+ }
+
+ @Test
+ public void shouldScanEndOfString() throws ParserException {
+ final Scanner scanner = new Scanner("");
+ Token token = scanner.nextToken();
+ Assert.assertEquals(TokenType.END_OF_STRING, token.getType());
+
+ scanner.reset("1");
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.INTEGER);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.END_OF_STRING);
+
+ scanner.reset("a");
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.ID);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.END_OF_STRING);
+
+ scanner.reset("'x");
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.STRING);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.END_OF_STRING);
+
+ scanner.reset("\"");
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.STRING);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.END_OF_STRING);
+
+ scanner.reset("*");
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.OP);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.END_OF_STRING);
+
+ scanner.reset("**");
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.OP);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.END_OF_STRING);
+
+ scanner.reset(" ");
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.END_OF_STRING);
+ }
+
+ @Test
+ public void shouldScanId() throws ParserException {
+ final Scanner scanner = new Scanner("x\tboy007\"a\"");
+ Token token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.ID);
+ Assert.assertEquals(token.getString(), "x");
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getString(), "boy007");
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.STRING);
+ }
+
+ @Test
+ public void shouldScanInteger() throws ParserException {
+ final Scanner scanner = new Scanner("9944 1\n'7733'");
+ Token token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.INTEGER);
+ Assert.assertEquals(token.getLongValue(), 9944);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.INTEGER);
+ Assert.assertEquals(token.getLongValue(), 1);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.STRING);
+
+ token = scanner.reset("0x1234567890 0Xabcdef1 0xABCDEF0").nextToken();
+ Assert.assertEquals(token.getType(), TokenType.INTEGER);
+ Assert.assertEquals(token.getLongValue(), 0x1234567890L);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.INTEGER);
+ Assert.assertEquals(token.getLongValue(), 0xabcdef1L);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.INTEGER);
+ Assert.assertEquals(token.getLongValue(), 0xABCDEF0L);
+ }
+
+ @Test
+ public void shouldScanOp() throws ParserException {
+ final Scanner scanner = new Scanner("+ - * / % ** ( )");
+ Token token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.OP);
+ Assert.assertEquals(token.getOpCode(), OpCode.PLUS);
+ Assert.assertEquals(token.getString(), "+");
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.OP);
+ Assert.assertEquals(token.getOpCode(), OpCode.MINUS);
+ Assert.assertEquals(token.getString(), "-");
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.OP);
+ Assert.assertEquals(token.getOpCode(), OpCode.TIMES);
+ Assert.assertEquals(token.getString(), "*");
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.OP);
+ Assert.assertEquals(token.getOpCode(), OpCode.DIV);
+ Assert.assertEquals(token.getString(), "/");
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.OP);
+ Assert.assertEquals(token.getOpCode(), OpCode.MOD);
+ Assert.assertEquals(token.getString(), "%");
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.OP);
+ Assert.assertEquals(token.getOpCode(), OpCode.POWER);
+ Assert.assertEquals(token.getString(), "**");
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.OP);
+ Assert.assertEquals(token.getOpCode(), OpCode.LEFT_PARENTH);
+ Assert.assertEquals(token.getString(), "(");
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.OP);
+ Assert.assertEquals(token.getOpCode(), OpCode.RIGHT_PARENT);
+ Assert.assertEquals(token.getString(), ")");
+ }
+
+ @Test
+ public void shouldScanSpaces() throws ParserException {
+ final Scanner scanner = new Scanner("\n\r\f \tX\t\r\n ");
+ Token token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+ Assert.assertEquals(token.getString(), "\n\r\f \t");
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.ID);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+ Assert.assertEquals(token.getString(), "\t\r\n ");
+ }
+
+ @Test
+ public void shouldScanString() throws ParserException {
+ final Scanner scanner = new Scanner("'a'\"xy\" '\\b\\n\\r' '\\\\\\'\\\"\\f'");
+ Token token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.STRING);
+ Assert.assertEquals(token.getString(), "a");
+ Assert.assertEquals(token.getRawString(), "'a'");
+ Assert.assertEquals(token.getDelimiter(), '\'');
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.STRING);
+ Assert.assertEquals(token.getString(), "xy");
+ Assert.assertEquals(token.getRawString(), "\"xy\"");
+ Assert.assertEquals(token.getDelimiter(), '\"');
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.STRING);
+ Assert.assertEquals(token.getString(), "\b\n\r");
+ Assert.assertEquals(token.getRawString(), "'\\b\\n\\r'");
+ Assert.assertEquals(token.getDelimiter(), '\'');
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.STRING);
+ Assert.assertEquals(token.getString(), "\\'\"\f");
+ Assert.assertEquals(token.getRawString(), "'\\\\\\'\\\"\\f'");
+ Assert.assertEquals(token.getDelimiter(), '\'');
+
+ scanner.reset("'\\xa.'");
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.STRING);
+ Assert.assertEquals(token.getString(), "\n.");
+ Assert.assertEquals(token.getRawString(), "'\\xa.'");
+ Assert.assertEquals(token.getDelimiter(), '\'');
+
+ scanner.reset("'\\x42c\\xd.\\x20'");
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.STRING);
+ Assert.assertEquals(token.getString(), "Bc\r. ");
+ Assert.assertEquals(token.getRawString(), "'\\x42c\\xd.\\x20'");
+ Assert.assertEquals(token.getDelimiter(), '\'');
+
+ scanner.reset("'\\u12ab' '\\u45'");
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.STRING);
+ Assert.assertEquals(token.getString(), "\u12ab");
+ Assert.assertEquals(token.getRawString(), "'\\u12ab'");
+ Assert.assertEquals(token.getDelimiter(), '\'');
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.SPACE);
+
+ token = scanner.nextToken();
+ Assert.assertEquals(token.getType(), TokenType.STRING);
+ Assert.assertEquals(token.getString(), "\u0045");
+ Assert.assertEquals(token.getRawString(), "'\\u45'");
+ Assert.assertEquals(token.getDelimiter(), '\'');
+ }
+
+}
--- /dev/null
+package de.republib.expr;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class VariableTest {
+
+ @Test
+ public void shouldAssign() {
+ final Variable var = new Variable("v1");
+ var.assign(new Variant(100L));
+ Assert.assertEquals(var.getValue().getType(), DataType.LONG);
+ Assert.assertEquals(var.getValue().getLongValue(), 100L);
+
+ var.assign(new Variant("Hi"));
+ Assert.assertEquals(var.getValue().getType(), DataType.STRING);
+ Assert.assertEquals(var.getValue().getStringValue(), "Hi");
+ }
+
+ @Test
+ public void ShouldConstruct() {
+ final Variable var = new Variable("v1");
+ Assert.assertEquals(var.getId(), "v1");
+ }
+}
--- /dev/null
+package de.republib.expr;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class VariantTest extends Variant {
+ VariantTest() {
+ super("Test");
+ }
+
+ @Test
+ public void shouldConstruct() {
+ final Variant varLong = new Variant(477L);
+ Assert.assertEquals(varLong.getLongValue(), 477L);
+
+ final Variant varBool = new Variant(true);
+ Assert.assertTrue(varBool.getBoolValue());
+
+ final Variant varFloat = new Variant(1.33E-99);
+ Assert.assertEquals(varFloat.getDoubleValue(), 1.33E-99);
+
+ final Variant varString = new Variant("Hi!");
+ Assert.assertEquals(varString.getStringValue(), "Hi!");
+ }
+
+ @Test
+ public void shouldOperateWithLong() throws VariantException {
+ final Variant var1 = new Variant(7L);
+ final Variant var2 = new Variant(4L);
+
+ var1.binaryOperation(OpCode.PLUS, var2);
+ Assert.assertEquals(var1.getLongValue(), 11L);
+
+ var1.binaryOperation(OpCode.MINUS, var2);
+ Assert.assertEquals(var1.getLongValue(), 7L);
+
+ var1.binaryOperation(OpCode.TIMES, var2);
+ Assert.assertEquals(var1.getLongValue(), 28L);
+
+ var1.binaryOperation(OpCode.DIV, var2);
+ Assert.assertEquals(var1.getLongValue(), 7L);
+
+ var1.binaryOperation(OpCode.MOD, var2);
+ Assert.assertEquals(var1.getLongValue(), 3L);
+
+ var1.binaryOperation(OpCode.POWER, var2);
+ Assert.assertEquals(var1.getLongValue(), 81L);
+ }
+
+ /**
+ * Change the data type to boolean.
+ *
+ * @param value
+ * the boolean value
+ * @return the instance (for chaining)
+ */
+
+ @Test
+ public void shouldOperateWithString() throws VariantException {
+ final Variant var1 = new Variant("Abc");
+ final Variant var2 = new Variant("...ZZZ");
+ final Variant var3 = new Variant(3L);
+ var1.binaryOperation(OpCode.TIMES, var3);
+ Assert.assertEquals(var1.getStringValue(), "AbcAbcAbc");
+
+ var1.binaryOperation(OpCode.PLUS, var2);
+ Assert.assertEquals(var1.getStringValue(), "AbcAbcAbc...ZZZ");
+
+ }
+
+ @Test
+ public void shouldRedefine() {
+ final Variant var1 = new Variant(477L);
+ Assert.assertTrue(var1.isType(DataType.LONG));
+ Assert.assertEquals(var1.getLongValue(), 477L);
+
+ var1.redefineBool(true);
+ Assert.assertTrue(var1.isType(DataType.BOOLEAN));
+ Assert.assertTrue(var1.getBoolValue());
+
+ var1.redefineString("Hi");
+ Assert.assertTrue(var1.isType(DataType.STRING));
+ Assert.assertEquals(var1.getStringValue(), "Hi");
+
+ var1.redefineDouble(7.33E-2);
+ Assert.assertTrue(var1.isType(DataType.DOUBLE));
+ Assert.assertEquals(var1.getDoubleValue(), 7.33E-2);
+
+ final Object obj = new Integer(22);
+ var1.redefineObject(obj);
+ Assert.assertTrue(var1.isType(DataType.OBJECT));
+ Assert.assertEquals(var1.getObjectValue(), obj);
+ }
+
+ @Test
+ public void shouldRedefineByCopy() {
+ final Variant var1 = new Variant(477L);
+ final Variant var2 = new Variant("Wow");
+ var1.redefine(var2);
+ Assert.assertTrue(var1.isType(DataType.STRING));
+ Assert.assertEquals(var1.asString(), "Wow");
+
+ var2.redefineBool(false);
+ var1.redefine(var2);
+ Assert.assertTrue(var1.isType(DataType.BOOLEAN));
+ Assert.assertEquals(var1.asString(), "false");
+
+ var2.redefineDouble(1E10);
+ var1.redefine(var2);
+ Assert.assertTrue(var1.isType(DataType.DOUBLE));
+ Assert.assertEquals(var1.asString(), "1.0E10");
+
+ var2.redefineLong(27L);
+ var1.redefine(var2);
+ Assert.assertTrue(var1.isType(DataType.LONG));
+ Assert.assertEquals(var1.asString(), "27");
+
+ final Object obj = new String("do it soon");
+ var2.redefineObject(obj);
+ var1.redefine(var2);
+ Assert.assertTrue(var1.isType(DataType.OBJECT));
+ Assert.assertEquals(var1.asString(), obj);
+
+ final Variable var = new Variable("dummy");
+ var2.redefineVariable(var);
+ var1.redefine(var2);
+ Assert.assertTrue(var1.isType(DataType.VARIABLE));
+ Assert.assertEquals(var1.getVariableValue(), var);
+ Assert.assertEquals(var1.getVariableValue().getValue().getStringValue(), var.getValue().getStringValue());
+ }
+
+ @Test
+ public void shouldThrowExceptionWithTypesAndOp() {
+ final Variant varLong = new Variant(477L);
+ redefineString("");
+ try {
+ wrongDataType(this, OpCode.POWER, varLong);
+ } catch (final VariantException e) {
+ Assert.assertTrue(e.getMessage().indexOf(DataType.LONG.name()) > 0);
+ Assert.assertTrue(e.getMessage().indexOf(DataType.STRING.name()) > 0);
+ Assert.assertTrue(e.getMessage().indexOf(OpCode.POWER.name()) > 0);
+ }
+ }
+
+ @Test
+ public void shouldThrowOnDivBy0() {
+ try {
+ final Variant var1 = new Variant(-7L);
+ final Variant var2 = new Variant(0L);
+ var1.binaryOperation(OpCode.DIV, var2);
+ } catch (final VariantException e) {
+ Assert.assertTrue(e.getMessage().indexOf("0") > 0);
+ }
+ }
+
+ @Test
+ public void shouldThrowOnPower() {
+ try {
+ final Variant var1 = new Variant(2L);
+ final Variant var2 = new Variant(-1L);
+ var1.binaryOperation(OpCode.POWER, var2);
+ } catch (final VariantException e) {
+ Assert.assertTrue(e.getMessage().indexOf("<") > 0);
+ }
+ try {
+ final Variant var1 = new Variant(2L);
+ final Variant var2 = new Variant(1024 * 1024);
+ var1.binaryOperation(OpCode.POWER, var2);
+ } catch (final VariantException e) {
+ Assert.assertTrue(e.getMessage().indexOf(String.valueOf(1024 * 1024)) > 0);
+ Assert.assertFalse(e.getMessage().indexOf("<") > 0);
+ }
+ }
+
+ @Test
+ public void shouldThrowOnTimesForString() {
+ try {
+ final Variant var1 = new Variant("x");
+ final Variant var2 = new Variant(1024L * 1024);
+ var1.binaryOperation(OpCode.TIMES, var2);
+ } catch (final VariantException e) {
+ Assert.assertTrue(e.getMessage().indexOf(String.valueOf(1024 * 1024)) > 0);
+ }
+ }
+
+ @Test
+ public void shouldThrowOnUnexpectedOp() {
+ final Variant var1 = new Variant("Abc");
+ final Variant var2 = new Variant("...ZZZ");
+ try {
+ var1.binaryOperation(OpCode.UNDEF, var2);
+ } catch (final VariantException e) {
+ Assert.assertTrue(e.getMessage().indexOf(OpCode.UNDEF.name()) > 0);
+ }
+ }
+
+ @Test
+ public void shouldWorkasLong() throws VariantException {
+ final Variant varLong = new Variant(477L);
+ Assert.assertEquals(varLong.asLong(-3), 477L);
+
+ final Variant varBool = new Variant(true);
+ Assert.assertEquals(varBool.asLong(5), 1L);
+
+ final Variant varFloat = new Variant(1.33E2);
+ Assert.assertEquals(varFloat.asLong(99), 133L);
+
+ final Variant varString = new Variant("744 x");
+ Assert.assertEquals(varString.asLong(44), 744L);
+ }
+
+ @Test
+ public void shouldWorkAsString() {
+ final Variant varLong = new Variant(477L);
+ Assert.assertEquals(varLong.asString(), "477");
+
+ final Variant varBool = new Variant(true);
+ Assert.assertEquals(varBool.asString(), "true");
+
+ final Variant varFloat = new Variant(1.33E-99);
+ Assert.assertEquals(varFloat.asString(), "1.33E-99");
+
+ final Variant varString = new Variant("Hi!");
+ Assert.assertEquals(varString.asString(), "Hi!");
+ }
+}
--- /dev/null
+package de.republib.gui;
+
+import javax.swing.JTextField;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class GuiUtilsTest {
+
+ @Test
+ public void numberOf() {
+ final JTextField field = new JTextField();
+ field.setText("-4711");
+ Assert.assertEquals(GuiUtils.numberOf(field, -1, true), -4711);
+ field.setText("");
+ Assert.assertEquals(GuiUtils.numberOf(field, -1, true), -1);
+ Assert.assertEquals(field.getText(), "-1");
+ field.setText("100k");
+ Assert.assertEquals(GuiUtils.numberOf(field, -44, false), -44);
+ Assert.assertEquals(field.getText(), "100k");
+ }
+}