Skip to content

Commit 2a77696

Browse files
WIP: Dynamic taskboard spawnin
1 parent 8c06c7a commit 2a77696

File tree

13 files changed

+788
-0
lines changed

13 files changed

+788
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
cmake_minimum_required(VERSION 3.8)
2+
project(aic_flowstate_interface)
3+
4+
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
5+
add_compile_options(-Wall -Wextra -Wpedantic)
6+
endif()
7+
8+
# find dependencies
9+
find_package(ament_cmake REQUIRED)
10+
find_package(rosidl_default_generators REQUIRED)
11+
12+
rosidl_generate_interfaces(${PROJECT_NAME}
13+
"srv/UpdateComponent.srv"
14+
)
15+
16+
ament_package()
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0"?>
2+
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
3+
<package format="3">
4+
<name>aic_flowstate_interface</name>
5+
<version>0.0.1</version>
6+
<description>The aic_flowstate_interface package provides custom ROS 2 services for the Flowstate Taskboard Spawner.</description>
7+
<maintainer email="kbalasundar@todo.todo">kbalasundar</maintainer>
8+
<license>Apache-2.0</license>
9+
10+
<buildtool_depend>ament_cmake</buildtool_depend>
11+
12+
<build_depend>rosidl_default_generators</build_depend>
13+
<build_depend>builtin_interfaces</build_depend>
14+
15+
<exec_depend>rosidl_default_runtime</exec_depend>
16+
<exec_depend>builtin_interfaces</exec_depend>
17+
18+
<member_of_group>rosidl_interface_packages</member_of_group>
19+
20+
<export>
21+
<build_type>ament_cmake</build_type>
22+
</export>
23+
</package>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Request
2+
string component_name # Key name of the rail/slot (e.g., "nic_rail_0", "lc_mount_rail_0")
3+
bool entity_present # Is the visual element active?
4+
string entity_name # Name of the instantiated entity (e.g., "lc_mount_0")
5+
6+
float64 translation # Translation shift (along the URDF defined axis)
7+
float64 roll # Orientation override
8+
float64 pitch
9+
float64 yaw
10+
11+
# Configurable Limits Override
12+
bool use_custom_limits # If true, the service will clamp to specified limits instead of system defaults
13+
float64 min_translation
14+
float64 max_translation
15+
16+
---
17+
# Response
18+
bool success # True if parsing and instantiation was successful
19+
string message # Status feedback (e.g., if clamping occurred or missing asset)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
cmake_minimum_required(VERSION 3.8)
2+
project(taskboard_spawning_service)
3+
4+
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
5+
add_compile_options(-Wall -Wextra -Wpedantic)
6+
endif()
7+
8+
# find dependencies
9+
find_package(ament_cmake REQUIRED)
10+
find_package(rclcpp REQUIRED)
11+
find_package(yaml-cpp REQUIRED)
12+
find_package(aic_flowstate_interface REQUIRED)
13+
find_package(intrinsic_sdk_cmake REQUIRED)
14+
15+
# Include directories
16+
include_directories(include)
17+
18+
# 1. Build Logic Library (Clean separation of concerns)
19+
add_library(taskboard_spawner_logic
20+
src/taskboard_spawner_logic.cc
21+
)
22+
target_link_libraries(taskboard_spawner_logic
23+
yaml-cpp
24+
)
25+
26+
# 2. Build ROS 2 Node Executable
27+
add_executable(taskboard_spawner_node
28+
src/taskboard_spawner_node.cpp
29+
)
30+
target_link_libraries(taskboard_spawner_node
31+
taskboard_spawner_logic
32+
rclcpp::rclcpp
33+
intrinsic_sdk_cmake::intrinsic_sdk_cmake
34+
${aic_flowstate_interface_TARGETS}
35+
)
36+
37+
# Install rules
38+
install(
39+
TARGETS taskboard_spawner_node
40+
RUNTIME DESTINATION lib/${PROJECT_NAME}
41+
)
42+
43+
#===============================================================================
44+
# Generate Solution Manifest
45+
intrinsic_sdk_generate_service_manifest(
46+
SERVICE_NAME taskboard_spawner_node
47+
MANIFEST taskboard_spawning_service.manifest.textproto
48+
PARAMETER_DESCRIPTOR taskboard_spawning_service.proto
49+
DEFAULT_CONFIGURATION default_config.pbtxt
50+
PROTOS_TARGET taskboard_spawning_service_protos
51+
)
52+
53+
# Force manifest generation
54+
add_dependencies(taskboard_spawner_node
55+
taskboard_spawner_node_manifest
56+
taskboard_spawner_node_default_config
57+
)
58+
59+
# Move manifest files to expected subfolder for Dockerfile.service
60+
install(FILES
61+
${CMAKE_CURRENT_BINARY_DIR}/service_manifest.binarypb
62+
${CMAKE_CURRENT_BINARY_DIR}/default_config.binarypb
63+
${CMAKE_CURRENT_BINARY_DIR}/taskboard_spawner_node_protos.desc
64+
DESTINATION share/${PROJECT_NAME}/taskboard_spawner_node
65+
)
66+
67+
ament_package()
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# proto-file: taskboard_spawning_service.proto
2+
# proto-message: TaskboardSpawningServiceConfig
3+
[type.googleapis.com/intrinsic.TaskboardSpawningServiceConfig] {
4+
# Standard YAML file path used instead of proto parameters
5+
# This is a placeholder config to satisfy Solution Manifest requirements
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2026 Intrinsic Innovation LLC
2+
// Confidential and Proprietary
3+
4+
#ifndef AIC_FLOWSTATE_SERVICES_TASKBOARD_SPAWNING_SERVICE_TASKBOARD_SPAWNER_CONSTANTS_H_
5+
#define AIC_FLOWSTATE_SERVICES_TASKBOARD_SPAWNING_SERVICE_TASKBOARD_SPAWNER_CONSTANTS_H_
6+
7+
namespace aic {
8+
namespace flowstate {
9+
10+
// Environment variables and defaults
11+
constexpr char kAssetServerDefault[] = "localhost:50051";
12+
13+
// Asset metadata
14+
constexpr char kPackageSideloaded[] = "ai.intrinsic";
15+
16+
// Topic and Service Names
17+
constexpr char kUpdateComponentService[] = "/taskboard_spawner_service/update_component";
18+
19+
// ROS Parameters
20+
constexpr char kParamConfigFile[] = "config_file_path";
21+
constexpr char kParamBaseX[] = "taskboard_base_x";
22+
constexpr char kParamBaseY[] = "taskboard_base_y";
23+
constexpr char kParamBaseZ[] = "taskboard_base_z";
24+
constexpr char kParamBaseYaw[] = "taskboard_base_yaw";
25+
26+
} // namespace flowstate
27+
} // namespace aic
28+
29+
#endif // AIC_FLOWSTATE_SERVICES_TASKBOARD_SPAWNING_SERVICE_TASKBOARD_SPAWNER_CONSTANTS_H_
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#ifndef AIC_FLOWSTATE_SERVICES_TASKBOARD_SPAWNER_LOGIC_H_
2+
#define AIC_FLOWSTATE_SERVICES_TASKBOARD_SPAWNER_LOGIC_H_
3+
4+
#include <string>
5+
#include <unordered_map>
6+
#include <vector>
7+
#include <yaml-cpp/yaml.h>
8+
9+
namespace aic {
10+
namespace flowstate {
11+
12+
/// Structure defining an entity pose (from YAML)
13+
struct EntityPose {
14+
double translation = 0.0;
15+
double roll = 0.0;
16+
double pitch = 0.0;
17+
double yaw = 0.0;
18+
};
19+
20+
/// Structure representing limit ranges
21+
struct Range {
22+
double min_val = 0.0;
23+
double max_val = 0.0;
24+
};
25+
26+
/// Structure representing a component state parsed from YAML
27+
struct ComponentConfig {
28+
bool entity_present = false;
29+
std::string entity_name;
30+
EntityPose pose;
31+
};
32+
33+
class TaskboardSpawnerLogic {
34+
public:
35+
TaskboardSpawnerLogic() = default;
36+
37+
/// Load trial configuration from a YAML file or string.
38+
bool LoadFromYaml(const std::string& yaml_file_path);
39+
bool LoadFromYamlNode(const YAML::Node& node);
40+
41+
/// Compute the relative pose for a child component based on its offsets from the base.
42+
/// \param component_name Key name of the rail component (e.g., "nic_rail_0", "lc_mount_rail_0").
43+
/// \param out_x, out_y, out_z, out_qx, out_qy, out_qz, out_qw The resulting relative pose.
44+
bool ComputeRelativePose(
45+
const std::string& component_name,
46+
double& out_x, double& out_y, double& out_z,
47+
double& out_qx, double& out_qy, double& out_qz, double& out_qw);
48+
49+
/// Check if translation is within limits for a component.
50+
bool IsWithinLimits(const std::string& component_name, double translation,
51+
double custom_min = 0.0, double custom_max = 0.0,
52+
bool use_custom_limits = false) const;
53+
54+
/// Get the component config parsed from YAML.
55+
const std::unordered_map<std::string, ComponentConfig>& GetComponents() const {
56+
return components_;
57+
}
58+
59+
private:
60+
std::unordered_map<std::string, ComponentConfig> components_;
61+
std::unordered_map<std::string, Range> standard_limits_;
62+
63+
void InitializeDefaultLimits();
64+
bool GetUrdfBaseOffset(const std::string& component_name,
65+
double& offset_x, double& offset_y, double& offset_z,
66+
double& init_roll, double& init_pitch, double& init_yaw) const;
67+
};
68+
69+
} // namespace flowstate
70+
} // namespace aic
71+
72+
#endif // AIC_FLOWSTATE_SERVICES_TASKBOARD_SPAWNER_LOGIC_H_
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2026 Intrinsic Innovation LLC
2+
// Confidential and Proprietary
3+
4+
#ifndef AIC_FLOWSTATE_SERVICES_TASKBOARD_SPAWNING_SERVICE_TASKBOARD_SPAWNER_NODE_H_
5+
#define AIC_FLOWSTATE_SERVICES_TASKBOARD_SPAWNING_SERVICE_TASKBOARD_SPAWNER_NODE_H_
6+
7+
#include "taskboard_spawning_service/taskboard_spawner_logic.h"
8+
#include <rclcpp/rclcpp.hpp>
9+
#include "aic_flowstate_interface/srv/update_component.hpp"
10+
11+
#include <grpcpp/grpcpp.h>
12+
#include "intrinsic/assets/proto/asset_deployment.grpc.pb.h"
13+
#include "intrinsic/assets/proto/installed_assets.grpc.pb.h"
14+
#include "intrinsic/world/proto/object_world_service.grpc.pb.h"
15+
#include "google/longrunning/operations.grpc.pb.h"
16+
17+
#include <memory>
18+
#include <string>
19+
20+
namespace aic {
21+
namespace flowstate {
22+
23+
class TaskboardSpawnerNode : public rclcpp::Node {
24+
public:
25+
TaskboardSpawnerNode();
26+
virtual ~TaskboardSpawnerNode() = default;
27+
28+
private:
29+
void InitGrpcStubs();
30+
void SpawnFromConfig(const std::string& config_path);
31+
bool SpawnComponent(const std::string& component_type, const std::string& instance_name,
32+
double x, double y, double z, double rx_or_qx, double ry_or_qy, double rz_or_qz,
33+
double rw = 1.0, bool use_quat = false, const std::string& parent_name = "");
34+
bool RemoveComponent(const std::string& entity_name);
35+
36+
void HandleUpdateComponent(
37+
const std::shared_ptr<aic_flowstate_interface::srv::UpdateComponent::Request> request,
38+
std::shared_ptr<aic_flowstate_interface::srv::UpdateComponent::Response> response);
39+
40+
TaskboardSpawnerLogic logic_;
41+
42+
// Grpc Stubs
43+
std::unique_ptr<intrinsic_proto::assets::AssetDeploymentService::Stub> asset_deployment_stub_;
44+
std::unique_ptr<intrinsic_proto::assets::v1::InstalledAssets::Stub> installed_assets_stub_;
45+
std::unique_ptr<intrinsic_proto::world::ObjectWorldService::Stub> object_world_stub_;
46+
std::unique_ptr<google::longrunning::Operations::Stub> operations_stub_;
47+
48+
rclcpp::Service<aic_flowstate_interface::srv::UpdateComponent>::SharedPtr update_service_;
49+
};
50+
51+
} // namespace flowstate
52+
} // namespace aic
53+
54+
#endif // AIC_FLOWSTATE_SERVICES_TASKBOARD_SPAWNING_SERVICE_TASKBOARD_SPAWNER_NODE_H_
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0"?>
2+
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
3+
<package format="3">
4+
<name>taskboard_spawning_service</name>
5+
<version>0.0.1</version>
6+
<description>The taskboard_spawning_service package provides a ROS 2 node to dynamically spawn and position context-aware visual elements from a catalog in the Flowstate workspace.</description>
7+
<maintainer email="kbalasundar@todo.todo">kbalasundar</maintainer>
8+
<license>Apache-2.0</license>
9+
10+
<buildtool_depend>ament_cmake</buildtool_depend>
11+
12+
<depend>rclcpp</depend>
13+
<depend>yaml-cpp</depend>
14+
<depend>aic_flowstate_interface</depend>
15+
16+
<export>
17+
<build_type>ament_cmake</build_type>
18+
</export>
19+
</package>

0 commit comments

Comments
 (0)