Update NetworkMap and LinkCalculator 24/118024/1
authorAijana Schumann <aijana.schumann@highstreet-technologies.com>
Thu, 18 Feb 2021 09:55:11 +0000 (10:55 +0100)
committerAijana Schumann <aijana.schumann@highstreet-technologies.com>
Thu, 18 Feb 2021 09:55:11 +0000 (10:55 +0100)
Update NetworkMap and LinkCalculator to use the topology-server-v2 API, minior bugfixes for NetworkMap

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

24 files changed:
sdnr/wt/odlux/apps/linkCalculationApp/src/handlers/linkCalculationAppRootHandler.ts
sdnr/wt/odlux/apps/linkCalculationApp/src/views/linkCalculationComponent.tsx
sdnr/wt/odlux/apps/linkCalculationApp/webpack.config.js
sdnr/wt/odlux/apps/networkMapApp/src/actions/detailsAction.ts
sdnr/wt/odlux/apps/networkMapApp/src/actions/mapActions.ts
sdnr/wt/odlux/apps/networkMapApp/src/actions/popupActions.ts
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/map.tsx
sdnr/wt/odlux/apps/networkMapApp/src/components/mapPopup.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/detailsReducer.ts
sdnr/wt/odlux/apps/networkMapApp/src/handlers/mapReducer.ts
sdnr/wt/odlux/apps/networkMapApp/src/handlers/popupReducer.ts
sdnr/wt/odlux/apps/networkMapApp/src/model/historyEntry.ts
sdnr/wt/odlux/apps/networkMapApp/src/model/link.ts
sdnr/wt/odlux/apps/networkMapApp/src/model/popupElements.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/networkMapApp/src/model/site.ts
sdnr/wt/odlux/apps/networkMapApp/src/utils/utils.ts
sdnr/wt/odlux/apps/networkMapApp/webpack.config.js
sdnr/wt/odlux/framework/pom.xml
sdnr/wt/odlux/odlux.properties

index 012e457..6083670 100644 (file)
@@ -45,8 +45,8 @@ export type ILinkCalculationAppStateState= {
   Lon2: number,
   rainVal : number,
   rainAtt : number,
-  siteA: any,
-  siteB: any,
+  siteA: string,
+  siteB: string,
   reachable: boolean,
   polarization : string | null,
   amslA: number, 
index 7c54ed1..c498379 100644 (file)
@@ -56,7 +56,7 @@ const mapProps = (state: IApplicationStoreState) => ({
   month : state.linkCalculation.calculations.month
 });
 
-const BASE_URL="/topology/services"
+const BASE_URL="/topology/linkcalculator"
 
 const mapDispatch = (dispatcher: IDispatcher) => ({
 
@@ -107,17 +107,14 @@ const mapDispatch = (dispatcher: IDispatcher) => ({
   UpdateWorstMonthRain : (month:string) => {
       dispatcher.dispatch (new UpdateWorstMonthRainAction(month))
     }
-
-  
 });
 
 
-
-
 type linkCalculationProps = Connect<typeof mapProps, typeof mapDispatch>;
 
 interface initialState {
   rainMethodDisplay: boolean, 
+  absorptionMethod : string;
   horizontalBoxChecked: boolean,
     latitude1Error: string,
       longitude1Error:string
@@ -125,6 +122,7 @@ interface initialState {
      longitude2Error:string,
      frequencyError: string,
      rainMethodError: string,
+     attenuationMethodError : string,
      worstmonth : boolean,
      showWM : string
      
@@ -137,19 +135,21 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
     this.state = {
       rainMethodDisplay: false,
       horizontalBoxChecked: true,
+      absorptionMethod : '0',
       latitude1Error: '',
       longitude1Error:'',
       latitude2Error: '',
       longitude2Error:'',
       frequencyError: '',
       rainMethodError: '',
+      attenuationMethodError : '',
       worstmonth : false,
       showWM: ''
         };
   } 
   
   updateAutoDistance = async (lat1: number, lon1: number, lat2: number, lon2: number)=>{
-     const result = await fetch(BASE_URL+'/calculations/distance/' + lat1 + '/' + lon1 + '/' + lat2 + '/' + lon2)
+     const result = await fetch(BASE_URL+'/distance/' + lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2)
       const json = await result.json()
       return json.distanceInKm
       }
@@ -176,7 +176,7 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
     const d = Math.floor(absoluteValue);
     const m = Math.floor((absoluteValue - d) * 60);
     const s = (absoluteValue - d - m / 60) * 3600;
-    const dms = `${d}° ${m}' ${s.toFixed(2)}"`
+    const dms = `${d}° ${m}' ${s.toFixed(2)}"`;
 
     const sign = Math.sign(value);
 
@@ -187,44 +187,44 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
     }
   }
 
-  rainAttCal = (lat1: any, lon1: any, lat2: any, lon2: any, frequency: any, distance: number, polarization : any, worstmonth:boolean) => {
+  rainAttCal = (lat1: any, lon1: any, lat2: any, lon2: any, frequency: any, distance: number, polarization : string, worstmonth:boolean) => {
     if(!worstmonth){
-      fetch(BASE_URL+'/calculations/rain/Annual/' + lat1 + '/' + lon1 + '/' + lat2 + '/' + lon2 + '/' + frequency+ '/'+ distance + '/' + polarization)
+      fetch(BASE_URL+'/rain/annual/' + lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2 + '/' + frequency+ '/'+ distance + '/' + polarization.toUpperCase())
       .then(res => res.json())
-      .then(result => { this.props.UpdateRainAtt(result.RainAtt) ; this.props.updateRainValue(result.rainfall) })
+      .then(result => { this.props.UpdateRainAtt(result.rainAttenuation) ; this.props.updateRainValue(result.rainFall.rainrate)})
     }
     else {
-      fetch(BASE_URL+'/calculations/rain/WM/' + lat1 + '/' + lon1 + '/' + lat2 + '/' + lon2 + '/' + frequency+ '/'+ distance + '/' + polarization)
+      fetch(BASE_URL+'/rain/worstmonth/' + lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2 + '/' + frequency+ '/'+ distance + '/' + polarization.toUpperCase())
       .then(res => res.json())
-      .then(result => { this.props.UpdateRainAtt(result.RainAtt) ; this.props.updateRainValue(result.rainfallWM); this.props.UpdateWorstMonthRain (result.month);  this.setState({showWM: '- Wm is : '})})
+      .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 : any) => {
-    fetch(BASE_URL+'/calculations/rain/' + rainfall + '/' + frequency + '/' + distance+ '/' + polarization)
+  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.RainAtt) })
+      .then(result => { this.props.specificRain(result.rainAttenuation) })
     }
 
 
     FSL = (distance:number, frequency:number) => {
-      fetch(BASE_URL+'/calculations/FSL/' + distance + '/' + frequency)
+      fetch(BASE_URL+'/fsl/' + distance + '/' + frequency)
       .then(res=>res.json())
-      .then (result => {this.props.FSL(result.free)})
+      .then (result => {this.props.FSL(result.fspl)})
     }
   
-    AbsorptionAtt =(lat1: number, lon1: number, lat2: number, lon2: number, distance:number, frequency:number, worstmonth:boolean) => {
+    AbsorptionAtt =(lat1: number, lon1: number, lat2: number, lon2: number, distance:number, frequency:number, worstmonth:boolean, absorptionMethod : string) => {
       if(!worstmonth)
       {
-      fetch(BASE_URL+'/calculations/absorption/Annual/' +lat1 + '/' + lon1 + '/' + lat2 + '/' + lon2 + '/' +  distance + '/' + frequency)
+      fetch(BASE_URL+'/absorption/annual/' +lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2 + '/' +  distance + '/' + frequency + '/' +absorptionMethod)
       .then(res=>res.json())
-      .then (result => {this.props.UpdateAbsorption(result.OxLoss, result.WaterLoss)})
+      .then (result => {this.props.UpdateAbsorption(result.oxygenLoss, result.waterLoss)})
       }
       else {
-       fetch(BASE_URL+'/calculations/absorption/WM/' +lat1 + '/' + lon1 + '/' + lat2 + '/' + lon2 + '/' +  distance + '/' + frequency)
+       fetch(BASE_URL+'/absorption/annual/' +lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2 + '/' +  distance + '/' + frequency + '/' +absorptionMethod)
       .then(res=>res.json())
-      .then (result => {this.props.UpdateAbsorption(result.OxLoss, result.WaterLoss)})
+      .then (result => {this.props.UpdateAbsorption(result.oxygenLoss, result.waterLoss)})
       }
     }
 
@@ -236,11 +236,12 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
       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.rainMethodError === null  && this.props.rainVal === 0 ? this.setState({rainMethodError: 'Select the rain method'}) : this.setState({rainMethodError: ''})
-      
-      console.log(this.props.lat1 !== 0 && this.props.lat2 !== 0 && this.props.lon1 !== 0 && this.props.lon2 !==0 && this.props.frequency!==0);
+      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 
+      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=== '';
 
      }
   
@@ -256,33 +257,33 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
       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.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){
 
-          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);
+          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.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.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 null
+      else console.log('form is not valid')
 
   }
 
   componentDidMount = () => {
-    fetch (BASE_URL+'/calculations/fsl/0/0')
-    .then(res => {if (res.ok) {this.props.reachable===false && this.props.UpdateConectivity(true)}else {this.props.reachable===true && this.props.UpdateConectivity(false)} })
-    .catch (res => {this.props.reachable===true && 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) => {
@@ -346,7 +347,7 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
 
           <div className='column1'>
             <div>&nbsp;</div>
-          <div >Site Name</div>
+          {(this.props.siteA.length>0 || this.props.siteB.length>0)  && <div >Site Name</div>}
           <div>Latitude</div>
           <div>Longitude</div>
           <div>Azimuth</div>
@@ -359,6 +360,7 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
           <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>
@@ -366,7 +368,7 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
 
           <div className='middlecolumn'>
           <div  >Site A</div>
-          <div> {this.props.siteA }</div>
+          {this.props.siteA.length>0 &&<div> {this.props.siteA }</div>}
           <div> {this.props.lat1 && this.LatLonToDMS(this.props.lat1)}</div>
           <div>{this.props.lon1 && this.LatLonToDMS(this.props.lon1)}</div>
           <div>0</div>
@@ -375,11 +377,11 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
         
 
           <div className='column2'>
-          <div>{this.props.distance.toFixed(3)} km</div>
+          <div>{this.props.distance?.toFixed(3)} km</div>
           <div>{<form><input type='checkbox' id='Horizontal' value ="Horizontal" checked= {this.props.polarization==='Horizontal'} onClick= {(e: any) => this.props.updatePolarization(e.target.value)}></input>Horizontal
                       <input 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 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:''})}}> 
