diff --git a/config.xml b/config.xml index 1206355..5c6013a 100644 --- a/config.xml +++ b/config.xml @@ -3,7 +3,7 @@ MyApp An awesome Ionic/Cordova app. Ionic Framework Team - + @@ -103,4 +103,5 @@ + diff --git a/platforms/ios/MyApp/config.xml b/platforms/ios/MyApp/config.xml index 58954e9..091cbd4 100644 --- a/platforms/ios/MyApp/config.xml +++ b/platforms/ios/MyApp/config.xml @@ -54,7 +54,7 @@ - + diff --git a/src/app/components/vital-signs-bp/vital-signs-bp.component.html b/src/app/components/vital-signs-bp/vital-signs-bp.component.html new file mode 100644 index 0000000..299fb7d --- /dev/null +++ b/src/app/components/vital-signs-bp/vital-signs-bp.component.html @@ -0,0 +1,46 @@ +
+
+ + + +
{{vitalUnits}}
+
+ +
{{vitalUnits2}}
+
+
+ + +
+
+ +
+
+
+ +
+ +
+ +
+ + + + + + + {{ timeSince }} + + + + + + +
+
diff --git a/src/app/components/vital-signs-bp/vital-signs-bp.component.scss b/src/app/components/vital-signs-bp/vital-signs-bp.component.scss new file mode 100644 index 0000000..2a53324 --- /dev/null +++ b/src/app/components/vital-signs-bp/vital-signs-bp.component.scss @@ -0,0 +1,205 @@ +@import '../../css/defaultColours'; + +.vitalHeaders { + height: 150px; +} + +.vitalTitle { + text-transform: uppercase; + width:70%;white-space:nowrap; overflow:hidden ;text-overflow: clip; + letter-spacing: 1px; + margin-top:-12px; +} + +.vitalTitle[data-theme='dark'] { + text-transform: uppercase; + color: lightgrey; +} + +.Normal[data-theme='dark'] { + color: lightgrey; +} + +.icon-Normal[data-theme='dark'] { + filter: invert(100%); +} + +.align { + &--left { + text-align: left; + padding-left: 6px !important; + } + + &--right { + text-align: right; + padding-right: 14px !important; + } + + &--bottom { + vertical-align: bottom !important; + } +} + +.lowerPart { + position: absolute; + bottom: 0; + width: 100%; +} + +.vitalInner { + padding: 12px 0 0 0 !important; + height: 150px; + background: linear-gradient(180deg, rgb(234, 234, 234) 0%, #ffffff 20%, #ffffff 80%, rgb(234, 234, 234) 100%); + + font-family: 'bebas', serif; + font-size: 20px; + font-weight: bold; + letter-spacing: 2px; +} + +.vitalInner[data-theme='dark'] { + padding: 12px 0 0 0 !important; + height: 150px; + background: linear-gradient(180deg, #212121 0%, #030365 50%, #212121 100%); + + font-family: 'bebas', serif; + font-size: 20px; + font-weight: bold; + letter-spacing: 2px; +} + +.timeSince { + font-size: 75%; + letter-spacing: -1px; +} + +.timeSince[data-theme='dark'] { + font-size: 75%; + letter-spacing: -1px; + color: lightgrey; +} + +.vitalValue { + position: absolute; + top: 27px; + left: 10px; + width: 90%; + height: 36px; + text-align: left; + font-size: 36px; +} + +.vitalValueBP { + position: absolute; + top: 15px; + left: 10px; + width: 90%; + height: 36px; + text-align: left; + font-size: 36px; +} + +.vitalUnit { + position: absolute; + bottom: 44px; + left: 10px; + font-size: 32px !important; +} + +.vitalUnitBP { + position: absolute; + bottom: 44px; + left: 80px; + font-size: 32px !important; +} + +.hrImg { + position: absolute; + top: 5px; + right: 15px; + width: 25%; + height: 25%; + //background-image: url('../assets/imgs/hr.png'); +} + +.hrBluetooth { + position: absolute; + top: 5px; + right: 10px; + width: 12%; + height: 12%; + //background-image: url('../assets/imgs/hr.png'); +} + +.vitalToFront { + z-index: 110000 !important; +} + +.OK { + color: $green; +} + +.Warn, +.WARN { + color: $yellow; +} + +.NotOK { + color: $red; +} + +.img15 { + width: 15%; + height: 15%; +} + +.img20 { + width: 20%; + height: 20%; +} + +.trendUp { + -webkit-transform: rotate(-90deg); + transform: rotate(-90deg); +} + +.trendFlat { + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); +} + +.trendDown { +} + +.trendUp[data-theme='dark'] { + -webkit-transform: rotate(-90deg); + transform: rotate(-90deg); + filter: invert(100%); +} + +.trendFlat[data-theme='dark'] { + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); + filter: invert(100%); +} + +.trendDown[data-theme='dark'] { + filter: invert(100%); +} + +.badCloud { + content: url('../../../assets/imgs/badcloud.png'); +} + +.badCloud[data-theme='dark'] { + content: url('../../../assets/imgs/badcloud.png'); + filter: invert(100%); +} + +.goodCloud { + content: url('../../../assets/imgs/goodcloud.png'); +} + +._PB { + border: 1px solid magenta; +} diff --git a/src/app/components/vital-signs-bp/vital-signs-bp.component.spec.ts b/src/app/components/vital-signs-bp/vital-signs-bp.component.spec.ts new file mode 100644 index 0000000..2780096 --- /dev/null +++ b/src/app/components/vital-signs-bp/vital-signs-bp.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { VitalSignsBpComponent } from './vital-signs-bp.component'; + +describe('VitalSignsBpComponent', () => { + let component: VitalSignsBpComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ VitalSignsBpComponent ], + imports: [IonicModule.forRoot()] + }).compileComponents(); + + fixture = TestBed.createComponent(VitalSignsBpComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/vital-signs-bp/vital-signs-bp.component.ts b/src/app/components/vital-signs-bp/vital-signs-bp.component.ts new file mode 100644 index 0000000..bcb8505 --- /dev/null +++ b/src/app/components/vital-signs-bp/vital-signs-bp.component.ts @@ -0,0 +1,121 @@ +import { Component, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { VitalSignsComponent } from '../vital-signs/vital-signs.component'; +import { VitalReading } from '../../models/VitalReading'; +import { trend } from 'basic-trend'; + +@Component({ + selector: 'app-vital-signs-bp', + templateUrl: './vital-signs-bp.component.html', + styleUrls: ['./vital-signs-bp.component.scss'] +}) +export class VitalSignsBpComponent extends VitalSignsComponent implements OnInit, OnChanges { + sysHistory: VitalReading[] = []; + diaHistory: VitalReading[] = []; + + constructor() { + super(); + } + + ngOnInit() { + console.log(`VitalSignsBPComponent::Init - ${this.thisID}`, this.config); + + if (this.config && this.config.vitalTitle) { + this.vitalTitle = this.config.vitalTitle; + } + + if (this.config && this.config.imageID) { + this.imageID = this.config.imageID; + } + + if (this.config && this.config.vitalUnits) { + this.vitalUnits = this.config.vitalUnits; + } + + if (this.config && this.config.vitalType) { + this.vitalType = this.config.vitalType; + this.vitalImage = `${this.config.vitalType}Normal`; + } + + if (this.config && this.config.vitalUnits2) { + this.vitalUnits2 = this.config.vitalUnits2; + this.visibleSecond = true; + } + + this.vitalTheme = !this.theme ? 'light' : 'dark'; + + clearTimeout(this.timerId); + this.timer = 0; + } + + /** + * Recalculates the trend + */ + recalculateTrend(): void { + console.log(`VitalSignsBPComponent::recalculateTrend - ${this.thisID}`); + const sequence: number[] = this.sysHistory.map((item: VitalReading): number => { + return parseInt(item.reading, 10); + }); + + this.currentTrend = trend(sequence); + + if (this.currentTrend === -1) { + this.trendClass = 'trendDown'; + } else if (this.currentTrend === 1) { + this.trendClass = 'trendUp'; + } else { + this.trendClass = 'trendFlat'; + } + } + + /** + * Handles changes to the input variables + * @param changes The updates change + */ + ngOnChanges(changes: SimpleChanges): void { + console.log(`VitalSignsBPComponent::changes - ${this.thisID}`, changes); + + if (changes.hasOwnProperty('newReading') && changes.newReading.isFirstChange() !== true) { + if (this.newReading.reading !== 'closePad') { + clearTimeout(this.timerId); + + const splitVal = this.newReading.reading.split('/'); + this.vitalValue = splitVal[0]; + this.vital2Value = splitVal[1]; + + const newSysHistory: VitalReading = { + reading: this.vitalValue, + timestamp: this.newReading.timestamp + }; + + const newDiaHistory: VitalReading = { + reading: this.vital2Value, + timestamp: this.newReading.timestamp + }; + + console.log(`VitalSignsBPComponent::changes - ${this.thisID} newSysHistory`, newSysHistory); + console.log(`VitalSignsBPComponent::changes - ${this.thisID} newDiaHistory`, newDiaHistory); + + this.sysHistory.push(newSysHistory); + this.diaHistory.push(newDiaHistory); + + this.timeSince = 'Fresh reading'; + this.timer = 0; + console.log('HISTORY:', this.history); + + this.vitalTextClass = this.checkVitals('bpSys', this.vitalValue); + this.vital2TextClass = this.checkVitals('bpDia', this.vital2Value); + + this.vitalIconClass = `icon-${this.vitalTextClass}`; + this.vitalImage = `${this.config.vitalType}${this.vitalTextClass}`; + this.recalculateTrend(); + this.doUpdate(); + + this.timerId = setTimeout(() => { + this.updateTimer(); + }, 60000); + } + } + + this.vitalTheme = !this.theme ? 'light' : 'dark'; + } +} diff --git a/src/app/components/vital-signs-bp/vital-signs-bp.module.ts b/src/app/components/vital-signs-bp/vital-signs-bp.module.ts new file mode 100644 index 0000000..49c6119 --- /dev/null +++ b/src/app/components/vital-signs-bp/vital-signs-bp.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { RouterModule } from '@angular/router'; + +import { IonicModule } from '@ionic/angular'; +import { VitalSignsBpComponent } from './vital-signs-bp.component'; + +@NgModule({ + imports: [CommonModule, FormsModule, IonicModule, RouterModule], + declarations: [VitalSignsBpComponent], + exports: [VitalSignsBpComponent] +}) +export class VitalSignsBpComponentModule {} diff --git a/src/app/components/vital-signs/vital-signs.component.html b/src/app/components/vital-signs/vital-signs.component.html index 77412a9..e8120ad 100644 --- a/src/app/components/vital-signs/vital-signs.component.html +++ b/src/app/components/vital-signs/vital-signs.component.html @@ -1,29 +1,31 @@ -
+
{{ vitalUnits }}
+
- +
- +
- - + + - + {{ timeSince }} - - + +
- - - diff --git a/src/app/components/vital-signs/vital-signs.component.ts b/src/app/components/vital-signs/vital-signs.component.ts index cbf629a..b4f21e3 100644 --- a/src/app/components/vital-signs/vital-signs.component.ts +++ b/src/app/components/vital-signs/vital-signs.component.ts @@ -20,18 +20,20 @@ export class VitalSignsComponent implements OnInit, OnChanges { vitalTheme = 'light'; vitalTitle = ''; vitalUnits = ''; + vitalUnits2 = ''; vitalType = ''; imageID = ''; currentTrend = 0; trendClass = 'trendFlat'; + visibleSecond = false; + vitalImage = ''; vitalValue = '--'; + vital2Value = '--'; vitalTextClass = 'Normal'; + vital2TextClass = 'Normal'; vitalIconClass = 'icon-Normal'; - hrBT = 'hideModal'; - - hrActive = 'col'; timer = 0; timerId = 0; @@ -51,6 +53,9 @@ export class VitalSignsComponent implements OnInit, OnChanges { constructor() {} + /** + * + */ ngOnInit() { console.log(`VitalSignsComponent::Init - ${this.thisID}`, this.config); @@ -71,12 +76,22 @@ export class VitalSignsComponent implements OnInit, OnChanges { this.vitalImage = `${this.config.vitalType}Normal`; } + if (this.config && this.config.vitalUnits2) { + this.vitalUnits2 = this.config.vitalUnits2; + this.visibleSecond = true; + } + this.vitalTheme = !this.theme ? 'light' : 'dark'; clearTimeout(this.timerId); this.timer = 0; } + /** + * Generates a new icon url string + * + * @return A new url to the correct icon + */ thisIcon(): string { if (this.imageID) { return `assets/imgs/${this.vitalImage}.png`; @@ -85,26 +100,28 @@ export class VitalSignsComponent implements OnInit, OnChanges { return ''; } - changeHR(): void { - console.log(`VitalSignsComponent::changeHR - ${this.thisID}`); + /** + * Click handler to request the keypad + */ + requestReading(): void { + console.log(`VitalSignsComponent::requestReading - ${this.thisID}`); this.clearVitals(); - /*this.vitalRequested = 'BPM'; - this.padVisible = true; - this.hrActive = 'col vitalToFront';*/ + this.doRequestInput(); } + /** + * + */ clearVitals(): void { this.padVisible = false; - this.hrActive = 'col'; - /*this.brActive = "col"; - this.satsActive = "col"; - this.bpActive = "col"; - this.tempActive = "col";*/ this.keyboardVis = 'hideModal'; } + /** + * Updates the timer text and restarts the minute timer + */ updateTimer(): void { this.timer++; @@ -123,6 +140,9 @@ export class VitalSignsComponent implements OnInit, OnChanges { }, 60000); } + /** + * Recalculates the trend + */ recalculateTrend(): void { const sequence: number[] = this.history.map((item: VitalReading): number => { return parseInt(item.reading, 10); @@ -139,6 +159,12 @@ export class VitalSignsComponent implements OnInit, OnChanges { } } + /** + * Responds to the updated vitals reading from the keypad, updates colour and icons accordingly + * @param vitalSign The vital sign + * @param vitalValue The Vital value + * @param override An override flag + */ checkVitals(vitalSign: string, vitalValue: string, override?: boolean): string { // console.log("sign: " + vitalSign + " - value: " + vitalValue); if (vitalValue === '--') { @@ -247,6 +273,9 @@ export class VitalSignsComponent implements OnInit, OnChanges { } } + /** + * Emits the updated reading + */ doUpdate(): void { const output = Object.assign({}, this.config, this.newReading); @@ -255,18 +284,26 @@ export class VitalSignsComponent implements OnInit, OnChanges { this.onRequestUpdate.emit(output); } + /** + * Emits a request to display the keypad with specific units + */ doRequestInput(): void { this.requestInput.emit({ vitalUnits: this.vitalUnits }); } + /** + * Handles changes to the input variables + * @param changes + */ ngOnChanges(changes: SimpleChanges): void { console.log(`VitalSignsComponent::changes - ${this.thisID}`, changes); if (changes.hasOwnProperty('newReading') && changes.newReading.isFirstChange() !== true) { if (this.newReading.reading !== 'closePad') { clearTimeout(this.timerId); + this.vitalValue = this.newReading.reading; const newHistory: VitalReading = this.newReading; diff --git a/src/app/home/home.module.ts b/src/app/home/home.module.ts index 8f661a1..c26e8c6 100644 --- a/src/app/home/home.module.ts +++ b/src/app/home/home.module.ts @@ -10,6 +10,7 @@ import {CpsTopBottomComponentModule} from '../components/cps-top-bottom/cps-top- import { VitalSignsComponentModule } from '../components/vital-signs/vital-signs.module'; import { AVPUSwipeComponentModule } from '../components/avpu-swipe/avpu-swipe.module'; import { NumPadComponentModule } from '../components/num-pad/num-pad.module'; +import { VitalSignsBpComponentModule } from '../components/vital-signs-bp/vital-signs-bp.module'; @NgModule({ @@ -22,7 +23,8 @@ import { NumPadComponentModule } from '../components/num-pad/num-pad.module'; CpsTopBottomComponentModule, VitalSignsComponentModule, AVPUSwipeComponentModule, - NumPadComponentModule + NumPadComponentModule, + VitalSignsBpComponentModule ], declarations: [HomePage] }) diff --git a/src/app/home/home.page.html b/src/app/home/home.page.html index ea0dde2..29e327f 100644 --- a/src/app/home/home.page.html +++ b/src/app/home/home.page.html @@ -25,6 +25,120 @@ --> + + + vital-signs + num-pad chain + + + + + + + + + +

Settings

+ + + New Reading + + Set + + + + + + Dark Mode + + + + +
+ + +

vitalTitle: {{tempVitalsReturnData.vitalTitle}}

+

vitalType: {{tempVitalsReturnData.vitalType}}

+

vitalUnits: {{tempVitalsReturnData.vitalUnits}}

+
+ + +

imageID: {{tempVitalsReturnData.imageID}}

+

reading: {{tempVitalsReturnData.reading}}

+

timestamp: {{tempVitalsReturnData.timestamp}}

+

vitalTheme: {{vitalTheme}}

+
+
+ +
+
+
+
+
+ + + + vital-signs-bp + num-pad chain + + + + + + + + + +

Settings

+ + + New Reading + + Set + + + + + + Dark Mode + + + + +
+ + +

vitalTitle: {{bpVitalsReturnData.vitalTitle}}

+

vitalType: {{bpVitalsReturnData.vitalType}}

+

vitalUnits: {{bpVitalsReturnData.vitalUnits}}

+
+ + +

imageID: {{bpVitalsReturnData.imageID}}

+

reading: {{bpVitalsReturnData.reading}}

+

timestamp: {{bpVitalsReturnData.timestamp}}

+

vitalTheme: {{vitalTheme}}

+
+
+ +
+
+
+
+
+ vital-signs + num-pad chain @@ -219,7 +333,7 @@ New Reading - + Set @@ -254,6 +368,9 @@ + + + app-cps-confirm-button diff --git a/src/app/home/home.page.ts b/src/app/home/home.page.ts index eae8f44..1786722 100644 --- a/src/app/home/home.page.ts +++ b/src/app/home/home.page.ts @@ -60,6 +60,7 @@ export class HomePage { vitalUnits2: 'dia', imageID: 'bpOK' }; + topBottomChainVal = ''; constructor() {} @@ -68,6 +69,7 @@ export class HomePage { testBR: string; testSATS: string; testTEMP: string; + testBP: string; blankVitalsReturnData: VitalsUpdate = { vitalTitle: '', @@ -86,10 +88,13 @@ export class HomePage { tempVitalsReturnData: VitalsUpdate = { ...this.blankVitalsReturnData }; + bpVitalsReturnData: VitalsUpdate = { ...this.blankVitalsReturnData }; + newHRValue: VitalReading; newBRValue: VitalReading; newSATSValue: VitalReading; newTEMPValue: VitalReading; + newBPValue: VitalReading; vitalTheme = false; padVisible = false; @@ -153,6 +158,16 @@ export class HomePage { } } + testSetBP() { + console.log('testSetBP', this.testSATS); + if (this.testBP !== null) { + this.newBPValue = { + reading: this.testBP, + timestamp: Date.now() + }; + } + } + /** * Captures the data exported from a vitals component * @param e the exported VitalsUpdate @@ -174,7 +189,12 @@ export class HomePage { captureTEMPVitalsUpdate(e: object): void { console.log('captureTEMPVitalsUpdate', e); - this.satsVitalsReturnData = e as VitalsUpdate; + this.tempVitalsReturnData = e as VitalsUpdate; + } + + captureBPVitalsUpdate(e: object): void { + console.log('captureBPVitalsUpdate', e); + this.bpVitalsReturnData = e as VitalsUpdate; } /** @@ -203,4 +223,11 @@ export class HomePage { this.vitalRequested = event.vitalUnits; this.padVisible = true; } + + bpRequestInput(event: object) { + console.log('vitalsRequestInput', event); + // @ts-ignore + this.vitalRequested = 'mmHg'; + this.padVisible = true; + } }