@ -17,26 +17,23 @@ package org.thingsboard.server.transport.mqtt.util.sparkplug;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties ;
import com.fasterxml.jackson.databind.annotation.JsonSerialize ;
import com.fasterxml.jackson.databind.node.ArrayNode ;
import com.fasterxml.jackson.databind.ser.std.FileSerializer ;
import com.google.gson.Gson ;
import com.google.gson.GsonBuilder ;
import com.google.gson.JsonArray ;
import lombok.extern.slf4j.Slf4j ;
import org.apache.commons.codec.binary.Hex ;
import org.apache.commons.lang3.StringUtils ;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode ;
import org.thingsboard.server.common.data.exception.ThingsboardException ;
import org.thingsboard.server.gen.transport.TransportProtos ;
import org.thingsboard.server.gen.transport.mqtt.SparkplugBProto ;
import java.math.BigInteger ;
import java.nio.ByteBuffer ;
import java.nio.ByteOrder ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.List ;
import java.util.Optional ;
import static org.thingsboard.common.util.JacksonUtil.newArrayNode ;
import static org.thingsboard.server.transport.mqtt.util.sparkplug.MetricDataType.BooleanArray ;
import static org.thingsboard.server.transport.mqtt.util.sparkplug.MetricDataType.Bytes ;
/ * *
* Provides utility methods for SparkplugB MQTT Payload Metric .
* /
@ -46,122 +43,153 @@ public class SparkplugMetricUtil {
public static Optional < TransportProtos . KeyValueProto > getFromSparkplugBMetricToKeyValueProto ( String key , SparkplugBProto . Payload . Metric protoMetric ) throws ThingsboardException {
// Check if the null flag has been set indicating that the value is null
if ( protoMetric . getIsNull ( ) ) {
return null ;
return Optional . empty ( ) ;
}
// Otherwise convert the value based on the type
int metricType = protoMetric . getDatatype ( ) ;
switch ( MetricDataType . fromInteger ( metricType ) ) {
TransportProtos . KeyValueProto . Builder builderProto = TransportProtos . KeyValueProto . newBuilder ( ) ;
ArrayNode nodeArray = newArrayNode ( ) ;
MetricDataType metricDataType = MetricDataType . fromInteger ( metricType ) ;
switch ( metricDataType ) {
case Boolean :
return Optional . of ( TransportProtos . KeyValueProto . newBuilder ( ) . setKey ( key ) . setType ( TransportProtos . KeyValueType . BOOLEAN_V )
return Optional . of ( builderProto . setKey ( key ) . setType ( TransportProtos . KeyValueType . BOOLEAN_V )
. setBoolV ( protoMetric . getBooleanValue ( ) ) . build ( ) ) ;
case DateTime :
case Int64 :
return Optional . of ( TransportProtos . KeyValueProto . newBuilder ( ) . setKey ( key ) . setType ( TransportProtos . KeyValueType . LONG_V )
return Optional . of ( builderProto . setKey ( key ) . setType ( TransportProtos . KeyValueType . LONG_V )
. setLongV ( protoMetric . getLongValue ( ) ) . build ( ) ) ;
case File :
String filename = protoMetric . getMetadata ( ) . getFileName ( ) ;
return Optional . of ( TransportProtos . KeyValueProto . newBuilder ( ) . setKey ( key + "_" + filename ) . setType ( TransportProtos . KeyValueType . STRING_V )
. setStringV ( Hex . encodeHexString ( ( protoMetric . getBytesValue ( ) . toByteArray ( ) ) ) ) . build ( ) ) ;
case Float :
return Optional . of ( TransportProtos . KeyValueProto . newBuilder ( ) . setKey ( key ) . setType ( TransportProtos . KeyValueType . LONG_V )
return Optional . of ( builderProto . setKey ( key ) . setType ( TransportProtos . KeyValueType . LONG_V )
. setLongV ( ( long ) protoMetric . getFloatValue ( ) ) . build ( ) ) ;
case Double :
return Optional . of ( TransportProtos . KeyValueProto . newBuilder ( ) . setKey ( key ) . setType ( TransportProtos . KeyValueType . DOUBLE_V )
return Optional . of ( builderProto . setKey ( key ) . setType ( TransportProtos . KeyValueType . DOUBLE_V )
. setDoubleV ( protoMetric . getDoubleValue ( ) ) . build ( ) ) ;
case Int8 :
case UInt8 :
case Int16 :
case Int32 :
case UInt16 :
return Optional . of ( TransportProtos . KeyValueProto . newBuilder ( ) . setKey ( key ) . setType ( TransportProtos . KeyValueType . LONG_V )
return Optional . of ( builderProto . setKey ( key ) . setType ( TransportProtos . KeyValueType . LONG_V )
. setLongV ( protoMetric . getIntValue ( ) ) . build ( ) ) ;
case UInt32 :
case UInt64 :
if ( protoMetric . hasIntValue ( ) ) {
return Optional . of ( TransportProtos . KeyValueProto . newBuilder ( ) . setKey ( key ) . setType ( TransportProtos . KeyValueType . LONG_V )
return Optional . of ( builderProto . setKey ( key ) . setType ( TransportProtos . KeyValueType . LONG_V )
. setLongV ( protoMetric . getIntValue ( ) ) . build ( ) ) ;
} else if ( protoMetric . hasLongValue ( ) ) {
return Optional . of ( TransportProtos . KeyValueProto . newBuilder ( ) . setKey ( key ) . setType ( TransportProtos . KeyValueType . LONG_V )
return Optional . of ( builderProto . setKey ( key ) . setType ( TransportProtos . KeyValueType . LONG_V )
. setLongV ( protoMetric . getLongValue ( ) ) . build ( ) ) ;
} else {
log . error ( "Invalid value for UInt32 datatype" ) ;
throw new ThingsboardException ( "Invalid value for UInt32 datatype " + metricType , ThingsboardErrorCode . INVALID_ARGUMENTS ) ;
throw new ThingsboardException ( "Invalid value for " + MetricDataType . fromInteger ( metricType ) . name ( ) + " datatype " + metricType , ThingsboardErrorCode . INVALID_ARGUMENTS ) ;
}
case UInt64 :
return Optional . of ( TransportProtos . KeyValueProto . newBuilder ( ) . setKey ( key ) . setType ( TransportProtos . KeyValueType . DOUBLE_V )
. setDoubleV ( ( new BigInteger ( Long . toUnsignedString ( protoMetric . getLongValue ( ) ) ) ) . longValue ( ) ) . build ( ) ) ;
case String :
case Text :
case UUID :
return Optional . of ( TransportProtos . KeyValueProto . newBuilder ( ) . setKey ( key ) . setType ( TransportProtos . KeyValueType . STRING_V )
return Optional . of ( builderProto . setKey ( key ) . setType ( TransportProtos . KeyValueType . STRING_V )
. setStringV ( protoMetric . getStringValue ( ) ) . build ( ) ) ;
case Bytes :
case BooleanArray :
case Int8Array :
case Int16Array :
case Int32Array :
case Int64Array :
case UInt8Array :
case UInt16Array :
case UInt32Array :
case UInt64Array :
case FloatArray :
case DoubleArray :
case BooleanArray :
return Optional . of ( TransportProtos . KeyValueProto . newBuilder ( ) . setKey ( key ) . setType ( TransportProtos . KeyValueType . STRING_V )
. setStringV ( Hex . encodeHexString ( protoMetric . getBytesValue ( ) . toByteArray ( ) ) ) . build ( ) ) ;
case DateTimeArray :
case Int64Array :
case UInt64Array :
case UInt32Array :
ByteBuffer byteBuffer = ByteBuffer . wrap ( protoMetric . getBytesValue ( ) . toByteArray ( ) ) ;
if ( ! ( metricDataType . equals ( Bytes ) | | metricDataType . equals ( BooleanArray ) ) ) {
byteBuffer . order ( ByteOrder . LITTLE_ENDIAN ) ;
}
while ( byteBuffer . hasRemaining ( ) ) {
setValueToNodeArray ( nodeArray , byteBuffer , metricType ) ;
}
return Optional . of ( builderProto . setKey ( key ) . setType ( TransportProtos . KeyValueType . JSON_V )
. setJsonV ( nodeArray . toString ( ) ) . build ( ) ) ;
case StringArray :
ByteBuffer stringByteBuffer = ByteBuffer . wrap ( protoMetric . getBytesValue ( ) . toByteArray ( ) ) ;
stringByteBuffer . order ( ByteOrder . LITTLE_ENDIAN ) ;
StringBuilder sb = new StringBuilder ( ) ;
while ( stringByteBuffer . hasRemaining ( ) ) {
byte b = stringByteBuffer . get ( ) ;
if ( b = = ( byte ) 0 ) {
nodeArray . add ( sb . toString ( ) ) ;
sb = new StringBuilder ( ) ;
} else {
sb . append ( ( char ) b ) ;
}
}
return Optional . of ( builderProto . setKey ( key ) . setType ( TransportProtos . KeyValueType . JSON_V )
. setJsonV ( nodeArray . toString ( ) ) . build ( ) ) ;
case DataSet :
SparkplugBProto . Payload . DataSet protoDataSet = protoMetric . getDatasetValue ( ) ;
case Template :
case File :
//TODO
// Build the and create the DataSet
/ * *
SparkplugBProto . Payload . DataSet protoDataSet = protoMetric . getDatasetValue ( ) ;
return new SparkplugBProto . Payload . DataSet . Builder ( protoDataSet . getNumOfColumns ( ) ) . addColumnNames ( protoDataSet . getColumnsList ( ) )
. addTypes ( convertDataSetDataTypes ( protoDataSet . getTypesList ( ) ) )
. addRows ( convertDataSetRows ( protoDataSet . getRowsList ( ) , protoDataSet . getTypesList ( ) ) )
. createDataSet ( ) ;
* * /
return Optional . of ( TransportProtos . KeyValueProto . newBuilder ( ) . setKey ( key ) . setType ( TransportProtos . KeyValueType . STRING_V )
return Optional . of ( builderProto . setKey ( key ) . setType ( TransportProtos . KeyValueType . STRING_V )
. setStringV ( protoDataSet . toString ( ) ) . build ( ) ) ;
case Template :
* * /
//TODO
// Build the and create the Template
/ * *
SparkplugBProto . Payload . Template protoTemplate = protoMetric . getTemplateValue ( ) ;
return Optional . of ( TransportProtos . KeyValueProto . newBuilder ( ) . setKey ( key ) . setType ( TransportProtos . KeyValueType . STRING_V )
return Optional . of ( builderProto . setKey ( key ) . setType ( TransportProtos . KeyValueType . STRING_V )
. setStringV ( protoTemplate . toString ( ) ) . build ( ) ) ;
case StringArray :
ByteBuffer stringByteBuffer = ByteBuffer . wrap ( protoMetric . getBytesValue ( ) . toByteArray ( ) ) ;
List < String > stringList = new ArrayList < > ( ) ;
stringByteBuffer . order ( ByteOrder . LITTLE_ENDIAN ) ;
StringBuilder sb = new StringBuilder ( ) ;
while ( stringByteBuffer . hasRemaining ( ) ) {
byte b = stringByteBuffer . get ( ) ;
if ( b = = ( byte ) 0 ) {
stringList . add ( sb . toString ( ) ) ;
sb = new StringBuilder ( ) ;
} else {
sb . append ( ( char ) b ) ;
}
}
String st = StringUtils . join ( stringList , "|" ) ;
return Optional . of ( TransportProtos . KeyValueProto . newBuilder ( ) . setKey ( key ) . setType ( TransportProtos . KeyValueType . STRING_V )
. setStringV ( st ) . build ( ) ) ;
case DateTimeArray :
ByteBuffer dateTimeByteBuffer = ByteBuffer . wrap ( protoMetric . getBytesValue ( ) . toByteArray ( ) ) ;
List < Long > dateTimeList = new ArrayList < Long > ( ) ;
dateTimeByteBuffer . order ( ByteOrder . LITTLE_ENDIAN ) ;
while ( dateTimeByteBuffer . hasRemaining ( ) ) {
long longValue = dateTimeByteBuffer . getLong ( ) ;
dateTimeList . add ( longValue ) ;
}
Gson gson = new GsonBuilder ( ) . create ( ) ;
JsonArray dateTimeArray = gson . toJsonTree ( dateTimeList ) . getAsJsonArray ( ) ;
return Optional . of ( TransportProtos . KeyValueProto . newBuilder ( ) . setKey ( key ) . setType ( TransportProtos . KeyValueType . JSON_V )
. setStringV ( dateTimeArray . toString ( ) ) . build ( ) ) ;
* * /
//TODO
// Build the and create the File
/ * *
String filename = protoMetric . getMetadata ( ) . getFileName ( ) ;
return Optional . of ( builderPrbyteValueoto . setKey ( key + "_" + filename ) . setType ( TransportProtos . KeyValueType . STRING_V )
. setStringV ( Hex . encodeHexString ( ( protoMetric . getBytesValue ( ) . toByteArray ( ) ) ) ) . build ( ) ) ;
* * /
return Optional . empty ( ) ;
case Unknown :
default :
throw new ThingsboardException ( "Failed to decode: Unknown MetricDataType " + metricType , ThingsboardErrorCode . INVALID_ARGUMENTS ) ;
}
}
private static void setValueToNodeArray ( ArrayNode nodeArray , ByteBuffer byteBuffer , int metricType ) {
switch ( MetricDataType . fromInteger ( metricType ) ) {
case Bytes :
nodeArray . add ( byteBuffer . get ( ) ) ;
break ;
case BooleanArray :
nodeArray . add ( byteBuffer . get ( ) = = ( byte ) 0 ? "false" : "true" ) ;
break ;
case Int8Array :
case Int16Array :
case Int32Array :
case UInt8Array :
case UInt16Array :
nodeArray . add ( byteBuffer . getInt ( ) ) ;
break ;
case FloatArray :
nodeArray . add ( byteBuffer . getFloat ( ) ) ;
break ;
case DoubleArray :
nodeArray . add ( byteBuffer . getDouble ( ) ) ;
break ;
case DateTimeArray :
case Int64Array :
case UInt64Array :
case UInt32Array :
nodeArray . add ( byteBuffer . getLong ( ) ) ;
}
}
@JsonIgnoreProperties (
value = { "fileName" } )