initialize
This commit is contained in:
commit
5b23ea99fd
43 changed files with 2336 additions and 0 deletions
8
Dash/dash.lua
Normal file
8
Dash/dash.lua
Normal 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
184
Dash/util/pixelbox_lite.lua
Normal 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
19
Dash/widgets/ART.md
Normal 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
10
Dash/widgets/README.md
Normal 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
1
GLONASS/README.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
I originally made this project in 2022 as a proof of concept.
|
63
GLONASS/UNS.lua
Normal file
63
GLONASS/UNS.lua
Normal 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
|
95
MusicPlayer/Client/client.lua
Normal file
95
MusicPlayer/Client/client.lua
Normal 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
|
167
MusicPlayer/Client/graphics.lua
Normal file
167
MusicPlayer/Client/graphics.lua
Normal 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
|
27
MusicPlayer/Client/playback.lua
Normal file
27
MusicPlayer/Client/playback.lua
Normal 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
|
59
MusicPlayer/Client/touch_processing.lua
Normal file
59
MusicPlayer/Client/touch_processing.lua
Normal 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
|
98
MusicPlayer/Server/audio_manager.lua
Normal file
98
MusicPlayer/Server/audio_manager.lua
Normal 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
|
60
MusicPlayer/Server/logger.lua
Normal file
60
MusicPlayer/Server/logger.lua
Normal 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
|
60
MusicPlayer/Server/server.lua
Normal file
60
MusicPlayer/Server/server.lua
Normal 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
|
14
MusicPlayer/Server/startup.lua
Normal file
14
MusicPlayer/Server/startup.lua
Normal 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
4
README.md
Normal 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
128
RSS/feeds.lua
Normal 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
5
RSS/feeds/nytimes.json
Normal 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
5
RSS/feeds/tass.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"link": "https://tass.ru/rss/v2.xml",
|
||||||
|
"title_override": "TACC",
|
||||||
|
"colour": "b"
|
||||||
|
}
|
5
RSS/feeds/youtube_generic.json
Normal file
5
RSS/feeds/youtube_generic.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"link": "https://www.youtube.com/@tag",
|
||||||
|
"title_override": "Youtuber name",
|
||||||
|
"colour": "e"
|
||||||
|
}
|
71
RSS/graphics.lua
Normal file
71
RSS/graphics.lua
Normal 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
98
RSS/log.lua
Normal 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
28
RSS/main.lua
Normal 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
259
RSS/slaxml.lua
Normal 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
3
RSS/todo.txt
Normal 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
96
TARDIM/NavDash.lua
Normal 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
85
TARDIM/NavDashTwo.lua
Normal 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
148
TUI71/TUI71-time.lua
Normal 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
9
TUI71/TUI71.lua
Normal 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
3
apricot/README.md
Normal 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
58
apricot/node/logger.lua
Normal 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
19
apricot/node/modems.lua
Normal 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
|
12
apricot/scripts/receiver.lua
Normal file
12
apricot/scripts/receiver.lua
Normal 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
|
6
apricot/scripts/sender.lua
Normal file
6
apricot/scripts/sender.lua
Normal 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
BIN
char_reference.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 172 KiB |
86
chest_monitor.lua
Normal file
86
chest_monitor.lua
Normal 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
140
dash_old/modules/clock.lua
Normal 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
25
dash_old/modules/rss.lua
Normal 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
|
36
dash_old/smart_monitor.lua
Normal file
36
dash_old/smart_monitor.lua
Normal 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
3
generator/README.md
Normal 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
83
generator/data.lua
Normal 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
0
generator/network.lua
Normal file
0
generator/startup.lua
Normal file
0
generator/startup.lua
Normal file
56
logger.lua
Normal file
56
logger.lua
Normal 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
|
Loading…
Reference in a new issue