ArduinoSignal.java
/*******************************************************************************
* jArduino: Arduino C++ Code Generation From Java
* Copyright 2020 Tony Washer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package net.sourceforge.jarduino.message;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import net.sourceforge.jarduino.ArduinoException;
import net.sourceforge.jarduino.message.ArduinoAttribute.ArduinoAttrObject;
import net.sourceforge.jarduino.message.ArduinoParser.ArduinoParserException;
/**
* Arduino Signal.
*/
public final class ArduinoSignal
implements ArduinoNamedObject, ArduinoAttrObject {
/**
* The Marker.
*/
static final String MARKER = "SG_";
/**
* The multiplex1 character.
*/
private static final char MULTI1 = 'M';
/**
* The multiplex2 character.
*/
private static final char MULTI2 = 'm';
/**
* The null multiplexId.
*/
public static final Long MULTI_NONE = -1L;
/**
* the owning message.
*/
private final ArduinoMessage theOwner;
/**
* the name of the signal.
*/
private final String theName;
/**
* the units of the signal.
*/
private final String theUnits;
/**
* the definition of the signal.
*/
private final ArduinoSignalDefinition theDefinition;
/**
* the factor of the signal.
*/
private final ArduinoSignalFactor theFactor;
/**
* the range of the signal.
*/
private final ArduinoSignalRange theRange;
/**
* the list of receiving Nodes.
*/
private final List<ArduinoNode> theReceivers;
/**
* the Map of explicit attributes.
*/
private final Map<ArduinoAttribute, Object> theAttributes;
/**
* the values.
*/
private ArduinoValues theValues;
/**
* is this the multiplex signal.
*/
private boolean isMultiplex;
/**
* the multiplex id.
*/
private Long theMultiplexId;
/**
* Constructor.
* @param pOwner the owner
* @param pName the name
* @param pDef the definition
* @param pFactor the factor
* @param pRange the range
* @param pUnits the units
*/
private ArduinoSignal(final ArduinoMessage pOwner,
final String pName,
final ArduinoSignalDefinition pDef,
final ArduinoSignalFactor pFactor,
final ArduinoSignalRange pRange,
final String pUnits) {
/* Store parameters */
theOwner = pOwner;
theName = pName;
theDefinition = pDef;
theFactor = pFactor;
theRange = pRange;
theUnits = pUnits;
isMultiplex = false;
theMultiplexId = MULTI_NONE;
/* Create the lists and maps */
theReceivers = new ArrayList<>();
theAttributes = new HashMap<>();
}
/**
* Parse the receiver nodes.
* @param pSystem the system
* @param pReceivers the receivers string
* @throws ArduinoParserException on error
*/
private void parseReceivers(final ArduinoSystem pSystem,
final String pReceivers) throws ArduinoParserException {
/* Loop through the tokens */
String myReceivers = pReceivers;
while (myReceivers.length() > 0) {
/* Split on comma separator */
final int myIndex = myReceivers.indexOf(ArduinoChar.COMMA);
if (myIndex == -1) {
theReceivers.add(pSystem.findNodeByName(myReceivers.trim()));
return;
}
final String myNode = myReceivers.substring(0, myIndex).trim();
theReceivers.add(pSystem.findNodeByName(myNode));
myReceivers = myReceivers.substring(myIndex + 1);
}
}
/**
* Obtain the owner.
* @return the owner
*/
public ArduinoMessage getOwner() {
return theOwner;
}
@Override
public String getName() {
return theName;
}
/**
* Obtain the units.
* @return the units
*/
public String getUnits() {
return theUnits;
}
/**
* Obtain the definition.
* @return the definition
*/
public ArduinoSignalDefinition getDefinition() {
return theDefinition;
}
/**
* Obtain the factor.
* @return the factor
*/
public ArduinoSignalFactor getFactor() {
return theFactor;
}
/**
* Obtain the range.
* @return the range
*/
public ArduinoSignalRange getRange() {
return theRange;
}
/**
* Does this signal use floats?
* @return true/false
*/
public boolean isFloat() {
return theFactor.isFloat() || theRange.isFloat();
}
/**
* Does this signal use signed ints?
* @return true/false
*/
public boolean isSigned() {
return !isFloat() && theDefinition.getDataType().isSigned();
}
/**
* Obtain values.
* @return the values
*/
public ArduinoValues getValues() {
return theValues;
}
/**
* set values.
* @param pValues the values
*/
void setValues(final ArduinoValues pValues) {
theValues = pValues;
}
/**
* Is this the multiplex signal.
* @return true/false
*/
public boolean isMultiplex() {
return isMultiplex;
}
/**
* set multiplex.
*/
private void setMultiplex() {
isMultiplex = true;
}
/**
* Obtain the multiplexId.
* @return the Id
*/
public Long getMultiplexId() {
return theMultiplexId;
}
/**
* Set the multiplexId.
* @param pId the multiplexId
*/
public void setMultiplexId(final Long pId) {
theMultiplexId = pId;
}
/**
* Obtain the receivers.
* @return the receivers
*/
public List<ArduinoNode> getReceivers() {
return theReceivers;
}
/**
* Set value for attribute.
* @param pAttr the attribute
* @param pValue the value
* @throws ArduinoParserException on error
*/
void setAttrValue(final ArduinoAttribute pAttr,
final Object pValue) throws ArduinoParserException {
/* Check that this is not a duplicate */
if (theAttributes.containsKey(pAttr)) {
throw new ArduinoParserException("Duplicate Attribute", pAttr.getName());
}
/* Store the value */
theAttributes.put(pAttr, pValue);
}
@Override
public Object getAttrValue(final ArduinoAttribute pAttr) {
final Object myValue = theAttributes.get(pAttr);
return myValue == null ? pAttr.getDefault() : myValue;
}
/**
* Parse signal.
* @param pOwner the owning message
* @param pSignalDef the signal representation
* @throws ArduinoException on error
*/
static void parseSignal(final ArduinoMessage pOwner,
final String pSignalDef) throws ArduinoException {
/* Split out header/signal */
final int myIndex = pSignalDef.indexOf(ArduinoChar.COLON);
if (myIndex == -1) {
throw new ArduinoException("Missing " + ArduinoChar.COLON + " separator", pSignalDef);
}
String myHdr = pSignalDef.substring(0, myIndex);
String myLine = pSignalDef.substring(myIndex + 1).trim();
/* Marker is first token in header */
final String myMarker = ArduinoParser.nextToken(myHdr);
myHdr = ArduinoParser.stripToken(myHdr, myMarker);
if (!myMarker.equals(MARKER)) {
throw new ArduinoException("Invalid marker", pSignalDef);
}
/* Name is next token in header */
final String myName = ArduinoParser.nextToken(myHdr);
myHdr = ArduinoParser.stripToken(myHdr, myName);
/* Protect against exceptions */
try {
/* Definition is first token in line */
final String myDataDef = ArduinoParser.nextToken(myLine);
myLine = ArduinoParser.stripToken(myLine, myDataDef);
final ArduinoSignalDefinition myDef = ArduinoSignalDefinition.parseDefinition(myDataDef);
/* Factor is next token */
final String myFactorDef = ArduinoParser.nextToken(myLine);
myLine = ArduinoParser.stripToken(myLine, myFactorDef);
final ArduinoSignalFactor myFactor = ArduinoSignalFactor.parseFactors(myFactorDef);
/* Range is next token */
final String myRangeDef = ArduinoParser.nextToken(myLine);
myLine = ArduinoParser.stripToken(myLine, myRangeDef);
final ArduinoSignalRange myRange = ArduinoSignalRange.parseRange(myRangeDef);
/* Units is next token */
final String myUnits = ArduinoParser.nextQuotedToken(myLine);
myLine = ArduinoParser.stripQuotedToken(myLine, myUnits);
/* Create the signal and parse the final token */
final ArduinoSignal mySignal = new ArduinoSignal(pOwner, myName, myDef, myFactor, myRange, myUnits);
mySignal.parseReceivers(pOwner.getSystem(), myLine);
/* If we have a multiplex details */
if (myHdr.length() > 0) {
if (myHdr.charAt(0) == MULTI1) {
mySignal.setMultiplex();
} else {
if (myHdr.charAt(0) != MULTI2) {
throw new ArduinoParserException("Invalid multiplex", myHdr);
}
mySignal.setMultiplexId((long) ArduinoParser.parseNumber(myHdr.substring(1)));
}
}
/* Register with owner */
pOwner.addSignal(mySignal);
/* Handle parser exceptions */
} catch (ArduinoParserException e) {
throw new ArduinoException(e.getMessage() + ArduinoChar.COLON + e.getDetail(), pSignalDef);
}
}
@Override
public boolean equals(final Object pThat) {
/* Handle trivial cases */
if (pThat == this) {
return true;
} else if (!(pThat instanceof ArduinoSignal)) {
return false;
}
/* Access correctly */
final ArduinoSignal myThat = (ArduinoSignal) pThat;
/* Check name and owner */
return theName.equals(myThat.getName())
&& theOwner.equals(myThat.getOwner());
}
@Override
public int hashCode() {
return Objects.hash(theOwner, theName);
}
@Override
public String toString() {
/* Handle headers */
final StringBuilder myBuilder = new StringBuilder();
myBuilder.append(MARKER);
myBuilder.append(ArduinoChar.BLANK);
myBuilder.append(theName);
/* Handle multiplex */
if (isMultiplex) {
myBuilder.append(ArduinoChar.BLANK);
myBuilder.append(MULTI1);
} else if (!theMultiplexId.equals(MULTI_NONE)) {
myBuilder.append(ArduinoChar.BLANK);
myBuilder.append(MULTI2);
myBuilder.append(theMultiplexId);
}
/* Append the detail */
myBuilder.append(ArduinoChar.COLON);
myBuilder.append(ArduinoChar.BLANK);
myBuilder.append(theDefinition);
myBuilder.append(ArduinoChar.BLANK);
myBuilder.append(theFactor);
myBuilder.append(ArduinoChar.BLANK);
myBuilder.append(theRange);
myBuilder.append(ArduinoChar.BLANK);
myBuilder.append(ArduinoChar.QUOTE);
myBuilder.append(theUnits);
myBuilder.append(ArduinoChar.QUOTE);
myBuilder.append(ArduinoChar.BLANK);
/* Loop through the receivers */
for (int i = 0; i < theReceivers.size(); i++) {
if (i > 0) {
myBuilder.append(ArduinoChar.COMMA);
}
myBuilder.append(theReceivers.get(i));
}
/* return the string */
return myBuilder.toString();
}
}