diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index aa652d4..070e538 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -65,6 +65,9 @@ function Storage:initStorage() v.adapter = InventoryAdapter.wrap({ side = k }) v.adapter.online = true v.adapter.dirty = true + elseif device[k] then + v.adapter = device[k] + v.adapter.online = true end if v.mtype == 'storage' then online = online and not not (v.adapter and v.adapter.online) @@ -73,15 +76,23 @@ function Storage:initStorage() if online ~= self.storageOnline then self.storageOnline = online + -- TODO: if online, then list items os.queueEvent(self.storageOnline and 'storage_online' or 'storage_offline', online) _G._debug('Storage: %s', self.storageOnline and 'online' or 'offline') end end -function Storage:filterActive(mtype, filter) +function Storage:getSingleNode(mtype) + local node = Util.find(self.nodes, 'mtype', mtype) + if node and node.adapter and node.adapter.online then + return node + end +end + +function Storage:filterNodes(mtype, filter) local iter = { } for _, v in pairs(self.nodes) do - if v.adapter and v.adapter.online and v.mtype == mtype then + if v.mtype == mtype then if not filter or filter(v) then table.insert(iter, v) end @@ -95,6 +106,14 @@ function Storage:filterActive(mtype, filter) end end +function Storage:filterActive(mtype, filter) + return self:filterNodes(mtype, function(v) + if v.adapter and v.adapter.online then + return not filter and true or filter(v) + end + end) +end + function Storage:onlineAdapters(reversed) local iter = { } for _, v in pairs(self.nodes) do @@ -184,6 +203,12 @@ _G._debug('STORAGE: refresh in ' .. timer()) end function Storage:updateCache(adapter, key, count) + if not adapter.cache then + adapter.dirty = true + self.dirty = true + return + end + local entry = adapter.cache[key] if not entry then diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 0dda36f..f8cb0cc 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -12,6 +12,8 @@ local turtle = _G.turtle local context = Milo:getContext() +local nodeWizard + local function saveConfig() local t = { } for k,v in pairs(context.config.nodes) do @@ -38,9 +40,6 @@ local networkPage = UI.Page { shadowText = 'filter', backgroundColor = colors.cyan, backgroundFocusColor = colors.cyan, - accelerators = { - [ 'enter' ] = 'eject', - }, }, grid = UI.ScrollingGrid { y = 2, ey = -3, @@ -91,12 +90,16 @@ end function networkPage:getList() for _, v in pairs(device) do - if v.pullItems then - if not context.config.nodes[v.name] then - context.config.nodes[v.name] = { - name = v.name, - mtype = 'ignore', - } + if not context.config.nodes[v.name] then + local node = { + name = v.name, + mtype = 'ignore', + } + for _, page in pairs(nodeWizard.wizard.pages) do + if page.isValidType and page:isValidType(node) then + context.config.nodes[v.name] = node + break + end end end end @@ -168,7 +171,7 @@ function networkPage:eventHandler(event) return true end -local nodeWizard = UI.Page { +nodeWizard = UI.Page { titleBar = UI.TitleBar { title = 'Configure' }, wizard = UI.Wizard { y = 2, ey = -2, @@ -185,16 +188,9 @@ local nodeWizard = UI.Page { limit = 64, pruneEmpty = true, }, [2] = UI.Chooser { - width = 15, + width = 25, formLabel = 'Type', formKey = 'mtype', - nochoice = 'Storage', - choices = { - { name = 'Storage', value = 'storage' }, - { name = 'Trashcan', value = 'trashcan' }, - { name = 'Input chest', value = 'input' }, - { name = 'Ignore', value = 'ignore' }, - { name = 'Machine', value = 'machine' }, - }, + --nochoice = 'Storage', help = 'Select type', }, }, @@ -401,36 +397,30 @@ function nodeWizard.wizard.pages.general.grid:getDisplayValues(row) end function nodeWizard.wizard.pages.general:validate() - return self.form:save() -end - ---[[ Wizard ]] -- -function nodeWizard.wizard:eventHandler(event) - if event.type == 'nextView' and - Util.find(self.pages, 'enabled', true) == self.pages.general then - - if self.pages.general.form:save() then - local index = 2 - for _, page in pairs(self.pages) do - page.index = nil - end - self.pages.general.index = 1 - self.pages.confirmation.index = 2 - - for _, page in pairs(self.pages) do - if not page.index and page:isValidFor(self.parent.node) then + if self.form:save() then + for _, page in pairs(nodeWizard.wizard.pages) do + page.index = nil + end + local index = 2 + nodeWizard.wizard.pages.general.index = 1 + nodeWizard.wizard.pages.confirmation.index = 2 + for _, page in pairs(nodeWizard.wizard.pages) do + if not page.index then + if not page.isValidFor or page:isValidFor(nodeWizard.node) then page.index = index index = index + 1 + if page.setNode then + page:setNode(nodeWizard.node) + end end end - self.pages.confirmation.index = index - return UI.Wizard.eventHandler(self, event) end - else - return UI.Wizard.eventHandler(self, event) + nodeWizard.wizard.pages.confirmation.index = index + return true end end +--[[ Wizard ]] -- function nodeWizard:enable(node) local adapter = node.adapter node.adapter = nil -- don't deep copy the adapter @@ -439,8 +429,21 @@ function nodeWizard:enable(node) node.adapter = adapter _G._p2 = self.node - self.wizard.pages.general.form:setValues(self.node) + + local choices = { + { name = 'Ignore', value = 'ignore' }, + } + for _, page in pairs(self.wizard.pages) do + if page.isValidType then + local choice = page:isValidType(self.node) + if choice then + table.insert(choices, choice) + end + end + end self.wizard.pages.general.form[1].shadowText = self.node.name + self.wizard.pages.general.form[2].choices = choices + self.wizard.pages.general.form:setValues(self.node) -- restore indices for _, page in pairs(self.wizard.pages) do @@ -451,12 +454,6 @@ _G._p2 = self.node end UI.Page.enable(self) - - for _, v in pairs(self.wizard.pages) do - if v.setNode then - v:setNode(self.node) - end - end end function nodeWizard:eventHandler(event) diff --git a/milo/plugins/activityView.lua b/milo/plugins/activityView.lua index 61ca2f0..7b48148 100644 --- a/milo/plugins/activityView.lua +++ b/milo/plugins/activityView.lua @@ -1,25 +1,47 @@ +local Ansi = require('ansi') local Event = require('event') local Milo = require('milo') -local Peripheral = require('peripheral') local UI = require('ui') local Util = require('util') -local colors = _G.colors -local context = Milo:getContext() -local mon = Peripheral.lookup(context.config.activityMonitor) +local colors = _G.colors +local context = Milo:getContext() +local device = _G.device +local monitor = context.storage:getSingleNode('activity') -local ActivityTask = { - name = 'activity', - priority = 30, +--[[ Configuration Page ]]-- +local template = +[[%sDisplays the amount of items entering or leaving storage%s + +Right-clicking on the activity monitor will reset the totals. + +%sMilo must be restarted to activate diplay. +]] + +local activityWizardPage = UI.Window { + title = 'Activity Monitor', + index = 2, + backgroundColor = colors.cyan, + [1] = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + value = string.format(template, Ansi.yellow, Ansi.reset, Ansi.orange), + }, } -if not mon then - return +function activityWizardPage:isValidType(node) + local m = device[node.name] + return m and m.type == 'monitor' and { name = 'Activity Monitor', value = 'activity' } end +function activityWizardPage:isValidFor(node) + return node.mtype == 'activity' +end + +UI:getPage('nodeWizard').wizard:add({ activity = activityWizardPage }) + local page = UI.Window { parent = UI.Device { - device = mon, + device = monitor.adapter, textScale = .5, }, grid = UI.Grid { @@ -130,17 +152,23 @@ Event.on({ 'storage_offline', 'storage_online' }, function() end) Event.on('monitor_touch', function(_, side) - if side == mon.side then + if side == monitor.adapter.side then page:reset() page:sync() end end) +page:draw() +page:sync() + +--[[ Task ]]-- +local ActivityTask = { + name = 'activity', + priority = 30, +} + function ActivityTask:cycle() page:update() end Milo:registerTask(ActivityTask) - -page:draw() -page:sync() diff --git a/milo/plugins/brewingStandView.lua b/milo/plugins/brewingStandView.lua index 332aa5e..018a8c3 100644 --- a/milo/plugins/brewingStandView.lua +++ b/milo/plugins/brewingStandView.lua @@ -16,7 +16,7 @@ When finished brewing, the recipe will be available upon refreshing. Note that you do not need to import items from the brewing stand, this will be done automatically.]] local brewingStandView = UI.Window { - title = 'Storage Options', + title = 'Brewing Stand', index = 2, backgroundColor = colors.cyan, [1] = UI.TextArea { @@ -25,11 +25,13 @@ 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' } +end + function brewingStandView:isValidFor(node) - if node.mtype == 'machine' then - local m = device[node.name] - return m and m.type == 'minecraft:brewing_stand' - end + return node.mtype == 'brewingStand' end UI:getPage('nodeWizard').wizard:add({ brewingStand = brewingStandView }) diff --git a/milo/plugins/exportView.lua b/milo/plugins/exportView.lua index 98d868d..201e8be 100644 --- a/milo/plugins/exportView.lua +++ b/milo/plugins/exportView.lua @@ -36,6 +36,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' } +end + function exportView:isValidFor(node) return node.mtype == 'machine' end diff --git a/milo/plugins/importView.lua b/milo/plugins/importView.lua index 06a7fcb..996736c 100644 --- a/milo/plugins/importView.lua +++ b/milo/plugins/importView.lua @@ -36,6 +36,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' } +end + function importView:isValidFor(node) return node.mtype == 'machine' end diff --git a/milo/plugins/inputChestView.lua b/milo/plugins/inputChestView.lua new file mode 100644 index 0000000..0177903 --- /dev/null +++ b/milo/plugins/inputChestView.lua @@ -0,0 +1,33 @@ +local Ansi = require('ansi') +local UI = require('ui') + +local colors = _G.colors +local device = _G.device + +--[[ Configuration Screen ]] +local template = +[[%sInput Chest%s + +Any items placed in this chest will be imported into storage. +]] + +local inputChestWizardPage = UI.Window { + title = 'Input Chest', + index = 2, + backgroundColor = colors.cyan, + [1] = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + value = string.format(template, Ansi.yellow, Ansi.reset), + }, +} + +function inputChestWizardPage:isValidType(node) + local m = device[node.name] + return m and m.pullItems and { name = 'Input Chest', value = 'input' } +end + +function inputChestWizardPage:isValidFor(node) + return node.mtype == 'input' +end + +UI:getPage('nodeWizard').wizard:add({ inputChest = inputChestWizardPage }) diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index 53411fe..bbd86ce 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -1,21 +1,53 @@ +local Ansi = require('ansi') local Craft = require('turtle.craft') local itemDB = require('itemDB') local Milo = require('milo') -local Peripheral = require('peripheral') local UI = require('ui') local Util = require('util') local colors = _G.colors +local context = Milo:getContext() +local device = _G.device +local monNode = context.storage:getSingleNode('jobs') -local context = Milo:getContext() -local mon = Peripheral.lookup(context.config.monitor) or - error('Monitor is not attached') +--[[ Configuration Screen ]] +local template = +[[%sDisplays the crafting progress%s + +%sMilo must be restarted to activate diplay. +]] + +local jobsWizardPage = UI.Window { + title = 'Crafting Monitor', + index = 2, + backgroundColor = colors.cyan, + [1] = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + value = string.format(template, Ansi.yellow, Ansi.reset, Ansi.orange), + }, +} + +function jobsWizardPage:isValidType(node) + local m = device[node.name] + return m and m.type == 'monitor' and { name = 'Crafting Monitor', value = 'jobs' } +end + +function jobsWizardPage:isValidFor(node) + return node.mtype == 'jobs' +end + +UI:getPage('nodeWizard').wizard:add({ jobs = jobsWizardPage }) + +--[[ Display ]] +if not monNode then + return +end -- TODO: some way to cancel a job local jobMonitor = UI.Page { parent = UI.Device { - device = mon, + device = monNode.adapter, textScale = .5, }, grid = UI.Grid { @@ -90,6 +122,7 @@ jobMonitor:enable() jobMonitor:draw() jobMonitor:sync() +--[[ Task ]] local jobMonitorTask = { name = 'job status', priority = 80, diff --git a/milo/plugins/manipulatorView.lua b/milo/plugins/manipulatorView.lua new file mode 100644 index 0000000..6fa7f76 --- /dev/null +++ b/milo/plugins/manipulatorView.lua @@ -0,0 +1,64 @@ +local Ansi = require('ansi') +local Milo = require('milo') +local Sync = require('sync') +local UI = require('ui') + +local colors = _G.colors +local device = _G.device +local turtle = _G.turtle + +--[[ Configuration Screen ]] +local template = +[[%sBound Manipulator%s + +Automatically import items into storage from your ender chest. +]] + +local wizardPage = UI.Window { + title = 'Manipulator', + index = 2, + backgroundColor = colors.cyan, + [1] = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + value = string.format(template, Ansi.yellow, Ansi.reset), + }, +} + +function wizardPage:isValidType(node) + local m = device[node.name] + return m and + m.type == 'manipulator' and + m.getEnder and + { name = 'Manipulator', value = 'manipulator' } +end + +function wizardPage:isValidFor(node) + return node.mtype == 'manipulator' +end + +UI:getPage('nodeWizard').wizard:add({ manipulator = wizardPage }) + +local task = { + name = 'manipulator', + priority = 15, +} + +function task:cycle(context) + local function filter(v) + return v.adapter.getEnder + end + + for manipulator in context.storage:filterActive('manipulator', filter) do + for slot, item in pairs(manipulator.adapter.getEnder().list()) do + Sync.sync(turtle, function() + manipulator.adapter.getEnder().pushItems( + context.localName, + slot, + item.count) + Milo:clearGrid() + end) + end + end +end + +Milo:registerTask(task) diff --git a/milo/plugins/redstoneView.lua b/milo/plugins/redstoneView.lua index 0ceb93a..07678a3 100644 --- a/milo/plugins/redstoneView.lua +++ b/milo/plugins/redstoneView.lua @@ -70,4 +70,4 @@ function dispenserView:setNode(node) self.form:setValues(node.redstone) end -UI:getPage('nodeWizard').wizard:add({ dispenser = dispenserView }) +--UI:getPage('nodeWizard').wizard:add({ dispenser = dispenserView }) diff --git a/milo/plugins/remote.lua b/milo/plugins/remote.lua index d01b5b4..0f522f8 100644 --- a/milo/plugins/remote.lua +++ b/milo/plugins/remote.lua @@ -29,7 +29,7 @@ local function client(socket) local manipulator = getManipulatorForUser(user) if not manipulator then - _debug('REMOTE: Manipulator with introspection module bound with user not found. Closing connection.') + _G._debug('REMOTE: Manipulator with introspection module bound with user not found. Closing connection.') socket:write({ msg = 'Manipulator not found' }) @@ -37,7 +37,7 @@ local function client(socket) return end - _debug('REMOTE: all good') + _G._debug('REMOTE: all good') socket:write({ data = 'ok', }) @@ -98,12 +98,14 @@ local function client(socket) request.requested, data.item) - turtle.eachFilledSlot(function(slot) - manipulator.getInventory().pullItems( - context.localName, - slot.index, - transferred) - end) + if transferred > 0 then + turtle.eachFilledSlot(function(slot) + manipulator.getInventory().pullItems( + context.localName, + slot.index, + slot.count) + end) + end end) end diff --git a/milo/plugins/storageView.lua b/milo/plugins/storageView.lua index e792cc3..70449b6 100644 --- a/milo/plugins/storageView.lua +++ b/milo/plugins/storageView.lua @@ -48,6 +48,11 @@ function storageView:validate() return self.form:save() end +function storageView:isValidType(node) + local m = device[node.name] + return m and m.pullItems and { name = 'Storage', value = 'storage' } +end + function storageView:isValidFor(node) return node.mtype == 'storage' end diff --git a/milo/plugins/trashcanView.lua b/milo/plugins/trashcanView.lua new file mode 100644 index 0000000..2b5547e --- /dev/null +++ b/milo/plugins/trashcanView.lua @@ -0,0 +1,35 @@ +local Ansi = require('ansi') +local UI = require('ui') + +local colors = _G.colors +local device = _G.device + +--[[ Configuration Screen ]] +local template = +[[%sUse this inventory as a trashcan%s + +If the number of items exceed the maximum value will be sent to this inventory. + +Any items that cannot fit into a locked chest will automatically be sent to this inventory. +]] + +local trashcanWizardPage = UI.Window { + title = 'Trashcan', + index = 2, + backgroundColor = colors.cyan, + [1] = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + value = string.format(template, Ansi.yellow, Ansi.reset), + }, +} + +function trashcanWizardPage:isValidType(node) + local m = device[node.name] + return m and m.pullItems and { name = 'Trashcan', value = 'trashcan' } +end + +function trashcanWizardPage:isValidFor(node) + return node.mtype == 'trashcan' +end + +UI:getPage('nodeWizard').wizard:add({ trashcan = trashcanWizardPage })