Package org.openhab.binding.gpio.internal

Source Code of org.openhab.binding.gpio.internal.GPIOBinding

/**
* Copyright (c) 2010-2014, openHAB.org and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.binding.gpio.internal;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;


import org.apache.commons.collections.BidiMap;
import org.apache.commons.collections.bidimap.DualHashBidiMap;
import org.openhab.binding.gpio.GPIOBindingProvider;
import org.openhab.core.binding.AbstractBinding;
import org.openhab.core.binding.BindingProvider;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.types.Command;
import org.openhab.core.types.UnDefType;
import org.openhab.io.gpio.GPIO;
import org.openhab.io.gpio.GPIOPin;
import org.openhab.io.gpio.GPIOPinEventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Binds items with GPIO pins. Manages live for backend GPIO pin
* objects and translates GPIO events to item events.
*
* @author Dancho Penev
* @since 1.5.0
*/
public class GPIOBinding extends AbstractBinding<GPIOBindingProvider> implements GPIOPinEventHandler {

  private static final Logger logger = LoggerFactory.getLogger(GPIOBinding.class);
  private static final long REGISTRYLOCK_TIMEOUT = 10;
  private static final TimeUnit REGISTRYLOCK_TIMEOUT_UNITS = TimeUnit.SECONDS;

  /** Read/write lock protecting item/pin object registry. */
  private final ReentrantReadWriteLock registryLock = new ReentrantReadWriteLock();

  /** Bidirectional map for storing registered items and their
   * corresponding pin objects.
   */
  private final BidiMap registry = new DualHashBidiMap();

  /** GPIO IO service. */
  private volatile GPIO gpio = null;

  @Override
  public void allBindingsChanged(BindingProvider provider) {

    if (gpio != null) {
      for (String itemName : provider.getItemNames()) {
        changeItem((GPIOBindingProvider) provider, itemName);
      }
    }

    super.allBindingsChanged(provider);
  }

  @Override
  public void bindingChanged(BindingProvider provider, String itemName) {

    if (gpio != null) {
      changeItem((GPIOBindingProvider) provider, itemName);
    }

    super.bindingChanged(provider, itemName);
  }

  @Override
  protected void internalReceiveCommand(String itemName, Command command) {

    if (gpio != null) {
      try {
        if (registryLock.readLock().tryLock(REGISTRYLOCK_TIMEOUT, REGISTRYLOCK_TIMEOUT_UNITS)) {
          try {
            GPIOPin gpioPin = (GPIOPin) registry.get(itemName);

            if (gpioPin != null) {
              if (command == OnOffType.ON) {
                gpioPin.setValue(GPIOPin.VALUE_HIGH);
              } else {
                gpioPin.setValue(GPIOPin.VALUE_LOW);
              }
            }
          } catch (IOException e) {
            logger.error("Error occured while changing pin state for item " + itemName + ", exception: " + e.getMessage());
            return;
          } finally {
            registryLock.readLock().unlock();
          }
        } else {
          logger.error("Pin state for item " + itemName + " hasn't been changed, timeout expired while waiting for registry lock");
          return;
        }
      } catch (InterruptedException e) {
        logger.error("Pin state for item " + itemName + " hasn't been changed, thread was interrupted while waiting for registry lock");
        return;
      }
    }
    super.internalReceiveCommand(itemName, command);
  }

  /**
   * Called when GPIO OSGi service is available, creates and configure
   * backend objects for configured items.
   * 
   * @param gpio GPIO OSGi service
   */
  public void bindGPIO(GPIO gpio) {

    this.gpio = gpio;

    for (GPIOBindingProvider provider : providers) {
      for (String itemName : provider.getItemNames()) {
        if (provider.isItemConfigured(itemName)) {
          newItem(provider, itemName);
        }
      }
    }
  }

  /**
   * Called when GPIO OSGi service is stopped, deletes previously
   * created backend objects for configured items.
   *
   * @param gpio GPIO OSGi service
   */
  public void unbindGPIO(GPIO gpio) {

    for (GPIOBindingProvider provider : providers) {
      for (String itemName : provider.getItemNames()) {
        deleteItem(itemName);
      }
    }

    this.gpio = null;
  }

