diff --git a/projects/planner/src/app/app.module.ts b/projects/planner/src/app/app.module.ts index 6c23e782..2c83f443 100644 --- a/projects/planner/src/app/app.module.ts +++ b/projects/planner/src/app/app.module.ts @@ -1,94 +1,99 @@ -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; -import { ReactiveFormsModule } from '@angular/forms'; -import { ServiceWorkerModule } from '@angular/service-worker'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; -import { DatePipe, DecimalPipe } from '@angular/common'; +import {BrowserModule} from '@angular/platform-browser'; +import {NgModule} from '@angular/core'; +import {ReactiveFormsModule} from '@angular/forms'; +import {ServiceWorkerModule} from '@angular/service-worker'; +import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; +import {FontAwesomeModule} from '@fortawesome/angular-fontawesome'; +import {DatePipe, DecimalPipe} from '@angular/common'; -import { ClipboardModule } from 'ngx-clipboard'; -import { MdbCollapseModule } from 'mdb-angular-ui-kit/collapse'; -import { MdbDropdownModule } from 'mdb-angular-ui-kit/dropdown'; -import { MdbFormsModule } from 'mdb-angular-ui-kit/forms'; -import { MdbTabsModule } from 'mdb-angular-ui-kit/tabs'; -import { MdbAccordionModule } from 'mdb-angular-ui-kit/accordion'; +import {ClipboardModule} from 'ngx-clipboard'; +import {MdbCollapseModule} from 'mdb-angular-ui-kit/collapse'; +import {MdbDropdownModule} from 'mdb-angular-ui-kit/dropdown'; +import {MdbFormsModule} from 'mdb-angular-ui-kit/forms'; +import {MdbTabsModule} from 'mdb-angular-ui-kit/tabs'; +import {MdbAccordionModule} from 'mdb-angular-ui-kit/accordion'; -import { environment } from '../environments/environment'; -import { AppRoutingModule } from './app-routing.module'; -import { AppComponent } from './app.component'; -import { TanksSimpleComponent } from './tanks-simple/tanks-simple.component'; -import { TanksComplexComponent } from './tanks-complex/tanks-complex.component'; -import { DiverComponent } from './diver/diver.component'; -import { DiveOptionsComponent } from './diveoptions/diveoptions.component'; -import { DiveInfoComponent } from './diveinfo/diveinfo.component'; -import { MainMenuComponent } from './mainmenu/mainmenu.component'; -import { DashboardComponent } from './dashboard/dashboard.component'; -import { GaslabelComponent } from './gaslabel/gaslabel.component'; -import { SacComponent } from './sac/sac.component'; -import { NitroxComponent } from './nitrox/nitrox.component'; -import { WayPointsComponent } from './waypoints/waypoints.component'; -import { ProfileChartComponent } from './profilechart/profilechart.component'; -import { AboutComponent } from './about/about.component'; -import { AppFooterComponent } from './footer/footer.component'; -import { DepthsSimpleComponent } from './depths-simple/depths-simple.component'; -import { DepthsComplexComponent } from './depths-complex/depths-complex.component'; -import { TankChartComponent } from './tank-chart/tank-chart.component'; -import { AppSettingsComponent } from './app-settings/app-settings.component'; -import { CalculatingComponent } from './calculating/calculating.component'; -import { NdlLimitsComponent } from './ndl-limits/ndl-limits.component'; -import { SalinityComponent } from './salinity/salinity.component'; -import { AltitudeComponent } from './altitude/altitude.component'; -import { GradientsComponent } from './gradients/gradients.component'; -import { DepthComponent } from './depth/depth.component'; -import { OxygenComponent } from './oxygen/oxygen.component'; +import {environment} from '../environments/environment'; +import {AppRoutingModule} from './app-routing.module'; +import {AppComponent} from './app.component'; +import {TanksSimpleComponent} from './tanks-simple/tanks-simple.component'; +import {TanksComplexComponent} from './tanks-complex/tanks-complex.component'; +import {DiverComponent} from './diver/diver.component'; +import {DiveOptionsComponent} from './diveoptions/diveoptions.component'; +import {DiveInfoComponent} from './diveinfo/diveinfo.component'; +import {MainMenuComponent} from './mainmenu/mainmenu.component'; +import {DashboardComponent} from './dashboard/dashboard.component'; +import {GaslabelComponent} from './gaslabel/gaslabel.component'; +import {SacComponent} from './sac/sac.component'; +import {NitroxComponent} from './nitrox/nitrox.component'; +import {WayPointsComponent} from './waypoints/waypoints.component'; +import {ProfileChartComponent} from './profilechart/profilechart.component'; +import {AboutComponent} from './about/about.component'; +import {AppFooterComponent} from './footer/footer.component'; +import {DepthsSimpleComponent} from './depths-simple/depths-simple.component'; +import {DepthsComplexComponent} from './depths-complex/depths-complex.component'; +import {TankChartComponent} from './tank-chart/tank-chart.component'; +import {AppSettingsComponent} from './app-settings/app-settings.component'; +import {CalculatingComponent} from './calculating/calculating.component'; +import {NdlLimitsComponent} from './ndl-limits/ndl-limits.component'; +import {SalinityComponent} from './salinity/salinity.component'; +import {AltitudeComponent} from './altitude/altitude.component'; +import {GradientsComponent} from './gradients/gradients.component'; +import {DepthComponent} from './depth/depth.component'; +import {OxygenComponent} from './oxygen/oxygen.component'; -import { DurationPipe } from './pipes/duration.pipe'; -import { PlannerService } from './shared/planner.service'; -import { PreferencesStore } from './shared/preferencesStore'; -import { UnitConversion } from './shared/UnitConversion'; -import { SelectedWaypoint } from './shared/selectedwaypointService'; -import { WorkersFactory } from './shared/workers.factory'; -import { WorkersFactoryCommon } from './shared/serial.workers.factory'; -import { NdlService } from './shared/ndl.service'; -import { OptionsService } from './shared/options.service'; -import { PpO2Component } from './pp-o2/pp-o2.component'; -import { DelayedScheduleService } from './shared/delayedSchedule.service'; -import { AppinfoComponent } from './appinfo/appinfo.component'; -import { DiveIssuesComponent } from './dive-issues/dive-issues.component'; -import { InputControls } from './shared/inputcontrols'; -import { ValidatorGroups } from './shared/ValidatorGroups'; -import { DepthsService } from './shared/depths.service'; -import { TanksService } from './shared/tanks.service'; -import { SacCalculatorService } from './shared/sac-calculator.service'; -import { NitroxCalculatorService } from './shared/nitrox-calculator.service'; -import { SettingsNormalizationService } from './shared/settings-normalization.service'; -import { ViewSwitchService } from './shared/viewSwitchService'; -import { OxygenDropDownComponent } from './oxygen-dropdown/oxygen-dropdown.component'; -import { Preferences } from './shared/preferences'; -import { PlanUrlSerialization } from './shared/PlanUrlSerialization'; -import { WayPointsService } from './shared/waypoints.service'; -import { PlanTabsComponent } from './plan.tabs/plan.tabs.component'; -import { TankSizeComponent } from './tank.size/tank.size.component'; -import { StopsFilter } from './shared/stopsFilter.service'; -import { ViewStates } from './shared/viewStates'; -import { Urls } from './shared/navigation.service'; -import { SubViewStorage } from './shared/subViewStorage'; -import { DashboardStartUp } from './shared/startUp'; -import { AltitudeCalcComponent } from './altitude-calc/altitude-calc.component'; -import { WeightCalcComponent } from './weight/weight.component'; -import { GasPropertiesCalcComponent } from './gas.props/gas.props.component'; -import { DiffComponent } from './diff/diff.component'; -import { DiveResults } from './shared/diveresults'; -import { DiveSchedules } from './shared/dive.schedules'; -import { RedundanciesComponent } from './redundancies/redundancies.component'; -import { RedundanciesService } from './shared/redundancies.service'; -import { ReloadDispatcher } from './shared/reloadDispatcher'; -import { ManagedDiveSchedules } from './shared/managedDiveSchedules'; -import { WaypointsDifferenceComponent } from './diff/waypoints/diff-waypoints.component'; -import { DiveInfoDifferenceComponent } from './diff/diveinfo/diff-diveinfo.component'; -import { ProfileDifferenceChartComponent } from './diff/profilechart/diff-profilechart.component'; -import { MaskitoModule } from '@maskito/angular'; -import { SurfaceIntervalComponent } from './surface-interval/surface-interval.component'; +import {DurationPipe} from './pipes/duration.pipe'; +import {PlannerService} from './shared/planner.service'; +import {PreferencesStore} from './shared/preferencesStore'; +import {UnitConversion} from './shared/UnitConversion'; +import {SelectedWaypoint} from './shared/selectedwaypointService'; +import {WorkersFactory} from './shared/workers.factory'; +import {WorkersFactoryCommon} from './shared/serial.workers.factory'; +import {NdlService} from './shared/ndl.service'; +import {OptionsService} from './shared/options.service'; +import {PpO2Component} from './pp-o2/pp-o2.component'; +import {DelayedScheduleService} from './shared/delayedSchedule.service'; +import {AppinfoComponent} from './appinfo/appinfo.component'; +import {DiveIssuesComponent} from './dive-issues/dive-issues.component'; +import {InputControls} from './shared/inputcontrols'; +import {ValidatorGroups} from './shared/ValidatorGroups'; +import {DepthsService} from './shared/depths.service'; +import {TanksService} from './shared/tanks.service'; +import {SacCalculatorService} from './shared/sac-calculator.service'; +import {NitroxCalculatorService} from './shared/nitrox-calculator.service'; +import {SettingsNormalizationService} from './shared/settings-normalization.service'; +import {ViewSwitchService} from './shared/viewSwitchService'; +import {OxygenDropDownComponent} from './oxygen-dropdown/oxygen-dropdown.component'; +import {Preferences} from './shared/preferences'; +import {PlanUrlSerialization} from './shared/PlanUrlSerialization'; +import {WayPointsService} from './shared/waypoints.service'; +import {PlanTabsComponent} from './plan.tabs/plan.tabs.component'; +import {TankSizeComponent} from './tank.size/tank.size.component'; +import {StopsFilter} from './shared/stopsFilter.service'; +import {ViewStates} from './shared/viewStates'; +import {Urls} from './shared/navigation.service'; +import {SubViewStorage} from './shared/subViewStorage'; +import {DashboardStartUp} from './shared/startUp'; +import {AltitudeCalcComponent} from './altitude-calc/altitude-calc.component'; +import {WeightCalcComponent} from './weight/weight.component'; +import {GasPropertiesCalcComponent} from './gas.props/gas.props.component'; +import {DiffComponent} from './diff/diff.component'; +import {DiveResults} from './shared/diveresults'; +import {DiveSchedules} from './shared/dive.schedules'; +import {RedundanciesComponent} from './redundancies/redundancies.component'; +import {RedundanciesService} from './shared/redundancies.service'; +import {ReloadDispatcher} from './shared/reloadDispatcher'; +import {ManagedDiveSchedules} from './shared/managedDiveSchedules'; +import {WaypointsDifferenceComponent} from './diff/waypoints/diff-waypoints.component'; +import {DiveInfoDifferenceComponent} from './diff/diveinfo/diff-diveinfo.component'; +import {ProfileDifferenceChartComponent} from './diff/profilechart/diff-profilechart.component'; +import {MaskitoModule} from '@maskito/angular'; +import {SurfaceIntervalComponent} from './surface-interval/surface-interval.component'; +import {TestDataInjector} from './diff/testData/testDataInjector'; +import {ProfileComparatorService} from './shared/profileComparatorService'; +import {LoadTestDataComponent} from './diff/testData/loadtestdata/loadtestdata.component'; +import {WaypointsDifferenceService} from './shared/waypoints-difference.service'; +import {DiveInfoResultsDifferenceComponent} from './diff/diveinfo/results/diff-diveinfo-results.component'; const ANGULAR_MODULES = [ AppRoutingModule, @@ -148,7 +153,9 @@ const COMPONENTS = [ WeightCalcComponent, WaypointsDifferenceComponent, DiveInfoDifferenceComponent, - ProfileDifferenceChartComponent + ProfileDifferenceChartComponent, + LoadTestDataComponent, + DiveInfoResultsDifferenceComponent ]; const SERVICES = [ @@ -182,7 +189,10 @@ const SERVICES = [ ValidatorGroups, ViewStates, ViewSwitchService, - WayPointsService + WayPointsService, + TestDataInjector, + ProfileComparatorService, + WaypointsDifferenceService ]; @NgModule({ diff --git a/projects/planner/src/app/diff/diff.component.html b/projects/planner/src/app/diff/diff.component.html index f815edde..16177a4d 100644 --- a/projects/planner/src/app/diff/diff.component.html +++ b/projects/planner/src/app/diff/diff.component.html @@ -1,14 +1,15 @@ + +
-
+
-
- +
+
-
+
- diff --git a/projects/planner/src/app/diff/diff.component.spec.ts b/projects/planner/src/app/diff/diff.component.spec.ts index 62b41587..e05de204 100644 --- a/projects/planner/src/app/diff/diff.component.spec.ts +++ b/projects/planner/src/app/diff/diff.component.spec.ts @@ -1,6 +1,10 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; -import { DiffComponent } from './diff.component'; +import {DiffComponent} from './diff.component'; +import {ProfileComparatorService} from '../shared/profileComparatorService'; +import {DiveSchedules} from '../shared/dive.schedules'; +import {UnitConversion} from '../shared/UnitConversion'; +import {ReloadDispatcher} from '../shared/reloadDispatcher'; describe('DiffComponent', () => { let component: DiffComponent; @@ -8,7 +12,13 @@ describe('DiffComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ - declarations: [DiffComponent] + declarations: [DiffComponent], + providers: [ + ProfileComparatorService, + DiveSchedules, + UnitConversion, + ReloadDispatcher + ] }); fixture = TestBed.createComponent(DiffComponent); component = fixture.componentInstance; diff --git a/projects/planner/src/app/diff/diff.component.ts b/projects/planner/src/app/diff/diff.component.ts index 9212d9ed..df2d4f3d 100644 --- a/projects/planner/src/app/diff/diff.component.ts +++ b/projects/planner/src/app/diff/diff.component.ts @@ -1,55 +1,5 @@ import {Component} from '@angular/core'; -import {WayPointsService} from '../shared/waypoints.service'; -import {UnitConversion} from '../shared/UnitConversion'; -import {Segments, StandardGases, Tank, Time} from 'scuba-physics'; -import {WayPoint} from '../shared/models'; - -export class TestData { - public readonly wayPointsA: WayPoint[]; - public readonly tanksA: Tank[]; - - public readonly wayPointsB: WayPoint[]; - public readonly tanksB: Tank[]; - - constructor() { - const units = new UnitConversion(); - const waypointService = new WayPointsService(units); - - this.tanksA = [ - new Tank(24, 200, 21), - new Tank(11, 200, 50), - new Tank(11, 150, 100), - ]; - this.tanksA[0].consumed = 120; - this.tanksA[1].consumed = 80; - this.tanksA[2].consumed = 60; - - const segmentsA = new Segments(); - segmentsA.add(0, 40, StandardGases.air, Time.oneMinute * 2); - segmentsA.add(40, 40, StandardGases.air, Time.oneMinute * 10); - segmentsA.add(40, 21, StandardGases.air, Time.oneMinute * 2); - segmentsA.add(21, 21, StandardGases.ean50, Time.oneMinute); - segmentsA.add(21, 3, StandardGases.ean50, Time.oneMinute * 3); - segmentsA.add(3, 3, StandardGases.oxygen, Time.oneMinute * 6); - segmentsA.add(3, 0, StandardGases.oxygen, Time.oneMinute); - this.wayPointsA = waypointService.calculateWayPoints(segmentsA.items); - - this.tanksB = [ - new Tank(24, 200, 21), - new Tank(11, 200, 50) - ]; - this.tanksB[0].consumed = 90; - this.tanksB[1].consumed = 40; - - const segmentsB = new Segments(); - segmentsB.add(0, 30, StandardGases.air, Time.oneMinute * 4); - segmentsB.add(30, 30, StandardGases.air, Time.oneMinute * 10); - segmentsB.add(30, 15, StandardGases.air, Time.oneMinute * 3); - segmentsB.add(15, 15, StandardGases.ean50, Time.oneMinute); - segmentsB.add(15, 0, StandardGases.ean50, Time.oneMinute * 2); - this.wayPointsB = waypointService.calculateWayPoints(segmentsB.items); - } -} +import {ProfileComparatorService} from '../shared/profileComparatorService'; @Component({ selector: 'app-diff', @@ -57,5 +7,6 @@ export class TestData { styleUrls: ['./diff.component.scss'] }) export class DiffComponent { - public testData = new TestData(); + constructor(public profileComparatorService: ProfileComparatorService) { + } } diff --git a/projects/planner/src/app/diff/diveinfo/diff-diveinfo.component.html b/projects/planner/src/app/diff/diveinfo/diff-diveinfo.component.html index e10fdb3b..073a858e 100644 --- a/projects/planner/src/app/diff/diveinfo/diff-diveinfo.component.html +++ b/projects/planner/src/app/diff/diveinfo/diff-diveinfo.component.html @@ -1 +1,15 @@ - \ No newline at end of file +
+
+
+ + Dive Results +
+
+ +
+
+ +
+
+
+ diff --git a/projects/planner/src/app/diff/diveinfo/diff-diveinfo.component.spec.ts b/projects/planner/src/app/diff/diveinfo/diff-diveinfo.component.spec.ts index 103b1275..26ef20cc 100644 --- a/projects/planner/src/app/diff/diveinfo/diff-diveinfo.component.spec.ts +++ b/projects/planner/src/app/diff/diveinfo/diff-diveinfo.component.spec.ts @@ -1,6 +1,6 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; -import { DiveInfoDifferenceComponent } from './diff-diveinfo.component'; +import {DiveInfoDifferenceComponent} from './diff-diveinfo.component'; describe('DiveInfoDifferenceComponent', () => { let component: DiveInfoDifferenceComponent; diff --git a/projects/planner/src/app/diff/diveinfo/diff-diveinfo.component.ts b/projects/planner/src/app/diff/diveinfo/diff-diveinfo.component.ts index 06d7e2f7..675e6092 100644 --- a/projects/planner/src/app/diff/diveinfo/diff-diveinfo.component.ts +++ b/projects/planner/src/app/diff/diveinfo/diff-diveinfo.component.ts @@ -1,4 +1,5 @@ -import { Component } from '@angular/core'; +import {Component} from '@angular/core'; +import {faSlidersH} from '@fortawesome/free-solid-svg-icons'; @Component({ selector: 'app-diff-diveinfo', @@ -6,5 +7,5 @@ import { Component } from '@angular/core'; styleUrls: ['./diff-diveinfo.component.scss'] }) export class DiveInfoDifferenceComponent { - + public icon = faSlidersH; } diff --git a/projects/planner/src/app/diff/diveinfo/results/diff-diveinfo-results.component.html b/projects/planner/src/app/diff/diveinfo/results/diff-diveinfo-results.component.html new file mode 100644 index 00000000..c86aace8 --- /dev/null +++ b/projects/planner/src/app/diff/diveinfo/results/diff-diveinfo-results.component.html @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Profile A
Profile B
+ Difference +
Total dive time [min]: +
+ {{ profileA.totalDuration | duration:profileA.totalDuration}} +
+
+
+ {{ profileB.totalDuration | duration:profileB.totalDuration}} +
+
+
+ + {{ totalDurationDifference | duration:totalDurationDifference}} + + +
+
Time to surface [min]: +
+ {{ profileA.timeToSurface | number:'1.0-0' }} +
+
+
+ {{ profileB.timeToSurface | number:'1.0-0' }} +
+
+
+ + {{ timeToSurfaceDifference | number:'1.0-0' }} + + +
+
Average depth [{{units.length}}]: +
+ {{ averageDepthOfProfile(profileA) | number:'1.0-1' }} +
+
+
+ {{ averageDepthOfProfile(profileB) | number:'1.0-1' }} +
+
+
+ + {{ averageDepthDifference | number:'1.0-1' }} + + +
+
Rock bottom at [min]: +
+ {{ profileA.emergencyAscentStart | duration:profileA.emergencyAscentStart }} +
+
+
+ {{ profileB.emergencyAscentStart | duration:profileB.emergencyAscentStart }} +
+
+
+ + {{ emergencyAscentStartDifference | duration:emergencyAscentStartDifference}} + + +
+
No decompression time [min]: +
+ {{ noDecoOfProfile(profileA) | number:'1.0-0' }} +
+
+
+ {{ noDecoOfProfile(profileB) | number:'1.0-0' }} +
+
+
+ + {{ noDecoDifference | number:'1.0-0' }} + + +
+
Maximum bottom time [min]: +
+ {{ profileA.maxTime | number:'1.0-0' }} +
+
+
+ {{ profileB.maxTime | number:'1.0-0' }} +
+
+
+ + {{ maxTimeDifference | number:'1.0-0' }} + + +
+
Highest gas density [{{units.density}}]: +
+ {{ highestDensityOfProfile(profileA) | number:'1.0-2' }} +
+ ({{densityTextOfProfile(profileA)}}) +
+
+
+ {{ highestDensityOfProfile(profileB) | number:'1.0-2' }} +
+ ({{densityTextOfProfile(profileB)}}) +
+
+
+ + {{ highestDensityDifference | number:'1.0-2' }} + + +
+
OTU toxicity [OTU]: +
+ {{ profileA.otu | number:'1.0-0' }} +
+
+
+ {{ profileB.otu | number:'1.0-0' }} +
+
+
+ + {{ otuDifference | number:'1.0-0' }} + + +
+
CNS toxicity [%]: +
+ {{cnsTextOfProfile(profileA)}} +
+
+
+ {{cnsTextOfProfile(profileB)}} +
+
+
+ + {{ cnsDifferenceText }} + + +
+
+ diff --git a/projects/planner/src/app/diff/diveinfo/results/diff-diveinfo-results.component.scss b/projects/planner/src/app/diff/diveinfo/results/diff-diveinfo-results.component.scss new file mode 100644 index 00000000..accf3a1e --- /dev/null +++ b/projects/planner/src/app/diff/diveinfo/results/diff-diveinfo-results.component.scss @@ -0,0 +1,26 @@ +@import "node_modules/mdb-angular-ui-kit/assets/scss/free/variables"; + +fa-icon { + margin-left: 0.5rem !important; + padding-left: 0.4rem; +} + +.delta { + max-width: 4.3rem; +} + +#table-header { + color:$black; + td:nth-child(2){ + background-color: $primary; + } + td:nth-child(3){ + background-color: $blue-gray-50; + } +} + +tr:not(#table-header) { + td:nth-child(n+2){ + border-left-width: medium !important; + } +} diff --git a/projects/planner/src/app/diff/diveinfo/results/diff-diveinfo-results.component.spec.ts b/projects/planner/src/app/diff/diveinfo/results/diff-diveinfo-results.component.spec.ts new file mode 100644 index 00000000..3aadf5b7 --- /dev/null +++ b/projects/planner/src/app/diff/diveinfo/results/diff-diveinfo-results.component.spec.ts @@ -0,0 +1,33 @@ +import {ComponentFixture, TestBed} from '@angular/core/testing'; + +import {DiveInfoResultsDifferenceComponent} from './diff-diveinfo-results.component'; +import {ViewSwitchService} from '../../../shared/viewSwitchService'; +import {DiveSchedules} from '../../../shared/dive.schedules'; +import {ReloadDispatcher} from '../../../shared/reloadDispatcher'; +import {UnitConversion} from '../../../shared/UnitConversion'; +import {ProfileComparatorService} from '../../../shared/profileComparatorService'; + +describe('DiveInfoResultsDifferenceComponent', () => { + let component: DiveInfoResultsDifferenceComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [DiveInfoResultsDifferenceComponent], + providers: [ + ViewSwitchService, + DiveSchedules, + ReloadDispatcher, + UnitConversion, + ProfileComparatorService + ] + }); + fixture = TestBed.createComponent(DiveInfoResultsDifferenceComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/planner/src/app/diff/diveinfo/results/diff-diveinfo-results.component.ts b/projects/planner/src/app/diff/diveinfo/results/diff-diveinfo-results.component.ts new file mode 100644 index 00000000..05827173 --- /dev/null +++ b/projects/planner/src/app/diff/diveinfo/results/diff-diveinfo-results.component.ts @@ -0,0 +1,182 @@ +import {Component} from '@angular/core'; +import {ViewSwitchService} from '../../../shared/viewSwitchService'; +import {UnitConversion} from '../../../shared/UnitConversion'; +import {ProfileComparatorService} from '../../../shared/profileComparatorService'; +import {DiveResults} from '../../../shared/diveresults'; +import {formatNumber} from '@angular/common'; +import {faArrowDown, faArrowUp, faMinus, IconDefinition} from '@fortawesome/free-solid-svg-icons'; +import {Logger} from '../../../shared/Logger'; +import {TextConstants} from '../../../shared/TextConstants'; + +@Component({ + selector: 'app-diff-diveinfo-results', + templateUrl: './diff-diveinfo-results.component.html', + styleUrls: ['./diff-diveinfo-results.component.scss'] +}) +export class DiveInfoResultsDifferenceComponent { + private readonly cnsDifferenceUnderMinusOneThousand = '< -1000'; + + private arrowUp: IconDefinition = faArrowUp; + private arrowDown: IconDefinition = faArrowDown; + private dash: IconDefinition = faMinus; + private rowColorVectorMap: Map = new Map([ + ['totalDuration', 1], + ['timeToSurface', -1], + ['averageDepth', -1], + ['emergencyAscentStart', -1], + ['noDeco', 1], + ['maxTime', 1], + ['highestDensity', -1], + ['otu', -1], + ['cns', -1] + ]); + + constructor( + private viewSwitch: ViewSwitchService, + public units: UnitConversion, + private profileComparatorService: ProfileComparatorService) { + } + + public get profileA(): DiveResults { + return this.profileComparatorService.profileAResults(); + } + + public get profileB(): DiveResults { + return this.profileComparatorService.profileBResults(); + } + + public get areResultsCalculated(): boolean { + return this.profileComparatorService.areResultsCalculated(); + } + + public get areDiveInfosCalculated(): boolean { + return this.profileComparatorService.areDiveInfosCalculated(); + } + + public get areProfilesCalculated(): boolean { + return this.profileComparatorService.areProfilesCalculated(); + } + + public get isComplex(): boolean { + return this.viewSwitch.isComplex; + } + + public get needsReturn(): boolean { + // TODO: Add Profile B + return this.profileA.needsReturn; + } + + public get totalDurationDifference(): number { + return this.profileB.totalDuration - this.profileA.totalDuration; + } + + public get timeToSurfaceDifference(): number { + return this.profileB.timeToSurface - this.profileA.timeToSurface; + } + + public get averageDepthDifference(): number { + return this.averageDepthOfProfile(this.profileB) - this.averageDepthOfProfile(this.profileA); + } + + public get emergencyAscentStartDifference(): number { + return this.profileB.emergencyAscentStart - this.profileA.emergencyAscentStart; + } + + public get noDecoDifference(): number { + return this.noDecoOfProfile(this.profileB) - this.noDecoOfProfile(this.profileA); + } + + public get maxTimeDifference(): number { + return this.profileB.maxTime - this.profileA.maxTime; + } + + public get highestDensityDifference(): number { + return this.highestDensityOfProfile(this.profileB) - this.highestDensityOfProfile(this.profileA); + } + + public get otuDifference(): number { + return this.profileB.otu - this.profileA.otu; + } + + public get cnsDifference(): number { + return this.profileB.cns - this.profileA.cns; + } + public get cnsDifferenceText(): string { + const diff = this.cnsDifference; + if(diff >= 1000) { + return TextConstants.cnsOverOneThousand; + } + + if(diff <= -1000) { + return this.cnsDifferenceUnderMinusOneThousand; + } + + return formatNumber(diff, 'en', '1.0-0'); + } + + public showMaxBottomTimeOfProfile(profile: DiveResults): boolean { + return profile.maxTime > 0; + } + + public noDecoOfProfile(profile: DiveResults): number { + return profile.noDecoTime; + } + + public averageDepthOfProfile(profile: DiveResults): number { + return this.units.fromMeters(profile.averageDepth); + } + + public highestDensityOfProfile(profile: DiveResults): number { + const density = profile.highestDensity.density; + return this.units.fromGramPerLiter(density); + } + + public densityTextOfProfile(profile: DiveResults): string { + const gas = profile.highestDensity.gas.name; + const depth = this.units.fromMeters(profile.highestDensity.depth); + return `${gas} at ${depth} ${this.units.length}`; + } + + public cnsTextOfProfile(profile: DiveResults): string { + if(profile.cns >= 1000) { + return TextConstants.cnsOverOneThousand; + } + + return formatNumber(profile.cns, 'en', '1.0-0'); + } + + public getArrow(difference: number): IconDefinition { + if(difference > 0) { + return this.arrowUp; + } + + if (difference < 0) { + return this.arrowDown; + } + + + return this.dash; + } + + public getBgColor(rowKey: string, value: number): string { + const cssClassOnImprovement = 'table-success'; + const cssClassOnDeterioration = 'table-danger'; + const cssClassOnNoChange = 'table-active'; + + if(!this.rowColorVectorMap.has(rowKey)){ + Logger.warn('Could not find vector for key: ' + rowKey); + } + + const projectedValue = (this.rowColorVectorMap.get(rowKey) ?? 0) * value; + + if (projectedValue > 0){ + return cssClassOnImprovement; + } + + if (projectedValue < 0){ + return cssClassOnDeterioration; + } + + return cssClassOnNoChange; + } +} diff --git a/projects/planner/src/app/diff/testData/TestDataJsonProvider.ts b/projects/planner/src/app/diff/testData/TestDataJsonProvider.ts new file mode 100644 index 00000000..2f6ef24b --- /dev/null +++ b/projects/planner/src/app/diff/testData/TestDataJsonProvider.ts @@ -0,0 +1,518 @@ +export class TestDataJsonProvider { + private diveProfiles: string[] = [ + `{ + "options": { + "gfLow": 0.4, + "gfHigh": 0.85, + "maxPpO2": 1.4, + "maxDecoPpO2": 1.6, + "salinity": 1, + "altitude": 0, + "roundStopsToMinutes": false, + "gasSwitchDuration": 2, + "safetyStop": 2, + "lastStopDepth": 3, + "decoStopDistance": 3, + "minimumAutoStopDepth": 10, + "maxEND": 30, + "oxygenNarcotic": true, + "ascentSpeed6m": 3, + "ascentSpeed50percTo6m": 6, + "ascentSpeed50perc": 9, + "descentSpeed": 18, + "problemSolvingDuration": 1 + }, + "diver": { + "rmv": 20 + }, + "tanks": [ + { + "id": 1, + "size": 15, + "workPressure": 0, + "startPressure": 200, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + } + ], + "plan": [] + }`, + `{ + "options": { + "gfLow": 0.4, + "gfHigh": 0.85, + "maxPpO2": 1.4, + "maxDecoPpO2": 1.6, + "salinity": 1, + "altitude": 0, + "roundStopsToMinutes": false, + "gasSwitchDuration": 2, + "safetyStop": 2, + "lastStopDepth": 3, + "decoStopDistance": 3, + "minimumAutoStopDepth": 10, + "maxEND": 30, + "oxygenNarcotic": true, + "ascentSpeed6m": 3, + "ascentSpeed50percTo6m": 6, + "ascentSpeed50perc": 9, + "descentSpeed": 18, + "problemSolvingDuration": 1 + }, + "diver": { + "rmv": 20 + }, + "tanks": [ + { + "id": 1, + "size": 15, + "workPressure": 0, + "startPressure": 200, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + } + ], + "plan": [ + { + "startDepth": 0, + "endDepth": 10, + "duration": 36, + "tankId": 1, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + }, + { + "startDepth": 10, + "endDepth": 10, + "duration": 3564, + "tankId": 1, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + } + ] + }`, + `{ + "options": { + "gfLow": 0.4, + "gfHigh": 0.85, + "maxPpO2": 1.4, + "maxDecoPpO2": 1.6, + "salinity": 1, + "altitude": 0, + "roundStopsToMinutes": false, + "gasSwitchDuration": 2, + "safetyStop": 2, + "lastStopDepth": 3, + "decoStopDistance": 3, + "minimumAutoStopDepth": 10, + "maxEND": 30, + "oxygenNarcotic": true, + "ascentSpeed6m": 3, + "ascentSpeed50percTo6m": 6, + "ascentSpeed50perc": 9, + "descentSpeed": 18, + "problemSolvingDuration": 1 + }, + "diver": { + "rmv": 20 + }, + "tanks": [ + { + "id": 1, + "size": 24, + "workPressure": 0, + "startPressure": 200, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + }, + { + "id": 2, + "size": 11, + "workPressure": 0, + "startPressure": 200, + "gas": { + "fO2": 0.5, + "fHe": 0 + } + }, + { + "id": 3, + "size": 11, + "workPressure": 0, + "startPressure": 150, + "gas": { + "fO2": 1, + "fHe": 0 + } + } + ], + "plan": [ + { + "startDepth": 0, + "endDepth": 40, + "duration": 120, + "tankId": 1, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + }, + { + "startDepth": 40, + "endDepth": 40, + "duration": 600, + "tankId": 1, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + }, + { + "startDepth": 40, + "endDepth": 21, + "duration": 120, + "tankId": 1, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + }, + { + "startDepth": 21, + "endDepth": 21, + "duration": 60, + "tankId": 2, + "gas": { + "fO2": 0.5, + "fHe": 0 + } + }, + { + "startDepth": 21, + "endDepth": 3, + "duration": 180, + "tankId": 2, + "gas": { + "fO2": 0.5, + "fHe": 0 + } + }, + { + "startDepth": 3, + "endDepth": 3, + "duration": 360, + "tankId": 3, + "gas": { + "fO2": 1, + "fHe": 0 + } + }, + { + "startDepth": 3, + "endDepth": 0, + "duration": 60, + "tankId": 3, + "gas": { + "fO2": 1, + "fHe": 0 + } + } + ] + }`, + `{ + "options": { + "gfLow": 0.4, + "gfHigh": 0.85, + "maxPpO2": 1.4, + "maxDecoPpO2": 1.6, + "salinity": 1, + "altitude": 0, + "roundStopsToMinutes": false, + "gasSwitchDuration": 2, + "safetyStop": 2, + "lastStopDepth": 3, + "decoStopDistance": 3, + "minimumAutoStopDepth": 10, + "maxEND": 30, + "oxygenNarcotic": true, + "ascentSpeed6m": 3, + "ascentSpeed50percTo6m": 6, + "ascentSpeed50perc": 9, + "descentSpeed": 18, + "problemSolvingDuration": 1 + }, + "diver": { + "rmv": 20 + }, + "tanks": [ + { + "id": 1, + "size": 24, + "workPressure": 0, + "startPressure": 200, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + }, + { + "id": 2, + "size": 11.1, + "workPressure": 0, + "startPressure": 200, + "gas": { + "fO2": 1, + "fHe": 0 + } + } + ], + "plan": [ + { + "startDepth": 0, + "endDepth": 40, + "duration": 180, + "tankId": 1, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + }, + { + "startDepth": 40, + "endDepth": 40, + "duration": 1020, + "tankId": 1, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + } + ] + }`, + ` + { + "options": { + "gfLow": 0.4, + "gfHigh": 0.85, + "maxPpO2": 1.4, + "maxDecoPpO2": 1.6, + "salinity": 1, + "altitude": 0, + "roundStopsToMinutes": false, + "gasSwitchDuration": 2, + "safetyStop": 2, + "lastStopDepth": 3, + "decoStopDistance": 3, + "minimumAutoStopDepth": 10, + "maxEND": 30, + "oxygenNarcotic": true, + "ascentSpeed6m": 3, + "ascentSpeed50percTo6m": 6, + "ascentSpeed50perc": 9, + "descentSpeed": 18, + "problemSolvingDuration": 1 + }, + "diver": { + "rmv": 20 + }, + "tanks": [ + { + "id": 1, + "size": 24, + "workPressure": 0, + "startPressure": 200, + "gas": { + "fO2": 0.18, + "fHe": 0.45 + } + }, + { + "id": 2, + "size": 11.1, + "workPressure": 0, + "startPressure": 200, + "gas": { + "fO2": 1, + "fHe": 0 + } + }, + { + "id": 3, + "size": 11.1, + "workPressure": 0, + "startPressure": 200, + "gas": { + "fO2": 0.5, + "fHe": 0 + } + } + ], + "plan": [ + { + "startDepth": 0, + "endDepth": 60, + "duration": 180, + "tankId": 1, + "gas": { + "fO2": 0.18, + "fHe": 0.45 + } + }, + { + "startDepth": 60, + "endDepth": 60, + "duration": 900, + "tankId": 1, + "gas": { + "fO2": 0.18, + "fHe": 0.45 + } + } + ] + }`, + `{ + "options": { + "gfLow": 0.4, + "gfHigh": 0.85, + "maxPpO2": 1.4, + "maxDecoPpO2": 1.6, + "salinity": 1, + "altitude": 0, + "roundStopsToMinutes": false, + "gasSwitchDuration": 2, + "safetyStop": 2, + "lastStopDepth": 3, + "decoStopDistance": 3, + "minimumAutoStopDepth": 10, + "maxEND": 30, + "oxygenNarcotic": true, + "ascentSpeed6m": 3, + "ascentSpeed50percTo6m": 6, + "ascentSpeed50perc": 9, + "descentSpeed": 18, + "problemSolvingDuration": 1 + }, + "diver": { + "rmv": 20 + }, + "tanks": [ + { + "id": 1, + "size": 15, + "workPressure": 0, + "startPressure": 200, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + } + ], + "plan": [ + { + "startDepth": 0, + "endDepth": 40, + "duration": 180, + "tankId": 1, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + }, + { + "startDepth": 40, + "endDepth": 40, + "duration": 300, + "tankId": 1, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + }, + { + "startDepth": 40, + "endDepth": 30, + "duration": 300, + "tankId": 1, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + }, + { + "startDepth": 30, + "endDepth": 30, + "duration": 300, + "tankId": 1, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + }, + { + "startDepth": 30, + "endDepth": 40, + "duration": 300, + "tankId": 1, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + }, + { + "startDepth": 40, + "endDepth": 40, + "duration": 300, + "tankId": 1, + "gas": { + "fO2": 0.209, + "fHe": 0 + } + } + ] + }` + ]; + private preferencesPrefix = ` + { + "options": { + "imperialUnits": false, + "isComplex": true, + "language": "en" + }, + "dives": [`; + private preferencesSuffix = ` + ] + } + `; + public get(profileAIndex: number, profileBIndex: number): string { + + return this.preferencesPrefix + + this.diveProfiles[profileAIndex] + + ',' + this.diveProfiles[profileBIndex] + + this.preferencesSuffix; + } + + public getAll(): string{ + return this.preferencesPrefix + + this.diveProfiles[0] + + ',' + this.diveProfiles[1] + + ',' + this.diveProfiles[2] + + ',' + this.diveProfiles[3] + + ',' + this.diveProfiles[4] + + ',' + this.diveProfiles[5] + + this.preferencesSuffix; + } + + public numberOfProfiles(): number { + return this.diveProfiles.length; + } +} diff --git a/projects/planner/src/app/diff/testData/loadtestdata/loadtestdata.component.html b/projects/planner/src/app/diff/testData/loadtestdata/loadtestdata.component.html new file mode 100644 index 00000000..1c00cd2d --- /dev/null +++ b/projects/planner/src/app/diff/testData/loadtestdata/loadtestdata.component.html @@ -0,0 +1,5 @@ +
+ +
diff --git a/projects/planner/src/app/diff/testData/loadtestdata/loadtestdata.component.scss b/projects/planner/src/app/diff/testData/loadtestdata/loadtestdata.component.scss new file mode 100644 index 00000000..c4e5d4ea --- /dev/null +++ b/projects/planner/src/app/diff/testData/loadtestdata/loadtestdata.component.scss @@ -0,0 +1,9 @@ +@import "node_modules/mdb-angular-ui-kit/assets/scss/free/variables"; + +#loadTestData { + position: absolute; + z-index: $fixed-action-button-zindex; + left: 92vw; + top: 10vh; + width: 5vw; +} diff --git a/projects/planner/src/app/diff/testData/loadtestdata/loadtestdata.component.spec.ts b/projects/planner/src/app/diff/testData/loadtestdata/loadtestdata.component.spec.ts new file mode 100644 index 00000000..b8b778c5 --- /dev/null +++ b/projects/planner/src/app/diff/testData/loadtestdata/loadtestdata.component.spec.ts @@ -0,0 +1,45 @@ +import {ComponentFixture, TestBed} from '@angular/core/testing'; + +import {LoadTestDataComponent} from './loadtestdata.component'; +import {ProfileComparatorService} from '../../../shared/profileComparatorService'; +import {TestDataInjector} from '../testDataInjector'; +import {PreferencesStore} from '../../../shared/preferencesStore'; +import {Preferences} from '../../../shared/preferences'; +import {ViewSwitchService} from '../../../shared/viewSwitchService'; +import {DiveSchedules} from '../../../shared/dive.schedules'; +import {UnitConversion} from '../../../shared/UnitConversion'; +import {ReloadDispatcher} from '../../../shared/reloadDispatcher'; +import {ViewStates} from '../../../shared/viewStates'; +import {PlannerService} from '../../../shared/planner.service'; +import {WorkersFactoryCommon} from '../../../shared/serial.workers.factory'; + +describe('LoadTestDataComponent', () => { + let component: LoadTestDataComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [LoadTestDataComponent], + providers: [ + ProfileComparatorService, + TestDataInjector, + PlannerService, + WorkersFactoryCommon, + PreferencesStore, + Preferences, + ViewSwitchService, + ViewStates, + DiveSchedules, + UnitConversion, + ReloadDispatcher + ] + }); + fixture = TestBed.createComponent(LoadTestDataComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/planner/src/app/diff/testData/loadtestdata/loadtestdata.component.ts b/projects/planner/src/app/diff/testData/loadtestdata/loadtestdata.component.ts new file mode 100644 index 00000000..fadeaa6d --- /dev/null +++ b/projects/planner/src/app/diff/testData/loadtestdata/loadtestdata.component.ts @@ -0,0 +1,22 @@ +import {Component} from '@angular/core'; +import {TestDataInjector} from '../testDataInjector'; +import {DiveSchedules} from '../../../shared/dive.schedules'; + +@Component({ + selector: 'app-loadtestdata', + templateUrl: './loadtestdata.component.html', + styleUrls: ['./loadtestdata.component.scss'] +}) +export class LoadTestDataComponent { + + constructor(public testDataInjector: TestDataInjector, private schedules: DiveSchedules) { + } + + public loadData(){ + this.testDataInjector.injectAllProfiles(); + } + + public isLoaded() { + return this.schedules.length === this.testDataInjector.testProfilesCount; + } +} diff --git a/projects/planner/src/app/diff/testData/testDataInjector.ts b/projects/planner/src/app/diff/testData/testDataInjector.ts new file mode 100644 index 00000000..ba1a0b81 --- /dev/null +++ b/projects/planner/src/app/diff/testData/testDataInjector.ts @@ -0,0 +1,36 @@ +import {Injectable} from '@angular/core'; +import {PreferencesStore} from '../../shared/preferencesStore'; +import {PlannerService} from '../../shared/planner.service'; +import {TestDataJsonProvider} from './TestDataJsonProvider'; + +@Injectable() +export class TestDataInjector { + + private testDataProvider = new TestDataJsonProvider(); + private _testProfilesCount = this.testDataProvider.numberOfProfiles(); + constructor(private preferencesStore: PreferencesStore, private plannerService: PlannerService) { + } + + get testProfilesCount(): number { + return this._testProfilesCount; + } + + // !! ONLY FOR TESTING PURPOSES !! + // Rewrites user defined dive profiles and replaces with 2 pre-defined testing profiles + public injectProfiles(profileAIndex: number, profileBIndex: number){ + const preferencesJson: string = this.testDataProvider.get(profileAIndex, profileBIndex); + localStorage.setItem('preferences', preferencesJson); + this.preferencesStore.load(); + this.plannerService.calculate(1); + this.plannerService.calculate(2); + } + + public injectAllProfiles() { + const preferencesJson: string = this.testDataProvider.getAll(); + localStorage.setItem('preferences', preferencesJson); + this.preferencesStore.load(); + for (let i = 1; i <= this.testDataProvider.numberOfProfiles(); i++){ + this.plannerService.calculate(i); + } + } +} diff --git a/projects/planner/src/app/diff/waypoints/diff-waypoints.component.html b/projects/planner/src/app/diff/waypoints/diff-waypoints.component.html index 8af30b1e..2fbc8158 100644 --- a/projects/planner/src/app/diff/waypoints/diff-waypoints.component.html +++ b/projects/planner/src/app/diff/waypoints/diff-waypoints.component.html @@ -1,12 +1,15 @@ -
+
Dive way points
-
- +
+ + +
@@ -14,29 +17,31 @@ - - - + + + - + diff --git a/projects/planner/src/app/diff/waypoints/diff-waypoints.component.scss b/projects/planner/src/app/diff/waypoints/diff-waypoints.component.scss index e1e07e89..5734b207 100644 --- a/projects/planner/src/app/diff/waypoints/diff-waypoints.component.scss +++ b/projects/planner/src/app/diff/waypoints/diff-waypoints.component.scss @@ -1,4 +1,9 @@ @import "node_modules/mdb-angular-ui-kit/assets/scss/free/variables"; +#body { + margin: 24px; + flex: 1 1 auto; + +} #waypointsDiffTable { .vertical-right-border { border-right-width: medium; diff --git a/projects/planner/src/app/diff/waypoints/diff-waypoints.component.spec.ts b/projects/planner/src/app/diff/waypoints/diff-waypoints.component.spec.ts index f859b4b0..a54be04a 100644 --- a/projects/planner/src/app/diff/waypoints/diff-waypoints.component.spec.ts +++ b/projects/planner/src/app/diff/waypoints/diff-waypoints.component.spec.ts @@ -1,7 +1,10 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {WaypointsDifferenceComponent} from './diff-waypoints.component'; import {UnitConversion} from '../../shared/UnitConversion'; -import {TestData} from '../diff.component'; +import {WaypointsDifferenceService} from '../../shared/waypoints-difference.service'; +import {ProfileComparatorService} from '../../shared/profileComparatorService'; +import {DiveSchedules} from '../../shared/dive.schedules'; +import {ReloadDispatcher} from '../../shared/reloadDispatcher'; describe('WaypointsDifferenceComponent', () => { let component: WaypointsDifferenceComponent; @@ -10,11 +13,15 @@ describe('WaypointsDifferenceComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ declarations: [WaypointsDifferenceComponent], - providers: [UnitConversion] + providers: [UnitConversion, + WaypointsDifferenceService, + ProfileComparatorService, + DiveSchedules, + ReloadDispatcher + ] }); fixture = TestBed.createComponent(WaypointsDifferenceComponent); component = fixture.componentInstance; - component.data = new TestData(); fixture.detectChanges(); }); diff --git a/projects/planner/src/app/diff/waypoints/diff-waypoints.component.ts b/projects/planner/src/app/diff/waypoints/diff-waypoints.component.ts index 0997e25a..5c095465 100644 --- a/projects/planner/src/app/diff/waypoints/diff-waypoints.component.ts +++ b/projects/planner/src/app/diff/waypoints/diff-waypoints.component.ts @@ -1,24 +1,19 @@ -import {Component, Input, OnInit} from '@angular/core'; -import {TestData} from '../diff.component'; +import {Component} from '@angular/core'; import {faTasks} from '@fortawesome/free-solid-svg-icons'; import {UnitConversion} from '../../shared/UnitConversion'; -import {WaypointsComparisonTableRowProvider} from '../../shared/waypointsComparisonTableRowProvider'; +import {WaypointsDifferenceService} from '../../shared/waypoints-difference.service'; +import {ProfileComparatorService} from '../../shared/profileComparatorService'; @Component({ selector: 'app-diff-waypoints', templateUrl: './diff-waypoints.component.html', styleUrls: ['./diff-waypoints.component.scss'], }) -export class WaypointsDifferenceComponent implements OnInit { - @Input({ required: true }) data!: TestData; +export class WaypointsDifferenceComponent { public tasks = faTasks; - public tableRowProvider: WaypointsComparisonTableRowProvider = - new WaypointsComparisonTableRowProvider([], []); - constructor(public units: UnitConversion) { - } - - ngOnInit() { - this.tableRowProvider = new WaypointsComparisonTableRowProvider(this.data.wayPointsA, this.data.wayPointsB); + constructor(public units: UnitConversion, + public tableRowProvider: WaypointsDifferenceService, + public profileComparatorService: ProfileComparatorService) { } } diff --git a/projects/planner/src/app/diveinfo/diveinfo.component.ts b/projects/planner/src/app/diveinfo/diveinfo.component.ts index 72044b83..834010a9 100644 --- a/projects/planner/src/app/diveinfo/diveinfo.component.ts +++ b/projects/planner/src/app/diveinfo/diveinfo.component.ts @@ -13,6 +13,7 @@ import { Streamed } from '../shared/streamed'; import { DepthsService } from '../shared/depths.service'; import { ViewSwitchService } from '../shared/viewSwitchService'; import { DiveSchedules } from '../shared/dive.schedules'; +import {TextConstants} from '../shared/TextConstants'; @Component({ selector: 'app-diveinfo', @@ -80,7 +81,7 @@ export class DiveInfoComponent extends Streamed { public get cnsText(): string { if(this.dive.cns >= 1000) { - return '> 1000'; + return TextConstants.cnsOverOneThousand; } return formatNumber(this.dive.cns, 'en', '1.0-0'); diff --git a/projects/planner/src/app/shared/TextConstants.ts b/projects/planner/src/app/shared/TextConstants.ts index 69d9ff90..730bdc9b 100644 --- a/projects/planner/src/app/shared/TextConstants.ts +++ b/projects/planner/src/app/shared/TextConstants.ts @@ -1,4 +1,5 @@ export class TextConstants { public static readonly depthConverterWarning = 'This calculator uses simple depth conversion (where 1 ATM = 1 bar), so the' + 'values shown here are less precise, than in rest of the application.'; + public static readonly cnsOverOneThousand = '> 1000'; } diff --git a/projects/planner/src/app/shared/profileComparatorService.ts b/projects/planner/src/app/shared/profileComparatorService.ts new file mode 100644 index 00000000..900a94dd --- /dev/null +++ b/projects/planner/src/app/shared/profileComparatorService.ts @@ -0,0 +1,52 @@ +import {Injectable} from '@angular/core'; +import {DiveSchedules} from './dive.schedules'; +import {DiveResults} from './diveresults'; + +@Injectable() +export class ProfileComparatorService { + + private _profileAIndex = 0; + private _profileBIndex = 0; + + constructor(private schedules: DiveSchedules) { + } + + get totalDuration(): number { + if(this.profileAResults().totalDuration > this.profileBResults().totalDuration){ + return this.profileAResults().totalDuration; + } + return this.profileBResults().totalDuration; + } + + set profileAIndex(value: number) { + this._profileAIndex = value; + } + + set profileBIndex(value: number) { + this._profileBIndex = value; + } + + public profileAResults(): DiveResults { + return this.schedules.dives[this._profileAIndex].diveResult; + } + + public profileBResults(): DiveResults { + return this.schedules.dives[this._profileBIndex].diveResult; + } + + public hasTwoProfiles(): boolean { + return this.schedules.length > 1; + } + + public areResultsCalculated(): boolean { + return this.profileAResults().calculated && this.profileBResults().calculated; + } + + public areDiveInfosCalculated(): boolean { + return this.profileAResults().diveInfoCalculated && this.profileBResults().diveInfoCalculated; + } + + public areProfilesCalculated(): boolean { + return this.profileAResults().profileCalculated && this.profileBResults().profileCalculated; + } +} diff --git a/projects/planner/src/app/shared/waypointsComparisonTableRowProvider.ts b/projects/planner/src/app/shared/waypoints-difference.service.ts similarity index 60% rename from projects/planner/src/app/shared/waypointsComparisonTableRowProvider.ts rename to projects/planner/src/app/shared/waypoints-difference.service.ts index e1af3a10..73772c8b 100644 --- a/projects/planner/src/app/shared/waypointsComparisonTableRowProvider.ts +++ b/projects/planner/src/app/shared/waypoints-difference.service.ts @@ -1,22 +1,39 @@ import {WayPoint} from './models'; import {WaypointsComparisonTableRow} from './WaypointsComparisonTableRow'; +import {Injectable} from '@angular/core'; +import {ProfileComparatorService} from './profileComparatorService'; -// TODO: Rework into injectable service that gets data from services instead of constructor -export class WaypointsComparisonTableRowProvider { +@Injectable() +export class WaypointsDifferenceService { - private wayPointsA: WayPoint[]; - private wayPointsB: WayPoint[]; private _waypointRows: WaypointsComparisonTableRow[] = []; + constructor(private profileComparatorService: ProfileComparatorService) { + } + get isCalculated(): boolean { + return this.profileComparatorService.areResultsCalculated(); + } + + private get wayPointsA(): WayPoint[]{ + return this.profileComparatorService.profileAResults().wayPoints; + } - constructor(wayPointsA: WayPoint[], wayPointsB: WayPoint[]) { - this.wayPointsA = wayPointsA; - this.wayPointsB = wayPointsB; + private get wayPointsB(): WayPoint[]{ + return this.profileComparatorService.profileBResults().wayPoints; } + public getRows(): WaypointsComparisonTableRow[] { const MAX_SAFETY_LIMIT = 65536; // 2**16 - let waypointA: WayPoint | undefined = this.wayPointsA.pop(); - let waypointB: WayPoint | undefined = this.wayPointsB.pop(); + this._waypointRows = []; + + if(!this.isCalculated){ + return []; + } + + const wayPointsACopy = [...this.wayPointsA]; + const wayPointsBCopy = [...this.wayPointsB]; + let waypointA: WayPoint | undefined = wayPointsACopy.pop(); + let waypointB: WayPoint | undefined = wayPointsBCopy.pop(); for (let i = 0; i < MAX_SAFETY_LIMIT; i++) { let row: WaypointsComparisonTableRow; @@ -32,7 +49,7 @@ export class WaypointsComparisonTableRowProvider { durationB: undefined, depthB: undefined, }; - waypointA = this.wayPointsA.pop(); + waypointA = wayPointsACopy.pop(); this._waypointRows.unshift(row); continue; } @@ -45,7 +62,7 @@ export class WaypointsComparisonTableRowProvider { durationB: waypointB?.duration, depthB: waypointB?.endDepth, }; - waypointB = this.wayPointsB.pop(); + waypointB = wayPointsBCopy.pop(); this._waypointRows.unshift(row); continue; } @@ -57,8 +74,8 @@ export class WaypointsComparisonTableRowProvider { durationB: waypointB?.duration, depthB: waypointB?.endDepth, }; - waypointA = this.wayPointsA.pop(); - waypointB = this.wayPointsB.pop(); + waypointA = wayPointsACopy.pop(); + waypointB = wayPointsBCopy.pop(); this._waypointRows.unshift(row); } return this._waypointRows;
Profile B
Run [min]Depth [{{units.length}}]Duration [min]Run [min]Depth [{{units.length}}]Duration [min] Depth [{{units.length}}] Duration [min]
- {{ row.runTime * 1000 | date:'mm:ss' }} + {{ row.runTime | duration:profileComparatorService.totalDuration }} {{ row.depthA | number: '1.0-0'}} - {{ row.durationA * 1000 | date: 'mm:ss'}} + {{ row.durationA * 1 | duration:profileComparatorService.totalDuration }} {{ row.depthB | number:'1.0-0' }} - {{ row.durationB * 1000 | date: 'mm:ss'}} + {{ row.durationB * 1 | duration:profileComparatorService.totalDuration }}