@corvina/device-example 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.env +96 -0
  2. package/README.md +56 -0
  3. package/bin/corvina-device-example.js +3 -0
  4. package/dist/app.module.js +31 -0
  5. package/dist/app.module.js.map +1 -0
  6. package/dist/controllers/app.controller.js +28 -0
  7. package/dist/controllers/app.controller.js.map +1 -0
  8. package/dist/controllers/device/config.controller.js +79 -0
  9. package/dist/controllers/device/config.controller.js.map +1 -0
  10. package/dist/controllers/device/dice.controller.js +65 -0
  11. package/dist/controllers/device/dice.controller.js.map +1 -0
  12. package/dist/controllers/device/dto/datapoint.dto.js +14 -0
  13. package/dist/controllers/device/dto/datapoint.dto.js.map +1 -0
  14. package/dist/controllers/device/dto/deviceconfig.dto.js +120 -0
  15. package/dist/controllers/device/dto/deviceconfig.dto.js.map +1 -0
  16. package/dist/controllers/device/dto/licensedata.dto.js +16 -0
  17. package/dist/controllers/device/dto/licensedata.dto.js.map +1 -0
  18. package/dist/controllers/device/json.controller.js +137 -0
  19. package/dist/controllers/device/json.controller.js.map +1 -0
  20. package/dist/controllers/device/sine.controller.js +81 -0
  21. package/dist/controllers/device/sine.controller.js.map +1 -0
  22. package/dist/controllers/health.controller.js +41 -0
  23. package/dist/controllers/health.controller.js.map +1 -0
  24. package/dist/main.js +36 -0
  25. package/dist/main.js.map +1 -0
  26. package/dist/services/device.health.js +37 -0
  27. package/dist/services/device.health.js.map +1 -0
  28. package/nest-cli.json +17 -0
  29. package/package.json +47 -0
  30. package/src/.env +130 -0
  31. package/src/app.module.ts +19 -0
  32. package/src/controllers/app.controller.ts +7 -0
  33. package/src/controllers/device/config.controller.ts +42 -0
  34. package/src/controllers/device/dice.controller.ts +38 -0
  35. package/src/controllers/device/dto/datapoint.dto.ts +7 -0
  36. package/src/controllers/device/dto/deviceconfig.dto.ts +110 -0
  37. package/src/controllers/device/dto/licensedata.dto.ts +9 -0
  38. package/src/controllers/device/json.controller.ts +111 -0
  39. package/src/controllers/device/sine.controller.ts +56 -0
  40. package/src/controllers/health.controller.ts +14 -0
  41. package/src/main.ts +33 -0
  42. package/src/services/device.health.ts +20 -0
  43. package/tsconfig.app.json +9 -0
