Dataflow-Light-Theme

This is the Light theme for Data Flow.

[{"id":"c486f6db.c58c78","type":"ui_template","z":"106ddccd.912d53","group":"54212e75.e6902","name":"Display vtView Dashboard","order":0,"width":"0","height":"0","format":"<!-- Load c3.css -->\n<link rel='stylesheet prefetch' href='/storflyiotvtview/bundle/c3/css/c3.min.css'>\n<link rel='stylesheet prefetch' href='/storflyiotvtview/bundle/material/css/angular-material.min.css'>\n\n<!-- Load d3.js and c3.js -->\n<script src='/storflyiotvtview/bundle/d3/js/d3.min.js'></script>\n<script src='/storflyiotvtview/bundle/c3/js/c3.min.js'></script>\n\n{{ setCurrentDevice(msg.payload.errorDevice, msg.payload.device) }}\n\n<div>\n  <div class='md-padding layout-row' layout=\"row\" layout-wrap>\n    <div class=\"flex-21 device-menu\">\n      <md-card style=\"width: 290px; height: auto;\">\n        <md-card-header class=\"gray-box gray-box-header\">\n          <md-card-avatar>\n            <img src=\"/storflyiotvtview/bundle/images/virtium-logo.png\" />\n          </md-card-avatar>\n          <md-card-header-text>\n            <span class=\"md-title\">StorFly IoT</span>\n            <span class=\"md-subhead\">Intelligent-Storage</span>\n          </md-card-header-text>\n        </md-card-header>\n        <md-card-content class=\"gray-box gray-box-content\">\n          <div class=\"ng-scope layout-row\">\n            <md-list class=\"device-menu-body\" flex>\n              <md-list-item class=\"md-3-line\" ng-repeat=\"errorDevice in msg.payload.errorDevice\" ng-click=\"chooseDevice(errorDevice.ipAddress)\">\n                <div class=\"md-list-item-text\" layout=\"column\" ng-if=\"errorDevice.ipAddress !== undefined\">\n                  <div class=\"flex-50\">\n                    <h4 class=\"title\" style=\"font-size: 18px\">{{ errorDevice.ipAddress }}</h4>\n                    <div class=\"icon\" ng-class=\"deviceIpAddress === errorDevice.ipAddress ? 'actived' : ''\"></div>\n                  </div>\n                  <h4 style=\"color: gray!important\">{{ errorDevice.error }}</h4>\n                </div>\n              </md-list-item>\n              <md-list-item class=\"md-3-line\" ng-repeat=\"device in msg.payload.device\" ng-click=\"chooseDevice(device.ipAddress)\">\n                <div class=\"md-list-item-text\" layout=\"column\" ng-if=\"device.ipAddress !== undefined\">\n                  <div class=\"flex-50\">\n                    <h4 class=\"title\" style=\"font-size: 18px;\">{{ device.ipAddress }}</h4>\n                    <div class=\"icon\" ng-class=\"deviceIpAddress === device.ipAddress ? 'actived' : ''\"></div>\n                  </div>\n                  <div class=\"flex-50\">\n                    <h4 style=\"color: gray!important\">{{ device.serial }}</h4>\n                  </div>\n                </div>\n              </md-list-item>\n            </md-list>\n          </div>\n        </md-card-content>\n      </md-card>\n    </div>\n    <div class=\"flex-80 device-content\">\n      <div layout=\"row\">\n        <md-card flex ng-repeat=\"errorDevice in msg.payload.errorDevice\" ng-if=\"errorDevice.ipAddress === deviceIpAddress\">\n          <md-card-content class=\"gray-box gray-box-content\">\n            <div layout=\"row\">\n              <div flex>\n                <div flex class=\"gray-box gray-box-content\">\n                  <div class=\"ng-scope layout-row\">\n                    <div class=\"flex-50 label-short-text\">\n                      <h5>IP Address</h5>\n                    </div>\n                    <div class=\"flex-50\">\n                      <h5>{{errorDevice.ipAddress}}</h5>\n                    </div>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </md-card-content>\n        </md-card>\n      </div>\n      <div layout=\"row\">\n        <md-card flex ng-repeat=\"errorDevice in msg.payload.errorDevice\" ng-if=\"errorDevice.ipAddress === deviceIpAddress\">\n          <md-card-content class=\"gray-box gray-box-content\">\n            <div layout=\"row\">\n              <div flex>\n                <div flex class=\"gray-box gray-box-content\">\n                  <div class=\"ng-scope layout-row\">\n                    <div class=\"flex-50\">\n                      <h5>{{errorDevice.error}}</h5>\n                    </div>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </md-card-content>\n        </md-card>\n      </div>\n      <div style=\"height: auto;\" ng-repeat=\"device in msg.payload.device\" ng-if=\"device.ipAddress === deviceIpAddress\">\n        <div layout=\"{{region.orientationRegion === 'Vertical' ? 'column' : 'row' }}\" ng-repeat=\"region in device.regions\">\n          <md-card flex style=\"height: auto;\" ng-repeat=\"group in region.groups\">\n            <md-card-header class=\"gray-box gray-box-header\" ng-if=\"group.groupName !== undefined\">\n              <md-card-header-text>\n                <span class=\"md-title\">{{group.groupName}}</span>\n              </md-card-header-text>\n            </md-card-header>\n            <md-card-content class=\"gray-box gray-box-content\">\n              <div layout=\"{{group.orientationGroup === 'Vertical' ? 'column' : 'row' }}\">\n                <div flex ng-repeat=\"component in group.components\">\n                  <div flex class=\"gray-box gray-box-content\" ng-if=\"component.displayMode === 'Message'\">\n                    <div class=\"ng-scope layout-row\">\n                      <h5>{{component.value}}</h5>\n                    </div>\n                  </div>\n                  <div flex class=\"gray-box gray-box-content\" ng-if=\"component.displayMode === 'ShortLabel'\">\n                    <div class=\"ng-scope layout-row\">\n                      <div class=\"flex-50 label-short-text\">\n                        <h5>{{component.name}}</h5>\n                      </div>\n                      <div class=\"flex-50\">\n                        <h5>{{component.value}}</h5>\n                      </div>\n                    </div>\n                  </div>\n                  <div flex class=\"gray-box gray-box-content\" ng-if=\"component.displayMode === 'LongLabel'\">\n                    <div class=\"ng-scope layout-row\">\n                      <div class=\"flex-50 label-long-text\">\n                        <h5>{{component.name}}</h5>\n                      </div>\n                      <div class=\"flex-50\">\n                        <h5>{{component.value}}</h5>\n                      </div>\n                    </div>\n                  </div>\n                  <div flex class=\"gray-box gray-box-content gaugeChartDiv\" ng-if=\"component.displayMode === 'Gauge'\">\n                    <div layout=\"row\" layout-align=\"center center\" class=\"layout-row layout-align-center-center\">\n                      <h5>{{component.name}}</h5>\n                    </div>\n                    <div layout=\"row\" layout-align=\"center center\" class=\"layout-row layout-align-center-center\">\n                      <div id=\"{{getGaugeChartId(component.ipAddress, component.regionIndex, component.groupIndex, $index)}}\" class=\"chartGauge\"></div>\n                      {{displayGaugueChart(component.ipAddress, component.regionIndex, component.groupIndex, $index, component.value)}}\n                    </div>\n                  </div>\n                  <div flex class=\"gray-box gray-box-content\" ng-if=\"component.displayMode === 'Table'\">\n                    <div layout=\"row\" class=\"layout-row\" ng-if=\"component.name !== undefined\">\n                      <h5>{{component.name}}</h5>\n                    </div>\n                    <div layout=\"row\" class=\"layout-row\" ng-if=\"getEventMessageType(component.value) === 'string'\">\n                      <h5>{{ component.value }}</h5>\n                    </div>\n                    <div layout=\"row\" class=\"layout-row\" ng-if=\"getEventMessageType(component.value) === 'object'\">\n                      <md-list class=\"md-dense list-view-body\" flex>\n                        <md-list-item class=\"md-2-line list-view-row list-view-header\">\n                          <div class=\"md-list-item-text\" layout=\"row\" layout-align=\"start center\">\n                            <h5 flex=\"5\">#</h5>\n                            <h5 flex=\"10\">Serverity</h5>\n                            <h5 flex=\"40\">Note</h5>\n                            <h5 flex=\"30\">Date</h5>\n                            <h5 flex=\"30\">Durations</h5>\n                          </div>\n                        </md-list-item>\n                        <md-list-item class=\"md-2-line list-view-row\" ng-repeat=\"item in component.value\" ng-class=\"!($index % 2) ? 'list-view-row-alt' : ''\">\n                          <div class=\"md-list-item-text\" layout=\"row\" layout-align=\"start center\">\n                            <h5 flex=\"5\">{{ $index + 1 }}</h5>\n                            <h5 flex=\"10\" class=\"label-{{ item.serverity }}\"><span>{{ item.serverity }}</span></h5>\n                            <h5 flex=\"40\">{{ item.note }}</h5>\n                            <h5 flex=\"30\">{{ item.startDate }}</h5>\n                            <h5 flex=\"30\">{{ displayDurations(item.startDateTimeStamp) }}</h5>\n                          </div>\n                        </md-list-item>\n                      </md-list>\n                    </div>\n                  </div>\n                </div>\n              </div>\n            </md-card-content>\n          </md-card>\n        </div>\n      </div>\n    </div>\n  </div>\n</div>\n\n<script>\n  (function (scope) {\n    //store current device id and frist loading state\n    scope.deviceIpAddress = 0;\n    scope.isFirstLoaded = false;\n\n    //set current device when first loading\n    scope.setCurrentDevice = function (errorDevice, device) {\n      if (scope.isFirstLoaded) {\n        return;\n      }\n      if (errorDevice === undefined && device === undefined) {\n        return;\n      }\n      if (device.length !== 0) {\n        scope.deviceIpAddress = device[0].ipAddress;\n      }\n      else if (errorDevice.length !== 0) {\n        scope.deviceIpAddress = errorDevice[0].ipAddress;\n      }\n      scope.isFirstLoaded = true;\n    }\n\n    //chose current device\n    scope.chooseDevice = function (ipAddress) {\n      scope.deviceIpAddress = ipAddress;\n    };\n\n    //draw Remaining Life chart\n    scope.displayGaugueChart = function (deviceIpAddress, regionIndex, groupIndex, index, value) {\n      var name = deviceIpAddress.replace(/\\./g, '') + regionIndex + groupIndex + index;\n      value = Number(value.toFixed(0));\n\n      scope.drawChart(name, value);\n    };\n\n    scope.drawChart = function (chartId, remainingLife) {\n      c3.generate({\n        bindto: '#gaugeChart' + chartId,\n        data: {\n          columns: [['Data', remainingLife]],\n          type: 'gauge'\n        },\n        color: {\n          pattern: ['#F49154', '#2AB050'],\n          threshold: {\n            values: [10, 100]\n          }\n        },\n        gauge: { label: { format: function (value) { return value + '%'; } } },\n        size: {\n          height: 180\n        }\n      });\n    }\n\n    //help functions\n    //check if display table mode\n    scope.hasDisplayTableMode = function (components) {\n      for (var i = 0; i < components.length; i++) {\n        if (components[i].displayMode === 'Table')\n          return true;\n      }\n      return false;\n    }\n    //get gauge chart id\n    scope.getGaugeChartId = function (deviceIpAddress, regionIndex, groupIndex, index) {\n      var id = 'gaugeChart' + deviceIpAddress.replace(/\\./g, '') + regionIndex + groupIndex + index;\n      return id;\n    }\n    //display message of events\n    scope.getEventMessageType = function (value) {\n      return (typeof value);\n    }\n    //get durations of events\n    scope.getDurations = function (startDateTimeStamp) {\n      var now = new Date();\n      var nowTimeStamp = now.getTime();\n      var delta = nowTimeStamp - startDateTimeStamp;\n      return delta;\n    }\n    //display durations, convert miliseconds to duration\n    scope.displayDurations = function (startDateTimeStamp) {\n      var res = \"\";\n      var millisec = scope.getDurations(startDateTimeStamp);\n      var seconds = (millisec / 1000).toFixed(0);\n      var minutes = Math.floor(seconds / 60);\n      var hours = Math.floor(minutes / 60);\n      var days = Math.floor(hours / 24);\n      var months = Math.floor(days / 30);\n      var years = Math.floor(months / 12);\n\n      if (!minutes || minutes === 1) {\n        res = \"just now\";\n      } else if (minutes > 1 && minutes < 59) {\n        res = minutes + \" minutes ago\";\n      } else if (hours === 1) {\n        res = hours + \" hour ago\";\n      } else if (hours > 1 && hours < 24) {\n        res = hours + \" hours ago\";\n      } else if (days === 1) {\n        res = days + \" day ago\";\n      } else if (days > 1 && days < 30) {\n        res = days + \" days ago\";\n      } else if (months === 1) {\n        res = months + \" month ago\";\n      } else if (months > 1 && months < 12) {\n        res = months + \" months ago\";\n      } else if (years === 1) {\n        res = years + \" year ago\";\n      } else {\n        res = years + \" years ago\";\n      }\n      return res;\n    }\n    //end help functions\n  })(scope);\n\n</script>\n\n<style>\n  div[id='StorFly IoT'] p:first-child {\n    display: none;\n  }\n\n  body {\n    background-color: #fff!important;\n  }\n\n  .nr-dashboard-theme,\n  .nr-dashboard-theme ui-card-panel,\n  .nr-dashboard-template {\n    border: none;\n    box-shadow: none;\n    height: auto!important;\n  }\n\n  h5 {\n    margin-top: 7px;\n    margin-bottom: 7px;\n    font-weight: 500;\n  }\n\n  .divider {\n    margin-bottom: 7px;\n  }\n\n  .layout-row md-card {\n    box-shadow: none;\n    border: 1px solid #d3d3d3;\n    color: #111111;\n  }\n\n  .layout-row .description {\n    color: gray;\n  }\n\n  .layout-row .gray-box {\n    background-color: #fff;\n    margin: 1px;\n  }\n\n  .layout-row .gray-box-header {\n    min-height: 20px;\n    border-bottom: 1px solid #d3d3d3;\n    background-color: #f5f5f6;\n  }\n\n  .layout-row .gray-box-content {\n    margin-top: 1px;\n    height: 100%;\n  }\n\n  .c3-chart-arc .c3-gauge-value {\n    fill: #111111;\n  }\n\n  .c3-tooltip-name-Data {\n    color: #404040;\n  }\n\n  .chartGauge {\n    height: 200px;\n    /*margin-top: -30px;\n    margin-left: -80px;*/\n  }\n\n  .md-list-item-text .ng-binding,\n  .md-subhead {\n    font-weight: 500;\n  }\n\n  .md-title {\n    font-size: 16px;\n  }\n\n  .md-3-line {\n    padding: 0 !important;\n  }\n\n  .label-Critical span, .label-Warning span, .label-Informative span {\n    width: 100px;\n    display: block;\n    height: 20px;\n    padding-top: 4px;\n    color: #fff;\n    text-align: center;\n    white-space: nowrap;\n    border-radius: .25em;\n    font-size: 12px;\n  }\n\n  .label-Critical span {\n    background-color: #ed5565;\n  }\n\n  .label-Warning span{\n    background-color: #f8ac59;\n  }\n\n  .label-Informative span{\n    background-color: #23c6c8;\n  }\n\n  .label-date {\n    padding-top: 4px;\n    font-size: 12px;\n  }\n\n  .label-short-text {\n    max-width: 80px!important;\n  }\n\n  .label-text {\n    max-width: 120px!important;\n  }\n\n  .label-long-text {\n    max-width: 200px!important;\n  }\n\n  .nr-dashboard-theme .nr-dashboard-template .md-button {\n    background: none;\n  }\n\n  .nr-dashboard-theme .nr-dashboard-template .md-button:hover {\n    background: none;\n  }\n\n  .nr-dashboard-theme .nr-dashboard-template .md-button:active {\n     background-color: rgba(237, 237, 237, 0.5);\n  }\n\n  .list-view-row {\n    border: none;\n    height: 38px!important;\n    min-height: 38px!important;\n  }\n\n  .list-view-header {\n    border: none;\n  }\n\n  .list-view-row-alt {\n    background-color: none;\n  }\n\n  .device-menu .title {\n    float: left;\n  }\n\n  .device-menu .icon {\n    float: right;\n    color: #404040;\n  }\n\n  .device-menu .actived:before {\n    font-family: FontAwesome;\n    content: \"\\f058\";\n    font-size: 20px;\n  }\n\n  .device-menu-body, .list-view-body {\n    overflow-x: hidden;\n  }\n\n  .device-menu-body::-webkit-scrollbar-track, .list-view-body::-webkit-scrollbar-track {\n    border-radius: 2px !important;\n    background-color: #fff !important;\n  }\n\n  .device-menu-body::-webkit-scrollbar, .list-view-body::-webkit-scrollbar {\n    width: 6px !important;\n    background-color: #fff !important;\n  }\n\n  .device-menu-body::-webkit-scrollbar-thumb, .list-view-body::-webkit-scrollbar-thumb {\n    background-color: #d3d3d3 !important;\n    box-shadow: none !important;\n  }\n\n  .device-menu-body {\n    max-height: 831px;\n  }\n\n  .list-view-body {\n    max-height: 240px;\n  }\n</style>","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":3059.1470947265625,"y":728.016040802002,"wires":[[]]},{"id":"c3781d8b.bdaa9","type":"comment","z":"106ddccd.912d53","name":"Data Filter & Calculation Components","info":"","x":1022.5238342285156,"y":109.91674995422363,"wires":[]},{"id":"85107afc.302988","type":"function","z":"106ddccd.912d53","name":"","func":"\nreturn msg;","outputs":1,"noerr":0,"x":690.8411521911621,"y":743.3853740692139,"wires":[["27273aec.082626","8b72e83.88a8418","add492e7.5e0bf","dc6914fd.5c32c8","5ed199e5.ee9ae8","77e72b6e.580434","6577f425.14cf5c","1b007fe5.18e34","d1758371.c11f1","cae3ed35.369f2","2bf67958.84b5a6","c7e8a192.4524","1925a882.203f07","45390256.b10c8c","633e37e6.db16f8","1dc4847d.46315c","27c2d4e9.eacb0c","78cc4d66.2025b4"]]},{"id":"84b64282.f8dd3","type":"component display group","z":"106ddccd.912d53","name":"Storage Usage","layout":"Vertical","index":"3","x":2008.488338470459,"y":1016.9999732971191,"wires":[["3abddea6.eecce2"]]},{"id":"d2c7ac61.8fbb9","type":"component display region","z":"106ddccd.912d53","name":"Region 1","layout":"Vertical","index":"1","x":2271.7026977539062,"y":169.5714111328125,"wires":[["8339d69.1331528"]]},{"id":"866cb24c.421dd","type":"comment","z":"106ddccd.912d53","name":"Choose Display Mode","info":"","x":1725.333351135254,"y":104.5833740234375,"wires":[]},{"id":"4d75deaa.dd8c6","type":"function","z":"106ddccd.912d53","name":"Get Components","func":"/*jshint esversion: 6 */\n\n//newPayload contains:\n//\"device\": valid credential device\n//\"errorDevice\": invalid credential device or timeout request\nvar newPayload = {};\n\n//private methods\nfunction groupByGroupName(list, keyGetter) {\n  var map = new Map();\n  list.forEach((item) => {\n    var key = keyGetter(item);\n    var collection = map.get(key);\n    if (!collection) {\n      map.set(key, [item]);\n    } else {\n      collection.push(item);\n    }\n  });\n  var arr = Array.from(map, self => {\n    var key = self[0];\n    var val = self[1];\n    var components = [];\n    for (var i = 0; i < val.length; i++) {\n      components.push({ \"name\": val[i].name, \"value\": val[i].value, \"displayMode\": val[i].displayMode, \"ipAddress\": val[i].ipAddress, \n                        \"regionIndex\": val[i].regionIndex, \"groupIndex\": val[i].groupIndex });\n    }\n\n    //only support 2 types: Vertical or Horizontal, default is Vertical\n    if (val[0].orientationGroup === undefined || val[0].orientationGroup !== 'Horizontal') {\n      val[0].orientationGroup = \"Vertical\";\n    }\n\n    return { groupName: key, groupIndex: val[0].groupIndex, orientationGroup: val[0].orientationGroup, components: components };\n  });\n  return arr;\n}\n\nfunction groupByRegion(list, keyGetter) {\n  var map = new Map();\n  list.forEach((item) => {\n    var key = keyGetter(item);\n    var collection = map.get(key);\n    if (!collection) {\n      map.set(key, [item]);\n    } else {\n      collection.push(item);\n    }\n  });\n  var arr = Array.from(map, self => {\n    var key = self[0];\n    var val = self[1];\n    //only support 2 types: Vertical or Horizontal, default is Vertical\n    if (val[0].orientationRegion === undefined || val[0].orientationRegion !== 'Horizontal') {\n      val[0].orientationRegion = \"Vertical\";\n    }\n    var groups = groupByGroupName(val, x => x.groupName);\n    //order by groupIndex\n    groups = groups.sort(function (a, b) {\n      if (a.groupIndex < b.groupIndex) return -1;\n      if (a.groupIndex > b.groupIndex) return 1;\n      return 0;\n    });\n\n    return { regionName: key, regionIndex: val[0].regionIndex, orientationRegion: val[0].orientationRegion, groups: groups };\n  });\n  return arr;\n}\n\nfunction groupByIpAddress(list, keyGetter) {\n  var map = new Map();\n  list.forEach((item) => {\n    var key = keyGetter(item);\n    var collection = map.get(key);\n    if (!collection) {\n      map.set(key, [item]);\n    } else {\n      collection.push(item);\n    }\n  });\n  var arr = Array.from(map, self => {\n    var key = self[0];\n    var val = self[1];\n    var regions = groupByRegion(val, x => x.regionName);\n    //order by regionIndex\n    regions = regions.sort(function (a, b) {\n      if (a.regionIndex < b.regionIndex) return -1;\n      if (a.regionIndex > b.regionIndex) return 1;\n      return 0;\n    });\n    return { ipAddress: key, serial: val[0].serial, regions: regions };\n  });\n  return arr;\n}\n\n//store payload to context, for case there are devices were be timeout request\nvar oldPayload = context.get('oldPayload') || {};\n\n//const variable\nvar ERROR_AUTHENTICATION_FAILED = \"Authentication failed\";\nvar ERROR_TIMEOUT_REQUEST = \"Request time out. Please check device network connection.\";\n\n//find requestTimeDevice\nvar requestTimeoutDevice = msg.payload.filter(function (device) {\n  return (device.error !== undefined && ERROR_TIMEOUT_REQUEST == device.error);\n});\n\n//add errorDevice\nnewPayload.errorDevice = msg.payload.filter(function (device) {\n  return (device.error !== undefined && ERROR_AUTHENTICATION_FAILED == device.error);\n});\n\n//get components of valid credential device\nvar components = msg.payload.filter(function (device) {\n  return (device.error === undefined);\n});\n\n//remove duplicated components\ncomponents = components.filter((comp, index, self) =>\n  index === self.findIndex((t) => (\n    t.ipAddress === comp.ipAddress &&\n    t.serial === comp.serial &&\n    t.name === comp.name &&\n    t.displayMode === comp.displayMode &&\n    t.groupName === comp.groupName &&\n    t.groupIndex === comp.groupIndex &&\n    t.regionName === comp.regionName &&\n    t.regionIndex === comp.regionIndex\n  ))\n);\n\n//order by componentIndex\ncomponents = components.sort(function (a, b) {\n  if (a.componentIndex < b.componentIndex) return -1;\n  if (a.componentIndex > b.componentIndex) return 1;\n  return 0;\n});\n\n//group components into device\nnewPayload.device = groupByIpAddress(components, x => x.ipAddress);\n\n//if there are devices were be timeout request, then update them to error devices\nif (0 !== requestTimeoutDevice.length) {\n\n  //keep errorDevice is to update them to new payload\n  var errorDeviceTemp = newPayload.errorDevice;\n  //keep device is to update them to new payload\n  var deviceTemp = newPayload.device;\n\n  //get old payload\n  newPayload = oldPayload;\n\n  //init errorDevice, device\n  if (undefined === newPayload.errorDevice) {\n    newPayload.errorDevice = [];\n  }\n  if (undefined === newPayload.device) {\n    newPayload.device = [];\n  }\n\n  //insert devices into newPayload\n  newPayload.device = deviceTemp.reduce(function (coll, item) {\n    coll.push(item);\n    return coll;\n  }, newPayload.device);\n\n  //insert errorTemp devices into newPayload\n  newPayload.errorDevice = errorDeviceTemp.reduce(function (coll, item) {\n    coll.push(item);\n    return coll;\n  }, newPayload.errorDevice);\n\n  //insert requestTimeout devices into newPayload\n  newPayload.errorDevice = requestTimeoutDevice.reduce(function (coll, item) {\n    coll.push(item);\n    return coll;\n  }, newPayload.errorDevice);\n\n  //remove requestTimeout devices from newPayload.device\n  newPayload.device = newPayload.device.filter(item1 => requestTimeoutDevice.every(item2 => item2.ipAddress != item1.ipAddress));\n}\n\n//remove duplicate other devices\nnewPayload.device = newPayload.device.filter((device, index, self) =>\n  index === self.findIndex((t) => (\n    t.ipAddress === device.ipAddress\n  ))\n);\n\n//keep lastest errorDevice, remove duplicate other error device\nnewPayload.errorDevice = newPayload.errorDevice.filter((thing, index, self) =>\n  index === self.findIndex((t) => (\n    t.ipAddress === thing.ipAddress\n  ))\n);\n\n//remove devices from newPayload.errorDevice\nnewPayload.errorDevice = newPayload.errorDevice.filter(item1 => newPayload.device.every(item2 => item2.ipAddress != item1.ipAddress));\n\n//update flow context oldPayload\ncontext.set('oldPayload', newPayload);\n\nmsg.payload = newPayload;\nreturn msg;","outputs":1,"noerr":0,"x":2775.605827331543,"y":728.2396335601807,"wires":[["c486f6db.c58c78"]]},{"id":"8b72e83.88a8418","type":"function","z":"106ddccd.912d53","name":"Version","func":"//set component index\nmsg.payload.componentIndex = 1;\n\n//validation\nif(msg.payload.software === undefined)\n{\n    return;\n}\n\nmsg.payload.name = \"Version\";\nmsg.payload.value = \"1.0.1\";\n\nreturn msg;","outputs":1,"noerr":0,"x":932.2026977539062,"y":372.7618579864502,"wires":[["76ded9f2.6b67d8"]]},{"id":"53b8ba7.841fe44","type":"comment","z":"106ddccd.912d53","name":"Choose Region For Display","info":"","x":2331.000358581543,"y":99,"wires":[]},{"id":"b9915e7c.96681","type":"comment","z":"106ddccd.912d53","name":"Virtium StorFly Devices","info":"","x":407.0833435058594,"y":107.75006103515625,"wires":[]},{"id":"f2c44676.e685a8","type":"comment","z":"106ddccd.912d53","name":"Choose Group For Display","info":"","x":2026.500358581543,"y":100.24999904632568,"wires":[]},{"id":"93e1cc95.e6f72","type":"function","z":"106ddccd.912d53","name":"Format Data Written","func":"//calcuation\nfunction calculateDataWritten(dataWritten) {\n  return dataWritten + \" GB\";\n}\n\nvar dataWritten = calculateDataWritten(msg.payload.dataWritten);\nmsg.payload.value = dataWritten;\n\nreturn msg;","outputs":1,"noerr":0,"x":1294.9962692260742,"y":810.9643898010254,"wires":[["8607bba6.28cad8"]]},{"id":"27273aec.082626","type":"function","z":"106ddccd.912d53","name":"Spec Operation Temperature","func":"//set component index\nmsg.payload.componentIndex = 4;\n\nmsg.payload.name = \"Spec Operation Temperature\";\nmsg.payload.value = \"-40C to 85C\";\n\nreturn msg;","outputs":1,"noerr":0,"x":988.4884757995605,"y":541.3333172798157,"wires":[["2ec2d71e.5d9698"]]},{"id":"a362e25f.04d09","type":"inject","z":"106ddccd.912d53","name":"","topic":"","payload":"","payloadType":"date","repeat":"10","crontab":"","once":true,"x":103,"y":742.7224292755127,"wires":[["cdb49e2b.1c531","3fcb4987.516946"]]},{"id":"8339d69.1331528","type":"join","z":"106ddccd.912d53","name":"Join & Set timeout","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"4","count":"","x":2510.345390319824,"y":726.8889389038086,"wires":[["4d75deaa.dd8c6"]]},{"id":"a5551e5f.33fba","type":"comment","z":"106ddccd.912d53","name":"Data Format","info":"","x":1273.6665954589844,"y":109.66663646697998,"wires":[]},{"id":"3aad47a4.6b3948","type":"function","z":"106ddccd.912d53","name":"Format Year In Service","func":"//help method\nfunction formatYear (data) {\n      let _str = '';\n      let _tmpN = 0;\n\n      if (data < 0.00273) {\n        return '0 day';\n      }\n\n      _tmpN = Math.floor(data / 1);\n\n      if (_tmpN > 0) {\n        _str = _tmpN + ' year';\n        if (_tmpN > 1) {\n          _str += 's';\n        }\n      }\n\n      _tmpN = Number(((data % 1) * 365).toFixed(0));\n\n      if (_tmpN > 0) {\n        _str += ' ' + _tmpN + ' day';\n        if (_tmpN > 1) {\n          _str += 's';\n        }\n      }\n      \n      return _str;\n }\n\n//calculation\nfunction calculateYearInService(yearInService) {\n  var estimateEOLStr = formatYear(yearInService);\n  return estimateEOLStr;\n}\n\nvar yearInService = calculateYearInService(msg.payload.yearInService);\nmsg.payload.value = yearInService;\n\nreturn msg;","outputs":1,"noerr":0,"x":1292.6668395996094,"y":764.6666164398193,"wires":[["8607bba6.28cad8"]]},{"id":"ef81b83c.1edb48","type":"function","z":"106ddccd.912d53","name":"Format Remaining TBW","func":"//calculation\nfunction calculateRemainingTBW(remainingTbw) {\n    var res = remainingTbw.toFixed(2) + \" TB\";\n    if (remainingTbw === 0) {\n        res = \"0 MB\";\n    } else if (remainingTbw < 1.0) {\n    var gb = remainingTbw * 1024;\n        if (gb < 1.0) {\n          var mb = gb * 1024;\n          res = mb.toFixed(2) + \" MB\";\n        } else {\n          res = gb.toFixed(2) + \" GB\";\n        }\n    }\n    return res;\n}\n\nvar remaningTBW = calculateRemainingTBW(msg.payload.remainingTBW);\nmsg.payload.value = remaningTBW;\n\nreturn msg;","outputs":1,"noerr":0,"x":1305.6666259765625,"y":858.6666431427002,"wires":[["8607bba6.28cad8"]]},{"id":"1484774b.d80169","type":"function","z":"106ddccd.912d53","name":"Format Estimate EOL","func":"//help method\nfunction formatYear (data) {\n      let _str = '';\n      let _tmpN = 0;\n\n      if (data < 1) {\n        return '1 day';\n      }\n\n      _tmpN = Math.floor(data / 365);\n\n      if (_tmpN > 0) {\n        _str = _tmpN + ' year';\n        if (_tmpN > 1) {\n          _str += 's';\n        }\n      }\n\n      _tmpN = Math.floor(data % 365);\n\n      if (_tmpN > 0) {\n        _str += ' ' + _tmpN + ' day';\n        if (_tmpN > 1) {\n          _str += 's';\n        }\n      }\n\n      return _str;\n }\n\n//calculation\nfunction calculateEOL(estimateEOL, errorMessage) {\n  if (0 === estimateEOL && 'Not enough vtview data' === errorMessage) {\n    estimateEOL = 10 * 365; //default is 10 years\n  }\n  var estimateEOLStr = formatYear(estimateEOL);\n  return estimateEOLStr;\n}\n\nvar estimateEndOfLife = calculateEOL(msg.payload.estimateEOL, msg.payload.errorMessage);\nmsg.payload.value = estimateEndOfLife;\n\nreturn msg;","outputs":1,"noerr":0,"x":1294.9998779296875,"y":906.0000019073486,"wires":[["8607bba6.28cad8"]]},{"id":"6fa7bb9b.dc0cb4","type":"function","z":"106ddccd.912d53","name":"Format Free Disk Space","func":"msg.payload.value = msg.payload.freeDiskSpace * 100 / msg.payload.capacity;\n\nreturn msg;","outputs":1,"noerr":0,"x":1305.5358505249023,"y":1020.9047107696533,"wires":[["ec7d103e.f8d8f"]]},{"id":"d06a8518.eb5df8","type":"function","z":"106ddccd.912d53","name":"Format Used Disk Space","func":"msg.payload.value = Number((msg.payload.capacity - msg.payload.freeDiskSpace).toFixed(2)) + \" GB\";\n\nreturn msg;","outputs":1,"noerr":0,"x":1303.8692626953125,"y":1119.2380065917969,"wires":[["5d5a0809.06be48"]]},{"id":"83c747f4.b21ed8","type":"function","z":"106ddccd.912d53","name":"Format Free Disk Space","func":"msg.payload.value = msg.payload.freeDiskSpace + \" GB\";\n\nreturn msg;","outputs":1,"noerr":0,"x":1303.8693466186523,"y":1164.0952682495117,"wires":[["5d5a0809.06be48"]]},{"id":"c223002a.460db","type":"function","z":"106ddccd.912d53","name":"Format Total Disk Space","func":"msg.payload.value = msg.payload.capacity + \" GB\";\n\nreturn msg;","outputs":1,"noerr":0,"x":1302.9403457641602,"y":1212.0118961334229,"wires":[["5d5a0809.06be48"]]},{"id":"c605d71d.17fc08","type":"function","z":"106ddccd.912d53","name":"Format Error / Warning Message","func":"if(msg.payload.errorMessage === 'Not enough vtview data')\n{\n    var message = \"The device needs at least 100 hours of usage data to calculate Life Remaining information. The life information below are default information.\";\n    msg.payload.name = \"\";\n    msg.payload.value = message;    \n    return msg;\n}\n","outputs":1,"noerr":0,"x":1323.8692626953125,"y":711.2380294799805,"wires":[["760662fb.66eacc"]]},{"id":"52597aa1.6320f4","type":"function","z":"106ddccd.912d53","name":"Format Events","func":"//not show the name\nmsg.payload.name = undefined;\n\nreturn msg;","outputs":1,"noerr":0,"x":1273.8692626953125,"y":1365.2381029129028,"wires":[["fb504ce0.afacf"]]},{"id":"add492e7.5e0bf","type":"component display","z":"106ddccd.912d53","name":"IP Address","attributeName":"ipAddress","index":"1","x":945.7261581420898,"y":167.9620532989502,"wires":[["dfe36450.939878"]]},{"id":"dc6914fd.5c32c8","type":"component display","z":"106ddccd.912d53","name":"Serial","attributeName":"serial","index":"2","x":924.226318359375,"y":212.6105136871338,"wires":[["dfe36450.939878"]]},{"id":"77e72b6e.580434","type":"component display","z":"106ddccd.912d53","name":"Software","attributeName":"software","index":"2","x":932.2026977539062,"y":415.5714111328125,"wires":[["76ded9f2.6b67d8"]]},{"id":"5ed199e5.ee9ae8","type":"component display","z":"106ddccd.912d53","name":"Capacity","attributeName":"capacity","index":"3","x":932.2026977539062,"y":458.5714111328125,"wires":[["f65dcf61.135df"]]},{"id":"6577f425.14cf5c","type":"component display","z":"106ddccd.912d53","name":"Life Remaining","attributeName":"remainingLife","index":"1","x":947.2026977539062,"y":664.5714111328125,"wires":[["20058910.b69746"]]},{"id":"1b007fe5.18e34","type":"component display","z":"106ddccd.912d53","name":"Error Message","attributeName":"errorMessage","index":"2","x":944.2026977539062,"y":711.5714111328125,"wires":[["c605d71d.17fc08"]]},{"id":"27c2d4e9.eacb0c","type":"component display","z":"106ddccd.912d53","name":"Year In Service","attributeName":"yearInService","index":"3","x":946.2026977539062,"y":766.5714111328125,"wires":[["3aad47a4.6b3948"]]},{"id":"d1758371.c11f1","type":"component display","z":"106ddccd.912d53","name":"Data Written","attributeName":"dataWritten","index":"4","x":936.2026977539062,"y":811.5714111328125,"wires":[["93e1cc95.e6f72"]]},{"id":"cae3ed35.369f2","type":"component display","z":"106ddccd.912d53","name":"Remaining TBW","attributeName":"remainingTBW","index":"5","x":945.2026977539062,"y":859.5714111328125,"wires":[["ef81b83c.1edb48"]]},{"id":"2bf67958.84b5a6","type":"component display","z":"106ddccd.912d53","name":"Estimate Remainning Time","attributeName":"estimateEOL","index":"6","x":984.2026977539062,"y":905.5714111328125,"wires":[["1484774b.d80169"]]},{"id":"633e37e6.db16f8","type":"component display","z":"106ddccd.912d53","name":"Used","attributeName":"freeDiskSpace","index":"2","x":915.2026977539062,"y":1119.5714111328125,"wires":[["d06a8518.eb5df8"]]},{"id":"45390256.b10c8c","type":"component display","z":"106ddccd.912d53","name":"Free","attributeName":"freeDiskSpace","index":"3","x":915.2026977539062,"y":1164.5714111328125,"wires":[["83c747f4.b21ed8"]]},{"id":"1dc4847d.46315c","type":"component display","z":"106ddccd.912d53","name":"Free Disk Space","attributeName":"freeDiskSpace","index":"1","x":944.2026977539062,"y":1022.5714111328125,"wires":[["6fa7bb9b.dc0cb4"]]},{"id":"1925a882.203f07","type":"component display","z":"106ddccd.912d53","name":"Total","attributeName":"capacity","index":"4","x":916.2026977539062,"y":1213.5714111328125,"wires":[["c223002a.460db"]]},{"id":"c7e8a192.4524","type":"component display","z":"106ddccd.912d53","name":"Events","attributeName":"events","index":"1","x":912.2026977539062,"y":1364.5714111328125,"wires":[["52597aa1.6320f4"]]},{"id":"78cc4d66.2025b4","type":"component display","z":"106ddccd.912d53","name":"Model","attributeName":"model","index":"3","x":923.2026977539062,"y":258.5714111328125,"wires":[["dfe36450.939878"]]},{"id":"dfe36450.939878","type":"component display mode","z":"106ddccd.912d53","name":"ShortLabel","x":1725.7026977539062,"y":170.5714111328125,"wires":[["ae8199de.7c0c48"]]},{"id":"76ded9f2.6b67d8","type":"component display mode","z":"106ddccd.912d53","name":"LongLabel","x":1722.2026977539062,"y":377.5714111328125,"wires":[["2e2940f4.caec8"]]},{"id":"2ec2d71e.5d9698","type":"component display mode","z":"106ddccd.912d53","name":"LongLabel","x":1720.2026977539062,"y":537.5714111328125,"wires":[["2e2940f4.caec8"]]},{"id":"20058910.b69746","type":"component display mode","z":"106ddccd.912d53","name":"Gauge","x":1729.2026977539062,"y":659.5714111328125,"wires":[["ac0b2a57.c56998"]]},{"id":"760662fb.66eacc","type":"component display mode","z":"106ddccd.912d53","name":"Message","x":1717.2026977539062,"y":710.5714111328125,"wires":[["ac0b2a57.c56998"]]},{"id":"8607bba6.28cad8","type":"component display mode","z":"106ddccd.912d53","name":"LongLabel","x":1716.2026977539062,"y":760.5714111328125,"wires":[["ac0b2a57.c56998"]]},{"id":"ec7d103e.f8d8f","type":"component display mode","z":"106ddccd.912d53","name":"Gauge","x":1725.2026977539062,"y":1018.5714111328125,"wires":[["84b64282.f8dd3"]]},{"id":"5d5a0809.06be48","type":"component display mode","z":"106ddccd.912d53","name":"LongLabel","x":1713.2026977539062,"y":1117.5714111328125,"wires":[["84b64282.f8dd3"]]},{"id":"fb504ce0.afacf","type":"component display mode","z":"106ddccd.912d53","name":"Table","x":1692.2026977539062,"y":1362.5714111328125,"wires":[["3ee4aa7c.432a66"]]},{"id":"ae8199de.7c0c48","type":"component display group","z":"106ddccd.912d53","name":"","layout":"Horizontal","index":"1","x":2035.7026977539062,"y":170.5714111328125,"wires":[["d2c7ac61.8fbb9"]]},{"id":"2e2940f4.caec8","type":"component display group","z":"106ddccd.912d53","name":"General","layout":"Vertical","index":"1","x":1998.488338470459,"y":375.99997329711914,"wires":[["3abddea6.eecce2"]]},{"id":"ac0b2a57.c56998","type":"component display group","z":"106ddccd.912d53","name":"Life Remaining","layout":"Vertical","index":"2","x":2013.488338470459,"y":657.9999732971191,"wires":[["3abddea6.eecce2"]]},{"id":"3ee4aa7c.432a66","type":"component display group","z":"106ddccd.912d53","name":"Events","layout":"Vertical","index":"1","x":1951.2026977539062,"y":1362.5714111328125,"wires":[["212037cc.52fa78"]]},{"id":"3abddea6.eecce2","type":"component display region","z":"106ddccd.912d53","name":"Region 2","layout":"Horizontal","index":"2","x":2268.2026977539062,"y":375.5714111328125,"wires":[["8339d69.1331528"]]},{"id":"212037cc.52fa78","type":"component display region","z":"106ddccd.912d53","name":"Region 3","layout":"Vertical","index":"3","x":2234.2026977539062,"y":1360.5714111328125,"wires":[["8339d69.1331528"]]},{"id":"f65dcf61.135df","type":"function","z":"106ddccd.912d53","name":"Format Capacity","func":"msg.payload.value = msg.payload.capacity + \" GB\";\n\nreturn msg;","outputs":1,"noerr":0,"x":1276.841152191162,"y":455,"wires":[["76ded9f2.6b67d8"]]},{"id":"cdb49e2b.1c531","type":"Storfly ICS vtView","z":"106ddccd.912d53","storFlyIp":"10.10.4.108","username":"admin","password":"SN3000001","x":430.5,"y":694,"wires":[["85107afc.302988"]]},{"id":"3fcb4987.516946","type":"Storfly ICS vtView","z":"106ddccd.912d53","storFlyIp":"10.10.4.72","username":"","password":"","x":430,"y":810,"wires":[["85107afc.302988"]]},{"id":"54212e75.e6902","type":"ui_group","z":"","name":"StorFly IoT","tab":"16ff865d.13b12a","disp":true,"width":"32"},{"id":"16ff865d.13b12a","type":"ui_tab","z":"","name":"home","icon":"dashboard"}]

Flow Info

Created 7 years, 7 months ago
Rating: not yet rated

Owner

Actions

Rate:

Node Types

Core
  • comment (x6)
  • function (x15)
  • inject (x1)
  • join (x1)
Other

Tags

Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option