dataflow-theme-dark

This is the dark theme for data flow

[{"id":"588c4bf9.9e58a4","type":"comment","z":"106ddccd.912d53","name":"Data Filter & Calculation Components","info":"","x":1086.3767395019531,"y":121.91674995422363,"wires":[]},{"id":"ac66f1f9.23896","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                      {{displayGuagueChart(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 }}\">{{ item.serverity }}</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.displayGuagueChart = 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: #333333!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: 2px solid #074f5b;\n  }\n\n  .layout-row .description {\n    color: gray;\n  }\n\n  .layout-row .gray-box {\n    background-color: #404040;\n    margin: 1px;\n  }\n\n  .layout-row .gray-box-header {\n    min-height: 20px;\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: #fff;\n  }\n\n  .c3-tooltip-name-Data {\n    color: #404040;\n  }\n\n  .c3-chart-arcs .c3-chart-arcs-background {\n    fill: rgb(81, 81, 81);\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    color: #fff !important;\n  }\n\n  .md-title {\n    font-size: 16px;\n  }\n\n  .md-3-line {\n    padding: 0 !important;\n  }\n\n  .label-Critical {\n    color: #ed5565;\n  }\n\n  .label-Warning {\n    color: #F49154;\n  }\n\n  .label-Informative {\n    color: #2AB050;\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  .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  }\n\n  .device-menu .actived:before {\n    font-family: FontAwesome;\n    content: \"\\f058\";\n    font-size: 18px;\n  }\n\n  .device-menu-body,\n  .list-view-body {\n    overflow-x: hidden;\n  }\n\n  .device-menu-body::-webkit-scrollbar-track,\n  .list-view-body::-webkit-scrollbar-track {\n    border-radius: 10px !important;\n    background-color: #404040 !important;\n  }\n\n  .device-menu-body::-webkit-scrollbar,\n  .list-view-body::-webkit-scrollbar {\n    width: 6px !important;\n    background-color: #404040 !important;\n  }\n\n  .device-menu-body::-webkit-scrollbar-thumb,\n  .list-view-body::-webkit-scrollbar-thumb {\n    background-color: #303030 !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":3123,"y":740.016040802002,"wires":[[]]},{"id":"53aa740f.a461ec","type":"function","z":"106ddccd.912d53","name":"","func":"\nreturn msg;","outputs":1,"noerr":0,"x":754.6940574645996,"y":755.3853740692139,"wires":[["93ef3f88.22622","e9fc514d.02838","ab07a32b.292f4","276aeeba.b83b42","2766a2e1.c66ace","40ff7b81.f04994","396b6aba.2cc956","51b8027.1aa5afc","80061e8.26b72e","d0a6a452.36c988","cede03ed.92335","c19e387.628e2c8","affa2c84.d41a9","efa0f66b.7a5d28","4fa41ca3.68fac4","60130182.7f4b2","18c1579.aef60a8","de6f3afd.c6cd28"]]},{"id":"ad30d428.d17908","type":"component display group","z":"106ddccd.912d53","name":"Storage Usage","layout":"Vertical","index":"3","x":2072.3412437438965,"y":1028.9999732971191,"wires":[["4a6953ed.9d448c"]]},{"id":"8d849e95.d58cc","type":"component display region","z":"106ddccd.912d53","name":"Region 1","layout":"Vertical","index":"1","x":2335.5556030273438,"y":181.5714111328125,"wires":[["89b5b679.375898"]]},{"id":"c5ae7c74.45c6a","type":"comment","z":"106ddccd.912d53","name":"Choose Display Mode","info":"","x":1789.1862564086914,"y":116.5833740234375,"wires":[]},{"id":"c44c71eb.18ac","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({\n        \"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\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  newPayload.errorDevice = [];\n  newPayload.device = [];\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":2839.4587326049805,"y":740.2396335601807,"wires":[["ac66f1f9.23896"]]},{"id":"e9fc514d.02838","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":996.0556030273438,"y":384.7618579864502,"wires":[["44a14ca2.a9f534"]]},{"id":"310f839.3a3d47c","type":"comment","z":"106ddccd.912d53","name":"Choose Region For Display","info":"","x":2394.8532638549805,"y":111,"wires":[]},{"id":"bc5f6cab.a61be","type":"comment","z":"106ddccd.912d53","name":"Virtium StorFly Devices","info":"","x":470.9362487792969,"y":119.75006103515625,"wires":[]},{"id":"abc5fc1c.8a1e4","type":"comment","z":"106ddccd.912d53","name":"Choose Group For Display","info":"","x":2090.3532638549805,"y":112.24999904632568,"wires":[]},{"id":"2f9d411.28542be","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":1358.8491744995117,"y":822.9643898010254,"wires":[["ff62571b.ab2618"]]},{"id":"93ef3f88.22622","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":1052.341381072998,"y":553.3333172798157,"wires":[["d58f66d2.8bfb98"]]},{"id":"cb375d77.5d0d9","type":"inject","z":"106ddccd.912d53","name":"","topic":"","payload":"","payloadType":"date","repeat":"10","crontab":"","once":true,"x":166.8529052734375,"y":754.7224292755127,"wires":[["16883de6.6f1682","dcf6c361.859"]]},{"id":"89b5b679.375898","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":2574.1982955932617,"y":738.8889389038086,"wires":[["c44c71eb.18ac"]]},{"id":"ddbabdbf.88c3","type":"comment","z":"106ddccd.912d53","name":"Data Format","info":"","x":1337.5195007324219,"y":121.66663646697998,"wires":[]},{"id":"d6ecc0f3.2bf42","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":1356.5197448730469,"y":776.6666164398193,"wires":[["ff62571b.ab2618"]]},{"id":"cb060051.ae865","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":1369.51953125,"y":870.6666431427002,"wires":[["ff62571b.ab2618"]]},{"id":"87497d9c.463fb","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":1358.852783203125,"y":918.0000019073486,"wires":[["ff62571b.ab2618"]]},{"id":"d05f89cb.f826a8","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":1369.3887557983398,"y":1032.9047107696533,"wires":[["bfc486f7.ecb068"]]},{"id":"5eccc87.2c3b738","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":1367.72216796875,"y":1131.2380065917969,"wires":[["8f08996d.9eb668"]]},{"id":"f93f237b.35966","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":1367.7222518920898,"y":1176.0952682495117,"wires":[["8f08996d.9eb668"]]},{"id":"2377959b.483fba","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":1366.7932510375977,"y":1224.0118961334229,"wires":[["8f08996d.9eb668"]]},{"id":"e4130285.c0c9c","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":1387.72216796875,"y":723.2380294799805,"wires":[["c2108f44.c1d7a"]]},{"id":"1ac22cd1.79fef3","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":1337.72216796875,"y":1377.2381029129028,"wires":[["c3d405e5.160d58"]]},{"id":"ab07a32b.292f4","type":"component display","z":"106ddccd.912d53","name":"IP Address","attributeName":"ipAddress","index":"1","x":1009.5790634155273,"y":179.9620532989502,"wires":[["977bc66d.7d4738"]]},{"id":"276aeeba.b83b42","type":"component display","z":"106ddccd.912d53","name":"Serial","attributeName":"serial","index":"2","x":988.0792236328125,"y":224.6105136871338,"wires":[["977bc66d.7d4738"]]},{"id":"40ff7b81.f04994","type":"component display","z":"106ddccd.912d53","name":"Software","attributeName":"software","index":"2","x":996.0556030273438,"y":427.5714111328125,"wires":[["44a14ca2.a9f534"]]},{"id":"2766a2e1.c66ace","type":"component display","z":"106ddccd.912d53","name":"Capacity","attributeName":"capacity","index":"3","x":996.0556030273438,"y":470.5714111328125,"wires":[["cc83f10a.3981d"]]},{"id":"396b6aba.2cc956","type":"component display","z":"106ddccd.912d53","name":"Life Remaining","attributeName":"remainingLife","index":"1","x":1011.0556030273438,"y":676.5714111328125,"wires":[["36143749.fbe0a8"]]},{"id":"51b8027.1aa5afc","type":"component display","z":"106ddccd.912d53","name":"Error Message","attributeName":"errorMessage","index":"2","x":1008.0556030273438,"y":723.5714111328125,"wires":[["e4130285.c0c9c"]]},{"id":"18c1579.aef60a8","type":"component display","z":"106ddccd.912d53","name":"Year In Service","attributeName":"yearInService","index":"3","x":1010.0556030273438,"y":778.5714111328125,"wires":[["d6ecc0f3.2bf42"]]},{"id":"80061e8.26b72e","type":"component display","z":"106ddccd.912d53","name":"Data Written","attributeName":"dataWritten","index":"4","x":1000.0556030273438,"y":823.5714111328125,"wires":[["2f9d411.28542be"]]},{"id":"d0a6a452.36c988","type":"component display","z":"106ddccd.912d53","name":"Remaining TBW","attributeName":"remainingTBW","index":"5","x":1009.0556030273438,"y":871.5714111328125,"wires":[["cb060051.ae865"]]},{"id":"cede03ed.92335","type":"component display","z":"106ddccd.912d53","name":"Estimate Remainning Time","attributeName":"estimateEOL","index":"6","x":1048.0556030273438,"y":917.5714111328125,"wires":[["87497d9c.463fb"]]},{"id":"4fa41ca3.68fac4","type":"component display","z":"106ddccd.912d53","name":"Used","attributeName":"freeDiskSpace","index":"2","x":979.0556030273438,"y":1131.5714111328125,"wires":[["5eccc87.2c3b738"]]},{"id":"efa0f66b.7a5d28","type":"component display","z":"106ddccd.912d53","name":"Free","attributeName":"freeDiskSpace","index":"3","x":979.0556030273438,"y":1176.5714111328125,"wires":[["f93f237b.35966"]]},{"id":"60130182.7f4b2","type":"component display","z":"106ddccd.912d53","name":"Free Disk Space","attributeName":"freeDiskSpace","index":"1","x":1008.0556030273438,"y":1034.5714111328125,"wires":[["d05f89cb.f826a8"]]},{"id":"affa2c84.d41a9","type":"component display","z":"106ddccd.912d53","name":"Total","attributeName":"capacity","index":"4","x":980.0556030273438,"y":1225.5714111328125,"wires":[["2377959b.483fba"]]},{"id":"c19e387.628e2c8","type":"component display","z":"106ddccd.912d53","name":"Events","attributeName":"events","index":"1","x":976.0556030273438,"y":1376.5714111328125,"wires":[["1ac22cd1.79fef3"]]},{"id":"de6f3afd.c6cd28","type":"component display","z":"106ddccd.912d53","name":"Model","attributeName":"model","index":"3","x":987.0556030273438,"y":270.5714111328125,"wires":[["977bc66d.7d4738"]]},{"id":"977bc66d.7d4738","type":"component display mode","z":"106ddccd.912d53","name":"ShortLabel","x":1789.5556030273438,"y":182.5714111328125,"wires":[["377737f5.822ea8"]]},{"id":"44a14ca2.a9f534","type":"component display mode","z":"106ddccd.912d53","name":"LongLabel","x":1786.0556030273438,"y":389.5714111328125,"wires":[["c4cae4a0.5486c8"]]},{"id":"d58f66d2.8bfb98","type":"component display mode","z":"106ddccd.912d53","name":"LongLabel","x":1784.0556030273438,"y":549.5714111328125,"wires":[["c4cae4a0.5486c8"]]},{"id":"36143749.fbe0a8","type":"component display mode","z":"106ddccd.912d53","name":"Gauge","x":1793.0556030273438,"y":671.5714111328125,"wires":[["e8a98967.d707e8"]]},{"id":"c2108f44.c1d7a","type":"component display mode","z":"106ddccd.912d53","name":"Message","x":1781.0556030273438,"y":722.5714111328125,"wires":[["e8a98967.d707e8"]]},{"id":"ff62571b.ab2618","type":"component display mode","z":"106ddccd.912d53","name":"LongLabel","x":1780.0556030273438,"y":772.5714111328125,"wires":[["e8a98967.d707e8"]]},{"id":"bfc486f7.ecb068","type":"component display mode","z":"106ddccd.912d53","name":"Gauge","x":1789.0556030273438,"y":1030.5714111328125,"wires":[["ad30d428.d17908"]]},{"id":"8f08996d.9eb668","type":"component display mode","z":"106ddccd.912d53","name":"LongLabel","x":1777.0556030273438,"y":1129.5714111328125,"wires":[["ad30d428.d17908"]]},{"id":"c3d405e5.160d58","type":"component display mode","z":"106ddccd.912d53","name":"Table","x":1756.0556030273438,"y":1374.5714111328125,"wires":[["22335806.caa538"]]},{"id":"377737f5.822ea8","type":"component display group","z":"106ddccd.912d53","name":"","layout":"Horizontal","index":"1","x":2099.5556030273438,"y":182.5714111328125,"wires":[["8d849e95.d58cc"]]},{"id":"c4cae4a0.5486c8","type":"component display group","z":"106ddccd.912d53","name":"General","layout":"Vertical","index":"1","x":2062.3412437438965,"y":387.99997329711914,"wires":[["4a6953ed.9d448c"]]},{"id":"e8a98967.d707e8","type":"component display group","z":"106ddccd.912d53","name":"Life Remaining","layout":"Vertical","index":"2","x":2077.3412437438965,"y":669.9999732971191,"wires":[["4a6953ed.9d448c"]]},{"id":"22335806.caa538","type":"component display group","z":"106ddccd.912d53","name":"Events","layout":"Vertical","index":"1","x":2015.0556030273438,"y":1374.5714111328125,"wires":[["a0ebe4f8.463a08"]]},{"id":"4a6953ed.9d448c","type":"component display region","z":"106ddccd.912d53","name":"Region 2","layout":"Horizontal","index":"2","x":2332.0556030273438,"y":387.5714111328125,"wires":[["89b5b679.375898"]]},{"id":"a0ebe4f8.463a08","type":"component display region","z":"106ddccd.912d53","name":"Region 3","layout":"Vertical","index":"3","x":2298.0556030273438,"y":1372.5714111328125,"wires":[["89b5b679.375898"]]},{"id":"cc83f10a.3981d","type":"function","z":"106ddccd.912d53","name":"Format Capacity","func":"msg.payload.value = msg.payload.capacity + \" GB\";\n\nreturn msg;","outputs":1,"noerr":0,"x":1340.6940574645996,"y":467,"wires":[["44a14ca2.a9f534"]]},{"id":"16883de6.6f1682","type":"Storfly ICS vtView","z":"106ddccd.912d53","storFlyIp":"10.10.4.108","username":"admin","password":"SN3000001","x":494.3529052734375,"y":706,"wires":[["53aa740f.a461ec"]]},{"id":"dcf6c361.859","type":"Storfly ICS vtView","z":"106ddccd.912d53","storFlyIp":"10.10.4.72","username":"","password":"","x":493.8529052734375,"y":822,"wires":[["53aa740f.a461ec"]]},{"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