From b5c70b0d3729e7c59bf834ff606d79f60e445445 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 4 Feb 2023 13:47:00 -0500 Subject: [PATCH] fixed process controller assuming ramp complete if burn rate setpoint was identical to setpoint before process control start --- coordinator/iocontrol.lua | 21 ++- coordinator/startup.lua | 2 +- coordinator/ui/components/processctl.lua | 17 +- coordinator/ui/components/unit_detail.lua | 21 ++- coordinator/ui/layout/main_view.lua | 48 +++--- reactor-plc/plc.lua | 7 +- reactor-plc/startup.lua | 3 +- rtu/startup.lua | 6 +- scada-common/comms.lua | 12 +- scada-common/log.lua | 197 +++++++++++----------- supervisor/session/facility.lua | 7 + supervisor/session/plc.lua | 38 ++++- supervisor/session/unit.lua | 3 +- supervisor/startup.lua | 2 +- 14 files changed, 225 insertions(+), 159 deletions(-) diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index d5b0a54..914f7ee 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -23,6 +23,8 @@ local io = {} function iocontrol.init(conf, comms) ---@class ioctl_facility io.facility = { + all_sys_ok = false, + auto_ready = false, auto_active = false, auto_ramping = false, @@ -273,13 +275,15 @@ function iocontrol.update_facility_status(status) local ctl_status = status[1] if type(ctl_status) == "table" then - fac.auto_ready = ctl_status[1] - fac.auto_active = ctl_status[2] > 0 - fac.auto_ramping = ctl_status[3] - fac.auto_scram = ctl_status[4] - fac.status_line_1 = ctl_status[5] - fac.status_line_2 = ctl_status[6] + fac.all_sys_ok = ctl_status[1] + fac.auto_ready = ctl_status[2] + fac.auto_active = ctl_status[3] > 0 + fac.auto_ramping = ctl_status[4] + fac.auto_scram = ctl_status[5] + fac.status_line_1 = ctl_status[6] + fac.status_line_2 = ctl_status[7] + fac.ps.publish("all_sys_ok", fac.all_sys_ok) fac.ps.publish("auto_ready", fac.auto_ready) fac.ps.publish("auto_active", fac.auto_active) fac.ps.publish("auto_ramping", fac.auto_ramping) @@ -287,12 +291,13 @@ function iocontrol.update_facility_status(status) fac.ps.publish("status_line_1", fac.status_line_1) fac.ps.publish("status_line_2", fac.status_line_2) - local group_map = ctl_status[7] + local group_map = ctl_status[8] if (type(group_map) == "table") and (#group_map == fac.num_units) then local names = { "Manual", "Primary", "Secondary", "Tertiary", "Backup" } for i = 1, #group_map do - io.units[i].unit_ps.publish("auto_group_id", group_map[i] + 1) + io.units[i].a_group = group_map[i] + io.units[i].unit_ps.publish("auto_group_id", group_map[i]) io.units[i].unit_ps.publish("auto_group", names[group_map[i] + 1]) end end diff --git a/coordinator/startup.lua b/coordinator/startup.lua index f8d3406..e52c1b9 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -19,7 +19,7 @@ local iocontrol = require("coordinator.iocontrol") local renderer = require("coordinator.renderer") local sounder = require("coordinator.sounder") -local COORDINATOR_VERSION = "beta-v0.8.16" +local COORDINATOR_VERSION = "beta-v0.8.17" local print = util.print local println = util.println diff --git a/coordinator/ui/components/processctl.lua b/coordinator/ui/components/processctl.lua index 922799b..0181a4d 100644 --- a/coordinator/ui/components/processctl.lua +++ b/coordinator/ui/components/processctl.lua @@ -50,10 +50,21 @@ local function new_view(root, x, y) facility.scram_ack = scram.on_response - local auto_act = IndicatorLight{parent=main,y=5,label="Auto Active",colors=cpair(colors.green,colors.gray)} - local auto_ramp = IndicatorLight{parent=main,label="Auto Ramping",colors=cpair(colors.white,colors.gray),flash=true,period=period.BLINK_250_MS} - local auto_scram = IndicatorLight{parent=main,label="Auto SCRAM",colors=cpair(colors.red,colors.gray),flash=true,period=period.BLINK_250_MS} + local all_ok = IndicatorLight{parent=main,y=5,label="Unit Systems Online",colors=cpair(colors.green,colors.red)} + local ind_mat = IndicatorLight{parent=main,label="Induction Matrix",colors=cpair(colors.green,colors.gray)} + local rad_mon = IndicatorLight{parent=main,label="Radiation Monitor",colors=cpair(colors.green,colors.gray)} + facility.ps.subscribe("all_sys_ok", all_ok.update) + facility.induction_ps_tbl[1].subscribe("computed_status", function (status) ind_mat.update(status > 1) end) + + main.line_break() + + local auto_ready = IndicatorLight{parent=main,label="Configured Units Ready",colors=cpair(colors.green,colors.red)} + local auto_act = IndicatorLight{parent=main,label="Process Active",colors=cpair(colors.green,colors.gray)} + local auto_ramp = IndicatorLight{parent=main,label="Process Ramping",colors=cpair(colors.white,colors.gray),flash=true,period=period.BLINK_250_MS} + local auto_scram = IndicatorLight{parent=main,label="Automatic SCRAM",colors=cpair(colors.red,colors.gray),flash=true,period=period.BLINK_250_MS} + + facility.ps.subscribe("auto_ready", auto_ready.update) facility.ps.subscribe("auto_active", auto_act.update) facility.ps.subscribe("auto_ramping", auto_ramp.update) facility.ps.subscribe("auto_scram", auto_scram.update) diff --git a/coordinator/ui/components/unit_detail.lua b/coordinator/ui/components/unit_detail.lua index ccb3266..99990e8 100644 --- a/coordinator/ui/components/unit_detail.lua +++ b/coordinator/ui/components/unit_detail.lua @@ -354,7 +354,7 @@ local function init(parent, id) 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) and - (not unit.annunciator.AutoControl) + (unit.a_group == 0) if can_start then start.enable() else start.disable() end end end @@ -459,7 +459,7 @@ local function init(parent, id) local group = RadioButton{parent=auto_div,options=ctl_opts,callback=function()end,radio_colors=cpair(colors.blue,colors.white),radio_bg=colors.gray} - u_ps.subscribe("auto_group_id", group.set_value) + u_ps.subscribe("auto_group_id", function (gid) group.set_value(gid + 1) end) auto_div.line_break() @@ -485,18 +485,27 @@ local function init(parent, id) a_stb.update(unit.annunciator.AutoControl and (not active)) end) + -- enable and disable controls based on group assignment + u_ps.subscribe("auto_group_id", function (gid) + start_button_en_check() + + if gid == 0 then + burn_rate.enable() + set_burn_btn.enable() + else + burn_rate.disable() + set_burn_btn.disable() + end + end) + -- enable and disable controls based on auto control state (start button is handled separately) u_ps.subscribe("AutoControl", function (auto_active) start_button_en_check() if auto_active then - burn_rate.disable() - set_burn_btn.disable() set_grp_btn.disable() a_stb.update(unit.reactor_data.mek_status.status == false) else - burn_rate.enable() - set_burn_btn.enable() set_grp_btn.enable() a_stb.update(false) end diff --git a/coordinator/ui/layout/main_view.lua b/coordinator/ui/layout/main_view.lua index f1b68ad..cf41366 100644 --- a/coordinator/ui/layout/main_view.lua +++ b/coordinator/ui/layout/main_view.lua @@ -88,33 +88,33 @@ local function init(monitor) -- testing ---@fixme remove test code - ColorMap{parent=main,x=132,y=(main.height()-1)} + ColorMap{parent=main,x=98,y=(main.height()-1)} - local audio = Div{parent=main,width=34,height=15,x=95,y=cnc_y_start} + local audio = Div{parent=main,width=23,height=23,x=107,y=cnc_y_start} - PushButton{parent=audio,x=1,y=1,text="TEST 1",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_1} - PushButton{parent=audio,x=1,text="TEST 2",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_2} - PushButton{parent=audio,x=1,text="TEST 3",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_3} - PushButton{parent=audio,x=1,text="TEST 4",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_4} - PushButton{parent=audio,x=1,text="TEST 5",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_5} - PushButton{parent=audio,x=1,text="TEST 6",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_6} - PushButton{parent=audio,x=1,text="TEST 7",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_7} - PushButton{parent=audio,x=1,text="TEST 8",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_8} - PushButton{parent=audio,x=1,text="STOP",min_width=8,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.stop} - PushButton{parent=audio,x=1,text="PSCALE",min_width=8,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_power_scale} + PushButton{parent=audio,x=16,y=1,text="TEST 1",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_1} + PushButton{parent=audio,x=16,text="TEST 2",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_2} + PushButton{parent=audio,x=16,text="TEST 3",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_3} + PushButton{parent=audio,x=16,text="TEST 4",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_4} + PushButton{parent=audio,x=16,text="TEST 5",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_5} + PushButton{parent=audio,x=16,text="TEST 6",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_6} + PushButton{parent=audio,x=16,text="TEST 7",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_7} + PushButton{parent=audio,x=16,text="TEST 8",min_width=8,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_8} + PushButton{parent=audio,x=16,text="STOP",min_width=8,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.stop} + PushButton{parent=audio,x=16,text="PSCALE",min_width=8,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_power_scale} - SwitchButton{parent=audio,x=11,y=1,text="CONTAINMENT BREACH",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_breach} - SwitchButton{parent=audio,x=11,text="CONTAINMENT RADIATION",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_rad} - SwitchButton{parent=audio,x=11,text="REACTOR LOST",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_lost} - SwitchButton{parent=audio,x=11,text="CRITICAL DAMAGE",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_crit} - SwitchButton{parent=audio,x=11,text="REACTOR DAMAGE",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_dmg} - SwitchButton{parent=audio,x=11,text="REACTOR OVER TEMP",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_overtemp} - SwitchButton{parent=audio,x=11,text="REACTOR HIGH TEMP",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_hightemp} - SwitchButton{parent=audio,x=11,text="REACTOR WASTE LEAK",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_wasteleak} - SwitchButton{parent=audio,x=11,text="REACTOR WASTE HIGH",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_highwaste} - SwitchButton{parent=audio,x=11,text="RPS TRANSIENT",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_rps} - SwitchButton{parent=audio,x=11,text="RCS TRANSIENT",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_rcs} - SwitchButton{parent=audio,x=11,text="TURBINE TRIP",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_turbinet} + SwitchButton{parent=audio,x=1,y=12,text="CONTAINMENT BREACH",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_breach} + SwitchButton{parent=audio,x=1,text="CONTAINMENT RADIATION",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_rad} + SwitchButton{parent=audio,x=1,text="REACTOR LOST",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_lost} + SwitchButton{parent=audio,x=1,text="CRITICAL DAMAGE",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_crit} + SwitchButton{parent=audio,x=1,text="REACTOR DAMAGE",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_dmg} + SwitchButton{parent=audio,x=1,text="REACTOR OVER TEMP",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_overtemp} + SwitchButton{parent=audio,x=1,text="REACTOR HIGH TEMP",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_hightemp} + SwitchButton{parent=audio,x=1,text="REACTOR WASTE LEAK",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_wasteleak} + SwitchButton{parent=audio,x=1,text="REACTOR WASTE HIGH",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_highwaste} + SwitchButton{parent=audio,x=1,text="RPS TRANSIENT",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_rps} + SwitchButton{parent=audio,x=1,text="RCS TRANSIENT",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_rcs} + SwitchButton{parent=audio,x=1,text="TURBINE TRIP",min_width=23,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.white,colors.gray),callback=sounder.test_turbinet} local imatrix_1 = imatrix(main, 131, cnc_y_start, facility.induction_data_tbl[1], facility.induction_ps_tbl[1]) diff --git a/reactor-plc/plc.lua b/reactor-plc/plc.lua index 7381c00..57c8471 100644 --- a/reactor-plc/plc.lua +++ b/reactor-plc/plc.lua @@ -68,7 +68,7 @@ function plc.rps_init(reactor, is_formed) formed = is_formed, force_disabled = false, tripped = false, - trip_cause = "" ---@type rps_trip_cause + trip_cause = "ok" ---@type rps_trip_cause } ---@class rps @@ -410,6 +410,7 @@ function plc.comms(id, version, modem, local_port, server_port, reactor, rps, co scrammed = false, linked = false, resend_build = false, + auto_ack_token = 0, status_cache = nil, max_burn_rate = nil } @@ -656,6 +657,7 @@ function plc.comms(id, version, modem, local_port, server_port, reactor, rps, co (not self.scrammed), -- requested control state no_reactor, -- no reactor peripheral connected formed, -- reactor formed + self.auto_ack_token, -- token to indicate auto command has been received before this status update heating_rate, -- heating rate mek_data -- mekanism status data } @@ -808,10 +810,11 @@ function plc.comms(id, version, modem, local_port, server_port, reactor, rps, co _send_ack(packet.type, true) elseif packet.type == RPLC_TYPES.AUTO_BURN_RATE then -- automatic control requested a new burn rate - if (packet.length == 2) and (type(packet.data[1]) == "number") then + if (packet.length == 3) and (type(packet.data[1]) == "number") and (type(packet.data[3]) == "number") then local ack = AUTO_ACK.FAIL local burn_rate = math.floor(packet.data[1] * 10) / 10 local ramp = packet.data[2] + self.auto_ack_token = packet.data[3] -- if no known max burn rate, check again if self.max_burn_rate == nil then diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua index f12bfc0..8ac0de2 100644 --- a/reactor-plc/startup.lua +++ b/reactor-plc/startup.lua @@ -14,7 +14,7 @@ local config = require("reactor-plc.config") local plc = require("reactor-plc.plc") local threads = require("reactor-plc.threads") -local R_PLC_VERSION = "beta-v0.10.3" +local R_PLC_VERSION = "beta-v0.10.4" local print = util.print local println = util.println @@ -169,6 +169,7 @@ local function main() log.debug("init> running without networking") end +---@diagnostic disable-next-line: param-type-mismatch util.push_event("clock_start") println("boot> completed") diff --git a/rtu/startup.lua b/rtu/startup.lua index 2188549..dec02ab 100644 --- a/rtu/startup.lua +++ b/rtu/startup.lua @@ -25,7 +25,7 @@ local sna_rtu = require("rtu.dev.sna_rtu") local sps_rtu = require("rtu.dev.sps_rtu") local turbinev_rtu = require("rtu.dev.turbinev_rtu") -local RTU_VERSION = "beta-v0.9.11" +local RTU_VERSION = "beta-v0.9.12" local rtu_t = types.rtu_t @@ -362,6 +362,10 @@ local function main() table.insert(units, rtu_unit) + if not formed then + log.debug(util.c("configure> device '", name, "' is not formed")) + end + local for_message = "facility" if for_reactor > 0 then for_message = util.c("reactor ", for_reactor) diff --git a/scada-common/comms.lua b/scada-common/comms.lua index 6fed4d5..b1a68f7 100644 --- a/scada-common/comms.lua +++ b/scada-common/comms.lua @@ -12,7 +12,7 @@ local rtu_t = types.rtu_t local insert = table.insert -comms.version = "1.1.2" +comms.version = "1.2.0" ---@alias PROTOCOLS integer local PROTOCOLS = { @@ -140,11 +140,11 @@ function comms.scada_packet() local self = { modem_msg_in = nil, valid = false, - raw = nil, - seq_num = nil, - protocol = nil, - length = nil, - payload = nil + raw = { -1, -1, {} }, + seq_num = -1, + protocol = -1, + length = 0, + payload = {} } ---@class scada_packet diff --git a/scada-common/log.lua b/scada-common/log.lua index 2aeb714..cc6dd0a 100644 --- a/scada-common/log.lua +++ b/scada-common/log.lua @@ -110,73 +110,40 @@ function log.dmesg(msg, tag, tag_color) local t_stamp = string.format("%12.2f", os.clock()) local out = _log_sys.dmesg_out - local out_w, out_h = out.getSize() - local lines = { msg } + if out ~= nil then + local out_w, out_h = out.getSize() - -- wrap if needed - if string.len(msg) > out_w then - local remaining = true - local s_start = 1 - local s_end = out_w - local i = 1 + local lines = { msg } - lines = {} + -- wrap if needed + if string.len(msg) > out_w then + local remaining = true + local s_start = 1 + local s_end = out_w + local i = 1 - while remaining do - local line = string.sub(msg, s_start, s_end) + lines = {} - if line == "" then - remaining = false - else - lines[i] = line + while remaining do + local line = string.sub(msg, s_start, s_end) - s_start = s_end + 1 - s_end = s_end + out_w - i = i + 1 + if line == "" then + remaining = false + else + lines[i] = line + + s_start = s_end + 1 + s_end = s_end + out_w + i = i + 1 + end end end - end - -- start output with tag and time, assuming we have enough width for this to be on one line - local cur_x, cur_y = out.getCursorPos() + -- start output with tag and time, assuming we have enough width for this to be on one line + local cur_x, cur_y = out.getCursorPos() - if cur_x > 1 then - if cur_y == out_h then - out.scroll(1) - out.setCursorPos(1, cur_y) - else - out.setCursorPos(1, cur_y + 1) - end - end - - -- colored time - local initial_color = out.getTextColor() - out.setTextColor(colors.white) - out.write("[") - out.setTextColor(colors.lightGray) - out.write(t_stamp) - ts_coord.x2, ts_coord.y = out.getCursorPos() - ts_coord.x2 = ts_coord.x2 - 1 - out.setTextColor(colors.white) - out.write("] ") - - -- print optionally colored tag - if tag ~= "" then - out.write("[") - if tag_color then out.setTextColor(tag_color) end - out.write(tag) - out.setTextColor(colors.white) - out.write("] ") - end - - out.setTextColor(initial_color) - - -- output message - for i = 1, #lines do - cur_x, cur_y = out.getCursorPos() - - if i > 1 and cur_x > 1 then + if cur_x > 1 then if cur_y == out_h then out.scroll(1) out.setCursorPos(1, cur_y) @@ -185,10 +152,46 @@ function log.dmesg(msg, tag, tag_color) end end - out.write(lines[i]) - end + -- colored time + local initial_color = out.getTextColor() + out.setTextColor(colors.white) + out.write("[") + out.setTextColor(colors.lightGray) + out.write(t_stamp) + ts_coord.x2, ts_coord.y = out.getCursorPos() + ts_coord.x2 = ts_coord.x2 - 1 + out.setTextColor(colors.white) + out.write("] ") - _log(util.c("[", t_stamp, "] [", tag, "] ", msg)) + -- print optionally colored tag + if tag ~= "" then + out.write("[") + if tag_color then out.setTextColor(tag_color) end + out.write(tag) + out.setTextColor(colors.white) + out.write("] ") + end + + out.setTextColor(initial_color) + + -- output message + for i = 1, #lines do + cur_x, cur_y = out.getCursorPos() + + if i > 1 and cur_x > 1 then + if cur_y == out_h then + out.scroll(1) + out.setCursorPos(1, cur_y) + else + out.setCursorPos(1, cur_y + 1) + end + end + + out.write(lines[i]) + end + + _log(util.c("[", t_stamp, "] [", tag, "] ", msg)) + end return ts_coord end @@ -204,52 +207,56 @@ function log.dmesg_working(msg, tag, tag_color) local out = _log_sys.dmesg_out local width = (ts_coord.x2 - ts_coord.x1) + 1 - local initial_color = out.getTextColor() + if out ~= nil then + local initial_color = out.getTextColor() - local counter = 0 + local counter = 0 - local function update(sec_remaining) - local time = util.sprintf("%ds", sec_remaining) - local available = width - (string.len(time) + 2) - local progress = "" + local function update(sec_remaining) + local time = util.sprintf("%ds", sec_remaining) + local available = width - (string.len(time) + 2) + local progress = "" - out.setCursorPos(ts_coord.x1, ts_coord.y) - out.write(" ") + out.setCursorPos(ts_coord.x1, ts_coord.y) + out.write(" ") - if counter % 4 == 0 then - progress = "|" - elseif counter % 4 == 1 then - progress = "/" - elseif counter % 4 == 2 then - progress = "-" - elseif counter % 4 == 3 then - progress = "\\" + if counter % 4 == 0 then + progress = "|" + elseif counter % 4 == 1 then + progress = "/" + elseif counter % 4 == 2 then + progress = "-" + elseif counter % 4 == 3 then + progress = "\\" + end + + out.setTextColor(colors.blue) + out.write(progress) + out.setTextColor(colors.lightGray) + out.write(util.spaces(available) .. time) + out.setTextColor(initial_color) + + counter = counter + 1 end - out.setTextColor(colors.blue) - out.write(progress) - out.setTextColor(colors.lightGray) - out.write(util.spaces(available) .. time) - out.setTextColor(initial_color) + local function done(ok) + out.setCursorPos(ts_coord.x1, ts_coord.y) - counter = counter + 1 - end + if ok or ok == nil then + out.setTextColor(colors.green) + out.write(util.pad("DONE", width)) + else + out.setTextColor(colors.red) + out.write(util.pad("FAIL", width)) + end - local function done(ok) - out.setCursorPos(ts_coord.x1, ts_coord.y) - - if ok or ok == nil then - out.setTextColor(colors.green) - out.write(util.pad("DONE", width)) - else - out.setTextColor(colors.red) - out.write(util.pad("FAIL", width)) + out.setTextColor(initial_color) end - out.setTextColor(initial_color) + return update, done + else + return function () end, function () end end - - return update, done end -- log debug messages diff --git a/supervisor/session/facility.lua b/supervisor/session/facility.lua index a63dfd2..af3e98b 100644 --- a/supervisor/session/facility.lua +++ b/supervisor/session/facility.lua @@ -41,6 +41,7 @@ function facility.new(num_reactors, cooling_conf) induction = {}, redstone = {}, status_text = { "START UP", "initializing..." }, + all_sys_ok = false, -- process control units_ready = false, mode = PROCESS.INACTIVE, @@ -199,6 +200,11 @@ function facility.new(num_reactors, cooling_conf) self.im_stat_init = false end + self.all_sys_ok = true + for i = 1, #self.units do + self.all_sys_ok = self.all_sys_ok and not self.units[i].get_control_inf().degraded + end + ------------------------- -- Run Process Control -- ------------------------- @@ -570,6 +576,7 @@ function facility.new(num_reactors, cooling_conf) -- get automatic process control status function public.get_control_status() return { + self.all_sys_ok, self.units_ready, self.mode, self.waiting_on_ramp, diff --git a/supervisor/session/plc.lua b/supervisor/session/plc.lua index c36d449..bf9037e 100644 --- a/supervisor/session/plc.lua +++ b/supervisor/session/plc.lua @@ -59,6 +59,7 @@ function plc.new_session(id, for_reactor, in_queue, out_queue) out_q = out_queue, commanded_state = false, commanded_burn_rate = 0.0, + auto_cmd_token = 0, ramping_rate = false, auto_scram = false, auto_lock = false, @@ -92,6 +93,7 @@ function plc.new_session(id, for_reactor, in_queue, out_queue) -- session database ---@class reactor_db sDB = { + auto_ack_token = 0, last_status_update = 0, control_state = false, no_reactor = false, @@ -305,18 +307,19 @@ function plc.new_session(id, for_reactor, in_queue, out_queue) -- handle packet by type if pkt.type == RPLC_TYPES.STATUS then -- status packet received, update data - if pkt.length >= 4 then + if pkt.length >= 5 then self.sDB.last_status_update = pkt.data[1] self.sDB.control_state = pkt.data[2] self.sDB.no_reactor = pkt.data[3] self.sDB.formed = pkt.data[4] + self.sDB.auto_ack_token = pkt.data[5] if not self.sDB.no_reactor and self.sDB.formed then - self.sDB.mek_status.heating_rate = pkt.data[5] or 0.0 + self.sDB.mek_status.heating_rate = pkt.data[6] or 0.0 -- attempt to read mek_data table - if pkt.data[6] ~= nil then - local status = pcall(_copy_status, pkt.data[6]) + if pkt.data[7] ~= nil then + local status = pcall(_copy_status, pkt.data[7]) if status then -- copied in status data OK self.received_status_cache = true @@ -496,6 +499,11 @@ function plc.new_session(id, for_reactor, in_queue, out_queue) -- get the session database function public.get_db() return self.sDB end + -- check if ramping is completed by first verifying auto command token ack + function public.is_ramp_complete() + return (self.sDB.auto_ack_token == self.auto_cmd_token) and (self.commanded_burn_rate == self.sDB.mek_status.act_burn_rate) + end + -- get the reactor structure function public.get_struct() if self.received_struct then @@ -618,6 +626,7 @@ function plc.new_session(id, for_reactor, in_queue, out_queue) cmd.val = math.floor(cmd.val * 10) / 10 -- round to 10ths place if cmd.val > 0 and cmd.val <= self.sDB.mek_struct.max_burn then self.commanded_burn_rate = cmd.val + self.auto_cmd_token = 0 self.ramping_rate = false self.acks.burn_rate = false self.retry_times.burn_rate_req = util.time() + INITIAL_WAIT @@ -630,6 +639,7 @@ function plc.new_session(id, for_reactor, in_queue, out_queue) cmd.val = math.floor(cmd.val * 10) / 10 -- round to 10ths place if cmd.val > 0 and cmd.val <= self.sDB.mek_struct.max_burn then self.commanded_burn_rate = cmd.val + self.auto_cmd_token = 0 self.ramping_rate = true self.acks.burn_rate = false self.retry_times.burn_rate_req = util.time() + INITIAL_WAIT @@ -640,14 +650,15 @@ function plc.new_session(id, for_reactor, in_queue, out_queue) -- set automatic burn rate if self.auto_lock then cmd.val = math.floor(cmd.val * 10) / 10 -- round to 10ths place - if cmd.val > 0 and cmd.val <= self.sDB.mek_struct.max_burn then + if cmd.val >= 0 and cmd.val <= self.sDB.mek_struct.max_burn then + self.auto_cmd_token = util.time_ms() self.commanded_burn_rate = cmd.val -- this is only for manual control, only retry auto ramps self.acks.burn_rate = not self.ramping_rate self.retry_times.burn_rate_req = util.time() + INITIAL_AUTO_WAIT - _send(RPLC_TYPES.AUTO_BURN_RATE, { self.commanded_burn_rate, self.ramping_rate }) + _send(RPLC_TYPES.AUTO_BURN_RATE, { self.commanded_burn_rate, self.ramping_rate, self.auto_cmd_token }) end end else @@ -717,10 +728,19 @@ function plc.new_session(id, for_reactor, in_queue, out_queue) if not self.acks.burn_rate then if rtimes.burn_rate_req - util.time() <= 0 then - if self.auto_lock then - _send(RPLC_TYPES.AUTO_BURN_RATE, { self.commanded_burn_rate, self.ramping_rate }) - else + if self.auto_cmd_token > 0 then + if self.auto_lock then + _send(RPLC_TYPES.AUTO_BURN_RATE, { self.commanded_burn_rate, self.ramping_rate, self.auto_cmd_token }) + log.debug("retried auto burn rate?") + else + -- would have been an auto command, but disengaged, so stop retrying + self.acks.burn_rate = true + end + elseif not self.auto_lock then _send(RPLC_TYPES.MEK_BURN_RATE, { self.commanded_burn_rate, self.ramping_rate }) + else + -- shouldn't be in this state, just pretend it was acknowledged + self.acks.burn_rate = true end rtimes.burn_rate_req = util.time() + RETRY_PERIOD diff --git a/supervisor/session/unit.lua b/supervisor/session/unit.lua index 742c5ce..aa0c5b7 100644 --- a/supervisor/session/unit.lua +++ b/supervisor/session/unit.lua @@ -409,8 +409,7 @@ function unit.new(for_reactor, num_boilers, num_turbines) ---@return boolean complete function public.a_ramp_complete() if self.plc_i ~= nil then - local cur_rate = math.floor(self.plc_i.get_db().mek_status.burn_rate * 10) - return (cur_rate == self.ramp_target_br10) or (self.ramp_target_br10 == 0) + return self.plc_i.is_ramp_complete() else return true end end diff --git a/supervisor/startup.lua b/supervisor/startup.lua index ae879f4..2a1eb32 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -14,7 +14,7 @@ local svsessions = require("supervisor.session.svsessions") local config = require("supervisor.config") local supervisor = require("supervisor.supervisor") -local SUPERVISOR_VERSION = "beta-v0.9.11" +local SUPERVISOR_VERSION = "beta-v0.9.12" local print = util.print local println = util.println