Refactor of valve to use the platform infrastructure (BaseDomain, BaseNodeAdapter, ChildRouter, commandRegistry, statusBadge). Extracts concerns into focused modules per .claude/refactor/MODULE_SPLIT.md generic template. Tests stay green; CONTRACT.md generated; legacy aliases preserved. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
85 lines
2.9 KiB
JavaScript
85 lines
2.9 KiB
JavaScript
'use strict';
|
|
|
|
// Sequence + setpoint execution. Mirrors the pre-refactor Valve.handleInput
|
|
// switch but delegates state transitions to host.state. Pre-shutdown ramp-down
|
|
// to 0 happens here so the existing test contract holds.
|
|
|
|
class FlowController {
|
|
constructor(host) {
|
|
this.host = host;
|
|
this.logger = host.logger;
|
|
}
|
|
|
|
isValidSourceForMode(source, mode) {
|
|
const allowed = this.host.config.mode.allowedSources[mode] || [];
|
|
return allowed.has(source);
|
|
}
|
|
|
|
async handleInput(source, action, parameter) {
|
|
if (!this.isValidSourceForMode(source, this.host.currentMode)) {
|
|
const msg = `Source '${source}' is not valid for mode '${this.host.currentMode}'.`;
|
|
this.logger.warn(msg);
|
|
return { status: false, feedback: msg };
|
|
}
|
|
this.logger.info(`Handling input from source '${source}' with action '${action}' in mode '${this.host.currentMode}'.`);
|
|
try {
|
|
switch (action) {
|
|
case 'execSequence':
|
|
await this.executeSequence(parameter);
|
|
break;
|
|
case 'execMovement':
|
|
await this.setpoint(parameter);
|
|
break;
|
|
case 'emergencyStop':
|
|
case 'emergencystop':
|
|
this.logger.warn(`Emergency stop activated by '${source}'.`);
|
|
await this.executeSequence('emergencystop');
|
|
break;
|
|
case 'statusCheck':
|
|
this.logger.info(`Status Check: Mode = '${this.host.currentMode}', Source = '${source}'.`);
|
|
break;
|
|
default:
|
|
this.logger.warn(`Action '${action}' is not implemented.`);
|
|
}
|
|
this.logger.debug(`Action '${action}' successfully executed`);
|
|
return { status: true, feedback: `Action '${action}' successfully executed.` };
|
|
} catch (error) {
|
|
this.logger.error(`Error handling input: ${error}`);
|
|
}
|
|
}
|
|
|
|
async executeSequence(sequenceName) {
|
|
const sequence = this.host.config.sequences[sequenceName];
|
|
if (!sequence || sequence.size === 0) {
|
|
this.logger.warn(`Sequence '${sequenceName}' not defined.`);
|
|
return;
|
|
}
|
|
if (this.host.state.getCurrentState() === 'operational' && sequenceName === 'shutdown') {
|
|
this.logger.info(`Machine will ramp down to position 0 before performing ${sequenceName} sequence`);
|
|
await this.setpoint(0);
|
|
}
|
|
this.logger.info(` --------- Executing sequence: ${sequenceName} -------------`);
|
|
for (const stateName of sequence) {
|
|
try {
|
|
await this.host.state.transitionToState(stateName);
|
|
} catch (error) {
|
|
this.logger.error(`Error during sequence '${sequenceName}': ${error}`);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
async setpoint(value) {
|
|
try {
|
|
if (typeof value !== 'number' || value < 0) {
|
|
throw new Error('Invalid setpoint: Setpoint must be a non-negative number.');
|
|
}
|
|
await this.host.state.moveTo(value);
|
|
} catch (error) {
|
|
this.logger.error(`Error setting setpoint: ${error}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = FlowController;
|