001package com.pi4j.io.gpio.impl;
002
003/*
004 * #%L
005 * **********************************************************************
006 * ORGANIZATION  :  Pi4J
007 * PROJECT       :  Pi4J :: Java Library (Core)
008 * FILENAME      :  GpioScheduledExecutorImpl.java  
009 * 
010 * This file is part of the Pi4J project. More information about 
011 * this project can be found here:  http://www.pi4j.com/
012 * **********************************************************************
013 * %%
014 * Copyright (C) 2012 - 2015 Pi4J
015 * %%
016 * This program is free software: you can redistribute it and/or modify
017 * it under the terms of the GNU Lesser General Public License as
018 * published by the Free Software Foundation, either version 3 of the
019 * License, or (at your option) any later version.
020 * 
021 * This program is distributed in the hope that it will be useful,
022 * but WITHOUT ANY WARRANTY; without even the implied warranty of
023 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
024 * GNU General Lesser Public License for more details.
025 * 
026 * You should have received a copy of the GNU General Lesser Public
027 * License along with this program.  If not, see
028 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
029 * #L%
030 */
031
032
033import com.pi4j.io.gpio.GpioFactory;
034import com.pi4j.io.gpio.GpioPinDigitalOutput;
035import com.pi4j.io.gpio.PinState;
036import com.pi4j.io.gpio.tasks.impl.GpioBlinkStopTaskImpl;
037import com.pi4j.io.gpio.tasks.impl.GpioBlinkTaskImpl;
038import com.pi4j.io.gpio.tasks.impl.GpioPulseTaskImpl;
039
040import java.util.ArrayList;
041import java.util.Map.Entry;
042import java.util.concurrent.*;
043
044public class GpioScheduledExecutorImpl {
045
046    private static final ConcurrentHashMap<GpioPinDigitalOutput, ArrayList<ScheduledFuture<?>>> pinTaskQueue = new ConcurrentHashMap<>();
047    private static ScheduledExecutorService scheduledExecutorService;
048
049    private synchronized static void init(GpioPinDigitalOutput pin) {
050        if (scheduledExecutorService == null) {
051            scheduledExecutorService = GpioFactory.getExecutorServiceFactory().getScheduledExecutorService();
052        }
053
054        // determine if any existing future tasks are already scheduled for this pin
055        if (pinTaskQueue.containsKey(pin)) {
056            // if a task is found, then cancel all pending tasks immediately and remove them
057            ArrayList<ScheduledFuture<?>> tasks = pinTaskQueue.get(pin);
058            if (tasks != null && !tasks.isEmpty()) {
059                for (int index =  (tasks.size() - 1); index >= 0; index--) {
060                    ScheduledFuture<?> task = tasks.get(index);
061                    task.cancel(true);
062                    tasks.remove(index);
063                }
064            }
065            
066            // if no remaining future tasks are remaining, then remove this pin from the tasks queue
067            if (tasks != null && tasks.isEmpty()) {
068                pinTaskQueue.remove(pin);
069            }
070        }
071    }
072    
073    private synchronized static ScheduledFuture<?> createCleanupTask(long delay) {
074        // create future task to clean up completed tasks
075
076        @SuppressWarnings({"rawtypes", "unchecked"})
077        ScheduledFuture<?> cleanupFutureTask = scheduledExecutorService.schedule(new Callable() {
078            public Object call() throws Exception {
079                for (Entry<GpioPinDigitalOutput, ArrayList<ScheduledFuture<?>>> item : pinTaskQueue.entrySet()) {
080                    ArrayList<ScheduledFuture<?>> remainingTasks = item.getValue();
081
082                    // if a task is found, then cancel all pending tasks immediately and remove them
083                    if (remainingTasks != null && !remainingTasks.isEmpty()) {
084                        for (int index = (remainingTasks.size() - 1); index >= 0; index--) {
085                            ScheduledFuture<?> remainingTask = remainingTasks.get(index);
086                            if (remainingTask.isCancelled() || remainingTask.isDone()) {
087                                remainingTasks.remove(index);
088                            }
089                        }
090                        
091                        // if no remaining future tasks are remaining, then remove this pin from the tasks queue
092                        if (remainingTasks.isEmpty()) {
093                            pinTaskQueue.remove(item.getKey());
094                        }
095                    }
096                }
097                return null;
098            }
099        }, delay, TimeUnit.MILLISECONDS);
100        
101        return cleanupFutureTask;
102    }
103
104    public synchronized static Future<?> pulse(GpioPinDigitalOutput pin, long duration, PinState pulseState) {
105        return pulse(pin, duration, pulseState, null);
106    }
107
108    public synchronized static Future<?> pulse(GpioPinDigitalOutput pin, long duration, PinState pulseState, Callable<?> callback) {
109        
110        // create future return object
111        ScheduledFuture<?> scheduledFuture = null; 
112                
113        // perform the initial startup and cleanup for this pin 
114        init(pin);
115
116        // we only pulse for requests with a valid duration in milliseconds
117        if (duration > 0) {
118            // set the active state
119            pin.setState(pulseState);
120            
121            // create future job to return the pin to the low state
122            scheduledFuture = scheduledExecutorService
123                .schedule(new GpioPulseTaskImpl(pin, PinState.getInverseState(pulseState), callback), duration, TimeUnit.MILLISECONDS);
124
125            // get pending tasks for the current pin
126            ArrayList<ScheduledFuture<?>> tasks;
127            if (!pinTaskQueue.containsKey(pin)) {
128                pinTaskQueue.put(pin, new ArrayList<ScheduledFuture<?>>());
129            }
130            tasks = pinTaskQueue.get(pin);
131            
132            // add the new scheduled task to the tasks collection
133            tasks.add(scheduledFuture);
134            
135            // create future task to clean up completed tasks
136            createCleanupTask(duration + 500);
137        }
138
139        // return future task
140        return scheduledFuture;
141    }
142
143    public synchronized static Future<?> blink(GpioPinDigitalOutput pin, long delay, long duration, PinState blinkState) {
144
145        // perform the initial startup and cleanup for this pin 
146        init(pin);
147        
148        // we only blink for requests with a valid delay in milliseconds
149        if (delay > 0) {
150            // make sure pin starts in active state
151            pin.setState(blinkState);
152            
153            // create future job to toggle the pin state
154            ScheduledFuture<?> scheduledFutureBlinkTask = scheduledExecutorService
155                .scheduleAtFixedRate(new GpioBlinkTaskImpl(pin), delay, delay, TimeUnit.MILLISECONDS);
156                
157            // get pending tasks for the current pin
158            ArrayList<ScheduledFuture<?>> tasks;
159            if (!pinTaskQueue.containsKey(pin)) {
160                pinTaskQueue.put(pin, new ArrayList<ScheduledFuture<?>>());
161            }
162            tasks = pinTaskQueue.get(pin);
163
164            // add the new scheduled task to the tasks collection
165            tasks.add(scheduledFutureBlinkTask);
166
167            // if a duration was defined, then schedule a future task to kill the blinker task
168            if (duration > 0) {
169                // create future job to stop blinking
170                ScheduledFuture<?> scheduledFutureBlinkStopTask = scheduledExecutorService
171                    .schedule(new GpioBlinkStopTaskImpl(pin,PinState.getInverseState(blinkState), scheduledFutureBlinkTask), duration, TimeUnit.MILLISECONDS);
172
173                // add the new scheduled stop task to the tasks collection
174                tasks.add(scheduledFutureBlinkStopTask);
175
176                // create future task to clean up completed tasks
177                createCleanupTask(duration + 500);                
178            }      
179
180            // return future task
181            return scheduledFutureBlinkTask;
182        }
183        
184        // no future task when a delay time has not been specified
185        return null;
186    }
187}