From 117ab6615ddbb42c8244552ab898d3856803d5d8 Mon Sep 17 00:00:00 2001 From: Piotr Rogowski Date: Sun, 19 Dec 2021 21:37:31 +0100 Subject: [PATCH] Base work for tooth logs (#325) --- package-lock.json | 32 +++- package.json | 4 +- public/logs/trigger/composite_1.csv | 3 +- public/logs/trigger/composite_1.csv.gz | Bin 3085 -> 3088 bytes public/logs/trigger/{3.csv => tooth_3.csv} | 0 public/logs/trigger/tooth_3.csv.gz | Bin 0 -> 1891 bytes src/components/CanvasHelp.tsx | 29 ++++ src/components/Diagnose.tsx | 122 ++++--------- src/components/Dialog/Curve.tsx | 11 +- src/components/Log/LogCanvas.tsx | 39 +---- src/components/TriggerLog/CompositeCanvas.tsx | 70 ++------ src/components/TriggerLog/ToothCanvas.tsx | 102 +++++++++++ src/utils/api.ts | 9 +- src/utils/colors.ts | 17 ++ src/utils/logs/TriggerLogsParser.ts | 160 ++++++++++++++++++ 15 files changed, 407 insertions(+), 191 deletions(-) rename public/logs/trigger/{3.csv => tooth_3.csv} (100%) create mode 100644 public/logs/trigger/tooth_3.csv.gz create mode 100644 src/components/CanvasHelp.tsx create mode 100644 src/components/TriggerLog/ToothCanvas.tsx create mode 100644 src/utils/colors.ts create mode 100644 src/utils/logs/TriggerLogsParser.ts diff --git a/package-lock.json b/package-lock.json index c752bda..2081631 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,9 @@ "react-scripts": "^4.0.3", "react-table-drag-select": "^0.3.1", "recharts": "^2.1.8", - "timechart": "^1.0.0-beta.4" + "timechart": "^1.0.0-beta.4", + "uplot": "^1.6.18", + "uplot-react": "^1.1.1" }, "devDependencies": { "@craco/craco": "^6.4.3", @@ -21479,6 +21481,23 @@ "yarn": "*" } }, + "node_modules/uplot": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/uplot/-/uplot-1.6.18.tgz", + "integrity": "sha512-x7+bFfIZ8rMjOmDGhUlJCkYWiZX617xQWNfT94JUhidliRtzMHKIX0xUiN92TZ/7il6xMf9oLwbhsz7nbqF1YQ==" + }, + "node_modules/uplot-react": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/uplot-react/-/uplot-react-1.1.1.tgz", + "integrity": "sha512-zCvwyZVm4nfYDi+KjaK0FppqftGzga/x+u0h2baRWj1vXMB9/hfJ1qb9gXAdXMfp17C9Rk57HoZDE9MewNWLfg==", + "engines": { + "node": ">=8.10" + }, + "peerDependencies": { + "react": ">=16.8.6", + "uplot": "^1.6.7" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -40124,6 +40143,17 @@ "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" }, + "uplot": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/uplot/-/uplot-1.6.18.tgz", + "integrity": "sha512-x7+bFfIZ8rMjOmDGhUlJCkYWiZX617xQWNfT94JUhidliRtzMHKIX0xUiN92TZ/7il6xMf9oLwbhsz7nbqF1YQ==" + }, + "uplot-react": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/uplot-react/-/uplot-react-1.1.1.tgz", + "integrity": "sha512-zCvwyZVm4nfYDi+KjaK0FppqftGzga/x+u0h2baRWj1vXMB9/hfJ1qb9gXAdXMfp17C9Rk57HoZDE9MewNWLfg==", + "requires": {} + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/package.json b/package.json index fdc3bbb..7dd85fc 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,9 @@ "react-scripts": "^4.0.3", "react-table-drag-select": "^0.3.1", "recharts": "^2.1.8", - "timechart": "^1.0.0-beta.4" + "timechart": "^1.0.0-beta.4", + "uplot": "^1.6.18", + "uplot-react": "^1.1.1" }, "devDependencies": { "@craco/craco": "^6.4.3", diff --git a/public/logs/trigger/composite_1.csv b/public/logs/trigger/composite_1.csv index db1536c..d8d6352 100644 --- a/public/logs/trigger/composite_1.csv +++ b/public/logs/trigger/composite_1.csv @@ -123,6 +123,7 @@ Flag,Flag,Flag,Flag,ms,ms,ms,ms 0.0,0.0,0.0,1.0,564200.94,564200.94,35.864,564200.94 0.0,1.0,1.0,1.0,564250.3,564250.3,49.376,564250.3 1.0,1.0,0.0,1.0,564258.0,564258.0,7.728,564258.0 +MARK 000 1.0,0.0,1.0,1.0,564286.5,564286.5,28.484,564286.5 0.0,0.0,0.0,1.0,564294.0,564294.0,7.468,564294.0 1.0,0.0,0.0,1.0,564351.06,564351.06,57.056,564351.06 @@ -382,4 +383,4 @@ Flag,Flag,Flag,Flag,ms,ms,ms,ms 1.0,0.0,0.0,1.0,568452.75,568452.75,57.408,568452.75 0.0,0.0,0.0,1.0,568488.8,568488.8,36.024,568488.8 0.0,1.0,1.0,1.0,568538.44,568538.44,49.624,568538.44 -MARK 000 +MARK 001 diff --git a/public/logs/trigger/composite_1.csv.gz b/public/logs/trigger/composite_1.csv.gz index bcf8144d7e996f5afb5ef17e052ec05eda4dbb7a..bc6f516f8444885f4395c97e2d169d1beb4d8e5f 100644 GIT binary patch delta 3052 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 u#^pu>A5~HBNdN%()yJFw delta 3031 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 ZA@K6`AHMnH7b&He{{k{q;RIz#007}`t!w}Q diff --git a/public/logs/trigger/3.csv b/public/logs/trigger/tooth_3.csv similarity index 100% rename from public/logs/trigger/3.csv rename to public/logs/trigger/tooth_3.csv diff --git a/public/logs/trigger/tooth_3.csv.gz b/public/logs/trigger/tooth_3.csv.gz new file mode 100644 index 0000000000000000000000000000000000000000..8bfdde37c07f2519841c64c85a2391d5883a2c9e GIT binary patch literal 1891 zcmV-p2b}mHiwFoRTEAfe19We1bZB2QE@N|c08LlTjwUq>y!TTeaUHD^Cvof}XT*iw zS3rUT+6%ltRb}_E(rEO>`LR2e}4@>-d=8U^!*gNp|q*-i^&mNe0?mfpi_8DvJ$Jgp{Om~b=PVK+@ zp4iV>yUvR-hjD)18oS$w=hz*CwWb4v)r&g5*s*n6J7|% zYnHLXQYn<06{Cmfrdm*!r`_g@Lj0~&?3xrpW zQ~5?7c^+~FSG-TwdVtOQUf|4l(l%0N574%uai)PEcZT9cbaP_=M3NOi?u!%-5=<9J zgiFG0KvCl~+!X;v1pubu)CZ_4!FhosF96SScyIE&Gk47+e6Ijq^9ed4po9Oq02~79 z=OQl%5Ii&Fj06^8$-8+R`&ZWdJirU*gX+dH=wcug;3c6j4%@0IQiuj@6eGtup%uUm z93n(@;yFMA9)=*>!)-D}h%0K0qQi{=Y#n%Qi((#D<1cUpjmM0UJ!|o+vq%zyWYWV^k;*2GisZ7c*T50SKaLk>og}VrT(sA3+k* zIspDzqh~J#fXtxS3E8a!G3nHVeQN$>k|Tsv^L`C%{RCuQ!xR8BFaHn(X$ElD1p>b= z_s7(ENhCDCv>u$eO1YCV8w!F@5dT@M;cau;Wy-ApFu)b$^cgzOY*D2!FOA%`kT1jo zUetO>7AbmR*BGUbWb=H*3x1p+K34dk-f{fkeh~r{ubPCUh%n|!&e6Z{fR+tv8vMJ) z5V0U(m;jA$P&1$aDSx?D)9^2R7fTQ8Ap{}}l127&zv5XF=#ns2gku_lggCS2aZM3s zN`sKqRfIZ6_)C>||Iz>`-hqti{2HS+aUuwrJq~29_G|2g7g?h7C>bny?32IiewhDx zRDu-QDSW;B2UJG>*U|l;8B_Qj?Ptxhr@Vwm1sEmgl%{SFG~^%5XFMQ4s;8YP{vZLH z1qS@YUtEygg8qd+9Z-`mrCVbpO!!oRZdTSI{#N^akf8K0en8+u^MG6Pth}rIi=X=j znMD8PFAf9Z`KJ4zdH~(q_vibAE5hRo_I$x2;0w{@Xdj4_>jsFsEqPSKD5kcc&!hwX*rDqo0L^@!Z}$*5Gy8t-Lw_sbR1 zh}AKZt)&9lC&Ne8vAu|0J`gIWjXZ>;cxb2demDD}6Q4JpV|W+x929h0peL1!+q)nE z(p>Pc*Py8=W;~5L9y}db=iu1xH9_Y(rx|sVrAt2hr=`^P@ZBa09zdH<4AfVn5orEH z3Y?u~=r!Ig;&b0L(48kahVI(uKo$5DQTHngI#OTD0AO{r=nMvWth1-h7uC31sKVENEt$#s!dtH`0UW4YD2MhADXmoD_EQ`HKI2Ri1LQ*!a>64*Sd^;d! zZ_BAK6rg>37g$@pU|V~q)<`$vT?4UPd^16``kV_;>+bKp0urryKdWO#nx%(W(=p@b zItSFKUD$oqJ$)6+l ( +
+ + Navigation + Pinch to zoom + Drag to pan + Ctrl + wheel scroll to zoom X axis + Hold Shift to speed up zoom 5 times + + } + > + + +
+); + +export default CanvasHelp; diff --git a/src/components/Diagnose.tsx b/src/components/Diagnose.tsx index 8c0382a..d96d7ca 100644 --- a/src/components/Diagnose.tsx +++ b/src/components/Diagnose.tsx @@ -13,6 +13,7 @@ import { Steps, Space, Divider, + Typography, } from 'antd'; import { FileTextOutlined, @@ -28,15 +29,23 @@ import { Config, Logs, } from '@speedy-tuner/types'; -import { loadCompositeLogs } from '../utils/api'; +import { + loadCompositeLogs, + loadToothLogs, +} from '../utils/api'; import store from '../store'; import { formatBytes } from '../utils/number'; import CompositeCanvas from './TriggerLog/CompositeCanvas'; -import { isNumber } from '../utils/tune/expression'; +import TriggerLogsParser, { + CompositeLogEntry, + ToothLogEntry, +} from '../utils/logs/TriggerLogsParser'; +import ToothCanvas from './TriggerLog/ToothCanvas'; const { TabPane } = Tabs; const { Content } = Layout; const { Step } = Steps; + const edgeUnknown = 'Unknown'; const mapStateToProps = (state: AppState) => ({ @@ -46,19 +55,6 @@ const mapStateToProps = (state: AppState) => ({ loadedLogs: state.logs, }); -// TODO: extract this to types package -interface CompositeLogEntry { - type: 'trigger' | 'marker'; - primaryLevel: number; - secondaryLevel: number; - trigger: number; - sync: number; - refTime: number; - maxTime: number; - toothTime: number; - time: number; -} - const Diagnose = ({ ui, config, loadedLogs }: { ui: UIState, config: Config, loadedLogs: Logs }) => { const { lg } = useBreakpoint(); const { Sider } = Layout; @@ -83,6 +79,7 @@ const Diagnose = ({ ui, config, loadedLogs }: { ui: UIState, config: Config, loa }, }; const [logs, setLogs] = useState(); + const [toothLogs, setToothLogs] = useState(); useEffect(() => { const controller = new AbortController(); @@ -90,79 +87,24 @@ const Diagnose = ({ ui, config, loadedLogs }: { ui: UIState, config: Config, loa const loadData = async () => { try { - const raw = await loadCompositeLogs((percent, total, edge) => { + const compositeRaw = await loadCompositeLogs((percent, total, edge) => { setProgress(percent); setFileSize(formatBytes(total)); setEdgeLocation(edge || edgeUnknown); }, signal); - setFileSize(formatBytes(raw.byteLength)); - - const buff = pako.inflate(new Uint8Array(raw)); - const string = (new TextDecoder()).decode(buff); - const result: CompositeLogEntry[] = []; + const toothRaw = await loadToothLogs(undefined, signal); + setFileSize(formatBytes(compositeRaw.byteLength)); setStep(1); - // TODO: extract this, make a parser class - string.split('\n').forEach((line, index) => { - const trimmed = line.trim(); + const parser = new TriggerLogsParser(); + const resultComposite = parser.parse(pako.inflate(new Uint8Array(compositeRaw))).getCompositeLogs(); + const resultTooth = parser.parse(pako.inflate(new Uint8Array(toothRaw))).getToothLogs(); - // skip comments - if (trimmed.startsWith('#')) { - return; - } + setLogs(resultComposite); + setToothLogs(resultTooth); - // markers - if (trimmed.startsWith('MARK')) { - const previous = result[result.length - 1] || { - primaryLevel: 0, - secondaryLevel: 0, - trigger: 0, - sync: 0, - refTime: 0, - maxTime: 0, - toothTime: 0, - time: 0, - }; - - result.push({ - type: '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, - }); - } - - const split = trimmed.split(','); - if (!isNumber(split[0])) { - return; - } - - const time = Number(split[7]); - if (!time) { - return; - } - - result.push({ - type: 'trigger', - primaryLevel: Number(split[0]), - secondaryLevel: Number(split[1]), - trigger: Number(split[2]), - sync: Number(split[3]), - refTime: Number(split[4]), - maxTime: Number(split[5]), - toothTime: Number(split[6]), - time, - }); - }); - - setLogs(result); setStep(2); } catch (error) { setFetchError(error as Error); @@ -191,7 +133,8 @@ const Diagnose = ({ ui, config, loadedLogs }: { ui: UIState, config: Config, loa } key="files"> - composite.csv + tooth.csv + composite.csv @@ -200,13 +143,22 @@ const Diagnose = ({ ui, config, loadedLogs }: { ui: UIState, config: Config, loa
- {logs + {toothLogs && logs ? - + ( + <> + + + + ) : @@ -93,14 +92,14 @@ const Curve = ({ value={`${yLabel} (${yUnits})`} position="left" angle={-90} - style={{ fill: mainColor }} + style={{ fill: Colors.TEXT }} /> `${xLabel} : ${value} ${xUnits}`} formatter={(value: number) => [`${value} ${yUnits}`, yLabel]} contentStyle={{ - backgroundColor: tooltipBg, + backgroundColor: Colors.MAIN, border: 0, boxShadow: '0 3px 6px -4px rgb(0 0 0 / 12%), 0 6px 16px 0 rgb(0 0 0 / 8%), 0 9px 28px 8px rgb(0 0 0 / 5%)', borderRadius: 5, @@ -111,7 +110,7 @@ const Curve = ({ strokeWidth={3} type="linear" dataKey="y" - stroke="#1e88ea" + stroke={Colors.ACCENT} animationDuration={animationDuration} /> diff --git a/src/components/Log/LogCanvas.tsx b/src/components/Log/LogCanvas.tsx index 1c06c2c..f591785 100644 --- a/src/components/Log/LogCanvas.tsx +++ b/src/components/Log/LogCanvas.tsx @@ -5,31 +5,13 @@ import { useRef, } from 'react'; import { Logs } from '@speedy-tuner/types'; -import { - Popover, - Space, - Typography, - Grid, -} from 'antd'; -import { QuestionCircleOutlined } from '@ant-design/icons'; +import { Grid } from 'antd'; import TimeChart from 'timechart'; import { EventsPlugin } from 'timechart/dist/lib/plugins_extra/events'; import { colorHsl } from '../../utils/number'; import LandscapeNotice from '../Dialog/LandscapeNotice'; +import CanvasHelp from '../CanvasHelp'; -// enum Colors { -// RED = '#f32450', -// CYAN = '#8dd3c7', -// YELLOW = '#ffff00', -// PURPLE = '#bebada', -// GREEN = '#77de3c', -// BLUE = '#2fe3ff', -// GREY = '#334455', -// WHITE = '#fff', -// BG = '#222629', -// } - -const { Text } = Typography; const { useBreakpoint } = Grid; export interface SelectedField { @@ -155,22 +137,7 @@ const LogCanvas = ({ data, width, height, selectedFields }: Props) => { return ( <> -
- - Navigation - Pinch to zoom - Drag to pan - Ctrl + wheel scroll to zoom X axis - Hold Shift to speed up zoom 5 times - - } - > - - -
+
{ const sync: DataPoint[] = []; data.forEach((entry, index) => { - if (entry.type === 'marker') { + if (entry.type === EntryType.MARKER) { markers.push({ x: index, name: '', }); } - if (entry.type === 'trigger') { + if (entry.type === EntryType.TRIGGER) { const prevSecondary = data[index - 1] ? data[index - 1].secondaryLevel : 0; const currentSecondary = (entry.secondaryLevel + 3) * 2; // apply scale @@ -166,22 +131,7 @@ const CompositeCanvas = ({ data, width, height }: Props) => { return ( <> -
- - Navigation - Pinch to zoom - Drag to pan - Ctrl + wheel scroll to zoom X axis - Hold Shift to speed up zoom 5 times - - } - > - - -
+
{ + const { sm } = useBreakpoint(); + const [options, setOptions] = useState(); + const [plotData, setPlotData] = useState(); + + useEffect(() => { + const xData: number[] = []; + const yData: (number | null)[] = []; + + data.forEach((entry: ToothLogEntry, index) => { + if (entry.type === EntryType.TRIGGER) { + yData.push(entry.toothTime); + xData.push(index); + } + }); + + setPlotData([ + xData, + yData, + ]); + + setOptions({ + title: 'Tooth logs', + width, + height, + scales: { + x: { time: false }, + }, + series: [ + { + label: 'Event', + }, + { + label: 'Tooth time', + points: { show: false }, + stroke: Colors.ACCENT, + fill: Colors.ACCENT, + scale: 'toothTime', + value: (self, rawValue) => `${rawValue.toLocaleString()}μs`, + paths: bars!({ size: [0.6, 100] }), + }, + ], + axes: [ + { + stroke: Colors.TEXT, + grid: { stroke: Colors.MAIN_LIGHT }, + }, + { + scale: 'toothTime', + label: '', + stroke: Colors.TEXT, + grid: { stroke: Colors.MAIN_LIGHT }, + }, + ], + cursor: { + drag: { y: false }, + }, + }); + }, [data, width, height, sm]); + + if (!sm) { + return ; + } + + return ( + <> + + + + ); +}; + +export default ToothCanvas; diff --git a/src/utils/api.ts b/src/utils/api.ts index cffd865..eab8bc4 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -90,8 +90,15 @@ export const loadLogs = (onProgress?: onProgressType, signal?: AbortSignal) => export const loadCompositeLogs = (onProgress?: onProgressType, signal?: AbortSignal) => fetchWithProgress( - 'https://d29mjpbgm6k6md.cloudfront.net/trigger-logs/composite_1.csv.gz', + 'https://d29mjpbgm6k6md.cloudfront.net/trigger-logs/composite_1_2.csv.gz', // 'https://d29mjpbgm6k6md.cloudfront.net/trigger-logs/2.csv.gz', onProgress, signal, ).then((response) => response); + +export const loadToothLogs = (onProgress?: onProgressType, signal?: AbortSignal) => + fetchWithProgress( + 'https://d29mjpbgm6k6md.cloudfront.net/trigger-logs/tooth_3.csv.gz', + onProgress, + signal, + ).then((response) => response); diff --git a/src/utils/colors.ts b/src/utils/colors.ts new file mode 100644 index 0000000..a42b474 --- /dev/null +++ b/src/utils/colors.ts @@ -0,0 +1,17 @@ +// eslint-disable-next-line import/prefer-default-export +export enum Colors { + RED = '#f32450', + CYAN = '#8dd3c7', + YELLOW = '#ffff00', + PURPLE = '#bebada', + GREEN = '#77de3c', + BLUE = '#2fe3ff', + GREY = '#334455', + + // dark theme + ACCENT = '#1e88ea', + TEXT = '#ddd', + MAIN = '#222629', + MAIN_DARK = '#191C1E', + MAIN_LIGHT = '#2E3338', +} diff --git a/src/utils/logs/TriggerLogsParser.ts b/src/utils/logs/TriggerLogsParser.ts new file mode 100644 index 0000000..08377cc --- /dev/null +++ b/src/utils/logs/TriggerLogsParser.ts @@ -0,0 +1,160 @@ +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 { + COMMENT_PREFIX = '#'; + + MARKER_PREFIX = 'MARK'; + + isTooth: boolean = false; + + isComposite: boolean = false; + + resultComposite: CompositeLogEntry[] = []; + + resultTooth: ToothLogEntry[] = []; + + parse(buffer: ArrayBuffer): TriggerLogsParser { + const raw = (new TextDecoder()).decode(buffer); + this.parseCompositeLogs(raw); + this.parseToothLogs(raw); + + return this; + } + + getCompositeLogs(): CompositeLogEntry[] { + return this.resultComposite; + } + + getToothLogs(): ToothLogEntry[] { + return this.resultTooth; + } + + private parseToothLogs(raw: string): void { + this.resultTooth = []; + + raw.split('\n').forEach((line) => { + const trimmed = line.trim(); + + if (trimmed.startsWith(this.COMMENT_PREFIX)) { + return; + } + + if (trimmed.startsWith(this.MARKER_PREFIX)) { + 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 split = trimmed.split(','); + if (!isNumber(split[0])) { + return; + } + + const time = Number(split[1]); + if (!time) { + return; + } + + this.resultTooth.push({ + type: EntryType.TRIGGER, + toothTime: Number(split[0]), + time, + }); + }); + } + + private parseCompositeLogs(raw: string): void { + this.resultComposite = []; + + raw.split('\n').forEach((line) => { + const trimmed = line.trim(); + + if (trimmed.startsWith(this.COMMENT_PREFIX)) { + return; + } + + if (trimmed.startsWith(this.MARKER_PREFIX)) { + 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 split = trimmed.split(','); + if (!isNumber(split[0])) { + return; + } + + const time = Number(split[7]); + if (!time) { + return; + } + + this.resultComposite.push({ + type: EntryType.TRIGGER, + primaryLevel: Number(split[0]), + secondaryLevel: Number(split[1]), + trigger: Number(split[2]), + sync: Number(split[3]), + refTime: Number(split[4]), + maxTime: Number(split[5]), + toothTime: Number(split[6]), + time, + }); + }); + } +} + +export default TriggerLogsParser;