/** * 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); } }
/** 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); } }