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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,8 @@
--
-- Utility Functions
--
---@class util
local util = {} local util = {}
-- PRINT -- -- PRINT --
@ -24,16 +29,22 @@ end
-- TIME -- -- TIME --
-- current time
---@return integer milliseconds
util.time_ms = function () util.time_ms = function ()
---@diagnostic disable-next-line: undefined-field ---@diagnostic disable-next-line: undefined-field
return os.epoch('local') return os.epoch('local')
end end
-- current time
---@return integer seconds
util.time_s = function () util.time_s = function ()
---@diagnostic disable-next-line: undefined-field ---@diagnostic disable-next-line: undefined-field
return os.epoch('local') / 1000 return os.epoch('local') / 1000
end end
-- current time
---@return integer milliseconds
util.time = function () util.time = function ()
return util.time_ms() return util.time_ms()
end end
@ -41,19 +52,24 @@ end
-- PARALLELIZATION -- -- PARALLELIZATION --
-- protected sleep call so we still are in charge of catching termination -- 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) util.psleep = function (t)
---@diagnostic disable-next-line: undefined-field ---@diagnostic disable-next-line: undefined-field
pcall(os.sleep, t) pcall(os.sleep, t)
end end
-- no-op to provide a brief pause (and a yield) -- no-op to provide a brief pause (1 tick) to yield
-- EVENT_CONSUMER: this function consumes events ---
--- EVENT_CONSUMER: this function consumes events
util.nop = function () util.nop = function ()
util.psleep(0.05) util.psleep(0.05)
end end
-- attempt to maintain a minimum loop timing (duration of execution) -- 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 -- EVENT_CONSUMER: this function consumes events
util.adaptive_delay = function (target_timing, last_update) util.adaptive_delay = function (target_timing, last_update)
local sleep_for = target_timing - (util.time() - 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() return util.time()
end 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 -- -- WATCHDOG --
-- ComputerCraft OS Timer based Watchdog -- ComputerCraft OS Timer based Watchdog