Repository navigation
Switch section control on automatically when an implement connects - #102
Conversation
Until now an implement stayed in manual mode after connecting until the operator pressed AOG's ISOBUS SC button, which sends PGN 241. The TC now remembers the section control state it wants and applies it to each client 2 s after its pool is activated: it sets the local auto flag and writes DDI 160 when the implement declares it. The initial state comes from the new settings.json key "sectionControlOnConnect" (default true). Pressing AOG's button still switches all clients and also becomes the state for implements that connect later. The implement can still decline or go back to manual; its DDI 160 report is followed as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New sectionControlOnFieldOpen setting (default on): when AOG opens a field, the TC switches section control on as if AOG's SC button was pressed (the same update_section_control_enabled(true) that PGN 241 "enabled" triggers). AOG sends the field name (PGN 0xF3) on every field open and close, and again when the TC reconnects. Older AOG versions do not, so PGN 229 starting after a 2 s gap (AOG sends it only while a field is open) counts as a field opening too. Both report the same opening, so it is acted on at most once per 5 s. Field open and close are logged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Field-open detection now uses lastSectionDemandMs and SECTION_DEMAND_TIMEOUT_MS from #108 in place of its own lastSectionStatesMs and FIELD_CLOSED_GAP_MS, which recorded the same PGN 229 arrival with the same 2 s gap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR #84 also adds a PGN 0xF3 branch to this if/else chain, between 0xF1 and 0xF2. In different places, the two merge without a conflict and the second never runs. In the same place, Git reports the conflict. No behaviour change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@sourcery-ai review |
Reviewer's GuideData structuresAdd the desired section-control state, connection scheduling, and demand tracking that the server uses when applying automatic control.
APIExpose settings accessors and the server operation for configuring automatic section control on connection.
Uses of the APIWire the settings into server setup and detect field openings from field names or legacy section-demand traffic.
Load, save, and expose the new automatic section-control settings.
ImplementationSchedule and apply the initial section-control state after client activation, and send current setpoints whenever section control is enabled.
ConfigurationAdd defaults and stored values for the connection and field-open section-control options.
DocumentationDocument the new settings, their defaults, and the field-open detection behavior.
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Linked issuesChecked at commit 3c90c20 #94 Late-registering implement is never told to go to Auto if AOG enabled SC earlier · 1 of 2 met
|
There was a problem hiding this comment.
Hey - I've found 5 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="src/app.cpp" line_range="537" />
<code_context>
tcServer->update_section_states(sectionStates);
+ if (resumed)
+ {
+ on_aog_field_opened("started sending section states (PGN 229)");
+ }
}
</code_context>
<issue_to_address>
**Unauthenticated field events activate implements**
When an untrusted local-network sender can reach the UDP listener and section control is enabled for field openings, a sender on the local network can forge a UDP packet with protocol source `0x7F` and a PGN 229 field-opening pattern or non-empty PGN 0xF3 payload; the handler calls `update_section_control_enabled(true)`, which applies DDI 160 and switches connected implements to automatic without operator authorization.
Authenticate and authorize the UDP sender and validate packet integrity before allowing field events to change implement state.
Also at `src/app.cpp:558`.
</issue_to_address>
### Comment 2
<location path="src/task_controller.cpp" line_range="977-979" />
<code_context>
+ state.set_section_control_enabled(enabled);
+
+ // Only send ISOBUS command to clients that support SectionControlState DDI and have sections
+ if (state.has_element_number_for_ddi(isobus::DataDescriptionIndex::SectionControlState) &&
+ state.get_number_of_sections() > 0)
+ {
+ send_section_control_state(client, enabled);
}
</code_context>
<issue_to_address>
**Implement remains in manual mode**
When an activated implement maps DDI 160 but does not mark it settable, `apply_section_control_state` sends the command and records section control as enabled; the implement rejects it and remains in manual mode while AOG reports enabled.
Check that DDI 160 is settable before sending the command or recording section control as enabled.
</issue_to_address>
### Comment 3
<location path="src/task_controller.cpp" line_range="899-905" />
<code_context>
+
+ // Switch the implement to automatic shortly after it connects, so AOG's SC button
+ // does not need pressing on every connect.
+ client.second.schedule_initial_section_control(isobus::SystemTiming::get_timestamp_ms() + INITIAL_SECTION_CONTROL_DELAY_MS);
+ }
+ if (client.second.take_due_initial_section_control(isobus::SystemTiming::get_timestamp_ms()))
+ {
</code_context>
<issue_to_address>
**Delayed enable overrides manual mode**
When the implement or its operator reports manual mode after activation but before the initial-state timer expires, `take_due_initial_section_control()` applies `desiredSectionControlEnabled` even if the implement has reported DDI 160 = 0 since the schedule was created. The TC therefore sends enabled after the operator has switched the implement to manual, and the implement ends up back in automatic mode.
Cancel or revise the pending initial application when the implement reports a newer section-control state, or otherwise distinguish the initial command from a later operator choice.
</issue_to_address>
### Comment 4
<location path="src/app.cpp" line_range="456" />
<code_context>
+ {
+ return;
+ }
+ if ((0 != lastFieldOpenAppliedMs) && !isobus::SystemTiming::time_expired_ms(lastFieldOpenAppliedMs, FIELD_OPEN_DEDUPLICATE_MS))
+ {
+ return;
</code_context>
<issue_to_address>
**New field openings are ignored**
When AOG opens a distinct field within five seconds of the previous applied opening, `on_aog_field_opened()` returns based only on the elapsed time, so it ignores the new field opening and leaves section control in its previous state, including manual mode if it was turned off.
Deduplicate repeated openings by field identity or transition rather than suppressing every opening within the five-second window.
</issue_to_address>
### Comment 5
<location path="src/app.cpp" line_range="527" />
<code_context>
}
}
+ // Older AOG versions do not send the field name, but PGN 229 only flows while a field is open
+ const bool resumed = (0 == lastSectionDemandMs) || isobus::SystemTiming::time_expired_ms(lastSectionDemandMs, SECTION_DEMAND_TIMEOUT_MS);
lastSectionDemandMs = lastAogPacketMs;
if (sectionStates != sectionDemand)
</code_context>
<issue_to_address>
**Manual-off choice is overridden**
When PGN 229 traffic pauses for more than two seconds while the same field remains open and section control has been turned off, `resumed` becomes true based on the packet gap, so `on_aog_field_opened()` enables section control without a new field opening and overrides the operator's manual-off choice.
Determine whether a field actually opened or reopened instead of treating a PGN 229 traffic gap as a field opening.
</issue_to_address>A gap of more than 2 s in PGN 229 (AOG stalling, lost UDP packets) was treated as a field opening and switched section control back on, even when the operator had switched it off and the same field stayed open. Once AOG has sent a field name (PGN 0xF3), only a change of field name switches section control on; the PGN 229 fallback stays for AOG versions that do not send the field name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tting is off - After section control is switched on (connect, field open, PGN 241), send every condensed setpoint block and DDI 289, forced, from AOG's current demand (all off when none arrived in the last 2 s). Change-only sends never told the implement "all off" when AOG's Auto was off, so it kept running on its own state (ISO 11783-10 F.3.5.3 b). - With sectionControlOnConnect off, schedule nothing on connect. Before, DDI 160 = 0 went out on every connect, and after one SC press or field open every later connect switched on. Helpers match #106 (send_all_section_setpoint_states, forceWorkState) so the two merge cleanly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summary
Closes #94.
An implement used to stay in manual mode after connecting until someone pressed AOG's ISOBUS SC button. With this change the TC switches section control on by itself.
settings.jsonkey,sectionControlOnConnect, which defaults totrue.ClientState, so this also covers implements that connect after startup, reconnects and pool re-uploads.AOG only sends PGN 241 when the button is pressed, and the button shows the state from the TC heartbeat. That means AOG doesn't override the default, and its button shows ON once the implement is switched on.
Switching on when AOG opens a field
A second setting,
sectionControlOnFieldOpen(defaulttrue), does the same thing when AOG opens a field: the TC calls the same code as PGN 241 "enabled", as if the SC button had been pressed.[Field] AOG opened field '...'and[Field] AOG closed the field.Background: in a field log from 2026-10-13, after field changes a spreader in automatic spread full width without receiving setpoints (see #106). This option makes a field change restart automatic section control in the TC without anyone pressing the button.
Notes
"sectionControlOnConnect": falseor"sectionControlOnFieldOpen": falseinsettings.json. Both are listed in the readme. There is no VT Config toggle yet.Testing
[TC] Applying initial section control state: enabledabout 2 s after the pool is activated, the implement reports DDI 160 = 1, and AOG's button shows ON.🤖 Generated with Claude Code
Summary by Sourcery
Automatically synchronize section control with implement connections and AOG field openings while keeping the behavior configurable.
New Features:
Bug Fixes:
Enhancements:
Documentation: