Cornell

ip-10-92-53-53.ec2.internal — Last seen:

Tag:

Loading license status…

Health

79% healthy
PodFunctionsOverridesScore
kwGithubExt 20 0 100%
kwLinkAnalyticsExt 326 50 85%
kwLinkCoreExt 154 4 97%
kwLinkDevToolsExt 17 0 100%
kwLinkDiagnosticsExt 55 0 100%
kwLinkegratorExt 100 1 99%
kwLinkEnergyAgentExt 169 29 83%
kwLinkEventsExt 22 0 100%
kwLinkMbcxExt 47 1 98%
kwLinkModelExt 191 10 95%
kwLinkRuleExt 177 9 95%
kwLinkUMichRuleExt 84 0 100%
kwLinkVirtualExt 95 10 89%
kwLinkWorkflowExt 22 0 100%
evseExt 20 0 100%
Show / hide diffs (113)
kwLinkAnalyticsExt
buildingLoopDigest
--- buildingLoopDigest (pod)
+++ buildingLoopDigest (local)
@@ -24,9 +24,11 @@
   condenserId: try nestedEquips.find(r => r.has("plantLoop") and r.has("condenser")).get("id") catch null

 

   // Override Opts Handling

-  if (opts.get("primaryLoopOverride").isRef)   primaryId   = opts.get("primaryLoopOverride")

-  if (opts.get("secondaryLoopOverride").isRef) secondaryId = opts.get("secondaryLoopOverride")

-  if (opts.get("condenserLoopOverride").isRef) condenserId = opts.get("condenserLoopOverride")

+  if (opts.get("primaryLoopOverride").isRef)     primaryId   = opts.get("primaryLoopOverride")

+  if (opts.get("primaryLoopOverride") == "null")   primaryId   = null

+  if (opts.get("secondaryLoopOverride").isRef)   secondaryId = opts.get("secondaryLoopOverride")

+  if (opts.get("secondaryLoopOverride") == "null") secondaryId = null

+  if (opts.get("condenserLoopOverride").isRef)   condenserId = opts.get("condenserLoopOverride")

 

   if(mode == "idMode") do

     if(secondaryId.isNonNull) return secondaryId else return primaryId

func_euiLibrary
--- func_euiLibrary (pod)
+++ func_euiLibrary (local)
@@ -74,7 +74,7 @@
         {primaryFunction:"Strip Mall", eui_source:228.8, eui_site:103.5},

         {primaryFunction:"Lifestyle Center", eui_source:228.8, eui_site:103.5},

         {primaryFunction:"Retail Store", eui_source:120, eui_site:103.5},

-        {primaryFunction:"Laboratory", eui_source:318.2, eui_site:115.3},

+        {primaryFunction:"Laboratory", eui_source:318.2, eui_site:256},

         {primaryFunction:"Drinking Water Treatment & Distribution", eui_source:5.9, eui_site:2.3},

         {primaryFunction:"Repair Services (Vehicle, Shoe, Locksmith, etc)", eui_source:96.9, eui_site:47.9},

         {primaryFunction:"Personal Services (Health/Beauty, Dry Cleaning, etc)", eui_source:96.9, eui_site:47.9},

getCHWMeterSummary
--- getCHWMeterSummary (pod)
+++ getCHWMeterSummary (local)
@@ -7,7 +7,6 @@
   //normalize inputs

   siteRec: site.toRec

   siteId: site->id

-  dates = dates.toSpan

 

   //get opts

   debug: if(opts.has("debug")) opts.get("debug")

@@ -57,14 +56,14 @@
   if (points.vals.all(v=>v==missingPointValue)) return {dis:"Missing all required points", action:"Add a site meter and the required points"}

 

   //define col meta options on points

-  if (chw_intervalCons!=null and chw_intervalCons!=missingValue) chw_intervalCons= chw_intervalCons                               .merge({dis:"Chilled Water Consumption", chartGroup:"chw", color:"skyBlue"})

-  if (chw_intervalModelCons!=null and chw_intervalModelCons!=missingValue) chw_intervalModelCons=    chw_intervalModelCons        .merge({dis:"Chilled Water Model", chartGroup:"chw", color:"skyBlue", strokeDasharray:"1,1"})

+  if (chw_intervalCons!=null and chw_intervalCons!=missingValue) chw_intervalCons= chw_intervalCons                               .merge({dis:"Chilled Water Consumption", chartGroup:"chw", color:"purple"})

+  if (chw_intervalModelCons!=null and chw_intervalModelCons!=missingValue) chw_intervalModelCons=    chw_intervalModelCons        .merge({dis:"Chilled Water Model", chartGroup:"chw", color:"purple", strokeDasharray:"1,1"})

   if (chw_intervalSavingsCons!=null and chw_intervalSavingsCons!=missingValue) chw_intervalSavingsCons=  chw_intervalSavingsCons  .merge({})

   if (chw_intervalSavingsCost!=null and chw_intervalSavingsCost!=missingValue) chw_intervalSavingsCost= chw_intervalSavingsCost   .merge({})

   if (chw_intervalCost!=null and chw_intervalCost!=missingValue) chw_intervalCost= chw_intervalCost                               .merge({})

 

-  if (chw_monthlyCons!=null and chw_monthlyCons!=missingValue) chw_monthlyCons= chw_monthlyCons                                   .merge({dis:"Chilled Water Consumption", chartGroup:"chw", color:"skyBlue"})

-  if (chw_monthlyModelCons!=null and chw_monthlyModelCons!=missingValue) chw_monthlyModelCons= chw_monthlyModelCons               .merge({dis:"Chilled Water Model", chartGroup:"chw", color:"skyBlue", strokeDasharray:"1,1"})

+  if (chw_monthlyCons!=null and chw_monthlyCons!=missingValue) chw_monthlyCons= chw_monthlyCons                                   .merge({dis:"Chilled Water Consumption", chartGroup:"chw", color:"purple"})

+  if (chw_monthlyModelCons!=null and chw_monthlyModelCons!=missingValue) chw_monthlyModelCons= chw_monthlyModelCons               .merge({dis:"Chilled Water Model", chartGroup:"chw", color:"purple", strokeDasharray:"1,1"})

   if (chw_monthlySavingsCons!=null and chw_monthlySavingsCons!=missingValue) chw_monthlySavingsCons= chw_monthlySavingsCons       .merge({})

   if (chw_monthlySavingsCost!=null and chw_monthlySavingsCost!=missingValue) chw_monthlySavingsCost= chw_monthlySavingsCost       .merge({})

   if (chw_monthlyCost!=null and chw_monthlyCost!=missingValue) chw_monthlyCost= chw_monthlyCost                                   .merge({})

@@ -73,11 +72,11 @@
 

 

   //get his

-  his_chw_intervalCons: try chw_intervalCons.hisRead(dates, {-limit}).renameCol("v0","chw_intervalCons") catch null

-  his_chw_intervalModelCons: try chw_intervalModelCons.hisRead(dates, {-limit}).renameCol("v0","chw_intervalModelCons") catch null

-  his_chw_intervalSavingsCons: try chw_intervalSavingsCons.hisRead(dates, {-limit}).hisRollup(avg, 1hr).renameCol("v0","chw_intervalSavingsCons") catch null

-  his_chw_intervalSavingsCost: try chw_intervalSavingsCost.hisRead(dates, {-limit}).renameCol("v0","chw_intervalSavingsCost") catch null

-  his_chw_intervalCost: try chw_intervalCost.hisRead(dates, {-limit}).renameCol("v0","chw_intervalCost") catch null

+  his_chw_intervalCons: try chw_intervalCons.hisRead(dates, {-limit}).hisRollup(avg, 1hr).hisInterpolate().renameCol("v0","chw_intervalCons") catch null

+  his_chw_intervalModelCons: try chw_intervalModelCons.hisRead(dates, {-limit}).hisRollup(avg, 1hr).hisInterpolate().renameCol("v0","chw_intervalModelCons") catch null

+  his_chw_intervalSavingsCons: try chw_intervalSavingsCons.hisRead(dates, {-limit}).hisRollup(avg, 1hr).hisInterpolate().renameCol("v0","chw_intervalSavingsCons") catch null

+  his_chw_intervalSavingsCost: try chw_intervalSavingsCost.hisRead(dates, {-limit}).hisRollup(avg, 1hr).hisInterpolate().renameCol("v0","chw_intervalSavingsCost") catch null

+  his_chw_intervalCost: try chw_intervalCost.hisRead(dates, {-limit}).hisRollup(avg, 1hr).hisInterpolate().renameCol("v0","chw_intervalCost") catch null

 

   his_chw_monthlyCons: try chw_monthlyCons.hisRead(dates, {-limit}).renameCol("v0","chw_monthlyCons") catch null

   his_chw_monthlyModelCons: try chw_monthlyModelCons.hisRead(dates, {-limit}).renameCol("v0","chw_monthlyModelCons") catch null

getElecMeterSummary
--- getElecMeterSummary (pod)
+++ getElecMeterSummary (local)
@@ -7,7 +7,6 @@
   //normalize inputs

   siteRec: site.getRecord

   siteId: site->id

-  dates = dates.toSpan

 

   //get opts

   debug: if (opts.has("debug")) opts.get("debug")

@@ -78,11 +77,11 @@
 

 

   //get his and rename columns

-  his_elec_intervalPwr: try elec_intervalPwr.hisRead(dates, {-limit}).hisRollup(avg,1hr).renameCol("v0","elec_intervalPwr").hisClean catch null

-  his_elec_modelPwr:    try elec_modelPwr.hisRead(dates, {-limit}).hisRollup(avg,1hr).renameCol("v0","elec_modelPwr").hisClean catch null

-  his_elec_savingsPwr:  try elec_savingsPwr.hisRead(dates, {-limit}).hisRollup(avg,1hr).renameCol("v0","elec_savingsPwr").hisClean catch null

-  his_elec_intervalSavingsCost: try elec_intervalSavingsCost.hisRead(dates, {-limit}).hisRollup(avg,1hr).renameCol("v0","elec_intervalSavingsCost").hisClean catch null

-  his_elec_intervalCost: try elec_intervalCost.hisRead(dates, {-limit}).hisRollup(avg,1hr).renameCol("v0","elec_intervalCost").hisClean catch null

+  his_elec_intervalPwr: try elec_intervalPwr.hisRead(dates, {-limit}).hisRollup(avg,1hr).hisInterpolate().renameCol("v0","elec_intervalPwr").hisClean catch null

+  his_elec_modelPwr:    try elec_modelPwr.hisRead(dates, {-limit}).hisRollup(avg,1hr).hisInterpolate().renameCol("v0","elec_modelPwr").hisClean catch null

+  his_elec_savingsPwr:  try elec_savingsPwr.hisRead(dates, {-limit}).hisRollup(avg,1hr).hisInterpolate().renameCol("v0","elec_savingsPwr").hisClean catch null

+  his_elec_intervalSavingsCost: try elec_intervalSavingsCost.hisRead(dates, {-limit}).hisRollup(avg,1hr).hisInterpolate().renameCol("v0","elec_intervalSavingsCost").hisClean catch null

+  his_elec_intervalCost: try elec_intervalCost.hisRead(dates, {-limit}).hisRollup(avg,1hr).hisInterpolate().renameCol("v0","elec_intervalCost").hisClean catch null

 

   his_elec_monthlyEnergy: try elec_monthlyEnergy.hisRead(dates, {-limit}).hisRollup(sum,1mo).renameCol("v0","elec_monthlyEnergy").hisClean catch null

   his_elec_modelEnergy:   try elec_modelEnergy.hisRead(dates, {-limit}).hisRollup(sum,1mo).renameCol("v0","elec_modelEnergy").hisClean catch null

@@ -125,9 +124,9 @@
   if (debug=="his") return hisSummary

 

   //rollup data into summary values we can use quickly in widgets

-  rollup_elec_intervalPwr: try his_elec_intervalPwr.hisRollup(avg,1hr).hisMap(v=>v.as("kWh")).hisRollup(sum,"*").first.get("elec_intervalPwr") catch missingValue

-  rollup_elec_modelPwr:    try his_elec_modelPwr.hisRollup(avg,1hr).hisMap(v=>v.as("kWh")).hisRollup(sum,"*").first.get("elec_modelPwr") catch missingValue

-  rollup_elec_savingsPwr:  try his_elec_savingsPwr.hisRollup(avg,1hr).hisMap(v=>v.as("kWh")).hisRollup(sum,"*").first.get("elec_savingsPwr") catch missingValue

+  rollup_elec_intervalPwr: try his_elec_intervalPwr.hisRollup(avg,1hr).hisMap(v=>v.to("kW")).hisMap(v=>v.as("kWh")).hisRollup(sum,"*").first.get("elec_intervalPwr") catch missingValue

+  rollup_elec_modelPwr:    try his_elec_modelPwr.hisRollup(avg,1hr).hisMap(v=>v.to("kW")).hisMap(v=>v.as("kWh")).hisRollup(sum,"*").first.get("elec_modelPwr") catch missingValue

+  rollup_elec_savingsPwr:  try his_elec_savingsPwr.hisRollup(avg,1hr).hisMap(v=>v.to("kW")).hisMap(v=>v.as("kWh")).hisRollup(sum,"*").first.get("elec_savingsPwr") catch missingValue

   rollup_elec_intervalSavingsCost:   try his_elec_intervalSavingsCost.hisRollup(sum,"*").first.get("elec_intervalSavingsCost") catch missingValue

   rollup_elec_intervalCost:   try his_elec_intervalCost.hisRollup(sum,"*").first.get("elec_intervalCost") catch missingValue

 

@@ -199,22 +198,22 @@
   //get effective rollups - only for the points that are relevant

   rollup_elec_usage: if (rollup_elec_intervalPwr!=null and rollup_elec_intervalPwr!=missingValue) rollup_elec_intervalPwr

                 else if (rollup_elec_monthlyEnergy!=null and rollup_elec_monthlyEnergy!=missingValue) rollup_elec_monthlyEnergy

-                else missingValue

+                else null

   rollup_elec_model: if (rollup_elec_modelPwr!=null and rollup_elec_modelPwr!=missingValue) rollup_elec_modelPwr

                 else if (rollup_elec_modelEnergy!=null and rollup_elec_modelEnergy!=missingValue) rollup_elec_modelEnergy

-                else missingValue

+                else null

   rollup_elec_savings: if (rollup_elec_savingsPwr!=null and rollup_elec_savingsPwr!=missingValue) rollup_elec_savingsPwr

                 else if (rollup_elec_savingsEnergy!=null and rollup_elec_savingsEnergy!=missingValue) rollup_elec_savingsEnergy

-                else missingValue

+                else null

   rollup_elec_cost: if (rollup_elec_intervalCost!=null and rollup_elec_intervalCost!=missingValue) rollup_elec_intervalCost

                 else if (rollup_elec_monthlyCost!=null and rollup_elec_monthlyCost!=missingValue) rollup_elec_monthlyCost

-                else missingValue

+                else null

   rollup_elec_savingsCost: if (rollup_elec_intervalSavingsCost!=null and rollup_elec_intervalSavingsCost!=missingValue) rollup_elec_intervalSavingsCost

                 else if (rollup_elec_monthlySavingsCost!=null and rollup_elec_monthlySavingsCost!=missingValue) rollup_elec_monthlySavingsCost

-                else missingValue

+                else null

   rollup_elec_rate: if (siteRec.has("elecRate")) siteRec.get("elecRate")

                 else if (rollup_elec_monthlyRateCost!=null and rollup_elec_monthlyRateCost!=missingValue) rollup_elec_monthlyRateCost

-                else missingValue

+                else null

 

   effectiveRollup: {elec_usage: rollup_elec_usage,

                     elec_model: rollup_elec_model,

getHWMeterSummary
--- getHWMeterSummary (pod)
+++ getHWMeterSummary (local)
@@ -7,7 +7,6 @@
   //normalize inputs

   siteRec: site.toRec

   siteId: site->id

-  dates = dates.toSpan

 

   //get opts

   debug: if(opts.has("debug")) opts.get("debug")

@@ -57,14 +56,14 @@
   if (points.vals.all(v=>v==missingPointValue)) return {dis:"Missing all required points", action:"Add a site meter and the required points"}

 

   //define col meta options on points

-  if (hw_intervalCons!=null and hw_intervalCons!=missingValue) hw_intervalCons= hw_intervalCons                               .merge({dis:"Hot Water Consumption", chartGroup:"hw", color:"tomato"})

-  if (hw_intervalModelCons!=null and hw_intervalModelCons!=missingValue) hw_intervalModelCons=    hw_intervalModelCons        .merge({dis:"Hot Water Model", chartGroup:"hw", color:"tomato", strokeDasharray:"1,1"})

+  if (hw_intervalCons!=null and hw_intervalCons!=missingValue) hw_intervalCons= hw_intervalCons                               .merge({dis:"Hot Water Consumption", chartGroup:"hw", color:"purple"})

+  if (hw_intervalModelCons!=null and hw_intervalModelCons!=missingValue) hw_intervalModelCons=    hw_intervalModelCons        .merge({dis:"Hot Water Model", chartGroup:"hw", color:"purple", strokeDasharray:"1,1"})

   if (hw_intervalSavingsCons!=null and hw_intervalSavingsCons!=missingValue) hw_intervalSavingsCons=  hw_intervalSavingsCons  .merge({})

   if (hw_intervalSavingsCost!=null and hw_intervalSavingsCost!=missingValue) hw_intervalSavingsCost= hw_intervalSavingsCost   .merge({})

   if (hw_intervalCost!=null and hw_intervalCost!=missingValue) hw_intervalCost= hw_intervalCost                               .merge({})

 

-  if (hw_monthlyCons!=null and hw_monthlyCons!=missingValue) hw_monthlyCons= hw_monthlyCons                                   .merge({dis:"Hot Water Consumption", chartGroup:"hw", color:"tomato"})

-  if (hw_monthlyModelCons!=null and hw_monthlyModelCons!=missingValue) hw_monthlyModelCons= hw_monthlyModelCons               .merge({dis:"Hot Water Model", chartGroup:"hw", color:"tomato", strokeDasharray:"1,1"})

+  if (hw_monthlyCons!=null and hw_monthlyCons!=missingValue) hw_monthlyCons= hw_monthlyCons                                   .merge({dis:"Hot Water Consumption", chartGroup:"hw", color:"purple"})

+  if (hw_monthlyModelCons!=null and hw_monthlyModelCons!=missingValue) hw_monthlyModelCons= hw_monthlyModelCons               .merge({dis:"Hot Water Model", chartGroup:"hw", color:"purple", strokeDasharray:"1,1"})

   if (hw_monthlySavingsCons!=null and hw_monthlySavingsCons!=missingValue) hw_monthlySavingsCons= hw_monthlySavingsCons       .merge({})

   if (hw_monthlySavingsCost!=null and hw_monthlySavingsCost!=missingValue) hw_monthlySavingsCost= hw_monthlySavingsCost       .merge({})

   if (hw_monthlyCost!=null and hw_monthlyCost!=missingValue) hw_monthlyCost= hw_monthlyCost                                   .merge({})

getSteamMeterSummary
--- getSteamMeterSummary (pod)
+++ getSteamMeterSummary (local)
@@ -7,7 +7,6 @@
   //normalize inputs

   siteRec: site.toRec

   siteId: site->id

-  dates = dates.toSpan

 

   //get opts

   debug: if(opts.has("debug")) opts.get("debug")

@@ -57,14 +56,14 @@
   if (points.vals.all(v=>v==missingPointValue)) return {dis:"Missing all required points", action:"Add a site meter and the required points"}

 

   //define col meta options on points

-  if (steam_intervalCons!=null and steam_intervalCons!=missingValue) steam_intervalCons= steam_intervalCons                               .merge({dis:"Steam Consumption", chartGroup:"steam", color:"slateBlue"})

-  if (steam_intervalModelCons!=null and steam_intervalModelCons!=missingValue) steam_intervalModelCons=    steam_intervalModelCons        .merge({dis:"Steam Model", chartGroup:"steam", color:"slateBlue", strokeDasharray:"1,1"})

+  if (steam_intervalCons!=null and steam_intervalCons!=missingValue) steam_intervalCons= steam_intervalCons                               .merge({dis:"Steam Consumption", chartGroup:"steam", color:"purple"})

+  if (steam_intervalModelCons!=null and steam_intervalModelCons!=missingValue) steam_intervalModelCons=    steam_intervalModelCons        .merge({dis:"Steam Model", chartGroup:"steam", color:"purple", strokeDasharray:"1,1"})

   if (steam_intervalSavingsCons!=null and steam_intervalSavingsCons!=missingValue) steam_intervalSavingsCons=  steam_intervalSavingsCons  .merge({})

   if (steam_intervalSavingsCost!=null and steam_intervalSavingsCost!=missingValue) steam_intervalSavingsCost= steam_intervalSavingsCost   .merge({})

   if (steam_intervalCost!=null and steam_intervalCost!=missingValue) steam_intervalCost= steam_intervalCost                               .merge({})

 

-  if (steam_monthlyCons!=null and steam_monthlyCons!=missingValue) steam_monthlyCons= steam_monthlyCons                                   .merge({dis:"Steam Consumption", chartGroup:"steam", color:"slateBlue"})

-  if (steam_monthlyModelCons!=null and steam_monthlyModelCons!=missingValue) steam_monthlyModelCons= steam_monthlyModelCons               .merge({dis:"Steam Model", chartGroup:"steam", color:"slateBlue", strokeDasharray:"1,1"})

+  if (steam_monthlyCons!=null and steam_monthlyCons!=missingValue) steam_monthlyCons= steam_monthlyCons                                   .merge({dis:"Steam Consumption", chartGroup:"steam", color:"purple"})

+  if (steam_monthlyModelCons!=null and steam_monthlyModelCons!=missingValue) steam_monthlyModelCons= steam_monthlyModelCons               .merge({dis:"Steam Model", chartGroup:"steam", color:"purple", strokeDasharray:"1,1"})

   if (steam_monthlySavingsCons!=null and steam_monthlySavingsCons!=missingValue) steam_monthlySavingsCons= steam_monthlySavingsCons       .merge({})

   if (steam_monthlySavingsCost!=null and steam_monthlySavingsCost!=missingValue) steam_monthlySavingsCost= steam_monthlySavingsCost       .merge({})

   if (steam_monthlyCost!=null and steam_monthlyCost!=missingValue) steam_monthlyCost= steam_monthlyCost                                   .merge({})

@@ -73,11 +72,11 @@
 

 

   //get his

-  his_steam_intervalCons: try steam_intervalCons.hisRead(dates, {-limit}).renameCol("v0","steam_intervalCons") catch null

-  his_steam_intervalModelCons: try steam_intervalModelCons.hisRead(dates, {-limit}).renameCol("v0","steam_intervalModelCons") catch null

-  his_steam_intervalSavingsCons: try steam_intervalSavingsCons.hisRead(dates, {-limit}).hisRollup(avg, 1hr).renameCol("v0","steam_intervalSavingsCons") catch null

-  his_steam_intervalSavingsCost: try steam_intervalSavingsCost.hisRead(dates, {-limit}).renameCol("v0","steam_intervalSavingsCost") catch null

-  his_steam_intervalCost: try steam_intervalCost.hisRead(dates, {-limit}).renameCol("v0","steam_intervalCost") catch null

+  his_steam_intervalCons: try steam_intervalCons.hisRead(dates, {-limit}).hisRollup(avg,1hr).hisInterpolate().renameCol("v0","steam_intervalCons") catch null

+  his_steam_intervalModelCons: try steam_intervalModelCons.hisRead(dates, {-limit}).hisRollup(avg,1hr).hisInterpolate().renameCol("v0","steam_intervalModelCons") catch null

+  his_steam_intervalSavingsCons: try steam_intervalSavingsCons.hisRead(dates, {-limit}).hisRollup(avg, 1hr).hisInterpolate().renameCol("v0","steam_intervalSavingsCons") catch null

+  his_steam_intervalSavingsCost: try steam_intervalSavingsCost.hisRead(dates, {-limit}).hisRollup(avg,1hr).hisInterpolate().renameCol("v0","steam_intervalSavingsCost") catch null

+  his_steam_intervalCost: try steam_intervalCost.hisRead(dates, {-limit}).hisRollup(avg,1hr).hisInterpolate().renameCol("v0","steam_intervalCost") catch null

 

   his_steam_monthlyCons: try steam_monthlyCons.hisRead(dates, {-limit}).renameCol("v0","steam_monthlyCons") catch null

   his_steam_monthlyModelCons: try steam_monthlyModelCons.hisRead(dates, {-limit}).renameCol("v0","steam_monthlyModelCons") catch null

@@ -120,9 +119,9 @@
   if (debug=="his") return hisSummary

 

   //rollup data into summary values we can use quickly in widgets

-  rollup_steam_intervalCons: try his_steam_intervalCons.hisRollup(avg,1hr).hisRollup(sum,"*").first.get("steam_intervalCons") catch missingValue

-  rollup_steam_intervalModelCons:    try his_steam_intervalModelCons.hisRollup(avg,1hr).hisRollup(sum,"*").first.get("steam_intervalModelCons") catch missingValue

-  rollup_steam_intervalSavingsCons:  try his_steam_intervalSavingsCons.hisRollup(avg,1hr).hisRollup(sum,"*").first.get("steam_intervalSavingsCons") catch missingValue

+  rollup_steam_intervalCons: try his_steam_intervalCons.hisRollup(avg,1hr)/*.hisRollup(sum,"*")*/.first.get("steam_intervalCons") catch missingValue

+  rollup_steam_intervalModelCons:    try his_steam_intervalModelCons.hisRollup(avg,1hr)/*.hisRollup(sum,"*")*/.first.get("steam_intervalModelCons") catch missingValue

+  rollup_steam_intervalSavingsCons:  try his_steam_intervalSavingsCons.hisRollup(avg,1hr)/*.hisRollup(sum,"*")*/.first.get("steam_intervalSavingsCons") catch missingValue

   rollup_steam_intervalSavingsCost:   try his_steam_intervalSavingsCost.hisRollup(sum,"*").first.get("steam_intervalSavingsCost") catch missingValue

   rollup_steam_intervalCost:   try his_steam_intervalCost.hisRollup(sum,"*").first.get("steam_intervalCost") catch missingValue

 

getUtilityMeterSummary
--- getUtilityMeterSummary (pod)
+++ getUtilityMeterSummary (local)
@@ -1,7 +1,6 @@
 (site, dates, opts:{}) => do

 

   //options

-  dates = dates.toSpan

 

   //get elec data

   elec: getElecMeterSummary(site, dates, opts)

@@ -9,14 +8,6 @@
   elecMeta: elec.meta

   //elec = elec.hisMap(v=>v.to("kW"))

   combined: elec.addMeta({elecMeta:elecMeta})

-

-  //get Gas data

-  gas: getGasMeterSummary(site, dates, opts)

-  if (not gas.isDict) do

-    gasMeta: gas.meta

-    //hw = hw.hisMap(v=>v.to("kBTU/h"))

-    combined = hisJoin([combined, gas]).addMeta({gasMeta:gasMeta})

-  end

 

   //get Hot Water data

   hw: getHWMeterSummary(site, dates, opts)

logic_ahu_ahuCoolingLoad
--- logic_ahu_ahuCoolingLoad (pod)
+++ logic_ahu_ahuCoolingLoad (local)
@@ -6,14 +6,12 @@
   //normalize inputs

   ahuRec: ahu.toRec

   ahuId: ahuRec->id

-  dates = dates.toSpan

 

   //opts

   rollupOverride: try if (opts.get("rollupOverride").isNull) null else if (opts.get("rollupOverride").isStr) opts.get("rollupOverride").parseNumber else if (opts.get("rollupOverride").isNumber) opts.get("rollupOverride") else null catch null

   if (rollupOverride.isNull and (dates.end - dates.start).to(1hr)<=25hr) rollupOverride=1hr

   constant: if (opts.has("constant")) opts.get("constant") else 1

   debug: opts.has("debug") and opts.get("debug")!="Disable"

-  occDisp: opts.optNorm("occDisp", false)

 

   //get points

   requiredPoints: ["mat_sensor","dat_sensor"]

@@ -31,18 +29,13 @@
   if (vavFlowPoints.size==0 and not points.get("daFlow_sensor").isRef) return "No vav flow points"

 

   //get his

-  if(occDisp) hisDict: points.retrieveHis(dates, {})

-  else hisDict: points.retrieveHis(dates, {rollupOverride:rollupOverride})

+  hisDict: points.retrieveHis(dates, {rollupOverride:rollupOverride})

   if (debug and opts.get("debug")=="His") return hisDict

   if (hisDict.has("err")) throw {dis:hisDict.get("err").size+" errors", errList:hisDict.get("err")}

-  his: try do

-        if(occDisp) hisDict.vals.findAll(v=>v.isGrid).hisJoin.hisFindInPeriods(logic_ahu_occPeriods(ahuId, dates, {}, {})).hisRollupAuto(rollupOverride)

-        else hisDict.vals.findAll(v=>v.isGrid).hisJoin

-       end

+  his: try hisDict.vals.findAll(v=>v.isGrid).hisJoin

        catch return "no his"

 

-  if(occDisp) vavFlowPointsHis: vavFlowPoints.hisRead(dates,{-limit}).hisFindInPeriods(logic_ahu_occPeriods(ahuId, dates, {}, {})).hisRollupAuto(rollupOverride, his=>avg)

-  else vavFlowPointsHis: vavFlowPoints.hisRead(dates,{-limit}).hisRollupAuto(rollupOverride, his=>avg)

+  vavFlowPointsHis: vavFlowPoints.hisRead(dates,{-limit}).hisRollupAuto(rollupOverride, his=>avg)

   if (vavFlowPointsHis.hisClip.size==0) "No vav flow his"

   else vavFlowPointsHis=vavFlowPointsHis.hisFoldCols(sum).renameCol("v0","flow")

 

logic_chw_supTempSpReset
--- logic_chw_supTempSpReset (pod)
+++ logic_chw_supTempSpReset (local)
@@ -44,6 +44,10 @@
 

   //points

   points: buildingLoopDigest(equipId, opts)

+  plantId: readById(points.get("buildingLoop").first.get("chw_supply_temp"))->equipRef->equipRef.nestedEquips.find(e=>e.get("navName").contains("Primary"))->id

+  priVlvCmd: try read(primary and cmd and valve and equipRef == plantId) catch null

+  priVlvPos: try read(primary and valve and position and equipRef == plantId) catch null

+

   try chw_supply_temp_sp: points.get("buildingLoop").first.get("chw_supply_temp_sp") catch return blankChart("No Chilled Water Loops Present")

   try chw_supply_temp: points.get("buildingLoop").first.get("chw_supply_temp") catch return blankChart("No Chilled Water Loops Present")

   points ={chw_supply_temp_sp:chw_supply_temp_sp, chw_supply_temp:chw_supply_temp}

@@ -58,6 +62,10 @@
   if (hisDict.has("err")) return "missingHis"

   his: try hisDict.vals.findAll(v=>v.isGrid).hisJoin

        catch return "missingHis"

+

+

+  priVlvCmdHis: try priVlvCmd.hisRead(dates) catch null

+  priVlvPosHis: try priVlvPos.hisRead(dates) catch null

 

   //look back in time to find min/max

   searchStart: dates.start - lookBack

@@ -106,7 +114,7 @@
          .addColMetaClean("chw_supply_temp",{chartGroup:"supTemp"})

 

   out = out.removeCol("delta")

+  out = [out, priVlvCmdHis, priVlvPosHis].hisJoin

 

   return out.addMeta(outMeta)

-

 end
logic_chws_deltaT
--- logic_chws_deltaT (pod)
+++ logic_chws_deltaT (local)
@@ -54,12 +54,12 @@
   bLoopId: buildingLoopDigest(targetId, opts)

 

   // Call Logic Func

-  pumpRunningHis: logic_chws_equipsRunning(bLoopId, dates, {equip:"pump"})

+  pumpRunningHis: if (opts.get("secondaryLoopOverride") != "null") logic_chws_equipsRunning(bLoopId, dates, {equip:"pump"}) else null

 

   // Handle edge-case

   if (pumpRunningHis.isStr) return pumpRunningHis

 

-  pumpRunningHis = pumpRunningHis.keepCols(["ts", "anyEquipRunning"]).findAll(r => r.get("anyEquipRunning").isNonNull)

+  pumpRunningHis = try pumpRunningHis.keepCols(["ts", "anyEquipRunning"]).findAll(r => r.get("anyEquipRunning").isNonNull) catch null

 

   if(pumpRunningHis.isNonNull) his = his.hisFindInPeriods(pumpRunningHis)

 

logic_chws_energyData
--- logic_chws_energyData (pod)
+++ logic_chws_energyData (local)
@@ -14,94 +14,26 @@
 *     Thomas Kuhrke Limia    8/02/2024   - Initial Creation

 *     Thomas Kuhrke Limia    10/8/2024   - Passed opts into chwsDigest function call (Line 30)

 */

-(target, dates, opts : {}) => do

-

-  // Handle Opts

-  quickModeUsage  : opts.get("quickModeUsage") == true

-  quickModeDemand : opts.get("quickModeDemand") == true

+(target, date, opts : {}) => do

 

   // Normalize Inputs

   targetRec: target.toRec

   targetId: targetRec->id

   site: targetRec.get("siteRef")

-  dates = dates.toSpan()

+  date = date.toSpan()

 

   // Retrieve points

-  points: chwsDigest(targetId, opts)

-

-  // Retrieve loops from logic.

-  primaryLoop  : points.get("primaryLoop").toGrid

-  secondaryLoop: points.get("secondaryLoop").toGrid

-  condenserLoop: points.get("condenserLoop").toGrid

-

-  // Edge-case where no loop has equip data.

-  if (primaryLoop.isStr and secondaryLoop.isStr and condenserLoop.isStr) return "No equips in any existing loop."

-

-  loops: [primaryLoop, secondaryLoop, condenserLoop]

-  equipPowerPoints: []

-  loops.each() (loop) => do

-    if (loop.isGrid) do

-      // Retrieve all possible equip list from loop

-      chillers      : loop.first.get("chillers")

-      pumps         : loop.first.get("pumps")

-      coolingTowers : loop.first.get("coolingTowers")

-

-      // If chillers are present, retrieve its power points if they exist

-      if (chillers.isGrid) do

-        chillerPowerPoints: chillers.colToList("points").map(v => v.get("chlr_power")).findAll(v => v.isRef)

-        if (not chillerPowerPoints.isEmpty) equipPowerPoints = equipPowerPoints.add(chillerPowerPoints)

-      end

-

-      // If pumps are present, rertrieve its power points if they exist.

-      if (pumps.isGrid) do

-        priPowerPoints: pumps.colToList("points").map(v => v.get("pri_pump_power")).findAll(v => v.isRef)

-        secPowerPoints: pumps.colToList("points").map(v => v.get("sec_pump_power")).findAll(v => v.isRef)

-        if (not priPowerPoints.isEmpty) equipPowerPoints = equipPowerPoints.add(priPowerPoints)

-        if (not secPowerPoints.isEmpty) equipPowerPoints = equipPowerPoints.add(secPowerPoints)

-      end

-

-      // If cooling towers are present, retrieve its power points if they exist

-      if (coolingTowers.isGrid) do

-        ctPowerPoints: coolingTowers.colToList("points").map(v => v.get("clgtwr_power")).findAll(v => v.isRef)

-        if (not ctPowerPoints.isEmpty) equipPowerPoints = equipPowerPoints.add(ctPowerPoints)

-      end

-    end

-  end

-  equipPowerPoints = equipPowerPoints.flatten

+  points: try "chilledWater_intervalCons".pointQuery({read, siteRef: site}) catch null

 

   // Handle Edge-Case where no equip power points where found, but equips were found.

-  if (equipPowerPoints.isEmpty) return "No power point in any of the equips of the system."

+  if (points.isNull or points.isEmpty) return "No energy point on the system."

 

   // Perform a hisRead on all the points

-  equipPowerHis: equipPowerPoints.hisRead(dates, {-limit}).hisInterpolate.hisClip

+  his : points.hisRead(date, {-limit}).hisInterpolate.hisClip

 

   // Edge-case for when the points exist, but have no his data for the given dates.

-  if (equipPowerHis.isEmpty) return "No his data found"

+  if (his.isEmpty) return "No his data found"

 

-  // Calculate the system demand and usage.

-  systemDemand : equipPowerHis.hisFoldCols(sum).renameCol("v0", "systemDemand")

-  systemUsage  : systemDemand.hisRollup(avg, 1h).hisMap(v => v.as(1kWh)).renameCol("systemDemand", "systemUsage")

+  return his

 

-  // QuickMode

-  if (quickModeUsage)  return systemUsage

-  if (quickModeDemand) return systemDemand

-

-  // Combine grids.

-  his : hisJoin([systemUsage, equipPowerHis, systemDemand])

-

-  // CHWS 2.0 Preparation

-  funcName: "logic_chws_energyData"

-  titles: logicFuncTitle(funcName, targetRec, dates)

-  outputMeta: {title:titles[0],

-              subtitle: titles[1],

-              funcName: funcName,

-              }

-

-  // Add Meta

-  his = his.stream

-           .addColMeta("systemUsage",  {chartGroup: "usage", dis: "Usage (kWh)", subtitle: "System Usage", color: "blue"})

-           .addColMeta("systemDemand", {chartGroup: "demand", dis: "Demand (kW)", subtitle: "System Demand", color:"green"})

-           .addMeta(outputMeta)

-           .collect

-  return his

 end
logic_chws_equipSummary
--- logic_chws_equipSummary (pod)
+++ logic_chws_equipSummary (local)
@@ -43,7 +43,7 @@
 

   // Retrieve condenser loop (added check to use the override with priority)

   condLoop: if (opts.get("condenserLoopOverride").isRef) opts.get("condenserLoopOverride")

-            else try   read(chilled and equip and water and condenserLoop and output and equipRef == chws)->id

+            else try   read(condenser and equip and water and condenserLoop and output and equipRef == chws)->id  //ADDED (condenser, removed chilled)

                  catch null

   if (opts.get("equip") == "cdwp" and condLoop.isNull) return "No Condenser Loop Available"

 

@@ -54,8 +54,8 @@
   targets: if      (opts.get("equip") == "chlr") try chillersDigest(primaryLoop).colToList("equip")

                                                  catch (err) return "No Chillers Available"

            else if (opts.get("equip") == "chwp") try do

-                                                   list1: pumpsDigest(primaryLoop).colToList("equip")

-                                                   list2: pumpsDigest(secondaryLoop).colToList("equip")

+                                                   list1: try pumpsDigest(primaryLoop).colToList("equip")   catch (err) //ADDED (try catch)

+                                                   list2: try pumpsDigest(secondaryLoop).colToList("equip") catch (err) //ADDED (try catch)

                                                    [list1, list2].flatten

                                                  end

                                                  catch (err) return "No Chilled Water Loop Pumps Available"

@@ -113,12 +113,20 @@
       val : if      (res.isNull or res.isEmpty)     "missing"

             else if (res.isDict and res.has("max")) ((res.get("max")*10).round)/10

             else                                    ((res.get(res.names.get(0))*10).round)/10

-      if   (incompleteCache and valueSource == "kpi" and val != "missing")  return {kpi: func(curFunc).get("dis"), value : val.toStr + " (Incomplete Cache)"}

-      else                                                                  return {kpi: func(curFunc).get("dis"), value : val}

+      if   (incompleteCache and valueSource == "kpi" and val != "missing")  return {kpi: func(curFunc).get("dis"), value : val.toStr + " (Incomplete Cache)", help: func(curFunc).get("help")} //ADDED (help tag/val)

+      else                                                                  return {kpi: func(curFunc).get("dis"), value : val, help: func(curFunc).get("help")} //ADDED (help tag/val)

     end).toGrid

     out = try out.addColMeta("value", {dis: target.toRec.get("navName"), enum:{missing: {icon:"x", iconColor:"#FF0000", dis:""}}}).renameCol("value", "v" + res.size) catch out

   res = res.add(out)

   end

