/**
* 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.maxcul.internal.message.sequencers;
import java.util.Calendar;
import java.util.GregorianCalendar;
import org.openhab.binding.maxcul.internal.MaxCulMsgHandler;
import org.openhab.binding.maxcul.internal.messages.AckMsg;
import org.openhab.binding.maxcul.internal.messages.BaseMsg;
import org.openhab.binding.maxcul.internal.messages.MaxCulMsgType;
import org.openhab.binding.maxcul.internal.messages.TimeInfoMsg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handle Time Update requests. Very simple sequence that simply responds to a
* time request and then handles the response
*
* @author Paul Hampson (cyclingengineer)
* @since 1.6.0
*/
public class TimeUpdateRequestSequence implements MessageSequencer {
private enum TimeUpdateRequestState {
RESPOND_TO_REQUEST,
HANDLE_RESPONSE,
FINISHED
}
private static final Logger logger = LoggerFactory.getLogger(TimeUpdateRequestSequence.class);
private static final long TIME_TOLERANCE = 30000; // 30s margin
TimeUpdateRequestState state = TimeUpdateRequestState.RESPOND_TO_REQUEST;
private MaxCulMsgHandler messageHandler;
private int pktLostCount = 0;
private String tzStr;
public TimeUpdateRequestSequence(String tz, MaxCulMsgHandler messageHandler) {
this.tzStr = tz;
this.messageHandler = messageHandler;
}
/**
* Compare two times and check they are within a certain tolerance
*
* @param a
* Time A
* @param b
* Time B
* @param t
* Tolerance in milliseconds
* @return true if within tolerance
*/
private boolean isValidDeviation(Calendar a, Calendar b, long t) {
return (Math.abs(a.getTimeInMillis() - b.getTimeInMillis()) <= t);
}
@Override
public void runSequencer(BaseMsg msg) {
pktLostCount = 0;
switch (state) {
case RESPOND_TO_REQUEST:
if (BaseMsg.getMsgType(msg.rawMsg) == MaxCulMsgType.TIME_INFO) {
TimeInfoMsg timeMsg = new TimeInfoMsg(msg.rawMsg);
if (isValidDeviation(timeMsg.getTimeInfo(), new GregorianCalendar(), TIME_TOLERANCE)) {
messageHandler.sendAck(msg);
state = TimeUpdateRequestState.FINISHED;
} else {
messageHandler.sendTimeInfo(msg.srcAddrStr, tzStr, this);
state = TimeUpdateRequestState.HANDLE_RESPONSE;
}
} else {
state = TimeUpdateRequestState.FINISHED;
logger.debug("Got invalid message type for Time Update Sequence");
}
break;
case HANDLE_RESPONSE:
/* check for ACK */
if (msg.msgType == MaxCulMsgType.ACK) {
AckMsg ack = new AckMsg(msg.rawMsg);
if (ack.getIsNack()) {
logger.error("TIME_INFO was nacked. Ending sequence");
state = TimeUpdateRequestState.FINISHED;
} else {
state = TimeUpdateRequestState.FINISHED;
}
}
state = TimeUpdateRequestState.FINISHED;
break;
case FINISHED:
/* do nothing */
break;
default:
logger.error("Invalid state for PairingInitialisation Message Sequence!");
break;
}
}
@Override
public void packetLost(BaseMsg msg) {
pktLostCount++;
logger.debug("Lost " + pktLostCount + " packets");
if (pktLostCount < 3) {
logger.debug("Attempt retransmission");
messageHandler.sendMessage(msg);
} else {
logger.error("Lost " + pktLostCount + " packets. Ending Sequence in state " + this.state);
state = TimeUpdateRequestState.FINISHED;
}
}
@Override
public boolean isComplete() {
return (state == TimeUpdateRequestState.FINISHED);
}
@Override
public boolean useFastSend() {
return true;
}
}