#100 interactive reactor controls (start, scram, reset)

This commit is contained in:
Mikayla Fischler 2022-11-06 18:41:52 -05:00
parent aaab34f1a8
commit 806b217d58
12 changed files with 211 additions and 39 deletions

View File

@ -431,15 +431,27 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, sv_wa
-- unit command acknowledgement
if packet.length == 3 then
local cmd = packet.data[1]
local unit = packet.data[2]
local unit_id = packet.data[2]
local ack = packet.data[3]
local unit = iocontrol.get_db().units[unit_id] ---@type ioctl_entry
if unit ~= nil then
if cmd == CRDN_COMMANDS.SCRAM then
unit.scram_ack(ack)
elseif cmd == CRDN_COMMANDS.START then
unit.start_ack(ack)
elseif cmd == CRDN_COMMANDS.RESET_RPS then
unit.reset_rps_ack(ack)
elseif cmd == CRDN_COMMANDS.SET_BURN then
unit.set_burn_ack(ack)
elseif cmd == CRDN_COMMANDS.SET_WASTE then
unit.set_waste_ack(ack)
else
log.debug(util.c("received command ack with unknown command ", cmd))
end
else
log.debug(util.c("received command ack with unknown unit ", unit_id))
end
else
log.debug("unit command ack packet length mismatch")

View File

@ -38,6 +38,13 @@ function iocontrol.init(conf, comms)
scram = function () end,
reset_rps = function () end,
set_burn = function (rate) end,
set_waste = function (mode) end,
start_ack = function (success) end,
scram_ack = function (success) end,
reset_rps_ack = function (success) end,
set_burn_ack = function (success) end,
set_waste_ack = function (success) end,
reactor_ps = psil.create(),
reactor_data = {}, ---@type reactor_db
@ -71,6 +78,11 @@ function iocontrol.init(conf, comms)
log.debug(util.c("UNIT[", i, "]: SET_BURN = ", rate))
end
function entry.set_waste(mode)
comms.send_command(CRDN_COMMANDS.SET_WASTE, i, mode)
log.debug(util.c("UNIT[", i, "]: SET_WASTE = ", mode))
end
-- create boiler tables
for _ = 1, conf.defs[(i * 2) - 1] do
local data = {} ---@type boilerv_session_db

View File

@ -16,7 +16,7 @@ local config = require("coordinator.config")
local coordinator = require("coordinator.coordinator")
local renderer = require("coordinator.renderer")
local COORDINATOR_VERSION = "alpha-v0.6.2"
local COORDINATOR_VERSION = "alpha-v0.6.3"
local print = util.print
local println = util.println

View File

