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 - 2021 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 pulse(duration, false, timeUnit);
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 pulseSync(long duration) throws InterruptedException{
383        pulseSync(duration, PinState.HIGH, TimeUnit.MILLISECONDS);
384    }
385
386    @Override
387    public void pulseSync(long duration, TimeUnit timeUnit) throws InterruptedException{
388        pulseSync(duration, PinState.HIGH, timeUnit);
389    }
390
391    @Override
392    public void pulseSync(long duration, PinState pulseState) throws InterruptedException{
393        pulseSync(duration, pulseState, TimeUnit.MILLISECONDS);
394    }
395
396    @Override
397    public void pulseSync(long duration, PinState pulseState, TimeUnit timeUnit) throws InterruptedException{
398
399        // validate duration argument
400        if (duration <= 0)
401            throw new IllegalArgumentException("Pulse duration must be greater than 0 milliseconds.");
402
403        // start the pulse state
404        setState(pulseState);
405
406        // block the current thread for the pulse duration
407        Thread.sleep(duration);
408
409        // end the pulse state
410        setState(PinState.getInverseState(pulseState));
411    }
412
413    @Override
414    public void setState(PinState state) {
415        provider.setState(pin, state);
416    }
417
418    @Override
419    public void setState(boolean state) {
420        provider.setState(pin, (state) ? PinState.HIGH : PinState.LOW);
421    }
422
423    @Override
424    public boolean isHigh() {
425        return (getState() == PinState.HIGH);
426    }
427
428    @Override
429    public boolean isLow() {
430        return (getState() == PinState.LOW);
431    }
432
433    @Override
434    public PinState getState() {
435        return provider.getState(pin);
436    }
437
438    @Override
439    public boolean isState(PinState state) {
440        return (getState() == state);
441    }
442
443    @Override
444    public boolean hasDebounce(PinState state) {
445        return (getDebounce(state) > NO_DEBOUCE);
446    }
447
448    @Override
449    public int getDebounce(PinState state) {
450        if (debounce.containsKey(state)) {
451            return debounce.get(state).intValue();
452        }
453        return NO_DEBOUCE;
454    }
455
456    @Override
457    public void setDebounce(int debounce, PinState... state) {
458        for (PinState ps : state) {
459            this.debounce.put(ps, new Integer(debounce));
460        }
461    }
462
463    @Override
464    public void setDebounce(int debounce) {
465        setDebounce(debounce, PinState.HIGH, PinState.LOW);
466    }
467
468    @Override
469    public void setValue(double value) {
470        provider.setValue(pin, value);
471    }
472
473    @Override
474    public void setValue(Number value) {
475        setValue(value.doubleValue());
476    }
477
478    @Override
479    public double getValue() {
480        return provider.getValue(pin);
481    }
482
483    @Override
484    public void setPwm(int value) {
485        provider.setPwm(pin, value);
486    }
487
488    @Override
489    public void setPwmRange(int range) {
490        provider.setPwmRange(pin, range);
491    }
492
493    @Override
494    public int getPwm() {
495        return provider.getPwm(pin);
496    }
497
498    private synchronized void updateInterruptListener() {
499        if (listeners.size() > 0 || triggers.size() > 0) {
500            if (monitor == null) {
501                // create new monitor and register for event callbacks
502                monitor = new GpioEventMonitorExecutorImpl(this);
503                provider.addListener(pin, monitor);
504            }
505        } else {
506            if (monitor != null) {
507                // remove monitor and unregister for event callbacks
508                provider.removeListener(pin, monitor);
509
510                // destroy monitor instance
511                monitor = null;
512            }
513        }
514    }
515
516    /**
517     * @param listener gpio pin listener interface
518     */
519    public synchronized void addListener(GpioPinListener... listener) {
520        if (listener == null || listener.length == 0) {
521            throw new IllegalArgumentException("Missing listener argument.");
522        }
523        Collections.addAll(listeners, listener);
524        updateInterruptListener();
525    }
526
527    public synchronized void addListener(List<? extends GpioPinListener> listeners) {
528        for (GpioPinListener listener : listeners) {
529            addListener(listener);
530        }
531    }
532
533    /**
534     *
535     */
536    public synchronized Collection<GpioPinListener> getListeners() {
537        return listeners;
538    }
539
540    @Override
541    public boolean hasListener(GpioPinListener... listener) {
542        if (listener == null || listener.length == 0) {
543            throw new IllegalArgumentException("Missing listener argument.");
544        }
545        for (GpioPinListener lsnr : listener) {
546            if (!listeners.contains(lsnr)) {
547                return false;
548            }
549        }
550
551        return true;
552    }
553
554    public synchronized void removeListener(GpioPinListener... listener) {
555        if (listener == null || listener.length == 0) {
556            throw new IllegalArgumentException("Missing listener argument.");
557        }
558        for (GpioPinListener lsnr : listener) {
559            listeners.remove(lsnr);
560        }
561
562        updateInterruptListener();
563    }
564
565    public synchronized void removeListener(List<? extends GpioPinListener> listeners) {
566        for (GpioPinListener listener : listeners) {
567            removeListener(listener);
568        }
569    }
570
571    public synchronized void removeAllListeners() {
572        List<GpioPinListener> listeners_copy = new ArrayList<>(listeners);
573        for (int index = (listeners_copy.size() - 1); index >= 0; index--) {
574            GpioPinListener listener = listeners_copy.get(index);
575            removeListener(listener);
576        }
577    }
578
579    /**
580     *
581     */
582    public synchronized Collection<GpioTrigger> getTriggers() {
583        return triggers;
584    }
585
586    public synchronized void addTrigger(GpioTrigger... trigger) {
587        if (trigger == null || trigger.length == 0) {
588            throw new IllegalArgumentException("Missing trigger argument.");
589        }
590        Collections.addAll(triggers, trigger);
591        updateInterruptListener();
592    }
593
594    public synchronized void addTrigger(List<? extends GpioTrigger> triggers) {
595        for (GpioTrigger trigger : triggers) {
596            addTrigger(trigger);
597        }
598    }
599
600    /**
601     * @param trigger GPIO trigger interface
602     */
603    public synchronized void removeTrigger(GpioTrigger... trigger) {
604        if (trigger == null || trigger.length == 0) {
605            throw new IllegalArgumentException("Missing trigger argument.");
606        }
607        for (GpioTrigger trgr : trigger) {
608            triggers.remove(trgr);
609        }
610
611        updateInterruptListener();
612    }
613
614    public synchronized void removeTrigger(List<? extends GpioTrigger> triggers) {
615        for (GpioTrigger trigger : triggers) {
616            removeTrigger(trigger);
617        }
618    }
619
620    public synchronized void removeAllTriggers() {
621        List<GpioTrigger> triggers_copy = new ArrayList<>(triggers);
622        for (int index = triggers_copy.size() - 1; index >= 0; index--) {
623            GpioTrigger trigger = triggers_copy.get(index);
624            removeTrigger(trigger);
625        }
626    }
627
628    @Override
629    public String toString() {
630        if (name != null && !name.isEmpty()) {
631            return String.format("\"%s\" <%s>", name, pin.toString());
632        } else {
633            return pin.toString();
634        }
635    }
636
637    @Override
638    public GpioPinShutdown getShutdownOptions() {
639        return shutdownOptions;
640    }
641
642    @Override
643    public void setShutdownOptions(GpioPinShutdown options) {
644        shutdownOptions.setUnexport(options.getUnexport());
645        shutdownOptions.setState(options.getState());
646        shutdownOptions.setMode(options.getMode());
647        shutdownOptions.setPullResistor(options.getPullResistor());
648    }
649
650    @Override
651    public void setShutdownOptions(Boolean unexport) {
652        setShutdownOptions(unexport, null);
653    }
654
655    @Override
656    public void setShutdownOptions(Boolean unexport, PinState state) {
657        setShutdownOptions(unexport, state, null);
658    }
659
660    @Override
661    public void setShutdownOptions(Boolean unexport, PinState state, PinPullResistance resistance) {
662        setShutdownOptions(unexport, state, resistance, null);
663    }
664
665    @Override
666    public void setShutdownOptions(Boolean unexport, PinState state, PinPullResistance resistance, PinMode mode) {
667        shutdownOptions.setUnexport(unexport);
668        shutdownOptions.setState(state);
669        shutdownOptions.setMode(mode);
670        shutdownOptions.setPullResistor(resistance);
671    }
672
673}