/*
* Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
*
* 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 org.traccar.protocol;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
import org.traccar.helper.BcdUtil;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.Position;
import java.net.SocketAddress;
import java.nio.ByteOrder;
public class T800xProtocolDecoder extends BaseProtocolDecoder {
public T800xProtocolDecoder(T800xProtocol protocol) {
super(protocol);
}
public static final int MSG_LOGIN = 0x01;
public static final int MSG_GPS = 0x02;
public static final int MSG_HEARTBEAT = 0x03;
public static final int MSG_ALARM = 0x04;
public static final int MSG_COMMAND = 0x81;
private static float readSwappedFloat(ChannelBuffer buf) {
byte[] bytes = new byte[4];
buf.readBytes(bytes);
return ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, bytes).readFloat();
}
private void sendResponse(Channel channel, int type, ChannelBuffer imei) {
if (channel != null) {
ChannelBuffer response = ChannelBuffers.directBuffer(15);
response.writeByte(0x23);
response.writeByte(0x23); // header
response.writeByte(type);
response.writeShort(response.capacity()); // length
response.writeShort(0x0001); // index
response.writeBytes(imei);
channel.write(response);
}
}
private String decodeAlarm(short value) {
switch (value) {
case 3:
return Position.ALARM_SOS;
case 4:
return Position.ALARM_OVERSPEED;
case 5:
return Position.ALARM_GEOFENCE_ENTER;
case 6:
return Position.ALARM_GEOFENCE_EXIT;
case 8:
case 10:
return Position.ALARM_VIBRATION;
default:
break;
}
return null;
}
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
ChannelBuffer buf = (ChannelBuffer) msg;
buf.skipBytes(2);
int type = buf.readUnsignedByte();
buf.readUnsignedShort(); // length
int index = buf.readUnsignedShort();
ChannelBuffer imei = buf.readBytes(8);
DeviceSession deviceSession = getDeviceSession(
channel, remoteAddress, ChannelBuffers.hexDump(imei).substring(1));
if (deviceSession == null) {
return null;
}
if (type == MSG_LOGIN || type == MSG_ALARM || type == MSG_HEARTBEAT) {
sendResponse(channel, type, imei);
}
if (type == MSG_GPS || type == MSG_ALARM) {
Position position = new Position();
position.setProtocol(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_INDEX, index);
buf.readUnsignedShort(); // acc on interval
buf.readUnsignedShort(); // acc off interval
buf.readUnsignedByte(); // angle compensation
buf.readUnsignedShort(); // distance compensation
buf.readUnsignedShort(); // speed alarm
int locationStatus = buf.readUnsignedByte();
buf.readUnsignedByte(); // gsensor manager status
buf.readUnsignedByte(); // other flags
buf.readUnsignedByte(); // heartbeat
buf.readUnsignedByte(); // relay status
buf.readUnsignedShort(); // drag alarm setting
int io = buf.readUnsignedShort();
position.set(Position.KEY_IGNITION, BitUtil.check(io, 14));
position.set("ac", BitUtil.check(io, 13));
position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
buf.readUnsignedByte(); // reserved
position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
int battery = BcdUtil.readInteger(buf, 2);
if (battery == 0) {
battery = 100;
}
position.set(Position.KEY_BATTERY, battery);
DateBuilder dateBuilder = new DateBuilder()
.setYear(BcdUtil.readInteger(buf, 2))
.setMonth(BcdUtil.readInteger(buf, 2))
.setDay(BcdUtil.readInteger(buf, 2))
.setHour(BcdUtil.readInteger(buf, 2))
.setMinute(BcdUtil.readInteger(buf, 2))
.setSecond(BcdUtil.readInteger(buf, 2));
if (BitUtil.check(locationStatus, 6)) {
position.setValid(!BitUtil.check(locationStatus, 7));
position.setTime(dateBuilder.getDate());
position.setAltitude(readSwappedFloat(buf));
position.setLongitude(readSwappedFloat(buf));
position.setLatitude(readSwappedFloat(buf));
position.setSpeed(UnitsConverter.knotsFromKph(
BcdUtil.readInteger(buf, 4) * 0.1));
position.setCourse(buf.readUnsignedShort());
} else {
getLastLocation(position, dateBuilder.getDate());
byte[] array = new byte[16];
buf.readBytes(array);
ChannelBuffer swapped = ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, array);
position.setNetwork(new Network(CellTower.from(
swapped.readUnsignedShort(), swapped.readUnsignedShort(),
swapped.readUnsignedShort(), swapped.readUnsignedShort())));
// two more cell towers
}
return position;
}
return null;
}
}