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