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) { 102 decodePane.dispose(); 103 } 104 displayPane.dispose(); 105 statsPane.dispose(); 106 super.dispose(); 107 } 108 109 /** 110 * {@inheritDoc} 111 */ 112 @Override 113 public void initComponents(CanSystemConnectionMemo memo) { 114 initComponents( memo, true); 115 } 116 117 /** 118 * Constructor For testing purposes, not for general use. 119 * @param memo System Connection 120 * @param launchEvTable true to launch a CBUS Event Table Model, else false. 121 */ 122 public void initComponents(CanSystemConnectionMemo memo, boolean launchEvTable) { 123 super.initComponents(memo); 124 tc = memo.getTrafficController(); 125 if ( tc == null ) { 126 throw new IllegalArgumentException("TC Should not be null for memo.getTrafficController() " + memo.getUserName() 127 + "" + memo.getTrafficController() + " on GUI Thread " + ThreadingUtil.isGUIThread()); 128 } 129 decodePane = new CbusConsoleDecodeOptionsPane(this); 130 logPane = new CbusConsoleLoggingPane(this); 131 if (launchEvTable){ 132 memo.get(CbusConfigurationManager.class).provide(CbusEventTableDataModel.class); 133 } 134 init(); 135 } 136 137 public void init() { 138 139 initTextAreas(); 140 141 // Sub-pane to hold buttons 142 JPanel paneA = new JPanel(); 143 paneA.setLayout(new BoxLayout(paneA, BoxLayout.Y_AXIS)); 144 paneA.add(getClearFreezeButtonPane()); 145 paneA.add(decodePane); 146 147 JPanel historyPane = new JPanel(); 148 historyPane.setLayout(new BorderLayout()); 149 historyPane.setBorder(BorderFactory.createTitledBorder( 150 BorderFactory.createEtchedBorder(), Bundle.getMessage("PacketHistoryTitle"))); 151 152 historyPane.add(getSplitPane(), BorderLayout.CENTER); 153 historyPane.add(paneA, BorderLayout.SOUTH); 154 155 setLayout(new BorderLayout()); 156 add(displayPane, BorderLayout.NORTH); 157 add(historyPane, BorderLayout.CENTER); 158 add(getAllBottomPanes(), BorderLayout.SOUTH); 159 displayPane.matchVisisbleToCheckBoxes(null); 160 161 } 162 163 private void initTextAreas() { 164 165 monTextPaneCan = new TextAreaFIFO(MAX_LINES); 166 monTextPaneCan.setVisible(true); 167 monTextPaneCan.setToolTipText(Bundle.getMessage("TooltipMonTextPaneCan")); 168 monTextPaneCan.setEditable(false); 169 monTextPaneCan.setRows(5); 170 monTextPaneCan.setColumns(5); 171 172 monTextPaneCbus = new TextAreaFIFO(MAX_LINES); 173 monTextPaneCbus.setVisible(true); 174 monTextPaneCbus.setToolTipText(Bundle.getMessage("TooltipMonTextPaneCbus")); 175 monTextPaneCbus.setEditable(false); 176 monTextPaneCbus.setRows(5); 177 monTextPaneCbus.setColumns(20); 178 179 cbusHighlighter = monTextPaneCbus.getHighlighter(); 180 canHighlighter = monTextPaneCan.getHighlighter(); 181 182 } 183 184 private JSplitPane getSplitPane(){ 185 186 JScrollPane jScrollPane1Can = new JScrollPane(); 187 jScrollPane1Can.getViewport().add(monTextPaneCan); 188 jScrollPane1Can.setVisible(true); 189 jScrollPane1Can.setBorder(BorderFactory.createTitledBorder( 190 BorderFactory.createEtchedBorder(), Bundle.getMessage("CanFrameTitle"))); 191 192 JScrollPane jScrollPane1Cbus = new JScrollPane(); 193 jScrollPane1Cbus.setBorder(BorderFactory.createTitledBorder( 194 BorderFactory.createEtchedBorder(), Bundle.getMessage("CbusMessageTitle"))); 195 jScrollPane1Cbus.getViewport().add(monTextPaneCbus); 196 jScrollPane1Cbus.setVisible(true); 197 198 jScrollPane1Can.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 199 jScrollPane1Cbus.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 200 jScrollPane1Can.setVerticalScrollBar(jScrollPane1Cbus.getVerticalScrollBar()); 201 202 // scroll panels to be side-by-side 203 JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, 204 jScrollPane1Can, jScrollPane1Cbus); 205 split.setResizeWeight(0.3); 206 split.setContinuousLayout(true); 207 208 return split; 209 } 210 211 private JPanel getClearFreezeButtonPane() { 212 213 JPanel messageButtonOptionpane = new JPanel(); 214 215 JButton clearButton = new JButton(); 216 freezeButton = new JToggleButton(); 217 218 clearButton.setText(Bundle.getMessage("ButtonClearScreen")); 219 clearButton.setToolTipText(Bundle.getMessage("ButtonClearLogTip")); 220 221 freezeButton.setText(Bundle.getMessage("ButtonFreezeScreen")); 222 freezeButton.setToolTipText(Bundle.getMessage("TooltipStopScroll")); 223 224 messageButtonOptionpane.setLayout(new BoxLayout(messageButtonOptionpane, BoxLayout.X_AXIS)); 225 messageButtonOptionpane.add(clearButton); 226 messageButtonOptionpane.add(freezeButton); 227 228 clearButton.addActionListener(this::clearButtonActionPerformed); 229 freezeButton.addActionListener(this::freezeButtonActionPerformed); 230 return messageButtonOptionpane; 231 232 } 233 234 private JPanel getAllBottomPanes() { 235 236 JPanel southPane = new JPanel(); 237 southPane.setLayout(new BoxLayout(southPane, BoxLayout.Y_AXIS)); 238 239 logPane.setVisible(false); 240 statsPane.setVisible(false); 241 packetPane.setVisible(false); 242 sendPane.setVisible(false); 243 244 southPane.add(logPane); 245 southPane.add(statsPane); 246 southPane.add(packetPane); 247 southPane.add(sendPane); 248 249 return southPane; 250 251 } 252 253 /** 254 * Handle display of traffic. 255 * @param line string the traffic in 'normal form', 256 * @param decoded string the decoded, protocol specific, form. 257 * Both should contain the same number of well-formed lines, e.g. end with \n 258 * @param highlight int 259 */ 260 public void nextLine(String line, String decoded, int highlight) { 261 262 logBuffer.add( new CbusConsoleLogEntry(line,decoded,highlight)); 263 264 // if not frozen, display it in the Swing thread 265 if (!freezeButton.isSelected()) { 266 ThreadingUtil.runOnGUIEventually( ()->{ 267 processLogBuffer(); 268 }); 269 } 270 271 // if requested, log to a file. 272 logPane.sendLogToFile( decoded ); 273 274 } 275 276 private void processLogBuffer() { 277 while (!logBuffer.isEmpty()){ 278 CbusConsoleLogEntry next = logBuffer.removeFirst(); 279 280 final int start = monTextPaneCbus.getText().length(); 281 final int startc= monTextPaneCan.getText().length(); 282 283 monTextPaneCan.append(next.getFrameText()); 284 monTextPaneCbus.append(next.getDecodedText()); 285 286 if (next.getHighlighter() > -1) { 287 try { 288 CbusHighlightPainter cbusHighlightPainter = new CbusHighlightPainter( 289 CbusEventHighlightFrame.highlightColors[next.getHighlighter()]); 290 // log.debug("Add highlight start: " + start + " end: " + end); 291 cbusHighlighter.addHighlight(start, monTextPaneCbus.getText().length() - 1, cbusHighlightPainter); 292 canHighlighter.addHighlight(startc, monTextPaneCan.getText().length() - 1, cbusHighlightPainter); 293 } catch (BadLocationException e) {} // do nothing 294 } 295 } 296 } 297 298 // clear the monitoring history 299 private void clearButtonActionPerformed(ActionEvent e) { 300 logBuffer.clear(); 301 monTextPaneCan.setText(""); 302 monTextPaneCbus.setText(""); 303 } 304 305 private void freezeButtonActionPerformed(ActionEvent e) { 306 if (freezeButton.isSelected()) { 307 freezeButton.setForeground(Color.red); 308 } else { 309 freezeButton.setForeground(new JTextField().getForeground()); // reset to default 310 nextLine("","",-1); // poke with zero content to refresh screen 311 } 312 } 313 314 // A private subclass of the default highlight painter 315 private class CbusHighlightPainter extends DefaultHighlighter.DefaultHighlightPainter { 316 protected CbusHighlightPainter(Color color) { 317 super(color); 318 } 319 } 320 321 protected String getMemoUserName(){ 322 return memo.getUserName(); 323 } 324 325 /** 326 * Nested class to create one of these using old-style defaults. 327 */ 328 public static class Default extends jmri.jmrix.can.swing.CanNamedPaneAction { 329 330 public Default() { 331 super(Bundle.getMessage("CbusConsoleTitle"), 332 new jmri.util.swing.sdi.JmriJFrameInterface(), 333 CbusConsolePane.class.getName(), 334 jmri.InstanceManager.getDefault(CanSystemConnectionMemo.class)); 335 } 336 } 337 338 // private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CbusConsolePane.class); 339}