Package org.openhab.binding.nikobus.internal

Source Code of org.openhab.binding.nikobus.internal.NikobusBinding

/**
* 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.nikobus.internal;

import java.util.Dictionary;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang.StringUtils;
import org.openhab.binding.nikobus.NikobusBindingProvider;
import org.openhab.binding.nikobus.internal.config.AbstractNikobusItemConfig;
import org.openhab.binding.nikobus.internal.core.NikobusAckMonitor;
import org.openhab.binding.nikobus.internal.core.NikobusCommand;
import org.openhab.binding.nikobus.internal.core.NikobusCommandListener;
import org.openhab.binding.nikobus.internal.core.NikobusCommandReceiver;
import org.openhab.binding.nikobus.internal.core.NikobusCommandSender;
import org.openhab.binding.nikobus.internal.core.NikobusInterface;
import org.openhab.binding.nikobus.internal.core.NikobusModule;
import org.openhab.core.binding.AbstractBinding;
import org.openhab.core.binding.BindingProvider;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Nikobus event binding class. Receives all command events and forwards them to
* the appropriate configured item.
*
* @author Davy Vanherbergen
* @since 1.3.0
*/
public class NikobusBinding extends AbstractBinding<NikobusBindingProvider> implements ManagedService {

  private static final Logger log = LoggerFactory.getLogger(NikobusBinding.class);

  private NikobusInterface serialInterface = new NikobusInterface();

  private NikobusCommandReceiver commandReceiver;

  private NikobusCommandSender commandSender;

  private long refreshInterval = 600;

  private int nextModuleToRefresh = 0;

  private ScheduledExecutorService refreshService;

  private ExecutorService statusRequestService = Executors.newSingleThreadExecutor();


  public NikobusBinding() {
    // setup a command receiver in it's own thread
    commandReceiver = new NikobusCommandReceiver(this);
    commandReceiver.setBufferQueue(serialInterface.getBufferQueue());
    Thread receiverThread = new Thread(commandReceiver);
    receiverThread.setName("Nikobus Receiver");
    receiverThread.setDaemon(true);
    receiverThread.start();
    log.debug("Started Command Receiver Thread.");

    commandSender = new NikobusCommandSender(serialInterface);
    Thread senderThread = new Thread(commandSender);
    senderThread.setName("Nikobus Sender");
    senderThread.setDaemon(true);
    senderThread.start();
    log.debug("Started Command Sender Thread.");
  }
 
 
  /**
   * {@inheritDoc}
   *
   * Redistribute an openHAB command to the nikobus binding for that item.
   */
  @Override
  public void internalReceiveCommand(String itemName, Command command) {

    log.trace("Received command {} for item {}", command.toString(), itemName);

    // get the item's corresponding Nikobus binding configuration
    AbstractNikobusItemConfig itemBinding = null;
    for (NikobusBindingProvider provider : providers) {
      if (provider.providesBindingFor(itemName)) {
        itemBinding = provider.getItemConfig(itemName);
        break;
      }
    }

    if (providers.size() == 0) {
      log.error("No providers");
    }
    if (itemBinding == null) {
      log.trace("No item found");
      return;
    }

    try {
      // process the command
      itemBinding.processCommand(command, this);
    } catch (Exception e) {
      log.error(
        "Error processing commmand {} for item {} : {}",
        new Object[] { command.toString(), itemBinding.getName(), e.getMessage() });
    }

  }

  /**
   * Send a status update to the openHab bus.
   *
   * @param itemName
   *            item for which to send update
   * @param state
   *            status
   */
  public void postUpdate(String itemName, State state) {
    if (eventPublisher == null) {
      log.error("Could not send status update.  Event Publisher is missing");
      return;
    }

    log.trace("Sending status update to {} : {}", itemName, state);
    eventPublisher.postUpdate(itemName, state);
    return;

  }

  /**
   * Send a command via the serial port to the Nikobus.
   *
   * @param nikobusCommand
   *            command to send
   * @throws Exception
   *             TimeoutException if waitForAck is true and no ack was
   *             received within the timeout limit.
   */
  public void sendCommand(NikobusCommand cmd) throws Exception {

    if (cmd.getAck() != null) {
      // send & wait for ACK
      log.trace("Sending command with ack {}", cmd.getCommand());

      NikobusAckMonitor monitor = new NikobusAckMonitor(cmd);
      commandReceiver.register(monitor);
      try {
        monitor.waitForAck(commandSender);
      } finally {
        commandReceiver.unregister(monitor);
      }

    } else {
      // send only
      log.trace("Sending command without waiting for ack {}",
          cmd.getCommand());
      commandSender.sendCommand(cmd);
    }
  }

  /**
   * Start a connection to nikobus.
   */
  public void connect() {

    try {
      log.trace("Starting connection");
      serialInterface.connect();
    } catch (Exception e) {
      log.error(e.getMessage());
    }
  }


  /**
   * @return nikobus connection status.
   */
  public String getConnectionStatus() {

    if (serialInterface != null && serialInterface.isConnected()) {
      return "Connected to " + serialInterface.getPort();
    }
    return "Not Connected.";
  }


