diff --git a/server/server.js b/server/server.js index f032b36..c118c65 100644 --- a/server/server.js +++ b/server/server.js @@ -254,10 +254,20 @@ app.get('/api/turtle/:id/commands', (req, res) => { commands.forEach(cmd => console.log(` - ${cmd.command}`, cmd.param ? `(${cmd.param})` : '')); } - // Clear pending commands - turtle.pendingCommands = []; - - res.json({ commands }); + // Mark commands as sent but don't clear them yet - they'll be cleared on next poll if turtle is processing + // This allows the webbridge to retry if the turtle didn't receive them + if (!turtle.lastCommandPollTime || (Date.now() - turtle.lastCommandPollTime) > 5000) { + // 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 { 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 app.get('/api/turtles', (req, res) => { res.json({ diff --git a/webbridge.lua b/webbridge.lua index b704c5a..996b522 100644 --- a/webbridge.lua +++ b/webbridge.lua @@ -320,6 +320,25 @@ local function pollCommands(turtleID) 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 if hasMonitor then drawDashboard() @@ -333,9 +352,12 @@ end addLog("Listening on channels " .. STATUS_CHANNEL .. " and " .. CHANNEL_RECEIVE, colors.lightBlue) -- 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) +-- Track which turtles we've sent commands to recently +local recentCommandSends = {} + -- Main loop local lastRefresh = os.epoch("utc") while true do @@ -346,23 +368,51 @@ while true do for turtleID, turtleData in pairs(turtles) do local commands = pollCommands(turtleID) - -- Forward commands back to turtle - for _, cmd in ipairs(commands) do - stats.commandsSent = stats.commandsSent + 1 - addLog("CMD: " .. cmd.command .. " -> Turtle #" .. turtleID, colors.yellow) + -- Only send commands if we got some + if #commands > 0 then + addLog("Received " .. #commands .. " command(s) for Turtle #" .. turtleID, colors.cyan) - local commandPacket = { - command = cmd.command, - param = cmd.param, - target = turtleID - } - - modem.transmit(COMMAND_CHANNEL, CHANNEL_RECEIVE, commandPacket) - - -- Debug: show what we're sending - if not hasMonitor then - print(" Sent to channel " .. COMMAND_CHANNEL .. ": target=" .. turtleID) + -- Forward commands back to turtle + for _, cmd in ipairs(commands) do + stats.commandsSent = stats.commandsSent + 1 + addLog(" CMD: " .. cmd.command .. " -> Turtle #" .. turtleID, colors.yellow) + + local commandPacket = { + command = cmd.command, + param = cmd.param, + 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 + + -- 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