@zucker-framework/things 1.0.0
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 +201 -0
- package/NOTICE +22 -0
- package/THIRD_PARTY_NOTICES +4 -0
- package/dist/index.d.mts +560 -0
- package/dist/index.d.ts +560 -0
- package/dist/index.js +451 -0
- package/dist/index.mjs +415 -0
- package/package.json +39 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// src/data-type.ts
|
|
5
|
+
function convertValue(type, raw) {
|
|
6
|
+
if (raw === null || raw === void 0) return raw;
|
|
7
|
+
switch (type.id) {
|
|
8
|
+
case "int":
|
|
9
|
+
case "long": {
|
|
10
|
+
const n = Number(raw);
|
|
11
|
+
return isNaN(n) ? raw : Math.trunc(n);
|
|
12
|
+
}
|
|
13
|
+
case "float":
|
|
14
|
+
case "double": {
|
|
15
|
+
const n = Number(raw);
|
|
16
|
+
if (isNaN(n)) return raw;
|
|
17
|
+
if (type.scale !== void 0) {
|
|
18
|
+
return parseFloat(n.toFixed(type.scale));
|
|
19
|
+
}
|
|
20
|
+
return n;
|
|
21
|
+
}
|
|
22
|
+
case "boolean": {
|
|
23
|
+
if (typeof raw === "boolean") return raw;
|
|
24
|
+
const tv = type.trueValue ?? "true";
|
|
25
|
+
const fv = type.falseValue ?? "false";
|
|
26
|
+
if (String(raw) === tv) return true;
|
|
27
|
+
if (String(raw) === fv) return false;
|
|
28
|
+
return Boolean(raw);
|
|
29
|
+
}
|
|
30
|
+
case "string":
|
|
31
|
+
case "password":
|
|
32
|
+
return String(raw);
|
|
33
|
+
case "date": {
|
|
34
|
+
if (raw instanceof Date) return raw.getTime();
|
|
35
|
+
const n = Number(raw);
|
|
36
|
+
return isNaN(n) ? new Date(String(raw)).getTime() : n;
|
|
37
|
+
}
|
|
38
|
+
case "enum": {
|
|
39
|
+
const match = type.elements.find((e) => e.value === raw || String(e.value) === String(raw));
|
|
40
|
+
return match ? match.value : raw;
|
|
41
|
+
}
|
|
42
|
+
default:
|
|
43
|
+
return raw;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
__name(convertValue, "convertValue");
|
|
47
|
+
|
|
48
|
+
// src/metadata.ts
|
|
49
|
+
var PropertyAccessMode = /* @__PURE__ */ (function(PropertyAccessMode2) {
|
|
50
|
+
PropertyAccessMode2["READ"] = "r";
|
|
51
|
+
PropertyAccessMode2["WRITE"] = "w";
|
|
52
|
+
PropertyAccessMode2["READ_WRITE"] = "rw";
|
|
53
|
+
return PropertyAccessMode2;
|
|
54
|
+
})({});
|
|
55
|
+
var EventLevel = /* @__PURE__ */ (function(EventLevel2) {
|
|
56
|
+
EventLevel2["OTHER"] = "other";
|
|
57
|
+
EventLevel2["WARN"] = "warn";
|
|
58
|
+
EventLevel2["ERROR"] = "error";
|
|
59
|
+
return EventLevel2;
|
|
60
|
+
})({});
|
|
61
|
+
function mergeMetadata(base, patch) {
|
|
62
|
+
const mergeList = /* @__PURE__ */ __name((a, b = []) => {
|
|
63
|
+
const map = new Map(a.map((i) => [
|
|
64
|
+
i.id,
|
|
65
|
+
i
|
|
66
|
+
]));
|
|
67
|
+
for (const item of b) {
|
|
68
|
+
map.set(item.id, item);
|
|
69
|
+
}
|
|
70
|
+
return Array.from(map.values());
|
|
71
|
+
}, "mergeList");
|
|
72
|
+
return {
|
|
73
|
+
properties: mergeList(base.properties, patch.properties),
|
|
74
|
+
functions: mergeList(base.functions, patch.functions),
|
|
75
|
+
events: mergeList(base.events, patch.events),
|
|
76
|
+
tags: mergeList(base.tags ?? [], patch.tags)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
__name(mergeMetadata, "mergeMetadata");
|
|
80
|
+
function createMetadata(partial = {}) {
|
|
81
|
+
return {
|
|
82
|
+
properties: partial.properties ?? [],
|
|
83
|
+
functions: partial.functions ?? [],
|
|
84
|
+
events: partial.events ?? [],
|
|
85
|
+
tags: partial.tags ?? []
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
__name(createMetadata, "createMetadata");
|
|
89
|
+
|
|
90
|
+
// src/thing.ts
|
|
91
|
+
var ThingState = /* @__PURE__ */ (function(ThingState2) {
|
|
92
|
+
ThingState2["INACTIVE"] = "notActive";
|
|
93
|
+
ThingState2["OFFLINE"] = "offline";
|
|
94
|
+
ThingState2["ONLINE"] = "online";
|
|
95
|
+
return ThingState2;
|
|
96
|
+
})({});
|
|
97
|
+
|
|
98
|
+
// src/thing-manager.ts
|
|
99
|
+
import { Injectable } from "@nestjs/common";
|
|
100
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
101
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
102
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
103
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
104
|
+
} else {
|
|
105
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
106
|
+
if (d = decorators[i]) {
|
|
107
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
112
|
+
}
|
|
113
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
114
|
+
var InMemoryThingManager = class {
|
|
115
|
+
static {
|
|
116
|
+
__name(this, "InMemoryThingManager");
|
|
117
|
+
}
|
|
118
|
+
types = /* @__PURE__ */ new Map();
|
|
119
|
+
things = /* @__PURE__ */ new Map();
|
|
120
|
+
async getThingType(typeId) {
|
|
121
|
+
return this.types.get(typeId);
|
|
122
|
+
}
|
|
123
|
+
async registerThingType(type) {
|
|
124
|
+
this.types.set(type.id, type);
|
|
125
|
+
}
|
|
126
|
+
async getThing(thingId) {
|
|
127
|
+
return this.things.get(thingId);
|
|
128
|
+
}
|
|
129
|
+
async registerThing(thing) {
|
|
130
|
+
const existing = this.things.get(thing.id);
|
|
131
|
+
this.things.set(thing.id, {
|
|
132
|
+
...existing,
|
|
133
|
+
...thing,
|
|
134
|
+
state: existing?.state ?? ThingState.INACTIVE
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
async unregisterThing(thingId) {
|
|
138
|
+
this.things.delete(thingId);
|
|
139
|
+
}
|
|
140
|
+
async updateState(thingId, state, timestamp) {
|
|
141
|
+
const thing = this.things.get(thingId);
|
|
142
|
+
if (!thing) return;
|
|
143
|
+
const update = {
|
|
144
|
+
state
|
|
145
|
+
};
|
|
146
|
+
if (state === ThingState.ONLINE) {
|
|
147
|
+
update.onlineTime = timestamp ?? Date.now();
|
|
148
|
+
} else if (state === ThingState.OFFLINE) {
|
|
149
|
+
update.offlineTime = timestamp ?? Date.now();
|
|
150
|
+
}
|
|
151
|
+
this.things.set(thingId, {
|
|
152
|
+
...thing,
|
|
153
|
+
...update
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
async getEffectiveMetadata(thingId) {
|
|
157
|
+
const thing = this.things.get(thingId);
|
|
158
|
+
if (!thing) return void 0;
|
|
159
|
+
const type = this.types.get(thing.thingTypeId);
|
|
160
|
+
if (!type) return void 0;
|
|
161
|
+
if (!thing.metadataPatch) {
|
|
162
|
+
return type.metadata;
|
|
163
|
+
}
|
|
164
|
+
return mergeMetadata(type.metadata, thing.metadataPatch);
|
|
165
|
+
}
|
|
166
|
+
/** 获取指定类型的所有实例 */
|
|
167
|
+
getThingsByType(typeId) {
|
|
168
|
+
return Array.from(this.things.values()).filter((t) => t.thingTypeId === typeId);
|
|
169
|
+
}
|
|
170
|
+
/** 获取所有在线实例数量 */
|
|
171
|
+
countOnline() {
|
|
172
|
+
return Array.from(this.things.values()).filter((t) => t.state === ThingState.ONLINE).length;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
InMemoryThingManager = _ts_decorate([
|
|
176
|
+
Injectable()
|
|
177
|
+
], InMemoryThingManager);
|
|
178
|
+
|
|
179
|
+
// src/message.ts
|
|
180
|
+
function createMessageId() {
|
|
181
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
182
|
+
}
|
|
183
|
+
__name(createMessageId, "createMessageId");
|
|
184
|
+
function createReportPropertyMessage(thingId, thingType, properties, extra) {
|
|
185
|
+
return {
|
|
186
|
+
messageType: "ReportProperty",
|
|
187
|
+
messageId: createMessageId(),
|
|
188
|
+
thingId,
|
|
189
|
+
thingType,
|
|
190
|
+
timestamp: Date.now(),
|
|
191
|
+
properties,
|
|
192
|
+
...extra
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
__name(createReportPropertyMessage, "createReportPropertyMessage");
|
|
196
|
+
function createEventMessage(thingId, thingType, event, data) {
|
|
197
|
+
return {
|
|
198
|
+
messageType: "Event",
|
|
199
|
+
messageId: createMessageId(),
|
|
200
|
+
thingId,
|
|
201
|
+
thingType,
|
|
202
|
+
timestamp: Date.now(),
|
|
203
|
+
event,
|
|
204
|
+
data
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
__name(createEventMessage, "createEventMessage");
|
|
208
|
+
|
|
209
|
+
// src/property-log.ts
|
|
210
|
+
import { Injectable as Injectable2 } from "@nestjs/common";
|
|
211
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
212
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
213
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
214
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
215
|
+
} else {
|
|
216
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
217
|
+
if (d = decorators[i]) {
|
|
218
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
223
|
+
}
|
|
224
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
225
|
+
function _ts_metadata(metadataKey, metadataValue) {
|
|
226
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
227
|
+
return Reflect.metadata(metadataKey, metadataValue);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
231
|
+
var DefaultThingsDataService = class {
|
|
232
|
+
static {
|
|
233
|
+
__name(this, "DefaultThingsDataService");
|
|
234
|
+
}
|
|
235
|
+
tsService;
|
|
236
|
+
constructor(tsService) {
|
|
237
|
+
this.tsService = tsService;
|
|
238
|
+
}
|
|
239
|
+
propertyMetric(thingType) {
|
|
240
|
+
return `thing_property_${thingType}`;
|
|
241
|
+
}
|
|
242
|
+
eventMetric(thingType, eventId) {
|
|
243
|
+
return `thing_event_${thingType}_${eventId}`;
|
|
244
|
+
}
|
|
245
|
+
async saveProperties(message) {
|
|
246
|
+
const metric = this.propertyMetric(message.thingType);
|
|
247
|
+
const points = Object.entries(message.properties).map(([key, value]) => ({
|
|
248
|
+
timestamp: message.propertySourceTimes?.[key] ?? message.timestamp,
|
|
249
|
+
tags: {
|
|
250
|
+
thingId: message.thingId,
|
|
251
|
+
thingType: message.thingType,
|
|
252
|
+
property: key
|
|
253
|
+
},
|
|
254
|
+
values: {
|
|
255
|
+
value: typeof value === "object" ? JSON.stringify(value) : value,
|
|
256
|
+
state: message.propertyStates?.[key] ?? "success"
|
|
257
|
+
}
|
|
258
|
+
}));
|
|
259
|
+
for (const point of points) {
|
|
260
|
+
const propMetric = `thing_property_${message.thingType}_${point.tags["property"]}`;
|
|
261
|
+
await this.tsService.save({
|
|
262
|
+
...point,
|
|
263
|
+
tags: {
|
|
264
|
+
thingId: point.tags["thingId"],
|
|
265
|
+
thingType: point.tags["thingType"]
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
void propMetric;
|
|
269
|
+
}
|
|
270
|
+
if (points.length > 0) {
|
|
271
|
+
await this.tsService.saveBatch(points.map((p) => ({
|
|
272
|
+
...p,
|
|
273
|
+
tags: {
|
|
274
|
+
...p.tags
|
|
275
|
+
},
|
|
276
|
+
values: {
|
|
277
|
+
...p.values,
|
|
278
|
+
metric
|
|
279
|
+
}
|
|
280
|
+
})));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
async saveEvent(message) {
|
|
284
|
+
const metric = this.eventMetric(message.thingType, message.event);
|
|
285
|
+
const data = {
|
|
286
|
+
timestamp: message.timestamp,
|
|
287
|
+
tags: {
|
|
288
|
+
thingId: message.thingId,
|
|
289
|
+
thingType: message.thingType,
|
|
290
|
+
event: message.event
|
|
291
|
+
},
|
|
292
|
+
values: {
|
|
293
|
+
data: typeof message.data === "object" ? JSON.stringify(message.data) : message.data,
|
|
294
|
+
metric
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
await this.tsService.save(data);
|
|
298
|
+
}
|
|
299
|
+
async queryPropertyLogs(query) {
|
|
300
|
+
const tsQuery = {
|
|
301
|
+
metric: this.propertyMetric(query.thingType),
|
|
302
|
+
from: query.from,
|
|
303
|
+
to: query.to,
|
|
304
|
+
tags: {
|
|
305
|
+
thingId: query.thingId,
|
|
306
|
+
property: query.id
|
|
307
|
+
},
|
|
308
|
+
limit: query.limit,
|
|
309
|
+
order: query.order
|
|
310
|
+
};
|
|
311
|
+
const results = await this.tsService.query(tsQuery);
|
|
312
|
+
return results.map((r) => ({
|
|
313
|
+
property: String(r.tags["property"] ?? query.id),
|
|
314
|
+
value: r.values["value"],
|
|
315
|
+
thingId: String(r.tags["thingId"]),
|
|
316
|
+
thingType: String(r.tags["thingType"] ?? query.thingType),
|
|
317
|
+
timestamp: r.timestamp,
|
|
318
|
+
state: r.values["state"]
|
|
319
|
+
}));
|
|
320
|
+
}
|
|
321
|
+
async queryEventLogs(query) {
|
|
322
|
+
const tsQuery = {
|
|
323
|
+
metric: this.eventMetric(query.thingType, query.id),
|
|
324
|
+
from: query.from,
|
|
325
|
+
to: query.to,
|
|
326
|
+
tags: {
|
|
327
|
+
thingId: query.thingId
|
|
328
|
+
},
|
|
329
|
+
limit: query.limit,
|
|
330
|
+
order: query.order
|
|
331
|
+
};
|
|
332
|
+
const results = await this.tsService.query(tsQuery);
|
|
333
|
+
return results.map((r) => {
|
|
334
|
+
let data = r.values["data"];
|
|
335
|
+
if (typeof data === "string") {
|
|
336
|
+
try {
|
|
337
|
+
data = JSON.parse(data);
|
|
338
|
+
} catch {
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
event: String(r.tags["event"] ?? query.id),
|
|
343
|
+
data,
|
|
344
|
+
thingId: String(r.tags["thingId"]),
|
|
345
|
+
thingType: String(r.tags["thingType"] ?? query.thingType),
|
|
346
|
+
timestamp: r.timestamp
|
|
347
|
+
};
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
async aggregateProperties(thingId, thingType, property, aggQuery) {
|
|
351
|
+
const query = {
|
|
352
|
+
...aggQuery,
|
|
353
|
+
metric: this.propertyMetric(thingType),
|
|
354
|
+
tags: {
|
|
355
|
+
...aggQuery.tags,
|
|
356
|
+
thingId,
|
|
357
|
+
property
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
return this.tsService.aggregate(query);
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
DefaultThingsDataService = _ts_decorate2([
|
|
364
|
+
Injectable2(),
|
|
365
|
+
_ts_metadata("design:type", Function),
|
|
366
|
+
_ts_metadata("design:paramtypes", [
|
|
367
|
+
typeof TimeSeriesService === "undefined" ? Object : TimeSeriesService
|
|
368
|
+
])
|
|
369
|
+
], DefaultThingsDataService);
|
|
370
|
+
|
|
371
|
+
// src/things.module.ts
|
|
372
|
+
import { Module } from "@nestjs/common";
|
|
373
|
+
function _ts_decorate3(decorators, target, key, desc) {
|
|
374
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
375
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
376
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
377
|
+
} else {
|
|
378
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
379
|
+
if (d = decorators[i]) {
|
|
380
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
385
|
+
}
|
|
386
|
+
__name(_ts_decorate3, "_ts_decorate");
|
|
387
|
+
var ThingsModule = class {
|
|
388
|
+
static {
|
|
389
|
+
__name(this, "ThingsModule");
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
ThingsModule = _ts_decorate3([
|
|
393
|
+
Module({
|
|
394
|
+
providers: [
|
|
395
|
+
InMemoryThingManager
|
|
396
|
+
],
|
|
397
|
+
exports: [
|
|
398
|
+
InMemoryThingManager
|
|
399
|
+
]
|
|
400
|
+
})
|
|
401
|
+
], ThingsModule);
|
|
402
|
+
export {
|
|
403
|
+
DefaultThingsDataService,
|
|
404
|
+
EventLevel,
|
|
405
|
+
InMemoryThingManager,
|
|
406
|
+
PropertyAccessMode,
|
|
407
|
+
ThingState,
|
|
408
|
+
ThingsModule,
|
|
409
|
+
convertValue,
|
|
410
|
+
createEventMessage,
|
|
411
|
+
createMessageId,
|
|
412
|
+
createMetadata,
|
|
413
|
+
createReportPropertyMessage,
|
|
414
|
+
mergeMetadata
|
|
415
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zucker-framework/things",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"module": "dist/index.mjs",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"LICENSE",
|
|
10
|
+
"NOTICE",
|
|
11
|
+
"THIRD_PARTY_NOTICES"
|
|
12
|
+
],
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@zucker-framework/core": "1.0.0",
|
|
15
|
+
"@zucker-framework/event": "1.0.0",
|
|
16
|
+
"@zucker-framework/timeseries": "1.0.0"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@nestjs/common": "^12.0.0",
|
|
20
|
+
"@nestjs/core": "^12.0.0",
|
|
21
|
+
"rxjs": "^7.0.0"
|
|
22
|
+
},
|
|
23
|
+
"license": "Apache-2.0",
|
|
24
|
+
"author": "Lind",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/Lind-pro/zucker-framework.git",
|
|
28
|
+
"directory": "packages/things"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public",
|
|
32
|
+
"registry": "https://registry.npmjs.org/"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
36
|
+
"clean": "rm -rf dist",
|
|
37
|
+
"lint": "eslint src/"
|
|
38
|
+
}
|
|
39
|
+
}
|