package com.o3dr.services.android.lib.drone.companion.solo.tlv;
import android.util.Log;
import com.o3dr.services.android.lib.drone.companion.solo.tlv.sitescan.inspect.SoloInspectStart;
import com.o3dr.services.android.lib.drone.companion.solo.tlv.sitescan.inspect.SoloInspectSetWaypoint;
import com.o3dr.services.android.lib.drone.companion.solo.tlv.sitescan.inspect.SoloInspectMoveGimbal;
import com.o3dr.services.android.lib.drone.companion.solo.tlv.sitescan.inspect.SoloInspectMoveVehicle;
import com.o3dr.services.android.lib.drone.companion.solo.tlv.mpcc.SoloSplineAttach;
import com.o3dr.services.android.lib.drone.companion.solo.tlv.mpcc.SoloSplineDurations;
import com.o3dr.services.android.lib.drone.companion.solo.tlv.mpcc.SoloSplinePathSettings;
import com.o3dr.services.android.lib.drone.companion.solo.tlv.mpcc.SoloSplinePlay;
import com.o3dr.services.android.lib.drone.companion.solo.tlv.mpcc.SoloSplinePlaybackStatus;
import com.o3dr.services.android.lib.drone.companion.solo.tlv.mpcc.SoloSplinePoint;
import com.o3dr.services.android.lib.drone.companion.solo.tlv.mpcc.SoloSplineRecord;
import com.o3dr.services.android.lib.drone.companion.solo.tlv.mpcc.SoloSplineSeek;
import com.o3dr.services.android.lib.drone.companion.solo.tlv.sitescan.scan.SoloScanStart;
import com.o3dr.services.android.lib.drone.companion.solo.tlv.sitescan.survey.SoloSurveyStart;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_ARTOO_INPUT_REPORT_MESSAGE;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_RTL_HOME_POINT;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_CABLE_CAM_OPTIONS;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_CABLE_CAM_WAYPOINT;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_FOLLOW_OPTIONS;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_FOLLOW_OPTIONS_V2;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_GET_BUTTON_SETTING;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_GOPRO_RECORD;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_GOPRO_REQUEST_STATE;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_GOPRO_SET_EXTENDED_REQUEST;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_GOPRO_SET_REQUEST;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_GOPRO_STATE;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_GOPRO_STATE_V2;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_INSPECT_MOVE_GIMBAL;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_INSPECT_MOVE_VEHICLE;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_INSPECT_SET_WAYPOINT;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_INSPECT_START;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_MESSAGE_GET_CURRENT_SHOT;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_MESSAGE_LOCATION;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_MESSAGE_RECORD_POSITION;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_MESSAGE_SET_CURRENT_SHOT;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_MESSAGE_SHOT_MANAGER_ERROR;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_PANO_OPTIONS;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_PANO_STATUS;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_PAUSE_BUTTON;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_REWIND_OPTIONS;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_SCAN_START;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_SET_BUTTON_SETTING;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_SHOT_ERROR;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_SHOT_OPTIONS;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_SPLINE_ATTACH;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_SPLINE_PATH_SETTINGS;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_SPLINE_DURATIONS;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_SPLINE_PLAY;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_SPLINE_PLAYBACK_STATUS;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_SPLINE_POINT;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_SPLINE_RECORD;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_SPLINE_SEEK;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_SURVEY_START;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_ZIPLINE_LOCK;
import static com.o3dr.services.android.lib.drone.companion.solo.tlv.TLVMessageTypes.TYPE_SOLO_ZIPLINE_OPTIONS;
/**
* Utility class to generate tlv packet from received bytes.
*/
public class TLVMessageParser {
private static final String TAG = TLVMessageParser.class.getSimpleName();
public static List<TLVPacket> parseTLVPacket(byte[] packetData){
if(packetData == null || packetData.length == 0)
return null;
return parseTLVPacket(ByteBuffer.wrap(packetData));
}
public static List<TLVPacket> parseTLVPacket(ByteBuffer packetBuffer) {
final List<TLVPacket> packetList = new ArrayList<>();
if (packetBuffer == null)
return packetList;
final int bufferSize = packetBuffer.limit();
if(bufferSize <= 0)
return packetList;
final ByteOrder originalOrder = packetBuffer.order();
packetBuffer.order(TLVPacket.TLV_BYTE_ORDER);
int messageType = -1;
try {
while (packetBuffer.remaining() >= TLVPacket.MIN_TLV_PACKET_SIZE) {
messageType = packetBuffer.getInt();
final int messageLength = packetBuffer.getInt();
int remaining = packetBuffer.remaining();
Log.d(TAG, String.format("Received message %d of with value of length %d. Remaining buffer size is %d", messageType, messageLength, remaining));
if (messageLength > remaining) {
break;
}
TLVPacket packet = null;
packetBuffer.mark();
switch (messageType) {
case TYPE_SOLO_MESSAGE_GET_CURRENT_SHOT:
case TYPE_SOLO_MESSAGE_SET_CURRENT_SHOT: {
final int shotType = packetBuffer.getInt();
if (messageType == TYPE_SOLO_MESSAGE_GET_CURRENT_SHOT)
packet = new SoloMessageShotGetter(shotType);
else
packet = new SoloMessageShotSetter(shotType);
break;
}
case TYPE_SOLO_MESSAGE_LOCATION: {
final double latitude = packetBuffer.getDouble();
final double longitude = packetBuffer.getDouble();
final float altitude = packetBuffer.getFloat();
packet = new SoloMessageLocation(latitude, longitude, altitude);
break;
}
case TYPE_SOLO_MESSAGE_RECORD_POSITION: {
packet = new SoloMessageRecordPosition();
break;
}
case TYPE_SOLO_CABLE_CAM_OPTIONS: {
final short camInterpolation = packetBuffer.getShort();
final short yawDirectionClockwise = packetBuffer.getShort();
final float cruiseSpeed = packetBuffer.getFloat();
packet = new SoloCableCamOptions(camInterpolation, yawDirectionClockwise, cruiseSpeed);
break;
}
case TYPE_SOLO_GET_BUTTON_SETTING:
case TYPE_SOLO_SET_BUTTON_SETTING: {
final int button = packetBuffer.getInt();
final int event = packetBuffer.getInt();
final int shotType = packetBuffer.getInt();
final int flightMode = packetBuffer.getInt();
if (messageType == TYPE_SOLO_GET_BUTTON_SETTING)
packet = new SoloButtonSettingGetter(button, event, shotType, flightMode);
else
packet = new SoloButtonSettingSetter(button, event, shotType, flightMode);
break;
}
case TYPE_SOLO_FOLLOW_OPTIONS: {
packet = new SoloFollowOptions(packetBuffer);
break;
}
case TYPE_SOLO_FOLLOW_OPTIONS_V2: {
packet = new SoloFollowOptionsV2(packetBuffer);
break;
}
case TYPE_SOLO_SHOT_OPTIONS: {
final float cruiseSpeed = packetBuffer.getFloat();
packet = new SoloShotOptions(cruiseSpeed);
break;
}
case TYPE_SOLO_SHOT_ERROR: {
final int errorType = packetBuffer.getInt();
packet = new SoloShotError(errorType);
break;
}
case TYPE_SOLO_MESSAGE_SHOT_MANAGER_ERROR: {
final byte[] exceptionData = new byte[messageLength];
packetBuffer.get(exceptionData);
packet = new SoloMessageShotManagerError(new String(exceptionData));
break;
}
case TYPE_SOLO_CABLE_CAM_WAYPOINT: {
final double latitude = packetBuffer.getDouble();
final double longitude = packetBuffer.getDouble();
final float altitude = packetBuffer.getFloat();
final float degreesYaw = packetBuffer.getFloat();
final float pitch = packetBuffer.getFloat();
packet = new SoloCableCamWaypoint(latitude, longitude, altitude, degreesYaw, pitch);
break;
}
case TYPE_ARTOO_INPUT_REPORT_MESSAGE: {
final double timestamp = packetBuffer.getDouble();
final short gimbalY = packetBuffer.getShort();
final short gimbalRate = packetBuffer.getShort();
final short battery = packetBuffer.getShort();
packet = new ControllerMessageInputReport(timestamp, gimbalY, gimbalRate, battery);
break;
}
case TYPE_SOLO_GOPRO_SET_REQUEST: {
final short command = packetBuffer.getShort();
final short value = packetBuffer.getShort();
packet = new SoloGoproSetRequest(command, value);
break;
}
case TYPE_SOLO_GOPRO_RECORD: {
@SoloGoproConstants.RecordCommand final int command = packetBuffer.getInt();
packet = new SoloGoproRecord(command);
break;
}
case TYPE_SOLO_GOPRO_STATE: {
packet = new SoloGoproState(packetBuffer);
break;
}
case TYPE_SOLO_GOPRO_STATE_V2:{
packet = new SoloGoproStateV2(packetBuffer);
break;
}
case TYPE_SOLO_GOPRO_REQUEST_STATE: {
packet = new SoloGoproRequestState();
break;
}
case TYPE_SOLO_GOPRO_SET_EXTENDED_REQUEST: {
final short command = packetBuffer.getShort();
final byte[] values = new byte[4];
packetBuffer.get(values);
packet = new SoloGoproSetExtendedRequest(command, values);
break;
}
case TYPE_SOLO_SPLINE_RECORD: {
packet = new SoloSplineRecord();
break;
}
case TYPE_SOLO_SPLINE_PLAY: {
packet = new SoloSplinePlay();
break;
}
case TYPE_SOLO_SPLINE_POINT:{
packet = new SoloSplinePoint(packetBuffer);
break;
}
case TYPE_SOLO_SPLINE_SEEK:{
packet = new SoloSplineSeek(packetBuffer);
break;
}
case TYPE_SOLO_SPLINE_PLAYBACK_STATUS:{
packet = new SoloSplinePlaybackStatus(packetBuffer);
break;
}
case TYPE_SOLO_SPLINE_PATH_SETTINGS:{
packet = new SoloSplinePathSettings(packetBuffer);
break;
}
case TYPE_SOLO_SPLINE_DURATIONS:{
packet = new SoloSplineDurations(packetBuffer);
break;
}
case TYPE_SOLO_SPLINE_ATTACH: {
packet = new SoloSplineAttach(packetBuffer);
break;
}
case TYPE_SOLO_PANO_OPTIONS: {
packet = new SoloPanoOptions(packetBuffer);
break;
}
case TYPE_SOLO_PANO_STATUS: {
packet = new SoloPanoStatus(packetBuffer);
break;
}
case TYPE_SOLO_ZIPLINE_OPTIONS: {
packet = new SoloZiplineOptions(packetBuffer);
break;
}
case TYPE_SOLO_ZIPLINE_LOCK: {
packet = new SoloZiplineLock();
break;
}
case TYPE_RTL_HOME_POINT: {
final double latitude = packetBuffer.getDouble();
final double longitude = packetBuffer.getDouble();
final float altitude = packetBuffer.getFloat();
packet = new SoloReturnHomeLocationMessage(latitude, longitude, altitude);
break;
}
case TYPE_SOLO_REWIND_OPTIONS: {
packet = new SoloRewindOptions(packetBuffer);
break;
}
/************************ Site Scan tlv packets **************/
case TYPE_SOLO_INSPECT_START:{
packet = new SoloInspectStart(packetBuffer);
break;
}
case TYPE_SOLO_INSPECT_SET_WAYPOINT:{
packet = new SoloInspectSetWaypoint(packetBuffer);
break;
}
case TYPE_SOLO_INSPECT_MOVE_GIMBAL:{
packet = new SoloInspectMoveGimbal(packetBuffer);
break;
}
case TYPE_SOLO_INSPECT_MOVE_VEHICLE:{
packet = new SoloInspectMoveVehicle(packetBuffer);
break;
}
case TYPE_SOLO_SCAN_START:{
packet = new SoloScanStart();
break;
}
case TYPE_SOLO_SURVEY_START:{
packet = new SoloSurveyStart();
break;
}
case TYPE_SOLO_PAUSE_BUTTON:{
packet = new SoloPause();
break;
}
default:
break;
}
if (packet != null && packet.getMessageLength() == messageLength) {
packetList.add(packet);
} else {
packetBuffer.reset();
packetBuffer.position(packetBuffer.position() + messageLength);
}
}
} catch (BufferUnderflowException e) {
Log.e(TAG, "Invalid data for tlv packet of type " + messageType);
}
packetBuffer.order(originalOrder);
return packetList;
}
//Private constructor to prevent instantiation
private TLVMessageParser() {}
}