+

+  //ADDED start

+

+  help: res.first.keepCols(["kpi", "help"])

+

+  res = res.map(g => return g.removeCol("help")).add(help)

+

+  //ADDED end

 

   // Join all equips

   out : res.joinAll("kpi")

logic_chws_equipsRunning

Sources are identical.

logic_chws_systemSummary
--- logic_chws_systemSummary (pod)
+++ logic_chws_systemSummary (local)
@@ -33,22 +33,22 @@
   target: targets.toRec.get("id")

 

   // KPI Funcs

-  funcs: [kpi_chws_kWTonAvg,

+  funcs: [//kpi_chws_kWTonAvg,

           kpi_chws_totalSystemEnergyUse,

           kpi_chws_systemPeakDemand,

-          kpi_chws_avgDeltaT,

+          kpi_chws_avgMeterDeltaT,

           kpi_chws_avgCHWSupplySetpoint,

+          kpi_chwp_percentRuntime_chws_dashboard,

           kpi_chws_avgSystemFlow,

-          kpi_chws_primaryLoop_percentRuntimeOnePump,

-          kpi_chws_primaryLoop_percentRuntimeMultiplePumps,

+          kpi_chws_primaryLoopDeltaT,

+          kpi_chws_avgCHWSupplyTempPrimaryLoop,

+          //kpi_chws_primaryLoop_percentRuntimeOnePump,

+          //kpi_chws_primaryLoop_percentRuntimeMultiplePumps,

+          kpi_chws_secondaryLoopDeltaT,

+          kpi_chws_avgCHWSupplyTempSecondaryLoop,

           kpi_chws_secondaryLoop_percentRuntimeOnePump,

           kpi_chws_secondaryLoop_percentRuntimeMultiplePumps,

-          kpi_chws_condenserLoop_percentRuntimeOnePump,

-          kpi_chws_condenserLoop_percentRuntimeMultiplePumps,

-          kpi_chws_condenserLoop_percentRuntimeOneTower,

-          kpi_chws_condenserLoop_percentRuntimeMultipleTowers,

-          kpi_chws_primaryLoop_percentRuntimeOneChiller,

-          kpi_chws_primaryLoop_percentRuntimeMultipleChillers

+          kpi_chwp_averagePumpSpeedSecLoop,

           ]

 

   // Create Output (NOTE: Add support for retrieving "min" values by checking the funcs displayName)

@@ -61,9 +61,9 @@
     val : if      (res.isNull or res.isEmpty)     "missing"

           else if (res.isDict and res.has("max")) ((res.get("max")*10).round)/10

           else                                    ((res.get(res.names.get(0))*10).round)/10

-    if   (incompleteCache and valueSource == "kpi" and val != "missing")  return {kpi: func(curFunc).get("dis"), value : val.toStr + " (Incomplete Cache)"}

-    else                                                                  return {kpi: func(curFunc).get("dis"), value : val}

-  end).toGrid

+    if   (incompleteCache and valueSource == "kpi" and val != "missing")  return {kpi: func(curFunc).get("dis"), value : val.toStr + " (Incomplete Cache)", help: func(curFunc).get("help")} //ADDED (help tag/val)

+    else                                                                  return {kpi: func(curFunc).get("dis"), value : val, help: func(curFunc).get("help")} //ADDED (help tag/val)

+  end).toGrid.reorderKeepCols(["kpi", "value", "help"])

 

   // Empty Values Opt

   if (hideEmptyValues) out = out.findAll(r => not r.rowToList[1..-1].all(v => v=="missing"))

@@ -71,7 +71,7 @@
   // Add Meta

   out = out.stream

            .addColMeta("kpi", {dis: "KPI"})

-           .addColMeta("value", {dis: "System", enum:{missing: {icon:"x", iconColor:"#FF0000", dis:""}}})

+           .addColMeta("value", {dis: "Values", enum:{missing: {icon:"x", iconColor:"#FF0000", dis:""}}})

            .collect()

   return out

 end
logic_cwp_pumpIsRunning
--- logic_cwp_pumpIsRunning (pod)
+++ logic_cwp_pumpIsRunning (local)
@@ -41,7 +41,7 @@
   existing : if      (not (pri_pump_speed.isStr  and sec_pump_speed.isStr))  [pri_pump_speed, sec_pump_speed].find(v => not v.isStr)

              else if (not (pri_pump_power.isStr  and sec_pump_power.isStr))  [pri_pump_power, sec_pump_power].find(v => not v.isStr)

              else if (not (pri_pump_status.isStr and sec_pump_status.isStr)) [pri_pump_status, sec_pump_status].find(v => not v.isStr)

-             else "All points doesn't exist, or the existing ones have no his."

+             else "All points don't exist, or the existing ones have no his."

 

   // Handle edge case where there are no existing points

   if (existing.isStr) return existing

@@ -53,7 +53,7 @@
   if (his.isEmpty) return "No his data"

 

   // Transform number hisGrid into boolean

-  if (his.first.get("v0").isNumber) his = his.hisRollupAuto().map(r => return {ts: r.get("ts"), v0: r.get("v0") > 1})

+  if (his.first.get("v0").isNumber) his = his.map(r => return {ts: r.get("ts"), v0: r.get("v0") > 1})

 

   // Return boolean grid if opt is present

   if (boolPeriods) return his

logic_hhw_deltaT
--- logic_hhw_deltaT (pod)
+++ logic_hhw_deltaT (local)
@@ -51,8 +51,8 @@
   //Filter for when any building loop pump is running

   opts = opts.set("id", true)

   bLoopId: logic_hws_buildingLoopDigest(targetId, opts)

-  pumpRunningHis: logic_hhws_equipsRunning(bLoopId, dates, {equip:"pump"}).keepCols(["ts", "anyEquipRunning"])

-                                                                          .findAll(r => r.get("anyEquipRunning").isNonNull)

+  pumpRunningHis: try logic_hhws_equipsRunning(bLoopId, dates, {equip:"pump"}).keepCols(["ts", "anyEquipRunning"])

+                                                                          .findAll(r => r.get("anyEquipRunning").isNonNull) catch null //ADDED try catch

   if(pumpRunningHis.isNonNull) his = his.hisFindInPeriods(pumpRunningHis)

 

   his = his.map() r => if(r.get("delta") < 0) r = r.set("delta", abs(r.get("delta"))) else r = r

logic_hhwp_pumpIsRunning
--- logic_hhwp_pumpIsRunning (pod)
+++ logic_hhwp_pumpIsRunning (local)
@@ -52,7 +52,7 @@
   if (his.isEmpty) return "No his data"

 

   // Transform number hisGrid into boolean

-  if (his.first.get("v0").isNumber) his = his.hisRollupAuto().map(r => return {ts: r.get("ts"), v0: r.get("v0") > 1})

+  if (his.first.get("v0").isNumber) his = his.map(r => return {ts: r.get("ts"), v0: r.get("v0") > 1})

 

   // Return boolean grid if opt is present

   if (boolPeriods) return his.renameCol("v0", "running")

@@ -61,7 +61,7 @@
   his = his.conditionToPeriods("running", r => r.get("v0") == true).addColMeta("v0", his.col("v0").meta)

 

   // CHWS 2.0 Preparation

-  funcName: "logic_cwp_pumpIsRunning"

+  funcName: "logic_hhwp_pumpIsRunning"

   titles: logicFuncTitle(funcName, targetRec, dates)

   outputMeta: {title:titles[0],

               subtitle: titles[1],

logic_hhws_equipsRunning

Sources are identical.

logic_hhws_systemSummary
--- logic_hhws_systemSummary (pod)
+++ logic_hhws_systemSummary (local)
@@ -36,24 +36,24 @@
 

   // KPI Funcs

   funcs: [kpi_hws_percentTimeDPAtSetpoint,

-          kpi_hhws_systemPeakElectricDemand,

-          kpi_hhws_peakSystemEnergyUse,

+          //kpi_hhws_systemPeakElectricDemand,

+          //kpi_hhws_peakSystemEnergyUse,

           kpi_hhws_totalSystemEnergyUse,

           kpi_hhw_avgDeltaT,

           kpi_hhw_percentTimeLowDeltaT,

-          kpi_hhw_avgBypassValvePosition,

+          //kpi_hhw_avgBypassValvePosition,

           kpi_hhw_avgHHWReturnTemp,

           kpi_hhw_avgHHWSupplySetpoint,

           kpi_hhw_avgHHWSupplyTemp,

           kpi_hhw_avgHHWSupplyFlow,

           kpi_hhw_percentTimeSupTempAtSetpoint,

           kpi_hws_percentTimeDPAtSetpoint,

-          kpi_hhws_primaryLoop_percentRuntimeOnePump,

-          kpi_hhws_primaryLoop_percentRuntimeMultiplePumps,

+          //kpi_hhws_primaryLoop_percentRuntimeOnePump,

+          //kpi_hhws_primaryLoop_percentRuntimeMultiplePumps,

           kpi_hhws_secondaryLoop_percentRuntimeOnePump,

           kpi_hhws_secondaryLoop_percentRuntimeMultiplePumps,

-          kpi_hhws_primaryLoop_percentRuntimeOneBlr,

-          kpi_hhws_primaryLoop_percentRuntimeMultipleBlrs

+          //kpi_hhws_primaryLoop_percentRuntimeOneBlr,

+          //kpi_hhws_primaryLoop_percentRuntimeMultipleBlrs

           ]

 

   // Create Output (NOTE: Add support for retrieving "min" values by checking the funcs displayName)

@@ -61,7 +61,7 @@
     cacheResSize    : try ruleKpis(target, dates, read(rule and ruleFunc == curFunc.get("name")).colToList("date")) catch null

     incompleteCache : cacheResSize != date.numDays

     engineRes: if (valueSource == "auto" or valueSource == "kpi") betterRuleKpis(curFunc, target, date) else null// Read rule cache

-    res : if (valueSource != "kpi" and (engineRes.isNull or valueSource == "call" or incompleteCache)) call(curFunc, [{target: target, date: date, opts: opts}]).get("out")

+    res : if (valueSource != "kpi" and (engineRes.isNull or valueSource == "call" or incompleteCache)) try call(curFunc, [{target: target, date: date, opts: opts}]).get("out") catch null //ADDED try catch

           else engineRes // Call the function regularly if the cache had no info.

     val : if      (res.isNull or res.isEmpty)     "missing"

           else if (res.isDict and res.has("max")) ((res.get("max")*10).round)/10

pumpDigest

Sources are identical.

wdg_ahu_table_summaryAnalytics
--- wdg_ahu_table_summaryAnalytics (pod)
+++ wdg_ahu_table_summaryAnalytics (local)
@@ -76,10 +76,12 @@
                        "hwValveOpenHot",

                        "avgChwValve",

                        "chwValveOpenCold",

+                       "avgDaFlow",

                        "simultaneousHeatingCooling",

                        "occupied247",

                        "sparks",

-                       "economizingProperly"

+                       "economizingProperly",

+                       "occHours"

                        ]

 

   //calculate total hours

@@ -87,14 +89,14 @@
 

   //AHU LOOP

   units: readAll((mau or ahu or rtu) and equip and siteRef==site->id and not ahuSummaryNoShow)

-  table: units.map row => do

+  table: units.map() (row,idx) => do

 

     //normalize input

     id: row->id

 

     //get points

     requiredPoints: []

-    optionalPoints: ["occ_cmd","daFan_run","daFan_speed","dat_sensor","dat_sp","dsp_sensor", "dsp_sp", "clgValve_cmd","htgValve_cmd","phtValve_cmd","daFan_speed","rtnFan_speed","rlfFan_speed","exhFan_speed","siteOcc_cmd"]

+    optionalPoints: ["occ_cmd","daFan_run","daFan_speed","dat_sensor","dat_sp","dsp_sensor", "dsp_sp", "clgValve_cmd","htgValve_cmd","phtValve_cmd","daFlow_sensor","daFan_speed","rtnFan_speed","rlfFan_speed","exhFan_speed","siteOcc_cmd"]

     points: retrievePoints(requiredPoints, optionalPoints, {equipRef:id})

     if (debug and opts.get("debug")=="Points") return points

     if (not points.get("htgValve_cmd").isRef and points.get("phtValve_cmd").isRef) points=points.set("htgValve_cmd",points.get("phtValve_cmd"))

@@ -150,6 +152,13 @@
                    else points.get("clgValve_cmd")

                    catch (err) err.get("dis")

                    if (not debug and avgChwValve.isStr) avgChwValve=noDataValue

+

+    avgDaFlow:    try

+                    if (points.get("daFlow_sensor").isRef)

+                     logic_ahu_pointRollup(points.get("daFlow_sensor"), dates, avg, curOpts)

+                   else points.get("daFlow_sensor")

+                   catch (err) err.get("dis")

+                   if (not debug and avgChwValve.isStr) avgDaFlow=noDataValue

 

     hwValveOpenHot: try

                     if (points.get("htgValve_cmd").isRef)

@@ -241,12 +250,16 @@
 

     //put together dict

     analytics: {id: id,

+                order: row.get("order"),

                 avgSupplyFan:              if (avgSupplyFan.isNumber) avgSupplyFan.round else avgSupplyFan,

                 avgReturnFan:              if (avgReturnFan.isNumber) avgReturnFan.round else avgReturnFan,

                 avgReliefFan:              if (avgReliefFan.isNumber) avgReliefFan.round else avgReliefFan,

                 avgExhaustFan:             if (avgExhaustFan.isNumber) avgExhaustFan.round else avgExhaustFan,

                 avgHwValve:                if (avgHwValve.isNumber) avgHwValve.round else avgHwValve,

                 avgChwValve:               if (avgChwValve.isNumber) avgChwValve.round else avgChwValve,

+

+                avgDaFlow:                 if (avgDaFlow.isNumber) avgDaFlow.round else avgDaFlow,

+

                 hwValveOpenHot:            if (hwValveOpenHot.isNumber) hwValveOpenHot.round else hwValveOpenHot,

                 chwValveOpenCold:          if (chwValveOpenCold.isNumber) chwValveOpenCold.round else chwValveOpenCold,

                 simultaneousHeatingCooling:if (simultaneousHeatingCooling.isNumber) simultaneousHeatingCooling.round else simultaneousHeatingCooling,

@@ -259,6 +272,7 @@
                 occHours:                  if (occHours.isNumber) occHours.round else occHours,

                 occupied247:               if (occupied247.isStr) occupied247 else "Couldn't Calculate",

                 economizingProperly:       if (economizingProperly.isNumber) economizingProperly.round else economizingProperly,

+                mod:now()

                 }

 

   end

@@ -268,31 +282,36 @@
 

 out: table.toGrid

           .stream

-          .addColMetaClean("id", {dis:"id"})

-          .addColMetaClean("avgSupplyFan"    ,{dis:"Avg Supply Fan Spd", color:fanColor, viz:"barCell"})

-          .addColMetaClean("avgReturnFan"    ,{dis:"Avg Rtn Fan Spd",    color:fanColor, viz:"barCell"})

-          .addColMetaClean("avgReliefFan"    ,{dis:"Avg Rlf Fan Spd",    color:fanColor, viz:"barCell"})

-          .addColMetaClean("avgExhaustFan"   ,{dis:"Avg Exh Fan Spd",    color:fanColor, viz:"barCell"})

-          .addColMetaClean("avgHwValve"      ,{dis:"Avg Heating Demand",   color:hwColor, viz:"barCell"})

-          .addColMetaClean("avgChwValve"     ,{dis:"Avg Cooling Demand",  color:chwColor, viz:"barCell"})

-          .addColMetaClean("hwValveOpenHot"  ,{dis:"Heating While > "+hotTemp+"F",  color:hwColor, viz:"barCell"})

-          .addColMetaClean("chwValveOpenCold",{dis:"Cooling While < "+coldTemp+"F", color:chwColor, viz:"barCell"})

+          .addColMetaClean("id", {dis:"AHU"})

+          .addColMetaClean("avgSupplyFan"    ,{dis:"Avg Supply Fan Spd", color:fanColor, viz:"barCell", chartMax:100%,})

+          .addColMetaClean("avgReturnFan"    ,{dis:"Avg Rtn Fan Spd",    color:fanColor, viz:"barCell", chartMax:100%,})

+          .addColMetaClean("avgReliefFan"    ,{dis:"Avg Rlf Fan Spd",    color:fanColor, viz:"barCell", chartMax:100%,})

+          .addColMetaClean("avgExhaustFan"   ,{dis:"Avg Exh Fan Spd",    color:fanColor, viz:"barCell", chartMax:100%,})

+          .addColMetaClean("avgHwValve"      ,{dis:"Avg Heating Demand",   color:hwColor, viz:"barCell", chartMax:100%,})

+          .addColMetaClean("avgChwValve"     ,{dis:"Avg Cooling Demand",  color:chwColor, viz:"barCell", chartMax:100%,})

+

+          .addColMetaClean("avgDaFlow"       ,{dis:"Avg Discharge Flow"})//,  color:chwColor, viz:"barCell", chartMax:100%,})

+

+          .addColMetaClean("hwValveOpenHot"  ,{dis:"Heating While > "+hotTemp+"F",  color:hwColor, viz:"barCell", chartMax:100%,})

+          .addColMetaClean("chwValveOpenCold",{dis:"Cooling While < "+coldTemp+"F", color:chwColor, viz:"barCell", chartMax:100%,})

           .addColMetaClean("simultaneousHeatingCooling"    ,{dis:"Simult Heat/Cool",   color:sparkColor, viz:"barCell", chartMax:hours})

           .addColMetaClean("datResetting"    ,{dis:"DAT Resetting",      color:"", viz:"barCell", })

           .addColMetaClean("dspResetting"    ,{dis:"DSP Resetting",      color:"", viz:"barCell", })

-          .addColMetaClean("datMeetingSp"    ,{dis:"DAT Meeting Sp",     color:meetingSpColor, viz:"barCell"})

-          .addColMetaClean("dspMeetingSp"    ,{dis:"DSP Meeting Sp",     color:meetingSpColor, viz:"barCell"})

-          .addColMetaClean("supplyFanRuntime",{dis:"Supply Fan Runtime", color:fanColor, viz:"barCell"})

+          .addColMetaClean("datMeetingSp"    ,{dis:"DAT Meeting Sp",     color:meetingSpColor, viz:"barCell", chartMax:100%})

+          .addColMetaClean("dspMeetingSp"    ,{dis:"DSP Meeting Sp",     color:meetingSpColor, viz:"barCell", chartMax:100%})

+          .addColMetaClean("supplyFanRuntime",{dis:"Supply Fan Runtime", color:fanColor, viz:"barCell", chartMax:100%})

           .addColMetaClean("sparks"          ,{dis:"Total Sparks",       color:sparkColor, viz:"barCell", })

           .addColMetaClean("occHours"        ,{dis:"Occupied Hours",     })

           .addColMetaClean("occupied247"     ,{dis:"Zones Occupied 24/7",     })

-          .addColMetaClean("economizingProperly",{dis:"Economizing Properly", color:meetingSpColor, viz:"barCell"})

+          .addColMetaClean("economizingProperly",{dis:"Economizing Properly", color:meetingSpColor, viz:"barCell", chartMax:100%})

+          .addColMetaClean("mod",{hidden})

           .collect(toGrid)

           .reorderKeepCols(colOrder)

           .sort("id")

+          .sort("order")

 

   //apply sorting

-  ahuTableSortBy.split(",").each(c => if (c.startsWith("r_")) out=out.sortr(c.replace("r_","")) else out=out.sort(c))

+  //ahuTableSortBy.split(",").each(c => if (c.startsWith("r_")) out=out.sortr(c.replace("r_","")) else out=out.sort(c))

 

 

   //add prez

wdg_ahu_widgetSelector
--- wdg_ahu_widgetSelector (pod)
+++ wdg_ahu_widgetSelector (local)
@@ -13,6 +13,7 @@
   if (widget=="Economizing")                  return wdg_ahu_chart_economizing(ahu, dates, opts)

   if (widget=="DAT/DSP Meeting Sp")           return wdg_ahu_chart_datDspMeetingSp(ahu, dates, opts)

   if (widget=="DAT/DSP Resets")               return wdg_ahu_chart_datDspResets(ahu, dates, opts)

+  if (widget=="DAF Sp Delta")                 return logic_ahu_flowSpDelta(ahu, dates, opts)

 

 //cards

 

wdg_chw_chart_supTempSpReset
--- wdg_chw_chart_supTempSpReset (pod)
+++ wdg_chw_chart_supTempSpReset (local)
@@ -16,7 +16,6 @@
         catch (err) return blankChart("Unable to calculate.", err.dis)

   if(data.isStr) return blankChart(data)

 

-

   //format

   out: data

 

wdg_chw_chart_temperaturesDeltaT
--- wdg_chw_chart_temperaturesDeltaT (pod)
+++ wdg_chw_chart_temperaturesDeltaT (local)
@@ -52,7 +52,7 @@
   // CHWS 2.0 Preparation

   funcName: "wdg_chw_chart_temperaturesDeltaT"

   titles: logicFuncTitle(funcName, equipRec, dates)

