The sqa-common
JS library is a collection of utilities to support the clients of the SQA api.
It is provided as a node module published on the public npm registry.
Add the following dependency to package.json
:
"@3e/sqa-common": "^1.1.0"
and import the module where relevant:
import sqa_common from '@3e/sqa-common';
Reference documentation for the utility functions provided by the sqa-common
library.
Example payload returned by the /views
endpoint:
[{
"category": "GENERAL",
"sqid": "view-sqid",
"ref": "app/views/view-sqid",
"name": "Plant level template view",
"type": "CHART",
"type_option": "TIME",
"owner_type": "SYNAPTIQ",
"label": "Plant level template view",
"id": 10001,
"parameters": [
{
"id": "Period",
"class": "Parameter",
"parameter": {
"class": "Period",
"label": "Period",
"initial": "last-12-months"
}
},
{
"id": "Granularity",
"class": "Parameter",
"parameter": {
"class": "Granularity",
"label": "Granularity",
"initial": "1-months"
}
},
{
"id": "plants",
"class": "Parameter",
"parameter": {
"class": "ObjectRef",
"label": "Plant",
"labelKey": "assetType.PLANT.label",
"constraint": {
"object_category": "plants",
"include_obsolete": false
}
}
}
]
},...]
In the following paragraph we will refer to the json payload in above as viewsPayload
.
Given the parameter
field from the payload returned by a call to /views
, viewVariablesRefCategories
returns
an array of view asset ref categories.
For example:
sqa_common.sqa.common.viewVariablesRefCategories(viewsPayload[0].parameters);
returns ["plants"]
.
IMPORTANT: this is very alpha and subject to change.
The sqa common library is able to create visualizations (specifically, highcharts) and populate them with data.
To get a simple chart working, use the following steps.
The library needs to be able to call the api so it can access /logical_devices
, /indicators
(to construct the highcharts spec) and /data
(to populate it)
Create an api functon that will call an endpoint on our api with authentication, and call the callback with the json response.
Then set the api fn using
sqa.common.setApiFn(apiFn)
This can be done (for example) by calling /views/<id>
from the api
This is necessary before the view can be rendered.
\\ to set the granularity (setView here is coming from the react hook)
sqa.common.setView(sqa_common.setGranularityParameter(view, "15-minutes"))
\\ to set the period
sqa.common.setView(sqa_common.setPeriodParameter(view, "last-month"))
(see section 9 for more on period handling)
\\ to set an object parameter by id
sqa.common.setView(sqa_common.setObjectParameter(view, "plants", "plants/P0237"))
sqa.common.getVisualization.then((visualization) => {
sqa.common.populateVisualization(visualization, (visualization) => {
console.log(visualization);
})
})
let cancel = sqa.common.populateVisualization(visualization, (visualization) => {
console.log(visualization);
})
cancel()
The function sqa.common.newEmptyView(options)
will create a new view ready to add objects and indicators to.
The options available are currently as follows:
{
granularity: "15-minutes",
period: "last-24-hours",
view-type: "time-chart",
description: "description",
label: "label"
}
The first dropdown is the list of available levels. This list is static, and stored in the variable sqa.common.Levels
It has a format like:
[{
"value": "site",
"label": "Site"
},
{
"value": "ac-node",
"label": "AC Node"
}]
The list of available containers depends on the selected level, and can be retrieved using the function sqa.common.getContainers(value)
where value is the value of the selected level.
It has a format like:
[{
"value": "all-sites",
"label": "All Sites"
},
{
"value": "plants/P0397",
"label": "BE Brugge"
}]
Call /logical_devices?ld_type=&container=&envelope=true
Note: the logical devices is paged so to ensure you get all the objects, navigate through the pages using the after
cursor. Using limit=100000
is not guaranteed to work as we may place a maximum on the limit property.
The endpoint indicators?level=<selectedLevel>&locale=<locale>
will return the full indicator information with label, unit, key etc. This can be cached.
The endpoint ld_indicators?object=<selectedObject>&object=<anotherSelectedObject>
will return the indicator keys that can be applied to the selected objects. This can be then combined with the full indicator information from /indicators
to provide the label and key for the applicable indidators.
The function sqa.common.addEnumDataset(view, objects, indicators, options}
will return a new view with the dataset added.
Objects: is a list of object refs e.g. ["plants/P0237","plants/P0350"]
Indicators: is a list of indicator keys e.g. ["irradiance"]
Following the designs, this should be called once per indicator so that the graphical representation and options for each indicator can be independently modified.
Use the function sqa.common.getTemplates(<view>, <spec>)
where spec
is an object like:
{
selectedLevel:"ac-nodes",
selectedContainer:"plants/P0237"
}
The function needs to call the api to get the labels for logical devices, so will return a promise. The promise will resolve to a list of templates like so:
[{:value "select-one",
:label "Selectable site"}
{:value "select-all",
:label "All sites"}]
With the templates, you only need to call `/indicators?level=&locale=
The function sqa.common.addTemplateDataset(view, spec, indicators, options}
will return a new view with the dataset added.
Spec: is an object like:
{
selectedLevel:"site",
selectedTemplate:"select-one",
selectedContainer: "site_groups/GRdemo.7"
}
Indicators: is a list of indicator keys e.g. ["irradiance"]
Following the designs, this should be called once per indicator so that the graphical representation and options for each indicator can be independently modified.
The function sqa.common.getDatasets(<view>)
will return the datasets available for the view. It needs to call the api to resolve the indicators and logical devices so will return a promise, which resolves to an array with one entry per dataset, like the following:
[{id:"bb380abd-ea20-4239-b3dc-077cd3308c77",
indicators:
[{level: "sites",
label: "Irradiance",
unit: "W/m2",
unit_key: "W_M2",
description: "Irradiance in the plane of the array(s) calculated using the same configuration and data as <i>Irradiation</i>.",
key: "irradiance"}],
objectSetLabel: "All sites",
objectSet: {template: "select-all", level: "site"},
representation: "line"}]
For enum datasets, the object set will also contain the resolved objects, which can be used to populate the expanded view, and the additional counter label.
[{id:"bb380abd-ea20-4239-b3dc-077cd3308c77",
indicators:
[{level: "sites",
label: "Irradiance",
unit: "W/m2",
unit_key: "W_M2",
description: "Irradiance in the plane of the array(s) calculated using the same configuration and data as <i>Irradiation</i>.",
key: "irradiance"}],
objectSetLabel: "BE Brugge",
objectSet: {
template: "enum",
level: "site",
objects: [{
name: "BE Brugge",
level: "site",
ref: "sites/P0237",
type: "value"
}]
},
representation: "line"}]
The basket will return an id for each dataset, which can be used as a handle to remove or modify it.
To remove:
sqa.common.removeDataset(<view>,<id>)
To update:
sqa.common.setDatasetOptions(<view>,<id>,<options>)
Currently the only available option is:
{
graphicalRepresentation:"line"
}
The list of representations is available from sqa.common.Representations
The variable sqa.common.ViewTypes
lists the available view types, currently as follows:
- Time chart
- Object heatmap
- Scatter chart
- Time table
- Object table
- Object chart
You can use the display
key in the visualization spec to determine whether to show a highcharts view or a data table.
The tables (object table and time table) return a spec as follows:
{
display:"table",
header: ["Object" "Irradiance" "Power AC"],
rows: [["BE Brugge" 100 200],
["BE Ghent" 300 400]
}
The view title and description can be retrieved with:
sqa.common.getTitle(<view>)
sqa.common.getDescription(<view>)
To update:
sqa.common.setTitle(<view>, <value>)
sqa.common.setDescription(<view>, <value>)
Some views will only work in certain cases.
The object heatmap requires exactly one indicator The scatter chart requires exactly two indicators, and the same object set for each indicator.
If the requirements are not met, getting the visualization will return a message spec:
{
display:"message",
value: "scatter-chart-object-sets-unmatched",
label: "Object sets must match for each indicator in a scatter chart"
}
The UI needs to provide a period dropdown, a granularity dropdown and custom period selector.
The options for periods are available from the api via /periods
, and the granularities from /granularities
.
Each period provides a default-granularity
which should be picked when it is selected, and a range of granularities which should be used for the granularity dropdown.
To set the values for the date picker you can use the method getPeriodParameterWithRange(view)
.
This returns an object with the following values
{
period: "last-24-hours",
start: "2021-07-12T23:23:00.000",
end: "2021-07-13T23:23:00.000"
}
The start and end datetimes are strings, not datetime objects, and should be passed to the datetimepacker as such, without intermediate processing or parsing.
This is because we want to do all our calculations in the user's timezone, not the browser timezone.
To set the start and end date for the period, use setPeriodStart(view,start)
and setPeriodEnd(view,end)
. The period will automatically be changed to a custom period.
If you set the period to custom
, it will default to a custom range with the currently selected start and end times.
For timezone support to work in highcharts, you need to require the library moment-timezone
, and initialize it with MomentTimeZone()
.
The library can operate in different timezones and timezone modes.
- To get the list of timezones, use the variable
Timezones
- To set the user timezone, call
setUserTimezone(timezone)
- To get the list of timezone modes, use the variable
DataTimezoneModes
- To set the timezone mode, call
setDataTimezoneMode(mode)
These affect how the time ranges are calculated and how the charts and tables are displayed.
The user's preferred timezone mode and timezone are available through the api /user
endpoint.