Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Todo
- -- Detect floor collisions
- local version = 1.02
- local MotorConfig = {}
- local MotorScreen = {}
- local motorSettingsFile = "Motor.Settings"
- local floorCount = 0
- local floorMap = { }
- local floorDesc = { }
- local floorPass = { }
- local currentFloor = nil
- local callQueue = { }
- local movingUp = false
- local movingDown = false
- local doorOpen = false
- local KeepWaiting = true
- -- ######################################################
- -- ## Load the API ##
- -- ######################################################
- if fs.exists("ECAPI") then --Check to make sure that the entered API exists
- os.loadAPI("ECAPI")
- print("ECAPI loaded.")
- else
- print("That API does not exist. Retrieving from PasteBin")
- -- we get it automagically
- shell.run("pastebin","get", "1mcEVzzP", "ECAPI")
- os.loadAPI("ECAPI")
- print("ECAPI loaded.")
- end
- -- ######################################################
- -- ## Setup Configuration ##
- -- ######################################################
- local DefaultConfig = {}
- DefaultConfig = ECAPI.addConfigOption(DefaultConfig,"Version",version,"Please do not change this.")
- DefaultConfig = ECAPI.addConfigOption(DefaultConfig,"Network","","Elevator network name:")
- DefaultConfig = ECAPI.addConfigOption(DefaultConfig,"Modem","","Which side is the modem?",'side')
- DefaultConfig = ECAPI.addConfigOption(DefaultConfig,"Up","","Which side controls Up?",'side')
- DefaultConfig = ECAPI.addConfigOption(DefaultConfig,"Down","","Which side controls Down?",'side')
- DefaultConfig = ECAPI.addConfigOption(DefaultConfig,"Debug","error","What level of debug output?")
- DefaultConfig = ECAPI.addConfigOption(DefaultConfig,"ConfigPW","","Config. Password:",'pass') -- note, this is per machine
- local function networkName()
- return ECAPI.getConfigOption(MotorConfig,"Network")
- end
- local function modemSide()
- return ECAPI.getConfigOption(MotorConfig,"Modem")
- end
- local function upControlSide()
- return ECAPI.getConfigOption(MotorConfig,"Up")
- end
- local function downControlSide()
- return ECAPI.getConfigOption(MotorConfig,"Down")
- end
- -- ######################################################
- -- ## Terminal Functions ##
- -- ######################################################
- local function termClear()
- ECAPI.printMotorTemplate(MotorConfig)
- MotorScreen = ECAPI.setupScreen(MotorScreen)
- term.setCursorPos(3,18)
- write("Command: ")
- end
- local function justPrintInfo(MotorScreen,moduleID,sourceID,floorID,newLine)
- local t = "["..ECAPI.padLeft(moduleID,6," ").."]"
- t = t .. "["..ECAPI.padLeft(sourceID,4," ")..":"
- t = t .. ECAPI.padLeft(floorID,2," ").."]: " .. ECAPI.padLeft(newLine,24," ")
- if (ECAPI.getConfigOption(MotorConfig,"Debug") == "info") then
- MotorScreen = ECAPI.justPrintLine(MotorScreen,t)
- end
- return MotorScreen
- end
- local function justPrintLine(newLine)
- MotorScreen = ECAPI.justPrintLine(MotorScreen,newLine)
- end
- -- ######################################################
- -- ## Settings Functions ##
- -- ######################################################
- local function createNewSettings()
- MotorConfig = DefaultConfig
- termClear()
- --MotorScreen = ECAPI.printConfig(MotorConfig,MotorScreen)
- -- Confirm settings with the user, then save to file
- MotorScreen = ECAPI.addPrintLine(MotorScreen,"No settings file found. Confirming settings:")
- --MotorScreen = ECAPI.printConfig(MotorConfig,MotorScreen)
- -- To ensure this code plays nice with multiple elevators, Unique network names are required (as best as can)
- MotorScreen, MotorConfig = ECAPI.getConfigFromUser(MotorScreen,MotorConfig,"Network")
- MotorScreen = ECAPI.printConfig(MotorConfig,MotorScreen,"Network")
- -- Confirm modem side
- MotorScreen, MotorConfig = ECAPI.getConfigFromUser(MotorScreen,MotorConfig,"Modem")
- MotorScreen = ECAPI.printConfig(MotorConfig,MotorScreen,"Modem")
- -- Confirm up control side
- MotorScreen, MotorConfig = ECAPI.getConfigFromUser(MotorScreen,MotorConfig,"Up")
- MotorScreen = ECAPI.printConfig(MotorConfig,MotorScreen,"Up")
- -- Confirm down control side
- MotorScreen, MotorConfig = ECAPI.getConfigFromUser(MotorScreen,MotorConfig,"Down")
- MotorScreen = ECAPI.printConfig(MotorConfig,MotorScreen,"Down")
- MotorScreen, MotorConfig = ECAPI.getConfigFromUser(MotorScreen,MotorConfig,"Debug")
- MotorScreen = ECAPI.printConfig(MotorConfig,MotorScreen,"Debug")
- -- Write to file
- ECAPI.table_save(MotorConfig,motorSettingsFile)
- termClear()
- end
- local function startupConfirmation()
- -- Check if there's a settings file
- if fs.exists(motorSettingsFile) then
- termClear()
- MotorConfig = ECAPI.table_load(motorSettingsFile)
- ECAPI.verifyConfig(DefaultConfig,MotorConfig)
- termClear()
- MotorScreen = ECAPI.justPrintLine(MotorScreen,"Settings loaded.")
- --MotorScreen = ECAPI.printConfig(MotorConfig,MotorScreen)
- os.sleep(0.5)
- else
- createNewSettings()
- end
- ECAPI.openModem(modemSide())
- -- see if there's any running Doors
- ECAPI.broadcast(networkName(),'controlreset')
- --MotorScreen = ECAPI.justPrintLine(MotorScreen,"Control Reset starting... Please wait.")
- end
- -- ######################################################
- -- ## Control Functions ##
- -- ######################################################
- local function getIDFromFloor(whichFloor)
- reverseMap = ECAPI.table_invert(floorMap)
- MotorScreen = justPrintInfo(MotorScreen,"Which: "..ECAPI.padLeft(whichFloor,4," ").." : "..ECAPI.padLeft(reverseMap[tonumber(whichFloor)],4," "))
- return reverseMap[whichFloor]
- end
- local function testUp()
- MotorScreen = justPrintInfo(MotorScreen,"MOTOR",nil,nil,"In 3, ["..upControlSide().."] will blink 3.")
- os.sleep(3)
- for i = 1,3 do
- redstone.setOutput(upControlSide(),true)
- os.sleep(0.2)
- redstone.setOutput(upControlSide(),false)
- os.sleep(0.2)
- end
- end
- local function testDown()
- MotorScreen = justPrintInfo(MotorScreen,"MOTOR",nil,nil,"In 3, ["..downControlSide().."] will blink 3.")
- os.sleep(3)
- for i = 1,3 do
- redstone.setOutput(downControlSide(),true)
- os.sleep(0.2)
- redstone.setOutput(downControlSide(),false)
- os.sleep(0.2)
- end
- end
- local function addFloor(computerID,whichFloor,description,floorPW)
- floorMap[computerID] = tonumber(whichFloor)
- floorDesc[tonumber(whichFloor)] = description
- if (floorPW ~= nil) and (floorPW ~= "") then
- floorPass[tonumber(whichFloor)] = floorPW
- end
- floorCount = floorCount + 1
- end
- local function printFloors()
- -- first, move the floor map to be keyed on the floor ID, rather than computer ID
- local floorListing = {}
- for i,v in pairs(floorMap) do
- floorListing[tonumber(v)] = tonumber(i)
- end
- table.sort(floorListing,reverseSort)
- justPrintLine("Printing floorlist")
- justPrintLine("Floor SysID Description")
- justPrintLine("--------------------------")
- if (floorCount > 0) then
- for fl,comp in ECAPI.ripairs(floorListing) do
- local indicator = ""
- if (currentFloor == fl) then indicator = "*" else indicator = "" end
- justPrintLine(ECAPI.padLeft(fl..indicator,7,' ')..ECAPI.padLeft(comp,8,' ')..ECAPI.padLeft(floorDesc[fl],35,' '))
- end
- else
- justPrintLine("* No Floor computers registered. *")
- end
- end
- local function startMotion()
- -- determine next direction of motion based on next queue entry
- local queueCount = # callQueue
- if (queueCount > 0) then
- local callFloor = floorMap[callQueue[1]]
- if (currentFloor == nil) then
- -- the elevator got lost
- elseif (callFloor > currentFloor) then
- movingUp = true
- ECAPI.broadcast(networkName(),'displayup|/\\|'..currentFloor)
- elseif (callFloor < currentFloor) then
- movingDown = true
- ECAPI.broadcast(networkName(),'displaydown|\\/|'..currentFloor)
- else
- -- open the door, then remove the request
- ECAPI.broadcast(networkName(),'opendoor|'..callQueue[1])
- table.remove(callQueue,1)
- end
- end
- end
- local function detectArrival(sendID)
- currentFloor = floorMap[sendID]
- -- If the first floor to 'call out' is the currentFloor, the map might not be populated yet
- if (currentFloor ~= nil) then
- -- detect if the floor is one of the ones in the queue. if so, pause motion for a bit.
- if ECAPI.tableContainsValue(callQueue,sendID) then
- --MotorScreen = justPrintInfo(MotorScreen,"On the queued floor. Pausing, opening door")
- movingUp = false
- ECAPI.broadcast(networkName(),'displayup| ')
- movingDown = false
- ECAPI.broadcast(networkName(),'displaydown| ')
- ECAPI.broadcast(networkName(),'opendoor|'..sendID)
- os.sleep(5)
- ECAPI.broadcast(networkName(),'opendoor|invalid')
- table.remove(callQueue,1)
- end
- startMotion()
- end
- end
- local function addCallQueue(sendID)
- local callFloor = floorMap[sendID]
- -- add the call to the queue after checking to make sure it's not a duplicate
- local found = false
- if (callQueue ~= nil) then
- for i,v in ipairs(callQueue) do
- if v == sendID then found = true end
- end
- end
- if not found then
- table.insert(callQueue,sendID)
- end
- if (not movingUp) and (not movingDown) then
- startMotion()
- end
- end
- local function rebootAll()
- ECAPI.broadcast(networkName(),'reboot')
- os.reboot()
- end
- local function sendFloors(toID)
- local floors = { }
- for floorID, desc in pairs(floorDesc) do
- local pass = ""
- if (floorPass[floorID] ~= nil) then
- pass = floorPass[floorID]
- end
- floors[floorID] = { ['Description']= desc , ['Password'] = pass}
- end
- local t = textutils.serialize(floors)
- ECAPI.send(networkName(),toID,"floordata|"..t)
- end
- local function reconfig(args)
- if (ECAPI.checkPassword(MotorScreen,MotorConfig,"ConfigPW")) then
- local what = args[1]
- if (what == nil) or (what == "") then
- MotorConfig = DefaultConfig
- createNewSettings()
- else
- what = ECAPI.caseCheck(MotorConfig,what)
- if (args[2] == nil) then
- MotorScreen, MotorConfig = ECAPI.getConfigFromUser(MotorScreen,MotorConfig,what)
- else
- MotorConfig = ECAPI.setConfigOption(MotorConfig,what,table.concat(args," ",2))
- end
- -- Make sure to SAVE changes
- ECAPI.table_save(MotorConfig,motorSettingsFile)
- -- if the modem changed, this makes sure we reopen it
- ECAPI.openModem(modemSide())
- -- see if there's any running Doors
- ECAPI.broadcast(networkName(),'controlreset')
- end
- else
- justPrintLine("Invalid password.")
- end
- end
- -- ######################################################
- -- ## Input Actions ##
- -- ######################################################
- -- MAX Topic details: 45 characters per table entry
- -- |---------------------------------------------|
- local helpTopics = {
- ["reboot"] = { "Restart this terminal." ,},
- ["clear"] = { "Refresh the terminal screen." ,},
- ["config"] = { "Prints the current settings. Will not print",
- "any set passwords." ,},
- ["reconfig"] = { "With no parameters, re-run the whole setup.",
- "RECONFIG [option] to change a single setting",
- "Note: Reconfig can be password protected via",
- "the ConfigPW setting. If this password is",
- "lost, you will need server admin help or be",
- "forced to recreate the terminal." ,},
- ["broadcast"] = { "Manually broadcast <message>",},
- ["send"] = { "Manually send <id> <message>",},
- ["testup"] = { "Blinks the Up control side." },
- ["testdown"] = { "Blinks the Down control side.", },
- ["floors"] = { "Print list of floors with Door IDs"},
- ["rebootall"] = { "Sends a restart command to the whole network.",},
- }
- local function printHelp(topic)
- justPrintLine("Help on '"..string.upper(topic).."':")
- local t = helpTopics[topic]
- for _, text in ipairs(t) do
- justPrintLine(" "..text)
- end
- end
- local commands = {
- ["help"] = function (x)
- if (x[1] ~= nil) then
- printHelp(x[1])
- else
- justPrintLine("Please use HELP [COMMAND]. ? for Command List")
- end
- end,
- ["reboot"] = function (x) os.reboot() end,
- ["clear"] = function (x) termClear() end,
- ["broadcast"] = function (x)
- MotorScreen = justPrintInfo(MotorScreen,"REDNET",nil,nil,"Broadcast: "..table.concat(x," ",2))
- ECAPI.broadcast(networkName(),table.concat(x," "))
- end,
- ["send"] = function (x)
- MotorScreen = justPrintInfo(MotorScreen,"REDNET",nil,nil,"Send("..tonumber(x[1]).."): "..table.concat(x," ",2))
- ECAPI.send(networkName(),tonumber(x[1]),table.concat(x," ",2))
- end,
- ["config"] = function (x)
- justPrintLine("Current Configuration:")
- justPrintLine("-------------------------------")
- ECAPI.printConfig(MotorConfig,MotorScreen)
- end,
- ["reconfig"] = function (x) reconfig(x) end,
- ["floors"] = function (x) printFloors() end,
- ["rebootall"] = function (x) rebootAll() end,
- ["testup"] = function (x) testUp(x) end,
- ["testdown"] = function (x) testDown(x) end,
- }
- local function printCommands()
- local col = 1
- local row = {}
- for named, _ in pairs(commands) do
- if (col < 5) then row[col] = string.upper(named) end
- col = col + 1
- if (col == 5) then
- justPrintLine(ECAPI.padLeft(row[1],12," ")..ECAPI.padLeft(row[2],12," ")..ECAPI.padLeft(row[3],12," ")..ECAPI.padLeft(row[4],11," "))
- row = {}
- col = 1
- end
- end
- if (col > 1) then
- -- handles 'remainders'
- justPrintLine(ECAPI.padLeft(row[1],12," ")..ECAPI.padLeft(row[2],12," ")..ECAPI.padLeft(row[3],12," ")..ECAPI.padLeft(row[4],11," "))
- end
- end
- local function processCommands(input)
- local arg = ECAPI.explode(" ",input)
- local command = arg[1]
- table.remove(arg,1)
- if ECAPI.tableContainsKey(commands, command) then
- commands[command](arg)
- else
- justPrintLine("Unknown command '"..command.."'. Commands are:")
- printCommands()
- end
- end
- -- ######################################################
- -- ## Handler Functions ##
- -- ######################################################
- -- User input listener
- local function userConsole()
- while KeepWaiting do
- processCommands(ECAPI.getInput("Command:"))
- end
- end
- -- Rednet listener
- local function rednetListener()
- while KeepWaiting do
- local sendID, rawmessage, distance = rednet.receive()
- local packet = ECAPI.explode("|",rawmessage)
- if packet[1] == networkName() then
- MotorScreen = justPrintInfo(MotorScreen,"REDNET",sendID,floorMap[sendID],table.concat(packet," ",2))
- if string.lower(packet[2]) == 'addfloor' then
- addFloor(tonumber(sendID),packet[3],packet[4],packet[5])
- elseif string.lower(packet[2]) == 'call' then
- addCallQueue(tonumber(sendID))
- elseif string.lower(packet[2]) == 'arrival' then
- detectArrival(tonumber(sendID))
- elseif string.lower(packet[2]) == 'getfloors' then
- sendFloors(tonumber(sendID))
- elseif string.lower(packet[2]) == 'openfloor' then
- if (not movingUp) and (not movingDown) then
- --MotorScreen = ECAPI.justPrintLine(MotorScreen,'Opening Door Floor[id]: '..currentFloor..'['..getIDFromFloor(currentFloor)..']')
- ECAPI.broadcast(networkName(),'opendoor|'..tostring(getIDFromFloor(currentFloor)))
- doorOpen = true
- end
- elseif string.lower(packet[2]) == 'request' then
- addCallQueue(getIDFromFloor(tonumber(packet[3])))
- end
- end
- end
- end
- local function cabinFinder()
- -- always wait for possible bootup
- os.sleep(6)
- local maxMove = 50
- local currMove = 0
- -- try to move 50 up to find a station, stop if we find it
- while (currentFloor == nil) and KeepWaiting and (currMove < maxMove) do
- redstone.setOutput(upControlSide(),true)
- os.sleep(0.2)
- redstone.setOutput(upControlSide(),false)
- os.sleep(2)
- currMove = currMove + 1
- end
- -- try to move 100 down to find a station, stop if we find it
- while (currentFloor == nil) and KeepWaiting and (currMove > (-maxMove*2)) do
- redstone.setOutput(upControlSide(),true)
- os.sleep(0.2)
- redstone.setOutput(upControlSide(),false)
- os.sleep(2)
- currMove = currMove - 1
- end
- end
- local function mover()
- while KeepWaiting do
- if (movingUp) then
- redstone.setOutput(upControlSide(),true)
- os.sleep(0.2)
- redstone.setOutput(upControlSide(),false)
- ECAPI.broadcast(networkName(),'displayup|/\\|'..currentFloor)
- elseif (movingDown) then
- redstone.setOutput(downControlSide(),true)
- os.sleep(0.2)
- redstone.setOutput(downControlSide(),false)
- ECAPI.broadcast(networkName(),'displaydown|\\/|'..currentFloor)
- end
- os.sleep(.8)
- end
- end
- local function doorCloser()
- -- if a door is manually opened, close the door. Check every 10 seconds
- while KeepWaiting do
- if doorOpen then
- ECAPI.broadcast(networkName(),'closedoor')
- doorOpen = false
- end
- os.sleep(10)
- end
- end
- -- ######################################################
- -- ## Actual Run Program ##
- -- ######################################################
- startupConfirmation()
- parallel.waitForAll(userConsole,rednetListener,cabinFinder,mover,doorCloser)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement