/**
* Copyright (c) 2010-2016 by the respective copyright holders.
*
* 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.HashMap;
import java.util.Map.Entry;
import org.openhab.binding.insteonplm.internal.driver.ModemDBEntry;
import org.openhab.binding.insteonplm.internal.driver.Port;
import org.openhab.binding.insteonplm.internal.message.FieldException;
import org.openhab.binding.insteonplm.internal.message.Msg;
import org.openhab.binding.insteonplm.internal.message.MsgListener;
import org.openhab.binding.insteonplm.internal.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Builds the modem database from incoming link record messages
*
* @author Bernd Pfrommer
* @since 1.5.0
*/
public class ModemDBBuilder implements MsgListener, Runnable {
private static final Logger logger = LoggerFactory.getLogger(ModemDBBuilder.class);
private boolean m_isComplete = false;
private Port m_port = null;
private Thread m_writeThread = null;
private int m_timeoutMillis = 120000;
public ModemDBBuilder(Port port) {
m_port = port;
}
public void setRetryTimeout(int timeout) {
m_timeoutMillis = timeout;
}
public void start() {
m_port.addListener(this);
m_writeThread = new Thread(this);
m_writeThread.setName("DBBuilder");
m_writeThread.start();
logger.debug("querying port for first link record");
}
public void startDownload() {
logger.trace("starting modem database download");
m_port.clearModemDB();
getFirstLinkRecord();
}
public synchronized boolean isComplete() {
return (m_isComplete);
}
@Override
public void run() {
logger.trace("starting modem db builder thread");
while (!isComplete()) {
startDownload();
try {
Thread.sleep(m_timeoutMillis); // wait for download to complete
} catch (InterruptedException e) {
logger.warn("modem db builder thread interrupted");
break;
}
if (!isComplete()) {
logger.warn("modem database download unsuccessful, restarting!");
}
}
logger.trace("exiting modem db builder thread");
}
private void getFirstLinkRecord() {
try {
m_port.writeMessage(Msg.s_makeMessage("GetFirstALLLinkRecord"));
} catch (IOException e) {
logger.error("error sending link record query ", e);
}
}
/**
* processes link record messages from the modem to build database
* and request more link records if not finished.
* {@inheritDoc}
*/
@Override
public void msg(Msg msg, String fromPort) {
if (msg.isPureNack()) {
return;
}
try {
if (msg.getByte("Cmd") == 0x69 || msg.getByte("Cmd") == 0x6a) {
// If the flag is "ACK/NACK", a record response
// will follow, so we do nothing here.
// If its "NACK", there are none
if (msg.getByte("ACK/NACK") == 0x15) {
logger.debug("got all link records.");
done();
}
} else if (msg.getByte("Cmd") == 0x57) {
// we got the link record response
updateModemDB(msg.getAddress("LinkAddr"), m_port, msg);
m_port.writeMessage(Msg.s_makeMessage("GetNextALLLinkRecord"));
}
} catch (FieldException e) {
logger.debug("bad field handling link records {}", e);
} catch (IOException e) {
logger.debug("got IO exception handling link records {}", e);
} catch (IllegalStateException e) {
logger.debug("got exception requesting link records {}", e);
}
}
private synchronized void done() {
m_isComplete = true;
logModemDB();
m_port.removeListener(this);
m_port.modemDBComplete();
}
private void logModemDB() {
try {
logger.debug("MDB ------- start of modem link records ------------------");
HashMap<InsteonAddress, ModemDBEntry> dbes = m_port.getDriver().lockModemDBEntries();
for (Entry<InsteonAddress, ModemDBEntry> db : dbes.entrySet()) {
ArrayList<Msg> lrs = db.getValue().getLinkRecords();
for (Msg m : lrs) {
int recordFlags = m.getByte("RecordFlags") & 0xff;
String ms = ((recordFlags & (0x1 << 6)) != 0) ? "CTRL" : "RESP";
logger.debug("MDB {}: {} group: {} data1: {} data2: {} data3: {}", db.getKey(), ms,
toHex(m.getByte("ALLLinkGroup")), toHex(m.getByte("LinkData1")),
toHex(m.getByte("LinkData2")), toHex(m.getByte("LinkData2")));
}
logger.debug("MDB -----");
}
logger.debug("MDB ---------------- end of modem link records -----------");
} catch (FieldException e) {
logger.error("cannot access field:", e);
} finally {
m_port.getDriver().unlockModemDBEntries();
}
}
public static String toHex(byte b) {
return Utils.getHexString(b);
}
public void updateModemDB(InsteonAddress linkAddr, Port port, Msg m) {
HashMap<InsteonAddress, ModemDBEntry> dbes = port.getDriver().lockModemDBEntries();
ModemDBEntry dbe = dbes.get(linkAddr);
if (dbe == null) {
dbe = new ModemDBEntry(linkAddr);
dbes.put(linkAddr, dbe);
}
dbe.setPort(port);
if (m != null) {
dbe.addLinkRecord(m);
try {
byte group = m.getByte("ALLLinkGroup");
int recordFlags = m.getByte("RecordFlags") & 0xff;
if ((recordFlags & (0x1 << 6)) != 0) {
dbe.addControls(group);
} else {
dbe.addRespondsTo(group);
}
} catch (FieldException e) {
logger.error("cannot access field:", e);
}
}
port.getDriver().unlockModemDBEntries();
}
}