+              <div> {<select 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' >Select Freq</option>
                       <option value='7' >7 GHz</option>
@@ -391,20 +393,28 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
                       <option value='38' >38 GHz</option>
                       <option value='42' >42 GHz</option>
                       <option value='80' >80 GHz</option>
-                      </select>} <div style={{fontSize:12, color:'red'}}>  {this.state.frequencyError} </div> </div>
+                      </select>} <div style={{fontSize:12, color:'red'}}>  {this.state.frequencyError} </div> </div> 
 
           <div>{this.props.fsl.toFixed(3)} dB</div>
 
-          <div> {<select 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===''? this.setState({rainMethodError: 'select a Rain model'}): this.setState({rainMethodError:''}) }}>
-                        <option value='' >Select Rain Method</option>
+          <div> {<select 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' >Select Rain Method</option>
                         <option value='itu' >ITU-R P.837-7</option>
-                        <option value='manual'  >Specific Rain</option>
+                        <option value='manual' >Specific Rain</option>
                         </select>} <div style={{fontSize:12,color:'red'}}>{this.state.rainMethodError}</div>
             </div> 
             <div> {<form><input 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>{this.props.rainAtt.toFixed(3)} dB</div>
+
+          <div> {<select 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' >Select Absorption Method</option>
+                        <option value='ITURP67612' >ITU-R P.676-12</option>
+                        <option value='ITURP67611'  >ITU-R P.676-11</option>
+                        <option value='ITURP67610'  >ITU-R P.676-10</option>
+                        </select>} <div style={{fontSize:12,color:'red'}}>{this.state.attenuationMethodError}</div>
+            </div> 
           <div>{this.props.absorptionOxygen.toFixed(3)} dB</div>
           <div>{this.props.absorptionWater.toFixed(3)} dB</div>
           <div>{<button style={{color: '#222', fontFamily:'Arial', boxAlign: 'center', display:'inline-block', insetInlineStart: '20' , alignSelf:'center' }}
@@ -415,7 +425,7 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
           </div>
           <div className= 'middlecolumn'>
           <div  >Site B</div>
-          <div> {this.props.siteB}</div>
+          {this.props.siteB.length>0 &&<div> {this.props.siteB}</div>}
           <div> {this.props.lat2 && this.LatLonToDMS(this.props.lat2)}</div>
           <div>{this.props.lon2 && this.LatLonToDMS(this.props.lon2)}</div>
           <div>0</div>
index 3c9093c..515c582 100644 (file)
@@ -141,27 +141,27 @@ module.exports = (env) => {
       },
       proxy: {
         "/oauth2/": {
-          target: "http://10.20.6.29:8181",
+          target: "http://sdnr:8181",
           secure: false
         },
         "/database/": {
-          target: "http://10.20.6.29:8181",
+          target: "http://sdnr:8181",
           secure: false
         },
-        "/restconf/": {
-          target: "http://10.20.6.29:8181",
+        "/rests/": {
+          target: "http://sdnr:8181",
           secure: false
         },
         "/topology/": {
-          target: "http://localhost:3001",
+          target: "http://localhost:3002",
           secure: false
         },
         "/help/": {
-          target: "http://10.20.6.29:8181",
+          target: "http://sdnr:8181",
           secure: false
         },
         "/websocket/": {
-          target: "http://10.20.6.29:8181",
+          target: "http://sdnr:8181",
           ws: true,
           changeOrigin: true,
           secure: false
index afa8ee4..34cf109 100644 (file)
@@ -20,14 +20,14 @@ import { Action } from '../../../../framework/src/flux/action';
 import { requestRest } from '../../../../framework/src/services/restService';
 
 
-import { site, Device } from "../model/site";
+import { Site, Device } from "../model/site";
 import { link } from '../model/link';
 import { HistoryEntry } from "../model/historyEntry";
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
 import { Dispatch } from '../../../../framework/src/flux/store';
 
 export class SelectSiteAction extends Action {
-  constructor(public site: site){
+  constructor(public site: Site){
     super()
   }
 }
@@ -74,6 +74,12 @@ export class ClearLoadedDevicesAction extends Action{
   }
 }
 
+export class InitializeLoadedDevicesAction extends Action{
+  constructor(public devices: Device[]){
+    super();
+  }
+}
+
 let running=false;
 
 export const UpdateDetailsView = (nodeId: string) =>(dispatcher: Dispatch, getState: () => IApplicationStoreState) =>{
@@ -92,7 +98,6 @@ export const UpdateDetailsView = (nodeId: string) =>(dispatcher: Dispatch, getSt
       dispatcher(new FinishedLoadingDeviceListAction(checkedDevices));
 
      });
-
   }
 }
 
index b8af40b..9830c06 100644 (file)
@@ -21,7 +21,7 @@ import { Dispatch } from '../../../../framework/src/flux/store';
 
 
 import { link } from "../model/link";
-import { site } from "../model/site";
+import { Site } from "../model/site";
 import { Feature } from '../model/Feature';
 import { URL_API } from '../config';
 
@@ -33,7 +33,7 @@ export class HighlightLinkAction extends Action{
 }
 
 export class HighlightSiteAction extends Action{
-    constructor(public site: site){
+    constructor(public site: Site){
         super();
     }
 }
@@ -51,7 +51,7 @@ export class ZoomToSearchResultAction extends Action{
 }
 
 export class AddAlarmAction extends Action{
-    constructor(public element: Feature){
+    constructor(public site: Site){
         super();
     }
 }
@@ -75,7 +75,8 @@ export class SetIconSwitchAction extends Action{
 }
 
 export const findSiteToAlarm = (alarmedNodeId: string) => (dispatcher: Dispatch) =>{
-    fetch(URL_API+"/site/geojson/device/"+alarmedNodeId)
+    //TODO: fix!
+    fetch(URL_API+"/sites/devices/"+alarmedNodeId)
     .then(res => res.json())
     .then(result=>{
         dispatcher(new AddAlarmAction(result));
index ff8d079..a8c54d4 100644 (file)
@@ -16,6 +16,7 @@
  * ============LICENSE_END==========================================================================
  */
 
+import { PopupElement } from '../model/popupElements';
 import { Action } from '../../../../framework/src/flux/action';
 
 export class SetPopupPositionAction extends Action {
@@ -25,7 +26,7 @@ export class SetPopupPositionAction extends Action {
 }
 
 export class SelectMultipleLinksAction extends Action {
-    constructor(public ids: string[]) {
+    constructor(public elements: PopupElement[]) {
         super();
     }
 }
@@ -33,7 +34,7 @@ export class SelectMultipleLinksAction extends Action {
 
 
 export class SelectMultipleSitesAction extends Action {
-    constructor(public ids: string[]) {
+    constructor(public elements: PopupElement[]) {
        super();
     }
 }
\ No newline at end of file
index 081276b..2540b20 100644 (file)
@@ -20,7 +20,7 @@ import * as React from 'react'
 
 import connect, { IDispatcher, Connect } from '../../../../../framework/src/flux/connect';
 
-import { site, Device } from '../../model/site';
+import { Site, Device } from '../../model/site';
 import Typography from '@material-ui/core/Typography';
 import { link } from '../../model/link';
 import { Breadcrumbs, Link, Paper } from '@material-ui/core';
@@ -76,7 +76,7 @@ const Details: React.FunctionComponent<porps> = (props) => {
     }, [props.data])
 
     const onLinkClick = async (id: string) => {
-        const result = await fetch(`${URL_API}/link/${id}`);
+        const result = await fetch(`${URL_API}/links/${id}`);
         if(result.ok){
             const resultAsJson = await result.json();
             const link = resultAsJson as link;
@@ -102,10 +102,9 @@ const Details: React.FunctionComponent<porps> = (props) => {
         e.preventDefault();
     }
 
-    const createDetailPanel = (data: site | link) => {
-
+    const createDetailPanel = (data: Site | link) => {
         if (isSite(data)) {
-            return <SiteDetails navigate={props.navigateToApplication} updatedDevices={props.updatedDevices} loadDevices={props.loadDevices} site={data} onLinkClick={onLinkClick} />
+            return <SiteDetails  site={data} onLinkClick={onLinkClick} />
         } else {
             return <LinkDetails link={data} />
         }
@@ -120,7 +119,7 @@ const Details: React.FunctionComponent<porps> = (props) => {
 
     const loadDetailsData = (id: string) =>{
 
-        fetch(`${URL_API}/link/${id}`)
+        fetch(`${URL_API}/links/${id}`)
                 .then(res => {
                     if (res.ok)
                         return res.json()
@@ -135,7 +134,7 @@ const Details: React.FunctionComponent<porps> = (props) => {
                 })
                 .catch(error => {
 
-                    fetch(`${URL_API}/site/${id}`)
+                    fetch(`${URL_API}/sites/name/${id}`)
                         .then(res => {
                             if (res.ok)
                                 return res.json()
@@ -188,13 +187,13 @@ const mapStateToProps = (state: IApplicationStoreState) => ({
 });
 
 const mapDispatchToProps = (dispatcher: IDispatcher) => ({
-    selectSite: (site: site) => dispatcher.dispatch(new SelectSiteAction(site)),
+    selectSite: (site: Site) => dispatcher.dispatch(new SelectSiteAction(site)),
     selectLink: (link: link) => dispatcher.dispatch(new SelectLinkAction(link)),
     clearDetails: () => dispatcher.dispatch(new ClearDetailsAction()),
     addHistory: (newEntry: HistoryEntry) => dispatcher.dispatch(new AddToHistoryAction(newEntry)),
     clearHistory: () => dispatcher.dispatch(new ClearHistoryAction()),
     highlightLink: (link: link) => dispatcher.dispatch(new HighlightLinkAction(link)),
-    highlightSite: (site: site) => dispatcher.dispatch(new HighlightSiteAction(site)),
+    highlightSite: (site: Site) => dispatcher.dispatch(new HighlightSiteAction(site)),
     loadDevices: async (networkElements: Device[]) => { await dispatcher.dispatch(CheckDeviceList(networkElements)) },
     navigateToApplication: (applicationName: string, path?: string) => dispatcher.dispatch(new NavigateToApplication(applicationName, path, "test3")),
     undoMapSelection: () => dispatcher.dispatch(new RemoveHighlightingAction())
index 81f9bba..b2c7246 100644 (file)
@@ -77,7 +77,7 @@ const LinkDetails: React.FunctionComponent<props> = (props) => {
    {name:"Site Name", val1: props.link.siteA, val2: props.link.siteB},
     {name:"Latitude", val1: LatLonToDMS(props.link.locationA.lat), val2: LatLonToDMS(props.link.locationB.lat)},
     {name:"Longitude", val1: LatLonToDMS(props.link.locationA.lon, true), val2: LatLonToDMS(props.link.locationB.lon, true)},
-    {name:"Azimuth in °", val1: props.link.azimuthA.toFixed(2), val2: props.link.azimuthB.toFixed(2)}
+    props.link.azimuthA!= null && props.link.azimuthB != null && {name:"Azimuth in °", val1: props.link.azimuthA.toFixed(2), val2: props.link.azimuthB.toFixed(2)}
 ];
 
     return (<div style={{ paddingLeft: "15px", paddingRight: "15px", paddingTop: "0px", display: 'flex', flexDirection: 'column' }}>
index 5e617be..3aa35c3 100644 (file)
@@ -23,19 +23,28 @@ import { TextField, Tabs, Tab, Typography, AppBar, Button, Tooltip } from '@mate
 import MaterialTable, { ColumnModel, ColumnType, MaterialTableCtorType } from "../../../../../framework/src/components/material-table";
 
 
-import { site, Device } from '../../model/site';
+import { Site, Device, Address } from '../../model/site';
 import DenseTable from '../denseTable';
 import { LatLonToDMS } from '../../utils/mapUtils';
+import { CheckDeviceList, InitializeLoadedDevicesAction } from '../../actions/detailsAction';
+import { NavigateToApplication } from '../../../../../framework/src/actions/navigationActions';
+import connect, { Connect, IDispatcher } from '../../../../../framework/src/flux/connect';
+import { IApplicationStoreState } from '../../../../../framework/src/store/applicationStore';
 
-type minLinks = { name: string, azimuth: string}
-
-const FaultAlarmNotificationTable = MaterialTable as MaterialTableCtorType<minLinks>;
+type linkRow = { name: string, azimuth?: string}
+type deviceRow = { id: string;type: string,name: string,manufacturer: string,owner: string,status?: string,port: number[]}
 
 
 type panelId="links" | "nodes";
-type props = { site: site, updatedDevices: Device[]|null, navigate(applicationName: string, path?: string):void, onLinkClick(id: string): void, loadDevices(devices:Device[]): void };
+type siteDetailProps = {
+    site: Site, 
+    onLinkClick(id: string): void, 
+} & props;
+
+type props =  Connect<typeof mapStateToProps, typeof mapDispatchToProps>;
+
 
-const SiteDetails: React.FunctionComponent<props> = (props) => {
+const SiteDetails: React.FunctionComponent<siteDetailProps> = (props) => {
 
     const [value, setValue] = React.useState<panelId>("links");
     const [height, setHeight] = React.useState(330);
@@ -59,21 +68,51 @@ const SiteDetails: React.FunctionComponent<props> = (props) => {
 
     // on update
     React.useEffect(()=>{
-
-        props.loadDevices(props.site.devices);
+        
+        if(props.site.devices!== null && props.site.devices.length>0){
+            props.initializeDevices(props.site.devices);
+            props.loadDevices(props.site.devices);
+        }
+      
         handleResize();
 
-    }, [props.site])
+    }, [props.site]);
 
     const onHandleTabChange = (event: React.ChangeEvent<{}>, newValue: panelId) => {
         setValue(newValue);
     }
 
-    const linkRows: minLinks[] = props.site.links.map(link=> 
+    //prepare link table
+
+    let hasAzimuth = false;
+    const linkRows: linkRow[] = props.site.links.map(link=> 
         { 
-            return {name: link.name, azimuth: link.azimuthB.toFixed(2) }   
+            if(link.azimuthB!==null){
+                hasAzimuth=true;
+                return {name: link.name, azimuth: link.azimuthB.toFixed(2) }   
+
+            }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 adressString = props.site.address == null ? null : buildAdress(props.site.address);
 
 
     return (<div  style={{ padding: '15px', display: "flex", flexDirection:"column", minWidth:0, minHeight:0 }}>
@@ -88,20 +127,20 @@ const SiteDetails: React.FunctionComponent<props> = (props) => {
             <TextField inputProps={{ 'aria-label': 'type' }} disabled={true} value={props.site.type} label="Type" style={{ marginTop: "5px" }} />
         }
         {
-            props.site.address !== undefined && props.site.address.length > 0 &&
-            <TextField inputProps={{ 'aria-label': 'adress' }} disabled={true} value={props.site.address} label="Adress" style={{ marginTop: "5px" }} />
+            adressString !== null && 
+            <TextField inputProps={{ 'aria-label': 'adress' }} disabled={true} value={adressString} label="Address" style={{ marginTop: "5px" }} />
         }
         {
-            props.site.heighAGLInMeters !== undefined && props.site.heighAGLInMeters > 0 &&
-            <TextField inputProps={{ 'aria-label': 'amsl-in-meters' }} disabled={true} value={props.site.heighAGLInMeters} label="AMSL in meters" style={{ marginTop: "5px" }} />
+            props.site.heightAmslInMeters !== undefined && props.site.heightAmslInMeters > 0 &&
+            <TextField inputProps={{ 'aria-label': 'amsl-in-meters' }} disabled={true} value={props.site.heightAmslInMeters} label="AMSL in meters" style={{ marginTop: "5px" }} />
         }
         {
-            props.site.antennaHeightAGLInMeters !== undefined && props.site.antennaHeightAGLInMeters > 0 &&
-            <TextField inputProps={{ 'aria-label': 'antenna-above-ground-in-meters' }} disabled={true} value={props.site.antennaHeightAGLInMeters} label="Atenna above ground in meters" style={{ marginTop: "5px" }} />
+            props.site.antennaHeightAmslInMeters !== undefined && props.site.antennaHeightAmslInMeters > 0 &&
+            <TextField inputProps={{ 'aria-label': 'antenna-above-ground-in-meters' }} disabled={true} value={props.site.antennaHeightAmslInMeters} label="Atenna above ground in meters" style={{ marginTop: "5px" }} />
         }
          
-        <TextField inputProps={{ 'aria-label': 'latitude' }} style={{ marginTop: "5px" }} disabled={true} value={LatLonToDMS(props.site.geoLocation.lat)} label="Latitude" />
-        <TextField inputProps={{ 'aria-label': 'longitude' }} style={{ marginTop: "5px" }} disabled={true} value={LatLonToDMS(props.site.geoLocation.lon, true)} label="Longitude" />
+        <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" value={value} onChange={onHandleTabChange} aria-label="simple tabs example">
@@ -119,13 +158,8 @@ const SiteDetails: React.FunctionComponent<props> = (props) => {
                
                 {
                     props.site.links.length > 0 &&
-                    <DenseTable ariaLabelRow="available-links-table" ariaLabelColumn={["link-name", "azimuth"]} height={height} hover={true} headers={["Link Name", "Azimuth in °"]}  data={linkRows} onClick={props.onLinkClick}  ></DenseTable>
-               /**
-                * 
-                * */
-                
-               
-               }
+                    <DenseTable ariaLabelRow="available-links-table" ariaLabelColumn={["link-name", "azimuth"]} height={height} hover={true} headers={linkTableHeader}  data={linkRows} onClick={props.onLinkClick}  ></DenseTable>
+                }
 
             </>
 
@@ -134,19 +168,46 @@ const SiteDetails: React.FunctionComponent<props> = (props) => {
             value === "nodes" &&
             <>
                 {
-                    props.site.devices.length === 0 &&
+                    props.site.devices === null &&
                     <Typography aria-label="no-nodes-avilable" variant="body1" style={{ marginTop: '10px' }}>No nodes available.</Typography>
                 }
 
                 {
-                    props.site.devices.length>0 && props.updatedDevices !== null &&
-                    <DenseTable ariaLabelRow="available-nodes-table" ariaLabelColumn={["id","name","type", "manufacturer","owner","status", "ports", "actions"]} navigate={props.navigate} height={height} hover={false} headers={["ID","Name","Type", "Manufacturer","Owner","Status", "Ports", "Actions"]} actions={true} data={props.updatedDevices!} />
+                    props.site.devices?.length>0 && props.updatedDevices !== null &&
+                    <DenseTable ariaLabelRow="available-nodes-table" ariaLabelColumn={["id","name","type","status", "manufacturer","owner", "ports", "actions"]} navigate={props.navigateToApplication} height={height} hover={false} headers={["ID","Name","Type","Status", "Manufacturer","Owner", "Ports", "Actions"]} actions={true} data={deviceRows!} />
                 }
             </>
         }
     </div>
     )
+}
+
+const buildAdress = (adress: Address) =>{
 
+    switch(adress.country){
+        case "de":
+            return `${adress.streetAndNr}, ${adress.zipCode!== null? adress.zipCode : ''} ${adress.city}`
+        
+        case "us": 
+        return `${adress.streetAndNr}, ${adress.city} ${adress.zipCode!== null? adress.zipCode : ''}`
+
+        default:
+            console.log("address formatting for country {"+adress.country+"} not recognized, defaulting.");
+            return `${adress.streetAndNr}, ${adress.zipCode!== null? adress.zipCode : ''} ${adress.city}`
+    }
+
+    
 }
 
-export default SiteDetails;
\ No newline at end of file
+const mapStateToProps = (state: IApplicationStoreState) => ({
+    updatedDevices: state.network.details.checkedDevices
+});
+
+const mapDispatchToProps = (dispatcher: IDispatcher) => ({
+    initializeDevices: (devices: Device[]) => {dispatcher.dispatch(new InitializeLoadedDevicesAction(devices))},
+    loadDevices: async (networkElements: Device[]) => { await dispatcher.dispatch(CheckDeviceList(networkElements)) },
+    navigateToApplication: (applicationName: string, path?: string) => dispatcher.dispatch(new NavigateToApplication(applicationName, path, "test3")),
+
+})
+
+export default connect(mapStateToProps, mapDispatchToProps)(SiteDetails);
\ No newline at end of file
index e2935f6..855e5ce 100644 (file)
@@ -21,7 +21,7 @@ import * as mapboxgl from 'mapbox-gl';
 import { RouteComponentProps, withRouter } from 'react-router-dom';
 
 
-import { site } from '../model/site';
+import { Site } from '../model/site';
 import { SelectSiteAction, ClearHistoryAction, SelectLinkAction } from '../actions/detailsAction';
 import { OSM_STYLE, URL_API, URL_BASEPATH, URL_TILE_API } from '../config';
 import { link } from '../model/link';
@@ -36,13 +36,10 @@ import SearchBar from './searchBar';
 import { verifyResponse, IsTileServerReachableAction, handleConnectionError, setTileServerReachableAction } from '../actions/connectivityAction';
 import ConnectionInfo from './connectionInfo'
 import { showIconLayers, addBaseLayers, addBaseSources, addIconLayers } 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';
 import { addImages } from '../services/mapImagesService';
+import { PopupElement } from '../model/popupElements';
 
 type coordinates = { lat: number, lon: number, zoom: number }
 
@@ -86,7 +83,7 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
                 console.error("tileserver " + URL_TILE_API + " can't be reached.");
             });
 
-        fetch(URL_API + "/info")
+        fetch(URL_API + "/info/count/all")
             .then(result => verifyResponse(result))
             .catch(error => this.props.handleConnectionError(error));
     }
@@ -132,7 +129,7 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
 
             const boundingBox = increaseBoundingBox(map);
 
-            fetch(`${URL_API}/links/geoJson/${boundingBox.west},${boundingBox.south},${boundingBox.east},${boundingBox.north}`)
+            fetch(`${URL_API}/links/geojson/${boundingBox.west},${boundingBox.south},${boundingBox.east},${boundingBox.north}`)
                 .then(result => verifyResponse(result))
                 .then(result => result.json())
                 .then(features => {
@@ -143,7 +140,7 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
                 .catch(error => this.props.handleConnectionError(error));
 
 
-            fetch(`${URL_API}/sites/geoJson/${boundingBox.west},${boundingBox.south},${boundingBox.east},${boundingBox.north}`)
+            fetch(`${URL_API}/sites/geojson/${boundingBox.west},${boundingBox.south},${boundingBox.east},${boundingBox.north}`)
                 .then(result => verifyResponse(result))
                 .then(result => result.json())
                 .then(features => {
@@ -439,8 +436,8 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
 
             if (lastBoundingBox == null) {
                 lastBoundingBox = bbox;
-                await this.draw('lines', `${URL_API}/links/geoJson/${lastBoundingBox.getWest()},${lastBoundingBox.getSouth()},${lastBoundingBox.getEast()},${lastBoundingBox.getNorth()}`);
-                await this.draw('points', `${URL_API}/sites/geoJson/${lastBoundingBox.getWest()},${lastBoundingBox.getSouth()},${lastBoundingBox.getEast()},${lastBoundingBox.getNorth()}`);
+                await this.draw('lines', `${URL_API}/links/geojson/${lastBoundingBox.getWest()},${lastBoundingBox.getSouth()},${lastBoundingBox.getEast()},${lastBoundingBox.getNorth()}`);
+                await this.draw('points', `${URL_API}/sites/geojson/${lastBoundingBox.getWest()},${lastBoundingBox.getSouth()},${lastBoundingBox.getEast()},${lastBoundingBox.getNorth()}`);
             } else {
 
                 // new bbox is bigger than old one
@@ -451,8 +448,8 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
                     //calculate new boundingBox
                     const increasedBoundingBox = increaseBoundingBox(map);
 
-                    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}`);
+                    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}`);
 
                 } else if (lastBoundingBox.contains(bbox.getNorthEast()) && lastBoundingBox.contains(bbox.getSouthWest())) { // last one contains new one
                     // bbox is contained in last one, do nothing
@@ -462,8 +459,8 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
 
                     lastBoundingBox.extend(bbox);
 
-                    await this.draw('lines', `${URL_API}/links/geoJson/${lastBoundingBox.getWest()},${lastBoundingBox.getSouth()},${lastBoundingBox.getEast()},${lastBoundingBox.getNorth()}`);
-                    await this.draw('points', `${URL_API}/sites/geoJson/${lastBoundingBox.getWest()},${lastBoundingBox.getSouth()},${lastBoundingBox.getEast()},${lastBoundingBox.getNorth()}`);
+                    await this.draw('lines', `${URL_API}/links/geojson/${lastBoundingBox.getWest()},${lastBoundingBox.getSouth()},${lastBoundingBox.getEast()},${lastBoundingBox.getNorth()}`);
+                    await this.draw('points', `${URL_API}/sites/geojson/${lastBoundingBox.getWest()},${lastBoundingBox.getSouth()},${lastBoundingBox.getEast()},${lastBoundingBox.getNorth()}`);
                 }
 
             }
@@ -481,18 +478,18 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
 
     showSitePopup = (sites: mapboxgl.MapboxGeoJSONFeature[], top: number, left: number) => {
         if (sites.length > 1) {
-            const ids = sites.map(feature => feature.properties!.id);
+            const elements: PopupElement[] = sites.map(feature =>  {return {name: feature.properties!.name, id: feature.properties!.id}});
 
             this.props.setPopupPosition(top, left);
-            this.props.selectMultipleSites(ids);
+            this.props.selectMultipleSites(elements); //name, id object container
             this.setState({ isPopupOpen: true });
 
         } else {
             const id = sites[0].properties!.id;
 
-            fetch(`${URL_API}/site/${id}`)
+            fetch(`${URL_API}/sites/${id}`)
                 .then(result => verifyResponse(result))
-                .then(res => res.json() as Promise<site>)
+                .then(res => res.json() as Promise<Site>)
                 .then(result => {
                     this.props.selectSite(result);
                     this.props.highlightSite(result);
@@ -506,15 +503,16 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> {
 
         if (links.length > 1) {
 
-            const ids = links.map(feature => feature.properties!.id as string);
+            const elements: PopupElement[] = links.map(feature =>  {return {name: feature.properties!.name, id: feature.properties!.id}});
+
             this.props.setPopupPosition(top, left);
-            this.props.selectMultipleLinks(ids);
+            this.props.selectMultipleLinks(elements);
             this.setState({ isPopupOpen: true });
 
         } else {
             var id = links[0].properties!.id;
 
-            fetch(`${URL_API}/link/${id}`)
+            fetch(`${URL_API}/links/${id}`)
                 .then(result => verifyResponse(result))
                 .then(res => res.json() as Promise<link>)
                 .then(result => {
@@ -578,14 +576,14 @@ const mapStateToProps = (state: IApplicationStoreState) => ({
 });
 
 const mapDispatchToProps = (dispatcher: IDispatcher) => ({
-    selectSite: (site: site) => dispatcher.dispatch(new SelectSiteAction(site)),
+    selectSite: (site: Site) => dispatcher.dispatch(new SelectSiteAction(site)),
     selectLink: (link: link) => dispatcher.dispatch(new SelectLinkAction(link)),
     clearDetailsHistory: () => dispatcher.dispatch(new ClearHistoryAction()),
-    selectMultipleLinks: (ids: string[]) => dispatcher.dispatch(new SelectMultipleLinksAction(ids)),
-    selectMultipleSites: (ids: string[]) => dispatcher.dispatch(new SelectMultipleSitesAction(ids)),
+    selectMultipleLinks: (ids: PopupElement[]) => dispatcher.dispatch(new SelectMultipleLinksAction(ids)),
+    selectMultipleSites: (ids: PopupElement[]) => dispatcher.dispatch(new SelectMultipleSitesAction(ids)),
     setPopupPosition: (x: number, y: number) => dispatcher.dispatch(new SetPopupPositionAction(x, y)),
     highlightLink: (link: link) => dispatcher.dispatch(new HighlightLinkAction(link)),
-    highlightSite: (site: site) => dispatcher.dispatch(new HighlightSiteAction(site)),
+    highlightSite: (site: Site) => dispatcher.dispatch(new HighlightSiteAction(site)),
     updateMapPosition: (lat: number, lon: number, zoom: number) => dispatcher.dispatch(new SetCoordinatesAction(lat, lon, zoom)),
     setStatistics: (linkCount: string, siteCount: string) => dispatcher.dispatch(new SetStatistics(siteCount, linkCount)),
     setTileServerLoaded: (reachable: boolean) => dispatcher.dispatch(setTileServerReachableAction(reachable)),
index 0400247..7435a0a 100644 (file)
@@ -19,7 +19,7 @@
 import * as React from 'react';
 import { Typography, Select, MenuItem, ClickAwayListener, Popper, Paper, FormGroup, Portal, Popover } from '@material-ui/core';
 import { SelectSiteAction, ClearHistoryAction, ClearDetailsAction } from '../actions/detailsAction';
-import { site } from '../model/site';
+import { Site } from '../model/site';
 import { link } from '../model/link';
 import { URL_API } from '../config';
 import { HighlightLinkAction, HighlightSiteAction } from '../actions/mapActions';
@@ -40,7 +40,7 @@ const MapPopup: React.FunctionComponent<props> = (props) => {
         const id = event.target.value;
 
        
-        fetch(`${URL_API}/${props.type}/${id}`)
+        fetch(`${URL_API}/${props.type.toLocaleLowerCase()}s/${id}`)
         .then(result => verifyResponse(result))
         .then(res => res.json())
         .then(result => {
@@ -64,7 +64,7 @@ const MapPopup: React.FunctionComponent<props> = (props) => {
                 <Select style={{ width: 300 }} onChange={handleChange} value={value} native>
                     <option value={""} disabled>{props.type} ids</option>
                     {
-                        props.ids.map(id => <option key={id} value={id}>{id}</option>)
+                        props.elements.map(el => <option key={el.id} value={el.id}>{el.name}</option>)
                     }
                 </Select>
             </Paper>
@@ -75,17 +75,17 @@ const MapPopup: React.FunctionComponent<props> = (props) => {
 type props = Connect<typeof mapStateToProps, typeof mapDispatchToProps>& { onClose(): void }
 
 const mapStateToProps = (state: IApplicationStoreState) => ({
-    ids: state.network.popup.selectionPendingForIds,
+    elements: state.network.popup.selectionPendingForElements,
     type: state.network.popup.pendingDataType,
     position: state.network.popup.position
 
 });
 
 const mapDispatchToProps = (dispatcher: IDispatcher) => ({ 
-    selectElement: (site: site) => dispatcher.dispatch(new SelectSiteAction(site)),
+    selectElement: (site: Site) => dispatcher.dispatch(new SelectSiteAction(site)),
     clearDetailsHistory:()=> dispatcher.dispatch(new ClearHistoryAction()),
     highlightLink: (link: link) => dispatcher.dispatch(new HighlightLinkAction(link)),
-    highlightSite: (site: site) => dispatcher.dispatch(new HighlightSiteAction(site)),
+    highlightSite: (site: Site) => dispatcher.dispatch(new HighlightSiteAction(site)),
     handleConnectionError: (error:Error) => dispatcher.dispatch(handleConnectionError(error)),
     clearDetails: () => dispatcher.dispatch(new ClearDetailsAction()),
 
index c825e5a..2e69815 100644 (file)
@@ -22,7 +22,7 @@ import SearchIcon from '@material-ui/icons/Search';
 
 import { URL_API } from '../config';
 import { isSite } from '../utils/utils';
-import { site } from '../model/site';
+import { Site } from '../model/site';
 import { link } from '../model/link';
 import { SelectSiteAction, SelectLinkAction } from '../actions/detailsAction';
 import { HighlightLinkAction, HighlightSiteAction, ZoomToSearchResultAction } from '../actions/mapActions';
@@ -74,9 +74,9 @@ const SearchBar: React.FunctionComponent<searchBarProps> = (props) =>{
       setAnchorEl(null);
       if(props.searchterm.length>0){
 
-        const siteResult = fetch(`${URL_API}/site/${props.searchterm}`)
+        const siteResult = fetch(`${URL_API}/sites/name/${props.searchterm}`)
 
-        const linkResult = fetch(`${URL_API}/link/${props.searchterm}`);
+        const linkResult = fetch(`${URL_API}/links/${props.searchterm}`);
   
            Promise.all([ siteResult, linkResult]).then((result)=>{
               const suceededResults = result.filter(el=> el.ok);
@@ -92,7 +92,7 @@ const SearchBar: React.FunctionComponent<searchBarProps> = (props) =>{
                 if(isSite(result)){
                   props.selectSite(result);
                   props.highlightSite(result);
-                  props.zoomToSearchResult(result.geoLocation.lat, result.geoLocation.lon);
+                  props.zoomToSearchResult(result.location.lat, result.location.lon);
                 }else{
                   props.selectLink(result);
                   props.highlightLink(result);
@@ -149,10 +149,10 @@ type searchBarProps = Connect<typeof mapStateToProps, typeof mapDispatchToProps>
 
 
 const mapDispatchToProps = (dispatcher: IDispatcher) => ({ 
-  selectSite:(site: site)=> dispatcher.dispatch(new SelectSiteAction(site)), 
+  selectSite:(site: Site)=> dispatcher.dispatch(new SelectSiteAction(site)), 
   selectLink:(link: link) => dispatcher.dispatch(new SelectLinkAction(link)), 
   highlightLink:(link: link)=> dispatcher.dispatch(new HighlightLinkAction(link)),
-  highlightSite: (site: site) => dispatcher.dispatch(new HighlightSiteAction(site)),
+  highlightSite: (site: Site) => dispatcher.dispatch(new HighlightSiteAction(site)),
   setSearchTerm: (value: string) => dispatcher.dispatch(new SetSearchValueAction(value)),
   zoomToSearchResult: (lat: number, lon: number) => dispatcher.dispatch(new ZoomToSearchResultAction(lat, lon)),
 });;
index a85259d..633bd37 100644 (file)
@@ -16,8 +16,8 @@
  * ============LICENSE_END==========================================================================
  */
 
-export const URL_API="/topology"
-export const URL_TILE_API = '/tiles';
+export const URL_API="/topology/network"
+export const URL_TILE_API = '/tiles'; // http://tile.openstreetmap.org can be used for local testing, never commit with tile url changed! /tiles
 
 
 export const OSM_STYLE = {
index f573009..67e10e6 100644 (file)
 
 import { IActionHandler } from '../../../../framework/src/flux/action';
 import { link } from "../model/link";
-import { site, Device } from "../model/site";
+import { Site, Device } from "../model/site";
 import { HistoryEntry } from "../model/historyEntry";
-import { SelectSiteAction, SelectLinkAction, AddToHistoryAction, ClearHistoryAction, IsBusyCheckingDeviceListAction, FinishedLoadingDeviceListAction, ClearLoadedDevicesAction, ClearDetailsAction } from '../actions/detailsAction';
+import { SelectSiteAction, SelectLinkAction, AddToHistoryAction, ClearHistoryAction, IsBusyCheckingDeviceListAction, FinishedLoadingDeviceListAction, ClearLoadedDevicesAction, ClearDetailsAction, InitializeLoadedDevicesAction } from '../actions/detailsAction';
 
 export type DetailsStoreState={
-    data: site | link | null,
+    data: Site | link | null,
     history: HistoryEntry[],
     isBusyCheckingDeviceList: boolean,
-    checkedDevices: Device[] | null
+    checkedDevices: Device[] 
 
 }
 
@@ -34,7 +34,7 @@ const initialState: DetailsStoreState = {
     data: null,
     history:[],
     isBusyCheckingDeviceList: false,
-    checkedDevices: null
+    checkedDevices: []
 }
 
 export const DetailsReducer:IActionHandler<DetailsStoreState>=(state = initialState, action)=>{
@@ -59,8 +59,10 @@ export const DetailsReducer:IActionHandler<DetailsStoreState>=(state = initialSt
         state = Object.assign({}, state, {checkedDevices: action.devices});
         
     }else if(action instanceof ClearLoadedDevicesAction){
-        state = Object.assign({}, state, {checkedDevices: null});
+        state = Object.assign({}, state, {checkedDevices: []});
 
+    }else if(action instanceof InitializeLoadedDevicesAction){
+        state = Object.assign({}, state, {checkedDevices: action.devices});
     }
 
 
index 5c1c6d2..b820746 100644 (file)
@@ -56,12 +56,12 @@ export const MapReducer: IActionHandler<mapState> = (state=initialState, action:
     }
     else if(action instanceof HighlightSiteAction){
        
-    state = Object.assign({}, state, {selectedLink: null, selectedSite:{type: "Feature", properties: {id: action.site.name, type:action.site.type}, geometry:{type:"Point", coordinates:[action.site.geoLocation.lon,action.site.geoLocation.lat ]}}})
+    state = Object.assign({}, state, {selectedLink: null, selectedSite:{type: "Feature", properties: {id: action.site.name, type:action.site.type}, geometry:{type:"Point", coordinates:[action.site.location.lon,action.site.location.lat ]}}})
 
     }else if (action instanceof ZoomToSearchResultAction){
         state = Object.assign({}, state, {zoomToElement:{lat: action.lat, lon: action.lon}});
     }else if (action instanceof AddAlarmAction){
-        state = Object.assign({}, state, {alarmlement:action.element});
+        state = Object.assign({}, state, {alarmlement:{type: "Feature", properties: {id: action.site.name, type:action.site.type}, geometry:{type:"Point", coordinates:[action.site.location.lon,action.site.location.lat ]}}});
 
     }else if(action instanceof SetCoordinatesAction){
         state = Object.assign({}, state, {lat:action.lat, lon: action.lon, zoom:action.zoom});
index dcac9c9..deb366e 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 
+import { PopupElement } from 'model/popupElements';
 import { IActionHandler } from '../../../../framework/src/flux/action';
 import { SelectMultipleLinksAction, SelectMultipleSitesAction, SetPopupPositionAction } from "../actions/popupActions";
 
 export type popupStoreState = {
-    selectionPendingForIds: string[],
+    selectionPendingForElements: PopupElement[],
     pendingDataType: "link"|"site"| "",
     position: { top: number, left: number }
 };
 
 const initialState: popupStoreState = {
-    selectionPendingForIds: [],
+    selectionPendingForElements: [],
     pendingDataType: "",
     position: { top: 0, left: 0 }
 };
@@ -34,10 +35,10 @@ const initialState: popupStoreState = {
 export const PopupsReducer: IActionHandler<popupStoreState> = (state = initialState, action) => {
    
     if(action instanceof SelectMultipleLinksAction){
-        state = Object.assign({}, state, { selectionPendingForIds: action.ids, pendingDataType: "link", isSelectionNeeded: true });
+        state = Object.assign({}, state, { selectionPendingForElements: action.elements, pendingDataType: "link", isSelectionNeeded: true });
 
     }else if(action instanceof SelectMultipleSitesAction){
-        state = Object.assign({}, state, { selectionPendingForIds: action.ids, pendingDataType: "site", isSelectionNeeded: true });
+        state = Object.assign({}, state, { selectionPendingForElements: action.elements, pendingDataType: "site", isSelectionNeeded: true });
 
     }else if(action instanceof SetPopupPositionAction){
         state= Object.assign({}, state, {position:{top:action.top, left: action.left}})
index 707ff3d..d7197a4 100644 (file)
@@ -16,7 +16,7 @@
  * ============LICENSE_END==========================================================================
  */
 
-import { site } from "./site";
+import { Site } from "./site";
 import { link } from "./link";
 
-export type HistoryEntry={id: string, data: site|link};
\ No newline at end of file
+export type HistoryEntry={id: string, data: Site|link};
\ No newline at end of file
index e11be1a..d992c66 100644 (file)
@@ -23,8 +23,8 @@ export type link = {id: string,
     type: string,
      siteA: string,
      siteB: string,
-    azimuthA: number,
-    azimuthB: number,
-    locationA: { lon: number, lat: number, amsl?:number, antennaHeight?: number },
-    locationB: { lon: number, lat: number, amsl?:number, antennaHeight?: number },
+    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 },
  };
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/model/popupElements.ts b/sdnr/wt/odlux/apps/networkMapApp/src/model/popupElements.ts
new file mode 100644 (file)
index 0000000..320d7ca
--- /dev/null
@@ -0,0 +1,22 @@
+/**
+ * ============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==========================================================================
+ */
+
+export type PopupElement = {
+    name: string,
+    id: string
+}
\ No newline at end of file
index 79af653..b9102e8 100644 (file)
 
 import { link } from "./link";
 
-export type site = {
+export type Site = {
     id: string,
     name: string,
-    address?: string,
-    heighAGLInMeters?: number, //AboveGroundLevel
-    antennaHeightAGLInMeters?: number,
+    address: Address,
+    heightAmslInMeters?: number, //AboveGroundLevel
+    antennaHeightAmslInMeters?: number,
     type?: string,
     operator: string,
-    geoLocation:{lon: number, lat: number},
+    location:{lon: number, lat: number},
     devices: Device[],
     links: link[]
 }
 
-export type Device = {
-    id: string,
-    type: string,
-    name: string,
-    manufacture: string,
-    owner: string,
-    status?: string,
-    port: number[],
-    simulatorId?: string,
+export type Address={
+    streetAndNr: string,
+    city: string,
+    zipCode: string | null,
+    country: string
+}
+
+export class Device {
+    id: string;
+    type: string;
+    name: string;
+    manufacturer: string;
+    owner: string;
+    status?: string;
+    port: number[];
 }
\ No newline at end of file
index 20c4e5a..59085d4 100644 (file)
@@ -17,9 +17,9 @@
  */
 
 import { link } from "../model/link";
-import { site } from "../model/site";
+import { Site } from "../model/site";
 
 
-export function isSite(data: link | site): data is site {
-    return (data as site).geoLocation !== undefined;
+export function isSite(data: link | Site): data is Site {
+    return (data as Site).location !== undefined;
 }
\ No newline at end of file
index 7e51f69..e0f16d0 100644 (file)
@@ -137,27 +137,27 @@ module.exports = (env) => {
       },
       proxy: {
         "/yang-schema/": {
-          target: "http://10.20.6.29:8181",
+          target: "http://sdnr:8181",
           secure: false
         },   
         "/oauth2/": {
-          target: "http://10.20.6.29:8181",
+          target: "http://sdnr:8181",
           secure: false
         },
         "/database/": {
-          target: "http://10.20.6.29:8181",
+          target: "http://sdnr:8181",
           secure: false
         },
         "/restconf/": {
-          target: "http://10.20.6.29:8181",
+          target: "http://sdnr:8181",
           secure: false
         },
         "/rests/": {
-          target: "http://10.20.6.29:8181",
+          target: "http://sdnr:8181",
           secure: false
         },
         "/topology/": {
-          target: "http://localhost:3001",
+          target: "http://localhost:3002",
           secure: false
         },
         "/tiles/": {
@@ -165,11 +165,11 @@ module.exports = (env) => {
           secure: false
         },
         "/help/": {
-          target: "http://10.20.6.29:8181",
+          target: "http://sdnr:8181",
           secure: false
         },
         "/websocket": {
-          target: "http://10.20.6.29:8181",
+          target: "http://sdnr:8181",
           ws: true,
           changeOrigin: true,
           secure: false
index 23854b5..a60c0af 100644 (file)
@@ -46,7 +46,7 @@
     <properties>
         <buildtime>${maven.build.timestamp}</buildtime>
         <distversion>ONAP Frankfurt (Neon, mdsal ${odl.mdsal.version})</distversion>
-        <buildno>89.977e4de(21/02/10)</buildno>
+        <buildno>90.49cc396(21/02/17)</buildno>
         <odlux.version>ONAP SDN-R | ONF Wireless for ${distversion} - Build: ${buildtime} ${buildno} ${project.version}</odlux.version>
     </properties>
 
index 11bffd4..e4d7fdc 100644 (file)
@@ -1,12 +1,12 @@
-odlux.framework.buildno=89.977e4de(21/02/10)
+odlux.framework.buildno=90.49cc396(21/02/17)
 odlux.apps.configurationApp.buildno=89.977e4de(21/02/10)
 odlux.apps.connectApp.buildno=89.977e4de(21/02/10)
 odlux.apps.eventLogApp.buildno=81.1c38886(20/12/04)
 odlux.apps.faultApp.buildno=81.1c38886(20/12/04)
 odlux.apps.helpApp.buildno=81.1c38886(20/12/04)
 odlux.apps.inventoryApp.buildno=89.977e4de(21/02/10)
-odlux.apps.linkCalculationApp.buildno=81.1c38886(20/12/04)
+odlux.apps.linkCalculationApp.buildno=90.49cc396(21/02/17)
 odlux.apps.maintenanceApp.buildno=81.1c38886(20/12/04)
 odlux.apps.mediatorApp.buildno=81.1c38886(20/12/04)
-odlux.apps.networkMapApp.buildno=81.1c38886(20/12/04)
+odlux.apps.networkMapApp.buildno=90.49cc396(21/02/17)
 odlux.apps.permanceHistoryApp.buildno=81.1c38886(20/12/04)