Major re-write of explore program with remote control for netNav

This commit is contained in:
blunty666 2016-12-10 21:55:02 +00:00
parent 5ba89ff6c0
commit 49a0f95f7b
2 changed files with 387 additions and 20 deletions

View File

@ -1,9 +1,28 @@
local tArgs = {...}
-- CONSTANTS
local REDNET_PROTOCOL = "NET_NAV:CONTROL"
-- VARIABLES
local minX, minY, minZ
local maxX, maxY, maxZ
local mapName
local states
local state, arguments
local idle
local startX, startY, startZ
-- CHECK MAP NAME
mapName = tArgs[1]
if type(mapName) ~= "string" then
printError("mapName must be string")
return
end
-- FIND AREA BOUNDARIES
local minX, minY, minZ = unpack(tArgs, 2, 4)
minX, minY, minZ = unpack(tArgs, 2, 4)
minX, minY, minZ = tonumber(minX), tonumber(minY), tonumber(minZ)
local maxX, maxY, maxZ = unpack(tArgs, 5, 7)
maxX, maxY, maxZ = unpack(tArgs, 5, 7)
maxX, maxY, maxZ = tonumber(maxX), tonumber(maxY), tonumber(maxZ)
local function isInteger(var)
return type(var) == "number" and math.floor(var) == var
@ -37,36 +56,82 @@ if not rednet.isOpen() then
end
-- SET NETNAV MAP
local mapName = tArgs[1]
if type(mapName) ~= "string" then
printError("mapName must be string")
return
end
netNav.setMap(mapName, 15)
local exit = false
local returning = false
local function main()
local startX, startY, startZ = gps.locate(1)
while turtle.getFuelLevel() > 0 and not exit do
-- SET UP STATES
local states = {
EXPLORE = function()
local x = math.random(minX, maxX)
local y = math.random(minY, maxY)
local z = math.random(minZ, maxZ)
netNav.goto(x, y, z)
end,
RETURN = function()
netNav.goto(startX, startY, startZ)
state = "IDLE"
end,
FOLLOW = function(xPos, yPos, zPos)
netNav.goto(xPos, yPos, zPos)
state = "IDLE"
end,
IDLE = function()
idle = true
while idle do
os.pullEvent()
end
end,
}
-- FIND START POSITION
startX, startY, startZ = gps.locate(1)
-- check coords valid
-- STATUS UPDATE FUNCTION
local function sendStatus(senderID)
local status = {
mapName,
senderID,
"PONG",
{netNav.getPosition()},
state,
arguments,
}
if senderID == -1 then
rednet.broadcast(status, REDNET_PROTOCOL)
else
rednet.send(senderID, status, REDNET_PROTOCOL)
end
returning = true
netNav.goto(startX, startY, startZ)
end
-- DEFINE CONTROL ROUTINE
local function control()
while true do
local senderID, message = rednet.receive("explore:return_to_base")
if not exit and not returning then
netNav.stop()
exit = true
local senderID, message = rednet.receive(REDNET_PROTOCOL)
if type(message) == "table" and message[1] == mapName then
local sentTo = message[2]
if sentTo == os.computerID() or sentTo == -1 then
local request = message[3]
if request == "PING" then
sendStatus(senderID)
elseif states[request] then
state, arguments = request, message[4]
netNav.stop()
idle = false
end
end
end
end
end
parallel.waitForAny(main, control)
-- DEFINE MAIN ROUTINE
local function main()
state, arguments = "EXPLORE", {}
while true do
sendStatus(-1)
if states[state] then
states[state](unpack(arguments))
end
end
end
parallel.waitForAny(control, main)

302
netNav/remoteControl.lua Normal file
View File

