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 - 2016 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 void setValue(Number value) { setValue(value.doubleValue()); }
385
386    @Override
387    public double getValue() {
388        return provider.getValue(pin);
389    }
390
391    @Override
392    public void setPwm(int value) {
393        provider.setPwm(pin, value);
394    }
395
396    @Override
397    public void setPwmRange(int range) {
398        provider.setPwmRange(pin, range);
399    }
400
401    @Override
402    public int getPwm() {
403        return provider.getPwm(pin);
404    }
405
406    private synchronized void updateInterruptListener() {
407        if (listeners.size() > 0 || triggers.size() > 0) {
408            if (monitor == null) {
409                // create new monitor and register for event callbacks
410                monitor = new GpioEventMonitorExecutorImpl(this);
411                provider.addListener(pin, monitor);
412            }
413        } else {
414            if (monitor != null) {
415                // remove monitor and unregister for event callbacks
416                provider.removeListener(pin, monitor);
417
418                // destroy monitor instance
419                monitor = null;
420            }
421        }
422    }
423
424    /**
425     *
426     * @param listener gpio pin listener interface
427     */
428    public synchronized void addListener(GpioPinListener... listener) {
429        if (listener == null || listener.length == 0) {
430            throw new IllegalArgumentException("Missing listener argument.");
431        }
432        Collections.addAll(listeners, listener);
433        updateInterruptListener();
434    }
435
436    public synchronized void addListener(List<? extends GpioPinListener> listeners) {
437        for (GpioPinListener listener : listeners) {
438            addListener(listener);
439        }
440    }
441
442    /**
443     *
444     */
445    public synchronized Collection<GpioPinListener> getListeners() {
446        return listeners;
447    }
448
449    @Override
450    public boolean hasListener(GpioPinListener... listener) {
451        if (listener == null || listener.length == 0) {
452            throw new IllegalArgumentException("Missing listener argument.");
453        }
454        for (GpioPinListener lsnr : listener) {
455            if (!listeners.contains(lsnr)) {
456                return false;
457            }
458        }
459
460        return true;
461    }
462
463    public synchronized void removeListener(GpioPinListener... listener) {
464        if (listener == null || listener.length == 0) {
465            throw new IllegalArgumentException("Missing listener argument.");
466        }
467        for (GpioPinListener lsnr : listener) {
468            listeners.remove(lsnr);
469        }
470
471        updateInterruptListener();
472    }
473
474    public synchronized void removeListener(List<? extends GpioPinListener> listeners) {
475        for (GpioPinListener listener : listeners) {
476            removeListener(listener);
477        }
478    }
479
480    public synchronized void removeAllListeners() {
481        List<GpioPinListener> listeners_copy = new ArrayList<>(listeners);
482        for (int index = (listeners_copy.size()-1); index >= 0; index --) {
483            GpioPinListener listener = listeners_copy.get(index);
484            removeListener(listener);
485        }
486    }
487
488    /**
489     *
490     */
491    public synchronized Collection<GpioTrigger> getTriggers() {
492        return triggers;
493    }
494
495    public synchronized void addTrigger(GpioTrigger... trigger) {
496        if (trigger == null || trigger.length == 0) {
497            throw new IllegalArgumentException("Missing trigger argument.");
498        }
499        Collections.addAll(triggers, trigger);
500        updateInterruptListener();
501    }
502
503    public synchronized void addTrigger(List<? extends GpioTrigger> triggers) {
504        for (GpioTrigger trigger : triggers) {
505            addTrigger(trigger);
506        }
507    }
508
509    /**
510     *
511     * @param trigger GPIO trigger interface
512     */
513    public synchronized void removeTrigger(GpioTrigger... trigger) {
514        if (trigger == null || trigger.length == 0) {
515            throw new IllegalArgumentException("Missing trigger argument.");
516        }
517        for (GpioTrigger trgr : trigger) {
518            triggers.remove(trgr);
519        }
520
521        updateInterruptListener();
522    }
523
524    public synchronized void removeTrigger(List<? extends GpioTrigger> triggers) {
525        for (GpioTrigger trigger : triggers) {
526            removeTrigger(trigger);
527        }
528    }
529
530    public synchronized void removeAllTriggers() {
531        List<GpioTrigger> triggers_copy = new ArrayList<>(triggers);
532        for (int index = triggers_copy.size() - 1; index >= 0; index--) {
533            GpioTrigger trigger = triggers_copy.get(index);
534            removeTrigger(trigger);
535        }
536    }
537
538    @Override
539    public String toString() {
540        if (name != null && !name.isEmpty()) {
541            return String.format("\"%s\" <%s>", name, pin.toString());
542        } else {
543            return pin.toString();
544        }
545    }
546
547    @Override
548    public GpioPinShutdown getShutdownOptions() {
549        return shutdownOptions;
550    }
551
552    @Override
553    public void setShutdownOptions(GpioPinShutdown options) {
554        shutdownOptions.setUnexport(options.getUnexport());
555        shutdownOptions.setState(options.getState());
556        shutdownOptions.setMode(options.getMode());
557        shutdownOptions.setPullResistor(options.getPullResistor());
558    }
559
560    @Override
561    public void setShutdownOptions(Boolean unexport) {
562        setShutdownOptions(unexport, null);
563    }
564
565    @Override
566    public void setShutdownOptions(Boolean unexport, PinState state) {
567        setShutdownOptions(unexport, state, null);
568    }
569
570    @Override
571    public void setShutdownOptions(Boolean unexport, PinState state, PinPullResistance resistance)
572    {
573        setShutdownOptions(unexport, state, resistance, null);
574    }
575
576    @Override
577    public void setShutdownOptions(Boolean unexport, PinState state, PinPullResistance resistance, PinMode mode) {
578        shutdownOptions.setUnexport(unexport);
579        shutdownOptions.setState(state);
580        shutdownOptions.setMode(mode);
581        shutdownOptions.setPullResistor(resistance);
582    }
583
584}