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"}]