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: https://www.pi4j.com/ 012 * ********************************************************************** 013 * %% 014 * Copyright (C) 2012 - 2019 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, CopyOnWriteArrayList<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 CopyOnWriteArrayList<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, TimeUnit timeUnit) { 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, CopyOnWriteArrayList<ScheduledFuture<?>>> item : pinTaskQueue.entrySet()) { 080 CopyOnWriteArrayList<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); 100 101 return cleanupFutureTask; 102 } 103 104 105 public synchronized static Future<?> pulse(GpioPinDigitalOutput pin, long duration, PinState pulseState, TimeUnit unit) { 106 return pulse(pin, duration, pulseState, null, unit); 107 } 108 109 public synchronized static Future<?> pulse(GpioPinDigitalOutput pin, long duration, PinState pulseState, Callable<?> callback, TimeUnit timeUnit) { 110 111 // create future return object 112 ScheduledFuture<?> scheduledFuture = null; 113 114 // perform the initial startup and cleanup for this pin 115 init(pin); 116 117 // we only pulse for requests with a valid duration in milliseconds 118 if (duration > 0) { 119 // set the active state 120 pin.setState(pulseState); 121 122 // create future job to return the pin to the low state 123 scheduledFuture = scheduledExecutorService 124 .schedule(new GpioPulseTaskImpl(pin, PinState.getInverseState(pulseState), callback), duration, timeUnit); 125 126 // get pending tasks for the current pin 127 CopyOnWriteArrayList<ScheduledFuture<?>> tasks; 128 if (!pinTaskQueue.containsKey(pin)) { 129 pinTaskQueue.put(pin, new CopyOnWriteArrayList<>()); 130 } 131 tasks = pinTaskQueue.get(pin); 132 133 // add the new scheduled task to the tasks collection 134 tasks.add(scheduledFuture); 135 136 // create future task to clean up completed tasks 137 createCleanupTask(duration + 500, timeUnit); 138 } 139 140 // return future task 141 return scheduledFuture; 142 } 143 144 public synchronized static Future<?> blink(GpioPinDigitalOutput pin, long delay, long duration, PinState blinkState, TimeUnit timeUnit) { 145 146 // perform the initial startup and cleanup for this pin 147 init(pin); 148 149 // we only blink for requests with a valid delay in milliseconds 150 if (delay > 0) { 151 // make sure pin starts in active state 152 pin.setState(blinkState); 153 154 // create future job to toggle the pin state 155 ScheduledFuture<?> scheduledFutureBlinkTask = scheduledExecutorService 156 .scheduleAtFixedRate(new GpioBlinkTaskImpl(pin), delay, delay, timeUnit); 157 158 // get pending tasks for the current pin 159 CopyOnWriteArrayList<ScheduledFuture<?>> tasks; 160 if (!pinTaskQueue.containsKey(pin)) { 161 pinTaskQueue.put(pin, new CopyOnWriteArrayList<>()); 162 } 163 tasks = pinTaskQueue.get(pin); 164 165 // add the new scheduled task to the tasks collection 166 tasks.add(scheduledFutureBlinkTask); 167 168 // if a duration was defined, then schedule a future task to kill the blinker task 169 if (duration > 0) { 170 // create future job to stop blinking 171 ScheduledFuture<?> scheduledFutureBlinkStopTask = scheduledExecutorService 172 .schedule(new GpioBlinkStopTaskImpl(pin,PinState.getInverseState(blinkState), scheduledFutureBlinkTask), duration, timeUnit); 173 174 // add the new scheduled stop task to the tasks collection 175 tasks.add(scheduledFutureBlinkStopTask); 176 177 // create future task to clean up completed tasks 178 createCleanupTask(duration + 500, timeUnit); 179 } 180 181 // return future task 182 return scheduledFutureBlinkTask; 183 } 184 185 // no future task when a delay time has not been specified 186 return null; 187 } 188}