diff --git a/FS25_ADSmartPickup/adFleetNames.lua b/FS25_ADSmartPickup/adFleetNames.lua index 84089a6..62cfa4f 100644 --- a/FS25_ADSmartPickup/adFleetNames.lua +++ b/FS25_ADSmartPickup/adFleetNames.lua @@ -9,7 +9,8 @@ ADFleetNames.LOG_PREFIX = "ADSmartPickup: navne: " -- ---- ren logik ---- -- vehicles = {{id, model, name, inRun, selfPropelled}} -> {{id, name, parkName, rename}} -function ADFleetNames.plan(vehicles) +-- parkUse = {[parkIndex] = antal køretøjer (også redskaber) med den parkering}: delte parkeringer røres ikke. +function ADFleetNames.plan(vehicles, parkUse) local byModel = {} for _, vehicle in ipairs(vehicles or {}) do if not vehicle.inRun and not vehicle.selfPropelled then @@ -27,7 +28,10 @@ function ADFleetNames.plan(vehicles) local result = {} for _, vehicle in ipairs(vehicles or {}) do local name = newNames[vehicle.id] or vehicle.name or vehicle.model - table.insert(result, {id = vehicle.id, name = name, parkName = "P " .. name, rename = newNames[vehicle.id] ~= nil and name ~= vehicle.name}) + local ownPark = vehicle.park == nil or ((parkUse or {})[vehicle.park] or 1) <= 1 + table.insert(result, {id = vehicle.id, name = name, parkName = "P " .. name, + rename = newNames[vehicle.id] ~= nil and name ~= vehicle.name, + renamePark = newNames[vehicle.id] ~= nil and ownPark}) end return result end @@ -35,18 +39,27 @@ end -- ---- spil ---- function ADFleetNames.apply(adEnv) - local vehicles, byId = {}, {} + local vehicles, byId, parkUse = {}, {}, {} + local farmId = g_currentMission:getFarmId() + for _, any in pairs(g_currentMission.vehicleSystem.vehicles) do + local index = any.advd ~= nil and any.advd.parkDestination or nil + if type(index) == "number" and index >= 1 and any.getOwnerFarmId ~= nil and any:getOwnerFarmId() == farmId then + parkUse[index] = (parkUse[index] or 0) + 1 + end + end for _, vehicle in ipairs(ADRunsController.getFarmVehicles(adEnv)) do local id = ADRunsController.getVehicleId(vehicle) if id ~= nil and vehicle.getName ~= nil then + local ownIndex = vehicle.advd ~= nil and vehicle.advd.parkDestination or nil table.insert(vehicles, {id = id, model = tostring(vehicle:getName()), name = ADRuns.vehicleLabel(vehicle), + park = type(ownIndex) == "number" and ownIndex >= 1 and ownIndex or nil, inRun = ADRunsController.getRunForVehicle(vehicle) ~= nil, selfPropelled = not ADPool.canPull(ADPool.ownRole(vehicle))}) byId[id] = vehicle end end local renamed, parks = 0, 0 - for _, entry in ipairs(ADFleetNames.plan(vehicles)) do + for _, entry in ipairs(ADFleetNames.plan(vehicles, parkUse)) do local vehicle = byId[entry.id] local module = vehicle.ad ~= nil and vehicle.ad.stateModule or nil if entry.rename and module ~= nil and module.setName ~= nil then @@ -56,7 +69,7 @@ function ADFleetNames.apply(adEnv) end local index = vehicle.advd ~= nil and vehicle.advd.parkDestination or nil local marker = type(index) == "number" and index >= 1 and adEnv.ADGraphManager:getMapMarkerById(index) or nil - if marker ~= nil and marker.name ~= entry.parkName then + if entry.renamePark and marker ~= nil and marker.name ~= entry.parkName then adEnv.ADGraphManager:renameMapMarker(entry.parkName, index) parks = parks + 1 end diff --git a/FS25_ADSmartPickup/adFlowWagons.lua b/FS25_ADSmartPickup/adFlowWagons.lua index bad83c9..33b0e18 100644 --- a/FS25_ADSmartPickup/adFlowWagons.lua +++ b/FS25_ADSmartPickup/adFlowWagons.lua @@ -25,6 +25,8 @@ end -- s = {hasRear, wagonId, wagonExists, hasConnect, wagonAttached, wagonBusy} -> "start" | "fetch" | "wait" function ADFlowWagons.startAction(s) + -- lånt ud af puljen (markarbejde, marksilo, på hjemtur): vent til den er fri + if s.tractorBusy then return "wait" end 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" @@ -41,8 +43,14 @@ 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 + for _, run in ipairs(runs or {}) do + if not run.stopped then + for _, id in pairs(run.wagons or {}) do + if id == wagonId then return true end + end + end + end + return false end -- Et tjek af om bygningen mangler: -> ny tæller, væk? @@ -52,12 +60,13 @@ function ADFlowWagons.missingStep(count, missingNow) return next, next >= ADFlowWagons.MISSING_CHECKS end --- Flowets bygnings-id'er der ikke findes i exists = {[id] = true}. -function ADFlowWagons.missingBuildings(run, exists) +-- Flowets bygnings-id'er der ikke findes i exists = {[id] = true}. Kun bygninger der er set i denne session +-- (seen) tæller: et forældet id i et gammelt flow må ikke stoppe et flow der virker. +function ADFlowWagons.missingBuildings(run, exists, seen) 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 + if not exists[id] and (seen or {})[id] then table.insert(missing, id) end end end return missing @@ -68,6 +77,7 @@ end 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 seenBuildings = {} -- bygnings-id'er set i denne session local rememberTimer, buildingTimer = 0, 0 local function runs() @@ -95,6 +105,17 @@ function ADFlowWagons.isFreeTractor(vehicle) return run == nil or run.stopped == true end +-- Holder flowet noget: en traktor henter/venter på sin vogn eller har en vogn bagpå (til Stop-knappen). +function ADFlowWagons.hasActivity(run, vehiclesById) + for _, vehicleId in ipairs(run.vehicleIds or {}) do + local vehicle = vehiclesById ~= nil and vehiclesById[vehicleId] or nil + if vehicle ~= nil and (waiting[vehicle] ~= nil or fetching[vehicle] ~= nil or #ADHitch.rearImplementsOf(vehicle) > 0) then + return true + end + end + return false +end + function ADFlowWagons.hasWagonFor(run, tractorId) return (run.wagons or {})[tractorId] ~= nil end @@ -103,6 +124,14 @@ function ADFlowWagons.isHeldWagon(wagonId) return ADFlowWagons.isHeld(runs(), wagonId) end +-- Lånt ud af puljen: markarbejde, marksilo-lån, på hjemtur eller Courseplay kører den. +function ADFlowWagons.isPoolBusy(vehicle, tractorId) + return (ADFieldJobs ~= nil and ADFieldJobs.state.assignments[tractorId] ~= nil) + or (ADFieldSilos ~= nil and ADFieldSilos.isLoaned(vehicle)) + or (ADPool ~= nil and ADPool.isReturning(vehicle)) + or (ADCpBridge ~= nil and ADCpBridge.isWorking(vehicle)) +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) @@ -111,6 +140,7 @@ function ADFlowWagons.beforeStart(adEnv, run, vehicle) local wagon = wagonId ~= nil and findAnyVehicle(wagonId) or nil local connectId = wagon ~= nil and connectOf(adEnv, wagon) or nil local action = ADFlowWagons.startAction({ + tractorBusy = ADFlowWagons.isPoolBusy(vehicle, tractorId), hasRear = #ADHitch.rearImplementsOf(vehicle) > 0, wagonId = wagonId, wagonExists = wagon ~= nil, hasConnect = connectId ~= nil, wagonAttached = wagon ~= nil and wagon.getAttacherVehicle ~= nil and wagon:getAttacherVehicle() ~= nil, @@ -129,7 +159,7 @@ function ADFlowWagons.beforeStart(adEnv, run, vehicle) waiting[vehicle] = nil fetching[vehicle] = run.id local runId = run.id - ADHitch.fetch(adEnv, vehicle, connectId, function(tractor, ok, detail) + local okFetch, errFetch = pcall(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 @@ -137,12 +167,22 @@ function ADFlowWagons.beforeStart(adEnv, run, vehicle) return end if ok then - ADRunsController.startVehicle(adEnv, current, tractor) + local result = ADRunsController.startVehicle(adEnv, current, tractor) + if result ~= nil and not result.ok then + -- flowet kan ikke startes (markør, bygning …): ikke efterladt ved Connect med vognen + Logging.warning(ADFlowWagons.LOG_PREFIX .. "flow '%s': '%s' kunne ikke starte (%s) — vognen sættes tilbage", + tostring(current.name), tostring(ADRuns.vehicleLabel(tractor)), tostring(result.reason)) + ADHitch.returnHome(adEnv, tractor) + end 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) + if not okFetch then + fetching[vehicle] = nil + return true, {vehicle = vehicle, ok = false, reason = "error"} + 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 @@ -204,9 +244,10 @@ local function checkBuildings(adEnv) if id ~= nil then exists[id] = true end end if next(exists) == nil then return end -- listen er ikke klar: sig intet + for id in pairs(exists) do seenBuildings[id] = true end for _, run in ipairs(runs()) do if not run.stopped then - local missing = ADFlowWagons.missingBuildings(run, exists) + local missing = ADFlowWagons.missingBuildings(run, exists, seenBuildings) local count, gone = ADFlowWagons.missingStep(missingCount[run.id], #missing > 0) missingCount[run.id] = count if gone then @@ -234,7 +275,8 @@ local function cleanupAfterLoad(adEnv) local vehicle = ADRunsController.findVehicleById(adEnv, vehicleId) local module = vehicle ~= nil and vehicle.ad ~= nil and vehicle.ad.stateModule or nil local controlled = vehicle ~= nil and vehicle.getIsControlled ~= nil and vehicle:getIsControlled() - if module ~= nil and not module:isActive() and not controlled and #ADHitch.poolParkIds(adEnv, vehicle) > 0 then + if module ~= nil and not module:isActive() and not controlled and #ADHitch.poolParkIds(adEnv, vehicle) > 0 + and not ADFlowWagons.isPoolBusy(vehicle, vehicleId) then Logging.info(ADFlowWagons.LOG_PREFIX .. "flow '%s' er stoppet: '%s' sætter sin vogn på plads", tostring(run.name), tostring(ADRuns.vehicleLabel(vehicle))) pcall(ADHitch.returnHome, adEnv, vehicle) diff --git a/FS25_ADSmartPickup/adRuns.lua b/FS25_ADSmartPickup/adRuns.lua index 25d43a8..8ee4845 100644 --- a/FS25_ADSmartPickup/adRuns.lua +++ b/FS25_ADSmartPickup/adRuns.lua @@ -338,6 +338,8 @@ function ADRuns.unassignVehicle(store, vehicleId) break end end + -- traktoren forlader flowet: dens huskede vogn hører ikke længere til flowet + if run.wagons ~= nil then run.wagons[vehicleId] = nil end end return new end diff --git a/FS25_ADSmartPickup/adRunsController.lua b/FS25_ADSmartPickup/adRunsController.lua index 4340b6f..76247f4 100644 --- a/FS25_ADSmartPickup/adRunsController.lua +++ b/FS25_ADSmartPickup/adRunsController.lua @@ -86,7 +86,7 @@ end function ADRunsController.deleteRun(runId, adEnv) local run = ADRuns.getRun(ADRunsController.store, runId) - if run ~= nil and adEnv ~= nil then + if run ~= nil and adEnv ~= nil and not run.stopped then for _, vehicleId in ipairs(run.vehicleIds) do ADRunsController.stopIfRunning(ADRunsController.findVehicleById(adEnv, vehicleId), "run slettet") end @@ -1252,6 +1252,11 @@ function ADRunsController.stopRun(adEnv, runId) if run == nil then return stopped end + -- allerede stoppet: traktorerne kan være lånt ud af maskinpuljen — de må ikke trækkes væk midt i et job + if run.stopped then + ADRunsController.runningRuns[runId] = nil + return stopped + end local vehiclesById = getFarmVehiclesById(adEnv) for _, vehicleId in ipairs(run.vehicleIds) do local vehicle = vehiclesById[vehicleId] @@ -1286,7 +1291,9 @@ function ADRunsController.getRunStatus(adEnv, run) if ADRunsController.runningRuns[run.id] and running == 0 then ADRunsController.runningRuns[run.id] = nil end - return {running = running, total = #run.vehicleIds, isRunning = isRunning} + -- canStop: Stop-knappen — også når et ikke-stoppet flow holder stille med vogne på / henter / venter på sin vogn + local canStop = isRunning or (not run.stopped and ADFlowWagons ~= nil and ADFlowWagons.hasActivity(run, vehiclesById)) + return {running = running, total = #run.vehicleIds, isRunning = isRunning, canStop = canStop} end -- Start alle (Oversigt): flows der allerede kører røres ikke, så et halvt kørende gårdbillede kan diff --git a/FS25_ADSmartPickup/gui/SmartPickupFlowGuide.lua b/FS25_ADSmartPickup/gui/SmartPickupFlowGuide.lua index 46090a3..9348083 100644 --- a/FS25_ADSmartPickup/gui/SmartPickupFlowGuide.lua +++ b/FS25_ADSmartPickup/gui/SmartPickupFlowGuide.lua @@ -211,7 +211,7 @@ function SmartPickupFrame:refreshFlowTab(adEnv) self:refreshOverview(adEnv) if run ~= nil then local status = ADRunsController.getRunStatus(adEnv, run) - isRunning = status.isRunning == true + isRunning = status.canStop == true local text = status.running > 0 and string.format(self.i18n:getText("spu_status_running"), status.running, status.total) or string.format(self.i18n:getText("spu_status_stopped"), status.total) self:setStatusText(self.statusOverride or text) diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua index 26686d3..501f06a 100644 --- a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua +++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua @@ -648,7 +648,7 @@ function SmartPickupFrame:refreshLive() self:refreshNeedsRows(adEnv, run) end local status = ADRunsController.getRunStatus(adEnv, run) - isRunning = status.isRunning == true + isRunning = status.canStop == true local text = status.running > 0 and string.format(self.i18n:getText("spu_status_running"), status.running, status.total) or string.format(self.i18n:getText("spu_status_stopped"), status.total) self:setStatusText(self.statusOverride or text) @@ -749,7 +749,7 @@ function SmartPickupFrame:onClickStartStop() self:safe(function(frame) local adEnv, run = frame.getAdEnv(), frame:getRun() if adEnv == nil or run == nil then return end - if ADRunsController.getRunStatus(adEnv, run).isRunning then + if ADRunsController.getRunStatus(adEnv, run).canStop then ADRunsController.stopRun(adEnv, run.id) frame.statusOverride = nil else diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index 0e9f826..babc9da 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.51.1.0 + 1.51.2.0 <en>AD Smart Pickup</en> diff --git a/tests/test_adFleetNames.lua b/tests/test_adFleetNames.lua index c3acba0..61918de 100644 --- a/tests/test_adFleetNames.lua +++ b/tests/test_adFleetNames.lua @@ -23,5 +23,11 @@ check("parkering: P + navn", byId.a.parkName, "P Puma 260 CVX 1") check("parkering: flow-traktor", byId.d.parkName, "P Cow Food 1") check("omdøb: kun hvis ændret", byId.d.rename, false) check("omdøb: ny navn", byId.a.rename, true) +-- R7: parkeringer omdøbes kun for maskiner der selv omdøbes, og ikke delte parkeringer +check("R7 flow-traktor: parkering røres ikke", byId.d.renamePark, false) +check("R7 selvkørende: parkering røres ikke", byId.e.renamePark, false) +check("R7 omdøbt traktor: parkering omdøbes", byId.a.renamePark, true) +local shared = N.plan({{id = "x", model = "M", park = 5}, {id = "y", model = "Q", park = 5}}, {[5] = 2}) +check("R7 delt parkering røres ikke", shared[1].renamePark, false) print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adFlowWagons.lua b/tests/test_adFlowWagons.lua index a9d1042..5ad6900 100644 --- a/tests/test_adFlowWagons.lua +++ b/tests/test_adFlowWagons.lua @@ -48,7 +48,14 @@ 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") +check("mangler: alle findes", #W.missingBuildings({sourceBuildingIds = {"a"}, targetBuildingIds = {"b"}}, exists, exists), 0) +check("mangler: én væk", W.missingBuildings({targetBuildingIds = {"a", "c"}, storeBuildingIds = {}}, exists, {a = true, c = true})[1], "c") +-- review v1.51 +check("R1 traktoren er lånt ud af puljen -> vent", W.startAction({tractorBusy = true, hasRear = true, wagonId = "w1", wagonExists = true, hasConnect = true}), "wait") +local runs2 = {{id = 1, stopped = true, wagons = {t1 = "w1"}}, {id = 2, stopped = false, wagons = {t9 = "w1"}}} +check("R4 holdt hvis ET kørende flow har vognen", W.isHeld(runs2, "w1"), true) +local seen = {a = true, c = true} +check("R+ bygning aldrig set i sessionen tæller ikke som væk", #W.missingBuildings({targetBuildingIds = {"b"}}, {a = true}, seen), 0) +check("R+ bygning set og nu væk", W.missingBuildings({targetBuildingIds = {"c"}}, {a = true}, seen)[1], "c") print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1) diff --git a/tests/test_adRuns.lua b/tests/test_adRuns.lua index a08b860..d52eacc 100644 --- a/tests/test_adRuns.lua +++ b/tests/test_adRuns.lua @@ -335,5 +335,15 @@ 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) +-- R4: flyttes en traktor til et andet flow (eller fjernes), glemmes dens vogn i det gamle flow +local ps = ADRuns.newStore() +local ps1, prunA = ADRuns.createRun(ps, "A", "supply") +local ps2, prunB = ADRuns.createRun(ps1, "B", "supply") +ps2 = ADRuns.assignVehicle(ps2, prunA.id, "trac") +ADRuns.getRun(ps2, prunA.id).wagons = {trac = "wag"} +local ps3 = ADRuns.assignVehicle(ps2, prunB.id, "trac") +check("R4 gammel flow glemmer vognen", ADRuns.getRun(ps3, prunA.id).wagons.trac, nil) +local ps4 = ADRuns.unassignVehicle(ps2, "trac") +check("R4 fjernet traktor: vogn glemt", ADRuns.getRun(ps4, prunA.id).wagons.trac, nil) print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)