@mxtommy/kip
Advanced tools
Comparing version 0.1.6 to 0.1.7
@@ -0,0 +0,0 @@ { |
@@ -16,2 +16,22 @@ ### V0.1 | ||
### V0.1.5 | ||
* fix select dialogs (missing mat-form-field) | ||
* fix select dialogs (missing mat-form-field) | ||
### V0.1.6 | ||
* Hash based routing | ||
* Start of Boolean state widget | ||
* default to /signalk in url | ||
* | ||
### V0.1.7 | ||
Note, Any configs stored in browser will be lost as config format has changed. | ||
* Performance gain in Numeric widget in large fonts | ||
* Sailgauge updates (laylines, windsectors, general refactor) | ||
* Simplified source selection in the code | ||
* Removed Derived Data | ||
* Added Percentage unit, and stopped showing "no unit" on numeric | ||
* Updated Angular from v4 to v5 and also all dependencies to latest version | ||
* Complete re-write of widget settings modal for future ease of coding | ||
* Complete re-write of unit conversion service. Now if metadata specifies unit, it only offers you compatible units | ||
* Gauge Background and frame color options! | ||
* new Signal K Theme | ||
{ | ||
"name": "@mxtommy/kip", | ||
"version": "0.1.6", | ||
"version": "0.1.7", | ||
"license": "MIT", | ||
@@ -26,34 +26,39 @@ "keywords": [ | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/npm/npm.git" | ||
}, | ||
"dependencies": { | ||
"@angular/animations": "^4.4.3", | ||
"@angular/cdk": "^2.0.0-beta.11", | ||
"@angular/common": "^4.0.0", | ||
"@angular/compiler": "^4.0.0", | ||
"@angular/core": "^4.0.0", | ||
"@angular/forms": "^4.0.0", | ||
"@angular/http": "^4.0.0", | ||
"@angular/material": "^2.0.0-beta.11", | ||
"@angular/platform-browser": "^4.0.0", | ||
"@angular/platform-browser-dynamic": "^4.0.0", | ||
"@angular/router": "^4.0.0", | ||
"angular-split": "^0.2.2", | ||
"chart.js": "^2.7.0", | ||
"core-js": "^2.4.1", | ||
"@angular/animations": "^5.2.9", | ||
"@angular/cdk": "^5.2.4", | ||
"@angular/common": "^5.2.9", | ||
"@angular/compiler": "^5.2.9", | ||
"@angular/core": "^5.2.9", | ||
"@angular/forms": "^5.2.9", | ||
"@angular/http": "^5.2.9", | ||
"@angular/material": "^5.2.4", | ||
"@angular/platform-browser": "^5.2.9", | ||
"@angular/platform-browser-dynamic": "^5.2.9", | ||
"@angular/platform-server": "^5.2.9", | ||
"@angular/router": "^5.2.9", | ||
"angular-split": "^1.0.0-rc.3", | ||
"chart.js": "^2.7.2", | ||
"core-js": "^2.5.3", | ||
"js-quantities": "^1.7.0", | ||
"rxjs": "^5.4.1", | ||
"rxjs": "^5.5.7", | ||
"screenfull": "^3.3.2", | ||
"zone.js": "^0.8.14" | ||
"zone.js": "^0.8.19" | ||
}, | ||
"devDependencies": { | ||
"@angular/cli": "1.2.7", | ||
"@angular/compiler-cli": "^4.0.0", | ||
"@angular/language-service": "^4.0.0", | ||
"@types/jasmine": "~2.5.53", | ||
"@angular/cli": "^1.7.3", | ||
"@angular/compiler-cli": "^5.2.9", | ||
"@angular/language-service": "^5.2.9", | ||
"@types/jasmine": "~2.8.3", | ||
"@types/jasminewd2": "~2.0.2", | ||
"@types/node": "~6.0.60", | ||
"codelyzer": "~3.0.1", | ||
"jasmine-core": "~2.6.2", | ||
"jasmine-spec-reporter": "~4.1.0", | ||
"karma": "~1.7.0", | ||
"karma-chrome-launcher": "~2.1.1", | ||
"codelyzer": "^4.0.1", | ||
"jasmine-core": "~2.8.0", | ||
"jasmine-spec-reporter": "~4.2.1", | ||
"karma": "~2.0.0", | ||
"karma-chrome-launcher": "~2.2.0", | ||
"karma-cli": "~1.0.1", | ||
@@ -64,6 +69,6 @@ "karma-coverage-istanbul-reporter": "^1.2.1", | ||
"protractor": "~5.1.2", | ||
"ts-node": "~3.0.4", | ||
"tslint": "~5.3.2", | ||
"typescript": "~2.3.3" | ||
"ts-node": "~4.1.0", | ||
"tslint": "~5.9.1", | ||
"typescript": "~2.5.3" | ||
} | ||
} |
/*! NoSleep.min.js v0.7.0 - git.io/vfn01 - Rich Tibbett - MIT license */ | ||
!function(A,B){"object"==typeof exports&&"object"==typeof module?module.exports=B():"function"==typeof define&&define.amd?define([],B):"object"==typeof exports?exports.NoSleep=B():A.NoSleep=B()}(this,function(){return function(A){function B(e){if(Q[e])return Q[e].exports;var o=Q[e]={i:e,l:!1,exports:{}};return A[e].call(o.exports,o,o.exports,B),o.l=!0,o.exports}var Q={};return B.m=A,B.c=Q,B.d=function(A,Q,e){B.o(A,Q)||Object.defineProperty(A,Q,{configurable:!1,enumerable:!0,get:e})},B.n=function(A){var Q=A&&A.__esModule?function(){return A.default}:function(){return A};return B.d(Q,"a",Q),Q},B.o=function(A,B){return Object.prototype.hasOwnProperty.call(A,B)},B.p="",B(B.s=0)}([function(A,B,Q){"use strict";function e(A,B){if(!(A instanceof B))throw new TypeError("Cannot call a class as a function")}var o=function(){function A(A,B){for(var Q=0;Q<B.length;Q++){var e=B[Q];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(A,e.key,e)}}return function(B,Q,e){return Q&&A(B.prototype,Q),e&&A(B,e),B}}(),t=Q(1),n="undefined"!=typeof navigator&&parseFloat((""+(/CPU.*OS ([0-9_]{3,4})[0-9_]{0,1}|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_",".").replace("_",""))<10&&!window.MSStream,E=function(){function A(){e(this,A),n?this.noSleepTimer=null:(this.noSleepVideo=document.createElement("video"),this.noSleepVideo.setAttribute("playsinline",""),this.noSleepVideo.setAttribute("src",t),this.noSleepVideo.addEventListener("timeupdate",function(A){this.noSleepVideo.currentTime>.5&&(this.noSleepVideo.currentTime=Math.random())}.bind(this)))}return o(A,[{key:"enable",value:function(){n?(this.disable(),this.noSleepTimer=window.setInterval(function(){window.location.href="/",window.setTimeout(window.stop,0)},15e3)):this.noSleepVideo.play()}},{key:"disable",value:function(){n?this.noSleepTimer&&(window.clearInterval(this.noSleepTimer),this.noSleepTimer=null):this.noSleepVideo.pause()}}]),A}();A.exports=E},function(A,B,Q){"use strict";A.exports="data:video/mp4;base64,AAAAIGZ0eXBtcDQyAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAACKBtZGF0AAAC8wYF///v3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE0MiByMjQ3OSBkZDc5YTYxIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTEgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MToweDExMSBtZT1oZXggc3VibWU9MiBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0wIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MCA4eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0wIHRocmVhZHM9NiBsb29rYWhlYWRfdGhyZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0ZT0xIGludGVybGFjZWQ9MCBibHVyYXlfY29tcGF0PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTMgYl9weXJhbWlkPTIgYl9hZGFwdD0xIGJfYmlhcz0wIGRpcmVjdD0xIHdlaWdodGI9MSBvcGVuX2dvcD0wIHdlaWdodHA9MSBrZXlpbnQ9MzAwIGtleWludF9taW49MzAgc2NlbmVjdXQ9NDAgaW50cmFfcmVmcmVzaD0wIHJjX2xvb2thaGVhZD0xMCByYz1jcmYgbWJ0cmVlPTEgY3JmPTIwLjAgcWNvbXA9MC42MCBxcG1pbj0wIHFwbWF4PTY5IHFwc3RlcD00IHZidl9tYXhyYXRlPTIwMDAwIHZidl9idWZzaXplPTI1MDAwIGNyZl9tYXg9MC4wIG5hbF9ocmQ9bm9uZSBmaWxsZXI9MCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAOWWIhAA3//p+C7v8tDDSTjf97w55i3SbRPO4ZY+hkjD5hbkAkL3zpJ6h/LR1CAABzgB1kqqzUorlhQAAAAxBmiQYhn/+qZYADLgAAAAJQZ5CQhX/AAj5IQAD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"}])}); |
@@ -0,0 +0,0 @@ /********************************************************************** |
@@ -20,14 +20,14 @@ # Kip | ||
### V0.2 | ||
* Sailgauge Laylines and sectors | ||
* Customization options for radial/linear Gauges | ||
* Sailgauge Laylines and sectors - Done! | ||
* Customization options for radial/linear Gauges - In Progress | ||
* Customization options for Historical Charts | ||
* Pre-created widgets for commonly used displays | ||
* Redo conversion service | ||
* Know at least which type of unit it should be. (either with meta or hard coded/derived from schema) | ||
* Redo conversion service | ||
* Know at least which type of unit it should be. Done! | ||
* Default unit for each unit type in config. | ||
* Show SignalK status/alert if disconnected | ||
* Radial/Linear Gauge Zones | ||
* Hash based routing to be friendly to node server *Done! | ||
* Boolean state widget | ||
* Attitude Indicator | ||
* Hash based routing to be friendly to node server * Done! | ||
* Boolean state widget * In progress | ||
* Attitude Indicator * In progress | ||
@@ -40,3 +40,2 @@ ### V0.3 | ||
* Delete Page. | ||
* Support boolean values. | ||
* Numeric widget max/avg values. | ||
@@ -43,0 +42,0 @@ |
@@ -11,6 +11,6 @@ import { Injectable } from '@angular/core'; | ||
import { IWidget } from './widget-manager.service'; | ||
import { IDerivation } from './derived.service'; | ||
import { BlankConfig } from './blank-config.const'; | ||
import { DemoConfig } from './demo-config.const'; | ||
import { isNumber } from 'util'; | ||
@@ -20,4 +20,7 @@ const defaultSignalKUrl = 'http://demo.signalk.org/signalk'; | ||
const defaultTheme = 'default-light'; | ||
const configVersion = 1; // used to invalidate old configs to avoir errors loading it. | ||
interface appSettings { | ||
configVersion: number; | ||
signalKUrl: string; | ||
@@ -30,3 +33,2 @@ themeName: string; | ||
rootSplits: string[]; | ||
derivations: IDerivation[]; | ||
} | ||
@@ -47,3 +49,2 @@ | ||
rootSplits: string[] = []; | ||
derivations: IDerivation[] = []; | ||
@@ -56,7 +57,14 @@ themeName: BehaviorSubject<string> = new BehaviorSubject<string>(defaultTheme); | ||
private router: Router) { | ||
let storageObject: appSettings | ||
if (localStorage.getItem('signalKData') == null) { | ||
this.setDefaultConfig(); | ||
storageObject = this.getDefaultConfig(); | ||
} | ||
storageObject = JSON.parse(localStorage.getItem('signalKData')); | ||
if (!isNumber(storageObject.configVersion) || (storageObject.configVersion != configVersion)) { | ||
console.error("Invalid config version, loading default"); | ||
storageObject = this.getDefaultConfig(); | ||
} | ||
this.loadSettings(); | ||
this.loadSettings(storageObject); | ||
@@ -66,4 +74,3 @@ } | ||
loadSettings() { | ||
let storageObject: appSettings = JSON.parse(localStorage.getItem('signalKData')); | ||
loadSettings(storageObject: appSettings) { | ||
this.signalKUrl.next(storageObject['signalKUrl']); | ||
@@ -76,3 +83,2 @@ this.themeName.next(storageObject['themeName']); | ||
this.rootSplits = storageObject.rootSplits; | ||
this.derivations = storageObject.derivations; | ||
} | ||
@@ -147,10 +153,2 @@ | ||
// derivations | ||
getDerivations() { | ||
return this.derivations; | ||
} | ||
saveDerivations(derivations: IDerivation[]) { | ||
this.derivations = derivations; | ||
this.saveToLocalStorage(); | ||
} | ||
@@ -162,2 +160,3 @@ // saving. | ||
let storageObject: appSettings = { | ||
configVersion: configVersion, | ||
signalKUrl: this.signalKUrl.getValue(), | ||
@@ -170,3 +169,2 @@ themeName: this.themeName.getValue(), | ||
rootSplits: this.rootSplits, | ||
derivations: this.derivations | ||
} | ||
@@ -205,7 +203,9 @@ return storageObject; | ||
setDefaultConfig() { | ||
getDefaultConfig(): appSettings { | ||
let config = BlankConfig; | ||
config.signalKUrl = window.location.origin; | ||
config['configVersion'] = configVersion; | ||
localStorage.setItem('signalKData', JSON.stringify(config)); | ||
return config; | ||
} | ||
} |
@@ -11,3 +11,3 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; | ||
import { DataSetService } from './data-set.service'; | ||
import { DerivedService } from './derived.service'; | ||
//import { DerivedService } from './derived.service'; | ||
@@ -43,3 +43,3 @@ | ||
private DataSetService: DataSetService, | ||
private DerivedService: DerivedService, | ||
//private DerivedService: DerivedService, | ||
private overlayContainer: OverlayContainer, | ||
@@ -65,3 +65,3 @@ private LayoutSplitsService: LayoutSplitsService) { } | ||
this.DataSetService.startAllDataSets(); | ||
this.DerivedService.startAllDerivations(); | ||
//this.DerivedService.startAllDerivations(); | ||
} | ||
@@ -68,0 +68,0 @@ |
@@ -5,2 +5,3 @@ import { BrowserModule } from '@angular/platform-browser'; | ||
import { FormsModule } from '@angular/forms'; | ||
import { ReactiveFormsModule } from '@angular/forms'; | ||
import { HttpClientModule } from '@angular/common/http'; | ||
@@ -39,7 +40,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; | ||
import { UnitConvertService } from './unit-convert.service'; | ||
import { DerivedService } from './derived.service'; | ||
import { UnitsService } from './units.service'; | ||
import { WidgetBlankComponent } from './widget-blank/widget-blank.component'; | ||
import { WidgetUnknownComponent } from './widget-unknown/widget-unknown.component'; | ||
import { WidgetTextGenericComponent, WidgetTextGenericModalComponent } from './widget-text-generic/widget-text-generic.component'; | ||
import { WidgetTextGenericComponent } from './widget-text-generic/widget-text-generic.component'; | ||
import { UnitWindowComponent, UnitWindowModalComponent } from './unit-window/unit-window.component'; | ||
@@ -49,16 +50,19 @@ import { SettingsComponent } from './settings/settings.component'; | ||
import { FilterSelfPipe } from './filter-self.pipe'; | ||
import { WidgetNumericComponent, WidgetNumericModalComponent } from './widget-numeric/widget-numeric.component'; | ||
import { WidgetNumericComponent } from './widget-numeric/widget-numeric.component'; | ||
import { SettingsDatasetsComponent, SettingsDatasetsModalComponent } from './settings-datasets/settings-datasets.component'; | ||
import { SettingsSignalkComponent } from './settings-signalk/settings-signalk.component'; | ||
import { WidgetHistoricalComponent, WidgetHistoricalModalComponent } from './widget-historical/widget-historical.component'; | ||
import { WidgetHistoricalComponent } from './widget-historical/widget-historical.component'; | ||
import { LayoutSplitComponent } from './layout-split/layout-split.component'; | ||
import { WidgetWindComponent, WidgetWindModalComponent } from './widget-wind/widget-wind.component'; | ||
import { WidgetWindComponent, } from './widget-wind/widget-wind.component'; | ||
import { SvgWindComponent } from './svg-wind/svg-wind.component'; | ||
import { WidgetGaugeComponent, WidgetGaugeModalComponent } from './widget-gauge/widget-gauge.component'; | ||
import { WidgetGaugeComponent } from './widget-gauge/widget-gauge.component'; | ||
import { GaugeSteelComponent } from './gauge-steel/gauge-steel.component'; | ||
import { SettingsConfigComponent } from './settings-config/settings-config.component'; | ||
import { SettingsDerivedComponent, SettingsDerivedModalComponent } from './settings-derived/settings-derived.component'; | ||
import { WidgetTutorialComponent } from './widget-tutorial/widget-tutorial.component'; | ||
import { ResetConfigComponent } from './reset-config/reset-config.component'; | ||
import { WidgetStateComponent, WidgetStateModalComponent } from './widget-state/widget-state.component' | ||
import { WidgetStateComponent } from './widget-state/widget-state.component'; | ||
import { ModalWidgetComponent } from './modal-widget/modal-widget.component'; | ||
import { ModalPathSelectorComponent } from './modal-path-selector/modal-path-selector.component'; | ||
import { ModalUnitSelectorComponent } from './modal-unit-selector/modal-unit-selector.component'; | ||
import { ObjectKeysPipe } from './object-keys.pipe' | ||
@@ -84,3 +88,2 @@ | ||
WidgetTextGenericComponent, | ||
WidgetTextGenericModalComponent, | ||
FitTextDirective, | ||
@@ -90,3 +93,2 @@ RootDisplayComponent, | ||
WidgetNumericComponent, | ||
WidgetNumericModalComponent, | ||
SettingsDatasetsComponent, | ||
@@ -96,17 +98,15 @@ SettingsDatasetsModalComponent, | ||
WidgetHistoricalComponent, | ||
WidgetHistoricalModalComponent, | ||
LayoutSplitComponent, | ||
WidgetWindComponent, | ||
WidgetWindModalComponent, | ||
SvgWindComponent, | ||
WidgetGaugeComponent, | ||
WidgetGaugeModalComponent, | ||
GaugeSteelComponent, | ||
SettingsConfigComponent, | ||
SettingsDerivedComponent, | ||
SettingsDerivedModalComponent, | ||
WidgetTutorialComponent, | ||
ResetConfigComponent, | ||
WidgetStateComponent, | ||
WidgetStateModalComponent | ||
ModalWidgetComponent, | ||
ModalPathSelectorComponent, | ||
ModalUnitSelectorComponent, | ||
ObjectKeysPipe | ||
], | ||
@@ -116,2 +116,3 @@ imports: [ | ||
FormsModule, | ||
ReactiveFormsModule, | ||
HttpClientModule, | ||
@@ -138,3 +139,2 @@ RouterModule.forRoot(appRoutes, { useHash: true /* , enableTracing: true */ } ), | ||
WidgetTextGenericComponent, | ||
WidgetTextGenericModalComponent, | ||
WidgetHistoricalComponent, | ||
@@ -147,10 +147,5 @@ WidgetWindComponent, | ||
//dialogs | ||
ModalWidgetComponent, | ||
UnitWindowModalComponent, | ||
WidgetNumericModalComponent, | ||
WidgetHistoricalModalComponent, | ||
WidgetWindModalComponent, | ||
WidgetGaugeModalComponent, | ||
SettingsDerivedModalComponent, | ||
SettingsDatasetsModalComponent, | ||
WidgetStateModalComponent | ||
], | ||
@@ -167,4 +162,4 @@ providers: [ | ||
UnitConvertService, | ||
DerivedService, | ||
AppSettingsService | ||
UnitsService, | ||
AppSettingsService | ||
], | ||
@@ -171,0 +166,0 @@ bootstrap: [AppComponent] |
export const BlankConfig = { | ||
"configVersion": 0, | ||
"signalKUrl": "", // get's overwritten with host | ||
@@ -3,0 +4,0 @@ "themeName": "default-light", |
@@ -36,2 +36,3 @@ import { Injectable } from '@angular/core'; | ||
interface DataSetSub { | ||
@@ -164,14 +165,5 @@ uuid: string; | ||
//Subscribe to path data | ||
this.dataSetSub[dataSubIndex].pathSub = this.SignalKService.subscribePath(this.dataSets[dataIndex].uuid, this.dataSets[dataIndex].path).subscribe( | ||
pathObject => { | ||
if (pathObject === null) { | ||
return; // we will get null back if we subscribe to a path before the app knows about it. when it learns about it we will get first value | ||
} | ||
let source: string; | ||
if (this.dataSets[dataIndex].signalKSource == 'default') { | ||
source = pathObject.defaultSource; | ||
} else { | ||
source = this.dataSets[dataIndex].signalKSource; | ||
} | ||
this.updateDataCache(uuid, pathObject.sources[source].value); | ||
this.dataSetSub[dataSubIndex].pathSub = this.SignalKService.subscribePath(this.dataSets[dataIndex].uuid, this.dataSets[dataIndex].path, this.dataSets[dataIndex].signalKSource).subscribe( | ||
newValue => { | ||
this.updateDataCache(uuid, newValue); | ||
}); | ||
@@ -215,3 +207,3 @@ | ||
getDataSets() { | ||
getDataSets(): IDataSet[] { | ||
let result = []; | ||
@@ -218,0 +210,0 @@ for (let i=0;i<this.dataSets.length; i++) { |
export const DemoConfig = { | ||
"configVersion": 1, | ||
"signalKUrl": "http://demo.signalk.org", | ||
@@ -11,212 +12,176 @@ "themeName": "default-light", | ||
"config": { | ||
"signalKPath": "vessels.self.environment.wind.speedTrue", | ||
"signalKSource": "default", | ||
"label": "True Wind", | ||
"unitGroup": "speed", | ||
"unitName": "knots", | ||
"numDecimal": 2, | ||
"numInt": 2 | ||
"widgetLabel": "Depth Feet", | ||
"paths": { | ||
"numericPath": { | ||
"description": "Numeric Data", | ||
"path": "self.environment.depth.belowTransducer", | ||
"source": "default", | ||
"pathType": "number" | ||
} | ||
}, | ||
"units": { | ||
"numericPath": "feet" | ||
}, | ||
"selfPaths": true, | ||
"numDecimal": 1, | ||
"numInt": 1 | ||
} | ||
}, | ||
{ | ||
"uuid": "a3d595eb-f1a0-45a2-9faa-7af31757f129", | ||
"uuid": "7298b3be-232f-48bf-9b3d-3b433131a908", | ||
"type": "WidgetWindComponent", | ||
"config": { | ||
"headingPath": "vessels.self.navigation.headingTrue", | ||
"headingSource": "default", | ||
"trueWindAnglePath": "vessels.self.environment.wind.angleTrueWater", | ||
"trueWindAngleSource": "default", | ||
"trueWindSpeedPath": "vessels.self.environment.wind.speedTrue", | ||
"trueWindSpeedSource": "default", | ||
"appWindAnglePath": "vessels.self.environment.wind.angleApparent", | ||
"appWindAngleSource": "default", | ||
"appWindSpeedPath": "vessels.self.environment.wind.speedApparent", | ||
"appWindSpeedSource": "default", | ||
"unitName": "knots" | ||
"paths": { | ||
"headingPath": { | ||
"description": "Heading", | ||
"path": "self.navigation.courseOverGroundTrue", | ||
"source": "default", | ||
"pathType": "number" | ||
}, | ||
"trueWindAngle": { | ||
"description": "True Wind Angle", | ||
"path": "self.environment.wind.angleTrueWater", | ||
"source": "default", | ||
"pathType": "number" | ||
}, | ||
"trueWindSpeed": { | ||
"description": "True Wind Speed", | ||
"path": "self.environment.wind.speedTrue", | ||
"source": "default", | ||
"pathType": "number" | ||
}, | ||
"appWindAngle": { | ||
"description": "Apparent Wind Angle", | ||
"path": "self.environment.wind.angleApparent", | ||
"source": "default", | ||
"pathType": "number" | ||
}, | ||
"appWindSpeed": { | ||
"description": "Apparent Wind Speed", | ||
"path": "self.environment.wind.speedApparent", | ||
"source": "default", | ||
"pathType": "number" | ||
} | ||
}, | ||
"units": { | ||
"trueWindSpeed": "knots", | ||
"appWindSpeed": "knots" | ||
}, | ||
"selfPaths": true, | ||
"windSectorEnable": true, | ||
"windSectorWindowSeconds": 10, | ||
"laylineEnable": true, | ||
"laylineAngle": 35 | ||
} | ||
}, | ||
{ | ||
"uuid": "e595897d-a78a-46c1-ba93-d4c6741983a4", | ||
"type": "WidgetNumeric", | ||
"config": { | ||
"signalKPath": "vessels.self.environment.depth.belowKeel", | ||
"signalKSource": "default", | ||
"label": "Depth below keel", | ||
"unitGroup": "distance", | ||
"unitName": "feet", | ||
"numDecimal": 2, | ||
"numInt": 2 | ||
} | ||
}, | ||
{ | ||
"uuid": "4d953a0b-a7be-4bdf-84fd-d6d244056378", | ||
"type": "WidgetNumeric", | ||
"config": { | ||
"signalKPath": "vessels.self.environment.wind.speedApparent", | ||
"signalKSource": "default", | ||
"label": "App Wind", | ||
"unitGroup": "speed", | ||
"unitName": "knots", | ||
"numDecimal": 2, | ||
"numInt": 2 | ||
} | ||
}, | ||
{ | ||
"uuid": "7955f86d-1ba6-4a2f-8c3e-a1f5da895768", | ||
"type": "WidgetNumeric", | ||
"config": { | ||
"signalKPath": "vessels.self.navigation.speedThroughWater", | ||
"signalKSource": "default", | ||
"label": "Speed (Water)", | ||
"unitGroup": "speed", | ||
"unitName": "knots", | ||
"numDecimal": 2, | ||
"numInt": 2 | ||
} | ||
}, | ||
{ | ||
"uuid": "d1068028-ea14-4db3-8a8d-8bbfea0fbfd3", | ||
"type": "WidgetHistorical", | ||
"config": { | ||
"dataSetUUID": "a5e77585-7e7b-4dbc-860e-0a53da687794", | ||
"label": "Depth", | ||
"unitGroup": "distance", | ||
"unitName": "m", | ||
"numDecimal": 2, | ||
"invertData": true, | ||
"displayMinMax": false, | ||
"animateGraph": false, | ||
"suggestedMin": -50, | ||
"suggestedMax": null, | ||
"includeZero": true | ||
} | ||
}, | ||
{ | ||
"uuid": "1bdb821f-4b0d-4232-bccb-6ea8eebf04cf", | ||
"type": "WidgetHistorical", | ||
"config": { | ||
"dataSetUUID": "63f7f37a-099c-48db-92aa-01e7ba2dc65d", | ||
"label": "Wind-True", | ||
"unitGroup": "speed", | ||
"unitName": "knots", | ||
"numDecimal": 2, | ||
"invertData": false, | ||
"displayMinMax": true, | ||
"animateGraph": false, | ||
"suggestedMin": null, | ||
"suggestedMax": 20, | ||
"includeZero": true | ||
} | ||
}, | ||
{ | ||
"uuid": "bb0da568-cb87-49f6-8359-48ff7b7cfa18", | ||
"uuid": "912b86e4-e068-49e9-9f75-a2292d772578", | ||
"type": "WidgetGaugeComponent", | ||
"config": { | ||
"widgetLabel": "Speed over ground", | ||
"paths": { | ||
"gaugePath": { | ||
"description": "Numeric Data", | ||
"path": "self.navigation.speedOverGround", | ||
"source": "default", | ||
"pathType": "number" | ||
} | ||
}, | ||
"units": { | ||
"gaugePath": "knots" | ||
}, | ||
"selfPaths": true, | ||
"gaugeType": "radial", | ||
"signalKPath": "vessels.self.electrical.batteries.1.voltage", | ||
"signalKSource": "default", | ||
"label": "Battery Voltage", | ||
"unitGroup": "electrity", | ||
"unitName": "volts", | ||
"barGraph": false, | ||
"radialSize": "three-quarter", | ||
"minValue": 8, | ||
"maxValue": 18 | ||
} | ||
}, | ||
{ | ||
"uuid": "a626a0ac-dcef-4e80-a3db-b121fe144854", | ||
"type": "WidgetGaugeComponent", | ||
"config": { | ||
"gaugeType": "linear", | ||
"signalKPath": "vessels.self.electrical.batteries.1.temperature", | ||
"signalKSource": "default", | ||
"label": "Battery Temp", | ||
"unitGroup": "temp", | ||
"unitName": "C", | ||
"barGraph": true, | ||
"radialSize": "full", | ||
"minValue": 0, | ||
"maxValue": 100 | ||
"maxValue": 10, | ||
"rotateFace": false, | ||
"backgroundColor": "turned", | ||
"frameColor": "tiltedBlack" | ||
} | ||
}, | ||
{ | ||
"uuid": "9b4420a5-5527-4be3-85fe-b8a6bff563f6", | ||
"uuid": "85525ebc-c40c-41e6-8379-05d573a331e1", | ||
"type": "WidgetGaugeComponent", | ||
"config": { | ||
"gaugeType": "radial", | ||
"signalKPath": "vessels.self.environment.wind.speedApparent", | ||
"signalKSource": "default", | ||
"label": "App Wind", | ||
"unitGroup": "speed", | ||
"unitName": "knots", | ||
"widgetLabel": "Apparent Wind Speed", | ||
"paths": { | ||
"gaugePath": { | ||
"description": "Numeric Data", | ||
"path": "self.environment.wind.speedApparent", | ||
"source": "default", | ||
"pathType": "number" | ||
} | ||
}, | ||
"units": { | ||
"gaugePath": "knots" | ||
}, | ||
"selfPaths": true, | ||
"gaugeType": "linear", | ||
"barGraph": true, | ||
"radialSize": "full", | ||
"minValue": 0, | ||
"maxValue": 30 | ||
"maxValue": 30, | ||
"rotateFace": false, | ||
"backgroundColor": "stainless", | ||
"frameColor": "chrome" | ||
} | ||
}, | ||
{ | ||
"uuid": "03e53522-77e0-4b09-8e1d-19959053a8fb", | ||
"type": "WidgetGaugeComponent", | ||
"uuid": "a49a59c6-b83d-40e0-b759-9d153da69105", | ||
"type": "WidgetNumeric", | ||
"config": { | ||
"gaugeType": "linear", | ||
"signalKPath": "vessels.self.environment.water.temperature", | ||
"signalKSource": "default", | ||
"label": "Water temperature", | ||
"unitGroup": "temp", | ||
"unitName": "C", | ||
"barGraph": false, | ||
"radialSize": "full", | ||
"minValue": 0, | ||
"maxValue": 30 | ||
"widgetLabel": "Speed (water)", | ||
"paths": { | ||
"numericPath": { | ||
"description": "Numeric Data", | ||
"path": "self.navigation.speedThroughWater", | ||
"source": "default", | ||
"pathType": "number" | ||
} | ||
}, | ||
"units": { | ||
"numericPath": "knots" | ||
}, | ||
"selfPaths": true, | ||
"numDecimal": 1, | ||
"numInt": 1 | ||
} | ||
}, | ||
{ | ||
"uuid": "3c84892b-60bb-47c1-8b42-8beef6260972", | ||
"type": "WidgetGaugeComponent", | ||
"uuid": "62fa8155-10fd-49cb-a495-cee6e9491b8a", | ||
"type": "WidgetNumeric", | ||
"config": { | ||
"gaugeType": "linear", | ||
"signalKPath": "vessels.self.navigation.gnss.satellites", | ||
"signalKSource": "default", | ||
"label": "GPS Satellites", | ||
"unitGroup": "discreet", | ||
"unitName": "no unit", | ||
"barGraph": true, | ||
"radialSize": "full", | ||
"minValue": 0, | ||
"maxValue": 20 | ||
"widgetLabel": "VMG", | ||
"paths": { | ||
"numericPath": { | ||
"description": "Numeric Data", | ||
"path": "self.performance.velocityMadeGood", | ||
"source": "default", | ||
"pathType": "number" | ||
} | ||
}, | ||
"units": { | ||
"numericPath": "knots" | ||
}, | ||
"selfPaths": true, | ||
"numDecimal": 1, | ||
"numInt": 1 | ||
} | ||
}, | ||
{ | ||
"uuid": "0e6067e7-e819-4a75-853c-d16dbfec5e05", | ||
"type": "WidgetGaugeComponent", | ||
"config": { | ||
"gaugeType": "radial", | ||
"signalKPath": "vessels.self.environment.wind.speedTrue", | ||
"signalKSource": "default", | ||
"label": "True Wind", | ||
"unitGroup": "speed", | ||
"unitName": "knots", | ||
"barGraph": true, | ||
"radialSize": "full", | ||
"minValue": 0, | ||
"maxValue": 30 | ||
} | ||
}, | ||
{ | ||
"uuid": "072502e7-5512-4c69-8b5e-118a1290bc27", | ||
"uuid": "42de0119-481c-4466-8b50-1407533ac2aa", | ||
"type": "WidgetHistorical", | ||
"config": { | ||
"dataSetUUID": "dd9f511f-d94f-4f40-a20e-c08d2cd4690e", | ||
"label": "True Wind", | ||
"unitGroup": "speed", | ||
"unitName": "knots", | ||
"numDecimal": 2, | ||
"widgetLabel": "WindSpeed True", | ||
"units": { | ||
"dataset": "knots" | ||
}, | ||
"dataSetUUID": "afbe4e41-26f5-404f-a55d-9f7b9b76fbd1", | ||
"invertData": false, | ||
"displayMinMax": true, | ||
"animateGraph": false, | ||
"suggestedMin": null, | ||
"suggestedMax": 30, | ||
"includeZero": true | ||
"displayMinMax": false, | ||
"includeZero": true, | ||
"minValue": null, | ||
"maxValue": null | ||
} | ||
@@ -228,21 +193,7 @@ } | ||
{ | ||
"uuid": "a5e77585-7e7b-4dbc-860e-0a53da687794", | ||
"path": "vessels.self.environment.depth.belowKeel", | ||
"uuid": "afbe4e41-26f5-404f-a55d-9f7b9b76fbd1", | ||
"path": "self.environment.wind.speedTrue", | ||
"signalKSource": "default", | ||
"updateTimer": 1, | ||
"dataPoints": 20 | ||
}, | ||
{ | ||
"uuid": "63f7f37a-099c-48db-92aa-01e7ba2dc65d", | ||
"path": "vessels.self.environment.wind.speedTrue", | ||
"signalKSource": "default", | ||
"updateTimer": 5, | ||
"dataPoints": 30 | ||
}, | ||
{ | ||
"uuid": "dd9f511f-d94f-4f40-a20e-c08d2cd4690e", | ||
"path": "vessels.self.environment.wind.speedApparent", | ||
"signalKSource": "default", | ||
"updateTimer": 10, | ||
"dataPoints": 60 | ||
"dataPoints": 15 | ||
} | ||
@@ -256,15 +207,15 @@ ], | ||
{ | ||
"uuid": "a68fff4a-15f2-4d62-8083-835e7ac74cf0", | ||
"uuid": "d107e54d-2db5-4abf-aba7-b96ce19f5abd", | ||
"type": "splitSet", | ||
"size": 24.166 | ||
"size": 27.406021225904265 | ||
}, | ||
{ | ||
"uuid": "a3d595eb-f1a0-45a2-9faa-7af31757f129", | ||
"type": "widget", | ||
"size": 48.177 | ||
"uuid": "9249373f-7aa4-4673-8004-3e4e900e0b3d", | ||
"type": "splitSet", | ||
"size": 45.296959997548555 | ||
}, | ||
{ | ||
"uuid": "be832489-4796-4f69-9025-11937c1e2bc4", | ||
"uuid": "d5be7f74-28c0-484c-a0cd-e623eb5db837", | ||
"type": "splitSet", | ||
"size": 27.656 | ||
"size": 27.297018776547173 | ||
} | ||
@@ -274,3 +225,3 @@ ] | ||
{ | ||
"uuid": "a68fff4a-15f2-4d62-8083-835e7ac74cf0", | ||
"uuid": "9249373f-7aa4-4673-8004-3e4e900e0b3d", | ||
"parentUUID": "isplitsx-xxxx-4xxx-yxxx-xxxxxxxxxxxx", | ||
@@ -280,20 +231,10 @@ "direction": "vertical", | ||
{ | ||
"uuid": "widgetno-1xxx-4xxx-yxxx-xxxxxxxxxxxx", | ||
"uuid": "7298b3be-232f-48bf-9b3d-3b433131a908", | ||
"type": "widget", | ||
"size": 25 | ||
"size": 77.30434782608697 | ||
}, | ||
{ | ||
"uuid": "4d953a0b-a7be-4bdf-84fd-d6d244056378", | ||
"uuid": "85525ebc-c40c-41e6-8379-05d573a331e1", | ||
"type": "widget", | ||
"size": 25 | ||
}, | ||
{ | ||
"uuid": "e595897d-a78a-46c1-ba93-d4c6741983a4", | ||
"type": "widget", | ||
"size": 25 | ||
}, | ||
{ | ||
"uuid": "7955f86d-1ba6-4a2f-8c3e-a1f5da895768", | ||
"type": "widget", | ||
"size": 25 | ||
"size": 22.69565217391304 | ||
} | ||
@@ -303,3 +244,3 @@ ] | ||
{ | ||
"uuid": "be832489-4796-4f69-9025-11937c1e2bc4", | ||
"uuid": "d107e54d-2db5-4abf-aba7-b96ce19f5abd", | ||
"parentUUID": "isplitsx-xxxx-4xxx-yxxx-xxxxxxxxxxxx", | ||
@@ -309,60 +250,15 @@ "direction": "vertical", | ||
{ | ||
"uuid": "d1068028-ea14-4db3-8a8d-8bbfea0fbfd3", | ||
"uuid": "widgetno-1xxx-4xxx-yxxx-xxxxxxxxxxxx", | ||
"type": "widget", | ||
"size": 50 | ||
"size": 33.13526570048309 | ||
}, | ||
{ | ||
"uuid": "1bdb821f-4b0d-4232-bccb-6ea8eebf04cf", | ||
"uuid": "a49a59c6-b83d-40e0-b759-9d153da69105", | ||
"type": "widget", | ||
"size": 50 | ||
} | ||
] | ||
}, | ||
{ | ||
"uuid": "ebe306cc-9da9-4531-89ed-2252a0251dbe", | ||
"direction": "horizontal", | ||
"splitAreas": [ | ||
{ | ||
"uuid": "bb0da568-cb87-49f6-8359-48ff7b7cfa18", | ||
"type": "widget", | ||
"size": 50 | ||
"size": 33.432367149758456 | ||
}, | ||
{ | ||
"uuid": "0663e4c3-51b3-4a92-beb2-009d9e5ee047", | ||
"type": "splitSet", | ||
"size": 50 | ||
} | ||
] | ||
}, | ||
{ | ||
"uuid": "0663e4c3-51b3-4a92-beb2-009d9e5ee047", | ||
"parentUUID": "ebe306cc-9da9-4531-89ed-2252a0251dbe", | ||
"direction": "vertical", | ||
"splitAreas": [ | ||
{ | ||
"uuid": "57311752-f510-497f-af83-476020d9116f", | ||
"type": "splitSet", | ||
"size": 50 | ||
}, | ||
{ | ||
"uuid": "a12ab428-9d8f-4550-8b33-33b921570dcf", | ||
"type": "splitSet", | ||
"size": 50 | ||
} | ||
] | ||
}, | ||
{ | ||
"uuid": "57311752-f510-497f-af83-476020d9116f", | ||
"parentUUID": "0663e4c3-51b3-4a92-beb2-009d9e5ee047", | ||
"direction": "horizontal", | ||
"splitAreas": [ | ||
{ | ||
"uuid": "a626a0ac-dcef-4e80-a3db-b121fe144854", | ||
"uuid": "62fa8155-10fd-49cb-a495-cee6e9491b8a", | ||
"type": "widget", | ||
"size": 21.055 | ||
}, | ||
{ | ||
"uuid": "a11f9ba9-9cda-40d4-a85e-949f3cb0794f", | ||
"type": "splitSet", | ||
"size": 78.945 | ||
"size": 33.432367149758456 | ||
} | ||
@@ -372,90 +268,22 @@ ] | ||
{ | ||
"uuid": "a11f9ba9-9cda-40d4-a85e-949f3cb0794f", | ||
"parentUUID": "57311752-f510-497f-af83-476020d9116f", | ||
"uuid": "d5be7f74-28c0-484c-a0cd-e623eb5db837", | ||
"parentUUID": "isplitsx-xxxx-4xxx-yxxx-xxxxxxxxxxxx", | ||
"direction": "vertical", | ||
"splitAreas": [ | ||
{ | ||
"uuid": "03e53522-77e0-4b09-8e1d-19959053a8fb", | ||
"uuid": "912b86e4-e068-49e9-9f75-a2292d772578", | ||
"type": "widget", | ||
"size": 50.441 | ||
"size": 25 | ||
}, | ||
{ | ||
"uuid": "3c84892b-60bb-47c1-8b42-8beef6260972", | ||
"uuid": "42de0119-481c-4466-8b50-1407533ac2aa", | ||
"type": "widget", | ||
"size": 49.559 | ||
"size": 25 | ||
} | ||
] | ||
}, | ||
{ | ||
"uuid": "a12ab428-9d8f-4550-8b33-33b921570dcf", | ||
"parentUUID": "0663e4c3-51b3-4a92-beb2-009d9e5ee047", | ||
"direction": "horizontal", | ||
"splitAreas": [ | ||
{ | ||
"uuid": "9b4420a5-5527-4be3-85fe-b8a6bff563f6", | ||
"type": "widget", | ||
"size": 50 | ||
}, | ||
{ | ||
"uuid": "0e6067e7-e819-4a75-853c-d16dbfec5e05", | ||
"type": "widget", | ||
"size": 50 | ||
} | ||
] | ||
}, | ||
{ | ||
"uuid": "d538dd07-679b-413e-9910-8b6508942059", | ||
"direction": "horizontal", | ||
"splitAreas": [ | ||
{ | ||
"uuid": "072502e7-5512-4c69-8b5e-118a1290bc27", | ||
"type": "widget", | ||
"size": 100 | ||
} | ||
] | ||
} | ||
], | ||
"rootSplits": [ | ||
"isplitsx-xxxx-4xxx-yxxx-xxxxxxxxxxxx", | ||
"ebe306cc-9da9-4531-89ed-2252a0251dbe", | ||
"d538dd07-679b-413e-9910-8b6508942059" | ||
], | ||
"derivations": [ | ||
{ | ||
"name": "True Wind", | ||
"updateAny": false, | ||
"paths": [ | ||
{ | ||
"path": "vessels.self.navigation.headingTrue", | ||
"source": "default" | ||
}, | ||
{ | ||
"path": "vessels.self.navigation.speedThroughWater", | ||
"source": "default" | ||
}, | ||
{ | ||
"path": "vessels.self.environment.wind.speedApparent", | ||
"source": "default" | ||
}, | ||
{ | ||
"path": "vessels.self.environment.wind.angleApparent", | ||
"source": "default" | ||
} | ||
] | ||
}, | ||
{ | ||
"name": "Dew Point", | ||
"updateAny": false, | ||
"paths": [ | ||
{ | ||
"path": "vessels.self.environment.outside.temperature", | ||
"source": "default" | ||
}, | ||
{ | ||
"path": "vessels.self.environment.outside.humidity", | ||
"source": "default" | ||
} | ||
] | ||
} | ||
"isplitsx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" | ||
] | ||
} |
@@ -0,0 +0,0 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; |
@@ -285,5 +285,18 @@ import { Component, Input, AfterViewInit, OnInit, OnChanges, AfterViewChecked, SimpleChanges, ViewChild, ElementRef } from '@angular/core'; | ||
if (changes.backgroundColor) { | ||
if ( !changes.backgroundColor.firstChange) { | ||
this.buildOptions(); | ||
this.startGauge();//reset | ||
} | ||
} | ||
if (changes.frameColor) { | ||
if ( !changes.frameColor.firstChange) { | ||
this.buildOptions(); | ||
this.startGauge();//reset | ||
} | ||
} | ||
} | ||
@@ -290,0 +303,0 @@ |
@@ -45,4 +45,4 @@ import { Component, OnInit, OnDestroy, OnChanges, SimpleChanges, Input } from '@angular/core'; | ||
onDragEnd(sizesArray: Array<number>) { | ||
this.LayoutSplitsService.updateSplitSizes(this.splitSet.uuid, sizesArray); | ||
onDragEnd(sizesArray: {gutterNum: number, sizes: Array<number>}) { | ||
this.LayoutSplitsService.updateSplitSizes(this.splitSet.uuid, sizesArray.sizes); | ||
} | ||
@@ -49,0 +49,0 @@ |
@@ -181,3 +181,3 @@ import { Injectable } from '@angular/core'; | ||
updateSplitSizes(splitSetUUID: string, sizesArray) { | ||
updateSplitSizes(splitSetUUID: string, sizesArray: Array<number>) { | ||
let splitIndex = this.splitSets.findIndex(sSet => sSet.uuid == splitSetUUID); | ||
@@ -184,0 +184,0 @@ if (splitIndex < 0) { return null; } |
@@ -0,0 +0,0 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; |
@@ -0,0 +0,0 @@ import { Component, OnInit } from '@angular/core'; |
@@ -0,0 +0,0 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; |
@@ -0,0 +0,0 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; |
@@ -0,0 +0,0 @@ import { Component, OnInit } from '@angular/core'; |
@@ -0,0 +0,0 @@ import { Component, OnInit, Inject } from '@angular/core'; |
@@ -0,0 +0,0 @@ import { Component, OnInit } from '@angular/core'; |
@@ -0,0 +0,0 @@ import { Injectable } from '@angular/core'; |
@@ -0,0 +0,0 @@ import { Injectable } from '@angular/core'; |
@@ -0,0 +0,0 @@ import { Injectable } from '@angular/core'; |
@@ -42,3 +42,4 @@ import { Injectable } from '@angular/core'; | ||
path: string; | ||
observable: BehaviorSubject<pathObject>; | ||
source?: string; // if this is set, updates to observable are the direct value of this source... | ||
observable: BehaviorSubject<any>; | ||
} | ||
@@ -73,3 +74,3 @@ | ||
subscribePath(uuid, path) { | ||
subscribePath(uuid: string, path: string, source: string = null) { | ||
//see if already subscribed, if yes return that... | ||
@@ -82,16 +83,25 @@ let registerIndex = this.pathRegister.findIndex(registration => (registration.path == path) && (registration.uuid == uuid)); | ||
//find if we already have a value for this path to return. | ||
let currentValue: pathObject; | ||
let currentValue = null; | ||
let pathIndex = this.paths.findIndex(pathObject => pathObject.path == path); | ||
if (pathIndex >= 0) { // exists | ||
currentValue = this.paths[pathIndex]; | ||
} else { | ||
currentValue = null; | ||
} | ||
if (source === null) { | ||
currentValue = this.paths[pathIndex]; // return the entire pathObject | ||
} else if (source == 'default') { | ||
currentValue = this.paths[pathIndex].sources[this.paths[pathIndex].defaultSource].value; | ||
} else if (source in this.paths[pathIndex].sources) { | ||
currentValue = this.paths[pathIndex].sources[source].value; | ||
} | ||
} | ||
//register | ||
this.pathRegister.push({ | ||
let newRegister = { | ||
uuid: uuid, | ||
path: path, | ||
observable: new BehaviorSubject<pathObject>(currentValue) | ||
}); | ||
observable: new BehaviorSubject<any>(currentValue) | ||
}; | ||
if (source !== null) { | ||
newRegister['source'] = source; | ||
} | ||
//register | ||
this.pathRegister.push(newRegister); | ||
// should be subscribed now, use search now as maybe someone else adds something and it's no longer last in array :P | ||
@@ -140,7 +150,25 @@ pathIndex = this.pathRegister.findIndex(registration => (registration.path == path) && (registration.uuid == uuid)); | ||
// push it to any subscriptions of that data | ||
for (let i = 0; i < this.pathRegister.length; i++) { | ||
if (this.pathRegister[i].path == pathSelf) { | ||
this.pathRegister[i].observable.next(this.paths[pathIndex]); | ||
this.pathRegister.filter(pathRegister => pathRegister.path == pathSelf).forEach( | ||
pathRegister => { | ||
// new type sub that just wants the value | ||
if ("source" in pathRegister) { | ||
let source: string = null; | ||
if (pathRegister.source == 'default') { | ||
source = this.paths[pathIndex].defaultSource; | ||
} else if (pathRegister.source in this.paths[pathIndex].sources) { | ||
source = pathRegister.source; | ||
} else { | ||
//we're looking for a source we don't know of... do nothing I guess? | ||
} | ||
if (source !== null) { | ||
pathRegister.observable.next(this.paths[pathIndex].sources[source].value); | ||
} | ||
} else { | ||
//old type sub that wants whole pathObject... | ||
pathRegister.observable.next(this.paths[pathIndex]); | ||
} | ||
} | ||
} | ||
); | ||
} | ||
@@ -182,2 +210,8 @@ | ||
getPathUnitType(path: string): string { | ||
let pathIndex = this.paths.findIndex(pathObject => pathObject.path == path); | ||
if (pathIndex < 0) { return null; } | ||
if (('meta' in this.paths[pathIndex]) && ('units' in this.paths[pathIndex].meta)) { return this.paths[pathIndex].meta.units; } else { return null; } | ||
} | ||
} |
@@ -0,0 +0,0 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; |
import { Component, OnInit, Input, ViewChild, ElementRef, OnChanges, SimpleChanges } from '@angular/core'; | ||
import { isNumeric } from 'rxjs/util/isNumeric'; | ||
import { isNumber } from 'util'; | ||
const angle = ([a,b],[c,d],[e,f]) => (Math.atan2(f-d,e-c)-Math.atan2(b-d,a-c)+3*Math.PI)%(2*Math.PI)-Math.PI; | ||
@Component({ | ||
@@ -13,2 +17,4 @@ selector: 'app-svg-wind', | ||
@ViewChild('trueWindAnimate') trueWindAnimate: ElementRef; | ||
//@ViewChild('laylinePortAnimate') laylinePortAnimate: ElementRef; | ||
//@ViewChild('laylineStbAnimate') laylineStbAnimate: ElementRef; | ||
@@ -21,9 +27,10 @@ | ||
@Input('appWindSpeed') appWindSpeed: number; | ||
@Input('laylinePortAngle') laylinePortAngle : number; | ||
@Input('laylineStbAngle') laylineStbAngle: number; | ||
@Input('windSectorPortStart') windSectorPortStart: number; | ||
@Input('windSectorPortEnd') windSectorPortEnd: number; | ||
@Input('windSectorStbStart') windSectorStbStart: number; | ||
@Input('windSectorStbEnd') windSectorStbEnd: number; | ||
@Input('laylineAngle') laylineAngle : number; | ||
@Input('laylineEnable') laylineEnable: boolean; | ||
@Input('windSectorEnable') windSectorEnable: boolean; | ||
@Input('trueWindMinHistoric') trueWindMinHistoric: number; | ||
@Input('trueWindMidHistoric') trueWindMidHistoric: number; | ||
@Input('trueWindMaxHistoric') trueWindMaxHistoric: number; | ||
@@ -42,6 +49,14 @@ constructor() { } | ||
//truewind | ||
oldTrueWindAngle: string = "0"; | ||
newTrueWindAngle: string = "0"; | ||
oldTrueWindRotateAngle: string = "0"; | ||
newTrueWindRotateAngle: string = "0"; | ||
trueWindHeading: number = 0; | ||
trueWindSpeedDisplay: string = ""; | ||
//laylines | ||
laylinePortPath: string = "M 250,250 250,90"; | ||
laylineStbdPath: string = "M 250,250 250,90"; | ||
//WindSectors | ||
portWindSectorPath: string = "none"; | ||
stbdWindSectorPath: string = "none"; | ||
ngOnInit() { | ||
@@ -62,2 +77,4 @@ | ||
this.compassAnimate.nativeElement.beginElement(); | ||
this.updateTrueWind();// rotates with heading change | ||
this.updateWindSectors();// they need to update to new heading too | ||
} | ||
@@ -87,8 +104,4 @@ } | ||
if (! changes.trueWindAngle.firstChange) { | ||
this.oldTrueWindAngle = this.newTrueWindAngle; | ||
this.newTrueWindAngle = changes.trueWindAngle.currentValue; //.toString(); | ||
if (this.trueWindAnimate) { // only update if on dom... | ||
this.trueWindAnimate.nativeElement.beginElement(); | ||
} | ||
this.trueWindHeading = changes.trueWindAngle.currentValue; | ||
this.updateTrueWind(); | ||
} | ||
@@ -103,6 +116,85 @@ } | ||
//Min/Max | ||
if (changes.trueWindMinHistoric || changes.trueWindMaxHistoric) { | ||
if (isNumber(this.trueWindMinHistoric) && isNumber(this.trueWindMaxHistoric)) { | ||
this.updateWindSectors(); | ||
} | ||
} | ||
} | ||
updateTrueWind(){ | ||
this.oldTrueWindRotateAngle = this.newTrueWindRotateAngle; | ||
this.newTrueWindRotateAngle = this.addHeading(this.trueWindHeading, (this.newCompassRotate*-1)).toFixed(0); //compass rotate is negative as we actually have to rotate counter clockwise | ||
if (this.trueWindAnimate) { // only update if on dom... | ||
this.trueWindAnimate.nativeElement.beginElement(); | ||
} | ||
//calculate laylines | ||
let portLaylineRotate = this.addHeading(Number(this.newTrueWindRotateAngle), (this.laylineAngle*-1)); | ||
//find xy of that roation (160 = radius of inner circle) | ||
let portX = 160 * Math.sin((portLaylineRotate*Math.PI)/180) + 250; //250 is middle | ||
let portY = (160 * Math.cos((portLaylineRotate*Math.PI)/180)*-1) + 250; //-1 since SVG 0 is at top | ||
this.laylinePortPath = 'M 250,250 ' + portX +',' + portY; | ||
let stbdLaylineRotate = this.addHeading(Number(this.newTrueWindRotateAngle), (this.laylineAngle)); | ||
//find xy of that roation (160 = radius of inner circle) | ||
let stbdX = 160 * Math.sin((stbdLaylineRotate*Math.PI)/180) + 250; //250 is middle | ||
let stbdY = (160 * Math.cos((stbdLaylineRotate*Math.PI)/180)*-1) + 250; //-1 since SVG 0 is at top | ||
this.laylineStbdPath = 'M 250,250 ' + stbdX +',' + stbdY; | ||
} | ||
updateWindSectors() { | ||
let portMin = this.addHeading(this.addHeading(this.trueWindMinHistoric, (this.newCompassRotate*-1)), (this.laylineAngle*-1)); | ||
let portMid = this.addHeading(this.addHeading(this.trueWindMidHistoric, (this.newCompassRotate*-1)), (this.laylineAngle*-1)); | ||
let portMax = this.addHeading(this.addHeading(this.trueWindMaxHistoric, (this.newCompassRotate*-1)), (this.laylineAngle*-1)); | ||
//console.log(this.trueWindMinHistoric.toFixed(0) + ' ' + this.trueWindMaxHistoric.toFixed(0) + ' ' + portMin.toFixed(0) + ' ' + portMax.toFixed(0)); | ||
let portMinX = 160 * Math.sin((portMin*Math.PI)/180) + 250; //250 is middle | ||
let portMinY = (160 * Math.cos((portMin*Math.PI)/180)*-1) + 250; //-1 since SVG 0 is at top | ||
let portMidX = 160 * Math.sin((portMid*Math.PI)/180) + 250; //250 is middle | ||
let portMidY = (160 * Math.cos((portMid*Math.PI)/180)*-1) + 250; //-1 since SVG 0 is at top | ||
let portMaxX = 160 * Math.sin((portMax*Math.PI)/180) + 250; //250 is middle | ||
let portMaxY = (160 * Math.cos((portMax*Math.PI)/180)*-1) + 250; //-1 since SVG 0 is at top | ||
//calculate angles for arc options https://stackoverflow.com/questions/21816286/svg-arc-how-to-determine-sweep-and-larg-arc-flags-given-start-end-via-point | ||
let portLgArcFl = Math.abs(angle([portMinX,portMinY],[portMidX,portMidY],[portMaxX,portMaxY])) > Math.PI/2 ? 0 : 1; | ||
let portSweepFl = angle([portMaxX,portMaxY],[portMinX,portMinY],[portMidX,portMidY]) > 0 ? 0 : 1; | ||
this.portWindSectorPath = 'M 250,250 L ' + portMinX + ',' + portMinY + ' A 160,160 0 ' + portLgArcFl + ' ' + portSweepFl + ' ' + portMaxX + ',' + portMaxY +' z'; | ||
////////// | ||
let stbdMin = this.addHeading(this.addHeading(this.trueWindMinHistoric, (this.newCompassRotate*-1)), (this.laylineAngle)); | ||
let stbdMid = this.addHeading(this.addHeading(this.trueWindMidHistoric, (this.newCompassRotate*-1)), (this.laylineAngle)); | ||
let stbdMax = this.addHeading(this.addHeading(this.trueWindMaxHistoric, (this.newCompassRotate*-1)), (this.laylineAngle)); | ||
let stbdMinX = 160 * Math.sin((stbdMin*Math.PI)/180) + 250; //250 is middle | ||
let stbdMinY = (160 * Math.cos((stbdMin*Math.PI)/180)*-1) + 250; //-1 since SVG 0 is at top | ||
let stbdMidX = 160 * Math.sin((stbdMid*Math.PI)/180) + 250; //250 is middle | ||
let stbdMidY = (160 * Math.cos((stbdMid*Math.PI)/180)*-1) + 250; //-1 since SVG 0 is at top | ||
let stbdMaxX = 160 * Math.sin((stbdMax*Math.PI)/180) + 250; //250 is middle | ||
let stbdMaxY = (160 * Math.cos((stbdMax*Math.PI)/180)*-1) + 250; //-1 since SVG 0 is at top | ||
let stbdLgArcFl = Math.abs(angle([stbdMinX,stbdMinY],[stbdMidX,stbdMidY],[stbdMaxX,stbdMaxY])) > Math.PI/2 ? 0 : 1; | ||
let stbdSweepFl = angle([stbdMaxX,stbdMaxY],[stbdMinX,stbdMinY],[stbdMidX,stbdMidY]) > 0 ? 0 : 1; | ||
this.stbdWindSectorPath = 'M 250,250 L ' + stbdMinX + ',' + stbdMinY + ' A 160,160 0 ' + stbdLgArcFl + ' ' + stbdSweepFl + ' ' + stbdMaxX + ',' + stbdMaxY +' z'; | ||
} | ||
addHeading(h1: number, h2: number) { | ||
let h3 = h1 + h2; | ||
while (h3 > 359) { h3 = h3 - 359; } | ||
while (h3 < 0) { h3 = h3 + 359; } | ||
return h3; | ||
} | ||
} | ||
@@ -109,0 +201,0 @@ |
@@ -68,2 +68,5 @@ import { Injectable } from '@angular/core'; | ||
"deg": Qty.swiftConverter('rad', 'deg') | ||
}, | ||
'ratio': { | ||
'%': function(v) { return v * 100 }, | ||
} | ||
@@ -70,0 +73,0 @@ |
@@ -0,0 +0,0 @@ import { Component, OnInit, Input, Inject, ComponentFactoryResolver, ComponentRef, ViewChild } from '@angular/core'; |
@@ -0,0 +0,0 @@ import { Component, OnInit, Input } from '@angular/core'; |
@@ -0,0 +0,0 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; |
import { Component, Input, OnInit, OnDestroy, Inject } from '@angular/core'; | ||
import { Subscription } from 'rxjs/Subscription'; | ||
import {MatDialog,MatDialogRef,MAT_DIALOG_DATA } from '@angular/material'; | ||
import { MatDialog } from '@angular/material'; | ||
import { SignalKService, pathObject } from '../signalk.service'; | ||
import { WidgetManagerService, IWidget } from '../widget-manager.service'; | ||
import { UnitConvertService } from '../unit-convert.service'; | ||
import { ModalWidgetComponent } from '../modal-widget/modal-widget.component'; | ||
import { WidgetManagerService, IWidget, IWidgetConfig } from '../widget-manager.service'; | ||
import { UnitsService } from '../units.service'; | ||
interface IWidgetConfig { | ||
gaugeType: string; | ||
signalKPath: string; | ||
signalKSource: string; | ||
label: string; | ||
unitGroup: string; | ||
unitName: string; | ||
barGraph: boolean; | ||
radialSize: string; | ||
minValue: number; | ||
maxValue: number; | ||
} | ||
const defaultConfig: IWidgetConfig = { | ||
widgetLabel: null, | ||
paths: { | ||
"gaugePath": { | ||
description: "Numeric Data", | ||
path: null, | ||
source: null, | ||
pathType: "number", | ||
} | ||
}, | ||
units: { | ||
"gaugePath": "unitless" | ||
}, | ||
selfPaths: true, | ||
gaugeType: 'linear', | ||
barGraph: false, // if linear/radial, is it digital? | ||
radialSize: 'full', | ||
minValue: 0, | ||
maxValue: 100, | ||
rotateFace: false, | ||
backgroundColor: 'carbon', | ||
frameColor: 'anthracite' | ||
}; | ||
@@ -36,6 +49,5 @@ | ||
converter = this.UnitConvertService.getConverter(); | ||
activeWidget: IWidget; | ||
config: IWidgetConfig; | ||
activeWidget: IWidget; | ||
dataValue: any = null; | ||
@@ -45,15 +57,2 @@ | ||
widgetConfig: IWidgetConfig = { | ||
gaugeType: 'linear', | ||
signalKPath: null, | ||
signalKSource: 'default', | ||
label: null, | ||
unitGroup: 'discreet', | ||
unitName: 'no unit', | ||
barGraph: false, | ||
radialSize: 'full', | ||
minValue: 0, | ||
maxValue: 100 | ||
} | ||
constructor( | ||
@@ -63,3 +62,3 @@ public dialog:MatDialog, | ||
private WidgetManagerService: WidgetManagerService, | ||
private UnitConvertService: UnitConvertService) { | ||
private UnitsService: UnitsService) { | ||
} | ||
@@ -71,7 +70,7 @@ | ||
// no data, let's set some! | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.widgetConfig); | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, defaultConfig); | ||
this.config = defaultConfig; // load default config. | ||
} else { | ||
this.widgetConfig = this.activeWidget.config; // load existing config. | ||
this.config = this.activeWidget.config; | ||
} | ||
this.subscribePath(); | ||
@@ -88,24 +87,7 @@ | ||
this.unsubscribePath(); | ||
if (this.widgetConfig.signalKPath === null) { return } // nothing to sub to... | ||
if (typeof(this.config.paths['gaugePath'].path) != 'string') { return } // nothing to sub to... | ||
this.valueSub = this.SignalKService.subscribePath(this.widgetUUID, this.widgetConfig.signalKPath).subscribe( | ||
pathObject => { | ||
if (pathObject === null) { | ||
return; // we will get null back if we subscribe to a path before the app knows about it. when it learns about it we will get first value | ||
} | ||
let source: string; | ||
if (this.widgetConfig.signalKSource == 'default') { | ||
source = pathObject.defaultSource; | ||
} else { | ||
source = this.widgetConfig.signalKSource; | ||
} | ||
if (pathObject.sources[source].value === null) { | ||
this.dataValue = null; | ||
} | ||
let value:number = pathObject.sources[source].value; | ||
let converted = this.converter[this.widgetConfig.unitGroup][this.widgetConfig.unitName](value); | ||
this.dataValue = converted; | ||
this.valueSub = this.SignalKService.subscribePath(this.widgetUUID, this.config.paths['gaugePath'].path, this.config.paths['gaugePath'].source).subscribe( | ||
newValue => { | ||
this.dataValue = this.UnitsService.convertUnit(this.config.units['gaugePath'], newValue); | ||
} | ||
@@ -119,3 +101,3 @@ ); | ||
this.valueSub = null; | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.widgetConfig.signalKPath) | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.config.paths['gaugePath'].path) | ||
} | ||
@@ -125,117 +107,23 @@ } | ||
openWidgetSettings() { | ||
//prepare current data | ||
let settingsData: IWidgetConfig = { | ||
gaugeType: this.widgetConfig.gaugeType, | ||
signalKPath: this.widgetConfig.signalKPath, | ||
signalKSource: this.widgetConfig.signalKSource, | ||
label: this.widgetConfig.label, | ||
unitGroup: this.widgetConfig.unitGroup, | ||
unitName: this.widgetConfig.unitName, | ||
barGraph: this.widgetConfig.barGraph, | ||
radialSize: this.widgetConfig.radialSize, | ||
minValue: this.widgetConfig.minValue, | ||
maxValue: this.widgetConfig.maxValue | ||
} | ||
let dialogRef = this.dialog.open(WidgetGaugeModalComponent, { | ||
width: '650px', | ||
data: settingsData | ||
}); | ||
dialogRef.afterClosed().subscribe(result => { | ||
// save new settings | ||
if (result) { | ||
console.debug("Updating widget config"); | ||
this.unsubscribePath();//unsub now as we will change variables so wont know what was subbed before... | ||
this.widgetConfig.gaugeType = result.gaugeType; | ||
this.widgetConfig.signalKPath = result.signalKPath; | ||
this.widgetConfig.signalKSource = result.signalKSource; | ||
this.widgetConfig.label = result.label; | ||
this.widgetConfig.unitGroup = result.unitGroup; | ||
this.widgetConfig.unitName = result.unitName; | ||
this.widgetConfig.barGraph = result.barGraph; | ||
this.widgetConfig.radialSize = result.radialSize; | ||
this.widgetConfig.minValue = result.minValue; | ||
this.widgetConfig.maxValue = result.maxValue; | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.widgetConfig); | ||
this.subscribePath(); | ||
} | ||
}); | ||
} | ||
} | ||
let dialogRef = this.dialog.open(ModalWidgetComponent, { | ||
width: '80%', | ||
data: this.config | ||
}); | ||
dialogRef.afterClosed().subscribe(result => { | ||
// save new settings | ||
if (result) { | ||
console.log(result); | ||
this.unsubscribePath();//unsub now as we will change variables so wont know what was subbed before... | ||
this.config = result; | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.config); | ||
this.subscribePath(); | ||
} | ||
@Component({ | ||
selector: 'gauge-widget-modal', | ||
templateUrl: './widget-gauge.modal.html', | ||
styleUrls: ['./widget-gauge.component.css'] | ||
}) | ||
export class WidgetGaugeModalComponent implements OnInit { | ||
}); | ||
settingsData: IWidgetConfig; | ||
selfPaths: boolean = true; | ||
availablePaths: Array<string> = []; | ||
availableSources: Array<string>; | ||
availableUnitGroups: string[]; | ||
availableUnitNames: string[]; | ||
} | ||
converter: Object = this.UnitConvertService.getConverter(); | ||
constructor( | ||
private SignalKService: SignalKService, | ||
private UnitConvertService: UnitConvertService, | ||
public dialogRef:MatDialogRef<WidgetGaugeModalComponent>, | ||
@Inject(MAT_DIALOG_DATA) public data: any) { } | ||
ngOnInit() { | ||
this.settingsData = this.data; | ||
//populate available choices | ||
this.availablePaths = this.SignalKService.getPathsByType('number').sort(); | ||
if (this.availablePaths.includes(this.settingsData.signalKPath)) { | ||
this.settingsDataUpdatePath(); //TODO: this wipes out existing config, not good when editing existing config... | ||
} | ||
this.availableUnitGroups = Object.keys(this.converter); | ||
if (this.converter.hasOwnProperty(this.settingsData.unitGroup)) { | ||
this.availableUnitNames = Object.keys(this.converter[this.settingsData.unitGroup]); | ||
} | ||
} | ||
settingsDataUpdatePath() { // called when we choose a new path. resets the rest with default info of this path | ||
let pathObject = this.SignalKService.getPathObject(this.settingsData.signalKPath); | ||
if (pathObject === null) { return; } | ||
this.availableSources = ['default'].concat(Object.keys(pathObject.sources)); | ||
this.settingsData.signalKSource = 'default'; | ||
if (pathObject.meta) { | ||
if (typeof(pathObject.meta.abbreviation) == 'string') { | ||
this.settingsData.label = pathObject.meta.abbreviation; | ||
} else if (typeof(pathObject.meta.label) == 'string') { | ||
this.settingsData.label = pathObject.meta.label; | ||
} else { | ||
this.settingsData.label = this.settingsData.signalKPath; // who knows? | ||
} | ||
} else { | ||
this.settingsData.label = this.settingsData.signalKPath;// who knows? | ||
} | ||
} | ||
updateUnitType() { | ||
if (this.converter.hasOwnProperty(this.settingsData.unitGroup)) { | ||
this.availableUnitNames = Object.keys(this.converter[this.settingsData.unitGroup]); | ||
// select first name | ||
this.settingsData.unitName = this.availableUnitNames[0]; | ||
} | ||
} | ||
submitConfig() { | ||
this.dialogRef.close(this.settingsData); | ||
} | ||
} |
import { Component, OnInit, ViewChild, ElementRef, OnDestroy, Input, Inject } from '@angular/core'; | ||
import Chart from 'chart.js'; | ||
import {MatDialog,MatDialogRef,MAT_DIALOG_DATA } from '@angular/material'; | ||
import * as Chart from 'chart.js'; | ||
import { MatDialog } from '@angular/material'; | ||
import { Subscription } from 'rxjs/Subscription'; | ||
import { ModalWidgetComponent } from '../modal-widget/modal-widget.component'; | ||
import { dataPoint, DataSetService } from '../data-set.service'; | ||
import { WidgetManagerService, IWidget } from '../widget-manager.service'; | ||
import { UnitConvertService } from '../unit-convert.service'; | ||
import { WidgetManagerService, IWidget, IWidgetConfig } from '../widget-manager.service'; | ||
import { UnitsService } from '../units.service'; | ||
const defaultConfig: IWidgetConfig = { | ||
widgetLabel: null, | ||
units: { | ||
"dataset": "unitless" | ||
}, | ||
dataSetUUID: null, | ||
invertData: false, | ||
displayMinMax: false, | ||
includeZero: true, | ||
minValue: null, | ||
maxValue: null | ||
}; | ||
interface IHistoricalWidgetSettings { | ||
selectedDataSet: string; | ||
label: string; | ||
selectedUnitGroup: string; | ||
selectedUnitName: string; | ||
numDecimal: number; | ||
invertData: boolean; | ||
displayMinMax: boolean; | ||
animateGraph: boolean; | ||
suggestedMin: number; | ||
suggestedMax: number; | ||
includeZero: boolean; | ||
} | ||
interface widgetConfig { | ||
dataSetUUID: string; | ||
label: string; | ||
unitGroup: string; | ||
unitName: string; | ||
numDecimal: number; // number of decimal places if a number | ||
invertData: boolean; | ||
displayMinMax: boolean; | ||
animateGraph: boolean; | ||
suggestedMin: number; | ||
suggestedMax: number; | ||
includeZero: boolean; | ||
} | ||
interface IDataSetOptions { | ||
@@ -61,3 +47,4 @@ label: string; | ||
activeWidget: IWidget; | ||
config: IWidgetConfig; | ||
chartCtx; | ||
@@ -72,22 +59,5 @@ chart = null; | ||
converter = this.UnitConvertService.getConverter(); | ||
dataSetSub: Subscription = null; | ||
widgetConfig: widgetConfig = { | ||
dataSetUUID: null, | ||
label: '', | ||
unitGroup: 'discreet', | ||
unitName: 'no unit', | ||
numDecimal: 2, | ||
invertData: false, | ||
displayMinMax: false, | ||
animateGraph: false, | ||
suggestedMin: null, | ||
suggestedMax: null, | ||
includeZero: true | ||
} | ||
constructor( | ||
@@ -97,307 +67,220 @@ public dialog:MatDialog, | ||
private WidgetManagerService: WidgetManagerService, | ||
private UnitConvertService: UnitConvertService | ||
private UnitsService: UnitsService | ||
) { } | ||
ngOnInit() { | ||
this.activeWidget = this.WidgetManagerService.getWidget(this.widgetUUID); | ||
if (this.activeWidget.config === null) { | ||
// no data, let's set some! | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.widgetConfig); | ||
} else { | ||
this.widgetConfig = this.activeWidget.config; // load existing config. | ||
} | ||
ngOnInit() { | ||
this.activeWidget = this.WidgetManagerService.getWidget(this.widgetUUID); | ||
if (this.activeWidget.config === null) { | ||
// no data, let's set some! | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, defaultConfig); | ||
this.config = defaultConfig; // load default config. | ||
} else { | ||
this.config = this.activeWidget.config; | ||
} | ||
//TODO, this only works on chart init... need to find when theme changes... | ||
this.textColor = window.getComputedStyle(this.lineGraph.nativeElement).color; | ||
this.chartCtx = this.lineGraph.nativeElement.getContext('2d'); | ||
this.startChart(); | ||
setTimeout(this.subscribeDataSet(),1000);//TODO, see why destroy called before we even get subbed (or just after...) | ||
//TODO, this only works on chart init... need to find when theme changes... | ||
this.textColor = window.getComputedStyle(this.lineGraph.nativeElement).color; | ||
this.chartCtx = this.lineGraph.nativeElement.getContext('2d'); | ||
this.startChart(); | ||
this.subscribeDataSet(); | ||
//setTimeout(this.subscribeDataSet(),1000);//TODO, see why destroy called before we even get subbed (or just after...) | ||
} | ||
} | ||
ngOnDestroy() { | ||
if (this.chart !== null) { | ||
//this.chart.destroy(); // doesn't seem to be needed since chart is destoryed when destroying component. was giving errors. (maybe html was destroyed before this is called?) | ||
} | ||
this.unsubscribeDataSet(); | ||
console.log("stopped Sub"); | ||
} | ||
ngOnDestroy() { | ||
if (this.chart !== null) { | ||
//this.chart.destroy(); // doesn't seem to be needed since chart is destoryed when destroying component. was giving errors. (maybe html was destroyed before this is called?) | ||
} | ||
this.unsubscribeDataSet(); | ||
console.log("stopped Sub"); | ||
} | ||
startChart() { | ||
if (this.chart !== null) { | ||
this.chart.destroy(); | ||
startChart() { | ||
if (this.chart !== null) { | ||
this.chart.destroy(); | ||
} | ||
// Setup DataSets | ||
let ds: IDataSetOptions[] = [ | ||
{ | ||
label: this.config.widgetLabel + '-Avg.', | ||
data: this.chartDataAvg, | ||
fill: 'false', | ||
//borderWidth: 1 | ||
borderColor: this.textColor | ||
} | ||
// Setup DataSets | ||
let ds: IDataSetOptions[] = [ | ||
]; | ||
if (this.config.displayMinMax) { | ||
ds.push( | ||
{ | ||
label: this.widgetConfig.label + '-Avg.', | ||
data: this.chartDataAvg, | ||
fill: 'false', | ||
label: this.config.widgetLabel + '-Min', | ||
data: this.chartDataMin, | ||
fill: '+1', | ||
//borderWidth: 1 | ||
borderColor: this.textColor | ||
borderColor: this.textColor, | ||
borderDash: [ 5, 5 ] | ||
}, | ||
{ | ||
label: this.config.widgetLabel + '-Max', | ||
data: this.chartDataMax, | ||
fill: '-1', | ||
//borderWidth: 1 | ||
borderColor: this.textColor, | ||
borderDash: [ 5, 5 ] | ||
} | ||
); | ||
} | ||
//setup Options | ||
let yAxisTickOptions = {}; | ||
if (this.config.includeZero) { | ||
yAxisTickOptions['beginAtZero'] = true; | ||
} | ||
if (this.config.minValue !== null) { | ||
yAxisTickOptions['suggestedMin'] = this.config.minValue; | ||
} | ||
if (this.config.maxValue !== null) { | ||
yAxisTickOptions['suggestedMax'] = this.config.maxValue; | ||
} | ||
this.chart = new Chart(this.chartCtx,{ | ||
type: 'line', | ||
data: { | ||
datasets: ds | ||
}, | ||
options: { | ||
maintainAspectRatio: false, | ||
scales: { | ||
yAxes: [{ | ||
scaleLabel: { | ||
labelString: 'feet', | ||
}, | ||
position: 'right', | ||
ticks: yAxisTickOptions | ||
}], | ||
xAxes: [{ | ||
type: 'time', | ||
time: { | ||
minUnit: 'second', | ||
round: 'second', | ||
displayFormats: 'YY', //no mater what it seems to default to full time... | ||
}, | ||
ticks: { | ||
// minRotation: 15, | ||
callback: function(value) { //TODO, left pad 0 for min/sec | ||
let tickTime = Date.parse(value); | ||
let nowTime = Date.now(); | ||
let timeDiff = Math.floor((nowTime - tickTime)/1000); | ||
if (timeDiff < 60) { | ||
return timeDiff.toString() + " sec ago"; | ||
} else if (timeDiff < 3600) { | ||
let minDiff = Math.floor(timeDiff / 60); | ||
let secDiff = timeDiff % 60; | ||
return (minDiff.toString() + ":" +secDiff.toString() + " mins ago"); | ||
} else if (timeDiff < 86400) { | ||
let hourDiff = Math.floor(timeDiff / 3600); | ||
return (hourDiff.toString() + " hours ago"); | ||
} else { | ||
let dayDiff = Math.floor(timeDiff / 86400); | ||
return (dayDiff.toString() + " days ago"); | ||
} | ||
} | ||
} | ||
}] | ||
} | ||
]; | ||
if (this.widgetConfig.displayMinMax) { | ||
ds.push( | ||
{ | ||
label: this.widgetConfig.label + '-Min', | ||
data: this.chartDataMin, | ||
fill: '+1', | ||
//borderWidth: 1 | ||
borderColor: this.textColor, | ||
borderDash: [ 5, 5 ] | ||
}, | ||
{ | ||
label: this.widgetConfig.label + '-Max', | ||
data: this.chartDataMax, | ||
fill: '-1', | ||
//borderWidth: 1 | ||
borderColor: this.textColor, | ||
borderDash: [ 5, 5 ] | ||
} | ||
); | ||
} | ||
//setup Options | ||
let yAxisTickOptions = {}; | ||
if (this.widgetConfig.includeZero) { | ||
yAxisTickOptions['beginAtZero'] = true; | ||
} | ||
if (this.widgetConfig.suggestedMin !== null) { | ||
yAxisTickOptions['suggestedMin'] = this.widgetConfig.suggestedMin; | ||
} | ||
if (this.widgetConfig.suggestedMax !== null) { | ||
yAxisTickOptions['suggestedMax'] = this.widgetConfig.suggestedMax; | ||
} | ||
this.chart = new Chart(this.chartCtx,{ | ||
type: 'line', | ||
data: { | ||
datasets: ds | ||
}, | ||
options: { | ||
maintainAspectRatio: false, | ||
scales: { | ||
yAxes: [{ | ||
scaleLabel: { | ||
labelString: 'feet', | ||
}, | ||
position: 'right', | ||
ticks: yAxisTickOptions | ||
}], | ||
xAxes: [{ | ||
type: 'time', | ||
time: { | ||
minUnit: 'second', | ||
round: 'second', | ||
displayFormats: 'YY', //no mater what it seems to default to full time... | ||
}, | ||
ticks: { | ||
// minRotation: 15, | ||
callback: function(value) { //TODO, left pad 0 for min/sec | ||
let tickTime = Date.parse(value); | ||
let nowTime = Date.now(); | ||
let timeDiff = Math.floor((nowTime - tickTime)/1000); | ||
if (timeDiff < 60) { | ||
return timeDiff.toString() + " sec ago"; | ||
} else if (timeDiff < 3600) { | ||
let minDiff = Math.floor(timeDiff / 60); | ||
let secDiff = timeDiff % 60; | ||
return (minDiff.toString() + ":" +secDiff.toString() + " mins ago"); | ||
} else if (timeDiff < 86400) { | ||
let hourDiff = Math.floor(timeDiff / 3600); | ||
return (hourDiff.toString() + " hours ago"); | ||
} else { | ||
let dayDiff = Math.floor(timeDiff / 86400); | ||
return (dayDiff.toString() + " days ago"); | ||
} | ||
} | ||
} | ||
}] | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
subscribeDataSet() { | ||
//this.unsubscribeDataSet(); | ||
if (this.widgetConfig.dataSetUUID === null) { return } // nothing to sub to... | ||
this.dataSetSub = this.DataSetService.subscribeDataSet(this.widgetUUID, this.widgetConfig.dataSetUUID).subscribe( | ||
dataSet => { | ||
if (dataSet === null) { | ||
subscribeDataSet() { | ||
//this.unsubscribeDataSet(); | ||
if (this.config.dataSetUUID === null) { return } // nothing to sub to... | ||
this.dataSetSub = this.DataSetService.subscribeDataSet(this.widgetUUID, this.config.dataSetUUID).subscribe( | ||
dataSet => { | ||
if (dataSet === null) { | ||
return; // we will get null back if we subscribe to a dataSet before the app has started it.when it learns about it we will get first value | ||
} | ||
let invert = 1; | ||
if (this.config.invertData) { invert = -1; } | ||
//Avg | ||
this.chartDataAvg = []; | ||
for (let i=0;i<dataSet.length;i++){ | ||
if (dataSet[i].average === null) { | ||
this.chartDataAvg.push({t: dataSet[i].timestamp, y: null }); | ||
continue; | ||
} | ||
let invert = 1; | ||
if (this.widgetConfig.invertData) { invert = -1; } | ||
//Avg | ||
this.chartDataAvg = []; | ||
this.chartDataAvg.push({ | ||
t: dataSet[i].timestamp, | ||
y: (this.UnitsService.convertUnit(this.config.units['dataset'], dataSet[i].average) * invert).toFixed(2) | ||
}); | ||
} | ||
this.chart.config.data.datasets[0].data = this.chartDataAvg; | ||
//min/max | ||
if (this.config.displayMinMax) { | ||
this.chartDataMin = []; | ||
this.chartDataMax = []; | ||
for (let i=0;i<dataSet.length;i++){ | ||
//process datapoint and add it to our chart. | ||
if (dataSet[i].average === null) { | ||
this.chartDataAvg.push({t: dataSet[i].timestamp, y: null }); | ||
continue; | ||
this.chartDataMin.push({t: dataSet[i].timestamp, y: null }); | ||
this.chartDataMax.push({t: dataSet[i].timestamp, y: null }); | ||
} else { | ||
this.chartDataMin.push({ | ||
t: dataSet[i].timestamp, | ||
y: (this.UnitsService.convertUnit(this.config.units['dataset'], dataSet[i].minValue) * invert).toFixed(2) | ||
}); | ||
this.chartDataMax.push({ | ||
t: dataSet[i].timestamp, | ||
y: (this.UnitsService.convertUnit(this.config.units['dataset'], dataSet[i].maxValue) * invert).toFixed(2) | ||
}); | ||
} | ||
this.chartDataAvg.push({ | ||
t: dataSet[i].timestamp, | ||
y: this.converter[this.widgetConfig.unitGroup][this.widgetConfig.unitName](dataSet[i].average).toFixed(this.widgetConfig.numDecimal)*invert | ||
}); | ||
} | ||
this.chart.config.data.datasets[0].data = this.chartDataAvg; | ||
//min/max | ||
if (this.widgetConfig.displayMinMax) { | ||
this.chartDataMin = []; | ||
this.chartDataMax = []; | ||
for (let i=0;i<dataSet.length;i++){ | ||
//process datapoint and add it to our chart. | ||
if (dataSet[i].average === null) { | ||
this.chartDataMin.push({t: dataSet[i].timestamp, y: null }); | ||
this.chartDataMax.push({t: dataSet[i].timestamp, y: null }); | ||
} else { | ||
this.chartDataMin.push({ | ||
t: dataSet[i].timestamp, | ||
y: this.converter[this.widgetConfig.unitGroup][this.widgetConfig.unitName](dataSet[i].minValue).toFixed(this.widgetConfig.numDecimal)*invert | ||
}); | ||
this.chartDataMax.push({ | ||
t: dataSet[i].timestamp, | ||
y: this.converter[this.widgetConfig.unitGroup][this.widgetConfig.unitName](dataSet[i].maxValue).toFixed(this.widgetConfig.numDecimal)*invert | ||
}); | ||
} | ||
} | ||
this.chart.config.data.datasets[1].data = this.chartDataMin; | ||
this.chart.config.data.datasets[2].data = this.chartDataMax; | ||
} | ||
this.chart.config.data.datasets[1].data = this.chartDataMin; | ||
this.chart.config.data.datasets[2].data = this.chartDataMax; | ||
} | ||
if (this.widgetConfig.animateGraph) { | ||
this.chart.update(); | ||
} else { | ||
this.chart.update(0); | ||
} | ||
} | ||
); | ||
} | ||
//if (this.widgetConfig.animateGraph) { | ||
// this.chart.update(); | ||
//} else { | ||
this.chart.update(0); | ||
//} | ||
} | ||
); | ||
} | ||
unsubscribeDataSet() { | ||
if (this.dataSetSub !== null) { | ||
this.dataSetSub.unsubscribe(); | ||
this.dataSetSub = null; | ||
} | ||
} | ||
unsubscribeDataSet() { | ||
if (this.dataSetSub !== null) { | ||
this.dataSetSub.unsubscribe(); | ||
this.dataSetSub = null; | ||
} | ||
} | ||
openWidgetSettings() { | ||
openWidgetSettings(content) { | ||
let dialogRef = this.dialog.open(ModalWidgetComponent, { | ||
width: '80%', | ||
data: this.config | ||
}); | ||
//prepare current data | ||
let settingsData: IHistoricalWidgetSettings = { | ||
selectedDataSet: this.widgetConfig.dataSetUUID, | ||
label: this.widgetConfig.label, | ||
numDecimal: this.widgetConfig.numDecimal, | ||
selectedUnitGroup: this.widgetConfig.unitGroup, | ||
selectedUnitName: this.widgetConfig.unitName, | ||
invertData: this.widgetConfig.invertData, | ||
displayMinMax: this.widgetConfig.displayMinMax, | ||
animateGraph: this.widgetConfig.animateGraph, | ||
suggestedMin: this.widgetConfig.suggestedMin, | ||
suggestedMax: this.widgetConfig.suggestedMax, | ||
includeZero: this.widgetConfig.includeZero | ||
}; | ||
let dialogRef = this.dialog.open(WidgetHistoricalModalComponent, { | ||
width: '650px', | ||
data: settingsData | ||
}); | ||
dialogRef.afterClosed().subscribe(result => { | ||
// save new settings | ||
if (result) { | ||
console.log(result); | ||
this.config = result; | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.config); | ||
this.startChart(); //need to recreate chart to update options :P | ||
this.subscribeDataSet(); | ||
} | ||
}); | ||
dialogRef.afterClosed().subscribe(result => { | ||
// save new settings | ||
if (result) { | ||
this.widgetConfig.dataSetUUID = result.selectedDataSet; | ||
this.widgetConfig.label = result.label; | ||
this.widgetConfig.unitGroup = result.selectedUnitGroup; | ||
this.widgetConfig.unitName = result.selectedUnitName; | ||
this.widgetConfig.numDecimal = result.numDecimal; | ||
this.widgetConfig.invertData = result.invertData; | ||
this.widgetConfig.displayMinMax = result.displayMinMax; | ||
this.widgetConfig.animateGraph = result.animateGraph; | ||
this.widgetConfig.includeZero = result.includeZero; | ||
if (typeof(result.suggestedMin) == 'number') { | ||
this.widgetConfig.suggestedMin = result.suggestedMin; | ||
} else { | ||
this.widgetConfig.suggestedMin = null; | ||
} | ||
if (typeof(result.suggestedMax) == 'number') { | ||
this.widgetConfig.suggestedMax = result.suggestedMax; | ||
} else { | ||
this.widgetConfig.suggestedMax = null; | ||
} | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.widgetConfig); | ||
this.startChart(); //need to recreate chart to update options :P | ||
this.subscribeDataSet(); | ||
} | ||
}); | ||
} | ||
} | ||
@Component({ | ||
selector: 'historical-widget-modal', | ||
templateUrl: './widget-historical.modal.html', | ||
styleUrls: ['./widget-historical.component.css'] | ||
}) | ||
export class WidgetHistoricalModalComponent implements OnInit { | ||
settingsData: IHistoricalWidgetSettings; | ||
availableDataSets: string[]; | ||
availableUnitGroups: string[]; | ||
availableUnitNames: string[]; | ||
converter: Object = this.UnitConvertService.getConverter(); | ||
constructor( | ||
private UnitConvertService: UnitConvertService, | ||
private DataSetService: DataSetService, | ||
public dialogRef:MatDialogRef<WidgetHistoricalModalComponent>, | ||
@Inject(MAT_DIALOG_DATA) public data: any) { } | ||
ngOnInit() { | ||
this.settingsData = this.data; | ||
this.availableUnitGroups = Object.keys(this.converter); | ||
if (this.converter.hasOwnProperty(this.settingsData.selectedUnitGroup)) { | ||
this.availableUnitNames = Object.keys(this.converter[this.settingsData.selectedUnitGroup]); | ||
} | ||
this.availableDataSets = this.DataSetService.getDataSets().sort(); | ||
} | ||
updateUnitType() { | ||
this.availableUnitNames = Object.keys(this.converter[this.settingsData.selectedUnitGroup]); | ||
this.settingsData.selectedUnitName = this.availableUnitNames[0]; | ||
} | ||
submitConfig() { | ||
this.dialogRef.close(this.settingsData); | ||
} | ||
} |
@@ -45,8 +45,8 @@ import { Injectable } from '@angular/core'; | ||
}, | ||
// { | ||
// name: 'WidgetStateComponent', | ||
// componentName: WidgetStateComponent, | ||
// description: 'State (boolean) Value', | ||
// }, | ||
{ | ||
name: 'WidgetStateComponent', | ||
componentName: WidgetStateComponent, | ||
description: 'State (boolean) Value', | ||
}, | ||
{ | ||
name: 'WidgetGaugeComponent', | ||
@@ -53,0 +53,0 @@ componentName: WidgetGaugeComponent, |
@@ -10,6 +10,53 @@ import { Injectable } from '@angular/core'; | ||
type: string; | ||
config: any; | ||
config: IWidgetConfig; | ||
} | ||
export interface IWidgetConfig { | ||
paths?: { | ||
[key: string]: ISignalKPathInfo; | ||
} | ||
units?: { | ||
[key: string]: string; // key should match key in paths, specifies unit for that path | ||
} | ||
widgetLabel?: string; | ||
selfPaths?: boolean; | ||
//numeric data | ||
numDecimal?: number; // number of decimal places if a number | ||
numInt?: number; | ||
//Wind Gague data | ||
windSectorEnable?: boolean; | ||
windSectorWindowSeconds?: number; | ||
laylineEnable?: boolean; | ||
laylineAngle?: number; | ||
//gauge Data | ||
gaugeType?: string; | ||
barGraph?: boolean; | ||
radialSize?: string; | ||
minValue?: number; | ||
maxValue?: number; | ||
rotateFace?: boolean; | ||
backgroundColor?: string; | ||
frameColor?: string; | ||
//Historical | ||
dataSetUUID?: string; | ||
invertData?: boolean; | ||
displayMinMax?: boolean; | ||
animateGraph?: boolean; | ||
includeZero?: boolean; | ||
} | ||
interface ISignalKPathInfo { | ||
description: string; | ||
path: string; //can be null or set | ||
source: string; //can be null or set | ||
pathType: string; | ||
} | ||
@Injectable() | ||
@@ -16,0 +63,0 @@ export class WidgetManagerService { |
import { Component, Input, OnInit, OnDestroy, Inject, ViewChild, ElementRef, AfterViewChecked } from '@angular/core'; | ||
import { Subscription } from 'rxjs/Subscription'; | ||
import {MatDialog,MatDialogRef,MAT_DIALOG_DATA } from '@angular/material'; | ||
import { MatDialog,MatDialogRef,MAT_DIALOG_DATA } from '@angular/material'; | ||
import { ModalWidgetComponent } from '../modal-widget/modal-widget.component'; | ||
import { SignalKService, pathObject } from '../signalk.service'; | ||
import { WidgetManagerService, IWidget } from '../widget-manager.service'; | ||
import { UnitConvertService } from '../unit-convert.service'; | ||
import { WidgetManagerService, IWidget, IWidgetConfig } from '../widget-manager.service'; | ||
import { UnitsService } from '../units.service'; | ||
import { isNumeric } from 'rxjs/util/isNumeric'; | ||
interface IWidgetConfig { | ||
signalKPath: string; | ||
signalKSource: string; | ||
label: string; | ||
unitGroup: string; | ||
unitName: string; | ||
numDecimal: number; // number of decimal places if a number | ||
numInt: number; | ||
} | ||
const defaultConfig: IWidgetConfig = { | ||
widgetLabel: null, | ||
paths: { | ||
"numericPath": { | ||
description: "Numeric Data", | ||
path: null, | ||
source: null, | ||
pathType: "number", | ||
} | ||
}, | ||
units: { | ||
"numericPath": "unitless" | ||
}, | ||
selfPaths: true, | ||
numDecimal: 1, | ||
numInt: 1 | ||
}; | ||
@Component({ | ||
@@ -31,21 +43,11 @@ selector: 'app-widget-numeric', | ||
@ViewChild('wrapperDiv') wrapperDiv: ElementRef; | ||
activeWidget: IWidget; | ||
config: IWidgetConfig; | ||
converter = this.UnitConvertService.getConverter(); | ||
activeWidget: IWidget; | ||
dataValue: any = null; | ||
dataTimestamp: number = Date.now(); | ||
widgetConfig: IWidgetConfig = { | ||
signalKPath: null, | ||
signalKSource: 'default', | ||
label: null, | ||
unitGroup: 'discreet', | ||
unitName: 'no unit', | ||
numDecimal: 2, | ||
numInt: 2 | ||
} | ||
//subs | ||
@@ -61,3 +63,3 @@ valueSub: Subscription = null; | ||
private WidgetManagerService: WidgetManagerService, | ||
private UnitConvertService: UnitConvertService) { | ||
private UnitsService: UnitsService) { | ||
} | ||
@@ -69,5 +71,6 @@ | ||
// no data, let's set some! | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.widgetConfig); | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, defaultConfig); | ||
this.config = defaultConfig; // load default config. | ||
} else { | ||
this.widgetConfig = this.activeWidget.config; // load existing config. | ||
this.config = this.activeWidget.config; | ||
} | ||
@@ -77,2 +80,3 @@ this.subscribePath(); | ||
this.canvasCtx = this.canvasEl.nativeElement.getContext('2d'); | ||
this.updateCanvas(); | ||
} | ||
@@ -97,2 +101,3 @@ | ||
} | ||
this.updateCanvas(); | ||
} | ||
@@ -102,26 +107,7 @@ | ||
this.unsubscribePath(); | ||
if (this.widgetConfig.signalKPath === null) { return } // nothing to sub to... | ||
if (typeof(this.config.paths['numericPath'].path) != 'string') { return } // nothing to sub to... | ||
this.valueSub = this.SignalKService.subscribePath(this.widgetUUID, this.widgetConfig.signalKPath).subscribe( | ||
pathObject => { | ||
if (pathObject === null) { | ||
return; // we will get null back if we subscribe to a path before the app knows about it. when it learns about it we will get first value | ||
} | ||
let source: string; | ||
if (this.widgetConfig.signalKSource == 'default') { | ||
source = pathObject.defaultSource; | ||
} else { | ||
source = this.widgetConfig.signalKSource; | ||
} | ||
this.dataTimestamp = pathObject.sources[source].timestamp; | ||
if (pathObject.sources[source].value === null) { | ||
this.dataValue = null; | ||
return; | ||
} | ||
let value:number = pathObject.sources[source].value; | ||
let converted = this.converter[this.widgetConfig.unitGroup][this.widgetConfig.unitName](value); | ||
this.dataValue = converted.toFixed(this.widgetConfig.numDecimal); | ||
this.valueSub = this.SignalKService.subscribePath(this.widgetUUID, this.config.paths['numericPath'].path, this.config.paths['numericPath'].source).subscribe( | ||
newValue => { | ||
this.dataValue = this.UnitsService.convertUnit(this.config.units['numericPath'], newValue); | ||
this.updateCanvas(); | ||
@@ -136,3 +122,4 @@ } | ||
this.valueSub = null; | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.widgetConfig.signalKPath) | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.config.paths['numericPath'].path); | ||
} | ||
@@ -143,17 +130,5 @@ } | ||
//prepare current data | ||
let settingsData: IWidgetConfig = { | ||
signalKPath: this.widgetConfig.signalKPath, | ||
signalKSource: this.widgetConfig.signalKSource, | ||
label: this.widgetConfig.label, | ||
numDecimal: this.widgetConfig.numDecimal, | ||
numInt: this.widgetConfig.numInt, | ||
unitGroup: this.widgetConfig.unitGroup, | ||
unitName: this.widgetConfig.unitName | ||
} | ||
let dialogRef = this.dialog.open(WidgetNumericModalComponent, { | ||
width: '650px', | ||
data: settingsData | ||
let dialogRef = this.dialog.open(ModalWidgetComponent, { | ||
width: '80%', | ||
data: this.config | ||
}); | ||
@@ -164,13 +139,8 @@ | ||
if (result) { | ||
console.debug("Updating widget config"); | ||
console.log(result); | ||
this.unsubscribePath();//unsub now as we will change variables so wont know what was subbed before... | ||
this.widgetConfig.signalKPath = result.signalKPath; | ||
this.widgetConfig.signalKSource = result.signalKSource; | ||
this.widgetConfig.label = result.label; | ||
this.widgetConfig.unitGroup = result.unitGroup; | ||
this.widgetConfig.unitName = result.unitName; | ||
this.widgetConfig.numDecimal = result.numDecimal; | ||
this.widgetConfig.numInt = result.numInt; | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.widgetConfig); | ||
this.config = result; | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.config); | ||
this.subscribePath(); | ||
this.updateCanvas(); | ||
} | ||
@@ -208,4 +178,11 @@ | ||
let maxTextHeight = Math.floor(this.canvasEl.nativeElement.height - (this.canvasEl.nativeElement.height * 0.2)); | ||
let valueText = this.padValue(this.dataValue, this.widgetConfig.numInt, this.widgetConfig.numDecimal); | ||
//TODO: at high res.large area, this can take way too long :( (500ms+) | ||
let valueText; | ||
if (isNumeric(this.dataValue)) { | ||
valueText = this.padValue(this.dataValue.toFixed(this.config.numDecimal), this.config.numInt, this.config.numDecimal); | ||
} else { | ||
valueText = "--"; | ||
} | ||
//TODO: at high res.large area, this can take way too long :( (500ms+) (added skip by 10 which helps, still feel it could be better...) | ||
// set font small and make bigger until we hit a max. | ||
@@ -215,4 +192,10 @@ let fontSize = 1; | ||
this.canvasCtx.font = "bold " + fontSize.toString() + "px Arial"; // need to init it so we do loop at least once :) | ||
//first increase fontsize by 10, skips lots of loops. | ||
while ( (this.canvasCtx.measureText(valueText).width < maxTextWidth) && (fontSize < maxTextHeight)) { | ||
fontSize++; | ||
fontSize = fontSize + 10; | ||
this.canvasCtx.font = "bold " + fontSize.toString() + "px Arial"; | ||
} | ||
// now decrease by 1 to find the right size | ||
while ( (this.canvasCtx.measureText(valueText).width < maxTextWidth) && (fontSize < maxTextHeight)) { | ||
fontSize--; | ||
this.canvasCtx.font = "bold " + fontSize.toString() + "px Arial"; | ||
@@ -227,6 +210,6 @@ } | ||
drawTitle() { | ||
var maxTextWidth = Math.floor(this.canvasEl.nativeElement.width - (this.canvasEl.nativeElement.width * 0.8)); | ||
var maxTextWidth = Math.floor(this.canvasEl.nativeElement.width - (this.canvasEl.nativeElement.width * 0.2)); | ||
var maxTextHeight = Math.floor(this.canvasEl.nativeElement.height - (this.canvasEl.nativeElement.height * 0.8)); | ||
// set font small and make bigger until we hit a max. | ||
if (this.config.widgetLabel === null) { return; } | ||
var fontSize = 1; | ||
@@ -237,3 +220,3 @@ // get color | ||
this.canvasCtx.font = "bold " + fontSize.toString() + "px Arial"; // need to init it so we do loop at least once :) | ||
while ( (this.canvasCtx.measureText(this.widgetConfig.label).width < maxTextWidth) && (fontSize < maxTextHeight)) { | ||
while ( (this.canvasCtx.measureText(this.config.widgetLabel).width < maxTextWidth) && (fontSize < maxTextHeight)) { | ||
fontSize++; | ||
@@ -244,6 +227,7 @@ this.canvasCtx.font = "bold " + fontSize.toString() + "px Arial"; | ||
this.canvasCtx.textBaseline="top"; | ||
this.canvasCtx.fillText(this.widgetConfig.label,this.canvasEl.nativeElement.width*0.03,this.canvasEl.nativeElement.height*0.03, maxTextWidth); | ||
this.canvasCtx.fillText(this.config.widgetLabel,this.canvasEl.nativeElement.width*0.03,this.canvasEl.nativeElement.height*0.03, maxTextWidth); | ||
} | ||
drawUnit() { | ||
if (this.config.units['numericPath'] == 'unitless') { return; } | ||
var maxTextWidth = Math.floor(this.canvasEl.nativeElement.width - (this.canvasEl.nativeElement.width * 0.8)); | ||
@@ -256,3 +240,3 @@ var maxTextHeight = Math.floor(this.canvasEl.nativeElement.height - (this.canvasEl.nativeElement.height * 0.8)); | ||
this.canvasCtx.font = "bold " + fontSize.toString() + "px Arial"; // need to init it so we do loop at least once :) | ||
while ( (this.canvasCtx.measureText(this.widgetConfig.unitName).width < maxTextWidth) && (fontSize < maxTextHeight)) { | ||
while ( (this.canvasCtx.measureText(this.config.units['numericPath']).width < maxTextWidth) && (fontSize < maxTextHeight)) { | ||
fontSize++; | ||
@@ -263,3 +247,3 @@ this.canvasCtx.font = "bold " + fontSize.toString() + "px Arial"; | ||
this.canvasCtx.textBaseline="bottom"; | ||
this.canvasCtx.fillText(this.widgetConfig.unitName,this.canvasEl.nativeElement.width*0.97,this.canvasEl.nativeElement.height*0.97, maxTextWidth); | ||
this.canvasCtx.fillText(this.config.units['numericPath'],this.canvasEl.nativeElement.width*0.97,this.canvasEl.nativeElement.height*0.97, maxTextWidth); | ||
} | ||
@@ -300,74 +284,1 @@ | ||
} | ||
@Component({ | ||
selector: 'numeric-widget-modal', | ||
templateUrl: './widget-numeric.modal.html', | ||
styleUrls: ['./widget-numeric.component.css'] | ||
}) | ||
export class WidgetNumericModalComponent implements OnInit { | ||
settingsData: IWidgetConfig; | ||
selfPaths: boolean = true; | ||
availablePaths: Array<string> = []; | ||
availableSources: Array<string>; | ||
availableUnitGroups: string[]; | ||
availableUnitNames: string[]; | ||
converter: Object = this.UnitConvertService.getConverter(); | ||
constructor( | ||
private SignalKService: SignalKService, | ||
private UnitConvertService: UnitConvertService, | ||
public dialogRef:MatDialogRef<WidgetNumericModalComponent>, | ||
@Inject(MAT_DIALOG_DATA) public data: any) { } | ||
ngOnInit() { | ||
this.settingsData = this.data; | ||
//populate available choices | ||
this.availablePaths = this.SignalKService.getPathsByType('number').sort(); | ||
if (this.availablePaths.includes(this.settingsData.signalKPath)) { | ||
this.settingsDataUpdatePath(); //TODO: this wipes out existing config, not good when editing existing config... | ||
} | ||
this.availableUnitGroups = Object.keys(this.converter); | ||
if (this.converter.hasOwnProperty(this.settingsData.unitGroup)) { | ||
this.availableUnitNames = Object.keys(this.converter[this.settingsData.unitGroup]); | ||
} | ||
} | ||
settingsDataUpdatePath() { // called when we choose a new path. resets the rest with default info of this path | ||
let pathObject = this.SignalKService.getPathObject(this.settingsData.signalKPath); | ||
if (pathObject === null) { return; } | ||
this.availableSources = ['default'].concat(Object.keys(pathObject.sources)); | ||
this.settingsData.signalKSource = 'default'; | ||
this.settingsData.numDecimal = this.data.numDecimal; | ||
if (pathObject.meta) { | ||
if (typeof(pathObject.meta.abbreviation) == 'string') { | ||
this.settingsData.label = pathObject.meta.abbreviation; | ||
} else if (typeof(pathObject.meta.label) == 'string') { | ||
this.settingsData.label = pathObject.meta.label; | ||
} else { | ||
this.settingsData.label = this.settingsData.signalKPath; // who knows? | ||
} | ||
} else { | ||
this.settingsData.label = this.settingsData.signalKPath;// who knows? | ||
} | ||
} | ||
updateUnitType() { | ||
if (this.converter.hasOwnProperty(this.settingsData.unitGroup)) { | ||
this.availableUnitNames = Object.keys(this.converter[this.settingsData.unitGroup]); | ||
// select first name | ||
this.settingsData.unitName = this.availableUnitNames[0]; | ||
} | ||
} | ||
submitConfig() { | ||
this.dialogRef.close(this.settingsData); | ||
} | ||
} |
@@ -0,0 +0,0 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; |
@@ -5,11 +5,19 @@ import { Component, Input, OnInit, OnDestroy, Inject } from '@angular/core'; | ||
import { SignalKService, pathObject } from '../signalk.service'; | ||
import { WidgetManagerService, IWidget } from '../widget-manager.service'; | ||
import { ModalWidgetComponent } from '../modal-widget/modal-widget.component'; | ||
import { SignalKService } from '../signalk.service'; | ||
import { WidgetManagerService, IWidget, IWidgetConfig } from '../widget-manager.service'; | ||
interface widgetConfig { | ||
signalKPath: string; | ||
signalKSource: string; | ||
label: string; | ||
} | ||
const defaultConfig: IWidgetConfig = { | ||
widgetLabel: null, | ||
paths: { | ||
"boolPath": { | ||
description: "Boolean Data", | ||
path: null, | ||
source: null, | ||
pathType: "boolean", | ||
} | ||
}, | ||
selfPaths: true | ||
}; | ||
@@ -29,11 +37,7 @@ | ||
activeWidget: IWidget; | ||
config: IWidgetConfig; | ||
state: boolean = null; | ||
widgetConfig: widgetConfig = { | ||
signalKPath: null, | ||
signalKSource: 'default', | ||
label: null | ||
} | ||
constructor( | ||
@@ -49,6 +53,8 @@ public dialog:MatDialog, | ||
// no data, let's set some! | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.widgetConfig); | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, defaultConfig); | ||
this.config = defaultConfig; // load default config. | ||
} else { | ||
this.widgetConfig = this.activeWidget.config; // load existing config. | ||
this.config = this.activeWidget.config; | ||
} | ||
this.subscribePath(); | ||
@@ -63,23 +69,7 @@ } | ||
this.unsubscribePath(); | ||
if (this.widgetConfig.signalKPath === null) { return } // nothing to sub to... | ||
if (typeof(this.config.paths['boolPath'].path) != 'string') { return } // nothing to sub to... | ||
this.valueSub = this.SignalKService.subscribePath(this.widgetUUID, this.widgetConfig.signalKPath).subscribe( | ||
pathObject => { | ||
if (pathObject === null) { | ||
return; // we will get null back if we subscribe to a path before the app knows about it. when it learns about it we will get first value | ||
} | ||
let source: string; | ||
if (this.widgetConfig.signalKSource == 'default') { | ||
source = pathObject.defaultSource; | ||
} else { | ||
source = this.widgetConfig.signalKSource; | ||
} | ||
if (pathObject.sources[source].value === null) { | ||
this.state = null; | ||
} | ||
this.state = pathObject.sources[source].value; | ||
this.valueSub = this.SignalKService.subscribePath(this.widgetUUID, this.config.paths['boolPath'].path, this.config.paths['boolPath'].source).subscribe( | ||
newValue => { | ||
this.state = newValue; | ||
} | ||
@@ -95,3 +85,3 @@ ); | ||
this.valueSub = null; | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.widgetConfig.signalKPath) | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.config.paths['boolPath'].path) | ||
} | ||
@@ -102,23 +92,17 @@ } | ||
//prepare current data | ||
let settingsData: widgetConfig = { | ||
signalKPath: this.widgetConfig.signalKPath, | ||
signalKSource: this.widgetConfig.signalKSource, | ||
label: this.widgetConfig.label | ||
} | ||
let dialogRef = this.dialog.open(WidgetStateModalComponent, { | ||
width: '500px', | ||
data: settingsData | ||
let dialogRef = this.dialog.open(ModalWidgetComponent, { | ||
width: '80%', | ||
data: this.config | ||
}); | ||
dialogRef.afterClosed().subscribe(result => { | ||
// save new settings | ||
if (result) { | ||
console.debug("Updating widget config"); | ||
console.log(result); | ||
this.unsubscribePath();//unsub now as we will change variables so wont know what was subbed before... | ||
this.widgetConfig.signalKPath = result.signalKPath; | ||
this.widgetConfig.signalKSource = result.signalKSource; | ||
this.widgetConfig.label = result.label; | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.widgetConfig); | ||
this.config = result; | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.config); | ||
this.subscribePath(); | ||
} | ||
}); | ||
@@ -129,63 +113,1 @@ } | ||
} | ||
@Component({ | ||
selector: 'state-widget-modal', | ||
templateUrl: './widget-state.modal.html', | ||
styleUrls: ['./widget-state.component.css'] | ||
}) | ||
export class WidgetStateModalComponent implements OnInit { | ||
settingsData: widgetConfig; | ||
selfPaths: boolean = true; | ||
availablePaths: Array<string> = []; | ||
availableSources: Array<string>; | ||
constructor( | ||
private SignalKService: SignalKService, | ||
public dialogRef:MatDialogRef<WidgetStateModalComponent>, | ||
@Inject(MAT_DIALOG_DATA) public data: any) { } | ||
ngOnInit() { | ||
this.settingsData = this.data; | ||
//populate available choices | ||
this.availablePaths = this.SignalKService.getPathsByType('boolean').sort(); | ||
if (this.availablePaths.includes(this.settingsData.signalKPath)) { | ||
this.settingsDataUpdatePath(); //TODO: this wipes out existing config, not good when editing existing config... | ||
} | ||
} | ||
settingsDataUpdatePath() { // called when we choose a new path. resets the rest with default info of this path | ||
let pathObject = this.SignalKService.getPathObject(this.settingsData.signalKPath); | ||
if (pathObject === null) { return; } | ||
this.availableSources = ['default'].concat(Object.keys(pathObject.sources)); | ||
this.settingsData.signalKSource = 'default'; | ||
if (pathObject.meta) { | ||
if (typeof(pathObject.meta.abbreviation) == 'string') { | ||
this.settingsData.label = pathObject.meta.abbreviation; | ||
} else if (typeof(pathObject.meta.label) == 'string') { | ||
this.settingsData.label = pathObject.meta.label; | ||
} else { | ||
this.settingsData.label = this.settingsData.signalKPath; // who knows? | ||
} | ||
} else { | ||
this.settingsData.label = this.settingsData.signalKPath;// who knows? | ||
} | ||
} | ||
submitConfig() { | ||
this.dialogRef.close(this.settingsData); | ||
} | ||
} | ||
import { Component, Input, OnInit, OnDestroy, Inject } from '@angular/core'; | ||
import { Subscription } from 'rxjs/Subscription'; | ||
import {MatDialog,MatDialogRef,MAT_DIALOG_DATA } from '@angular/material'; | ||
import { MatDialog,MatDialogRef,MAT_DIALOG_DATA } from '@angular/material'; | ||
import { SignalKService, pathObject } from '../signalk.service'; | ||
import { WidgetManagerService, IWidget } from '../widget-manager.service'; | ||
import { UnitConvertService } from '../unit-convert.service'; | ||
import { ModalWidgetComponent } from '../modal-widget/modal-widget.component'; | ||
import { SignalKService } from '../signalk.service'; | ||
import { WidgetManagerService, IWidget, IWidgetConfig } from '../widget-manager.service'; | ||
interface widgetConfig { | ||
signalKPath: string; | ||
signalKSource: string; | ||
label: string; | ||
} | ||
const defaultConfig: IWidgetConfig = { | ||
widgetLabel: null, | ||
paths: { | ||
"stringPath": { | ||
description: "String Data", | ||
path: null, | ||
source: null, | ||
pathType: "string", | ||
} | ||
}, | ||
selfPaths: true | ||
}; | ||
interface IWidgetsettingsData { | ||
selectedPath: string; | ||
selectedSource: string; | ||
label: string; | ||
} | ||
@Component({ | ||
@@ -33,13 +33,10 @@ selector: 'app-widget-text-generic', | ||
activeWidget: IWidget; | ||
activeWidget: IWidget; | ||
config: IWidgetConfig; | ||
dataValue: any = null; | ||
dataTimestamp: number = Date.now(); | ||
widgetConfig: widgetConfig = { | ||
signalKPath: null, | ||
signalKSource: 'default', | ||
label: null | ||
} | ||
//subs | ||
@@ -59,6 +56,9 @@ valueSub: Subscription = null; | ||
// no data, let's set some! | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.widgetConfig); | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, defaultConfig); | ||
this.config = defaultConfig; // load default config. | ||
} else { | ||
this.widgetConfig = this.activeWidget.config; // load existing config. | ||
this.config = this.activeWidget.config; | ||
} | ||
this.subscribePath(); | ||
@@ -74,24 +74,6 @@ } | ||
this.unsubscribePath(); | ||
if (this.widgetConfig.signalKPath === null) { return } // nothing to sub to... | ||
this.valueSub = this.SignalKService.subscribePath(this.widgetUUID, this.widgetConfig.signalKPath).subscribe( | ||
pathObject => { | ||
if (pathObject === null) { | ||
return; // we will get null back if we subscribe to a path before the app knows about it. when it learns about it we will get first value | ||
} | ||
let source: string; | ||
if (this.widgetConfig.signalKSource == 'default') { | ||
source = pathObject.defaultSource; | ||
} else { | ||
source = this.widgetConfig.signalKSource; | ||
} | ||
this.dataTimestamp = pathObject.sources[source].timestamp; | ||
if (pathObject.sources[source].value === null) { | ||
this.dataValue = null; | ||
} | ||
this.dataValue = pathObject.sources[source].value; | ||
if (typeof(this.config.paths['stringPath'].path) != 'string') { return } // nothing to sub to... | ||
this.valueSub = this.SignalKService.subscribePath(this.widgetUUID, this.config.paths['stringPath'].path, this.config.paths['stringPath'].source).subscribe( | ||
newValue => { | ||
this.dataValue = newValue; | ||
} | ||
@@ -105,3 +87,3 @@ ); | ||
this.valueSub = null; | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.widgetConfig.signalKPath) | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.config.paths['stringPath'].path) | ||
} | ||
@@ -112,86 +94,20 @@ } | ||
//prepare current data | ||
let settingsData: IWidgetsettingsData = { | ||
selectedPath: this.widgetConfig.signalKPath, | ||
selectedSource: this.widgetConfig.signalKSource, | ||
label: this.widgetConfig.label | ||
} | ||
let dialogRef = this.dialog.open(WidgetTextGenericModalComponent, { | ||
width: '500px', | ||
data: settingsData | ||
let dialogRef = this.dialog.open(ModalWidgetComponent, { | ||
width: '80%', | ||
data: this.config | ||
}); | ||
dialogRef.afterClosed().subscribe(result => { | ||
// save new settings | ||
if (result) { | ||
console.debug("Updating widget config"); | ||
console.log(result); | ||
this.unsubscribePath();//unsub now as we will change variables so wont know what was subbed before... | ||
this.widgetConfig.signalKPath = result.selectedPath; | ||
this.widgetConfig.signalKSource = result.selectedSource; | ||
this.widgetConfig.label = result.label; | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.widgetConfig); | ||
this.config = result; | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.config); | ||
this.subscribePath(); | ||
} | ||
}); | ||
} | ||
} | ||
@Component({ | ||
selector: 'text-widget-modal', | ||
templateUrl: './widget-text-generic.modal.html', | ||
styleUrls: ['./widget-text-generic.component.css'] | ||
}) | ||
export class WidgetTextGenericModalComponent implements OnInit { | ||
settingsData: IWidgetsettingsData; | ||
selfPaths: boolean = true; | ||
availablePaths: Array<string> = []; | ||
availableSources: Array<string>; | ||
constructor( | ||
private SignalKService: SignalKService, | ||
private UnitConvertService: UnitConvertService, | ||
public dialogRef:MatDialogRef<WidgetTextGenericModalComponent>, | ||
@Inject(MAT_DIALOG_DATA) public data: any) { } | ||
ngOnInit() { | ||
this.settingsData = this.data; | ||
//populate available choices | ||
this.availablePaths = this.SignalKService.getPathsByType('string').sort(); | ||
if (this.availablePaths.includes(this.settingsData.selectedPath)) { | ||
this.settingsDataUpdatePath(); //TODO: this wipes out existing config, not good when editing existing config... | ||
} | ||
} | ||
settingsDataUpdatePath() { // called when we choose a new path. resets the rest with default info of this path | ||
let pathObject = this.SignalKService.getPathObject(this.settingsData.selectedPath); | ||
if (pathObject === null) { return; } | ||
this.availableSources = ['default'].concat(Object.keys(pathObject.sources)); | ||
this.settingsData.selectedSource = 'default'; | ||
if (pathObject.meta) { | ||
if (typeof(pathObject.meta.abbreviation) == 'string') { | ||
this.settingsData.label = pathObject.meta.abbreviation; | ||
} else if (typeof(pathObject.meta.label) == 'string') { | ||
this.settingsData.label = pathObject.meta.label; | ||
} else { | ||
this.settingsData.label = this.settingsData.selectedPath; // who knows? | ||
} | ||
} else { | ||
this.settingsData.label = this.settingsData.selectedPath;// who knows? | ||
} | ||
} | ||
submitConfig() { | ||
this.dialogRef.close(this.settingsData); | ||
} | ||
} |
@@ -0,0 +0,0 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; |
@@ -0,0 +0,0 @@ import { Component, Input, OnInit } from '@angular/core'; |
@@ -0,0 +0,0 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; |
import { Component, Input, OnInit, OnDestroy, Inject } from '@angular/core'; | ||
import { Subscription } from 'rxjs/Subscription'; | ||
import {MatDialog,MatDialogRef,MAT_DIALOG_DATA } from '@angular/material'; | ||
import { Observable } from 'rxjs/Observable'; | ||
import { SignalKService, pathObject } from '../signalk.service'; | ||
import { WidgetManagerService, IWidget } from '../widget-manager.service'; | ||
import { UnitConvertService } from '../unit-convert.service'; | ||
import { MatDialog } from '@angular/material'; | ||
import { ModalWidgetComponent } from '../modal-widget/modal-widget.component'; | ||
import { SignalKService } from '../signalk.service'; | ||
import { WidgetManagerService, IWidget, IWidgetConfig } from '../widget-manager.service'; | ||
import { UnitsService } from '../units.service'; | ||
interface IWidgetConfig { | ||
headingPath: string; | ||
headingSource: string; | ||
trueWindAnglePath: string; | ||
trueWindAngleSource: string; | ||
trueWindSpeedPath: string; | ||
trueWindSpeedSource: string; | ||
appWindAnglePath: string; | ||
appWindAngleSource: string; | ||
appWindSpeedPath: string; | ||
appWindSpeedSource: string; | ||
unitName: string; | ||
} | ||
const defaultConfig: IWidgetConfig = { | ||
paths: { | ||
"headingPath": { | ||
description: "Heading", | ||
path: 'self.navigation.courseOverGroundTrue', | ||
source: 'default', | ||
pathType: "number", | ||
}, | ||
"trueWindAngle": { | ||
description: "True Wind Angle", | ||
path: 'self.environment.wind.angleTrueWater', | ||
source: 'default', | ||
pathType: "number", | ||
}, | ||
"trueWindSpeed": { | ||
description: "True Wind Speed", | ||
path: 'self.environment.wind.speedTrue', | ||
source: 'default', | ||
pathType: "number", | ||
}, | ||
"appWindAngle": { | ||
description: "Apparent Wind Angle", | ||
path: 'self.environment.wind.angleApparent', | ||
source: 'default', | ||
pathType: "number", | ||
}, | ||
"appWindSpeed": { | ||
description: "Apparent Wind Speed", | ||
path: 'self.environment.wind.speedApparent', | ||
source: 'default', | ||
pathType: "number", | ||
}, | ||
}, | ||
units: { | ||
"trueWindSpeed": "knots", | ||
"appWindSpeed": "knots" | ||
}, | ||
selfPaths: true, | ||
windSectorEnable: true, | ||
windSectorWindowSeconds: 10, | ||
laylineEnable: true, | ||
laylineAngle: 35, | ||
}; | ||
@Component({ | ||
@@ -33,19 +68,5 @@ selector: 'app-widget-wind', | ||
@Input('unlockStatus') unlockStatus: boolean; | ||
converter: Object = this.UnitConvertService.getConverter(); | ||
activeWidget: IWidget; | ||
widgetConfig: IWidgetConfig = { | ||
headingPath: 'vessels.self.navigation.headingTrue', | ||
headingSource: 'default', | ||
trueWindAnglePath: 'vessels.self.environment.wind.angleTrueWater', | ||
trueWindAngleSource: 'default', | ||
trueWindSpeedPath: 'vessels.self.environment.wind.speedTrue', | ||
trueWindSpeedSource: 'default', | ||
appWindAnglePath: 'vessels.self.environment.wind.angleApparent', | ||
appWindAngleSource: 'default', | ||
appWindSpeedPath: 'vessels.self.environment.wind.speedApparent', | ||
appWindSpeedSource: 'default', | ||
unitName: 'knots' | ||
} | ||
config: IWidgetConfig; | ||
@@ -67,2 +88,13 @@ currentHeading: number = 0; | ||
trueWindHistoric: { | ||
timestamp: number; | ||
heading: number; | ||
}[] = []; | ||
trueWindMinHistoric: number; | ||
trueWindMidHistoric: number; | ||
trueWindMaxHistoric: number; | ||
windSectorObservableSub: Subscription; | ||
constructor( | ||
@@ -72,3 +104,3 @@ public dialog:MatDialog, | ||
private WidgetManagerService: WidgetManagerService, | ||
private UnitConvertService: UnitConvertService) { | ||
private UnitsService: UnitsService) { | ||
} | ||
@@ -81,5 +113,6 @@ | ||
// no data, let's set some! | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.widgetConfig); | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, defaultConfig); | ||
this.config = defaultConfig; // load default config. | ||
} else { | ||
this.widgetConfig = this.activeWidget.config; // load existing config. | ||
this.config = this.activeWidget.config; | ||
} | ||
@@ -99,2 +132,3 @@ this.startAll(); | ||
this.subscribeTrueWindSpeed(); | ||
this.startWindSectors(); | ||
} | ||
@@ -107,3 +141,4 @@ | ||
this.unsubscribeTrueWindAngle(); | ||
this.unsubscribeTrueWindSpeed(); | ||
this.unsubscribeTrueWindSpeed(); | ||
this.stopWindSectors(); | ||
} | ||
@@ -113,23 +148,11 @@ | ||
this.unsubscribeHeading(); | ||
if (this.widgetConfig.headingPath === null) { return } // nothing to sub to... | ||
this.headingSub = this.SignalKService.subscribePath(this.widgetUUID, this.widgetConfig.headingPath).subscribe( | ||
pathObject => { | ||
if (pathObject === null) { | ||
return; // we will get null back if we subscribe to a path before the app knows about it. when it learns about it we will get first value | ||
} | ||
let source: string; | ||
if (this.widgetConfig.headingSource == 'default') { | ||
source = pathObject.defaultSource; | ||
if (typeof(this.config.paths['headingPath'].path) != 'string') { return } // nothing to sub to... | ||
this.headingSub = this.SignalKService.subscribePath(this.widgetUUID, this.config.paths['headingPath'].path, this.config.paths['headingPath'].source).subscribe( | ||
newValue => { | ||
if (newValue === null) { | ||
this.currentHeading = 0; | ||
} else { | ||
source = this.widgetConfig.headingSource; | ||
this.currentHeading = this.UnitsService.convertUnit('deg', newValue); | ||
} | ||
if (pathObject.sources[source].value === null) { | ||
this.currentHeading = 0; | ||
} | ||
let value:number = pathObject.sources[source].value; | ||
let converted = this.converter['angle']['deg'](value); | ||
this.currentHeading = converted; | ||
} | ||
@@ -141,17 +164,7 @@ ); | ||
this.unsubscribeAppWindAngle(); | ||
if (this.widgetConfig.appWindAnglePath === null) { return } // nothing to sub to... | ||
if (typeof(this.config.paths['appWindAngle'].path) != 'string') { return } // nothing to sub to... | ||
this.appWindAngleSub = this.SignalKService.subscribePath(this.widgetUUID, this.widgetConfig.appWindAnglePath).subscribe( | ||
pathObject => { | ||
if (pathObject === null) { | ||
return; // we will get null back if we subscribe to a path before the app knows about it. when it learns about it we will get first value | ||
} | ||
let source: string; | ||
if (this.widgetConfig.appWindAngleSource == 'default') { | ||
source = pathObject.defaultSource; | ||
} else { | ||
source = this.widgetConfig.appWindAngleSource; | ||
} | ||
if (pathObject.sources[source].value === null) { | ||
this.appWindAngleSub = this.SignalKService.subscribePath(this.widgetUUID, this.config.paths['appWindAngle'].path, this.config.paths['appWindAngle'].source).subscribe( | ||
newValue => { | ||
if (newValue === null) { | ||
this.appWindAngle = null; | ||
@@ -161,11 +174,10 @@ return; | ||
let value:number = pathObject.sources[source].value; | ||
let converted = this.converter['angle']['deg'](value); | ||
let converted = this.UnitsService.convertUnit('deg', newValue); | ||
// 0-180+ for stb | ||
// -0 to -180 for port | ||
// need in 0-360 | ||
if (converted > 0) {// stb | ||
this.appWindAngle= 360 - converted; | ||
} else if (converted < 0) { | ||
this.appWindAngle = (converted * -1); | ||
if (converted < 0) {// stb | ||
this.appWindAngle= 360 + converted; // adding a negative number subtracts it... | ||
} else { | ||
this.appWindAngle = converted; | ||
} | ||
@@ -179,23 +191,7 @@ | ||
this.unsubscribeAppWindSpeed(); | ||
if (this.widgetConfig.appWindSpeedPath === null) { return } // nothing to sub to... | ||
if (typeof(this.config.paths['appWindSpeed'].path) != 'string') { return } // nothing to sub to... | ||
this.appWindSpeedSub = this.SignalKService.subscribePath(this.widgetUUID, this.widgetConfig.appWindSpeedPath).subscribe( | ||
pathObject => { | ||
if (pathObject === null) { | ||
return; // we will get null back if we subscribe to a path before the app knows about it. when it learns about it we will get first value | ||
} | ||
let source: string; | ||
if (this.widgetConfig.appWindSpeedSource == 'default') { | ||
source = pathObject.defaultSource; | ||
} else { | ||
source = this.widgetConfig.appWindSpeedSource; | ||
} | ||
if (pathObject.sources[source].value === null) { | ||
this.appWindSpeed = null; | ||
return; | ||
} | ||
let value:number = pathObject.sources[source].value; | ||
this.appWindSpeed = this.converter['speed'][this.widgetConfig.unitName](value); | ||
this.appWindSpeedSub = this.SignalKService.subscribePath(this.widgetUUID, this.config.paths['appWindSpeed'].path, this.config.paths['appWindSpeed'].source).subscribe( | ||
newValue => { | ||
this.appWindSpeed = this.UnitsService.convertUnit(this.config.units['appWindSpeed'], newValue); | ||
} | ||
@@ -207,32 +203,31 @@ ); | ||
this.unsubscribeTrueWindAngle(); | ||
if (this.widgetConfig.trueWindAnglePath === null) { return } // nothing to sub to... | ||
if (typeof(this.config.paths['trueWindAngle'].path) != 'string') { return } // nothing to sub to... | ||
this.trueWindAngleSub = this.SignalKService.subscribePath(this.widgetUUID, this.widgetConfig.trueWindAnglePath).subscribe( | ||
pathObject => { | ||
if (pathObject === null) { | ||
return; // we will get null back if we subscribe to a path before the app knows about it. when it learns about it we will get first value | ||
} | ||
let source: string; | ||
if (this.widgetConfig.trueWindAngleSource == 'default') { | ||
source = pathObject.defaultSource; | ||
} else { | ||
source = this.widgetConfig.trueWindAngleSource; | ||
} | ||
if (pathObject.sources[source].value === null) { | ||
this.trueWindAngleSub = this.SignalKService.subscribePath(this.widgetUUID, this.config.paths['trueWindAngle'].path, this.config.paths['trueWindAngle'].source).subscribe( | ||
newValue => { | ||
if (newValue === null) { | ||
this.trueWindAngle = null; | ||
return; | ||
} | ||
let converted = this.UnitsService.convertUnit('deg', newValue); | ||
let value:number = pathObject.sources[source].value; | ||
let converted = this.converter['angle']['deg'](value); | ||
// 0-180+ for stb | ||
// -0 to -180 for port | ||
// need in 0-360 | ||
if (converted > 0) {// stb | ||
this.trueWindAngle= 360 - converted; | ||
} else if (converted < 0) { | ||
this.trueWindAngle = (converted * -1); | ||
// Depending on path, this number can either be the magnetic compass heading, true compass heading, or heading relative to boat heading (-180 to 180deg)... Ugh... | ||
// 0-180+ for stb | ||
// -0 to -180 for port | ||
// need in 0-360 | ||
if (this.config.paths['trueWindAngle'].path.match('angleTrueWater')|| | ||
this.config.paths['trueWindAngle'].path.match('angleTrueGround')) { | ||
//-180 to 180 | ||
this.trueWindAngle = this.addHeading(this.currentHeading, converted); | ||
} else if (this.config.paths['trueWindAngle'].path.match('direction')) { | ||
//0-360 | ||
this.trueWindAngle = converted; | ||
} | ||
//add to historical for wind sectors | ||
if (this.config.windSectorEnable) { | ||
this.addHistoricalTrue(this.trueWindAngle); | ||
} | ||
} | ||
@@ -244,28 +239,57 @@ ); | ||
this.unsubscribeTrueWindSpeed(); | ||
if (this.widgetConfig.trueWindSpeedPath === null) { return } // nothing to sub to... | ||
if (typeof(this.config.paths['trueWindSpeed'].path) != 'string') { return } // nothing to sub to... | ||
this.trueWindSpeedSub = this.SignalKService.subscribePath(this.widgetUUID, this.widgetConfig.trueWindSpeedPath).subscribe( | ||
pathObject => { | ||
if (pathObject === null) { | ||
return; // we will get null back if we subscribe to a path before the app knows about it. when it learns about it we will get first value | ||
} | ||
let source: string; | ||
if (this.widgetConfig.trueWindSpeedSource == 'default') { | ||
source = pathObject.defaultSource; | ||
} else { | ||
source = this.widgetConfig.trueWindSpeedSource; | ||
} | ||
this.trueWindSpeedSub = this.SignalKService.subscribePath(this.widgetUUID, this.config.paths['trueWindSpeed'].path, this.config.paths['trueWindSpeed'].source).subscribe( | ||
newValue => { | ||
this.trueWindSpeed = this.UnitsService.convertUnit(this.config.units['trueWindSpeed'], newValue); | ||
} | ||
); | ||
} | ||
if (pathObject.sources[source].value === null) { | ||
this.trueWindSpeed = null; | ||
return; | ||
} | ||
startWindSectors() { | ||
this.windSectorObservableSub = Observable.interval (500).subscribe(x => { | ||
this.historicalCleanup(); | ||
}); | ||
} | ||
let value:number = pathObject.sources[source].value; | ||
this.trueWindSpeed = this.converter['speed'][this.widgetConfig.unitName](value); | ||
addHistoricalTrue (windHeading) { | ||
this.trueWindHistoric.push({ | ||
timestamp: Date.now(), | ||
heading: windHeading | ||
}); | ||
let arr = this.arcForAngles(this.trueWindHistoric.map(d => d.heading)); | ||
this.trueWindMinHistoric = arr[0]; | ||
this.trueWindMaxHistoric = arr[1]; | ||
this.trueWindMidHistoric = arr[2]; | ||
} | ||
arcForAngles (data) { | ||
return data.slice(1).reduce((acc, theValue) => { | ||
let value = theValue | ||
while (value < acc[0] - 180) { | ||
value += 360 | ||
} | ||
); | ||
while (value > acc[1] + 180) { | ||
value -= 360 | ||
} | ||
acc[0] = Math.min(acc[0], value) | ||
acc[1] = Math.max(acc[1], value) | ||
acc[2] = ((acc[1]-acc[0])/2)+acc[0]; | ||
return acc | ||
}, [data[0], data[0]]) | ||
} | ||
historicalCleanup() { | ||
let n = Date.now()-(this.config.windSectorWindowSeconds*1000); | ||
for (var i = this.trueWindHistoric.length - 1; i >= 0; --i) { | ||
if (this.trueWindHistoric[i].timestamp < n) { | ||
this.trueWindHistoric.splice(i,1); | ||
} | ||
} | ||
} | ||
stopWindSectors() { | ||
this.windSectorObservableSub.unsubscribe(); | ||
} | ||
unsubscribeHeading() { | ||
@@ -275,3 +299,3 @@ if (this.headingSub !== null) { | ||
this.headingSub = null; | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.widgetConfig.headingPath); | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.config.paths['headingPath'].path); | ||
} | ||
@@ -284,3 +308,3 @@ } | ||
this.appWindAngleSub = null; | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.widgetConfig.appWindAnglePath); | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.config.paths['appWindAngle'].path); | ||
} | ||
@@ -293,3 +317,3 @@ } | ||
this.appWindSpeedSub = null; | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.widgetConfig.appWindSpeedPath); | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.config.paths['appWindSpeed'].path); | ||
} | ||
@@ -302,3 +326,3 @@ } | ||
this.trueWindAngleSub = null; | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.widgetConfig.trueWindAnglePath); | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.config.paths['trueWindAngle'].path); | ||
} | ||
@@ -311,158 +335,45 @@ } | ||
this.trueWindSpeedSub = null; | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.widgetConfig.trueWindSpeedPath); | ||
this.SignalKService.unsubscribePath(this.widgetUUID, this.config.paths['trueWindSpeed'].path); | ||
} | ||
} | ||
openWidgetSettings() { | ||
//prepare current data | ||
let settingsData: IWidgetConfig = { | ||
headingPath: this.widgetConfig.headingPath, | ||
headingSource: this.widgetConfig.headingSource, | ||
trueWindAnglePath: this.widgetConfig.trueWindAnglePath, | ||
trueWindAngleSource: this.widgetConfig.trueWindAngleSource, | ||
trueWindSpeedPath: this.widgetConfig.trueWindSpeedPath, | ||
trueWindSpeedSource: this.widgetConfig.trueWindSpeedSource, | ||
appWindAnglePath: this.widgetConfig.appWindAnglePath, | ||
appWindAngleSource: this.widgetConfig.appWindAngleSource, | ||
appWindSpeedPath: this.widgetConfig.appWindSpeedPath, | ||
appWindSpeedSource: this.widgetConfig.appWindSpeedSource, | ||
unitName: this.widgetConfig.unitName | ||
} | ||
let dialogRef = this.dialog.open(WidgetWindModalComponent, { | ||
width: '650px', | ||
data: settingsData | ||
}); | ||
dialogRef.afterClosed().subscribe(result => { | ||
// save new settings | ||
if (result) { | ||
console.debug("Updating widget config"); | ||
this.stopAll();//unsub now as we will change variables so wont know what was subbed before... | ||
this.widgetConfig.headingPath = result.headingPath; | ||
this.widgetConfig.headingSource = result.headingSource; | ||
this.widgetConfig.trueWindAnglePath = result.trueWindAnglePath; | ||
this.widgetConfig.trueWindAngleSource = result.trueWindAngleSource; | ||
this.widgetConfig.trueWindSpeedPath = result.trueWindSpeedPath; | ||
this.widgetConfig.trueWindSpeedSource = result.trueWindSpeedSource; | ||
this.widgetConfig.appWindAnglePath = result.appWindAnglePath; | ||
this.widgetConfig.appWindAngleSource = result.appWindAngleSource; | ||
this.widgetConfig.appWindSpeedPath = result.appWindSpeedPath; | ||
this.widgetConfig.appWindSpeedSource = result.appWindSpeedSource; | ||
this.widgetConfig.unitName = result.unitName; | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.widgetConfig); | ||
this.startAll(); | ||
} | ||
}); | ||
addHeading(h1: number, h2: number) { | ||
let h3 = h1 + h2; | ||
while (h3 > 359) { h3 = h3 - 359; } | ||
while (h3 < 0) { h3 = h3 + 359; } | ||
return h3; | ||
} | ||
} | ||
/************************************************************* | ||
* *********************************************************** | ||
* *********************************************************** | ||
* Modal | ||
* *********************************************************** | ||
* *********************************************************** | ||
*/ | ||
@Component({ | ||
selector: 'wind-widget-modal', | ||
templateUrl: './widget-wind.modal.html', | ||
styleUrls: ['./widget-wind.component.css'] | ||
}) | ||
export class WidgetWindModalComponent implements OnInit { | ||
settingsData: IWidgetConfig; | ||
availableUnitNames: string[]; | ||
selfPaths: boolean = true; | ||
availablePaths: Array<string> = []; | ||
headingSources: Array<string>; | ||
trueWindAngleSources: Array<string>; | ||
trueWindSpeedSources: Array<string>; | ||
appWindAngleSources: Array<string>; | ||
appWindSpeedSources: Array<string>; | ||
converter: Object = this.UnitConvertService.getConverter(); | ||
openWidgetSettings() { | ||
constructor( | ||
private SignalKService: SignalKService, | ||
private UnitConvertService: UnitConvertService, | ||
public dialogRef:MatDialogRef<WidgetWindModalComponent>, | ||
@Inject(MAT_DIALOG_DATA) public data: any) { } | ||
let dialogRef = this.dialog.open(ModalWidgetComponent, { | ||
width: '80%', | ||
data: this.config | ||
}); | ||
ngOnInit() { | ||
this.settingsData = this.data; | ||
//populate available choices | ||
this.availablePaths = this.SignalKService.getPathsByType('number').sort(); | ||
dialogRef.afterClosed().subscribe(result => { | ||
// save new settings | ||
if (result) { | ||
console.log(result); | ||
this.stopAll();//unsub now as we will change variables so wont know what was subbed before... | ||
this.config = result; | ||
this.WidgetManagerService.updateWidgetConfig(this.widgetUUID, this.config); | ||
this.startAll(); | ||
} | ||
this.updateHeadingSources(); | ||
this.updateTrueWindAngleSources(); | ||
this.updateTrueWindSpeedSources(); | ||
this.updateAppWindAngleSources(); | ||
this.updateAppWindSpeedSources(); | ||
this.availableUnitNames = Object.keys(this.converter['speed']); | ||
}); | ||
} | ||
updateHeadingSources() { | ||
let pathObject = this.SignalKService.getPathObject(this.settingsData.headingPath); | ||
if (pathObject === null) { return; } | ||
this.headingSources = ['default'].concat(Object.keys(pathObject.sources)); | ||
if (!this.headingSources.includes(this.settingsData.headingSource)) | ||
{ this.settingsData.headingSource = 'default'; } | ||
} | ||
updateTrueWindAngleSources() { | ||
let pathObject = this.SignalKService.getPathObject(this.settingsData.trueWindAnglePath); | ||
if (pathObject === null) { return; } | ||
this.trueWindAngleSources = ['default'].concat(Object.keys(pathObject.sources)); | ||
if (!this.trueWindAngleSources.includes(this.settingsData.trueWindAngleSource)) | ||
{ this.settingsData.trueWindAngleSource = 'default'; } | ||
} | ||
updateTrueWindSpeedSources() { | ||
let pathObject = this.SignalKService.getPathObject(this.settingsData.trueWindSpeedPath); | ||
if (pathObject === null) { return; } | ||
this.trueWindSpeedSources = ['default'].concat(Object.keys(pathObject.sources)); | ||
if (!this.trueWindSpeedSources.includes(this.settingsData.trueWindSpeedSource)) | ||
{ this.settingsData.trueWindSpeedSource = 'default'; } | ||
} | ||
updateAppWindAngleSources() { | ||
let pathObject = this.SignalKService.getPathObject(this.settingsData.appWindAnglePath); | ||
if (pathObject === null) { return; } | ||
this.appWindAngleSources = ['default'].concat(Object.keys(pathObject.sources)); | ||
if (!this.appWindAngleSources.includes(this.settingsData.appWindAngleSource)) | ||
{ this.settingsData.appWindAngleSource = 'default'; } | ||
} | ||
updateAppWindSpeedSources() { | ||
let pathObject = this.SignalKService.getPathObject(this.settingsData.appWindSpeedPath); | ||
if (pathObject === null) { return; } | ||
this.appWindSpeedSources = ['default'].concat(Object.keys(pathObject.sources)); | ||
if (!this.appWindSpeedSources.includes(this.settingsData.appWindSpeedSource)) | ||
{ this.settingsData.appWindSpeedSource = 'default'; } | ||
} | ||
submitConfig() { | ||
this.dialogRef.close(this.settingsData); | ||
} | ||
} |
/*! NoSleep.min.js v0.7.0 - git.io/vfn01 - Rich Tibbett - MIT license */ | ||
!function(A,B){"object"==typeof exports&&"object"==typeof module?module.exports=B():"function"==typeof define&&define.amd?define([],B):"object"==typeof exports?exports.NoSleep=B():A.NoSleep=B()}(this,function(){return function(A){function B(e){if(Q[e])return Q[e].exports;var o=Q[e]={i:e,l:!1,exports:{}};return A[e].call(o.exports,o,o.exports,B),o.l=!0,o.exports}var Q={};return B.m=A,B.c=Q,B.d=function(A,Q,e){B.o(A,Q)||Object.defineProperty(A,Q,{configurable:!1,enumerable:!0,get:e})},B.n=function(A){var Q=A&&A.__esModule?function(){return A.default}:function(){return A};return B.d(Q,"a",Q),Q},B.o=function(A,B){return Object.prototype.hasOwnProperty.call(A,B)},B.p="",B(B.s=0)}([function(A,B,Q){"use strict";function e(A,B){if(!(A instanceof B))throw new TypeError("Cannot call a class as a function")}var o=function(){function A(A,B){for(var Q=0;Q<B.length;Q++){var e=B[Q];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(A,e.key,e)}}return function(B,Q,e){return Q&&A(B.prototype,Q),e&&A(B,e),B}}(),t=Q(1),n="undefined"!=typeof navigator&&parseFloat((""+(/CPU.*OS ([0-9_]{3,4})[0-9_]{0,1}|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_",".").replace("_",""))<10&&!window.MSStream,E=function(){function A(){e(this,A),n?this.noSleepTimer=null:(this.noSleepVideo=document.createElement("video"),this.noSleepVideo.setAttribute("playsinline",""),this.noSleepVideo.setAttribute("src",t),this.noSleepVideo.addEventListener("timeupdate",function(A){this.noSleepVideo.currentTime>.5&&(this.noSleepVideo.currentTime=Math.random())}.bind(this)))}return o(A,[{key:"enable",value:function(){n?(this.disable(),this.noSleepTimer=window.setInterval(function(){window.location.href="/",window.setTimeout(window.stop,0)},15e3)):this.noSleepVideo.play()}},{key:"disable",value:function(){n?this.noSleepTimer&&(window.clearInterval(this.noSleepTimer),this.noSleepTimer=null):this.noSleepVideo.pause()}}]),A}();A.exports=E},function(A,B,Q){"use strict";A.exports="data:video/mp4;base64,AAAAIGZ0eXBtcDQyAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAACKBtZGF0AAAC8wYF///v3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE0MiByMjQ3OSBkZDc5YTYxIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTEgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MToweDExMSBtZT1oZXggc3VibWU9MiBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0wIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MCA4eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0wIHRocmVhZHM9NiBsb29rYWhlYWRfdGhyZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0ZT0xIGludGVybGFjZWQ9MCBibHVyYXlfY29tcGF0PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTMgYl9weXJhbWlkPTIgYl9hZGFwdD0xIGJfYmlhcz0wIGRpcmVjdD0xIHdlaWdodGI9MSBvcGVuX2dvcD0wIHdlaWdodHA9MSBrZXlpbnQ9MzAwIGtleWludF9taW49MzAgc2NlbmVjdXQ9NDAgaW50cmFfcmVmcmVzaD0wIHJjX2xvb2thaGVhZD0xMCByYz1jcmYgbWJ0cmVlPTEgY3JmPTIwLjAgcWNvbXA9MC42MCBxcG1pbj0wIHFwbWF4PTY5IHFwc3RlcD00IHZidl9tYXhyYXRlPTIwMDAwIHZidl9idWZzaXplPTI1MDAwIGNyZl9tYXg9MC4wIG5hbF9ocmQ9bm9uZSBmaWxsZXI9MCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAOWWIhAA3//p+C7v8tDDSTjf97w55i3SbRPO4ZY+hkjD5hbkAkL3zpJ6h/LR1CAABzgB1kqqzUorlhQAAAAxBmiQYhn/+qZYADLgAAAAJQZ5CQhX/AAj5IQAD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"}])}); |
@@ -0,0 +0,0 @@ /********************************************************************** |
@@ -15,6 +15,9 @@ { | ||
"lib": [ | ||
"es2016", | ||
"es2017", | ||
"dom" | ||
] | ||
}, | ||
"angularCompilerOptions": { | ||
"preserveWhitespaces": false | ||
} | ||
} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
255
0
7941275
19
40810
43
+ Added@angular/animations@5.2.11(transitive)
+ Added@angular/cdk@5.2.5(transitive)
+ Added@angular/common@5.2.117.2.16(transitive)
+ Added@angular/compiler@5.2.11(transitive)
+ Added@angular/core@5.2.117.2.16(transitive)
+ Added@angular/forms@5.2.11(transitive)
+ Added@angular/http@5.2.11(transitive)
+ Added@angular/material@5.2.5(transitive)
+ Added@angular/platform-browser@5.2.11(transitive)
+ Added@angular/platform-browser-dynamic@5.2.11(transitive)
+ Added@angular/platform-server@5.2.11(transitive)
+ Added@angular/router@5.2.11(transitive)
+ Addedangular-split@1.0.4(transitive)
+ Addeddomino@1.0.30(transitive)
+ Addedrxjs@6.6.7(transitive)
+ Addedxhr2@0.1.4(transitive)
- Removed@angular/animations@4.4.7(transitive)
- Removed@angular/cdk@2.0.0-beta.12(transitive)
- Removed@angular/common@4.4.7(transitive)
- Removed@angular/compiler@4.4.7(transitive)
- Removed@angular/core@4.4.7(transitive)
- Removed@angular/forms@4.4.7(transitive)
- Removed@angular/http@4.4.7(transitive)
- Removed@angular/material@2.0.0-beta.12(transitive)
- Removed@angular/platform-browser@4.4.7(transitive)
- Removed@angular/platform-browser-dynamic@4.4.7(transitive)
- Removed@angular/router@4.4.7(transitive)
- Removedangular-split@0.2.7(transitive)
Updated@angular/animations@^5.2.9
Updated@angular/cdk@^5.2.4
Updated@angular/common@^5.2.9
Updated@angular/compiler@^5.2.9
Updated@angular/core@^5.2.9
Updated@angular/forms@^5.2.9
Updated@angular/http@^5.2.9
Updated@angular/material@^5.2.4
Updated@angular/router@^5.2.9
Updatedangular-split@^1.0.0-rc.3
Updatedchart.js@^2.7.2
Updatedcore-js@^2.5.3
Updatedrxjs@^5.5.7
Updatedzone.js@^0.8.19