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