@ -239,15 +239,32 @@ local function init(parent, id)
---@todo radiation monitor
IndicatorLight{parent=annunciator,label="Radiation Monitor",colors=cpair(colors.green,colors.gray)}
IndicatorLight{parent=annunciator,label="Radiation Alarm",colors=cpair(colors.red,colors.gray),flash=true,period=period.BLINK_250_MS}
DataIndicator{parent=main,x=34,y=51,label="",format="%10.1f",value=0,unit="mSv/h",lu_colors=lu_cpair,width=18,fg_bg=stat_fg_bg}
DataIndicator{parent=main,x=22,y=22,label="",format="%3.2f",value=0,unit="mSv/h",lu_colors=lu_cpair,width=11,fg_bg=stat_fg_bg}
-- reactor controls --
HazardButton{parent=main,x=2,y=44,text="START",accent=colors.lightBlue,callback=unit.start,fg_bg=scram_fg_bg}
HazardButton{parent=main,x=12,y=44,text="SCRAM",accent=colors.yellow,callback=unit.scram,fg_bg=scram_fg_bg}
HazardButton{parent=main,x=22,y=44,text="RESET",accent=colors.red,callback=unit.reset_rps,fg_bg=scram_fg_bg}
local dis_colors = cpair(colors.white, colors.lightGray)
local burn_control = Div{parent=main,x=12,y=40,width=19,height=3,fg_bg=cpair(colors.gray,colors.white)}
local start = HazardButton{parent=main,x=2,y=26,text="START",accent=colors.lightBlue,dis_colors=dis_colors,callback=unit.start,fg_bg=scram_fg_bg}
local scram = HazardButton{parent=main,x=12,y=26,text="SCRAM",accent=colors.yellow,dis_colors=dis_colors,callback=unit.scram,fg_bg=scram_fg_bg}
local reset = HazardButton{parent=main,x=22,y=26,text="RESET",accent=colors.red,dis_colors=dis_colors,callback=unit.reset_rps,fg_bg=scram_fg_bg}
unit.start_ack = start.on_response
unit.scram_ack = scram.on_response
unit.reset_rps_ack = reset.on_response
local function start_button_en_check()
if (unit.reactor_data ~= nil) and (unit.reactor_data.mek_status ~= nil) then
local can_start = (not unit.reactor_data.mek_status.status) and (not unit.reactor_data.rps_tripped)
if can_start then start.enable() else start.disable() end
end
end
r_ps.subscribe("status", start_button_en_check)
r_ps.subscribe("rps_tripped", start_button_en_check)
r_ps.subscribe("rps_tripped", function (active) if active then reset.enable() else reset.disable() end end)
local burn_control = Div{parent=main,x=2,y=22,width=19,height=3,fg_bg=cpair(colors.gray,colors.white)}
local burn_rate = SpinboxNumeric{parent=burn_control,x=2,y=1,whole_num_precision=4,fractional_precision=1,arrow_fg_bg=cpair(colors.gray,colors.white),fg_bg=cpair(colors.black,colors.white)}
TextBox{parent=burn_control,x=9,y=2,text="mB/t"}
@ -280,10 +297,9 @@ local function init(parent, id)
}
---@todo waste selection
local waste_sel_f = function (s) print("waste: " .. s) end
local waste_sel = Div{parent=main,x=2,y=48,width=29,height=2,fg_bg=cpair(colors.black, colors.white)}
MultiButton{parent=waste_sel,x=1,y=1,options=opts,callback=waste_sel_f,min_width=6,fg_bg=cpair(colors.black, colors.white)}
MultiButton{parent=waste_sel,x=1,y=1,options=opts,callback=unit.set_waste,min_width=6,fg_bg=cpair(colors.black, colors.white)}
TextBox{parent=waste_sel,text="Waste Processing",alignment=TEXT_ALIGN.CENTER,x=1,y=1,height=1}
---@fixme test code

View File

@ -163,6 +163,11 @@ function element.new(args)
function protected.on_update(...)
end
-- callback on control press responses
---@param result any
function protected.response_callback(result)
end
-- get value
function protected.get_value()
return protected.value
@ -354,6 +359,12 @@ function element.new(args)
protected.on_update(...)
end
-- on a control request response
---@param result any
function public.on_response(result)
protected.response_callback(result)
end
-- VISIBILITY --
-- show the element

View File

