Compare commits

..

14 Commits

Author SHA1 Message Date
57f83bd56b updated 2025-12-13 20:13:54 -05:00
5890c68d8e updated 2025-12-12 17:14:05 -05:00
facaef5983 update volume 2025-12-12 16:07:40 -05:00
256a7ecca6 updated 2025-12-12 16:02:50 -05:00
4f8df39b69 up 2025-12-12 16:01:03 -05:00
e41caf713c updated 2025-12-12 16:00:07 -05:00
c80f0fed9a updated 2025-12-12 15:57:59 -05:00
8c1048698d updated 2025-12-12 15:55:29 -05:00
903a8baf87 fixed 2025-12-12 15:53:33 -05:00
e77752efae Added notificaiton sound 2025-12-12 15:52:18 -05:00
4e6b83a456 Added chat 2025-12-12 14:36:18 -05:00
f6dd0bd669 updated 2025-12-12 14:29:57 -05:00
06f5605e3d updated 2025-12-12 14:10:32 -05:00
2fa9be5ebf added inital chat 2025-12-12 14:09:35 -05:00
10 changed files with 461 additions and 3 deletions

View File

@@ -1,16 +1,27 @@
local kernel = require("kernel")
local monitor = kernel.addDriver("monitor-driver")
local data = kernel.addDriver("disk-driver")
local taskManager = require("task-manager")
local monitor = require("monitor-driver")
local data = require("disk-driver")
local speaker = require("speaker-driver")
local chat = kernel.addProgram("chat")
local gps = kernel.addProgram("gps")
local function run()
monitor.initialize()
data.initialize()
speaker.initialize()
monitor.writeLine("Starting system...")
monitor.writeLine("System started successfully!")
computerName = data.getRead("computer-name")
os.setComputerLabel(computerName)
monitor.writeLine("Computer name: " .. computerName)
taskManager.addProgram("Communication", chat);
taskManager.addProgram("GPS", gps);
taskManager.listPrograms()
end
return { run = run }

View File

@@ -1,3 +1,5 @@
local function addDriver(fileName)
local extension = ".lua"
local fullFile = fileName .. extension
@@ -18,4 +20,41 @@ local function addFolderDriver(folder, fileName)
return require(fileName)
end
return { addDriver = addDriver, addFolderDriver = addFolderDriver }
local function addProgram(fileName)
local extension = ".lua"
local fullFile = fileName .. extension
shell.execute("rm", fullFile)
local baseRoute = "https://git.astrocore.space/root/nova-corp/raw/branch/main/"
shell.execute("wget", baseRoute .. "programs" .. "/" .. fullFile)
sleep(2)
return require(fileName)
end
local function addSound(fileName)
local extension = ".dfpwm"
local fullFile = fileName .. extension
shell.execute("rm", fullFile)
local baseRoute = "https://git.astrocore.space/root/nova-corp/raw/branch/main/"
shell.execute("wget", baseRoute .. "sounds" .. "/" .. fullFile)
sleep(2)
return
end
local function addServerHandler(fileName)
local extension = ".lua"
local fullFile = fileName .. extension
shell.execute("rm", fullFile)
local baseRoute = "https://git.astrocore.space/root/nova-corp/raw/branch/main/"
shell.execute("wget", baseRoute .. "server" .. "/" .. fullFile)
sleep(2)
return require(fileName)
end
addDriver("task-manager")
addDriver("monitor-driver")
addDriver("disk-driver")
addDriver("speaker-driver")
return { addDriver = addDriver, addFolderDriver = addFolderDriver, addProgram = addProgram, addSound = addSound, addServerHandler = addServerHandler }

70
programs/chat.lua Normal file
View File

@@ -0,0 +1,70 @@
local modem = peripheral.find("modem")
local speaker = require("speaker-driver")
local SERVER_PORT = 100
local CLIENT_PORT = 101
local username = os.getComputerLabel() or ("User" .. os.getComputerID())
local function start()
if modem then
modem.open(CLIENT_PORT)
modem.transmit(SERVER_PORT, CLIENT_PORT, { type = "join", username = username })
end
print("Chat program started as " .. username)
end
local function stop()
if modem then
modem.transmit(SERVER_PORT, CLIENT_PORT, { type = "leave", username = username })
modem.close(CLIENT_PORT)
end
print("Chat program stopped.")
end
local function restart()
stop()
start()
end
local function receiveLoop()
while true do
local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
if channel == CLIENT_PORT and type(message) == "table" then
local x, y = term.getCursorPos()
term.setCursorPos(1, y)
term.clearLine()
if message.type == "system" then
print("[System] " .. message.content)
speaker.play("notification")
elseif message.type == "chat" then
print(message.username .. ": " .. message.content)
speaker.play("notification")
speaker.playTTSFile(message.content)
end
write("> ")
end
end
end
local function sendLoop()
while true do
write("> ")
local input = read()
if modem then
modem.transmit(SERVER_PORT, CLIENT_PORT, { type = "chat", username = username, content = input })
end
end
end
local function main()
if not modem then
print("No modem attached")
while true do os.pullEvent() end
end
parallel.waitForAny(receiveLoop, sendLoop)
end
return { start = start, stop = stop, restart = restart, main = main }

