From 9f9dcafc607800bbf1d3abcfedb5ddf16af51998 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Thu, 25 Oct 2018 05:51:46 -0400 Subject: [PATCH] milo wip --- milo/Milo.lua | 8 +- milo/apis/milo.lua | 224 ++++--------------------------- milo/apis/networkedAdapter18.lua | 6 +- milo/apis/turtle/craft.lua | 103 ++++++++++---- milo/plugins/craftTask.lua | 141 +++++++++++++++++++ milo/plugins/demandCraft.lua | 57 +------- milo/plugins/jobList.lua | 25 +++- milo/plugins/replenishTask.lua | 14 +- 8 files changed, 275 insertions(+), 303 deletions(-) create mode 100644 milo/plugins/craftTask.lua diff --git a/milo/Milo.lua b/milo/Milo.lua index 8e26af9..1f1e736 100644 --- a/milo/Milo.lua +++ b/milo/Milo.lua @@ -109,6 +109,8 @@ local context = { learnTypes = { }, machineTypes = { }, localName = modem.getNameLocal(), + tasks = { }, + craftingQueue = { }, } local function initStorage(detachedDevice) @@ -180,12 +182,12 @@ local programDir = fs.getDir(shell.getRunningProgram()) loadDirectory(fs.combine(programDir, 'core')) loadDirectory(fs.combine(programDir, 'plugins')) -table.sort(Milo.tasks, function(a, b) +table.sort(context.tasks, function(a, b) return a.priority < b.priority end) debug('Tasks\n-----') -for _, task in ipairs(Milo.tasks) do +for _, task in ipairs(context.tasks) do debug('%d: %s', task.priority, task.name) end @@ -200,7 +202,7 @@ Event.onInterval(5, function() Milo:resetCraftingStatus() context.inventoryAdapter:refresh() - for _, task in ipairs(Milo.tasks) do + for _, task in ipairs(context.tasks) do local s, m = pcall(function() task:cycle(context) end) if not s and m then Util.print(task) diff --git a/milo/apis/milo.lua b/milo/apis/milo.lua index f506a3b..6c18d5e 100644 --- a/milo/apis/milo.lua +++ b/milo/apis/milo.lua @@ -3,7 +3,6 @@ local Craft = require('turtle.craft') local itemDB = require('itemDB') local Util = require('util') -local os = _G.os local turtle = _G.turtle local Milo = { @@ -13,9 +12,6 @@ local Milo = { STATUS_INFO = 'info', STATUS_WARNING = 'warning', STATUS_ERROR = 'error', - - tasks = { }, - craftingStatus = { }, } function Milo:init(context) @@ -26,6 +22,18 @@ function Milo:getContext() return self.context end +function Milo:requestCrafting(item) + local key = Milo:uniqueKey(item) + + if not self.context.craftingQueue[key] then + item.processing = { } + item.requested = item.count + item.crafted = 0 + + self.context.craftingQueue[key] = item + end +end + function Milo:pauseCrafting() self.craftingPaused = true Milo:showError('Crafting Paused') @@ -60,23 +68,25 @@ function Milo:uniqueKey(item) return table.concat({ item.name, item.damage, item.nbtHash }, ':') end -function Milo:getCraftingStatus() - return self.craftingStatus -end - function Milo:resetCraftingStatus() - self.craftingStatus = { } - self.context.inventoryAdapter.activity = { } -end -function Milo:updateCraftingStatus(list) - for k,v in pairs(list) do - self.craftingStatus[k] = v + -- todo: move to end of processing tasks ? + -- what if someone hoppers in items ? -- this shouldnt be allowed + -- all items must come in via pullItems + self.context.inventoryAdapter.activity = { } + + for _,key in pairs(Util.keys(self.context.craftingQueue)) do + local item = self.context.craftingQueue[key] + if item.crafted >= item.requested then + debug('removing:') + debug(item) + self.context.craftingQueue[key] = nil + end end end function Milo:registerTask(task) - table.insert(self.tasks, task) + table.insert(self.context.tasks, task) end function Milo:showError(msg) @@ -194,190 +204,8 @@ end -- Return a list of everything in the system function Milo:listItems() - for _ = 1, 5 do - self.items = self.context.inventoryAdapter:listItems() - if self.items then - break - end --- jobList:showError('Error - retrying in 3 seconds') - os.sleep(3) - end - if not self.items then - self:showError('Error - rebooting in 5 seconds') - end - + self.items = self.context.inventoryAdapter:listItems() return self.items end -function Milo:addCraftingRequest(item, craftList, count) - local key = self:uniqueKey(item) - local request = craftList[key] - if not craftList[key] then - request = { name = item.name, damage = item.damage, nbtHash = item.nbtHash, count = 0 } - request.displayName = itemDB:getName(request) - craftList[key] = request - end - request.count = request.count + count - return request -end - --- Craft -function Milo:craftItem(recipe, items, originalItem, craftList, count) - local missing = { } - local toCraft = Craft.getCraftableAmount(recipe, count, items, missing) - if missing.name then - originalItem.status = string.format('%s missing', itemDB:getName(missing.name)) - originalItem.statusCode = self.STATUS_WARNING - end - - local crafted = 0 - - if toCraft > 0 then - crafted = Craft.craftRecipe(recipe, toCraft, self.context.inventoryAdapter) - self:clearGrid() - items = self:listItems() - count = count - crafted - end - - if count > 0 and items then - local ingredients = Craft.getResourceList4(recipe, items, count) - for _,ingredient in pairs(ingredients) do - if ingredient.need > 0 then - local item = self:addCraftingRequest(ingredient, craftList, ingredient.need) - if Craft.findRecipe(item) then - item.status = string.format('%s missing', itemDB:getName(ingredient)) - item.statusCode = self.STATUS_WARNING - else - item.status = 'no recipe' - item.statusCode = self.STATUS_ERROR - end - end - end - end - return crafted -end - --- Craft as much as possible regardless if all ingredients are available -function Milo:forceCraftItem(inRecipe, items, originalItem, craftList, inCount) - local summed = { } - local throttle = Util.throttle() - - local function sumItems(recipe, count) - count = math.ceil(count / recipe.count) - local craftable = count - - for key,iqty in pairs(Craft.sumIngredients(recipe)) do - throttle() - local item = itemDB:splitKey(key) - local summedItem = summed[key] - if not summedItem then - summedItem = Util.shallowCopy(item) - summedItem.recipe = Craft.findRecipe(item) - summedItem.count = Craft.getItemCount(items, key) - summedItem.need = 0 - summedItem.used = 0 - summedItem.craftable = 0 - summed[key] = summedItem - end - - local total = count * iqty -- 4 * 2 - local used = math.min(summedItem.count, total) -- 5 - local need = total - used -- 3 - - if recipe.craftingTools and recipe.craftingTools[key] then - if summedItem.count > 0 then - summedItem.used = 1 - summedItem.need = 0 - need = 0 - elseif not summedItem.recipe then - summedItem.need = 1 - need = 1 - else - need = 1 - end - else - summedItem.count = summedItem.count - used - summedItem.used = summedItem.used + used - end - - if need > 0 then - if not summedItem.recipe then - craftable = math.min(craftable, math.floor(used / iqty)) - summedItem.need = summedItem.need + need - else - local c = sumItems(summedItem.recipe, need) -- 4 - craftable = math.min(craftable, math.floor((used + c) / iqty)) - summedItem.craftable = summedItem.craftable + c - end - end - end - if craftable > 0 then - craftable = Craft.craftRecipe(recipe, craftable * recipe.count, - self.context.inventoryAdapter) / recipe.count - self:clearGrid() - end - - return craftable * recipe.count - end - - local count = sumItems(inRecipe, inCount) - - if count < inCount then - for _,ingredient in pairs(summed) do - if ingredient.need > 0 then - local item = self:addCraftingRequest(ingredient, craftList, ingredient.need) - if Craft.findRecipe(item) then - item.status = string.format('%s missing', itemDB:getName(ingredient)) - item.statusCode = self.STATUS_WARNING - else - item.status = '(no recipe)' - item.statusCode = self.STATUS_ERROR - end - end - end - end - return count -end - -function Milo:craft(recipe, items, item, craftList) - item.status = nil - item.statusCode = nil - item.crafted = 0 - - if self:isCraftingPaused() then - return - end - - if not self:clearGrid() then - item.status = 'Grid obstructed' - item.statusCode = self.STATUS_ERROR - return - end - - if item.forceCrafting then - item.crafted = self:forceCraftItem(recipe, items, item, craftList, item.count) - else - item.crafted = self:craftItem(recipe, items, item, craftList, item.count) - end -end - -function Milo:craftItems(craftList) - for _,key in pairs(Util.keys(craftList)) do - local item = craftList[key] - if item.count > 0 then - local recipe = Craft.recipes[key] - if recipe then - self:craft(recipe, self:listItems(), item, craftList) - elseif not self.context.controllerAdapter then - item.status = '(no recipe)' - item.statusCode = self.STATUS_ERROR - end - end - end - self:updateCraftingStatus(craftList) - for _,v in pairs(craftList) do - --debug(v) - end -end - return Milo diff --git a/milo/apis/networkedAdapter18.lua b/milo/apis/networkedAdapter18.lua index 54546c5..64ab686 100644 --- a/milo/apis/networkedAdapter18.lua +++ b/milo/apis/networkedAdapter18.lua @@ -67,17 +67,13 @@ self.listCount = self.listCount + 1 --debug(self.listCount) -- todo: only listItems from dirty remotes - -- todo: better handling of empty inventories local cache = { } local items = { } throttle = throttle or Util.throttle() for _, remote in pairs(self.remotes) do - if not remote:listItems(throttle) then - debug('no List: ' .. remote.name) - --error('Listing failed: ' .. remote.name) - end + remote:listItems(throttle) local rcache = remote.cache or { } -- TODO: add a method in each adapter that only updates a passed cache diff --git a/milo/apis/turtle/craft.lua b/milo/apis/turtle/craft.lua index 9e1b1a0..fb5cb4b 100644 --- a/milo/apis/turtle/craft.lua +++ b/milo/apis/turtle/craft.lua @@ -12,17 +12,17 @@ local MACHINE_LOOKUP = 'usr/config/machine_crafting.db' local Craft = { } local function clearGrid(inventoryAdapter) - turtle.eachFilledSlot(function(slot) - inventoryAdapter:insert(slot.index, slot.count, nil, slot) - end) + turtle.eachFilledSlot(function(slot) + inventoryAdapter:insert(slot.index, slot.count, nil, slot) + end) - for i = 1, 16 do - if turtle.getItemCount(i) ~= 0 then - return false - end - end + for i = 1, 16 do + if turtle.getItemCount(i) ~= 0 then + return false + end + end - return true + return true end local function splitKey(key) @@ -55,29 +55,57 @@ function Craft.getItemCount(items, item) return count end -local function machineCraft(recipe, qty, inventoryAdapter, machineName) +local function machineCraft(recipe, qty, inventoryAdapter, machineName, oitem) + local key = recipe.result + local request = oitem.processing[key] + if request then + request.crafted = request.crafted + (inventoryAdapter.activity[key] or 0) + if request.crafted >= request.requested then + oitem.processing[key] = nil -- TODO: check... + return true + end + return + end + local machine = device[machineName] if not machine then debug('machine not found') + oitem.processing[recipe.result] = { + status = 'machine not found' + } else for k in pairs(recipe.ingredients) do if machine.getItemMeta(k) then + oitem.processing[recipe.result] = { + status = 'machine in use' + } debug('machine in use: ' .. k) - return false end end + debug('processing ' .. recipe.result) for k,v in pairs(recipe.ingredients) do - inventoryAdapter:provide(splitKey(v), qty, k, machineName) + if inventoryAdapter:provide(splitKey(v), qty, k, machineName) ~= qty then + -- TODO: suck em back out + oitem.processing[recipe.result] = { + status = 'unknown error' + } + end end + oitem.processing[recipe.result] = { + status = 'processing', + requested = qty, + crafted = 0, + } end - - return false end -local function turtleCraft(recipe, qty, inventoryAdapter) +local function turtleCraft(recipe, qty, inventoryAdapter, oitem) if not clearGrid(inventoryAdapter) then - return false + oitem.processing[recipe.result] = { + status = 'grid in use', + } + return end for k,v in pairs(recipe.ingredients) do @@ -90,15 +118,22 @@ local function turtleCraft(recipe, qty, inventoryAdapter) provideQty = 1 end ]]-- - inventoryAdapter:provide(item, provideQty, k) - if turtle.getItemCount(k) == 0 then -- ~= qty then + if inventoryAdapter:provide(item, provideQty, k) ~= provideQty then -- FIX: ingredients cannot be stacked --debug('failed ' .. v .. ' - ' .. provideQty) - return false + oitem.processing[recipe.result] = { + status = 'unknown error', + } + return end end - return turtle.craft() + if turtle.craft() then + return true + end + oitem.processing[recipe.result] = { + status = 'processing', + } end function Craft.loadRecipes() @@ -139,16 +174,22 @@ local function makeRecipeKey(item) return table.concat({ item.name, item.damage or 0, item.nbtHash }, ':') end -function Craft.craftRecipe(recipe, count, inventoryAdapter) +function Craft.craftRecipe(recipe, count, inventoryAdapter, origItem) if type(recipe) == 'string' then recipe = Craft.recipes[recipe] if not recipe then + origItem.processing[recipe.result] = { + status = 'no recipe', + } return 0, 'No recipe' end end local items = inventoryAdapter:listItems() if not items then + origItem.processing[recipe.result] = { + status = 'Inventory changed', + } return 0, 'Inventory changed' end @@ -166,7 +207,7 @@ function Craft.craftRecipe(recipe, count, inventoryAdapter) local irecipe = Craft.findRecipe(key) if irecipe then local iqty = need - itemCount - local crafted = Craft.craftRecipe(irecipe, iqty, inventoryAdapter) + local crafted = Craft.craftRecipe(irecipe, iqty, inventoryAdapter, origItem) if crafted ~= iqty then turtle.select(1) return 0 @@ -177,15 +218,19 @@ function Craft.craftRecipe(recipe, count, inventoryAdapter) local crafted = 0 repeat --- fix + local requested = math.min(count, maxCount) + if Craft.machineLookup[recipe.result] then - machineCraft(recipe, math.min(count, maxCount), inventoryAdapter, Craft.machineLookup[recipe.result]) - break - elseif not turtleCraft(recipe, math.min(count, maxCount), inventoryAdapter) then - turtle.select(1) - break + if not machineCraft(recipe, requested, inventoryAdapter, Craft.machineLookup[recipe.result], origItem) then + break + end + else + if not turtleCraft(recipe, requested, inventoryAdapter) then + break + end end - crafted = crafted + math.min(count, maxCount) + + crafted = crafted + requested count = count - maxCount until count <= 0 diff --git a/milo/plugins/craftTask.lua b/milo/plugins/craftTask.lua new file mode 100644 index 0000000..94bb712 --- /dev/null +++ b/milo/plugins/craftTask.lua @@ -0,0 +1,141 @@ +local Craft = require('turtle.craft') +local itemDB = require('itemDB') +local Milo = require('milo') +local Util = require('util') + +local context = Milo:getContext() + +local craftTask = { + name = 'crafting', + priority = 70, +} + +-- Craft +function craftTask:craftItem(recipe, originalItem, count) + local missing = { } + local toCraft = Craft.getCraftableAmount(recipe, count, Milo:listItems(), missing) + if missing.name then + originalItem.status = string.format('%s missing', itemDB:getName(missing.name)) + originalItem.statusCode = Milo.STATUS_WARNING + end + + local crafted = 0 + + if toCraft > 0 then + crafted = Craft.craftRecipe(recipe, toCraft, context.inventoryAdapter, originalItem) + Milo:clearGrid() + end + + return crafted +end + +-- Craft as much as possible regardless if all ingredients are available +function craftTask:forceCraftItem(inRecipe, originalItem, inCount) + local summed = { } + local items = Milo:listItems() + local throttle = Util.throttle() + + local function sumItems(recipe, count) + count = math.ceil(count / recipe.count) + local craftable = count + + for key,iqty in pairs(Craft.sumIngredients(recipe)) do + throttle() + local item = itemDB:splitKey(key) + local summedItem = summed[key] + if not summedItem then + summedItem = Util.shallowCopy(item) + summedItem.recipe = Craft.findRecipe(item) + summedItem.count = Craft.getItemCount(items, key) + summedItem.need = 0 + summedItem.used = 0 + summedItem.craftable = 0 + summed[key] = summedItem + end + + local total = count * iqty -- 4 * 2 + local used = math.min(summedItem.count, total) -- 5 + local need = total - used -- 3 + + if recipe.craftingTools and recipe.craftingTools[key] then + if summedItem.count > 0 then + summedItem.used = 1 + summedItem.need = 0 + need = 0 + elseif not summedItem.recipe then + summedItem.need = 1 + need = 1 + else + need = 1 + end + else + summedItem.count = summedItem.count - used + summedItem.used = summedItem.used + used + end + + if need > 0 then + if not summedItem.recipe then + craftable = math.min(craftable, math.floor(used / iqty)) + summedItem.need = summedItem.need + need + else + local c = sumItems(summedItem.recipe, need) -- 4 + craftable = math.min(craftable, math.floor((used + c) / iqty)) + summedItem.craftable = summedItem.craftable + c + end + end + end + if craftable > 0 then + craftable = Craft.craftRecipe(recipe, craftable * recipe.count, + context.inventoryAdapter, originalItem) / recipe.count + Milo:clearGrid() + end + + return craftable * recipe.count + end + + return sumItems(inRecipe, inCount) +end + +function craftTask:craft(recipe, item) + item.status = nil + item.statusCode = nil + item.crafted = 0 + + if Milo:isCraftingPaused() then + return + end + + -- todo: is this needed ? + if not Milo:clearGrid() then + item.status = 'Grid obstructed' + item.statusCode = Milo.STATUS_ERROR + return + end + + if item.forceCrafting then + item.crafted = self:forceCraftItem(recipe, item, item.count) + else + item.crafted = self:craftItem(recipe, item, item.count) + end +end + +function craftTask:cycle() + for _,key in pairs(Util.keys(context.craftingQueue)) do + local item = context.craftingQueue[key] + if item.count > 0 then + local recipe = Craft.recipes[key] + if recipe then + self:craft(recipe, item) + if item.eject and item.crafted >= item.requested then + Milo:eject(item, item.requested) + end + elseif not context.controllerAdapter then + item.status = '(no recipe)' + item.statusCode = Milo.STATUS_ERROR + item.crafted = 0 + end + end + end +end + +Milo:registerTask(craftTask) \ No newline at end of file diff --git a/milo/plugins/demandCraft.lua b/milo/plugins/demandCraft.lua index fbb5d7c..8bde995 100644 --- a/milo/plugins/demandCraft.lua +++ b/milo/plugins/demandCraft.lua @@ -6,8 +6,6 @@ local Util = require('util') local colors = _G.colors -local demandCrafting = { } - local craftPage = UI.Page { titleBar = UI.TitleBar { }, wizard = UI.Wizard { @@ -118,12 +116,11 @@ function craftPage:eventHandler(event) UI:setPreviousPage() elseif event.type == 'accept' then - local key = Milo:uniqueKey(self.item) - demandCrafting[key] = Util.shallowCopy(self.item) - demandCrafting[key].count = tonumber(self.wizard.pages.quantity.count.value) - demandCrafting[key].ocount = demandCrafting[key].count - demandCrafting[key].forceCrafting = true - demandCrafting[key].eject = self.wizard.pages.quantity.eject.value == true + local item = Util.shallowCopy(self.item) + item.count = tonumber(self.wizard.pages.quantity.count.value) + item.forceCrafting = true + item.eject = self.wizard.pages.quantity.eject.value == true + Milo:requestCrafting(item) UI:setPreviousPage() else return UI.Page.eventHandler(self, event) @@ -131,48 +128,4 @@ function craftPage:eventHandler(event) return true end -local demandCraftingTask = { - name = 'demand crafting', - priority = 60, -} - -function demandCraftingTask:cycle(context) - local demandCrafted = { } - - -- look directly at the adapter import activity to determine - -- if the item was imported into storage from any source. - -- The item does NOT need to come from the machine that did - -- the crafting. - for _,key in pairs(Util.keys(demandCrafting)) do - local item = demandCrafting[key] - - local imported = context.inventoryAdapter.activity[key] - if imported then - item.crafted = math.min(imported, item.count) - item.count = math.max(0, item.count - item.crafted) - context.inventoryAdapter.activity[key] = imported - item.crafted - end - demandCrafted[key] = item - end - - if Util.size(demandCrafted) > 0 then - Milo:craftItems(demandCrafted) - end - - for _,key in pairs(Util.keys(demandCrafting)) do - local item = demandCrafting[key] - if item.crafted then - item.count = math.max(0, item.count - item.crafted) - if item.count <= 0 then - item.statusCode = 'success' - demandCrafting[key] = nil - if item.eject then - Milo:eject(item, item.ocount) - end - end - end - end -end - UI:addPage('craft', craftPage) -Milo:registerTask(demandCraftingTask) diff --git a/milo/plugins/jobList.lua b/milo/plugins/jobList.lua index 6e0f82b..a5d01d9 100644 --- a/milo/plugins/jobList.lua +++ b/milo/plugins/jobList.lua @@ -15,12 +15,14 @@ local display = UI.Device { local jobList = UI.Page { parent = display, grid = UI.Grid { - sortColumn = 'displayName', + sortColumn = 'index', backgroundFocusColor = colors.black, columns = { { heading = 'Qty', key = 'count', width = 6 }, - { heading = 'Crafting', key = 'displayName', width = display.width / 2 - 10 }, - { heading = 'Status', key = 'status', width = display.width - 10 }, + { heading = 'Crafting', key = 'displayName', }, -- width = display.width / 2 - 10 }, + { heading = 'Status', key = 'status', }, -- width = display.width - 10 }, + { heading = 'Req', key = 'requested', width = 6 }, + { heading = 'Cra', key = 'crafted', width = 6 }, }, }, } @@ -33,7 +35,20 @@ end function jobList:updateList(craftList) if not Milo:isCraftingPaused() then - self.grid:setValues(craftList) + local t = { } + local index = 1 + for k,v in pairs(craftList) do + t[k] = v + v.index = index + index = index + 1 + for k2,v2 in pairs(v.processing) do + t[k2] = v2 + v2.displayName = k2 + v2.index = index + index = index + 1 + end + end + self.grid:setValues(t) self.grid:update() self:draw() self:sync() @@ -61,7 +76,7 @@ local JobListTask = { } function JobListTask:cycle() - jobList:updateList(Milo:getCraftingStatus()) + jobList:updateList(context.craftingQueue) end Milo:registerTask(JobListTask) diff --git a/milo/plugins/replenishTask.lua b/milo/plugins/replenishTask.lua index 729a83c..d204245 100644 --- a/milo/plugins/replenishTask.lua +++ b/milo/plugins/replenishTask.lua @@ -3,12 +3,10 @@ local Milo = require('milo') local ReplenishTask = { name = 'replenish', - priority = 70, + priority = 60, } function ReplenishTask:cycle(context) - local craftList = { } - for _,res in pairs(context.resources) do if res.low then local item = Milo:getItemWithQty(res, res.ignoreDamage, res.ignoreNbtHash) @@ -26,22 +24,16 @@ function ReplenishTask:cycle(context) if res.ignoreDamage then item.damage = 0 end - local key = Milo:uniqueKey(res) - - craftList[key] = { + Milo:requestCrafting({ damage = item.damage, nbtHash = item.nbtHash, count = res.low - item.count, name = item.name, displayName = item.displayName, - status = '', - rsControl = res.rsControl, - } + }) end end end - - Milo:craftItems(craftList) end Milo:registerTask(ReplenishTask)