/*
* Copyright 2007 ETH Zurich
*
* Licensed under the Apache License, Version 2.0 (the "License");
*
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions
* and limitations under the License.
*/
package kr.ac.kaist.resl.ltk.net;
import java.math.BigInteger;
import org.apache.log4j.Logger;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import kr.ac.kaist.resl.ltk.generated.LLRPMessageFactory;
import org.llrp.ltk.types.LLRPMessage;
/**
* LLRPBinaryDecoder decodes incoming binary LLRP messages to LLRPMessage objects.
*/
public class LLRPBinaryDecoder extends CumulativeProtocolDecoder {
private static final String MESSAGE_VERSION_KEY = "MessageVersion";
private static final String MESSAGE_LENGTH_ARRAY = "LengthArray";
private static final String MESSAGE_LENGTH_KEY = "MessageLength";
private Logger log = Logger.getLogger(LLRPBinaryDecoder.class);
enum BYTESTATE {
STAT_VERSION0,
STAT_VERSION1,
STAT_LENGTH0,
STAT_LENGTH1,
STAT_LENGTH2,
STAT_LENGTH3,
STAT_DATA,
STAT_NEXT_VERSION0,
STAT_NEXT_VERSION1,
STAT_NEXT_LENGTH0,
STAT_NEXT_LENGTH1,
STAT_NEXT_LENGTH2,
STAT_NEXT_LENGTH3,
STAT_NEXT_DATA,
};
byte[] versionArray = new byte[2];
int version = 0;
byte[] lengthArray = new byte[4];
int length = -1;
byte[] nextVersionArray = new byte[2];
int nextVersion = 0;
byte[] nextLengthArray = new byte[4];
int nextLength = -1;
protected boolean doDecode_old(IoSession session, IoBuffer in,
ProtocolDecoderOutput out) throws Exception {
// if 6 bytes in the buffer we can determine the next length to see
// if buffer contains a completely delivered message.
// in.getInt(2) will throw a BufferUnderflowException if there are not
// even enough bytes to determine length
System.out.println("doDecode is called");
in.mark();
BYTESTATE _state = BYTESTATE.STAT_VERSION0;
while(in.remaining() > 0) {
try {
byte data = in.get();
switch(_state) {
case STAT_VERSION0:
versionArray[0] = data;
_state = BYTESTATE.STAT_VERSION1;
break;
case STAT_VERSION1:
versionArray[1] = data;
version = new BigInteger(versionArray).intValue();
//TODO version check
_state = BYTESTATE.STAT_LENGTH0;
break;
case STAT_LENGTH0:
lengthArray[0] = data;
_state = BYTESTATE.STAT_LENGTH1;
break;
case STAT_LENGTH1:
lengthArray[1] = data;
_state = BYTESTATE.STAT_LENGTH2;
break;
case STAT_LENGTH2:
lengthArray[2] = data;
_state = BYTESTATE.STAT_LENGTH3;
break;
case STAT_LENGTH3:
lengthArray[3] = data;
length = new BigInteger(lengthArray).intValue();
// TODO length validity check
_state = BYTESTATE.STAT_DATA;
break;
case STAT_DATA:
// message generation and write
byte[] msg = new byte[length];
msg[0] = versionArray[0];
msg[1] = versionArray[1];
msg[2] = lengthArray[0];
msg[3] = lengthArray[1];
msg[4] = lengthArray[2];
msg[5] = lengthArray[3];
for (int i = 6; i < length; i++) {
msg[i] = (byte) in.get();
}
log.debug("message completely received");
log.debug("start decoding message");
LLRPMessage message = LLRPMessageFactory.createLLRPMessage(msg);
log.debug("message decoded: " + message.getClass());
out.write(message);
// there might be an other message to be decoded
// see if there's another completly delivered message in the buffer
// in this case, we would have to return true
in.mark();
_state = BYTESTATE.STAT_NEXT_VERSION0;
break;
case STAT_NEXT_VERSION0:
nextVersionArray[0] = data;
_state = BYTESTATE.STAT_NEXT_VERSION1;
break;
case STAT_NEXT_VERSION1:
nextVersionArray[1] = data;
nextVersion = new BigInteger(nextVersionArray).intValue();
//TODO version check
_state = BYTESTATE.STAT_NEXT_LENGTH0;
break;
case STAT_NEXT_LENGTH0:
nextLengthArray[0] = data;
_state = BYTESTATE.STAT_NEXT_LENGTH1;
break;
case STAT_NEXT_LENGTH1:
nextLengthArray[1] = data;
_state = BYTESTATE.STAT_NEXT_LENGTH2;
break;
case STAT_NEXT_LENGTH2:
nextLengthArray[2] = data;
_state = BYTESTATE.STAT_NEXT_LENGTH3;
break;
case STAT_NEXT_LENGTH3:
nextLengthArray[3] = data;
nextLength = new BigInteger(nextLengthArray).intValue();
// TODO length validity check
_state = BYTESTATE.STAT_NEXT_DATA;
break;
case STAT_NEXT_DATA:
try {
for (int i = 6; i < nextLength; i++) {
in.get();
}
System.out.println("next message completely received");
in.position(in.markValue());
return true;
} catch(Exception e) {
// not enough bytes to determine length
System.out.println("not enough bytes to determine next message length");
in.position(in.markValue());
return false;
}
default:
in.position(in.markValue());
throw new Exception("Error during LLRP decode");
}
} catch(Exception e) {
System.out.println("not enough bytes to determine message length");
in.position(in.markValue());
return false;
}
}
in.position(in.markValue());
return false;
}
@Override
protected boolean doDecode(IoSession session, IoBuffer in,
ProtocolDecoderOutput out) throws Exception {
// if 6 bytes in the buffer we can determine the next length to see
// if buffer contains a completely delivered message.
// in.getInt(2) will throw a BufferUnderflowException if there are not
// even enough bytes to determine length
int length = -1;
byte[] lengthArray = null;
byte[] version = null;
if (in.remaining() >= 6
&& session.getAttribute(MESSAGE_LENGTH_KEY) == null) {
// enough bytes to decode length
log.debug("determine length of message");
version = new byte[2];
version[0] = in.get();
version[1] = in.get();
lengthArray = new byte[4];
lengthArray[0] = in.get();
lengthArray[1] = in.get();
lengthArray[2] = in.get();
lengthArray[3] = in.get();
length = new BigInteger(lengthArray).intValue();
session.setAttribute(MESSAGE_LENGTH_ARRAY, lengthArray);
session.setAttribute(MESSAGE_LENGTH_KEY, new Integer(length));
session.setAttribute(MESSAGE_VERSION_KEY, version);
// if the entire message is already available, call doDecode again.
return (in.remaining()>=length-6);
} else if (session.getAttribute(MESSAGE_LENGTH_KEY) != null) {
log.debug("length already determined, see if enough bytes are available");
length = ((Integer)session.getAttribute(MESSAGE_LENGTH_KEY)).intValue();
version = (byte[]) session.getAttribute(MESSAGE_VERSION_KEY);
lengthArray = (byte[]) session.getAttribute(MESSAGE_LENGTH_ARRAY);
if (in.remaining() >= length-6) {
// all bytes received to decode message
byte[] msg = new byte[length];
msg[0] = version[0];
msg[1] = version[1];
msg[2] = lengthArray[0];
msg[3] = lengthArray[1];
msg[4] = lengthArray[2];
msg[5] = lengthArray[3];
for (int i = 6; i < length; i++) {
msg[i] = (byte) in.get();
}
log.debug("message completely received");
log.debug("start decoding message");
LLRPMessage message = LLRPMessageFactory.createLLRPMessage(msg);
log.debug("message decoded: " + message.getClass());
out.write(message);
session.removeAttribute(MESSAGE_LENGTH_ARRAY);
session.removeAttribute(MESSAGE_LENGTH_KEY);
session.removeAttribute(MESSAGE_VERSION_KEY);
// there might be an other message to be decoded
// see if there's another completly delivered message in the
// buffer
// in this case, we would have to return true
try {
if (in.remaining() >= 6) {
version = new byte[2];
version[0] = in.get();
version[1] = in.get();
lengthArray = new byte[4];
lengthArray[0] = in.get();
lengthArray[1] = in.get();
lengthArray[2] = in.get();
lengthArray[3] = in.get();
length = new BigInteger(lengthArray).intValue();
session.setAttribute(MESSAGE_LENGTH_ARRAY, lengthArray);
session.setAttribute(MESSAGE_LENGTH_KEY, new Integer(
length));
session.setAttribute(MESSAGE_VERSION_KEY, version);
if (in.remaining() - in.markValue() >= length-6) {
log.debug("another message already in the buffer");
return true;
} else {
log.debug("message not yet completly delivered");
return false;
}
}
} catch (Exception e) {
// not enough bytes to determine length
log.debug("not enough bytes to determine message length");
return false;
}
} else {
// not enough bytes to determin length
log.debug("not enough bytes to determine message length");
return false;
}
} else {
log.debug("not enough bytes to determine length");
return false;
}
return false;
}
}