mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
#61 monitor configuration and init, render engine started, dmesg changes, ppm monitor listing changes
This commit is contained in:
parent
ff5b163c1d
commit
e65a1bf6e1
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -8,6 +8,8 @@
|
||||
"parallel",
|
||||
"colors",
|
||||
"textutils",
|
||||
"shell"
|
||||
"shell",
|
||||
"settings",
|
||||
"window"
|
||||
]
|
||||
}
|
||||
|
22
coordinator/config.lua
Normal file
22
coordinator/config.lua
Normal 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
|
@ -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
41
coordinator/renderer.lua
Normal 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
|
@ -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)
|
||||
|
45
coordinator/util/dialog.lua
Normal file
45
coordinator/util/dialog.lua
Normal 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
|
@ -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,11 +129,43 @@ local _write = function (msg)
|
||||
end
|
||||
end
|
||||
|
||||
-- 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
|
||||
if cur_y == out_h then
|
||||
out.scroll(1)
|
||||
out.setCursorPos(1, cur_y)
|
||||
else
|
||||
out.setCursorPos(1, cur_y + 1)
|
||||
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
|
||||
local cur_x, cur_y = out.getCursorPos()
|
||||
cur_x, cur_y = out.getCursorPos()
|
||||
|
||||
if cur_x > 1 then
|
||||
if i > 1 and cur_x > 1 then
|
||||
if cur_y == out_h then
|
||||
out.scroll(1)
|
||||
out.setCursorPos(1, cur_y)
|
||||
@ -131,15 +176,8 @@ local _write = function (msg)
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user