spaces->tab, equipper improvements, supertreefarm rewrite, follow improvements, sensor cleanup, milo multiple items allowed in recipes, remote canvas access
This commit is contained in:
@@ -15,18 +15,18 @@ local REGISTRY_DIR = 'usr/.registry'
|
||||
-- FIX SOMEDAY
|
||||
local function registerApp(app, key)
|
||||
|
||||
app.key = SHA1.sha1(key)
|
||||
Util.writeTable(fs.combine(REGISTRY_DIR, app.key), app)
|
||||
os.queueEvent('os_register_app')
|
||||
app.key = SHA1.sha1(key)
|
||||
Util.writeTable(fs.combine(REGISTRY_DIR, app.key), app)
|
||||
os.queueEvent('os_register_app')
|
||||
end
|
||||
|
||||
local function unregisterApp(key)
|
||||
|
||||
local filename = fs.combine(REGISTRY_DIR, SHA1.sha1(key))
|
||||
if fs.exists(filename) then
|
||||
fs.delete(filename)
|
||||
os.queueEvent('os_register_app')
|
||||
end
|
||||
local filename = fs.combine(REGISTRY_DIR, SHA1.sha1(key))
|
||||
if fs.exists(filename) then
|
||||
fs.delete(filename)
|
||||
os.queueEvent('os_register_app')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -40,366 +40,366 @@ local APP_DIR = 'usr/apps'
|
||||
|
||||
local sources = {
|
||||
|
||||
{ text = "STD Default",
|
||||
event = 'source',
|
||||
url = "http://pastebin.com/raw/zVws7eLq" }, --stock
|
||||
{ text = "STD Default",
|
||||
event = 'source',
|
||||
url = "http://pastebin.com/raw/zVws7eLq" }, --stock
|
||||
--[[
|
||||
{ text = "Discover",
|
||||
event = 'source',
|
||||
generateName = true,
|
||||
url = "http://pastebin.com/raw/9bXfCz6M" }, --owned by dannysmc95
|
||||
{ text = "Discover",
|
||||
event = 'source',
|
||||
generateName = true,
|
||||
url = "http://pastebin.com/raw/9bXfCz6M" }, --owned by dannysmc95
|
||||
|
||||
{ text = "Opus",
|
||||
event = 'source',
|
||||
url = "http://pastebin.com/raw/ajQ91Rmn" },
|
||||
{ text = "Opus",
|
||||
event = 'source',
|
||||
url = "http://pastebin.com/raw/ajQ91Rmn" },
|
||||
]]
|
||||
}
|
||||
|
||||
shell.setDir(APP_DIR)
|
||||
|
||||
local function downloadApp(app)
|
||||
local h
|
||||
local h
|
||||
|
||||
if type(app.url) == "table" then
|
||||
h = contextualGet(app.url[1])
|
||||
else
|
||||
h = http.get(app.url)
|
||||
end
|
||||
if type(app.url) == "table" then
|
||||
h = contextualGet(app.url[1])
|
||||
else
|
||||
h = http.get(app.url)
|
||||
end
|
||||
|
||||
if h then
|
||||
local contents = h.readAll()
|
||||
h:close()
|
||||
return contents
|
||||
end
|
||||
if h then
|
||||
local contents = h.readAll()
|
||||
h:close()
|
||||
return contents
|
||||
end
|
||||
end
|
||||
|
||||
local function runApp(app, checkExists, ...)
|
||||
|
||||
local path, fn
|
||||
local args = { ... }
|
||||
local path, fn
|
||||
local args = { ... }
|
||||
|
||||
if checkExists and fs.exists(fs.combine(APP_DIR, app.name)) then
|
||||
path = fs.combine(APP_DIR, app.name)
|
||||
else
|
||||
local program = downloadApp(app)
|
||||
if checkExists and fs.exists(fs.combine(APP_DIR, app.name)) then
|
||||
path = fs.combine(APP_DIR, app.name)
|
||||
else
|
||||
local program = downloadApp(app)
|
||||
|
||||
fn = function()
|
||||
fn = function()
|
||||
|
||||
if not program then
|
||||
error('Failed to download')
|
||||
end
|
||||
if not program then
|
||||
error('Failed to download')
|
||||
end
|
||||
|
||||
local fn = loadstring(program, app.name)
|
||||
local fn = loadstring(program, app.name)
|
||||
|
||||
if not fn then
|
||||
error('Failed to download')
|
||||
end
|
||||
if not fn then
|
||||
error('Failed to download')
|
||||
end
|
||||
|
||||
setfenv(fn, sandboxEnv)
|
||||
fn(unpack(args))
|
||||
end
|
||||
end
|
||||
setfenv(fn, sandboxEnv)
|
||||
fn(unpack(args))
|
||||
end
|
||||
end
|
||||
|
||||
multishell.openTab({
|
||||
title = app.name,
|
||||
env = sandboxEnv,
|
||||
path = path,
|
||||
fn = fn,
|
||||
focused = true,
|
||||
})
|
||||
multishell.openTab({
|
||||
title = app.name,
|
||||
env = sandboxEnv,
|
||||
path = path,
|
||||
fn = fn,
|
||||
focused = true,
|
||||
})
|
||||
|
||||
return true, 'Running program'
|
||||
return true, 'Running program'
|
||||
end
|
||||
|
||||
local installApp = function(app)
|
||||
|
||||
local program = downloadApp(app)
|
||||
if not program then
|
||||
return false, "Failed to download"
|
||||
end
|
||||
local program = downloadApp(app)
|
||||
if not program then
|
||||
return false, "Failed to download"
|
||||
end
|
||||
|
||||
local fullPath = fs.combine(APP_DIR, app.name)
|
||||
Util.writeFile(fullPath, program)
|
||||
return true, 'Installed as ' .. fullPath
|
||||
local fullPath = fs.combine(APP_DIR, app.name)
|
||||
Util.writeFile(fullPath, program)
|
||||
return true, 'Installed as ' .. fullPath
|
||||
end
|
||||
|
||||
local viewApp = function(app)
|
||||
|
||||
local program = downloadApp(app)
|
||||
if not program then
|
||||
return false, "Failed to download"
|
||||
end
|
||||
local program = downloadApp(app)
|
||||
if not program then
|
||||
return false, "Failed to download"
|
||||
end
|
||||
|
||||
Util.writeFile('/.source', program)
|
||||
shell.openForegroundTab('edit /.source')
|
||||
fs.delete('/.source')
|
||||
return true
|
||||
Util.writeFile('/.source', program)
|
||||
shell.openForegroundTab('edit /.source')
|
||||
fs.delete('/.source')
|
||||
return true
|
||||
end
|
||||
|
||||
local getSourceListing = function(source)
|
||||
local contents = http.get(source.url)
|
||||
if contents then
|
||||
local contents = http.get(source.url)
|
||||
if contents then
|
||||
|
||||
local fn = loadstring(contents.readAll(), source.text)
|
||||
contents.close()
|
||||
local fn = loadstring(contents.readAll(), source.text)
|
||||
contents.close()
|
||||
|
||||
local env = { std = { } }
|
||||
setmetatable(env, { __index = _G })
|
||||
setfenv(fn, env)
|
||||
fn()
|
||||
local env = { std = { } }
|
||||
setmetatable(env, { __index = _G })
|
||||
setfenv(fn, env)
|
||||
fn()
|
||||
|
||||
if env.contextualGet then
|
||||
contextualGet = env.contextualGet
|
||||
end
|
||||
if env.contextualGet then
|
||||
contextualGet = env.contextualGet
|
||||
end
|
||||
|
||||
source.storeURLs = env.std.storeURLs
|
||||
source.storeCatagoryNames = env.std.storeCatagoryNames
|
||||
source.storeURLs = env.std.storeURLs
|
||||
source.storeCatagoryNames = env.std.storeCatagoryNames
|
||||
|
||||
if source.storeURLs and source.storeCatagoryNames then
|
||||
for k,v in pairs(source.storeURLs) do
|
||||
if source.generateName then
|
||||
v.name = v.title:match('(%w+)')
|
||||
if not v.name or #v.name == 0 then
|
||||
v.name = tostring(k)
|
||||
else
|
||||
v.name = v.name:lower()
|
||||
end
|
||||
else
|
||||
v.name = k
|
||||
end
|
||||
v.categoryName = source.storeCatagoryNames[v.catagory]
|
||||
v.ltitle = v.title:lower()
|
||||
end
|
||||
end
|
||||
end
|
||||
if source.storeURLs and source.storeCatagoryNames then
|
||||
for k,v in pairs(source.storeURLs) do
|
||||
if source.generateName then
|
||||
v.name = v.title:match('(%w+)')
|
||||
if not v.name or #v.name == 0 then
|
||||
v.name = tostring(k)
|
||||
else
|
||||
v.name = v.name:lower()
|
||||
end
|
||||
else
|
||||
v.name = k
|
||||
end
|
||||
v.categoryName = source.storeCatagoryNames[v.catagory]
|
||||
v.ltitle = v.title:lower()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local appPage = UI.Page {
|
||||
menuBar = UI.MenuBar {
|
||||
menuBar = UI.MenuBar {
|
||||
-- showBackButton = not pocket,
|
||||
buttons = {
|
||||
{ text = '\027', event = 'back' },
|
||||
{ text = 'Install', event = 'install' },
|
||||
{ text = 'Run', event = 'run' },
|
||||
{ text = 'View', event = 'view' },
|
||||
{ text = 'Remove', event = 'uninstall', name = 'removeButton' },
|
||||
},
|
||||
},
|
||||
container = UI.Window {
|
||||
x = 2, y = 3, ex = -2, ey = -3,
|
||||
viewport = UI.Viewport(),
|
||||
},
|
||||
notification = UI.Notification(),
|
||||
accelerators = {
|
||||
q = 'back',
|
||||
backspace = 'back',
|
||||
},
|
||||
buttons = {
|
||||
{ text = '\027', event = 'back' },
|
||||
{ text = 'Install', event = 'install' },
|
||||
{ text = 'Run', event = 'run' },
|
||||
{ text = 'View', event = 'view' },
|
||||
{ text = 'Remove', event = 'uninstall', name = 'removeButton' },
|
||||
},
|
||||
},
|
||||
container = UI.Window {
|
||||
x = 2, y = 3, ex = -2, ey = -3,
|
||||
viewport = UI.Viewport(),
|
||||
},
|
||||
notification = UI.Notification(),
|
||||
accelerators = {
|
||||
q = 'back',
|
||||
backspace = 'back',
|
||||
},
|
||||
}
|
||||
|
||||
function appPage.container.viewport:draw()
|
||||
local app = self.parent.parent.app
|
||||
local str = string.format(
|
||||
'%s \nBy: %s \nCategory: %s\nFile name: %s\n\n%s',
|
||||
Ansi.yellow .. app.title .. Ansi.reset,
|
||||
app.creator,
|
||||
app.categoryName, app.name,
|
||||
Ansi.yellow .. app.description .. Ansi.reset)
|
||||
local app = self.parent.parent.app
|
||||
local str = string.format(
|
||||
'%s \nBy: %s \nCategory: %s\nFile name: %s\n\n%s',
|
||||
Ansi.yellow .. app.title .. Ansi.reset,
|
||||
app.creator,
|
||||
app.categoryName, app.name,
|
||||
Ansi.yellow .. app.description .. Ansi.reset)
|
||||
|
||||
self:clear()
|
||||
self:setCursorPos(1, 1)
|
||||
self:print(str)
|
||||
self.ymax = self.cursorY
|
||||
self:clear()
|
||||
self:setCursorPos(1, 1)
|
||||
self:print(str)
|
||||
self.ymax = self.cursorY
|
||||
|
||||
if appPage.notification.enabled then
|
||||
appPage.notification:draw()
|
||||
end
|
||||
if appPage.notification.enabled then
|
||||
appPage.notification:draw()
|
||||
end
|
||||
end
|
||||
|
||||
function appPage:enable(source, app)
|
||||
self.source = source
|
||||
self.app = app
|
||||
UI.Page.enable(self)
|
||||
self.source = source
|
||||
self.app = app
|
||||
UI.Page.enable(self)
|
||||
|
||||
self.container.viewport:setScrollPosition(0)
|
||||
if fs.exists(fs.combine(APP_DIR, app.name)) then
|
||||
self.menuBar.removeButton:enable('Remove')
|
||||
else
|
||||
self.menuBar.removeButton:disable('Remove')
|
||||
end
|
||||
self.container.viewport:setScrollPosition(0)
|
||||
if fs.exists(fs.combine(APP_DIR, app.name)) then
|
||||
self.menuBar.removeButton:enable('Remove')
|
||||
else
|
||||
self.menuBar.removeButton:disable('Remove')
|
||||
end
|
||||
end
|
||||
|
||||
function appPage:eventHandler(event)
|
||||
if event.type == 'back' then
|
||||
UI:setPreviousPage()
|
||||
if event.type == 'back' then
|
||||
UI:setPreviousPage()
|
||||
|
||||
elseif event.type == 'run' then
|
||||
self.notification:info('Running program', 3)
|
||||
self:sync()
|
||||
runApp(self.app, true)
|
||||
elseif event.type == 'run' then
|
||||
self.notification:info('Running program', 3)
|
||||
self:sync()
|
||||
runApp(self.app, true)
|
||||
|
||||
elseif event.type == 'view' then
|
||||
self.notification:info('Downloading program', 3)
|
||||
self:sync()
|
||||
viewApp(self.app)
|
||||
elseif event.type == 'view' then
|
||||
self.notification:info('Downloading program', 3)
|
||||
self:sync()
|
||||
viewApp(self.app)
|
||||
|
||||
elseif event.type == 'uninstall' then
|
||||
if self.app.runOnly then
|
||||
runApp(self.app, false, 'uninstall')
|
||||
else
|
||||
fs.delete(fs.combine(APP_DIR, self.app.name))
|
||||
self.notification:success("Uninstalled " .. self.app.name, 3)
|
||||
self:focusFirst(self)
|
||||
self.menuBar.removeButton:disable('Remove')
|
||||
self.menuBar:draw()
|
||||
elseif event.type == 'uninstall' then
|
||||
if self.app.runOnly then
|
||||
runApp(self.app, false, 'uninstall')
|
||||
else
|
||||
fs.delete(fs.combine(APP_DIR, self.app.name))
|
||||
self.notification:success("Uninstalled " .. self.app.name, 3)
|
||||
self:focusFirst(self)
|
||||
self.menuBar.removeButton:disable('Remove')
|
||||
self.menuBar:draw()
|
||||
|
||||
unregisterApp(self.app.creator .. '.' .. self.app.name)
|
||||
end
|
||||
unregisterApp(self.app.creator .. '.' .. self.app.name)
|
||||
end
|
||||
|
||||
elseif event.type == 'install' then
|
||||
self.notification:info("Installing", 3)
|
||||
self:sync()
|
||||
local s, m
|
||||
if self.app.runOnly then
|
||||
s,m = runApp(self.app, false)
|
||||
else
|
||||
s,m = installApp(self.app)
|
||||
end
|
||||
if s then
|
||||
self.notification:success(m, 3)
|
||||
elseif event.type == 'install' then
|
||||
self.notification:info("Installing", 3)
|
||||
self:sync()
|
||||
local s, m
|
||||
if self.app.runOnly then
|
||||
s,m = runApp(self.app, false)
|
||||
else
|
||||
s,m = installApp(self.app)
|
||||
end
|
||||
if s then
|
||||
self.notification:success(m, 3)
|
||||
|
||||
if not self.app.runOnly then
|
||||
self.menuBar.removeButton:enable('Remove')
|
||||
self.menuBar:draw()
|
||||
if not self.app.runOnly then
|
||||
self.menuBar.removeButton:enable('Remove')
|
||||
self.menuBar:draw()
|
||||
|
||||
local category = 'Apps'
|
||||
if self.app.catagoryName == 'Game' then
|
||||
category = 'Games'
|
||||
end
|
||||
local category = 'Apps'
|
||||
if self.app.catagoryName == 'Game' then
|
||||
category = 'Games'
|
||||
end
|
||||
|
||||
registerApp({
|
||||
run = fs.combine(APP_DIR, self.app.name),
|
||||
title = self.app.title,
|
||||
category = category,
|
||||
icon = self.app.icon,
|
||||
}, self.app.creator .. '.' .. self.app.name)
|
||||
end
|
||||
else
|
||||
self.notification:error(m, 3)
|
||||
end
|
||||
else
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
registerApp({
|
||||
run = fs.combine(APP_DIR, self.app.name),
|
||||
title = self.app.title,
|
||||
category = category,
|
||||
icon = self.app.icon,
|
||||
}, self.app.creator .. '.' .. self.app.name)
|
||||
end
|
||||
else
|
||||
self.notification:error(m, 3)
|
||||
end
|
||||
else
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local categoryPage = UI.Page {
|
||||
menuBar = UI.MenuBar {
|
||||
buttons = {
|
||||
{ text = 'Catalog', dropdown = sources },
|
||||
{ text = 'Category', name = 'categoryButton', dropdown = { } },
|
||||
},
|
||||
},
|
||||
grid = UI.ScrollingGrid {
|
||||
y = 2, ey = -2,
|
||||
columns = {
|
||||
{ heading = 'Title', key = 'title' },
|
||||
},
|
||||
sortColumn = 'title',
|
||||
},
|
||||
statusBar = UI.StatusBar(),
|
||||
accelerators = {
|
||||
l = 'lua',
|
||||
q = 'quit',
|
||||
},
|
||||
menuBar = UI.MenuBar {
|
||||
buttons = {
|
||||
{ text = 'Catalog', dropdown = sources },
|
||||
{ text = 'Category', name = 'categoryButton', dropdown = { } },
|
||||
},
|
||||
},
|
||||
grid = UI.ScrollingGrid {
|
||||
y = 2, ey = -2,
|
||||
columns = {
|
||||
{ heading = 'Title', key = 'title' },
|
||||
},
|
||||
sortColumn = 'title',
|
||||
},
|
||||
statusBar = UI.StatusBar(),
|
||||
accelerators = {
|
||||
l = 'lua',
|
||||
q = 'quit',
|
||||
},
|
||||
}
|
||||
|
||||
function categoryPage:setCategory(source, name, index)
|
||||
self.grid.values = { }
|
||||
for _,v in pairs(source.storeURLs) do
|
||||
if index == 0 or index == v.catagory then
|
||||
table.insert(self.grid.values, v)
|
||||
end
|
||||
end
|
||||
self.statusBar:setStatus(string.format('%s: %s', source.text, name))
|
||||
self.grid:update()
|
||||
self.grid:setIndex(1)
|
||||
self.grid.values = { }
|
||||
for _,v in pairs(source.storeURLs) do
|
||||
if index == 0 or index == v.catagory then
|
||||
table.insert(self.grid.values, v)
|
||||
end
|
||||
end
|
||||
self.statusBar:setStatus(string.format('%s: %s', source.text, name))
|
||||
self.grid:update()
|
||||
self.grid:setIndex(1)
|
||||
end
|
||||
|
||||
function categoryPage:setSource(source)
|
||||
|
||||
if not source.categoryMenu then
|
||||
if not source.categoryMenu then
|
||||
|
||||
self.statusBar:setStatus('Loading...')
|
||||
self.statusBar:draw()
|
||||
self:sync()
|
||||
self.statusBar:setStatus('Loading...')
|
||||
self.statusBar:draw()
|
||||
self:sync()
|
||||
|
||||
getSourceListing(source)
|
||||
getSourceListing(source)
|
||||
|
||||
if not source.storeURLs then
|
||||
error('Unable to download application list')
|
||||
end
|
||||
if not source.storeURLs then
|
||||
error('Unable to download application list')
|
||||
end
|
||||
|
||||
local buttons = { }
|
||||
for k,v in Util.spairs(source.storeCatagoryNames,
|
||||
function(a, b) return a:lower() < b:lower() end) do
|
||||
local buttons = { }
|
||||
for k,v in Util.spairs(source.storeCatagoryNames,
|
||||
function(a, b) return a:lower() < b:lower() end) do
|
||||
|
||||
if v ~= 'Operating System' then
|
||||
table.insert(buttons, {
|
||||
text = v,
|
||||
event = 'category',
|
||||
index = k,
|
||||
})
|
||||
end
|
||||
end
|
||||
if v ~= 'Operating System' then
|
||||
table.insert(buttons, {
|
||||
text = v,
|
||||
event = 'category',
|
||||
index = k,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
source.categoryMenu = UI.DropMenu({
|
||||
buttons = buttons,
|
||||
})
|
||||
source.index, source.name = Util.first(source.storeCatagoryNames)
|
||||
source.categoryMenu = UI.DropMenu({
|
||||
buttons = buttons,
|
||||
})
|
||||
source.index, source.name = Util.first(source.storeCatagoryNames)
|
||||
|
||||
categoryPage.menuBar.categoryButton:add({
|
||||
categoryMenu = source.categoryMenu
|
||||
})
|
||||
end
|
||||
categoryPage.menuBar.categoryButton:add({
|
||||
categoryMenu = source.categoryMenu
|
||||
})
|
||||
end
|
||||
|
||||
self.source = source
|
||||
self.menuBar.categoryButton.dropmenu = source.categoryMenu
|
||||
categoryPage:setCategory(source, source.name, source.index)
|
||||
self.source = source
|
||||
self.menuBar.categoryButton.dropmenu = source.categoryMenu
|
||||
categoryPage:setCategory(source, source.name, source.index)
|
||||
end
|
||||
|
||||
function categoryPage.grid:sortCompare(a, b)
|
||||
return a.ltitle < b.ltitle
|
||||
return a.ltitle < b.ltitle
|
||||
end
|
||||
|
||||
function categoryPage.grid:getRowTextColor(row, selected)
|
||||
if fs.exists(fs.combine(APP_DIR, row.name)) then
|
||||
return colors.orange
|
||||
end
|
||||
return UI.Grid:getRowTextColor(row, selected)
|
||||
if fs.exists(fs.combine(APP_DIR, row.name)) then
|
||||
return colors.orange
|
||||
end
|
||||
return UI.Grid:getRowTextColor(row, selected)
|
||||
end
|
||||
|
||||
function categoryPage:eventHandler(event)
|
||||
|
||||
if event.type == 'grid_select' or event.type == 'select' then
|
||||
UI:setPage(appPage, self.source, self.grid:getSelected())
|
||||
if event.type == 'grid_select' or event.type == 'select' then
|
||||
UI:setPage(appPage, self.source, self.grid:getSelected())
|
||||
|
||||
elseif event.type == 'category' then
|
||||
self:setCategory(self.source, event.button.text, event.button.index)
|
||||
self:setFocus(self.grid)
|
||||
self:draw()
|
||||
elseif event.type == 'category' then
|
||||
self:setCategory(self.source, event.button.text, event.button.index)
|
||||
self:setFocus(self.grid)
|
||||
self:draw()
|
||||
|
||||
elseif event.type == 'source' then
|
||||
self:setFocus(self.grid)
|
||||
self:setSource(event.button)
|
||||
self:draw()
|
||||
elseif event.type == 'source' then
|
||||
self:setFocus(self.grid)
|
||||
self:setSource(event.button)
|
||||
self:draw()
|
||||
|
||||
elseif event.type == 'quit' then
|
||||
UI:exitPullEvents()
|
||||
elseif event.type == 'quit' then
|
||||
UI:exitPullEvents()
|
||||
|
||||
else
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
else
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
print("Retrieving catalog list")
|
||||
|
||||
@@ -10,196 +10,196 @@ local peripheral = _G.peripheral
|
||||
|
||||
--[[ -- PeripheralsPage -- ]] --
|
||||
local peripheralsPage = UI.Page {
|
||||
grid = UI.ScrollingGrid {
|
||||
ey = -2,
|
||||
columns = {
|
||||
{ heading = 'Type', key = 'type' },
|
||||
{ heading = 'Side', key = 'side' },
|
||||
},
|
||||
sortColumn = 'type',
|
||||
autospace = true,
|
||||
},
|
||||
statusBar = UI.StatusBar {
|
||||
values = 'Select peripheral',
|
||||
},
|
||||
accelerators = {
|
||||
q = 'quit',
|
||||
},
|
||||
grid = UI.ScrollingGrid {
|
||||
ey = -2,
|
||||
columns = {
|
||||
{ heading = 'Type', key = 'type' },
|
||||
{ heading = 'Side', key = 'side' },
|
||||
},
|
||||
sortColumn = 'type',
|
||||
autospace = true,
|
||||
},
|
||||
statusBar = UI.StatusBar {
|
||||
values = 'Select peripheral',
|
||||
},
|
||||
accelerators = {
|
||||
q = 'quit',
|
||||
},
|
||||
}
|
||||
|
||||
function peripheralsPage.grid:draw()
|
||||
local sides = peripheral.getNames()
|
||||
local sides = peripheral.getNames()
|
||||
|
||||
Util.clear(self.values)
|
||||
for _,side in pairs(sides) do
|
||||
table.insert(self.values, {
|
||||
type = peripheral.getType(side),
|
||||
side = side
|
||||
})
|
||||
end
|
||||
self:update()
|
||||
self:adjustWidth()
|
||||
UI.Grid.draw(self)
|
||||
Util.clear(self.values)
|
||||
for _,side in pairs(sides) do
|
||||
table.insert(self.values, {
|
||||
type = peripheral.getType(side),
|
||||
side = side
|
||||
})
|
||||
end
|
||||
self:update()
|
||||
self:adjustWidth()
|
||||
UI.Grid.draw(self)
|
||||
end
|
||||
|
||||
function peripheralsPage:updatePeripherals()
|
||||
if UI:getCurrentPage() == self then
|
||||
self.grid:draw()
|
||||
self:sync()
|
||||
end
|
||||
if UI:getCurrentPage() == self then
|
||||
self.grid:draw()
|
||||
self:sync()
|
||||
end
|
||||
end
|
||||
|
||||
function peripheralsPage:eventHandler(event)
|
||||
if event.type == 'quit' then
|
||||
Event.exitPullEvents()
|
||||
if event.type == 'quit' then
|
||||
Event.exitPullEvents()
|
||||
|
||||
elseif event.type == 'grid_select' then
|
||||
UI:setPage('methods', event.selected)
|
||||
elseif event.type == 'grid_select' then
|
||||
UI:setPage('methods', event.selected)
|
||||
|
||||
end
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
|
||||
--[[ -- MethodsPage -- ]] --
|
||||
local methodsPage = UI.Page {
|
||||
backgroundColor = colors.black,
|
||||
doc = UI.TextArea {
|
||||
backgroundColor = colors.black,
|
||||
x = 2, y = 2, ex = -1, ey = -7,
|
||||
},
|
||||
grid = UI.ScrollingGrid {
|
||||
y = -6, ey = -2,
|
||||
columns = {
|
||||
{ heading = 'Name', key = 'name', width = UI.term.width }
|
||||
},
|
||||
sortColumn = 'name',
|
||||
},
|
||||
statusBar = UI.StatusBar {
|
||||
status = 'q to return',
|
||||
},
|
||||
accelerators = {
|
||||
q = 'back',
|
||||
backspace = 'back',
|
||||
},
|
||||
backgroundColor = colors.black,
|
||||
doc = UI.TextArea {
|
||||
backgroundColor = colors.black,
|
||||
x = 2, y = 2, ex = -1, ey = -7,
|
||||
},
|
||||
grid = UI.ScrollingGrid {
|
||||
y = -6, ey = -2,
|
||||
columns = {
|
||||
{ heading = 'Name', key = 'name', width = UI.term.width }
|
||||
},
|
||||
sortColumn = 'name',
|
||||
},
|
||||
statusBar = UI.StatusBar {
|
||||
status = 'q to return',
|
||||
},
|
||||
accelerators = {
|
||||
q = 'back',
|
||||
backspace = 'back',
|
||||
},
|
||||
}
|
||||
|
||||
function methodsPage:enable(p)
|
||||
|
||||
self.peripheral = p or self.peripheral
|
||||
self.peripheral = p or self.peripheral
|
||||
|
||||
p = peripheral.wrap(self.peripheral.side)
|
||||
if p.getDocs then
|
||||
-- plethora
|
||||
self.grid.values = { }
|
||||
for k,v in pairs(p.getDocs()) do
|
||||
table.insert(self.grid.values, {
|
||||
name = k,
|
||||
doc = v,
|
||||
})
|
||||
end
|
||||
elseif not p.getAdvancedMethodsData then
|
||||
-- computercraft
|
||||
self.grid.values = { }
|
||||
for name in pairs(p) do
|
||||
table.insert(self.grid.values, {
|
||||
name = name,
|
||||
noext = true,
|
||||
})
|
||||
end
|
||||
else
|
||||
-- open peripherals
|
||||
self.grid.values = p.getAdvancedMethodsData()
|
||||
for name,f in pairs(self.grid.values) do
|
||||
f.name = name
|
||||
end
|
||||
end
|
||||
p = peripheral.wrap(self.peripheral.side)
|
||||
if p.getDocs then
|
||||
-- plethora
|
||||
self.grid.values = { }
|
||||
for k,v in pairs(p.getDocs()) do
|
||||
table.insert(self.grid.values, {
|
||||
name = k,
|
||||
doc = v,
|
||||
})
|
||||
end
|
||||
elseif not p.getAdvancedMethodsData then
|
||||
-- computercraft
|
||||
self.grid.values = { }
|
||||
for name in pairs(p) do
|
||||
table.insert(self.grid.values, {
|
||||
name = name,
|
||||
noext = true,
|
||||
})
|
||||
end
|
||||
else
|
||||
-- open peripherals
|
||||
self.grid.values = p.getAdvancedMethodsData()
|
||||
for name,f in pairs(self.grid.values) do
|
||||
f.name = name
|
||||
end
|
||||
end
|
||||
|
||||
self.grid:update()
|
||||
self.grid:setIndex(1)
|
||||
self.grid:update()
|
||||
self.grid:setIndex(1)
|
||||
|
||||
self.doc:setText(self:getDocumentation())
|
||||
self.doc:setText(self:getDocumentation())
|
||||
|
||||
self.statusBar:setStatus(self.peripheral.type)
|
||||
UI.Page.enable(self)
|
||||
self.statusBar:setStatus(self.peripheral.type)
|
||||
UI.Page.enable(self)
|
||||
|
||||
self:setFocus(self.grid)
|
||||
self:setFocus(self.grid)
|
||||
end
|
||||
|
||||
function methodsPage:eventHandler(event)
|
||||
if event.type == 'back' then
|
||||
UI:setPage(peripheralsPage)
|
||||
return true
|
||||
elseif event.type == 'grid_focus_row' then
|
||||
self.doc:setText(self:getDocumentation())
|
||||
end
|
||||
return UI.Page.eventHandler(self, event)
|
||||
if event.type == 'back' then
|
||||
UI:setPage(peripheralsPage)
|
||||
return true
|
||||
elseif event.type == 'grid_focus_row' then
|
||||
self.doc:setText(self:getDocumentation())
|
||||
end
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
|
||||
function methodsPage:getDocumentation()
|
||||
|
||||
local method = self.grid:getSelected()
|
||||
local method = self.grid:getSelected()
|
||||
|
||||
if method.noext then -- computercraft docs
|
||||
return 'No documentation'
|
||||
end
|
||||
if method.noext then -- computercraft docs
|
||||
return 'No documentation'
|
||||
end
|
||||
|
||||
if method.doc then -- plethora docs
|
||||
return Ansi.yellow .. method.doc
|
||||
end
|
||||
if method.doc then -- plethora docs
|
||||
return Ansi.yellow .. method.doc
|
||||
end
|
||||
|
||||
-- open peripherals docs
|
||||
local sb = { }
|
||||
if method.description then
|
||||
table.insert(sb, method.description .. '\n\n')
|
||||
end
|
||||
-- open peripherals docs
|
||||
local sb = { }
|
||||
if method.description then
|
||||
table.insert(sb, method.description .. '\n\n')
|
||||
end
|
||||
|
||||
if method.returnTypes ~= '()' then
|
||||
table.insert(sb, Ansi.yellow .. method.returnTypes .. ' ')
|
||||
end
|
||||
table.insert(sb, Ansi.blue .. method.name .. Ansi.reset .. '(')
|
||||
if method.returnTypes ~= '()' then
|
||||
table.insert(sb, Ansi.yellow .. method.returnTypes .. ' ')
|
||||
end
|
||||
table.insert(sb, Ansi.blue .. method.name .. Ansi.reset .. '(')
|
||||
|
||||
for k,arg in ipairs(method.args) do
|
||||
if arg.optional then
|
||||
table.insert(sb, Ansi.orange .. string.format('[%s]', arg.name))
|
||||
else
|
||||
table.insert(sb, Ansi.green .. arg.name)
|
||||
end
|
||||
if k < #method.args then
|
||||
table.insert(sb, ',')
|
||||
end
|
||||
end
|
||||
table.insert(sb, Ansi.reset .. ')')
|
||||
for k,arg in ipairs(method.args) do
|
||||
if arg.optional then
|
||||
table.insert(sb, Ansi.orange .. string.format('[%s]', arg.name))
|
||||
else
|
||||
table.insert(sb, Ansi.green .. arg.name)
|
||||
end
|
||||
if k < #method.args then
|
||||
table.insert(sb, ',')
|
||||
end
|
||||
end
|
||||
table.insert(sb, Ansi.reset .. ')')
|
||||
|
||||
Util.filterInplace(method.args, function(a) return #a.description > 0 end)
|
||||
if #method.args > 0 then
|
||||
table.insert(sb, '\n\n')
|
||||
for k,arg in ipairs(method.args) do
|
||||
if arg.optional then
|
||||
table.insert(sb, Ansi.orange)
|
||||
else
|
||||
table.insert(sb, Ansi.green)
|
||||
end
|
||||
table.insert(sb, arg.name .. Ansi.reset .. ': ' .. arg.description)
|
||||
if k ~= #method.args then
|
||||
table.insert(sb, '\n\n')
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.concat(sb)
|
||||
Util.filterInplace(method.args, function(a) return #a.description > 0 end)
|
||||
if #method.args > 0 then
|
||||
table.insert(sb, '\n\n')
|
||||
for k,arg in ipairs(method.args) do
|
||||
if arg.optional then
|
||||
table.insert(sb, Ansi.orange)
|
||||
else
|
||||
table.insert(sb, Ansi.green)
|
||||
end
|
||||
table.insert(sb, arg.name .. Ansi.reset .. ': ' .. arg.description)
|
||||
if k ~= #method.args then
|
||||
table.insert(sb, '\n\n')
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.concat(sb)
|
||||
end
|
||||
|
||||
Event.on('peripheral', function()
|
||||
peripheralsPage:updatePeripherals()
|
||||
peripheralsPage:updatePeripherals()
|
||||
end)
|
||||
|
||||
Event.on('peripheral_detach', function()
|
||||
peripheralsPage:updatePeripherals()
|
||||
peripheralsPage:updatePeripherals()
|
||||
end)
|
||||
|
||||
UI:setPage(peripheralsPage)
|
||||
|
||||
UI:setPages({
|
||||
methods = methodsPage,
|
||||
methods = methodsPage,
|
||||
})
|
||||
|
||||
UI:pullEvents()
|
||||
|
||||
@@ -10,230 +10,230 @@ local peripheral = _G.peripheral
|
||||
|
||||
local drives = { }
|
||||
peripheral.find('drive', function(n, v)
|
||||
if not drives.left then
|
||||
drives.left = Util.shallowCopy(v)
|
||||
drives.left.name = n
|
||||
else
|
||||
drives.right = Util.shallowCopy(v)
|
||||
drives.right.name = n
|
||||
end
|
||||
if not drives.left then
|
||||
drives.left = Util.shallowCopy(v)
|
||||
drives.left.name = n
|
||||
else
|
||||
drives.right = Util.shallowCopy(v)
|
||||
drives.right.name = n
|
||||
end
|
||||
end)
|
||||
|
||||
if not (drives.left and drives.right) then
|
||||
error('Two drives are required')
|
||||
error('Two drives are required')
|
||||
end
|
||||
|
||||
local COPY_LEFT = 1
|
||||
local COPY_RIGHT = 2
|
||||
local directions = {
|
||||
[ COPY_LEFT ] = { text = '-->>' },
|
||||
[ COPY_RIGHT ] = { text = '<<--' },
|
||||
[ COPY_LEFT ] = { text = '-->>' },
|
||||
[ COPY_RIGHT ] = { text = '<<--' },
|
||||
}
|
||||
|
||||
local config = Config.load('DiskCopy', {
|
||||
eject = true,
|
||||
automatic = false,
|
||||
copyDir = COPY_LEFT
|
||||
eject = true,
|
||||
automatic = false,
|
||||
copyDir = COPY_LEFT
|
||||
})
|
||||
|
||||
local page = UI.Page {
|
||||
linfo = UI.Window {
|
||||
x = 2, y = 2, ey = 5, width = 18,
|
||||
},
|
||||
rinfo = UI.Window {
|
||||
x = -19, y = 2, ey = 5, width = 18,
|
||||
},
|
||||
dir = UI.Button {
|
||||
x = 17, y = 7, width = 6,
|
||||
event = 'change_dir',
|
||||
},
|
||||
progress = UI.ProgressBar {
|
||||
x = 2, ex = -2, y = -4,
|
||||
backgroundColor = colors.black,
|
||||
},
|
||||
ejectText = UI.Text {
|
||||
x = 2, y = -2,
|
||||
value = 'Eject'
|
||||
},
|
||||
eject = UI.Checkbox {
|
||||
x = 8, y = -2,
|
||||
},
|
||||
automaticText = UI.Text {
|
||||
x = 12, y = -2,
|
||||
value = 'Copy automatically'
|
||||
},
|
||||
automatic = UI.Checkbox {
|
||||
x = 31, y = -2,
|
||||
},
|
||||
copyButton = UI.Button {
|
||||
x = -7, y = -2,
|
||||
text = 'Copy',
|
||||
event = 'copy',
|
||||
inactive = true,
|
||||
},
|
||||
warning = UI.Text {
|
||||
x = 2, ex = -2, y = -5,
|
||||
align = 'center',
|
||||
textColor = colors.orange,
|
||||
},
|
||||
notification = UI.Notification { },
|
||||
linfo = UI.Window {
|
||||
x = 2, y = 2, ey = 5, width = 18,
|
||||
},
|
||||
rinfo = UI.Window {
|
||||
x = -19, y = 2, ey = 5, width = 18,
|
||||
},
|
||||
dir = UI.Button {
|
||||
x = 17, y = 7, width = 6,
|
||||
event = 'change_dir',
|
||||
},
|
||||
progress = UI.ProgressBar {
|
||||
x = 2, ex = -2, y = -4,
|
||||
backgroundColor = colors.black,
|
||||
},
|
||||
ejectText = UI.Text {
|
||||
x = 2, y = -2,
|
||||
value = 'Eject'
|
||||
},
|
||||
eject = UI.Checkbox {
|
||||
x = 8, y = -2,
|
||||
},
|
||||
automaticText = UI.Text {
|
||||
x = 12, y = -2,
|
||||
value = 'Copy automatically'
|
||||
},
|
||||
automatic = UI.Checkbox {
|
||||
x = 31, y = -2,
|
||||
},
|
||||
copyButton = UI.Button {
|
||||
x = -7, y = -2,
|
||||
text = 'Copy',
|
||||
event = 'copy',
|
||||
inactive = true,
|
||||
},
|
||||
warning = UI.Text {
|
||||
x = 2, ex = -2, y = -5,
|
||||
align = 'center',
|
||||
textColor = colors.orange,
|
||||
},
|
||||
notification = UI.Notification { },
|
||||
}
|
||||
|
||||
function page:enable()
|
||||
Util.merge(self.dir, directions[config.copyDir])
|
||||
Util.merge(self.dir, directions[config.copyDir])
|
||||
|
||||
self.eject.value = config.eject
|
||||
self.automatic.value = config.automatic
|
||||
self.eject.value = config.eject
|
||||
self.automatic.value = config.automatic
|
||||
|
||||
self.dir.x = math.floor((self.width / 2) - 3) + 1
|
||||
self.dir.x = math.floor((self.width / 2) - 3) + 1
|
||||
|
||||
UI.Page.enable(self)
|
||||
UI.Page.enable(self)
|
||||
end
|
||||
|
||||
local function isValid(drive)
|
||||
return drive.isDiskPresent() and drive.getMountPath()
|
||||
return drive.isDiskPresent() and drive.getMountPath()
|
||||
end
|
||||
|
||||
local function needsLabel(drive)
|
||||
return drive.isDiskPresent() and not drive.getMountPath() and not drive.getAudioTitle()
|
||||
return drive.isDiskPresent() and not drive.getMountPath() and not drive.getAudioTitle()
|
||||
end
|
||||
|
||||
function page:drawInfo(drive, textArea)
|
||||
local function getLabel()
|
||||
return not drive.isDiskPresent() and 'empty' or
|
||||
not drive.getMountPath() and 'invalid' or
|
||||
drive.getDiskLabel() or 'unlabeled'
|
||||
end
|
||||
local function getLabel()
|
||||
return not drive.isDiskPresent() and 'empty' or
|
||||
not drive.getMountPath() and 'invalid' or
|
||||
drive.getDiskLabel() or 'unlabeled'
|
||||
end
|
||||
|
||||
local function getUsed()
|
||||
return isValid(drive) and fs.getSize(drive.getMountPath(), true) or 0
|
||||
end
|
||||
local function getUsed()
|
||||
return isValid(drive) and fs.getSize(drive.getMountPath(), true) or 0
|
||||
end
|
||||
|
||||
local function getFree()
|
||||
return isValid(drive) and fs.getFreeSpace(drive.getMountPath()) or 0
|
||||
end
|
||||
local function getFree()
|
||||
return isValid(drive) and fs.getFreeSpace(drive.getMountPath()) or 0
|
||||
end
|
||||
|
||||
textArea:setCursorPos(1, 1)
|
||||
textArea:print(string.format('Drive: %s%s%s\nLabel: %s%s%s\nUsed: %s%s%s\nFree: %s%s%s',
|
||||
Ansi.yellow, drive.name, Ansi.reset,
|
||||
isValid(drive) and Ansi.yellow or Ansi.orange, getLabel():sub(1, 10), Ansi.reset,
|
||||
Ansi.yellow, Util.toBytes(getUsed()), Ansi.reset,
|
||||
Ansi.yellow, Util.toBytes(getFree()), Ansi.reset))
|
||||
textArea:setCursorPos(1, 1)
|
||||
textArea:print(string.format('Drive: %s%s%s\nLabel: %s%s%s\nUsed: %s%s%s\nFree: %s%s%s',
|
||||
Ansi.yellow, drive.name, Ansi.reset,
|
||||
isValid(drive) and Ansi.yellow or Ansi.orange, getLabel():sub(1, 10), Ansi.reset,
|
||||
Ansi.yellow, Util.toBytes(getUsed()), Ansi.reset,
|
||||
Ansi.yellow, Util.toBytes(getFree()), Ansi.reset))
|
||||
end
|
||||
|
||||
function page:scan()
|
||||
local showWarning = needsLabel(drives.left) or needsLabel(drives.right)
|
||||
local valid = isValid(drives.left) and isValid(drives.right)
|
||||
local showWarning = needsLabel(drives.left) or needsLabel(drives.right)
|
||||
local valid = isValid(drives.left) and isValid(drives.right)
|
||||
|
||||
self.warning.value = showWarning and 'Computers must be labeled'
|
||||
self.copyButton.inactive = not valid
|
||||
self.warning.value = showWarning and 'Computers must be labeled'
|
||||
self.copyButton.inactive = not valid
|
||||
|
||||
self:draw()
|
||||
self.progress:centeredWrite(1, 'Analyzing Disks..')
|
||||
self.progress:sync()
|
||||
self:draw()
|
||||
self.progress:centeredWrite(1, 'Analyzing Disks..')
|
||||
self.progress:sync()
|
||||
|
||||
self:drawInfo(drives.left, self.linfo)
|
||||
self:drawInfo(drives.right, self.rinfo)
|
||||
self:drawInfo(drives.left, self.linfo)
|
||||
self:drawInfo(drives.right, self.rinfo)
|
||||
|
||||
self.progress:clear()
|
||||
self.progress:clear()
|
||||
end
|
||||
|
||||
function page:copy()
|
||||
local sdrive = config.copyDir == COPY_LEFT and drives.left or drives.right
|
||||
local tdrive = config.copyDir == COPY_LEFT and drives.right or drives.left
|
||||
local sdrive = config.copyDir == COPY_LEFT and drives.left or drives.right
|
||||
local tdrive = config.copyDir == COPY_LEFT and drives.right or drives.left
|
||||
|
||||
local throttle = Util.throttle()
|
||||
local sourceFiles, targetFiles = { }, { }
|
||||
local throttle = Util.throttle()
|
||||
local sourceFiles, targetFiles = { }, { }
|
||||
|
||||
local function getListing(mountPath, path, files)
|
||||
for _,f in pairs(fs.list(path)) do
|
||||
local file = fs.combine(path, f)
|
||||
if not fs.isReadOnly(file) then
|
||||
files[string.sub(file, #mountPath + 1)] = true
|
||||
if fs.isDir(file) then
|
||||
getListing(mountPath, file, files)
|
||||
end
|
||||
end
|
||||
end
|
||||
throttle()
|
||||
end
|
||||
local function getListing(mountPath, path, files)
|
||||
for _,f in pairs(fs.list(path)) do
|
||||
local file = fs.combine(path, f)
|
||||
if not fs.isReadOnly(file) then
|
||||
files[string.sub(file, #mountPath + 1)] = true
|
||||
if fs.isDir(file) then
|
||||
getListing(mountPath, file, files)
|
||||
end
|
||||
end
|
||||
end
|
||||
throttle()
|
||||
end
|
||||
|
||||
self.progress:centeredWrite(1, 'Computing..')
|
||||
self.progress:sync()
|
||||
self.progress:centeredWrite(1, 'Computing..')
|
||||
self.progress:sync()
|
||||
|
||||
getListing(sdrive.getMountPath(), sdrive.getMountPath(), sourceFiles)
|
||||
getListing(tdrive.getMountPath(), tdrive.getMountPath(), targetFiles)
|
||||
getListing(sdrive.getMountPath(), sdrive.getMountPath(), sourceFiles)
|
||||
getListing(tdrive.getMountPath(), tdrive.getMountPath(), targetFiles)
|
||||
|
||||
local copied = 0
|
||||
local totalFiles = Util.size(sourceFiles)
|
||||
local copied = 0
|
||||
local totalFiles = Util.size(sourceFiles)
|
||||
|
||||
local function rawCopy(source, target)
|
||||
if fs.isDir(source) then
|
||||
copied = copied + 1
|
||||
if not fs.exists(target) then
|
||||
fs.makeDir(target)
|
||||
end
|
||||
for _,f in pairs(fs.list(source)) do
|
||||
rawCopy(fs.combine(source, f), fs.combine(target, f))
|
||||
end
|
||||
local function rawCopy(source, target)
|
||||
if fs.isDir(source) then
|
||||
copied = copied + 1
|
||||
if not fs.exists(target) then
|
||||
fs.makeDir(target)
|
||||
end
|
||||
for _,f in pairs(fs.list(source)) do
|
||||
rawCopy(fs.combine(source, f), fs.combine(target, f))
|
||||
end
|
||||
|
||||
else
|
||||
if fs.exists(target) then
|
||||
fs.delete(target)
|
||||
end
|
||||
else
|
||||
if fs.exists(target) then
|
||||
fs.delete(target)
|
||||
end
|
||||
|
||||
fs.copy(source, target)
|
||||
copied = copied + 1
|
||||
self.progress.value = copied * 100 / totalFiles
|
||||
self.progress:draw()
|
||||
self.progress:sync()
|
||||
end
|
||||
throttle()
|
||||
end
|
||||
fs.copy(source, target)
|
||||
copied = copied + 1
|
||||
self.progress.value = copied * 100 / totalFiles
|
||||
self.progress:draw()
|
||||
self.progress:sync()
|
||||
end
|
||||
throttle()
|
||||
end
|
||||
|
||||
local function cleanup()
|
||||
for k in pairs(targetFiles) do
|
||||
if not sourceFiles[k] then
|
||||
fs.delete(fs.combine(tdrive.getMountPath(), k))
|
||||
end
|
||||
end
|
||||
end
|
||||
local function cleanup()
|
||||
for k in pairs(targetFiles) do
|
||||
if not sourceFiles[k] then
|
||||
fs.delete(fs.combine(tdrive.getMountPath(), k))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.progress:clear()
|
||||
rawCopy(sdrive.getMountPath(), tdrive.getMountPath())
|
||||
cleanup()
|
||||
self.progress:centeredWrite(1, 'Copy Complete', colors.lime, colors.black)
|
||||
self.progress:sync()
|
||||
self.progress:clear()
|
||||
rawCopy(sdrive.getMountPath(), tdrive.getMountPath())
|
||||
cleanup()
|
||||
self.progress:centeredWrite(1, 'Copy Complete', colors.lime, colors.black)
|
||||
self.progress:sync()
|
||||
|
||||
self.progress.value = 0
|
||||
self.progress:clear()
|
||||
self.progress.value = 0
|
||||
self.progress:clear()
|
||||
|
||||
self:scan()
|
||||
self:scan()
|
||||
|
||||
if config.eject then
|
||||
tdrive.ejectDisk()
|
||||
end
|
||||
if config.eject then
|
||||
tdrive.ejectDisk()
|
||||
end
|
||||
end
|
||||
|
||||
function page:eventHandler(event)
|
||||
if event.type == 'change_dir' then
|
||||
config.copyDir = (config.copyDir) % 2 + 1
|
||||
Util.merge(self.dir, directions[config.copyDir])
|
||||
Config.update('DiskCopy', config)
|
||||
self.dir:draw()
|
||||
if event.type == 'change_dir' then
|
||||
config.copyDir = (config.copyDir) % 2 + 1
|
||||
Util.merge(self.dir, directions[config.copyDir])
|
||||
Config.update('DiskCopy', config)
|
||||
self.dir:draw()
|
||||
|
||||
elseif event.type == 'copy' then
|
||||
self:copy()
|
||||
elseif event.type == 'copy' then
|
||||
self:copy()
|
||||
|
||||
elseif event.type == 'checkbox_change' then
|
||||
if event.element == self.eject then
|
||||
config.eject = not not event.checked
|
||||
elseif event.element == self.automatic then
|
||||
config.automatic = not not event.checked
|
||||
end
|
||||
elseif event.type == 'checkbox_change' then
|
||||
if event.element == self.eject then
|
||||
config.eject = not not event.checked
|
||||
elseif event.element == self.automatic then
|
||||
config.automatic = not not event.checked
|
||||
end
|
||||
|
||||
Config.update('DiskCopy', config)
|
||||
event.element:draw()
|
||||
Config.update('DiskCopy', config)
|
||||
event.element:draw()
|
||||
|
||||
else
|
||||
return UI.Page.eventHandler(self, event)
|
||||
@@ -242,22 +242,22 @@ function page:eventHandler(event)
|
||||
end
|
||||
|
||||
Event.on("disk", function()
|
||||
page:scan()
|
||||
page:sync()
|
||||
page:scan()
|
||||
page:sync()
|
||||
|
||||
if config.automatic and not page.copyButton.inactive then
|
||||
page:copy()
|
||||
end
|
||||
if config.automatic and not page.copyButton.inactive then
|
||||
page:copy()
|
||||
end
|
||||
end)
|
||||
|
||||
Event.on("disk_eject", function()
|
||||
page:scan()
|
||||
page:sync()
|
||||
page:scan()
|
||||
page:sync()
|
||||
end)
|
||||
|
||||
Event.onTimeout(.2, function()
|
||||
page:scan()
|
||||
page:sync()
|
||||
page:scan()
|
||||
page:sync()
|
||||
end)
|
||||
|
||||
UI:setPage(page)
|
||||
|
||||
@@ -8,131 +8,131 @@ local os = _G.os
|
||||
UI:configure('Events', ...)
|
||||
|
||||
local page = UI.Page {
|
||||
menuBar = UI.MenuBar {
|
||||
buttons = {
|
||||
{ text = 'Filter', event = 'filter' },
|
||||
{ text = 'Reset', event = 'reset' },
|
||||
{ text = 'Pause ', event = 'toggle', name = 'pauseButton' },
|
||||
},
|
||||
},
|
||||
grid = UI.Grid {
|
||||
y = 2,
|
||||
columns = {
|
||||
{ key = 'event' },
|
||||
{ key = 'p1' },
|
||||
{ key = 'p2' },
|
||||
{ key = 'p3' },
|
||||
{ key = 'p4' },
|
||||
{ key = 'p5' },
|
||||
},
|
||||
autospace = true,
|
||||
disableHeader = true,
|
||||
},
|
||||
accelerators = {
|
||||
f = 'filter',
|
||||
p = 'toggle',
|
||||
r = 'reset',
|
||||
c = 'clear',
|
||||
q = 'quit',
|
||||
},
|
||||
filtered = { },
|
||||
menuBar = UI.MenuBar {
|
||||
buttons = {
|
||||
{ text = 'Filter', event = 'filter' },
|
||||
{ text = 'Reset', event = 'reset' },
|
||||
{ text = 'Pause ', event = 'toggle', name = 'pauseButton' },
|
||||
},
|
||||
},
|
||||
grid = UI.Grid {
|
||||
y = 2,
|
||||
columns = {
|
||||
{ key = 'event' },
|
||||
{ key = 'p1' },
|
||||
{ key = 'p2' },
|
||||
{ key = 'p3' },
|
||||
{ key = 'p4' },
|
||||
{ key = 'p5' },
|
||||
},
|
||||
autospace = true,
|
||||
disableHeader = true,
|
||||
},
|
||||
accelerators = {
|
||||
f = 'filter',
|
||||
p = 'toggle',
|
||||
r = 'reset',
|
||||
c = 'clear',
|
||||
q = 'quit',
|
||||
},
|
||||
filtered = { },
|
||||
}
|
||||
|
||||
function page:eventHandler(event)
|
||||
|
||||
if event.type == 'filter' then
|
||||
local entry = self.grid:getSelected()
|
||||
self.filtered[entry.event] = true
|
||||
if event.type == 'filter' then
|
||||
local entry = self.grid:getSelected()
|
||||
self.filtered[entry.event] = true
|
||||
|
||||
elseif event.type == 'toggle' then
|
||||
self.paused = not self.paused
|
||||
if self.paused then
|
||||
self.menuBar.pauseButton.text = 'Resume'
|
||||
else
|
||||
self.menuBar.pauseButton.text = 'Pause '
|
||||
end
|
||||
self.menuBar:draw()
|
||||
elseif event.type == 'toggle' then
|
||||
self.paused = not self.paused
|
||||
if self.paused then
|
||||
self.menuBar.pauseButton.text = 'Resume'
|
||||
else
|
||||
self.menuBar.pauseButton.text = 'Pause '
|
||||
end
|
||||
self.menuBar:draw()
|
||||
|
||||
elseif event.type == 'grid_select' then
|
||||
multishell.openTab({
|
||||
path = 'sys/apps/Lua.lua',
|
||||
args = { event.selected },
|
||||
focused = true,
|
||||
})
|
||||
elseif event.type == 'grid_select' then
|
||||
multishell.openTab({
|
||||
path = 'sys/apps/Lua.lua',
|
||||
args = { event.selected },
|
||||
focused = true,
|
||||
})
|
||||
|
||||
elseif event.type == 'reset' then
|
||||
self.filtered = { }
|
||||
self.grid:setValues({ })
|
||||
self.grid:draw()
|
||||
if self.paused then
|
||||
self:emit({ type = 'toggle' })
|
||||
end
|
||||
elseif event.type == 'reset' then
|
||||
self.filtered = { }
|
||||
self.grid:setValues({ })
|
||||
self.grid:draw()
|
||||
if self.paused then
|
||||
self:emit({ type = 'toggle' })
|
||||
end
|
||||
|
||||
elseif event.type == 'clear' then
|
||||
self.grid:setValues({ })
|
||||
self.grid:draw()
|
||||
elseif event.type == 'clear' then
|
||||
self.grid:setValues({ })
|
||||
self.grid:draw()
|
||||
|
||||
elseif event.type == 'quit' then
|
||||
UI:exitPullEvents()
|
||||
elseif event.type == 'quit' then
|
||||
UI:exitPullEvents()
|
||||
|
||||
--[[
|
||||
elseif event.type == 'focus_change' then
|
||||
if event.focused == self.grid then
|
||||
if not self.paused then
|
||||
self:emit({ type = 'toggle' })
|
||||
end
|
||||
end
|
||||
--]]
|
||||
--[[
|
||||
elseif event.type == 'focus_change' then
|
||||
if event.focused == self.grid then
|
||||
if not self.paused then
|
||||
self:emit({ type = 'toggle' })
|
||||
end
|
||||
end
|
||||
--]]
|
||||
|
||||
else
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
else
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function page.grid:getDisplayValues(row)
|
||||
row = Util.shallowCopy(row)
|
||||
row = Util.shallowCopy(row)
|
||||
|
||||
local function tovalue(s)
|
||||
if type(s) == 'table' then
|
||||
return 'table'
|
||||
end
|
||||
return s
|
||||
end
|
||||
local function tovalue(s)
|
||||
if type(s) == 'table' then
|
||||
return 'table'
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
for k,v in pairs(row) do
|
||||
row[k] = tovalue(v)
|
||||
end
|
||||
for k,v in pairs(row) do
|
||||
row[k] = tovalue(v)
|
||||
end
|
||||
|
||||
return row
|
||||
return row
|
||||
end
|
||||
|
||||
function page.grid:draw()
|
||||
self:adjustWidth()
|
||||
UI.Grid.draw(self)
|
||||
self:adjustWidth()
|
||||
UI.Grid.draw(self)
|
||||
end
|
||||
|
||||
Event.addRoutine(function()
|
||||
|
||||
while true do
|
||||
local e = { os.pullEvent() }
|
||||
if not page.paused and not page.filtered[e[1]] then
|
||||
table.insert(page.grid.values, 1, {
|
||||
event = e[1],
|
||||
p1 = e[2],
|
||||
p2 = e[3],
|
||||
p3 = e[4],
|
||||
p4 = e[5],
|
||||
p5 = e[6],
|
||||
})
|
||||
if #page.grid.values > page.grid.height then
|
||||
table.remove(page.grid.values, #page.grid.values)
|
||||
end
|
||||
page.grid:update()
|
||||
page.grid:draw()
|
||||
page:sync()
|
||||
end
|
||||
end
|
||||
while true do
|
||||
local e = { os.pullEvent() }
|
||||
if not page.paused and not page.filtered[e[1]] then
|
||||
table.insert(page.grid.values, 1, {
|
||||
event = e[1],
|
||||
p1 = e[2],
|
||||
p2 = e[3],
|
||||
p3 = e[4],
|
||||
p4 = e[5],
|
||||
p5 = e[6],
|
||||
})
|
||||
if #page.grid.values > page.grid.height then
|
||||
table.remove(page.grid.values, #page.grid.values)
|
||||
end
|
||||
page.grid:update()
|
||||
page.grid:draw()
|
||||
page:sync()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
UI:setPage(page)
|
||||
|
||||
@@ -15,32 +15,64 @@ local gpt = GPS.getPoint() or error('GPS not found')
|
||||
local pts, blocks
|
||||
|
||||
local page = UI.Page {
|
||||
mode = UI.Chooser {
|
||||
x = 13, y = -1,
|
||||
choices = {
|
||||
{ name = 'No breaking', value = 'digNone' },
|
||||
{ name = 'Destructive', value = 'turtleSafe' },
|
||||
},
|
||||
value = 'digNone',
|
||||
},
|
||||
grid = UI.ScrollingGrid {
|
||||
y = 2, ey = -2,
|
||||
columns = {
|
||||
{ heading = 'Label', key = 'label' },
|
||||
{ heading = 'Dist', key = 'distance' },
|
||||
{ heading = 'Status', key = 'status' },
|
||||
{ heading = 'Fuel', key = 'fuel' },
|
||||
},
|
||||
sortColumn = 'distance',
|
||||
autospace = true,
|
||||
},
|
||||
menuBar = UI.MenuBar {
|
||||
buttons = {
|
||||
{ text = 'Range', event = 'range' },
|
||||
{ text = 'Stop', event = 'stop' },
|
||||
},
|
||||
mode = UI.Chooser {
|
||||
x = -16,
|
||||
choices = {
|
||||
{ name = 'No breaking', value = 'digNone' },
|
||||
{ name = 'Destructive', value = 'turtleSafe' },
|
||||
},
|
||||
value = 'digNone',
|
||||
},
|
||||
},
|
||||
grid = UI.ScrollingGrid {
|
||||
y = 2, ey = -2,
|
||||
columns = {
|
||||
{ heading = 'Label', key = 'label' },
|
||||
{ heading = 'Dist', key = 'distance' },
|
||||
{ heading = 'Status', key = 'status' },
|
||||
{ heading = 'Fuel', key = 'fuel' },
|
||||
},
|
||||
sortColumn = 'distance',
|
||||
autospace = true,
|
||||
},
|
||||
range = UI.SlideOut {
|
||||
y = -7, height = 7,
|
||||
backgroundColor = colors.cyan,
|
||||
titleBar = UI.TitleBar {
|
||||
event = 'cancel',
|
||||
title = 'Enter range',
|
||||
},
|
||||
notice = UI.TextArea {
|
||||
x = 2, ex = -2, y = 3, ey = 4,
|
||||
value =
|
||||
[[Select all turtles within a specified range]],
|
||||
},
|
||||
entry = UI.TextEntry {
|
||||
y = 6, x = 2, ex = 10,
|
||||
limit = 4,
|
||||
shadowText = 'range',
|
||||
accelerators = {
|
||||
enter = 'select_range',
|
||||
},
|
||||
},
|
||||
button = UI.Button {
|
||||
x = 12, y = 6,
|
||||
text = 'Apply',
|
||||
event = 'select_range',
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
function page.grid:getRowTextColor(row, selected)
|
||||
if swarm.pool[row.id] then
|
||||
return colors.yellow
|
||||
end
|
||||
return UI.ScrollingGrid.getRowTextColor(self, row, selected)
|
||||
if swarm.pool[row.id] then
|
||||
return colors.yellow
|
||||
end
|
||||
return UI.ScrollingGrid.getRowTextColor(self, row, selected)
|
||||
end
|
||||
|
||||
function page.grid:getDisplayValues(row)
|
||||
@@ -55,130 +87,155 @@ function page.grid:getDisplayValues(row)
|
||||
end
|
||||
|
||||
function page:enable()
|
||||
local function update()
|
||||
local t = { }
|
||||
for _,v in pairs(network) do
|
||||
if v.fuel and v.active and v.fuel > 0 and v.distance then
|
||||
table.insert(t, v)
|
||||
end
|
||||
end
|
||||
self.grid:setValues(t)
|
||||
end
|
||||
local function update()
|
||||
local t = { }
|
||||
for _,v in pairs(network) do
|
||||
if v.fuel and v.active and v.fuel > 0 and v.distance then
|
||||
table.insert(t, v)
|
||||
end
|
||||
end
|
||||
self.grid:setValues(t)
|
||||
end
|
||||
|
||||
Event.onInterval(3, function()
|
||||
update()
|
||||
self.grid:draw()
|
||||
self:sync()
|
||||
end)
|
||||
Event.onInterval(3, function()
|
||||
update()
|
||||
self.grid:draw()
|
||||
self:sync()
|
||||
end)
|
||||
|
||||
update()
|
||||
update()
|
||||
|
||||
UI.Page.enable(self)
|
||||
UI.Page.enable(self)
|
||||
end
|
||||
|
||||
local function follow(member)
|
||||
local turtle = member.turtle
|
||||
turtle.reset()
|
||||
turtle.set({
|
||||
digPolicy = page.mode.value,
|
||||
status = 'Following',
|
||||
})
|
||||
local turtle = member.turtle
|
||||
turtle.reset()
|
||||
turtle.set({
|
||||
digPolicy = page.menuBar.mode.value,
|
||||
status = 'Following',
|
||||
})
|
||||
|
||||
if not turtle.enableGPS(nil, true) then
|
||||
error('turtle: No GPS found')
|
||||
end
|
||||
if not turtle.enableGPS(nil, true) then
|
||||
error('turtle: No GPS found')
|
||||
end
|
||||
|
||||
member.snmp = Socket.connect(member.id, 161)
|
||||
member.snmp.co = coroutine.running()
|
||||
member.snmp = Socket.connect(member.id, 161)
|
||||
member.snmp.co = coroutine.running()
|
||||
|
||||
local pt
|
||||
local pt
|
||||
|
||||
while true do
|
||||
while pt and Point.same(gpt, pt) do
|
||||
os.sleep(.5)
|
||||
end
|
||||
pt = Point.copy(gpt)
|
||||
while true do
|
||||
while pt and Point.same(gpt, pt) do
|
||||
os.sleep(.5)
|
||||
end
|
||||
pt = Point.copy(gpt)
|
||||
|
||||
local cpt = Point.closest(turtle.getPoint(), pts)
|
||||
local cpt = Point.closest(turtle.getPoint(), pts)
|
||||
|
||||
turtle.abort(false)
|
||||
if turtle.pathfind(cpt, { blocks = blocks }) then
|
||||
turtle.headTowards(pt)
|
||||
end
|
||||
end
|
||||
turtle.abort(false)
|
||||
if turtle.pathfind(cpt, { blocks = blocks }) then
|
||||
turtle.headTowards(pt)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function swarm:onRemove(member, status, message)
|
||||
if member.socket then
|
||||
pcall(function()
|
||||
member.turtle.set({ status = 'idle' })
|
||||
member.turtle.abort(true)
|
||||
end)
|
||||
end
|
||||
if member.snmp then
|
||||
member.snmp:close()
|
||||
member.snmp = nil
|
||||
end
|
||||
if not status then
|
||||
_G._syslog(message)
|
||||
end
|
||||
if member.socket then
|
||||
pcall(function()
|
||||
member.turtle.set({ status = 'idle' })
|
||||
member.turtle.abort(true)
|
||||
end)
|
||||
end
|
||||
if member.snmp then
|
||||
member.snmp:close()
|
||||
member.snmp = nil
|
||||
end
|
||||
if not status then
|
||||
_G._syslog(message)
|
||||
end
|
||||
end
|
||||
|
||||
function page:eventHandler(event)
|
||||
if event.type == 'grid_select' then
|
||||
if not swarm.pool[event.selected.id] then
|
||||
swarm:add(event.selected.id)
|
||||
swarm:run(follow)
|
||||
else
|
||||
swarm:remove(event.selected.id)
|
||||
end
|
||||
self.grid:draw()
|
||||
if event.type == 'grid_select' then
|
||||
if not swarm.pool[event.selected.id] then
|
||||
swarm:add(event.selected.id)
|
||||
swarm:run(follow)
|
||||
else
|
||||
swarm:remove(event.selected.id)
|
||||
end
|
||||
self.grid:draw()
|
||||
|
||||
elseif event.type == 'choice_change' then
|
||||
local script = string.format('turtle.set({ digPolicy = "%s"})', event.value)
|
||||
for _, member in pairs(swarm.pool) do
|
||||
member.snmp:write({ type = 'scriptEx', args = script })
|
||||
end
|
||||
elseif event.type == 'choice_change' then
|
||||
local script = string.format('turtle.set({ digPolicy = "%s"})', event.value)
|
||||
for _, member in pairs(swarm.pool) do
|
||||
member.snmp:write({ type = 'scriptEx', args = script })
|
||||
end
|
||||
|
||||
else
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
elseif event.type == 'stop' then
|
||||
for id in pairs(swarm.pool) do
|
||||
swarm:remove(id)
|
||||
end
|
||||
|
||||
elseif event.type == 'range' then
|
||||
self.range:show()
|
||||
|
||||
elseif event.type == 'cancel' then
|
||||
self.range:hide()
|
||||
|
||||
elseif event.type == 'select_range' then
|
||||
local range = tonumber(self.range.entry.value)
|
||||
|
||||
if range and range > 0 then
|
||||
for id, v in pairs(network) do
|
||||
if not swarm.pool[id] then
|
||||
if v.fuel and v.active and v.fuel > 0 and v.distance and v.distance <= range then
|
||||
swarm:add(id)
|
||||
end
|
||||
end
|
||||
end
|
||||
swarm:run(follow)
|
||||
self.range:hide()
|
||||
end
|
||||
else
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
Event.addRoutine(function()
|
||||
while true do
|
||||
local pt = GPS.getPoint()
|
||||
if not pts or (pt and not Point.same(pt, gpt)) then
|
||||
gpt = pt
|
||||
pts = {
|
||||
{ x = pt.x + 2, z = pt.z, y = pt.y },
|
||||
{ x = pt.x - 2, z = pt.z, y = pt.y },
|
||||
{ x = pt.x, z = pt.z + 2, y = pt.y },
|
||||
{ x = pt.x, z = pt.z - 2, y = pt.y },
|
||||
}
|
||||
blocks = { }
|
||||
while true do
|
||||
local pt = GPS.getPoint()
|
||||
if not pts or (pt and not Point.same(pt, gpt)) then
|
||||
gpt = pt
|
||||
pts = {
|
||||
{ x = pt.x + 2, z = pt.z, y = pt.y },
|
||||
{ x = pt.x - 2, z = pt.z, y = pt.y },
|
||||
{ x = pt.x, z = pt.z + 2, y = pt.y },
|
||||
{ x = pt.x, z = pt.z - 2, y = pt.y },
|
||||
}
|
||||
blocks = { }
|
||||
|
||||
local function addBlocks(tpt)
|
||||
table.insert(blocks, tpt)
|
||||
local apts = Point.adjacentPoints(tpt)
|
||||
for _,apt in pairs(apts) do
|
||||
table.insert(blocks, apt)
|
||||
end
|
||||
end
|
||||
local function addBlocks(tpt)
|
||||
table.insert(blocks, tpt)
|
||||
local apts = Point.adjacentPoints(tpt)
|
||||
for _,apt in pairs(apts) do
|
||||
table.insert(blocks, apt)
|
||||
end
|
||||
end
|
||||
|
||||
-- don't run into player
|
||||
addBlocks(pt)
|
||||
addBlocks(Point.above(pt))
|
||||
-- don't run into player
|
||||
addBlocks(pt)
|
||||
addBlocks(Point.above(pt))
|
||||
|
||||
for _, member in pairs(swarm.pool) do
|
||||
if member.snmp then
|
||||
member.snmp:write({ type = 'scriptEx', args = 'turtle.abort(true)' })
|
||||
end
|
||||
end
|
||||
end
|
||||
os.sleep(1)
|
||||
end
|
||||
for _, member in pairs(swarm.pool) do
|
||||
if member.snmp then
|
||||
member.snmp:write({ type = 'scriptEx', args = 'turtle.abort(true)' })
|
||||
end
|
||||
end
|
||||
end
|
||||
os.sleep(1)
|
||||
end
|
||||
end)
|
||||
|
||||
UI:setPage(page)
|
||||
|
||||
@@ -21,12 +21,12 @@ local config = { }
|
||||
Config.load('Turtles', config)
|
||||
|
||||
local options = {
|
||||
turtle = { arg = 'i', type = 'number', value = config.id or -1,
|
||||
desc = 'Turtle ID' },
|
||||
tab = { arg = 's', type = 'string', value = config.tab or 'Sel',
|
||||
desc = 'Selected tab to display' },
|
||||
help = { arg = 'h', type = 'flag', value = false,
|
||||
desc = 'Displays the options' },
|
||||
turtle = { arg = 'i', type = 'number', value = config.id or -1,
|
||||
desc = 'Turtle ID' },
|
||||
tab = { arg = 's', type = 'string', value = config.tab or 'Sel',
|
||||
desc = 'Selected tab to display' },
|
||||
help = { arg = 'h', type = 'flag', value = false,
|
||||
desc = 'Displays the options' },
|
||||
}
|
||||
|
||||
local SCRIPTS_PATH = 'packages/common/etc/scripts'
|
||||
@@ -35,343 +35,343 @@ local nullTerm = Terminal.getNullTerm(term.current())
|
||||
local socket
|
||||
|
||||
local page = UI.Page {
|
||||
coords = UI.Window {
|
||||
backgroundColor = colors.black,
|
||||
height = 3,
|
||||
},
|
||||
tabs = UI.Tabs {
|
||||
x = 1, y = 4, ey = -2,
|
||||
scripts = UI.ScrollingGrid {
|
||||
tabTitle = 'Run',
|
||||
backgroundColor = colors.cyan,
|
||||
columns = {
|
||||
{ heading = '', key = 'label' },
|
||||
},
|
||||
disableHeader = true,
|
||||
sortColumn = 'label',
|
||||
autospace = true,
|
||||
},
|
||||
turtles = UI.ScrollingGrid {
|
||||
tabTitle = 'Select',
|
||||
backgroundColor = colors.cyan,
|
||||
columns = {
|
||||
{ heading = 'label', key = 'label' },
|
||||
{ heading = 'Dist', key = 'distance' },
|
||||
{ heading = 'Status', key = 'status' },
|
||||
{ heading = 'Fuel', key = 'fuel' },
|
||||
},
|
||||
disableHeader = true,
|
||||
sortColumn = 'label',
|
||||
autospace = true,
|
||||
},
|
||||
inventory = UI.ScrollingGrid {
|
||||
backgroundColor = colors.cyan,
|
||||
tabTitle = 'Inv',
|
||||
columns = {
|
||||
{ heading = '', key = 'index', width = 2 },
|
||||
{ heading = '', key = 'qty', width = 2 },
|
||||
{ heading = 'Inventory', key = 'id', width = UI.term.width - 7 },
|
||||
},
|
||||
disableHeader = true,
|
||||
sortColumn = 'index',
|
||||
},
|
||||
coords = UI.Window {
|
||||
backgroundColor = colors.black,
|
||||
height = 3,
|
||||
},
|
||||
tabs = UI.Tabs {
|
||||
x = 1, y = 4, ey = -2,
|
||||
scripts = UI.ScrollingGrid {
|
||||
tabTitle = 'Run',
|
||||
backgroundColor = colors.cyan,
|
||||
columns = {
|
||||
{ heading = '', key = 'label' },
|
||||
},
|
||||
disableHeader = true,
|
||||
sortColumn = 'label',
|
||||
autospace = true,
|
||||
},
|
||||
turtles = UI.ScrollingGrid {
|
||||
tabTitle = 'Select',
|
||||
backgroundColor = colors.cyan,
|
||||
columns = {
|
||||
{ heading = 'label', key = 'label' },
|
||||
{ heading = 'Dist', key = 'distance' },
|
||||
{ heading = 'Status', key = 'status' },
|
||||
{ heading = 'Fuel', key = 'fuel' },
|
||||
},
|
||||
disableHeader = true,
|
||||
sortColumn = 'label',
|
||||
autospace = true,
|
||||
},
|
||||
inventory = UI.ScrollingGrid {
|
||||
backgroundColor = colors.cyan,
|
||||
tabTitle = 'Inv',
|
||||
columns = {
|
||||
{ heading = '', key = 'index', width = 2 },
|
||||
{ heading = '', key = 'qty', width = 2 },
|
||||
{ heading = 'Inventory', key = 'id', width = UI.term.width - 7 },
|
||||
},
|
||||
disableHeader = true,
|
||||
sortColumn = 'index',
|
||||
},
|
||||
--[[
|
||||
policy = UI.ScrollingGrid {
|
||||
tabTitle = 'Mod',
|
||||
backgroundColor = UI.TabBar.defaults.selectedBackgroundColor,
|
||||
columns = {
|
||||
{ heading = 'label', key = 'label' },
|
||||
},
|
||||
values = policies,
|
||||
disableHeader = true,
|
||||
sortColumn = 'label',
|
||||
autospace = true,
|
||||
},
|
||||
]]
|
||||
action = UI.Window {
|
||||
tabTitle = 'Action',
|
||||
backgroundColor = colors.cyan,
|
||||
moveUp = UI.Button {
|
||||
x = 5, y = 2,
|
||||
text = 'up',
|
||||
fn = 'turtle.up',
|
||||
},
|
||||
moveDown = UI.Button {
|
||||
x = 5, y = 4,
|
||||
text = 'dn',
|
||||
fn = 'turtle.down',
|
||||
},
|
||||
moveForward = UI.Button {
|
||||
x = 9, y = 3,
|
||||
text = 'f',
|
||||
fn = 'turtle.forward',
|
||||
},
|
||||
moveBack = UI.Button {
|
||||
x = 2, y = 3,
|
||||
text = 'b',
|
||||
fn = 'turtle.back',
|
||||
},
|
||||
turnLeft = UI.Button {
|
||||
x = 2, y = 6,
|
||||
text = 'lt',
|
||||
fn = 'turtle.turnLeft',
|
||||
},
|
||||
turnRight = UI.Button {
|
||||
x = 8, y = 6,
|
||||
text = 'rt',
|
||||
fn = 'turtle.turnRight',
|
||||
},
|
||||
info = UI.TextArea {
|
||||
x = 15, y = 2,
|
||||
inactive = true,
|
||||
}
|
||||
},
|
||||
},
|
||||
statusBar = UI.StatusBar {
|
||||
values = { },
|
||||
columns = {
|
||||
{ key = 'status' },
|
||||
{ key = 'distance', width = 6 },
|
||||
{ key = 'fuel', width = 6 },
|
||||
},
|
||||
},
|
||||
notification = UI.Notification(),
|
||||
accelerators = {
|
||||
q = 'quit',
|
||||
},
|
||||
policy = UI.ScrollingGrid {
|
||||
tabTitle = 'Mod',
|
||||
backgroundColor = UI.TabBar.defaults.selectedBackgroundColor,
|
||||
columns = {
|
||||
{ heading = 'label', key = 'label' },
|
||||
},
|
||||
values = policies,
|
||||
disableHeader = true,
|
||||
sortColumn = 'label',
|
||||
autospace = true,
|
||||
},
|
||||
]]
|
||||
action = UI.Window {
|
||||
tabTitle = 'Action',
|
||||
backgroundColor = colors.cyan,
|
||||
moveUp = UI.Button {
|
||||
x = 5, y = 2,
|
||||
text = 'up',
|
||||
fn = 'turtle.up',
|
||||
},
|
||||
moveDown = UI.Button {
|
||||
x = 5, y = 4,
|
||||
text = 'dn',
|
||||
fn = 'turtle.down',
|
||||
},
|
||||
moveForward = UI.Button {
|
||||
x = 9, y = 3,
|
||||
text = 'f',
|
||||
fn = 'turtle.forward',
|
||||
},
|
||||
moveBack = UI.Button {
|
||||
x = 2, y = 3,
|
||||
text = 'b',
|
||||
fn = 'turtle.back',
|
||||
},
|
||||
turnLeft = UI.Button {
|
||||
x = 2, y = 6,
|
||||
text = 'lt',
|
||||
fn = 'turtle.turnLeft',
|
||||
},
|
||||
turnRight = UI.Button {
|
||||
x = 8, y = 6,
|
||||
text = 'rt',
|
||||
fn = 'turtle.turnRight',
|
||||
},
|
||||
info = UI.TextArea {
|
||||
x = 15, y = 2,
|
||||
inactive = true,
|
||||
}
|
||||
},
|
||||
},
|
||||
statusBar = UI.StatusBar {
|
||||
values = { },
|
||||
columns = {
|
||||
{ key = 'status' },
|
||||
{ key = 'distance', width = 6 },
|
||||
{ key = 'fuel', width = 6 },
|
||||
},
|
||||
},
|
||||
notification = UI.Notification(),
|
||||
accelerators = {
|
||||
q = 'quit',
|
||||
},
|
||||
}
|
||||
|
||||
function page:enable(turtle)
|
||||
self.turtle = turtle
|
||||
UI.Page.enable(self)
|
||||
self.turtle = turtle
|
||||
UI.Page.enable(self)
|
||||
end
|
||||
|
||||
function page:runFunction(script, nowrap)
|
||||
for _ = 1, 2 do
|
||||
if not socket then
|
||||
socket = Socket.connect(self.turtle.id, 161)
|
||||
end
|
||||
for _ = 1, 2 do
|
||||
if not socket then
|
||||
socket = Socket.connect(self.turtle.id, 161)
|
||||
end
|
||||
|
||||
if socket then
|
||||
if not nowrap then
|
||||
script = 'turtle.run(' .. script .. ')'
|
||||
end
|
||||
if socket:write({ type = 'scriptEx', args = script }) then
|
||||
local t = socket:read(3)
|
||||
if t then
|
||||
return table.unpack(t)
|
||||
end
|
||||
return false, 'Socket timeout'
|
||||
end
|
||||
end
|
||||
socket = nil
|
||||
end
|
||||
self.notification:error('Unable to connect')
|
||||
if socket then
|
||||
if not nowrap then
|
||||
script = 'turtle.run(' .. script .. ')'
|
||||
end
|
||||
if socket:write({ type = 'scriptEx', args = script }) then
|
||||
local t = socket:read(3)
|
||||
if t then
|
||||
return table.unpack(t)
|
||||
end
|
||||
return false, 'Socket timeout'
|
||||
end
|
||||
end
|
||||
socket = nil
|
||||
end
|
||||
self.notification:error('Unable to connect')
|
||||
end
|
||||
|
||||
function page:runScript(scriptName)
|
||||
if self.turtle then
|
||||
self.notification:info('Connecting')
|
||||
self:sync()
|
||||
if self.turtle then
|
||||
self.notification:info('Connecting')
|
||||
self:sync()
|
||||
|
||||
local cmd = string.format('Script %d %s', self.turtle.id, scriptName)
|
||||
local ot = term.redirect(nullTerm)
|
||||
pcall(function() shell.run(cmd) end)
|
||||
term.redirect(ot)
|
||||
self.notification:success('Sent')
|
||||
end
|
||||
local cmd = string.format('Script %d %s', self.turtle.id, scriptName)
|
||||
local ot = term.redirect(nullTerm)
|
||||
pcall(function() shell.run(cmd) end)
|
||||
term.redirect(ot)
|
||||
self.notification:success('Sent')
|
||||
end
|
||||
end
|
||||
|
||||
function page.coords:draw()
|
||||
local t = self.parent.turtle
|
||||
self:clear()
|
||||
if t then
|
||||
self:setCursorPos(2, 2)
|
||||
local ind = 'GPS'
|
||||
if not t.point.gps then
|
||||
ind = 'REL'
|
||||
end
|
||||
self:print(string.format('%s : %d,%d,%d',
|
||||
ind, t.point.x, t.point.y, t.point.z))
|
||||
end
|
||||
local t = self.parent.turtle
|
||||
self:clear()
|
||||
if t then
|
||||
self:setCursorPos(2, 2)
|
||||
local ind = 'GPS'
|
||||
if not t.point.gps then
|
||||
ind = 'REL'
|
||||
end
|
||||
self:print(string.format('%s : %d,%d,%d',
|
||||
ind, t.point.x, t.point.y, t.point.z))
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Inventory Tab ]]--
|
||||
function page.tabs.inventory:getRowTextColor(row, selected)
|
||||
if page.turtle and row.selected then
|
||||
return colors.yellow
|
||||
end
|
||||
return UI.ScrollingGrid.getRowTextColor(self, row, selected)
|
||||
if page.turtle and row.selected then
|
||||
return colors.yellow
|
||||
end
|
||||
return UI.ScrollingGrid.getRowTextColor(self, row, selected)
|
||||
end
|
||||
|
||||
function page.tabs.inventory:draw()
|
||||
local t = page.turtle
|
||||
Util.clear(self.values)
|
||||
if t then
|
||||
for _,v in ipairs(t.inventory) do
|
||||
if v.qty > 0 then
|
||||
table.insert(self.values, v)
|
||||
if v.index == t.slotIndex then
|
||||
v.selected = true
|
||||
end
|
||||
if v.id then
|
||||
v.id = itemDB:getName(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self:adjustWidth()
|
||||
self:update()
|
||||
UI.ScrollingGrid.draw(self)
|
||||
local t = page.turtle
|
||||
Util.clear(self.values)
|
||||
if t then
|
||||
for _,v in ipairs(t.inventory) do
|
||||
if v.qty > 0 then
|
||||
table.insert(self.values, v)
|
||||
if v.index == t.slotIndex then
|
||||
v.selected = true
|
||||
end
|
||||
if v.id then
|
||||
v.id = itemDB:getName(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self:adjustWidth()
|
||||
self:update()
|
||||
UI.ScrollingGrid.draw(self)
|
||||
end
|
||||
|
||||
function page.tabs.inventory:eventHandler(event)
|
||||
if event.type == 'grid_select' then
|
||||
local fn = string.format('turtle.select(%d)', event.selected.index)
|
||||
page:runFunction(fn)
|
||||
else
|
||||
return UI.ScrollingGrid.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
if event.type == 'grid_select' then
|
||||
local fn = string.format('turtle.select(%d)', event.selected.index)
|
||||
page:runFunction(fn)
|
||||
else
|
||||
return UI.ScrollingGrid.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function page.tabs.scripts:draw()
|
||||
|
||||
Util.clear(self.values)
|
||||
local files = fs.list(SCRIPTS_PATH)
|
||||
for _,path in pairs(files) do
|
||||
table.insert(self.values, { label = path, path = fs.combine(SCRIPTS_PATH, path) })
|
||||
end
|
||||
self:update()
|
||||
UI.ScrollingGrid.draw(self)
|
||||
Util.clear(self.values)
|
||||
local files = fs.list(SCRIPTS_PATH)
|
||||
for _,path in pairs(files) do
|
||||
table.insert(self.values, { label = path, path = fs.combine(SCRIPTS_PATH, path) })
|
||||
end
|
||||
self:update()
|
||||
UI.ScrollingGrid.draw(self)
|
||||
end
|
||||
|
||||
function page.tabs.scripts:eventHandler(event)
|
||||
if event.type == 'grid_select' then
|
||||
page:runScript(event.selected.label)
|
||||
else
|
||||
return UI.ScrollingGrid.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
if event.type == 'grid_select' then
|
||||
page:runScript(event.selected.label)
|
||||
else
|
||||
return UI.ScrollingGrid.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function page.tabs.turtles:getDisplayValues(row)
|
||||
row = Util.shallowCopy(row)
|
||||
if row.fuel then
|
||||
row.fuel = Util.toBytes(row.fuel)
|
||||
end
|
||||
if row.distance then
|
||||
row.distance = Util.round(row.distance, 1)
|
||||
end
|
||||
return row
|
||||
row = Util.shallowCopy(row)
|
||||
if row.fuel then
|
||||
row.fuel = Util.toBytes(row.fuel)
|
||||
end
|
||||
if row.distance then
|
||||
row.distance = Util.round(row.distance, 1)
|
||||
end
|
||||
return row
|
||||
end
|
||||
|
||||
function page.tabs.turtles:draw()
|
||||
Util.clear(self.values)
|
||||
for _,v in pairs(network) do
|
||||
if v.fuel then
|
||||
table.insert(self.values, v)
|
||||
end
|
||||
end
|
||||
self:update()
|
||||
UI.ScrollingGrid.draw(self)
|
||||
Util.clear(self.values)
|
||||
for _,v in pairs(network) do
|
||||
if v.fuel then
|
||||
table.insert(self.values, v)
|
||||
end
|
||||
end
|
||||
self:update()
|
||||
UI.ScrollingGrid.draw(self)
|
||||
end
|
||||
|
||||
function page.tabs.turtles:eventHandler(event)
|
||||
if event.type == 'grid_select' then
|
||||
page.turtle = event.selected
|
||||
config.id = event.selected.id
|
||||
Config.update('Turtles', config)
|
||||
multishell.setTitle(multishell.getCurrent(), page.turtle.label)
|
||||
if socket then
|
||||
socket:close()
|
||||
socket = nil
|
||||
end
|
||||
else
|
||||
return UI.ScrollingGrid.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
if event.type == 'grid_select' then
|
||||
page.turtle = event.selected
|
||||
config.id = event.selected.id
|
||||
Config.update('Turtles', config)
|
||||
multishell.setTitle(multishell.getCurrent(), page.turtle.label)
|
||||
if socket then
|
||||
socket:close()
|
||||
socket = nil
|
||||
end
|
||||
else
|
||||
return UI.ScrollingGrid.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function page.statusBar:draw()
|
||||
local t = self.parent.turtle
|
||||
if t then
|
||||
self.values.status = t.status
|
||||
self.values.distance = Util.round(t.distance, 2)
|
||||
self.values.fuel = Util.toBytes(t.fuel)
|
||||
end
|
||||
UI.StatusBar.draw(self)
|
||||
local t = self.parent.turtle
|
||||
if t then
|
||||
self.values.status = t.status
|
||||
self.values.distance = Util.round(t.distance, 2)
|
||||
self.values.fuel = Util.toBytes(t.fuel)
|
||||
end
|
||||
UI.StatusBar.draw(self)
|
||||
end
|
||||
|
||||
function page:showBlocks()
|
||||
local script = [[
|
||||
local function inspect(direction)
|
||||
local s,b = turtle['inspect' .. (direction or '')]()
|
||||
if not s then
|
||||
return 'minecraft:air:0'
|
||||
end
|
||||
return string.format('%s:%d', b.name, b.metadata)
|
||||
end
|
||||
local script = [[
|
||||
local function inspect(direction)
|
||||
local s,b = turtle['inspect' .. (direction or '')]()
|
||||
if not s then
|
||||
return 'minecraft:air:0'
|
||||
end
|
||||
return string.format('%s:%d', b.name, b.metadata)
|
||||
end
|
||||
|
||||
local bu, bf, bd = inspect('Up'), inspect(), inspect('Down')
|
||||
return string.format('%s\n%s\n%s', bu, bf, bd)
|
||||
]]
|
||||
local bu, bf, bd = inspect('Up'), inspect(), inspect('Down')
|
||||
return string.format('%s\n%s\n%s', bu, bf, bd)
|
||||
]]
|
||||
|
||||
local s, m = self:runFunction(script, true)
|
||||
self.tabs.action.info:setText(s or m)
|
||||
local s, m = self:runFunction(script, true)
|
||||
self.tabs.action.info:setText(s or m)
|
||||
end
|
||||
|
||||
function page:eventHandler(event)
|
||||
if event.type == 'quit' then
|
||||
UI:exitPullEvents()
|
||||
if event.type == 'quit' then
|
||||
UI:exitPullEvents()
|
||||
|
||||
elseif event.type == 'tab_select' then
|
||||
config.tab = event.button.text
|
||||
Config.update('Turtles', config)
|
||||
elseif event.type == 'tab_select' then
|
||||
config.tab = event.button.text
|
||||
Config.update('Turtles', config)
|
||||
|
||||
elseif event.type == 'button_press' then
|
||||
if event.button.fn then
|
||||
self:runFunction(event.button.fn, event.button.nowrap)
|
||||
self:showBlocks()
|
||||
elseif event.button.script then
|
||||
self:runScript(event.button.script)
|
||||
end
|
||||
else
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
elseif event.type == 'button_press' then
|
||||
if event.button.fn then
|
||||
self:runFunction(event.button.fn, event.button.nowrap)
|
||||
self:showBlocks()
|
||||
elseif event.button.script then
|
||||
self:runScript(event.button.script)
|
||||
end
|
||||
else
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function page:enable()
|
||||
UI.Page.enable(self)
|
||||
UI.Page.enable(self)
|
||||
-- self.tabs:activateTab(page.tabs.turtles)
|
||||
end
|
||||
|
||||
if not Util.getOptions(options, { ... }, true) then
|
||||
return
|
||||
return
|
||||
end
|
||||
|
||||
if options.turtle.value >= 0 then
|
||||
for _ = 1, 10 do
|
||||
page.turtle = _G.network[options.turtle.value]
|
||||
if page.turtle then
|
||||
break
|
||||
end
|
||||
os.sleep(1)
|
||||
end
|
||||
for _ = 1, 10 do
|
||||
page.turtle = _G.network[options.turtle.value]
|
||||
if page.turtle then
|
||||
break
|
||||
end
|
||||
os.sleep(1)
|
||||
end
|
||||
end
|
||||
|
||||
Event.onInterval(1, function()
|
||||
if page.turtle then
|
||||
local t = _G.network[page.turtle.id]
|
||||
page.turtle = t
|
||||
page:draw()
|
||||
page:sync()
|
||||
end
|
||||
if page.turtle then
|
||||
local t = _G.network[page.turtle.id]
|
||||
page.turtle = t
|
||||
page:draw()
|
||||
page:sync()
|
||||
end
|
||||
end)
|
||||
|
||||
if config.tab then
|
||||
page.tabs.tabBar:selectTab(config.tab)
|
||||
page.tabs.tabBar:selectTab(config.tab)
|
||||
end
|
||||
|
||||
UI:setPage(page)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
local c = function(shell, nIndex, sText)
|
||||
if nIndex == 1 then
|
||||
return _G.fs.complete(sText, shell.dir(), true, false)
|
||||
end
|
||||
if nIndex == 1 then
|
||||
return _G.fs.complete(sText, shell.dir(), true, false)
|
||||
end
|
||||
end
|
||||
|
||||
_ENV.shell.setCompletionFunction("packages/common/edit.lua", c)
|
||||
|
||||
45
common/canvasClient.lua
Normal file
45
common/canvasClient.lua
Normal file
@@ -0,0 +1,45 @@
|
||||
local Point = require('point')
|
||||
local Util = require('util')
|
||||
|
||||
local device = _G.device
|
||||
local os = _G.os
|
||||
local turtle = _G.turtle
|
||||
|
||||
local function convert(blocks, reference)
|
||||
if not reference then
|
||||
return blocks
|
||||
end
|
||||
|
||||
local rotated = {
|
||||
[0] = 0,
|
||||
[1] = 3,
|
||||
[2] = 2,
|
||||
[3] = 1,
|
||||
}
|
||||
return Util.reduce(blocks, function(acc, b)
|
||||
local c = Util.shallowCopy(b)
|
||||
Point.rotate(c, rotated[reference.heading])
|
||||
c.x = c.x + reference.x
|
||||
c.y = c.y + reference.y
|
||||
c.z = c.z + reference.z
|
||||
table.insert(acc, c)
|
||||
return acc
|
||||
end, { })
|
||||
end
|
||||
|
||||
local function broadcast(blocks, displayType, source)
|
||||
if device.wireless_modem then
|
||||
device.wireless_modem.transmit(3773, os.getComputerID(), {
|
||||
type = displayType,
|
||||
data = convert(blocks, source),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
while true do
|
||||
local _, msg = os.pullEvent('canvas')
|
||||
|
||||
local reference = turtle and turtle.getState().reference
|
||||
|
||||
broadcast(msg.data, msg.type, reference)
|
||||
end
|
||||
2044
common/edit.lua
2044
common/edit.lua
File diff suppressed because it is too large
Load Diff
@@ -22,37 +22,37 @@ local scanner = device['plethora:scanner'] or
|
||||
-- hud
|
||||
local canvas = glasses and glasses.canvas()
|
||||
if canvas then
|
||||
local lh
|
||||
local lh
|
||||
|
||||
local function addText(x, y, text, color)
|
||||
local th = canvas.group.addText({ x, y }, text, color or 0xa0a0a0FF)
|
||||
lh = lh or th.getLineHeight()
|
||||
th.setShadow(true)
|
||||
th.setScale(.75)
|
||||
return th
|
||||
end
|
||||
local function addText(x, y, text, color)
|
||||
local th = canvas.group.addText({ x, y }, text, color or 0xa0a0a0FF)
|
||||
lh = lh or th.getLineHeight()
|
||||
th.setShadow(true)
|
||||
th.setScale(.75)
|
||||
return th
|
||||
end
|
||||
|
||||
canvas.group = canvas.addGroup({ 4, 90 })
|
||||
canvas.group.bg = canvas.group.addRectangle(0, 0, 80, 10, 0x40404080)
|
||||
canvas.group.addLines(
|
||||
{ 0, 0 },
|
||||
{ 80, 0 },
|
||||
{ 80, 10 },
|
||||
{ 0, 10 },
|
||||
{ 0, 0 },
|
||||
0x202020FF,
|
||||
2)
|
||||
addText(20, 2, 'Swarm Miner', 0xc0c0c0FF)
|
||||
canvas.group = canvas.addGroup({ 4, 90 })
|
||||
canvas.group.bg = canvas.group.addRectangle(0, 0, 80, 10, 0x40404080)
|
||||
canvas.group.addLines(
|
||||
{ 0, 0 },
|
||||
{ 80, 0 },
|
||||
{ 80, 10 },
|
||||
{ 0, 10 },
|
||||
{ 0, 0 },
|
||||
0x202020FF,
|
||||
2)
|
||||
addText(20, 2, 'Swarm Miner', 0xc0c0c0FF)
|
||||
|
||||
local y = 15
|
||||
addText(3, y, 'Turtles')
|
||||
canvas.turtles = addText(60, y, '')
|
||||
canvas.group.addLine({ 0, y + lh - 2 }, { 80, y + lh - 2 }, 0x404040FF, 4)
|
||||
local y = 15
|
||||
addText(3, y, 'Turtles')
|
||||
canvas.turtles = addText(60, y, '')
|
||||
canvas.group.addLine({ 0, y + lh - 2 }, { 80, y + lh - 2 }, 0x404040FF, 4)
|
||||
|
||||
y = y + lh + 5
|
||||
addText(3, y, 'Queue')
|
||||
canvas.queue = addText(60, y, '')
|
||||
canvas.group.addLine({ 0, y + lh - 2 }, { 80, y + lh - 2 }, 0x404040FF, 4)
|
||||
y = y + lh + 5
|
||||
addText(3, y, 'Queue')
|
||||
canvas.queue = addText(60, y, '')
|
||||
canvas.group.addLine({ 0, y + lh - 2 }, { 80, y + lh - 2 }, 0x404040FF, 4)
|
||||
end
|
||||
|
||||
-- container
|
||||
@@ -62,19 +62,19 @@ local box, offset
|
||||
local paused = false
|
||||
|
||||
local function inBox(pt)
|
||||
if not box or not box.ex then
|
||||
return true
|
||||
end
|
||||
return Point.inBox(pt, box)
|
||||
if not box or not box.ex then
|
||||
return true
|
||||
end
|
||||
return Point.inBox(pt, box)
|
||||
end
|
||||
|
||||
local function locate()
|
||||
for _ = 1, 3 do
|
||||
local pt = GPS.getPoint()
|
||||
if pt then
|
||||
return pt
|
||||
end
|
||||
end
|
||||
for _ = 1, 3 do
|
||||
local pt = GPS.getPoint()
|
||||
if pt then
|
||||
return pt
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local spt = GPS.getPoint() or error('GPS failure')
|
||||
@@ -88,7 +88,7 @@ local abort
|
||||
local function hijackTurtle(remoteId)
|
||||
local socket, msg = Socket.connect(remoteId, 188)
|
||||
|
||||
if not socket then
|
||||
if not socket then
|
||||
error(msg)
|
||||
end
|
||||
|
||||
@@ -111,240 +111,240 @@ local function hijackTurtle(remoteId)
|
||||
end
|
||||
|
||||
local function getNextPoint(turtle)
|
||||
if not paused then
|
||||
local pt = Point.closest(turtle.getPoint(), queue)
|
||||
if pt then
|
||||
turtle.pt = pt
|
||||
queue[pt.pkey] = nil
|
||||
return pt
|
||||
end
|
||||
end
|
||||
if not paused then
|
||||
local pt = Point.closest(turtle.getPoint(), queue)
|
||||
if pt then
|
||||
turtle.pt = pt
|
||||
queue[pt.pkey] = nil
|
||||
return pt
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function run(member, point)
|
||||
Event.addRoutine(function()
|
||||
local turtle, socket
|
||||
local _, m = pcall(function()
|
||||
member.active = true
|
||||
turtle, socket = hijackTurtle(member.id)
|
||||
Event.addRoutine(function()
|
||||
local turtle, socket
|
||||
local _, m = pcall(function()
|
||||
member.active = true
|
||||
turtle, socket = hijackTurtle(member.id)
|
||||
|
||||
local function emptySlots(retain, pt)
|
||||
local slots = turtle.getFilledSlots()
|
||||
for _,slot in pairs(slots) do
|
||||
if not retain[slot.key] then
|
||||
turtle.select(slot.index)
|
||||
if pt then
|
||||
turtle.dropAt(pt, 64)
|
||||
else
|
||||
turtle.dropUp(64)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local function emptySlots(retain, pt)
|
||||
local slots = turtle.getFilledSlots()
|
||||
for _,slot in pairs(slots) do
|
||||
if not retain[slot.key] then
|
||||
turtle.select(slot.index)
|
||||
if pt then
|
||||
turtle.dropAt(pt, 64)
|
||||
else
|
||||
turtle.dropUp(64)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function dropOff()
|
||||
-- go to 2 above chest
|
||||
local topPoint = Point.copy(chestPoint)
|
||||
topPoint.y = topPoint.y + 2
|
||||
turtle.gotoY(topPoint.y)
|
||||
while not turtle.go(topPoint) do
|
||||
os.sleep(.5)
|
||||
end
|
||||
local function dropOff()
|
||||
-- go to 2 above chest
|
||||
local topPoint = Point.copy(chestPoint)
|
||||
topPoint.y = topPoint.y + 2
|
||||
turtle.gotoY(topPoint.y)
|
||||
while not turtle.go(topPoint) do
|
||||
os.sleep(.5)
|
||||
end
|
||||
|
||||
-- path to chest
|
||||
local box = Point.makeBox(
|
||||
{ x = chestPoint.x - 3, y = chestPoint.y + 3, z = chestPoint.z - 3 },
|
||||
{ x = chestPoint.x + 3, y = chestPoint.y, z = chestPoint.z + 3 }
|
||||
)
|
||||
turtle.set({
|
||||
movementStrategy = 'pathing',
|
||||
pathingBox = Point.normalizeBox(box),
|
||||
digPolicy = 'digNone',
|
||||
})
|
||||
while not turtle.moveAgainst(chestPoint) do
|
||||
os.sleep(.5)
|
||||
end
|
||||
emptySlots({ }, chestPoint)
|
||||
-- path to chest
|
||||
local box = Point.makeBox(
|
||||
{ x = chestPoint.x - 3, y = chestPoint.y + 3, z = chestPoint.z - 3 },
|
||||
{ x = chestPoint.x + 3, y = chestPoint.y, z = chestPoint.z + 3 }
|
||||
)
|
||||
turtle.set({
|
||||
movementStrategy = 'pathing',
|
||||
pathingBox = Point.normalizeBox(box),
|
||||
digPolicy = 'digNone',
|
||||
})
|
||||
while not turtle.moveAgainst(chestPoint) do
|
||||
os.sleep(.5)
|
||||
end
|
||||
emptySlots({ }, chestPoint)
|
||||
|
||||
-- path to 3 above chest
|
||||
turtle.pathfind(Point.above(topPoint))
|
||||
turtle.set({
|
||||
movementStrategy = 'goto',
|
||||
digPolicy = 'blacklist',
|
||||
})
|
||||
end
|
||||
-- path to 3 above chest
|
||||
turtle.pathfind(Point.above(topPoint))
|
||||
turtle.set({
|
||||
movementStrategy = 'goto',
|
||||
digPolicy = 'blacklist',
|
||||
})
|
||||
end
|
||||
|
||||
if turtle then
|
||||
turtles[member.id] = turtle
|
||||
if turtle then
|
||||
turtles[member.id] = turtle
|
||||
|
||||
turtle.reset()
|
||||
turtle.set({
|
||||
attackPolicy = 'attack',
|
||||
digPolicy = 'blacklist',
|
||||
blacklist = {
|
||||
'turtle',
|
||||
'chest',
|
||||
'shulker',
|
||||
},
|
||||
movementStrategy = 'goto',
|
||||
point = point,
|
||||
})
|
||||
turtle.select(1)
|
||||
turtle.reset()
|
||||
turtle.set({
|
||||
attackPolicy = 'attack',
|
||||
digPolicy = 'blacklist',
|
||||
blacklist = {
|
||||
'turtle',
|
||||
'chest',
|
||||
'shulker',
|
||||
},
|
||||
movementStrategy = 'goto',
|
||||
point = point,
|
||||
})
|
||||
turtle.select(1)
|
||||
|
||||
repeat
|
||||
local pt = getNextPoint(turtle)
|
||||
if pt then
|
||||
member.status = 'digging'
|
||||
repeat
|
||||
local pt = getNextPoint(turtle)
|
||||
if pt then
|
||||
member.status = 'digging'
|
||||
|
||||
if blockTypes[pt.key] == true then
|
||||
if turtle.moveAgainst(pt) then
|
||||
local index = turtle.selectOpenSlot()
|
||||
if turtle.digAt(pt, pt.name) then
|
||||
local slot = turtle.getSlot(index)
|
||||
if slot.count > 0 then
|
||||
blockTypes[pt.key] = slot.key
|
||||
if slot.key ~= pt.key then
|
||||
blockTypes[slot.key] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
turtle.select(1)
|
||||
else
|
||||
turtle.digAt(pt, pt.name)
|
||||
end
|
||||
if blockTypes[pt.key] == true then
|
||||
if turtle.moveAgainst(pt) then
|
||||
local index = turtle.selectOpenSlot()
|
||||
if turtle.digAt(pt, pt.name) then
|
||||
local slot = turtle.getSlot(index)
|
||||
if slot.count > 0 then
|
||||
blockTypes[pt.key] = slot.key
|
||||
if slot.key ~= pt.key then
|
||||
blockTypes[slot.key] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
turtle.select(1)
|
||||
else
|
||||
turtle.digAt(pt, pt.name)
|
||||
end
|
||||
|
||||
if turtle.getItemCount(15) > 0 then
|
||||
member.status = 'ejecting trash'
|
||||
emptySlots(blockTypes)
|
||||
turtle.condense()
|
||||
if turtle.getItemCount(15) > 0 then
|
||||
member.status = 'dropping off'
|
||||
if not chestPoint then
|
||||
member.abort = true
|
||||
member.status = 'full'
|
||||
else
|
||||
dropOff()
|
||||
end
|
||||
end
|
||||
turtle.select(1)
|
||||
end
|
||||
else
|
||||
member.status = 'waiting'
|
||||
os.sleep(1)
|
||||
end
|
||||
if member.fuel < 100 then
|
||||
member.status = 'out of fuel'
|
||||
break
|
||||
end
|
||||
until member.abort
|
||||
end
|
||||
if turtle.getItemCount(15) > 0 then
|
||||
member.status = 'ejecting trash'
|
||||
emptySlots(blockTypes)
|
||||
turtle.condense()
|
||||
if turtle.getItemCount(15) > 0 then
|
||||
member.status = 'dropping off'
|
||||
if not chestPoint then
|
||||
member.abort = true
|
||||
member.status = 'full'
|
||||
else
|
||||
dropOff()
|
||||
end
|
||||
end
|
||||
turtle.select(1)
|
||||
end
|
||||
else
|
||||
member.status = 'waiting'
|
||||
os.sleep(1)
|
||||
end
|
||||
if member.fuel < 100 then
|
||||
member.status = 'out of fuel'
|
||||
break
|
||||
end
|
||||
until member.abort
|
||||
end
|
||||
|
||||
emptySlots(blockTypes)
|
||||
emptySlots(blockTypes)
|
||||
|
||||
if chestPoint then
|
||||
dropOff()
|
||||
while not turtle.go(Point.above(spt)) do
|
||||
os.sleep(.5)
|
||||
end
|
||||
--if turtle.selectSlotWithQuantity(0) then
|
||||
--turtle.set({ digPolicy = 'dig' })
|
||||
--end
|
||||
while not turtle.go(spt) do
|
||||
os.sleep(.5)
|
||||
end
|
||||
else
|
||||
turtle.gotoY(spt.y)
|
||||
while not turtle.go(spt) do
|
||||
os.sleep(.5)
|
||||
end
|
||||
end
|
||||
end)
|
||||
if chestPoint then
|
||||
dropOff()
|
||||
while not turtle.go(Point.above(spt)) do
|
||||
os.sleep(.5)
|
||||
end
|
||||
--if turtle.selectSlotWithQuantity(0) then
|
||||
--turtle.set({ digPolicy = 'dig' })
|
||||
--end
|
||||
while not turtle.go(spt) do
|
||||
os.sleep(.5)
|
||||
end
|
||||
else
|
||||
turtle.gotoY(spt.y)
|
||||
while not turtle.go(spt) do
|
||||
os.sleep(.5)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
turtles[member.id] = nil
|
||||
member.status = m
|
||||
member.active = false
|
||||
if socket then
|
||||
socket:close()
|
||||
end
|
||||
end)
|
||||
turtles[member.id] = nil
|
||||
member.status = m
|
||||
member.active = false
|
||||
if socket then
|
||||
socket:close()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function drawContainer(pos)
|
||||
if canvas3d then
|
||||
canvas3d.clear()
|
||||
if canvas3d then
|
||||
canvas3d.clear()
|
||||
|
||||
local function addBox(b)
|
||||
canvas3d.addBox(
|
||||
b.x - offset.x + .25,
|
||||
b.y - offset.y + .25 ,
|
||||
b.z - offset.z + .25 ,
|
||||
.5, .5, .5).setDepthTested(false)
|
||||
end
|
||||
if box and box.ex then
|
||||
addBox({ x = box.x, y = box.y, z = box.z })
|
||||
addBox({ x = box.x, y = box.y, z = box.ez })
|
||||
addBox({ x = box.ex, y = box.y, z = box.z })
|
||||
addBox({ x = box.ex, y = box.y, z = box.ez })
|
||||
addBox({ x = box.x, y = box.ey, z = box.z })
|
||||
addBox({ x = box.x, y = box.ey, z = box.ez })
|
||||
addBox({ x = box.ex, y = box.ey, z = box.z })
|
||||
addBox({ x = box.ex, y = box.ey, z = box.ez })
|
||||
elseif box then
|
||||
canvas3d.recenter({ -(pos.x % 1), -(pos.y % 1), -(pos.z % 1) })
|
||||
addBox(box)
|
||||
end
|
||||
end
|
||||
local function addBox(b)
|
||||
canvas3d.addBox(
|
||||
b.x - offset.x + .25,
|
||||
b.y - offset.y + .25 ,
|
||||
b.z - offset.z + .25 ,
|
||||
.5, .5, .5).setDepthTested(false)
|
||||
end
|
||||
if box and box.ex then
|
||||
addBox({ x = box.x, y = box.y, z = box.z })
|
||||
addBox({ x = box.x, y = box.y, z = box.ez })
|
||||
addBox({ x = box.ex, y = box.y, z = box.z })
|
||||
addBox({ x = box.ex, y = box.y, z = box.ez })
|
||||
addBox({ x = box.x, y = box.ey, z = box.z })
|
||||
addBox({ x = box.x, y = box.ey, z = box.ez })
|
||||
addBox({ x = box.ex, y = box.ey, z = box.z })
|
||||
addBox({ x = box.ex, y = box.ey, z = box.ez })
|
||||
elseif box then
|
||||
canvas3d.recenter({ -(pos.x % 1), -(pos.y % 1), -(pos.z % 1) })
|
||||
addBox(box)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local pauseResume = {
|
||||
{ text = 'Pause', event = 'pause' },
|
||||
{ text = 'Resume', event = 'resume' },
|
||||
{ text = 'Pause', event = 'pause' },
|
||||
{ text = 'Resume', event = 'resume' },
|
||||
}
|
||||
local containerText = {
|
||||
[[Set a corner to contain mining area]],
|
||||
[[Set ending corner]],
|
||||
[[Set again to clear]],
|
||||
[[Set a corner to contain mining area]],
|
||||
[[Set ending corner]],
|
||||
[[Set again to clear]],
|
||||
}
|
||||
|
||||
local containTab = UI.Tab {
|
||||
tabTitle = 'Contain',
|
||||
button = UI.Button {
|
||||
x = 2, y = 2,
|
||||
text = 'Set corner',
|
||||
event = 'contain'
|
||||
},
|
||||
textArea = UI.TextArea {
|
||||
x = 2, y = 4,
|
||||
value = containerText[1],
|
||||
},
|
||||
tabTitle = 'Contain',
|
||||
button = UI.Button {
|
||||
x = 2, y = 2,
|
||||
text = 'Set corner',
|
||||
event = 'contain'
|
||||
},
|
||||
textArea = UI.TextArea {
|
||||
x = 2, y = 4,
|
||||
value = containerText[1],
|
||||
},
|
||||
}
|
||||
|
||||
local blocksTab = UI.Tab {
|
||||
tabTitle = 'Blocks',
|
||||
grid = UI.ScrollingGrid {
|
||||
y = 1,
|
||||
columns = {
|
||||
{ heading = 'Count', key = 'count', width = 6, align = 'right' },
|
||||
{ heading = 'Name', key = 'displayName' },
|
||||
},
|
||||
sortColumn = 'displayName',
|
||||
},
|
||||
tabTitle = 'Blocks',
|
||||
grid = UI.ScrollingGrid {
|
||||
y = 1,
|
||||
columns = {
|
||||
{ heading = 'Count', key = 'count', width = 6, align = 'right' },
|
||||
{ heading = 'Name', key = 'displayName' },
|
||||
},
|
||||
sortColumn = 'displayName',
|
||||
},
|
||||
}
|
||||
|
||||
local turtlesTab = UI.Tab {
|
||||
tabTitle = 'Turtles',
|
||||
grid = UI.ScrollingGrid {
|
||||
y = 1,
|
||||
values = pool,
|
||||
columns = {
|
||||
{ heading = 'ID', key = 'id', width = 5, },
|
||||
{ heading = ' Fuel', key = 'fuel', width = 5, align = 'right' },
|
||||
{ heading = ' Dist', key = 'distance', width = 5, align = 'right' },
|
||||
{ heading = 'Status', key = 'status' },
|
||||
},
|
||||
sortColumn = 'label',
|
||||
},
|
||||
tabTitle = 'Turtles',
|
||||
grid = UI.ScrollingGrid {
|
||||
y = 1,
|
||||
values = pool,
|
||||
columns = {
|
||||
{ heading = 'ID', key = 'id', width = 5, },
|
||||
{ heading = ' Fuel', key = 'fuel', width = 5, align = 'right' },
|
||||
{ heading = ' Dist', key = 'distance', width = 5, align = 'right' },
|
||||
{ heading = 'Status', key = 'status' },
|
||||
},
|
||||
sortColumn = 'label',
|
||||
},
|
||||
}
|
||||
|
||||
local page = UI.Page {
|
||||
@@ -354,264 +354,264 @@ local page = UI.Page {
|
||||
{ text = 'Abort', event = 'abort' },
|
||||
pauseResume[1],
|
||||
},
|
||||
},
|
||||
tabs = UI.Tabs {
|
||||
y = 2, ey = -2,
|
||||
[1] = blocksTab,
|
||||
[2] = turtlesTab,
|
||||
[3] = containTab,
|
||||
},
|
||||
info = UI.Window {
|
||||
y = -1,
|
||||
backgroundColor = colors.blue,
|
||||
}
|
||||
},
|
||||
tabs = UI.Tabs {
|
||||
y = 2, ey = -2,
|
||||
[1] = blocksTab,
|
||||
[2] = turtlesTab,
|
||||
[3] = containTab,
|
||||
},
|
||||
info = UI.Window {
|
||||
y = -1,
|
||||
backgroundColor = colors.blue,
|
||||
}
|
||||
}
|
||||
|
||||
function page.info:draw()
|
||||
self:clear()
|
||||
self:write(2, 1, 'Turtles: ' .. Util.size(turtles))
|
||||
if not chestPoint then
|
||||
self:write(16, 1, 'No chest')
|
||||
end
|
||||
self:write(28, 1, 'Queue: ' .. Util.size(queue))
|
||||
self:clear()
|
||||
self:write(2, 1, 'Turtles: ' .. Util.size(turtles))
|
||||
if not chestPoint then
|
||||
self:write(16, 1, 'No chest')
|
||||
end
|
||||
self:write(28, 1, 'Queue: ' .. Util.size(queue))
|
||||
end
|
||||
|
||||
function turtlesTab.grid:getDisplayValues(row)
|
||||
row = Util.shallowCopy(row)
|
||||
row.distance = row.distance and Util.round(row.distance, 1)
|
||||
row.fuel = row.fuel and row.fuel > 0 and Util.toBytes(row.fuel) or ''
|
||||
return row
|
||||
row.distance = row.distance and Util.round(row.distance, 1)
|
||||
row.fuel = row.fuel and row.fuel > 0 and Util.toBytes(row.fuel) or ''
|
||||
return row
|
||||
end
|
||||
|
||||
function page:scan()
|
||||
local gpt = GPS.getPoint()
|
||||
if not gpt then
|
||||
return
|
||||
end
|
||||
local rawBlocks = scanner:scan()
|
||||
local candidates = { }
|
||||
local gpt = GPS.getPoint()
|
||||
if not gpt then
|
||||
return
|
||||
end
|
||||
local rawBlocks = scanner:scan()
|
||||
local candidates = { }
|
||||
|
||||
self.totals = Util.reduce(rawBlocks,
|
||||
function(acc, b)
|
||||
b.key = table.concat({ b.name, b.metadata }, ':')
|
||||
local entry = acc[b.key]
|
||||
if not entry then
|
||||
b.displayName = itemDB:getName(b.key)
|
||||
b.count = 1
|
||||
acc[b.key] = b
|
||||
self.totals = Util.reduce(rawBlocks,
|
||||
function(acc, b)
|
||||
b.key = table.concat({ b.name, b.metadata }, ':')
|
||||
local entry = acc[b.key]
|
||||
if not entry then
|
||||
b.displayName = itemDB:getName(b.key)
|
||||
b.count = 1
|
||||
acc[b.key] = b
|
||||
else
|
||||
entry.count = entry.count + 1
|
||||
end
|
||||
entry.count = entry.count + 1
|
||||
end
|
||||
|
||||
if b.name == 'computercraft:turtle_advanced' or
|
||||
b.name == 'computercraft:turtle_expanded' or
|
||||
b.name == 'computercraft:turtle' then
|
||||
table.insert(candidates, b)
|
||||
end
|
||||
if b.name == 'computercraft:turtle_advanced' or
|
||||
b.name == 'computercraft:turtle_expanded' or
|
||||
b.name == 'computercraft:turtle' then
|
||||
table.insert(candidates, b)
|
||||
end
|
||||
|
||||
if b.name == 'minecraft:chest' or b.name:find('shulker') then
|
||||
chestPoint = b
|
||||
end
|
||||
if b.name == 'minecraft:chest' or b.name:find('shulker') then
|
||||
chestPoint = b
|
||||
end
|
||||
|
||||
-- add relevant blocks to queue
|
||||
b.x = gpt.x + b.x
|
||||
b.y = gpt.y + b.y
|
||||
b.z = gpt.z + b.z
|
||||
b.pkey = table.concat({ b.x, b.y, b.z }, ':')
|
||||
if blockTypes[b.key] and inBox(b) then
|
||||
if not Util.any(turtles, function(t)
|
||||
return t.pt and t.pt.pkey == b.pkey
|
||||
end) then
|
||||
queue[b.pkey] = b
|
||||
end
|
||||
else
|
||||
queue[b.pkey] = nil
|
||||
end
|
||||
return acc
|
||||
-- add relevant blocks to queue
|
||||
b.x = gpt.x + b.x
|
||||
b.y = gpt.y + b.y
|
||||
b.z = gpt.z + b.z
|
||||
b.pkey = table.concat({ b.x, b.y, b.z }, ':')
|
||||
if blockTypes[b.key] and inBox(b) then
|
||||
if not Util.any(turtles, function(t)
|
||||
return t.pt and t.pt.pkey == b.pkey
|
||||
end) then
|
||||
queue[b.pkey] = b
|
||||
end
|
||||
else
|
||||
queue[b.pkey] = nil
|
||||
end
|
||||
return acc
|
||||
end,
|
||||
{ })
|
||||
{ })
|
||||
|
||||
for _, b in pairs(candidates) do
|
||||
local v = scanner.getBlockMeta(b.x - gpt.x, b.y - gpt.y, b.z - gpt.z)
|
||||
if v and v.computer then
|
||||
local member = pool[v.computer.id]
|
||||
if not member then
|
||||
member = {
|
||||
id = v.computer.id,
|
||||
label = v.computer.label,
|
||||
}
|
||||
pool[v.computer.id] = member
|
||||
end
|
||||
for _, b in pairs(candidates) do
|
||||
local v = scanner.getBlockMeta(b.x - gpt.x, b.y - gpt.y, b.z - gpt.z)
|
||||
if v and v.computer then
|
||||
local member = pool[v.computer.id]
|
||||
if not member then
|
||||
member = {
|
||||
id = v.computer.id,
|
||||
label = v.computer.label,
|
||||
}
|
||||
pool[v.computer.id] = member
|
||||
end
|
||||
|
||||
member.fuel = v.turtle.fuel
|
||||
member.distance = 0
|
||||
member.fuel = v.turtle.fuel
|
||||
member.distance = 0
|
||||
|
||||
if not v.computer.isOn then
|
||||
member.status = 'Powered off'
|
||||
elseif v.turtle.fuel < 100 and not member.active then
|
||||
member.status = 'Not enough fuel'
|
||||
elseif not member.active and not member.abort then
|
||||
local pt = Point.copy(b)
|
||||
pt.heading = Point.facings[v.state.facing].heading
|
||||
run(member, pt)
|
||||
end
|
||||
end
|
||||
end
|
||||
if not v.computer.isOn then
|
||||
member.status = 'Powered off'
|
||||
elseif v.turtle.fuel < 100 and not member.active then
|
||||
member.status = 'Not enough fuel'
|
||||
elseif not member.active and not member.abort then
|
||||
local pt = Point.copy(b)
|
||||
pt.heading = Point.facings[v.state.facing].heading
|
||||
run(member, pt)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function blocksTab.grid:getDisplayValues(row)
|
||||
row = Util.shallowCopy(row)
|
||||
row.count = Util.toBytes(row.count) .. ' '
|
||||
return row
|
||||
return row
|
||||
end
|
||||
|
||||
function blocksTab.grid:getRowTextColor(row, selected)
|
||||
return blockTypes[row.key] and
|
||||
colors.yellow or
|
||||
UI.Grid.getRowTextColor(self, row, selected)
|
||||
return blockTypes[row.key] and
|
||||
colors.yellow or
|
||||
UI.Grid.getRowTextColor(self, row, selected)
|
||||
end
|
||||
|
||||
function blocksTab:eventHandler(event)
|
||||
if event.type == 'grid_select' then
|
||||
local key = event.selected.key
|
||||
if blockTypes[key] then
|
||||
for k,v in pairs(queue) do
|
||||
if v.key == key then
|
||||
queue[k] = nil
|
||||
end
|
||||
end
|
||||
blockTypes[key] = nil
|
||||
else
|
||||
blockTypes[key] = true
|
||||
end
|
||||
self.grid:draw()
|
||||
end
|
||||
local key = event.selected.key
|
||||
if blockTypes[key] then
|
||||
for k,v in pairs(queue) do
|
||||
if v.key == key then
|
||||
queue[k] = nil
|
||||
end
|
||||
end
|
||||
blockTypes[key] = nil
|
||||
else
|
||||
blockTypes[key] = true
|
||||
end
|
||||
self.grid:draw()
|
||||
end
|
||||
end
|
||||
|
||||
function page:eventHandler(event)
|
||||
if event.type == 'scan' then
|
||||
blocksTab.grid:setValues(self.totals)
|
||||
blocksTab.grid:draw()
|
||||
self.tabs:selectTab(blocksTab)
|
||||
if event.type == 'scan' then
|
||||
blocksTab.grid:setValues(self.totals)
|
||||
blocksTab.grid:draw()
|
||||
self.tabs:selectTab(blocksTab)
|
||||
|
||||
elseif event.type == 'pause' then
|
||||
paused = true
|
||||
Util.merge(event.button, pauseResume[2])
|
||||
event.button:draw()
|
||||
elseif event.type == 'pause' then
|
||||
paused = true
|
||||
Util.merge(event.button, pauseResume[2])
|
||||
event.button:draw()
|
||||
|
||||
elseif event.type == 'resume' then
|
||||
paused = false
|
||||
Util.merge(event.button, pauseResume[1])
|
||||
event.button:draw()
|
||||
elseif event.type == 'resume' then
|
||||
paused = false
|
||||
Util.merge(event.button, pauseResume[1])
|
||||
event.button:draw()
|
||||
|
||||
elseif event.type == 'contain' then
|
||||
local pt = { gps.locate() }
|
||||
local pos = {
|
||||
x = pt[1],
|
||||
y = pt[2],
|
||||
z = pt[3],
|
||||
}
|
||||
elseif event.type == 'contain' then
|
||||
local pt = { gps.locate() }
|
||||
local pos = {
|
||||
x = pt[1],
|
||||
y = pt[2],
|
||||
z = pt[3],
|
||||
}
|
||||
|
||||
if not box then
|
||||
offset = {
|
||||
x = math.floor(pos.x),
|
||||
y = math.floor(pos.y),
|
||||
z = math.floor(pos.z),
|
||||
}
|
||||
box = {
|
||||
x = math.floor(pos.x),
|
||||
y = math.floor(pos.y) - 1,
|
||||
z = math.floor(pos.z),
|
||||
}
|
||||
containTab.textArea.value = containerText[2]
|
||||
elseif not box.ex then
|
||||
box.ex = math.floor(pos.x)
|
||||
box.ey = math.floor(pos.y) - 1
|
||||
box.ez = math.floor(pos.z)
|
||||
box = Point.normalizeBox(box)
|
||||
containTab.textArea.value = containerText[3]
|
||||
else
|
||||
box = nil
|
||||
containTab.textArea.value = containerText[1]
|
||||
end
|
||||
if not box then
|
||||
offset = {
|
||||
x = math.floor(pos.x),
|
||||
y = math.floor(pos.y),
|
||||
z = math.floor(pos.z),
|
||||
}
|
||||
box = {
|
||||
x = math.floor(pos.x),
|
||||
y = math.floor(pos.y) - 1,
|
||||
z = math.floor(pos.z),
|
||||
}
|
||||
containTab.textArea.value = containerText[2]
|
||||
elseif not box.ex then
|
||||
box.ex = math.floor(pos.x)
|
||||
box.ey = math.floor(pos.y) - 1
|
||||
box.ez = math.floor(pos.z)
|
||||
box = Point.normalizeBox(box)
|
||||
containTab.textArea.value = containerText[3]
|
||||
else
|
||||
box = nil
|
||||
containTab.textArea.value = containerText[1]
|
||||
end
|
||||
|
||||
containTab.textArea:draw()
|
||||
drawContainer(pos)
|
||||
containTab.textArea:draw()
|
||||
drawContainer(pos)
|
||||
|
||||
elseif event.type == 'abort' then
|
||||
for _, v in pairs(pool) do
|
||||
v.abort = true
|
||||
v.status = 'aborting'
|
||||
end
|
||||
spt = Point.above(locate())
|
||||
abort = true
|
||||
end
|
||||
elseif event.type == 'abort' then
|
||||
for _, v in pairs(pool) do
|
||||
v.abort = true
|
||||
v.status = 'aborting'
|
||||
end
|
||||
spt = Point.above(locate())
|
||||
abort = true
|
||||
end
|
||||
|
||||
UI.Page.eventHandler(self, event)
|
||||
end
|
||||
|
||||
Event.onInterval(5, function()
|
||||
if not abort and not paused then
|
||||
if not abort and not paused then
|
||||
|
||||
--local meta = scanner.getMetaOwner()
|
||||
--if meta.isSneaking then
|
||||
page:scan()
|
||||
-- Sound.play('entity.bobber.throw', .6)
|
||||
--end
|
||||
end
|
||||
--local meta = scanner.getMetaOwner()
|
||||
--if meta.isSneaking then
|
||||
page:scan()
|
||||
-- Sound.play('entity.bobber.throw', .6)
|
||||
--end
|
||||
end
|
||||
end)
|
||||
|
||||
Event.onInterval(1, function()
|
||||
for id,v in pairs(network) do
|
||||
if v.fuel then
|
||||
if pool[id] then
|
||||
pool[id].fuel = v.fuel
|
||||
pool[id].distance = v.distance
|
||||
end
|
||||
end
|
||||
end
|
||||
for id,v in pairs(network) do
|
||||
if v.fuel then
|
||||
if pool[id] then
|
||||
pool[id].fuel = v.fuel
|
||||
pool[id].distance = v.distance
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if abort and Util.size(turtles) == 0 then
|
||||
Event.exitPullEvents()
|
||||
end
|
||||
if abort and Util.size(turtles) == 0 then
|
||||
Event.exitPullEvents()
|
||||
end
|
||||
|
||||
if turtlesTab.enabled then
|
||||
turtlesTab.grid:update()
|
||||
turtlesTab.grid:draw()
|
||||
end
|
||||
if turtlesTab.enabled then
|
||||
turtlesTab.grid:update()
|
||||
turtlesTab.grid:draw()
|
||||
end
|
||||
|
||||
page.info:draw()
|
||||
page.info:sync()
|
||||
page.info:draw()
|
||||
page.info:sync()
|
||||
|
||||
if canvas then
|
||||
canvas.turtles.setText(tostring(Util.size(turtles)))
|
||||
canvas.queue.setText(tostring(Util.size(queue)))
|
||||
end
|
||||
if canvas then
|
||||
canvas.turtles.setText(tostring(Util.size(turtles)))
|
||||
canvas.queue.setText(tostring(Util.size(queue)))
|
||||
end
|
||||
end)
|
||||
|
||||
Event.onTimeout(.1, function()
|
||||
page:scan()
|
||||
blocksTab.grid:setValues(page.totals)
|
||||
blocksTab.grid:draw()
|
||||
page:sync()
|
||||
page:scan()
|
||||
blocksTab.grid:setValues(page.totals)
|
||||
blocksTab.grid:draw()
|
||||
page:sync()
|
||||
end)
|
||||
|
||||
UI:setPage(page)
|
||||
|
||||
--[[
|
||||
Event.onTerminate(function()
|
||||
spt = Point.above(locate())
|
||||
for _, v in pairs(pool) do
|
||||
v.status = 'aborting'
|
||||
v.abort = true
|
||||
end
|
||||
abort = true
|
||||
spt = Point.above(locate())
|
||||
for _, v in pairs(pool) do
|
||||
v.status = 'aborting'
|
||||
v.abort = true
|
||||
end
|
||||
abort = true
|
||||
end)
|
||||
]]
|
||||
|
||||
Event.pullEvents()
|
||||
|
||||
if canvas then
|
||||
canvas3d.clear()
|
||||
canvas.group.remove()
|
||||
canvas3d.clear()
|
||||
canvas.group.remove()
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user