Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
120 commits
Select commit Hold shift + click to select a range
a825668
paginate substation images
nbeatty-gpa May 11, 2026
dd8cb66
reset to first page on sort
nbeatty-gpa May 19, 2026
4b86907
reset to first page on sort
nbeatty-gpa May 19, 2026
2591eed
reset to first page on sort
nbeatty-gpa May 19, 2026
6513500
reset to first page on sort
nbeatty-gpa May 19, 2026
7ba0a60
reset to first page on sort
nbeatty-gpa May 19, 2026
1791ceb
fix styling for paging
nbeatty-gpa May 22, 2026
2d8505f
paginate value list group item
nbeatty-gpa May 26, 2026
e1d9eda
page asset ConnectedChannels endpoint
nbeatty-gpa May 27, 2026
c90f1b3
page Asset Channels table
nbeatty-gpa May 27, 2026
a7673e1
paginate tables tab of external db
nbeatty-gpa May 28, 2026
6276f59
Fix error filtering users by additional fields
nbeatty-gpa Jun 5, 2026
cd77f84
Refactor byUser page from slices to local state with Generic Controller
nbeatty-gpa Jun 5, 2026
ce5d297
clean up byUser
nbeatty-gpa Jun 5, 2026
1a759b5
move ByUserGroup from GenericSlice to GenericController
nbeatty-gpa Jun 5, 2026
f9b21b7
paginate GroupUsers
nbeatty-gpa Jun 8, 2026
2b9e792
paginate ExternalDBTableFields
nbeatty-gpa Jun 8, 2026
6761faf
paginate byAssetGroup
nbeatty-gpa Jun 9, 2026
e6f1cb4
paginate assetGroupSubGroups
nbeatty-gpa Jun 9, 2026
2cb8765
paginate assetGroupsAssets
nbeatty-gpa Jun 9, 2026
3b8507e
paginate assetGroupMeters
nbeatty-gpa Jun 9, 2026
ef53931
paginate assetSubstations
nbeatty-gpa Jun 9, 2026
6905653
paginate assetMeters
nbeatty-gpa Jun 9, 2026
6fdee61
paginate assetConnections
nbeatty-gpa Jun 9, 2026
9e26f19
paginate customerMeter
nbeatty-gpa Jun 9, 2026
911f692
paginate customerAsset
nbeatty-gpa Jun 9, 2026
286d76a
paginate MeterTrendChannel
nbeatty-gpa Jun 9, 2026
35f6a87
remove accidental comment
nbeatty-gpa Jun 9, 2026
c88a43a
paginate meterChannelScaling table
nbeatty-gpa Jun 9, 2026
cf87455
paginate meterEventChannels
nbeatty-gpa Jun 10, 2026
0e07c4f
Paginate remoteXDA assets
nbeatty-gpa Jun 10, 2026
b073fb7
Paginate remoteXDA meters
nbeatty-gpa Jun 10, 2026
d890346
simplify settings sort
nbeatty-gpa Jun 10, 2026
203b6e1
clean up user and userGroup
nbeatty-gpa Jun 10, 2026
8f06df6
fix generic controller typing
nbeatty-gpa Jun 10, 2026
82a76ee
fix dependency array
nbeatty-gpa Jun 10, 2026
38df9db
use stricter typing in EventChannelSlice
nbeatty-gpa Jun 10, 2026
d4d1b2c
changes to store typing
nbeatty-gpa Jun 10, 2026
15564f3
cleanup import
nbeatty-gpa Jun 10, 2026
2c552fe
fix typing
nbeatty-gpa Jun 10, 2026
4648845
refresh on changed
nbeatty-gpa Jun 10, 2026
22d5847
fix broken Additional Field selector in ExternalDBTable
nbeatty-gpa Jun 11, 2026
566f49b
fix decimal page
nbeatty-gpa Jun 15, 2026
d540c39
fix grammar mistakes in ExternalDBUpdate
nbeatty-gpa Jun 15, 2026
7782fe4
do not error out completely on uncompleted search
nbeatty-gpa Jun 15, 2026
8060133
fix utf-A typo
nbeatty-gpa Jun 19, 2026
f648d6d
remove companySlice from merged store
nbeatty-gpa Jun 19, 2026
ac24c69
abort correct handle
nbeatty-gpa Jun 19, 2026
006da5f
return empty PagedResults if no images found for substation
nbeatty-gpa Jun 19, 2026
c9da134
include 'loading' status during paged search
nbeatty-gpa Jun 19, 2026
35cee7b
filter customer assets on customer ID
nbeatty-gpa Jun 19, 2026
351caa7
add filters to value list
nbeatty-gpa Jun 19, 2026
ab9287e
fix effect cleanup logic
nbeatty-gpa Jun 22, 2026
141bf75
check OrderBy values
nbeatty-gpa Jun 22, 2026
a4eeb99
correct use of hasPermissions
nbeatty-gpa Jun 23, 2026
dd48bbf
clean up substation images
nbeatty-gpa Jun 23, 2026
cbde198
move valuelistitem away from slices
nbeatty-gpa Jun 23, 2026
4ba46b4
remove unused imports
nbeatty-gpa Jun 26, 2026
efc6e9b
fix use effect returns
nbeatty-gpa Jun 26, 2026
ef50240
cleanup valuelistitem
nbeatty-gpa Jun 26, 2026
44b6f42
changes to store typing
nbeatty-gpa Jun 10, 2026
a3cbd68
fix cleanup function
nbeatty-gpa Jun 29, 2026
deefb0e
move usergroups away from slices
nbeatty-gpa Jun 29, 2026
452adf9
remove DataFileSlice
nbeatty-gpa Jun 29, 2026
918e91e
clean up value list items table
nbeatty-gpa Jul 2, 2026
e65ad0b
fix refresh on change
nbeatty-gpa Jul 2, 2026
fb9492f
cleanup
nbeatty-gpa Jul 2, 2026
76e08b6
move CustomerAsset away from slices
nbeatty-gpa Jul 2, 2026
760776a
move CustomerMeter away from slices
nbeatty-gpa Jul 2, 2026
2e0365d
move ExternalDBTableFields away from slices
nbeatty-gpa Jul 6, 2026
ed04ae6
revert callback pattern
nbeatty-gpa Jul 6, 2026
f42fd15
cleanup user
nbeatty-gpa Jul 6, 2026
f9bec20
cleanup extdb tables
nbeatty-gpa Jul 6, 2026
e6515e4
remove dead callback
nbeatty-gpa Jul 6, 2026
f88c362
remove dead callback
nbeatty-gpa Jul 6, 2026
301142e
fix additional field window pagination
nbeatty-gpa Jul 10, 2026
9a9ad9b
use AssetGroupController for searches
nbeatty-gpa Jul 17, 2026
7de633e
move byMeter away from slice
nbeatty-gpa Jul 17, 2026
6575dba
move location away from slice
nbeatty-gpa Jul 17, 2026
c99c5aa
cleanup
nbeatty-gpa Jul 17, 2026
b9f0fe1
search for and fix 'handle.abort == null' cleanup function bug
nbeatty-gpa Jul 17, 2026
0252bcd
make more severe delete warning, wait for delete action to return bef…
nbeatty-gpa Jul 20, 2026
02ab11d
fix pagination and string comparison
nbeatty-gpa Jul 20, 2026
423cada
allow orderBy to be case-insensitive and simplify ordering
nbeatty-gpa Jul 20, 2026
48e63dc
add status display to TaskRunner page, and set to loading during delete
nbeatty-gpa Jul 20, 2026
b686a00
move paging into sql and use whitelisting for orderBy
nbeatty-gpa Jul 20, 2026
02f15c3
simplify sorting
nbeatty-gpa Jul 20, 2026
c9b7e0a
simplify paged asset-asset connection endpoint, and return BadRequest…
nbeatty-gpa Jul 21, 2026
9ec92f4
add missing semicolon
nbeatty-gpa Jul 21, 2026
637a7ad
clean up connected channels endpoint
nbeatty-gpa Jul 21, 2026
4ff2cfc
clean up location image endpoint
nbeatty-gpa Jul 21, 2026
5ce6243
remove stray semicolon
nbeatty-gpa Jul 21, 2026
41a93c5
cleanup UserGroup User paged endpoint
nbeatty-gpa Jul 21, 2026
1a211fa
move byAdditionalField to controller
nbeatty-gpa Jul 21, 2026
10c6c29
cleanup asset channels
nbeatty-gpa Jul 21, 2026
4fefee3
correct status initiation
nbeatty-gpa Jul 21, 2026
6674f38
remove extraneous import
nbeatty-gpa Jul 21, 2026
dd813f0
Add Result Note
nbeatty-gpa Jul 21, 2026
fe1e165
cleanup
nbeatty-gpa Jul 21, 2026
5ee33c5
clean up asset connection
nbeatty-gpa Jul 21, 2026
14c7d55
clean up asset connection
nbeatty-gpa Jul 21, 2026
3a3056e
cleanup assetChannels
nbeatty-gpa Jul 21, 2026
708c608
reorganize imports
nbeatty-gpa Jul 21, 2026
7e096c0
cleanup ByAdditionalField and paging snippet
nbeatty-gpa Jul 21, 2026
84b4618
add doc
nbeatty-gpa Jul 21, 2026
824c184
cleanup assetLocation
nbeatty-gpa Jul 21, 2026
bcec313
cleanup assetMeter
nbeatty-gpa Jul 22, 2026
a14dcdd
correct loading text placement
nbeatty-gpa Jul 22, 2026
d9dd1c7
cleanup AssetGroup Assets tab
nbeatty-gpa Jul 22, 2026
f71e9fe
cleanup AssetGroup ASsetGroup tab
nbeatty-gpa Jul 22, 2026
c1b1e3b
cleanup Asset Group meters tab
nbeatty-gpa Jul 22, 2026
6cf4be4
cleanup Remote Asset Tab
nbeatty-gpa Jul 22, 2026
332aeab
correct result note
nbeatty-gpa Jul 22, 2026
057029d
move away from slices and use states to track network requests
nbeatty-gpa Jul 23, 2026
ac6d752
cleanup customer assets and meters
nbeatty-gpa Jul 23, 2026
4f8ee84
cleanup externalDBTable and Fields
nbeatty-gpa Jul 23, 2026
f4dfbf4
additional cleaning value list group item
nbeatty-gpa Jul 23, 2026
2fb4172
cleanup users and groups
nbeatty-gpa Jul 23, 2026
662110f
clean up location image
nbeatty-gpa Jul 23, 2026
7b66d02
cleanup by pages
nbeatty-gpa Jul 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,29 @@
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net.Http;
using System.Transactions;
using System.Reflection;
using System.Web.Http;
using GSF.Data;
using GSF.Data.Model;
using GSF.Web.Model;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using openXDA.Model;
using SystemCenter.Model;

