#47 scada-common doc comments

This commit is contained in:
Mikayla Fischler 2022-05-10 17:06:27 -04:00
parent 6e1ece8183
commit 3c688bfafa
8 changed files with 288 additions and 151 deletions

View File

@ -1,7 +1,9 @@
local util = require("scada-common.util")
---@class alarm
local alarm = {}
---@alias SEVERITY integer
SEVERITY = {
INFO = 0, -- basic info message
WARNING = 1, -- warning about some abnormal state
@ -13,6 +15,8 @@ SEVERITY = {
alarm.SEVERITY = SEVERITY
-- severity integer to string
---@param severity SEVERITY
alarm.severity_to_string = function (severity)
if severity == SEVERITY.INFO then
return "INFO"
@ -31,6 +35,10 @@ alarm.severity_to_string = function (severity)
end
end
-- create a new scada alarm entry
---@param severity SEVERITY
---@param device string
---@param message string
alarm.scada_alarm = function (severity, device, message)
local self = {
time = util.time(),
@ -40,11 +48,17 @@ alarm.scada_alarm = function (severity, device, message)
message = message
}
local format = function ()
---@class scada_alarm
local public = {}
-- format the alarm as a string
---@return string message
public.format = function ()
return self.ts_string .. " [" .. alarm.severity_to_string(self.severity) .. "] (" .. self.device ") >> " .. self.message
end
local properties = function ()
-- get alarm properties
public.properties = function ()
return {
time = self.time,
severity = self.severity,
@ -53,10 +67,7 @@ alarm.scada_alarm = function (severity, device, message)
}
end
return {
format = format,
properties = properties
}
return public
end
return alarm

View File

@ -5,11 +5,13 @@
local log = require("scada-common.log")
local types = require("scada-common.types")
---@class comms
local comms = {}
local rtu_t = types.rtu_t
local insert = table.insert
---@alias PROTOCOLS integer
local PROTOCOLS = {
MODBUS_TCP = 0, -- our "MODBUS TCP"-esque protocol
RPLC = 1, -- reactor PLC protocol
@ -18,6 +20,7 @@ local PROTOCOLS = {
COORD_API = 4 -- data/control packets for pocket computers to/from coordinators
}
---@alias RPLC_TYPES integer
local RPLC_TYPES = {
LINK_REQ = 0, -- linking requests
STATUS = 1, -- reactor/system status
@ -30,12 +33,14 @@ local RPLC_TYPES = {
RPS_RESET = 8 -- clear RPS trip (if in bad state, will trip immediately)
}
---@alias RPLC_LINKING integer
local RPLC_LINKING = {
ALLOW = 0, -- link approved
DENY = 1, -- link denied
COLLISION = 2 -- link denied due to existing active link
}
---@alias SCADA_MGMT_TYPES integer
local SCADA_MGMT_TYPES = {
KEEP_ALIVE = 0, -- keep alive packet w/ RTT
CLOSE = 1, -- close a connection
@ -43,6 +48,7 @@ local SCADA_MGMT_TYPES = {
REMOTE_LINKED = 3 -- remote device linked
}
---@alias RTU_ADVERT_TYPES integer
local RTU_ADVERT_TYPES = {
REDSTONE = 0, -- redstone I/O
BOILER = 1, -- boiler
@ -71,8 +77,14 @@ comms.scada_packet = function ()
payload = nil
}
---@class scada_packet
local public = {}
-- make a SCADA packet
local make = function (seq_num, protocol, payload)
---@param seq_num integer
---@param protocol PROTOCOLS
---@param payload table
public.make = function (seq_num, protocol, payload)
self.valid = true
self.seq_num = seq_num
self.protocol = protocol
@ -82,7 +94,12 @@ comms.scada_packet = function ()
end
-- parse in a modem message as a SCADA packet
local receive = function (side, sender, reply_to, message, distance)
---@param side string
---@param sender integer
---@param reply_to integer
---@param message any
---@param distance integer
public.receive = function (side, sender, reply_to, message, distance)
self.modem_msg_in = {
iface = side,
s_port = sender,
@ -108,40 +125,23 @@ comms.scada_packet = function ()
-- public accessors --
local modem_event = function () return self.modem_msg_in end
local raw_sendable = function () return self.raw end
public.modem_event = function () return self.modem_msg_in end
public.raw_sendable = function () return self.raw end
local local_port = function () return self.modem_msg_in.s_port end
local remote_port = function () return self.modem_msg_in.r_port end
public.local_port = function () return self.modem_msg_in.s_port end
public.remote_port = function () return self.modem_msg_in.r_port end
local is_valid = function () return self.valid end
public.is_valid = function () return self.valid end
local seq_num = function () return self.seq_num end
local protocol = function () return self.protocol end
local length = function () return self.length end
local data = function () return self.payload end
public.seq_num = function () return self.seq_num end
public.protocol = function () return self.protocol end
public.length = function () return self.length end
public.data = function () return self.payload end
return {
-- construct
make = make,
receive = receive,
-- raw access
modem_event = modem_event,
raw_sendable = raw_sendable,
-- ports
local_port = local_port,
remote_port = remote_port,
-- well-formed
is_valid = is_valid,
-- packet properties
seq_num = seq_num,
protocol = protocol,
length = length,
data = data
}
return public
end
-- MODBUS packet
-- MODBUS packet
-- modeled after MODBUS TCP packet
comms.modbus_packet = function ()
local self = {
@ -154,8 +154,15 @@ comms.modbus_packet = function ()
data = nil
}
---@class modbus_packet
local public = {}
-- make a MODBUS packet
local make = function (txn_id, unit_id, func_code, data)
---@param txn_id integer
---@param unit_id integer
---@param func_code MODBUS_FCODE
---@param data table
public.make = function (txn_id, unit_id, func_code, data)
self.txn_id = txn_id
self.length = #data
self.unit_id = unit_id
@ -170,18 +177,20 @@ comms.modbus_packet = function ()
end
-- decode a MODBUS packet from a SCADA frame
local decode = function (frame)
---@param frame scada_packet
---@return boolean success
public.decode = function (frame)
if frame then
self.frame = frame
if frame.protocol() == PROTOCOLS.MODBUS_TCP then
local size_ok = frame.length() >= 3
if size_ok then
local data = frame.data()
make(data[1], data[2], data[3], { table.unpack(data, 4, #data) })
public.make(data[1], data[2], data[3], { table.unpack(data, 4, #data) })
end
return size_ok
else
log.debug("attempted MODBUS_TCP parse of incorrect protocol " .. frame.protocol(), true)
@ -194,10 +203,10 @@ comms.modbus_packet = function ()
end
-- get raw to send
local raw_sendable = function () return self.raw end
public.raw_sendable = function () return self.raw end
-- get this packet
local get = function ()
public.get = function ()
return {
scada_frame = self.frame,
txn_id = self.txn_id,
@ -208,15 +217,7 @@ comms.modbus_packet = function ()
}
end
return {
-- construct
make = make,
decode = decode,
-- raw access
raw_sendable = raw_sendable,
-- formatted access
get = get
}
return public
end
-- reactor PLC packet
@ -230,10 +231,12 @@ comms.rplc_packet = function ()
body = nil
}
---@class rplc_packet
local public = {}
-- check that type is known
local _rplc_type_valid = function ()
return self.type == RPLC_TYPES.KEEP_ALIVE or
self.type == RPLC_TYPES.LINK_REQ or
return self.type == RPLC_TYPES.LINK_REQ or
self.type == RPLC_TYPES.STATUS or
self.type == RPLC_TYPES.MEK_STRUCT or
self.type == RPLC_TYPES.MEK_BURN_RATE or
@ -245,7 +248,10 @@ comms.rplc_packet = function ()
end
-- make an RPLC packet
local make = function (id, packet_type, data)
---@param id integer
---@param packet_type RPLC_TYPES
---@param data table
public.make = function (id, packet_type, data)
-- packet accessor properties
self.id = id
self.type = packet_type
@ -260,7 +266,9 @@ comms.rplc_packet = function ()
end
-- decode an RPLC packet from a SCADA frame
local decode = function (frame)
---@param frame scada_packet
---@return boolean success
public.decode = function (frame)
if frame then
self.frame = frame
@ -269,7 +277,7 @@ comms.rplc_packet = function ()
if ok then
local data = frame.data()
make(data[1], data[2], { table.unpack(data, 3, #data) })
public.make(data[1], data[2], { table.unpack(data, 3, #data) })
ok = _rplc_type_valid()
end
@ -285,10 +293,10 @@ comms.rplc_packet = function ()
end
-- get raw to send
local raw_sendable = function () return self.raw end
public.raw_sendable = function () return self.raw end
-- get this packet
local get = function ()
public.get = function ()
return {
scada_frame = self.frame,
id = self.id,
@ -298,15 +306,7 @@ comms.rplc_packet = function ()
}
end
return {
-- construct
make = make,
decode = decode,
-- raw access
raw_sendable = raw_sendable,
-- formatted access
get = get
}
return public
end
-- SCADA management packet
@ -319,17 +319,21 @@ comms.mgmt_packet = function ()
data = nil
}
---@class mgmt_packet
local public = {}
-- check that type is known
local _scada_type_valid = function ()
return self.type == SCADA_MGMT_TYPES.PING or
return self.type == SCADA_MGMT_TYPES.KEEP_ALIVE or
self.type == SCADA_MGMT_TYPES.CLOSE or
self.type == SCADA_MGMT_TYPES.REMOTE_LINKED or
self.type == SCADA_MGMT_TYPES.RTU_ADVERT or
self.type == SCADA_MGMT_TYPES.RTU_HEARTBEAT
self.type == SCADA_MGMT_TYPES.RTU_ADVERT
end
-- make a SCADA management packet
local make = function (packet_type, data)
---@param packet_type SCADA_MGMT_TYPES
---@param data table
public.make = function (packet_type, data)
-- packet accessor properties
self.type = packet_type
self.length = #data
@ -343,19 +347,21 @@ comms.mgmt_packet = function ()
end
-- decode a SCADA management packet from a SCADA frame
local decode = function (frame)
---@param frame scada_packet
---@return boolean success
public.decode = function (frame)
if frame then
self.frame = frame
if frame.protocol() == PROTOCOLS.SCADA_MGMT then
local ok = frame.length() >= 1
if ok then
local data = frame.data()
make(data[1], { table.unpack(data, 2, #data) })
public.make(data[1], { table.unpack(data, 2, #data) })
ok = _scada_type_valid()
end
return ok
else
log.debug("attempted SCADA_MGMT parse of incorrect protocol " .. frame.protocol(), true)
@ -368,10 +374,10 @@ comms.mgmt_packet = function ()
end
-- get raw to send
local raw_sendable = function () return self.raw end
public.raw_sendable = function () return self.raw end
-- get this packet
local get = function ()
public.get = function ()
return {
scada_frame = self.frame,
type = self.type,
@ -380,15 +386,7 @@ comms.mgmt_packet = function ()
}
end
return {
-- construct
make = make,
decode = decode,
-- raw access
raw_sendable = raw_sendable,
-- formatted access
get = get
}
return public
end
-- SCADA coordinator packet
@ -402,13 +400,18 @@ comms.coord_packet = function ()
data = nil
}
---@class coord_packet
local public = {}
local _coord_type_valid = function ()
-- @todo
return false
end
-- make a coordinator packet
local make = function (packet_type, data)
---@param packet_type any
---@param data table
public.make = function (packet_type, data)
-- packet accessor properties
self.type = packet_type
self.length = #data
@ -422,7 +425,9 @@ comms.coord_packet = function ()
end
-- decode a coordinator packet from a SCADA frame
local decode = function (frame)
---@param frame scada_packet
---@return boolean success
public.decode = function (frame)
if frame then
self.frame = frame
@ -431,7 +436,7 @@ comms.coord_packet = function ()
if ok then
local data = frame.data()
make(data[1], { table.unpack(data, 2, #data) })
public.make(data[1], { table.unpack(data, 2, #data) })
ok = _coord_type_valid()
end
@ -447,10 +452,10 @@ comms.coord_packet = function ()
end
-- get raw to send
local raw_sendable = function () return self.raw end
public.raw_sendable = function () return self.raw end
-- get this packet
local get = function ()
public.get = function ()
return {
scada_frame = self.frame,
type = self.type,
@ -459,15 +464,7 @@ comms.coord_packet = function ()
}
end
return {
-- construct
make = make,
decode = decode,
-- raw access
raw_sendable = raw_sendable,
-- formatted access
get = get
}
return public
end
-- coordinator API (CAPI) packet
@ -481,13 +478,18 @@ comms.capi_packet = function ()
data = nil
}
---@class capi_packet
local public = {}
local _coord_type_valid = function ()
-- @todo
return false
end
-- make a coordinator packet
local make = function (packet_type, data)
-- make a coordinator API packet
---@param packet_type any
---@param data table
public.make = function (packet_type, data)
-- packet accessor properties
self.type = packet_type
self.length = #data
@ -500,8 +502,10 @@ comms.capi_packet = function ()
end
end
-- decode a coordinator packet from a SCADA frame
local decode = function (frame)
-- decode a coordinator API packet from a SCADA frame
---@param frame scada_packet
---@return boolean success
public.decode = function (frame)
if frame then
self.frame = frame
@ -510,7 +514,7 @@ comms.capi_packet = function ()
if ok then
local data = frame.data()
make(data[1], { table.unpack(data, 2, #data) })
public.make(data[1], { table.unpack(data, 2, #data) })
ok = _coord_type_valid()
end
@ -526,10 +530,10 @@ comms.capi_packet = function ()
end
-- get raw to send
local raw_sendable = function () return self.raw end
public.raw_sendable = function () return self.raw end
-- get this packet
local get = function ()
public.get = function ()
return {
scada_frame = self.frame,
type = self.type,
@ -538,18 +542,12 @@ comms.capi_packet = function ()
}
end
return {
-- construct
make = make,
decode = decode,
-- raw access
raw_sendable = raw_sendable,
-- formatted access
get = get
}
return public
end
-- convert rtu_t to RTU advertisement type
---@param type rtu_t
---@return RTU_ADVERT_TYPES|nil
comms.rtu_t_to_advert_type = function (type)
if type == rtu_t.redstone then
return RTU_ADVERT_TYPES.REDSTONE
@ -571,6 +569,8 @@ comms.rtu_t_to_advert_type = function (type)
end
-- convert RTU advertisement type to rtu_t
---@param atype RTU_ADVERT_TYPES
---@return rtu_t|nil
comms.advert_type_to_rtu_t = function (atype)
if atype == RTU_ADVERT_TYPES.REDSTONE then
return rtu_t.redstone

View File

@ -4,8 +4,10 @@ local util = require("scada-common.util")
-- File System Logger
--
---@class log
local log = {}
---@alias MODE integer
local MODE = {
APPEND = 0,
NEW = 1
@ -13,6 +15,7 @@ local MODE = {
log.MODE = MODE
-- whether to log debug messages or not
local LOG_DEBUG = true
local _log_sys = {
@ -21,9 +24,12 @@ local _log_sys = {
file = nil
}
---@type function
local free_space = fs.getFreeSpace
-- initialize logger
---@param path string file path
---@param write_mode MODE
log.init = function (path, write_mode)
_log_sys.path = path
_log_sys.mode = write_mode
@ -36,6 +42,7 @@ log.init = function (path, write_mode)
end
-- private log write function
---@param msg string
local _log = function (msg)
local time_stamp = os.date("[%c] ")
local stamped = time_stamp .. msg
@ -70,6 +77,8 @@ local _log = function (msg)
end
-- log debug messages
---@param msg string message
---@param trace? boolean include file trace
log.debug = function (msg, trace)
if LOG_DEBUG then
local dbg_info = ""
@ -90,16 +99,20 @@ log.debug = function (msg, trace)
end
-- log info messages
---@param msg string message
log.info = function (msg)
_log("[INF] " .. msg)
end
-- log warning messages
---@param msg string message
log.warning = function (msg)
_log("[WRN] " .. msg)
end
-- log error messages
---@param msg string message
---@param trace? boolean include file trace
log.error = function (msg, trace)
local dbg_info = ""
@ -118,6 +131,7 @@ log.error = function (msg, trace)
end
-- log fatal errors
---@param msg string message
log.fatal = function (msg)
_log("[FTL] " .. msg)
end

View File

@ -4,6 +4,7 @@
local mqueue = {}
---@alias TYPE integer
local TYPE = {
COMMAND = 0,
DATA = 1,
@ -12,41 +13,61 @@ local TYPE = {
mqueue.TYPE = TYPE
-- create a new message queue
mqueue.new = function ()
local queue = {}
local insert = table.insert
local remove = table.remove
local length = function ()
return #queue
end
---@class queue_item
local queue_item = {
qtype = 0, ---@type TYPE
message = 0 ---@type any
}
local empty = function ()
return #queue == 0
end
---@class mqueue
local public = {}
local ready = function ()
return #queue ~= 0
end
-- get queue length
public.length = function () return #queue end
-- check if queue is empty
---@return boolean is_empty
public.empty = function () return #queue == 0 end
-- check if queue has contents
public.ready = function () return #queue ~= 0 end
-- push a new item onto the queue
---@param qtype TYPE
---@param message string
local _push = function (qtype, message)
insert(queue, { qtype = qtype, message = message })
end
local push_command = function (message)
-- push a command onto the queue
---@param message any
public.push_command = function (message)
_push(TYPE.COMMAND, message)
end
local push_data = function (key, value)
-- push data onto the queue
---@param key any
---@param value any
public.push_data = function (key, value)
_push(TYPE.DATA, { key = key, val = value })
end
local push_packet = function (message)
_push(TYPE.PACKET, message)
-- push a packet onto the queue
---@param packet scada_packet|modbus_packet|rplc_packet|coord_packet|capi_packet
public.push_packet = function (packet)
_push(TYPE.PACKET, packet)
end
local pop = function ()
-- get an item off the queue
---@return queue_item|nil
public.pop = function ()
if #queue > 0 then
return remove(queue, 1)
else
@ -54,15 +75,7 @@ mqueue.new = function ()
end
end
return {
length = length,
empty = empty,
ready = ready,
push_packet = push_packet,
push_data = push_data,
push_command = push_command,
pop = pop
}
return public
end
return mqueue

View File

@ -4,9 +4,10 @@ local log = require("scada-common.log")
-- Protected Peripheral Manager
--
---@class ppm
local ppm = {}
local ACCESS_FAULT = nil
local ACCESS_FAULT = nil ---@type nil
ppm.ACCESS_FAULT = ACCESS_FAULT
@ -22,9 +23,12 @@ local _ppm_sys = {
mute = false
}
-- wrap peripheral calls with lua protected call
-- we don't want a disconnect to crash a program
-- also provides peripheral-specific fault checks (auto-clear fault defaults to true)
-- wrap peripheral calls with lua protected call as we don't want a disconnect to crash a program
---
---also provides peripheral-specific fault checks (auto-clear fault defaults to true)
---
---assumes iface is a valid peripheral
---@param iface string CC peripheral interface
local peri_init = function (iface)
local self = {
faulted = false,
@ -150,6 +154,8 @@ ppm.mount_all = function ()
end
-- mount a particular device
---@param iface string CC peripheral interface
---@return string|nil type, table|nil device
ppm.mount = function (iface)
local ifaces = peripheral.getNames()
local pm_dev = nil
@ -171,33 +177,44 @@ ppm.mount = function (iface)
end
-- handle peripheral_detach event
---@param iface string CC peripheral interface
---@return string|nil type, table|nil device
ppm.handle_unmount = function (iface)
local pm_dev = nil
local pm_type = nil
-- what got disconnected?
local lost_dev = _ppm_sys.mounts[iface]
if lost_dev then
local type = lost_dev.type
log.warning("PPM: lost device " .. type .. " mounted to " .. iface)
pm_type = lost_dev.type
pm_dev = lost_dev.dev
log.warning("PPM: lost device " .. pm_type .. " mounted to " .. iface)
else
log.error("PPM: lost device unknown to the PPM mounted to " .. iface)
end
return lost_dev
return pm_type, pm_dev
end
-- GENERAL ACCESSORS --
-- list all available peripherals
---@return table names
ppm.list_avail = function ()
return peripheral.getNames()
end
-- list mounted peripherals
---@return table mounts
ppm.list_mounts = function ()
return _ppm_sys.mounts
end
-- get a mounted peripheral by side/interface
---@param iface string CC peripheral interface
---@return table|nil device function table
ppm.get_periph = function (iface)
if _ppm_sys.mounts[iface] then
return _ppm_sys.mounts[iface].dev
@ -205,6 +222,8 @@ ppm.get_periph = function (iface)
end
-- get a mounted peripheral type by side/interface
---@param iface string CC peripheral interface
---@return string|nil type
ppm.get_type = function (iface)
if _ppm_sys.mounts[iface] then
return _ppm_sys.mounts[iface].type
@ -212,6 +231,8 @@ ppm.get_type = function (iface)
end
-- get all mounted peripherals by type
---@param name string type name
---@return table devices device function tables
ppm.get_all_devices = function (name)
local devices = {}
@ -225,6 +246,8 @@ ppm.get_all_devices = function (name)
end
-- get a mounted peripheral by type (if multiple, returns the first)
---@param name string type name
---@return table|nil device function table
ppm.get_device = function (name)
local device = nil
@ -241,11 +264,13 @@ end
-- SPECIFIC DEVICE ACCESSORS --
-- get the fission reactor (if multiple, returns the first)
---@return table|nil reactor function table
ppm.get_fission_reactor = function ()
return ppm.get_device("fissionReactor")
end
-- get the wireless modem (if multiple, returns the first)
---@return table|nil modem function table
ppm.get_wireless_modem = function ()
local w_modem = nil
@ -260,6 +285,7 @@ ppm.get_wireless_modem = function ()
end
-- list all connected monitors
---@return table monitors
ppm.list_monitors = function ()
return ppm.get_all_devices("monitor")
end

View File

@ -8,16 +8,19 @@ local rsio = {}
-- RS I/O CONSTANTS --
----------------------
---@alias IO_LVL integer
local IO_LVL = {
LOW = 0,
HIGH = 1
}
---@alias IO_DIR integer
local IO_DIR = {
IN = 0,
OUT = 1
}
---@alias IO_MODE integer
local IO_MODE = {
DIGITAL_OUT = 0,
DIGITAL_IN = 1,
@ -25,6 +28,7 @@ local IO_MODE = {
ANALOG_IN = 3
}
---@alias RS_IO integer
local RS_IO = {
-- digital inputs --
@ -73,6 +77,7 @@ rsio.IO = RS_IO
-----------------------
-- channel to string
---@param channel RS_IO
rsio.to_string = function (channel)
local names = {
"F_SCRAM",
@ -155,6 +160,8 @@ local RS_DIO_MAP = {
}
-- get the mode of a channel
---@param channel RS_IO
---@return IO_MODE
rsio.get_io_mode = function (channel)
local modes = {
IO_MODE.DIGITAL_IN, -- F_SCRAM
@ -194,11 +201,15 @@ end
local RS_SIDES = rs.getSides()
-- check if a channel is valid
---@param channel RS_IO
---@return boolean valid
rsio.is_valid_channel = function (channel)
return channel ~= nil and channel > 0 and channel <= RS_IO.A_T_FLOW_RATE
return (channel ~= nil) and (channel > 0) and (channel <= RS_IO.A_T_FLOW_RATE)
end
-- check if a side is valid
---@param side string
---@return boolean valid
rsio.is_valid_side = function (side)
if side ~= nil then
for i = 0, #RS_SIDES do
@ -209,6 +220,8 @@ rsio.is_valid_side = function (side)
end
-- check if a color is a valid single color
---@param color integer
---@return boolean valid
rsio.is_color = function (color)
return (color > 0) and (_B_AND(color, (color - 1)) == 0);
end
@ -218,6 +231,8 @@ end
-----------------
-- get digital IO level reading
---@param rs_value boolean
---@return IO_LVL
rsio.digital_read = function (rs_value)
if rs_value then
return IO_LVL.HIGH
@ -227,6 +242,9 @@ rsio.digital_read = function (rs_value)
end
-- returns the level corresponding to active
---@param channel RS_IO
---@param active boolean
---@return IO_LVL
rsio.digital_write = function (channel, active)
if channel < RS_IO.WASTE_PO or channel > RS_IO.R_PLC_TIMEOUT then
return IO_LVL.LOW
@ -236,6 +254,9 @@ rsio.digital_write = function (channel, active)
end
-- returns true if the level corresponds to active
---@param channel RS_IO
---@param level IO_LVL
---@return boolean
rsio.digital_is_active = function (channel, level)
if channel > RS_IO.R_ENABLE or channel > RS_IO.R_PLC_TIMEOUT then
return false

View File

@ -2,8 +2,10 @@
-- Global Types
--
---@class types
local types = {}
---@alias rtu_t string
types.rtu_t = {
redstone = "redstone",
boiler = "boiler",
@ -14,6 +16,7 @@ types.rtu_t = {
induction_matrix = "induction_matrix"
}
---@alias rps_status_t string
types.rps_status_t = {
ok = "ok",
dmg_crit = "dmg_crit",
@ -30,6 +33,7 @@ types.rps_status_t = {
-- MODBUS
-- modbus function codes
---@alias MODBUS_FCODE integer
types.MODBUS_FCODE = {
READ_COILS = 0x01,
READ_DISCRETE_INPUTS = 0x02,
@ -43,6 +47,7 @@ types.MODBUS_FCODE = {
}
-- modbus exception codes
---@alias MODBUS_EXCODE integer
types.MODBUS_EXCODE = {
ILLEGAL_FUNCTION = 0x01,
ILLEGAL_DATA_ADDR = 0x02,

View File

@ -1,3 +1,8 @@
--
-- Utility Functions
--
---@class util
local util = {}
-- PRINT --
@ -24,16 +29,22 @@ end
-- TIME --
-- current time
---@return integer milliseconds
util.time_ms = function ()
---@diagnostic disable-next-line: undefined-field
return os.epoch('local')
end
-- current time
---@return integer seconds
util.time_s = function ()
---@diagnostic disable-next-line: undefined-field
return os.epoch('local') / 1000
end
-- current time
---@return integer milliseconds
util.time = function ()
return util.time_ms()
end
@ -41,19 +52,24 @@ end
-- PARALLELIZATION --
-- protected sleep call so we still are in charge of catching termination
-- EVENT_CONSUMER: this function consumes events
---@param t integer seconds
--- EVENT_CONSUMER: this function consumes events
util.psleep = function (t)
---@diagnostic disable-next-line: undefined-field
pcall(os.sleep, t)
end
-- no-op to provide a brief pause (and a yield)
-- EVENT_CONSUMER: this function consumes events
-- no-op to provide a brief pause (1 tick) to yield
---
--- EVENT_CONSUMER: this function consumes events
util.nop = function ()
util.psleep(0.05)
end
-- attempt to maintain a minimum loop timing (duration of execution)
---@param target_timing integer minimum amount of milliseconds to wait for
---@param last_update integer millisecond time of last update
---@return integer time_now
-- EVENT_CONSUMER: this function consumes events
util.adaptive_delay = function (target_timing, last_update)
local sleep_for = target_timing - (util.time() - last_update)
@ -64,6 +80,37 @@ util.adaptive_delay = function (target_timing, last_update)
return util.time()
end
-- MEKANISM POWER --
-- function kFE(fe) return fe / 1000 end
-- function MFE(fe) return fe / 1000000 end
-- function GFE(fe) return fe / 1000000000 end
-- function TFE(fe) return fe / 1000000000000 end
-- -- FLOATING POINT PRINTS --
-- local function fractional_1s(number)
-- return number == math.round(number)
-- end
-- local function fractional_10ths(number)
-- number = number * 10
-- return number == math.round(number)
-- end
-- local function fractional_100ths(number)
-- number = number * 100
-- return number == math.round(number)
-- end
-- function power_format(fe)
-- if fe < 1000 then
-- return string.format("%.2f FE", fe)
-- elseif fe < 1000000 then
-- return string.format("%.3f kFE", kFE(fe))
-- end
-- end
-- WATCHDOG --
-- ComputerCraft OS Timer based Watchdog