diff options
-rw-r--r-- | LICENSE | 24 | ||||
-rw-r--r-- | depends.txt | 1 | ||||
-rw-r--r-- | fw.lua | 1094 | ||||
-rw-r--r-- | init.lua | 345 | ||||
-rw-r--r-- | textures/ltc4000e_cabinet_front.png | bin | 0 -> 3833 bytes | |||
-rw-r--r-- | textures/ltc4000e_cabinet_sides.png | bin | 0 -> 3213 bytes | |||
-rw-r--r-- | textures/ltc4000e_formspec_bg.png | bin | 0 -> 1735 bytes |
7 files changed, 1464 insertions, 0 deletions
@@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to <http://unlicense.org/> diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..da1d119 --- /dev/null +++ b/depends.txt @@ -0,0 +1 @@ +digilines @@ -0,0 +1,1094 @@ +--LTC-4000E Firmware +--A product of Advanced Mesecons Devices, a Cheapie Systems company +--This is free and unencumbered software released into the public domain. +--See http://unlicense.org/ for more information + +--Lookup tables for human-readable strings and such +lttypes = {"Permissive","Protected","Yellow Arrow Prot/Perm","Circular Green Prot/Perm"} +pedtypes = {"Unsignalized","Signalized"} +signaltypes = {"Streets 1","Roads","Streets 2"} +pedbuttontypes = {"Normal","TrafficNeXt Compatibility"} +shortphases = {"O","G","R","Y","FR","FY","FG","RY"} -- Green/red switched for, uh, reasons +phases = {O = "Off",R = "Red",Y = "Yellow",G = "Green",RY = "RedYellow",FR = "FlashRed",FY = "FlashYellow",FG = "FlashGreen"} +monitortypes = {"Straight","Left Turn","Pedestrian"} +modes = {"Sensor","Timer","Phase Lock"} +panellock = {"Unlocked","Locked"} +logmodes = {"Quiet","Normal","Verbose"} + +--Used for reverse lookups +function pivot(table) + local out = {} + for k,v in pairs(table) do + out[v] = k + end + return(out) +end + +--Phase setter thing +function setlight(light,phase) + mem.currentphase[light] = phase +end + +--Log filter/formatter +function log(desc,verboseonly) + if mem.logmode ~= 1 and (mem.logmode == 3 or not verboseonly) then + print("[LTC-4000E @ "..mem.name.."] "..desc) + end +end + +--Fault logger +function logfault(desc,fatal) + if fatal then mem.phaselocked = true end + log(string.format("%s FAULT: ",(fatal and "FATAL" or "Non-fatal"))..desc,false) + local date = os.datetable() + local time = string.format(" - %04u-%02u-%02u %02u:%02u:%02u",date.year,date.month,date.day,date.hour,date.min,date.sec) + table.insert(mem.faultlog,1,desc..time) +end + +--Checks if the schedule is currently active +function isscheduled() + local hour = os.datetable().hour + if mem.schedstart < mem.schedend then + --Active during the day + return(hour >= mem.schedstart and hour < mem.schedend) + elseif mem.schedstart > mem.schedend then + --Active during the night + return(hour >= mem.schedstart or hour < mem.schedend) + else + --Disabled + return(false) + end +end + +--Default parameters +if event.type == "program" then + mem.menu = "run" + mem.monitor = 1 + mem.monflash = false + if not mem.schedstart then mem.schedstart = 0 end + if not mem.schedend then mem.schedend = 0 end + if not mem.normalmode then + if mem.phaselocked then + mem.normalmode = 3 + else + mem.normalmode = 1 + end + end + if not mem.schedmode then mem.schedmode = 3 end + if not mem.ltatype then mem.ltatype = 1 end + if not mem.ltbtype then mem.ltbtype = 1 end + if not mem.ltctype then mem.ltctype = 1 end + if not mem.ltdtype then mem.ltdtype = 1 end + if not mem.pedatype then mem.pedatype = 1 end + if not mem.pedbtype then mem.pedbtype = 1 end + if not mem.pedctype then mem.pedctype = 1 end + if not mem.peddtype then mem.peddtype = 1 end + if not mem.signaltype then mem.signaltype = 2 end + if not mem.pedbuttontype then mem.pedbuttontype = 1 end + if not mem.allreda then mem.allreda = 2 end + if not mem.allredb then mem.allredb = 2 end + if not mem.yellowa then mem.yellowa = 3 end + if not mem.yellowb then mem.yellowb = 3 end + if not mem.minsidegreen then mem.minsidegreen = (mem.sidegreen or 7) end + if not mem.gapout then mem.gapout = 3 end + if not mem.maxsidegreen then mem.maxsidegreen = 20 end + if not mem.mindwell then mem.mindwell = 10 end + if not mem.pedwarn then mem.pedwarn = 7 end + if not mem.sideped then mem.sideped = 5 end + if not mem.llturn then mem.llturn = 4 end + if not mem.name then mem.name = "Unnamed Intersection" end + if not mem.logmode then mem.logmode = 2 end + if not mem.lock then mem.lock = 2 end + mem.stats = {a = 0,b = 0,c = 0,d = 0,at = 0,bt = 0,ct = 0,dt = 0,ap = 0,bp = 0,cp = 0,dp = 0,cycles = 0,lastreset = os.time()} + mem.det = {} + mem.busy = false + mem.preempt = nil + mem.cycle = nil + mem.currentphase = {a = "G",b = "R",c = "G",d = "R",at = "R",bt = "R",ct = "R",dt = "R",ap = "R",bp = "R",cp = "R",dp = "R"} + if not mem.phaselock then mem.phaselock = {a = "FR",b = "FR",c = "FR",d = "FR",at = "FR",bt = "FR",ct = "FR",dt = "FR",ap = "O",bp = "O",cp = "O",dp = "O"} end + mem.phaselocked = false + mem.faultlog = {} + log("Chip programmed",true) +end + +--Handle special modes +was_phaselocked = mem.phaselocked +was_timed = mem.timed +mem.phaselocked = (isscheduled() and mem.schedmode == 3) or (not isscheduled() and mem.normalmode == 3) +mem.timed = (isscheduled() and mem.schedmode == 2) or (not isscheduled() and mem.normalmode == 2) +if was_phaselocked and not mem.phaselocked then + mem.currentphase = {a = "G",b = "R",c = "G",d = "R",at = "R",bt = "R",ct = "R",dt = "R",ap = "R",bp = "R",cp = "R",dp = "R"} + mem.cycle = "mindwell" + mem.busy = true + interrupt(0,"tick") +end +if mem.timed then + mem.det = {a = true,b = true,c = true,d = true,ap = true,bp = true,cp = true,dp = true,at = true,bt = true,ct = true,dt = true} +elseif was_timed then + mem.det = {} +end + +--Detector signal handling +if (not mem.timed and not mem.phaselocked and not mem.preempt) and event.type == "digiline" and string.sub(event.channel,1,9) == "detector_" then + local detname = string.sub(event.channel,10) + if mem.stats[detname] then + mem.stats[detname] = mem.stats[detname] + 1 + end + if detname == "a" or detname == "c" then + log("Ignoring stats-only detector "..detname,true) + elseif (detname == "at" and mem.ltatype == 1) then + logfault("AT disabled but detector present",false) + elseif (detname == "bt" and mem.ltbtype == 1) then + logfault("BT disabled but detector present",false) + elseif (detname == "ct" and mem.ltctype == 1) then + logfault("CT disabled but detector present",false) + elseif (detname == "dt" and mem.ltdtype == 1) then + logfault("DT disabled but detector present",false) + elseif (detname == "ap" and mem.pedatype == 1) then + logfault("AP disabled but detector present",false) + elseif (detname == "bp" and mem.pedbtype == 1) then + logfault("BP disabled but detector present",false) + elseif (detname == "cp" and mem.pedctype == 1) then + logfault("CP disabled but detector present",false) + elseif (detname == "dp" and mem.peddtype == 1) then + logfault("DP disabled but detector present",false) + else + log("Detector "..detname.." activated",true) + mem.det[detname] = true + end +end + +if event.type == "digiline" and mem.pedbuttontype == 2 and event.channel == "pedbutton" then + if event.msg == "main" then + log("Emulating detector_ap/cp for TrafficNeXt compatibility",true) + mem.det.ap = true + mem.det.cp = true + elseif event.msg == "side" then + log("Emulating detector_bp/dp for TrafficNeXt compatibility",true) + mem.det.bp = true + mem.det.dp = true + else + logfault("Unrecognized pedbutton type "..event.msg,false) + end +end + +--Volume Density Logic +if event.type == "interrupt" and (event.iid == "gapout" or event.iid == "maxgreen") and (mem.cycle == "straight2.5" or mem.cycle == "straight3") then + if mem.cycle == "straight2.5" or (mem.currentphase.ap ~= "FR" and mem.currentphase.cp ~= "FR") then + if event.iid == "gapout" then + log("Gapped out",true) + elseif event.iid == "maxgreen" then + log("Reached maximum green",true) + end + --Either gapped out or reached max green, go to next step + interrupt(0,"tick") + + interrupt(nil,"gapout") + end +end + +if (event.channel == "detector_b" or event.channel == "detector_d") and (mem.cycle == "straight2.5" or mem.cycle == "straight3") then + --Car detected, reset gap-out timer + interrupt(mem.gapout,"gapout") +end + +--Preemption logic +if event.type == "digiline" and string.sub(event.channel,1,8) == "preempt_" then + log("Preemption detector activated",true) + mem.preempt = string.sub(event.channel,9,10) + log("Entering preemption on approach "..mem.preempt.." from state "..(mem.cycle or "idle"),true) + mem.busy = true + local pedactive = (mem.currentphase.ap ~= "R" or mem.currentphase.bp ~= "R" or mem.currentphase.cp ~= "R" or mem.currentphase.dp ~= "R") + for k,v in pairs(mem.currentphase) do + if pedactive then + if k == "ap" or k == "bp" or k == "cp" or k == "dp" then + if v == "G" then setlight(k,"FR") end + end + else + if v ~= "R" then + setlight(k,"Y") + end + end + end + if pedactive then + mem.cycle = "preempt_yellow" + interrupt(mem.pedwarn,"tick") + else + mem.cycle = "preempt_allred" + interrupt(mem.yellowa,"tick") + end +end + +--Phase logic for already-running cycles +if mem.busy and event.type == "interrupt" and event.iid == "tick" and not mem.phaselocked then + log("Continuing existing cycle at phase "..mem.cycle,true) + if mem.cycle == "preempt_yellow" then + for k,v in pairs(mem.currentphase) do + if k == "ap" or k == "bp" or k == "cp" or k == "dp" then + setlight(k,"R") + else + if v ~= "R" then setlight(k,"Y") end + end + mem.cycle = "preempt_allred" + interrupt(mem.yellowa,"tick") + end + elseif mem.cycle == "preempt_allred" then + mem.currentphase = {a = "R",b = "R",c = "R",d = "R",at = "R",bt = "R",ct = "R",dt = "R",ap = "R",bp = "R",cp = "R",dp = "R"} + mem.cycle = "preempt_green" + interrupt(mem.allreda,"tick") + elseif mem.cycle == "preempt_green" then + if mem["lt"..mem.preempt.."type"] ~= 1 then + mem.cycle = mem.preempt.."lead1" + else + if mem.preempt == "a" or mem.preempt == "c" then + mem.cycle = "mindwell" + else + mem.cycle = "straight2" + end + end + mem.preempt = nil + mem.det = {} + interrupt(0,"tick") + elseif mem.cycle == "straight1" then + mem.cycle = "straight2" + setlight("a","R") + setlight("c","R") + setlight("at","R") + setlight("ct","R") + --Branch over to B/D leading left turn if necessary + if (mem.det.bt and mem.det.dt and mem.ltbtype ~= 1 and mem.ltdtype ~= 1) or (((mem.det.bt and mem.ltbtype ~= 1) or (mem.det.dt and mem.ltdtype ~= 1)) and ((mem.det.ap and mem.pedatype ~= 1) or (mem.det.cp and mem.pedctype ~= 1))) then + mem.cycle = "bdlead1" + elseif mem.det.bt and mem.ltbtype ~= 1 then + mem.cycle = "blead1" + elseif mem.det.dt and mem.ltdtype ~= 1 then + mem.cycle = "dlead1" + end + --If nobody wants to go straight or turn on B/D, but there are cars an AT or CT, serve them instead + if not mem.det.b and not mem.det.d and not mem.det.bt and not mem.det.dt and not mem.det.ap and not mem.det.cp then + if mem.det.at and mem.ltatype ~= 1 and mem.det.ct and mem.ltctype ~= 1 then + mem.cycle = "aclead1" + elseif mem.det.at and mem.ltatype ~= 1 then + mem.cycle = "alead1" + elseif mem.det.ct and mem.ltctype ~= 1 then + mem.cycle = "clead1" + end + end + interrupt(mem.allreda,"tick") + elseif mem.cycle == "straight2" then + mem.cycle = "straight3" + setlight("b","G") + setlight("d","G") + if mem.ltbtype == 3 then setlight("bt","FY") end + if mem.ltdtype == 3 then setlight("dt","FY") end + if (mem.det.ap and mem.pedatype ~= 1) or (mem.det.cp and mem.pedctype ~= 1) and not mem.preempt then + mem.cycle = "straight2.5" + if mem.pedatype ~= 1 then setlight("ap","G") end + if mem.pedctype ~= 1 then setlight("cp","G") end + end + log("Entering volume density control",true) + interrupt(mem.minsidegreen,"gapout") + interrupt(mem.maxsidegreen,"maxgreen") + elseif mem.cycle == "straight2.5" then + mem.cycle = "straight3" + mem.det.ap = nil + mem.det.cp = nil + if mem.pedatype ~= 1 then setlight("ap","FR") end + if mem.pedctype ~= 1 then setlight("cp","FR") end + interrupt(mem.pedwarn,"tick") + elseif mem.cycle == "straight3" then + mem.cycle = "straight4" + setlight("b","Y") + setlight("d","Y") + if mem.pedatype ~= 1 then setlight("ap","R") end + if mem.pedctype ~= 1 then setlight("cp","R") end + if mem.ltbtype == 3 then setlight("bt","Y") end + if mem.ltdtype == 3 then setlight("dt","Y") end + mem.det.b = nil + mem.det.d = nil + interrupt(mem.yellowb,"tick") + elseif mem.cycle == "straight4" then + mem.cycle = "mindwell" + setlight("b","R") + setlight("d","R") + setlight("bt","R") + setlight("dt","R") + --Branch over to A/C leading left turn if necessary + if mem.det.at and mem.det.ct and mem.ltatype ~= 1 and mem.ltctype ~= 1 then + mem.cycle = "aclead1" + elseif mem.det.at and mem.ltatype ~= 1 then + mem.cycle = "alead1" + elseif mem.det.ct and mem.ltctype ~= 1 then + mem.cycle = "clead1" + end + interrupt(mem.allredb,"tick") + elseif mem.cycle == "mindwell" then + mem.cycle = "reset" + setlight("a","G") + setlight("c","G") + if mem.ltatype == 3 then setlight("at","FY") end + if mem.ltctype == 3 then setlight("ct","FY") end + if (mem.det.bp and mem.pedbtype ~= 1) or (mem.det.dp and mem.peddtype ~= 1) then + mem.cycle = "sideped1" + if mem.ltatype == 3 and mem.pedbtype ~= 1 then setlight("at","R") end + if mem.ltctype == 3 and mem.peddtype ~= 1 then setlight("ct","R") end + if mem.ltatype == 3 or mem.ltctype == 3 then + interrupt(mem.yellowa,"tick") + else + interrupt(0,"tick") + end + else + interrupt(mem.mindwell,"tick") + end + elseif mem.cycle == "bdlead1" then + mem.cycle = "bdlead2" + setlight("bt","G") + setlight("dt","G") + interrupt(mem.llturn,"tick") + elseif mem.cycle == "bdlead2" then + mem.det.bt = nil + mem.det.dt = nil + if mem.ltbtype ~= 3 and mem.ltdtype ~= 3 then + mem.cycle = "bdlead3" + else + mem.cycle = "straight2" + end + setlight("bt","Y") + setlight("dt","Y") + interrupt(mem.yellowb,"tick") + elseif mem.cycle == "bdlead3" then + mem.cycle = "straight2" + setlight("bt","R") + setlight("dt","R") + interrupt(mem.allredb,"tick") + elseif mem.cycle == "blead1" then + mem.cycle = "blead2" + setlight("bt","G") + setlight("b","G") + interrupt(mem.llturn,"tick") + elseif mem.cycle == "blead2" then + if mem.ltbtype ~= 3 then + mem.cycle = "blead3" + else + mem.cycle = "straight2" + end + mem.det.bt = nil + setlight("bt","Y") + interrupt(mem.yellowb,"tick") + elseif mem.cycle == "blead3" then + mem.cycle = "straight2" + setlight("bt","R") + interrupt(mem.allredb,"tick") + elseif mem.cycle == "dlead1" then + mem.cycle = "dlead2" + setlight("dt","G") + setlight("d","G") + interrupt(mem.llturn,"tick") + elseif mem.cycle == "dlead2" then + if mem.ltdtype ~= 3 then + mem.cycle = "dlead3" + else + mem.cycle = "straight2" + end + mem.det.dt = nil + setlight("dt","Y") + interrupt(mem.yellowb,"tick") + elseif mem.cycle == "dlead3" then + mem.cycle = "straight2" + setlight("dt","R") + interrupt(mem.allredb,"tick") + elseif mem.cycle == "aclead1" then + mem.cycle = "aclead2" + setlight("at","G") + setlight("ct","G") + interrupt(mem.llturn,"tick") + elseif mem.cycle == "aclead2" then + if mem.ltatype ~= 3 and mem.ltctype ~= 3 then + mem.cycle = "aclead3" + else + mem.cycle = "mindwell" + end + setlight("at","Y") + setlight("ct","Y") + mem.det.at = nil + mem.det.ct = nil + interrupt(mem.yellowa,"tick") + elseif mem.cycle == "aclead3" then + mem.cycle = "mindwell" + setlight("at","R") + setlight("ct","R") + interrupt(mem.allreda,"tick") + elseif mem.cycle == "alead1" then + if mem.det.ct and mem.ltctype ~= 1 then + mem.cycle = "aclead2" + setlight("ct","G") + else + mem.cycle = "alead2" + setlight("a","G") + end + setlight("at","G") + interrupt(mem.llturn,"tick") + elseif mem.cycle == "alead2" then + mem.cycle = "alead3" + setlight("at","Y") + mem.det.at = nil + interrupt(mem.yellowa,"tick") + elseif mem.cycle == "alead3" then + mem.cycle = "mindwell" + setlight("at","R") + interrupt(mem.allreda,"tick") + elseif mem.cycle == "clead1" then + if mem.det.at and mem.ltatype ~= 1 then + mem.cycle = "aclead2" + setlight("at","G") + else + mem.cycle = "clead2" + setlight("c","G") + end + setlight("ct","G") + interrupt(mem.llturn,"tick") + elseif mem.cycle == "clead2" then + mem.cycle = "clead3" + setlight("ct","Y") + mem.det.ct = nil + interrupt(mem.yellowa,"tick") + elseif mem.cycle == "clead3" then + mem.cycle = "mindwell" + setlight("ct","R") + interrupt(mem.allreda,"tick") + elseif mem.cycle == "yta1" then + mem.cycle = "yta2" + setlight("c","R") + interrupt(mem.allreda,"tick") + elseif mem.cycle == "yta2" then + mem.cycle = "yta3" + setlight("at","G") + interrupt(mem.llturn,"tick") + elseif mem.cycle == "yta3" then + mem.cycle = "yta4" + setlight("at","Y") + mem.det.at = nil + interrupt(mem.yellowa,"tick") + elseif mem.cycle == "yta4" then + mem.cycle = "mindwell" + if mem.ltatype == 3 then + setlight("at","FY") + else + setlight("at","R") + end + interrupt(mem.allreda,"tick") + elseif mem.cycle == "ytc1" then + mem.cycle = "ytc2" + setlight("a","R") + interrupt(mem.allreda,"tick") + elseif mem.cycle == "ytc2" then + mem.cycle = "ytc3" + setlight("ct","G") + interrupt(mem.llturn,"tick") + elseif mem.cycle == "ytc3" then + mem.cycle = "ytc4" + setlight("ct","Y") + mem.det.ct = nil + interrupt(mem.yellowa,"tick") + elseif mem.cycle == "ytc4" then + mem.cycle = "mindwell" + if mem.ltctype == 3 then + setlight("ct","FY") + else + setlight("ct","R") + end + interrupt(mem.allreda,"tick") + elseif mem.cycle == "sideped1" then + mem.cycle = "sideped2" + if mem.ltatype == 3 and mem.pedbtype ~= 1 then setlight("at","R") end + if mem.ltctype == 3 and mem.peddtype ~= 1 then setlight("ct","R") end + interrupt(mem.allreda,"tick") + elseif mem.cycle == "sideped2" then + mem.cycle = "sideped3" + if mem.pedbtype ~= 1 then setlight("bp","G") end + if mem.peddtype ~= 1 then setlight("dp","G") end + interrupt(mem.sideped,"tick") + elseif mem.cycle == "sideped3" then + mem.cycle = "sideped4" + mem.det.bp = nil + mem.det.dp = nil + if mem.pedbtype ~= 1 then setlight("bp","FR") end + if mem.peddtype ~= 1 then setlight("dp","FR") end + interrupt(mem.pedwarn,"tick") + elseif mem.cycle == "sideped4" then + if mem.det.b or mem.det.d or mem.det.at or mem.det.bt or mem.det.ct or mem.det.dt or mem.det.ap or mem.det.cp then + mem.cycle = "reset" + interrupt(mem.allredb,"tick") + else + mem.cycle = "mindwell" + interrupt(0,"tick") + end + setlight("bp","R") + setlight("dp","R") + elseif mem.cycle == "reset" then + mem.cycle = nil + mem.busy = false + --If someone shows up on AT/CT and turns immediately, + --we shouldn't change for them unless we're completely idle or protected only + if mem.ltatype ~= 2 then mem.det.at = nil end + if mem.ltctype ~= 2 then mem.det.ct = nil end + else + logfault("Unrecognized phase "..mem.cycle,true) + end +end + +--Phase logic for starting new cycles +detactive = false +for _,_ in pairs(mem.det) do detactive = true end +if (not mem.busy) and detactive then + if mem.phaselocked then + log("Not starting cycle due to phase lock",true) + elseif mem.preempt then + log("Not starting cycle due to preemption",true) + else + log("Starting new cycle",true) + mem.stats.cycles = mem.stats.cycles + 1 + if mem.det.at and mem.ltatype ~= 1 and (mem.ltctype == 2 or mem.ltctype == 3) and not (mem.det.b or mem.det.d or mem.det.bt or mem.det.ct or mem.det.dt) then + mem.cycle = "yta1" + mem.busy = true + setlight("c","Y") + interrupt(mem.yellowa,"tick") + elseif mem.det.ct and mem.ltctype ~=1 and (mem.ltatype == 2 or mem.ltatype == 3) and not (mem.det.b or mem.det.d or mem.det.at or mem.det.bt or mem.det.dt) then + mem.cycle = "ytc1" + mem.busy = true + setlight("a","Y") + interrupt(mem.yellowa,"tick") + elseif mem.det.b or mem.det.d or mem.det.bt or mem.det.dt or (mem.det.at and mem.ltatype ~= 1) or (mem.det.ct and mem.ltctype ~= 1) then + mem.cycle = "straight1" + mem.busy = true + setlight("a","Y") + setlight("c","Y") + if mem.ltatype == 3 then setlight("at","Y") end + if mem.ltctype == 3 then setlight("ct","Y") end + interrupt(mem.yellowa,"tick") + elseif (mem.det.ap and mem.pedatype ~= 1) or (mem.det.cp and mem.pedctype ~= 1) then + mem.cycle = "straight1" + mem.busy = true + setlight("a","Y") + setlight("c","Y") + if mem.ltatype == 3 then setlight("at","Y") end + if mem.ltctype == 3 then setlight("ct","Y") end + interrupt(mem.yellowa,"tick") + elseif (mem.det.bp and mem.pedbtype ~= 1) or (mem.det.dp and mem.peddtype ~= 1) then + mem.cycle = "sideped1" + mem.busy = true + if mem.ltatype == 3 and mem.pedbtype ~= 1 then setlight("at","Y") end + if mem.ltctype == 3 and mem.peddtype ~= 1 then setlight("ct","Y") end + if mem.ltatype == 3 or mem.ltctype == 3 then + interrupt(mem.yellowa,"tick") + else + interrupt(0,"tick") + end + else + logfault("Detectors active but no cycle found",true) + end + end +end + +--Touch event handling +if event.type == "digiline" and event.channel == "touchscreen" then + local fields = event.msg + if mem.menu == "main" then + if fields.exit then + mem.menu = "run" + elseif fields.hwsetup then + mem.menu = "hwsetup" + elseif fields.timing then + mem.menu = "timing" + elseif fields.options then + mem.menu = "options" + elseif fields.phaselock then + mem.menu = "phaselock" + elseif fields.about then + mem.menu = "about" + elseif fields.log then + mem.menu = "log" + elseif fields.monitoring then + mem.menu = "monitoring" + elseif fields.mancyc then + mem.menu = "mancyc" + elseif fields.stats then + mem.menu = "stats" + elseif fields.mode then + mem.menu = "mode" + end + elseif mem.menu == "run" then + if fields.menu then + mem.menu = "main" + end + elseif mem.menu == "hwsetup" then + if fields.cancel then + mem.menu = "main" + elseif fields.save then + mem.menu = "main" + mem.ltatype = pivot(lttypes)[fields.ltatype] + mem.ltbtype = pivot(lttypes)[fields.ltbtype] + mem.ltctype = pivot(lttypes)[fields.ltctype] + mem.ltdtype = pivot(lttypes)[fields.ltdtype] + mem.pedatype = pivot(pedtypes)[fields.pedatype] + mem.pedbtype = pivot(pedtypes)[fields.pedbtype] + mem.pedctype = pivot(pedtypes)[fields.pedctype] + mem.peddtype = pivot(pedtypes)[fields.peddtype] + mem.signaltype = pivot(signaltypes)[fields.signaltype] + mem.pedbuttontype = pivot(pedbuttontypes)[fields.pedbuttontype] + mem.logmode = pivot(logmodes)[fields.logmode] + mem.lock = pivot(panellock)[fields.panellock] + end + elseif mem.menu == "timing" then + if fields.cancel then + mem.menu = "main" + elseif fields.save then + mem.menu = "main" + mem.allreda = tonumber(fields.allreda) or mem.allreda + mem.allredb = tonumber(fields.allredb) or mem.allredb + mem.yellowa = tonumber(fields.yellowa) or mem.yellowa + mem.yellowb = tonumber(fields.yellowb) or mem.yellowb + mem.mindwell = tonumber(fields.mindwell) or mem.mindwell + mem.minsidegreen = tonumber(fields.minsidegreen) or mem.minsidegreen + mem.gapout = tonumber(fields.gapout) or mem.minsidegreen + mem.maxsidegreen = tonumber(fields.maxsidegreen) or mem.minsidegreen + mem.pedwarn = tonumber(fields.pedwarn) or mem.pedwarn + mem.sideped = tonumber(fields.sideped) or mem.sideped + mem.llturn = tonumber(fields.llturn) or mem.llturn + end + elseif mem.menu == "options" then + if fields.cancel then + mem.menu = "main" + elseif fields.save then + mem.menu = "main" + mem.name = fields.name + end + elseif mem.menu == "mancyc" then + if fields.cancel then + mem.menu = "main" + else + for _,v in pairs({"a","b","c","d","at","bt","ct","dt","ap","bp","cp","dp"}) do + if fields[v] then + mem.det[v] = true + interrupt(0) --Forces the program to be re-run since the cycle logic is up there ^^ + end + end + end + elseif mem.menu == "log" then + if fields.cancel then + mem.menu = "main" + elseif fields.clear then + mem.faultlog = {} + end + elseif mem.menu == "monitoring" then + if fields.cancel then + mem.menu = "main" + elseif fields.straight then + mem.monitor = 1 + elseif fields.leftturn then + mem.monitor = 2 + elseif fields.pedestrian then + mem.monitor = 3 + end + elseif mem.menu == "phaselock" then + if fields.cancel then + mem.menu = "main" + elseif fields.save then + mem.menu = "main" + local phases_reverse = pivot(phases) + mem.phaselock.a = phases_reverse[fields.a] + mem.phaselock.b = phases_reverse[fields.b] + mem.phaselock.c = phases_reverse[fields.c] + mem.phaselock.d = phases_reverse[fields.d] + mem.phaselock.at = phases_reverse[fields.at] + mem.phaselock.bt = phases_reverse[fields.bt] + mem.phaselock.ct = phases_reverse[fields.ct] + mem.phaselock.dt = phases_reverse[fields.dt] + mem.phaselock.ap = phases_reverse[fields.ap] + mem.phaselock.bp = phases_reverse[fields.bp] + mem.phaselock.cp = phases_reverse[fields.cp] + mem.phaselock.dp = phases_reverse[fields.dp] + end + elseif mem.menu == "about" then + if fields.cancel then + mem.menu = "main" + end + elseif mem.menu == "stats" then + if fields.cancel then + mem.menu = "main" + elseif fields.clear then + mem.stats = {a = 0,b = 0,c = 0,d = 0,at = 0,bt = 0,ct = 0,dt = 0,ap = 0,bp = 0,cp = 0,dp = 0,cycles = 0,lastreset = os.time()} + end + elseif mem.menu == "mode" then + if fields.cancel then + mem.menu = "main" + elseif fields.save then + mem.schedstart = tonumber(fields.schedstart) or mem.schedstart + mem.schedend = tonumber(fields.schedend) or mem.schedend + mem.normalmode = pivot(modes)[fields.normalmode] + mem.schedmode = pivot(modes)[fields.schedmode] + mem.menu = "main" + interrupt(0) -- Some of these need to take immediate effect + end + else + logfault("Unrecognized menu "..mem.menu,false) + mem.menu = "run" + end +end + +--Light control signal sending +if mem.phaselocked then + mem.busy = false + mem.cycle = nil + mem.currentphase = mem.phaselock + for k,v in pairs(mem.phaselock) do + digiline_send(k,string.upper(phases[v])) + end +else + for k,v in pairs(mem.currentphase) do + digiline_send(k,string.upper(phases[v])) + end +end + +--Display drawing +if event.type == "interrupt" and event.iid == "monflash" then + mem.monflash = not mem.monflash +end +local disp = {{command="clear"}} +if mem.lock == 2 then + table.insert(disp,{command="lock"}) +else + table.insert(disp,{command="unlock"}) +end +if mem.menu == "main" then + table.insert(disp,{command="addlabel",X=0,Y=0,label="Main Menu"}) + table.insert(disp,{command="addbutton",X=1,Y=1,W=2,H=1,name="exit",label="Exit"}) + table.insert(disp,{command="addbutton",X=1,Y=3,W=2,H=1,name="hwsetup",label="Hardware Setup"}) + table.insert(disp,{command="addbutton",X=1,Y=5,W=2,H=1,name="timing",label="Timing"}) + table.insert(disp,{command="addbutton",X=4,Y=1,W=2,H=1,name="options",label="Set Name"}) + table.insert(disp,{command="addbutton",X=4,Y=3,W=2,H=1,name="phaselock",label="Phase Lock"}) + table.insert(disp,{command="addbutton",X=4,Y=5,W=2,H=1,name="about",label="About"}) + table.insert(disp,{command="addbutton",X=7,Y=1,W=2,H=1,name="log",label="Log"}) + table.insert(disp,{command="addbutton",X=7,Y=3,W=2,H=1,name="monitoring",label="Monitoring"}) + table.insert(disp,{command="addbutton",X=7,Y=5,W=2,H=1,name="mancyc",label="Manual Call Entry"}) + table.insert(disp,{command="addbutton",X=1,Y=7,W=2,H=1,name="stats",label="Statistics"}) + table.insert(disp,{command="addbutton",X=4,Y=7,W=2,H=1,name="mode",label="Mode/Schedule"}) +elseif mem.menu == "run" then + table.insert(disp,{command="addlabel",X=0,Y=0,label=mem.name}) + table.insert(disp,{command="addlabel",X=0,Y=1,label="Advanced Mesecons Devices LTC-4000E"}) + if #mem.faultlog > 0 then + table.insert(disp,{command="addlabel",X=0,Y=2,label="FAULT DETECTED! Use Menu->Log to view"}) + else + table.insert(disp,{command="addlabel",X=0,Y=2,label="No Current Faults"}) + end + table.insert(disp,{command="addbutton",X=0,Y=7,W=2,H=1,name="menu",label="Menu"}) +elseif mem.menu == "hwsetup" then + table.insert(disp,{command="addlabel",X=0,Y=0,label="Hardware Setup"}) + table.insert(disp,{command="addlabel",X=0,Y=1,label="Left Turn A Type"}) + table.insert(disp,{command="adddropdown",X=0.1,Y=1.5,W=2.25,H=1,name="ltatype",selected_id=mem.ltatype,choices=lttypes}) + table.insert(disp,{command="addlabel",X=0,Y=2.5,label="Left Turn B Type"}) + table.insert(disp,{command="adddropdown",X=0.1,Y=3,W=2.25,H=1,name="ltbtype",selected_id=mem.ltbtype,choices=lttypes}) + table.insert(disp,{command="addlabel",X=0,Y=4,label="Left Turn C Type"}) + table.insert(disp,{command="adddropdown",X=0.1,Y=4.5,W=2.25,H=1,name="ltctype",selected_id=mem.ltctype,choices=lttypes}) + table.insert(disp,{command="addlabel",X=0,Y=5.5,label="Left Turn D Type"}) + table.insert(disp,{command="adddropdown",X=0.1,Y=6,W=2.25,H=1,name="ltdtype",selected_id=mem.ltdtype,choices=lttypes}) + table.insert(disp,{command="addlabel",X=3,Y=1,label="Pedestrian A Type"}) + table.insert(disp,{command="adddropdown",X=3.1,Y=1.5,W=2.25,H=1,name="pedatype",selected_id=mem.pedatype,choices=pedtypes}) + table.insert(disp,{command="addlabel",X=3,Y=2.5,label="Pedestrian B Type"}) + table.insert(disp,{command="adddropdown",X=3.1,Y=3,W=2.25,H=1,name="pedbtype",selected_id=mem.pedbtype,choices=pedtypes}) + table.insert(disp,{command="addlabel",X=3,Y=4,label="Pedestrian C Type"}) + table.insert(disp,{command="adddropdown",X=3.1,Y=4.5,W=2.25,H=1,name="pedctype",selected_id=mem.pedctype,choices=pedtypes}) + table.insert(disp,{command="addlabel",X=3,Y=5.5,label="Pedestrian D Type"}) + table.insert(disp,{command="adddropdown",X=3.1,Y=6,W=2.25,H=1,name="peddtype",selected_id=mem.peddtype,choices=pedtypes}) + table.insert(disp,{command="addlabel",X=6,Y=1,label="Signal Type"}) + table.insert(disp,{command="adddropdown",X=6.1,Y=1.5,W=2.25,H=1,name="signaltype",selected_id=mem.signaltype,choices=signaltypes}) + table.insert(disp,{command="addlabel",X=6,Y=2.5,label="Pedestrian Button Type"}) + table.insert(disp,{command="adddropdown",X=6.1,Y=3,W=2.25,H=1,name="pedbuttontype",selected_id=mem.pedbuttontype,choices=pedbuttontypes}) + table.insert(disp,{command="addlabel",X=6,Y=4,label="Log Output Mode"}) + table.insert(disp,{command="adddropdown",X=6.1,Y=4.5,W=2.25,H=1,name="logmode",selected_id=mem.logmode,choices=logmodes}) + table.insert(disp,{command="addlabel",X=6,Y=5.5,label="Panel Lock"}) + table.insert(disp,{command="adddropdown",X=6.1,Y=6,W=2.25,H=1,name="panellock",selected_id=mem.lock,choices=panellock}) + table.insert(disp,{command="addbutton",X=2,Y=7,W=2,H=1,name="save",label="Save"}) + table.insert(disp,{command="addbutton",X=5,Y=7,W=2,H=1,name="cancel",label="Cancel"}) +elseif mem.menu == "timing" then + table.insert(disp,{command="addlabel",X=0,Y=0,label="Timing"}) + table.insert(disp,{command="addfield",X=0.25,Y=1,W=2.25,H=1,name="allreda",label="All Red A/C->B/D",default=tostring(mem.allreda)}) + table.insert(disp,{command="addfield",X=0.25,Y=2.5,W=2.25,H=1,name="allredb",label="All Red B/D->A/C",default=tostring(mem.allredb)}) + table.insert(disp,{command="addfield",X=0.25,Y=4,W=2.25,H=1,name="yellowa",label="Yellow A/C->B/D",default=tostring(mem.yellowa)}) + table.insert(disp,{command="addfield",X=0.25,Y=5.5,W=2.25,H=1,name="yellowb",label="Yellow B/D->A/C",default=tostring(mem.yellowb)}) + table.insert(disp,{command="addfield",X=3.25,Y=1,W=2.25,H=1,name="mindwell",label="Minimum Dwell",default=tostring(mem.mindwell)}) + table.insert(disp,{command="addfield",X=3.25,Y=4,W=2.25,H=1,name="pedwarn",label="Pedestrian Warn",default=tostring(mem.pedwarn)}) + table.insert(disp,{command="addfield",X=3.25,Y=5.5,W=2.25,H=1,name="sideped",label="B/D Pedestrian Walk",default=tostring(mem.sideped)}) + table.insert(disp,{command="addfield",X=3.25,Y=2.5,W=2.25,H=1,name="llturn",label="Lead/Lag Turn",default=tostring(mem.llturn)}) + table.insert(disp,{command="addfield",X=0.25,Y=7,W=2.25,H=1,name="minsidegreen",label="B/D Min Green",default=tostring(mem.minsidegreen)}) + table.insert(disp,{command="addfield",X=3.25,Y=7,W=2.25,H=1,name="gapout",label="B/D Gap-out",default=tostring(mem.gapout)}) + table.insert(disp,{command="addfield",X=6.25,Y=7,W=2.25,H=1,name="maxsidegreen",label="B/D Max Green",default=tostring(mem.maxsidegreen)}) + table.insert(disp,{command="addbutton",X=6,Y=3.75,W=2,H=1,name="save",label="Save"}) + table.insert(disp,{command="addbutton",X=6,Y=5,W=2,H=1,name="cancel",label="Cancel"}) +elseif mem.menu == "options" then + table.insert(disp,{command="addlabel",X=0,Y=0,label="Set Name"}) + table.insert(disp,{command="addfield",X=0.25,Y=1,W=5,H=1,name="name",label="Intersection Name",default=mem.name}) + table.insert(disp,{command="addbutton",X=0.25,Y=2.5,W=2,H=1,name="save",label="Save"}) + table.insert(disp,{command="addbutton",X=0.25,Y=3.5,W=2,H=1,name="cancel",label="Cancel"}) +elseif mem.menu == "mancyc" then + table.insert(disp,{command="addlabel",X=0,Y=0,label="Manual Call Entry"}) + table.insert(disp,{command="addimage_button",X=1,Y=2,W=2,H=1,name="b",label="Straight B",image="digistuff_ts_bg.png"..(mem.det.b and "^[brighten" or "")}) + table.insert(disp,{command="addimage_button",X=1,Y=4,W=2,H=1,name="d",label="Straight D",image="digistuff_ts_bg.png"..(mem.det.d and "^[brighten" or "")}) + if mem.ltatype ~= 1 then table.insert(disp,{command="addimage_button",X=4,Y=1,W=2,H=1,name="at",label="Left Turn A",image="digistuff_ts_bg.png"..(mem.det.at and "^[brighten" or "")}) end + if mem.ltbtype ~= 1 then table.insert(disp,{command="addimage_button",X=4,Y=2,W=2,H=1,name="bt",label="Left Turn B",image="digistuff_ts_bg.png"..(mem.det.bt and "^[brighten" or "")}) end + if mem.ltctype ~= 1 then table.insert(disp,{command="addimage_button",X=4,Y=3,W=2,H=1,name="ct",label="Left Turn C",image="digistuff_ts_bg.png"..(mem.det.ct and "^[brighten" or "")}) end + if mem.ltdtype ~= 1 then table.insert(disp,{command="addimage_button",X=4,Y=4,W=2,H=1,name="dt",label="Left Turn D",image="digistuff_ts_bg.png"..(mem.det.dt and "^[brighten" or "")}) end + if mem.pedatype ~= 1 then table.insert(disp,{command="addimage_button",X=7,Y=1,W=2,H=1,name="ap",label="Pedestrian A",image="digistuff_ts_bg.png"..(mem.det.ap and "^[brighten" or "")}) end + if mem.pedbtype ~= 1 then table.insert(disp,{command="addimage_button",X=7,Y=2,W=2,H=1,name="bp",label="Pedestrian B",image="digistuff_ts_bg.png"..(mem.det.bp and "^[brighten" or "")}) end + if mem.pedctype ~= 1 then table.insert(disp,{command="addimage_button",X=7,Y=3,W=2,H=1,name="cp",label="Pedestrian C",image="digistuff_ts_bg.png"..(mem.det.cp and "^[brighten" or "")}) end + if mem.peddtype ~= 1 then table.insert(disp,{command="addimage_button",X=7,Y=4,W=2,H=1,name="dp",label="Pedestrian D",image="digistuff_ts_bg.png"..(mem.det.dp and "^[brighten" or "")}) end + table.insert(disp,{command="addbutton",X=4,Y=6,W=2,H=1,name="cancel",label="Back"}) +elseif mem.menu == "log" then + table.insert(disp,{command="addlabel",X=0,Y=0,label="Fault Log"}) + table.insert(disp,{command="addbutton",X=2,Y=0,W=2,H=1,name="cancel",label="Back"}) + table.insert(disp,{command="addbutton",X=4,Y=0,W=2,H=1,name="clear",label="Clear"}) + if #mem.faultlog > 0 then + for i=1,math.max(#mem.faultlog,10),1 do + table.insert(disp,{command="addlabel",X=0,Y=1+(i/2),label=mem.faultlog[i]}) + end + else + table.insert(disp,{command="addlabel",X=0,Y=1.5,label="No Faults"}) + end +elseif mem.menu == "monitoring" then + interrupt(0.6,"monflash") + + local monitor_textures = {} + monitor_textures.O = "streets_tl_off.png" + monitor_textures.R = "streets_tl_red.png" + monitor_textures.Y = "streets_tl_yellow.png" + monitor_textures.G = "streets_tl_green.png" + if mem.monflash then + monitor_textures.FR = "streets_tl_red.png" + monitor_textures.FY = "streets_tl_yellow.png" + monitor_textures.FG = "streets_tl_green.png" + else + monitor_textures.FR = "streets_tl_off.png" + monitor_textures.FY = "streets_tl_off.png" + monitor_textures.FG = "streets_tl_off.png" + end + monitor_textures.RY = "streets_tl_redyellow.png" + + local monitor_textures_lt = {} + monitor_textures_lt.O = "streets_tl_left_off.png" + monitor_textures_lt.R = "streets_tl_left_red.png" + monitor_textures_lt.Y = "streets_tl_left_yellow.png" + monitor_textures_lt.G = "streets_tl_left_green.png" + if mem.monflash then + monitor_textures_lt.FR = "streets_tl_left_red.png" + monitor_textures_lt.FY = "streets_tl_left_yellow.png" + monitor_textures_lt.FG = "streets_tl_left_green.png" + else + monitor_textures_lt.FR = "streets_tl_left_off.png" + monitor_textures_lt.FY = "streets_tl_left_off.png" + monitor_textures_lt.FG = "streets_tl_left_off.png" + end + monitor_textures_lt.RY = "streets_tl_left_redyellow.png" + + local monitor_textures_ped = {} + monitor_textures_ped.O = "streets_pl_off.png" + monitor_textures_ped.R = "streets_pl_dontwalk.png" + monitor_textures_ped.G = "streets_pl_walk.png" + if mem.monflash then + monitor_textures_ped.FR = "streets_pl_dontwalk.png" + monitor_textures_ped.FY = "streets_pl_dontwalk.png" + monitor_textures_ped.FG = "streets_pl_dontwalk.png" + monitor_textures_ped.Y = "streets_pl_dontwalk.png" + else + monitor_textures_ped.FR = "streets_pl_off.png" + monitor_textures_ped.FY = "streets_pl_off.png" + monitor_textures_ped.FG = "streets_pl_off.png" + monitor_textures_ped.Y = "streets_pl_off.png" + end + monitor_textures_ped.RY = "streets_pl_dontwalk.png" + + table.insert(disp,{command="addlabel",X=0,Y=0,label="Monitor"}) + table.insert(disp,{command="addbutton",X=0,Y=2,W=2,H=1,name="straight",label="Straight"}) + table.insert(disp,{command="addbutton",X=0,Y=3,W=2,H=1,name="leftturn",label="Left Turn"}) + table.insert(disp,{command="addbutton",X=0,Y=4,W=2,H=1,name="pedestrian",label="Pedestrian"}) + table.insert(disp,{command="addbutton",X=0,Y=6,W=2,H=1,name="cancel",label="Back"}) + + --Texture names + local asphalt = "" + local sideline = "" + local centerline = "" + if mem.signaltype == 1 then + asphalt = "streets_asphalt.png" + sideline = asphalt.."^streets_asphalt_side.png" + centerline = asphalt.."^streets_rw_dashed_line.png" + elseif mem.signaltype == 2 then + asphalt = "streets_asphalt.png" + sideline = asphalt.."^streets_asphalt_side.png" + centerline = asphalt.."^infrastructure_double_yellow_line.png" + elseif mem.signaltype == 3 then + asphalt = "streets_asphalt.png" + sideline = asphalt.."^streets_solid_side_line.png" + centerline = asphalt.."^(streets_double_solid_center_line.png^[colorize:#ecb100)" + end + + --Center of intersection + table.insert(disp,{command="addimage",X=5.25, Y=3.25, W=1,H=1,texture_name=asphalt}) + table.insert(disp,{command="addimage",X=6, Y=3.25, W=1,H=1,texture_name=asphalt}) + table.insert(disp,{command="addimage",X=6.75, Y=3.25, W=1,H=1,texture_name=asphalt}) + table.insert(disp,{command="addimage",X=5.25, Y=4, W=1,H=1,texture_name=asphalt}) + table.insert(disp,{command="addimage",X=6, Y=4, W=1,H=1,texture_name=asphalt}) + table.insert(disp,{command="addimage",X=6.75, Y=4, W=1,H=1,texture_name=asphalt}) + table.insert(disp,{command="addimage",X=5.25, Y=4.75, W=1,H=1,texture_name=asphalt}) + table.insert(disp,{command="addimage",X=6, Y=4.75, W=1,H=1,texture_name=asphalt}) + table.insert(disp,{command="addimage",X=6.75, Y=4.75, W=1,H=1,texture_name=asphalt}) + + --Approach Labels + table.insert(disp,{command="addlabel",X=3.5, Y=4.25, label="A"}) + table.insert(disp,{command="addlabel",X=6.25, Y=1.25, label="B"}) + table.insert(disp,{command="addlabel",X=9.25, Y=4.25, label="C"}) + table.insert(disp,{command="addlabel",X=6.25, Y=7.25, label="D"}) + + --Approach A (left) + table.insert(disp,{command="addimage",X=4.5, Y=3.25, W=1,H=1,texture_name=sideline.."^[transformR270"}) + table.insert(disp,{command="addimage",X=4.5, Y=4, W=1,H=1,texture_name=centerline.."^[transformR90"}) + table.insert(disp,{command="addimage",X=4.5, Y=4.75, W=1,H=1,texture_name=sideline.."^[transformR90"}) + table.insert(disp,{command="addimage",X=3.75, Y=3.25, W=1,H=1,texture_name=sideline.."^[transformR270"}) + table.insert(disp,{command="addimage",X=3.75, Y=4, W=1,H=1,texture_name=centerline.."^[transformR90"}) + table.insert(disp,{command="addimage",X=3.75, Y=4.75, W=1,H=1,texture_name=sideline.."^[transformR90"}) + + --Approach B (top) + table.insert(disp,{command="addimage",X=5.25, Y=2.5, W=1,H=1,texture_name=sideline}) + table.insert(disp,{command="addimage",X=6, Y=2.5, W=1,H=1,texture_name=centerline}) + table.insert(disp,{command="addimage",X=6.75, Y=2.5, W=1,H=1,texture_name=sideline.."^[transformR180"}) + table.insert(disp,{command="addimage",X=5.25, Y=1.75, W=1,H=1,texture_name=sideline}) + table.insert(disp,{command="addimage",X=6, Y=1.75, W=1,H=1,texture_name=centerline}) + table.insert(disp,{command="addimage",X=6.75, Y=1.75, W=1,H=1,texture_name=sideline.."^[transformR180"}) + + --Approach C (right) + table.insert(disp,{command="addimage",X=7.5, Y=3.25, W=1,H=1,texture_name=sideline.."^[transformR270"}) + table.insert(disp,{command="addimage",X=7.5, Y=4, W=1,H=1,texture_name=centerline.."^[transformR90"}) + table.insert(disp,{command="addimage",X=7.5, Y=4.75, W=1,H=1,texture_name=sideline.."^[transformR90"}) + table.insert(disp,{command="addimage",X=8.25, Y=3.25, W=1,H=1,texture_name=sideline.."^[transformR270"}) + table.insert(disp,{command="addimage",X=8.25, Y=4, W=1,H=1,texture_name=centerline.."^[transformR90"}) + table.insert(disp,{command="addimage",X=8.25, Y=4.75, W=1,H=1,texture_name=sideline.."^[transformR90"}) + + --Approach D (bottom) + table.insert(disp,{command="addimage",X=5.25, Y=5.5, W=1,H=1,texture_name=sideline}) + table.insert(disp,{command="addimage",X=6, Y=5.5, W=1,H=1,texture_name=centerline}) + table.insert(disp,{command="addimage",X=6.75, Y=5.5, W=1,H=1,texture_name=sideline.."^[transformR180"}) + table.insert(disp,{command="addimage",X=5.25, Y=6.25, W=1,H=1,texture_name=sideline}) + table.insert(disp,{command="addimage",X=6, Y=6.25, W=1,H=1,texture_name=centerline}) + table.insert(disp,{command="addimage",X=6.75, Y=6.25, W=1,H=1,texture_name=sideline.."^[transformR180"}) + + --Traffic Lights + if mem.monitor == 1 then + table.insert(disp,{command="addimage",X=4.25, Y=5.5, W=1,H=1,texture_name=monitor_textures[mem.currentphase.a].."^[transformR270"}) --A + table.insert(disp,{command="addimage",X=4.5, Y=2.25, W=1,H=1,texture_name=monitor_textures[mem.currentphase.b].."^[transformR180"}) --B + table.insert(disp,{command="addimage",X=7.75, Y=2.5, W=1,H=1,texture_name=monitor_textures[mem.currentphase.c].."^[transformR90"}) --C + table.insert(disp,{command="addimage",X=7.5, Y=5.75, W=1,H=1,texture_name=monitor_textures[mem.currentphase.d]}) --D + elseif mem.monitor == 2 then + table.insert(disp,{command="addimage",X=4.25, Y=5.5, W=1,H=1,texture_name=monitor_textures_lt[mem.currentphase.at].."^[transformR270"}) --A + table.insert(disp,{command="addimage",X=4.5, Y=2.25, W=1,H=1,texture_name=monitor_textures_lt[mem.currentphase.bt].."^[transformR180"}) --B + table.insert(disp,{command="addimage",X=7.75, Y=2.5, W=1,H=1,texture_name=monitor_textures_lt[mem.currentphase.ct].."^[transformR90"}) --C + table.insert(disp,{command="addimage",X=7.5, Y=5.75, W=1,H=1,texture_name=monitor_textures_lt[mem.currentphase.dt]}) --D + elseif mem.monitor == 3 then + table.insert(disp,{command="addimage",X=4.25, Y=5.5, W=1,H=1,texture_name=monitor_textures_ped[mem.currentphase.ap].."^[transformR270"}) --A + table.insert(disp,{command="addimage",X=4.5, Y=2.25, W=1,H=1,texture_name=monitor_textures_ped[mem.currentphase.bp].."^[transformR180"}) --B + table.insert(disp,{command="addimage",X=7.75, Y=2.5, W=1,H=1,texture_name=monitor_textures_ped[mem.currentphase.cp].."^[transformR90"}) --C + table.insert(disp,{command="addimage",X=7.5, Y=5.75, W=1,H=1,texture_name=monitor_textures_ped[mem.currentphase.dp]}) --D + end +elseif mem.menu == "phaselock" then + local phaselock_phases = {"Off","Green","Red"} + if mem.signaltype >= 2 then + table.insert(phaselock_phases,"Yellow") + table.insert(phaselock_phases,"FlashRed") + table.insert(phaselock_phases,"FlashYellow") + end + if mem.signaltype == 3 then + table.insert(phaselock_phases,"FlashGreen") + table.insert(phaselock_phases,"RedYellow") + end + local shortphase_reverse = pivot(shortphases) + table.insert(disp,{command="addlabel",X=0,Y=0,label="Phase Lock"}) + table.insert(disp,{command="addlabel",X=0,Y=1,label="Straight A"}) + table.insert(disp,{command="adddropdown",X=0.1,Y=1.5,W=2.25,H=1,name="a",selected_id=shortphase_reverse[mem.phaselock.a],choices=phaselock_phases}) + table.insert(disp,{command="addlabel",X=0,Y=2.5,label="Straight B"}) + table.insert(disp,{command="adddropdown",X=0.1,Y=3,W=2.25,H=1,name="b",selected_id=shortphase_reverse[mem.phaselock.b],choices=phaselock_phases}) + table.insert(disp,{command="addlabel",X=0,Y=4,label="Straight C"}) + table.insert(disp,{command="adddropdown",X=0.1,Y=4.5,W=2.25,H=1,name="c",selected_id=shortphase_reverse[mem.phaselock.c],choices=phaselock_phases}) + table.insert(disp,{command="addlabel",X=0,Y=5.5,label="Straight D"}) + table.insert(disp,{command="adddropdown",X=0.1,Y=6,W=2.25,H=1,name="d",selected_id=shortphase_reverse[mem.phaselock.d],choices=phaselock_phases}) + table.insert(disp,{command="addlabel",X=3,Y=1,label="Left Turn A"}) + table.insert(disp,{command="adddropdown",X=3.1,Y=1.5,W=2.25,H=1,name="at",selected_id=shortphase_reverse[mem.phaselock.at],choices=phaselock_phases}) + table.insert(disp,{command="addlabel",X=3,Y=2.5,label="Left Turn B"}) + table.insert(disp,{command="adddropdown",X=3.1,Y=3,W=2.25,H=1,name="bt",selected_id=shortphase_reverse[mem.phaselock.bt],choices=phaselock_phases}) + table.insert(disp,{command="addlabel",X=3,Y=4,label="Left Turn C"}) + table.insert(disp,{command="adddropdown",X=3.1,Y=4.5,W=2.25,H=1,name="ct",selected_id=shortphase_reverse[mem.phaselock.ct],choices=phaselock_phases}) + table.insert(disp,{command="addlabel",X=3,Y=5.5,label="Left Turn D"}) + table.insert(disp,{command="adddropdown",X=3.1,Y=6,W=2.25,H=1,name="dt",selected_id=shortphase_reverse[mem.phaselock.dt],choices=phaselock_phases}) + table.insert(disp,{command="addlabel",X=6,Y=1,label="Pedestrian A"}) + table.insert(disp,{command="adddropdown",X=6.1,Y=1.5,W=2.25,H=1,name="ap",selected_id=shortphase_reverse[mem.phaselock.ap],choices=phaselock_phases}) + table.insert(disp,{command="addlabel",X=6,Y=2.5,label="Pedestrian B"}) + table.insert(disp,{command="adddropdown",X=6.1,Y=3,W=2.25,H=1,name="bp",selected_id=shortphase_reverse[mem.phaselock.bp],choices=phaselock_phases}) + table.insert(disp,{command="addlabel",X=6,Y=4,label="Pedestrian C"}) + table.insert(disp,{command="adddropdown",X=6.1,Y=4.5,W=2.25,H=1,name="cp",selected_id=shortphase_reverse[mem.phaselock.cp],choices=phaselock_phases}) + table.insert(disp,{command="addlabel",X=6,Y=5.5,label="Pedestrian D"}) + table.insert(disp,{command="adddropdown",X=6.1,Y=6,W=2.25,H=1,name="dp",selected_id=shortphase_reverse[mem.phaselock.dp],choices=phaselock_phases}) + table.insert(disp,{command="addbutton",X=3,Y=7,W=2,H=1,name="save",label="Save"}) + table.insert(disp,{command="addbutton",X=6,Y=7,W=2,H=1,name="cancel",label="Cancel"}) +elseif mem.menu == "about" then + table.insert(disp,{command="addlabel",X=0,Y=0,label="About"}) + table.insert(disp,{command="addlabel",X=0,Y=1,label="LTC-4000E"}) + table.insert(disp,{command="addlabel",X=0,Y=1.5,label="A product of Advanced Mesecons Devices, a Cheapie Systems company."}) + table.insert(disp,{command="addlabel",X=0,Y=2,label="This is free and unencumbered software released into the public domain."}) + table.insert(disp,{command="addlabel",X=0,Y=2.5,label="See http://unlicense.org/ for more information."}) + table.insert(disp,{command="addbutton",X=0,Y=7.25,W=2,H=1,name="cancel",label="Back"}) +elseif mem.menu == "stats" then + interrupt(1,"statsrefresh") + table.insert(disp,{command="addlabel",X=0,Y=0,label="Statistics"}) + table.insert(disp,{command="addbutton",X=2,Y=0,W=2,H=1,name="cancel",label="Back"}) + table.insert(disp,{command="addbutton",X=4,Y=0,W=2,H=1,name="clear",label="Clear"}) + table.insert(disp,{command="addlabel",X=0,Y=1,label="Numbers represent total seconds waiting (for detectors) or total presses (for buttons)"}) + table.insert(disp,{command="addlabel",X=0,Y=2,label="A: "..mem.stats.a}) + table.insert(disp,{command="addlabel",X=0,Y=2.5,label="B: "..mem.stats.b}) + table.insert(disp,{command="addlabel",X=0,Y=3,label="C: "..mem.stats.c}) + table.insert(disp,{command="addlabel",X=0,Y=3.5,label="D: "..mem.stats.d}) + table.insert(disp,{command="addlabel",X=2,Y=2,label="AT: "..mem.stats.at}) + table.insert(disp,{command="addlabel",X=2,Y=2.5,label="BT: "..mem.stats.bt}) + table.insert(disp,{command="addlabel",X=2,Y=3,label="CT: "..mem.stats.ct}) + table.insert(disp,{command="addlabel",X=2,Y=3.5,label="DT: "..mem.stats.dt}) + table.insert(disp,{command="addlabel",X=4,Y=2,label="AP: "..mem.stats.ap}) + table.insert(disp,{command="addlabel",X=4,Y=2.5,label="BP: "..mem.stats.bp}) + table.insert(disp,{command="addlabel",X=4,Y=3,label="CP: "..mem.stats.cp}) + table.insert(disp,{command="addlabel",X=4,Y=3.5,label="DP: "..mem.stats.dp}) + table.insert(disp,{command="addlabel",X=0,Y=4.5,label="Total cycles: "..mem.stats.cycles}) + local timesinceclear = os.time() - mem.stats.lastreset + local lastclear = "ERROR" + if timesinceclear < 120 then --Two minutes + lastclear = math.floor(timesinceclear).." seconds" + elseif timesinceclear < 7200 then --Two hours + lastclear = math.floor(timesinceclear/60).." minutes" + elseif timesinceclear < 172800 then --Two days + lastclear = math.floor(timesinceclear/3600).." hours" + else + lastclear = math.floor(timesinceclear/86400).." days" + end + table.insert(disp,{command="addlabel",X=0,Y=5.5,label="Last cleared: "..lastclear.." ago"}) +elseif mem.menu == "mode" then + table.insert(disp,{command="addlabel",X=0,Y=0,label="Mode / Schedule"}) + table.insert(disp,{command="addlabel",X=0,Y=1,label="Normal Mode"}) + table.insert(disp,{command="adddropdown",X=0.1,Y=1.5,W=2.25,H=1,name="normalmode",selected_id=mem.normalmode,choices=modes}) + table.insert(disp,{command="addlabel",X=0,Y=2.5,label="Scheduled Mode"}) + table.insert(disp,{command="adddropdown",X=0.1,Y=3,W=2.25,H=1,name="schedmode",selected_id=mem.schedmode,choices=modes}) + table.insert(disp,{command="addfield",X=0.5,Y=4.5,W=2,H=1,name="schedstart",label="Schedule Start Hour",default=tostring(mem.schedstart)}) + table.insert(disp,{command="addfield",X=0.5,Y=5.5,W=2,H=1,name="schedend",label="Schedule End Hour",default=tostring(mem.schedend)}) + table.insert(disp,{command="addbutton",X=3,Y=7,W=2,H=1,name="save",label="Save"}) + table.insert(disp,{command="addbutton",X=6,Y=7,W=2,H=1,name="cancel",label="Cancel"}) +else + logfault("Unrecognized menu "..mem.menu,false) + mem.menu = "run" +end +digiline_send("touchscreen",disp) + +--Wake up periodically to check schedule +if (mem.schedstart ~= mem.schedend) then + interrupt(10,"schedcheck") +end diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..21775bc --- /dev/null +++ b/init.lua @@ -0,0 +1,345 @@ +--Just enough of the touchscreen to enable it to work here +local function process_command(meta,data,msg) + if msg.command == "clear" then + data = {} + elseif msg.command == "addimage" then + for _,i in pairs({"X","Y","W","H"}) do + if not msg[i] or type(msg[i]) ~= "number" then + return + end + end + if not msg.texture_name or type(msg.texture_name) ~= "string" then + return + end + local field = {type="image",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,texture_name=minetest.formspec_escape(msg.texture_name)} + table.insert(data,field) + elseif msg.command == "addfield" then + for _,i in pairs({"X","Y","W","H"}) do + if not msg[i] or type(msg[i]) ~= "number" then + return + end + end + for _,i in pairs({"name","label","default"}) do + if not msg[i] or type(msg[i]) ~= "string" then + return + end + end + local field = {type="field",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,name=minetest.formspec_escape(msg.name),label=minetest.formspec_escape(msg.label),default=minetest.formspec_escape(msg.default)} + table.insert(data,field) + elseif msg.command == "addpwdfield" then + for _,i in pairs({"X","Y","W","H"}) do + if not msg[i] or type(msg[i]) ~= "number" then + return + end + end + for _,i in pairs({"name","label"}) do + if not msg[i] or type(msg[i]) ~= "string" then + return + end + end + local field = {type="pwdfield",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,name=minetest.formspec_escape(msg.name),label=minetest.formspec_escape(msg.label)} + table.insert(data,field) + elseif msg.command == "addtextarea" then + for _,i in pairs({"X","Y","W","H"}) do + if not msg[i] or type(msg[i]) ~= "number" then + return + end + end + for _,i in pairs({"name","label","default"}) do + if not msg[i] or type(msg[i]) ~= "string" then + return + end + end + local field = {type="textarea",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,name=minetest.formspec_escape(msg.name),label=minetest.formspec_escape(msg.label),default=minetest.formspec_escape(msg.default)} + table.insert(data,field) + elseif msg.command == "addlabel" then + for _,i in pairs({"X","Y"}) do + if not msg[i] or type(msg[i]) ~= "number" then + return + end + end + if not msg.label or type(msg.label) ~= "string" then + return + end + local field = {type="label",X=msg.X,Y=msg.Y,label=minetest.formspec_escape(msg.label)} + table.insert(data,field) + elseif msg.command == "addvertlabel" then + for _,i in pairs({"X","Y"}) do + if not msg[i] or type(msg[i]) ~= "number" then + return + end + end + if not msg.label or type(msg.label) ~= "string" then + return + end + local field = {type="vertlabel",X=msg.X,Y=msg.Y,label=minetest.formspec_escape(msg.label)} + table.insert(data,field) + elseif msg.command == "addbutton" then + for _,i in pairs({"X","Y","W","H"}) do + if not msg[i] or type(msg[i]) ~= "number" then + return + end + end + for _,i in pairs({"name","label"}) do + if not msg[i] or type(msg[i]) ~= "string" then + return + end + end + local field = {type="button",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,name=minetest.formspec_escape(msg.name),label=minetest.formspec_escape(msg.label)} + table.insert(data,field) + elseif msg.command == "addbutton_exit" then + for _,i in pairs({"X","Y","W","H"}) do + if not msg[i] or type(msg[i]) ~= "number" then + return + end + end + for _,i in pairs({"name","label"}) do + if not msg[i] or type(msg[i]) ~= "string" then + return + end + end + local field = {type="button_exit",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,name=minetest.formspec_escape(msg.name),label=minetest.formspec_escape(msg.label)} + table.insert(data,field) + elseif msg.command == "addimage_button" then + for _,i in pairs({"X","Y","W","H"}) do + if not msg[i] or type(msg[i]) ~= "number" then + return + end + end + for _,i in pairs({"image","name","label"}) do + if not msg[i] or type(msg[i]) ~= "string" then + return + end + end + local field = {type="image_button",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,image=minetest.formspec_escape(msg.image),name=minetest.formspec_escape(msg.name),label=minetest.formspec_escape(msg.label)} + table.insert(data,field) + elseif msg.command == "addimage_button_exit" then + for _,i in pairs({"X","Y","W","H"}) do + if not msg[i] or type(msg[i]) ~= "number" then + return + end + end + for _,i in pairs({"image","name","label"}) do + if not msg[i] or type(msg[i]) ~= "string" then + return + end + end + local field = {type="image_button_exit",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,image=minetest.formspec_escape(msg.image),name=minetest.formspec_escape(msg.name),label=minetest.formspec_escape(msg.label)} + table.insert(data,field) + elseif msg.command == "adddropdown" then + for _,i in pairs({"X","Y","W","H","selected_id"}) do + if not msg[i] or type(msg[i]) ~= "number" then + return + end + end + if not msg.name or type(msg.name) ~= "string" then + return + end + if not msg.choices or type(msg.choices) ~= "table" or #msg.choices < 1 then + return + end + local field = {type="dropdown",X=msg.X,Y=msg.Y,W=msg.W,H=msg.H,name=msg.name,selected_id=msg.selected_id,choices=msg.choices} + table.insert(data,field) + elseif msg.command == "lock" then + meta:set_int("locked",1) + elseif msg.command == "unlock" then + meta:set_int("locked",0) + end + return data +end + +local function update_ts_formspec(pos,data) + local meta = minetest.get_meta(pos) + local fs = "size[10,8]".. + "background[0,0;0,0;ltc4000e_formspec_bg.png;true]" + for _,field in pairs(data) do + if field.type == "image" then + fs = fs..string.format("image[%s,%s;%s,%s;%s]",field.X,field.Y,field.W,field.H,field.texture_name) + elseif field.type == "field" then + fs = fs..string.format("field[%s,%s;%s,%s;%s;%s;%s]",field.X,field.Y,field.W,field.H,field.name,field.label,field.default) + elseif field.type == "pwdfield" then + fs = fs..string.format("pwdfield[%s,%s;%s,%s;%s;%s]",field.X,field.Y,field.W,field.H,field.name,field.label) + elseif field.type == "textarea" then + fs = fs..string.format("textarea[%s,%s;%s,%s;%s;%s;%s]",field.X,field.Y,field.W,field.H,field.name,field.label,field.default) + elseif field.type == "label" then + fs = fs..string.format("label[%s,%s;%s]",field.X,field.Y,field.label) + elseif field.type == "vertlabel" then + fs = fs..string.format("vertlabel[%s,%s;%s]",field.X,field.Y,field.label) + elseif field.type == "button" then + fs = fs..string.format("button[%s,%s;%s,%s;%s;%s]",field.X,field.Y,field.W,field.H,field.name,field.label) + elseif field.type == "button_exit" then + fs = fs..string.format("button_exit[%s,%s;%s,%s;%s;%s]",field.X,field.Y,field.W,field.H,field.name,field.label) + elseif field.type == "image_button" then + fs = fs..string.format("image_button[%s,%s;%s,%s;%s;%s;%s]",field.X,field.Y,field.W,field.H,field.image,field.name,field.label) + elseif field.type == "image_button_exit" then + fs = fs..string.format("image_button_exit[%s,%s;%s,%s;%s;%s;%s]",field.X,field.Y,field.W,field.H,field.image,field.name,field.label) + elseif field.type == "dropdown" then + local choices = "" + for _,i in ipairs(field.choices) do + if type(i) == "string" then + choices = choices..minetest.formspec_escape(i).."," + end + end + choices = string.sub(choices,1,-2) + fs = fs..string.format("dropdown[%s,%s;%s,%s;%s;%s;%s]",field.X,field.Y,field.W,field.H,field.name,choices,field.selected_id) + end + end + meta:set_string("formspec",fs) +end + +local function ts_on_digiline_receive(pos,msg) + local meta = minetest.get_meta(pos) + if type(msg) ~= "table" then return end + local data = minetest.deserialize(meta:get_string("data")) or {} + if msg.command then + data = process_command(meta,data,msg) + else + for _,i in ipairs(msg) do + if i.command then + data = process_command(meta,data,i) or data + end + end + end + meta:set_string("data",minetest.serialize(data)) + update_ts_formspec(pos,data) +end + +--Load the (mostly unmodified) firmware +local fw = loadfile(minetest.get_modpath("ltc4000e")..DIR_DELIM.."fw.lua") + +local function run(pos,event) + --Initialize environment + local context = {} + local meta = minetest.get_meta(pos) + context.mem = minetest.deserialize(meta:get_string("mem")) or {} + context.event = event + context.string = string + context.table = table + context.math = math + context.pairs = pairs + context.ipairs = ipairs + context.tostring = tostring + context.tonumber = tonumber + context.type = type + context.print = print + context.os = {} + for k,v in pairs(os) do + context.os[k] = v + end + + function context.os.datetable() + return(os.date("*t",os.time())) + end + + function context.digiline_send(channel,msg) + if channel == "touchscreen" then + --Touchscreen is integrated into the chip + ts_on_digiline_receive(pos,msg) + else + --Not an integrated peripheral, so send the message + digiline:receptor_send(pos,digiline.rules.default,channel,msg) + end + end + + function context.interrupt(time,iid) + if iid == "gapout" then + --This one can have the time changed on-the-fly, so it has to be done with node timers + local timer = minetest.get_node_timer(pos) + if time then + timer:start(time) + else + timer:stop() + end + else + local event = {} + event.type = "interrupt" + event.iid = iid + minetest.after(time,run,pos,event) + end + end + + --This is where the magic happens... + setfenv(fw,context) + + --Run code + local success,err = pcall(fw) + if not success then + print("Error in LTC-4000E execution, aborting: "..err) + return + end + + --Save memory after execution + meta:set_string("mem",minetest.serialize(context.mem)) +end + +local function ts_on_receive_fields(pos,formname,fields,sender) + local meta = minetest.get_meta(pos) + local playername = sender:get_player_name() + local locked = meta:get_int("locked") == 1 + local can_bypass = minetest.check_player_privs(playername,{protection_bypass=true}) + local is_protected = minetest.is_protected(pos,playername) + if (locked and is_protected) and not can_bypass then + minetest.record_protection_violation(pos,playername) + minetest.chat_send_player(playername,"You are not authorized to use this controller.") + return + end + local event = {} + event.type = "digiline" + event.channel = "touchscreen" + event.msg = fields + run(pos,event) +end + +local nodebox = { + { -0.35, -0.45, 0.25, 0.35, 0.45, 0.75 } +} + + +minetest.register_node("ltc4000e:controller", { + tiles = { + "ltc4000e_cabinet_sides.png", + "ltc4000e_cabinet_sides.png", + "ltc4000e_cabinet_sides.png", + "ltc4000e_cabinet_sides.png", + "ltc4000e_cabinet_sides.png", + "ltc4000e_cabinet_front.png", + }, + description = "LTC-4000E Traffic Signal Controller", + paramtype = "light", + paramtype2 = "facedir", + drawtype = "nodebox", + groups = {dig_immediate=2}, + sounds = default.node_sound_stone_defaults(), + on_construct = function(pos) + local event = {type="program"} + run(pos,event) + end, + on_timer = function(pos) + local event = {} + event.type = "interrupt" + event.iid = "gapout" + run(pos,event) + end, + node_box = { + type = "fixed", + fixed = nodebox + }, + selection_box = { + type = "fixed", + fixed = nodebox + }, + on_receive_fields = ts_on_receive_fields, + digiline = + { + receptor = {}, + effector = { + action = function(pos,_,channel,msg) + local event = {} + event.type = "digiline" + event.channel = channel + event.msg = msg + run(pos,event) + end + }, + }, +}) diff --git a/textures/ltc4000e_cabinet_front.png b/textures/ltc4000e_cabinet_front.png Binary files differnew file mode 100644 index 0000000..4e85319 --- /dev/null +++ b/textures/ltc4000e_cabinet_front.png diff --git a/textures/ltc4000e_cabinet_sides.png b/textures/ltc4000e_cabinet_sides.png Binary files differnew file mode 100644 index 0000000..49a5ef8 --- /dev/null +++ b/textures/ltc4000e_cabinet_sides.png diff --git a/textures/ltc4000e_formspec_bg.png b/textures/ltc4000e_formspec_bg.png Binary files differnew file mode 100644 index 0000000..87c54f2 --- /dev/null +++ b/textures/ltc4000e_formspec_bg.png |