@ -8,6 +8,7 @@ local element = require("graphics.element")
---@class hazard_button_args
---@field text string text to show on button
---@field accent color accent color for hazard border
---@field dis_colors? cpair text color and border color when disabled
---@field callback function function to call on touch
---@field parent graphics_element
---@field id? string element id
@ -62,6 +63,82 @@ local function hazard_button(args)
e.window.write("\x99\x98\x98\x98\x98\x98\x98\x98\x99")
end
-- on request timeout: recursively calls itself to double flash button text
---@param n integer call count
local function on_timeout(n)
-- start at 0
if n == nil then n = 0 end
if n == 0 then
-- go back off
e.window.setTextColor(args.fg_bg.fgd)
e.window.setCursorPos(3, 2)
e.window.write(args.text)
end
if n >= 4 then
-- done
elseif n % 2 == 0 then
-- toggle text color on after 0.25 seconds
tcd.dispatch(0.25, function ()
e.window.setTextColor(args.accent)
e.window.setCursorPos(3, 2)
e.window.write(args.text)
on_timeout(n + 1)
on_timeout(n + 1)
end)
elseif n % 1 then
-- toggle text color off after 0.25 seconds
tcd.dispatch(0.25, function ()
e.window.setTextColor(args.fg_bg.fgd)
e.window.setCursorPos(3, 2)
e.window.write(args.text)
on_timeout(n + 1)
end)
end
end
-- blink routine for success indication
local function on_success()
e.window.setTextColor(args.fg_bg.fgd)
e.window.setCursorPos(3, 2)
e.window.write(args.text)
end
-- blink routine for failure indication
---@param n integer call count
local function on_failure(n)
-- start at 0
if n == nil then n = 0 end
if n == 0 then
-- go back off
e.window.setTextColor(args.fg_bg.fgd)
e.window.setCursorPos(3, 2)
e.window.write(args.text)
end
if n >= 2 then
-- done
elseif n % 2 == 0 then
-- toggle text color on after 0.5 seconds
tcd.dispatch(0.5, function ()
e.window.setTextColor(args.accent)
e.window.setCursorPos(3, 2)
e.window.write(args.text)
on_failure(n + 1)
end)
elseif n % 1 then
-- toggle text color off after 0.25 seconds
tcd.dispatch(0.25, function ()
e.window.setTextColor(args.fg_bg.fgd)
e.window.setCursorPos(3, 2)
e.window.write(args.text)
on_failure(n + 1)
end)
end
end
-- handle touch
---@param event monitor_touch monitor touch event
---@diagnostic disable-next-line: unused-local
@ -75,12 +152,25 @@ local function hazard_button(args)
e.window.setCursorPos(3, 2)
e.window.write(args.text)
-- restore text color after 1 second
tcd.dispatch(1, function ()
e.window.setTextColor(args.fg_bg.fgd)
e.window.setCursorPos(3, 2)
e.window.write(args.text)
end)
-- abort any other callbacks
tcd.abort(on_timeout)
tcd.abort(on_success)
tcd.abort(on_failure)
-- 1.5 second timeout
tcd.dispatch(1.5, on_timeout)
end
end
-- callback on request response
---@param result boolean true for success, false for failure
function e.response_callback(result)
tcd.abort(on_timeout)
if result then
on_success()
else
on_failure(0)
end
end
@ -90,8 +180,25 @@ local function hazard_button(args)
if val then e.handle_touch(core.events.touch("", 1, 1)) end
end
-- show the button as disabled
function e.disable()
if args.dis_colors then
draw_border(args.dis_colors.color_a)
e.window.setTextColor(args.dis_colors.color_b)
e.window.setCursorPos(3, 2)
e.window.write(args.text)
end
end
-- show the button as enabled
function e.enable()
draw_border(args.accent)
e.window.setTextColor(args.fg_bg.fgd)
e.window.setCursorPos(3, 2)
e.window.write(args.text)
end
-- initial draw of border
---@todo disabling will change border
draw_border(args.accent)
return e.get()

View File

