All files / sportident-testbench-shell/src/commands simulate.ts

4.93% Statements 4/81
0% Branches 0/15
0% Functions 0/13
5% Lines 4/80

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 1351x           1x   1x   1x                                                                                                                                                                                                                                                        
import si from 'sportident/lib';
import * as siProtocol from 'sportident/lib/siProtocol';
import {FakeSiMainStationMessageEvent} from 'sportident/lib/fakes/IFakeSiMainStation';
import * as utils from 'sportident/lib/utils';
import {ShellCommandContext} from '../Shell';
import {SiExternalApplicationReceiveEvent} from '../ISiExternalApplication';
import {BaseCommand, ArgType} from './BaseCommand';
 
const isSubclassOf = (subclass: unknown, superclass: unknown) => typeof subclass === 'function' && typeof superclass === 'function' && subclass.prototype instanceof superclass;
 
export class SimulateCommand extends BaseCommand {
    getArgTypes(): ArgType[] {
        const testCases = si.getSiStationExamples();
        return [
            {
                name: 'testCase',
                choices: Object.keys(testCases),
            },
            {
                name: 'path',
                regex: /^\S+$/,
                description: 'The absolute path of a pipe (on the machine where testbench-server is running)',
            },
        ];
    }
 
    printUsage(context: ShellCommandContext): void {
        super.printUsage(context);
        context.putString('e.g. simulate BSM8Station /tmp/vwin_com1\n');
    }
 
    run(context: ShellCommandContext): Promise<void> {
        const what = context.args[1];
 
        const testCases = si.getSiStationExamples();
        const testCase = testCases[what];
        Iif (!testCase) {
            const availableTestCases = Object.keys(testCases).join(', ');
            context.putString(`No such SiMainStation data: ${what}\n`);
            context.putString(`Available data: ${availableTestCases}\n`);
            return Promise.resolve();
        }
 
        const url = context.args[2];
        const SiExternalApplication = context.env.externalApplication;
        return new Promise((resolve, reject) => {
            Iif (!SiExternalApplication) {
                reject(new Error('No SiExternalApplication.'));
                return;
            }
            const externalApplication = new SiExternalApplication(url);
            const fakeSiMainStation = new si.FakeSiMainStation(testCase.storageData);
 
            let applicationToSimulatorBuffer: number[] = [];
 
            const onSimulatorMessage: utils.EventCallback<FakeSiMainStationMessageEvent> =
                (e) => {
                    const message = e.message;
                    console.log('FakeSiMainStation:', message);
                    const uint8Data = si.protocol.render(message);
                    externalApplication.send(uint8Data);
                };
            fakeSiMainStation.addEventListener('message', onSimulatorMessage);
            const onApplicationReceive: utils.EventCallback<SiExternalApplicationReceiveEvent> =
                (e) => {
                    const uint8Data = e.uint8Data;
                    applicationToSimulatorBuffer.push(...uint8Data);
                    const {messages, remainder} = si.protocol.parseAll(applicationToSimulatorBuffer);
                    messages.forEach((message: siProtocol.SiMessage) => {
                        console.log('SiExternalApplication:', si.protocol.prettyMessage(message));
                        fakeSiMainStation.sendMessage(message);
                    });
                    applicationToSimulatorBuffer = remainder;
                };
            externalApplication.addEventListener('receive', onApplicationReceive);
 
 
            const onSubCommand = (line: string) => {
                const subResIn = /in\s+([^\s]+)\s+([^\s]+)/.exec(line);
                const subResOut = /out/.exec(line);
                if (subResIn) {
                    const simulatorName = subResIn[1];
                    // @ts-ignore
                    const simulatorType = si[`Fake${simulatorName}`];
                    Iif (!simulatorType || !isSubclassOf(simulatorType, si.BaseFakeSiCard)) {
                        const availableSimulatorNames = Object.keys(si)
                            .filter((key) => /^Fake/.exec(key))
                            // @ts-ignore
                            .filter((key) => isSubclassOf(si[key], si.BaseFakeSiCard))
                            .join(', ');
                        context.putString(`No such FakeSiCard: ${simulatorName}\n`);
                        context.putString(`Available simulator names: ${availableSimulatorNames}\n`);
                        return;
                    }
                    const simulatorDataName = subResIn[2];
                    const availableSimulatorData = simulatorType.getAllExamples();
                    // @ts-ignore
                    const simulatorData = availableSimulatorData[simulatorDataName];
                    Iif (!simulatorData) {
                        const availableSimulatorDataNames = Object.keys(availableSimulatorData);
                        context.putString(`No such FakeSiCard data: ${simulatorDataName}\n`);
                        context.putString(`Available simulator data: ${availableSimulatorDataNames}\n`);
                    }
                    const simulator = new simulatorType(simulatorData.storageData);
                    context.putString('Insert Card\n');
                    fakeSiMainStation.insertCard(simulator);
                } else if (subResOut) {
                    context.putString('out\n');
                } else {
                    context.putString('Possible commands:\n');
                    context.putString('in [cardType] [sampleName]\n');
                    context.putString('out\n');
                    context.putString('e.g. in SiCard5 cardWith16Punches\n');
                }
            };
 
            const mainLoop = () => {
                context.getLine().then((line: string) => {
                    if (line === 'exit') { // Escape || Ctrl-C
                        externalApplication.close();
                        fakeSiMainStation.removeEventListener('message', onSimulatorMessage);
                        externalApplication.removeEventListener('receive', onApplicationReceive);
                        context.putString('Simulation finished.\n');
                        resolve();
                    } else {
                        onSubCommand(line);
                        mainLoop();
                    }
                });
            };
            mainLoop();
        });
    }
}