24
programs/gps.lua Normal file
View File

@@ -0,0 +1,24 @@
local function start()
print("GPS program started.")
end
local function stop()
print("GPS program stopped.")
end
local function restart()
stop()
start()
end
local function main()
print("Chat main loop running...")
while true do
local event = os.pullEvent()
if event == "char" then
print("GPS Char")
end
end
end
return { start = start, stop = stop, restart = restart, main = main }

51
server/chat-server.lua Normal file
View File

@@ -0,0 +1,51 @@
local modem = peripheral.find("modem")
local SERVER_PORT = 100
local CLIENT_PORT = 101
local function log(msg)
print("[Server]: " .. tostring(msg))
end
local function broadcast(message)
if modem then
modem.transmit(CLIENT_PORT, SERVER_PORT, message)
end
end
local function handleMessage(event, side, channel, replyChannel, message, distance)
if type(message) == "table" then
if message.type == "join" then
log(message.username .. " connected.")
broadcast({ type = "system", content = message.username .. " joined the chat." })
elseif message.type == "leave" then
log(message.username .. " disconnected.")
broadcast({ type = "system", content = message.username .. " left the chat." })
elseif message.type == "chat" then
log(message.username .. ": " .. message.content)
broadcast({ type = "chat", username = message.username, content = message.content })
end
end
end
local function listenLoop()
if not modem then
log("No modem found!")
return
end
modem.open(SERVER_PORT)
log("Listening on port " .. SERVER_PORT)
while true do
local eventData = {os.pullEvent("modem_message")}
-- event, side, channel, replyChannel, message, distance
if eventData[3] == SERVER_PORT then
handleMessage(table.unpack(eventData))
end
end
end
local function main()
parallel.waitForAll(listenLoop)
end
return { main = main }

12
server/main.lua Normal file
View File

@@ -0,0 +1,12 @@
local kernel = require("kernel")
local chat = kernel.addServerHandler("chat-server")
local function run()
print("Starting Server...")
local tasks = {}
table.insert(tasks, chat.main)
parallel.waitForAll(table.unpack(tasks))
end
return { run = run }

7
server/startup.lua Normal file
View File

@@ -0,0 +1,7 @@
shell.execute("rm", "kernel.lua")
shell.execute("wget", "https://git.astrocore.space/root/nova-corp/raw/branch/main/kernel.lua")
sleep(5)
local kernel = require("kernel")
local main = kernel.addFolderDriver("server", "main")
main.run()

BIN
sounds/notification.dfpwm Normal file

Binary file not shown.

98
speaker-driver.lua Normal file
View File

@@ -0,0 +1,98 @@
local speaker = peripheral.find("speaker")
local dfpwm = require("cc.audio.dfpwm")
local encoder = dfpwm.make_encoder()
local decoder = dfpwm.make_decoder()
local sounds = {
{name = "notification", file = "notification.dfpwm"}
}
local function initialize()
kernel = require("kernel")
kernel.addSound("notification")
speaker = peripheral.find("speaker")
if not speaker then
print("Warning: No speaker attached.")
return false
end
return true
end
local function play(name)
if not speaker then return false end
local filePath
for _, sound in ipairs(sounds) do
if sound.name == name then
filePath = sound.file
break
end
end
if not filePath then
print("Sound '" .. name .. "' not defined.")
return false
end
if not fs.exists(filePath) then
print("File '" .. filePath .. "' not found.")
return false
end
-- Create a new decoder for this playback to reset state
local decoder = dfpwm.make_decoder()
for chunk in io.lines(filePath, 16 * 1024) do
local buffer = decoder(chunk)
while not speaker.playAudio(buffer, 3.0) do
os.pullEvent("speaker_audio_empty")
end
end
return true
end
----------------
local function getFileName(name)
local extension = ".dfpwm"
local fullFile = name .. extension
return fullFile
end
local function randomFileName()
local name = ""
for i = 1, 12 do
name = name .. string.char(math.random(97, 122)) -- az
end
return name
end
local function playSound(fileName)
local fileStream = getFileName(fileName)
local values = io.lines(fileStream, 16 * 1024)
for input in values do
local decoded = decoder(input)
while not speaker.playAudio(decoded, 3.0) do
os.pullEvent("speaker_audio_empty")
end
end
end
local function playTTSFile(value)
local message = textutils.urlEncode(value)
local url = "http://api.astrocore.space/api/TextToSpeech?message=" .. message
local response, err = http.get { url = url, binary = true }
local name = randomFileName()
local fileName = name .. ".dfpwm"
local fileData = response.readAll()
local file = fs.open(fileName,"w")
file.write(fileData)
file.close()
response.close()
playSound(name)
shell.execute("rm", fileName)
end
return { initialize = initialize, play = play, playTTSFile = playTTSFile }

