hyper-tuner/hyper-tuner-cloud

View on GitHub
src/utils/logs/TriggerLogsParser.ts

Summary

Maintainability
C
7 hrs
Test Coverage
import { ParserInterface } from '../ParserInterface';
import { isNumber } from '../tune/expression';

export enum EntryType {
  TRIGGER = 'trigger',
  MARKER = 'marker',
}

export interface CompositeLogEntry {
  type: EntryType;
  primaryLevel: number;
  secondaryLevel: number;
  trigger: number;
  sync: number;
  refTime: number;
  maxTime: number;
  toothTime: number;
  time: number;
}

export interface ToothLogEntry {
  type: EntryType;
  toothTime: number;
  time: number;
}

class TriggerLogsParser implements ParserInterface {
  private commentPrefix = '#';

  private markerPrefix = 'MARK';

  private isToothLogs = false;

  private isCompositeLogs = false;

  private resultComposite: CompositeLogEntry[] = [];

  private resultTooth: ToothLogEntry[] = [];

  private alreadyParsed = false;

  private raw = '';

  public constructor(buffer: ArrayBuffer) {
    this.raw = new TextDecoder().decode(buffer);
  }

  public parse(): this {
    this.detectType();

    if (this.isToothLogs) {
      this.parseToothLogs(this.raw);
    }

    if (this.isCompositeLogs) {
      this.parseCompositeLogs(this.raw);
    }

    this.alreadyParsed = true;

    return this;
  }

  public getCompositeLogs(): CompositeLogEntry[] {
    return this.resultComposite;
  }

  public getToothLogs(): ToothLogEntry[] {
    return this.resultTooth;
  }

  public isTooth(): boolean {
    if (!this.alreadyParsed) {
      this.parse();
    }

    return this.isToothLogs;
  }

  public isComposite(): boolean {
    if (!this.alreadyParsed) {
      this.parse();
    }

    return this.isCompositeLogs;
  }

  private detectType(): void {
    this.raw.split('\n').forEach((line, index) => {
      const trimmed = line.trim();

      if (trimmed.startsWith(this.commentPrefix)) {
        return;
      }

      // give up
      if (index > 10) {
        return true;
      }

      const parts = trimmed.split(',');

      if (parts.length === 2) {
        this.isToothLogs = true;

        return true;
      }

      if (parts.length >= 6) {
        this.isCompositeLogs = true;

        return true;
      }

      return false;
    });
  }

  private parseToothLogs(raw: string): void {
    this.resultTooth = [];

    raw.split('\n').forEach((line) => {
      const trimmed = line.trim();

      if (trimmed.startsWith(this.commentPrefix)) {
        return;
      }

      if (trimmed.startsWith(this.markerPrefix)) {
        const previous = this.resultTooth[this.resultTooth.length - 1] || {
          toothTime: 0,
          time: 0,
        };

        this.resultTooth.push({
          type: EntryType.MARKER,
          toothTime: previous.toothTime,
          time: previous.time,
        });

        return;
      }

      const parts = trimmed.split(',');
      if (parts.length !== 2) {
        return;
      }

      if (!(isNumber(parts[0]) && isNumber(parts[1]))) {
        return;
      }

      this.resultTooth.push({
        type: EntryType.TRIGGER,
        toothTime: Number(parts[0]),
        time: Number(parts[1]),
      });
    });
  }

  private parseCompositeLogs(raw: string): void {
    this.resultComposite = [];

    raw.split('\n').forEach((line) => {
      const trimmed = line.trim();

      if (trimmed.startsWith(this.commentPrefix)) {
        return;
      }

      if (trimmed.startsWith(this.markerPrefix)) {
        const previous = this.resultComposite[this.resultComposite.length - 1] || {
          primaryLevel: 0,
          secondaryLevel: 0,
          trigger: 0,
          sync: 0,
          refTime: 0,
          maxTime: 0,
          toothTime: 0,
          time: 0,
        };

        this.resultComposite.push({
          type: EntryType.MARKER,
          primaryLevel: previous.primaryLevel,
          secondaryLevel: previous.secondaryLevel,
          trigger: previous.trigger,
          sync: previous.sync,
          refTime: previous.refTime,
          maxTime: previous.maxTime,
          toothTime: previous.toothTime,
          time: previous.time,
        });

        return;
      }

      const parts = trimmed.split(',');

      if (!(isNumber(parts[0]) && isNumber(parts[1]))) {
        return;
      }

      const base = {
        type: EntryType.TRIGGER,
        primaryLevel: Number(parts[0]),
        secondaryLevel: Number(parts[1]),
        trigger: Number(parts[2]),
        sync: Number(parts[3]),
        refTime: Number(parts[4]),
      };

      if (parts.length === 8) {
        // PriLevel,SecLevel,Trigger,Sync,RefTime,MaxTime,ToothTime,Time
        this.resultComposite.push({
          ...base,
          maxTime: Number(parts[5]),
          toothTime: Number(parts[6]),
          time: Number(parts[7]),
        });

        return;
      }

      if (parts.length === 7) {
        // PriLevel,SecLevel,Trigger,Sync,RefTime,ToothTime,Time
        this.resultComposite.push({
          ...base,
          maxTime: 0,
          toothTime: Number(parts[5]),
          time: Number(parts[6]),
        });
      }

      if (parts.length === 6) {
        // PriLevel,SecLevel,Trigger,Sync,ToothTime,Time
        this.resultComposite.push({
          ...base,
          maxTime: 0,
          toothTime: Number(parts[4]),
          time: Number(parts[5]),
        });
      }
    });
  }
}

export default TriggerLogsParser;