initialize

This commit is contained in:
Andrew-71 2024-03-10 23:45:44 +03:00
commit 5b23ea99fd
43 changed files with 2336 additions and 0 deletions

8
Dash/dash.lua Normal file
View file

@ -0,0 +1,8 @@
local m = peripheral.find("monitor")
m.setTextScale(0.5)
local pixelbox = require("./util/pixelbox_lite")
local screen = pixelbox.new(m)
screen:set_pixel(3, 2, 1)
screen:render()
print(screen:get_size())

184
Dash/util/pixelbox_lite.lua Normal file
View file

@ -0,0 +1,184 @@
--[[
Made by O. Caha (9551-Dev), licensed under MIT
Modified by Andrew_7_1
]]
local PIXELBOX = {}
local OBJECT = {}
local t_sort,t_cat,s_char = table.sort,table.concat,string.char
local function sort(a,b) return a[2] > b[2] end
local distances = {
{5,256,16,8,64,32},
{4,16,16384,256,128},
[4] ={4,64,1024,256,128},
[8] ={4,512,2048,256,1},
[16] ={4,2,16384,256,1},
[32] ={4,8192,4096,256,1},
[64] ={4,4,1024,256,1},
[128] ={6,32768,256,1024,2048,4096,16384},
[256] ={6,1,128,2,512,4,8192},
[512] ={4,8,2048,256,128},
[1024] ={4,4,64,128,32768},
[2048] ={4,512,8,128,32768},
[4096] ={4,8192,32,128,32768},
[8192] ={3,32,4096,256128},
[16384]={4,2,16,128,32768},
[32768]={5,128,1024,2048,4096,16384}
}
local to_blit = {}
for i = 0, 15 do
to_blit[2^i] = ("%x"):format(i)
end
function PIXELBOX.RESTORE(BOX,color)
local bc = {}
for y=1,BOX.height*3 do
for x=1,BOX.width*2 do
if not bc[y] then bc[y] = {} end
bc[y][x] = color
end
end
BOX.CANVAS = bc
end
local function build_drawing_char(a,b,c,d,e,f)
local arr = {a,b,c,d,e,f}
local c_types = {}
local sortable = {}
local ind = 0
for i=1,6 do
local c = arr[i]
if not c_types[c] then
ind = ind + 1
c_types[c] = {0,ind}
end
local t = c_types[c]
local t1 = t[1] + 1
t[1] = t1
sortable[t[2]] = {c,t1}
end
local n = #sortable
while n > 2 do
t_sort(sortable,sort)
local bit6 = distances[sortable[n][1]]
local index,run = 1,false
local nm1 = n - 1
for i=2,bit6[1] do
if run then break end
local tab = bit6[i]
for j=1,nm1 do
if sortable[j][1] == tab then
index = j
run = true
break
end
end
end
local from,to = sortable[n][1],sortable[index][1]
for i=1,6 do
if arr[i] == from then
arr[i] = to
local sindex = sortable[index]
sindex[2] = sindex[2] + 1
end
end
sortable[n] = nil
n = n - 1
end
local n = 128
local a6 = arr[6]
if arr[1] ~= a6 then n = n + 1 end
if arr[2] ~= a6 then n = n + 2 end
if arr[3] ~= a6 then n = n + 4 end
if arr[4] ~= a6 then n = n + 8 end
if arr[5] ~= a6 then n = n + 16 end
if sortable[1][1] == arr[6] then
return s_char(n),sortable[2][1],arr[6]
else
return s_char(n),sortable[1][1],arr[6]
end
end
function OBJECT:render()
local t = self.term
local blit_line,set_cursor = t.blit,t.setCursorPos
local w_double = self.width*2
local canv = self.CANVAS
local sy = 0
for y=1,self.height*3,3 do
sy = sy + 1
local layer_1 = canv[y]
local layer_2 = canv[y+1]
local layer_3 = canv[y+2]
local char_line,fg_line,bg_line = {},{},{}
local n = 0
for x=1,w_double,2 do
local xp1 = x+1
local b11,b21,b12,b22,b13,b23 =
layer_1[x],layer_1[xp1],
layer_2[x],layer_2[xp1],
layer_3[x],layer_3[xp1]
local char,fg,bg = " ",1,b11
if not (b21 == b11
and b12 == b11
and b22 == b11
and b13 == b11
and b23 == b11) then
char,fg,bg = build_drawing_char(b11,b21,b12,b22,b13,b23)
end
n = n + 1
char_line[n] = char
fg_line [n] = to_blit[fg]
bg_line [n] = to_blit[bg]
end
set_cursor(1,sy)
blit_line(
t_cat(char_line,""),
t_cat(fg_line,""),
t_cat(bg_line,"")
)
end
end
function OBJECT:clear(color)
PIXELBOX.RESTORE(self,color)
end
function OBJECT:set_pixel(x,y,color)
self.CANVAS[y][x] = color
end
function OBJECT:get_size()
return #(self.CANVAS), #(self.CANVAS[1])
end
function PIXELBOX.new(terminal,bg)
local bg = bg or terminal.getBackgroundColor() or colors.black
local BOX = {}
local w,h = terminal.getSize()
BOX.term = terminal
setmetatable(BOX,{__index = OBJECT})
BOX.width = w
BOX.height = h
PIXELBOX.RESTORE(BOX,bg)
return BOX
end
return PIXELBOX

19
Dash/widgets/ART.md Normal file
View file

@ -0,0 +1,19 @@
Changes through time:
* Morning
* Day
* Evening
* Night
Changes with weather:
* Sunny
* Cloudy
* Rainy
* Stormy (diff from rain?)
* Snowy - alternative to rainy
Changes with dates:
<this assumes some things present on the scene>
Fireworks
Lowered flag

10
Dash/widgets/README.md Normal file
View file

@ -0,0 +1,10 @@
# Widgets I'll need
target: 5x3 monitors big (200x114 pixels allegedly)
### Essential
* Time (with zones?)
* Weather
* Some kind of feed?
### Cool
* Dynamic cityscape that changes with time and weather

1
GLONASS/README.md Normal file
View file

@ -0,0 +1 @@
I originally made this project in 2022 as a proof of concept.

63
GLONASS/UNS.lua Normal file
View file

@ -0,0 +1,63 @@
--[[
Unitary Navigational System v0.0
This basic script attempts to consolidate a GPS constellation into a single computer. Because using 4 is plain wasteful!
It was made out of project "GLONASS Tower" I made in 2022 after discovering CC
TODO: Bring back cool logging!
]]
-- Modems declaration. Edit this
local modems = {
{
id = "modem_id_here",
pos = {
x = 1,
y = 1,
z = 1
},
modem = nil,
primary = true -- Highest modem, to increase coverage ever so slightly
}
}
-- Prepare all modems
local primary_modem = nil
for i = 1 .. #modems do
modems[i].modem = peripheral.wrap(modems[i].id)
if modems[i].primary then
primary_modem = modems[i].modem
end
end
if primary_modem == nil then
primary_modem = modems[0].modem
end
primary_modem.open(gps.CHANNEL_GPS) -- Only *listen* on 1 modem to avoid repeats
-- Simple logging. It's not ideal but will do as a "temporary" solution
local function log(message)
term.write(message)
term.setCursorPos(1, select(2, term.getCursorPos()) + 1)
if select(2, term.getCursorPos()) >= select(2, term.getSize()) then
term.scroll(1)
term.setCursorPos(1, select(2, term.getCursorPos()) - 1)
end
end
term.clear()
term.setCursorPos(1, 1)
log("UNS v0.0 starting...")
while true do
-- Wait for a GPS query
local event, side, channel, replyChannel, message, distance
repeat
event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
until channel == gps.CHANNEL_GPS and message == "PING"
log("Recieved request, distance: " .. distance)
-- Send data from all modems
for _, modem in ipairs(modems) do
modem.transmit(channel, replyChannel, modem.pos)
log("Modem ID <" .. modem.id .. "> transmitted location")
end
end

View file

