package management
This commit is contained in:
9
pickup/.package
Normal file
9
pickup/.package
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
required = {
|
||||
'opus-develop-1.8',
|
||||
},
|
||||
title = 'Move resources around with turtles',
|
||||
repository = 'kepler155c/opus-apps/develop1.8/pickup',
|
||||
description = [[ ... ]],
|
||||
licence = 'MIT',
|
||||
}
|
||||
321
pickup/pickup.lua
Normal file
321
pickup/pickup.lua
Normal file
@@ -0,0 +1,321 @@
|
||||
_G.requireInjector()
|
||||
|
||||
local Event = require('event')
|
||||
local ChestAdapter = require('chestAdapter18')
|
||||
local Point = require('point')
|
||||
local Socket = require('socket')
|
||||
local Util = require('util')
|
||||
|
||||
local device = _G.device
|
||||
local os = _G.os
|
||||
local peripheral = _G.peripheral
|
||||
local printError = _G.printError
|
||||
local turtle = _G.turtle
|
||||
|
||||
if not turtle then
|
||||
error('Can only be run on a turtle')
|
||||
end
|
||||
|
||||
local blocks = { }
|
||||
local items = { }
|
||||
|
||||
local locations = Util.readTable('/usr/config/pickup') or {
|
||||
pickups = { },
|
||||
cells = { },
|
||||
refills = { },
|
||||
fluids = { },
|
||||
}
|
||||
|
||||
local fuel = {
|
||||
item = {
|
||||
name = 'minecraft:coal',
|
||||
damage = 0,
|
||||
},
|
||||
qty = 64
|
||||
}
|
||||
|
||||
local slots
|
||||
|
||||
turtle.setMoveCallback(function()
|
||||
if slots then
|
||||
for _,slot in pairs(slots) do
|
||||
if turtle.getItemCount(slot.index) ~= slot.qty then
|
||||
printError('Slots changed')
|
||||
Event.exitPullEvents()
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local function gotoPoint(pt, doDetect)
|
||||
slots = turtle.getInventory()
|
||||
while not turtle.pathfind(pt, { blocks = blocks }) do
|
||||
if turtle.isAborted() then
|
||||
error('aborted')
|
||||
end
|
||||
turtle.setStatus('blocked')
|
||||
os.sleep(5)
|
||||
end
|
||||
|
||||
if doDetect and not turtle.detectDown() then
|
||||
printError('Missing target')
|
||||
Event.exitPullEvents()
|
||||
end
|
||||
end
|
||||
|
||||
local function dropOff(pt)
|
||||
if turtle.selectSlotWithItems() then
|
||||
gotoPoint(pt, true)
|
||||
turtle.emptyInventory(turtle.dropDown)
|
||||
if pt == locations.dropPt then
|
||||
print('refreshing items')
|
||||
local chestAdapter = ChestAdapter()
|
||||
items = chestAdapter:refresh()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function refuel()
|
||||
if turtle.getFuelLevel() < 5000 and locations.dropPt then
|
||||
print('refueling')
|
||||
turtle.setStatus('refueling')
|
||||
gotoPoint(locations.dropPt, true)
|
||||
dropOff(locations.dropPt)
|
||||
local chestAdapter = ChestAdapter({
|
||||
wrapSide = 'bottom',
|
||||
direction = 'up',
|
||||
})
|
||||
while turtle.getFuelLevel() < 5000 do
|
||||
turtle.select(1)
|
||||
chestAdapter:provide(fuel.item, fuel.qty, 1)
|
||||
turtle.refuel(64)
|
||||
print(turtle.getFuelLevel())
|
||||
os.sleep(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function pickUp(pt)
|
||||
turtle.setStatus('picking up')
|
||||
gotoPoint(pt, true)
|
||||
while true do
|
||||
if not turtle.selectOpenSlot() then
|
||||
dropOff(locations.dropPt)
|
||||
gotoPoint(pt, true)
|
||||
end
|
||||
turtle.select(1)
|
||||
if not turtle.suckDown(64) then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function checkCell(pt)
|
||||
if not turtle.selectOpenSlot() then
|
||||
dropOff(locations.dropPt)
|
||||
end
|
||||
|
||||
print('checking cell')
|
||||
turtle.setStatus('recharging')
|
||||
gotoPoint(pt, true)
|
||||
local c = peripheral.wrap('bottom')
|
||||
local energy = c.getMaxEnergyStored() -
|
||||
c.getEnergyStored()
|
||||
if energy > 20000 then
|
||||
print('charging cell')
|
||||
turtle.selectOpenSlot()
|
||||
turtle.digDown()
|
||||
gotoPoint(locations.chargePt, true)
|
||||
turtle.dropDown()
|
||||
os.sleep(energy / 20000)
|
||||
turtle.suckDown()
|
||||
print('replacing cell')
|
||||
gotoPoint(pt)
|
||||
if not turtle.placeDown() then
|
||||
error('could not place down cell')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function fluid(points)
|
||||
print('checking fluid')
|
||||
turtle.setStatus('fluiding')
|
||||
gotoPoint(points.source, true)
|
||||
turtle.select(1)
|
||||
turtle.digDown()
|
||||
gotoPoint(points.target)
|
||||
if not turtle.placeDown() then
|
||||
error('could not place fluid container')
|
||||
end
|
||||
os.sleep(5)
|
||||
turtle.digDown()
|
||||
gotoPoint(points.source)
|
||||
turtle.placeDown()
|
||||
end
|
||||
|
||||
local function refill(entry)
|
||||
dropOff(locations.dropPt)
|
||||
|
||||
turtle.setStatus('refilling')
|
||||
gotoPoint(locations.dropPt)
|
||||
local chestAdapter = ChestAdapter()
|
||||
for _,item in pairs(entry.items) do
|
||||
chestAdapter:provide(item, tonumber(item.qty), turtle.selectOpenSlot())
|
||||
end
|
||||
|
||||
if turtle.selectSlotWithItems() then
|
||||
if entry.point then
|
||||
dropOff(entry.point)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function makeKey(pt)
|
||||
return string.format('%d:%d:%d', pt.x, pt.y, pt.z)
|
||||
end
|
||||
|
||||
local function pickupHost(socket)
|
||||
|
||||
while true do
|
||||
local data = socket:read()
|
||||
if not data then
|
||||
print('pickup: closing connection to ' .. socket.dhost)
|
||||
return
|
||||
end
|
||||
|
||||
print('command: ' .. data.type)
|
||||
|
||||
if data.type == 'pickup' then
|
||||
local key = makeKey(data.point)
|
||||
locations.pickups[key] = data.point
|
||||
Util.writeTable('/usr/config/pickup', locations)
|
||||
socket:write( { type = "response", response = 'added' })
|
||||
|
||||
elseif data.type == 'items' then
|
||||
socket:write( { type = "response", response = items })
|
||||
|
||||
elseif data.type == 'refill' then
|
||||
local key = makeKey(data.entry.point)
|
||||
locations.refills[key] = data.entry
|
||||
Util.writeTable('/usr/config/pickup', locations)
|
||||
socket:write( { type = "response", response = 'added' })
|
||||
|
||||
elseif data.type == 'setPickup' then
|
||||
locations.dropPt = data.point
|
||||
Util.writeTable('/usr/config/pickup', locations)
|
||||
socket:write( { type = "response", response = 'Location set' })
|
||||
|
||||
elseif data.type == 'setRecharge' then
|
||||
locations.chargePt = data.point
|
||||
Util.writeTable('/usr/config/pickup', locations)
|
||||
socket:write( { type = "response", response = 'Location set' })
|
||||
|
||||
elseif data.type == 'charge' then
|
||||
local key = makeKey(data.point)
|
||||
locations.cells[key] = data.point
|
||||
Util.writeTable('/usr/config/pickup', locations)
|
||||
socket:write( { type = "response", response = 'added' })
|
||||
|
||||
elseif data.type == 'clear' then
|
||||
local key = makeKey(data.point)
|
||||
locations.refills[key] = nil
|
||||
locations.cells[key] = nil
|
||||
locations.fluids[key] = nil
|
||||
locations.pickups[key] = nil
|
||||
|
||||
Util.writeTable('/usr/config/pickup', locations)
|
||||
|
||||
socket:write( { type = "response", response = 'cleared' })
|
||||
else
|
||||
print('unknown command')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if device.wireless_modem then
|
||||
Event.addRoutine(function()
|
||||
while true do
|
||||
print('waiting for connection on port 5222')
|
||||
local socket = Socket.server(5222)
|
||||
|
||||
print('pickup: connection from ' .. socket.dhost)
|
||||
|
||||
Event.addRoutine(function() pickupHost(socket) end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function eachEntry(t, fn)
|
||||
|
||||
local keys = Util.keys(t)
|
||||
for _,key in pairs(keys) do
|
||||
if t[key] then
|
||||
if turtle.isAborted() then
|
||||
return
|
||||
end
|
||||
fn(t[key])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function eachClosestEntry(t, fn)
|
||||
|
||||
local points = { }
|
||||
for k,v in pairs(t) do
|
||||
v = Util.shallowCopy(v)
|
||||
v.key = k
|
||||
table.insert(points, v)
|
||||
end
|
||||
|
||||
while not Util.empty(points) do
|
||||
local closest = Point.closest(turtle.point, points)
|
||||
if turtle.isAborted() then
|
||||
return
|
||||
end
|
||||
if t[closest.key] then
|
||||
fn(closest)
|
||||
end
|
||||
for k,v in pairs(points) do
|
||||
if v.key == closest.key then
|
||||
table.remove(points, k)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Event.addRoutine(function()
|
||||
if not turtle.enableGPS() then
|
||||
error('turtle: No GPS found')
|
||||
end
|
||||
|
||||
refuel()
|
||||
|
||||
while true do
|
||||
if locations.dropPt then
|
||||
eachClosestEntry(locations.pickups, pickUp)
|
||||
eachEntry(locations.refills, refill)
|
||||
refuel()
|
||||
end
|
||||
dropOff(locations.dropPt)
|
||||
eachEntry(locations.fluids, fluid)
|
||||
if locations.chargePt then
|
||||
eachEntry(locations.cells, checkCell)
|
||||
end
|
||||
print('sleeping')
|
||||
turtle.setStatus('sleeping')
|
||||
if turtle.isAborted() then
|
||||
printError('aborted')
|
||||
break
|
||||
end
|
||||
os.sleep(60)
|
||||
end
|
||||
|
||||
Event.exitPullEvents()
|
||||
end)
|
||||
|
||||
turtle.run(function()
|
||||
|
||||
Event.pullEvents()
|
||||
|
||||
end)
|
||||
233
pickup/pickupRemote.lua
Normal file
233
pickup/pickupRemote.lua
Normal file
@@ -0,0 +1,233 @@
|
||||
if not device.wireless_modem then
|
||||
error('Wireless modem is required')
|
||||
end
|
||||
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local Event = require('event')
|
||||
local GPS = require('gps')
|
||||
local Socket = require('socket')
|
||||
local UI = require('ui')
|
||||
local Util = require('util')
|
||||
|
||||
multishell.setTitle(multishell.getCurrent(), 'Pickup Remote')
|
||||
|
||||
local id
|
||||
|
||||
local mainPage = UI.Page({
|
||||
menu = UI.Menu({
|
||||
centered = true,
|
||||
y = 2,
|
||||
height = 8,
|
||||
menuItems = {
|
||||
{ prompt = 'Pickup', event = 'pickup', help = 'Pickup items from this location' },
|
||||
{ prompt = 'Charge cell', event = 'charge', help = 'Recharge this cell' },
|
||||
{ prompt = 'Refill', event = 'refill', help = 'Recharge this cell' },
|
||||
{ prompt = 'Set drop off location', event = 'setPickup', help = 'Recharge this cell' },
|
||||
{ prompt = 'Set recharge location', event = 'setRecharge', help = 'Recharge this cell' },
|
||||
{ prompt = 'Clear', event = 'clear', help = 'Remove this location' },
|
||||
},
|
||||
}),
|
||||
statusBar = UI.StatusBar(),
|
||||
accelerators = {
|
||||
q = 'quit',
|
||||
},
|
||||
})
|
||||
|
||||
local refillPage = UI.Page({
|
||||
menuBar = UI.MenuBar({
|
||||
y = 1,
|
||||
buttons = {
|
||||
{ text = 'Done', event = 'done', help = 'Pickup items from this location' },
|
||||
{ text = 'Back', event = 'back', help = 'Recharge this cell' },
|
||||
},
|
||||
}),
|
||||
grid1 = UI.ScrollingGrid({
|
||||
columns = {
|
||||
{ heading = 'Name', key = 'name', width = UI.term.width-9 },
|
||||
{ heading = 'Qty', key = 'fQty', width = 5 },
|
||||
},
|
||||
sortColumn = 'name',
|
||||
height = 8,
|
||||
y = 3,
|
||||
}),
|
||||
grid2 = UI.ScrollingGrid({
|
||||
columns = {
|
||||
{ heading = 'Name', key = 'name', width = UI.term.width-9 },
|
||||
{ heading = 'Qty', key = 'qty', width = 5 },
|
||||
},
|
||||
sortColumn = 'name',
|
||||
height = 4,
|
||||
y = 12,
|
||||
}),
|
||||
statusBar = UI.StatusBar(),
|
||||
accelerators = {
|
||||
q = 'quit',
|
||||
},
|
||||
})
|
||||
|
||||
refillPage.menuBar:add({
|
||||
filter = UI.TextEntry({
|
||||
x = UI.term.width-10,
|
||||
width = 10,
|
||||
})
|
||||
})
|
||||
|
||||
local function sendCommand(cmd)
|
||||
local socket = Socket.connect(id, 5222)
|
||||
if not socket then
|
||||
mainPage.statusBar:timedStatus('Unable to connect', 3)
|
||||
return
|
||||
end
|
||||
|
||||
socket:write(cmd)
|
||||
local m = socket:read(3)
|
||||
socket:close()
|
||||
if m then
|
||||
return m.response
|
||||
end
|
||||
mainPage.statusBar:timedStatus('No response', 3)
|
||||
end
|
||||
|
||||
local function getPoint()
|
||||
local gpt = GPS.getPoint(2)
|
||||
if not gpt then
|
||||
mainPage.statusBar:timedStatus('Unable to get location', 3)
|
||||
end
|
||||
gpt.y = gpt.y - 1
|
||||
return gpt
|
||||
end
|
||||
|
||||
function refillPage:eventHandler(event)
|
||||
|
||||
if event.type == 'grid_select' then
|
||||
|
||||
local item = {
|
||||
name = event.selected.name,
|
||||
id = event.selected.id,
|
||||
dmg = event.selected.dmg,
|
||||
qty = 0,
|
||||
}
|
||||
|
||||
local dialog = UI.Dialog({
|
||||
x = 1,
|
||||
width = UI.term.width,
|
||||
text = UI.Text({ x = 3, y = 3, value = 'Quantity' }),
|
||||
textEntry = UI.TextEntry({ x = 14, y = 3 })
|
||||
})
|
||||
|
||||
dialog.eventHandler = function(self, event)
|
||||
if event.type == 'accept' then
|
||||
local l = tonumber(self.textEntry.value)
|
||||
if l and l <= 1024 and l > 0 then
|
||||
item.qty = self.textEntry.value
|
||||
table.insert(refillPage.grid2.values, item)
|
||||
refillPage.grid2:update()
|
||||
UI:setPreviousPage()
|
||||
else
|
||||
self.statusBar:timedStatus('Invalid Quantity', 3)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return UI.Dialog.eventHandler(self, event)
|
||||
end
|
||||
|
||||
dialog.titleBar.title = item.name
|
||||
dialog:setFocus(dialog.textEntry)
|
||||
UI:setPage(dialog)
|
||||
|
||||
elseif event.type == 'text_change' then
|
||||
local text = event.text
|
||||
if #text == 0 then
|
||||
self.grid1.values = self.allItems
|
||||
else
|
||||
self.grid1.values = { }
|
||||
for _,item in pairs(self.allItems) do
|
||||
if string.find(item.lname, text) then
|
||||
table.insert(self.grid1.values, item)
|
||||
end
|
||||
end
|
||||
end
|
||||
--self.grid:adjustWidth()
|
||||
self.grid1:update()
|
||||
self.grid1:setIndex(1)
|
||||
self.grid1:draw()
|
||||
|
||||
elseif event.type == 'back' then
|
||||
UI:setPreviousPage()
|
||||
|
||||
elseif event.type == 'done' then
|
||||
UI:setPage(mainPage)
|
||||
local pt = getPoint()
|
||||
if pt then
|
||||
local response = sendCommand({ type = 'refill', entry = { point = pt, items = self.grid2.values } })
|
||||
if response then
|
||||
mainPage.statusBar:timedStatus(response, 3)
|
||||
end
|
||||
end
|
||||
|
||||
elseif event.type == 'grid_focus_row' then
|
||||
self.statusBar:setStatus(event.selected.id .. ':' .. event.selected.dmg)
|
||||
self.statusBar:draw()
|
||||
end
|
||||
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
|
||||
function refillPage:enable()
|
||||
for _,item in pairs(self.allItems) do
|
||||
item.lname = string.lower(item.name)
|
||||
item.fQty = Util.toBytes(item.qty)
|
||||
end
|
||||
|
||||
self.grid1:setValues(self.allItems)
|
||||
|
||||
self.menuBar.filter.value = ''
|
||||
self.menuBar.filter.pos = 1
|
||||
self:setFocus(self.menuBar.filter)
|
||||
UI.Page.enable(self)
|
||||
end
|
||||
|
||||
function mainPage:eventHandler(event)
|
||||
|
||||
if event.type == 'quit' then
|
||||
Event.exitPullEvents()
|
||||
|
||||
elseif event.type == 'refill' then
|
||||
local response = sendCommand({ type = 'items' })
|
||||
if response then
|
||||
refillPage.allItems = response
|
||||
refillPage.grid2:setValues({ })
|
||||
UI:setPage(refillPage)
|
||||
end
|
||||
|
||||
elseif event.type == 'pickup' or event.type == 'setPickup' or
|
||||
event.type == 'setRecharge' or event.type == 'charge' or
|
||||
event.type == 'clear' then
|
||||
local pt = getPoint()
|
||||
if pt then
|
||||
local response = sendCommand({ type = event.type, point = pt })
|
||||
if response then
|
||||
self.statusBar:timedStatus(response, 3)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
|
||||
local args = { ... }
|
||||
if #args == 1 then
|
||||
id = tonumber(args[1])
|
||||
end
|
||||
|
||||
if not id then
|
||||
error('Syntax: pickupRemote <turtle ID>')
|
||||
end
|
||||
|
||||
UI:setPage(mainPage)
|
||||
|
||||
Event.pullEvents()
|
||||
UI.term:reset()
|
||||
Reference in New Issue
Block a user