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:
@@ -146,7 +146,8 @@ function ADFieldWork.getRigs(adEnv)
|
|||||||
local role = ADFieldFleet.roleFromSpecs(specs)
|
local role = ADFieldFleet.roleFromSpecs(specs)
|
||||||
local id = ADRunsController.getVehicleId(vehicle)
|
local id = ADRunsController.getVehicleId(vehicle)
|
||||||
-- traktorer i et almindeligt flow (æg, gylle, foder ...) hører til den drift og er ikke markflåde
|
-- traktorer i et almindeligt flow (æg, gylle, foder ...) hører til den drift og er ikke markflåde
|
||||||
if role ~= nil and id ~= nil and ADRunsController.getRunForVehicle(vehicle) == nil then
|
-- (et stoppet flows traktorer er ledige — maskinpulje/flow-vogne)
|
||||||
|
if role ~= nil and id ~= nil and (ADPool ~= nil and ADPool.isFreeFromFlows(vehicle) or ADRunsController.getRunForVehicle(vehicle) == nil) then
|
||||||
local x, z = vehiclePosition(vehicle)
|
local x, z = vehiclePosition(vehicle)
|
||||||
local rig = {
|
local rig = {
|
||||||
id = id, vehicle = vehicle, role = role, label = ADRuns.vehicleLabel(vehicle),
|
id = id, vehicle = vehicle, role = role, label = ADRuns.vehicleLabel(vehicle),
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -107,6 +107,7 @@ function ADPool.implementStatus(s)
|
|||||||
if s.parkName == nil then return "noPark" end
|
if s.parkName == nil then return "noPark" end
|
||||||
if not s.hasConnect then return "noConnect", s.parkName end
|
if not s.hasConnect then return "noConnect", s.parkName end
|
||||||
if s.attachedTo ~= nil then return "attached", s.attachedTo end
|
if s.attachedTo ~= nil then return "attached", s.attachedTo end
|
||||||
|
if s.flowName ~= nil then return "flow", s.flowName end
|
||||||
if (s.quarantineMin or 0) > 0 then return "quarantine", s.quarantineMin end
|
if (s.quarantineMin or 0) > 0 then return "quarantine", s.quarantineMin end
|
||||||
if s.reserved then return "reserved" end
|
if s.reserved then return "reserved" end
|
||||||
return "parked", s.parkName
|
return "parked", s.parkName
|
||||||
@@ -308,6 +309,12 @@ function ADPool.cargoOf(vehicle)
|
|||||||
return carries, capacity
|
return carries, capacity
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Ikke bundet af et kørende flow (traktorer i stoppede flows er ledige).
|
||||||
|
function ADPool.isFreeFromFlows(vehicle)
|
||||||
|
if ADFlowWagons ~= nil then return ADFlowWagons.isFreeTractor(vehicle) end
|
||||||
|
return ADRunsController.getRunForVehicle(vehicle) == nil
|
||||||
|
end
|
||||||
|
|
||||||
local function isAttached(vehicle)
|
local function isAttached(vehicle)
|
||||||
return vehicle.getAttacherVehicle ~= nil and vehicle:getAttacherVehicle() ~= nil
|
return vehicle.getAttacherVehicle ~= nil and vehicle:getAttacherVehicle() ~= nil
|
||||||
end
|
end
|
||||||
@@ -335,7 +342,8 @@ function ADPool.collect(adEnv, isBusy)
|
|||||||
local connect = park ~= nil and ADPool.connectFor(park.name, markers) or nil
|
local connect = park ~= nil and ADPool.connectFor(park.name, markers) or nil
|
||||||
local specs = connect ~= nil and ADCpBridge.getSpecs(vehicle) or nil
|
local specs = connect ~= nil and ADCpBridge.getSpecs(vehicle) or nil
|
||||||
local role = specs ~= nil and ADPool.roleOf(specs) or nil
|
local role = specs ~= nil and ADPool.roleOf(specs) or nil
|
||||||
if role ~= nil then
|
local held = role ~= nil and ADFlowWagons ~= nil and ADFlowWagons.isHeldWagon(ADRunsController.getVehicleId(vehicle))
|
||||||
|
if role ~= nil and not held then
|
||||||
local x, z = positionOf(vehicle)
|
local x, z = positionOf(vehicle)
|
||||||
local carries, capacity = ADPool.cargoOf(vehicle)
|
local carries, capacity = ADPool.cargoOf(vehicle)
|
||||||
table.insert(implements, {carries = carries, capacity = capacity,
|
table.insert(implements, {carries = carries, capacity = capacity,
|
||||||
@@ -352,7 +360,7 @@ function ADPool.collect(adEnv, isBusy)
|
|||||||
local park = parkMarker(adEnv, vehicle)
|
local park = parkMarker(adEnv, vehicle)
|
||||||
local controlled = vehicle.getIsControlled ~= nil and vehicle:getIsControlled()
|
local controlled = vehicle.getIsControlled ~= nil and vehicle:getIsControlled()
|
||||||
if id ~= nil and park ~= nil and vehicle.spec_attacherJoints ~= nil and #ADHitch.rearImplementsOf(vehicle) == 0
|
if id ~= nil and park ~= nil and vehicle.spec_attacherJoints ~= nil and #ADHitch.rearImplementsOf(vehicle) == 0
|
||||||
and ADRunsController.getRunForVehicle(vehicle) == nil and not controlled and not isBusy(vehicle)
|
and ADPool.isFreeFromFlows(vehicle) and not controlled and not isBusy(vehicle)
|
||||||
and not ADPool.isReturning(vehicle) and ADFieldFleet.isEnabled(state.fleetMode, state.fleet, id)
|
and not ADPool.isReturning(vehicle) and ADFieldFleet.isEnabled(state.fleetMode, state.fleet, id)
|
||||||
and ADPool.canPull(ADPool.ownRole(vehicle)) then
|
and ADPool.canPull(ADPool.ownRole(vehicle)) then
|
||||||
local x, z = positionOf(vehicle)
|
local x, z = positionOf(vehicle)
|
||||||
@@ -387,6 +395,8 @@ function ADPool.overview(adEnv)
|
|||||||
attachedTo = attacher ~= nil and ADRuns.vehicleLabel(attacher) or nil,
|
attachedTo = attacher ~= nil and ADRuns.vehicleLabel(attacher) or nil,
|
||||||
quarantineMin = id ~= nil and ADPool.quarantineMinutes(id, nowMs) or 0,
|
quarantineMin = id ~= nil and ADPool.quarantineMinutes(id, nowMs) or 0,
|
||||||
reserved = id ~= nil and ADPool.isReserved(id),
|
reserved = id ~= nil and ADPool.isReserved(id),
|
||||||
|
flowName = id ~= nil and ADFlowWagons ~= nil and ADFlowWagons.isHeldWagon(id)
|
||||||
|
and tostring(ADFlowWagons.ownerOf(ADRunsController.store.runs, id).name) or nil,
|
||||||
})
|
})
|
||||||
table.insert(implements, {id = id, label = ADRuns.vehicleLabel(vehicle), role = role, status = key, arg = arg})
|
table.insert(implements, {id = id, label = ADRuns.vehicleLabel(vehicle), role = role, status = key, arg = arg})
|
||||||
end
|
end
|
||||||
@@ -396,7 +406,7 @@ function ADPool.overview(adEnv)
|
|||||||
for _, vehicle in ipairs(ADRunsController.getFarmVehicles(adEnv)) do
|
for _, vehicle in ipairs(ADRunsController.getFarmVehicles(adEnv)) do
|
||||||
local id = ADRunsController.getVehicleId(vehicle)
|
local id = ADRunsController.getVehicleId(vehicle)
|
||||||
-- en traktor med et redskab der giver den en markrolle, står allerede i flåden som kombination
|
-- en traktor med et redskab der giver den en markrolle, står allerede i flåden som kombination
|
||||||
if id ~= nil and vehicle.spec_attacherJoints ~= nil and ADRunsController.getRunForVehicle(vehicle) == nil
|
if id ~= nil and vehicle.spec_attacherJoints ~= nil and ADPool.isFreeFromFlows(vehicle)
|
||||||
and ADPool.canPull(ADPool.ownRole(vehicle)) and ADFieldFleet.roleFromSpecs(ADCpBridge.getSpecs(vehicle)) == nil then
|
and ADPool.canPull(ADPool.ownRole(vehicle)) and ADFieldFleet.roleFromSpecs(ADCpBridge.getSpecs(vehicle)) == nil then
|
||||||
local x, z = positionOf(vehicle)
|
local x, z = positionOf(vehicle)
|
||||||
local distance = ADFieldWork.distanceToNetwork(adEnv, x, z)
|
local distance = ADFieldWork.distanceToNetwork(adEnv, x, z)
|
||||||
|
|||||||
@@ -42,6 +42,28 @@ local function clampRange(value, default, min, max)
|
|||||||
return math.max(min, math.min(max, math.floor(number)))
|
return math.max(min, math.min(max, math.floor(number)))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Flow-vogne: {[traktorId] = vognId} (maskinpulje — flowet henter sin egen vogn).
|
||||||
|
function ADRuns.copyWagons(wagons)
|
||||||
|
local copy = {}
|
||||||
|
for tractorId, wagonId in pairs(wagons or {}) do copy[tractorId] = wagonId end
|
||||||
|
return copy
|
||||||
|
end
|
||||||
|
|
||||||
|
function ADRuns.serializeWagons(wagons)
|
||||||
|
local keys = {}
|
||||||
|
for tractorId in pairs(wagons or {}) do table.insert(keys, tractorId) end
|
||||||
|
table.sort(keys)
|
||||||
|
local parts = {}
|
||||||
|
for _, tractorId in ipairs(keys) do table.insert(parts, tractorId .. "=" .. wagons[tractorId]) end
|
||||||
|
return table.concat(parts, ";")
|
||||||
|
end
|
||||||
|
|
||||||
|
function ADRuns.parseWagons(text)
|
||||||
|
local wagons = {}
|
||||||
|
for tractorId, wagonId in string.gmatch(tostring(text or ""), "([^=;]+)=([^=;]+)") do wagons[tractorId] = wagonId end
|
||||||
|
return wagons
|
||||||
|
end
|
||||||
|
|
||||||
local function copyRun(run)
|
local function copyRun(run)
|
||||||
return {
|
return {
|
||||||
id = run.id,
|
id = run.id,
|
||||||
@@ -64,6 +86,8 @@ local function copyRun(run)
|
|||||||
loops = run.loops,
|
loops = run.loops,
|
||||||
returnBelowPercent = run.returnBelowPercent,
|
returnBelowPercent = run.returnBelowPercent,
|
||||||
vehicleIds = copyList(run.vehicleIds),
|
vehicleIds = copyList(run.vehicleIds),
|
||||||
|
stopped = run.stopped == true,
|
||||||
|
wagons = ADRuns.copyWagons(run.wagons),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -372,6 +396,8 @@ function ADRuns.toRecords(store)
|
|||||||
storeAbove = run.storeAbovePercent,
|
storeAbove = run.storeAbovePercent,
|
||||||
sellAbove = run.sellAbovePercent or 0,
|
sellAbove = run.sellAbovePercent or 0,
|
||||||
collectRound = run.collectRound and 1 or 0,
|
collectRound = run.collectRound and 1 or 0,
|
||||||
|
stopped = run.stopped and 1 or 0,
|
||||||
|
wagons = ADRuns.serializeWagons(run.wagons),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
return records
|
return records
|
||||||
@@ -424,6 +450,8 @@ function ADRuns.fromRecords(records)
|
|||||||
loops = clampLoops(record.loops),
|
loops = clampLoops(record.loops),
|
||||||
returnBelowPercent = clampPercent(record.returnBelow),
|
returnBelowPercent = clampPercent(record.returnBelow),
|
||||||
vehicleIds = copyList(record.vehicleIds),
|
vehicleIds = copyList(record.vehicleIds),
|
||||||
|
stopped = tonumber(record.stopped) == 1,
|
||||||
|
wagons = ADRuns.parseWagons(record.wagons),
|
||||||
})
|
})
|
||||||
store.nextId = math.max(store.nextId, id + 1)
|
store.nextId = math.max(store.nextId, id + 1)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ function ADRunsController.deleteRun(runId, adEnv)
|
|||||||
for _, vehicleId in ipairs(run.vehicleIds) do
|
for _, vehicleId in ipairs(run.vehicleIds) do
|
||||||
ADRunsController.stopIfRunning(ADRunsController.findVehicleById(adEnv, vehicleId), "run slettet")
|
ADRunsController.stopIfRunning(ADRunsController.findVehicleById(adEnv, vehicleId), "run slettet")
|
||||||
end
|
end
|
||||||
|
if ADFlowWagons ~= nil then pcall(ADFlowWagons.sendHome, adEnv, run) end
|
||||||
end
|
end
|
||||||
ADRunsController.store = ADRuns.deleteRun(ADRunsController.store, runId)
|
ADRunsController.store = ADRuns.deleteRun(ADRunsController.store, runId)
|
||||||
end
|
end
|
||||||
@@ -1074,6 +1075,12 @@ end
|
|||||||
-- og logges med årsag (samme nøgle som fanen viser).
|
-- og logges med årsag (samme nøgle som fanen viser).
|
||||||
-- Start én traktor i et run: valider, anvend run'et og start AutoDrive. Returnerer resultat-posten.
|
-- Start én traktor i et run: valider, anvend run'et og start AutoDrive. Returnerer resultat-posten.
|
||||||
function ADRunsController.startVehicle(adEnv, run, vehicle)
|
function ADRunsController.startVehicle(adEnv, run, vehicle)
|
||||||
|
-- solo-traktor med en husket vogn: hent den først (valideringen ville afvise en traktor uden vogn)
|
||||||
|
if ADFlowWagons ~= nil then
|
||||||
|
local okWagon, handled, wagonResult = pcall(ADFlowWagons.beforeStart, adEnv, run, vehicle)
|
||||||
|
if okWagon and handled then return wagonResult end
|
||||||
|
if not okWagon then Logging.warning(ADRunsController.LOG_PREFIX .. "flow-vogn: %s", tostring(handled)) end
|
||||||
|
end
|
||||||
local validation = ADRunsController.validateVehicle(adEnv, run, vehicle)
|
local validation = ADRunsController.validateVehicle(adEnv, run, vehicle)
|
||||||
local result = {vehicle = vehicle, ok = validation.ok, reason = validation.reason, missingBuilding = validation.missingBuilding}
|
local result = {vehicle = vehicle, ok = validation.ok, reason = validation.reason, missingBuilding = validation.missingBuilding}
|
||||||
if not validation.ok then
|
if not validation.ok then
|
||||||
@@ -1101,6 +1108,7 @@ function ADRunsController.startRun(adEnv, runId)
|
|||||||
if run == nil then
|
if run == nil then
|
||||||
return results
|
return results
|
||||||
end
|
end
|
||||||
|
run.stopped = false
|
||||||
local vehiclesById = getFarmVehiclesById(adEnv)
|
local vehiclesById = getFarmVehiclesById(adEnv)
|
||||||
for _, vehicleId in ipairs(run.vehicleIds) do
|
for _, vehicleId in ipairs(run.vehicleIds) do
|
||||||
local vehicle = vehiclesById[vehicleId]
|
local vehicle = vehiclesById[vehicleId]
|
||||||
@@ -1132,11 +1140,13 @@ function ADRunsController.queueStartAll(adEnv)
|
|||||||
if ADRunsController.getRunStatus(adEnv, run).isRunning then
|
if ADRunsController.getRunStatus(adEnv, run).isRunning then
|
||||||
summary.alreadyRunning = summary.alreadyRunning + 1
|
summary.alreadyRunning = summary.alreadyRunning + 1
|
||||||
else
|
else
|
||||||
|
run.stopped = false
|
||||||
for _, vehicleId in ipairs(run.vehicleIds) do
|
for _, vehicleId in ipairs(run.vehicleIds) do
|
||||||
local vehicle = vehiclesById[vehicleId]
|
local vehicle = vehiclesById[vehicleId]
|
||||||
if vehicle ~= nil then
|
if vehicle ~= nil then
|
||||||
local validation = ADRunsController.validateVehicle(adEnv, run, vehicle)
|
local validation = ADRunsController.validateVehicle(adEnv, run, vehicle)
|
||||||
if validation.ok then
|
-- solo-traktor med husket vogn: henter den ved start (valideringen ser den uden vogn)
|
||||||
|
if validation.ok or (ADFlowWagons ~= nil and ADFlowWagons.hasWagonFor(run, vehicleId)) then
|
||||||
table.insert(queue, {runId = run.id, vehicleId = vehicleId})
|
table.insert(queue, {runId = run.id, vehicleId = vehicleId})
|
||||||
else
|
else
|
||||||
table.insert(summary.skipped, {vehicle = vehicle, ok = false, reason = validation.reason, missingBuilding = validation.missingBuilding})
|
table.insert(summary.skipped, {vehicle = vehicle, ok = false, reason = validation.reason, missingBuilding = validation.missingBuilding})
|
||||||
@@ -1175,6 +1185,7 @@ function ADRunsController.stopAllAndPark(adEnv)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
ADRunsController.runningRuns[run.id] = nil
|
ADRunsController.runningRuns[run.id] = nil
|
||||||
|
run.stopped = true
|
||||||
end
|
end
|
||||||
if ADFieldWork ~= nil and ADFieldWork.stopAllAssignments ~= nil then
|
if ADFieldWork ~= nil and ADFieldWork.stopAllAssignments ~= nil then
|
||||||
for _, stopped in ipairs(ADFieldWork.stopAllAssignments(adEnv) or {}) do
|
for _, stopped in ipairs(ADFieldWork.stopAllAssignments(adEnv) or {}) do
|
||||||
@@ -1192,8 +1203,13 @@ function ADRunsController.stopAllAndPark(adEnv)
|
|||||||
if ADFieldSilos ~= nil and ADFieldSilos.stopAll ~= nil then pcall(ADFieldSilos.stopAll, adEnv) end
|
if ADFieldSilos ~= nil and ADFieldSilos.stopAll ~= nil then pcall(ADFieldSilos.stopAll, adEnv) end
|
||||||
if ADFieldJobs ~= nil and ADFieldJobs.setPaused ~= nil then ADFieldJobs.setPaused(true) end
|
if ADFieldJobs ~= nil and ADFieldJobs.setPaused ~= nil then ADFieldJobs.setPaused(true) end
|
||||||
for _, vehicle in ipairs(toPark) do
|
for _, vehicle in ipairs(toPark) do
|
||||||
local park = ADFieldWork ~= nil and ADFieldWork.parkMarkerId ~= nil and ADFieldWork.parkMarkerId(adEnv, vehicle) or nil
|
-- flow-vogn/puljeredskab bag traktoren: sæt det af på sin plads og kør hjem
|
||||||
if park ~= nil then
|
local okHome, pooled = false, false
|
||||||
|
if ADHitch ~= nil then okHome, pooled = pcall(ADHitch.returnHome, adEnv, vehicle) end
|
||||||
|
local park = not (okHome and pooled) and ADFieldWork ~= nil and ADFieldWork.parkMarkerId ~= nil and ADFieldWork.parkMarkerId(adEnv, vehicle) or nil
|
||||||
|
if okHome and pooled then
|
||||||
|
summary.parked = summary.parked + 1
|
||||||
|
elseif park ~= nil then
|
||||||
local ok = pcall(ADFieldWork.startAd, adEnv, vehicle, adEnv.AutoDrive.MODE_DRIVETO, park, nil)
|
local ok = pcall(ADFieldWork.startAd, adEnv, vehicle, adEnv.AutoDrive.MODE_DRIVETO, park, nil)
|
||||||
if ok then summary.parked = summary.parked + 1 end
|
if ok then summary.parked = summary.parked + 1 end
|
||||||
else
|
else
|
||||||
@@ -1246,6 +1262,12 @@ function ADRunsController.stopRun(adEnv, runId)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
ADRunsController.runningRuns[runId] = nil
|
ADRunsController.runningRuns[runId] = nil
|
||||||
|
-- stoppet flow: vognene på plads og traktorerne hjem (ledige i maskinpuljen, vognene er stadig flowets)
|
||||||
|
run.stopped = true
|
||||||
|
if ADFlowWagons ~= nil then
|
||||||
|
local okHome, err = pcall(ADFlowWagons.sendHome, adEnv, run)
|
||||||
|
if not okHome then Logging.warning(ADRunsController.LOG_PREFIX .. "flow-vogne hjem: %s", tostring(err)) end
|
||||||
|
end
|
||||||
return stopped
|
return stopped
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ function ADRunsStorage.writeRecords(xmlFile, records)
|
|||||||
xmlFile:setInt(key .. "#storeAbove", record.storeAbove or ADRuns.DEFAULT_STORE_ABOVE_PERCENT)
|
xmlFile:setInt(key .. "#storeAbove", record.storeAbove or ADRuns.DEFAULT_STORE_ABOVE_PERCENT)
|
||||||
xmlFile:setInt(key .. "#sellAbove", record.sellAbove or 0)
|
xmlFile:setInt(key .. "#sellAbove", record.sellAbove or 0)
|
||||||
xmlFile:setInt(key .. "#collectRound", record.collectRound or 0)
|
xmlFile:setInt(key .. "#collectRound", record.collectRound or 0)
|
||||||
|
-- maskinpulje: flowet stoppet + husket vogn pr. traktor
|
||||||
|
xmlFile:setInt(key .. "#stopped", record.stopped or 0)
|
||||||
|
xmlFile:setString(key .. "#wagons", record.wagons or "")
|
||||||
for vehicleIndex, vehicleId in ipairs(record.vehicleIds) do
|
for vehicleIndex, vehicleId in ipairs(record.vehicleIds) do
|
||||||
xmlFile:setString(string.format("%s.vehicle(%d)#id", key, vehicleIndex - 1), vehicleId)
|
xmlFile:setString(string.format("%s.vehicle(%d)#id", key, vehicleIndex - 1), vehicleId)
|
||||||
end
|
end
|
||||||
@@ -77,6 +80,8 @@ function ADRunsStorage.readRecords(xmlFile)
|
|||||||
storeAbove = xmlFile:getInt(key .. "#storeAbove"),
|
storeAbove = xmlFile:getInt(key .. "#storeAbove"),
|
||||||
sellAbove = xmlFile:getInt(key .. "#sellAbove"),
|
sellAbove = xmlFile:getInt(key .. "#sellAbove"),
|
||||||
collectRound = xmlFile:getInt(key .. "#collectRound"),
|
collectRound = xmlFile:getInt(key .. "#collectRound"),
|
||||||
|
stopped = xmlFile:getInt(key .. "#stopped"),
|
||||||
|
wagons = xmlFile:getString(key .. "#wagons"),
|
||||||
vehicleIds = {},
|
vehicleIds = {},
|
||||||
}
|
}
|
||||||
xmlFile:iterate(key .. ".vehicle", function(_, vehicleKey)
|
xmlFile:iterate(key .. ".vehicle", function(_, vehicleKey)
|
||||||
|
|||||||
@@ -1272,6 +1272,10 @@ function ADSmartPickup:update(dt)
|
|||||||
Logging.warning(ADSmartPickup.LOG_PREFIX .. "marksilo-flow fejlede: %s", tostring(errSilos))
|
Logging.warning(ADSmartPickup.LOG_PREFIX .. "marksilo-flow fejlede: %s", tostring(errSilos))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if ADFlowWagons ~= nil then
|
||||||
|
local okFlow, errFlow = pcall(ADFlowWagons.tick, getAutoDriveEnv(), dt)
|
||||||
|
if not okFlow then Logging.warning(ADSmartPickup.LOG_PREFIX .. "flow-vogne fejlede: %s", tostring(errFlow)) end
|
||||||
|
end
|
||||||
if ADHitch ~= nil then
|
if ADHitch ~= nil then
|
||||||
local okHitch, errHitch = pcall(ADHitch.tick, getAutoDriveEnv(), dt)
|
local okHitch, errHitch = pcall(ADHitch.tick, getAutoDriveEnv(), dt)
|
||||||
if not okHitch then
|
if not okHitch then
|
||||||
|
|||||||
@@ -98,6 +98,7 @@
|
|||||||
<text name="spu_pool_tractor_notInFleet" text="ikke i markflåden (flueben)%.0s"/>
|
<text name="spu_pool_tractor_notInFleet" text="ikke i markflåden (flueben)%.0s"/>
|
||||||
<text name="spu_pool_tractor_offNetwork" text="står uden for AutoDrive-vejnettet%.0s"/>
|
<text name="spu_pool_tractor_offNetwork" text="står uden for AutoDrive-vejnettet%.0s"/>
|
||||||
<text name="spu_pool_tractor_hitched" text="har et redskab på (kobl det af for at bruge den i puljen)%.0s"/>
|
<text name="spu_pool_tractor_hitched" text="har et redskab på (kobl det af for at bruge den i puljen)%.0s"/>
|
||||||
|
<text name="spu_pool_implement_flow" text="hører til flowet '%s'"/>
|
||||||
<text name="spu_pool_implement_parked" text="parkeret ved %s"/>
|
<text name="spu_pool_implement_parked" text="parkeret ved %s"/>
|
||||||
<text name="spu_pool_implement_noPark" text="ikke i puljen — mangler AD-parkering%.0s"/>
|
<text name="spu_pool_implement_noPark" text="ikke i puljen — mangler AD-parkering%.0s"/>
|
||||||
<text name="spu_pool_implement_noConnect" text="ikke i puljen — mangler markøren "Connect %s""/>
|
<text name="spu_pool_implement_noConnect" text="ikke i puljen — mangler markøren "Connect %s""/>
|
||||||
|
|||||||
@@ -98,6 +98,7 @@
|
|||||||
<text name="spu_pool_tractor_notInFleet" text="not in field fleet (tick)%.0s"/>
|
<text name="spu_pool_tractor_notInFleet" text="not in field fleet (tick)%.0s"/>
|
||||||
<text name="spu_pool_tractor_offNetwork" text="off the AutoDrive network%.0s"/>
|
<text name="spu_pool_tractor_offNetwork" text="off the AutoDrive network%.0s"/>
|
||||||
<text name="spu_pool_tractor_hitched" text="has an implement attached (detach it to use the pool)%.0s"/>
|
<text name="spu_pool_tractor_hitched" text="has an implement attached (detach it to use the pool)%.0s"/>
|
||||||
|
<text name="spu_pool_implement_flow" text="belongs to flow '%s'"/>
|
||||||
<text name="spu_pool_implement_parked" text="parked at %s"/>
|
<text name="spu_pool_implement_parked" text="parked at %s"/>
|
||||||
<text name="spu_pool_implement_noPark" text="not in pool — needs an AD park destination%.0s"/>
|
<text name="spu_pool_implement_noPark" text="not in pool — needs an AD park destination%.0s"/>
|
||||||
<text name="spu_pool_implement_noConnect" text="not in pool — needs marker "Connect %s""/>
|
<text name="spu_pool_implement_noConnect" text="not in pool — needs marker "Connect %s""/>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<modDesc descVersion="107">
|
<modDesc descVersion="107">
|
||||||
<author>masterdraco</author>
|
<author>masterdraco</author>
|
||||||
<version>1.50.5.0</version>
|
<version>1.51.0.0</version>
|
||||||
<title>
|
<title>
|
||||||
<en>AD Smart Pickup</en>
|
<en>AD Smart Pickup</en>
|
||||||
</title>
|
</title>
|
||||||
@@ -48,6 +48,7 @@
|
|||||||
<sourceFile filename="adCpResume.lua"/>
|
<sourceFile filename="adCpResume.lua"/>
|
||||||
<sourceFile filename="adHitch.lua"/>
|
<sourceFile filename="adHitch.lua"/>
|
||||||
<sourceFile filename="adPool.lua"/>
|
<sourceFile filename="adPool.lua"/>
|
||||||
|
<sourceFile filename="adFlowWagons.lua"/>
|
||||||
<sourceFile filename="adFieldJobs.lua"/>
|
<sourceFile filename="adFieldJobs.lua"/>
|
||||||
<sourceFile filename="adMonthView.lua"/>
|
<sourceFile filename="adMonthView.lua"/>
|
||||||
<sourceFile filename="adProfiler.lua"/>
|
<sourceFile filename="adProfiler.lua"/>
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# Flow-vogne — design
|
||||||
|
|
||||||
|
Dato: 2026-09-28 · Status: godkendt i chat ("1 vognen hører til flowet", "1 automatisk", "ja alle tre", "når du har
|
||||||
|
det hele klar"). Bygger på maskinpuljen (`2026-09-28-maskinpulje-design.md`).
|
||||||
|
|
||||||
|
## Formål
|
||||||
|
|
||||||
|
Alle påhæng har nu egen AD-parkering "P X" og en markør "Connect P X". Flows (fodring, mælk, slurry …) skal ikke
|
||||||
|
længere have traktor og vogn låst sammen: et flow henter **sin egen** vogn, når det starter, og når flowet ikke har
|
||||||
|
noget job længere, køres vognen på plads og traktoren hjem, så begge bliver ledige i puljen.
|
||||||
|
|
||||||
|
## Brugerens valg
|
||||||
|
|
||||||
|
| Spørgsmål | Valg |
|
||||||
|
|---|---|
|
||||||
|
| Hvem ejer en flow-vogn? | Flowet. Puljen må kun låne den, når flowet er stoppet/slettet/uden bygning |
|
||||||
|
| Hvordan kendes flowets vogn? | Automatisk: vognen der hænger på flowets traktor huskes pr. traktor |
|
||||||
|
| Hvornår "intet job"? | Alle tre: bygning væk, flow slettet, flow stoppet |
|
||||||
|
| Mejetærskere/skærebord | Uændret — skærebord er aldrig i puljen; mejetærsker henter ikke redskaber |
|
||||||
|
|
||||||
|
## Begreber
|
||||||
|
|
||||||
|
- **Flowets vogn:** `run.wagons = {[traktorId] = vognId}`. Opdateres automatisk: hænger der et redskab bagpå en af
|
||||||
|
flowets traktorer (mens flowet ikke er stoppet), huskes det som traktorens vogn. Gemmes i `ADSmartPickup_runs.xml`
|
||||||
|
(`wagons="traktorId=vognId;…"`).
|
||||||
|
- **Stoppet flow:** `run.stopped = true`, gemt. Sættes af Stop på flowet, "Stop alle"/"Stop alt og parkér" og
|
||||||
|
"bygning væk"; ryddes af Start / "Start alle". Ikke udledt af om AD kører (en traktor ved et Wait-punkt kan holde
|
||||||
|
stille uden at flowet er stoppet).
|
||||||
|
- **Bygning væk:** en bygning i flowets kilde-/mål-/lagerliste findes ikke længere som placeable (uniqueId) i to
|
||||||
|
tjek i træk (60 s mellem tjek).
|
||||||
|
|
||||||
|
## Adfærd
|
||||||
|
|
||||||
|
1. **Husk vogn** (hvert 10. s): for hvert ikke-stoppet flow og hver traktor med et bagerste redskab → `wagons[traktor]
|
||||||
|
= redskab` (ændringer gemmes med næste gem).
|
||||||
|
2. **Start** (`startVehicle`): er traktoren solo og har den en husket vogn:
|
||||||
|
- vognen frakoblet og ikke reserveret/lånt → `ADHitch.fetch` til "Connect <vognens parkering>", derefter startes
|
||||||
|
traktoren i flowet som i dag. Fanen/loggen: "henter sin vogn".
|
||||||
|
- vognen er lånt ud / koblet på en anden → traktoren venter ("venter på sin vogn"), der prøves igen hvert 10. s.
|
||||||
|
- vognen findes ikke eller har ingen Connect-markør → startes som i dag (solo; valideringen afgør).
|
||||||
|
3. **Stop / "Stop alle" / "Stop alt og parkér"**: `stopped = true`; hver traktor stoppes og køres hjem med
|
||||||
|
`ADHitch.returnHome` (vogn af på sin parkering, traktor til sin egen). Vognen forbliver flowets.
|
||||||
|
4. **Slet flow**: som stop; derefter er vognen ikke længere flowets (flowet findes ikke).
|
||||||
|
5. **Bygning væk**: flowet stoppes som i 3, markeres i loggen og med en besked: "Flow 'X': bygning findes ikke længere
|
||||||
|
— stoppet, traktorer kørt hjem".
|
||||||
|
|
||||||
|
## Puljen og flows
|
||||||
|
|
||||||
|
- `ADPool.collect`: traktor i et flow er kun puljetraktor, når flowet er **stoppet**.
|
||||||
|
- Puljeredskab: en vogn, der er husket af et **ikke-stoppet** flow, er aldrig puljeredskab.
|
||||||
|
- `ADFieldWork.getRigs`: uændret for kørende flows; traktorer i stoppede flows må indgå.
|
||||||
|
|
||||||
|
## Komponenter
|
||||||
|
|
||||||
|
- `adFlowWagons.lua` (nyt): ren logik (serialisering af `wagons`, beslutning ved start: fetch/wait/start, "bygning
|
||||||
|
væk"-debounce, ejer-opslag) + spil-del (tick: husk vogne, ventende starter, bygningstjek; `fetchThenStart`,
|
||||||
|
`sendHome(run)`).
|
||||||
|
- `adRuns.lua`/`adRunsStorage.lua`: felterne `stopped`, `wagons` i records.
|
||||||
|
- `adRunsController.lua`: kroge i `startVehicle`, `stopRun`, `deleteRun`, `stopAllRuns`, `stopAllAndPark`,
|
||||||
|
`startRun`/`startAllRuns`/`queueStartAll` (ryd `stopped`).
|
||||||
|
- `adPool.lua`: udelukker vogne ejet af ikke-stoppede flows; traktorer i stoppede flows tæller.
|
||||||
|
- `adSmartPickup.lua`: `ADFlowWagons.tick` i update-løkken.
|
||||||
|
|
||||||
|
## Test
|
||||||
|
|
||||||
|
- `tests/test_adFlowWagons.lua`: serialisering frem/tilbage, startbeslutning (fetch/wait/start), debounce af
|
||||||
|
bygning væk, ejer-opslag (stoppet vs kørende).
|
||||||
|
- `tests/test_adRuns.lua`: records med `stopped` og `wagons`.
|
||||||
|
- In-game: savegame2 — stop et fodringsflow → vogne sættes på plads, traktorer hjem; start det → traktorerne henter
|
||||||
|
deres egne vogne og kører.
|
||||||
|
|
||||||
|
## Uden for scope
|
||||||
|
|
||||||
|
- Valg af vogn i GUI (automatisk er valgt).
|
||||||
|
- At et flow tager en anden vogn end sin egen.
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
-- Kør: luajit tests/test_adFlowWagons.lua (fra repo-roden)
|
||||||
|
dofile("FS25_ADSmartPickup/adRuns.lua")
|
||||||
|
dofile("FS25_ADSmartPickup/adFlowWagons.lua")
|
||||||
|
local W = ADFlowWagons
|
||||||
|
local failures = 0
|
||||||
|
local function check(name, actual, expected)
|
||||||
|
if actual == expected then print("OK " .. name) else
|
||||||
|
failures = failures + 1
|
||||||
|
print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- gem/indlæs: traktor -> vogn
|
||||||
|
local text = W.serialize({vehicleB = "wagon2", vehicleA = "wagon1"})
|
||||||
|
check("gem: sorteret", text, "vehicleA=wagon1;vehicleB=wagon2")
|
||||||
|
local parsed = W.parse(text)
|
||||||
|
check("indlæs: A", parsed.vehicleA, "wagon1")
|
||||||
|
check("indlæs: B", parsed.vehicleB, "wagon2")
|
||||||
|
check("indlæs: tom", next(W.parse("")), nil)
|
||||||
|
check("indlæs: nil", next(W.parse(nil)), nil)
|
||||||
|
check("indlæs: skrald springes over", next(W.parse("=x;;y=")), nil)
|
||||||
|
-- husk vogn: ny tabel, gammel urørt
|
||||||
|
local before = {t1 = "w1"}
|
||||||
|
local after = W.remember(before, "t1", "w9")
|
||||||
|
check("husk: ny vogn", after.t1, "w9")
|
||||||
|
check("husk: original urørt", before.t1, "w1")
|
||||||
|
check("husk: samme vogn -> samme tabel", W.remember(after, "t1", "w9"), after)
|
||||||
|
-- start: hent / vent / start
|
||||||
|
check("start: har redskab bagpå", W.startAction({hasRear = true, wagonId = "w1", wagonExists = true, hasConnect = true}), "start")
|
||||||
|
check("start: ingen husket vogn", W.startAction({}), "start")
|
||||||
|
check("start: vognen findes ikke", W.startAction({wagonId = "w1", wagonExists = false, hasConnect = true}), "start")
|
||||||
|
check("start: vognen uden Connect", W.startAction({wagonId = "w1", wagonExists = true, hasConnect = false}), "start")
|
||||||
|
check("start: vognen på en anden", W.startAction({wagonId = "w1", wagonExists = true, hasConnect = true, wagonAttached = true}), "wait")
|
||||||
|
check("start: vognen lånt ud", W.startAction({wagonId = "w1", wagonExists = true, hasConnect = true, wagonBusy = true}), "wait")
|
||||||
|
check("start: hent", W.startAction({wagonId = "w1", wagonExists = true, hasConnect = true}), "fetch")
|
||||||
|
-- ejer: kun et ikke-stoppet flow holder vognen
|
||||||
|
local runs = {{id = 1, stopped = false, wagons = {t1 = "w1"}}, {id = 2, stopped = true, wagons = {t2 = "w2"}}}
|
||||||
|
check("ejer: kørende flow holder", W.isHeld(runs, "w1"), true)
|
||||||
|
check("ejer: stoppet flow holder ikke", W.isHeld(runs, "w2"), false)
|
||||||
|
check("ejer: ukendt vogn", W.isHeld(runs, "w3"), false)
|
||||||
|
check("ejer: flowet findes", W.ownerOf(runs, "w2").id, 2)
|
||||||
|
-- bygning væk: to tjek i træk
|
||||||
|
local count, gone = W.missingStep(0, true)
|
||||||
|
check("væk: første tjek -> ikke endnu", gone, false)
|
||||||
|
count, gone = W.missingStep(count, true)
|
||||||
|
check("væk: andet tjek -> væk", gone, true)
|
||||||
|
count, gone = W.missingStep(1, false)
|
||||||
|
check("væk: fundet igen nulstiller", count, 0)
|
||||||
|
check("væk: fundet igen -> ikke væk", gone, false)
|
||||||
|
-- manglende bygninger i et flow
|
||||||
|
local exists = {a = true, b = true}
|
||||||
|
check("mangler: alle findes", #W.missingBuildings({sourceBuildingIds = {"a"}, targetBuildingIds = {"b"}}, exists), 0)
|
||||||
|
check("mangler: én væk", W.missingBuildings({targetBuildingIds = {"a", "c"}, storeBuildingIds = {}}, exists)[1], "c")
|
||||||
|
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
||||||
|
os.exit(failures == 0 and 0 or 1)
|
||||||
@@ -149,5 +149,8 @@ check("par-alle: antal (v2 reserveret, 2 traktorer)", #pairs3, 2)
|
|||||||
check("par-alle: v1 -> tA", pairs3[1].implement.id .. ">" .. pairs3[1].tractor.id, "v1>tA")
|
check("par-alle: v1 -> tA", pairs3[1].implement.id .. ">" .. pairs3[1].tractor.id, "v1>tA")
|
||||||
check("par-alle: v3 -> tB (tA er brugt)", pairs3[2].implement.id .. ">" .. pairs3[2].tractor.id, "v3>tB")
|
check("par-alle: v3 -> tB (tA er brugt)", pairs3[2].implement.id .. ">" .. pairs3[2].tractor.id, "v3>tB")
|
||||||
check("par-alle: ingen traktorer", #P.pairAll("unloader", wagons, {}, {}), 0)
|
check("par-alle: ingen traktorer", #P.pairAll("unloader", wagons, {}, {}), 0)
|
||||||
|
-- flow-vogne: vogn der hører til et kørende flow vises sådan (og er ikke i puljen)
|
||||||
|
check("redskab: flowets vogn", ist({parkName = "P Cow Food vogn 1", hasConnect = true, flowName = "Cow Food"}), "flow|Cow Food")
|
||||||
|
check("redskab: flowets vogn koblet på", ist({parkName = "P Cow Food vogn 1", hasConnect = true, flowName = "Cow Food", attachedTo = "Cow Food 1"}), "attached|Cow Food 1")
|
||||||
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
||||||
os.exit(failures == 0 and 0 or 1)
|
os.exit(failures == 0 and 0 or 1)
|
||||||
|
|||||||
@@ -322,5 +322,18 @@ do
|
|||||||
check("RS roundtrip", table.concat(ADRuns.getRun(back, run.id).storeBuildingIds, ","), "BGA500kw")
|
check("RS roundtrip", table.concat(ADRuns.getRun(back, run.id).storeBuildingIds, ","), "BGA500kw")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- flow-vogne: stoppet + husket vogn pr. traktor følger med records frem og tilbage
|
||||||
|
local wagonStore = ADRuns.newStore()
|
||||||
|
local ws, wrun = ADRuns.createRun(wagonStore, "Fodring", "supply")
|
||||||
|
wrun.stopped, wrun.wagons = true, {vehA = "wagonA"}
|
||||||
|
local wrecords = ADRuns.toRecords(ws)
|
||||||
|
check("vogne: record stoppet", wrecords[1].stopped, 1)
|
||||||
|
check("vogne: record vogne", wrecords[1].wagons, "vehA=wagonA")
|
||||||
|
local wback = ADRuns.fromRecords(wrecords).runs[1]
|
||||||
|
check("vogne: stoppet tilbage", wback.stopped, true)
|
||||||
|
check("vogne: vogn tilbage", wback.wagons.vehA, "wagonA")
|
||||||
|
local wupdated = ADRuns.getRun(ADRuns.updateRun(ws, wrun.id, {name = "Fodring 2"}), wrun.id)
|
||||||
|
check("vogne: overlever updateRun", wupdated.wagons.vehA, "wagonA")
|
||||||
|
check("vogne: stoppet overlever updateRun", wupdated.stopped, true)
|
||||||
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
||||||
os.exit(failures == 0 and 0 or 1)
|
os.exit(failures == 0 and 0 or 1)
|
||||||
|
|||||||
Reference in New Issue
Block a user