146
task-manager.lua Normal file
View File

@@ -0,0 +1,146 @@
local monitor = peripheral.find("monitor")
local programs = {}
local isRunning = false
local function addProgram(name, program)
table.insert(programs, {name = name, program = program})
end
local function runProgram(programOrPath)
local programModule
if type(programOrPath) == "string" then
if fs.exists(programOrPath) then
-- Load the file as a chunk. In Lua 5.2+ use the env parameter instead of setfenv.
local env = _ENV or _G
local programChunk, err = loadfile(programOrPath, "t", env)
if not programChunk then
print("Error loading program: " .. err)
return false
end
-- Run the chunk to get the module table
local success, result = pcall(programChunk)
if not success then
print("Error executing program file: " .. tostring(result))
return false
end
programModule = result
else
print("Program not found: " .. programOrPath)
return false
end
elseif type(programOrPath) == "table" then
programModule = programOrPath
else
print("Invalid program type")
return false
end
if type(programModule) ~= "table" or not programModule.main then
print("Error: Program must return a table with at least a 'main' function.")
return false
end
-- Setup Monitor
local w, h = monitor.getSize()
monitor.setBackgroundColor(colors.black)
monitor.clear()
-- Draw X button
monitor.setCursorPos(w, 1)
monitor.setBackgroundColor(colors.red)
monitor.setTextColor(colors.white)
monitor.write("X")
monitor.setBackgroundColor(colors.black)
-- Create window for program
local win = window.create(monitor, 1, 2, w, h - 1)
local oldTerm = term.redirect(win)
-- Lifecycle: START
if programModule.start then
local ok, err = pcall(programModule.start)
if not ok then
print("Error in start(): " .. tostring(err))
term.redirect(oldTerm)
return false
end
end
-- Lifecycle: MAIN (Run in parallel with exit watcher)
local function runMain()
-- We wrap main in pcall to catch errors without crashing the driver
local ok, err = pcall(programModule.main)
if not ok then
win.setTextColor(colors.red)
win.write("Runtime Error: " .. tostring(err))
sleep(2) -- Let user see error
end
end
local function watchExit()
while true do
local _, _, x, y = os.pullEvent("monitor_touch")
if x == w and y == 1 then
return
end
end
end
parallel.waitForAny(runMain, watchExit)
-- Lifecycle: STOP
if programModule.stop then
pcall(programModule.stop)
end
term.redirect(oldTerm)
return true
end
local function exitProgram()
isRunning = false
end
local function drawList()
monitor.clear()
monitor.setCursorPos(1, 1)
monitor.write("Available Programs:")
for i, program in ipairs(programs) do
monitor.setCursorPos(1, i + 2) -- Start from line 3
monitor.write(i .. ". " .. program.name)
end
monitor.setCursorPos(1, #programs + 4)
monitor.write("Click to run")
end
local function listPrograms()
isRunning = true
while isRunning do
drawList()
local event, side, x, y = os.pullEvent("monitor_touch")
local programIndex = y - 2
if programIndex > 0 and programIndex <= #programs then
local program = programs[programIndex]
monitor.clear()
monitor.setCursorPos(1,1)
monitor.write("Running " .. program.name .. "...")
sleep(0.5)
runProgram(program.program)
end
end
end
return {
addProgram = addProgram,
runProgram = runProgram,
exitProgram = exitProgram,
listPrograms = listPrograms
}