0119

好的,明白了!根据你的确认,我来提供完整的实现代码。

完整修改方案

一、UdpSocketServer.h 修改

在文件中添加以下内容:

1. 添加委托声明(在现有委托声明区域,建议放在 FOnMissileGroupDataReceived 之后)

DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(
    FOnLoiteringMunitionGroupDataReceived, 
    const TArray<uint8>&, MunitionIds, 
    const TArray<uint8>&, MunitionTypes, 
    const TArray<uint8>&, MunitionStates, 
    const TArray<FVector>&, MunitionPositions,
    const TArray<FRotator>&, MunitionRotations
);

2. 添加委托实例(在public区域,建议放在 OnMissileGroupDataReceived 之后)

UPROPERTY(BlueprintAssignable, Category = "UDP Loitering Munition Delegate")
FOnLoiteringMunitionGroupDataReceived OnLoiteringMunitionGroupDataReceived;

3. 添加解析函数声明(在 ParseMissileGroupData 之后)

void ParseLoiteringMunitionGroupData(const TArray<uint8>& Data, FString& OutStr);

二、UdpSocketServer.cpp 修改

1. 在 DataRecv 函数的 switch 语句中添加 case 5

找到 DataRecv 函数中的 switch 语句,在 case 4 之后添加:

case 5:
{
    FString DebugStr;
    ParseLoiteringMunitionGroupData(ReceivedData, DebugStr);
    str = DebugStr;
    success = true;
    break;
}

2. 实现 ParseLoiteringMunitionGroupData 函数

在文件末尾(建议放在 ParseMissileGroupData 函数之后)添加:

void UUdpSocketServer::ParseLoiteringMunitionGroupData(const TArray<uint8>& Data, FString& OutStr)
{
    int32 Offset = 1;
    uint8 MunitionCount = Data[Offset];
    Offset += 1;

    const int32 SingleMunitionDataSize = 51;  // 1(ID) + 1(Type) + 1(State) + 24(Position) + 24(Rotation)
    
    if ((Data.Num() - Offset) < (MunitionCount * SingleMunitionDataSize))
    {
        UE_LOG(LogTemp, Warning, TEXT("Insufficient data for loitering munition group. munition count: %d, hope length: %d, fact length: %d"),
            MunitionCount, Offset + MunitionCount * SingleMunitionDataSize, Data.Num());
        OutStr = TEXT("Error: Insufficient data for loitering munition group");
        return;
    }

    TArray<uint8> MunitionIds;
    TArray<uint8> MunitionTypes;
    TArray<uint8> MunitionStates;
    TArray<FVector> MunitionPositions;
    TArray<FRotator> MunitionRotations;

    FString GroupStr = TEXT("Loitering Munition Group: ");

    for (int32 i = 0; i < MunitionCount; ++i)
    {
        uint8 MunitionId = Data[Offset];
        uint8 MunitionType = Data[Offset + 1];
        uint8 MunitionState = Data[Offset + 2];

        // 解析位置
        double X = 0, Y = 0, Z = 0;
        FMemory::Memcpy(&X, &Data[Offset + 3], sizeof(double));
        FMemory::Memcpy(&Y, &Data[Offset + 11], sizeof(double));
        FMemory::Memcpy(&Z, &Data[Offset + 19], sizeof(double));

        // 解析姿态
        double Yaw = 0, Pitch = 0, Roll = 0;
        FMemory::Memcpy(&Yaw, &Data[Offset + 27], sizeof(double));
        FMemory::Memcpy(&Pitch, &Data[Offset + 35], sizeof(double));
        FMemory::Memcpy(&Roll, &Data[Offset + 43], sizeof(double));

        FVector Position(static_cast<float>(X), static_cast<float>(Y), static_cast<float>(Z));
        FRotator Rotation(static_cast<float>(Pitch), static_cast<float>(Yaw), static_cast<float>(Roll));

        GroupStr += FString::Printf(TEXT("[ID: %d, Type: %d, State: %d, Pos: %s, Rot: %s] "),
            MunitionId, MunitionType, MunitionState, *Position.ToString(), *Rotation.ToString());

        Position = ConvertUEOffset(Position);

        if (MunitionId > 0)  // 0 为无效巡飞弹
        {
            MunitionIds.Add(MunitionId);
            MunitionTypes.Add(MunitionType);
            MunitionStates.Add(MunitionState);
            MunitionPositions.Add(Position);
            MunitionRotations.Add(Rotation);
        }

        Offset += SingleMunitionDataSize;
    }

    OutStr = GroupStr;
    if (bIsLogReceivedPackage)
        UE_LOG(LogTemp, Warning, TEXT("the data parse loitering munition group is: %s"), *OutStr);

    // 广播委托
    OnLoiteringMunitionGroupDataReceived.Broadcast(
        MunitionIds, 
        MunitionTypes, 
        MunitionStates, 
        MunitionPositions, 
        MunitionRotations
    );
}

三、Python 测试脚本

创建一个测试脚本来验证功能:

import socket
import struct
import time

def send_loitering_munition_group(sock, target_ip, target_port, munitions_data):
    """
    发送巡飞弹组数据
    munitions_data: list of dict, 每个dict包含 id, type, state, position(x,y,z), rotation(yaw,pitch,roll)
    """
    message_type = 5
    munition_count = len(munitions_data)
    
    # 构建数据包
    packet = struct.pack('BB', message_type, munition_count)
    
    for munition in munitions_data:
        # 基本信息: ID, Type, State
        packet += struct.pack('BBB', 
                            munition['id'], 
                            munition['type'], 
                            munition['state'])
        
        # 位置: X, Y, Z (double)
        packet += struct.pack('ddd', 
                            munition['position'][0],
                            munition['position'][1],
                            munition['position'][2])
        
        # 姿态: Yaw, Pitch, Roll (double)
        packet += struct.pack('ddd',
                            munition['rotation'][0],  # Yaw
                            munition['rotation'][1],  # Pitch
                            munition['rotation'][2])  # Roll
    
    sock.sendto(packet, (target_ip, target_port))
    print(f"发送巡飞弹组数据: {munition_count} 个巡飞弹")
    print(f"数据包大小: {len(packet)} 字节")

# 测试代码
if __name__ == "__main__":
    # 配置
    TARGET_IP = "127.0.0.1"  # 修改为你的目标IP
    TARGET_PORT = 8888        # 修改为你的目标端口
    
    # 创建UDP socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    # 测试数据:3个巡飞弹
    test_munitions = [
        {
            'id': 1,
            'type': 1,
            'state': 0,
            'position': [100.0, 200.0, 50.0],
            'rotation': [45.0, 10.0, 5.0]  # Yaw, Pitch, Roll
        },
        {
            'id': 2,
            'type': 2,
            'state': 1,
            'position': [150.0, 250.0, 60.0],
            'rotation': [90.0, 15.0, -5.0]
        },
        {
            'id': 3,
            'type': 1,
            'state': 0,
            'position': [200.0, 300.0, 70.0],
            'rotation': [135.0, 20.0, 0.0]
        }
    ]
    
    try:
        while True:
            send_loitering_munition_group(sock, TARGET_IP, TARGET_PORT, test_munitions)
            
            # 更新位置和姿态(模拟移动)
            for munition in test_munitions:
                munition['position'][0] += 1.0
                munition['position'][1] += 0.5
                munition['rotation'][0] = (munition['rotation'][0] + 1) % 360
            
            time.sleep(0.1)  # 100ms发送一次
            
    except KeyboardInterrupt:
        print("\n停止发送")
    finally:
        sock.close()

四、修改总结

修改的文件:

  1. UdpSocketServer.h - 添加委托和函数声明
  2. UdpSocketServer.cpp - 添加解析逻辑

关键点:

  • ✅ 复用 NumMissilesPerPacket 配置
  • ✅ 使用消息类型 5
  • ✅ 数据包格式:1(消息类型) + 1(数量) + N×51(每个巡飞弹数据)
  • ✅ 仅实现 Group 版本
  • ✅ 包含位置和姿态数据
  • ✅ 支持坐标转换 ConvertUEOffset

数据包结构:

[消息类型:1字节] [数量:1字节] [巡飞弹1数据:51字节] [巡飞弹2数据:51字节] ...

每个巡飞弹数据(51字节):
- ID: 1字节
- Type: 1字节  
- State: 1字节
- Position X: 8字节(double)
- Position Y: 8字节(double)
- Position Z: 8字节(double)
- Yaw: 8字节(double)
- Pitch: 8字节(double)
- Roll: 8字节(double)

这样修改后,你就可以在蓝图中订阅 OnLoiteringMunitionGroupDataReceived 委托来接收和处理巡飞弹数据了!

posted @ 2026-01-19 20:05  灰色耳机  阅读(5)  评论(0)    收藏  举报