193 lines
4.8 KiB
TypeScript
193 lines
4.8 KiB
TypeScript
import { MorphFeature } from 'morphux';
|
|
import { Main } from './main';
|
|
import {
|
|
formatUptime,
|
|
ServiceState,
|
|
setStatusState,
|
|
StatusType,
|
|
} from './utils';
|
|
|
|
const CELCIUS = 'ºC';
|
|
|
|
export class DashboardCameraRunner {
|
|
private _Main: Main;
|
|
|
|
container: HTMLDivElement = document.querySelector(
|
|
'.ntsh_dashboard-camerarunner'
|
|
);
|
|
|
|
connectionStatus: HTMLDivElement = this.container.querySelector(
|
|
'.ntsh_dashboard-camerarunner-connectionstatus'
|
|
);
|
|
connectionInfo: HTMLDivElement = this.container.querySelector(
|
|
'.ntsh_dashboard-camerarunner-connectioninfo'
|
|
);
|
|
rebootButton: HTMLDivElement = this.container.querySelector(
|
|
'.ntsh_dashboard-camerarunner-reboot'
|
|
);
|
|
|
|
processStatus: HTMLDivElement = this.container.querySelector(
|
|
'.ntsh_dashboard-camerarunner-processstatus'
|
|
);
|
|
processInfo: HTMLDivElement = this.container.querySelector(
|
|
'.ntsh_dashboard-camerarunner-processinfo'
|
|
);
|
|
restartButton: HTMLDivElement = this.container.querySelector(
|
|
'.ntsh_dashboard-camerarunner-restart'
|
|
);
|
|
|
|
uptimeInfo: HTMLDivElement = this.container.querySelector(
|
|
'.ntsh_dashboard-camerarunner-uptime'
|
|
);
|
|
|
|
errorContainer: HTMLDivElement = this.container.querySelector(
|
|
'.ntsh_dashboard-camerarunner-error'
|
|
);
|
|
errorText: HTMLDivElement = this.container.querySelector(
|
|
'.ntsh_dashboard-camerarunner-errortext'
|
|
);
|
|
|
|
constructor(Main: Main) {
|
|
this._Main = Main;
|
|
|
|
this.registerListeners();
|
|
}
|
|
|
|
updateState(state: CameraRunnerStatus) {
|
|
// ----------- Connection -----------
|
|
setStatusState(
|
|
this.connectionStatus,
|
|
{
|
|
CONNECTING: 'yellow',
|
|
CONNECTED: 'green',
|
|
DISCONNECTED: 'red',
|
|
FAILED: 'red',
|
|
}[state.state] as StatusType
|
|
);
|
|
this.connectionInfo.innerText = state.message ?? '';
|
|
|
|
// ----------- Process -----------
|
|
if (state.state != 'CONNECTED') {
|
|
state.processStatus.state = 'STOPPED';
|
|
state.processStatus.message = 'Not connected';
|
|
state.processStatus.startTime = -1;
|
|
|
|
this.restartButton.style.display = 'none';
|
|
this.rebootButton.style.display = 'none';
|
|
} else {
|
|
this.rebootButton.style.display = 'flex';
|
|
|
|
if (state.processStatus.state == 'RUNNING')
|
|
this.restartButton.style.display = 'flex';
|
|
else this.restartButton.style.display = 'none';
|
|
}
|
|
|
|
setStatusState(
|
|
this.processStatus,
|
|
{
|
|
RUNNING: 'green',
|
|
STOPPED: 'gray',
|
|
STARTING: 'yellow',
|
|
PROBLEM: 'red',
|
|
}[state.processStatus.state] as StatusType
|
|
);
|
|
this.processInfo.innerText = state.processStatus.message ?? '';
|
|
|
|
// ----------- Uptime -----------
|
|
const uptimeSeconds =
|
|
state.processStatus.startTime == -1
|
|
? -1
|
|
: (Date.now() - state.processStatus.startTime) / 1000;
|
|
this.uptimeInfo.innerText = formatUptime(uptimeSeconds);
|
|
|
|
// ----------- Error -----------
|
|
const errors: string[] = [];
|
|
if ((state?.error ?? '').trim().length > 0) errors.push(state.error);
|
|
if ((state?.processStatus?.error ?? '').trim().length > 0)
|
|
errors.push(state.processStatus.error);
|
|
if (errors.length > 0) {
|
|
this.errorText.innerText = errors.join('\n');
|
|
this.errorContainer.style.display = 'block';
|
|
} else {
|
|
this.errorContainer.style.display = 'none';
|
|
this.errorText.innerText = '';
|
|
}
|
|
|
|
this._Main.Logs.setCameraLogs(state.processStatus.output.current);
|
|
}
|
|
|
|
registerListeners() {
|
|
this._Main.socket.on(
|
|
'cameraRunnerState',
|
|
(state: CameraRunnerStatus) => {
|
|
this.updateState(state);
|
|
}
|
|
);
|
|
|
|
this.restartButton.addEventListener('click', async () => {
|
|
this.executeCommand(
|
|
'restart',
|
|
'Are you sure you want to restart the Camera Runner process?'
|
|
);
|
|
});
|
|
|
|
this.rebootButton.addEventListener('click', async () => {
|
|
this.executeCommand(
|
|
'reboot',
|
|
'Are you sure you want to reboot the Camera Runner machine?'
|
|
);
|
|
});
|
|
}
|
|
|
|
private async executeCommand(command: string, message: string) {
|
|
const confirmed = await MorphFeature.Confirm({
|
|
title: 'Are you sure?',
|
|
message,
|
|
});
|
|
if (!confirmed) return;
|
|
|
|
MorphFeature.Loader({
|
|
active: true,
|
|
message: `Requesting Camera Runner ${command}...`,
|
|
});
|
|
this._Main.socket.emit(
|
|
'cameraRunner',
|
|
command,
|
|
(response: { succeed: boolean; message?: string }) => {
|
|
MorphFeature.Loader({ active: false });
|
|
|
|
if (!response.succeed)
|
|
return MorphFeature.Alert({
|
|
title: 'Error',
|
|
message: response.message,
|
|
});
|
|
|
|
MorphFeature.Notification({
|
|
level: 'success',
|
|
message: `Camera Runner is ${command}ing...`,
|
|
});
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
interface CameraRunnerStatus {
|
|
state: ServiceState;
|
|
message?: string;
|
|
error?: string;
|
|
|
|
processStatus: ProcessStatus;
|
|
}
|
|
|
|
export type ProcessStatusState = 'RUNNING' | 'STOPPED' | 'STARTING' | 'PROBLEM';
|
|
|
|
interface ProcessStatusSimple {
|
|
state: ProcessStatusState;
|
|
message?: string;
|
|
error?: string;
|
|
}
|
|
interface ProcessStatus extends ProcessStatusSimple {
|
|
startTime: number;
|
|
output: { current: string[]; last: string[] };
|
|
}
|