diff --git a/config/InfoTexts.xml b/config/InfoTexts.xml index 3133e3c8..39c1394a 100644 --- a/config/InfoTexts.xml +++ b/config/InfoTexts.xml @@ -24,6 +24,7 @@ + @@ -41,4 +42,5 @@ + \ No newline at end of file diff --git a/config/MasterTranslations.xml b/config/MasterTranslations.xml index aeb8eb7c..f0cb2157 100644 --- a/config/MasterTranslations.xml +++ b/config/MasterTranslations.xml @@ -71,10 +71,6 @@ - - - - @@ -389,6 +385,10 @@ + + + + @@ -1724,6 +1724,10 @@ The course is saved automatically on closing of the editor and overrides the sel + + + + diff --git a/scripts/Course.lua b/scripts/Course.lua index 09bcf215..39adf18b 100644 --- a/scripts/Course.lua +++ b/scripts/Course.lua @@ -76,22 +76,6 @@ function Course:getVehicle() return self.vehicle end -function Course:setFieldPolygon(polygon) - self.fieldPolygon = polygon -end - --- The field polygon used to generate the course -function Course:getFieldPolygon() - local i = 1 - while self.fieldPolygon == nil and i < self:getNumberOfWaypoints() do - CpUtil.debugVehicle(CpDebug.DBG_COURSES, self.vehicle, 'Field polygon not found, regenerating it (%d).', i) - local px, _, pz = self:getWaypointPosition(i) - self.fieldPolygon = CpFieldUtil.getFieldPolygonAtWorldPosition(px, pz) - i = i + 1 - end - return self.fieldPolygon -end - function Course:getName() return self.name end diff --git a/scripts/ai/AIMessages.lua b/scripts/ai/AIMessages.lua index a7563f0e..7e69409a 100644 --- a/scripts/ai/AIMessages.lua +++ b/scripts/ai/AIMessages.lua @@ -54,6 +54,13 @@ function AIMessageErrorWrongMissionFruitType:getI18NText() return g_i18n:getText("CP_ai_messageErrorWrongMissionFruitType") end +---@class AIMessageErrorTooFarFromField +AIMessageErrorTooFarFromField = CpObject(AIMessage, AIMessage.new) +AIMessageErrorTooFarFromField.name = "CP_ERROR_TOO_FAR_FROM_FIELD" +function AIMessageErrorTooFarFromField:getI18NText() + return g_i18n:getText("CP_ai_messageErrorTooFarFromField") +end + CpAIMessages = {} function CpAIMessages.register() local function register(messageClass) @@ -67,6 +74,7 @@ function CpAIMessages.register() register(AIMessageErrorCutterNotSupported) register(AIMessageErrorAutomaticCutterAttachNotActive) register(AIMessageErrorWrongMissionFruitType) + register(AIMessageErrorTooFarFromField) end --- Another ugly hack, as the giants code to get the message index in mp isn't working .. diff --git a/scripts/ai/jobs/CpAIJob.lua b/scripts/ai/jobs/CpAIJob.lua index f9ac15b9..bb981306 100644 --- a/scripts/ai/jobs/CpAIJob.lua +++ b/scripts/ai/jobs/CpAIJob.lua @@ -248,6 +248,38 @@ function CpAIJob:validate(farmId) return isValid, errorMessage end +--- Start an asynchronous field boundary detection. Results are delivered by the callback +--- onFieldBoundaryDetectionFinished(vehicle, fieldPolygon, islandPolygons) +--- If the field position hasn't changed since the last call, the detection is skipped and this returns true. +--- In that case, the polygon from the previous run is still available from vehicle:cpGetFieldPolygon() +---@return boolean, string TODO +function CpAIJob:detectFieldBoundary() + local vehicle = self.vehicleParameter:getVehicle() + + local tx, tz = self.cpJobParameters.fieldPosition:getPosition() + if tx == nil or tz == nil then + return false, g_i18n:getText("CP_error_not_on_field") + end + if vehicle:cpIsFieldBoundaryDetectionRunning() then + return false, g_i18n:getText("CP_error_field_detection_still_running") + end + local x, z = vehicle:cpGetFieldPosition() + if x == tx and z == tz then + self:debug('Field position still at %.1f/%.1f, do not detect field boundary again', tx, tz) + return true, '' + end + self:debug('Field position changed to %.1f/%.1f, start field boundary detection', tx, tz) + self.foundVines = nil + + vehicle:cpDetectFieldBoundary(tx, tz, self, self.onFieldBoundaryDetectionFinished) + -- TODO: return false and nothing, as the detection is still running? + return true, g_i18n:getText('CP_error_field_detection_still_running') +end + +function CpAIJob:onFieldBoundaryDetectionFinished(vehicle, fieldPolygon, islandPolygons) + -- override in the derived classes to handle the detected field boundary +end + function CpAIJob:getIsStartable(connection) local vehicle = self.vehicleParameter:getVehicle() diff --git a/scripts/ai/jobs/CpAIJobBaleFinder.lua b/scripts/ai/jobs/CpAIJobBaleFinder.lua index 45aa7215..4f311ea7 100644 --- a/scripts/ai/jobs/CpAIJobBaleFinder.lua +++ b/scripts/ai/jobs/CpAIJobBaleFinder.lua @@ -4,7 +4,6 @@ CpAIJobBaleFinder = CpObject(CpAIJob) CpAIJobBaleFinder.name = "BALE_FINDER_CP" CpAIJobBaleFinder.jobName = "CP_job_baleCollect" -CpAIJobBaleFinder.minStartDistanceToField = 20 function CpAIJobBaleFinder:init(isServer) CpAIJob.init(self, isServer) self.selectedFieldPlot = FieldPlot(true) @@ -28,10 +27,9 @@ function CpAIJobBaleFinder:getIsAvailableForVehicle(vehicle, cpJobsAllowed) end function CpAIJobBaleFinder:getCanStartJob() - return self:getFieldPolygon() ~= nil + return self:getVehicle():cpGetFieldPolygon() ~= nil end - function CpAIJobBaleFinder:applyCurrentState(vehicle, mission, farmId, isDirectStart, isStartPositionInvalid) CpAIJob.applyCurrentState(self, vehicle, mission, farmId, isDirectStart) self.cpJobParameters:validateSettings() @@ -64,43 +62,11 @@ function CpAIJobBaleFinder:validate(farmId) -------------------------------------------------------------- --- Validate field setup -------------------------------------------------------------- - - isValid, errorMessage = self:validateFieldPosition(isValid, errorMessage) - local fieldPolygon = self:getFieldPolygon() - -------------------------------------------------------------- - --- Validate start distance to field, if started with the hud - -------------------------------------------------------------- - if isValid and self.isDirectStart and fieldPolygon then - --- Checks the distance for starting with the hud, as a safety check. - --- Firstly check, if the vehicle is near the field. - local x, _, z = getWorldTranslation(vehicle.rootNode) - isValid = CpMathUtil.isPointInPolygon(fieldPolygon, x, z) or - CpMathUtil.getClosestDistanceToPolygonEdge(fieldPolygon, x, z) < self.minStartDistanceToField - if not isValid then - return false, g_i18n:getText("CP_error_vehicle_too_far_away_from_field") - end - end - + isValid, errorMessage = self:detectFieldBoundary(isValid, errorMessage) return isValid, errorMessage end -function CpAIJobBaleFinder:validateFieldPosition(isValid, errorMessage) - local tx, tz = self.cpJobParameters.fieldPosition:getPosition() - if tx == nil or tz == nil then - return false, g_i18n:getText("CP_error_not_on_field") - end - local fieldPolygon, _ = CpFieldUtil.getFieldPolygonAtWorldPosition(tx, tz) - self:setFieldPolygon(fieldPolygon) - if fieldPolygon then - self.selectedFieldPlot:setWaypoints(fieldPolygon) - self.selectedFieldPlot:setVisible(true) - else - return false, g_i18n:getText("CP_error_not_on_field") - end - return isValid, errorMessage -end - function CpAIJobBaleFinder:draw(map, isOverviewMap) CpAIJob.draw(self, map, isOverviewMap) if not isOverviewMap then diff --git a/scripts/ai/jobs/CpAIJobFieldWork.lua b/scripts/ai/jobs/CpAIJobFieldWork.lua index cd352e43..4d34981d 100644 --- a/scripts/ai/jobs/CpAIJobFieldWork.lua +++ b/scripts/ai/jobs/CpAIJobFieldWork.lua @@ -10,11 +10,10 @@ CpAIJobFieldWork.jobName = "CP_job_fieldWork" CpAIJobFieldWork.GenerateButton = "FIELDWORK_BUTTON" function CpAIJobFieldWork:init(isServer) CpAIJob.init(self, isServer) - self.logger = Logger('CpAIJobFieldWork', nil, CpDebug.DBG_FIELDWORK) - self.hasValidPosition = false self.foundVines = nil self.selectedFieldPlot = FieldPlot(true) self.selectedFieldPlot:setVisible(false) + self.selectedFieldPlot:setBrightColor(true) self.courseGeneratorInterface = CourseGeneratorInterface() end @@ -77,7 +76,6 @@ end ---@param isDirectStart boolean disables the drive to by giants ---@param resetToVehiclePosition boolean resets the drive to target position by giants and the field position to the vehicle position. function CpAIJobFieldWork:applyCurrentState(vehicle, mission, farmId, isDirectStart, resetToVehiclePosition) - print('********************** Apply current state **********************') CpAIJob.applyCurrentState(self, vehicle, mission, farmId, isDirectStart) if resetToVehiclePosition then -- set the start and the field position to the vehicle's position ( @@ -97,39 +95,16 @@ function CpAIJobFieldWork:applyCurrentState(vehicle, mission, farmId, isDirectSt self.cpJobParameters.fieldPosition:setPosition(x, z) end end -end - ---- Checks the field position setting. -function CpAIJobFieldWork:validateFieldSetup() - print('********************** Validate field setup **********************') - local vehicle = self.vehicleParameter:getVehicle() - - -- everything else is valid, now find the field - local tx, tz = self.cpJobParameters.fieldPosition:getPosition() - if tx == nil or tz == nil then - return false, g_i18n:getText("CP_error_not_on_field") - end - if vehicle:cpIsFieldBoundaryDetectionRunning() then - return false, g_i18n:getText("CP_error_field_detection_still_running") - end - local x, z = vehicle:cpGetFieldPosition() - if x == tx and z == tz then - self.logger:debug(vehicle, 'Field position still at %.1f/%.1f, do not detect field boundary again', tx, tz) - return true, '' + local fieldPolygon = vehicle:cpGetFieldPolygon() + if fieldPolygon then + -- if we already have a field polygon, show it + self.selectedFieldPlot:setWaypoints(fieldPolygon) + self.selectedFieldPlot:setVisible(true) end - self.logger:debug(vehicle, 'Field position changed to %.1f/%.1f, start field boundary detection', tx, tz) - self.hasValidPosition = false - self.foundVines = nil - - vehicle:cpDetectFieldBoundary(tx, tz, self, CpAIJobFieldWork.onFieldBoundaryDetectionFinished) - -- TODO: return false and nothing, as the detection is still running? end function CpAIJobFieldWork:onFieldBoundaryDetectionFinished(vehicle, fieldPolygon, islandPolygons) - - self:setFieldPolygon(fieldPolygon) if fieldPolygon then - self.hasValidPosition = true local x, z = vehicle:cpGetFieldPosition() self.foundVines = g_vineScanner:findVineNodesInField(fieldPolygon, x, z, self.customField ~= nil) if self.foundVines then @@ -138,7 +113,6 @@ function CpAIJobFieldWork:onFieldBoundaryDetectionFinished(vehicle, fieldPolygon end self.selectedFieldPlot:setWaypoints(fieldPolygon) self.selectedFieldPlot:setVisible(true) - self.selectedFieldPlot:setBrightColor(true) else self.selectedFieldPlot:setVisible(false) -- TODO: here we need to tell somehow the frame about the detection success/failure @@ -167,7 +141,7 @@ function CpAIJobFieldWork:validate(farmId) --- Only check the valid field position in the in game menu. if not self.isDirectStart then - isValid, errorMessage = self:validateFieldSetup() + isValid, errorMessage = self:detectFieldBoundary() if not isValid then return isValid, errorMessage end @@ -190,7 +164,8 @@ function CpAIJobFieldWork:draw(map, isOverviewMap) end function CpAIJobFieldWork:getCanGenerateFieldWorkCourse() - return self.hasValidPosition + local vehicle = self:getVehicle() + return vehicle and vehicle:cpGetFieldPolygon() ~= nil and not vehicle:cpIsFieldBoundaryDetectionRunning() end -- To pass an alignment course from the drive to fieldwork start to the fieldwork, so the diff --git a/scripts/ai/strategies/AIDriveStrategyCombineCourse.lua b/scripts/ai/strategies/AIDriveStrategyCombineCourse.lua index 2d3816d2..9849a75d 100644 --- a/scripts/ai/strategies/AIDriveStrategyCombineCourse.lua +++ b/scripts/ai/strategies/AIDriveStrategyCombineCourse.lua @@ -122,6 +122,14 @@ function AIDriveStrategyCombineCourse:getStateAsString() return s end +--- Combine needs a field polygon for the self-unload to work. Although it is a user setting, but it can be +--- changed during the work, so we always require the field polygon, regardless of the setting, as it is checked +--- only after starting the CP driver. +---@return boolean true if the strategy needs the field polygon to work +function AIDriveStrategyCombineCourse:needsFieldPolygon() + return true +end + ----------------------------------------------------------------------------------------------------------------------- --- Initialization ----------------------------------------------------------------------------------------------------------------------- diff --git a/scripts/ai/strategies/AIDriveStrategyCourse.lua b/scripts/ai/strategies/AIDriveStrategyCourse.lua index 7bd9f5c9..febd8f49 100644 --- a/scripts/ai/strategies/AIDriveStrategyCourse.lua +++ b/scripts/ai/strategies/AIDriveStrategyCourse.lua @@ -25,6 +25,7 @@ AIDriveStrategyCourse = CpObject() AIDriveStrategyCourse.myStates = { INITIAL = {}, WAITING_FOR_PATHFINDER = {}, + WAITING_FOR_FIELD_BOUNDARY_DETECTION = {}, } --- Implement controller events. @@ -703,4 +704,32 @@ function AIDriveStrategyCourse:updateInfoTexts() self:clearInfoText(infoText) end end -end \ No newline at end of file +end + +------------------------------------------------------------------------------------------------------------------------ +--- Field boundary detection +--------------------------------------------------------------------------------------------------------------------------- +--- Some strategies need to know the field boundaries. Bale finder must search for bales on the field, combine +--- unload will look for harvesters on the field, self-unload will look for trailers around the field. When +--- these strategies are started directly from the HUD or by a shortcut, they won't necessarily have a the +--- field boundary yet, as detection is an asynchronous process. Once the detection is done, the field polygon is +--- available in the CpCourseGenerator specialization by calling cpGetFieldPolygon(). +--- +--- Strategies that need the boundary should set the state WAITING_FOR_FIELD_BOUNDARY_DETECTION and call this on +--- until it returns true and only then transition to the INITIAL state. +--- +---@return boolean true if the field boundary is already available +function AIDriveStrategyCourse:haveFieldPolygon() + if self.fieldPolygon == nil then + if self.vehicle:cpGetFieldPolygon() then + self:clearInfoText(InfoTextManager.WAITING_FOR_FIELD_BOUNDARY_DETECTION) + self.fieldPolygon = self.vehicle:cpGetFieldPolygon() + return true + else + self:setInfoText(InfoTextManager.WAITING_FOR_FIELD_BOUNDARY_DETECTION) + return false + end + else + return true + end +end diff --git a/scripts/ai/strategies/AIDriveStrategyFieldWorkCourse.lua b/scripts/ai/strategies/AIDriveStrategyFieldWorkCourse.lua index 281b018c..83bd1009 100644 --- a/scripts/ai/strategies/AIDriveStrategyFieldWorkCourse.lua +++ b/scripts/ai/strategies/AIDriveStrategyFieldWorkCourse.lua @@ -94,12 +94,27 @@ function AIDriveStrategyFieldWorkCourse:start(course, startIx, jobParameters) --- Store a reference to the original generated course self.originalGeneratedFieldWorkCourse = self.vehicle:getFieldWorkCourse() - if self.fieldPolygon == nil then - self:debug("No field polygon received, so regenerate it by the course.") - self.fieldPolygon = self.fieldWorkCourse:getFieldPolygon() + if self:needsFieldPolygon() then + self.fieldPolygon = self.vehicle:cpGetFieldPolygon() + if self.fieldPolygon == nil then + self:debug("Need field boundary to work, start detection now.") + -- TODO: should really store the field position (or the polygon itself?) with the course, as + -- in some rare cases, for instance when using field margin, the start waypoint may be outside + -- of the field, and thus the detection won't work. + local x, _, z = self.fieldWorkCourse:getWaypointPosition(startIx) + -- no callback, haveFieldPolygon() will take care of the result + self.vehicle:cpDetectFieldBoundary(x, z) + end end end +--- If the strategy needs a field polygon to work, it won't transition out of the INITIAL state +--- until the field detection, an asynchronous process that may have started only when the job was started, is finished. +---@return boolean true if the strategy needs the field polygon to work +function AIDriveStrategyFieldWorkCourse:needsFieldPolygon() + return false +end + --- Make sure all implements are in the working state function AIDriveStrategyFieldWorkCourse:prepareForFieldWork() self.vehicle:raiseAIEvent('onAIFieldWorkerPrepareForWork', 'onAIImplementPrepareForWork') @@ -163,8 +178,11 @@ function AIDriveStrategyFieldWorkCourse:getDriveData(dt, vX, vY, vZ) ---------------------------------------------------------------- if self.state == self.states.INITIAL then self:setMaxSpeed(0) - self:startWaitingForLower() - self:lowerImplements() + if not self:needsFieldPolygon() or (self:needsFieldPolygon() and self:haveFieldPolygon()) then + -- continue only if we have the field polygon, if we need it. + self:startWaitingForLower() + self:lowerImplements() + end elseif self.state == self.states.WAITING_FOR_LOWER then self:setMaxSpeed(0) if self:getCanContinueWork() then @@ -755,10 +773,6 @@ function AIDriveStrategyFieldWorkCourse:setAllStaticParameters() self.fieldWorkerProximityController = FieldWorkerProximityController(self.vehicle, self.workWidth) end -function AIDriveStrategyFieldWorkCourse:setFieldPolygon(polygon) - self.fieldPolygon = polygon -end - ----------------------------------------------------------------------------------------------------------------------- --- Dynamic parameters (may change while driving) ----------------------------------------------------------------------------------------------------------------------- diff --git a/scripts/ai/strategies/AIDriveStrategyFindBales.lua b/scripts/ai/strategies/AIDriveStrategyFindBales.lua index d349c839..56c64b66 100644 --- a/scripts/ai/strategies/AIDriveStrategyFindBales.lua +++ b/scripts/ai/strategies/AIDriveStrategyFindBales.lua @@ -36,6 +36,7 @@ AIDriveStrategyFindBales.myStates = { } --- Offset to apply at the goal marker, so we don't crash with an empty unloader waiting there with the same position. AIDriveStrategyFindBales.invertedGoalPositionOffset = -4.5 +AIDriveStrategyFindBales.minStartDistanceToField = 20 function AIDriveStrategyFindBales:init(task, job) AIDriveStrategyCourse.init(self, task, job) @@ -183,32 +184,11 @@ function AIDriveStrategyFindBales:setAllStaticParameters() self.numBalesLeftOver = 0 end -function AIDriveStrategyFindBales:setFieldPolygon(fieldPolygon) - self.fieldPolygon = fieldPolygon -end - --- Bale wrap type for the bale loader. function AIDriveStrategyFindBales:setAIVehicle(vehicle, jobParameters) AIDriveStrategyCourse.setAIVehicle(self, vehicle, jobParameters) self.baleWrapType = jobParameters.baleWrapType:getValue() self:debug("Bale type selected: %s", tostring(self.baleWrapType)) - - local x, z = jobParameters.startPosition:getPosition() - local angle = jobParameters.startPosition:getAngle() - if x ~= nil and z ~= nil and angle ~= nil then - --- Additionally safety check, if the position is on the field or near it. - if CpMathUtil.isPointInPolygon(self.fieldPolygon, x, z) - or CpMathUtil.getClosestDistanceToPolygonEdge(self.fieldPolygon, x, z) < 2 * CpAIJobBaleFinder.minStartDistanceToField then - --- Goal position marker set in the ai menu rotated by 180 degree. - self.invertedStartPositionMarkerNode = CpUtil.createNode("Inverted Start position marker", - x, z, angle + math.pi) - self:debug("Valid goal position marker was set.") - else - self:debug("Start position is too far away from the field for a valid goal position!") - end - else - self:debug("Invalid start position found!") - end end ----------------------------------------------------------------------------------------------------------------------- --- Bale finding @@ -536,7 +516,7 @@ end function AIDriveStrategyFindBales:getDriveData(dt, vX, vY, vZ) self:updateLowFrequencyImplementControllers() self:updateLowFrequencyPathfinder() - if self.state == self.states.INITIAL then + if self.state == self.states.INITIAL and self:isPositionOk() then if self:getCanContinueWork() then self.state = self.states.SEARCHING_FOR_NEXT_BALE else @@ -607,6 +587,46 @@ function AIDriveStrategyFindBales:approachBale() end end +--- Check if we have the field polygon (as we need it to find the bales) and if we are on or close to the field. +--- The field polygon generation is started by the job and if we direct start, may not have finished yet. +--- Once we have it, check the vehicle and start positions. +function AIDriveStrategyFindBales:isPositionOk() + if self:haveFieldPolygon() then + if not self.positionChecked then + self.positionChecked = true + --- check, if the vehicle is near the field. + local x, _, z = getWorldTranslation(self.vehicle.rootNode) + if CpMathUtil.isPointInPolygon(self.fieldPolygon, x, z) or + CpMathUtil.getClosestDistanceToPolygonEdge(self.fieldPolygon, x, z) < AIDriveStrategyFindBales.minStartDistanceToField then + -- now check the start position + x, z = self.jobParameters.startPosition:getPosition() + local angle = self.jobParameters.startPosition:getAngle() + if x ~= nil and z ~= nil and angle ~= nil then + --- Additionally safety check, if the position is on the field or near it. + if CpMathUtil.isPointInPolygon(self.fieldPolygon, x, z) + or CpMathUtil.getClosestDistanceToPolygonEdge(self.fieldPolygon, x, z) < 2 * AIDriveStrategyFindBales.minStartDistanceToField then + --- Goal position marker set in the ai menu rotated by 180 degree. + self.invertedStartPositionMarkerNode = CpUtil.createNode("Inverted Start position marker", + x, z, angle + math.pi) + self:debug("Valid goal position marker was set.") + else + self:debug("Start position is too far away from the field for a valid goal position!") + end + else + self:debug("Invalid start position found!") + end + else + self:debug('Vehicle not on field or too far away from field') + self.vehicle:stopCurrentAIJob(AIMessageErrorTooFarFromField.new()) + return false + end + end + return true + else + return false + end +end + function AIDriveStrategyFindBales:workOnBale() if self.baleLoader then if self:isReadyToLoadNextBale() then diff --git a/scripts/ai/tasks/CpAITaskBaleFinder.lua b/scripts/ai/tasks/CpAITaskBaleFinder.lua index db650c8f..7e486e6b 100644 --- a/scripts/ai/tasks/CpAITaskBaleFinder.lua +++ b/scripts/ai/tasks/CpAITaskBaleFinder.lua @@ -1,12 +1,11 @@ ----@class CpAIJobBaleFinder : CpAITask +---@class CpAITaskBaleFinder : CpAITask CpAITaskBaleFinder = CpObject(CpAITask) function CpAITaskBaleFinder:start() if self.isServer then self:debug("CP bale finder task started.") local strategy = AIDriveStrategyFindBales(self, self.job) - strategy:setFieldPolygon(self.job:getFieldPolygon()) strategy:setAIVehicle(self.vehicle, self.job:getCpJobParameters()) self.vehicle:startCpWithStrategy(strategy) end diff --git a/scripts/ai/tasks/CpAITaskFieldWork.lua b/scripts/ai/tasks/CpAITaskFieldWork.lua index e5a0d562..dc561d87 100644 --- a/scripts/ai/tasks/CpAITaskFieldWork.lua +++ b/scripts/ai/tasks/CpAITaskFieldWork.lua @@ -76,7 +76,7 @@ function CpAITaskFieldWork:start() --- Remembers the last lane offset setting value that was used. cpSpec.cpJobStartAtLastWp:getCpJobParameters().laneOffset:setValue(self.job:getCpJobParameters().laneOffset:getValue()) if spec.driveStrategies ~= nil then - -- This deltition code could be removed, but just to be sure we let it stay here for now. + -- This deletion code could be removed, but just to be sure we let it stay here for now. for i = #spec.driveStrategies, 1, -1 do spec.driveStrategies[i]:delete() table.remove(spec.driveStrategies, i) @@ -105,7 +105,6 @@ function CpAITaskFieldWork:start() cpDriveStrategy = AIDriveStrategyFieldWorkCourse(self, self.job) end end - cpDriveStrategy:setFieldPolygon(self.job:getFieldPolygon()) cpDriveStrategy:setAIVehicle(self.vehicle, self.job:getCpJobParameters()) cpSpec.driveStrategy = cpDriveStrategy --- Only the last driving strategy can stop the helper, while it is running. diff --git a/scripts/courseGenerator/CourseGeneratorInterface.lua b/scripts/courseGenerator/CourseGeneratorInterface.lua index 25e8f3c8..83622374 100644 --- a/scripts/courseGenerator/CourseGeneratorInterface.lua +++ b/scripts/courseGenerator/CourseGeneratorInterface.lua @@ -166,7 +166,6 @@ function CourseGeneratorInterface:generate(fieldPolygon, local course = Course.createFromGeneratedCourse(vehicle, self.generatedCourse, settings.workWidth:getValue(), numberOfHeadlands, settings.multiTools:getValue(), settings.headlandClockwise:getValue(), settings.islandHeadlandClockwise:getValue(), not settings.useBaseLineEdge:getValue()) - course:setFieldPolygon(fieldPolygon) self:setCourse(vehicle, course) return true, course end @@ -228,7 +227,6 @@ function CourseGeneratorInterface:generateVineCourse( local course = Course.createFromGeneratedCourse(vehicle, self.generatedCourse, workWidth, 0, multiTools, true, true, true) - course:setFieldPolygon(fieldPolygon) self:setCourse(vehicle, course) return true, course end diff --git a/scripts/specializations/CpCourseGenerator.lua b/scripts/specializations/CpCourseGenerator.lua index 982c17f8..45643125 100644 --- a/scripts/specializations/CpCourseGenerator.lua +++ b/scripts/specializations/CpCourseGenerator.lua @@ -20,6 +20,8 @@ end function CpCourseGenerator.registerEventListeners(vehicleType) SpecializationUtil.registerEventListener(vehicleType, "onUpdate", CpCourseGenerator) SpecializationUtil.registerEventListener(vehicleType, "onLoad", CpCourseGenerator) + SpecializationUtil.registerEventListener(vehicleType, "onReadStream", CpCourseGenerator) + SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", CpCourseGenerator) end function CpCourseGenerator.registerFunctions(vehicleType) @@ -45,7 +47,7 @@ end function CpCourseGenerator:cpDetectFieldBoundary(x, z, object, onFinishedFunc) local spec = self.spec_cpCourseGenerator if spec.isFieldBoundaryDetectionRunning then - self.logger:warning(self, 'Not starting field boundary detection for %.1f/%.1f, previous for %.1f/%.1f is still running', + spec.logger:warning(self, 'Not starting field boundary detection for %.1f/%.1f, previous for %.1f/%.1f is still running', x, z, spec.position.x, spec.position.z) return end @@ -78,10 +80,12 @@ function CpCourseGenerator:onUpdate(dt) spec.fieldPolygon = spec.fieldBoundaryDetector:getFieldPolygon() spec.islandPolygons = spec.fieldBoundaryDetector:getIslandPolygons() spec.fieldBoundaryDetector = nil - if spec.object then + if spec.object and spec.onFinishedFunc then spec.onFinishedFunc(spec.object, self, spec.fieldPolygon, spec.islandPolygons) - else + elseif spec.onFinishedFunc then spec.onFinishedFunc(self, spec.fieldPolygon, spec.islandPolygons) + else + spec.logger:debug('Field boundary detection finished, but no callback given') end end end @@ -110,3 +114,33 @@ function CpCourseGenerator:cpDrawFieldPolygon() end end end + +function CpCourseGenerator:onReadStream(streamId, connection) + local spec = self.spec_cpCourseGenerator + local numVertices = streamReadInt32(streamId) + if numVertices == 0 then + spec.fieldPolygon = nil + else + spec.fieldPolygon = {} + for _ = 1, numVertices do + local x = streamReadFloat32(streamId) + local y = streamReadFloat32(streamId) + local z = streamReadFloat32(streamId) + table.insert(spec.fieldPolygon, { x = x, y = y, z = z }) + end + end +end + +function CpCourseGenerator:onWriteStream(streamId, connection) + local spec = self.spec_cpCourseGenerator + if spec.fieldPolygon then + streamWriteInt32(streamId, #spec.fieldPolygon) + for _, point in pairs(spec.fieldPolygon) do + streamWriteFloat32(streamId, point.x) + streamWriteFloat32(streamId, point.y) + streamWriteFloat32(streamId, point.z) + end + else + streamWriteInt32(streamId, 0) + end +end \ No newline at end of file