Merge "update odlux for notification change"
[ccsdk/features.git] / sdnr / wt / odlux / apps / linkCalculationApp / src / views / linkCalculationComponent.tsx
1 /**
2  * ============LICENSE_START========================================================================
3  * ONAP : ccsdk feature sdnr wt odlux
4  * =================================================================================================
5  * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved.
6  * =================================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software distributed under the License
13  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14  * or implied. See the License for the specific language governing permissions and limitations under
15  * the License.
16  * ============LICENSE_END==========================================================================
17  */
18
19 import * as React from "react";
20
21 import { Connect, connect, IDispatcher } from '../../../../framework/src/flux/connect';
22 import { MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table';
23 import { TextField, Tabs, Tab, Typography, AppBar, Button, Tooltip, Checkbox, Table, TableCell, TableHead, TableRow, TableBody, Paper } from '@material-ui/core';
24 import './Style.scss'
25
26 import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore";
27 import { UpdateFrequencyAction, UpdateLatLonAction, UpdateRainAttAction, UpdateRainValAction, UpdateFslCalculation, isCalculationServerReachableAction, UpdatePolAction, UpdateDistanceAction, updateAltitudeAction, UpdateAbsorptionLossAction, UpdateWorstMonthRainAction } from "../actions/commonLinkCalculationActions";
28 import { faPlaneArrival, faAlignCenter } from "@fortawesome/free-solid-svg-icons";
29 import ConnectionInfo from '../components/connectionInfo'
30 import { red } from "@material-ui/core/colors";
31
32
33
34 const mapProps = (state: IApplicationStoreState) => ({
35   linkId: state.linkCalculation.calculations.linkId,
36   frequency: state.linkCalculation.calculations.frequency,
37   lat1: state.linkCalculation.calculations.Lat1,
38   lon1: state.linkCalculation.calculations.Lon1,
39   lat2: state.linkCalculation.calculations.Lat2,
40   lon2: state.linkCalculation.calculations.Lon2,
41   rainAtt: state.linkCalculation.calculations.rainAtt,
42   rainVal: state.linkCalculation.calculations.rainVal,
43   formView: state.linkCalculation.calculations.formView,
44   fsl:state.linkCalculation.calculations.fsl,
45   siteA: state.linkCalculation.calculations.siteA,
46   siteB: state.linkCalculation.calculations.siteB,
47   distance: state.linkCalculation.calculations.distance,
48   reachable :state.linkCalculation.calculations.reachable,
49   polarization:state.linkCalculation.calculations.polarization,
50   amslA:state.linkCalculation.calculations.amslA,
51   amslB:state.linkCalculation.calculations.amslB,
52   aglA:state.linkCalculation.calculations.aglA,
53   aglB:state.linkCalculation.calculations.aglB,
54   absorptionOxygen : state.linkCalculation.calculations.absorptionOxygen,
55   absorptionWater : state.linkCalculation.calculations.absorptionWater,
56   month : state.linkCalculation.calculations.month
57 });
58
59 const BASE_URL="/topology/linkcalculator"
60
61 const mapDispatch = (dispatcher: IDispatcher) => ({
62
63   updateFrequency: (frequency: number) => {
64     dispatcher.dispatch(new UpdateFrequencyAction(frequency))
65
66   },
67   updateLatLon: (Lat1: number, Lon1: number, Lat2: number, Lon2: number) => {
68     dispatcher.dispatch(new UpdateLatLonAction(Lat1, Lon1, Lat2, Lon2))
69   },
70   
71   updateRainValue: (rainVal: number) => {
72     dispatcher.dispatch(new UpdateRainValAction(rainVal))
73   },
74
75   UpdateRainAtt: (rainAtt: number) => {
76     dispatcher.dispatch(new UpdateRainAttAction(rainAtt))
77   },
78
79   specificRain: (rainAtt: number) => {
80     dispatcher.dispatch(new UpdateRainAttAction(rainAtt))
81
82   },
83  
84   FSL :(free:number)=> {
85     dispatcher.dispatch(new UpdateFslCalculation (free))
86   },
87
88   UpdateConectivity : (reachable:boolean) => {
89     dispatcher.dispatch (new isCalculationServerReachableAction (reachable))
90   },
91
92   updatePolarization :(polarization:any)=>{
93     dispatcher.dispatch (new UpdatePolAction(polarization))
94   },
95
96   updateAutoDistance : (distance:number)=>{
97     dispatcher.dispatch (new UpdateDistanceAction(distance))
98   },
99
100   UpdateAbsorption : (OxLoss:number , WaterLoss:number) => {
101     dispatcher.dispatch (new UpdateAbsorptionLossAction (OxLoss, WaterLoss))
102   },
103   // UpdateWorstMonth : (worstmonth:boolean) => {
104   //   dispatcher.dispatch (new UpdateWorstMonthAction(worstmonth))
105   // }, 
106
107   UpdateWorstMonthRain : (month:string) => {
108       dispatcher.dispatch (new UpdateWorstMonthRainAction(month))
109     }
110 });
111
112
113 type linkCalculationProps = Connect<typeof mapProps, typeof mapDispatch>;
114
115 interface initialState {
116   rainMethodDisplay: boolean, 
117   absorptionMethod : string;
118   horizontalBoxChecked: boolean,
119     latitude1Error: string,
120       longitude1Error:string
121       latitude2Error: string,
122      longitude2Error:string,
123      frequencyError: string,
124      rainMethodError: string,
125      attenuationMethodError : string,
126      worstmonth : boolean,
127      showWM : string
128      
129   
130 }
131
132 class LinkCalculation extends React.Component<linkCalculationProps, initialState> {
133   constructor(props: any) {
134     super(props);
135     this.state = {
136       rainMethodDisplay: false,
137       horizontalBoxChecked: true,
138       absorptionMethod : '0',
139       latitude1Error: '',
140       longitude1Error:'',
141       latitude2Error: '',
142       longitude2Error:'',
143       frequencyError: '',
144       rainMethodError: '',
145       attenuationMethodError : '',
146       worstmonth : false,
147       showWM: ''
148         };
149   } 
150   
151   updateAutoDistance = async (lat1: number, lon1: number, lat2: number, lon2: number)=>{
152      const result = await fetch(BASE_URL+'/distance/' + lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2)
153       const json = await result.json()
154       return json.distanceInKm
155       }
156
157   updateLatLon = (e:any) => {
158     
159     e.target.id== 'Lat1'? this.props.updateLatLon(e.target.value, this.props.lon1, this.props.lat2, this.props.lon2) : null
160     e.target.id== 'Lon1'? this.props.updateLatLon(this.props.lat1, e.target.value, this.props.lat2, this.props.lon2) : null
161     e.target.id== 'Lat2'? this.props.updateLatLon(this.props.lat1, this.props.lon1, e.target.value, this.props.lon2) : null
162     e.target.id== 'Lon2'? this.props.updateLatLon(this.props.lat1, e.target.value, this.props.lat2, e.target.value) : null
163
164    
165   }
166
167   updatePoli = (val: string) =>{
168
169     this.setState({horizontalBoxChecked: !this.state.horizontalBoxChecked});
170     this.props.updatePolarization(val);
171    
172   }
173
174   LatLonToDMS = (value: number, isLon: boolean = false) => {
175     const absoluteValue = Math.abs(value);
176     const d = Math.floor(absoluteValue);
177     const m = Math.floor((absoluteValue - d) * 60);
178     const s = (absoluteValue - d - m / 60) * 3600;
179     const dms = `${d}° ${m}' ${s.toFixed(2)}"`;
180
181     const sign = Math.sign(value);
182
183     if (isLon) {
184       return (sign === -1 || sign === -0) ? dms + " W" : dms + " E";
185     } else {
186       return (sign === -1 || sign === -0) ? dms + " S" : dms + " N";
187     }
188   }
189
190   rainAttCal = (lat1: any, lon1: any, lat2: any, lon2: any, frequency: any, distance: number, polarization : string, worstmonth:boolean) => {
191     if(!worstmonth){
192       fetch(BASE_URL+'/rain/annual/' + lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2 + '/' + frequency+ '/'+ distance + '/' + polarization.toUpperCase())
193       .then(res => res.json())
194       .then(result => { this.props.UpdateRainAtt(result.rainAttenuation) ; this.props.updateRainValue(result.rainFall.rainrate)})
195     }
196     else {
197       fetch(BASE_URL+'/rain/worstmonth/' + lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2 + '/' + frequency+ '/'+ distance + '/' + polarization.toUpperCase())
198       .then(res => res.json())
199       .then(result => { this.props.UpdateRainAtt(result.rainAttenuation) ; this.props.updateRainValue(result.rainFall.rainrate); this.props.UpdateWorstMonthRain (result.rainFall.period);  this.setState({showWM: '- Wm is : '})})
200     }
201   }
202
203
204   manualRain = (rainfall: number, frequency: number, distance:number, polarization : string) => {
205     fetch(BASE_URL+'/rain/' + rainfall + '/' + frequency + '/' + distance+ '/' + polarization.toUpperCase())
206       .then(res => res.json())
207       .then(result => { this.props.specificRain(result.rainAttenuation) })
208     }
209
210
211     FSL = (distance:number, frequency:number) => {
212       fetch(BASE_URL+'/fsl/' + distance + '/' + frequency)
213       .then(res=>res.json())
214       .then (result => {this.props.FSL(result.fspl)})
215     }
216   
217     AbsorptionAtt =(lat1: number, lon1: number, lat2: number, lon2: number, distance:number, frequency:number, worstmonth:boolean, absorptionMethod : string) => {
218       if(!worstmonth)
219       {
220       fetch(BASE_URL+'/absorption/annual/' +lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2 + '/' +  distance + '/' + frequency + '/' +absorptionMethod)
221       .then(res=>res.json())
222       .then (result => {this.props.UpdateAbsorption(result.oxygenLoss, result.waterLoss)})
223       }
224       else {
225        fetch(BASE_URL+'/absorption/annual/' +lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2 + '/' +  distance + '/' + frequency + '/' +absorptionMethod)
226       .then(res=>res.json())
227       .then (result => {this.props.UpdateAbsorption(result.oxygenLoss, result.waterLoss)})
228       }
229     }
230
231     formValid = () => {
232       
233       this.props.lat1 === 0 ? this.setState({latitude1Error: 'Enter a number between -90 to 90'}) : null
234       this.props.lat2 === 0 ? this.setState({latitude2Error: 'Enter a number between -90 to 90'}) : null
235       this.props.lon1 === 0 ? this.setState({longitude1Error: 'Enter a number between -180 to 180' }) : null
236       this.props.lon2 === 0 ? this.setState({longitude2Error: 'Enter a number between -180 to 180' }) : null
237       this.props.frequency === 0 ? this.setState({frequencyError: 'Select a frequency' }) : this.setState({frequencyError: ''})
238       
239       this.state.rainMethodDisplay === null  && this.props.rainVal === 0 ? this.setState({rainMethodError: 'Select the rain method'}) : this.setState({rainMethodError: ''})
240       this.state.absorptionMethod === '0' ? this.setState({attenuationMethodError: 'Select the attenuation method'}) : this.setState({attenuationMethodError: ''})
241       console.log(this.state);
242       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=== '');
243
244       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=== '';
245
246      }
247   
248
249     
250   buttonHandler = async () => {
251    
252      if (this.formValid()) {
253
254       this.props.updateAutoDistance(await this.updateAutoDistance(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2))
255       this.FSL(this.props.distance, this.props.frequency)
256
257       if (this.state.worstmonth===false) {
258         this.setState({showWM : ' '})
259         this.props.UpdateWorstMonthRain('')
260         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)
261
262         if (this.state.rainMethodDisplay === true){
263
264           this.manualRain(this.props.rainVal, this.props.frequency, this.props.distance, this.props.polarization!); 
265         }
266         else {
267           // this.updateRainValue(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.state.worstmonth)
268           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);
269         }
270       } 
271       else { 
272         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)
273
274           // this.updateRainValue(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.state.worstmonth)
275
276           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);
277       }
278     }
279       else console.log('form is not valid')
280
281   }
282
283   componentDidMount = () => {
284     fetch (BASE_URL+'/fsl/1/1')
285     .then(res => {if (res.ok) { this.props.UpdateConectivity(true)}else {this.props.UpdateConectivity(false)} })
286     .catch (res => {this.props.UpdateConectivity(false)} )
287   }
288
289   handleChange =(e:any) => {
290     
291     switch (e.target.id){
292     case 'Lat1' : if ( e.target.value >90 || e.target.value<-90 )
293     { this.setState({latitude1Error: 'Enter a number between -90 to 90'})}
294     else {this.updateLatLon(e)
295        this.setState({latitude1Error: ''}) }
296     break;
297     case 'Lat2' : if ( e.target.value >90 || e.target.value<-90 )
298     { this.setState({latitude2Error: 'Enter a number between -90 to 90'})}
299     else {this.updateLatLon(e)
300        this.setState({latitude2Error: ''}) }
301     break;
302     case 'Lon1' : if ( e.target.value >180 || e.target.value<-180 )
303     { this.setState({longitude1Error: 'Enter a number between -180 to 180'})}
304     else {this.updateLatLon(e)
305        this.setState({longitude1Error: ''}) }
306     break;
307     case 'Lon2' : if ( e.target.value >180 || e.target.value<-180 )
308     { this.setState({longitude2Error: 'Enter a number between -180 to 180'})}
309     else {this.updateLatLon(e)
310        this.setState({longitude2Error: ''}) }
311     break;
312    
313     }
314   }
315
316     render() {
317     return (
318     
319     <div >
320       
321       {!this.props.formView && 
322
323         <div className = 'parent'>
324
325           <div >Site A
326           <form >
327           <label>Latitude:  <input aria-label="site-a-latitude-input" className={this.state.latitude1Error.length>0 ? 'error' : 'input'} id='Lat1' type='number' onChange={(e: any) => {this.handleChange(e)} }/></label>
328           <div style={{fontSize:12, color:'red'}}> {this.state.latitude1Error}  </div></form> 
329           <form><label>Longitude: <input aria-label="site-a-longitude-input" className={this.state.longitude1Error.length>0 ? 'error' : 'input'} id='Lon1' type='number' onChange={(e: any) => this.handleChange(e) } /></label><div style={{fontSize:12, color:'red'}}> {this.state.longitude1Error} </div>
330           </form> 
331           </div>
332           
333           <div>Site B
334           <form>
335           <label>Latitude: <input aria-label="site-b-latitude-input" className={this.state.latitude2Error.length>0 ? 'error' : 'input'} id='Lat2' type='number' onChange={(e: any) => {this.handleChange(e) }} /></label><div style={{fontSize:12, color:'red'}}> {this.state.latitude2Error} </div></form>
336           <form><label>Longitude: <input aria-label="site-b-longitude-input" className={this.state.longitude2Error.length>0 ? 'error' : 'input'}  id='Lon2' type='number' onChange={(e: any) => {this.handleChange(e) } }/></label><div style={{fontSize:12, color:'red'}}> {this.state.longitude2Error} </div></form>
337           </div>
338           
339           </div>
340         }
341
342
343         <div className='container-1'>
344           <div>{<form><input aria-label="annual" type='checkbox' id='Annual' value ="Annual" checked= {this.state.worstmonth===false} onClick= {(e: any) => this.setState ({worstmonth: false})}></input>Annual
345                       <input aria-label="worst-month" style={{marginLeft:10}} type='checkbox' id='Worst Month' value ="Worst" checked= {this.state.worstmonth===true} onClick= {(e:any)=>this.setState ({worstmonth: true})}></input>WM</form>}</div>
346
347
348           <div className='column1'>
349             <div>&nbsp;</div>
350           {(this.props.siteA.length>0 || this.props.siteB.length>0)  && <div >Site Name</div>}
351           <div>Latitude</div>
352           <div>Longitude</div>
353           <div>Azimuth</div>
354           <div>Average Mean Sea Level</div>
355           <div>Antenna Height Above Ground</div>
356           <div>Distance</div>
357           <div style={{marginTop:20}}>Polarization</div>
358           <div style={{marginTop:20}}>Frequency</div>
359           <div>Free Space Loss</div>
360           <div style={{marginTop:10}}>Rain Model</div>
361           <div style={{marginTop:20}}>Rainfall Rate</div>
362           <div>Rain Loss</div>
363           <div style={{marginTop:18}}>Absorption Model</div>
364           <div>Oxygen Specific Attenuation</div>
365           <div>Water Vapor Specific Attenuation</div>
366           </div>
367         
368
369           <div className='middlecolumn'>
370           <div  >Site A</div>
371           {this.props.siteA.length>0 &&<div> {this.props.siteA }</div>}
372           <div aria-label="site-a-latitude-dms"> {this.props.lat1 && this.LatLonToDMS(this.props.lat1)}</div>
373           <div aria-label="site-a-longitude-dms">{this.props.lon1 && this.LatLonToDMS(this.props.lon1)}</div>
374           <div>0</div>
375           <div aria-label="site-a-amsl">{this.props.amslA.toFixed(2)} m</div>
376           <div aria-label="site-a-antenna-amsl">{this.props.aglA.toFixed(2)} m</div>
377         
378
379           <div className='column2'>
380           <div aria-label="distance-between-sites">{this.props.distance?.toFixed(3)} km</div>
381           <div>{<form><input aria-label="polarization-horizontal" type='checkbox' id='Horizontal' value ="Horizontal" checked= {this.props.polarization==='Horizontal'} onClick= {(e: any) => this.props.updatePolarization(e.target.value)}></input>Horizontal
382                       <input aria-label="polarization-vertical" style={{marginLeft:10}} type='checkbox' id='Vertical' value ="Vertical" checked= {this.props.polarization==='Vertical'} onClick= {(e:any)=>{this.props.updatePolarization(e.target.value)}}></input>Vertical</form>}</div>
383           
384               <div> {<select aria-label="select-frequency-in-ghz" className={this.state.frequencyError.length>0 ? 'error' : 'input'}  onChange={(e) => { this.props.updateFrequency(Number(e.target.value)); e.target.value==='0'? this.setState({frequencyError: 'select a frequency'}): this.setState({frequencyError:''})}}> 
385                       
386                       <option value='0' aria-label="none-value" >Select Freq</option>
387                       <option value='7' aria-label="7" >7 GHz</option>
388                       <option value='11' aria-label="11" >11 GHz</option>
389                       <option value='15' aria-label="15" >15 GHz</option>
390                       <option value='23' aria-label="23">23 GHz</option>
391                       <option value='26' aria-label="26">26 GHz</option>
392                       <option value='28' aria-label="28">28 GHz</option>
393                       <option value='38' aria-label="38">38 GHz</option>
394                       <option value='42' aria-label="42">42 GHz</option>
395                       <option value='80' aria-label="80">80 GHz</option>
396                       </select>} <div style={{fontSize:12, color:'red'}}>  {this.state.frequencyError} </div> </div> 
397
398           <div aria-label="fspl-value">{this.props.fsl.toFixed(3)} dB</div>
399
400           <div> {<select aria-label="select-rain-method" className={this.state.rainMethodError.length>0 ? 'error' : 'input'} onChange = {(e) => {e.target.value === 'itu' ? this.setState({ rainMethodDisplay: false}):this.setState({ rainMethodDisplay: true}); e.target.value==='0'? this.setState({rainMethodError: 'select a Rain model'}): this.setState({rainMethodError:''}) }}>
401                         <option value='0' aria-label="none-value" >Select Rain Method</option>
402                         <option value='itu' aria-label="itur8377">ITU-R P.837-7</option>
403                         <option value='manual' aria-label="manual-entry">Specific Rain</option>
404                         </select>} <div style={{fontSize:12,color:'red'}}>{this.state.rainMethodError}</div>
405             </div> 
406             <div> {<form><input aria-label="rain-value" type="number" style={{ width: 70, height: 15, fontSize: 14 }} onChange={(e) => { this.props.updateRainValue(Number(e.target.value)) }}
407                     value={this.props.rainVal} disabled={this.state.rainMethodDisplay === false ? true : false}>
408                     </input>  mm/hr  {this.state.showWM} {this.props.month}</form> } </div>
409           <div aria-label="rain-attenuation-value">{this.props.rainAtt.toFixed(3)} dB</div>
410
411           <div> {<select aria-label="select-absorption-method" className={this.state.attenuationMethodError.length>0 ? 'error' : 'input'} onChange = {(e) => { if (e.target.value!== ''){ this.setState({absorptionMethod : e.target.value}); this.setState({attenuationMethodError:''})  }}}>
412                         <option value='0' aria-label="none-value" >Select Absorption Method</option>
413                         <option value='ITURP67612' aria-label="iturp67612" >ITU-R P.676-12</option>
414                         <option value='ITURP67611' aria-label="iturp67611"  >ITU-R P.676-11</option>
415                         <option value='ITURP67610' aria-label="iturp67610" >ITU-R P.676-10</option>
416                         </select>} <div style={{fontSize:12,color:'red'}}>{this.state.attenuationMethodError}</div>
417             </div> 
418           <div aria-label="absorption-oxygen-value">{this.props.absorptionOxygen.toFixed(3)} dB</div>
419           <div aria-label="absorption-water-value">{this.props.absorptionWater.toFixed(3)} dB</div>
420           <div>{<button aria-label="calculate-button" style={{color: '#222', fontFamily:'Arial', boxAlign: 'center', display:'inline-block', insetInlineStart: '20' , alignSelf:'center' }}
421                     onClick = {(e) => this.buttonHandler()} >Calculate</button>} </div>
422
423
424           </div>
425           </div>
426           <div className= 'middlecolumn'>
427           <div  >Site B</div>
428           {this.props.siteB.length>0 &&<div> {this.props.siteB}</div>}
429           <div aria-label="site-b-latitude-dms"> {this.props.lat2 && this.LatLonToDMS(this.props.lat2)}</div>
430           <div aria-label="site-b-longitude-dms">{this.props.lon2 && this.LatLonToDMS(this.props.lon2)}</div>
431           <div>0</div>
432           <div aria-label="site-b-asml">{this.props.amslB.toFixed(2)} m</div>
433           <div aria-label="site-b-antenna-asml">{this.props.aglB.toFixed(2)} m</div>
434
435           
436           </div>
437
438
439         </div>
440
441         
442         <ConnectionInfo />
443         
444         
445         
446         </div>
447       )
448     }
449     
450   }
451
452 export default connect(mapProps, mapDispatch)(LinkCalculation);