@hashgraph/hedera-local
Version:
Developer tooling for running Local Hedera Network (Consensus + Mirror Nodes).
131 lines (119 loc) • 4.85 kB
text/typescript
// SPDX-License-Identifier: Apache-2.0
import { CHECK_FAIL, CHECK_SUCCESS } from '../constants';
import { StateData } from '../data/StateData';
import { LoggerService } from '../services/LoggerService';
import { ServiceLocator } from '../services/ServiceLocator';
import { CleanUpState } from '../state/CleanUpState';
import { RecoveryState } from '../state/RecoveryState';
import { EventType } from '../types/EventType';
import { StateConfiguration } from '../types/StateConfiguration';
import { IOBserver } from './IObserver';
/**
* Represents a state controller that manages the state machine.
* Implements the IOBserver interface.
* @implements {IObserver}
*/
export class StateController implements IOBserver{
/**
* Logger service instance.
* @private
*/
private logger: LoggerService;
/**
* Configuration for the state.
* @private
*/
private stateConfiguration: StateConfiguration | undefined;
/**
* Current state number.
* @private
*/
private currStateNum: number;
/**
* Maximum state number.
* @private
*/
private maxStateNum: number;
/**
* Name of the controller.
* @private
*/
private controllerName: string;
/**
* Constructs a new instance of the StateController class.
* @param stateName - The name of the state.
*/
constructor(stateName: string) {
this.controllerName = StateController.name;
this.logger = ServiceLocator.Current.get<LoggerService>(LoggerService.name);
this.stateConfiguration = new StateData().getSelectedStateConfiguration(stateName);
this.currStateNum = 0;
this.maxStateNum = 0;
this.logger.trace(`${CHECK_SUCCESS} State Controller Initialized!`, this.controllerName);
this.logger.trace(`${CHECK_SUCCESS} Initiating ${stateName} procedure!`, this.controllerName);
}
/**
* Starts the state machine.
* If the state configuration is not set, it logs an error and exits the process.
* Subscribes to the current state and calls the onStart method of the current state.
*/
public async startStateMachine() {
if (!this.stateConfiguration) {
this.logger.error(`${CHECK_FAIL} Something is wrong with state configuration!`, this.controllerName);
// TODO: handle error
process.exit(1);
} else {
this.maxStateNum = this.stateConfiguration.states.length - 1;
this.stateConfiguration.states[this.currStateNum].subscribe(this);
await this.stateConfiguration.states[this.currStateNum].onStart();
}
}
/**
* Updates the state based on the given event.
* If the event is EventType.Finish, transitions to the next state.
* If the event is EventType.UnknownError, performs cleanup and exits the process with code 1.
* Otherwise, starts a new RecoveryState with the given event.
* @param {EventType} event - The event type.
* @returns {Promise<void>}
*/
public async update(event: EventType): Promise<void> {
if (event === EventType.Finish) {
await this.transitionToNextState();
} else {
if (event === EventType.UnknownError || event === EventType.UnresolvableError) {
await new CleanUpState().onStart();
process.exit(1);
} else {
await new RecoveryState(event).onStart();
}
}
}
/**
* Transitions to the next state.
* If the current state number is equal to or greater than the maximum state number,
* the process will exit with a code of 0.
* Otherwise, it increments the current state number, subscribes to the next state,
* and calls the onStart method of the next state.
* If an error occurs during the transition, it logs the error and exits the process with a code of 1,
* unless the error is a TypeError, in which case it ignores the error and continues execution.
* @returns {Promise<void>}
* @private
*/
private async transitionToNextState(): Promise<void> {
if (this.currStateNum >= this.maxStateNum) {
process.exit(0);
}
this.currStateNum+=1;
try {
this.stateConfiguration!.states[this.currStateNum].subscribe(this);
await this.stateConfiguration!.states[this.currStateNum].onStart();
} catch (error) {
if (error instanceof TypeError) {
// Ignore this error, it finds the methods and executes the code, but still results in TypeError
} else {
this.logger.error(`Trying to transition to next state was not possible. Error is: ${error}`, this.controllerName);
process.exit(1);
}
}
}
}