@@ -0,0 +1,7 @@
1
+ import { DataPoint } from "@corvina/corvina-device-sdk";
2
+
3
+ export class DataPointDTO implements DataPoint {
4
+ tagName: string; // tag name
5
+ value: any;
6
+ timestamp: number; // posix time
7
+ }
@@ -0,0 +1,110 @@
1
+ import { ApiProperty } from "@nestjs/swagger";
2
+ import {
3
+ AlarmDesc,
4
+ MultiLangString,
5
+ NoiseSimulationProperties,
6
+ NoiseSimulationType,
7
+ NullableSimulationProperties,
8
+ NullableSimulationStateMachine,
9
+ SimulationDesc,
10
+ SimulationType,
11
+ PacketFormatEnum,
12
+ TagDesc,
13
+ } from "@corvina/corvina-device-sdk";
14
+ import { DeviceConfig } from "@corvina/corvina-device-sdk";
15
+
16
+ export class NullableSimulationStateMachineDTO implements NullableSimulationStateMachine {
17
+ nullifying: boolean;
18
+ start: number;
19
+ duration: number;
20
+ }
21
+
22
+ export class NullableSimulationPropertiesDTO implements NullableSimulationProperties {
23
+ probability: number;
24
+ dt_min: number;
25
+ dt_max: number;
26
+ state?: NullableSimulationStateMachineDTO;
27
+ }
28
+
29
+ export class NoiseSimulationPropertiesDTO implements NoiseSimulationProperties {
30
+ type: NoiseSimulationType;
31
+ amplitude: number;
32
+ }
33
+
34
+ export class SimulationDescDTO implements SimulationDesc {
35
+ type: SimulationType;
36
+ noise: NoiseSimulationPropertiesDTO;
37
+ nullable: NullableSimulationPropertiesDTO;
38
+ }
39
+
40
+ export class TagDescDTO implements TagDesc {
41
+ name: string;
42
+ type: string;
43
+ simulation?: SimulationDescDTO;
44
+ }
45
+
46
+ export class MultiLangStringDTO implements MultiLangString {
47
+ [languageCode: string]: string;
48
+ }
49
+
50
+ export class AlarmDescDTO implements AlarmDesc {
51
+ name: string;
52
+ desc: MultiLangStringDTO;
53
+ source: string;
54
+ severity: number;
55
+ ack_required: boolean;
56
+ reset_required: boolean;
57
+ enabled: boolean;
58
+ simulation: SimulationDescDTO;
59
+ }
60
+
61
+ export class DeviceConfigDTO {
62
+ activationKey?: string;
63
+ pairingEndpoint?: string;
64
+ availableTagsFile?: string; // json array string
65
+ availableTags?: TagDescDTO[]; // json array string
66
+ simulateTags?: boolean;
67
+ availableAlarms?: AlarmDescDTO[]; // json array string
68
+ simulateAlarms?: boolean;
69
+ packetFormat?: PacketFormatEnum;
70
+
71
+ private arrayToMap(input: { name: string }[]): Map<string, any> {
72
+ const map = new Map<string, any>();
73
+ for (const item of input) {
74
+ map.set(item.name, item);
75
+ }
76
+ return map;
77
+ }
78
+
79
+ private mapToArray(input: Map<string, any>) {
80
+ const array = [];
81
+ input.forEach((value, key) => {
82
+ array.push({ name: key, value });
83
+ });
84
+ return array;
85
+ }
86
+
87
+ toDeviceConfig(): DeviceConfig {
88
+ return {
89
+ activationKey: this.activationKey,
90
+ pairingEndpoint: this.pairingEndpoint,
91
+ availableTagsFile: this.availableTagsFile,
92
+ availableTags: this.arrayToMap(this.availableTags),
93
+ simulateTags: this.simulateTags,
94
+ availableAlarms: this.arrayToMap(this.availableAlarms),
95
+ simulateAlarms: this.simulateAlarms,
96
+ packetFormat: this.packetFormat,
97
+ };
98
+ }
99
+
100
+ constructor(deviceConfig: DeviceConfig) {
101
+ this.activationKey = deviceConfig.activationKey;
102
+ this.pairingEndpoint = deviceConfig.pairingEndpoint;
103
+ this.availableTagsFile = deviceConfig.availableTagsFile;
104
+ this.availableTags = this.mapToArray(deviceConfig.availableTags);
105
+ this.simulateTags = deviceConfig.simulateTags;
106
+ this.availableAlarms = this.mapToArray(deviceConfig.availableAlarms);
107
+ this.simulateAlarms = deviceConfig.simulateAlarms;
108
+ this.packetFormat = deviceConfig.packetFormat;
109
+ }
110
+ }
@@ -0,0 +1,9 @@
1
+ import { LicenseData } from "@corvina/corvina-device-sdk";
2
+
3
+ export class LicenseDataDTO implements LicenseData {
4
+ realm: string;
5
+ logicalId: string;
6
+ apiKey: string;
7
+ platformPairingApiUrl: string;
8
+ brokerUrls: string[];
9
+ }
@@ -0,0 +1,111 @@
1
+ import { Logger, Controller, Injectable, Post, Query, Inject, Body, Param } from "@nestjs/common";
2
+ import { DeviceService } from "@corvina/corvina-device-sdk";
3
+ import { DataPoint } from "@corvina/corvina-device-sdk";
4
+ import { ApiBody, ApiOperation, ApiQuery, ApiTags } from "@nestjs/swagger";
5
+ import { DataPointDTO } from "./dto/datapoint.dto";
6
+ import axios from "axios";
7
+
8
+ /** Handles requests from Nebbiolo FOG CEP */
9
+ @ApiTags("device")
10
+ @Controller("/device/json/")
11
+ @Injectable()
12
+ export class Json {
13
+ private readonly l = new Logger(Json.name);
14
+ @Inject() private readonly deviceService: DeviceService;
15
+
16
+ @ApiOperation({
17
+ summary: "Post a new JSON value",
18
+ callbacks: {
19
+ result: {
20
+ "{$request.query#/callback}": {
21
+ post: {
22
+ requestBody: {
23
+ required: true,
24
+ content: {
25
+ "application/json": {
26
+ schema: {
27
+ type: "object",
28
+ properties: {
29
+ error: {
30
+ type: "object",
31
+ properties: {
32
+ message: {
33
+ type: "string",
34
+ },
35
+ },
36
+ },
37
+ tagName: {
38
+ type: "string",
39
+ },
40
+ modelPath: {
41
+ type: "string",
42
+ },
43
+ },
44
+ },
45
+ },
46
+ },
47
+ },
48
+ responses: {
49
+ "200": { description: "The server acknowledged the request" },
50
+ },
51
+ },
52
+ },
53
+ },
54
+ },
55
+ })
56
+ @ApiQuery({
57
+ name: "tagName",
58
+ description:
59
+ "Prefix of device identifier (name) of data source. The actual tag names advertised to the cloud are automatically generated as this prefix plus each property JSON path. The prefix can be undefined, so that only JSON paths are used",
60
+ schema: { default: undefined, example: "temperature" },
61
+ required: false,
62
+ })
63
+ @ApiQuery({
64
+ name: "timestamp",
65
+ description: "Specify a timestamp (if omitted the request timestamp is used) ",
66
+ required: false,
67
+ })
68
+ @ApiQuery({
69
+ name: "callback",
70
+ description: "Specify an optional callback to be invoked when the message has been delivered",
71
+ schema: { type: "string", format: "uri", example: "http://localhost:30001" },
72
+ required: false,
73
+ })
74
+ @ApiQuery({
75
+ name: "qos",
76
+ description: "Specify an optional qos (by default is zeo)",
77
+ schema: { type: "number", minimum: 0, maximum: 2, default: 0, example: 1 },
78
+ required: false,
79
+ })
80
+ @ApiBody({
81
+ description:
82
+ "The json value to set. Each property in the json is assigned as full tag name advertised to the cloud as ```availableTags``` the corresponding json path prefixed by ```tagName```. For instance, if ```tagName=\"tag\"``` and value is ```{ a: {b : 1 } }``` the advertised list of available tags is ```['tag.a.b']```. If ```tagName``` is undefined the advertised list of available tags is simply ```['a.b']```",
83
+ schema: { default: "{}", example: { Tag1: 1 } },
84
+ })
85
+ @Post()
86
+ async post(
87
+ @Query("tagName") tagName = undefined,
88
+ @Query("timestamp") timestamp = undefined,
89
+ @Query("qos") qos = undefined,
90
+ @Query("callback") postCallback = undefined,
91
+ @Body() v: any,
92
+ ): Promise<DataPointDTO[]> {
93
+ const t = timestamp || Date.now();
94
+ const dataPoints = new Array<DataPoint>();
95
+ const dp: DataPoint = {
96
+ tagName,
97
+ value: v,
98
+ timestamp: t,
99
+ };
100
+ dataPoints.push(dp);
101
+ await this.deviceService.post(dataPoints, {
102
+ qos: qos || 0,
103
+ cb: postCallback
104
+ ? (error, tagName, modelPath) => {
105
+ axios.post(postCallback, { error: { message: error.message }, tagName, modelPath });
106
+ }
107
+ : undefined,
108
+ });
109
+ return dataPoints;
110
+ }
111
+ }
@@ -0,0 +1,56 @@
1
+ import { Logger, Controller, Injectable, Post, Query, Inject } from "@nestjs/common";
2
+ import { DeviceService } from "@corvina/corvina-device-sdk";
3
+ import { DataPoint } from "@corvina/corvina-device-sdk";
4
+ import { ApiOperation, ApiQuery, ApiTags } from "@nestjs/swagger";
5
+ import { DataPointDTO } from "./dto/datapoint.dto";
6
+
7
+ /** Handles requests from Nebbiolo FOG CEP */
8
+ @ApiTags("device")
9
+ @Controller("/device/sine")
10
+ @Injectable()
11
+ export class Sine {
12
+ private readonly l = new Logger(Sine.name);
13
+ @Inject() private readonly deviceService: DeviceService;
14
+
15
+ @ApiOperation({
16
+ summary: "Post a new value sampled from a sine wave with the given parameters, using wall clock as time base",
17
+ })
18
+ @ApiQuery({
19
+ name: "tagName",
20
+ description: "device identifier (name) of data source",
21
+ schema: { default: "Tag" },
22
+ required: false,
23
+ })
24
+ @ApiQuery({
25
+ name: "period",
26
+ description: "period in ms of the sine wave",
27
+ required: false,
28
+ schema: { default: 1000 },
29
+ })
30
+ @ApiQuery({
31
+ name: "amplitude",
32
+ description: "amplitude of the sine wave",
33
+ required: false,
34
+ schema: { default: 1000 },
35
+ })
36
+ @ApiQuery({ name: "phase", description: "phase of the sine wave", required: false, schema: { default: 0 } })
37
+ @Post()
38
+ async post(
39
+ @Query("tagName") tagName = "Tag",
40
+ @Query("period") period = 1000,
41
+ @Query("amplitude") amplitude = 1000,
42
+ @Query("phase") phase = 0,
43
+ ): Promise<DataPointDTO[]> {
44
+ const t = Date.now();
45
+ const v = amplitude * Math.sin(phase + (Date.now() / period) * 2 * Math.PI);
46
+ const dataPoints = new Array<DataPoint>();
47
+ const dp: DataPoint = {
48
+ tagName,
49
+ value: v,
50
+ timestamp: t,
51
+ };
52
+ dataPoints.push(dp);
53
+ await this.deviceService.post(dataPoints);
54
+ return dataPoints;
55
+ }
56
+ }
@@ -0,0 +1,14 @@
1
+ import { Controller, Get } from "@nestjs/common";
2
+ import { DeviceHealthIndicator } from "../services/device.health";
3
+ import { HealthCheck, HealthCheckService } from "@nestjs/terminus";
4
+
5
+ @Controller("health")
6
+ export class HealthController {
7
+ constructor(private health: HealthCheckService, private deviceHealthIndicator: DeviceHealthIndicator) {}
8
+
9
+ @Get()
10
+ @HealthCheck()
11
+ check() {
12
+ return this.health.check([async () => this.deviceHealthIndicator.isHealthy("device")]);
13
+ }
14
+ }
package/src/main.ts ADDED
@@ -0,0 +1,33 @@
1
+ import { DeviceService, DeviceRunnerService } from "@corvina/corvina-device-sdk";
2
+ import { NestFactory } from "@nestjs/core";
3
+ import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
4
+ import axios from "axios";
5
+ import { AppModule } from "./app.module";
6
+
7
+ async function bootstrap() {
8
+ const app = await NestFactory.create(AppModule);
9
+
10
+ try {
11
+ const config = new DocumentBuilder()
12
+ .setTitle("Corvina NodeJS device API")
13
+ .setDescription("")
14
+ .setVersion("1.0")
15
+ .addTag("device")
16
+ .build();
17
+ const document = SwaggerModule.createDocument(app, config);
18
+ SwaggerModule.setup("swagger-ui", app, document);
19
+ } catch (e) {
20
+ console.error("Error caught: ", e);
21
+ }
22
+
23
+ await app.listen(3000);
24
+
25
+ app.get(DeviceRunnerService).run();
26
+ app.get(DeviceService).on("write", (event) => {
27
+ console.log("Write event received", event);
28
+ if (process.env["WRITE_CALLBACK"]) {
29
+ axios.post(process.env["WRITE_CALLBACK"], event);
30
+ }
31
+ });
32
+ }
33
+ bootstrap();
@@ -0,0 +1,20 @@
1
+ import { Injectable } from "@nestjs/common";
2
+ import { HealthIndicatorResult, HealthIndicator, HealthCheckError } from "@nestjs/terminus";
3
+ import { DeviceService } from "@corvina/corvina-device-sdk";
4
+
5
+ @Injectable()
6
+ export class DeviceHealthIndicator extends HealthIndicator {
7
+ constructor(private readonly deviceService: DeviceService) {
8
+ super();
9
+ }
10
+
11
+ async isHealthy(key: string): Promise<HealthIndicatorResult> {
12
+ const isHealthy = this.deviceService.status.ready == true;
13
+ const result = this.getStatus(key, isHealthy, this.deviceService.status);
14
+
15
+ if (this.deviceService.isReady) {
16
+ return result;
17
+ }
18
+ throw new HealthCheckError("Device not ready", result);
19
+ }
20
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "declaration": false,
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src/**/*"],
8
+ "exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
9
+ }