master_manager.lua
  1. local MODEM_SIDE = "bottom"
  2. local MONITOR_SIDE = "left"
  3. local DB_FILE = "switchmap.db"
  4. local PROTOCOL = "rtm_switch"
  5. local native = term.current()
  6.  
  7. local map = {
  8. version = 1,
  9. gridW = 20,
  10. gridH = 12,
  11. tiles = {},
  12. }
  13.  
  14. local selectedX, selectedY = 1, 1
  15. local currentTool = "rail_h"
  16. local status = "Indulas..."
  17. local monitor = nil
  18. local lastTouch = nil
  19. local running = true
  20.  
  21. local tileTypes = {
  22. "empty",
  23. "rail_h",
  24. "rail_v",
  25. "curve_ne",
  26. "curve_nw",
  27. "curve_se",
  28. "curve_sw",
  29. "cross",
  30. "switch_cross",
  31. "signal",
  32. "label",
  33. }
  34.  
  35. local tileChars = {
  36. empty = " ",
  37. rail_h = "=",
  38. rail_v = "|",
  39. curve_ne = "/",
  40. curve_nw = "\\",
  41. curve_se = "\\",
  42. curve_sw = "/",
  43. cross = "X",
  44. switch_cross = "S",
  45. signal = "!",
  46. label = "L",
  47. }
  48.  
  49. local function center(text, y, txt, bg)
  50. local w, _ = term.getSize()
  51. term.setCursorPos(math.max(1, math.floor((w - #text) / 2) + 1), y)
  52. if bg then term.setBackgroundColor(bg) end
  53. if txt then term.setTextColor(txt) end
  54. term.write(text)
  55. end
  56.  
  57. local function setStatus(msg)
  58. status = msg
  59. end
  60.  
  61. local function openNet()
  62. if peripheral.getType(MODEM_SIDE) ~= "modem" then
  63. setStatus("Nincs modem a bottom oldalon")
  64. return false
  65. end
  66. if not rednet.isOpen(MODEM_SIDE) then
  67. rednet.open(MODEM_SIDE)
  68. end
  69. return true
  70. end
  71.  
  72. local function getTile(x, y)
  73. for i = 1, #map.tiles do
  74. local t = map.tiles[i]
  75. if t.x == x and t.y == y then return t, i end
  76. end
  77. return nil, nil
  78. end
  79.  
  80. local function setTile(x, y, data)
  81. local _, idx = getTile(x, y)
  82. if data.type == "empty" then
  83. if idx then table.remove(map.tiles, idx) end
  84. return
  85. end
  86. if idx then
  87. map.tiles[idx] = data
  88. else
  89. table.insert(map.tiles, data)
  90. end
  91. end
  92.  
  93. local function saveDb()
  94. local h = fs.open(DB_FILE, "w")
  95. h.write(textutils.serialize(map))
  96. h.close()
  97. setStatus("Mentve: " .. DB_FILE)
  98. end
  99.  
  100. local function loadDb()
  101. if not fs.exists(DB_FILE) then
  102. setStatus("Nincs adatbazis, uj keszul")
  103. return
  104. end
  105. local h = fs.open(DB_FILE, "r")
  106. local raw = h.readAll()
  107. h.close()
  108. local t = textutils.unserialize(raw)
  109. if type(t) == "table" then
  110. map = t
  111. setStatus("Betoltve: " .. DB_FILE)
  112. else
  113. setStatus("Hibas adatbazis")
  114. end
  115. end
  116.  
  117. local function tileColor(tile)
  118. if not tile then return colors.black, colors.gray end
  119. if tile.type == "rail_h" or tile.type == "rail_v" or tile.type == "curve_ne" or tile.type == "curve_nw" or tile.type == "curve_se" or tile.type == "curve_sw" or tile.type == "cross" then
  120. return colors.black, colors.lightGray
  121. elseif tile.type == "switch_cross" then
  122. if tile.offline then return colors.black, colors.red end
  123. if tile.state == "cross" then return colors.black, colors.orange end
  124. return colors.black, colors.lime
  125. elseif tile.type == "signal" then
  126. return colors.black, colors.red
  127. elseif tile.type == "label" then
  128. return colors.black, colors.cyan
  129. end
  130. return colors.black, colors.white
  131. end
  132.  
  133. local function drawMap()
  134. if not monitor then return end
  135. monitor.setBackgroundColor(colors.black)
  136. monitor.clear()
  137. local mw, mh = monitor.getSize()
  138. local ox, oy = 2, 2
  139.  
  140. monitor.setCursorPos(2, 1)
  141. monitor.setTextColor(colors.cyan)
  142. monitor.write("RTM TERKEP")
  143.  
  144. for y = 1, map.gridH do
  145. for x = 1, map.gridW do
  146. local sx = ox + (x - 1) * 2
  147. local sy = oy + (y - 1)
  148. if sx <= mw - 1 and sy <= mh - 2 then
  149. local tile = getTile(x, y)
  150. local bg = colors.black
  151. local txt = colors.gray
  152. local ch = "."
  153. if tile then
  154. bg, txt = tileColor(tile)
  155. ch = tileChars[tile.type] or "?"
  156. end
  157. if x == selectedX and y == selectedY then
  158. monitor.setBackgroundColor(colors.blue)
  159. monitor.setTextColor(colors.white)
  160. else
  161. monitor.setBackgroundColor(bg)
  162. monitor.setTextColor(txt)
  163. end
  164. monitor.setCursorPos(sx, sy)
  165. monitor.write(ch .. " ")
  166. end
  167. end
  168. end
  169.  
  170. monitor.setBackgroundColor(colors.black)
  171. monitor.setTextColor(colors.white)
  172. monitor.setCursorPos(2, mh)
  173. monitor.write("Erintes: valto/kurzor")
  174. end
  175.  
  176. local function prompt(text, default)
  177. term.setBackgroundColor(colors.black)
  178. term.setTextColor(colors.white)
  179. write(text)
  180. local v = read(nil, nil, nil, default)
  181. return v
  182. end
  183.  
  184. local function editTileAt(x, y)
  185. term.redirect(native)
  186. term.setBackgroundColor(colors.black)
  187. term.setTextColor(colors.white)
  188. term.clear()
  189. term.setCursorPos(1,1)
  190. local tile = getTile(x, y) or {x = x, y = y, type = currentTool}
  191.  
  192. print("Szerkesztes: " .. x .. "," .. y)
  193. print("Tipusok:")
  194. for i = 1, #tileTypes do
  195. print(i .. " - " .. tileTypes[i])
  196. end
  197. write("Tipus szama [" .. tile.type .. "]: ")
  198. local choice = tonumber(read())
  199. if choice and tileTypes[choice] then tile.type = tileTypes[choice] end
  200.  
  201. if tile.type == "empty" then
  202. setTile(x, y, {x = x, y = y, type = "empty"})
  203. setStatus("Torolve: " .. x .. "," .. y)
  204. return
  205. end
  206.  
  207. tile.x = x
  208. tile.y = y
  209.  
  210. if tile.type == "switch_cross" then
  211. tile.name = prompt("Nev [" .. (tile.name or "Valto") .. "]: ", tile.name or "Valto")
  212. tile.slaveId = tonumber(prompt("Slave ID [" .. tostring(tile.slaveId or "") .. "]: ", tile.slaveId and tostring(tile.slaveId) or ""))
  213. tile.state = prompt("Allapot (straight/cross) [" .. (tile.state or "straight") .. "]: ", tile.state or "straight")
  214. tile.protocol = PROTOCOL
  215. tile.offline = false
  216. elseif tile.type == "label" then
  217. tile.text = prompt("Felirat [" .. (tile.text or "") .. "]: ", tile.text or "")
  218. elseif tile.type == "signal" then
  219. tile.name = prompt("Jelzo neve [" .. (tile.name or "Jelzo") .. "]: ", tile.name or "Jelzo")
  220. end
  221.  
  222. setTile(x, y, tile)
  223. setStatus("Elem mentve: " .. tile.type .. " @ " .. x .. "," .. y)
  224. end
  225.  
  226. local function sendSwitch(tile, newState)
  227. if not tile or tile.type ~= "switch_cross" then return end
  228. if not tile.slaveId then
  229. setStatus("Nincs slave ID")
  230. return
  231. end
  232. if not openNet() then return end
  233.  
  234. rednet.send(tile.slaveId, {cmd = "set", state = newState}, tile.protocol or PROTOCOL)
  235. local timer = os.startTimer(2)
  236. while true do
  237. local e, a, b, c = os.pullEvent()
  238. if e == "rednet_message" then
  239. local senderId, msg, protocol = a, b, c
  240. if senderId == tile.slaveId and protocol == (tile.protocol or PROTOCOL) and type(msg) == "table" and msg.ok then
  241. tile.state = msg.state or newState
  242. tile.offline = false
  243. setStatus("Valasz: " .. (tile.name or "valt") .. " -> " .. tile.state)
  244. return
  245. end
  246. elseif e == "timer" and a == timer then
  247. tile.offline = true
  248. setStatus("Timeout slave #" .. tile.slaveId)
  249. return
  250. end
  251. end
  252. end
  253.  
  254. local function pingAllSwitches()
  255. if not openNet() then return end
  256. for _, tile in ipairs(map.tiles) do
  257. if tile.type == "switch_cross" and tile.slaveId then
  258. rednet.send(tile.slaveId, {cmd = "ping"}, tile.protocol or PROTOCOL)
  259. tile.offline = true
  260. end
  261. end
  262.  
  263. local timer = os.startTimer(1.5)
  264. while true do
  265. local e, a, b, c = os.pullEvent()
  266. if e == "rednet_message" then
  267. local senderId, msg, protocol = a, b, c
  268. if type(msg) == "table" and msg.ok and protocol == PROTOCOL then
  269. for _, tile in ipairs(map.tiles) do
  270. if tile.type == "switch_cross" and tile.slaveId == senderId then
  271. tile.offline = false
  272. tile.state = msg.state or tile.state
  273. end
  274. end
  275. end
  276. elseif e == "timer" and a == timer then
  277. setStatus("Ping kesz")
  278. return
  279. end
  280. end
  281. end
  282.  
  283. local function handleMonitorTouch(side, x, y)
  284. if side ~= MONITOR_SIDE then return end
  285. local gx = math.floor((x - 2) / 2) + 1
  286. local gy = (y - 2) + 1
  287. if gx >= 1 and gx <= map.gridW and gy >= 1 and gy <= map.gridH then
  288. selectedX, selectedY = gx, gy
  289. lastTouch = {x = gx, y = gy}
  290. local tile = getTile(gx, gy)
  291. if tile and tile.type == "switch_cross" then
  292. local ns = tile.state == "straight" and "cross" or "straight"
  293. sendSwitch(tile, ns)
  294. else
  295. setStatus("Kijelolve: " .. gx .. "," .. gy)
  296. end
  297. end
  298. end
  299.  
  300. local function uiLoop()
  301. while running do
  302. term.redirect(native)
  303. term.setBackgroundColor(colors.black)
  304. term.setTextColor(colors.white)
  305. term.clear()
  306. term.setCursorPos(1,1)
  307. print("RTM HALOZATI MANAGER")
  308. print("====================")
  309. print("Kijelolt tile: " .. selectedX .. "," .. selectedY)
  310. local tile = getTile(selectedX, selectedY)
  311. if tile then
  312. print("Tipus: " .. tile.type)
  313. if tile.type == "switch_cross" then
  314. print("Slave: " .. tostring(tile.slaveId or "nincs"))
  315. print("Allapot: " .. tostring(tile.state or "?"))
  316. print("Offline: " .. tostring(tile.offline or false))
  317. end
  318. else
  319. print("Tipus: ures")
  320. end
  321. print("")
  322. print("1 - Tile szerkesztes")
  323. print("2 - Kurzor mozgas")
  324. print("3 - Valto valtasa")
  325. print("4 - Osszes slave ping")
  326. print("5 - Mentes")
  327. print("6 - Betoltes")
  328. print("7 - Palya meret")
  329. print("8 - Kilepes")
  330. print("")
  331. print("Status: " .. status)
  332. write("> ")
  333. local cmd = read()
  334.  
  335. if cmd == "1" then
  336. editTileAt(selectedX, selectedY)
  337. elseif cmd == "2" then
  338. local nx = tonumber(prompt("X: ", tostring(selectedX))) or selectedX
  339. local ny = tonumber(prompt("Y: ", tostring(selectedY))) or selectedY
  340. selectedX = math.max(1, math.min(map.gridW, nx))
  341. selectedY = math.max(1, math.min(map.gridH, ny))
  342. setStatus("Kurzor mozgatva")
  343. elseif cmd == "3" then
  344. local t = getTile(selectedX, selectedY)
  345. if t and t.type == "switch_cross" then
  346. sendSwitch(t, t.state == "straight" and "cross" or "straight")
  347. else
  348. setStatus("Itt nincs valto")
  349. end
  350. elseif cmd == "4" then
  351. pingAllSwitches()
  352. elseif cmd == "5" then
  353. saveDb()
  354. elseif cmd == "6" then
  355. loadDb()
  356. elseif cmd == "7" then
  357. local gw = tonumber(prompt("Grid W: ", tostring(map.gridW))) or map.gridW
  358. local gh = tonumber(prompt("Grid H: ", tostring(map.gridH))) or map.gridH
  359. map.gridW = math.max(5, gw)
  360. map.gridH = math.max(5, gh)
  361. setStatus("Palya meret frissitve")
  362. elseif cmd == "8" then
  363. running = false
  364. break
  365. else
  366. setStatus("Ismeretlen parancs")
  367. end
  368. drawMap()
  369. end
  370. end
  371.  
  372. local function monitorLoop()
  373. while running do
  374. drawMap()
  375. local e, side, x, y = os.pullEvent()
  376. if e == "monitor_touch" then
  377. handleMonitorTouch(side, x, y)
  378. drawMap()
  379. elseif e == "monitor_resize" then
  380. drawMap()
  381. end
  382. end
  383. end
  384.  
  385. local function netLoop()
  386. while running do
  387. local senderId, msg, protocol = rednet.receive(PROTOCOL)
  388. if type(msg) == "table" and msg.ok then
  389. for _, tile in ipairs(map.tiles) do
  390. if tile.type == "switch_cross" and tile.slaveId == senderId then
  391. tile.offline = false
  392. tile.state = msg.state or tile.state
  393. setStatus("Frissult: " .. (tile.name or ("#" .. senderId)))
  394. end
  395. end
  396. drawMap()
  397. end
  398. end
  399. end
  400.  
  401. loadDb()
  402. openNet()
  403. if peripheral.getType(MONITOR_SIDE) == "monitor" then
  404. monitor = peripheral.wrap(MONITOR_SIDE)
  405. monitor.setTextScale(0.5)
  406. else
  407. setStatus("Nincs monitor a left oldalon")
  408. end
  409.  
  410. if monitor then
  411. parallel.waitForAny(uiLoop, monitorLoop, netLoop)
  412. else
  413. parallel.waitForAny(uiLoop, netLoop)
  414. end
  415.  
  416. term.redirect(native)
  417. term.setBackgroundColor(colors.black)
  418. term.setTextColor(colors.white)
  419. term.clear()
  420. term.setCursorPos(1,1)
  421. print("Manager leallva")
  422.  
Pasted 2026-04-25 18:19:43

Short link: