From 5c34c7c1670a389f7c7a06d4d87798b199a90d15 Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Sun, 20 Sep 2020 18:05:37 +0300 Subject: [PATCH] Add Zwift XML generation --- src/generateZwo.ts | 71 ++++++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 6 +++- 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 src/generateZwo.ts diff --git a/src/generateZwo.ts b/src/generateZwo.ts new file mode 100644 index 0000000..17fb752 --- /dev/null +++ b/src/generateZwo.ts @@ -0,0 +1,71 @@ +import * as xml from "xml"; +import { Interval, Workout } from "./ast"; + +// Zwift Workout XML generator + +const generateRangeInterval = ( + tagName: "Warmup" | "Cooldown", + { duration, intensity, cadence }: Interval +): xml.XmlObject => { + return { + [tagName]: [ + { + _attr: { + Duration: duration, + PowerLow: intensity.from, + PowerHigh: intensity.from, + ...(cadence ? { Cadence: cadence } : {}), + }, + }, + ], + }; +}; + +const generateSteadyStateInterval = ({ + duration, + intensity, + cadence, +}: Interval): xml.XmlObject => { + return { + SteadyState: [ + { + _attr: { + Duration: duration, + Power: intensity.from, + ...(cadence ? { Cadence: cadence } : {}), + }, + }, + ], + }; +}; + +const generateInterval = (interval: Interval): xml.XmlObject => { + const { intensity } = interval; + if (intensity.from < intensity.to) { + return generateRangeInterval("Warmup", interval); + } else if (intensity.from > intensity.to) { + return generateRangeInterval("Cooldown", interval); + } else { + return generateSteadyStateInterval(interval); + } +}; + +export const generateZwo = ({ + name, + author, + description, + intervals, +}: Workout): string => { + return xml( + { + workout_file: [ + { name: name }, + { author: author }, + { description: description }, + { sportType: "bike" }, + ...intervals.map(generateInterval), + ], + }, + { indent: " " } + ); +}; diff --git a/src/index.ts b/src/index.ts index c1b0700..11dedd3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ import * as fs from "fs"; import { averageIntensity } from "./averageIntensity"; +import { generateZwo } from "./generateZwo"; import { normalizedIntensity } from "./normalizedIntensity"; import { parse } from "./parser"; import { tokenize } from "./tokenizer"; @@ -13,7 +14,8 @@ console.log(`Parsing: ${filename}`); const file = fs.readFileSync(filename, "utf8"); -const { intervals } = parse(tokenize(file)); +const workout = parse(tokenize(file)); +const { intervals } = workout; const duration = totalDuration(intervals); const avgIntensity = averageIntensity(intervals); @@ -30,3 +32,5 @@ Normalized intensity: ${(normIntensity * 100).toFixed()}% TSS #1: ${tss(intervals).toFixed()} TSS #2: ${tss2(duration, normIntensity).toFixed()} `); + +console.log(generateZwo(workout));