scada-common annotation updates

This commit is contained in:
Mikayla 2024-09-12 17:47:12 +00:00
parent fb56634fc4
commit 3003e57531
10 changed files with 89 additions and 65 deletions

View File

@ -6,10 +6,10 @@
-- note: max samples = 0x20000 (128 * 1024 samples) -- note: max samples = 0x20000 (128 * 1024 samples)
local _2_PI = 2 * math.pi -- 2 whole pies, hope you're hungry local _2_PI = 2 * math.pi -- 2 whole pies, hope you're hungry
local _DRATE = 48000 -- 48kHz audio local _DRATE = 48000 -- 48kHz audio data rate
local _MAX_VAL = 127 / 2 -- max signed integer in this 8-bit audio local _MAX_VAL = 127 / 2 -- max signed integer in this 8-bit audio
local _05s_SAMPLES = 24000 -- half a second worth of samples local _05s_SAMPLES = 24000 -- half a second worth of samples
---@class audio ---@class audio
local audio = {} local audio = {}
@ -28,6 +28,7 @@ local TONE = {
audio.TONE = TONE audio.TONE = TONE
---@type integer[][][]
local tone_data = { local tone_data = {
{ {}, {}, {}, {} }, -- 340Hz @ 2Hz Intermittent { {}, {}, {}, {} }, -- 340Hz @ 2Hz Intermittent
{ {}, {}, {}, {} }, -- 544Hz 100mS / 440Hz 400mS Alternating { {}, {}, {}, {} }, -- 544Hz 100mS / 440Hz 400mS Alternating
@ -214,7 +215,14 @@ end
-- generate all 8 tone sequences -- generate all 8 tone sequences
function audio.generate_tones() function audio.generate_tones()
gen_tone_1(); gen_tone_2(); gen_tone_3(); gen_tone_4(); gen_tone_5(); gen_tone_6(); gen_tone_7(); gen_tone_8() gen_tone_1()
gen_tone_2()
gen_tone_3()
gen_tone_4()
gen_tone_5()
gen_tone_6()
gen_tone_7()
gen_tone_8()
end end
-- hard audio limiter -- hard audio limiter
@ -226,7 +234,7 @@ local function limit(output)
end end
-- clear output buffer -- clear output buffer
---@param buffer table quad buffer ---@param buffer integer[][] quad buffer
local function clear(buffer) local function clear(buffer)
for i = 1, 4 do for i = 1, 4 do
for s = 1, _05s_SAMPLES do buffer[i][s] = 0 end for s = 1, _05s_SAMPLES do buffer[i][s] = 0 end
@ -239,7 +247,7 @@ function audio.new_stream()
any_active = false, any_active = false,
need_recompute = false, need_recompute = false,
next_block = 1, next_block = 1,
-- split audio up into 0.5s samples, so specific components can be ended quicker ---@type integer[][] split audio up into 0.5s samples, so specific components can be ended quicker
quad_buffer = { {}, {}, {}, {} }, quad_buffer = { {}, {}, {}, {} },
-- all tone enable states -- all tone enable states
tone_active = { false, false, false, false, false, false, false, false } tone_active = { false, false, false, false, false, false, false, false }
@ -263,14 +271,17 @@ function audio.new_stream()
-- check if a tone is active -- check if a tone is active
---@param index TONE tone index ---@param index TONE tone index
function public.is_active(index) function public.is_active(index)
if self.tone_active[index] then return self.tone_active[index] end return self.tone_active[index] or false
return false
end end
-- set all tones inactive, reset next block, and clear output buffer -- set all tones inactive, reset next block, and clear output buffer
function public.stop() function public.stop()
for i = 1, #self.tone_active do self.tone_active[i] = false end for i = 1, #self.tone_active do
self.tone_active[i] = false
end
self.next_block = 1 self.next_block = 1
clear(self.quad_buffer) clear(self.quad_buffer)
end end
@ -287,9 +298,11 @@ function audio.new_stream()
for id = 1, #tone_data do for id = 1, #tone_data do
if self.tone_active[id] then if self.tone_active[id] then
self.any_active = true self.any_active = true
for i = 1, 4 do for i = 1, 4 do
local buffer = self.quad_buffer[i] local buffer = self.quad_buffer[i]
local values = tone_data[id][i] local values = tone_data[id][i]
for s = 1, _05s_SAMPLES do self.quad_buffer[i][s] = limit(buffer[s] + values[s]) end for s = 1, _05s_SAMPLES do self.quad_buffer[i][s] = limit(buffer[s] + values[s]) end
end end
end end
@ -305,8 +318,13 @@ function audio.new_stream()
-- get the next audio block -- get the next audio block
function public.get_next_block() function public.get_next_block()
local block = self.quad_buffer[self.next_block] local block = self.quad_buffer[self.next_block]
self.next_block = self.next_block + 1 self.next_block = self.next_block + 1
if self.next_block > 4 then self.next_block = 1 end
if self.next_block > 4 then
self.next_block = 1
end
return block return block
end end

View File

@ -405,7 +405,7 @@ function comms.modbus_packet()
self.raw = { self.txn_id, self.unit_id, self.func_code } self.raw = { self.txn_id, self.unit_id, self.func_code }
for i = 1, self.length do insert(self.raw, data[i]) end for i = 1, self.length do insert(self.raw, data[i]) end
else else
log.error("comms.modbus_packet.make(): data not table") log.error("comms.modbus_packet.make(): data not a table")
end end
end end
@ -491,7 +491,7 @@ function comms.rplc_packet()
self.raw = { self.id, self.type } self.raw = { self.id, self.type }
for i = 1, #data do insert(self.raw, data[i]) end for i = 1, #data do insert(self.raw, data[i]) end
else else
log.error("comms.rplc_packet.make(): data not table") log.error("comms.rplc_packet.make(): data not a table")
end end
end end
@ -573,7 +573,7 @@ function comms.mgmt_packet()
self.raw = { self.type } self.raw = { self.type }
for i = 1, #data do insert(self.raw, data[i]) end for i = 1, #data do insert(self.raw, data[i]) end
else else
log.error("comms.mgmt_packet.make(): data not table") log.error("comms.mgmt_packet.make(): data not a table")
end end
end end
@ -652,7 +652,7 @@ function comms.crdn_packet()
self.raw = { self.type } self.raw = { self.type }
for i = 1, #data do insert(self.raw, data[i]) end for i = 1, #data do insert(self.raw, data[i]) end
else else
log.error("comms.crdn_packet.make(): data not table") log.error("comms.crdn_packet.make(): data not a table")
end end
end end

View File

@ -7,7 +7,7 @@ local util = require("scada-common.util")
---@class logger ---@class logger
local log = {} local log = {}
---@alias MODE integer ---@enum MODE
local MODE = { APPEND = 0, NEW = 1 } local MODE = { APPEND = 0, NEW = 1 }
log.MODE = MODE log.MODE = MODE
@ -18,7 +18,7 @@ local logger = {
mode = MODE.APPEND, mode = MODE.APPEND,
debug = false, debug = false,
file = nil, ---@type table|nil file = nil, ---@type table|nil
dmesg_out = nil, dmesg_out = nil, ---@type table|nil
dmesg_restore_coord = { 1, 1 }, dmesg_restore_coord = { 1, 1 },
dmesg_scroll_count = 0 dmesg_scroll_count = 0
} }
@ -37,7 +37,7 @@ local function _log(msg)
local out_of_space = false local out_of_space = false
local time_stamp = os.date("[%c] ") local time_stamp = os.date("[%c] ")
local stamped = time_stamp .. util.strval(msg) local stamped = util.c(time_stamp, msg)
-- attempt to write log -- attempt to write log
local status, result = pcall(function () local status, result = pcall(function ()
@ -291,7 +291,7 @@ function log.dmesg_working(msg, tag, tag_color)
end end
-- log debug messages -- log debug messages
---@param msg string message ---@param msg any message
---@param trace? boolean include file trace ---@param trace? boolean include file trace
function log.debug(msg, trace) function log.debug(msg, trace)
if logger.debug then if logger.debug then
@ -302,30 +302,30 @@ function log.debug(msg, trace)
local name = "" local name = ""
if info.name ~= nil then if info.name ~= nil then
name = ":" .. info.name .. "():" name = util.c(":", info.name, "():")
end end
dbg_info = info.short_src .. ":" .. name .. info.currentline .. " > " dbg_info = util.c(info.short_src, ":", name, info.currentline, " > ")
end end
_log("[DBG] " .. dbg_info .. util.strval(msg)) _log(util.c("[DBG] ", dbg_info, msg))
end end
end end
-- log info messages -- log info messages
---@param msg string message ---@param msg any message
function log.info(msg) function log.info(msg)
_log("[INF] " .. util.strval(msg)) _log(util.c("[INF] ", msg))
end end
-- log warning messages -- log warning messages
---@param msg string message ---@param msg any message
function log.warning(msg) function log.warning(msg)
_log("[WRN] " .. util.strval(msg)) _log(util.c("[WRN] ", msg))
end end
-- log error messages -- log error messages
---@param msg string message ---@param msg any message
---@param trace? boolean include file trace ---@param trace? boolean include file trace
function log.error(msg, trace) function log.error(msg, trace)
local dbg_info = "" local dbg_info = ""
@ -335,19 +335,19 @@ function log.error(msg, trace)
local name = "" local name = ""
if info.name ~= nil then if info.name ~= nil then
name = ":" .. info.name .. "():" name = util.c(":", info.name, "():")
end end
dbg_info = info.short_src .. ":" .. name .. info.currentline .. " > " dbg_info = util.c(info.short_src, ":", name, info.currentline, " > ")
end end
_log("[ERR] " .. dbg_info .. util.strval(msg)) _log(util.c("[ERR] ", dbg_info, msg))
end end
-- log fatal errors -- log fatal errors
---@param msg string message ---@param msg any message
function log.fatal(msg) function log.fatal(msg)
_log("[FTL] " .. util.strval(msg)) _log(util.c("[FTL] ", msg))
end end
return log return log

View File

@ -27,6 +27,7 @@ local remove = table.remove
-- create a new message queue -- create a new message queue
---@nodiscard ---@nodiscard
function mqueue.new() function mqueue.new()
---@type queue_item[]
local queue = {} local queue = {}
---@class mqueue ---@class mqueue

View File

@ -85,18 +85,15 @@ function network.nic(modem)
} }
---@class nic ---@class nic
---@field open function ---@field isOpen fun(channel: integer) : boolean check if a channel is open
---@field isOpen function ---@field isWireless fun() : boolean determine if this is a wired or wireless modem
---@field close function ---@field getNamesRemote fun() : string[] list all remote peripherals on the wired network
---@field closeAll function ---@field isPresentRemote fun(name: string) : boolean determine if a peripheral is available on this wired network
---@field isWireless function ---@field getTypeRemote fun(name: string) : string|nil get the type of a peripheral is available on this wired network
---@field getNameLocal function ---@field hasTypeRemote fun(name: string, type: string) : boolean|nil check a peripheral is of a particular type
---@field getNamesRemote function ---@field getMethodsRemote fun(name: string) : string[] get all available methods for the remote peripheral with the given name
---@field isPresentRemote function ---@field callRemote fun(remoteName: string, method: string, ...) : table call a method on a peripheral on this wired network
---@field getTypeRemote function ---@field getNameLocal fun() : string|nil returns the network name of the current computer, if the modem is on
---@field hasTypeRemote function
---@field getMethodsRemote function
---@field callRemote function
local public = {} local public = {}
-- check if this NIC has a connected modem -- check if this NIC has a connected modem

View File

@ -23,7 +23,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 = {}, ---@type { [string]: ppm_entry }
next_vid = 0, next_vid = 0,
auto_cf = false, auto_cf = false,
faulted = false, faulted = false,
@ -40,10 +40,10 @@ local function peri_init(iface)
local self = { local self = {
faulted = false, faulted = false,
last_fault = "", last_fault = "",
fault_counts = {}, fault_counts = {}, ---@type { [string]: integer }
auto_cf = true, auto_cf = true,
type = VIRTUAL_DEVICE_TYPE, type = VIRTUAL_DEVICE_TYPE, ---@type string
device = {} device = {} ---@type { [string]: function }
} }
if iface ~= "__virtual__" then if iface ~= "__virtual__" then
@ -181,7 +181,7 @@ local function peri_init(iface)
setmetatable(self.device, mt) setmetatable(self.device, mt)
---@class ppm_entry ---@class ppm_entry
local entry = { type = self.type, dev = self.device } local entry = { type = self.type, dev = self.device }
return entry return entry
end end
@ -284,10 +284,10 @@ end
---@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 iface, 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 ", iface))
ppm_sys.mounts[side] = nil ppm_sys.mounts[iface] = nil
break break
end end
end end
@ -352,8 +352,8 @@ end
---@return string|nil iface CC peripheral interface ---@return string|nil iface CC peripheral interface
function ppm.get_iface(device) function ppm.get_iface(device)
if device then if device then
for side, data in pairs(ppm_sys.mounts) do for iface, data in pairs(ppm_sys.mounts) do
if data.dev == device then return side end if data.dev == device then return iface end
end end
end end

View File

@ -6,9 +6,10 @@ local util = require("scada-common.util")
local psil = {} local psil = {}
-- instantiate a new PSI layer -- instantiate a new interconnect layer
---@nodiscard ---@nodiscard
function psil.create() function psil.create()
---@type { [string]: { subscribers: { notify: fun(param: any) }[], value: any } } interconnect table
local ic = {} local ic = {}
-- allocate a new interconnect field -- allocate a new interconnect field

View File

@ -113,10 +113,13 @@ assert(#dup_chk == rsio.NUM_PORTS, "port list malformed")
local IO = IO_PORT local IO = IO_PORT
-- list of all port names -- list of all port names
---@type string[]
local PORT_NAMES = {} local PORT_NAMES = {}
for k, v in pairs(IO) do PORT_NAMES[v] = k end for k, v in pairs(IO) do PORT_NAMES[v] = k end
-- list of all port I/O modes -- list of all port I/O modes
---@type { [IO_PORT]: IO_MODE }
local MODES = { local MODES = {
[IO.F_SCRAM] = IO_MODE.DIGITAL_IN, [IO.F_SCRAM] = IO_MODE.DIGITAL_IN,
[IO.F_ACK] = IO_MODE.DIGITAL_IN, [IO.F_ACK] = IO_MODE.DIGITAL_IN,
@ -233,6 +236,7 @@ end
--#region Generic Checks --#region Generic Checks
---@type string[]
local RS_SIDES = rs.getSides() local RS_SIDES = rs.getSides()
-- check if a port is valid -- check if a port is valid

View File

@ -7,6 +7,7 @@ local util = require("scada-common.util")
local tcd = {} local tcd = {}
---@type { callback: function, duration: number, expiry: number }[]
local registry = {} local registry = {}
-- request a function to be called after the specified time -- request a function to be called after the specified time

View File

@ -24,7 +24,7 @@ local t_pack = table.pack
local util = {} local util = {}
-- scada-common version -- scada-common version
util.version = "1.4.4" util.version = "1.4.5"
util.TICK_TIME_S = 0.05 util.TICK_TIME_S = 0.05
util.TICK_TIME_MS = 50 util.TICK_TIME_MS = 50
@ -33,9 +33,9 @@ util.TICK_TIME_MS = 50
-- trinary operator -- trinary operator
---@nodiscard ---@nodiscard
---@param cond boolean|nil condition ---@param cond any condition
---@param a any return if true ---@param a any return if evaluated as true
---@param b any return if false ---@param b any return if false or nil
---@return any value ---@return any value
function util.trinary(cond, a, b) function util.trinary(cond, a, b)
if cond then return a else return b end if cond then return a else return b end
@ -84,7 +84,7 @@ end
-- does not behave exactly like C's strtok -- does not behave exactly like C's strtok
---@param str string string to tokenize ---@param str string string to tokenize
---@param sep string separator to tokenize by ---@param sep string separator to tokenize by
---@return table token_list ---@return string[] token_list
function util.strtok(str, sep) function util.strtok(str, sep)
local list = {} local list = {}
for part in string.gmatch(str, "([^" .. sep .. "]+)") do t_insert(list, part) end for part in string.gmatch(str, "([^" .. sep .. "]+)") do t_insert(list, part) end
@ -123,7 +123,7 @@ end
---@nodiscard ---@nodiscard
---@param str string ---@param str string
---@param limit integer line limit, must be greater than 0 ---@param limit integer line limit, must be greater than 0
---@return table lines ---@return string[] lines
function util.strwrap(str, limit) function util.strwrap(str, limit)
assert(limit > 0, "util.strwrap() limit not greater than 0") assert(limit > 0, "util.strwrap() limit not greater than 0")
return cc_strings.wrap(str, limit) return cc_strings.wrap(str, limit)
@ -343,8 +343,9 @@ end
-- delete elements from a table if the passed function returns false when passed a table element<br> -- 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 ---@generic Type
---@param f function should return false to delete an element when passed the element: f(elem) = true|false ---@param t Type[] table to remove elements from
---@param f fun(t_elem: Type) : boolean should return false to delete an element when passed the element
---@param on_delete? function optional function to execute on deletion, passed the table element to be deleted as the parameter ---@param on_delete? function optional function to execute on deletion, passed the table element to be deleted as the parameter
function util.filter_table(t, f, on_delete) function util.filter_table(t, f, on_delete)
local move_to = 1 local move_to = 1
@ -366,9 +367,10 @@ 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
---@generic Type
---@nodiscard ---@nodiscard
---@param t table table to check ---@param t Type[] table to check
---@param element any element to check for ---@param element Type element to check for
function util.table_contains(t, element) function util.table_contains(t, element)
for i = 1, #t do for i = 1, #t do
if t[i] == element then return true end if t[i] == element then return true end