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}