Package org.openhab.binding.insteonplm.internal.device

Source Code of org.openhab.binding.insteonplm.internal.device.InsteonDevice

/**
* Copyright (c) 2010-2013, 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.insteonplm.internal.device;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map.Entry;

import org.openhab.binding.insteonplm.InsteonPLMBindingConfig;
import org.openhab.binding.insteonplm.internal.driver.Driver;
import org.openhab.binding.insteonplm.internal.message.FieldException;
import org.openhab.binding.insteonplm.internal.message.Msg;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/*
* The InsteonDevice class holds known per-device state of a single Insteon device,
* including the address, what port(modem) to reach it on etc.
* Note that some Insteon devices de facto consist of two devices (let's say
* a relay and a sensor), but operate under the same address. Such devices will
* be represented just by a single InsteonDevice. Their different personalities
* will then be represented by DeviceFeatures.
*
* @author Bernd Pfrommer
* @since 1.5.0
*/
public class InsteonDevice {
  private static final Logger logger = LoggerFactory.getLogger(InsteonDevice.class);

  public static enum DeviceStatus {
    INITIALIZED,
    POLLING
  }

  private InsteonAddress        m_address    = new InsteonAddress();
  private ArrayList<String>      m_ports      = new ArrayList<String>();
  private long            m_pollInterval  = -1L; // in milliseconds
  private Driver            m_driver    = null;
  private HashMap<String, DeviceFeature>   m_features  = new HashMap<String, DeviceFeature>();
  private String            m_productKey  = null;
  private Long            m_lastTimePolled = 0L;
  private Long            m_lastMsgReceived = 0L;
  private boolean            m_isModem    = false;
  private   Deque<QEntry>        m_requestQueue  = new LinkedList<QEntry>();
  private  boolean            m_hasModemDBEntry = false;
  private DeviceStatus        m_status    = DeviceStatus.INITIALIZED;
 
  /**
   * Constructor
   */
  public InsteonDevice() {
    m_lastMsgReceived = System.currentTimeMillis();
  }

  // --------------------- simple getters -----------------------------

  public boolean      hasProductKey()    { return m_productKey != null; }
  public String      getProductKey()    { return m_productKey; }
  public boolean       hasModemDBEntry()  { return m_hasModemDBEntry; }
  public DeviceStatus   getStatus()      { return m_status; }
  public InsteonAddress  getAddress()     { return (m_address)}
  public Driver      getDriver()      { return m_driver; }
  public boolean       hasValidPorts()    { return (!m_ports.isEmpty())}
  public long        getPollInterval()  { return m_pollInterval; }
  public boolean      isModem()       { return m_isModem; }
  public DeviceFeature  getFeature(String f) {   return m_features.get(f)}
  public HashMap<String, DeviceFeature> getFeatures() { return m_features; }
  public boolean      hasProductKey(String key) {
    return m_productKey != null && m_productKey.equals(key);
  }
  public boolean      hasValidPollingInterval() {
    return (m_pollInterval > 0);
  }
  public long       getPollOverDueTime() {
    return (m_lastTimePolled - m_lastMsgReceived);
  }
 
  public String       getPort() throws IOException {
    if (m_ports.isEmpty()) throw new IOException("no ports configured for instrument " + getAddress());
    return (m_ports.iterator().next());
  }
  public boolean       hasAnyListeners() {
    synchronized (m_features) {
      for (DeviceFeature f: m_features.values()) {
        if (f.hasListeners()) return true;
      }
    }
    return false;
  }
  // --------------------- simple setters ----------------------------- 

  public void setStatus(DeviceStatus aI)    { m_status = aI; }
  public void setHasModemDBEntry(boolean b)  { m_hasModemDBEntry = b; }
  public void setAddress(InsteonAddress ia)  { m_address = ia; }
  public void setDriver(Driver d)       { m_driver = d; }
  public void setIsModem(boolean f)        { m_isModem = f; }
  public void setProductKey(String pk)    { m_productKey = pk; }
  public void setPollInterval(long pi) {
    logger.trace("setting poll interval for {} to {} ", m_address, pi);
    if (pi > 0) m_pollInterval = pi;
  }

  /**
   * Add a port. Currently only a single port is being used.
   * @param p the port to add
   */
  public void addPort(String p) {
    if (p == null) return;
    if (!m_ports.contains(p)) {
      m_ports.add(p);
    }
  }

  /**
   * Removes feature listener from this device
   * @param aItemName name of the feature listener to remove
   * @return true if a feature listener was successfully removed
   */
  public boolean removeFeatureListener(String aItemName) {
    boolean removedListener = false;
    synchronized (m_features) {
      for (Iterator<Entry<String, DeviceFeature>> it = m_features.entrySet().iterator(); it.hasNext();) {       
        DeviceFeature f = it.next().getValue();
        if (f.removeListener(aItemName)) {
          removedListener = true;
        }
      }
    }
    return removedListener;
  }
  /**
   * Invoked to process an openHAB command
   * @param driver The driver to use
   * @param c The item configuration
   * @param command The actual command to execute
   */
  public void processCommand(Driver driver, InsteonPLMBindingConfig c, Command command) {
    logger.debug("processing command {} features: {}", command, m_features.size());
    synchronized(m_features) {
      for (DeviceFeature i : m_features.values()) {
        i.handleCommand(c, command);
      }
    }
  }

  /**
   * Execute poll on this device: create an array of messages,
   * add them to the request queue, and schedule the queue
   * for processing.
   */
  public void doPoll() {
    ArrayList<QEntry> l = new ArrayList<QEntry>();
    synchronized(m_features) {
      for (DeviceFeature i : m_features.values()) {
        if (i.hasListeners()) {
          Msg m = i.makePollMsg();
          if (m != null) l.add(new QEntry(i, m));
        }
      }
    }
    if (l.isEmpty()) return;
    synchronized (m_requestQueue) {
      for (QEntry e : l) {
        m_requestQueue.add(e);
      }
    }
    long now = System.currentTimeMillis();
    RequestQueueManager.s_instance().addQueue(this, now);
   
    if (!l.isEmpty()) {
      synchronized(m_lastTimePolled) {
        m_lastTimePolled = now;
      }
    }
  }

  /**
   * Handle incoming message for this device by forwarding
   * it to all features that this device supports  
   * @param fromPort port from which the message come in
   * @param msg the incoming message
   */
  public void handleMessage(String fromPort, Msg msg) {
    synchronized (m_lastMsgReceived) {
      m_lastMsgReceived = System.currentTimeMillis();
    }
    synchronized(m_features) {
      // first update all features that are
      // not status features
      for (DeviceFeature f : m_features.values()) {
        if (!f.isStatusFeature()) {
          if (f.handleMessage(msg, fromPort)) {
            break;
          }
        }
      }
      // then update all the status features,
      // e.g. when the device was last updated
      for (DeviceFeature f : m_features.values()) {
        if (f.isStatusFeature()) {
          f.handleMessage(msg, fromPort);
        }
      }
    }
  }

  /**
   * Helper method to make standard message
   * @param flags
   * @param cmd1
   * @param cmd2
   * @return standard message
   * @throws FieldException
   * @throws IOException
   */
  public Msg makeStandardMessage(byte flags, byte cmd1, byte cmd2)
      throws FieldException, IOException {
    Msg m = Msg.s_makeMessage("SendStandardMessage");
    m.setAddress("toAddress", getAddress());
    m.setByte("messageFlags", flags);
    m.setByte("command1", cmd1);
    m.setByte("command2", cmd2);
    return m;
  }

  /**
   * Helper method to make extended message
   * @param flags
   * @param cmd1
   * @param cmd2
   * @return extended message
   * @throws FieldException
   * @throws IOException
   */
  public Msg makeExtendedMessage(byte flags, byte cmd1, byte cmd2)
      throws FieldException, IOException {
    Msg m = Msg.s_makeMessage("SendExtendedMessage");
    m.setAddress("toAddress", getAddress());
    m.setByte("messageFlags", (byte) (((flags & 0xff) | 0x10) & 0xff));
    m.setByte("command1", cmd1);
    m.setByte("command2", cmd2);
    int checksum = (~(cmd1 + cmd2) + 1) &0xff;
    m.setByte("userData14", (byte)checksum);
    return m;
  }

  /**
   * Called by the RequestQueueManager when the queue has expired
   * @param timeNow
   * @return time when to schedule the next message (timeNow + quietTime)
   */
  public long processRequestQueue(long timeNow) {
    synchronized (m_requestQueue) {
      if (m_requestQueue.isEmpty()) {
        return 0L;
      }
      QEntry qe = m_requestQueue.poll();
      qe.getFeature().setQueryStatus(DeviceFeature.QueryStatus.QUERY_PENDING);
      long quietTime = qe.getMsg().getQuietTime();
      qe.getMsg().setQuietTime(500L); // rate limiting downstream:
      try {
        writeMessage(qe.getMsg());
      } catch (IOException e) {
        logger.error("message write failed for msg {}", qe.getMsg(), e);
      }
      return (timeNow + quietTime);
    }
  }
  /**
   * Enqueues message to be sent at the next possible time
   * @param m message to be sent
   * @param f device feature that sent this message (so we can associate the response message with it)
   */
  public void enqueueMessage(Msg m, DeviceFeature f) {
    synchronized (m_requestQueue) {
      m_requestQueue.add(new QEntry(f, m));
    }
    long now = System.currentTimeMillis();
    RequestQueueManager.s_instance().addQueue(this, now);
  }
 
  private void writeMessage(Msg m) throws IOException {
    m_driver.writeMessage(getPort(), m);
  }

  private void instantiateFeatures(DeviceType dt) {
    for (Entry<String,String> fe : dt.getFeatures().entrySet()) {
      DeviceFeature f = DeviceFeature.s_makeDeviceFeature(fe.getValue());
      if (f == null) {
        logger.error("device type {} references unknown feature: {}", dt, fe.getValue());
      } else {
        addFeature(fe.getKey(), f);
      }
    }
  }
 
  private void addFeature(String name, DeviceFeature f) {
    f.setDevice(this);
    synchronized (m_features) {
      m_features.put(name, f);
    }
  }


  @Override
  public String toString() {
    String s = m_address.toString();
    for (Entry<String, DeviceFeature> f : m_features.entrySet()) {
      s += "|" + f.getKey() + "->" + f.getValue().toString();
    }
    return s;
  }
  /**
   * Factory method
   * @param dt device type after which to model the device
   * @return newly created device
   */
  public static InsteonDevice s_makeDevice(DeviceType dt) {
    InsteonDevice dev = new InsteonDevice();
    dev.instantiateFeatures(dt);
    return dev;
  }
 
  /**
   * Queue entry helper class
   * @author Bernd Pfrommer
   */
  public static class QEntry {
    private DeviceFeature  m_feature  = null;
    private Msg       m_msg    = null;
    public DeviceFeature getFeature() { return m_feature; }
    public Msg getMsg() { return m_msg; }
    QEntry(DeviceFeature f, Msg m) {
      m_feature  = f;
      m_msg    = m;
    }
  }
}
TOP

Related Classes of org.openhab.binding.insteonplm.internal.device.InsteonDevice

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.