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 - 2013 Pi4J
015 * %%
016 * Licensed under the Apache License, Version 2.0 (the "License");
017 * you may not use this file except in compliance with the License.
018 * You may obtain a copy of the License at
019 * 
020 *      http://www.apache.org/licenses/LICENSE-2.0
021 * 
022 * Unless required by applicable law or agreed to in writing, software
023 * distributed under the License is distributed on an "AS IS" BASIS,
024 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
025 * See the License for the specific language governing permissions and
026 * limitations under the License.
027 * #L%
028 */
029
030
031import java.util.ArrayList;
032import java.util.Map.Entry;
033import java.util.concurrent.Callable;
034import java.util.concurrent.Future;
035import java.util.concurrent.ScheduledExecutorService;
036import java.util.concurrent.ScheduledFuture;
037import java.util.concurrent.TimeUnit;
038import java.util.concurrent.ConcurrentHashMap;
039
040import com.pi4j.io.gpio.GpioFactory;
041import com.pi4j.io.gpio.GpioPinDigitalOutput;
042import com.pi4j.io.gpio.PinState;
043import com.pi4j.io.gpio.tasks.impl.GpioBlinkStopTaskImpl;
044import com.pi4j.io.gpio.tasks.impl.GpioBlinkTaskImpl;
045import com.pi4j.io.gpio.tasks.impl.GpioPulseTaskImpl;
046
047public class GpioScheduledExecutorImpl {
048
049    private static final ConcurrentHashMap<GpioPinDigitalOutput, ArrayList<ScheduledFuture<?>>> pinTaskQueue = new ConcurrentHashMap<GpioPinDigitalOutput, ArrayList<ScheduledFuture<?>>>();
050    private static ScheduledExecutorService scheduledExecutorService;
051
052    private synchronized static void init(GpioPinDigitalOutput pin) {
053        if (scheduledExecutorService == null) {
054            scheduledExecutorService = GpioFactory.getExecutorServiceFactory().getScheduledExecutorService();
055        }
056
057        // determine if any existing future tasks are already scheduled for this pin
058        if (pinTaskQueue.containsKey(pin)) {
059            // if a task is found, then cancel all pending tasks immediately and remove them
060            ArrayList<ScheduledFuture<?>> tasks = pinTaskQueue.get(pin);
061            if (tasks != null && !tasks.isEmpty()) {
062                for (int index =  (tasks.size() - 1); index >= 0; index--) {
063                    ScheduledFuture<?> task = tasks.get(index);
064                    task.cancel(true);
065                    tasks.remove(index);
066                }
067            }
068            
069            // if no remaining future tasks are remaining, then remove this pin from the tasks queue
070            if (tasks.isEmpty()) {
071                pinTaskQueue.remove(pin);
072            }
073        }
074    }
075    
076    private synchronized static ScheduledFuture<?> createCleanupTask(long delay) {
077        // create future task to clean up completed tasks
078
079        @SuppressWarnings({"rawtypes", "unchecked"})
080        ScheduledFuture<?> cleanupFutureTask = scheduledExecutorService.schedule(new Callable() {
081            public Object call() throws Exception {
082                for (Entry<GpioPinDigitalOutput, ArrayList<ScheduledFuture<?>>> item : pinTaskQueue.entrySet()) {
083                    ArrayList<ScheduledFuture<?>> remainingTasks = item.getValue();
084
085                    // if a task is found, then cancel all pending tasks immediately and remove them
086                    if (remainingTasks != null && !remainingTasks.isEmpty()) {
087                        for (int index = (remainingTasks.size() - 1); index >= 0; index--) {
088                            ScheduledFuture<?> remainingTask = remainingTasks.get(index);
089                            if (remainingTask.isCancelled() || remainingTask.isDone()) {
090                                remainingTasks.remove(index);
091                            }
092                        }
093                        
094                        // if no remaining future tasks are remaining, then remove this pin from the tasks queue
095                        if (remainingTasks.isEmpty()) {
096                            pinTaskQueue.remove(item.getKey());
097                        }
098                    }
099                }
100                return null;
101            }
102        }, delay, TimeUnit.MILLISECONDS);
103        
104        return cleanupFutureTask;
105    }
106    
107    public synchronized static Future<?> pulse(GpioPinDigitalOutput pin, long duration, PinState pulseState) {
108        
109        // create future return object
110        ScheduledFuture<?> scheduledFuture = null; 
111                
112        // perform the initial startup and cleanup for this pin 
113        init(pin);
114
115        // we only pulse for requests with a valid duration in milliseconds
116        if (duration > 0) {
117            // set the active state
118            pin.setState(pulseState);
119            
120            // create future job to return the pin to the low state
121            scheduledFuture = scheduledExecutorService
122                .schedule(new GpioPulseTaskImpl(pin, PinState.getInverseState(pulseState)), duration, TimeUnit.MILLISECONDS);
123
124            // get pending tasks for the current pin
125            ArrayList<ScheduledFuture<?>> tasks;
126            if (!pinTaskQueue.containsKey(pin)) {
127                pinTaskQueue.put(pin, new ArrayList<ScheduledFuture<?>>());
128            }
129            tasks = pinTaskQueue.get(pin);
130            
131            // add the new scheduled task to the tasks collection
132            tasks.add(scheduledFuture);
133            
134            // create future task to clean up completed tasks
135            createCleanupTask(duration + 500);
136        }
137
138        // return future task
139        return scheduledFuture;
140    }
141
142    public synchronized static Future<?> blink(GpioPinDigitalOutput pin, long delay, long duration, PinState blinkState) {
143
144        // perform the initial startup and cleanup for this pin 
145        init(pin);
146        
147        // we only blink for requests with a valid delay in milliseconds
148        if (delay > 0) {
149            // make sure pin starts in active state
150            pin.setState(blinkState);
151            
152            // create future job to toggle the pin state
153            ScheduledFuture<?> scheduledFutureBlinkTask = scheduledExecutorService
154                .scheduleAtFixedRate(new GpioBlinkTaskImpl(pin), delay, delay, TimeUnit.MILLISECONDS);
155                
156            // get pending tasks for the current pin
157            ArrayList<ScheduledFuture<?>> tasks;
158            if (!pinTaskQueue.containsKey(pin)) {
159                pinTaskQueue.put(pin, new ArrayList<ScheduledFuture<?>>());
160            }
161            tasks = pinTaskQueue.get(pin);
162
163            // add the new scheduled task to the tasks collection
164            tasks.add(scheduledFutureBlinkTask);
165
166            // if a duration was defined, then schedule a future task to kill the blinker task
167            if (duration > 0) {
168                // create future job to stop blinking
169                ScheduledFuture<?> scheduledFutureBlinkStopTask = scheduledExecutorService
170                    .schedule(new GpioBlinkStopTaskImpl(pin,PinState.getInverseState(blinkState), scheduledFutureBlinkTask), duration, TimeUnit.MILLISECONDS);
171
172                // add the new scheduled stop task to the tasks collection
173                tasks.add(scheduledFutureBlinkStopTask);
174
175                // create future task to clean up completed tasks
176                createCleanupTask(duration + 500);                
177            }      
178
179            // return future task
180            return scheduledFutureBlinkTask;
181        }
182        
183        // no future task when a delay time has not been specified
184        return null;
185    }
186}