001package jmri.jmrix.can.cbus.swing.console; 002 003import java.awt.BorderLayout; 004import java.awt.Color; 005import java.awt.event.ActionEvent; 006import java.util.concurrent.ConcurrentLinkedDeque; 007 008import javax.swing.*; 009import javax.swing.text.BadLocationException; 010import javax.swing.text.DefaultHighlighter; 011import javax.swing.text.Highlighter; 012 013import jmri.jmrix.can.CanSystemConnectionMemo; 014import jmri.jmrix.can.TrafficController; 015import jmri.jmrix.can.cbus.CbusConfigurationManager; 016import jmri.jmrix.can.cbus.eventtable.CbusEventTableDataModel; 017import jmri.jmrix.can.cbus.swing.CbusEventHighlightFrame; 018import jmri.jmrix.can.cbus.swing.CbusSendEventPane; 019import jmri.util.ThreadingUtil; 020import jmri.util.swing.TextAreaFIFO; 021 022/** 023 * Frame for CBUS Console 024 * 025 * @author Andrew Crosland Copyright (C) 2008 026 * @author Steve Young Copyright (C) 2018 027 */ 028public class CbusConsolePane extends jmri.jmrix.can.swing.CanPanel { 029 030 static int console_instance_num; 031 private static final int MAX_LINES = 5000; 032 033 private final ConcurrentLinkedDeque<CbusConsoleLogEntry> logBuffer; 034 035 private JToggleButton freezeButton; 036 037 public TextAreaFIFO monTextPaneCan; 038 public TextAreaFIFO monTextPaneCbus; 039 private Highlighter cbusHighlighter; 040 private Highlighter canHighlighter; 041 042 protected final CbusConsoleStatsPane statsPane; 043 protected final CbusConsolePacketPane packetPane; 044 protected final CbusSendEventPane sendPane; 045 protected CbusConsoleDecodeOptionsPane decodePane; 046 protected CbusConsoleLoggingPane logPane; 047 public final CbusConsoleDisplayOptionsPane displayPane; 048 049 // members for handling the CBUS interface 050 protected TrafficController tc; 051 052 public CbusConsolePane() { 053 super(); 054 incrementInstance(); 055 logBuffer = new ConcurrentLinkedDeque<>(); 056 statsPane = new CbusConsoleStatsPane(this); 057 packetPane = new CbusConsolePacketPane(this); 058 sendPane = new CbusSendEventPane(this); 059 displayPane = new CbusConsoleDisplayOptionsPane(this); 060 061 } 062 063 public static int getConsoleInstanceNum() { 064 return console_instance_num; 065 } 066 067 public static void incrementInstance() { 068 console_instance_num++; 069 } 070 071 /** 072 * {@inheritDoc} 073 */ 074 @Override 075 public String getTitle() { 076 if (memo != null) { 077 StringBuilder title = new StringBuilder(20); 078 title.append(memo.getUserName()).append(" "); 079 title.append(Bundle.getMessage("CbusConsoleTitle")); 080 if (getConsoleInstanceNum() > 1) { 081 title.append(" ").append( getConsoleInstanceNum() ); 082 } 083 return title.toString(); 084 } 085 return Bundle.getMessage("CbusConsoleTitle"); 086 } 087 088 /** 089 * {@inheritDoc} 090 */ 091 @Override 092 public String getHelpTarget() { 093 return "package.jmri.jmrix.can.cbus.swing.console.CbusConsoleFrame"; 094 } 095 096 /** 097 * {@inheritDoc} 098 */ 099 @Override 100 public void dispose() { 101 if (decodePane!=null) { // initComponents called 102 decodePane.dispose(); 103 } 104 if (logPane!=null) { // initComponents called 105 logPane.dispose(); 106 } 107 displayPane.dispose(); 108 statsPane.dispose(); 109 super.dispose(); 110 } 111 112 /** 113 * {@inheritDoc} 114 */ 115 @Override 116 public void initComponents(CanSystemConnectionMemo memo) { 117 initComponents( memo, true); 118 } 119 120 /** 121 * Constructor For testing purposes, not for general use. 122 * @param memo System Connection 123 * @param launchEvTable true to launch a CBUS Event Table Model, else false. 124 */ 125 public void initComponents(CanSystemConnectionMemo memo, boolean launchEvTable) { 126 super.initComponents(memo); 127 tc = memo.getTrafficController(); 128 if ( tc == null ) { 129 throw new IllegalArgumentException("TC Should not be null for memo.getTrafficController() " + memo.getUserName() 130 + "" + memo.getTrafficController() + " on GUI Thread " + ThreadingUtil.isGUIThread()); 131 } 132 decodePane = new CbusConsoleDecodeOptionsPane(this); 133 logPane = new CbusConsoleLoggingPane(this); 134 if (launchEvTable){ 135 memo.get(CbusConfigurationManager.class).provide(CbusEventTableDataModel.class); 136 } 137 init(); 138 } 139 140 public void init() { 141 142 initTextAreas(); 143 144 // Sub-pane to hold buttons 145 JPanel paneA = new JPanel(); 146 paneA.setLayout(new BoxLayout(paneA, BoxLayout.Y_AXIS)); 147 paneA.add(getClearFreezeButtonPane()); 148 paneA.add(decodePane); 149 150 JPanel historyPane = new JPanel(); 151 historyPane.setLayout(new BorderLayout()); 152 historyPane.setBorder(BorderFactory.createTitledBorder( 153 BorderFactory.createEtchedBorder(), Bundle.getMessage("PacketHistoryTitle"))); 154 155 historyPane.add(getSplitPane(), BorderLayout.CENTER); 156 historyPane.add(paneA, BorderLayout.SOUTH); 157 158 setLayout(new BorderLayout()); 159 add(displayPane, BorderLayout.NORTH); 160 add(historyPane, BorderLayout.CENTER); 161 add(getAllBottomPanes(), BorderLayout.SOUTH); 162 displayPane.matchVisisbleToCheckBoxes(null); 163 164 } 165 166 private void initTextAreas() { 167 168 monTextPaneCan = new TextAreaFIFO(MAX_LINES); 169 monTextPaneCan.setVisible(true); 170 monTextPaneCan.setToolTipText(Bundle.getMessage("TooltipMonTextPaneCan")); 171 monTextPaneCan.setEditable(false); 172 monTextPaneCan.setRows(5); 173 monTextPaneCan.setColumns(5); 174 175 monTextPaneCbus = new TextAreaFIFO(MAX_LINES); 176 monTextPaneCbus.setVisible(true); 177 monTextPaneCbus.setToolTipText(Bundle.getMessage("TooltipMonTextPaneCbus")); 178 monTextPaneCbus.setEditable(false); 179 monTextPaneCbus.setRows(5); 180 monTextPaneCbus.setColumns(20); 181 182 cbusHighlighter = monTextPaneCbus.getHighlighter(); 183 canHighlighter = monTextPaneCan.getHighlighter(); 184 185 } 186 187 private JSplitPane getSplitPane(){ 188 189 JScrollPane jScrollPane1Can = new JScrollPane(); 190 jScrollPane1Can.getViewport().add(monTextPaneCan); 191 jScrollPane1Can.setVisible(true); 192 jScrollPane1Can.setBorder(BorderFactory.createTitledBorder( 193 BorderFactory.createEtchedBorder(), Bundle.getMessage("CanFrameTitle"))); 194 195 JScrollPane jScrollPane1Cbus = new JScrollPane(); 196 jScrollPane1Cbus.setBorder(BorderFactory.createTitledBorder( 197 BorderFactory.createEtchedBorder(), Bundle.getMessage("CbusMessageTitle"))); 198 jScrollPane1Cbus.getViewport().add(monTextPaneCbus); 199 jScrollPane1Cbus.setVisible(true); 200 201 jScrollPane1Can.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 202 jScrollPane1Cbus.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 203 jScrollPane1Can.setVerticalScrollBar(jScrollPane1Cbus.getVerticalScrollBar()); 204 205 // scroll panels to be side-by-side 206 JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, 207 jScrollPane1Can, jScrollPane1Cbus); 208 split.setResizeWeight(0.3); 209 split.setContinuousLayout(true); 210 211 return split; 212 } 213 214 private JPanel getClearFreezeButtonPane() { 215 216 JPanel messageButtonOptionpane = new JPanel(); 217 218 JButton clearButton = new JButton(); 219 freezeButton = new JToggleButton(); 220 221 clearButton.setText(Bundle.getMessage("ButtonClearScreen")); 222 clearButton.setToolTipText(Bundle.getMessage("ButtonClearLogTip")); 223 224 freezeButton.setText(Bundle.getMessage("ButtonFreezeScreen")); 225 freezeButton.setToolTipText(Bundle.getMessage("TooltipStopScroll")); 226 227 messageButtonOptionpane.setLayout(new BoxLayout(messageButtonOptionpane, BoxLayout.X_AXIS)); 228 messageButtonOptionpane.add(clearButton); 229 messageButtonOptionpane.add(freezeButton); 230 231 clearButton.addActionListener(this::clearButtonActionPerformed); 232 freezeButton.addActionListener(this::freezeButtonActionPerformed); 233 return messageButtonOptionpane; 234 235 } 236 237 private JPanel getAllBottomPanes() { 238 239 JPanel southPane = new JPanel(); 240 southPane.setLayout(new BoxLayout(southPane, BoxLayout.Y_AXIS)); 241 242 logPane.setVisible(false); 243 statsPane.setVisible(false); 244 packetPane.setVisible(false); 245 sendPane.setVisible(false); 246 247 southPane.add(logPane); 248 southPane.add(statsPane); 249 southPane.add(packetPane); 250 southPane.add(sendPane); 251 252 return southPane; 253 254 } 255 256 /** 257 * Handle display of traffic. 258 * @param line string the traffic in 'normal form', 259 * @param decoded string the decoded, protocol specific, form. 260 * Both should contain the same number of well-formed lines, e.g. end with \n 261 * @param highlight int 262 */ 263 public void nextLine(String line, String decoded, int highlight) { 264 265 logBuffer.add( new CbusConsoleLogEntry(line,decoded,highlight)); 266 267 // if not frozen, display it in the Swing thread 268 if (!freezeButton.isSelected()) { 269 ThreadingUtil.runOnGUIEventually( ()->{ 270 processLogBuffer(); 271 }); 272 } 273 274 // if requested, log to a file. 275 logPane.sendLogToFile( decoded ); 276 277 } 278 279 private void processLogBuffer() { 280 while (!logBuffer.isEmpty()){ 281 CbusConsoleLogEntry next = logBuffer.removeFirst(); 282 283 final int start = monTextPaneCbus.getText().length(); 284 final int startc= monTextPaneCan.getText().length(); 285 286 monTextPaneCan.append(next.getFrameText()); 287 monTextPaneCbus.append(next.getDecodedText()); 288 289 if (next.getHighlighter() > -1) { 290 try { 291 CbusHighlightPainter cbusHighlightPainter = new CbusHighlightPainter( 292 CbusEventHighlightFrame.highlightColors[next.getHighlighter()]); 293 // log.debug("Add highlight start: " + start + " end: " + end); 294 cbusHighlighter.addHighlight(start, monTextPaneCbus.getText().length() - 1, cbusHighlightPainter); 295 canHighlighter.addHighlight(startc, monTextPaneCan.getText().length() - 1, cbusHighlightPainter); 296 } catch (BadLocationException e) {} // do nothing 297 } 298 } 299 } 300 301 // clear the monitoring history 302 private void clearButtonActionPerformed(ActionEvent e) { 303 logBuffer.clear(); 304 monTextPaneCan.setText(""); 305 monTextPaneCbus.setText(""); 306 } 307 308 private void freezeButtonActionPerformed(ActionEvent e) { 309 if (freezeButton.isSelected()) { 310 freezeButton.setForeground(Color.red); 311 } else { 312 freezeButton.setForeground(new JTextField().getForeground()); // reset to default 313 nextLine("","",-1); // poke with zero content to refresh screen 314 } 315 } 316 317 // A private subclass of the default highlight painter 318 private class CbusHighlightPainter extends DefaultHighlighter.DefaultHighlightPainter { 319 protected CbusHighlightPainter(Color color) { 320 super(color); 321 } 322 } 323 324 /** 325 * Nested class to create one of these using old-style defaults. 326 */ 327 public static class Default extends jmri.jmrix.can.swing.CanNamedPaneAction { 328 329 public Default() { 330 super(Bundle.getMessage("CbusConsoleTitle"), 331 new jmri.util.swing.sdi.JmriJFrameInterface(), 332 CbusConsolePane.class.getName(), 333 jmri.InstanceManager.getDefault(CanSystemConnectionMemo.class)); 334 } 335 } 336 337 // private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CbusConsolePane.class); 338}