# XLOC Manual Control API Guide Now you have **FULL MANUAL CONTROL** over XLOC SLAM operations! Control via: 1. C# Service Methods 2. REST API Endpoints 3. SignalR Hub Methods --- ## 1. C# Service Methods (Direct) Inject `XlocIntegrationService` into your service: ```csharp public class MyNavigationService { private readonly XlocIntegrationService _xloc; public MyNavigationService(XlocIntegrationService xloc) { _xloc = xloc; } public void StartLocalizationWithMap() { // Activate map if (_xloc.ActivateMap("/maps/factory_floor.pbstream")) { // Start localization _xloc.StartLocalization(); } } public void BeginMapping() { _xloc.StartMapping(); } public void SaveAndStopMapping() { _xloc.StopMapping("/maps/new_map.pbstream"); } } ``` --- ## 2. REST API Endpoints ### Activate Map ```bash curl -X POST "https://localhost:7002/api/xloc/activate-map?mapPath=/maps/factory.pbstream" ``` Response: ```json { "success": true, "message": "Map activated" } ``` ### Start Mapping ```bash dotnet build # Restart app pkill -f dotnet ./run-quiet.sh # Test mapping curl -k -X POST https://127.0.0.1:7002/api/motion/ps5/enable curl -k -X POST https://localhost:7002/api/xloc/mapping/start # ... di chuyển robot ... ``` ### Stop Mapping ```bash curl -k -X POST https://localhost:7002/api/xloc/mapping/stop \ -H "Content-Type: application/json" \ -d '{"map_file_path": "test14"}' ``` ### Active Map ```bash curl -k -X POST https://localhost:7002/api/xloc/map/activate \ -H "Content-Type: application/json" \ -d '{"map_file_path": "test10"}' ``` ### Start Localization ```bash curl -k -X POST https://localhost:7002/api/xloc/localization/start ``` ### Stop Localization ```bash curl -k -X POST https://localhost:7002/api/xloc/localization/stop ``` ### Reset SLAM State ```bash # Reset SLAM error state (automatically called before start mapping/localization) # Useful if you manually need to clear previous trajectory state curl -k -X POST https://localhost:7002/api/xloc/slam/reset ``` **Note:** `StartMapping()` and `StartLocalization()` now automatically call reset before starting, so you typically don't need to call this manually. ### Stop Mapping & Save **Option 1: Manual (will crash, but map is saved)** ```bash # Save with timestamp MAP_NAME="map_$(date +%Y%m%d_%H%M%S).pbstream" curl -k -X POST "https://localhost:7002/api/xloc/stop-mapping?savePath=/home/robotics/sonvh/RobotNet10/srcs/RobotNet10/RobotApp/RobotNet10.RobotApp/Xloc/map/$MAP_NAME" # Or save with custom name (MUST include .pbstream extension!) curl -k -X POST "https://localhost:7002/api/xloc/stop-mapping?savePath=/home/robotics/sonvh/RobotNet10/srcs/RobotNet10/RobotApp/RobotNet10.RobotApp/Xloc/map/my_map.pbstream" cd Xloc ./map-and-save.sh my_office_map.pbstream # Note: Application will crash due to XLOC Cairo bug, but map is saved successfully # Restart with: ./run-quiet.sh ``` **Option 2: Automated script (recommended)** ```bash # Use automated script that handles crash and restart cd Xloc ./map-and-save.sh my_map.pbstream # Script will: # 1. Wait for you to drive robot # 2. Save map on ENTER # 3. Handle crash gracefully # 4. Auto-restart application ``` ### Get Current Pose ```bash curl "https://localhost:7002/api/xloc/pose" ``` Response: ```json { "x": 1.234, "y": 5.678, "yaw": 1.57, "yawDegrees": 90.0 } ``` --- ## 3. SignalR Hub Methods ### TypeScript/JavaScript Client ```typescript import * as signalR from "@microsoft/signalr"; const connection = new signalR.HubConnectionBuilder() .withUrl("https://localhost:7002/hubs/xloc/pose") .build(); // Subscribe to pose updates (realtime streaming) connection.on("ReceivePose", (pose) => { console.log(`Position: (${pose.x}, ${pose.y}), Heading: ${pose.yawDegrees}°`); }); await connection.start(); // Control SLAM manually async function startLocalization() { const success = await connection.invoke("StartLocalization"); console.log("Localization started:", success); } async function activateMap(mapPath: string) { const success = await connection.invoke("ActivateMap", mapPath); console.log("Map activated:", success); } async function startMapping() { const success = await connection.invoke("StartMapping"); console.log("Mapping started:", success); } async function stopMapping(savePath: string) { const success = await connection.invoke("StopMapping", savePath); console.log("Map saved:", success); } async function getCurrentPose() { const pose = await connection.invoke("GetCurrentPose2D"); console.log("Current pose:", pose); // { x: 1.234, y: 5.678, yaw: 1.57, yawDegrees: 90.0 } } ``` ### React Example ```tsx import { HubConnectionBuilder } from '@microsoft/signalr'; import { useState, useEffect } from 'react'; export function XlocControl() { const [connection, setConnection] = useState(null); const [pose, setPose] = useState(null); useEffect(() => { const conn = new HubConnectionBuilder() .withUrl("https://localhost:7002/hubs/xloc/pose") .build(); conn.on("ReceivePose", (data) => { setPose(data); }); conn.start(); setConnection(conn); return () => conn.stop(); }, []); const handleStartLocalization = async () => { const result = await connection.invoke("StartLocalization"); console.log("Started:", result); }; const handleStartMapping = async () => { const result = await connection.invoke("StartMapping"); console.log("Mapping started:", result); }; return (
X: {pose.x.toFixed(2)}m
Y: {pose.y.toFixed(2)}m
Heading: {pose.yawDegrees.toFixed(1)}°