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:  https://www.pi4j.com/
012 * **********************************************************************
013 * %%
014 * Copyright (C) 2012 - 2019 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;
041import java.util.concurrent.TimeUnit;
042
043public class GpioPinImpl implements GpioPin,
044        GpioPinDigitalInput,
045        GpioPinDigitalOutput,
046        GpioPinDigitalMultipurpose,
047        GpioPinAnalogInput,
048        GpioPinAnalogOutput,
049        GpioPinPwmOutput,
050        GpioPinInput,
051        GpioPinOutput {
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, TimeUnit timeUnit) {
228        return blink(delay, PinState.HIGH, timeUnit);
229    }
230
231    @Override
232    public Future<?> blink(long delay, PinState blinkState) {
233        // NOTE: a value of 0 milliseconds will stop the blinking
234        return blink(delay, 0, blinkState);
235    }
236
237    @Override
238    public Future<?> blink(long delay, PinState blinkState, TimeUnit timeUnit) {
239        return blink(delay, 0, blinkState, timeUnit);
240    }
241
242    @Override
243    public Future<?> blink(long delay, long duration) {
244        return blink(delay, duration, PinState.HIGH);
245    }
246
247    @Override
248    public Future<?> blink(long delay, long duration, TimeUnit timeUnit) {
249        return blink(delay, duration, PinState.HIGH, timeUnit);
250    }
251
252    @Override
253    public Future<?> blink(long delay, long duration, PinState blinkState) {
254        // NOTE: a value of 0 milliseconds will stop the blinking
255        return GpioScheduledExecutorImpl.blink(this, delay, duration, blinkState, TimeUnit.MILLISECONDS);
256    }
257
258    @Override
259    public Future<?> blink(long delay, long duration, PinState blinkState, TimeUnit timeUnit) {
260        return GpioScheduledExecutorImpl.blink(this, delay, duration, blinkState, timeUnit);
261    }
262
263    @Override
264    public Future<?> pulse(long duration) {
265        return pulse(duration, false);
266    }
267
268    @Override
269    public Future<?> pulse(long duration, TimeUnit timeUnit) {
270        return null;
271    }
272
273    @Override
274    public Future<?> pulse(long duration, Callable<Void> callback) {
275        return pulse(duration, false, callback);
276    }
277
278    @Override
279    public Future<?> pulse(long duration, Callable<Void> callback, TimeUnit timeUnit) {
280        return pulse(duration, false, callback, timeUnit);
281    }
282
283    @Override
284    public Future<?> pulse(long duration, PinState pulseState) {
285        return pulse(duration, pulseState, false);
286    }
287
288    @Override
289    public Future<?> pulse(long duration, PinState pulseState, TimeUnit timeUnit) {
290        return pulse(duration, pulseState, false, timeUnit);
291    }
292
293    @Override
294    public Future<?> pulse(long duration, PinState pulseState, Callable<Void> callback) {
295        return pulse(duration, pulseState, false, callback);
296    }
297
298    @Override
299    public Future<?> pulse(long duration, PinState pulseState, Callable<Void> callback, TimeUnit timeUnit) {
300        return pulse(duration, pulseState, false, callback, timeUnit);
301    }
302
303    @Override
304    public Future<?> pulse(long duration, boolean blocking) {
305        return pulse(duration, PinState.HIGH, blocking);
306    }
307
308    @Override
309    public Future<?> pulse(long duration, boolean blocking, TimeUnit timeUnit) {
310        return pulse(duration, PinState.HIGH, blocking, timeUnit);
311    }
312
313    @Override
314    public Future<?> pulse(long duration, boolean blocking, Callable<Void> callback) {
315        return pulse(duration, PinState.HIGH, blocking, callback);
316    }
317
318    @Override
319    public Future<?> pulse(long duration, boolean blocking, Callable<Void> callback, TimeUnit timeUnit) {
320        return pulse(duration, PinState.HIGH, blocking, callback, timeUnit);
321    }
322
323    @Override
324    public Future<?> pulse(long duration, PinState pulseState, boolean blocking) {
325        return pulse(duration, pulseState, blocking, null, TimeUnit.MILLISECONDS);
326    }
327
328    @Override
329    public Future<?> pulse(long duration, PinState pulseState, boolean blocking, TimeUnit timeUnit) {
330        return pulse(duration, pulseState, blocking, null, timeUnit);
331    }
332
333    @Override
334    public Future<?> pulse(long duration, PinState pulseState, boolean blocking, Callable<Void> callback, TimeUnit timeUnit) {
335
336        // validate duration argument
337        if (duration <= 0)
338            throw new IllegalArgumentException("Pulse duration must be greater than 0 milliseconds.");
339
340        // if this is a blocking pulse, then execute the pulse
341        // and sleep the caller's thread to block the operation
342        // until the pulse is complete
343        if (blocking) {
344            // start the pulse state
345            setState(pulseState);
346
347            // block the current thread for the pulse duration
348            try {
349                Thread.sleep(duration);
350            } catch (InterruptedException e) {
351                throw new RuntimeException("Pulse blocking thread interrupted.", e);
352            }
353
354            // end the pulse state
355            setState(PinState.getInverseState(pulseState));
356
357            // invoke callback if one was defined
358            if (callback != null) {
359                try {
360                    callback.call();
361                } catch (Exception e) {
362                    e.printStackTrace();
363                }
364            }
365
366            // we are done; no future is returned for blocking pulses
367            return null;
368        } else {
369            // if this is not a blocking call, then setup the pulse
370            // instruction to be completed in a background worker
371            // thread pool using a scheduled executor
372            return GpioScheduledExecutorImpl.pulse(this, duration, pulseState, callback, timeUnit);
373        }
374    }
375
376    @Override
377    public Future<?> pulse(long duration, PinState pulseState, boolean blocking, Callable<Void> callback) {
378        return pulse(duration, pulseState, blocking, callback, TimeUnit.MILLISECONDS);
379    }
380
381    @Override
382    public void setState(PinState state) {
383        provider.setState(pin, state);
384    }
385
386    @Override
387    public void setState(boolean state) {
388        provider.setState(pin, (state) ? PinState.HIGH : PinState.LOW);
389    }
390
391    @Override
392    public boolean isHigh() {
393        return (getState() == PinState.HIGH);
394    }
395
396    @Override
397    public boolean isLow() {
398        return (getState() == PinState.LOW);
399    }
400
401    @Override
402    public PinState getState() {
403        return provider.getState(pin);
404    }
405
406    @Override
407    public boolean isState(PinState state) {
408        return (getState() == state);
409    }
410
411    @Override
412    public boolean hasDebounce(PinState state) {
413        return (getDebounce(state) > NO_DEBOUCE);
414    }
415
416    @Override
417    public int getDebounce(PinState state) {
418        if (debounce.containsKey(state)) {
419            return debounce.get(state).intValue();
420        }
421        return NO_DEBOUCE;
422    }
423
424    @Override
425    public void setDebounce(int debounce, PinState... state) {
426        for (PinState ps : state) {
427            this.debounce.put(ps, new Integer(debounce));
428        }
429    }
430
431    @Override
432    public void setDebounce(int debounce) {
433        setDebounce(debounce, PinState.HIGH, PinState.LOW);
434    }
435
436    @Override
437    public void setValue(double value) {
438        provider.setValue(pin, value);
439    }
440
441    @Override
442    public void setValue(Number value) {
443        setValue(value.doubleValue());
444    }
445
446    @Override
447    public double getValue() {
448        return provider.getValue(pin);
449    }
450
451    @Override
452    public void setPwm(int value) {
453        provider.setPwm(pin, value);
454    }
455
456    @Override
457    public void setPwmRange(int range) {
458        provider.setPwmRange(pin, range);
459    }
460
461    @Override
462    public int getPwm() {
463        return provider.getPwm(pin);
464    }
465
466    private synchronized void updateInterruptListener() {
467        if (listeners.size() > 0 || triggers.size() > 0) {
468            if (monitor == null) {
469                // create new monitor and register for event callbacks
470                monitor = new GpioEventMonitorExecutorImpl(this);
471                provider.addListener(pin, monitor);
472            }
473        } else {
474            if (monitor != null) {
475                // remove monitor and unregister for event callbacks
476                provider.removeListener(pin, monitor);
477
478                // destroy monitor instance
479                monitor = null;
480            }
481        }
482    }
483
484    /**
485     * @param listener gpio pin listener interface
486     */
487    public synchronized void addListener(GpioPinListener... listener) {
488        if (listener == null || listener.length == 0) {
489            throw new IllegalArgumentException("Missing listener argument.");
490        }
491        Collections.addAll(listeners, listener);
492        updateInterruptListener();
493    }
494
495    public synchronized void addListener(List<? extends GpioPinListener> listeners) {
496        for (GpioPinListener listener : listeners) {
497            addListener(listener);
498        }
499    }
500
501    /**
502     *
503     */
504    public synchronized Collection<GpioPinListener> getListeners() {
505        return listeners;
506    }
507
508    @Override
509    public boolean hasListener(GpioPinListener... listener) {
510        if (listener == null || listener.length == 0) {
511            throw new IllegalArgumentException("Missing listener argument.");
512        }
513        for (GpioPinListener lsnr : listener) {
514            if (!listeners.contains(lsnr)) {
515                return false;
516            }
517        }
518
519        return true;
520    }
521
522    public synchronized void removeListener(GpioPinListener... listener) {
523        if (listener == null || listener.length == 0) {
524            throw new IllegalArgumentException("Missing listener argument.");
525        }
526        for (GpioPinListener lsnr : listener) {
527            listeners.remove(lsnr);
528        }
529
530        updateInterruptListener();
531    }
532
533    public synchronized void removeListener(List<? extends GpioPinListener> listeners) {
534        for (GpioPinListener listener : listeners) {
535            removeListener(listener);
536        }
537    }
538
539    public synchronized void removeAllListeners() {
540        List<GpioPinListener> listeners_copy = new ArrayList<>(listeners);
541        for (int index = (listeners_copy.size() - 1); index >= 0; index--) {
542            GpioPinListener listener = listeners_copy.get(index);
543            removeListener(listener);
544        }
545    }
546
547    /**
548     *
549     */
550    public synchronized Collection<GpioTrigger> getTriggers() {
551        return triggers;
552    }
553
554    public synchronized void addTrigger(GpioTrigger... trigger) {
555        if (trigger == null || trigger.length == 0) {
556            throw new IllegalArgumentException("Missing trigger argument.");
557        }
558        Collections.addAll(triggers, trigger);
559        updateInterruptListener();
560    }
561
562    public synchronized void addTrigger(List<? extends GpioTrigger> triggers) {
563        for (GpioTrigger trigger : triggers) {
564            addTrigger(trigger);
565        }
566    }
567
568    /**
569     * @param trigger GPIO trigger interface
570     */
571    public synchronized void removeTrigger(GpioTrigger... trigger) {
572        if (trigger == null || trigger.length == 0) {
573            throw new IllegalArgumentException("Missing trigger argument.");
574        }
575        for (GpioTrigger trgr : trigger) {
576            triggers.remove(trgr);
577        }
578
579        updateInterruptListener();
580    }
581
582    public synchronized void removeTrigger(List<? extends GpioTrigger> triggers) {
583        for (GpioTrigger trigger : triggers) {
584            removeTrigger(trigger);
585        }
586    }
587
588    public synchronized void removeAllTriggers() {
589        List<GpioTrigger> triggers_copy = new ArrayList<>(triggers);
590        for (int index = triggers_copy.size() - 1; index >= 0; index--) {
591            GpioTrigger trigger = triggers_copy.get(index);
592            removeTrigger(trigger);
593        }
594    }
595
596    @Override
597    public String toString() {
598        if (name != null && !name.isEmpty()) {
599            return String.format("\"%s\" <%s>", name, pin.toString());
600        } else {
601            return pin.toString();
602        }
603    }
604
605    @Override
606    public GpioPinShutdown getShutdownOptions() {
607        return shutdownOptions;
608    }
609
610    @Override
611    public void setShutdownOptions(GpioPinShutdown options) {
612        shutdownOptions.setUnexport(options.getUnexport());
613        shutdownOptions.setState(options.getState());
614        shutdownOptions.setMode(options.getMode());
615        shutdownOptions.setPullResistor(options.getPullResistor());
616    }
617
618    @Override
619    public void setShutdownOptions(Boolean unexport) {
620        setShutdownOptions(unexport, null);
621    }
622
623    @Override
624    public void setShutdownOptions(Boolean unexport, PinState state) {
625        setShutdownOptions(unexport, state, null);
626    }
627
628    @Override
629    public void setShutdownOptions(Boolean unexport, PinState state, PinPullResistance resistance) {
630        setShutdownOptions(unexport, state, resistance, null);
631    }
632
633    @Override
634    public void setShutdownOptions(Boolean unexport, PinState state, PinPullResistance resistance, PinMode mode) {
635        shutdownOptions.setUnexport(unexport);
636        shutdownOptions.setState(state);
637        shutdownOptions.setMode(mode);
638        shutdownOptions.setPullResistor(resistance);
639    }
640
641}