All files / sportident/src/SiCard BaseSiCard.ts

100% Statements 31/31
100% Branches 2/2
100% Functions 16/16
100% Lines 31/31

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  17x 17x       17x           17x                   17x   17x 17x     13x               47x 47x       63x           3x 3x 1x   2x       7x 64x 64x 7x                 60x     60x       68x       4x 1x       2x       4x           2x 1x   1x           2x       2x      
import _ from 'lodash';
import {proto} from '../constants';
import * as utils from '../utils';
import * as siProtocol from '../siProtocol';
import * as storage from '../storage';
import {IRaceResultData} from './IRaceResultData';
import {makeStartZeroTime, monotonizeRaceResult, prettyRaceResult} from './raceResultTools';
 
export type SiCardType<T extends BaseSiCard> = {
    new(cardNumber: number): T,
    typeSpecificInstanceFromMessage: (message: siProtocol.SiMessage) => T|undefined,
};
const initialRegistry: utils.NumberRangeRegistry<SiCardType<BaseSiCard>> = new utils.NumberRangeRegistry();
 
export interface ISiMainStation {
    sendMessage: (
        message: siProtocol.SiMessage,
        numResponses?: number,
        timeoutInMiliseconds?: number,
    ) => Promise<number[][]>;
}
 
export abstract class BaseSiCard {
    // abstract static maxNumPunches: number;
    static NumberRange: typeof utils.NumberRange = utils.NumberRange;
    static cardNumberRangeRegistry: utils.NumberRangeRegistry<SiCardType<BaseSiCard>> = initialRegistry;
 
    static resetNumberRangeRegistry(): void {
        this.cardNumberRangeRegistry = new utils.NumberRangeRegistry();
    }
 
    static registerNumberRange<T extends BaseSiCard>(
        firstCardNumberInRange: number,
        firstCardNumberAfterRange: number,
        siCardType: SiCardType<T>,
    ): void {
        const cardNumberRange = new utils.NumberRange(firstCardNumberInRange, firstCardNumberAfterRange);
        this.cardNumberRangeRegistry.register(cardNumberRange, siCardType);
    }
 
    static getTypeByCardNumber<T extends BaseSiCard>(cardNumber: number): SiCardType<T>|undefined {
        return this.cardNumberRangeRegistry.getValueForNumber(cardNumber) as SiCardType<T>|undefined;
    }
 
    // abstract static getPunchOffset(index: number): number;
 
    static fromCardNumber(cardNumber: number): BaseSiCard|undefined {
        const cardType = this.getTypeByCardNumber(cardNumber);
        if (!cardType) {
            return undefined;
        }
        return new cardType(cardNumber);
    }
 
    static detectFromMessage(message: siProtocol.SiMessage): BaseSiCard|undefined {
        const possibleCards = this.cardNumberRangeRegistry.values
            .map((cardType) => cardType.typeSpecificInstanceFromMessage(message))
            .filter((cardInstance) => cardInstance !== undefined);
        return possibleCards.get(0);
    }
 
    // abstract static typeSpecificInstanceFromMessage<Fields extends IBaseSiCardStorageFields>(
    //     _message: siProtocol.SiMessage,
    // ): BaseSiCard<Fields>|undefined;
 
    public mainStation?: ISiMainStation|undefined;
    public raceResult: IRaceResultData&{cardNumber: number};
    public storage: storage.ISiStorage<unknown> = {} as storage.ISiStorage<unknown>;
 
    constructor(cardNumber: number) {
        this.raceResult = {cardNumber: cardNumber};
    }
 
    get cardNumber(): number {
        return this.raceResult.cardNumber;
    }
 
    read(): Promise<BaseSiCard> {
        return this.typeSpecificRead()
            .then(() => this);
    }
 
    getNormalizedRaceResult(): IRaceResultData {
        return makeStartZeroTime(this.getMonotonizedRaceResult());
    }
 
    getMonotonizedRaceResult(): IRaceResultData {
        return monotonizeRaceResult(this.raceResult);
    }
 
    abstract typeSpecificRead(): Promise<void>;
 
    confirm(): Promise<number[][]> {
        if (!this.mainStation) {
            return Promise.reject(new Error('No main station'));
        }
        return this.mainStation.sendMessage({
            mode: proto.ACK,
        }, 0);
    }
 
    toDict(): IRaceResultData {
        return this.raceResult;
    }
 
    toString(): string {
        return `${this.constructor.name}\n${prettyRaceResult(this.raceResult)}`;
    }
}