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}