  /**
   * @return NikobusBindingProvider
   */
  private NikobusBindingProvider getBindingProvider() {   
    for (NikobusBindingProvider p : providers) {
      return p;
    }   
    return null;
  }
 
 
  /**
   * Request a status update for the module with the given address. This will
   * trigger a read command being sent to the nikobus if none is already
   * pending. The read command will be send using a separate thread.
   *
   * @param moduleAddress
   *            address of the module for which to request the status update.
   * @param delayedSend
   *            when true, sending will wait for empty bus
   */
  public void scheduleStatusUpdateRequest(String moduleAddress, boolean delayedSend) {

    NikobusModule module = getBindingProvider().getModule(moduleAddress);
    final NikobusCommand cmd = module.getStatusRequestCommand();
    cmd.setWaitForSilence(delayedSend);
    cmd.setMaxRetryCount(10);

    if (commandSender.isCommandRedundant(cmd)) {
      // no need to send, there is a similar command already pending
      return;
    }
   
    Runnable updater = new Runnable() {
      @Override
      public void run() {
        try {
          sendCommand(cmd);
        } catch (Exception e) {
          log.error("Error occurred during status request.", e);
        }
      }
    };

    statusRequestService.submit(updater);

  }

  /**
   * Activate the binding provider. After initial connection;
   */
  public void activate() {

    log.trace("Activating binding");
    startRefreshScheduler();
   
    log.trace("Registering modules");
    for (NikobusModule module : getBindingProvider().getAllModules()) {
      register(module);
   
  }

 
  /**
   * Start an automatic refresh scheduler.
   */
  private void startRefreshScheduler() {

    // stop any running instance..
    if (refreshService != null) {
      refreshService.shutdownNow();
    }

    if (refreshInterval > 0) {
      refreshService = Executors.newScheduledThreadPool(1);

      Runnable refreshCommand = new Runnable() {

        @Override
        public void run() {

          List<NikobusModule> allModules = getBindingProvider().getAllModules();
         
          if (allModules == null || allModules.isEmpty()) {
            log.trace("No modules available to refresh");
            return;
          }

          if (nextModuleToRefresh >= allModules.size()) {
            nextModuleToRefresh = 0;
          }

          NikobusModule m = allModules.get(nextModuleToRefresh);

          log.trace("Requesting scheduled status update for {}", m.getAddress());
          NikobusCommand cmd = m.getStatusRequestCommand();
          cmd.setWaitForSilence(true);
          try {
            sendCommand(cmd);
          } catch (Exception e) {
            log.error("Error occurred during scheduled status refresh.", e);
          }
          nextModuleToRefresh++;
        }

      };

      // don't start the scheduler too soon, otherwise the connection may
      // not be available yet on slower systems like a pi
      refreshService.scheduleAtFixedRate(refreshCommand, 600, refreshInterval, TimeUnit.SECONDS);
      log.debug(
        "Refresh service started. Will refresh a module every {} seconds.",
        refreshInterval);
    }

  }

  /**
   * Cleanup when binding is unloaded by DS.
   */
  public void deactivate() {

    log.trace("Deactivating binding");

    refreshService.shutdownNow();
    commandReceiver.stop();
    commandSender.stop();

    serialInterface.disconnect();

  }

  /**
   * Register a new item binding.
   *
   * @param itemBinding
   */
  public void register(NikobusCommandListener itemBinding) {
    commandReceiver.register(itemBinding);
  }

  /**
   * Unregister a new item binding.
   *
   * @param itemBinding
   */

  public void unregister(NikobusCommandListener itemBinding) {
    commandReceiver.unregister(itemBinding);
  }

  /**
   * Update the configuration.
   */
  public void updated(Dictionary<String, ?> config)
      throws ConfigurationException {
    if (config != null) {

      String configuredSerialPort = (String) config.get("serial.port");
      if (StringUtils.isNotBlank(configuredSerialPort)) {
        log.trace("Setting serial port to {}", configuredSerialPort);
        serialInterface.setPort(configuredSerialPort);
      }

      String configuredInterval = (String) config.get("refresh");
      if (StringUtils.isNotBlank(configuredInterval)) {
        refreshInterval = Long.parseLong(configuredInterval);
        log.trace("Setting refresh interval to {}", refreshInterval);
        startRefreshScheduler();
      }
      connect();

    }
  }

  @Override
  public void allBindingsChanged(BindingProvider provider) {
   
    // clear all previous listeners..
    commandReceiver.unregisterAll();
   
    NikobusBindingProvider bindingProvider = (NikobusBindingProvider) provider;
    for (String itemName : provider.getItemNames()) {
      if (provider.providesBindingFor(itemName)) {
        register(bindingProvider.getItemConfig(itemName));
        log.trace("Registering command listener for item {} ", itemName);
      }
    }

  }

  @Override
  public void bindingChanged(BindingProvider provider, String itemName) {
    NikobusBindingProvider bindingProvider = (NikobusBindingProvider) provider;
    if (!provider.providesBindingFor(itemName)) {
      log.trace("Removing command listener for item {}", itemName);
      commandReceiver.unregisterItem(itemName);
    } else {
      log.trace("Registering command listener for item {} ", itemName);
      register(bindingProvider.getItemConfig(itemName));
    }
  }

}
TOP

Related Classes of org.openhab.binding.nikobus.internal.NikobusBinding

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.