001package com.pi4j.io.gpio.impl;
002
003/*
004 * #%L
005 * **********************************************************************
006 * ORGANIZATION  :  Pi4J
007 * PROJECT       :  Pi4J :: Java Library (Core)
008 * FILENAME      :  GpioPinImpl.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"); you
017 * may not use this file except in compliance with the License. You may obtain a copy of the License
018 * at
019 * 
020 * http://www.apache.org/licenses/LICENSE-2.0
021 * 
022 * Unless required by applicable law or agreed to in writing, software distributed under the License
023 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
024 * or implied. See the License for the specific language governing permissions and limitations under
025 * the License.
026 * #L%
027 */
028
029import java.util.ArrayList;
030import java.util.Collection;
031import java.util.List;
032import java.util.Map;
033import java.util.concurrent.ConcurrentHashMap;
034import java.util.concurrent.Future;
035
036import com.pi4j.io.gpio.GpioPinAnalogInput;
037import com.pi4j.io.gpio.GpioPinAnalogOutput;
038import com.pi4j.io.gpio.GpioController;
039import com.pi4j.io.gpio.GpioPinDigitalInput;
040import com.pi4j.io.gpio.GpioPinDigitalMultipurpose;
041import com.pi4j.io.gpio.GpioPinDigitalOutput;
042import com.pi4j.io.gpio.GpioPin;
043import com.pi4j.io.gpio.GpioPinInput;
044import com.pi4j.io.gpio.GpioPinOutput;
045import com.pi4j.io.gpio.GpioPinPwmOutput;
046import com.pi4j.io.gpio.GpioProvider;
047import com.pi4j.io.gpio.Pin;
048import com.pi4j.io.gpio.PinMode;
049import com.pi4j.io.gpio.PinPullResistance;
050import com.pi4j.io.gpio.GpioPinShutdown;
051import com.pi4j.io.gpio.PinState;
052import com.pi4j.io.gpio.event.GpioPinListener;
053import com.pi4j.io.gpio.event.PinListener;
054import com.pi4j.io.gpio.trigger.GpioTrigger;
055
056public class GpioPinImpl implements GpioPin, 
057                                    GpioPinDigitalInput, 
058                                    GpioPinDigitalOutput, 
059                                    GpioPinDigitalMultipurpose,
060                                    GpioPinAnalogInput, 
061                                    GpioPinAnalogOutput,
062                                    GpioPinPwmOutput,
063                                    GpioPinInput,
064                                    GpioPinOutput
065{
066 
067    @SuppressWarnings("unused")
068    private final GpioController gpio;
069    private String name = null;
070    private Object tag = null;
071    private final GpioProvider provider;
072    private final Pin pin;
073    private PinListener monitor;
074    private final GpioPinShutdownImpl shutdownOptions;
075    private final Map<String, String> properties = new ConcurrentHashMap<String, String>();
076    private final List<GpioPinListener> listeners = new ArrayList<GpioPinListener>();
077    private final List<GpioTrigger> triggers = new ArrayList<GpioTrigger>();
078    
079    public GpioPinImpl(GpioController gpio, GpioProvider provider, Pin pin) {
080        this.gpio = gpio;
081        this.provider = provider;
082        this.pin = pin;
083        shutdownOptions = new GpioPinShutdownImpl();
084    }
085
086    @Override
087    public Pin getPin() {
088        return this.pin;
089    }
090
091    @Override
092    public GpioProvider getProvider() {
093        return this.provider;
094    }
095    
096    @Override
097    public void setName(String name) {
098        this.name = name;
099    }
100
101    @Override
102    public String getName() {
103        if (name == null || name.length() == 0) {
104            return pin.toString();
105        }
106        return name;
107    }
108    
109    @Override
110    public void setTag(Object tag) {
111        this.tag = tag;
112    }
113
114    @Override
115    public Object getTag() {
116        return tag;
117    }
118
119    @Override
120    public void setProperty(String key, String value) {
121        properties.put(key, value);
122    }
123
124    @Override
125    public boolean hasProperty(String key) {
126        return properties.containsKey(key);
127    }
128
129    @Override
130    public String getProperty(String key, String defaultValue) {
131        if (properties.containsKey(key)) {
132            if(properties.get(key) == null || properties.get(key).isEmpty())
133                return defaultValue;
134            else
135                return properties.get(key);
136        }
137        return defaultValue;
138    }
139
140    @Override
141    public String getProperty(String key) {
142        return getProperty(key, null);
143    }
144    
145    @Override
146    public Map<String, String> getProperties() {
147        return properties;
148    }
149
150    @Override
151    public void removeProperty(String key) {
152        if (properties.containsKey(key)) {
153            properties.remove(key);
154        }
155    }
156
157    @Override
158    public void clearProperties() {
159        properties.clear();
160    }
161
162    @Override
163    public void export(PinMode mode) {
164        // export the pin
165        provider.export(pin, mode);
166    }
167
168    @Override
169    public void unexport() {
170        // unexport the pin
171        provider.unexport(pin);
172    }
173
174    @Override
175    public boolean isExported() {
176        return provider.isExported(pin);
177    }
178
179    @Override
180    public void setMode(PinMode mode) {
181        provider.setMode(pin, mode);
182    }
183
184    @Override
185    public PinMode getMode() {
186        return provider.getMode(pin);
187    }
188
189    @Override
190    public boolean isMode(PinMode mode) {
191        return (getMode() == mode);
192    }
193
194    @Override
195    public void setPullResistance(PinPullResistance resistance) {
196        provider.setPullResistance(pin, resistance);
197    }
198
199    @Override
200    public PinPullResistance getPullResistance() {
201        return provider.getPullResistance(pin);
202    }
203
204    @Override
205    public boolean isPullResistance(PinPullResistance resistance) {
206        return (getPullResistance() == resistance);
207    }
208
209    @Override
210    public void high() {
211        setState(PinState.HIGH);
212    }
213
214    @Override
215    public void low() {
216        setState(PinState.LOW);
217    }
218
219    @Override
220    public void toggle() {
221        if (getState() == PinState.HIGH) {
222            setState(PinState.LOW);
223        } else {
224            setState(PinState.HIGH);
225        }
226    }
227
228    @Override
229    public Future<?> blink(long delay) {
230        return blink(delay, PinState.HIGH);
231    }
232
233    @Override
234    public Future<?> blink(long delay, PinState blinkState) {
235        // NOTE: a value of 0 milliseconds will stop the blinking
236        return blink(delay, 0, blinkState);
237    }
238
239    @Override
240    public Future<?> blink(long delay, long duration) {
241        return blink(delay, duration, PinState.HIGH);
242    }
243
244    @Override
245    public Future<?> blink(long delay, long duration, PinState blinkState) {
246        // NOTE: a value of 0 milliseconds will stop the blinking
247        return GpioScheduledExecutorImpl.blink(this, delay, duration, blinkState);
248    }
249    
250    @Override
251    public Future<?> pulse(long duration) {
252        return pulse(duration, false);
253    }
254
255    @Override
256    public Future<?> pulse(long duration, PinState pulseState) {
257        return pulse(duration, pulseState, false);
258    }
259    
260    @Override
261    public Future<?> pulse(long duration, boolean blocking) {
262        return pulse(duration, PinState.HIGH, blocking);
263    }
264
265    @Override
266    public Future<?> pulse(long duration, PinState pulseState, boolean blocking) {
267        
268        // validate duration argument
269        if(duration <= 0)
270            throw new IllegalArgumentException("Pulse duration must be greater than 0 milliseconds.");
271        
272        // if this is a blocking pulse, then execute the pulse 
273        // and sleep the caller's thread to block the operation 
274        // until the pulse is complete
275        if(blocking) {
276            // start the pulse state
277            setState(pulseState);
278            
279            // block the current thread for the pulse duration 
280            try {
281                Thread.sleep(duration);
282            }
283            catch (InterruptedException e) {
284                throw new RuntimeException("Pulse blocking thread interrupted.", e);
285            }
286
287            // end the pulse state
288            setState(PinState.getInverseState(pulseState));
289            
290            // we are done; no future is returned for blocking pulses
291            return null;
292        }
293        else {            
294            // if this is not a blocking call, then setup the pulse 
295            // instruction to be completed in a background worker
296            // thread pool using a scheduled executor 
297            return GpioScheduledExecutorImpl.pulse(this, duration, pulseState);
298        }
299    }
300    
301    @Override
302    public void setState(PinState state) {
303        provider.setState(pin, state);
304    }
305
306    @Override
307    public void setState(boolean state) {
308        provider.setState(pin, (state) ? PinState.HIGH : PinState.LOW);
309    }
310
311    @Override
312    public boolean isHigh() {
313        return (getState() == PinState.HIGH);
314    }
315
316    @Override
317    public boolean isLow() {
318        return (getState() == PinState.LOW);
319    }
320
321    @Override
322    public PinState getState() {
323        return provider.getState(pin);
324    }
325
326    @Override
327    public boolean isState(PinState state) {
328        return (getState() == state);
329    }
330
331    @Override
332    public void setValue(double value) {
333        provider.setValue(pin, value);
334    }
335    
336    @Override
337    public double getValue() {
338        return provider.getValue(pin);
339    }    
340
341    @Override
342    public void setPwm(int value) {
343        provider.setPwm(pin, value);
344    }
345    
346    @Override
347    public int getPwm() {
348        return provider.getPwm(pin);
349    }  
350    
351    private synchronized void updateInterruptListener() {
352        if (listeners.size() > 0 || triggers.size() > 0) {
353            if (monitor == null) { 
354                // create new monitor and register for event callbacks
355                monitor = new GpioEventMonitorExecutorImpl(this);
356                provider.addListener(pin, monitor);
357            }
358        } else {
359            if (monitor != null) {
360                // remove monitor and unregister for event callbacks
361                provider.removeListener(pin, monitor);
362
363                // destroy monitor instance
364                monitor = null;
365            }
366        }
367    }
368
369    /**
370     * 
371     * @param listener
372     */
373    public synchronized void addListener(GpioPinListener... listener) {
374        if (listener == null || listener.length == 0) {
375            throw new IllegalArgumentException("Missing listener argument.");
376        }
377        for (GpioPinListener lsnr : listener) {
378            listeners.add(lsnr);
379        }
380        updateInterruptListener();
381    }
382
383    public synchronized void addListener(List<? extends GpioPinListener> listeners) {
384        for (GpioPinListener listener : listeners) {
385            addListener(listener);
386        }
387    }
388
389    /**
390     * 
391     * @param listener
392     */
393    public synchronized Collection<GpioPinListener> getListeners() {
394        return listeners;
395    }
396
397    @Override
398    public boolean hasListener(GpioPinListener... listener) {
399        if (listener == null || listener.length == 0) {
400            throw new IllegalArgumentException("Missing listener argument.");
401        }
402        for (GpioPinListener lsnr : listener) {
403            if (!listeners.contains(lsnr)) {
404                return false;
405            }
406        }
407
408        return true;
409    }
410    
411    public synchronized void removeListener(GpioPinListener... listener) {
412        if (listener == null || listener.length == 0) {
413            throw new IllegalArgumentException("Missing listener argument.");
414        }
415        for (GpioPinListener lsnr : listener) {
416            listeners.remove(lsnr);
417        }
418        
419        updateInterruptListener();
420    }
421
422    public synchronized void removeListener(List<? extends GpioPinListener> listeners) {
423        for (GpioPinListener listener : listeners) {
424            removeListener(listener);
425        }
426    }
427    
428    public synchronized void removeAllListeners() {
429        for (int index = (listeners.size()-1); index >= 0; index --) {
430            GpioPinListener listener = listeners.get(index);
431            removeListener(listener);
432        }
433    }
434
435    /**
436     * 
437     * @param trigger
438     */
439    public synchronized Collection<GpioTrigger> getTriggers() {
440        return triggers;
441    }
442
443    public synchronized void addTrigger(GpioTrigger... trigger) {
444        if (trigger == null || trigger.length == 0) {
445            throw new IllegalArgumentException("Missing trigger argument.");
446        }
447        for (GpioTrigger trgr : trigger) {
448            triggers.add(trgr);
449        }
450        
451        updateInterruptListener();
452    }
453
454    public synchronized void addTrigger(List<? extends GpioTrigger> triggers) {
455        for (GpioTrigger trigger : triggers) {
456            addTrigger(trigger);
457        }
458    }
459
460    /**
461     * 
462     * @param trigger
463     */
464    public synchronized void removeTrigger(GpioTrigger... trigger) {
465        if (trigger == null || trigger.length == 0) {
466            throw new IllegalArgumentException("Missing trigger argument.");
467        }
468        for (GpioTrigger trgr : trigger) {
469            triggers.remove(trgr);
470        }
471        
472        updateInterruptListener();
473    }
474
475    public synchronized void removeTrigger(List<? extends GpioTrigger> triggers) {
476        for (GpioTrigger trigger : triggers) {
477            removeTrigger(trigger);
478        }
479    }
480
481    public synchronized void removeAllTriggers() {
482        for (int index = triggers.size() - 1; index >= 0; index--) {
483            GpioTrigger trigger = triggers.get(index);
484            removeTrigger(trigger);
485        }
486    }
487
488    @Override
489    public String toString() {
490        if (name != null && !name.isEmpty()) {
491            return String.format("\"%s\" <%s>", name, pin.toString());
492        } else {
493            return pin.toString();
494        }
495    }
496
497    @Override
498    public GpioPinShutdown getShutdownOptions() {
499        return shutdownOptions;
500    }
501
502    @Override
503    public void setShutdownOptions(GpioPinShutdown options) {
504        shutdownOptions.setUnexport(options.getUnexport());
505        shutdownOptions.setState(options.getState());
506        shutdownOptions.setMode(options.getMode());
507        shutdownOptions.setPullResistor(options.getPullResistor());
508    }
509
510    @Override
511    public void setShutdownOptions(Boolean unexport) {
512        setShutdownOptions(unexport, null);
513    }
514
515    @Override
516    public void setShutdownOptions(Boolean unexport, PinState state) {
517        setShutdownOptions(unexport, state, null);
518    }
519
520    @Override
521    public void setShutdownOptions(Boolean unexport, PinState state, PinPullResistance resistance)
522    {
523        setShutdownOptions(unexport, state, resistance, null);
524    }
525
526    @Override
527    public void setShutdownOptions(Boolean unexport, PinState state, PinPullResistance resistance, PinMode mode) {
528        shutdownOptions.setUnexport(unexport);
529        shutdownOptions.setState(state);
530        shutdownOptions.setMode(mode);
531        shutdownOptions.setPullResistor(resistance);
532    }
533
534}