Files
enarobot_copy/Source/EnARobot/RobotBaseActor.cpp
2025-05-15 18:14:46 +09:00

193 lines
9.5 KiB
C++

// Source/ENARobot/RobotBaseActor.cpp
#include "RobotBaseActor.h"
//#include "PythonCommServerComponent.h" // 통신 컴포넌트 헤더 포함
#include "Kismet/GameplayStatics.h" // UGameplayStatics 사용
#include "JsonUtilities.h" // FJsonObjectConverter 사용 (필요시)
// 생성자: 컴포넌트 생성 및 기본 설정
ARobotBaseActor::ARobotBaseActor()
{
// 각 프레임 업데이트 필요시 활성화 (BP에서 Tick 이벤트를 사용하면 C++ Tick은 꺼도 됨)
PrimaryActorTick.bCanEverTick = true;
// 스켈레탈 메시 컴포넌트 생성 및 RootComponent로 설정
RobotMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("RobotSkeletalMesh"));
RootComponent = RobotMesh;
// 용접 아크 파티클 시스템 컴포넌트 생성 및 메시의 소켓에 부착
WeldingArcParticle = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("WeldingArcParticle"));
// "WeldingTipSocket"은 로봇 스켈레탈 메시의 용접 팁 위치에 정의된 소켓 이름이어야 합니다.
WeldingArcParticle->SetupAttachment(RobotMesh, TEXT("WeldingTipSocket"));
WeldingArcParticle->SetVisibility(false); // 초기에는 보이지 않게 설정
WeldingArcParticle->bAutoActivate = false; // 자동으로 활성화되지 않게 설정
// (선택적) 용접 팁 메시 컴포넌트
// WeldingTipMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("WeldingTipMesh"));
// WeldingTipMesh->SetupAttachment(RobotMesh, TEXT("WeldingTipSocket"));
// 로봇 스켈레탈 메시 에셋은 이 클래스를 상속받는 블루프린트에서 설정해야 합니다.
// 예시: RobotMesh->SetSkeletalMesh(LoadMeshFromPath("..."));
}
// 게임 시작 시 호출
void ARobotBaseActor::BeginPlay()
{
Super::BeginPlay();
// PythonCommServerComponent를 찾아 자신을 등록합니다.
// 레벨에 배치된 모든 Actor를 검사하여 해당 컴포넌트를 가진 액터를 찾습니다.
UPythonCommServerComponent* CommComponent = nullptr;
TArray<AActor*> FoundActors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AActor::StaticClass(), FoundActors); // 모든 액터 검색 (비효율적일 수 있음)
// 더 나은 방법: 컴포넌트 자체에 static 함수를 만들어 싱글톤처럼 접근하거나, GameState/GameMode에 참조를 저장
// 예시에서는 간단하게 모든 액터 검색 후 컴포넌트 찾기
for (AActor* Actor : FoundActors)
{
//CommComponent = Actor->FindComponentByClass<UPythonCommServerComponent>();
if (CommComponent)
{
// 컴포넌트를 찾았으면 이 로봇 액터를 등록합니다.
//CommComponent->RegisterRobotActor(RobotID, this);
// PythonCommServer = CommComponent; // 참조 저장 (선택적)
break; // 컴포넌트는 하나만 있다고 가정
}
}
if (!CommComponent)
{
UE_LOG(LogTemp, Error, TEXT("ARobotBaseActor '%s' (ID %d): UPythonCommServerComponent not found in level! Cannot register."), *GetName(), RobotID);
}
}
// Python에서 받은 'set_robot_pose' 명령을 처리하는 함수 구현
/*void ARobotBaseActor::SetRobotPoseFromJSON(const TSharedPtr<FJsonObject>& PoseParameters)
{
if (!PoseParameters.IsValid())
{
UE_LOG(LogTemp, Warning, TEXT("SetRobotPoseFromJSON called with invalid parameters for Robot %d."), RobotID);
return;
}
// --- JSON 파라미터에서 포즈 데이터 추출 및 적용 ---
// Python에서 보낸 JSON 구조와 일치해야 합니다: {"joint_angles": [...]} 또는 {"tcp_transform": {"position": [...], "rotation": [...]}}
const TArray<TSharedPtr<FJsonValue>>* JointAnglesArray;
if (PoseParameters->TryGetArrayField(TEXT("joint_angles"), JointAnglesArray))
{
// 관절 각도 적용 (블루프린트 이벤트 호출)
TArray<float> JointAngles;
if (JointAnglesArray)
{
for (const TSharedPtr<FJsonValue>& JsonValue : *JointAnglesArray)
{
double Angle;
if (JsonValue->TryGetNumber(Angle))
{
JointAngles.Add(static_cast<float>(Angle));
}
// 유효하지 않은 값에 대한 처리 로직 추가 필요
}
}
// 블루프린트 이벤트 호출 또는 C++ 로직으로 직접 관절 회전 설정
// 관절 개수 확인 등 유효성 검사 추가 필요
if (JointAngles.Num() > 0)
{
ApplyJointAngles(JointAngles); // <-- 블루프린트 이벤트 호출
UE_LOG(LogTemp, Verbose, TEXT("Robot %d: Applied %d joint angles."), RobotID, JointAngles.Num());
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Robot %d: Received empty or invalid joint angles array."), RobotID);
}
}
const TSharedPtr<FJsonObject>* TcpTransformObject;
if (PoseParameters->TryGetObjectField(TEXT("tcp_transform"), TcpTransformObject) && TcpTransformObject && TcpTransformObject->IsValid())
{
// TCP 트랜스폼 적용 (위치 및 회전)
const TArray<TSharedPtr<FJsonValue>>* PositionArray;
const TArray<TSharedPtr<FJsonValue>>* RotationArray; // 형식은 Python과 일치해야 함
FVector Location = FVector::ZeroVector;
FRotator Rotation = FRotator::ZeroRotator; // 예시: FRotator 사용. Quaternion 사용시 FQuat.
// 위치 추출 (예시: [x, y, z] 배열)
if ((*TcpTransformObject)->TryGetArrayField(TEXT("position"), PositionArray) && PositionArray && PositionArray->Num() >= 3)
{
// UE 좌표계와 Python/로봇 좌표계 간의 변환이 필요할 수 있습니다.
// 예시: Python의 X, Y, Z가 UE의 X, Y, Z와 다를 경우
Location.X = static_cast<float>((*PositionArray)[0]->AsNumber());
Location.Y = static_cast<float>((*PositionArray)[1]->AsNumber());
Location.Z = static_cast<float>((*PositionArray)[2]->AsNumber());
// TODO: 좌표계 변환 로직 구현
} else { UE_LOG(LogTemp, Warning, TEXT("Robot %d: Missing or invalid 'position' array for TCP transform."), RobotID); }
// 회전 추출 (예시: Quaternion [x, y, z, w] 배열)
if ((*TcpTransformObject)->TryGetArrayField(TEXT("rotation"), RotationArray) && RotationArray && RotationArray->Num() >= 4)
{
FQuat QuatRotation;
QuatRotation.X = static_cast<float>((*RotationArray)[0]->AsNumber());
QuatRotation.Y = static_cast<float>((*RotationArray)[1]->AsNumber());
QuatRotation.Z = static_cast<float>((*RotationArray)[2]->AsNumber());
QuatRotation.W = static_cast<float>((*RotationArray)[3]->AsNumber());
QuatRotation.Normalize(); // 쿼터니언 정규화
Rotation = QuatRotation.Rotator(); // 쿼터니언을 FRotator로 변환 (또는 FRotator로 직접 받거나 FQuat 그대로 사용)
// TODO: 회전 형식(Euler, Quaternion) 및 축 변환 로직 구현
} // Else: 회전 데이터 없음, 기본값 사용
// 블루프린트 이벤트 호출 또는 C++ 로직으로 직접 액터/컴포넌트 트랜스폼 설정
ApplyTCPTransform(Location, Rotation); // <-- 블루프린트 이벤트 호출
UE_LOG(LogTemp, Verbose, TEXT("Robot %d: Applied TCP transform. Location: %s, Rotation: %s"), RobotID, *Location.ToString(), *Rotation.ToString());
}
// joint_angles와 tcp_transform 둘 다 없으면 경고
if (!JointAnglesArray && (!TcpTransformObject || !TcpTransformObject.IsValid()))
{
UE_LOG(LogTemp, Warning, TEXT("Robot %d: Received 'set_robot_pose' message but no valid 'joint_angles' or 'tcp_transform' parameters found."), RobotID);
}
}*/
// Python에서 받은 'welding_visual_command' 명령을 처리하는 함수 구현
/*void ARobotBaseActor::HandleWeldingVisualCommand(const TSharedPtr<FJsonObject>& CommandParameters)
{
if (!CommandParameters.IsValid())
{
UE_LOG(LogTemp, Warning, TEXT("HandleWeldingVisualCommand called with invalid parameters for Robot %d."), RobotID);
return;
}
FString CommandType;
if (!CommandParameters->TryGetStringField(TEXT("command_type"), CommandType))
{
UE_LOG(LogTemp, Warning, TEXT("Robot %d: Received welding_visual_command missing 'command_type' field."), RobotID);
return;
}
UE_LOG(LogTemp, Log, TEXT("Robot %d received welding visual command: %s"), RobotID, *CommandType);
// --- 명령 타입에 따른 처리 ---
if (CommandType == TEXT("arc_on"))
{
SetArcVisibility(true); // <-- 블루프린트 이벤트 호출
// Optional: Update arc parameters if details are provided
TSharedPtr<FJsonObject> Details = CommandParameters->GetObjectField(TEXT("details"));
if (Details.IsValid()) {
UpdateArcVisuals(Details); // <-- 블루프린트 이벤트 호출
}
}
else if (CommandType == TEXT("arc_off"))
{
SetArcVisibility(false); // <-- 블루프린트 이벤트 호출
}
// TODO: 다른 시각 명령 추가 (예: "set_bead_visibility", "update_bead_shape")
else
{
UE_LOG(LogTemp, Warning, TEXT("Robot %d: Unknown welding visual command type: %s."), RobotID, *CommandType);
}
}*/
// Blueprint Implementable Event 함수들은 블루프린트 에디터에서 오버라이드하여 구현해야 합니다.
// 예: ApplyJointAngles 구현 -> 블루프린트에서 SkeletalMeshComponent의 SetBoneRotation 또는 SetBoneTransform 사용