@ -0,0 +1,95 @@
local graphics = require("graphics")
local modem = peripheral.find('modem')
if not modem then error('No modem found') end
modem.open(7101)
local dfpwm = require("cc.audio.dfpwm")
local speaker = peripheral.find("speaker")
if not speaker then error('No speaker found') end
local decoder = dfpwm.make_decoder()
local status_vars = {
is_playing = true,
is_paused = false,
current_second = 0,
track_index = 1,
start_index = 1,
}
local cfg_vars = {
forward_skip = 5,
backward_skip = 5,
port = 7101,
chunk_size = 0.5
}
local function get_catalogue()
modem.transmit(7101, 7101, {type = 'query'})
local event, sides, channel, replyChannel, message, distance = os.pullEvent('modem_message')
return message.audio_catalogue
end
local audio_catalogue = get_catalogue()
local function music_loop()
local play_music = require("playback")
while true do
status_vars.current_second = play_music(status_vars, cfg_vars, audio_catalogue, modem)
sleep()
end
end
local function draw_ui_loop()
while true do
if not status_vars.is_playing then
graphics.draw_track_list(1, 1, audio_catalogue, status_vars.start_index, select(2, term.getSize()) - 1, term)
else
graphics.draw_track_list(1, 1, audio_catalogue, status_vars.start_index, select(2, term.getSize()) - 4, term)
end
sleep(1)
end
end
local function control_loop()
while true do
local event, button, x, y = os.pullEvent("mouse_click")
local btn_actions = {
pause = {
x = x + math.floor((select(1, term.getSize()) - 11) / 2) + 5,
y = select(2, term.getSize()) - 1,
func = function ()
status_vars.is_paused = not status_vars.is_paused
end
},
forward = {
x = math.floor((select(1, term.getSize()) - 11) / 2) + 9,
y = select(2, term.getSize()) - 1,
func = function ()
status_vars.current_second = status_vars.current_second + cfg_vars.forward_skip
end
},
backward = {
x = math.floor((select(1, term.getSize()) - 11) / 2) + 1,
y = select(2, term.getSize()) - 1,
func = function ()
status_vars.current_second = status_vars.current_second - cfg_vars.backward_skip
end
}
}
if x == 3 and y == 1 then return end
term.setCursorPos(btn_actions.pause.x, btn_actions.pause.y)
term.write('%')
for i, v in pairs(btn_actions) do
if x == v.x and y == v.y then
v.func()
break
end
end
end
end
term.clear() -- Clear terminal
parallel.waitForAny(music_loop, draw_ui_loop, control_loop) -- Start loops

View file

