Update NetworkMap 23/112123/1
authorAijana Schumann <aijana.schumann@highstreet-technologies.com>
Thu, 3 Sep 2020 16:16:13 +0000 (18:16 +0200)
committerAijana Schumann <aijana.schumann@highstreet-technologies.com>
Thu, 3 Sep 2020 16:16:13 +0000 (18:16 +0200)
update networkmap

Issue-ID: CCSDK-2713
Signed-off-by: Aijana Schumann <aijana.schumann@highstreet-technologies.com>
Change-Id: I97b0950a4d7f98fdb9044c1e05dfa8dfca34efaf

24 files changed:
sdnr/wt/odlux/apps/networkMapApp/icons/README.md
sdnr/wt/odlux/apps/networkMapApp/icons/datacenterred.png [new file with mode: 0644]
sdnr/wt/odlux/apps/networkMapApp/icons/datacenterred.png.d.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/networkMapApp/icons/factory.png [new file with mode: 0644]
sdnr/wt/odlux/apps/networkMapApp/icons/factory.png.d.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/networkMapApp/icons/factoryred.png [new file with mode: 0644]
sdnr/wt/odlux/apps/networkMapApp/icons/factoryred.png.d.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/networkMapApp/icons/lampred.png [new file with mode: 0644]
sdnr/wt/odlux/apps/networkMapApp/icons/lampred.png.d.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/networkMapApp/src/actions/detailsAction.ts
sdnr/wt/odlux/apps/networkMapApp/src/components/denseTable.tsx
sdnr/wt/odlux/apps/networkMapApp/src/components/details/details.tsx
sdnr/wt/odlux/apps/networkMapApp/src/components/details/linkDetails.tsx
sdnr/wt/odlux/apps/networkMapApp/src/components/details/siteDetails.tsx
sdnr/wt/odlux/apps/networkMapApp/src/components/iconSwitch.tsx [new file with mode: 0644]
sdnr/wt/odlux/apps/networkMapApp/src/components/map.tsx
sdnr/wt/odlux/apps/networkMapApp/src/components/searchBar.tsx
sdnr/wt/odlux/apps/networkMapApp/src/config.ts
sdnr/wt/odlux/apps/networkMapApp/src/handlers/mapReducer.ts
sdnr/wt/odlux/apps/networkMapApp/src/index.html
sdnr/wt/odlux/apps/networkMapApp/src/model/link.ts
sdnr/wt/odlux/apps/networkMapApp/src/pluginTransport.tsx
sdnr/wt/odlux/apps/networkMapApp/src/utils/mapLayers.ts
sdnr/wt/odlux/framework/pom.xml

index b26fbc2..acfbcf8 100644 (file)
@@ -19,7 +19,7 @@ Copyright of icons is as followes:
  */
  -->
  
-datacenter.png and lamp.png 
+datacenter.png, lamp.png, factory.png, datacenterred.png, lampred.png, factoryred.png,   
 
 Taken from MS Word
 
diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/datacenterred.png b/sdnr/wt/odlux/apps/networkMapApp/icons/datacenterred.png
new file mode 100644 (file)
index 0000000..5d5a6c5
Binary files /dev/null and b/sdnr/wt/odlux/apps/networkMapApp/icons/datacenterred.png differ
diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/datacenterred.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/datacenterred.png.d.ts
new file mode 100644 (file)
index 0000000..33f3061
--- /dev/null
@@ -0,0 +1,2 @@
+declare const datacenterred: string;
+export default datacenterred;
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/factory.png b/sdnr/wt/odlux/apps/networkMapApp/icons/factory.png
new file mode 100644 (file)
index 0000000..a38781b
Binary files /dev/null and b/sdnr/wt/odlux/apps/networkMapApp/icons/factory.png differ
diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/factory.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/factory.png.d.ts
new file mode 100644 (file)
index 0000000..b5c4f19
--- /dev/null
@@ -0,0 +1,2 @@
+declare const factory: string;
+export default factory;
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/factoryred.png b/sdnr/wt/odlux/apps/networkMapApp/icons/factoryred.png
new file mode 100644 (file)
index 0000000..959603a
Binary files /dev/null and b/sdnr/wt/odlux/apps/networkMapApp/icons/factoryred.png differ
diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/factoryred.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/factoryred.png.d.ts
new file mode 100644 (file)
index 0000000..1fac0a9
--- /dev/null
@@ -0,0 +1,2 @@
+declare const factoryRed: string;
+export default factoryRed;
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/lampred.png b/sdnr/wt/odlux/apps/networkMapApp/icons/lampred.png
new file mode 100644 (file)
index 0000000..4678ce9
Binary files /dev/null and b/sdnr/wt/odlux/apps/networkMapApp/icons/lampred.png differ
diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/lampred.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/lampred.png.d.ts
new file mode 100644 (file)
index 0000000..12a8f91
--- /dev/null
@@ -0,0 +1,2 @@
+declare const lampred: string;
+export default lampred;
\ No newline at end of file
index 8a005bc..5288f61 100644 (file)
@@ -117,8 +117,6 @@ running=true;
     
 
     result.forEach((res: any, index)=>{
-      console.log("value")
-     console.log(res);
      if(res !==null && res.node!==null){
 
       list[index].status = res.node[0]["netconf-node-topology:connection-status"];
@@ -137,28 +135,4 @@ running=true;
   dispatcher(new IsBusyCheckingDeviceListAction(false));
 
   });
-
-  /*  result.forEach((res: Promise<any>, index)=>{
-      console.log("value")
-     console.log(res);
-     console.log(res.value);
-     if(res.value!==null){
-      list[index].status = res.value.node[0]["netconf-node-topology:connection-status"];
-     }else{
-      list[index].status = "Not connected";
-     }*/
-    
-  
-
-
-
-
-  //get devices
-  //wait on all to finish
-  //update array
-
-
-
-
-
 }
