Commit cdfc3702 authored by p x's avatar p x
Browse files

删除16jinzhi 几西裤

parent 99964718
......@@ -42,6 +42,4 @@ rootProject.name = "MapMultiEngine"
include(":app")
include(":mapapi")
include(":maplibs")//存放三方地图aar包
//include(":sixteen_parse")//16进制解析库
//include(":mypubdep") //公共应用
include(":fusedlibrary")
include(":fusedlibrary")//融合库
/build
\ No newline at end of file
plugins {
alias(libs.plugins.android.library)
// alias(libs.plugins.kotlin.android)
}
android {
namespace = "com.sixteen.parse"
compileSdk = 35
defaultConfig {
minSdk = 29
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = false
isShrinkResources = false
consumerProguardFiles("proguard-rules.pro")
// proguardFiles(
// getDefaultProguardFile("proguard-android-optimize.txt"),
// "proguard-rules.pro"
// )
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
//kotlin 注解库
implementation("org.jetbrains.kotlin:kotlin-reflect:2.0.0")
//Netty是一个异步事件驱动的网络应用框架
api("io.netty:netty-all:4.1.92.Final")
//事件分发
api("org.greenrobot:eventbus:3.3.1")
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
#-keep class com.sixteen.parse.normal.** { *; }
#-keep class com.sixteen.parse.assist_88.** { *; }
#-keep class com.sixteen.parse.vehcloud_state_82.** { *; }
#-keep class com.sixteen.parse.cloud2veh_advice_85.** { *; }
#-keep class com.sixteen.parse.selfdefine_810.** { *; }
# -- 解析Bean
#-keep class com.sixteen.parse.assist_88.** { *; }
#-keep class com.sixteen.parse.cloud2veh_advice_85.** { *; }
#-keep class com.sixteen.parse.vehcloud_state_82.** { *; }
#-keep class com.sixteen.parse.selfdefine_810.** { *; }
-keep class com.sixteen.parse.** { *; }
# 保留 Netty 核心类
-keep class io.netty.** { *; }
-keepclasseswithmembers class io.netty.** {
*;
}
-keepnames class io.netty.** { *; }
-dontwarn io.netty.**
\ No newline at end of file
package com.sixteen.parse
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.sixteen.parse.test", appContext.packageName)
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
\ No newline at end of file
package com.sixteen.parse;
public class MessageHeadInfo {
}
package com.sixteen.parse
import com.sixteen.parse.annotates.DependencyDef
import com.sixteen.parse.annotates.DynamicsClassDef
import com.sixteen.parse.annotates.FieldDef
import com.sixteen.parse.annotates.OffsetDef
import com.sixteen.parse.annotates.Order
import com.sixteen.parse.annotates.RefNumFlag
import com.sixteen.parse.normal.DynamicsClassMap
import io.netty.buffer.ByteBuf
import java.lang.reflect.Field
import java.lang.reflect.ParameterizedType
import java.nio.charset.StandardCharsets
import java.util.concurrent.ConcurrentHashMap
import kotlin.math.pow
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.jvm.isAccessible
object ParseSixteen {
//属性排序缓存
private var cacheSortMap = ConcurrentHashMap<Class<*>, List<Field>>()
/**
* 解析数据
* @param data byte数组
* @param t 被解析的类
*/
suspend fun parse(data: ByteBuf, t: Any) {
var clz = t::class.java
var orderedProps: List<Field>
if (cacheSortMap.containsKey(clz)) {
orderedProps = cacheSortMap.get(clz) ?: listOf()
} else {
val fields = clz.declaredFields
orderedProps = fields.sortedBy { field ->
var order = field.getAnnotation<Order>(Order::class.java)
order?.value ?: Int.MAX_VALUE
}
cacheSortMap.put(clz, orderedProps)
}
try {
orderedProps.forEach { field ->
field.setAccessible(true)
var returnType = field.type
// var clsFier = field.returnType.classifier
// var jclass = field.returnType.classifier?.javaClass
when {
returnType.isPrimitive && !returnType.isArray -> {
readBuf(t, field, data)
}
returnType.isPrimitive -> {
readBuf(t, field, data)
}
returnType == String::class.java -> {
readBuf(t, field, data)
}
List::class.java.isAssignableFrom(returnType) -> {
var b = 0
readListBuf(data, t, field)
}
returnType is Class<*> -> {//其他对象类型
if (data.readerIndex() == data.maxCapacity()) {
return
}
createKClass(data, t, field)
}
}
}
var a = 0
} catch (e: Exception) {
e.printStackTrace()
}
}
private suspend fun readListBuf(data: ByteBuf, t: Any, field: Field) {
// 集合类型
//读取属性的注解是否引用其他字段的长度
if (field.isAnnotationPresent(RefNumFlag::class.java) && !field.isAnnotationPresent(
DynamicsClassDef::class.java
)
) {
val refNumFlag = field.getAnnotation(RefNumFlag::class.java)
val refField: String = refNumFlag!!.value
val clazz1: Class<*> = t.javaClass
val nameField = clazz1.getDeclaredField(refField)
val genericType = field.getGenericType()
nameField.setAccessible(true)
if (genericType is ParameterizedType) {
val pt = genericType
val actualTypes = pt.getActualTypeArguments()
if (actualTypes != null && actualTypes.size > 0) {
actualTypes[0]!!.javaClass
var len = 0L
if (nameField.getType() == Short::class.javaPrimitiveType) {
val `val` = nameField.get(t) as Short
len = `val`.toLong()
} else if (nameField.getType() == Int::class.javaPrimitiveType) {
val `val` = nameField.get(t) as Int
len = `val`.toLong()
} else if (nameField.getType() == Long::class.javaPrimitiveType) {
len = nameField.get(t) as Long
}
//short len= (short) nameField.get(t);
if (len > 0) {
val list = mutableListOf<Any>()
for (i in 0..<len) {
val classname = actualTypes[0]!!.getTypeName()
var obj: Any
if (classname.contains("Short")) {
obj = data.readUnsignedByte()
} else if (classname.contains("Integer")) {
obj = data.readUnsignedShort()
} else if (classname.contains("Long")) {
obj = data.readUnsignedInt()
} else {
val cls = Class.forName(actualTypes[0]!!.getTypeName())
obj = cls.getDeclaredConstructor().newInstance()
parse(data, obj)
}
list.add(obj)
}
println("actualTypes::" + actualTypes[0]!!.getTypeName())
field.set(t, list)
}
}
}
} else if (field.isAnnotationPresent(RefNumFlag::class.java) && field.isAnnotationPresent(
DynamicsClassDef::class.java
)
) {
val dynamicsClassDef = field.getAnnotation(DynamicsClassDef::class.java)
val classtype = dynamicsClassDef?.classtype
val dependencyDef = field.getAnnotation(DependencyDef::class.java)
val fieldname = dependencyDef!!.value
val tField2 = t.javaClass.getDeclaredField(fieldname)
tField2.setAccessible(true)
val value = tField2.get(t) as Short?
val refNumFlag = field.getAnnotation(RefNumFlag::class.java)
val refFieldName = refNumFlag.value
val tField = t.javaClass.getDeclaredField(fieldname)
tField.setAccessible(true)
val tField1 = t.javaClass.getDeclaredField(refFieldName)
tField1.setAccessible(true)
val len = tField1.get(t) as Int
if (len > 0) {
val clazz1: Class<*> = DynamicsClassMap.get(classtype, value)
val cls = Class.forName(clazz1.getName())
val list: MutableList<Any?> = ArrayList<Any?>()
for (i in 0..<len) {
val obj: Any = cls.newInstance()
println("clazz1 name::" + clazz1.getName())
parse(data, obj)
list.add(obj)
}
field.set(t, list)
}
}
}
private suspend fun createKClass(data: ByteBuf, t: Any, field: Field) {
var dynamicsClassDef = field.getAnnotation(DynamicsClassDef::class.java)
if (dynamicsClassDef != null) {
val classtype = dynamicsClassDef.classtype
val dependencyDef = field.getAnnotation(DependencyDef::class.java)
val fieldname = dependencyDef?.value
// val tField2 = t.javaClass.getDeclaredField(fieldname)
val tField2 = t::class.java.getDeclaredField(fieldname)
tField2.isAccessible = true
// val tField2 = t::class.declaredMemberProperties.first { it.name == fieldname }
// tField2.setAccessible(true)
val key = tField2.get(t) as Short
// val key = tField2.getter.call(t) as Short
val clazz1: Class<*> = DynamicsClassMap.get(classtype, key)
val cls = Class.forName(clazz1.getName())
val obj = cls.getDeclaredConstructor().newInstance()
parse(data, obj)
field.set(t, obj)
} else {
var clsName = field.type.toString().split(" ").get(1)
.trimEnd { it == '?' }
val cls = Class.forName(clsName)
val obj = cls.getDeclaredConstructor().newInstance()
parse(data, obj)
field.set(t, obj)
}
}
private fun readBuf(t: Any, field: Field, data: ByteBuf) {
if (data.readerIndex() == data.maxCapacity()) return
var fieldDef = field.getAnnotation(FieldDef::class.java)
var refNumFlag = field.getAnnotation(RefNumFlag::class.java)
val type = field.type
when {
type == String::class.java && fieldDef != null && fieldDef.type == "BYTE" -> {
var by = ByteArray(fieldDef.length)
data.readBytes(by)
by = by.filter { it != 0.toByte() }.toByteArray()
val str = String(by, StandardCharsets.UTF_8)
field.set(t, str)
// val strField = field as KMutableProperty1<Any, String>
// strField.set(t, str)
}
type == String::class.java && refNumFlag != null -> {
val refNumFlagField = t.javaClass.getDeclaredField(refNumFlag.value)
refNumFlagField.setAccessible(true)
val size = refNumFlagField.get(t) as Int
var by = ByteArray(size)
data.readBytes(by)
val str = String(by, StandardCharsets.UTF_8)
by = by.filter { it != 0.toByte() }.toByteArray()
field.set(t, str)
}
type == Short::class.java -> {
field.set(t, data.readUnsignedByte())
}
type == Int::class.java -> {
field.set(t, data.readUnsignedShort())
}
type == Long::class.java && fieldDef == null -> {
field.set(t, data.readUnsignedInt())
}
type == Long::class.java && fieldDef != null && fieldDef.type == "TIMESTAMP" && fieldDef.length == 8 -> {
field.set(t, data.readLong())
}
type == Long::class.java && fieldDef != null && fieldDef.type == "BYTE" -> {
field.set(t, data.readLong())
}
type == Array::class.java && refNumFlag != null -> {
// val refNumFlagField = t.javaClass.getDeclaredField(refNumFlag.value)
val refNumFlagField =
t::class.declaredMemberProperties.first { it.name == refNumFlag.value }
// val refNumFlagField = t::class.declaredMemberProperties.first { it.findAnnotation<RefNumFlag>() != null }
refNumFlagField.isAccessible = true
val type1 = refNumFlagField.returnType.classifier
var by: ByteArray? = null
if (type1 == Byte::class) {
val byteField = refNumFlagField as KMutableProperty1<Any, Byte>
val obj = byteField.get(t).toInt()
by = ByteArray(obj)
} else if (type1 == Short::class) {
val shortField = refNumFlagField as KMutableProperty1<Any, Short>
val obj = shortField.get(t).toInt()
by = ByteArray(obj.toInt())
} else if (type1 == Int::class) {
val intField = refNumFlagField as KMutableProperty1<Any, Int>
val obj = intField.get(t)
by = ByteArray(obj)
}
by = by?.filter { it != 0.toByte() }?.toByteArray()
data.readBytes(by)
field.set(t, by)
}
}
}
/**
* 转换对象
* @param source 源对象
* @param target 目标对象
*/
fun converterBean(source: Any?, target: Any) {
if (source == null)
return
var clz = source::class.java
var orderedProps: List<Field>
if (cacheSortMap.containsKey(clz)) {
orderedProps = cacheSortMap.get(clz) ?: listOf()
} else {
val fields = clz.declaredFields
orderedProps = fields.sortedBy { field ->
var order = field.getAnnotation<Order>(Order::class.java)
// 无注解的属性排最后(或抛异常)
// val order = prop.findAnnotation<Order>()
order?.value ?: Int.MAX_VALUE
}
cacheSortMap.put(clz, orderedProps)
}
var tField: Field? = null
try {
//获取目标对象指定的属性
for (field in orderedProps) {
field.setAccessible(true)
val o = field.get(source)
if (o == null) {
continue
}
// System.out.println("---------convoterBean old fieldName " + field.getName());
var offsetDef = field.getAnnotation(OffsetDef::class.java)
//获取目标对象指定的属性
tField = target.javaClass.getDeclaredField(field.getName())
tField.setAccessible(true)
if (field.isAnnotationPresent(Order::class.java)) {
if (field.getType().isPrimitive() && !field.getType().isArray()) {
//如果属性添加了自定义注解 offset
val type = field.getType()
if (offsetDef != null) {
val offset: Double = offsetDef.value
val type1: Int = offsetDef.type
val minValidLength: Int = offsetDef.minValidLength
//startindex
if (type == Byte::class.javaPrimitiveType) {
tField.set(target, o)
} else if (type == Short::class.javaPrimitiveType) {
if (minValidLength > 0) {
val len = (o as Short).toString().length
if (len < minValidLength) {
val subtract = minValidLength - len
val f = o * 10.0.pow(subtract.toDouble()) * offset
tField.set(target, f.toFloat())
} else {
val f = o * offset
tField.set(target, f.toFloat())
}
} else {
val f = o as Short * offset
tField.set(target, f.toFloat())
}
} else if (type == Int::class.javaPrimitiveType) {
if (minValidLength > 0) {
val len = (o as Int).toString().length
if (len < minValidLength) {
val subtract = minValidLength - len
val f = o * 10.0.pow(subtract.toDouble()) * offset
tField.set(target, f.toFloat())
} else {
val f = o * offset
tField.set(target, f.toFloat())
}
} else {
val f = o as Int * offset
tField.set(target, f.toFloat())
}
} else if (type == Long::class.javaPrimitiveType) {
if (minValidLength > 0) {
val len = (o as Long).toString().length
if (len < minValidLength) {
val subtract = minValidLength - len
val f = o * 10.0.pow(subtract.toDouble()) * offset
tField.set(target, f)
} else {
val f = o * offset
tField.set(target, f)
}
} else {
val d = o as Long * offset
tField.set(target, d)
}
}
} else {
tField.set(target, o)
}
} else if (field.javaClass.isPrimitive() && field.getType().isArray()) {
// 基础数据类型 且 是数组
} else if (MutableList::class.java.isAssignableFrom(field.getType())) {
// 集合类型
//读取属性的注解是否引用其他字段的长度
if (field.isAnnotationPresent(RefNumFlag::class.java) && !field.isAnnotationPresent(
DynamicsClassDef::class.java
)
) {
val temp = o as MutableList<*>
val refNumFlag = field.getAnnotation(RefNumFlag::class.java)
val refField: String = refNumFlag.value
val clazz1: Class<*> = target.javaClass
val nameField = clazz1.getDeclaredField(refField)
val genericType = field.getGenericType()
val targetType = tField.getGenericType()
nameField.setAccessible(true)
if (genericType is ParameterizedType) {
val dsts: MutableList<Any?> = java.util.ArrayList<Any?>()
val pt = genericType
val actualTypes = pt.getActualTypeArguments()
val dspt = targetType as ParameterizedType
val dstTypes = dspt.getActualTypeArguments()
if (actualTypes.size > 0 && actualTypes != null) {
for (i in temp.indices) {
val src: Any = temp.get(i)!!
var dst: Any? = null
if (src is Short) {
dst = src
} else if (src is Int) {
dst = src
} else {
val cls = Class.forName(dstTypes[0]!!.getTypeName())
dst = cls.newInstance()
converterBean(src, dst)
}
dsts.add(dst)
}
tField.set(target, dsts)
}
}
}
} else if (field.getType() == String::class.java) {
//类型为字符串
tField.set(target, o)
} else {
//其他对象类型
if (field.isAnnotationPresent(DynamicsClassDef::class.java)) {
val dynamicsClassDef = field.getAnnotation(DynamicsClassDef::class.java)
val classtype: String? = dynamicsClassDef.classtype
val dependencyDef =
field.getAnnotation(DependencyDef::class.java)
val fieldname: String = dependencyDef.value
val tField2 = target.javaClass.getDeclaredField(fieldname)
tField2.setAccessible(true)
val key = tField2.get(target) as Short?
val cls: Class<*> = DynamicsClassMap.getDto(classtype, key)
val obj: Any = cls.newInstance()
converterBean(o, obj)
tField.set(target, obj)
} else {
val cls = Class.forName(tField.getType().getName())
val obj: Any = cls.newInstance()
converterBean(o, obj)
tField.set(target, obj)
}
}
}
}
} catch (e: Exception) {
println("----convoterBean Exception " + e + " tField " + tField)
}
}
}
\ No newline at end of file
package com.sixteen.parse;
import com.sixteen.parse.annotates.FieldDef;
import com.sixteen.parse.annotates.OffsetDef;
import com.sixteen.parse.annotates.Order;
import com.sixteen.parse.annotates.RefNumFlag;
import com.sixteen.parse.genHex.MsgHeadInfo;
import com.sixteen.parse.genHex.Position2DT;
import com.sixteen.parse.genHex.PositionT;
import com.sixteen.parse.genHex.VehInfo;
import com.sixteen.parse.genHex.VehMsg;
import com.sixteen.parse.genHex.VehMsgBody;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import io.netty.buffer.ByteBufUtil;
/**
* 完整的VehicleMessage转16进制字符串工具类
* 包含:MessageHeadInfo + VehicleMessageBody 完整序列化
*/
public class VehicleMessageHexSerializer {
// 全局字节序:统一使用大端(BIG_ENDIAN),可按需改为小端LITTLE_ENDIAN
private static final ByteOrder BYTE_ORDER = ByteOrder.BIG_ENDIAN;
/**
* @param speed 速度 km/h
* @param longitude
* @param latitude
* @param evel 高程(米)
* @param head 航向角
* @return 返回8.2字节数组
*/
public static byte[] take8_2Bytes(short speed, double longitude, double latitude, double evel, float head, String vehicleId) throws Exception {
// 1. 构建完整的VehicleMessage对象
VehMsg message = new VehMsg();
// 1.1 构建消息头(MessageHeadInfo)
MsgHeadInfo headInfo = new MsgHeadInfo();
headInfo.setFlag((byte) 0xF2); // 协议版本
headInfo.setBodyLenth((int) 74);
headInfo.setDataType((byte) 0x15); // 消息类型8.2
headInfo.setVersion((byte) 0x01); //版本号
headInfo.setTimestamp(System.currentTimeMillis());
headInfo.setControlContent((byte) 0x01);
message.setMsgHeadInfo(headInfo);
// 1.2 构建消息体
VehMsgBody body = new VehMsgBody();
// 1.2.1 车辆基础信息 VehInfo
VehInfo vehInfo = new VehInfo();
vehInfo.setVehicleId(vehicleId);
vehInfo.setTimestampGNSS(System.currentTimeMillis()); // 车辆ID(8字节)
vehInfo.setVelocityGNSS(speed); //传进来是千米/时)
PositionT position = new PositionT(); //位置
position.setLongitude(longitude + 180);
position.setLatitude(latitude + 90);
position.setElevation(evel + 500); //传进来是米
vehInfo.setPosition(position);
vehInfo.setHeading((int) (head * 10000));
vehInfo.setVelocity(speed);
Position2DT destLocation = new Position2DT();//目的地
destLocation.setLongitude(180);
destLocation.setLatitude(90);
vehInfo.setDestLocation(destLocation);
body.setVehInfo(vehInfo);
message.setVehMsgBody(body);
return serialize(message);
}
/**
* 对外核心方法:将完整的VehicleMessage转为16进制字符串
*
* @param message 待序列化的完整车辆消息对象
* @return 大写16进制字符串
* @throws Exception 反射/序列化异常
*/
private static byte[] serialize(VehMsg message) throws Exception {
if (message == null) {
throw new IllegalArgumentException("VehicleMessage 不能为空");
}
// 预分配足够的字节缓冲区(可根据实际业务调整大小)
ByteBuffer buffer = ByteBuffer.allocate(512);
buffer.order(BYTE_ORDER);
// 第一步:序列化消息头(MessageHeadInfo)- Order=0,优先级最高
serializeField(buffer, message.getMsgHeadInfo(), MsgHeadInfo.class);
// 第二步:序列化消息体(VehicleMessageBody)- Order=1
int bodyStartPos = buffer.position(); // 序列化前的位置(消息体起始位置)
serializeField(buffer, message.getVehMsgBody(), VehMsgBody.class);
int bodyEndPos = buffer.position(); // 序列化后的位置(消息体结束位置)
int msgBodyLength = bodyEndPos - bodyStartPos;
buffer.putInt(1, msgBodyLength);
// 切换为读模式,截取有效字节(避免空字节)
buffer.flip();
byte[] validBytes = new byte[buffer.remaining()];
buffer.get(validBytes);
String hexStr = ByteBufUtil.hexDump(validBytes);
System.out.println("完整VehicleMessage 16进制字符串:");
System.out.println(hexStr);
return validBytes;
// return bytesToHex(validBytes);
}
/**
* 通用递归序列化方法:支持基本类型、自定义对象、List集合
* 复用你原有的VehicleMessageBody序列化逻辑,统一处理所有字段
*/
private static void serializeField(ByteBuffer buffer, Object fieldValue, Class<?> fieldClass) throws Exception {
if (fieldValue == null) {
return;
}
// 1. 处理基本类型/包装类型(short/int/long/byte/float/double等)
if (isBasicType(fieldClass)) {
serializeBasicType(buffer, fieldValue);
return;
}
// 2. 处理List集合(适配@RefNumFlag注解,如多轮速、多传感器数据)
/* if (List.class.isAssignableFrom(fieldClass)) {
List<?> list = (List<?>) fieldValue;
for (Object item : list) {
if (item != null) {
serializeField(buffer, item, item.getClass());
}
}
return;
}*/
// 3. 处理自定义对象(按@Order注解升序排序字段)
Field[] fields = fieldClass.getDeclaredFields();
java.util.Arrays.sort(fields, (f1, f2) -> {
Order o1 = f1.getAnnotation(Order.class);
Order o2 = f2.getAnnotation(Order.class);
int order1 = o1 == null ? Integer.MAX_VALUE : o1.value();
int order2 = o2 == null ? Integer.MAX_VALUE : o2.value();
return Integer.compare(order1, order2);
});
// 遍历排序后的字段,递归序列化
for (Field field : fields) {
field.setAccessible(true);
Object value = field.get(fieldValue);
if (value == null) {
continue;
}
// 解析各类注解并处理字段
OffsetDef offsetDef = field.getAnnotation(OffsetDef.class);
FieldDef fieldDef = field.getAnnotation(FieldDef.class);
RefNumFlag refNumFlag = field.getAnnotation(RefNumFlag.class);
// 处理@OffsetDef:数值偏移(如0.1km、0.01m/s等单位转换)
Object actualValue = handleOffsetDef(value, offsetDef, fieldDef);
// 处理@FieldDef:固定长度/字节数组类型(如设备ID、时间戳)
if (fieldDef != null) {
serializeFieldDef(buffer, actualValue, fieldDef);
continue;
}
// 处理@RefNumFlag:基于其他字段的集合(已在List分支处理,此处跳过)
if (refNumFlag != null) {
continue;
}
// 递归序列化嵌套对象(如VehicleInfo/Position等)
serializeField(buffer, actualValue, field.getType());
}
}
/**
* 处理@OffsetDef注解:还原原始字节值(实际值 / 偏移量)
*/
private static Object handleOffsetDef(Object value, OffsetDef offsetDef, FieldDef fieldDef) {
if (offsetDef == null || value == null) {
return value;
}
double offset = offsetDef.value();
if (offset == 0) {
return value;
}
String type = "";
if (fieldDef != null) {
type = fieldDef.type();
}
// 按数据类型处理偏移转换
if (value instanceof Long) {
return (long) ((Long) value / offset);
} else if (value instanceof Integer) {
return (int) ((Integer) value / offset);
} else if (value instanceof Short) {
return (short) ((Short) value / offset);
} else if (value instanceof Double) {
if (type.equals("Position")) {
return (long) ((Double) value / offset);
}
return ((Double) value / offset);
} else if (value instanceof Float) {
return (float) ((Float) value / offset);
}
return value;
}
/**
* 处理@FieldDef注解:固定长度字节数组/特殊类型(TIMESTAMP/BYTE)
*/
private static void serializeFieldDef(ByteBuffer buffer, Object value, FieldDef fieldDef) throws Exception {
String type = fieldDef.type();
int length = fieldDef.length();
// 处理BYTE类型:固定长度字节数组(如vehicleId固定8字节)
if ("BYTE".equals(type)) {
byte[] bytes;
if (value instanceof String) {
// 1. 将字符串转为UTF-8字节数组
bytes = ((String) value).getBytes("UTF-8");
} else if (value instanceof Long) {
bytes = longToBytes((Long) value);
} else {
bytes = value.toString().getBytes("UTF-8");
}
// 固定长度填充/截断(不足补0,超出截断)
byte[] fixedBytes = new byte[length];
System.arraycopy(bytes, 0, fixedBytes, 0, Math.min(bytes.length, length));
// System.arraycopy(bytes, 0, fixedBytes, 0, length);
buffer.put(fixedBytes);
}
// 处理TIMESTAMP类型:8字节时间戳(long类型)
else if ("TIMESTAMP".equals(type)) {
buffer.putLong(((Long) value));
} else if ("Position".equals(type)) {
int intObjValue = ((Long) value).intValue();
buffer.putInt(intObjValue);
}
}
/**
* 序列化基本类型到字节缓冲区
*/
private static void serializeBasicType(ByteBuffer buffer, Object value) {
if (value instanceof Short) {
buffer.putShort((Short) value);
} else if (value instanceof Integer) {
buffer.putInt((Integer) value);
} else if (value instanceof Long) {
buffer.putLong((Long) value);
} else if (value instanceof Byte) {
buffer.put((Byte) value);
} else if (value instanceof Float) {
buffer.putFloat((Float) value);
} else if (value instanceof Double) {
buffer.putDouble((Double) value);
}
}
/**
* 判断是否为基本类型/包装类型
*/
private static boolean isBasicType(Class<?> clazz) {
return clazz.isPrimitive()
|| clazz == Short.class
|| clazz == Integer.class
|| clazz == Long.class
|| clazz == Byte.class
|| clazz == Float.class
|| clazz == Double.class
|| clazz == Boolean.class
|| clazz == Character.class;
}
/**
* 字节数组转大写16进制字符串(单个字节补前导0)
*/
private static String bytesToHex(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
return "";
}
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() == 1) {
sb.append('0');
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
/**
* long转字节数组(适配BYTE类型的long字段)
*/
private static byte[] longToBytes(long value) {
ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.order(BYTE_ORDER);
buffer.putLong(value);
return buffer.array();
}
// ------------------------------ 测试示例 ------------------------------
public static void main(String[] args) throws Exception {
}
}
package com.sixteen.parse.annotates;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时可用
@Target(ElementType.FIELD) // 注解只能用于字段(属性)
public @interface DependencyDef {
String value() ;
}
package com.sixteen.parse.annotates;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时可用
@Target(ElementType.FIELD) // 注解只能用于字段(属性)
public @interface DynamicsClassDef {
String classtype();
}
package com.sixteen.parse.annotates;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时可用
@Target(ElementType.FIELD) // 注解只能用于字段(属性)
public @interface FieldDef {
int length() default 1;
String type();
boolean isArray()default false;
boolean isAutoLength() default false ;
}
package com.sixteen.parse.annotates;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时可用
@Target(ElementType.FIELD) // 注解只能用于字段(属性)
public @interface NumFlag {
// 定义注解的属性
String value() default ""; // 默认值
}
package com.sixteen.parse.annotates;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时可用
@Target(ElementType.FIELD) // 注解只能用于字段(属性)
public @interface OffsetDef {
double value() default 1;
int type() default 1; //1:除法 2:乘法;
int minValidLength() default 0;
int minusValue() default 0;//减多少多少
}
package com.sixteen.parse.annotates;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时可用
@Target(ElementType.FIELD) // 注解只能用于字段(属性)
public @interface Order {
int value(); // 定义一个名为value的属性,用于存储序号
}
package com.sixteen.parse.annotates;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时可用
@Target(ElementType.FIELD) // 注解只能用于字段(属性)
public @interface RefNumFlag {
String value();
}
package com.sixteen.parse.assist_88.dto;
public class AssistMessageBodyDto<T> {
//车辆编号
private String vehicleId;
//消息标识
private long seq;
//辅助功能类别
private short astFuncType;
//命令长度
private int dataLen;
//命令数据
private T infoData;
public short getAstFuncType() {
return astFuncType;
}
public T getInfoData() {
return infoData;
}
}
package com.sixteen.parse.assist_88.dto;
import com.sixteen.parse.annotates.OffsetDef;
import com.sixteen.parse.annotates.Order;
import com.sixteen.parse.assist_88.vo.FilterInfo;
import com.sixteen.parse.assist_88.vo.TrajectoryInfo;
import java.util.List;
/****感知目标物**/
public class PerceptualDto {
//唯一编号
@Order(1)
private String uuid;
//目标编号
@Order(2)
private int objId;
//目标类型
@Order(3)
private short type;
//目标状态
@Order(4)
private short status;
//目标长度 单位:cm
@Order(5)
@OffsetDef(value = 0.01)
private int len;
//目标宽度 单位:cm
@Order(6)
@OffsetDef(value = 0.01)
private int width;
//目标高度 单位:cm
@Order(7)
@OffsetDef(value = 0.01)
private int height;
//经度
@Order(8)
@OffsetDef(value=0.0000001,minValidLength=10)
private double longitude;
//纬度
@Order(9)
@OffsetDef(value=0.00000001,minValidLength=10)
private double latitude;
//东西向距离 单位:cm
@Order(10)
@OffsetDef(value = 0.01)
private long locEast;
//南北向距离 单位:cm
@Order(11)
@OffsetDef(value = 0.01)
private long locNorth;
//位置精度等级
@Order(12)
private short posConfidence;
//高程 单位:dm
@Order(13)
@OffsetDef(value = 0.1)
private long elevation;
//高程精度
@Order(14)
private short elevConfidence;
//速度 ,单位:0.01 m/s
@Order(15)
@OffsetDef(value = 0.036)
private float speed;
//速度精度等级
@Order(16)
private short speedConfidence;
//东西向速度 cm/s
@Order(17)
@OffsetDef(value = 0.0036)
private float speedEast;
//东西向速度精 度等级
@Order(18)
private short speedEastConfidence;
//南北向速度 cm/s,
@Order(19)
@OffsetDef(value = 0.0036)
private float speedNorth;
//南北向速度精 度等级
@Order(20)
private short speedNorthConfidence;
//航向角
@Order(21)
@OffsetDef(value=0.0001,minValidLength=7)
private double heading;
//航向精度等级
@Order(22)
private short headConfidence;
//目标纵向加速 度 单位:0.01m/
@Order(23)
@OffsetDef(value = 0.036)
private float accelVert;
//目标纵向加速 度置精度等级
@Order(24)
private short accelVertConfidence;
//目标跟踪时长 单位:毫秒
@Order(25)
private long trackedTimes;
//目标历史轨迹 数量
@Order(26)
private int histLocNum;
//目标历史轨迹 列表
@Order(27)
private List<TrajectoryInfo> histLocs;
//目标预测轨迹 数量
@Order(28)
private int predLocNum;
//目标预测轨迹 列表
@Order(29)
private List<TrajectoryInfo> predLocs;
//目标所在车道编号
@Order(30)
private short laneId;
//滤波信息的类型
@Order(31)
private short filterInfoType;
//卡尔曼滤波信息
@Order(32)
private List<FilterInfo> filterInfo;
//车牌号字节数
@Order(33)
private short plateNoLen;
//车牌号
@Order(34)
private byte[] plateNo;
//车牌类型
@Order(35)
private short plateType;
//车牌颜色
@Order(36)
private short plateColor;
//车身颜色
@Order(37)
private short vehicleColor;
}
package com.sixteen.parse.assist_88.dto;
import java.util.List;
/*** CLOUD2VEH_ASTFUNC_RSM 云端下发实时感知信息 11 **/
public class PerceptualInfoDto {
//数据源
private short source;
// 感知目标个数
private int objNum;
//感知目标数据
private List<PerceptualDto> detectionData;
public List<PerceptualDto> getDetectionData() {
return detectionData;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment