#61 monitor configuration and init, render engine started, dmesg changes, ppm monitor listing changes

This commit is contained in:
Mikayla Fischler 2022-05-29 14:34:09 -04:00
parent ff5b163c1d
commit e65a1bf6e1
8 changed files with 333 additions and 23 deletions

View File

@ -8,6 +8,8 @@
"parallel",
"colors",
"textutils",
"shell"
"shell",
"settings",
"window"
]
}

22
coordinator/config.lua Normal file
View File

@ -0,0 +1,22 @@
local config = {}
-- port of the SCADA supervisor
config.SCADA_SV_PORT = 16100
-- port to listen to incoming packets from supervisor
config.SCADA_SV_LISTEN = 16101
-- listen port for SCADA coordinator API access
config.SCADA_API_LISTEN = 16200
-- expected number of reactor units
config.NUM_UNITS = 4
-- log path
config.LOG_PATH = "/log.txt"
-- log mode
-- 0 = APPEND (adds to existing file on start)
-- 1 = NEW (replaces existing file on start)
config.LOG_MODE = 0
-- crypto config
config.SECURE = true
-- must be common between all devices
config.PASSWORD = "testpassword!"
return config

View File

@ -1,9 +1,141 @@
local comms = require("scada-common.comms")
local log = require("scada-common.log")
local ppm = require("scada-common.ppm")
local util = require("scada-common.util")
local dialog = require("coordinator.util.dialog")
local print = util.print
local println = util.println
local print_ts = util.print_ts
local println_ts = util.println_ts
local coordinator = {}
local function ask_monitor(names)
println("available monitors:")
for i = 1, #names do
print(" " .. names[i])
end
println("")
println("select a monitor or type c to cancel")
local iface = dialog.ask_options(names, "c")
if iface ~= false and iface ~= nil then
util.filter_table(names, function (x) return x ~= iface end)
end
return iface
end
function coordinator.configure_monitors(num_units)
---@class monitors_struct
local monitors = {
primary = nil,
unit_displays = {}
}
local monitors_avail = ppm.get_monitor_list()
local names = {}
-- get all interface names
for iface, _ in pairs(monitors_avail) do
table.insert(names, iface)
end
-- we need a certain number of monitors (1 per unit + 1 primary display)
if #names ~= num_units + 1 then
println("not enough monitors connected (need " .. num_units + 1 .. ")")
log.warning("insufficient monitors present (need " .. num_units + 1 .. ")")
return false
end
-- attempt to load settings
settings.load("/coord.settings")
---------------------
-- PRIMARY DISPLAY --
---------------------
local iface_primary_display = settings.get("PRIMARY_DISPLAY")
if not util.table_contains(names, iface_primary_display) then
println("primary display is not connected")
local response = dialog.ask_y_n("would you like to change it", true)
if response == false then return false end
iface_primary_display = nil
end
while iface_primary_display == nil and #names > 0 do
-- lets get a monitor
iface_primary_display = ask_monitor(names)
end
if iface_primary_display == false then return false end
settings.set("PRIMARY_DISPLAY", iface_primary_display)
util.filter_table(names, function (x) return x ~= iface_primary_display end)
monitors.primary = ppm.get_periph(iface_primary_display)
-------------------
-- UNIT DISPLAYS --
-------------------
local unit_displays = settings.get("UNIT_DISPLAYS")
if unit_displays == nil then
unit_displays = {}
for i = 1, num_units do
local display = nil
while display == nil and #names > 0 do
-- lets get a monitor
println("please select monitor for unit " .. i)
display = ask_monitor(names)
end
if display == false then return false end
unit_displays[i] = display
end
else
-- make sure all displays are connected
for i = 1, num_units do
---@diagnostic disable-next-line: need-check-nil
local display = unit_displays[i]
if not util.table_contains(names, display) then
local response = dialog.ask_y_n("unit display " .. i .. " is not connected, would you like to change it?", true)
if response == false then return false end
display = nil
end
while display == nil and #names > 0 do
-- lets get a monitor
display = ask_monitor(names)
end
if display == false then return false end
unit_displays[i] = display
end
end
settings.set("UNIT_DISPLAYS", unit_displays)
settings.save("/coord.settings")
for i = 1, #unit_displays do
monitors.unit_displays[i] = ppm.get_periph(unit_displays[i])
end
return true, monitors
end
-- coordinator communications
coordinator.coord_comms = function ()
function coordinator.coord_comms()
local self = {
reactor_struct_cache = nil
}

41
coordinator/renderer.lua Normal file
View File

@ -0,0 +1,41 @@
local log = require("scada-common.log")
local util = require("scada-common.util")
local renderer = {}
local engine = {
monitors = nil,
dmesg_window = nil
}
---@param monitors monitors_struct
function renderer.set_displays(monitors)
engine.monitors = monitors
end
function renderer.reset()
-- reset primary monitor
engine.monitors.primary.setTextScale(0.5)
engine.monitors.primary.setTextColor(colors.white)
engine.monitors.primary.setBackgroundColor(colors.black)
engine.monitors.primary.clear()
engine.monitors.primary.setCursorPos(1, 1)
-- reset unit displays
for _, monitor in pairs(engine.monitors.unit_displays) do
monitor.setTextScale(0.5)
monitor.setTextColor(colors.white)
monitor.setBackgroundColor(colors.black)
monitor.clear()
monitor.setCursorPos(1, 1)
end
end
function renderer.init_dmesg()
local disp_x, disp_y = engine.monitors.primary.getSize()
engine.dmesg_window = window.create(engine.monitors.primary, 1, 1, disp_x, disp_y)
log.direct_dmesg(engine.dmesg_window)
end
return renderer

