Skip to content

Commit

Permalink
replaced measurement -> datapoint selector
Browse files Browse the repository at this point in the history
  • Loading branch information
YashPShah-swag committed Oct 17, 2023
1 parent ce71e67 commit 79379f2
Show file tree
Hide file tree
Showing 8 changed files with 1,963 additions and 1,491 deletions.
3,266 changes: 1,834 additions & 1,432 deletions package-lock.json

Large diffs are not rendered by default.

49 changes: 24 additions & 25 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cumulocity-silo-capacity-widget-plugin",
"version": "1.0.1",
"version": "1.1.0",
"description": "The Silo Capacity Widget displays a configurable silo capacity graphic with fill levels, foreground image, background image and thresholds.",
"scripts": {
"start": "c8ycli server",
Expand Down Expand Up @@ -28,12 +28,12 @@
"@angular/platform-browser-dynamic": "14.0.6",
"@angular/router": "14.0.6",
"@angular/upgrade": "14.0.6",
"@c8y/client": "1016.0.40",
"@c8y/ngx-components": "1016.0.40",
"@c8y/client": "1016.0.170",
"@c8y/ngx-components": "1016.0.170",
"@ngx-translate/core": "14.0.0",
"rxjs": "~6.6.3",
"zone.js": "~0.11.7",
"@c8y/style": "1016.0.40",
"@c8y/style": "1016.0.170",
"chart.js": "2.9.3"
},
"devDependencies": {
Expand All @@ -48,7 +48,7 @@
"jest": "^28.1.3",
"jest-preset-angular": "^12.2.0",
"typescript": "4.7.4",
"@c8y/cli": "1016.0.40",
"@c8y/cli": "1016.0.170",
"gulp": "^4.0.2",
"gulp-zip": "^5.0.1",
"del": "^6.1.1",
Expand Down Expand Up @@ -82,26 +82,25 @@
"SiloCapacityWidgetModule"
]
},
"copy": [
{
"from": "images/batterycapacity.png",
"to": "images/batterycapacity.png"
},
{
"from": "images/currentfill.png",
"to": "images/currentfill.png"
},
{
"from": "images/oilstorage.png",
"to": "images/oilstorage.png"
},
{
"from": "LICENSE",
"to": "LICENSE.txt"
}

]
},
"copy": [
{
"from": "images/batterycapacity.png",
"to": "images/batterycapacity.png"
},
{
"from": "images/currentfill.png",
"to": "images/currentfill.png"
},
{
"from": "images/oilstorage.png",
"to": "images/oilstorage.png"
},
{
"from": "LICENSE",
"to": "LICENSE.txt"
}
]
},
"cli": {}
},
"browserslist": [
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"experimentalDecorators": true,
"target": "es6",
"module": "es2020",
"skipLibCheck": true,
"lib": ["dom", "es2015", "es2016"]
},
"angularCompilerOptions": {
Expand Down
1 change: 1 addition & 0 deletions widget/i-widget-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,5 @@ export interface WidgetConfig {
thresholdMediumColor: string;

debugMode: boolean;
datapoints?:any;
}
23 changes: 11 additions & 12 deletions widget/silo-capacity-widget-config.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@
</div>
</div>
<div class="row">
<div class="col-lg-6">
<c8y-form-group>
<label for="measurementSeries">
Measurement
</label>
<select class="form-control" id="measurementSeries" name="measurementSeries"
[(ngModel)]="config.measurementSeries" (click)="loadFragmentSeries()">
<option [ngValue]="seriesValue" *ngFor="let seriesValue of supportedSeries"
[selected]="seriesValue == config.measurementSeries">{{seriesValue}}</option>
required
</select>
</c8y-form-group>
<div class="form-group col-sm-12 " *ngIf="configDevice">
<form [formGroup]="formGroup" >
<c8y-datapoint-selection-list
[minActiveCount]="1"
[maxActiveCount]="1"
[defaultFormOptions]="datapointSelectDefaultFormOptions"
[config]="datapointSelectionConfig"
formControlName="datapoints"
name="datapoints"
></c8y-datapoint-selection-list>
</form>
</div>
</div>
<div class="row">
Expand Down
74 changes: 70 additions & 4 deletions widget/silo-capacity-widget-config.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,49 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, Input, OnInit } from '@angular/core';
import { Component, Input, OnInit,DoCheck } from '@angular/core';
import { WidgetConfig } from './i-widget-config';
import * as _ from 'lodash'
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { FetchClient } from '@c8y/client';
import {
DatapointAttributesFormConfig,
DatapointSelectorModalOptions,
KPIDetails,
} from '@c8y/ngx-components/datapoint-selector';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { AbstractControl, FormBuilder, NgForm, ValidationErrors, ValidatorFn, Validators } from '@angular/forms';

