143 lines
5.5 KiB
Markdown
143 lines
5.5 KiB
Markdown
# Manual Mode Refactor Proposal
|
|
|
|
**Status:** Implemented
|
|
**Feature:** Enhanced Manual Mode Safety & Interlock Support
|
|
|
|
## Problem Statement
|
|
|
|
Currently, `PressCylinder::onJoystickUp` implements a simplified manual control loop that bypasses safety checks (`CFlags`) and complex logic available in `loopSingle` and `loopMulti`. It directly enables solenoids based on Joystick Position, ignoring:
|
|
|
|
* Load balancing (in multi-cylinder setups)
|
|
* Stall detection
|
|
* Interlock status (implicitly, though safety overrides exist)
|
|
* Operation timeouts
|
|
|
|
The goal is to unify the logic so that "Manual" operation is simply a special case of the robust `loop*` functions, but controlled by the Joystick/PushButton rather than a fixed "Auto" process.
|
|
|
|
## Proposed Strategy
|
|
|
|
We will treat Manual operation as "Auto operation towards a Virtual Maximum Target" that only persists while the operator holds the input (Joystick or PushButton).
|
|
|
|
### 1. Unified Control Logic
|
|
|
|
Instead of direct `SOLENOID_ON/OFF` in `onJoystickUp`, we will:
|
|
|
|
1. Detect "Active Input" (Joystick UP *OR* PushButton Pressed).
|
|
2. If Active:
|
|
* Temporarily override `m_targetSP` to `100%` (or a `manual_override_sp`).
|
|
* Set effective mode to `DEFAULT_AUTO_MODE_NORMAL` (if single/unlocked) or `DEFAULT_AUTO_MODE_INTERLOCKED` (if interlocked).
|
|
* Call `loopSingle()` or `loopMulti()` respectively.
|
|
3. If Inactive:
|
|
* Reset `m_targetSP` to 0 (or restore original).
|
|
* Enforce `SOLENOIDS_OFF`.
|
|
|
|
### 2. Implementation Plan
|
|
|
|
#### A. Define Helper: `getVirtualTargetSP()`
|
|
|
|
For manual moves, we want to press as long as the operator commands, up to the absolute hardware limit (`maxload_threshold` or `100%`).
|
|
|
|
```cpp
|
|
uint32_t PressCylinder::getVirtualTargetSP() {
|
|
// In Manual mode, "Target" is effectively "Go until Max or Stop"
|
|
return 70; // 70% of maxload_threshold (Virtual SP Max)
|
|
}
|
|
```
|
|
|
|
#### B. Refactor `onJoystickUp` / Main Loop Integration
|
|
|
|
We will stop using `onJoystickUp` as a direct actuator. Instead, it becomes an input state detector.
|
|
|
|
**Modified `loop()` Logic:**
|
|
|
|
```cpp
|
|
// 1. Determine "Effective Mode" and "Effective Target"
|
|
E_Mode effective_mode = (E_Mode)m_mode.getValue();
|
|
uint32_t effective_sp = m_targetSP.getValue();
|
|
bool manual_input_active = false;
|
|
|
|
// Check Inputs (Joystick or PushButton)
|
|
// Check Inputs (Joystick or PushButton)
|
|
if (_pushButton->getState() == PushButton::State::PRESSED || _pushButton->getState() == PushButton::State::HELD) {
|
|
// Condition 1: PushButton Held -> Force Manual Single Cylinder
|
|
manual_input_active = true;
|
|
effective_mode = MODE_MANUAL;
|
|
effective_sp = 70;
|
|
}
|
|
else if (_joystick->getPosition() == Joystick::E_POSITION::UP) {
|
|
// Condition 2: Joystick UP -> Smart Auto / Interlock Dependent
|
|
manual_input_active = true;
|
|
effective_sp = 70;
|
|
|
|
if (effective_mode == MODE_MANUAL || effective_mode == MODE_MANUAL_MULTI) {
|
|
if (m_interlocked.getValue()) {
|
|
effective_mode = DEFAULT_AUTO_MODE_INTERLOCKED; // e.g. AUTO_MULTI_BALANCED
|
|
} else {
|
|
effective_mode = DEFAULT_AUTO_MODE_NORMAL; // e.g. AUTO
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. Dispatch to Control Loops
|
|
if (manual_input_active || isAutoRunning()) {
|
|
// These functions now use 'effective_mode' and 'effective_sp'
|
|
// instead of reading m_targetSP/m_mode directly if we pass them as args,
|
|
// OR we temporarily override member vars (simpler but riskier state).
|
|
|
|
// BETTER APPROACH: Refactor loopSingle/loopMulti to take args
|
|
if (isMultiMode(effective_mode)) {
|
|
loopMulti(effective_mode, effective_sp);
|
|
} else {
|
|
loopSingle(effective_mode, effective_sp);
|
|
}
|
|
} else {
|
|
// Idle / Stop
|
|
SOLENOIDS_OFF();
|
|
}
|
|
```
|
|
|
|
### 3. Modifications Required
|
|
|
|
1. **Refactor `loopSingle` / `loopMulti`**:
|
|
* Change signature to accept `E_Mode mode` and `uint16_t target_sp` arguments.
|
|
* Remove internal calls to `m_mode.getValue()` and `m_targetSP.getValue()` in favor of these arguments.
|
|
|
|
2. **Update `onJoystickUp`**:
|
|
* Actually, we might deprecate `onJoystickUp` direct logic entirely and move it into the main `loop()` structure as shown above.
|
|
* Retain `onJoystickDoubleUp` for the HOLD feature (capturing Load to SP).
|
|
|
|
3. **PushButton Integration**:
|
|
* Map `_pushButton->getState() == PushButton::State::PRESSED` (or HELD) to `manual_input_active`.
|
|
|
|
### 4. Safety Considerations
|
|
|
|
* **Deadman Switch**: The moment the Joystick/Button is released, `manual_input_active` becomes false, and the `else` block triggers `SOLENOIDS_OFF()`.
|
|
* **Existing Safety**: `loopSingle/Multi` already contain `CheckMinLoad`, `Stall`, `Balance`, and `MaxTime` checks. By routing manual control through them, we gain all these features automatically.
|
|
* **Virtual SP**: Setting SP to 100% is safe because `loop*` logic checks `canPress()` which respects `maxload_threshold`.
|
|
|
|
## Diagram: New Flow
|
|
|
|
```mermaid
|
|
graph TD
|
|
Input[Inputs: Joystick / Button] --> Check{Active?}
|
|
Check -- Yes --> MapMode[Map to Effective Auto Mode]
|
|
MapMode --> SetSP[Set Virtual SP = 70%]
|
|
SetSP --> Dispatch{Is Multi?}
|
|
|
|
Dispatch -- Single --> LoopSingle[loopSingle(SafeMode, 70%)]
|
|
Dispatch -- Multi --> LoopMulti[loopMulti(SafeMode, 70%)]
|
|
|
|
LoopSingle --> Actuators
|
|
LoopMulti --> Actuators
|
|
|
|
Check -- No --> Stop[SOLENOIDS_OFF]
|
|
```
|
|
|
|
### References
|
|
|
|
### Data
|
|
|
|
cs-1150 - interlocked
|
|
|
|
min load : 50-150
|