namespace SystemCenter.Controllers.OpenXDA
{
[RoutePrefix("api/OpenXDA/AssetGroup")]
public class OpenXDAAssetGroupController : ModelController<AssetGroupView>
{
private class extendedAssetGroupView: AssetGroupView
private class extendedAssetGroupView : AssetGroupView
{
public List<int> MeterList { get; set; }
public List<int> AssetList { get; set; }
public List<int> UserList { get; set; }
public List<int> AssetGroupList { get; set; }

}

[HttpGet, Route("{assetGroupID:int}/Assets")]
public IHttpActionResult GetAssets(int assetGroupID)
{
Expand Down Expand Up @@ -93,7 +92,7 @@ GROUP BY
AssetAssetGroup.AssetGroupID
HAVING AssetAssetGroup.AssetGroupID = {0}";

return Ok(connection.RetrieveData(sql,assetGroupID));
return Ok(connection.RetrieveData(sql, assetGroupID));
}
catch (Exception ex)
{
Expand All @@ -105,6 +104,74 @@ GROUP BY
return Unauthorized();
}

[HttpPost, Route("{assetGroupID:int}/Assets/{page:int}")]
public IHttpActionResult GetAssetsPaged([FromBody] PostData postData, [FromUri] int assetGroupID, [FromUri] int page)
{

if (!GetAuthCheck())
return Unauthorized();

int recordsPerPage = Take ?? 50;

PagedResults results = new PagedResults();
results.RecordsPerPage = recordsPerPage;

string[] sortFields = { "AssetName", "AssetKey", "AssetType" };

if (!sortFields.Any(f => f.Equals(postData.OrderBy, StringComparison.OrdinalIgnoreCase)))
{
return BadRequest($"{postData.OrderBy} is not a valid search field.");
}

using (AdoDataConnection connection = new AdoDataConnection(Connection))
{
string allSql = $@"SELECT
DISTINCT
Asset.ID,
AssetAssetGroup.AssetGroupID,
Asset.AssetKey,
Asset.AssetName,
Asset.VoltageKV,
AssetType.Name as AssetType,
COUNT(DISTINCT Meter.ID) as Meters,
COUNT(DISTINCT Location.ID) as Locations
FROM
Asset Join
AssetType ON Asset.AssetTypeID = AssetType.ID LEFT JOIN
MeterAsset ON MeterAsset.AssetID = Asset.ID LEFT JOIN
Meter ON MeterAsset.MeterID = Meter.ID LEFT JOIN
AssetLocation ON AssetLocation.AssetID = Asset.ID LEFT JOIN
Location ON AssetLocation.LocationID = Location.ID LEFT JOIN
AssetAssetGroup ON Asset.ID = AssetAssetGroup.AssetID
GROUP BY
Asset.ID,
Asset.AssetKey,
Asset.AssetName,
Asset.VoltageKV,
AssetType.Name,
AssetAssetGroup.AssetGroupID
HAVING AssetAssetGroup.AssetGroupID = {{0}}
";
Comment thread
nbeatty-gpa marked this conversation as resolved.

DataTable allRecords = connection.RetrieveData(allSql, assetGroupID);

string sql = $@"{allSql}
ORDER BY {postData.OrderBy} {(postData.Ascending ? "ASC" : "DESC")}
OFFSET {page * recordsPerPage} ROWS FETCH NEXT {recordsPerPage} ROWS ONLY
";

DataTable records = connection.RetrieveData(sql, assetGroupID);

int totalRecords = allRecords.Rows.Count;

results.TotalRecords = totalRecords;
results.NumberOfPages = (totalRecords + recordsPerPage - 1) / recordsPerPage;
results.Data = JsonConvert.SerializeObject(records);
}

return Ok(results);
}

[HttpPost, Route("{assetGroupID:int}/AddAssets")]
public IHttpActionResult AddAssets(int assetGroupID, [FromBody] IEnumerable<int> assets)
{
Expand All @@ -119,7 +186,7 @@ public IHttpActionResult AddAssets(int assetGroupID, [FromBody] IEnumerable<int>
{
int n = connection.ExecuteScalar<int>("Select Count(ID) FROM AssetAssetGroup WHERE AssetID = {0} AND AssetGroupID = {1}", assetID, assetGroupID);
if (n == 0)
assetassetGroupTbl.AddNewRecord( new AssetAssetGroup() { AssetGroupID = assetGroupID, AssetID = assetID});
assetassetGroupTbl.AddNewRecord(new AssetAssetGroup() { AssetGroupID = assetGroupID, AssetID = assetID });
}
return Ok(1);
}
Expand Down Expand Up @@ -192,8 +259,8 @@ GROUP BY
Location.Name,
MeterAssetGroup.AssetGroupID
HAVING MeterAssetGroup.AssetGroupID = {0}";
return Ok(connection.RetrieveData(sql,assetGroupID));

return Ok(connection.RetrieveData(sql, assetGroupID));
}
catch (Exception ex)
{
Expand All @@ -205,6 +272,71 @@ GROUP BY
return Unauthorized();
}

[HttpPost, Route("{assetGroupID:int}/Meters/{page:int}")]
public IHttpActionResult GetMetersPaged([FromBody] PostData postData, [FromUri] int assetGroupID, [FromUri] int page)
{
if (!GetAuthCheck())
return Unauthorized();

int recordsPerPage = Take ?? 50;

PagedResults results = new PagedResults();
results.RecordsPerPage = recordsPerPage;

string[] sortFields = { "Name", "Location" };

if (!sortFields.Any(f => f.Equals(postData.OrderBy, StringComparison.OrdinalIgnoreCase)))
{
return BadRequest($"{postData.OrderBy} is not a valid search field.");
}

using (AdoDataConnection connection = new AdoDataConnection(Connection))
{
string allSql = $@"SELECT DISTINCT
Meter.ID,
MeterAssetGroup.AssetGroupID,
Meter.AssetKey,
Meter.Name,
Meter.Make,
Meter.Model,
Location.Name as Location,
COUNT(DISTINCT MeterAsset.AssetID) as MappedAssets
FROM
Meter LEFT JOIN
Location ON Meter.LocationID = Location.ID LEFT JOIN
MeterAsset ON Meter.ID = MeterAsset.MeterID LEFT JOIN
Asset ON MeterAsset.AssetID = Asset.ID LEFT JOIN
MeterAssetGroup ON Meter.ID = MeterAssetGroup.MeterID
GROUP BY
Meter.ID,
Meter.AssetKey,
Meter.Name,
Meter.Make,
Meter.Model,
Location.Name,
MeterAssetGroup.AssetGroupID
HAVING MeterAssetGroup.AssetGroupID = {{0}}
";
Comment thread
nbeatty-gpa marked this conversation as resolved.

DataTable allRecords = connection.RetrieveData(allSql, assetGroupID);

string orderedPagedSql = $@"{allSql}
ORDER BY {postData.OrderBy} {(postData.Ascending ? "ASC" : "DESC")}
OFFSET {page * recordsPerPage} ROWS FETCH NEXT {recordsPerPage} ROWS ONLY;
";

DataTable records = connection.RetrieveData(orderedPagedSql, assetGroupID);

int totalRecords = allRecords.Rows.Count;

results.TotalRecords = totalRecords;
results.NumberOfPages = (totalRecords + recordsPerPage - 1) / recordsPerPage;
results.Data = JsonConvert.SerializeObject(records);
}

return Ok(results);
}

[HttpPost, Route("{assetGroupID:int}/AddMeters")]
public IHttpActionResult AddMeters(int assetGroupID, [FromBody] IEnumerable<int> meters)
{
Expand Down Expand Up @@ -293,7 +425,6 @@ public IHttpActionResult GetSubGroups(int assetGroupID)
try
{
IEnumerable<AssetGroupView> records = new TableOperations<AssetGroupView>(connection).QueryRecordsWhere("ID in (SELECT ChildAssetGroupID FROM AssetGroupAssetGroupView WHERE ParentAssetGroupID = {0})", assetGroupID);

return Ok(records);
}
catch (Exception ex)
Expand All @@ -306,6 +437,34 @@ public IHttpActionResult GetSubGroups(int assetGroupID)
return Unauthorized();
}

[HttpPost, Route("{assetGroupID:int}/AssetGroups/{page:int}")]
public IHttpActionResult GetSubGroupsPaged([FromBody] PostData postData, [FromUri] int assetGroupID, [FromUri] int page)
{
if (!GetAuthCheck())
return Unauthorized();

int recordsPerPage = Take ?? 50;

PagedResults results = new PagedResults();
results.RecordsPerPage = recordsPerPage;

String[] sortFields = { "AssetGroups", "Meters", "Assets", "Name" };

if (!sortFields.Any(f => f.Equals(postData.OrderBy, StringComparison.OrdinalIgnoreCase)))
return BadRequest($"{postData.OrderBy} is not a valid search field.");

using (AdoDataConnection connection = new AdoDataConnection(Connection))
{
IEnumerable<AssetGroupView> records = new TableOperations<AssetGroupView>(connection).QueryRecords($"{postData.OrderBy} {(postData.Ascending ? "ASC" : "DESC")}", new RecordRestriction("ID in (SELECT ChildAssetGroupID FROM AssetGroupAssetGroupView WHERE ParentAssetGroupID = {0})", assetGroupID));

results.TotalRecords = records.Count();
results.NumberOfPages = (records.Count() + recordsPerPage - 1) / recordsPerPage;
results.Data = JsonConvert.SerializeObject(records.Skip(page * recordsPerPage).Take(recordsPerPage));
}

return Ok(results);
}

[HttpPost, Route("{assetGroupID:int}/AddAssetGroups")]
public IHttpActionResult AddSubgroups(int assetGroupID, [FromBody] IEnumerable<int> subGroups)
{
Expand Down Expand Up @@ -368,7 +527,7 @@ public override IHttpActionResult Delete(AssetGroupView record)

using (AdoDataConnection connection = new AdoDataConnection(Connection))
{

int id = record.ID;
int result = connection.ExecuteNonQuery($"EXEC UniversalCascadeDelete 'AssetGroup', 'ID = {id}'");
return Ok(result);
Expand Down Expand Up @@ -396,14 +555,14 @@ public override IHttpActionResult Post([FromBody] JObject record)
{

extendedAssetGroupView newRecord = record.ToObject<extendedAssetGroupView>();
AssetGroup newGroup = new AssetGroup() { ID= newRecord.ID, DisplayDashboard = newRecord.DisplayDashboard, Name= newRecord.Name, DisplayEmail=newRecord.DisplayEmail };
AssetGroup newGroup = new AssetGroup() { ID = newRecord.ID, DisplayDashboard = newRecord.DisplayDashboard, Name = newRecord.Name, DisplayEmail = newRecord.DisplayEmail };

int result = new TableOperations<AssetGroup>(connection).AddNewRecord(newRecord);

return Ok(new TableOperations<AssetGroupView>(connection).QueryRecordWhere("Name = {0}", newRecord.Name));


}

}
}
else
{
Expand Down
Loading