export function exactlyASingleDatapointActive(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const datapoints: any[] = control.value;
if (!datapoints || !datapoints.length) {
return null;
}
const activeDatapoints = datapoints.filter(datapoint => datapoint.__active);
if (activeDatapoints.length === 1) {
return null;
}
return { exactlyOneDatapointNeedsToBeActive: true };
};
}

@Component({
selector: 'silo-capacity-widget-config',
templateUrl: './silo-capacity-widget-config.component.html',
styleUrls: ['./silo-capacity-widget-config.component.css']
})
export class SiloCapacityWidgetConfig implements OnInit {
export class SiloCapacityWidgetConfig implements OnInit,DoCheck {

@Input() config: WidgetConfig;
datapointSelectDefaultFormOptions: Partial<DatapointAttributesFormConfig> = {
showRange: false,
showChart: false,
};
datapointSelectionConfig: Partial<DatapointSelectorModalOptions> = {};
formGroup: ReturnType<SiloCapacityWidgetConfig['createForm']>;
configDevice = null;

private oldDeviceId: string = '';
private foregroundImageFileAsString: string;
Expand All @@ -42,16 +71,37 @@ export class SiloCapacityWidgetConfig implements OnInit {
public supportedSeries: string[];

public CONST_HELP_IMAGE_FILE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAADdgAAA3YBfdWCzAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAATzSURBVGiB7VlrTBxVFP7usLu8kUeBLSAFipUqFg1Qq5EgaCU2/DAxpYqJCVExmNC0Km1jolmbxgSCKbWoITG+oq1Ba6M1mvQHqxJTEyS0aEBiSyvIY2F5dl32Mczxh1WZndmdubOoTeD7d88995zvzH2cM/cC61jH2gZbFSs2m2B1l5VIEMoYUArgFgBZAa5GARogRj0CE7ono77uhc0mhes6rAAyD9iz/MQamUCPgZDJOXwUhA9FUWqfOXrfmFEOhgLIPtSd5JXEwwCeAhBp1Pk1eMDQ4fXCNt9WMc87mDsA68GuGiLWDiCVd6wGHAR6Zqql8lOeQfoDqP/BnJ7oageonpsaB4jw+lQs9sFWIerR1xVAqs0eJyyxUyB6IDx6+kDAV0zy7Xa0Vv2upStoKeQ3fhkpuPHFf0UeABjwIATLmVttnRYtXc0AXFFRRwGUrwozPlQ4l1JbtJRCLqH0JvseMHy0epz4QaCHQ23soAFsOHA2I4JZBkGUoNcZY8CO3CRUF1lRdGM8Yi0mAIBPlHBx2o2uwWmc6XfAJ/LkLzYLybvV0Vo1pdZrCjYsAubDPOQTos048lAB7t6cpNqfEmfBnbmJqN2RiYOfDOLilOb+vAZKZoLlZQANar2qM2A9ZM8hCb8gRIArYRIYOh7fhqKsG3RRcrp8qOnoxeKSX5c+AH8EE/PHm3eOBHaobmJaxtPQSR4AqovSFeRFidBzZR7nhufg9i/L+jbEWVC7navyMC+TSTX/KAOw2U1gqOOxvqswTdb2ixLq37+Ahg/60XjiR9S8qfza5VuSeVwAYHXY3RkRKFUEkLYkbQeQzmM6LzVW1u4amkH/b4t/tycXPbAPzch0spKjeVwAoAxrbkpxoFQRACOhgtMyEmPMsvbo7JJCx+WVVwbE6wQAoOSmts5LeM2WHPlWU6d4k3yPXJ7WewqtAENpoEhtE9/Ebzk0HinNRIE1Xib7/LyD2w4RtgTKVAJgG7kth0B1UTr278yTyfpGFnC6b8KIOQU3tSUUZ8SyGmpKMtBUlQ+2Ittcdrrx3McDkIxtgvhAgcoM0Kr8J2/LSsDzVZtl5H+dcWPvyZ94Epgm1JbQ1dUw3HBvDoQV7CcWPHjyvQuYWPCEY1bBTW0GDC3OlYiLNOGObPmp8+JnQ5hzh/3lFdyUeYDh53C9bEqJgUn45+uPz3twfmQhXLOACjdFAEToC9dPQpQ841+adodrEgDACL2BMsUpREyyM9L8UQuJc8NzupIbPyR7oETBdCq6+3uAKcrW/x9seLKlsidQqlKN2iQQnQjHlUlgaCjPwbt1t+N47W3YulFxfBsAnQSYInuo/w+Yl9sAKCsyndhTmoknyrJRmJmAu/KS8NqjhYgxKyphHrgiltGm1qEawNQr9zuI8LZRb8U5ibJ2UowZeWmxQbR14a3xVyucah1Bd6voWXoBKueuHozNySdPlMh4AmMYW4b5pWDdQQOYPb5rEYT9Rny+890oBib+TJp+UULr2UuYcfmMmAIR7XW23BO0OtCse6xNXW8QY6o3AlrYEGfBVa8Ir9/gMwDDMUdzxb5QKpoH/uQVZyMYThvx73T5DJNnDKcc0d88q6mnx9j1fLm7Nq7XV+J6e+DgLnommys7IwXTzQDaAXh5x6vAA4ZjXh8KeMkDa/WRT4Hgz6x/3fTO/VvPrOtYx1rHHxm4yOkGvwZ0AAAAAElFTkSuQmCC";
private destroy$ = new Subject<void>();

constructor(private fetchClient: FetchClient) { }
constructor(private fetchClient: FetchClient, private formBuilder: FormBuilder, private form: NgForm) { }

ngOnInit() {
if (this.config.device && this.config.device.id) {
this.configDevice = this.config.device.id;
this.datapointSelectionConfig.contextAsset = this.config.device;
this.datapointSelectionConfig.assetSelectorConfig;
}
// Populate the dropdown data if a device id has been previously selected
if (this.config && this.config.device && this.config.device.id) {
this.loadFragmentSeries();
}
this.initForm();
this.formGroup.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
this.config.datapoints = [ ...value.datapoints ];
});
}

ngDoCheck(): void {
if (this.config.device && this.config.device.id !== this.configDevice) {
this.configDevice = this.config.device.id;
const context = this.config.device;
if (context?.id) {
this.datapointSelectionConfig.contextAsset = context;
this.datapointSelectionConfig.assetSelectorConfig
}
}
}

public async loadFragmentSeries(): Promise<void> {
if (!_.has(this.config, "device.id")) {
console.log("Cannot get Measurement fragment and series because the device id is blank.");
Expand Down Expand Up @@ -278,6 +328,22 @@ export class SiloCapacityWidgetConfig implements OnInit {
this.config.enableThresholds = true;
}


private initForm(): void {
this.formGroup = this.createForm();
this.form.form.addControl('config', this.formGroup);
if (this.config?.datapoints) {
this.formGroup.patchValue({ datapoints: this.config.datapoints });
}
}

private createForm() {
return this.formBuilder.group({
datapoints: this.formBuilder.control(new Array<KPIDetails>(), [
Validators.required,
Validators.minLength(1),
exactlyASingleDatapointActive()
])
});
}

}
31 changes: 15 additions & 16 deletions widget/silo-capacity-widget.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,15 @@ export class SiloCapacityWidget implements OnInit, OnDestroy, AfterViewInit, DoC
}

public ngOnInit(): void {

// set the initial state
this.setWidgetInitialState()
.then(() => {
// Only subscribe to realtime measurements if we're not in debugMode
if (!this.config.debugMode) {
// Subscribe to real-time measurements
this.realtimeMeasurement$ = this.realtime.subscribe(`/measurements/${this.config.device.id}`, realtimeData => {
const measurementFragmentAndSeries = this.config.measurementSeries.split('.');
const dataPointsObj = this.config.datapoints.find( dp => dp.__active == true);
const measurementFragmentAndSeries=[dataPointsObj.fragment,dataPointsObj.series];
if (_.has(realtimeData.data.data, `${measurementFragmentAndSeries[0]}.${measurementFragmentAndSeries[1]}`)) {
// console.log('Received realtime measurement: ', JSON.stringify(realtimeData.data.data[measurementFragmentAndSeries[0]][measurementFragmentAndSeries[1]]));
const measurementValue = realtimeData.data.data[measurementFragmentAndSeries[0]][measurementFragmentAndSeries[1]].value;
Expand Down Expand Up @@ -223,7 +223,7 @@ export class SiloCapacityWidget implements OnInit, OnDestroy, AfterViewInit, DoC

private async setWidgetInitialState() {
return new Promise<void>(async (resolve, reject) => {
if (this.config.measurementSeries !== undefined) {
if(this.config.datapoints && this.config.datapoints.length > 0){

// Set the defaults if the information wasn't entered in the widget configuration screen
if (this.config.cylinderHeight === undefined) {
Expand Down Expand Up @@ -346,16 +346,12 @@ export class SiloCapacityWidget implements OnInit, OnDestroy, AfterViewInit, DoC
});

// Only retrieve the latest historical measurement if we're not in debugMode
if (!this.config.debugMode) {
if (this.config.device && this.config.device.id) {
if (!this.config.debugMode) {
if (this.config.datapoints && this.config.datapoints.length > 0) {
const dataPointsObj = this.config.datapoints.find( dp => dp.__active == true);
// Get the measurement configuration details
const measurement = this.config.measurementSeries.split('.');
if (measurement.length !== 2) {
reject('Measurement Series is invalid');
}
const measurementFragment = measurement[0];
const measurementSeries = measurement[1];

const measurementFragment = dataPointsObj.fragment;
const measurementSeries = dataPointsObj.series;
// Get the events ordered by creation date DESC
const endOfToday = new Date();
endOfToday.setHours(23, 59, 59, 0);
Expand Down Expand Up @@ -383,16 +379,19 @@ export class SiloCapacityWidget implements OnInit, OnDestroy, AfterViewInit, DoC
this.setCurrentValue(measurementValue);
}
}
} else {
}
else {
console.log('Unable to subscribe to realtime measurements as no device has been selected');
}
} else {
}
else {
// for debug mode purposes, set the initial measurement to 25%
this.setCurrentFillPercentage(25);
}
resolve();
} else {
reject('Measurement series has not been defined in the widget configuration');
}
else {
reject('Datapoint has not been defined in the widget configuration');
}
});

Expand Down
9 changes: 7 additions & 2 deletions widget/silo-capacity-widget.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { CoreModule, HOOK_COMPONENTS } from "@c8y/ngx-components";
import { CoreModule, DynamicDatapointsResolver, HOOK_COMPONENTS } from "@c8y/ngx-components";
import { NgModule } from "@angular/core";
import { ColorPickerComponent } from "./color-picker/color-picker-component";
import { ColorPaletteComponent } from "./color-picker/color-palette/color-palette-component";
Expand All @@ -24,11 +24,13 @@ import { HttpClientModule } from "@angular/common/http";
import { SiloCapacityWidget } from "./silo-capacity-widget.component";
import { SiloCapacityWidgetConfig } from "./silo-capacity-widget-config.component";
import * as preview from './preview-image';
import { DatapointSelectorModule } from '@c8y/ngx-components/datapoint-selector';

@NgModule({
imports: [
CoreModule,
HttpClientModule
HttpClientModule,
DatapointSelectorModule
],
declarations: [SiloCapacityWidget, SiloCapacityWidgetConfig, ColorPickerComponent, ColorSliderComponent, ColorPaletteComponent],
entryComponents: [SiloCapacityWidget, SiloCapacityWidgetConfig],
Expand All @@ -44,6 +46,9 @@ import * as preview from './preview-image';
component: SiloCapacityWidget,
configComponent: SiloCapacityWidgetConfig,
previewImage: preview.previewImage,
resolve: {
datapoints: DynamicDatapointsResolver,
},
data: {
ng1: {
options: {
Expand Down

0 comments on commit 79379f2

Please sign in to comment.