941 lines · 40.8 KB · Text
RawDownload
@*******************************************************************************************************//  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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t