@*******************************************************************************************************// GraphMeasurements.cshtml - Gbtc//// Copyright © 2016, Grid Protection Alliance. All Rights Reserved.//// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See// the NOTICE file distributed with this work for additional information regarding copyright ownership.// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this// file except in compliance with the License. You may obtain a copy of the License at://// http://opensource.org/licenses/MIT//// Unless agreed to in writing, the subject software distributed under the License is distributed on an// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the// License for the specific language governing permissions and limitations.//// Code Modification History:// ----------------------------------------------------------------------------------------------------// 01/15/2016 - J. Ritchie Carroll// Generated original version of source code.////******************************************************************************************************// To use in an ASP.NET project, include a GraphMeasurements.cshtml view with following content://// @using GSF.Web// @section StyleSheets{@Html.Raw(ViewBag.StyleSheetsSection?.ToString())}// @Html.RenderResource("GSF.Web.Shared.Views.GraphMeasurements.cshtml")// @section Scripts{@Html.Raw(ViewBag.ScriptsSection?.ToString())}////******************************************************************************************************// To use in a self-hosted web project, include a GraphMeasurements.cshtml view with following content://// @using GSF.Web.Model// @using <MyAppNameSpace>.Model// @inherits ExtendedTemplateBase<AppModel>// @section StyleSheets{@Html.Raw(ViewBag.StyleSheetsSection.ToString())}// @{Layout = "Layout.cshtml";}// @Html.RenderResource("GSF.Web.Shared.Views.GraphMeasurements.cshtml")// @section Scripts{@Html.Raw(ViewBag.ScriptsSection.ToString())}//*****************************************************************************************************@@* ReSharper disable RedundantUsingDirective *@@* ReSharper disable Html.PathError *@@* ReSharper disable InlineOutVariableDeclaration *@@* ReSharper disable UnknownCssClass *@@* ReSharper disable CoercedEqualsUsing *@@using System.Collections.Generic@using System.Net.Http@using GSF@using GSF.Web@using GSF.Web.Model@using GSF.Web.Shared@inherits ExtendedTemplateBase@{ //Layout = "Layout.cshtml"; ViewBag.Title = "Graph Measurements"; ViewBag.SetFullWidth = true; HttpRequestMessage request = ViewBag.Request; Dictionary<string, string> parameters = request.QueryParameters(); string showMenu; parameters.TryGetValue("ShowMenu", out showMenu); if (string.IsNullOrEmpty(showMenu)) { showMenu = "true"; } ViewBag.ShowMenu = showMenu.ParseBoolean();}@section StyleSheets { <link href="@Resources.Root/Shared/Content/jquery-ui.css" rel="stylesheet"> <style> html { overflow-x: hidden; height: 100%; } body { overflow-x: hidden; overflow-y: hidden; height: 100%; } .list-group { padding-left: 0; margin-left: 0; } .list-group-item { padding-left: 0; margin-left: 0; } .panel-body { padding: 0; } .panel-heading { padding: 0; } ul.nowrap { overflow: auto; } li.nowrap { white-space: nowrap; } #messageBlock { width: 300px; margin: 0 auto; text-align: center } .ui-resizable-e { right: 2px; border-left-width: 8px; border-left-style: double; border-left-color: #C8C8C8; } .modal-dialog { width: 850px; margin: 2rem auto; } /* Collapsible button style */ .btn-collapsible.btn { color: rgb(255, 255, 255); padding-bottom: 0; margin-left: -8px; font-family: "Glyphicons Halflings"; font-size: 8pt; height: 22px; } /* Collapsible button icon when content is shown - arrow down */ .btn-collapsible.btn:after { content: "\e114"; } /* Collapsible button icon when content is hidden - arrow right */ .btn-collapsible.btn.collapsed:after { content: "\e080"; } .glyphicon-spin { -webkit-animation: spin 1.25s infinite linear; -moz-animation: spin 1.25s infinite linear; -o-animation: spin 1.25s infinite linear; animation: spin 1.25s infinite linear; } @@-moz-keyframes spin { 0% { -moz-transform: rotate(0); } 100% { -moz-transform: rotate(359deg); } } @@-webkit-keyframes spin { 0% { -webkit-transform: rotate(0); } 100% { -webkit-transform: rotate(359deg); } } @@-o-keyframes spin { 0% { -o-transform: rotate(0); } 100% { -o-transform: rotate(359deg); } } @@keyframes spin { 0% { -webkit-transform: rotate(0); transform: rotate(0); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } </style>}@section Scripts { <script src="@Resources.Root/Shared/Scripts/jquery-ui.js"></script> <script src="@Resources.Root/Shared/Scripts/jquery.subscriber.js"></script> <script src="@Resources.Root/Shared/Scripts/flot/jquery.flot.min.js"></script> <script src="@Resources.Root/Shared/Scripts/flot/jquery.flot.crosshair.min.js"></script> <script src="@Resources.Root/Shared/Scripts/flot/jquery.flot.navigate.min.js"></script> <script src="@Resources.Root/Shared/Scripts/flot/jquery.flot.resize.min.js"></script> <script src="@Resources.Root/Shared/Scripts/flot/jquery.flot.selection.min.js"></script> <script src="@Resources.Root/Shared/Scripts/flot/jquery.flot.time.min.js"></script> <script src="@Resources.Root/Shared/Scripts/flot/jquery.flot.axislabels.min.js"></script> <script src="@Resources.Root/Shared/Scripts/angular.js"></script> <script src="@Resources.Root/Shared/Scripts/jstorage.js"></script> <script> // Need to define a global function to reset the filter so // that it can be called by the click handler for a global button var resetFilter; (function ($) { var plot; var plotData = []; const graphMeasurements = angular.module('GraphMeasurements', []); var root = "@Resources.Root"; // Logs messages to an array so they can be // observed on demand in the JavaScript console function log(obj) { const max = 500; if (window.log === undefined) window.log = []; window.log.push(obj); if (window.log.length > max) window.log.splice(0, window.log.length - max); } // Removes invalid characters from an identifier // so it can be used as an ID in HTML function cleanIdentifier(identifier) { if (identifier == null) return ""; return identifier.replace(/[!"#$%&'()*+,.\/:;<=>?@@[\\\]^`{|}~]/g, "-"); } // Builds the plot upon which the chart will be displayed function buildPlot() { plot = $.plot("#placeholder", plotData, { series: { shadowSize: 0 }, yaxes: [ { show: true, position: "left", axisLabel: "Frequency" }, { show: true, position: "left", axisLabel: "Voltage" }, { show: true, position: "right", axisLabel: "Current" }, { show: true, position: "right", axisLabel: "Angle" }, { show: true, position: "right", axisLabel: "Analog Data" } ], xaxis: { mode: "time", timeformat: "%H:%M:%S", timezone: "browser" }, legend: { show: true, container: $('#legend'), noColumns: 1, margin: 5 } }); } // Builds the stats modal for the device with the given acronym function buildModal(acronym) { $("#modals").append('<div id="' + acronym + '"></div>'); let html = '<div id="mod' + acronym + '" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="confirm-modal" aria-hidden="true">'; html += '<div class="modal-dialog">'; html += '<div class="modal-content">'; html += '<div class="modal-header">'; html += '<a class="close" data-dismiss="modal">×</a>'; html += '<h4>' + acronym + ' Statistics</h4>'; html += '</div>'; html += '<div class="modal-body" style="height: 300px; overflow-y: scroll" >'; html += '<table id="stat' + acronym + '" class="table" style="font-size: x-small">'; html += '<thead>Run-time Statistics:<thead />'; html += '<tr><th style="text-align: center; white-space: nowrap">Stat ID</th><th>Statistic</th><th style="text-align: center">Value</th><th>TimeTag</th></tr>'; html += '</table>'; html += '</div>'; html += '<div class="modal-footer">'; html += '<span class="btn" data-dismiss="modal">Close</span>'; // close button html += '</div>'; // footer html += '</div>'; // modalWindow $('#' + acronym).html(html); $("#mod" + acronym).modal({ 'show': false }); } // Explicitly sets heights of various components on // the page to take up available screen space function resizeElements() { const sidebar = $("#sidebar"); const sidebarExpanded = sidebar.hasClass("in"); const sidebarHeight = $(window).innerHeight() - sidebar.offset().top - 20; const chartHeight = sidebarExpanded ? sidebarHeight - 40 : $(window).innerHeight() - 180; if (!sidebar.hasClass("init-width")) { sidebar.addClass("init-width"); sidebar.width($(window).innerWidth() * 0.20); } sidebar.height(sidebarHeight); $("#placeholder").height(chartHeight); $("#graphContainer").css({ "top": (sidebarExpanded ? -sidebar.height() : 0) + "px" }); $("#graphContainer").css({ "left": (sidebarExpanded ? sidebar.width() + 30 : 0) + "px" }); $("#graphContainer").width($(window).innerWidth() - (sidebarExpanded ? sidebar.width() + 80 : $("#collapseSidebarBtn").outerWidth() + 20)); $(".ui-resizable-e").height(sidebar.prop("scrollHeight")); } // Loads settings from local storage and prepares // callback functions for the initial page load function loadSettings($scope) { $scope.dataPoints = 300; $scope.selections = []; if ($.jStorage.get("dataPoints") !== null) $scope.dataPoints = $.jStorage.get("dataPoints"); if ($.jStorage.get("selections") !== null) $scope.selections = $.jStorage.get("selections"); $scope.deviceCallbacks = {}; $scope.measurementCallbacks = {}; $.each($scope.selections, function (_, selection) { $scope.deviceCallbacks[selection.device] = function (deviceMetadata) { const identifier = cleanIdentifier(deviceMetadata.acronym); $("#dd" + identifier).removeClass("collapse"); deviceMetadata.updateMeasurements(); } $scope.measurementCallbacks[selection.signalid] = function (measurementMetadata) { const cleanID = cleanIdentifier(measurementMetadata.id); $("#cb" + cleanID).prop("checked", true); updateFilter($scope, true, measurementMetadata); } }); $('#datapoints').val($scope.dataPoints); } function measurementChecked($scope, signalID) { if (signalID) { const selections = $.jStorage.get("selections"); if (selections) { for (let i = 0; i < selections.length; i++) { if (selections[i].signalid == signalID) return true; } } } return false; } // Updates the subscription filter for the data subscriber function updateFilter($scope, checked, measurementMetadata) { if (!hubIsConnected) return; if (checked) { $scope.selectedMeasurements[measurementMetadata.signalid] = { selection: { device: measurementMetadata.deviceacronym, signalid: measurementMetadata.signalid }, plotData: { label: measurementMetadata.pointtag + " - " + measurementMetadata.signalacronym, yaxis: measurementMetadata.YAxis, data: [] } }; } else { delete $scope.selectedMeasurements[measurementMetadata.signalid]; } const selectedIDs = Object.keys($scope.selectedMeasurements); if (selectedIDs.length > 0) { const filterStr = selectedIDs.join(";"); $scope.dataSubscriber.subscribe({ FilterExpression: filterStr }); } else { $scope.dataSubscriber.unsubscribe(); } plotData = Object.keys($scope.selectedMeasurements) .map(function (key) { return $scope.selectedMeasurements[key].plotData; }); plot.setData(plotData); plot.setupGrid(); plot.draw(); $scope.selections = Object.keys($scope.selectedMeasurements) .map(function (key) { return $scope.selectedMeasurements[key].selection; }); $.jStorage.set("selections", $scope.selections); } // Queries metadata for the measurements associated with the given device function getMeasurements($scope, deviceMetadata) { if (!hubIsConnected) return; $scope.dataSubscriber.getMetadata("MeasurementDetail", "DeviceAcronym = '" + deviceMetadata.acronym + "' AND SignalAcronym <> 'STAT'") .fail(e => showErrorMessage(e, null, true)) .done(function (data) { data.sort(function (m1, m2) { function signalIndex (measurementMetadata) { const matches = measurementMetadata.signalreference.match(/[0-9]+$/); if (matches === null) return 0; return Number(matches[0]); } function pointID (measurementMetadata) { return Number(measurementMetadata.id.split(':')[1]); } const comparisons = []; comparisons.push({ m1: m1.signalacronym, m2: m2.signalacronym }); comparisons.push({ m1: signalIndex(m1), m2: signalIndex(m2) }); comparisons.push({ m1: pointID(m1), m2: pointID(m2) }); for (let i = 0; i < comparisons.length; i++) { const comparison = comparisons[i]; if (comparison.m1 < comparison.m2) return -1; if (comparison.m1 > comparison.m2) return 1; } return 0; }); $.each(data, function (_, measurementMetadata) { if (measurementMetadata.signalacronym === "FREQ") measurementMetadata.YAxis = 1; else if (measurementMetadata.signalacronym === "VPHM") measurementMetadata.YAxis = 2; else if (measurementMetadata.signalacronym === "IPHM") measurementMetadata.YAxis = 3; else if (measurementMetadata.signalacronym.endsWith("PHA")) measurementMetadata.YAxis = 4; else measurementMetadata.YAxis = 5; }); deviceMetadata.measurements = data; $scope.$apply(); // Handle the callbacks for automatic measurement auto-selection $.each(data, function (_, measurementMetadata) { const callback = $scope.measurementCallbacks[measurementMetadata.signalid]; if (callback !== undefined) callback(measurementMetadata); }); }); } // Queries metadata for all statistics and // the modals to display stat information function getStatistics($scope) { $scope.dataSubscriber.getMetadata("MeasurementDetail", "SignalAcronym = 'STAT'") .fail(e => showErrorMessage(e, null, true))Lines 1–500 of 941. Load more
@******************************************************************************************************* // GraphMeasurements.cshtml - Gbtc // // Copyright © 2016, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this // file except in compliance with the License. You may obtain a copy of the License at: // // http://opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 01/15/2016 - J. Ritchie Carroll // Generated original version of source code. // //****************************************************************************************************** // To use in an ASP.NET project, include a GraphMeasurements.cshtml view with following content: // // @using GSF.Web // @section StyleSheets{@Html.Raw(ViewBag.StyleSheetsSection?.ToString())} // @Html.RenderResource("GSF.Web.Shared.Views.GraphMeasurements.cshtml") // @section Scripts{@Html.Raw(ViewBag.ScriptsSection?.ToString())} // //****************************************************************************************************** // To use in a self-hosted web project, include a GraphMeasurements.cshtml view with following content: // // @using GSF.Web.Model // @using <MyAppNameSpace>.Model // @inherits ExtendedTemplateBase<AppModel> // @section StyleSheets{@Html.Raw(ViewBag.StyleSheetsSection.ToString())} // @{Layout = "Layout.cshtml";} // @Html.RenderResource("GSF.Web.Shared.Views.GraphMeasurements.cshtml") // @section Scripts{@Html.Raw(ViewBag.ScriptsSection.ToString())} //*****************************************************************************************************@ @* ReSharper disable RedundantUsingDirective *@ @* ReSharper disable Html.PathError *@ @* ReSharper disable InlineOutVariableDeclaration *@ @* ReSharper disable UnknownCssClass *@ @* ReSharper disable CoercedEqualsUsing *@ @using System.Collections.Generic @using System.Net.Http @using GSF @using GSF.Web @using GSF.Web.Model @using GSF.Web.Shared @inherits ExtendedTemplateBase @{ //Layout = "Layout.cshtml"; ViewBag.Title = "Graph Measurements"; ViewBag.SetFullWidth = true; HttpRequestMessage request = ViewBag.Request; Dictionary<string, string> parameters = request.QueryParameters(); string showMenu; parameters.TryGetValue("ShowMenu", out showMenu); if (string.IsNullOrEmpty(showMenu)) { showMenu = "true"; } ViewBag.ShowMenu = showMenu.ParseBoolean(); } @section StyleSheets { <link href="@Resources.Root/Shared/Content/jquery-ui.css" rel="stylesheet"> <style> html { overflow-x: hidden; height: 100%; } body { overflow-x: hidden; overflow-y: hidden; height: 100%; } .list-group { padding-left: 0; margin-left: 0; } .list-group-item { padding-left: 0; margin-left: 0; } .panel-body { padding: 0; } .panel-heading { padding: 0; } ul.nowrap { overflow: auto; } li.nowrap { white-space: nowrap; } #messageBlock { width: 300px; margin: 0 auto; text-align: center } .ui-resizable-e { right: 2px; border-left-width: 8px; border-left-style: double; border-left-color: #C8C8C8; } .modal-dialog { width: 850px; margin: 2rem auto; } /* Collapsible button style */ .btn-collapsible.btn { color: rgb(255, 255, 255); padding-bottom: 0; margin-left: -8px; font-family: "Glyphicons Halflings"; font-size: 8pt; height: 22px; } /* Collapsible button icon when content is shown - arrow down */ .btn-collapsible.btn:after { content: "\e114"; } /* Collapsible button icon when content is hidden - arrow right */ .btn-collapsible.btn.collapsed:after { content: "\e080"; } .glyphicon-spin { -webkit-animation: spin 1.25s infinite linear; -moz-animation: spin 1.25s infinite linear; -o-animation: spin 1.25s infinite linear; animation: spin 1.25s infinite linear; } @@-moz-keyframes spin { 0% { -moz-transform: rotate(0); } 100% { -moz-transform: rotate(359deg); } } @@-webkit-keyframes spin { 0% { -webkit-transform: rotate(0); } 100% { -webkit-transform: rotate(359deg); } } @@-o-keyframes spin { 0% { -o-transform: rotate(0); } 100% { -o-transform: rotate(359deg); } } @@keyframes spin { 0% { -webkit-transform: rotate(0); transform: rotate(0); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } </style> } @section Scripts { <script src="@Resources.Root/Shared/Scripts/jquery-ui.js"></script> <script src="@Resources.Root/Shared/Scripts/jquery.subscriber.js"></script> <script src="@Resources.Root/Shared/Scripts/flot/jquery.flot.min.js"></script> <script src="@Resources.Root/Shared/Scripts/flot/jquery.flot.crosshair.min.js"></script> <script src="@Resources.Root/Shared/Scripts/flot/jquery.flot.navigate.min.js"></script> <script src="@Resources.Root/Shared/Scripts/flot/jquery.flot.resize.min.js"></script> <script src="@Resources.Root/Shared/Scripts/flot/jquery.flot.selection.min.js"></script> <script src="@Resources.Root/Shared/Scripts/flot/jquery.flot.time.min.js"></script> <script src="@Resources.Root/Shared/Scripts/flot/jquery.flot.axislabels.min.js"></script> <script src="@Resources.Root/Shared/Scripts/angular.js"></script> <script src="@Resources.Root/Shared/Scripts/jstorage.js"></script> <script> // Need to define a global function to reset the filter so // that it can be called by the click handler for a global button var resetFilter; (function ($) { var plot; var plotData = []; const graphMeasurements = angular.module('GraphMeasurements', []); var root = "@Resources.Root"; // Logs messages to an array so they can be // observed on demand in the JavaScript console function log(obj) { const max = 500; if (window.log === undefined) window.log = []; window.log.push(obj); if (window.log.length > max) window.log.splice(0, window.log.length - max); } // Removes invalid characters from an identifier // so it can be used as an ID in HTML function cleanIdentifier(identifier) { if (identifier == null) return ""; return identifier.replace(/[!"#$%&'()*+,.\/:;<=>?@@[\\\]^`{|}~]/g, "-"); } // Builds the plot upon which the chart will be displayed function buildPlot() { plot = $.plot("#placeholder", plotData, { series: { shadowSize: 0 }, yaxes: [ { show: true, position: "left", axisLabel: "Frequency" }, { show: true, position: "left", axisLabel: "Voltage" }, { show: true, position: "right", axisLabel: "Current" }, { show: true, position: "right", axisLabel: "Angle" }, { show: true, position: "right", axisLabel: "Analog Data" } ], xaxis: { mode: "time", timeformat: "%H:%M:%S", timezone: "browser" }, legend: { show: true, container: $('#legend'), noColumns: 1, margin: 5 } }); } // Builds the stats modal for the device with the given acronym function buildModal(acronym) { $("#modals").append('<div id="' + acronym + '"></div>'); let html = '<div id="mod' + acronym + '" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="confirm-modal" aria-hidden="true">'; html += '<div class="modal-dialog">'; html += '<div class="modal-content">'; html += '<div class="modal-header">'; html += '<a class="close" data-dismiss="modal">×</a>'; html += '<h4>' + acronym + ' Statistics</h4>'; html += '</div>'; html += '<div class="modal-body" style="height: 300px; overflow-y: scroll" >'; html += '<table id="stat' + acronym + '" class="table" style="font-size: x-small">'; html += '<thead>Run-time Statistics:<thead />'; html += '<tr><th style="text-align: center; white-space: nowrap">Stat ID</th><th>Statistic</th><th style="text-align: center">Value</th><th>TimeTag</th></tr>'; html += '</table>'; html += '</div>'; html += '<div class="modal-footer">'; html += '<span class="btn" data-dismiss="modal">Close</span>'; // close button html += '</div>'; // footer html += '</div>'; // modalWindow $('#' + acronym).html(html); $("#mod" + acronym).modal({ 'show': false }); } // Explicitly sets heights of various components on // the page to take up available screen space function resizeElements() { const sidebar = $("#sidebar"); const sidebarExpanded = sidebar.hasClass("in"); const sidebarHeight = $(window).innerHeight() - sidebar.offset().top - 20; const chartHeight = sidebarExpanded ? sidebarHeight - 40 : $(window).innerHeight() - 180; if (!sidebar.hasClass("init-width")) { sidebar.addClass("init-width"); sidebar.width($(window).innerWidth() * 0.20); } sidebar.height(sidebarHeight); $("#placeholder").height(chartHeight); $("#graphContainer").css({ "top": (sidebarExpanded ? -sidebar.height() : 0) + "px" }); $("#graphContainer").css({ "left": (sidebarExpanded ? sidebar.width() + 30 : 0) + "px" }); $("#graphContainer").width($(window).innerWidth() - (sidebarExpanded ? sidebar.width() + 80 : $("#collapseSidebarBtn").outerWidth() + 20)); $(".ui-resizable-e").height(sidebar.prop("scrollHeight")); } // Loads settings from local storage and prepares // callback functions for the initial page load function loadSettings($scope) { $scope.dataPoints = 300; $scope.selections = []; if ($.jStorage.get("dataPoints") !== null) $scope.dataPoints = $.jStorage.get("dataPoints"); if ($.jStorage.get("selections") !== null) $scope.selections = $.jStorage.get("selections"); $scope.deviceCallbacks = {}; $scope.measurementCallbacks = {}; $.each($scope.selections, function (_, selection) { $scope.deviceCallbacks[selection.device] = function (deviceMetadata) { const identifier = cleanIdentifier(deviceMetadata.acronym); $("#dd" + identifier).removeClass("collapse"); deviceMetadata.updateMeasurements(); } $scope.measurementCallbacks[selection.signalid] = function (measurementMetadata) { const cleanID = cleanIdentifier(measurementMetadata.id); $("#cb" + cleanID).prop("checked", true); updateFilter($scope, true, measurementMetadata); } }); $('#datapoints').val($scope.dataPoints); } function measurementChecked($scope, signalID) { if (signalID) { const selections = $.jStorage.get("selections"); if (selections) { for (let i = 0; i < selections.length; i++) { if (selections[i].signalid == signalID) return true; } } } return false; } // Updates the subscription filter for the data subscriber function updateFilter($scope, checked, measurementMetadata) { if (!hubIsConnected) return; if (checked) { $scope.selectedMeasurements[measurementMetadata.signalid] = { selection: { device: measurementMetadata.deviceacronym, signalid: measurementMetadata.signalid }, plotData: { label: measurementMetadata.pointtag + " - " + measurementMetadata.signalacronym, yaxis: measurementMetadata.YAxis, data: [] } }; } else { delete $scope.selectedMeasurements[measurementMetadata.signalid]; } const selectedIDs = Object.keys($scope.selectedMeasurements); if (selectedIDs.length > 0) { const filterStr = selectedIDs.join(";"); $scope.dataSubscriber.subscribe({ FilterExpression: filterStr }); } else { $scope.dataSubscriber.unsubscribe(); } plotData = Object.keys($scope.selectedMeasurements) .map(function (key) { return $scope.selectedMeasurements[key].plotData; }); plot.setData(plotData); plot.setupGrid(); plot.draw(); $scope.selections = Object.keys($scope.selectedMeasurements) .map(function (key) { return $scope.selectedMeasurements[key].selection; }); $.jStorage.set("selections", $scope.selections); } // Queries metadata for the measurements associated with the given device function getMeasurements($scope, deviceMetadata) { if (!hubIsConnected) return; $scope.dataSubscriber.getMetadata("MeasurementDetail", "DeviceAcronym = '" + deviceMetadata.acronym + "' AND SignalAcronym <> 'STAT'") .fail(e => showErrorMessage(e, null, true)) .done(function (data) { data.sort(function (m1, m2) { function signalIndex (measurementMetadata) { const matches = measurementMetadata.signalreference.match(/[0-9]+$/); if (matches === null) return 0; return Number(matches[0]); } function pointID (measurementMetadata) { return Number(measurementMetadata.id.split(':')[1]); } const comparisons = []; comparisons.push({ m1: m1.signalacronym, m2: m2.signalacronym }); comparisons.push({ m1: signalIndex(m1), m2: signalIndex(m2) }); comparisons.push({ m1: pointID(m1), m2: pointID(m2) }); for (let i = 0; i < comparisons.length; i++) { const comparison = comparisons[i]; if (comparison.m1 < comparison.m2) return -1; if (comparison.m1 > comparison.m2) return 1; } return 0; }); $.each(data, function (_, measurementMetadata) { if (measurementMetadata.signalacronym === "FREQ") measurementMetadata.YAxis = 1; else if (measurementMetadata.signalacronym === "VPHM") measurementMetadata.YAxis = 2; else if (measurementMetadata.signalacronym === "IPHM") measurementMetadata.YAxis = 3; else if (measurementMetadata.signalacronym.endsWith("PHA")) measurementMetadata.YAxis = 4; else measurementMetadata.YAxis = 5; }); deviceMetadata.measurements = data; $scope.$apply(); // Handle the callbacks for automatic measurement auto-selection $.each(data, function (_, measurementMetadata) { const callback = $scope.measurementCallbacks[measurementMetadata.signalid]; if (callback !== undefined) callback(measurementMetadata); }); }); } // Queries metadata for all statistics and // the modals to display stat information function getStatistics($scope) { $scope.dataSubscriber.getMetadata("MeasurementDetail", "SignalAcronym = 'STAT'") .fail(e => showErrorMessage(e, null, true))