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