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}