Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Transactions;
using System.Web.Http;
Expand All @@ -33,6 +32,7 @@
using GSF.Data.Model;
using GSF.Reflection;
using GSF.Web.Model;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using openXDA.Model;
using SystemCenter.Model;
Expand Down Expand Up @@ -65,6 +65,38 @@ public IHttpActionResult GetAssetLocations(int assetID)
return Unauthorized();
}

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

int recordsPerPage = Take ?? 50;

PagedResults results = new PagedResults();

results.RecordsPerPage = recordsPerPage;

string[] sortFields = { "Name", "LocationKey", "Latitude", "Longitude" };

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

using (AdoDataConnection connection = new AdoDataConnection(Connection))
{
RecordRestriction assetLocationRestriction = new RecordRestriction("ID IN (SELECT LocationID FROM AssetLocation WHERE AssetID = {0})", assetID);

int count = new TableOperations<Location>(connection).QueryRecordCount(assetLocationRestriction);

IEnumerable<Location> records = new TableOperations<Location>(connection).QueryRecords(postData.OrderBy, postData.Ascending, page + 1, recordsPerPage, assetLocationRestriction);

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

[HttpGet, Route("{assetID:int}/AssetLocations")]
public IHttpActionResult GetAssetLocationModels(int assetID)
{
Expand Down Expand Up @@ -123,16 +155,79 @@ public IHttpActionResult GetAssetMeters(int assetID)
return Unauthorized();
}

[HttpGet, Route("{assetID:int}/AssetConnections")]
public IHttpActionResult GetAssetAssetConnections(int assetID)
[HttpPost, Route("{assetID:int}/Meters/{page:int}")]
public IHttpActionResult GetAssetMetersPaged([FromBody] PostData postData, [FromUri] int assetID, [FromUri] int page)
{
if (GetRoles == string.Empty || User.IsInRole(GetRoles))
{
using (AdoDataConnection connection = new AdoDataConnection(Connection))
{
try
{
DataTable records = connection.RetrieveData(@"
int recordsPerPage = Take ?? 50;

string[] sortFields = { "AssetKey", "Name", "Make", "Model" };

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

RecordRestriction assetMeterRestriction = new RecordRestriction("ID IN (SELECT MeterID FROM MeterAsset WHERE AssetID = {0})", assetID);

int count = new TableOperations<Meter>(connection).QueryRecordCount(assetMeterRestriction);

IEnumerable<Meter> records = new TableOperations<Meter>(connection).QueryRecords(postData.OrderBy, postData.Ascending, page + 1, recordsPerPage, assetMeterRestriction);

return Ok(new PagedResults()
{
RecordsPerPage = recordsPerPage,
TotalRecords = count,
NumberOfPages = (count + recordsPerPage - 1) / recordsPerPage,
Data = JsonConvert.SerializeObject(records)
});
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
}
return Unauthorized();
}


[HttpPost, Route("{assetID:int}/AssetConnections/{page:int}")]
public IHttpActionResult GetAssetAssetConnections([FromBody] PostData postData, [FromUri] int assetID, [FromUri] int page)
{
if (GetRoles == string.Empty || User.IsInRole(GetRoles))
{
using (AdoDataConnection connection = new AdoDataConnection(Connection))
{
try
{
int recordsPerPage = Take ?? 50;

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

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

int count = connection.ExecuteScalar<int>(@"
SELECT
COUNT(AssetRelationship.ID)
FROM
AssetRelationship JOIN
AssetRelationshipType ON AssetRelationship.AssetRelationshipTypeID = AssetRelationshipType.ID JOIN
ASset ON Asset.ID = (
CASE
WHEN ParentID = {0} THEN AssetRelationship.ChildID
ELSE AssetRelationship.ParentID
END
)
WHERE
ParentID = {0} OR ChildID = {0}
", assetID);

DataTable records = connection.RetrieveData(@$"
SELECT
AssetRelationship.AssetRelationshipTypeID,
AssetRelationshipType.Name,
Expand All @@ -144,15 +239,25 @@ AssetRelationship JOIN
AssetRelationshipType ON AssetRelationship.AssetRelationshipTypeID = AssetRelationshipType.ID JOIN
ASset ON Asset.ID = (
CASE
WHEN ParentID = {0} THEN AssetRelationship.ChildID
WHEN ParentID = {{0}} THEN AssetRelationship.ChildID
ELSE AssetRelationship.ParentID
END
)
WHERE
ParentID = {0} OR ChildID = {0}
ParentID = {{0}} OR ChildID = {{0}}
ORDER BY {postData.OrderBy} {(postData.Ascending ? "ASC" : "DESC")}
OFFSET {page * recordsPerPage} ROWS FETCH NEXT {recordsPerPage} ROWS ONLY
", assetID);

return Ok(records);
PagedResults results = new PagedResults()
{
RecordsPerPage = recordsPerPage,
TotalRecords = count,
NumberOfPages = (count + recordsPerPage - 1) / recordsPerPage,
Data = JsonConvert.SerializeObject(records)
};

return Ok(results);
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,8 +307,8 @@ ORDER BY {orderByExpression}
}
}

[HttpGet, Route("{locationID:int}/Images")]
public IHttpActionResult GetImagesForLocation(int locationID)
[HttpGet, Route("{locationID:int}/Images/{page:int}")]
public IHttpActionResult GetImagesForLocation(int locationID, int page)
{
try
{
Expand All @@ -321,9 +321,18 @@ public IHttpActionResult GetImagesForLocation(int locationID)
if (path == null) return BadRequest("ImageDirectory.Path not set in settings table.");

if (Directory.Exists(Path.Combine(path, key)))
return Ok(Directory.GetFiles(Path.Combine(path, key)).Select(fp => new FileInfo(fp).Name));
{
IEnumerable<string> imagePaths = Directory.GetFiles(Path.Combine(path, key)).Select(fp => new FileInfo(fp).Name);
return Ok(PageImagePaths(imagePaths, page, Take ?? 50));
}
else
return Ok(new string[] { });
return Ok(new PagedResults()
{
Data = JsonConvert.SerializeObject(new string[0]),
TotalRecords = 0,
NumberOfPages = 0,
RecordsPerPage = Take ?? 50
});
}
else
return Unauthorized();
Expand All @@ -335,6 +344,23 @@ public IHttpActionResult GetImagesForLocation(int locationID)

}

public static PagedResults PageImagePaths(IEnumerable<string> imagePaths, int page, int recordsPerPage)
{
int totalImages = imagePaths.Count();

IEnumerable<string> pagedImagePaths = imagePaths
.Skip((page) * recordsPerPage)
.Take(recordsPerPage);

return new PagedResults()
{
Data = JsonConvert.SerializeObject(pagedImagePaths),
TotalRecords = totalImages,
NumberOfPages = (totalImages + recordsPerPage - 1) / recordsPerPage,
RecordsPerPage = recordsPerPage
};
}

[HttpGet, Route("{locationID:int}/Images/{file}")]
public HttpResponseMessage GetImageForLocation(int locationID, string file)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

import * as React from 'react';
import _ from 'lodash';
import { Table, Column } from '@gpa-gemstone/react-table';
import { Table, Column, Paging } from '@gpa-gemstone/react-table';
import { useNavigate } from "react-router-dom";
import { LoadingIcon, Modal, Search, ServerErrorIcon } from '@gpa-gemstone/react-interactive';
import { ToolTip } from '@gpa-gemstone/react-forms';
Expand Down Expand Up @@ -53,6 +53,11 @@ function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number
const [selectedTypeID, setSelectedtypeID] = React.useState<number>(0);
const [localAssets, setLocalAssets] = React.useState<Array<OpenXDA.Types.Asset>>([]);

const [page, setPage] = React.useState<number>(0);
const [totalPages, setTotalPages] = React.useState<number>(0);
const [totalRecords, setTotalRecords] = React.useState<number>(0);
const [recordsPerPage, setRecordsPerPage] = React.useState<number>(0);

const [sortKey, setSortKey] = React.useState<string>('AssetName');
const [ascending, setAscending] = React.useState<boolean>(true);
const [showModal, setShowModal] = React.useState<boolean>(false);
Expand All @@ -65,9 +70,31 @@ function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number
const roles = useAppSelector(SelectRoles);

React.useEffect(() => {
let handle = getAssetConnections();
return () => { if (handle != null || handle.abort != null) handle.abort();}
}, [props.ID, trigger])
setStatus('loading');
let handle = $.ajax({
type: "POST",
url: `${homePath}api/OpenXDA/Asset/${props.ID}/AssetConnections/${page}`,
contentType: "application/json; charset=utf-8",
dataType: 'json',
cache: true,
async: true,
data: JSON.stringify({ OrderBy: sortKey, Ascending: ascending })
})

handle.done((d) => {
setAssetConnections(JSON.parse(d.Data as unknown as string));
setTotalPages(d.NumberOfPages);
setTotalRecords(d.TotalRecords);
setRecordsPerPage(d.RecordsPerPage);
if (page >= d.NumberOfPages)
setPage(Math.max(d.NumberOfPages - 1, 0));
setStatus('idle');
})

handle.fail(() => setStatus('error'));

return () => { if (handle != null || handle.abort != null) handle.abort(); }
}, [props.ID, trigger, page, sortKey, ascending])

React.useEffect(() => {
if (props.ID > 0) {
Expand Down Expand Up @@ -108,25 +135,6 @@ function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number
setSelectedAssetID(localAssets[0].ID)
}, [localAssets])

function getAssetConnections(): JQuery.jqXHR<OpenXDA.Types.AssetConnection> {
setStatus('loading');
return $.ajax({
type: "GET",
url: `${homePath}api/OpenXDA/Asset/${props.ID}/AssetConnections`,
contentType: "application/json; charset=utf-8",
dataType: 'json',
cache: true,
async: true
}).done((d) => {
setStatus('idle')
const sortedConnections = sortData(sortKey, ascending, d);
setAssetConnections(sortedConnections)
}).fail(() => setStatus('error'));
}

function sortData(key: string, ascending: boolean, data: AssetConnection[]) {
return _.orderBy(data, [key], [(ascending ? "asc" : "desc")]);
}

function getAssets(): JQuery.jqXHR<string> {
const filter = [
Expand Down Expand Up @@ -238,28 +246,24 @@ function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number
<h4>Connections:</h4>
</div>
</div>
<div className="row">
<div className="col">
<p style={{ marginTop: 2, marginBottom: 2 }}>
{`Displaying Asset Connection(s) ${totalRecords > 0 ? (recordsPerPage * page + 1) : 0} - ${recordsPerPage * page + assetConnections.length} out of ${totalRecords}`}
</p>
</div>
<div className="card-body" style={{ flex: 1, overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
</div>
</div>
<div className="card-body d-flex flex-column" style={{ flex: 1, overflow: 'hidden' }}>
<div className="row d-flex flex-column" style={{ flex: 1, overflow: 'hidden' }}>
<Table<AssetConnection>
TableClass="table table-hover"
Data={assetConnections}
SortKey={sortKey}
Ascending={ascending}
OnSort={(d) => {
if (d.colKey === "DeleteButton")
return;

if (d.colKey === sortKey) {
setAscending(!ascending);
const ordered = _.orderBy(assetConnections, [d.colKey], [(!ascending ? "asc" : "desc")]);
setAssetConnections(ordered);
}
else {
setAscending(true);
setSortKey(d.colKey);
const ordered = _.orderBy(assetConnections, [d.colKey], ["asc"]);
setAssetConnections(ordered);
}
if (d.colKey === sortKey) setAscending(a => !a);
else setSortKey(d.colField);
}}
TableStyle={{ height: '100%' }}
TheadStyle={{ fontSize: 'smaller' }}
Expand Down Expand Up @@ -307,6 +311,16 @@ function AssetConnectionWindow(props: { Name: string, ID: number, TypeID: number
> <p></p>
</Column>
</Table>
</div>
<div className="row">
<div className="col">
<Paging
Current={1}
SetPage={() => { }}
Total={1}
/>
</div>
</div>
</div>
<div className="card-footer">
<div className="btn-group mr-2">
Expand Down
Loading