@ -0,0 +1,302 @@
local tArgs = {...}
-- CONSTANTS
local REDNET_PROTOCOL = "NET_NAV:CONTROL"
-- VARIABLES
local mapName
local listWidth, listHeight
local buttonInstance
local turtleList
local selectedTurtle
local setSelectedTurtle
local turtleData
local guiOffset
local mainTerm
-- CHECK MAP NAME
mapName = tArgs[1]
if type(mapName) ~= "string" then
printError("mapName must be string")
return
end
-- LOAD BUTTON_HANDLER API
if not buttonHandler then
if not os.loadAPI("buttonHandler") then
error("could not load buttonHandler API")
end
end
-- OPEN REDNET
for _, side in ipairs(redstone.getSides()) do
if peripheral.getType(side) == "modem" then
rednet.open(side)
end
end
if not rednet.isOpen() then
printError("Could not open rednet")
return
end
-- DEFINE REDNET METHODS
local function newPacket(senderID, request, arguments)
return {
mapName,
senderID,
request,
arguments,
}
end
local function sendPacket(packet)
if packet[2] == -1 then
rednet.broadcast(packet, REDNET_PROTOCOL)
else
rednet.send(packet[2], packet, REDNET_PROTOCOL)
end
end
local function sendPingPacket(sendTo)
sendPacket(newPacket(sendTo, "PING"))
end
local function sendExplorePacket(sendTo)
sendPacket(newPacket(sendTo, "EXPLORE", {}))
end
local function sendReturnPacket(sendTo)
sendPacket(newPacket(sendTo, "RETURN", {}))
end
local function sendFollowPacket(sendTo, position)
sendPacket(newPacket(sendTo, "FOLLOW", position))
end
local function sendIdlePacket(sendTo)
sendPacket(newPacket(sendTo, "IDLE", {}))
end
-- SET UP GUI
local function createListValue(turtleID, status)
local value = turtleID..string.rep(" ", math.max(0, 4 - #tostring(turtleID)))
value = value..string.rep(" ", math.max(0, 8 - #status))..status
return value
end
do
term.setBackgroundColour(colours.cyan)
term.setTextColour(colours.white)
term.clear()
local width, height = term.getSize()
guiOffset = math.ceil((width - 26)/2)
local win = window.create(term.current(), guiOffset + 1, 1, 26, height, true)
win.setBackgroundColour(colours.grey)
win.clear()
mainTerm = term.redirect(win)
end
buttonInstance = buttonHandler.new(term.current())
local width, height = term.getSize()
paintutils.drawLine(13, 1, 13, height, colours.cyan)
paintutils.drawLine(14, height - 5, width, height - 5, colours.cyan)
paintutils.drawLine(14, 6, width, 6, colours.cyan)
-- SET UP TURTLE LIST SECTION
local function turtleListHandler(value)
if value then
local turtleID = tonumber(value:match("^%d+"))
local data = turtleData[turtleID] or {}
setSelectedTurtle(turtleID, unpack(data))
end
end
turtleData = {}
turtleList = buttonInstance:AddList("turtle", {}, turtleListHandler, 1, 1, 12, height - 1)
local function allPing()
buttonInstance:Flash("ALL_PING")
turtleData = {}
turtleList:SetValues({})
sendPingPacket(-1)
end
buttonInstance:Add("ALL_PING", allPing, 1, height, 12, 1, " Refresh ", colours.red)
-- SET UP INDIVIDUAL TURTLE CONTROL SECTION
term.setCursorPos(14, 1)
term.setBackgroundColour(colours.red)
term.setTextColour(colours.white)
term.write(" Control: nil")
local function selectedExplore()
if selectedTurtle then
buttonInstance:Flash("EXPLORE")
sendExplorePacket(selectedTurtle)
end
end
buttonInstance:Add("EXPLORE", selectedExplore, 14, 2, 13, 1, " Explore ", colours.grey)
local function selectedReturn()
if selectedTurtle then
buttonInstance:Flash("RETURN")
sendReturnPacket(selectedTurtle)
end
end
buttonInstance:Add("RETURN", selectedReturn, 14, 3, 13, 1, " Return ", colours.lightGrey)
local function selectedFollow()
if selectedTurtle then
buttonInstance:Flash("FOLLOW")
local x, y, z = gps.locate()
if x then
local position = {math.floor(x), math.floor(y), math.floor(z)}
sendFollowPacket(selectedTurtle, position)
end
end
end
buttonInstance:Add("FOLLOW", selectedFollow, 14, 4, 13, 1, " Follow ", colours.grey)
local function selectedStop()
if selectedTurtle then
buttonInstance:Flash("STOP")
sendIdlePacket(selectedTurtle)
end
end
buttonInstance:Add("STOP", selectedStop, 14, 5, 13, 1, " Stop ", colours.lightGrey)
-- SET UP TURTLE INFO SECTION
setSelectedTurtle = function(turtleID, xPos, yPos, zPos)
if turtleID then
selectedTurtle = turtleID
end
term.setCursorPos(14, 1)
term.setBackgroundColour(colours.red)
term.setTextColour(colours.white)
term.write(" Control:"..string.rep(" ", math.max(0, 4 - #tostring(turtleID)))..tostring(turtleID))
term.setBackgroundColour(colours.grey)
term.setTextColour(colours.white)
term.setCursorPos(14, 7)
term.write("ID:"..string.rep(" ", 10 - #tostring(turtleID))..tostring(turtleID))
term.setCursorPos(14, 9)
term.write("X Pos:"..string.rep(" ", 7 - #tostring(xPos))..tostring(xPos))
term.setCursorPos(14, 10)
term.write("Y Pos:"..string.rep(" ", 7 - #tostring(yPos))..tostring(yPos))
term.setCursorPos(14, 11)
term.write("Z Pos:"..string.rep(" ", 7 - #tostring(zPos))..tostring(zPos))
end
setSelectedTurtle()
local function selectedPing()
if selectedTurtle then
buttonInstance:Flash("PING")
sendPingPacket(selectedTurtle)
end
end
buttonInstance:Add("PING", selectedPing, 14, height - 6, 13, 1, " Refresh ", colours.red)
-- SET UP CONTROL ALL SECTION
term.setCursorPos(14, height - 4)
term.setBackgroundColour(colours.red)
term.setTextColour(colours.white)
term.write(" Control All ")
local function allExplore()
buttonInstance:Flash("ALL_EXPLORE")
sendExplorePacket(-1)
end
buttonInstance:Add("ALL_EXPLORE", allExplore, 14, height - 3, 13, 1, " Explore ", colours.grey)
local function allReturn()
buttonInstance:Flash("ALL_RETURN")
sendReturnPacket(-1)
end
buttonInstance:Add("ALL_RETURN", allReturn, 14, height - 2, 13, 1, " Return ", colours.lightGrey)
local function allFollow()
buttonInstance:Flash("ALL_FOLLOW")
local x, y, z = gps.locate()
if x then
local position = {math.floor(x), math.floor(y), math.floor(z)}
sendFollowPacket(-1, position)
end
end
buttonInstance:Add("ALL_FOLLOW", allFollow, 14, height - 1, 13, 1, " Follow ", colours.grey)
local function allStop()
buttonInstance:Flash("ALL_STOP")
sendIdlePacket(-1)
end
buttonInstance:Add("ALL_STOP", allStop, 14, height, 13, 1, " Stop ", colours.lightGrey)
-- DEFINE INPUT ROUTINE
local function input()
buttonInstance:Draw()
while true do
local event = {os.pullEvent()}
if event[1] == "mouse_click" then
buttonInstance:HandleClick(event[3] - guiOffset, event[4])
elseif event[1] == "key" then
if event[2] == keys.backspace then
break
elseif event[2] == keys.up then
turtleList:SetOffset(turtleList:GetOffset() - 1)
elseif event[2] == keys.down then
turtleList:SetOffset(turtleList:GetOffset() + 1)
end
end
end
end
-- DEFINE LISTEN ROUTINE
local function listen()
sendPingPacket(-1)
while true do
local senderID, message = rednet.receive(REDNET_PROTOCOL)
if type(message) == "table" and message[1] == mapName then
local sentTo = message[2]
if sentTo == os.computerID() or sentTo == -1 then
if message[3] == "PONG" then
-- update turtleList
local values = turtleList:GetValues()
local found = false
for index, data in ipairs(values) do
local id = tonumber(data:match("^%d+"))
if id == senderID then
data = createListValue(senderID, message[5])
table.remove(values, index)
table.insert(values, index, data)
turtleList:SetValues(values)
found = true
break
end
end
if not found then
local data = createListValue(senderID, message[5])
table.insert(values, data)
turtleList:SetValues(values)
end
turtleData[senderID] = message[4]
if senderID == selectedTurtle then
setSelectedTurtle(senderID, unpack(message[4]))
-- update selectedTurtle info
end
end
end
end
end
end
parallel.waitForAny(input, listen)
term.redirect(mainTerm)
term.setBackgroundColour(colours.black)
term.setTextColour(colours.white)
term.clear()
term.setCursorPos(1, 1)