package com.openxc.messages.formatters.binary;
import com.google.protobuf.ByteString;
import com.google.protobuf.MessageLite;
import com.openxc.BinaryMessages;
import com.openxc.messages.CanMessage;
import com.openxc.messages.Command;
import com.openxc.messages.Command.CommandType;
import com.openxc.messages.CommandResponse;
import com.openxc.messages.DiagnosticRequest;
import com.openxc.messages.DiagnosticResponse;
import com.openxc.messages.EventedSimpleVehicleMessage;
import com.openxc.messages.NamedVehicleMessage;
import com.openxc.messages.SerializationException;
import com.openxc.messages.SimpleVehicleMessage;
import com.openxc.messages.VehicleMessage;
public class BinarySerializer {
public static MessageLite preSerialize(VehicleMessage message)
throws SerializationException {
if(message.hasExtras()) {
throw new SerializationException("Messages with extras cannot be " +
"serialized to the binary format - use JSON instead");
}
BinaryMessages.VehicleMessage.Builder builder =
BinaryMessages.VehicleMessage.newBuilder();
if(message instanceof CanMessage) {
serializeCanMessage(builder, (CanMessage) message);
} else if(message instanceof DiagnosticResponse) {
serializeDiagnosticResponse(builder, (DiagnosticResponse) message);
} else if(message instanceof Command) {
serializeCommand(builder, (Command) message);
} else if(message instanceof CommandResponse) {
serializeCommandResponse(builder, (CommandResponse) message);
} else if(message instanceof EventedSimpleVehicleMessage) {
serializeEventedSimpleVehicleMessage(builder,
(EventedSimpleVehicleMessage) message);
} else if(message instanceof SimpleVehicleMessage) {
serializeSimpleVehicleMessage(builder, (SimpleVehicleMessage) message);
} else if(message instanceof NamedVehicleMessage) {
serializeNamedVehicleMessage(builder, (NamedVehicleMessage) message);
} else {
serializeGenericVehicleMessage(builder, message);
}
return builder.build();
}
private static void serializeCanMessage(BinaryMessages.VehicleMessage.Builder builder,
CanMessage message) {
builder.setType(BinaryMessages.VehicleMessage.Type.CAN);
BinaryMessages.CanMessage.Builder messageBuilder =
BinaryMessages.CanMessage.newBuilder();
messageBuilder.setBus(message.getBusId());
messageBuilder.setId(message.getId());
messageBuilder.setData(ByteString.copyFrom(message.getData()));
builder.setCanMessage(messageBuilder);
}
private static void serializeDiagnosticResponse(BinaryMessages.VehicleMessage.Builder builder,
DiagnosticResponse message) {
builder.setType(BinaryMessages.VehicleMessage.Type.DIAGNOSTIC);
BinaryMessages.DiagnosticResponse.Builder messageBuilder =
BinaryMessages.DiagnosticResponse.newBuilder();
messageBuilder.setBus(message.getBusId());
messageBuilder.setMessageId(message.getId());
messageBuilder.setMode(message.getMode());
messageBuilder.setPid(message.getPid());
messageBuilder.setNegativeResponseCode(message.getNegativeResponseCode().code());
messageBuilder.setSuccess(message.isSuccessful());
if(message.hasValue()) {
messageBuilder.setValue(message.getValue());
}
if(message.hasPayload()) {
messageBuilder.setPayload(ByteString.copyFrom(message.getPayload()));
}
builder.setDiagnosticResponse(messageBuilder);
}
private static BinaryMessages.DiagnosticControlCommand.Builder
startSerializingDiagnosticRequest(Command message) {
DiagnosticRequest diagnosticRequest = message.getDiagnosticRequest();
BinaryMessages.DiagnosticControlCommand.Builder messageBuilder =
BinaryMessages.DiagnosticControlCommand.newBuilder();
BinaryMessages.DiagnosticRequest.Builder requestBuilder =
BinaryMessages.DiagnosticRequest.newBuilder();
requestBuilder.setBus(diagnosticRequest.getBusId());
requestBuilder.setMessageId(diagnosticRequest.getId());
requestBuilder.setMode(diagnosticRequest.getMode());
requestBuilder.setMultipleResponses(diagnosticRequest.getMultipleResponses());
if(diagnosticRequest.hasPid()) {
requestBuilder.setPid(diagnosticRequest.getPid());
}
if(diagnosticRequest.hasFrequency()) {
requestBuilder.setFrequency(diagnosticRequest.getFrequency());
}
if(diagnosticRequest.hasName()) {
requestBuilder.setName(diagnosticRequest.getName());
}
if(diagnosticRequest.hasPayload()) {
requestBuilder.setPayload(ByteString.copyFrom(diagnosticRequest.getPayload()));
}
// TODO add decoded_type when it hits the spec:
// https://github.com/openxc/openxc-message-format/issues/17
// messageBuilder.setDecodedType(diagnosticRequest.getDecodedType());
messageBuilder.setRequest(requestBuilder);
if(message.hasAction()) {
if(message.getAction().equals(DiagnosticRequest.ADD_ACTION_KEY)) {
messageBuilder.setAction(
BinaryMessages.DiagnosticControlCommand.Action.ADD);
} else if(message.getAction().equals(DiagnosticRequest.CANCEL_ACTION_KEY)) {
messageBuilder.setAction(
BinaryMessages.DiagnosticControlCommand.Action.CANCEL);
}
}
return messageBuilder;
}
private static void serializeCommand(BinaryMessages.VehicleMessage.Builder builder,
Command message) throws SerializationException {
builder.setType(BinaryMessages.VehicleMessage.Type.CONTROL_COMMAND);
BinaryMessages.ControlCommand.Builder messageBuilder =
BinaryMessages.ControlCommand.newBuilder();
CommandType commandType = message.getCommand();
if(commandType.equals(CommandType.VERSION)) {
messageBuilder.setType(BinaryMessages.ControlCommand.Type.VERSION);
} else if(commandType.equals(CommandType.DEVICE_ID)) {
messageBuilder.setType(BinaryMessages.ControlCommand.Type.DEVICE_ID);
} else if(commandType.equals(CommandType.DIAGNOSTIC_REQUEST)) {
messageBuilder.setType(BinaryMessages.ControlCommand.Type.DIAGNOSTIC);
messageBuilder.setDiagnosticRequest(
startSerializingDiagnosticRequest(message));
} else {
throw new SerializationException(
"Unrecognized command type in response: " + commandType);
}
builder.setControlCommand(messageBuilder);
}
private static void serializeCommandResponse(BinaryMessages.VehicleMessage.Builder builder,
CommandResponse message) throws SerializationException {
builder.setType(BinaryMessages.VehicleMessage.Type.COMMAND_RESPONSE);
BinaryMessages.CommandResponse.Builder messageBuilder =
BinaryMessages.CommandResponse.newBuilder();
if(message.getCommand().equals(CommandType.VERSION)) {
messageBuilder.setType(BinaryMessages.ControlCommand.Type.VERSION);
} else if(message.getCommand().equals(CommandType.DEVICE_ID)) {
messageBuilder.setType(BinaryMessages.ControlCommand.Type.DEVICE_ID);
} else if(message.getCommand().equals(CommandType.DIAGNOSTIC_REQUEST)) {
messageBuilder.setType(BinaryMessages.ControlCommand.Type.DIAGNOSTIC);
} else {
throw new SerializationException(
"Unrecognized command type in response: " +
message.getCommand());
}
messageBuilder.setStatus(message.getStatus());
if(message.hasMessage()) {
messageBuilder.setMessage(message.getMessage());
}
builder.setCommandResponse(messageBuilder);
}
private static BinaryMessages.DynamicField.Builder buildDynamicField(
Object value) {
BinaryMessages.DynamicField.Builder fieldBuilder =
BinaryMessages.DynamicField.newBuilder();
if(value instanceof String) {
fieldBuilder.setType(BinaryMessages.DynamicField.Type.STRING);
fieldBuilder.setStringValue((String)value);
} else if(value instanceof Number) {
fieldBuilder.setType(BinaryMessages.DynamicField.Type.NUM);
fieldBuilder.setNumericValue(((Number)value).doubleValue());
} else if(value instanceof Boolean) {
fieldBuilder.setType(BinaryMessages.DynamicField.Type.BOOL);
fieldBuilder.setBooleanValue((Boolean) value);
}
return fieldBuilder;
}
private static BinaryMessages.SimpleMessage.Builder startBuildingSimple(
BinaryMessages.VehicleMessage.Builder builder,
NamedVehicleMessage message) {
builder.setType(BinaryMessages.VehicleMessage.Type.SIMPLE);
BinaryMessages.SimpleMessage.Builder messageBuilder =
BinaryMessages.SimpleMessage.newBuilder();
messageBuilder.setName(message.getName());
return messageBuilder;
}
private static void serializeEventedSimpleVehicleMessage(BinaryMessages.VehicleMessage.Builder builder,
EventedSimpleVehicleMessage message) {
BinaryMessages.SimpleMessage.Builder messageBuilder =
startBuildingSimple(builder, message);
messageBuilder.setValue(buildDynamicField(message.getValue()));
messageBuilder.setEvent(buildDynamicField(message.getEvent()));
builder.setSimpleMessage(messageBuilder);
}
private static void serializeSimpleVehicleMessage(BinaryMessages.VehicleMessage.Builder builder,
SimpleVehicleMessage message) {
BinaryMessages.SimpleMessage.Builder messageBuilder =
startBuildingSimple(builder, message);
messageBuilder.setValue(buildDynamicField(message.getValue()));
builder.setSimpleMessage(messageBuilder);
}
private static void serializeNamedVehicleMessage(BinaryMessages.VehicleMessage.Builder builder,
NamedVehicleMessage message) {
BinaryMessages.SimpleMessage.Builder messageBuilder =
startBuildingSimple(builder, message);
builder.setSimpleMessage(messageBuilder);
}
private static void serializeGenericVehicleMessage(BinaryMessages.VehicleMessage.Builder builder,
VehicleMessage message) throws SerializationException {
// The binary format doesn't support arbitrary extra fields right now -
// could support with protobuf extensions but that is not something I
// want to do right now
throw new SerializationException(
"Can't serialize generic VehicleMessage to binary: " + message);
}
}