-  outputMeta: {title:titles[0],

+  outputMeta: {title:"Temperature ΔT",

               subtitle: titles[1],

               funcName: funcName,

               chartLegend:"hide"

wdg_chws_chart_chillerStaging
--- wdg_chws_chart_chillerStaging (pod)
+++ wdg_chws_chart_chillerStaging (local)
@@ -37,6 +37,7 @@
   // Replaced logic to support loop overrides.

   chillers: try chwsDigest(equipId, opts).get("primaryLoop").first.get("chillers")

             catch return blankChart("No Chillers in this Site")

+            if (chillers.isNull) return blankChart("No Chillers in this Site") // ADDED isNull CHECKING

 

   // Retrieve Temp Points

   tempPoints:[]

wdg_chws_table_systemSummary
--- wdg_chws_table_systemSummary (pod)
+++ wdg_chws_table_systemSummary (local)
@@ -32,7 +32,7 @@
   // Add Additional Meta

   out : grid.stream

             .addColMeta("val", {dis: "Info"})

-            .addMeta({title: "System"})

+            .addMeta({title: site->dis + " KPIs"})

             .collect

 

   return out.table

wdg_chws_widgetSelector
--- wdg_chws_widgetSelector (pod)
+++ wdg_chws_widgetSelector (local)
@@ -36,6 +36,9 @@
   if (widget == "Supply Temperature Setpoint Resetting")  return wdg_chw_chart_supTempSpReset(targetId, dates, opts)

   if (widget == "Temperature ΔT")                         return wdg_chw_chart_temperaturesDeltaT(targetId, dates, opts)

 

+  if (widget == "Building vs Plant ΔT")                    return wdg_chw_chart_bldgVsPlantDeltaT(targetId, dates, opts)

+  if (widget == "CHW Valve Position")      return wdg_chw_valves_chart(targetId, dates, opts)

+

   // Cards

   /* None for now*/

 

wdg_hhws_chart_pumpSpeeds

Sources are identical.

wdg_hhws_chart_temperaturesDeltaT
--- wdg_hhws_chart_temperaturesDeltaT (pod)
+++ wdg_hhws_chart_temperaturesDeltaT (local)
@@ -53,5 +53,5 @@
               }

 

   out: his.keepCols(["ts","delta"]).createHistogram(numBars,{}).addMeta(outputMeta)

-  return out.chart

+  return out.chart.addMeta({title:"HW System - Temperatures (ΔT)"})

 end
wdg_hhws_table_boilersSummary

Sources are identical.

wdg_hhws_table_hhwpSummary

Sources are identical.

wdg_hhws_table_systemSummary
--- wdg_hhws_table_systemSummary (pod)
+++ wdg_hhws_table_systemSummary (local)
@@ -32,7 +32,7 @@
   // Add Additional Meta

   out : grid.stream

             .addColMeta("val", {dis: "Info"})

-            .addMeta({title: "System"})

+            .addMeta({title: site->dis + " KPIs"})

             .collect

 

   return out.table

wdg_hhws_widgetSelector

Sources are identical.

wdg_portfolio_card_energy
--- wdg_portfolio_card_energy (pod)
+++ wdg_portfolio_card_energy (local)
@@ -1,7 +1,7 @@
 //todo add emissions data

 

 (dates, opts:{}) => do

-  dates = dates.toSpan

+

 

   //validate and authorize current user

   kwLinkValidate("analytics")

@@ -11,55 +11,121 @@
   debug: debugVal!="Disable" and debugVal!=null

   //debug:true

 

+  //Before caching

+  /*

   //get sites

-  grid: xq().xqProjs(navProjNames())

+  grid: xq().xqTimeout(5min).xqProjs(navProjNames())

             .xqDefine("dates",dates)

             .xqReadAll(site)

             .xqMap("site => try {site:site->id}.merge(getUtilityMeterSummary(site,dates).meta) catch (err) {site:site->id, err:err}")

             .xqExecute

+  */

 

-  totalSites: grid.size

-  errSites: grid.findAll(r=>r.has("err")).size

-  goodSites: grid.findAll(r=>r.missing("err")).size

+  //Get Cached records

+  startDate: dates.start

+  endDate: dates.end

+  grid: readAll(cache and cache_portfolio_energySummary and cacheYear=="all" and ts >=startDate and ts<= endDate)

+        //.findAll(r => r.get("ts") >= startDate and r.get("ts") <= endDate)

+        //.reorderKeepCols(["ts"])

+        .removeCols(["id", "cache", "cacheYear", "cache_portfolio_energySummary"])

+  gridCols: grid.colNames.moveTo("ts", 0)

+  grid = grid.reorderCols(gridCols)

+        //.renameCol("siteR", "site")

+  //return grid

+

+  //totalSites: grid.size

+  //errSites: grid.findAll(r=>r.has("err")).size

+  //goodSites: grid.findAll(r=>r.missing("err")).size

   if (debug=="Enable") return grid.reorderKeepCols(["site","err","elecMeta","gasMeta"])

-

-

+  //return grid

+  todaysDate: today()

+  card: {title:"Energy Summary"}

   //split elec and gas

-  elecGrid: grid.colToList("elecMeta").findAll(v=>v!=null).toGrid.map(r=>r.get("effectiveRollup"))

-  gasGrid: grid.colToList("gasMeta").findAll(v=>v!=null).toGrid.map(r=>r.get("effectiveRollup"))

-

-  if (debug=="Elec") return elecGrid

-  if (debug=="Gas")  return gasGrid

-

-  //split it up into new card

-  gas_usage_numbers:  gasGrid.colToList("gas_usage").findAll(v=>v.isNumber)

-  gas_usage: gas_usage_numbers.fold(sum)

-  gas_cost:   gasGrid.colToList("gas_cost").findAll(v=>v.isNumber).fold(sum)

-  gasMessage: errSites+" errs, "+ (goodSites - gas_usage_numbers.size)+" missing"

-

-  elec_usage_numbers:  elecGrid.colToList("elec_usage").findAll(v=>v.isNumber)

-  elec_usage: elec_usage_numbers.fold(sum)

-  elec_cost:   try elecGrid.colToList("elec_cost").findAll(v=>v.isNumber).fold(sum) catch "Costs unavailable"

-  elecMessage: errSites+" errs, "+ (goodSites - elec_usage_numbers.size)+" missing"

-

-  card: {title:"Energy Summary",

-         elecUse: elec_usage,

-         elecCost: elec_cost,

-         elecMessage:elecMessage,

-         gasUse: gas_usage,

-         gasCost: gas_cost,

-         gasMessage: gasMessage,

-         }

+  /*

+  if(grid.colNames.contains("elecMeta")) do

+    elecGrid: grid.colToList("elecMeta").findAll(v=>v!=null).toGrid.map(r=>r.get("effectiveRollup"))

+    elec_usage_numbers:  elecGrid.colToList("elec_usage").findAll(v=>v.isNumber)

+    elec_usage: elec_usage_numbers.fold(sum)

+    elec_cost:   try elecGrid.colToList("elec_cost").findAll(v=>v.isNumber).fold(sum) catch "Costs unavailable"

+    elecMessage: errSites+" errs, "+ (goodSites - elec_usage_numbers.size)+" missing"

+    card = card.merge({"elecUse": elec_usage, "elecCost": elec_cost/*, "elecMessage":elecMessage*/})

+  end

+  */

+  //return grid.colNames

+  elec_usage_col_names : grid.colNames.findAll(r => r.contains("elec_usage")).add("ts")

+  x: grid.keepCols(elec_usage_col_names)

+  elec_usage: try x.hisFoldCols(sum).colToList("v0").fold(sum) catch "Incompatible Units"

+  elec_cost_col_names : grid.colNames.findAll(r => r.contains("elec_cost")).add("ts")

+  y: grid.keepCols(elec_cost_col_names)

+  elec_cost: y.hisFoldCols(sum).colToList("v0").fold(sum)

+  card = card.merge({"elecUse": elec_usage, "elecCost": elec_cost/*, "elecMessage":elecMessage*/})

+ /*

+  if(grid.colNames.contains("hwMeta")) do

+    hwGrid: grid.colToList("hwMeta").findAll(v=>v!=null).toGrid.map(r=>r.get("effectiveRollup"))

+    hw_usage_numbers:  hwGrid.colToList("hw_usage").findAll(v=>v.isNumber).map(s => return s.to("MMBTU/h"))

+    hw_usage: hw_usage_numbers.fold(sum).as("MMBTU")

+    hw_cost:   try hwGrid.colToList("hw_cost").findAll(v=>v.isNumber).fold(sum) catch "Costs unavailable"

+    hwMessage: errSites+" errs, "+ (goodSites - hw_usage_numbers.size)+" missing"

+    card = card.merge({"hwUse": hw_usage, "hwCost": hw_cost/*, "hwMessage":hwMessage*/})

+  end

+  */

+  hw_usage_col_names : grid.colNames.findAll(r => r.contains("hw_usage")).add("ts")

+  x= grid.keepCols(hw_usage_col_names)

+  hw_usage: try x.hisFoldCols(sum).colToList("v0").fold(sum) catch "Incompatible Units"

+  hw_cost_col_names : grid.colNames.findAll(r => r.contains("hw_cost")).add("ts")

+  y= grid.keepCols(hw_cost_col_names)

+  hw_cost: y.hisFoldCols(sum).colToList("v0").fold(sum)

+  card = card.merge({"hwUse": hw_usage, "hwCost": hw_cost/*, "hwMessage":hwMessage*/})

+  /*

+  if(grid.colNames.contains("chwMeta")) do

+    chwGrid: grid.colToList("chwMeta").findAll(v=>v!=null).toGrid.map(r=>r.get("effectiveRollup"))

+    chw_usage_numbers:  chwGrid.colToList("chw_usage").findAll(v=>v.isNumber)

+    chw_usage: chw_usage_numbers.fold(sum)

+    chw_cost:   try chwGrid.colToList("chw_cost").findAll(v=>v.isNumber).fold(sum) catch "Costs unavailable"

+    chwMessage: errSites+" errs, "+ (goodSites - chw_usage_numbers.size)+" missing"

+    card = card.merge({"chwUse": chw_usage, "chwCost": chw_cost/*, "chwMessage":chwMessage*/})

+  end

+  */

+  chw_usage_col_names : grid.colNames.findAll(r => r.contains("chw_usage")).add("ts")

+  x= grid.keepCols(chw_usage_col_names)

+  chw_usage: try x.hisFoldCols(sum).colToList("v0").fold(sum) catch "Incompatible Units"

+  chw_cost_col_names : grid.colNames.findAll(r => r.contains("chw_cost")).add("ts")

+  y= grid.keepCols(chw_cost_col_names)

+  chw_cost: y.hisFoldCols(sum).colToList("v0").fold(sum)

+  card = card.merge({"chwUse": chw_usage, "chwCost": chw_cost/*, "hwMessage":hwMessage*/})

+  /*

+  if(grid.colNames.contains("steamMeta")) do

+    steamGrid: grid.colToList("steamMeta").findAll(v=>v!=null).toGrid.map(r=>r.get("effectiveRollup"))

+    steam_usage_numbers:  steamGrid.colToList("steam_usage").findAll(v=>v.isNumber).map(s => return s.to("klb/h"))

+    steam_usage: steam_usage_numbers.fold(sum).as("klb")

+    steam_cost:   try steamGrid.colToList("steam_cost").findAll(v=>v.isNumber).fold(sum) catch "Costs unavailable"

+    steamMessage: errSites+" errs, "+ (goodSites - steam_usage_numbers.size)+" missing"

+    card = card.merge({"steamUse": steam_usage, "steamCost": steam_cost/*, "steamMessage":steamMessage*/})

+  end

+  */

+  steam_usage_col_names : grid.colNames.findAll(r => r.contains("steam_usage")).add("ts")

+  x= grid.keepCols(steam_usage_col_names)

+  steam_usage: try x.hisFoldCols(sum).colToList("v0").fold(sum) catch "Incompatible Units"

+  steam_cost_col_names : grid.colNames.findAll(r => r.contains("steam_cost")).add("ts")

+  y= grid.keepCols(steam_cost_col_names)

+  steam_cost: y.hisFoldCols(sum).colToList("v0").fold(sum)

+  card = card.merge({"steamUse": steam_usage, "steamCost": steam_cost/*, "steamMessage":steamMessage*/})

 

   out: card.toGrid

-  try out=out.addColMeta("elecCost",       {viz:"barCell", color:"orange", dis: "Electricity Cost", }) catch null

-  try out=out.addColMeta("elecUse",        {color:"orange", dis: "Electricity Use", }) catch null

-  try out=out.addColMeta("elecMessage",    {color:"lightGray", dis: "Warning", }) catch null

-  try out=out.addColMeta("gasCost",        {viz:"barCell", color:"purple", dis: "Gas Cost", }) catch null

-  try out=out.addColMeta("gasUse",         {color:"purple", dis: "Gas Use", }) catch null

-  try out=out.addColMeta("gasMessage",    {color:"lightGray", dis: "Warning", }) catch null

-  try out=out.addMeta({vizByUnit}) catch null

+  try out=out.addColMetaClean("elecCost",       {viz:"barCell", color:"orange", dis: "Electricity Cost", }) catch null

+  try out=out.addColMetaClean("elecUse",        {color:"orange", dis: "Electricity Use", }) catch null

+  try out=out.addColMetaClean("elecMessage",    {color:"lightGray", dis: "Electricity Warning", }) catch null

+  try out=out.addColMetaClean("hwCost",       {viz:"barCell", color:"orangeRed", dis: "Hot Water Cost", }) catch null

+  try out=out.addColMetaClean("hwUse",        {color:"orangeRed", dis: "Hot Water Use", }) catch null

+  try out=out.addColMetaClean("hwMessage",    {color:"lightGray", dis: "Hot Water Warning", }) catch null

+  try out=out.addColMetaClean("chwCost",       {viz:"barCell", color:"skyBlue", dis: "Chilled Water Cost", }) catch null

+  try out=out.addColMetaClean("chwUse",        {color:"skyBlue", dis: "Chilled Water Use", }) catch null

+  try out=out.addColMetaClean("chwMessage",    {color:"lightGray", dis: "Chilled Water Warning", }) catch null

+  try out=out.addColMetaClean("steamCost",       {viz:"barCell", color:"slateBlue", dis: "Steam Cost", }) catch null

+  try out=out.addColMetaClean("steamUse",        {color:"slateBlue", dis: "Steam Use", }) catch null

+  try out=out.addColMetaClean("steamMessage",    {color:"lightGray", dis: "Steam Warning", }) catch null

+  try out=out.reorderKeepCols(["elecUse", "elecCost", "chwUse", "chwCost", "steamUse", "steamCost"]) catch null

 

-  return out

+  return out//.sort()

 

 end
wdg_portfolio_card_energySavings
--- wdg_portfolio_card_energySavings (pod)
+++ wdg_portfolio_card_energySavings (local)
@@ -1,7 +1,7 @@
 //todo add emissions data

 

 (dates, opts:{}) => do

-  dates = dates.toSpan

+

 

   //validate and authorize current user

   kwLinkValidate("analytics")

@@ -12,9 +12,9 @@
   //debug:true

 

   //get sites

-  grid   :  xq().xqProjs(navProjNames())

+  grid   :  xq().xqTimeout(5min).xqProjs(navProjNames())

                 .xqDefine("dates",dates)

-                .xqReadAll(site)

