diff options
| author | cheapie <no-email-for-you@example.com> | 2017-01-20 01:26:42 -0600 | 
|---|---|---|
| committer | cheapie <no-email-for-you@example.com> | 2017-01-20 01:26:42 -0600 | 
| commit | eba7b01888cf6383c4e986bc6d5f5d511c6566f7 (patch) | |
| tree | 1e3ffed84d8bebd62db81c3226be842e742dc644 | |
| parent | 71fb7ba9e77201005124766b24ab107b1fe908b1 (diff) | |
| download | ltc4000e-eba7b01888cf6383c4e986bc6d5f5d511c6566f7.tar ltc4000e-eba7b01888cf6383c4e986bc6d5f5d511c6566f7.tar.gz ltc4000e-eba7b01888cf6383c4e986bc6d5f5d511c6566f7.tar.bz2 ltc4000e-eba7b01888cf6383c4e986bc6d5f5d511c6566f7.tar.xz ltc4000e-eba7b01888cf6383c4e986bc6d5f5d511c6566f7.zip  | |
Convert interrupts to use node timers
| -rw-r--r-- | fw.lua | 66 | ||||
| -rw-r--r-- | init.lua | 107 | 
2 files changed, 128 insertions, 45 deletions
@@ -157,7 +157,7 @@ elseif was_timed then  end  --Detector signal handling -if (not mem.phaselocked and not mem.preempt) and event.type == "digiline" and string.sub(event.channel,1,9) == "detector_" then +if (not mem.phaselocked and not mem.preempt) and event.type == "digiline" and string.sub(event.channel,1,9) == "detector_" and not mem.stoptime then  	local detname = string.sub(event.channel,10)  	if mem.stats[detname] then  		mem.stats[detname] = mem.stats[detname] + 1 @@ -191,7 +191,7 @@ if (not mem.phaselocked and not mem.preempt) and event.type == "digiline" and st  	end  end -if event.type == "digiline" and mem.pedbuttontype == 2 and event.channel == "pedbutton" then +if event.type == "digiline" and mem.pedbuttontype == 2 and event.channel == "pedbutton" and not mem.stoptime then  	if event.msg == "main" then  		log("Emulating detector_ap/cp for TrafficNeXt compatibility",true)  		mem.det.ap = true @@ -214,6 +214,8 @@ if event.type == "interrupt" and (event.iid == "gapout" or event.iid == "maxgree  			log("Reached maximum green",true)  		end  		--Either gapped out or reached max green, go to next step +		interrupt(nil,"gapout") +		interrupt(nil,"maxgreen")  		interrupt(0,"tick")  	end  end @@ -224,7 +226,7 @@ if (event.channel == "detector_b" or event.channel == "detector_d") and (mem.cyc  end  --Preemption logic -if event.type == "digiline" and string.sub(event.channel,1,8) == "preempt_" then +if event.type == "digiline" and string.sub(event.channel,1,8) == "preempt_" and not mem.stoptime 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) @@ -251,7 +253,7 @@ if event.type == "digiline" and string.sub(event.channel,1,8) == "preempt_" then  end  --Phase logic for already-running cycles -if mem.busy and event.type == "interrupt" and event.iid == "tick" and not mem.phaselocked then +if mem.busy and event.type == "interrupt" and (event.iid == "tick" or event.iid == "manualtick") and not mem.phaselocked and (event.iid == "manualtick" or not mem.stoptime) then  	log("Continuing existing cycle at phase "..mem.cycle,true)  	if mem.cycle == "preempt_yellow" then  		for k,v in pairs(mem.currentphase) do @@ -580,7 +582,7 @@ 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 (not mem.busy) and detactive and (not mem.stoptime) then  	if mem.phaselocked then  		log("Not starting cycle due to phase lock",true)  	elseif mem.preempt then @@ -656,6 +658,8 @@ if event.type == "digiline" and event.channel == "touchscreen" then  			mem.menu = "stats"  		elseif fields.mode then  			mem.menu = "mode" +		elseif fields.diag then +			mem.menu = "diag"  		end  	elseif mem.menu == "run" then  		if fields.menu then @@ -712,7 +716,7 @@ if event.type == "digiline" and event.channel == "touchscreen" then  			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 ^^ +					interrupt(0,"tick") --Forces the program to be re-run since the cycle logic is up there ^^  				end  			end  		end @@ -770,7 +774,33 @@ if event.type == "digiline" and event.channel == "touchscreen" then  			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 +			interrupt(0,"rerun") -- Some of these need to take immediate effect +		end +	elseif mem.menu == "diag" then +		if fields.cancel then +			mem.menu = "main" +		elseif fields.startstop then +			if mem.stoptime then +				mem.stoptime = false +				interrupt(1,"tick") +			else +				mem.stoptime = true +				interrupt(nil,"tick") +			end +		elseif fields.stepnow then +			interrupt(0,"manualtick") +		elseif fields.reboot then +			mem.phaselocked = true +			mem.cycle = nil +			mem.menu = "reboot" +			mem.busy = false +			mem.preempt = nil +			mem.stoptime = true +			interrupt(10,"reboot") +			interrupt(nil,"step") +			interrupt(nil,"rerun") +			interrupt(nil,"gapout") +			interrupt(nil,"maxgreen")  		end  	else  		logfault("Unrecognized menu "..mem.menu,false) @@ -778,6 +808,14 @@ if event.type == "digiline" and event.channel == "touchscreen" then  	end  end +if event.iid == "reboot" then +	mem.phaselocked = false +	mem.menu = "main" +	mem.stoptime = false +	mem.cycle = nil +	interrupt(0,"tick") +end +  --Light control signal sending  if mem.phaselocked then  	mem.busy = false @@ -815,6 +853,7 @@ if mem.menu == "main" then  	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"}) +	table.insert(disp,{command="addbutton",X=7,Y=7,W=2,H=1,name="diag",label="Diagnostics"})  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"}) @@ -901,8 +940,7 @@ elseif mem.menu == "log" then  		table.insert(disp,{command="addlabel",X=0,Y=1.5,label="No Faults"})  	end  elseif mem.menu == "monitoring" then -	--interrupt(0.6,"monflash") -	mem.monflash = true +	interrupt(1,"monflash")  	local monitor_textures = {}  	monitor_textures.O = "streets_tl_off.png" @@ -1130,6 +1168,16 @@ elseif mem.menu == "mode" then  	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"}) +elseif mem.menu == "diag" then +	table.insert(disp,{command="addlabel",X=0,Y=0,label="Diagnostics"}) +	table.insert(disp,{command="addlabel",X=0,Y=1,label="State: "..(mem.cycle or "Idle")}) +	table.insert(disp,{command="addbutton",X=0,Y=2,W=2,H=1,name="startstop",label=(mem.stoptime and "Start Time" or "Stop Time")}) +	table.insert(disp,{command="addbutton",X=0,Y=3,W=2,H=1,name="stepnow",label="Next Step"}) +	table.insert(disp,{command="addbutton",X=0,Y=5,W=2,H=1,name="reboot",label="Reboot"}) +	table.insert(disp,{command="addbutton",X=0,Y=7,W=2,H=1,name="cancel",label="Back"}) +elseif mem.menu == "reboot" then +	table.insert(disp,{command="addlabel",X=0,Y=0,label="Rebooting, please wait..."}) +	table.insert(disp,{command="addlabel",X=0,Y=0.5,label="This will take about 10 seconds."})  else  	logfault("Unrecognized menu "..mem.menu,false)  	mem.menu = "run" @@ -230,6 +230,43 @@ local function ts_on_digiline_receive(pos,msg)  	update_ts_formspec(pos,data)  end +--Interrupt node timer stuff +local function getnextinterrupt(interrupts) +	local nextint = 0 +	for k,v in pairs(interrupts) do +		if nextint == 0 or v < nextint then +			nextint = v +		end +	end +	if nextint ~= 0 then return(nextint) end +end + +local function getcurrentinterrupts(interrupts) +	local current = {} +	for k,v in pairs(interrupts) do +		if v <= os.time() then +			table.insert(current,k) +		end +	end +	return(current) +end + +local function setinterrupt(pos,time,iid) +	local meta = minetest.get_meta(pos) +	local timer = minetest.get_node_timer(pos) +	local interrupts = minetest.deserialize(meta:get_string("interrupts")) or {} +	if time == nil then +		interrupts[iid] = nil +	else +		interrupts[iid] = os.time()+time +	end +	local nextint = getnextinterrupt(interrupts) +	if nextint then +		timer:start(nextint-os.time()) +	end +	meta:set_string("interrupts",minetest.serialize(interrupts)) +end +  --Load the (mostly unmodified) firmware  local fw = loadfile(minetest.get_modpath("ltc4000e")..DIR_DELIM.."fw.lua") @@ -284,22 +321,9 @@ local function run(pos,event)  	end  	function context.interrupt(time,iid) -		--Enforce a minimum interrupt time of half a second -		time = math.max(time,0.5) -		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 +		--Enforce a minimum interrupt time of one second +		if time ~= nil then time = math.max(time,1) end +		setinterrupt(pos,time,iid)  	end  	--This is where the magic happens... @@ -316,6 +340,33 @@ local function run(pos,event)  	meta:set_string("mem",minetest.serialize(context.mem))  end +local function oninterrupt(pos) +	local meta = minetest.get_meta(pos) +	local timer = minetest.get_node_timer(pos) +	local interrupts = minetest.deserialize(meta:get_string("interrupts")) or {} +	local current = getcurrentinterrupts(interrupts) +	for _,i in ipairs(current) do +		interrupts[i] = nil +		local event = {} +		event.type = "interrupt" +		event.iid = i +		run(pos,event) +	end +	local interrupts = minetest.deserialize(meta:get_string("interrupts")) or {} --Reload as it may have changed +	for _,i in ipairs(current) do +		if interrupts[i] and interrupts[i] <= os.time() then +			interrupts[i] = nil +		end +	end +	local nextint = getnextinterrupt(interrupts) +	if nextint then +		timer:start(nextint-os.time()) +	else +		timer:stop() +	end +	meta:set_string("interrupts",minetest.serialize(interrupts)) +end +  local function ts_on_receive_fields(pos,formname,fields,sender)  	local meta = minetest.get_meta(pos)  	local playername = sender:get_player_name() @@ -358,12 +409,7 @@ minetest.register_node("ltc4000e:polemount", {  		local event = {type="program"}  		run(pos,event)  	end, -	on_timer = function(pos) -		local event = {} -		event.type = "interrupt" -		event.iid = "gapout" -		run(pos,event) -	end, +	on_timer = oninterrupt,  	node_box = {  		type = "fixed",  		fixed = polemount_nodebox @@ -466,12 +512,7 @@ minetest.register_node("ltc4000e:nema_bottom", {  		end,pos)  		return ret  	end, -	on_timer = function(pos) -		local event = {} -		event.type = "interrupt" -		event.iid = "gapout" -		run(pos,event) -	end, +	on_timer = oninterrupt,  	on_punch = function(pos,node,puncher)  		if not puncher:is_player() then  			return @@ -615,12 +656,7 @@ minetest.register_node("ltc4000e:nema_bottom_open", {  		minetest.set_node(fronttoppos,{name="air"})  	end,  	on_rotate = false, -	on_timer = function(pos) -		local event = {} -		event.type = "interrupt" -		event.iid = "gapout" -		run(pos,event) -	end, +	on_timer = oninterrupt,  	on_punch = function(pos,node,puncher)  		if not puncher:is_player() then  			return @@ -749,12 +785,11 @@ minetest.register_node("ltc4000e:door_top", {  	sounds = default.node_sound_metal_defaults()  }) ---Make sure lights don't "stall" if unloaded +--Make sure lights don't "stall" if unloaded and not yet converted to node timers  minetest.register_lbm({  	label = "Restart LTC-4000E timers",  	name = "ltc4000e:restart_timers",  	nodenames = {"ltc4000e:polemount","ltc4000e:nema_bottom","ltc4000e:nema_bottom_open"}, -	run_at_every_load = true,  	action = function(pos)  		local meta = minetest.get_meta(pos)  		local mem = minetest.deserialize(meta:get_string("mem"))  | 
