001package jmri.jmrix.cmri.serial.serialmon;
002
003import jmri.jmrix.cmri.CMRISystemConnectionMemo;
004import jmri.jmrix.cmri.serial.SerialListener;
005import jmri.jmrix.cmri.serial.SerialMessage;
006import jmri.jmrix.cmri.serial.SerialReply;
007import jmri.jmrix.cmri.serial.SerialNode;
008
009import java.awt.event.ActionEvent;
010
011import javax.swing.BoxLayout;
012import javax.swing.JButton;
013import javax.swing.JPanel;
014
015/**
016 * Frame displaying (and logging) CMRI serial command messages.
017 *
018 * @author Bob Jacobsen Copyright (C) 2001
019 * @author Chuck Catania  Copyright (C) 2014, 2016, 2017
020 */
021public class SerialMonFrame extends jmri.jmrix.AbstractMonFrame implements SerialListener {
022    // member declarations
023    // String deltaTCheck = this.getClass().getName()+".DeltaT"; // NOI18N
024   
025    protected JButton packetfilterButton = new JButton(Bundle.getMessage("FilterPacketsText") );  // NOI18N
026    protected static int _DLE    = 0x10;    
027   
028    private CMRISystemConnectionMemo _memo = null;    
029
030    public SerialMonFrame(CMRISystemConnectionMemo memo) {
031        super();
032        _memo = memo;        
033    }
034
035    @Override
036    public void dispose() { 
037        super.dispose();
038  }
039
040    @Override
041    protected String title() {
042        return Bundle.getMessage("SerialCommandMonTitle")+" "+Bundle.getMessage("Connection")+_memo.getUserName();  // NOI18N
043    }
044
045    @Override
046    protected void init() {
047        // connect to TrafficController
048        _memo.getTrafficController().addSerialListener(this);
049        // Load the packet filter bits
050        initializePacketFilters();
051        
052        // Add additional controls to the super class window
053        JPanel paneA = new JPanel();
054        paneA.setLayout(new BoxLayout(paneA, BoxLayout.Y_AXIS));
055        
056        JPanel pane1 = new JPanel();
057        pane1.setLayout(new BoxLayout(pane1, BoxLayout.X_AXIS));
058        
059        packetfilterButton.setText(Bundle.getMessage("FilterPacketsText"));  // NOI18N
060        packetfilterButton.setVisible(true);
061        packetfilterButton.setToolTipText(Bundle.getMessage("FilterPacketTip"));  // NOI18N
062        pane1.add(packetfilterButton);
063        packetfilterButton.addActionListener(this::openPacketFilterPerformed);
064        
065        paneA.add(pane1);
066        getContentPane().add(paneA);
067        pack();
068        paneA.setMaximumSize(paneA.getSize());
069        pack();
070        
071        // Move the filter packets button to the middle
072        getContentPane().setComponentZOrder(paneA,1);
073    }
074    
075    /**
076     * Define system-specific help item.
077     */
078    @Override
079    protected void setHelp() {
080        addHelpMenu("package.jmri.jmrix.cmri.serial.serialmon.SerialMonFrame", true);  // NOI18N
081    }
082
083    /**
084     * Initialize packet type filters.
085     */  
086    public void initializePacketFilters() {
087        // get all configured nodes
088        SerialNode node = (SerialNode) _memo.getTrafficController().getNode(0);
089        int index = 1,
090                pktTypeIndex = 0;
091
092        while (node != null) {
093            // Set all nodes and packet types to be monitored
094            //-----------------------------------------------
095            do {
096                node.setMonitorPacketBit(pktTypeIndex, true);
097                pktTypeIndex++;
098            } while (pktTypeIndex < SerialFilterFrame.numMonPkts);
099
100            node = (SerialNode) _memo.getTrafficController().getNode(index);
101            index++;
102            pktTypeIndex = 0;
103        }
104    }
105
106    /**
107     * Open the node/packet filter window.
108     * @param e unused.
109     */
110    public void openPacketFilterPerformed(ActionEvent e) {
111        // create a SerialFilterFrame
112        SerialFilterFrame f = new SerialFilterFrame(_memo);
113        try {
114            f.initComponents();
115        }
116        catch (Exception ex) {
117            log.warn("SerialMonFrame starting SerialFilterFrame: Exception: {}", ex.toString());
118        }
119        f.setVisible(true);
120    }
121
122    //-------------------
123    //  Transmit Packets
124    //-------------------
125    @Override
126    public synchronized void message(SerialMessage l) {
127
128        SerialNode monitorNode = (SerialNode) _memo.getTrafficController().getNodeFromAddress(l.getUA());
129        
130        // Test for node and packets being monitored
131        //------------------------------------------
132        if (monitorNode == null) return;       
133        if (!monitorNode.getMonitorNodePackets()) return;
134
135        int aPacketTypeID = l.getElement(1);
136        
137        // check for valid length
138        if (l.getNumDataElements() < 2) {
139            nextLine("Truncated message of length "+l.getNumDataElements()+"\n",l.toString());
140            return;
141        }
142        
143        switch(aPacketTypeID) {
144            case 0x50:        // (P) Poll
145                if(monitorNode.getMonitorPacketBit(SerialFilterFrame.monPktPoll))
146                {
147                    nextLine("Poll ua="+l.getUA()+"\n", l.toString());
148                }
149                break;
150
151            case 0x54:        // (T) Transmit
152                if (monitorNode.getMonitorPacketBit(SerialFilterFrame.monPktTransmit))
153                {
154                    StringBuilder sb = new StringBuilder("Transmit ua=");
155                    sb.append(l.getUA());
156                    sb.append(" OB=");
157                    for (int i=2; i<l.getNumDataElements(); i++)
158                    {
159                        if ((rawCheckBox.isSelected()) && ( l.getElement(i) == _DLE )) //c2
160                        {
161                            sb.append("<dle>");  // Convert DLE (0x10) to text
162                            i++;
163                        }
164
165                        sb.append(Integer.toHexString(l.getElement(i)&0x000000ff).toUpperCase());  //c2
166                        sb.append(" ");
167                    }
168                    sb.append("\n");
169                    nextLine(new String(sb), l.toString());
170                }
171                break;
172
173            case 0x49:        // (I) Initialize
174                if (monitorNode.getMonitorPacketBit(SerialFilterFrame.monPktInit)) {
175                    StringBuilder sb = new StringBuilder("Init ua=");
176                    sb.append(l.getUA());
177                    sb.append(" type=");
178                    int ndp=l.getElement(2); // ndp node type
179                    sb.append((char)ndp);
180                    int len = l.getNumDataElements();
181
182                    switch (ndp) {
183                        // SMINI/SUSIC/USIC
184                        case SerialNode.NDP_USICSUSIC24:
185                        case SerialNode.NDP_USICSUSIC32:
186                        case SerialNode.NDP_SMINI:
187                            if (len>=5)
188                            {
189                                sb.append(" DL=");
190                                sb.append(l.getElement(3)*256+l.getElement(4));
191                            }
192
193                            if (len>=6)
194                            {
195                                sb.append(" NS=");
196                                sb.append(l.getElement(5));
197                                sb.append(" CT: ");
198                                for (int i=6; i<l.getNumDataElements(); i++)
199                                {
200                                    sb.append(Integer.toHexString(l.getElement(i)&0x000000ff).toUpperCase()); //c2
201                                    sb.append(" ");
202                                }
203                            }
204                            break;
205
206                        case SerialNode.NDP_CPNODE: // CPNODE
207                        case SerialNode.NDP_CPMEGA: // CPMEGA
208                        case SerialNode.NDP_ESP32NODE: // ESP32Node -- same init payload layout as CPNODE/CPMEGA
209                            if (len>=5)
210                            {
211                                sb.append(" DL=");
212                                sb.append(l.getElement(3)*256+l.getElement(4));
213                            }
214                            sb.append(" Opts=");
215                            int i=5;
216                            while (i<l.getNumDataElements()) {
217                                if (l.getElement(i) != _DLE) { // skip DLE
218                                    sb.append(Integer.toHexString(l.getElement(i)&0x000000ff).toUpperCase()); //c2
219                                    sb.append(" ");
220                                }
221                                i++;
222                            }
223                            break;
224
225                        default:
226                            sb.append("Unrecognized node type NDP: [").append(ndp).append("] ");
227                            break;
228
229                        } //ndp case
230
231                    sb.append("\n");
232                    nextLine(new String(sb), l.toString());
233                }
234                break;
235
236            default:
237                nextLine("Unrecognized cmd: \""+l.toString()+"\"\n", "");
238        }  // end packet ID case
239    }
240
241    //-------------------
242    //  Receive Packets
243    //-------------------
244    @Override
245    public synchronized void reply(SerialReply l) {
246
247        // Test for node and packets being monitored 
248        //------------------------------------------
249        SerialNode monitorNode = (SerialNode) _memo.getTrafficController().getNodeFromAddress(l.getUA());
250
251        if (monitorNode == null) {
252            return;
253        }
254        if (!monitorNode.getMonitorNodePackets()) {
255            return;
256        }
257
258        int aPacketTypeID = l.getElement(1);
259
260        // check for valid length
261        if (l.getNumDataElements() < 2) {
262            nextLine("Truncated reply of length " + l.getNumDataElements() + "\n", l.toString());
263            // CMRInetMetricsData.incMetricErrValue( CMRInetMetricsData.CMRInetMetricTruncReply );
264            return;
265        }
266        switch (aPacketTypeID) {
267            case 0x52:  // (R) Receive (poll reply)
268                if (monitorNode.getMonitorPacketBit(SerialFilterFrame.monPktRead)) {
269                    StringBuilder sb = new StringBuilder("Receive ua=");
270                    sb.append(l.getUA());
271                    sb.append(" IB=");
272                    for (int i = 2; i < l.getNumDataElements(); i++) {
273                        if ((rawCheckBox.isSelected()) && (l.getElement(i) == _DLE)) //c2
274                        {
275                            sb.append("<dle>");
276                            i++;
277                        }
278                        sb.append(Integer.toHexString(l.getElement(i) & 0x000000ff).toUpperCase());  //c2
279                        sb.append(" ");
280                    }
281                    sb.append("\n");
282                    nextLine(new String(sb), l.toString());
283                }
284                break;
285
286            case 0x45:  // (E) EOT c2
287                if (monitorNode.getMonitorPacketBit(SerialFilterFrame.monPktEOT)) {
288                    StringBuilder sb = new StringBuilder("Receive ua=");
289                    sb.append(l.getUA());
290                    sb.append(" eot");
291                    sb.append("\n");
292                    nextLine(new String(sb), l.toString());
293                }
294                break;
295
296            default:
297                // CMRInetMetricsData.incMetricErrValue( CMRInetMetricsData.CMRInetMetricUnrecResponse );
298                nextLine("Unrecognized response: \"" + l.toString() + "\"\n", "");
299                break;
300        }
301    }
302
303    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SerialMonFrame.class);
304
305}
306