/** * The Dialog Class * * @author Frank Kunz The dialog class draws the basic dialog with a grid layout. The dialog * consists of three main parts. A settings panel, a table panel and three buttons. */ public final class UARTProtocolAnalysisDialog extends BaseToolDialog<UARTDataSet> implements ExportAware<UARTDataSet> { // INNER TYPES /** Provides a combobox renderer for {@link UARTParity} values. */ static final class UARTParityItemRenderer extends EnumItemRenderer<Parity> { // CONSTANTS private static final long serialVersionUID = 1L; // METHODS /** * @see * nl.lxtreme.ols.client.diagram.settings.GeneralSettingsDialog.EnumItemRenderer#getDisplayValue(java.lang.Enum) */ @Override protected String getDisplayValue(final Parity aValue) { String text = super.getDisplayValue(aValue); if (Parity.EVEN.equals(aValue)) { text = "Even parity"; } else if (Parity.NONE.equals(aValue)) { text = "No parity"; } else if (Parity.ODD.equals(aValue)) { text = "Odd parity"; } return text; } } /** Provides a combobox renderer for {@link UARTStopBits} values. */ static final class UARTStopBitsItemRenderer extends EnumItemRenderer<StopBits> { // CONSTANTS private static final long serialVersionUID = 1L; // METHODS /** * @see * nl.lxtreme.ols.client.diagram.settings.GeneralSettingsDialog.EnumItemRenderer#getDisplayValue(java.lang.Enum) */ @Override protected String getDisplayValue(final StopBits aValue) { String text = super.getDisplayValue(aValue); if (StopBits.ONE.equals(aValue)) { text = "1"; } else if (StopBits.ONE_HALF.equals(aValue)) { text = "1.5"; } else if (StopBits.TWO.equals(aValue)) { text = "2"; } return text; } } /** Provides a combobox renderer for {@link BitOrder} values. */ static final class UARTBitOrderItemRenderer extends EnumItemRenderer<BitOrder> { // CONSTANTS private static final long serialVersionUID = 1L; // METHODS /** * @see * nl.lxtreme.ols.client.diagram.settings.GeneralSettingsDialog.EnumItemRenderer#getDisplayValue(java.lang.Enum) */ @Override protected String getDisplayValue(final BitOrder aValue) { String text = super.getDisplayValue(aValue); if (BitOrder.LSB_FIRST.equals(aValue)) { text = "LSB first"; } else if (BitOrder.MSB_FIRST.equals(aValue)) { text = "MSB first"; } return text; } } /** Provides a combobox renderer for {@link BitEncoding} values. */ static final class UARTBitEncodingItemRenderer extends EnumItemRenderer<BitEncoding> { // CONSTANTS private static final long serialVersionUID = 1L; // METHODS /** * @see * nl.lxtreme.ols.client.diagram.settings.GeneralSettingsDialog.EnumItemRenderer#getDisplayValue(java.lang.Enum) */ @Override protected String getDisplayValue(final BitEncoding aValue) { String text = super.getDisplayValue(aValue); if (BitEncoding.HIGH_IS_MARK.equals(aValue)) { text = "High is mark (1)"; } else if (BitEncoding.HIGH_IS_SPACE.equals(aValue)) { text = "High is space (0)"; } return text; } } /** Provides a combobox renderer for {@link BitLevel} values. */ static final class UARTIdleLevelItemRenderer extends EnumItemRenderer<BitLevel> { // CONSTANTS private static final long serialVersionUID = 1L; // METHODS /** * @see * nl.lxtreme.ols.client.diagram.settings.GeneralSettingsDialog.EnumItemRenderer#getDisplayValue(java.lang.Enum) */ @Override protected String getDisplayValue(final BitLevel aValue) { String text = super.getDisplayValue(aValue); if (BitLevel.HIGH.equals(aValue)) { text = "High (start = L, stop = H)"; } else if (BitLevel.LOW.equals(aValue)) { text = "Low (start = H, stop = L)"; } return text; } } // CONSTANTS private static final long serialVersionUID = 1L; private static final Logger LOG = Logger.getLogger(UARTProtocolAnalysisDialog.class.getName()); // VARIABLES private JComboBox rxd; private JComboBox txd; private JComboBox cts; private JComboBox rts; private JComboBox dtr; private JComboBox dsr; private JComboBox dcd; private JComboBox ri; private JComboBox parity; private JComboBox bits; private JComboBox stop; private JComboBox bitEncoding; private JComboBox bitOrder; private JComboBox idleLevel; private JCheckBox autoDetectBaudRate; private JComboBox baudrate; private JEditorPane outText; private RestorableAction runAnalysisAction; private Action closeAction; private Action exportAction; // CONSTRUCTORS /** * Creates a new UARTProtocolAnalysisDialog instance. * * @param aOwner the owner of this dialog; * @param aToolContext the tool context; * @param aContext the OSGi bundle context to use; * @param aTool the {@link UARTAnalyser} tool. */ public UARTProtocolAnalysisDialog( final Window aOwner, final ToolContext aToolContext, final BundleContext aContext, final UARTAnalyser aTool) { super(aOwner, aToolContext, aContext, aTool); initDialog(); setLocationRelativeTo(getOwner()); } // METHODS /** {@inheritDoc} */ @Override public void exportToFile( final File aOutputFile, final nl.lxtreme.ols.tool.base.ExportAware.ExportFormat aFormat) throws IOException { if (ExportFormat.HTML.equals(aFormat)) { storeToHtmlFile(aOutputFile, getLastResult()); } else if (ExportFormat.CSV.equals(aFormat)) { storeToCsvFile(aOutputFile, getLastResult()); } } /** {@inheritDoc} */ @Override public void readPreferences(final UserSettings aSettings) { // Issue #114: avoid setting illegal values... setComboBoxIndex(this.rxd, aSettings, "rxd"); setComboBoxIndex(this.txd, aSettings, "txd"); setComboBoxIndex(this.cts, aSettings, "cts"); setComboBoxIndex(this.rts, aSettings, "rts"); setComboBoxIndex(this.dtr, aSettings, "dtr"); setComboBoxIndex(this.dsr, aSettings, "dsr"); setComboBoxIndex(this.dcd, aSettings, "dcd"); setComboBoxIndex(this.ri, aSettings, "ri"); this.parity.setSelectedIndex(aSettings.getInt("parity", this.parity.getSelectedIndex())); this.bits.setSelectedIndex(aSettings.getInt("bits", this.bits.getSelectedIndex())); this.stop.setSelectedIndex(aSettings.getInt("stop", this.stop.getSelectedIndex())); this.idleLevel.setSelectedIndex( aSettings.getInt("idle-state", this.idleLevel.getSelectedIndex())); this.bitEncoding.setSelectedIndex( aSettings.getInt("bit-encoding", this.bitEncoding.getSelectedIndex())); this.bitOrder.setSelectedIndex(aSettings.getInt("bit-order", this.bitOrder.getSelectedIndex())); this.baudrate.setSelectedItem(Integer.valueOf(aSettings.getInt("baudrate", 9600))); this.autoDetectBaudRate.setSelected( aSettings.getBoolean("auto-baudrate", this.autoDetectBaudRate.isSelected())); } /** {@inheritDoc} */ @Override public void reset() { this.outText.setText(getEmptyHtmlPage()); this.outText.setEditable(false); this.runAnalysisAction.restore(); setControlsEnabled(true); this.exportAction.setEnabled(false); } /** @see nl.lxtreme.ols.api.Configurable#writePreferences(nl.lxtreme.ols.api.UserSettings) */ @Override public void writePreferences(final UserSettings aSettings) { aSettings.putInt("rxd", this.rxd.getSelectedIndex()); aSettings.putInt("txd", this.txd.getSelectedIndex()); aSettings.putInt("cts", this.cts.getSelectedIndex()); aSettings.putInt("rts", this.rts.getSelectedIndex()); aSettings.putInt("dtr", this.dtr.getSelectedIndex()); aSettings.putInt("dsr", this.dsr.getSelectedIndex()); aSettings.putInt("dcd", this.dcd.getSelectedIndex()); aSettings.putInt("ri", this.ri.getSelectedIndex()); aSettings.putInt("parity", this.parity.getSelectedIndex()); aSettings.putInt("bits", this.bits.getSelectedIndex()); aSettings.putInt("stop", this.stop.getSelectedIndex()); aSettings.putInt("idle-state", this.idleLevel.getSelectedIndex()); aSettings.putInt("bit-encoding", this.bitEncoding.getSelectedIndex()); aSettings.putInt("bit-order", this.bitOrder.getSelectedIndex()); aSettings.putInt("baudrate", ((Integer) this.baudrate.getSelectedItem()).intValue()); aSettings.putBoolean("auto-baudrate", this.autoDetectBaudRate.isSelected()); } /** {@inheritDoc} */ @Override protected void onToolEnded(final UARTDataSet aAnalysisResult) { try { final String htmlPage; if (aAnalysisResult != null) { htmlPage = toHtmlPage(null /* aFile */, aAnalysisResult); } else { htmlPage = getEmptyHtmlPage(); } this.outText.setText(htmlPage); this.outText.setEditable(false); this.runAnalysisAction.restore(); } catch (final IOException exception) { // Make sure to handle IO-interrupted exceptions properly! if (!HostUtils.handleInterruptedException(exception)) { // Should not happen in this situation! throw new RuntimeException(exception); } } } /** {@inheritDoc} */ @Override protected void onToolStarted() { // NO-op } /** {@inheritDoc} */ @Override protected void prepareToolTask(final ToolTask<UARTDataSet> aToolTask) { final UARTAnalyserTask toolTask = (UARTAnalyserTask) aToolTask; // The value at index zero is "Unused", so extracting one of all items // causes all "unused" values to be equivalent to -1, which is interpreted // as not used... toolTask.setRxdIndex(this.rxd.getSelectedIndex() - 1); toolTask.setTxdIndex(this.txd.getSelectedIndex() - 1); toolTask.setCtsIndex(this.cts.getSelectedIndex() - 1); toolTask.setRtsIndex(this.rts.getSelectedIndex() - 1); toolTask.setDcdIndex(this.dcd.getSelectedIndex() - 1); toolTask.setRiIndex(this.ri.getSelectedIndex() - 1); toolTask.setDsrIndex(this.dsr.getSelectedIndex() - 1); toolTask.setDtrIndex(this.dtr.getSelectedIndex() - 1); // Handle the auto detect option for baudrates... if (this.autoDetectBaudRate.isSelected()) { toolTask.setBaudRate(UARTAnalyserTask.AUTO_DETECT_BAUDRATE); } else { toolTask.setBaudRate(((Integer) this.baudrate.getSelectedItem()).intValue()); } // Other properties... toolTask.setIdleLevel((BitLevel) this.idleLevel.getSelectedItem()); toolTask.setBitEncoding((BitEncoding) this.bitEncoding.getSelectedItem()); toolTask.setBitOrder((BitOrder) this.bitOrder.getSelectedItem()); toolTask.setParity((Parity) this.parity.getSelectedItem()); toolTask.setStopBits((StopBits) this.stop.getSelectedItem()); toolTask.setBitCount(NumberUtils.smartParseInt((String) this.bits.getSelectedItem(), 8)); } /** * set the controls of the dialog enabled/disabled * * @param aEnable status of the controls */ @Override protected void setControlsEnabled(final boolean aEnable) { this.rxd.setEnabled(aEnable); this.txd.setEnabled(aEnable); this.cts.setEnabled(aEnable); this.rts.setEnabled(aEnable); this.dtr.setEnabled(aEnable); this.dsr.setEnabled(aEnable); this.dcd.setEnabled(aEnable); this.ri.setEnabled(aEnable); this.parity.setEnabled(aEnable); this.bits.setEnabled(aEnable); this.stop.setEnabled(aEnable); this.idleLevel.setEnabled(aEnable); this.bitEncoding.setEnabled(aEnable); this.bitOrder.setEnabled(aEnable); this.closeAction.setEnabled(aEnable); this.exportAction.setEnabled(aEnable); } /** * Creates the HTML template for exports to HTML. * * @param aExporter the HTML exporter instance to use, cannot be <code>null</code>. * @return a HTML exporter filled with the template, never <code>null</code>. */ private HtmlExporter createHtmlTemplate(final HtmlExporter aExporter) { aExporter.addCssStyle("body { font-family: sans-serif; } "); aExporter.addCssStyle( "table { border-width: 1px; border-spacing: 0px; border-color: gray;" + " border-collapse: collapse; border-style: solid; margin-bottom: 15px; } "); aExporter.addCssStyle( "table th { border-width: 1px; padding: 2px; border-style: solid; border-color: gray;" + " background-color: #C0C0FF; text-align: left; font-weight: bold; font-family: sans-serif; } "); aExporter.addCssStyle( "table td { border-width: 1px; padding: 2px; border-style: solid; border-color: gray;" + " font-family: monospace; } "); aExporter.addCssStyle(".error { color: red; } "); aExporter.addCssStyle(".warning { color: orange; } "); aExporter.addCssStyle(".date { text-align: right; font-size: x-small; margin-bottom: 15px; } "); aExporter.addCssStyle(".w100 { width: 100%; } "); aExporter.addCssStyle(".w35 { width: 35%; } "); aExporter.addCssStyle(".w30 { width: 30%; } "); aExporter.addCssStyle(".w15 { width: 15%; } "); aExporter.addCssStyle(".w10 { width: 10%; } "); aExporter.addCssStyle(".w8 { width: 8%; } "); aExporter.addCssStyle(".w7 { width: 7%; } "); final Element body = aExporter.getBody(); body.addChild(H1).addContent("UART Analysis results"); body.addChild(HR); body.addChild(DIV).addAttribute("class", "date").addContent("Generated: ", "{date-now}"); Element table, tr, thead, tbody; table = body.addChild(TABLE).addAttribute("class", "w100"); tbody = table.addChild(TBODY); tr = tbody.addChild(TR); tr.addChild(TH).addAttribute("colspan", "2").addContent("Statistics"); tr = tbody.addChild(TR); tr.addChild(TD).addAttribute("class", "w30").addContent("Decoded bytes"); tr.addChild(TD).addContent("{decoded-bytes}"); tr = tbody.addChild(TR); tr.addChild(TD).addAttribute("class", "w30").addContent("Detected bus errors"); tr.addChild(TD).addContent("{detected-bus-errors}"); tr = tbody.addChild(TR); tr.addChild(TD).addAttribute("class", "w30").addContent("Baudrate"); tr.addChild(TD).addContent("{baudrate}"); table = body.addChild(TABLE).addAttribute("class", "w100"); thead = table.addChild(THEAD); tr = thead.addChild(TR); tr.addChild(TH).addAttribute("class", "w30").addAttribute("colspan", "2"); tr.addChild(TH).addAttribute("class", "w35").addAttribute("colspan", "4").addContent("RxD"); tr.addChild(TH).addAttribute("class", "w35").addAttribute("colspan", "4").addContent("TxD"); tr = thead.addChild(TR); tr.addChild(TH).addAttribute("class", "w15").addContent("Index"); tr.addChild(TH).addAttribute("class", "w15").addContent("Time"); tr.addChild(TH).addAttribute("class", "w10").addContent("Hex"); tr.addChild(TH).addAttribute("class", "w10").addContent("Bin"); tr.addChild(TH).addAttribute("class", "w8").addContent("Dec"); tr.addChild(TH).addAttribute("class", "w7").addContent("ASCII"); tr.addChild(TH).addAttribute("class", "w10").addContent("Hex"); tr.addChild(TH).addAttribute("class", "w10").addContent("Bin"); tr.addChild(TH).addAttribute("class", "w8").addContent("Dec"); tr.addChild(TH).addAttribute("class", "w7").addContent("ASCII"); tbody = table.addChild(TBODY); tbody.addContent("{decoded-data}"); return aExporter; } /** @return */ private JPanel createPreviewPane() { final JPanel panTable = new JPanel(new GridLayout(1, 1, 0, 0)); this.outText = new JEditorPane("text/html", getEmptyHtmlPage()); this.outText.setEditable(false); panTable.add(new JScrollPane(this.outText)); return panTable; } /** @return */ private JPanel createSettingsPane() { final int channelCount = getData().getChannels(); final Integer[] baudrates = new Integer[AsyncSerialDataDecoder.COMMON_BAUDRATES.length]; for (int i = 0; i < baudrates.length; i++) { baudrates[i] = Integer.valueOf(AsyncSerialDataDecoder.COMMON_BAUDRATES[i]); } final String[] bitarray = new String[10]; // allow symbol lengths between 5 and 14 bits... for (int i = 0; i < bitarray.length; i++) { bitarray[i] = String.format("%d", Integer.valueOf(i + 5)); } final JPanel settings = new JPanel(new SpringLayout()); SpringLayoutUtils.addSeparator(settings, "Settings"); settings.add(createRightAlignedLabel("RxD")); this.rxd = SwingComponentUtils.createOptionalChannelSelector(channelCount); settings.add(this.rxd); settings.add(createRightAlignedLabel("TxD")); this.txd = SwingComponentUtils.createOptionalChannelSelector(channelCount); settings.add(this.txd); settings.add(createRightAlignedLabel("CTS")); this.cts = SwingComponentUtils.createOptionalChannelSelector(channelCount); settings.add(this.cts); settings.add(createRightAlignedLabel("RTS")); this.rts = SwingComponentUtils.createOptionalChannelSelector(channelCount); settings.add(this.rts); settings.add(createRightAlignedLabel("DTR")); this.dtr = SwingComponentUtils.createOptionalChannelSelector(channelCount); settings.add(this.dtr); settings.add(createRightAlignedLabel("DSR")); this.dsr = SwingComponentUtils.createOptionalChannelSelector(channelCount); settings.add(this.dsr); settings.add(createRightAlignedLabel("DCD")); this.dcd = SwingComponentUtils.createOptionalChannelSelector(channelCount); settings.add(this.dcd); settings.add(createRightAlignedLabel("RI")); this.ri = SwingComponentUtils.createOptionalChannelSelector(channelCount); settings.add(this.ri); settings.add(createRightAlignedLabel("Baudrate")); this.autoDetectBaudRate = new JCheckBox("Auto detect"); settings.add(this.autoDetectBaudRate); settings.add(new JLabel("")); this.baudrate = new JComboBox(baudrates); // Issue #90: allow custom baudrates to be specified... this.baudrate.setEditable(true); this.baudrate.setSelectedIndex(0); settings.add(this.baudrate); this.autoDetectBaudRate.addItemListener( new ItemListener() { @Override public void itemStateChanged(final ItemEvent aEvent) { final JCheckBox cb = (JCheckBox) aEvent.getSource(); UARTProtocolAnalysisDialog.this.baudrate.setEnabled(!cb.isSelected()); } }); settings.add(createRightAlignedLabel("Parity")); this.parity = new JComboBox(Parity.values()); this.parity.setSelectedIndex(0); this.parity.setRenderer(new UARTParityItemRenderer()); settings.add(this.parity); settings.add(createRightAlignedLabel("Bits")); this.bits = new JComboBox(bitarray); this.bits.setSelectedIndex(3); settings.add(this.bits); settings.add(createRightAlignedLabel("Stopbits")); this.stop = new JComboBox(StopBits.values()); this.stop.setSelectedIndex(0); this.stop.setRenderer(new UARTStopBitsItemRenderer()); settings.add(this.stop); settings.add(createRightAlignedLabel("Idle level")); this.idleLevel = new JComboBox(BitLevel.values()); this.idleLevel.setSelectedIndex(0); this.idleLevel.setRenderer(new UARTIdleLevelItemRenderer()); settings.add(this.idleLevel); settings.add(createRightAlignedLabel("Bit encoding")); this.bitEncoding = new JComboBox(BitEncoding.values()); this.bitEncoding.setSelectedIndex(0); this.bitEncoding.setRenderer(new UARTBitEncodingItemRenderer()); settings.add(this.bitEncoding); settings.add(createRightAlignedLabel("Bit order")); this.bitOrder = new JComboBox(BitOrder.values()); this.bitOrder.setSelectedIndex(0); this.bitOrder.setRenderer(new UARTBitOrderItemRenderer()); settings.add(this.bitOrder); SpringLayoutUtils.makeEditorGrid(settings, 10, 4); return settings; } /** * generate a HTML page * * @param empty if this is true an empty output is generated * @return String with HTML data */ private String getEmptyHtmlPage() { final HtmlExporter exporter = createHtmlTemplate(ExportUtils.createHtmlExporter()); return exporter.toString( new MacroResolver() { @Override public Object resolve(final String aMacro, final Element aParent) { if ("date-now".equals(aMacro)) { final DateFormat df = DateFormat.getDateInstance(DateFormat.LONG); return df.format(new Date()); } else if ("decoded-bytes".equals(aMacro) || "detected-bus-errors".equals(aMacro) || "baudrate".equals(aMacro)) { return "-"; } else if ("decoded-data".equals(aMacro)) { return null; } return null; } }); } /** Initializes this dialog. */ private void initDialog() { setMinimumSize(new Dimension(640, 480)); final JComponent settingsPane = createSettingsPane(); final JComponent previewPane = createPreviewPane(); final JPanel contentPane = new JPanel(new GridBagLayout()); contentPane.add( settingsPane, new GridBagConstraints( 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.NONE, new Insets(2, 0, 2, 0), 0, 0)); contentPane.add( previewPane, new GridBagConstraints( 1, 0, 1, 1, 1.0, 1.0, GridBagConstraints.NORTH, GridBagConstraints.BOTH, new Insets(2, 0, 2, 0), 0, 0)); final JButton runAnalysisButton = ToolUtils.createRunAnalysisButton(this); this.runAnalysisAction = (RestorableAction) runAnalysisButton.getAction(); final JButton exportButton = ToolUtils.createExportButton(this); this.exportAction = exportButton.getAction(); this.exportAction.setEnabled(false); final JButton closeButton = ToolUtils.createCloseButton(); this.closeAction = closeButton.getAction(); final JComponent buttons = SwingComponentUtils.createButtonPane(runAnalysisButton, exportButton, closeButton); SwingComponentUtils.setupWindowContentPane(this, contentPane, buttons, runAnalysisButton); } /** * exports the data to a CSV file * * @param aFile File object */ private void storeToCsvFile(final File aFile, final UARTDataSet aDataSet) { try { final CsvExporter exporter = ExportUtils.createCsvExporter(aFile); exporter.setHeaders( "index", "start-time", "end-time", "event?", "event-type", "RxD event", "TxD event", "RxD data", "TxD data"); final List<UARTData> decodedData = aDataSet.getData(); for (int i = 0; i < decodedData.size(); i++) { final UARTData ds = decodedData.get(i); final String startTime = Unit.Time.format(aDataSet.getTime(ds.getStartSampleIndex())); final String endTime = Unit.Time.format(aDataSet.getTime(ds.getEndSampleIndex())); String eventType = null; String rxdEvent = null; String txdEvent = null; String rxdData = null; String txdData = null; switch (ds.getType()) { case UARTData.UART_TYPE_EVENT: eventType = ds.getEventName(); break; case UARTData.UART_TYPE_RXEVENT: rxdEvent = ds.getEventName(); break; case UARTData.UART_TYPE_TXEVENT: txdEvent = ds.getEventName(); break; case UARTData.UART_TYPE_RXDATA: rxdData = Integer.toString(ds.getData()); break; case UARTData.UART_TYPE_TXDATA: txdData = Integer.toString(ds.getData()); break; default: break; } exporter.addRow( Integer.valueOf(i), startTime, endTime, Boolean.valueOf(ds.isEvent()), eventType, rxdEvent, txdEvent, rxdData, txdData); } exporter.close(); } catch (final IOException exception) { // Make sure to handle IO-interrupted exceptions properly! if (!HostUtils.handleInterruptedException(exception)) { LOG.log(Level.WARNING, "CSV export failed!", exception); } } } /** * stores the data to a HTML file * * @param aFile file object */ private void storeToHtmlFile(final File aFile, final UARTDataSet aDataSet) { try { toHtmlPage(aFile, aDataSet); } catch (final IOException exception) { // Make sure to handle IO-interrupted exceptions properly! if (!HostUtils.handleInterruptedException(exception)) { LOG.log(Level.WARNING, "HTML export failed!", exception); } } } /** * generate a HTML page * * @param empty if this is true an empty output is generated * @return String with HTML data */ private String toHtmlPage(final File aFile, final UARTDataSet aDataSet) throws IOException { final int bitCount = Integer.parseInt((String) this.bits.getSelectedItem()); final int bitAdder = ((bitCount % 4) != 0) ? 1 : 0; final MacroResolver macroResolver = new MacroResolver() { @Override public Object resolve(final String aMacro, final Element aParent) { if ("date-now".equals(aMacro)) { final DateFormat df = DateFormat.getDateInstance(DateFormat.LONG); return df.format(new Date()); } else if ("decoded-bytes".equals(aMacro)) { return Integer.valueOf(aDataSet.getDecodedSymbols()); } else if ("detected-bus-errors".equals(aMacro)) { return Integer.valueOf(aDataSet.getDetectedErrors()); } else if ("baudrate".equals(aMacro)) { final String baudrate; if (aDataSet.getBaudRate() <= 0) { baudrate = "<span class='error'>Baudrate calculation failed!</span>"; } else { baudrate = String.format( "%d (exact: %d)", Integer.valueOf(aDataSet.getBaudRate()), Integer.valueOf(aDataSet.getBaudRateExact())); if (!aDataSet.isBitLengthUsable()) { return baudrate.concat( " <span class='warning'>The baudrate may be wrong, use a higher samplerate to avoid this!</span>"); } return baudrate; } } else if ("decoded-data".equals(aMacro)) { final List<UARTData> decodedData = aDataSet.getData(); Element tr; for (int i = 0; i < decodedData.size(); i++) { final UARTData ds = decodedData.get(i); if (ds.isEvent()) { String rxEventData = ""; String txEventData = ""; String bgColor; if (UARTData.UART_TYPE_EVENT == ds.getType()) { rxEventData = txEventData = ds.getEventName(); bgColor = "#e0e0e0"; } else if (UARTData.UART_TYPE_RXEVENT == ds.getType()) { rxEventData = ds.getEventName(); bgColor = "#c0ffc0"; } else if (UARTData.UART_TYPE_TXEVENT == ds.getType()) { txEventData = ds.getEventName(); bgColor = "#c0ffc0"; } else { // unknown event bgColor = "#ff8000"; } if (txEventData.endsWith("_ERR") || rxEventData.endsWith("_ERR")) { bgColor = "#ff8000"; } tr = aParent .addChild(TR) .addAttribute("style", "background-color: " + bgColor + ";"); tr.addChild(TD).addContent(String.valueOf(i)); tr.addChild(TD) .addContent(Unit.Time.format(aDataSet.getTime(ds.getStartSampleIndex()))); tr.addChild(TD).addContent(rxEventData); tr.addChild(TD); tr.addChild(TD); tr.addChild(TD); tr.addChild(TD).addContent(txEventData); tr.addChild(TD); tr.addChild(TD); tr.addChild(TD); } else { String rxDataHex = "", rxDataBin = "", rxDataDec = "", rxDataASCII = ""; String txDataHex = "", txDataBin = "", txDataDec = "", txDataASCII = ""; // Normal data... if (UARTData.UART_TYPE_RXDATA == ds.getType()) { final int rxData = ds.getData(); rxDataHex = integerToHexString(rxData, (bitCount / 4) + bitAdder); rxDataBin = integerToBinString(rxData, bitCount); rxDataDec = String.valueOf(rxData); rxDataASCII = toASCII((char) rxData); } else /* if ( UARTData.UART_TYPE_TXDATA == ds.getType() ) */ { final int txData = ds.getData(); txDataHex = integerToHexString(txData, (bitCount / 4) + bitAdder); txDataBin = integerToBinString(txData, bitCount); txDataDec = String.valueOf(txData); txDataASCII = toASCII(txData); } tr = aParent.addChild(TR); tr.addChild(TD).addContent(String.valueOf(i)); tr.addChild(TD) .addContent(Unit.Time.format(aDataSet.getTime(ds.getStartSampleIndex()))); tr.addChild(TD).addContent("0x", rxDataHex); tr.addChild(TD).addContent("0b", rxDataBin); tr.addChild(TD).addContent(rxDataDec); tr.addChild(TD).addContent(rxDataASCII); tr.addChild(TD).addContent("0x", txDataHex); tr.addChild(TD).addContent("0b", txDataBin); tr.addChild(TD).addContent(txDataDec); tr.addChild(TD).addContent(txDataASCII); } } } return null; } }; if (aFile == null) { final HtmlExporter exporter = createHtmlTemplate(ExportUtils.createHtmlExporter()); return exporter.toString(macroResolver); } else { final HtmlFileExporter exporter = (HtmlFileExporter) createHtmlTemplate(ExportUtils.createHtmlExporter(aFile)); exporter.write(macroResolver); exporter.close(); } return null; } }
/** Denotes a front-end controller for the client. */ public final class ClientController implements ActionProvider, CaptureCallback, AnalysisCallback { // INNER TYPES /** Provides a default tool context implementation. */ static final class DefaultToolContext implements ToolContext { // VARIABLES private final int startSampleIdx; private final int endSampleIdx; // CONSTRUCTORS /** * Creates a new DefaultToolContext instance. * * @param aStartSampleIdx the starting sample index; * @param aEndSampleIdx the ending sample index. */ public DefaultToolContext(final int aStartSampleIdx, final int aEndSampleIdx) { this.startSampleIdx = aStartSampleIdx; this.endSampleIdx = aEndSampleIdx; } /** @see nl.lxtreme.ols.api.tools.ToolContext#getEndSampleIndex() */ @Override public int getEndSampleIndex() { return this.endSampleIdx; } /** @see nl.lxtreme.ols.api.tools.ToolContext#getLength() */ @Override public int getLength() { return Math.max(0, this.endSampleIdx - this.startSampleIdx); } /** @see nl.lxtreme.ols.api.tools.ToolContext#getStartSampleIndex() */ @Override public int getStartSampleIndex() { return this.startSampleIdx; } } // CONSTANTS private static final Logger LOG = Logger.getLogger(ClientController.class.getName()); // VARIABLES private final ActionManager actionManager; private final BundleContext bundleContext; private final DataContainer dataContainer; private final EventListenerList evenListeners; private final ProjectManager projectManager; private final Host host; private MainFrame mainFrame; private volatile DeviceController currentDevCtrl; // CONSTRUCTORS /** Creates a new ClientController instance. */ public ClientController( final BundleContext aBundleContext, final Host aHost, final ProjectManager aProjectManager) { this.bundleContext = aBundleContext; this.host = aHost; this.projectManager = aProjectManager; this.dataContainer = new DataContainer(this.projectManager); this.actionManager = new ActionManager(); this.evenListeners = new EventListenerList(); fillActionManager(this.actionManager); } // METHODS /** * Adds a cursor change listener. * * @param aListener the listener to add, cannot be <code>null</code>. */ public void addCursorChangeListener(final DiagramCursorChangeListener aListener) { this.evenListeners.add(DiagramCursorChangeListener.class, aListener); } /** * Adds the given device controller to this controller. * * @param aDeviceController the device controller to add, cannot be <code>null</code>. */ public void addDevice(final DeviceController aDeviceController) { if (this.mainFrame != null) { if (this.mainFrame.addDeviceMenuItem(aDeviceController)) { this.currentDevCtrl = aDeviceController; } } updateActions(); } /** * Adds the given exporter to this controller. * * @param aExporter the exporter to add, cannot be <code>null</code>. */ public void addExporter(final Exporter aExporter) { if (this.mainFrame != null) { this.mainFrame.addExportMenuItem(aExporter.getName()); } updateActions(); } /** * Adds the given tool to this controller. * * @param aTool the tool to add, cannot be <code>null</code>. */ public void addTool(final Tool aTool) { if (this.mainFrame != null) { this.mainFrame.addToolMenuItem(aTool.getName()); } updateActions(); } /** @see nl.lxtreme.ols.api.tools.AnalysisCallback#analysisAborted(java.lang.String) */ @Override public void analysisAborted(final String aReason) { setStatus("Analysis aborted! " + aReason); updateActions(); } /** * @see * nl.lxtreme.ols.api.tools.AnalysisCallback#analysisComplete(nl.lxtreme.ols.api.data.CapturedData) */ @Override public void analysisComplete(final CapturedData aNewCapturedData) { if (aNewCapturedData != null) { this.dataContainer.setCapturedData(aNewCapturedData); } if (this.mainFrame != null) { repaintMainFrame(); } setStatus(""); updateActions(); } /** Cancels the current capturing (if in progress). */ public void cancelCapture() { final DeviceController deviceController = getDeviceController(); if (deviceController == null) { return; } deviceController.cancel(); } /** @see nl.lxtreme.ols.api.devices.CaptureCallback#captureAborted(java.lang.String) */ @Override public void captureAborted(final String aReason) { setStatus("Capture aborted! " + aReason); updateActions(); } /** * @see * nl.lxtreme.ols.api.devices.CaptureCallback#captureComplete(nl.lxtreme.ols.api.data.CapturedData) */ @Override public void captureComplete(final CapturedData aCapturedData) { setCapturedData(aCapturedData); setStatus("Capture finished at {0,date,medium} {0,time,medium}.", new Date()); updateActions(); } /** * Captures the data of the current device controller. * * @param aParent the parent window to use, can be <code>null</code>. * @return <code>true</code> if the capture succeeded, <code>false</code> otherwise. * @throws IOException in case of I/O problems. */ public boolean captureData(final Window aParent) { final DeviceController devCtrl = getDeviceController(); if (devCtrl == null) { return false; } try { if (devCtrl.setupCapture(aParent)) { setStatus( "Capture from {0} started at {1,date,medium} {1,time,medium} ...", devCtrl.getName(), new Date()); devCtrl.captureData(this); return true; } return false; } catch (IOException exception) { captureAborted("I/O problem: " + exception.getMessage()); // Make sure to handle IO-interrupted exceptions properly! if (!HostUtils.handleInterruptedException(exception)) { exception.printStackTrace(); } return false; } finally { updateActions(); } } /** @see nl.lxtreme.ols.api.devices.CaptureCallback#captureStarted(int, int, int) */ @Override public synchronized void captureStarted( final int aSampleRate, final int aChannelCount, final int aChannelMask) { final Runnable runner = new Runnable() { @Override public void run() { updateActions(); } }; if (SwingUtilities.isEventDispatchThread()) { runner.run(); } else { SwingUtilities.invokeLater(runner); } } /** Clears all current cursors. */ public void clearAllCursors() { for (int i = 0; i < CapturedData.MAX_CURSORS; i++) { this.dataContainer.setCursorPosition(i, null); } fireCursorChangedEvent(0, -1); // removed... updateActions(); } /** Clears the current device controller. */ public void clearDeviceController() { this.currentDevCtrl = null; } /** * Clears the current project, and start over as it were a new project, in which no captured data * is shown. */ public void createNewProject() { this.projectManager.createNewProject(); if (this.mainFrame != null) { this.mainFrame.repaint(); } updateActions(); } /** Exits the client application. */ public void exit() { if (this.host != null) { this.host.exit(); } } /** * Exports the current diagram to the given exporter. * * @param aExporter the exporter to export to, cannot be <code>null</code>. * @param aOutputStream the output stream to write the export to, cannot be <code>null</code>. * @throws IOException in case of I/O problems. */ public void exportTo(final Exporter aExporter, final OutputStream aOutputStream) throws IOException { if (this.mainFrame != null) { aExporter.export(this.dataContainer, this.mainFrame.getDiagramScrollPane(), aOutputStream); } } /** @see nl.lxtreme.ols.client.ActionProvider#getAction(java.lang.String) */ public Action getAction(final String aID) { return this.actionManager.getAction(aID); } /** @return the dataContainer */ public DataContainer getDataContainer() { return this.dataContainer; } /** * Returns the current device controller. * * @return the current device controller, can be <code>null</code>. */ public DeviceController getDeviceController() { return this.currentDevCtrl; } /** * Returns all current tools known to the OSGi framework. * * @return a collection of tools, never <code>null</code>. */ public final Collection<DeviceController> getDevices() { final List<DeviceController> tools = new ArrayList<DeviceController>(); synchronized (this.bundleContext) { try { final ServiceReference[] serviceRefs = this.bundleContext.getAllServiceReferences(DeviceController.class.getName(), null); for (ServiceReference serviceRef : serviceRefs) { tools.add((DeviceController) this.bundleContext.getService(serviceRef)); } } catch (InvalidSyntaxException exception) { throw new RuntimeException(exception); } } return tools; } /** * Returns the exporter with the given name. * * @param aName the name of the exporter to return, cannot be <code>null</code>. * @return the exporter with the given name, can be <code>null</code> if no such exporter is * found. * @throws IllegalArgumentException in case the given name was <code>null</code> or empty. */ public Exporter getExporter(final String aName) throws IllegalArgumentException { if ((aName == null) || aName.trim().isEmpty()) { throw new IllegalArgumentException("Name cannot be null or empty!"); } try { final ServiceReference[] serviceRefs = this.bundleContext.getAllServiceReferences(Exporter.class.getName(), null); final int count = (serviceRefs == null) ? 0 : serviceRefs.length; for (int i = 0; i < count; i++) { final Exporter exporter = (Exporter) this.bundleContext.getService(serviceRefs[i]); if (aName.equals(exporter.getName())) { return exporter; } } return null; } catch (InvalidSyntaxException exception) { throw new RuntimeException("getExporter failed!", exception); } } /** * Returns the names of all current available exporters. * * @return an array of exporter names, never <code>null</code>, but can be empty. */ public String[] getExporterNames() { try { final ServiceReference[] serviceRefs = this.bundleContext.getAllServiceReferences(Exporter.class.getName(), null); final int count = serviceRefs == null ? 0 : serviceRefs.length; final String[] result = new String[count]; for (int i = 0; i < count; i++) { final Exporter exporter = (Exporter) this.bundleContext.getService(serviceRefs[i]); result[i] = exporter.getName(); this.bundleContext.ungetService(serviceRefs[i]); } return result; } catch (InvalidSyntaxException exception) { throw new RuntimeException("getAllExporterNames failed!", exception); } } /** * Returns the current project's filename. * * @return a project filename, as file object, can be <code>null</code>. */ public File getProjectFilename() { return this.projectManager.getCurrentProject().getFilename(); } /** * Returns all current tools known to the OSGi framework. * * @return a collection of tools, never <code>null</code>. */ public final Collection<Tool> getTools() { final List<Tool> tools = new ArrayList<Tool>(); synchronized (this.bundleContext) { try { final ServiceReference[] serviceRefs = this.bundleContext.getAllServiceReferences(Tool.class.getName(), null); for (ServiceReference serviceRef : serviceRefs) { tools.add((Tool) this.bundleContext.getService(serviceRef)); } } catch (InvalidSyntaxException exception) { throw new RuntimeException(exception); } } return tools; } /** * Goes to the current cursor position of the cursor with the given index. * * @param aCursorIdx the index of the cursor to go to, >= 0 && < 10. */ public void gotoCursorPosition(final int aCursorIdx) { if ((this.mainFrame != null) && this.dataContainer.isCursorsEnabled()) { final Long cursorPosition = this.dataContainer.getCursorPosition(aCursorIdx); if (cursorPosition != null) { this.mainFrame.gotoPosition(cursorPosition.longValue()); } } } /** Goes to the current cursor position of the first available cursor. */ public void gotoFirstAvailableCursor() { if ((this.mainFrame != null) && this.dataContainer.isCursorsEnabled()) { for (int c = 0; c < CapturedData.MAX_CURSORS; c++) { if (this.dataContainer.isCursorPositionSet(c)) { final Long cursorPosition = this.dataContainer.getCursorPosition(c); if (cursorPosition != null) { this.mainFrame.gotoPosition(cursorPosition.longValue()); } break; } } } } /** Goes to the current cursor position of the last available cursor. */ public void gotoLastAvailableCursor() { if ((this.mainFrame != null) && this.dataContainer.isCursorsEnabled()) { for (int c = CapturedData.MAX_CURSORS - 1; c >= 0; c--) { if (this.dataContainer.isCursorPositionSet(c)) { final Long cursorPosition = this.dataContainer.getCursorPosition(c); if (cursorPosition != null) { this.mainFrame.gotoPosition(cursorPosition.longValue()); } break; } } } } /** Goes to the position of the trigger. */ public void gotoTriggerPosition() { if ((this.mainFrame != null) && this.dataContainer.hasTriggerData()) { final long position = this.dataContainer.getTriggerPosition(); this.mainFrame.gotoPosition(position); } } /** * Returns whether there is a device selected or not. * * @return <code>true</code> if there is a device selected, <code>false</code> if no device is * selected. */ public synchronized boolean isDeviceSelected() { return this.currentDevCtrl != null; } /** * Returns whether the current device is setup at least once. * * @return <code>true</code> if the current device is setup, <code>false</code> otherwise. * @see #isDeviceSelected() */ public synchronized boolean isDeviceSetup() { return (this.currentDevCtrl != null) && this.currentDevCtrl.isSetup(); } /** * Returns whether or not the current project is changed. * * @return <code>true</code> if the current project is changed, <code>false</code> if the current * project is not changed. */ public boolean isProjectChanged() { return this.projectManager.getCurrentProject().isChanged(); } /** * Loads an OLS data file from the given file. * * @param aFile the file to load as OLS data, cannot be <code>null</code>. * @throws IOException in case of I/O problems. */ public void openDataFile(final File aFile) throws IOException { final FileReader reader = new FileReader(aFile); try { final Project tempProject = this.projectManager.createTemporaryProject(); OlsDataHelper.read(tempProject, reader); setChannelLabels(tempProject.getChannelLabels()); setCapturedData(tempProject.getCapturedData()); setCursorData(tempProject.getCursorPositions(), tempProject.isCursorsEnabled()); } finally { reader.close(); zoomToFit(); updateActions(); } } /** * Opens the project denoted by the given file. * * @param aFile the project file to open, cannot be <code>null</code>. * @throws IOException in case of I/O problems. */ public void openProjectFile(final File aFile) throws IOException { FileInputStream fis = new FileInputStream(aFile); this.projectManager.loadProject(fis); final Project project = this.projectManager.getCurrentProject(); project.setFilename(aFile); zoomToFit(); } /** * Removes the cursor denoted by the given cursor index. * * @param aCursorIdx the index of the cursor to remove, >= 0 && < 10. */ public void removeCursor(final int aCursorIdx) { if (this.mainFrame != null) { this.dataContainer.setCursorPosition(aCursorIdx, null); fireCursorChangedEvent(aCursorIdx, -1); // removed... } updateActions(); } /** * Removes a cursor change listener. * * @param aListener the listener to remove, cannot be <code>null</code>. */ public void removeCursorChangeListener(final DiagramCursorChangeListener aListener) { this.evenListeners.remove(DiagramCursorChangeListener.class, aListener); } /** * Removes the given device from the list of devices. * * @param aDeviceController the device to remove, cannot be <code>null</code>. */ public void removeDevice(final DeviceController aDeviceController) { if (this.currentDevCtrl == aDeviceController) { this.currentDevCtrl = null; } if (this.mainFrame != null) { this.mainFrame.removeDeviceMenuItem(aDeviceController.getName()); } updateActions(); } /** * Removes the given exporter from the list of exporters. * * @param aExporter the exporter to remove, cannot be <code>null</code>. */ public void removeExporter(final Exporter aExporter) { if (this.mainFrame != null) { this.mainFrame.removeExportMenuItem(aExporter.getName()); } updateActions(); } /** * Removes the given tool from the list of tools. * * @param aTool the tool to remove, cannot be <code>null</code>. */ public void removeTool(final Tool aTool) { if (this.mainFrame != null) { this.mainFrame.removeToolMenuItem(aTool.getName()); } updateActions(); } /** * Repeats the capture with the current settings. * * @param aParent the parent window to use, can be <code>null</code>. */ public boolean repeatCaptureData(final Window aParent) { final DeviceController devCtrl = getDeviceController(); if (devCtrl == null) { return false; } try { setStatus( "Capture from {0} started at {1,date,medium} {1,time,medium} ...", devCtrl.getName(), new Date()); devCtrl.captureData(this); return true; } catch (IOException exception) { captureAborted("I/O problem: " + exception.getMessage()); exception.printStackTrace(); // Make sure to handle IO-interrupted exceptions properly! HostUtils.handleInterruptedException(exception); return false; } finally { updateActions(); } } /** * Runs the tool denoted by the given name. * * @param aToolName the name of the tool to run, cannot be <code>null</code>; * @param aParent the parent window to use, can be <code>null</code>. */ public void runTool(final String aToolName, final Window aParent) { if (LOG.isLoggable(Level.INFO)) { LOG.log(Level.INFO, "Running tool: \"{0}\" ...", aToolName); } final Tool tool = findToolByName(aToolName); if (tool == null) { JOptionPane.showMessageDialog( aParent, "No such tool found: " + aToolName, "Error ...", JOptionPane.ERROR_MESSAGE); } else { final ToolContext context = createToolContext(); tool.process(aParent, this.dataContainer, context, this); } updateActions(); } /** @see nl.lxtreme.ols.api.devices.CaptureCallback#samplesCaptured(java.util.List) */ @Override public void samplesCaptured(final List<Sample> aSamples) { if (this.mainFrame != null) { this.mainFrame.sampleCaptured(aSamples); } updateActions(); } /** * Saves an OLS data file to the given file. * * @param aFile the file to save the OLS data to, cannot be <code>null</code>. * @throws IOException in case of I/O problems. */ public void saveDataFile(final File aFile) throws IOException { final FileWriter writer = new FileWriter(aFile); try { final Project tempProject = this.projectManager.createTemporaryProject(); tempProject.setCapturedData(this.dataContainer); OlsDataHelper.write(tempProject, writer); } finally { writer.flush(); writer.close(); } } /** * Saves the current project to the given file. * * @param aFile the file to save the project information to, cannot be <code>null</code>. * @throws IOException in case of I/O problems. */ public void saveProjectFile(final String aName, final File aFile) throws IOException { FileOutputStream out = null; try { final Project project = this.projectManager.getCurrentProject(); project.setFilename(aFile); project.setName(aName); out = new FileOutputStream(aFile); this.projectManager.saveProject(out); } finally { HostUtils.closeResource(out); } } /** * Sets whether or not cursors are enabled. * * @param aState <code>true</code> if the cursors should be enabled, <code>false</code> otherwise. */ public void setCursorMode(final boolean aState) { this.dataContainer.setCursorEnabled(aState); // Reflect the change directly on the diagram... repaintMainFrame(); updateActions(); } /** * Sets the cursor position of the cursor with the given index. * * @param aCursorIdx the index of the cursor to set, >= 0 && < 10; * @param aLocation the mouse location on screen where the cursor should become, cannot be <code> * null</code>. */ public void setCursorPosition(final int aCursorIdx, final Point aLocation) { // Implicitly enable cursor mode, the user already had made its // intensions clear that he want to have this by opening up the // context menu anyway... setCursorMode(true); if (this.mainFrame != null) { // Convert the mouse-position to a sample index... final long sampleIdx = this.mainFrame.convertMousePositionToSampleIndex(aLocation); this.dataContainer.setCursorPosition(aCursorIdx, Long.valueOf(sampleIdx)); fireCursorChangedEvent(aCursorIdx, aLocation.x); } updateActions(); } /** * Sets the current device controller to the given value. * * @param aDeviceName the name of the device controller to set, cannot be <code>null</code>. */ public synchronized void setDeviceController(final String aDeviceName) { if (LOG.isLoggable(Level.INFO)) { final String name = (aDeviceName == null) ? "no device" : aDeviceName; LOG.log(Level.INFO, "Setting current device controller to: \"{0}\" ...", name); } final Collection<DeviceController> devices = getDevices(); for (DeviceController device : devices) { if (aDeviceName.equals(device.getName())) { this.currentDevCtrl = device; } } updateActions(); } /** @param aMainFrame the mainFrame to set */ public void setMainFrame(final MainFrame aMainFrame) { if (this.mainFrame != null) { this.projectManager.removePropertyChangeListener(this.mainFrame); } if (aMainFrame != null) { this.projectManager.addPropertyChangeListener(aMainFrame); } this.mainFrame = aMainFrame; } /** * Sets a status message. * * @param aMessage the message to set; * @param aMessageArgs the (optional) message arguments. */ public final void setStatus(final String aMessage, final Object... aMessageArgs) { if (this.mainFrame != null) { this.mainFrame.setStatus(aMessage, aMessageArgs); } } /** Shows the "about OLS" dialog on screen. the parent window to use, can be <code>null</code>. */ public void showAboutBox() { MainFrame.showAboutBox(this.host.getVersion()); } /** * Shows a dialog with all running OSGi bundles. * * @param aOwner the owning window to use, can be <code>null</code>. */ public void showBundlesDialog(final Window aOwner) { BundlesDialog dialog = new BundlesDialog(aOwner, this.bundleContext); if (dialog.showDialog()) { dialog.dispose(); dialog = null; } } /** * Shows the label-editor dialog on screen. * * <p>Display the diagram labels dialog. Will block until the dialog is closed again. * * @param aParent the parent window to use, can be <code>null</code>. */ public void showLabelsDialog(final Window aParent) { if (this.mainFrame != null) { DiagramLabelsDialog dialog = new DiagramLabelsDialog(aParent, this.dataContainer.getChannelLabels()); if (dialog.showDialog()) { final String[] channelLabels = dialog.getChannelLabels(); setChannelLabels(channelLabels); } dialog.dispose(); dialog = null; } } /** * Shows the settings-editor dialog on screen. * * <p>Display the diagram settings dialog. Will block until the dialog is closed again. * * @param aParent the parent window to use, can be <code>null</code>. */ public void showModeSettingsDialog(final Window aParent) { if (this.mainFrame != null) { ModeSettingsDialog dialog = new ModeSettingsDialog(aParent, getDiagramSettings()); if (dialog.showDialog()) { updateDiagramSettings(dialog.getDiagramSettings()); } dialog.dispose(); dialog = null; } } /** @param aOwner */ public void showPreferencesDialog(final Window aParent) { GeneralSettingsDialog dialog = new GeneralSettingsDialog(aParent, getDiagramSettings()); if (dialog.showDialog()) { updateDiagramSettings(dialog.getDiagramSettings()); } dialog.dispose(); dialog = null; } /** @see nl.lxtreme.ols.api.ProgressCallback#updateProgress(int) */ @Override public void updateProgress(final int aPercentage) { if (this.mainFrame != null) { this.mainFrame.setProgress(aPercentage); } } /** Zooms in to the maximum zoom level. */ public void zoomDefault() { if (this.mainFrame != null) { this.mainFrame.zoomDefault(); } updateActions(); } /** Zooms in with a factor of 2.0. */ public void zoomIn() { if (this.mainFrame != null) { this.mainFrame.zoomIn(); } updateActions(); } /** Zooms out with a factor of 2.0. */ public void zoomOut() { if (this.mainFrame != null) { this.mainFrame.zoomOut(); } updateActions(); } /** Zooms to fit the diagram to the current window dimensions. */ public void zoomToFit() { if (this.mainFrame != null) { this.mainFrame.zoomToFit(); } updateActions(); } /** * Returns the current main frame. * * @return the main frame, can be <code>null</code>. */ final MainFrame getMainFrame() { return this.mainFrame; } /** * Creates the tool context denoting the range of samples that should be analysed by a tool. * * @return a tool context, never <code>null</code>. */ private ToolContext createToolContext() { int startOfDecode = -1; int endOfDecode = -1; final int dataLength = this.dataContainer.getValues().length; if (this.dataContainer.isCursorsEnabled()) { if (this.dataContainer.isCursorPositionSet(0)) { final Long cursor1 = this.dataContainer.getCursorPosition(0); startOfDecode = this.dataContainer.getSampleIndex(cursor1.longValue()) - 1; } if (this.dataContainer.isCursorPositionSet(1)) { final Long cursor2 = this.dataContainer.getCursorPosition(1); endOfDecode = this.dataContainer.getSampleIndex(cursor2.longValue()) + 1; } } else { startOfDecode = 0; endOfDecode = dataLength; } startOfDecode = Math.max(0, startOfDecode); if ((endOfDecode < 0) || (endOfDecode >= dataLength)) { endOfDecode = dataLength - 1; } return new DefaultToolContext(startOfDecode, endOfDecode); } /** @param aActionManager */ private void fillActionManager(final ActionManager aActionManager) { aActionManager.add(new NewProjectAction(this)); aActionManager.add(new OpenProjectAction(this)); aActionManager.add(new SaveProjectAction(this)).setEnabled(false); aActionManager.add(new SaveProjectAsAction(this)).setEnabled(false); aActionManager.add(new OpenDataFileAction(this)); aActionManager.add(new SaveDataFileAction(this)).setEnabled(false); aActionManager.add(new ExitAction(this)); aActionManager.add(new CaptureAction(this)); aActionManager.add(new CancelCaptureAction(this)).setEnabled(false); aActionManager.add(new RepeatCaptureAction(this)).setEnabled(false); aActionManager.add(new ZoomInAction(this)).setEnabled(false); aActionManager.add(new ZoomOutAction(this)).setEnabled(false); aActionManager.add(new ZoomDefaultAction(this)).setEnabled(false); aActionManager.add(new ZoomFitAction(this)).setEnabled(false); aActionManager.add(new GotoTriggerAction(this)).setEnabled(false); for (int c = 0; c < CapturedData.MAX_CURSORS; c++) { aActionManager.add(new GotoNthCursorAction(this, c)).setEnabled(false); } aActionManager.add(new GotoFirstCursorAction(this)).setEnabled(false); aActionManager.add(new GotoLastCursorAction(this)).setEnabled(false); aActionManager.add(new ClearCursors(this)).setEnabled(false); aActionManager.add(new SetCursorModeAction(this)); for (int c = 0; c < CapturedData.MAX_CURSORS; c++) { aActionManager.add(new SetCursorAction(this, c)); } aActionManager.add(new ShowGeneralSettingsAction(this)); aActionManager.add(new ShowModeSettingsAction(this)); aActionManager.add(new ShowDiagramLabelsAction(this)); aActionManager.add(new HelpAboutAction(this)); aActionManager.add(new ShowBundlesAction(this)); } /** * Searches for the tool with the given name. * * @param aToolName the name of the tool to search for, cannot be <code>null</code>. * @return the tool with the given name, can be <code>null</code> if no such tool can be found. */ private Tool findToolByName(final String aToolName) { Tool tool = null; final Collection<Tool> tools = getTools(); for (Tool _tool : tools) { if (aToolName.equals(_tool.getName())) { tool = _tool; break; } } return tool; } /** * @param aCursorIdx * @param aMouseXpos */ private void fireCursorChangedEvent(final int aCursorIdx, final int aMouseXpos) { final DiagramCursorChangeListener[] listeners = this.evenListeners.getListeners(DiagramCursorChangeListener.class); for (final DiagramCursorChangeListener listener : listeners) { if (aMouseXpos >= 0) { listener.cursorChanged(aCursorIdx, aMouseXpos); } else { listener.cursorRemoved(aCursorIdx); } } } /** * Returns the current diagram settings. * * @return the current diagram settings, can be <code>null</code> if there is no main frame to * take the settings from. */ private DiagramSettings getDiagramSettings() { return this.mainFrame != null ? this.mainFrame.getDiagramSettings() : null; } /** Dispatches a request to repaint the entire main frame. */ private void repaintMainFrame() { SwingUtilities.invokeLater( new Runnable() { @Override public void run() { ClientController.this.mainFrame.repaint(); } }); } /** * Sets the captured data and zooms the view to show all the data. * * @param aCapturedData the new captured data to set, cannot be <code>null</code>. */ private void setCapturedData(final CapturedData aCapturedData) { this.dataContainer.setCapturedData(aCapturedData); if (this.mainFrame != null) { this.mainFrame.zoomToFit(); } } /** * Set the channel labels. * * @param aChannelLabels the channel labels to set, cannot be <code>null</code>. */ private void setChannelLabels(final String[] aChannelLabels) { if (aChannelLabels != null) { this.dataContainer.setChannelLabels(aChannelLabels); this.mainFrame.setChannelLabels(aChannelLabels); } } /** * @param aCursorData the cursor positions to set, cannot be <code>null</code>; * @param aCursorsEnabled <code>true</code> if cursors should be enabled, <code>false</code> if * they should be disabled. */ private void setCursorData(final Long[] aCursorData, final boolean aCursorsEnabled) { this.dataContainer.setCursorEnabled(aCursorsEnabled); for (int i = 0; i < CapturedData.MAX_CURSORS; i++) { this.dataContainer.setCursorPosition(i, aCursorData[i]); } } /** Synchronizes the state of the actions to the current state of this host. */ private void updateActions() { final DeviceController currentDeviceController = getDeviceController(); final boolean deviceControllerSet = currentDeviceController != null; final boolean deviceCapturing = deviceControllerSet && currentDeviceController.isCapturing(); final boolean deviceSetup = deviceControllerSet && !deviceCapturing && currentDeviceController.isSetup(); getAction(CaptureAction.ID).setEnabled(deviceControllerSet); getAction(CancelCaptureAction.ID).setEnabled(deviceCapturing); getAction(RepeatCaptureAction.ID).setEnabled(deviceSetup); final boolean projectChanged = this.projectManager.getCurrentProject().isChanged(); final boolean projectSavedBefore = this.projectManager.getCurrentProject().getFilename() != null; final boolean dataAvailable = this.dataContainer.hasCapturedData(); getAction(SaveProjectAction.ID).setEnabled(projectChanged); getAction(SaveProjectAsAction.ID).setEnabled(projectSavedBefore && projectChanged); getAction(SaveDataFileAction.ID).setEnabled(dataAvailable); getAction(ZoomInAction.ID).setEnabled(dataAvailable); getAction(ZoomOutAction.ID).setEnabled(dataAvailable); getAction(ZoomDefaultAction.ID).setEnabled(dataAvailable); getAction(ZoomFitAction.ID).setEnabled(dataAvailable); final boolean triggerEnable = dataAvailable && this.dataContainer.hasTriggerData(); getAction(GotoTriggerAction.ID).setEnabled(triggerEnable); // Update the cursor actions accordingly... final boolean enableCursors = dataAvailable && this.dataContainer.isCursorsEnabled(); for (int c = 0; c < CapturedData.MAX_CURSORS; c++) { final boolean enabled = enableCursors && this.dataContainer.isCursorPositionSet(c); getAction(GotoNthCursorAction.getID(c)).setEnabled(enabled); } getAction(GotoFirstCursorAction.ID).setEnabled(enableCursors); getAction(GotoLastCursorAction.ID).setEnabled(enableCursors); getAction(SetCursorModeAction.ID).setEnabled(dataAvailable); getAction(SetCursorModeAction.ID) .putValue(Action.SELECTED_KEY, Boolean.valueOf(this.dataContainer.isCursorsEnabled())); boolean anyCursorSet = false; for (int c = 0; c < CapturedData.MAX_CURSORS; c++) { final boolean cursorPositionSet = this.dataContainer.isCursorPositionSet(c); anyCursorSet |= cursorPositionSet; final Action action = getAction(SetCursorAction.getCursorId(c)); action.setEnabled(dataAvailable); action.putValue(Action.SELECTED_KEY, Boolean.valueOf(cursorPositionSet)); } getAction(ClearCursors.ID).setEnabled(enableCursors && anyCursorSet); } /** * Should be called after the diagram settings are changed. This method will cause the settings to * be set on the main frame and writes them to the preference store. * * @param aSettings the (new/changed) diagram settings to set, cannot be <code>null</code>. */ private void updateDiagramSettings(final DiagramSettings aSettings) { if (this.mainFrame != null) { this.mainFrame.setDiagramSettings(aSettings); repaintMainFrame(); } } }
/** Provides a device profile. */ public final class DeviceProfile implements Cloneable, Comparable<DeviceProfile> { // INNER TYPES /** The various capture clock sources. */ public static enum CaptureClockSource { INTERNAL, EXTERNAL_FALLING, EXTERNAL_RISING; } /** The various interfaces of the device. */ public static enum DeviceInterface { SERIAL, NETWORK, USB; } /** The various numbering schemes. */ public static enum NumberingScheme { DEFAULT, INSIDE, OUTSIDE; } /** The various types of triggers. */ public static enum TriggerType { SIMPLE, COMPLEX; } // CONSTANTS /** The short (single word) type of the device described in this profile */ public static final String DEVICE_TYPE = "device.type"; /** A longer description of the device */ public static final String DEVICE_DESCRIPTION = "device.description"; /** The device interface, currently SERIAL only */ public static final String DEVICE_INTERFACE = "device.interface"; /** The device's native clockspeed, in Hertz. */ public static final String DEVICE_CLOCKSPEED = "device.clockspeed"; /** * Whether or not double-data-rate is supported by the device (also known as the "demux"-mode). */ public static final String DEVICE_SUPPORTS_DDR = "device.supports_ddr"; /** Supported sample rates in Hertz, separated by comma's */ public static final String DEVICE_SAMPLERATES = "device.samplerates"; /** What capture clocks are supported */ public static final String DEVICE_CAPTURECLOCK = "device.captureclock"; /** The supported capture sizes, in bytes */ public static final String DEVICE_CAPTURESIZES = "device.capturesizes"; /** Whether or not the noise filter is supported */ public static final String DEVICE_FEATURE_NOISEFILTER = "device.feature.noisefilter"; /** Whether or not Run-Length encoding is supported */ public static final String DEVICE_FEATURE_RLE = "device.feature.rle"; /** Whether or not a testing mode is supported. */ public static final String DEVICE_FEATURE_TEST_MODE = "device.feature.testmode"; /** Whether or not triggers are supported */ public static final String DEVICE_FEATURE_TRIGGERS = "device.feature.triggers"; /** The number of trigger stages */ public static final String DEVICE_TRIGGER_STAGES = "device.trigger.stages"; /** Whether or not "complex" triggers are supported */ public static final String DEVICE_TRIGGER_COMPLEX = "device.trigger.complex"; /** The total number of channels usable for capturing */ public static final String DEVICE_CHANNEL_COUNT = "device.channel.count"; /** * The number of channels groups, together with the channel count determines the channels per * group */ public static final String DEVICE_CHANNEL_GROUPS = "device.channel.groups"; /** Whether the capture size is limited by the enabled channel groups */ public static final String DEVICE_CAPTURESIZE_BOUND = "device.capturesize.bound"; /** What channel numbering schemes are supported by the device. */ public static final String DEVICE_CHANNEL_NUMBERING_SCHEMES = "device.channel.numberingschemes"; /** * Is a delay after opening the port and device detection needed? (0 = no delay, >0 = delay in * milliseconds) */ public static final String DEVICE_OPEN_PORT_DELAY = "device.open.portdelay"; /** The receive timeout (100 = default, in milliseconds) */ public static final String DEVICE_RECEIVE_TIMEOUT = "device.receive.timeout"; /** * Which metadata keys correspond to this device profile? Value is a comma-separated list of * (double quoted) names. */ public static final String DEVICE_METADATA_KEYS = "device.metadata.keys"; /** * In which order are samples sent back from the device? If <code>true</code> then last sample * first, if <code>false</code> then first sample first. */ public static final String DEVICE_SAMPLE_REVERSE_ORDER = "device.samples.reverseOrder"; /** In case of a serial port, does the DTR-line need to be high (= true) or low (= false)? */ public static final String DEVICE_OPEN_PORT_DTR = "device.open.portdtr"; /** Filename of the actual file picked up by Felix's FileInstall. */ public static final String FELIX_FILEINSTALL_FILENAME = "felix.fileinstall.filename"; /** Service PID of this device profile. */ private static final String FELIX_SERVICE_PID = "service.pid"; /** Factory Service PID of this device profile. */ private static final String FELIX_SERVICE_FACTORY_PID = "service.factoryPid"; /** All the profile keys that are supported. */ private static final List<String> KNOWN_KEYS = Arrays.asList( new String[] { DEVICE_TYPE, DEVICE_DESCRIPTION, DEVICE_INTERFACE, DEVICE_CLOCKSPEED, DEVICE_SUPPORTS_DDR, DEVICE_SAMPLERATES, DEVICE_CAPTURECLOCK, DEVICE_CAPTURESIZES, DEVICE_FEATURE_NOISEFILTER, DEVICE_FEATURE_RLE, DEVICE_FEATURE_TEST_MODE, DEVICE_FEATURE_TRIGGERS, DEVICE_TRIGGER_STAGES, DEVICE_TRIGGER_COMPLEX, DEVICE_CHANNEL_COUNT, DEVICE_CHANNEL_GROUPS, DEVICE_CAPTURESIZE_BOUND, DEVICE_CHANNEL_NUMBERING_SCHEMES, DEVICE_OPEN_PORT_DELAY, DEVICE_METADATA_KEYS, DEVICE_SAMPLE_REVERSE_ORDER, DEVICE_OPEN_PORT_DTR, DEVICE_RECEIVE_TIMEOUT, FELIX_FILEINSTALL_FILENAME }); private static final List<String> IGNORED_KEYS = Arrays.asList(new String[] {FELIX_SERVICE_PID, FELIX_SERVICE_FACTORY_PID}); private static final Logger LOG = Logger.getLogger(DeviceProfile.class.getName()); // VARIABLES private final ConcurrentMap<String, String> properties; // CONSTRUCTORS /** Creates a new DeviceProfile. */ public DeviceProfile() { this.properties = new ConcurrentHashMap<String, String>(); } // METHODS /** * @param aFilename * @return */ static final File createFile(final String aFilename) { if (aFilename == null) { throw new IllegalArgumentException("Filename cannot be null!"); } return new File(aFilename.replaceAll("^file:", "")); } /** * Returns a deep copy of this device profile, including all properties. * * @return a deep copy of this device profile, never <code>null</code>. * @see java.lang.Object#clone() */ @Override public DeviceProfile clone() { try { DeviceProfile clone = (DeviceProfile) super.clone(); clone.properties.putAll(this.properties); return clone; } catch (CloneNotSupportedException exception) { throw new IllegalStateException(exception); } } /** {@inheritDoc} */ @Override public int compareTo(DeviceProfile aProfile) { // Issue #123: allow device profiles to be sorted alphabetically... int result = getDescription().compareTo(aProfile.getDescription()); if (result == 0) { result = getType().compareTo(aProfile.getType()); } return result; } /** {@inheritDoc} */ @Override public boolean equals(final Object aObject) { if (this == aObject) { return true; } if ((aObject == null) || !(aObject instanceof DeviceProfile)) { return false; } final DeviceProfile other = (DeviceProfile) aObject; return this.properties.equals(other.properties); } /** * Returns the capture clock sources supported by the device. * * @return an array of capture clock sources, never <code>null</code>. */ public CaptureClockSource[] getCaptureClock() { final String rawValue = this.properties.get(DEVICE_CAPTURECLOCK); final String[] values = rawValue.split(",\\s*"); final CaptureClockSource[] result = new CaptureClockSource[values.length]; for (int i = 0; i < values.length; i++) { result[i] = CaptureClockSource.valueOf(values[i].trim()); } return result; } /** * Returns all supported capture sizes. * * @return an array of capture sizes, in bytes, never <code>null</code>. */ public Integer[] getCaptureSizes() { final String rawValue = this.properties.get(DEVICE_CAPTURESIZES); final String[] values = rawValue.split(",\\s*"); final List<Integer> result = new ArrayList<Integer>(); for (String value : values) { result.add(Integer.valueOf(value.trim())); } Collections.sort( result, NumberUtils.<Integer>createNumberComparator(false /* aSortAscending */)); return result.toArray(new Integer[result.size()]); } /** * Returns the total number of capture channels. * * @return a capture channel count, greater than 0. */ public int getChannelCount() { final String value = this.properties.get(DEVICE_CHANNEL_COUNT); return Integer.parseInt(value); } /** * Returns the total number of channel groups. * * @return a channel group count, greater than 0. */ public int getChannelGroupCount() { final String value = this.properties.get(DEVICE_CHANNEL_GROUPS); return Integer.parseInt(value); } /** * Returns all supported channel numbering schemes. * * @return an array of numbering schemes, never <code>null</code>. */ public NumberingScheme[] getChannelNumberingSchemes() { final String rawValue = this.properties.get(DEVICE_CHANNEL_NUMBERING_SCHEMES); final String[] values = rawValue.split(",\\s*"); final NumberingScheme[] result = new NumberingScheme[values.length]; for (int i = 0; i < result.length; i++) { result[i] = NumberingScheme.valueOf(values[i].trim()); } return result; } /** * Returns the (maximum) capture clock of the device. * * @return a capture clock, in Hertz. */ public int getClockspeed() { final String value = this.properties.get(DEVICE_CLOCKSPEED); return Integer.parseInt(value); } /** * Returns the description of the device this profile denotes. * * @return a device description, never <code>null</code>. */ public String getDescription() { final String result = this.properties.get(DEVICE_DESCRIPTION); return result == null ? "" : (String) result; } /** * Returns the metadata keys that allow identification of this device profile. * * <p>Note: if the returned array contains an empty string value (not <code>null</code>, but * <code>""</code>!), it means that this profile can be used for <em>all</em> devices. * * @return an array of metadata keys this profile supports, never <code>null</code>. */ public String[] getDeviceMetadataKeys() { final String rawValue = this.properties.get(DEVICE_METADATA_KEYS); return StringUtils.tokenizeQuotedStrings(rawValue, ", "); } /** * Returns the interface over which the device communicates. * * @return the device interface, never <code>null</code>. */ public DeviceInterface getInterface() { final String value = this.properties.get(DEVICE_INTERFACE); return DeviceInterface.valueOf(value); } /** * Returns the maximum capture size for the given number of <em>enabled</em> channel groups. * * <p>If the maximum capture size is bound to the number of enabled channel(group)s, this method * will divide the maximum possible capture size by the given group count, otherwise the maximum * capture size will be returned. * * @param aChannelGroups the number of channel groups that should be enabled, > 0 && < channel * group count. * @return a maximum capture size, in bytes, or -1 if no maximum could be determined, or the given * parameter was <tt>0</tt>. * @see #isCaptureSizeBoundToEnabledChannels() * @see #getChannelGroupCount() */ public int getMaximumCaptureSizeFor(final int aChannelGroups) { final Integer[] sizes = getCaptureSizes(); if ((sizes == null) || (sizes.length == 0) || (aChannelGroups == 0)) { return -1; } final int maxSize = sizes[0].intValue(); if (isCaptureSizeBoundToEnabledChannels()) { int indication = maxSize / aChannelGroups; // Issue #58: Search the best matching value... Integer result = null; for (int i = sizes.length - 1; i >= 0; i--) { if (sizes[i].compareTo(Integer.valueOf(indication)) <= 0) { result = sizes[i]; } } return (result == null) ? indication : result.intValue(); } return maxSize; } /** * Returns the delay between opening the port to the device and starting the device detection * cycle. * * @return a delay, in milliseconds, >= 0. */ public int getOpenPortDelay() { final String value = this.properties.get(DEVICE_OPEN_PORT_DELAY); return Integer.parseInt(value); } /** * Returns the (optional) receive timeout. * * <p>WARNING: if no receive timeout is used, the communication essentially results in a * non-blocking I/O operation which can not be cancelled! * * @return the receive timeout, in ms, or <code>null</code> when no receive timeout should be * used. */ public Integer getReceiveTimeout() { final String value = this.properties.get(DEVICE_RECEIVE_TIMEOUT); if (value == null) { return null; } int timeout = Integer.parseInt(value); return (timeout <= 0) ? null : Integer.valueOf(timeout); } /** * Returns all supported sample rates. * * @return an array of sample rates, in Hertz, never <code>null</code>. */ public Integer[] getSampleRates() { final String rawValue = this.properties.get(DEVICE_SAMPLERATES); final String[] values = rawValue.split(",\\s*"); final SortedSet<Integer> result = new TreeSet<Integer>( NumberUtils.<Integer>createNumberComparator(false /* aSortAscending */)); for (String value : values) { result.add(Integer.valueOf(value.trim())); } return result.toArray(new Integer[result.size()]); } /** * Returns the total number of trigger stages (in the complex trigger mode). * * @return a trigger stage count, greater than 0. */ public int getTriggerStages() { final String value = this.properties.get(DEVICE_TRIGGER_STAGES); return Integer.parseInt(value); } /** * Returns the device type this profile denotes. * * @return a device type name, never <code>null</code>. */ public String getType() { final String result = this.properties.get(DEVICE_TYPE); return result == null ? "<unknown>" : result; } /** {@inheritDoc} */ @Override public int hashCode() { final int prime = 31; int result = 1; result = (prime * result) + ((this.properties == null) ? 0 : this.properties.hashCode()); return result; } /** * Returns whether or not the capture size is bound to the number of channels. * * @return <code>true</code> if the capture size is bound to the number of channels, <code>false * </code> otherwise. */ public boolean isCaptureSizeBoundToEnabledChannels() { final String value = this.properties.get(DEVICE_CAPTURESIZE_BOUND); return Boolean.parseBoolean(value); } /** * Returns whether or not the device supports "complex" triggers. * * @return <code>true</code> if complex triggers are supported by the device, <code>false</code> * otherwise. */ public boolean isComplexTriggersSupported() { final String value = this.properties.get(DEVICE_TRIGGER_COMPLEX); return Boolean.parseBoolean(value); } /** * Returns whether or not the device supports "double-data rate" sampling, also known as * "demux"-sampling. * * @return <code>true</code> if DDR is supported by the device, <code>false</code> otherwise. */ public boolean isDoubleDataRateSupported() { final String value = this.properties.get(DEVICE_SUPPORTS_DDR); return Boolean.parseBoolean(value); } /** * Returns whether or not the device supports a noise filter. * * @return <code>true</code> if a noise filter is present in the device, <code>false</code> * otherwise. */ public boolean isNoiseFilterSupported() { final String value = this.properties.get(DEVICE_FEATURE_NOISEFILTER); return Boolean.parseBoolean(value); } /** * Returns whether upon opening the DTR line needs to be high (= <code>true</code>) or low (= * <code>false</code>). * * <p>This method has no meaning if the used interface is <em>not</em> {@link * DeviceInterface#SERIAL}. * * @return <code>true</code> if the DTR line needs to be set upon opening the serial port, <code> * false</code> if the DTR line needs to be reset upon opening the serial port. */ public boolean isOpenPortDtr() { final String value = this.properties.get(DEVICE_OPEN_PORT_DTR); return Boolean.parseBoolean(value); } /** * Returns whether or not the device supports RLE (Run-Length Encoding). * * @return <code>true</code> if a RLE encoder is present in the device, <code>false</code> * otherwise. */ public boolean isRleSupported() { final String value = this.properties.get(DEVICE_FEATURE_RLE); return Boolean.parseBoolean(value); } /** * Returns whether the device send its samples in "reverse" order. * * @return <code>true</code> if samples are send in reverse order (= last sample first), <code> * false</code> otherwise. */ public boolean isSamplesInReverseOrder() { final String rawValue = this.properties.get(DEVICE_SAMPLE_REVERSE_ORDER); return Boolean.parseBoolean(rawValue); } /** * Returns whether or not the device supports a testing mode. * * @return <code>true</code> if testing mode is supported by the device, <code>false</code> * otherwise. */ public boolean isTestModeSupported() { final String value = this.properties.get(DEVICE_FEATURE_TEST_MODE); return Boolean.parseBoolean(value); } /** * Returns whether or not the device supports triggers. * * @return <code>true</code> if the device supports triggers, <code>false</code> otherwise. */ public boolean isTriggerSupported() { final String value = this.properties.get(DEVICE_FEATURE_TRIGGERS); return Boolean.parseBoolean(value); } /** {@inheritDoc} */ @Override public String toString() { return getType(); } /** * Returns the configuration file picked up by Felix's FileInstall bundle. * * @return a configuration file, never <code>null</code>. */ final File getConfigurationFile() { final String value = this.properties.get(FELIX_FILEINSTALL_FILENAME); assert value != null : "Internal error: no fileinstall filename?!"; return createFile(value); } /** @return the properties of this device profile, never <code>null</code>. */ final Dictionary<String, String> getProperties() { return new Hashtable<String, String>(this.properties); } /** @param aProperties the updated properties. */ @SuppressWarnings("rawtypes") final void setProperties(final Dictionary aProperties) { final Map<String, String> newProps = new HashMap<String, String>(); Enumeration keys = aProperties.keys(); while (keys.hasMoreElements()) { final String key = (String) keys.nextElement(); if (!KNOWN_KEYS.contains(key) && !IGNORED_KEYS.contains(key)) { LOG.log(Level.WARNING, "Unknown/unsupported profile key: " + key); continue; } final String value = aProperties.get(key).toString(); newProps.put(key, value.trim()); } // Verify whether all known keys are defined... final List<String> checkedKeys = new ArrayList<String>(KNOWN_KEYS); checkedKeys.removeAll(newProps.keySet()); if (!checkedKeys.isEmpty()) { throw new IllegalArgumentException( "Profile settings not complete! Missing keys are: " + checkedKeys.toString()); } this.properties.putAll(newProps); LOG.log( Level.INFO, "New device profile settings applied for {1} ({0}) ...", // new Object[] {getType(), getDescription()}); } }
/** * The Dialog Class * * @author Frank Kunz The dialog class draws the basic dialog with a grid layout. The dialog * consists of three main parts. A settings panel, a table panel and three buttons. */ public final class UARTProtocolAnalysisDialog extends BaseAsyncToolDialog<UARTDataSet, UARTAnalyserWorker> { // INNER TYPES /** Provides a combobox renderer for {@link UARTParity} values. */ static final class UARTParityItemRenderer extends EnumItemRenderer<UARTParity> { // CONSTANTS private static final long serialVersionUID = 1L; // METHODS /** * @see * nl.lxtreme.ols.client.diagram.settings.GeneralSettingsDialog.EnumItemRenderer#getDisplayValue(java.lang.Enum) */ @Override protected String getDisplayValue(final UARTParity aValue) { String text = super.getDisplayValue(aValue); if (UARTParity.EVEN.equals(aValue)) { text = "Even parity"; } else if (UARTParity.NONE.equals(aValue)) { text = "No parity"; } else if (UARTParity.ODD.equals(aValue)) { text = "Odd parity"; } return text; } } /** Provides a combobox renderer for {@link UARTStopBits} values. */ static final class UARTStopBitsItemRenderer extends EnumItemRenderer<UARTStopBits> { // CONSTANTS private static final long serialVersionUID = 1L; // METHODS /** * @see * nl.lxtreme.ols.client.diagram.settings.GeneralSettingsDialog.EnumItemRenderer#getDisplayValue(java.lang.Enum) */ @Override protected String getDisplayValue(final UARTStopBits aValue) { String text = super.getDisplayValue(aValue); if (UARTStopBits.STOP_1.equals(aValue)) { text = "1"; } else if (UARTStopBits.STOP_15.equals(aValue)) { text = "1.5"; } else if (UARTStopBits.STOP_2.equals(aValue)) { text = "2"; } return text; } } // CONSTANTS private static final long serialVersionUID = 1L; private static final Logger LOG = Logger.getLogger(UARTProtocolAnalysisDialog.class.getName()); // VARIABLES private JComboBox rxd; private JComboBox txd; private JComboBox cts; private JComboBox rts; private JComboBox dtr; private JComboBox dsr; private JComboBox dcd; private JComboBox ri; private JComboBox parity; private JComboBox bits; private JComboBox stop; private JCheckBox inv; private JEditorPane outText; private RestorableAction runAnalysisAction; private Action exportAction; private Action closeAction; // CONSTRUCTORS /** * @param aOwner * @param aName */ public UARTProtocolAnalysisDialog(final Window aOwner, final String aName) { super(aOwner, aName); initDialog(); setLocationRelativeTo(getOwner()); } // METHODS /** * This is the UART protocol decoder core The decoder scans for a decode start event like CS high * to low edge or the trigger of the captured data. After this the decoder starts to decode the * data by the selected mode, number of bits and bit order. The decoded data are put to a JTable * object directly. */ @Override public void onToolWorkerReady(final UARTDataSet aAnalysisResult) { super.onToolWorkerReady(aAnalysisResult); try { final String htmlPage; if (aAnalysisResult != null) { htmlPage = toHtmlPage(null /* aFile */, aAnalysisResult); this.exportAction.setEnabled(!aAnalysisResult.isEmpty()); } else { htmlPage = getEmptyHtmlPage(); this.exportAction.setEnabled(false); } this.outText.setText(htmlPage); this.outText.setEditable(false); this.runAnalysisAction.restore(); } catch (final IOException exception) { // Make sure to handle IO-interrupted exceptions properly! if (!HostUtils.handleInterruptedException(exception)) { // Should not happen in this situation! throw new RuntimeException(exception); } } } /** @see nl.lxtreme.ols.api.Configurable#readPreferences(nl.lxtreme.ols.api.UserSettings) */ @Override public void readPreferences(final UserSettings aSettings) { this.rxd.setSelectedIndex(aSettings.getInt("rxd", this.rxd.getSelectedIndex())); this.txd.setSelectedIndex(aSettings.getInt("txd", this.txd.getSelectedIndex())); this.cts.setSelectedIndex(aSettings.getInt("cts", this.cts.getSelectedIndex())); this.rts.setSelectedIndex(aSettings.getInt("rts", this.rts.getSelectedIndex())); this.dtr.setSelectedIndex(aSettings.getInt("dtr", this.dtr.getSelectedIndex())); this.dsr.setSelectedIndex(aSettings.getInt("dsr", this.dsr.getSelectedIndex())); this.dcd.setSelectedIndex(aSettings.getInt("dcd", this.dcd.getSelectedIndex())); this.ri.setSelectedIndex(aSettings.getInt("ri", this.ri.getSelectedIndex())); this.parity.setSelectedIndex(aSettings.getInt("parity", this.parity.getSelectedIndex())); this.bits.setSelectedIndex(aSettings.getInt("bits", this.bits.getSelectedIndex())); this.stop.setSelectedIndex(aSettings.getInt("stop", this.stop.getSelectedIndex())); this.inv.setSelected(aSettings.getBoolean("inverted", this.inv.isSelected())); } /** @see nl.lxtreme.ols.tool.base.ToolDialog#reset() */ @Override public void reset() { this.outText.setText(getEmptyHtmlPage()); this.outText.setEditable(false); this.exportAction.setEnabled(false); this.runAnalysisAction.restore(); setControlsEnabled(true); } /** @see nl.lxtreme.ols.api.Configurable#writePreferences(nl.lxtreme.ols.api.UserSettings) */ @Override public void writePreferences(final UserSettings aSettings) { aSettings.putInt("rxd", this.rxd.getSelectedIndex()); aSettings.putInt("txd", this.txd.getSelectedIndex()); aSettings.putInt("cts", this.cts.getSelectedIndex()); aSettings.putInt("rts", this.rts.getSelectedIndex()); aSettings.putInt("dtr", this.dtr.getSelectedIndex()); aSettings.putInt("dsr", this.dsr.getSelectedIndex()); aSettings.putInt("dcd", this.dcd.getSelectedIndex()); aSettings.putInt("ri", this.ri.getSelectedIndex()); aSettings.putInt("parity", this.parity.getSelectedIndex()); aSettings.putInt("bits", this.bits.getSelectedIndex()); aSettings.putInt("stop", this.stop.getSelectedIndex()); aSettings.putBoolean("inverted", this.inv.isSelected()); } /** * set the controls of the dialog enabled/disabled * * @param aEnable status of the controls */ @Override protected void setControlsEnabled(final boolean aEnable) { this.rxd.setEnabled(aEnable); this.txd.setEnabled(aEnable); this.cts.setEnabled(aEnable); this.rts.setEnabled(aEnable); this.dtr.setEnabled(aEnable); this.dsr.setEnabled(aEnable); this.dcd.setEnabled(aEnable); this.ri.setEnabled(aEnable); this.parity.setEnabled(aEnable); this.bits.setEnabled(aEnable); this.stop.setEnabled(aEnable); this.inv.setEnabled(aEnable); this.closeAction.setEnabled(aEnable); } /** * @see * nl.lxtreme.ols.tool.base.BaseAsyncToolDialog#setupToolWorker(nl.lxtreme.ols.tool.base.BaseAsyncToolWorker) */ @Override protected void setupToolWorker(final UARTAnalyserWorker aToolWorker) { // The value at index zero is "Unused", so extracting one of all items // causes all "unused" values to be equivalent to -1, which is interpreted // as not used... aToolWorker.setRxdIndex(this.rxd.getSelectedIndex() - 1); aToolWorker.setTxdIndex(this.txd.getSelectedIndex() - 1); aToolWorker.setCtsIndex(this.cts.getSelectedIndex() - 1); aToolWorker.setRtsIndex(this.rts.getSelectedIndex() - 1); aToolWorker.setDcdIndex(this.dcd.getSelectedIndex() - 1); aToolWorker.setRiIndex(this.ri.getSelectedIndex() - 1); aToolWorker.setDsrIndex(this.dsr.getSelectedIndex() - 1); aToolWorker.setDtrIndex(this.dtr.getSelectedIndex() - 1); // Other properties... aToolWorker.setInverted(this.inv.isSelected()); aToolWorker.setParity((UARTParity) this.parity.getSelectedItem()); aToolWorker.setStopBits((UARTStopBits) this.stop.getSelectedItem()); aToolWorker.setBitCount(NumberUtils.smartParseInt((String) this.bits.getSelectedItem(), 8)); } /** * exports the data to a CSV file * * @param aFile File object */ @Override protected void storeToCsvFile(final File aFile, final UARTDataSet aDataSet) { try { final CsvExporter exporter = ExportUtils.createCsvExporter(aFile); exporter.setHeaders( "index", "start-time", "end-time", "event?", "event-type", "RxD event", "TxD event", "RxD data", "TxD data"); final List<UARTData> decodedData = aDataSet.getData(); for (int i = 0; i < decodedData.size(); i++) { final UARTData ds = decodedData.get(i); final String startTime = aDataSet.getDisplayTime(ds.getStartSampleIndex()); final String endTime = aDataSet.getDisplayTime(ds.getEndSampleIndex()); String eventType = null; String rxdEvent = null; String txdEvent = null; String rxdData = null; String txdData = null; switch (ds.getType()) { case UARTData.UART_TYPE_EVENT: eventType = ds.getEventName(); break; case UARTData.UART_TYPE_RXEVENT: rxdEvent = ds.getEventName(); break; case UARTData.UART_TYPE_TXEVENT: txdEvent = ds.getEventName(); break; case UARTData.UART_TYPE_RXDATA: rxdData = Integer.toString(ds.getData()); break; case UARTData.UART_TYPE_TXDATA: txdData = Integer.toString(ds.getData()); break; default: break; } exporter.addRow( Integer.valueOf(i), startTime, endTime, Boolean.valueOf(ds.isEvent()), eventType, rxdEvent, txdEvent, rxdData, txdData); } exporter.close(); } catch (final IOException exception) { // Make sure to handle IO-interrupted exceptions properly! if (!HostUtils.handleInterruptedException(exception)) { LOG.log(Level.WARNING, "CSV export failed!", exception); } } } /** * stores the data to a HTML file * * @param aFile file object */ @Override protected void storeToHtmlFile(final File aFile, final UARTDataSet aDataSet) { try { toHtmlPage(aFile, aDataSet); } catch (final IOException exception) { // Make sure to handle IO-interrupted exceptions properly! if (!HostUtils.handleInterruptedException(exception)) { LOG.log(Level.WARNING, "HTML export failed!", exception); } } } /** * Creates the HTML template for exports to HTML. * * @param aExporter the HTML exporter instance to use, cannot be <code>null</code>. * @return a HTML exporter filled with the template, never <code>null</code>. */ private HtmlExporter createHtmlTemplate(final HtmlExporter aExporter) { aExporter.addCssStyle("body { font-family: sans-serif; } "); aExporter.addCssStyle( "table { border-width: 1px; border-spacing: 0px; border-color: gray;" + " border-collapse: collapse; border-style: solid; margin-bottom: 15px; } "); aExporter.addCssStyle( "table th { border-width: 1px; padding: 2px; border-style: solid; border-color: gray;" + " background-color: #C0C0FF; text-align: left; font-weight: bold; font-family: sans-serif; } "); aExporter.addCssStyle( "table td { border-width: 1px; padding: 2px; border-style: solid; border-color: gray;" + " font-family: monospace; } "); aExporter.addCssStyle(".error { color: red; } "); aExporter.addCssStyle(".warning { color: orange; } "); aExporter.addCssStyle(".date { text-align: right; font-size: x-small; margin-bottom: 15px; } "); aExporter.addCssStyle(".w100 { width: 100%; } "); aExporter.addCssStyle(".w35 { width: 35%; } "); aExporter.addCssStyle(".w30 { width: 30%; } "); aExporter.addCssStyle(".w15 { width: 15%; } "); aExporter.addCssStyle(".w10 { width: 10%; } "); aExporter.addCssStyle(".w8 { width: 8%; } "); aExporter.addCssStyle(".w7 { width: 7%; } "); final Element body = aExporter.getBody(); body.addChild(H1).addContent("UART Analysis results"); body.addChild(HR); body.addChild(DIV).addAttribute("class", "date").addContent("Generated: ", "{date-now}"); Element table, tr, thead, tbody; table = body.addChild(TABLE).addAttribute("class", "w100"); tbody = table.addChild(TBODY); tr = tbody.addChild(TR); tr.addChild(TH).addAttribute("colspan", "2").addContent("Statistics"); tr = tbody.addChild(TR); tr.addChild(TD).addAttribute("class", "w30").addContent("Decoded bytes"); tr.addChild(TD).addContent("{decoded-bytes}"); tr = tbody.addChild(TR); tr.addChild(TD).addAttribute("class", "w30").addContent("Detected bus errors"); tr.addChild(TD).addContent("{detected-bus-errors}"); tr = tbody.addChild(TR); tr.addChild(TD).addAttribute("class", "w30").addContent("Baudrate"); tr.addChild(TD).addContent("{baudrate}"); table = body.addChild(TABLE).addAttribute("class", "w100"); thead = table.addChild(THEAD); tr = thead.addChild(TR); tr.addChild(TH).addAttribute("class", "w30").addAttribute("colspan", "2"); tr.addChild(TH).addAttribute("class", "w35").addAttribute("colspan", "4").addContent("RxD"); tr.addChild(TH).addAttribute("class", "w35").addAttribute("colspan", "4").addContent("TxD"); tr = thead.addChild(TR); tr.addChild(TH).addAttribute("class", "w15").addContent("Index"); tr.addChild(TH).addAttribute("class", "w15").addContent("Time"); tr.addChild(TH).addAttribute("class", "w10").addContent("Hex"); tr.addChild(TH).addAttribute("class", "w10").addContent("Bin"); tr.addChild(TH).addAttribute("class", "w8").addContent("Dec"); tr.addChild(TH).addAttribute("class", "w7").addContent("ASCII"); tr.addChild(TH).addAttribute("class", "w10").addContent("Hex"); tr.addChild(TH).addAttribute("class", "w10").addContent("Bin"); tr.addChild(TH).addAttribute("class", "w8").addContent("Dec"); tr.addChild(TH).addAttribute("class", "w7").addContent("ASCII"); tbody = table.addChild(TBODY); tbody.addContent("{decoded-data}"); return aExporter; } /** @return */ private JPanel createPreviewPane() { final JPanel panTable = new JPanel(new GridLayout(1, 1, 0, 0)); this.outText = new JEditorPane("text/html", getEmptyHtmlPage()); this.outText.setEditable(false); panTable.add(new JScrollPane(this.outText)); return panTable; } /** @return */ private JPanel createSettingsPane() { final String channels[] = new String[33]; channels[0] = "Unused"; for (int i = 0; i < 32; i++) { channels[i + 1] = new String("Channel " + i); } final JPanel settings = new JPanel(new SpringLayout()); SpringLayoutUtils.addSeparator(settings, "Settings"); settings.add(createRightAlignedLabel("RxD")); this.rxd = new JComboBox(channels); this.rxd.setSelectedIndex(0); settings.add(this.rxd); settings.add(createRightAlignedLabel("TxD")); this.txd = new JComboBox(channels); this.txd.setSelectedIndex(0); settings.add(this.txd); settings.add(createRightAlignedLabel("CTS")); this.cts = new JComboBox(channels); this.cts.setSelectedIndex(0); settings.add(this.cts); settings.add(createRightAlignedLabel("RTS")); this.rts = new JComboBox(channels); this.rts.setSelectedIndex(0); settings.add(this.rts); settings.add(createRightAlignedLabel("DTR")); this.dtr = new JComboBox(channels); this.dtr.setSelectedIndex(0); settings.add(this.dtr); settings.add(createRightAlignedLabel("DSR")); this.dsr = new JComboBox(channels); this.dsr.setSelectedIndex(0); settings.add(this.dsr); settings.add(createRightAlignedLabel("DCD")); this.dcd = new JComboBox(channels); this.dcd.setSelectedIndex(0); settings.add(this.dcd); settings.add(createRightAlignedLabel("RI")); this.ri = new JComboBox(channels); this.ri.setSelectedIndex(0); settings.add(this.ri); settings.add(createRightAlignedLabel("Parity")); this.parity = new JComboBox(UARTParity.values()); this.parity.setSelectedIndex(0); this.parity.setRenderer(new UARTParityItemRenderer()); settings.add(this.parity); settings.add(createRightAlignedLabel("Bits")); final String[] bitarray = new String[4]; for (int i = 0; i < bitarray.length; i++) { bitarray[i] = String.format("%d", Integer.valueOf(i + 5)); } this.bits = new JComboBox(bitarray); this.bits.setSelectedIndex(3); settings.add(this.bits); settings.add(createRightAlignedLabel("Stopbits")); this.stop = new JComboBox(UARTStopBits.values()); this.stop.setSelectedIndex(0); this.stop.setRenderer(new UARTStopBitsItemRenderer()); settings.add(this.stop); this.inv = new JCheckBox(); this.inv.setSelected(false); settings.add(createRightAlignedLabel("Invert?")); settings.add(this.inv); SpringLayoutUtils.makeEditorGrid(settings, 10, 4); return settings; } /** * generate a HTML page * * @param empty if this is true an empty output is generated * @return String with HTML data */ private String getEmptyHtmlPage() { final HtmlExporter exporter = createHtmlTemplate(ExportUtils.createHtmlExporter()); return exporter.toString( new MacroResolver() { @Override public Object resolve(final String aMacro, final Element aParent) { if ("date-now".equals(aMacro)) { final DateFormat df = DateFormat.getDateInstance(DateFormat.LONG); return df.format(new Date()); } else if ("decoded-bytes".equals(aMacro) || "detected-bus-errors".equals(aMacro) || "baudrate".equals(aMacro)) { return "-"; } else if ("decoded-data".equals(aMacro)) { return null; } return null; } }); } /** Initializes this dialog. */ private void initDialog() { setMinimumSize(new Dimension(640, 480)); final JComponent settingsPane = createSettingsPane(); final JComponent previewPane = createPreviewPane(); final JPanel contentPane = new JPanel(new GridBagLayout()); contentPane.add( settingsPane, new GridBagConstraints( 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.NONE, new Insets(2, 0, 2, 0), 0, 0)); contentPane.add( previewPane, new GridBagConstraints( 1, 0, 1, 1, 1.0, 1.0, GridBagConstraints.NORTH, GridBagConstraints.BOTH, new Insets(2, 0, 2, 0), 0, 0)); final JButton runAnalysisButton = createRunAnalysisButton(); this.runAnalysisAction = (RestorableAction) runAnalysisButton.getAction(); final JButton exportButton = createExportButton(); this.exportAction = exportButton.getAction(); this.exportAction.setEnabled(false); final JButton closeButton = createCloseButton(); this.closeAction = closeButton.getAction(); final JComponent buttons = SwingComponentUtils.createButtonPane(runAnalysisButton, exportButton, closeButton); SwingComponentUtils.setupDialogContentPane(this, contentPane, buttons, runAnalysisButton); } /** * generate a HTML page * * @param empty if this is true an empty output is generated * @return String with HTML data */ private String toHtmlPage(final File aFile, final UARTDataSet aDataSet) throws IOException { final int bitCount = Integer.parseInt((String) this.bits.getSelectedItem()); final int bitAdder = (bitCount % 4 != 0) ? 1 : 0; final MacroResolver macroResolver = new MacroResolver() { @Override public Object resolve(final String aMacro, final Element aParent) { if ("date-now".equals(aMacro)) { final DateFormat df = DateFormat.getDateInstance(DateFormat.LONG); return df.format(new Date()); } else if ("decoded-bytes".equals(aMacro)) { return Integer.valueOf(aDataSet.getDecodedSymbols()); } else if ("detected-bus-errors".equals(aMacro)) { return Integer.valueOf(aDataSet.getDetectedErrors()); } else if ("baudrate".equals(aMacro)) { final String baudrate; if (aDataSet.getBitLength() <= 0) { baudrate = "<span class='error'>Baudrate calculation failed!</span>"; } else { baudrate = String.format( "%d (exact: %d)", Integer.valueOf(aDataSet.getBaudRate()), Integer.valueOf(aDataSet.getBaudRateExact())); if (aDataSet.getBitLength() < 15) { return baudrate.concat( " <span class='warning'>The baudrate may be wrong, use a higher samplerate to avoid this!</span>"); } return baudrate; } } else if ("decoded-data".equals(aMacro)) { final List<UARTData> decodedData = aDataSet.getData(); Element tr; for (int i = 0; i < decodedData.size(); i++) { final UARTData ds = decodedData.get(i); if (ds.isEvent()) { String rxEventData = ""; String txEventData = ""; String bgColor; if (UARTData.UART_TYPE_EVENT == ds.getType()) { rxEventData = txEventData = ds.getEventName(); bgColor = "#e0e0e0"; } else if (UARTData.UART_TYPE_RXEVENT == ds.getType()) { rxEventData = ds.getEventName(); bgColor = "#c0ffc0"; } else if (UARTData.UART_TYPE_TXEVENT == ds.getType()) { txEventData = ds.getEventName(); bgColor = "#c0ffc0"; } else { // unknown event bgColor = "#ff8000"; } if (txEventData.endsWith("_ERR") || rxEventData.endsWith("_ERR")) { bgColor = "#ff8000"; } tr = aParent .addChild(TR) .addAttribute("style", "background-color: " + bgColor + ";"); tr.addChild(TD).addContent(String.valueOf(i)); tr.addChild(TD).addContent(aDataSet.getDisplayTime(ds.getStartSampleIndex())); tr.addChild(TD).addContent(rxEventData); tr.addChild(TD); tr.addChild(TD); tr.addChild(TD); tr.addChild(TD).addContent(txEventData); tr.addChild(TD); tr.addChild(TD); tr.addChild(TD); } else { String rxDataHex = "", rxDataBin = "", rxDataDec = "", rxDataASCII = ""; String txDataHex = "", txDataBin = "", txDataDec = "", txDataASCII = ""; // Normal data... if (UARTData.UART_TYPE_RXDATA == ds.getType()) { final int rxData = ds.getData(); rxDataHex = "0x" + DisplayUtils.integerToHexString(rxData, bitCount / 4 + bitAdder); rxDataBin = "0b" + DisplayUtils.integerToBinString(rxData, bitCount); rxDataDec = String.valueOf(rxData); if ((rxData >= 32) && (bitCount == 8)) { rxDataASCII = String.valueOf((char) rxData); } } else /* if ( UARTData.UART_TYPE_TXDATA == ds.getType() ) */ { final int txData = ds.getData(); txDataHex = "0x" + DisplayUtils.integerToHexString(txData, bitCount / 4 + bitAdder); txDataBin = "0b" + DisplayUtils.integerToBinString(txData, bitCount); txDataDec = String.valueOf(txData); if ((txData >= 32) && (bitCount == 8)) { txDataASCII = String.valueOf((char) txData); } } tr = aParent.addChild(TR); tr.addChild(TD).addContent(String.valueOf(i)); tr.addChild(TD).addContent(aDataSet.getDisplayTime(ds.getStartSampleIndex())); tr.addChild(TD).addContent(rxDataHex); tr.addChild(TD).addContent(rxDataBin); tr.addChild(TD).addContent(rxDataDec); tr.addChild(TD).addContent(rxDataASCII); tr.addChild(TD).addContent(txDataHex); tr.addChild(TD).addContent(txDataBin); tr.addChild(TD).addContent(txDataDec); tr.addChild(TD).addContent(txDataASCII); } } } return null; } }; if (aFile == null) { final HtmlExporter exporter = createHtmlTemplate(ExportUtils.createHtmlExporter()); return exporter.toString(macroResolver); } else { final HtmlFileExporter exporter = (HtmlFileExporter) createHtmlTemplate(ExportUtils.createHtmlExporter(aFile)); exporter.write(macroResolver); exporter.close(); } return null; } }