-- Kør: luajit tests/test_adRunsController.lua (fra repo-roden) -- Controlleren oversætter et run til AutoDrive-tilstand pr. traktor. Mock'er AD + de -- få ADSmartPickup-opslag den bruger. local STRAW, TMR, WATER, MILK = 31, 116, 8, 30 local FILL_TYPE_NAMES = {[STRAW] = "STRAW", [TMR] = "FORAGE", [WATER] = "WATER", [MILK] = "MILK"} local FILL_TYPE_INDEX = {} for index, name in pairs(FILL_TYPE_NAMES) do FILL_TYPE_INDEX[name] = index end g_fillTypeManager = { getFillTypeNameByIndex = function(_, index) return FILL_TYPE_NAMES[index] end, getFillTypeIndexByName = function(_, name) return FILL_TYPE_INDEX[name] end, getFillTypes = function() return { {index = STRAW, name = "STRAW", title = "Halm"}, {index = TMR, name = "FORAGE", title = "TMR"}, {index = WATER, name = "WATER", title = "Vand"}, {index = MILK, name = "MILK", title = "Mælk"}, } end, } local logLines = {} Logging = { info = function(fmt, ...) table.insert(logLines, string.format(fmt, ...)) end, warning = function(fmt, ...) table.insert(logLines, "WARN " .. string.format(fmt, ...)) end, } Utils = {prependedFunction = function(original, extra) return function(...) extra(...); return original(...) end end} ItemSystem = {save = function() end} g_currentMission = {getFarmId = function() return 1 end, missionInfo = {}} -- ---- AutoDrive-mock ---- local RT_NONE, RT_ONLYPICKUP = 1, 2 local MODE_PICKUPANDDELIVER = 2 local vehicles, markers, wayPoints = {}, {}, {} FS25_AutoDrive = { AutoDrive = { RT_NONE = RT_NONE, RT_ONLYPICKUP = RT_ONLYPICKUP, MODE_PICKUPANDDELIVER = MODE_PICKUPANDDELIVER, settings = { useFolders = {values = {false, true}, current = 1, isVehicleSpecific = false}, rotateTargets = {values = {1, 2, 3, 4}, current = 1, isVehicleSpecific = true}, ALUnload = {values = {0, 1, 2, 3, 4}, current = 1, isVehicleSpecific = true}, }, getAllVehicles = function() return vehicles end, getAllUnits = function(vehicle) return vehicle.trailers end, getAllFillLevels = function(trailers) return trailers.level or 0, trailers.capacity or 0, false, 0 end, getSupportedFillTypesOfAllUnitsAlphabetically = function(vehicle) return vehicle.carriable or {} end, }, ADGraphManager = { getMapMarkers = function() return markers end, getMapMarkerByWayPointId = function(_, id) for _, m in ipairs(markers) do if m.id == id then return m end end end, getWayPointById = function(_, id) return wayPoints[id] end, getDistanceFromNetwork = function(_, vehicle) return vehicle.networkDistance or 0 end, }, } local function addMarker(id, name, group, x, z) wayPoints[id] = {x = x, z = z, incoming = {}} table.insert(markers, {id = id, markerIndex = #markers + 1, name = name, group = group}) end -- ADSmartPickup-opslag: hvilke waypoints har hvilken slags station, og hvad målet tager local unloadAt, loadAt = {}, {} ADSmartPickup = { getFarmId = function(vehicle) return vehicle.farmId end, getUnloadStationAtWayPointId = function(_, id) return unloadAt[id] end, getLoadStationAtWayPointId = function(_, id) return loadAt[id] end, getStationLevelAndCapacity = function(station, fillType) if station.accepts[fillType] == nil then return nil end return (station.levels or {})[fillType] or 0, station.accepts[fillType] end, -- forenklet rangering: mest fri plads (relativt) først; fulde sidst getTargetNeedsRanked = function(station, fillTypes) local ranked = {} for _, fillType in ipairs(fillTypes) do local capacity = station.accepts[fillType] if capacity ~= nil then local level = (station.levels or {})[fillType] or 0 table.insert(ranked, {fillType = fillType, level = level, capacity = capacity, freeSpace = capacity - level, score = (capacity - level) / capacity}) end end table.sort(ranked, function(a, b) return a.score > b.score end) return ranked end, } ADSupplyPlanner = {isWaitMarker = function(marker) return string.find(string.lower(marker.group or ""), "%f[%a]wait%f[%A]") ~= nil end} local function makeVehicle(uniqueId, name, carriable, capacity, x, z) local stateModule = {selectedFillTypes = {}, fillType = 0, active = false, loopCounter = 5, loopsDone = 3, dirtyCount = 0} stateModule.setMode = function(self, mode) self.mode = mode end stateModule.setFirstMarkerByWayPointId = function(self, id) self.firstMarkerId = id end stateModule.setSecondMarkerByWayPointId = function(self, id) self.secondMarkerId = id end stateModule.setFillType = function(self, fillType) self.fillType = fillType end stateModule.raiseDirtyFlag = function(self) self.dirtyCount = self.dirtyCount + 1 end stateModule.setLoopsDone = function(self, n) self.loopsDone = n end stateModule.isActive = function(self) return self.active end -- Bruges af C13: getTargetFillTypes skal følge AD's LIVE mål (mock'er AD's eget getSecondMarker). stateModule.getSecondMarker = function(self) return self.secondMarkerId ~= nil and {id = self.secondMarkerId} or nil end local vehicle = { uniqueId = uniqueId, farmId = 1, rootNode = uniqueId, carriable = carriable, trailers = {level = 0, capacity = capacity}, getName = function() return name end, getOwnerFarmId = function() return 1 end, startAutoDrive = function(self) self.ad.stateModule.active = true; self.started = (self.started or 0) + 1 end, getClosestWayPoint = function() return nil, 0 end, stopAutoDrive = function(self) self.ad.stateModule.active = false; self.stopped = (self.stopped or 0) + 1 end, ad = {stateModule = stateModule, settings = {rotateTargets = {values = {1, 2, 3, 4}, current = 1, new = 1, isVehicleSpecific = true}, ALUnload = {values = {0, 1, 2, 3, 4}, current = 1, new = 1, isVehicleSpecific = true}}}, } nodePositions = nodePositions or {} nodePositions[uniqueId] = {x = x or 0, z = z or 0} table.insert(vehicles, vehicle) return vehicle end function getWorldTranslation(node) local p = nodePositions[node]; return p.x, 0, p.z end dofile("FS25_ADSmartPickup/adRuns.lua") dofile("FS25_ADSmartPickup/adRunsStorage.lua") dofile("FS25_ADSmartPickup/adWaitPool.lua") dofile("FS25_ADSmartPickup/adRunsController.lua") local adEnv = FS25_AutoDrive 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 local function buildWorld() vehicles, markers, wayPoints, unloadAt, loadAt, logLines = {}, {}, {}, {}, {}, {} ADRunsController.store = ADRuns.newStore() addMarker(94, "Cow 1 Food", "Cow Food", 5000, 0); unloadAt[94] = {accepts = {[STRAW] = 300000, [TMR] = 300000, [WATER] = 50000}} addMarker(44, "10 Silo Storage", "Siloer", 0, 0); loadAt[44] = {} addMarker(75, "TMR Load", "Fabrication", 900, 0); loadAt[75] = {} addMarker(100, "Cow Wait", "Cow Wait", 5100, 0) addMarker(101, "Cow Wait", "Cow Wait", 5200, 0) addMarker(200, "F91 Wait", "F91", 8000, 0) addMarker(300, "Pig Wait", "Pig Wait", 9000, 0) end -- C1: køretøjs-id og farmens AD-køretøjer buildWorld() local fastracA = makeVehicle("vehicleA", "Fastrac A", {STRAW, TMR}, 1000000, 100, 0) local fastracB = makeVehicle("vehicleB", "Fastrac B", {STRAW}, 700000, 4000, 0) local noAd = {uniqueId = "vehicleC", getOwnerFarmId = function() return 1 end} table.insert(vehicles, noAd) local otherFarm = makeVehicle("vehicleD", "Nabo", {STRAW}, 1000, 0, 0); otherFarm.getOwnerFarmId = function() return 2 end check("C1 uniqueId", ADRunsController.getVehicleId(fastracA), "vehicleA") check("C1 fallback savegame-id", ADRunsController.getVehicleId({currentSavegameId = 7}), "7") check("C1 intet id -> nil", ADRunsController.getVehicleId({}), nil) check("C1 farmens AD-køretøjer", #ADRunsController.getFarmVehicles(adEnv), 2) check("C1 opslag på id", ADRunsController.findVehicleById(adEnv, "vehicleB"), fastracB) check("C1 ukendt id -> nil", ADRunsController.findVehicleById(adEnv, "nope"), nil) -- C2: opret run, tildel; opslag run for køretøj; flyt mellem runs local cow = ADRunsController.createRun("Cow Feed") check("C2 run oprettet", ADRuns.getRun(ADRunsController.store, cow.id).name, "Cow Feed") check("C2 ingen tidligere", ADRunsController.assignVehicle(cow.id, fastracA), nil) check("C2 run for køretøj", ADRunsController.getRunForVehicle(fastracA).id, cow.id) check("C2 ikke tildelt -> nil", ADRunsController.getRunForVehicle(fastracB), nil) local pig = ADRunsController.createRun("Pig Feed") check("C2 flyttet fra Cow", ADRunsController.assignVehicle(pig.id, fastracA), cow.id) check("C2 nu i Pig", ADRunsController.getRunForVehicle(fastracA).id, pig.id) ADRunsController.unassignVehicle(fastracA) check("C2 fjernet", ADRunsController.getRunForVehicle(fastracA), nil) ADRunsController.deleteRun(pig.id) check("C2 slettet", ADRuns.getRun(ADRunsController.store, pig.id), nil) -- C3: Auto-vareliste = vognens varer ∩ målets varer; manual = navne -> indeks (ukendte droppes) ADRunsController.updateRun(cow.id, {targetWayPointId = 94}) local cowRun = ADRuns.getRun(ADRunsController.store, cow.id) check("C3 auto: halm+TMR (vand kan vognen ikke bære)", table.concat(ADRunsController.getRunIngredients(adEnv, cowRun, fastracA), ","), STRAW .. "," .. TMR) check("C3 auto: kun halm for halm-vogn", table.concat(ADRunsController.getRunIngredients(adEnv, cowRun, fastracB), ","), tostring(STRAW)) ADRunsController.updateRun(cow.id, {ingredientMode = "manual", ingredients = {"FORAGE", "UNKNOWN_THING"}}) cowRun = ADRuns.getRun(ADRunsController.store, cow.id) check("C3 manual: FORAGE -> indeks, ukendt droppes", table.concat(ADRunsController.getRunIngredients(adEnv, cowRun, fastracA), ","), tostring(TMR)) ADRunsController.updateRun(cow.id, {ingredientMode = "auto"}) local noTarget = ADRunsController.createRun("Uden mål") check("C3 uden mål -> tom liste", #ADRunsController.getRunIngredients(adEnv, ADRuns.getRun(ADRunsController.store, noTarget.id), fastracA), 0) -- C4: validering cowRun = ADRuns.getRun(ADRunsController.store, cow.id) check("C4 ok", ADRunsController.validateVehicle(adEnv, cowRun, fastracA).ok, true) check("C4 kapacitet", ADRunsController.validateVehicle(adEnv, cowRun, fastracA).capacity, 1000000) local noTrailer = makeVehicle("vehicleE", "Uden vogn", {}, 0) check("C4 ingen vogn", ADRunsController.validateVehicle(adEnv, cowRun, noTrailer).reason, "noTrailer") local milkTruck = makeVehicle("vehicleF", "Mælkebil", {MILK}, 20000) check("C4 kan ikke bære", ADRunsController.validateVehicle(adEnv, cowRun, milkTruck).reason, "cannotCarry") fastracB.networkDistance = 31 check("C4 for langt fra vejnettet", ADRunsController.validateVehicle(adEnv, cowRun, fastracB).reason, "farFromNetwork") fastracB.networkDistance = 0 check("C4 intet mål", ADRunsController.validateVehicle(adEnv, ADRuns.getRun(ADRunsController.store, noTarget.id), fastracA).reason, "noTarget") ADRunsController.updateRun(noTarget.id, {targetWayPointId = 4242}) check("C4 mål-markør findes ikke", ADRunsController.validateVehicle(adEnv, ADRuns.getRun(ADRunsController.store, noTarget.id), fastracA).reason, "noTarget") -- C5: Start skriver AD-tilstand og starter; ✖-traktorer springes over ADRunsController.updateRun(cow.id, {loops = 4, waitPoolGroup = "Cow Wait"}) ADRunsController.assignVehicle(cow.id, fastracA) ADRunsController.assignVehicle(cow.id, noTrailer) local results = ADRunsController.startRun(adEnv, cow.id) check("C5 to resultater", #results, 2) check("C5 A startet", fastracA.started, 1) check("C5 A ok", results[1].ok, true) check("C5 uden vogn sprunget over", noTrailer.started, nil) check("C5 uden vogn grund", results[2].reason, "noTrailer") local state = fastracA.ad.stateModule check("C5 mode P&D", state.mode, MODE_PICKUPANDDELIVER) check("C5 mål = run'ets mål", state.secondMarkerId, 94) check("C5 start-pickup = nærmeste læssestation", state.firstMarkerId, 44) check("C5 varer = auto-listen", table.concat(state.selectedFillTypes, ","), STRAW .. "," .. TMR) check("C5 fillType = første", state.fillType, STRAW) check("C5 loops", state.loopCounter, 4) check("C5 loopsDone nulstillet", state.loopsDone, 0) check("C5 rotateTargets = kun pålæsning (current)", fastracA.ad.settings.rotateTargets.current, 2) check("C5 rotateTargets = kun pålæsning (new)", fastracA.ad.settings.rotateTargets.new, 2) check("C5 useFolders globalt til", adEnv.AutoDrive.settings.useFolders.current, 2) check("C5 dirty flag", state.dirtyCount > 0, true) check("C5 status", ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, cow.id)).running, 1) check("C5 total", ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, cow.id)).total, 2) check("C5 ukendt run -> tom", #ADRunsController.startRun(adEnv, 999), 0) -- C6: Stop check("C6 stoppede", ADRunsController.stopRun(adEnv, cow.id), 1) check("C6 A stoppet", fastracA.stopped, 1) check("C6 status 0", ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, cow.id)).running, 0) check("C6 medlemskab bevaret", ADRunsController.getRunForVehicle(fastracA).id, cow.id) -- C7: Start på traktor der allerede kører AD (fx startet manuelt) -> stoppes og startes på run'et fastracA.ad.stateModule.active = true fastracA.stopped = 0 ADRunsController.startRun(adEnv, cow.id) check("C7 kørende traktor stoppes først", fastracA.stopped, 1) check("C7 og startes på run'et", fastracA.started, 2) check("C7 run markeret som kørende", ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, cow.id)).isRunning, true) ADRunsController.stopRun(adEnv, cow.id) check("C7 stop rydder markeringen", ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, cow.id)).isRunning, false) -- en traktor der kører AD uden at run'et er startet af os, gør IKKE run'et "kørende" fastracA.ad.stateModule.active = true check("C7 manuel AD tæller ikke som kørende run", ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, cow.id)).isRunning, false) check("C7 men aktive tæller stadig", ADRunsController.getRunStatus(adEnv, ADRuns.getRun(ADRunsController.store, cow.id)).running, 1) fastracA.ad.stateModule.active = false -- sprungne traktorer logges med årsag logLines = {} ADRunsController.startRun(adEnv, cow.id) local skippedLogged = false for _, line in ipairs(logLines) do if line:find("sprunget over", 1, true) and line:find("noTrailer", 1, true) then skippedLogged = true end end check("C7 sprunget over logges med årsag", skippedLogged, true) -- kapacitet under 100 l (kun trækkerens egne tanke) = ingen vogn local tinyCapacity = makeVehicle("vehicleTiny", "T255", {STRAW, TMR}, 1, 100, 0) check("C7 1 l kapacitet -> noTrailer", ADRunsController.validateVehicle(adEnv, ADRuns.getRun(ADRunsController.store, cow.id), tinyCapacity).reason, "noTrailer") -- UAL-køretøj: AutoDrive melder kapacitet i PALLER (læssede + 1), så en tom pallebil har "1" — det er ikke "ingen vogn" adEnv.AutoDrive.hasAL = function(_, unit) return unit.ual == true end local palletTruck = makeVehicle("vehicleDragon", "Dragon", {}, 1, 100, 0) palletTruck.trailers[1] = {ual = true} local truckValidation = ADRunsController.validateVehicle(adEnv, ADRuns.getRun(ADRunsController.store, cow.id), palletTruck) check("C7 pallebil: ikke noTrailer", truckValidation.reason ~= "noTrailer", true) check("C7 pallebil: kapacitet i paller markeret", truckValidation.isAutoload, true) check("C7 pallebil: liter-kapacitet ukendt", truckValidation.capacity, nil) adEnv.AutoDrive.hasAL = nil -- C8: mål-markører (kun stalde og fabrikker — siloer tager alt og er ikke mål; stalde først, -- så fabrikker, så navn) og Wait-puljer til dropdowns unloadAt[94].owningPlaceable = {getHusbandryFillLevel = function() return 0 end} addMarker(96, "8 Silo Storage", "Siloer", 100, 0); unloadAt[96] = {accepts = {[STRAW] = 1}, owningPlaceable = {spec_silo = {}}} addMarker(97, "TMR Unload", "Fabrication", 900, 0); unloadAt[97] = {accepts = {[STRAW] = 1}, owningPlaceable = {spec_productionPoint = {}}} addMarker(98, "Bull Food", "Cow Food", 5100, 0); unloadAt[98] = {accepts = {[STRAW] = 1}, owningPlaceable = {getHusbandryFillLevel = function() return 0 end}} local targets = ADRunsController.getTargetMarkers(adEnv, 1) check("C8 kun stalde og fabrikker (ikke siloen)", #targets, 3) check("C8 stalde først, alfabetisk", targets[1].name .. "," .. targets[2].name, "Bull Food,Cow 1 Food") check("C8 fabrik sidst", targets[3].name, "TMR Unload") local pools = ADRunsController.getWaitPools(adEnv) check("C8 to puljer (F91 er ikke en Wait-mappe)", #pools, 2) check("C8 sorteret", pools[1].group, "Cow Wait") check("C8 antal", pools[1].count, 2) -- C9: gem/indlæs; ukendte køretøjs-id'er logges ved indlæsning men beholdes i run'et local savedRecords ADRunsStorage.save = function(store) savedRecords = ADRuns.toRecords(store); return true end ADRunsStorage.load = function() return ADRuns.fromRecords(savedRecords) end ADRunsController.assignVehicle(cow.id, {uniqueId = "vehicleGONE", ad = {}, getOwnerFarmId = function() return 1 end, getName = function() return "Væk" end}) check("C9 gemt", ADRunsController.save(), true) ADRunsController.store = ADRuns.newStore() ADRunsController.load(adEnv) check("C9 indlæst", ADRuns.getRun(ADRunsController.store, cow.id).name, "Cow Feed") check("C9 ukendt køretøj logget", logLines[#logLines]:find("vehicleGONE", 1, true) ~= nil, true) check("C9 run beholder kendte", ADRunsController.getRunForVehicle(fastracA).id, cow.id) local hooked = false ItemSystem.save = function() hooked = true end ADRunsController.installSaveHook() ItemSystem.save() check("C9 save-hook kaldes før ItemSystem.save", hooked, true) -- C11: vogn uden egen ad.settings-tabel (ingen rotateTargets pr. køretøj) -> den -- køretøjs-specifikke indstilling skrives IKKE til den globale settings-tabel adEnv.AutoDrive.settings.rotateTargets.current = 1 adEnv.AutoDrive.settings.useFolders.current = 1 local settingsRun = ADRunsController.createRun("Settings Test") ADRunsController.updateRun(settingsRun.id, {targetWayPointId = 94, loops = 1}) local noSettings = makeVehicle("vehicleH", "Uden AD-settings", {STRAW}, 500000, 200, 0) noSettings.ad.settings = {} ADRunsController.assignVehicle(settingsRun.id, noSettings) ADRunsController.startRun(adEnv, settingsRun.id) check("C11 global rotateTargets urørt", adEnv.AutoDrive.settings.rotateTargets.current, 1) check("C11 useFolders global stadig skrevet", adEnv.AutoDrive.settings.useFolders.current, 2) check("C11 køretøjet startet", noSettings.started, 1) -- C12: startAutoDrive() kaster for én traktor -> pcall'et applyAndStart fanger fejlen, -- resultatet markeres ok=false/reason="error", og de ØVRIGE traktorer i run'et startes stadig -- (fejlen må ikke undslippe startRun-loopet). buildWorld() local brokenRun = ADRunsController.createRun("Broken Start") ADRunsController.updateRun(brokenRun.id, {targetWayPointId = 94, loops = 2}) local broken = makeVehicle("vehicleBroken", "Fejl-traktor", {STRAW, TMR}, 500000, 100, 0) broken.startAutoDrive = function() error("boom") end local normal = makeVehicle("vehicleNormal", "Normal-traktor", {STRAW, TMR}, 500000, 200, 0) ADRunsController.assignVehicle(brokenRun.id, broken) ADRunsController.assignVehicle(brokenRun.id, normal) local c12Results = ADRunsController.startRun(adEnv, brokenRun.id) check("C12 to resultater", #c12Results, 2) check("C12 fejl-traktor ok=false", c12Results[1].ok, false) check("C12 fejl-traktor reason=error", c12Results[1].reason, "error") check("C12 fejl-traktor ikke startet", broken.started, nil) check("C12 normal-traktor ok=true", c12Results[2].ok, true) check("C12 normal-traktor startet", normal.started, 1) -- C13: Auto-listen følger traktorens LIVE AD-mål (stateModule:getSecondMarker()) mens den kører, -- IKKE run'ets gemte targetWayPointId — ellers afviger listen fra det choosePickup/findSupplyPickup -- faktisk bruger, hvis brugeren redigerer run'ets Mål mens traktoren kører. addMarker(95, "Alt Cow Food", "Cow Food", 5300, 0) unloadAt[95] = {accepts = {[STRAW] = 1000}} local liveRun = ADRunsController.createRun("Live Target") ADRunsController.updateRun(liveRun.id, {targetWayPointId = 94}) local liveRunData = ADRuns.getRun(ADRunsController.store, liveRun.id) local liveVehicle = makeVehicle("vehicleLive", "Live-traktor", {STRAW, TMR}, 500000, 300, 0) check("C13 inaktiv: auto-liste fra run'ets mål (94)", table.concat(ADRunsController.getRunIngredients(adEnv, liveRunData, liveVehicle), ","), STRAW .. "," .. TMR) liveVehicle.ad.stateModule.active = true liveVehicle.ad.stateModule.secondMarkerId = 95 check("C13 aktiv: auto-liste fra AD's live mål (95)", table.concat(ADRunsController.getRunIngredients(adEnv, liveRunData, liveVehicle), ","), tostring(STRAW)) liveVehicle.ad.stateModule.active = false liveVehicle.ad.stateModule.secondMarkerId = nil -- C10: målets accepterede varer (til afkrydsning i GUI'et) + køretøjs-label buildWorld() local labelA = makeVehicle("vehicleA", "Fastrac A", {STRAW, TMR}, 1000000) local labelB = makeVehicle("vehicleB", "Fastrac B", {STRAW}, 700000) local accRun = ADRunsController.createRun("Cow Feed") check("C10 uden mål -> tom", #ADRunsController.getTargetAcceptedFillTypes(adEnv, ADRuns.getRun(ADRunsController.store, accRun.id)), 0) ADRunsController.updateRun(accRun.id, {targetWayPointId = 94}) local accepted = ADRunsController.getTargetAcceptedFillTypes(adEnv, ADRuns.getRun(ADRunsController.store, accRun.id)) check("C10 tre varer (halm, TMR, vand)", #accepted, 3) check("C10 sorteret på titel", accepted[1].title .. "," .. accepted[2].title .. "," .. accepted[3].title, "Halm,TMR,Vand") check("C10 navn med", accepted[1].name, "STRAW") check("C10 label", ADRunsController.getVehicleLabel(adEnv, labelB), "Fastrac B (#2)") -- C14: Start går gennem AD's mode:start(USER_PLAYER) (som HUD'en), ikke kun startAutoDrive buildWorld() local modeVehicle = makeVehicle("vehicleM", "Mode-traktor", {STRAW, TMR}, 1000000, 100, 0) local modeStarts = {} modeVehicle.ad.stateModule.getCurrentMode = function() return {start = function(_, user) table.insert(modeStarts, user); modeVehicle:startAutoDrive() end} end modeVehicle.ad.stateModule.setActualFarmId = function(self, farmId) self.actualFarmId = farmId end adEnv.AutoDrive.USER_PLAYER = 1 local modeRun = ADRunsController.createRun("Mode") ADRunsController.updateRun(modeRun.id, {targetWayPointId = 94}) ADRunsController.assignVehicle(modeRun.id, modeVehicle) ADRunsController.startRun(adEnv, modeRun.id) check("C14 mode:start kaldt med USER_PLAYER", modeStarts[1], 1) check("C14 traktor aktiv", modeVehicle.started, 1) check("C14 farm-id sat", modeVehicle.ad.stateModule.actualFarmId, 1) check("C14 loopsDone nulstillet", modeVehicle.ad.stateModule.loopsDone, 0) -- C15: aktivitet til overblikket — stoppet / venter / med læs / tom på vej efter vare ADUnloadWait = {getWaitFor = function(v) return v.waitInfo end, getRestLoad = function(_, v) return v.loadType, v.loadLiters or 0 end} ADSmartPickup.getActivePickup = function(v) return v.pickupInfo end buildWorld() local actVehicle = makeVehicle("vehicleAct", "Akt", {STRAW, TMR}, 1000000, 100, 0) check("C15 stoppet", ADRunsController.getVehicleActivity(adEnv, actVehicle).kind, "stopped") actVehicle.ad.stateModule.active = true actVehicle.ad.stateModule.secondMarkerId = 94 actVehicle.loadType, actVehicle.loadLiters = STRAW, 8000 local loaded = ADRunsController.getVehicleActivity(adEnv, actVehicle) check("C15 med læs", loaded.kind .. "/" .. loaded.fillType .. "/" .. loaded.liters, "loaded/" .. STRAW .. "/8000") actVehicle.loadLiters = 0 actVehicle.pickupInfo = {markerName = "Silo 3", fillType = TMR} local empty = ADRunsController.getVehicleActivity(adEnv, actVehicle) check("C15 tom på vej efter TMR", empty.kind .. "/" .. tostring(empty.place) .. "/" .. tostring(empty.fillType), "empty/Silo 3/" .. TMR) actVehicle.waitInfo = {markerName = "Cow Wait", fillType = STRAW, rest = 5000} check("C15 venter", ADRunsController.getVehicleActivity(adEnv, actVehicle).kind, "waiting") ADUnloadWait = nil -- C16: flere mål pr. run — Auto-varer = union; chooseTarget = den stald der mangler mest; validering buildWorld() addMarker(99, "Cow 2 Food", "Cow Food", 5200, 0); unloadAt[99] = {accepts = {[STRAW] = 300000, [TMR] = 300000}, levels = {[STRAW] = 0, [TMR] = 0}} unloadAt[94].levels = {[STRAW] = 250000, [TMR] = 250000, [WATER] = 50000} local multiVehicle = makeVehicle("vehicleMulti", "Multi", {STRAW, TMR, WATER}, 1000000, 100, 0) local multi = ADRunsController.createRun("Kvæg") ADRunsController.updateRun(multi.id, {targetWayPointIds = {94, 99}}) local multiRun = ADRuns.getRun(ADRunsController.store, multi.id) check("C16 auto-varer = union (vand kun i stald 1)", table.concat(ADRunsController.getRunIngredients(adEnv, multiRun, multiVehicle), ","), WATER .. "," .. STRAW .. "," .. TMR) check("C16 accepterede varer = union", #ADRunsController.getTargetAcceptedFillTypes(adEnv, multiRun), 3) check("C16 vælger stald 2 (tom)", ADRunsController.chooseTarget(adEnv, multiRun, multiVehicle), 99) unloadAt[99].levels = {[STRAW] = 290000, [TMR] = 290000} check("C16 vælger stald 1 når stald 2 er fuld", ADRunsController.chooseTarget(adEnv, multiRun, multiVehicle), 94) check("C16 validering ok med flere mål", ADRunsController.validateVehicle(adEnv, multiRun, multiVehicle).ok, true) ADRunsController.updateRun(multi.id, {targetWayPointIds = {4242}}) check("C16 kun ukendte mål -> noTarget", ADRunsController.validateVehicle(adEnv, ADRuns.getRun(ADRunsController.store, multi.id), multiVehicle).reason, "noTarget") -- C17: kørende traktor der fjernes fra run stoppes; slettes run, stoppes dets traktorer; Stop logger buildWorld() local runVehicle = makeVehicle("vehicleR", "Kørende", {STRAW, TMR}, 1000000, 100, 0) local stopRun = ADRunsController.createRun("Stop-test") ADRunsController.updateRun(stopRun.id, {targetWayPointId = 94}) ADRunsController.assignVehicle(stopRun.id, runVehicle) ADRunsController.startRun(adEnv, stopRun.id) check("C17 kører", runVehicle.started, 1) ADRunsController.unassignVehicle(runVehicle) check("C17 fjernet fra run -> stoppet", runVehicle.stopped, 1) ADRunsController.assignVehicle(stopRun.id, runVehicle) ADRunsController.startRun(adEnv, stopRun.id) ADRunsController.deleteRun(stopRun.id, adEnv) check("C17 run slettet -> stoppet", runVehicle.stopped, 2) check("C17 stop logget", logLines[#logLines]:find("stoppet", 1, true) ~= nil, true) -- C18: forkert last ved Start (halm på vognen, run mod sukkerfabrik) -> første stop = silo der tager halm ADUnloadWait = {getRestLoad = function(_, v) return v.loadType, v.loadLiters or 0 end, getWaitFor = function() return nil end, hasRouteBothWays = function() return true end} ADLoadSwap = {findDumpMarker = function(_, v, fillType, rest, homeId, isReachable) return {id = 44, name = "10 Silo Storage", markerIndex = 2} end} buildWorld() addMarker(77, "sugar offload", "Fabrication", 950, 0); unloadAt[77] = {accepts = {[TMR] = 360000}} -- "fabrik" der ikke tager halm (TMR står for roer her) local sugarVehicle = makeVehicle("vehicleSugar", "Roe-traktor", {STRAW, TMR}, 20000, 100, 0) sugarVehicle.loadType, sugarVehicle.loadLiters = STRAW, 8000 local sugar = ADRunsController.createRun("Sugar") ADRunsController.updateRun(sugar.id, {targetWayPointId = 77}) ADRunsController.assignVehicle(sugar.id, sugarVehicle) ADRunsController.startRun(adEnv, sugar.id) check("C18 første stop = siloen (aflæs forkert last)", sugarVehicle.ad.stateModule.secondMarkerId, 44) check("C18 logget", logLines[#logLines]:find("læsser", 1, true) ~= nil or logLines[#logLines - 1]:find("læsser", 1, true) ~= nil, true) -- rigtig last (TMR=roer) -> direkte til fabrikken sugarVehicle.loadType = TMR sugarVehicle.ad.stateModule.active = false ADRunsController.startRun(adEnv, sugar.id) check("C18 rigtig last -> målet", sugarVehicle.ad.stateModule.secondMarkerId, 77) ADUnloadWait = nil; ADLoadSwap = nil -- C19: Wait-markører er aldrig mål — heller ikke når de står inden for 40 m af staldens trigger buildWorld() -- staldens station med bygning; Wait-markøren ligger i triggerens rækkevidde og "mangler mere" end 94 local barnPlaceable = {getHusbandryFillLevel = function() return 0 end} unloadAt[94].owningPlaceable = barnPlaceable addMarker(400, "Cow Wait", "Cow Wait", 5005, 0); unloadAt[400] = {accepts = {[STRAW] = 300000, [TMR] = 300000}, levels = {}, owningPlaceable = barnPlaceable} local targetNames = {} for _, marker in ipairs(ADRunsController.getTargetMarkers(adEnv, 1)) do table.insert(targetNames, marker.name) end check("C19 Wait-markør ikke i mål-listen", table.concat(targetNames, ","):find("Cow Wait", 1, true), nil) local waitTargetRun = ADRunsController.createRun("Wait som mål") ADRunsController.updateRun(waitTargetRun.id, {targetWayPointIds = {94, 400}}) local waitVehicle = makeVehicle("vehicleW", "W", {STRAW, TMR}, 20000, 5000, 0) check("C19 chooseTarget springer Wait over", ADRunsController.chooseTarget(adEnv, ADRuns.getRun(ADRunsController.store, waitTargetRun.id), waitVehicle), 94) -- C-out: udkørsels-runs — oprettelse, kilder, produkter, validering, start, status local BASKET = 7 FILL_TYPE_NAMES[BASKET] = "STRAW_BASKET"; FILL_TYPE_INDEX["STRAW_BASKET"] = BASKET local sourcesAt, productsAt = {}, {} ADSources = { identify = function(_, id) return sourcesAt[id] end, getProducts = function(_, source) return productsAt[source.wayPointId] or {} end, getSellMarkers = function() return {} end, stationAccepts = function() return false end, getSourcePlaceables = function() return {} end, } local outboundStatus = {reasons = {"SUGAR 78 % af top (kræver 90), lager 41 %"}, warning = nil} local holdFor = {} ADOutbound = { isOutboundVehicle = function(v) local r = ADRunsController.getRunForVehicle(v) return r ~= nil and r.kind == "outbound" end, getRunStatus = function() return outboundStatus end, getHoldFor = function(v) return holdFor[v] end, getTripFor = function() return nil end, releaseInactive = function() end, } ADUnloadWait = {getRestLoad = function() return nil, 0 end, getWaitFor = function() return nil end} buildWorld() addMarker(1, "Suger Load", "Pickup points", 100, 0); loadAt[1] = {} addMarker(3, "Objektlager", "Pickup points", 300, 0) addMarker(10, "selling station", "selling", 500, 0) sourcesAt[1] = {kind = "factory", wayPointId = 1, station = loadAt[1], name = "Sugar mill"} sourcesAt[3] = {kind = "objectStorage", wayPointId = 3, name = "Objektlager"} productsAt[1] = {{fillType = TMR, name = "FORAGE", title = "TMR", available = 150000, level = 150000, capacity = 180000, isPallet = false}} productsAt[3] = {{fillType = BASKET, name = "STRAW_BASKET", title = "Halmkurve", available = 8000, level = 8, capacity = 4000, isPallet = true}} local bulkTractor = makeVehicle("vehicleBulk", "MT635", {STRAW, TMR}, 20000, 100, 0) local ualTractor = makeVehicle("vehicleUal", "Fendt", {}, 8000, 300, 0) ualTractor.trailers[1] = {ual = true} adEnv.AutoDrive.hasAL = function(_, trailer) return trailer.ual == true end local out = ADRunsController.createRun("Sugar out", "outbound") check("Cout1 kind", ADRuns.getRun(ADRunsController.store, out.id).kind, "outbound") check("Cout1 supply-run har kind supply", ADRunsController.createRun("Cows").kind, "supply") check("Cout1 kilde-markører", #ADRunsController.getSourceMarkers(adEnv, 1), 2) check("Cout1 første kilde-markør", ADRunsController.getSourceMarkers(adEnv, 1)[1].marker.name, "Objektlager") -- Cout2: validering local outRun = ADRuns.getRun(ADRunsController.store, out.id) -- diagnose: hvad moden ser på enhederne, når ingen autoload findes (logges én gang pr. traktor, -- første gang varerne slås op — dvs. allerede ved noProducts-tjekket nedenfor) logLines = {} bulkTractor.trailers[1] = {spec_universalAutoload = {isAutoloadAvailable = false, autoloadDisabled = true}, getName = function() return "Vogn" end} check("Cout2 ingen kilder -> noSource", ADRunsController.validateVehicle(adEnv, outRun, bulkTractor).reason, "noSource") ADRunsController.store = ADRuns.toggleSource(ADRunsController.store, out.id, 1) ADRunsController.store = ADRuns.toggleSource(ADRunsController.store, out.id, 3) outRun = ADRuns.getRun(ADRunsController.store, out.id) check("Cout2 ingen vare valgt -> noProducts", ADRunsController.validateVehicle(adEnv, outRun, bulkTractor).reason, "noProducts") ADRunsController.store = ADRuns.setProductPolicy(ADRunsController.store, out.id, "STRAW_BASKET", "sell") outRun = ADRuns.getRun(ADRunsController.store, out.id) check("Cout2 kun paller uden UAL -> needsAutoload", ADRunsController.validateVehicle(adEnv, outRun, bulkTractor).reason, "needsAutoload") local diag = nil for _, line in ipairs(logLines) do if line:find("ingen autoload", 1, true) then diag = line end end check("Cout2 diagnose logges", diag ~= nil, true) check("Cout2 diagnose viser UAL-flag", diag ~= nil and diag:find("isAutoloadAvailable=false", 1, true) ~= nil, true) logLines = {} ADRunsController.validateVehicle(adEnv, outRun, bulkTractor) check("Cout2 diagnose kun én gang", #logLines, 0) bulkTractor.trailers[1] = nil check("Cout2 UAL-vogn ok", ADRunsController.validateVehicle(adEnv, outRun, ualTractor).ok, true) ADRunsController.store = ADRuns.setProductPolicy(ADRunsController.store, out.id, "FORAGE", "store") outRun = ADRuns.getRun(ADRunsController.store, out.id) check("Cout2 bulk-vogn ok med TMR", ADRunsController.validateVehicle(adEnv, outRun, bulkTractor).ok, true) local strawOnly = makeVehicle("vehicleStraw", "Halm", {STRAW}, 20000, 100, 0) check("Cout2 kan ikke bære -> cannotCarry", ADRunsController.validateVehicle(adEnv, outRun, strawOnly).reason, "cannotCarry") -- Cout3: start sætter mode, 1./2. markør = første kilde, varevalg = det vognen kan bære af run'ets varer ADRunsController.assignVehicle(out.id, bulkTractor) ADRunsController.assignVehicle(out.id, ualTractor) local results = ADRunsController.startRun(adEnv, out.id) check("Cout3 to startet", #results, 2) check("Cout3 mode P&D", bulkTractor.ad.stateModule.mode, MODE_PICKUPANDDELIVER) check("Cout3 1. markør = første kilde", bulkTractor.ad.stateModule.firstMarkerId, 1) check("Cout3 2. markør = første kilde (pladsholder)", bulkTractor.ad.stateModule.secondMarkerId, 1) check("Cout3 bulk-vogn: kun TMR", table.concat(bulkTractor.ad.stateModule.selectedFillTypes, ","), tostring(TMR)) check("Cout3 UAL-vogn: kurve", table.concat(ualTractor.ad.stateModule.selectedFillTypes, ","), tostring(BASKET)) check("Cout3 rotation kun pålæsning", bulkTractor.ad.settings.rotateTargets.current, 2) check("Cout3 startet", bulkTractor.started, 1) -- AD's "AL unload" er slået FRA som standard (values[1] = 0): autoload-vognen ville aldrig læsse af -- ved salgsstedet. Ved Start sættes den til "center" hvis den er fra; et brugervalg (left/right/behind) røres ikke. check("Cout3 ALUnload sat til center for autoload-bil", ualTractor.ad.settings.ALUnload.current, 2) check("Cout3 ALUnload rører ikke tipvogn", bulkTractor.ad.settings.ALUnload.current, 1) ualTractor.ad.settings.ALUnload.current = 4 ualTractor.ad.stateModule.active = false ADRunsController.startRun(adEnv, out.id) check("Cout3 ALUnload brugervalg bevares", ualTractor.ad.settings.ALUnload.current, 4) -- Cout4: produkter til fanen (politik slået op, sorteret på titel) og status local list = ADRunsController.getRunProducts(adEnv, outRun) check("Cout4 to produkter", #list, 2) check("Cout4 første = Halmkurve", list[1].title, "Halmkurve") check("Cout4 politik", list[1].policy, "sell") check("Cout4 TMR lager", list[2].policy, "store") check("Cout4 available", list[2].available, 150000) local st = ADRunsController.getOutboundStatus(adEnv, outRun) check("Cout4 status-begrundelse", st.reasons[1], "SUGAR 78 % af top (kræver 90), lager 41 %") holdFor[bulkTractor] = {markerName = "Wait 3", reasons = {"SUGAR 78 % af top (kræver 90), lager 41 %"}} local activity = ADRunsController.getVehicleActivity(adEnv, bulkTractor) check("Cout4 aktivitet = holder", activity.kind, "holding") check("Cout4 aktivitet sted", activity.place, "Wait 3") check("Cout4 udkørsel har ingen mål", #ADRunsController.getRunTargets(outRun), 0) -- Cout5 (I2): Start kræver et Wait-punkt i puljen; Cout6 (I3): last på vognen ved Start får eget mål ADSupplyPlanner.isWaitMarker = function(marker) return string.find(string.lower(marker.group or ""), "%f[%a]wait%f[%A]") ~= nil end local noWait = ADRunsController.createRun("No wait", "outbound") ADRunsController.store = ADRuns.toggleSource(ADRunsController.store, noWait.id, 1) ADRunsController.store = ADRuns.setProductPolicy(ADRunsController.store, noWait.id, "FORAGE", "store") ADRunsController.store = ADRuns.updateRun(ADRunsController.store, noWait.id, {waitPoolGroup = "Pig Wait"}) ADRunsController.store = ADRuns.updateRun(ADRunsController.store, noWait.id, {waitPoolGroup = "Findes ikke"}) local noWaitRun = ADRuns.getRun(ADRunsController.store, noWait.id) check("Cout5 ingen Wait-punkt i puljen -> noWaitPoint", ADRunsController.validateVehicle(adEnv, noWaitRun, bulkTractor).reason, "noWaitPoint") ADRunsController.store = ADRuns.updateRun(ADRunsController.store, noWait.id, {waitPoolGroup = "Cow Wait"}) noWaitRun = ADRuns.getRun(ADRunsController.store, noWait.id) check("Cout5 Wait-punkt findes -> ok", ADRunsController.validateVehicle(adEnv, noWaitRun, bulkTractor).ok, true) ADUnloadWait.getRestLoad = function(_, v) return v.loadType, v.loadLiters or 0 end ADOutbound.planCargoTarget = function(adEnv, run, vehicle, fillType) return fillType == TMR and 10 or nil end bulkTractor.loadType, bulkTractor.loadLiters = TMR, 8000 ADRunsController.assignVehicle(noWait.id, bulkTractor) bulkTractor.ad.stateModule.active = false ADRunsController.startRun(adEnv, noWait.id) check("Cout6 last på vognen -> 2. markør = lastens mål", bulkTractor.ad.stateModule.secondMarkerId, 10) bulkTractor.loadType = STRAW check("Cout6 last uden mål -> cargoNoTarget", ADRunsController.validateVehicle(adEnv, noWaitRun, bulkTractor).reason, "cargoNoTarget") bulkTractor.loadType = nil ADUnloadWait = nil; ADOutbound = nil; ADSources = nil -- C-flow: bygninger -> markører (resolveMarkers), migration af gamle markør-runs, noMarker, flow-rækker local buildings = { {id = "barn1", name = "Kostald 1", kind = "husbandry", loadMarker = {id = 10, name = "cow 1 milk", distance = 5}, loadMarkers = {{id = 10, name = "cow 1 milk", distance = 5}, {id = 11, name = "cow 1 slurry", distance = 3}}, unloadMarker = {id = 94, name = "Cow 1 Food", distance = 8}, canBeSource = true, canBeTarget = true, canSell = false}, {id = "shed", name = "Palleskur", kind = "objectStorage", loadMarker = nil, unloadMarker = nil, canBeSource = true, canBeTarget = false, canSell = false}, {id = "mill", name = "Sukkerfabrik", kind = "factory", loadMarker = {id = 12694, name = "Suger Load", distance = 3}, unloadMarker = {id = 77, name = "sugar offload", distance = 4}, canBeSource = true, canBeTarget = true, canSell = false}, } ADBuildings = { list = function() return buildings end, find = function(_, _, id) for _, b in ipairs(buildings) do if b.id == id then return b end end end, byMarker = function(_, _, wayPointId, role) for _, b in ipairs(buildings) do if role == "unload" then if b.unloadMarker ~= nil and b.unloadMarker.id == wayPointId then return b end else for _, m in ipairs(b.loadMarkers or {b.loadMarker}) do if m ~= nil and m.id == wayPointId then return b end end end end end, } ADSources = {identify = function(_, id) return (id == 10 or id == 12694 or id == 555) and {kind = "x", wayPointId = id, name = "k"} or nil end, getProducts = function() return {} end, getSellMarkers = function() return {} end, stationAccepts = function() return false end, getSourcePlaceables = function() return {} end} ADOutbound = {isOutboundVehicle = function() return false end, getRunStatus = function() return {reasons = {"SUGAR 78 % af top"}} end, getHoldFor = function() return nil end, getTripFor = function() return nil end, releaseInactive = function() end} buildWorld() addMarker(10, "cow 1 milk", "Milk", 100, 0); loadAt[10] = {} addMarker(555, "raa markoer", "Pickup", 200, 0); loadAt[555] = {} addMarker(77, "sugar offload", "production", 950, 0); unloadAt[77] = {accepts = {[TMR] = 360000}} -- CF1: udkørsel: bygninger + rå markør -> sourceWayPointIds; manglende markør rapporteres local flowOut = ADRunsController.createRun("Milk", "outbound") ADRunsController.updateRun(flowOut.id, {sourceBuildingIds = {"barn1", "shed"}, sourceWayPointIds = {555}}) local resolved = ADRunsController.resolveMarkers(adEnv, ADRuns.getRun(ADRunsController.store, flowOut.id)) check("CF1 kildemarkører = ALLE bygningens læssemarkører + rå", table.concat(ADRuns.getRun(ADRunsController.store, flowOut.id).sourceWayPointIds, ","), "10,11,555") check("CF1 manglende markør", resolved.missing[1], "Palleskur") -- en markør der HØRER til en bygning som ikke længere er valgt, fjernes (den er ikke rå) ADRunsController.updateRun(flowOut.id, {sourceWayPointIds = {12694, 555}}) ADRunsController.resolveMarkers(adEnv, ADRuns.getRun(ADRunsController.store, flowOut.id)) check("CF1 fravalgt bygnings markør fjernes", table.concat(ADRuns.getRun(ADRunsController.store, flowOut.id).sourceWayPointIds, ","), "10,11,555") -- CF1b: gamle rå markører ryddes: ikke-kilde (identify = nil) og markør der hører til en valgt bygning ADSources.identify = function(_, id) if id == 10 or id == 12694 or id == 555 then return {kind = "x", wayPointId = id, name = "k"} end if id == 556 then return {kind = "husbandry", wayPointId = id, name = "Kostald 1", placeable = "barn1-placeable"} end return nil end buildings[1].placeable = "barn1-placeable" ADRunsController.updateRun(flowOut.id, {sourceBuildingIds = {"barn1"}, sourceWayPointIds = {10, 11, 555, 556, 999}}) logLines = {} ADRunsController.resolveMarkers(adEnv, ADRuns.getRun(ADRunsController.store, flowOut.id)) check("CF1b rå ikke-kilde og dublet-bygning fjernet", table.concat(ADRuns.getRun(ADRunsController.store, flowOut.id).sourceWayPointIds, ","), "10,11,555") local pruneLogged = false for _, line in ipairs(logLines) do if line:find("gamle markører fjernet", 1, true) then pruneLogged = true end end check("CF1b logget", pruneLogged, true) -- CF2: forsyning: mål-bygninger -> targetWayPointIds + targetWayPointId local flowIn = ADRunsController.createRun("Foder", "supply") ADRunsController.updateRun(flowIn.id, {targetBuildingIds = {"barn1", "mill"}}) ADRunsController.resolveMarkers(adEnv, ADRuns.getRun(ADRunsController.store, flowIn.id)) check("CF2 målmarkører", table.concat(ADRunsController.getRunTargets(ADRuns.getRun(ADRunsController.store, flowIn.id)), ","), "94,77") check("CF2 primært mål", ADRuns.getRun(ADRunsController.store, flowIn.id).targetWayPointId, 94) -- CF2b: gamle rå mål der ikke er en markør (slettet i AD) eller hører til en valgt bygning, ryddes addMarker(78, "Anden stald food", "Cow Food", 3000, 0); unloadAt[78] = {accepts = {[TMR] = 1000}, owningPlaceable = "anden-stald"} unloadAt[11062] = {accepts = {[TMR] = 1000}, owningPlaceable = "anden-stald"} -- waypoint uden markør ADRunsController.updateRun(flowIn.id, {targetWayPointIds = {94, 77, 11062, 78}}) ADRunsController.resolveMarkers(adEnv, ADRuns.getRun(ADRunsController.store, flowIn.id)) check("CF2b ikke-markør fjernet, rå markør med station beholdt", table.concat(ADRunsController.getRunTargets(ADRuns.getRun(ADRunsController.store, flowIn.id)), ","), "94,77,78") ADRunsController.updateRun(flowIn.id, {targetWayPointIds = {94, 77}}) -- CF3: migration: gamle markør-runs får bygninger local oldOut = ADRunsController.createRun("Gammel ud", "outbound") ADRunsController.updateRun(oldOut.id, {sourceWayPointIds = {12694, 555}}) local oldIn = ADRunsController.createRun("Gammel ind", "supply") ADRunsController.updateRun(oldIn.id, {targetWayPointIds = {94}}) ADRunsController.migrateBuildings(adEnv) check("CF3 ud: markør -> bygning", ADRuns.getRun(ADRunsController.store, oldOut.id).sourceBuildingIds[1], "mill") check("CF3 ud: rå markør uden bygning beholdes", table.concat(ADRuns.getRun(ADRunsController.store, oldOut.id).sourceWayPointIds, ","), "12694,555") check("CF3 ind: markør -> bygning", ADRuns.getRun(ADRunsController.store, oldIn.id).targetBuildingIds[1], "barn1") ADRunsController.migrateBuildings(adEnv) check("CF3 migration er idempotent", #ADRuns.getRun(ADRunsController.store, oldOut.id).sourceBuildingIds, 1) -- CF4: validering: valgt bygning uden markør -> noMarker local cfVehicle = makeVehicle("vehicleCF", "CF", {STRAW, TMR}, 20000, 100, 0) ADRunsController.updateRun(flowOut.id, {sourceBuildingIds = {"shed"}, sourceWayPointIds = {}}) local cfValidation = ADRunsController.validateVehicle(adEnv, ADRuns.getRun(ADRunsController.store, flowOut.id), cfVehicle) check("CF4 noMarker", cfValidation.reason, "noMarker") check("CF4 hvilken bygning", cfValidation.missingBuilding, "Palleskur") -- CF4b: ÉN bygning uden markør blandt flere med markør -> start alligevel, advar om den ene ADRunsController.updateRun(flowOut.id, {sourceBuildingIds = {"barn1", "shed"}, sourceWayPointIds = {}}) logLines = {} local cfValidation2 = ADRunsController.validateVehicle(adEnv, ADRuns.getRun(ADRunsController.store, flowOut.id), cfVehicle) check("CF4b ikke afvist pga. markør", cfValidation2.reason ~= "noMarker", true) local warned = false for _, line in ipairs(logLines) do if line:find("Palleskur", 1, true) and line:find("springes over", 1, true) then warned = true end end check("CF4b advarsel nævner bygningen", warned, true) -- CF5: flow-række til tavlen ADRunsController.updateRun(flowOut.id, {sourceBuildingIds = {"barn1"}, products = {{fillType = "SUGAR", policy = "sell"}, {fillType = "FORAGE", policy = "store"}}, collectRound = true}) ADRunsController.assignVehicle(flowOut.id, cfVehicle) local row = ADRunsController.getFlowRow(adEnv, ADRuns.getRun(ADRunsController.store, flowOut.id)) check("CF5 vare-navne", table.concat(row.productNames, ","), "SUGAR,FORAGE") check("CF5 fra", row.fromNames[1], "Kostald 1") check("CF5 politikker", table.concat(row.policies, ","), "sell,store") check("CF5 traktor", row.vehicleNames[1], "CF") check("CF5 begrundelse", row.reason, "SUGAR 78 % af top") check("CF5 runde", row.collectRound, true) ADRunsController.updateRun(flowOut.id, {sourceBuildingIds = {"barn1", "shed"}}) check("CF5 manglende bygning markeres", ADRunsController.getFlowRow(adEnv, ADRuns.getRun(ADRunsController.store, flowOut.id)).missing[1], "Palleskur") local rowIn = ADRunsController.getFlowRow(adEnv, ADRuns.getRun(ADRunsController.store, flowIn.id)) check("CF5 forsyning fra = alle siloer", rowIn.fromAllSilos, true) check("CF5 forsyning til", table.concat(rowIn.toNames, ","), "Kostald 1,Sukkerfabrik") check("CF5 forsyning auto-varer", rowIn.productAuto, true) ADBuildings = nil; ADSources = nil; ADOutbound = nil print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL")) os.exit(failures == 0 and 0 or 1)