+                .xqReadAll(site and workedOn)

                 .xqMap("""site => do

                           dict: {site:site->id}

                           grid: wdg_site_card_costSavings(site,dates)

@@ -27,47 +27,38 @@
   totalSites: grid.size

   errSites: grid.findAll(r=>r.has("err")).size

   goodSites: grid.findAll(r=>r.missing("err")).size

+  //return goodSites

   if (debug=="Enable") return grid.reorderKeepCols(["site","err"])

+  //return grid

+  //split it up into columns

+  chwCost_numbers:  grid.colToList("chwCost").findAll(v=>v.isNumber)

+  chwCost:   chwCost_numbers.fold(sum)

 

-  //split it up into columns

-  gasCost_numbers:  grid.colToList("gasCost").findAll(v=>v.isNumber and v>0)

-  gasCost:   gasCost_numbers.fold(sum)

+  steamCost_numbers:  grid.colToList("steamCost").findAll(v=>v.isNumber)

+  steamCost:   steamCost_numbers.fold(sum)

 

-  elecCost_numbers:  grid.colToList("elecCost").findAll(v=>v.isNumber and v>0)

+  elecCost_numbers:  grid.colToList("elecCost").findAll(v=>v.isNumber)

   elecCost:   elecCost_numbers.fold(sum)

 

-  savingsIncentives_numbers:  grid.colToList("incentives").findAll(v=>v.isNumber and v>0)

-  savingsIncentives:   savingsIncentives_numbers.fold(sum)

 

-  savingsExtendedLife_numbers:  grid.colToList("savingsExtendedLife").findAll(v=>v.isNumber and v>0)

-  savingsExtendedLife:   savingsExtendedLife_numbers.fold(sum)

-

-  savingsAvoidedOutsourcing_numbers:  grid.colToList("savingsAvoidedOutsourcing").findAll(v=>v.isNumber and v>0)

-  savingsAvoidedOutsourcing:   savingsAvoidedOutsourcing_numbers.fold(sum)

-

-  totalCost: [gasCost,elecCost,savingsIncentives,savingsExtendedLife,savingsAvoidedOutsourcing].findAll(v=>v.isNumber and v>0).fold(sum)

+  totalCost: [elecCost,chwCost,steamCost].findAll(v=>v.isNumber).fold(sum)

 

   //put data into card and convert to grid

   card :  {title:"Cost Savings",

            totalCost       :    totalCost,

            elecCost        :    elecCost,

-           gasCost         :    gasCost,

-           savingsExtendedLife:       savingsExtendedLife,

-           savingsAvoidedOutsourcing: savingsAvoidedOutsourcing,

-           incentives:                savingsIncentives

+           chwCost         :    chwCost,

+           steamCost       :    steamCost,

            }

   //return card

   out: card.toGrid

            .addMeta({vizByUnit})

-           .reorderKeepCols(["totalCost","elecCost","gasCost"])

+           .reorderKeepCols(["totalCost","elecCost","chwCost","steamCost"])

 

   out=out.addColMetaClean("totalCost",{dis:"Total Cost Savings", viz:"barCell", color:"gray"})

          .addColMetaClean("elecCost", {dis:"Elec Cost Savings", viz:"barCell", color:"orange"})

-         .addColMetaClean("gasCost",  {dis:"Gas Cost Savings", viz:"barCell", color:"purple"})

-         .addColMetaClean("savingsExtendedLife",  {dis:"Extended Life", viz:"barCell", color:"green"})

-         .addColMetaClean("savingsAvoidedOutsourcing",  {dis:"Avoided Outsourcing", viz:"barCell", color:"blue"})

-         .addColMetaClean("incentives",  {dis:"Incentives Paid", viz:"barCell", color:"pink"})

-

+         .addColMetaClean("chwCost", {dis:"Chilled Water Cost Savings", viz:"barCell", color:"skyBlue"})

+         .addColMetaClean("steamCost",  {dis:"Steam Cost Savings", viz:"barCell", color:"slateBlue"})

   //check if anything is not a number and alter the colMeta so it shows up

   return out

 end

wdg_portfolio_card_sitesConnected
--- wdg_portfolio_card_sitesConnected (pod)
+++ wdg_portfolio_card_sitesConnected (local)
@@ -1,10 +1,14 @@
-() => do

+(opts:{}) => do

 

   //validate and authorize current user

   kwLinkValidate("analytics")

 

+  //opts

+  projSel: opts.get("projSel")

+

   //get all sites

-  sites: xq().xqReadAll(site).xqExecute()

+  //sites: xq().xqReadAll(site).xqExecute()

+  sites: readById(projSel).get("siteList").toRecList

   sitesWithPoints: sites.map(r => {site:r->id, points:xq().xqReadAll(point and hisEnd and siteRef==r->id).xqExecute().size}).findAll(r => r.get("points")>0)

 

   card: {primary: sitesWithPoints.size.toStr+"/"+sites.size.toStr,

wdg_portfolio_chart_cumulativeSavings
--- wdg_portfolio_chart_cumulativeSavings (pod)
+++ wdg_portfolio_chart_cumulativeSavings (local)
@@ -1,28 +1,35 @@
 (dates, opts:{}) => do

-  dates = dates.toSpan

+

   //validate and authorize current user

   kwLinkValidate("analytics")

+  if(dates.end.date > today()) dates= (dates.start..(today()-10day)).toSpan

 

   //opts

   debugVal: opts.get("debug")

   debug: debugVal!=null and debugVal!="Disable"

-

+  projSel: opts.get("projSel")

+  projTypeName: readById(projSel).get("projName")

+  siteList: readById(projSel).get("siteList")

+  //return siteList

   //get arcs and sites

-  sites: xq().xqReadAll(site).xqExecute

+  sites: xq().xqTimeout(5min).xqReadAll(site).xqExecute

   if (sites.size==0) return blankChart("No sites", "Please add sites to get started")

 

-  portfolio: xq().xqProjs(navProjNames())

+

+  portfolio: xq().xqTimeout(5min)

+                 .xqProjs(navProjNames())

                  .xqDefine("dates",dates)

                  .xqDefine("preDate",dates.start - 1s)

-                 .xqReadAll(site)

+                 .xqReadByIds(siteList)

                  .xqMap("""site => do

-                           try {site: site->id, output:wdg_site_chart_cumulativeSavings(site->id,dates).hisFoldCols(sum).addRow({ts:preDate,v0:0\$}).addColMeta(\"v0\",{dis:site.dis}).renameCol(\"v0\",site.dis.toTagName)}

+                           try {site: site->id, output:wdg_site_chart_cumulativeSavings(site->id,dates).hisInterpolate().hisFoldCols(sum).addRow({ts:preDate,v0:0\$}).addColMeta(\"v0\",{dis:site.dis}).renameCol(\"v0\",site.dis.toTagName)}

                            catch (err) {site:site->id, err:err}

                            end

                            """)

                  .xqExecute()

 

   //handle errors

+  //return portfolio

   if (debug) return portfolio

   goodSites: portfolio.findAll(r=>r.missing("err") and r.has("output") and r.get("output").meta.has("hisStart"))

   badSites:  portfolio.findAll(r=>r.has("err") and r.has("output") and r.get("output").meta.missing("hisStart"))

@@ -34,11 +41,12 @@
                      .hisJoin

                      .addColMetaAll({hisMode:"cov"})

 

-

+  //return goodData

   //output

-  try combinedHis: goodData.hisInterpolate.stackedAreaChart.hisRollup(avg,1mo).addColMetaAll({-hisMode})

+  try combinedHis: goodData.hisInterpolate.stackedAreaChart.hisRollup(avg,1day).addColMetaAll({-hisMode})

   catch (err) return blankChart("Error - please report.", err.get("dis"))

-  out: combinedHis.addMeta({title:"Cumulative Cost Savings", subtitle:"Gas and Electric - "+goodSites.size+" / "+totalSites+" sites", -chartLegend})

+  //return combinedHis//.hisFoldCols(sum)

+  out: combinedHis.addMeta({title: "Project: " + projTypeName + " Cumulative Cost Savings", subtitle:"Electric, Hot Water, Chilled Water, Steam - "+goodSites.size+" / "+totalSites+" sites", -chartLegend})

 

   return out

 end
wdg_portfolio_chart_energy
--- wdg_portfolio_chart_energy (pod)
+++ wdg_portfolio_chart_energy (local)
@@ -1,5 +1,5 @@
 (dates, opts:{}) => do

-  dates = dates.toSpan

+

   //validate and authorize current user

   kwLinkValidate("analytics")

 

@@ -8,18 +8,21 @@
   debug: debugVal!=null and debugVal!="Disable"

 

   //get arcs and sites

-  sites: xq().xqReadAll(site).xqExecute

+  sites: xq().xqTimeout(5min).xqReadAll(site).xqExecute

   if (sites.size==0) return blankChart("No sites", "Please add sites to get started")

 

   //get energy data

-  portfolio: xq().xqProjs(navProjNames())

+  portfolio: xq().xqTimeout(5min)

+                 .xqProjs(navProjNames())

                  .xqDefine("dates",dates)

                  .xqReadAll(site)

                  .xqMap("""site => do

-                           output: try getUtilityMeterSummary(site->id,dates).keepCols("ts,elec_usage,gas_usage".split(",")).hisRollupAuto

+                           output: try getUtilityMeterSummary(site->id,dates).keepCols("ts,elec_usage,hw_usage,chw_usage,steam_usage".split(",")).hisRollupAuto

                                    catch (err) err

                            try output=output.addColMeta("elec_usage", {chartGroup:"Elec", dis:site.dis+" (Elec)", -color, subtitle:"Electricity"}) catch null

-                           try output=output.addColMeta("gas_usage",  {chartGroup:"Gas",  dis:site.dis+" (Gas)", -color, subtitle:"Gas"}) catch null

+                           try output=output.addColMeta("hw_usage",  {chartGroup:"Hot Water",  dis:site.dis+" (HW)", -color, subtitle:"Hot Water"}) catch null

+                           try output=output.addColMeta("chw_usage",  {chartGroup:"Chilled Water",  dis:site.dis+" (CHW)", -color, subtitle:"Chilled Water"}) catch null

+                           try output=output.addColMeta("steam_usage",  {chartGroup:"Steam",  dis:site.dis+" (Steam)", -color, subtitle:"Steam"}) catch null

                            if (output.isDict) {site:site->id, err:output}

                            else {site: site->id, output:output}

                            end

@@ -27,7 +30,7 @@
                  .xqExecute()

 

   //handle errors

-  if (debug) return portfolio

+  if (debug) return portfolio.table

   goodSites: portfolio.findAll(r=>r.missing("err") and r.has("output") and r.get("output").meta.has("hisStart"))

   badSites:  portfolio.findAll(r=>r.has("err") and r.has("output") and r.get("output").meta.missing("hisStart"))

   totalSites: portfolio.size

@@ -38,7 +41,7 @@
                      .hisJoin

 

   //output

-  out: goodData.addMeta({title:"Energy Usage", subtitle:"Gas and Electric - "+goodSites.size+" / "+totalSites+" sites"})

+  out: goodData.addMeta({title:"Energy Usage", subtitle:"Electric, Steam, Hot Water, and Chilled Water - "+goodSites.size+" / "+totalSites+" sites"})

 

-  return out

+  return out.addMeta({chartLegend:"hide"}).hisClip()

 end
wdg_portfolio_chart_savings
--- wdg_portfolio_chart_savings (pod)
+++ wdg_portfolio_chart_savings (local)
@@ -1,5 +1,5 @@
 (dates, opts:{}) => do

-  dates = dates.toSpan

+

   //validate and authorize current user

   kwLinkValidate("analytics")

 

@@ -8,19 +8,22 @@
   debug: debugVal!=null and debugVal!="Disable"

 

   //get arcs and sites

-  sites: xq().xqReadAll(site).xqExecute

+  sites: xq().xqTimeout(5min).xqReadAll(site).xqExecute

   if (sites.size==0) return blankChart("No sites", "Please add sites to get started")

 

 

   //get energy data

-  portfolio: xq().xqProjs(navProjNames())

+  portfolio: xq().xqTimeout(5min)

+                 .xqProjs(navProjNames())

                  .xqDefine("dates",dates)

                  .xqReadAll(site)

                  .xqMap("""site => do

-                           output: try getUtilityMeterSummary(site->id,dates).keepCols("ts,elec_savings,gas_savings".split(",")).hisRollupAuto

+                           output: try getUtilityMeterSummary(site->id,dates).keepCols("ts,elec_savings,steam_savings,hw_savings,chw_savings".split(",")).hisRollupAuto

                                    catch (err) err

                            try output=output.addColMeta("elec_savings", {chartGroup:"Elec", dis:site.dis+" (Elec)", -color, subtitle:"Electricity"}) catch null

-                           try output=output.addColMeta("gas_savings",  {chartGroup:"Gas",  dis:site.dis+" (Gas)", -color, subtitle:"Gas"}) catch null

+                           try output=output.addColMeta("steam_savings",  {chartGroup:"Steam",  dis:site.dis+" (Steam)", -color, subtitle:"Steam"}) catch null

+                           try output=output.addColMeta("hw_savings",  {chartGroup:"HW",  dis:site.dis+" (HW)", -color, subtitle:"Hot Water"}) catch null

+                           try output=output.addColMeta("chw_savings",  {chartGroup:"CHW",  dis:site.dis+" (CHW)", -color, subtitle:"Chilled Water"}) catch null

                            if (output.isDict) {site:site->id, err:output}

                            else {site: site->id, output:output}

                            end

wdg_portfolio_widgetSelector

Sources are identical.

wdg_site_card_costSavings
--- wdg_site_card_costSavings (pod)
+++ wdg_site_card_costSavings (local)
@@ -1,5 +1,5 @@
 (site, dates, opts:{}, debug:false) => do

-  dates = dates.toSpan

+

   //validate and authorize current user

   kwLinkValidate("analytics")

 

@@ -10,47 +10,43 @@
   //opts

 

   //get site kickoff date, when savings started

-  savingsDate: getSiteKickoffDate(site)

+  savingsDate: null//getSiteKickoffDate(site)

   //if no savings date, just take the first day of this year

-  if (savingsDate==null) savingsDate = (dates.end - 1day).year.toSpan.start.date

+  if (savingsDate==null) savingsDate = (dates.start.date)

 

   //get utility meter summary

   data: try getUtilityMeterSummary(site, dates).meta catch (err) return {title:"Error! Action Required", error:(err.get("dis")), action:err.get("action")}

-  elecMeta: data.get("elecMeta")

-  gasMeta:  data.get("gasMeta")

+//  elecMeta: data.get("elecMeta")

+//  gasMeta:  data.get("gasMeta")

 

   //get utility rates

-  elecRate: if (siteRec.has("elecRate")) siteRec.get("elecRate")

-            else if (elecMeta.get("effectiveRollup").get("elec_rate").isNumber) elecMeta.get("effectiveRollup").get("elec_rate")

-            else null

-  gasRate:  if (siteRec.has("gasRate")) siteRec.get("gasRate")

-            else if (gasMeta.get("effectiveRollup").get("gas_rate").isNumber) gasMeta.get("effectiveRollup").get("gas_rate")

-            else null

 

   //get additional savings

   year: (dates.end - 1day).year.toSpan

-  arcs: taskRun(xq().xqReadAll(arc and projectTracker).xqExecute()).futureGet

-        .filter(projectSite==siteId or siteRef==siteId)

 

-  additionalSavings: getArcAdditionalSavings(year, arcs)

+  //return data

+  //additionalSavings: getArcAdditionalSavings(year, arcs)

 

   //put data together for card

-  elecCost: data.get("elecMeta").get("effectiveRollup").get("elec_savingsCost")

-  gasCost:  data.get("gasMeta").get("effectiveRollup").get("gas_savingsCost")

-  savingsExtendedLife:       additionalSavings.get("savingsExtendedLife")

-  savingsAvoidedOutsourcing: additionalSavings.get("savingsAvoidedOutsourcing")

-  savingsIncentives:         additionalSavings.get("savingsIncentives")

-  totalCost: [elecCost, gasCost, savingsExtendedLife, savingsAvoidedOutsourcing, savingsIncentives].findAll(v=>v.isNumber and v>0).fold(sum)

+  elecCost: try data.get("elecMeta").get("effectiveRollup").get("elec_savingsCost") catch null

+//  hwCost: data.meta.get("effectiveRollup").get("hw_savingsCost")

+  chwCost: try data.get("chwMeta").get("effectiveRollup").get("chw_savingsCost") catch null

+  steamCost: try data.get("steamMeta").get("effectiveRollup").get("steam_savingsCost") catch null

+  //gasCost:  data.get("gasMeta").get("effectiveRollup").get("gas_savingsCost")

+  //savingsExtendedLife:       additionalSavings.get("savingsExtendedLife")

+  //savingsAvoidedOutsourcing: additionalSavings.get("savingsAvoidedOutsourcing")

+  //savingsIncentives:         additionalSavings.get("savingsIncentives")

+  totalCost: [elecCost, chwCost, steamCost].findAll(v=>v.isNumber).fold(sum)

   if (not totalCost.isNumber) totalCost = 0.as("\$")

 

   //round numbers

   if (elecCost.isNumber) elecCost = elecCost.round

     else "missing"

-  if (gasCost.isNumber) gasCost = gasCost.round

+  if (chwCost.isNumber) chwCost = chwCost.round

     else "missing"

-  if (savingsExtendedLife.isNumber) savingsExtendedLife = savingsExtendedLife.round

-  if (savingsAvoidedOutsourcing.isNumber) savingsAvoidedOutsourcing = savingsAvoidedOutsourcing.round

-  if (savingsIncentives.isNumber) savingsIncentives = savingsIncentives.round

+  if (steamCost.isNumber) steamCost = steamCost.round

+    else "missing"

+

   if (totalCost.isNumber) totalCost = totalCost.round

 

   //put data into card and convert to grid

@@ -58,24 +54,21 @@
            subtitle        :    savingsDate.format("MMM DD YYYY").toStr+" to date",

            totalCost       :    totalCost,

            elecCost        :    elecCost,

-           gasCost         :    gasCost,

-           savingsExtendedLife:       savingsExtendedLife,

-           savingsAvoidedOutsourcing: savingsAvoidedOutsourcing,

-           incentives:                savingsIncentives

+           chwCost         :    chwCost,

+           steamCost       :    steamCost,

            }

   out: card.toGrid

            .addMeta({vizByUnit})

-           .reorderKeepCols(["totalCost","elecCost","gasCost"])

-           .addColMetaClean("totalCost",{dis:"Total Cost Savings", viz:"barCell", color:"gray"})

-           .addColMetaClean("elecCost", {dis:"Elec Cost Savings", viz:"barCell", color:"orange"})

-           .addColMetaClean("gasCost",  {dis:"Gas Cost Savings", viz:"barCell", color:"purple"})

-           .addColMetaClean("savingsExtendedLife",  {dis:"Extended Life", viz:"barCell", color:"green"})

-           .addColMetaClean("savingsAvoidedOutsourcing",  {dis:"Avoided Outsourcing", viz:"barCell", color:"blue"})

-           .addColMetaClean("incentives",  {dis:"Incentives Paid", viz:"barCell", color:"pink"})

+           .reorderKeepCols(["totalCost","elecCost","chwCost","steamCost"])

+           .addColMetaClean("totalCost",{dis:"Total Cost Savings", viz:"barCell", color:"gray", format:"U#,###."})

+           .addColMetaClean("elecCost", {dis:"Elec Cost Savings", viz:"barCell", color:"orange", format:"U#,###."})

+           .addColMetaClean("chwCost", {dis:"Chilled Water Cost Savings", viz:"barCell", color:"skyBlue", format:"U#,###."})

+           .addColMetaClean("steamCost", {dis:"Steam Cost Savings", viz:"barCell", color:"slateBlue", format:"U#,###."})

 

   //check if anything is not a number and alter the colMeta so it shows up

   if (not elecCost.isNumber) out = out.addColMetaClean("elecCost",{-viz})

-  if (not gasCost.isNumber) out = out.addColMetaClean("gasCost",{-viz})

+  if (not chwCost.isNumber) out = out.addColMetaClean("chwCost",{-viz})

+  if (not steamCost.isNumber) out = out.addColMetaClean("steamCost",{-viz})

 

   return out

 

wdg_site_card_energyBenchmarks

Sources are identical.

wdg_site_card_energyCosts
--- wdg_site_card_energyCosts (pod)
+++ wdg_site_card_energyCosts (local)
@@ -1,5 +1,5 @@
 (site, dates, opts:{}) => do

-  dates = dates.toSpan

+

   //validate and authorize current user

   kwLinkValidate("analytics")

 

@@ -12,50 +12,64 @@
   //get data

   data: try getUtilityMeterSummary(site, dates).meta catch (err) return {title:"Error! Action Required", error:(err.get("dis")), action:err.get("action")}

   elecMeta: data.get("elecMeta")

-  gasMeta: data.get("gasMeta")

+  steamMeta: data.get("steamMeta")

+  chwMeta: data.get("chwMeta")

+

 

   //get costs from meta

   elecCost: try elecMeta.get("effectiveRollup").get("elec_cost") catch "missing"

-  gasCost:  try gasMeta.get("effectiveRollup").get("gas_cost") catch "missing"

-  totalCost: [elecCost, gasCost].findAll(v=>v.isNumber).fold(sum)

+  steamCost:  try steamMeta.get("effectiveRollup").get("steam_cost") catch "missing"

+  chwCost:  try chwMeta.get("effectiveRollup").get("chw_cost") catch "missing"

+  totalCost: [elecCost, steamCost, chwCost].findAll(v=>v.isNumber).fold(sum)

 

   //get hisEnd from effective points

-  asOfElecDate: elecMeta.get("effectivePoints").get("elec_cost").toRec.get("hisEnd").date

-  if (asOfElecDate > dates.end.date) asOfElecDate = dates.end.date

+  asOfElecDate: try elecMeta.get("effectivePoints").get("elec_cost").toRec.get("hisEnd").date catch "missing"

+  if (asOfElecDate!="missing" and asOfElecDate > dates.end.date) asOfElecDate = dates.end.date

   elecDateMsg: try "(as of "+asOfElecDate.format("MMM D YYYY") +")"

                catch "(no data)"

-  asOfGasDate: gasMeta.get("effectivePoints").get("gas_cost").toRec.get("hisEnd").date

-  if (asOfGasDate > dates.end.date) asOfGasDate = dates.end.date

-  gasDateMsg:  try "(as of "+asOfGasDate.format("MMM D YYYY")  +")"

+

+  asOfsteamDate: try steamMeta.get("effectivePoints").get("steam_cost").toRec.get("hisEnd").date catch "missing"

+  if (asOfsteamDate!="missing" and asOfsteamDate > dates.end.date) asOfsteamDate = dates.end.date

+  steamDateMsg:  try "(as of "+asOfsteamDate.format("MMM D YYYY")  +")"

                catch "(no data)"

 

+  asOfchwDate: try chwMeta.get("effectivePoints").get("chw_cost").toRec.get("hisEnd").date catch "missing"

+  if (asOfchwDate!="missing" and asOfchwDate > dates.end.date) asOfchwDate = dates.end.date

+  chwDateMsg:  try "(as of "+asOfchwDate.format("MMM D YYYY")  +")"

+               catch "(no data)"

   //round numbers

   if (elecCost.isNumber) elecCost = elecCost.round.as(1)

-  if (gasCost.isNumber) gasCost = gasCost.round.as(1)

+  if (steamCost.isNumber) steamCost = steamCost.round.as(1)

+  if (chwCost.isNumber) chwCost = chwCost.round.as(1)

+

   if (totalCost.isNumber) totalCost = totalCost.round.as(1)

   else if (totalCost.isNull) totalCost = 0

 

   //dates string

   dateString: if (dates.end > now()) dates.start.date.format("MMM YYYY").toStr + " to " + "date"

-              else dates.start.date.format("MMM YYYY").toStr + " to " + dates.end.date.format("MMM-YYYY").toStr

+              else dates.start.date.format("MMM DD YYYY").toStr + " to " + dates.end.date.format("MMM-YYYY").toStr

 

    card : {title:"Energy Costs",

            subtitle        : dateString,

-           totalCost       :    "",

-           totalCostVal     :    totalCost,

-           elecCost        :    elecDateMsg,

-           elecCostVal     :    elecCost,

-           gasCost         :    gasDateMsg,

-           gasCostVal     :    gasCost,

+           totalCost       : "",

+           totalCostVal    : totalCost,

+           elecCost        : elecDateMsg,

+           elecCostVal     : elecCost,

+           steamCost       : steamDateMsg,

+           steamCostVal    : steamCost,

+           chwCost         : chwDateMsg,

+           chwCostVal      : chwCost,

            }

 

 

   out: card.toGrid.addMeta({vizByUnit})

-               .addColMeta("totalCost",{dis:"Total Energy Costs"})

-               .addColMeta("elecCost",{dis:"Elec Cost ", color:"lightGray"})

-               .addColMeta("gasCost",{dis:"Gas Cost ", color:"lightGray"})

-               .addColMeta("totalCostVal",{dis:"", viz:"barCell", color:"gray", format:"\$#,###,###"})

-               .addColMeta("elecCostVal",{dis:"", viz:"barCell", color:"orange", format:"\$#,###,###"})

-               .addColMeta("gasCostVal",{dis:"", viz:"barCell", color:"purple", format:"\$#,###,###"})

-               .reorderKeepCols(["totalCost","totalCostVal","elecCost","elecCostVal","gasCost","gasCostVal"])

+               .addColMetaClean("totalCost",{dis:"Total Energy Costs"})

+               .addColMetaClean("elecCost",{dis:"Elec Cost ", color:"lightGray"})

+               .addColMetaClean("steamCost",{dis:"Steam Cost ", color:"lightGray"})

+               .addColMetaClean("chwCost",{dis:"CHW Cost ", color:"lightGray"})

+               .addColMetaClean("totalCostVal",{dis:"", viz:"barCell", color:"gray", format:"\$#,###,###"})

+               .addColMetaClean("elecCostVal",{dis:"", viz:"barCell", color:"orange", format:"\$#,###,###"})

+               .addColMetaClean("steamCostVal",{dis:"", viz:"barCell", color:"slateBlue", format:"\$#,###,###"})

+               .addColMetaClean("chwCostVal",{dis:"", viz:"barCell", color:"skyBlue", format:"\$#,###,###"})

+               .reorderKeepCols(["totalCost","totalCostVal","elecCost","elecCostVal","steamCost","steamCostVal","chwCost","chwCostVal"])

 end
wdg_site_chart_baselineEnergy
--- wdg_site_chart_baselineEnergy (pod)
+++ wdg_site_chart_baselineEnergy (local)
@@ -2,15 +2,17 @@
 

   //validate and authorize current user

   kwLinkValidate("analytics")

+  hisEndVal : if (dates.end > now()) now() else dates.end

+  dates = (dates.start..hisEndVal).toSpan

+

+

 

   siteDis: try site.toRec.dis catch null

-  dates = dates.toSpan

-

   //opts

   rollup: null //1hr

   rollupFunc: avg

   chartType: "line"

-  strokeDasharray:"1,1"

+  strokeDasharray:"8,5"

 

   //get kickoff date for site

   kickoffDate: getSiteKickoffDate(site)

@@ -19,27 +21,29 @@
   //get data

   try grid: getUtilityMeterSummary(site, dates)

   catch (err) return blankChart("Error: "+err.get("dis"), err.get("action"))

-  colNames: grid.colNames.findAll(r => r.contains("_usage") or r.contains("_model")).add("ts")

-  grid=grid.keepCols(colNames)

 

+  grid=grid.keepCols(["ts","elec_usage","elec_model", "steam_usage","steam_model", "chw_usage","chw_model", "hw_usage", "hw_model"])

+  //grid = grid.addMeta({hisEnd:hisEndVal})

   if (grid==null) return blankChart("Energy Compared To Baseline", "No data found for "+siteDis)

 

   out: grid.toGrid.addMeta({title:"Energy Compared To Baseline", subtitle:siteDis})

            .hisRollupAuto(rollup, his=>rollupFunc)

 

-  if (out.meta.get("hisEnd") > now()) out=out.addMeta({hisEnd:now()})

+  //if (out.meta.get("hisEnd") > now()) out=out.addMeta({hisEnd:now()})

 

-  out= out.addColMetaClean("elec_usage", {chartType:chartType})

-          .addColMetaClean("elec_model", {chartType:chartType, strokeDasharray:strokeDasharray})

-          .addColMetaClean("gas_usage", {chartType:chartType})

-          .addColMetaClean("gas_model", {chartType:chartType, strokeDasharray:strokeDasharray})

+  out= out.addColMetaClean("elec_usage", {chartType:chartType, color: "orange"})

+          .addColMetaClean("elec_model", {chartType:chartType, strokeDasharray:strokeDasharray, color: "orange"})

+          .addColMetaClean("steam_usage", {chartType:chartType, color: "slateBlue"})

+          .addColMetaClean("steam_model", {chartType:chartType, strokeDasharray:strokeDasharray, color: "slateBlue"})

+          .addColMetaClean("chw_usage", {chartType:chartType, color: "skyBlue"})

+          .addColMetaClean("chw_model", {chartType:chartType, strokeDasharray:strokeDasharray, color: "skyBlue"})

           .addColMetaClean("hw_usage", {chartType:chartType})

           .addColMetaClean("hw_model", {chartType:chartType, strokeDasharray:strokeDasharray})

-          .addColMetaClean("chw_usage", {chartType:chartType})

-          .addColMetaClean("chw_model", {chartType:chartType, strokeDasharray:strokeDasharray})

-          .addColMetaClean("steam_usage", {chartType:chartType})

-          .addColMetaClean("steam_model", {chartType:chartType, strokeDasharray:strokeDasharray})

+

+  //g: wdg_site_chart_cumulativeSavings(site, dates)//.addMeta({chartGroup: "savings"})

+  //outFinal : hisJoin([out, g])

+

+  return out//.addMeta({hisEnd:hisEndVal})

 

 

-  return out

 end
wdg_site_chart_cumulativeSavings
--- wdg_site_chart_cumulativeSavings (pod)
+++ wdg_site_chart_cumulativeSavings (local)
@@ -1,17 +1,18 @@
 // 6/3/24 JG fix bug on line 37, convert dates to span

 

-(site, dates) => do

+(site, dates, opts:{showStackedAreaChart:false}) => do

 

   //validate and authorize current user

   kwLinkValidate("analytics")

-  dates = dates.toSpan

+  dates = dates//.toSpan

 

   //normalize inputs

   siteRec: site.toRec

   siteId: siteRec->id

 

   //opts

-  showKickoffDate: true

+  showKickoffDate: false

+  showStackedAreaChart: opts.get("showStackedAreaChart")

 

   //get kickoff date

   savingsDate: getSiteKickoffDate(siteId)

@@ -20,28 +21,32 @@
   data: try getUtilityMeterSummary(site, dates) catch (err) return blankChart("Error: "+err.get("dis"), err.get("action"))

 

   //keep savings for gas/elec

-  savings: data.keepCols(["ts","elec_savingsCost","gas_savingsCost"])

+  savings: data.keepCols(["ts","elec_savingsCost","hw_savingsCost","chw_savingsCost","steam_savingsCost"])

                .hisRollup(sum, 1day)

 

   //if both savings columns are empty, return blank grid

-  if (savings.isColBlank("gas_savingsCost") and savings.isColBlank("elec_savingsCost"))

+  if (savings.isColBlank("hw_savingsCost") and savings.isColBlank("elec_savingsCost") and savings.isColBlank("chw_savingsCost") and savings.isColBlank("steam_savingsCost"))

     return blankChart("Cost savings data is unavailable", "Check cost savings points", dates)

+  hisEndVal : if (dates.end > now()) now() else savings.meta.get("hisEnd")

+  //add cumulative sum columns

+  out:   savings.addMeta({title:"Cumulative Cost Savings", subtitle:readById(site).dis, hisEnd:hisEndVal})

+            //.findAll(r => r->ts.date >= savingsDate)

+            .addColCuSum("steamSum","steam_savingsCost")

+            .addColCuSum("chwSum","chw_savingsCost")

+            //.addColCuSum("hwSum","hw_savingsCost")

+            .addColCuSum("elecSum","elec_savingsCost")

+            .keepCols(["ts","chwSum","elecSum","steamSum"])

+  if (out.isColBlank("steamSum") and out.isColBlank("elecSum") and out.isColBlank("chwSum") and out.isColBlank("hwSum")) return blankChart("Cost savings data is unavailable", "No data after kickoff date", dates)

 

-  //add cumulative sum columns

-  out:   savings.addMeta({title:"Cumulative Cost Savings", subtitle:readById(site).dis})

-            .findAll(r => r->ts.date >= savingsDate)

-            .addColCuSum("gasSum","gas_savingsCost")

-            .addColCuSum("elecSum","elec_savingsCost")

-            .keepCols(["ts","gasSum","elecSum"])

-

-  if (out.isColBlank("gasSum") and out.isColBlank("elecSum")) return blankChart("Cost savings data is unavailable", "No data after kickoff date", dates)

-

-  try out = out.stackedAreaChart() catch out

-  out=out.addColMetaClean("gasSum",{chartGroup:"main", color:"purple", dis:"Gas Savings"})

-         .addColMetaClean("elecSum",{chartGroup:"main", color:"orange", dis:"Electricity Savings", title:"Cumulative Cost Savings - "+site.toRec.dis})

+  if(showStackedAreaChart) try out = out.stackedAreaChart() catch out

+  out=out.addColMetaClean("steamSum",{chartGroup:"savings", color:"slateBlue", dis:"Steam Savings"})

+         .addColMetaClean("elecSum",{chartGroup:"savings", color:"orange", dis:"Electricity Savings"})

+         .addColMetaClean("chwSum",{chartGroup:"savings", color:"skyBlue", dis:"Chilled Water Savings"})

+         //.addColMetaClean("hwSum",{chartGroup:"main", color:"orange", dis:"Hot Water Savings", title:"Cumulative Cost Savings - "+site.toRec.dis})

 

   //fix hisEnd so chart axes are correct

-  if (dates.end > now()) out=out.addMeta({hisEnd:now()})

+  // ERRORING IN 3.1.11

+  //if (dates.toDateSpan.end > now()) out=out.addMeta({hisEnd:out.last.get("ts")})

 

   //add kickoff date line

   if (showKickoffDate) do

@@ -50,5 +55,5 @@
              .addColMetaClean("kickoff",{chartGroup:"main", dis:"Kickoff Date", strokeDasharray:"2,4", color:"black", format:"#"})

     end

 

-  return out

+  return out.addMeta({chartNoScroll})//hisJoin([out, wdg_site_chart_baselineEnergy(site, dates)]).addMeta({chartLegendNoSort})

 end
wdg_site_chart_monthlykBTUUsage

Sources are identical.

wdg_terminalUnits_chart_damperPositions
--- wdg_terminalUnits_chart_damperPositions (pod)
+++ wdg_terminalUnits_chart_damperPositions (local)
@@ -54,8 +54,9 @@
     if (colm.name !="ts") do

       avg: his.colToList(colm.name).fold(avg,"*")

       pointMeta: colm.meta

-      color: "#B2E1FA"

-      strokeWidth:0.25

+      //color: "#B2f1FA"

+      color: "#007BFE"

+      strokeWidth: 0.25

       if (avg>90) do

         color = "red"

         strokeWidth=2

wdg_terminalUnits_chart_hotColdZones
--- wdg_terminalUnits_chart_hotColdZones (pod)
+++ wdg_terminalUnits_chart_hotColdZones (local)
@@ -28,19 +28,17 @@
   end

   opts = opts.set("rollupOverride",rollupOverride)

 

-  occDisp: opts.optNorm("occDisp", false)

-

   //logic

 

 

   //check status function

   checkStatus: (val) => do

-    if      (val==null) "unknown"

+    if      (val==null) "unkown"

     else if (val > 0)   "too hot"

     else if (val < 0)   "too cold"

     else                "good"

   end

-

+  if(vavRecList.isNull or vavRecList.isEmpty) return "No Data"

   //create output grid

   grid: []

   errGrid: []

@@ -71,36 +69,17 @@
      unknown: vals.findAll(v=>v=="unknown").size

      }

   end

-  //add total cannot calculate vav number

-  out = out.map(r=> do

-    cold: r.get("cold")

-    good: r.get("good")

-    hot: r.get("hot")

-    unknown: r.get("unknown")

-    totalCalculated: cold + good + hot + unknown

-    totalVavs: readAll(equip and vav and siteRef == vavs.first->siteRef).size

-    r = r.set("cannotCalc", totalVavs-totalCalculated)

-  end)

 

   //format

   if (showUpstreamData) try out = [out,  wdg_terminalUnits_chart_upstreamData(goodPoints.first.toRec.get("equipRef"), dates, opts)].hisJoin catch null

 

-return  out = out.stream

-           .reorderCols(["ts","cold","good","hot", "unknown", "cannotCalc"])

-           .addColMetaClean("cold", {dis:"Too Cold", chartType:"area", chartAreaMode:"zero", color:"blue", chartMin:0, chartMax:vavRecList.size, title:"VAV Zone Status", subtitle:"Blue = cold, Red = hot, Green = good, Gray = unknown, Purple = cannot calculate"})

+  out = out.stream

+           .reorderCols(["ts","cold","good","hot", "unknown"])

+           .addColMetaClean("cold", {dis:"Too Cold", chartType:"area", chartAreaMode:"zero", color:"blue", chartMin:0, chartMax:grid.size, title:"VAV Zone Status", subtitle:"Blue = cold, Red = hot, Green = good, Gray = unknown"})

            .addColMetaClean("good", {dis:"Good", chartType:"area", chartAreaMode:"prevSeries", color:"green"})

            .addColMetaClean("hot", {dis:"Too Hot", chartType:"area", chartAreaMode:"prevSeries", color:"red"})

            .addColMetaClean("unknown", {dis:"Unknown", chartType:"area", chartAreaMode:"prevSeries", color:"gray"})

-           .addColMetaClean("cannotCalc", {dis:"Cannot Calculate", chartType:"area", chartAreaMode:"prevSeries", color:"purple"})

            .collect

            .stackedAreaChart

-

-  if (occDisp) do

-    if (ahuId.isNonNull) ahuIds: [ahuId]

-    else ahuIds: vavRecList.map(v => v.get("airRef")).unique.findAll(v => v.isNonNull)

-    occHis: ahuIds.map(v => v.logic_ahu_occPeriods(dates, {}, {}))

-    out = [out].addAll(occHis).hisJoin

-  end

-

   return out

-end
+end //end func
wdg_terminalUnits_chart_zonesHeatingCooling
--- wdg_terminalUnits_chart_zonesHeatingCooling (pod)
+++ wdg_terminalUnits_chart_zonesHeatingCooling (local)
@@ -1,4 +1,3 @@
-// Updated 2025-08-27 by Kyle Wolfe to add occ/AHU status to display if occDisp option is true

 (ahu, dates, opts:{}, vavs:null) => do

 

   //validate and authorize current user

@@ -29,10 +28,8 @@
   end

   opts = opts.set("rollupOverride",rollupOverride)

 

-  occDisp: opts.optNorm("occDisp", false)

-

   //logic

-

+  if(vavRecList.isNull or vavRecList.isEmpty) return "No Data"

   //create output grid

   grid: []

   errGrid: []

@@ -74,13 +71,5 @@
            .addColMetaClean("unknown", {dis:"Unknown", chartType:"area", chartAreaMode:"prevSeries", color:"gray"})

            .collect

            .stackedAreaChart

-

-  if (occDisp) do

-    if (ahuId.isNonNull) ahuIds: [ahuId]

-    else ahuIds: vavRecList.map(v => v.get("airRef")).unique.findAll(v => v.isNonNull)

-    occHis: ahuIds.map(v => v.logic_ahu_occPeriods(dates, {}, {}))

-    out = [out].addAll(occHis).hisJoin

-  end

-

   return out

-end
+end //end func
wdg_terminalUnits_table_ahuSummary
--- wdg_terminalUnits_table_ahuSummary (pod)
+++ wdg_terminalUnits_table_ahuSummary (local)
@@ -1,5 +1,6 @@
-// Updated by Kyle Wolfe on 2025-08-27 to get sparks from both the individual points on the AHU,

-// and the AHU rec itself

+// Updated by Kyle Wolfe on 2025-08-27 to get sparks from both the individual points on the AHU, and the AHU rec itself

+// Updated by Justin Brunner on 2026-01-23 to add supplyOnly opt that will filter all vav lists to be supply only vavs when true

+

 (site, dates, opts:{}) => do

 

   //validate and authorize current user

@@ -14,6 +15,7 @@
   rollupOverride: if (opts.has("rollupOverride")) opts.get("rollupOverride") else null

   debug: opts.get("debug")!=null and opts.get("debug")!="Disable"

   occOnly: if (opts.has("occOnly")) opts.get("occOnly") else true

+  supplyOnly: if (opts.has("supplyOnly")) opts.get("supplyOnly") else false

 

   //AHU LOOP

   ahus: readAll(ahu and siteRef==siteId and equip and not vavSummaryNoShow)

@@ -28,6 +30,7 @@
 

     //get downstream units

     downstream: readAll(equip and airRef==row->id)

+    if(supplyOnly) downstream = downstream.filter(supply)

     downstreamIds: try downstream.colToList("id") catch null

 

     rules: readAll(rule and not disabled)

@@ -47,9 +50,11 @@
   end

 

   //get undefined and all units

-  undefined: try readAll(siteRef==siteId and vav and not airRef and equip).colToList("id")

+  undefined: try if(supplyOnly) readAll(siteRef==siteId and vav and not airRef and equip and supply).colToList("id")

+                 else           readAll(siteRef==siteId and vav and not airRef and equip).colToList("id")

              catch []

-  all: try readAll(siteRef==siteId and equip and vav).colToList("id")

+  all: try if(supplyOnly) readAll(siteRef==siteId and equip and vav and supply).colToList("id")

+           else           readAll(siteRef==siteId and equip and vav).colToList("id")

        catch []

 

     out: grid.sort("id")

kwLinkCoreExt
cacheGrid
--- cacheGrid (pod)
+++ cacheGrid (local)
@@ -1,15 +1,23 @@
 (records, specialTagName, dates) => do

-  cipherText :

-    "AxD:T1BkkICIb4SOTMNJwbx-wA::PFhoj0MDm4_m4f9zzsJ0r_2M-Jv19NOtXHFa-dXnXemkHI9vgIdn" +

-    "KI3TBsEUEto9uneAdwPxA5TYYbg5op3Exs2g0mcAtC2nSFl-g-ZIX6N0H9xJLgsiF5MhjetDhkor5QdG" +

-    "qIzzrBZcQ_lZiZ-NLEvj6038-R0XV8tZFbPv3iIDcAxPX5Zfq-j82GnABIdiUd3v-tMDlLql3-TDf622" +

-    "0wrebG9FJDy7a7hZCwuK26r0UMve0W54qs_ADpr0GLK4EEEd5Ld0KRhYtlsLaWn11LXNx6M9qwLx_FLl" +

-    "JS7Av6E-2QoTCmFzPRiWhpu5m8m01-tpPaDyB--paC2NzvlVfP0NUqqjn0vKyoGsrBeoonpcqhFqYs5x" +

-    "B5bEUL1PWuS4xJLeUNSf_eCymbRzE1zwIJkrk9Cm_Yku3X8gQVbGarqBz7LD92WbxPxC2kaY0EL7fzZI" +

-    "MiPj7rmj9ep3y9sEqUoyfjYK7w3uQrG4Cd5AngMwprhL_lwYLwcTSA5Ci1f9DSIg92LU8w2wK6AyZogB" +

-    "tihcDWo3j7W2T8BS3ptKSZ80UGlcXWNAacH16FbjHC4mEvJ06wFIwwtnnrEhzdmlHpE8LDe0RUbheqo0" +

-    "oLoIiBWsKAO_ctuuEyN729NuwPpRS4tENIYqFWtkTD79eJIxnHSUo6N8paPpN1nCJ1PXK5_gBBth4xTx" +

-    "iBXgPJYoB9qutd5LxHIAnHIfyHO2IsjpbkX7VgYmdj3P_U2zije45u4"

-  keyFunc : () => "ddJNIRm2qio2Z7CXpKADiw"

-  return afAxonEncryptorRt_callFunc("cacheGrid", cipherText, [records, specialTagName, dates], keyFunc)

+  if(not dates.isSpan) dates = try dates.toSpan catch "all"

+  dateYear: try dates.start.year() catch "all"

+  if(records.isNull) return "First parameter needs to be a grid"

+  if(records.isEmpty) return "First parameter needs to be a grid"

+  if(not specialTagName.isStr) return "Second parameter needs to be a string"

+  //if(dateYear == "all") filter: "cache and " + specialTagName + " and cacheYear==everything"

+  filter: "cache and " + specialTagName

+  previousCachedRecs: readAll(parseFilter(filter)).findAll(r => r.get("cacheYear") == dateYear)

+  if(previousCachedRecs.size > 0) do

+    //recDelete(previousCachedRecs)

+    previousCachedRecs.each() p => do

+      diff(p, {trash}, {remove}).commit

+    end

+  end

+  //Write to the cache

+  //if(specialTagName.contains("portfolio")) records = records.renameCol("site", "siteR")

+  records.each() r => do

+    newTags: {cache:marker(), cacheYear:dateYear}.set(specialTagName.toStr, marker())

+    r = r.merge(newTags)

+    diff(null, r, {add}).commit()

+  end

 end
logic_hisPoint_missingData
--- logic_hisPoint_missingData (pod)
+++ logic_hisPoint_missingData (local)
@@ -1,16 +1,48 @@
+/*

+*   Description :

+*   Retrieves the periods in which the historized point is missing data.

+*

+*   Parameters :

+*     Type                   Name            Description

+*     Ref                    target          Historized Point

+*     DateSpan               dates           Dates to look at

+*     Dict                   opts            Additional Options

+*

+*   Output :

+*     Type                   Name            Description

+*     hisGrid                out            - Period Grid of missing periods, or null if no history found.

+*

+*   Work :

+*     Who                    When            Change

+*     Thomas Kuhrke Limia    08/22/2025      - Initial Creation

+*/

 (target, dates, opts: {}) => do

-  cipherText :

-    "AxD:tnVmPw83PQLMOKwn-pkJeA::ckW6lCkR97Mf2mjlP2jqEEqYo9IYSPGYT5z6X0x9d0ttkHwpJ7j1" +

-    "Rd-BFOMN8fajur85oSps3v8-f5B14FJgQzh3u3NQsunH7zENgNuLkDqpDu29PlWFju5EVk2daOEQbUQz" +

-    "6tLidM3mcTfnWTUULp19ofGH_XpSq3vbFs6Nyywq1N-9B1hIVMBR3Ze1ex0tYbBCHVg1_Ix3lSCQGGXZ" +

-    "rXP8hyjKfhINXzaWrXsY-gdE4MQPb3TnHWqM2lvYrhsZYQ_itA1TPgRzdEAI_3q3rgieUv19XxwLoV7u" +

-    "9PftpgqpskBDN71X2dZL2CyAzwXBI1xyJoHr-Ou-WEQ4lRI5mDpT0kwzF3nD3tQepHuAc_VYu4O89V6Q" +

-    "Kpq0DmzGHV5pXq_YGDXWD6X6XfxfoO9t24KGfX6LrDMJX5hgxzq9b0c_YIXWyQA4fFdrwbM9MQAqI3VD" +

-    "j3jkTpGW0bakHSV5mvY4l65uFVCv3jqNSVBEq5JVXvOX3sZBl9XvRIrFknyBgj8wOpe4r07pKkP_eom3" +

-    "ewuHx7-Jr5GoxCc-H1XliBn36JDWTabYt3VUvjapAI3x4j9hhauwnaCXrPax-9IfVJdl1B4d_nupqhw3" +

-    "cGGr-eqY1EjPFtu4TGHiNPr_FyHTPpSVBe8aRyJFNCMold5RdWcSfwvWMSkZ4t1D5ACCrR9YpqzR59a9" +

-    "DbixgAQXmMpyJndvANPAIbrY3iAzsYhO_OHhmFfzEZSom54-pvPwd-juS4neMQmwIg6c8HEWgQkYcarA" +

-    "Ui2AoMoPhNgqpmu0aA"

-  keyFunc : () => "ddJNIRm2qio2Z7CXpKADiw"

-  return afAxonEncryptorRt_callFunc("logic_hisPoint_missingData", cipherText, [target, dates, opts], keyFunc)

+

+    // Normalize Inputs

+    targetRec: target.toRec

+    targetId: targetRec.get("id")

+    dates = dates.toSpan

+

+    // Retrieve his data, and calculate durations between timestamps

+    his: targetId.hisRead(dates, {-limit})

+

+    // Edge-case (Empty His returns null)

+    if (his.hisClip.isEmpty) return null

+

+    // Add the delta column of durations

+    deltaHis: his.addColDelta("ts")

+

+    // Determine Data Interval to use (prefer meta interval, and fallback on median)

+    dataInterval: deltaHis.foldCol("delta", median)

+

+    // Find periods where the dataInterval doesnt match

+    periods: deltaHis.keepCols(["ts", "delta"])

+                     .hisFindPeriods(v => v.isNumber and v != dataInterval)

+                     .renameCol("delta", "dur")

+

+    // Combine all data for output

+    out: hisJoin([his, periods])

+

+    // Return

+    return out

 end
pointQueryLookup
--- pointQueryLookup (pod)
+++ pointQueryLookup (local)
@@ -1,61 +1,325 @@
-(target: null, opts: {}) => do

-  cipherText :

-    "AxD:J7ik5GzASro65uWVIxMkAg::7cPS7lsTZSH5N2vG2V_-JnF7WnnW0YzEzAkoWuWN0t33cekbuST1" +

-    "jdwUtsnrnsWKnY2ZJ11FgjHRBw-4DchwEYWtNDzsPhtoG9qIeWzimhGiuRu1zA5WRrk0313ancLuQXcH" +

-    "zUsoSr9-_gDRNSvmhd8jJ8XaV0eozzR7scJJx801Sxs1lYKqn9-efJgMoGE5yUdio-ucRJMKwXCuww-J" +

-    "b8TqM0LRciBpX_qgy9sWW83-toyx1g1E6NXoPfuyE4EB_DXdpr3CmMSWscawuIAE3KH2FmPCMCU8ihr3" +

-    "tr_mcJm1MYsEo96AokjwJZusgH38mbnKrRoCf8NHPHr0pfCbgMSC-bF-D68vIgcs7pkROhkb8dHHy0Yb" +

-    "BGZHrQwecGLFxh3jego2SM4MSQxMksgcXyMChH17bY10l6VEqvIczPolCB66sNO2bVrCc5FWAFPGXxtr" +

-    "m_T7E__lMU78LEPWLfO1UMWE_FLJtlEAQbuv_xhB4jUy4kyqpWCjuwNLkCrYY7sWHzyE90-Ys6SJzJLF" +

-    "INz9kBTIPWsbbjDHx0satQxguftZqyzoAjtoNJvymRdqyVTMTH2kLe1zoF_fI7afvDstxdp_YJ1Snx8y" +

-    "b3DLsnyLYJmKX7M8z4og1VWfRq6MdK-OCyjnRkLCO8Yv2ieCKWXBBbt7b5H7sVq8aZe-QHMINJW9XJQa" +

-    "AnOn9ePqiMWZoCfUbSB_dpwe89a9xMwOB_Ndj9gW8AoXEiK2uUgBEN_p4zcjOmNOJemXpluU-yQDFqJ3" +

-    "bZKgktkwNLUwrhYAYsp8Rg12CkGeXqx5hEEhEeXaUZisO7OKUHC9iFpi0CXbKbIB7bQc5kmWXCQNOKpk" +

-    "WJX_EPBrGrlmKXXseMsPuT78bkNoPxGbYxGwE4cSIfDqE0wkZOpILvuYXWkc5_jIdwuNsHSNLMXtEG2W" +

-    "NZ2dCBXpdFS7pFE5KrdkkydqepBBHgcCia5uNWcm1Sm9MuqqhjSK3R2r8D8VxPAJNIP-B05znD5pcUt8" +

-    "H-XKlS1RIkPpOpT3_0o_ieo7Dk2A51Ta4aSMsV1dkCGA78aq6mSF6oBYWefmW2FGYsmJjl0_KhJ30cuM" +

-    "-VLV1Tm58wPWB4gk-VTLLCCYqcu2eiH2XOMA5h2A0Jqm8nAp0-VH8rIvsgcdXV7OyBp7RThiFhmXrczT" +

-    "9UjSq1fDGg5ss22R-nyAlL6Cy9lFNgAwJXhhZGyoeEmKAnTju_Eszm8fkclrd66V2OG8Oumqyo_uLyAT" +

-    "WaOvw-a_AV8IniBRcJsnpdMFFuoGLKQ_IeAojF82KpVG0sB7Q9SR5iOJOCgu1zOfjmbJJuRiKt_5lvlX" +

-    "vjjxxHEwjXF_f-PFNDclHEwLpjwPm0u3Vln7BaLW0nbWkRsC8_GugIUsI0JIOZXfv7gCowIcqWPqFl2k" +

-    "AQTXjulVkhATkjviOkhCRzRpJgBt_4vjZzIPZKnJZqEq1aIDDCk8TxIBlABWT2Secx_B7z6vXxM6sKJO" +

-    "nThHzuHjlsJYDXrMEq1NVYtIz6HOv86FEksDz8zR7cCtSfQE2z-PMEeIBL0g5g4k5lDduU0ltFQDamy8" +

-    "jGokPxsAKgmYeqtWlvQDFircTDaUEURdVC0t0aRmXAOHIlhqYslE5vMmXfaZyB8tY8XC-QO4RwKRNOsA" +

-    "Ii_lUujsAX-uRZib_Qj79SArzhQVKHalYEglFumjDyOfjftf0dAqW7Lm3RzLBPnSAP5drZMTfCqr_keo" +

-    "qCRPNnnsdgVhHpauC4oI3UwJJ3cnm6wK1qCQzw3LQTapoht77RJRHPZ0uepNmuxAENkXf0tm_ViUbsSj" +

-    "6EFCV5CDWQqH3aXWIdkiCAU4A0RkucCfFULcqINf2dfBkY3R9167QcyyhIIR335dIg-Jn2S3Ms2TuOiJ" +

-    "mVIx3ht7V091uCY6UWCmpIKebvMwnDubPnelivQ9tAwKnC2jR4eo_SppqT981sI61cG0tmud_TU6lwIW" +

-    "KoCXkm74hOIIw-JtB0zwfMXU1joh9m0_aPdw2FHyn4IhZVKpLeLYGlq8LtA_f723YpAqGzUM3VdjBqBq" +

-    "evHqr-YL5X8Rq5CdMndKEk5xKgy7a9tabPjmQINlDJvPOh69qnCXU0EjY9YJkJAfVBM0-QUw2ENrkk8X" +

-    "0Jnerfrdo-jFmA4UGKQlRooB3WU2zRbzNHpUOlUhdr1PGvwaKFeZBVgD8mKPorQ9M9fpbfvLv94j4u0a" +

-    "vGuJnHS8fIEIIttwkptkl6dZY5VJPirMexsQ8x9h7WlmoJc5KmkWxBQhDvM3Ct_MUx1rZJNrOLNi4Ie5" +

-    "Js_rUphBPRUBvsZoHHP6a7BkoGVK1Jkkz94oVE3S43QHcwxLRg7LerPwKMIfnnNynQDm7IE0n7FJ6I1J" +

-    "SrIJnQ0Lw2TrKHGF3LxRqJPpnzxYXyk__AlFTONjPxdh5D5GWbOXxupbq2XetLrtcbPlXiVhewejzhdo" +

-    "9AAHINpe-THmsGMM0YgSq6TGY-nbox6NZfQIV7lFpKfbhb4-vRv6CTcrSo2nPxcPPJklUYDX8AK7h4L2" +

-    "Hmc6n3T-BFv3rttrQnM2kmSPKZ2CzO3cMuLCOIdgoO12BVTnhT6Q46g5gwL6YSdsuOcRR1lpko7AMUmv" +

-    "KYDmI_qQ3gpToK2m2VtjzmpdQwQPsHEoGZ3KCv3FE37x64gVBjFjkcJuOj8PL87x2xJVivML5GYLopia" +

-    "u6glIJnSB-lRUd_NZZanFi05MKqaD37mr4thZHmNgUbyKKJceApVRwPQnH5JXZ-hIdOkErcKotzzFSXH" +

-    "QElnlHMJwPqlMH-_7FC2pKOgpjeIZTaAZtI5yubMlvRu6xEYYZMVWjko6C0w6JGAztBsbGGih-cp9UgZ" +

-    "vqVdxZpueTfDpepAdIPFNYl5GthaQfyzfldAD2a16HgTl7_RGNKkecRQEq_KUr6iX9Q1PyvswN4LSN6v" +

-    "AMgI8Uimcz0ZFZ2GT9AV8rWnSSLxTq-QIMjbJORY3LwDzKOG9IOs4T-5ZAuGoaDwmNklXTxUyPrCAGgT" +

-    "koFLAbO8hgV3OIuAMAjOFf77vsgFVzsQhSCAx9uDKsowXuxTnwwfkWn60jPoeZFW_D_IMw_SUil3b73h" +

-    "XvVU9TzYdYzAldUUSs8c9T32w_EqTx3dAFeq_UZenGz-Hpb0UvrdWaG4Nak31iEDMWgEaa6A5m7DVrCp" +

-    "Ep95i5hgSxW_A9piXVMf0yuX0nS2pUk9I156QvkqM_nqLi0KeHTdObLNxq40FWgvzE8yh-1xYXkUOS9C" +

-    "x2pkdEnzNWPzvKEIUPKyaNS6rDKHi4OW2Z20UHi9G08mrHfFWV-tv59bisy7pJA5Gaa2sB7GFqUqyh5X" +

-    "mLu9Vy98861yGdSVdKybEmwLrOgz20N63ie0ACuRk6UVaQaFgQX5ZZFBKSO19gsqiNxAGTPucd-9-K8y" +

-    "cBtQJ3bJgjboXjjQ7J-ehZxYvVJI-88Rsst_hnmgLUlmqwcZEtOVPuZEl7PjecfjB_2TzH0YQcW1BfWZ" +

-    "kr7fT7nwjf-Ow_za4spUGPe2BVMsG8wxDTvVwzzP8Qs-CrExhKiqxD7vImKOyel2BXU79lSJ80EFZJpV" +

-    "AmWma5DPsQHQGWxHVYCSe4MBvKBFvu0nE2P3YohFlxU4eR4FDC9ELCSuzy3O-FPmIDZNlb5H-CxKSDf-" +

-    "x7RL9sAPVduoRyKm30xnjUh61B2MOlORwB4buvAMVFIqURU86jNv6UqSUjHK_JigaLN9FDz-QJfUT72e" +

-    "XX10CJiRvdf9MNPOQ6UZ3scFY0OgFNW6g6BCs3u_wrRvljuvkFI5LYLENV1XGLKLjjmBufV3vfjSql3f" +

-    "ihfVGCGjXRLXgutD5lSZpGbSF05Clf1JxfSUJCq1rP4V_3GzXMwZD9AB7_Z3loOAkeRAmsjGndYKW833" +

-    "DbR_zGnFc6EoVlDvHScL_l1UYF-piWIb9IOUZ5WdGc0LDiOfSIj3oltosUnyC73nBmLcnRiBKbbKgIlm" +

-    "TnyGvqONb_vKNBsOM5f36dcW9AeFBrxLl0-huFK3XA81fp9shkvpybeJz06GmRhVIu9kzAWhqH-rRoE2" +

-    "7YuB8U4a6I-5VpHQGdMxAN7tIqpziaSDjsVjlqmvOPk0VQzES2d4Asf6IYRyiA7W-OfGi5pBiw3f2JNR" +

-    "leiFvUGd_nBA0Rc-6C9HGTzMLmMzIr_SXfvUxJvBvJRfoEQkWwfdb-FE5dkCupIiFrTR0Lu699B7ACEJ" +

-    "k1itx5Q-p3jUqdSEG7AeqdLT6JJ5CwBoEiC1wa15itVs4q2TlsYKVgVWfXDQ06ax7qGwWgYQyk_xwTfE" +

-    "p7erfra5P0wL3XDCtKE_oMX0HSpgVgqvKAzDF7GulOpzs8pFiGlNa2a0-6MeZ3cY3Id9vjlDSfdwtg_r" +

-    "5yPKDd7I10BFV9PSq6PtI6LVpZDGgTsThjMRpjnloYsrcik5Y_JN8HdTDe2UUZzLlQ"

-  keyFunc : () => "ddJNIRm2qio2Z7CXpKADiw"

-  return afAxonEncryptorRt_callFunc("pointQueryLookup", cipherText, [target, opts], keyFunc)

+(target:null, opts:{}) => do

+

+  //opts

+

+

+  colMeta: if(opts.has("colMeta")) true else false

+  show: if(opts.has("show")) true else false

+

+

+  queries: [

+    /////////////////////// AHUS /////////////////////////////////

+    //temps

+              {target:"oat_sensor",                  query:"temp and air and point and outside and sensor",                            colMeta:{color:"#00b843"} },

+              {target:"dat_sensor",                  query:"temp and air and point and discharge and sensor and not leaving and not entering", colMeta:{color:"#0547fc"} },

+              {target:"dat_sp",                      query:"temp and air and point and discharge and sp",                              colMeta:{color:"#5cb3ff"} },

+              {target:"mat_sensor",                  query:"temp and air and point and mixed and sensor",                              colMeta:{color:"#FFD580"} },

+              {target:"mat_sp",                      query:"temp and air and point and mixed and sp",                                  colMeta:{color:"#FFA500"} },

+              {target:"eat_sensor",                  query:"temp and air and point and exhaust and sensor",                            colMeta:{color:null} },

+              {target:"rat_sensor",                  query:"temp and air and point and return and sensor",                             colMeta:{color:"#FF0000"} },

+              {target:"zntClg_sp",                   query:"temp and air and point and effective and zone and cooling and sp",         colMeta:{color:"#0547fc"} },

+              {target:"zntHtg_sp",                   query:"temp and air and point and effective and zone and heating and sp",         colMeta:{color:"#FF0000"} },

+              {target:"znt_sensor",                  query:"temp and air and point and zone and sensor",                               colMeta:{color:"#00b843"} },

+              {target:"znt_sp",                      query:"temp and air and point and zone and sp and not heating and not cooling",   colMeta:{color:"#71bd8d"} },

+

+    //pressures

+              {target:"dsp_sensor",                  query:"pressure and air and point and discharge and sensor and not filter"       ,colMeta:{color:null} },

+              {target:"dsp_sp",                      query:"pressure and air and point and discharge and sp and not filter"           ,colMeta:{color:null} },

+              {target:"bldg_press_sensor",           query:"air and building and point and pressure and sensor"                       ,colMeta:{color:null} },

+              {target:"bldg_press_sp",               query:"air and building and point and pressure and sp"                           ,colMeta:{color:null} },

+    //Flow

+              {target:"daFlow_sensor",               query:"flow and air and point and discharge and sensor"                          ,colMeta:{color:null} },

+              {target:"daFlow_sp",                   query:"flow and air and point and discharge and sp"                              ,colMeta:{color:null} },

+              {target:"eaFlow_sensor",               query:"flow and air and point and exhaust and sensor"                            ,colMeta:{color:null} },

+              {target:"eaFlow_sp",                   query:"flow and air and point and exhaust and sp"                                ,colMeta:{color:null} },

+              {target:"raFlow_sensor",               query:"flow and air and point and return and sensor"                             ,colMeta:{color:null} },

+              {target:"raFlow_sp",                   query:"flow and air and point and return and sp"                                 ,colMeta:{color:null} },

+              {target:"znflow_sp",                   query:"flow and air and point and discharge and sp"                              ,colMeta:{color:null} },

+              {target:"znflow_sensor",               query:"flow and air and point and discharge and sensor"                          ,colMeta:{color:null} },

+              {target:"tot_daFlow_sensor",           query:"flow and air and point and discharge and sensor and total"                ,colMeta:{color:null} },

+              {target:"tot_eaFlow_sensor",           query:"flow and air and point and exhaust and sensor and total"                  ,colMeta:{color:null} },

+              {target:"znDiffFlow_sensor",           query:"flow and air and point and delta and zone"                                ,colMeta:{color:null} },

+    //valves

+              {target:"htgValve_cmd",                query:"point and cmd and valve and water and (reheat or hot)"                    ,colMeta:{color:null} },

+              {target:"clgValve_cmd",                query:"point and water and (cool or chilled) and cmd and valve"                  ,colMeta:{color:null} },

+              {target:"phtValve_cmd",                query:"point and preheat and cmd and valve"                                      ,colMeta:{color:null} },

+    //dampers

+              {target:"oaDmpr_cmd",                  query:"damper and air and point and outside and cmd"                             ,colMeta:{color:null} },

+              {target:"maDmpr_cmd",                  query:"damper and air and point and mixed and cmd"                               ,colMeta:{color:null} },

+              {target:"eaDmpr_cmd",                  query:"damper and air and point and exhaust and cmd"                             ,colMeta:{color:null} },

+              {target:"raDmpr_cmd",                  query:"damper and air and point and return and cmd"                              ,colMeta:{color:null} },

+              {target:"rlfDmpr_cmd",                 query:"damper and air and point and relief and cmd"                              ,colMeta:{color:null} },

+              {target:"dmpr_cmd",                    query:"damper and air and point and cmd"                                         ,colMeta:{color:null} },

+    //fans

+              {target:"rtnFan_speed",                query:"return and point and fan and speed and cmd"                               ,colMeta:{color:null} },

+              {target:"rtnFan_cmd",                  query:"return and point and fan and run and cmd"                                 ,colMeta:{color:null} },

+              {target:"rlfFan_speed",                query:"relief and point and fan and speed and cmd"                               ,colMeta:{color:null} },

+              {target:"rlfFan_cmd",                  query:"relief and point and fan and run and cmd"                                 ,colMeta:{color:null} },

+              {target:"exhFan_speed",                query:"exhaust and point and fan and speed and cmd"                              ,colMeta:{color:null} },

+              {target:"daFan_speed",                 query:"discharge and point and fan and speed and cmd"                            ,colMeta:{color:null} },

+              {target:"daFan_run",                   query:"discharge and point and fan and run and cmd"                              ,colMeta:{color:null} },

+

+    //humidity

+              {target:"daHumid_sensor",              query:"point and sensor and discharge and air and humidity"                      ,colMeta:{color:null} },

+              {target:"humidifier_cmd",              query:"point and air and cmd and humidifier and not output"                      ,colMeta:{color:null} },

+    //lab

+              {target:"savFlow_sensor",              query:"(discharge or supply) and point and flow and sensor"},

+    //other

+              {target:"occ_cmd",                     query:"occupied and point and cmd"},

+              {target:"znOcc_cmd",                   query:"occupied and point and cmd"},

+              {target:"labOcc_cmd",                  query:"occupied and point and cmd and lab"},

+              {target:"fumeHoodOcc_cmd",             query:"occupied and point and cmd and fumeHood"},

+              {target:"siteOcc_cmd",                 query:"occupied and sitePoint"},

+              {target:"ahu_state",                   query:"point and (ahu or ahuState)"},

+    //dual duct

+              {target:"hotDeck_daFlow_sensor",       query:"hotDeck and flow and air and point and discharge and sensor"},

+              {target:"hotDeck_daFlow_sp",           query:"hotDeck and flow and air and point and discharge and sp"},

+              {target:"hotDeck_dmpr_cmd",            query:"hotDeck and damper and air and point and cmd"},

+              {target:"hotDeck_dmpr_reset",          query:"hotDeck and damper and air and point and cmd and reset"},

+

+              {target:"coldDeck_daFlow_sensor",      query:"coldDeck and flow and air and point and discharge and sensor"},

+              {target:"coldDeck_daFlow_sp",          query:"coldDeck and flow and air and point and discharge and sp"},

+              {target:"coldDeck_dmpr_cmd",           query:"coldDeck and damper and air and point and cmd"},

+              {target:"coldDeck_dmpr_reset",         query:"coldDeck and damper and air and point and cmd and reset"},

+

+    //buildingHealth

+              {target:"site_eui",                     query:"point and eui and equipRef->buildingHealth"},

+              {target:"other_eui",                    query:"point and other and equipRef->buildingHealth"},

+              {target:"chw_eui",                      query:"point and chilledWater and euiContributor and equipRef->buildingHealth"},

+              {target:"elec_eui",                     query:"point and elec and euiContributor and equipRef->buildingHealth"},

+              {target:"hw_eui",                       query:"point and hotWater and euiContributor and equipRef->buildingHealth"},

+              {target:"naturalGas_eui",               query:"point and naturalGas and euiContributor and equipRef->buildingHealth"},

+              {target:"natGas_eui",                   query:"point and naturalGas and euiContributor and equipRef->buildingHealth"},

+              {target:"site_eci",                     query:"point and eci and equipRef->buildingHealth"},

+              {target:"site_uncomfortableVAVs",       query:"point and vavScorePt and uncomfortable and equipRef->buildingHealth"},

+              {target:"site_comfortableVAVs",         query:"point and vavScorePt and comfortable and equipRef->buildingHealth"},

+              {target:"site_equipLife",               query:"point and equipmentLife and equipRef->buildingHealth"},

+              {target:"site_buildingComfort",         query:"point and buildingScore and comfort and equipRef->buildingHealth"},

+              {target:"site_buildingArea",            query:"point and buildingArea and equipRef->buildingHealth"},

+    //elec meter

+              {target:"elec_intervalPwr",            query:"elec and power and interval and equipRef->siteMeter and not cost and not modelPoint  and not syntheticPoint and not savings"},

+              {target:"elec_modelPwr",               query:"elec and power and calculated and modelHisFunction and modelPoint and interval and equipRef->siteMeter"},

+              {target:"elec_monthlyEnergy",          query:"elec and energy and monthly and not cost and not rateCost and equipRef->siteMeter and not modelPoint and not savings"},

+              {target:"elec_modelEnergy",            query:"elec and energy and monthly and equipRef->siteMeter and modelPoint"},

+              {target:"elec_monthlyCost",            query:"elec and energy and monthly and equipRef->siteMeter and cost"},

+              {target:"elec_intervalCost",           query:"elec and power and interval and equipRef->siteMeter and cost and not savings"},

+              {target:"elec_monthlyRateCost",        query:"elec and energy and monthly and equipRef->siteMeter and rateCost"},

+              {target:"elec_intervalSavingsCost",    query:"elec and power and interval and equipRef->siteMeter and savings and cost"},

+              {target:"elec_savingsPwr",             query:"elec and power and interval and equipRef->siteMeter and savings and not cost"},

+              {target:"elec_monthlySavingsCost",     query:"elec and energy and monthly and equipRef->siteMeter and savings and cost"},

+              {target:"elec_savingsEnergy",          query:"elec and energy and monthly and equipRef->siteMeter and savings and not cost"},

+              {target:"elec_genAIModelDemand",       query:"elec and power and syntheticPoint and mlPoint and interval and equipRef->siteMeter"},

+    //natGas meter

+              {target:"natGas_intervalCons",         query:"naturalGas and consumption and interval and equipRef->siteMeter and not cost and not modelPoint and not syntheticPoint and not savings"},

+              {target:"natGas_intervalModelCons",    query:"naturalGas and consumption and calculated and modelHisFunction and modelPoint and interval and equipRef->siteMeter"},

+              {target:"natGas_monthlyCons",          query:"naturalGas and consumption and monthly and not cost and not rateCost and equipRef->siteMeter and not modelPoint and not savings"},

+              {target:"natGas_monthlyModelCons",     query:"naturalGas and consumption and monthly and equipRef->siteMeter and modelPoint"},

+              {target:"natGas_monthlyCost",          query:"naturalGas and consumption and monthly and equipRef->siteMeter and cost"},

+              {target:"natGas_intervalCost",         query:"naturalGas and consumption and interval and equipRef->siteMeter and cost and not savings"},

+              {target:"natGas_monthlyRateCost",      query:"naturalGas and consumption and monthly and equipRef->siteMeter and rateCost"},

+              {target:"natGas_intervalSavingsCost",  query:"naturalGas and consumption and interval and equipRef->siteMeter and savings and cost"},

+              {target:"natGas_intervalSavingsCons",  query:"naturalGas and consumption and interval and equipRef->siteMeter and savings and not cost"},

+              {target:"natGas_monthlySavingsCost",   query:"naturalGas and consumption and monthly and equipRef->siteMeter and savings and cost"},

+              {target:"natGas_monthlySavingsCons",   query:"naturalGas and consumption and monthly and equipRef->siteMeter and savings and not cost"},

+              {target:"natGas_genAIModelCons",       query:"naturalGas and consumption and interval and syntheticPoint and mlPoint and equipRef->siteMeter"},

+    //hot water meter

+              {target:"hotWater_intervalCons",         query:"hot and water and interval and equipRef->siteMeter and not cost and not modelPoint and not syntheticPoint and not savings"},

+              {target:"hotWater_intervalModelCons",    query:"hot and water and calculated and modelHisFunction and modelPoint and interval and equipRef->siteMeter"},

+              {target:"hotWater_monthlyCons",          query:"hot and water and monthly and not cost and not rateCost and equipRef->siteMeter and not modelPoint and not savings"},

+              {target:"hotWater_monthlyModelCons",     query:"hot and water and monthly and equipRef->siteMeter and modelPoint"},

+              {target:"hotWater_monthlyCost",          query:"hot and water and monthly and equipRef->siteMeter and cost"},

+              {target:"hotWater_intervalCost",         query:"hot and water and interval and equipRef->siteMeter and not savings and cost"},

+              {target:"hotWater_monthlyRateCost",      query:"hot and water and monthly and equipRef->siteMeter and rateCost"},

+              {target:"hotWater_intervalSavingsCost",  query:"hot and water and interval and equipRef->siteMeter and savings and cost"},

+              {target:"hotWater_intervalSavingsCons",  query:"hot and water and interval and equipRef->siteMeter and savings and not cost"},

+              {target:"hotWater_monthlySavingsCost",   query:"hot and water and monthly and equipRef->siteMeter and savings and cost"},

+              {target:"hotWater_monthlySavingsCons",   query:"hot and water and monthly and equipRef->siteMeter and savings and not cost"},

+              {target:"hotWater_genAIModelCons",       query:"hot and water and interval and syntheticPoint and mlPoint and equipRef->siteMeter"},

+    //chilled water meter

+              {target:"chilledWater_intervalCons",         query:"chilled and water and interval and equipRef->siteMeter and not cost and not modelPoint and not syntheticPoint and not savings"},

+              {target:"chilledWater_intervalModelCons",    query:"chilled and water and calculated and modelHisFunction and modelPoint and interval and equipRef->siteMeter"},

+              {target:"chilledWater_monthlyCons",          query:"chilled and water and monthly and not cost and not rateCost and equipRef->siteMeter and not modelPoint and not savings"},

+              {target:"chilledWater_monthlyModelCons",     query:"chilled and water and monthly and equipRef->siteMeter and modelPoint"},

+              {target:"chilledWater_monthlyCost",          query:"chilled and water and monthly and equipRef->siteMeter and cost"},

+              {target:"chilledWater_intervalCost",         query:"chilled and water and interval and equipRef->siteMeter and cost and not savings"},

+              {target:"chilledWater_monthlyRateCost",      query:"chilled and water and monthly and equipRef->siteMeter and rateCost"},

+              {target:"chilledWater_intervalSavingsCost",  query:"chilled and water and interval and equipRef->siteMeter and savings and cost"},

+              {target:"chilledWater_intervalSavingsCons",  query:"chilled and water and interval and equipRef->siteMeter and savings and not cost"},

+              {target:"chilledWater_monthlySavingsCost",   query:"chilled and water and monthly and equipRef->siteMeter and savings and cost"},

+              {target:"chilledWater_monthlySavingsCons",   query:"chilled and water and monthly and equipRef->siteMeter and savings and not cost"},

+              {target:"chilledWater_genAIModelCons",       query:"chilled and water and interval and syntheticPoint and mlPoint and equipRef->siteMeter"},

+    //steam meter

+              {target:"steam_intervalCons",         query:"steam and interval and equipRef->siteMeter and not cost and not modelPoint and not syntheticPoint and not savings"},

+              {target:"steam_intervalModelCons",    query:"steam and calculated and modelHisFunction and modelPoint and interval and equipRef->siteMeter"},

+              {target:"steam_monthlyCons",          query:"steam and monthly and not cost and not rateCost and equipRef->siteMeter and not modelPoint and not savings"},

+              {target:"steam_monthlyModelCons",     query:"steam and monthly and equipRef->siteMeter and modelPoint"},

+              {target:"steam_monthlyCost",          query:"steam and monthly and equipRef->siteMeter and cost"},

+              {target:"steam_intervalCost",         query:"steam and interval and equipRef->siteMeter and cost and not savings"},

+              {target:"steam_monthlyRateCost",      query:"steam and monthly and equipRef->siteMeter and rateCost"},

+              {target:"steam_intervalSavingsCost",  query:"steam and interval and equipRef->siteMeter and savings and cost"},

+              {target:"steam_intervalSavingsCons",  query:"steam and interval and equipRef->siteMeter and savings and not cost"},

+              {target:"steam_monthlySavingsCost",   query:"steam and monthly and equipRef->siteMeter and savings and cost"},

+              {target:"steam_monthlySavingsCons",   query:"steam and monthly and equipRef->siteMeter and savings and not cost"},

+              {target:"steam_genAIModelCons",       query:"steam and interval and syntheticPoint and mlPoint and equipRef->siteMeter"},

+    //water meter

+              {target:"water_intervalCons",         query:"water and not chilled and not hot and consumption and interval and equipRef->siteMeter and not cost and not modelPoint and not savings"},

+

+    //hot water

+              {target:"hw_delta_press",             query:"hot and water and pressure and delta and sensor"},

+              {target:"hw_flow",                    query:"hot and water and flow and sensor"},

+              {target:"hw_pri_pump_speed",          query:"hot and water and pump and speed and cmd and primary"},

+              {target:"hw_pri_pump_status",         query:"hot and water and pump and run and cmd and primary"},

+              {target:"hw_sec_pump_speed",          query:"hot and water and pump and speed and cmd and secondary"},

+              {target:"hw_sec_pump_status",         query:"hot and water and pump and run and cmd and secondary"},

+              {target:"hw_pri_pump_power",          query:"hot and water and pump and power and sensor and primary"},

+              {target:"hw_sec_pump_power",          query:"hot and water and pump and power and sensor and secondary"},

+              {target:"hw_supply_temp",             query:"hot and water and supply and temp and sensor"},

+              {target:"hw_return_temp",             query:"hot and water and return and temp and sensor and not domestic"},

+              {target:"hw_supply_temp_sp",          query:"hot and water and supply and temp and sp"},

+              {target:"hw_delta_press_sp",          query:"hot and water and pressure and delta and sp"},

+              {target:"hw_delta_temp",              query:"hot and water and temp and delta and sensor"},

+              {target:"hw_sys_load",                query:"hot and water and load and energy"},

+              {target:"hw_bypass_valve",            query:"hot and water and bypass and valve and cmd"},

+

+    //chilled water

+              {target:"chw_delta_press",             query:"chilled and water and pressure and delta and sensor"},

+              {target:"chw_flow",                    query:"chilled and water and flow and sensor and not bypass"},

+              {target:"chw_pri_pump_speed",          query:"chilled and water and pump and speed and cmd and primary"},

+              {target:"chw_sec_pump_speed",          query:"chilled and water and pump and speed and cmd and secondary"},

+              {target:"chw_pri_pump_status",         query:"chilled and water and pump and run and cmd and primary"},

+              {target:"chw_sec_pump_status",         query:"chilled and water and pump and run and cmd and secondary"},

+              {target:"chw_pri_pump_power",          query:"chilled and water and pump and power and sensor and primary"},

+              {target:"chw_sec_pump_power",          query:"chilled and water and pump and power and sensor and secondary"},

+            //{target:"chw_pri_pump_status",         query:"chilled and water and pump and run and cmd and primary"},

+            //{target:"chw_sec_pump_status",         query:"chilled and water and pump and run and cmd and secondary"},

+              {target:"chw_supply_temp",             query:"chilled and water and supply and temp and sensor"},

+              {target:"chw_return_temp",             query:"chilled and water and return and temp and sensor"},

+              {target:"chw_supply_temp_sp",          query:"chilled and water and supply and temp and sp"},

+              {target:"chw_delta_press_sp",          query:"chilled and water and pressure and delta and sp"},

+              {target:"chw_delta_temp",              query:"chilled and water and temp and delta and sensor"},

+              {target:"chw_sys_load",                query:"chilled and water and load and energy"},

+

+     //General Pump Points

+              {target:"pri_pump_speed",              query:"water and pump and speed and cmd and primary"},

+              {target:"sec_pump_speed",              query:"water and pump and speed and cmd and secondary"},

+              {target:"pri_pump_status",             query:"water and pump and run and cmd and primary"},

+              {target:"sec_pump_status",             query:"water and pump and run and cmd and secondary"},

+              {target:"pri_pump_power",              query:"water and pump and power and sensor and primary"},

+              {target:"sec_pump_power",              query:"water and pump and power and sensor and secondary"},

+

+     //chillers

+              {target:"chlr_status",                   query:"chiller and cmd and run"},

+              {target:"chlr_power",                    query:"chiller and power and sensor"},

+              {target:"chlr_entering_temp",            query:"chiller and chilled and entering and temp and water and sensor and not condenser"},

+              {target:"chlr_leaving_temp",             query:"chiller and chilled and leaving and temp and water and sensor and not condenser"},

+              {target:"chlr_flow",                     query:"chiller and chilled and flow and water and sensor"},

+              {target:"chlr_fla",                      query:"chiller and fla and sensor"},

+              {target:"chlr_cw_entering_temp",         query:"chiller and condenser and entering and temp and water and sensor"},

+              {target:"chlr_cw_leaving_temp",          query:"chiller and condenser and leaving and temp and water and sensor"},

+

+     //boilers

+              {target:"blr_firing_rate",              query:"boiler and firingRate and sensor"},

+              {target:"blr_entering_temp",            query:"boiler and hot and (entering or return) and temp and water and sensor"},

+              {target:"blr_leaving_temp",             query:"boiler and hot and leaving and temp and water and sensor"},

+              {target:"blr_leaving_temp_sp",          query:"boiler and hot and leaving and temp and water and sp"},

+              {target:"blr_run_status",               query:"boiler and run and cmd"},

+              {target:"blr_enable",                   query:"boiler and cmd and enable"},

+              {target:"blr_gas_flow_rate",            query:"boiler and flow and rate and sensor and naturalGas"},

+              {target:"blr_flow",                     query:"boiler and notImplemented"},

+              {target:"blr_power",                    query:"boiler and power and sensor"},

+

+

+     //cooling tower

+              {target:"clgtwr_leaving_temp",         query:"coolingTower and temp and (leaving or supply) and water and sensor"},

+              {target:"clgtwr_entering_temp",        query:"coolingTower and temp and (entering or return) and water and sensor"},

+              {target:"clgtwr_fan_speed",            query:"coolingTower and fan and speed and air and cmd"},

+              {target:"clgtwr_power",                query:"coolingTower and power and sensor"},

+              {target:"clgtwr_fan_status",           query:"coolingTower and cmd and run"},

+

+

+     //condenser water

+              {target:"cws_supply_temp",             query:"condenser and water and supply and temp and sensor"},

+              {target:"cws_supply_temp_sp",          query:"condenser and water and supply and temp and sp"},

+              {target:"cws_return_temp",             query:"condenser and water and return and temp and sensor"},

+

+     //miscellaneous

+              {target:"wet_bulb",                    query:"temp and wetBulb"},

+              {target:"oah_sensor",                  query:"humidity and air and point and outside and sensor"},

+

+     // Heat Recovery Coil

+       // Discharge

+              {target:"disHRC_ea_sensor",            query:"heatRecovery and coil and air and temp and sensor and discharge and entering"},

+              {target:"disHRC_la_sensor",            query:"heatRecovery and coil and air and temp and sensor and discharge and leaving"},

+              {target:"disHRC_flow_sensor",          query:"heatRecovery and coil and air and flow and sensor and discharge"},

+              {target:"disHRC_valve_cmd",            query:"heatRecovery and coil and water and valve and cmd and discharge"},

+       // Exhaust

+              {target:"exHRC_ea_sensor",             query:"heatRecovery and coil and air and temp and sensor and exhaust and entering"},

+              {target:"exHRC_la_sensor",             query:"heatRecovery and coil and air and temp and sensor and exhaust and leaving"},

+              {target:"exHRC_flow_sensor",           query:"heatRecovery and coil and air and flow and sensor and exhaust"},

+              {target:"exHRC_valve_cmd",             query:"heatRecovery and coil and water and valve and cmd and exhaust"},

+

+     // Heat Recovery Wheel

+              {target:"disHRW_ea_sensor",            query:"heatRecovery and wheel and air and temp and sensor and discharge and entering"},

+              {target:"disHRW_la_sensor",            query:"heatRecovery and wheel and air and temp and sensor and discharge and leaving"},

+              {target:"disHRW_flow_sensor",          query:"heatRecovery and wheel and air and flow and sensor and discharge"},

+

+              {target:"exHRW_ea_sensor",             query:"heatRecovery and wheel and air and temp and sensor and exhaust and entering"},

+              {target:"exHRW_la_sensor",             query:"heatRecovery and wheel and air and temp and sensor and exhaust and leaving"},

+              {target:"exHRW_flow_sensor",           query:"heatRecovery and wheel and air and flow and sensor and exhaust"},

+

+              {target:"hrw_status",                  query:"heatRecovery and wheel and sensor and run"},

+              {target:"hrw_cmd",                     query:"heatRecovery and wheel and cmd"},

+

+     // Lab Spaces

+              {target:"ach",                         query:"ach and air and sensor"},

+              {target:"sash_cmd",                    query:"sash and cmd and fumeHood"},

+

+     //Coil Temps

+             //hot

+             {target:"chw_entg_temp",                query:"chilled and water and valve"},

+             {target:"chw_lvg_temp",                 query:"humidity and air and point and outside and sensor"},

+

+             //chilled

+             {target:"hw_entg_temp",                 query:"temp and wetBulb"},

+             {target:"hw_lvg_temp",                  query:"humidity and air and point and outside and sensor"},

+

+     //Baseline Search

+             {target:"baseline_calc",                query:"point and calculated and modelPoint and not cost and not savings"},

+

+     //Utility Level Points for Dashboards

+             //Elec

+             {target:"portfolioDashboard_elecUsage",        query:"portfolioDashboardPoint and elec and not cost and not savings"},

+             {target:"portfolioDashboard_elecCost",         query:"portfolioDashboardPoint and elec and cost and not savings"},

+             {target:"portfolioDashboard_elecSavingsCost",  query:"portfolioDashboardPoint and elec and cost and savings"},

+             {target:"portfolioDashboard_elecSavings",      query:"portfolioDashboardPoint and elec and usage and savings and not cost"},

+

+             //Steam

+             {target:"portfolioDashboard_steamUsage",        query:"portfolioDashboardPoint and steam and not cost and not savings"},

+             {target:"portfolioDashboard_steamCost",         query:"portfolioDashboardPoint and steam and cost and not savings"},

+             {target:"portfolioDashboard_steamSavingsCost",  query:"portfolioDashboardPoint and steam and cost and savings"},

+             {target:"portfolioDashboard_steamSavings",      query:"portfolioDashboardPoint and steam and usage and savings and not cost"},

+

+             //Chilled Water

+             {target:"portfolioDashboard_chwUsage",        query:"portfolioDashboardPoint and chw and not cost and not savings"},

+             {target:"portfolioDashboard_chwCost",         query:"portfolioDashboardPoint and chw and cost and not savings"},

+             {target:"portfolioDashboard_chwSavingsCost",  query:"portfolioDashboardPoint and chw and cost and savings"},

+             {target:"portfolioDashboard_chwSavings",      query:"portfolioDashboardPoint and chw and usage and savings and not cost"},

+

+             //Hot Water

+             {target:"portfolioDashboard_hwUsage",        query:"portfolioDashboardPoint and hw and not cost and not savings"},

+             {target:"portfolioDashboard_hwCost",         query:"portfolioDashboardPoint and hw and cost and not savings"},

+             {target:"portfolioDashboard_hwSavingsCost",  query:"portfolioDashboardPoint and hw and cost and savings"},

+             {target:"portfolioDashboard_hwSavings",      query:"portfolioDashboardPoint and hw and usage and savings and not cost"},

+           ].toGrid

+

+

+  if(show) return queries

+

+  match: queries.find(r => r.get("target")==target)

+  if (match.isNull) throw "Cannot find query: "+target

+

+  out: if (colMeta) match.get("colMeta")

+       else         match.get("query")

+

+  return out

 end
stackedAreaChart
--- stackedAreaChart (pod)
+++ stackedAreaChart (local)
@@ -1,18 +1,57 @@
-(data, opts: {}) => do

-  cipherText :

-    "AxD:38dDwRQ-pgdqgJiurXebHQ::mhXfpADGbIC7GxoTqRSqdV464RPnMp4CLuc657aoHaKd9_HM1D4a" +

-    "HIskOslaBmvn9jSqOhTYxcOCs_3woqg599Elx2Fcyc1JBY-NqdgSwE8XsMz7_c1Wz8fyLkddRGT1pzE_" +

-    "kw5hFRL3Sr_Q8-KUBzP8fTjHvzOitZVKQDCcmThHuvxjhdgAtGmeIwmHXrzAEOxrOicz38xgiAcerjFi" +

-    "CpxfehBlJt-8borMQX1bT-eBm-mTZqCbijfc1guvAfAQscQLYCYuTD5cpMps-6wkk2689l5Z7eTAehHB" +

-    "v6mxCJS_95jACQ_Kez_os1YNCoNMO45G-fkjaKq-Jhu8BfrxOMxAm4GKBGVQUE-hmd9ktMBgmzjx_k_R" +

-    "csMl5bYyW8CsJ38vK4TOIit_9K8vKk4BK2aWwqOeca1VB16VFgt3_K53nqDujbk6zLct1BSPZVrUL9_B" +

-    "RTqmKKAIiHUR57r7R84k1J5C57XfI_Bk-3EcqyUVrrfHZPdxiYUMEsMXgiODwQ8HMdO4pz7lO9Vjuti8" +

-    "nqXRwMZm3o3x7DKDAQdRHSaawNBUrJnBpM2VKmA-ZcEud5xZZQl177mVpVtxMFRxllIJxBRJ864BVXxD" +

-    "rzwWf-UyEcxauEhCcTZYqJSgIWzuTydLATJlBjQLceRRGcNo0TKcxwbZ-4H1bOa5pK-bmQKz7AI7FBNB" +

-    "5IcHwyPZXydqmnIQ44Y93vyeA1GzOyyVL_IvI2_16095Rklz96bRvj654lLJaKwTCcKpcZX7hMwovOTj" +

-    "J_utAqV0DN2mr2U-t1ImzPNCNF2umqB799KwX1AZkHikHM-9HVfliUKCndI51prCfo2Axd7aERcdkOxv" +

-    "Xz5g3wgMzTBskd5UdgYG9YQ4kMEQauanpxvKe_ySHg7vy3_zWq1TXZJSjRKt4lKUJRSfbwuB909fW26j" +

-    "-j9CRykqL9T-ArZqZ02B455KDjUyAkFw8XtYRxjLHkYyNII5c7EmNvL6Kl8_trUYcbiA8iA"

-  keyFunc : () => "ddJNIRm2qio2Z7CXpKADiw"

-  return afAxonEncryptorRt_callFunc("stackedAreaChart", cipherText, [data, opts], keyFunc)

+(data, opts:{}) => do

+

+  //STACK FUNCTION

+  stack: (data) => do

+    // map column names to their index

+    colNames: data.colNames

+    colNameToIndex: {}

+    colNames[1..-1].each((n,i) => do

+      colNameToIndex = colNameToIndex.set(n, i+1)

+    end)

+

+    // map the data cells to the sum of values to the left

+    stream: data.stream.map(r => do

+      r.map((v, n) => do

+        if (n == "ts" or n=="time") return v

+        sum: 0

+        index: colNameToIndex[n]

+        (1..index).each(i => sum = sum + r[colNames[i]])

+        return sum

+      end)

+    end)

+

+    // add area mode tag to value columes

+    colNames[1..-1].each((c,i) => do

+      mode: if (i == 0) "zero" else "prevSeries"

+      clmMeta: if (opts.has("removeColor")) {chartAreaMode:mode, -color}

+               else {chartAreaMode:mode}

+      stream = stream.addColMeta(c,clmMeta)

+    end)

+

+    // ensure column order of input data

+    return stream.addMeta({chartLegendNoSort}).reorderCols(colNames).collect(toGrid)

+  end

+

+  //check incoming grid for empty columns

+  //filter empty colms

+   if (data.cols.any(colm => data.colToList(colm).all(v=>v==null))) do

+     nonEmptyCols: data.cols.findAll(colm => not data.colToList(colm).all(v=>v==null))

+     data = data.keepCols(nonEmptyCols)

+     end

+

+  //check how many unique units

+  units: data.cols.map(r => r.meta.get("unit")).findAll(v=>v!=null).unique

+  unitCount: units.size

+

+  if (unitCount<2) return data.stack()

+  else do

+    out: []

+    units.each u => do

+      unitCols: data.cols.findAll(c=>c.meta.get("unit")==u)

+      unitData: data.keepCols(["ts"].addAll(unitCols))

+      out=out.add(unitData.stack())

+    end

+    return out.hisJoin

+  end

+

 end
kwLinkegratorExt
setIgnoredTags
--- setIgnoredTags (pod)
+++ setIgnoredTags (local)
@@ -1,10 +1,3 @@
 () => do

-  cipherText :

-  """AxD:Yxba0aIY9Y5BKvFonyFhAQ::BtgrCvguDM6BZW0ICtmF0cD4evFw471bebhtHhIy1K8RTeLq6tL0

-     SJr_hQfimBgLDmfLzNoOdBZ2-SVOsFjM9oz5iYgXoxAPfxoLWDlTRgS-bigO-p1P0hSeVxJ1yEoyVvx3

-     NnLH1R6QO4AtEgHgk6T6GGRbTwPwBPoAcOJ233Fdj_wKuiOFsuCLCG5YJ_e6A6uAhre_POD25IjZpNej

-     3WMNzKuTOr6oamZQs4aS4yqXPS7bddsCzncgbn0YJaOR6mIBLdT8PbY1BnSoV0LIjwa8IVemqeVLF89v

-     bkfHFw0h9HD-j3lOxjpE_0voXJzI"""

-  keyFunc : () => "uF_n7EQ34krJ1dsJbt7y5w"

-  return afAxonEncryptorRt_callFunc("setIgnoredTags", cipherText, [], keyFunc)

+  ["clarificationNeeded","manualReview","clarificationComment","clarificationComment_kw","notDuplicate","g36DashReady","emcsPoint"]

 end
kwLinkEnergyAgentExt
construct_ts

Sources are identical.

kpi_siteMeter_actualElecKwh
--- kpi_siteMeter_actualElecKwh (pod)
+++ kpi_siteMeter_actualElecKwh (local)
@@ -10,7 +10,7 @@
   target:              {}

   date:                {}

   out:                 {readonly}

-  elec_intervalPwr:    {bind:"elec and power and interval and equipRef->siteMeter and not cost and not modelPoint and not savings and equipRef=={{target->id}}"}

+  elec_intervalPwr:    {bind:"elec and power and interval and equipRef->siteMeter and not cost and not modelPoint and not syntheticPoint and not savings and equipRef=={{target->id}}"}

   opts:                {bindTuning:"opts",      defVal:{}}

 

   do

kpi_siteMeter_actualSteamKlbs
--- kpi_siteMeter_actualSteamKlbs (pod)
+++ kpi_siteMeter_actualSteamKlbs (local)
@@ -10,7 +10,7 @@
   target:              {}

   date:                {}

   out:                 {readonly}

-  steam_intervalCons:  {bind:"steam and interval and equipRef->siteMeter and not cost and not modelPoint and not savings and equipRef=={{target->id}}"}

+  steam_intervalCons:  {bind:"steam and interval and equipRef->siteMeter and not cost and not modelPoint and not syntheticPoint and not savings and equipRef=={{target->id}}"}

   opts:                {bindTuning:"opts",      defVal:{}}

 

   do

kpi_siteMeter_actualchwTonHrs
--- kpi_siteMeter_actualchwTonHrs (pod)
+++ kpi_siteMeter_actualchwTonHrs (local)
@@ -10,7 +10,7 @@
   target:                    {}

   date:                      {}

   out:                       {readonly}

-  chilledWater_intervalCons: {bind:"chilled and water and interval and equipRef->siteMeter and not cost and not modelPoint and not savings and equipRef=={{target->id}}"}

+  chilledWater_intervalCons: {bind:"chilled and water and interval and equipRef->siteMeter and not cost and not modelPoint and not syntheticPoint and not savings and equipRef=={{target->id}}"}

   opts:                      {bindTuning:"opts",      defVal:{}}

 

   do

kpi_siteMeter_maxChwsDemand
--- kpi_siteMeter_maxChwsDemand (pod)
+++ kpi_siteMeter_maxChwsDemand (local)
@@ -10,7 +10,7 @@
   target:                    {}

   date:                      {}

   out:                       {readonly}

-  chilledWater_intervalCons: {bind:"chilled and water and interval and equipRef->siteMeter and not cost and not modelPoint and not savings and equipRef=={{target->id}}"}

+  chilledWater_intervalCons: {bind:"chilled and water and interval and equipRef->siteMeter and not cost and not modelPoint and not syntheticPoint and not savings and equipRef=={{target->id}}"}

   opts:                      {bindTuning:"opts",      defVal:{}}

 

   do

kpi_siteMeter_maxElecDemand
--- kpi_siteMeter_maxElecDemand (pod)
+++ kpi_siteMeter_maxElecDemand (local)
@@ -10,7 +10,7 @@
   target:              {}

   date:                {}

   out:                 {readonly}

-  elec_intervalPwr:    {bind:"elec and power and interval and equipRef->siteMeter and not cost and not modelPoint and not savings and equipRef=={{target->id}}"}

+  elec_intervalPwr:    {bind:"elec and power and interval and equipRef->siteMeter and not cost and not modelPoint and not syntheticPoint and not savings and equipRef=={{target->id}}"}

   opts:                {bindTuning:"opts",      defVal:{}}

 

   do

kpi_siteMeter_maxSteamDemand
--- kpi_siteMeter_maxSteamDemand (pod)
+++ kpi_siteMeter_maxSteamDemand (local)
@@ -10,7 +10,7 @@
   target:              {}

   date:                {}

   out:                 {readonly}

-  steam_intervalCons:  {bind:"steam and interval and equipRef->siteMeter and not cost and not modelPoint and not savings and equipRef=={{target->id}}"}

+  steam_intervalCons:  {bind:"steam and interval and equipRef->siteMeter and not cost and not modelPoint and not syntheticPoint and not savings and equipRef=={{target->id}}"}

   opts:                {bindTuning:"opts",      defVal:{}}

 

   do

logic_anomaly_anomalyImpact
--- logic_anomaly_anomalyImpact (pod)
+++ logic_anomaly_anomalyImpact (local)
@@ -28,4 +28,63 @@
 // end

 // // Double checking logic

 // return {totalUsage:totalUsage, totalKWH:totalKWH}

-//
+// ------------------------------------------------------------------------

+// // Case 2: For a random gas meter (fails and throws err)

+// logic_elecMeter_usage(@p:kwLink_energyAgent:r:2ee9da19-8d561eae, 2023)

+// ------------------------------------------------------------------------

+

+(meter, dates, opts:{}, pointOverrides:{}) => do

+

+  //////////////////////////////////////////////////////////////////////////////////////////

+  // NORMALIZE + VALIDATE INPUTS

+  //////////////////////////////////////////////////////////////////////////////////////////

+

+  if (meter.isNull) return "meter point missing"

+  meterRec: meter.toRec

+  meterId: meterRec.get("id")

+  dates = dates.toSpan

+

+  //////////////////////////////////////////////////////////////////////////////////////////

+  // OPTS

+  //////////////////////////////////////////////////////////////////////////////////////////

+

+  debug: opts.optNorm("debug", "Disable").lower // Available options: "all anomalies", "filtered anomalies", "filtered anomaly count"

+

+  //////////////////////////////////////////////////////////////////////////////////////////

+  // LOGIC

+  //////////////////////////////////////////////////////////////////////////////////////////

+

+  // Points

+  allAnomalies: readAll(anomaly and targetVarRef==meterId)

+    if (debug=="all anomalies")   return allAnomalies

+

+  // Only keep the anomalies within the selected span

+  filteredAnomalies: allAnomalies.findAll(v=> v.get("ts") >= dates.start and v.get("ts") < dates.end )  // Robust: check if ts + duration is outside dates.end? Would we still count it as an anomaly for this span?

+    if (debug=="filtered anomalies")           return filteredAnomalies

+    if (debug=="filtered anomaly count")       return filteredAnomalies.size

+

+  // Calculate total anomaly impact over the selected span

+  totalAnomalyImpact: 0

+

+  filteredAnomalies.each() r=> do

+    anomalyImpact: r.get("anomalyImpactLikely")

+    totalAnomalyImpact = if (anomalyImpact >= 0) totalAnomalyImpact + anomalyImpact else if (anomalyImpact < 0) totalAnomalyImpact  // workaround for now. TODO: Update the anomaly detection funcs to include +ve only anomalies.

+  end

+

+

+    if (debug=="enable") return {errList: ["TODO: Add potential errors above in try catch statements and list them out here"], powerGrid:powerGrid, usageGrid:usageGrid, totalUsage:totalUsage}

+

+  // TODO: Add potential errors above in try catch statements

+

+  //////////////////////////////////////////////////////////////////////////////////////////

+  // FORMAT

+  //////////////////////////////////////////////////////////////////////////////////////////

+

+

+  //////////////////////////////////////////////////////////////////////////////////////////

+  //RETURN RESULT

+  //////////////////////////////////////////////////////////////////////////////////////////

+

+  return totalAnomalyImpact

+

+end
logic_energyAgent_meterPointToPredictionsChart
--- logic_energyAgent_meterPointToPredictionsChart (pod)
+++ logic_energyAgent_meterPointToPredictionsChart (local)
@@ -49,14 +49,8 @@
   //////////////////////////////////////////////////////////////////////////////////////////

 

   meterHisGrid: meterPointRef.hisRead(dates)

-  modelPoint: try read(syntheticPoint and pointRef==meterPointRef).toRec catch null

-    //akTODO: Just return Meter history if model point missing

-    if (modelPoint.isNull) do

-      //out: meterPointRef.hisRead(dates)

-      gridMeta: {title: "Meter history for "+meterPointRec.get("navName"),

-                 subtitle: "(Run model to show predictions)"}

-      return meterHisGrid.addColMeta("v0", {dis:"Meter readings", strokeWidth:1, chartGroup: "modelFit", chartType:"line"}).addMeta(gridMeta)//

-    end

+  modelPoint: try read(syntheticPoint and pointRef==meterPointRef, false).toRec catch null

+    if (modelPoint.isNull) return {info: modelPoint}.toGrid.addMeta({noData: "Model point missing for the selected utility."}).addColMeta("info", {dis: "Information"})

   modelPointRef: modelPoint.get("id")

 

   // Retrieve the detailed model results

@@ -72,6 +66,7 @@
   end

 

   modelPredictionsGrid = modelPredictionsGrid.removeCol("v0").reorderKeepCols(["ts"]).filter((ts>dates.start) and (ts<dates.end)).addMeta({hisStart: dates.start, hisEnd: dates.end})

+

 

   modelPredictionsGrid = hisJoin([modelPredictionsGrid, meterHisGrid]).reorderKeepCols(["ts", "v0"])

   // akTODO: add spanStart to modelPointStart meter his

logic_kwModeling_anomalyDetection_simPreview_v2
--- logic_kwModeling_anomalyDetection_simPreview_v2 (pod)
+++ logic_kwModeling_anomalyDetection_simPreview_v2 (local)
@@ -88,4 +88,63 @@
 

   // ReadHis for meter and model points

   data : points.hisRead(dateSpan, {-limit})

-               .hisRollup(avg, 1h)         //
+               .hisRollup(avg, 1h)         // ------- Double check logic robustness for all units. Should we use hisRollupAuto (1h) instead? Can it handle 15 minute kW/kWh data both?

+               .hisClip

+//return data

+//////// Add checks: hisStart and hisEnd for both points against the extended span

+

+  requiredCols: ["ts", "v0", "v1"]

+  if (requiredCols.any(v => data.missing(v) == true)) return [].toGrid.addMeta({noData: "Missing Data Error. Missing required column(s): " + requiredCols.findAll(v=>his.missing(v) == true)})

+  // hisRead create a grid that has v1 col even if the col doesn't have any data. So the above condition doesn't fail.

+

+  if (not (data.col("v0").meta.has("hisStart") or data.col("v0").meta.has("hisEnd"))) return blankChart("Meter Point missing hisStart or hisEnd tags. Might be missing his data for the selected span.")

+  if (not (data.col("v1").meta.has("hisStart") or data.col("v1").meta.has("hisEnd"))) return blankChart("Model Point missing hisStart or hisEnd tags. Might be missing his data for the selected span.")

+

+  if (data.col("v0").meta.get("hisStart") > dateSpan.start) return "Meter Point his data start date (" + data.col("v0").meta.get("hisStart").date.toStr + ") is later than selected span start date. Select a span that begins a week after this date."

+  if (data.col("v1").meta.get("hisStart") > dateSpan.start) return "Model Point his data start date (" + data.col("v1").meta.get("hisStart").date.toStr + ") is later than selected span start date. Select a span that begins a week after this date."

+

+  if (data.col("v0").meta.get("hisEnd") < dateSpan.end) return "Meter Point his data end date (" + data.col("v0").meta.get("hisEnd").date.toStr + ") is before the selected span end date. Select a span that ends before this date."

+  if (data.col("v1").meta.get("hisEnd") < dateSpan.end) return "Model Point his data end date (" + data.col("v1").meta.get("hisEnd").date.toStr + ") is before the selected span end date. Select a span that ends before this date."

+

+

+//////// Get confidence interval data

+

+  // Get confidence intervals for the model point

+  confIntervals: logic_kwModeling_getModelPointResults(modelPoint).reorderKeepCols(["ts"])

+    if (confIntervals.isStr) return confIntervals

+    if (confIntervals.first.get("ts") > dateSpan.start) return "The results file his data start date (" + confIntervals.first.get("ts").date.toStr + ") is later than selected span start date. Select a span that begins a week after this date."

+

+  // Filter for extendedDateSpan and update meta based on extendedSpan (needed for processing results)

+  filteredConfIntervals: confIntervals.findAll() r=> (r.get("ts") >= dateSpan.start and r.get("ts") < dateSpan.end)

+  filteredConfIntervals= filteredConfIntervals.addMeta({hisStart:dateSpan.start, hisEnd: dateSpan.end})

+//return confIntervals

+

+  // hisJoin meter data, predictions and confidence intervals

+  out : hisJoin([data, filteredConfIntervals]).reorderKeepCols(["ts", "v0", "v1"])

+//return out

+//////// Find and flag different types of anomalies

+

+  // Process hisGrid to find anomalous periods and their durations Send to

+  out = out.logic_kwModeling_anomalyDetection_processSimResults_v2(sensitivity, opts)

+    if (out.isStr) return out

+

+  // Filter for selected dateSpan

+  out = out.findAll() r=> (r.get("ts") >= dateSpan.start and r.get("ts") < dateSpan.end)

+

+

+

+  //////////////////////////////////////////////////////////////////////////////////////////

+  // FORMAT

+  //////////////////////////////////////////////////////////////////////////////////////////

+

+  // Update meta based on actual selected Span

+  out = out.addMeta({hisStart:dateSpan.start, hisEnd: dateSpan.end})

+

+

+  //////////////////////////////////////////////////////////////////////////////////////////

+  //RETURN RESULT

+  //////////////////////////////////////////////////////////////////////////////////////////

+

+  return out

+

+end
logic_meter_dataStagnant

Sources are identical.

spark_chilledWaterMeter_dataGaps

Sources are identical.

spark_chilledWaterMeter_dataStagnant

Sources are identical.

spark_elecMeter_dataGaps

Sources are identical.

spark_elecMeter_dataStagnant

Sources are identical.

spark_gasMeter_dataGaps

Sources are identical.

spark_hotWaterMeter_dataGaps

Sources are identical.

spark_hotWaterMeter_dataStagnant

Sources are identical.

spark_steamMeter_dataGaps

Sources are identical.

spark_steamMeter_dataStagnant

Sources are identical.

task_energyAgent_anomalyArcOrchestrator
--- task_energyAgent_anomalyArcOrchestrator (pod)
+++ task_energyAgent_anomalyArcOrchestrator (local)
@@ -1,10 +1,10 @@
 (opts: {syncDuration: 1wk, testMode:false}) => do

 

   // Sync all model points

-  callTask("task_energyAgent_syncGenAIModels", [], true)

+  //callTask("task_energyAgent_syncGenAIModels", [], true)

 

   // Delete old anomaly recs

-  readAll(anomaly).colToList("id").recDelete

+  try readAll(anomaly or anomalyTrigger).colToList("id").recDelete catch null

 

   // Find triggers and anomalies

   callTask("task_energyAgent_syncDataAndFindAnomalyTriggers", [opts], true)

task_energyAgent_syncDataAndFindAnomalyTriggers
--- task_energyAgent_syncDataAndFindAnomalyTriggers (pod)
+++ task_energyAgent_syncDataAndFindAnomalyTriggers (local)
@@ -2,7 +2,7 @@
 

   syncDuration: opts.optNorm("syncDuration", 1wk)

 

-  modelPoints: readAll(syntheticPoint and modelType=="adaptiveBaseline" and hisSize)

+  modelPoints: readAll(syntheticPoint and modelType=="adaptiveBaseline" and hisSize).toRecList

 

   modelPoints.each() mp => do

     //Get Meter Point

@@ -11,31 +11,29 @@
     meterHisEnd   : meterPoint.get("hisEnd")

     logInfo("energyAgentTask", "EnergyAgent Evaluating model for "+meterPoint.dis)

 

-    // TODO?: Add check for modelPoint hisEnd, compare to old/new hisEnds. Include the following code chunk in a conditional as well?

-    // modelPoint => virtualSyncHis

-    modelPointRef: mp.get("id")

-    modelEndDate: mp.get("hisEnd")

+    modelPointRef : mp.get("id")

+    modelHisEnd   : mp.get("hisEnd")

 

+    siteTz: meterPoint.get("siteRef").toRec.get("tz")

+    // Overwritten date on Cornell. Might just default to past month for the pod

+    anomalyFindingStartDate: parseDateTime("2025-08-01 00:00:00", "YYYY-MM-DD hh:mm:SS", siteTz)

     // Add check for anomalyArc status, only find new triggers if no open arcs exist.

-    // akTODO: Also check if triggers exist. Figure out the unique conditional

+    //========================================================> Also check if triggers exist. Figure out the unique conditional

     // Otherwise need to delete triggers before each run

-    openArcs: readAll(anomalyArc and arcOn==meterPointRef).findAll(v=>v.get("anomalyArcState")!="resolved")

-    // Only map through the meterPoints that got updated?

-    //if(newHisEnd.date > oldHisEnd.date and openArcs.size == 0) do

+    //openArcs: readAll(anomalyArc and arcOn==meterPointRef).findAll(v=>v.get("anomalyArcState")!="resolved")

+    // No more auto creating arcs (might want to fit this in again once we finalize the MBCx pipeline anomaly arc creation

+    // Only map through the meterPoints that got updated? //if(newHisEnd.date > oldHisEnd.date and openArcs.size == 0) do

 

-    if(openArcs.size == 0) do

+    if(modelHisEnd>anomalyFindingStartDate and meterHisEnd>anomalyFindingStartDate) do

       //Find and commit anomalyTriggers

-      //dateSpan: (oldHisEnd..newHisEnd).toSpan

-      modelPointHisEnd: mp.get("hisEnd")

-      targetPointHisEnd: mp.get("pointRef")->hisEnd

-      modelEnd: if(modelPointHisEnd < targetPointHisEnd) modelPointHisEnd else targetPointHisEnd

 

-      dateSpan: (modelEnd-3mo..modelEnd).toSpan // (Only finds triggers/anomalies for the past 3 months. Anchor a start date instead? akTODO: potentially YES, different for different projects)

-      sensitivityOpts: {minAnomalyDuration:24h, highAnomalyThreshold:10%, sustainedAnomalyThreshold:24h}

-

+      modelEnd: if(modelHisEnd < meterHisEnd) modelHisEnd else meterHisEnd

+      //logInfo("energyAgentTask", "Task 1: Checkpoint 2")

+      dateSpan: (anomalyFindingStartDate..modelEnd).toSpan

+      sensitivityOpts: {minAnomalyDuration:24hr, highAnomalyThreshold:10%, sustainedAnomalyThreshold:24hr}

       try anomalyTrigger: logic_kwModeling_anomalyDetection_findAndCommitAnomalyTriggers(mp, dateSpan, sensitivity:"high", sensitivityOpts, opts:{commitToRec:true})

       catch(err) return diff(mp, {anomalyTriggerCreationErr: err.dis}, {transient}).commit()

-

+      //logInfo("energyAgentTask", "Task 1: Anomaly trigger found: "+anomalyTrigger.get("id").toStr)

     end

     //end

   end

task_energyAgent_syncGenAIModels
--- task_energyAgent_syncGenAIModels (pod)
+++ task_energyAgent_syncGenAIModels (local)
@@ -1,5 +1,5 @@
 () => do

-  syncDuration: 1wk

+  syncDuration: 7day

   modelPoints: readAll(syntheticPoint and modelType=="adaptiveBaseline" and hisSize)

   modelPoints.each() mp => do

     //Get Meter Point

wdg_anomalyDetection_v2_chart_modelPredictionsPreview_v2
--- wdg_anomalyDetection_v2_chart_modelPredictionsPreview_v2 (pod)
+++ wdg_anomalyDetection_v2_chart_modelPredictionsPreview_v2 (local)
@@ -42,7 +42,7 @@
             else if (utilityType=="water")                      try pointQuery("water_intervalCons", {read, siteRef: siteRef, checked: false}) catch null

       // return     chart = blankChart(meterRec.toStr)

   if (meterRec.isNull) return {info: meterRec}.toGrid.addMeta({noData: "Utility meter point missing or tagged incorrectly."}).addColMeta("info", {dis: "Information"})

-  else if (meterRec.isNonNull) return chart: logic_energyAgent_meterPointToPredictionsChart(meterRec, dateSpan)

+  else if (meterRec.isNonNull) return chart: logic_energyAgent_meterPointToPredictionsChart_v2(meterRec, dateSpan)

 

 

   if (chart.isNonNull) return chart

@@ -72,4 +72,101 @@
   targetVarRef  : anomalyRec.get("targetVarRef")

   //staticModelConfig:  try anomalyRec.get("staticModelConfigRef").toRec catch return blankChart("Old anomaly record. Missing staticModelConfigRef. Reobserve anomalies and try again.")

 

-  // Dates
+  // Dates -----> -3mo to +3mo?

+  anomalyStartTS  : anomalyRec.get("ts")

+  extendedStartTS : anomalyStartTS - 3mo

+  anomalyEndTS    : anomalyStartTS + anomalyRec.get("duration")

+  extendedEndTS   : anomalyEndTS + 3mo

+

+  // Check hisStart and hisEnd for both points

+  modelPointStartDate: modelPointRef.toRec.get("hisStart")

+  modelPointEndDate  : modelPointRef.toRec.get("hisEnd")

+    // Usually model point would be the bottleneck ===================> TODO: How can we show the meter data and perhaps the adaptiveBaseline modelPoint along with confInts for the previous 3 months?

+    //if (modelPointStartDate > extendedStartTS) extendedStartTS = modelPointStartDate

+    //if (modelPointEndDate < extendedEndTS) extendedEndTS = modelPointEndDate

+

+  extendedSpan    : (extendedStartTS..extendedEndTS).toSpan

+

+  // His

+  hisGrid: [targetVarRef, modelPointRef].hisRead(dateSpan).hisClip

+  //hisGrid: try logic_kwModeling_getBaselineModelResults(staticModelConfig) catch (err) return blankChart(err.toStr)

+  //  if (hisGrid.isStr) return blankChart(hisGrid)

+

+  confIntGrid         : try logic_kwModeling_getModelPointResults(modelPointRef).reorderKeepCols(["ts"]) catch (err) return blankChart(err.dis)

+  filteredConfIntGrid : confIntGrid.findAll(v=> v.get("ts") >= dateSpan.start and v.get("ts") < dateSpan.end)  // filteredHisGrid.first.get("ts") -1hr?

+  loCol               : confIntGrid.colNames.find() r=>r.contains("lo")

+  hiCol               : confIntGrid.colNames.find() r=>r.contains("hi")

+

+  //filteredHisGrid: hisGrid.findAll(v=> v.get("ts") >= extendedStartTS and v.get("ts") < extendedEndTS )

+  //  if (filteredHisGrid.last.get("ts") < extendedEndTS) extendedEndTS = filteredHisGrid.last.get("ts")

+

+  //return blankChart(hisGrid.colNames.toStr)//.map(r=> {"colName":r}).toGrid

+  //// Fill in the inital 3 months with Adaptive baseline modelPoint his data

+  //if (filteredHisGrid.first.get("ts") > extendedStartTS) do

+  //

+  //  // Get points:

+  //  meterPoint:                  anomalyRec.get("targetVarRef")

+  //  adaptiveBaselineModelPoint:  try anomalyRec.get("anomalyTriggerRef").toRec.get("dynamicModelPointRef") catch (err) return blankChart("Widget trying to read a deleted Anomaly Trigger - Rerun Energy Agent Task", err.dis)

+  //  //return blankChart(adaptiveBaselineModelPoint.toStr)

+  //  // Get His

+  //  preAnomalySpan: (extendedStartTS..filteredHisGrid.first.get("ts")).toSpan                                                       // filteredHisGrid.first.get("ts") -1hr?

+  //

+  //  adaptiveModelHisGrid: [meterPoint, adaptiveBaselineModelPoint].hisRead(preAnomalySpan).hisClip

+  //

+  //  confIntGrid         : try logic_kwModeling_getModelPointResults(adaptiveBaselineModelPoint) catch (err) return blankChart(err.dis)

+  //  filteredConfIntGrid : confIntGrid.findAll(v=> v.get("ts") >= extendedStartTS and v.get("ts") < filteredHisGrid.first.get("ts"))  // filteredHisGrid.first.get("ts") -1hr?

+  //  loCol               : confIntGrid.colNames.find() r=>r.contains("lo")

+  //  hiCol               : confIntGrid.colNames.find() r=>r.contains("hi")

+  //

+  //  preAnomalyHisGrid: hisJoin([adaptiveModelHisGrid, filteredConfIntGrid]).reorderKeepCols(["ts", "v0", hiCol, "v1", loCol]).renameCol("v1", "timegpt")

+  //  preAnomalyHisGrid = transferMeta(hisGrid, preAnomalyHisGrid)

+//

+  //  hisGrid = addRows(filteredHisGrid, preAnomalyHisGrid)

+  //

+  //end

+

+

+  //holidayCalendar: read(calendar and dis=="Holiday Calendar", false).scheduleHis(extendedSpan)

+  //                                                                  .map(r=> r = if (r.get("val")!= true) r.set("val", false) else r.set("val", true))

+  //                                                                  .addColMeta("holidayCalendar", {chartGroup: "duration"})

+

+

+  //Create Anomaly Duration Grid

+  anomalyDurationGrid: {ts: anomalyStartTS, anomalyDuration: anomalyRec.get("duration")}.toGrid.addMeta({hisStart: dateSpan.start, hisEnd: dateSpan.end, hisSize:1})

+

+

+  allAnomalies: readAll(anomaly and targetVarRef==anomalyRec.get("targetVarRef") and ts >= dateSpan.start and ts < dateSpan.end)

+  try allAnomalies = allAnomalies.reorderCols(["ts", "duration"]).toGrid.addMeta({hisStart: dateSpan.start, hisEnd: dateSpan.end, hisSize:allAnomalies.size}) catch null

+

+  //out: hisJoin([hisGrid, anomalyDurationGrid, allAnomalies, holidayCalendar])

+  lineCharts: hisJoin([hisGrid, filteredConfIntGrid]).addMeta({hisStart: dateSpan.start, hisEnd: dateSpan.end})

+  runtimeCharts: hisJoin([ anomalyDurationGrid, allAnomalies])

+  out: hisJoin([lineCharts, runtimeCharts])

+

+  //////////////////////////////////////////////////////////////////////////////////////////

+  // FORMAT

+  //////////////////////////////////////////////////////////////////////////////////////////

+

+  gridMeta: {title: "Extended Model Prediction Preview - " + anomalyRec.get("siteRef").toRec.get("dis"), subtitle: anomalyRec.get("targetVarRef").toRec.get("navName").toStr + " ("+dateSpan.start.date+" to "+dateSpan.end.date+")", hisStart:dateSpan.start, hisEnd:dateSpan.end}

+

+  // prezGrid for anomalyImpact color?

+

+  out = out.stream

+           .addColMeta("ts",            {dis:"Timestamp"})

+           .addColMeta("v0", {dis:"Actual", strokeWidth:1, chartGroup: "predictions", chartType:"line"})//

+           .addColMeta(hiCol, {dis:"Upper Bound", strokeWidth:0.5, chartGroup: "predictions", chartAreaMode: "nextSeries", color:"#FDD081", opacity: 0.01, chartType:"line"})//

+           .addColMeta("v1", {dis:"Model", strokeWidth:1.5, chartGroup: "predictions", chartType:"line", strokeDasharray:"6,3"})//

+           .addColMeta(loCol, {dis:"Lower Bound", strokeWidth:0.5, chartGroup: "predictions", chartAreaMode: "prevSeries", color:"#FDD081", opacity: 0.01, chartType:"line"})//

+           .addColMeta("anomalyDuration", {chartGroup: "xduration", chartType:"runtime", dis:"Active Anomaly"})

+           .addColMeta("duration", {chartGroup: "xduration", chartType:"runtime", dis: "All Anomalies"})

+           .addMeta(gridMeta)

+           .addPrez_chartOrder("", [ "v0", "v1", loCol, hiCol])//

+           .collect

+

+  //////////////////////////////////////////////////////////////////////////////////////////

+  //RETURN RESULT

+  //////////////////////////////////////////////////////////////////////////////////////////

+

+  return out.reorderKeepCols(["ts", "v0", hiCol, "v1", loCol, "anomalyDuration", "duration"])

+

+end
wdg_chart_energyAgent_portfolioHealthOverview_portfolioPointPredictions
--- wdg_chart_energyAgent_portfolioHealthOverview_portfolioPointPredictions (pod)
+++ wdg_chart_energyAgent_portfolioHealthOverview_portfolioPointPredictions (local)
@@ -41,18 +41,18 @@
   //////////////////////////////////////////////////////////////////////////////////////////

 

   // Find relevant portfolio level calculatedPoint/virtualPoint based on utility

-  portfolioMeterPoint: if      (utilType=="all")   try read(portfolioPoint and elec and power and his and virtualPoint and not cost and not savings and not modelPoint and not syntheticPoint) catch null

-                       else if (utilType=="elec")  try read(portfolioPoint and elec and power and his and virtualPoint and not cost and not savings and not modelPoint and not syntheticPoint) catch null

-                       else if (utilType=="chw")   try read(portfolioPoint and chilled and water and his and virtualPoint and not cost and not savings and not modelPoint and not syntheticPoint) catch null

-                       else if (utilType=="steam") try read(portfolioPoint and steam and his and virtualPoint and not cost and not savings and not modelPoint and not syntheticPoint) catch null

-                       else if (utilType=="gas")   try read(portfolioPoint and naturalGas and his and virtualPoint and not cost and not savings and not modelPoint and not syntheticPoint) catch null

+  portfolioMeterPoint: if      (utilType=="all")   read(portfolioPoint and elec and power and his and virtualPoint and not cost and not savings and not modelPoint and not syntheticPoint)

+                       else if (utilType=="elec")  read(portfolioPoint and elec and power and his and virtualPoint and not cost and not savings and not modelPoint and not syntheticPoint)

+                       else if (utilType=="chw")   read(portfolioPoint and chilled and water and his and virtualPoint and not cost and not savings and not modelPoint and not syntheticPoint)

+                       else if (utilType=="steam") read(portfolioPoint and steam and his and virtualPoint and not cost and not savings and not modelPoint and not syntheticPoint)

+                       else if (utilType=="gas")   read(portfolioPoint and naturalGas and his and virtualPoint and not cost and not savings and not modelPoint and not syntheticPoint)

 

   if (portfolioMeterPoint.isNull) return {info: "Portfolio Level Meter point doesn't exist for the selected utility."}.toGrid.addMeta({noData: "Portfolio Level Meter point doesn't exist for the selected utility."}).addColMeta("info", {dis: "Information"})

 

 

 // pointQuery portfolio summed calculatedPoint/virtualPoint

 

-  out: logic_energyAgent_meterPointToPredictionsChart(portfolioMeterPoint, dateSpan, opts)

+  out: logic_energyAgent_meterPointToPredictionsChart_v2(portfolioMeterPoint, dateSpan, opts)

 

   return out

 

@@ -64,7 +64,10 @@
   // FORMAT

   //////////////////////////////////////////////////////////////////////////////////////////

 

-  gridMeta: {title: "Extended Model Prediction Preview - " + anomalyRec.get("siteRef").toRec.get("dis"), subtitle: anomalyRec.get("targetVarRef").toRec.get("navName").toStr + " ("+dateSpan.start.date+" to "+dateSpan.end.date+")", hisStart:dateSpan.start, hisEnd:dateSpan.end}

+  gridMeta: {title: "Extended Model Prediction Preview - " + anomalyRec.get("siteRef").toRec.get("dis"),

+             subtitle: anomalyRec.get("targetVarRef").toRec.get("navName").toStr + " ("+dateSpan.start.date+" to "+dateSpan.end.date+")",

+             hisStart:dateSpan.start,

+             hisEnd:dateSpan.end}

 

   // prezGrid for anomalyImpact color?

 

wdg_energyAgent_anomalyDetection_widgetSelector
--- wdg_energyAgent_anomalyDetection_widgetSelector (pod)
+++ wdg_energyAgent_anomalyDetection_widgetSelector (local)
@@ -15,7 +15,7 @@
 *     Apoorv Khanuja         05/21/2025      - Initial Creation

 */

 

-(wdg, site, utilityType, dates, rules, opts:{}) => do

+(wdg, site, utility, dates, rules, opts:{}) => do

 

   //////////////////////////////////////////////////////////////////////////////////////////

   // NORMALIZE + VALIDATE INPUTS

@@ -32,9 +32,7 @@
   //  anomalyRec : anomalyRef.toRec

   //  anomalyRef = anomalyRec.get("id")

   //end catch return blankChart("Please refresh page and select an anomaly to populate charts")

-

-  site = site.toRec

-  siteRef: site.get("id")

+  anomalyRef:null

 

   // Nav resolve site, let the wdg input be resolved siteRef

   //return blankChart(site.toStr)

@@ -50,20 +48,16 @@
   // LOGIC

   //////////////////////////////////////////////////////////////////////////////////////////

 

-  meterRec: if      (utilityType=="elec" or utilityType=="all") try pointQuery("elec_intervalPwr", {read, siteRef: siteRef, checked: false}) catch null

-            else if (utilityType=="steam")                      try pointQuery("steam_intervalCons", {read, siteRef: siteRef, checked: false}) catch null

-            else if (utilityType=="chw")                        try pointQuery("chilledWater_intervalCons", {read, siteRef: siteRef, checked: false}) catch null

-            else if (utilityType=="hhw")                        try pointQuery("hotWater_intervalCons", {read, siteRef: siteRef, checked: false}) catch null

-            else if (utilityType=="naturalGas")                 try pointQuery("natGas_intervalCons", {read, siteRef: siteRef, checked: false}) catch null

-            else if (utilityType=="water")                      try pointQuery("water_intervalCons", {read, siteRef: siteRef, checked: false}) catch null

-

   // Call individual wdg/logic funcs here

   // Handle which wdg to call/skip depending on available inputs here/

 

-  // akTODO: update wdg_energyAgent_anomalyDetection_exoVarProcessedHis and wdg_energyAgent_anomalyDetection_shapOverTime to work without anomalyRef

-  if      (wdg=="exoVarHis")                      wdg_energyAgent_anomalyDetection_exoVarProcessedHis(meterRec, dates) // return blankChart("exoVarHis widget implementation in Progress") //wdg_energyAgent_anomalyDetection_exoVarProcessedHis

-  else if (wdg=="exoVarSHAP")                     wdg_energyAgent_anomalyDetection_shapOverTime(meterRec, dates)

+    // akTODO: update wdg_energyAgent_anomalyDetection_exoVarProcessedHis and wdg_energyAgent_anomalyDetection_shapOverTime to work without anomalyRef

+  if (wdg=="exoVarHis")                           wdg_energyAgent_anomalyDetection_exoVarProcessedHis(anomalyRef, dates) // return blankChart("exoVarHis widget implementation in Progress") //wdg_energyAgent_anomalyDetection_exoVarProcessedHis

+  else if (wdg=="exoVarSHAP")                     wdg_energyAgent_anomalyDetection_shapOverTime(anomalyRef, dates)

   else if (wdg=="siteSparks")                     wdg_energyAgent_anomalyDetection_concurrentSparks(site, dates, rules, opts)

+  //else if (wdg=="modelPredictionsWithAnomalyRef") wdg_anomalyDetection_v2_chart_modelPredictionsPreview_v2(anomalyRef) //return blankChart("modelPredictionsWithAnomalyRef widget implementation in Progress")

+

+

 

   // modelPredictionsWithoutAnomaly needs a specific utility, not "all"

     // Consolidate in a single func, check for @null/null i/p, call the logicFunc for modelPredictionsWithoutAnomaly

wdg_portfolioDashboard_chart_anomalyImpactDollarPieChart

Sources are identical.

wdg_site_table_monthlyUsage
--- wdg_site_table_monthlyUsage (pod)
+++ wdg_site_table_monthlyUsage (local)
@@ -28,13 +28,48 @@
   out = out.addColMetaAll({viz:"barCell"}).addColMeta("ts", {format:"MMM-YYYY"}).addMeta({view:"chart",

                                                        chartType: "bar",

                                                        chartLegend: "hide",

-                                                       title: title: "Monthly Usage",

+                                                       title: title: sites.toRec.get("dis") + " Monthly Usage",

                                                        subtitle: if (dateStart==dateEnd)  dateStart.format("MMM YYYY")

                                                                  else dateStart.format("MMM YYYY") + " - " + dateEnd.format("MMM YYYY")})

  out.colNames.map(v => if      (out.col(v).meta.get("unit")=="therm") out = out.addColMeta(v, {color: "purple"}) // dis: out.col(v).meta.get("siteRef").toRec.get("dis") + " - Natural Gas Use"

                        else if (out.col(v).meta.get("unit")=="kW")    out = out.addColMeta(v, {color: "orange"}))//, dis: out.col(v).meta.get("siteRef").toRec.get("dis") + " - Electricity Use"

 

+ // Shorten column names to only include navName instead of equipRef and navName

+ elecList:[]

+ gasList:[]

+ steamList:[]

+ chilledList:[]

+ out.colNames.each()c => do

+   meta: out.col(c).meta

+   if(meta.has("elec")) elecList = elecList.add(c)

+   if(meta.has("gas")) gasList = gasList.add(c)

+   if(meta.has("steam")) steamList = steamList.add(c)

+   if(meta.has("chilled")) chilledList = chilledList.add(c)

+ end

 

 

-  return out

+ out = out.colNames.reduce(out, (g, v) => do

+   meta: g.col(v).meta

+   if (meta.has("equipRef")) do

+     equip: meta.get("equipRef").toRec

+     navDis: equip.get("navName")

+     g.setColMeta(v, meta.set("disMacro", navDis + " \$navName"))

+   else

+     g

+ end)

+

+

+ out = out.map() r => do

+   out.colNames.each() c => do

+     if(c == "ts") return null

+     if(r.get(c).unit  == "kW") r = r.set(c, r.get(c).as("kWh"))

+     else if(r.get(c).unit   == "lb/h") r = r.set(c, r.get(c).as("lb"))

+     else if(r.get(c).unit   == "tonref") r = r.set(c, r.get(c).as("tonrefh"))

+   end

+   return r = r

+ end

+

+

+  colOrder: [["ts"], elecList, gasList, steamList, chilledList].flatten//.toList

+  return out.reorderKeepCols(colOrder)

 end
kwLinkMbcxExt
getSiteKickoffDate
--- getSiteKickoffDate (pod)
+++ getSiteKickoffDate (local)
@@ -13,7 +13,8 @@
   if (arcs.size==null) return missingVal

 

   types: "active".split(",")

-

+  //return types

+  //return arcs

   //timeline where each row is an arc and its transition dates

   timeline: arcs.map row => do

 

@@ -22,15 +23,15 @@
 

     out: {id: row->id}

     types.each type => do

-      if (type=="new") out=out.set(type, row.get("created"))

-      else out=out.set(type, try audits.find(r => r.get("audit").get("new").get("projectTrackerState")==type).get("ts") catch null)

+      if (type=="new") out=out.set(type, row.get("estCompleteDate"))

+      else out=out.set(type, try audits.find(r => r.get("audit").get("new").get("projectTrackerState")==type).get("audit").get("new").get("estCompleteDate") catch null)

     end

     return out

   end

-

+  //return timeline

   resolved: timeline.filter(active)

+  //return resolved

   if (resolved.size<1) return missingVal

-

-  return resolved.sort("active").first.get("active").date

+  return resolved.sort("active").first.get("active")//.date

 

 end
kwLinkModelExt
action_createCalculatedPoint
--- action_createCalculatedPoint (pod)
+++ action_createCalculatedPoint (local)
@@ -52,7 +52,7 @@
   pointDefs.each r => do

     tag: r.toStr

     if(pointTags.contains(tag)) do

-      intersectDict = intersectDict.set(tag, marker())

+      intersectDict = intersectDict//.set(tag, marker()) NOTE: This was disabled to prevent automatic tagging of point

     end

   end

 

action_kwModeling_myModels_activateModel
--- action_kwModeling_myModels_activateModel (pod)
+++ action_kwModeling_myModels_activateModel (local)
@@ -36,10 +36,8 @@
   modelPointRef: modelConfigRec.get("modelPointRef")

 

   // Figure out activation span based on model engine.

-  startDate: if ((modelConfigRec.get("pointRef").toRec.get("hisEnd") - 52wk) <  modelConfigRec.get("pointRef").toRec.get("hisStart")) modelConfigRec.get("pointRef").toRec.get("hisStart")

-             else                                                                                                                     modelConfigRec.get("pointRef").toRec.get("hisEnd") - 52wk

   dates: if (modelConfigRec.get("modelEngine") == "nmecpy") (modelConfigRec.get("modelParameters").get("trainDates").start..modelConfigRec.get("modelParameters").get("testDates").end).toSpan

-         else                                               (startDate..(modelConfigRec.get("pointRef").toRec.get("hisEnd")))

+         else                                               (2025-01-01..(modelConfigRec.get("pointRef").toRec.get("hisEnd"))) // 2025-01-01 for campus level points 03-01 for others

 

   // Populate the modelPoint with data

   callTask("virtualSyncHis", [modelPointRef, dates], getVal: true)

func_syncCalculatedPoints

Sources are identical.

logic_kwModeling_autoCreateModelConfig
--- logic_kwModeling_autoCreateModelConfig (pod)
+++ logic_kwModeling_autoCreateModelConfig (local)
@@ -27,10 +27,11 @@
   modelEngine: opts.get("modelEngine")

   modelMethod: opts.get("modelMethod")

   tempPointId: read(point and temp and weatherStationRef == pointRec.get("siteRef").toRec.get("weatherStationRef"), false).toRec.get("id")

+  logInfo("kwBulkModeling", "Temp Point selected: " + tempPointId)

 

   occPointId:  if (opts.has("occ")) opts.get("occ").toRec.get("id")

                else try read(wifi and occ and point and sensor and calculatedPercent and siteRef == siteRef).toRec.get("id") catch null

-  additionalPoints: if (opts.has("additionalPoints") and opts.get("additionalPoints").isList and not opts.get("additionalPoints").isEmpty) opts.get("additionalPoints") else try readAll(indicatorVariable).colToList("id") catch null

+  additionalPoints: if (opts.has("additionalPoints") and opts.get("additionalPoints").isList and not opts.get("additionalPoints").isEmpty) opts.get("additionalPoints").findAll(v=>v.isNonNull) else try readAll(indicatorVariable).colToList("id") catch null

 

   // PreProcess Configs Retrieval

   zScoreOutlierExclusion: if (opts.get("zScoreOutlierExclusion").isNonNull) opts.get("zScoreOutlierExclusion")

@@ -83,8 +84,8 @@
                               span:               dates,

                               confInterval:       confInterval,

                               testTrainSplitDate: (dates.end-1wk),

-                              additionalPoint:    if   (additionalPoints.isNull) [] // Removed occ Point (occPointId)

-                                                  else                           [additionalPoints].flatten.findAll(v=>v.isNonNull),

+                              additionalPoint:    if   (additionalPoints.isNull) [occPointId].flatten.findAll(v=>v.isNonNull) // Removed occ Point (occPointId)

+                                                  else                           [occPointId, additionalPoints].flatten.findAll(v=>v.isNonNull),

                              },

            preProcessConfig: {

                                includeTOW:       includeTOW,

@@ -94,16 +95,13 @@
         end

 

   // action_kwModeling_runModel returns a taskFuture, so we follow logic rather than call it itself.

-  modelConfigRec: try         logic_kwModeling_saveModelConfig_v2(dict, opts)

-                  catch (err) return {point: pointId, err: err}

+  logInfo("kwBulkModeling", "Attempting to save ModelConfig"+dict.toStr)

+

+  modelConfigRec: logic_kwModeling_saveModelConfig_v2(dict, opts) //try

+                  //catch (err) return {point: pointId, err: err}

 

   updatedRec: diff(modelConfigRec, {"modelRunState": "queued"}, {update}).commit()

-

-  // NOTE: Change so after the callTask is done, we retrieve the future and check if done vs. done error

-  //try         callTask(logic_kwModeling_runModel, [updatedRec], getVal: true)

-  //catch (err) return {modelConfigRec: updatedRec, err: err}

-  // Edit 06/2025: Running the config is unnecessary. Just save config and activate point.

-

+  logInfo("kwBulkModeling", "ModelConfig saved.")

   // Return the updated/created modelRec

   return updatedRec

 end
logic_kwModeling_bulkGenerateModelPoints
--- logic_kwModeling_bulkGenerateModelPoints (pod)
+++ logic_kwModeling_bulkGenerateModelPoints (local)
@@ -80,46 +80,6 @@
   successfulConfigs: modelConfigs.findAll(r => r.isDict and r.missing("err"))

   failedConfigs:     modelConfigs.findAll(r => r.isDict and r.has("err"))

 

-  // Report as a warning the configs that werent able to create a config.

-  //if (not failedConfigs.isEmpty) logWarn("kwBulkModeling", failedConfigs.size + " of valid points failed to create a config.", failedConfigs)

-  // Commented out for now.

-  //Fails with: Func failed: logWarn(Obj tags,Str msg,Dict err); args: (Str,Str,GbGrid)

-  //  sys::ArgErr: java.lang.IllegalArgumentException: argument type mismatch

-

-//  // Point-Per-Task Opt

-//  pointPerTask: opts.get("pointPerTask")

-//  bulkMode:     opts.get("bulkMode")

-//

-//  // Reset pointPerTask to 1 if bulkMode is single-task

-//  if (pointPerTask < 1 or bulkMode=="Single-Task") pointPerTask = 1

-//

-//  // Split model configs into buckets for efficient/safe task processing

-//  buckets: []

-//  idx: 0

-//  successfulConfigs.each(r => do

-//    try do

-//      subGrid: buckets.get(idx)

-//      if   (subGrid.size < pointPerTask) buckets = buckets.set(idx, subGrid.add(r->id))

-//      else do

-//        idx = idx + 1

-//        throw "Create new bucket" // Dummy throw to jump into the catch

-//      end

-//    catch do

-//      buckets = buckets.add([r->id])

-//    end

-//  end)

-//

-//  // Log progress (buckets were created)

-//  logInfo("kwBulkModeling", buckets.size + " bucket(s) created. Initializing Bulk Modeling Activation")

-//

-//  // Activate models using a taskRun/futureGet pair on a per-bucket basis (bucket size is dependant on pointPerTask Opt)

-//  buckets.each() (bucket) => do

-//    logInfo("kwBulkModeling", "Activating these models at the same time: " + bucket.toStr)

-//    bucket.each() (modelConfigRef) => do

-//      if   (modelConfigRef != bucket.last) callTask(action_kwModeling_myModels_activateModel, [modelConfigRef], getVal:false)

-//      else                                 callTask(action_kwModeling_myModels_activateModel, [modelConfigRef], getVal:true)

-//    end

-//  end

 

   // Activating one config at a time

   successfulConfigs.each() r => do

logic_kwModeling_createModelPointRec_v2
--- logic_kwModeling_createModelPointRec_v2 (pod)
+++ logic_kwModeling_createModelPointRec_v2 (local)
@@ -29,12 +29,8 @@
 

   modelPointBlueprint : try read(virtualBlueprint and navName=="GenAI Adaptive Baseline Model Point") catch throw "No modelPoint Blueprint found."

 

-  modelActivationConfig: if      (modelConfigRec.get("modelEngine")=="timegpt") {trainLength: 6mo, forecastLength: 1week}

+  modelActivationConfig: if      (modelConfigRec.get("modelEngine")=="timegpt") {trainLength: 12mo, forecastLength: 1week}

                          else if (modelConfigRec.get("modelEngine")=="nmecpy") {trainLength: 12mo, forecastLength: 1week}

-

-  meterHisStart : modelConfigRec.get("pointRef").toRec.get("hisStart")

-  meterHisEnd   : modelConfigRec.get("pointRef").toRec.get("hisEnd")

-  trainStartDateOverride : if (meterHisStart < meterHisEnd - 12mo) meterHisEnd - 12mo else meterHisStart

 

   virtualPointTags: modelPointBlueprint.get("virtualPointTags").listTagsToDict

 

@@ -48,7 +44,6 @@
                              equipRef               : try modelConfigRec.get("pointRef")->equipRef catch null,

                              unit                   : try modelConfigRec.get("pointRef")->unit     catch null,

                              siteRef                : modelConfigRec.get("siteRef"),

-                             trainStartDateOverride : trainStartDateOverride,

                              tz                     : modelConfigRec.get("siteRef").toRec.get("tz"),

                              modelType              : modelType,

                              mlIdentificationPeriod : mlIdentificationPeriod,

logic_kwModeling_modelParameterWeightsPlot
--- logic_kwModeling_modelParameterWeightsPlot (pod)
+++ logic_kwModeling_modelParameterWeightsPlot (local)
@@ -23,6 +23,24 @@
 

   // Extract modelCoeffs

   modelCoeffs: modelCacheRec.get("modelCoeffs")

+  //return modelCoeffs.colNames

+  // Extract other colMeta information for better column names

+  modelConfigRec : modelCacheRec.get("modelConfigRef").toRec

+  modelParameters: modelConfigRec.get("modelParameters")

+  exoVarPoints: [modelParameters.get("temp"), modelParameters.get("additionalPoint")].flatten.findAll(v=>v.isNonNull)

+

+  exoVarMeta: {}.toGrid

+

+  exoVarPoints.each() (r,idx) => do

+    exoVarNum: idx + 1

+    exoVarLabel: "v"+exoVarNum.toStr

+

+    curVarMeta: {exoVarLabel:exoVarLabel, curVarDis: r.toRec.get("navName")}

+    exoVarMeta = exoVarMeta.addRow(curVarMeta)

+  end

+//return exoVarMeta.findAll(v => v.get("exoVarLabel")=="v1").toRec.get("curVarDis") //blankChart(exoVarMeta.size.toStr)

+

+

 

 ////////////////////// If modelCoeffs is a number (In case of nmecpy>towt model)

 

@@ -46,12 +64,24 @@
 //////// v1: regular grid with average model weights

 

   else if (modelCoeffs.debugType=="haystack::GbGrid") do

-    outString: "SHAP Value implemenation in progress."

-    out: blankChart(outString)/*modelCoeffs.stream

+    return {info: "SHAP Value implemenation in progress."}.toGrid.addColMeta("info", {dis:"Information"})

+    // Shows up as "No Data"

+    // akTODO: Finish writing this chunk with the new SHAP values grid

+    modelCoeffs = modelCoeffs.removeCols(["unique_id", "ts", "timegpt", "base_value"]).map() r => do

+      if (r.has("v1")) r = r.set("v1", r.get("v1").abs)

+      if (r.has("v2")) r = r.set("v2", r.get("v2").abs)

+      if (r.has("v3")) r = r.set("v3", r.get("v3").abs)

+      if (r.has("v4")) r = r.set("v4", r.get("v4").abs)

+      if (r.has("v5")) r = r.set("v5", r.get("v5").abs) // Works for up to 5 exo variables

+

+    end

+    //

+    return modelCoeffs//.colNames

+    out: modelCoeffs.stream

                     .addMeta({title: "Average weights of exogenous variable used for predictions", chartType:"bar", chartLegend:"hide"})

                     .addColMeta("timeOfWeek", {dis: "Time of week"})

                     .collect

-                    .chart*/

+                    .chart

   end

 

 

logic_kwModeling_validatePointModeling
--- logic_kwModeling_validatePointModeling (pod)
+++ logic_kwModeling_validatePointModeling (local)
@@ -65,15 +65,12 @@
       if (wsHisSize < 1)                                                                       errors = errors.add("Weather Station has no historized data.")

       if (wsHisEnd < now()-2year)                                                              warnings = warnings.add("Weather Station's hisEnd is older than 2 years from today: " + pointRec.get("hisEnd") + ".")

       // NOTE: Make this logic better?

-      if (pointRec.has("hisEnd") and

-         ((pointRec.get("hisEnd") - 1year) < wsHisStart) or

-          pointRec.get("hisEnd") > wsHisEnd)                                                   errors = errors.add("Overlap data between Point and Temp is not complete")

+      if (pointRec.has("hisEnd") and ((pointRec.get("hisEnd") - 1year) < wsHisStart) or pointRec.get("hisEnd") > wsHisEnd) errors = errors.add("Overlap data between Point and Temp is not complete")

      end

   end

 

   // NOTE: Change to pointQuery

-  //if (readAll(kwStockSchedule).isEmpty)                                                        errors = errors.add("No Occupancy Schedule (kwStockSchedule) in this Project.")

-  // Commented out. Don't really need Stock schedule for EA.

+  if (readAll(kwStockSchedule).isEmpty)                                                        errors = errors.add("No Occupancy Schedule (kwStockSchedule) in this Project.")

 

   // Miscellaneous Checks from current modeling app

   if (modelEngine == "nmecpy" and (modelMethod != "TOWT" and modelMethod != "HD-CD"))          errors = errors.add("Additional Points unsupported by " + modelMethod + ". Select a valid model method for additional points, or remove the additional point from this model method. Supported Model Methods for Additional Points: [\"TOWT\", \"HD-CD\"]")

view_calculateSavings
--- view_calculateSavings (pod)
+++ view_calculateSavings (local)
@@ -36,7 +36,9 @@
   equip: modelPoint->equipRef

   basePt: readById(modelPoint->modelRef)->pointRef

   interval: modelPoint.get("hisRollupInterval")

-  weather: readAll(weatherStationRef==equip->siteRef->weatherStationRef and temp)

+  weather: readById(readById(modelPoint->modelRef)->modelConfig->viewInputs->temp)//readAll(weatherStationRef==equip->siteRef->weatherStationRef and temp and not enthalpy) //Added filter to remove enthalpy as an accidentally considered point here

+  weatherUnit: weather.get("unit")

+  if(weatherUnit == "°F") temp = 50°F else if (weatherUnit == "btu/lb_dry") temp = 28.as("btu/lb_dry")

   weatherHis: weather.hisRead(dates, {-limit}).renameCol("v0","oat").setColMeta("oat",{chartGroup:"a", color:"gray", opacity:0.1, strokeDasharray:"2,2", dis:"Outside Air Temp"})

   siteDis: readById(modelPoint->siteRef).dis

   modelDis: modelPoint.dis

virtual_kwModeling_forecast_syncModelPointPredictions_timegpt
--- virtual_kwModeling_forecast_syncModelPointPredictions_timegpt (pod)
+++ virtual_kwModeling_forecast_syncModelPointPredictions_timegpt (local)
@@ -54,7 +54,6 @@
     spanX  : if (trainStartDateOverride.isNull) ((r - trainLength)..spanEnd).toSpan  else (trainStartDateOverride..spanEnd).toSpan

     spans  = spans.add(spanX)

   end

-

 

   fakeModelConfigRec     : modelConfigRec.remove("id").remove("mod").remove("modelConfig2")

   fakeModelConfigRecGrid : {}.toGrid

kwLinkRuleExt
kpi_chwp_averagePumpSpeed

Sources are identical.

kpi_chwp_percentRuntime
--- kpi_chwp_percentRuntime (pod)
+++ kpi_chwp_percentRuntime (local)
@@ -15,12 +15,12 @@
   target: {}

   date:   {}

   out:    {readonly}

-  pri_pump_speed  : {bind:"water and pump and speed and cmd and primary and equipRef=={{target->id}}"}

-  sec_pump_speed  : {bind:"water and pump and speed and cmd and secondary and equipRef=={{target->id}}"}

-  pri_pump_status : {bind:"water and pump and run and cmd and primary and equipRef=={{target->id}}"}

-  sec_pump_status : {bind:"water and pump and run and cmd and secondary and equipRef=={{target->id}}"}

-  pri_pump_power  : {bind:"water and pump and power and sensor and primary and equipRef=={{target->id}}"}

-  sec_pump_power  : {bind:"water and pump and power and sensor and secondary and equipRef=={{target->id}}"}

+  pri_pump_speed  : {bind:"water and pump and speed and cmd and primary and equipRef->equipRef->equipRef=={{target->id}}"}

+  sec_pump_speed  : {bind:"water and pump and speed and cmd and secondary and equipRef->equipRef->equipRef=={{target->id}}"}

+  pri_pump_status : {bind:"water and pump and run and cmd and primary and equipRef->equipRef->equipRef=={{target->id}}"}

+  sec_pump_status : {bind:"water and pump and run and cmd and secondary and equipRef->equipRef->equipRef=={{target->id}}"}

+  pri_pump_power  : {bind:"water and pump and power and sensor and primary and equipRef->equipRef->equipRef=={{target->id}}"}

+  sec_pump_power  : {bind:"water and pump and power and sensor and secondary and equipRef->equipRef->equipRef=={{target->id}}"}

   do

     // Call logic function

     his: logic_cwp_pumpIsRunning(target, date)

kpi_chws_avgDeltaT

Sources are identical.

kpi_chws_avgSystemFlow
--- kpi_chws_avgSystemFlow (pod)
+++ kpi_chws_avgSystemFlow (local)
@@ -14,6 +14,9 @@
   opts:   {bindTuning:"opts",      defVal:{}}

 

   do

+

+    opts = {secondaryLoopOverride: "null"}

+

     // Retrieve loop

     points: buildingLoopDigest(target, opts).get("buildingLoop")

 

@@ -38,9 +41,9 @@
     pumpRunningHis: logic_chws_equipsRunning(bLoopId, date, {equip:"pump"})

 

     // Edge-case handling

-    if (pumpRunningHis.isStr) return pumpRunningHis

+    //if (pumpRunningHis.isStr) return pumpRunningHis

 

-    pumpRunningHis = pumpRunningHis.keepCols(["ts", "anyEquipRunning"]).findAll(r => r.get("anyEquipRunning").isNonNull)

+    pumpRunningHis = try pumpRunningHis.keepCols(["ts", "anyEquipRunning"]).findAll(r => r.get("anyEquipRunning").isNonNull) catch null

 

     if(pumpRunningHis.isNonNull) his = his.hisFindInPeriods(pumpRunningHis)

 

kpi_chws_secondaryLoop_percentRuntimeOnePump

Sources are identical.

kpi_chws_systemPeakDemand
--- kpi_chws_systemPeakDemand (pod)
+++ kpi_chws_systemPeakDemand (local)
@@ -22,8 +22,8 @@
     if (demandHis.isStr) return null

 

     // Fold resulting hisGrid column based on min/max, and return.

-    maxPeak : demandHis.foldCol("systemDemand", max)

-    minPeak : demandHis.foldCol("systemDemand", min)

+    maxPeak : demandHis.foldCol("v0", max)

+    minPeak : demandHis.foldCol("v0", min)

 

     out = {min: minPeak, max: maxPeak}

   end

kpi_chws_totalSystemEnergyUse
--- kpi_chws_totalSystemEnergyUse (pod)
+++ kpi_chws_totalSystemEnergyUse (local)
@@ -22,7 +22,7 @@
     if (usageHis.isStr) return null

 

     // Fold resulting hisGrid column based on min/max, and return.

-    totalUsage : usageHis.findAll(r => not r.get("systemUsage").isNaN).foldCol("systemUsage", sum)

+    totalUsage : usageHis.findAll(r => not r.get("v0").isNaN).foldCol("v0", sum)

 

     out = {sum: totalUsage}

   end

kpi_hhws_secondaryLoop_percentRuntimeOnePump
--- kpi_hhws_secondaryLoop_percentRuntimeOnePump (pod)
+++ kpi_hhws_secondaryLoop_percentRuntimeOnePump (local)
@@ -11,7 +11,7 @@
   target: {}

   date:   {}

   out:    {readonly}

-  opts:   {bindTuning:"opts", defVal:{}}

+  opts:   {bindTuning:"opts",      defVal:{}}

 

   do

     // Normalize Inputs

spark_chws_notMeetingSupplySetpoint

Sources are identical.

kwLinkVirtualExt
action_virtualPoints_createSiteMeterPts
--- action_virtualPoints_createSiteMeterPts (pod)
+++ action_virtualPoints_createSiteMeterPts (local)
@@ -1,9 +1,12 @@
-(dicts) => do

-

+(list) => do

   //normalize inputs to grid

   //dict contains ONLY ID, mod, and CHANGES made to form

-  input: actionNormInput(dicts,"dicts").toGrid

-

+  //input: actionNormInput(dicts,"dicts").toGrid

+  input: []

+  list.each(r => input=input.add(r.toStr.split("|")[2].parseRef))

+  input = readByIds(input)

+  equip: list[0].toStr.split("|")[1].parseRef

+  site: list[0].toStr.split("|")[0].parseRef

   //match input dict up with blueprint list

   blueprints: wdg_virtualPoints_table_blueprintList()

 

@@ -18,7 +21,7 @@
           catch (err) blueprint.merge({result:"failure", err:err})

 

     //return result

-    out: if (dict.missing("err")) diff(null, dict, {add}).commit.merge({result:"succes"})

+    out: if (dict.missing("err")) diff(null, dict.set("equipRef", equip).set("siteRef", site), {add}).commit.merge({result:"succes"})

          else                     dict

   end

   recs: readByIds(results.colToList("id"))

calcTMRASavings
--- calcTMRASavings (pod)
+++ calcTMRASavings (local)
@@ -1,4 +1,4 @@
-(siteId, savingsHis, modelPt,  meterPt, performancePeriodStartDate, performancePeriodEndDate, mildTempRangeLower: "40", mildTempRangeUpper: "65") => do

+(siteId, savingsHis, modelPt,  meterPt, performancePeriodStartDate, performancePeriodEndDate, mildTempRangeLower: "35", mildTempRangeUpper: "50") => do

   mildTempRange: (mildTempRangeLower.parseNumber.as("°F")..mildTempRangeUpper.parseNumber.as("°F"))

   performancePeriod : (performancePeriodStartDate..performancePeriodEndDate).toDateSpan

   siteRec: siteId.toRec

logic_eui
--- logic_eui (pod)
+++ logic_eui (local)
@@ -36,20 +36,18 @@
     //convert units

     if (not eci) do

       if (p.get("unit")=="kW") pointHis = pointHis.hisRollup(avg,1hr).hisMap(v=>v.as("kWh"))

+      if (p.get("unit")=="MW") pointHis = pointHis.hisMap(v=>v.to("kW")).hisRollup(avg,1hr).hisMap(v=>v.as("kWh"))

       if (p.get("unit")=="BTU/h") pointHis = pointHis.hisRollup(avg,1hr).hisMap(v=>v.as("BTU"))

       if (p.get("unit")=="kBTU/h") pointHis = pointHis.hisRollup(avg,1hr).hisMap(v=>v.as("kBTU"))

 

       if (p.get("unit")=="lb/h") do

-        pointHis = pointHis.hisRollup(avg,1hr).hisMap(v=>(v*0.0012850675).as("kBTU"))

+        pointHis = pointHis.hisRollup(avg,1hr).hisMap(v=>(v*1.03).as("kBTU"))

       end

       if (p.get("unit")=="tonref") do

-        pointHis = pointHis.hisRollup(avg,1hr).hisMap(v=>(v*12000).as("kBTU"))

+        pointHis = pointHis.hisRollup(avg,1hr).hisMap(v=>(v*12).as("kBTU"))

       end

       if (p.get("unit")=="klb/h") do

-        pointHis = pointHis.hisRollup(avg,1hr).hisMap(v=>(v*1.2850675).as("kBTU"))

-      end

-      if (p.get("unit")=="ft³_gas") do

-        pointHis = pointHis.hisRollup(avg,1hr).hisMap(v=>v.as("kBTU"))

+        pointHis = pointHis.hisRollup(avg,1hr).hisMap(v=>(v*1030).as("kBTU"))

       end

     end

 

siteMeter_retrieveRequiredVirtualPoints
--- siteMeter_retrieveRequiredVirtualPoints (pod)
+++ siteMeter_retrieveRequiredVirtualPoints (local)
@@ -22,32 +22,56 @@
   meterScope : smRec.get("scope")

   type       : if      (smRec.has("elec"))                                                   "elec"

                else if (smRec.has("gas") or smRec.has("naturalGas") or smRec.has("natGas"))  "gas"

+               else if (smRec.has("hot") and smRec.has("water"))                             "hotWater"

+               else if (smRec.has("chilled") and smRec.has("water"))                         "chilledWater"

+               else if (smRec.has("steam"))                                                  "steam"

                else                                                                          "invalid"

 

   // Retrieve Required pointQueryTarget Tags

-  tags: if      (meterScope == "interval" and type=="elec") ["elec_modelPwr", "elec_monthlyEnergy", "elec_monthlyCost", "elec_monthlyRateCost", "elec_intervalPwr", "elec_modelPwr", "elec_savingsPwr", "elec_intervalSavingsCost", "elec_intervalCost"]

-        else if (meterScope == "interval" and type=="gas")  ["natGas_intervalModelCons", "natGas_monthlyCons", "natGas_monthlyCost", "natGas_monthlyRateCost", "natGas_intervalModelCons", "natGas_intervalCost", "natGas_intervalSavingsCost", "natGas_intervalSavingsCons", "natGas_intervalCons"]

-        else if (meterScope == "monthly"  and type=="elec") ["elec_modelEnergy", "elec_monthlyEnergy", "elec_monthlyCost", "elec_monthlyRateCost", "elec_modelEnergy", "elec_savingsEnergy", "elec_monthlySavingsCost"]

-        else if (meterScope == "monthly"  and type=="gas")  ["natGas_monthlyModelCons", "natGas_monthlyCons", "natGas_monthlyCost", "natGas_monthlyRateCost", "natGas_monthlyModelCons", "natGas_monthlySavingsCost", "natGas_monthlySavingsCons"]

-        else                                                []

+  tags: if (meterScope == "interval" and type=="elec")               ["elec_intervalPwr", "elec_modelPwr", "elec_savingsPwr", "elec_intervalSavingsCost", "elec_intervalCost"]

+        else  if (meterScope == "interval" and type=="hotWater")     ["hotWater_intervalCons", "hotWater_intervalModelCons", "hotWater_intervalSavingsCons", "hotWater_intervalSavingsCost", "hotWater_intervalCost"]

+        else  if (meterScope == "interval" and type=="chilledWater") ["chilledWater_intervalCons", "chilledWater_intervalModelCons", "chilledWater_intervalSavingsCons", "chilledWater_intervalSavingsCost", "chilledWater_intervalCost"]

+        else  if (meterScope == "interval" and type=="steam")        ["steam_intervalCons", "steam_intervalModelCons", "steam_intervalSavingsCons", "steam_intervalSavingsCost", "steam_intervalCost"]

+        else  if (meterScope == "interval" and type=="gas")          ["elec_intervalPwr", "elec_modelPwr", "elec_savingsPwr", "elec_intervalSavingsCost", "elec_intervalCost"]

+        else  if (meterScope == "monthly"  and type=="elec")         ["elec_intervalPwr", "elec_modelPwr", "elec_savingsPwr", "elec_intervalSavingsCost", "elec_intervalCost"]

+        else  if (meterScope == "monthly"  and type=="hotWater")     ["elec_intervalPwr", "elec_modelPwr", "elec_savingsPwr", "elec_intervalSavingsCost", "elec_intervalCost"]

+        else  if (meterScope == "monthly"  and type=="chilledWater") ["elec_intervalPwr", "elec_modelPwr", "elec_savingsPwr", "elec_intervalSavingsCost", "elec_intervalCost"]

+        else  if (meterScope == "monthly"  and type=="steam")        ["elec_intervalPwr", "elec_modelPwr", "elec_savingsPwr", "elec_intervalSavingsCost", "elec_intervalCost"]

+        else  if (meterScope == "monthly"  and type=="gas")          ["elec_intervalPwr", "elec_modelPwr", "elec_savingsPwr", "elec_intervalSavingsCost", "elec_intervalCost"]

+        else                                                         []

 

   // Determine return value based on pointType opt.

-  if (opts.get("pointType") == "tags")    return tags

-  if (opts.get("pointType") == "valid")   return tags.map(point => if (pointQuery(point, {equipRef: smId}).isNonNull) pointQuery(point, {equipRef: smId}) else null).findAll(v => v.isNonNull).toGrid

+  if (opts.get("pointType") == "tags")  return tags

+  if (opts.get("pointType") == "valid") do

+    validRecs: tags.map(point => if (pointQuery(point, {equipRef: smId}).isNonNull) pointQuery(point, {equipRef: smId}) else null).findAll(v => v.isNonNull).toGrid

+    if(validRecs.isNonNull and validRecs.has("id"))  validRecs = validRecs.unique("id")

+    return validRecs

+  end

   if (opts.get("pointType") == "missing") do

     // Find Missing Points through the blueprint list and pointQueryLookups

     missing: getVirtualBlueprintList().findAll(r => tags.contains(r.get("pointQueryTarget")) and pointQuery(r.get("pointQueryTarget"), {equipRef: smId}).isNull)

                    .map(r=> r.set("tz", smRec.get("siteRef").toRec.get("tz")))

 

     // Add Model Point Missing Message

-    if      (meterScope == "interval" and type=="elec" and pointQuery("elec_modelPwr", {equipRef: smId}).isNull) missing = missing.addRow({virtualPointCategory: "Missing Model Point"})

-    else if (meterScope == "interval" and type=="gas" and pointQuery("natGas_intervalModelCons", {equipRef: smId}).isNull)  missing = missing.addRow({virtualPointCategory: "Missing Model Point"})

-    else if (meterScope == "monthly"  and type=="elec" and pointQuery("elec_modelEnergy", {equipRef: smId}).isNull) missing = missing.addRow({virtualPointCategory: "Missing Model Point"})

-    else if (meterScope == "monthly"  and type=="gas" and pointQuery("natGas_monthlyModelCons", {equipRef: smId}).isNull)  missing = missing.addRow({virtualPointCategory: "Missing Model Point"})

+    if (meterScope == "interval" and type=="elec" and pointQuery("elec_modelPwr", {equipRef: smId}).isNull)                          missing = missing.addRow({virtualPointCategory: "Missing Model Point"})

+    if (meterScope == "interval" and type=="hotWater" and pointQuery("hotWater_intervalModelCons", {equipRef: smId}).isNull)         missing = missing.addRow({virtualPointCategory: "Missing Model Point"})

+    if (meterScope == "interval" and type=="chilledWater" and pointQuery("chilledWater_intervalModelCons", {equipRef: smId}).isNull) missing = missing.addRow({virtualPointCategory: "Missing Model Point"})

+    if (meterScope == "interval" and type=="steam" and pointQuery("steam_intervalModelCons", {equipRef: smId}).isNull)               missing = missing.addRow({virtualPointCategory: "Missing Model Point"})

+    if (meterScope == "interval" and type=="gas" and pointQuery("natGas_intervalModelCons", {equipRef: smId}).isNull)                missing = missing.addRow({virtualPointCategory: "Missing Model Point"})

+    if (meterScope == "monthly"  and type=="elec" and pointQuery("elec_modelEnergy", {equipRef: smId}).isNull)                       missing = missing.addRow({virtualPointCategory: "Missing Model Point"})

+    if (meterScope == "monthly"  and type=="hotWater" and pointQuery("hotWater_monthlyModelCons", {equipRef: smId}).isNull)          missing = missing.addRow({virtualPointCategory: "Missing Model Point"})

+    if (meterScope == "monthly"  and type=="chilledWater" and pointQuery("chilledWater_monthlyModelCons", {equipRef: smId}).isNull)  missing = missing.addRow({virtualPointCategory: "Missing Model Point"})

+    if (meterScope == "monthly"  and type=="steam" and pointQuery("steam_monthlyModelCons", {equipRef: smId}).isNull)                missing = missing.addRow({virtualPointCategory: "Missing Model Point"})

+    if (meterScope == "monthly"  and type=="gas" and pointQuery("natGas_monthlyModelCons", {equipRef: smId}).isNull)                 missing = missing.addRow({virtualPointCategory: "Missing Model Point"})

     return missing.sortDis()

   end

-  if (opts.get("pointType") == "extra") return readAll(point and equipRef == smId).findAll(r => r.missing("pointQueryTarget") or (r.has("pointQueryTarget") and pointQuery(r.get("pointQueryTarget"), {equipRef: smId}).isNull)).toGrid

-

+  if (opts.get("pointType") == "extra") do

+    validRecs: tags.map(point => if (pointQuery(point, {equipRef: smId}).isNonNull) pointQuery(point, {equipRef: smId}) else null).findAll(v => v.isNonNull).toGrid

+    extras: readAll(point and equipRef == smId).findAll(r => r.missing("pointQueryTarget") or (r.has("pointQueryTarget") and pointQuery(r.get("pointQueryTarget"), {equipRef: smId}).isNull)).toGrid

+    if(validRecs.isNull or validRecs.isEmpty) return extras

+    extras = extras.findAll(r => not validRecs.colToList("id").contains(r.get("id")))

+    return extras

+  end

   // If none of the opts is called, return the opt key name and the valid values for that key.

   return {info: "Accepted \"pointType\" Opts: [tags, valid, missing, extra]"}.toGrid.addColMeta("info", {dis: "Information"})

 end
virtualBlueprintToPoint
--- virtualBlueprintToPoint (pod)
+++ virtualBlueprintToPoint (local)
@@ -8,17 +8,20 @@
     blueprint = blueprint.remove("id").remove("mod")

 

     //check for required tags

-    if (blueprint.missing("siteRef")) throw "Failed - missing siteRef"

-    if (blueprint.missing("equipRef")) throw "Failed - missing equipRef"

+    //if (blueprint.missing("siteRef")) throw "Failed - missing siteRef"

+    //if (blueprint.missing("equipRef")) throw "Failed - missing equipRef"

 

     //add and convert tags

-    tz: blueprint.get("siteRef").toRec.get("tz")

+    tz: blueprint.get("tz")

     virtualPointTags: blueprint.get("virtualPointTags").listTagsToDict

     if (virtualPointTags.isNull) virtualPointTags = {}

     dict: blueprint.merge({point,

                            virtualPoint,

                            his,

                            tz:tz,

+                           unit: blueprint.get("unit"),

+                           siteRef: blueprint.get("siteRef"),

+                           equipRef: blueprint.get("equipRef"),

                            -virtualBlueprint,

                            virtualPointCreator: userCur()->id,

                            -virtualPointTags,

virtualSyncHis
--- virtualSyncHis (pod)
+++ virtualSyncHis (local)
@@ -103,6 +103,7 @@
 

       //find all the non-null data

       // Added != nan() check 6/24/2025

+      if(data.isList) data = data.toGrid.reorderKeepCols(["ts"])

       data = data.findAll(r => r.get(data.colNames[1]).isNonNull and r.get(data.colNames[1]) != nan() and r.get(data.colNames[1]) != na())

 

       //check if units match

wdg_hisPoint_displayMissingDataPeriods

Sources are identical.

wdg_hisPoint_missingDataPeriods

Sources are identical.

wdg_siteMeterManager_missingPoints
--- wdg_siteMeterManager_missingPoints (pod)
+++ wdg_siteMeterManager_missingPoints (local)
@@ -16,5 +16,5 @@
   out: siteMeter_retrieveRequiredVirtualPoints(smId, {pointType: "missing"})

   out = out.map(r => return r.set("mixyRefs", smRec.get("siteRef").toStr+"|"+smId.toStr+"|"+r.get("id")))

   if (out.isEmpty) out = {info: "No Missing Points found."}.toGrid.addColMeta("info", {dis: "Information"})

-  return out.addMeta({title: "❌ Missing Points ❌"}).addColMeta("mixyRefs", {hide, hidden})

+  return out.addMeta({title: "❌ Missing Points ❌"})

 end
wdg_siteMeterManager_siteMeters

Sources are identical.

113 overridden function(s) total Export JSON

Browser Checks

Loading…

Steps run in order after login. Drag specs to reorder.
Register SkySpark view templates with named variables. Use them to insert navigate steps into specs.

Loading…

Current Pods

PodReportedCanonicalModifiedStatus
constellationBrowser 1.0.31 1.0.33 mismatch
constellationSftp 1.0.26 1.0.40 mismatch
kWEnergyCapExt 1.0.7 not reported
kWEPOExt 1.8 1.8 ok
kwGithubExt 1.1.1 1.1.1 ok
kwLinkAlc 0.0.5 0.0.5 ok
kwLinkAnalyticsExt 1.5.31 1.5.31 ok
kwLinkArcadia 2.0.16 2.0.16 ok
kwLinkBlink 0.0.1 not reported
kwLinkCoreExt 1.3.47 1.3.48 mismatch
kwLinkDesigo 2.0.9 2.0.9 ok
kwLinkDevToolsExt 1.0.12 1.0.12 ok
kwLinkDiagnosticsExt 0.1.4 not reported
kwLinkegratorExt 2.0.1 not reported
kwLinkEmcs 0.0.1 0.0.1 ok
kwLinkEnergyAgentExt 0.0.29 0.0.26 mismatch
kwLinkEventsExt 0.1.0 0.1.0 ok
kwLinkFCoreExt 1.0.39 1.0.39 ok
kwLinkLicenseExt 1.0.4 not reported
kwLinkMbcxExt 1.4.54 1.4.54 ok
kwLinkModelExt 1.1.21 1.1.21 ok
kwLinkRuleExt 0.4.0 0.4.2 mismatch
kwLinkUMichRuleExt 0.0.6 not reported
kwLinkVirtualExt 1.2.25 1.2.25 ok
kwLinkWorkflowExt 0.2.0 not reported
kWMonnitExt 1.1 not reported
kWTrackerExt 1.11 not reported
kWUtilityAPIExt 1.0.15 not reported
evseExt 1.0 not reported
ocpp 3.1.12 not reported
ocpp1_6 3.1.12 not reported
ocpp2_0_1 3.1.12 not reported
ocppExt 3.1.12 not reported

Queue Update

Updates are queued here and downloaded by the instance on its next check-in.

Configuration (.props)

Saved content will be written to lib/local/constellationSftp.props on the instance's next check-in.

Queued ✓

Live Pod Status

Last report:

Loading…

Update Log

Pushed AtPodVersionStatusErrorRestart
constellationSftp 1.0.26 success
constellationBrowser 1.0.31 success
constellationBrowser 1.0.29 success
constellationBrowser 1.0.27 success
constellationBrowser 1.0.25 success
constellationBrowser 1.0.24 success
constellationBrowser 1.0.22 success
constellationBrowser 1.0.21 success
constellationBrowser 1.0.20 success
constellationBrowser 1.0.19 success
constellationBrowser 1.0.18 success
constellationBrowser 1.0.15 success
constellationSftp 1.0.26 success
constellationBrowser 1.0.14 success
constellationBrowser 1.0.13 success
constellationSftp 1.0.25 success
constellationBrowser 1.0.11 success
constellationSftp 1.0.22 success
constellationBrowser 1.0.4 success
constellationSftp 1.0.22 success
constellationBrowser 1.0.4 success
constellationBrowser 1.0.8 success
constellationBrowser 1.0.7 success

Loading network data...