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