com.sitewhere.communication.protobuf.ProtobufMessageBuilder Maven / Gradle / Ivy
/**
* Copyright © 2014-2021 The SiteWhere Authors
*
* 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 com.sitewhere.communication.protobuf;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.protobuf.DescriptorProtos;
import com.google.protobuf.Descriptors;
import com.google.protobuf.DynamicMessage;
import com.sitewhere.microservice.api.device.IDeviceManagement;
import com.sitewhere.spi.SiteWhereException;
import com.sitewhere.spi.device.IDeviceAssignment;
import com.sitewhere.spi.device.IDeviceNestingContext;
import com.sitewhere.spi.device.IDeviceType;
import com.sitewhere.spi.device.command.IDeviceCommandExecution;
import io.sitewhere.k8s.crd.tenant.SiteWhereTenant;
/**
* Produces an encoded message based on Google Protocol Buffer derived from an
* {@link IDeviceType}.
*
* @author Derek
*/
public class ProtobufMessageBuilder {
/** Static logger instance */
private static Logger LOGGER = LoggerFactory.getLogger(ProtobufMessageBuilder.class);
/**
* Create a protobuf message for an {@link IDeviceCommandExecution}.
*
* @param execution
* @param nested
* @param assignments
* @param tenant
* @param deviceManagement
* @return
* @throws SiteWhereException
*/
public static byte[] createMessage(IDeviceCommandExecution execution, IDeviceNestingContext nested,
List extends IDeviceAssignment> assignments, SiteWhereTenant tenant, IDeviceManagement deviceManagement)
throws SiteWhereException {
IDeviceType deviceType = deviceManagement.getDeviceType(execution.getCommand().getDeviceTypeId());
DescriptorProtos.FileDescriptorProto fdproto = getFileDescriptor(deviceType, tenant, deviceManagement);
LOGGER.debug("Using the following device type proto:\n" + fdproto.toString());
Descriptors.FileDescriptor[] fdescs = new Descriptors.FileDescriptor[0];
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
Descriptors.FileDescriptor filedesc = Descriptors.FileDescriptor.buildFrom(fdproto, fdescs);
Descriptors.Descriptor mdesc = filedesc
.findMessageTypeByName(ProtobufNaming.getDeviceTypeIdentifier(deviceType));
// Create the header message.
Descriptors.Descriptor header = mdesc.findNestedTypeByName(ProtobufNaming.HEADER_MSG_NAME);
DynamicMessage.Builder headBuilder = DynamicMessage.newBuilder(header);
// Set enum value based on command.
Descriptors.EnumDescriptor enumDesc = mdesc.findEnumTypeByName(ProtobufNaming.COMMAND_TYPES_ENUM);
Descriptors.EnumValueDescriptor enumValue = enumDesc
.findValueByName(ProtobufNaming.getCommandEnumName(execution.getCommand()));
if (enumValue == null) {
throw new SiteWhereException("No enum value found for command: " + execution.getCommand().getName());
}
headBuilder.setField(header.findFieldByName(ProtobufNaming.HEADER_COMMAND_FIELD_NAME), enumValue);
headBuilder.setField(header.findFieldByName(ProtobufNaming.HEADER_ORIGINATOR_FIELD_NAME),
execution.getInvocation().getId().toString());
if (nested.getNested() != null) {
IDeviceType nestedType = deviceManagement.getDeviceType(nested.getNested().getDeviceTypeId());
LOGGER.debug(
"Targeting nested device with type: " + nestedType.getName() + " at path " + nested.getPath());
headBuilder.setField(header.findFieldByName(ProtobufNaming.HEADER_NESTED_PATH_FIELD_NAME),
nested.getPath());
headBuilder.setField(header.findFieldByName(ProtobufNaming.HEADER_NESTED_TYPE_FIELD_NAME),
nestedType.getToken());
}
DynamicMessage hmessage = headBuilder.build();
LOGGER.debug("Header:\n" + hmessage.toString());
hmessage.writeDelimitedTo(out);
// Find nested type for command and create/populate an instance.
Descriptors.Descriptor command = mdesc.findNestedTypeByName(execution.getCommand().getName());
DynamicMessage.Builder cbuilder = DynamicMessage.newBuilder(command);
// Set each field in the command message.
for (String name : execution.getParameters().keySet()) {
Object value = execution.getParameters().get(name);
Descriptors.FieldDescriptor field = command.findFieldByName(name);
if (field == null) {
throw new SiteWhereException("Command parameter '" + name + "' not found in device type: ");
}
try {
cbuilder.setField(field, value);
} catch (IllegalArgumentException iae) {
LOGGER.error(
"Error setting field '" + name + "' with object of type: " + value.getClass().getName(),
iae);
}
}
DynamicMessage cmessage = cbuilder.build();
LOGGER.debug("Message:\n" + cmessage.toString());
cmessage.writeDelimitedTo(out);
return out.toByteArray();
} catch (Descriptors.DescriptorValidationException e) {
throw new SiteWhereException("Unable to create protobuf message.", e);
} catch (IOException e) {
throw new SiteWhereException("Unable to encode protobuf message.", e);
}
}
/**
* Gets a file descriptor for protobuf representation of {@link IDeviceType}.
*
* @param deviceType
* @param tenant
* @param deviceManagement
* @return
* @throws SiteWhereException
*/
protected static DescriptorProtos.FileDescriptorProto getFileDescriptor(IDeviceType deviceType,
SiteWhereTenant tenant, IDeviceManagement deviceManagement) throws SiteWhereException {
return ProtobufSpecificationBuilder.createFileDescriptor(deviceType, tenant, deviceManagement);
}
}