001package jmri.jmrix.bidib.swing.mon;
002
003import java.util.List;
004import java.util.LinkedList;
005import java.util.ArrayList;
006import java.util.HashMap;
007import java.util.Map;
008import javax.swing.BoxLayout;
009import javax.swing.JCheckBox;
010import javax.swing.JPanel;
011import jmri.InstanceManager;
012import jmri.UserPreferencesManager;
013import jmri.jmrix.bidib.BiDiBSystemConnectionMemo;
014import jmri.jmrix.bidib.BiDiBTrafficController;
015import jmri.jmrix.bidib.swing.BiDiBPanelInterface;
016import org.bidib.jbidibc.messages.AddressData;
017
018import org.bidib.jbidibc.messages.CRC8;
019import org.bidib.jbidibc.messages.BidibLibrary; //new
020import org.bidib.jbidibc.messages.exception.ProtocolException; //new
021import org.bidib.jbidibc.messages.utils.ByteUtils; //new
022import org.bidib.jbidibc.messages.utils.NodeUtils;
023import org.bidib.jbidibc.messages.base.RawMessageListener;
024import org.bidib.jbidibc.messages.Node;
025import org.bidib.jbidibc.messages.Feature;
026import org.bidib.jbidibc.messages.StringData;
027import org.bidib.jbidibc.messages.enums.AddressTypeEnum;
028import org.bidib.jbidibc.messages.enums.CommandStationProgState;
029import org.bidib.jbidibc.messages.enums.CommandStationPt;
030import org.bidib.jbidibc.messages.enums.LcOutputType;
031import org.bidib.jbidibc.messages.enums.PortModelEnum;
032import org.bidib.jbidibc.messages.message.*;
033
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037/**
038 * Swing action to create and register a MonFrame object.
039 *
040 * @author Bob Jacobsen Copyright (C) 2001, 2008
041 * @author Matthew Harris Copyright (C) 2011
042 * @since 2.11.4
043 * @author Eckart Meyer    Copyright (c) 2020-2023
044 */
045public class BiDiBMonPane extends jmri.jmrix.AbstractMonPane implements BiDiBPanelInterface {
046
047    final java.util.ResourceBundle rb = java.util.ResourceBundle.getBundle("jmri.jmrix.bidib.swing.BiDiBSwingBundle"); // NOI18N
048
049    protected BiDiBTrafficController tc = null;
050    protected BiDiBSystemConnectionMemo memo = null;
051    protected RawMessageListener rawMessageListener = null;
052    private final BidibResponseFactory responseFactory = new BidibResponseFactory();
053    private String output;
054    private final Map<Long, String> debugStringBuffer = new HashMap<>();
055
056    private final UserPreferencesManager pm;
057    final JCheckBox suppressDiagMessagesCheckBox = new JCheckBox();
058    final String suppressDiagMessagesCheck = this.getClass().getName() + ".SuppressDiagMessages";
059    
060    public BiDiBMonPane() {
061        super();
062        pm = InstanceManager.getDefault(UserPreferencesManager.class);
063    }
064
065//    @Override
066//    public String getHelpTarget() {
067//        // TODO: BiDiB specific help - if we need this
068//        return "package.jmri.jmrix.bidib.MonFrame"; // NOI18N
069//    }
070
071    @Override
072    public String getTitle() {
073        return (rb.getString("BiDiBMonPaneTitle")); // NOI18N
074    }
075
076    @Override
077    public void dispose() {
078        log.debug("Stopping BiDiB Monitor Panel");
079        if (rawMessageListener != null) {
080            tc.removeRawMessageListener(rawMessageListener);        
081            rawMessageListener = null;
082        }
083        pm.setSimplePreferenceState(suppressDiagMessagesCheck, suppressDiagMessagesCheckBox.isSelected());
084        super.dispose();        
085    }
086
087    @Override
088    public void init() {
089    }
090
091    @Override
092    protected void addCustomControlPanes(JPanel parent) {
093
094        JPanel p = new JPanel();
095        p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
096                
097        suppressDiagMessagesCheckBox.setText(rb.getString("CheckBoxSuppressDiagMessages"));
098        suppressDiagMessagesCheckBox.setVisible(true);
099        suppressDiagMessagesCheckBox.setSelected(pm.getSimplePreferenceState(suppressDiagMessagesCheck));
100        p.add(suppressDiagMessagesCheckBox);
101
102        parent.add(p);
103        super.addCustomControlPanes(parent);
104    }
105
106    @Override
107    public void initContext(Object context) {
108        if (context instanceof BiDiBSystemConnectionMemo) {
109            initComponents((BiDiBSystemConnectionMemo) context);
110        }
111    }
112
113    @Override
114    public void initComponents(BiDiBSystemConnectionMemo memo) {
115        log.debug("Starting BiDiB Monitor Panel");
116        this.memo = memo;
117        tc = memo.getBiDiBTrafficController();
118        createMonListener();
119    }
120    
121    private boolean suppressMessage(BidibMessageInterface message) {
122        if (suppressDiagMessagesCheckBox.isSelected()) {
123            int type = ByteUtils.getInt(message.getType());
124            switch (type) {
125                case BidibLibrary.MSG_BOOST_DIAGNOSTIC:
126                case BidibLibrary.MSG_BM_SPEED:
127                case BidibLibrary.MSG_BM_DYN_STATE:
128                case BidibLibrary.MSG_BM_CURRENT:
129                case BidibLibrary.MSG_CS_STATE:
130                case BidibLibrary.MSG_CS_SET_STATE:
131                case BidibLibrary.MSG_LOCAL_PING:
132                case BidibLibrary.MSG_LOCAL_PONG:
133                    return true;
134                default:
135                    break;
136            }
137        }
138        return false;
139    }
140
141    private void log1Message(BidibMessageInterface message, String line) {
142        Node node = tc.getNodeByAddr(message.getAddr());
143        if (node != null) {
144            output += String.format(" %010X (%s)", node.getUniqueId() & 0xffffffffffL,
145                node.getStoredString(StringData.INDEX_USERNAME)) + ": ";
146        }
147        else {
148            output += NodeUtils.formatAddress(message.getAddr()) + ": ";
149        }
150        if (rawCheckBox.isSelected()) {
151            output += "[" + ByteUtils.bytesToHex(message.getContent()) + "] " + message.toString() + "  ";
152        }
153        output += line + "\n";
154        
155    }
156    protected void logMessage(String prefix, byte[] data, List<BidibMessageInterface> messages, List<String> lines) {
157        output = prefix + " ";
158        if (messages.size() != 1) {
159            if (rawCheckBox.isSelected()) {
160                output += "[" + ByteUtils.bytesToHex(data) + "] ";
161            }
162            output += messages.size() + " Messages:\n";
163        }
164        if (messages.size() == 1) {
165            log.debug("Monitor: show message: {}", ((BidibMessage)messages.get(0)).getName());
166            if (suppressMessage(messages.get(0))) {
167                return;
168            }
169            log1Message(messages.get(0), lines.get(0));
170        }
171        else {
172            for (int i = 0; i < messages.size(); i++) {
173                output += "        ";
174                log1Message(messages.get(i), lines.get(i));
175            }
176        }
177        nextLine(output, null);
178    }
179    
180    private String evaluateMessage(final BidibMessageInterface message) {
181        String line = "";
182        Node node = tc.getNodeByAddr(message.getAddr());
183        PortModelEnum portModel = PortModelEnum.type;
184        if (node != null) {
185            portModel = tc.getPortModel(node);
186        }
187        int type = ByteUtils.getInt(message.getType());
188        switch (type) {
189            // received messages
190            case BidibLibrary.MSG_ACCESSORY_STATE:
191            {
192                AccessoryStateResponse m = (AccessoryStateResponse)message;
193                if (m.getAccessoryState().getExecute() == 0) {
194                    line = "accessory number: " + m.getAccessoryState().getAccessoryNumber() + ", aspect: " + m.getAccessoryState().getActiveAspect();
195                }
196                else {
197                    line += m.getAccessoryState().toString();
198                }
199            }
200                break;
201            case BidibLibrary.MSG_BOOST_DIAGNOSTIC:
202            {
203                BoostDiagnosticResponse m = (BoostDiagnosticResponse)message;
204                line = "Voltage: " + m.getVoltage() + " mV, Current: " + m.getCurrent() + " mA, Temperature: " + m.getTemperature() + " °C";
205            }
206                break;
207            case BidibLibrary.MSG_BOOST_STAT:
208            {
209                BoostStatResponse m = (BoostStatResponse)message;
210                line = "Booster State " + m.getState() + ", control: " + m.getControl();
211            }
212                break;
213            case BidibLibrary.MSG_BM_ADDRESS:
214            {
215                FeedbackAddressResponse m = (FeedbackAddressResponse)message;
216                line = "mnum: " + m.getDetectorNumber();
217                line += ", locos: ";
218                List<AddressData> addrList = m.getAddresses();
219                if (addrList.size() > 0) {
220                    for (AddressData addressData : addrList) {
221                        //line += String.format("0x%d ", addressData.getAddress() & 0xff);
222                        line += addressData + " ";
223                    }
224                }
225            }
226                break;
227            case BidibLibrary.MSG_BM_CURRENT:
228            {
229                FeedbackCurrentResponse m = (FeedbackCurrentResponse)message;
230                line = "mnum: " + m.getLocalDetectorAddress() + "current: " + m.getCurrent() + " mA";
231            }
232                break;
233            case BidibLibrary.MSG_BM_DYN_STATE:
234            {
235                FeedbackDynStateResponse m = (FeedbackDynStateResponse)message;
236                line = "mnum: " + m.getDetectorNumber() + ", decoder: " + m.getAddress() + " ";
237                int dynNumber = m.getDynNumber();
238                String dynText;
239                switch (dynNumber) {
240                    case 1:
241                        dynText = rb.getString("BmDynState1"); // NOI18N
242                        line += dynText + ": " + m.getDynValue() + "%";
243                        break;
244                    case 2:
245                        dynText = rb.getString("BmDynState2"); // NOI18N
246                        line += dynText + ": " + m.getDynValue() + " °C";
247                        break;
248                    case 3:
249                        dynText = rb.getString("BmDynState3"); // NOI18N
250                        line += dynText + ": " + m.getDynValue() + "%";
251                        break;
252                    case 4:
253                        dynText = rb.getString("BmDynState4"); // NOI18N
254                        line += dynText + ": " + m.getDynValue() + "%";
255                        break;
256                    case 5:
257                        dynText = rb.getString("BmDynState5"); // NOI18N
258                        line += dynText + ": " + m.getDynValue() + "%";
259                        break;
260                    case 6:
261                        dynText = rb.getString("BmDynState6"); // NOI18N
262                        line += dynText + ": " + m.getDynValue() + " mm";
263                        if (m.getTimestamp() != null) {
264                            dynText = rb.getString("BmDynStateTimeStamp"); // NOI18N
265                            line += ", " + dynText + ": " + m.getTimestamp();
266                        }
267                        break;
268                    default:
269                        log.error("Unexpected case: {}", dynNumber);
270                }
271            }
272                break;
273            case BidibLibrary.MSG_BM_FREE:
274            {
275                FeedbackFreeResponse m = (FeedbackFreeResponse)message;
276                line = "mnum: " + m.getDetectorNumber();
277            }
278                break;
279            case BidibLibrary.MSG_BM_OCC:
280            {
281                FeedbackOccupiedResponse m = (FeedbackOccupiedResponse)message;
282                line = "mnum: " + m.getDetectorNumber();
283            }
284                break;
285            case BidibLibrary.MSG_BM_MULTIPLE:
286            {
287                FeedbackMultipleResponse m = (FeedbackMultipleResponse)message;
288                line = "mnum: " + m.getBaseAddress() + ", size: " + m.getSize();
289                line += ", state bits: ";
290                byte[] stateBits = m.getDetectorData();
291                if (stateBits.length > 0) {
292                    for (int f : stateBits) {
293                        line += String.format("0x%02X ", f & 0xff);
294                    }
295                }
296            }
297                break;
298            case BidibLibrary.MSG_BM_SPEED:
299            {
300                FeedbackSpeedResponse m = (FeedbackSpeedResponse)message;
301                AddressData addressData = m.getAddress();
302                line = "Decoder: " + addressData + ", speed: " + m.getSpeed();
303            }
304                break;
305            case BidibLibrary.MSG_BM_CV:
306            {
307                FeedbackCvResponse m = (FeedbackCvResponse)message;
308                line = m.getAddress().toString() + ", CV" + m.getCvNumber() + " = " + m.getDat();
309            }
310                break;
311            case BidibLibrary.MSG_CS_DRIVE_STATE:
312            {
313                CommandStationDriveStateResponse m = (CommandStationDriveStateResponse)message;
314                AddressTypeEnum addressTypeEnum = AddressTypeEnum.LOCOMOTIVE_BACKWARD;
315                if ((m.getSpeed() & 0x80) == 0x80) {
316                    addressTypeEnum = AddressTypeEnum.LOCOMOTIVE_FORWARD;
317                }
318                AddressData addressData = new AddressData(m.getDecoderAddress(), addressTypeEnum);
319                line = "Decoder: " + addressData + ", speed: " + (m.getSpeed() & 0x7F);
320                line += ", function bits: ";
321//                line += String.format("0x%02X  ", m.getFunctionBitsF0toF4());
322                byte[] functionBits = m.getDriveState().getFunctions();
323                if (functionBits.length > 0) {
324                    for (int f : functionBits) {
325                        line += String.format("0x%02X ", f & 0xff);
326                    } 
327                }
328            }
329                break;
330            case BidibLibrary.MSG_CS_DRIVE_MANUAL:
331            {
332                CommandStationDriveManualResponse m = (CommandStationDriveManualResponse)message;
333                AddressTypeEnum addressTypeEnum = AddressTypeEnum.LOCOMOTIVE_BACKWARD;
334                if ((m.getSpeed() & 0x80) == 0x80) {
335                    addressTypeEnum = AddressTypeEnum.LOCOMOTIVE_FORWARD;
336                }
337                AddressData addressData = new AddressData(m.getAddress(), addressTypeEnum);
338                line = "Decoder: " + addressData + ", speed: " + (m.getSpeed() & 0x7F);
339                line += ", function bits: ";
340//                line += String.format("0x%02X  ", m.getFunctionBitsF0toF4());
341                byte[] functionBits = m.getDriveState().getFunctions();
342                if (functionBits.length > 0) {
343                    for (int f : functionBits) {
344                        line += String.format("0x%02X ", f & 0xff);
345                    } 
346                }
347            }
348                break;
349            case BidibLibrary.MSG_CS_STATE:
350            {
351                CommandStationStateResponse m = (CommandStationStateResponse)message;
352                line = "CS state " + m.getState();
353            }
354                break;
355            case BidibLibrary.MSG_CS_POM_ACK:
356            {
357                CommandStationPomAcknowledgeResponse m = (CommandStationPomAcknowledgeResponse)message;
358                line = "Addr: " + m.getAddress().toString() + ", Ack: " + m.getAcknState().toString();
359            }
360                break;
361            case BidibLibrary.MSG_CS_PROG_STATE:
362            {
363                CommandStationProgStateResponse m = (CommandStationProgStateResponse)message;
364                line = m.getState() + " CV" + (m.getCvNumber());
365                if (m.getState() == CommandStationProgState.PROG_OKAY) {
366                    line += " = " + m.getCvData();
367                }
368                line += ", remaining time: " + (m.getRemainingTime() * 100) + "ms";
369            }
370                break;
371            case BidibLibrary.MSG_LC_STAT:
372            {
373                LcStatResponse m = (LcStatResponse)message;
374                line = "port " + m.getPortNumber(portModel) + " (" + makePortTypeString(portModel, m.getPortType(portModel)) + "), state: " + (m.getPortStatus()& 0xFF);
375            }
376                break;
377            case BidibLibrary.MSG_LC_NA:
378            {
379                LcNotAvailableResponse m = (LcNotAvailableResponse)message;
380                line = "port " + m.getPortNumber(portModel) + " (" + makePortTypeString(portModel, m.getPortType(portModel)) + "), error code: " + (m.getErrorCode());
381            }
382                break;
383            case BidibLibrary.MSG_NODETAB_COUNT:
384            {
385                NodeTabCountResponse m = (NodeTabCountResponse)message;
386                line = "count: " + m.getCount();
387            }
388                break;
389            case BidibLibrary.MSG_FEATURE_COUNT:
390            {
391                FeatureCountResponse m = (FeatureCountResponse)message;
392                line = "count: " + m.getCount();
393            }
394                break;
395            case BidibLibrary.MSG_FEATURE:
396            {
397                FeatureResponse m = (FeatureResponse)message;
398                Feature f = m.getFeature();
399                line = f.getFeatureName() + " (" + f.getType() + ") = " + f.getValue();
400            }
401                break;
402            case BidibLibrary.MSG_STRING:
403            {
404                StringResponse m = (StringResponse)message;
405                // handle debug messages from a node
406                if (m.getStringData().getNamespace() == StringData.NAMESPACE_DEBUG) {
407                    String prefix = "===== device";
408                    int stringId = m.getStringData().getIndex();
409                    String value = m.getStringData().getValue();
410                    if (node == null) {
411                        log.error("Found node null in MSG_STRING");
412                        break;
413                    }
414                    long key = (node.getUniqueId() & 0x0000ffffffffffL) | (long)stringId << 40;
415                    if (value.charAt(value.length() - 1) == '\n') {
416                        String txt = "";
417                        // check if we have previous received imcomplete text
418                        if (debugStringBuffer.containsKey(key)) {
419                            txt = debugStringBuffer.get(key);
420                            debugStringBuffer.remove(key);
421                        }
422                        txt += value.replace("\n","");
423                        String line2 = "";
424                        switch(stringId) {
425                            case StringData.INDEX_DEBUG_STDOUT:
426                                line2 += prefix + " stdout: " + txt;
427                                break;
428                            case StringData.INDEX_DEBUG_STDERR:
429                                line2 += prefix + " stderr: " + txt;
430                                break;
431                            case StringData.INDEX_DEBUG_WARN:
432                                if (log.isWarnEnabled()) {
433                                    line2 += prefix + " WARN: " + txt;
434                                }
435                                break;
436                            case StringData.INDEX_DEBUG_INFO:
437                                if (log.isInfoEnabled()) {
438                                    line2 += prefix + " INFO: " + txt;
439                                }
440                                break;
441                            case StringData.INDEX_DEBUG_DEBUG:
442                                if (log.isDebugEnabled()) {
443                                    line2 += prefix + " DEBUG: " + txt;
444                                }
445                                break;
446                            case StringData.INDEX_DEBUG_TRACE:
447                                if (log.isTraceEnabled()) {
448                                    line2 += prefix + " TRACE: " + txt;
449                                }
450                                break;
451                            default: break;
452                        }
453                        if (!line2.isEmpty()) {
454                            line = line2;
455                        }
456                    }
457                    else {
458                        String txt = "";
459                        if (debugStringBuffer.containsKey(key)) {
460                            txt = debugStringBuffer.get(key);
461                        }
462                        debugStringBuffer.put(key, (txt + value));
463                    }
464                }
465                else {
466                    if (m.getStringData().getIndex() == 0) {
467                        line = "Product Name: " + m.getStringData().getValue();
468                    }
469                    else if (m.getStringData().getIndex() == 1) {
470                        line = "Username: " + m.getStringData().getValue();
471                    }
472                    else {
473                        line = "index: " + m.getStringData().getIndex() + ", value: " + m.getStringData().getValue();
474                    }
475                }
476            }
477                break;
478                
479                
480            // messages to send
481            case BidibLibrary.MSG_ACCESSORY_GET:
482            {
483                AccessoryGetMessage m = (AccessoryGetMessage)message;
484                line = "accessory number: " + m.getAccessoryNumber();
485            }
486                break;
487            case BidibLibrary.MSG_ACCESSORY_SET:
488            {
489                AccessorySetMessage m = (AccessorySetMessage)message;
490                line = "accessory number: " + m.getAccessoryNumber() + ", set aspect to " + m.getAspect();
491            }    
492                break;
493            case BidibLibrary.MSG_CS_ACCESSORY:
494            {
495                CommandStationAccessoryMessage m = (CommandStationAccessoryMessage)message;
496                line = "CS accessory decoder address: " + m.getDecoderAddress() + ", set aspect to " + m.getAspect();
497            }    
498                break;
499            case BidibLibrary.MSG_CS_DRIVE:
500            {
501                CommandStationDriveMessage m = (CommandStationDriveMessage)message;
502                line = "CS decoder address: " + m.getDecoderAddress() + ", speed: " + m.getSpeed();
503                line += ", function bits: ";
504                //line += String.format("0x%02X  ", m.getFunctionBitsF0toF4());
505                int[] functionBits = m.getFunctionBits();
506                if (functionBits.length > 0) {
507                    for (int f : functionBits) {
508                        line += String.format("0x%02X ", f & 0xff);
509                    } 
510                }
511            }    
512                break;
513            case BidibLibrary.MSG_CS_SET_STATE:
514            {
515                CommandStationSetStateMessage m = (CommandStationSetStateMessage)message;
516                line = "CS set state to " + m.getState();
517            }    
518                break;
519            case BidibLibrary.MSG_CS_POM:
520            {
521                CommandStationPomMessage m = (CommandStationPomMessage)message;
522                line = "OpCode " + ByteUtils.byteToHex(m.getOpCode()) + ", Addr: " + m.getDecoderAddress().toString() + ", CV" + m.getCvNumber();
523                int op = m.getOpCode();
524                if (op != 0x00  &&  op != 0x01  &&  op != 0x81) {
525                    line += " = " + ByteUtils.getCvXValue(m.getData(), 9, m.getData().length - 9);
526                }
527            }
528                break;
529            case BidibLibrary.MSG_CS_PROG:
530            {
531                CommandStationProgMessage m = (CommandStationProgMessage)message;
532                line = m.getOpCode() + " CV" + (m.getCvNumber());
533                if (m.getOpCode() == CommandStationPt.BIDIB_CS_PROG_RDWR_BIT  ||  m.getOpCode() == CommandStationPt.BIDIB_CS_PROG_WR_BYTE) {
534                    line += " = " + m.getCvData();
535                }
536            }
537                break;
538            case BidibLibrary.MSG_BM_ADDR_GET_RANGE:
539            {
540                FeedbackGetAddressRangeMessage m = (FeedbackGetAddressRangeMessage)message;
541                line = "get feedback status from number " + m.getBegin() + " to " + m.getEnd();
542            }    
543                break;
544            case BidibLibrary.MSG_LC_CONFIG_GET:
545            {
546                LcConfigGetMessage m = (LcConfigGetMessage)message;
547                line = "get port config for port " + m.toString();
548            }
549                break;
550            case BidibLibrary.MSG_LC_OUTPUT:
551            {
552                LcOutputMessage m = (LcOutputMessage)message;
553                line = "output to port " + m.getOutputNumber(portModel) + " (" + makePortTypeString(portModel, m.getOutputType(portModel)) + "), state: " + (m.getOutputStatus() & 0xFF);
554            }
555                break;
556
557            // - those messages either won't be used at all in JMRI or we just have not done it...:
558            // received messages
559            case BidibLibrary.MSG_BM_CONFIDENCE:
560            case BidibLibrary.MSG_BM_POSITION:
561            case BidibLibrary.MSG_BM_ACCESSORY: //what is this??
562            case BidibLibrary.MSG_BM_XPOM:
563            case BidibLibrary.MSG_BM_RCPLUS:
564            case BidibLibrary.MSG_ACCESSORY_NOTIFY:
565            case BidibLibrary.MSG_ACCESSORY_PARA:
566            case BidibLibrary.MSG_LC_KEY:
567            case BidibLibrary.MSG_LC_WAIT:
568            case BidibLibrary.MSG_LC_CONFIG:
569            case BidibLibrary.MSG_LC_CONFIGX:
570            case BidibLibrary.MSG_LC_MACRO_PARA:
571            case BidibLibrary.MSG_LC_MACRO:
572            case BidibLibrary.MSG_LC_MACRO_STATE:
573            case BidibLibrary.MSG_STALL:
574            case BidibLibrary.MSG_NODE_NEW:
575            case BidibLibrary.MSG_NODE_LOST:
576            case BidibLibrary.MSG_NODE_NA:
577            case BidibLibrary.MSG_NODETAB:
578            case BidibLibrary.MSG_SYS_ERROR:
579            case BidibLibrary.MSG_SYS_IDENTIFY_STATE:
580            case BidibLibrary.MSG_SYS_PONG:
581            case BidibLibrary.MSG_SYS_MAGIC:
582            case BidibLibrary.MSG_SYS_P_VERSION:
583            case BidibLibrary.MSG_SYS_SW_VERSION:
584            case BidibLibrary.MSG_SYS_UNIQUE_ID:
585            case BidibLibrary.MSG_CS_DRIVE_ACK:
586            case BidibLibrary.MSG_CS_DRIVE_EVENT:
587            case BidibLibrary.MSG_CS_ACCESSORY_ACK:
588            case BidibLibrary.MSG_CS_ACCESSORY_MANUAL:
589            case BidibLibrary.MSG_CS_RCPLUS_ACK:
590            case BidibLibrary.MSG_CS_M4_ACK:
591            case BidibLibrary.MSG_VENDOR_ACK:
592            case BidibLibrary.MSG_VENDOR:
593            case BidibLibrary.MSG_LOCAL_PONG:
594            case BidibLibrary.MSG_LOCAL_BIDIB_UP:
595            case BidibLibrary.MSG_FEATURE_NA:
596            case BidibLibrary.MSG_FW_UPDATE_STAT:
597            case BidibLibrary.MSG_LOGON:
598            // messages to send
599            case BidibLibrary.MSG_ACCESSORY_PARA_GET:
600            case BidibLibrary.MSG_ACCESSORY_PARA_SET:
601            case BidibLibrary.MSG_BOOST_OFF:
602            case BidibLibrary.MSG_BOOST_ON:
603            case BidibLibrary.MSG_BOOST_QUERY:
604            case BidibLibrary.MSG_CS_BIN_STATE:
605            case BidibLibrary.MSG_CS_M4:
606            case BidibLibrary.MSG_CS_QUERY:
607            case BidibLibrary.MSG_CS_RCPLUS:
608            case BidibLibrary.MSG_FEATURE_GETALL:
609            case BidibLibrary.MSG_FEATURE_GET:
610            case BidibLibrary.MSG_FEATURE_GETNEXT:
611            case BidibLibrary.MSG_FEATURE_SET:
612            case BidibLibrary.MSG_BM_GET_CONFIDENCE:
613            case BidibLibrary.MSG_BM_GET_RANGE:
614            case BidibLibrary.MSG_BM_MIRROR_FREE:
615            case BidibLibrary.MSG_BM_MIRROR_MULTIPLE:
616            case BidibLibrary.MSG_BM_MIRROR_OCC:
617            case BidibLibrary.MSG_BM_MIRROR_POSITION:
618            case BidibLibrary.MSG_FW_UPDATE_OP:
619            case BidibLibrary.MSG_LC_CONFIG_SET:
620            case BidibLibrary.MSG_LC_CONFIGX_GET_ALL:
621            case BidibLibrary.MSG_LC_CONFIGX_GET:
622            case BidibLibrary.MSG_LC_CONFIGX_SET:
623            case BidibLibrary.MSG_LC_KEY_QUERY:
624            case BidibLibrary.MSG_LC_MACRO_GET:
625            case BidibLibrary.MSG_LC_MACRO_HANDLE:
626            case BidibLibrary.MSG_LC_MACRO_PARA_GET:
627            case BidibLibrary.MSG_LC_MACRO_PARA_SET:
628            case BidibLibrary.MSG_LC_MACRO_SET:
629            case BidibLibrary.MSG_LC_PORT_QUERY:
630            case BidibLibrary.MSG_LC_PORT_QUERY_ALL:
631            case BidibLibrary.MSG_LOCAL_BIDIB_DOWN:
632            case BidibLibrary.MSG_LOCAL_EMITTER:
633            case BidibLibrary.MSG_LOCAL_PING:
634            case BidibLibrary.MSG_NODE_CHANGED_ACK:
635            case BidibLibrary.MSG_NODETAB_GETALL:
636            case BidibLibrary.MSG_NODETAB_GETNEXT:
637            case BidibLibrary.MSG_STRING_GET:
638            case BidibLibrary.MSG_STRING_SET:
639            case BidibLibrary.MSG_SYS_CLOCK:
640            case BidibLibrary.MSG_SYS_DISABLE:
641            case BidibLibrary.MSG_SYS_ENABLE:
642            case BidibLibrary.MSG_SYS_GET_ERROR:
643            case BidibLibrary.MSG_SYS_GET_P_VERSION:
644            case BidibLibrary.MSG_SYS_GET_SW_VERSION:
645            case BidibLibrary.MSG_SYS_GET_UNIQUE_ID:
646            case BidibLibrary.MSG_SYS_IDENTIFY:
647            case BidibLibrary.MSG_SYS_GET_MAGIC:
648            case BidibLibrary.MSG_SYS_PING:
649            case BidibLibrary.MSG_SYS_RESET:
650            case BidibLibrary.MSG_VENDOR_DISABLE:
651            case BidibLibrary.MSG_VENDOR_ENABLE:
652            case BidibLibrary.MSG_VENDOR_GET:
653            case BidibLibrary.MSG_VENDOR_SET:
654            default:
655                break;
656        }
657        BidibMessage m = (BidibMessage)message;
658        if (type != BidibLibrary.MSG_STRING  ||  !line.isEmpty()) {
659            return (line.isEmpty() ? m.getName() : m.getName() + ": " + line);
660        }
661        else {
662            return "";
663        }
664    }
665    
666    private String makePortModelString(PortModelEnum portModel) {
667        String portModelName = "unknown";
668        switch (portModel) {
669            case type:
670                portModelName = "type-based";
671                break;
672            case flat:
673                portModelName = "flat";
674                break;
675            case flat_extended:
676                portModelName = "flat-extended";
677                break;
678            default:
679                break;
680        }
681        return portModelName;
682    }
683
684    private String makePortTypeString(PortModelEnum portModel, LcOutputType portType) {
685        String ret = makePortModelString(portModel);
686        if (portModel == PortModelEnum.type) {
687            ret += ", " + portType;
688        }
689        return ret;
690    }
691    
692    private List<BidibMessageInterface> splitBidibMessages(byte[] data, boolean checkCRC)  throws ProtocolException {
693        log.trace("splitMessages: {}", ByteUtils.bytesToHex(data));
694        int index = 0;
695        List<BidibMessageInterface> result = new LinkedList<>();
696
697        while (index < data.length) {
698            int size = ByteUtils.getInt(data[index]) + 1 /* len */;
699            log.trace("Current size: {}", size);
700
701            if (size <= 0) {
702                throw new ProtocolException("cannot split messages, array size is " + size);
703            }
704
705            byte[] message = new byte[size];
706
707            try {
708                System.arraycopy(data, index, message, 0, message.length);
709            }
710            catch (ArrayIndexOutOfBoundsException ex) {
711                log
712                    .warn("Failed to copy, msg.len: {}, size: {}, output.len: {}, index: {}, output: {}",
713                        message.length, size, data.length, index, ByteUtils.bytesToHex(data));
714                throw new ProtocolException("Copy message data to buffer failed.");
715            }
716            result.add(responseFactory.create(message));
717            index += size;
718
719            if (checkCRC) {
720                // CRC
721                if (index == data.length - 1) {
722                    int crc = 0;
723                    int crcIndex = 0;
724                    for (crcIndex = 0; crcIndex < data.length - 1; crcIndex++) {
725                        crc = CRC8.getCrcValue((data[crcIndex] ^ crc) & 0xFF);
726                    }
727                    if (crc != (data[crcIndex] & 0xFF)) {
728                        throw new ProtocolException(
729                            "CRC failed: should be " + crc + " but was " + (data[crcIndex] & 0xFF));
730                    }
731                    break;
732                }
733            }
734        }
735
736        return result;
737
738    }
739    
740    private void createMonListener() {
741        rawMessageListener = new RawMessageListener() {
742            @Override
743            public void notifyReceived(byte[] data) {
744                log.debug("MON received message");
745                List<String> lines = new ArrayList<>();
746                List<BidibMessageInterface> messages = new ArrayList<>();
747                try {
748//                    Collection<byte[]> messagesData = MessageUtils.splitBidibMessages(data, true);
749//
750//                    //log.debug("MON: Number of splited messages: {}", messagesData.size());
751//
752//                    for (byte[] messageArray : messagesData) {
753//                        BidibMessageInterface message;
754//                        try {
755//                            message = responseFactory.create(messageArray);
756//                            messages.add(message);
757//                            String line = evaluateMessage(message);
758//                            lines.add(line);
759//                        }
760//                        catch (ProtocolException ex) {
761//                            log.error("Illegal BiDiB Message received: {} {}", messageArray, ex);
762//                        }
763                    List<BidibMessageInterface> commandMessages = splitBidibMessages(data, true);
764                    for (BidibMessageInterface message : commandMessages) {
765                        String line = evaluateMessage(message);
766                        //log.debug("**line: \"{}\", isEmpty: {}", line, line.isEmpty());
767                        if (!line.isEmpty()) {
768                            messages.add(message);
769                            lines.add(line);
770                        }
771                    }
772                    if (messages.size() > 0) {
773                        logMessage("<<", data, messages, lines);
774                    }
775                }
776                catch (ProtocolException ex) {
777                    log.warn("CRC failed.", ex);
778                }
779            }
780
781            @Override
782            public void notifySend(byte[] data) {
783                log.debug("MON sending message");
784                List<String> lines = new ArrayList<>();
785                List<BidibMessageInterface> messages = new ArrayList<>();
786                BidibRequestFactory requestFactory = tc.getBidib().getRootNode().getRequestFactory();
787                // Note: netBiDiB does NOT use the escape sequence. We must tell the parser not to use them
788                // otherwise a byte could be misinterpreted as an escape character and the parser will fail.
789                requestFactory.setEscapeMagic(!tc.isNetBiDiB());
790                try {
791                    List<BidibMessageInterface> commandMessages = requestFactory.create(data);
792                    for (BidibMessageInterface message : commandMessages) {
793                        messages.add(message);
794                        String line = evaluateMessage(message);
795                        lines.add(line);
796                    }
797                    logMessage(">>", data, messages, lines);
798                }
799                catch (ProtocolException ex) {
800                    log.error("Illegal BiDiB Message to send: {}", data, ex);
801                }
802            }
803        };
804        tc.addRawMessageListener(rawMessageListener);
805    }
806    
807
808    /**
809     * Nested class to create one of these using old-style defaults.
810     */
811//    static public class Default extends BiDiBNamedPaneAction {
812//
813//        public Default() {
814//            super(Bundle.getMessage("MonitorXTitle", "RFID Device"),
815//                    new JmriJFrameInterface(),
816//                    BiDiBMonPane.class.getName(),
817//                    InstanceManager.getDefault(BiDiBSystemConnectionMemo.class));
818//        }
819//    }
820
821    private final static Logger log = LoggerFactory.getLogger(BiDiBMonPane.class);
822
823}