@measurequick/measurequick-report-generator 1.5.221 → 1.5.223

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@measurequick/measurequick-report-generator",
3
- "version": "1.5.221",
3
+ "version": "1.5.223",
4
4
  "description": "Generates PDF documents for various measureQuick applications.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -254,6 +254,8 @@ export async function getReport(payload, _test) {
254
254
  return isFinite(n) ? n : NaN;
255
255
  }
256
256
  var coAmbRaw = toNum(data && data.co_ambient);
257
+ var coRetRaw = toNum(data && data.co_return);
258
+ var coSupRaw = toNum(data && data.co_supply);
257
259
  if (isFinite(coAmbRaw) && coAmbRaw > 9) {
258
260
  safeSetText(
259
261
  form,
@@ -511,16 +513,57 @@ export async function getReport(payload, _test) {
511
513
  passFails.splice(3, 1);
512
514
  pfLabels.splice(3, 1);
513
515
  }
516
+
517
+ // CO threshold color helpers
518
+ function getCOIcon(coValue) {
519
+ if (coValue === null || coValue === undefined || isNaN(coValue)) {
520
+ return iconRangeGreen;
521
+ }
522
+ if (coValue <= 9) {
523
+ return iconRangeGreen;
524
+ } else if (coValue > 9 && coValue <= 35) {
525
+ return iconRangeYellow;
526
+ } else {
527
+ return iconRangeRed;
528
+ }
529
+ }
530
+
531
+ function getCOTextColor(coValue) {
532
+ if (coValue === null || coValue === undefined || isNaN(coValue)) {
533
+ return rgb(0, 0, 0);
534
+ }
535
+ if (coValue <= 9) {
536
+ return rgb(0, 0, 0); // black - OK
537
+ } else if (coValue > 9 && coValue <= 35) {
538
+ return rgb(1, 0.6, 0); // orange - Elevated
539
+ } else {
540
+ return rgb(0.96, 0.26, 0.21); // red - Dangerous
541
+ }
542
+ }
543
+
544
+ function getCOStatus(coValue) {
545
+ if (coValue === null || coValue === undefined || isNaN(coValue)) {
546
+ return "";
547
+ }
548
+ if (coValue <= 9) {
549
+ return "OK";
550
+ } else {
551
+ return "Elevated";
552
+ }
553
+ }
554
+
555
+ // Collect valid items first (no gaps)
556
+ let validItems = [];
557
+
514
558
  for (let i = 0; i < passFails.length; i++) {
515
559
  const meas = test.testInfo[passFails[i]];
516
560
 
517
- // If no value provided, hide the label (set empty) and skip icon
561
+ // Skip items with no value
518
562
  if (meas === undefined || meas === null || meas === "") {
519
- safeSetText(form, `SSR${i + 1}`, "");
520
563
  continue;
521
564
  }
522
565
 
523
- // Choose icon based on value
566
+ // Determine icon based on value
524
567
  let icon;
525
568
  if (meas === "Caution") {
526
569
  icon = iconRangeYellow;
@@ -529,53 +572,85 @@ export async function getReport(payload, _test) {
529
572
  } else {
530
573
  icon = iconRangeRed;
531
574
  }
532
- if (passFails[i] === "heating_efficiency_pass_fail")
575
+ if (passFails[i] === "heating_efficiency_pass_fail") {
533
576
  icon = iconRangeGreen;
534
-
535
- // Set the icon as before
536
- safeSetImage(form, `ImageSubsystem${i + 1}_af_image`, icon);
577
+ }
537
578
 
538
579
  const suffix = test.testInfo[passFails[i] + "_override"] ? " *" : "";
539
- const fieldName = `SSR${i + 1}`;
540
-
541
- // Default label for non-special cases
542
580
  let label = pfLabels[i] + suffix;
581
+ let textColor = rgb(0, 0, 0); // default black
543
582
 
544
- // Special handling: Ambient CO failure should be red and show a stronger warning
583
+ // Special handling for Ambient CO - show actual values
545
584
  if (passFails[i] === "ambient_pass_fail") {
546
- const isFail = !(
547
- meas === "Pass" ||
548
- meas === "High" ||
549
- meas === "Mid" ||
550
- meas === "Caution"
551
- );
552
-
553
- if (isFail) {
554
- label = "CO Ambient High, Investigate Source!";
555
- try {
556
- const tf = form.getTextField(fieldName);
557
- tf.setText(label);
558
- tf.setTextColor(rgb(1, 0, 0)); // red
559
- } catch (e) {}
560
- continue; // move to next item; we've already set text & color
561
- } else {
562
- // Non-fail states: ensure text is black
563
- try {
564
- const tf = form.getTextField(fieldName);
565
- tf.setText(label);
566
- tf.setTextColor(rgb(0, 0, 0)); // black
567
- } catch (e) {}
568
- continue;
585
+ // Calculate CO delta
586
+ const coDelta = (isFinite(coSupRaw) && isFinite(coRetRaw)) ? (coSupRaw - coRetRaw) : null;
587
+ const hasCODeltaWarning = test.testInfo.ambient_pass_fail_co_delta_warning || (coDelta !== null && coDelta >= 1);
588
+
589
+ // Add CO Ambient with value
590
+ if (isFinite(coAmbRaw)) {
591
+ const coAmbIcon = getCOIcon(coAmbRaw);
592
+ const coAmbColor = getCOTextColor(coAmbRaw);
593
+ const coAmbStatus = getCOStatus(coAmbRaw);
594
+ const coAmbLabel = "CO Ambient: " + Math.round(coAmbRaw) + " ppm (" + coAmbStatus + ")" + suffix;
595
+ validItems.push({ label: coAmbLabel, icon: coAmbIcon, textColor: coAmbColor });
596
+ } else if (meas) {
597
+ // No CO value but has pass/fail - show generic
598
+ const isFail = !(meas === "Pass" || meas === "High" || meas === "Mid" || meas === "Caution");
599
+ if (isFail) {
600
+ validItems.push({ label: "CO Ambient: Elevated" + suffix, icon: iconRangeYellow, textColor: rgb(1, 0.6, 0) });
601
+ } else {
602
+ validItems.push({ label: "CO Ambient: OK" + suffix, icon: iconRangeGreen, textColor: rgb(0, 0, 0) });
603
+ }
604
+ }
605
+
606
+ // Add CO Return with value (if available and not boiler)
607
+ if (isFinite(coRetRaw) && !payload.meta.boiler_type) {
608
+ const coRetIcon = getCOIcon(coRetRaw);
609
+ const coRetColor = getCOTextColor(coRetRaw);
610
+ const coRetStatus = getCOStatus(coRetRaw);
611
+ const coRetLabel = "CO Return: " + Math.round(coRetRaw) + " ppm (" + coRetStatus + ")";
612
+ validItems.push({ label: coRetLabel, icon: coRetIcon, textColor: coRetColor });
613
+ }
614
+
615
+ // Add CO Supply with value (if available and not boiler)
616
+ if (isFinite(coSupRaw) && !payload.meta.boiler_type) {
617
+ const coSupIcon = getCOIcon(coSupRaw);
618
+ const coSupColor = getCOTextColor(coSupRaw);
619
+ const coSupStatus = getCOStatus(coSupRaw);
620
+ const coSupLabel = "CO Supply: " + Math.round(coSupRaw) + " ppm (" + coSupStatus + ")";
621
+ validItems.push({ label: coSupLabel, icon: coSupIcon, textColor: coSupColor });
569
622
  }
623
+
624
+ // Add CO Delta (if both return and supply available and not boiler)
625
+ if (coDelta !== null && !payload.meta.boiler_type) {
626
+ const deltaIcon = hasCODeltaWarning ? iconRangeRed : iconRangeGreen;
627
+ const deltaColor = hasCODeltaWarning ? rgb(0.96, 0.26, 0.21) : rgb(0, 0, 0);
628
+ const deltaStatus = hasCODeltaWarning ? "Investigate Heat Exchanger" : "OK";
629
+ const deltaLabel = "CO Delta: " + Math.round(coDelta) + " ppm (" + deltaStatus + ")";
630
+ validItems.push({ label: deltaLabel, icon: deltaIcon, textColor: deltaColor });
631
+ }
632
+
633
+ continue; // Skip adding the default ambient_pass_fail item
570
634
  }
571
635
 
572
- // All other pass/fail items: keep existing label, ensure black text
636
+ validItems.push({ label, icon, textColor });
637
+ }
638
+
639
+ // Fill fields sequentially with valid items (no gaps)
640
+ for (let i = 0; i < validItems.length; i++) {
641
+ const item = validItems[i];
642
+ safeSetImage(form, `ImageSubsystem${i + 1}_af_image`, item.icon);
573
643
  try {
574
- const tf = form.getTextField(fieldName);
575
- tf.setText(label);
576
- tf.setTextColor(rgb(0, 0, 0));
644
+ const tf = form.getTextField(`SSR${i + 1}`);
645
+ tf.setText(item.label);
646
+ tf.setTextColor(item.textColor);
577
647
  } catch (e) {}
578
648
  }
649
+
650
+ // Clear any remaining unused fields
651
+ for (let i = validItems.length; i < passFails.length + 3; i++) {
652
+ safeSetText(form, `SSR${i + 1}`, "");
653
+ }
579
654
  } else safeSetText(form, `SSR1`, "Not yet reviewed");
580
655
 
581
656
  if (payload.meta.report_type != "FullReport") {
@@ -330,13 +330,13 @@ export async function getReport(payload, _test) {
330
330
  // Build warning message focused on ambient CO
331
331
  var warningMsg, warningMsg2;
332
332
  if (ambientReading) {
333
- warningMsg = "WARNING: " + ambientReading.level + " Ambient CO Detected: " + Math.round(ambientReading.value) + " ppm\nVentilate the area and investigate source.";
334
- warningMsg2 = "WARNING: " + ambientReading.level + " Ambient CO Detected: " + Math.round(ambientReading.value) + " ppm. Ventilate the area and investigate source.";
333
+ warningMsg = "WARNING: " + ambientReading.level + " Ambient CO " + Math.round(ambientReading.value) + " ppm\nVentilate the area and investigate source.";
334
+ warningMsg2 = "WARNING: " + ambientReading.level + " Ambient CO " + Math.round(ambientReading.value) + " ppm. Ventilate the area and investigate source.";
335
335
  } else {
336
336
  // Fallback if no ambient reading (use first reading)
337
337
  var firstReading = reasons[0];
338
- warningMsg = "WARNING: " + firstReading.level + " " + firstReading.type + " CO Detected: " + Math.round(firstReading.value) + " ppm\nVentilate the area and investigate source.";
339
- warningMsg2 = "WARNING: " + firstReading.level + " " + firstReading.type + " CO Detected: " + Math.round(firstReading.value) + " ppm. Ventilate the area and investigate source.";
338
+ warningMsg = "WARNING: " + firstReading.level + " " + firstReading.type + " CO " + Math.round(firstReading.value) + " ppm\nVentilate the area and investigate source.";
339
+ warningMsg2 = "WARNING: " + firstReading.level + " " + firstReading.type + " CO " + Math.round(firstReading.value) + " ppm. Ventilate the area and investigate source.";
340
340
  }
341
341
 
342
342
  // Set text with color based on severity level
@@ -672,13 +672,23 @@ export async function getReport(payload, _test) {
672
672
  // Use coAmbRaw which is already defined and validated earlier in the function
673
673
  const coValue = isFinite(coAmbRaw) ? coAmbRaw : null;
674
674
 
675
+ // Check for CO delta warning (Supply - Return >= 1 ppm)
676
+ const coDelta = (isFinite(coSupRaw) && isFinite(coRetRaw)) ? (coSupRaw - coRetRaw) : null;
677
+ const hasCODeltaWarning = test.testInfo.ambient_pass_fail_co_delta_warning || (coDelta !== null && coDelta >= 1);
678
+
675
679
  // Use CO-specific icon based on actual value
676
680
  if (coValue !== null) {
677
681
  icon = getCOIcon(coValue);
678
682
  const stateText = getCOStateText(coValue);
679
683
 
680
- // Format label based on CO level
681
- if (coValue <= 8) {
684
+ // Format label based on CO level and delta warning
685
+ if (hasCODeltaWarning) {
686
+ // CO delta detected - recommend heat exchanger investigation
687
+ icon = iconRangeRed;
688
+ textColor = rgb(0.96, 0.26, 0.21); // Red
689
+ const deltaValue = coDelta !== null ? Math.round(coDelta) : "";
690
+ label = "CO Delta " + deltaValue + " ppm: Investigate heat exchanger" + suffix;
691
+ } else if (coValue <= 8) {
682
692
  // Normal - just show value
683
693
  label = "Ambient CO: " + Math.round(coValue) + " ppm" + suffix;
684
694
  } else {
@@ -686,15 +696,23 @@ export async function getReport(payload, _test) {
686
696
  label = "CO Ambient " + stateText + " " + Math.round(coValue) + " ppm, Investigate!" + suffix;
687
697
  }
688
698
 
689
- // Set text color based on CO level
690
- // Orange for Elevated, Red for Dangerous, Dark Red for Emergency
691
- if (coValue >= 70) {
692
- textColor = rgb(0.72, 0.11, 0.11); // Dark red for Emergency
693
- } else if (coValue >= 36) {
694
- textColor = rgb(0.96, 0.26, 0.21); // Red for Dangerous
695
- } else if (coValue >= 9) {
696
- textColor = rgb(1, 0.6, 0); // Orange for Elevated
699
+ // Set text color based on CO level (if not already set by delta warning)
700
+ if (!hasCODeltaWarning) {
701
+ // Orange for Elevated, Red for Dangerous, Dark Red for Emergency
702
+ if (coValue >= 70) {
703
+ textColor = rgb(0.72, 0.11, 0.11); // Dark red for Emergency
704
+ } else if (coValue >= 36) {
705
+ textColor = rgb(0.96, 0.26, 0.21); // Red for Dangerous
706
+ } else if (coValue >= 9) {
707
+ textColor = rgb(1, 0.6, 0); // Orange for Elevated
708
+ }
697
709
  }
710
+ } else if (hasCODeltaWarning) {
711
+ // No ambient CO value but delta warning detected
712
+ icon = iconRangeRed;
713
+ textColor = rgb(0.96, 0.26, 0.21); // Red
714
+ const deltaValue = coDelta !== null ? Math.round(coDelta) : "";
715
+ label = "CO Delta " + deltaValue + " ppm: Investigate heat exchanger" + suffix;
698
716
  } else {
699
717
  // No CO value, use pass/fail status
700
718
  const isFail = !(meas === "Pass" || meas === "High" || meas === "Mid" || meas === "Caution");