{
  "productInfo" : {
    "company" : "HighByte",
    "product" : "IntelligenceHub",
    "version" : "4.5.0",
    "build" : "2026.8.7.376",
    "stage" : "Alpha"
  },
  "project" : {
    "version" : 14,
    "connections" : [ {
      "name" : "canary",
      "uri" : "rest.client://canary",
      "description" : "This is a Connection to Canary Historian. The purpose of the Connection is to obtain data for starter solution examples and, specifically starter solutions related to Canary. This Connection interacts with the Canary Views Service API.  https://readapi.canarylabs.com/26.5/\n",
      "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",
        "maxMessagesReceived" : 0
      }
    } ],
    "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"
      },
      "referenceMap" : [ ],
      "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"
      },
      "referenceMap" : [ ],
      "parameters" : {
        "type" : "inline",
        "model" : {
          "name" : "params",
          "tags" : [ ],
          "attributes" : [ {
            "attributeType" : "Internal",
            "name" : "pumpName",
            "nullable" : false,
            "required" : false,
            "array" : false,
            "defaultValue" : "CanaryWater.Guadalupe.Station01.Pump01",
            "internalType" : {
              "variant" : "String"
            }
          } ],
          "modelVersion" : ""
        }
      }
    }, {
      "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"
      },
      "referenceMap" : [ ],
      "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"
      },
      "referenceMap" : [ ],
      "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"
      },
      "referenceMap" : [ ],
      "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" : {
              "variant" : "String"
            }
          }, {
            "attributeType" : "Internal",
            "name" : "endTime",
            "nullable" : false,
            "required" : false,
            "array" : false,
            "defaultValue" : "2026-05-13T20:58:07.2320000-00:00",
            "internalType" : {
              "variant" : "String"
            }
          } ],
          "modelVersion" : ""
        }
      }
    } ],
    "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" : {
            "variant" : "String"
          }
        }, {
          "attributeType" : "Internal",
          "name" : "valueNumeric",
          "nullable" : true,
          "required" : false,
          "array" : false,
          "internalType" : {
            "variant" : "Real64"
          }
        }, {
          "attributeType" : "Internal",
          "name" : "valueOther",
          "nullable" : true,
          "required" : false,
          "array" : false,
          "internalType" : {
            "variant" : "String"
          }
        }, {
          "attributeType" : "Internal",
          "name" : "quality",
          "nullable" : true,
          "required" : false,
          "array" : false,
          "internalType" : {
            "variant" : "String"
          }
        }, {
          "attributeType" : "Internal",
          "name" : "time",
          "nullable" : false,
          "required" : true,
          "array" : false,
          "internalType" : {
            "variant" : "DateTime"
          }
        }, {
          "attributeType" : "Internal",
          "name" : "transactionType",
          "nullable" : true,
          "required" : false,
          "array" : false,
          "internalType" : {
            "variant" : "String"
          }
        } ],
        "modelVersion" : ""
      } ],
      "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" : "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" : 1140,
            "y" : 0
          }
        },
        "description" : "The Stage breakups up the object payload per tag.",
        "config" : {
          "type" : ".BreakupConfig",
          "breakupType" : "object",
          "depth" : 1
        },
        "outputs" : [ "apply_model" ]
      }, {
        "name" : "apply_model",
        "display" : {
          "position" : {
            "x" : 1590,
            "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[0].v) && event.value[0].v !==null && event.value[0].v !== \"\" && isFinite(event.value[0].v))\r\n{\r\n\treturn Number(event.value[0].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[0].v) && event.value[0].v !==null && event.value[0].v !== \"\" && isFinite(event.value[0].v))\r\n{\r\n\treturn null;\r\n}\r\nelse\r\n{\r\n\treturn (event.value[0].v);\r\n}"
            }
          }, {
            "name" : "quality",
            "expression" : {
              "type" : "PipelineStageReference",
              "stageReference" : "event.value[0].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[0].t\r\nvar convertedTime = canaryToIsoString(time)\r\nreturn convertedTime"
            }
          }, {
            "name" : "transactionType",
            "expression" : {
              "type" : "PipelineStageReference",
              "stageReference" : ""
            }
          } ],
          "expandArray" : true
        },
        "outputs" : [ "to_mqtt" ]
      }, {
        "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" : ".data",
            "params" : {
              "startTime" : "{{event.metadata.canaryStartTime}}",
              "endTime" : "{{event.metadata.canaryEndTime}}"
            },
            "connectionName" : "canary"
          },
          "handleGoodNoData" : false,
          "goodNoDataOutputs" : [ ]
        },
        "outputs" : [ "breakup_object" ]
      }, {
        "name" : "to_mqtt",
        "display" : {
          "position" : {
            "x" : 2040,
            "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}}",
          "eventPath" : "",
          "connectionType" : "mqtt",
          "qualifier" : {
            "topic" : "Time_Span_Values_Canary",
            "qos" : 0,
            "namedRoot" : false,
            "retained" : false,
            "breakupArrays" : false,
            "filterList" : [ "_model", "_name", "_timestamp" ]
          },
          "qualifierExpression" : "",
          "writeReturn" : "ignore"
        },
        "outputs" : [ ]
      } ],
      "trackActivity" : false,
      "queueSize" : 10000,
      "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" : [ ],
    "lookupMap" : [ ],
    "servers" : {
      "i3x" : {
        "historyReadPipeline" : {
          "type" : "Empty"
        },
        "writePipeline" : {
          "type" : "Empty"
        }
      }
    }
  },
  "network" : {
    "groups" : [ ],
    "hubs" : [ ]
  }
}