DoubleAsynchronousSource.java

/*
 * Created on 2010/01/04
 * Copyright (C) 2010 Koga Laboratory. All rights reserved.
 *
 */
package org.mklab.tool.control.system.source;

import org.mklab.nfc.matrix.DoubleMatrix;
import org.mklab.nfc.ode.SolverStopException;


/**
 * 非同期のソースを同期するソースです。 <p> このソースの {@link #outputEquation(double)}では、入力が発生するまでブロックすることにより、全体の同期を行っています。
 * 
 * @author Yuhi Ishikura
 * @version $Revision$, 2010/01/04
 */
public abstract class DoubleAsynchronousSource extends DoubleContinuousSource implements Importer {

  //  /** 時間から値へのマップです。 */
  //  private BlockingSortedMap<Double, double[]> map = new BlockingSortedMap<Double, double[]>();
  /** 入力中に例外が発生した場合の例外保存変数です。 */
  Throwable thrown;
  /** 入力処理が終了したことを示します。 */
  private boolean done;
  /** 入力ソースの待機のロックに使用します。 */
  private final Object inputSourceLock = new Object();
  /** {@link #open()}が実行されたかどうかを示します。 */
  private boolean opened;

  /**
   * {@link DoubleAsynchronousSource}オブジェクトを構築します。
   */
  protected DoubleAsynchronousSource() {
    super();
  }

  /**
   * {@link DoubleAsynchronousSource}オブジェクトを構築します。
   * 
   * @param outputSize 出力数
   */
  protected DoubleAsynchronousSource(int outputSize) {
    super(outputSize);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public final DoubleMatrix outputEquation(double t) throws SolverStopException {
    // バッファが空の間待ち続ける
    synchronized (this.inputSourceLock) {
      while (isReadyFor(t) == false) {
        // これ以上データの入力がない場合
        if (isDone()) {
          break;
        }

        try {
          this.inputSourceLock.wait();
        } catch (InterruptedException e) {
          throw new SolverStopException(Messages.getString("AsynchronousSource.0"), e); //$NON-NLS-1$
        }
      }
    }

    checkSourceError();

    try {
      return getOutput(t);
    } catch (Exception ex) {
      // TODO ここでキャッチしなくてもいいようにする。
      // FixedRateAsynchronousSourceで、値取得の際に、範囲外参照、未来の参照が起こり得る
      // どう防ぐか。
      throw new SolverStopException(Messages.getString("AsynchronousSource.2"), ex); //$NON-NLS-1$
    }
  }

  /**
   * 入力処理中に発生した例外のチェックを行います。
   * 
   * @throws SolverStopException 入力処理中に例外が発生していた場合
   */
  private void checkSourceError() throws SolverStopException {
    final Throwable thrownInSource = getThrown();
    if (thrownInSource != null) {
      throw new SolverStopException(Messages.getString("AsynchronousSource.1"), thrownInSource); //$NON-NLS-1$
    }
  }

  /**
   * バッファの変更を通知します。
   */
  protected void bufferChanged() {
    synchronized (this.inputSourceLock) {
      this.inputSourceLock.notifyAll();
    }
  }

  /**
   * doneを取得します。
   * 
   * @return done
   */
  protected final boolean isDone() {
    return this.done;
  }

  /**
   * 入力の終了を通知します。 <p> このメソッドは、読み込み処理終了時にこのクラスで呼び出されるため、このクラスのサブクラスで呼び出す必要はありません。
   */
  final void done() {
    this.done = true;
    bufferChanged();
  }

  /**
   * 与えられた時間の入力が準備できているか調べます。
   * 
   * @param t 調べる時間
   * @return 準備ができていればtrue,できていなければfalse
   */
  protected abstract boolean isReadyFor(double t);

  /**
   * 与えられた時間の入力を取得します。
   * 
   * @param t 取得する時間
   * @return 出力
   */
  protected abstract DoubleMatrix getOutput(double t);

  /**
   * 別スレッドを開始し、入力処理を行います。
   * 
   * @see org.mklab.tool.control.system.source.Importer#open()
   */
  @Override
  public final void open() {
    if (this.opened) {
      throw new IllegalStateException();
    }
    final Thread importerThread = new ImporterThread();
    importerThread.start();
    this.opened = true;
  }

  /**
   * @see org.mklab.tool.control.system.source.Importer#importData()
   */
  @Override
  public final void importData() {
    // do nothing
  }

  /**
   * thrownを設定します。
   * 
   * @param thrown thrown
   */
  protected final void setThrown(Throwable thrown) {
    this.thrown = thrown;
  }

  /**
   * 入力処理中の例外を取得します。
   * 
   * @return thrown 入力処理中の例外。例外が発生しなかった場合はnull
   */
  protected final Throwable getThrown() {
    return this.thrown;
  }

  /**
   * @see org.mklab.tool.control.system.source.Importer#isActive()
   */
  @Override
  public final boolean isActive() {
    return this.opened && isDone() == false;
  }

  /**
   * ソースの記録を開始します。 <p> このメソッドは別スレッドにて呼び出されるため、ブロックしてかまいません。
   * 
   * @throws Throwable 入力処理中に例外が発生した場合
   */
  protected abstract void processImport() throws Throwable;

  /**
   * 入力処理を行うスレッドです。
   * 
   * @author Yuhi Ishikura
   * @version $Revision$, 2010/01/04
   */
  class ImporterThread extends Thread {

    /**
     * @see java.lang.Thread#run()
     */
    @Override
    public void run() {
      try {
        processImport();
      } catch (Throwable e) {
        setThrown(e);
      }
      done();
    }

  }

}