486 lines
22 KiB
Lua
486 lines
22 KiB
Lua
-- AD Pool
|
|
-- Maskinpulje: plove, kultivatorer, såmaskiner … holder frakoblet på deres AD-parkering "X", og en ledig
|
|
-- solo-traktor henter dem via markøren "Connect X", når markarbejdet mangler en færdig kombination.
|
|
-- Ren logik øverst (testes med luajit), spil-adapter nederst.
|
|
|
|
ADPool = {}
|
|
ADPool.LOG_PREFIX = "ADSmartPickup: pulje: "
|
|
ADPool.QUARANTINE_MS = 600000 -- redskab der ikke kunne kobles på, springes over så længe
|
|
|
|
-- ---- ren logik ----
|
|
|
|
-- Rolle for et frakoblet redskab ud fra dets egne specs (samme prioritet som markflåden); en ren vogn med
|
|
-- tip/snegl er tømmevogn.
|
|
function ADPool.roleOf(specs)
|
|
local role = ADFieldFleet.roleFromSpecs(specs)
|
|
if role ~= nil and role ~= "unloader" then return role end
|
|
if specs.spec_trailer and specs.spec_dischargeable then return "unloader" end
|
|
return role
|
|
end
|
|
|
|
-- Butiksdata er enten et tal eller en tabel pr. konfiguration: største tal, ellers nil.
|
|
function ADPool.numberOf(value)
|
|
if type(value) == "number" then return value end
|
|
if type(value) ~= "table" then return nil end
|
|
local best = nil
|
|
for _, entry in pairs(value) do
|
|
if type(entry) == "number" and (best == nil or entry > best) then best = entry end
|
|
end
|
|
return best
|
|
end
|
|
|
|
-- Mangler et af tallene, tillades det (intet må gå i stå på manglende data).
|
|
function ADPool.powerOk(tractorHp, neededHp)
|
|
if tractorHp == nil or neededHp == nil or tractorHp <= 0 or neededHp <= 0 then return true end
|
|
return tractorHp >= neededHp
|
|
end
|
|
|
|
-- Butikskategori: markredskaber kun bag traktorer (tractorsS/M/L …) — ikke lastbil/Unimog, læssere o.l.
|
|
-- Tømmevogne må alle trække. Ukendt kategori tillades (intet må gå i stå på manglende data).
|
|
ADPool.ANY_PULLER_ROLES = {unloader = true}
|
|
|
|
function ADPool.categoryOk(category, role)
|
|
if category == nil or ADPool.ANY_PULLER_ROLES[role] then return true end
|
|
return tostring(category):lower():find("^tractors") ~= nil
|
|
end
|
|
|
|
local function suits(tractor, implement, role)
|
|
return ADPool.powerOk(tractor.hp, implement.neededHp) and ADPool.categoryOk(tractor.category, role)
|
|
end
|
|
|
|
-- Markøren "Connect <parkering>" for en parkering (navneregel).
|
|
function ADPool.connectFor(parkName, markers)
|
|
if type(parkName) ~= "string" then return nil end
|
|
local wanted = parkName:lower()
|
|
for _, marker in pairs(markers or {}) do
|
|
local action, target = ADHitch.parseMarker(marker.name)
|
|
if action == "attach" and target ~= nil and target:lower() == wanted then return marker end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function hasRole(item, role)
|
|
return item.role == role or (item.roles ~= nil and item.roles[role] == true)
|
|
end
|
|
|
|
local function distanceSq(ax, az, bx, bz)
|
|
return (ax - bx) ^ 2 + (az - bz) ^ 2
|
|
end
|
|
|
|
local function nearest(items, x, z, keep)
|
|
local best, bestDistance = nil, math.huge
|
|
for _, item in ipairs(items) do
|
|
if keep == nil or keep(item) then
|
|
local distance = distanceSq(item.x, item.z, x, z)
|
|
if distance < bestDistance then best, bestDistance = item, distance end
|
|
end
|
|
end
|
|
return best
|
|
end
|
|
|
|
-- Når opgaven slutter, sættes puljeredskabet af — også når AutoDrive ellers selv ville parkere (påfyldte
|
|
-- sæt): AD parkerer ved redskabets plads med redskabet på. Aldrig ved manuel transport.
|
|
-- Bedste pasform: den mindste traktor med hk nok; afstand afgør kun mellem traktorer inden for FIT_BAND af den
|
|
-- mindste. Over OVERSIZE_FACTOR x behovet kun når intet andet passer; ukendt traktor-hk efter de kendte.
|
|
-- Redskab uden hk-data: nærmeste (intet at matche på). tractors = allerede filtrerede kandidater.
|
|
ADPool.FIT_BAND = 1.15
|
|
ADPool.OVERSIZE_FACTOR = 2.5
|
|
|
|
function ADPool.bestFit(tractors, implement, role)
|
|
local fitting = {}
|
|
for _, tractor in ipairs(tractors or {}) do
|
|
if suits(tractor, implement, role) then table.insert(fitting, tractor) end
|
|
end
|
|
local needed = implement.neededHp
|
|
if needed == nil or needed <= 0 then return nearest(fitting, implement.x, implement.z) end
|
|
local sized, oversized, unknown = {}, {}, {}
|
|
for _, tractor in ipairs(fitting) do
|
|
if tractor.hp == nil or tractor.hp <= 0 then table.insert(unknown, tractor)
|
|
elseif tractor.hp > needed * ADPool.OVERSIZE_FACTOR then table.insert(oversized, tractor)
|
|
else table.insert(sized, tractor) end
|
|
end
|
|
local pool = #sized > 0 and sized or (#unknown > 0 and unknown or oversized)
|
|
if pool ~= unknown then
|
|
local smallest = math.huge
|
|
for _, tractor in ipairs(pool) do smallest = math.min(smallest, tractor.hp) end
|
|
local band = {}
|
|
for _, tractor in ipairs(pool) do
|
|
if tractor.hp <= smallest * ADPool.FIT_BAND then table.insert(band, tractor) end
|
|
end
|
|
pool = band
|
|
end
|
|
return nearest(pool, implement.x, implement.z)
|
|
end
|
|
|
|
function ADPool.shouldReturnHome(assignment)
|
|
return not assignment.manual
|
|
end
|
|
|
|
-- Kun maskiner uden egen arbejdsrolle (almindelige traktorer) henter puljeredskaber — ikke mejetærskere,
|
|
-- selvkørende sprøjter o.l., selv om de har koblingspunkter.
|
|
function ADPool.canPull(role)
|
|
return role == nil
|
|
end
|
|
|
|
-- Hvem fik koblingen til at fejle? Kom traktoren ikke frem, er det traktoren (ingen AD-rute), ellers redskabet.
|
|
function ADPool.failureBlames(reason)
|
|
if tostring(reason):find("^stoppedAway") ~= nil then return "tractor" end
|
|
return "implement"
|
|
end
|
|
|
|
-- Efter koblingen: refill = markarbejdets påfyldningsplan (nil = intet at fylde).
|
|
function ADPool.nextDrive(refill)
|
|
if refill == nil or not refill.empty then return "drive" end
|
|
if refill.refillId ~= nil then return "load" end
|
|
return "empty"
|
|
end
|
|
|
|
-- Puljen bruges til opgaver med ét sæt (også tømmevogne ved høst), ikke når marken har egne faste maskiner
|
|
-- til rollen.
|
|
function ADPool.mayUsePool(decision, hasOwnForRole)
|
|
return (decision.count or 1) == 1 and not hasOwnForRole
|
|
end
|
|
|
|
-- Kan puljevognen bære varen? (implement.carries = {[fillType] = true}; nil vare = intet krav)
|
|
function ADPool.canCarry(implement, fillType)
|
|
if fillType == nil then return true end
|
|
return implement.carries ~= nil and implement.carries[fillType] == true
|
|
end
|
|
|
|
-- Status til flådefanen. s = {parkName, hasConnect, attachedTo, quarantineMin, reserved} -> nøgle, argument
|
|
function ADPool.implementStatus(s)
|
|
if s.parkName == nil then return "noPark" end
|
|
if not s.hasConnect then return "noConnect", s.parkName 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.reserved then return "reserved" end
|
|
return "parked", s.parkName
|
|
end
|
|
|
|
-- s = {hasPark, enabled, controlled, returning, fieldId, busy, offNetwork, hitched} -> nøgle, argument
|
|
function ADPool.tractorStatus(s)
|
|
if not s.hasPark then return "noPark" end
|
|
if s.controlled then return "player" end
|
|
if s.returning then return "returning" end
|
|
if s.fieldId ~= nil then return "job", s.fieldId end
|
|
if s.busy then return "busy" end
|
|
if not s.enabled then return "notInFleet" end
|
|
if s.offNetwork then return "offNetwork" end
|
|
if s.hitched then return "hitched" end
|
|
return "free"
|
|
end
|
|
|
|
-- Redskab med rollen nærmest marken, derefter nærmeste ledige traktor med nok hk til redskabet.
|
|
-- blocked = {[id] = true} (reserverede og redskaber i karantæne). accept (valgfri): implement -> bool.
|
|
function ADPool.pairFor(role, fieldX, fieldZ, implements, tractors, blocked, accept)
|
|
blocked = blocked or {}
|
|
local free = {}
|
|
for _, tractor in ipairs(tractors or {}) do
|
|
if not blocked[tractor.id] and not tractor.offNetwork then table.insert(free, tractor) end
|
|
end
|
|
local candidates = {}
|
|
for _, implement in ipairs(implements or {}) do
|
|
if hasRole(implement, role) and not blocked[implement.id] and (accept == nil or accept(implement)) then
|
|
table.insert(candidates, implement)
|
|
end
|
|
end
|
|
if #candidates == 0 then return nil, "noPoolImplement" end
|
|
if #free == 0 then return nil, "noPoolTractor" end
|
|
table.sort(candidates, function(a, b)
|
|
return distanceSq(a.x, a.z, fieldX, fieldZ) < distanceSq(b.x, b.z, fieldX, fieldZ)
|
|
end)
|
|
local weakest = nil
|
|
for _, implement in ipairs(candidates) do
|
|
local tractor = ADPool.bestFit(free, implement, role)
|
|
if tractor ~= nil then return {implement = implement, tractor = tractor} end
|
|
if weakest == nil or (implement.neededHp or 0) < (weakest.neededHp or 0) then weakest = implement end
|
|
end
|
|
return nil, "tooWeak", {weakest ~= nil and weakest.neededHp or 0, weakest ~= nil and weakest.name or "?"}
|
|
end
|
|
|
|
-- Alle frie puljeredskaber med rollen, hver parret med den nærmeste endnu ubrugte traktor med nok hk
|
|
-- (marksilo-flowet planlægger over mange vogne på én gang).
|
|
function ADPool.pairAll(role, implements, tractors, blocked)
|
|
blocked = blocked or {}
|
|
local used, result = {}, {}
|
|
for _, implement in ipairs(implements or {}) do
|
|
if hasRole(implement, role) and not blocked[implement.id] then
|
|
local open = {}
|
|
for _, t in ipairs(tractors or {}) do
|
|
if not used[t.id] and not blocked[t.id] and not t.offNetwork then table.insert(open, t) end
|
|
end
|
|
local tractor = ADPool.bestFit(open, implement, role)
|
|
if tractor ~= nil then
|
|
used[tractor.id] = true
|
|
table.insert(result, {implement = implement, tractor = tractor})
|
|
end
|
|
end
|
|
end
|
|
return result
|
|
end
|
|
|
|
-- Ventende marker prøver igen på fælles taktslag (ikke hver sin 30 s fra eget forsøg): så udløber de i samme
|
|
-- tick, og køens rækkefølge (længst ventende først) afgør hvem der får den næste ledige traktor.
|
|
function ADPool.alignedRetry(nowMs, periodMs)
|
|
return (math.floor(nowMs / periodMs) + 1) * periodMs
|
|
end
|
|
|
|
-- Ventende marker først (længst ventende først, lige -> lavest id); resten bagefter i oprindelig rækkefølge.
|
|
function ADPool.queueOrder(infos, waitingSince)
|
|
local waiting, rest = {}, {}
|
|
for _, info in ipairs(infos or {}) do
|
|
if waitingSince ~= nil and waitingSince[info.id] ~= nil then table.insert(waiting, info) else table.insert(rest, info) end
|
|
end
|
|
table.sort(waiting, function(a, b)
|
|
local wa, wb = waitingSince[a.id], waitingSince[b.id]
|
|
if wa ~= wb then return wa < wb end
|
|
return a.id < b.id
|
|
end)
|
|
for _, info in ipairs(rest) do table.insert(waiting, info) end
|
|
return waiting
|
|
end
|
|
|
|
-- Reservationer (i hukommelsen): owner -> {id, ...}. Karantæne: id -> udløbstid.
|
|
local reservations, quarantined = {}, {}
|
|
|
|
function ADPool.reserve(owner, ids)
|
|
reservations[owner] = ids
|
|
end
|
|
|
|
function ADPool.release(owner)
|
|
reservations[owner] = nil
|
|
end
|
|
|
|
function ADPool.quarantine(id, nowMs)
|
|
quarantined[id] = nowMs + ADPool.QUARANTINE_MS
|
|
end
|
|
|
|
function ADPool.quarantineMinutes(id, nowMs)
|
|
local untilMs = quarantined[id]
|
|
if untilMs == nil or nowMs >= untilMs then return 0 end
|
|
return math.ceil((untilMs - nowMs) / 60000)
|
|
end
|
|
|
|
function ADPool.isReserved(id)
|
|
for _, ids in pairs(reservations) do
|
|
for _, reserved in ipairs(ids) do if reserved == id then return true end end
|
|
end
|
|
return false
|
|
end
|
|
|
|
function ADPool.blocked(nowMs)
|
|
local result = {}
|
|
for _, ids in pairs(reservations) do
|
|
for _, id in ipairs(ids) do result[id] = true end
|
|
end
|
|
for id, untilMs in pairs(quarantined) do
|
|
if nowMs < untilMs then result[id] = true end
|
|
end
|
|
return result
|
|
end
|
|
|
|
-- Traktorer på hjemtur (sætter redskab af, kører hjem): optaget for markarbejdet til de er hjemme.
|
|
local returning = setmetatable({}, {__mode = "k"})
|
|
|
|
function ADPool.setReturning(vehicle, isOn)
|
|
returning[vehicle] = isOn and true or nil
|
|
end
|
|
|
|
function ADPool.isReturning(vehicle)
|
|
return returning[vehicle] == true
|
|
end
|
|
|
|
-- ---- spil ----
|
|
|
|
local function parkMarker(adEnv, vehicle)
|
|
local index = vehicle.advd ~= nil and vehicle.advd.parkDestination or nil
|
|
if type(index) ~= "number" or index < 1 then return nil end
|
|
return adEnv.ADGraphManager:getMapMarkerById(index)
|
|
end
|
|
|
|
local function positionOf(vehicle)
|
|
local x, _, z = getWorldTranslation(vehicle.rootNode)
|
|
return x, z
|
|
end
|
|
|
|
-- Butiksdata: redskabets krævede effekt (hk). specs indlæses først, når butikken har vist varen.
|
|
local neededHpCache = {} -- configFileName -> hk eller false (butiksdata ændrer sig ikke i et spil)
|
|
|
|
local function neededHpOf(vehicle)
|
|
if g_storeManager == nil or vehicle.configFileName == nil then return nil end
|
|
local cached = neededHpCache[vehicle.configFileName]
|
|
if cached ~= nil then return cached or nil end
|
|
local value = ADPool.readNeededHp(vehicle)
|
|
neededHpCache[vehicle.configFileName] = value or false
|
|
return value
|
|
end
|
|
|
|
function ADPool.readNeededHp(vehicle)
|
|
local item = g_storeManager:getItemByXMLFilename(vehicle.configFileName)
|
|
if item == nil then return nil end
|
|
if item.specs == nil and StoreItemUtil ~= nil and StoreItemUtil.loadSpecsFromXML ~= nil then
|
|
pcall(StoreItemUtil.loadSpecsFromXML, item)
|
|
end
|
|
return item.specs ~= nil and ADPool.numberOf(item.specs.neededPower) or nil
|
|
end
|
|
|
|
local tractorHpCache = setmetatable({}, {__mode = "k"}) -- vehicle -> hk eller false
|
|
|
|
-- Traktorens effekt i hk: motorens topeffekt (kW) omregnet; ellers butiksdata. Gemmes pr. traktor.
|
|
local function tractorHpOf(vehicle)
|
|
local cached = tractorHpCache[vehicle]
|
|
if cached ~= nil then return cached or nil end
|
|
local value = ADPool.readTractorHp(vehicle)
|
|
tractorHpCache[vehicle] = value or false
|
|
return value
|
|
end
|
|
|
|
function ADPool.readTractorHp(vehicle)
|
|
local motor = vehicle.spec_motorized ~= nil and vehicle.spec_motorized.motor or nil
|
|
local kw = motor ~= nil and motor.peakMotorPower or nil
|
|
if type(kw) == "number" and kw > 0 then return kw * 1.35962 end
|
|
if g_storeManager == nil or vehicle.configFileName == nil then return nil end
|
|
local item = g_storeManager:getItemByXMLFilename(vehicle.configFileName)
|
|
return item ~= nil and item.specs ~= nil and ADPool.numberOf(item.specs.power) or nil
|
|
end
|
|
|
|
-- Butikskategori (fx "TRACTORSM", "TRUCKS") eller nil.
|
|
local function categoryOf(vehicle)
|
|
if g_storeManager == nil or vehicle.configFileName == nil then return nil end
|
|
local item = g_storeManager:getItemByXMLFilename(vehicle.configFileName)
|
|
return item ~= nil and item.categoryName or nil
|
|
end
|
|
|
|
-- Varer og samlet kapacitet i redskabets fyldenheder (tømmevogne).
|
|
function ADPool.cargoOf(vehicle)
|
|
local carries, capacity = {}, 0
|
|
local units = vehicle.spec_fillUnit ~= nil and vehicle.spec_fillUnit.fillUnits or {}
|
|
for index in ipairs(units) do
|
|
capacity = capacity + (vehicle:getFillUnitCapacity(index) or 0)
|
|
for fillType in pairs(vehicle:getFillUnitSupportedFillTypes(index) or {}) do carries[fillType] = true end
|
|
end
|
|
return carries, capacity
|
|
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)
|
|
return vehicle.getAttacherVehicle ~= nil and vehicle:getAttacherVehicle() ~= nil
|
|
end
|
|
|
|
-- Traktorens egen rolle uden de redskaber der hænger på (mejetærsker/selvkørende har en, en traktor ikke).
|
|
function ADPool.ownRole(vehicle)
|
|
local specs = {}
|
|
for _, name in ipairs(ADCpBridge.SPECS) do
|
|
if vehicle[name] ~= nil then specs[name] = true end
|
|
end
|
|
return ADFieldFleet.roleFromSpecs(specs)
|
|
end
|
|
|
|
-- Puljeredskaber (frakoblet, parkering "X", markør "Connect X") og puljetraktorer.
|
|
-- isBusy(vehicle): markarbejdets optaget-regel (opgave, AD/CP kører, lånt af marksilo, på hjemtur).
|
|
function ADPool.collect(adEnv, isBusy)
|
|
local markers = adEnv.ADGraphManager:getMapMarkers()
|
|
local farmId = g_currentMission:getFarmId()
|
|
local implements, tractors = {}, {}
|
|
-- spillets egen liste: frakoblede redskaber er ikke nødvendigvis i AutoDrive's køretøjsliste
|
|
for _, vehicle in pairs(g_currentMission.vehicleSystem.vehicles) do
|
|
local owned = vehicle.getOwnerFarmId ~= nil and vehicle:getOwnerFarmId() == farmId
|
|
if owned and vehicle.spec_motorized == nil and vehicle.spec_attachable ~= nil and not isAttached(vehicle) then
|
|
local park = parkMarker(adEnv, vehicle)
|
|
local connect = park ~= nil and ADPool.connectFor(park.name, markers) or nil
|
|
local specs = connect ~= nil and ADCpBridge.getSpecs(vehicle) or nil
|
|
local role = specs ~= nil and ADPool.roleOf(specs) or nil
|
|
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 carries, capacity = ADPool.cargoOf(vehicle)
|
|
table.insert(implements, {carries = carries, capacity = capacity,
|
|
id = ADRunsController.getVehicleId(vehicle), vehicle = vehicle, name = ADRuns.vehicleLabel(vehicle),
|
|
role = role, roles = specs.sprayRoles or ADFieldFleet.rolesFor(role), x = x, z = z, width = ADCpBridge.getWorkWidth(vehicle),
|
|
neededHp = neededHpOf(vehicle), connectId = connect.id, parkId = park.id,
|
|
})
|
|
end
|
|
end
|
|
end
|
|
local state = ADFieldJobs.state
|
|
for _, vehicle in ipairs(ADRunsController.getFarmVehicles(adEnv)) do
|
|
local id = ADRunsController.getVehicleId(vehicle)
|
|
local park = parkMarker(adEnv, vehicle)
|
|
local controlled = vehicle.getIsControlled ~= nil and vehicle:getIsControlled()
|
|
if id ~= nil and park ~= nil and vehicle.spec_attacherJoints ~= nil and #ADHitch.rearImplementsOf(vehicle) == 0
|
|
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 ADPool.canPull(ADPool.ownRole(vehicle)) then
|
|
local x, z = positionOf(vehicle)
|
|
-- uden for AD-vejnettet: den kan ikke køre ud efter redskabet (ville sende redskaber i karantæne)
|
|
local networkDistance = ADFieldWork.distanceToNetwork(adEnv, x, z)
|
|
table.insert(tractors, {id = id, vehicle = vehicle, name = ADRuns.vehicleLabel(vehicle), x = x, z = z,
|
|
hp = tractorHpOf(vehicle), category = categoryOf(vehicle), parkId = park.id,
|
|
offNetwork = networkDistance ~= nil and networkDistance > ADFieldWork.MAX_OFF_NETWORK})
|
|
end
|
|
end
|
|
return implements, tractors
|
|
end
|
|
|
|
-- Oversigt til flådefanen: alle redskaber med en markrolle (også dem der mangler parkering/Connect, så
|
|
-- spilleren kan se hvorfor) og alle traktorer uden egen arbejdsrolle, der ikke hører til et flow.
|
|
function ADPool.overview(adEnv)
|
|
local markers = adEnv.ADGraphManager:getMapMarkers()
|
|
local farmId = g_currentMission:getFarmId()
|
|
local nowMs = ADFieldJobs.now or 0
|
|
local implements, tractors = {}, {}
|
|
for _, vehicle in pairs(g_currentMission.vehicleSystem.vehicles) do
|
|
local owned = vehicle.getOwnerFarmId ~= nil and vehicle:getOwnerFarmId() == farmId
|
|
if owned and vehicle.spec_motorized == nil and vehicle.spec_attachable ~= nil then
|
|
local role = ADPool.roleOf(ADCpBridge.getSpecs(vehicle))
|
|
if role ~= nil then
|
|
local id = ADRunsController.getVehicleId(vehicle)
|
|
local park = parkMarker(adEnv, vehicle)
|
|
local attacher = isAttached(vehicle) and vehicle:getAttacherVehicle() or nil
|
|
local key, arg = ADPool.implementStatus({
|
|
parkName = park ~= nil and park.name or nil,
|
|
hasConnect = park ~= nil and ADPool.connectFor(park.name, markers) ~= nil,
|
|
attachedTo = attacher ~= nil and ADRuns.vehicleLabel(attacher) or nil,
|
|
quarantineMin = id ~= nil and ADPool.quarantineMinutes(id, nowMs) or 0,
|
|
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})
|
|
end
|
|
end
|
|
end
|
|
local state = ADFieldJobs.state
|
|
for _, vehicle in ipairs(ADRunsController.getFarmVehicles(adEnv)) do
|
|
local id = ADRunsController.getVehicleId(vehicle)
|
|
-- 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 ADPool.isFreeFromFlows(vehicle)
|
|
and ADPool.canPull(ADPool.ownRole(vehicle)) and ADFieldFleet.roleFromSpecs(ADCpBridge.getSpecs(vehicle)) == nil then
|
|
local x, z = positionOf(vehicle)
|
|
local distance = ADFieldWork.distanceToNetwork(adEnv, x, z)
|
|
local assignment = state.assignments[id]
|
|
local rear = ADHitch.rearImplementsOf(vehicle)
|
|
local key, arg = ADPool.tractorStatus({
|
|
hasPark = parkMarker(adEnv, vehicle) ~= nil,
|
|
enabled = ADFieldFleet.isEnabled(state.fleetMode, state.fleet, id),
|
|
controlled = vehicle.getIsControlled ~= nil and vehicle:getIsControlled(),
|
|
returning = ADPool.isReturning(vehicle),
|
|
fieldId = assignment ~= nil and assignment.fieldId or nil,
|
|
busy = ADFieldWork.isBusyVehicle(vehicle, id),
|
|
offNetwork = distance ~= nil and distance > ADFieldWork.MAX_OFF_NETWORK,
|
|
hitched = #rear > 0,
|
|
})
|
|
table.insert(tractors, {id = id, label = ADRuns.vehicleLabel(vehicle), hp = tractorHpOf(vehicle), status = key, arg = arg,
|
|
enabled = ADFieldFleet.isEnabled(state.fleetMode, state.fleet, id)})
|
|
end
|
|
end
|
|
return implements, tractors
|
|
end
|
|
|