| @@ -72,6 +72,7 @@ · 71 unmodified lines |
| 72 | bool userIsEditor = UserIsEditor(); | 72 | bool userIsEditor = UserIsEditor(); |
| 73 | } | 73 | } |
| 74 | @section StyleSheets { | 74 | @section StyleSheets { |
| | 75 | <link href="@Resources.Root/Shared/Content/jquery-ui.css" rel="stylesheet"> |
| 75 | <link href="@Resources.Root/Shared/Content/select2.css" rel="stylesheet"> | 76 | <link href="@Resources.Root/Shared/Content/select2.css" rel="stylesheet"> |
| 76 | <link href="@Resources.Root/Shared/Content/select2-bootstrap.css" rel="stylesheet"> | 77 | <link href="@Resources.Root/Shared/Content/select2-bootstrap.css" rel="stylesheet"> |
| 77 | <style> | 78 | <style> |
| @@ -79,6 +80,64 @@ · 1 unmodified line |
| 79 | html, body { | 80 | html, body { |
| 80 | height: 100%; | 81 | height: 100%; |
| 81 | } | 82 | } |
| | 83 | |
| | 84 | .tabs-nohdr { |
| | 85 | padding: 0; |
| | 86 | background: none; |
| | 87 | border-width: 0; |
| | 88 | } |
| | 89 | |
| | 90 | .tabs-nohdr .ui-tabs-nav { |
| | 91 | padding: 0; |
| | 92 | background: transparent; |
| | 93 | border: none; |
| | 94 | } |
| | 95 | |
| | 96 | .tabs-nohdr .ui-tabs-panel { |
| | 97 | border: none; |
| | 98 | padding: 0; |
| | 99 | margin: 0; |
| | 100 | } |
| | 101 | |
| | 102 | #tabs { |
| | 103 | border: none; |
| | 104 | padding: 0; |
| | 105 | } |
| | 106 | |
| | 107 | #tabs ul { |
| | 108 | border: none; |
| | 109 | background: none; |
| | 110 | } |
| | 111 | |
| | 112 | #tabs ul li { |
| | 113 | outline: none; |
| | 114 | } |
| | 115 | |
| | 116 | #tabs ul li:nth-child(1) { |
| | 117 | margin-left: -3px; |
| | 118 | } |
| | 119 | |
| | 120 | #tabs ul li a { |
| | 121 | outline: none; |
| | 122 | padding: 2px 8px; |
| | 123 | } |
| | 124 | |
| | 125 | .ui-tabs-nav li.ui-tabs-active, |
| | 126 | .ui-tabs-nav li:hover { |
| | 127 | background: #337ab7; |
| | 128 | } |
| | 129 | |
| | 130 | #tab1 { |
| | 131 | padding: 10px 0px; |
| | 132 | } |
| | 133 | |
| | 134 | #tab2 { |
| | 135 | padding: 5px 0px; |
| | 136 | } |
| | 137 | |
| | 138 | .input-group-btn button { |
| | 139 | font-size: 14px; |
| | 140 | } |
| 82 | | 141 | |
| 83 | span.table-cell-text { | 142 | span.table-cell-text { |
| 84 | display: table-cell; | 143 | display: table-cell; |
| @@ -122,6 +181,7 @@ · 37 unmodified lines |
| 122 | | 181 | |
| 123 | div.save-button { | 182 | div.save-button { |
| 124 | margin-top: -33px; | 183 | margin-top: -33px; |
| | 184 | margin-right: 2px; |
| 125 | z-index: 10; | 185 | z-index: 10; |
| 126 | } | 186 | } |
| 127 | | 187 | |
| @@ -133,6 +193,11 @@ · 5 unmodified lines |
| 133 | position: relative; | 193 | position: relative; |
| 134 | } | 194 | } |
| 135 | | 195 | |
| | 196 | div.panel-heading-condensed { |
| | 197 | height: 30px; |
| | 198 | padding-top: 5px |
| | 199 | } |
| | 200 | |
| 136 | tr.separator-top { | 201 | tr.separator-top { |
| 137 | visibility: hidden; | 202 | visibility: hidden; |
| 138 | border-bottom-width: 4px; | 203 | border-bottom-width: 4px; |
| @@ -158,6 +223,13 @@ · 19 unmodified lines |
| 158 | vertical-align: middle; | 223 | vertical-align: middle; |
| 159 | } | 224 | } |
| 160 | | 225 | |
| | 226 | label.col-input { |
| | 227 | padding-right: 0; |
| | 228 | margin-right: -10px; |
| | 229 | margin-left: 10px; |
| | 230 | white-space: nowrap; |
| | 231 | } |
| | 232 | |
| 161 | tr.top-border { | 233 | tr.top-border { |
| 162 | border-top-width: 2px; | 234 | border-top-width: 2px; |
| 163 | border-top-style: solid; | 235 | border-top-style: solid; |
| @@ -167,11 +239,86 @@ · 3 unmodified lines |
| 167 | padding-bottom: 10px; | 239 | padding-bottom: 10px; |
| 168 | } | 240 | } |
| 169 | | 241 | |
| | 242 | .form-group { |
| | 243 | padding-top: 5px; |
| | 244 | } |
| | 245 | |
| | 246 | .top-padding { |
| | 247 | padding-top: 5px; |
| | 248 | } |
| | 249 | |
| | 250 | .top-margin { |
| | 251 | margin-top: 5px; |
| | 252 | } |
| | 253 | |
| | 254 | .bottom-margin { |
| | 255 | margin-bottom: 5px; |
| | 256 | } |
| | 257 | |
| | 258 | .connection-status { |
| | 259 | background-color: black; |
| | 260 | padding: 10px; |
| | 261 | margin: 0; |
| | 262 | overflow: auto; |
| | 263 | } |
| | 264 | |
| | 265 | .glyphicon-interpret-type { |
| | 266 | color: darkgrey; |
| | 267 | vertical-align: middle; |
| | 268 | font-size: 15pt; |
| | 269 | margin-right: -5px; |
| | 270 | margin-left: -5px; |
| | 271 | } |
| | 272 | |
| | 273 | .glyphicon-interpret-result { |
| | 274 | color: darkgrey; |
| | 275 | vertical-align: middle; |
| | 276 | font-size: 17pt; |
| | 277 | } |
| | 278 | |
| | 279 | .glyphicon-delete-sequence { |
| | 280 | vertical-align: text-top; |
| | 281 | color: red; |
| | 282 | font-size: larger; |
| | 283 | } |
| | 284 | |
| | 285 | .glyphicon-action-button { |
| | 286 | color: #606060; |
| | 287 | vertical-align: text-top; |
| | 288 | } |
| | 289 | |
| | 290 | /* Collapsable button style */ |
| | 291 | .btn-collapsable.btn { |
| | 292 | color: #606060; |
| | 293 | font-family: "Glyphicons Halflings"; |
| | 294 | font-size: 8pt; |
| | 295 | position: relative; |
| | 296 | top: 0; |
| | 297 | right: -10px; |
| | 298 | padding: 0 2px 2px 2px; |
| | 299 | height: 18px; |
| | 300 | float: right !important; |
| | 301 | } |
| | 302 | |
| | 303 | /* Collapsable button icon when content is shown - arrow down */ |
| | 304 | .btn-collapsable.btn:after { |
| | 305 | content: "\e114"; |
| | 306 | } |
| | 307 | |
| | 308 | /* Collapsable button icon when content is hidden - arrow right */ |
| | 309 | .btn-collapsable.btn.collapsed:after { |
| | 310 | content: "\e080"; |
| | 311 | } |
| | 312 | |
| 170 | @@media screen { | 313 | @@media screen { |
| 171 | .page-logo { | 314 | .page-logo { |
| 172 | margin-bottom: 2px; | 315 | margin-bottom: 2px; |
| 173 | margin-top: -38px; | 316 | margin-top: -38px; |
| 174 | } | 317 | } |
| | 318 | |
| | 319 | .offset-width { |
| | 320 | width: 10px |
| | 321 | } |
| 175 | } | 322 | } |
| 176 | | 323 | |
| 177 | @@media screen and (min-width: 638px) { | 324 | @@media screen and (min-width: 638px) { |
| @@ -179,6 +326,10 @@ · 1 unmodified line |
| 179 | margin-bottom: 2px; | 326 | margin-bottom: 2px; |
| 180 | margin-top: -12px; | 327 | margin-top: -12px; |
| 181 | } | 328 | } |
| | 329 | |
| | 330 | .offset-width { |
| | 331 | width: 10px |
| | 332 | } |
| 182 | } | 333 | } |
| 183 | | 334 | |
| 184 | @@media screen and (min-width: 868px) { | 335 | @@media screen and (min-width: 868px) { |
| @@ -186,6 +337,10 @@ · 1 unmodified line |
| 186 | margin-bottom: 2px; | 337 | margin-bottom: 2px; |
| 187 | margin-top: -12px; | 338 | margin-top: -12px; |
| 188 | } | 339 | } |
| | 340 | |
| | 341 | .offset-width { |
| | 342 | width: 10px |
| | 343 | } |
| 189 | } | 344 | } |
| 190 | | 345 | |
| 191 | @@media screen and (min-width: 992px) { | 346 | @@media screen and (min-width: 992px) { |
| @@ -193,6 +348,10 @@ · 1 unmodified line |
| 193 | margin-bottom: -25px; | 348 | margin-bottom: -25px; |
| 194 | margin-top: -4px; | 349 | margin-top: -4px; |
| 195 | } | 350 | } |
| | 351 | |
| | 352 | .offset-width { |
| | 353 | width: 150px |
| | 354 | } |
| 196 | } | 355 | } |
| 197 | </style> | 356 | </style> |
| 198 | } | 357 | } |
| @@ -481,7 +640,7 @@ · 282 unmodified lines |
| 481 | } | 640 | } |
| 482 | else { | 641 | else { |
| 483 | result(tagName); | 642 | result(tagName); |
| 484 | } | 643 | } |
| 485 | }); | 644 | }); |
| 486 | }); | 645 | }); |
| 487 | | 646 | |
| @@ -864,6 +1023,36 @@ · 376 unmodified lines |
| 864 | firstCurrent = false; | 1023 | firstCurrent = false; |
| 865 | } | 1024 | } |
| 866 | } | 1025 | } |
| | 1026 | |
| | 1027 | cell.serializeCell = function () { |
| | 1028 | var phasorDefinitions = []; |
| | 1029 | |
| | 1030 | cell.PhasorDefinitions.forEach(function (phasorDefinition) { |
| | 1031 | const isVoltage = phasorDefinition.PhasorType.toLowerCase() === "voltage"; |
| | 1032 | |
| | 1033 | phasorDefinitions.push({ |
| | 1034 | ID: phasorDefinition.ID, |
| | 1035 | Label: phasorDefinition.Label(), |
| | 1036 | PhasorType: phasorDefinition.PhasorType, |
| | 1037 | Phase: phasorDefinition.Phase(), |
| | 1038 | DestinationPhasorID: isVoltage ? phasorDefinition.DestinationPhasorID : phasorDefinition.Current.AssociatedVoltage(), |
| | 1039 | NominalVoltage: isVoltage ? phasorDefinition.Voltage.NominalVoltage() : phasorDefinition.Current.AssociatedNominalVoltage(), |
| | 1040 | SourceIndex: phasorDefinition.SourceIndex |
| | 1041 | }); |
| | 1042 | }); |
| | 1043 | |
| | 1044 | return { |
| | 1045 | ID: cell.ID, |
| | 1046 | ParentID: cell.ParentID, |
| | 1047 | IDCode: cell.IDCode, |
| | 1048 | StationName: self.configFrame().Cells.length === 1 ? self.deviceName() : cell.IDLabel(), |
| | 1049 | IDLabel: cell.IDLabel(), |
| | 1050 | FrequencyDefinition: cell.FrequencyDefinition, |
| | 1051 | PhasorDefinitions: phasorDefinitions, |
| | 1052 | AnalogDefinitions: cell.AnalogDefinitions, |
| | 1053 | DigitalDefinitions: cell.DigitalDefinitions |
| | 1054 | }; |
| | 1055 | }; |
| 867 | } | 1056 | } |
| 868 | | 1057 | |
| 869 | // Make sure table is refreshed | 1058 | // Make sure table is refreshed |
| @@ -1127,6 +1316,75 @@ · 257 unmodified lines |
| 1127 | return current; | 1316 | return current; |
| 1128 | }; | 1317 | }; |
| 1129 | | 1318 | |
| | 1319 | self.serializeConfiguration = function () { |
| | 1320 | const configFrame = self.configFrame(); |
| | 1321 | const cells = configFrame.Cells; |
| | 1322 | var serializedCells = []; |
| | 1323 | |
| | 1324 | if (cells) { |
| | 1325 | cells.forEach(function (cell) { |
| | 1326 | serializedCells.push(cell.serializeCell()); |
| | 1327 | }); |
| | 1328 | } |
| | 1329 | |
| | 1330 | return { |
| | 1331 | IDCode: configFrame.IDCode, |
| | 1332 | StationName: self.deviceName(), |
| | 1333 | IDLabel: self.deviceAcronym(), |
| | 1334 | FrameRate: configFrame.FrameRate, |
| | 1335 | ConnectionString: configFrame.ConnectionString, |
| | 1336 | ProtocolID: configFrame.ProtocolID, |
| | 1337 | Cells: serializedCells |
| | 1338 | }; |
| | 1339 | }; |
| | 1340 | |
| | 1341 | self.saveConfiguration = function (fileName) { |
| | 1342 | const data = JSON.stringify(self.serializeConfiguration(), null, 2); |
| | 1343 | const anchor = $("#saveConfigurationFileLink"); |
| | 1344 | |
| | 1345 | if (typeof anchor[0].download != "undefined") { |
| | 1346 | anchor.attr("href", "data:text/json;charset=utf-8," + encodeURIComponent(data)); |
| | 1347 | anchor.attr("download", fileName); |
| | 1348 | anchor[0].click(); |
| | 1349 | } else { |
| | 1350 | if (isIE) |
| | 1351 | window.navigator.msSaveBlob(new Blob([data]), fileName); |
| | 1352 | else |
| | 1353 | window.open("data:text/json;charset=utf-8," + encodeURIComponent(data), "_blank", ""); |
| | 1354 | } |
| | 1355 | }; |
| | 1356 | |
| | 1357 | self.loadConfiguration = function (fileBlob) { |
| | 1358 | var reader = new FileReader(); |
| | 1359 | |
| | 1360 | reader.onload = function () { |
| | 1361 | if (!self.phasorHubIsConnected()) |
| | 1362 | return; |
| | 1363 | |
| | 1364 | const configFrame = JSON.parse(reader.result); |
| | 1365 | |
| | 1366 | self.voltages.removeAll(); |
| | 1367 | self.currents.removeAll(); |
| | 1368 | |
| | 1369 | $("#responsiveTableDiv").show(); |
| | 1370 | $("[id=voltageRow]").css("visibility", "hidden"); |
| | 1371 | $("[id=currentRow]").css("visibility", "hidden"); |
| | 1372 | $("#bulkSelectionButtons").hide(); |
| | 1373 | showLoadingLabel(); |
| | 1374 | |
| | 1375 | self.configFrameDetails("Loading..."); |
| | 1376 | |
| | 1377 | self.loadConfigFrame(configFrame); |
| | 1378 | } |
| | 1379 | |
| | 1380 | reader.onloadend = function () { |
| | 1381 | if (reader.error && reader.error.message) |
| | 1382 | showErrorMessage("Failed to load configuration: " + reader.error.message); |
| | 1383 | }; |
| | 1384 | |
| | 1385 | reader.readAsText(fileBlob); |
| | 1386 | }; |
| | 1387 | |
| 1130 | // Validations | 1388 | // Validations |
| 1131 | self.deviceAcronym.extend({ | 1389 | self.deviceAcronym.extend({ |
| 1132 | required: true, | 1390 | required: true, |
| @@ -2681,11 +2939,18 @@ · 1548 unmodified lines |
| 2681 | }); | 2939 | }); |
| 2682 | } | 2940 | } |
| 2683 | | 2941 | |
| | 2942 | function getActiveTab() { |
| | 2943 | return $("#tabs").tabs("option", "active"); |
| | 2944 | } |
| | 2945 | |
| 2684 | function resizePageElements() { | 2946 | function resizePageElements() { |
| 2685 | if (deviceID === 0) { | 2947 | if (deviceID === 0) { |
| 2686 | let inputConfigFileWidth = $("#inputConfigFile").width(); | 2948 | let width = $("#connectionSettings").hasClass("in") ? |
| 2687 | $("#inputDeviceAcronym").width(inputConfigFileWidth); | 2949 | (getActiveTab() === 0 ? $("#inputConfigFile").width() : $("#inputConnectionString").width()) : |
| 2688 | $("#inputDeviceName").width(inputConfigFileWidth); | 2950 | $("#connectionSettingsPanel").width() * 0.60; |
| | 2951 | |
| | 2952 | $("#inputDeviceAcronym").width(width); |
| | 2953 | $("#inputDeviceName").width(width); |
| 2689 | } | 2954 | } |
| 2690 | | 2955 | |
| 2691 | $("#contentWell").height(calculateRemainingBodyHeight() - $("#headerArea").outerHeight(true)); | 2956 | $("#contentWell").height(calculateRemainingBodyHeight() - $("#headerArea").outerHeight(true)); |
| @@ -2707,9 +2972,104 @@ · 15 unmodified lines |
| 2707 | else | 2972 | else |
| 2708 | showInfoMessage(message); | 2973 | showInfoMessage(message); |
| 2709 | } | 2974 | } |
| | 2975 | |
| | 2976 | function showConnectionBuilderDialog() { |
| | 2977 | switch ($("#selectTransport").val()) { |
| | 2978 | case "TCP": |
| | 2979 | $("#tcpBuilderDialog").show(); |
| | 2980 | $("#inputTcpHostName").focus(); |
| | 2981 | break; |
| | 2982 | case "UDP": |
| | 2983 | $("#udpBuilderDialog").show(); |
| | 2984 | $("#inputUdpPort").focus(); |
| | 2985 | break; |
| | 2986 | case "SERIAL": |
| | 2987 | $("#serialBuilderDialog").show(); |
| | 2988 | $("#inputSerialPort").focus(); |
| | 2989 | break; |
| | 2990 | // TODO: case "FILE:" |
| | 2991 | } |
| | 2992 | } |
| | 2993 | |
| | 2994 | function applyTcpConnectionSettings() { |
| | 2995 | //viewModel.connectionString(String.format("hostName = {0}; port = {1}", |
| | 2996 | // $("#inputTcpHostName").val(), |
| | 2997 | // $("#inputTcpPort").val())); |
| | 2998 | |
| | 2999 | $('#tcpBuilderDialog').hide(); |
| | 3000 | } |
| | 3001 | |
| | 3002 | function applyUdpConnectionSettings() { |
| | 3003 | //viewModel.connectionString(String.format("port = {0}; interface = {1}", |
| | 3004 | // $("#inputUdpPort").val(), |
| | 3005 | // $("#inputUdpInterface").val())); |
| | 3006 | |
| | 3007 | $('#udpBuilderDialog').hide(); |
| | 3008 | } |
| | 3009 | |
| | 3010 | function applySerialConnectionSettings() { |
| | 3011 | //viewModel.connectionString(String.format("portName = COM{0}; baudRate = {1}; dataBits = {2}; parity = {3}; stopBits = {4}", |
| | 3012 | // $("#inputSerialPort").val(), |
| | 3013 | // $("#selectSerialBaudRate").val(), |
| | 3014 | // $("#inputSerialDataBits").val(), |
| | 3015 | // $("#selectSerialParity").val(), |
| | 3016 | // $("#selectSerialStopBits").val())); |
| | 3017 | |
| | 3018 | $('#serialBuilderDialog').hide(); |
| | 3019 | } |
| | 3020 | |
| | 3021 | function connectToDevice() { |
| | 3022 | $("#connectionStatus").collapse("hide"); |
| | 3023 | |
| | 3024 | if (hubIsConnected) { |
| | 3025 | //if (deviceConnected) |
| | 3026 | // dataHub.modbusDisconnect(); |
| | 3027 | //else |
| | 3028 | // dataHub.modbusConnect($("#inputConnectionString").val()); |
| | 3029 | } else { |
| | 3030 | showErrorMessage("Device is inaccessible, cannot connect to server.", -1); |
| | 3031 | } |
| | 3032 | } |
| | 3033 | |
| | 3034 | function scrollConnectionStatusWindowToBottom() { |
| | 3035 | const connectionStatusWindow = $("#connectionStatusWindow"); |
| | 3036 | connectionStatusWindow.scrollTop(connectionStatusWindow[0].scrollHeight); |
| | 3037 | } |
| | 3038 | |
| | 3039 | function showSaveConfigurationFileNameDialog() { |
| | 3040 | $("#saveConfigurationFileNameDialog").toggle(); |
| | 3041 | |
| | 3042 | if ($("#saveConfigurationFileNameDialog").is(":visible")) { |
| | 3043 | $("#inputConfigurationFileName").val(viewModel.deviceAcronym() + ".json"); |
| | 3044 | $("#inputConfigurationFileName").focus(); |
| | 3045 | } |
| | 3046 | } |
| | 3047 | |
| | 3048 | function showLoadConfigurationFileNameDialog() { |
| | 3049 | $("#loadConfigurationFileName").trigger("click"); |
| | 3050 | } |
| | 3051 | |
| | 3052 | function loadConfigurationFile(event) { |
| | 3053 | viewModel.loadConfiguration(event.target.files[0]); |
| | 3054 | $("#loadConfigurationFileName").val(""); |
| | 3055 | } |
| 2710 | | 3056 | |
| 2711 | // Page initialization function | 3057 | // Page initialization function |
| 2712 | $(function () { | 3058 | $(function () { |
| | 3059 | $("#tabs").tabs({ |
| | 3060 | active: 0, |
| | 3061 | activate: function(event, ui) { |
| | 3062 | switch (ui.newPanel.attr("id")) { |
| | 3063 | case "tab1": |
| | 3064 | break; |
| | 3065 | case "tab2": |
| | 3066 | break; |
| | 3067 | } |
| | 3068 | |
| | 3069 | resizePageElements(); |
| | 3070 | } |
| | 3071 | }); |
| | 3072 | |
| 2713 | // Connect to phasor hub | 3073 | // Connect to phasor hub |
| 2714 | phasorHub = $.connection.phasorHub.server; | 3074 | phasorHub = $.connection.phasorHub.server; |
| 2715 | phasorHubClient = $.connection.phasorHub.client; | 3075 | phasorHubClient = $.connection.phasorHub.client; |
| @@ -2795,6 +3155,82 @@ · 79 unmodified lines |
| 2795 | ko.watch(viewModel.devices, { depth: -1 }, function (parents, child, item) { | 3155 | ko.watch(viewModel.devices, { depth: -1 }, function (parents, child, item) { |
| 2796 | viewModel.isDirty(true); | 3156 | viewModel.isDirty(true); |
| 2797 | }); | 3157 | }); |
| | 3158 | |
| | 3159 | $("#connectionSettings").on("shown.bs.collapse", function() { |
| | 3160 | resizePageElements(); |
| | 3161 | }); |
| | 3162 | |
| | 3163 | $("#connectionSettings").on("hidden.bs.collapse", function() { |
| | 3164 | resizePageElements(); |
| | 3165 | }); |
| | 3166 | |
| | 3167 | $("#connectionStatus").on("shown.bs.collapse", function() { |
| | 3168 | scrollConnectionStatusWindowToBottom(); |
| | 3169 | }); |
| | 3170 | |
| | 3171 | $("#tcpBuilderDialog").modal({ |
| | 3172 | show: false, |
| | 3173 | backdrop: "static", |
| | 3174 | keyboard: false |
| | 3175 | }); |
| | 3176 | |
| | 3177 | $("#udpBuilderDialog").modal({ |
| | 3178 | show: false, |
| | 3179 | backdrop: "static", |
| | 3180 | keyboard: false |
| | 3181 | }); |
| | 3182 | |
| | 3183 | $("#serialBuilderDialog").modal({ |
| | 3184 | show: false, |
| | 3185 | backdrop: "static", |
| | 3186 | keyboard: false |
| | 3187 | }); |
| | 3188 | |
| | 3189 | $("#saveDeviceConnectionDialog").modal({ |
| | 3190 | show: false, |
| | 3191 | backdrop: "static", |
| | 3192 | keyboard: false |
| | 3193 | }); |
| | 3194 | |
| | 3195 | $("#inputTcpHostName").focus(function() { |
| | 3196 | $(this).select(); |
| | 3197 | }); |
| | 3198 | |
| | 3199 | $("#inputTcpPort").focus(function() { |
| | 3200 | $(this).select(); |
| | 3201 | }); |
| | 3202 | |
| | 3203 | $("#inputUdpPort").focus(function() { |
| | 3204 | $(this).select(); |
| | 3205 | }); |
| | 3206 | |
| | 3207 | $("#inputUdpInterface").focus(function() { |
| | 3208 | $(this).select(); |
| | 3209 | }); |
| | 3210 | |
| | 3211 | $("#inputSerialPort").focus(function() { |
| | 3212 | $(this).select(); |
| | 3213 | }); |
| | 3214 | |
| | 3215 | $("#inputSerialDataBits").focus(function() { |
| | 3216 | $(this).select(); |
| | 3217 | }); |
| | 3218 | |
| | 3219 | $("#inputDeviceAcronym").focus(function() { |
| | 3220 | $(this).select(); |
| | 3221 | }); |
| | 3222 | |
| | 3223 | $("#inputDeviceName").focus(function() { |
| | 3224 | $(this).select(); |
| | 3225 | }); |
| | 3226 | |
| | 3227 | $("#latitude").focus(function() { |
| | 3228 | $(this).select(); |
| | 3229 | }); |
| | 3230 | |
| | 3231 | $("#longitude").focus(function() { |
| | 3232 | $(this).select(); |
| | 3233 | }); |
| 2798 | | 3234 | |
| 2799 | // Let view model know about hub connectivity changes | 3235 | // Let view model know about hub connectivity changes |
| 2800 | $(window).on("hubConnected", function () { | 3236 | $(window).on("hubConnected", function () { |
| @@ -2837,6 +3273,20 @@ · 36 unmodified lines |
| 2837 | }); | 3273 | }); |
| 2838 | } | 3274 | } |
| 2839 | | 3275 | |
| | 3276 | const protocolSelect = $("#selectProtocol"); |
| | 3277 | |
| | 3278 | // Load available synchrophasor protocols, if not already loaded |
| | 3279 | if (protocolSelect.children("option").length === 0) { |
| | 3280 | phasorHub.getSynchrophasorProtocols().done(function (protocols) { |
| | 3281 | for (let i = 0; i < protocols.length; i++) { |
| | 3282 | const protocol = protocols[i]; |
| | 3283 | protocolSelect.append($("<option>", { value: protocol.Acronym, text: protocol.Name })); |
| | 3284 | } |
| | 3285 | |
| | 3286 | protocolSelect.autoSizeSelect(); |
| | 3287 | }); |
| | 3288 | } |
| | 3289 | |
| 2840 | const templateTypeSelect = $("#templateType"); | 3290 | const templateTypeSelect = $("#templateType"); |
| 2841 | | 3291 | |
| 2842 | // Load available calculation templates, if not already loaded | 3292 | // Load available calculation templates, if not already loaded |
| @@ -2906,6 +3356,43 @@ · 63 unmodified lines |
| 2906 | | 3356 | |
| 2907 | $("#inputConfigFile").tooltip("hide"); | 3357 | $("#inputConfigFile").tooltip("hide"); |
| 2908 | }); | 3358 | }); |
| | 3359 | |
| | 3360 | // Setup save configuration file name dialog functionality |
| | 3361 | $("#saveConfigurationFile").click(function() { |
| | 3362 | $("#saveConfigurationFileNameDialog").hide(); |
| | 3363 | |
| | 3364 | var fileName = notNull($("#inputConfigurationFileName").val()); |
| | 3365 | |
| | 3366 | if (fileName.length === 0) |
| | 3367 | fileName = viewModel.deviceAcronym() + ".json"; |
| | 3368 | |
| | 3369 | if (!fileName.endsWith(".json")) |
| | 3370 | fileName += ".json"; |
| | 3371 | |
| | 3372 | viewModel.saveConfiguration(fileName); |
| | 3373 | }); |
| | 3374 | |
| | 3375 | // Make enter key auto-click save |
| | 3376 | $("#inputConfigurationFileName").keyup(function(event) { |
| | 3377 | if (event.keyCode === 13) |
| | 3378 | $("#saveConfigurationFile").click(); |
| | 3379 | }); |
| | 3380 | |
| | 3381 | // Auto-select all text on focus |
| | 3382 | $("#inputConifgurationFileName").focus(function() { |
| | 3383 | $(this).select(); |
| | 3384 | }); |
| | 3385 | |
| | 3386 | // Prevent default form submission when user presses enter |
| | 3387 | $("#saveConfigurationFileNameDialog").submit(function() { |
| | 3388 | return false; |
| | 3389 | }); |
| | 3390 | |
| | 3391 | // Auto-hide pop-up form when user clicks outside form area |
| | 3392 | $("#saveConfigurationFileNameDialog").focusout(function() { |
| | 3393 | if (!$("#saveConfigurationFileNameDialog").is(":hover") && !$("#saveConfigurationFileNameDialogButton").is(":hover")) |
| | 3394 | $("#saveConfigurationFileNameDialog").hide(); |
| | 3395 | }); |
| 2909 | | 3396 | |
| 2910 | resizePageElements(); | 3397 | resizePageElements(); |
| 2911 | | 3398 | |
| @@ -2926,12 +3413,96 @@ · 14 unmodified lines |
| 2926 | <form> | 3413 | <form> |
| 2927 | <table style="width: 100%"> | 3414 | <table style="width: 100%"> |
| 2928 | <tr data-bind="visible: !editMode()"> | 3415 | <tr data-bind="visible: !editMode()"> |
| 2929 | <td class="input-label"> | 3416 | <td colspan="2"> |
| 2930 | <label for="inputConfigFile">Connection File:</label> | 3417 | @* Connection Settings and Status Panel *@ |
| 2931 | </td> | 3418 | <div class="panel panel-primary" id="connectionSettingsPanel" style="margin: 2px"> |
| 2932 | <td> | 3419 | <div class="panel-heading panel-heading-condensed clearfix" onclick="$('#connectionSettings').collapse('toggle')"> |
| 2933 | <div class="input-group input-area"> | 3420 | <label> |
| 2934 | <input id="inputConfigFile" type="file" placeholder="Connection file name..." title="Select PMUConnection file..." accept=".PmuConnection,.xml,text/xml" data-toggle="tooltip" data-placement="right" class="form-control" style="width: 100%"> | 3421 | Connection Settings |
| | 3422 | </label> |
| | 3423 | <div class="pull-right"> |
| | 3424 | <span id="notConnected">Not Connected <span class="glyphicon glyphicon-ban-circle" style="color: #FF003B;"></span></span> |
| | 3425 | <span id="attemptingConnection" style="display: none">Attempting Connection <span class="glyphicon glyphicon-refresh glyphicon-spin" style="color: yellow"></span></span> |
| | 3426 | <span id="connected" style="display: none">Connected <span class="glyphicon glyphicon-check" style="color: chartreuse"></span></span> |
| | 3427 | <button type="button" class="btn btn-xs btn-collapsable" data-toggle="collapse" data-target="#connectionSettings"></button> |
| | 3428 | </div> |
| | 3429 | </div> |
| | 3430 | <div class="panel-body collapse in" id="connectionSettings" style="padding: 2px 6px"> |
| | 3431 | <div id="tabs"> |
| | 3432 | <ul> |
| | 3433 | <li> |
| | 3434 | <a href="#tab1">Use Connection File</a> |
| | 3435 | </li> |
| | 3436 | <li><a href="#tab2">Build Connection</a></li> |
| | 3437 | </ul> |
| | 3438 | <hr style="margin: 0; border-top-color: lightgray" /> |
| | 3439 | <div id="tab1" class="ui-tabs-active"> |
| | 3440 | <table style="width: 100%"> |
| | 3441 | <tr> |
| | 3442 | <td class="input-label" style="width: 150px"> |
| | 3443 | <label for="inputConfigFile" style="white-space: nowrap">Connection File:</label> |
| | 3444 | </td> |
| | 3445 | <td> |
| | 3446 | <div class="input-group input-area"> |
| | 3447 | <input id="inputConfigFile" type="file" placeholder="Connection file name..." title="Select PMUConnection file..." accept=".PmuConnection,.xml" data-toggle="tooltip" data-placement="right" class="form-control" style="width: 100%"> |
| | 3448 | </div> |
| | 3449 | </td> |
| | 3450 | </tr> |
| | 3451 | </table> |
| | 3452 | </div> |
| | 3453 | <div id="tab2"> |
| | 3454 | <form class="form-hortizontal" role="form"> |
| | 3455 | <div class="row"> |
| | 3456 | <div class="form-group"> |
| | 3457 | <label class="col-md-2 control-label top-padding text-right text-nowrap col-input offset-width" for="selectProtocol">Protocol:</label> |
| | 3458 | <div class="col-md-3"> |
| | 3459 | <select class="form-control" id="selectProtocol" @*data-bind="value: frameFormat"*@> |
| | 3460 | </select> |
| | 3461 | </div> |
| | 3462 | <label class="col-md-2 control-label top-padding text-right text-nowrap col-input" for="selectTransport">Transport:</label> |
| | 3463 | <div class="col-md-1"> |
| | 3464 | <select class="form-control" id="selectTransport" style="width: 85px" @*data-bind="value: transport"*@> |
| | 3465 | <option value="TCP">TCP</option> |
| | 3466 | <option value="UDP">UDP</option> |
| | 3467 | <option value="SERIAL">Serial</option> |
| | 3468 | <option value="FILE">File</option> |
| | 3469 | </select> |
| | 3470 | </div> |
| | 3471 | </div> |
| | 3472 | <div class="form-group" style="padding-top: 0; margin-top: -15px;" @*data-bind="validationElement: unitID"*@> |
| | 3473 | <label class="col-md-2 control-label top-padding text-right text-nowrap col-input" for="inputUnitID">Access ID:</label> |
| | 3474 | <div class="col-md-2"> |
| | 3475 | <input type="number" class="form-control" id="inputUnitID" @*data-bind="integer, textInput: unitID"*@> |
| | 3476 | </div> |
| | 3477 | </div> |
| | 3478 | </div> |
| | 3479 | <div class="row"> |
| | 3480 | <div class="form-group"> |
| | 3481 | <label class="col-md-2 control-label top-padding text-right text-nowrap col-input offset-width" for="inputConnectionString">Connection String:</label> |
| | 3482 | <div class="col-md-10"> |
| | 3483 | <div class="input-group"> |
| | 3484 | <input type="text" class="form-control" id="inputConnectionString" @*data-bind="textInput: connectionString"*@> |
| | 3485 | <span class="input-group-btn"> |
| | 3486 | <button type="button" class="btn btn-default" onclick="showConnectionBuilderDialog()"><span class="glyphicon glyphicon-edit glyphicon-action-button"></span> Build...</button> |
| | 3487 | <button type="button" class="btn btn-primary" style="margin-left: 5px" onclick="connectToDevice()" title="Connect to Device" hub-dependent><span class="glyphicon glyphicon-transfer" style="vertical-align: text-top"></span> <strong id="connectButtonLabel">Connect</strong> </button> |
| | 3488 | </span> |
| | 3489 | </div> |
| | 3490 | </div> |
| | 3491 | </div> |
| | 3492 | </div> |
| | 3493 | <div class="panel panel-default" style="margin: 5px 1px"> |
| | 3494 | <div class="panel-heading panel-heading-condensed" onclick="$('#connectionStatus').collapse('toggle')"> |
| | 3495 | <label>Connection Status</label> |
| | 3496 | <button type="button" class="btn btn-xs btn-collapsable collapsed" data-toggle="collapse" data-target="#connectionStatus"></button> |
| | 3497 | </div> |
| | 3498 | <div class="panel-body collapse" style="padding: 2px" id="connectionStatus"> |
| | 3499 | <pre id="connectionStatusWindow" class="small connection-status" style="height: 150px"></pre> |
| | 3500 | </div> |
| | 3501 | </div> |
| | 3502 | </form> |
| | 3503 | </div> |
| | 3504 | </div> |
| | 3505 | </div> |
| 2935 | </div> | 3506 | </div> |
| 2936 | </td> | 3507 | </td> |
| 2937 | </tr> | 3508 | </tr> |
| @@ -2944,20 +3515,20 @@ · 6 unmodified lines |
| 2944 | <input id="inputDeviceAcronym" type="text" class="form-control" style="width: 250px" data-bind="acronym: deviceAcronym, textInput: deviceAcronym, disable: readOnly" required> | 3515 | <input id="inputDeviceAcronym" type="text" class="form-control" style="width: 250px" data-bind="acronym: deviceAcronym, textInput: deviceAcronym, disable: readOnly" required> |
| 2945 | <span class="glyphicon glyphicon-remove form-control-feedback" data-bind="visible: !fieldIsValid(deviceAcronym, true)"></span> | 3516 | <span class="glyphicon glyphicon-remove form-control-feedback" data-bind="visible: !fieldIsValid(deviceAcronym, true)"></span> |
| 2946 | </div> | 3517 | </div> |
| 2947 | @if (userIsEditor) | 3518 | @if (userIsEditor) |
| 2948 | { | 3519 | { |
| 2949 | <div class="input-group save-button pull-right"> | 3520 | <div class="input-group save-button pull-right"> |
| 2950 | <button type="button" class="btn btn-sm btn-primary pull-right" id="saveData" data-bind="disable: working() || validationErrors() > 0 || !phasorHubIsConnected() || (!editMode() && isEmpty(configFrame().ConnectionString))"> | 3521 | <button type="button" class="btn btn-primary pull-right" id="saveData" data-bind="disable: working() || validationErrors() > 0 || !phasorHubIsConnected() || (!editMode() && isEmpty(configFrame().ConnectionString))"> |
| 2951 | <span class="glyphicon glyphicon-plus"></span> Save Device | 3522 | <span class="glyphicon glyphicon-plus"></span> Save Device |
| 2952 | </button> | 3523 | </button> |
| 2953 | </div> | 3524 | </div> |
| 2954 | } | 3525 | } |
| 2955 | else | 3526 | else |
| 2956 | { | 3527 | { |
| 2957 | <div class="label label-danger label-md no-access pull-right" style=""> | 3528 | <div class="label label-danger label-md no-access pull-right" style=""> |
| 2958 | User "@GetCurrentUserID()" does not have rights to add or modify device data. | 3529 | User "@GetCurrentUserID()" does not have rights to add or modify device data. |
| 2959 | </div> | 3530 | </div> |
| 2960 | } | 3531 | } |
| 2961 | </td> | 3532 | </td> |
| 2962 | </tr> | 3533 | </tr> |
| 2963 | <tr> | 3534 | <tr> |
| @@ -2969,6 +3540,28 @@ · 5 unmodified lines |
| 2969 | <input id="inputDeviceName" type="text" class="form-control" style="width: 250px" data-bind="textInput: deviceName, disable: readOnly" required> | 3540 | <input id="inputDeviceName" type="text" class="form-control" style="width: 250px" data-bind="textInput: deviceName, disable: readOnly" required> |
| 2970 | <span class="glyphicon glyphicon-remove form-control-feedback" data-bind="visible: !fieldHasValue(deviceName, true)"></span> | 3541 | <span class="glyphicon glyphicon-remove form-control-feedback" data-bind="visible: !fieldHasValue(deviceName, true)"></span> |
| 2971 | </div> | 3542 | </div> |
| | 3543 | @if (userIsEditor) |
| | 3544 | { |
| | 3545 | <div class="input-group save-button pull-right"> |
| | 3546 | <button type="button" class="btn btn-default pull-right" onclick="showSaveConfigurationFileNameDialog()" id="saveConfigurationFileNameDialogButton" data-bind="enable: configFrame().Cells.length > 0"> |
| | 3547 | <span class="glyphicon glyphicon-floppy-save glyphicon-action-button"></span> Export Config |
| | 3548 | </button> |
| | 3549 | </div> |
| | 3550 | <div class="well well-sm floating-form" id="saveConfigurationFileNameDialog" style="z-index: 1000"> |
| | 3551 | <form class="form-inline" role="form"> |
| | 3552 | <div class="form-group form-group-sm"> |
| | 3553 | <button type="button" class="close" onclick="$('#saveConfigurationFileNameDialog').hide()" style="margin-top: -13px; margin-left: 8px">×</button> |
| | 3554 | <div class="input-group"> |
| | 3555 | <input type="text" class="form-control" id="inputConfigurationFileName" placeholder="File name..." /> |
| | 3556 | <span class="input-group-btn"> |
| | 3557 | <button type="button" class="btn btn-default btn-sm" id="saveConfigurationFile" style="margin-right: -3px; font-weight: bold">Save</button> |
| | 3558 | </span> |
| | 3559 | </div> |
| | 3560 | </div> |
| | 3561 | </form> |
| | 3562 | </div> |
| | 3563 | <a href="#" id="saveConfigurationFileLink" target="_blank" style="display: none"></a> |
| | 3564 | } |
| 2972 | </td> | 3565 | </td> |
| 2973 | </tr> | 3566 | </tr> |
| 2974 | <tr> | 3567 | <tr> |
| @@ -2993,7 +3586,15 @@ · 18 unmodified lines |
| 2993 | </td> | 3586 | </td> |
| 2994 | </tr> | 3587 | </tr> |
| 2995 | </table> | 3588 | </table> |
| 2996 | | 3589 | @if (userIsEditor) |
| | 3590 | { |
| | 3591 | <div class="input-group save-button pull-right"> |
| | 3592 | <button type="button" class="btn btn-default pull-right" onclick="showLoadConfigurationFileNameDialog()"> |
| | 3593 | <span class="glyphicon glyphicon-import glyphicon-action-button"></span> Import Config |
| | 3594 | </button> |
| | 3595 | </div> |
| | 3596 | <input id="loadConfigurationFileName" type="file" accept="text/json" style="position: fixed; top: -100em" onchange="loadConfigurationFile(event)"> |
| | 3597 | } |
| 2997 | </td> | 3598 | </td> |
| 2998 | </tr> | 3599 | </tr> |
| 2999 | <tr> | 3600 | <tr> |
| @@ -3043,65 +3644,65 @@ · 43 unmodified lines |
| 3043 | <h3>Voltages</h3> | 3644 | <h3>Voltages</h3> |
| 3044 | <table class="table table-condensed table-striped table-hover"> | 3645 | <table class="table table-condensed table-striped table-hover"> |
| 3045 | <thead> | 3646 | <thead> |
| 3046 | <tr> | 3647 | <tr> |
| 3047 | <!-- ko if: configFrame().Cells.length > 1 --> | 3648 | <!-- ko if: configFrame().Cells.length > 1 --> |
| 3048 | <th class="text-left">Device Acronym</th> | 3649 | <th class="text-left">Device Acronym</th> |
| 3049 | <!-- /ko --> | 3650 | <!-- /ko --> |
| 3050 | <th class="text-left">Phasor Label</th> | 3651 | <th class="text-left">Phasor Label</th> |
| 3051 | <th class="text-center">Phase</th> | 3652 | <th class="text-center">Phase</th> |
| 3052 | <th class="text-left">Nominal Voltage</th> | 3653 | <th class="text-left">Nominal Voltage</th> |
| 3053 | </tr> | 3654 | </tr> |
| 3054 | </thead> | 3655 | </thead> |
| 3055 | <tbody data-bind="foreach: { data: voltages }"> | 3656 | <tbody data-bind="foreach: { data: voltages }"> |
| 3056 | <!-- ko if: $index() > 0 && FirstVoltage && $parent.configFrame().Cells.length > 1 --> | 3657 | <!-- ko if: $index() > 0 && FirstVoltage && $parent.configFrame().Cells.length > 1 --> |
| 3057 | <tr id="voltageRow" class="separator-top"> | 3658 | <tr id="voltageRow" class="separator-top"> |
| 3058 | <td colspan="4" style="padding: 2px"></td> | 3659 | <td colspan="4" style="padding: 2px"></td> |
| 3059 | </tr> | 3660 | </tr> |
| 3060 | <tr id="voltageRow" class="separator-bottom"> | 3661 | <tr id="voltageRow" class="separator-bottom"> |
| 3061 | <td colspan="4" style="padding: 2px"></td> | 3662 | <td colspan="4" style="padding: 2px"></td> |
| 3062 | </tr> | 3663 | </tr> |
| | 3664 | <!-- /ko --> |
| | 3665 | <tr id="voltageRow" style="visibility: hidden"> |
| | 3666 | <!-- ko if: !FirstVoltage && $parent.configFrame().Cells.length > 1 --> |
| | 3667 | <td width="190px" class="text-left" style="padding-right: 20px"> |
| | 3668 | <div class="input-group"></div> |
| | 3669 | </td> |
| 3063 | <!-- /ko --> | 3670 | <!-- /ko --> |
| 3064 | <tr id="voltageRow" style="visibility: hidden"> | 3671 | <!-- ko if: FirstVoltage && $parent.configFrame().Cells.length > 1 --> |
| 3065 | <!-- ko if: !FirstVoltage && $parent.configFrame().Cells.length > 1 --> | 3672 | <td width="190px" class="text-left" style="padding-right: 20px"> |
| 3066 | <td width="190px" class="text-left" style="padding-right: 20px"> | 3673 | <span class="org-label" data-bind="visible: OriginalLabel != Label()"> </span> |
| 3067 | <div class="input-group"></div> | 3674 | <div class="input-group" data-bind="css: { 'has-error': !fieldIsValid(Device.IDLabel, true), 'has-feedback': !fieldIsValid(Device.IDLabel, true) }"> |
| 3068 | </td> | 3675 | <input type="text" class="form-control" style="width: 180px" data-bind="acronym: Device.IDLabel, textInput: Device.IDLabel, disable: $parent.readOnly() || !$data.Enabled()" required> |
| 3069 | <!-- /ko --> | 3676 | <span class="glyphicon glyphicon-remove form-control-feedback" data-bind="visible: !fieldIsValid(Device.IDLabel, true)"></span> |
| 3070 | <!-- ko if: FirstVoltage && $parent.configFrame().Cells.length > 1 --> | 3677 | </div> |
| 3071 | <td width="190px" class="text-left" style="padding-right: 20px"> | 3678 | </td> |
| 3072 | <span class="org-label" data-bind="visible: OriginalLabel != Label()"> </span> | 3679 | <!-- /ko --> |
| 3073 | <div class="input-group" data-bind="css: { 'has-error': !fieldIsValid(Device.IDLabel, true), 'has-feedback': !fieldIsValid(Device.IDLabel, true) }"> | 3680 | <td width="210px" class="text-left" style="position: sticky"> |
| 3074 | <input type="text" class="form-control" style="width: 180px" data-bind="acronym: Device.IDLabel, textInput: Device.IDLabel, disable: $parent.readOnly() || !$data.Enabled()" required> | 3681 | <span class="org-label" data-bind="text: OriginalLabel, visible: OriginalLabel != Label()"></span> |
| 3075 | <span class="glyphicon glyphicon-remove form-control-feedback" data-bind="visible: !fieldIsValid(Device.IDLabel, true)"></span> | 3682 | <div class="checkbox" style="margin-top: 0; margin-bottom: 0"> |
| 3076 | </div> | 3683 | <input type="checkbox" style="margin-top: 10px" data-bind="checked: Enabled"> |
| 3077 | </td> | 3684 | </div> |
| 3078 | <!-- /ko --> | 3685 | <div class="input-group" data-bind="css: { 'has-error': !fieldIsValid(Label, true), 'has-feedback': !fieldIsValid(Label, true), 'error-margin': !fieldIsValid(Label, true) }"> |
| 3079 | <td width="210px" class="text-left" style="position: sticky"> | 3686 | <input type="text" class="form-control" style="width: 200px" data-bind="acronym: Label, textInput: Label, disable: $parent.readOnly() || !$data.Enabled()" required> |
| 3080 | <span class="org-label" data-bind="text: OriginalLabel, visible: OriginalLabel != Label()"></span> | 3687 | <span class="glyphicon glyphicon-remove form-control-feedback" style="z-index: 5" data-bind="visible: !fieldIsValid(Label, true)"></span> |
| 3081 | <div class="checkbox" style="margin-top: 0; margin-bottom: 0"> | 3688 | </div> |
| 3082 | <input type="checkbox" style="margin-top: 10px" data-bind="checked: Enabled"> | 3689 | <span class="tag-name-label">Tag name:</span><span class="tag-name" data-bind="text: TagName"></span> |
| 3083 | </div> | 3690 | </td> |
| 3084 | <div class="input-group" data-bind="css: { 'has-error': !fieldIsValid(Label, true), 'has-feedback': !fieldIsValid(Label, true), 'error-margin': !fieldIsValid(Label, true) }"> | 3691 | <td width="55px" class="text-center"> |
| 3085 | <input type="text" class="form-control" style="width: 200px" data-bind="acronym: Label, textInput: Label, disable: $parent.readOnly() || !$data.Enabled()" required> | 3692 | <span class="org-label" data-bind="visible: OriginalLabel != Label()"> </span> |
| 3086 | <span class="glyphicon glyphicon-remove form-control-feedback" style="z-index: 5" data-bind="visible: !fieldIsValid(Label, true)"></span> | 3693 | <div class="input-group" data-bind="css: { 'has-error': !fieldIsValid(Phase, true), 'has-feedback': !fieldIsValid(Phase, true), 'error-margin': !fieldIsValid(Phase, true) }"> |
| 3087 | </div> | 3694 | <input type="text" class="form-control" maxlength="1" style="width: 37px; margin-left: 3px" onclick="this.select()" data-bind="upperCase: Phase, textInput: Phase, style: { width: Phase.isValid() ? '37px' : '70px' }, selectOnError: Phase, enable: Enabled" placeholder="Ø" required> |
| 3088 | <span class="tag-name-label">Tag name:</span><span class="tag-name" data-bind="text: TagName"></span> | 3695 | <span class="glyphicon glyphicon-remove form-control-feedback" style="z-index: 5" data-bind="visible: !fieldIsValid(Phase, true)"></span> |
| 3089 | </td> | 3696 | </div> |
| 3090 | <td width="55px" class="text-center"> | 3697 | </td> |
| 3091 | <span class="org-label" data-bind="visible: OriginalLabel != Label()"> </span> | 3698 | <td class="text-left"> |
| 3092 | <div class="input-group" data-bind="css: { 'has-error': !fieldIsValid(Phase, true), 'has-feedback': !fieldIsValid(Phase, true), 'error-margin': !fieldIsValid(Phase, true) }"> | 3699 | <span class="org-label" data-bind="visible: OriginalLabel != Label()"> </span> |
| 3093 | <input type="text" class="form-control" maxlength="1" style="width: 37px; margin-left: 3px" onclick="this.select()" data-bind="upperCase: Phase, textInput: Phase, style: { width: Phase.isValid() ? '37px' : '70px' }, selectOnError: Phase, enable: Enabled" placeholder="Ø" required> | 3700 | <div class="input-group" data-bind="css: { 'has-error': !fieldIsValid(NominalVoltage, true), 'has-feedback': !fieldIsValid(NominalVoltage, true), 'error-margin':! fieldIsValid(NominalVoltage, true) }"> |
| 3094 | <span class="glyphicon glyphicon-remove form-control-feedback" style="z-index: 5" data-bind="visible: !fieldIsValid(Phase, true)"></span> | 3701 | <input type="text" class="form-control" style="width: 70px; margin-left: 10px" data-bind="integer: NominalVoltage, textInput: NominalVoltage, enable: Enabled" required> <span class="table-cell-text">kV</span> |
| 3095 | </div> | 3702 | <span class="glyphicon glyphicon-remove form-control-feedback" style="z-index: 5; width: 65px" data-bind="visible: !fieldIsValid(NominalVoltage, true)"></span> |
| 3096 | </td> | 3703 | </div> |
| 3097 | <td class="text-left"> | 3704 | </td> |
| 3098 | <span class="org-label" data-bind="visible: OriginalLabel != Label()"> </span> | 3705 | </tr> |
| 3099 | <div class="input-group" data-bind="css: { 'has-error': !fieldIsValid(NominalVoltage, true), 'has-feedback': !fieldIsValid(NominalVoltage, true), 'error-margin':! fieldIsValid(NominalVoltage, true) }"> | | |
| 3100 | <input type="text" class="form-control" style="width: 70px; margin-left: 10px" data-bind="integer: NominalVoltage, textInput: NominalVoltage, enable: Enabled" required> <span class="table-cell-text">kV</span> | | |
| 3101 | <span class="glyphicon glyphicon-remove form-control-feedback" style="z-index: 5; width: 65px" data-bind="visible: !fieldIsValid(NominalVoltage, true)"></span> | | |
| 3102 | </div> | | |
| 3103 | </td> | | |
| 3104 | </tr> | | |
| 3105 | </tbody> | 3706 | </tbody> |
| 3106 | </table> | 3707 | </table> |
| 3107 | </div> | 3708 | </div> |
| @@ -3111,67 +3712,193 @@ · 3 unmodified lines |
| 3111 | <h3>Currents</h3> | 3712 | <h3>Currents</h3> |
| 3112 | <table class="table table-condensed table-striped table-hover"> | 3713 | <table class="table table-condensed table-striped table-hover"> |
| 3113 | <thead> | 3714 | <thead> |
| 3114 | <tr> | 3715 | <tr> |
| 3115 | <!-- ko if: configFrame().Cells.length > 1 --> | 3716 | <!-- ko if: configFrame().Cells.length > 1 --> |
| 3116 | <th class="text-left">Device Acronym</th> | 3717 | <th class="text-left">Device Acronym</th> |
| 3117 | <!-- /ko --> | 3718 | <!-- /ko --> |
| 3118 | <th class="text-left">Phasor Label</th> | 3719 | <th class="text-left">Phasor Label</th> |
| 3119 | <th class="text-center">Phase</th> | 3720 | <th class="text-center">Phase</th> |
| 3120 | <th class="text-left">Associated Voltage</th> | 3721 | <th class="text-left">Associated Voltage</th> |
| 3121 | </tr> | 3722 | </tr> |
| 3122 | </thead> | 3723 | </thead> |
| 3123 | <tbody data-bind="foreach: { data: currents }"> | 3724 | <tbody data-bind="foreach: { data: currents }"> |
| 3124 | <!-- ko if: $index() > 0 && FirstCurrent && $parent.configFrame().Cells.length > 1 --> | 3725 | <!-- ko if: $index() > 0 && FirstCurrent && $parent.configFrame().Cells.length > 1 --> |
| 3125 | <tr id="currentRow" class="separator-top"> | 3726 | <tr id="currentRow" class="separator-top"> |
| 3126 | <td colspan="4" style="padding: 2px"></td> | 3727 | <td colspan="4" style="padding: 2px"></td> |
| 3127 | </tr> | 3728 | </tr> |
| 3128 | <tr id="currentRow" class="separator-bottom"> | 3729 | <tr id="currentRow" class="separator-bottom"> |
| 3129 | <td colspan="4" style="padding: 2px"></td> | 3730 | <td colspan="4" style="padding: 2px"></td> |
| 3130 | </tr> | 3731 | </tr> |
| | 3732 | <!-- /ko --> |
| | 3733 | <tr id="currentRow" style="visibility: hidden"> |
| | 3734 | <!-- ko if: !FirstCurrent && $parent.configFrame().Cells.length > 1 --> |
| | 3735 | <td width="190px" class="text-left" style="padding-right: 20px"> |
| | 3736 | <div class="input-group"></div> |
| | 3737 | </td> |
| 3131 | <!-- /ko --> | 3738 | <!-- /ko --> |
| 3132 | <tr id="currentRow" style="visibility: hidden"> | 3739 | <!-- ko if: FirstCurrent && $parent.configFrame().Cells.length > 1 --> |
| 3133 | <!-- ko if: !FirstCurrent && $parent.configFrame().Cells.length > 1 --> | 3740 | <td width="190px" class="text-left" style="padding-right: 20px"> |
| 3134 | <td width="190px" class="text-left" style="padding-right: 20px"> | 3741 | <span class="org-label" data-bind="visible: OriginalLabel != Label()"> </span> |
| 3135 | <div class="input-group"></div> | 3742 | <div class="input-group" data-bind="css: { 'has-error': !fieldIsValid(Device.IDLabel, true), 'has-feedback': !fieldIsValid(Device.IDLabel, true) }"> |
| 3136 | </td> | 3743 | <input type="text" class="form-control" style="width: 180px" data-bind="acronym: Device.IDLabel, textInput: Device.IDLabel, disable: $parent.readOnly() || !$data.Enabled()" required> |
| 3137 | <!-- /ko --> | 3744 | <span class="glyphicon glyphicon-remove form-control-feedback" data-bind="visible: !fieldIsValid(Device.IDLabel, true)"></span> |
| 3138 | <!-- ko if: FirstCurrent && $parent.configFrame().Cells.length > 1 --> | 3745 | </div> |
| 3139 | <td width="190px" class="text-left" style="padding-right: 20px"> | 3746 | </td> |
| 3140 | <span class="org-label" data-bind="visible: OriginalLabel != Label()"> </span> | 3747 | <!-- /ko --> |
| 3141 | <div class="input-group" data-bind="css: { 'has-error': !fieldIsValid(Device.IDLabel, true), 'has-feedback': !fieldIsValid(Device.IDLabel, true) }"> | 3748 | <td width="210px" class="text-left" style="position: sticky"> |
| 3142 | <input type="text" class="form-control" style="width: 180px" data-bind="acronym: Device.IDLabel, textInput: Device.IDLabel, disable: $parent.readOnly() || !$data.Enabled()" required> | 3749 | <span class="org-label" data-bind="text: OriginalLabel, visible: OriginalLabel != Label()"></span> |
| 3143 | <span class="glyphicon glyphicon-remove form-control-feedback" data-bind="visible: !fieldIsValid(Device.IDLabel, true)"></span> | 3750 | <div class="checkbox" style="margin-top: 0; margin-bottom: 0"> |
| 3144 | </div> | 3751 | <input type="checkbox" style="margin-top: 10px" data-bind="checked: Enabled"> |
| 3145 | </td> | 3752 | </div> |
| 3146 | <!-- /ko --> | 3753 | <div class="input-group" data-bind="css: { 'has-error': !fieldIsValid(Label, true), 'has-feedback': !fieldIsValid(Label, true), 'error-margin': !fieldIsValid(Label, true) }"> |
| 3147 | <td width="210px" class="text-left" style="position: sticky"> | 3754 | <input type="text" class="form-control" style="width: 200px" data-bind="acronym: Label, textInput: Label, disable: $parent.readOnly() || !$data.Enabled()" required> |
| 3148 | <span class="org-label" data-bind="text: OriginalLabel, visible: OriginalLabel != Label()"></span> | 3755 | <span class="glyphicon glyphicon-remove form-control-feedback" style="z-index: 5" data-bind="visible: !fieldIsValid(Label, true)"></span> |
| 3149 | <div class="checkbox" style="margin-top: 0; margin-bottom: 0"> | 3756 | </div> |
| 3150 | <input type="checkbox" style="margin-top: 10px" data-bind="checked: Enabled"> | 3757 | <span class="tag-name-label">Tag name:</span><span class="tag-name" data-bind="text: TagName"></span> |
| 3151 | </div> | 3758 | </td> |
| 3152 | <div class="input-group" data-bind="css: { 'has-error': !fieldIsValid(Label, true), 'has-feedback': !fieldIsValid(Label, true), 'error-margin': !fieldIsValid(Label, true) }"> | 3759 | <td width="55px" class="text-center"> |
| 3153 | <input type="text" class="form-control" style="width: 200px" data-bind="acronym: Label, textInput: Label, disable: $parent.readOnly() || !$data.Enabled()" required> | 3760 | <span class="org-label" data-bind="visible: OriginalLabel != Label()"> </span> |
| 3154 | <span class="glyphicon glyphicon-remove form-control-feedback" style="z-index: 5" data-bind="visible: !fieldIsValid(Label, true)"></span> | 3761 | <div class="input-group" data-bind="css: { 'has-error': !fieldIsValid(Phase, true), 'has-feedback': !fieldIsValid(Phase, true), 'error-margin': !fieldIsValid(Phase, true)}"> |
| 3155 | </div> | 3762 | <input type="text" class="form-control" maxlength="1" style="width: 37px; margin-left: 3px" onclick="this.select()" data-bind="upperCase: Phase, textInput: Phase, style: { width: Phase.isValid() ? '37px' : '70px' }, selectOnError: Phase, enable: Enabled" placeholder="Ø" required> |
| 3156 | <span class="tag-name-label">Tag name:</span><span class="tag-name" data-bind="text: TagName"></span> | 3763 | <span class="glyphicon glyphicon-remove form-control-feedback" style="z-index: 5" data-bind="visible: !fieldIsValid(Phase, true)"></span> |
| 3157 | </td> | 3764 | </div> |
| 3158 | <td width="55px" class="text-center"> | 3765 | </td> |
| 3159 | <span class="org-label" data-bind="visible: OriginalLabel != Label()"> </span> | 3766 | <td class="text-left"> |
| 3160 | <div class="input-group" data-bind="css: { 'has-error': !fieldIsValid(Phase, true), 'has-feedback': !fieldIsValid(Phase, true), 'error-margin': !fieldIsValid(Phase, true)}"> | 3767 | <span class="org-label" data-bind="visible: OriginalLabel != Label()"> </span> |
| 3161 | <input type="text" class="form-control" maxlength="1" style="width: 37px; margin-left: 3px" onclick="this.select()" data-bind="upperCase: Phase, textInput: Phase, style: { width: Phase.isValid() ? '37px' : '70px' }, selectOnError: Phase, enable: Enabled" placeholder="Ø" required> | 3768 | <select class="form-control" style="width: 85%" data-bind="options: DeviceVoltages, optionsCaption: 'None', value: AssociatedVoltage, optionsText: associatedVoltageLabel, optionsValue: 'ID', valueAllowUnset: true, enable: Enabled, select2: { templateSelection: formatAssociatedVoltageLabel, templateResult: formatAssociatedVoltageLabel, minimumResultsForSearch: -1, theme: 'bootstrap' }"></select> |
| 3162 | <span class="glyphicon glyphicon-remove form-control-feedback" style="z-index: 5" data-bind="visible: !fieldIsValid(Phase, true)"></span> | 3769 | </td> |
| 3163 | </div> | 3770 | </tr> |
| 3164 | </td> | | |
| 3165 | <td class="text-left"> | | |
| 3166 | <span class="org-label" data-bind="visible: OriginalLabel != Label()"> </span> | | |
| 3167 | <select class="form-control" style="width: 90%" data-bind="options: DeviceVoltages, optionsCaption: 'None', value: AssociatedVoltage, optionsText: associatedVoltageLabel, optionsValue: 'ID', valueAllowUnset: true, enable: Enabled, select2: { templateSelection: formatAssociatedVoltageLabel, templateResult: formatAssociatedVoltageLabel, minimumResultsForSearch: -1, theme: 'bootstrap' }"></select> | | |
| 3168 | </td> | | |
| 3169 | </tr> | | |
| 3170 | </tbody> | 3771 | </tbody> |
| 3171 | </table> | 3772 | </table> |
| 3172 | </div> | 3773 | </div> |
| 3173 | </div> | 3774 | </div> |
| 3174 | </div> | 3775 | </div> |
| | 3776 | @* JSON Sequences Display Panel *@ |
| | 3777 | <hr class="thick-spacer" @*data-bind="visible: sequences().length > 0"*@ /> |
| | 3778 | <div class="panel panel-default"> |
| | 3779 | <div class="panel-heading panel-heading-condensed" onclick="$('#serializedConfiguration').collapse('toggle')"> |
| | 3780 | <label>Serialized Configuration</label> |
| | 3781 | <button type="button" class="btn btn-xs btn-collapsable collapsed" style="top: 0; right: -10px" data-toggle="collapse" data-target="#serializedConfiguration"></button> |
| | 3782 | </div> |
| | 3783 | <div class="panel-body collapse" style="padding: 2px; margin-bottom: -10px" id="serializedConfiguration"> |
| | 3784 | <pre data-bind="text: JSON.stringify(serializeConfiguration(), null, 2)"></pre> |
| | 3785 | </div> |
| | 3786 | </div> |
| | 3787 | @* TCP Connection String Builder *@ |
| | 3788 | <div id="tcpBuilderDialog" class="modal" role="dialog"> |
| | 3789 | <div class="modal-dialog modal-md"> |
| | 3790 | <div class="modal-content"> |
| | 3791 | <div class="modal-header"> |
| | 3792 | <button type="button" class="close" onclick="$('#tcpBuilderDialog').hide()">×</button> |
| | 3793 | <h4 class="modal-title">Define TCP Connection Parameters</h4> |
| | 3794 | </div> |
| | 3795 | <div class="modal-body"> |
| | 3796 | <form role="form"> |
| | 3797 | <div class="form-group" @*data-bind="validationElement: hostName"*@> |
| | 3798 | <label for="inputTcpHostName">Host Name or IP:</label> |
| | 3799 | <input type="text" class="form-control" id="inputTcpHostName" maxlength="200" size="200" @*data-bind="textInput: hostName"*@> |
| | 3800 | </div> |
| | 3801 | <div class="form-group" @*data-bind="validationElement: ipPort"*@> |
| | 3802 | <label for="inputTcpPort">Port:</label> |
| | 3803 | <input type="number" class="form-control" id="inputTcpPort" maxlength="5" size="5" @*data-bind="integer, textInput: ipPort"*@> |
| | 3804 | </div> |
| | 3805 | </form> |
| | 3806 | </div> |
| | 3807 | <div class="modal-footer"> |
| | 3808 | <button type="submit" class="btn btn-primary" onclick="applyTcpConnectionSettings()">Save</button> |
| | 3809 | <button type="button" class="btn btn-default" onclick="$('#tcpBuilderDialog').hide()">Cancel</button> |
| | 3810 | </div> |
| | 3811 | </div> |
| | 3812 | </div> |
| | 3813 | </div> |
| | 3814 | @* UDP Connection String Builder *@ |
| | 3815 | <div id="udpBuilderDialog" class="modal" role="dialog"> |
| | 3816 | <div class="modal-dialog modal-md"> |
| | 3817 | <div class="modal-content"> |
| | 3818 | <div class="modal-header"> |
| | 3819 | <button type="button" class="close" onclick="$('#udpBuilderDialog').hide()">×</button> |
| | 3820 | <h4 class="modal-title">Define UDP Connection Parameters</h4> |
| | 3821 | </div> |
| | 3822 | <div class="modal-body"> |
| | 3823 | <form role="form"> |
| | 3824 | <div class="form-group" @*data-bind="validationElement: ipPort"*@> |
| | 3825 | <label for="inputUdpPort">Port:</label> |
| | 3826 | <input type="number" class="form-control" id="inputUdpPort" maxlength="5" size="5" @*data-bind="integer, textInput: ipPort"*@> |
| | 3827 | </div> |
| | 3828 | <div class="form-group" @*data-bind="validationElement: interface"*@> |
| | 3829 | <label for="inputUdpInterface">Interface IP:</label> |
| | 3830 | <input type="text" class="form-control" id="inputUdpInterface" maxlength="200" size="200" @*data-bind="textInput: interface"*@> |
| | 3831 | </div> |
| | 3832 | </form> |
| | 3833 | </div> |
| | 3834 | <div class="modal-footer"> |
| | 3835 | <button type="submit" class="btn btn-primary" onclick="applyUdpConnectionSettings()">Save</button> |
| | 3836 | <button type="button" class="btn btn-default" onclick="$('#udpBuilderDialog').hide()">Cancel</button> |
| | 3837 | </div> |
| | 3838 | </div> |
| | 3839 | </div> |
| | 3840 | </div> |
| | 3841 | @* Serial Connection String Builder *@ |
| | 3842 | <div id="serialBuilderDialog" class="modal" role="dialog"> |
| | 3843 | <div class="modal-dialog modal-md"> |
| | 3844 | <div class="modal-content"> |
| | 3845 | <div class="modal-header"> |
| | 3846 | <button type="button" class="close" onclick="$('#serialBuilderDialog').hide()">×</button> |
| | 3847 | <h4 class="modal-title">Define Serial Connection Parameters</h4> |
| | 3848 | </div> |
| | 3849 | <div class="modal-body"> |
| | 3850 | <form role="form"> |
| | 3851 | <div class="form-group" @*data-bind="validationElement: comPort"*@> |
| | 3852 | <label for="inputSerialPort">Port:</label> |
| | 3853 | <div class="input-group"> |
| | 3854 | <span class="input-group-addon">COM</span> |
| | 3855 | <input type="number" class="form-control" id="inputSerialPort" maxlength="5" size="5" @*data-bind="integer, textInput: comPort"*@> |
| | 3856 | </div> |
| | 3857 | </div> |
| | 3858 | <div class="form-group"> |
| | 3859 | <label for="selectSerialBaudRate">Baud Rate:</label> |
| | 3860 | <select class="form-control" id="selectSerialBaudRate"> |
| | 3861 | <option value="115200">115200</option> |
| | 3862 | <option value="57600" selected>57600</option> |
| | 3863 | <option value="38400">38400</option> |
| | 3864 | <option value="19200">19200</option> |
| | 3865 | <option value="9600">9600</option> |
| | 3866 | <option value="4800">4800</option> |
| | 3867 | <option value="2400">2400</option> |
| | 3868 | <option value="1200">1200</option> |
| | 3869 | </select> |
| | 3870 | </div> |
| | 3871 | <div class="form-group"> |
| | 3872 | <label for="selectSerialParity">Parity:</label> |
| | 3873 | <select class="form-control" id="selectSerialParity"> |
| | 3874 | <option value="None" selected>None</option> |
| | 3875 | <option value="Odd">Odd</option> |
| | 3876 | <option value="Even">Even</option> |
| | 3877 | <option value="Mark">Mark</option> |
| | 3878 | <option value="Space">Space</option> |
| | 3879 | </select> |
| | 3880 | </div> |
| | 3881 | <div class="form-group"> |
| | 3882 | <label for="selectSerialStopBits">Stop Bits:</label> |
| | 3883 | <select class="form-control" id="selectSerialStopBits"> |
| | 3884 | <option value="One" selected>One</option> |
| | 3885 | <option value="Two">Two</option> |
| | 3886 | <option value="OnePointFive">OnePointFive</option> |
| | 3887 | </select> |
| | 3888 | </div> |
| | 3889 | <div class="form-group" @*data-bind="validationElement: dataBits"*@> |
| | 3890 | <label for="inputSerialDataBits">Data Bits:</label> |
| | 3891 | <input type="number" class="form-control" id="inputSerialDataBits" maxlength="200" size="200" @*data-bind="integer, textInput: dataBits"*@> |
| | 3892 | </div> |
| | 3893 | </form> |
| | 3894 | </div> |
| | 3895 | <div class="modal-footer"> |
| | 3896 | <button type="submit" class="btn btn-primary" onclick="applySerialConnectionSettings()">Save</button> |
| | 3897 | <button type="button" class="btn btn-default" onclick="$('#serialBuilderDialog').hide()">Cancel</button> |
| | 3898 | </div> |
| | 3899 | </div> |
| | 3900 | </div> |
| | 3901 | </div> |
| 3175 | @{ | 3902 | @{ |
| 3176 | ViewBag.StyleSheetsSection = RenderSection("StyleSheets").ToString(); | 3903 | ViewBag.StyleSheetsSection = RenderSection("StyleSheets").ToString(); |
| 3177 | ViewBag.ScriptsSection = RenderSection("Scripts").ToString(); | 3904 | ViewBag.ScriptsSection = RenderSection("Scripts").ToString(); |