feat(marker): sideriver — mål hvor riven lægger skåret, forskyd læssevognen tilsvarende (v1.24.0.0)
Brugerkrav 25/9: alle river-typer skal virke. Nedlægningsområdet (runtime-felt eller riverens XML) måles i traktorens ramme mens riven arbejder; læssevognen på rivens kursus får CP-toolOffsetX = egen - skårets forskydning og får sin værdi tilbage når den slippes.
This commit is contained in:
@@ -74,6 +74,68 @@ function ADCpBridge.isWorking(vehicle)
|
|||||||
return vehicle.getIsAIActive ~= nil and vehicle:getIsAIActive() or false
|
return vehicle.getIsAIActive ~= nil and vehicle:getIsAIActive() or false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Courseplays værktøjsforskydning (venstre positiv) på køretøjet; den der flytter kursuslinjen.
|
||||||
|
function ADCpBridge.getToolOffset(vehicle)
|
||||||
|
if vehicle == nil or vehicle.getCpSettings == nil then return 0 end
|
||||||
|
local ok, value = pcall(function() return vehicle:getCpSettings().toolOffsetX:getValue() end)
|
||||||
|
return ok and type(value) == "number" and value or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function ADCpBridge.setToolOffset(vehicle, value)
|
||||||
|
if vehicle == nil or vehicle.getCpSettings == nil then return false end
|
||||||
|
return (pcall(function() vehicle:getCpSettings().toolOffsetX:setFloatValue(value) end))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Riverens XML: workArea-nr (1-baseret) -> dropWindrowWorkAreaIndex. Bruges når spillet ikke har feltet
|
||||||
|
-- på workArea-tabellen. Cachet pr. fil.
|
||||||
|
ADCpBridge.dropIndexCache = {}
|
||||||
|
function ADCpBridge.readDropIndicesFromXml(fileName)
|
||||||
|
if ADCpBridge.dropIndexCache[fileName] ~= nil then return ADCpBridge.dropIndexCache[fileName] end
|
||||||
|
local indices = {}
|
||||||
|
local ok = pcall(function()
|
||||||
|
local xmlFile = XMLFile.load("adspuRake", fileName)
|
||||||
|
if xmlFile == nil then return end
|
||||||
|
local position = 0
|
||||||
|
xmlFile:iterate("vehicle.workAreas.workArea", function(_, key)
|
||||||
|
position = position + 1
|
||||||
|
local dropIndex = xmlFile:getInt(key .. ".windrower#dropWindrowWorkAreaIndex")
|
||||||
|
if dropIndex ~= nil then indices[position] = dropIndex end
|
||||||
|
end)
|
||||||
|
xmlFile:delete()
|
||||||
|
end)
|
||||||
|
if not ok then indices = {} end
|
||||||
|
ADCpBridge.dropIndexCache[fileName] = indices
|
||||||
|
return indices
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Hvor langt til venstre (positiv) for rivens kursuslinje skåret lægges: midten af nedlægningsområdet i
|
||||||
|
-- traktorens ramme minus rivens egen CP-forskydning. Midterrive ≈ 0, siderive fx 3 m. nil = ingen river.
|
||||||
|
function ADCpBridge.getWindrowOffset(vehicle)
|
||||||
|
local root = vehicle.rootNode
|
||||||
|
local sum, count = 0, 0
|
||||||
|
for _, object in ipairs(collect(vehicle)) do
|
||||||
|
local areas = object.spec_windrower ~= nil and object.spec_workArea ~= nil and object.spec_workArea.workAreas or nil
|
||||||
|
if areas ~= nil then
|
||||||
|
local fromXml = nil
|
||||||
|
for position, workArea in ipairs(areas) do
|
||||||
|
local dropIndex = workArea.dropWindrowWorkAreaIndex
|
||||||
|
if dropIndex == nil and object.configFileName ~= nil then
|
||||||
|
fromXml = fromXml or ADCpBridge.readDropIndicesFromXml(object.configFileName)
|
||||||
|
dropIndex = fromXml[position]
|
||||||
|
end
|
||||||
|
local drop = dropIndex ~= nil and areas[dropIndex] or nil
|
||||||
|
if drop ~= nil and drop.width ~= nil and drop.height ~= nil then
|
||||||
|
local widthX = localToLocal(drop.width, root, 0, 0, 0)
|
||||||
|
local heightX = localToLocal(drop.height, root, 0, 0, 0)
|
||||||
|
sum, count = sum + (widthX + heightX) / 2, count + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if count == 0 then return nil end
|
||||||
|
return sum / count - ADCpBridge.getToolOffset(vehicle)
|
||||||
|
end
|
||||||
|
|
||||||
-- Køretøjets nuværende Courseplay-kursus (slåmaskinens kursus genbruges af de næste trin).
|
-- Køretøjets nuværende Courseplay-kursus (slåmaskinens kursus genbruges af de næste trin).
|
||||||
function ADCpBridge.getCourse(vehicle)
|
function ADCpBridge.getCourse(vehicle)
|
||||||
if vehicle == nil or vehicle.getFieldWorkCourse == nil then return nil end
|
if vehicle == nil or vehicle.getFieldWorkCourse == nil then return nil end
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ ADFieldJobs.ARRIVE_DISTANCE = 30
|
|||||||
ADFieldJobs.IDLE_CONFIRM_MS = 5000
|
ADFieldJobs.IDLE_CONFIRM_MS = 5000
|
||||||
ADFieldJobs.START_TIMEOUT_MS = 60000
|
ADFieldJobs.START_TIMEOUT_MS = 60000
|
||||||
ADFieldJobs.PLAYER_DRIVING_KMH = 3
|
ADFieldJobs.PLAYER_DRIVING_KMH = 3
|
||||||
|
-- riven skal være udfoldet og i gang, før nedlægningsområdet måles
|
||||||
|
ADFieldJobs.WINDROW_MEASURE_MS = 10000
|
||||||
-- Courseplay kan efterlade hjørner på skæve marker: efter så mange færdige slå-ture går marken videre.
|
-- Courseplay kan efterlade hjørner på skæve marker: efter så mange færdige slå-ture går marken videre.
|
||||||
ADFieldJobs.MAX_MOW_PASSES = 2
|
ADFieldJobs.MAX_MOW_PASSES = 2
|
||||||
ADFieldJobs.state = {fields = {}, fleet = {}, assignments = {}}
|
ADFieldJobs.state = {fields = {}, fleet = {}, assignments = {}}
|
||||||
@@ -239,7 +241,16 @@ local function forgetFinished(fieldId)
|
|||||||
ADFieldJobs.finished = remaining
|
ADFieldJobs.finished = remaining
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Læssevognens oprindelige CP-forskydning sættes tilbage, når mod'en slipper den.
|
||||||
|
local function restoreToolOffset(assignment)
|
||||||
|
if assignment.restoreToolOffset ~= nil then
|
||||||
|
ADCpBridge.setToolOffset(assignment.vehicle, assignment.restoreToolOffset)
|
||||||
|
assignment.restoreToolOffset = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function release(assignment, phase)
|
local function release(assignment, phase)
|
||||||
|
restoreToolOffset(assignment)
|
||||||
assignment.phase = phase or "released"
|
assignment.phase = phase or "released"
|
||||||
ADFieldJobs.state.assignments[assignment.vehicleId] = nil
|
ADFieldJobs.state.assignments[assignment.vehicleId] = nil
|
||||||
end
|
end
|
||||||
@@ -317,6 +328,15 @@ local function beginWork(adEnv, field, info, assignment)
|
|||||||
if source ~= nil and course == nil then
|
if source ~= nil and course == nil then
|
||||||
log(field.id, "intet %s-kursus kendt — Courseplay laver et nyt kursus til %s", source, assignment.role)
|
log(field.id, "intet %s-kursus kendt — Courseplay laver et nyt kursus til %s", source, assignment.role)
|
||||||
end
|
end
|
||||||
|
if source == "rake" and field.windrowOffset ~= nil and math.abs(field.windrowOffset) > 0.05 then
|
||||||
|
local own = ADCpBridge.getToolOffset(vehicle)
|
||||||
|
local target = ADFieldModel.collectorOffset(own, field.windrowOffset)
|
||||||
|
if ADCpBridge.setToolOffset(vehicle, target) then
|
||||||
|
assignment.restoreToolOffset = own
|
||||||
|
log(field.id, "skår ligger %.1f m %s for rivens linje -> læssevognens forskydning %.1f m (var %.1f m)",
|
||||||
|
math.abs(field.windrowOffset), field.windrowOffset > 0 and "til venstre" or "til højre", target, own)
|
||||||
|
end
|
||||||
|
end
|
||||||
local handle, err = ADCpBridge.start(vehicle, point.x, point.z, function(ok, reason)
|
local handle, err = ADCpBridge.start(vehicle, point.x, point.z, function(ok, reason)
|
||||||
if ok then
|
if ok then
|
||||||
assignment.phase = "working"
|
assignment.phase = "working"
|
||||||
@@ -382,6 +402,12 @@ local function follow(adEnv, assignment)
|
|||||||
log(assignment.fieldId, "Courseplay svarede ikke inden 60 s")
|
log(assignment.fieldId, "Courseplay svarede ikke inden 60 s")
|
||||||
end
|
end
|
||||||
elseif assignment.phase == "working" then
|
elseif assignment.phase == "working" then
|
||||||
|
if assignment.role == "rake" and field.windrowOffset == nil and ADFieldJobs.now - assignment.since >= ADFieldJobs.WINDROW_MEASURE_MS
|
||||||
|
and ADCpBridge.isWorking(vehicle) then
|
||||||
|
local offset = ADCpBridge.getWindrowOffset(vehicle)
|
||||||
|
note(assignment.fieldId, {windrowOffset = offset or 0})
|
||||||
|
log(assignment.fieldId, offset ~= nil and "river lægger skåret %.1f m fra sin kursuslinje (venstre +)" or "river: nedlægningsområde ikke fundet — regner med midten (%.1f m)", offset or 0)
|
||||||
|
end
|
||||||
local busy = ADCpBridge.isWorking(vehicle) or isAdActive(vehicle)
|
local busy = ADCpBridge.isWorking(vehicle) or isAdActive(vehicle)
|
||||||
if busy then
|
if busy then
|
||||||
assignment.idleSince = nil
|
assignment.idleSince = nil
|
||||||
@@ -398,6 +424,7 @@ local function follow(adEnv, assignment)
|
|||||||
log(assignment.fieldId, "%s '%s' færdig", assignment.role, ADRuns.vehicleLabel(vehicle))
|
log(assignment.fieldId, "%s '%s' færdig", assignment.role, ADRuns.vehicleLabel(vehicle))
|
||||||
local park = assignment.role ~= "collector" and parkMarkerId(adEnv, vehicle) or nil
|
local park = assignment.role ~= "collector" and parkMarkerId(adEnv, vehicle) or nil
|
||||||
if park ~= nil then startAd(adEnv, vehicle, adEnv.AutoDrive.MODE_DRIVETO, park, nil, false) end
|
if park ~= nil then startAd(adEnv, vehicle, adEnv.AutoDrive.MODE_DRIVETO, park, nil, false) end
|
||||||
|
restoreToolOffset(assignment)
|
||||||
assignment.phase = "finished"
|
assignment.phase = "finished"
|
||||||
ADFieldJobs.finished = ADFieldJobs.finished or {}
|
ADFieldJobs.finished = ADFieldJobs.finished or {}
|
||||||
table.insert(ADFieldJobs.finished, assignment)
|
table.insert(ADFieldJobs.finished, assignment)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ ADFieldModel.CP_MAX_WIDTH = 50
|
|||||||
ADFieldModel.WIDTH_TOLERANCE = 0.05
|
ADFieldModel.WIDTH_TOLERANCE = 0.05
|
||||||
ADFieldModel.RETRY_MS = 300000
|
ADFieldModel.RETRY_MS = 300000
|
||||||
ADFieldModel.MAX_COLLECTORS = 2
|
ADFieldModel.MAX_COLLECTORS = 2
|
||||||
|
ADFieldModel.CP_MAX_TOOL_OFFSET = 10
|
||||||
|
|
||||||
ADFieldModel.ROLES = {mowing = "mower", tedding = "tedder", raking = "rake", collecting = "collector"}
|
ADFieldModel.ROLES = {mowing = "mower", tedding = "tedder", raking = "rake", collecting = "collector"}
|
||||||
local KNOWN_STAGES = {idle = true, ready = true, mowing = true, tedding = true, raking = true, collecting = true, done = true}
|
local KNOWN_STAGES = {idle = true, ready = true, mowing = true, tedding = true, raking = true, collecting = true, done = true}
|
||||||
@@ -110,6 +111,7 @@ function ADFieldModel.start(field)
|
|||||||
copy.mowWidth = nil
|
copy.mowWidth = nil
|
||||||
copy.mowVehicleId = nil
|
copy.mowVehicleId = nil
|
||||||
copy.rakeVehicleId = nil
|
copy.rakeVehicleId = nil
|
||||||
|
copy.windrowOffset = nil
|
||||||
return copy
|
return copy
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -169,6 +171,13 @@ function ADFieldModel.with(field, changes)
|
|||||||
return copy
|
return copy
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Læssevognens Courseplay-forskydning når den kører rivens kursus: pickuppen skal ligge over skåret, der
|
||||||
|
-- ligger windrowOffset m til venstre (positiv) for rivens kursuslinje. Courseplay tillader ±10 m.
|
||||||
|
function ADFieldModel.collectorOffset(ownOffset, windrowOffset)
|
||||||
|
local offset = (ownOffset or 0) - (windrowOffset or 0)
|
||||||
|
return math.max(-ADFieldModel.CP_MAX_TOOL_OFFSET, math.min(ADFieldModel.CP_MAX_TOOL_OFFSET, offset))
|
||||||
|
end
|
||||||
|
|
||||||
function ADFieldModel.toRecord(field)
|
function ADFieldModel.toRecord(field)
|
||||||
return {
|
return {
|
||||||
id = field.id, stage = field.stage, mode = field.plan.mode, product = field.plan.product,
|
id = field.id, stage = field.stage, mode = field.plan.mode, product = field.plan.product,
|
||||||
@@ -176,6 +185,7 @@ function ADFieldModel.toRecord(field)
|
|||||||
mowWidth = field.mowWidth or 0,
|
mowWidth = field.mowWidth or 0,
|
||||||
mowVehicle = field.mowVehicleId or "",
|
mowVehicle = field.mowVehicleId or "",
|
||||||
rakeVehicle = field.rakeVehicleId or "",
|
rakeVehicle = field.rakeVehicleId or "",
|
||||||
|
windrowOffset = field.windrowOffset ~= nil and string.format("%.2f", field.windrowOffset) or "",
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -189,5 +199,6 @@ function ADFieldModel.fromRecord(record)
|
|||||||
field.mowWidth = (record.mowWidth or 0) > 0 and record.mowWidth or nil
|
field.mowWidth = (record.mowWidth or 0) > 0 and record.mowWidth or nil
|
||||||
field.mowVehicleId = record.mowVehicle ~= nil and record.mowVehicle ~= "" and record.mowVehicle or nil
|
field.mowVehicleId = record.mowVehicle ~= nil and record.mowVehicle ~= "" and record.mowVehicle or nil
|
||||||
field.rakeVehicleId = record.rakeVehicle ~= nil and record.rakeVehicle ~= "" and record.rakeVehicle or nil
|
field.rakeVehicleId = record.rakeVehicle ~= nil and record.rakeVehicle ~= "" and record.rakeVehicle or nil
|
||||||
|
field.windrowOffset = tonumber(record.windrowOffset)
|
||||||
return field
|
return field
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ function ADFieldStorage.save(state)
|
|||||||
xmlFile:setFloat(key .. "#mowWidth", record.mowWidth)
|
xmlFile:setFloat(key .. "#mowWidth", record.mowWidth)
|
||||||
xmlFile:setString(key .. "#mowVehicle", record.mowVehicle)
|
xmlFile:setString(key .. "#mowVehicle", record.mowVehicle)
|
||||||
xmlFile:setString(key .. "#rakeVehicle", record.rakeVehicle)
|
xmlFile:setString(key .. "#rakeVehicle", record.rakeVehicle)
|
||||||
|
xmlFile:setString(key .. "#windrowOffset", record.windrowOffset)
|
||||||
index = index + 1
|
index = index + 1
|
||||||
end
|
end
|
||||||
local vehicleIndex = 0
|
local vehicleIndex = 0
|
||||||
@@ -55,6 +56,7 @@ function ADFieldStorage.load()
|
|||||||
rake = xmlFile:getInt(key .. "#rake"), store = xmlFile:getString(key .. "#store"),
|
rake = xmlFile:getInt(key .. "#rake"), store = xmlFile:getString(key .. "#store"),
|
||||||
mowWidth = xmlFile:getFloat(key .. "#mowWidth"), mowVehicle = xmlFile:getString(key .. "#mowVehicle"),
|
mowWidth = xmlFile:getFloat(key .. "#mowWidth"), mowVehicle = xmlFile:getString(key .. "#mowVehicle"),
|
||||||
rakeVehicle = xmlFile:getString(key .. "#rakeVehicle"),
|
rakeVehicle = xmlFile:getString(key .. "#rakeVehicle"),
|
||||||
|
windrowOffset = xmlFile:getString(key .. "#windrowOffset"),
|
||||||
})
|
})
|
||||||
if field.id ~= nil then result.fields[field.id] = field end
|
if field.id ~= nil then result.fields[field.id] = field end
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -111,6 +111,10 @@ function SmartPickupFrame:fillFieldDetail(adEnv, info)
|
|||||||
if field.mowWidth ~= nil then
|
if field.mowWidth ~= nil then
|
||||||
self:addTextRow(string.format(i18n:getText("spu_field_mowWidth"), field.mowWidth))
|
self:addTextRow(string.format(i18n:getText("spu_field_mowWidth"), field.mowWidth))
|
||||||
end
|
end
|
||||||
|
if field.plan.rake and field.windrowOffset ~= nil then
|
||||||
|
self:addTextRow(string.format(i18n:getText("spu_field_windrowOffset"), math.abs(field.windrowOffset),
|
||||||
|
i18n:getText(math.abs(field.windrowOffset) <= 0.05 and "spu_field_side_center" or (field.windrowOffset > 0 and "spu_field_side_left" or "spu_field_side_right"))))
|
||||||
|
end
|
||||||
self:fillFieldStoreRows(adEnv, info, field)
|
self:fillFieldStoreRows(adEnv, info, field)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,10 @@
|
|||||||
<text name="spu_field_noRigs" text="ingen køretøjer med slåmaskine, vender, river eller læssevogn"/>
|
<text name="spu_field_noRigs" text="ingen køretøjer med slåmaskine, vender, river eller læssevogn"/>
|
||||||
<text name="spu_field_status" text="Marker i gang: %d af %d"/>
|
<text name="spu_field_status" text="Marker i gang: %d af %d"/>
|
||||||
<text name="spu_field_startRequested" text="F%d startes, når en maskine er ledig"/>
|
<text name="spu_field_startRequested" text="F%d startes, når en maskine er ledig"/>
|
||||||
|
<text name="spu_field_windrowOffset" text=" Riven lægger skåret %.1f m %s — læssevognen forskydes tilsvarende"/>
|
||||||
|
<text name="spu_field_side_center" text="i midten"/>
|
||||||
|
<text name="spu_field_side_left" text="til venstre"/>
|
||||||
|
<text name="spu_field_side_right" text="til højre"/>
|
||||||
<text name="spu_button_start" text="Start run"/>
|
<text name="spu_button_start" text="Start run"/>
|
||||||
<text name="spu_button_stop" text="Stop run"/>
|
<text name="spu_button_stop" text="Stop run"/>
|
||||||
<text name="spu_button_new" text="Nyt run"/>
|
<text name="spu_button_new" text="Nyt run"/>
|
||||||
|
|||||||
@@ -87,6 +87,10 @@
|
|||||||
<text name="spu_field_noRigs" text="no vehicles with mower, tedder, rake or loading wagon"/>
|
<text name="spu_field_noRigs" text="no vehicles with mower, tedder, rake or loading wagon"/>
|
||||||
<text name="spu_field_status" text="Fields in progress: %d of %d"/>
|
<text name="spu_field_status" text="Fields in progress: %d of %d"/>
|
||||||
<text name="spu_field_startRequested" text="F%d starts when a machine is free"/>
|
<text name="spu_field_startRequested" text="F%d starts when a machine is free"/>
|
||||||
|
<text name="spu_field_windrowOffset" text=" The rake drops the swath %.1f m %s — the loading wagon is offset to match"/>
|
||||||
|
<text name="spu_field_side_center" text="in the middle"/>
|
||||||
|
<text name="spu_field_side_left" text="to the left"/>
|
||||||
|
<text name="spu_field_side_right" text="to the right"/>
|
||||||
<text name="spu_button_start" text="Start run"/>
|
<text name="spu_button_start" text="Start run"/>
|
||||||
<text name="spu_button_stop" text="Stop run"/>
|
<text name="spu_button_stop" text="Stop run"/>
|
||||||
<text name="spu_button_new" text="New run"/>
|
<text name="spu_button_new" text="New run"/>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<modDesc descVersion="107">
|
<modDesc descVersion="107">
|
||||||
<author>masterdraco</author>
|
<author>masterdraco</author>
|
||||||
<version>1.23.0.0</version>
|
<version>1.24.0.0</version>
|
||||||
<title>
|
<title>
|
||||||
<en>AD Smart Pickup</en>
|
<en>AD Smart Pickup</en>
|
||||||
</title>
|
</title>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# AD Smart Pickup — brugermanual
|
# AD Smart Pickup — brugermanual
|
||||||
|
|
||||||
Version 1.23.0 · Farming Simulator 25 · tillæg til AutoDrive
|
Version 1.24.0 · Farming Simulator 25 · tillæg til AutoDrive
|
||||||
|
|
||||||
*English version: [USER_MANUAL.md](USER_MANUAL.md)*
|
*English version: [USER_MANUAL.md](USER_MANUAL.md)*
|
||||||
|
|
||||||
@@ -555,7 +555,9 @@ hvilket trin og hvilken maskine. Kræver **Courseplay** ud over AutoDrive.
|
|||||||
smallere end slåmaskinen.
|
smallere end slåmaskinen.
|
||||||
- **Læssevognen** skal ramme skårene og kører derfor et kopieret kursus:
|
- **Læssevognen** skal ramme skårene og kører derfor et kopieret kursus:
|
||||||
- **uden Riv:** slåmaskinens kursus, og vognen skal være **mindst lige så bred som slåbredden**.
|
- **uden Riv:** slåmaskinens kursus, og vognen skal være **mindst lige så bred som slåbredden**.
|
||||||
- **med Riv:** rivens kursus. Skåret ligger midt i hver rivebane, så vognens bredde er ligegyldig.
|
- **med Riv:** rivens kursus, og vognens bredde er ligegyldig. Moden **måler, hvor riven lægger
|
||||||
|
skåret** (midt i banen eller ude til siden) og forskyder læssevognen tilsvarende med Courseplays
|
||||||
|
værktøjsforskydning. Vognens egen indstilling sættes tilbage bagefter. Målingen står under marken.
|
||||||
- Har du kun smalle standardvogne, så slå **Riv** til på marken (også ved ensilage). Med Unrealistic Pack: sæt slåmaskinen og
|
- Har du kun smalle standardvogne, så slå **Riv** til på marken (også ved ensilage). Med Unrealistic Pack: sæt slåmaskinen og
|
||||||
læssevognene til samme bredde, fx 40,8 m.
|
læssevognene til samme bredde, fx 40,8 m.
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -1,6 +1,6 @@
|
|||||||
# AD Smart Pickup — user manual
|
# AD Smart Pickup — user manual
|
||||||
|
|
||||||
Version 1.23.0 · Farming Simulator 25 · add-on for AutoDrive
|
Version 1.24.0 · Farming Simulator 25 · add-on for AutoDrive
|
||||||
|
|
||||||
*Dansk version: [BRUGERMANUAL.md](BRUGERMANUAL.md)*
|
*Dansk version: [BRUGERMANUAL.md](BRUGERMANUAL.md)*
|
||||||
|
|
||||||
@@ -561,8 +561,10 @@ which step and which machine. Requires **Courseplay** in addition to AutoDrive.
|
|||||||
narrower than the mower.
|
narrower than the mower.
|
||||||
- **The loading wagon** must hit the swaths, so it drives a copied course:
|
- **The loading wagon** must hit the swaths, so it drives a copied course:
|
||||||
- **without Rake:** the mower's course, and the wagon must be **at least as wide as the mowing width**.
|
- **without Rake:** the mower's course, and the wagon must be **at least as wide as the mowing width**.
|
||||||
- **with Rake:** the rake's course. The swath lies in the middle of each rake lane, so the wagon's width
|
- **with Rake:** the rake's course, and the wagon's width does not matter. The mod **measures where the
|
||||||
does not matter.
|
rake drops the swath** (centre or to the side) and offsets the loading wagon to match using
|
||||||
|
Courseplay's tool offset. The wagon's own setting is restored afterwards. The measurement is shown
|
||||||
|
under the field.
|
||||||
- If you only have narrow standard wagons, turn **Rake** on for the field (also for silage). With Unrealistic Pack: set the mower and the
|
- If you only have narrow standard wagons, turn **Rake** on for the field (also for silage). With Unrealistic Pack: set the mower and the
|
||||||
loading wagons to the same width, such as 40.8 m.
|
loading wagons to the same width, such as 40.8 m.
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ Ikke med i etape 1: mejetærskerafgrøder (etape 2), roer (etape 3), jordbehandl
|
|||||||
eget kursus (intet breddekrav). Læssevognen følger skårene: uden riv slåmaskinens kursus og bredde ≥
|
eget kursus (intet breddekrav). Læssevognen følger skårene: uden riv slåmaskinens kursus og bredde ≥
|
||||||
slåbredde; med riv rivens kursus (skåret ligger midt i banen) og intet breddekrav. Riv kan vælges til
|
slåbredde; med riv rivens kursus (skåret ligger midt i banen) og intet breddekrav. Riv kan vælges til
|
||||||
både hø og ensilage.
|
både hø og ensilage.
|
||||||
|
**Sideriver (25/9):** mens riven arbejder, måles nedlægningsområdets midte (runtime-felt
|
||||||
|
`dropWindrowWorkAreaIndex`, ellers riverens XML) i traktorens ramme minus rivens CP-forskydning =
|
||||||
|
skårets sideforskydning (gemt pr. mark). Læssevognen på rivens kursus får `toolOffsetX = egen −
|
||||||
|
skårets` (±10 m) og får sin egen værdi tilbage bagefter.
|
||||||
7. **Silovalg** genbruger Smart Pickups motor (`ADLoadSwap`): nærmeste silo med plads, der tager varen,
|
7. **Silovalg** genbruger Smart Pickups motor (`ADLoadSwap`): nærmeste silo med plads, der tager varen,
|
||||||
med vej fra `F`-markøren. *Tving lager* pr. mark (som "Lager til" i flows). Fabrikker er ikke mål.
|
med vej fra `F`-markøren. *Tving lager* pr. mark (som "Lager til" i flows). Fabrikker er ikke mål.
|
||||||
8. **Marksilo.** En silo, hvis afstand til markens polygon er ≤ 150 m, er et mellemlager. Den må gerne
|
8. **Marksilo.** En silo, hvis afstand til markens polygon er ≤ 150 m, er et mellemlager. Den må gerne
|
||||||
|
|||||||
@@ -35,5 +35,31 @@ local zeroCp = {getVariableWorkWidth = function(_, isLeft) return isLeft and 5 o
|
|||||||
getCourseGeneratorSettings = function() return {workWidth = {getValue = function() return 0 end}} end}
|
getCourseGeneratorSettings = function() return {workWidth = {getValue = function() return 0 end}} end}
|
||||||
check("W5 CP 0 -> egen beregning", ADCpBridge.getWorkWidth(zeroCp), 10)
|
check("W5 CP 0 -> egen beregning", ADCpBridge.getWorkWidth(zeroCp), 10)
|
||||||
|
|
||||||
|
-- WO: skårets sideforskydning på en river, målt fra nedlægningsområdet (venstre positiv), minus rivens
|
||||||
|
-- egen CP-forskydning. Noder er tal; localToLocal-mock giver nodens x i traktorens ramme.
|
||||||
|
local nodeX = {[11] = 2.0, [12] = 4.0, [21] = -1.0, [22] = 1.0}
|
||||||
|
function localToLocal(node) return nodeX[node] or 0, 0, 0 end
|
||||||
|
local function rake(dropIndex, runtime)
|
||||||
|
local areas = {
|
||||||
|
{start = 1, width = 2, height = 3, dropWindrowWorkAreaIndex = runtime and dropIndex or nil},
|
||||||
|
{start = 10, width = 11, height = 12},
|
||||||
|
{start = 20, width = 21, height = 22},
|
||||||
|
}
|
||||||
|
return {spec_windrower = {}, spec_workArea = {workAreas = areas}, configFileName = "rake.xml"}
|
||||||
|
end
|
||||||
|
local sideRake = rake(2, true)
|
||||||
|
local tractorWithRake = {rootNode = 100, getAttachedImplements = function() return {{object = sideRake}} end,
|
||||||
|
getCpSettings = function() return {toolOffsetX = {getValue = function() return 0.5 end}} end}
|
||||||
|
check("WO1 sideriver (runtime-felt)", ADCpBridge.getWindrowOffset(tractorWithRake), 2.5)
|
||||||
|
local centerRake = rake(3, true)
|
||||||
|
local tractorCenter = {rootNode = 100, getAttachedImplements = function() return {{object = centerRake}} end}
|
||||||
|
check("WO2 midterrive", ADCpBridge.getWindrowOffset(tractorCenter), 0)
|
||||||
|
-- WO3: runtime-felt mangler -> riverens XML (workArea(0) windrower#dropWindrowWorkAreaIndex = 2)
|
||||||
|
ADCpBridge.readDropIndicesFromXml = function(fileName) return fileName == "rake.xml" and {[1] = 2} or {} end
|
||||||
|
local xmlRake = rake(2, false)
|
||||||
|
local tractorXml = {rootNode = 100, getAttachedImplements = function() return {{object = xmlRake}} end}
|
||||||
|
check("WO3 fra XML", ADCpBridge.getWindrowOffset(tractorXml), 3)
|
||||||
|
check("WO4 ingen river -> nil", ADCpBridge.getWindrowOffset({rootNode = 100}), nil)
|
||||||
|
|
||||||
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
||||||
os.exit(failures == 0 and 0 or 1)
|
os.exit(failures == 0 and 0 or 1)
|
||||||
|
|||||||
@@ -121,5 +121,17 @@ local withRake = M.with(raked, {rakeVehicleId = "vehicleRake"})
|
|||||||
check("M11 rivemaskine gemt", M.fromRecord(M.toRecord(withRake)).rakeVehicleId, "vehicleRake")
|
check("M11 rivemaskine gemt", M.fromRecord(M.toRecord(withRake)).rakeVehicleId, "vehicleRake")
|
||||||
check("M11 start glemmer rivemaskine", M.start(M.with(M.onReadiness(M.newField(9), 1), {rakeVehicleId = "x"})).rakeVehicleId, nil)
|
check("M11 start glemmer rivemaskine", M.start(M.with(M.onReadiness(M.newField(9), 1), {rakeVehicleId = "x"})).rakeVehicleId, nil)
|
||||||
|
|
||||||
|
-- M12: sideriver — læssevognens CP-forskydning = egen forskydning − skårets forskydning (venstre positiv),
|
||||||
|
-- holdt inden for Courseplays ±10 m; skårets forskydning gemmes pr. mark
|
||||||
|
check("M12 midterrive", M.collectorOffset(0, 0), 0)
|
||||||
|
check("M12 skår 3 m til venstre", M.collectorOffset(0, 3), -3)
|
||||||
|
check("M12 vogn med egen forskydning", M.collectorOffset(0.5, -2), 2.5)
|
||||||
|
check("M12 holdes inden for 10 m", M.collectorOffset(0, 14), -10)
|
||||||
|
check("M12 ukendt skår -> egen forskydning", M.collectorOffset(0.4, nil), 0.4)
|
||||||
|
local side = M.with(M.newField(11), {windrowOffset = 3.2})
|
||||||
|
check("M12 skår gemt", M.fromRecord(M.toRecord(side)).windrowOffset, 3.2)
|
||||||
|
check("M12 intet skår gemt", M.fromRecord(M.toRecord(M.newField(12))).windrowOffset, nil)
|
||||||
|
check("M12 start glemmer skår", M.start(M.with(M.onReadiness(M.newField(13), 1), {windrowOffset = 1})).windrowOffset, nil)
|
||||||
|
|
||||||
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
|
||||||
os.exit(failures == 0 and 0 or 1)
|
os.exit(failures == 0 and 0 or 1)
|
||||||
|
|||||||
Reference in New Issue
Block a user