feat: Implement command acknowledgment and improve command polling reliability for turtles
This commit is contained in:
@@ -254,10 +254,20 @@ app.get('/api/turtle/:id/commands', (req, res) => {
|
|||||||
commands.forEach(cmd => console.log(` - ${cmd.command}`, cmd.param ? `(${cmd.param})` : ''));
|
commands.forEach(cmd => console.log(` - ${cmd.command}`, cmd.param ? `(${cmd.param})` : ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear pending commands
|
// Mark commands as sent but don't clear them yet - they'll be cleared on next poll if turtle is processing
|
||||||
turtle.pendingCommands = [];
|
// This allows the webbridge to retry if the turtle didn't receive them
|
||||||
|
if (!turtle.lastCommandPollTime || (Date.now() - turtle.lastCommandPollTime) > 5000) {
|
||||||
res.json({ commands });
|
// First poll or more than 5 seconds since last poll - send commands
|
||||||
|
turtle.lastCommandPollTime = Date.now();
|
||||||
|
res.json({ commands });
|
||||||
|
} else {
|
||||||
|
// Recent poll - assume previous commands were received, clear them
|
||||||
|
if (commands.length > 0) {
|
||||||
|
console.log(`✅ Clearing ${commands.length} command(s) for turtle ${turtleID} (acknowledged)`);
|
||||||
|
turtle.pendingCommands = [];
|
||||||
|
}
|
||||||
|
res.json({ commands: [] });
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
res.json({ commands: [] });
|
res.json({ commands: [] });
|
||||||
}
|
}
|
||||||
@@ -267,6 +277,30 @@ app.get('/api/turtle/:id/commands', (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Acknowledge commands were delivered (clears them)
|
||||||
|
app.post('/api/turtle/:id/commands/ack', (req, res) => {
|
||||||
|
try {
|
||||||
|
const turtleID = parseInt(req.params.id);
|
||||||
|
|
||||||
|
if (turtleData.has(turtleID)) {
|
||||||
|
const turtle = turtleData.get(turtleID);
|
||||||
|
const clearedCount = (turtle.pendingCommands || []).length;
|
||||||
|
|
||||||
|
if (clearedCount > 0) {
|
||||||
|
console.log(`✅ Turtle ${turtleID} acknowledged ${clearedCount} command(s)`);
|
||||||
|
turtle.pendingCommands = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({ success: true, cleared: clearedCount });
|
||||||
|
} else {
|
||||||
|
res.status(404).json({ error: 'Turtle not found' });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Error acknowledging commands:', error);
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Get all turtles
|
// Get all turtles
|
||||||
app.get('/api/turtles', (req, res) => {
|
app.get('/api/turtles', (req, res) => {
|
||||||
res.json({
|
res.json({
|
||||||
|
|||||||
@@ -320,6 +320,25 @@ local function pollCommands(turtleID)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Function to acknowledge commands were sent
|
||||||
|
local function acknowledgeCommands(turtleID)
|
||||||
|
local success = pcall(function()
|
||||||
|
local response = http.post(
|
||||||
|
SERVER_URL .. "/api/turtle/" .. turtleID .. "/commands/ack",
|
||||||
|
"{}",
|
||||||
|
{["Content-Type"] = "application/json"}
|
||||||
|
)
|
||||||
|
|
||||||
|
if response then
|
||||||
|
response.close()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end)
|
||||||
|
|
||||||
|
return success
|
||||||
|
end
|
||||||
|
|
||||||
-- Initial setup
|
-- Initial setup
|
||||||
if hasMonitor then
|
if hasMonitor then
|
||||||
drawDashboard()
|
drawDashboard()
|
||||||
@@ -333,9 +352,12 @@ end
|
|||||||
addLog("Listening on channels " .. STATUS_CHANNEL .. " and " .. CHANNEL_RECEIVE, colors.lightBlue)
|
addLog("Listening on channels " .. STATUS_CHANNEL .. " and " .. CHANNEL_RECEIVE, colors.lightBlue)
|
||||||
|
|
||||||
-- Start polling timer
|
-- Start polling timer
|
||||||
local POLL_INTERVAL = 1 -- Poll every 1 second
|
local POLL_INTERVAL = 2 -- Poll every 2 seconds (reduced frequency for better reliability)
|
||||||
os.startTimer(POLL_INTERVAL)
|
os.startTimer(POLL_INTERVAL)
|
||||||
|
|
||||||
|
-- Track which turtles we've sent commands to recently
|
||||||
|
local recentCommandSends = {}
|
||||||
|
|
||||||
-- Main loop
|
-- Main loop
|
||||||
local lastRefresh = os.epoch("utc")
|
local lastRefresh = os.epoch("utc")
|
||||||
while true do
|
while true do
|
||||||
@@ -346,23 +368,51 @@ while true do
|
|||||||
for turtleID, turtleData in pairs(turtles) do
|
for turtleID, turtleData in pairs(turtles) do
|
||||||
local commands = pollCommands(turtleID)
|
local commands = pollCommands(turtleID)
|
||||||
|
|
||||||
-- Forward commands back to turtle
|
-- Only send commands if we got some
|
||||||
for _, cmd in ipairs(commands) do
|
if #commands > 0 then
|
||||||
stats.commandsSent = stats.commandsSent + 1
|
addLog("Received " .. #commands .. " command(s) for Turtle #" .. turtleID, colors.cyan)
|
||||||
addLog("CMD: " .. cmd.command .. " -> Turtle #" .. turtleID, colors.yellow)
|
|
||||||
|
|
||||||
local commandPacket = {
|
-- Forward commands back to turtle
|
||||||
command = cmd.command,
|
for _, cmd in ipairs(commands) do
|
||||||
param = cmd.param,
|
stats.commandsSent = stats.commandsSent + 1
|
||||||
target = turtleID
|
addLog(" CMD: " .. cmd.command .. " -> Turtle #" .. turtleID, colors.yellow)
|
||||||
}
|
|
||||||
|
local commandPacket = {
|
||||||
modem.transmit(COMMAND_CHANNEL, CHANNEL_RECEIVE, commandPacket)
|
command = cmd.command,
|
||||||
|
param = cmd.param,
|
||||||
-- Debug: show what we're sending
|
target = turtleID
|
||||||
if not hasMonitor then
|
}
|
||||||
print(" Sent to channel " .. COMMAND_CHANNEL .. ": target=" .. turtleID)
|
|
||||||
|
-- Send command multiple times for reliability
|
||||||
|
for i = 1, 3 do
|
||||||
|
modem.transmit(COMMAND_CHANNEL, CHANNEL_RECEIVE, commandPacket)
|
||||||
|
os.sleep(0.05) -- Small delay between retransmissions
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Debug: show what we're sending
|
||||||
|
if not hasMonitor then
|
||||||
|
print(" Sent to channel " .. COMMAND_CHANNEL .. ": target=" .. turtleID)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Acknowledge that we sent the commands
|
||||||
|
os.sleep(0.5) -- Give turtle time to receive
|
||||||
|
if acknowledgeCommands(turtleID) then
|
||||||
|
addLog(" ACK: Commands acknowledged", colors.lime)
|
||||||
|
else
|
||||||
|
addLog(" WARN: Failed to acknowledge", colors.orange)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Mark that we sent commands to this turtle
|
||||||
|
recentCommandSends[turtleID] = os.epoch("utc")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Clean up old command send timestamps (older than 10 seconds)
|
||||||
|
local now = os.epoch("utc")
|
||||||
|
for turtleID, timestamp in pairs(recentCommandSends) do
|
||||||
|
if now - timestamp > 10000 then
|
||||||
|
recentCommandSends[turtleID] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user