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}