diff --git a/programs.info b/programs.info index 0711090..63f2443 100644 --- a/programs.info +++ b/programs.info @@ -153,6 +153,16 @@ "libevent","checkarg","util","simple_data_structure" } }, + ["noserver-station"]={ + title="NoServer Station", + info="Railway control system used in NoServer", + files={ + "programs/noserver-station/station.lua" + }, + requires={ + "libevent" + } + }, ["prtsc"]={ title="Print Screen", info="Save screen to file when PrtSc key is pressed.", diff --git a/programs/noserver-station/station.lua b/programs/noserver-station/station.lua new file mode 100644 index 0000000..b1b563c --- /dev/null +++ b/programs/noserver-station/station.lua @@ -0,0 +1,179 @@ +require("libevent") +local event=require('event') +local computer=require('computer') +local component=require('component') +local sides=require('sides') + +---------- Config ---------- +local addr_redin = "" +local addr_redout = "" +local addr_next_station = "" +local limit_train_interval = 10 -- Trains must be separate with this time, should be greater than 2. +---------------------------- + +local note = component.iron_noteblock +local sign = component.sign +local redin = component.proxy(addr_redin) +local redout = component.proxy(addr_redout) + +if not redin or not redout or not sign or not note then + print("device not ready.") + return +end + +local track_side_map = { + sides.north, + sides.east, + sides.south, + sides.west +} +local side_track_map = { + [sides.north] = 1, + [sides.east] = 2, + [sides.south] = 3, + [sides.west] = 4 +} + +local function setRed(track_id) + redout.setOutput(track_side_map[track_id], 0) +end + +local function setGreen(track_id) + redout.setOutput(track_side_map[track_id], 15) +end + +local function printf(fmt, ...) + print(string.format(fmt, ...)) +end + +local workers = {} +local bus = CreateEventBus() +bus:listen("redstone_changed") +bus:listen("_eve_timer") +bus:listen("interrupted") + +local function StartTask(fn) + local c = coroutine.create(fn) + local ok, ticket = coroutine.resume(c) + if coroutine.status(c) ~= "dead" then + workers[ticket] = c + end +end + +local _ticket_holder = 0 + +local function GenerateTicket() + _ticket_holder = _ticket_holder + 1 + return string.format("tk_%d", _ticket_holder) +end + +local function listenerSleep(second) + local ticket = GenerateTicket() + event.timer(second, function() + event.push("_eve_timer", ticket) + end, 1) + -- print("<-- listenerSleep yield.", ticket) + coroutine.yield(ticket) + -- print("--> listenerSleep resume.", ticket) +end + +local function stationAlarm(second) + for i=1, second*2 do + component.iron_noteblock.playNote(6, 6) + listenerSleep(0.5) + end +end + +-- Try to acquire token that allow train leave station +local global_token_locked = false + +local function acquireToken() + if not global_token_locked then + global_token_locked = true + return + end + while global_token_locked do + listenerSleep(1) + end + global_token_locked = true +end + +local function releaseToken() + global_token_locked = false +end + +local sign_states = {} + +local function stationSignUpdate() + local t = {} + for i, s in pairs(sign_states) do + table.insert(t, string.format("[%s]: %s", i, s)) + end + sign.setValue(sides.up, table.concat(t, '\n')) +end + +local function stationAddSign(track_id, status) + sign_states[track_id] = status + stationSignUpdate() +end + +local function stationClearSign(track_id) + sign_states[track_id] = nil + stationSignUpdate() +end + +print("Press Ctrl+C to stop.") +while true do + local e = bus:next(-1, 0.05) + -- print(e.event) + if e.event == "interrupted" then + break + elseif e.event == "redstone_changed" then + if e.address == addr_redin and e.newValue == 15 then + local track_id = side_track_map[e.side] + if track_id then + StartTask(function() + printf("got train at track %s", track_id) + stationAddSign(track_id, "Waiting") + listenerSleep(15) + acquireToken() + printf("track %s acquired token.", track_id) + stationAddSign(track_id, "About to leave") + printf("start alarm for track %s", track_id) + stationAlarm(8) + stationAddSign(track_id, "Leaving...") + printf("track %s set to green.", track_id) + setGreen(track_id) + listenerSleep(2.5) + printf("track %s set to red.", track_id) + setRed(track_id) + stationClearSign(track_id) + listenerSleep(limit_train_interval - 2) + releaseToken() + printf("token released at track %s", track_id) + end) + end + end + elseif e.event == "_eve_timer" then + local ticket = e.data[1] + if type(ticket) == "table" then + for k, v in pairs(ticket) do + print(k,v) + end + end + -- print(" got ticket", ticket) + if workers[ticket] then + local c = workers[ticket] + workers[ticket] = nil + -- print(" resuming thread", c) + local ok, new_ticket = coroutine.resume(c) + if coroutine.status(c) ~= "dead" then + workers[new_ticket] = c + end + else + -- print(" ticket not found.", ticket) + end + end +end + +bus:close()