001package com.pi4j.io.gpio; 002 003/* 004 * #%L 005 * ********************************************************************** 006 * ORGANIZATION : Pi4J 007 * PROJECT : Pi4J :: Java Library (Core) 008 * FILENAME : GpioProviderBase.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.event.PinAnalogValueChangeEvent; 034import com.pi4j.io.gpio.event.PinDigitalStateChangeEvent; 035import com.pi4j.io.gpio.event.PinListener; 036import com.pi4j.io.gpio.exception.InvalidPinException; 037import com.pi4j.io.gpio.exception.InvalidPinModeException; 038import com.pi4j.io.gpio.exception.UnsupportedPinModeException; 039import com.pi4j.io.gpio.exception.UnsupportedPinPullResistanceException; 040 041import java.util.ArrayList; 042import java.util.List; 043import java.util.Map; 044import java.util.concurrent.ConcurrentHashMap; 045 046/** 047 * Abstract base implementation of {@link com.pi4j.io.gpio.GpioProvider}. 048 * 049 * @author Robert Savage (<a 050 * href="http://www.savagehomeautomation.com">http://www.savagehomeautomation.com</a>) 051 */ 052@SuppressWarnings("unused") 053public abstract class GpioProviderBase implements GpioProvider { 054 055 public abstract String getName(); 056 057 protected final Map<Pin, List<PinListener>> listeners = new ConcurrentHashMap<>(); 058 protected final Map<Pin, GpioProviderPinCache> cache = new ConcurrentHashMap<>(); 059 protected boolean isshutdown = false; 060 061 @Override 062 public boolean hasPin(Pin pin) { 063 return (pin.getProvider().equals(getName())); 064 } 065 066 protected GpioProviderPinCache getPinCache(Pin pin) { 067 if (!cache.containsKey(pin)) { 068 cache.put(pin, new GpioProviderPinCache(pin)); 069 } 070 return cache.get(pin); 071 } 072 073 @Override 074 public void export(Pin pin, PinMode mode, PinState defaultState) { 075 // export the pin and set it's mode 076 export(pin, mode); 077 078 // apply default state if one was provided and only if this pin is a digital output 079 if(defaultState != null && mode == PinMode.DIGITAL_OUTPUT) { 080 setState(pin, defaultState); 081 } 082 } 083 084 @Override 085 public void export(Pin pin, PinMode mode) { 086 if (!hasPin(pin)) { 087 throw new InvalidPinException(pin); 088 } 089 090 if (!pin.getSupportedPinModes().contains(mode)) { 091 throw new UnsupportedPinModeException(pin, mode); 092 } 093 094 // cache exported state 095 getPinCache(pin).setExported(true); 096 097 // cache mode 098 getPinCache(pin).setMode(mode); 099 } 100 101 @Override 102 public boolean isExported(Pin pin) { 103 if (!hasPin(pin)) { 104 throw new InvalidPinException(pin); 105 } 106 107 // return cached exported state 108 return getPinCache(pin).isExported(); 109 } 110 111 @Override 112 public void unexport(Pin pin) { 113 if (!hasPin(pin)) { 114 throw new InvalidPinException(pin); 115 } 116 117 // cache exported state 118 getPinCache(pin).setExported(false); 119 } 120 121 @Override 122 public void setMode(Pin pin, PinMode mode) { 123 if (!pin.getSupportedPinModes().contains(mode)) { 124 throw new InvalidPinModeException(pin, "Invalid pin mode [" + mode.getName() + "]; pin [" + pin.getName() + "] does not support this mode."); 125 } 126 127 if (!pin.getSupportedPinModes().contains(mode)) { 128 throw new UnsupportedPinModeException(pin, mode); 129 } 130 131 // cache mode 132 getPinCache(pin).setMode(mode); 133 } 134 135 @Override 136 public PinMode getMode(Pin pin) { 137 if (!hasPin(pin)) { 138 throw new InvalidPinException(pin); 139 } 140 141 // return cached mode value 142 return getPinCache(pin).getMode(); 143 } 144 145 146 @Override 147 public void setPullResistance(Pin pin, PinPullResistance resistance) { 148 if (!hasPin(pin)) { 149 throw new InvalidPinException(pin); 150 } 151 152 if (!pin.getSupportedPinPullResistance().contains(resistance)) { 153 throw new UnsupportedPinPullResistanceException(pin, resistance); 154 } 155 156 // cache resistance 157 getPinCache(pin).setResistance(resistance); 158 } 159 160 @Override 161 public PinPullResistance getPullResistance(Pin pin) { 162 if (!hasPin(pin)) { 163 throw new InvalidPinException(pin); 164 } 165 166 // return cached resistance 167 return getPinCache(pin).getResistance(); 168 } 169 170 @Override 171 public void setState(Pin pin, PinState state) { 172 if (!hasPin(pin)) { 173 throw new InvalidPinException(pin); 174 } 175 176 PinMode mode = getMode(pin); 177 178 // only permit invocation on pins set to DIGITAL_OUTPUT modes 179 if (mode != PinMode.DIGITAL_OUTPUT) { 180 throw new InvalidPinModeException(pin, "Invalid pin mode on pin [" + pin.getName() + "]; cannot setState() when pin mode is [" + mode.getName() + "]"); 181 } 182 183 // for digital output pins, we will echo the event feedback 184 dispatchPinDigitalStateChangeEvent(pin, state); 185 186 // cache pin state 187 getPinCache(pin).setState(state); 188 } 189 190 @Override 191 public PinState getState(Pin pin) { 192 if (!hasPin(pin)) { 193 throw new InvalidPinException(pin); 194 } 195 196 PinMode mode = getMode(pin); 197 198 // only permit invocation on pins set to DIGITAL modes 199 if (!PinMode.allDigital().contains(mode)) { 200 throw new InvalidPinModeException(pin, "Invalid pin mode on pin [" + pin.getName() + "]; cannot getState() when pin mode is [" + mode.getName() + "]"); 201 } 202 203 // return cached pin state 204 return getPinCache(pin).getState(); 205 } 206 207 @Override 208 public void setValue(Pin pin, double value) { 209 if (!hasPin(pin)) { 210 throw new InvalidPinException(pin); 211 } 212 213 PinMode mode = getMode(pin); 214 215 // only permit invocation on pins set to OUTPUT modes 216 if (!PinMode.allOutput().contains(mode)) { 217 throw new InvalidPinModeException(pin, "Invalid pin mode on pin [" + pin.getName() + "]; cannot setValue(" + value + ") when pin mode is [" + mode.getName() + "]"); 218 } 219 220 // for digital analog pins, we will echo the event feedback 221 dispatchPinAnalogValueChangeEvent(pin, value); 222 223 // cache pin analog value 224 getPinCache(pin).setAnalogValue(value); 225 } 226 227 @Override 228 public double getValue(Pin pin) { 229 if (!hasPin(pin)) { 230 throw new InvalidPinException(pin); 231 } 232 233 PinMode mode = getMode(pin); 234 235 if (mode == PinMode.DIGITAL_OUTPUT) { 236 return getState(pin).getValue(); 237 } 238 239 // return cached pin analog value 240 return getPinCache(pin).getAnalogValue(); 241 } 242 243 @Override 244 public void setPwm(Pin pin, int value) { 245 if (!hasPin(pin)) { 246 throw new InvalidPinException(pin); 247 } 248 249 PinMode mode = getMode(pin); 250 251 if (mode != PinMode.PWM_OUTPUT) { 252 throw new InvalidPinModeException(pin, "Invalid pin mode [" + mode.getName() + "]; unable to setPwm(" + value + ")"); 253 } 254 255 // cache pin PWM value 256 getPinCache(pin).setPwmValue(value); 257 } 258 259 260 @Override 261 public int getPwm(Pin pin) { 262 if (!hasPin(pin)) { 263 throw new InvalidPinException(pin); 264 } 265 266 // return cached pin PWM value 267 return getPinCache(pin).getPwmValue(); 268 } 269 270 @Override 271 public void addListener(Pin pin, PinListener listener) { 272 synchronized (listeners) { 273 // create new pin listener entry if one does not already exist 274 if (!listeners.containsKey(pin)) { 275 listeners.put(pin, new ArrayList<PinListener>()); 276 } 277 278 // add the listener instance to the listeners map entry 279 List<PinListener> lsnrs = listeners.get(pin); 280 if (!lsnrs.contains(listener)) { 281 lsnrs.add(listener); 282 } 283 } 284 } 285 286 @Override 287 public void removeListener(Pin pin, PinListener listener) { 288 synchronized (listeners) { 289 // lookup to pin entry in the listeners map 290 if (listeners.containsKey(pin)) { 291 // remote the listener instance from the listeners map entry if found 292 List<PinListener> lsnrs = listeners.get(pin); 293 if (lsnrs.contains(listener)) { 294 lsnrs.remove(listener); 295 } 296 297 // if the listener list is empty, then remove the listener pin from the map 298 if (lsnrs.isEmpty()) { 299 listeners.remove(pin); 300 } 301 } 302 } 303 } 304 305 @Override 306 public void removeAllListeners() { 307 synchronized (listeners) { 308 // iterate over all listener pins in the map 309 List<Pin> pins_copy = new ArrayList<>(listeners.keySet()); 310 for (Pin pin : pins_copy) { 311 if(listeners.containsKey(pin)) { 312 // iterate over all listener handler in the map entry 313 // and remove each listener handler instance 314 List<PinListener> lsnrs = listeners.get(pin); 315 if (!lsnrs.isEmpty()) { 316 List<PinListener> lsnrs_copy = new ArrayList<>(lsnrs); 317 for (int index = lsnrs_copy.size() - 1; index >= 0; index--) { 318 PinListener listener = lsnrs_copy.get(index); 319 removeListener(pin, listener); 320 } 321 } 322 } 323 } 324 } 325 } 326 327 protected void dispatchPinDigitalStateChangeEvent(Pin pin, PinState state) { 328 // if the pin listeners map contains this pin, then dispatch event 329 if (listeners.containsKey(pin)) { 330 // dispatch this event to all listener handlers 331 for (PinListener listener : listeners.get(pin)) { 332 listener.handlePinEvent(new PinDigitalStateChangeEvent(this, pin, state)); 333 } 334 } 335 } 336 337 protected void dispatchPinAnalogValueChangeEvent(Pin pin, double value) { 338 // if the pin listeners map contains this pin, then dispatch event 339 if (listeners.containsKey(pin)) { 340 // dispatch this event to all listener handlers 341 for (PinListener listener : listeners.get(pin)) { 342 listener.handlePinEvent(new PinAnalogValueChangeEvent(this, pin, value)); 343 } 344 } 345 } 346 347 @Override 348 public void shutdown() { 349 350 // prevent reentrant invocation 351 if(isShutdown()) 352 return; 353 354 // remove all listeners 355 removeAllListeners(); 356 357 // set shutdown tracking state variable 358 isshutdown = true; 359 } 360 361 /** 362 * This method returns TRUE if the GPIO provider has been shutdown. 363 * 364 * @return shutdown state 365 */ 366 @Override 367 public boolean isShutdown(){ 368 return isshutdown; 369 } 370}