{
  "productInfo" : {
    "company" : "HighByte",
    "product" : "IntelligenceHub",
    "version" : "4.4.1",
    "build" : "2026.4.14.7",
    "stage" : "Release"
  },
  "project" : {
    "version" : 13,
    "connections" : [ {
      "name" : "canary",
      "uri" : "rest.client://canary",
      "tags" : [ "starter_canary" ],
      "writes" : {
        "flattenModeledValues" : false
      },
      "writeThrottle" : {
        "enabled" : false,
        "maxBatchSizePerTarget" : 1000,
        "batchDelay" : {
          "duration" : 20,
          "units" : "Milliseconds"
        }
      },
      "subscriptions" : { },
      "storeForward" : {
        "enabled" : false,
        "maxEntries" : 100,
        "waitOnFailureInterval" : {
          "duration" : 1,
          "units" : "Seconds"
        }
      },
      "settings" : {
        "authentication" : {
          "type" : "Bearer Token",
          "options" : {
            "token" : "{{System.Variables.canary_token}}"
          }
        },
        "baseURL" : "https://{{System.Variables.canary_host_port}}/api/v2",
        "ignoreCertificate" : true
      }
    }, {
      "name" : "mqtt",
      "uri" : "mqtt://localhost:1889",
      "description" : "This is a Connection to the Intelligence Hub MQTT broker created for the purpose of viewing Pipeline outputs.",
      "tags" : [ "starter_canary", "starter_ip21", "starter_pi_af", "starter_pi_da" ],
      "writes" : {
        "flattenModeledValues" : false
      },
      "writeThrottle" : {
        "enabled" : false,
        "maxBatchSizePerTarget" : 1000,
        "batchDelay" : {
          "duration" : 20,
          "units" : "Milliseconds"
        }
      },
      "subscriptions" : { },
      "storeForward" : {
        "enabled" : false,
        "maxEntries" : 100,
        "waitOnFailureInterval" : {
          "duration" : 1,
          "units" : "Seconds"
        }
      },
      "settings" : {
        "connectionTimeoutSeconds" : 10,
        "keepAliveSeconds" : 60,
        "requestTimeoutMS" : 5000,
        "maxInflight" : 1000,
        "cleanSession" : true,
        "mcpEnabled" : "disabled",
        "ssl" : false,
        "redundantBrokers" : [ ],
        "inputDiscovery" : "",
        "clientId" : "Intelligence_Hub_Reference"
      }
    } ],
    "inputs" : [ {
      "name" : "canary_input_all_pumps",
      "connection" : "canary",
      "type" : "rest.client",
      "qualifier" : {
        "acceptType" : "*/*",
        "method" : "GET",
        "contentType" : "application/json",
        "includeMetadata" : false,
        "endpointURL" : "/getAssetInstances?view=Roadshow - Water District&assetType=Pump",
        "isFile" : false
      },
      "cacheLifetime" : {
        "enabled" : true,
        "interval" : {
          "duration" : 1,
          "units" : "Days"
        }
      },
      "template" : {
        "type" : "Off"
      },
      "parameters" : {
        "type" : "EmptyParameters"
      }
    }, {
      "name" : "canary_input_last_values",
      "connection" : "canary",
      "type" : "rest.client",
      "qualifier" : {
        "acceptType" : "*/*",
        "method" : "GET",
        "contentType" : "application/json",
        "includeMetadata" : false,
        "endpointURL" : "/getTagData?deep=true&includeQuality=true&search={{this.pumpName}}",
        "isFile" : false
      },
      "cacheLifetime" : {
        "enabled" : false
      },
      "template" : {
        "type" : "Off"
      },
      "parameters" : {
        "type" : "inline",
        "model" : {
          "name" : "params",
          "tags" : [ ],
          "attributes" : [ {
            "attributeType" : "Internal",
            "name" : "pumpName",
            "nullable" : false,
            "required" : false,
            "array" : false,
            "defaultValue" : "CanaryWater.Guadalupe.Station01.Pump01",
            "internalType" : "String"
          } ]
        }
      }
    }, {
      "name" : "canary_input_metadata_tags",
      "connection" : "canary",
      "type" : "rest.client",
      "qualifier" : {
        "acceptType" : "*/*",
        "method" : "GET",
        "contentType" : "application/json",
        "includeMetadata" : false,
        "endpointURL" : "/getTagProperties?deep=true&path=Roadshow - Water District.CanaryWater&search=Pump",
        "isFile" : false
      },
      "cacheLifetime" : {
        "enabled" : false
      },
      "template" : {
        "type" : "Off"
      },
      "parameters" : {
        "type" : "EmptyParameters"
      }
    }, {
      "name" : "canary_input_tag_list",
      "connection" : "canary",
      "type" : "rest.client",
      "qualifier" : {
        "acceptType" : "*/*",
        "method" : "GET",
        "contentType" : "application/json",
        "includeMetadata" : false,
        "endpointURL" : "/browseTags?path=Roadshow - Water District.CanaryWater&search=Pump&deep=true",
        "isFile" : false
      },
      "cacheLifetime" : {
        "enabled" : true,
        "interval" : {
          "duration" : 1,
          "units" : "Days"
        }
      },
      "template" : {
        "type" : "Off"
      },
      "parameters" : {
        "type" : "EmptyParameters"
      }
    }, {
      "name" : "canary_input_values_time_span",
      "connection" : "canary",
      "type" : "rest.client",
      "qualifier" : {
        "acceptType" : "*/*",
        "method" : "GET",
        "contentType" : "application/json",
        "includeMetadata" : false,
        "endpointURL" : "/getTagData?deep=true&includeQuality=true&startTime={{this.startTime}}&endTime={{this.endTime}}&path=Roadshow - Water District.CanaryWater&search=Pump",
        "isFile" : false
      },
      "cacheLifetime" : {
        "enabled" : false
      },
      "template" : {
        "type" : "Off"
      },
      "parameters" : {
        "type" : "inline",
        "model" : {
          "name" : "params",
          "tags" : [ ],
          "attributes" : [ {
            "attributeType" : "Internal",
            "name" : "startTime",
            "nullable" : false,
            "required" : false,
            "array" : false,
            "defaultValue" : "2026-05-13T20:50:37.2340000-00:00",
            "internalType" : "String"
          }, {
            "attributeType" : "Internal",
            "name" : "endTime",
            "nullable" : false,
            "required" : false,
            "array" : false,
            "defaultValue" : "2026-05-13T20:58:07.2320000-00:00",
            "internalType" : "String"
          } ]
        }
      }
    } ],
    "outputs" : [ ],
    "modeling" : {
      "models" : [ {
        "name" : "starter_values",
        "description" : "This Model defines a simple narrow schema and supports multiple historian related starter solutions.",
        "groupAs" : "/starter_hist_values",
        "tags" : [ "starter_canary", "starter_ip21", "starter_pi_da" ],
        "attributes" : [ {
          "attributeType" : "Internal",
          "name" : "tagId",
          "nullable" : false,
          "required" : true,
          "array" : false,
          "internalType" : "String"
        }, {
          "attributeType" : "Internal",
          "name" : "valueNumeric",
          "nullable" : true,
          "required" : false,
          "array" : false,
          "internalType" : "Real64"
        }, {
          "attributeType" : "Internal",
          "name" : "valueOther",
          "nullable" : true,
          "required" : false,
          "array" : false,
          "internalType" : "String"
        }, {
          "attributeType" : "Internal",
          "name" : "quality",
          "nullable" : true,
          "required" : false,
          "array" : false,
          "internalType" : "String"
        }, {
          "attributeType" : "Internal",
          "name" : "time",
          "nullable" : false,
          "required" : true,
          "array" : false,
          "internalType" : "DateTime"
        }, {
          "attributeType" : "Internal",
          "name" : "transactionType",
          "nullable" : true,
          "required" : false,
          "array" : false,
          "internalType" : "String"
        } ]
      } ],
      "instances" : [ ]
    },
    "conditions" : [ ],
    "functions" : [ ],
    "tags" : [ {
      "name" : "starter_canary"
    } ],
    "pipelines" : [ {
      "name" : "starter_output_canary_time_span_values_to_mqtt",
      "description" : "This Pipeline obtains value changes for a time span from Canary and models the data before publishing the data to the Intelligence Hub MQTT Broker.",
      "groupAs" : "/starter_hist_read_time_span_values",
      "tags" : [ "starter_canary" ],
      "inputStages" : [ "get_times" ],
      "stages" : [ {
        "name" : "read_values",
        "display" : {
          "position" : {
            "x" : 690,
            "y" : 0
          }
        },
        "description" : "This Stage reads values for the given time span from Canary.",
        "config" : {
          "type" : ".ReadConfig",
          "failureOutputs" : [ ],
          "reference" : {
            "type" : "Input",
            "name" : "canary_input_values_time_span",
            "path" : "",
            "params" : {
              "startTime" : "{{event.metadata.canaryStartTime}}",
              "endTime" : "{{event.metadata.canaryEndTime}}"
            },
            "connectionName" : "canary"
          }
        },
        "outputs" : [ "elevate_data" ]
      }, {
        "name" : "to_mqtt",
        "display" : {
          "position" : {
            "x" : 3390,
            "y" : 0
          }
        },
        "description" : "This Stage publishes the payload to the Intelligence Hub MQTT broker which is being used to view the output.",
        "config" : {
          "type" : ".DynamicWriteConfig",
          "failureOutputs" : [ ],
          "connectionReference" : "{{Connection.mqtt}}",
          "qualifier" : {
            "topic" : "Time_Span_Values_Canary",
            "qos" : 0,
            "namedRoot" : false,
            "retained" : false,
            "breakupArrays" : false,
            "filterList" : [ "_model", "_name", "_timestamp" ]
          },
          "qualifierExpression" : "",
          "writeReturn" : "ignore"
        },
        "outputs" : [ ]
      }, {
        "name" : "size_buffer",
        "display" : {
          "position" : {
            "x" : 2940,
            "y" : 0
          }
        },
        "description" : "A buffer Stage is often used before writing data to a Data Lake or Data Warehouse.",
        "config" : {
          "type" : ".SizedBufferConfig",
          "windowExpression" : "stage.setBufferKey(null);",
          "windowSize" : 100,
          "timeout" : {
            "duration" : 0,
            "units" : "Seconds"
          }
        },
        "outputs" : [ "to_mqtt" ]
      }, {
        "name" : "breakup_array",
        "display" : {
          "position" : {
            "x" : 2040,
            "y" : 0
          }
        },
        "description" : "The Stage breakups up the tag array.",
        "config" : {
          "type" : ".BreakupConfig",
          "breakupType" : "array",
          "depth" : 1
        },
        "outputs" : [ "apply_model" ]
      }, {
        "name" : "elevate_data",
        "display" : {
          "position" : {
            "x" : 1140,
            "y" : 0
          }
        },
        "description" : "",
        "config" : {
          "type" : ".JavaScriptTransformConfig",
          "transformExpression" : "stage.setValue(event.value.data);"
        },
        "outputs" : [ "breakup_object" ]
      }, {
        "name" : "apply_model",
        "display" : {
          "position" : {
            "x" : 2490,
            "y" : 0
          }
        },
        "description" : "This Stage applies the desired schema for the values payload.",
        "config" : {
          "type" : ".ModelConfig",
          "model" : "starter_values",
          "objectName" : "",
          "initExpression" : "",
          "attributes" : [ {
            "name" : "tagId",
            "expression" : {
              "type" : "PipelineStageReference",
              "stageReference" : "event.metadata.breakupName"
            }
          }, {
            "name" : "valueNumeric",
            "expression" : {
              "type" : "JavaScript",
              "expression" : "// checks if event.value.v is not not a number (is a number), not null, not blank and is finite number (not +Infinity, or -Infinity).   If true, cast event.value.v to a number.  If false, set to null.\r\nif (!isNaN(event.value.v) && event.value.v !==null && event.value.v !== \"\" && isFinite(event.value.v))\r\n{\r\n\treturn Number(event.value.v);\r\n}\r\nelse\r\n{\r\n\treturn null;\r\n}"
            }
          }, {
            "name" : "valueOther",
            "expression" : {
              "type" : "JavaScript",
              "expression" : "// checks if event.value.v is not not a number (is a number), not null, not blank and is finite number (not +Infinity, or -Infinity).   If true, set to null.  If false, set to event.value.v.\r\nif (!isNaN(event.value.v) && event.value.v !==null && event.value.v !== \"\" && isFinite(event.value.v))\r\n{\r\n\treturn null;\r\n}\r\nelse\r\n{\r\n\treturn (event.value.v);\r\n}"
            }
          }, {
            "name" : "quality",
            "expression" : {
              "type" : "PipelineStageReference",
              "stageReference" : "event.value.q"
            }
          }, {
            "name" : "time",
            "expression" : {
              "type" : "JavaScript",
              "expression" : "function canaryToIsoString(canaryDateStr) {\r\n  // Example input: \"2025-05-11T05:00:00.0000000-0000\"\r\n\r\n  // Extract main parts\r\n  const [dateTime, fractionalAndOffset] = canaryDateStr.split('.');\r\n  const fractionalSeconds = fractionalAndOffset.slice(0, 7); // \"0000000\"\r\n  const rawOffset = fractionalAndOffset.slice(7);            // \"-0000\"\r\n\r\n  // Convert \"-0000\" to \"-00:00\"\r\n  const offsetFormatted = rawOffset.replace(/([+-]\\d{2})(\\d{2})/, '$1:$2');\r\n\r\n  // Take only first 3 fractional digits for milliseconds\r\n  const ms = fractionalSeconds.slice(0, 3);\r\n\r\n  // Construct ISO-compatible string\r\n  const isoString = `${dateTime}.${ms}${offsetFormatted}`;\r\n\r\n  // Convert to JavaScript Date and back to ISO string\r\n  const dateObj = new Date(isoString);\r\n  return dateObj.toISOString();\r\n}\r\n\r\n// Example usage:\r\nconst input = \"2025-05-11T05:00:00.0000000-0000\";\r\nconsole.log(canaryToIsoString(input)); \r\n// Output: \"2025-05-11T05:00:00.000Z\"\r\nvar time = event.value.t\r\nvar convertedTime = canaryToIsoString(time)\r\nreturn convertedTime"
            }
          }, {
            "name" : "transactionType",
            "expression" : {
              "type" : "PipelineStageReference",
              "stageReference" : ""
            }
          } ]
        },
        "outputs" : [ "size_buffer" ]
      }, {
        "name" : "get_times",
        "display" : {
          "position" : {
            "x" : 240,
            "y" : 0
          }
        },
        "description" : "This JavaScript Stage is used to set the interval and calculate the time span for the current execution of the Pipeline.",
        "config" : {
          "type" : ".JavaScriptTransformConfig",
          "transformExpression" : "function formatToCanaryStyle(date, fixedOffset = \"-00:00\") {\r\n  const year = date.getUTCFullYear();\r\n  const month = String(date.getUTCMonth() + 1).padStart(2, '0');\r\n  const day = String(date.getUTCDate()).padStart(2, '0');\r\n  const hour = String(date.getUTCHours()).padStart(2, '0');\r\n  const minute = String(date.getUTCMinutes()).padStart(2, '0');\r\n  const second = String(date.getUTCSeconds()).padStart(2, '0');\r\n  const milliseconds = String(date.getUTCMilliseconds()).padStart(3, '0');\r\n\r\n  const fractionalSeconds = milliseconds + \"0000\"; // pad to 7 digits\r\n\r\n  return `${year}-${month}-${day}T${hour}:${minute}:${second}.${fractionalSeconds}${fixedOffset}`;\r\n}\r\n// intervalSeconds\r\nlet intervalSeconds = 15;\r\n\r\n// intervalMilliseconds\r\nlet intervalMilliseconds = intervalSeconds * 1000;\r\n\r\n// set initial for start time\r\nlet initialStartTime = new Date();\r\ninitialStartTime.setTime(initialStartTime.getTime() - intervalMilliseconds);\r\n\r\n// read NextStartTime as the state value if it exists or initialStartTime if it does not exist.\r\nlet nextStartTime = state.pipeline.get(\"NextStartTime\", initialStartTime);\r\n\r\n// set startTime\r\nlet startTime = new Date(nextStartTime);\r\n\r\n// avoid flood of date if pipeline has not been run for a long period of time\r\nlet diffTime = initialStartTime - startTime;\r\nif (diffTime > intervalMilliseconds) {\r\n    startTime = initialStartTime;\r\n}\r\n\r\n// set endTime\r\nlet endTime = new Date();\r\nendTime.setTime(endTime.getTime());\r\n\r\n// set start and end time in CanaryStyle.\r\nlet canaryStartTime = formatToCanaryStyle(startTime);\r\nlet canaryEndTime = formatToCanaryStyle(endTime);\r\nstage.setMetadata(\"canaryStartTime\", canaryStartTime);\r\nstage.setMetadata(\"canaryEndTime\", canaryEndTime);\r\n\r\n// set NextStartTime pipeline state to endTime of current run.\r\nstate.pipeline.set(\"NextStartTime\", canaryEndTime);\r\n\r\nstage.setValue(event.value);"
        },
        "outputs" : [ "read_values" ]
      }, {
        "name" : "breakup_object",
        "display" : {
          "position" : {
            "x" : 1590,
            "y" : 0
          }
        },
        "description" : "The Stage breakups up the object payload per tag.",
        "config" : {
          "type" : ".BreakupConfig",
          "breakupType" : "object",
          "depth" : 1
        },
        "outputs" : [ "breakup_array" ]
      } ],
      "trackActivity" : false,
      "triggers" : [ {
        "name" : "polled_trigger",
        "display" : {
          "position" : {
            "x" : -450,
            "y" : 0
          }
        },
        "description" : "The trigger Stage initiates the Pipeline.",
        "config" : {
          "type" : ".TriggerPolled",
          "enabled" : false,
          "interval" : {
            "duration" : 15,
            "units" : "Seconds"
          },
          "mode" : "interval"
        }
      } ],
      "errorHandler" : {
        "type" : "default"
      }
    } ],
    "namespace" : [ ]
  },
  "network" : {
    "groups" : [ ],
    "hubs" : [ ]
  }
}