View File

@ -10,6 +10,7 @@ local util = require("scada-common.util")
local config = require("coordinator.config")
local coordinator = require("coordinator.coordinator")
local renderer = require("coordinator.renderer")
local COORDINATOR_VERSION = "alpha-v0.1.2"
@ -18,7 +19,7 @@ local println = util.println
local print_ts = util.print_ts
local println_ts = util.println_ts
log.init("/log.txt", log.MODE.APPEND)
log.init(config.LOG_PATH, config.LOG_MODE)
log.info("========================================")
log.info("BOOTING coordinator.startup " .. COORDINATOR_VERSION)
@ -28,10 +29,31 @@ println(">> SCADA Coordinator " .. COORDINATOR_VERSION .. " <<")
-- mount connected devices
ppm.mount_all()
local modem = ppm.get_wireless_modem()
-- we need a modem
if modem == nil then
println("please connect a wireless modem")
-- setup monitors
local configured, monitors = coordinator.configure_monitors(config.NUM_UNITS)
if not configured then
println("boot> monitor setup failed")
log.fatal("monitor configuration failed")
return
end
log.info("monitors ready, dmesg input incoming...")
-- init renderer
renderer.set_displays(monitors)
renderer.reset()
renderer.init_dmesg()
log.dmesg("displays connected and reset", "GRAPHICS", colors.green)
log.dmesg("system start on " .. os.date("%c"), "SYSTEM", colors.cyan)
log.dmesg("starting " .. COORDINATOR_VERSION, "BOOT", colors.blue)
-- get the communications modem
local modem = ppm.get_wireless_modem()
if modem == nil then
println("boot> wireless modem not found")
log.fatal("no wireless modem on startup")
return
end
log.dmesg("wireless modem connected", "COMMS", colors.purple)

View File

@ -0,0 +1,45 @@
local completion = require("cc.completion")
local util = require("scada-common.util")
local print = util.print
local println = util.println
local print_ts = util.print_ts
local println_ts = util.println_ts
local dialog = {}
function dialog.ask_y_n(question, default)
print(question)
if default == true then
print(" (Y/n)? ")
else
print(" (y/N)? ")
end
local response = read(nil, nil)
if response == "" then
return default
elseif response == "Y" or response == "y" then
return true
elseif response == "N" or response == "n" then
return false
else
return nil
end
end
function dialog.ask_options(options, cancel)
print("> ")
local response = read(nil, nil, function(text) return completion.choice(text, options) end)
if response == cancel then return false end
if util.table_contains(options, response) then
return response
else return nil end
end
return dialog

View File

@ -49,6 +49,12 @@ log.init = function (path, write_mode, dmesg_redirect)
end
end
-- direct dmesg output to a monitor/window
---@param window table window or terminal reference
log.direct_dmesg = function (window)
_log_sys.dmesg_out = window
end
-- private log write function
---@param msg string
local _log = function (msg)
@ -84,9 +90,16 @@ local _log = function (msg)
end
end
-- write a message to the dmesg output
---@param msg string message to write
local _write = function (msg)
-- dmesg style logging for boot because I like linux-y things
---@param msg string message
---@param tag? string log tag
---@param tag_color? integer log tag color
log.dmesg = function (msg, tag, tag_color)
msg = util.strval(msg)
tag = tag or ""
tag = util.strval(tag)
local t_stamp = string.format("%12.2f", os.clock())
local out = _log_sys.dmesg_out
local out_w, out_h = out.getSize()
@ -116,8 +129,7 @@ local _write = function (msg)
end
end
-- output message
for i = 1, #lines do
-- start output with tag and time, assuming we have enough width for this to be on one line
local cur_x, cur_y = out.getCursorPos()
if cur_x > 1 then
@ -129,17 +141,43 @@ local _write = function (msg)
end
end
-- colored time
local initial_color = out.getTextColor()
out.setTextColor(colors.white)
out.write("[")
out.setTextColor(colors.lightGray)
out.write(t_stamp)
out.setTextColor(colors.white)
out.write("] ")
-- colored tag
if tag ~= "" then
out.write("[")
out.setTextColor(tag_color)
out.write(tag)
out.setTextColor(colors.white)
out.write("] ")
end
out.setTextColor(initial_color)
-- output message
for i = 1, #lines do
cur_x, cur_y = out.getCursorPos()
if i > 1 and cur_x > 1 then
if cur_y == out_h then
out.scroll(1)
out.setCursorPos(1, cur_y)
else
out.setCursorPos(1, cur_y + 1)
end
end
out.write(lines[i])
end
end
-- dmesg style logging for boot because I like linux-y things
---@param msg string message
---@param show_term? boolean whether or not to show on terminal output
log.dmesg = function (msg, show_term)
local message = string.format("[%10.3f] ", os.clock()) .. util.strval(msg)
if show_term then _write(message) end
_log(message)
_log("[" .. t_stamp .. "] " .. tag .. " " .. msg)
end
-- log debug messages

View File

@ -317,8 +317,16 @@ end
-- list all connected monitors
---@return table monitors
ppm.list_monitors = function ()
return ppm.get_all_devices("monitor")
ppm.get_monitor_list = function ()
local list = {}
for iface, device in pairs(_ppm_sys.mounts) do
if device.type == "monitor" then
list[iface] = device
end
end
return list
end
return ppm