424 lines · 18.1 KB · Text
RawDownload
@*******************************************************************************************************//  Status.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 destination project, include a Users.cshtml view with following content:////  @using GSF.Web//  @section StyleSheets{@Html.Raw(ViewBag.StyleSheetsSection?.ToString())}//  @Html.RenderResource("ModbusAdapters.Status.cshtml")//  @section Scripts{@Html.Raw(ViewBag.ScriptsSection?.ToString())}////******************************************************************************************************//  To use in a self-hosted web project, include a Users.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("ModbusAdapters.Status.cshtml")//  @section Scripts{@Html.Raw(ViewBag.ScriptsSection.ToString())}////*****************************************************************************************************@@* ReSharper disable RedundantUsingDirective *@@* ReSharper disable Html.PathError *@@* ReSharper disable UnknownCssClass *@@* ReSharper disable InlineOutVariableDeclaration *@@using System.Net.Http@using GSF@using GSF.Web@using GSF.Web.Model@using GSF.Web.Shared@using ModbusAdapters.Model@inherits ExtendedTemplateBase@{    // Layout will depend on rendering environment, so do not specify one    ViewBag.Title = "Status";    HttpRequestMessage request = ViewBag.Request;    Dictionary<string, string> parameters = request.QueryParameters();    string parameter, acronymn;     parameters.TryGetValue("ShowMenu", out parameter);     if (string.IsNullOrEmpty(parameter)) {        parameter = "true";    }     parameters.TryGetValue("Acronym", out acronymn);     ViewBag.ShowMenu = parameter.ParseBoolean();}@section StyleSheets {    <style>        html, body {            height: 100%;        }    </style>}@helper SearchFilter(){    @* Make sure to use single quotes in this function: *@    <div class='form-group' style='margin: 0'>        <div class='right-inner-addon'>            <i class='glyphicon glyphicon-search'></i>            <input class='form-control' type='search' id='searchFilter' placeholder='Search'/>        </div>    </div>}@section Scripts {    <script src="@Resources.Root/Shared/Scripts/knockout.js"></script>    <script>        // Define view model for device status        function StatusViewModel() {            const self = this;             // Configuration fields            self.filterText = "";                       // Search filter text             // Observable fields            self.items = ko.observableArray();          // Items to display            self.columnLength = ko.observable(4);       // Columns in each row of items             // Internal fields            self._itemLimit = ko.observable(-1);       // Item query limit             // Properties             // Gets record count            self.recordCount = ko.pureComputed({                read: function() {                    return self.items().length;                }            });             self.itemLimit = ko.pureComputed({                read: self._itemLimit,                write: function(value) {                    if (value < 1)                        value = 1;                     if (value !== self._itemLimit()) {                        self._itemLimit(value);                        self.queryItems();                    }                }            });             // Return an array of rows where each row is an array of items            self.rows = ko.computed(function() {                const result = [];                const columns = parseInt(self.columnLength(), 10);                var row;                 for (var i = 0, j = self.items().length; i < j; i++) {                    if (i % columns === 0) {                        if (row)                            result.push(row);                         row = [];                    }                     if (row)                        row.push(self.items()[i]);                }                 // Push final row                if (row)                    result.push(row);                 return result;            });             self.queryItems = function() {                if (!hubIsConnected)                    return $.Deferred().resolve().promise();                 return dataHub.queryEnabledDevices("@ViewBag.NodeID", self.itemLimit(), self.filterText).done(function(devices) {                    self.items.removeAll();                    self.items(devices);                    $(self).trigger("itemsQueried");                });            }             self.fixAcronym = function(acronym) {                return acronym.replace(/[^A-Za-z0-9]/g, "");            }        }         const viewModel = new StatusViewModel();         $(viewModel).one("itemsQueried", function () {            const acronym = "@Raw(acronymn)";             if (notNull(acronym)) {                $(viewModel).one("itemsQueried", function () {                    $(`#${viewModel.fixAcronym(acronym)}Heading`).click();                });                 $("#searchFilter").val(acronym);                viewModel.filterText = acronym;                viewModel.queryItems();            }        });         const ProgressState = {            Queued: 0,            Processing: 1,            PartialSuccess: 2,            Success: 3,            Fail: 4        };         $(window).on("beforeHubConnected", function (event) {            var deviceProgress = {};             dataHubClient.deviceProgressUpdate = function (deviceName, updates) {                var fixedDeviceName = viewModel.fixAcronym(deviceName);                const messageWell = $(`#${fixedDeviceName}Message`);                const heading = $(`#${fixedDeviceName}Heading`);                var progress = deviceProgress[deviceName];                 // TODO: This function is a mess, all the variable redeclarations are confusing, needs simplification:                $.each(updates, function (_, update) {                    if (!progress) {                        heading.click(function () {                            $.each(viewModel.items(), function (_, item) {                                $(`#${viewModel.fixAcronym(item.Acronym)}Message`).text("");                            });                             var progress = deviceProgress[deviceName];                             $.each(progress.messages, function (_, message) {                                messageWell.append(message).append($("<br>"));                            });                             var progressBar;                            var progressValue;                             progressBar = $(`#${fixedDeviceName}CurrentProgress`);                            progressValue = Math.ceil(progress.progress / progress.progressTotal * 100.0);                             if (isNumber(progressValue)) {                                progressBar.text(progressValue.toString() + "%");                                progressBar.css("width", progressBar.text());                                 if (progressValue < 100 && !progressBar.hasClass("active"))                                    progressBar.addClass("active");                                else if (progressValue == 100 && progressBar.hasClass("active"))                                    progressBar.removeClass("active");                            }                             progressBar = $(`#${fixedDeviceName}OverallProgress`);                            progressValue = Math.ceil(progress.overallProgress / progress.overallProgressTotal * 100.0);                             if (isNumber(progressValue)) {                                progressBar.text(progressValue.toString() + "%");                                progressBar.css("width", progressBar.text());                                 if (progressValue < 100 && !progressBar.hasClass("active"))                                    progressBar.addClass("active");                                else if (progressValue == 100 && progressBar.hasClass("active"))                                    progressBar.removeClass("active");                            }                        });                    }                     if (!progress || update.State === ProgressState.Queued) {                        deviceProgress[deviceName] = progress = {                            state: update.State,                            lastMessageText: "",                            lastErrorMessageText: "",                            progress: 0,                            progressTotal: 1,                            overallProgress: 0,                            overallProgressTotal: 1,                            messages: []                        };                         messageWell.text("");                    }                     if (update.State !== undefined) {                        const panel = $(`#${fixedDeviceName}Panel`);                        panel.removeClass("panel-default panel-primary panel-info panel-success panel-warning panel-danger");                        progress.state = update.State;                         switch (progress.state) {                            case ProgressState.Queued:                                panel.addClass("panel-primary");                                break;                            case ProgressState.Processing:                                panel.addClass("panel-info");                                break;                            case ProgressState.PartialSuccess:                                panel.addClass("panel-warning");                                break;                            case ProgressState.Success:                                panel.addClass("panel-success");                                break;                            case ProgressState.Fail:                                panel.addClass("panel-danger");                                break;                        }                    }                     if (notNull(update.Summary).length > 0)                        heading.text(deviceName + ": " + update.Summary);                     if (notNull(update.Message).length > 0) {                        var messageText = update.Message.substring("[yyyy-MM-dd HH:mm:ss.fff] ".length);                         if (messageText !== progress.lastMessageText) {                            progress.lastMessageText = messageText;                            progress.messages.push(update.Message);                             if (!heading.hasClass("collapsed"))                                messageWell.append(update.Message).append($("<br>"));                        }                    }                     if (notNull(update.ErrorMessage).length > 0) {                        var messageText = update.ErrorMessage.substring("[yyyy-MM-dd HH:mm:ss.fff] ".length);                         if (messageText !== progress.lastErrorMessageText) {                            progress.lastErrorMessageText = messageText;                             var message = `<span style="color: red">${update.ErrorMessage}</span>`                            progress.messages.push(message);                             if (!heading.hasClass("collapsed"))                                messageWell.append(message).append($("<br>"));                        }                    }                     if (update.Progress !== undefined || update.ProgressTotal !== undefined) {                        var progressBar = $(`#${fixedDeviceName}CurrentProgress`);                         if (update.Progress !== undefined)                            progress.progress = update.Progress;                         progress.progressTotal = update.ProgressTotal || progress.progressTotal;                        var progressValue = Math.ceil(progress.progress / progress.progressTotal * 100.0);                         if (!heading.hasClass("collapsed") && isNumber(progressValue)) {                            progressBar.text(progressValue.toString() + "%");                            progressBar.css("width", progressBar.text());                             if (progressValue < 100 && !progressBar.hasClass("active"))                                progressBar.addClass("active");                            else if (progressValue == 100 && progressBar.hasClass("active"))                                progressBar.removeClass("active");                        }                    }                     if (update.OverallProgress !== undefined || update.OverallProgressTotal !== undefined) {                        var progressBar = $(`#${fixedDeviceName}OverallProgress`);                         if (update.OverallProgress !== undefined)                            progress.overallProgress = update.OverallProgress;                         progress.overallProgressTotal = update.OverallProgressTotal || progress.overallProgressTotal;                        var progressValue = Math.ceil(progress.overallProgress / progress.overallProgressTotal * 100.0);                         if (!heading.hasClass("collapsed") && isNumber(progressValue)) {                            progressBar.text(progressValue.toString() + "%");                            progressBar.css("width", progressBar.text());                             if (progressValue < 100 && !progressBar.hasClass("active"))                                progressBar.addClass("active");                            else if (progressValue == 100 && progressBar.hasClass("active"))                                progressBar.removeClass("active");                        }                    }                });            }        });         $(function() {            $("#pageHeader").append("@Raw(SearchFilter().ToString().RemoveDuplicateWhiteSpace().Replace("\r\n", ""))");             $("#titleText").html("Records: <span data-bind='text: recordCount'>calculating...</span>");             $("#searchFilter").on("keyup", function(event) {                if ($("#searchFilter").val().length === 0) {                    $('.panel').show();                } else {                    $('.panel').hide();                    $('.panel').filter(function(i,d) {                        return $(d).attr('id').toLowerCase().indexOf($("#searchFilter").val().toLowerCase()) != -1                    }).show();                }            });             ko.applyBindings(viewModel, document.getElementsByTagName("body")[0]);        });         $(window).on("hubConnected", function (event) {            viewModel.queryItems().done(function () {                dataHub.queryDeviceStatus();            });        });         function queueNow(record) {            if (!hubIsConnected)                return;             serviceHub.sendCommand(`INVOKE ${record.Acronym} QueueTasks`);        }         function queueAll() {            for (let i = 0; i < viewModel.items().length; i++) {                queueNow(viewModel.items()[i]);            }        }    </script>}<div class="bs-example">    <div class="panel-group" id="accordion" data-bind="foreach: items">        <div class="panel panel-default" data-bind="attr: {id: $parent.fixAcronym(Acronym) + 'Panel'}">            <div class="panel-heading" >                <h4 class="panel-title">                    <a data-toggle="collapse" data-parent="#accordion" data-bind="attr: {id: $parent.fixAcronym(Acronym) +'Heading','data-target': '#sm' + $parent.fixAcronym(Acronym)}, text: Acronym"></a>                </h4>            </div>            <div data-bind="attr: {id: 'sm' + $parent.fixAcronym(Acronym)}" class="panel-collapse collapse">                <div class="panel-body">                    <div class="table-cell-wrap" data-bind="attr: {id: $parent.fixAcronym(Acronym) + 'Message'}" style="overflow-y: auto; height: 500px; padding: 5px; margin-bottom: 5px; border: 1px solid; border-color: #ddd"></div>                    <div class="progress" style="margin: 0">                        <div class="progress-bar progress-bar-striped active no-transition" role="progressbar" style="width: 0%" data-bind="attr: {id: $parent.fixAcronym(Acronym) + 'CurrentProgress'}">0%</div>                    </div>                    <div class="progress" style="margin-top: 5px; margin-bottom: -5px">                        <div class="progress-bar progress-bar-striped active" role="progressbar" style="width: 0%" data-bind="attr: {id: $parent.fixAcronym(Acronym) + 'OverallProgress'}">0%</div>                    </div>                </div>                <div class="panel-footer">                    <button type="button" class="btn btn-primary" data-bind="click: queueNow.bind($data)">Queue Processing...</button>                </div>                </div>            </div>        </div>    </div></div>@{    ViewBag.StyleSheetsSection = RenderSection("StyleSheets").ToString();    ViewBag.ScriptsSection = RenderSection("Scripts").ToString();}

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