  public void onEvent(GPIOPin pin, int value) {

    try {
      if (registryLock.readLock().tryLock(REGISTRYLOCK_TIMEOUT, REGISTRYLOCK_TIMEOUT_UNITS)) {
        try {
          String itemName = (String) registry.getKey(pin);

          /* The item may be deleted while waiting to acquire read lock */
          if (itemName != null) {
            if (value == GPIOPin.VALUE_HIGH) {
              eventPublisher.postUpdate(itemName, OpenClosedType.OPEN);
            } else {
              eventPublisher.postUpdate(itemName, OpenClosedType.CLOSED);
            }
          }
        } finally {
          registryLock.readLock().unlock();
        }
      } else {
        logger.error("Item state hasn't been changed, timeout expired while waiting for registry lock");
      }
    } catch (InterruptedException e) {
      logger.error("Item state hasn't been changed, thread was interrupted while waiting for registry lock");
    }
  }

  public void onError(GPIOPin pin, Exception exception) {

    String itemName = null;

    logger.error("Error occured in pin event processing, exception: " + exception.getMessage());

    try {
      if (registryLock.readLock().tryLock(REGISTRYLOCK_TIMEOUT, REGISTRYLOCK_TIMEOUT_UNITS)) {
        try {
          itemName = (String) registry.getKey(pin);
        } finally {
          registryLock.readLock().unlock();
        }
      } else {
        logger.error("Item name can't be determined, timeout expired while waiting for registry lock");
      }
    } catch (InterruptedException e) {
      logger.error("Item name can't be determined, thread was interrupted while waiting for registry lock");
    }

    /* The item may be deleted while waiting to acquire read lock */
    if (itemName != null) {
      deleteItem(itemName);
    }
  }

  /**
   * Changes backend GPIO pin object for the item.
   *
   * @param provider binding provider
   * @param itemName the name of the item which to process
   */
  private void changeItem(GPIOBindingProvider provider, String itemName) {

    if (provider.isItemConfigured(itemName)) {

      /* The item configuration was changed */

      try {
        if (registryLock.writeLock().tryLock(REGISTRYLOCK_TIMEOUT, REGISTRYLOCK_TIMEOUT_UNITS)) {
          try {
            GPIOPin gpioPin = (GPIOPin) registry.get(itemName);

            /* Existing or new item */
            if (gpioPin != null) {

              /* Pin number change requires deletion of old and creation of new backend object */
              if (gpioPin.getPinNumber() != provider.getPinNumber(itemName)) {
                deleteItem(itemName);
                newItem(provider, itemName);
              } else {
                int newActiveLow = provider.getActiveLow(itemName);
                int currentDirection = gpioPin.getDirection();
                int newDirection = provider.getDirection(itemName);

                if (newActiveLow != gpioPin.getActiveLow()) {
                  gpioPin.setActiveLow(newActiveLow);
                }

                if (newDirection != currentDirection) {
                  if (currentDirection == GPIOPin.DIRECTION_IN) {
                    /* Tracking interrupts on output pins is meaningless */
                    gpioPin.removeEventHandler(this);
                  }
                  gpioPin.setDirection(newDirection);
                  if (newDirection == GPIOPin.DIRECTION_IN) {
                    gpioPin.setEdgeDetection(GPIOPin.EDGEDETECTION_BOTH);
                    gpioPin.addEventHandler(this);
                  }
                }

                /* Debouncing is valid only for input pins */
                if (newDirection == GPIOPin.DIRECTION_IN) {
                  long currentDebounceInterval = gpioPin.getDebounceInterval();
                  long defaultDebounceInterval = gpio.getDefaultDebounceInterval();
                  long newDebounceInterval = provider.getDebounceInterval(itemName);

                  /* If debounceInterval isn't configured its value is GPIOBindingProvider.DEBOUNCEINTERVAL_UNDEFINED */
                  if (newDebounceInterval != GPIOBindingProvider.DEBOUNCEINTERVAL_UNDEFINED) {
                    if (newDebounceInterval != currentDebounceInterval) {
                      gpioPin.setDebounceInterval(newDebounceInterval);
                    }
                  } else {
                    /* Revert back to default if was set before and after then deleted */
                    if (currentDebounceInterval != defaultDebounceInterval) {
                      gpioPin.setDebounceInterval(defaultDebounceInterval);
                    }
                  }                 
                }
              }
            } else {
              newItem(provider, itemName);
            }
          } catch (Exception e) {
            logger.error("Error occured while changing backend object for item " + itemName + ", exception: " + e.getMessage());
          } finally {
            registryLock.writeLock().unlock();
          }
        } else {
          logger.error("Item " + itemName + " hasn't been changed, timeout expired while waiting for registry lock");
        }
      } catch (InterruptedException e) {
        logger.error("Item " + itemName + " hasn't been changed, thread was interrupted while waiting for registry lock");
      }
    } else {

      /* The item was deleted from configuration file or system is shut down */
      deleteItem(itemName);
    }
  }

