Xixili

Elevator Motor

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