milo better user experience

This commit is contained in:
kepler155c
2018-11-12 20:22:14 -05:00
parent 52ea4e039c
commit 1eaaca2cc3
17 changed files with 204 additions and 82 deletions

View File

@@ -31,6 +31,7 @@ local function safeString(text)
end end
function itemDB:makeKey(item) function itemDB:makeKey(item)
if not item then error('itemDB:makeKey: item is required', 2) end
return table.concat({ item.name, item.damage or '*', item.nbtHash }, ':') return table.concat({ item.name, item.damage or '*', item.nbtHash }, ':')
end end

View File

@@ -139,7 +139,7 @@ local function harvest(blocks)
elseif b.action == 'bump' then elseif b.action == 'bump' then
if turtle.faceAgainst(b) then if turtle.faceAgainst(b) then
turtle.equip('right', 'plethora:module:3') turtle.equip('right', 'plethora:module:3')
os.sleep(.3) os.sleep(.5)
-- search the ground for the dropped cactus -- search the ground for the dropped cactus
local sensed = peripheral.call('right', 'sense') local sensed = peripheral.call('right', 'sense')
turtle.equip('right', 'minecraft:diamond_pickaxe') turtle.equip('right', 'minecraft:diamond_pickaxe')

View File

@@ -1,5 +1,6 @@
_G.requireInjector(_ENV) _G.requireInjector(_ENV)
local Config = require('config')
local Event = require('event') local Event = require('event')
local Socket = require('socket') local Socket = require('socket')
local sync = require('sync').sync local sync = require('sync').sync
@@ -13,48 +14,26 @@ local socket
local SHIELD_SLOT = 2 local SHIELD_SLOT = 2
local options = { local config = Config.load('miloRemote', { })
user = { arg = 'u', type = 'string',
desc = 'User name associated with bound manipulator' },
slot = { arg = 's', type = 'number',
desc = 'Optional inventory slot to use to transfer to milo' },
shield = { arg = 'e', type = 'flag',
desc = 'Use shield slot to use to transfer to milo' },
server = { arg = 'm', type = 'number',
desc = 'ID of Milo server' },
help = { arg = 'h', type = 'flag', value = false,
desc = 'Displays the options' },
}
local args = { ... }
if not Util.getOptions(options, args) then
print()
error('Invalid arguments')
end
if not options.user.value or not options.server.value then
Util.showOptions(options)
print()
error('Invalid arguments')
end
if (options.slot.value or options.shield.value) and
not (device.neuralInterface and device.neuralInterface.getInventory) then
error('Introspection module is required for transferring items')
end
local page = UI.Page { local page = UI.Page {
dummy = UI.Window { dummy = UI.Window {
x = 1, ex = -10, y = 1, height = 1, x = 1, ex = -13, y = 1, height = 1,
infoBar = UI.StatusBar { infoBar = UI.StatusBar {
backgroundColor = colors.lightGray, backgroundColor = colors.lightGray,
}, },
}, },
refresh = UI.Button { refresh = UI.Button {
y = 1, x = -9, y = 1, x = -12,
event = 'refresh', event = 'refresh',
text = 'Refresh', text = 'Refresh',
}, },
setupButton = UI.Button {
y = 1, x = -3,
event = 'setup',
text = '\206',
help = 'Configuration',
},
grid = UI.Grid { grid = UI.Grid {
y = 2, ey = -2, y = 2, ey = -2,
columns = { columns = {
@@ -106,6 +85,46 @@ local page = UI.Page {
q = 'quit', q = 'quit',
}, },
setup = UI.SlideOut {
backgroundColor = colors.cyan,
titleBar = UI.TitleBar {
title = 'Remote Setup',
},
form = UI.Form {
x = 2, ex = -2, y = 2, ey = -1,
values = config,
[1] = UI.TextEntry {
formLabel = 'Server', formKey = 'server',
help = 'ID for the server',
shadowText = 'Milo server ID',
limit = 6,
validate = 'numeric',
required = true,
},
[2] = UI.TextEntry {
formLabel = 'User Name', formKey = 'user',
help = 'User name for bound manipulator',
shadowText = 'User name',
limit = 50,
required = true,
},
[3] = UI.TextEntry {
formLabel = 'Return Slot', formKey = 'slot',
help = 'Use a slot for sending to storage',
shadowText = 'Inventory slot #',
limit = 5,
validate = 'numeric',
required = true,
},
[4] = UI.Checkbox {
formLabel = 'Shield Slot', formKey = 'useShield',
help = 'or use the shield slot for sending'
},
},
statusBar = UI.StatusBar {
backgroundColor = colors.cyan,
},
},
displayMode = 0, displayMode = 0,
items = { }, items = { },
} }
@@ -139,14 +158,22 @@ end
function page:sendRequest(data) function page:sendRequest(data)
local response local response
if not config.server then
self:setStatus('Invalid configuration')
Event.onTimeout(2, function()
self:setStatus('')
end)
return
end
sync(self, function() sync(self, function()
local msg local msg
for _ = 1, 2 do for _ = 1, 2 do
if not socket or not socket.connected then if not socket or not socket.connected then
self:setStatus('connecting ...') self:setStatus('connecting ...')
socket, msg = Socket.connect(options.server.value, 4242) socket, msg = Socket.connect(config.server, 4242)
if socket then if socket then
socket:write(options.user.value) socket:write(config.user)
local r = socket:read(2) local r = socket:read(2)
if r and not r.msg then if r and not r.msg then
self:setStatus('connected ...') self:setStatus('connected ...')
@@ -203,7 +230,6 @@ end
function page:transfer(item, count) function page:transfer(item, count)
local response = self:sendRequest({ request = 'transfer', item = item, count = count }) local response = self:sendRequest({ request = 'transfer', item = item, count = count })
if response then if response then
_debug(response)
item.count = response.current - response.count item.count = response.current - response.count
self.grid:draw() self.grid:draw()
if response.craft > 0 then if response.craft > 0 then
@@ -214,10 +240,29 @@ function page:transfer(item, count)
end end
end end
function page.setup:eventHandler(event)
if event.type == 'focus_change' then
self.statusBar:setStatus(event.focused.help)
end
return UI.SlideOut.eventHandler(self, event)
end
function page:eventHandler(event) function page:eventHandler(event)
if event.type == 'quit' then if event.type == 'quit' then
UI:exitPullEvents() UI:exitPullEvents()
elseif event.type == 'setup' then
self.setup:show()
elseif event.type == 'form_complete' then
Config.update('miloRemote', config)
self.setup:hide()
self:refresh()
self.grid:draw()
elseif event.type == 'form_cancel' then
self.setup:hide()
elseif event.type == 'focus_change' then elseif event.type == 'focus_change' then
self.dummy.infoBar:setStatus(event.focused.help) self.dummy.infoBar:setStatus(event.focused.help)
@@ -290,6 +335,9 @@ end
function page:enable() function page:enable()
self:setFocus(self.statusBar.filter) self:setFocus(self.statusBar.filter)
UI.Page.enable(self) UI.Page.enable(self)
if not config.server then
self.setup:show()
end
Event.onTimeout(.1, function() Event.onTimeout(.1, function()
self:refresh() self:refresh()
self.grid:draw() self.grid:draw()
@@ -311,31 +359,21 @@ function page:applyFilter()
self.grid:setValues(t) self.grid:setValues(t)
end end
if options.slot.value or options.shield.value then Event.addRoutine(function()
local inv = 'getInventory' while true do
local slotNo = options.slot.value os.sleep(1.5)
local slotValue = options.slot.value local neural = device.neuralInterface
local inv = config.useShield and 'getEquipment' or 'getInventory'
if options.shield.value then if not neural or not neural[inv] then
slotNo = SHIELD_SLOT _G._debug('missing Introspection module')
slotValue = 'shield' elseif config.server then
inv = 'getEquipment' local method = neural[inv]
end local item = method and method().getItemMeta(config.useShield and SHIELD_SLOT or config.slot)
Event.addRoutine(function()
while true do
os.sleep(1.5)
local neural = device.neuralInterface
if not neural or not neural[inv] then
_G._debug('missing Introspection module')
end
local method = neural and neural[inv]
local item = method and method().getItemMeta(slotNo)
if item then if item then
local slotNo = config.useShield and 'shield' or config.slot
local response = page:sendRequest({ local response = page:sendRequest({
request = 'deposit', request = 'deposit',
slot = slotValue, slot = slotNo,
count = item.count, count = item.count,
key = table.concat({ item.name, item.damage, item.nbtHash }, ':') key = table.concat({ item.name, item.damage, item.nbtHash }, ':')
}) })
@@ -349,8 +387,8 @@ if options.slot.value or options.shield.value then
end end
end end
end end
end) end
end end)
UI:setPage(page) UI:setPage(page)
UI:pullEvents() UI:pullEvents()

8
milo/autorun/milo.lua Normal file
View File

@@ -0,0 +1,8 @@
local device = _G.device
local shell = _ENV.shell
if device.workbench then
shell.openForegroundTab('Milo')
elseif device.neuralInterface then
shell.openForegroundTab('MiloRemote')
end

View File

@@ -62,6 +62,9 @@ local networkPage = UI.Page {
backgroundColor = colors.lightGray, backgroundColor = colors.lightGray,
}, },
notification = UI.Notification { }, notification = UI.Notification { },
accelerators = {
delete = 'remove_node',
}
} }
function networkPage.grid:getDisplayValues(row) function networkPage.grid:getDisplayValues(row)
@@ -236,7 +239,9 @@ The settings will take effect immediately!]],
{ heading = 'Name', key = 'displayName' }, { heading = 'Name', key = 'displayName' },
}, },
sortColumn = 'displayName', sortColumn = 'displayName',
help = 'Select item to export', accelerators = {
delete = 'remove_entry',
},
}, },
remove = UI.Button { remove = UI.Button {
x = -4, y = 4, x = -4, y = 4,
@@ -255,7 +260,7 @@ The settings will take effect immediately!]],
{ name = 'Yes', value = true }, { name = 'Yes', value = true },
{ name = 'No', value = false }, { name = 'No', value = false },
}, },
help = 'Ignore damage of item when exporting' help = 'Ignore damage of item'
}, },
[2] = UI.Chooser { [2] = UI.Chooser {
width = 7, width = 7,
@@ -266,7 +271,7 @@ The settings will take effect immediately!]],
{ name = 'Yes', value = true }, { name = 'Yes', value = true },
{ name = 'No', value = false }, { name = 'No', value = false },
}, },
help = 'Ignore NBT of item when exporting' help = 'Ignore NBT of item'
}, },
[3] = UI.Chooser { [3] = UI.Chooser {
width = 13, width = 13,
@@ -276,7 +281,7 @@ The settings will take effect immediately!]],
{ name = 'whitelist', value = false }, { name = 'whitelist', value = false },
{ name = 'blacklist', value = true }, { name = 'blacklist', value = true },
}, },
help = 'Ignore damage of item when exporting' help = 'Ignore damage of item'
}, },
scan = UI.Button { scan = UI.Button {
x = -11, y = 1, x = -11, y = 1,
@@ -377,7 +382,7 @@ function nodeWizard.wizard.pages.general:enable()
self:focusFirst() self:focusFirst()
end end
function nodeWizard.wizard.pages.general:setNode(node) function nodeWizard.wizard.pages.general:showInventory(node)
local inventory local inventory
if device[node.name] and device[node.name].list then if device[node.name] and device[node.name].list then
@@ -437,7 +442,7 @@ _G._p2 = self.node
if page.isValidType then if page.isValidType then
-- TODO: dedupe list -- TODO: dedupe list
local choice = page:isValidType(self.node) local choice = page:isValidType(self.node)
if choice then if choice and not Util.find(choices, 'value', choice.value) then
table.insert(choices, choice) table.insert(choices, choice)
end end
end end
@@ -446,6 +451,8 @@ _G._p2 = self.node
self.wizard.pages.general.form[2].choices = choices self.wizard.pages.general.form[2].choices = choices
self.wizard.pages.general.form:setValues(self.node) self.wizard.pages.general.form:setValues(self.node)
self.wizard.pages.general:showInventory(self.node)
-- restore indices -- restore indices
for _, page in pairs(self.wizard.pages) do for _, page in pairs(self.wizard.pages) do
if not page.oindex then if not page.oindex then
@@ -479,6 +486,15 @@ function nodeWizard:eventHandler(event)
UI:setPreviousPage() UI:setPreviousPage()
elseif event.type == 'choice_change' then
local help
if event.choice and event.choice.help then -- TODO: new param sent by UI api
help = event.choice.help
else
help = ''
end
self.statusBar:setStatus(help)
elseif event.type == 'edit_filter' then elseif event.type == 'edit_filter' then
self.filter:show(event.entry, event.callback, event.whitelistOnly) self.filter:show(event.entry, event.callback, event.whitelistOnly)

13
milo/etc/apps/apps.db Normal file
View File

@@ -0,0 +1,13 @@
{
[ "9302912a2d9794a47241faefc475335b4e07a581" ] = {
title = "Remote",
category = "Apps",
run = "MiloRemote",
},
[ "eea426f9baef72a8fcefd091e0cec5ab94a76698" ] = {
title = "Milo",
category = "Apps",
run = "Milo",
requires = 'advancedTurtle',
},
}

View File

@@ -30,7 +30,11 @@ local activityWizardPage = UI.Window {
function activityWizardPage:isValidType(node) function activityWizardPage:isValidType(node)
local m = device[node.name] local m = device[node.name]
return m and m.type == 'monitor' and { name = 'Activity Monitor', value = 'activity' } return m and m.type == 'monitor' and {
name = 'Activity Monitor',
value = 'activity',
help = 'Display storage activity'
}
end end
function activityWizardPage:isValidFor(node) function activityWizardPage:isValidFor(node)

View File

@@ -27,7 +27,11 @@ local brewingStandView = UI.Window {
function brewingStandView:isValidType(node) function brewingStandView:isValidType(node)
local m = device[node.name] local m = device[node.name]
return m and m.type == 'minecraft:brewing_stand'and { name = 'Brewing Stand', value = 'brewingStand' } return m and m.type == 'minecraft:brewing_stand'and {
name = 'Brewing Stand',
value = 'brewingStand',
help = 'Auto-learning brewing stand',
}
end end
function brewingStandView:isValidFor(node) function brewingStandView:isValidFor(node)

View File

@@ -15,6 +15,9 @@ local exportView = UI.Window {
}, },
sortColumn = 'slot', sortColumn = 'slot',
help = 'Edit this entry', help = 'Edit this entry',
accelerators = {
delete = 'remove_entry',
},
}, },
text = UI.Text { text = UI.Text {
x = 2, y = -2, x = 2, y = -2,
@@ -38,7 +41,11 @@ local exportView = UI.Window {
function exportView:isValidType(node) function exportView:isValidType(node)
local m = device[node.name] local m = device[node.name]
return m and m.pullItems and { name = 'Generic Inventory', value = 'machine' } return m and m.pullItems and {
name = 'Generic Inventory',
value = 'machine',
help = 'Chest, furnace... (has an inventory)'
}
end end
function exportView:isValidFor(node) function exportView:isValidFor(node)

View File

@@ -15,6 +15,9 @@ local importView = UI.Window {
}, },
sortColumn = 'slot', sortColumn = 'slot',
help = 'Edit this entry', help = 'Edit this entry',
accelerators = {
delete = 'remove_entry',
},
}, },
text = UI.Text { text = UI.Text {
x = 2, y = -2, x = 2, y = -2,
@@ -38,7 +41,11 @@ local importView = UI.Window {
function importView:isValidType(node) function importView:isValidType(node)
local m = device[node.name] local m = device[node.name]
return m and m.pullItems and { name = 'Generic Inventory', value = 'machine' } return m and m.pullItems and {
name = 'Generic Inventory',
value = 'machine',
help = 'Chest, furnace... (has an inventory)',
}
end end
function importView:isValidFor(node) function importView:isValidFor(node)

View File

@@ -23,7 +23,11 @@ local inputChestWizardPage = UI.Window {
function inputChestWizardPage:isValidType(node) function inputChestWizardPage:isValidType(node)
local m = device[node.name] local m = device[node.name]
return m and m.pullItems and { name = 'Input Chest', value = 'input' } return m and m.pullItems and {
name = 'Input Chest',
value = 'input',
help = 'Sends all items to storage',
}
end end
function inputChestWizardPage:isValidFor(node) function inputChestWizardPage:isValidFor(node)

View File

@@ -29,7 +29,11 @@ local jobsWizardPage = UI.Window {
function jobsWizardPage:isValidType(node) function jobsWizardPage:isValidType(node)
local m = device[node.name] local m = device[node.name]
return m and m.type == 'monitor' and { name = 'Crafting Monitor', value = 'jobs' } return m and m.type == 'monitor' and {
name = 'Crafting Monitor',
value = 'jobs',
help = 'Display crafting progress / jobs'
}
end end
function jobsWizardPage:isValidFor(node) function jobsWizardPage:isValidFor(node)

View File

@@ -36,8 +36,8 @@ local listingPage = UI.Page {
--{ text = 'Forget', event = 'forget' }, --{ text = 'Forget', event = 'forget' },
{ text = 'Craft', event = 'craft' }, { text = 'Craft', event = 'craft' },
{ text = 'Edit', event = 'details' }, { text = 'Edit', event = 'details' },
{ text = 'Network', event = 'network' }, { text = 'Refresh', event = 'refresh', x = -12 },
{ text = 'Refresh', event = 'refresh', x = -9 }, { text = '\206', event = 'network', x = -3 },
}, },
}, },
grid = UI.Grid { grid = UI.Grid {
@@ -59,7 +59,7 @@ local listingPage = UI.Page {
backgroundColor = colors.cyan, backgroundColor = colors.cyan,
backgroundFocusColor = colors.cyan, backgroundFocusColor = colors.cyan,
accelerators = { accelerators = {
[ 'enter' ] = 'craft', [ 'enter' ] = 'eject',
}, },
}, },
storageStatus = UI.Text { storageStatus = UI.Text {
@@ -198,10 +198,12 @@ function listingPage:eventHandler(event)
elseif event.type == 'craft' then elseif event.type == 'craft' then
local item = self.grid:getSelected() local item = self.grid:getSelected()
if Craft.findRecipe(item) or true then -- or item.is_craftable then if item then
UI:setPage('craft', self.grid:getSelected()) if Craft.findRecipe(item) then -- or item.is_craftable then
else UI:setPage('craft', self.grid:getSelected())
self.notification:error('No recipe defined') else
self.notification:error('No recipe defined')
end
end end
elseif event.type == 'text_change' and event.element == self.statusBar.filter then elseif event.type == 'text_change' and event.element == self.statusBar.filter then

View File

@@ -35,7 +35,11 @@ function wizardPage:isValidType(node)
return m and return m and
m.type == 'manipulator' and m.type == 'manipulator' and
m.getEnder and m.getEnder and
{ name = 'Manipulator', value = 'manipulator' } {
name = 'Manipulator',
value = 'manipulator',
help = 'Manipulator w/bound introspection mod'
}
end end
function wizardPage:isValidFor(node) function wizardPage:isValidFor(node)

View File

@@ -12,7 +12,7 @@ local function filter(a)
end end
function PotionImportTask:cycle(context) function PotionImportTask:cycle(context)
for bs in context.storage:filterActive('machine', filter) do for bs in context.storage:filterActive('brewingStand', filter) do
if bs.adapter.getBrewTime() == 0 then if bs.adapter.getBrewTime() == 0 then
local list = bs.adapter.list() local list = bs.adapter.list()

View File

@@ -26,6 +26,7 @@ local storageView = UI.Window {
x = 16, ex = -2, y = 3, x = 16, ex = -2, y = 3,
value = '', value = '',
}, },
--[[
[4] = UI.Checkbox { [4] = UI.Checkbox {
formLabel = 'Void', formKey = 'voidExcess', formLabel = 'Void', formKey = 'voidExcess',
help = 'Void excess if locked - TODO', help = 'Void excess if locked - TODO',
@@ -36,6 +37,7 @@ local storageView = UI.Window {
help = 'TODO', help = 'TODO',
pruneEmpty = true, pruneEmpty = true,
}, },
]]--
}, },
} }
@@ -50,7 +52,11 @@ end
function storageView:isValidType(node) function storageView:isValidType(node)
local m = device[node.name] local m = device[node.name]
return m and m.pullItems and { name = 'Storage', value = 'storage' } return m and m.pullItems and {
name = 'Storage',
value = 'storage',
help = 'Use for item storage',
}
end end
function storageView:isValidFor(node) function storageView:isValidFor(node)

View File

@@ -25,7 +25,11 @@ local trashcanWizardPage = UI.Window {
function trashcanWizardPage:isValidType(node) function trashcanWizardPage:isValidType(node)
local m = device[node.name] local m = device[node.name]
return m and m.pullItems and { name = 'Trashcan', value = 'trashcan' } return m and m.pullItems and {
name = 'Trashcan',
value = 'trashcan',
help = 'An inventory to send unwanted items',
}
end end
function trashcanWizardPage:isValidFor(node) function trashcanWizardPage:isValidFor(node)