Advertisement
Guest User

Master 0.2a - ChestMaster

a guest
Sep 9th, 2013
553
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.07 KB | None | 0 0
  1. -- master API by PonyKuu
  2. -- Modified by Levorto (Complete credits to PonyKuu)
  3. -- Version 0.2a
  4.  
  5. -- A little change to standard assert function
  6. _G.assert = function(condition, errMsg, level)
  7. if not condition then
  8. error(errMsg, (tonumber(level) or 1) + 1)
  9. end
  10. return condition
  11. end
  12.  
  13. --[[
  14. *********************************************************************************************
  15. * Communication Part *
  16. *********************************************************************************************
  17. ]]--
  18.  
  19. -- MasterID is the unique identifier of master. Default is 100
  20. local MasterID = 100
  21. -- Channel is the channel Master listens.
  22. local channel
  23. -- A modem.
  24. local modem = peripheral.wrap ("right")
  25.  
  26. -- The first function is used to parse a message received on modem
  27. -- It determines whether the message is valid or not
  28. -- Message should be a table with fields
  29. -- 1) Protocol - must be equal to "KuuNet"
  30. -- 2) ID - that's a sender ID
  31. -- 3) Master - that must be equal to MasterID variable.
  32. -- 4) Type - the type of the module. Used to know how to handle its task requests
  33. -- Some other fields
  34. local function isValid (message)
  35. return message ~= nil and
  36. type(message) == "table" and
  37. message.Protocol == "KuuNet" and
  38. message.ID ~= nil and
  39. message.Master == MasterID
  40. end
  41.  
  42. -- The function that listens for a valid message and returns it
  43. function listen ()
  44. local msg
  45. while not isValid(msg) do
  46. local _, _, _, _, text_message = os.pullEvent("modem_message")
  47. msg = textutils.unserialize (text_message)
  48. end
  49. return msg
  50. end
  51.  
  52. -- And a function to send a response
  53. function response (ID, chan, message)
  54. assert (type(ID) == "number", "Bad module ID: Number required, got "..type(ID), 2)
  55. assert (type(chan) == "number", "Bad channel: Number required, got "..type(chan), 2)
  56. assert (type(message) == "table", "Bad message: Table required, got "..type(message), 2)
  57. message.Protocol = "KuuNet"
  58. message.ID = ID
  59. message.Master = MasterID
  60. modem.transmit (chan+1, chan, textutils.serialize(message))
  61. end
  62.  
  63.  
  64. --[[
  65. *********************************************************************************************
  66. * Module Placement Part *
  67. *********************************************************************************************
  68. ]]--
  69. -- moduleCount is the number of active modules
  70. -- needModules is the number of modules required
  71. local moduleCount = 0
  72. local needModules = 0
  73.  
  74. -- Equipment is a table that contains information about slots with chests and turtles.
  75. -- Fuel is the fuel chest, Stuff is the stuff chest and Turtle is the wireless mining turtle.
  76. -- If Stuff or Fuel is nil, it is not used, otherwize it is the slot where it lies.
  77. -- Make sure that if Master sucks items from Module and then breaks it, the equipment table will still be correct.
  78. local equipment = {Fuel = 1, Stuff = 2, Turtle = 3}
  79. local equipmentTest = {Fuel = 1, Stuff = 2}
  80. function setEquipment (newEquipment)
  81. EnderChests = turtle.getItemCount(1)
  82. write("Number of Modules: ")
  83. print(EnderChests)
  84. assert (type(newEquipment)=="table", "Bad equipment configuration: Table required.", 2)
  85. assert (newEquipment.Turtle, "\"Turtle\" field is required in equipment configuration", 2)
  86. for k, v in pairs(newEquipment) do
  87. assert (type(v) == "number" and v > 0 and v < 17, "Bad equipment field "..k.." : Not a slot number", 2)
  88. end
  89. equipment = newEquipment
  90. equipment = equipmentTest
  91. end
  92.  
  93. count = 0
  94.  
  95. -- Function to place a new module
  96. local function addModule()
  97. --for i = 1,EnderChests do
  98. while true do
  99. EnderCheck = turtle.getItemCount(1)
  100. if EnderCheck == 0 then
  101. isPlacing = false
  102. sleep(5)
  103. print("Done Placing Turtles!")
  104. break
  105. else
  106. turtle.select(3)
  107. if turtle.getItemCount(3) == 0 then
  108. turtle.suckUp()
  109. end
  110. if turtle.getItemCount(3 + 1) > 0 then
  111. turtle.select(3 + 1)
  112. turtle.dropUp()
  113. end
  114. turtle.select(3)
  115. -- put it down
  116. if turtle.place() then
  117. count = count + 1
  118. -- Add a fuel chest if it is set
  119. if equipment.Fuel ~= nil then
  120. turtle.select (equipment.Fuel)
  121. turtle.drop (1)
  122. end
  123. -- And a stuff chest
  124. if equipment.Stuff ~= nil then
  125. turtle.select (equipment.Stuff)
  126. turtle.drop (1)
  127. end
  128. -- select the first slot to make things look good :)
  129. turtle.select (1)
  130. -- Turn on the module
  131. peripheral.call ("front", "turnOn")
  132. end
  133. end
  134. end
  135. if turtle.getItemCount(3) > 0 then
  136. turtle.dropUp()
  137. end
  138. turtle.select(1)
  139. end
  140.  
  141.  
  142. --[[
  143. *********************************************************************************************
  144. * Operation Part *
  145. *********************************************************************************************
  146. ]]--
  147.  
  148. -- tStates is a table that contains all the states of the modules.
  149. -- Indexes of that table are ID's and values are tables with following fields
  150. -- 1) Type - type of the module. Each Type has it's own task table
  151. -- 2) State - the state of the module. List of states is unique for each module type, but there are some common states: "Waiting" and "Returning"
  152. tStates = {}
  153.  
  154. -- There is a function that searches for a free ID. Let's limit the maximun number of IDs to 1024
  155. local function freeID ()
  156. for i = 1, 64 do
  157. if tStates [i] == nil then
  158. return i
  159. end
  160. end
  161. end
  162.  
  163. -- Tasks table. I'll explain it a bit later
  164. tTasks = {}
  165.  
  166. -- Here is the table used to handle the requests.
  167. tRequests = {
  168. -- "Master" is the request sent by new modules. We should assign a new ID to it and remember that ID in tStates table
  169. Master = function (request)
  170. local ID = freeID ()
  171. response (request.ID, channel, {NewID = ID})
  172. tStates[ID] = {State = "Waiting", Type = request.Type}
  173. moduleCount = moduleCount + 1
  174. end,
  175. -- Task is the request used to reques the next thing module should do
  176. -- It uses the tTasks table. There is a function for each module type which returns a response
  177. -- Response may contain coordinate of the next place and id should contain "Task" field
  178. -- This function may do something else, change the state of module and so on.
  179. -- To associate it with the module, sender ID is passed to that function
  180. -- tTasks table should be filled by user of this API
  181. Task = function (request)
  182. response (request.ID, channel, tTasks[request.Type](request.ID))
  183. end,
  184. -- "Returned" is the request sent by module that has returned to Master and waiting there until Master collects it
  185. Returned = function (request)
  186. turtle.select(1)
  187. while turtle.suck () do end -- Get all the items that module had
  188. turtle.dig () -- And get the module back
  189. if turtle.getItemCount(3) > 0 then
  190. turtle.select(3)
  191. turtle.dropUp()
  192. end
  193. if turtle.getItemCount(3+1) > 0 then
  194. turtle.select(3+1)
  195. turtle.dropUp()
  196. end
  197. turtle.select(1)
  198. tStates[request.ID] = nil -- delete that module from our state table
  199. moduleCount = moduleCount - 1 -- and decrease the counter
  200. end
  201. }
  202.  
  203. -- This is the variable used to determine whether the master should place modules
  204. isPlacing = false
  205.  
  206. -- This is the function used to place modules if there are any available
  207. -- It automatically stops placing modules when there is enough modules
  208. function moduleManager ()
  209. -- A function which checks each slot from the Equipment table and returns is there at least one item in each slot
  210. local function freeEquipment ()
  211. for key, value in pairs(equipment) do
  212. if turtle.getItemCount (value) == 0 then
  213. return true
  214. end
  215. end
  216. return true
  217. end
  218. while true do
  219. if isPlacing then
  220. if moduleCount == needModules or not freeEquipment () then
  221. isPlacing = false
  222. else
  223. addModule ()
  224. end
  225. end
  226. sleep (6)
  227. end
  228. end
  229.  
  230. -- The main operation function
  231. -- stateFunction is the function used to determine when master should stop.
  232. -- Master stops when there are no active modules and stateFunction returns false.
  233. -- State function can do other things, such as reinitialization to a new module script and so on, but it shouldn't take too much time to execute
  234. function operate (stateFunction)
  235. assert (type(stateFunction) == "function", "Bad state function: Function required, got "..type(stateFunction), 2)
  236. local server = function ()
  237. -- run is used to determine whether master should run.
  238. local run = stateFunction ()
  239. while run or moduleCount > 0 do
  240. -- check our state function
  241. run = stateFunction()
  242. -- If state function returns false, then stop placing modules
  243. if isPlacing and not run then
  244. isPlacing = false
  245. end
  246. local request = listen ()
  247. tRequests[request.Request](request) -- Just execute the request handler.
  248. end
  249. end
  250. parallel.waitForAny (server, moduleManager)
  251. modem.closeAll ()
  252. end
  253.  
  254. --[[
  255. *********************************************************************************************
  256. * Initialization Part *
  257. *********************************************************************************************
  258. ]]--
  259. -- Position is just the position of the Master.
  260. local Position = {x = 0, y = 0, z = 0, f = 0}
  261. -- New placed modules will request "Position" to know where they are.
  262. local modPosition = {x = 1, y = 0, z = 0, f = 0}
  263.  
  264. -- naviData is used by modules to navigate and not interlock themselves
  265. -- by default they use highway navigation method, which requires prameters x, z and height
  266. local naviData = {x = 0, z = 0, height = 0}
  267.  
  268. function setNavigation (newNaviData)
  269. assert (type(newNaviData) == "table", "Bad navigation data: Table required.", 2)
  270. naviData = newNaviData
  271. end
  272.  
  273. -- Some basic movement and refueling
  274. -- Refuel temporary uses 16 slot to get the fuel out of fuel chest
  275. local function refuel (amount)
  276. local fuel = turtle.getFuelLevel ()
  277. if fuel == "unlimited" or fuel > amount then
  278. return true
  279. else
  280. assert (equipment.Fuel, "Fuel chest is not set while fuel is finite.", 2)
  281. turtle.select (equipment.Fuel)
  282. turtle.placeUp ()
  283. turtle.select (16)
  284. turtle.suckUp ()
  285. while true do
  286. if not turtle.refuel (1) then
  287. turtle.suckUp ()
  288. end
  289. if turtle.getFuelLevel() >= amount then
  290. turtle.dropUp ()
  291. turtle.select (equipment.Fuel)
  292. turtle.digUp ()
  293. return true
  294. end
  295. end
  296. end
  297. end
  298.  
  299. local tMoves = {
  300. forward = turtle.forward,
  301. back = turtle.back
  302. }
  303. local function forceMove (direction)
  304. refuel (1)
  305. while not tMoves[direction]() do
  306. print "Movement obstructed. Waiting"
  307. sleep (1)
  308. end
  309. end
  310.  
  311. -- The function to set the "task" function for the said module type
  312. function setType (Type, taskFunction)
  313. assert (type(Type) == "string", "Bad module type: String required, got "..type(Type), 2)
  314. assert (type(taskFunction) == "function", "Bad task function: Function required, got "..type(taskFunction), 2)
  315. tTasks[Type] = taskFunction
  316. end
  317.  
  318.  
  319. -- This function is used to make all the files required by module to run.
  320. -- Scriptname is the name of the main module script
  321. local function prepareFiles (scriptname)
  322. -- Copy all the required files on the disk
  323. if fs.exists ("/disk/module") then
  324. fs.delete ("/disk/module")
  325. end
  326. fs.copy ("module", "/disk/module")
  327. if fs.exists ("/disk/"..scriptname) then
  328. fs.delete ("/disk/"..scriptname)
  329. end
  330. fs.copy (scriptname, "/disk/"..scriptname)
  331. -- Make a startup file for modules
  332. local file = fs.open ("/disk/startup", "w")
  333. file.writeLine ("shell.run(\"copy\", \"/disk/module\", \"/module\")")
  334. file.writeLine ("shell.run(\"copy\", \"/disk/"..scriptname.."\", \""..scriptname.."\")")
  335. file.writeLine ("shell.run(\""..scriptname.."\")")
  336. file.close()
  337.  
  338. -- Now, make a file with all the data modules need
  339. file = fs.open ("/disk/initdata", "w")
  340. -- Communication data: master ID and communication channel
  341. file.writeLine (textutils.serialize ({MasterID = MasterID, channel = channel}) )
  342. -- Location data:
  343. file.writeLine (textutils.serialize (modPosition))
  344. -- Navigation data:
  345. file.writeLine (textutils.serialize (naviData))
  346. file.close()
  347. end
  348.  
  349. -- The initialization function of the master.
  350. -- Filename is the name of module's script. It will be copied on the disk drive
  351. function init (filename, ID, mainChannel, moduleCount)
  352. assert (fs.exists(filename), "Module script \""..filename.."\" does not exist.", 2)
  353. assert (fs.exists("module"), "Module API is required.", 2)
  354. assert (type(ID) == "string" or type(ID) == "number" , "Bad Master ID: String or number required, got "..type(ID), 2)
  355. assert (type(mainChannel) == "number", "Bad channel: Number required, got "..type(mainChannel), 2)
  356. assert (type(moduleCount) == "number" and moduleCount > 0, "Bad module count: Positive number required.", 2)
  357.  
  358. -- Set the ID of the Master
  359. MasterID = ID
  360. -- Set the main channel
  361. channel = mainChannel
  362.  
  363. -- Next, we need to know the position of the master.
  364. -- If gps is not found, use relative coordinates
  365. modem.open(gps.CHANNEL_GPS)
  366. local gpsPresent = true
  367. local x, y, z = gps.locate(5)
  368. if x == nil then
  369. x, y, z = 0, 0, 0
  370. print "No gps found. Using relative coordinates."
  371. gpsPresent = false
  372. end
  373.  
  374. -- Now we need to move forward to copy files on disk and determine our f
  375. forceMove ("forward")
  376. local newX, newZ = 1, 0 -- if there is no gps, assume that master is facing positive x
  377. if gpsPresent then
  378. newX, __, newZ = gps.locate (5)
  379. end
  380. if channel ~= gps.CHANNEL_GPS then
  381. modem.close(gps.CHANNEL_GPS)
  382. end
  383. -- Determine f by the difference of coordinates.
  384. local xDiff = newX - x
  385. local zDiff = newZ - z
  386. if xDiff ~= 0 then
  387. if xDiff > 0 then
  388. f = 0 -- Positive x
  389. else
  390. f = 2 -- Negative x
  391. end
  392. else
  393. if zDiff > 0 then
  394. f = 1 -- Positive z
  395. else
  396. f = 3 -- Negative z
  397. end
  398. end
  399. -- And set the position and modPosition variables.
  400. Position = {x = x, y = y, z = z, f = f}
  401. modPosition = {x = newX, y = y, z = newZ, f = f}
  402.  
  403. -- Make all the file required
  404. prepareFiles (filename)
  405. -- And go back to initial location
  406. forceMove ("back")
  407.  
  408. -- Set the amount of modules needed. I use "+" because one can run init again to add a different type of module to the operation
  409. needModules = needModules + moduleCount
  410. -- Open the channel to listen for requests
  411. modem.open (channel)
  412. -- And start placing modules
  413. isPlacing = true
  414. end
  415.  
  416. --[[
  417. *********************************************************************************************
  418. * Utility Part *
  419. *********************************************************************************************
  420. ]]--
  421.  
  422. -- This is just a return response added for convinience, because every task function should return the module eventually.
  423. function makeReturnTask ()
  424. -- return position is two block away from Master. So, let's calculate it.
  425. local tShifts = {
  426. [0] = { 1, 0},
  427. [1] = { 0, 1},
  428. [2] = {-1, 0},
  429. [3] = { 0, -1},
  430. }
  431. local xShift, zShift = unpack (tShifts[modPosition.f])
  432. returnX = modPosition.x + xShift
  433. returnZ = modPosition.z + zShift
  434. return { Task = "Return",
  435. x = returnX,
  436. y = modPosition.y,
  437. z = returnZ,
  438. f = (modPosition.f+2)%4, -- basically, reverse direction
  439. }
  440. end
  441.  
  442. -- And a function to make any other task
  443. -- additionalData is optional.
  444. function makeTask (taskName, coordinates, additionalData)
  445. assert (type(taskName) == "string", "Bad task name: String required, got "..type(taskName), 2)
  446. assert (type(coordinates) == "table", "Bad coordinates: Table required, got "..type(coordinates), 2)
  447. local tCoordinates = {"x", "y", "z", "f"}
  448. -- Check if the coordinates are actually numbers
  449. for index, value in ipairs(tCoordinates) do
  450. assert (type(coordinates[value]) == "number", "Bad "..value.." coordinate: Number required, got"..type(coordinates[value]), 2)
  451. end
  452.  
  453. local newTask = {Task = taskName}
  454. for key, value in pairs (coordinates) do
  455. newTask[key] = value
  456. end
  457. if type(additionalData) == "table" then
  458. for key, value in pairs (additionalData) do
  459. newTask[key] = value
  460. end
  461. end
  462. return newTask
  463. end
  464.  
  465. -- These functions are used to get or set the state of the module with specified ID
  466. function getState (ID)
  467. return tStates[ID].State
  468. end
  469. function setState (ID, newState)
  470. assert (tStates[ID], "No module with such ID: "..ID, 2)
  471. assert (type(newState) == "string", "Bad task name: String required, got "..type(newState), 2)
  472. tStates[ID].State = newState
  473. end
  474.  
  475. -- reinit function is just a simplified init, that doesn't update channel, MasterID and Master's position. Use it to change the modules script
  476. function reinit (filename, moduleCount)
  477. assert (fs.exists(filename), "Module script \""..filename.."\" does not exist.", 2)
  478. assert (type(moduleCount) == "number" and moduleCount > 0, "Bad module count: Positive number required.", 2)
  479. -- Prepare the files on disk
  480. forceMove ("forward")
  481. prepareFiles (filename)
  482. forceMove ("back")
  483. -- Set the amount of modules needed.
  484. needModules = needModules + moduleCount
  485. -- Open the channel to listen for requests
  486. modem.open (channel)
  487. -- And start placing modules
  488. isPlacing = true
  489. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement