| 2536 | // ============================================================================= |
| 2537 | |
| 2538 | TSharedPtr<FJsonObject> FURLabRpcDispatcher::HandleSetTwist(const TSharedPtr<FJsonObject>& Req) |
| 2539 | { |
| 2540 | AAMjManager* Mgr = OwnerMgr.Get(); |
| 2541 | if (!Mgr) |
| 2542 | return MakeError(TEXT("not_ready"), TEXT("Manager missing")); |
| 2543 | |
| 2544 | FString ArtName; |
| 2545 | if (!Req->TryGetStringField(TEXT("articulation"), ArtName)) |
| 2546 | return MakeError(TEXT("missing_field"), TEXT("set_twist requires 'articulation'")); |
| 2547 | |
| 2548 | AMjArticulation* Art = Mgr->GetArticulation(ArtName); |
| 2549 | if (!Art) |
| 2550 | return MakeError(TEXT("unknown_articulation"), ArtName); |
| 2551 | |
| 2552 | UMjTwistController* TC = Art->FindComponentByClass<UMjTwistController>(); |
| 2553 | if (!TC) |
| 2554 | return MakeError(TEXT("no_twist_controller"), |
| 2555 | FString::Printf(TEXT("Articulation '%s' has no UMjTwistController"), *ArtName)); |
| 2556 | |
| 2557 | // Wire format mirrors how the bridge already reads twist: linear is |
| 2558 | // (vx, vy, _) m/s, angular is (_, _, yaw_rate) rad/s. Tuple slots |
| 2559 | // beyond the ones used are accepted but ignored. |
| 2560 | auto ReadAxis = [](const TArray<TSharedPtr<FJsonValue>>* Arr, int32 Idx, float& Out) { |
| 2561 | if (Arr && Arr->IsValidIndex(Idx)) |
| 2562 | Out = (float)(*Arr)[Idx]->AsNumber(); |
| 2563 | }; |
| 2564 | |
| 2565 | float Vx = 0.f, Vy = 0.f, YawRate = 0.f; |
| 2566 | const TArray<TSharedPtr<FJsonValue>>* LinArr = nullptr; |
| 2567 | const TArray<TSharedPtr<FJsonValue>>* AngArr = nullptr; |
| 2568 | Req->TryGetArrayField(TEXT("linear"), LinArr); |
| 2569 | Req->TryGetArrayField(TEXT("angular"), AngArr); |
| 2570 | ReadAxis(LinArr, 0, Vx); |
| 2571 | ReadAxis(LinArr, 1, Vy); |
| 2572 | ReadAxis(AngArr, 2, YawRate); |
| 2573 | |
| 2574 | TC->SetTwist(Vx, Vy, YawRate); |
| 2575 | |
| 2576 | TSharedPtr<FJsonObject> Reply = MakeShared<FJsonObject>(); |
| 2577 | Reply->SetStringField(TEXT("op"), TEXT("set_twist_ok")); |
| 2578 | Reply->SetStringField(TEXT("articulation"), ArtName); |
| 2579 | { |
| 2580 | TArray<TSharedPtr<FJsonValue>> L; |
| 2581 | L.Add(MakeShared<FJsonValueNumber>(Vx)); |
| 2582 | L.Add(MakeShared<FJsonValueNumber>(Vy)); |
| 2583 | L.Add(MakeShared<FJsonValueNumber>(0.0)); |
| 2584 | Reply->SetArrayField(TEXT("linear"), L); |
| 2585 | TArray<TSharedPtr<FJsonValue>> A; |
| 2586 | A.Add(MakeShared<FJsonValueNumber>(0.0)); |
| 2587 | A.Add(MakeShared<FJsonValueNumber>(0.0)); |
| 2588 | A.Add(MakeShared<FJsonValueNumber>(YawRate)); |
| 2589 | Reply->SetArrayField(TEXT("angular"), A); |
| 2590 | } |
| 2591 | return Reply; |
| 2592 | } |
| 2593 | |
| 2594 | // ============================================================================= |
| 2595 | // set_qpos — manager-required runtime write to a single articulation's qpos. |
nothing calls this directly
no test coverage detected