@ -27,7 +27,7 @@ end
---@param time number seconds
---@param f function callback function
function tcallbackdsp.dispatch_unique(time, f)
-- ignore if already registered
-- cancel if already registered
for timer, entry in pairs(registry) do
if entry.callback == f then
-- found an instance of this function reference, abort it
@ -49,6 +49,18 @@ function tcallbackdsp.dispatch_unique(time, f)
-- log.debug(util.c("TCD: queued callback for ", f, " [timer: ", timer, "]"))
end
-- abort a requested callback
---@param f function callback function
function tcallbackdsp.abort(f)
for timer, entry in pairs(registry) do
if entry.callback == f then
-- cancel event and remove from registry (even if it fires it won't call)
util.cancel_timer(timer)
registry[timer] = nil
end
end
end
-- lookup a timer event and execute the callback if found
---@param event integer timer event timer ID
function tcallbackdsp.handle(event)

View File

@ -29,6 +29,7 @@ local CRD_S_CMDS = {
}
local CRD_S_DATA = {
CMD_ACK = 1
}
coordinator.CRD_S_CMDS = CRD_S_CMDS
@ -271,6 +272,12 @@ function coordinator.new_session(id, in_queue, out_queue, facility_units)
end
elseif message.qtype == mqueue.TYPE.DATA then
-- instruction with body
local cmd = message.message ---@type queue_data
if cmd.key == CRD_S_DATA.CMD_ACK then
local ack = cmd.val ---@type coord_ack
_send(SCADA_CRDN_TYPES.COMMAND_UNIT, { ack.cmd, ack.unit, ack.ack })
end
end
end

View File

@ -74,14 +74,12 @@ function plc.new_session(id, for_reactor, in_queue, out_queue)
struct_req = (util.time() + 500),
status_req = (util.time() + 500),
scram_req = 0,
enable_req = 0,
burn_rate_req = 0,
rps_reset_req = 0
},
-- command acknowledgements
acks = {
scram = true,
enable = true,
burn_rate = true,
rps_reset = true
},
@ -355,7 +353,6 @@ function plc.new_session(id, for_reactor, in_queue, out_queue)
-- enable acknowledgement
local ack = _get_ack(pkt)
if ack then
self.acks.enable = true
self.sDB.control_state = true
elseif ack == false then
log.debug(log_header .. "enable failed!")
@ -537,8 +534,6 @@ function plc.new_session(id, for_reactor, in_queue, out_queue)
local cmd = message.message
if cmd == PLC_S_CMDS.ENABLE then
-- enable reactor
self.acks.enable = false
self.retry_times.enable_req = util.time() + INITIAL_WAIT
_send(RPLC_TYPES.RPS_ENABLE, {})
elseif cmd == PLC_S_CMDS.SCRAM then
-- SCRAM reactor
@ -635,15 +630,6 @@ function plc.new_session(id, for_reactor, in_queue, out_queue)
end
end
-- enable request retry
if not self.acks.enable then
if rtimes.enable_req - util.time() <= 0 then
_send(RPLC_TYPES.RPS_ENABLE, {})
rtimes.enable_req = util.time() + RETRY_PERIOD
end
end
-- burn rate request retry
if not self.acks.burn_rate then

View File

@ -14,6 +14,11 @@ local SV_Q_DATA = {
CRDN_ACK = 7
}
---@class coord_ack
---@field unit integer
---@field cmd integer
---@field ack boolean
svqtypes.SV_Q_CMDS = SV_Q_CMDS
svqtypes.SV_Q_DATA = SV_Q_DATA

View File

@ -17,6 +17,7 @@ local SV_Q_DATA = svqtypes.SV_Q_DATA
local PLC_S_CMDS = plc.PLC_S_CMDS
local PLC_S_DATA = plc.PLC_S_DATA
local CRD_S_CMDS = coordinator.CRD_S_CMDS
local CRD_S_DATA = coordinator.CRD_S_DATA
local svsessions = {}
@ -70,7 +71,6 @@ local function _sv_handle_outq(session)
if cmd.key < SV_Q_DATA.__END_PLC_CMDS__ then
-- PLC commands from coordinator
local crdn_sid = session.instance.get_id()
local plc_s = svsessions.get_reactor_session(cmd.val[1])
if plc_s ~= nil then
@ -90,7 +90,11 @@ local function _sv_handle_outq(session)
end
else
if cmd.key == SV_Q_DATA.CRDN_ACK then
---@todo ack to be sent to coordinator
-- ack to be sent to coordinator
local crd_s = svsessions.get_coord_session()
if crd_s ~= nil then
crd_s.in_queue.push_data(CRD_S_DATA.CMD_ACK, cmd.val)
end
end
end
end

View File

@ -13,7 +13,7 @@ local svsessions = require("supervisor.session.svsessions")
local config = require("supervisor.config")
local supervisor = require("supervisor.supervisor")
local SUPERVISOR_VERSION = "beta-v0.6.7"
local SUPERVISOR_VERSION = "beta-v0.6.8"
local print = util.print
local println = util.println