feat(flows): flow-vogne — flowets vogn huskes pr. traktor, solo-traktor henter sin vogn ved start (venter hvis den er lånt ud), stop/slet/bygning væk sætter vognen på plads og kører traktoren hjem; stoppede flows er ledige i maskinpuljen (v1.51.0.0)
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
-- AD Flow Wagons
|
||||
-- Flows ejer deres vogne, men traktor og vogn er ikke låst sammen: den vogn der hænger på en flow-traktor huskes
|
||||
-- (run.wagons = {[traktorId] = vognId}). Starter flowet med en solo-traktor, henter den sin vogn ved "Connect <vognens
|
||||
-- parkering>". Stoppes/slettes flowet, eller er dets bygning væk, sættes vognen på plads og traktoren kører hjem —
|
||||
-- så er begge ledige i maskinpuljen (en stoppet flows vogn må lånes; en kørende flows aldrig).
|
||||
|
||||
ADFlowWagons = {}
|
||||
ADFlowWagons.LOG_PREFIX = "ADSmartPickup: flow-vogne: "
|
||||
ADFlowWagons.REMEMBER_MS = 10000 -- husk vogne / prøv ventende starter igen
|
||||
ADFlowWagons.BUILDING_CHECK_MS = 60000
|
||||
ADFlowWagons.MISSING_CHECKS = 2 -- så mange tjek i træk før en bygning regnes for væk
|
||||
|
||||
-- ---- ren logik ----
|
||||
|
||||
ADFlowWagons.serialize = function(wagons) return ADRuns.serializeWagons(wagons) end
|
||||
ADFlowWagons.parse = function(text) return ADRuns.parseWagons(text) end
|
||||
|
||||
-- Husk traktorens vogn: ny tabel ved ændring, samme tabel når intet ændres.
|
||||
function ADFlowWagons.remember(wagons, tractorId, wagonId)
|
||||
if (wagons or {})[tractorId] == wagonId then return wagons end
|
||||
local copy = ADRuns.copyWagons(wagons)
|
||||
copy[tractorId] = wagonId
|
||||
return copy
|
||||
end
|
||||
|
||||
-- s = {hasRear, wagonId, wagonExists, hasConnect, wagonAttached, wagonBusy} -> "start" | "fetch" | "wait"
|
||||
function ADFlowWagons.startAction(s)
|
||||
if s.hasRear or s.wagonId == nil or not s.wagonExists or not s.hasConnect then return "start" end
|
||||
if s.wagonAttached or s.wagonBusy then return "wait" end
|
||||
return "fetch"
|
||||
end
|
||||
|
||||
function ADFlowWagons.ownerOf(runs, wagonId)
|
||||
for _, run in ipairs(runs or {}) do
|
||||
for _, id in pairs(run.wagons or {}) do
|
||||
if id == wagonId then return run end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Holdes vognen af et flow der ikke er stoppet? (så må puljen ikke låne den)
|
||||
function ADFlowWagons.isHeld(runs, wagonId)
|
||||
local run = ADFlowWagons.ownerOf(runs, wagonId)
|
||||
return run ~= nil and not run.stopped
|
||||
end
|
||||
|
||||
-- Et tjek af om bygningen mangler: -> ny tæller, væk?
|
||||
function ADFlowWagons.missingStep(count, missingNow)
|
||||
if not missingNow then return 0, false end
|
||||
local next = (count or 0) + 1
|
||||
return next, next >= ADFlowWagons.MISSING_CHECKS
|
||||
end
|
||||
|
||||
-- Flowets bygnings-id'er der ikke findes i exists = {[id] = true}.
|
||||
function ADFlowWagons.missingBuildings(run, exists)
|
||||
local missing = {}
|
||||
for _, key in ipairs({"sourceBuildingIds", "targetBuildingIds", "storeBuildingIds"}) do
|
||||
for _, id in ipairs(run[key] or {}) do
|
||||
if not exists[id] then table.insert(missing, id) end
|
||||
end
|
||||
end
|
||||
return missing
|
||||
end
|
||||
|
||||
-- ---- spil ----
|
||||
|
||||
local waiting = setmetatable({}, {__mode = "k"}) -- traktor -> runId (venter på sin vogn)
|
||||
local fetching = setmetatable({}, {__mode = "k"}) -- traktor -> runId (henter sin vogn)
|
||||
local missingCount = {} -- runId -> tjek i træk
|
||||
local rememberTimer, buildingTimer = 0, 0
|
||||
|
||||
local function runs()
|
||||
return ADRunsController.store ~= nil and ADRunsController.store.runs or {}
|
||||
end
|
||||
|
||||
local function findAnyVehicle(id)
|
||||
for _, vehicle in pairs(g_currentMission.vehicleSystem.vehicles) do
|
||||
if ADRunsController.getVehicleId(vehicle) == id then return vehicle end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function connectOf(adEnv, wagon)
|
||||
local index = wagon.advd ~= nil and wagon.advd.parkDestination or nil
|
||||
local park = type(index) == "number" and index >= 1 and adEnv.ADGraphManager:getMapMarkerById(index) or nil
|
||||
if park == nil then return nil end
|
||||
local marker = ADPool.connectFor(park.name, adEnv.ADGraphManager:getMapMarkers())
|
||||
return marker ~= nil and marker.id or nil
|
||||
end
|
||||
|
||||
-- Traktorer i stoppede flows er ledige i puljen; traktorer i kørende flows aldrig.
|
||||
function ADFlowWagons.isFreeTractor(vehicle)
|
||||
local run = ADRunsController.getRunForVehicle(vehicle)
|
||||
return run == nil or run.stopped == true
|
||||
end
|
||||
|
||||
function ADFlowWagons.hasWagonFor(run, tractorId)
|
||||
return (run.wagons or {})[tractorId] ~= nil
|
||||
end
|
||||
|
||||
function ADFlowWagons.isHeldWagon(wagonId)
|
||||
return ADFlowWagons.isHeld(runs(), wagonId)
|
||||
end
|
||||
|
||||
-- Før en flow-traktor startes: hent sin vogn / vent på den. -> håndteret, resultat (som startVehicle)
|
||||
function ADFlowWagons.beforeStart(adEnv, run, vehicle)
|
||||
local tractorId = ADRunsController.getVehicleId(vehicle)
|
||||
if fetching[vehicle] ~= nil then return true, {vehicle = vehicle, ok = true, reason = "fetchingWagon"} end
|
||||
local wagonId = (run.wagons or {})[tractorId]
|
||||
local wagon = wagonId ~= nil and findAnyVehicle(wagonId) or nil
|
||||
local connectId = wagon ~= nil and connectOf(adEnv, wagon) or nil
|
||||
local action = ADFlowWagons.startAction({
|
||||
hasRear = #ADHitch.rearImplementsOf(vehicle) > 0, wagonId = wagonId, wagonExists = wagon ~= nil,
|
||||
hasConnect = connectId ~= nil,
|
||||
wagonAttached = wagon ~= nil and wagon.getAttacherVehicle ~= nil and wagon:getAttacherVehicle() ~= nil,
|
||||
wagonBusy = wagonId ~= nil and ADPool.isReserved(wagonId),
|
||||
})
|
||||
if action == "start" then return false end
|
||||
ADRunsController.runningRuns[run.id] = true
|
||||
if action == "wait" then
|
||||
if waiting[vehicle] == nil then
|
||||
Logging.info(ADFlowWagons.LOG_PREFIX .. "flow '%s': '%s' venter på sin vogn (i brug andetsteds)", tostring(run.name),
|
||||
tostring(ADRuns.vehicleLabel(vehicle)))
|
||||
end
|
||||
waiting[vehicle] = run.id
|
||||
return true, {vehicle = vehicle, ok = true, reason = "waitingWagon"}
|
||||
end
|
||||
waiting[vehicle] = nil
|
||||
fetching[vehicle] = run.id
|
||||
local runId = run.id
|
||||
ADHitch.fetch(adEnv, vehicle, connectId, function(tractor, ok, detail)
|
||||
fetching[tractor] = nil
|
||||
local current = ADRuns.getRun(ADRunsController.store, runId)
|
||||
if current == nil or current.stopped then
|
||||
ADHitch.returnHome(adEnv, tractor)
|
||||
return
|
||||
end
|
||||
if ok then
|
||||
ADRunsController.startVehicle(adEnv, current, tractor)
|
||||
else
|
||||
Logging.warning(ADFlowWagons.LOG_PREFIX .. "flow '%s': '%s' kunne ikke hente sin vogn (%s)", tostring(current.name),
|
||||
tostring(ADRuns.vehicleLabel(tractor)), tostring(detail))
|
||||
end
|
||||
end)
|
||||
Logging.info(ADFlowWagons.LOG_PREFIX .. "flow '%s': '%s' henter sin vogn", tostring(run.name), tostring(ADRuns.vehicleLabel(vehicle)))
|
||||
return true, {vehicle = vehicle, ok = true, reason = "fetchingWagon"}
|
||||
end
|
||||
|
||||
-- Stop/slet/bygning væk: vognen på plads, traktoren hjem.
|
||||
function ADFlowWagons.sendHome(adEnv, run)
|
||||
for _, vehicleId in ipairs(run.vehicleIds or {}) do
|
||||
local vehicle = ADRunsController.findVehicleById(adEnv, vehicleId)
|
||||
if vehicle ~= nil then
|
||||
waiting[vehicle] = nil
|
||||
if fetching[vehicle] ~= nil then
|
||||
fetching[vehicle] = nil
|
||||
ADHitch.cancel(vehicle)
|
||||
end
|
||||
local okHome, pooled = pcall(ADHitch.returnHome, adEnv, vehicle)
|
||||
if not (okHome and pooled) then
|
||||
local park = ADFieldWork.parkMarkerId(adEnv, vehicle)
|
||||
if park ~= nil then pcall(ADFieldWork.startAd, adEnv, vehicle, adEnv.AutoDrive.MODE_DRIVETO, park, nil) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function rememberAll(adEnv)
|
||||
for _, run in ipairs(runs()) do
|
||||
if not run.stopped then
|
||||
for _, vehicleId in ipairs(run.vehicleIds or {}) do
|
||||
local vehicle = ADRunsController.findVehicleById(adEnv, vehicleId)
|
||||
local rear = vehicle ~= nil and ADHitch.rearImplementsOf(vehicle) or {}
|
||||
local wagonId = rear[1] ~= nil and ADRunsController.getVehicleId(rear[1].object) or nil
|
||||
if wagonId ~= nil then
|
||||
local updated = ADFlowWagons.remember(run.wagons, vehicleId, wagonId)
|
||||
if updated ~= run.wagons then
|
||||
run.wagons = updated
|
||||
Logging.info(ADFlowWagons.LOG_PREFIX .. "flow '%s': '%s' har vognen '%s'", tostring(run.name),
|
||||
tostring(ADRuns.vehicleLabel(vehicle)), tostring(ADRuns.vehicleLabel(rear[1].object)))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function retryWaiting(adEnv)
|
||||
local list = {}
|
||||
for vehicle, runId in pairs(waiting) do table.insert(list, {vehicle = vehicle, runId = runId}) end
|
||||
for _, item in ipairs(list) do
|
||||
waiting[item.vehicle] = nil
|
||||
local run = ADRuns.getRun(ADRunsController.store, item.runId)
|
||||
if run ~= nil and not run.stopped then pcall(ADRunsController.startVehicle, adEnv, run, item.vehicle) end
|
||||
end
|
||||
end
|
||||
|
||||
local function checkBuildings(adEnv)
|
||||
local exists = {}
|
||||
local placeables = g_currentMission.placeableSystem ~= nil and g_currentMission.placeableSystem.placeables or {}
|
||||
for _, placeable in pairs(placeables) do
|
||||
local id = ADBuildings.getId(placeable)
|
||||
if id ~= nil then exists[id] = true end
|
||||
end
|
||||
if next(exists) == nil then return end -- listen er ikke klar: sig intet
|
||||
for _, run in ipairs(runs()) do
|
||||
if not run.stopped then
|
||||
local missing = ADFlowWagons.missingBuildings(run, exists)
|
||||
local count, gone = ADFlowWagons.missingStep(missingCount[run.id], #missing > 0)
|
||||
missingCount[run.id] = count
|
||||
if gone then
|
||||
missingCount[run.id] = 0
|
||||
Logging.warning(ADFlowWagons.LOG_PREFIX .. "flow '%s': bygning findes ikke længere (%s) — stoppet, traktorer kørt hjem",
|
||||
tostring(run.name), table.concat(missing, ", "))
|
||||
ADRunsController.stopRun(adEnv, run.id)
|
||||
if ADFieldJobs ~= nil and ADFieldJobs.notify ~= nil then
|
||||
pcall(ADFieldJobs.notify, string.format("Flow '%s': bygning findes ikke længere — stoppet, traktorer kørt hjem", tostring(run.name)), true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ADFlowWagons.tick(adEnv, dt)
|
||||
if adEnv == nil or ADRunsController.store == nil then return end
|
||||
rememberTimer = rememberTimer + dt
|
||||
buildingTimer = buildingTimer + dt
|
||||
if rememberTimer >= ADFlowWagons.REMEMBER_MS then
|
||||
rememberTimer = 0
|
||||
rememberAll(adEnv)
|
||||
retryWaiting(adEnv)
|
||||
end
|
||||
if buildingTimer >= ADFlowWagons.BUILDING_CHECK_MS then
|
||||
buildingTimer = 0
|
||||
checkBuildings(adEnv)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user