History

PhasorWebUI: Added import / export config, serialized section and inital connection builder to wizard screen

J. Ritchie Carroll committed 6 years ago

Commit 352378c1 parent 139422b4 files · +887 −135Browse files

Unified · Split

4 files
  1. Source/Libraries/Adapters/PhasorWebUI/Model/SynchrophasorProtocol.cs +10 −0
  2. Source/Libraries/Adapters/PhasorWebUI/PhasorHub.cs +15 −1
  3. Source/Libraries/Adapters/PhasorWebUI/PhasorWebUI.csproj +1 −0
  4. Source/Libraries/Adapters/PhasorWebUI/Views/AddSynchrophasorDevice.cshtml +861 −134
Source/Libraries/Adapters/PhasorWebUI/Model/SynchrophasorProtocol.cs+10−0
@@ -0,0 +1,10 @@
1#pragma warning disable 1591
2
3namespace PhasorWebUI.Model
4{
5 public class SynchrophasorProtocol
6 {
7 public string Acronym;
8 public string Name;
9 }
10}
Source/Libraries/Adapters/PhasorWebUI/PhasorHub.cs+15−1
@@ -731,7 +731,7 @@ namespace PhasorWebUI · 730 unmodified lines
731 if (!string.IsNullOrWhiteSpace(connectionString))731 if (!string.IsNullOrWhiteSpace(connectionString))
732 {732 {
733 Dictionary<string, string> settings = connectionString.ParseKeyValuePairs();733 Dictionary<string, string> settings = connectionString.ParseKeyValuePairs();
734 protocolID = DataContext.Table<Protocol>().QueryRecordWhere("Acronym = {0}", settings["phasorProtocol"]).ID;734 protocolID = GetProtocolID(settings["phasorProtocol"]);
735 }735 }
736 736
737 derivedFrame = new ConfigurationFrame737 derivedFrame = new ConfigurationFrame
@@ -818,11 +818,25 @@ namespace PhasorWebUI · 80 unmodified lines
818 return tagTemplates;818 return tagTemplates;
819 }819 }
820 820
821 public int GetProtocolID(string protocolAcronym)
822 {
823 return DataContext.Table<Protocol>().QueryRecordWhere("Acronym = {0}", protocolAcronym).ID;
824 }
825
821 public string GetProtocolCategory(int protocolID)826 public string GetProtocolCategory(int protocolID)
822 {827 {
823 return DataContext.Table<Protocol>().QueryRecordWhere("ID = {0}", protocolID).Category;828 return DataContext.Table<Protocol>().QueryRecordWhere("ID = {0}", protocolID).Category;
824 }829 }
825 830
831 public IEnumerable<SynchrophasorProtocol> GetSynchrophasorProtocols()
832 {
833 return Enum.GetValues(typeof(PhasorProtocol)).Cast<PhasorProtocol>().Select(protocol => new SynchrophasorProtocol
834 {
835 Acronym = protocol.ToString(),
836 Name = protocol.GetFormattedProtocolName()
837 });
838 }
839
826 public void ValidateCalculatorConfigurations(int? historianID)840 public void ValidateCalculatorConfigurations(int? historianID)
827 {841 {
828 const int Avg = 0, Max = 1, Min = 2;842 const int Avg = 0, Max = 1, Min = 2;
Source/Libraries/Adapters/PhasorWebUI/PhasorWebUI.csproj+1−0
@@ -94,6 +94,7 @@ · 93 unmodified lines
94 <Compile Include="Model\SignalType.cs" />94 <Compile Include="Model\SignalType.cs" />
95 <Compile Include="Model\TagTemplate.cs" />95 <Compile Include="Model\TagTemplate.cs" />
96 <Compile Include="PhasorHub.cs" />96 <Compile Include="PhasorHub.cs" />
97 <Compile Include="Model\SynchrophasorProtocol.cs" />
97 </ItemGroup>98 </ItemGroup>
98 <ItemGroup>99 <ItemGroup>
99 <ProjectReference Include="..\..\GSF.Communication\GSF.Communication.csproj">100 <ProjectReference Include="..\..\GSF.Communication\GSF.Communication.csproj">
Source/Libraries/Adapters/PhasorWebUI/Views/AddSynchrophasorDevice.cshtml+861−134
@@ -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 refreshed1058 // 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 // Validations1388 // 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 else2972 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 function3057 // 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 hub3073 // 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 changes3235 // 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 loaded3292 // 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&nbsp;PMUConnection&nbsp;file..." accept=".PmuConnection,.xml,text/xml" data-toggle="tooltip" data-placement="right" class="form-control" style="width: 100%">3421 Connection&nbsp;Settings
3422 </label>
3423 <div class="pull-right">
3424 <span id="notConnected">Not&nbsp;Connected&nbsp;&nbsp;<span class="glyphicon glyphicon-ban-circle" style="color: #FF003B;"></span></span>&nbsp;&nbsp;
3425 <span id="attemptingConnection" style="display: none">Attempting&nbsp;Connection&nbsp;&nbsp;<span class="glyphicon glyphicon-refresh glyphicon-spin" style="color: yellow"></span></span>&nbsp;&nbsp;
3426 <span id="connected" style="display: none">Connected&nbsp;&nbsp;<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&nbsp;PMUConnection&nbsp;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>&nbsp;</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&nbsp;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>&nbsp;&nbsp;Save&nbsp;Device3522 <span class="glyphicon glyphicon-plus"></span>&nbsp;&nbsp;Save&nbsp;Device&nbsp;
2952 </button>3523 </button>
2953 </div>3524 </div>
2954 }3525 }
2955 else3526 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>&nbsp;Export&nbsp;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">&times;</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>&nbsp;Import&nbsp;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&nbsp;Acronym</th>3649 <th class="text-left">Device&nbsp;Acronym</th>
3049 <!-- /ko -->3650 <!-- /ko -->
3050 <th class="text-left">Phasor&nbsp;Label</th>3651 <th class="text-left">Phasor&nbsp;Label</th>
3051 <th class="text-center">Phase</th>3652 <th class="text-center">Phase</th>
3052 <th class="text-left">Nominal&nbsp;Voltage</th>3653 <th class="text-left">Nominal&nbsp;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()">&nbsp;</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()">&nbsp;</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()">&nbsp;</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()">&nbsp;</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()">&nbsp;</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>&nbsp;<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()">&nbsp;</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>&nbsp;<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&nbsp;Acronym</th>3717 <th class="text-left">Device&nbsp;Acronym</th>
3117 <!-- /ko -->3718 <!-- /ko -->
3118 <th class="text-left">Phasor&nbsp;Label</th>3719 <th class="text-left">Phasor&nbsp;Label</th>
3119 <th class="text-center">Phase</th>3720 <th class="text-center">Phase</th>
3120 <th class="text-left">Associated&nbsp;Voltage</th>3721 <th class="text-left">Associated&nbsp;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()">&nbsp;</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()">&nbsp;</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()">&nbsp;</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()">&nbsp;</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()">&nbsp;</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()">&nbsp;</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&nbsp;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()">&times;</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()">&times;</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()">&times;</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();

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