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
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 }, ':')
end

View File

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

View File

@@ -1,5 +1,6 @@
_G.requireInjector(_ENV)
local Config = require('config')
local Event = require('event')
local Socket = require('socket')
local sync = require('sync').sync
@@ -13,48 +14,26 @@ local socket
local SHIELD_SLOT = 2
local options = {
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 config = Config.load('miloRemote', { })
local page = UI.Page {
dummy = UI.Window {
x = 1, ex = -10, y = 1, height = 1,
x = 1, ex = -13, y = 1, height = 1,
infoBar = UI.StatusBar {
backgroundColor = colors.lightGray,
},
},
refresh = UI.Button {
y = 1, x = -9,
y = 1, x = -12,
event = 'refresh',
text = 'Refresh',
},
setupButton = UI.Button {
y = 1, x = -3,
event = 'setup',
text = '\206',
help = 'Configuration',
},
grid = UI.Grid {
y = 2, ey = -2,
columns = {
@@ -106,6 +85,46 @@ local page = UI.Page {
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,
items = { },
}
@@ -139,14 +158,22 @@ end
function page:sendRequest(data)
local response
if not config.server then
self:setStatus('Invalid configuration')
Event.onTimeout(2, function()
self:setStatus('')
end)
return
end
sync(self, function()
local msg
for _ = 1, 2 do
if not socket or not socket.connected then
self:setStatus('connecting ...')
socket, msg = Socket.connect(options.server.value, 4242)
socket, msg = Socket.connect(config.server, 4242)
if socket then
socket:write(options.user.value)
socket:write(config.user)
local r = socket:read(2)
if r and not r.msg then
self:setStatus('connected ...')
@@ -203,7 +230,6 @@ end
function page:transfer(item, count)
local response = self:sendRequest({ request = 'transfer', item = item, count = count })
if response then
_debug(response)
item.count = response.current - response.count
self.grid:draw()
if response.craft > 0 then
@@ -214,10 +240,29 @@ function page:transfer(item, count)
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)
if event.type == 'quit' then
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
self.dummy.infoBar:setStatus(event.focused.help)
@@ -290,6 +335,9 @@ end
function page:enable()
self:setFocus(self.statusBar.filter)
UI.Page.enable(self)
if not config.server then
self.setup:show()
end
Event.onTimeout(.1, function()
self:refresh()
self.grid:draw()
@@ -311,31 +359,21 @@ function page:applyFilter()
self.grid:setValues(t)
end
if options.slot.value or options.shield.value then
local inv = 'getInventory'
local slotNo = options.slot.value
local slotValue = options.slot.value
if options.shield.value then
slotNo = SHIELD_SLOT
slotValue = 'shield'
inv = 'getEquipment'
end
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)
Event.addRoutine(function()
while true do
os.sleep(1.5)
local neural = device.neuralInterface
local inv = config.useShield and 'getEquipment' or 'getInventory'
if not neural or not neural[inv] then
_G._debug('missing Introspection module')
elseif config.server then
local method = neural[inv]
local item = method and method().getItemMeta(config.useShield and SHIELD_SLOT or config.slot)
if item then
local slotNo = config.useShield and 'shield' or config.slot
local response = page:sendRequest({
request = 'deposit',
slot = slotValue,
slot = slotNo,
count = item.count,
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)
UI:setPage(page)
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,
},
notification = UI.Notification { },
accelerators = {
delete = 'remove_node',
}
}
function networkPage.grid:getDisplayValues(row)
@@ -236,7 +239,9 @@ The settings will take effect immediately!]],
{ heading = 'Name', key = 'displayName' },
},
sortColumn = 'displayName',
help = 'Select item to export',
accelerators = {
delete = 'remove_entry',
},
},
remove = UI.Button {
x = -4, y = 4,
@@ -255,7 +260,7 @@ The settings will take effect immediately!]],
{ name = 'Yes', value = true },
{ name = 'No', value = false },
},
help = 'Ignore damage of item when exporting'
help = 'Ignore damage of item'
},
[2] = UI.Chooser {
width = 7,
@@ -266,7 +271,7 @@ The settings will take effect immediately!]],
{ name = 'Yes', value = true },
{ name = 'No', value = false },
},
help = 'Ignore NBT of item when exporting'
help = 'Ignore NBT of item'
},
[3] = UI.Chooser {
width = 13,
@@ -276,7 +281,7 @@ The settings will take effect immediately!]],
{ name = 'whitelist', value = false },
{ name = 'blacklist', value = true },
},
help = 'Ignore damage of item when exporting'
help = 'Ignore damage of item'
},
scan = UI.Button {
x = -11, y = 1,
@@ -377,7 +382,7 @@ function nodeWizard.wizard.pages.general:enable()
self:focusFirst()
end
function nodeWizard.wizard.pages.general:setNode(node)
function nodeWizard.wizard.pages.general:showInventory(node)
local inventory
if device[node.name] and device[node.name].list then
@@ -437,7 +442,7 @@ _G._p2 = self.node
if page.isValidType then
-- TODO: dedupe list
local choice = page:isValidType(self.node)
if choice then
if choice and not Util.find(choices, 'value', choice.value) then
table.insert(choices, choice)
end
end
@@ -446,6 +451,8 @@ _G._p2 = self.node
self.wizard.pages.general.form[2].choices = choices
self.wizard.pages.general.form:setValues(self.node)
self.wizard.pages.general:showInventory(self.node)
-- restore indices
for _, page in pairs(self.wizard.pages) do
if not page.oindex then
@@ -479,6 +486,15 @@ function nodeWizard:eventHandler(event)
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
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)
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
function activityWizardPage:isValidFor(node)

View File

@@ -27,7 +27,11 @@ local brewingStandView = UI.Window {
function brewingStandView:isValidType(node)
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
function brewingStandView:isValidFor(node)

View File

@@ -15,6 +15,9 @@ local exportView = UI.Window {
},
sortColumn = 'slot',
help = 'Edit this entry',
accelerators = {
delete = 'remove_entry',
},
},
text = UI.Text {
x = 2, y = -2,
@@ -38,7 +41,11 @@ local exportView = UI.Window {
function exportView:isValidType(node)
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
function exportView:isValidFor(node)

View File

@@ -15,6 +15,9 @@ local importView = UI.Window {
},
sortColumn = 'slot',
help = 'Edit this entry',
accelerators = {
delete = 'remove_entry',
},
},
text = UI.Text {
x = 2, y = -2,
@@ -38,7 +41,11 @@ local importView = UI.Window {
function importView:isValidType(node)
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
function importView:isValidFor(node)

View File

@@ -23,7 +23,11 @@ local inputChestWizardPage = UI.Window {
function inputChestWizardPage:isValidType(node)
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
function inputChestWizardPage:isValidFor(node)

View File

@@ -29,7 +29,11 @@ local jobsWizardPage = UI.Window {
function jobsWizardPage:isValidType(node)
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
function jobsWizardPage:isValidFor(node)

View File

@@ -36,8 +36,8 @@ local listingPage = UI.Page {
--{ text = 'Forget', event = 'forget' },
{ text = 'Craft', event = 'craft' },
{ text = 'Edit', event = 'details' },
{ text = 'Network', event = 'network' },
{ text = 'Refresh', event = 'refresh', x = -9 },
{ text = 'Refresh', event = 'refresh', x = -12 },
{ text = '\206', event = 'network', x = -3 },
},
},
grid = UI.Grid {
@@ -59,7 +59,7 @@ local listingPage = UI.Page {
backgroundColor = colors.cyan,
backgroundFocusColor = colors.cyan,
accelerators = {
[ 'enter' ] = 'craft',
[ 'enter' ] = 'eject',
},
},
storageStatus = UI.Text {
@@ -198,10 +198,12 @@ function listingPage:eventHandler(event)
elseif event.type == 'craft' then
local item = self.grid:getSelected()
if Craft.findRecipe(item) or true then -- or item.is_craftable then
UI:setPage('craft', self.grid:getSelected())
else
self.notification:error('No recipe defined')
if item then
if Craft.findRecipe(item) then -- or item.is_craftable then
UI:setPage('craft', self.grid:getSelected())
else
self.notification:error('No recipe defined')
end
end
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
m.type == 'manipulator' and
m.getEnder and
{ name = 'Manipulator', value = 'manipulator' }
{
name = 'Manipulator',
value = 'manipulator',
help = 'Manipulator w/bound introspection mod'
}
end
function wizardPage:isValidFor(node)

View File

@@ -12,7 +12,7 @@ local function filter(a)
end
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
local list = bs.adapter.list()

View File

@@ -26,6 +26,7 @@ local storageView = UI.Window {
x = 16, ex = -2, y = 3,
value = '',
},
--[[
[4] = UI.Checkbox {
formLabel = 'Void', formKey = 'voidExcess',
help = 'Void excess if locked - TODO',
@@ -36,6 +37,7 @@ local storageView = UI.Window {
help = 'TODO',
pruneEmpty = true,
},
]]--
},
}
@@ -50,7 +52,11 @@ end
function storageView:isValidType(node)
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
function storageView:isValidFor(node)

View File

@@ -25,7 +25,11 @@ local trashcanWizardPage = UI.Window {
function trashcanWizardPage:isValidType(node)
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
function trashcanWizardPage:isValidFor(node)