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}