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 - 2013 Pi4J 015 * %% 016 * Licensed under the Apache License, Version 2.0 (the "License"); you 017 * may not use this file except in compliance with the License. You may obtain a copy of the License 018 * at 019 * 020 * http://www.apache.org/licenses/LICENSE-2.0 021 * 022 * Unless required by applicable law or agreed to in writing, software distributed under the License 023 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 024 * or implied. See the License for the specific language governing permissions and limitations under 025 * the License. 026 * #L% 027 */ 028 029import java.util.ArrayList; 030import java.util.Collection; 031import java.util.List; 032import java.util.Map; 033import java.util.concurrent.ConcurrentHashMap; 034import java.util.concurrent.Future; 035 036import com.pi4j.io.gpio.GpioPinAnalogInput; 037import com.pi4j.io.gpio.GpioPinAnalogOutput; 038import com.pi4j.io.gpio.GpioController; 039import com.pi4j.io.gpio.GpioPinDigitalInput; 040import com.pi4j.io.gpio.GpioPinDigitalMultipurpose; 041import com.pi4j.io.gpio.GpioPinDigitalOutput; 042import com.pi4j.io.gpio.GpioPin; 043import com.pi4j.io.gpio.GpioPinInput; 044import com.pi4j.io.gpio.GpioPinOutput; 045import com.pi4j.io.gpio.GpioPinPwmOutput; 046import com.pi4j.io.gpio.GpioProvider; 047import com.pi4j.io.gpio.Pin; 048import com.pi4j.io.gpio.PinMode; 049import com.pi4j.io.gpio.PinPullResistance; 050import com.pi4j.io.gpio.GpioPinShutdown; 051import com.pi4j.io.gpio.PinState; 052import com.pi4j.io.gpio.event.GpioPinListener; 053import com.pi4j.io.gpio.event.PinListener; 054import com.pi4j.io.gpio.trigger.GpioTrigger; 055 056public class GpioPinImpl implements GpioPin, 057 GpioPinDigitalInput, 058 GpioPinDigitalOutput, 059 GpioPinDigitalMultipurpose, 060 GpioPinAnalogInput, 061 GpioPinAnalogOutput, 062 GpioPinPwmOutput, 063 GpioPinInput, 064 GpioPinOutput 065{ 066 067 @SuppressWarnings("unused") 068 private final GpioController gpio; 069 private String name = null; 070 private Object tag = null; 071 private final GpioProvider provider; 072 private final Pin pin; 073 private PinListener monitor; 074 private final GpioPinShutdownImpl shutdownOptions; 075 private final Map<String, String> properties = new ConcurrentHashMap<String, String>(); 076 private final List<GpioPinListener> listeners = new ArrayList<GpioPinListener>(); 077 private final List<GpioTrigger> triggers = new ArrayList<GpioTrigger>(); 078 079 public GpioPinImpl(GpioController gpio, GpioProvider provider, Pin pin) { 080 this.gpio = gpio; 081 this.provider = provider; 082 this.pin = pin; 083 shutdownOptions = new GpioPinShutdownImpl(); 084 } 085 086 @Override 087 public Pin getPin() { 088 return this.pin; 089 } 090 091 @Override 092 public GpioProvider getProvider() { 093 return this.provider; 094 } 095 096 @Override 097 public void setName(String name) { 098 this.name = name; 099 } 100 101 @Override 102 public String getName() { 103 if (name == null || name.length() == 0) { 104 return pin.toString(); 105 } 106 return name; 107 } 108 109 @Override 110 public void setTag(Object tag) { 111 this.tag = tag; 112 } 113 114 @Override 115 public Object getTag() { 116 return tag; 117 } 118 119 @Override 120 public void setProperty(String key, String value) { 121 properties.put(key, value); 122 } 123 124 @Override 125 public boolean hasProperty(String key) { 126 return properties.containsKey(key); 127 } 128 129 @Override 130 public String getProperty(String key, String defaultValue) { 131 if (properties.containsKey(key)) { 132 if(properties.get(key) == null || properties.get(key).isEmpty()) 133 return defaultValue; 134 else 135 return properties.get(key); 136 } 137 return defaultValue; 138 } 139 140 @Override 141 public String getProperty(String key) { 142 return getProperty(key, null); 143 } 144 145 @Override 146 public Map<String, String> getProperties() { 147 return properties; 148 } 149 150 @Override 151 public void removeProperty(String key) { 152 if (properties.containsKey(key)) { 153 properties.remove(key); 154 } 155 } 156 157 @Override 158 public void clearProperties() { 159 properties.clear(); 160 } 161 162 @Override 163 public void export(PinMode mode) { 164 // export the pin 165 provider.export(pin, mode); 166 } 167 168 @Override 169 public void unexport() { 170 // unexport the pin 171 provider.unexport(pin); 172 } 173 174 @Override 175 public boolean isExported() { 176 return provider.isExported(pin); 177 } 178 179 @Override 180 public void setMode(PinMode mode) { 181 provider.setMode(pin, mode); 182 } 183 184 @Override 185 public PinMode getMode() { 186 return provider.getMode(pin); 187 } 188 189 @Override 190 public boolean isMode(PinMode mode) { 191 return (getMode() == mode); 192 } 193 194 @Override 195 public void setPullResistance(PinPullResistance resistance) { 196 provider.setPullResistance(pin, resistance); 197 } 198 199 @Override 200 public PinPullResistance getPullResistance() { 201 return provider.getPullResistance(pin); 202 } 203 204 @Override 205 public boolean isPullResistance(PinPullResistance resistance) { 206 return (getPullResistance() == resistance); 207 } 208 209 @Override 210 public void high() { 211 setState(PinState.HIGH); 212 } 213 214 @Override 215 public void low() { 216 setState(PinState.LOW); 217 } 218 219 @Override 220 public void toggle() { 221 if (getState() == PinState.HIGH) { 222 setState(PinState.LOW); 223 } else { 224 setState(PinState.HIGH); 225 } 226 } 227 228 @Override 229 public Future<?> blink(long delay) { 230 return blink(delay, PinState.HIGH); 231 } 232 233 @Override 234 public Future<?> blink(long delay, PinState blinkState) { 235 // NOTE: a value of 0 milliseconds will stop the blinking 236 return blink(delay, 0, blinkState); 237 } 238 239 @Override 240 public Future<?> blink(long delay, long duration) { 241 return blink(delay, duration, PinState.HIGH); 242 } 243 244 @Override 245 public Future<?> blink(long delay, long duration, PinState blinkState) { 246 // NOTE: a value of 0 milliseconds will stop the blinking 247 return GpioScheduledExecutorImpl.blink(this, delay, duration, blinkState); 248 } 249 250 @Override 251 public Future<?> pulse(long duration) { 252 return pulse(duration, false); 253 } 254 255 @Override 256 public Future<?> pulse(long duration, PinState pulseState) { 257 return pulse(duration, pulseState, false); 258 } 259 260 @Override 261 public Future<?> pulse(long duration, boolean blocking) { 262 return pulse(duration, PinState.HIGH, blocking); 263 } 264 265 @Override 266 public Future<?> pulse(long duration, PinState pulseState, boolean blocking) { 267 268 // validate duration argument 269 if(duration <= 0) 270 throw new IllegalArgumentException("Pulse duration must be greater than 0 milliseconds."); 271 272 // if this is a blocking pulse, then execute the pulse 273 // and sleep the caller's thread to block the operation 274 // until the pulse is complete 275 if(blocking) { 276 // start the pulse state 277 setState(pulseState); 278 279 // block the current thread for the pulse duration 280 try { 281 Thread.sleep(duration); 282 } 283 catch (InterruptedException e) { 284 throw new RuntimeException("Pulse blocking thread interrupted.", e); 285 } 286 287 // end the pulse state 288 setState(PinState.getInverseState(pulseState)); 289 290 // we are done; no future is returned for blocking pulses 291 return null; 292 } 293 else { 294 // if this is not a blocking call, then setup the pulse 295 // instruction to be completed in a background worker 296 // thread pool using a scheduled executor 297 return GpioScheduledExecutorImpl.pulse(this, duration, pulseState); 298 } 299 } 300 301 @Override 302 public void setState(PinState state) { 303 provider.setState(pin, state); 304 } 305 306 @Override 307 public void setState(boolean state) { 308 provider.setState(pin, (state) ? PinState.HIGH : PinState.LOW); 309 } 310 311 @Override 312 public boolean isHigh() { 313 return (getState() == PinState.HIGH); 314 } 315 316 @Override 317 public boolean isLow() { 318 return (getState() == PinState.LOW); 319 } 320 321 @Override 322 public PinState getState() { 323 return provider.getState(pin); 324 } 325 326 @Override 327 public boolean isState(PinState state) { 328 return (getState() == state); 329 } 330 331 @Override 332 public void setValue(double value) { 333 provider.setValue(pin, value); 334 } 335 336 @Override 337 public double getValue() { 338 return provider.getValue(pin); 339 } 340 341 @Override 342 public void setPwm(int value) { 343 provider.setPwm(pin, value); 344 } 345 346 @Override 347 public int getPwm() { 348 return provider.getPwm(pin); 349 } 350 351 private synchronized void updateInterruptListener() { 352 if (listeners.size() > 0 || triggers.size() > 0) { 353 if (monitor == null) { 354 // create new monitor and register for event callbacks 355 monitor = new GpioEventMonitorExecutorImpl(this); 356 provider.addListener(pin, monitor); 357 } 358 } else { 359 if (monitor != null) { 360 // remove monitor and unregister for event callbacks 361 provider.removeListener(pin, monitor); 362 363 // destroy monitor instance 364 monitor = null; 365 } 366 } 367 } 368 369 /** 370 * 371 * @param listener 372 */ 373 public synchronized void addListener(GpioPinListener... listener) { 374 if (listener == null || listener.length == 0) { 375 throw new IllegalArgumentException("Missing listener argument."); 376 } 377 for (GpioPinListener lsnr : listener) { 378 listeners.add(lsnr); 379 } 380 updateInterruptListener(); 381 } 382 383 public synchronized void addListener(List<? extends GpioPinListener> listeners) { 384 for (GpioPinListener listener : listeners) { 385 addListener(listener); 386 } 387 } 388 389 /** 390 * 391 * @param listener 392 */ 393 public synchronized Collection<GpioPinListener> getListeners() { 394 return listeners; 395 } 396 397 @Override 398 public boolean hasListener(GpioPinListener... listener) { 399 if (listener == null || listener.length == 0) { 400 throw new IllegalArgumentException("Missing listener argument."); 401 } 402 for (GpioPinListener lsnr : listener) { 403 if (!listeners.contains(lsnr)) { 404 return false; 405 } 406 } 407 408 return true; 409 } 410 411 public synchronized void removeListener(GpioPinListener... listener) { 412 if (listener == null || listener.length == 0) { 413 throw new IllegalArgumentException("Missing listener argument."); 414 } 415 for (GpioPinListener lsnr : listener) { 416 listeners.remove(lsnr); 417 } 418 419 updateInterruptListener(); 420 } 421 422 public synchronized void removeListener(List<? extends GpioPinListener> listeners) { 423 for (GpioPinListener listener : listeners) { 424 removeListener(listener); 425 } 426 } 427 428 public synchronized void removeAllListeners() { 429 for (int index = (listeners.size()-1); index >= 0; index --) { 430 GpioPinListener listener = listeners.get(index); 431 removeListener(listener); 432 } 433 } 434 435 /** 436 * 437 * @param trigger 438 */ 439 public synchronized Collection<GpioTrigger> getTriggers() { 440 return triggers; 441 } 442 443 public synchronized void addTrigger(GpioTrigger... trigger) { 444 if (trigger == null || trigger.length == 0) { 445 throw new IllegalArgumentException("Missing trigger argument."); 446 } 447 for (GpioTrigger trgr : trigger) { 448 triggers.add(trgr); 449 } 450 451 updateInterruptListener(); 452 } 453 454 public synchronized void addTrigger(List<? extends GpioTrigger> triggers) { 455 for (GpioTrigger trigger : triggers) { 456 addTrigger(trigger); 457 } 458 } 459 460 /** 461 * 462 * @param trigger 463 */ 464 public synchronized void removeTrigger(GpioTrigger... trigger) { 465 if (trigger == null || trigger.length == 0) { 466 throw new IllegalArgumentException("Missing trigger argument."); 467 } 468 for (GpioTrigger trgr : trigger) { 469 triggers.remove(trgr); 470 } 471 472 updateInterruptListener(); 473 } 474 475 public synchronized void removeTrigger(List<? extends GpioTrigger> triggers) { 476 for (GpioTrigger trigger : triggers) { 477 removeTrigger(trigger); 478 } 479 } 480 481 public synchronized void removeAllTriggers() { 482 for (int index = triggers.size() - 1; index >= 0; index--) { 483 GpioTrigger trigger = triggers.get(index); 484 removeTrigger(trigger); 485 } 486 } 487 488 @Override 489 public String toString() { 490 if (name != null && !name.isEmpty()) { 491 return String.format("\"%s\" <%s>", name, pin.toString()); 492 } else { 493 return pin.toString(); 494 } 495 } 496 497 @Override 498 public GpioPinShutdown getShutdownOptions() { 499 return shutdownOptions; 500 } 501 502 @Override 503 public void setShutdownOptions(GpioPinShutdown options) { 504 shutdownOptions.setUnexport(options.getUnexport()); 505 shutdownOptions.setState(options.getState()); 506 shutdownOptions.setMode(options.getMode()); 507 shutdownOptions.setPullResistor(options.getPullResistor()); 508 } 509 510 @Override 511 public void setShutdownOptions(Boolean unexport) { 512 setShutdownOptions(unexport, null); 513 } 514 515 @Override 516 public void setShutdownOptions(Boolean unexport, PinState state) { 517 setShutdownOptions(unexport, state, null); 518 } 519 520 @Override 521 public void setShutdownOptions(Boolean unexport, PinState state, PinPullResistance resistance) 522 { 523 setShutdownOptions(unexport, state, resistance, null); 524 } 525 526 @Override 527 public void setShutdownOptions(Boolean unexport, PinState state, PinPullResistance resistance, PinMode mode) { 528 shutdownOptions.setUnexport(unexport); 529 shutdownOptions.setState(state); 530 shutdownOptions.setMode(mode); 531 shutdownOptions.setPullResistor(resistance); 532 } 533 534}