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