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