Package nl.lxtreme.ols.client.signaldisplay.view

Source Code of nl.lxtreme.ols.client.signaldisplay.view.MeasurementView$SignalMeasurerWorker

/*
* OpenBench LogicSniffer / SUMP project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
*
* Copyright (C) 2006-2010 Michael Poppitz, www.sump.org
* Copyright (C) 2010-2012 J.W. Janssen, www.lxtreme.nl
*/
package nl.lxtreme.ols.client.signaldisplay.view;


import static nl.lxtreme.ols.client.signaldisplay.view.ViewUtils.*;
import static nl.lxtreme.ols.util.swing.SwingComponentUtils.*;

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;

import javax.swing.*;

import nl.lxtreme.ols.api.acquisition.*;
import nl.lxtreme.ols.api.data.*;
import nl.lxtreme.ols.api.data.Cursor;
import nl.lxtreme.ols.client.action.*;
import nl.lxtreme.ols.client.actionmanager.*;
import nl.lxtreme.ols.client.signaldisplay.*;
import nl.lxtreme.ols.client.signaldisplay.model.*;
import nl.lxtreme.ols.client.signaldisplay.signalelement.*;
import nl.lxtreme.ols.client.signaldisplay.util.*;
import nl.lxtreme.ols.client.signaldisplay.util.CursorFlagTextFormatter.*;
import nl.lxtreme.ols.util.swing.*;
import nl.lxtreme.ols.util.swing.component.*;


