package com.pi4j.io.gpio.impl; /* * #%L * ********************************************************************** * ORGANIZATION : Pi4J * PROJECT : Pi4J :: Java Library (Core) * FILENAME : GpioScheduledExecutorImpl.java * * This file is part of the Pi4J project. More information about * this project can be found here: http://www.pi4j.com/ * ********************************************************************** * %% * Copyright (C) 2012 - 2013 Pi4J * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import java.util.ArrayList; import java.util.Map.Entry; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.ConcurrentHashMap; import com.pi4j.io.gpio.GpioFactory; import com.pi4j.io.gpio.GpioPinDigitalOutput; import com.pi4j.io.gpio.PinState; import com.pi4j.io.gpio.tasks.impl.GpioBlinkStopTaskImpl; import com.pi4j.io.gpio.tasks.impl.GpioBlinkTaskImpl; import com.pi4j.io.gpio.tasks.impl.GpioPulseTaskImpl; public class GpioScheduledExecutorImpl { private static final ConcurrentHashMap<GpioPinDigitalOutput, ArrayList<ScheduledFuture<?>>> pinTaskQueue = new ConcurrentHashMap<GpioPinDigitalOutput, ArrayList<ScheduledFuture<?>>>(); private static ScheduledExecutorService scheduledExecutorService; private synchronized static void init(GpioPinDigitalOutput pin) { if (scheduledExecutorService == null) { scheduledExecutorService = GpioFactory.getExecutorServiceFactory().getScheduledExecutorService(); } // determine if any existing future tasks are already scheduled for this pin if (pinTaskQueue.containsKey(pin)) { // if a task is found, then cancel all pending tasks immediately and remove them ArrayList<ScheduledFuture<?>> tasks = pinTaskQueue.get(pin); if (tasks != null && !tasks.isEmpty()) { for (int index = (tasks.size() - 1); index >= 0; index--) { ScheduledFuture<?> task = tasks.get(index); task.cancel(true); tasks.remove(index); } } // if no remaining future tasks are remaining, then remove this pin from the tasks queue if (tasks.isEmpty()) { pinTaskQueue.remove(pin); } } } private synchronized static ScheduledFuture<?> createCleanupTask(long delay) { // create future task to clean up completed tasks @SuppressWarnings({"rawtypes", "unchecked"}) ScheduledFuture<?> cleanupFutureTask = scheduledExecutorService.schedule(new Callable() { public Object call() throws Exception { for (Entry<GpioPinDigitalOutput, ArrayList<ScheduledFuture<?>>> item : pinTaskQueue.entrySet()) { ArrayList<ScheduledFuture<?>> remainingTasks = item.getValue(); // if a task is found, then cancel all pending tasks immediately and remove them if (remainingTasks != null && !remainingTasks.isEmpty()) { for (int index = (remainingTasks.size() - 1); index >= 0; index--) { ScheduledFuture<?> remainingTask = remainingTasks.get(index); if (remainingTask.isCancelled() || remainingTask.isDone()) { remainingTasks.remove(index); } } // if no remaining future tasks are remaining, then remove this pin from the tasks queue if (remainingTasks.isEmpty()) { pinTaskQueue.remove(item.getKey()); } } } return null; } }, delay, TimeUnit.MILLISECONDS); return cleanupFutureTask; } public synchronized static Future<?> pulse(GpioPinDigitalOutput pin, long duration, PinState pulseState) { // create future return object ScheduledFuture<?> scheduledFuture = null; // perform the initial startup and cleanup for this pin init(pin); // we only pulse for requests with a valid duration in milliseconds if (duration > 0) { // set the active state pin.setState(pulseState); // create future job to return the pin to the low state scheduledFuture = scheduledExecutorService .schedule(new GpioPulseTaskImpl(pin, PinState.getInverseState(pulseState)), duration, TimeUnit.MILLISECONDS); // get pending tasks for the current pin ArrayList<ScheduledFuture<?>> tasks; if (!pinTaskQueue.containsKey(pin)) { pinTaskQueue.put(pin, new ArrayList<ScheduledFuture<?>>()); } tasks = pinTaskQueue.get(pin); // add the new scheduled task to the tasks collection tasks.add(scheduledFuture); // create future task to clean up completed tasks createCleanupTask(duration + 500); } // return future task return scheduledFuture; } public synchronized static Future<?> blink(GpioPinDigitalOutput pin, long delay, long duration, PinState blinkState) { // perform the initial startup and cleanup for this pin init(pin); // we only blink for requests with a valid delay in milliseconds if (delay > 0) { // make sure pin starts in active state pin.setState(blinkState); // create future job to toggle the pin state ScheduledFuture<?> scheduledFutureBlinkTask = scheduledExecutorService .scheduleAtFixedRate(new GpioBlinkTaskImpl(pin), delay, delay, TimeUnit.MILLISECONDS); // get pending tasks for the current pin ArrayList<ScheduledFuture<?>> tasks; if (!pinTaskQueue.containsKey(pin)) { pinTaskQueue.put(pin, new ArrayList<ScheduledFuture<?>>()); } tasks = pinTaskQueue.get(pin); // add the new scheduled task to the tasks collection tasks.add(scheduledFutureBlinkTask); // if a duration was defined, then schedule a future task to kill the blinker task if (duration > 0) { // create future job to stop blinking ScheduledFuture<?> scheduledFutureBlinkStopTask = scheduledExecutorService .schedule(new GpioBlinkStopTaskImpl(pin,PinState.getInverseState(blinkState), scheduledFutureBlinkTask), duration, TimeUnit.MILLISECONDS); // add the new scheduled stop task to the tasks collection tasks.add(scheduledFutureBlinkStopTask); // create future task to clean up completed tasks createCleanupTask(duration + 500); } // return future task return scheduledFutureBlinkTask; } // no future task when a delay time has not been specified return null; } }