Merge branch 'devel' into 580-wired-comms-networking
This commit is contained in:
commit
deeeb612b1
@ -53,7 +53,6 @@ function databus.tx_hw_status(plc_state)
|
|||||||
databus.ps.publish("reactor_dev_state", util.trinary(plc_state.no_reactor, 1, util.trinary(plc_state.reactor_formed, 3, 2)))
|
databus.ps.publish("reactor_dev_state", util.trinary(plc_state.no_reactor, 1, util.trinary(plc_state.reactor_formed, 3, 2)))
|
||||||
databus.ps.publish("has_modem", not plc_state.no_modem)
|
databus.ps.publish("has_modem", not plc_state.no_modem)
|
||||||
databus.ps.publish("degraded", plc_state.degraded)
|
databus.ps.publish("degraded", plc_state.degraded)
|
||||||
databus.ps.publish("init_ok", plc_state.init_ok)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- transmit thread (routine) statuses
|
-- transmit thread (routine) statuses
|
||||||
|
|||||||
@ -51,11 +51,11 @@ local function init(panel)
|
|||||||
|
|
||||||
local system = Div{parent=panel,width=14,height=18,x=2,y=3}
|
local system = Div{parent=panel,width=14,height=18,x=2,y=3}
|
||||||
|
|
||||||
local init_ok = LED{parent=system,label="STATUS",colors=cpair(colors.green,colors.red)}
|
local degraded = LED{parent=system,label="STATUS",colors=cpair(colors.red,colors.green)}
|
||||||
local heartbeat = LED{parent=system,label="HEARTBEAT",colors=ind_grn}
|
local heartbeat = LED{parent=system,label="HEARTBEAT",colors=ind_grn}
|
||||||
system.line_break()
|
system.line_break()
|
||||||
|
|
||||||
init_ok.register(databus.ps, "init_ok", init_ok.update)
|
degraded.register(databus.ps, "degraded", degraded.update)
|
||||||
heartbeat.register(databus.ps, "heartbeat", heartbeat.update)
|
heartbeat.register(databus.ps, "heartbeat", heartbeat.update)
|
||||||
|
|
||||||
local reactor = LEDPair{parent=system,label="REACTOR",off=colors.red,c1=colors.yellow,c2=colors.green}
|
local reactor = LEDPair{parent=system,label="REACTOR",off=colors.red,c1=colors.yellow,c2=colors.green}
|
||||||
|
|||||||
@ -120,7 +120,7 @@ function plc.rps_init(reactor, is_formed)
|
|||||||
reactor_enabled = false,
|
reactor_enabled = false,
|
||||||
enabled_at = 0,
|
enabled_at = 0,
|
||||||
emer_cool_active = nil, ---@type boolean
|
emer_cool_active = nil, ---@type boolean
|
||||||
formed = is_formed,
|
formed = is_formed, ---@type boolean|nil
|
||||||
force_disabled = false,
|
force_disabled = false,
|
||||||
tripped = false,
|
tripped = false,
|
||||||
trip_cause = "ok" ---@type rps_trip_cause
|
trip_cause = "ok" ---@type rps_trip_cause
|
||||||
@ -366,14 +366,16 @@ function plc.rps_init(reactor, is_formed)
|
|||||||
return public.activate()
|
return public.activate()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check all safety conditions
|
-- check all safety conditions if we have a formed reactor, otherwise handle a subset of conditions
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
|
---@param has_reactor boolean if the PLC state indicates we have a reactor
|
||||||
---@return boolean tripped, rps_trip_cause trip_status, boolean first_trip
|
---@return boolean tripped, rps_trip_cause trip_status, boolean first_trip
|
||||||
function public.check()
|
function public.check(has_reactor)
|
||||||
local status = RPS_TRIP_CAUSE.OK
|
local status = RPS_TRIP_CAUSE.OK
|
||||||
local was_tripped = self.tripped
|
local was_tripped = self.tripped
|
||||||
local first_trip = false
|
local first_trip = false
|
||||||
|
|
||||||
|
if has_reactor then
|
||||||
if self.formed then
|
if self.formed then
|
||||||
-- update state
|
-- update state
|
||||||
parallel.waitForAll(
|
parallel.waitForAll(
|
||||||
@ -390,6 +392,10 @@ function plc.rps_init(reactor, is_formed)
|
|||||||
-- check to see if its now formed
|
-- check to see if its now formed
|
||||||
_is_formed()
|
_is_formed()
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
self.formed = nil
|
||||||
|
self.state[CHK.SYS_FAIL] = true
|
||||||
|
end
|
||||||
|
|
||||||
-- check system states in order of severity
|
-- check system states in order of severity
|
||||||
if self.tripped then
|
if self.tripped then
|
||||||
@ -476,6 +482,7 @@ function plc.rps_init(reactor, is_formed)
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
function public.is_active() return self.reactor_enabled end
|
function public.is_active() return self.reactor_enabled end
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
|
---@return boolean|nil formed true if formed, false if not, nil if unknown
|
||||||
function public.is_formed() return self.formed end
|
function public.is_formed() return self.formed end
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function public.is_force_disabled() return self.force_disabled end
|
function public.is_force_disabled() return self.force_disabled end
|
||||||
@ -497,14 +504,14 @@ function plc.rps_init(reactor, is_formed)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- partial RPS reset that only clears fault and sys_fail
|
-- partial RPS reset that only clears fault and sys_fail
|
||||||
function public.reset_formed()
|
function public.reset_reattach()
|
||||||
self.tripped = false
|
self.tripped = false
|
||||||
self.trip_cause = RPS_TRIP_CAUSE.OK
|
self.trip_cause = RPS_TRIP_CAUSE.OK
|
||||||
|
|
||||||
self.state[CHK.FAULT] = false
|
self.state[CHK.FAULT] = false
|
||||||
self.state[CHK.SYS_FAIL] = false
|
self.state[CHK.SYS_FAIL] = false
|
||||||
|
|
||||||
log.info("RPS: partial reset on formed")
|
log.info("RPS: partial reset on connected or formed")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- reset the automatic and timeout trip flags, then clear trip if that was the trip cause
|
-- reset the automatic and timeout trip flags, then clear trip if that was the trip cause
|
||||||
@ -588,11 +595,7 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
-- dynamic reactor status information, excluding heating rate
|
-- dynamic reactor status information, excluding heating rate
|
||||||
---@return table data_table, boolean faulted
|
---@return table data_table, boolean faulted
|
||||||
local function _get_reactor_status()
|
local function _get_reactor_status()
|
||||||
local fuel = nil
|
local fuel, waste, coolant, hcoolant = nil, nil, nil, nil
|
||||||
local waste = nil
|
|
||||||
local coolant = nil
|
|
||||||
local hcoolant = nil
|
|
||||||
|
|
||||||
local data_table = {}
|
local data_table = {}
|
||||||
|
|
||||||
reactor.__p_disable_afc()
|
reactor.__p_disable_afc()
|
||||||
@ -711,6 +714,112 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
reactor.__p_enable_afc()
|
reactor.__p_enable_afc()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- handle a burn rate command
|
||||||
|
---@param packet rplc_frame
|
||||||
|
---@param setpoints plc_setpoints
|
||||||
|
--- EVENT_CONSUMER: this function consumes events
|
||||||
|
local function _handle_burn_rate(packet, setpoints)
|
||||||
|
if (packet.length == 2) and (type(packet.data[1]) == "number") then
|
||||||
|
local success = false
|
||||||
|
local burn_rate = math.floor(packet.data[1] * 10) / 10
|
||||||
|
local ramp = packet.data[2]
|
||||||
|
|
||||||
|
-- if no known max burn rate, check again
|
||||||
|
if self.max_burn_rate == nil then
|
||||||
|
self.max_burn_rate = reactor.getMaxBurnRate()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if we know our max burn rate, update current burn rate setpoint if in range
|
||||||
|
if self.max_burn_rate ~= ppm.ACCESS_FAULT then
|
||||||
|
if burn_rate > 0 and burn_rate <= self.max_burn_rate then
|
||||||
|
if ramp then
|
||||||
|
setpoints.burn_rate_en = true
|
||||||
|
setpoints.burn_rate = burn_rate
|
||||||
|
success = true
|
||||||
|
else
|
||||||
|
reactor.setBurnRate(burn_rate)
|
||||||
|
success = reactor.__p_is_ok()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug(burn_rate .. " rate outside of 0 < x <= " .. self.max_burn_rate)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
_send_ack(packet.type, success)
|
||||||
|
else
|
||||||
|
log.debug("RPLC set burn rate packet length mismatch or non-numeric burn rate")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle an auto burn rate command
|
||||||
|
---@param packet rplc_frame
|
||||||
|
---@param setpoints plc_setpoints
|
||||||
|
--- EVENT_CONSUMER: this function consumes events
|
||||||
|
local function _handle_auto_burn_rate(packet, setpoints)
|
||||||
|
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] * 100) / 100
|
||||||
|
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
|
||||||
|
self.max_burn_rate = reactor.getMaxBurnRate()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if we know our max burn rate, update current burn rate setpoint if in range
|
||||||
|
if self.max_burn_rate ~= ppm.ACCESS_FAULT then
|
||||||
|
if burn_rate < 0.01 then
|
||||||
|
if rps.is_active() then
|
||||||
|
-- auto scram to disable
|
||||||
|
log.debug("AUTO: stopping the reactor to meet 0.0 burn rate")
|
||||||
|
if rps.scram() then
|
||||||
|
ack = AUTO_ACK.ZERO_DIS_OK
|
||||||
|
else
|
||||||
|
log.warning("AUTO: automatic reactor stop failed")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
ack = AUTO_ACK.ZERO_DIS_OK
|
||||||
|
end
|
||||||
|
elseif burn_rate <= self.max_burn_rate then
|
||||||
|
if not rps.is_active() then
|
||||||
|
-- activate the reactor
|
||||||
|
log.debug("AUTO: activating the reactor")
|
||||||
|
|
||||||
|
reactor.setBurnRate(0.01)
|
||||||
|
if reactor.__p_is_faulted() then
|
||||||
|
log.warning("AUTO: failed to reset burn rate for auto activation")
|
||||||
|
else
|
||||||
|
if not rps.auto_activate() then
|
||||||
|
log.warning("AUTO: automatic reactor activation failed")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if active, set/ramp burn rate
|
||||||
|
if rps.is_active() then
|
||||||
|
if ramp then
|
||||||
|
log.debug(util.c("AUTO: setting burn rate ramp to ", burn_rate))
|
||||||
|
setpoints.burn_rate_en = true
|
||||||
|
setpoints.burn_rate = burn_rate
|
||||||
|
ack = AUTO_ACK.RAMP_SET_OK
|
||||||
|
else
|
||||||
|
log.debug(util.c("AUTO: setting burn rate directly to ", burn_rate))
|
||||||
|
reactor.setBurnRate(burn_rate)
|
||||||
|
ack = util.trinary(reactor.__p_is_faulted(), AUTO_ACK.FAIL, AUTO_ACK.DIRECT_SET_OK)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug(util.c(burn_rate, " rate outside of 0 < x <= ", self.max_burn_rate))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
_send_ack(packet.type, ack)
|
||||||
|
else
|
||||||
|
log.debug("RPLC set automatic burn rate packet length mismatch or non-numeric burn rate")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- PUBLIC FUNCTIONS --
|
-- PUBLIC FUNCTIONS --
|
||||||
|
|
||||||
---@class plc_comms
|
---@class plc_comms
|
||||||
@ -807,15 +916,11 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
-- get as RPLC packet
|
-- get as RPLC packet
|
||||||
if s_pkt.protocol() == PROTOCOL.RPLC then
|
if s_pkt.protocol() == PROTOCOL.RPLC then
|
||||||
local rplc_pkt = comms.rplc_packet()
|
local rplc_pkt = comms.rplc_packet()
|
||||||
if rplc_pkt.decode(s_pkt) then
|
if rplc_pkt.decode(s_pkt) then pkt = rplc_pkt.get() end
|
||||||
pkt = rplc_pkt.get()
|
|
||||||
end
|
|
||||||
-- get as SCADA management packet
|
-- get as SCADA management packet
|
||||||
elseif s_pkt.protocol() == PROTOCOL.SCADA_MGMT then
|
elseif s_pkt.protocol() == PROTOCOL.SCADA_MGMT then
|
||||||
local mgmt_pkt = comms.mgmt_packet()
|
local mgmt_pkt = comms.mgmt_packet()
|
||||||
if mgmt_pkt.decode(s_pkt) then
|
if mgmt_pkt.decode(s_pkt) then pkt = mgmt_pkt.get() end
|
||||||
pkt = mgmt_pkt.get()
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
log.debug("unsupported packet type " .. s_pkt.protocol(), true)
|
log.debug("unsupported packet type " .. s_pkt.protocol(), true)
|
||||||
end
|
end
|
||||||
@ -827,16 +932,13 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
-- handle RPLC and MGMT packets
|
-- handle RPLC and MGMT packets
|
||||||
---@param packet rplc_frame|mgmt_frame packet frame
|
---@param packet rplc_frame|mgmt_frame packet frame
|
||||||
---@param plc_state plc_state PLC state
|
---@param plc_state plc_state PLC state
|
||||||
---@param setpoints setpoints setpoint control table
|
---@param setpoints plc_setpoints setpoint control table
|
||||||
function public.handle_packet(packet, plc_state, setpoints)
|
---@param println_ts function console print, when UI isn't running
|
||||||
-- print a log message to the terminal as long as the UI isn't running
|
function public.handle_packet(packet, plc_state, setpoints, println_ts)
|
||||||
local function println_ts(message) if not plc_state.fp_ok then util.println_ts(message) end end
|
|
||||||
|
|
||||||
local protocol = packet.scada_frame.protocol()
|
local protocol = packet.scada_frame.protocol()
|
||||||
local l_chan = packet.scada_frame.local_channel()
|
local l_chan = packet.scada_frame.local_channel()
|
||||||
local src_addr = packet.scada_frame.src_addr()
|
local src_addr = packet.scada_frame.src_addr()
|
||||||
|
|
||||||
-- handle packets now that we have prints setup
|
|
||||||
if l_chan == config.PLC_Channel then
|
if l_chan == config.PLC_Channel then
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.r_seq_num == nil then
|
if self.r_seq_num == nil then
|
||||||
@ -871,36 +973,7 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
log.debug("sent out structure again, did supervisor miss it?")
|
log.debug("sent out structure again, did supervisor miss it?")
|
||||||
elseif packet.type == RPLC_TYPE.MEK_BURN_RATE then
|
elseif packet.type == RPLC_TYPE.MEK_BURN_RATE then
|
||||||
-- set the burn rate
|
-- set the burn rate
|
||||||
if (packet.length == 2) and (type(packet.data[1]) == "number") then
|
_handle_burn_rate(packet, setpoints)
|
||||||
local success = false
|
|
||||||
local burn_rate = math.floor(packet.data[1] * 10) / 10
|
|
||||||
local ramp = packet.data[2]
|
|
||||||
|
|
||||||
-- if no known max burn rate, check again
|
|
||||||
if self.max_burn_rate == nil then
|
|
||||||
self.max_burn_rate = reactor.getMaxBurnRate()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if we know our max burn rate, update current burn rate setpoint if in range
|
|
||||||
if self.max_burn_rate ~= ppm.ACCESS_FAULT then
|
|
||||||
if burn_rate > 0 and burn_rate <= self.max_burn_rate then
|
|
||||||
if ramp then
|
|
||||||
setpoints.burn_rate_en = true
|
|
||||||
setpoints.burn_rate = burn_rate
|
|
||||||
success = true
|
|
||||||
else
|
|
||||||
reactor.setBurnRate(burn_rate)
|
|
||||||
success = reactor.__p_is_ok()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log.debug(burn_rate .. " rate outside of 0 < x <= " .. self.max_burn_rate)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
_send_ack(packet.type, success)
|
|
||||||
else
|
|
||||||
log.debug("RPLC set burn rate packet length mismatch or non-numeric burn rate")
|
|
||||||
end
|
|
||||||
elseif packet.type == RPLC_TYPE.RPS_ENABLE then
|
elseif packet.type == RPLC_TYPE.RPS_ENABLE then
|
||||||
-- enable the reactor
|
-- enable the reactor
|
||||||
self.scrammed = false
|
self.scrammed = false
|
||||||
@ -929,68 +1002,7 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
_send_ack(packet.type, true)
|
_send_ack(packet.type, true)
|
||||||
elseif packet.type == RPLC_TYPE.AUTO_BURN_RATE then
|
elseif packet.type == RPLC_TYPE.AUTO_BURN_RATE then
|
||||||
-- automatic control requested a new burn rate
|
-- automatic control requested a new burn rate
|
||||||
if (packet.length == 3) and (type(packet.data[1]) == "number") and (type(packet.data[3]) == "number") then
|
_handle_auto_burn_rate(packet, setpoints)
|
||||||
local ack = AUTO_ACK.FAIL
|
|
||||||
local burn_rate = math.floor(packet.data[1] * 100) / 100
|
|
||||||
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
|
|
||||||
self.max_burn_rate = reactor.getMaxBurnRate()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if we know our max burn rate, update current burn rate setpoint if in range
|
|
||||||
if self.max_burn_rate ~= ppm.ACCESS_FAULT then
|
|
||||||
if burn_rate < 0.01 then
|
|
||||||
if rps.is_active() then
|
|
||||||
-- auto scram to disable
|
|
||||||
log.debug("AUTO: stopping the reactor to meet 0.0 burn rate")
|
|
||||||
if rps.scram() then
|
|
||||||
ack = AUTO_ACK.ZERO_DIS_OK
|
|
||||||
else
|
|
||||||
log.warning("AUTO: automatic reactor stop failed")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
ack = AUTO_ACK.ZERO_DIS_OK
|
|
||||||
end
|
|
||||||
elseif burn_rate <= self.max_burn_rate then
|
|
||||||
if not rps.is_active() then
|
|
||||||
-- activate the reactor
|
|
||||||
log.debug("AUTO: activating the reactor")
|
|
||||||
|
|
||||||
reactor.setBurnRate(0.01)
|
|
||||||
if reactor.__p_is_faulted() then
|
|
||||||
log.warning("AUTO: failed to reset burn rate for auto activation")
|
|
||||||
else
|
|
||||||
if not rps.auto_activate() then
|
|
||||||
log.warning("AUTO: automatic reactor activation failed")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if active, set/ramp burn rate
|
|
||||||
if rps.is_active() then
|
|
||||||
if ramp then
|
|
||||||
log.debug(util.c("AUTO: setting burn rate ramp to ", burn_rate))
|
|
||||||
setpoints.burn_rate_en = true
|
|
||||||
setpoints.burn_rate = burn_rate
|
|
||||||
ack = AUTO_ACK.RAMP_SET_OK
|
|
||||||
else
|
|
||||||
log.debug(util.c("AUTO: setting burn rate directly to ", burn_rate))
|
|
||||||
reactor.setBurnRate(burn_rate)
|
|
||||||
ack = util.trinary(reactor.__p_is_faulted(), AUTO_ACK.FAIL, AUTO_ACK.DIRECT_SET_OK)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log.debug(util.c(burn_rate, " rate outside of 0 < x <= ", self.max_burn_rate))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
_send_ack(packet.type, ack)
|
|
||||||
else
|
|
||||||
log.debug("RPLC set automatic burn rate packet length mismatch or non-numeric burn rate")
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
log.debug("received unknown RPLC packet type " .. packet.type)
|
log.debug("received unknown RPLC packet type " .. packet.type)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -87,7 +87,6 @@ local function main()
|
|||||||
-- PLC system state flags
|
-- PLC system state flags
|
||||||
---@class plc_state
|
---@class plc_state
|
||||||
plc_state = {
|
plc_state = {
|
||||||
init_ok = true,
|
|
||||||
fp_ok = false,
|
fp_ok = false,
|
||||||
shutdown = false,
|
shutdown = false,
|
||||||
degraded = true,
|
degraded = true,
|
||||||
@ -97,21 +96,24 @@ local function main()
|
|||||||
},
|
},
|
||||||
|
|
||||||
-- control setpoints
|
-- control setpoints
|
||||||
---@class setpoints
|
---@class plc_setpoints
|
||||||
setpoints = {
|
setpoints = {
|
||||||
burn_rate_en = false,
|
burn_rate_en = false,
|
||||||
burn_rate = 0.0
|
burn_rate = 0.0
|
||||||
},
|
},
|
||||||
|
|
||||||
-- core PLC devices
|
-- core PLC devices
|
||||||
|
---@class plc_dev
|
||||||
plc_dev = {
|
plc_dev = {
|
||||||
reactor = ppm.get_fission_reactor(),
|
---@diagnostic disable-next-line: assign-type-mismatch
|
||||||
|
reactor = ppm.get_fission_reactor(), ---@type table
|
||||||
|
modem = nil, ---@type Modem|nil
|
||||||
modem_wired = type(config.WiredModem) == "string",
|
modem_wired = type(config.WiredModem) == "string",
|
||||||
modem_iface = config.WiredModem,
|
modem_iface = config.WiredModem
|
||||||
modem = nil
|
|
||||||
},
|
},
|
||||||
|
|
||||||
-- system objects
|
-- system objects
|
||||||
|
---@class plc_sys
|
||||||
plc_sys = {
|
plc_sys = {
|
||||||
rps = nil, ---@type rps
|
rps = nil, ---@type rps
|
||||||
nic = nil, ---@type nic
|
nic = nil, ---@type nic
|
||||||
@ -143,14 +145,20 @@ local function main()
|
|||||||
|
|
||||||
-- we need a reactor, can at least do some things even if it isn't formed though
|
-- we need a reactor, can at least do some things even if it isn't formed though
|
||||||
if plc_state.no_reactor then
|
if plc_state.no_reactor then
|
||||||
println("init> fission reactor not found")
|
println("startup> fission reactor not found")
|
||||||
log.warning("init> no reactor on startup")
|
log.warning("startup> no reactor on startup")
|
||||||
|
|
||||||
plc_state.init_ok = false
|
|
||||||
plc_state.degraded = true
|
plc_state.degraded = true
|
||||||
|
plc_state.reactor_formed = false
|
||||||
|
|
||||||
|
-- mount a virtual peripheral to init the RPS with
|
||||||
|
local _, dev = ppm.mount_virtual()
|
||||||
|
smem_dev.reactor = dev
|
||||||
|
|
||||||
|
log.info("startup> mounted virtual device as reactor")
|
||||||
elseif not smem_dev.reactor.isFormed() then
|
elseif not smem_dev.reactor.isFormed() then
|
||||||
println("init> fission reactor is not formed")
|
println("startup> fission reactor is not formed")
|
||||||
log.warning("init> reactor logic adapter present, but reactor is not formed")
|
log.warning("startup> reactor logic adapter present, but reactor is not formed")
|
||||||
|
|
||||||
plc_state.degraded = true
|
plc_state.degraded = true
|
||||||
plc_state.reactor_formed = false
|
plc_state.reactor_formed = false
|
||||||
@ -158,89 +166,74 @@ local function main()
|
|||||||
|
|
||||||
-- comms modem is required if networked
|
-- comms modem is required if networked
|
||||||
if __shared_memory.networked and plc_state.no_modem then
|
if __shared_memory.networked and plc_state.no_modem then
|
||||||
println("init> comms modem not found")
|
println("startup> comms modem not found")
|
||||||
log.warning("init> no comms modem on startup")
|
log.warning("startup> no comms modem on startup")
|
||||||
|
|
||||||
-- scram reactor if present and enabled
|
-- scram reactor if present and enabled
|
||||||
if (smem_dev.reactor ~= nil) and plc_state.reactor_formed and smem_dev.reactor.getStatus() then
|
if (smem_dev.reactor ~= nil) and plc_state.reactor_formed and smem_dev.reactor.getStatus() then
|
||||||
smem_dev.reactor.scram()
|
smem_dev.reactor.scram()
|
||||||
end
|
end
|
||||||
|
|
||||||
plc_state.init_ok = false
|
|
||||||
plc_state.degraded = true
|
plc_state.degraded = true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- print a log message to the terminal as long as the UI isn't running
|
|
||||||
local function _println_no_fp(message) if not plc_state.fp_ok then println(message) end end
|
|
||||||
|
|
||||||
-- PLC init<br>
|
|
||||||
--- EVENT_CONSUMER: this function consumes events
|
|
||||||
local function init()
|
|
||||||
-- scram on boot if networked, otherwise leave the reactor be
|
-- scram on boot if networked, otherwise leave the reactor be
|
||||||
if __shared_memory.networked and (not plc_state.no_reactor) and plc_state.reactor_formed and smem_dev.reactor.getStatus() then
|
if __shared_memory.networked and (not plc_state.no_reactor) and plc_state.reactor_formed and smem_dev.reactor.getStatus() then
|
||||||
|
log.debug("startup> power-on SCRAM")
|
||||||
smem_dev.reactor.scram()
|
smem_dev.reactor.scram()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- setup front panel
|
-- setup front panel
|
||||||
if not renderer.ui_ready() then
|
|
||||||
local message
|
local message
|
||||||
plc_state.fp_ok, message = renderer.try_start_ui(config.FrontPanelTheme, config.ColorMode)
|
plc_state.fp_ok, message = renderer.try_start_ui(config.FrontPanelTheme, config.ColorMode)
|
||||||
|
|
||||||
-- ...or not
|
-- ...or not
|
||||||
if not plc_state.fp_ok then
|
if not plc_state.fp_ok then
|
||||||
println_ts(util.c("UI error: ", message))
|
println_ts(util.c("UI error: ", message))
|
||||||
println("init> running without front panel")
|
println("startup> running without front panel")
|
||||||
log.error(util.c("front panel GUI render failed with error ", message))
|
log.error(util.c("front panel GUI render failed with error ", message))
|
||||||
log.info("init> running in headless mode without front panel")
|
log.info("startup> running in headless mode without front panel")
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if plc_state.init_ok then
|
-- print a log message to the terminal as long as the UI isn't running
|
||||||
|
local function _println_no_fp(msg) if not plc_state.fp_ok then println(msg) end end
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
-- initialize PLC
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
-- init reactor protection system
|
-- init reactor protection system
|
||||||
smem_sys.rps = plc.rps_init(smem_dev.reactor, plc_state.reactor_formed)
|
smem_sys.rps = plc.rps_init(smem_dev.reactor, util.trinary(plc_state.no_reactor, nil, plc_state.reactor_formed))
|
||||||
log.debug("init> rps init")
|
log.debug("startup> rps init")
|
||||||
|
|
||||||
|
-- notify user of emergency coolant configuration status
|
||||||
|
if config.EmerCoolEnable then
|
||||||
|
_println_no_fp("startup> emergency coolant control ready")
|
||||||
|
log.info("startup> emergency coolant control available")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- conditionally init comms
|
||||||
if __shared_memory.networked then
|
if __shared_memory.networked then
|
||||||
-- comms watchdog
|
-- comms watchdog
|
||||||
smem_sys.conn_watchdog = util.new_watchdog(config.ConnTimeout)
|
smem_sys.conn_watchdog = util.new_watchdog(config.ConnTimeout)
|
||||||
log.debug("init> conn watchdog started")
|
log.debug("startup> conn watchdog started")
|
||||||
|
|
||||||
-- create network interface then setup comms
|
-- create network interface then setup comms
|
||||||
smem_sys.nic = network.nic(smem_dev.modem)
|
smem_sys.nic = network.nic(smem_dev.modem)
|
||||||
smem_sys.plc_comms = plc.comms(R_PLC_VERSION, smem_sys.nic, smem_dev.reactor, smem_sys.rps, smem_sys.conn_watchdog)
|
smem_sys.plc_comms = plc.comms(R_PLC_VERSION, smem_sys.nic, smem_dev.reactor, smem_sys.rps, smem_sys.conn_watchdog)
|
||||||
log.debug("init> comms init")
|
log.debug("startup> comms init")
|
||||||
else
|
else
|
||||||
_println_no_fp("init> starting in offline mode")
|
_println_no_fp("startup> starting in non-networked mode")
|
||||||
log.info("init> running without networking")
|
log.info("startup> starting without networking")
|
||||||
end
|
|
||||||
|
|
||||||
-- notify user of emergency coolant configuration status
|
|
||||||
if config.EmerCoolEnable then
|
|
||||||
println("init> emergency coolant control ready")
|
|
||||||
log.info("init> running with emergency coolant control available")
|
|
||||||
end
|
|
||||||
|
|
||||||
util.push_event("clock_start")
|
|
||||||
|
|
||||||
_println_no_fp("init> completed")
|
|
||||||
log.info("init> startup completed")
|
|
||||||
else
|
|
||||||
_println_no_fp("init> system in degraded state, awaiting devices...")
|
|
||||||
log.warning("init> started in a degraded state, awaiting peripheral connections...")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
databus.tx_hw_status(plc_state)
|
databus.tx_hw_status(plc_state)
|
||||||
end
|
|
||||||
|
|
||||||
----------------------------------------
|
_println_no_fp("startup> completed")
|
||||||
-- start system
|
log.info("startup> completed")
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
-- initialize PLC
|
|
||||||
init()
|
|
||||||
|
|
||||||
-- init threads
|
-- init threads
|
||||||
local main_thread = threads.thread__main(__shared_memory, init)
|
local main_thread = threads.thread__main(__shared_memory)
|
||||||
local rps_thread = threads.thread__rps(__shared_memory)
|
local rps_thread = threads.thread__rps(__shared_memory)
|
||||||
|
|
||||||
if __shared_memory.networked then
|
if __shared_memory.networked then
|
||||||
@ -254,14 +247,12 @@ local function main()
|
|||||||
-- run threads
|
-- run threads
|
||||||
parallel.waitForAll(main_thread.p_exec, rps_thread.p_exec, comms_thread_tx.p_exec, comms_thread_rx.p_exec, sp_ctrl_thread.p_exec)
|
parallel.waitForAll(main_thread.p_exec, rps_thread.p_exec, comms_thread_tx.p_exec, comms_thread_rx.p_exec, sp_ctrl_thread.p_exec)
|
||||||
|
|
||||||
if plc_state.init_ok then
|
|
||||||
-- send status one last time after RPS shutdown
|
-- send status one last time after RPS shutdown
|
||||||
smem_sys.plc_comms.send_status(plc_state.no_reactor, plc_state.reactor_formed)
|
smem_sys.plc_comms.send_status(plc_state.no_reactor, plc_state.reactor_formed)
|
||||||
smem_sys.plc_comms.send_rps_status()
|
smem_sys.plc_comms.send_rps_status()
|
||||||
|
|
||||||
-- close connection
|
-- close connection
|
||||||
smem_sys.plc_comms.close()
|
smem_sys.plc_comms.close()
|
||||||
end
|
|
||||||
else
|
else
|
||||||
-- run threads, excluding comms
|
-- run threads, excluding comms
|
||||||
parallel.waitForAll(main_thread.p_exec, rps_thread.p_exec)
|
parallel.waitForAll(main_thread.p_exec, rps_thread.p_exec)
|
||||||
|
|||||||
@ -31,8 +31,7 @@ local MQ__COMM_CMD = {
|
|||||||
-- main thread
|
-- main thread
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param smem plc_shared_memory
|
---@param smem plc_shared_memory
|
||||||
---@param init function
|
function threads.thread__main(smem)
|
||||||
function threads.thread__main(smem, init)
|
|
||||||
-- print a log message to the terminal as long as the UI isn't running
|
-- print a log message to the terminal as long as the UI isn't running
|
||||||
local function println_ts(message) if not smem.plc_state.fp_ok then util.println_ts(message) end end
|
local function println_ts(message) if not smem.plc_state.fp_ok then util.println_ts(message) end end
|
||||||
|
|
||||||
@ -42,7 +41,7 @@ function threads.thread__main(smem, init)
|
|||||||
-- execute thread
|
-- execute thread
|
||||||
function public.exec()
|
function public.exec()
|
||||||
databus.tx_rt_status("main", true)
|
databus.tx_rt_status("main", true)
|
||||||
log.debug("main thread init, clock inactive")
|
log.debug("OS: main thread start")
|
||||||
|
|
||||||
-- send status updates at 2Hz (every 10 server ticks) (every loop tick)
|
-- send status updates at 2Hz (every 10 server ticks) (every loop tick)
|
||||||
-- send link requests at 0.5Hz (every 40 server ticks) (every 8 loop ticks)
|
-- send link requests at 0.5Hz (every 40 server ticks) (every 8 loop ticks)
|
||||||
@ -55,6 +54,9 @@ function threads.thread__main(smem, init)
|
|||||||
local plc_state = smem.plc_state
|
local plc_state = smem.plc_state
|
||||||
local plc_dev = smem.plc_dev
|
local plc_dev = smem.plc_dev
|
||||||
|
|
||||||
|
-- start clock
|
||||||
|
loop_clock.start()
|
||||||
|
|
||||||
-- event loop
|
-- event loop
|
||||||
while true do
|
while true do
|
||||||
-- get plc_sys fields (may have been set late due to degraded boot)
|
-- get plc_sys fields (may have been set late due to degraded boot)
|
||||||
@ -67,7 +69,6 @@ function threads.thread__main(smem, init)
|
|||||||
|
|
||||||
-- handle event
|
-- handle event
|
||||||
if event == "timer" and loop_clock.is_clock(param1) then
|
if event == "timer" and loop_clock.is_clock(param1) then
|
||||||
-- note: loop clock is only running if init_ok = true
|
|
||||||
-- blink heartbeat indicator
|
-- blink heartbeat indicator
|
||||||
databus.heartbeat()
|
databus.heartbeat()
|
||||||
|
|
||||||
@ -93,7 +94,7 @@ function threads.thread__main(smem, init)
|
|||||||
-- reactor now formed
|
-- reactor now formed
|
||||||
plc_state.reactor_formed = true
|
plc_state.reactor_formed = true
|
||||||
|
|
||||||
println_ts("reactor is now formed.")
|
println_ts("reactor is now formed")
|
||||||
log.info("reactor is now formed")
|
log.info("reactor is now formed")
|
||||||
|
|
||||||
-- SCRAM newly formed reactor
|
-- SCRAM newly formed reactor
|
||||||
@ -106,10 +107,10 @@ function threads.thread__main(smem, init)
|
|||||||
|
|
||||||
-- partial reset of RPS, specific to becoming formed
|
-- partial reset of RPS, specific to becoming formed
|
||||||
-- without this, auto control can't resume on chunk load
|
-- without this, auto control can't resume on chunk load
|
||||||
rps.reset_formed()
|
rps.reset_reattach()
|
||||||
elseif plc_state.reactor_formed and not rps.is_formed() then
|
elseif plc_state.reactor_formed and (rps.is_formed() == false) then
|
||||||
-- reactor no longer formed
|
-- reactor no longer formed
|
||||||
println_ts("reactor is no longer formed.")
|
println_ts("reactor is no longer formed")
|
||||||
log.info("reactor is no longer formed")
|
log.info("reactor is no longer formed")
|
||||||
|
|
||||||
plc_state.reactor_formed = false
|
plc_state.reactor_formed = false
|
||||||
@ -118,14 +119,14 @@ function threads.thread__main(smem, init)
|
|||||||
|
|
||||||
-- update indicators
|
-- update indicators
|
||||||
databus.tx_hw_status(plc_state)
|
databus.tx_hw_status(plc_state)
|
||||||
elseif event == "modem_message" and networked and plc_state.init_ok and nic.is_connected() then
|
elseif event == "modem_message" and networked and nic.is_connected() then
|
||||||
-- got a packet
|
-- got a packet
|
||||||
local packet = plc_comms.parse_packet(param1, param2, param3, param4, param5)
|
local packet = plc_comms.parse_packet(param1, param2, param3, param4, param5)
|
||||||
if packet ~= nil then
|
if packet ~= nil then
|
||||||
-- pass the packet onto the comms message queue
|
-- pass the packet onto the comms message queue
|
||||||
smem.q.mq_comms_rx.push_packet(packet)
|
smem.q.mq_comms_rx.push_packet(packet)
|
||||||
end
|
end
|
||||||
elseif event == "timer" and networked and plc_state.init_ok and conn_watchdog.is_timer(param1) then
|
elseif event == "timer" and networked and conn_watchdog.is_timer(param1) then
|
||||||
-- haven't heard from server recently? close connection and shutdown reactor
|
-- haven't heard from server recently? close connection and shutdown reactor
|
||||||
plc_comms.close()
|
plc_comms.close()
|
||||||
smem.q.mq_rps.push_command(MQ__RPS_CMD.TRIP_TIMEOUT)
|
smem.q.mq_rps.push_command(MQ__RPS_CMD.TRIP_TIMEOUT)
|
||||||
@ -146,8 +147,7 @@ function threads.thread__main(smem, init)
|
|||||||
elseif networked and type == "modem" then
|
elseif networked and type == "modem" then
|
||||||
---@cast device Modem
|
---@cast device Modem
|
||||||
-- we only care if this is our comms modem
|
-- we only care if this is our comms modem
|
||||||
-- note, check init_ok first since nic will be nil if it is false
|
if nic.is_modem(device) then
|
||||||
if plc_state.init_ok and nic.is_modem(device) then
|
|
||||||
nic.disconnect()
|
nic.disconnect()
|
||||||
|
|
||||||
println_ts("comms modem disconnected!")
|
println_ts("comms modem disconnected!")
|
||||||
@ -161,11 +161,9 @@ function threads.thread__main(smem, init)
|
|||||||
plc_state.no_modem = true
|
plc_state.no_modem = true
|
||||||
plc_state.degraded = true
|
plc_state.degraded = true
|
||||||
|
|
||||||
if plc_state.init_ok then
|
|
||||||
-- try to scram reactor if it is still connected
|
-- try to scram reactor if it is still connected
|
||||||
smem.q.mq_rps.push_command(MQ__RPS_CMD.DEGRADED_SCRAM)
|
smem.q.mq_rps.push_command(MQ__RPS_CMD.DEGRADED_SCRAM)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
else
|
else
|
||||||
log.warning("non-comms modem disconnected")
|
log.warning("non-comms modem disconnected")
|
||||||
end
|
end
|
||||||
@ -184,7 +182,7 @@ function threads.thread__main(smem, init)
|
|||||||
plc_dev.reactor = device
|
plc_dev.reactor = device
|
||||||
plc_state.no_reactor = false
|
plc_state.no_reactor = false
|
||||||
|
|
||||||
println_ts("reactor reconnected.")
|
println_ts("reactor reconnected")
|
||||||
log.info("reactor reconnected")
|
log.info("reactor reconnected")
|
||||||
|
|
||||||
-- we need to assume formed here as we cannot check in this main loop
|
-- we need to assume formed here as we cannot check in this main loop
|
||||||
@ -196,7 +194,6 @@ function threads.thread__main(smem, init)
|
|||||||
plc_state.degraded = false
|
plc_state.degraded = false
|
||||||
end
|
end
|
||||||
|
|
||||||
if plc_state.init_ok then
|
|
||||||
smem.q.mq_rps.push_command(MQ__RPS_CMD.SCRAM)
|
smem.q.mq_rps.push_command(MQ__RPS_CMD.SCRAM)
|
||||||
|
|
||||||
rps.reconnect_reactor(plc_dev.reactor)
|
rps.reconnect_reactor(plc_dev.reactor)
|
||||||
@ -206,25 +203,24 @@ function threads.thread__main(smem, init)
|
|||||||
|
|
||||||
-- partial reset of RPS, specific to becoming formed/reconnected
|
-- partial reset of RPS, specific to becoming formed/reconnected
|
||||||
-- without this, auto control can't resume on chunk load
|
-- without this, auto control can't resume on chunk load
|
||||||
rps.reset_formed()
|
rps.reset_reattach()
|
||||||
end
|
|
||||||
elseif networked and type == "modem" then
|
elseif networked and type == "modem" then
|
||||||
---@cast device Modem
|
---@cast device Modem
|
||||||
local is_comms_modem = util.trinary(plc_dev.modem_wired, plc_dev.modem_iface == param1, device.isWireless())
|
local is_comms_modem = util.trinary(plc_dev.modem_wired, plc_dev.modem_iface == param1, device.isWireless())
|
||||||
|
|
||||||
-- note, check init_ok first since nic will be nil if it is false
|
-- note, check init_ok first since nic will be nil if it is false
|
||||||
if is_comms_modem and not (plc_state.init_ok and nic.is_connected()) then
|
if is_comms_modem and not nic.is_connected() then
|
||||||
-- reconnected modem
|
-- reconnected modem
|
||||||
plc_dev.modem = device
|
plc_dev.modem = device
|
||||||
plc_state.no_modem = false
|
plc_state.no_modem = false
|
||||||
|
|
||||||
if plc_state.init_ok then nic.connect(device) end
|
nic.connect(device)
|
||||||
|
|
||||||
println_ts("comms modem reconnected.")
|
println_ts("comms modem reconnected")
|
||||||
log.info("comms modem reconnected")
|
log.info("comms modem reconnected")
|
||||||
|
|
||||||
-- determine if we are still in a degraded state
|
-- determine if we are still in a degraded state
|
||||||
if not plc_state.no_reactor then
|
if plc_state.reactor_formed and not plc_state.no_reactor then
|
||||||
plc_state.degraded = false
|
plc_state.degraded = false
|
||||||
end
|
end
|
||||||
elseif device.isWireless() then
|
elseif device.isWireless() then
|
||||||
@ -235,27 +231,17 @@ function threads.thread__main(smem, init)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if not init'd and no longer degraded, proceed to init
|
|
||||||
if not plc_state.init_ok and not plc_state.degraded then
|
|
||||||
plc_state.init_ok = true
|
|
||||||
init()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- update indicators
|
-- update indicators
|
||||||
databus.tx_hw_status(plc_state)
|
databus.tx_hw_status(plc_state)
|
||||||
elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or
|
elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or
|
||||||
event == "double_click" then
|
event == "double_click" then
|
||||||
-- handle a mouse event
|
-- handle a mouse event
|
||||||
renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3))
|
renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3))
|
||||||
elseif event == "clock_start" then
|
|
||||||
-- start loop clock
|
|
||||||
loop_clock.start()
|
|
||||||
log.debug("main thread clock started")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check for termination request
|
-- check for termination request
|
||||||
if event == "terminate" or ppm.should_terminate() then
|
if event == "terminate" or ppm.should_terminate() then
|
||||||
log.info("terminate requested, main thread exiting")
|
log.info("OS: terminate requested, main thread exiting")
|
||||||
-- rps handles reactor shutdown
|
-- rps handles reactor shutdown
|
||||||
plc_state.shutdown = true
|
plc_state.shutdown = true
|
||||||
break
|
break
|
||||||
@ -279,8 +265,7 @@ function threads.thread__main(smem, init)
|
|||||||
-- if not, we need to restart the clock
|
-- if not, we need to restart the clock
|
||||||
-- this thread cannot be slept because it will miss events (namely "terminate" otherwise)
|
-- this thread cannot be slept because it will miss events (namely "terminate" otherwise)
|
||||||
if not plc_state.shutdown then
|
if not plc_state.shutdown then
|
||||||
log.info("main thread restarting now...")
|
log.info("OS: main thread restarting now...")
|
||||||
util.push_event("clock_start")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -301,7 +286,7 @@ function threads.thread__rps(smem)
|
|||||||
-- execute thread
|
-- execute thread
|
||||||
function public.exec()
|
function public.exec()
|
||||||
databus.tx_rt_status("rps", true)
|
databus.tx_rt_status("rps", true)
|
||||||
log.debug("rps thread start")
|
log.debug("OS: rps thread start")
|
||||||
|
|
||||||
-- load in from shared memory
|
-- load in from shared memory
|
||||||
local networked = smem.networked
|
local networked = smem.networked
|
||||||
@ -318,32 +303,25 @@ function threads.thread__rps(smem)
|
|||||||
-- get plc_sys fields (may have been set late due to degraded boot)
|
-- get plc_sys fields (may have been set late due to degraded boot)
|
||||||
local rps = smem.plc_sys.rps
|
local rps = smem.plc_sys.rps
|
||||||
local plc_comms = smem.plc_sys.plc_comms
|
local plc_comms = smem.plc_sys.plc_comms
|
||||||
-- get reactor, may have changed do to disconnect/reconnect
|
-- get reactor, it may have changed due to a disconnect/reconnect
|
||||||
local reactor = plc_dev.reactor
|
local reactor = plc_dev.reactor
|
||||||
|
|
||||||
-- RPS checks
|
|
||||||
if plc_state.init_ok then
|
|
||||||
-- SCRAM if no open connection
|
-- SCRAM if no open connection
|
||||||
if networked and not plc_comms.is_linked() then
|
if networked and not plc_comms.is_linked() then
|
||||||
if was_linked then
|
if was_linked then
|
||||||
was_linked = false
|
was_linked = false
|
||||||
rps.trip_timeout()
|
rps.trip_timeout()
|
||||||
end
|
end
|
||||||
else
|
else was_linked = true end
|
||||||
was_linked = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if (not plc_state.no_reactor) and rps.is_formed() then
|
|
||||||
-- check reactor status
|
-- check reactor status
|
||||||
---@diagnostic disable-next-line: need-check-nil
|
if (not plc_state.no_reactor) and rps.is_formed() then
|
||||||
local reactor_status = reactor.getStatus()
|
local reactor_status = reactor.getStatus()
|
||||||
databus.tx_reactor_state(reactor_status)
|
databus.tx_reactor_state(reactor_status)
|
||||||
|
|
||||||
-- if we tried to SCRAM but failed, keep trying
|
-- if we tried to SCRAM but failed, keep trying
|
||||||
-- in that case, SCRAM won't be called until it reconnects (this is the expected use of this check)
|
-- in that case, SCRAM won't be called until it reconnects (this is the expected use of this check)
|
||||||
if rps.is_tripped() and reactor_status then
|
if rps.is_tripped() and reactor_status then rps.scram() end
|
||||||
rps.scram()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if we are in standalone mode and the front panel isn't working, continuously reset RPS
|
-- if we are in standalone mode and the front panel isn't working, continuously reset RPS
|
||||||
@ -351,16 +329,10 @@ function threads.thread__rps(smem)
|
|||||||
if not (networked or smem.plc_state.fp_ok) then rps.reset(true) end
|
if not (networked or smem.plc_state.fp_ok) then rps.reset(true) end
|
||||||
|
|
||||||
-- check safety (SCRAM occurs if tripped)
|
-- check safety (SCRAM occurs if tripped)
|
||||||
if not plc_state.no_reactor then
|
local rps_tripped, rps_status_string, rps_first = rps.check(not plc_state.no_reactor)
|
||||||
local rps_tripped, rps_status_string, rps_first = rps.check()
|
|
||||||
|
|
||||||
if rps_tripped and rps_first then
|
if rps_tripped and rps_first then
|
||||||
println_ts("[RPS] SCRAM! safety trip: " .. rps_status_string)
|
println_ts("RPS: SCRAM on safety trip (" .. rps_status_string .. ")")
|
||||||
if networked and not plc_state.no_modem then
|
if networked then plc_comms.send_rps_alarm(rps_status_string) end
|
||||||
plc_comms.send_rps_alarm(rps_status_string)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check for messages in the message queue
|
-- check for messages in the message queue
|
||||||
@ -370,19 +342,19 @@ function threads.thread__rps(smem)
|
|||||||
if msg ~= nil then
|
if msg ~= nil then
|
||||||
if msg.qtype == mqueue.TYPE.COMMAND then
|
if msg.qtype == mqueue.TYPE.COMMAND then
|
||||||
-- received a command
|
-- received a command
|
||||||
if plc_state.init_ok then
|
|
||||||
if msg.message == MQ__RPS_CMD.SCRAM then
|
if msg.message == MQ__RPS_CMD.SCRAM then
|
||||||
-- SCRAM
|
-- SCRAM
|
||||||
|
log.info("RPS: OS requested SCRAM")
|
||||||
rps.scram()
|
rps.scram()
|
||||||
elseif msg.message == MQ__RPS_CMD.DEGRADED_SCRAM then
|
elseif msg.message == MQ__RPS_CMD.DEGRADED_SCRAM then
|
||||||
-- lost peripheral(s)
|
-- lost peripheral(s)
|
||||||
|
log.info("RPS: received PLC degraded alert")
|
||||||
rps.trip_fault()
|
rps.trip_fault()
|
||||||
elseif msg.message == MQ__RPS_CMD.TRIP_TIMEOUT then
|
elseif msg.message == MQ__RPS_CMD.TRIP_TIMEOUT then
|
||||||
-- watchdog tripped
|
-- watchdog tripped
|
||||||
|
println_ts("RPS: supervisor timeout")
|
||||||
|
log.warning("RPS: received supervisor timeout alert")
|
||||||
rps.trip_timeout()
|
rps.trip_timeout()
|
||||||
println_ts("server timeout")
|
|
||||||
log.warning("server timeout")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
elseif msg.qtype == mqueue.TYPE.DATA then
|
elseif msg.qtype == mqueue.TYPE.DATA then
|
||||||
-- received data
|
-- received data
|
||||||
@ -398,17 +370,17 @@ function threads.thread__rps(smem)
|
|||||||
-- check for termination request
|
-- check for termination request
|
||||||
if plc_state.shutdown then
|
if plc_state.shutdown then
|
||||||
-- safe exit
|
-- safe exit
|
||||||
log.info("rps thread shutdown initiated")
|
log.info("OS: rps thread shutdown initiated")
|
||||||
if plc_state.init_ok then
|
|
||||||
if rps.scram() then
|
if rps.scram() then
|
||||||
println_ts("reactor disabled")
|
println_ts("exiting, reactor disabled")
|
||||||
log.info("rps thread reactor SCRAM OK")
|
log.info("OS: rps thread reactor SCRAM OK on exit")
|
||||||
else
|
else
|
||||||
println_ts("exiting, reactor failed to disable")
|
println_ts("exiting, reactor failed to disable")
|
||||||
log.error("rps thread failed to SCRAM reactor on exit")
|
log.error("OS: rps thread failed to SCRAM reactor on exit")
|
||||||
end
|
end
|
||||||
end
|
|
||||||
log.info("rps thread exiting")
|
log.info("OS: rps thread exiting")
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -430,8 +402,8 @@ function threads.thread__rps(smem)
|
|||||||
databus.tx_rt_status("rps", false)
|
databus.tx_rt_status("rps", false)
|
||||||
|
|
||||||
if not plc_state.shutdown then
|
if not plc_state.shutdown then
|
||||||
if plc_state.init_ok then smem.plc_sys.rps.scram() end
|
smem.plc_sys.rps.scram()
|
||||||
log.info("rps thread restarting in 5 seconds...")
|
log.info("OS: rps thread restarting in 5 seconds...")
|
||||||
util.psleep(5)
|
util.psleep(5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -450,7 +422,7 @@ function threads.thread__comms_tx(smem)
|
|||||||
-- execute thread
|
-- execute thread
|
||||||
function public.exec()
|
function public.exec()
|
||||||
databus.tx_rt_status("comms_tx", true)
|
databus.tx_rt_status("comms_tx", true)
|
||||||
log.debug("comms tx thread start")
|
log.debug("OS: comms tx thread start")
|
||||||
|
|
||||||
-- load in from shared memory
|
-- load in from shared memory
|
||||||
local plc_state = smem.plc_state
|
local plc_state = smem.plc_state
|
||||||
@ -467,7 +439,7 @@ function threads.thread__comms_tx(smem)
|
|||||||
while comms_queue.ready() and not plc_state.shutdown do
|
while comms_queue.ready() and not plc_state.shutdown do
|
||||||
local msg = comms_queue.pop()
|
local msg = comms_queue.pop()
|
||||||
|
|
||||||
if msg ~= nil and plc_state.init_ok then
|
if msg ~= nil then
|
||||||
if msg.qtype == mqueue.TYPE.COMMAND then
|
if msg.qtype == mqueue.TYPE.COMMAND then
|
||||||
-- received a command
|
-- received a command
|
||||||
if msg.message == MQ__COMM_CMD.SEND_STATUS then
|
if msg.message == MQ__COMM_CMD.SEND_STATUS then
|
||||||
@ -488,7 +460,7 @@ function threads.thread__comms_tx(smem)
|
|||||||
|
|
||||||
-- check for termination request
|
-- check for termination request
|
||||||
if plc_state.shutdown then
|
if plc_state.shutdown then
|
||||||
log.info("comms tx thread exiting")
|
log.info("OS: comms tx thread exiting")
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -510,7 +482,7 @@ function threads.thread__comms_tx(smem)
|
|||||||
databus.tx_rt_status("comms_tx", false)
|
databus.tx_rt_status("comms_tx", false)
|
||||||
|
|
||||||
if not plc_state.shutdown then
|
if not plc_state.shutdown then
|
||||||
log.info("comms tx thread restarting in 5 seconds...")
|
log.info("OS: comms tx thread restarting in 5 seconds...")
|
||||||
util.psleep(5)
|
util.psleep(5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -523,13 +495,16 @@ end
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param smem plc_shared_memory
|
---@param smem plc_shared_memory
|
||||||
function threads.thread__comms_rx(smem)
|
function threads.thread__comms_rx(smem)
|
||||||
|
-- print a log message to the terminal as long as the UI isn't running
|
||||||
|
local function println_ts(message) if not smem.plc_state.fp_ok then util.println_ts(message) end end
|
||||||
|
|
||||||
---@class parallel_thread
|
---@class parallel_thread
|
||||||
local public = {}
|
local public = {}
|
||||||
|
|
||||||
-- execute thread
|
-- execute thread
|
||||||
function public.exec()
|
function public.exec()
|
||||||
databus.tx_rt_status("comms_rx", true)
|
databus.tx_rt_status("comms_rx", true)
|
||||||
log.debug("comms rx thread start")
|
log.debug("OS: comms rx thread start")
|
||||||
|
|
||||||
-- load in from shared memory
|
-- load in from shared memory
|
||||||
local plc_state = smem.plc_state
|
local plc_state = smem.plc_state
|
||||||
@ -548,7 +523,7 @@ function threads.thread__comms_rx(smem)
|
|||||||
while comms_queue.ready() and not plc_state.shutdown do
|
while comms_queue.ready() and not plc_state.shutdown do
|
||||||
local msg = comms_queue.pop()
|
local msg = comms_queue.pop()
|
||||||
|
|
||||||
if msg ~= nil and plc_state.init_ok then
|
if msg ~= nil then
|
||||||
if msg.qtype == mqueue.TYPE.COMMAND then
|
if msg.qtype == mqueue.TYPE.COMMAND then
|
||||||
-- received a command
|
-- received a command
|
||||||
elseif msg.qtype == mqueue.TYPE.DATA then
|
elseif msg.qtype == mqueue.TYPE.DATA then
|
||||||
@ -557,7 +532,7 @@ function threads.thread__comms_rx(smem)
|
|||||||
-- received a packet
|
-- received a packet
|
||||||
-- handle the packet (setpoints passed to update burn rate setpoint)
|
-- handle the packet (setpoints passed to update burn rate setpoint)
|
||||||
-- (plc_state passed to check if degraded)
|
-- (plc_state passed to check if degraded)
|
||||||
plc_comms.handle_packet(msg.message, plc_state, setpoints)
|
plc_comms.handle_packet(msg.message, plc_state, setpoints, println_ts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -567,7 +542,7 @@ function threads.thread__comms_rx(smem)
|
|||||||
|
|
||||||
-- check for termination request
|
-- check for termination request
|
||||||
if plc_state.shutdown then
|
if plc_state.shutdown then
|
||||||
log.info("comms rx thread exiting")
|
log.info("OS: comms rx thread exiting")
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -589,7 +564,7 @@ function threads.thread__comms_rx(smem)
|
|||||||
databus.tx_rt_status("comms_rx", false)
|
databus.tx_rt_status("comms_rx", false)
|
||||||
|
|
||||||
if not plc_state.shutdown then
|
if not plc_state.shutdown then
|
||||||
log.info("comms rx thread restarting in 5 seconds...")
|
log.info("OS: comms rx thread restarting in 5 seconds...")
|
||||||
util.psleep(5)
|
util.psleep(5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -608,7 +583,7 @@ function threads.thread__setpoint_control(smem)
|
|||||||
-- execute thread
|
-- execute thread
|
||||||
function public.exec()
|
function public.exec()
|
||||||
databus.tx_rt_status("spctl", true)
|
databus.tx_rt_status("spctl", true)
|
||||||
log.debug("setpoint control thread start")
|
log.debug("OS: setpoint control thread start")
|
||||||
|
|
||||||
-- load in from shared memory
|
-- load in from shared memory
|
||||||
local plc_state = smem.plc_state
|
local plc_state = smem.plc_state
|
||||||
@ -631,9 +606,7 @@ function threads.thread__setpoint_control(smem)
|
|||||||
-- get reactor, may have changed do to disconnect/reconnect
|
-- get reactor, may have changed do to disconnect/reconnect
|
||||||
local reactor = plc_dev.reactor
|
local reactor = plc_dev.reactor
|
||||||
|
|
||||||
if plc_state.init_ok and (not plc_state.no_reactor) then
|
if not plc_state.no_reactor then
|
||||||
---@cast reactor table won't be nil
|
|
||||||
|
|
||||||
-- check if we should start ramping
|
-- check if we should start ramping
|
||||||
if setpoints.burn_rate_en and (setpoints.burn_rate ~= last_burn_sp) then
|
if setpoints.burn_rate_en and (setpoints.burn_rate ~= last_burn_sp) then
|
||||||
local cur_burn_rate = reactor.getBurnRate()
|
local cur_burn_rate = reactor.getBurnRate()
|
||||||
@ -700,7 +673,7 @@ function threads.thread__setpoint_control(smem)
|
|||||||
|
|
||||||
-- check for termination request
|
-- check for termination request
|
||||||
if plc_state.shutdown then
|
if plc_state.shutdown then
|
||||||
log.info("setpoint control thread exiting")
|
log.info("OS: setpoint control thread exiting")
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -722,7 +695,7 @@ function threads.thread__setpoint_control(smem)
|
|||||||
databus.tx_rt_status("spctl", false)
|
databus.tx_rt_status("spctl", false)
|
||||||
|
|
||||||
if not plc_state.shutdown then
|
if not plc_state.shutdown then
|
||||||
log.info("setpoint control thread restarting in 5 seconds...")
|
log.info("OS: setpoint control thread restarting in 5 seconds...")
|
||||||
util.psleep(5)
|
util.psleep(5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -214,7 +214,7 @@ function comms.scada_packet()
|
|||||||
|
|
||||||
if (type(max_distance) == "number") and (type(distance) == "number") and (distance > max_distance) then
|
if (type(max_distance) == "number") and (type(distance) == "number") and (distance > max_distance) then
|
||||||
-- outside of maximum allowable transmission distance
|
-- outside of maximum allowable transmission distance
|
||||||
-- log.debug("comms.scada_packet.receive(): discarding packet with distance " .. distance .. " (outside trusted range)")
|
-- log.debug("COMMS: comms.scada_packet.receive(): discarding packet with distance " .. distance .. " (outside trusted range)")
|
||||||
else
|
else
|
||||||
if type(self.raw) == "table" then
|
if type(self.raw) == "table" then
|
||||||
if #self.raw == 5 then
|
if #self.raw == 5 then
|
||||||
@ -337,7 +337,7 @@ function comms.authd_packet()
|
|||||||
|
|
||||||
if (type(max_distance) == "number") and ((type(distance) ~= "number") or (distance > max_distance)) then
|
if (type(max_distance) == "number") and ((type(distance) ~= "number") or (distance > max_distance)) then
|
||||||
-- outside of maximum allowable transmission distance
|
-- outside of maximum allowable transmission distance
|
||||||
-- log.debug("comms.authd_packet.receive(): discarding packet with distance " .. distance .. " (outside trusted range)")
|
-- log.debug("COMMS: comms.authd_packet.receive(): discarding packet with distance " .. distance .. " (outside trusted range)")
|
||||||
else
|
else
|
||||||
if type(self.raw) == "table" then
|
if type(self.raw) == "table" then
|
||||||
if #self.raw == 4 then
|
if #self.raw == 4 then
|
||||||
@ -423,7 +423,7 @@ function comms.modbus_packet()
|
|||||||
self.raw = { self.txn_id, self.unit_id, self.func_code }
|
self.raw = { self.txn_id, self.unit_id, self.func_code }
|
||||||
for i = 1, self.length do insert(self.raw, data[i]) end
|
for i = 1, self.length do insert(self.raw, data[i]) end
|
||||||
else
|
else
|
||||||
log.error("comms.modbus_packet.make(): data not a table")
|
log.error("COMMS: modbus_packet.make(): data not a table")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -446,11 +446,11 @@ function comms.modbus_packet()
|
|||||||
|
|
||||||
return size_ok and valid
|
return size_ok and valid
|
||||||
else
|
else
|
||||||
log.debug("attempted MODBUS_TCP parse of incorrect protocol " .. frame.protocol(), true)
|
log.debug("COMMS: attempted MODBUS_TCP parse of incorrect protocol " .. frame.protocol(), true)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
log.debug("nil frame encountered", true)
|
log.debug("COMMS: nil frame encountered", true)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -509,7 +509,7 @@ function comms.rplc_packet()
|
|||||||
self.raw = { self.id, self.type }
|
self.raw = { self.id, self.type }
|
||||||
for i = 1, #data do insert(self.raw, data[i]) end
|
for i = 1, #data do insert(self.raw, data[i]) end
|
||||||
else
|
else
|
||||||
log.error("comms.rplc_packet.make(): data not a table")
|
log.error("COMMS: rplc_packet.make(): data not a table")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -532,11 +532,11 @@ function comms.rplc_packet()
|
|||||||
|
|
||||||
return ok
|
return ok
|
||||||
else
|
else
|
||||||
log.debug("attempted RPLC parse of incorrect protocol " .. frame.protocol(), true)
|
log.debug("COMMS: attempted RPLC parse of incorrect protocol " .. frame.protocol(), true)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
log.debug("nil frame encountered", true)
|
log.debug("COMMS: nil frame encountered", true)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -591,7 +591,7 @@ function comms.mgmt_packet()
|
|||||||
self.raw = { self.type }
|
self.raw = { self.type }
|
||||||
for i = 1, #data do insert(self.raw, data[i]) end
|
for i = 1, #data do insert(self.raw, data[i]) end
|
||||||
else
|
else
|
||||||
log.error("comms.mgmt_packet.make(): data not a table")
|
log.error("COMMS: mgmt_packet.make(): data not a table")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -612,11 +612,11 @@ function comms.mgmt_packet()
|
|||||||
|
|
||||||
return ok
|
return ok
|
||||||
else
|
else
|
||||||
log.debug("attempted SCADA_MGMT parse of incorrect protocol " .. frame.protocol(), true)
|
log.debug("COMMS: attempted SCADA_MGMT parse of incorrect protocol " .. frame.protocol(), true)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
log.debug("nil frame encountered", true)
|
log.debug("COMMS: nil frame encountered", true)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -670,7 +670,7 @@ function comms.crdn_packet()
|
|||||||
self.raw = { self.type }
|
self.raw = { self.type }
|
||||||
for i = 1, #data do insert(self.raw, data[i]) end
|
for i = 1, #data do insert(self.raw, data[i]) end
|
||||||
else
|
else
|
||||||
log.error("comms.crdn_packet.make(): data not a table")
|
log.error("COMMS: crdn_packet.make(): data not a table")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -691,11 +691,11 @@ function comms.crdn_packet()
|
|||||||
|
|
||||||
return ok
|
return ok
|
||||||
else
|
else
|
||||||
log.debug("attempted SCADA_CRDN parse of incorrect protocol " .. frame.protocol(), true)
|
log.debug("COMMS: attempted SCADA_CRDN parse of incorrect protocol " .. frame.protocol(), true)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
log.debug("nil frame encountered", true)
|
log.debug("COMMS: nil frame encountered", true)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -20,7 +20,7 @@ local MODE = { APPEND = 0, NEW = 1 }
|
|||||||
|
|
||||||
log.MODE = MODE
|
log.MODE = MODE
|
||||||
|
|
||||||
local logger = {
|
local _log = {
|
||||||
not_ready = true,
|
not_ready = true,
|
||||||
path = "/log.txt",
|
path = "/log.txt",
|
||||||
mode = MODE.APPEND,
|
mode = MODE.APPEND,
|
||||||
@ -42,36 +42,36 @@ local free_space = fs.getFreeSpace
|
|||||||
---@param err_msg string|nil error message
|
---@param err_msg string|nil error message
|
||||||
---@return boolean out_of_space
|
---@return boolean out_of_space
|
||||||
local function check_out_of_space(err_msg)
|
local function check_out_of_space(err_msg)
|
||||||
return (free_space(logger.path) < MIN_SPACE) or ((err_msg ~= nil) and (string.find(err_msg, OUT_OF_SPACE) ~= nil))
|
return (free_space(_log.path) < MIN_SPACE) or ((err_msg ~= nil) and (string.find(err_msg, OUT_OF_SPACE) ~= nil))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- private log write function
|
-- private log write function
|
||||||
---@param msg_bits any[]
|
---@param msg_bits any[]
|
||||||
local function _log(msg_bits)
|
local function write_log(msg_bits)
|
||||||
if logger.not_ready then return end
|
if _log.not_ready then return end
|
||||||
|
|
||||||
local time_stamp = os.date(TIME_FMT)
|
local time_stamp = os.date(TIME_FMT)
|
||||||
local stamped = util.c(time_stamp, table.unpack(msg_bits))
|
local stamped = util.c(time_stamp, table.unpack(msg_bits))
|
||||||
|
|
||||||
-- attempt to write log
|
-- attempt to write log
|
||||||
local status, result = pcall(function ()
|
local status, result = pcall(function ()
|
||||||
logger.file.writeLine(stamped)
|
_log.file.writeLine(stamped)
|
||||||
logger.file.flush()
|
_log.file.flush()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- if we don't have space, we need to create a new log file
|
-- if we don't have space, we need to create a new log file
|
||||||
if check_out_of_space() then
|
if check_out_of_space() then
|
||||||
-- delete the old log file before opening a new one
|
-- delete the old log file before opening a new one
|
||||||
logger.file.close()
|
_log.file.close()
|
||||||
fs.delete(logger.path)
|
fs.delete(_log.path)
|
||||||
|
|
||||||
-- re-init logger and pass dmesg_out so that it doesn't change
|
-- re-init logger and pass dmesg_out so that it doesn't change
|
||||||
log.init(logger.path, logger.mode, logger.debug, logger.dmesg_out)
|
log.init(_log.path, _log.mode, _log.debug, _log.dmesg_out)
|
||||||
|
|
||||||
-- log the message and recycle warning
|
-- log the message and recycle warning
|
||||||
logger.file.writeLine(time_stamp .. WRN_TAG .. "recycled log file")
|
_log.file.writeLine(time_stamp .. WRN_TAG .. "recycled log file")
|
||||||
logger.file.writeLine(stamped)
|
_log.file.writeLine(stamped)
|
||||||
logger.file.flush()
|
_log.file.flush()
|
||||||
elseif (not status) and (result ~= nil) then
|
elseif (not status) and (result ~= nil) then
|
||||||
util.println("unexpected error writing to the log file: " .. result)
|
util.println("unexpected error writing to the log file: " .. result)
|
||||||
end
|
end
|
||||||
@ -89,45 +89,45 @@ end
|
|||||||
function log.init(path, write_mode, include_debug, dmesg_redirect)
|
function log.init(path, write_mode, include_debug, dmesg_redirect)
|
||||||
local err_msg
|
local err_msg
|
||||||
|
|
||||||
logger.path = path
|
_log.path = path
|
||||||
logger.mode = write_mode
|
_log.mode = write_mode
|
||||||
logger.debug = include_debug
|
_log.debug = include_debug
|
||||||
logger.file, err_msg = fs.open(path, util.trinary(logger.mode == MODE.APPEND, "a", "w"))
|
_log.file, err_msg = fs.open(path, util.trinary(_log.mode == MODE.APPEND, "a", "w"))
|
||||||
|
|
||||||
if dmesg_redirect then
|
if dmesg_redirect then
|
||||||
logger.dmesg_out = dmesg_redirect
|
_log.dmesg_out = dmesg_redirect
|
||||||
else
|
else
|
||||||
logger.dmesg_out = term.current()
|
_log.dmesg_out = term.current()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check for space issues
|
-- check for space issues
|
||||||
local out_of_space = check_out_of_space(err_msg)
|
local out_of_space = check_out_of_space(err_msg)
|
||||||
|
|
||||||
-- try to handle problems
|
-- try to handle problems
|
||||||
if logger.file == nil or out_of_space then
|
if _log.file == nil or out_of_space then
|
||||||
if out_of_space then
|
if out_of_space then
|
||||||
if fs.exists(logger.path) then
|
if fs.exists(_log.path) then
|
||||||
fs.delete(logger.path)
|
fs.delete(_log.path)
|
||||||
|
|
||||||
logger.file, err_msg = fs.open(path, util.trinary(logger.mode == MODE.APPEND, "a", "w"))
|
_log.file, err_msg = fs.open(path, util.trinary(_log.mode == MODE.APPEND, "a", "w"))
|
||||||
|
|
||||||
if logger.file then
|
if _log.file then
|
||||||
logger.file.writeLine(os.date(TIME_FMT) .. WRN_TAG .. "init recycled log file")
|
_log.file.writeLine(os.date(TIME_FMT) .. WRN_TAG .. "init recycled log file")
|
||||||
logger.file.flush()
|
_log.file.flush()
|
||||||
else error("failed to setup the log file: " .. err_msg) end
|
else error("failed to setup the log file: " .. err_msg) end
|
||||||
else error("failed to make space for the log file, please delete unused files") end
|
else error("failed to make space for the log file, please delete unused files") end
|
||||||
else error("unexpected error setting up the log file: " .. err_msg) end
|
else error("unexpected error setting up the log file: " .. err_msg) end
|
||||||
end
|
end
|
||||||
|
|
||||||
logger.not_ready = false
|
_log.not_ready = false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- close the log file handle
|
-- close the log file handle
|
||||||
function log.close() logger.file.close() end
|
function log.close() _log.file.close() end
|
||||||
|
|
||||||
-- direct dmesg output to a monitor/window
|
-- direct dmesg output to a monitor/window
|
||||||
---@param window Window window or terminal reference
|
---@param window Window window or terminal reference
|
||||||
function log.direct_dmesg(window) logger.dmesg_out = window end
|
function log.direct_dmesg(window) _log.dmesg_out = window end
|
||||||
|
|
||||||
-- dmesg style logging for boot because I like linux-y things
|
-- dmesg style logging for boot because I like linux-y things
|
||||||
---@param msg any message
|
---@param msg any message
|
||||||
@ -142,7 +142,7 @@ function log.dmesg(msg, tag, tag_color)
|
|||||||
tag = util.strval(tag or "")
|
tag = util.strval(tag or "")
|
||||||
|
|
||||||
local t_stamp = string.format("%12.2f", os.clock())
|
local t_stamp = string.format("%12.2f", os.clock())
|
||||||
local out = logger.dmesg_out
|
local out = _log.dmesg_out
|
||||||
|
|
||||||
if out ~= nil then
|
if out ~= nil then
|
||||||
local out_w, out_h = out.getSize()
|
local out_w, out_h = out.getSize()
|
||||||
@ -180,7 +180,7 @@ function log.dmesg(msg, tag, tag_color)
|
|||||||
if cur_y == out_h then
|
if cur_y == out_h then
|
||||||
out.scroll(1)
|
out.scroll(1)
|
||||||
out.setCursorPos(1, cur_y)
|
out.setCursorPos(1, cur_y)
|
||||||
logger.dmesg_scroll_count = logger.dmesg_scroll_count + 1
|
_log.dmesg_scroll_count = _log.dmesg_scroll_count + 1
|
||||||
else
|
else
|
||||||
out.setCursorPos(1, cur_y + 1)
|
out.setCursorPos(1, cur_y + 1)
|
||||||
end
|
end
|
||||||
@ -216,7 +216,7 @@ function log.dmesg(msg, tag, tag_color)
|
|||||||
if cur_y == out_h then
|
if cur_y == out_h then
|
||||||
out.scroll(1)
|
out.scroll(1)
|
||||||
out.setCursorPos(1, cur_y)
|
out.setCursorPos(1, cur_y)
|
||||||
logger.dmesg_scroll_count = logger.dmesg_scroll_count + 1
|
_log.dmesg_scroll_count = _log.dmesg_scroll_count + 1
|
||||||
else
|
else
|
||||||
out.setCursorPos(1, cur_y + 1)
|
out.setCursorPos(1, cur_y + 1)
|
||||||
end
|
end
|
||||||
@ -225,9 +225,9 @@ function log.dmesg(msg, tag, tag_color)
|
|||||||
out.write(lines[i])
|
out.write(lines[i])
|
||||||
end
|
end
|
||||||
|
|
||||||
logger.dmesg_restore_coord = { out.getCursorPos() }
|
_log.dmesg_restore_coord = { out.getCursorPos() }
|
||||||
|
|
||||||
_log{"[", t_stamp, "] [", tag, "] ", msg}
|
write_log{"[", t_stamp, "] [", tag, "] ", msg}
|
||||||
end
|
end
|
||||||
|
|
||||||
return ts_coord
|
return ts_coord
|
||||||
@ -241,9 +241,9 @@ end
|
|||||||
---@return function update, function done
|
---@return function update, function done
|
||||||
function log.dmesg_working(msg, tag, tag_color)
|
function log.dmesg_working(msg, tag, tag_color)
|
||||||
local ts_coord = log.dmesg(msg, tag, tag_color)
|
local ts_coord = log.dmesg(msg, tag, tag_color)
|
||||||
local initial_scroll = logger.dmesg_scroll_count
|
local initial_scroll = _log.dmesg_scroll_count
|
||||||
|
|
||||||
local out = logger.dmesg_out
|
local out = _log.dmesg_out
|
||||||
local width = (ts_coord.x2 - ts_coord.x1) + 1
|
local width = (ts_coord.x2 - ts_coord.x1) + 1
|
||||||
|
|
||||||
if out ~= nil then
|
if out ~= nil then
|
||||||
@ -252,7 +252,7 @@ function log.dmesg_working(msg, tag, tag_color)
|
|||||||
local counter = 0
|
local counter = 0
|
||||||
|
|
||||||
local function update(sec_remaining)
|
local function update(sec_remaining)
|
||||||
local new_y = ts_coord.y - (logger.dmesg_scroll_count - initial_scroll)
|
local new_y = ts_coord.y - (_log.dmesg_scroll_count - initial_scroll)
|
||||||
if new_y < 1 then return end
|
if new_y < 1 then return end
|
||||||
|
|
||||||
local time = util.sprintf("%ds", sec_remaining)
|
local time = util.sprintf("%ds", sec_remaining)
|
||||||
@ -280,11 +280,11 @@ function log.dmesg_working(msg, tag, tag_color)
|
|||||||
|
|
||||||
counter = counter + 1
|
counter = counter + 1
|
||||||
|
|
||||||
out.setCursorPos(table.unpack(logger.dmesg_restore_coord))
|
out.setCursorPos(table.unpack(_log.dmesg_restore_coord))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function done(ok)
|
local function done(ok)
|
||||||
local new_y = ts_coord.y - (logger.dmesg_scroll_count - initial_scroll)
|
local new_y = ts_coord.y - (_log.dmesg_scroll_count - initial_scroll)
|
||||||
if new_y < 1 then return end
|
if new_y < 1 then return end
|
||||||
|
|
||||||
out.setCursorPos(ts_coord.x1, new_y)
|
out.setCursorPos(ts_coord.x1, new_y)
|
||||||
@ -299,7 +299,7 @@ function log.dmesg_working(msg, tag, tag_color)
|
|||||||
|
|
||||||
out.setTextColor(initial_color)
|
out.setTextColor(initial_color)
|
||||||
|
|
||||||
out.setCursorPos(table.unpack(logger.dmesg_restore_coord))
|
out.setCursorPos(table.unpack(_log.dmesg_restore_coord))
|
||||||
end
|
end
|
||||||
|
|
||||||
return update, done
|
return update, done
|
||||||
@ -312,28 +312,28 @@ end
|
|||||||
---@param msg any message
|
---@param msg any message
|
||||||
---@param trace? boolean include file trace
|
---@param trace? boolean include file trace
|
||||||
function log.debug(msg, trace)
|
function log.debug(msg, trace)
|
||||||
if logger.debug then
|
if _log.debug then
|
||||||
if trace then
|
if trace then
|
||||||
local info = debug.getinfo(2)
|
local info = debug.getinfo(2)
|
||||||
|
|
||||||
if info.name ~= nil then
|
if info.name ~= nil then
|
||||||
_log{DBG_TAG, info.short_src, COLON, info.name, FUNC, info.currentline, ARROW, msg}
|
write_log{DBG_TAG, info.short_src, COLON, info.name, FUNC, info.currentline, ARROW, msg}
|
||||||
else
|
else
|
||||||
_log{DBG_TAG, info.short_src, COLON, info.currentline, ARROW, msg}
|
write_log{DBG_TAG, info.short_src, COLON, info.currentline, ARROW, msg}
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
_log{DBG_TAG, msg}
|
write_log{DBG_TAG, msg}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- log info messages
|
-- log info messages
|
||||||
---@param msg any message
|
---@param msg any message
|
||||||
function log.info(msg) _log{INF_TAG, msg} end
|
function log.info(msg) write_log{INF_TAG, msg} end
|
||||||
|
|
||||||
-- log warning messages
|
-- log warning messages
|
||||||
---@param msg any message
|
---@param msg any message
|
||||||
function log.warning(msg) _log{WRN_TAG, msg} end
|
function log.warning(msg) write_log{WRN_TAG, msg} end
|
||||||
|
|
||||||
-- log error messages
|
-- log error messages
|
||||||
---@param msg any message
|
---@param msg any message
|
||||||
@ -343,17 +343,17 @@ function log.error(msg, trace)
|
|||||||
local info = debug.getinfo(2)
|
local info = debug.getinfo(2)
|
||||||
|
|
||||||
if info.name ~= nil then
|
if info.name ~= nil then
|
||||||
_log{ERR_TAG, info.short_src, COLON, info.name, FUNC, info.currentline, ARROW, msg}
|
write_log{ERR_TAG, info.short_src, COLON, info.name, FUNC, info.currentline, ARROW, msg}
|
||||||
else
|
else
|
||||||
_log{ERR_TAG, info.short_src, COLON, info.currentline, ARROW, msg}
|
write_log{ERR_TAG, info.short_src, COLON, info.currentline, ARROW, msg}
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
_log{ERR_TAG, msg}
|
write_log{ERR_TAG, msg}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- log fatal errors
|
-- log fatal errors
|
||||||
---@param msg any message
|
---@param msg any message
|
||||||
function log.fatal(msg) _log{FTL_TAG, msg} end
|
function log.fatal(msg) write_log{FTL_TAG, msg} end
|
||||||
|
|
||||||
return log
|
return log
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
--
|
--
|
||||||
-- Network Communications
|
-- Network Communications and Message Authentication
|
||||||
--
|
--
|
||||||
|
|
||||||
local comms = require("scada-common.comms")
|
local comms = require("scada-common.comms")
|
||||||
@ -18,7 +18,7 @@ local array = require("lockbox.util.array")
|
|||||||
local network = {}
|
local network = {}
|
||||||
|
|
||||||
-- cryptography engine
|
-- cryptography engine
|
||||||
local c_eng = {
|
local _crypt = {
|
||||||
key = nil,
|
key = nil,
|
||||||
hmac = nil
|
hmac = nil
|
||||||
}
|
}
|
||||||
@ -40,23 +40,23 @@ function network.init_mac(passkey)
|
|||||||
key_deriv.setPassword(passkey)
|
key_deriv.setPassword(passkey)
|
||||||
key_deriv.finish()
|
key_deriv.finish()
|
||||||
|
|
||||||
c_eng.key = array.fromHex(key_deriv.asHex())
|
_crypt.key = array.fromHex(key_deriv.asHex())
|
||||||
|
|
||||||
-- initialize HMAC
|
-- initialize HMAC
|
||||||
c_eng.hmac = hmac()
|
_crypt.hmac = hmac()
|
||||||
c_eng.hmac.setBlockSize(64)
|
_crypt.hmac.setBlockSize(64)
|
||||||
c_eng.hmac.setDigest(md5)
|
_crypt.hmac.setDigest(md5)
|
||||||
c_eng.hmac.setKey(c_eng.key)
|
_crypt.hmac.setKey(_crypt.key)
|
||||||
|
|
||||||
local init_time = util.time_ms() - start
|
local init_time = util.time_ms() - start
|
||||||
log.info("network.init_mac completed in " .. init_time .. "ms")
|
log.info("NET: network.init_mac completed in " .. init_time .. "ms")
|
||||||
|
|
||||||
return init_time
|
return init_time
|
||||||
end
|
end
|
||||||
|
|
||||||
-- de-initialize message authentication system
|
-- de-initialize message authentication system
|
||||||
function network.deinit_mac()
|
function network.deinit_mac()
|
||||||
c_eng.key, c_eng.hmac = nil, nil
|
_crypt.key, _crypt.hmac = nil, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- generate HMAC of message
|
-- generate HMAC of message
|
||||||
@ -65,20 +65,20 @@ end
|
|||||||
local function compute_hmac(message)
|
local function compute_hmac(message)
|
||||||
-- local start = util.time_ms()
|
-- local start = util.time_ms()
|
||||||
|
|
||||||
c_eng.hmac.init()
|
_crypt.hmac.init()
|
||||||
c_eng.hmac.update(stream.fromString(message))
|
_crypt.hmac.update(stream.fromString(message))
|
||||||
c_eng.hmac.finish()
|
_crypt.hmac.finish()
|
||||||
|
|
||||||
local hash = c_eng.hmac.asHex()
|
local hash = _crypt.hmac.asHex()
|
||||||
|
|
||||||
-- log.debug("compute_hmac(): hmac-md5 = " .. util.strval(hash) .. " (took " .. (util.time_ms() - start) .. "ms)")
|
-- log.debug("NET: compute_hmac(): hmac-md5 = " .. util.strval(hash) .. " (took " .. (util.time_ms() - start) .. "ms)")
|
||||||
|
|
||||||
return hash
|
return hash
|
||||||
end
|
end
|
||||||
|
|
||||||
-- NIC: Network Interface Controller<br>
|
-- NIC: Network Interface Controller<br>
|
||||||
-- utilizes HMAC-MD5 for message authentication, if enabled and this is wireless
|
-- utilizes HMAC-MD5 for message authentication, if enabled and this is wireless
|
||||||
---@param modem Modem modem to use
|
---@param modem Modem|nil modem to use
|
||||||
function network.nic(modem)
|
function network.nic(modem)
|
||||||
local self = {
|
local self = {
|
||||||
-- modem interface name
|
-- modem interface name
|
||||||
@ -86,9 +86,9 @@ function network.nic(modem)
|
|||||||
-- phy name
|
-- phy name
|
||||||
name = "?",
|
name = "?",
|
||||||
-- used to quickly return out of tx/rx functions if there is nothing to do
|
-- used to quickly return out of tx/rx functions if there is nothing to do
|
||||||
connected = true,
|
connected = false,
|
||||||
-- used to avoid costly MAC calculations if not required
|
-- used to avoid costly MAC calculations if not required
|
||||||
use_hash = c_eng.hmac and modem.isWireless(),
|
use_hash = false,
|
||||||
-- open channels
|
-- open channels
|
||||||
channels = {}
|
channels = {}
|
||||||
}
|
}
|
||||||
@ -112,7 +112,7 @@ function network.nic(modem)
|
|||||||
self.iface = ppm.get_iface(modem)
|
self.iface = ppm.get_iface(modem)
|
||||||
self.name = util.c(util.trinary(modem.isWireless(), "WLAN_PHY", "ETH_PHY"), "{", self.iface, "}")
|
self.name = util.c(util.trinary(modem.isWireless(), "WLAN_PHY", "ETH_PHY"), "{", self.iface, "}")
|
||||||
self.connected = true
|
self.connected = true
|
||||||
self.use_hash = c_eng.hmac and modem.isWireless()
|
self.use_hash = _crypt.hmac and modem.isWireless()
|
||||||
|
|
||||||
-- open only previously opened channels
|
-- open only previously opened channels
|
||||||
modem.closeAll()
|
modem.closeAll()
|
||||||
@ -135,13 +135,13 @@ function network.nic(modem)
|
|||||||
function public.is_modem(device) return device == modem end
|
function public.is_modem(device) return device == modem end
|
||||||
|
|
||||||
-- wrap modem functions, then create custom functions
|
-- wrap modem functions, then create custom functions
|
||||||
public.connect(modem)
|
if modem then public.connect(modem) end
|
||||||
|
|
||||||
-- open a channel on the modem<br>
|
-- open a channel on the modem<br>
|
||||||
-- if disconnected *after* opening, previousy opened channels will be re-opened on reconnection
|
-- if disconnected *after* opening, previousy opened channels will be re-opened on reconnection
|
||||||
---@param channel integer
|
---@param channel integer
|
||||||
function public.open(channel)
|
function public.open(channel)
|
||||||
modem.open(channel)
|
if modem then modem.open(channel) end
|
||||||
|
|
||||||
local already_open = false
|
local already_open = false
|
||||||
for i = 1, #self.channels do
|
for i = 1, #self.channels do
|
||||||
@ -159,7 +159,7 @@ function network.nic(modem)
|
|||||||
-- close a channel on the modem
|
-- close a channel on the modem
|
||||||
---@param channel integer
|
---@param channel integer
|
||||||
function public.close(channel)
|
function public.close(channel)
|
||||||
modem.close(channel)
|
if modem then modem.close(channel) end
|
||||||
|
|
||||||
for i = 1, #self.channels do
|
for i = 1, #self.channels do
|
||||||
if self.channels[i] == channel then
|
if self.channels[i] == channel then
|
||||||
@ -171,7 +171,7 @@ function network.nic(modem)
|
|||||||
|
|
||||||
-- close all channels on the modem
|
-- close all channels on the modem
|
||||||
function public.closeAll()
|
function public.closeAll()
|
||||||
modem.closeAll()
|
if modem then modem.closeAll() end
|
||||||
self.channels = {}
|
self.channels = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -190,10 +190,13 @@ function network.nic(modem)
|
|||||||
---@cast tx_packet authd_packet
|
---@cast tx_packet authd_packet
|
||||||
tx_packet.make(packet, compute_hmac)
|
tx_packet.make(packet, compute_hmac)
|
||||||
|
|
||||||
-- log.debug("network.modem.transmit: data processing took " .. (util.time_ms() - start) .. "ms")
|
-- log.debug("NET: network.modem.transmit: data processing took " .. (util.time_ms() - start) .. "ms")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@diagnostic disable-next-line: need-check-nil
|
||||||
modem.transmit(dest_channel, local_channel, tx_packet.raw_sendable())
|
modem.transmit(dest_channel, local_channel, tx_packet.raw_sendable())
|
||||||
|
else
|
||||||
|
log.debug("NET: network.transmit tx dropped, link is down")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -224,10 +227,10 @@ function network.nic(modem)
|
|||||||
local computed_hmac = compute_hmac(textutils.serialize(s_packet.raw_header(), { allow_repetitions = true, compact = true }))
|
local computed_hmac = compute_hmac(textutils.serialize(s_packet.raw_header(), { allow_repetitions = true, compact = true }))
|
||||||
|
|
||||||
if a_packet.mac() == computed_hmac then
|
if a_packet.mac() == computed_hmac then
|
||||||
-- log.debug("network.modem.receive: HMAC verified in " .. (util.time_ms() - start) .. "ms")
|
-- log.debug("NET: network.modem.receive: HMAC verified in " .. (util.time_ms() - start) .. "ms")
|
||||||
s_packet.stamp_authenticated()
|
s_packet.stamp_authenticated()
|
||||||
else
|
else
|
||||||
-- log.debug("network.modem.receive: HMAC failed verification in " .. (util.time_ms() - start) .. "ms")
|
-- log.debug("NET: network.modem.receive: HMAC failed verification in " .. (util.time_ms() - start) .. "ms")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -22,7 +22,7 @@ ppm.VIRTUAL_DEVICE_TYPE = VIRTUAL_DEVICE_TYPE
|
|||||||
|
|
||||||
local REPORT_FREQUENCY = 20 -- log every 20 faults per function
|
local REPORT_FREQUENCY = 20 -- log every 20 faults per function
|
||||||
|
|
||||||
local ppm_sys = {
|
local _ppm = {
|
||||||
mounts = {}, ---@type { [string]: ppm_entry }
|
mounts = {}, ---@type { [string]: ppm_entry }
|
||||||
next_vid = 0,
|
next_vid = 0,
|
||||||
auto_cf = false,
|
auto_cf = false,
|
||||||
@ -66,7 +66,7 @@ local function peri_init(iface)
|
|||||||
if status then
|
if status then
|
||||||
-- auto fault clear
|
-- auto fault clear
|
||||||
if self.auto_cf then self.faulted = false end
|
if self.auto_cf then self.faulted = false end
|
||||||
if ppm_sys.auto_cf then ppm_sys.faulted = false end
|
if _ppm.auto_cf then _ppm.faulted = false end
|
||||||
|
|
||||||
self.fault_counts[key] = 0
|
self.fault_counts[key] = 0
|
||||||
|
|
||||||
@ -78,10 +78,10 @@ local function peri_init(iface)
|
|||||||
self.faulted = true
|
self.faulted = true
|
||||||
self.last_fault = result
|
self.last_fault = result
|
||||||
|
|
||||||
ppm_sys.faulted = true
|
_ppm.faulted = true
|
||||||
ppm_sys.last_fault = result
|
_ppm.last_fault = result
|
||||||
|
|
||||||
if not ppm_sys.mute and (self.fault_counts[key] % REPORT_FREQUENCY == 0) then
|
if not _ppm.mute and (self.fault_counts[key] % REPORT_FREQUENCY == 0) then
|
||||||
local count_str = ""
|
local count_str = ""
|
||||||
if self.fault_counts[key] > 0 then
|
if self.fault_counts[key] > 0 then
|
||||||
count_str = " [" .. self.fault_counts[key] .. " total faults]"
|
count_str = " [" .. self.fault_counts[key] .. " total faults]"
|
||||||
@ -92,7 +92,7 @@ local function peri_init(iface)
|
|||||||
|
|
||||||
self.fault_counts[key] = self.fault_counts[key] + 1
|
self.fault_counts[key] = self.fault_counts[key] + 1
|
||||||
|
|
||||||
if result == "Terminated" then ppm_sys.terminate = true end
|
if result == "Terminated" then _ppm.terminate = true end
|
||||||
|
|
||||||
return ACCESS_FAULT, result
|
return ACCESS_FAULT, result
|
||||||
end
|
end
|
||||||
@ -159,10 +159,10 @@ local function peri_init(iface)
|
|||||||
self.faulted = true
|
self.faulted = true
|
||||||
self.last_fault = UNDEFINED_FIELD
|
self.last_fault = UNDEFINED_FIELD
|
||||||
|
|
||||||
ppm_sys.faulted = true
|
_ppm.faulted = true
|
||||||
ppm_sys.last_fault = UNDEFINED_FIELD
|
_ppm.last_fault = UNDEFINED_FIELD
|
||||||
|
|
||||||
if not ppm_sys.mute and (self.fault_counts[key] % REPORT_FREQUENCY == 0) then
|
if not _ppm.mute and (self.fault_counts[key] % REPORT_FREQUENCY == 0) then
|
||||||
local count_str = ""
|
local count_str = ""
|
||||||
if self.fault_counts[key] > 0 then
|
if self.fault_counts[key] > 0 then
|
||||||
count_str = " [" .. self.fault_counts[key] .. " total calls]"
|
count_str = " [" .. self.fault_counts[key] .. " total calls]"
|
||||||
@ -193,35 +193,35 @@ end
|
|||||||
-- REPORTING --
|
-- REPORTING --
|
||||||
|
|
||||||
-- silence error prints
|
-- silence error prints
|
||||||
function ppm.disable_reporting() ppm_sys.mute = true end
|
function ppm.disable_reporting() _ppm.mute = true end
|
||||||
|
|
||||||
-- allow error prints
|
-- allow error prints
|
||||||
function ppm.enable_reporting() ppm_sys.mute = false end
|
function ppm.enable_reporting() _ppm.mute = false end
|
||||||
|
|
||||||
-- FAULT MEMORY --
|
-- FAULT MEMORY --
|
||||||
|
|
||||||
-- enable automatically clearing fault flag
|
-- enable automatically clearing fault flag
|
||||||
function ppm.enable_afc() ppm_sys.auto_cf = true end
|
function ppm.enable_afc() _ppm.auto_cf = true end
|
||||||
|
|
||||||
-- disable automatically clearing fault flag
|
-- disable automatically clearing fault flag
|
||||||
function ppm.disable_afc() ppm_sys.auto_cf = false end
|
function ppm.disable_afc() _ppm.auto_cf = false end
|
||||||
|
|
||||||
-- clear fault flag
|
-- clear fault flag
|
||||||
function ppm.clear_fault() ppm_sys.faulted = false end
|
function ppm.clear_fault() _ppm.faulted = false end
|
||||||
|
|
||||||
-- check fault flag
|
-- check fault flag
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function ppm.is_faulted() return ppm_sys.faulted end
|
function ppm.is_faulted() return _ppm.faulted end
|
||||||
|
|
||||||
-- get the last fault message
|
-- get the last fault message
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function ppm.get_last_fault() return ppm_sys.last_fault end
|
function ppm.get_last_fault() return _ppm.last_fault end
|
||||||
|
|
||||||
-- TERMINATION --
|
-- TERMINATION --
|
||||||
|
|
||||||
-- if a caught error was a termination request
|
-- if a caught error was a termination request
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function ppm.should_terminate() return ppm_sys.terminate end
|
function ppm.should_terminate() return _ppm.terminate end
|
||||||
|
|
||||||
-- MOUNTING --
|
-- MOUNTING --
|
||||||
|
|
||||||
@ -229,12 +229,12 @@ function ppm.should_terminate() return ppm_sys.terminate end
|
|||||||
function ppm.mount_all()
|
function ppm.mount_all()
|
||||||
local ifaces = peripheral.getNames()
|
local ifaces = peripheral.getNames()
|
||||||
|
|
||||||
ppm_sys.mounts = {}
|
_ppm.mounts = {}
|
||||||
|
|
||||||
for i = 1, #ifaces do
|
for i = 1, #ifaces do
|
||||||
ppm_sys.mounts[ifaces[i]] = peri_init(ifaces[i])
|
_ppm.mounts[ifaces[i]] = peri_init(ifaces[i])
|
||||||
|
|
||||||
log.info(util.c("PPM: found a ", ppm_sys.mounts[ifaces[i]].type, " (", ifaces[i], ")"))
|
log.info(util.c("PPM: found a ", _ppm.mounts[ifaces[i]].type, " (", ifaces[i], ")"))
|
||||||
end
|
end
|
||||||
|
|
||||||
if #ifaces == 0 then
|
if #ifaces == 0 then
|
||||||
@ -253,10 +253,10 @@ function ppm.mount(iface)
|
|||||||
|
|
||||||
for i = 1, #ifaces do
|
for i = 1, #ifaces do
|
||||||
if iface == ifaces[i] then
|
if iface == ifaces[i] then
|
||||||
ppm_sys.mounts[iface] = peri_init(iface)
|
_ppm.mounts[iface] = peri_init(iface)
|
||||||
|
|
||||||
pm_type = ppm_sys.mounts[iface].type
|
pm_type = _ppm.mounts[iface].type
|
||||||
pm_dev = ppm_sys.mounts[iface].dev
|
pm_dev = _ppm.mounts[iface].dev
|
||||||
|
|
||||||
log.info(util.c("PPM: mount(", iface, ") -> found a ", pm_type))
|
log.info(util.c("PPM: mount(", iface, ") -> found a ", pm_type))
|
||||||
break
|
break
|
||||||
@ -278,12 +278,12 @@ function ppm.remount(iface)
|
|||||||
for i = 1, #ifaces do
|
for i = 1, #ifaces do
|
||||||
if iface == ifaces[i] then
|
if iface == ifaces[i] then
|
||||||
log.info(util.c("PPM: remount(", iface, ") -> is a ", pm_type))
|
log.info(util.c("PPM: remount(", iface, ") -> is a ", pm_type))
|
||||||
ppm.unmount(ppm_sys.mounts[iface].dev)
|
ppm.unmount(_ppm.mounts[iface].dev)
|
||||||
|
|
||||||
ppm_sys.mounts[iface] = peri_init(iface)
|
_ppm.mounts[iface] = peri_init(iface)
|
||||||
|
|
||||||
pm_type = ppm_sys.mounts[iface].type
|
pm_type = _ppm.mounts[iface].type
|
||||||
pm_dev = ppm_sys.mounts[iface].dev
|
pm_dev = _ppm.mounts[iface].dev
|
||||||
|
|
||||||
log.info(util.c("PPM: remount(", iface, ") -> remounted a ", pm_type))
|
log.info(util.c("PPM: remount(", iface, ") -> remounted a ", pm_type))
|
||||||
break
|
break
|
||||||
@ -297,24 +297,24 @@ end
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@return string type, table device
|
---@return string type, table device
|
||||||
function ppm.mount_virtual()
|
function ppm.mount_virtual()
|
||||||
local iface = "ppm_vdev_" .. ppm_sys.next_vid
|
local iface = "ppm_vdev_" .. _ppm.next_vid
|
||||||
|
|
||||||
ppm_sys.mounts[iface] = peri_init("__virtual__")
|
_ppm.mounts[iface] = peri_init("__virtual__")
|
||||||
ppm_sys.next_vid = ppm_sys.next_vid + 1
|
_ppm.next_vid = _ppm.next_vid + 1
|
||||||
|
|
||||||
log.info(util.c("PPM: mount_virtual() -> allocated new virtual device ", iface))
|
log.info(util.c("PPM: mount_virtual() -> allocated new virtual device ", iface))
|
||||||
|
|
||||||
return ppm_sys.mounts[iface].type, ppm_sys.mounts[iface].dev
|
return _ppm.mounts[iface].type, _ppm.mounts[iface].dev
|
||||||
end
|
end
|
||||||
|
|
||||||
-- manually unmount a peripheral from the PPM
|
-- manually unmount a peripheral from the PPM
|
||||||
---@param device table device table
|
---@param device table device table
|
||||||
function ppm.unmount(device)
|
function ppm.unmount(device)
|
||||||
if device then
|
if device then
|
||||||
for iface, data in pairs(ppm_sys.mounts) do
|
for iface, data in pairs(_ppm.mounts) do
|
||||||
if data.dev == device then
|
if data.dev == device then
|
||||||
log.warning(util.c("PPM: manually unmounted ", data.type, " mounted to ", iface))
|
log.warning(util.c("PPM: manually unmounted ", data.type, " mounted to ", iface))
|
||||||
ppm_sys.mounts[iface] = nil
|
_ppm.mounts[iface] = nil
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -330,7 +330,7 @@ function ppm.handle_unmount(iface)
|
|||||||
local pm_type = nil
|
local pm_type = nil
|
||||||
|
|
||||||
-- what got disconnected?
|
-- what got disconnected?
|
||||||
local lost_dev = ppm_sys.mounts[iface]
|
local lost_dev = _ppm.mounts[iface]
|
||||||
|
|
||||||
if lost_dev then
|
if lost_dev then
|
||||||
pm_type = lost_dev.type
|
pm_type = lost_dev.type
|
||||||
@ -341,18 +341,18 @@ function ppm.handle_unmount(iface)
|
|||||||
log.error(util.c("PPM: lost device unknown to the PPM mounted to ", iface))
|
log.error(util.c("PPM: lost device unknown to the PPM mounted to ", iface))
|
||||||
end
|
end
|
||||||
|
|
||||||
ppm_sys.mounts[iface] = nil
|
_ppm.mounts[iface] = nil
|
||||||
|
|
||||||
return pm_type, pm_dev
|
return pm_type, pm_dev
|
||||||
end
|
end
|
||||||
|
|
||||||
-- log all mounts, to be used if `ppm.mount_all` is called before logging is ready
|
-- log all mounts, to be used if `ppm.mount_all` is called before logging is ready
|
||||||
function ppm.log_mounts()
|
function ppm.log_mounts()
|
||||||
for iface, mount in pairs(ppm_sys.mounts) do
|
for iface, mount in pairs(_ppm.mounts) do
|
||||||
log.info(util.c("PPM: had found a ", mount.type, " (", iface, ")"))
|
log.info(util.c("PPM: had found a ", mount.type, " (", iface, ")"))
|
||||||
end
|
end
|
||||||
|
|
||||||
if util.table_len(ppm_sys.mounts) == 0 then
|
if util.table_len(_ppm.mounts) == 0 then
|
||||||
log.warning("PPM: no devices had been found")
|
log.warning("PPM: no devices had been found")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -369,7 +369,7 @@ function ppm.list_avail() return peripheral.getNames() end
|
|||||||
---@return { [string]: ppm_entry } mounts
|
---@return { [string]: ppm_entry } mounts
|
||||||
function ppm.list_mounts()
|
function ppm.list_mounts()
|
||||||
local list = {}
|
local list = {}
|
||||||
for k, v in pairs(ppm_sys.mounts) do list[k] = v end
|
for k, v in pairs(_ppm.mounts) do list[k] = v end
|
||||||
return list
|
return list
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -379,7 +379,7 @@ end
|
|||||||
---@return string|nil iface CC peripheral interface
|
---@return string|nil iface CC peripheral interface
|
||||||
function ppm.get_iface(device)
|
function ppm.get_iface(device)
|
||||||
if device then
|
if device then
|
||||||
for iface, data in pairs(ppm_sys.mounts) do
|
for iface, data in pairs(_ppm.mounts) do
|
||||||
if data.dev == device then return iface end
|
if data.dev == device then return iface end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -392,8 +392,8 @@ end
|
|||||||
---@param iface string CC peripheral interface
|
---@param iface string CC peripheral interface
|
||||||
---@return { [string]: function }|nil device function table
|
---@return { [string]: function }|nil device function table
|
||||||
function ppm.get_periph(iface)
|
function ppm.get_periph(iface)
|
||||||
if ppm_sys.mounts[iface] then
|
if _ppm.mounts[iface] then
|
||||||
return ppm_sys.mounts[iface].dev
|
return _ppm.mounts[iface].dev
|
||||||
else return nil end
|
else return nil end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -402,8 +402,8 @@ end
|
|||||||
---@param iface string CC peripheral interface
|
---@param iface string CC peripheral interface
|
||||||
---@return string|nil type
|
---@return string|nil type
|
||||||
function ppm.get_type(iface)
|
function ppm.get_type(iface)
|
||||||
if ppm_sys.mounts[iface] then
|
if _ppm.mounts[iface] then
|
||||||
return ppm_sys.mounts[iface].type
|
return _ppm.mounts[iface].type
|
||||||
else return nil end
|
else return nil end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -414,7 +414,7 @@ end
|
|||||||
function ppm.get_all_devices(name)
|
function ppm.get_all_devices(name)
|
||||||
local devices = {}
|
local devices = {}
|
||||||
|
|
||||||
for _, data in pairs(ppm_sys.mounts) do
|
for _, data in pairs(_ppm.mounts) do
|
||||||
if data.type == name then
|
if data.type == name then
|
||||||
table.insert(devices, data.dev)
|
table.insert(devices, data.dev)
|
||||||
end
|
end
|
||||||
@ -430,7 +430,7 @@ end
|
|||||||
function ppm.get_device(name)
|
function ppm.get_device(name)
|
||||||
local device = nil
|
local device = nil
|
||||||
|
|
||||||
for _, data in pairs(ppm_sys.mounts) do
|
for _, data in pairs(_ppm.mounts) do
|
||||||
if data.type == name then
|
if data.type == name then
|
||||||
device = data.dev
|
device = data.dev
|
||||||
break
|
break
|
||||||
@ -468,7 +468,7 @@ function ppm.get_wireless_modem()
|
|||||||
local w_modem, w_iface = nil, nil
|
local w_modem, w_iface = nil, nil
|
||||||
local emulated_env = periphemu ~= nil
|
local emulated_env = periphemu ~= nil
|
||||||
|
|
||||||
for iface, device in pairs(ppm_sys.mounts) do
|
for iface, device in pairs(_ppm.mounts) do
|
||||||
if device.type == "modem" and (emulated_env or device.dev.isWireless()) then
|
if device.type == "modem" and (emulated_env or device.dev.isWireless()) then
|
||||||
w_iface = iface
|
w_iface = iface
|
||||||
w_modem = device.dev
|
w_modem = device.dev
|
||||||
@ -498,7 +498,7 @@ end
|
|||||||
function ppm.get_monitor_list()
|
function ppm.get_monitor_list()
|
||||||
local list = {}
|
local list = {}
|
||||||
|
|
||||||
for iface, device in pairs(ppm_sys.mounts) do
|
for iface, device in pairs(_ppm.mounts) do
|
||||||
if device.type == "monitor" then list[iface] = device end
|
if device.type == "monitor" then list[iface] = device end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user