@meta2d/core 1.0.70 → 1.0.72

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/src/core.d.ts CHANGED
@@ -3,7 +3,7 @@ import { Canvas } from './canvas';
3
3
  import { Options, PenPlugin, PluginOptions } from './options';
4
4
  import { calcTextDrawRect, calcTextLines, calcTextRect, facePen, getWords, LockState, Pen, renderPenRaw, IValue, setElemPosition } from './pen';
5
5
  import { Point } from './point';
6
- import { EditAction, register, registerAnchors, registerCanvasDraw, Meta2dData, Meta2dStore, Network, HttpOptions } from './store';
6
+ import { EditAction, register, registerAnchors, registerCanvasDraw, Meta2dData, Meta2dStore, Network, HttpOptions, Sql } from './store';
7
7
  import { Padding } from './utils';
8
8
  import { Rect } from './rect';
9
9
  import { Event, TriggerCondition } from './event';
@@ -17,6 +17,7 @@ export declare class Meta2d {
17
17
  mqttClient: MqttClient;
18
18
  websockets: WebSocket[];
19
19
  mqttClients: MqttClient[];
20
+ eventSources: EventSource[];
20
21
  penPluginMap: Map<PenPlugin, {
21
22
  tag?: string;
22
23
  name?: string;
@@ -235,9 +236,17 @@ export declare class Meta2d {
235
236
  updateTimerList: any[];
236
237
  sqlTimerList: any[];
237
238
  connectNetwork(): void;
239
+ iotMqttClient: MqttClient;
240
+ iotTimer: any;
241
+ iotWebsocketClient: WebSocket;
242
+ connectIot(): Promise<void>;
243
+ connectSqls(): void;
244
+ connectSSE(net: Network): void;
245
+ closeSSE(): void;
238
246
  connectNetWebSocket(net: Network): void;
239
- getIotToken(devices: any): Promise<any>;
240
- doSqlCode(type: string, dbid: string, sql: string): Promise<void>;
247
+ getMqttUrl(): Promise<string>;
248
+ getIotToken(devices: any, type: number): Promise<any>;
249
+ doSqlCode(sql: Sql): Promise<void>;
241
250
  randomString(e: number): string;
242
251
  mockValue(data: any): any;
243
252
  dataMock(): void;
package/src/core.js CHANGED
@@ -13,9 +13,10 @@ import pkg from '../package.json';
13
13
  import { lockedError } from './utils/error';
14
14
  import { Scroll } from './scroll';
15
15
  import { getter } from './utils/object';
16
- import { getCookie, getMeta2dData, queryURLParams } from './utils/url';
16
+ import { getCookie, getMeta2dData, getToken, queryURLParams } from './utils/url';
17
17
  import { HotkeyType } from './data';
18
18
  import { Message, messageList } from './message';
19
+ import { closeJetLinks, connectJetLinks, getSendData, sendJetLinksData } from './utils/jetLinks';
19
20
  import { le5leTheme } from './theme';
20
21
  export class Meta2d {
21
22
  store;
@@ -24,6 +25,7 @@ export class Meta2d {
24
25
  mqttClient;
25
26
  websockets;
26
27
  mqttClients;
28
+ eventSources;
27
29
  penPluginMap = new Map();
28
30
  socketFn;
29
31
  events = {};
@@ -133,6 +135,7 @@ export class Meta2d {
133
135
  this.canvas.scroll && this.canvas.scroll.hide();
134
136
  }
135
137
  }
138
+ this.canvas?.initGlobalStyle();
136
139
  }
137
140
  getOptions() {
138
141
  return this.store.options;
@@ -150,6 +153,11 @@ export class Meta2d {
150
153
  // 更新全局的主题css变量
151
154
  le5leTheme.updateCssRule(this.store.id, theme);
152
155
  this.canvas.initGlobalStyle();
156
+ for (let i = 0; i < this.store.data.pens.length; i++) {
157
+ const pen = this.store.data.pens[i];
158
+ // 调用pen的主题设置函数,如果单个pen有主题的自定义设置的话
159
+ pen.setTheme && pen.setTheme(pen, this.store.styles);
160
+ }
153
161
  this.render();
154
162
  }
155
163
  setDatabyOptions(options = {}) {
@@ -368,6 +376,16 @@ export class Meta2d {
368
376
  });
369
377
  }
370
378
  }
379
+ Object.keys(e.extend).forEach((key) => {
380
+ if (!['x', 'y', 'width', 'height'].includes(key)) {
381
+ if (url.indexOf('?') !== -1) {
382
+ url += `&${key}=${e.extend[key]}`;
383
+ }
384
+ else {
385
+ url += `?${key}=${e.extend[key]}`;
386
+ }
387
+ }
388
+ });
371
389
  const data = this.getEventData(e.list, pen);
372
390
  this.canvas.dialog.show(e.value, url, e.extend, data);
373
391
  }
@@ -375,51 +393,10 @@ export class Meta2d {
375
393
  this.events[EventAction.SendData] = (pen, e) => {
376
394
  if (e.list?.length) {
377
395
  // if (e.targetType === 'id') {
378
- if (e.network && e.network.protocol === 'jetLinks') {
379
- const list = [];
380
- e.list.forEach((item, index) => {
381
- const _pen = item.params ? this.findOne(item.params) : pen;
382
- list[index] = {
383
- deviceId: _pen.deviceId,
384
- productId: _pen.productId,
385
- properties: {},
386
- };
387
- for (let key in item.value) {
388
- if (item.value[key] === undefined || item.value[key] === '') {
389
- //找到绑定了这个设备属性的图元属性
390
- const realTime = _pen.realTimes?.find((item) => item.propertyId === key);
391
- if (realTime) {
392
- list[index].properties[key] = _pen[realTime.key];
393
- }
394
- }
395
- else if (typeof item.value[key] === 'string' &&
396
- item.value[key]?.indexOf('${') > -1) {
397
- let keys = item.value[key].match(/\$\{([^}]+)\}/g)?.map(m => m.slice(2, -1));
398
- if (keys?.length) {
399
- list[index].properties[key] =
400
- _pen[keys[0]] ?? this.getDynamicParam(keys[0]);
401
- }
402
- }
403
- else {
404
- list[index].properties[key] = item.value[key];
405
- }
406
- }
407
- });
408
- if (this.jetLinksClient && list.length) {
409
- list.forEach((item) => {
410
- this.jetLinksClient.send(JSON.stringify({
411
- type: 'sub',
412
- topic: `/device-message-sender/${item.productId}/${item.deviceId}`,
413
- parameter: {
414
- messageType: 'WRITE_PROPERTY',
415
- properties: item.properties,
416
- headers: {
417
- async: false,
418
- },
419
- },
420
- id: item.productId + '/' + item.deviceId + '-' + s8(),
421
- }));
422
- });
396
+ if (e.network && e.network.protocol === 'ADIIOT') {
397
+ const list = getSendData(this, pen, e);
398
+ if (list.length) {
399
+ sendJetLinksData(this, list);
423
400
  }
424
401
  return;
425
402
  }
@@ -558,6 +535,7 @@ export class Meta2d {
558
535
  const data = await getMeta2dData(this.store, id);
559
536
  if (data) {
560
537
  this.open(data);
538
+ this.fitView(true, 10);
561
539
  }
562
540
  }
563
541
  doSendDataEvent(value, topics) {
@@ -949,9 +927,47 @@ export class Meta2d {
949
927
  initBinds() {
950
928
  this.jetLinksList = [];
951
929
  this.store.bind = {};
930
+ const devices = [];
931
+ const properties = [];
952
932
  this.store.data.pens.forEach((pen) => {
953
933
  pen.realTimes?.forEach((realTime) => {
954
934
  if (realTime.bind && realTime.bind.id) {
935
+ // if (!this.store.bind[realTime.bind.id]) {
936
+ // this.store.bind[realTime.bind.id] = [];
937
+ // }
938
+ // this.store.bind[realTime.bind.id].push({
939
+ // id: pen.id,
940
+ // key: realTime.key,
941
+ // });
942
+ //JetLinks
943
+ let productId = realTime.productId || pen.productId;
944
+ let deviceId = realTime.deviceId || pen.deviceId;
945
+ let propertyId = realTime.propertyId;
946
+ let flag = false;
947
+ if (productId && productId.indexOf('${') > -1) {
948
+ let keys = productId.match(/(?<=\$\{).*?(?=\})/g);
949
+ if (keys?.length) {
950
+ productId = this.getDynamicParam(keys[0]) || productId;
951
+ }
952
+ flag = true;
953
+ }
954
+ if (deviceId && deviceId.indexOf('${') > -1) {
955
+ let keys = deviceId.match(/(?<=\$\{).*?(?=\})/g);
956
+ if (keys?.length) {
957
+ deviceId = this.getDynamicParam(keys[0]) || deviceId;
958
+ }
959
+ flag = true;
960
+ }
961
+ if (propertyId && propertyId.indexOf('${') > -1) {
962
+ let keys = propertyId.match(/(?<=\$\{).*?(?=\})/g);
963
+ if (keys?.length) {
964
+ propertyId = this.getDynamicParam(keys[0]) || propertyId;
965
+ }
966
+ flag = true;
967
+ }
968
+ if (flag) {
969
+ realTime.bind && (realTime.bind.id = productId + '#' + deviceId + '#' + propertyId);
970
+ }
955
971
  if (!this.store.bind[realTime.bind.id]) {
956
972
  this.store.bind[realTime.bind.id] = [];
957
973
  }
@@ -959,10 +975,6 @@ export class Meta2d {
959
975
  id: pen.id,
960
976
  key: realTime.key,
961
977
  });
962
- //JetLinks
963
- const productId = realTime.productId || pen.productId;
964
- const deviceId = realTime.deviceId || pen.deviceId;
965
- const propertyId = realTime.propertyId;
966
978
  if (productId && deviceId && propertyId) {
967
979
  const index = this.jetLinksList.findIndex((item) => item.topic.startsWith(`/${productId}/${deviceId}`));
968
980
  if (index > -1) {
@@ -979,9 +991,55 @@ export class Meta2d {
979
991
  });
980
992
  }
981
993
  }
994
+ if (realTime.bind.class === 'iot') {
995
+ let bind = realTime.bind.id.split('#');
996
+ let idx = devices.findIndex((item) => item.deviceId === bind[0]);
997
+ if (idx > -1) {
998
+ if (!devices[idx].properties.includes(bind[1])) {
999
+ devices[idx].properties.push(bind[1]);
1000
+ }
1001
+ }
1002
+ else {
1003
+ devices.push({
1004
+ deviceId: bind[0],
1005
+ properties: [bind[1]],
1006
+ token: realTime.bind.token
1007
+ });
1008
+ }
1009
+ let index = properties.findIndex((item) => item.key === realTime.bind.id);
1010
+ if (index === -1) {
1011
+ properties.push({
1012
+ key: realTime.bind.id,
1013
+ label: realTime.bind.label,
1014
+ });
1015
+ }
1016
+ }
1017
+ else if (realTime.bind.class === 'sql') {
1018
+ let bind = realTime.bind.id.split('#');
1019
+ const sql = this.store.data.sqls.find((item) => item.bindId === bind[0]);
1020
+ if (sql) {
1021
+ if (!sql.keys) {
1022
+ sql.keys = [];
1023
+ }
1024
+ bind.shift();
1025
+ const key = bind.join('#');
1026
+ if (!sql.keys.includes(key)) {
1027
+ sql.keys.push(key);
1028
+ }
1029
+ }
1030
+ }
982
1031
  }
983
1032
  });
984
1033
  });
1034
+ if (devices.length) {
1035
+ if (!this.store.data.iot) {
1036
+ this.store.data.iot = {};
1037
+ }
1038
+ this.store.data.iot.devices = devices;
1039
+ }
1040
+ if (properties.length) {
1041
+ this.store.data.iot.list = properties;
1042
+ }
985
1043
  }
986
1044
  connectSocket() {
987
1045
  this.connectWebsocket();
@@ -1929,8 +1987,10 @@ export class Meta2d {
1929
1987
  let mqttIndex = 0;
1930
1988
  this.mqttClients = [];
1931
1989
  let websocketIndex = 0;
1990
+ let sseIndex = 0;
1932
1991
  let sqlIndex = 0;
1933
1992
  this.websockets = [];
1993
+ this.eventSources = [];
1934
1994
  networks.forEach(async (net) => {
1935
1995
  // if (net.type === 'subscribe') {
1936
1996
  if (net.protocol === 'mqtt') {
@@ -1999,93 +2059,101 @@ export class Meta2d {
1999
2059
  body: net.body,
2000
2060
  });
2001
2061
  }
2002
- else if (net.protocol === 'iot') {
2003
- const token = await this.getIotToken(net.devices);
2004
- //物联网设备
2005
- if (net.method === 'mqtt') {
2006
- net.index = mqttIndex;
2007
- this.mqttClients[mqttIndex] = mqtt.connect(net.url);
2008
- this.mqttClients[mqttIndex].on('message', (topic, message) => {
2009
- this.socketCallback(message.toString(), {
2010
- topic: `le5le-iot/properties/${token}`,
2011
- type: 'iot',
2012
- url: net.url,
2013
- method: 'mqtt',
2014
- });
2015
- });
2016
- this.mqttClients[mqttIndex].on('error', (error) => {
2017
- this.store.emitter.emit('error', { type: 'mqtt', error });
2018
- });
2019
- this.mqttClients[mqttIndex].subscribe(`le5le-iot/properties/${token}`);
2020
- mqttIndex += 1;
2021
- }
2022
- else if (net.method === 'websocket') {
2023
- net.index = websocketIndex;
2024
- this.websockets[websocketIndex] = new WebSocket(`${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}/api/ws/iot/properties`, token);
2025
- this.websockets[websocketIndex].onmessage = (e) => {
2026
- this.socketCallback(e.data, { type: 'iot', method: 'websocket' });
2027
- };
2028
- this.websockets[websocketIndex].onerror = (error) => {
2029
- this.store.emitter.emit('error', { type: 'websocket', error });
2030
- };
2031
- websocketIndex += 1;
2032
- }
2062
+ else if (net.protocol === 'ADIIOT') {
2063
+ connectJetLinks(this, net);
2033
2064
  }
2034
- else if (net.protocol === 'sql') {
2035
- await this.doSqlCode('list', net.dbId, net.sql);
2036
- if (net.interval) {
2037
- net.index = sqlIndex;
2038
- this.sqlTimerList[sqlIndex] = setInterval(async () => {
2039
- await this.doSqlCode('list', net.dbId, net.sql);
2040
- }, net.interval);
2041
- sqlIndex += 1;
2042
- }
2043
- }
2044
- else if (net.protocol === 'jetLinks') {
2045
- if (this.jetLinksList.length) {
2046
- this.jetLinksClient = new WebSocket(`${net.url}/${localStorage.getItem('X-Access-Token') ||
2047
- getCookie('X-Access-Token') ||
2048
- new URLSearchParams(location.search).get('X-Access-Token') ||
2049
- ''}`
2050
- // 'ws://8.134.86.52:29000/api/messaging/961d8b395298d3ec3a021df70d6b6ca4'
2051
- );
2052
- //消息接收
2053
- this.jetLinksClient.onmessage = (e) => {
2054
- const mess = JSON.parse(e.data);
2055
- if (mess.payload &&
2056
- mess.payload.success &&
2057
- mess.payload?.properties) {
2058
- const data = [];
2059
- for (let key in mess.payload.properties) {
2060
- if (!key.startsWith('_')) {
2061
- data.push({
2062
- id: `${mess.payload.headers.productId}#${mess.payload.deviceId}#${key}`,
2063
- value: mess.payload.properties[key],
2064
- });
2065
- }
2066
- }
2067
- this.setDatas(data, { history: false });
2068
- }
2069
- };
2070
- this.jetLinksClient.onopen = () => {
2071
- this.jetLinksList.forEach((item) => {
2072
- this.jetLinksClient.send(JSON.stringify({
2073
- type: 'sub',
2074
- topic: `/device${item.topic}/message/property/report`,
2075
- parameter: {
2076
- deviceId: item.deviceId,
2077
- properties: item.properties,
2078
- history: 1,
2079
- },
2080
- id: item.topic + '-' + s8(),
2081
- }));
2082
- });
2083
- };
2084
- }
2065
+ else if (net.protocol === 'SSE') {
2066
+ net.index = sseIndex;
2067
+ this.connectSSE(net);
2068
+ sseIndex += 1;
2085
2069
  }
2086
2070
  });
2087
2071
  }
2088
2072
  this.onNetworkConnect(https);
2073
+ this.connectIot();
2074
+ this.connectSqls();
2075
+ }
2076
+ iotMqttClient;
2077
+ iotTimer;
2078
+ iotWebsocketClient;
2079
+ async connectIot() {
2080
+ const { iot } = this.store.data;
2081
+ if (!(iot && iot?.devices?.length)) {
2082
+ return;
2083
+ }
2084
+ const url = globalThis.iotUrl || await this.getMqttUrl();
2085
+ if (!url) {
2086
+ console.warn('iot Request address error');
2087
+ return;
2088
+ }
2089
+ const token = await this.getIotToken(iot.devices, iot.protocol === 'websocket' ? 1 : undefined);
2090
+ //物联网设备
2091
+ // if(iot.protocol === 'mqtt'){
2092
+ // const url ='ws://192.168.110.148:8083/mqtt'; //`${location.protocol === 'https:'?'wss':'ws'}://${iot.host}:${location.protocol === 'https:'?'8084':'8083'}/mqtt`
2093
+ this.iotMqttClient = mqtt.connect(url);
2094
+ this.iotMqttClient.on('message', (topic, message) => {
2095
+ this.socketCallback(message.toString(), {
2096
+ topic: `le5le-iot/properties/${token}`,
2097
+ type: 'iot',
2098
+ url,
2099
+ method: 'mqtt'
2100
+ });
2101
+ });
2102
+ this.iotMqttClient.on('error', (error) => {
2103
+ this.store.emitter.emit('error', { type: 'mqtt', error });
2104
+ });
2105
+ this.iotMqttClient.subscribe(`le5le-iot/properties/${token}`);
2106
+ this.iotTimer = setInterval(() => {
2107
+ this.iotMqttClient && this.iotMqttClient.publish(`le5le-iot/subscribe/ping`, token);
2108
+ }, 300000);
2109
+ // }else if(iot.protocol === 'websocket'){
2110
+ // const url = 'ws://192.168.110.6/api/ws/iot/properties'// `${location.protocol === 'https:'?'wss':'ws'}://${location.host}/api/ws/iot/properties`
2111
+ // this.iotWebsocketClient = new WebSocket(
2112
+ // url,
2113
+ // token
2114
+ // );
2115
+ // this.iotWebsocketClient.onmessage = (e) => {
2116
+ // this.socketCallback(e.data, { type: 'iot', method: 'websocket' });
2117
+ // };
2118
+ // this.iotWebsocketClient.onerror = (error) => {
2119
+ // this.store.emitter.emit('error', { type: 'websocket', error });
2120
+ // };
2121
+ // }
2122
+ }
2123
+ // type SqlType = 'list' | 'get' | 'exec' | 'add' | 'update' | 'delete';
2124
+ connectSqls() {
2125
+ const { sqls } = this.store.data;
2126
+ if (sqls && sqls.length) {
2127
+ let sqlIndex = 0;
2128
+ sqls.forEach(async (sql) => {
2129
+ await this.doSqlCode(sql);
2130
+ if (sql.interval) {
2131
+ sql.index = sqlIndex;
2132
+ this.sqlTimerList[sqlIndex] = setInterval(async () => {
2133
+ await this.doSqlCode(sql);
2134
+ }, sql.interval);
2135
+ sqlIndex += 1;
2136
+ }
2137
+ });
2138
+ }
2139
+ }
2140
+ connectSSE(net) {
2141
+ this.eventSources[net.index] = new EventSource(net.url, { withCredentials: net.withCredentials });
2142
+ this.eventSources[net.index].onmessage = (e) => {
2143
+ this.socketCallback(e.data, { type: 'SSE', url: net.url });
2144
+ };
2145
+ this.eventSources[net.index].onerror = (error) => {
2146
+ this.store.emitter.emit('error', { type: 'SSE', error });
2147
+ };
2148
+ }
2149
+ closeSSE() {
2150
+ this.eventSources &&
2151
+ this.eventSources.forEach((es) => {
2152
+ if (es) {
2153
+ es.close();
2154
+ es = undefined;
2155
+ }
2156
+ });
2089
2157
  }
2090
2158
  connectNetWebSocket(net) {
2091
2159
  if (this.websockets[net.index]) {
@@ -2117,28 +2185,59 @@ export class Meta2d {
2117
2185
  }, 2000);
2118
2186
  };
2119
2187
  }
2120
- async getIotToken(devices) {
2188
+ async getMqttUrl() {
2189
+ const res = await fetch('/api/iot/app/mqtt', {
2190
+ method: 'GET',
2191
+ headers: {
2192
+ Authorization: `Bearer ${getToken()}`,
2193
+ },
2194
+ });
2195
+ if (res.ok) {
2196
+ const data = await res.text();
2197
+ let results = JSON.parse(data);
2198
+ let port = results.wssPort || results.wsPort;
2199
+ if (!port) {
2200
+ return;
2201
+ }
2202
+ return `${location.protocol === 'https:' ? 'wss' : 'ws'}://${results.host}:${location.protocol === 'https:' ? results.wssPort : results.wsPort}${results.path}`;
2203
+ }
2204
+ }
2205
+ async getIotToken(devices, type) {
2121
2206
  const res = await fetch('/api/iot/subscribe/properties', {
2122
2207
  method: 'POST',
2123
- body: JSON.stringify({ devices: devices }),
2208
+ headers: {
2209
+ Authorization: `Bearer ${getToken()}`,
2210
+ },
2211
+ body: JSON.stringify({ devices: devices, type }),
2124
2212
  });
2125
2213
  if (res.ok) {
2126
2214
  const data = await res.text();
2127
2215
  return JSON.parse(data).token;
2128
2216
  }
2129
2217
  }
2130
- async doSqlCode(type, dbid, sql) {
2131
- const res = await fetch(`/api/iot/data/sql/${type}`, {
2218
+ async doSqlCode(sql) {
2219
+ const method = sql.method || 'get';
2220
+ let _sql = sql.sql;
2221
+ if (method === 'list') {
2222
+ _sql += ` LIMIT ${sql.pageSize || 20}` + (sql.current > 1 ? (' OFFSET ' + (sql.current - 1) * sql.pageSize) : '');
2223
+ }
2224
+ const res = await fetch(`/api/iot/data/sql/${method}`, {
2132
2225
  method: 'POST',
2133
- body: JSON.stringify({ dbid, sql }),
2226
+ headers: {
2227
+ Authorization: `Bearer ${getCookie('token') || localStorage.getItem('token') || new URLSearchParams(location.search).get('token') || ''}`,
2228
+ },
2229
+ body: JSON.stringify({ dbid: sql.dbid, sql: _sql, }),
2134
2230
  });
2135
2231
  if (res.ok) {
2136
- const data = await res.text();
2232
+ let data = await res.text();
2137
2233
  if (data) {
2138
- this.socketCallback(data, {
2139
- type: 'sql',
2140
- url: `/api/iot/data/sql/${type}`,
2234
+ const arr = [];
2235
+ data = JSON.parse(data);
2236
+ sql.keys?.forEach((key) => {
2237
+ arr.push({ id: sql.bindId + '#' + key, value: getter(data, key.split('#').join('.')) });
2141
2238
  });
2239
+ arr.push({ id: sql.bindId, value: data });
2240
+ this.socketCallback(JSON.stringify(arr), { type: 'sql', url: `/api/iot/data/sql/${method}`, method });
2142
2241
  }
2143
2242
  }
2144
2243
  }
@@ -2473,6 +2572,19 @@ export class Meta2d {
2473
2572
  clearInterval(_sqlTimer);
2474
2573
  _sqlTimer = undefined;
2475
2574
  });
2575
+ if (this.iotMqttClient) {
2576
+ this.iotMqttClient.end();
2577
+ this.iotMqttClient = undefined;
2578
+ }
2579
+ clearInterval(this.iotTimer);
2580
+ this.iotTimer = undefined;
2581
+ // if(this.iotWebsocketClient){
2582
+ // this.iotWebsocketClient.onclose = undefined;
2583
+ // this.iotWebsocketClient.close();
2584
+ // this.iotWebsocketClient = undefined;
2585
+ // }
2586
+ closeJetLinks(this);
2587
+ this.closeSSE();
2476
2588
  }
2477
2589
  socketCallback(message, context) {
2478
2590
  this.store.emitter.emit('socket', { message, context });