diff options
author | Aijana Schumann <aijana.schumann@highstreet-technologies.com> | 2021-08-04 11:59:18 +0200 |
---|---|---|
committer | Aijana Schumann <aijana.schumann@highstreet-technologies.com> | 2021-08-04 16:06:05 +0200 |
commit | 437f67407aece6f7aed8e989638b0d64075f0c0a (patch) | |
tree | 53e9e336cd8544edf8a06c889e33f5b9c98fe083 /sdnr/wt/odlux/apps | |
parent | 1c4995eb199437e9c86336efff9972f2049e1532 (diff) |
Update ODLUX
Add various updates and bugfixes to NetworkMap, Configuration, LinkCalculation and ConnectApp
Issue-ID: CCSDK-3414
Signed-off-by: Aijana Schumann <aijana.schumann@highstreet-technologies.com>
Change-Id: I6ea5c3a9d6ccbe9c450da43220654a53fd2f262b
Signed-off-by: Aijana Schumann <aijana.schumann@highstreet-technologies.com>
Diffstat (limited to 'sdnr/wt/odlux/apps')
78 files changed, 2044 insertions, 1063 deletions
diff --git a/sdnr/wt/odlux/apps/apiDemo/package.json b/sdnr/wt/odlux/apps/apiDemo/package.json index 3805c020e..af47a025e 100644 --- a/sdnr/wt/odlux/apps/apiDemo/package.json +++ b/sdnr/wt/odlux/apps/apiDemo/package.json @@ -27,8 +27,8 @@ "@types/react": "17.0.3", "@types/react-dom": "17.0.2", "@types/react-router-dom": "5.1.7", - "@material-ui/core": "4.11.0", - "@material-ui/icons": "4.9.1", + "@material-ui/core": "4.11.4", + "@material-ui/icons": "4.11.2", "@types/classnames": "2.2.6", "@types/flux": "3.1.8", "@types/jquery": "3.3.10", diff --git a/sdnr/wt/odlux/apps/apiDemo/pom.xml b/sdnr/wt/odlux/apps/apiDemo/pom.xml index 2e9da77fb..6c181cc09 100644 --- a/sdnr/wt/odlux/apps/apiDemo/pom.xml +++ b/sdnr/wt/odlux/apps/apiDemo/pom.xml @@ -140,8 +140,8 @@ <!-- optional: default phase is "generate-resources" --> <phase>initialize</phase> <configuration> - <nodeVersion>v10.16.3</nodeVersion> - <yarnVersion>v1.19.0</yarnVersion> + <nodeVersion>v12.13.0</nodeVersion> + <yarnVersion>v1.22.10</yarnVersion> </configuration> </execution> <execution> diff --git a/sdnr/wt/odlux/apps/configurationApp/package.json b/sdnr/wt/odlux/apps/configurationApp/package.json index 16588cd18..8dd2d041f 100644 --- a/sdnr/wt/odlux/apps/configurationApp/package.json +++ b/sdnr/wt/odlux/apps/configurationApp/package.json @@ -27,8 +27,8 @@ "@types/react": "17.0.3", "@types/react-dom": "17.0.2", "@types/react-router-dom": "5.1.7", - "@material-ui/core": "4.11.0", - "@material-ui/icons": "4.9.1", + "@material-ui/core": "4.11.4", + "@material-ui/icons": "4.11.2", "@types/classnames": "2.2.6", "@types/flux": "3.1.8", "@types/jquery": "3.3.10", diff --git a/sdnr/wt/odlux/apps/configurationApp/pom.xml b/sdnr/wt/odlux/apps/configurationApp/pom.xml index bc6bb5da4..43802970f 100644 --- a/sdnr/wt/odlux/apps/configurationApp/pom.xml +++ b/sdnr/wt/odlux/apps/configurationApp/pom.xml @@ -140,8 +140,8 @@ <!-- optional: default phase is "generate-resources" --> <phase>initialize</phase> <configuration> - <nodeVersion>v10.16.3</nodeVersion> - <yarnVersion>v1.19.0</yarnVersion> + <nodeVersion>v12.13.0</nodeVersion> + <yarnVersion>v1.22.10</yarnVersion> </configuration> </execution> <execution> diff --git a/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts b/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts index f80fbfc4d..b5dd310bc 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts @@ -52,26 +52,20 @@ export const updateNodeIdAsyncActionCreator = (nodeId: string) => async (dispatc dispatch(new UpdateDeviceDescription("", {}, [])); dispatch(new SetCollectingSelectionData(true)); - const { avaliableCapabilities, unavaliableCapabilities } = await restService.getCapabilitiesByMoutId(nodeId); - - if (!avaliableCapabilities || avaliableCapabilities.length <= 0) { + const { availableCapabilities, unavailableCapabilities, importOnlyModules } = await restService.getCapabilitiesByMountId(nodeId); + + if (!availableCapabilities || availableCapabilities.length <= 0) { throw new Error(`NetworkElement : [${nodeId}] has no capabilities.`); } - - const capParser = /^\(.*\?revision=(\d{4}-\d{2}-\d{2})\)(\S+)$/i; - - const parser = new YangParser(unavaliableCapabilities?.map(cap => { - const capMatch = cap && capParser.exec(cap.capability); - return { capability:capMatch && capMatch[2] || '', failureReason: cap.failureReason }; - }) || undefined); - - for (let i = 0; i < avaliableCapabilities.length; ++i){ - const capRaw = avaliableCapabilities[i]; - const capMatch = capRaw && capParser.exec(capRaw.capability); + + const parser = new YangParser(unavailableCapabilities || undefined, importOnlyModules || undefined); + + for (let i = 0; i < availableCapabilities.length; ++i){ + const capRaw = availableCapabilities[i]; try { - capMatch && await parser.addCapability(capMatch[2], capMatch[1]); + await parser.addCapability(capRaw.capability, capRaw.version); } catch (err) { - console.error(`Error in ${capMatch && capMatch[2]} ${capMatch && capMatch[1]}`, err); + console.error(`Error in ${capRaw.capability} ${capRaw.version}`, err); } } diff --git a/sdnr/wt/odlux/apps/configurationApp/src/index.html b/sdnr/wt/odlux/apps/configurationApp/src/index.html index 78fff78c5..4a0496bff 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/index.html +++ b/sdnr/wt/odlux/apps/configurationApp/src/index.html @@ -15,11 +15,13 @@ <script type="text/javascript" src="./config.js"></script> <script> // run the application - require(["app", "connectApp", "maintenanceApp", "configurationApp"], function (app, connectApp, maintenanceApp, configurationApp) { + require(["app", "connectApp", "maintenanceApp", "configurationApp", "faultApp"], function (app, connectApp, maintenanceApp, configurationApp, faultApp) { connectApp.register(); configurationApp.register(); maintenanceApp.register(); - app("./app.tsx").configureApplication({ authentication:"oauth", enablePolicy: true,}); + faultApp.register(); + // app("./app.tsx").configureApplication({ authentication:"oauth", enablePolicy: true,}); + app("./app.tsx").configureApplication({ authentication:"basic", enablePolicy: false,}); app("./app.tsx").runApplication(); }); </script> diff --git a/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts b/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts index e4ab6f59f..10f538c2e 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts @@ -17,12 +17,12 @@ */ import { ViewElement, ViewSpecification } from "./uiModels"; -import { StepLabel } from "@material-ui/core"; export enum ModuleState { stable, instable, - unabaliabe, + importOnly, + unavailable, } export type Token = { diff --git a/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx b/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx index 3bc0e3968..3b9baa657 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx @@ -88,7 +88,7 @@ const ConfigurationApplicationRouteAdapter = connect(undefined, mapDisp)((props: // result += `${indention} [${view.canEdit ? 'rw' : 'ro'}] ${view.ns}:${view.name} ${ds.displayMode === DisplayModeType.displayAsList ? '[LIST]' : ''}\r\n`; result += Object.keys(view.elements).reduce((acc, cur) => { const elm = view.elements[cur]; - acc += `${indention} [${elm.config ? 'rw' : 'ro'}:${elm.id}] (${elm.module}:${elm.label}) {${elm.uiType}} ${elm.uiType === "object" && elm.isList ? `as LIST with KEY [${elm.key}]` : ""}\r\n`; + acc += `${indention} [${elm.uiType === "rpc" ? "x" : elm.config ? 'rw' : 'ro'}:${elm.id}] (${elm.module}:${elm.label}) {${elm.uiType}} ${elm.uiType === "object" && elm.isList ? `as LIST with KEY [${elm.key}]` : ""}\r\n`; // acc += `${indention} +${elm.mandatory ? "mandetory" : "none"} - ${elm.path} \r\n`; switch (elm.uiType) { diff --git a/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts b/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts index bdef64cf2..02060ef12 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts @@ -21,52 +21,101 @@ import { convertPropertyNames, replaceHyphen } from "../../../../framework/src/u import { NetworkElementConnection } from "../models/networkElementConnection"; +type ImportOnlyResponse = { + "ietf-yang-library:yang-library": { + "module-set": { + "import-only-module": { + "name": string, + "revision": string, + }[], + }[], + }, +} + + type CapabilityResponse = { - "network-topology:node": { - "node-id": string, - "netconf-node-topology:available-capabilities": { - "available-capability": { - "capability-origin": string, - "capability": string, - }[] - }, - "netconf-node-topology:unavailable-capabilities": { - "unavailable-capability": { - "capability": string, - "failure-reason": string, - }[] - } - }[] + "network-topology:node": { + "node-id": string, + "netconf-node-topology:available-capabilities": { + "available-capability": { + "capability-origin": string, + "capability": string, + }[] + }, + "netconf-node-topology:unavailable-capabilities": { + "unavailable-capability": { + "capability": string, + "failure-reason": string, + }[] + } + }[] } -type CapabilityAnswer = { - avaliableCapabilities: { - capabilityOrigin: string, - capability: string - }[] | null , - unavaliableCapabilities: { - failureReason: string, - capability: string - }[] | null , +type CapabilityAnswer = { + availableCapabilities: { + capabilityOrigin: string, + capability: string, + version: string, + }[] | null, + unavailableCapabilities: { + failureReason: string, + capability: string, + version: string, + }[] | null, + importOnlyModules: { + name: string, + revision: string, + }[] | null } +const capParser = /^\(.*\?revision=(\d{4}-\d{2}-\d{2})\)(\S+)$/i; + class RestService { public getNetworkElementUri = (nodeId: string) => '/rests/data/network-topology:network-topology/topology=topology-netconf/node=' + nodeId; - public async getCapabilitiesByMoutId(nodeId: string): Promise<CapabilityAnswer> { + public async getImportOnlyModules(nodeId: string): Promise<{ name: string, revision: string }[]> { + const path = `${this.getNetworkElementUri(nodeId)}/yang-ext:mount/ietf-yang-library:yang-library?content=nonconfig&fields=module-set(import-only-module(name;revision))`; + const importOnlyResult = await requestRest<ImportOnlyResponse>(path, { method: "GET" }); + const importOnlyModules = importOnlyResult + ? importOnlyResult["ietf-yang-library:yang-library"]["module-set"][0]["import-only-module"] + : []; + return importOnlyModules; + } + + public async getCapabilitiesByMountId(nodeId: string): Promise<CapabilityAnswer> { const path = this.getNetworkElementUri(nodeId); const capabilitiesResult = await requestRest<CapabilityResponse>(path, { method: "GET" }); - const avaliableCapabilities = capabilitiesResult && capabilitiesResult["network-topology:node"] && capabilitiesResult["network-topology:node"].length > 0 && - capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"] && - capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"]["available-capability"] && - capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"]["available-capability"].map<any>(obj => convertPropertyNames(obj, replaceHyphen)) || []; - - const unavaliableCapabilities = capabilitiesResult && capabilitiesResult["network-topology:node"] && capabilitiesResult["network-topology:node"].length > 0 && - capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"] && - capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"]["unavailable-capability"] && - capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"]["unavailable-capability"].map<any>(obj => convertPropertyNames(obj, replaceHyphen)) || [] - - return { avaliableCapabilities, unavaliableCapabilities }; + const availableCapabilities = capabilitiesResult && capabilitiesResult["network-topology:node"] && capabilitiesResult["network-topology:node"].length > 0 && + (capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"] && + capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"]["available-capability"] && + capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"]["available-capability"].map<any>(obj => convertPropertyNames(obj, replaceHyphen)) || []) + .map(cap => { + const capMatch = cap && capParser.exec(cap.capability); + return capMatch ? { + capabilityOrigin: cap.capabilityOrigin, + capability: capMatch && capMatch[2] || '', + version: capMatch && capMatch[1] || '', + } : null ; + }).filter((cap) => cap != null) || [] as any; + + const unavailableCapabilities = capabilitiesResult && capabilitiesResult["network-topology:node"] && capabilitiesResult["network-topology:node"].length > 0 && + (capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"] && + capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"]["unavailable-capability"] && + capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"]["unavailable-capability"].map<any>(obj => convertPropertyNames(obj, replaceHyphen)) || []) + .map(cap => { + const capMatch = cap && capParser.exec(cap.capability); + return capMatch ? { + failureReason: cap.failureReason, + capability: capMatch && capMatch[2] || '', + version: capMatch && capMatch[1] || '', + } : null ; + }).filter((cap) => cap != null) || [] as any; + + const importOnlyModules = availableCapabilities && availableCapabilities.findIndex((ac: {capability: string }) => ac.capability && ac.capability.toLowerCase() === "ietf-yang-library") > -1 + ? await this.getImportOnlyModules(nodeId) + : null; + + return { availableCapabilities, unavailableCapabilities, importOnlyModules }; } public async getMountedNetworkElementByMountId(nodeId: string): Promise<NetworkElementConnection | null> { @@ -80,7 +129,7 @@ class RestService { return networkElementResult && networkElementResult["data-provider:output"] && networkElementResult["data-provider:output"].data && networkElementResult["data-provider:output"].data.map(obj => convertPropertyNames(obj, replaceHyphen))[0] || null; } - + /** Reads the config data by restconf path. * @param path The restconf path to be used for read. * @returns The data. diff --git a/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx b/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx index db426e814..8d0e19246 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx @@ -578,9 +578,11 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp private renderUIViewList(listSpecification: ViewSpecification, dataPath: string, listKeyProperty: string, apiDocPath: string, listData: { [key: string]: any }[]) { const listElements = listSpecification.elements; - const apiDocPathCreate = apiDocPath ? `${location.origin}${apiDocPath + const apiDocPathCreate = apiDocPath ? `${location.origin}${apiDocPath .replace("$$$standard$$$", "topology-netconfnode%20resources%20-%20RestConf%20RFC%208040") - .replace("$$$action$$$", "put")}_${listKeyProperty.replace(/[\/=\-\:]/g, '_')}_` : undefined; + .replace("$$$action$$$", "put")}${listKeyProperty ? `_${listKeyProperty.replace(/[\/=\-\:]/g, '_')}_` : '' }` : undefined; + + const config = listSpecification.config && listKeyProperty; // We can not configure a list with no key. const navigate = (path: string) => { this.props.history.push(`${this.props.match.url}${path}`); @@ -593,7 +595,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp onClick: () => { navigate("[]"); // empty key means new element }, - disabled: !listSpecification.config, + disabled: !config, }; const addWithApiDocElementAction = { @@ -603,7 +605,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp onClick: () => { window.open(apiDocPathCreate, '_blank'); }, - disabled: !listSpecification.config, + disabled: !config, }; const { classes, removeElement } = this.props; @@ -650,13 +652,13 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp }, []).concat([{ property: "Actions", disableFilter: true, disableSorting: true, type: ColumnType.custom, customControl: (({ rowData }) => { return ( - <DeleteIconWithConfirmation disabled={!listSpecification.config} rowData={rowData} onReload={() => this.props.vPath && this.props.reloadView(this.props.vPath)} /> + <DeleteIconWithConfirmation disabled={!config} rowData={rowData} onReload={() => this.props.vPath && this.props.reloadView(this.props.vPath)} /> ); }) }]) } onHandleClick={(ev, row) => { ev.preventDefault(); - navigate(`[${encodeURIComponent(row[listKeyProperty])}]`); + listKeyProperty && navigate(`[${encodeURIComponent(row[listKeyProperty])}]`); // Do not navigate without key. }} ></SelectElementTable> ); } diff --git a/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts b/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts index 2d38976d5..c80bd4c84 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts @@ -286,7 +286,7 @@ export class YangParser { public static ResolveStack = Symbol("ResolveStack"); - constructor(private _unavailableCapabilities: { failureReason: string; capability: string; }[] = []) { + constructor(private _unavailableCapabilities: { failureReason: string; capability: string; }[] = [], private _importOnlyModules: { name: string; revision: string; }[] = []) { } @@ -298,16 +298,16 @@ export class YangParser { return this._views; } - public async addCapability(capability: string, version?: string) { + public async addCapability(capability: string, version?: string, parentImportOnlyModule?: boolean) { // do not add twice if (this._modules[capability]) { - // console.warn(`Skipped capability: ${capability} since allready contained.` ); + // console.warn(`Skipped capability: ${capability} since already contained.` ); return; } - // // do not add unavaliabe capabilities + // // do not add unavailable capabilities // if (this._unavailableCapabilities.some(c => c.capability === capability)) { - // // console.warn(`Skipped capability: ${capability} since it is marked as unavaliable.` ); + // // console.warn(`Skipped capability: ${capability} since it is marked as unavailable.` ); // return; // } @@ -325,7 +325,8 @@ export class YangParser { throw new Error(`Root element capability ${rootStatement.arg} does not requested ${capability}.`); } - const isUnavaliabe = this._unavailableCapabilities.some(c => c.capability === capability); + const isUnavailable = this._unavailableCapabilities.some(c => c.capability === capability); + const isImportOnly = parentImportOnlyModule === true || this._importOnlyModules.some(c => c.name === capability); const module = this._modules[capability] = { name: rootStatement.arg, @@ -338,9 +339,11 @@ export class YangParser { typedefs: {}, views: {}, elements: {}, - state: isUnavaliabe - ? ModuleState.unabaliabe - : ModuleState.stable, + state: isUnavailable + ? ModuleState.unavailable + : isImportOnly + ? ModuleState.importOnly + : ModuleState.stable, }; await this.handleModule(module, rootStatement, capability); @@ -406,7 +409,7 @@ export class YangParser { // import all required files and set module state if (imports) for (let ind = 0; ind < imports.length; ++ind) { const moduleName = imports[ind].arg!; - await this.addCapability(moduleName); + await this.addCapability(moduleName, undefined, module.state === ModuleState.importOnly); const importedModule = this._modules[imports[ind].arg!]; if (importedModule && importedModule.state > ModuleState.stable) { module.state = Math.max(module.state, ModuleState.instable); @@ -415,7 +418,7 @@ export class YangParser { this.extractTypeDefinitions(rootStatement, module, ""); - this.extractIdentites(rootStatement, 0, module, ""); + this.extractIdentities(rootStatement, 0, module, ""); const groupings = this.extractGroupings(rootStatement, 0, module, ""); this._views.push(...groupings); @@ -440,8 +443,8 @@ export class YangParser { module.views[key] = this._views[viewIdIndex]; } - // add only the UI View if the module is avliable - if (module.state !== ModuleState.unabaliabe) this._views[0].elements[key] = module.elements[key]; + // add only the UI View if the module is available + if (module.state === ModuleState.stable || module.state === ModuleState.instable) this._views[0].elements[key] = module.elements[key]; }); }); return module; @@ -449,7 +452,7 @@ export class YangParser { public postProcess() { - // execute all post processes like resolving in propper order + // execute all post processes like resolving in proper order this._unionsToResolve.forEach(cb => { try { cb(); } catch (error) { console.warn(error.message); @@ -463,7 +466,7 @@ export class YangParser { } }); - // process all augmentations / sort by namespace changes to ensure propper order + // process all augmentations / sort by namespace changes to ensure proper order Object.keys(this.modules).forEach(modKey => { const module = this.modules[modKey]; const augmentKeysWithCounter = Object.keys(module.augments).map((key) => { @@ -517,7 +520,7 @@ export class YangParser { return result; } - const baseIdentites: Identity[] = []; + const baseIdentities: Identity[] = []; Object.keys(this.modules).forEach(modKey => { const module = this.modules[modKey]; Object.keys(module.identities).forEach(idKey => { @@ -526,11 +529,11 @@ export class YangParser { const base = this.resolveIdentity(identity.base, module); base.children?.push(identity); } else { - baseIdentites.push(identity); + baseIdentities.push(identity); } }); }); - baseIdentites.forEach(identity => { + baseIdentities.forEach(identity => { identity.values = identity.children && traverseIdentity(identity.children) || []; }); @@ -598,7 +601,7 @@ export class YangParser { }); } - /** Handles Goupings like named Container */ + /** Handles groupings like named Container */ private extractGroupings(statement: Statement, parentId: number, module: Module, currentPath: string): ViewSpecification[] { const subViews: ViewSpecification[] = []; const groupings = this.extractNodes(statement, "grouping"); @@ -620,7 +623,7 @@ export class YangParser { return subViews; } - /** Handles Augmants also like named Container */ + /** Handles augments also like named container */ private extractAugments(statement: Statement, parentId: number, module: Module, currentPath: string): ViewSpecification[] { const subViews: ViewSpecification[] = []; const augments = this.extractNodes(statement, "augment"); @@ -645,8 +648,8 @@ export class YangParser { return subViews; } - /** Handles Identities */ - private extractIdentites(statement: Statement, parentId: number, module: Module, currentPath: string) { + /** Handles identities */ + private extractIdentities(statement: Statement, parentId: number, module: Module, currentPath: string) { const identities = this.extractNodes(statement, "identity"); module.identities = identities.reduce<{ [name: string]: Identity }>((acc, cur) => { if (!cur.arg) { diff --git a/sdnr/wt/odlux/apps/connectApp/package.json b/sdnr/wt/odlux/apps/connectApp/package.json index 4fae39ccb..a6b23df84 100644 --- a/sdnr/wt/odlux/apps/connectApp/package.json +++ b/sdnr/wt/odlux/apps/connectApp/package.json @@ -27,8 +27,8 @@ "@types/react": "17.0.3", "@types/react-dom": "17.0.2", "@types/react-router-dom": "5.1.7", - "@material-ui/core": "4.11.0", - "@material-ui/icons": "4.9.1", + "@material-ui/core": "4.11.4", + "@material-ui/icons": "4.11.2", "@types/classnames": "2.2.6", "@types/flux": "3.1.8", "@types/jquery": "3.3.10", diff --git a/sdnr/wt/odlux/apps/connectApp/pom.xml b/sdnr/wt/odlux/apps/connectApp/pom.xml index e3ca9dd4d..117a374c4 100644 --- a/sdnr/wt/odlux/apps/connectApp/pom.xml +++ b/sdnr/wt/odlux/apps/connectApp/pom.xml @@ -140,8 +140,8 @@ <!-- optional: default phase is "generate-resources" --> <phase>initialize</phase> <configuration> - <nodeVersion>v10.16.3</nodeVersion> - <yarnVersion>v1.19.0</yarnVersion> + <nodeVersion>v12.13.0</nodeVersion> + <yarnVersion>v1.22.10</yarnVersion> </configuration> </execution> <execution> diff --git a/sdnr/wt/odlux/apps/connectApp/src/actions/tlsKeyActions.ts b/sdnr/wt/odlux/apps/connectApp/src/actions/tlsKeyActions.ts new file mode 100644 index 000000000..1da16d9ad --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/actions/tlsKeyActions.ts @@ -0,0 +1,60 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ +import { Action } from '../../../../framework/src/flux/action'; +import { Dispatch } from '../../../../framework/src/flux/store'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +import { TlsKeys } from '../models/networkElementConnection'; +import { connectService } from '../services/connectService'; + +/** + * Represents the base action. + */ +export class BaseAction extends Action { } + +/** + * Represents an action causing the store to load all TLS Keys. + */ +export class LoadAllTlsKeyListAction extends BaseAction { } + +/** + * Represents an action causing the store to get all TLS Keys. + */ +export class AllTlsKeyListLoadedAction extends BaseAction { + /** + * Initialize this instance. + * + * @param gets all the tlsKey list from the database. + */ + constructor(public tlsList: TlsKeys[] | null, public error?: string) { + super(); + } +} + +/** + * Represents an asynchronous thunk action to load all tlsKeys + */ + +export const loadAllTlsKeyListAsync = () => async (dispatch: Dispatch) => { + dispatch(new LoadAllTlsKeyListAction()); + connectService.getTlsKeys().then(TlsKeyList => { + dispatch(new AllTlsKeyListLoadedAction(TlsKeyList)); + }).catch(error => { + dispatch(new AllTlsKeyListLoadedAction(null, error)); + }); +}; diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx index 7d2f96af3..5a5ebcc45 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx @@ -54,7 +54,7 @@ class ConnectionStatusLogComponent extends React.Component<ConnectionStatusLogCo render(): JSX.Element { const refreshConnectionStatusLogAction = { - icon: Refresh, tooltip: 'Refresh Connection Status Log Table', ariaLabel:'refresh', onClick: () => { + icon: Refresh, tooltip: 'Refresh Connection Status Log Table',ariaLabel:'refresh', onClick: () => { this.setState({ refreshConnectionStatusLogEditorMode: RefreshConnectionStatusLogDialogMode.RefreshConnectionStatusLogTable }); diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx index 97e6647cf..df265c23d 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx @@ -24,7 +24,9 @@ import DialogActions from '@material-ui/core/DialogActions'; import DialogContent from '@material-ui/core/DialogContent'; import DialogContentText from '@material-ui/core/DialogContentText'; import DialogTitle from '@material-ui/core/DialogTitle'; -import { FormControl, InputLabel, Select, MenuItem, Typography } from '@material-ui/core'; +import { FormControl, InputLabel, Select, MenuItem, Typography, Radio, RadioGroup, Options, FormLabel, FormControlLabel } from '@material-ui/core'; +import { loadAllTlsKeyListAsync } from '../actions/tlsKeyActions'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect'; @@ -67,13 +69,13 @@ const mapDispatch = (dispatcher: IDispatcher) => ({ //make sure properties are there in case they get renamed const idProperty = propertyOf<UpdateNetworkElement>("id"); const isRequiredProperty = propertyOf<UpdateNetworkElement>("isRequired"); - + if (values.length === 2 && values.includes(idProperty as string) && values.includes(isRequiredProperty as string)) { // do not mount network element, if only isRequired is changed await dispatcher.dispatch(editNetworkElementAsyncActionCreator(element)); - } else if(!(values.length===1 &&values.includes(idProperty as string))) { //do not edit or mount element, if only id was saved into object (no changes made!) + } else if (!(values.length === 1 && values.includes(idProperty as string))) { //do not edit or mount element, if only id was saved into object (no changes made!) await dispatcher.dispatch(editNetworkElementAsyncActionCreator(element)); await dispatcher.dispatch(mountNetworkElementAsyncActionCreator(mountElement)); } @@ -81,7 +83,8 @@ const mapDispatch = (dispatcher: IDispatcher) => ({ removeNetworkElement: async (element: UpdateNetworkElement) => { await dispatcher.dispatch(removeNetworkElementAsyncActionCreator(element)); dispatcher.dispatch(removeWebUriAction(element.id)); - } + }, + getAvailableTlsKeys: async () => await dispatcher.dispatch(loadAllTlsKeyListAsync()), }); type DialogSettings = { @@ -156,26 +159,62 @@ type EditNetworkElementDialogComponentProps = Connect<undefined, typeof mapDispa mode: EditNetworkElementDialogMode; initialNetworkElement: NetworkElementConnection; onClose: () => void; + radioChecked: string }; -type EditNetworkElementDialogComponentState = NetworkElementConnection & { isNameValid: boolean, isHostSet: boolean }; +type EditNetworkElementDialogComponentState = NetworkElementConnection & { + isNameValid: boolean, + isHostSet: boolean, + isPasswordSelected: boolean, + isTlsSelected: boolean, + radioSelected: string, + showPasswordTextField: boolean, + showTlsDropdown: boolean +}; class EditNetworkElementDialogComponent extends React.Component<EditNetworkElementDialogComponentProps, EditNetworkElementDialogComponentState> { constructor(props: EditNetworkElementDialogComponentProps) { super(props); - + this.handleRadioChange = this.handleRadioChange.bind(this); this.state = { nodeId: this.props.initialNetworkElement.nodeId, isRequired: false, host: this.props.initialNetworkElement.host, port: this.props.initialNetworkElement.port, isNameValid: true, - isHostSet: true + isHostSet: true, + isPasswordSelected: true, + isTlsSelected: false, + radioSelected: '', + showPasswordTextField: true, + showTlsDropdown: false }; } + public handleRadioChange = (event: any) => { + this.setState({ + radioSelected: event.target.value, + showPasswordTextField: event.target.value === 'password', + showTlsDropdown: event.target.value === 'tlsKey' + }); + } render(): JSX.Element { const setting = settings[this.props.mode]; + let { showPasswordTextField, showTlsDropdown, radioSelected } = this.state; + radioSelected = this.state.radioSelected.length > 0 ? this.state.radioSelected : this.props.radioChecked; + + if (radioSelected === 'password') { + radioSelected = 'password'; + showPasswordTextField = true; + showTlsDropdown = false; + } else if (radioSelected === 'tlsKey') { + radioSelected = 'tlsKey'; + showPasswordTextField = false; + showTlsDropdown = true; + } + + let tlsKeysList = this.props.state.connect.availableTlsKeys ? this.props.state.connect.availableTlsKeys.tlsKeysList ? this.props.state.connect.availableTlsKeys.tlsKeysList : [] : [] + return ( <Dialog open={this.props.mode !== EditNetworkElementDialogMode.None}> <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, "-").toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle> @@ -187,9 +226,39 @@ class EditNetworkElementDialogComponent extends React.Component<EditNetworkEleme {!this.state.isNameValid && <Typography variant="body1" color="error">Name cannot be empty.</Typography>} <TextField disabled={!setting.enableMountIdEditor} spellCheck={false} margin="dense" id="ipaddress" label="IP address" aria-label="ip adress" type="text" fullWidth value={this.state.host} onChange={(event) => { this.setState({ host: event.target.value }); }} /> {!this.state.isHostSet && <Typography variant="body1" color="error">IP Adress cannot be empty.</Typography>} + <TextField disabled={!setting.enableMountIdEditor} spellCheck={false} margin="dense" id="netconfport" label="NetConf port" aria-label="netconf port" type="number" fullWidth value={this.state.port.toString()} onChange={(event) => { this.setState({ port: +event.target.value }); }} /> {setting.enableUsernameEditor && <TextField disabled={!setting.enableUsernameEditor} spellCheck={false} margin="dense" id="username" label="Username" aria-label="username" type="text" fullWidth value={this.state.username} onChange={(event) => { this.setState({ username: event.target.value }); }} /> || null} - {setting.enableUsernameEditor && <TextField disabled={!setting.enableUsernameEditor} spellCheck={false} margin="dense" id="password" label="Password" aria-label="password" type="password" fullWidth value={this.state.password} onChange={(event) => { this.setState({ password: event.target.value }); }} /> || null} + + {setting.enableUsernameEditor && + <RadioGroup row aria-label="password-tls-key" name="password-tls-key" value={radioSelected} + onChange={this.handleRadioChange} > + <FormControlLabel value='password' control={<Radio />} label="Password" onChange={this.onRadioSelect} /> + <FormControlLabel value='tlsKey' control={<Radio />} label="TlsKey" onChange={this.onRadioSelect} /> + </RadioGroup> || null} + + {setting.enableUsernameEditor && showPasswordTextField && + <TextField disabled={!setting.enableUsernameEditor || !showPasswordTextField} spellCheck={false} margin="dense" + id="password" aria-label="password" type="password" fullWidth value={this.state.password} + onChange={(event) => { this.setState({ password: event.target.value }); }} + /> || null} + + <FormControl fullWidth disabled={!setting.enableUsernameEditor}> + {setting.enableUsernameEditor && showTlsDropdown && + <div> + <InputLabel htmlFor="pass">--Select tls-key--</InputLabel> + <Select disabled={!setting.enableUsernameEditor || !showTlsDropdown} + id="tlsKey" aria-label="tlsKey" value={this.state.tlsKey} fullWidth // displayEmpty + onChange={(event) => { this.setState({ tlsKey: event.target.value as any }); }} + inputProps={{ name: 'tlsKey', id: 'tlsKey' }} > + <MenuItem value={""} disabled >--Select tls-key--</MenuItem> + {tlsKeysList.map(tlsKey => + (<MenuItem value={tlsKey.key} key={tlsKey.key} aria-label={tlsKey.key} >{tlsKey.key}</MenuItem>))} + </Select> + </div> + } + </FormControl> + <FormControl fullWidth disabled={!setting.enableUsernameEditor}> <InputLabel htmlFor="active">Required</InputLabel> <Select aria-label="required-selection" value={this.state.isRequired || false} onChange={(event) => { @@ -212,6 +281,7 @@ class EditNetworkElementDialogComponent extends React.Component<EditNetworkEleme port: this.state.port, username: this.state.username, password: this.state.password, + tlsKey: this.state.tlsKey }); } event.preventDefault(); @@ -227,14 +297,44 @@ class EditNetworkElementDialogComponent extends React.Component<EditNetworkEleme ) } + public renderTlsKeys = () => { + try { + this.props.getAvailableTlsKeys(); + } catch (err) { + console.log(err); + } + } + + public componentDidMount() { + this.renderTlsKeys(); + } + + public onRadioSelect = (e: any) => { + if (e.target.value == 'password') { + this.setState({ isPasswordSelected: true, isTlsSelected: false }) + } else if (e.target.value == 'tlsKey') { + this.setState({ isPasswordSelected: false, isTlsSelected: true }) + } + }; + private onApply = (element: NetworkElementConnection) => { this.props.onClose && this.props.onClose(); let updateElement: UpdateNetworkElement = { id: this.state.nodeId } + if (this.state.isPasswordSelected) { + element.tlsKey = '' + } + else if (this.state.isTlsSelected) { //check here + element.password = '' + } + switch (this.props.mode) { case EditNetworkElementDialogMode.AddNewNetworkElement: element && this.props.addNewNetworkElement(element); + this.setState({ + radioSelected: '' + }); break; case EditNetworkElementDialogMode.MountNetworkElement: element && this.props.mountNetworkElement(element); @@ -247,22 +347,31 @@ class EditNetworkElementDialogComponent extends React.Component<EditNetworkEleme updateElement.isRequired = this.state.isRequired; if (this.props.initialNetworkElement.username !== this.state.username) updateElement.username = this.state.username; - if (this.props.initialNetworkElement.password !== this.state.password) + if (this.props.initialNetworkElement.password !== this.state.password && this.state.isPasswordSelected) { updateElement.password = this.state.password; + updateElement.tlsKey = ''; + } + if (this.props.initialNetworkElement.tlsKey !== this.state.tlsKey && this.state.isTlsSelected) { + updateElement.tlsKey = this.state.tlsKey; + updateElement.password = ''; + } element && this.props.editNetworkElement(updateElement, element); + this.setState({ + radioSelected: '' + }); break; case EditNetworkElementDialogMode.RemoveNetworkElement: element && this.props.removeNetworkElement(updateElement); break; } - this.setState({ password: '', username: '' }); + this.setState({ password: '', username: '', tlsKey: '' }); this.resetRequieredFields(); }; private onCancel = () => { this.props.onClose && this.props.onClose(); - this.setState({ password: '', username: '' }); + this.setState({ password: '', username: '', tlsKey: '', radioSelected: '' }); this.resetRequieredFields(); } diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx index 73706f678..5d0757ab2 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx @@ -19,7 +19,7 @@ import * as React from 'react'; import { Theme, createStyles, withStyles, WithStyles } from '@material-ui/core/styles'; import AddIcon from '@material-ui/icons/Add'; -import Refresh from '@material-ui/icons/Refresh'; +import Refresh from '@material-ui/icons/Refresh'; import LinkIcon from '@material-ui/icons/Link'; import LinkOffIcon from '@material-ui/icons/LinkOff'; import RemoveIcon from '@material-ui/icons/RemoveCircleOutline'; @@ -68,18 +68,18 @@ const styles = (theme: Theme) => createStyles({ }); type GetStatelessComponentProps<T> = T extends (props: infer P & { children?: React.ReactNode }) => any ? P : any -const MenuItemExt : React.FC<GetStatelessComponentProps<typeof MenuItem>> = (props) => { +const MenuItemExt: React.FC<GetStatelessComponentProps<typeof MenuItem>> = (props) => { const [disabled, setDisabled] = React.useState(true); const onMouseDown = (ev: React.MouseEvent<HTMLElement>) => { - if (ev.button ===1){ - setDisabled(!disabled); - ev.preventDefault(); - } + if (ev.button === 1) { + setDisabled(!disabled); + ev.preventDefault(); + } }; return ( <div onMouseDown={onMouseDown} > - <MenuItem {...{...props, disabled: props.disabled && disabled }} /> - </div> + <MenuItem {...{ ...props, disabled: props.disabled && disabled }} /> + </div> ); }; @@ -119,17 +119,17 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement networkElementEditorMode: EditNetworkElementDialogMode.None, refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.None, elementInfo: null, - elementInfoFeature:null, + elementInfoFeature: null, infoNetworkElementEditorMode: InfoNetworkElementDialogMode.None }; } - + getContextMenu(rowData: NetworkElementConnection): JSX.Element[] { const mountUri = rowData.id && connectService.getNetworkElementUri(rowData.id); const mountPolicy = mountUri && getAccessPolicyByUrl(mountUri); - const canMount = mountPolicy && mountPolicy.POST || false; - - const { configuration} = this.props.applicationState as any; + const canMount = mountPolicy && mountPolicy.POST || false; + + const { configuration } = this.props.applicationState as any; const buttonArray = [ <MenuItemExt aria-label={"mount-button"} onClick={event => this.onOpenMountdNetworkElementsDialog(event, rowData)} disabled={!canMount} ><LinkIcon /><Typography>Mount</Typography></MenuItemExt>, <MenuItemExt aria-label={"unmount-button"} onClick={event => this.onOpenUnmountdNetworkElementsDialog(event, rowData)} disabled={!canMount} ><LinkOffIcon /><Typography>Unmount</Typography></MenuItemExt>, @@ -160,6 +160,12 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement render(): JSX.Element { const { classes } = this.props; const { networkElementToEdit } = this.state; + let savedRadio = "password"; + if (this.state.networkElementToEdit.password && this.state.networkElementToEdit.password.length > 0) { + savedRadio = 'password' + } else if (this.state.networkElementToEdit.tlsKey && this.state.networkElementToEdit.tlsKey.length > 0) { + savedRadio = 'tlsKey' + } // const mountUri = rowData.id && connectService.getNetworkElementUri(rowData.id); // const mountPolicy = mountUri && getAccessPolicyByUrl(mountUri); @@ -167,7 +173,7 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement const canAdd = true; const addRequireNetworkElementAction = { - icon: AddIcon, tooltip: 'Add', ariaLabel:"add-element", onClick: () => { + icon: AddIcon, tooltip: 'Add', ariaLabel: "add-element", onClick: () => { this.setState({ networkElementEditorMode: EditNetworkElementDialogMode.AddNewNetworkElement, networkElementToEdit: emptyRequireNetworkElement, @@ -176,16 +182,16 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement }; const refreshNetworkElementsAction = { - icon: Refresh, tooltip: 'Refresh Network Elements table', ariaLabel:'refresh', onClick: () => { + icon: Refresh, tooltip: 'Refresh Network Elements table', ariaLabel: 'refresh', onClick: () => { this.setState({ refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.RefreshNetworkElementsTable }); } }; - + return ( <> - <NetworkElementTable stickyHeader tableId="network-element-table" customActionButtons={[refreshNetworkElementsAction, ...canAdd ? [addRequireNetworkElementAction]: []]} columns={[ + <NetworkElementTable stickyHeader tableId="network-element-table" customActionButtons={[refreshNetworkElementsAction, ...canAdd ? [addRequireNetworkElementAction] : []]} columns={[ { property: "nodeId", title: "Node Name", type: ColumnType.text }, { property: "isRequired", title: "Required", type: ColumnType.boolean }, { property: "status", title: "Connection Status", type: ColumnType.text }, @@ -202,6 +208,7 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement initialNetworkElement={networkElementToEdit} mode={this.state.networkElementEditorMode} onClose={this.onCloseEditNetworkElementDialog} + radioChecked={savedRadio} /> <RefreshNetworkElementsDialog mode={this.state.refreshNetworkElementsEditorMode} @@ -240,6 +247,11 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement } private onOpenEditNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => { + let radioSaved; + if (element.password && element.password.length > 0) + radioSaved = 'password' + else if (element.tlsKey && element.tlsKey.length > 0) + radioSaved = 'tlsKey' this.setState({ networkElementToEdit: { nodeId: element.nodeId, @@ -248,6 +260,7 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement port: element.port, username: element.username, password: element.password, + tlsKey: element.tlsKey }, networkElementEditorMode: EditNetworkElementDialogMode.EditNetworkElement }); diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts index 81ee97a0a..dbb9b2c04 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts @@ -25,6 +25,7 @@ import { SetPanelAction, AddWebUriList, RemoveWebUri, SetWeburiSearchBusy } from import { PanelId } from '../models/panelId'; import { guiCutThrough } from '../models/guiCutTrough'; import { connectionStatusCountHandler, IConnectionStatusCount } from './connectionStatusCountHandler'; +import { availableTlsKeysActionHandler, IAvailableTlsKeysState } from './tlsKeyHandler'; export interface IConnectAppStoreState { networkElements: INetworkElementsState; @@ -34,6 +35,7 @@ export interface IConnectAppStoreState { elementFeatureInfo: IInfoNetworkElementFeaturesState; guiCutThrough: guiCutThroughState; connectionStatusCount: IConnectionStatusCount; + availableTlsKeys: IAvailableTlsKeysState } const currentOpenPanelHandler: IActionHandler<PanelId> = (state = null, action) => { @@ -92,7 +94,8 @@ const actionHandlers = { elementInfo: infoNetworkElementsActionHandler, elementFeatureInfo: infoNetworkElementFeaturesActionHandler, guiCutThrough: guiCutThroughHandler, - connectionStatusCount: connectionStatusCountHandler + connectionStatusCount: connectionStatusCountHandler, + availableTlsKeys: availableTlsKeysActionHandler }; export const connectAppRootHandler = combineActionHandler<IConnectAppStoreState>(actionHandlers); diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/tlsKeyHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/tlsKeyHandler.ts new file mode 100644 index 000000000..326b3cc8e --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/tlsKeyHandler.ts @@ -0,0 +1,55 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ +import { IActionHandler } from '../../../../framework/src/flux/action'; + +import { AllTlsKeyListLoadedAction, LoadAllTlsKeyListAction } from '../actions/tlsKeyActions'; +import { TlsKeys } from '../models/networkElementConnection'; + +export interface IAvailableTlsKeysState { + tlsKeysList: TlsKeys[]; + busy: boolean; +} + +const tlsKeysStateInit: IAvailableTlsKeysState = { + tlsKeysList: [], + busy: false, +}; + +export const availableTlsKeysActionHandler: IActionHandler<IAvailableTlsKeysState> = (state = tlsKeysStateInit, action) => { + if (action instanceof LoadAllTlsKeyListAction) { + state = { + ...state, + busy: true + }; + + } else if (action instanceof AllTlsKeyListLoadedAction) { + if (!action.error && action.tlsList) { + state = { + ...state, + tlsKeysList: action.tlsList, + busy: false, + }; + } else { + state = { + ...state, + busy: false + }; + } + } + return state; +};
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src/index.html b/sdnr/wt/odlux/apps/connectApp/src/index.html index 6f44c25f7..35dbdf71d 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/index.html +++ b/sdnr/wt/odlux/apps/connectApp/src/index.html @@ -19,7 +19,7 @@ connectApp.register(); faultApp.register(); inventoryApp.register(); - app("./app.tsx").configureApplication({ authentication:"basic", enablePolicy: false,}); + app("./app.tsx").configureApplication({ authentication:"oauth", enablePolicy: false, transportpceUrl:"http://test.de"}); app("./app.tsx").runApplication(); }); </script> diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnection.ts b/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnection.ts index 71eddc808..89070ab39 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnection.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnection.ts @@ -24,6 +24,7 @@ export type NetworkElementConnection = { port: number; username?: string; password?: string; + tlsKey?: string; weburi?: string; isWebUriUnreachable?: boolean; status?: "Connected" | "mounted" | "unmounted" | "Connecting" | "Disconnected" | "idle"; @@ -47,12 +48,18 @@ export type UpdateNetworkElement = { isRequired?: boolean; username?: string; password?: string; + tlsKey?: string; } export type ConnectionStatus = { status: string } +export type TlsKeys = { + key: string +} + + /** * Checks if a object has a given propertyname, if yes, the name is returned as string. * @throws at compile time if property is not available diff --git a/sdnr/wt/odlux/apps/connectApp/src/pluginConnect.tsx b/sdnr/wt/odlux/apps/connectApp/src/pluginConnect.tsx index 461e14023..1990cc03d 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/pluginConnect.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/pluginConnect.tsx @@ -88,9 +88,9 @@ export function register() { subscribe<IFormatedMessage>(["object-creation-notification", "object-deletion-notification", "attribute-value-changed-notification"], (msg => { const store = applicationApi.applicationStore; if (msg && msg.type.type === "object-creation-notification" && store) { - store.dispatch(new AddSnackbarNotification({ message: `Adding network element [${msg['node-id']}]`, options: { variant: 'info' } })); + store.dispatch(new AddSnackbarNotification({ message: `Adding network element [${msg.data['object-id-ref']}]`, options: { variant: 'info' } })); } else if (msg && (msg.type.type === "object-deletion-notification" || msg.type.type === "attribute-value-changed-notification") && store) { - store.dispatch(new AddSnackbarNotification({ message: `Updating network element [${msg['node-id']}]`, options: { variant: 'info' } })); + store.dispatch(new AddSnackbarNotification({ message: `Updating network element [${msg.data['object-id-ref']}]`, options: { variant: 'info' } })); } if (store) { store.dispatch(updateCurrentViewAsyncAction() as any).then(() => { diff --git a/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts b/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts index ed8b0f67d..427acd3ec 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts @@ -16,243 +16,289 @@ * ============LICENSE_END========================================================================== */ - import { requestRest } from '../../../../framework/src/services/restService'; - import { NetworkElementConnection, ConnectionStatus, UpdateNetworkElement } from '../models/networkElementConnection'; - import { convertPropertyNames, replaceUpperCase } from '../../../../framework/src/utilities/yangHelper'; - import { Result } from '../../../../framework/src/models/elasticSearch'; - - import { FeatureTopology, Topology, TopologyNode, Module } from '../models/topologyNetconf'; - import { guiCutThrough } from '../models/guiCutTrough'; - - /** - * Represents a web api accessor service for all Network Elements actions. - */ - class ConnectService { - public getNetworkElementUri = (nodeId: string) => '/rests/data/network-topology:network-topology/topology=topology-netconf/node=' + nodeId; - public getNetworkElementConnectDataProviderUri = (operation: "create" | "update" | "delete" ) => `/rests/operations/data-provider:${operation}-network-element-connection`; - public getAllWebUriExtensionsForNetworkElementListUri = (nodeId: string) => this.getNetworkElementUri(nodeId) + '/yang-ext:mount/core-model:network-element'; - public getNetworkElementYangLibraryFeature = (nodeId: string) => '/rests/data/network-topology:network-topology/topology=topology-netconf/node=' + nodeId + '/yang-ext:mount/ietf-yang-library:yang-library?content=nonconfig' - - /** - * Inserts a network elements. - */ - public async createNetworkElement(element: NetworkElementConnection): Promise<NetworkElementConnection | null> { - const path = this.getNetworkElementConnectDataProviderUri("create") ; - const result = await requestRest<NetworkElementConnection>(path, { - method: "POST", body: JSON.stringify(convertPropertyNames({ "data-provider:input": element }, replaceUpperCase)) - }); - return result || null; - } - - /** - * Updates a network element. +import { requestRest } from '../../../../framework/src/services/restService'; +import { NetworkElementConnection, ConnectionStatus, UpdateNetworkElement } from '../models/networkElementConnection'; +import { TlsKeys } from '../models/networkElementConnection' +import { convertPropertyNames, replaceUpperCase } from '../../../../framework/src/utilities/yangHelper'; +import { Result } from '../../../../framework/src/models/elasticSearch'; + +import { FeatureTopology, Topology, TopologyNode, Module } from '../models/topologyNetconf'; +import { guiCutThrough } from '../models/guiCutTrough'; + +/** +* Represents a web api accessor service for all Network Elements actions. +*/ +class ConnectService { + public getNetworkElementUri = (nodeId: string) => '/rests/data/network-topology:network-topology/topology=topology-netconf/node=' + nodeId; + public getNetworkElementConnectDataProviderUri = (operation: "create" | "update" | "delete") => `/rests/operations/data-provider:${operation}-network-element-connection`; + public getAllWebUriExtensionsForNetworkElementListUri = (nodeId: string) => this.getNetworkElementUri(nodeId) + '/yang-ext:mount/core-model:network-element'; + public getNetworkElementYangLibraryFeature = (nodeId: string) => '/rests/data/network-topology:network-topology/topology=topology-netconf/node=' + nodeId + '/yang-ext:mount/ietf-yang-library:yang-library?content=nonconfig' + + /** + * Inserts a network elements. */ - public async updateNetworkElement(element: UpdateNetworkElement): Promise<NetworkElementConnection | null> { - const path = this.getNetworkElementConnectDataProviderUri("update"); - const result = await requestRest<NetworkElementConnection>(path, { - method: "POST", body: JSON.stringify(convertPropertyNames({ "data-provider:input": element }, replaceUpperCase)) - }); - return result || null; - } - - /** - * Deletes a network element. - */ - public async deleteNetworkElement(element: UpdateNetworkElement): Promise<NetworkElementConnection | null> { - const query = { - "id": element.id - }; - const path = this.getNetworkElementConnectDataProviderUri("delete"); - const result = await requestRest<NetworkElementConnection>(path, { - method: "POST", body: JSON.stringify(convertPropertyNames({ "data-provider:input": query }, replaceUpperCase)) - }); - return result || null; - } - - /** Mounts network element. */ - public async mountNetworkElement(networkElement: NetworkElementConnection): Promise<boolean> { - const path = this.getNetworkElementUri(networkElement.nodeId); - const mountXml = [ - '<node xmlns="urn:TBD:params:xml:ns:yang:network-topology">', - `<node-id>${networkElement.nodeId}</node-id>`, - `<host xmlns="urn:opendaylight:netconf-node-topology">${networkElement.host}</host>`, - `<port xmlns="urn:opendaylight:netconf-node-topology">${networkElement.port}</port>`, - `<username xmlns="urn:opendaylight:netconf-node-topology">${networkElement.username}</username>`, - `<password xmlns="urn:opendaylight:netconf-node-topology">${networkElement.password}</password>`, - ' <tcp-only xmlns="urn:opendaylight:netconf-node-topology">false</tcp-only>', - - ' <!-- non-mandatory fields with default values, you can safely remove these if you do not wish to override any of these values-->', - ' <reconnect-on-changed-schema xmlns="urn:opendaylight:netconf-node-topology">false</reconnect-on-changed-schema>', - ' <connection-timeout-millis xmlns="urn:opendaylight:netconf-node-topology">20000</connection-timeout-millis>', - ' <max-connection-attempts xmlns="urn:opendaylight:netconf-node-topology">100</max-connection-attempts>', - ' <between-attempts-timeout-millis xmlns="urn:opendaylight:netconf-node-topology">2000</between-attempts-timeout-millis>', - ' <sleep-factor xmlns="urn:opendaylight:netconf-node-topology">1.5</sleep-factor>', - - ' <!-- keepalive-delay set to 0 turns off keepalives-->', - ' <keepalive-delay xmlns="urn:opendaylight:netconf-node-topology">120</keepalive-delay>', - '</node>'].join(''); - - try { - const result = await requestRest<string>(path, { - method: 'PUT', - headers: { - 'Content-Type': 'application/xml', - 'Accept': 'application/xml' - }, - body: mountXml - }); - // expect an empty answer - return result !== null; - } catch { - return false; - } - }; - - /** Unmounts a network element by its id. */ - public async unmountNetworkElement(nodeId: string): Promise<boolean> { - const path = this.getNetworkElementUri(nodeId); - - try { - const result = await requestRest<string>(path, { - method: 'DELETE', - headers: { - 'Content-Type': 'application/xml', - 'Accept': 'application/xml' - }, - }); - // expect an empty answer - return result !== null; - - } catch { - return false; - } - }; - - /** Yang capabilities of the selected network elements. */ - public async infoNetworkElement(nodeId: string): Promise<TopologyNode | null> { - const path = this.getNetworkElementUri(nodeId); - const topologyRequestPomise = requestRest<Topology>(path, { method: "GET" }); - - return topologyRequestPomise && topologyRequestPomise.then(result => { - return result && result["network-topology:node"] && result["network-topology:node"][0] || null; - }); - } - - - /** Yang features of the selected network element module. */ - public async infoNetworkElementFeatures(nodeId: string): Promise<Module[] | null | undefined> { - const path = this.getNetworkElementYangLibraryFeature(nodeId); - const topologyRequestPomise = requestRest<FeatureTopology>(path, { method: "GET" }); - - return topologyRequestPomise && topologyRequestPomise.then(result => { - const resultFinal = result && result['ietf-yang-library:yang-library'] - && result["ietf-yang-library:yang-library"]["module-set"] && - result["ietf-yang-library:yang-library"]["module-set"][0] && - result["ietf-yang-library:yang-library"]["module-set"][0]['module'] || null; - return resultFinal; - }); - } - - - - /** - * Get the connection state of the network element. + public async createNetworkElement(element: NetworkElementConnection): Promise<NetworkElementConnection | null> { + const path = this.getNetworkElementConnectDataProviderUri("create"); + const result = await requestRest<NetworkElementConnection>(path, { + method: "POST", body: JSON.stringify(convertPropertyNames({ "data-provider:input": element }, replaceUpperCase)) + }); + return result || null; + } + + /** + * Updates a network element. + */ + public async updateNetworkElement(element: UpdateNetworkElement): Promise<NetworkElementConnection | null> { + const path = this.getNetworkElementConnectDataProviderUri("update"); + const result = await requestRest<NetworkElementConnection>(path, { + method: "POST", body: JSON.stringify(convertPropertyNames({ "data-provider:input": element }, replaceUpperCase)) + }); + return result || null; + } + + /** + * Deletes a network element. */ - public async getNetworkElementConnectionStatus(element: string): Promise<(ConnectionStatus)[] | null> { - const path = `/rests/operations/data-provider:read-network-element-connection-list`; - const query = { - "data-provider:input": { - "filter": [{ - "property": "node-id", - "filtervalue": element - }], - "pagination": { - "size": 20, - "page": 1 - } - } - } - const result = await requestRest<Result<ConnectionStatus>>(path, { method: "POST", body: JSON.stringify(query) }); - return result && result["data-provider:output"] && result["data-provider:output"].data && result["data-provider:output"].data.map(ne => ({ - status: ne.status - })) || null; - } - - public async getAllWebUriExtensionsForNetworkElementListAsync(neList: string[]): Promise<(guiCutThrough)[]> { - const path = `/rests/operations/data-provider:read-gui-cut-through-entry`; - let webUriList: guiCutThrough[] = [] - const query = { - "data-provider:input": { - "filter": [{ - "property": "id", - "filtervalues": neList - }], - "pagination": { - "size": 20, - "page": 1 - } - } - } - - const result = await requestRest<Result<guiCutThrough>>(path, { method: "POST", body: JSON.stringify(query) }); - const resultData = result && result["data-provider:output"] && result["data-provider:output"].data; - neList.forEach(nodeId => { - let entryNotFound = true; - if (resultData) { - const BreakException = {}; - try { - resultData.forEach(entry => { - if (entry.id == nodeId) { - entryNotFound = false; - if (entry.weburi) { - webUriList.push({ id: nodeId, weburi: entry.weburi }); - } else { - webUriList.push({ id: nodeId, weburi: undefined }); - } - throw BreakException; - } - }); - } catch (e) {} - } - if (entryNotFound) - webUriList.push({ id: nodeId, weburi: undefined }); - }); - return webUriList; - } - - // public async getAllWebUriExtensionsForNetworkElementListAsync(ne: string[]): Promise<(guiCutThrough)[] | null> { - - // let promises: any[] = []; - // let webUris: guiCutThrough[] = [] - - // ne.forEach(nodeId => { - // const path = this.getAllWebUriExtensionsForNetworkElementListUri(nodeId); - - // // add search request to array - // promises.push(requestRest<any>(path, { method: "GET" }) - // .then(result => { - // if (result != null && result['core-model:network-element'] && result['core-model:network-element'].extension) { - // const webUri = result['core-model:network-element'].extension.find((item: any) => item['value-name'] === "webUri") - // if (webUri) { - // webUris.push({ weburi: webUri.value, id: nodeId }); - // } else { - // webUris.push({ weburi: undefined, id: nodeId }); - // } - // } else { - // webUris.push({ weburi: undefined, id: nodeId }); - // } - // }) - // .catch(error => { - // webUris.push({ weburi: undefined, id: nodeId }); - // })) - // }) - // // wait until all promises are done and return weburis - // return Promise.all(promises).then(result => { return webUris }); - // } - - } - - - - export const connectService = new ConnectService(); -
\ No newline at end of file + public async deleteNetworkElement(element: UpdateNetworkElement): Promise<NetworkElementConnection | null> { + const query = { + "id": element.id + }; + const path = this.getNetworkElementConnectDataProviderUri("delete"); + const result = await requestRest<NetworkElementConnection>(path, { + method: "POST", body: JSON.stringify(convertPropertyNames({ "data-provider:input": query }, replaceUpperCase)) + }); + return result || null; + } + + /** Mounts network element. */ + public async mountNetworkElement(networkElement: NetworkElementConnection): Promise<boolean> { + const path = this.getNetworkElementUri(networkElement.nodeId); + const mountXml = [ + '<node xmlns="urn:TBD:params:xml:ns:yang:network-topology">', + `<node-id>${networkElement.nodeId}</node-id>`, + `<host xmlns="urn:opendaylight:netconf-node-topology">${networkElement.host}</host>`, + `<port xmlns="urn:opendaylight:netconf-node-topology">${networkElement.port}</port>`, + `<username xmlns="urn:opendaylight:netconf-node-topology">${networkElement.username}</username>`, + `<password xmlns="urn:opendaylight:netconf-node-topology">${networkElement.password}</password>`, + ' <tcp-only xmlns="urn:opendaylight:netconf-node-topology">false</tcp-only>', + + ' <!-- non-mandatory fields with default values, you can safely remove these if you do not wish to override any of these values-->', + ' <reconnect-on-changed-schema xmlns="urn:opendaylight:netconf-node-topology">false</reconnect-on-changed-schema>', + ' <connection-timeout-millis xmlns="urn:opendaylight:netconf-node-topology">20000</connection-timeout-millis>', + ' <max-connection-attempts xmlns="urn:opendaylight:netconf-node-topology">100</max-connection-attempts>', + ' <between-attempts-timeout-millis xmlns="urn:opendaylight:netconf-node-topology">2000</between-attempts-timeout-millis>', + ' <sleep-factor xmlns="urn:opendaylight:netconf-node-topology">1.5</sleep-factor>', + + ' <!-- keepalive-delay set to 0 turns off keepalives-->', + ' <keepalive-delay xmlns="urn:opendaylight:netconf-node-topology">120</keepalive-delay>', + '</node>'].join(''); + + const tlsXml = [ + '<node xmlns="urn:TBD:params:xml:ns:yang:network-topology">', + `<node-id>${networkElement.nodeId}</node-id>`, + '<key-based xmlns="urn:opendaylight:netconf-node-topology">', + `<key-id xmlns="urn:opendaylight:netconf-node-topology">${networkElement.tlsKey}</key-id>`, + `<username xmlns="urn:opendaylight:netconf-node-topology">${networkElement.username}</username>`, + '</key-based>', + `<host xmlns="urn:opendaylight:netconf-node-topology">${networkElement.host}</host>`, + `<port xmlns="urn:opendaylight:netconf-node-topology">${networkElement.port}</port>`, + '<tcp-only xmlns="urn:opendaylight:netconf-node-topology">false</tcp-only>', + '<protocol xmlns="urn:opendaylight:netconf-node-topology">', + '<name xmlns="urn:opendaylight:netconf-node-topology">TLS</name>', + ' </protocol>', + '<max-connection-attempts xmlns="urn:opendaylight:netconf-node-topology">2</max-connection-attempts>', + '</node>'].join('') + let bodyXml; + if (networkElement.password) { + bodyXml = mountXml + } + else { + bodyXml = tlsXml + } + + try { + const result = await requestRest<string>(path, { + method: 'PUT', + headers: { + 'Content-Type': 'application/xml', + 'Accept': 'application/xml' + }, + body: bodyXml + }); + // expect an empty answer + return result !== null; + } catch { + return false; + } + }; + + /** Unmounts a network element by its id. */ + public async unmountNetworkElement(nodeId: string): Promise<boolean> { + const path = this.getNetworkElementUri(nodeId); + + try { + const result = await requestRest<string>(path, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/xml', + 'Accept': 'application/xml' + }, + }); + // expect an empty answer + return result !== null; + + } catch { + return false; + } + }; + + /** Yang capabilities of the selected network elements. */ + public async infoNetworkElement(nodeId: string): Promise<TopologyNode | null> { + const path = this.getNetworkElementUri(nodeId); + const topologyRequestPomise = requestRest<Topology>(path, { method: "GET" }); + + return topologyRequestPomise && topologyRequestPomise.then(result => { + return result && result["network-topology:node"] && result["network-topology:node"][0] || null; + }); + } + + + /** Yang features of the selected network element module. */ + public async infoNetworkElementFeatures(nodeId: string): Promise<Module[] | null | undefined> { + const path = this.getNetworkElementYangLibraryFeature(nodeId); + const topologyRequestPomise = requestRest<FeatureTopology>(path, { method: "GET" }); + + return topologyRequestPomise && topologyRequestPomise.then(result => { + const resultFinal = result && result['ietf-yang-library:yang-library'] + && result["ietf-yang-library:yang-library"]["module-set"] && + result["ietf-yang-library:yang-library"]["module-set"][0] && + result["ietf-yang-library:yang-library"]["module-set"][0]['module'] || null; + return resultFinal; + }); + } + + + + /** + * Get the connection state of the network element. + */ + public async getNetworkElementConnectionStatus(element: string): Promise<(ConnectionStatus)[] | null> { + const path = `/rests/operations/data-provider:read-network-element-connection-list`; + const query = { + "data-provider:input": { + "filter": [{ + "property": "node-id", + "filtervalue": element + }], + "pagination": { + "size": 20, + "page": 1 + } + } + } + const result = await requestRest<Result<ConnectionStatus>>(path, { method: "POST", body: JSON.stringify(query) }); + return result && result["data-provider:output"] && result["data-provider:output"].data && result["data-provider:output"].data.map(ne => ({ + status: ne.status + })) || null; + } + + /** + * Gets all available tlsKeys. + */ + + public async getTlsKeys(): Promise<(TlsKeys)[] | null> { + const path = '/rests/operations/data-provider:read-tls-key-entry'; + const query = { + "data-provider:input": { + "filter": [], + "sortorder": [], + "pagination": { + "size": 20, + "page": 1 + } + } + }; + + const result = await requestRest<Result<string>>(path, { method: "POST", body: JSON.stringify(query) }); + return result && result["data-provider:output"] && result["data-provider:output"].data && result["data-provider:output"].data.map(ne => ({ + key: ne + })) || null; + } + + public async getAllWebUriExtensionsForNetworkElementListAsync(neList: string[]): Promise<(guiCutThrough)[]> { + const path = `/rests/operations/data-provider:read-gui-cut-through-entry`; + let webUriList: guiCutThrough[] = [] + const query = { + "data-provider:input": { + "filter": [{ + "property": "id", + "filtervalues": neList + }], + "pagination": { + "size": 20, + "page": 1 + } + } + } + + const result = await requestRest<Result<guiCutThrough>>(path, { method: "POST", body: JSON.stringify(query) }); + const resultData = result && result["data-provider:output"] && result["data-provider:output"].data; + neList.forEach(nodeId => { + let entryNotFound = true; + if (resultData) { + const BreakException = {}; + try { + resultData.forEach(entry => { + if (entry.id == nodeId) { + entryNotFound = false; + if (entry.weburi) { + webUriList.push({ id: nodeId, weburi: entry.weburi }); + } else { + webUriList.push({ id: nodeId, weburi: undefined }); + } + throw BreakException; + } + }); + } catch (e) { } + } + if (entryNotFound) + webUriList.push({ id: nodeId, weburi: undefined }); + }); + return webUriList; + } + + // public async getAllWebUriExtensionsForNetworkElementListAsync(ne: string[]): Promise<(guiCutThrough)[] | null> { + + // let promises: any[] = []; + // let webUris: guiCutThrough[] = [] + + // ne.forEach(nodeId => { + // const path = this.getAllWebUriExtensionsForNetworkElementListUri(nodeId); + + // // add search request to array + // promises.push(requestRest<any>(path, { method: "GET" }) + // .then(result => { + // if (result != null && result['core-model:network-element'] && result['core-model:network-element'].extension) { + // const webUri = result['core-model:network-element'].extension.find((item: any) => item['value-name'] === "webUri") + // if (webUri) { + // webUris.push({ weburi: webUri.value, id: nodeId }); + // } else { + // webUris.push({ weburi: undefined, id: nodeId }); + // } + // } else { + // webUris.push({ weburi: undefined, id: nodeId }); + // } + // }) + // .catch(error => { + // webUris.push({ weburi: undefined, id: nodeId }); + // })) + // }) + // // wait until all promises are done and return weburis + // return Promise.all(promises).then(result => { return webUris }); + // } + +} + + + +export const connectService = new ConnectService(); diff --git a/sdnr/wt/odlux/apps/demoApp/package.json b/sdnr/wt/odlux/apps/demoApp/package.json index 7756fe328..d52383719 100644 --- a/sdnr/wt/odlux/apps/demoApp/package.json +++ b/sdnr/wt/odlux/apps/demoApp/package.json @@ -27,8 +27,8 @@ "@types/react": "17.0.3", "@types/react-dom": "17.0.2", "@types/react-router-dom": "5.1.7", - "@material-ui/core": "4.11.0", - "@material-ui/icons": "4.9.1", + "@material-ui/core": "4.11.4", + "@material-ui/icons": "4.11.2", "@types/classnames": "2.2.6", "@types/flux": "3.1.8", "@types/jquery": "3.3.10", diff --git a/sdnr/wt/odlux/apps/demoApp/pom.xml b/sdnr/wt/odlux/apps/demoApp/pom.xml index e3f27d5c9..7c95fc3b5 100644 --- a/sdnr/wt/odlux/apps/demoApp/pom.xml +++ b/sdnr/wt/odlux/apps/demoApp/pom.xml @@ -140,8 +140,8 @@ <!-- optional: default phase is "generate-resources" --> <phase>initialize</phase> <configuration> - <nodeVersion>v10.16.3</nodeVersion> - <yarnVersion>v1.19.0</yarnVersion> + <nodeVersion>v12.13.0</nodeVersion> + <yarnVersion>v1.22.10</yarnVersion> </configuration> </execution> <execution> diff --git a/sdnr/wt/odlux/apps/eventLogApp/package.json b/sdnr/wt/odlux/apps/eventLogApp/package.json index dfcebfe63..52c49b328 100644 --- a/sdnr/wt/odlux/apps/eventLogApp/package.json +++ b/sdnr/wt/odlux/apps/eventLogApp/package.json @@ -27,8 +27,8 @@ "@types/react": "17.0.3", "@types/react-dom": "17.0.2", "@types/react-router-dom": "5.1.7", - "@material-ui/core": "4.11.0", - "@material-ui/icons": "4.9.1", + "@material-ui/core": "4.11.4", + "@material-ui/icons": "4.11.2", "@types/classnames": "2.2.6", "@types/flux": "3.1.8", "@types/jquery": "3.3.10", diff --git a/sdnr/wt/odlux/apps/eventLogApp/pom.xml b/sdnr/wt/odlux/apps/eventLogApp/pom.xml index 0b061a5b9..2cc6234da 100644 --- a/sdnr/wt/odlux/apps/eventLogApp/pom.xml +++ b/sdnr/wt/odlux/apps/eventLogApp/pom.xml @@ -140,8 +140,8 @@ <!-- optional: default phase is "generate-resources" --> <phase>initialize</phase> <configuration> - <nodeVersion>v10.16.3</nodeVersion> - <yarnVersion>v1.19.0</yarnVersion> + <nodeVersion>v12.13.0</nodeVersion> + <yarnVersion>v1.22.10</yarnVersion> </configuration> </execution> <execution> diff --git a/sdnr/wt/odlux/apps/faultApp/package.json b/sdnr/wt/odlux/apps/faultApp/package.json index 5d1439b85..cb03e49d9 100644 --- a/sdnr/wt/odlux/apps/faultApp/package.json +++ b/sdnr/wt/odlux/apps/faultApp/package.json @@ -27,8 +27,8 @@ "@types/react": "17.0.3", "@types/react-dom": "17.0.2", "@types/react-router-dom": "5.1.7", - "@material-ui/core": "4.11.0", - "@material-ui/icons": "4.9.1", + "@material-ui/core": "4.11.4", + "@material-ui/icons": "4.11.2", "@types/classnames": "2.2.6", "@types/flux": "3.1.8", "@types/jquery": "3.3.10", diff --git a/sdnr/wt/odlux/apps/faultApp/pom.xml b/sdnr/wt/odlux/apps/faultApp/pom.xml index 63236dab4..fec425892 100644 --- a/sdnr/wt/odlux/apps/faultApp/pom.xml +++ b/sdnr/wt/odlux/apps/faultApp/pom.xml @@ -140,8 +140,8 @@ <!-- optional: default phase is "generate-resources" --> <phase>initialize</phase> <configuration> - <nodeVersion>v10.16.3</nodeVersion> - <yarnVersion>v1.19.0</yarnVersion> + <nodeVersion>v12.13.0</nodeVersion> + <yarnVersion>v1.22.10</yarnVersion> </configuration> </execution> <execution> diff --git a/sdnr/wt/odlux/apps/helpApp/package.json b/sdnr/wt/odlux/apps/helpApp/package.json index 58e68dd03..51e383b11 100644 --- a/sdnr/wt/odlux/apps/helpApp/package.json +++ b/sdnr/wt/odlux/apps/helpApp/package.json @@ -32,8 +32,8 @@ "@types/react": "17.0.3", "@types/react-dom": "17.0.2", "@types/react-router-dom": "5.1.7", - "@material-ui/core": "4.11.0", - "@material-ui/icons": "4.9.1", + "@material-ui/core": "4.11.4", + "@material-ui/icons": "4.11.2", "@types/classnames": "2.2.6", "@types/flux": "3.1.8", "@types/jquery": "3.3.10", diff --git a/sdnr/wt/odlux/apps/helpApp/pom.xml b/sdnr/wt/odlux/apps/helpApp/pom.xml index da49a7850..6bea827e5 100644 --- a/sdnr/wt/odlux/apps/helpApp/pom.xml +++ b/sdnr/wt/odlux/apps/helpApp/pom.xml @@ -140,8 +140,8 @@ <!-- optional: default phase is "generate-resources" --> <phase>initialize</phase> <configuration> - <nodeVersion>v10.16.3</nodeVersion> - <yarnVersion>v1.19.0</yarnVersion> + <nodeVersion>v12.13.0</nodeVersion> + <yarnVersion>v1.22.10</yarnVersion> </configuration> </execution> <execution> diff --git a/sdnr/wt/odlux/apps/inventoryApp/package.json b/sdnr/wt/odlux/apps/inventoryApp/package.json index 0f6b40379..e78f3ac82 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/package.json +++ b/sdnr/wt/odlux/apps/inventoryApp/package.json @@ -27,8 +27,8 @@ "@types/react": "17.0.3", "@types/react-dom": "17.0.2", "@types/react-router-dom": "5.1.7", - "@material-ui/core": "4.11.0", - "@material-ui/icons": "4.9.1", + "@material-ui/core": "4.11.4", + "@material-ui/icons": "4.11.2", "@types/classnames": "2.2.6", "@types/flux": "3.1.8", "@types/jquery": "3.3.10", diff --git a/sdnr/wt/odlux/apps/inventoryApp/pom.xml b/sdnr/wt/odlux/apps/inventoryApp/pom.xml index 2701e04ed..377446edc 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/pom.xml +++ b/sdnr/wt/odlux/apps/inventoryApp/pom.xml @@ -140,8 +140,8 @@ <!-- optional: default phase is "generate-resources" --> <phase>initialize</phase> <configuration> - <nodeVersion>v10.16.3</nodeVersion> - <yarnVersion>v1.19.0</yarnVersion> + <nodeVersion>v12.13.0</nodeVersion> + <yarnVersion>v1.22.10</yarnVersion> </configuration> </execution> <execution> diff --git a/sdnr/wt/odlux/apps/linkCalculationApp/package.json b/sdnr/wt/odlux/apps/linkCalculationApp/package.json index 171770603..c03afe19b 100644 --- a/sdnr/wt/odlux/apps/linkCalculationApp/package.json +++ b/sdnr/wt/odlux/apps/linkCalculationApp/package.json @@ -29,8 +29,8 @@ "@types/react": "17.0.3", "@types/react-dom": "17.0.2", "@types/react-router-dom": "5.1.7", - "@material-ui/core": "4.11.0", - "@material-ui/icons": "4.9.1", + "@material-ui/core": "4.11.4", + "@material-ui/icons": "4.11.2", "@types/classnames": "2.2.6", "@types/flux": "3.1.8", "@types/jquery": "3.3.10", diff --git a/sdnr/wt/odlux/apps/linkCalculationApp/pom.xml b/sdnr/wt/odlux/apps/linkCalculationApp/pom.xml index 5467c8b5d..c5d8c3617 100644 --- a/sdnr/wt/odlux/apps/linkCalculationApp/pom.xml +++ b/sdnr/wt/odlux/apps/linkCalculationApp/pom.xml @@ -140,8 +140,8 @@ <!-- optional: default phase is "generate-resources" --> <phase>initialize</phase> <configuration> - <nodeVersion>v10.16.3</nodeVersion> - <yarnVersion>v1.19.0</yarnVersion> + <nodeVersion>v12.13.0</nodeVersion> + <yarnVersion>v1.22.10</yarnVersion> </configuration> </execution> <execution> diff --git a/sdnr/wt/odlux/apps/linkCalculationApp/src/actions/commonLinkCalculationActions.ts b/sdnr/wt/odlux/apps/linkCalculationApp/src/actions/commonLinkCalculationActions.ts index e7427e4cc..0849058dc 100644 --- a/sdnr/wt/odlux/apps/linkCalculationApp/src/actions/commonLinkCalculationActions.ts +++ b/sdnr/wt/odlux/apps/linkCalculationApp/src/actions/commonLinkCalculationActions.ts @@ -21,98 +21,155 @@ import { Dispatch } from "../../../../framework/src/flux/store"; import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore"; -export class UpdateLinkIdAction extends Action{ - constructor(public linkId: string){ +export class UpdateLinkIdAction extends Action { + constructor(public linkId: string) { super(); } } -export class UpdateFrequencyAction extends Action{ - constructor(public frequency: number){ +export class UpdateFrequencyAction extends Action { + constructor(public frequency: number) { super(); } } -export class UpdateSiteAction extends Action{ +export class UpdateSiteAction extends Action { constructor( public siteA?: any, public siteB?: any - ){ + ) { super(); } } -export class UpdateRainAttAction extends Action{ - - constructor(public rainAtt: number){ +export class UpdateRainAttAction extends Action { + + constructor(public rainAtt: number) { super(); } } -export class UpdateRainValAction extends Action{ - constructor(public rainVal: number){ +export class UpdateRainValAction extends Action { + constructor(public rainVal: number) { super(); } } -export class updateHideForm extends Action{ - constructor(public formView: boolean){ +export class updateHideForm extends Action { + constructor(public formView: boolean) { super(); } } -export class UpdateDistanceAction extends Action{ - constructor(public distance: number){ +export class UpdateDistanceAction extends Action { + constructor(public distance: number) { super(); } } -export class UpdateFslCalculation extends Action{ - constructor(public fsl: number){ +export class UpdateFslCalculation extends Action { + constructor(public fsl: number) { super(); } } -export class UpdateLatLonAction extends Action{ +export class UpdateLatLonAction extends Action { constructor( public Lat1: number, - public Lon1:number, - public Lat2: number, + public Lon1: number, + public Lat2: number, public Lon2: number - ){ + ) { + super(); + + } +} +export class UpdatePolAction extends Action { + constructor(public polarization: string) { super(); - } } -export class UpdatePolAction extends Action{ - constructor(public polarization: string){ +export class isCalculationServerReachableAction extends Action { + constructor(public reachable: boolean) { super(); } } -export class isCalculationServerReachableAction extends Action{ - constructor(public reachable: boolean){ +export class updateAltitudeAction extends Action { + constructor( + public amslA: number, + public aglA: number, + public amslB: number, + public aglB: number + ) { super(); } } -export class updateAltitudeAction extends Action{ +export class UpdateAbsorptionLossAction extends Action { constructor( - public amslA:number, - public aglA:number, - public amslB:number, - public aglB:number - ){ + public absorptionOxygen: number, + public absorptionWater: number, + + ) { + super(); + } +} +export class UpdateWorstMonthRainAction extends Action { + constructor(public month: string) { + super(); + } +} + +export class UpdateEIRPAction extends Action { + constructor(public eirpA: number,public eirpB: number) { + super(); + } +} +export class UpdateAntennaGainAction extends Action { + constructor(public antennaGainList: string[]) { + super(); + } +} +export class UpdateAntennaListAction extends Action { + constructor(public antennaList: string[]) { + super(); + } +} +export class UpdateAntennaAction extends Action { + constructor(public antennaA: string | null, public antennaB : string | null) { + super(); + } +} +export class UpdateRadioAttributesAction extends Action { + constructor(public som: number , public eirpA : number, public eirpB : number) { super(); } } -export class UpdateAbsorptionLossAction extends Action{ - constructor( - public absorptionOxygen:number, - public absorptionWater:number, - - ){ - super(); - } +export class UpdateTxPowerAction extends Action { + constructor(public txPowerA: string | null , public txPowerB : string | null) { + super(); + } } -export class UpdateWorstMonthRainAction extends Action{ - constructor(public month: string){ +export class UpdateRxSensitivityAction extends Action { + constructor(public rxSensitivityA: string | null , public rxSensitivityB : string | null) { super(); } } + +export const updateAntennaList = (frequency: number) => async (dispatcher: Dispatch, getState: () => IApplicationStoreState) => { + let antennaList: string[] = [] + let antennaDiameterList: string[] = [] + let antennaGainList :string[] =[] + //switch case here frequency = 26? antennaList.push + switch (frequency) { + case 7: antennaList.push('ANDREW VHLPX2.5-7W', 'ANDREW VHLPX3-7W', 'ANDREW VHLPX4-7W', 'ANDREW VHLPX6-7W' ), antennaDiameterList.push('0.6','0.9','1.2','1.8'), antennaGainList.push('33.90','35.50','37.30','40.61'); break + case 11: antennaList.push('ANDREW VHLPX2-11W', 'ANDREW VHLPX3-11W', 'ANDREW VHLPX4-11W'), antennaDiameterList.push('0.6','0.9','1.2'), antennaGainList.push('34.50','38.4','40.70');break + case 15: antennaList.push('ANDREW VHLPX1-15', 'ANDREW VHLPX2-15', 'ANDREW VHLPX3-15', 'ANDREW VHLPX4-15'), antennaDiameterList.push('0.3','0.6','0.9','1.2'), antennaGainList.push('32.00','36.80','41.11','42.90');break + case 23: antennaList.push('ANDREW VHLPX1-23', 'ANDREW VHLPX2-23', 'ANDREW VHLPX3-23', 'ANDREW VHLPX4-23'), antennaDiameterList.push('0.3','0.6','0.9','1.2'), antennaGainList.push('35.30','40.21','44.80','46.71');break + case 26: antennaList.push('ANDREW VHLPX1-26', 'ANDREW VHLPX2-26', 'ANDREW VHLPX3-26'), antennaDiameterList.push('0.3','0.6','0.9'), antennaGainList.push('36.61','40.21','41.21','45.80');break + case 28: antennaList.push('ANDREW VHLPX1-28', 'ANDREW VHLPX2-28'), antennaDiameterList.push('0.3','0.6'), antennaGainList.push('38.11','42.21');break + case 38: antennaList.push('ANDREW VHLPX1-38', 'ANDREW VHLPX2-38'), antennaDiameterList.push('0.3','0.6'), antennaGainList.push('40.11','45.21');break + case 42: antennaList.push('ANDREW VHLPX1-42-XXX/D', 'ANDREW VHLPX2-42-XXX/A'), antennaDiameterList.push('0.3','0.6'), antennaGainList.push('40.80','46.00');break + case 80: antennaList.push('Radio Waves HPCPE-80', 'Radio Waves HPLP2-80'), antennaDiameterList.push('0.3','0.6'), antennaGainList.push('43.80','50.80');break + } + dispatcher(new UpdateAntennaListAction(antennaList)) + dispatcher(new UpdateAntennaGainAction(antennaGainList)) +} + diff --git a/sdnr/wt/odlux/apps/linkCalculationApp/src/handlers/linkCalculationAppRootHandler.ts b/sdnr/wt/odlux/apps/linkCalculationApp/src/handlers/linkCalculationAppRootHandler.ts index 608367072..edfad052a 100644 --- a/sdnr/wt/odlux/apps/linkCalculationApp/src/handlers/linkCalculationAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/linkCalculationApp/src/handlers/linkCalculationAppRootHandler.ts @@ -21,7 +21,7 @@ import { combineActionHandler } from '../../../../framework/src/flux/middleware' // ** do not remove ** import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; import { IActionHandler } from '../../../../framework/src/flux/action';; -import { UpdateLinkIdAction, UpdateFrequencyAction , UpdateLatLonAction, UpdateRainAttAction, UpdateRainValAction, updateHideForm, UpdateFslCalculation, UpdateSiteAction, UpdateDistanceAction, isCalculationServerReachableAction, UpdatePolAction, updateAltitudeAction, UpdateAbsorptionLossAction, UpdateWorstMonthRainAction} from '../actions/commonLinkCalculationActions'; +import { UpdateLinkIdAction, UpdateFrequencyAction , UpdateLatLonAction, UpdateRainAttAction, UpdateRainValAction, updateHideForm, UpdateFslCalculation, UpdateSiteAction, UpdateDistanceAction, isCalculationServerReachableAction, UpdatePolAction, updateAltitudeAction, UpdateAbsorptionLossAction, UpdateWorstMonthRainAction, UpdateEIRPAction, UpdateAntennaAction, UpdateAntennaListAction, UpdateAntennaGainAction, UpdateTxPowerAction, UpdateRxSensitivityAction} from '../actions/commonLinkCalculationActions'; declare module '../../../../framework/src/store/applicationStore' { interface IApplicationStoreState { @@ -55,7 +55,20 @@ export type ILinkCalculationAppStateState= { aglB:number, absorptionWater:number, absorptionOxygen: number, - month: string + month: string, + eirpA: number, + eirpB: number, + antennaGainA: number, + antennaGainB :number, + antennaList:string[], + antennaGainList:string[], + antennaA: string, + antennaB:string, + systemOperatingMargin : number, + txPowerA : string, + txPowerB: string, + rxSensitivityA : string, + rxSensitivityB: string } const initialState: ILinkCalculationAppStateState ={ @@ -80,7 +93,20 @@ const initialState: ILinkCalculationAppStateState ={ aglB:0, absorptionWater:0, absorptionOxygen: 0, - month: '' + month: '', + eirpA: 0, + eirpB: 0, + antennaGainA :0, + antennaGainB :0, + antennaList:[], + antennaGainList:[], + antennaA: '0', + antennaB:'0', + systemOperatingMargin : 0, + txPowerA : '0', + txPowerB: '0', + rxSensitivityA: '0', + rxSensitivityB: '0' } @@ -130,6 +156,25 @@ export const LinkCalculationHandler: IActionHandler<ILinkCalculationAppStateStat else if (action instanceof UpdateWorstMonthRainAction){ state = Object.assign({}, state, {month:action.month}) } + else if (action instanceof UpdateEIRPAction){ + state = Object.assign({}, state, {eirpA:action.eirpA, eirpB:action.eirpB}) + } + else if (action instanceof UpdateAntennaGainAction){ + state = Object.assign({}, state, {antennaGainList:action.antennaGainList}) + } + else if (action instanceof UpdateAntennaListAction){ + state = Object.assign({}, state, {antennaList:action.antennaList}) + } + else if (action instanceof UpdateAntennaAction){ + state = Object.assign({}, state, {antennaA:action.antennaA == null ? state.antennaA : action.antennaA , antennaB: action.antennaB == null? state.antennaB : action.antennaB}) + } + else if (action instanceof UpdateTxPowerAction){ + state = Object.assign({}, state, {txPowerA:action.txPowerA == null ? state.txPowerA : action.txPowerA , txPowerB: action.txPowerB == null? state.txPowerB : action.txPowerB}) + } + else if (action instanceof UpdateRxSensitivityAction){ + state = Object.assign({}, state, {rxSensitivityA:action.rxSensitivityA == null ? state.rxSensitivityA : action.rxSensitivityA , rxSensitivityB: action.rxSensitivityB == null? state.rxSensitivityB : action.rxSensitivityB}) + } + return state } diff --git a/sdnr/wt/odlux/apps/linkCalculationApp/src/views/Style.scss b/sdnr/wt/odlux/apps/linkCalculationApp/src/views/Style.scss index 35a9b9702..e4b0c7797 100644 --- a/sdnr/wt/odlux/apps/linkCalculationApp/src/views/Style.scss +++ b/sdnr/wt/odlux/apps/linkCalculationApp/src/views/Style.scss @@ -1,11 +1,11 @@ -.parent{ +// .parent{ - display: flex; - justify-content: space-evenly; - margin: auto; +// display: flex; +// justify-content: space-evenly; +// margin: auto; -} +//} .input { width: 150px; box-sizing: border-box; @@ -18,56 +18,99 @@ box-sizing: border-box; } +.container1{ + margin-bottom: 15px; -.container-1 { - height: 50vh; - width: 80%; - justify-content: center; - align-items: baseline;; - display: flex; - flex-direction: row; - align-content: space-between; - padding: 20px 40px; - border-radius: 10px; - // box-shadow: 0px 10px 50px #555; - background-color: #ffffff; - // padding-top: 10px; - } - .column1 { - flex-direction: column; - width: 30%; - align-items: flex-end;; - // padding: 2em; +.container1 div{ + display:flex; + flex-direction: row; + + width:100%; + padding-top: 2px; +} + +.container1 div div{ + width:50%; + border-bottom-style: solid; + border-bottom-width: thin; + border-color: silver; +} + +.firstBox div:first-child{ + margin-left: 33.5%; +} + +// .container-1 { +// width: 80%; +// justify-content: center; +// align-items: baseline;; +// display: flex; +// flex-direction: row; +// align-content: space-between; +// padding: 20px 40px; +// border-radius: 10px; +// // box-shadow: 0px 10px 50px #555; +// background-color: #ffffff; +// // padding-top: 10px; + +// } + + // .column1 { + // flex-direction: column; + // width: 30%; + // align-items: flex-end;; + // // padding: 2em; - } - .column1 div { - margin-top: 10px; - // align-items: space-between; - // flex-wrap: wrap; - border-bottom-style: solid; - border-bottom-width: thin; - border-color: silver; - } - .middlecolumn{ + // } + // .column1 div { + // margin-top: 10px; + // // align-items: space-between; + // // flex-wrap: wrap; + // border-bottom-style: solid; + // border-bottom-width: thin; + // border-color: silver; + // } + // .middlecolumn{ - flex-direction: column; - flex-grow: 1; - // padding: 10px 10px; - } + // flex-direction: column; + // flex-grow: 1; + // // padding: 10px 10px; + // } - .middlecolumn div{ - margin-top: 10px; - border-bottom-style: solid; - border-bottom-width: thin; - border-color: silver; - } + // .middlecolumn div{ + // margin-top: 10px; + // border-bottom-style: solid; + // border-bottom-width: thin; + // border-color: silver; + // } - .column2 { - margin-left: 200px; - } - .column2 div{ - margin-top: 10px; + // .column2 { + // margin-left: 200px; + // } + // .column2 div{ + // margin-top: 10px; - }
\ No newline at end of file + // } + + .antennaContainer{ + margin-bottom: 15px; + background-color: rgb(184, 181, 181); + + } + .antennaContainer div { + display:flex; + flex-direction: row; + + width:100%; + padding-top: 2px; +} +.antennaContainer div div{ + + width:50%; + +} +.antennaFont{ + font-family: "Lucida Console"; +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/linkCalculationApp/src/views/linkCalculationComponent.tsx b/sdnr/wt/odlux/apps/linkCalculationApp/src/views/linkCalculationComponent.tsx index 61a700c71..063926269 100644 --- a/sdnr/wt/odlux/apps/linkCalculationApp/src/views/linkCalculationComponent.tsx +++ b/sdnr/wt/odlux/apps/linkCalculationApp/src/views/linkCalculationComponent.tsx @@ -24,10 +24,11 @@ import { TextField, Tabs, Tab, Typography, AppBar, Button, Tooltip, Checkbox, Ta import './Style.scss' import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore"; -import { UpdateFrequencyAction, UpdateLatLonAction, UpdateRainAttAction, UpdateRainValAction, UpdateFslCalculation, isCalculationServerReachableAction, UpdatePolAction, UpdateDistanceAction, updateAltitudeAction, UpdateAbsorptionLossAction, UpdateWorstMonthRainAction } from "../actions/commonLinkCalculationActions"; +import { UpdateFrequencyAction, UpdateLatLonAction, UpdateRainAttAction, UpdateRainValAction, UpdateFslCalculation, isCalculationServerReachableAction, UpdatePolAction, UpdateDistanceAction, updateAltitudeAction, UpdateAbsorptionLossAction, UpdateWorstMonthRainAction, updateAntennaList, UpdateAntennaAction, UpdateRadioAttributesAction, UpdateTxPowerAction, UpdateRxSensitivityAction } from "../actions/commonLinkCalculationActions"; import { faPlaneArrival, faAlignCenter } from "@fortawesome/free-solid-svg-icons"; import ConnectionInfo from '../components/connectionInfo' import { red } from "@material-ui/core/colors"; +import { Dvr } from "@material-ui/icons"; @@ -41,33 +42,44 @@ const mapProps = (state: IApplicationStoreState) => ({ rainAtt: state.linkCalculation.calculations.rainAtt, rainVal: state.linkCalculation.calculations.rainVal, formView: state.linkCalculation.calculations.formView, - fsl:state.linkCalculation.calculations.fsl, + fsl: state.linkCalculation.calculations.fsl, siteA: state.linkCalculation.calculations.siteA, siteB: state.linkCalculation.calculations.siteB, distance: state.linkCalculation.calculations.distance, - reachable :state.linkCalculation.calculations.reachable, - polarization:state.linkCalculation.calculations.polarization, - amslA:state.linkCalculation.calculations.amslA, - amslB:state.linkCalculation.calculations.amslB, - aglA:state.linkCalculation.calculations.aglA, - aglB:state.linkCalculation.calculations.aglB, - absorptionOxygen : state.linkCalculation.calculations.absorptionOxygen, - absorptionWater : state.linkCalculation.calculations.absorptionWater, - month : state.linkCalculation.calculations.month + reachable: state.linkCalculation.calculations.reachable, + polarization: state.linkCalculation.calculations.polarization, + amslA: state.linkCalculation.calculations.amslA, + amslB: state.linkCalculation.calculations.amslB, + aglA: state.linkCalculation.calculations.aglA, + aglB: state.linkCalculation.calculations.aglB, + absorptionOxygen: state.linkCalculation.calculations.absorptionOxygen, + absorptionWater: state.linkCalculation.calculations.absorptionWater, + month: state.linkCalculation.calculations.month, + eirpSiteA: state.linkCalculation.calculations.eirpA, + eirpSiteB: state.linkCalculation.calculations.eirpB, + antennaGainA: state.linkCalculation.calculations.antennaGainA, + antennaGainB: state.linkCalculation.calculations.antennaGainB, + antennaList: state.linkCalculation.calculations.antennaList, + antennaGainList: state.linkCalculation.calculations.antennaGainList, + antennaA: state.linkCalculation.calculations.antennaA, + antennaB: state.linkCalculation.calculations.antennaB, + systemOperatingMargin : state.linkCalculation.calculations.systemOperatingMargin + }); -const BASE_URL="/topology/linkcalculator" +const BASE_URL = "/topology/linkcalculator" const mapDispatch = (dispatcher: IDispatcher) => ({ updateFrequency: (frequency: number) => { - dispatcher.dispatch(new UpdateFrequencyAction(frequency)) + dispatcher.dispatch(new UpdateFrequencyAction(frequency)) + dispatcher.dispatch(updateAntennaList(frequency)) }, updateLatLon: (Lat1: number, Lon1: number, Lat2: number, Lon2: number) => { dispatcher.dispatch(new UpdateLatLonAction(Lat1, Lon1, Lat2, Lon2)) }, - + updateRainValue: (rainVal: number) => { dispatcher.dispatch(new UpdateRainValAction(rainVal)) }, @@ -80,53 +92,59 @@ const mapDispatch = (dispatcher: IDispatcher) => ({ dispatcher.dispatch(new UpdateRainAttAction(rainAtt)) }, - - FSL :(free:number)=> { - dispatcher.dispatch(new UpdateFslCalculation (free)) - }, - UpdateConectivity : (reachable:boolean) => { - dispatcher.dispatch (new isCalculationServerReachableAction (reachable)) + FSL: (free: number) => { + dispatcher.dispatch(new UpdateFslCalculation(free)) }, - updatePolarization :(polarization:any)=>{ - dispatcher.dispatch (new UpdatePolAction(polarization)) + UpdateConectivity: (reachable: boolean) => { + dispatcher.dispatch(new isCalculationServerReachableAction(reachable)) }, - updateAutoDistance : (distance:number)=>{ - dispatcher.dispatch (new UpdateDistanceAction(distance)) + updatePolarization: (polarization: any) => { + dispatcher.dispatch(new UpdatePolAction(polarization)) }, - UpdateAbsorption : (OxLoss:number , WaterLoss:number) => { - dispatcher.dispatch (new UpdateAbsorptionLossAction (OxLoss, WaterLoss)) + updateAutoDistance: (distance: number) => { + dispatcher.dispatch(new UpdateDistanceAction(distance)) }, - // UpdateWorstMonth : (worstmonth:boolean) => { - // dispatcher.dispatch (new UpdateWorstMonthAction(worstmonth)) - // }, - - UpdateWorstMonthRain : (month:string) => { - dispatcher.dispatch (new UpdateWorstMonthRainAction(month)) - } + UpdateAbsorption: (OxLoss: number, WaterLoss: number) => { + dispatcher.dispatch(new UpdateAbsorptionLossAction(OxLoss, WaterLoss)) + }, + UpdateWorstMonthRain: (month: string) => { + dispatcher.dispatch(new UpdateWorstMonthRainAction(month)) + }, + UpdateAntenas: (antennaA: string | null, antennaB: string | null) => { + dispatcher.dispatch(new UpdateAntennaAction(antennaA, antennaB)) + }, + UpdateRadioAttributes :(som: number, eirpA: number, eirpB: number)=>{ + dispatcher.dispatch(new UpdateRadioAttributesAction(som,eirpA, eirpB)) + }, + UpdateTxPower :(txPowerA: string | null, txPowerB: string | null)=>{ + dispatcher.dispatch(new UpdateTxPowerAction(txPowerA, txPowerB)) + }, + UpdateRxSensitivity :(rxSensitivityA : string | null, rxSensitivityB : string | null)=>{ + dispatcher.dispatch(new UpdateRxSensitivityAction(rxSensitivityA, rxSensitivityB)) + } }); type linkCalculationProps = Connect<typeof mapProps, typeof mapDispatch>; interface initialState { - rainMethodDisplay: boolean, - absorptionMethod : string; + rainMethodDisplay: boolean, + absorptionMethod: string; horizontalBoxChecked: boolean, - latitude1Error: string, - longitude1Error:string - latitude2Error: string, - longitude2Error:string, - frequencyError: string, - rainMethodError: string, - attenuationMethodError : string, - worstmonth : boolean, - showWM : string - - + latitude1Error: string, + longitude1Error: string + latitude2Error: string, + longitude2Error: string, + frequencyError: string, + rainMethodError: string, + antennaTypeError: string, + attenuationMethodError: string, + worstmonth: boolean, + showWM: string, } class LinkCalculation extends React.Component<linkCalculationProps, initialState> { @@ -135,40 +153,41 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState this.state = { rainMethodDisplay: false, horizontalBoxChecked: true, - absorptionMethod : '0', + absorptionMethod: '0', latitude1Error: '', - longitude1Error:'', + longitude1Error: '', latitude2Error: '', - longitude2Error:'', + longitude2Error: '', frequencyError: '', rainMethodError: '', - attenuationMethodError : '', - worstmonth : false, - showWM: '' - }; - } - - updateAutoDistance = async (lat1: number, lon1: number, lat2: number, lon2: number)=>{ - const result = await fetch(BASE_URL+'/distance/' + lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2) - const json = await result.json() - return json.distanceInKm - } + attenuationMethodError: '', + antennaTypeError: '', + worstmonth: false, + showWM: '', + }; + } + + updateAutoDistance = async (lat1: number, lon1: number, lat2: number, lon2: number) => { + const result = await fetch(BASE_URL + '/distance/' + lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2) + const json = await result.json() + return json.distanceInKm + } + + updateLatLon = (e: any) => { + + e.target.id == 'Lat1' ? this.props.updateLatLon(e.target.value, this.props.lon1, this.props.lat2, this.props.lon2) : null + e.target.id == 'Lon1' ? this.props.updateLatLon(this.props.lat1, e.target.value, this.props.lat2, this.props.lon2) : null + e.target.id == 'Lat2' ? this.props.updateLatLon(this.props.lat1, this.props.lon1, e.target.value, this.props.lon2) : null + e.target.id == 'Lon2' ? this.props.updateLatLon(this.props.lat1, e.target.value, this.props.lat2, e.target.value) : null - updateLatLon = (e:any) => { - - e.target.id== 'Lat1'? this.props.updateLatLon(e.target.value, this.props.lon1, this.props.lat2, this.props.lon2) : null - e.target.id== 'Lon1'? this.props.updateLatLon(this.props.lat1, e.target.value, this.props.lat2, this.props.lon2) : null - e.target.id== 'Lat2'? this.props.updateLatLon(this.props.lat1, this.props.lon1, e.target.value, this.props.lon2) : null - e.target.id== 'Lon2'? this.props.updateLatLon(this.props.lat1, e.target.value, this.props.lat2, e.target.value) : null - } - updatePoli = (val: string) =>{ + updatePoli = (val: string) => { - this.setState({horizontalBoxChecked: !this.state.horizontalBoxChecked}); + this.setState({ horizontalBoxChecked: !this.state.horizontalBoxChecked }); this.props.updatePolarization(val); - + } LatLonToDMS = (value: number, isLon: boolean = false) => { @@ -187,266 +206,359 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState } } - rainAttCal = (lat1: any, lon1: any, lat2: any, lon2: any, frequency: any, distance: number, polarization : string, worstmonth:boolean) => { - if(!worstmonth){ - fetch(BASE_URL+'/rain/annual/' + lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2 + '/' + frequency+ '/'+ distance + '/' + polarization.toUpperCase()) - .then(res => res.json()) - .then(result => { this.props.UpdateRainAtt(result.rainAttenuation) ; this.props.updateRainValue(result.rainFall.rainrate)}) + rainAttCal = (lat1: any, lon1: any, lat2: any, lon2: any, frequency: any, distance: number, polarization: string, worstmonth: boolean) => { + if (!worstmonth) { + fetch(BASE_URL + '/rain/annual/' + lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2 + '/' + frequency + '/' + distance + '/' + polarization.toUpperCase()) + .then(res => res.json()) + .then(result => { this.props.UpdateRainAtt(result.rainAttenuation); this.props.updateRainValue(result.rainFall.rainrate) }) } else { - fetch(BASE_URL+'/rain/worstmonth/' + lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2 + '/' + frequency+ '/'+ distance + '/' + polarization.toUpperCase()) - .then(res => res.json()) - .then(result => { this.props.UpdateRainAtt(result.rainAttenuation) ; this.props.updateRainValue(result.rainFall.rainrate); this.props.UpdateWorstMonthRain (result.rainFall.period); this.setState({showWM: '- Wm is : '})}) + fetch(BASE_URL + '/rain/worstmonth/' + lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2 + '/' + frequency + '/' + distance + '/' + polarization.toUpperCase()) + .then(res => res.json()) + .then(result => { this.props.UpdateRainAtt(result.rainAttenuation); this.props.updateRainValue(result.rainFall.rainrate); this.props.UpdateWorstMonthRain(result.rainFall.period); this.setState({ showWM: '- Wm is : ' }) }) } } - manualRain = (rainfall: number, frequency: number, distance:number, polarization : string) => { - fetch(BASE_URL+'/rain/' + rainfall + '/' + frequency + '/' + distance+ '/' + polarization.toUpperCase()) + manualRain = (rainfall: number, frequency: number, distance: number, polarization: string) => { + fetch(BASE_URL + '/rain/' + rainfall + '/' + frequency + '/' + distance + '/' + polarization.toUpperCase()) .then(res => res.json()) .then(result => { this.props.specificRain(result.rainAttenuation) }) - } + } + + FSL = (distance: number, frequency: number) => { + fetch(BASE_URL + '/fsl/' + distance + '/' + frequency) + .then(res => res.json()) + .then(result => { this.props.FSL(result.fspl) }) + } - FSL = (distance:number, frequency:number) => { - fetch(BASE_URL+'/fsl/' + distance + '/' + frequency) - .then(res=>res.json()) - .then (result => {this.props.FSL(result.fspl)}) + AbsorptionAtt = (lat1: number, lon1: number, lat2: number, lon2: number, distance: number, frequency: number, worstmonth: boolean, absorptionMethod: string) => { + if (!worstmonth) { + fetch(BASE_URL + '/absorption/annual/' + lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2 + '/' + distance + '/' + frequency + '/' + absorptionMethod) + .then(res => res.json()) + .then(result => { this.props.UpdateAbsorption(result.oxygenLoss, result.waterLoss) }) } - - AbsorptionAtt =(lat1: number, lon1: number, lat2: number, lon2: number, distance:number, frequency:number, worstmonth:boolean, absorptionMethod : string) => { - if(!worstmonth) - { - fetch(BASE_URL+'/absorption/annual/' +lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2 + '/' + distance + '/' + frequency + '/' +absorptionMethod) - .then(res=>res.json()) - .then (result => {this.props.UpdateAbsorption(result.oxygenLoss, result.waterLoss)}) - } - else { - fetch(BASE_URL+'/absorption/annual/' +lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2 + '/' + distance + '/' + frequency + '/' +absorptionMethod) - .then(res=>res.json()) - .then (result => {this.props.UpdateAbsorption(result.oxygenLoss, result.waterLoss)}) - } + else { + fetch(BASE_URL + '/absorption/annual/' + lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2 + '/' + distance + '/' + frequency + '/' + absorptionMethod) + .then(res => res.json()) + .then(result => { this.props.UpdateAbsorption(result.oxygenLoss, result.waterLoss) }) } + } + + linkBudget = (antennaA: string, antennaB: string, transmissionPowerA: number, transmissionPowerB: number) => { + fetch(BASE_URL + '/linkbudget/' + antennaA + '/' + antennaB + '/' + transmissionPowerA + '/' + transmissionPowerB) + .then(res=>res.json()) + .then(result => {this.props.UpdateRadioAttributes(result.systemOperatingMargin, result.eirpA, result.eirpB)}) + } + + formValid = () => { + + this.props.lat1 === 0 ? this.setState({ latitude1Error: 'Enter a number between -90 to 90' }) : null + this.props.lat2 === 0 ? this.setState({ latitude2Error: 'Enter a number between -90 to 90' }) : null + this.props.lon1 === 0 ? this.setState({ longitude1Error: 'Enter a number between -180 to 180' }) : null + this.props.lon2 === 0 ? this.setState({ longitude2Error: 'Enter a number between -180 to 180' }) : null + this.props.frequency === 0 ? this.setState({ frequencyError: 'Select a frequency' }) : this.setState({ frequencyError: '' }) + + this.state.rainMethodDisplay === null && this.props.rainVal === 0 ? this.setState({ rainMethodError: 'Select the rain method' }) : this.setState({ rainMethodError: '' }) + this.state.absorptionMethod === '0' ? this.setState({ attenuationMethodError: 'Select the attenuation method' }) : this.setState({ attenuationMethodError: '' }) + console.log(this.state); + console.log(this.props.lat1 !== 0 && this.props.lat2 !== 0 && this.props.lon1 !== 0 && this.props.lon2 !== 0 && this.props.frequency !== 0 && this.state.rainMethodError === '' && this.state.attenuationMethodError === ''); + + return this.props.lat1 !== 0 && this.props.lat2 !== 0 && this.props.lon1 !== 0 && this.props.lon2 !== 0 && this.props.frequency !== 0 && this.state.rainMethodError === '' && this.state.attenuationMethodError === ''; + + } + + - formValid = () => { - - this.props.lat1 === 0 ? this.setState({latitude1Error: 'Enter a number between -90 to 90'}) : null - this.props.lat2 === 0 ? this.setState({latitude2Error: 'Enter a number between -90 to 90'}) : null - this.props.lon1 === 0 ? this.setState({longitude1Error: 'Enter a number between -180 to 180' }) : null - this.props.lon2 === 0 ? this.setState({longitude2Error: 'Enter a number between -180 to 180' }) : null - this.props.frequency === 0 ? this.setState({frequencyError: 'Select a frequency' }) : this.setState({frequencyError: ''}) - - this.state.rainMethodDisplay === null && this.props.rainVal === 0 ? this.setState({rainMethodError: 'Select the rain method'}) : this.setState({rainMethodError: ''}) - this.state.absorptionMethod === '0' ? this.setState({attenuationMethodError: 'Select the attenuation method'}) : this.setState({attenuationMethodError: ''}) - console.log(this.state); - console.log(this.props.lat1 !== 0 && this.props.lat2 !== 0 && this.props.lon1 !== 0 && this.props.lon2 !==0 && this.props.frequency!==0 && this.state.rainMethodError==='' && this.state.attenuationMethodError=== ''); - - return this.props.lat1 !== 0 && this.props.lat2 !== 0 && this.props.lon1 !== 0 && this.props.lon2 !==0 && this.props.frequency!==0 && this.state.rainMethodError==='' && this.state.attenuationMethodError=== ''; - - } - - - buttonHandler = async () => { - - if (this.formValid()) { + + if (this.formValid()) { this.props.updateAutoDistance(await this.updateAutoDistance(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2)) this.FSL(this.props.distance, this.props.frequency) - if (this.state.worstmonth===false) { - this.setState({showWM : ' '}) + if (this.state.worstmonth === false) { + this.setState({ showWM: ' ' }) this.props.UpdateWorstMonthRain('') - this.AbsorptionAtt (this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.props.distance, this.props.frequency, this.state.worstmonth, this.state.absorptionMethod) + this.AbsorptionAtt(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.props.distance, this.props.frequency, this.state.worstmonth, this.state.absorptionMethod) - if (this.state.rainMethodDisplay === true){ + if (this.state.rainMethodDisplay === true) { - this.manualRain(this.props.rainVal, this.props.frequency, this.props.distance, this.props.polarization!); + this.manualRain(this.props.rainVal, this.props.frequency, this.props.distance, this.props.polarization!); } else { // this.updateRainValue(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.state.worstmonth) this.rainAttCal(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.props.frequency, this.props.distance, this.props.polarization!, this.state.worstmonth); } - } - else { - this.AbsorptionAtt (this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.props.distance, this.props.frequency, this.state.worstmonth, this.state.absorptionMethod) + } + else { + this.AbsorptionAtt(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.props.distance, this.props.frequency, this.state.worstmonth, this.state.absorptionMethod) - // this.updateRainValue(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.state.worstmonth) + // this.updateRainValue(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.state.worstmonth) - this.rainAttCal(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.props.frequency, this.props.distance, this.props.polarization!, this.state.worstmonth); + this.rainAttCal(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.props.frequency, this.props.distance, this.props.polarization!, this.state.worstmonth); } } - else console.log('form is not valid') + else console.log('form is not valid') } componentDidMount = () => { - fetch (BASE_URL+'/fsl/1/1') - .then(res => {if (res.ok) { this.props.UpdateConectivity(true)}else {this.props.UpdateConectivity(false)} }) - .catch (res => {this.props.UpdateConectivity(false)} ) + fetch(BASE_URL + '/fsl/1/1') + .then(res => { if (res.ok) { this.props.UpdateConectivity(true) } else { this.props.UpdateConectivity(false) } }) + .catch(res => { this.props.UpdateConectivity(false) }) } - handleChange =(e:any) => { - - switch (e.target.id){ - case 'Lat1' : if ( e.target.value >90 || e.target.value<-90 ) - { this.setState({latitude1Error: 'Enter a number between -90 to 90'})} - else {this.updateLatLon(e) - this.setState({latitude1Error: ''}) } - break; - case 'Lat2' : if ( e.target.value >90 || e.target.value<-90 ) - { this.setState({latitude2Error: 'Enter a number between -90 to 90'})} - else {this.updateLatLon(e) - this.setState({latitude2Error: ''}) } - break; - case 'Lon1' : if ( e.target.value >180 || e.target.value<-180 ) - { this.setState({longitude1Error: 'Enter a number between -180 to 180'})} - else {this.updateLatLon(e) - this.setState({longitude1Error: ''}) } - break; - case 'Lon2' : if ( e.target.value >180 || e.target.value<-180 ) - { this.setState({longitude2Error: 'Enter a number between -180 to 180'})} - else {this.updateLatLon(e) - this.setState({longitude2Error: ''}) } - break; - + handleChange = (e: any) => { + + switch (e.target.id) { + case 'Lat1': if (e.target.value > 90 || e.target.value < -90) { this.setState({ latitude1Error: 'Enter a number between -90 to 90' }) } + else { + this.updateLatLon(e) + this.setState({ latitude1Error: '' }) + } + break; + case 'Lat2': if (e.target.value > 90 || e.target.value < -90) { this.setState({ latitude2Error: 'Enter a number between -90 to 90' }) } + else { + this.updateLatLon(e) + this.setState({ latitude2Error: '' }) + } + break; + case 'Lon1': if (e.target.value > 180 || e.target.value < -180) { this.setState({ longitude1Error: 'Enter a number between -180 to 180' }) } + else { + this.updateLatLon(e) + this.setState({ longitude1Error: '' }) + } + break; + case 'Lon2': if (e.target.value > 180 || e.target.value < -180) { this.setState({ longitude2Error: 'Enter a number between -180 to 180' }) } + else { + this.updateLatLon(e) + this.setState({ longitude2Error: '' }) + } + break; + } } - render() { + render() { return ( - - <div > - - {!this.props.formView && - - <div className = 'parent'> - - <div >Site A - <form > - <label>Latitude: <input aria-label="site-a-latitude-input" className={this.state.latitude1Error.length>0 ? 'error' : 'input'} id='Lat1' type='number' onChange={(e: any) => {this.handleChange(e)} }/></label> - <div style={{fontSize:12, color:'red'}}> {this.state.latitude1Error} </div></form> - <form><label>Longitude: <input aria-label="site-a-longitude-input" className={this.state.longitude1Error.length>0 ? 'error' : 'input'} id='Lon1' type='number' onChange={(e: any) => this.handleChange(e) } /></label><div style={{fontSize:12, color:'red'}}> {this.state.longitude1Error} </div> - </form> - </div> - - <div>Site B - <form> - <label>Latitude: <input aria-label="site-b-latitude-input" className={this.state.latitude2Error.length>0 ? 'error' : 'input'} id='Lat2' type='number' onChange={(e: any) => {this.handleChange(e) }} /></label><div style={{fontSize:12, color:'red'}}> {this.state.latitude2Error} </div></form> - <form><label>Longitude: <input aria-label="site-b-longitude-input" className={this.state.longitude2Error.length>0 ? 'error' : 'input'} id='Lon2' type='number' onChange={(e: any) => {this.handleChange(e) } }/></label><div style={{fontSize:12, color:'red'}}> {this.state.longitude2Error} </div></form> - </div> - + + <div > + + {!this.props.formView && + + <div className='container1'> + <div className='firstBox'> + <div>SiteA</div> + <div>SiteB</div> + </div> + + <div className='firstBox'> + <div> + <form > + <label>Latitude: <input aria-label="site-a-latitude-input" className={this.state.latitude1Error.length > 0 ? 'error' : 'input'} id='Lat1' type='number' onChange={(e: any) => { this.handleChange(e) }} /></label> + <div style={{ fontSize: 12, color: 'red' }}> {this.state.latitude1Error} </div> + </form></div> + <div> + <form> + <label>Latitude: <input aria-label="site-b-latitude-input" className={this.state.latitude2Error.length > 0 ? 'error' : 'input'} id='Lat2' type='number' onChange={(e: any) => { this.handleChange(e) }} /></label><div style={{ fontSize: 12, color: 'red' }}> {this.state.latitude2Error} </div> + </form></div> + </div> + + <div className='firstBox'> + <div> + <form><label>Longitude: <input aria-label="site-a-longitude-input" className={this.state.longitude1Error.length > 0 ? 'error' : 'input'} id='Lon1' type='number' onChange={(e: any) => this.handleChange(e)} /></label><div style={{ fontSize: 12, color: 'red' }}> {this.state.longitude1Error} </div> + </form></div> + <div> + <form><label>Longitude: <input aria-label="site-b-longitude-input" className={this.state.longitude2Error.length > 0 ? 'error' : 'input'} id='Lon2' type='number' onChange={(e: any) => { this.handleChange(e) }} /></label><div style={{ fontSize: 12, color: 'red' }}> {this.state.longitude2Error} </div></form> + </div> + </div> + + + </div> } - <div className='container-1'> - <div>{<form><input aria-label="annual" type='checkbox' id='Annual' value ="Annual" checked= {this.state.worstmonth===false} onClick= {(e: any) => this.setState ({worstmonth: false})}></input>Annual - <input aria-label="worst-month" style={{marginLeft:10}} type='checkbox' id='Worst Month' value ="Worst" checked= {this.state.worstmonth===true} onClick= {(e:any)=>this.setState ({worstmonth: true})}></input>WM</form>}</div> - - - <div className='column1'> - <div> </div> - {(this.props.siteA.length>0 || this.props.siteB.length>0) && <div >Site Name</div>} - <div>Latitude</div> - <div>Longitude</div> - <div>Azimuth</div> - <div>Average Mean Sea Level</div> - <div>Antenna Height Above Ground</div> - <div>Distance</div> - <div style={{marginTop:20}}>Polarization</div> - <div style={{marginTop:20}}>Frequency</div> - <div>Free Space Loss</div> - <div style={{marginTop:10}}>Rain Model</div> - <div style={{marginTop:20}}>Rainfall Rate</div> - <div>Rain Loss</div> - <div style={{marginTop:18}}>Absorption Model</div> - <div>Oxygen Specific Attenuation</div> - <div>Water Vapor Specific Attenuation</div> + <div className='container1'> + <div >{<form><input aria-label="annual" type='checkbox' id='Annual' value="Annual" checked={this.state.worstmonth === false} onClick={(e: any) => this.setState({ worstmonth: false })}></input>Annual + <input aria-label="worst-month" style={{ marginLeft: 10 }} type='checkbox' id='Worst Month' value="Worst" checked={this.state.worstmonth === true} onClick={(e: any) => this.setState({ worstmonth: true })}></input>WM</form>} </div> - - - <div className='middlecolumn'> - <div >Site A</div> - {this.props.siteA.length>0 &&<div> {this.props.siteA }</div>} - <div aria-label="site-a-latitude-dms"> {this.props.lat1 && this.LatLonToDMS(this.props.lat1)}</div> - <div aria-label="site-a-longitude-dms">{this.props.lon1 && this.LatLonToDMS(this.props.lon1)}</div> - <div>0</div> - <div aria-label="site-a-amsl">{this.props.amslA.toFixed(2)} m</div> - <div aria-label="site-a-antenna-amsl">{this.props.aglA.toFixed(2)} m</div> - - - <div className='column2'> - <div aria-label="distance-between-sites">{this.props.distance?.toFixed(3)} km</div> - <div>{<form><input aria-label="polarization-horizontal" type='checkbox' id='Horizontal' value ="Horizontal" checked= {this.props.polarization==='Horizontal'} onClick= {(e: any) => this.props.updatePolarization(e.target.value)}></input>Horizontal - <input aria-label="polarization-vertical" style={{marginLeft:10}} type='checkbox' id='Vertical' value ="Vertical" checked= {this.props.polarization==='Vertical'} onClick= {(e:any)=>{this.props.updatePolarization(e.target.value)}}></input>Vertical</form>}</div> - - <div> {<select aria-label="select-frequency-in-ghz" className={this.state.frequencyError.length>0 ? 'error' : 'input'} onChange={(e) => { this.props.updateFrequency(Number(e.target.value)); e.target.value==='0'? this.setState({frequencyError: 'select a frequency'}): this.setState({frequencyError:''})}}> - - <option value='0' aria-label="none-value" >Select Freq</option> - <option value='7' aria-label="7" >7 GHz</option> - <option value='11' aria-label="11" >11 GHz</option> - <option value='15' aria-label="15" >15 GHz</option> - <option value='23' aria-label="23">23 GHz</option> - <option value='26' aria-label="26">26 GHz</option> - <option value='28' aria-label="28">28 GHz</option> - <option value='38' aria-label="38">38 GHz</option> - <option value='42' aria-label="42">42 GHz</option> - <option value='80' aria-label="80">80 GHz</option> - </select>} <div style={{fontSize:12, color:'red'}}> {this.state.frequencyError} </div> </div> - - <div aria-label="fspl-value">{this.props.fsl.toFixed(3)} dB</div> - - <div> {<select aria-label="select-rain-method" className={this.state.rainMethodError.length>0 ? 'error' : 'input'} onChange = {(e) => {e.target.value === 'itu' ? this.setState({ rainMethodDisplay: false}):this.setState({ rainMethodDisplay: true}); e.target.value==='0'? this.setState({rainMethodError: 'select a Rain model'}): this.setState({rainMethodError:''}) }}> - <option value='0' aria-label="none-value" >Select Rain Method</option> - <option value='itu' aria-label="itur8377">ITU-R P.837-7</option> - <option value='manual' aria-label="manual-entry">Specific Rain</option> - </select>} <div style={{fontSize:12,color:'red'}}>{this.state.rainMethodError}</div> - </div> + <div className='firstBox'> + <div>Site A</div> + <div>Site B</div> + </div> + {/* <div> </div> */} + <div> + {(this.props.siteA.length > 0 || this.props.siteB.length > 0) && <div >Site Name</div>} + <div> {this.props.siteA}</div> + <div> {this.props.siteB}</div> + </div> + <div> + <div>Latitude</div> + <div aria-label="site-a-latitude-dms"> {this.props.lat1 && this.LatLonToDMS(this.props.lat1)}</div> + <div aria-label="site-b-latitude-dms"> {this.props.lat2 && this.LatLonToDMS(this.props.lat2)}</div> + + </div> + <div> + <div>Longitude</div> + <div aria-label="site-a-longitude-dms">{this.props.lon1 && this.LatLonToDMS(this.props.lon1)}</div> + <div aria-label="site-b-longitude-dms">{this.props.lon2 && this.LatLonToDMS(this.props.lon2)}</div> + </div> + <div> + <div>Azimuth</div> + <div>0</div> + <div>0</div> + </div> + <div> + <div>Average Mean Sea Level</div> + <div aria-label="site-a-amsl">{this.props.amslA.toFixed(2)} m</div> + <div aria-label="site-b-amsl">{this.props.amslB.toFixed(2)} m</div> + </div> + <div> + <div>Antenna Height Above Ground</div> + <div aria-label="site-a-antenna-amsl">{this.props.aglA.toFixed(2)} m</div> + <div aria-label="site-b-antenna-amsl">{this.props.aglB.toFixed(2)} m</div> + </div> + <div> + <div >Distance</div> + <div aria-label="distance-between-sites">{this.props.distance?.toFixed(3)} km</div> + </div> + <div> + <div >Polarization</div> + <div >{<form><input aria-label="polarization-horizontal" type='checkbox' id='Horizontal' value="Horizontal" checked={this.props.polarization === 'Horizontal'} onClick={(e: any) => this.props.updatePolarization(e.target.value)}></input>Horizontal + <input aria-label="polarization-vertical" style={{ marginLeft: 10 }} type='checkbox' id='Vertical' value="Vertical" checked={this.props.polarization === 'Vertical'} onClick={(e: any) => { this.props.updatePolarization(e.target.value) }}></input>Vertical</form>}</div> + </div> + <div> + <div style={{ marginTop: 5 }}>Frequency</div> + <div style={{ marginTop: 5 }}> {<select aria-label="select-frequency-in-ghz" className={this.state.frequencyError.length > 0 ? 'error' : 'input'} onChange={(e) => { this.props.updateFrequency(Number(e.target.value)); e.target.value === '0' ? this.setState({ frequencyError: 'select a frequency' }) : this.setState({ frequencyError: '' }); this.props.UpdateAntenas('0', '0') }}> + + <option value='0' aria-label="none-value" >Select Freq</option> + <option value='7' aria-label="7" >7 GHz</option> + <option value='11' aria-label="11" >11 GHz</option> + <option value='15' aria-label="15" >15 GHz</option> + <option value='23' aria-label="23">23 GHz</option> + <option value='26' aria-label="26">26 GHz</option> + <option value='28' aria-label="28">28 GHz</option> + <option value='38' aria-label="38">38 GHz</option> + <option value='42' aria-label="42">42 GHz</option> + <option value='80' aria-label="80">80 GHz</option> + </select>} <div style={{ fontSize: 12, color: 'red' }}> {this.state.frequencyError} </div> </div> + </div> + <div> + <div>Free Space Loss</div> + <div aria-label="fspl-value">{this.props.fsl.toFixed(3)} dB</div> + </div> + <div> + <div>Rain Model</div> + <div> {<select aria-label="select-rain-method" className={this.state.rainMethodError.length > 0 ? 'error' : 'input'} onChange={(e) => { e.target.value === 'itu' ? this.setState({ rainMethodDisplay: false }) : this.setState({ rainMethodDisplay: true }); e.target.value === '0' ? this.setState({ rainMethodError: 'select a Rain model' }) : this.setState({ rainMethodError: '' }) }}> + <option value='0' aria-label="none-value" >Select Rain Method</option> + <option value='itu' aria-label="itur8377">ITU-R P.837-7</option> + <option value='manual' aria-label="manual-entry">Specific Rain</option> + </select>} <div style={{ fontSize: 12, color: 'red' }}>{this.state.rainMethodError}</div> + </div> + </div> + <div> + <div>Rainfall Rate</div> <div> {<form><input aria-label="rain-value" type="number" style={{ width: 70, height: 15, fontSize: 14 }} onChange={(e) => { this.props.updateRainValue(Number(e.target.value)) }} - value={this.props.rainVal} disabled={this.state.rainMethodDisplay === false ? true : false}> - </input> mm/hr {this.state.showWM} {this.props.month}</form> } </div> - <div aria-label="rain-attenuation-value">{this.props.rainAtt.toFixed(3)} dB</div> - - <div> {<select aria-label="select-absorption-method" className={this.state.attenuationMethodError.length>0 ? 'error' : 'input'} onChange = {(e) => { if (e.target.value!== ''){ this.setState({absorptionMethod : e.target.value}); this.setState({attenuationMethodError:''}) }}}> - <option value='0' aria-label="none-value" >Select Absorption Method</option> - <option value='ITURP67612' aria-label="iturp67612" >ITU-R P.676-12</option> - <option value='ITURP67611' aria-label="iturp67611" >ITU-R P.676-11</option> - <option value='ITURP67610' aria-label="iturp67610" >ITU-R P.676-10</option> - </select>} <div style={{fontSize:12,color:'red'}}>{this.state.attenuationMethodError}</div> - </div> - <div aria-label="absorption-oxygen-value">{this.props.absorptionOxygen.toFixed(3)} dB</div> - <div aria-label="absorption-water-value">{this.props.absorptionWater.toFixed(3)} dB</div> - <div>{<button aria-label="calculate-button" style={{color: '#222', fontFamily:'Arial', boxAlign: 'center', display:'inline-block', insetInlineStart: '20' , alignSelf:'center' }} - onClick = {(e) => this.buttonHandler()} >Calculate</button>} </div> + value={this.props.rainVal} disabled={this.state.rainMethodDisplay === false ? true : false}> + </input> mm/hr {this.state.showWM} {this.props.month}</form>} </div> + </div> + <div> + <div>Rain Loss</div> + <div aria-label="rain-attenuation-value">{this.props.rainAtt.toFixed(3)} dB</div> + </div> + <div> + <div>Absorption Model</div> + <div> {<select aria-label="select-absorption-method" className={this.state.attenuationMethodError.length > 0 ? 'error' : 'input'} onChange={(e) => { if (e.target.value !== '') { this.setState({ absorptionMethod: e.target.value }); this.setState({ attenuationMethodError: '' }) } }}> + <option value='0' aria-label="none-value" >Select Absorption Method</option> + <option value='ITURP67612' aria-label="iturp67612" >ITU-R P.676-12</option> + <option value='ITURP67611' aria-label="iturp67611" >ITU-R P.676-11</option> + <option value='ITURP67610' aria-label="iturp67610" >ITU-R P.676-10</option> + </select>} <div style={{ fontSize: 12, color: 'red' }}>{this.state.attenuationMethodError}</div> + </div> + </div> + <div> + <div>Oxygen Specific Attenuation</div> + <div aria-label="absorption-oxygen-value">{this.props.absorptionOxygen.toFixed(3)} dB</div> + </div> + <div> + <div>Water Vapor Specific Attenuation</div> + <div aria-label="absorption-water-value">{this.props.absorptionWater.toFixed(3)} dB</div> + </div> + <div> + <div>System Operating Margin</div> + <div aria-label="system-operating-margin">{this.props.systemOperatingMargin} dB</div> + </div> + <div> + <div>Radio Transmitted Power</div> + <div> {<form><input aria-label="site-a-transmitted-power" type="number" style={{ width: 70, height: 15, fontSize: 14 }} onChange={(e) => {if (e.target.value !== '') this.props.UpdateTxPower(e.target.value,null) }} + > + </input> dBm </form>} </div> + <div> {<form><input aria-label="site-b-transmitted-power" type="number" style={{ width: 70, height: 15, fontSize: 14 }} onChange={(e) => { if (e.target.value !== '') this.props.UpdateTxPower(null,e.target.value) }} + > + </input> dBm </form>} </div> + </div> + <div> + <div>RF Receiver Sensitivity</div> + <div> {<form><input aria-label="site-a-receiver-sensitivity" type="number" style={{ width: 70, height: 15, fontSize: 14 }} onChange={(e) => { if (e.target.value !== '') this.props.UpdateRxSensitivity(e.target.value, null) }} + > + </input> dBm </form>} </div> + <div> {<form><input aria-label="site-b-receiver-sensitivity" type="number" style={{ width: 70, height: 15, fontSize: 14 }} onChange={(e) => { if (e.target.value !== '') this.props.UpdateRxSensitivity(null, e.target.value) }} + > + </input> dBm </form>} </div> + </div> + </div> + <div className='antennaContainer'> + <div> + <div></div> + <div className='antennaFont'>Antenna Settings</div> + </div> + <div> + <div>Antenna</div> + + <div> {<select aria-label="site-a-select-antenna" value={this.props.antennaA} style={{ width: 160, height: 22, fontSize: 13 }} className={this.state.antennaTypeError.length > 0 ? 'error' : 'input'} onChange={(e) => { if (e.target.value !== '') { this.props.UpdateAntenas(e.target.value, null); this.setState({ antennaTypeError: '' }) } }}> + <option value='0' aria-label="none-value" >Select Antenna</option> + {this.props.antennaList.map(antenna => <option value={antenna}>{antenna}</option>)} + </select>} <div style={{ fontSize: 12, color: 'red' }}>{this.state.antennaTypeError}</div> + </div> + <div> {<select aria-label="site-b-select-antenna" value={this.props.antennaB} style={{ width: 160, height: 22, fontSize: 13 }} className={this.state.antennaTypeError.length > 0 ? 'error' : 'input'} onChange={(e) => { if (e.target.value !== '') { this.props.UpdateAntenas(null, e.target.value); this.setState({ antennaTypeError: '' }) } }}> + <option value='0' aria-label="none-value" >Select Antenna</option> + {this.props.antennaList.map(antenna => <option value={antenna}>{antenna}</option>)} + </select>} <div style={{ fontSize: 12, color: 'red' }}>{this.state.antennaTypeError}</div> + </div> </div> + <div> + <div>EIRP</div> + <div aria-label="site-a-effective-isotropic-radiated-power">{this.props.eirpSiteA} dBm</div> + <div aria-label="site-b-effective-isotropic-radiated-power">{this.props.eirpSiteB} dBm</div> </div> - <div className= 'middlecolumn'> - <div >Site B</div> - {this.props.siteB.length>0 &&<div> {this.props.siteB}</div>} - <div aria-label="site-b-latitude-dms"> {this.props.lat2 && this.LatLonToDMS(this.props.lat2)}</div> - <div aria-label="site-b-longitude-dms">{this.props.lon2 && this.LatLonToDMS(this.props.lon2)}</div> - <div>0</div> - <div aria-label="site-b-asml">{this.props.amslB.toFixed(2)} m</div> - <div aria-label="site-b-antenna-asml">{this.props.aglB.toFixed(2)} m</div> - - </div> - + <div> + <div>Gain</div> + <div aria-label="site-a-antenna-gain" > {this.props.antennaGainList[this.props.antennaList.indexOf(this.props.antennaA)]} dBi</div> + <div aria-label="site-b-antenna-gain">{this.props.antennaGainList[this.props.antennaList.indexOf(this.props.antennaB)]} dBi</div> + </div> + <div> + <div></div> + <div>{<button aria-label="calculate-button" style={{ color: '#222', fontFamily: 'Arial', boxAlign: 'center', display: 'inline-block', insetInlineStart: '20', alignSelf: 'center' }} + onClick={(e) => this.buttonHandler()} >Calculate</button>} </div> + </div> </div> - + <ConnectionInfo /> - - - - </div> - ) - } - + + + </div> + + ) } +} + export default connect(mapProps, mapDispatch)(LinkCalculation); diff --git a/sdnr/wt/odlux/apps/maintenanceApp/package.json b/sdnr/wt/odlux/apps/maintenanceApp/package.json index d2980f1dc..939044e20 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/package.json +++ b/sdnr/wt/odlux/apps/maintenanceApp/package.json @@ -28,8 +28,8 @@ "@types/react": "17.0.3", "@types/react-dom": "17.0.2", "@types/react-router-dom": "5.1.7", - "@material-ui/core": "4.11.0", - "@material-ui/icons": "4.9.1", + "@material-ui/core": "4.11.4", + "@material-ui/icons": "4.11.2", "@types/classnames": "2.2.6", "@types/flux": "3.1.8", "@types/jquery": "3.3.10", diff --git a/sdnr/wt/odlux/apps/maintenanceApp/pom.xml b/sdnr/wt/odlux/apps/maintenanceApp/pom.xml index 29eec4bed..e510d2a3b 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/pom.xml +++ b/sdnr/wt/odlux/apps/maintenanceApp/pom.xml @@ -140,8 +140,8 @@ <!-- optional: default phase is "generate-resources" --> <phase>initialize</phase> <configuration> - <nodeVersion>v10.16.3</nodeVersion> - <yarnVersion>v1.19.0</yarnVersion> + <nodeVersion>v12.13.0</nodeVersion> + <yarnVersion>v1.22.10</yarnVersion> </configuration> </execution> <execution> diff --git a/sdnr/wt/odlux/apps/mediatorApp/package.json b/sdnr/wt/odlux/apps/mediatorApp/package.json index 0f0e74673..74fdd33ac 100644 --- a/sdnr/wt/odlux/apps/mediatorApp/package.json +++ b/sdnr/wt/odlux/apps/mediatorApp/package.json @@ -28,8 +28,8 @@ "@types/react": "17.0.3", "@types/react-dom": "17.0.2", "@types/react-router-dom": "5.1.7", - "@material-ui/core": "4.11.0", - "@material-ui/icons": "4.9.1", + "@material-ui/core": "4.11.4", + "@material-ui/icons": "4.11.2", "@types/classnames": "2.2.6", "@types/flux": "3.1.8", "@types/jquery": "3.3.10", diff --git a/sdnr/wt/odlux/apps/mediatorApp/pom.xml b/sdnr/wt/odlux/apps/mediatorApp/pom.xml index 9d593dc90..ffc09ca38 100644 --- a/sdnr/wt/odlux/apps/mediatorApp/pom.xml +++ b/sdnr/wt/odlux/apps/mediatorApp/pom.xml @@ -140,8 +140,8 @@ <!-- optional: default phase is "generate-resources" --> <phase>initialize</phase> <configuration> - <nodeVersion>v10.16.3</nodeVersion> - <yarnVersion>v1.19.0</yarnVersion> + <nodeVersion>v12.13.0</nodeVersion> + <yarnVersion>v1.22.10</yarnVersion> </configuration> </execution> <execution> diff --git a/sdnr/wt/odlux/apps/minimumApp/package.json b/sdnr/wt/odlux/apps/minimumApp/package.json index bcda2fb58..6c4193310 100644 --- a/sdnr/wt/odlux/apps/minimumApp/package.json +++ b/sdnr/wt/odlux/apps/minimumApp/package.json @@ -27,8 +27,8 @@ "@types/react": "17.0.3", "@types/react-dom": "17.0.2", "@types/react-router-dom": "5.1.7", - "@material-ui/core": "4.11.0", - "@material-ui/icons": "4.9.1", + "@material-ui/core": "4.11.4", + "@material-ui/icons": "4.11.2", "@types/classnames": "2.2.6", "@types/flux": "3.1.8", "@types/jquery": "3.3.10", diff --git a/sdnr/wt/odlux/apps/minimumApp/pom.xml b/sdnr/wt/odlux/apps/minimumApp/pom.xml index dd9504be1..02a0a8dac 100644 --- a/sdnr/wt/odlux/apps/minimumApp/pom.xml +++ b/sdnr/wt/odlux/apps/minimumApp/pom.xml @@ -140,8 +140,8 @@ <!-- optional: default phase is "generate-resources" --> <phase>initialize</phase> <configuration> - <nodeVersion>v10.16.3</nodeVersion> - <yarnVersion>v1.19.0</yarnVersion> + <nodeVersion>v12.13.0</nodeVersion> + <yarnVersion>v1.22.10</yarnVersion> </configuration> </execution> <execution> diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/README.md b/sdnr/wt/odlux/apps/networkMapApp/icons/README.md index 85c75c4e9..b26fbc29b 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/icons/README.md +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/README.md @@ -19,7 +19,7 @@ Copyright of icons is as followes: */ --> -datacenter.png, lamp.png and customize.png with all variations (different colors) +datacenter.png and lamp.png Taken from MS Word diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/apartment.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/apartment.png.d.ts index 9f956f813..bf398f5a4 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/icons/apartment.png.d.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/apartment.png.d.ts @@ -1,20 +1,2 @@ -/** - * ============LICENSE_START======================================================================== - * ONAP : ccsdk feature sdnr wt odlux - * ================================================================================================= - * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. - * ================================================================================================= - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - * ============LICENSE_END========================================================================== - */ - declare const apartment: string; export default apartment;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/customize.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/customize.png.d.ts index 16327f5af..7bcffb2ca 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/icons/customize.png.d.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/customize.png.d.ts @@ -1,20 +1,2 @@ -/** - * ============LICENSE_START======================================================================== - * ONAP : ccsdk feature sdnr wt odlux - * ================================================================================================= - * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved. - * ================================================================================================= - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - * ============LICENSE_END========================================================================== - */ - declare const customize: string; export default customize;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/datacenter.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/datacenter.png.d.ts index dc7019766..a58a9f5af 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/icons/datacenter.png.d.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/datacenter.png.d.ts @@ -1,20 +1,2 @@ -/** - * ============LICENSE_START======================================================================== - * ONAP : ccsdk feature sdnr wt odlux - * ================================================================================================= - * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. - * ================================================================================================= - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - * ============LICENSE_END========================================================================== - */ - declare const datacenter: string; export default datacenter;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/datacenterred.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/datacenterred.png.d.ts index 74836be8d..33f3061e2 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/icons/datacenterred.png.d.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/datacenterred.png.d.ts @@ -1,20 +1,2 @@ -/** - * ============LICENSE_START======================================================================== - * ONAP : ccsdk feature sdnr wt odlux - * ================================================================================================= - * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. - * ================================================================================================= - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - * ============LICENSE_END========================================================================== - */ - declare const datacenterred: string; export default datacenterred;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/factory.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/factory.png.d.ts index e341c5f95..b5c4f19d9 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/icons/factory.png.d.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/factory.png.d.ts @@ -1,20 +1,2 @@ -/** - * ============LICENSE_START======================================================================== - * ONAP : ccsdk feature sdnr wt odlux - * ================================================================================================= - * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. - * ================================================================================================= - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - * ============LICENSE_END========================================================================== - */ - declare const factory: string; export default factory;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/factoryred.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/factoryred.png.d.ts index 317ff7e85..1fac0a943 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/icons/factoryred.png.d.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/factoryred.png.d.ts @@ -1,20 +1,2 @@ -/** - * ============LICENSE_START======================================================================== - * ONAP : ccsdk feature sdnr wt odlux - * ================================================================================================= - * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. - * ================================================================================================= - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - * ============LICENSE_END========================================================================== - */ - declare const factoryRed: string; export default factoryRed;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/lamp.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/lamp.png.d.ts index eeedc7fcd..9634b1275 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/icons/lamp.png.d.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/lamp.png.d.ts @@ -1,20 +1,2 @@ -/** - * ============LICENSE_START======================================================================== - * ONAP : ccsdk feature sdnr wt odlux - * ================================================================================================= - * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. - * ================================================================================================= - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - * ============LICENSE_END========================================================================== - */ - declare const lamp: string; export default lamp;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/lampred.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/lampred.png.d.ts index b053df0b8..12a8f91cb 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/icons/lampred.png.d.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/lampred.png.d.ts @@ -1,20 +1,2 @@ -/** - * ============LICENSE_START======================================================================== - * ONAP : ccsdk feature sdnr wt odlux - * ================================================================================================= - * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. - * ================================================================================================= - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - * ============LICENSE_END========================================================================== - */ - declare const lampred: string; export default lampred;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/package.json b/sdnr/wt/odlux/apps/networkMapApp/package.json index cde312ce3..160537045 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/package.json +++ b/sdnr/wt/odlux/apps/networkMapApp/package.json @@ -30,8 +30,8 @@ "@types/react": "17.0.3", "@types/react-dom": "17.0.2", "@types/react-router-dom": "5.1.7", - "@material-ui/core": "4.11.0", - "@material-ui/icons": "4.9.1", + "@material-ui/core": "4.11.4", + "@material-ui/icons": "4.11.2", "@types/classnames": "2.2.6", "@types/flux": "3.1.8", "@types/jquery": "3.3.10", diff --git a/sdnr/wt/odlux/apps/networkMapApp/pom.xml b/sdnr/wt/odlux/apps/networkMapApp/pom.xml index 8284149e0..76da05dc1 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/pom.xml +++ b/sdnr/wt/odlux/apps/networkMapApp/pom.xml @@ -140,8 +140,8 @@ <!-- optional: default phase is "generate-resources" --> <phase>initialize</phase> <configuration> - <nodeVersion>v10.16.3</nodeVersion> - <yarnVersion>v1.19.0</yarnVersion> + <nodeVersion>v12.13.0</nodeVersion> + <yarnVersion>v1.22.10</yarnVersion> </configuration> </execution> <execution> diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/actions/detailsAction.ts b/sdnr/wt/odlux/apps/networkMapApp/src/actions/detailsAction.ts index 34cf10915..a9bea4fc2 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/actions/detailsAction.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/src/actions/detailsAction.ts @@ -25,6 +25,7 @@ import { link } from '../model/link'; import { HistoryEntry } from "../model/historyEntry"; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; import { Dispatch } from '../../../../framework/src/flux/store'; +import { SITEDOC_URL } from '../config'; export class SelectSiteAction extends Action { constructor(public site: Site){ @@ -80,6 +81,12 @@ export class InitializeLoadedDevicesAction extends Action{ } } +export class IsSitedocReachableAction extends Action{ + constructor(public isReachable: boolean){ + super(); + } +} + let running=false; export const UpdateDetailsView = (nodeId: string) =>(dispatcher: Dispatch, getState: () => IApplicationStoreState) =>{ @@ -137,4 +144,18 @@ running=true; dispatcher(new IsBusyCheckingDeviceListAction(false)); }); +} + +export const checkSitedockReachablity = () => async (dispatcher: Dispatch, getState: () => IApplicationStoreState) =>{ + console.log("searching for sitedoc server...") + requestRest<any>(SITEDOC_URL+'/app/versioninfo').then(response =>{ + console.log(response); + if(response){ + + dispatcher(new IsSitedocReachableAction(true)); + + }else{ + dispatcher(new IsSitedocReachableAction(false)); + } + }) }
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/denseTable.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/denseTable.tsx index 7e378b81e..e04fda547 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/components/denseTable.tsx +++ b/sdnr/wt/odlux/apps/networkMapApp/src/components/denseTable.tsx @@ -26,7 +26,16 @@ import TableRow from '@material-ui/core/TableRow'; import Paper from '@material-ui/core/Paper'; import { makeStyles, Button, Tooltip } from '@material-ui/core'; -type props = { headers: string[], height: number, navigate?(applicationName: string, path?: string): void, onLinkClick?(id: string): void, data: any[], hover: boolean, ariaLabelRow: string, ariaLabelColumn: string[], verticalTable?: boolean, onClick?(id: string): void, actions?: boolean }; +type props = { headers: string[], + height: number, + navigate?(applicationName: string, path?: string): void, + onLinkClick?(id: string): void, data: any[], + hover: boolean, + ariaLabelRow: string, + ariaLabelColumn?: string[], + verticalTable?: boolean, + onClick?(id: string): void, + actions?: boolean }; const styles = makeStyles({ @@ -81,7 +90,7 @@ const DenseTable: React.FunctionComponent<props> = (props) => { if (data !== undefined) { if (!props.verticalTable) { - const ariaLabel = props.ariaLabelColumn[i]; + const ariaLabel = props.ariaLabelColumn === undefined ? props.headers[i].toLowerCase() : props.ariaLabelColumn[i]; if (ariaLabel.length > 0) { return <TableCell aria-label={ariaLabel}>{data}</TableCell> } else { @@ -93,7 +102,7 @@ const DenseTable: React.FunctionComponent<props> = (props) => { if (i === 0) { return <TableCell>{data}</TableCell> } else { - const ariaLabel = props.ariaLabelColumn[index]; + const ariaLabel = props.ariaLabelColumn === undefined ? props.headers[index].toLowerCase() : props.ariaLabelColumn[index]; return <TableCell aria-label={ariaLabel}>{data}</TableCell> } } diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/details/linkDetails.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/details/linkDetails.tsx index 57091aebf..96727cc0d 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/components/details/linkDetails.tsx +++ b/sdnr/wt/odlux/apps/networkMapApp/src/components/details/linkDetails.tsx @@ -37,7 +37,7 @@ const LinkDetails: React.FunctionComponent<props> = (props) => { if(el && el2){ if(props.link.type==="microwave") - setHeight(el!.height - el2!.y -30); + setHeight(el!.height - el2!.y -50); else setHeight(el!.height - el2!.y +20); @@ -68,10 +68,34 @@ const LinkDetails: React.FunctionComponent<props> = (props) => { const distance = props.link.length > 0 ? props.link.length : props.link.calculatedLength; const azimuthA = props.link.azimuthA; const azimuthB = props.link.azimuthB; + const antennaA = props.link.locationA.antenna; + const antennaB = props.link.locationB.antenna; + + + let antennaData = ""; + if(antennaA!==null && antennaB!==null){ + antennaData = `&antennaNameA=${antennaA.name}&antennaGainA=${antennaA.gain}&waveguideLossA=${antennaA.waveguideLossIndB}&antennaNameB=${antennaB.name}&antennaGainB=${antennaB.gain}&waveguideLossB=${antennaB.waveguideLossIndB}`; + } + + const baseUrl = window.location.pathname.split('#')[0]; - window.open(`${baseUrl}#/linkCalculation?lat1=${siteA.lat}&lon1=${siteA.lon}&lat2=${siteB.lat}&lon2=${siteB.lon}&siteA=${nameA}&siteB=${nameB}&azimuthA=${azimuthA}&azimuthB=${azimuthB}&distance=${distance}&amslSiteA=${siteA.amsl}&AGLsiteA=${siteA.antennaHeight}&amslSiteB=${siteB.amsl}&AGLsiteB=${siteB.antennaHeight}`) + window.open(`${baseUrl}#/linkCalculation?lat1=${siteA.lat}&lon1=${siteA.lon}&lat2=${siteB.lat}&lon2=${siteB.lon}&siteA=${nameA}&siteB=${nameB}&azimuthA=${azimuthA}&azimuthB=${azimuthB}&distance=${distance}&amslSiteA=${siteA.amsl}&AGLsiteA=${siteA.antennaHeight}&amslSiteB=${siteB.amsl}&AGLsiteB=${siteB.antennaHeight}${antennaData}`) + + } + + const onLineofSightClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) =>{ + e.preventDefault(); + + const siteA= props.link.locationA; + const siteB =props.link.locationB; + + //TODO: add check if available + let heightPart = `&amslA=${siteA.amsl}&antennaHeightA=${siteA.antennaHeight}&amslB=${siteB.amsl}&antennaHeightB=${siteB.antennaHeight}`; + + const baseUrl = window.location.pathname.split('#')[0]; + window.open(`${baseUrl}#/lineofsight/los?lat1=${siteA.lat}&lon1=${siteA.lon}&lat2=${siteB.lat}&lon2=${siteB.lon}${heightPart}`); } const data = [ @@ -94,7 +118,11 @@ const LinkDetails: React.FunctionComponent<props> = (props) => { </AppBar> <DenseTable ariaLabelRow="site-information-table-entry" ariaLabelColumn={["site-name", "latitude", "longitude", "azimuth"]} verticalTable height={height} hover={false} headers={["", "Site A", "Site B"]} data={data} /> { - props.link.type==="microwave" && <Button style={{marginTop:20}} fullWidth variant="contained" color="primary" onClick={onCalculateLinkClick}>Calculate link</Button> + props.link.type==="microwave" &&<> + <Button style={{marginTop:20}} fullWidth variant="contained" color="primary" onClick={onCalculateLinkClick}>Calculate link</Button> + <Button style={{marginTop:20}} fullWidth variant="contained" color="primary" onClick={onLineofSightClick}>Line of Sight</Button> + + </> } </div>) } diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/details/siteDetails.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/details/siteDetails.tsx index 3aa35c348..7f0c1c926 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/components/details/siteDetails.tsx +++ b/sdnr/wt/odlux/apps/networkMapApp/src/components/details/siteDetails.tsx @@ -30,6 +30,9 @@ import { CheckDeviceList, InitializeLoadedDevicesAction } from '../../actions/de import { NavigateToApplication } from '../../../../../framework/src/actions/navigationActions'; import connect, { Connect, IDispatcher } from '../../../../../framework/src/flux/connect'; import { IApplicationStoreState } from '../../../../../framework/src/store/applicationStore'; +import StadokSite from '../../model/stadokSite'; +import { requestRest } from '../../../../../framework/src/services/restService'; +import StadokDetailsPopup from './stadokDetailsPopup'; type linkRow = { name: string, azimuth?: string} type deviceRow = { id: string;type: string,name: string,manufacturer: string,owner: string,status?: string,port: number[]} @@ -48,6 +51,8 @@ const SiteDetails: React.FunctionComponent<siteDetailProps> = (props) => { const [value, setValue] = React.useState<panelId>("links"); const [height, setHeight] = React.useState(330); + const [openPopup, setOpenPopup] = React.useState(false); + const [staSite, setStaSite] = React.useState<StadokSite|null>(null); const handleResize = () =>{ const el = document.getElementById('site-details-panel')?.getBoundingClientRect(); @@ -82,35 +87,55 @@ const SiteDetails: React.FunctionComponent<siteDetailProps> = (props) => { setValue(newValue); } + const getFurtherInformation = (url: string) =>{ + + const request = requestRest<StadokSite>(url, { method: "GET"}); + + request.then(result =>{ + if(result){ + setStaSite(result); + setOpenPopup(true); + }else{ + console.error(result); + } + + + }); + } + + const closePopup = () =>{ + setOpenPopup(false); + } + //prepare link table let hasAzimuth = false; - const linkRows: linkRow[] = props.site.links.map(link=> + const linkRows: linkRow[] = props.site.links?.map(link=> { if(link.azimuthB!==null){ hasAzimuth=true; - return {name: link.name, azimuth: link.azimuthB.toFixed(2) } + return {name: link.name, azimuth: link.azimuthB.toFixed(2) } - }else{ - return {name: link.name } - } - - }); + }else{ + return {name: link.name } + } + }); const linkTableHeader = hasAzimuth ? ["Link Name", "Azimuth in °"] : ["Link Name"]; //prepare device table - const deviceRows : deviceRow[] = props.updatedDevices.map(device=>{ - return{ - id: device.id, - name: device.name, - type: device.type, - status: device.status, - manufacturer: device.manufacturer, - owner: device.owner, - port: device.port - } - }); + const deviceRows : deviceRow[] = props.updatedDevices?.map(device=>{ + return{ + id: device.id, + name: device.name, + type: device.type, + status: device.status, + manufacturer: device.manufacturer, + owner: device.owner, + port: device.port + } + }); + const adressString = props.site.address == null ? null : buildAdress(props.site.address); @@ -152,12 +177,12 @@ const SiteDetails: React.FunctionComponent<siteDetailProps> = (props) => { value === "links" && <> { - props.site.links.length === 0 && + props.site.links==null && <Typography aria-label="no-links-available" variant="body1" style={{ marginTop: '10px' }}>No links available.</Typography> } { - props.site.links.length > 0 && + props.site.links?.length > 0 && <DenseTable ariaLabelRow="available-links-table" ariaLabelColumn={["link-name", "azimuth"]} height={height} hover={true} headers={linkTableHeader} data={linkRows} onClick={props.onLinkClick} ></DenseTable> } @@ -178,6 +203,15 @@ const SiteDetails: React.FunctionComponent<siteDetailProps> = (props) => { } </> } + { + props.isSitedocReachable && props.site.furtherInformation!==null && props.site.furtherInformation.length>0 && + <Button style={{marginTop:20}} fullWidth variant="contained" color="primary" onClick={e => getFurtherInformation(props.site.furtherInformation) }>Further information available</Button> + } + + { + staSite !== null && openPopup && <StadokDetailsPopup site={staSite} onClose={closePopup} open={true} /> + } + </div> ) } @@ -200,7 +234,8 @@ const buildAdress = (adress: Address) =>{ } const mapStateToProps = (state: IApplicationStoreState) => ({ - updatedDevices: state.network.details.checkedDevices + updatedDevices: state.network.details.checkedDevices, + isSitedocReachable: state.network.details.isSitedocReachable }); const mapDispatchToProps = (dispatcher: IDispatcher) => ({ diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/details/stadokDetailsPopup.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/details/stadokDetailsPopup.tsx new file mode 100644 index 000000000..4f3235db7 --- /dev/null +++ b/sdnr/wt/odlux/apps/networkMapApp/src/components/details/stadokDetailsPopup.tsx @@ -0,0 +1,274 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import * as React from 'react'; + +import MuiDialogTitle from '@material-ui/core/DialogTitle'; +import { AppBar, Dialog, DialogContent, IconButton, Tab, Tabs, TextField, Typography } from '@material-ui/core'; +import CloseIcon from '@material-ui/icons/Close'; +import { withStyles, WithStyles, createStyles, Theme, makeStyles } from '@material-ui/core/styles'; + + +import StadokSite from '../../model/stadokSite'; +import { LatLonToDMS } from '../../utils/mapUtils'; +import DenseTable from '../../components/denseTable'; +import { requestRest } from '../../../../../framework/src/services/restService'; +import { OrderToDisplay, StadokOrder } from '../../model/stadokOrder'; +import { CSSProperties } from '@material-ui/core/styles/withStyles'; +import { SITEDOC_URL } from '../../config'; + + +type props = { site: StadokSite; onClose(): void; open:boolean }; + +const styles = (theme: Theme) => createStyles({ + root: { + margin: 0, + padding: theme.spacing(2), + }, + closeButton: { + position: 'absolute', + right: theme.spacing(1), + top: theme.spacing(1), + color: theme.palette.grey[500], + }, +}); + +const useStyles = makeStyles({ + largeImage:{cursor:'pointer', width:300}, + smallImage:{cursor:'pointer', width: 50, marginTop:'10px', marginLeft:'10px'} +}); + +const StadokDetailsPopup: React.FunctionComponent<props> = (props) => { + const classes = useStyles(); + + const [open, setOpen] = React.useState(props.open); + const [value, setValue] = React.useState("devices"); + const [orders, setOrders] = React.useState<OrderToDisplay[]|null>(null); + + const DialogTitle = withStyles(styles)((props: any) => { + const { children, classes, onClose, ...other } = props; + return ( + <MuiDialogTitle disableTypography className={classes.root} {...other}> + <Typography variant="h6">{children}</Typography> + {onClose ? ( + <IconButton aria-label="close" style={{position: 'absolute', top:0, right:0, color: 'black'}} onClick={onClose}> + <CloseIcon /> + </IconButton> + ) : null} + </MuiDialogTitle> + ); + }); + + const getContacts = (site: StadokSite) =>{ + const contacts = []; + + if(site.createdBy){ + contacts.push({h: "Site Creator",col1: site.createdBy.firstName, col2: site.createdBy.lastName, col3: site.createdBy.email, col4: site.createdBy.telephoneNumber }); + } + + if(site.contacts.manager){ + contacts.push({h: "Manager",col1: site.contacts.manager.firstName, col2: site.contacts.manager.lastName, col3: site.contacts.manager.email, col4: site.contacts.manager.telephoneNumber }); + } + + if(site.contacts.owner){ + contacts.push({h: "Owner",col1: site.contacts.owner.firstName, col2: site.contacts.owner.lastName, col3: site.contacts.owner.email, col4: site.contacts.owner.telephoneNumber }); + } + return contacts; + } + + const onClose = () =>{ + // setOpen(false); + props.onClose() + } + + //todo: use a set 'panelId' -> which values are allowed + const onHandleTabChange = (event: React.ChangeEvent<{}>, newValue: string) => { + setValue(newValue); +} +console.log(props.site) + const contacts = getContacts(props.site); + + const orderUrl=`${SITEDOC_URL}/site/${props.site.siteId}/orders`; + + if(orders==null){ + requestRest<StadokOrder[]>(orderUrl,{ method: "GET"}).then(result =>{ + if(result){ + const orderList = result.map(order =>{ + return OrderToDisplay.parse(order); + }); + setOrders(orderList); + + }else{ + setOrders([]); + } + }); + } + + const createOrderInfo = () => { + + if (orders === null) { + return (<div style={{ height: 300 }}> + <Typography variant="body1" style={{ marginTop: '10px' }}> + Loading orders + </Typography> + </div>) + } else if (orders.length === 0) { + return (<div style={{ height: 300 }}> + <Typography variant="body1" style={{ marginTop: '10px' }}> + No orders available + </Typography> + </div>) + } else { + return <DenseTable data={orders} height={300} headers={["Person", "State", "Current Task"]} hover={false} ariaLabelRow="activity-log-table" /> + } + } + + const displayImages = () => { + + if (props.site.images.length === 1) { + return stadokImage(props.site.siteId, props.site.images[0],"large") + } else { + return <> + { + stadokImage(props.site.siteId, props.site.images[0], "large") + } + <div style={{ display: 'flex', flexDirection: 'row', flexWrap:'wrap' }}> + + { + props.site.images.length<=9 ? + props.site.images.slice(1, props.site.images.length).map(image => + stadokImage(props.site.siteId, image, "small") + ) + : + <> + { + props.site.images.slice(1, 9).map(image => + stadokImage(props.site.siteId, image, "small") + ) + } + + </> + } + </div> + </> + } + + } + + const stadokImage = (siteId: string, imagename: string, size: 'large' | 'small') => { + const url = `${SITEDOC_URL}/site/${siteId}/files/${imagename}`; + const className = size === "small" ? classes.smallImage : classes.largeImage; + return <img className={className} src={url} onClick={e => window.open(url)} /> + + } + + + return (<Dialog onClose={onClose} fullWidth maxWidth="md" aria-labelledby="customized-dialog-title" open={open}> + <DialogTitle id="customized-dialog-title" onClose={onClose}> + {props.site.siteId} + </DialogTitle> + <DialogContent style={{minWidth:'900px'}} dividers> + <div style={{ display: 'flex', flexDirection: 'row', flexGrow: 1 }}> + <div style={{ width: '60%', display:'flex', flexDirection: 'column' }}> + + <TextField inputProps={{ 'aria-label': 'type' }} disabled={true} value={props.site.updatedOn} label="Updated on" style={{ marginTop: "5px" }} /> + + + { + props.site.type !== undefined && props.site.type.length > 0 && + <TextField inputProps={{ 'aria-label': 'type' }} disabled={true} value={props.site.type} label="Type" style={{ marginTop: "5px" }} /> + } + + + <TextField inputProps={{ 'aria-label': 'adress' }} disabled={true} value={`${props.site.address.streetAndNr}, ${props.site.address.zipCode !== null ? props.site.address.zipCode : ''} ${props.site.address.city}`} label="Address" style={{ marginTop: "5px" }} /> + + + <TextField inputProps={{ 'aria-label': 'latitude' }} style={{ marginTop: "5px" }} disabled={true} value={LatLonToDMS(props.site.location.lat)} label="Latitude" /> + <TextField inputProps={{ 'aria-label': 'longitude' }} style={{ marginTop: "5px" }} disabled={true} value={LatLonToDMS(props.site.location.lon, true)} label="Longitude" /> + <AppBar position="static" style={{ marginTop: "5px", background: '#2E3B55' }}> + <Tabs id="site-tabs" variant="scrollable" scrollButtons="on" value={value} onChange={onHandleTabChange} aria-label="simple tabs example"> + <Tab label="Devices" value="devices" /> + <Tab label="Contacts" value="contacts" /> + <Tab label="Saftey" value="safteyInfo" /> + <Tab label="Logs" value="logs" /> + <Tab label="Orders" value="orders" /> + </Tabs> + </AppBar> + { + value == "devices" && (props.site.devices?.length>0 ? + <DenseTable data={props.site.devices} height={300} headers={["Device", "Antenna"]} hover={false} ariaLabelRow="devices-table" /> + : + <div style={{height:300}}> + <Typography variant="body1" style={{ marginTop: '10px' }}> + No devices available + </Typography> + </div>) + } + { + value == "contacts" && (contacts.length>0 ? + <DenseTable data={contacts} height={300} headers={["Person", "Firstname", "Lastname", "Email", "Phone No."]} hover={false} ariaLabelRow="contacts-table" ariaLabelColumn={["person", "firstname", "lastname", "email", "phoneno"]} /> + : + <div style={{height:300}}> + <Typography variant="body1" style={{ marginTop: '10px' }}> + No contacts available + </Typography> + </div>) + } + { + value == "safteyInfo" && (props.site.safteyNotices.length>0 ? + <DenseTable data={props.site.safteyNotices} height={300} headers={["Note"]} hover={false} ariaLabelRow="saftey-info-table" /> + : + <div style={{height:300}}> + <Typography variant="body1" style={{ marginTop: '10px' }}> + No saftey notices applicable + </Typography> + </div>) + } + { + value == "logs" && (props.site.logs.length>0 ? + <DenseTable data={props.site.logs} height={300} headers={["Date","Person", "Activity"]} hover={false} ariaLabelRow="activity-log-table" /> + : + <div style={{height:300}}> + <Typography variant="body1" style={{ marginTop: '10px' }}> + No activity log available + </Typography> + </div>) + } + + { + value ==="orders" && createOrderInfo() + } + + </div> + <div style={{padding: '10px', display: 'flex', alignItems:'center', flexDirection:'column', justifyContent: 'start', width:'40%'}}> + { + props.site.images.length == 0 ? + <Typography variant="body1" style={{ marginTop: '10px' }}> + No images available + </Typography> + : displayImages() + } + </div> + </div> + + </DialogContent> + </Dialog>) + +} + +export default StadokDetailsPopup;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/map/connectionInfo.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/connectionInfo.tsx index c1fdccfb8..3b5a15ce5 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/components/map/connectionInfo.tsx +++ b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/connectionInfo.tsx @@ -29,7 +29,7 @@ type props = Connect<typeof mapStateToProps, typeof mapDispatchToProps>; const ConnectionInfo: React.FunctionComponent<props> = (props) => { - return ((props.isTopoServerReachable === false || props.isTileServerReachable === false )? <Paper style={{padding:5, position: 'absolute', top: 160, width: 230, left:"40%"}}> + return ((props.isTopoServerReachable === false || props.isTileServerReachable === false )? <Paper style={{padding:5, position: 'absolute', top: 160, width: 230, left:"40%", zIndex:1}}> <div style={{display: 'flex', flexDirection: 'column'}}> <div style={{'alignSelf': 'center', marginBottom:5}}> <Typography> <FontAwesomeIcon icon={faExclamationTriangle} /> Connection Error</Typography></div> {props.isTileServerReachable === false && <Typography> Tile data can't be loaded.</Typography>} diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/map/iconSwitch.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/iconSwitch.tsx index bb98cb467..221e7dab8 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/components/map/iconSwitch.tsx +++ b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/iconSwitch.tsx @@ -32,9 +32,9 @@ const IconSwitch: React.FunctionComponent<props> = (props) =>{ return ( props.visible ? - <FormControlLabel style={{ padding:5, position: 'absolute',top: 190}} + <FormControlLabel style={{ padding:5, position: 'absolute',top: 190, zIndex:1}} value="end" - control={<Switch color="secondary" checked={props.areIconsEnabled} onChange={toggleChecked} />} + control={<Switch color="secondary" style={{zIndex:1}} checked={props.areIconsEnabled} onChange={toggleChecked} />} label="Show icons" labelPlacement="end" />: null) diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/map/map.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/map.tsx index 9637d745e..1314edbba 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/components/map/map.tsx +++ b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/map.tsx @@ -54,6 +54,7 @@ let notLoadedBoundingBoxes: mapboxgl.LngLatBounds[] = []; let lastBoundingBox: mapboxgl.LngLatBounds | null = null; let myRef = React.createRef<HTMLDivElement>(); +import 'mapbox-gl/dist/mapbox-gl.css'; class Map extends React.Component<mapProps, { isPopupOpen: boolean }> { @@ -158,7 +159,7 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> { mapLayerService.addBaseSources(map, this.props.selectedSite, this.props.selectedLink); addImages(map, (result: boolean)=>{ - if(map.getZoom()>11) + if(map.getZoom()>11 && this.props.showIcons) { mapLayerService.addIconLayers(map, this.props.selectedSite?.properties.id) }else{ @@ -190,134 +191,142 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> { } }) .catch(error => this.props.handleConnectionError(error)); + + map.on('click', this.mapClick); + map.on('moveend', this.mapMoveEnd); + map.on('move', this.mapMove); + }); + } - map.on('click', (e: any) => { + mapMove = () => { + + const mapZoom = map.getZoom(); - if (map.getLayer('points')) { // data is shown as points + const boundingBox = map.getBounds(); + + this.loadNetworkData(boundingBox); + if (mapZoom > 9) { - var clickedLines = getUniqueFeatures(map.queryRenderedFeatures([[e.point.x - 5, e.point.y - 5], - [e.point.x + 5, e.point.y + 5]], { - layers: ['microwave-lines', 'fibre-lines'] - }), "id"); + if (map.getLayer('points')) { + map.setLayoutProperty('selectedPoints', 'visibility', 'visible'); + map.setPaintProperty('points', 'circle-radius', 7); + } + } else { - const clickedPoints = getUniqueFeatures(map.queryRenderedFeatures(e.point, { layers: ['points'] }), "id"); - const alarmedSites = getUniqueFeatures(map.queryRenderedFeatures(e.point, { layers: ['alarmedPoints'] }), "id"); + // reduce size of points / lines if zoomed out + map.setPaintProperty('points', 'circle-radius', 2); + map.setLayoutProperty('selectedPoints', 'visibility', 'none'); - if (clickedPoints.length != 0) { + if (mapZoom <= 4) { + map.setPaintProperty('fibre-lines', 'line-width', 1); + map.setPaintProperty('microwave-lines', 'line-width', 1); + + } else { + map.setPaintProperty('fibre-lines', 'line-width', 2); + map.setPaintProperty('microwave-lines', 'line-width', 2); + } + } + }; + mapClick = (e: any) => { - if (alarmedSites.length > 0) { - alarmedSites.forEach(alarm => { - const index = clickedPoints.findIndex(item => item.properties!.id === alarm.properties!.id); + + if (map.getLayer('points')) { // data is shown as points - if (index !== -1) { - clickedPoints[index].properties!.alarmed = true; - clickedPoints[index].properties!.type = "alarmed"; - } - }); - } + var clickedLines = getUniqueFeatures(map.queryRenderedFeatures([[e.point.x - 5, e.point.y - 5], + [e.point.x + 5, e.point.y + 5]], { + layers: ['microwave-lines', 'fibre-lines'] + }), "id"); - this.showSitePopup(clickedPoints, e.point.x, e.point.y); - } else if (clickedLines.length != 0) { - this.showLinkPopup(clickedLines, e.point.x, e.point.y); - } + const clickedPoints = getUniqueFeatures(map.queryRenderedFeatures(e.point, { layers: ['points'] }), "id"); + const alarmedSites = getUniqueFeatures(map.queryRenderedFeatures(e.point, { layers: ['alarmedPoints'] }), "id"); + if (clickedPoints.length != 0) { - } else { // data is shown as icons - const clickedSites = getUniqueFeatures(map.queryRenderedFeatures(e.point, { layers: ['point-lamps', 'point-building', 'point-data-center', 'point-factory', 'point-remaining'] }), "id"); - const clickedLines = getUniqueFeatures(map.queryRenderedFeatures([[e.point.x - 5, e.point.y - 5], - [e.point.x + 5, e.point.y + 5]], { - layers: ['microwave-lines', 'fibre-lines'] - }), "id"); + if (alarmedSites.length > 0) { + alarmedSites.forEach(alarm => { + const index = clickedPoints.findIndex(item => item.properties!.id === alarm.properties!.id); - if (clickedSites.length > 0) - this.showSitePopup(clickedSites, e.point.x, e.point.y); - else if (clickedLines.length != 0) { - this.showLinkPopup(clickedLines, e.point.x, e.point.y); + if (index !== -1) { + clickedPoints[index].properties!.alarmed = true; + clickedPoints[index].properties!.type = "alarmed"; + } + }); } - } - }); + this.showSitePopup(clickedPoints, e.point.x, e.point.y); + } else if (clickedLines.length != 0) { + this.showLinkPopup(clickedLines, e.point.x, e.point.y); + } - map.on('moveend', () => { - const mapZoom = Number(map.getZoom().toFixed(2)); - const lat = Number(map.getCenter().lat.toFixed(4)); - const lon = Number(map.getCenter().lng.toFixed(4)); + } else { // data is shown as icons + const clickedSites = getUniqueFeatures(map.queryRenderedFeatures(e.point, { layers: ['point-lamps', 'point-building', 'point-data-center', 'point-factory', 'point-remaining'] }), "id"); + const clickedLines = getUniqueFeatures(map.queryRenderedFeatures([[e.point.x - 5, e.point.y - 5], + [e.point.x + 5, e.point.y + 5]], { + layers: ['microwave-lines', 'fibre-lines'] + }), "id"); - if (this.props.lat !== lat || this.props.lon !== lon || this.props.zoom !== mapZoom) { - this.props.updateMapPosition(lat, lon, mapZoom) + if (clickedSites.length > 0) + this.showSitePopup(clickedSites, e.point.x, e.point.y); + else if (clickedLines.length != 0) { + this.showLinkPopup(clickedLines, e.point.x, e.point.y); } + } + }; - // update the url to current lat,lon,zoom values + mapMoveEnd = () => { - const currentUrl = window.location.href; - const parts = currentUrl.split(URL_BASEPATH); - if(parts.length>0){ + const mapZoom = Number(map.getZoom().toFixed(2)); + const lat = Number(map.getCenter().lat.toFixed(4)); + const lon = Number(map.getCenter().lng.toFixed(4)); - const detailsPath = parts[1].split("/details/"); - if (detailsPath[1] !== undefined && detailsPath[1].length > 0) { - this.props.history.replace(`/${URL_BASEPATH}/${map.getCenter().lat.toFixed(4)},${map.getCenter().lng.toFixed(4)},${mapZoom.toFixed(2)}/details/${detailsPath[1]}`) - } - else { - this.props.history.replace(`/${URL_BASEPATH}/${map.getCenter().lat.toFixed(4)},${map.getCenter().lng.toFixed(4)},${mapZoom.toFixed(2)}`) - } - } - - - //switch icon layers if applicable - mapLayerService.showIconLayers(map, this.props.showIcons, this.props.selectedSite?.properties.id); + if (this.props.lat !== lat || this.props.lon !== lon || this.props.zoom !== mapZoom) { + this.props.updateMapPosition(lat, lon, mapZoom) + } - //update statistics - const boundingBox = map.getBounds(); + // update the url to current lat,lon,zoom values - fetch(`${URL_API}/info/count/${boundingBox.getWest()},${boundingBox.getSouth()},${boundingBox.getEast()},${boundingBox.getNorth()}`) - .then(result => verifyResponse(result)) - .then(res => res.json()) - .then(result => { - if (result.links !== this.props.linkCount || result.sites !== this.props.siteCount) { - this.props.setStatistics(result.links, result.sites); - } - }) - .catch(error => this.props.handleConnectionError(error));; - }) + const currentUrl = window.location.href; + const parts = currentUrl.split(URL_BASEPATH); + if (parts.length > 0) { - map.on('move', () => { - const mapZoom = map.getZoom(); + const detailsPath = parts[1].split("/details/"); - const boundingBox = map.getBounds(); + if (detailsPath[1] !== undefined && detailsPath[1].length > 0) { + this.props.history.replace(`/${URL_BASEPATH}/${map.getCenter().lat.toFixed(4)},${map.getCenter().lng.toFixed(4)},${mapZoom.toFixed(2)}/details/${detailsPath[1]}`) + } + else { + this.props.history.replace(`/${URL_BASEPATH}/${map.getCenter().lat.toFixed(4)},${map.getCenter().lng.toFixed(4)},${mapZoom.toFixed(2)}`) + } + } - this.loadNetworkData(boundingBox); - if (mapZoom > 9) { - if (map.getLayer('points')) { - map.setLayoutProperty('selectedPoints', 'visibility', 'visible'); - map.setPaintProperty('points', 'circle-radius', 7); - } - } else { + //switch icon layers if applicable - // reduce size of points / lines if zoomed out - map.setPaintProperty('points', 'circle-radius', 2); - map.setLayoutProperty('selectedPoints', 'visibility', 'none'); + mapLayerService.showIconLayers(map, this.props.showIcons, this.props.selectedSite?.properties.id); - if (mapZoom <= 4) { - map.setPaintProperty('fibre-lines', 'line-width', 1); - map.setPaintProperty('microwave-lines', 'line-width', 1); + //update statistics + const boundingBox = map.getBounds(); - } else { - map.setPaintProperty('fibre-lines', 'line-width', 2); - map.setPaintProperty('microwave-lines', 'line-width', 2); + fetch(`${URL_API}/info/count/${boundingBox.getWest()},${boundingBox.getSouth()},${boundingBox.getEast()},${boundingBox.getNorth()}`) + .then(result => verifyResponse(result)) + .then(res => res.json()) + .then(result => { + if (result.links !== this.props.linkCount || result.sites !== this.props.siteCount) { + this.props.setStatistics(result.links, result.sites); } - } - }); + }) + .catch(error => this.props.handleConnectionError(error));; } componentDidUpdate(prevProps: mapProps, prevState: {}) { + if(prevProps !== this.props){ //(load map) //triggered if either settings were done loading or tile/topology server connectivity checked if(prevProps.settings !== this.props.settings || this.props.isConnectivityCheckBusy !== prevProps.isConnectivityCheckBusy){ @@ -329,6 +338,7 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> { //if everything done loading/reachable, load map if(!this.props.isConnectivityCheckBusy && this.props.isTileServerReachable && !this.props.settings.isLoadingData && (prevProps.settings.isLoadingData !==this.props.settings.isLoadingData || prevProps.isConnectivityCheckBusy !== this.props.isConnectivityCheckBusy)){ + if(map == undefined){ this.setupMap(); } @@ -443,12 +453,22 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> { } } } + } } componentWillUnmount(){ + + //unregister events window.removeEventListener("menu-resized", this.handleResize); - lastBoundingBox=null; + + if(map){ + map.off('click', this.mapClick); + map.off('moveend', this.mapMoveEnd); + map.off('move', this.mapMove); + } + lastBoundingBox=null; + // will be checked again on next load this.props.setConnectivityCheck(true); } @@ -624,7 +644,7 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> { <ConnectionInfo /> <Button disabled={!this.props.isTopoServerReachable} - style={{'position': 'absolute', 'right':5, top:5, backgroundColor:'white'}} + style={{'position': 'absolute', 'right':5, top:5, backgroundColor:'white', zIndex:1}} onClick={e => this.props.navigateToApplication("network", "customize")} > <img src={customize} /> </Button> diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/map/searchBar.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/searchBar.tsx index 45bc6092d..307c5d203 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/components/map/searchBar.tsx +++ b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/searchBar.tsx @@ -46,6 +46,7 @@ const styles = makeStyles({ top: 15, marginLeft: 5, width: 400, + zIndex:1 }, input: { flex: 1, diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/map/statistics.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/statistics.tsx index 116b789d7..562689198 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/components/map/statistics.tsx +++ b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/statistics.tsx @@ -41,7 +41,7 @@ const Statistics: React.FunctionComponent<props> = (props: props) =>{ const reachabe = props.isTopoServerReachable && props.isTileServerReachable; - return (<Paper style={{ padding: 5, position: 'absolute', display: 'flex', flexDirection: "column", top: 70, width: 200, marginLeft: 5 }}> + return (<Paper style={{ padding: 5, position: 'absolute', display: 'flex', flexDirection: "column", top: 70, width: 200, marginLeft: 5, zIndex:1 }}> <div style={{ display: 'flex', flexDirection: "row" }}> <Typography style={{ fontWeight: "bold", flex: "1", color: reachabe ? "black" : "lightgrey" }} >Statistics</Typography> <Tooltip style={{ alignSelf: "flex-end" }} title="Gets updated when the map stops moving."> diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/config.ts b/sdnr/wt/odlux/apps/networkMapApp/src/config.ts index 633bd3732..bdb7d15e6 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/config.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/src/config.ts @@ -17,6 +17,7 @@ */ export const URL_API="/topology/network" +export const SITEDOC_URL="/sitedoc"; export const URL_TILE_API = '/tiles'; // http://tile.openstreetmap.org can be used for local testing, never commit with tile url changed! /tiles diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/handlers/detailsReducer.ts b/sdnr/wt/odlux/apps/networkMapApp/src/handlers/detailsReducer.ts index 67e10e629..8a7fc6ada 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/handlers/detailsReducer.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/src/handlers/detailsReducer.ts @@ -20,13 +20,14 @@ import { IActionHandler } from '../../../../framework/src/flux/action'; import { link } from "../model/link"; import { Site, Device } from "../model/site"; import { HistoryEntry } from "../model/historyEntry"; -import { SelectSiteAction, SelectLinkAction, AddToHistoryAction, ClearHistoryAction, IsBusyCheckingDeviceListAction, FinishedLoadingDeviceListAction, ClearLoadedDevicesAction, ClearDetailsAction, InitializeLoadedDevicesAction } from '../actions/detailsAction'; +import { SelectSiteAction, SelectLinkAction, AddToHistoryAction, ClearHistoryAction, IsBusyCheckingDeviceListAction, FinishedLoadingDeviceListAction, ClearLoadedDevicesAction, ClearDetailsAction, InitializeLoadedDevicesAction, IsSitedocReachableAction } from '../actions/detailsAction'; export type DetailsStoreState={ data: Site | link | null, history: HistoryEntry[], isBusyCheckingDeviceList: boolean, - checkedDevices: Device[] + checkedDevices: Device[], + isSitedocReachable: boolean } @@ -34,7 +35,8 @@ const initialState: DetailsStoreState = { data: null, history:[], isBusyCheckingDeviceList: false, - checkedDevices: [] + checkedDevices: [], + isSitedocReachable: false } export const DetailsReducer:IActionHandler<DetailsStoreState>=(state = initialState, action)=>{ @@ -63,6 +65,8 @@ export const DetailsReducer:IActionHandler<DetailsStoreState>=(state = initialSt }else if(action instanceof InitializeLoadedDevicesAction){ state = Object.assign({}, state, {checkedDevices: action.devices}); + }else if(action instanceof IsSitedocReachableAction){ + state = Object.assign({}, state, {isSitedocReachable: action.isReachable}); } diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/index.html b/sdnr/wt/odlux/apps/networkMapApp/src/index.html index e64c08c1e..f70571152 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/index.html +++ b/sdnr/wt/odlux/apps/networkMapApp/src/index.html @@ -13,15 +13,14 @@ <div id="app"></div> <script type="text/javascript" src="./require.js"></script> <script type="text/javascript" src="./config.js"></script> - <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.8.1/mapbox-gl.css' rel='stylesheet' /> <script> // run the application require(["app","connectApp","faultApp", "networkMapApp", "configurationApp", "linkCalculationApp"], function (app, connectApp, faultApp, networkMapApp, configurationApp, linkCalculationApp) { connectApp.register(); - //faultApp.register(); + faultApp.register(); //configurationApp.register(); - //linkCalculationApp.register(); + linkCalculationApp.register(); networkMapApp.register(); app("./app.tsx").runApplication(); }); diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/model/link.ts b/sdnr/wt/odlux/apps/networkMapApp/src/model/link.ts index d992c66db..c1612098d 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/model/link.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/src/model/link.ts @@ -16,6 +16,12 @@ * ============LICENSE_END========================================================================== */ +type Antenna = { + name:string, + waveguideLossIndB: number, + gain: number +} + export type link = {id: string, name: string, length: number, @@ -25,6 +31,6 @@ export type link = {id: string, siteB: string, azimuthA: number | null, azimuthB: number | null, - locationA: { lon: number, lat: number, amsl:number | null, antennaHeight: number | null }, - locationB: { lon: number, lat: number, amsl:number | null, antennaHeight: number | null }, + locationA: { lon: number, lat: number, amsl:number | null, antennaHeight: number | null, antenna: Antenna |null }, + locationB: { lon: number, lat: number, amsl:number | null, antennaHeight: number | null, antenna: Antenna |null }, };
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/model/site.ts b/sdnr/wt/odlux/apps/networkMapApp/src/model/site.ts index b9102e871..13a7361f6 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/model/site.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/src/model/site.ts @@ -28,7 +28,8 @@ export type Site = { operator: string, location:{lon: number, lat: number}, devices: Device[], - links: link[] + links: link[], + furtherInformation:string } export type Address={ diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/model/stadokOrder.ts b/sdnr/wt/odlux/apps/networkMapApp/src/model/stadokOrder.ts new file mode 100644 index 000000000..1aad3aa97 --- /dev/null +++ b/sdnr/wt/odlux/apps/networkMapApp/src/model/stadokOrder.ts @@ -0,0 +1,56 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +export type StadokOrder = { + date: Date, + assignedUser: string, + state: string, //todo: type restrict + tasks: Task[], + + +}; + +export class OrderToDisplay { + + static parse = (stadokOrder: StadokOrder) =>{ + let order = new OrderToDisplay(); + order.assignedUser=stadokOrder.assignedUser; + order.state=stadokOrder.state; + + const firstOpenTask = stadokOrder.tasks.find(task => !task.status); + + if(firstOpenTask){ + order.currentTask=firstOpenTask.description; + }else{ + order.currentTask="No task description available"; + } + + return order; + } + + state: string; + assignedUser: string; + currentTask: string; + +}; + +type Task = { + type: string, + description: string, + status: boolean +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/model/stadokSite.ts b/sdnr/wt/odlux/apps/networkMapApp/src/model/stadokSite.ts new file mode 100644 index 000000000..ed0ca397f --- /dev/null +++ b/sdnr/wt/odlux/apps/networkMapApp/src/model/stadokSite.ts @@ -0,0 +1,54 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { Address } from "./site"; + +type StadokSite = { + + siteId: string; + createdBy: Contact; + updatedOn: Date; + location: { lat: number, lon: number }, + address: Address; + contacts: { manager: Contact, owner: Contact }; + safteyNotices: string[]; + images: string[]; + type: string; + devices: Device[]; + logs: Log[]; + +}; + +type Contact = { + firstName: string; + lastName: string; + email: string; + telephoneNumber: string; +}; +type Log = { + date: Date, //string? + person: string; + entry: string; +}; + +type Device = { + "device": string, + "antenna": string +}; + +export default StadokSite;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/pluginTransport.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/pluginTransport.tsx index e93368524..24a46994a 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/pluginTransport.tsx +++ b/sdnr/wt/odlux/apps/networkMapApp/src/pluginTransport.tsx @@ -27,7 +27,7 @@ import { networkmapRootHandler } from './handlers/rootReducer'; import MainView from "./App"; import { subscribe, IFormatedMessage } from "../../../framework/src/services/notificationService"; import applicationApi from "../../../framework/src/services/applicationApi"; -import { UpdateDetailsView } from "./actions/detailsAction"; +import { checkSitedockReachablity, IsSitedocReachableAction, UpdateDetailsView } from "./actions/detailsAction"; import { findSiteToAlarm } from "./actions/mapActions"; import { URL_BASEPATH } from "./config"; import { Redirect, Route, RouteComponentProps, Switch, withRouter } from "react-router-dom"; @@ -40,7 +40,8 @@ const mapProps = (state: IApplicationStoreState) => ({ }); const mapDisp = (dispatcher: IDispatcher) => ({ - getSettings: () => dispatcher.dispatch(getSettings()) + getSettings: () => dispatcher.dispatch(getSettings()), + tryReachSitedoc: () => dispatcher.dispatch(checkSitedockReachablity()) }); @@ -51,6 +52,8 @@ const NetworkRouterApp = withRouter(connect(mapProps, mapDisp)((props: RouteComp (async function waitFor() { await props.getSettings(); })(); + + props.tryReachSitedoc(); }, []); diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/utils/mapLayers.ts b/sdnr/wt/odlux/apps/networkMapApp/src/utils/mapLayers.ts index 6dfd7983a..7ce4bfa92 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/utils/mapLayers.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/src/utils/mapLayers.ts @@ -141,6 +141,8 @@ class MapLayerService { public addBaseLayers = (map: mapboxgl.Map, themesettings?: ThemeElement) => { const theme = !themesettings ? this.pickTheme() : themesettings; + console.log("user selected theme: " + this.selectedTheme) + console.log("found theme:" + theme); this.addCommonLayers(map); diff --git a/sdnr/wt/odlux/apps/networkMapApp/webpack.config.js b/sdnr/wt/odlux/apps/networkMapApp/webpack.config.js index 3e80514f5..5684040b7 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/networkMapApp/webpack.config.js @@ -67,6 +67,10 @@ module.exports = (env) => { name: './icons/[hash].[ext]' } }] + }, + { + test: /\.css$/i, + use: ["style-loader", "css-loader"], }] }, @@ -168,8 +172,15 @@ module.exports = (env) => { target: "http://localhost:3002", secure: false }, + "/sitedoc/": { + target: "http://localhost:3002", + secure: false, + pathRewrite(pathname) { + return pathname.replace(/^\/sitedoc/, '/topology/stadok') + } + }, "/tiles/": { - target: "http://www.openstreetmap.org", + target: "http://tile.openstreetmap.org", secure: false }, "/help/": { diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/package.json b/sdnr/wt/odlux/apps/performanceHistoryApp/package.json index 305fc07f4..af38fb116 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/package.json +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/package.json @@ -30,8 +30,8 @@ "@types/react": "17.0.3", "@types/react-dom": "17.0.2", "@types/react-router-dom": "5.1.7", - "@material-ui/core": "4.11.0", - "@material-ui/icons": "4.9.1", + "@material-ui/core": "4.11.4", + "@material-ui/icons": "4.11.2", "@types/classnames": "2.2.6", "@types/flux": "3.1.8", "@types/jquery": "3.3.10", diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/pom.xml b/sdnr/wt/odlux/apps/performanceHistoryApp/pom.xml index cb4cfefde..2fe758114 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/pom.xml +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/pom.xml @@ -140,8 +140,8 @@ <!-- optional: default phase is "generate-resources" --> <phase>initialize</phase> <configuration> - <nodeVersion>v10.16.3</nodeVersion> - <yarnVersion>v1.19.0</yarnVersion> + <nodeVersion>v12.13.0</nodeVersion> + <yarnVersion>v1.22.10</yarnVersion> </configuration> </execution> <execution> |