@uns-kit/cli 2.0.24 → 2.0.26
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.
- package/LICENSE +21 -21
- package/README.md +175 -175
- package/package.json +6 -6
- package/templates/api/src/examples/api-example.ts +91 -91
- package/templates/azure-pipelines.yml +21 -21
- package/templates/codegen/codegen.ts +15 -15
- package/templates/codegen/src/uns/uns-tags.ts +1 -1
- package/templates/codegen/src/uns/uns-topics.ts +1 -1
- package/templates/config-files/config-docker.json +26 -26
- package/templates/config-files/config-localhost.json +26 -26
- package/templates/cron/AGENTS.md +24 -23
- package/templates/cron/src/examples/cron-example.ts +71 -71
- package/templates/default/.prettierignore +1 -1
- package/templates/default/.prettierrc +7 -7
- package/templates/default/AGENTS.md +24 -23
- package/templates/default/README.md +43 -41
- package/templates/default/config.json +27 -27
- package/templates/default/eslint.config.js +30 -30
- package/templates/default/gitignore +51 -51
- package/templates/default/package.json +49 -46
- package/templates/default/src/config/project.config.extension.example +23 -23
- package/templates/default/src/config/project.config.extension.ts +6 -6
- package/templates/default/src/examples/data-example.ts +86 -86
- package/templates/default/src/examples/load-test-data.ts +110 -110
- package/templates/default/src/examples/table-example.ts +97 -97
- package/templates/default/src/examples/table-window-load-test.ts +446 -446
- package/templates/default/src/examples/uns-gateway-cli.ts +10 -10
- package/templates/default/src/index.ts +15 -15
- package/templates/default/src/uns/uns-assets.ts +12 -12
- package/templates/default/src/uns/uns-dictionary.generated.ts +758 -758
- package/templates/default/src/uns/uns-measurements.generated.ts +366 -366
- package/templates/default/src/uns/uns-tags.ts +2 -2
- package/templates/default/src/uns/uns-topics.ts +2 -2
- package/templates/default/tsconfig.json +29 -29
- package/templates/python/app/README.md +8 -8
- package/templates/python/examples/README.md +134 -134
- package/templates/python/examples/api_handler.py +28 -28
- package/templates/python/examples/data_publish.py +11 -11
- package/templates/python/examples/data_subscribe.py +8 -8
- package/templates/python/examples/data_transformer.py +17 -17
- package/templates/python/examples/table_transformer.py +15 -15
- package/templates/python/gateway/cli.py +75 -75
- package/templates/python/gateway/client.py +155 -155
- package/templates/python/gateway/manager.py +97 -97
- package/templates/python/gen/__init__.py +1 -0
- package/templates/python/gen/uns_gateway_pb2.py +70 -0
- package/templates/python/gen/uns_gateway_pb2_grpc.py +312 -0
- package/templates/python/gitignore +47 -47
- package/templates/python/proto/uns-gateway.proto +102 -102
- package/templates/python/pyproject.toml +4 -4
- package/templates/python/runtime.json +4 -4
- package/templates/python/scripts/setup.sh +87 -87
- package/templates/temporal/src/examples/temporal-example.ts +37 -37
- package/templates/uns-dictionary/uns-dictionary.json +650 -650
- package/templates/uns-measurements/uns-measurements.json +360 -360
- package/templates/vscode/.vscode/launch.json +164 -164
- package/templates/vscode/.vscode/settings.json +9 -9
- package/templates/vscode/.vscode/tasks.json +27 -27
- package/templates/vscode/uns-kit.code-workspace +13 -13
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
# Dependencies
|
|
2
|
-
node_modules/
|
|
3
|
-
.pnpm-store/
|
|
4
|
-
.pnpm/
|
|
5
|
-
.pnp.cjs
|
|
6
|
-
.pnp.loader.mjs
|
|
7
|
-
|
|
8
|
-
# Build outputs
|
|
9
|
-
build/
|
|
10
|
-
dist/
|
|
11
|
-
tmp/
|
|
12
|
-
temp/
|
|
13
|
-
*.tsbuildinfo
|
|
14
|
-
|
|
15
|
-
# Logs & diagnostics
|
|
16
|
-
logs/
|
|
17
|
-
*.log
|
|
18
|
-
npm-debug.log*
|
|
19
|
-
yarn-debug.log*
|
|
20
|
-
pnpm-debug.log*
|
|
21
|
-
lerna-debug.log*
|
|
22
|
-
|
|
23
|
-
# Test & coverage artifacts
|
|
24
|
-
coverage/
|
|
25
|
-
.nyc_output/
|
|
26
|
-
.vitest/
|
|
27
|
-
|
|
28
|
-
# Runtime queues & sandboxes
|
|
29
|
-
publisherQueue/
|
|
30
|
-
sandbox-*
|
|
31
|
-
tmp-rtt
|
|
32
|
-
.tmp-rtt
|
|
33
|
-
|
|
34
|
-
# Environment & secrets
|
|
35
|
-
.env
|
|
36
|
-
.env.*
|
|
37
|
-
!.env.example
|
|
38
|
-
!.env.sample
|
|
39
|
-
|
|
40
|
-
# UNS configuration overrides
|
|
41
|
-
config*.json
|
|
42
|
-
|
|
43
|
-
# Editor files & OS cruft
|
|
44
|
-
.idea/
|
|
45
|
-
.DS_Store
|
|
46
|
-
Thumbs.db
|
|
47
|
-
*.swp
|
|
48
|
-
*.swo
|
|
49
|
-
|
|
50
|
-
# Package managers
|
|
51
|
-
package-lock.json
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
.pnpm-store/
|
|
4
|
+
.pnpm/
|
|
5
|
+
.pnp.cjs
|
|
6
|
+
.pnp.loader.mjs
|
|
7
|
+
|
|
8
|
+
# Build outputs
|
|
9
|
+
build/
|
|
10
|
+
dist/
|
|
11
|
+
tmp/
|
|
12
|
+
temp/
|
|
13
|
+
*.tsbuildinfo
|
|
14
|
+
|
|
15
|
+
# Logs & diagnostics
|
|
16
|
+
logs/
|
|
17
|
+
*.log
|
|
18
|
+
npm-debug.log*
|
|
19
|
+
yarn-debug.log*
|
|
20
|
+
pnpm-debug.log*
|
|
21
|
+
lerna-debug.log*
|
|
22
|
+
|
|
23
|
+
# Test & coverage artifacts
|
|
24
|
+
coverage/
|
|
25
|
+
.nyc_output/
|
|
26
|
+
.vitest/
|
|
27
|
+
|
|
28
|
+
# Runtime queues & sandboxes
|
|
29
|
+
publisherQueue/
|
|
30
|
+
sandbox-*
|
|
31
|
+
tmp-rtt
|
|
32
|
+
.tmp-rtt
|
|
33
|
+
|
|
34
|
+
# Environment & secrets
|
|
35
|
+
.env
|
|
36
|
+
.env.*
|
|
37
|
+
!.env.example
|
|
38
|
+
!.env.sample
|
|
39
|
+
|
|
40
|
+
# UNS configuration overrides
|
|
41
|
+
config*.json
|
|
42
|
+
|
|
43
|
+
# Editor files & OS cruft
|
|
44
|
+
.idea/
|
|
45
|
+
.DS_Store
|
|
46
|
+
Thumbs.db
|
|
47
|
+
*.swp
|
|
48
|
+
*.swo
|
|
49
|
+
|
|
50
|
+
# Package managers
|
|
51
|
+
package-lock.json
|
|
@@ -1,46 +1,49 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "__APP_NAME__",
|
|
3
|
-
"version": "0.0.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"description": "UNS application generated by @uns-kit/cli",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"engines": {
|
|
8
|
-
"node": ">=22"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"dist"
|
|
12
|
-
],
|
|
13
|
-
"exports": {
|
|
14
|
-
".": {
|
|
15
|
-
"types": "./dist/index.d.ts",
|
|
16
|
-
"default": "./dist/index.js"
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
"scripts": {
|
|
20
|
-
"clean": "rimraf dist tsconfig.tsbuildinfo",
|
|
21
|
-
"typecheck": "tsc --noEmit",
|
|
22
|
-
"build": "pnpm run clean && tsc -p tsconfig.json",
|
|
23
|
-
"dev": "tsx watch src/index.ts",
|
|
24
|
-
"start": "node dist/index.js",
|
|
25
|
-
"lint": "eslint src --ext .ts",
|
|
26
|
-
"lint:fix": "eslint src --ext .ts --fix",
|
|
27
|
-
"format": "prettier --write .",
|
|
28
|
-
"format:check": "prettier --check .",
|
|
29
|
-
"generate-config-schema": "node ./node_modules/@uns-kit/core/dist/tools/generate-config-schema.js",
|
|
30
|
-
"generate-codegen": "graphql-code-generator --require dotenv/config --config codegen.ts",
|
|
31
|
-
"generate-uns-metadata": "node ./node_modules/@uns-kit/core/dist/tools/refresh-uns.js",
|
|
32
|
-
"generate-uns-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "__APP_NAME__",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "UNS application generated by @uns-kit/cli",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=22"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"clean": "rimraf dist tsconfig.tsbuildinfo",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"build": "pnpm run clean && tsc -p tsconfig.json",
|
|
23
|
+
"dev": "tsx watch src/index.ts",
|
|
24
|
+
"start": "node dist/index.js",
|
|
25
|
+
"lint": "eslint src --ext .ts",
|
|
26
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
27
|
+
"format": "prettier --write .",
|
|
28
|
+
"format:check": "prettier --check .",
|
|
29
|
+
"generate-config-schema": "node ./node_modules/@uns-kit/core/dist/tools/generate-config-schema.js",
|
|
30
|
+
"generate-codegen": "graphql-code-generator --require dotenv/config --config codegen.ts",
|
|
31
|
+
"generate-uns-metadata": "node ./node_modules/@uns-kit/core/dist/tools/refresh-uns.js",
|
|
32
|
+
"generate-uns-dictionary": "node ./node_modules/@uns-kit/core/dist/tools/generate-uns-dictionary.js --input uns-dictionary.json --output src/uns/uns-dictionary.generated.ts",
|
|
33
|
+
"generate-uns-measurements": "node ./node_modules/@uns-kit/core/dist/tools/generate-uns-measurements.js --input uns-measurements.json --output src/uns/uns-measurements.generated.ts",
|
|
34
|
+
"generate-uns-reference": "node ./node_modules/@uns-kit/core/dist/tools/generate-uns-reference.js --dictionary uns-dictionary.json --dictionary-output src/uns/uns-dictionary.generated.ts --measurements uns-measurements.json --measurements-output src/uns/uns-measurements.generated.ts",
|
|
35
|
+
"sync-uns-schema": "node ./node_modules/@uns-kit/core/dist/tools/sync-uns-schema.js"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@uns-kit/core": "__UNS_KIT_CORE_VERSION__",
|
|
39
|
+
"zod": "^3.23.8"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^22.19.1",
|
|
43
|
+
"eslint": "^9.39.2",
|
|
44
|
+
"prettier": "^3.8.0",
|
|
45
|
+
"rimraf": "^6.1.2",
|
|
46
|
+
"tsx": "^4.20.6",
|
|
47
|
+
"typescript": "^5.9.3"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { secretValueSchema } from "@uns-kit/core/uns-config/secret-placeholders.js";
|
|
3
|
-
import { hostValueSchema } from "@uns-kit/core/uns-config/host-placeholders.js";
|
|
4
|
-
|
|
5
|
-
// Extend this schema with project-specific configuration sections.
|
|
6
|
-
export const projectExtrasSchema = z.object({
|
|
7
|
-
pg: z.object({
|
|
8
|
-
user: z.string().min(1, "pg.user is required"),
|
|
9
|
-
host: hostValueSchema,
|
|
10
|
-
port: z.number().int().positive().default(5432),
|
|
11
|
-
ssl: z.boolean().default(false),
|
|
12
|
-
database: z.string().min(1, "pg.database is required"),
|
|
13
|
-
isPoolConnection: z.boolean().default(false),
|
|
14
|
-
password: secretValueSchema.optional(),
|
|
15
|
-
}),
|
|
16
|
-
|
|
17
|
-
caddy: z.object({
|
|
18
|
-
adminUrl: z.string().url(),
|
|
19
|
-
proxyHost: z.string().url(),
|
|
20
|
-
}),
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
export type ProjectExtras = z.infer<typeof projectExtrasSchema>;
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { secretValueSchema } from "@uns-kit/core/uns-config/secret-placeholders.js";
|
|
3
|
+
import { hostValueSchema } from "@uns-kit/core/uns-config/host-placeholders.js";
|
|
4
|
+
|
|
5
|
+
// Extend this schema with project-specific configuration sections.
|
|
6
|
+
export const projectExtrasSchema = z.object({
|
|
7
|
+
pg: z.object({
|
|
8
|
+
user: z.string().min(1, "pg.user is required"),
|
|
9
|
+
host: hostValueSchema,
|
|
10
|
+
port: z.number().int().positive().default(5432),
|
|
11
|
+
ssl: z.boolean().default(false),
|
|
12
|
+
database: z.string().min(1, "pg.database is required"),
|
|
13
|
+
isPoolConnection: z.boolean().default(false),
|
|
14
|
+
password: secretValueSchema.optional(),
|
|
15
|
+
}),
|
|
16
|
+
|
|
17
|
+
caddy: z.object({
|
|
18
|
+
adminUrl: z.string().url(),
|
|
19
|
+
proxyHost: z.string().url(),
|
|
20
|
+
}),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export type ProjectExtras = z.infer<typeof projectExtrasSchema>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
// Extend this schema with project-specific configuration sections.
|
|
4
|
-
export const projectExtrasSchema = z.object({});
|
|
5
|
-
|
|
6
|
-
export type ProjectExtras = z.infer<typeof projectExtrasSchema>;
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
// Extend this schema with project-specific configuration sections.
|
|
4
|
+
export const projectExtrasSchema = z.object({});
|
|
5
|
+
|
|
6
|
+
export type ProjectExtras = z.infer<typeof projectExtrasSchema>;
|
|
@@ -1,86 +1,86 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Change this file according to your specifications and rename it to index.ts
|
|
3
|
-
*/
|
|
4
|
-
import { UnsProxyProcess, ConfigFile, getLogger } from "@uns-kit/core";
|
|
5
|
-
import { registerAttributeDescriptions, registerObjectTypeDescriptions } from "@uns-kit/core/uns/uns-dictionary-registry.js";
|
|
6
|
-
import { GeneratedPhysicalMeasurements } from "../uns/uns-measurements.generated.js";
|
|
7
|
-
import { UnsTopics } from "@uns-kit/core/uns/uns-topics.js";
|
|
8
|
-
import {
|
|
9
|
-
GeneratedObjectTypes,
|
|
10
|
-
GeneratedAttributes,
|
|
11
|
-
GeneratedAttributesByType,
|
|
12
|
-
GeneratedObjectTypeDescriptions,
|
|
13
|
-
} from "../uns/uns-dictionary.generated.js";
|
|
14
|
-
import { GeneratedAssets, resolveGeneratedAsset } from "../uns/uns-assets.js";
|
|
15
|
-
import type { ISO8601 } from "@uns-kit/core/uns/uns-interfaces.js";
|
|
16
|
-
|
|
17
|
-
const logger = getLogger(import.meta.url);
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Load the configuration from a file.
|
|
21
|
-
* On the server, this file is provided by the `uns-datahub-controller`.
|
|
22
|
-
* In the development environment, you are responsible for creating and maintaining this file and its contents.
|
|
23
|
-
*/
|
|
24
|
-
const config = await ConfigFile.loadConfig();
|
|
25
|
-
registerObjectTypeDescriptions(GeneratedObjectTypeDescriptions);
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Connect to input and output brokers
|
|
29
|
-
*/
|
|
30
|
-
const unsProxyProcess = new UnsProxyProcess(config.infra.host!, {processName: config.uns.processName!});
|
|
31
|
-
const mqttInput = await unsProxyProcess.createUnsMqttProxy((config.input?.host)!, "templateUnsRttInput", config.uns.instanceMode!, config.uns.handover!, {
|
|
32
|
-
mqttSubToTopics: ["raw/#"],
|
|
33
|
-
});
|
|
34
|
-
const mqttOutput = await unsProxyProcess.createUnsMqttProxy((config.output?.host)!, "templateUnsRttOutput", config.uns.instanceMode!, config.uns.handover!, { publishThrottlingDelay: 1000});
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Event listener for input events.
|
|
39
|
-
* Transform an input message and publish it with publishMqttMessage function.
|
|
40
|
-
*/
|
|
41
|
-
mqttInput.event.on("input", async (event) => {
|
|
42
|
-
try {
|
|
43
|
-
if (event.topic === "raw/data") {
|
|
44
|
-
const values = event.message.split(",");
|
|
45
|
-
const [countRaw, timestampRaw, sensorRaw] = values;
|
|
46
|
-
if (!countRaw || !timestampRaw || !sensorRaw) {
|
|
47
|
-
logger.warn(`Skipping malformed raw/data payload: ${event.message}`);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
const numberValue = Number.parseFloat(countRaw);
|
|
51
|
-
const eventDate = new Date(Number.parseInt(timestampRaw, 10));
|
|
52
|
-
const sensorValue = Number.parseFloat(sensorRaw);
|
|
53
|
-
const time: ISO8601 = eventDate.toISOString() as ISO8601;
|
|
54
|
-
const intervalStart: ISO8601 = new Date(eventDate.getTime() - 1000).toISOString() as ISO8601;
|
|
55
|
-
const intervalEnd: ISO8601 = eventDate.toISOString() as ISO8601;
|
|
56
|
-
|
|
57
|
-
const dataGroup = "sensor";
|
|
58
|
-
|
|
59
|
-
const topic: UnsTopics = "enterprise/site/area/line/";
|
|
60
|
-
const asset = resolveGeneratedAsset("asset");
|
|
61
|
-
const assetDescription = ""; // customize manually
|
|
62
|
-
|
|
63
|
-
mqttOutput.publishMqttMessage({
|
|
64
|
-
topic,
|
|
65
|
-
asset,
|
|
66
|
-
assetDescription,
|
|
67
|
-
objectType: GeneratedObjectTypes["energy-resource"],
|
|
68
|
-
objectId: "main",
|
|
69
|
-
attributes: [
|
|
70
|
-
{
|
|
71
|
-
attribute: GeneratedAttributesByType["energy-resource"]["current"],
|
|
72
|
-
data: { dataGroup, time, value: numberValue, uom: GeneratedPhysicalMeasurements.Ampere, intervalStart, intervalEnd },
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
attribute: GeneratedAttributes["voltage"],
|
|
76
|
-
data: { dataGroup, time, value: sensorValue, uom: GeneratedPhysicalMeasurements.Volt, intervalStart, intervalEnd },
|
|
77
|
-
},
|
|
78
|
-
],
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
} catch (error) {
|
|
82
|
-
const reason = error instanceof Error ? error : new Error(String(error));
|
|
83
|
-
logger.error(`Error publishing message to MQTT: ${reason.message}`);
|
|
84
|
-
throw reason;
|
|
85
|
-
}
|
|
86
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* Change this file according to your specifications and rename it to index.ts
|
|
3
|
+
*/
|
|
4
|
+
import { UnsProxyProcess, ConfigFile, getLogger } from "@uns-kit/core";
|
|
5
|
+
import { registerAttributeDescriptions, registerObjectTypeDescriptions } from "@uns-kit/core/uns/uns-dictionary-registry.js";
|
|
6
|
+
import { GeneratedPhysicalMeasurements } from "../uns/uns-measurements.generated.js";
|
|
7
|
+
import { UnsTopics } from "@uns-kit/core/uns/uns-topics.js";
|
|
8
|
+
import {
|
|
9
|
+
GeneratedObjectTypes,
|
|
10
|
+
GeneratedAttributes,
|
|
11
|
+
GeneratedAttributesByType,
|
|
12
|
+
GeneratedObjectTypeDescriptions,
|
|
13
|
+
} from "../uns/uns-dictionary.generated.js";
|
|
14
|
+
import { GeneratedAssets, resolveGeneratedAsset } from "../uns/uns-assets.js";
|
|
15
|
+
import type { ISO8601 } from "@uns-kit/core/uns/uns-interfaces.js";
|
|
16
|
+
|
|
17
|
+
const logger = getLogger(import.meta.url);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Load the configuration from a file.
|
|
21
|
+
* On the server, this file is provided by the `uns-datahub-controller`.
|
|
22
|
+
* In the development environment, you are responsible for creating and maintaining this file and its contents.
|
|
23
|
+
*/
|
|
24
|
+
const config = await ConfigFile.loadConfig();
|
|
25
|
+
registerObjectTypeDescriptions(GeneratedObjectTypeDescriptions);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Connect to input and output brokers
|
|
29
|
+
*/
|
|
30
|
+
const unsProxyProcess = new UnsProxyProcess(config.infra.host!, {processName: config.uns.processName!});
|
|
31
|
+
const mqttInput = await unsProxyProcess.createUnsMqttProxy((config.input?.host)!, "templateUnsRttInput", config.uns.instanceMode!, config.uns.handover!, {
|
|
32
|
+
mqttSubToTopics: ["raw/#"],
|
|
33
|
+
});
|
|
34
|
+
const mqttOutput = await unsProxyProcess.createUnsMqttProxy((config.output?.host)!, "templateUnsRttOutput", config.uns.instanceMode!, config.uns.handover!, { publishThrottlingDelay: 1000});
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Event listener for input events.
|
|
39
|
+
* Transform an input message and publish it with publishMqttMessage function.
|
|
40
|
+
*/
|
|
41
|
+
mqttInput.event.on("input", async (event) => {
|
|
42
|
+
try {
|
|
43
|
+
if (event.topic === "raw/data") {
|
|
44
|
+
const values = event.message.split(",");
|
|
45
|
+
const [countRaw, timestampRaw, sensorRaw] = values;
|
|
46
|
+
if (!countRaw || !timestampRaw || !sensorRaw) {
|
|
47
|
+
logger.warn(`Skipping malformed raw/data payload: ${event.message}`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const numberValue = Number.parseFloat(countRaw);
|
|
51
|
+
const eventDate = new Date(Number.parseInt(timestampRaw, 10));
|
|
52
|
+
const sensorValue = Number.parseFloat(sensorRaw);
|
|
53
|
+
const time: ISO8601 = eventDate.toISOString() as ISO8601;
|
|
54
|
+
const intervalStart: ISO8601 = new Date(eventDate.getTime() - 1000).toISOString() as ISO8601;
|
|
55
|
+
const intervalEnd: ISO8601 = eventDate.toISOString() as ISO8601;
|
|
56
|
+
|
|
57
|
+
const dataGroup = "sensor";
|
|
58
|
+
|
|
59
|
+
const topic: UnsTopics = "enterprise/site/area/line/";
|
|
60
|
+
const asset = resolveGeneratedAsset("asset");
|
|
61
|
+
const assetDescription = ""; // customize manually
|
|
62
|
+
|
|
63
|
+
mqttOutput.publishMqttMessage({
|
|
64
|
+
topic,
|
|
65
|
+
asset,
|
|
66
|
+
assetDescription,
|
|
67
|
+
objectType: GeneratedObjectTypes["energy-resource"],
|
|
68
|
+
objectId: "main",
|
|
69
|
+
attributes: [
|
|
70
|
+
{
|
|
71
|
+
attribute: GeneratedAttributesByType["energy-resource"]["current"],
|
|
72
|
+
data: { dataGroup, time, value: numberValue, uom: GeneratedPhysicalMeasurements.Ampere, intervalStart, intervalEnd },
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
attribute: GeneratedAttributes["voltage"],
|
|
76
|
+
data: { dataGroup, time, value: sensorValue, uom: GeneratedPhysicalMeasurements.Volt, intervalStart, intervalEnd },
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
const reason = error instanceof Error ? error : new Error(String(error));
|
|
83
|
+
logger.error(`Error publishing message to MQTT: ${reason.message}`);
|
|
84
|
+
throw reason;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Load the configuration from a file.
|
|
3
|
-
* On the server, this file is provided by the `uns-datahub-controller`.
|
|
4
|
-
* In the development environment, you are responsible for creating and maintaining this file and its contents.
|
|
5
|
-
*/
|
|
6
|
-
import readline from "readline";
|
|
7
|
-
import { ConfigFile, getLogger } from "@uns-kit/core";
|
|
8
|
-
import UnsMqttProxy from "@uns-kit/core/uns-mqtt/uns-mqtt-proxy.js";
|
|
9
|
-
|
|
10
|
-
const logger = getLogger(import.meta.url);
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Produces a smooth oscillating value to mimic a real-world sensor signal.
|
|
14
|
-
* Combines fast and slow sine waves plus tiny ripple so that subsequent values
|
|
15
|
-
* rise and fall without appearing purely random.
|
|
16
|
-
*/
|
|
17
|
-
function simulateSensorValue(step: number): number {
|
|
18
|
-
const baseValue = 42; // arbitrary midpoint for the simulated signal
|
|
19
|
-
const fastCycle = Math.sin(step / 5) * 3;
|
|
20
|
-
const slowCycle = Math.sin(step / 25) * 6;
|
|
21
|
-
const ripple = Math.sin(step / 2 + Math.PI / 4) * 0.5;
|
|
22
|
-
const value = baseValue + fastCycle + slowCycle + ripple;
|
|
23
|
-
|
|
24
|
-
return Number(value.toFixed(2));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* This script initializes an MQTT output proxy for load testing purposes.
|
|
29
|
-
* It sets up a connection to the specified MQTT broker and configures
|
|
30
|
-
* a proxy instance. The load test is designed to evaluate the performance
|
|
31
|
-
* and reliability of the MQTT broker under simulated load conditions.
|
|
32
|
-
*/
|
|
33
|
-
async function main() {
|
|
34
|
-
try {
|
|
35
|
-
const config = await ConfigFile.loadConfig();
|
|
36
|
-
const outputHost = (config.output?.host)!;
|
|
37
|
-
|
|
38
|
-
const mqttOutput = new UnsMqttProxy(
|
|
39
|
-
outputHost,
|
|
40
|
-
"loadTest",
|
|
41
|
-
"templateUnsRttLoadTest",
|
|
42
|
-
{ publishThrottlingDelay: 0 },
|
|
43
|
-
true
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
const rl = readline.createInterface({
|
|
47
|
-
input: process.stdin,
|
|
48
|
-
output: process.stdout,
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
52
|
-
|
|
53
|
-
rl.question(`Would you like to continue with load-test on ${outputHost}? (Y/n) `, async (answer) => {
|
|
54
|
-
if (answer.toLowerCase() === "y" || answer.trim() === "") {
|
|
55
|
-
rl.question("How many iterations should be run? (default is 100) ", async (iterations) => {
|
|
56
|
-
const maxIntervals = parseInt(iterations) || 100;
|
|
57
|
-
|
|
58
|
-
rl.question("What should be the delay between intervals in milliseconds? (default is 0 ms) ", async (intervalDelay) => {
|
|
59
|
-
const delay = parseInt(intervalDelay) || 0;
|
|
60
|
-
|
|
61
|
-
logger.info(`Starting load test with ${maxIntervals} messages and ${delay} ms delay...`);
|
|
62
|
-
|
|
63
|
-
let count = 0;
|
|
64
|
-
const startTime = Date.now();
|
|
65
|
-
|
|
66
|
-
while (count < maxIntervals) {
|
|
67
|
-
try {
|
|
68
|
-
const currentDate = new Date();
|
|
69
|
-
const sensorValue = simulateSensorValue(count);
|
|
70
|
-
const rawData = `${count},${currentDate.getTime()},${sensorValue}`;
|
|
71
|
-
await mqttOutput.publishMessage("raw/data", rawData);
|
|
72
|
-
} catch (error) {
|
|
73
|
-
const reason = error instanceof Error ? error : new Error(String(error));
|
|
74
|
-
logger.error("Error publishing message:", reason.message);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
count++;
|
|
78
|
-
if (delay > 0) {
|
|
79
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
logger.info(`Sleeping for 50ms.`);
|
|
84
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
85
|
-
|
|
86
|
-
const endTime = Date.now();
|
|
87
|
-
const duration = (endTime - startTime) / 1000;
|
|
88
|
-
const messagesPerSecond = maxIntervals / duration;
|
|
89
|
-
|
|
90
|
-
logger.info(`Load test completed in ${duration.toFixed(2)} seconds.`);
|
|
91
|
-
logger.info(`Message rate: ${messagesPerSecond.toFixed(2)} msg/s.`);
|
|
92
|
-
|
|
93
|
-
rl.close();
|
|
94
|
-
process.exit(0);
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
} else {
|
|
98
|
-
logger.info("Load test aborted.");
|
|
99
|
-
rl.close();
|
|
100
|
-
process.exit(0);
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
} catch (error) {
|
|
104
|
-
const reason = error instanceof Error ? error : new Error(String(error));
|
|
105
|
-
logger.error("Error initializing load test:", reason.message);
|
|
106
|
-
process.exit(1);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
main();
|
|
1
|
+
/**
|
|
2
|
+
* Load the configuration from a file.
|
|
3
|
+
* On the server, this file is provided by the `uns-datahub-controller`.
|
|
4
|
+
* In the development environment, you are responsible for creating and maintaining this file and its contents.
|
|
5
|
+
*/
|
|
6
|
+
import readline from "readline";
|
|
7
|
+
import { ConfigFile, getLogger } from "@uns-kit/core";
|
|
8
|
+
import UnsMqttProxy from "@uns-kit/core/uns-mqtt/uns-mqtt-proxy.js";
|
|
9
|
+
|
|
10
|
+
const logger = getLogger(import.meta.url);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Produces a smooth oscillating value to mimic a real-world sensor signal.
|
|
14
|
+
* Combines fast and slow sine waves plus tiny ripple so that subsequent values
|
|
15
|
+
* rise and fall without appearing purely random.
|
|
16
|
+
*/
|
|
17
|
+
function simulateSensorValue(step: number): number {
|
|
18
|
+
const baseValue = 42; // arbitrary midpoint for the simulated signal
|
|
19
|
+
const fastCycle = Math.sin(step / 5) * 3;
|
|
20
|
+
const slowCycle = Math.sin(step / 25) * 6;
|
|
21
|
+
const ripple = Math.sin(step / 2 + Math.PI / 4) * 0.5;
|
|
22
|
+
const value = baseValue + fastCycle + slowCycle + ripple;
|
|
23
|
+
|
|
24
|
+
return Number(value.toFixed(2));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* This script initializes an MQTT output proxy for load testing purposes.
|
|
29
|
+
* It sets up a connection to the specified MQTT broker and configures
|
|
30
|
+
* a proxy instance. The load test is designed to evaluate the performance
|
|
31
|
+
* and reliability of the MQTT broker under simulated load conditions.
|
|
32
|
+
*/
|
|
33
|
+
async function main() {
|
|
34
|
+
try {
|
|
35
|
+
const config = await ConfigFile.loadConfig();
|
|
36
|
+
const outputHost = (config.output?.host)!;
|
|
37
|
+
|
|
38
|
+
const mqttOutput = new UnsMqttProxy(
|
|
39
|
+
outputHost,
|
|
40
|
+
"loadTest",
|
|
41
|
+
"templateUnsRttLoadTest",
|
|
42
|
+
{ publishThrottlingDelay: 0 },
|
|
43
|
+
true
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const rl = readline.createInterface({
|
|
47
|
+
input: process.stdin,
|
|
48
|
+
output: process.stdout,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
52
|
+
|
|
53
|
+
rl.question(`Would you like to continue with load-test on ${outputHost}? (Y/n) `, async (answer) => {
|
|
54
|
+
if (answer.toLowerCase() === "y" || answer.trim() === "") {
|
|
55
|
+
rl.question("How many iterations should be run? (default is 100) ", async (iterations) => {
|
|
56
|
+
const maxIntervals = parseInt(iterations) || 100;
|
|
57
|
+
|
|
58
|
+
rl.question("What should be the delay between intervals in milliseconds? (default is 0 ms) ", async (intervalDelay) => {
|
|
59
|
+
const delay = parseInt(intervalDelay) || 0;
|
|
60
|
+
|
|
61
|
+
logger.info(`Starting load test with ${maxIntervals} messages and ${delay} ms delay...`);
|
|
62
|
+
|
|
63
|
+
let count = 0;
|
|
64
|
+
const startTime = Date.now();
|
|
65
|
+
|
|
66
|
+
while (count < maxIntervals) {
|
|
67
|
+
try {
|
|
68
|
+
const currentDate = new Date();
|
|
69
|
+
const sensorValue = simulateSensorValue(count);
|
|
70
|
+
const rawData = `${count},${currentDate.getTime()},${sensorValue}`;
|
|
71
|
+
await mqttOutput.publishMessage("raw/data", rawData);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
const reason = error instanceof Error ? error : new Error(String(error));
|
|
74
|
+
logger.error("Error publishing message:", reason.message);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
count++;
|
|
78
|
+
if (delay > 0) {
|
|
79
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
logger.info(`Sleeping for 50ms.`);
|
|
84
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
85
|
+
|
|
86
|
+
const endTime = Date.now();
|
|
87
|
+
const duration = (endTime - startTime) / 1000;
|
|
88
|
+
const messagesPerSecond = maxIntervals / duration;
|
|
89
|
+
|
|
90
|
+
logger.info(`Load test completed in ${duration.toFixed(2)} seconds.`);
|
|
91
|
+
logger.info(`Message rate: ${messagesPerSecond.toFixed(2)} msg/s.`);
|
|
92
|
+
|
|
93
|
+
rl.close();
|
|
94
|
+
process.exit(0);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
} else {
|
|
98
|
+
logger.info("Load test aborted.");
|
|
99
|
+
rl.close();
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
} catch (error) {
|
|
104
|
+
const reason = error instanceof Error ? error : new Error(String(error));
|
|
105
|
+
logger.error("Error initializing load test:", reason.message);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
main();
|