/**
* Provides a simple measurement component for measuring some common aspects of
* signals, such as frequency, # of pulses and so on.
*/
public class MeasurementView extends AbstractViewLayer implements IToolWindow, ISignalElementChangeListener,
    ICursorChangeListener, IMeasurementListener
{
  // INNER TYPES

  /**
   * {@link ActionListener} implementation for the channel comboboxes.
   */
  final class ChannelActionListener implements ActionListener
  {
    /**
     * {@inheritDoc}
     */
    @Override
    public void actionPerformed( final ActionEvent aEvent )
    {
      final Collection<Channel> channelList = getAllChannels();
      updateChannelModel( channelList );
    }
  }

  /**
   * Provides a renderer for the channels combobox.
   */
  static final class ChannelComboBoxRenderer extends DefaultListCellRenderer
  {
    private static final long serialVersionUID = 1L;

    @Override
    public Component getListCellRendererComponent( final JList aList, final Object aValue, final int aIndex,
        final boolean aIsSelected, final boolean aCellHasFocus )
    {
      StringBuilder sb = new StringBuilder();
      if ( ( aValue != null ) && ( aValue instanceof Channel ) )
      {
        final Channel channel = ( Channel )aValue;
        sb.append( channel.getIndex() );
        if ( channel.hasName() )
        {
          sb.append( ", " ).append( channel.getLabel() );
        }
      }

      return super.getListCellRendererComponent( aList, sb.toString(), aIndex, aIsSelected, aCellHasFocus );
    }
  }

  /**
   * {@link ActionListener} implementation for the cursor comboboxes.
   */
  final class CursorActionListener implements ActionListener
  {
    /**
     * {@inheritDoc}
     */
    @Override
    public void actionPerformed( final ActionEvent aEvent )
    {
      updateCursorModels();
    }
  }

  /**
   * Provides a renderer for the cursor combobox.
   */
  final class CursorComboBoxRenderer extends DefaultListCellRenderer
  {
    private static final long serialVersionUID = 1L;

    @Override
    public Component getListCellRendererComponent( final JList aList, final Object aValue, final int aIndex,
        final boolean aIsSelected, final boolean aCellHasFocus )
    {
      String text;
      if ( ( aValue != null ) && ( aValue instanceof Cursor ) )
      {
        final Cursor cursor = ( Cursor )aValue;
        text = CursorFlagTextFormatter.getCursorFlagText( getSignalDiagramModel(), cursor, LabelStyle.LABEL_TIME );
      }
      else if ( aValue != null )
      {
        text = String.valueOf( aValue );
      }
      else
      {
        text = "";
      }

      return super.getListCellRendererComponent( aList, text, aIndex, aIsSelected, aCellHasFocus );
    }
  }

  /**
   * Represents a small DTO for measured pulse count information.
   */
  static final class PulseCountInfo
  {
    final Double measureTime;
    final Integer risingEdgeCount;
    final Integer fallingEdgeCount;
    final Integer totalEdgeCount;
    final long totalLowTime;
    final long totalHighTime;
    final Integer pulseCount;
    final int sampleRate;
    final boolean hasTimingData;

    /**
     * Creates a new {@link PulseCountInfo} instance.
     */
    public PulseCountInfo( final double aMeasureTime, final int aRisingEdgeCount, final int aFallingEdgeCount,
        final long aTotalLowTime, final long aTotalHighTime, final int aSampleRate, final boolean aHasTimingData )
    {
      this.measureTime = Double.valueOf( aMeasureTime );
      this.risingEdgeCount = Integer.valueOf( aRisingEdgeCount );
      this.fallingEdgeCount = Integer.valueOf( aFallingEdgeCount );
      this.totalEdgeCount = Integer.valueOf( aRisingEdgeCount + aFallingEdgeCount );
      this.totalLowTime = aTotalLowTime;
      this.totalHighTime = aTotalHighTime;
      this.pulseCount = Integer.valueOf( this.totalEdgeCount.intValue() / 2 );
      this.sampleRate = aSampleRate;
      this.hasTimingData = aHasTimingData;
    }

    /**
     * @return
     */
    public double getDutyCycle()
    {
      final double avgHighTime = getAveragePulseHighTime();
      final double avgLowTime = getAveragePulseLowTime();
      return 100.0 * ( avgHighTime / ( avgHighTime + avgLowTime ) );
    }

    /**
     * @return
     */
    public Double getFrequency()
    {
      return Double.valueOf( this.sampleRate / ( getAveragePulseHighTime() + getAveragePulseLowTime() ) );
    }

    /**
     * @return
     */
    private double getAveragePulseHighTime()
    {
      return ( this.totalHighTime / this.fallingEdgeCount.doubleValue() );
    }

    /**
     * @return
     */
    private double getAveragePulseLowTime()
    {
      return ( this.totalLowTime / this.risingEdgeCount.doubleValue() );
    }
  }

  /**
   * Does the actual measurement of the signal.
   */
  static final class SignalMeasurer
  {
    // VARIABLES

    private final AcquisitionResult result;
    private final int mask;
    private final long startTimestamp;
    private final long endTimestamp;

    // CONSTRUCTORS

    /**
     * Creates a new {@link SignalMeasurer} instance.
     */
    public SignalMeasurer( final AcquisitionResult aResult, final int aIndex, final long aStartTimestamp,
        final long aEndTimestamp )
    {
      this.result = aResult;
      this.mask = ( 1 << aIndex );
      this.startTimestamp = aStartTimestamp;
      this.endTimestamp = aEndTimestamp;
    }

    // METHODS

    /**
     * Executes the actual measurement.
     *
     * @return the measurement information, never <code>null</code>.
     */
    public PulseCountInfo run()
    {
      final int startIdx = this.result.getSampleIndex( this.startTimestamp );
      final int endIdx = this.result.getSampleIndex( this.endTimestamp );

      final boolean hasTimingData = this.result.hasTimingData();

      final int[] values = this.result.getValues();
      final long[] timestamps = this.result.getTimestamps();

      int fallingEdgeCount = 0;
      long highTime = 0;
      int risingEdgeCount = 0;
      long lowTime = 0;

      int i = startIdx;
      long lastTransition = timestamps[i];
      int lastBitValue = values[i++] & this.mask;

      for ( ; !Thread.currentThread().isInterrupted() && ( i <= endIdx ); i++ )
      {
        final int bitValue = values[i] & this.mask;
        final Edge edge = Edge.toEdge( lastBitValue, bitValue );

        if ( !edge.isNone() )
        {
          final long periodTime = timestamps[i] - lastTransition;
          lastTransition = timestamps[i];

          if ( edge.isRising() )
          {
            // Low to high transition: previously seen a low-state...
            risingEdgeCount++;
            lowTime += periodTime;
          }
          else
          /* if ( edge.isFalling() ) */
          {
            // High to low transition: previously seen a high-state...
            fallingEdgeCount++;
            highTime += periodTime;
          }
        }

        lastBitValue = bitValue;
      }

      final double measureTime = Math.abs( ( this.endTimestamp - this.startTimestamp )
          / ( double )this.result.getSampleRate() );

      return new PulseCountInfo( measureTime, risingEdgeCount, fallingEdgeCount, lowTime, highTime,
          this.result.getSampleRate(), hasTimingData );
    }
  }

  /**
   * Provides a {@link SwingWorker} to measure the frequency, dutycycle and such
   * asynchronously from the UI.
   */
  final class SignalMeasurerWorker extends SwingWorker<PulseCountInfo, Boolean>
  {
    // VARIABLES

    private final int index;
    private final long startTimestamp;
    private final long endTimestamp;

    // CONSTRUCTORS

    /**
     * Creates a new {@link SignalMeasurerWorker} instance.
     *
     * @param aChannel
     *          the channel to measure;
     * @param aCursorA
     *          the cursor denoting the start of measurement;
     * @param aCursorB
     *          the cursor denoting the end of measurement.
     */
    public SignalMeasurerWorker( final Channel aChannel, final Cursor aCursorA, final Cursor aCursorB )
    {
      this.index = aChannel.getIndex();
      this.startTimestamp = aCursorA != null ? aCursorA.getTimestamp() : -1L;
      this.endTimestamp = aCursorB != null ? aCursorB.getTimestamp() : -1L;
    }

    // METHODS

    /**
     * {@inheritDoc}
     */
    @Override
    protected PulseCountInfo doInBackground() throws Exception
    {
      final SignalDiagramModel model = getSignalDiagramModel();

      long start = this.startTimestamp;
      if ( start < 0L )
      {
        start = model.getTimestamps()[0];
      }
      long end = this.endTimestamp;
      if ( end < 0L )
      {
        end = model.getAbsoluteLength();
      }

      return new SignalMeasurer( model.getCapturedData(), this.index, start, end ).run();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void done()
    {
      try
      {
        updatePulseCountInformation( get() );

        repaint( 50L );
      }
      catch ( Exception exception )
      {
        exception.printStackTrace();
      }
      finally
      {
        MeasurementView.this.indicator.setVisible( false );
      }
    }
  }

  // CONSTANTS

  /** The identifier of this tool-window view. */
  public static final String ID = "Measure";

  private static final long serialVersionUID = 1L;

  // VARIABLES

  private JCheckBox enableMeasurementMode;
  private JComboBox measureChannel;
  private JComboBox cursorA;
  private JComboBox cursorB;
  private JBusyIndicator indicator;

  private final JLabel mi_channel;
  private final JLabel mi_referenceLabel;
  private final JLabel mi_reference;
  private final JLabel mi_period;
  private final JLabel mi_frequency;
  private final JLabel mi_widthHigh;
  private final JLabel mi_widthLow;
  private final JLabel mi_dutyCycle;

  private final JLabel pci_timeLabel;
  private final JLabel pci_time;
  private final JLabel pci_frequency;
  private final JLabel pci_dutyCycle;
  private final JLabel pci_pulseCountLabel;
  private final JLabel pci_pulseCount;

  private volatile boolean listening;
  private volatile SignalMeasurerWorker signalMeasurerWorker;

  private final List<Component> comps;

  // CONSTRUCTORS

  /**
   * Creates a new MeasurementView instance.
   *
   * @param aController
   */
  public MeasurementView( final SignalDiagramController aController )
  {
    super( aController );

    this.comps = new ArrayList<Component>();

    this.mi_channel = new JLabel();
    this.mi_referenceLabel = new JLabel( "Time:" );
    this.mi_referenceLabel.setHorizontalAlignment( SwingConstants.RIGHT );
    this.mi_reference = new JLabel();
    this.mi_period = new JLabel();
    this.mi_frequency = new JLabel();
    this.mi_widthHigh = new JLabel();
    this.mi_widthLow = new JLabel();
    this.mi_dutyCycle = new JLabel();

    this.pci_timeLabel = new JLabel();
    this.pci_timeLabel.setHorizontalAlignment( SwingConstants.RIGHT );
    this.pci_time = new JLabel();
    this.pci_frequency = new JLabel();
    this.pci_dutyCycle = new JLabel();
    this.pci_pulseCountLabel = new JLabel( "# of pulses:" );
    this.pci_pulseCountLabel.setHorizontalAlignment( SwingConstants.RIGHT );
    this.pci_pulseCount = new JLabel();

    initComponent();

    this.listening = true;
  }

  // METHODS

  /**
   * Factory method to create a new {@link MeasurementView} instance.
   *
   * @param aController
   *          the controller to use for the SignalDetailsView instance, cannot
   *          be <code>null</code>.
   * @return a new {@link MeasurementView} instance, never <code>null</code>.
   */
  public static MeasurementView create( final SignalDiagramController aController )
  {
    final MeasurementView result = new MeasurementView( aController );

    aController.addChannelChangeListener( result );
    aController.addCursorChangeListener( result );
    aController.addMeasurementListener( result );

    return result;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void cursorAdded( final Cursor aCursor )
  {
    if ( this.listening )
    {
      updateCursorModels();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void cursorChanged( final String aPropertyName, final Cursor aOldCursor, final Cursor aNewCursor )
  {
    if ( this.listening && !ICursorChangeListener.PROPERTY_COLOR.equals( aPropertyName ) )
    {
      updateCursorModels();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void cursorRemoved( final Cursor aOldCursor )
  {
    if ( this.listening )
    {
      updateCursorModels();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void cursorsInvisible()
  {
    if ( this.listening )
    {
      updateCursorModels();
    }

    setState( false );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void cursorsVisible()
  {
    if ( this.listening )
    {
      updateCursorModels();
    }

    setState( true );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void disableMeasurementMode()
  {
    if ( this.listening )
    {
      updateCursorModels();
    }

    setState( false );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void enableMeasurementMode()
  {
    if ( this.listening )
    {
      updateCursorModels();
    }

    setState( true );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Icon getIcon()
  {
    return null; // XXX
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String getId()
  {
    return ID;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void groupStructureChanged( final Collection<SignalElement> aSignalElements )
  {
    if ( this.listening )
    {
      updateChannelModel( getAllChannels( aSignalElements ) );
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void handleMeasureEvent( final MeasurementInfo aEvent )
  {
    updateMeasurementInformation( aEvent );

    SwingComponentUtils.invokeOnEDT( new Runnable()
    {
      @Override
      public void run()
      {
        repaint( 50L );
      }
    } );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isListening()
  {
    return this.listening;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void signalElementMoved( final ElementMoveEvent aEvent )
  {
    // NO-op
  }

  /**
   * Returns all available channels.
   *
   * @return a collection of all channels, never <code>null</code>.
   */
  final Collection<Channel> getAllChannels()
  {
    return getAllChannels( getSignalElementManager().getAllElements() );
  }

  /**
   * @return a signal diagram model, never <code>null</code>.
   */
  final SignalDiagramModel getSignalDiagramModel()
  {
    return getController().getSignalDiagramModel();
  }

  /**
   * @param aMeasurementInfo
   * @return
   */
  final void updateMeasurementInformation( final MeasurementInfo aMeasurementInfo )
  {
    String channelId = "-";
    String referenceLabel = "Time:";
    String reference = "-";
    String totalWidth = "-";
    String frequency = "-";
    String pwHigh = "-";
    String pwLow = "-";
    String dc = "-";
    boolean hasTimingData = true;

    if ( aMeasurementInfo != null )
    {
      hasTimingData = aMeasurementInfo.hasTimingData();

      channelId = Integer.toString( aMeasurementInfo.getChannelIndex() );
      if ( aMeasurementInfo.getChannelLabel() != null )
      {
        channelId = channelId.concat( ", " ).concat( aMeasurementInfo.getChannelLabel() );
      }

      reference = formatReference( hasTimingData, aMeasurementInfo.getReferenceTime() );

      if ( hasTimingData )
      {
        totalWidth = formatTime( aMeasurementInfo.getTotalTime() );
        frequency = formatPeriodAsFrequency( aMeasurementInfo.getTotalTime() );
        pwHigh = formatTime( aMeasurementInfo.getHighTime() );
        pwLow = formatTime( aMeasurementInfo.getLowTime() );
        dc = formatDutyCycle( aMeasurementInfo.getDutyCycle() );
      }
      else
      {
        referenceLabel = "State:";
      }
    }

    this.mi_channel.setText( channelId );
    this.mi_referenceLabel.setText( referenceLabel );
    this.mi_reference.setText( reference );
    this.mi_frequency.setText( frequency );
    this.mi_period.setText( totalWidth );
    this.mi_widthHigh.setText( pwHigh );
    this.mi_widthLow.setText( pwLow );
    this.mi_dutyCycle.setText( dc );
  }

  /**
   * @param aPulseCountInfo
   * @return
   */
  final void updatePulseCountInformation( final PulseCountInfo aPulseCountInfo )
  {
    boolean hasTimingData = true;
    boolean hasPulses = false;

    String timeTextLabel = "\u0394T (B-A):";
    String timeText = "-";
    String frequencyText = "-";
    String dutyCycleText = "-";
    String pulseCountLabel = "Pulses:";
    String pulseCountText = "-";

    // Issue #150: use alternative characters to denote rising/falling edges...
    String upArrow = "\u02C4"; // \u2191 is not supported on Windows
    String downArrow = "\u02C5"; // \u2193 is not supported on Windows

    if ( aPulseCountInfo != null )
    {
      hasTimingData = aPulseCountInfo.hasTimingData;
      hasPulses = aPulseCountInfo.pulseCount.intValue() != 0;

      if ( hasTimingData )
      {
        timeText = formatTime( aPulseCountInfo.measureTime );

        pulseCountText = String.format( "%d (%s%d, %s%d)", aPulseCountInfo.pulseCount, upArrow,
            aPulseCountInfo.risingEdgeCount, downArrow, aPulseCountInfo.fallingEdgeCount );

        if ( hasPulses )
        {
          frequencyText = formatFrequency( aPulseCountInfo.getFrequency() );
          dutyCycleText = formatDutyCycle( Double.valueOf( aPulseCountInfo.getDutyCycle() ) );
        }
      }
      else
      {
        timeTextLabel = "\u0394S (B-A):";
        timeText = Integer.toString( aPulseCountInfo.measureTime.intValue() );

        pulseCountLabel = "Transitions:";
        pulseCountText = String.format( "%d", aPulseCountInfo.pulseCount );
        pulseCountText = String.format( "%d (%s%d, %s%d)", aPulseCountInfo.totalEdgeCount, upArrow,
            aPulseCountInfo.risingEdgeCount, downArrow, aPulseCountInfo.fallingEdgeCount );
      }
    }

    this.pci_timeLabel.setText( timeTextLabel );
    this.pci_time.setText( timeText );

    this.pci_frequency.setText( frequencyText );
    this.pci_dutyCycle.setText( dutyCycleText );

    this.pci_pulseCountLabel.setText( pulseCountLabel );
    this.pci_pulseCount.setText( pulseCountText );
  }

  /**
   * Updates the channel model to the current list of channels.
   */
  protected void updateChannelModel( final Collection<Channel> aChannels )
  {
    SwingComponentUtils.invokeOnEDT( new Runnable()
    {
      @Override
      public void run()
      {
        updateChannelComboBoxModel( MeasurementView.this.measureChannel, aChannels );

        updatePulseCountInformation( null );

        updateMeasurementInfo();

        repaint( 50L );
      }
    } );
  }

  /**
   * Updates both combobox models to the current list of available cursors.
   */
  protected void updateCursorModels()
  {
    SwingComponentUtils.invokeOnEDT( new Runnable()
    {
      @Override
      public void run()
      {
        updateCursorComboBoxModel( MeasurementView.this.cursorA );
        updateCursorComboBoxModel( MeasurementView.this.cursorB );

        updatePulseCountInformation( null );

        updateMeasurementInfo();

        repaint( 50L );
      }
    } );
  }

  /**
   * Determines whether all preconditions are met to perform a measurement.
   *
   * @return <code>true</code> if a measurement can be performed,
   *         <code>false</code> otherwise.
   */
  private boolean canPerformMeasurement()
  {
    if ( !getController().getSignalDiagramModel().isMeasurementMode() )
    {
      return false;
    }

    Channel channel = ( Channel )this.measureChannel.getSelectedItem();
    if ( ( channel == null ) || !channel.isEnabled() )
    {
      return false;
    }

    Cursor selectedCursorA = ( Cursor )this.cursorA.getSelectedItem();
    if ( ( selectedCursorA != null ) && !selectedCursorA.isDefined() )
    {
      return false;
    }

    Cursor selectedCursorB = ( Cursor )this.cursorB.getSelectedItem();
    if ( ( selectedCursorB != null ) && !selectedCursorB.isDefined() )
    {
      return false;
    }

    if ( ( selectedCursorA != null ) && ( selectedCursorB != null ) )
    {
      return selectedCursorA != selectedCursorB;
    }

    return true;
  }

  /**
   * Returns all available channels.
   *
   * @return a collection of all channels, never <code>null</code>.
   */
  private Collection<Channel> getAllChannels( final Collection<SignalElement> aSignalElements )
  {
    final List<Channel> channels = new ArrayList<Channel>();
    for ( SignalElement element : aSignalElements )
    {
      if ( element.isDigitalSignal() )
      {
        channels.add( element.getChannel() );
      }
    }
    return channels;
  }

  /**
   * Returns the channel group manager.
   *
   * @return a channel group manager, never <code>null</code>.
   */
  private SignalElementManager getSignalElementManager()
  {
    return getSignalDiagramModel().getSignalElementManager();
  }

  /**
   * Initializes this component.
   */
  private void initComponent()
  {
    IActionManager actionManager = getController().getActionManager();
    Action enableMeasurementModeAction = actionManager.getAction( SetMeasurementModeAction.ID );

    this.enableMeasurementMode = new JCheckBox( enableMeasurementModeAction );
    this.enableMeasurementMode.setText( "" );

    this.measureChannel = updateChannelComboBoxModel( new JComboBox(), Collections.<Channel> emptyList() );
    this.measureChannel.setRenderer( new ChannelComboBoxRenderer() );
    this.measureChannel.addActionListener( new CursorActionListener() );
    // Make the component a bit smaller and a pop-down on OSX...
    this.measureChannel.putClientProperty( "JComponent.sizeVariant", "small" );
    this.measureChannel.putClientProperty( "JComboBox.isPopDown", Boolean.TRUE );
    this.measureChannel.setEnabled( false );

    this.cursorA = updateCursorComboBoxModel( new JComboBox() );
    this.cursorA.setRenderer( new CursorComboBoxRenderer() );
    this.cursorA.addActionListener( new ChannelActionListener() );
    // Make the component a bit smaller and a pop-down on OSX...
    this.cursorA.putClientProperty( "JComponent.sizeVariant", "small" );
    this.cursorA.putClientProperty( "JComboBox.isPopDown", Boolean.TRUE );
    this.cursorA.setEnabled( false );

    this.cursorB = updateCursorComboBoxModel( new JComboBox() );
    this.cursorB.setRenderer( new CursorComboBoxRenderer() );
    this.cursorB.addActionListener( new ChannelActionListener() );
    // Make the component a bit smaller and a pop-down on OSX...
    this.cursorB.putClientProperty( "JComponent.sizeVariant", "small" );
    this.cursorB.putClientProperty( "JComboBox.isPopDown", Boolean.TRUE );
    this.cursorB.setEnabled( false );

    this.indicator = new JBusyIndicator();
    this.indicator.setVisible( false );

    setOpaque( false );
    setLayout( new BorderLayout() );
    setName( "Measurement" );

    JPanel panel = new JPanel( new SpringLayout() );

    // ROW 0 -- HEADER
    SpringLayoutUtils.addSeparator( panel, "Measurement" );

    panel.add( createRightAlignedLabel( "Enabled" ) );
    panel.add( this.enableMeasurementMode );

    this.comps.add( panel.add( createRightAlignedLabel( "Channel:" ) ) );
    this.comps.add( panel.add( this.mi_channel ) );

    this.comps.add( panel.add( this.mi_referenceLabel ) );
    this.comps.add( panel.add( this.mi_reference ) );

    this.comps.add( panel.add( createRightAlignedLabel( "Period:" ) ) );
    this.comps.add( panel.add( this.mi_period ) );

    this.comps.add( panel.add( createRightAlignedLabel( "Frequency:" ) ) );
    this.comps.add( panel.add( this.mi_frequency ) );

    this.comps.add( panel.add( createRightAlignedLabel( "Width (H):" ) ) );
    this.comps.add( panel.add( this.mi_widthHigh ) );

    this.comps.add( panel.add( createRightAlignedLabel( "Width (L):" ) ) );
    this.comps.add( panel.add( this.mi_widthLow ) );

    this.comps.add( panel.add( createRightAlignedLabel( "Duty cycle:" ) ) );
    this.comps.add( panel.add( this.mi_dutyCycle ) );

    // ROW 8 -- HEADER
    SpringLayoutUtils.addSeparator( panel, "Pulse counter" );

    this.comps.add( panel.add( createRightAlignedLabel( "Channel:" ) ) );
    this.comps.add( panel.add( this.measureChannel ) );

    this.comps.add( panel.add( createRightAlignedLabel( "Cursor A:" ) ) );
    this.comps.add( panel.add( this.cursorA ) );

    this.comps.add( panel.add( createRightAlignedLabel( "Cursor B:" ) ) );
    this.comps.add( panel.add( this.cursorB ) );

    this.comps.add( panel.add( this.pci_pulseCountLabel ) );
    this.comps.add( panel.add( this.pci_pulseCount ) );

    this.comps.add( panel.add( this.pci_timeLabel ) );
    this.comps.add( panel.add( this.pci_time ) );

    this.comps.add( panel.add( createRightAlignedLabel( "Frequency:" ) ) );
    this.comps.add( panel.add( this.pci_frequency ) );

    this.comps.add( panel.add( createRightAlignedLabel( "Duty cycle:" ) ) );
    this.comps.add( panel.add( this.pci_dutyCycle ) );

    panel.add( new JLabel( "" ) );
    panel.add( this.indicator );

    SpringLayoutUtils.makeEditorGrid( panel, 10, 10 );

    // Synchronize model state with UI state...
    setState( this.enableMeasurementMode.isSelected() );

    updateMeasurementInformation( null );
    updatePulseCountInformation( null );

    add( panel, BorderLayout.NORTH );
  }

  /**
   * Enables/disables the various components on this view.
   */
  private void setState( final boolean aEnabled )
  {
    SwingComponentUtils.invokeOnEDT( new Runnable()
    {
      @Override
      public void run()
      {
        for ( Component label : MeasurementView.this.comps )
        {
          label.setEnabled( aEnabled );
        }
      }
    } );
  }

  /**
   * Updates a given combobox' model to contain the current list of defined
   * cursors.
   *
   * @return the given combobox.
   */
  private JComboBox updateChannelComboBoxModel( final JComboBox aComboBox, final Collection<Channel> aChannels )
  {
    ComboBoxModel model = new DefaultComboBoxModel( new Vector<Channel>( aChannels ) );

    final Object oldSelectedItem = aComboBox.getSelectedItem();
    aComboBox.setModel( model );
    aComboBox.setSelectedItem( oldSelectedItem );

    return aComboBox;
  }

  /**
   * Updates a given combobox' model to contain the current list of defined
   * cursors.
   *
   * @return the given combobox.
   */
  private JComboBox updateCursorComboBoxModel( final JComboBox aComboBox )
  {
    final Cursor[] cursors = getSignalDiagramModel().getDefinedCursors();
    ComboBoxModel model = new DefaultComboBoxModel( cursors );

    final Object oldSelectedItem = aComboBox.getSelectedItem();
    aComboBox.setModel( model );
    aComboBox.setSelectedItem( oldSelectedItem );

    return aComboBox;
  }

  /**
   * Updates the actual measurement information.
   */
  private void updateMeasurementInfo()
  {
    this.listening = false;

    try
    {
      if ( canPerformMeasurement() )
      {
        if ( ( this.signalMeasurerWorker == null ) || this.signalMeasurerWorker.isDone() )
        {
          this.indicator.setVisible( true );

          Channel channel = ( Channel )MeasurementView.this.measureChannel.getSelectedItem();
          Cursor cursorA = ( Cursor )MeasurementView.this.cursorA.getSelectedItem();
          Cursor cursorB = ( Cursor )MeasurementView.this.cursorB.getSelectedItem();

          this.signalMeasurerWorker = new SignalMeasurerWorker( channel, cursorA, cursorB );
          this.signalMeasurerWorker.execute();
        }
      }
    }
    finally
    {
      this.listening = true;
    }
  }
}
TOP

Related Classes of nl.lxtreme.ols.client.signaldisplay.view.MeasurementView$SignalMeasurerWorker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.