@ -0,0 +1,167 @@
local api = {}
api.symbols = {
audio_controls = {
play = "\16",
pause = "\19",
forward = "\16",
backward = "\17",
full_back = "\171"
},
app_controls = {
up = "\30",
down = "\31",
download = "\25",
play = "\16"
},
misc = {
music = "\15",
}
}
-- Draw a spinner loop at a specified position. Useful for waitForAny.
function api.spinner_loop(x, y, spin_monitor)
local spin_chars = {
"\139",
"\138",
"\142",
"\140",
"\141",
"\133",
"\135",
"\131"
}
local index = 1
while true do
spin_monitor.setCursorPos(x, y)
spin_monitor.write(spin_chars[index])
index = index + 1
if index > #spin_chars then index = index - #spin_chars end
sleep(0.1)
end
end
--[[
Function to create simple progress bars of any length.
size - length of the bar in pixels, taken, max - ratio of the bar that should be full.
There is an ability to set custom symbols for full and empty pixels,
however it is not necessary and by default the program uses '#' and '.' for these
]]
function api.make_progressbar(size, taken, max, char_full, char_empty)
--[[
Maths behind the function are quite simple:
taken/max = x/size
taken * size = x * max
x = taken * size / max
]]
local full_symbol, empty_symbol = char_full or '#', char_empty or '.'
size = size - 2 -- make space for "[" and "]"
local progress = math.floor(size * taken / max)
local hash = (full_symbol):rep(progress)
local dots = (empty_symbol):rep(size - progress)
return ("[" .. hash .. dots .. "]")
end
local function seconds_to_string(seconds)
-- int to mm:ss
local minutes = math.floor(seconds / 60)
local seconds = seconds - minutes * 60
return string.format("%02d:%02d", minutes, seconds)
end
function api.draw_audio_bar(x, y, progress_seconds, track_info, is_paused, monitor)
--[[
Example:
| Among us soundtrack lol |
| [<] [####........[<]............] [>] |
| 12:43/14:31 |
]]
local x_max, y_max = monitor.getSize()
-- Title
monitor.setCursorPos(x, y)
local title = track_info.title
if #title > x_max - 2 then
title = title:sub(1, x_max - 4) .. ".."
end
monitor.write((' '):rep(math.floor((x_max - #title) / 2)) .. title .. (' '):rep(math.ceil((x_max - #title) / 2)))
-- Progress bar
monitor.setCursorPos(x, y + 1)
local progress_bar = api.make_progressbar(x_max - 16, progress_seconds, track_info.length)
--monitor.blit(' ' .. seconds_to_string(progress_seconds) .. ' ' .. progress_bar .. ' ' .. seconds_to_string(track_info.length), 'f00000f0' .. ('3'):rep(x_max - 16) .. '0f00000f', ('f'):rep(x_max))
monitor.write(' ' .. seconds_to_string(progress_seconds) .. ' ' .. progress_bar .. ' ' .. seconds_to_string(track_info.length))
-- Controls
local controls = {
api.symbols.audio_controls.backward,
is_paused and api.symbols.audio_controls.play or api.symbols.audio_controls.pause,
api.symbols.audio_controls.forward
}
for i, control in ipairs(controls) do
monitor.setCursorPos(x + math.floor((x_max - 11) / 2) + (i - 1) * 4, y + 1)
monitor.write('[' .. control .. ']')
end
end
-- Copy of draw_audio_bar, but for when no track is played - just a blank bar.
function api.draw_track_list(x, y, audio_catalogue, start_index, height, monitor)
height = height or select(2, monitor.getSize())
local x_max, y_max = monitor.getSize()
--[[
top bar:
"[X] 21:00:01 Tue 12"
-button to close the app
-time and date
bottom bar:
"[up_btn] [down_btn] Showing [current_max_index - list_size]-[current_max_index] of [#audio_catalogue] | [REFRESH]"
-buttons to go up and down the list
-current index and total number of tracks
-button to refresh the list
list items:
"[index] [title] [length] [play_btn] [download_btn]"
-index - number of the track in the list
-title - title of the track
-length - length of the track
-play_btn - button to play the track
-download_btn - button to download the track (not implemented yet)
]]
-- top bar
monitor.setCursorPos(x, y)
local current_time = os.date("%H:%M:%S %b %d")
local exit_btn = "[X]"
monitor.blit(' ' .. exit_btn .. ' ' ..current_time, 'f0e0f00000000f000f00', 'ffffffffffffffffffff')
-- bottom bar
monitor.setCursorPos(x, y + height - 1)
local bottom_btns = '[' .. api.symbols.app_controls.up .. '] [' .. api.symbols.app_controls.down .. ']'
local showing = 'Showing ' .. tostring(start_index) .. '-' .. tostring(start_index + height - 2) .. ' of ' .. tostring(#audio_catalogue)
local refresh_btn = '[REFRESH]'
monitor.blit(' ' .. bottom_btns .. ' ' .. showing .. ' | ' .. refresh_btn, 'f0e0f0b0f' .. ('0'):rep(#showing) .. "f0f033333330", ('f'):rep(#bottom_btns + #showing + #refresh_btn + 5))
-- monitor.write(' ' .. bottom_btns .. ' ' .. showing .. ' | ' .. refresh_btn)
-- list items
-- i should be current_max_index - height - 2 or 1 if current_max_index - height - 2 < 1
for i = start_index, math.min(start_index + height - 2, #audio_catalogue) do
if (y + 1 + i >= y + height - 1) or i > #audio_catalogue then break end
local track = audio_catalogue[i]
local index = tostring(i)
local title = track.title
local length = seconds_to_string(track.length)
local play_btn = api.symbols.audio_controls.play
local download_btn = api.symbols.app_controls.download
local list_item = ' ' .. index .. ' ' .. title .. ' ' .. length .. ' ' .. play_btn .. ' ' .. download_btn
monitor.setCursorPos(x, y + 1 + i)
monitor.write(list_item)
end
end
return api

View file

@ -0,0 +1,27 @@
local dfpwm = require("cc.audio.dfpwm")
local speaker = peripheral.find("speaker")
local decoder = dfpwm.make_decoder()
local graphics = require("graphics")
local function play_music(status_vars, cfg_vars, audio_catalogue, modem)
if (not status_vars.is_playing) or status_vars.is_paused then return 0 end
modem.transmit(7101, 7101, {type = 'track_chunk',
index = status_vars.track_index,
starting_second = status_vars.current_second,
length = cfg_vars.chunk_size})
local event, sides, channel, replyChannel, message, distance = os.pullEvent('modem_message')
local buffer = decoder(message.track)
while not speaker.playAudio(buffer) do
os.pullEvent("speaker_audio_empty")
graphics.draw_audio_bar(1, select(2, term.getSize()) - 2, status_vars.current_second + cfg_vars.chunk_size, audio_catalogue[status_vars.track_index], status_vars.is_paused, term)
end
return status_vars.current_second + cfg_vars.chunk_size
end
return play_music

View file

@ -0,0 +1,59 @@
local touch_processer = {}
touch_processer.init = function ()
local self = {}
function self.process_minimised_controls()
local x, y = e[3], e[4]
local btn_actions = {
pause = {
x = pause_btn_coords.x,
y = pause_btn_coords.y,
func = function()
status_vars.is_paused = not status_vars.is_paused
end
},
forward = {
x = forward_btn_coords.x,
y = forward_btn_coords.y,
func = function()
status_vars.current_second = math.min(status_vars.current_second + cfg_vars.forward_skip, status_vars.max_seconds)
end
},
backward = {
x = backward_btn_coords.x,
y = backward_btn_coords.y,
func = function()
status_vars.current_second = math.max(status_vars.current_second - cfg_vars.backward_skip, 0)
end
},
exit = {
x = exit_btn_coords.x,
y = exit_btn_coords.y,
func = function()
status_vars.is_playing = false
return
end
},
minimise = {
x = minimise_btn_coords.x,
y = minimise_btn_coords.y,
func = function()
status_vars.is_minimised = not status_vars.is_minimised
end
},
}
for k, v in pairs(btn_actions) do
if x == v.x and y == v.y then
v.func()
end
end
end
return self
end
local function key_to_func()
end

View file

@ -0,0 +1,98 @@
--[[
Class to manage ComputerCraft (CC) audio tracks on different disks
Written by Andrew_7_1
]]
local audio_manager = {}
audio_manager.init = function (dirs_file)
local self = {}
-- Load audio directories from file where directories are seperated by newlines"
local function load_audio_dirs(file_location)
local audio_dirs = {}
local file = io.open(file_location, 'r')
if file then
for line in file:lines() do
table.insert(audio_dirs, line)
end
file:close()
end
return audio_dirs
end
local audio_dirs = load_audio_dirs(dirs_file or 'audio_dirs.txt')
--[[
Iterate over all audio directories and load all audio files into a catalogue.
Audio files have extensions of .dfpwm and rarely .wav
Save them to a table of tables like this:
{
["filepath"] = absolute file path
["length"] = file size / 6000
["title"] = file name without extension and path, e.g. "title.dfpwm" -> "title"
["extension"] = file extension, e.g. "dfpwm"
}
]]
local function load_audio_catalogue()
local audio_catalogue = {}
for _, dir in pairs(audio_dirs) do
for _, file in pairs(fs.list(dir)) do
if file:find('.dfpwm') or file:find('.wav') then
local file_path = dir .. '/' .. file
local file_size = fs.getSize(file_path)
local file_title = file:sub(1, #file - #file:match('.*%.(.*)$'))
local file_extension = file:match('.*%.(.*)$')
table.insert(audio_catalogue, {
["filepath"] = file_path,
["length"] = file_size / 6000,
["title"] = file_title,
["extension"] = file_extension
})
end
end
end
return audio_catalogue
end
self.audio_catalogue = load_audio_catalogue()
-- Update audio catalogue
function self.update_catalogue()
self.audio_catalogue = load_audio_catalogue()
end
function self.get_catalogue()
return self.audio_catalogue
end
-- Get part of an audio file in the catalogue
function self.get_track_chunk(index, starting_second, length)
if index > #self.audio_catalogue then
return false
end
local track = self.audio_catalogue[index]
local file = io.open(track.filepath, 'rb')
if not file then
return false
end
file:seek('set', starting_second * 6000)
local chunk = file:read(length * 6000)
file:close()
return chunk
end
-- More convenient way to get track info than using entire catalogue
function self.get_track_details(index)
if index > #self.audio_catalogue then
return false
end
return self.audio_catalogue[index]
end
return self
end
return audio_manager

View file

@ -0,0 +1,60 @@
local logger = {}
logger.init = function(monitor_wrap, scale)
local self = {}
local monitor = monitor_wrap or peripheral.find("monitor")
if not monitor then
monitor = term
else monitor.setTextScale(scale or 0.5)
end
local log_colours = {
['log'] = colours.white,
['info'] = colours.lightBlue,
['warn'] = colours.yellow,
['err'] = colours.red,
['success'] = colours.lime
}
local function move_line()
monitor.setCursorPos(1, select(2, monitor.getCursorPos()) + 1)
if select(2, monitor.getCursorPos()) >= select(2, monitor.getSize()) then
monitor.scroll(1)
monitor.setCursorPos(1, select(2, monitor.getCursorPos()) - 1)
end
end
local function write_msg(message, msg_colour)
local time_str = os.date('%Y-%b-%d %H:%M:%S')
local msg_formatted = "[" .. time_str .. "] " .. message
monitor.setTextColour(msg_colour)
monitor.write(msg_formatted)
move_line()
end
function self.log(msg)
write_msg(msg, log_colours['log'])
end
function self.info(msg)
write_msg('INFO: ' .. msg, log_colours['info'])
end
function self.warning(msg)
write_msg('WARNING: ' .. msg, log_colours['warn'])
end
function self.error(msg)
write_msg('ERROR: ' .. msg, log_colours['err'])
end
function self.success(msg)
write_msg('SUCCESS: ' .. msg, log_colours['success'])
end
return self
end
return logger

View file

@ -0,0 +1,60 @@
--[[
Audio server for ComputerCraft (CC)
Written by Andrew_7_1
]]
local server = {}
server.init = function (port, audio_dir)
local self = {}
local logger = require('logger').init()
local audio_manager = require('audio_manager').init(audio_dir or 'audio_dirs.txt')
local port = port or 7101
local modem = peripheral.find('modem')
if not modem then error('No modem found') end
modem.open(port)
logger.info('Audio server started on port ' .. port .. ' with audio directories from ' .. audio_dir)
local message_types = {
query = function (replyChannel, msg)
modem.transmit(replyChannel, port, {code = 200, audio_catalogue = audio_manager.get_catalogue()})
logger.success('Query sent')
end,
refresh = function (replyChannel, msg)
audio_manager.update_catalogue()
modem.transmit(replyChannel, port, {code = 200})
logger.success('Catalogue updated')
end,
track_info = function (replyChannel, msg)
local track = audio_manager.get_track_details(msg.index)
if not track then
modem.transmit(replyChannel, port, {code = 404})
logger.error('Track not found (id: ' .. msg.index .. ')')
else
modem.transmit(replyChannel, port, {code = 200, track = track})
logger.success('Track details sent (id: ' .. msg.index .. ')')
end
end,
track_chunk = function (replyChannel, msg)
local track = audio_manager.get_track_chunk(msg.index, msg.starting_second, msg.length)
if not track then
modem.transmit(replyChannel, port, {code = 404})
logger.error('Track not found (id: ' .. msg.index .. ')')
else
modem.transmit(replyChannel, port, {code = 200, track = track})
logger.success('Part of a track sent (id: ' .. msg.index .. ', part:' .. msg.starting_second .. '-' .. msg.starting_second + msg.length .. ')')
end
end
}
function self.process_transmission(event, side, channel, replyChannel, message, distance)
message_types[message.type](replyChannel, message)
end
return self
end
return server

View file

@ -0,0 +1,14 @@
local config = {
port = 7101,
audio_dir = 'audio_dirs.txt'
}
local server = require("server").init(config.port, config.audio_dir)
local modem = peripheral.find("modem")
if not modem then error("No modem found") end
modem.open(config.port)
while true do
local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
server.process_transmission(event, side, channel, replyChannel, message, distance)
end

4
README.md Normal file
View file

@ -0,0 +1,4 @@
# CC Projects
Collection of all my ComputerCraft projects, as none of them are big or finished enough to warrant separate repositories.
If you think that this is a disorganized mess then you are 100% correct.

128
RSS/feeds.lua Normal file
View file

@ -0,0 +1,128 @@
local FEEDS = {
feeds = {}
}
local FEEDS_PATH = "./feeds"
local SLAXML = require('slaxml')
local LOG = require('log')
-- Retrieve feeds from config files and prepare them for use
function FEEDS:Load()
self.feeds = {}
local files = fs.find(FEEDS_PATH .. "/*.json")
for _, v in ipairs(files) do
local file = fs.open(v, "r")
local contents = file.readAll()
local feed = textutils.unserialiseJSON(contents)
local response, err, _ = http.get(feed.link)
if response == nil then
LOG:Error("HTTP Error loading feed (" .. v .. "): " .. err)
else
local xml = response.readAll()
response.close()
local element
local in_item = false
local function startElement(name, nsURI, nsPrefix)
-- Track the current element name
element = name
if element == "item" then
in_item = true
end
end
local function text(text, cdata)
-- Store the text content based on the current element name
if element == 'title' and not feed.title then
feed.title = text
elseif in_item and not feed.last and element == 'guid' then
feed.last = text
end
end
local parser = SLAXML:parser{
startElement = startElement,
text = text
}
parser:parse(xml)
if feed.title_override then
feed.title = feed.title_override
end
table.insert(self.feeds, feed)
LOG:Info("Loaded feed " .. feed.title)
end
end
if #self.feeds == 0 then
LOG:Info("No feeds loaded, nothing to do")
return true
end
return nil
end
function FEEDS:ParseLatest()
local output = {}
for _, feed in ipairs(self.feeds) do
local response, err, _ = http.get(feed.link)
if response == nil then
LOG:Error("HTTP Error fetching feed: " .. err)
else
local xml = response.readAll()
response.close()
local element
local new_last
local done = false
local current_item = {
colour = feed.colour,
source = feed.title
}
local in_item = false
local function startElement(name, nsURI, nsPrefix)
if done then return end
-- Track the current element name
element = name
if element == "item" then
in_item = true
current_item = {
colour = feed.colour,
source = feed.title
}
end
end
local function closeElement(name, nsURI, nsPrefix)
if done then return end
if name == 'item' and not done then
table.insert(output, current_item)
end
end
local function text(text, cdata)
if done or not in_item then return end
-- Store the text content based on the current element name
if element == 'title' then
current_item.title = text
elseif in_item and element == 'guid' then
if not new_last then
new_last = text
end
if feed.last == text then
done = true
end
end
end
local parser = SLAXML:parser{
startElement = startElement,
closeElement = closeElement,
text = text
}
parser:parse(xml)
feed.last = new_last
end
end
return output
end
return FEEDS

5
RSS/feeds/nytimes.json Normal file
View file

@ -0,0 +1,5 @@
{
"link": "https://rss.nytimes.com/services/xml/rss/nyt/Technology.xml",
"title_override": "NYT",
"colour": "9"
}

5
RSS/feeds/tass.json Normal file
View file

@ -0,0 +1,5 @@
{
"link": "https://tass.ru/rss/v2.xml",
"title_override": "TACC",
"colour": "b"
}

View file

@ -0,0 +1,5 @@
{
"link": "https://www.youtube.com/@tag",
"title_override": "Youtuber name",
"colour": "e"
}

71
RSS/graphics.lua Normal file
View file

@ -0,0 +1,71 @@
local GRAPHICS = {
target = nil,
contents = {}
}
local LOG = require('log')
-- Set target's colorscheme to gruvbox for better visuals
function GRAPHICS:SetColours()
if self.target == nil then
return
end
self.target.setPaletteColour(colours.red, 0xfb4934)
self.target.setPaletteColour(colours.blue, 0x83a598)
self.target.setPaletteColour(colours.green, 0xb8bb26)
self.target.setPaletteColour(colours.purple, 0xd3869b)
self.target.setPaletteColour(colours.cyan, 0x8ec07c)
self.target.setPaletteColour(colours.white, 0xf9f5d7)
self.target.setPaletteColour(colours.black, 0x1d2021)
end
-- Set target output for our graphics
function GRAPHICS:SetTarget(target)
self.target = target
if self.target == nil then
LOG:Error("Nil display target, items will not display")
end
target.clear()
target.setTextScale(0.5)
self:Refresh(true)
end
function GRAPHICS:Refresh(full)
if self.target == nil then
return
end
local _, y = self.target.getSize()
while #self.contents > y do
table.remove(self.contents, 1)
end
if full then
self.target.scroll(#self.contents)
for k, entry in ipairs(self.contents) do
self.target.setCursorPos(1, y - k + 1)
local output = "[" .. entry.source .. "] " .. entry.title
local fg_blit = "0" .. (entry.colour):rep(#entry.source) .. ("0"):rep(#entry.title + 2)
self.target.blit(output, fg_blit, ("f"):rep(#output))
end
else
self.target.scroll(1)
self.target.setCursorPos(1, y)
local entry = self.contents[#self.contents]
local output = "[" .. entry.source .. "] " .. entry.title
local fg_blit = "0" .. (entry.colour):rep(#entry.source) .. ("0"):rep(#entry.title + 2)
self.target.blit(output, fg_blit, ("f"):rep(#output))
end
end
function GRAPHICS:AddEntry(entry)
table.insert(self.contents, entry)
self:Refresh()
end
function GRAPHICS:Add(entries)
for _, entry in ipairs(entries) do
self:AddEntry(entry)
end
end
return GRAPHICS

98
RSS/log.lua Normal file
View file

@ -0,0 +1,98 @@
local LOG = {
target = term,
history = {},
history_cutoff = 50,
log_path = "./logs/",
debug = false
}
-- Set target's colorscheme to gruvbox for better visuals
function LOG:SetColours()
self.target.setPaletteColour(colours.red, 0xfb4934)
self.target.setPaletteColour(colours.blue, 0x83a598)
self.target.setPaletteColour(colours.green, 0xb8bb26)
self.target.setPaletteColour(colours.purple, 0xd3869b)
self.target.setPaletteColour(colours.cyan, 0x8ec07c)
self.target.setPaletteColour(colours.white, 0xf9f5d7)
self.target.setPaletteColour(colours.black, 0x1d2021)
end
-- Set target output for our log
function LOG:SetTarget(target)
self.target = target
target.clear()
target.setTextScale(0.5)
end
function LOG:SetDebug(value)
self.debug = value
end
-- Utility function to output some kind of log message
local function genericLog(log, level, message, col)
if #level ~= 4 or #col ~= 1 then
LOG:Error("Incorrect generic log format")
return
end
local target = log.target
target.scroll(1)
local _, y = target.getSize()
target.setCursorPos(1, y)
local time = os.date("%T", os.epoch("local") / 1000)
local output = "[" .. time .. "|" .. level .. "] " .. message
local fg_blit = ("0"):rep(#time + 2) .. (col):rep(4) .. ("0"):rep(#message + 2)
target.blit(output, fg_blit, ("f"):rep(#output))
table.insert(log.history, output)
if #log.history > log.history_cutoff then
table.remove(log.history, 1)
end
end
function LOG:Info(message)
genericLog(self, "INFO", message, "b")
end
function LOG:Success(message)
genericLog(self, "SCCS", message, "d")
end
function LOG:Debug(message)
if not self.debug then return end
genericLog(self, "DBUG", message, "8")
end
function LOG:Warning(message)
genericLog(self, "WARN", message, "4")
end
function LOG:Error(message)
genericLog(self, "ERRO", message, "e")
end
function LOG:Critical(message)
genericLog(self, "CRIT", message, "e")
self:Dump()
error("critical exception occured, read above")
end
function LOG:Clear()
self.history = {}
end
-- Put all recent log messages into a file for later study
function LOG:Dump()
local out = ""
for _, v in ipairs(self.history) do
out = out + v .. "\n"
end
local time = os.date("%F-%T", os.epoch("local") / 1000)
local file = fs.open(string.format("%s/dump_%s.log", self.log_path, time), "w")
file.write(out)
file.close()
return out
end
return LOG

28
RSS/main.lua Normal file
View file

@ -0,0 +1,28 @@
local LOG = require('log')
local GRAPHICS = require('graphics')
local FEEDS = require('feeds')
local REFRESH_INTERVAL = 8
local DEBUG = true
local MONITOR = assert(peripheral.find('monitor'), "No monitor detected")
local function main()
LOG:Info("RSS System init")
LOG:SetDebug(DEBUG)
LOG:SetTarget(term)
LOG:SetColours()
GRAPHICS:SetTarget(MONITOR)
GRAPHICS:SetColours()
local err = FEEDS:Load()
if err ~= nil then
return
end
LOG:Success("System fully loaded, beginning RSS processing")
while true do
GRAPHICS:Add(FEEDS:ParseLatest())
sleep(REFRESH_INTERVAL)
end
end
main()

259
RSS/slaxml.lua Normal file
View file

@ -0,0 +1,259 @@
--[=====================================================================[
v0.8 Copyright © 2013-2018 Gavin Kistner <!@phrogz.net>; MIT Licensed
See http://github.com/Phrogz/SLAXML for details.
--]=====================================================================]
local SLAXML = {
VERSION = "0.8",
_call = {
pi = function(target,content)
print(string.format("<?%s %s?>",target,content))
end,
comment = function(content)
print(string.format("<!-- %s -->",content))
end,
startElement = function(name,nsURI,nsPrefix)
io.write("<")
if nsPrefix then io.write(nsPrefix,":") end
io.write(name)
if nsURI then io.write(" (ns='",nsURI,"')") end
print(">")
end,
attribute = function(name,value,nsURI,nsPrefix)
io.write(' ')
if nsPrefix then io.write(nsPrefix,":") end
io.write(name,'=',string.format('%q',value))
if nsURI then io.write(" (ns='",nsURI,"')") end
io.write("\n")
end,
text = function(text,cdata)
print(string.format(" %s: %q",cdata and 'cdata' or 'text',text))
end,
closeElement = function(name,nsURI,nsPrefix)
io.write("</")
if nsPrefix then io.write(nsPrefix,":") end
print(name..">")
end,
}
}
function SLAXML:parser(callbacks)
return { _call=callbacks or self._call, parse=SLAXML.parse }
end
function SLAXML:parse(xml,options)
if not options then options = { stripWhitespace=false } end
-- Cache references for maximum speed
local find, sub, gsub, char, push, pop, concat = string.find, string.sub, string.gsub, string.char, table.insert, table.remove, table.concat
local first, last, match1, match2, match3, pos2, nsURI
local unpack = unpack or table.unpack
local pos = 1
local state = "text"
local textStart = 1
local currentElement={}
local currentAttributes={}
local currentAttributeCt -- manually track length since the table is re-used
local nsStack = {}
local anyElement = false
local utf8markers = { {0x7FF,192}, {0xFFFF,224}, {0x1FFFFF,240} }
local function utf8(decimal) -- convert unicode code point to utf-8 encoded character string
if decimal<128 then return char(decimal) end
local charbytes = {}
for bytes,vals in ipairs(utf8markers) do
if decimal<=vals[1] then
for b=bytes+1,2,-1 do
local mod = decimal%64
decimal = (decimal-mod)/64
charbytes[b] = char(128+mod)
end
charbytes[1] = char(vals[2]+decimal)
return concat(charbytes)
end
end
end
local entityMap = { ["lt"]="<", ["gt"]=">", ["amp"]="&", ["quot"]='"', ["apos"]="'" }
local entitySwap = function(orig,n,s) return entityMap[s] or n=="#" and utf8(tonumber('0'..s)) or orig end
local function unescape(str) return gsub( str, '(&(#?)([%d%a]+);)', entitySwap ) end
local function finishText()
if first>textStart and self._call.text then
local text = sub(xml,textStart,first-1)
if options.stripWhitespace then
text = gsub(text,'^%s+','')
text = gsub(text,'%s+$','')
if #text==0 then text=nil end
end
if text then self._call.text(unescape(text),false) end
end
end
local function findPI()
first, last, match1, match2 = find( xml, '^<%?([:%a_][:%w_.-]*) ?(.-)%?>', pos )
if first then
finishText()
if self._call.pi then self._call.pi(match1,match2) end
pos = last+1
textStart = pos
return true
end
end
local function findComment()
first, last, match1 = find( xml, '^<!%-%-(.-)%-%->', pos )
if first then
finishText()
if self._call.comment then self._call.comment(match1) end
pos = last+1
textStart = pos
return true
end
end
local function nsForPrefix(prefix)
if prefix=='xml' then return 'http://www.w3.org/XML/1998/namespace' end -- http://www.w3.org/TR/xml-names/#ns-decl
for i=#nsStack,1,-1 do if nsStack[i][prefix] then return nsStack[i][prefix] end end
error(("Cannot find namespace for prefix %s"):format(prefix))
end
local function startElement()
anyElement = true
first, last, match1 = find( xml, '^<([%a_][%w_.-]*)', pos )
if first then
currentElement[2] = nil -- reset the nsURI, since this table is re-used
currentElement[3] = nil -- reset the nsPrefix, since this table is re-used
finishText()
pos = last+1
first,last,match2 = find(xml, '^:([%a_][%w_.-]*)', pos )
if first then
currentElement[1] = match2
currentElement[3] = match1 -- Save the prefix for later resolution
match1 = match2
pos = last+1
else
currentElement[1] = match1
for i=#nsStack,1,-1 do if nsStack[i]['!'] then currentElement[2] = nsStack[i]['!']; break end end
end
currentAttributeCt = 0
push(nsStack,{})
return true
end
end
local function findAttribute()
first, last, match1 = find( xml, '^%s+([:%a_][:%w_.-]*)%s*=%s*', pos )
if first then
pos2 = last+1
first, last, match2 = find( xml, '^"([^<"]*)"', pos2 ) -- FIXME: disallow non-entity ampersands
if first then
pos = last+1
match2 = unescape(match2)
else
first, last, match2 = find( xml, "^'([^<']*)'", pos2 ) -- FIXME: disallow non-entity ampersands
if first then
pos = last+1
match2 = unescape(match2)
end
end
end
if match1 and match2 then
local currentAttribute = {match1,match2}
local prefix,name = string.match(match1,'^([^:]+):([^:]+)$')
if prefix then
if prefix=='xmlns' then
nsStack[#nsStack][name] = match2
else
currentAttribute[1] = name
currentAttribute[4] = prefix
end
else
if match1=='xmlns' then
nsStack[#nsStack]['!'] = match2
currentElement[2] = match2
end
end
currentAttributeCt = currentAttributeCt + 1
currentAttributes[currentAttributeCt] = currentAttribute
return true
end
end
local function findCDATA()
first, last, match1 = find( xml, '^<!%[CDATA%[(.-)%]%]>', pos )
if first then
finishText()
if self._call.text then self._call.text(match1,true) end
pos = last+1
textStart = pos
return true
end
end
local function closeElement()
first, last, match1 = find( xml, '^%s*(/?)>', pos )
if first then
state = "text"
pos = last+1
textStart = pos
-- Resolve namespace prefixes AFTER all new/redefined prefixes have been parsed
if currentElement[3] then currentElement[2] = nsForPrefix(currentElement[3]) end
if self._call.startElement then self._call.startElement(unpack(currentElement)) end
if self._call.attribute then
for i=1,currentAttributeCt do
if currentAttributes[i][4] then currentAttributes[i][3] = nsForPrefix(currentAttributes[i][4]) end
self._call.attribute(unpack(currentAttributes[i]))
end
end
if match1=="/" then
pop(nsStack)
if self._call.closeElement then self._call.closeElement(unpack(currentElement)) end
end
return true
end
end
local function findElementClose()
first, last, match1, match2 = find( xml, '^</([%a_][%w_.-]*)%s*>', pos )
if first then
nsURI = nil
for i=#nsStack,1,-1 do if nsStack[i]['!'] then nsURI = nsStack[i]['!']; break end end
else
first, last, match2, match1 = find( xml, '^</([%a_][%w_.-]*):([%a_][%w_.-]*)%s*>', pos )
if first then nsURI = nsForPrefix(match2) end
end
if first then
finishText()
if self._call.closeElement then self._call.closeElement(match1,nsURI) end
pos = last+1
textStart = pos
pop(nsStack)
return true
end
end
while pos<#xml do
if state=="text" then
if not (findPI() or findComment() or findCDATA() or findElementClose()) then
if startElement() then
state = "attributes"
else
first, last = find( xml, '^[^<]+', pos )
pos = (first and last or pos) + 1
end
end
elseif state=="attributes" then
if not findAttribute() then
if not closeElement() then
error("Was in an element and couldn't find attributes or the close.")
end
end
end
end
if not anyElement then error("Parsing did not discover any elements") end
if #nsStack > 0 then error("Parsing ended with unclosed elements") end
end
return SLAXML

3
RSS/todo.txt Normal file
View file

@ -0,0 +1,3 @@
* Add test file that checks entire system/functions before startup? or at least improve error checking
* Enhance customisation per-feed e.g. define 14 colours and set which color each char in feed title is
* Pre-Processing for russian language

96
TARDIM/NavDash.lua Normal file
View file

@ -0,0 +1,96 @@
local tardim = peripheral.find("digital_tardim_interface")
local screen = peripheral.find("monitor")
screen.clear()
screen.setCursorBlink(false)
screen.setTextScale(0.5)
-- 15x24
--[[
~ Current pos
X
Y
Z
Dimension
Facing
~ Destination
X
Y
Z
Dimension
Facing
~ Fuel
Remaining
Required
]]
-- This changes the colors to be like the gruvbox theme
local function set_colorscheme(screen)
screen.setPaletteColour(colours.red, 0xfb4934)
screen.setPaletteColour(colours.blue, 0x83a598)
screen.setPaletteColour(colours.green, 0xb8bb26)
screen.setPaletteColour(colours.purple, 0xd3869b)
screen.setPaletteColour(colours.cyan, 0x8ec07c)
screen.setPaletteColour(colours.white, 0xf9f5d7)
screen.setPaletteColour(colours.black, 0x1d2021)
end
local function dim_char(dimension_str)
if dimension_str == "minecraft:overworld" then
return "OWR"
elseif dimension_str == "minecraft:the_nether" then
return "NETH"
elseif dimension_str == "minecraft:the_end" then
return "END"
else
return "???"
end
end
local function drawPos(screen, pos, screen_y)
screen.setCursorPos(1, screen_y)
screen.blit("X: " .. pos.pos.x, "e" .. string.rep("0", #tostring(pos.pos.x) + 2), string.rep("f", #tostring(pos.pos.x) + 3))
screen.setCursorPos(1, screen_y + 1)
screen.blit("Y: " .. pos.pos.y, "d" .. string.rep("0", #tostring(pos.pos.y) + 2), string.rep("f", #tostring(pos.pos.y) + 3))
screen.setCursorPos(1, screen_y + 2)
screen.blit("Z: " .. pos.pos.z, "b" .. string.rep("0", #tostring(pos.pos.z) + 2), string.rep("f", #tostring(pos.pos.z) + 3))
screen.setCursorPos(1, screen_y + 3)
screen.write("Dimension: " .. dim_char(pos.dimension), "00000000000111", "ffffffffffffff")
screen.setCursorPos(1, screen_y + 4)
screen.write("Facing: " .. pos.facing)
end
set_colorscheme(screen)
screen.setCursorPos(1, 1)
screen.blit("> TARDIM NAV", "009999990999", "ffffffffffff")
while true do
screen.setCursorPos(1, 3)
local pos = tardim.getCurrentLocation()
screen.write("> Current pos")
drawPos(screen, pos, 4)
local dest = tardim.getTravelLocation()
screen.setCursorPos(1, 10)
screen.write("> Destination")
drawPos(screen, dest, 11)
local fuel = tardim.getFuel()
screen.setCursorPos(1, 17)
screen.write("> Fuel")
screen.setCursorPos(1, 18)
screen.write("REM.: " .. fuel .. "/100")
screen.setCursorPos(1, 19)
local Required = tardim.calculateFuelForJourney()
screen.write("REQ.: " .. Required)
screen.setCursorPos(1, 20)
screen.blit("ENOUGH: " .. (fuel >= Required and "YES" or "NO "), "00000000" .. (fuel >= Required and "ddd" or "eee"), "ffffffff" .. (fuel >= Required and "fff" or "fff"))
local inFlight = tardim.isInFlight()
screen.setCursorPos(1, 22)
screen.blit("IN FLIGHT: " .. (inFlight and "YES" or "NO "), "00000000000" .. (inFlight and "ddd" or "eef"), "fffffffffff" .. (inFlight and "fff" or "fff"))
end

85
TARDIM/NavDashTwo.lua Normal file
View file

@ -0,0 +1,85 @@
local tardim = peripheral.find("digital_tardim_interface")
local screen = peripheral.find("monitor")
-- This changes the colors to be like the gruvbox theme
local function SetColourscheme(screen, scheme)
local schemes = {
gruvbox = {
red = 0xfb4934,
blue = 0x83a598,
green = 0xb8bb26,
purple = 0xd3869b,
cyan = 0x8ec07c,
white = 0xf9f5d7,
black = 0x1d2021
},
starfield = {
red = 0xc72138,
blue = 0x304c7a,
yellow = 0xd7a64b,
white = 0xf4f5f7,
orange = 0xe06236,
black = 0x121212
}
}
for i, _ in pairs(schemes[scheme]) do
screen.setPaletteColour(colours[i], schemes[scheme][i])
end
end
local function DrawStripes(screen)
local stripes = {
[1] = 'b',
[2] = '4',
[3] = '1',
[4] = 'e'
}
local _, y = screen.getSize()
for h=1,y do
for w=1,4 do
screen.setCursorPos(w, h)
screen.blit(' ', '0', stripes[w])
end
end
end
local function DrawFuel(screen, fuel)
local bar_colours = {
[75] = 'd', -- green
[60] = '4', -- orange
[30] = '1', -- yellow
[1] = 'e' -- red
}
fuel = math.floor(fuel)
local col = bar_colours[1]
for i, _ in pairs(bar_colours) do
if fuel > i then
col = bar_colours[i]
break
end
end
local x, y = screen.getSize()
screen.setCursorPos(8, y - 2)
screen.write('FUEL - ' .. tostring(fuel))
screen.setCursorPos(8, y - 1)
screen.blit(string.rep(' ', x - 16), string.rep('8', x - 16), string.rep('8', x - 16))
screen.setCursorPos(8, y - 1)
screen.blit(string.rep(' ', x - 25), string.rep('8', x - 25), string.rep(col, x - 25))
end
SetColourscheme(screen, 'starfield')
screen.setCursorBlink(false)
screen.setTextScale(0.5)
screen.setTextColour(colours.black)
screen.setBackgroundColour(colours.white)
screen.clear()
DrawStripes(screen)
screen.setCursorPos(10, 5)
screen.write("Hello Universe!")
DrawFuel(screen, tardim.getFuel())

148
TUI71/TUI71-time.lua Normal file
View file

@ -0,0 +1,148 @@
--[[
Time module for TUI71
by Andrew-7-1
MIT License
]]
local time_api = {}
-- ASCII-art digits for bigger numbers
local ascii_digits = {
{
" 0000 ",
"00 00",
"00 00",
"00 00",
" 0000 "
},
{
"1111 ",
" 11 ",
" 11 ",
" 11 ",
"111111"
},
{
" 2222 ",
"22 22",
" 22 ",
" 22 ",
"222222",
},
{
" 3333 ",
"33 33",
" 333",
"33 33",
" 3333 ",
},
{
"44 44",
"44 44",
"444444",
" 44",
" 44"
},
{
"555555",
"55 ",
"55555 ",
" 55",
"55555 "
},
{
" 6666 ",
"66 ",
"66666 ",
"66 66",
" 6666 "
},
{
"777777",
" 77 ",
" 77 ",
" 77 ",
"77 "
},
{
" 8888 ",
"88 88",
" 8888 ",
"88 88",
" 8888 "
},
{
" 9999 ",
"99 99",
" 99999",
" 99",
" 9999 "
}
}
-- Seperator for time e.g. "22:01"
local seperator = {
" ",
" # ",
" ",
" # ",
" ",
}
-- Draw a big digit
function time_api.draw_digit(number, x, y, monitor, colour)
local digit = ascii_digits[number]
monitor.setCursorPos(x, y)
monitor.setTextColor(colour)
for i = 1, 5 do
monitor.write(digit[i])
monitor.setCursorPos(x, y + i)
end
end
-- Draw a big ascii-art clock type HH:MM.
function time_api.draw_time(x, y, monitor)
-- Get the current time
local h, m = tostring(os.date("%H")), tostring(os.date("%M"))
local h1, h2 = tonumber(string.sub(h, 1, 1)), tonumber(string.sub(h, 2, 2))
local m1, m2 = tonumber(string.sub(m, 1, 1)), tonumber(string.sub(m, 2, 2))
local s = os.date("%S")
monitor.setCursorPos(x, y) -- Set cursor position
-- Draw digits layer by layer
for i = 1, 5 do
monitor.setCursorPos(x, select(2, monitor.getCursorPos()) + 1)
monitor.write(ascii_digits[h1 + 1][i] .. " " .. ascii_digits[h2 + 1][i] .. seperator[i] .. ascii_digits[m1 + 1][i] .. " " .. ascii_digits[m2 + 1][i])
end
end
local function draw_timezone(x, y, tz_name, utc_offset, tz_monitor)
--[[
/SYD\
|22:34|
\_ _/
]]
if #tz_name ~= 3 then error("Invalid timezone name (not 3 letters)") end
local utc = os.date("!*t")
local tz_hour = utc.hour + utc_offset
if tz_hour > 23 then
tz_hour = -24 + tz_hour end
local day_part = {
["day"] = "",
["night"] = ""
}
local tz_symbol
if (tz_hour > 20 or tz_hour < 7) then tz_symbol = day_part["night"] else tz_symbol = day_part["day"] end
tz_monitor.setCursorPos(x, y)
tz_monitor.blit(" /" .. tz_name .. "\\", "f04440", "ffffff")
tz_monitor.setCursorPos(x, y + 1)
tz_monitor.write("|" .. tostring(utc.hour + utc_offset) .. ":" .. tostring(utc.min) .. "|")
tz_monitor.setCursorPos(x, y + 2)
tz_monitor.blit(" \\_" .. tz_symbol .. "_/ ", "f00400f", "fffffff")
end

9
TUI71/TUI71.lua Normal file
View file

@ -0,0 +1,9 @@
--[[
TUI-71 - An overly simple graphics library for CC computers.
by Andrew-7-1
MIT License
]]
local api = {}

3
apricot/README.md Normal file
View file

@ -0,0 +1,3 @@
If this ever gets worked on again, this was intended to log **all** messages on **all** possible channels, reverse tria- wait no there's a different word for this. Basically global spy monitoring station
If you're wondering why it's named apricot, I literally had no idea and randomly thought about throwing apricots at people in Hitman 3. **Yes.**

58
apricot/node/logger.lua Normal file
View file

@ -0,0 +1,58 @@
local logger = {}
logger.init = function(monitor_wrap, scale)
local self = {}
local monitor = monitor_wrap or peripheral.find("monitor")
if not monitor then
monitor = term
else monitor.setTextScale(scale or 0.5)
end
local log_colours = {
['log'] = colours.white,
['info'] = colours.lightBlue,
['warn'] = colours.yellow,
['err'] = colours.red,
['success'] = colours.lime
}
local function move_line()
monitor.setCursorPos(1, select(2, monitor.getCursorPos()) + 1)
if select(2, monitor.getCursorPos()) >= select(2, monitor.getSize()) then
monitor.scroll(1)
monitor.setCursorPos(1, select(2, monitor.getCursorPos()) - 1)
end
end
local function write_msg(message, msg_colour)
local time_str = os.date('%Y-%b-%d %H:%M:%S')
local msg_formatted = "[" .. time_str .. "] " .. message
monitor.setTextColour(msg_colour)
monitor.write(msg_formatted)
move_line()
end
function self.log(msg)
write_msg(msg, log_colours['log'])
end
function self.info(msg)
write_msg('INFO: ' .. msg, log_colours['info'])
end
function self.warning(msg)
write_msg('WARNING: ' .. msg, log_colours['warn'])
end
function self.error(msg)
write_msg('ERROR: ' .. msg, log_colours['err'])
end
function self.success(msg)
write_msg('SUCCESS: ' .. msg, log_colours['success'])
end
return self
end
return logger

19
apricot/node/modems.lua Normal file
View file

@ -0,0 +1,19 @@
local modems = {peripheral.find("modem")}
local logger = require('logger').init()
logger.info("Powering up!")
for modem_i, modem in pairs(modems) do
modem.closeAll()
modem.open(1)
-- for i = (128 * (modem_i - 1)),((128 * modem_i) - 1) do
-- modem.open(i)
-- end
logger.success("Modem " .. modem_i .. " opened ports " .. (128 * (modem_i - 1)) .. " - " .. ((128 * modem_i) - 1))
end
local event, side, channel, replyChannel, message, distance
while true do
event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
logger.info(channel .. " " .. replyChannel .. " " .. tostring(message) .. " " .. distance )
end

View file

@ -0,0 +1,12 @@
local modem = peripheral.find("modem") or error("No modem attached", 0)
local logger = require('logger').init()
for i = 1,125 do
modem.open(i)
end
local event, side, channel, replyChannel, message, distance
while true do
event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
logger.info(channel .. " " .. replyChannel .. " " .. tostring(message) .. " " .. distance )
end

View file

@ -0,0 +1,6 @@
local modem = peripheral.find("modem") or error("No modem attached", 0)
modem.transmit(15, 43, "Hello, world!")
modem.transmit(25, 2, {message=1})
modem.transmit(1, 1, "What if I put you in an infinite loop?")

BIN
char_reference.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

86
chest_monitor.lua Normal file
View file

@ -0,0 +1,86 @@
-- There are a lot of chest monitors beyond this one
-- Each branching into countless features of their own
-- But this monitor is being preserved because it's special
-- It's special, because despite being so basic...
-- it was the first practical CC program I ever wrote
local chests = {peripheral.find("minecraft:chest")}
local data_monitor = peripheral.find("monitor")
local sleep_time = sleep(#(chests)/20)
local function move_line()
data_monitor.setCursorPos(1, select(2, data_monitor.getCursorPos()) + 1)
if select(2, data_monitor.getCursorPos()) >= select(2, data_monitor.getSize()) then
data_monitor.scroll(1)
data_monitor.setCursorPos(1, select(2, data_monitor.getCursorPos()) - 1)
end
end
local function get_chest_data(chest)
local count = 0
local slots = 0
local calls = {}
local max_storage = 0
for i, item in pairs(chest.list()) do
table.insert(calls,function()
slots = slots + 1
count = count + item.count
max_storage = chest.getItemDetail(i).maxCount + max_storage
end)
end
parallel.waitForAll(table.unpack(calls))
return count, max_storage + (chest.size() - slots) * 64
end
local function make_progressbar(size, taken, max)
size = size - 2 -- make space for "[" and "]"
--[[
taken/max = x/size
taken * size = x * max
x = taken * size / max
]]
local progress = math.floor(size * taken / max)
local hash = ("#"):rep(progress)
local dots = ("."):rep(size - progress)
return ("[" .. hash .. dots .. "]")
end
local function write_progressbar(taken, max)
-- Progressbar
local percent = 100 - math.floor(100 * taken/max)
local taken_space = #tostring(percent) + #tostring(taken) + #tostring(max) + 11 -- " taken/free [PROGRESSBAR] percent% free "
local size = select(1, data_monitor.getSize()) - taken_space
data_monitor.write(" " .. tostring(taken) .. "/" .. tostring(max) .. " " .. make_progressbar(size, taken, max) .. " ".. percent .. "% free")
move_line()
end
local function write_chest_data(index, taken, max)
local start = " Chest " .. index .. (" "):rep(8 - #tostring(index) - #tostring(taken)) .. tostring(taken) .. "/" .. tostring(max) .. (" "):rep(5 - #tostring(max))
data_monitor.write(start .. make_progressbar(20, taken, max))
end
while true do
local max_storage = 0
local taken_storage = 0
local chests_list = {}
for i, chest in pairs(chests) do
local taken, max = get_chest_data(chest)
taken_storage = taken_storage + taken
max_storage = max_storage + max
table.insert(chests_list, {["taken"] = taken, ["max"] = max})
end
data_monitor.clear()
data_monitor.setCursorPos(1, 1)
write_progressbar(taken_storage, max_storage)
for i, chest in pairs(chests_list) do
write_chest_data(i, chest["taken"], chest["max"])
move_line()
end
sleep(sleep_time)
end

140
dash_old/modules/clock.lua Normal file
View file

@ -0,0 +1,140 @@
local ascii_digits = {
{
" 0000 ",
"00 00",
"00 00",
"00 00",
" 0000 "
},
{
"1111 ",
" 11 ",
" 11 ",
" 11 ",
"111111"
},
{
" 2222 ",
"22 22",
" 22 ",
" 22 ",
"222222",
},
{
" 3333 ",
"33 33",
" 333",
"33 33",
" 3333 ",
},
{
"44 44",
"44 44",
"444444",
" 44",
" 44"
},
{
"555555",
"55 ",
"55555 ",
" 55",
"55555"
},
{
" 6666 ",
"66 ",
"66666 ",
"66 66",
" 6666 "
},
{
"777777",
" 77 ",
" 77 ",
" 77 ",
"77 "
},
{
" 8888 ",
"88 88",
" 8888 ",
"88 88",
" 8888 "
},
{
" 9999 ",
"99 99",
" 99999",
" 99",
" 9999 "
}
}
local seperator = {
" ",
" # ",
" ",
" # ",
" ",
}
local function draw_time(x, y, monitor)
local date = os.date("%b %d")
local h, m = tostring(os.date("%H")), tostring(os.date("%M"))
local h1, h2 = tonumber(string.sub(h, 1, 1)), tonumber(string.sub(h, 2, 2))
local m1, m2 = tonumber(string.sub(m, 1, 1)), tonumber(string.sub(m, 2, 2))
local s = os.date("%S")
monitor.setCursorPos(x, y)
monitor.blit(date, "eeef00", "ffffff")
monitor.setCursorPos(x, y + 1)
for i = 1, 5 do
monitor.setCursorPos(x, select(2, monitor.getCursorPos()) + 1)
monitor.write(ascii_digits[h1 + 1][i] .. " " .. ascii_digits[h2 + 1][i] .. seperator[i] .. ascii_digits[m1 + 1][i] .. " " .. ascii_digits[m2 + 1][i])
end
end
local function draw_timezone(x, y, tz_name, utc_offset, tz_monitor)
--[[
/SYD\
|22:34|
\_ _/
]]
if #tz_name ~= 3 then error("Invalid timezone name (not 3 letters)") end
local utc = os.date("!*t")
local tz_hour = utc.hour + utc_offset
if tz_hour > 23 then
tz_hour = -24 + tz_hour end
local day_part = {
["day"] = "",
["night"] = ""
}
local tz_symbol
if (tz_hour > 20 or tz_hour < 7) then tz_symbol = day_part["night"] else tz_symbol = day_part["day"] end
tz_monitor.setCursorPos(x, y)
tz_monitor.blit(" /" .. tz_name .. "\\", "f04440", "ffffff")
tz_monitor.setCursorPos(x, y + 1)
tz_monitor.write("|" .. tostring(utc.hour + utc_offset) .. ":" .. tostring(utc.min) .. "|")
tz_monitor.setCursorPos(x, y + 2)
tz_monitor.blit(" \\_" .. tz_symbol .. "_/ ", "f00400f", "fffffff")
end
local clock = {}
clock.new = function (monitor_wrap)
local self = {}
local monitor = monitor_wrap or peripheral.find("monitor")
function self.update()
draw_time(2, 2, monitor)
draw_timezone(2, 11, "SYD", 10, monitor)
end
return self
end
return clock

25
dash_old/modules/rss.lua Normal file
View file

@ -0,0 +1,25 @@
local rss_feed = {}
rss_feed.new = function (monitor_wrap)
local self = {}
local monitor = monitor_wrap or peripheral.find("monitor")
local function move_line()
monitor.setCursorPos(1, select(2, monitor.getCursorPos()) + 1)
if select(2, monitor.getCursorPos()) >= select(2, monitor.getSize()) then
monitor.scroll(1)
monitor.setCursorPos(1, select(2, monitor.getCursorPos()) - 1)
end
end
function self.update()
--monitor.write("[TASS] Joeee biden!")
--move_line()
end
return self
end
return rss_feed

View file

@ -0,0 +1,36 @@
--[[
Programme for large smart screen in my base
- RSS feed
- Modular functions
- Time
Screen is 8 by 4 blocls
8
#############
############# 4
#############
#############
]]
local monitor_main = peripheral.find("monitor")
local x_max, y_max = monitor_main.getSize()
local monitor_clock = window.create(monitor_main, 1, 1, x_max / 8 * 3, y_max / 2)
local monitor_rss = window.create(monitor_main, x_max - x_max / 4, 1, x_max, y_max, true)
local modules = {
require("modules.clock").new(monitor_clock),
require("modules.rss").new(monitor_rss)
}
while true do
local calls = {}
for i, module in pairs(modules) do
--module.update()
table.insert(calls, module.update())
end
parallel.waitForAll(table.unpack(calls))
sleep(0.05)
end

3
generator/README.md Normal file
View file

@ -0,0 +1,3 @@
This holds code for the steam generator powering my base on my Minecraft SMP
It is not designed for compatibility.

83
generator/data.lua Normal file
View file

@ -0,0 +1,83 @@
--[[
== GENERATOR STATUS =============================
SU 580000 / 598030 | 96% | 18030 avl
RPM 180 / 256 | 70%
GEN Online
== ENG ==========================================
ENG1 lvl 9 | watr 100% | powr 80% | size 100%
ENG2 lvl 9 | watr 100% | powr 78% | size 100%
ENG2 lvl 9 | watr 80% | powr 79% | size 100%
LAVA [||||||||| ] 53% | 49mbu
== BAT ==========================================
BAT1 [||||||||| ] 35%
BAT2 [||||||||||||||| ] 70%
BAT3 [|||||||||||| ] 42%
Total 140 MFE / 270 MFE
== LOG ==========================================
Time 21:50:52 GMT+3
21:50:50 energy -3992 FE
21:50:51 energy -539 FE
21:50:52 GEN off -> on
21:50:52 energy +44 FE
]]
local RPM_CONTROLLER = peripheral.find("create:speedcontroller") -- TODO: Type
local ENGINES = {peripheral.find("create:engine")} -- TODO: Type
local BATTERIES = {peripheral.find("createcraftsandadditions:accumulator")} -- TODO: Type
local LAVA = peripheral.find("create:fluid_tank") -- TODO: Type
local GENERATOR_SWITCH = {
set = function (on)
redstone.setOutput("top", on) -- Change side with redstone here!
end
} -- TODO: Type? Redstone???? Side?
local MONITOR_INFO
local MONITOR_CONTROLS
local monitors = {peripheral.find("monitor")}
for _, monitor in pairs(monitors) do
local x, y = monitor.getSize()
if x < 0 and y < 0 then
MONITOR_CONTROLS = monitor
else
MONITOR_INFO = monitor
end
end
-- All data, to be updated by relevant functions
local DATA = {
su = {
used = 0,
total = 0
},
rpm = 0,
generation = false,
engines = {
},
batteries = {
},
events = {
}
}
local function update_data()
end
local function change_rpm(rpm)
RPM_CONTROLLER.setTarget(rpm) -- TODO: Real method?
end
local function set_generation()
end
local function log(msg)
local current_time = textutils.formatTime(os.time("local"), true)
end

0
generator/network.lua Normal file
View file

0
generator/startup.lua Normal file
View file

56
logger.lua Normal file
View file

@ -0,0 +1,56 @@
-- Simple logger for use in any projects
local logger = {}
logger.new = function(monitor_wrap)
local self = {}
local monitor = monitor_wrap
local log_colours = {
['log'] = colours.white,
['info'] = colours.lightBlue,
['warn'] = colours.yellow,
['err'] = colours.red,
['success'] = colours.lime
}
local function move_line()
monitor.setCursorPos(1, select(2, monitor.getCursorPos()) + 1)
if select(2, monitor.getCursorPos()) >= select(2, monitor.getSize()) then
monitor.scroll(1)
monitor.setCursorPos(1, select(2, monitor.getCursorPos()) - 1)
end
end
local function write_msg(message, msg_colour)
local time_str = os.date('%Y-%b-%d %H:%M:%S')
local msg_formatted = "[" .. time_str .. "] " .. message
monitor.setTextColour(msg_colour)
monitor.write(msg_formatted)
move_line()
end
function self.log(msg)
write_msg(msg, log_colours['log'])
end
function self.info(msg)
write_msg('INFO: ' .. msg, log_colours['info'])
end
function self.warning(msg)
write_msg('WARNING: ' .. msg, log_colours['warn'])
end
function self.error(msg)
write_msg('ERROR: ' .. msg, log_colours['err'])
end
function self.success(msg)
write_msg('SUCCESS: ' .. msg, log_colours['success'])
end
return self
end
return logger