ArduinoMessage.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.LinkedHashMap;
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 Message.
*/
public class ArduinoMessage
implements ArduinoNamedObject, ArduinoAttrObject {
/**
* The Marker.
*/
static final String MARKER = "BO_";
/**
* The id of the message.
*/
private final String theId;
/**
* The name of the message.
*/
private final String theName;
/**
* The length of the message.
*/
private final int theLength;
/**
* The sending node.
*/
private final ArduinoNode theSender;
/**
* the list of signals.
*/
private final List<ArduinoSignal> theSignals;
/**
* Does the message have any float signals?
*/
private boolean hasFloat;
/**
* the multiplex signal.
*/
private ArduinoSignal theMultiplexSignal;
/**
* the map of multiplex signals.
*/
private final Map<Long, List<ArduinoSignal>> theMultiplex;
/**
* the Map of explicit attributes.
*/
private final Map<ArduinoAttribute, Object> theAttributes;
/**
* Constructor.
* @param pSender the sender node
* @param pMsgId the messageId
* @param pName the name
* @param pLength the length of the message
*/
ArduinoMessage(final ArduinoNode pSender,
final String pMsgId,
final String pName,
final int pLength) {
/* Store parameters */
theSender = pSender;
theId = pMsgId;
theName = pName;
theLength = pLength;
/* Create the lists and map */
theSignals = new ArrayList<>();
theMultiplex = new LinkedHashMap<>();
theAttributes = new HashMap<>();
}
/**
* Add signal.
* @param pSignal the signal
*/
public void addSignal(final ArduinoSignal pSignal) {
/* Adjust float indication */
hasFloat |= pSignal.isFloat() && !pSignal.getRange().unBounded();
/* If this is a main signal */
final Long myMulti = pSignal.getMultiplexId();
if (myMulti.equals(ArduinoSignal.MULTI_NONE)) {
/* Add to main list */
theSignals.add(pSignal);
/* Record multiplex signal (if any) */
if (pSignal.isMultiplex()) {
theMultiplexSignal = pSignal;
}
/* Else add to the appropriate multiplex list */
} else {
final List<ArduinoSignal> myList = theMultiplex.computeIfAbsent(myMulti, m -> new ArrayList<>());
myList.add(pSignal);
}
}
/**
* Obtain the id.
* @return the id
*/
public String getId() {
return theId;
}
@Override
public String getName() {
return theName;
}
/**
* Obtain the length.
* @return the length
*/
public int getLength() {
return theLength;
}
/**
* Obtain the sender.
* @return the sender
*/
public ArduinoNode getSender() {
return theSender;
}
/**
* Obtain the system.
* @return the system
*/
public ArduinoSystem getSystem() {
return theSender.getOwner();
}
/**
* Obtain the non-multiplex signals.
* @return the signals
*/
public List<ArduinoSignal> getSignals() {
return theSignals;
}
/**
* Obtain a list of all the signals.
* @return the signals
*/
public List<ArduinoSignal> getAllSignals() {
/* Create the list from the non-multiplex signals */
final List<ArduinoSignal> myResult = new ArrayList<>(theSignals);
/* Loop through the multiplex map */
for (List<ArduinoSignal> myList : theMultiplex.values()) {
/* Add the multiplex signals */
myResult.addAll(myList);
}
/* Return the list */
return myResult;
}
/**
* Does this message have float signals?
* @return true/false
*/
public boolean hasFloat() {
return hasFloat;
}
/**
* Does the message have multiplex versions?
* @return true/false
*/
public boolean hasMultiplex() {
return !theMultiplex.isEmpty();
}
/**
* Obtain the multiplex signal.
* @return the multiplex signal
*/
public ArduinoSignal getMultiplexSignal() {
return theMultiplexSignal;
}
/**
* Obtain the multiplex map.
* @return the map
*/
public Map<Long, List<ArduinoSignal>> getMultiplexMap() {
return theMultiplex;
}
/**
* 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;
}
/**
* find signal by name.
* @param pName the signal name.
* @return the signal
* @throws ArduinoParserException on error
*/
ArduinoSignal findSignalByName(final String pName) throws ArduinoParserException {
/* Loop through the list */
for (ArduinoSignal mySignal : theSignals) {
if (pName.equals(mySignal.getName())) {
return mySignal;
}
}
/* Loop through the multiplex map */
for (List<ArduinoSignal> myList : theMultiplex.values()) {
/* Loop through the list */
for (ArduinoSignal mySignal : myList) {
if (pName.equals(mySignal.getName())) {
return mySignal;
}
}
}
/* Not found */
throw new ArduinoParserException("Unknown signal", pName);
}
/**
* Parse message.
* @param pSystem the system
* @param pMessageDef the message representation
* @return the message
* @throws ArduinoException on error
*/
static ArduinoMessage parseMessage(final ArduinoSystem pSystem,
final String pMessageDef) throws ArduinoException {
/* Split out header/definition */
final int myIndex = pMessageDef.indexOf(ArduinoChar.COLON);
if (myIndex == -1) {
throw new ArduinoException("Missing " + ArduinoChar.COLON + " separator", pMessageDef);
}
String myHdr = pMessageDef.substring(0, myIndex);
final String myLine = pMessageDef.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", pMessageDef);
}
/* Id is next token */
final String myId = ArduinoParser.nextToken(myHdr);
myHdr = ArduinoParser.stripToken(myHdr, myId);
/* Name is final token in header */
final String myName = ArduinoParser.nextToken(myHdr);
/* Protect against exceptions */
try {
/* Length is first token in line */
final String myLen = ArduinoParser.nextToken(myLine);
final String mySenderNode = ArduinoParser.stripToken(myLine, myLen);
final int myLength = ArduinoParser.parseNumber(myLen).intValue();
final ArduinoNode mySender = pSystem.findNodeByName(mySenderNode);
/* Create the message and add to list */
final ArduinoMessage myMessage = new ArduinoMessage(mySender, myId, myName, myLength);
mySender.addMessage(myMessage);
/* Return the message */
return myMessage;
/* Catch parser exceptions */
} catch (ArduinoParserException e) {
throw new ArduinoException(e.getMessage() + ArduinoChar.COLON + e.getDetail(), pMessageDef);
}
}
@Override
public boolean equals(final Object pThat) {
/* Handle trivial cases */
if (pThat == this) {
return true;
} else if (!(pThat instanceof ArduinoMessage)) {
return false;
}
/* Access correctly */
final ArduinoMessage myThat = (ArduinoMessage) pThat;
/* Check message id/name */
return theId.equals(myThat.getId())
&& theName.equals(myThat.getName())
&& theSender.equals(myThat.getSender());
}
@Override
public int hashCode() {
return Objects.hash(theId, theName, theSender);
}
@Override
public String toString() {
/* Handle headers */
final StringBuilder myBuilder = new StringBuilder();
myBuilder.append(MARKER);
myBuilder.append(ArduinoChar.BLANK);
myBuilder.append(theId);
myBuilder.append(ArduinoChar.BLANK);
myBuilder.append(theName);
myBuilder.append(ArduinoChar.COLON);
myBuilder.append(ArduinoChar.BLANK);
myBuilder.append(theLength);
myBuilder.append(ArduinoChar.BLANK);
myBuilder.append(theSender);
myBuilder.append(ArduinoChar.LF);
/* Loop through the signals */
for (ArduinoSignal theSignal : theSignals) {
myBuilder.append(ArduinoChar.BLANK);
myBuilder.append(theSignal);
myBuilder.append(ArduinoChar.LF);
}
/* Loop through the map */
for (List<ArduinoSignal> myList : theMultiplex.values()) {
/* Loop through the signals */
for (ArduinoSignal arduinoSignal : myList) {
myBuilder.append(ArduinoChar.BLANK);
myBuilder.append(arduinoSignal);
myBuilder.append(ArduinoChar.LF);
}
}
/* return the string */
return myBuilder.toString();
}
}