Fix branching logic and robot action timing

- Remove onCompleted() call after branch selection to prevent count increment
- Add proper duration estimation for speech actions (word count + emotion overhead)
- Add say_with_emotion and wave_goodbye to built-in actions
- Add identifier field to admin.ts plugin creation
This commit is contained in:
2026-03-21 20:15:39 -04:00
parent 8e647c958e
commit bbc34921b5
4 changed files with 43 additions and 6 deletions

View File

@@ -564,6 +564,7 @@ async function seedNAO6Plugin() {
const pluginData: InsertPlugin = { const pluginData: InsertPlugin = {
robotId: robotId, robotId: robotId,
identifier: "nao6-ros2",
name: "NAO6 Robot (Enhanced ROS2 Integration)", name: "NAO6 Robot (Enhanced ROS2 Integration)",
version: "2.0.0", version: "2.0.0",
description: description:

View File

@@ -536,7 +536,8 @@ export function WizardActionItem({
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
onExecute(action.id, { value, label, nextStepId }); onExecute(action.id, { value, label, nextStepId });
onCompleted(); // Don't call onCompleted() here - the branching logic in handleWizardResponse
// will handle the jump and reset completedActionsCount
}} }}
disabled={readOnly || isExecuting} disabled={readOnly || isExecuting}
> >

View File

@@ -438,8 +438,25 @@ export class WizardRosService extends EventEmitter {
this.publish(config.topic, config.messageType, msg); this.publish(config.topic, config.messageType, msg);
// Wait for action completion (simple delay for now) // Wait for action completion based on topic type
await new Promise((resolve) => setTimeout(resolve, 100)); if (config.topic === "/speech") {
// Estimate speech duration based on text content
const text =
typeof msg === "object" && msg !== null && "data" in msg
? String((msg as any).data || "")
: JSON.stringify(msg);
const wordCount = text.split(/\s+/).filter(Boolean).length;
// Emotion markup adds overhead: ~200ms per word base + emotion animation time
const emotionOverhead = 1500; // Animation prep time
const duration = emotionOverhead + Math.max(1000, wordCount * 300);
console.log(
`[WizardROS] Speech action estimated duration: ${duration}ms (${wordCount} words)`,
);
await new Promise((resolve) => setTimeout(resolve, duration));
} else {
// Short delay for non-speech actions
await new Promise((resolve) => setTimeout(resolve, 500));
}
} }
/** /**
@@ -452,13 +469,25 @@ export class WizardRosService extends EventEmitter {
): Promise<void> { ): Promise<void> {
switch (actionId) { switch (actionId) {
case "say_text": case "say_text":
case "say_with_emotion":
const text = String(parameters.text || "Hello"); const text = String(parameters.text || "Hello");
this.publish("/speech", "std_msgs/String", { data: text }); this.publish("/speech", "std_msgs/String", { data: text });
const wordCount = text.split(/\s+/).length; const wordCount = text.split(/\s+/).filter(Boolean).length;
const duration = Math.max(800, wordCount * 250 + 500); const emotion = String(parameters.emotion || "neutral");
const emotionOverhead = 1500;
const duration = emotionOverhead + Math.max(1000, wordCount * 300);
console.log(
`[WizardROS] Speech action (${actionId}) estimated: ${duration}ms`,
);
await new Promise((resolve) => setTimeout(resolve, duration)); await new Promise((resolve) => setTimeout(resolve, duration));
break; break;
case "wave_goodbye":
const waveText = String(parameters.text || "Goodbye");
this.publish("/speech", "std_msgs/String", { data: waveText });
await new Promise((resolve) => setTimeout(resolve, 3000));
break;
case "walk_forward": case "walk_forward":
this.publish("/cmd_vel", "geometry_msgs/Twist", { this.publish("/cmd_vel", "geometry_msgs/Twist", {
linear: { x: Number(parameters.speed) || 0.1, y: 0, z: 0 }, linear: { x: Number(parameters.speed) || 0.1, y: 0, z: 0 },

View File

@@ -927,11 +927,17 @@ export const adminRouter = createTRPCRouter({
if (existingPlugin.length === 0) { if (existingPlugin.length === 0) {
// Create new plugin // Create new plugin
const pluginName = pluginData.name ?? "unknown";
const slugIdentifier = pluginName
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-|-$/g, "");
const newPlugin = await db const newPlugin = await db
.insert(plugins) .insert(plugins)
.values({ .values({
identifier: slugIdentifier,
robotId, robotId,
name: pluginData.name ?? "", name: pluginName,
version: pluginData.version ?? "", version: pluginData.version ?? "",
description: pluginData.description, description: pluginData.description,
author: author: