feat: runs-controller (validering, Start/Stop, Auto-vareliste, gem/indlæs)
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
-- 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,
|
||||
}
|
||||
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},
|
||||
},
|
||||
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 0, station.accepts[fillType]
|
||||
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
|
||||
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,
|
||||
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}}},
|
||||
}
|
||||
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/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 -> ikke startet igen, men tilstand skrives
|
||||
fastracA.ad.stateModule.active = true
|
||||
ADRunsController.startRun(adEnv, cow.id)
|
||||
check("C7 ikke dobbelt-startet", fastracA.started, 1)
|
||||
|
||||
-- C8: mål-markører og Wait-puljer til dropdowns
|
||||
local targets = ADRunsController.getTargetMarkers(adEnv, 1)
|
||||
check("C8 kun markører med aflæsningsstation", #targets, 1)
|
||||
check("C8 navn", targets[1].name, "Cow 1 Food")
|
||||
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)
|
||||
|
||||
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
||||
os.exit(failures == 0 and 0 or 1)
|
||||
Reference in New Issue
Block a user