View Javadoc
1   /*******************************************************************************
2    * jArduino: Arduino C++ Code Generation From Java
3    * Copyright 2020 Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   ******************************************************************************/
17  package net.sourceforge.jarduino.message;
18  
19  import java.util.ArrayList;
20  import java.util.HashMap;
21  import java.util.LinkedHashMap;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Objects;
25  
26  import net.sourceforge.jarduino.ArduinoException;
27  import net.sourceforge.jarduino.message.ArduinoAttribute.ArduinoAttrObject;
28  import net.sourceforge.jarduino.message.ArduinoParser.ArduinoParserException;
29  
30  /**
31   * Arduino Signal Message.
32   */
33  public class ArduinoMessage
34          implements ArduinoNamedObject, ArduinoAttrObject {
35      /**
36       * The Marker.
37       */
38      static final String MARKER = "BO_";
39  
40      /**
41       * The id of the message.
42       */
43      private final String theId;
44  
45      /**
46       * The name of the message.
47       */
48      private final String theName;
49  
50      /**
51       * The length of the message.
52       */
53      private final int theLength;
54  
55      /**
56       * The sending node.
57       */
58      private final ArduinoNode theSender;
59  
60      /**
61       * the list of signals.
62       */
63      private final List<ArduinoSignal> theSignals;
64  
65      /**
66       * Does the message have any float signals?
67       */
68      private boolean hasFloat;
69  
70      /**
71       * the multiplex signal.
72       */
73      private ArduinoSignal theMultiplexSignal;
74  
75      /**
76       * the map of multiplex signals.
77       */
78      private final Map<Long, List<ArduinoSignal>> theMultiplex;
79  
80      /**
81       * the Map of explicit attributes.
82       */
83      private final Map<ArduinoAttribute, Object> theAttributes;
84  
85      /**
86       * Constructor.
87       * @param pSender the sender node
88       * @param pMsgId the messageId
89       * @param pName the name
90       * @param pLength the length of the message
91       */
92      ArduinoMessage(final ArduinoNode pSender,
93                     final String pMsgId,
94                     final String pName,
95                     final int pLength) {
96          /* Store parameters */
97          theSender = pSender;
98          theId = pMsgId;
99          theName = pName;
100         theLength = pLength;
101 
102         /* Create the lists and map */
103         theSignals = new ArrayList<>();
104         theMultiplex = new LinkedHashMap<>();
105         theAttributes = new HashMap<>();
106     }
107 
108     /**
109      * Add signal.
110      * @param pSignal the signal
111      */
112     public void addSignal(final ArduinoSignal pSignal) {
113         /* Adjust float indication */
114         hasFloat |= pSignal.isFloat() && !pSignal.getRange().unBounded();
115 
116         /* If this is a main signal */
117         final Long myMulti = pSignal.getMultiplexId();
118         if (myMulti.equals(ArduinoSignal.MULTI_NONE)) {
119             /* Add to main list */
120             theSignals.add(pSignal);
121 
122             /* Record multiplex signal (if any) */
123             if (pSignal.isMultiplex()) {
124                 theMultiplexSignal = pSignal;
125             }
126 
127             /* Else add to the appropriate multiplex list */
128         } else {
129             final List<ArduinoSignal> myList = theMultiplex.computeIfAbsent(myMulti, m -> new ArrayList<>());
130             myList.add(pSignal);
131         }
132     }
133 
134     /**
135      * Obtain the id.
136      * @return the id
137      */
138     public String getId() {
139         return theId;
140     }
141 
142     @Override
143     public String getName() {
144         return theName;
145     }
146 
147     /**
148      * Obtain the length.
149      * @return the length
150      */
151     public int getLength() {
152         return theLength;
153     }
154 
155     /**
156      * Obtain the sender.
157      * @return the sender
158      */
159     public ArduinoNode getSender() {
160         return theSender;
161     }
162 
163     /**
164      * Obtain the system.
165      * @return the system
166      */
167     public ArduinoSystem getSystem() {
168         return theSender.getOwner();
169     }
170 
171     /**
172      * Obtain the non-multiplex signals.
173      * @return the signals
174      */
175     public List<ArduinoSignal> getSignals() {
176         return theSignals;
177     }
178 
179     /**
180      * Obtain a list of all the signals.
181      * @return the signals
182      */
183     public List<ArduinoSignal> getAllSignals() {
184         /* Create the list from the non-multiplex signals */
185         final List<ArduinoSignal> myResult = new ArrayList<>(theSignals);
186 
187         /* Loop through the multiplex map */
188         for (List<ArduinoSignal> myList : theMultiplex.values()) {
189             /* Add the multiplex signals */
190             myResult.addAll(myList);
191         }
192 
193         /* Return the list */
194         return myResult;
195     }
196 
197     /**
198      * Does this message have float signals?
199      * @return true/false
200      */
201     public boolean hasFloat() {
202         return hasFloat;
203     }
204 
205     /**
206      * Does the message have multiplex versions?
207      * @return true/false
208      */
209     public boolean hasMultiplex() {
210         return !theMultiplex.isEmpty();
211     }
212 
213     /**
214      * Obtain the multiplex signal.
215      * @return the multiplex signal
216      */
217     public ArduinoSignal getMultiplexSignal() {
218         return theMultiplexSignal;
219     }
220 
221     /**
222      * Obtain the multiplex map.
223      * @return the map
224      */
225     public Map<Long, List<ArduinoSignal>> getMultiplexMap() {
226         return theMultiplex;
227     }
228 
229     /**
230      * Set value for attribute.
231      * @param pAttr the attribute
232      * @param pValue the value
233      * @throws ArduinoParserException on error
234      */
235     void setAttrValue(final ArduinoAttribute pAttr,
236                       final Object pValue) throws ArduinoParserException {
237         /* Check that this is not a duplicate */
238         if (theAttributes.containsKey(pAttr)) {
239             throw new ArduinoParserException("Duplicate Attribute", pAttr.getName());
240         }
241 
242         /* Store the value */
243         theAttributes.put(pAttr, pValue);
244     }
245 
246     @Override
247     public Object getAttrValue(final ArduinoAttribute pAttr) {
248         final Object myValue = theAttributes.get(pAttr);
249         return myValue == null ? pAttr.getDefault() : myValue;
250     }
251 
252     /**
253      * find signal by name.
254      * @param pName the signal name.
255      * @return the signal
256      * @throws ArduinoParserException on error
257      */
258     ArduinoSignal findSignalByName(final String pName) throws ArduinoParserException {
259         /* Loop through the list */
260         for (ArduinoSignal mySignal : theSignals) {
261             if (pName.equals(mySignal.getName())) {
262                 return mySignal;
263             }
264         }
265 
266         /* Loop through the multiplex map */
267         for (List<ArduinoSignal> myList : theMultiplex.values()) {
268             /* Loop through the list */
269             for (ArduinoSignal mySignal : myList) {
270                 if (pName.equals(mySignal.getName())) {
271                     return mySignal;
272                 }
273             }
274         }
275 
276         /* Not found */
277         throw new ArduinoParserException("Unknown signal", pName);
278     }
279 
280     /**
281      * Parse message.
282      * @param pSystem the system
283      * @param pMessageDef the message representation
284      * @return the message
285      * @throws ArduinoException on error
286      */
287     static ArduinoMessage parseMessage(final ArduinoSystem pSystem,
288                                        final String pMessageDef) throws ArduinoException {
289         /* Split out header/definition */
290         final int myIndex = pMessageDef.indexOf(ArduinoChar.COLON);
291         if (myIndex == -1) {
292             throw new ArduinoException("Missing " + ArduinoChar.COLON + " separator", pMessageDef);
293         }
294         String myHdr = pMessageDef.substring(0, myIndex);
295         final String myLine = pMessageDef.substring(myIndex + 1).trim();
296 
297         /* Marker is first token in header */
298         final String myMarker = ArduinoParser.nextToken(myHdr);
299         myHdr = ArduinoParser.stripToken(myHdr, myMarker);
300         if (!myMarker.equals(MARKER)) {
301             throw new ArduinoException("Invalid marker", pMessageDef);
302         }
303 
304         /* Id is next token */
305         final String myId = ArduinoParser.nextToken(myHdr);
306         myHdr = ArduinoParser.stripToken(myHdr, myId);
307 
308         /* Name is final token in header */
309         final String myName = ArduinoParser.nextToken(myHdr);
310 
311         /* Protect against exceptions */
312         try {
313             /* Length is first token in line */
314             final String myLen = ArduinoParser.nextToken(myLine);
315             final String mySenderNode = ArduinoParser.stripToken(myLine, myLen);
316             final int myLength = ArduinoParser.parseNumber(myLen).intValue();
317             final ArduinoNode mySender = pSystem.findNodeByName(mySenderNode);
318 
319             /* Create the message and add to list */
320             final ArduinoMessageoMessage.html#ArduinoMessage">ArduinoMessage myMessage = new ArduinoMessage(mySender, myId, myName, myLength);
321             mySender.addMessage(myMessage);
322 
323             /* Return the message */
324             return myMessage;
325 
326             /* Catch parser exceptions */
327         } catch (ArduinoParserException e) {
328             throw new ArduinoException(e.getMessage() + ArduinoChar.COLON + e.getDetail(), pMessageDef);
329         }
330     }
331 
332     @Override
333     public boolean equals(final Object pThat) {
334         /* Handle trivial cases */
335         if (pThat == this) {
336             return true;
337         } else if (!(pThat instanceof ArduinoMessage)) {
338             return false;
339         }
340 
341         /* Access correctly */
342         final ArduinoMessage/../net/sourceforge/jarduino/message/ArduinoMessage.html#ArduinoMessage">ArduinoMessage myThat = (ArduinoMessage) pThat;
343 
344         /* Check message id/name */
345         return theId.equals(myThat.getId())
346                 && theName.equals(myThat.getName())
347                 && theSender.equals(myThat.getSender());
348     }
349 
350     @Override
351     public int hashCode() {
352         return Objects.hash(theId, theName, theSender);
353     }
354 
355     @Override
356     public String toString() {
357         /* Handle headers */
358         final StringBuilder myBuilder = new StringBuilder();
359         myBuilder.append(MARKER);
360         myBuilder.append(ArduinoChar.BLANK);
361         myBuilder.append(theId);
362         myBuilder.append(ArduinoChar.BLANK);
363         myBuilder.append(theName);
364         myBuilder.append(ArduinoChar.COLON);
365         myBuilder.append(ArduinoChar.BLANK);
366         myBuilder.append(theLength);
367         myBuilder.append(ArduinoChar.BLANK);
368         myBuilder.append(theSender);
369         myBuilder.append(ArduinoChar.LF);
370 
371         /* Loop through the signals */
372         for (ArduinoSignal theSignal : theSignals) {
373             myBuilder.append(ArduinoChar.BLANK);
374             myBuilder.append(theSignal);
375             myBuilder.append(ArduinoChar.LF);
376         }
377 
378         /* Loop through the map */
379         for (List<ArduinoSignal> myList : theMultiplex.values()) {
380             /* Loop through the signals */
381             for (ArduinoSignal arduinoSignal : myList) {
382                 myBuilder.append(ArduinoChar.BLANK);
383                 myBuilder.append(arduinoSignal);
384                 myBuilder.append(ArduinoChar.LF);
385             }
386         }
387 
388         /* return the string */
389         return myBuilder.toString();
390     }
391 }