/** * Wait for value of device to reach the desired value (within tolerance) * * @throws TimeoutException on timeout * @throws Exception on interruption or device read error */ @Override public void await() throws TimeoutException, Exception { final WaitWithTimeout timeout = new WaitWithTimeout(this.timeout); // Fetch initial value with get-callback initial_value = VTypeHelper.toDouble(device.read(value_check_timeout)); device.addListener(this); try { // Synchronize to avoid the following situation: // 1. not at desired value // 2. device changes and we would be notified // 3. ... but that's before we call wait, so we wait forever synchronized (this) { is_condition_met = isConditionMet(); while (!is_condition_met) { // Wait for update from device if (timeout.waitUntilTimeout(this)) throw new TimeoutException( "Timeout while waiting for " + device + " " + comparison + " " + desired_value); if (error != null) throw error; } } } finally { device.removeListener(this); } }
/** * Execute one step of the loop * * @param context * @param device * @param condition * @param readback * @param value * @throws Exception */ private void executeStep( final ScanContext context, final Device device, final NumericValueCondition condition, final Device readback, double value) throws Exception { logger.log( Level.INFO, "Loop setting {0} = {1}{2}", new Object[] {device.getAlias(), value, (condition != null ? " (waiting)" : "")}); // Set device to value for current step of loop do_skip = false; synchronized (this) { thread = Thread.currentThread(); } try { if (command.getCompletion()) device.write(value, TimeDuration.ofSeconds(command.getTimeout())); else device.write(value); // .. wait for device to reach value if (condition != null) { condition.setDesiredValue(value); condition.await(); } // Log the device's value? if (context.isAutomaticLogMode()) { final DataLog log = context.getDataLog().get(); final long serial = log.getNextScanDataSerial(); log.log(readback.getAlias(), VTypeHelper.createSample(serial, readback.read())); } } catch (InterruptedException ex) { // Ignore if 'next' was requested if (!do_skip) throw ex; } finally { synchronized (this) { thread = null; } } // Execute loop body or show some estimate of progress // (not including nested commands) if (do_skip) context.workPerformed(implementation.size()); else context.execute(implementation); // If there are no commands that inc. the work units, do it yourself if (implementation.size() <= 0) context.workPerformed(1); }
/** Trigger another check of device's value {@inheritDoc} */ @Override public void deviceChanged(final Device device) { synchronized (this) { try { if (Double.isNaN(initial_value)) initial_value = VTypeHelper.toDouble(device.read()); is_condition_met = isConditionMet(); } catch (Exception ex) { is_condition_met = false; error = ex; } // Notify await() so it can check again. notifyAll(); } }
/** * Simulate one step in the loop iteration * * @param context {@link SimulationContext} * @param device {@link SimulatedDevice} that the loop modifies * @param value Value of the loop variable for this iteration * @throws Exception on error */ private void simulateStep( final SimulationContext context, final SimulatedDevice device, final double value) throws Exception { // Get previous value final double original = VTypeHelper.toDouble(device.read()); // Estimate execution time final double time_estimate = command.getWait() ? device.getChangeTimeEstimate(value) : 0.0; // Show command final StringBuilder buf = new StringBuilder(); buf.append("Loop '").append(command.getDeviceName()).append("' = ").append(value); command.appendConditionDetail(buf); if (!Double.isNaN(original)) buf.append(" [was ").append(original).append("]"); context.logExecutionStep(context.getMacros().resolveMacros(buf.toString()), time_estimate); // Set to (simulated) new value device.write(value); // Simulate loop body context.simulate(implementation); }
/** * Determine if the condition is currently met * * @return <code>true</code> if condition is met * @throws Exception on error reading from the device */ public boolean isConditionMet() throws Exception { final double value = VTypeHelper.toDouble(device.read()); // Note that these need to fail "safe" if any of the values are NaN switch (comparison) { case EQUALS: return Math.abs(desired_value - value) <= tolerance; case AT_LEAST: return value >= desired_value; case ABOVE: return value > desired_value; case AT_MOST: return value <= desired_value; case BELOW: return value < desired_value; case INCREASE_BY: return value >= initial_value + desired_value; case DECREASE_BY: return value <= initial_value - desired_value; default: throw new Error("Condition not implemented: " + comparison); } }