\ No newline at end of file
index 9846a22..1506df5 100644 (file)
@@ -26,7 +26,7 @@ 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, 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, ariaLabel: string, onClick?(id: string): void, actions?:boolean  };
 
 
 const styles = makeStyles({
@@ -81,13 +81,13 @@ const DenseTable: React.FunctionComponent<props> = (props) => {
                        
 
                         return (
-                            <TableRow key={index} hover={props.hover} onMouseOver={e => handleHover(e,row.name)} onClick={ e =>  handleClick(e, row.name)}>
+                            <TableRow aria-label={props.ariaLabel} key={index} hover={props.hover} onMouseOver={e => handleHover(e,row.name)} onClick={ e =>  handleClick(e, row.name)}>
 
                                 {
                                     values.map((data:any) => {
                                        
                                         if(data!== undefined)
-                                        return <TableCell >  {data} </TableCell>
+                                        return <TableCell>  {data} </TableCell>
                                         else
                                         return null;
                                     })
@@ -95,12 +95,15 @@ const DenseTable: React.FunctionComponent<props> = (props) => {
                                 {
 
                                     props.actions && <TableCell >  
-<div style={{display:"flex"}}>                                           
+<div style={{display:"flex"}}>
+    <Tooltip title="Connect">
+    <Button className={classes.button} disabled={true} onClick={(e: any) =>{ e.preventDefault(); e.stopPropagation(); props.navigate && props.navigate("connect", row.simulatorId ? row.simulatorId : row.name)}}>C</Button>
+    </Tooltip>                                           
     <Tooltip title="Configure">
-    <Button className={classes.button} disabled={row.status!=="connected"} onClick={(e: any) =>{ e.preventDefault(); e.stopPropagation(); props.navigate && props.navigate("configuration", row.simulatorId ? row.simulatorId : row.name)}}>C</Button>
+    <Button className={classes.button} disabled={true} onClick={(e: any) =>{ e.preventDefault(); e.stopPropagation(); props.navigate && props.navigate("configuration", row.simulatorId ? row.simulatorId : row.name)}}>C</Button>
     </Tooltip>
     <Tooltip title="Fault">
-    <Button className={classes.button} onClick={(e: any) =>{ e.preventDefault(); e.stopPropagation(); props.navigate && props.navigate("fault", row.simulatorId ? row.simulatorId : row.name)}}>F</Button>
+    <Button className={classes.button} disabled={true} onClick={(e: any) =>{ e.preventDefault(); e.stopPropagation(); props.navigate && props.navigate("fault", row.simulatorId ? row.simulatorId : row.name)}}>F</Button>
     </Tooltip>
     </div> 
     </TableCell>
index a2e51d3..081276b 100644 (file)
@@ -50,10 +50,9 @@ const Details: React.FunctionComponent<porps> = (props) => {
 
     }, []);
 
-    // if url changed
+    // if url changed, load details data
     React.useEffect(() => {
         const detailsId = getDetailsIdFromUrl();
-        console.log(detailsId)
         if (detailsId !== null && props.data?.name !== detailsId) {
             loadDetailsData(detailsId)
         }
@@ -154,16 +153,17 @@ const Details: React.FunctionComponent<porps> = (props) => {
                 })
     }
 
+    const panelId = props.data!== null ? (isSite(props.data) ? 'site-details-panel' : 'link-details-panel' ): 'details-panel';
 
     return (<div style={{ width: '30%', background: "#bbbdbf", padding: "20px", alignSelf:"stretch" }}>
-        <Paper style={{ height:"100%"}} id="site-details-panel"  >
+        <Paper style={{ height:"100%"}} id={panelId} aria-label={panelId}  >
             {
                 props.breadcrumbs.length > 0 &&
-                <Breadcrumbs style={{ marginLeft: "15px", marginTop: "5px" }} aria-label="breadcrumb">
-                    <Link color="inherit" href="/" onClick={backClick}>
+                <Breadcrumbs style={{ marginLeft: "15px", marginTop: "5px" }} aria-label="breadcrumbs-navigation">
+                    <Link aria-label="parent-element" color="inherit" href="/" onClick={backClick}>
                         {props.breadcrumbs[0].id}
                     </Link>
-                    <Link>
+                    <Link aria-label="child-element" color="textSecondary">
                         {props.data?.name}
                     </Link>
                 </Breadcrumbs>
@@ -171,7 +171,7 @@ const Details: React.FunctionComponent<porps> = (props) => {
             {
                 props.data !== null ?
                     createDetailPanel(props.data)
-                    : <Typography style={{ marginTop: "5px" }} align="center" variant="body1">{message}</Typography>
+                    : <Typography aria-label="details-panel-alt-message" style={{ marginTop: "5px" }} align="center" variant="body1">{message}</Typography>
 
             }
         </Paper>
index de1bf6b..0c9f603 100644 (file)
@@ -32,8 +32,7 @@ const LinkDetails: React.FunctionComponent<props> = (props) => {
     const [height, setHeight] = React.useState(330);
 
     const handleResize = () =>{
-        console.log("resize")
-        const el = document.getElementById('site-details-panel')?.getBoundingClientRect();
+        const el = document.getElementById('link-details-panel')?.getBoundingClientRect();
         const el2 = document.getElementById('site-tabs')?.getBoundingClientRect();
 
         if(el && el2){
@@ -69,7 +68,7 @@ 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;
-       window.open(`/#/linkCalculation?lat1=${siteA.lat}&lon1=${siteA.lon}&lat2=${siteB.lat}&lon2=${siteB.lon}&siteA=${nameA}&siteB=${nameB}&azimuthA=${azimuthA}&azimuthB=${azimuthB}&distance=${distance}`)
+       window.open(`/#/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}`)
 
     }
 
@@ -83,15 +82,15 @@ const LinkDetails: React.FunctionComponent<props> = (props) => {
 
     return (<div style={{ paddingLeft: "15px", paddingRight: "15px", paddingTop: "0px", display: 'flex', flexDirection: 'column' }}>
         <h2>{props.link.name}</h2>
-        <TextField disabled style={{ marginTop: "5px" }} value="Unkown" label="Operator" />
-        <TextField disabled style={{ marginTop: "5px" }} value={props.link.type} label="Type" />
-        <TextField disabled style={{ marginTop: "5px" }} value={props.link.length.toFixed(2)} label="Distance planned in km" />
-        <TextField disabled style={{ marginTop: "5px" }} value={props.link.calculatedLength.toFixed(2)} label="Distance calculated in km" />
+        <TextField aria-label="operator" disabled style={{ marginTop: "5px" }} value="Unkown" label="Operator" />
+        <TextField aria-label="type" disabled style={{ marginTop: "5px" }} value={props.link.type} label="Type" />
+        <TextField aria-label="planned-distance-in-km" disabled style={{ marginTop: "5px" }} value={props.link.length.toFixed(2)} label="Distance planned in km" />
+        <TextField aria-label="calculated-distance-in-km" disabled style={{ marginTop: "5px" }} value={props.link.calculatedLength.toFixed(2)} label="Distance calculated in km" />
 
         <AppBar position="static" id="site-tabs" style={{ marginTop: "20px", background: '#2E3B55' }}>
-            <Typography style={{ margin:"5px"}}>SITE DETAILS</Typography>
+            <Typography aria-label="details-of-link-sites" style={{ margin:"5px"}}>SITE DETAILS</Typography>
         </AppBar>
-        <DenseTable height={height} hover={false} headers={["", "Site A", "Site B"]} data={data} />
+        <DenseTable ariaLabel="site-information-table-entry" 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>
         }
index a95666e..92643d0 100644 (file)
@@ -81,28 +81,28 @@ const SiteDetails: React.FunctionComponent<props> = (props) => {
         <h2 >{props.site.name}</h2>
         {
             props.site.operator !== '' && props.site.operator !== null ?
-                <TextField disabled={true} value={props.site.operator} label="Operator" /> :
-                <TextField disabled={true} value="Unkown" label="Operator" style={{ marginTop: "5px" }} />
+                <TextField aria-label="operator" disabled={true} value={props.site.operator} label="Operator" /> :
+                <TextField aria-label="operator" disabled={true} value="Unkown" label="Operator" style={{ marginTop: "5px" }} />
         }
         {
             props.site.type !== undefined && props.site.type.length > 0 &&
-            <TextField disabled={true} value={props.site.type} label="Type" style={{ marginTop: "5px" }} />
+            <TextField aria-label="type" disabled={true} value={props.site.type} label="Type" style={{ marginTop: "5px" }} />
         }
         {
             props.site.address !== undefined && props.site.address.length > 0 &&
-            <TextField disabled={true} value={props.site.address} label="Adress" style={{ marginTop: "5px" }} />
+            <TextField aria-label="adress" disabled={true} value={props.site.address} label="Adress" style={{ marginTop: "5px" }} />
         }
         {
             props.site.heighAGLInMeters !== undefined && props.site.heighAGLInMeters > 0 &&
-            <TextField disabled={true} value={props.site.heighAGLInMeters} label="AMSL in meters" style={{ marginTop: "5px" }} />
+            <TextField aria-label="amsl-in-meters" disabled={true} value={props.site.heighAGLInMeters} label="AMSL in meters" style={{ marginTop: "5px" }} />
         }
         {
             props.site.antennaHeightAGLInMeters !== undefined && props.site.antennaHeightAGLInMeters > 0 &&
-            <TextField disabled={true} value={props.site.antennaHeightAGLInMeters} label="Atenna above ground in meters" style={{ marginTop: "5px" }} />
+            <TextField aria-label="antenna-above-ground-in-meters" disabled={true} value={props.site.antennaHeightAGLInMeters} label="Atenna above ground in meters" style={{ marginTop: "5px" }} />
         }
          
-        <TextField style={{ marginTop: "5px" }} disabled={true} value={LatLonToDMS(props.site.geoLocation.lat)} label="Latitude" />
-        <TextField style={{ marginTop: "5px" }} disabled={true} value={LatLonToDMS(props.site.geoLocation.lon, true)} label="Longitude" />
+        <TextField aria-label="latitude" style={{ marginTop: "5px" }} disabled={true} value={LatLonToDMS(props.site.geoLocation.lat)} label="Latitude" />
+        <TextField aria-label="longitude" style={{ marginTop: "5px" }} disabled={true} value={LatLonToDMS(props.site.geoLocation.lon, true)} label="Longitude" />
 
         <AppBar position="static" style={{ marginTop: "5px", background: '#2E3B55' }}>
             <Tabs id="site-tabs" value={value} onChange={onHandleTabChange} aria-label="simple tabs example">
@@ -115,12 +115,12 @@ const SiteDetails: React.FunctionComponent<props> = (props) => {
             <>
                 {
                     props.site.links.length === 0 &&
-                    <Typography variant="body1" style={{ marginTop: '10px' }}>No links available.</Typography>
+                    <Typography aria-label="no-links-available" variant="body1" style={{ marginTop: '10px' }}>No links available.</Typography>
                 }
                
                 {
                     props.site.links.length > 0 &&
-                    <DenseTable height={height} hover={true} headers={["Link Name", "Azimuth in Â°"]}  data={linkRows} onClick={props.onLinkClick}  ></DenseTable>
+                    <DenseTable ariaLabel="available-links-table-entry" height={height} hover={true} headers={["Link Name", "Azimuth in Â°"]}  data={linkRows} onClick={props.onLinkClick}  ></DenseTable>
                /**
                 * 
                 * */
@@ -136,12 +136,12 @@ const SiteDetails: React.FunctionComponent<props> = (props) => {
             <>
                 {
                     props.site.devices.length === 0 &&
-                    <Typography variant="body1" style={{ marginTop: '10px' }}>No nodes available.</Typography>
+                    <Typography aria-label="no-nodes-avilable" variant="body1" style={{ marginTop: '10px' }}>No nodes available.</Typography>
                 }
 
                 {
                     props.site.devices.length>0 && props.updatedDevices !== null &&
-                    <DenseTable navigate={props.navigate} height={height} hover={false} headers={["ID","Name","Type", "Manufacturer","Owner","Status", "Ports", "Actions"]} actions={true} data={props.updatedDevices!} />
+                    <DenseTable ariaLabel="available-nodes-table-entry" navigate={props.navigate} height={height} hover={false} headers={["ID","Name","Type", "Manufacturer","Owner","Status", "Ports", "Actions"]} actions={true} data={props.updatedDevices!} />
                 }
             </>
         }
diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/iconSwitch.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/iconSwitch.tsx
new file mode 100644 (file)
index 0000000..8df1385
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * ============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==========================================================================
+ */
+
+import * as React from 'react';
+import { FormControlLabel, Switch, Paper } from "@material-ui/core";
+import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect';
+import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
+import { SetIconSwitchAction } from '../actions/mapActions';
+
+type props = Connect<typeof mapStateToProps, typeof mapDispatchToProps> & {visible: boolean}
+
+const IconSwitch: React.FunctionComponent<props> = (props) =>{
+
+    const toggleChecked = () => {
+      props.toogle(!props.areIconsEnabled)
+    };
+
+    return (
+      props.visible ?
+             <FormControlLabel style={{ padding:5, position: 'absolute',top: 190}}
+        value="end"
+        control={<Switch color="secondary" checked={props.areIconsEnabled} onChange={toggleChecked} />}
+        label="Show icons"
+        labelPlacement="end"
+      />: null)
+}
+
+const mapStateToProps = (state: IApplicationStoreState) => ({
+  areIconsEnabled: state.network.map.allowIconSwitch
+});
+
+
+const mapDispatchToProps = (dispatcher: IDispatcher) => ({ 
+  toogle : (enable:boolean) => dispatcher.dispatch(new SetIconSwitchAction(enable))
+
+});;
+
+export default (connect(mapStateToProps,mapDispatchToProps)(IconSwitch))
index 363178e..ceb51d4 100644 (file)
@@ -30,24 +30,18 @@ import { SetPopupPositionAction, SelectMultipleLinksAction, SelectMultipleSitesA
 import { Feature } from '../model/Feature';
 import { HighlightLinkAction, HighlightSiteAction, SetCoordinatesAction, SetStatistics } from '../actions/mapActions';
 import { addDistance, getUniqueFeatures } from '../utils/mapUtils';
-import { location } from '../handlers/mapReducer'
-import { Typography, Paper, Tooltip } from '@material-ui/core';
-import { elementCount } from '../model/count';
-import lamp from '../../icons/lamp.png';
-import apartment from '../../icons/apartment.png';
-import datacenter from '../../icons/datacenter.png';
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
 import connect, { IDispatcher, Connect } from '../../../../framework/src/flux/connect';
 import SearchBar from './searchBar';
 import { verifyResponse, IsTileServerReachableAction, handleConnectionError } from '../actions/connectivityAction';
 import ConnectionInfo from './connectionInfo'
-import { ApplicationStore } from '../../../../framework/src/store/applicationStore';
-import { showIconLayers, addBaseLayers, swapLayersBack } from '../utils/mapLayers';
-import Statistics from './statistics'
-
-
-
-
+import { showIconLayers, addBaseLayers } from '../utils/mapLayers';
+import lamp from '../../icons/lamp.png';
+import apartment from '../../icons/apartment.png';
+import datacenter from '../../icons/datacenter.png';
+import factory from '../../icons/factory.png';
+import Statistics from './statistics';
+import IconSwitch from './iconSwitch';
 
 type coordinates = { lat: number, lon: number, zoom: number }
 
@@ -71,8 +65,11 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
 
     componentDidMount() {
 
+        // resize the map, if menu gets collapsed
         window.addEventListener("menu-resized", this.handleResize);
 
+        // try if connection to tile + topologyserver is available
+
         fetch(URL_TILE_API + '/10/0/0.png')
             .then(res => {
                 if (res.ok) {
@@ -138,6 +135,13 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
                     map.addImage('house', image);
                 });
 
+                map.loadImage(
+                    factory,
+                    function (error: any, image: any) {
+                        if (error) throw error;
+                        map.addImage('factory', image);
+                    });               
+
             const boundingBox = map.getBounds();
 
 
@@ -145,7 +149,7 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
                 .then(result => verifyResponse(result))
                 .then(result => result.json())
                 .then(features => {
-                    if (map.getLayer('lines')) {
+                    if (map.getLayer('fibre-lines')) {
                         (map.getSource('lines') as mapboxgl.GeoJSONSource).setData(features);
                     }
                 })
@@ -160,8 +164,7 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
                         (map.getSource('points') as mapboxgl.GeoJSONSource).setData(features);
                     }
                 })
-                .catch(error => this.props.handleConnectionError(error));;
-
+                .catch(error => this.props.handleConnectionError(error));
         });
 
         map.on('click', (e: any) => {
@@ -170,7 +173,7 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
 
                 var clickedLines = getUniqueFeatures(map.queryRenderedFeatures([[e.point.x - 5, e.point.y - 5],
                 [e.point.x + 5, e.point.y + 5]], {
-                    layers: ['lines']
+                    layers: ['microwave-lines', 'fibre-lines']
                 }), "id");
 
                 const clickedPoints = getUniqueFeatures(map.queryRenderedFeatures(e.point, { layers: ['points'] }), "id");
@@ -182,14 +185,12 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
                     if (alarmedSites.length > 0) {
                         alarmedSites.forEach(alarm => {
                             const index = clickedPoints.findIndex(item => item.properties!.id === alarm.properties!.id);
-                            console.log(index);
 
                             if (index !== -1) {
                                 clickedPoints[index].properties!.alarmed = true;
                                 clickedPoints[index].properties!.type = "alarmed";
                             }
                         });
-                        console.log(clickedPoints);
                     }
 
                     this.showSitePopup(clickedPoints, e.point.x, e.point.y);
@@ -203,12 +204,13 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
                 const clickedLamps = getUniqueFeatures(map.queryRenderedFeatures(e.point, { layers: ['point-lamps'] }), "id");
                 const buildings = getUniqueFeatures(map.queryRenderedFeatures(e.point, { layers: ['point-building'] }), "id");
                 const houses = getUniqueFeatures(map.queryRenderedFeatures(e.point, { layers: ['point-data-center'] }), "id");
+                const factories = getUniqueFeatures(map.queryRenderedFeatures(e.point, { layers: ['point-factory'] }), "id");
 
-                const combinedFeatures = [...clickedLamps, ...buildings, ...houses];
+                const combinedFeatures = [...clickedLamps, ...buildings, ...houses, ...factories];
 
                 const clickedLines = getUniqueFeatures(map.queryRenderedFeatures([[e.point.x - 5, e.point.y - 5],
                 [e.point.x + 5, e.point.y + 5]], {
-                    layers: ['lines']
+                    layers: ['microwave-lines', 'fibre-lines']
                 }), "id");
 
                 if (combinedFeatures.length > 0)
@@ -231,6 +233,8 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
                 this.props.updateMapPosition(lat, lon, mapZoom)
             }
 
+            // update the url to current lat,lon,zoom values
+
             const currentUrl = window.location.href;
             const parts = currentUrl.split(URL_BASEPATH);
             const detailsPath = parts[1].split("/details/");
@@ -241,16 +245,17 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
             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
+            showIconLayers(map, this.props.showIcons, this.props.selectedSite?.properties.id);
 
+            //update statistics
             const boundingBox = map.getBounds();
 
-            showIconLayers(map, this.props.showIcons, this.props.selectedSite?.properties.id);
-
             fetch(`${URL_API}/info/count/${boundingBox.getWest()},${boundingBox.getSouth()},${boundingBox.getEast()},${boundingBox.getNorth()}`)
                 .then(result => verifyResponse(result))
                 .then(res => res.json())
                 .then(result => {
-                    console.log(result);
                     if (result.links !== this.props.linkCount || result.sites !== this.props.siteCount) {
                         this.props.setStatistics(result.links, result.sites);
                     }
@@ -277,9 +282,12 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
                 map.setLayoutProperty('selectedPoints', 'visibility', 'none');
 
                 if (mapZoom <= 4) {
-                    map.setPaintProperty('lines', 'line-width', 1);
+                    map.setPaintProperty('fibre-lines', 'line-width', 1);
+                    map.setPaintProperty('microwave-lines', 'line-width', 1);
+
                 } else {
-                    map.setPaintProperty('lines', 'line-width', 2);
+                    map.setPaintProperty('fibre-lines', 'line-width', 2);
+                    map.setPaintProperty('microwave-lines', 'line-width', 2);
                 }
             }
         });
@@ -301,7 +309,8 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
 
                         map.setFilter('point-lamps', ['==', 'type', 'street lamp']);
                         map.setFilter('point-data-center', ['==', 'type', 'data center']);
-                        map.setFilter('point-building', ['==', 'type', 'high rise building'])
+                        map.setFilter('point-building', ['==', 'type', 'high rise building']);
+                        map.setFilter('point-factory', ['==', 'type', 'factory'])
 
                         if (this.props.selectedSite?.properties.type !== undefined) {
                             switch (this.props.selectedSite?.properties.type) {
@@ -313,7 +322,9 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
                                     break;
                                 case 'high rise building':
                                     map.setFilter('point-building', ["all", ['==', 'type', 'high rise building'], ['!=', 'id', this.props.selectedSite.properties.id]])
-
+                                    break;
+                                case 'factory':
+                                    map.setFilter('point-factory', ["all", ['==', 'type', 'factory'], ['!=', 'id', this.props.selectedSite.properties.id]]);
                                     break;
                             }
                         }
@@ -336,6 +347,7 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
                         map.setFilter('point-lamps', ['==', 'type', 'street lamp']);
                         map.setFilter('point-data-center', ['==', 'type', 'data center']);
                         map.setFilter('point-building', ['==', 'type', 'high rise building']);
+                        map.setFilter('point-factory', ['==', 'type', 'factory']);
                     }
 
                     if (map.getSource("selectedLine") !== undefined) {
@@ -367,7 +379,6 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
 
             if (prevProps.showIcons !== this.props.showIcons) {
                 if (map && map.getZoom() > 11) {
-                    console.log(this.props.showIcons);
                     showIconLayers(map, this.props.showIcons, this.props.selectedSite?.properties.id);
                 }
             }
@@ -388,6 +399,10 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
         }
     }
 
+    componentWillUnmount(){
+        window.removeEventListener("menu-resized", this.handleResize);
+    }
+
     handleResize = () => {
         if (map) {
             // wait a moment until resizing actually happened
@@ -432,10 +447,6 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
         }
     }
 
-
-    //TODO: how to handle if too much data gets loaded? (1 mio points...?)
-    // data might have gotten collected, reload if necessary!
-    //always save count, if count and current view count differ -> reload last boundingbox
     loadNetworkData = async (bbox: mapboxgl.LngLatBounds) => {
         if (!isLoadingInProgress) { // only load data if loading not in progress
             isLoadingInProgress = true;
@@ -458,7 +469,6 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
 
                     await this.draw('lines', `${URL_API}/links/geoJson/${increasedBoundingBox.west},${increasedBoundingBox.south},${increasedBoundingBox.east},${increasedBoundingBox.north}`);
                     await this.draw('points', `${URL_API}/sites/geoJson/${increasedBoundingBox.west},${increasedBoundingBox.south},${increasedBoundingBox.east},${increasedBoundingBox.north}`);
-                    console.log("bbox is bigger");
 
                 } else if (lastBoundingBox.contains(bbox.getNorthEast()) && lastBoundingBox.contains(bbox.getSouthWest())) { // last one contains new one
                     // bbox is contained in last one, do nothing
@@ -506,17 +516,13 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
                 })
                 .catch(error => this.props.handleConnectionError(error));;
         }
-
     }
 
-
-
     showLinkPopup = (links: mapboxgl.MapboxGeoJSONFeature[], top: number, left: number) => {
 
         if (links.length > 1) {
 
             const ids = links.map(feature => feature.properties!.id as string);
-
             this.props.setPopupPosition(top, left);
             this.props.selectMultipleLinks(ids);
             this.setState({ isPopupOpen: true });
@@ -562,6 +568,7 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
                 }
                 <SearchBar />
                 <Statistics />
+                <IconSwitch visible={this.props.zoom>11} />
                 <ConnectionInfo />
             </div>
         </>
index 0c7607b..c825e5a 100644 (file)
@@ -79,11 +79,13 @@ const SearchBar: React.FunctionComponent<searchBarProps> = (props) =>{
         const linkResult = fetch(`${URL_API}/link/${props.searchterm}`);
   
            Promise.all([ siteResult, linkResult]).then((result)=>{
-              const suceededResults = result.filter(el=> el!==undefined);
+              const suceededResults = result.filter(el=> el.ok);
   
              if(suceededResults.length==0){
               setAnchorEl(divRef.current);
               setErrorMessage("No element found.")
+              // hide message after 3 sec
+              window.setTimeout(()=>{setAnchorEl(null)}, 3000);
 
              }else{
               suceededResults[0].json().then(result =>{
@@ -115,7 +117,7 @@ const SearchBar: React.FunctionComponent<searchBarProps> = (props) =>{
           disabled={!reachabe}
             className={classes.input}
             placeholder="Find sites or links by name"
-            inputProps={{ 'aria-label': 'search sites or links' }}
+            inputProps={{ 'aria-label': 'networkmap-searchbar' }}
             value={props.searchterm}
             onChange={e=> props.setSearchTerm(e.currentTarget.value)}
           />
index bdfcd24..e2e5718 100644 (file)
@@ -27,11 +27,10 @@ export const OSM_STYLE = {
             'type': 'raster',
             'tiles': [
                URL_TILE_API+'/{z}/{x}/{y}.png'
-
             ],
             'tileSize': 256,
             'attribution':
-                'Data by <a target="_top" rel="noopener" href="http://openstreetmap.org">OpenStreetMap</a>, under <a target="_top" rel="noopener" href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>'
+                '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
         }
     },
     'layers': [
@@ -40,7 +39,7 @@ export const OSM_STYLE = {
             'type': 'raster',
             'source': 'raster-tiles',
             'minzoom': 0,
-            'maxzoom': 22
+            'maxzoom': 18
         }
     ]
 };
index a140e9b..6f62773 100644 (file)
@@ -50,7 +50,7 @@ export const MapReducer: IActionHandler<mapState> = (state=initialState, action:
     
     if(action instanceof HighlightLinkAction){
       
-        state = Object.assign({}, state, {selectedSite: null, selectedLink:{type: "Feature", geometry:{type:"LineString", coordinates:[[action.link.locationA.lon,action.link.locationA.lat ],[action.link.locationB.lon,action.link.locationB.lat ]]}}})
+        state = Object.assign({}, state, {selectedSite: null, selectedLink:{type: "Feature", properties:{id:action.link.id, type: action.link.type}, geometry:{type:"LineString", coordinates:[[action.link.locationA.lon,action.link.locationA.lat ],[action.link.locationB.lon,action.link.locationB.lat ]]}}})
 
 
     }
index f68e8c1..9a7f77a 100644 (file)
 
   <script>
     // run the application
-    require(["app","connectApp","faultApp", "networkMapApp", "configurationApp"], function (app, connectApp, faultApp, networkMapApp, configurationApp) {
+    require(["app","connectApp","faultApp", "networkMapApp", "configurationApp", "linkCalculationApp"], function (app, connectApp, faultApp, networkMapApp, configurationApp, linkCalculationApp) {
       connectApp.register();
       //faultApp.register();
       configurationApp.register();
+      linkCalculationApp.register();
       networkMapApp.register();
       app("./app.tsx").runApplication();
     });
index a6ef65c..e11be1a 100644 (file)
@@ -25,6 +25,6 @@ export type link = {id: string,
      siteB: string,
     azimuthA: number,
     azimuthB: number,
-    locationA:{lon: number, lat: number},
-    locationB:{lon: number, lat: number}
+    locationA: { lon: number, lat: number, amsl?:number, antennaHeight?: number },
+    locationB: { lon: number, lat: number, amsl?:number, antennaHeight?: number },
  };
\ No newline at end of file
index 0ff9418..67c75ce 100644 (file)
@@ -1,24 +1,3 @@
-/**
-* ============LICENSE_START========================================================================
-* ONAP : ccsdk feature sdnr wt odlux
-* =================================================================================================
-* Copyright (C) 2019 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==========================================================================
-*/
-// app configuration and main entry point for the app
-
-import * as React from "react";
-import { faMapMarked } from '@fortawesome/free-solid-svg-icons'; // select app icon
 /**
  * ============LICENSE_START========================================================================
  * ONAP : ccsdk feature sdnr wt odlux
@@ -37,6 +16,10 @@ import { faMapMarked } from '@fortawesome/free-solid-svg-icons'; // select app i
  * ============LICENSE_END==========================================================================
  */
 
+// app configuration and main entry point for the app
+
+import * as React from "react";
+import { faMapMarked } from '@fortawesome/free-solid-svg-icons'; // select app icon
 import applicationManager from '../../../framework/src/services/applicationManager';
 
 
@@ -80,7 +63,7 @@ type FaultAlarmNotification = {
   type: string;
   sourceType: string;
 }
-
+/*
 // subscribe to the websocket notifications from connect
 subscribe<ObjectNotification & IFormatedMessage>(["ObjectCreationNotification", "ObjectDeletionNotification", "AttributeValueChangedNotification"], (msg => {
   const store = applicationApi.applicationStore;
@@ -99,3 +82,4 @@ subscribe<FaultAlarmNotification & IFormatedMessage>("ProblemNotification", (fau
   }
 }));
 
+*/
\ No newline at end of file
index e11d964..f2cabf5 100644 (file)
@@ -19,6 +19,9 @@
 import * as mapboxgl from 'mapbox-gl';
 import { Feature } from 'model/Feature';
 
+const fibreLinkColor = "#1154d9";
+const microwaveLinkColor="#039903";
+
 
 export const addBaseLayers = (map: mapboxgl.Map, selectedPoint: Feature|null, selectedLine: Feature|null) => {
        
@@ -50,7 +53,7 @@ export const addBaseLayers = (map: mapboxgl.Map, selectedPoint: Feature|null, se
     });
 
     map.addLayer({
-        'id': 'lines',
+        'id': 'microwave-lines',
         'type': 'line',
         'source': 'lines',
         'layout': {
@@ -58,13 +61,29 @@ export const addBaseLayers = (map: mapboxgl.Map, selectedPoint: Feature|null, se
             'line-cap': 'round'
         },
         'paint': {
-            'line-color': '#888',
+            'line-color': microwaveLinkColor,
             'line-width': 2
-        }
+        },
+        'filter': ['==', 'type', 'microwave']
     });
 
     map.addLayer({
-        'id': 'selectedLine',
+        'id': 'fibre-lines',
+        'type': 'line',
+        'source': 'lines',
+        'layout': {
+            'line-join': 'round',
+            'line-cap': 'round'
+        },
+        'paint': {
+            'line-color': fibreLinkColor,
+            'line-width': 2
+        },
+        'filter': ['==', 'type', 'fibre']
+    });
+
+    map.addLayer({
+        'id': 'selectedLineMicrowave',
         'type': 'line',
         'source': 'selectedLine',
         'layout': {
@@ -72,9 +91,25 @@ export const addBaseLayers = (map: mapboxgl.Map, selectedPoint: Feature|null, se
             'line-cap': 'round'
         },
         'paint': {
-            'line-color': '#888',
+            'line-color': microwaveLinkColor,
             'line-width': 4
-        }
+        },
+        'filter': ['==', 'type', 'microwave']
+    });
+
+    map.addLayer({
+        'id': 'selectedLineFibre',
+        'type': 'line',
+        'source': 'selectedLine',
+        'layout': {
+            'line-join': 'round',
+            'line-cap': 'round'
+        },
+        'paint': {
+            'line-color': fibreLinkColor,
+            'line-width': 4
+        },
+        'filter': ['==', 'type', 'fibre']
     });
 
 
@@ -136,7 +171,7 @@ export const removeBaseLayers = (map: mapboxgl.Map) => {
 
 let checkedLayers = false;
 
-const createFilter = (type:'street lamp'|'high rise building'|'data center', selectedSiteId?:string) =>{
+const createFilter = (type:'street lamp'|'high rise building'|'data center'|'factory', selectedSiteId?:string) =>{
 
     return selectedSiteId === undefined ? ['==', 'type', type] : ["all", ['==', 'type', type], ['!=', 'id', selectedSiteId]]
 }
@@ -159,11 +194,11 @@ export const showIconLayers = (map: mapboxgl.Map, show: boolean, selectedSiteId?
             });
             checkedLayers=true;
 
-            if(elements.length>0 && elements.length<1000){
+        if(elements.length>0 && elements.length<1000){
 
         if (map.getLayer('point-lamps') === undefined) {
             map.removeLayer('points');
-
+            map.setLayoutProperty('alarmedPoints', 'visibility', 'none');
             map.setLayoutProperty('selectedPoints', 'visibility', 'none');
 
             map.addLayer({
@@ -202,6 +237,70 @@ export const showIconLayers = (map: mapboxgl.Map, show: boolean, selectedSiteId?
                     'icon-size': 0.1
                 } });
 
+                map.addLayer({
+                    'id': 'point-factory',
+                    'type': 'symbol',
+                    'source': 'points',
+                    'filter': createFilter("factory", selectedSiteId),
+                    'layout': {
+                        'icon-allow-overlap': true,
+                        'icon-image': 'factory',
+                        'icon-size': 0.2
+                    }
+                });
+
+                //alarm layers
+
+                map.addLayer({
+                    'id': 'point-lamps-alarm',
+                    'type': 'symbol',
+                    'source': 'alarmedPoints',
+                    'layout': {
+                        'icon-allow-overlap': true,
+                        'icon-image': 'lamp-red',
+                        'icon-size': 0.1
+    
+                    },
+                    'filter': createFilter("street lamp"),
+                });
+    
+                map.addLayer({
+                    'id': 'point-building-alarm',
+                    'type': 'symbol',
+                    'source': 'alarmedPoints',
+                    'filter': createFilter("high rise building"),
+                    'layout': {
+                        'icon-allow-overlap': true,
+                        'icon-image': 'house-red',
+                        'icon-size': 0.1
+                    }
+                });
+    
+                map.addLayer({
+                    'id': 'point-data-center-alarm',
+                    'type': 'symbol',
+                    'source': 'alarmedPoints',
+                    'filter': createFilter("data center"),
+                    'layout': {
+                        'icon-allow-overlap': true,
+                        'icon-image': 'data-center_red',
+                        'icon-size': 0.1
+                    } });
+
+                    map.addLayer({
+                        'id': 'point-factory-alarm',
+                        'type': 'symbol',
+                        'source': 'alarmedPoints',
+                        'filter': createFilter("factory"),
+                        'layout': {
+                            'icon-allow-overlap': true,
+                            'icon-image': 'factory-red',
+                            'icon-size': 0.2
+                        }
+                    });
+
+
+
             map.addLayer({
                 id: 'point-remaining',
                 source: 'points',
@@ -251,6 +350,19 @@ export const showIconLayers = (map: mapboxgl.Map, show: boolean, selectedSiteId?
                     'icon-size': 0.15
                 }
             });
+
+
+            map.addLayer({
+                'id': 'select-point-factory',
+                'type': 'symbol',
+                'source': 'selectedPoints',
+                'filter': ['==', 'type', 'factory'],
+                'layout': {
+                    'icon-allow-overlap': true,
+                    'icon-image': 'factory',
+                    'icon-size': 0.3
+                }
+            });
             }
         }
         }
@@ -270,14 +382,22 @@ export const swapLayersBack = (map: mapboxgl.Map) =>{
     if (map.getLayer('points') === undefined) {
 
         map.setLayoutProperty('selectedPoints', 'visibility', 'visible');
+        map.setLayoutProperty('alarmedPoints', 'visibility', 'visible');
+
 
         map.removeLayer('point-building');
         map.removeLayer('point-lamps');
         map.removeLayer('point-data-center');
+        map.removeLayer('point-factory');
         map.removeLayer('point-remaining');
         map.removeLayer('select-point-data-center');
         map.removeLayer('select-point-buildings');
         map.removeLayer('select-point-lamps');
+        map.removeLayer('select-point-factory');
+        map.removeLayer('point-building-alarm');
+        map.removeLayer('point-lamps-alarm');
+        map.removeLayer('point-data-center-alarm');
+        map.removeLayer('point-factory-alarm');
 
 
 
index 2ed43fa..a8630ae 100644 (file)
@@ -46,7 +46,7 @@
     <properties>
         <buildtime>${maven.build.timestamp}</buildtime>
         <distversion>ONAP Frankfurt (Neon, mdsal ${odl.mdsal.version})</distversion>
-        <buildno>65.cd16345(20/09/03)</buildno>
+        <buildno>66.be51a7d(20/09/03)</buildno>
         <odlux.version>ONAP SDN-R | ONF Wireless for ${distversion} - Build: ${buildtime} ${buildno} ${project.version}</odlux.version>
     </properties>