  /**
   * Setup new backend GPIO pin object and relate it with the item.
   *
   * @param provider binding provider
   * @param itemName the name of the item which to process
   */
  private void newItem(GPIOBindingProvider provider, String itemName) {

    try {
      int direction;

      GPIOPin gpioPin = gpio.reservePin(provider.getPinNumber(itemName));

      gpioPin.setActiveLow(provider.getActiveLow(itemName));

      direction = provider.getDirection(itemName);
      gpioPin.setDirection(direction);

      /* Edge detection and debouncing are valid only for input pins */
      if (direction == GPIOPin.DIRECTION_IN) {
        long debounceInterval = provider.getDebounceInterval(itemName);

        gpioPin.setEdgeDetection(GPIOPin.EDGEDETECTION_BOTH);

        /* If debounceInterval isn't configured its value is GPIOBindingProvider.DEBOUNCEINTERVAL_UNDEFINED */
        if (debounceInterval != GPIOBindingProvider.DEBOUNCEINTERVAL_UNDEFINED) {
          gpioPin.setDebounceInterval(debounceInterval);
        }
      }

      /* Register the pin */
      try {
        if (registryLock.writeLock().tryLock(REGISTRYLOCK_TIMEOUT, REGISTRYLOCK_TIMEOUT_UNITS)) {
          try {
            registry.put(itemName, gpioPin);
          } finally {
            registryLock.writeLock().unlock();
          }
        } else {
          logger.error("Item " + itemName + " hasn't been inserted into the registry, timeout expired while waiting for registry lock");
          return;
        }
      } catch (InterruptedException e) {
        logger.error("Item " + itemName + " hasn't been inserted into the registry, thread was interrupted while waiting for registry lock");
        return;
      }

      /* Set initial item state */
      if (direction == GPIOPin.DIRECTION_IN) {

        /* Item type 'Contact' */
        if (gpioPin.getValue() == GPIOPin.VALUE_HIGH) {
          eventPublisher.postUpdate(itemName, OpenClosedType.OPEN);
        } else {
          eventPublisher.postUpdate(itemName, OpenClosedType.CLOSED);
        }
      } else {

        /* Item type 'Switch' */
        if (gpioPin.getValue() == GPIOPin.VALUE_HIGH) {
          eventPublisher.postUpdate(itemName, OnOffType.ON);
        } else {
          eventPublisher.postUpdate(itemName, OnOffType.OFF);
        }
      }

      /* The item is of type 'Contact', register for state change notifications */
      if (direction == GPIOPin.DIRECTION_IN) {
        gpioPin.addEventHandler(this);
      }
    } catch (Exception e) {
      logger.error("Error occured while creating backend object for item " +itemName + ", exception: "+ e.getMessage());
    }
  }

  /**
   * Destroys backend GPIO pin object and clear relationship with the item.
   *
   * @param itemName the name of the item which to process
   */
  private void deleteItem(String itemName) {

    try {
      if (registryLock.writeLock().tryLock(REGISTRYLOCK_TIMEOUT, REGISTRYLOCK_TIMEOUT_UNITS)) {
        try {

          /* Remove the item from registry */
          GPIOPin gpioPin = (GPIOPin) registry.remove(itemName);

          /* Release the backend object */
          if (gpioPin != null) {
            try {
              gpio.releasePin(gpioPin);
            } catch (Exception e) {
              logger.error("Error occured while deleting backend object for item " +itemName + ", exception: "+ e.getMessage());
            }
          }

          /* Change the item state to 'Undefined' */
          eventPublisher.postUpdate(itemName, UnDefType.UNDEF);
        } finally {
          registryLock.writeLock().unlock();
        }
      } else {
        logger.error("Item " + itemName + " hasn't been deleted, timeout expired while waiting for registry lock");
      }
    } catch (InterruptedException e) {
      logger.error("Item " + itemName + " hasn't been deleted, thread was interrupted while waiting for registry lock");
    }
  }
}
TOP

Related Classes of org.openhab.binding.gpio.internal.GPIOBinding

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.