diff options
author | Herbert Eiselt <herbert.eiselt@highstreet-technologies.com> | 2019-06-07 17:55:16 +0200 |
---|---|---|
committer | Herbert Eiselt <herbert.eiselt@highstreet-technologies.com> | 2019-06-07 17:58:18 +0200 |
commit | 47fc603b864b52a70157515f29ec741dd9192f3a (patch) | |
tree | 12e8cb752efc4b2c92e35325780ac4242b5d791d /sdnr/wt | |
parent | d93e6a996e60fb6abce9a870cef6b2d57bfa70fd (diff) |
SDNR align ODLUX
Add missing chart view to UX Performance app
Issue-ID: SDNC-790
Signed-off-by: Herbert Eiselt <herbert.eiselt@highstreet-technologies.com>
Change-Id: I6f5af1e01d2246927b8d05f826f629c7dd5f59a5
Signed-off-by: Herbert Eiselt <herbert.eiselt@highstreet-technologies.com>
Diffstat (limited to 'sdnr/wt')
36 files changed, 1271 insertions, 575 deletions
diff --git a/sdnr/wt/odlux/apps/connectApp/src/plugin.tsx b/sdnr/wt/odlux/apps/connectApp/src/plugin.tsx index c9c11820e..6ab4be156 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/plugin.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/plugin.tsx @@ -31,7 +31,13 @@ export function register() { menuEntry: "Connect" }); - applicationApi.applicationStoreInitialized.then(applicationStore => { applicationStore.dispatch(loadAllMountedNetworkElementsAsync); }); + const updateAllMountedNetworkElements = () => { + applicationApi.applicationStoreInitialized.then(applicationStore => { applicationStore.dispatch(loadAllMountedNetworkElementsAsync); }) + }; + + applicationApi.loginEvent.addHandler(updateAllMountedNetworkElements); + updateAllMountedNetworkElements(); + // subscribe to the websocket notifications subscribe<ObjectNotification & IFormatedMessage>(["ObjectCreationNotification", "ObjectDeletionNotification", "AttributeValueChangedNotification"], (msg => { const store = applicationApi.applicationStore; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/package.json b/sdnr/wt/odlux/apps/performanceHistoryApp/package.json index 91e0bf3d3..74b4d9c68 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/package.json +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/package.json @@ -21,7 +21,10 @@ "author": "Sai Neetha Phulmali", "license": "Apache License, Version 2.0", "dependencies": { - "@odlux/framework" : "*" + "@odlux/framework" : "*", + "@odlux/connect-app": "*", + "react-chartjs-2":"2.7.6", + "chart.js":"2.8.0" }, "peerDependencies": { "@types/react": "16.4.14", diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/connectedNetworkElementsActions.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/connectedNetworkElementsActions.ts index 0906584b7..8f7e99c40 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/connectedNetworkElementsActions.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/connectedNetworkElementsActions.ts @@ -1,8 +1,8 @@ import { Action } from '../../../../framework/src/flux/action'; import { Dispatch } from '../../../../framework/src/flux/store'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import { ConnectedNetworkElements } from '../models/connectedNetworkElements'; -import { PerformanceHistoryService } from '../services/performanceHistoryService'; +import { ConnectedNetworkElementIds } from '../models/connectedNetworkElements'; /** * Represents the base action. @@ -10,32 +10,41 @@ import { PerformanceHistoryService } from '../services/performanceHistoryService export class BaseAction extends Action { } /** - * Represents an action causing the store to load all connected network elements. + * Represents an action causing the store to load all connected network element Ids. */ export class LoadAllConnectedNetworkElementsAction extends BaseAction { } /** - * Represents an action causing the store to update all connected network elements. + * Represents an action causing the store to update all connected network element Ids. */ -export class AllConnectedNetworkElementsLoadedAction extends BaseAction { +export class AllConnectedNetworkElementsLoadedAction extends BaseAction { /** * Initialize this instance. * - * @param connectedNetworkElements The network elements which are returned from the restconf. + * @param connectedNetworkElements The connected network element Ids which are loaded from the state of connectApp. */ - constructor(public connectedNetworkElements: ConnectedNetworkElements[] | null, public error?: string) { + constructor(public connectedNetworkElementIds: ConnectedNetworkElementIds[] | null, public error?: string) { super(); } } /** - * Represents an asynchronous thunk action to load all connected network elements from the restconf. + * Represents an asynchronous thunk action to load all connected network element Ids. */ -export const loadAllConnectedNetworkElementsAsync = (dispatch: Dispatch) => { - dispatch(new LoadAllConnectedNetworkElementsAction()); - PerformanceHistoryService.getConnectedNetworkElementsList().then(networkElements => { - networkElements && dispatch(new AllConnectedNetworkElementsLoadedAction(networkElements)); - }).catch(error => { - dispatch(new AllConnectedNetworkElementsLoadedAction(null, error)); - }); +export const loadAllConnectedNetworkElementsAsync = (dispatch: Dispatch, getState: () => IApplicationStoreState) => { + dispatch(new LoadAllConnectedNetworkElementsAction()); + const connectedNetworkElementsIds = getState().connect.mountedNetworkElements; + let mountIdList: ConnectedNetworkElementIds[] = []; + connectedNetworkElementsIds.elements.forEach(element => { + const connectedNetworkElement = { + mountId: element.mountId + } + mountIdList.push(connectedNetworkElement); + }); + mountIdList.sort((a, b) => { + if (a.mountId < b.mountId) return -1; + if (a.mountId > b.mountId) return 1; + return 0; + }); + dispatch(new AllConnectedNetworkElementsLoadedAction(mountIdList)); };
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/ltpAction.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/ltpAction.ts index d8842ffbe..577066cee 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/ltpAction.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/ltpAction.ts @@ -1,7 +1,7 @@ import { Action } from '../../../../framework/src/flux/action'; import { Dispatch } from '../../../../framework/src/flux/store'; -import { Ltp } from '../models/availableLtps'; +import { LtpIds } from '../models/availableLtps'; import { PerformanceHistoryService } from '../services/performanceHistoryService'; /** @@ -22,7 +22,7 @@ export class AllAvailableLtpsLoadedAction extends BaseAction { * Initialize this instance. * @param availableLtps The available ltps which are returned from the database. */ - constructor(public availableLtps: Ltp[] | null, public error?: string) { + constructor(public availableLtps: LtpIds[] | null, public error?: string) { super(); } } @@ -37,39 +37,25 @@ export class AllAvailableLtpsLoadedAction extends BaseAction { * @param resetLtp The function to verify if the selected ltp is also available in the selected time period database else reset the Ltp dropdown to select. */ export const loadDistinctLtpsbyNetworkElementAsync = (networkElement: string, selectedTimePeriod: string, selectedLtp: string, selectFirstLtp?: Function, resetLtp?: Function) => (dispatch: Dispatch) => { - if (selectedTimePeriod == "15min") { - dispatch(new LoadAllAvailableLtpsAction()); - PerformanceHistoryService.getDistinctLtpsFrom15minDatabase(networkElement).then(distinctLtps => { - if(distinctLtps) { - let ltpNotSelected: boolean = true; - selectFirstLtp && selectFirstLtp(distinctLtps[0].key); - distinctLtps.forEach((value: Ltp) => { - if(value.key === selectedLtp) { - ltpNotSelected = false; - } - }); - resetLtp && resetLtp(ltpNotSelected); - dispatch(new AllAvailableLtpsLoadedAction(distinctLtps)) - } - }).catch(error => { - dispatch(new AllAvailableLtpsLoadedAction(null, error)); - }); - } else { - dispatch(new LoadAllAvailableLtpsAction()); - PerformanceHistoryService.getDistinctLtpsFrom24hoursDatabase(networkElement).then(distinctLtps => { - if(distinctLtps) { - let ltpNotSelected: boolean = true; - selectFirstLtp && selectFirstLtp(distinctLtps[0].key); - distinctLtps.forEach((value: Ltp) => { - if(value.key === selectedLtp) { - ltpNotSelected = false; - } - }); - resetLtp && resetLtp(ltpNotSelected); - dispatch(new AllAvailableLtpsLoadedAction(distinctLtps)) - } - }).catch(error => { - dispatch(new AllAvailableLtpsLoadedAction(null, error)); - }); - } -};
\ No newline at end of file + dispatch(new LoadAllAvailableLtpsAction()); + PerformanceHistoryService.getDistinctLtpsFromDatabase(networkElement, selectedTimePeriod).then(distinctLtps => { + if (distinctLtps) { + const ltps = getDistinctLtps(distinctLtps, selectedLtp, selectFirstLtp, resetLtp); + dispatch(new AllAvailableLtpsLoadedAction(ltps)); + } + }).catch(error => { + dispatch(new AllAvailableLtpsLoadedAction(null, error)); + }); +}; + +const getDistinctLtps = (distinctLtps: LtpIds[], selectedLtp: string, selectFirstLtp?: Function, resetLtp?: Function) => { + let ltpNotSelected: boolean = true; + selectFirstLtp && selectFirstLtp(distinctLtps[0].key); + distinctLtps.forEach((value: LtpIds) => { + if (value.key === selectedLtp) { + ltpNotSelected = false; + } + }); + resetLtp && resetLtp(ltpNotSelected); + return distinctLtps; +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/adaptiveModulation.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/adaptiveModulation.tsx index 024315c3a..281ee2f8d 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/adaptiveModulation.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/adaptiveModulation.tsx @@ -2,23 +2,25 @@ import * as React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; -import { MaterialTable, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; import { AdaptiveModulationDataType } from '../models/adaptiveModulationDataType'; -import { createAdaptiveModulation15minProperties, createAdaptiveModulation15minActions, adaptiveModulation15minReloadAction } from '../handlers/adaptiveModulation15minHandler'; -import { createAdaptiveModulation24hoursProperties, createAdaptiveModulation24hoursActions, adaptiveModulation24hoursReloadAction } from '../handlers/adaptiveModulation24hoursHandler'; - +import { IDataSet, IDataSetsObject } from '../models/chartTypes'; +import { createAdaptiveModulation15minProperties, createAdaptiveModulation15minActions } from '../handlers/adaptiveModulation15minHandler'; +import { createAdaptiveModulation24hoursProperties, createAdaptiveModulation24hoursActions } from '../handlers/adaptiveModulation24hoursHandler'; +import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; +import { addColumnLabels } from '../utils/tableUtils'; const mapProps = (state: IApplicationStoreState) => ({ adaptiveModulation15minProperties: createAdaptiveModulation15minProperties(state), - adaptiveModulation24hoursProperties: createAdaptiveModulation24hoursProperties(state), + adaptiveModulation24hoursProperties: createAdaptiveModulation24hoursProperties(state) }); const mapDisp = (dispatcher: IDispatcher) => ({ adaptiveModulation15minActions: createAdaptiveModulation15minActions(dispatcher.dispatch), - adaptiveModulation24hoursActions: createAdaptiveModulation24hoursActions(dispatcher.dispatch), + adaptiveModulation24hoursActions: createAdaptiveModulation24hoursActions(dispatcher.dispatch) }); type AdaptiveModulationComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { @@ -32,109 +34,391 @@ const AdaptiveModulationTable = MaterialTable as MaterialTableCtorType<AdaptiveM */ class AdaptiveModulationComponent extends React.Component<AdaptiveModulationComponentProps>{ render(): JSX.Element { - if (this.props.selectedTimePeriod == "15min") { - return ( - <AdaptiveModulationTable idProperty={"_id"} columns={[ - { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, - { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, - { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, - { - property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { - const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); - return <div >{suspectIntervalFlag} </div> - } - }, - { property: "time2-states-s", title: "QAM2S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time2-states", title: "QAM2", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time2-states-l", title: "QAM2L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time4-states-s", title: "QAM4S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time4-states", title: "QAM4", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time4-states-l", title: "QAM4L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time16-states-s", title: "QAM16S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time16-states", title: "QAM16", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time16-states-l", title: "QAM16L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time32-states-s", title: "QAM32S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time32-states", title: "QAM32", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time32-states-l", title: "QAM32L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time64-states-s", title: "QAM64S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time64-states", title: "QAM64", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time64-states-l", title: "QAM64L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time128-states-s", title: "QAM128S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time128-states", title: "QAM128", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time128-states-l", title: "QAM128L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time256-states-s", title: "QAM256S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time256-states", title: "QAM256", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time256-states-l", title: "QAM256L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time512-states-s", title: "QAM512S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time512-states", title: "QAM512", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time512-states-l", title: "QAM512L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time1024-states-s", title: "QAM1024S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time1024-states", title: "QAM1024", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time1024-states-l", title: "QAM1024L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time2048-states-s", title: "QAM2048S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time2048-states", title: "QAM2048", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time2048-states-l", title: "QAM2048L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time4096-states-s", title: "QAM4096S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time4096-states", title: "QAM4096", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time4096-states-l", title: "QAM4096L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time8192-states-s", title: "QAM8192S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time8192-states", title: "QAM8192", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time8192-states-l", title: "QAM8192L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - ]} {...this.props.adaptiveModulation15minProperties} {...this.props.adaptiveModulation15minActions} /> - ); - } else { - return ( - <AdaptiveModulationTable idProperty={"_id"} columns={[ - { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, - { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, - { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, - { - property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { - const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); - return <div >{suspectIntervalFlag} </div> - } - }, - { property: "time2-states-s", title: "QAM2S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time2-states", title: "QAM2", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time2-states-l", title: "QAM2L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time4-states-s", title: "QAM4S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time4-states", title: "QAM4", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time4-states-l", title: "QAM4L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time16-states-s", title: "QAM16S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time16-states", title: "QAM16", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time16-states-l", title: "QAM16L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time32-states-s", title: "QAM32S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time32-states", title: "QAM32", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time32-states-l", title: "QAM32L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time64-states-s", title: "QAM64S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time64-states", title: "QAM64", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time64-states-l", title: "QAM64L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time128-states-s", title: "QAM128S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time128-states", title: "QAM128", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time128-states-l", title: "QAM128L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time256-states-s", title: "QAM256S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time256-states", title: "QAM256", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time256-states-l", title: "QAM256L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time512-states-s", title: "QAM512S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time512-states", title: "QAM512", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time512-states-l", title: "QAM512L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time1024-states-s", title: "QAM1024S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time1024-states", title: "QAM1024", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time1024-states-l", title: "QAM1024L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time2048-states-s", title: "QAM2048S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time2048-states", title: "QAM2048", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time2048-states-l", title: "QAM2048L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time4096-states-s", title: "QAM4096S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time4096-states", title: "QAM4096", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time4096-states-l", title: "QAM4096L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time8192-states-s", title: "QAM8192S", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time8192-states", title: "QAM8192", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "time8192-states-l", title: "QAM8192L", type: ColumnType.text, disableFilter: true, disableSorting: true }, - ]} {...this.props.adaptiveModulation24hoursProperties} {...this.props.adaptiveModulation24hoursActions} /> - ); - } + const properties = this.props.selectedTimePeriod === "15min" + ? this.props.adaptiveModulation15minProperties + : this.props.adaptiveModulation24hoursProperties; + + const actions = this.props.selectedTimePeriod === "15min" + ? this.props.adaptiveModulation15minActions + : this.props.adaptiveModulation24hoursActions; + + const chartPagedData = this.getChartDataValues(properties.rows); + const adaptiveModulationColumns: ColumnModel<AdaptiveModulationDataType>[] = [ + { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, + { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, + { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, + { + property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { + const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); + return <div >{suspectIntervalFlag} </div> + } + }]; + + chartPagedData.datasets.forEach(ds => { + adaptiveModulationColumns.push(addColumnLabels<AdaptiveModulationDataType>(ds.name, ds.columnLabel)); + }); + + return ( + <> + {lineChart(chartPagedData)} + <AdaptiveModulationTable idProperty={"_id"} columns={adaptiveModulationColumns} {...properties} {...actions} /> + </> + ); }; -} -export const AdaptiveModulation = withRouter(connect(mapProps, mapDisp)(AdaptiveModulationComponent)); + /** + * This function gets the performance values for Adaptive modulation according on the chartjs dataset structure + * which is to be sent to the chart. + */ + + private getChartDataValues = (rows: AdaptiveModulationDataType[]): IDataSetsObject => { + const _rows = [...rows]; + sortDataByTimeStamp(_rows); + + const datasets: IDataSet[] = [{ + name: "time2-states-s", + label: "QAM2S", + borderColor: '#62a309fc', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM2S", + }, { + name: "time2-states", + label: "QAM2", + borderColor: '#62a309fc', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM2", + }, { + name: "time2-states-l", + label: "QAM2L", + borderColor: '#62a309fc', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM2L", + }, { + name: "time4-states-s", + label: "QAM4S", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM4S", + }, { + name: "time4-states", + label: "QAM4", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM4", + }, { + name: "time4-states-l", + label: "QAM4L", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM4L", + }, { + name: "time16-states-s", + label: "QAM16S", + borderColor: '#9b15e2', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM16S", + }, { + name: "time16-states", + label: "QAM16", + borderColor: '#9b15e2', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM16", + }, { + name: "time16-states-l", + label: "QAM16L", + borderColor: '#9b15e2', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM16L", + }, { + name: "time32-states-s", + label: "QAM32S", + borderColor: '#2704f5f0', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM32S", + }, { + name: "time32-states", + label: "QAM32", + borderColor: '#2704f5f0', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM32", + }, { + name: "time32-states-l", + label: "QAM32L", + borderColor: '#2704f5f0', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM32L", + }, { + name: "time64-states-s", + label: "QAM64S", + borderColor: '#347692', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM64S", + }, { + name: "time64-states", + label: "QAM64", + borderColor: '#347692', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM64", + }, { + name: "time64-states-l", + label: "QAM64L", + borderColor: '#347692', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM64L", + }, { + name: "time128-states-s", + label: "QAM128S", + borderColor: '#885e22', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM128S", + }, { + name: "time128-states", + label: "QAM128", + borderColor: '#885e22', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM128", + }, { + name: "time128-states-l", + label: "QAM128L", + borderColor: '#885e22', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM128L", + }, { + name: "time256-states-s", + label: "QAM256S", + borderColor: '#de07807a', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM256S", + }, { + name: "time256-states", + label: "QAM256", + borderColor: '#de07807a', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM256", + }, { + name: "time256-states-l", + label: "QAM256L", + borderColor: '#de07807a', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM256L", + }, { + name: "time512-states-s", + label: "QAM512S", + borderColor: '#8fdaacde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM512S", + }, { + name: "time512-states", + label: "QAM512", + borderColor: '#8fdaacde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM512", + }, { + + name: "time512-states-l", + label: "QAM512L", + borderColor: '#8fdaacde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM512L", + }, { + + name: "time1024-states-s", + label: "QAM1024S", + borderColor: '#435b22', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM1024S", + }, { + + name: "time1024-states", + label: "QAM1024", + borderColor: '#435b22', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM1024", + }, { + + name: "time1024-states-l", + label: "QAM1024L", + borderColor: '#435b22', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM1024L", + }, { + name: "time2048-states-s", + label: "QAM2048S", + borderColor: '#e87a5b', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM2048S", + }, { + name: "time2048-states", + label: "QAM2048", + borderColor: '#e87a5b', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM2048", + }, { + name: "time2048-states-l", + label: "QAM2048L", + borderColor: '#e87a5b', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM2048L", + }, { + name: "time4096-states-s", + label: "QAM4096S", + borderColor: '#5be878', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM4096S", + }, { + name: "time4096-states", + label: "QAM4096", + borderColor: '#5be878', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM4096", + }, { + name: "time4096-states-l", + label: "QAM4096L", + borderColor: '#5be878', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM4096L", + }, { + name: "time8192-states-s", + label: "QAM8192s", + borderColor: '#cb5be8', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM8192S", + }, { + name: "time8192-states", + label: "QAM8192", + borderColor: '#cb5be8', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM8192", + }, { + name: "time8192-states-l", + label: "QAM8192L", + borderColor: '#cb5be8', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM8192L", + } + ]; + + _rows.forEach(row => { + datasets.forEach(ds => { + ds.data.push({ + x: row["time-stamp"], + y: row[ds.name as keyof AdaptiveModulationDataType] as string + }); + }); + }); + + return { + datasets: datasets + }; + } +} +const AdaptiveModulation = withRouter(connect(mapProps, mapDisp)(AdaptiveModulationComponent)); export default AdaptiveModulation; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx index bbed8abfe..42e3f93a6 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx @@ -2,23 +2,25 @@ import * as React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; -import { MaterialTable, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { MaterialTable, ColumnType, MaterialTableCtorType, ColumnModel } from '../../../../framework/src/components/material-table'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; import { CrossPolarDiscriminationDataType } from '../models/crossPolarDiscriminationDataType'; -import { createCrossPolarDiscrimination15minProperties, createCrossPolarDiscrimination15minActions, crossPolarDiscrimination15minReloadAction } from '../handlers/crossPolarDiscrimination15minHandler'; -import { createCrossPolarDiscrimination24hoursProperties, createCrossPolarDiscrimination24hoursActions, crossPolarDiscrimination24hoursReloadAction } from '../handlers/crossPolarDiscrimination24hoursHandler'; - +import { IDataSet, IDataSetsObject } from '../models/chartTypes'; +import { createCrossPolarDiscrimination15minProperties, createCrossPolarDiscrimination15minActions } from '../handlers/crossPolarDiscrimination15minHandler'; +import { createCrossPolarDiscrimination24hoursProperties, createCrossPolarDiscrimination24hoursActions } from '../handlers/crossPolarDiscrimination24hoursHandler'; +import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; +import { addColumnLabels } from '../utils/tableUtils'; const mapProps = (state: IApplicationStoreState) => ({ crossPolarDiscrimination15minProperties: createCrossPolarDiscrimination15minProperties(state), - crossPolarDiscrimination24hoursProperties: createCrossPolarDiscrimination24hoursProperties(state), + crossPolarDiscrimination24hoursProperties: createCrossPolarDiscrimination24hoursProperties(state) }); const mapDisp = (dispatcher: IDispatcher) => ({ crossPolarDiscrimination15minActions: createCrossPolarDiscrimination15minActions(dispatcher.dispatch), - crossPolarDiscrimination24hoursActions: createCrossPolarDiscrimination24hoursActions(dispatcher.dispatch), + crossPolarDiscrimination24hoursActions: createCrossPolarDiscrimination24hoursActions(dispatcher.dispatch) }); type CrossPolarDiscriminationComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { @@ -32,43 +34,87 @@ const CrossPolarDiscriminationTable = MaterialTable as MaterialTableCtorType<Cro */ class CrossPolarDiscriminationComponent extends React.Component<CrossPolarDiscriminationComponentProps>{ render(): JSX.Element { - if (this.props.selectedTimePeriod == "15min") { - return ( - <CrossPolarDiscriminationTable idProperty={"_id"} columns={[ - { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, - { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, - { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, - { - property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { - const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); - return <div >{suspectIntervalFlag} </div> - } - }, - { property: "xpd-min", title: "CPD (min)[db]", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "xpd-avg", title: "CPD (avg)[db]", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "xpd-max", title: "CPD (max)[db]", type: ColumnType.text, disableFilter: true, disableSorting: true }, - ]} {...this.props.crossPolarDiscrimination15minProperties} {...this.props.crossPolarDiscrimination15minActions} /> - ); - } else { - return ( - <CrossPolarDiscriminationTable idProperty={"_id"} columns={[ - { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, - { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, - { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, - { - property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { - const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); - return <div >{suspectIntervalFlag} </div> - } - }, - { property: "xpd-min", title: "CPD (min)[db]", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "xpd-avg", title: "CPD (avg)[db]", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "xpd-max", title: "CPD (max)[db]", type: ColumnType.text, disableFilter: true, disableSorting: true }, - ]} {...this.props.crossPolarDiscrimination24hoursProperties} {...this.props.crossPolarDiscrimination24hoursActions} /> - ); - } + const properties = this.props.selectedTimePeriod === "15min" + ? this.props.crossPolarDiscrimination15minProperties + : this.props.crossPolarDiscrimination24hoursProperties; + const actions = this.props.selectedTimePeriod === "15min" + ? this.props.crossPolarDiscrimination15minActions + : this.props.crossPolarDiscrimination24hoursActions; + + const chartPagedData = this.getChartDataValues(properties.rows); + + const cpdColumns: ColumnModel<CrossPolarDiscriminationDataType>[] = [ + { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, + { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, + { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, + { + property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { + const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); + return <div >{suspectIntervalFlag} </div> + } + } + ]; + + chartPagedData.datasets.forEach(ds => { + cpdColumns.push(addColumnLabels<CrossPolarDiscriminationDataType>(ds.name, ds.columnLabel)); + }); + return ( + <> + {lineChart(chartPagedData)} + <CrossPolarDiscriminationTable idProperty={"_id"} columns={cpdColumns} {...properties} {...actions} /> + </> + ); }; -} -export const CrossPolarDiscrimination = withRouter(connect(mapProps, mapDisp)(CrossPolarDiscriminationComponent)); + /** + * This function gets the performance values for CPD according on the chartjs dataset structure + * which is to be sent to the chart. + */ + private getChartDataValues = (rows: CrossPolarDiscriminationDataType[]): IDataSetsObject => { + const _rows = [...rows]; + sortDataByTimeStamp(_rows); + + const datasets: IDataSet[] = [{ + name: "xpd-min", + label: "xpd-min", + borderColor: '#0e17f3de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "CPD (min)[db]" + }, { + name: "xpd-avg", + label: "xpd-avg", + borderColor: '#08edb6de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "CPD (avg)[db]" + }, { + name: "xpd-max", + label: "xpd-max", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "CPD (max)[db]" + }]; + + _rows.forEach(row => { + datasets.forEach(ds => { + ds.data.push({ + x: row["time-stamp"], + y: row[ds.name as keyof CrossPolarDiscriminationDataType] as string + }); + }); + }); + return { + datasets: datasets + }; + } +} +const CrossPolarDiscrimination = withRouter(connect(mapProps, mapDisp)(CrossPolarDiscriminationComponent)); export default CrossPolarDiscrimination; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx index e9a373b11..578022db7 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx @@ -2,22 +2,24 @@ import * as React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; -import { MaterialTable, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; - import { PerformanceDataType } from '../models/performanceDataType'; +import { IDataSet, IDataSetsObject } from '../models/chartTypes'; import { createPerformanceData15minProperties, createPerformanceData15minActions } from '../handlers/performanceData15minHandler'; import { createPerformanceData24hoursProperties, createPerformanceData24hoursActions } from '../handlers/performanceData24hoursHandler'; +import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; +import { addColumnLabels } from '../utils/tableUtils'; const mapProps = (state: IApplicationStoreState) => ({ performanceData15minProperties: createPerformanceData15minProperties(state), - performanceData24hoursProperties: createPerformanceData24hoursProperties(state), + performanceData24hoursProperties: createPerformanceData24hoursProperties(state) }); const mapDisp = (dispatcher: IDispatcher) => ({ performanceData15minActions: createPerformanceData15minActions(dispatcher.dispatch), - performanceData24hoursActions: createPerformanceData24hoursActions(dispatcher.dispatch), + performanceData24hoursActions: createPerformanceData24hoursActions(dispatcher.dispatch) }); type PerformanceDataComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { @@ -31,45 +33,87 @@ const PerformanceDataTable = MaterialTable as MaterialTableCtorType<PerformanceD */ class PerformanceDataComponent extends React.Component<PerformanceDataComponentProps>{ render(): JSX.Element { - if (this.props.selectedTimePeriod == "15min") { - return ( - <PerformanceDataTable idProperty={"_id"} columns={[ - { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, - { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, - { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, - { - property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { - const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); - return <div >{suspectIntervalFlag} </div> - } - }, - { property: "es", title: "ES", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "ses", title: "SES", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "unavailability", title: "UAS", type: ColumnType.text, disableFilter: true, disableSorting: true }, - ]} {...this.props.performanceData15minProperties} {...this.props.performanceData15minActions} - /> - ); - } else { - return ( - <PerformanceDataTable idProperty={"_id"} columns={[ - { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, - { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, - { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, - { - property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { - const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); - return <div >{suspectIntervalFlag} </div> - } - }, - { property: "es", title: "ES", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "ses", title: "SES", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "unavailability", title: "UAS", type: ColumnType.text, disableFilter: true, disableSorting: true }, - ]} {...this.props.performanceData24hoursProperties} {...this.props.performanceData24hoursActions} - /> - ); - } + const properties = this.props.selectedTimePeriod === "15min" + ? this.props.performanceData15minProperties + : this.props.performanceData24hoursProperties; + const actions = this.props.selectedTimePeriod === "15min" + ? this.props.performanceData15minActions + : this.props.performanceData24hoursActions; + + const chartPagedData = this.getChartDataValues(properties.rows); + const performanceColumns: ColumnModel<PerformanceDataType>[] = [ + { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, + { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, + { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, + { + property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { + const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); + return <div >{suspectIntervalFlag} </div> + } + } + ]; + + chartPagedData.datasets.forEach(ds => { + performanceColumns.push(addColumnLabels<PerformanceDataType>(ds.name, ds.columnLabel)); + }); + return ( + <> + {lineChart(chartPagedData)} + <PerformanceDataTable idProperty={"_id"} columns={performanceColumns} {...properties} {...actions} /> + </> + ); }; + + /** + * This function gets the performance values for PerformanceData according on the chartjs dataset structure + * which is to be sent to the chart. + */ + private getChartDataValues = (rows: PerformanceDataType[]): IDataSetsObject => { + const _rows = [...rows]; + sortDataByTimeStamp(_rows); + + const datasets: IDataSet[] = [{ + name: "es", + label: "es", + borderColor: '#0e17f3de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "ES" + }, { + name: "ses", + label: "ses", + borderColor: '#08edb6de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "SES" + }, { + name: "unavailability", + label: "unavailability", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Unavailability" + }]; + + _rows.forEach(row => { + datasets.forEach(ds => { + ds.data.push({ + x: row["time-stamp"], + y: row[ds.name as keyof PerformanceDataType] as string + }); + }); + }); + return { + datasets: datasets + }; + } } -export const PerformanceData = withRouter(connect(mapProps, mapDisp)(PerformanceDataComponent)); +const PerformanceData = withRouter(connect(mapProps, mapDisp)(PerformanceDataComponent)); export default PerformanceData; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx index 5f62e585a..b893ce149 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx @@ -2,23 +2,25 @@ import * as React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; -import { MaterialTable, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; import { ReceiveLevelDataType } from '../models/receiveLevelDataType'; -import { createReceiveLevel15minProperties, createReceiveLevel15minActions, receiveLevel15minReloadAction } from '../handlers/receiveLevel15minHandler'; -import { createReceiveLevel24hoursProperties, createReceiveLevel24hoursActions, receiveLevel24hoursReloadAction } from '../handlers/receiveLevel24hoursHandler'; - +import { IDataSet, IDataSetsObject } from '../models/chartTypes'; +import { createReceiveLevel15minProperties, createReceiveLevel15minActions } from '../handlers/receiveLevel15minHandler'; +import { createReceiveLevel24hoursProperties, createReceiveLevel24hoursActions } from '../handlers/receiveLevel24hoursHandler'; +import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; +import { addColumnLabels } from '../utils/tableUtils'; const mapProps = (state: IApplicationStoreState) => ({ receiveLevel15minProperties: createReceiveLevel15minProperties(state), - receiveLevel24hoursProperties: createReceiveLevel24hoursProperties(state), + receiveLevel24hoursProperties: createReceiveLevel24hoursProperties(state) }); const mapDisp = (dispatcher: IDispatcher) => ({ receiveLevel15minActions: createReceiveLevel15minActions(dispatcher.dispatch), - receiveLevel24hoursActions: createReceiveLevel24hoursActions(dispatcher.dispatch), + receiveLevel24hoursActions: createReceiveLevel24hoursActions(dispatcher.dispatch) }); type ReceiveLevelComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { @@ -32,43 +34,88 @@ const ReceiveLevelTable = MaterialTable as MaterialTableCtorType<ReceiveLevelDat */ class ReceiveLevelComponent extends React.Component<ReceiveLevelComponentProps>{ render(): JSX.Element { - if (this.props.selectedTimePeriod == "15min") { - return ( - <ReceiveLevelTable idProperty={"_id"} columns={[ - { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, - { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, - { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, - { - property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { - const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); - return <div >{suspectIntervalFlag} </div> - } - }, - { property: "rx-level-min", title: "Rx min", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "rx-level-avg", title: "Rx avg", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "rx-level-max", title: "Rx max", type: ColumnType.text, disableFilter: true, disableSorting: true }, - ]} {...this.props.receiveLevel15minProperties} {...this.props.receiveLevel15minActions} /> - ); - } else { - return ( - <ReceiveLevelTable idProperty={"_id"} columns={[ - { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, - { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, - { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, - { - property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { - const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); - return <div >{suspectIntervalFlag} </div> - } - }, - { property: "rx-level-min", title: "Rx min", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "rx-level-avg", title: "Rx avg", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "rx-level-max", title: "Rx max", type: ColumnType.text, disableFilter: true, disableSorting: true }, - ]} {...this.props.receiveLevel24hoursProperties} {...this.props.receiveLevel24hoursActions} /> - ); - } + const properties = this.props.selectedTimePeriod === "15min" + ? this.props.receiveLevel15minProperties + : this.props.receiveLevel24hoursProperties; + const actions = this.props.selectedTimePeriod === "15min" + ? this.props.receiveLevel15minActions + : this.props.receiveLevel24hoursActions; + + const chartPagedData = this.getChartDataValues(properties.rows); + const receiveLevelColumns: ColumnModel<ReceiveLevelDataType>[] = [ + { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, + { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, + { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, + { + property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { + const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); + return <div >{suspectIntervalFlag} </div> + } + } + ]; + + chartPagedData.datasets.forEach(ds => { + receiveLevelColumns.push(addColumnLabels<ReceiveLevelDataType>(ds.name, ds.columnLabel)); + }); + + return ( + <> + {lineChart(chartPagedData)} + <ReceiveLevelTable idProperty={"_id"} columns={receiveLevelColumns} {...properties} {...actions} /> + </> + ); }; + + /** + * This function gets the performance values for ReceiveLevel according on the chartjs dataset structure + * which is to be sent to the chart. + */ + private getChartDataValues = (rows: ReceiveLevelDataType[]): IDataSetsObject => { + const _rows = [...rows]; + sortDataByTimeStamp(_rows); + + const datasets: IDataSet[] = [{ + name: "rx-level-min", + label: "rx-level-min", + borderColor: '#0e17f3de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Rx min" + }, { + name: "rx-level-avg", + label: "rx-level-avg", + borderColor: '#08edb6de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Rx avg" + }, { + name: "rx-level-max", + label: "rx-level-max", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Rx max" + }]; + + _rows.forEach(row => { + datasets.forEach(ds => { + ds.data.push({ + x: row["time-stamp"], + y: row[ds.name as keyof ReceiveLevelDataType] as string + }); + }); + }); + return { + datasets: datasets + }; + } } -export const ReceiveLevel = withRouter(connect(mapProps, mapDisp)(ReceiveLevelComponent)); +const ReceiveLevel = withRouter(connect(mapProps, mapDisp)(ReceiveLevelComponent)); export default ReceiveLevel; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx index bd612822e..90ed1a9cb 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx @@ -2,22 +2,25 @@ import * as React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; -import { MaterialTable, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; import { SignalToInterferenceDataType } from '../models/signalToInteferenceDataType'; +import { IDataSet, IDataSetsObject } from '../models/chartTypes'; import { createSignalToInterference15minProperties, createSignalToInterference15minActions } from '../handlers/signalToInterference15minHandler'; import { createSignalToInterference24hoursProperties, createSignalToInterference24hoursActions } from '../handlers/signalToInterference24hoursHandler'; +import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; +import { addColumnLabels } from '../utils/tableUtils'; const mapProps = (state: IApplicationStoreState) => ({ signalToInterference15minProperties: createSignalToInterference15minProperties(state), - signalToInterference24hoursProperties: createSignalToInterference24hoursProperties(state), + signalToInterference24hoursProperties: createSignalToInterference24hoursProperties(state) }); const mapDisp = (dispatcher: IDispatcher) => ({ signalToInterference15minActions: createSignalToInterference15minActions(dispatcher.dispatch), - signalToInterference24hoursActions: createSignalToInterference24hoursActions(dispatcher.dispatch), + signalToInterference24hoursActions: createSignalToInterference24hoursActions(dispatcher.dispatch) }); type SignalToInterferenceComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { @@ -31,45 +34,91 @@ const SignalToInterferenceTable = MaterialTable as MaterialTableCtorType<SignalT */ class SignalToInterferenceComponent extends React.Component<SignalToInterferenceComponentProps>{ render(): JSX.Element { - if (this.props.selectedTimePeriod == "15min") { - return ( - <SignalToInterferenceTable idProperty={"_id"} columns={[ - { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, - { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, - { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, - { - property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { - const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); - return <div >{suspectIntervalFlag} </div> - } - }, - { property: "snir-min", title: "SINR (min)[db]", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "snir-avg", title: "SINR (avg)[db]", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "snir-max", title: "SINR (max)[db]", type: ColumnType.text, disableFilter: true, disableSorting: true }, - ]} {...this.props.signalToInterference15minProperties} {...this.props.signalToInterference15minActions} - /> - ); - } else { - return ( - <SignalToInterferenceTable idProperty={"_id"} columns={[ - { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, - { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, - { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, - { - property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { - const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); - return <div >{suspectIntervalFlag} </div> - } - }, - { property: "snir-min", title: "SINR (min)[db]", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "snir-avg", title: "SINR (avg)[db]", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "snir-max", title: "SINR (max)[db]", type: ColumnType.text, disableFilter: true, disableSorting: true }, - ]} {...this.props.signalToInterference24hoursProperties} {...this.props.signalToInterference24hoursActions} + const properties = this.props.selectedTimePeriod === "15min" + ? this.props.signalToInterference15minProperties + : this.props.signalToInterference24hoursProperties; + const actions = this.props.selectedTimePeriod === "15min" + ? this.props.signalToInterference15minActions + : this.props.signalToInterference24hoursActions; + + const chartPagedData = this.getChartDataValues(properties.rows); + + const sinrColumns: ColumnModel<SignalToInterferenceDataType>[] = [ + + { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, + { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, + { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, + { + property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { + const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); + return <div >{suspectIntervalFlag} </div> + } + } + ]; + + chartPagedData.datasets.forEach(ds => { + sinrColumns.push(addColumnLabels<SignalToInterferenceDataType>(ds.name, ds.columnLabel)); + }); + return ( + <> + {lineChart(chartPagedData)} + <SignalToInterferenceTable idProperty={"_id"} columns={sinrColumns} {...properties} {...actions} /> - ); - } + </> + ); }; + + /** + * This function gets the performance values for SINR according on the chartjs dataset structure + * which is to be sent to the chart. + */ + + private getChartDataValues = (rows: SignalToInterferenceDataType[]): IDataSetsObject => { + const _rows = [...rows]; + sortDataByTimeStamp(_rows); + + const datasets: IDataSet[] = [{ + name: "snir-min", + label: "snir-min", + borderColor: '#0e17f3de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "SINR (min)[db]" + }, { + name: "snir-avg", + label: "snir-avg", + borderColor: '#08edb6de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "SINR (avg)[db]" + }, { + name: "snir-max", + label: "snir-max", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "SINR (max)[db]" + }]; + + _rows.forEach(row => { + datasets.forEach(ds => { + ds.data.push({ + x: row["time-stamp"], + y: row[ds.name as keyof SignalToInterferenceDataType] as string + }); + }); + }); + return { + datasets: datasets + }; + } } -export const SignalToInterference = withRouter(connect(mapProps, mapDisp)(SignalToInterferenceComponent)); +const SignalToInterference = withRouter(connect(mapProps, mapDisp)(SignalToInterferenceComponent)); export default SignalToInterference; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx index 1496396aa..f105bfd46 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx @@ -2,23 +2,25 @@ import * as React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; -import { MaterialTable, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; import { TemperatureDataType } from '../models/temperatureDataType'; -import { createTemperature15minProperties, createTemperature15minActions, temperature15minReloadAction } from '../handlers/temperature15minHandler'; -import { createTemperature24hoursProperties, createTemperature24hoursActions, temperature24hoursReloadAction } from '../handlers/temperature24hoursHandler'; - +import { IDataSet, IDataSetsObject } from '../models/chartTypes'; +import { createTemperature15minProperties, createTemperature15minActions } from '../handlers/temperature15minHandler'; +import { createTemperature24hoursProperties, createTemperature24hoursActions } from '../handlers/temperature24hoursHandler'; +import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; +import { addColumnLabels } from '../utils/tableUtils'; const mapProps = (state: IApplicationStoreState) => ({ temperature15minProperties: createTemperature15minProperties(state), - temperature24hoursProperties: createTemperature24hoursProperties(state), + temperature24hoursProperties: createTemperature24hoursProperties(state) }); const mapDisp = (dispatcher: IDispatcher) => ({ temperature15minActions: createTemperature15minActions(dispatcher.dispatch), - temperature24hoursActions: createTemperature24hoursActions(dispatcher.dispatch), + temperature24hoursActions: createTemperature24hoursActions(dispatcher.dispatch) }); type TemperatureComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { @@ -32,43 +34,88 @@ const TemperatureTable = MaterialTable as MaterialTableCtorType<TemperatureDataT */ class TemperatureComponent extends React.Component<TemperatureComponentProps>{ render(): JSX.Element { - if (this.props.selectedTimePeriod == "15min") { - return ( - <TemperatureTable idProperty={"_id"} columns={[ - { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, - { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, - { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, - { - property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { - const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); - return <div >{suspectIntervalFlag} </div> - } - }, - { property: "rf-temp-min", title: "Rx min", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "rf-temp-avg", title: "Rx avg", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "rf-temp-max", title: "Rx max", type: ColumnType.text, disableFilter: true, disableSorting: true }, - ]} {...this.props.temperature15minProperties} {...this.props.temperature15minActions} /> - ); - } else { - return ( - <TemperatureTable idProperty={"_id"} columns={[ - { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, - { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, - { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, - { - property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { - const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); - return <div >{suspectIntervalFlag} </div> - } - }, - { property: "rf-temp-min", title: "Rx min", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "rf-temp-avg", title: "Rx avg", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "rf-temp-max", title: "Rx max", type: ColumnType.text, disableFilter: true, disableSorting: true }, - ]} {...this.props.temperature24hoursProperties} {...this.props.temperature24hoursActions} /> - ); - } + const properties = this.props.selectedTimePeriod === "15min" + ? this.props.temperature15minProperties + : this.props.temperature24hoursProperties; + const actions = this.props.selectedTimePeriod === "15min" + ? this.props.temperature15minActions + : this.props.temperature24hoursActions; + + const chartPagedData = this.getChartDataValues(properties.rows); + const temperatureColumns: ColumnModel<TemperatureDataType>[] = [ + { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, + { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, + { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, + { + property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { + const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); + return <div >{suspectIntervalFlag} </div> + } + } + ]; + + chartPagedData.datasets.forEach(ds => { + temperatureColumns.push(addColumnLabels<TemperatureDataType>(ds.name, ds.columnLabel)); + }); + return ( + <> + {lineChart(chartPagedData)} + <TemperatureTable idProperty={"_id"} columns={temperatureColumns} {...properties} {...actions} /> + </> + ); }; + + /** + * This function gets the performance values for Temperature according on the chartjs dataset structure + * which is to be sent to the chart. + */ + + private getChartDataValues = (rows: TemperatureDataType[]): IDataSetsObject => { + const _rows = [...rows]; + sortDataByTimeStamp(_rows); + + const datasets: IDataSet[] = [{ + name: "rf-temp-min", + label: "rf-temp-min", + borderColor: '#0e17f3de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Rf Temp Min[deg C]" + }, { + name: "rf-temp-avg", + label: "rf-temp-avg", + borderColor: '#08edb6de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Rf Temp Avg[deg C]" + }, { + name: "rf-temp-max", + label: "rf-temp-max", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Rf Temp Max[deg C]" + }]; + + _rows.forEach(row => { + datasets.forEach(ds => { + ds.data.push({ + x: row["time-stamp"], + y: row[ds.name as keyof TemperatureDataType] as string + }); + }); + }); + return { + datasets: datasets + }; + } } -export const Temperature = withRouter(connect(mapProps, mapDisp)(TemperatureComponent)); +const Temperature = withRouter(connect(mapProps, mapDisp)(TemperatureComponent)); export default Temperature; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx index 10c25874a..088a83eed 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx @@ -2,23 +2,25 @@ import * as React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; -import { MaterialTable, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; import { TransmissionPowerDataType } from '../models/transmissionPowerDataType'; +import { IDataSet, IDataSetsObject } from '../models/chartTypes'; import { createTransmissionPower15minProperties, createTransmissionPower15minActions } from '../handlers/transmissionPower15minHandler'; import { createTransmissionPower24hoursProperties, createTransmissionPower24hoursActions } from '../handlers/transmissionPower24hoursHandler'; - +import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; +import { addColumnLabels } from '../utils/tableUtils'; const mapProps = (state: IApplicationStoreState) => ({ transmissionPower15minProperties: createTransmissionPower15minProperties(state), - transmissionPower24hoursProperties: createTransmissionPower24hoursProperties(state), + transmissionPower24hoursProperties: createTransmissionPower24hoursProperties(state) }); const mapDisp = (dispatcher: IDispatcher) => ({ transmissionPower15minActions: createTransmissionPower15minActions(dispatcher.dispatch), - transmissionPower24hoursActions: createTransmissionPower24hoursActions(dispatcher.dispatch), + transmissionPower24hoursActions: createTransmissionPower24hoursActions(dispatcher.dispatch) }); type TransmissionPowerComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { @@ -32,43 +34,90 @@ const TransmissionPowerTable = MaterialTable as MaterialTableCtorType<Transmissi */ class TransmissionPowerComponent extends React.Component<TransmissionPowerComponentProps>{ render(): JSX.Element { - if (this.props.selectedTimePeriod == "15min") { - return ( - <TransmissionPowerTable idProperty={"_id"} columns={[ - { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, - { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, - { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, - { - property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { - const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); - return <div >{suspectIntervalFlag} </div> - } - }, - { property: "tx-level-min", title: "Tx min", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "tx-level-avg", title: "Tx avg", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "tx-level-max", title: "Tx max", type: ColumnType.text, disableFilter: true, disableSorting: true }, - ]} {...this.props.transmissionPower15minProperties} {...this.props.transmissionPower15minActions} /> - ); - } else { - return ( - <TransmissionPowerTable idProperty={"_id"} columns={[ - { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, - { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, - { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, - { - property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { - const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); - return <div >{suspectIntervalFlag} </div> - } - }, - { property: "tx-level-min", title: "Tx min", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "tx-level-avg", title: "Tx avg", type: ColumnType.text, disableFilter: true, disableSorting: true }, - { property: "tx-level-max", title: "Tx max", type: ColumnType.text, disableFilter: true, disableSorting: true }, - ]} {...this.props.transmissionPower24hoursProperties} {...this.props.transmissionPower24hoursActions} /> - ); - } + const properties = this.props.selectedTimePeriod === "15min" + ? this.props.transmissionPower15minProperties + : this.props.transmissionPower24hoursProperties; + const actions = this.props.selectedTimePeriod === "15min" + ? this.props.transmissionPower15minActions + : this.props.transmissionPower24hoursActions; + + const chartPagedData = this.getChartDataValues(properties.rows); + + const transmissionColumns: ColumnModel<TransmissionPowerDataType>[] = [ + { property: "radio-signal-id", title: "Radio signal", type: ColumnType.text }, + { property: "scanner-id", title: "Scanner ID", type: ColumnType.text }, + { property: "time-stamp", title: "End Time", type: ColumnType.text, disableFilter: true }, + { + property: "suspect-interval-flag", title: "Suspect Interval", type: ColumnType.custom, customControl: ({ rowData }) => { + const suspectIntervalFlag = rowData["suspect-interval-flag"].toString(); + return <div >{suspectIntervalFlag} </div> + } + } + ]; + + chartPagedData.datasets.forEach(ds => { + transmissionColumns.push(addColumnLabels<TransmissionPowerDataType>(ds.name, ds.columnLabel)); + }); + + return ( + <> + {lineChart(chartPagedData)} + <TransmissionPowerTable idProperty={"_id"} columns={transmissionColumns} {...properties} {...actions} /> + </> + ); }; + + /** + * This function gets the performance values for TransmissionPower according on the chartjs dataset structure + * which is to be sent to the chart. + */ + + private getChartDataValues = (rows: TransmissionPowerDataType[]): IDataSetsObject => { + const _rows = [...rows]; + sortDataByTimeStamp(_rows); + + const datasets: IDataSet[] = [{ + name: "tx-level-min", + label: "tx-level-min", + borderColor: '#0e17f3de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Tx min" + }, { + name: "tx-level-avg", + label: "tx-level-avg", + borderColor: '#08edb6de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Tx avg" + }, { + name: "tx-level-max", + label: "tx-level-max", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Tx max" + }]; + + _rows.forEach(row => { + datasets.forEach(ds => { + ds.data.push({ + x: row["time-stamp"], + y: row[ds.name as keyof TransmissionPowerDataType] as string + }); + }); + }); + return { + datasets: datasets + }; + } } -export const TransmissionPower = withRouter(connect(mapProps, mapDisp)(TransmissionPowerComponent)); +const TransmissionPower = withRouter(connect(mapProps, mapDisp)(TransmissionPowerComponent)); export default TransmissionPower; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts index 2fd0ac8f1..415b073c9 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts @@ -5,10 +5,10 @@ import { LoadAllAvailableLtpsAction, } from '../actions/ltpAction'; -import { Ltp } from '../models/availableLtps'; +import { LtpIds } from '../models/availableLtps'; export interface IAvailableLtpsState { - distinctLtps: Ltp[]; + distinctLtps: LtpIds[]; busy: boolean; } diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/connectedNetworkElementsActionHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/connectedNetworkElementsActionHandler.ts index 039ae5357..ad153f0ec 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/connectedNetworkElementsActionHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/connectedNetworkElementsActionHandler.ts @@ -1,19 +1,15 @@ import { IActionHandler } from '../../../../framework/src/flux/action'; -import { - AllConnectedNetworkElementsLoadedAction, - LoadAllConnectedNetworkElementsAction, -} from '../actions/connectedNetworkElementsActions'; - -import { ConnectedNetworkElements } from '../models/connectedNetworkElements'; +import { AllConnectedNetworkElementsLoadedAction, LoadAllConnectedNetworkElementsAction } from '../actions/connectedNetworkElementsActions'; +import { ConnectedNetworkElementIds } from '../models/connectedNetworkElements'; export interface IConnectedNetworkElementsState { - connectedNetworkElements: ConnectedNetworkElements[]; + connectedNetworkElementIds: ConnectedNetworkElementIds[]; busy: boolean; } const connectedNetworkElementsStateInit: IConnectedNetworkElementsState = { - connectedNetworkElements: [], + connectedNetworkElementIds: [], busy: false }; @@ -26,10 +22,10 @@ export const connectedNetworkElementsActionHandler: IActionHandler<IConnectedNet }; } else if (action instanceof AllConnectedNetworkElementsLoadedAction) { - if (!action.error && action.connectedNetworkElements) { + if (!action.error && action.connectedNetworkElementIds) { state = { ...state, - connectedNetworkElements: action.connectedNetworkElements, + connectedNetworkElementIds: action.connectedNetworkElementIds, busy: false }; } else { diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts index 1cf814b5a..43813e573 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts @@ -6,6 +6,7 @@ import { combineActionHandler } from '../../../../framework/src/flux/middleware' import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; import { IActionHandler } from '../../../../framework/src/flux/action'; +import { IConnectAppStoreState } from '../../../connectApp/src/handlers/connectAppRootHandler'; import { IPerformanceData15minState, performanceData15minActionHandler } from './performanceData15minHandler'; import { IReceiveLevel15minState, receiveLevel15minActionHandler } from './receiveLevel15minHandler'; import { ITransmissionPower15minState, transmissionPower15minActionHandler } from './transmissionPower15minHandler'; @@ -55,6 +56,7 @@ const currentOpenPanelHandler: IActionHandler<string | null> = (state = null, ac declare module '../../../../framework/src/store/applicationStore' { interface IApplicationStoreState { performanceHistory: IPerformanceHistoryStoreState; + connect: IConnectAppStoreState; } } @@ -75,9 +77,9 @@ const actionHandlers = { signalToInterference24hours: signalToInterference24hoursActionHandler, crossPolarDiscrimination15min: crossPolarDiscrimination15minActionHandler, crossPolarDiscrimination24hours: crossPolarDiscrimination24hoursActionHandler, - currentOpenPanel: currentOpenPanelHandler, + currentOpenPanel: currentOpenPanelHandler }; -export const performanceHistoryRootHandler = combineActionHandler<IPerformanceHistoryStoreState>(actionHandlers); +const performanceHistoryRootHandler = combineActionHandler<IPerformanceHistoryStoreState>(actionHandlers); export default performanceHistoryRootHandler; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/temperature15minHandler.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/temperature15minHandler.ts index 20fb57035..20fb57035 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/temperature15minHandler.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/temperature15minHandler.ts diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/transmissionPower15minHandler.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/transmissionPower15minHandler.ts index e6ba90f10..e6ba90f10 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/transmissionPower15minHandler.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/transmissionPower15minHandler.ts diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/index.html b/sdnr/wt/odlux/apps/performanceHistoryApp/src/index.html index fce395d33..8cb775be2 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/index.html +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/index.html @@ -5,7 +5,7 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> - <link rel="stylesheet" href="./vendor.css" > + <!-- <link rel="stylesheet" href="./vendor.css" > --> <title>PM History Application</title> </head> @@ -15,7 +15,8 @@ <script type="text/javascript" src="./config.js"></script> <script> // run the application - require(["app","performanceHistoryApp"], function (app, performanceHistoryApp) { + require(["app", "connectApp", "performanceHistoryApp"], function (app, connectApp, performanceHistoryApp) { + connectApp.register(); performanceHistoryApp.register(); app("./app.tsx").runApplication(); }); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/adaptiveModulationDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/adaptiveModulationDataType.ts index 686e3bc96..701d54a38 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/adaptiveModulationDataType.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/adaptiveModulationDataType.ts @@ -47,7 +47,6 @@ export type AdaptiveModulationDataType = { "time8192-states-l": string; }; - export type AdaptiveModulationResult = { "performance-data": AdaptiveModulationDataType }; -export type AdaptiveModulation = AdaptiveModulationDataType & { _id: string };
\ No newline at end of file +export type AdaptiveModulation = AdaptiveModulationDataType & { _id: string }; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/availableLtps.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/availableLtps.ts index 15a27f147..d5a6c6888 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/availableLtps.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/availableLtps.ts @@ -1,14 +1,14 @@ - export type Ltp = { + export type LtpIds = { key: string } - export type Bucket={ - buckets: Ltp[] + export type Bucket<T>={ + buckets: T[] } /** * Represents distinct available ltps using elasticsearch aggregations structure. */ export type DistinctLtp = { - "uuid-interface": Bucket + "uuid-interface": Bucket<LtpIds> } diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/chartTypes.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/chartTypes.ts new file mode 100644 index 000000000..8ea59e2ec --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/chartTypes.ts @@ -0,0 +1,33 @@ +export interface IData { + x: string; + y: string; +} + +/** + * Structure of chartjs dataset with the chart properties. + */ + export interface IDataSet { + name: string, + label: string, + lineTension: 0, + bezierCurve: boolean; + fill: boolean, + borderColor: string, + data: IData[], + columnLabel:string + } + +/** + * Structure of chartjs dataset which is sent to the chart. + */ + export interface IDataSetsObject { + datasets: IDataSet[] + } + +/** + * Interface used by chart for sorting on time-stamp + */ + export interface ITimeStamp { + "time-stamp": string; + } +
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/connectedNetworkElements.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/connectedNetworkElements.ts index f4afa645d..b7277f777 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/connectedNetworkElements.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/connectedNetworkElements.ts @@ -1,6 +1,7 @@ /** * Represents connected network elements. */ -export type ConnectedNetworkElements = { - mountId: string, + +export type ConnectedNetworkElementIds = { + mountId: string; } diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/crossPolarDiscriminationDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/crossPolarDiscriminationDataType.ts index 2dc183eda..43e74eb3e 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/crossPolarDiscriminationDataType.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/crossPolarDiscriminationDataType.ts @@ -14,7 +14,6 @@ export type CrossPolarDiscriminationDataType = { "xpd-max": string; }; - export type CrossPolarDiscriminationResult = { "performance-data": CrossPolarDiscriminationDataType }; export type CrossPolarDiscrimination = CrossPolarDiscriminationDataType & { _id: string };
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/performanceDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/performanceDataType.ts index 6972998b6..3c9b5cb21 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/performanceDataType.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/performanceDataType.ts @@ -14,7 +14,6 @@ export type PerformanceDataType = { "unavailability": string; }; - export type PerformanceResult = { "performance-data": PerformanceDataType }; export type Performance = PerformanceDataType & { _id: string };
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/receiveLevelDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/receiveLevelDataType.ts index 9f51c8fee..57ea48cce 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/receiveLevelDataType.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/receiveLevelDataType.ts @@ -14,7 +14,6 @@ export type ReceiveLevelDataType = { "rx-level-max": string; }; - export type ReceiveLevelResult = { "performance-data": ReceiveLevelDataType }; -export type ReceiveLevel = ReceiveLevelDataType & { _id: string };
\ No newline at end of file +export type ReceiveLevel = ReceiveLevelDataType & { _id: string }; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/signalToInteferenceDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/signalToInteferenceDataType.ts index 73934bd85..51d43b455 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/signalToInteferenceDataType.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/signalToInteferenceDataType.ts @@ -14,7 +14,6 @@ export type SignalToInterferenceDataType = { "snir-max": string; }; - export type SignalToInterferenceResult = { "performance-data": SignalToInterferenceDataType }; -export type SignalToInterference = SignalToInterferenceDataType & { _id: string };
\ No newline at end of file +export type SignalToInterference = SignalToInterferenceDataType & { _id: string }; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/temperatureDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/temperatureDataType.ts index e5be6e36c..3179d7b6a 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/temperatureDataType.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/temperatureDataType.ts @@ -14,7 +14,6 @@ export type TemperatureDataType = { "rf-temp-max": string; }; - export type TemperatureResult = { "performance-data": TemperatureDataType }; -export type Temperature = TemperatureDataType & { _id: string };
\ No newline at end of file +export type Temperature = TemperatureDataType & { _id: string }; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/transmissionPowerDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/transmissionPowerDataType.ts index 948baff12..af6f9892d 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/transmissionPowerDataType.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/transmissionPowerDataType.ts @@ -14,7 +14,6 @@ export type TransmissionPowerDataType = { "tx-level-max": string; }; - export type TransmissionPowerResult = { "performance-data": TransmissionPowerDataType }; -export type TransmissionPower = TransmissionPowerDataType & { _id: string };
\ No newline at end of file +export type TransmissionPower = TransmissionPowerDataType & { _id: string }; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.ts new file mode 100644 index 000000000..d0b8346cd --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.ts @@ -0,0 +1,46 @@ +import { requestRest } from '../../../../framework/src/services/restService'; +import { Result } from '../../../../framework/src/models/elasticSearch'; + +import { DistinctLtp, LtpIds } from '../models/availableLtps'; + +/** + * Represents a web api accessor service for Network elements actions. + */ +class PerformanceService { + + /** + * Get distinct ltps based on the selected network element and time period from the historicalperformance15min database table. + */ + public async getDistinctLtpsFromDatabase(networkElement: string, selectedTimePeriod: string): Promise<LtpIds[] | null> { + let path; + const query = { + "size": 0, + "query": { + "match": { + "node-name": networkElement + } + }, + "aggs": { + "uuid-interface": { + "terms": { + "field": "uuid-interface" + } + } + } + }; + + if (selectedTimePeriod === "15min") { + path = 'database/sdnperformance/historicalperformance15min/_search'; + } else { + path = 'database/sdnperformance/historicalperformance24h/_search'; + } + + const result = await requestRest<Result<DistinctLtp>>(path, { method: "POST", body: JSON.stringify(query) }); + return result && result.aggregations && result.aggregations["uuid-interface"].buckets.map(ne => ({ + key: ne.key + })) || null; + } +} + +export const PerformanceHistoryService = new PerformanceService(); +export default PerformanceHistoryService; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.tsx deleted file mode 100644 index a1cdcffcc..000000000 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { requestRest } from '../../../../framework/src/services/restService'; -import { Result } from '../../../../framework/src/models/elasticSearch'; - -import { ConnectedNetworkElements } from '../models/connectedNetworkElements'; -import { DistinctLtp, Ltp } from '../models/availableLtps'; -import { Topology, TopologyNode } from '../models/topologyNetConf'; - -/** - * Represents a web api accessor service for Network elements actions. - */ -class PerformanceService { - - private static networkElementTopology = (mountPoint: TopologyNode) => { - const mountId = mountPoint["node-id"]; - return { - mountId: mountId, - } - } - - /** - * Get all connected network elements from restconf. - */ - public async getConnectedNetworkElementsList(): Promise<ConnectedNetworkElements[] | null> { - const path = "restconf/operational/network-topology:network-topology/topology/topology-netconf"; - const topologyRequestPomise = requestRest<{ topology: Topology[] | null }>(path, { method: "GET" }, true); - const [netconfResponse] = await Promise.all([topologyRequestPomise]); - const topologyNetconf = netconfResponse && netconfResponse.topology && netconfResponse.topology.find(topology => topology["topology-id"] === "topology-netconf"); - let mountPoints = topologyNetconf && topologyNetconf.node && topologyNetconf.node.filter( - mountPoint => mountPoint["netconf-node-topology:connection-status"] == "connected").map(mountedElement => { - return PerformanceService.networkElementTopology(mountedElement); - }); - return mountPoints || []; - } - - /** - * Get distinct ltps based on the selected network element and time period from the historicalperformance15min database table. - */ - public async getDistinctLtpsFrom15minDatabase(networkElement: string): Promise<Ltp[] | null> { - const path = 'database/sdnperformance/historicalperformance15min/_search'; - const query = { - "size": 0, - "query": { - "match": { - "node-name": networkElement - } - }, - "aggs": { - "uuid-interface": { - "terms": { - "field": "uuid-interface" - } - } - } - }; - const result = await requestRest<Result<DistinctLtp>>(path, { method: "POST", body: JSON.stringify(query) }); - if(result && result.aggregations) { - } - return result && result.aggregations && result.aggregations["uuid-interface"].buckets.map(ne=>({ - key:ne.key - }))|| null; - } - - /** - * Get distinct ltps based on the selected network element and time period from the historicalperformance24h database table. - */ - public async getDistinctLtpsFrom24hoursDatabase(networkElement: string): Promise<Ltp[] | null> { - const path = 'database/sdnperformance/historicalperformance24h/_search'; - const query = { - "size": 0, - "query": { - "match": { - "node-name": networkElement - } - }, - "aggs": { - "uuid-interface": { - "terms": { - "field": "uuid-interface" - } - } - } - }; - const result = await requestRest<Result<DistinctLtp>>(path, { method: "POST", body: JSON.stringify(query) }); - if(result && result.aggregations) { - } - return result && result.aggregations && result.aggregations["uuid-interface"].buckets.map(ne=>({ - key:ne.key - }))|| null; - } -} - -export const PerformanceHistoryService = new PerformanceService(); -export default PerformanceHistoryService; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx new file mode 100644 index 000000000..5d583e855 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx @@ -0,0 +1,51 @@ +import * as React from 'react'; +import { IDataSetsObject } from '../models/chartTypes'; +import { Line } from 'react-chartjs-2'; +import * as moment from 'moment'; +import { ITimeStamp } from 'models/chartTypes'; + +export const lineChart = (chartPagedData: IDataSetsObject) => { + return ( + <Line ref="chart" data={chartPagedData} options={{ + scales: { + xAxes: [{ + type: 'time', + time: { + displayFormats: { + 'second': 'DD MMM YYYY HH:mm:ss', + 'minute': 'DD MMM YYYY HH:mm:ss', + 'hour': 'DD MMM YYYY HH:mm:ss', + 'year': 'DD MMM YYYY HH:mm:ss', + }, + parser: function (date: string) { + let offsetValue = new Date().getTimezoneOffset(); + var utcDate = moment(date, 'YYYY-MM-DDTHH:mm:ss').utcOffset(offsetValue).utc(false); + return utcDate; + } + }, + display: true, + scaleLabel: { + display: true, + labelString: 'Timestamp' + } + }], + yAxes: [{ + ticks: { + beginAtZero: true + }, + scaleLabel: { + display: true, + labelString: 'Value' + } + }] + } + }} /> + ); +} + +export const sortDataByTimeStamp = <T extends ITimeStamp>(_rows: T[]): T[] => { + return (_rows.sort((a, b) => { + const result = Date.parse(a["time-stamp"]) - Date.parse(b["time-stamp"]); + return isNaN(result) ? 0 : result; + })); +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/tableUtils.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/tableUtils.ts new file mode 100644 index 000000000..ad50e5165 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/tableUtils.ts @@ -0,0 +1,5 @@ +import { ColumnType, ColumnModel } from '../../../../framework/src/components/material-table'; + +export const addColumnLabels = <T>(name: string, title: string, disableFilter = true, disableSorting = true): ColumnModel<T> => { + return { property: name as keyof T, title: title, type: ColumnType.text, disableFilter: disableFilter, disableSorting: disableSorting }; +} diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx index 12027e499..0cadfffe7 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx @@ -10,14 +10,15 @@ import { IApplicationStoreState } from '../../../../framework/src/store/applicat import { Panel } from '../../../../framework/src/components/material-ui'; import { PanelId } from '../models/panelId'; -import { PerformanceData } from '../components/performanceData'; -import { ReceiveLevel } from '../components/receiveLevel'; -import { TransmissionPower } from '../components/transmissionPower'; -import { AdaptiveModulation } from '../components/adaptiveModulation'; -import { Temperature } from '../components/temperature'; -import { SignalToInterference } from '../components/signalToInterference'; -import { CrossPolarDiscrimination } from '../components/crossPolarDiscrimination'; +import PerformanceData from '../components/performanceData'; +import ReceiveLevel from '../components/receiveLevel'; +import TransmissionPower from '../components/transmissionPower'; +import AdaptiveModulation from '../components/adaptiveModulation'; +import Temperature from '../components/temperature'; +import SignalToInterference from '../components/signalToInterference'; +import CrossPolarDiscrimination from '../components/crossPolarDiscrimination'; import { loadAllConnectedNetworkElementsAsync } from '../actions/connectedNetworkElementsActions'; +import { loadAllMountedNetworkElementsAsync } from '../../../connectApp/src/actions/mountedNetworkElementsActions'; import { loadDistinctLtpsbyNetworkElementAsync } from '../actions/ltpAction'; import { SetPanelAction } from '../actions/panelChangeActions'; import { createPerformanceData15minPreActions, performanceData15minReloadAction } from '../handlers/performanceData15minHandler'; @@ -35,6 +36,7 @@ import { createSignalToInterference24hoursPreActions, signalToInterference24hour import { createCrossPolarDiscrimination15minPreActions, crossPolarDiscrimination15minReloadAction } from '../handlers/crossPolarDiscrimination15minHandler'; import { createCrossPolarDiscrimination24hoursPreActions, crossPolarDiscrimination24hoursReloadAction } from '../handlers/crossPolarDiscrimination24hoursHandler'; + const PerformanceHistoryComponentStyles = (theme: Theme) => createStyles({ root: { display: "flex", @@ -61,7 +63,7 @@ const PerformanceHistoryComponentStyles = (theme: Theme) => createStyles({ const mapProps = (state: IApplicationStoreState) => ({ activePanel: state.performanceHistory.currentOpenPanel, availableLtps: state.performanceHistory.ltps.distinctLtps, - networkElements: state.performanceHistory.networkElements.connectedNetworkElements + networkElements: state.performanceHistory.networkElements.connectedNetworkElementIds }); const mapDispatcher = (dispatcher: IDispatcher) => ({ @@ -93,8 +95,11 @@ const mapDispatcher = (dispatcher: IDispatcher) => ({ signalToInterference24hoursPreActions: createSignalToInterference24hoursPreActions(dispatcher.dispatch), crossPolarDiscrimination15minPreActions: createCrossPolarDiscrimination15minPreActions(dispatcher.dispatch), crossPolarDiscrimination24hoursPreActions: createCrossPolarDiscrimination24hoursPreActions(dispatcher.dispatch), - getConnectedNetworkElements: () => dispatcher.dispatch(loadAllConnectedNetworkElementsAsync), - getDistinctLtps: (selectedNetworkElement: string, selectedTimePeriod: string, selectedLtp: string, selectFirstLtp?: Function, resetLTP?: Function) => dispatcher.dispatch(loadDistinctLtpsbyNetworkElementAsync(selectedNetworkElement, selectedTimePeriod, selectedLtp, selectFirstLtp, resetLTP)), + getConnectedNetworkElementsIds: async() => { + await dispatcher.dispatch(loadAllMountedNetworkElementsAsync) + dispatcher.dispatch(loadAllConnectedNetworkElementsAsync); + }, + getDistinctLtpsIds: (selectedNetworkElement: string, selectedTimePeriod: string, selectedLtp: string, selectFirstLtp?: Function, resetLTP?: Function) => dispatcher.dispatch(loadDistinctLtpsbyNetworkElementAsync(selectedNetworkElement, selectedTimePeriod, selectedLtp, selectFirstLtp, resetLTP)), setCurrentPanel: (panelId: PanelId) => dispatcher.dispatch(new SetPanelAction(panelId)) }); @@ -135,58 +140,56 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp this.props.setCurrentPanel(nextActivePanel); switch (nextActivePanel) { case "PerformanceData": - if (this.state.selectedTimePeriod == "15min") { + if (this.state.selectedTimePeriod === "15min") { this.props.reloadPerformanceData15min(); } else { - this.props.reloadPerformanceData24hours(); } break; case "ReceiveLevel": - if (this.state.selectedTimePeriod == "15min") { + if (this.state.selectedTimePeriod === "15min") { this.props.reloadReceiveLevel15min(); } else { this.props.reloadReceiveLevel24hours(); } break; case "TransmissionPower": - if (this.state.selectedTimePeriod == "15min") { + if (this.state.selectedTimePeriod === "15min") { this.props.reloadTransmissionPower15min(); } else { this.props.reloadTransmissionPower24hours(); } break; case "AdaptiveModulation": - if (this.state.selectedTimePeriod == "15min") { + if (this.state.selectedTimePeriod === "15min") { this.props.reloadAdaptiveModulation15min(); } else { this.props.reloadAdaptiveModulation24hours(); } break; case "Temperature": - if (this.state.selectedTimePeriod == "15min") { + if (this.state.selectedTimePeriod === "15min") { this.props.reloadTemperature15min(); } else { this.props.reloadTemperature24hours(); } break; case "SINR": - if (this.state.selectedTimePeriod == "15min") { + if (this.state.selectedTimePeriod === "15min") { this.props.reloadSignalToInterference15min(); } else { this.props.reloadSignalToInterference24hours(); } break; case "CPD": - if (this.state.selectedTimePeriod == "15min") { + if (this.state.selectedTimePeriod === "15min") { this.props.reloadCrossPolarDiscrimination15min(); } else { this.props.reloadCrossPolarDiscrimination24hours(); } break; - case null: - break; default: + // do nothing if all panels are closed break; } } @@ -255,7 +258,7 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp }; public componentDidMount() { - this.props.getConnectedNetworkElements(); + this.props.getConnectedNetworkElementsIds(); } /** @@ -277,7 +280,7 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp "node-name": networkElement, "uuid-interface": ltp }; - if (timePeriod == "15min") { + if (timePeriod === "15min") { this.props.performanceData15minPreActions.onPreFilterChanged(preFilter); this.props.receiveLevel15minPreActions.onPreFilterChanged(preFilter); this.props.transmissionPower15minPreActions.onPreFilterChanged(preFilter); @@ -285,7 +288,7 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp this.props.temperature15minPreActions.onPreFilterChanged(preFilter); this.props.signalToInterference15minPreActions.onPreFilterChanged(preFilter); this.props.crossPolarDiscrimination15minPreActions.onPreFilterChanged(preFilter); - } else if (timePeriod == "24hours") { + } else if (timePeriod === "24hours") { this.props.performanceData24hoursPreActions.onPreFilterChanged(preFilter); this.props.receiveLevel24hoursPreActions.onPreFilterChanged(preFilter); this.props.transmissionPower24hoursPreActions.onPreFilterChanged(preFilter); @@ -313,7 +316,7 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp selectedNetworkElement: event.target.value, selectedLtp: "-1" }); - this.props.getDistinctLtps(event.target.value, this.state.selectedTimePeriod, "-1", this.selectFirstLtp); + this.props.getDistinctLtpsIds(event.target.value, this.state.selectedTimePeriod, "-1", this.selectFirstLtp); } /** @@ -335,10 +338,10 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp this.setState({ selectedTimePeriod: event.target.value, }); - if (event.target.value == "15min") { - this.props.getDistinctLtps(this.state.selectedNetworkElement, event.target.value, this.state.selectedLtp, undefined, this.resetLtpDropdown); - } else if (event.target.value == "24hours") { - this.props.getDistinctLtps(this.state.selectedNetworkElement, event.target.value, this.state.selectedLtp, undefined, this.resetLtpDropdown); + if (event.target.value === "15min") { + this.props.getDistinctLtpsIds(this.state.selectedNetworkElement, event.target.value, this.state.selectedLtp, undefined, this.resetLtpDropdown); + } else if (event.target.value === "24hours") { + this.props.getDistinctLtpsIds(this.state.selectedNetworkElement, event.target.value, this.state.selectedLtp, undefined, this.resetLtpDropdown); } this.preFilterChangeAndReload(this.state.selectedNetworkElement, event.target.value, this.state.selectedLtp); } @@ -359,5 +362,5 @@ class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComp } } -export const PerformanceHistoryApplication = withStyles(PerformanceHistoryComponentStyles)(connect(mapProps, mapDispatcher)(PerformanceHistoryComponent)); +const PerformanceHistoryApplication = withStyles(PerformanceHistoryComponentStyles)(connect(mapProps, mapDispatcher)(PerformanceHistoryComponent)); export default PerformanceHistoryApplication; diff --git a/sdnr/wt/odlux/framework/pom.xml b/sdnr/wt/odlux/framework/pom.xml index 1ffdd397c..f1588cee2 100644 --- a/sdnr/wt/odlux/framework/pom.xml +++ b/sdnr/wt/odlux/framework/pom.xml @@ -16,7 +16,7 @@ <properties> <buildtime>${maven.build.timestamp}</buildtime> <distversion>ONAP Dublin (Flourine-SR2)</distversion> - <buildno>9.ac4a3af(19/06/06)</buildno> + <buildno>10.2befc1b(19/06/07)</buildno> <odlux.version>ONAP SDN-R | ONF Wireless for ${distversion} - Build: ${buildtime} ${buildno} ${project.version}</odlux.version> </properties> <licenses> diff --git a/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts b/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts index 2abe82142..a159cb627 100644 --- a/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts +++ b/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts @@ -3,6 +3,8 @@ import { UpdateAuthentication } from '../actions/authentication'; import { User } from '../models/authentication'; +import { onLogin, onLogout } from '../services/applicationApi'; + export interface IAuthenticationState { user?: User; } @@ -19,8 +21,10 @@ export const authenticationStateHandler: IActionHandler<IAuthenticationState> = const user = action.bearerToken && new User(action.bearerToken) || undefined; if (user) { localStorage.setItem("userToken", user.toString()); + onLogin(); } else { localStorage.removeItem("userToken"); + onLogout(); } state = { diff --git a/sdnr/wt/odlux/framework/src/services/applicationApi.ts b/sdnr/wt/odlux/framework/src/services/applicationApi.ts index bddfb24c6..b097b23b1 100644 --- a/sdnr/wt/odlux/framework/src/services/applicationApi.ts +++ b/sdnr/wt/odlux/framework/src/services/applicationApi.ts @@ -1,10 +1,21 @@ +import { Event } from '../common/event'; import { ApplicationStore } from '../store/applicationStore'; - let resolveApplicationStoreInitialized: (store: ApplicationStore) => void; let applicationStore: ApplicationStore | null = null; const applicationStoreInitialized: Promise<ApplicationStore> = new Promise((resolve) => resolveApplicationStoreInitialized = resolve); +const loginEvent = new Event(); +const logoutEvent = new Event(); + +export const onLogin = () => { + loginEvent.invoke(); +} + +export const onLogout = () => { + logoutEvent.invoke(); +} + export const setApplicationStore = (store: ApplicationStore) => { if (!applicationStore && store) { applicationStore = store; @@ -19,6 +30,14 @@ export const applicationApi = { get applicationStoreInitialized(): Promise<ApplicationStore> { return applicationStoreInitialized; + }, + + get loginEvent() { + return loginEvent; + }, + + get logoutEvent() { + return logoutEvent; } }; diff --git a/sdnr/wt/odlux/yarn.lock b/sdnr/wt/odlux/yarn.lock index c2ef0de90..8c3e51114 100644 --- a/sdnr/wt/odlux/yarn.lock +++ b/sdnr/wt/odlux/yarn.lock @@ -2605,6 +2605,29 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +chart.js@2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.8.0.tgz#b703b10d0f4ec5079eaefdcd6ca32dc8f826e0e9" + integrity sha512-Di3wUL4BFvqI5FB5K26aQ+hvWh8wnP9A3DWGvXHVkO13D3DSnaSsdZx29cXlEsYKVkn1E2az+ZYFS4t0zi8x0w== + dependencies: + chartjs-color "^2.1.0" + moment "^2.10.2" + +chartjs-color-string@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz#1df096621c0e70720a64f4135ea171d051402f71" + integrity sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A== + dependencies: + color-name "^1.0.0" + +chartjs-color@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/chartjs-color/-/chartjs-color-2.3.0.tgz#0e7e1e8dba37eae8415fd3db38bf572007dd958f" + integrity sha512-hEvVheqczsoHD+fZ+tfPUE+1+RbV6b+eksp2LwAhwRTVXEjCSEavvk+Hg3H6SZfGlPh/UfmWKGIvZbtobOEm3g== + dependencies: + chartjs-color-string "^0.6.0" + color-convert "^0.5.3" + chokidar@^2.0.0, chokidar@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" @@ -2734,6 +2757,11 @@ collection-visit@^1.0.0: map-visit "^1.0.0" object-visit "^1.0.0" +color-convert@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd" + integrity sha1-vbbGnOZg+t/+CwAHzER+G59ygr0= + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -2744,6 +2772,11 @@ color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" +color-name@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + colors@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" @@ -3676,9 +3709,9 @@ es-to-primitive@^1.1.1: is-symbol "^1.0.1" es6-promise@^4.0.3: - version "4.2.6" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.6.tgz#b685edd8258886365ea62b57d30de28fadcd974f" - integrity sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q== + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== es6-promisify@^5.0.0: version "5.0.0" @@ -6679,6 +6712,11 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== +moment@^2.10.2: + version "2.24.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" + integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -6694,7 +6732,12 @@ ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" -ms@^2.0.0, ms@^2.1.1: +ms@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" @@ -7726,6 +7769,15 @@ prop-types@15.6.2, prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1, loose-envify "^1.3.1" object-assign "^4.1.1" +prop-types@^15.5.8: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + proto-list@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" @@ -7890,6 +7942,14 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-chartjs-2@2.7.6: + version "2.7.6" + resolved "https://registry.yarnpkg.com/react-chartjs-2/-/react-chartjs-2-2.7.6.tgz#b8cd29bed00bf55b9e8172b06466b4ecf2b86bfb" + integrity sha512-xDr0jhgt/o26atftXxTVsepz+QYZI2GNKBYpxtLvYgwffLUm18a9n562reUJAHvuwKsy2v+qMlK5HyjFtSW0mg== + dependencies: + lodash "^4.17.4" + prop-types "^15.5.8" + react-dom@16.5.2: version "16.5.2" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.5.2.tgz#b69ee47aa20bab5327b2b9d7c1fe2a30f2cfa9d7" @@ -7912,6 +7972,11 @@ react-is@^16.3.2, react-is@^16.6.3: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.7.0.tgz#c1bd21c64f1f1364c6f70695ec02d69392f41bfa" integrity sha512-Z0VRQdF4NPDoI0tsXVMLkJLiwEBa+RP66g0xDHxgxysxSoCUccSten4RTF/UFvZF1dZvZ9Zu1sx+MDXwcOR34g== +react-is@^16.8.1: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" + integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA== + react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" |