Advertisement
Rihlsul

ElevatorControl Motor 1.02

Apr 5th, 2013
856
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 16.92 KB | None | 0 0
  1. -- Todo
  2.     -- Detect floor collisions
  3.  
  4. local version = 1.02
  5.  
  6. local MotorConfig = {}
  7. local MotorScreen = {}
  8. local motorSettingsFile = "Motor.Settings"
  9.  
  10. local floorCount = 0
  11. local floorMap = { }
  12. local floorDesc = { }
  13. local floorPass = { }
  14. local currentFloor = nil
  15. local callQueue = { }
  16.  
  17. local movingUp = false
  18. local movingDown = false
  19. local doorOpen = false
  20. local KeepWaiting = true
  21.  
  22. -- ######################################################
  23. -- ##  Load the API                                    ##
  24. -- ######################################################
  25.  
  26. if fs.exists("ECAPI") then --Check to make sure that the entered API exists
  27.   os.loadAPI("ECAPI")
  28.   print("ECAPI loaded.")
  29. else
  30.   print("That API does not exist.  Retrieving from PasteBin")
  31.   -- we get it automagically
  32.   shell.run("pastebin","get", "1mcEVzzP", "ECAPI")
  33.   os.loadAPI("ECAPI")
  34.   print("ECAPI loaded.")
  35. end
  36.  
  37. -- ######################################################
  38. -- ##  Setup Configuration                             ##
  39. -- ######################################################
  40.  
  41. local DefaultConfig = {}
  42. DefaultConfig = ECAPI.addConfigOption(DefaultConfig,"Version",version,"Please do not change this.")
  43. DefaultConfig = ECAPI.addConfigOption(DefaultConfig,"Network","","Elevator network name:")
  44. DefaultConfig = ECAPI.addConfigOption(DefaultConfig,"Modem","","Which side is the modem?",'side')
  45. DefaultConfig = ECAPI.addConfigOption(DefaultConfig,"Up","","Which side controls Up?",'side')
  46. DefaultConfig = ECAPI.addConfigOption(DefaultConfig,"Down","","Which side controls Down?",'side')
  47. DefaultConfig = ECAPI.addConfigOption(DefaultConfig,"Debug","error","What level of debug output?")
  48. DefaultConfig = ECAPI.addConfigOption(DefaultConfig,"ConfigPW","","Config. Password:",'pass')   -- note, this is per machine
  49.  
  50.  
  51. local function networkName()
  52.     return ECAPI.getConfigOption(MotorConfig,"Network")
  53. end
  54.  
  55. local function modemSide()
  56.     return ECAPI.getConfigOption(MotorConfig,"Modem")
  57. end
  58.  
  59. local function upControlSide()
  60.     return ECAPI.getConfigOption(MotorConfig,"Up")
  61. end
  62.  
  63. local function downControlSide()
  64.     return ECAPI.getConfigOption(MotorConfig,"Down")
  65. end
  66.  
  67. -- ######################################################
  68. -- ##  Terminal Functions                              ##
  69. -- ######################################################
  70.  
  71. local function termClear()
  72.     ECAPI.printMotorTemplate(MotorConfig)
  73.     MotorScreen = ECAPI.setupScreen(MotorScreen)
  74.     term.setCursorPos(3,18)
  75.     write("Command: ")
  76. end
  77.  
  78. local function justPrintInfo(MotorScreen,moduleID,sourceID,floorID,newLine)
  79.     local t = "["..ECAPI.padLeft(moduleID,6," ").."]"
  80.     t = t .. "["..ECAPI.padLeft(sourceID,4," ")..":"
  81.     t = t .. ECAPI.padLeft(floorID,2," ").."]: " .. ECAPI.padLeft(newLine,24," ")
  82.    
  83.     if (ECAPI.getConfigOption(MotorConfig,"Debug") == "info") then
  84.         MotorScreen = ECAPI.justPrintLine(MotorScreen,t)
  85.     end
  86.     return MotorScreen
  87. end
  88.  
  89. local function justPrintLine(newLine)
  90.     MotorScreen = ECAPI.justPrintLine(MotorScreen,newLine)
  91. end
  92.  
  93. -- ######################################################
  94. -- ##  Settings Functions                              ##
  95. -- ######################################################
  96.  
  97. local function createNewSettings()
  98.     MotorConfig = DefaultConfig
  99.     termClear()
  100.     --MotorScreen = ECAPI.printConfig(MotorConfig,MotorScreen)
  101.  
  102.     -- Confirm settings with the user, then save to file
  103.     MotorScreen = ECAPI.addPrintLine(MotorScreen,"No settings file found. Confirming settings:")
  104.     --MotorScreen = ECAPI.printConfig(MotorConfig,MotorScreen)
  105.  
  106.     -- To ensure this code plays nice with multiple elevators, Unique network names are required (as best as can)
  107.     MotorScreen, MotorConfig = ECAPI.getConfigFromUser(MotorScreen,MotorConfig,"Network")
  108.     MotorScreen = ECAPI.printConfig(MotorConfig,MotorScreen,"Network")
  109.    
  110.     -- Confirm modem side
  111.     MotorScreen, MotorConfig = ECAPI.getConfigFromUser(MotorScreen,MotorConfig,"Modem")
  112.     MotorScreen = ECAPI.printConfig(MotorConfig,MotorScreen,"Modem")
  113.    
  114.     -- Confirm up control side
  115.     MotorScreen, MotorConfig = ECAPI.getConfigFromUser(MotorScreen,MotorConfig,"Up")
  116.     MotorScreen = ECAPI.printConfig(MotorConfig,MotorScreen,"Up")
  117.    
  118.     -- Confirm down control side
  119.     MotorScreen, MotorConfig = ECAPI.getConfigFromUser(MotorScreen,MotorConfig,"Down")
  120.     MotorScreen = ECAPI.printConfig(MotorConfig,MotorScreen,"Down")
  121.    
  122.     MotorScreen, MotorConfig = ECAPI.getConfigFromUser(MotorScreen,MotorConfig,"Debug")
  123.     MotorScreen = ECAPI.printConfig(MotorConfig,MotorScreen,"Debug")
  124.        
  125.     -- Write to file
  126.     ECAPI.table_save(MotorConfig,motorSettingsFile)
  127.     termClear()
  128. end
  129.  
  130. local function startupConfirmation()
  131.     -- Check if there's a settings file
  132.     if fs.exists(motorSettingsFile) then
  133.         termClear()
  134.         MotorConfig = ECAPI.table_load(motorSettingsFile)
  135.         ECAPI.verifyConfig(DefaultConfig,MotorConfig)
  136.         termClear()
  137.         MotorScreen = ECAPI.justPrintLine(MotorScreen,"Settings loaded.")
  138.         --MotorScreen = ECAPI.printConfig(MotorConfig,MotorScreen)
  139.         os.sleep(0.5)
  140.     else
  141.         createNewSettings()
  142.     end
  143.     ECAPI.openModem(modemSide())
  144.     -- see if there's any running Doors
  145.     ECAPI.broadcast(networkName(),'controlreset')
  146.     --MotorScreen = ECAPI.justPrintLine(MotorScreen,"Control Reset starting... Please wait.")
  147. end
  148.  
  149.  
  150. -- ######################################################
  151. -- ##  Control Functions                               ##
  152. -- ######################################################
  153.  
  154.  
  155. local function getIDFromFloor(whichFloor)
  156.     reverseMap = ECAPI.table_invert(floorMap)
  157.     MotorScreen = justPrintInfo(MotorScreen,"Which: "..ECAPI.padLeft(whichFloor,4," ").."  : "..ECAPI.padLeft(reverseMap[tonumber(whichFloor)],4," "))
  158.     return reverseMap[whichFloor]
  159. end
  160.  
  161. local function testUp()
  162.     MotorScreen = justPrintInfo(MotorScreen,"MOTOR",nil,nil,"In 3, ["..upControlSide().."] will blink 3.")
  163.     os.sleep(3)
  164.     for i = 1,3 do
  165.         redstone.setOutput(upControlSide(),true)
  166.         os.sleep(0.2)
  167.         redstone.setOutput(upControlSide(),false)
  168.         os.sleep(0.2)
  169.     end
  170. end
  171.  
  172. local function testDown()
  173.     MotorScreen = justPrintInfo(MotorScreen,"MOTOR",nil,nil,"In 3, ["..downControlSide().."] will blink 3.")
  174.     os.sleep(3)
  175.     for i = 1,3 do
  176.         redstone.setOutput(downControlSide(),true)
  177.         os.sleep(0.2)
  178.         redstone.setOutput(downControlSide(),false)
  179.         os.sleep(0.2)
  180.     end
  181. end
  182.  
  183. local function addFloor(computerID,whichFloor,description,floorPW)
  184.     floorMap[computerID] = tonumber(whichFloor)
  185.     floorDesc[tonumber(whichFloor)] = description
  186.     if (floorPW ~= nil) and (floorPW ~= "") then
  187.         floorPass[tonumber(whichFloor)] = floorPW
  188.     end
  189.     floorCount = floorCount + 1
  190. end
  191.  
  192. local function printFloors()
  193.     -- first, move the floor map to be keyed on the floor ID, rather than computer ID
  194.     local floorListing = {}
  195.     for i,v in pairs(floorMap) do
  196.         floorListing[tonumber(v)] = tonumber(i)
  197.     end
  198.     table.sort(floorListing,reverseSort)
  199.  
  200.     justPrintLine("Printing floorlist")
  201.     justPrintLine("Floor  SysID   Description")
  202.     justPrintLine("--------------------------")
  203.     if (floorCount > 0) then
  204.         for fl,comp in ECAPI.ripairs(floorListing) do
  205.             local indicator = ""
  206.             if (currentFloor == fl) then indicator = "*" else indicator = "" end
  207.             justPrintLine(ECAPI.padLeft(fl..indicator,7,' ')..ECAPI.padLeft(comp,8,' ')..ECAPI.padLeft(floorDesc[fl],35,' '))
  208.         end
  209.     else
  210.         justPrintLine("* No Floor computers registered. *")
  211.     end
  212. end
  213.  
  214. local function startMotion()
  215.     -- determine next direction of motion based on next queue entry
  216.     local queueCount = # callQueue
  217.     if (queueCount > 0) then
  218.         local callFloor = floorMap[callQueue[1]]
  219.         if (currentFloor == nil) then
  220.             -- the elevator got lost
  221.            
  222.         elseif (callFloor > currentFloor) then
  223.             movingUp = true
  224.             ECAPI.broadcast(networkName(),'displayup|/\\|'..currentFloor)
  225.         elseif (callFloor < currentFloor) then
  226.             movingDown = true
  227.             ECAPI.broadcast(networkName(),'displaydown|\\/|'..currentFloor)
  228.         else
  229.             -- open the door, then remove the request
  230.             ECAPI.broadcast(networkName(),'opendoor|'..callQueue[1])
  231.             table.remove(callQueue,1)
  232.         end
  233.     end
  234. end
  235.  
  236. local function detectArrival(sendID)
  237.     currentFloor = floorMap[sendID]
  238.    
  239.     -- If the first floor to 'call out' is the currentFloor, the map might not be populated yet
  240.     if (currentFloor ~= nil) then
  241.        
  242.         -- detect if the floor is one of the ones in the queue.  if so, pause motion for a bit.
  243.         if ECAPI.tableContainsValue(callQueue,sendID) then
  244.             --MotorScreen = justPrintInfo(MotorScreen,"On the queued floor. Pausing, opening door")
  245.             movingUp = false
  246.             ECAPI.broadcast(networkName(),'displayup|  ')
  247.             movingDown = false
  248.             ECAPI.broadcast(networkName(),'displaydown|  ')
  249.             ECAPI.broadcast(networkName(),'opendoor|'..sendID)
  250.             os.sleep(5)
  251.             ECAPI.broadcast(networkName(),'opendoor|invalid')
  252.            
  253.             table.remove(callQueue,1)
  254.         end
  255.         startMotion()
  256.     end
  257. end
  258.  
  259. local function addCallQueue(sendID)
  260.     local callFloor = floorMap[sendID]
  261.    
  262.     -- add the call to the queue after checking to make sure it's not a duplicate
  263.     local found = false
  264.     if (callQueue ~= nil) then
  265.         for i,v in ipairs(callQueue) do
  266.             if v == sendID then found = true end
  267.         end
  268.     end
  269.     if not found then
  270.         table.insert(callQueue,sendID)
  271.     end
  272.    
  273.     if (not movingUp) and (not movingDown) then
  274.         startMotion()
  275.     end
  276. end
  277.  
  278. local function rebootAll()
  279.     ECAPI.broadcast(networkName(),'reboot')
  280.     os.reboot()
  281. end
  282.  
  283. local function sendFloors(toID)
  284.     local floors = { }
  285.     for floorID, desc in pairs(floorDesc) do
  286.         local pass = ""
  287.         if (floorPass[floorID] ~= nil) then
  288.             pass = floorPass[floorID]
  289.         end
  290.         floors[floorID] = { ['Description']= desc , ['Password'] = pass}
  291.     end
  292.    
  293.     local t = textutils.serialize(floors)
  294.     ECAPI.send(networkName(),toID,"floordata|"..t)
  295. end
  296.  
  297. local function reconfig(args)
  298.     if (ECAPI.checkPassword(MotorScreen,MotorConfig,"ConfigPW")) then
  299.         local what = args[1]
  300.         if (what == nil) or (what == "") then
  301.             MotorConfig = DefaultConfig
  302.             createNewSettings()
  303.         else
  304.             what = ECAPI.caseCheck(MotorConfig,what)
  305.             if (args[2] == nil) then
  306.                 MotorScreen, MotorConfig = ECAPI.getConfigFromUser(MotorScreen,MotorConfig,what)
  307.             else
  308.                 MotorConfig = ECAPI.setConfigOption(MotorConfig,what,table.concat(args," ",2))
  309.             end
  310.             -- Make sure to SAVE changes
  311.             ECAPI.table_save(MotorConfig,motorSettingsFile)
  312.            
  313.             -- if the modem changed, this makes sure we reopen it
  314.             ECAPI.openModem(modemSide())
  315.             -- see if there's any running Doors
  316.             ECAPI.broadcast(networkName(),'controlreset')
  317.         end
  318.     else
  319.         justPrintLine("Invalid password.")
  320.     end
  321. end
  322.  
  323. -- ######################################################
  324. -- ##  Input Actions                                   ##
  325. -- ######################################################
  326.  
  327. -- MAX Topic details: 45 characters per table entry
  328. --                      |---------------------------------------------|
  329. local helpTopics = {
  330.     ["reboot"]      = { "Restart this terminal." ,},
  331.     ["clear"]       = { "Refresh the terminal screen." ,},
  332.     ["config"]      = { "Prints the current settings. Will not print",
  333.                         "any set passwords." ,},
  334.     ["reconfig"]    = { "With no parameters, re-run the whole setup.",
  335.                         "RECONFIG [option] to change a single setting",
  336.                         "Note: Reconfig can be password protected via",
  337.                         "the ConfigPW setting. If this password is",
  338.                         "lost, you will need server admin help or be",
  339.                         "forced to recreate the terminal." ,},
  340.     ["broadcast"]   = { "Manually broadcast <message>",},
  341.     ["send"]        = { "Manually send <id> <message>",},
  342.  
  343.     ["testup"]      = { "Blinks the Up control side." },
  344.     ["testdown"]    = { "Blinks the Down control side.", },
  345.     ["floors"]      = { "Print list of floors with Door IDs"},
  346.     ["rebootall"]   = { "Sends a restart command to the whole network.",},
  347. }
  348.  
  349. local function printHelp(topic)
  350.     justPrintLine("Help on '"..string.upper(topic).."':")
  351.     local t = helpTopics[topic]
  352.     for _, text in ipairs(t) do
  353.         justPrintLine("  "..text)
  354.     end
  355. end
  356.  
  357. local commands = {
  358.     ["help"] = function (x)
  359.             if (x[1] ~= nil) then
  360.                 printHelp(x[1])
  361.             else
  362.                 justPrintLine("Please use HELP [COMMAND]. ? for Command List")
  363.             end
  364.         end,
  365.     ["reboot"] = function (x) os.reboot() end,
  366.     ["clear"] = function (x) termClear() end,
  367.     ["broadcast"] = function (x)
  368.             MotorScreen = justPrintInfo(MotorScreen,"REDNET",nil,nil,"Broadcast: "..table.concat(x," ",2))
  369.             ECAPI.broadcast(networkName(),table.concat(x," "))
  370.         end,
  371.     ["send"] = function (x)
  372.             MotorScreen = justPrintInfo(MotorScreen,"REDNET",nil,nil,"Send("..tonumber(x[1]).."): "..table.concat(x," ",2))
  373.             ECAPI.send(networkName(),tonumber(x[1]),table.concat(x," ",2))
  374.         end,
  375.    
  376.     ["config"] = function (x)
  377.             justPrintLine("Current Configuration:")
  378.             justPrintLine("-------------------------------")
  379.             ECAPI.printConfig(MotorConfig,MotorScreen)
  380.         end,
  381.     ["reconfig"] = function (x) reconfig(x) end,
  382.  
  383.  
  384.     ["floors"] = function (x) printFloors() end,
  385.     ["rebootall"] = function (x) rebootAll() end,
  386.    
  387.     ["testup"] = function (x) testUp(x) end,
  388.     ["testdown"] = function (x) testDown(x) end,
  389. }
  390.  
  391. local function printCommands()
  392.     local col = 1
  393.     local row = {}
  394.     for named, _ in pairs(commands) do
  395.         if (col < 5) then row[col] = string.upper(named) end
  396.         col = col + 1
  397.         if (col == 5) then
  398.             justPrintLine(ECAPI.padLeft(row[1],12," ")..ECAPI.padLeft(row[2],12," ")..ECAPI.padLeft(row[3],12," ")..ECAPI.padLeft(row[4],11," "))
  399.             row = {}
  400.             col = 1
  401.         end
  402.     end
  403.     if (col > 1) then
  404.         -- handles 'remainders'
  405.         justPrintLine(ECAPI.padLeft(row[1],12," ")..ECAPI.padLeft(row[2],12," ")..ECAPI.padLeft(row[3],12," ")..ECAPI.padLeft(row[4],11," "))
  406.     end
  407. end
  408.  
  409.  
  410. local function processCommands(input)
  411.     local arg = ECAPI.explode(" ",input)
  412.     local command = arg[1]
  413.     table.remove(arg,1)
  414.     if ECAPI.tableContainsKey(commands, command) then
  415.         commands[command](arg)
  416.     else
  417.         justPrintLine("Unknown command '"..command.."'. Commands are:")
  418.         printCommands()
  419.     end
  420. end
  421.  
  422.  
  423.  
  424. -- ######################################################
  425. -- ##  Handler Functions                               ##
  426. -- ######################################################
  427.  
  428. -- User input listener
  429. local function userConsole()
  430.     while KeepWaiting do
  431.         processCommands(ECAPI.getInput("Command:"))
  432.     end
  433. end
  434.  
  435. -- Rednet listener
  436. local function rednetListener()
  437.     while KeepWaiting do       
  438.         local sendID, rawmessage, distance = rednet.receive()
  439.         local packet = ECAPI.explode("|",rawmessage)
  440.         if packet[1] == networkName() then
  441.             MotorScreen = justPrintInfo(MotorScreen,"REDNET",sendID,floorMap[sendID],table.concat(packet," ",2))
  442.             if string.lower(packet[2]) ==     'addfloor'       then
  443.                 addFloor(tonumber(sendID),packet[3],packet[4],packet[5])
  444.             elseif string.lower(packet[2]) == 'call'           then
  445.                 addCallQueue(tonumber(sendID))
  446.             elseif string.lower(packet[2]) == 'arrival'           then
  447.                 detectArrival(tonumber(sendID))
  448.             elseif string.lower(packet[2]) == 'getfloors'           then
  449.                 sendFloors(tonumber(sendID))
  450.             elseif string.lower(packet[2]) == 'openfloor'           then
  451.                 if (not movingUp) and (not movingDown) then
  452.                     --MotorScreen = ECAPI.justPrintLine(MotorScreen,'Opening Door Floor[id]: '..currentFloor..'['..getIDFromFloor(currentFloor)..']')
  453.                     ECAPI.broadcast(networkName(),'opendoor|'..tostring(getIDFromFloor(currentFloor)))
  454.                     doorOpen = true
  455.                 end
  456.             elseif string.lower(packet[2]) == 'request'          then
  457.                 addCallQueue(getIDFromFloor(tonumber(packet[3])))
  458.             end
  459.         end
  460.     end
  461. end
  462.  
  463. local function cabinFinder()
  464.     -- always wait for possible bootup
  465.     os.sleep(6)
  466.     local maxMove = 50    
  467.     local currMove = 0
  468.     -- try to move 50 up to find a station, stop if we find it
  469.     while (currentFloor == nil) and KeepWaiting and (currMove < maxMove) do
  470.         redstone.setOutput(upControlSide(),true)
  471.         os.sleep(0.2)
  472.         redstone.setOutput(upControlSide(),false)
  473.         os.sleep(2)
  474.         currMove = currMove + 1
  475.     end
  476.     -- try to move 100 down to find a station, stop if we find it
  477.     while (currentFloor == nil) and KeepWaiting and (currMove > (-maxMove*2)) do
  478.         redstone.setOutput(upControlSide(),true)
  479.         os.sleep(0.2)
  480.         redstone.setOutput(upControlSide(),false)
  481.         os.sleep(2)
  482.         currMove = currMove - 1
  483.     end
  484. end
  485.  
  486. local function mover()
  487.     while KeepWaiting do
  488.         if (movingUp) then
  489.             redstone.setOutput(upControlSide(),true)
  490.             os.sleep(0.2)
  491.             redstone.setOutput(upControlSide(),false)
  492.             ECAPI.broadcast(networkName(),'displayup|/\\|'..currentFloor)
  493.         elseif (movingDown) then
  494.             redstone.setOutput(downControlSide(),true)
  495.             os.sleep(0.2)
  496.             redstone.setOutput(downControlSide(),false)
  497.             ECAPI.broadcast(networkName(),'displaydown|\\/|'..currentFloor)
  498.         end
  499.         os.sleep(.8)
  500.     end
  501. end
  502.  
  503. local function doorCloser()
  504.     -- if a door is manually opened, close the door. Check every 10 seconds
  505.     while KeepWaiting do
  506.         if doorOpen then
  507.             ECAPI.broadcast(networkName(),'closedoor')
  508.             doorOpen = false
  509.         end
  510.         os.sleep(10)
  511.     end
  512. end
  513.  
  514.  
  515. -- ######################################################
  516. -- ##  Actual Run Program                              ##
  517. -- ######################################################
  518. startupConfirmation()
  519. parallel.waitForAll(userConsole,rednetListener,cabinFinder,mover,doorCloser)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement