milo wip
This commit is contained in:
@@ -85,6 +85,9 @@ if not modem or not modem.getNameLocal then
|
||||
error('Wired modem is not connected')
|
||||
end
|
||||
|
||||
local introspectionModule = Peripheral.get('plethora:introspection') or
|
||||
error('Introspection module not found')
|
||||
|
||||
local function loadResources()
|
||||
local resources = Util.readTable(Milo.RESOURCE_FILE) or { }
|
||||
for k,v in pairs(resources) do
|
||||
@@ -98,12 +101,15 @@ local context = {
|
||||
config = config,
|
||||
resources = loadResources(),
|
||||
userRecipes = Util.readTable(Milo.RECIPES_FILE) or { },
|
||||
learnTypes = { },
|
||||
machineTypes = { },
|
||||
localName = modem.getNameLocal(),
|
||||
tasks = { },
|
||||
|
||||
craftingQueue = { },
|
||||
|
||||
learnTypes = { },
|
||||
tasks = { },
|
||||
|
||||
localName = modem.getNameLocal(),
|
||||
storage = Storage(config),
|
||||
introspectionModule = introspectionModule,
|
||||
}
|
||||
|
||||
_G._p = context --debug
|
||||
|
||||
@@ -9,7 +9,6 @@ local Util = require('util')
|
||||
local colors = _G.colors
|
||||
local device = _G.device
|
||||
local socket
|
||||
local neural = device.neuralInterface
|
||||
|
||||
local options = {
|
||||
user = { arg = 'u', type = 'string',
|
||||
@@ -34,6 +33,11 @@ if not options.user.value or not options.server.value then
|
||||
error('Invalid arguments')
|
||||
end
|
||||
|
||||
if options.slot.value and
|
||||
not (device.neuralInterface and device.neuralInterface.getInventory) then
|
||||
error('Introspection module is required for transferring items')
|
||||
end
|
||||
|
||||
local page = UI.Page {
|
||||
menuBar = UI.MenuBar {
|
||||
buttons = {
|
||||
@@ -240,14 +244,21 @@ function page:applyFilter()
|
||||
self.grid:setValues(t)
|
||||
end
|
||||
|
||||
if neural and options.slot.value and neural.getInventory then
|
||||
if options.slot.value then
|
||||
debug('Transfer items initialized')
|
||||
Event.onInterval(1, function()
|
||||
local inv = neural.getInventory()
|
||||
if inv and inv.getItem(options.slot.value) then
|
||||
page:sendRequest({ request = 'deposit', slot = options.slot.value })
|
||||
-- local item =
|
||||
-- TODO: update count for this one item
|
||||
-- page.grid:draw() page:sync()
|
||||
local neural = device.neuralInterface
|
||||
if neural and neural.getInventory then
|
||||
local item = neural.getInventory().getItem(options.slot.value)
|
||||
if item then
|
||||
debug('depositing')
|
||||
page:sendRequest({ request = 'deposit', slot = options.slot.value })
|
||||
-- local item =
|
||||
-- TODO: update count for this one item
|
||||
-- page.grid:draw() page:sync()
|
||||
end
|
||||
else
|
||||
debug('missing Introspection module')
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -129,6 +129,20 @@ function Milo:clearGrid()
|
||||
return clear() or clear()
|
||||
end
|
||||
|
||||
function Milo:getTurtleInventory()
|
||||
local list = { }
|
||||
for i = 1,16 do
|
||||
-- TODO: update item db
|
||||
local item = self.context.introspectionModule.getInventory().getItemMeta(i)
|
||||
if item then
|
||||
itemDB:add(item)
|
||||
list[i] = item
|
||||
end
|
||||
end
|
||||
itemDB:flush()
|
||||
return list
|
||||
end
|
||||
|
||||
function Milo:eject(item, qty)
|
||||
local s, m = pcall(function()
|
||||
self.context.storage:provide(item, qty)
|
||||
|
||||
@@ -23,17 +23,24 @@ listCount = 0,
|
||||
local modem = Peripheral.get('wired_modem') or error('Wired modem not attached')
|
||||
self.localName = modem.getNameLocal()
|
||||
|
||||
Event.on({ 'device_attach' }, function(_, dev)
|
||||
debug('attach: ' .. dev)
|
||||
Event.on({ 'device_attach', 'device_detach' }, function(e, dev)
|
||||
debug('%s: %s', e, tostring(dev))
|
||||
self:initStorage()
|
||||
end)
|
||||
|
||||
Event.on({ 'device_detach' }, function(_, dev)
|
||||
debug('detach: ' .. dev)
|
||||
self:initStorage(dev)
|
||||
Event.onInterval(15, function()
|
||||
self:showStorage()
|
||||
end)
|
||||
end
|
||||
|
||||
function NetworkedAdapter:showStorage()
|
||||
debug('Storage:')
|
||||
for k,v in pairs(self.remoteDefaults) do
|
||||
local online = v.adapter and v.adapter.online
|
||||
debug(' %s: %s', online and ' online' or 'offline', k)
|
||||
end
|
||||
debug('')
|
||||
end
|
||||
|
||||
function NetworkedAdapter:setOnline(online)
|
||||
if online ~= self.storageOnline then
|
||||
self.storageOnline = online
|
||||
@@ -59,7 +66,6 @@ function NetworkedAdapter:initStorage()
|
||||
end
|
||||
if v.mtype == 'storage' then
|
||||
online = online and not not (v.adapter and v.adapter.online)
|
||||
debug(' %s: %s', v.adapter and v.adapter.online and ' online' or 'offline', k)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -51,6 +51,22 @@ local machinesPage = UI.Page {
|
||||
},
|
||||
}
|
||||
|
||||
function machinesPage.grid:getDisplayValues(row)
|
||||
row = Util.shallowCopy(row)
|
||||
row.displayName = row.displayName or row.name
|
||||
return row
|
||||
end
|
||||
|
||||
function machinesPage.grid:getRowTextColor(row, selected)
|
||||
if not device[row.name] then
|
||||
return colors.red
|
||||
end
|
||||
if row.mtype == 'ignore' then
|
||||
return colors.lightGray
|
||||
end
|
||||
return UI.Grid:getRowTextColor(row, selected)
|
||||
end
|
||||
|
||||
function machinesPage:getList()
|
||||
-- TODO: remove dedupe naming in perf code ?
|
||||
for _, v in pairs(device) do
|
||||
@@ -82,22 +98,6 @@ function machinesPage:disable()
|
||||
Event.off(self.handler)
|
||||
end
|
||||
|
||||
function machinesPage.grid:getDisplayValues(row)
|
||||
row = Util.shallowCopy(row)
|
||||
row.displayName = row.displayName or row.name
|
||||
return row
|
||||
end
|
||||
|
||||
function machinesPage.grid:getRowTextColor(row, selected)
|
||||
if not device[row.name] then
|
||||
return colors.red
|
||||
end
|
||||
if row.mtype == 'ignore' then
|
||||
return colors.lightGray
|
||||
end
|
||||
return UI.Grid:getRowTextColor(row, selected)
|
||||
end
|
||||
|
||||
function machinesPage:eventHandler(event)
|
||||
if event.type == 'grid_select' then
|
||||
UI:setPage('machineWizard', event.selected)
|
||||
@@ -178,8 +178,146 @@ The settings will take effect immediately!]],
|
||||
backgroundColor = colors.cyan,
|
||||
},
|
||||
notification = UI.Notification { },
|
||||
filter = UI.SlideOut {
|
||||
backgroundColor = colors.cyan,
|
||||
menuBar = UI.MenuBar {
|
||||
buttons = {
|
||||
{ text = 'Save', event = 'save' },
|
||||
{ text = 'Cancel', event = 'cancel' },
|
||||
},
|
||||
},
|
||||
grid = UI.ScrollingGrid {
|
||||
x = 2, ex = -6, y = 2, ey = -6,
|
||||
columns = {
|
||||
{ heading = 'Name', key = 'displayName' },
|
||||
},
|
||||
sortColumn = 'displayName',
|
||||
help = 'Select item to export',
|
||||
},
|
||||
remove = UI.Button {
|
||||
x = -4, y = 4,
|
||||
text = '-', event = 'remove_entry', help = 'Remove',
|
||||
},
|
||||
form = UI.Form {
|
||||
x = 2, y = -4, height = 3,
|
||||
margin = 1,
|
||||
manualControls = true,
|
||||
[1] = UI.Chooser {
|
||||
width = 7,
|
||||
formLabel = 'Ignore Dmg', formKey = 'ignoreDamage',
|
||||
pruneEmpty = true,
|
||||
nochoice = 'No',
|
||||
choices = {
|
||||
{ name = 'Yes', value = true },
|
||||
{ name = 'No', value = false },
|
||||
},
|
||||
help = 'Ignore damage of item when exporting'
|
||||
},
|
||||
[2] = UI.Chooser {
|
||||
width = 7,
|
||||
formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash',
|
||||
pruneEmpty = true,
|
||||
nochoice = 'No',
|
||||
choices = {
|
||||
{ name = 'Yes', value = true },
|
||||
{ name = 'No', value = false },
|
||||
},
|
||||
help = 'Ignore NBT of item when exporting'
|
||||
},
|
||||
[3] = UI.Chooser {
|
||||
width = 13,
|
||||
formLabel = 'Mode', formKey = 'blacklist',
|
||||
nochoice = 'whitelist',
|
||||
choices = {
|
||||
{ name = 'whitelist', value = false },
|
||||
{ name = 'blacklist', value = true },
|
||||
},
|
||||
help = 'Ignore damage of item when exporting'
|
||||
},
|
||||
scan = UI.Button {
|
||||
x = -11, y = 1,
|
||||
text = 'Scan', event = 'scan_turtle',
|
||||
help = 'Add items to turtle to add to filter',
|
||||
},
|
||||
},
|
||||
statusBar = UI.StatusBar {
|
||||
backgroundColor = colors.cyan,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
--[[ Filter slide out ]] --
|
||||
function machineWizard.filter:show(entry, callback, whitelistOnly)
|
||||
self.entry = entry
|
||||
self.callback = callback
|
||||
|
||||
if not self.entry.filter then
|
||||
self.entry.filter = { }
|
||||
end
|
||||
|
||||
self.form:setValues(entry)
|
||||
self:resetGrid()
|
||||
|
||||
self.form[3].inactive = whitelistOnly
|
||||
|
||||
UI.SlideOut.show(self)
|
||||
-- self:setFocus(self.filter)
|
||||
end
|
||||
|
||||
function machineWizard.filter:resetGrid()
|
||||
local t = { }
|
||||
for k in pairs(self.entry.filter) do
|
||||
table.insert(t, itemDB:splitKey(k))
|
||||
end
|
||||
self.grid:setValues(t)
|
||||
end
|
||||
|
||||
function machineWizard.filter.grid:getDisplayValues(row)
|
||||
row = Util.shallowCopy(row)
|
||||
row.displayName = itemDB:getName(row)
|
||||
return row
|
||||
end
|
||||
|
||||
function machineWizard.filter:eventHandler(event)
|
||||
if event.type == 'focus_change' then
|
||||
self.statusBar:setStatus(event.focused.help)
|
||||
|
||||
elseif event.type == 'scan_turtle' then
|
||||
local inventory = Milo:getTurtleInventory()
|
||||
for _,item in pairs(inventory) do
|
||||
self.entry.filter[Milo:uniqueKey(item)] = true
|
||||
end
|
||||
self:resetGrid()
|
||||
self.grid:update()
|
||||
self.grid:draw()
|
||||
|
||||
elseif event.type == 'remove_entry' then
|
||||
local row = self.grid:getSelected()
|
||||
if row then
|
||||
Util.removeByValue(self.grid.values, row)
|
||||
self.grid:update()
|
||||
self.grid:draw()
|
||||
end
|
||||
|
||||
elseif event.type == 'save' then
|
||||
self.form:save()
|
||||
self.entry.filter = { }
|
||||
for _,v in pairs(self.grid.values) do
|
||||
self.entry.filter[Milo:uniqueKey(v)] = true
|
||||
end
|
||||
self:hide()
|
||||
self.callback()
|
||||
|
||||
elseif event.type == 'cancel' then
|
||||
self:hide()
|
||||
|
||||
else
|
||||
return UI.SlideOut.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--[[ General Page ]] --
|
||||
function machineWizard.wizard.pages.general:enable()
|
||||
UI.Window.enable(self)
|
||||
self:focusFirst()
|
||||
@@ -208,6 +346,7 @@ function machineWizard.wizard.pages.general:validate()
|
||||
return self.form:save()
|
||||
end
|
||||
|
||||
--[[ Wizard ]] --
|
||||
function machineWizard.wizard:eventHandler(event)
|
||||
if event.type == 'nextView' and
|
||||
Util.find(self.pages, 'enabled', true) == self.pages.general then
|
||||
@@ -215,11 +354,16 @@ function machineWizard.wizard:eventHandler(event)
|
||||
if self.pages.general.form:save() then
|
||||
local index = 2
|
||||
for _, page in pairs(self.pages) do
|
||||
if page.mtype == machineWizard.machine.mtype then
|
||||
page.index = nil
|
||||
end
|
||||
self.pages.general.index = 1
|
||||
self.pages.confirmation.index = 2
|
||||
|
||||
for k, page in pairs(self.pages) do
|
||||
debug(k)
|
||||
if not page.index and page:isValidFor(self.parent.machine) then
|
||||
page.index = index
|
||||
index = index + 1
|
||||
elseif page.index ~= 1 then
|
||||
page.index = nil
|
||||
end
|
||||
end
|
||||
self.pages.confirmation.index = index
|
||||
@@ -237,6 +381,7 @@ function machineWizard:enable(machine)
|
||||
self.machine.adapter = adapter
|
||||
machine.adapter = adapter
|
||||
|
||||
_G._p2 = self.machine
|
||||
self.wizard.pages.general.form:setValues(self.machine)
|
||||
self.wizard.pages.general.form[1].shadowText = self.machine.name
|
||||
|
||||
@@ -286,6 +431,9 @@ function machineWizard:eventHandler(event)
|
||||
|
||||
UI:setPreviousPage()
|
||||
|
||||
elseif event.type == 'edit_filter' then
|
||||
self.filter:show(event.entry, event.callback, event.whitelistOnly)
|
||||
|
||||
elseif event.type == 'enable_view' then
|
||||
local current = event.next or event.prev
|
||||
self.titleBar.title = current.title or 'Machine'
|
||||
|
||||
@@ -14,22 +14,30 @@ function ExportTask:cycle(context)
|
||||
local machine = device[target]
|
||||
if machine and machine.getItemMeta then
|
||||
for _, entry in pairs(v.exports) do
|
||||
local slot = machine.getItemMeta(entry.slot) or { count = 0 }
|
||||
local item = itemDB:splitKey(entry.name)
|
||||
local slotNo = type(entry.slot) == 'number' and entry.slot or nil -- '*' indicates any slot
|
||||
|
||||
-- is something else is in this slot
|
||||
if not slot.name or slot.name == item.name then
|
||||
local maxCount = slot.maxCount or itemDB:getMaxCount(item)
|
||||
local count = maxCount - slot.count
|
||||
local slot = (slotNo and machine.getItemMeta(slotNo)) or { count = 0 }
|
||||
for key in pairs(entry.filter) do
|
||||
local item = itemDB:splitKey(key)
|
||||
|
||||
if count > 0 then
|
||||
item = Milo:getItemWithQty(item)
|
||||
if item and count > 0 then
|
||||
context.storage:export(
|
||||
target,
|
||||
entry.slot,
|
||||
math.min(count, item.count),
|
||||
item)
|
||||
-- is something else is in this slot
|
||||
if not slot.name or slot.name == item.name then
|
||||
local maxCount = slot.maxCount or itemDB:getMaxCount(item)
|
||||
local count = maxCount - slot.count
|
||||
if not slotNo then
|
||||
-- TODO: should we just execute export -
|
||||
-- or scan all slots for space ??
|
||||
count = machine.size() * maxCount - slot.count
|
||||
end
|
||||
if count > 0 then
|
||||
item = Milo:getItemWithQty(item)
|
||||
if item and count > 0 then
|
||||
context.storage:export(
|
||||
target,
|
||||
slotNo,
|
||||
math.min(count, item.count),
|
||||
item)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,181 +1,43 @@
|
||||
local itemDB = require('itemDB')
|
||||
local Milo = require('milo')
|
||||
local UI = require('ui')
|
||||
local Util = require('util')
|
||||
|
||||
local colors = _G.colors
|
||||
local device = _G.device
|
||||
|
||||
local itemSlideout = UI.SlideOut {
|
||||
backgroundColor = colors.cyan,
|
||||
menuBar = UI.MenuBar {
|
||||
buttons = {
|
||||
{ text = 'Save', event = 'save' },
|
||||
{ text = 'Cancel', event = 'cancel' },
|
||||
{ text = 'Refresh', event = 'refresh', x = -9 },
|
||||
},
|
||||
},
|
||||
grid = UI.ScrollingGrid {
|
||||
y = 2, ey = -6,
|
||||
columns = {
|
||||
{ heading = 'Name', key = 'displayName', width = 31 },
|
||||
{ heading = 'Qty', key = 'count' , width = 5 },
|
||||
},
|
||||
sortColumn = 'displayName',
|
||||
help = 'Select item to export',
|
||||
},
|
||||
filter = UI.TextEntry {
|
||||
x = 2, ex = 18, y = -3,
|
||||
limit = 50,
|
||||
shadowText = 'filter',
|
||||
backgroundColor = colors.lightGray,
|
||||
backgroundFocusColor = colors.lightGray,
|
||||
},
|
||||
form = UI.Form {
|
||||
x = 21, y = -4, height = 3,
|
||||
margin = 1,
|
||||
manualControls = true,
|
||||
[1] = UI.Chooser {
|
||||
width = 7,
|
||||
formLabel = 'Slot', formKey = 'slot',
|
||||
nochoice = 1,
|
||||
help = 'Export into this slot',
|
||||
},
|
||||
[2] = UI.Chooser {
|
||||
width = 7,
|
||||
formLabel = 'Ignore Dmg', formKey = 'ignoreDamage',
|
||||
pruneEmpty = true,
|
||||
nochoice = 'No',
|
||||
choices = {
|
||||
{ name = 'Yes', value = true },
|
||||
{ name = 'No', value = false },
|
||||
},
|
||||
help = 'Ignore damage of item when exporting'
|
||||
},
|
||||
[3] = UI.Chooser {
|
||||
width = 7,
|
||||
formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash',
|
||||
pruneEmpty = true,
|
||||
nochoice = 'No',
|
||||
choices = {
|
||||
{ name = 'Yes', value = true },
|
||||
{ name = 'No', value = false },
|
||||
},
|
||||
help = 'Ignore NBT of item when exporting'
|
||||
},
|
||||
},
|
||||
statusBar = UI.StatusBar {
|
||||
backgroundColor = colors.cyan,
|
||||
},
|
||||
}
|
||||
|
||||
function itemSlideout:filterItems(t, filter)
|
||||
if filter and #filter > 0 then
|
||||
local r = {}
|
||||
filter = filter:lower()
|
||||
for _,v in pairs(t) do
|
||||
if string.find(v.lname, filter) then
|
||||
table.insert(r, v)
|
||||
end
|
||||
end
|
||||
return r
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
function itemSlideout.grid:enable()
|
||||
if not self.allItems then
|
||||
self.allItems = Milo:listItems()
|
||||
Milo:mergeResources(self.allItems)
|
||||
self:setValues(self.allItems)
|
||||
end
|
||||
UI.Grid.enable(self)
|
||||
end
|
||||
|
||||
function itemSlideout:show(machine, entry, callback)
|
||||
self.entry = entry
|
||||
self.callback = callback
|
||||
|
||||
self.form.choices = { }
|
||||
local m = device[machine.name]
|
||||
for k = 1, m.size() do
|
||||
table.insert(self.form[1].choices, {
|
||||
name = k,
|
||||
value = k,
|
||||
})
|
||||
end
|
||||
|
||||
if not entry.slot then
|
||||
entry.slot = 1
|
||||
end
|
||||
self.form:setValues(entry)
|
||||
|
||||
UI.SlideOut.show(self)
|
||||
self:setFocus(self.filter)
|
||||
--self.filter:focus()
|
||||
end
|
||||
|
||||
function itemSlideout:eventHandler(event)
|
||||
if event.type == 'text_change' then
|
||||
local t = self:filterItems(self.grid.allItems, event.text)
|
||||
self.grid:setValues(t)
|
||||
self.grid:draw()
|
||||
|
||||
elseif event.type == 'focus_change' then
|
||||
self.statusBar:setStatus(event.focused.help)
|
||||
|
||||
elseif event.type == 'save' then
|
||||
local selected = self.grid:getSelected()
|
||||
if not selected then
|
||||
self.statusBar:setStatus('Select an item to export')
|
||||
else
|
||||
self.form:save()
|
||||
self.form.values.name = itemDB:makeKey(selected)
|
||||
self:hide()
|
||||
self.callback()
|
||||
end
|
||||
|
||||
elseif event.type == 'cancel' then
|
||||
self:hide()
|
||||
|
||||
elseif event.type == 'refresh' then
|
||||
self.allItems = Milo:listItems()
|
||||
Milo:mergeResources(self.allItems)
|
||||
local t = self:filterItems(self.allItems, self.filter.value)
|
||||
self.grid:setValues(t)
|
||||
self.grid:draw()
|
||||
self.filter:focus()
|
||||
else
|
||||
return UI.SlideOut.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local exportView = UI.Window {
|
||||
mtype = 'machine',
|
||||
title = 'Export item into machine',
|
||||
index = 3,
|
||||
grid = UI.ScrollingGrid {
|
||||
x = 2, ex = -6, y = 2, ey = -2,
|
||||
x = 2, ex = -6, y = 2, ey = -4,
|
||||
columns = {
|
||||
{ heading = 'Slot', key = 'slot', width = 4 },
|
||||
{ heading = 'Item', key = 'displayName' },
|
||||
{ heading = 'Slot', key = 'slot', width = 4 },
|
||||
{ heading = 'Filter', key = 'filter' },
|
||||
},
|
||||
sortColumn = 'slot',
|
||||
help = 'Edit this entry',
|
||||
},
|
||||
text = UI.Text {
|
||||
x = 2, y = -2,
|
||||
value = 'Slot',
|
||||
},
|
||||
slots = UI.Chooser {
|
||||
x = 7, y = -2,
|
||||
width = 7,
|
||||
nochoice = 'All',
|
||||
help = 'Export to this slot',
|
||||
},
|
||||
add = UI.Button {
|
||||
x = -4, y = 4,
|
||||
text = '+', event = 'add_export', help = '...',
|
||||
x = 15, y = -2,
|
||||
text = '+', event = 'add_entry', help = 'Add',
|
||||
},
|
||||
remove = UI.Button {
|
||||
x = -4, y = 6,
|
||||
text = '-', event = 'remove_export', help = '...',
|
||||
x = -4, y = 4,
|
||||
text = '-', event = 'remove_entry', help = 'Remove',
|
||||
},
|
||||
}
|
||||
|
||||
function exportView:save(machine)
|
||||
machine.exports = not Util.empty(self.grid.values) and self.grid.values or nil
|
||||
return true
|
||||
function exportView:isValidFor(machine)
|
||||
return machine.mtype == 'machine'
|
||||
end
|
||||
|
||||
function exportView:setMachine(machine)
|
||||
@@ -184,30 +46,53 @@ function exportView:setMachine(machine)
|
||||
self.machine.exports = { }
|
||||
end
|
||||
self.grid:setValues(machine.exports)
|
||||
|
||||
self.slots.choices = {
|
||||
{ name = 'All', value = '*' }
|
||||
}
|
||||
|
||||
-- TODO: what if device is dettached ?
|
||||
local m = device[machine.name]
|
||||
for k = 1, m.size() do
|
||||
table.insert(self.slots.choices, { name = k, value = k })
|
||||
end
|
||||
end
|
||||
|
||||
function exportView.grid:getDisplayValues(row)
|
||||
row = Util.shallowCopy(row)
|
||||
row.displayName = itemDB:getName(row.name)
|
||||
if not row.filter or Util.empty(row.filter) then
|
||||
row.filter = 'none'
|
||||
else
|
||||
local t = { }
|
||||
for key in pairs(row.filter) do
|
||||
table.insert(t, itemDB:getName(key))
|
||||
end
|
||||
row.filter = table.concat(t, ', ')
|
||||
end
|
||||
return row
|
||||
end
|
||||
|
||||
function exportView:eventHandler(event)
|
||||
if event.type == 'grid_select' then
|
||||
itemSlideout:show(self.machine, self.grid:getSelected(), function()
|
||||
self.grid:update()
|
||||
self.grid:draw()
|
||||
end)
|
||||
self:emit({
|
||||
type = 'edit_filter',
|
||||
entry = self.grid:getSelected(),
|
||||
whitelistOnly = true,
|
||||
callback = function()
|
||||
self.grid:update()
|
||||
self.grid:draw()
|
||||
end,
|
||||
})
|
||||
|
||||
elseif event.type == 'add_export' then
|
||||
local export = { }
|
||||
itemSlideout:show(self.machine, export, function()
|
||||
table.insert(self.machine.exports, export)
|
||||
self.grid:update()
|
||||
self.grid:draw()
|
||||
end)
|
||||
elseif event.type == 'add_entry' then
|
||||
table.insert(self.machine.exports, {
|
||||
slot = self.slots.value or '*',
|
||||
filter = { },
|
||||
})
|
||||
self.grid:update()
|
||||
self.grid:draw()
|
||||
|
||||
elseif event.type == 'remove_export' then
|
||||
elseif event.type == 'remove_entry' then
|
||||
local row = self.grid:getSelected()
|
||||
if row then
|
||||
Util.removeByValue(self.grid.values, row)
|
||||
@@ -217,5 +102,4 @@ function exportView:eventHandler(event)
|
||||
end
|
||||
end
|
||||
|
||||
UI:getPage('machineWizard'):add({ items = itemSlideout })
|
||||
UI:getPage('machineWizard').wizard:add({ export = exportView })
|
||||
|
||||
@@ -11,11 +11,34 @@ function ImportTask:cycle(context)
|
||||
for source, v in pairs(context.config.remoteDefaults) do
|
||||
if v.imports then
|
||||
local inventory = device[source]
|
||||
if inventory and inventory.getItemMeta then
|
||||
for slot in pairs(v.imports) do
|
||||
local item = inventory.getItemMeta(slot)
|
||||
if item then
|
||||
context.storage:import(source, slot, item.count, item)
|
||||
if inventory then
|
||||
for _, entry in pairs(v.imports) do
|
||||
|
||||
local function matchesFilter(item)
|
||||
if not entry.filter then
|
||||
return true
|
||||
end
|
||||
|
||||
local key = Milo:uniqueKey(item)
|
||||
if entry.blacklist then
|
||||
return not entry.filter[key]
|
||||
end
|
||||
return entry.filter[key]
|
||||
end
|
||||
|
||||
local function importSlot(slotNo)
|
||||
local item = inventory.getItemMeta(slotNo)
|
||||
if item and matchesFilter(item) then
|
||||
context.storage:import(source, slotNo, item.count, item)
|
||||
end
|
||||
end
|
||||
|
||||
if type(entry.slot) == 'number' then
|
||||
importSlot(entry.slot)
|
||||
else
|
||||
for i = 1, inventory.size() do
|
||||
importSlot(i)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
|
||||
@@ -1,55 +1,103 @@
|
||||
local UI = require('ui')
|
||||
local Util = require('util')
|
||||
local itemDB = require('itemDB')
|
||||
local UI = require('ui')
|
||||
local Util = require('util')
|
||||
|
||||
local device = _G.device
|
||||
|
||||
local importView = UI.Window {
|
||||
mtype = 'machine',
|
||||
title = 'Import item from machine',
|
||||
index = 4,
|
||||
grid = UI.ScrollingGrid {
|
||||
y = 2, ey = -2,
|
||||
x = 2, ex = -6, y = 2, ey = -4,
|
||||
columns = {
|
||||
{ heading = 'Slot', key = 'slot', width = 4 },
|
||||
{ heading = 'Import', key = 'import' },
|
||||
{ heading = 'Filter', key = 'filter' },
|
||||
},
|
||||
sortColumn = 'slot',
|
||||
help = 'Double-click to toggle'
|
||||
help = 'Edit this entry',
|
||||
},
|
||||
text = UI.Text {
|
||||
x = 2, y = -2,
|
||||
value = 'Slot',
|
||||
},
|
||||
slots = UI.Chooser {
|
||||
x = 7, y = -2,
|
||||
width = 7,
|
||||
nochoice = 'All',
|
||||
help = 'Import from this slot',
|
||||
},
|
||||
add = UI.Button {
|
||||
x = 15, y = -2,
|
||||
text = '+', event = 'add_entry', help = 'Add',
|
||||
},
|
||||
remove = UI.Button {
|
||||
x = -4, y = 4,
|
||||
text = '-', event = 'remove_entry', help = 'Remove',
|
||||
},
|
||||
}
|
||||
|
||||
function importView:setMachine(machine)
|
||||
local m = device[machine.name]
|
||||
|
||||
local t = { }
|
||||
for k = 1, m.size() do
|
||||
t[k] = { slot = k }
|
||||
end
|
||||
|
||||
if machine.imports then
|
||||
for k,v in pairs(machine.imports) do
|
||||
t[k] = { slot = k, import = v }
|
||||
end
|
||||
end
|
||||
|
||||
self.grid:setValues(t)
|
||||
function importView:isValidFor(machine)
|
||||
return machine.mtype == 'machine'
|
||||
end
|
||||
|
||||
function importView:save(machine)
|
||||
local t = { }
|
||||
for k,v in pairs(self.grid.values) do
|
||||
if v.import then
|
||||
t[k] = true
|
||||
end
|
||||
function importView:setMachine(machine)
|
||||
self.machine = machine
|
||||
if not self.machine.imports then
|
||||
self.machine.imports = { }
|
||||
end
|
||||
machine.imports = not Util.empty(t) and t or nil
|
||||
return true
|
||||
self.grid:setValues(machine.imports)
|
||||
|
||||
self.slots.choices = {
|
||||
{ name = 'All', value = '*' }
|
||||
}
|
||||
|
||||
-- TODO: what if device is dettached ?
|
||||
local m = device[machine.name]
|
||||
for k = 1, m.size() do
|
||||
table.insert(self.slots.choices, { name = k, value = k })
|
||||
end
|
||||
end
|
||||
|
||||
function importView.grid:getDisplayValues(row)
|
||||
row = Util.shallowCopy(row)
|
||||
if not row.filter or Util.empty(row.filter) then
|
||||
row.filter = 'none'
|
||||
else
|
||||
local t = { }
|
||||
for key in pairs(row.filter) do
|
||||
table.insert(t, itemDB:getName(key))
|
||||
end
|
||||
row.filter = table.concat(t, ', ')
|
||||
end
|
||||
return row
|
||||
end
|
||||
|
||||
function importView:eventHandler(event)
|
||||
if event.type == 'grid_select' then
|
||||
event.selected.import = not event.selected.import
|
||||
self:emit({
|
||||
type = 'edit_filter',
|
||||
entry = self.grid:getSelected(),
|
||||
callback = function()
|
||||
self.grid:update()
|
||||
self.grid:draw()
|
||||
end,
|
||||
})
|
||||
|
||||
elseif event.type == 'add_entry' then
|
||||
table.insert(self.machine.imports, {
|
||||
slot = self.slots.value or '*',
|
||||
filter = { },
|
||||
})
|
||||
self.grid:update()
|
||||
self.grid:draw()
|
||||
|
||||
elseif event.type == 'remove_entry' then
|
||||
local row = self.grid:getSelected()
|
||||
if row then
|
||||
Util.removeByValue(self.grid.values, row)
|
||||
self.grid:update()
|
||||
self.grid:draw()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ end
|
||||
function jobList.grid:getDisplayValues(row)
|
||||
row = Util.shallowCopy(row)
|
||||
if row.showRemaining then
|
||||
row.remaining = row.count - row.crafted
|
||||
row.remaining = math.max(0, row.count - row.crafted)
|
||||
else
|
||||
row.displayName = ' ' .. row.displayName
|
||||
end
|
||||
|
||||
@@ -12,17 +12,6 @@ local MACHINE_LOOKUP = 'usr/config/machine_crafting.db'
|
||||
|
||||
local context = Milo:getContext()
|
||||
|
||||
local function getTurtleInventory()
|
||||
local introspectionModule = device['plethora:introspection'] or
|
||||
error('Introspection module not found')
|
||||
|
||||
local list = { }
|
||||
for i = 1,16 do
|
||||
list[i] = introspectionModule.getInventory().getItemMeta(i)
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
local machineLearnWizard = UI.Page {
|
||||
titleBar = UI.TitleBar { title = 'Learn a crafting recipe' },
|
||||
wizard = UI.Wizard {
|
||||
@@ -92,7 +81,7 @@ function pages.machine:validate()
|
||||
end
|
||||
|
||||
function pages.confirmation:validate()
|
||||
local inventory = getTurtleInventory()
|
||||
local inventory = Milo:getTurtleInventory()
|
||||
local result = inventory[16]
|
||||
local slotCount = machine.size()
|
||||
|
||||
|
||||
28
milo/plugins/redstoneTask.lua
Normal file
28
milo/plugins/redstoneTask.lua
Normal file
@@ -0,0 +1,28 @@
|
||||
local Event = require('event')
|
||||
local Milo = require('milo')
|
||||
|
||||
local device = _G.device
|
||||
|
||||
local RedstoneTask = {
|
||||
name = 'redstone',
|
||||
priority = 40,
|
||||
}
|
||||
|
||||
function RedstoneTask:cycle(context)
|
||||
for _,v in pairs(context.config.remoteDefaults) do
|
||||
if v.redstone then
|
||||
local ri = device[v.redstone.integrator]
|
||||
local function conditionsSatisfied()
|
||||
return not not next(ri.list())
|
||||
end
|
||||
if conditionsSatisfied() then
|
||||
ri.setOutput(v.redstone.side, true)
|
||||
Event.onTimeout(.25, function()
|
||||
ri.setOutput(v.redstone.side, false)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Milo:registerTask(RedstoneTask)
|
||||
73
milo/plugins/redstoneView.lua
Normal file
73
milo/plugins/redstoneView.lua
Normal file
@@ -0,0 +1,73 @@
|
||||
local UI = require('ui')
|
||||
|
||||
local colors = _G.colors
|
||||
local device = _G.device
|
||||
|
||||
local dispenserView = UI.Window {
|
||||
index = 10,
|
||||
title = 'Redstone Control',
|
||||
backgroundColor = colors.cyan,
|
||||
form = UI.Form {
|
||||
x = 1, y = 2, ex = -1, ey = -2,
|
||||
manualControls = true,
|
||||
[1] = UI.TextEntry {
|
||||
formLabel = 'Interval', formKey = 'interval',
|
||||
help = 'Pulse redstone if items are present',
|
||||
limit = 6,
|
||||
validate = 'numeric',
|
||||
},
|
||||
[2] = UI.Chooser {
|
||||
formLabel = 'Integrator', formKey = 'integrator',
|
||||
nochoice = 'disable',
|
||||
help = 'Control via redstone',
|
||||
},
|
||||
[3] = UI.Chooser {
|
||||
width = 10,
|
||||
formLabel = 'Side', formKey = 'side',
|
||||
choices = {
|
||||
{ name = 'up', value = 'up' },
|
||||
{ name = 'down', value = 'down' },
|
||||
{ name = 'east', value = 'east' },
|
||||
{ name = 'north', value = 'north' },
|
||||
{ name = 'west', value = 'west' },
|
||||
{ name = 'south', value = 'south' },
|
||||
},
|
||||
help = 'Output side',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
function dispenserView:isValidFor(machine)
|
||||
if machine.mtype == 'machine' then
|
||||
local m = device[machine.name]
|
||||
return m and m.type == 'minecraft:dispenser'
|
||||
end
|
||||
end
|
||||
|
||||
function dispenserView:enable()
|
||||
UI.Window.enable(self)
|
||||
self:focusFirst()
|
||||
|
||||
self.form[2].choices = { }
|
||||
for _,m in pairs(device) do
|
||||
if m.type == 'redstone_integrator' then
|
||||
table.insert(self.form[2].choices, {
|
||||
name = m.name,
|
||||
value = m.name,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function dispenserView:validate()
|
||||
return self.form:save()
|
||||
end
|
||||
|
||||
function dispenserView:setMachine(machine)
|
||||
if not machine.redstone then
|
||||
machine.redstone = { }
|
||||
end
|
||||
self.form:setValues(machine.redstone)
|
||||
end
|
||||
|
||||
UI:getPage('machineWizard').wizard:add({ dispenser = dispenserView })
|
||||
@@ -33,6 +33,7 @@ local function client(socket)
|
||||
if not data then
|
||||
break
|
||||
end
|
||||
debug('remote: ' .. data.request)
|
||||
if data.request == 'list' then
|
||||
local items = Milo:refreshItems()
|
||||
Milo:mergeResources(items)
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
local itemDB = require('itemDB')
|
||||
local UI = require('ui')
|
||||
local UI = require('ui')
|
||||
|
||||
local colors = _G.colors
|
||||
local device = _G.device
|
||||
|
||||
local storageView = UI.Window {
|
||||
mtype = 'storage',
|
||||
title = 'Storage Options',
|
||||
index = 2,
|
||||
backgroundColor = colors.cyan,
|
||||
@@ -49,6 +48,10 @@ function storageView:validate()
|
||||
return self.form:save()
|
||||
end
|
||||
|
||||
function storageView:isValidFor(machine)
|
||||
return machine.mtype == 'storage'
|
||||
end
|
||||
|
||||
function storageView:setMachine(machine)
|
||||
self.machine = machine
|
||||
self.form:setValues(machine)
|
||||
|
||||
@@ -4,24 +4,12 @@ local Milo = require('milo')
|
||||
local UI = require('ui')
|
||||
local Util = require('util')
|
||||
|
||||
local device = _G.device
|
||||
local turtle = _G.turtle
|
||||
|
||||
local context = Milo:getContext()
|
||||
|
||||
local function getTurtleInventory()
|
||||
local introspectionModule = device['plethora:introspection'] or
|
||||
error('Introspection module not found')
|
||||
|
||||
local list = { }
|
||||
for i = 1,16 do
|
||||
list[i] = introspectionModule.getInventory().getItemMeta(i)
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
local function learnRecipe()
|
||||
local ingredients = getTurtleInventory()
|
||||
local ingredients = Milo:getTurtleInventory()
|
||||
|
||||
if not ingredients then
|
||||
return false, 'No recipe defined'
|
||||
@@ -32,7 +20,7 @@ local function learnRecipe()
|
||||
return false, 'Failed to craft'
|
||||
end
|
||||
|
||||
local results = getTurtleInventory()
|
||||
local results = Milo:getTurtleInventory()
|
||||
if not results or not results[1] then
|
||||
return false, 'Failed to craft'
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user