#118 cleanup started of scada-common

This commit is contained in:
Mikayla Fischler 2023-02-21 10:31:05 -05:00
parent e2d2a0f1dc
commit 34cac6a8b8
13 changed files with 273 additions and 222 deletions

View File

@ -173,7 +173,6 @@ local function main()
log.debug("init> running without networking") log.debug("init> running without networking")
end end
---@diagnostic disable-next-line: param-type-mismatch
util.push_event("clock_start") util.push_event("clock_start")
println("boot> completed") println("boot> completed")

View File

@ -266,7 +266,6 @@ function threads.thread__main(smem, init)
-- 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("main thread restarting now...")
---@diagnostic disable-next-line: param-type-mismatch
util.push_event("clock_start") util.push_event("clock_start")
end end
end end

View File

@ -16,7 +16,7 @@ local max_distance = nil
comms.version = "1.4.0" comms.version = "1.4.0"
---@alias PROTOCOLS integer ---@enum PROTOCOLS
local PROTOCOLS = { local PROTOCOLS = {
MODBUS_TCP = 0, -- our "MODBUS TCP"-esque protocol MODBUS_TCP = 0, -- our "MODBUS TCP"-esque protocol
RPLC = 1, -- reactor PLC protocol RPLC = 1, -- reactor PLC protocol
@ -25,7 +25,7 @@ local PROTOCOLS = {
COORD_API = 4 -- data/control packets for pocket computers to/from coordinators COORD_API = 4 -- data/control packets for pocket computers to/from coordinators
} }
---@alias RPLC_TYPES integer ---@enum RPLC_TYPES
local RPLC_TYPES = { local RPLC_TYPES = {
STATUS = 0, -- reactor/system status STATUS = 0, -- reactor/system status
MEK_STRUCT = 1, -- mekanism build structure MEK_STRUCT = 1, -- mekanism build structure
@ -40,7 +40,7 @@ local RPLC_TYPES = {
AUTO_BURN_RATE = 10 -- set an automatic burn rate, PLC will respond with status, enable toggle speed limited AUTO_BURN_RATE = 10 -- set an automatic burn rate, PLC will respond with status, enable toggle speed limited
} }
---@alias SCADA_MGMT_TYPES integer ---@enum SCADA_MGMT_TYPES
local SCADA_MGMT_TYPES = { local SCADA_MGMT_TYPES = {
ESTABLISH = 0, -- establish new connection ESTABLISH = 0, -- establish new connection
KEEP_ALIVE = 1, -- keep alive packet w/ RTT KEEP_ALIVE = 1, -- keep alive packet w/ RTT
@ -49,7 +49,7 @@ local SCADA_MGMT_TYPES = {
RTU_DEV_REMOUNT = 4 -- RTU multiblock possbily changed (formed, unformed) due to PPM remount RTU_DEV_REMOUNT = 4 -- RTU multiblock possbily changed (formed, unformed) due to PPM remount
} }
---@alias SCADA_CRDN_TYPES integer ---@enum SCADA_CRDN_TYPES
local SCADA_CRDN_TYPES = { local SCADA_CRDN_TYPES = {
INITIAL_BUILDS = 0, -- initial, complete builds packet to the coordinator INITIAL_BUILDS = 0, -- initial, complete builds packet to the coordinator
FAC_BUILDS = 1, -- facility RTU builds FAC_BUILDS = 1, -- facility RTU builds
@ -60,12 +60,11 @@ local SCADA_CRDN_TYPES = {
UNIT_CMD = 6 -- command a reactor unit UNIT_CMD = 6 -- command a reactor unit
} }
---@alias CAPI_TYPES integer ---@enum CAPI_TYPES
local CAPI_TYPES = { local CAPI_TYPES = {
ESTABLISH = 0 -- initial greeting
} }
---@alias ESTABLISH_ACK integer ---@enum ESTABLISH_ACK
local ESTABLISH_ACK = { local ESTABLISH_ACK = {
ALLOW = 0, -- link approved ALLOW = 0, -- link approved
DENY = 1, -- link denied DENY = 1, -- link denied
@ -73,7 +72,7 @@ local ESTABLISH_ACK = {
BAD_VERSION = 3 -- link denied due to comms version mismatch BAD_VERSION = 3 -- link denied due to comms version mismatch
} }
---@alias DEVICE_TYPES integer ---@enum DEVICE_TYPES
local DEVICE_TYPES = { local DEVICE_TYPES = {
PLC = 0, -- PLC device type for establish PLC = 0, -- PLC device type for establish
RTU = 1, -- RTU device type for establish RTU = 1, -- RTU device type for establish
@ -81,7 +80,7 @@ local DEVICE_TYPES = {
CRDN = 3 -- coordinator device type for establish CRDN = 3 -- coordinator device type for establish
} }
---@alias RTU_UNIT_TYPES integer ---@enum RTU_UNIT_TYPES
local RTU_UNIT_TYPES = { local RTU_UNIT_TYPES = {
REDSTONE = 0, -- redstone I/O REDSTONE = 0, -- redstone I/O
BOILER_VALVE = 1, -- boiler mekanism 10.1+ BOILER_VALVE = 1, -- boiler mekanism 10.1+
@ -92,7 +91,7 @@ local RTU_UNIT_TYPES = {
ENV_DETECTOR = 6 -- environment detector ENV_DETECTOR = 6 -- environment detector
} }
---@alias PLC_AUTO_ACK integer ---@enum PLC_AUTO_ACK
local PLC_AUTO_ACK = { local PLC_AUTO_ACK = {
FAIL = 0, -- failed to set burn rate/burn rate invalid FAIL = 0, -- failed to set burn rate/burn rate invalid
DIRECT_SET_OK = 1, -- successfully set burn rate DIRECT_SET_OK = 1, -- successfully set burn rate
@ -100,7 +99,7 @@ local PLC_AUTO_ACK = {
ZERO_DIS_OK = 3 -- successfully disabled reactor with < 0.01 burn rate ZERO_DIS_OK = 3 -- successfully disabled reactor with < 0.01 burn rate
} }
---@alias FAC_COMMANDS integer ---@enum FAC_COMMANDS
local FAC_COMMANDS = { local FAC_COMMANDS = {
SCRAM_ALL = 0, -- SCRAM all reactors SCRAM_ALL = 0, -- SCRAM all reactors
STOP = 1, -- stop automatic control STOP = 1, -- stop automatic control
@ -108,7 +107,7 @@ local FAC_COMMANDS = {
ACK_ALL_ALARMS = 3 -- acknowledge all alarms on all units ACK_ALL_ALARMS = 3 -- acknowledge all alarms on all units
} }
---@alias UNIT_COMMANDS integer ---@enum UNIT_COMMANDS
local UNIT_COMMANDS = { local UNIT_COMMANDS = {
SCRAM = 0, -- SCRAM the reactor SCRAM = 0, -- SCRAM the reactor
START = 1, -- start the reactor START = 1, -- start the reactor
@ -152,6 +151,7 @@ function comms.set_trusted_range(distance)
end end
-- generic SCADA packet object -- generic SCADA packet object
---@nodiscard
function comms.scada_packet() function comms.scada_packet()
local self = { local self = {
modem_msg_in = nil, modem_msg_in = nil,
@ -180,11 +180,12 @@ function comms.scada_packet()
end end
-- parse in a modem message as a SCADA packet -- parse in a modem message as a SCADA packet
---@param side string ---@param side string modem side
---@param sender integer ---@param sender integer sender port
---@param reply_to integer ---@param reply_to integer reply port
---@param message any ---@param message any message body
---@param distance integer ---@param distance integer transmission distance
---@return boolean valid valid message received
function public.receive(side, sender, reply_to, message, distance) function public.receive(side, sender, reply_to, message, distance)
self.modem_msg_in = { self.modem_msg_in = {
iface = side, iface = side,
@ -223,24 +224,34 @@ function comms.scada_packet()
-- public accessors -- -- public accessors --
---@nodiscard
function public.modem_event() return self.modem_msg_in end function public.modem_event() return self.modem_msg_in end
---@nodiscard
function public.raw_sendable() return self.raw end function public.raw_sendable() return self.raw end
---@nodiscard
function public.local_port() return self.modem_msg_in.s_port end function public.local_port() return self.modem_msg_in.s_port end
---@nodiscard
function public.remote_port() return self.modem_msg_in.r_port end function public.remote_port() return self.modem_msg_in.r_port end
---@nodiscard
function public.is_valid() return self.valid end function public.is_valid() return self.valid end
---@nodiscard
function public.seq_num() return self.seq_num end function public.seq_num() return self.seq_num end
---@nodiscard
function public.protocol() return self.protocol end function public.protocol() return self.protocol end
---@nodiscard
function public.length() return self.length end function public.length() return self.length end
---@nodiscard
function public.data() return self.payload end function public.data() return self.payload end
return public return public
end end
-- MODBUS packet -- MODBUS packet<br>
-- modeled after MODBUS TCP packet -- modeled after MODBUS TCP packet
---@nodiscard
function comms.modbus_packet() function comms.modbus_packet()
local self = { local self = {
frame = nil, frame = nil,
@ -309,9 +320,11 @@ function comms.modbus_packet()
end end
-- get raw to send -- get raw to send
---@nodiscard
function public.raw_sendable() return self.raw end function public.raw_sendable() return self.raw end
-- get this packet as a frame with an immutable relation to this object -- get this packet as a frame with an immutable relation to this object
---@nodiscard
function public.get() function public.get()
---@class modbus_frame ---@class modbus_frame
local frame = { local frame = {
@ -330,6 +343,7 @@ function comms.modbus_packet()
end end
-- reactor PLC packet -- reactor PLC packet
---@nodiscard
function comms.rplc_packet() function comms.rplc_packet()
local self = { local self = {
frame = nil, frame = nil,
@ -410,9 +424,11 @@ function comms.rplc_packet()
end end
-- get raw to send -- get raw to send
---@nodiscard
function public.raw_sendable() return self.raw end function public.raw_sendable() return self.raw end
-- get this packet as a frame with an immutable relation to this object -- get this packet as a frame with an immutable relation to this object
---@nodiscard
function public.get() function public.get()
---@class rplc_frame ---@class rplc_frame
local frame = { local frame = {
@ -430,6 +446,7 @@ function comms.rplc_packet()
end end
-- SCADA management packet -- SCADA management packet
---@nodiscard
function comms.mgmt_packet() function comms.mgmt_packet()
local self = { local self = {
frame = nil, frame = nil,
@ -500,9 +517,11 @@ function comms.mgmt_packet()
end end
-- get raw to send -- get raw to send
---@nodiscard
function public.raw_sendable() return self.raw end function public.raw_sendable() return self.raw end
-- get this packet as a frame with an immutable relation to this object -- get this packet as a frame with an immutable relation to this object
---@nodiscard
function public.get() function public.get()
---@class mgmt_frame ---@class mgmt_frame
local frame = { local frame = {
@ -519,6 +538,7 @@ function comms.mgmt_packet()
end end
-- SCADA coordinator packet -- SCADA coordinator packet
---@nodiscard
function comms.crdn_packet() function comms.crdn_packet()
local self = { local self = {
frame = nil, frame = nil,
@ -532,6 +552,7 @@ function comms.crdn_packet()
local public = {} local public = {}
-- check that type is known -- check that type is known
---@nodiscard
local function _crdn_type_valid() local function _crdn_type_valid()
return self.type == SCADA_CRDN_TYPES.INITIAL_BUILDS or return self.type == SCADA_CRDN_TYPES.INITIAL_BUILDS or
self.type == SCADA_CRDN_TYPES.FAC_BUILDS or self.type == SCADA_CRDN_TYPES.FAC_BUILDS or
@ -590,9 +611,11 @@ function comms.crdn_packet()
end end
-- get raw to send -- get raw to send
---@nodiscard
function public.raw_sendable() return self.raw end function public.raw_sendable() return self.raw end
-- get this packet as a frame with an immutable relation to this object -- get this packet as a frame with an immutable relation to this object
---@nodiscard
function public.get() function public.get()
---@class crdn_frame ---@class crdn_frame
local frame = { local frame = {
@ -609,7 +632,8 @@ function comms.crdn_packet()
end end
-- coordinator API (CAPI) packet -- coordinator API (CAPI) packet
-- @todo ---@todo implement for pocket access
---@nodiscard
function comms.capi_packet() function comms.capi_packet()
local self = { local self = {
frame = nil, frame = nil,
@ -623,7 +647,7 @@ function comms.capi_packet()
local public = {} local public = {}
local function _capi_type_valid() local function _capi_type_valid()
-- @todo ---@todo
return false return false
end end
@ -675,9 +699,11 @@ function comms.capi_packet()
end end
-- get raw to send -- get raw to send
---@nodiscard
function public.raw_sendable() return self.raw end function public.raw_sendable() return self.raw end
-- get this packet as a frame with an immutable relation to this object -- get this packet as a frame with an immutable relation to this object
---@nodiscard
function public.get() function public.get()
---@class capi_frame ---@class capi_frame
local frame = { local frame = {
@ -694,6 +720,7 @@ function comms.capi_packet()
end end
-- convert rtu_t to RTU unit type -- convert rtu_t to RTU unit type
---@nodiscard
---@param type rtu_t ---@param type rtu_t
---@return RTU_UNIT_TYPES|nil ---@return RTU_UNIT_TYPES|nil
function comms.rtu_t_to_unit_type(type) function comms.rtu_t_to_unit_type(type)
@ -717,6 +744,7 @@ function comms.rtu_t_to_unit_type(type)
end end
-- convert RTU unit type to rtu_t -- convert RTU unit type to rtu_t
---@nodiscard
---@param utype RTU_UNIT_TYPES ---@param utype RTU_UNIT_TYPES
---@return rtu_t|nil ---@return rtu_t|nil
function comms.advert_type_to_rtu_t(utype) function comms.advert_type_to_rtu_t(utype)

View File

@ -70,6 +70,7 @@ function crypto.init(password, server_port)
end end
-- encrypt plaintext -- encrypt plaintext
---@nodiscard
---@param plaintext string ---@param plaintext string
---@return table initial_value, string ciphertext ---@return table initial_value, string ciphertext
function crypto.encrypt(plaintext) function crypto.encrypt(plaintext)
@ -113,6 +114,7 @@ function crypto.encrypt(plaintext)
end end
-- decrypt ciphertext -- decrypt ciphertext
---@nodiscard
---@param iv string CTR initial value ---@param iv string CTR initial value
---@param ciphertext string ciphertext hex ---@param ciphertext string ciphertext hex
---@return string plaintext ---@return string plaintext
@ -135,6 +137,7 @@ function crypto.decrypt(iv, ciphertext)
end end
-- generate HMAC of message -- generate HMAC of message
---@nodiscard
---@param message_hex string initial value concatenated with ciphertext ---@param message_hex string initial value concatenated with ciphertext
function crypto.hmac(message_hex) function crypto.hmac(message_hex)
local start = util.time() local start = util.time()
@ -201,11 +204,12 @@ function crypto.secure_modem(modem)
end end
-- parse in a modem message as a network packet -- parse in a modem message as a network packet
---@param side string ---@nodiscard
---@param sender integer ---@param side string modem side
---@param reply_to integer ---@param sender integer sender port
---@param reply_to integer reply port
---@param message any encrypted packet sent with secure_modem.transmit ---@param message any encrypted packet sent with secure_modem.transmit
---@param distance integer ---@param distance integer transmission distance
---@return string side, integer sender, integer reply_to, any plaintext_message, integer distance ---@return string side, integer sender, integer reply_to, any plaintext_message, integer distance
function public.receive(side, sender, reply_to, message, distance) function public.receive(side, sender, reply_to, message, distance)
local body = "" local body = ""

View File

@ -18,7 +18,7 @@ log.MODE = MODE
-- whether to log debug messages or not -- whether to log debug messages or not
local LOG_DEBUG = true local LOG_DEBUG = true
local _log_sys = { local log_sys = {
path = "/log.txt", path = "/log.txt",
mode = MODE.APPEND, mode = MODE.APPEND,
file = nil, file = nil,
@ -33,27 +33,25 @@ local free_space = fs.getFreeSpace
---@param write_mode MODE ---@param write_mode MODE
---@param dmesg_redirect? table terminal/window to direct dmesg to ---@param dmesg_redirect? table terminal/window to direct dmesg to
function log.init(path, write_mode, dmesg_redirect) function log.init(path, write_mode, dmesg_redirect)
_log_sys.path = path log_sys.path = path
_log_sys.mode = write_mode log_sys.mode = write_mode
if _log_sys.mode == MODE.APPEND then if log_sys.mode == MODE.APPEND then
_log_sys.file = fs.open(path, "a") log_sys.file = fs.open(path, "a")
else else
_log_sys.file = fs.open(path, "w") log_sys.file = fs.open(path, "w")
end end
if dmesg_redirect then if dmesg_redirect then
_log_sys.dmesg_out = dmesg_redirect log_sys.dmesg_out = dmesg_redirect
else else
_log_sys.dmesg_out = term.current() log_sys.dmesg_out = term.current()
end end
end end
-- direct dmesg output to a monitor/window -- direct dmesg output to a monitor/window
---@param window table window or terminal reference ---@param window table window or terminal reference
function log.direct_dmesg(window) function log.direct_dmesg(window) log_sys.dmesg_out = window end
_log_sys.dmesg_out = window
end
-- private log write function -- private log write function
---@param msg string ---@param msg string
@ -64,8 +62,8 @@ local function _log(msg)
-- attempt to write log -- attempt to write log
local status, result = pcall(function () local status, result = pcall(function ()
_log_sys.file.writeLine(stamped) log_sys.file.writeLine(stamped)
_log_sys.file.flush() log_sys.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
@ -80,18 +78,18 @@ local function _log(msg)
end end
end end
if out_of_space or (free_space(_log_sys.path) < 100) then if out_of_space or (free_space(log_sys.path) < 100) then
-- delete the old log file before opening a new one -- delete the old log file before opening a new one
_log_sys.file.close() log_sys.file.close()
fs.delete(_log_sys.path) fs.delete(log_sys.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(_log_sys.path, _log_sys.mode, _log_sys.dmesg_out) log.init(log_sys.path, log_sys.mode, log_sys.dmesg_out)
-- leave a message -- leave a message
_log_sys.file.writeLine(time_stamp .. "recycled log file") log_sys.file.writeLine(time_stamp .. "recycled log file")
_log_sys.file.writeLine(stamped) log_sys.file.writeLine(stamped)
_log_sys.file.flush() log_sys.file.flush()
end end
end end
@ -109,7 +107,7 @@ function log.dmesg(msg, tag, tag_color)
tag = util.strval(tag) tag = util.strval(tag)
local t_stamp = string.format("%12.2f", os.clock()) local t_stamp = string.format("%12.2f", os.clock())
local out = _log_sys.dmesg_out local out = log_sys.dmesg_out
if out ~= nil then if out ~= nil then
local out_w, out_h = out.getSize() local out_w, out_h = out.getSize()
@ -197,6 +195,7 @@ function log.dmesg(msg, tag, tag_color)
end end
-- print a dmesg message, but then show remaining seconds instead of timestamp -- print a dmesg message, but then show remaining seconds instead of timestamp
---@nodiscard
---@param msg string message ---@param msg string message
---@param tag? string log tag ---@param tag? string log tag
---@param tag_color? integer log tag color ---@param tag_color? integer log tag color
@ -204,7 +203,7 @@ end
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 out = _log_sys.dmesg_out local out = log_sys.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

View File

@ -4,7 +4,7 @@
local mqueue = {} local mqueue = {}
---@alias MQ_TYPE integer ---@enum MQ_TYPE
local TYPE = { local TYPE = {
COMMAND = 0, COMMAND = 0,
DATA = 1, DATA = 1,
@ -14,6 +14,7 @@ local TYPE = {
mqueue.TYPE = TYPE mqueue.TYPE = TYPE
-- create a new message queue -- create a new message queue
---@nodiscard
function mqueue.new() function mqueue.new()
local queue = {} local queue = {}
@ -35,10 +36,13 @@ function mqueue.new()
function public.length() return #queue end function public.length() return #queue end
-- check if queue is empty -- check if queue is empty
---@nodiscard
---@return boolean is_empty ---@return boolean is_empty
function public.empty() return #queue == 0 end function public.empty() return #queue == 0 end
-- check if queue has contents -- check if queue has contents
---@nodiscard
---@return boolean has_contents
function public.ready() return #queue ~= 0 end function public.ready() return #queue ~= 0 end
-- push a new item onto the queue -- push a new item onto the queue
@ -68,6 +72,7 @@ function mqueue.new()
end end
-- get an item off the queue -- get an item off the queue
---@nodiscard
---@return queue_item|nil ---@return queue_item|nil
function public.pop() function public.pop()
if #queue > 0 then if #queue > 0 then

View File

@ -24,7 +24,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_sys = {
mounts = {}, mounts = {},
next_vid = 0, next_vid = 0,
auto_cf = false, auto_cf = false,
@ -34,11 +34,9 @@ local _ppm_sys = {
mute = false mute = false
} }
-- wrap peripheral calls with lua protected call as we don't want a disconnect to crash a program -- wrap peripheral calls with lua protected call as we don't want a disconnect to crash a program<br>
--- -- also provides peripheral-specific fault checks (auto-clear fault defaults to true)<br>
---also provides peripheral-specific fault checks (auto-clear fault defaults to true) -- assumes iface is a valid peripheral
---
---assumes iface is a valid peripheral
---@param iface string CC peripheral interface ---@param iface string CC peripheral interface
local function peri_init(iface) local function peri_init(iface)
local self = { local self = {
@ -68,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_sys.auto_cf then ppm_sys.faulted = false end
self.fault_counts[key] = 0 self.fault_counts[key] = 0
@ -80,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_sys.faulted = true
_ppm_sys.last_fault = result ppm_sys.last_fault = result
if not _ppm_sys.mute and (self.fault_counts[key] % REPORT_FREQUENCY == 0) then if not ppm_sys.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]"
@ -95,7 +93,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 if result == "Terminated" then
_ppm_sys.terminate = true ppm_sys.terminate = true
end end
return ACCESS_FAULT return ACCESS_FAULT
@ -136,10 +134,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_sys.faulted = true
_ppm_sys.last_fault = UNDEFINED_FIELD ppm_sys.last_fault = UNDEFINED_FIELD
if not _ppm_sys.mute and (self.fault_counts[key] % REPORT_FREQUENCY == 0) then if not ppm_sys.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]"
@ -169,48 +167,35 @@ end
-- REPORTING -- -- REPORTING --
-- silence error prints -- silence error prints
function ppm.disable_reporting() function ppm.disable_reporting() ppm_sys.mute = true end
_ppm_sys.mute = true
end
-- allow error prints -- allow error prints
function ppm.enable_reporting() function ppm.enable_reporting() ppm_sys.mute = false end
_ppm_sys.mute = false
end
-- FAULT MEMORY -- -- FAULT MEMORY --
-- enable automatically clearing fault flag -- enable automatically clearing fault flag
function ppm.enable_afc() function ppm.enable_afc() ppm_sys.auto_cf = true end
_ppm_sys.auto_cf = true
end
-- disable automatically clearing fault flag -- disable automatically clearing fault flag
function ppm.disable_afc() function ppm.disable_afc() ppm_sys.auto_cf = false end
_ppm_sys.auto_cf = false
end
-- clear fault flag -- clear fault flag
function ppm.clear_fault() function ppm.clear_fault() ppm_sys.faulted = false end
_ppm_sys.faulted = false
end
-- check fault flag -- check fault flag
function ppm.is_faulted() ---@nodiscard
return _ppm_sys.faulted function ppm.is_faulted() return ppm_sys.faulted end
end
-- get the last fault message -- get the last fault message
function ppm.get_last_fault() ---@nodiscard
return _ppm_sys.last_fault function ppm.get_last_fault() return ppm_sys.last_fault end
end
-- TERMINATION -- -- TERMINATION --
-- if a caught error was a termination request -- if a caught error was a termination request
function ppm.should_terminate() ---@nodiscard
return _ppm_sys.terminate function ppm.should_terminate() return ppm_sys.terminate end
end
-- MOUNTING -- -- MOUNTING --
@ -218,12 +203,12 @@ end
function ppm.mount_all() function ppm.mount_all()
local ifaces = peripheral.getNames() local ifaces = peripheral.getNames()
_ppm_sys.mounts = {} ppm_sys.mounts = {}
for i = 1, #ifaces do for i = 1, #ifaces do
_ppm_sys.mounts[ifaces[i]] = peri_init(ifaces[i]) ppm_sys.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_sys.mounts[ifaces[i]].type, " (", ifaces[i], ")"))
end end
if #ifaces == 0 then if #ifaces == 0 then
@ -232,6 +217,7 @@ function ppm.mount_all()
end end
-- mount a particular device -- mount a particular device
---@nodiscard
---@param iface string CC peripheral interface ---@param iface string CC peripheral interface
---@return string|nil type, table|nil device ---@return string|nil type, table|nil device
function ppm.mount(iface) function ppm.mount(iface)
@ -241,10 +227,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_sys.mounts[iface] = peri_init(iface)
pm_type = _ppm_sys.mounts[iface].type pm_type = ppm_sys.mounts[iface].type
pm_dev = _ppm_sys.mounts[iface].dev pm_dev = ppm_sys.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
@ -255,26 +241,27 @@ function ppm.mount(iface)
end end
-- mount a virtual, placeholder device (specifically designed for RTU startup with missing devices) -- mount a virtual, placeholder device (specifically designed for RTU startup with missing devices)
---@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_sys.next_vid
_ppm_sys.mounts[iface] = peri_init("__virtual__") ppm_sys.mounts[iface] = peri_init("__virtual__")
_ppm_sys.next_vid = _ppm_sys.next_vid + 1 ppm_sys.next_vid = ppm_sys.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_sys.mounts[iface].type, ppm_sys.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 side, data in pairs(_ppm_sys.mounts) do for side, data in pairs(ppm_sys.mounts) do
if data.dev == device then if data.dev == device then
log.warning(util.c("PPM: manually unmounted ", data.type, " mounted to ", side)) log.warning(util.c("PPM: manually unmounted ", data.type, " mounted to ", side))
_ppm_sys.mounts[side] = nil ppm_sys.mounts[side] = nil
break break
end end
end end
@ -282,6 +269,7 @@ function ppm.unmount(device)
end end
-- handle peripheral_detach event -- handle peripheral_detach event
---@nodiscard
---@param iface string CC peripheral interface ---@param iface string CC peripheral interface
---@return string|nil type, table|nil device ---@return string|nil type, table|nil device
function ppm.handle_unmount(iface) function ppm.handle_unmount(iface)
@ -289,7 +277,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_sys.mounts[iface]
if lost_dev then if lost_dev then
pm_type = lost_dev.type pm_type = lost_dev.type
@ -300,7 +288,7 @@ 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_sys.mounts[iface] = nil
return pm_type, pm_dev return pm_type, pm_dev
end end
@ -308,23 +296,26 @@ end
-- GENERAL ACCESSORS -- -- GENERAL ACCESSORS --
-- list all available peripherals -- list all available peripherals
---@nodiscard
---@return table names ---@return table names
function ppm.list_avail() function ppm.list_avail()
return peripheral.getNames() return peripheral.getNames()
end end
-- list mounted peripherals -- list mounted peripherals
---@nodiscard
---@return table mounts ---@return table mounts
function ppm.list_mounts() function ppm.list_mounts()
return _ppm_sys.mounts return ppm_sys.mounts
end end
-- get a mounted peripheral side/interface by device table -- get a mounted peripheral side/interface by device table
---@nodiscard
---@param device table device table ---@param device table device table
---@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 side, data in pairs(_ppm_sys.mounts) do for side, data in pairs(ppm_sys.mounts) do
if data.dev == device then return side end if data.dev == device then return side end
end end
end end
@ -333,30 +324,33 @@ function ppm.get_iface(device)
end end
-- get a mounted peripheral by side/interface -- get a mounted peripheral by side/interface
---@nodiscard
---@param iface string CC peripheral interface ---@param iface string CC peripheral interface
---@return table|nil device function table ---@return table|nil device function table
function ppm.get_periph(iface) function ppm.get_periph(iface)
if _ppm_sys.mounts[iface] then if ppm_sys.mounts[iface] then
return _ppm_sys.mounts[iface].dev return ppm_sys.mounts[iface].dev
else return nil end else return nil end
end end
-- get a mounted peripheral type by side/interface -- get a mounted peripheral type by side/interface
---@nodiscard
---@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_sys.mounts[iface] then
return _ppm_sys.mounts[iface].type return ppm_sys.mounts[iface].type
else return nil end else return nil end
end end
-- get all mounted peripherals by type -- get all mounted peripherals by type
---@nodiscard
---@param name string type name ---@param name string type name
---@return table devices device function tables ---@return table devices device function tables
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_sys.mounts) do
if data.type == name then if data.type == name then
table.insert(devices, data.dev) table.insert(devices, data.dev)
end end
@ -366,12 +360,13 @@ function ppm.get_all_devices(name)
end end
-- get a mounted peripheral by type (if multiple, returns the first) -- get a mounted peripheral by type (if multiple, returns the first)
---@nodiscard
---@param name string type name ---@param name string type name
---@return table|nil device function table ---@return table|nil device function table
function ppm.get_device(name) function ppm.get_device(name)
local device = nil local device = nil
for side, data in pairs(_ppm_sys.mounts) do for _, data in pairs(ppm_sys.mounts) do
if data.type == name then if data.type == name then
device = data.dev device = data.dev
break break
@ -384,20 +379,21 @@ end
-- SPECIFIC DEVICE ACCESSORS -- -- SPECIFIC DEVICE ACCESSORS --
-- get the fission reactor (if multiple, returns the first) -- get the fission reactor (if multiple, returns the first)
---@nodiscard
---@return table|nil reactor function table ---@return table|nil reactor function table
function ppm.get_fission_reactor() function ppm.get_fission_reactor()
return ppm.get_device("fissionReactorLogicAdapter") return ppm.get_device("fissionReactorLogicAdapter")
end end
-- get the wireless modem (if multiple, returns the first) -- get the wireless modem (if multiple, returns the first)<br>
--
-- if this is in a CraftOS emulated environment, wired modems will be used instead -- if this is in a CraftOS emulated environment, wired modems will be used instead
---@nodiscard
---@return table|nil modem function table ---@return table|nil modem function table
function ppm.get_wireless_modem() function ppm.get_wireless_modem()
local w_modem = nil local w_modem = nil
local emulated_env = periphemu ~= nil local emulated_env = periphemu ~= nil
for _, device in pairs(_ppm_sys.mounts) do for _, device in pairs(ppm_sys.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_modem = device.dev w_modem = device.dev
break break
@ -408,11 +404,12 @@ function ppm.get_wireless_modem()
end end
-- list all connected monitors -- list all connected monitors
---@nodiscard
---@return table monitors ---@return table monitors
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_sys.mounts) do
if device.type == "monitor" then if device.type == "monitor" then
list[iface] = device list[iface] = device
end end

View File

@ -5,6 +5,7 @@
local psil = {} local psil = {}
-- instantiate a new PSI layer -- instantiate a new PSI layer
---@nodiscard
function psil.create() function psil.create()
local self = { local self = {
ic = {} ic = {}
@ -19,8 +20,7 @@ function psil.create()
---@class psil ---@class psil
local public = {} local public = {}
-- subscribe to a data object in the interconnect -- subscribe to a data object in the interconnect<br>
--
-- will call func() right away if a value is already avaliable -- will call func() right away if a value is already avaliable
---@param key string data key ---@param key string data key
---@param func function function to call on change ---@param func function function to call on change

View File

@ -89,6 +89,7 @@ rsio.IO = IO_PORT
----------------------- -----------------------
-- port to string -- port to string
---@nodiscard
---@param port IO_PORT ---@param port IO_PORT
function rsio.to_string(port) function rsio.to_string(port)
local names = { local names = {
@ -194,6 +195,7 @@ local RS_DIO_MAP = {
} }
-- get the mode of a port -- get the mode of a port
---@nodiscard
---@param port IO_PORT ---@param port IO_PORT
---@return IO_MODE ---@return IO_MODE
function rsio.get_io_mode(port) function rsio.get_io_mode(port)
@ -239,6 +241,7 @@ end
local RS_SIDES = rs.getSides() local RS_SIDES = rs.getSides()
-- check if a port is valid -- check if a port is valid
---@nodiscard
---@param port IO_PORT ---@param port IO_PORT
---@return boolean valid ---@return boolean valid
function rsio.is_valid_port(port) function rsio.is_valid_port(port)
@ -246,6 +249,7 @@ function rsio.is_valid_port(port)
end end
-- check if a side is valid -- check if a side is valid
---@nodiscard
---@param side string ---@param side string
---@return boolean valid ---@return boolean valid
function rsio.is_valid_side(side) function rsio.is_valid_side(side)
@ -258,6 +262,7 @@ function rsio.is_valid_side(side)
end end
-- check if a color is a valid single color -- check if a color is a valid single color
---@nodiscard
---@param color integer ---@param color integer
---@return boolean valid ---@return boolean valid
function rsio.is_color(color) function rsio.is_color(color)
@ -269,22 +274,25 @@ end
----------------- -----------------
-- get digital I/O level reading from a redstone boolean input value -- get digital I/O level reading from a redstone boolean input value
---@param rs_value boolean ---@nodiscard
---@param rs_value boolean raw value from redstone
---@return IO_LVL ---@return IO_LVL
function rsio.digital_read(rs_value) function rsio.digital_read(rs_value)
if rs_value then return IO_LVL.HIGH else return IO_LVL.LOW end if rs_value then return IO_LVL.HIGH else return IO_LVL.LOW end
end end
-- get redstone boolean output value corresponding to a digital I/O level -- get redstone boolean output value corresponding to a digital I/O level
---@param level IO_LVL ---@nodiscard
---@param level IO_LVL logic level
---@return boolean ---@return boolean
function rsio.digital_write(level) function rsio.digital_write(level)
return level == IO_LVL.HIGH return level == IO_LVL.HIGH
end end
-- returns the level corresponding to active -- returns the level corresponding to active
---@param port IO_PORT ---@nodiscard
---@param active boolean ---@param port IO_PORT port (to determine active high/low)
---@param active boolean state to convert to logic level
---@return IO_LVL|false ---@return IO_LVL|false
function rsio.digital_write_active(port, active) function rsio.digital_write_active(port, active)
if (not util.is_int(port)) or (port < IO_PORT.F_ALARM) or (port > IO_PORT.U_EMER_COOL) then if (not util.is_int(port)) or (port < IO_PORT.F_ALARM) or (port > IO_PORT.U_EMER_COOL) then
@ -295,9 +303,10 @@ function rsio.digital_write_active(port, active)
end end
-- returns true if the level corresponds to active -- returns true if the level corresponds to active
---@param port IO_PORT ---@nodiscard
---@param level IO_LVL ---@param port IO_PORT port (to determine active low/high)
---@return boolean|nil ---@param level IO_LVL logic level
---@return boolean|nil state true for active, false for inactive, or nil if invalid port or level provided
function rsio.digital_is_active(port, level) function rsio.digital_is_active(port, level)
if not util.is_int(port) then if not util.is_int(port) then
return nil return nil
@ -313,6 +322,7 @@ end
---------------- ----------------
-- read an analog value scaled from min to max -- read an analog value scaled from min to max
---@nodiscard
---@param rs_value number redstone reading (0 to 15) ---@param rs_value number redstone reading (0 to 15)
---@param min number minimum of range ---@param min number minimum of range
---@param max number maximum of range ---@param max number maximum of range
@ -323,6 +333,7 @@ function rsio.analog_read(rs_value, min, max)
end end
-- write an analog value from the provided scale range -- write an analog value from the provided scale range
---@nodiscard
---@param value number value to write (from min to max range) ---@param value number value to write (from min to max range)
---@param min number minimum of range ---@param min number minimum of range
---@param max number maximum of range ---@param max number maximum of range

View File

@ -19,8 +19,6 @@ function tcallbackdsp.dispatch(time, f)
duration = time, duration = time,
expiry = time + util.time_s() expiry = time + util.time_s()
} }
-- log.debug(util.c("TCD: queued callback for ", f, " [timer: ", timer, "]"))
end end
-- request a function to be called after the specified time, aborting any registered instances of that function reference -- request a function to be called after the specified time, aborting any registered instances of that function reference
@ -45,8 +43,6 @@ function tcallbackdsp.dispatch_unique(time, f)
duration = time, duration = time,
expiry = time + util.time_s() expiry = time + util.time_s()
} }
-- log.debug(util.c("TCD: queued callback for ", f, " [timer: ", timer, "]"))
end end
-- abort a requested callback -- abort a requested callback
@ -72,8 +68,7 @@ function tcallbackdsp.handle(event)
end end
end end
-- identify any overdo callbacks -- identify any overdo callbacks<br>
--
-- prints to log debug output -- prints to log debug output
function tcallbackdsp.diagnostics() function tcallbackdsp.diagnostics()
for timer, entry in pairs(registry) do for timer, entry in pairs(registry) do

View File

@ -12,12 +12,14 @@ local types = {}
---@field amount integer ---@field amount integer
-- create a new tank fluid -- create a new tank fluid
---@nodiscard
---@param n string name ---@param n string name
---@param a integer amount ---@param a integer amount
---@return radiation_reading ---@return radiation_reading
function types.new_tank_fluid(n, a) return { name = n, amount = a } end function types.new_tank_fluid(n, a) return { name = n, amount = a } end
-- create a new empty tank fluid -- create a new empty tank fluid
---@nodiscard
---@return tank_fluid ---@return tank_fluid
function types.new_empty_gas() return { type = "mekanism:empty_gas", amount = 0 } end function types.new_empty_gas() return { type = "mekanism:empty_gas", amount = 0 } end
@ -26,12 +28,14 @@ function types.new_empty_gas() return { type = "mekanism:empty_gas", amount = 0
---@field unit string ---@field unit string
-- create a new radiation reading -- create a new radiation reading
---@nodiscard
---@param r number radiaiton level ---@param r number radiaiton level
---@param u string radiation unit ---@param u string radiation unit
---@return radiation_reading ---@return radiation_reading
function types.new_radiation_reading(r, u) return { radiation = r, unit = u } end function types.new_radiation_reading(r, u) return { radiation = r, unit = u } end
-- create a new zeroed radiation reading -- create a new zeroed radiation reading
---@nodiscard
---@return radiation_reading ---@return radiation_reading
function types.new_zero_radiation_reading() return { radiation = 0, unit = "nSv" } end function types.new_zero_radiation_reading() return { radiation = 0, unit = "nSv" } end
@ -41,6 +45,7 @@ function types.new_zero_radiation_reading() return { radiation = 0, unit = "nSv"
---@field z integer ---@field z integer
-- create a new coordinate -- create a new coordinate
---@nodiscard
---@param x integer ---@param x integer
---@param y integer ---@param y integer
---@param z integer ---@param z integer
@ -48,11 +53,12 @@ function types.new_zero_radiation_reading() return { radiation = 0, unit = "nSv"
function types.new_coordinate(x, y, z) return { x = x, y = y, z = z } end function types.new_coordinate(x, y, z) return { x = x, y = y, z = z } end
-- create a new zero coordinate -- create a new zero coordinate
---@nodiscard
---@return coordinate ---@return coordinate
function types.new_zero_coordinate() return { x = 0, y = 0, z = 0 } end function types.new_zero_coordinate() return { x = 0, y = 0, z = 0 } end
---@class rtu_advertisement ---@class rtu_advertisement
---@field type integer ---@field type RTU_UNIT_TYPES
---@field index integer ---@field index integer
---@field reactor integer ---@field reactor integer
---@field rsio table|nil ---@field rsio table|nil
@ -62,15 +68,16 @@ function types.new_zero_coordinate() return { x = 0, y = 0, z = 0 } end
---@alias color integer ---@alias color integer
-- ENUMERATION TYPES -- -- ENUMERATION TYPES --
--#region
---@alias TRI_FAIL integer ---@enum TRI_FAIL
types.TRI_FAIL = { types.TRI_FAIL = {
OK = 0, OK = 0,
PARTIAL = 1, PARTIAL = 1,
FULL = 2 FULL = 2
} }
---@alias PROCESS integer ---@enum PROCESS
types.PROCESS = { types.PROCESS = {
INACTIVE = 0, INACTIVE = 0,
MAX_BURN = 1, MAX_BURN = 1,
@ -93,7 +100,7 @@ types.PROCESS_NAMES = {
"GEN_RATE_FAULT_IDLE" "GEN_RATE_FAULT_IDLE"
} }
---@alias WASTE_MODE integer ---@enum WASTE_MODE
types.WASTE_MODE = { types.WASTE_MODE = {
AUTO = 1, AUTO = 1,
PLUTONIUM = 2, PLUTONIUM = 2,
@ -101,7 +108,7 @@ types.WASTE_MODE = {
ANTI_MATTER = 4 ANTI_MATTER = 4
} }
---@alias ALARM integer ---@enum ALARM
types.ALARM = { types.ALARM = {
ContainmentBreach = 1, ContainmentBreach = 1,
ContainmentRadiation = 2, ContainmentRadiation = 2,
@ -117,7 +124,7 @@ types.ALARM = {
TurbineTrip = 12 TurbineTrip = 12
} }
types.alarm_string = { types.ALARM_NAMES = {
"ContainmentBreach", "ContainmentBreach",
"ContainmentRadiation", "ContainmentRadiation",
"ReactorLost", "ReactorLost",
@ -132,7 +139,7 @@ types.alarm_string = {
"TurbineTrip" "TurbineTrip"
} }
---@alias ALARM_PRIORITY integer ---@enum ALARM_PRIORITY
types.ALARM_PRIORITY = { types.ALARM_PRIORITY = {
CRITICAL = 0, CRITICAL = 0,
EMERGENCY = 1, EMERGENCY = 1,
@ -140,30 +147,14 @@ types.ALARM_PRIORITY = {
TIMELY = 3 TIMELY = 3
} }
types.alarm_prio_string = { types.ALARM_PRIORITY_NAMES = {
"CRITICAL", "CRITICAL",
"EMERGENCY", "EMERGENCY",
"URGENT", "URGENT",
"TIMELY" "TIMELY"
} }
-- map alarms to alarm priority ---@enum ALARM_STATE
types.ALARM_PRIO_MAP = {
types.ALARM_PRIORITY.CRITICAL,
types.ALARM_PRIORITY.CRITICAL,
types.ALARM_PRIORITY.URGENT,
types.ALARM_PRIORITY.CRITICAL,
types.ALARM_PRIORITY.EMERGENCY,
types.ALARM_PRIORITY.EMERGENCY,
types.ALARM_PRIORITY.TIMELY,
types.ALARM_PRIORITY.EMERGENCY,
types.ALARM_PRIORITY.TIMELY,
types.ALARM_PRIORITY.URGENT,
types.ALARM_PRIORITY.TIMELY,
types.ALARM_PRIORITY.URGENT
}
---@alias ALARM_STATE integer
types.ALARM_STATE = { types.ALARM_STATE = {
INACTIVE = 0, INACTIVE = 0,
TRIPPED = 1, TRIPPED = 1,
@ -171,7 +162,10 @@ types.ALARM_STATE = {
RING_BACK = 3 RING_BACK = 3
} }
--#endregion
-- STRING TYPES -- -- STRING TYPES --
--#region
---@alias os_event ---@alias os_event
---| "alarm" ---| "alarm"
@ -206,21 +200,7 @@ types.ALARM_STATE = {
---| "websocket_failure" ---| "websocket_failure"
---| "websocket_message" ---| "websocket_message"
---| "websocket_success" ---| "websocket_success"
---| "clock_start" custom, added for reactor PLC
---@alias rps_trip_cause
---| "ok"
---| "dmg_crit"
---| "high_temp"
---| "no_coolant"
---| "full_waste"
---| "heated_coolant_backup"
---| "no_fuel"
---| "fault"
---| "timeout"
---| "manual"
---| "automatic"
---| "sys_fail"
---| "force_disabled"
---@alias fluid ---@alias fluid
---| "mekanism:empty_gas" ---| "mekanism:empty_gas"
@ -246,6 +226,21 @@ types.rtu_t = {
env_detector = "environment_detector" env_detector = "environment_detector"
} }
---@alias rps_trip_cause
---| "ok"
---| "dmg_crit"
---| "high_temp"
---| "no_coolant"
---| "full_waste"
---| "heated_coolant_backup"
---| "no_fuel"
---| "fault"
---| "timeout"
---| "manual"
---| "automatic"
---| "sys_fail"
---| "force_disabled"
---@alias rps_status_t rps_trip_cause ---@alias rps_status_t rps_trip_cause
types.rps_status_t = { types.rps_status_t = {
ok = "ok", ok = "ok",
@ -263,18 +258,24 @@ types.rps_status_t = {
force_disabled = "force_disabled" force_disabled = "force_disabled"
} }
-- turbine steam dumping modes ---@alias DUMPING_MODE
---@alias DUMPING_MODE string ---| "IDLE"
---| "DUMPING"
---| "DUMPING_EXCESS"
types.DUMPING_MODE = { types.DUMPING_MODE = {
IDLE = "IDLE", IDLE = "IDLE",
DUMPING = "DUMPING", DUMPING = "DUMPING",
DUMPING_EXCESS = "DUMPING_EXCESS" DUMPING_EXCESS = "DUMPING_EXCESS"
} }
-- MODBUS --#endregion
-- modbus function codes -- MODBUS --
---@alias MODBUS_FCODE integer --#region
-- MODBUS function codes
---@enum MODBUS_FCODE
types.MODBUS_FCODE = { types.MODBUS_FCODE = {
READ_COILS = 0x01, READ_COILS = 0x01,
READ_DISCRETE_INPUTS = 0x02, READ_DISCRETE_INPUTS = 0x02,
@ -287,8 +288,8 @@ types.MODBUS_FCODE = {
ERROR_FLAG = 0x80 ERROR_FLAG = 0x80
} }
-- modbus exception codes -- MODBUS exception codes
---@alias MODBUS_EXCODE integer ---@enum MODBUS_EXCODE
types.MODBUS_EXCODE = { types.MODBUS_EXCODE = {
ILLEGAL_FUNCTION = 0x01, ILLEGAL_FUNCTION = 0x01,
ILLEGAL_DATA_ADDR = 0x02, ILLEGAL_DATA_ADDR = 0x02,
@ -302,4 +303,6 @@ types.MODBUS_EXCODE = {
GATEWAY_TARGET_TIMEOUT = 0x0B GATEWAY_TARGET_TIMEOUT = 0x0B
} }
--#endregion
return types return types

View File

@ -14,6 +14,7 @@ util.TICK_TIME_MS = 50
--#region --#region
-- trinary operator -- trinary operator
---@nodiscard
---@param cond boolean|nil condition ---@param cond boolean|nil condition
---@param a any return if true ---@param a any return if true
---@param b any return if false ---@param b any return if false
@ -57,6 +58,7 @@ end
--#region --#region
-- get a value as a string -- get a value as a string
---@nodiscard
---@param val any ---@param val any
---@return string ---@return string
function util.strval(val) function util.strval(val)
@ -69,6 +71,7 @@ function util.strval(val)
end end
-- repeat a string n times -- repeat a string n times
---@nodiscard
---@param str string ---@param str string
---@param n integer ---@param n integer
---@return string ---@return string
@ -81,6 +84,7 @@ function util.strrep(str, n)
end end
-- repeat a space n times -- repeat a space n times
---@nodiscard
---@param n integer ---@param n integer
---@return string ---@return string
function util.spaces(n) function util.spaces(n)
@ -88,6 +92,7 @@ function util.spaces(n)
end end
-- pad text to a minimum width -- pad text to a minimum width
---@nodiscard
---@param str string text ---@param str string text
---@param n integer minimum width ---@param n integer minimum width
---@return string ---@return string
@ -100,6 +105,7 @@ function util.pad(str, n)
end end
-- wrap a string into a table of lines, supporting single dash splits -- wrap a string into a table of lines, supporting single dash splits
---@nodiscard
---@param str string ---@param str string
---@param limit integer line limit ---@param limit integer line limit
---@return table lines ---@return table lines
@ -147,13 +153,12 @@ function util.strwrap(str, limit)
end end
-- concatenation with built-in to string -- concatenation with built-in to string
---@nodiscard
---@vararg any ---@vararg any
---@return string ---@return string
function util.concat(...) function util.concat(...)
local str = "" local str = ""
for _, v in ipairs(arg) do for _, v in ipairs(arg) do str = str .. util.strval(v) end
str = str .. util.strval(v)
end
return str return str
end end
@ -161,15 +166,16 @@ end
util.c = util.concat util.c = util.concat
-- sprintf implementation -- sprintf implementation
---@nodiscard
---@param format string ---@param format string
---@vararg any ---@vararg any
function util.sprintf(format, ...) function util.sprintf(format, ...)
return string.format(format, table.unpack(arg)) return string.format(format, table.unpack(arg))
end end
-- format a number string with commas as the thousands separator -- format a number string with commas as the thousands separator<br>
--
-- subtracts from spaces at the start if present for each comma used -- subtracts from spaces at the start if present for each comma used
---@nodiscard
---@param num string number string ---@param num string number string
---@return string ---@return string
function util.comma_format(num) function util.comma_format(num)
@ -196,6 +202,7 @@ end
--#region --#region
-- is a value an integer -- is a value an integer
---@nodiscard
---@param x any value ---@param x any value
---@return boolean is_integer if the number is an integer ---@return boolean is_integer if the number is an integer
function util.is_int(x) function util.is_int(x)
@ -203,6 +210,7 @@ function util.is_int(x)
end end
-- get the sign of a number -- get the sign of a number
---@nodiscard
---@param x number value ---@param x number value
---@return integer sign (-1 for < 0, 1 otherwise) ---@return integer sign (-1 for < 0, 1 otherwise)
function util.sign(x) function util.sign(x)
@ -210,12 +218,14 @@ function util.sign(x)
end end
-- round a number to an integer -- round a number to an integer
---@nodiscard
---@return integer rounded ---@return integer rounded
function util.round(x) function util.round(x)
return math.floor(x + 0.5) return math.floor(x + 0.5)
end end
-- get a new moving average object -- get a new moving average object
---@nodiscard
---@param length integer history length ---@param length integer history length
---@param default number value to fill history with for first call to compute() ---@param default number value to fill history with for first call to compute()
function util.mov_avg(length, default) function util.mov_avg(length, default)
@ -249,6 +259,7 @@ function util.mov_avg(length, default)
end end
-- compute the moving average -- compute the moving average
---@nodiscard
---@return number average ---@return number average
function public.compute() function public.compute()
local sum = 0 local sum = 0
@ -264,6 +275,7 @@ end
-- TIME -- -- TIME --
-- current time -- current time
---@nodiscard
---@return integer milliseconds ---@return integer milliseconds
function util.time_ms() function util.time_ms()
---@diagnostic disable-next-line: undefined-field ---@diagnostic disable-next-line: undefined-field
@ -271,6 +283,7 @@ function util.time_ms()
end end
-- current time -- current time
---@nodiscard
---@return number seconds ---@return number seconds
function util.time_s() function util.time_s()
---@diagnostic disable-next-line: undefined-field ---@diagnostic disable-next-line: undefined-field
@ -278,10 +291,9 @@ function util.time_s()
end end
-- current time -- current time
---@nodiscard
---@return integer milliseconds ---@return integer milliseconds
function util.time() function util.time() return util.time_ms() end
return util.time_ms()
end
--#endregion --#endregion
@ -289,6 +301,7 @@ end
--#region --#region
-- OS pull event raw wrapper with types -- OS pull event raw wrapper with types
---@nodiscard
---@param target_event? string event to wait for ---@param target_event? string event to wait for
---@return os_event event, any param1, any param2, any param3, any param4, any param5 ---@return os_event event, any param1, any param2, any param3, any param4, any param5
function util.pull_event(target_event) function util.pull_event(target_event)
@ -309,6 +322,7 @@ function util.push_event(event, param1, param2, param3, param4, param5)
end end
-- start an OS timer -- start an OS timer
---@nodiscard
---@param t number timer duration in seconds ---@param t number timer duration in seconds
---@return integer timer ID ---@return integer timer ID
function util.start_timer(t) function util.start_timer(t)
@ -336,14 +350,12 @@ function util.psleep(t)
pcall(os.sleep, t) pcall(os.sleep, t)
end end
-- no-op to provide a brief pause (1 tick) to yield -- no-op to provide a brief pause (1 tick) to yield<br>
---
--- EVENT_CONSUMER: this function consumes events --- EVENT_CONSUMER: this function consumes events
function util.nop() function util.nop() util.psleep(0.05) end
util.psleep(0.05)
end
-- attempt to maintain a minimum loop timing (duration of execution) -- attempt to maintain a minimum loop timing (duration of execution)
---@nodiscard
---@param target_timing integer minimum amount of milliseconds to wait for ---@param target_timing integer minimum amount of milliseconds to wait for
---@param last_update integer millisecond time of last update ---@param last_update integer millisecond time of last update
---@return integer time_now ---@return integer time_now
@ -351,9 +363,7 @@ end
function util.adaptive_delay(target_timing, last_update) function util.adaptive_delay(target_timing, last_update)
local sleep_for = target_timing - (util.time() - last_update) local sleep_for = target_timing - (util.time() - last_update)
-- only if >50ms since worker loops already yield 0.05s -- only if >50ms since worker loops already yield 0.05s
if sleep_for >= 50 then if sleep_for >= 50 then util.psleep(sleep_for / 1000.0) end
util.psleep(sleep_for / 1000.0)
end
return util.time() return util.time()
end end
@ -362,8 +372,7 @@ end
-- TABLE UTILITIES -- -- TABLE UTILITIES --
--#region --#region
-- delete elements from a table if the passed function returns false when passed a table element -- delete elements from a table if the passed function returns false when passed a table element<br>
--
-- put briefly: deletes elements that return false, keeps elements that return true -- put briefly: deletes elements that return false, keeps elements that return true
---@param t table table to remove elements from ---@param t table table to remove elements from
---@param f function should return false to delete an element when passed the element: f(elem) = true|false ---@param f function should return false to delete an element when passed the element: f(elem) = true|false
@ -388,6 +397,7 @@ function util.filter_table(t, f, on_delete)
end end
-- check if a table contains the provided element -- check if a table contains the provided element
---@nodiscard
---@param t table table to check ---@param t table table to check
---@param element any element to check for ---@param element any element to check for
function util.table_contains(t, element) function util.table_contains(t, element)
@ -404,11 +414,13 @@ end
--#region --#region
-- convert Joules to FE -- convert Joules to FE
---@nodiscard
---@param J number Joules ---@param J number Joules
---@return number FE Forge Energy ---@return number FE Forge Energy
function util.joules_to_fe(J) return (J * 0.4) end function util.joules_to_fe(J) return (J * 0.4) end
-- convert FE to Joules -- convert FE to Joules
---@nodiscard
---@param FE number Forge Energy ---@param FE number Forge Energy
---@return number J Joules ---@return number J Joules
function util.fe_to_joules(FE) return (FE * 2.5) end function util.fe_to_joules(FE) return (FE * 2.5) end
@ -418,10 +430,11 @@ local function MFE(fe) return fe / 1000000.0 end
local function GFE(fe) return fe / 1000000000.0 end local function GFE(fe) return fe / 1000000000.0 end
local function TFE(fe) return fe / 1000000000000.0 end local function TFE(fe) return fe / 1000000000000.0 end
local function PFE(fe) return fe / 1000000000000000.0 end local function PFE(fe) return fe / 1000000000000000.0 end
local function EFE(fe) return fe / 1000000000000000000.0 end -- if you accomplish this please touch grass local function EFE(fe) return fe / 1000000000000000000.0 end -- if you accomplish this please touch grass
local function ZFE(fe) return fe / 1000000000000000000000.0 end -- please stop local function ZFE(fe) return fe / 1000000000000000000000.0 end -- please stop
-- format a power value into XXX.XX UNIT format (FE, kFE, MFE, GFE, TFE, PFE, EFE, ZFE) -- format a power value into XXX.XX UNIT format (FE, kFE, MFE, GFE, TFE, PFE, EFE, ZFE)
---@nodiscard
---@param fe number forge energy value ---@param fe number forge energy value
---@param combine_label? boolean if a label should be included in the string itself ---@param combine_label? boolean if a label should be included in the string itself
---@param format? string format override ---@param format? string format override
@ -430,9 +443,7 @@ function util.power_format(fe, combine_label, format)
local unit local unit
local value local value
if type(format) ~= "string" then if type(format) ~= "string" then format = "%.2f" end
format = "%.2f"
end
if fe < 1000.0 then if fe < 1000.0 then
unit = "FE" unit = "FE"
@ -474,10 +485,10 @@ end
-- WATCHDOG -- -- WATCHDOG --
-- ComputerCraft OS Timer based Watchdog -- OS timer based watchdog<br>
-- triggers a timer event if not fed within 'timeout' seconds
---@nodiscard
---@param timeout number timeout duration ---@param timeout number timeout duration
---
--- triggers a timer event if not fed within 'timeout' seconds
function util.new_watchdog(timeout) function util.new_watchdog(timeout)
local self = { local self = {
timeout = timeout, timeout = timeout,
@ -487,10 +498,10 @@ function util.new_watchdog(timeout)
---@class watchdog ---@class watchdog
local public = {} local public = {}
-- check if a timer is this watchdog
---@nodiscard
---@param timer number timer event timer ID ---@param timer number timer event timer ID
function public.is_timer(timer) function public.is_timer(timer) return self.wd_timer == timer end
return self.wd_timer == timer
end
-- satiate the beast -- satiate the beast
function public.feed() function public.feed()
@ -512,10 +523,10 @@ end
-- LOOP CLOCK -- -- LOOP CLOCK --
-- ComputerCraft OS Timer based Loop Clock -- OS timer based loop clock<br>
-- fires a timer event at the specified period, does not start at construct time
---@nodiscard
---@param period number clock period ---@param period number clock period
---
--- fires a timer event at the specified period, does not start at construct time
function util.new_clock(period) function util.new_clock(period)
local self = { local self = {
period = period, period = period,
@ -525,24 +536,22 @@ function util.new_clock(period)
---@class clock ---@class clock
local public = {} local public = {}
-- check if a timer is this clock
---@nodiscard
---@param timer number timer event timer ID ---@param timer number timer event timer ID
function public.is_clock(timer) function public.is_clock(timer) return self.timer == timer end
return self.timer == timer
end
-- start the clock -- start the clock
function public.start() function public.start() self.timer = util.start_timer(self.period) end
self.timer = util.start_timer(self.period)
end
return public return public
end end
-- FIELD VALIDATOR -- -- FIELD VALIDATOR --
-- create a new type validator -- create a new type validator<br>
--
-- can execute sequential checks and check valid() to see if it is still valid -- can execute sequential checks and check valid() to see if it is still valid
---@nodiscard
function util.new_validator() function util.new_validator()
local valid = true local valid = true
@ -565,6 +574,8 @@ function util.new_validator()
function public.assert_port(port) valid = valid and type(port) == "number" and port >= 0 and port <= 65535 end function public.assert_port(port) valid = valid and type(port) == "number" and port >= 0 and port <= 65535 end
-- check if all assertions passed successfully
---@nodiscard
function public.valid() return valid end function public.valid() return valid end
return public return public

View File

@ -367,8 +367,8 @@ local function _update_alarm_state(self, tripped, alarm)
else else
alarm.state = AISTATE.TRIPPED alarm.state = AISTATE.TRIPPED
self.db.alarm_states[alarm.id] = ALARM_STATE.TRIPPED self.db.alarm_states[alarm.id] = ALARM_STATE.TRIPPED
log.info(util.c("UNIT ", self.r_id, " ALARM ", alarm.id, " (", types.alarm_string[alarm.id], "): TRIPPED [PRIORITY ", log.info(util.c("UNIT ", self.r_id, " ALARM ", alarm.id, " (", types.ALARM_NAMES[alarm.id], "): TRIPPED [PRIORITY ",
types.alarm_prio_string[alarm.tier + 1],"]")) types.ALARM_PRIORITY_NAMES[alarm.tier + 1],"]"))
end end
else else
alarm.trip_time = util.time_ms() alarm.trip_time = util.time_ms()
@ -381,8 +381,8 @@ local function _update_alarm_state(self, tripped, alarm)
if elapsed > (alarm.hold_time * 1000) then if elapsed > (alarm.hold_time * 1000) then
alarm.state = AISTATE.TRIPPED alarm.state = AISTATE.TRIPPED
self.db.alarm_states[alarm.id] = ALARM_STATE.TRIPPED self.db.alarm_states[alarm.id] = ALARM_STATE.TRIPPED
log.info(util.c("UNIT ", self.r_id, " ALARM ", alarm.id, " (", types.alarm_string[alarm.id], "): TRIPPED [PRIORITY ", log.info(util.c("UNIT ", self.r_id, " ALARM ", alarm.id, " (", types.ALARM_NAMES[alarm.id], "): TRIPPED [PRIORITY ",
types.alarm_prio_string[alarm.tier + 1],"]")) types.ALARM_PRIORITY_NAMES[alarm.tier + 1],"]"))
end end
elseif int_state == AISTATE.RING_BACK_TRIPPING then elseif int_state == AISTATE.RING_BACK_TRIPPING then
alarm.trip_time = 0 alarm.trip_time = 0
@ -432,7 +432,7 @@ local function _update_alarm_state(self, tripped, alarm)
-- check for state change -- check for state change
if alarm.state ~= int_state then if alarm.state ~= int_state then
local change_str = util.c(aistate_string[int_state + 1], " -> ", aistate_string[alarm.state + 1]) local change_str = util.c(aistate_string[int_state + 1], " -> ", aistate_string[alarm.state + 1])
log.debug(util.c("UNIT ", self.r_id, " ALARM ", alarm.id, " (", types.alarm_string[alarm.id], "): ", change_str)) log.debug(util.c("UNIT ", self.r_id, " ALARM ", alarm.id, " (", types.ALARM_NAMES[alarm.id], "): ", change_str))
end end
end end
@ -530,8 +530,8 @@ function logic.update_auto_safety(public, self)
for _, alarm in pairs(self.alarms) do for _, alarm in pairs(self.alarms) do
if alarm.tier <= PRIO.URGENT and (alarm.state == AISTATE.TRIPPED or alarm.state == AISTATE.ACKED) then if alarm.tier <= PRIO.URGENT and (alarm.state == AISTATE.TRIPPED or alarm.state == AISTATE.ACKED) then
if not self.auto_was_alarmed then if not self.auto_was_alarmed then
log.info(util.c("UNIT ", self.r_id, " AUTO SCRAM due to ALARM ", alarm.id, " (", types.alarm_string[alarm.id], ") [PRIORITY ", log.info(util.c("UNIT ", self.r_id, " AUTO SCRAM due to ALARM ", alarm.id, " (", types.ALARM_NAMES[alarm.id], ") [PRIORITY ",
types.alarm_prio_string[alarm.tier + 1],"]")) types.ALARM_PRIORITY_NAMES[alarm.tier + 1],"]"))
end end
alarmed = true alarmed = true