@tier0/node-red-contrib-opcda-client 1.0.4 → 1.0.6

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.
@@ -1,460 +0,0 @@
1
- networks:
2
- edge_network:
3
- driver: bridge
4
-
5
- services:
6
- frontend:
7
- image: tier0/tier0-frontend:1.0.1-R8
8
- container_name: frontend
9
- ports:
10
- - "3010:3000"
11
- - "4000:4000"
12
- environment:
13
- - LLM_MODEL=${LLM_MODEL}
14
- - LLM_API_KEY=${LLM_API_KEY}
15
- - LLM_TYPE=${LLM_TYPE}
16
- - REACT_APP_OS_LANG=${LANGUAGE}
17
- - TOKEN_MAX_AGE=${TOKEN_MAX_AGE}
18
- - TZ=UTC
19
- command: >
20
- sh -c "
21
- if [ -z \"$LLM_API_KEY\" ]; then
22
- echo 'LLM_API_KEY为空,跳过Node.js服务启动'
23
- concurrently \"serve -s /app/web-dist -l 3000\"
24
- else
25
- echo 'LLM_API_KEY已配置,启动所有服务'
26
- concurrently \"serve -s /app/web-dist -l 3000\" \"node /app/services-express-dist/index.js\"
27
- fi
28
- "
29
- volumes:
30
- - /etc/docker/certs:/certs
31
- networks:
32
- - edge_network
33
- restart: always
34
- uns:
35
- image: tier0/tier0-backend:1.0.1-R8
36
- container_name: uns
37
- environment:
38
- - LLM_MODEL=${LLM_MODEL}
39
- - LLM_API_KEY=${LLM_API_KEY}
40
- - LLM_TYPE=${LLM_TYPE}
41
- - REACT_APP_OS_LANG=${LANGUAGE}
42
- - OAUTH_REDIRECT_URI=${BASE_URL}/inter-api/supos/auth/token
43
- - OAUTH_SUPOS_HOME=${BASE_URL}/uns
44
- - OAUTH_REALM=${OAUTH_REALM}
45
- - OAUTH_CLIENT_NAME=${OAUTH_CLIENT_NAME}
46
- - OAUTH_CLIENT_ID=${OAUTH_CLIENT_ID}
47
- - OAUTH_CLIENT_SECRET=${OAUTH_CLIENT_SECRET}
48
- - OAUTH_GRANT_TYPE=${OAUTH_GRANT_TYPE}
49
- - OAUTH_ISSUER_URI=${OAUTH_ISSUER_URI}
50
- - OAUTH_REFRESH_TOKEN_TIME=${OAUTH_REFRESH_TOKEN_TIME}
51
- - SYS_OS_APP_TITLE=${OS_NAME}
52
- - SYS_OS_LOGIN_PATH=${OS_LOGIN_PATH}
53
- - SYS_OS_LANG=${LANGUAGE}
54
- - TOKEN_MAX_AGE=${TOKEN_MAX_AGE}
55
- - TZ=UTC
56
- - GRPC_POOL_CORE_SIZE=2
57
- - GRPC_POOL_MAX_SIZE=4
58
- - dbDSN=${UNS_DB_URL}
59
- - SINK_PG_URL=${SINK_PG_URL}
60
- - SINK_TSDB_URL=${SINK_TSDB_URL}
61
- - NODE_RED_HOST=nodered
62
- - NODE_RED_PORT=1880
63
- - SYS_OS_MULTIPLE_TOPIC=false
64
- - SYS_OS_VERSION=${OS_VERSION}
65
- - SYS_OS_AUTH_ENABLE=${OS_AUTH_ENABLE}
66
- - SYS_OS_LLM_TYPE=${OS_LLM_TYPE}
67
- - SYS_OS_MQTT_TCP_PORT=${OS_MQTT_TCP_PORT}
68
- - SYS_OS_MQTT_WEBSOCKET_TSL_PORT=${OS_MQTT_WEBSOCKET_TSL_PORT}
69
- - SYS_OS_PLATFORM_TYPE=${OS_PLATFORM_TYPE}
70
- - SYS_OS_ENTRANCE_URL=${BASE_URL}
71
- - SYS_OS_QUALITY_NAME=quality
72
- - SYS_OS_TIMESTAMP_NAME=timeStamp
73
- - SYS_OS_LAZY_TREE=${LAZY_TREE}
74
- - UNS_ADD_BATCH_SIZE=1000
75
- volumes:
76
- - ${VOLUMES_PATH}/edge/system/:/app/go-edge/system/
77
- - ${VOLUMES_PATH}/edge/attachment/:/app/go-edge/attachment/
78
- - ${VOLUMES_PATH}/edge/apps/:/app/data/apps/
79
- - /etc/docker/certs:/certs
80
- ports:
81
- - "18998:8080"
82
- networks:
83
- - edge_network
84
- depends_on:
85
- postgresql:
86
- condition: service_healthy
87
- restart: always
88
- extra_hosts:
89
- - "host.docker.internal:host-gateway"
90
- marimo:
91
- image: suposce/marimo:latest
92
- container_name: marimo
93
- environment:
94
- - TSDB_URL=postgresql+psycopg2://postgres:${TSDB_PASSWORD}@tsdb:5432/postgres
95
- - PG_URL=postgresql+psycopg2://postgres:${POSTGRES_PASSWORD}@postgresql:5432/postgres
96
- volumes:
97
- - ${VOLUMES_PATH}/marimo/data:/app
98
- # command: marimo edit /app/main.py --host 0.0.0.0 --port 8080 --no-token
99
- restart: always
100
- deploy:
101
- resources:
102
- limits:
103
- memory: 2g
104
- reservations:
105
- memory: 512M
106
- networks:
107
- - edge_network
108
- emqx:
109
- image: emqx/emqx:5.8
110
- container_name: emqx
111
- ports:
112
- - "1883:1883" # MQTT端口
113
- - "8883:8883" # MQTT加密端口
114
- - "8083:8083" # WebSocket端口
115
- - "8084:8084" # WebSocket加密端口
116
- - "18083:18083" # EMQX Dashboard端口
117
- environment:
118
- - EMQX_NAME=emqx
119
- - EMQX_NODE__COOKIE=secretcookie # 节点通信时的cookie
120
- - service_logo=emqx-original.svg
121
- - service_description=aboutus.emqxDescription
122
- - service_redirect_url=/emqx/home/
123
- - service_account=admin
124
- - service_password=public
125
- volumes:
126
- - /etc/localtime:/etc/localtime:ro
127
- - ${VOLUMES_PATH}/emqx/data:/opt/emqx/data
128
- - ${VOLUMES_PATH}/emqx/log:/opt/emqx/log
129
- - ${VOLUMES_PATH}/emqx/config/emqx.conf:/opt/emqx/etc/emqx.conf
130
- - ${VOLUMES_PATH}/emqx/config/default_api_key.conf:/opt/emqx/etc/default_api_key.conf
131
- - ${VOLUMES_PATH}/emqx/config/acl.conf:/opt/emqx/etc/acl.conf
132
- restart: always
133
- networks:
134
- - edge_network
135
- nodered:
136
- image: nodered/node-red:4.0.8-22
137
- container_name: nodered
138
- user: root
139
- ports:
140
- - "1880:1880" # Node-RED web UI端口
141
- environment:
142
- - service_logo=nodered-original.svg
143
- - service_description=aboutus.nodeRedDescription
144
- - FLOWS=/data/flows.json
145
- - USE_ALIAS_AS_TOPIC=false
146
- - TIMESTAMP_NAME=timeStamp
147
- - QUALITY_NAME=quality
148
- - TZ=UTC
149
- - OS_LANG=${LANGUAGE}
150
- - NODE_OPTIONS=--openssl-legacy-provider
151
- - NODE_HTTP_API_PREFIX=/nodered-api
152
- volumes:
153
- - /etc/localtime:/etc/localtime:ro
154
- - ${VOLUMES_PATH}/node-red:/data # 使用当前目录的 data 目录
155
- depends_on:
156
- - emqx
157
- restart: always
158
- networks:
159
- - edge_network
160
- eventflow:
161
- image: nodered/node-red:4.0.8-22
162
- container_name: eventflow
163
- user: root
164
- ports:
165
- - "1889:1889" # Node-RED web UI端口
166
- environment:
167
- - service_logo=nodered-original.svg
168
- - service_description=aboutus.nodeRedDescription
169
- - FLOWS=/data/flows.json
170
- - USE_ALIAS_AS_TOPIC=false
171
- - QUALITY_NAME=status
172
- - TIMESTAMP_NAME=timeStamp
173
- - TZ=UTC
174
- - OS_LANG=${LANGUAGE}
175
- - NODE_OPTIONS=--openssl-legacy-provider
176
- - NODE_HTTP_API_PREFIX=/eventflow-api
177
- volumes:
178
- - /etc/localtime:/etc/localtime:ro
179
- - ${VOLUMES_PATH}/eventflow:/data # 使用当前目录的 data 目录
180
- restart: always
181
- networks:
182
- - edge_network
183
- grafana:
184
- image: grafana/grafana:11.5.6
185
- profiles:
186
- - grafana
187
- container_name: grafana
188
- user: root
189
- ports:
190
- - "3000:3000" # Grafana web UI端口
191
- volumes:
192
- - ${VOLUMES_PATH}/grafana/data:/var/lib/grafana
193
- # - ${VOLUMES_PATH}/grafana/data/plugins:/var/lib/grafana/plugins # 使用当前目录的 data 目录
194
- environment:
195
- service_logo: grafana-original.svg
196
- service_description: aboutus.grafanaDescription
197
- service_redirect_url: /grafana/home/dashboards/
198
- # 设置管理员用户的初始密码
199
- GF_SECURITY_ADMIN_PASSWORD: "supos"
200
- # 开启 Grafana 的 Explore 功能
201
- GF_EXPLORE_ENABLED: "true"
202
- # 安装 Grafana 插件
203
- # GF_INSTALL_PLUGINS: "grafana-clock-panel,grafana-mqtt-datasource,tdengine-datasource,yesoreyeram-infinity-datasource"
204
- # 注释掉的设置,用于改变 Grafana 用户界面的语言
205
- GF_VIEWER_LANGUAGE: "${GRAFANA_LANG:-en-US}"
206
- GF_AUTH_ANONYMOUS_ENABLED: "true"
207
- GF_AUTH_ANONYMOUS_ORG_ROLE: "Admin"
208
- GF_SECURITY_ALLOW_EMBEDDING: "true"
209
- GF_SERVER_ROOT_URL: "http://${ENTRANCE_DOMAIN}/grafana/home/"
210
- GF_USERS_DEFAULT_THEME: "light"
211
- GF_DATABASE_TYPE: postgres
212
- GF_DATABASE_HOST: postgresql:5432
213
- GF_DATABASE_NAME: grafana
214
- GF_DATABASE_USER: postgres
215
- GF_DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
216
- restart: always
217
- depends_on:
218
- postgresql:
219
- condition: service_healthy # 依赖 PostgreSQL 的健康状态
220
- networks:
221
- - edge_network
222
- portainer:
223
- image: portainer/portainer-ce:2.23.0
224
- container_name: portainer
225
- environment:
226
- service_logo: portainer.svg
227
- command: --admin-password="$$2y$$05$$ZTAqF7Tn.hil8X.ifVmQTuKiJQoZDiKDW3t1lRR2/VPR06QoHv4AC"
228
- ports:
229
- - "8000:8000"
230
- - "9443:9443"
231
- restart: always
232
- volumes:
233
- - /var/run/docker.sock:/var/run/docker.sock
234
- - ${VOLUMES_PATH}/portainer:/data
235
- networks:
236
- - edge_network
237
- postgresql:
238
- image: timescale/timescaledb:2.20.0-pg17
239
- container_name: postgresql
240
- environment:
241
- TZ: UTC # 设置容器时区
242
- service_logo: postgresql-original.svg
243
- service_description: aboutus.postgresqlDescription
244
- POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
245
- ports:
246
- - "5432:5432"
247
- volumes:
248
- - ${VOLUMES_PATH}/postgresql/conf/postgresql.conf:/etc/postgresql/custom.conf
249
- - /etc/localtime:/etc/localtime:ro
250
- - ${VOLUMES_PATH}/postgresql/pgdata:/var/lib/postgresql/data # 持久化数据
251
- - ${VOLUMES_PATH}/postgresql/init-scripts:/docker-entrypoint-initdb.d # 加载初始化脚本
252
- command:
253
- - postgres
254
- - -c
255
- - config_file=/etc/postgresql/custom.conf
256
- healthcheck:
257
- test: [ "CMD-SHELL", "psql -U postgres -d keycloak -c '\\dt public.initialization_complete' | grep -q 'initialization_complete'" ]
258
- interval: 15s
259
- timeout: 15s
260
- retries: 60
261
- start_period: 120s
262
- start_interval: 10s
263
- restart: always
264
- networks:
265
- - edge_network
266
- keycloak:
267
- image: keycloak/keycloak:26.0 # 使用 Keycloak 的最新镜像
268
- container_name: keycloak
269
- ports:
270
- - "8081:8080"
271
- user: root
272
- deploy:
273
- resources:
274
- limits:
275
- memory: 2G
276
- reservations:
277
- memory: 512M
278
- environment:
279
- service_logo: keycloak-original.svg
280
- JAVA_OPTS: >-
281
- -Xms512m
282
- -Xmx1g
283
- -XX:MaxMetaspaceSize=256m
284
- -XX:MetaspaceSize=256m
285
- -Xss512k
286
- -XX:+UseG1GC
287
- service_description: aboutus.keycloakDescription
288
- service_redirect_url: /keycloak/home/
289
- service_account: admin
290
- service_password: tier0
291
- KC_SSL_REQUIRED: none # 不要求 SSL
292
- KC_PROXY: passthrough # 设置代理模式
293
- KC_HOSTNAME: "${ENTRANCE_DOMAIN}" # 指定主机名
294
- KC_FRONTEND_URL: "${BASE_URL}" # 设置前端 URL
295
- KC_BOOTSTRAP_ADMIN_USERNAME: admin
296
- KC_BOOTSTRAP_ADMIN_PASSWORD: tier0
297
- KC_COOKIE_SECURE: false # 禁用安全 cookie
298
- KC_DB: postgres
299
- KC_DB_URL: "jdbc:postgresql://postgresql:5432/keycloak"
300
- KC_DB_USERNAME: postgres
301
- KC_DB_PASSWORD: ${POSTGRES_PASSWORD}
302
- KC_DB_POOL_INITIAL_SIZE: 2
303
- KC_DB_POOL_MIN_SIZE: 2
304
- KC_DB_POOL_MAX_SIZE: 4
305
- KC_DB_POOL_MAX_LIFETIME: 1800
306
- KC_HEALTH_ENABLED: true
307
- KC_FEATURES: token-exchange
308
- JAVA_OPTS_APPEND: -Dcom.sun.jndi.ldap.object.disableEndpointIdentification=true
309
- volumes:
310
- - /etc/localtime:/etc/localtime:ro
311
- - ${VOLUMES_PATH}/keycloak/data:/opt/keycloak/data # 将 Keycloak 的数据目录挂载到本地
312
- - ${VOLUMES_PATH}/keycloak/theme/keycloak.v2:/opt/keycloak/themes/wenhao
313
- - ${VOLUMES_PATH}/keycloak/ca/ca.crt:/opt/keycloak/ca.crt
314
- - ${VOLUMES_PATH}/keycloak/ca/init-ldaps-cert.sh:/opt/keycloak/init-ldaps-cert.sh
315
- depends_on:
316
- postgresql:
317
- condition: service_healthy # 依赖 PostgreSQL 的健康状态
318
- command: start-dev --hostname ${BASE_URL}/keycloak/home/auth --proxy-headers forwarded
319
- healthcheck:
320
- test: >
321
- /bin/bash -c " exec 3<>/dev/tcp/localhost/8080"
322
- interval: 10s
323
- timeout: 5s
324
- retries: 60
325
- start_period: 120s # 新增:给予2分钟的启动宽限期
326
- start_interval: 10s # 新增:宽限期内的检查间隔
327
- restart: always
328
- networks:
329
- - edge_network
330
- tsdb:
331
- image: timescale/timescaledb:2.20.0-pg17
332
- container_name: tsdb
333
- environment:
334
- TZ: UTC # 设置容器时区
335
- service_logo: postgresql-original.svg
336
- service_description: aboutus.postgresqlDescription
337
- POSTGRES_PASSWORD: ${TSDB_PASSWORD}
338
- ports:
339
- - "2345:5432"
340
- volumes:
341
- - ${VOLUMES_PATH}/tsdb/conf/postgresql.conf:/etc/postgresql/custom.conf
342
- - ${VOLUMES_PATH}/tsdb/data:/var/lib/postgresql/data # 持久化数据
343
- - ${VOLUMES_PATH}/tsdb/init-scripts:/docker-entrypoint-initdb.d # 加载初始化脚本
344
- command:
345
- - postgres
346
- - -c
347
- - config_file=/etc/postgresql/custom.conf
348
- healthcheck:
349
- test: [ "CMD-SHELL", "pg_isready -U postgres" ]
350
- interval: 10s
351
- timeout: 5s
352
- retries: 10
353
- start_period: 30s
354
- start_interval: 60s
355
- restart: always
356
- networks:
357
- - edge_network
358
- kong:
359
- image: kong:3.9.0
360
- container_name: kong
361
- environment:
362
- KONG_WORKER_STATE_UPDATE_FREQUENCY: 30
363
- KONG_DB_CACHE_TTL: 600
364
- KONG_NGINX_WORKER_PROCESSES: 2
365
- KONG_LUA_GC_PAUSE: 100 # 提高暂停率,降低 GC 触发频率
366
- KONG_LUA_GC_STEPMUL: 200 # 增加单次回收强度
367
- service_logo: konga-original.svg
368
- service_description: aboutus.kongaDescription
369
- service_redirect_url: /konga/home/
370
- KONG_DATABASE: postgres
371
- KONG_PG_HOST: postgresql
372
- KONG_PG_PASSWORD: ${POSTGRES_PASSWORD}
373
- KONG_PG_USER: postgres
374
- KONG_ADMIN_LISTEN: 0.0.0.0:8001
375
- KONG_SSL_CERT: /etc/kong/ssl/fullchain.cer
376
- KONG_SSL_CERT_KEY: /etc/kong/ssl/private.key
377
- KONG_PROXY_LISTEN: 0.0.0.0:8000, 0.0.0.0:8443 ssl
378
- KONG_PLUGINS: bundled,supos-auth-checker,supos-url-transformer
379
- KONG_PG_POOL_SIZE: 2
380
- KONG_LOG_LEVEL: error
381
- KONG_NGINX_HTTP_CLIENT_MAX_BODY_SIZE: 2048m
382
- KONG_NGINX_PROXY_CLIENT_MAX_BODY_SIZE: 2048m
383
- KONG_NGINX_HTTP_PROXY_CONNECT_TIMEOUT: 300000
384
- KONG_NGINX_HTTP_PROXY_SEND_TIMEOUT: 300000
385
- KONG_NGINX_HTTP_PROXY_READ_TIMEOUT: 300000
386
- volumes:
387
- - ${VOLUMES_PATH}/kong/certificationfile:/etc/kong/ssl:ro
388
- - ${VOLUMES_PATH}/kong/kong_config.yml:/etc/kong/kong_config.yml
389
- - ${VOLUMES_PATH}/kong/start.sh:/usr/local/bin/start.sh
390
- - ${VOLUMES_PATH}/kong/kong-plugin-auth-checker:/usr/local/share/lua/5.1/kong/plugins/supos-auth-checker
391
- - ${VOLUMES_PATH}/kong/kong-plugin-url-transformer:/usr/local/share/lua/5.1/kong/plugins/supos-url-transformer
392
- ports:
393
- - "${ENTRANCE_PORT:-8088}:8000"
394
- - "${ENTRANCE_SSL_PORT:-8443}:8443"
395
- - "8001:8001"
396
- - "8444:8444"
397
- depends_on:
398
- - emqx
399
- - uns
400
- - keycloak
401
- - postgresql
402
- command: >
403
- sh -c "kong migrations bootstrap &&
404
- kong config db_import /etc/kong/kong_config.yml &&
405
- kong start"
406
- restart: always # 确保 Kong 自动重启
407
- networks:
408
- - edge_network
409
- konga:
410
- image: suposce/konga:1.0.0
411
- container_name: konga
412
- profiles:
413
- - konga
414
- environment:
415
- # DB_ADAPTER: mysql # 使用 MySQL 作为数据库
416
- # DB_HOST: konga_mysql_database # MySQL 容器的主机名
417
- # DB_USER: konga # MySQL 用户
418
- # DB_PASSWORD: konga # MySQL 用户密码
419
- # DB_DATABASE: konga # MySQL 数据库名
420
- # NODE_ENV: production # 设置 NODE_ENV 为生产环境
421
- NO_AUTH: "true" # 禁用认证
422
- KONGA_SEED_KONG_NODE_DATA_SOURCE_FILE: /node.data
423
- ports:
424
- - "1337:1337" # 映射 Konga 的端口到主机
425
- volumes:
426
- - ${VOLUMES_PATH}/konga/db/:/app/kongadata/
427
- - ${VOLUMES_PATH}/konga/node.data:/node.data # 持久化数据库数据
428
- restart: always
429
- networks:
430
- - edge_network
431
- minio:
432
- image: minio/minio:RELEASE.2024-12-18T13-15-44Z
433
- container_name: minio
434
- profiles:
435
- - minio
436
- environment:
437
- - service_logo=minio-original.svg
438
- - service_description=aboutus.minioDescription
439
- - service_redirect_url=/minio/home/
440
- - MINIO_ACCESS_KEY=admin
441
- - MINIO_SECRET_KEY=adminpassword
442
- - MINIO_BROWSER_REDIRECT_URL=${BASE_URL}/minio/home
443
- - MINIO_IDENTITY_OPENID_CONFIG_URL=${OAUTH_ISSUER_URI}/realms/supos/.well-known/openid-configuration
444
- - MINIO_IDENTITY_OPENID_CLIENT_ID=${OAUTH_CLIENT_ID}
445
- - MINIO_IDENTITY_OPENID_CLIENT_SECRET=${OAUTH_CLIENT_SECRET}
446
- - MINIO_IDENTITY_OPENID_SCOPES=openid
447
- - MINIO_IDENTITY_OPENID_ROLE_POLICY=public-delete-policy
448
- - MINIO_IDENTITY_OPENID_REDIRECT_URI=${BASE_URL}/minio/home/oauth_callback
449
- ports:
450
- - "9000:9000" # Web UI 端口
451
- - "9001:9001" # Admin API 端口 (如果需要访问管理接口)
452
- volumes:
453
- - ${VOLUMES_PATH}/minio/data:/data # 数据存储位置
454
- command: server /data --console-address ":9001"
455
- depends_on:
456
- keycloak:
457
- condition: service_healthy
458
- restart: always
459
- networks:
460
- - edge_network
package/opctest.py DELETED
@@ -1,214 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- import OpenOPC
3
- import paho.mqtt.client as mqtt
4
- import json
5
- import time
6
- import signal
7
- import sys
8
- reload(sys)
9
- sys.setdefaultencoding('utf-8')
10
-
11
- OPC_SERVER = 'Kepware.KEPServerEX.V6'
12
- OPC_HOST = '192.168.31.75'
13
-
14
- MQTT_BROKER = '192.168.31.45'
15
- MQTT_PORT = 1883
16
- MQTT_TOPIC_PREFIX = 'opcda'
17
-
18
- POLL_INTERVAL = 1
19
-
20
- TAGS = [
21
- u'通道 1.设备 1.标记 1',
22
- u'TI2022.PV',
23
- u'TI2022.SV',
24
- u'TI2022.MV',
25
- ]
26
-
27
- running = True
28
-
29
- def on_exit(sig, frame):
30
- global running
31
- print("\n[*] Shutting down...")
32
- running = False
33
-
34
- signal.signal(signal.SIGINT, on_exit)
35
- signal.signal(signal.SIGTERM, on_exit)
36
-
37
-
38
- def make_topic(tag):
39
- parts = tag.split('.')
40
- if len(parts) >= 2:
41
- parent = '/'.join(parts[:-1])
42
- leaf = parts[-1]
43
- topic = '%s/%s/Metric/%s' % (MQTT_TOPIC_PREFIX, parent, leaf)
44
- else:
45
- topic = '%s/Metric/%s' % (MQTT_TOPIC_PREFIX, tag)
46
- return topic.replace(' ', '_')
47
-
48
-
49
- UNS_EXPORT_FILE = 'uns_import.json'
50
-
51
- TOPIC_FIELDS = [
52
- {"name": "timeStamp", "type": "DATETIME", "unique": True, "systemField": True},
53
- {"name": "tag", "type": "LONG", "unique": True, "tbValueName": "tag", "systemField": True},
54
- {"name": "value", "type": "FLOAT", "index": "double_1"},
55
- {"name": "quality", "type": "LONG", "systemField": True},
56
- ]
57
-
58
-
59
- def generate_uns_json(tags):
60
- def get_or_create(children, name, node_type="path", extra=None):
61
- for child in children:
62
- if child["name"] == name:
63
- return child
64
- node = {"type": node_type, "name": name}
65
- if extra:
66
- node.update(extra)
67
- if node_type == "path":
68
- node["children"] = []
69
- children.append(node)
70
- return node
71
-
72
- root_children = []
73
- for tag in tags:
74
- parts = tag.replace(' ', '_').split('.')
75
- if len(parts) >= 2:
76
- path_parts = parts[:-1]
77
- leaf = parts[-1]
78
- else:
79
- path_parts = []
80
- leaf = parts[0]
81
-
82
- current = root_children
83
- prefix_node = get_or_create(current, MQTT_TOPIC_PREFIX)
84
- current = prefix_node["children"]
85
-
86
- for p in path_parts:
87
- node = get_or_create(current, p)
88
- current = node["children"]
89
-
90
- metric_node = get_or_create(current, "Metric", extra={
91
- "displayName": "Metric",
92
- "dataType": "METRIC",
93
- })
94
- current = metric_node["children"]
95
-
96
- topic_node = {
97
- "type": "topic",
98
- "name": leaf,
99
- "fields": TOPIC_FIELDS,
100
- "dataType": "TIME_SEQUENCE_TYPE",
101
- "generateDashboard": "TRUE",
102
- "enableHistory": "TRUE",
103
- "mockData": "FALSE",
104
- "writeData": "FALSE",
105
- "topicType": "METRIC",
106
- }
107
- exists = False
108
- for child in current:
109
- if child["name"] == leaf:
110
- exists = True
111
- break
112
- if not exists:
113
- current.append(topic_node)
114
-
115
- result = {"UNS": root_children}
116
- with open(UNS_EXPORT_FILE, 'w') as f:
117
- json.dump(result, f, indent=2, ensure_ascii=False)
118
- print("[+] UNS import JSON written to %s" % UNS_EXPORT_FILE)
119
-
120
-
121
- def run_bridge():
122
- global running
123
-
124
- print("[*] OPC DA -> MQTT Bridge")
125
- print(" OPC Server: %s @ %s" % (OPC_SERVER, OPC_HOST))
126
- print(" MQTT Broker: %s:%d" % (MQTT_BROKER, MQTT_PORT))
127
- print(" Tags: %d" % len(TAGS))
128
- print("")
129
-
130
- opc = None
131
- mqtt_client = None
132
-
133
- try:
134
- mqtt_client = mqtt.Client()
135
- mqtt_client.connect(MQTT_BROKER, MQTT_PORT)
136
- mqtt_client.loop_start()
137
- print("[+] MQTT connected")
138
-
139
- opc = OpenOPC.client()
140
- opc.connect(OPC_SERVER, OPC_HOST)
141
- print("[+] OPC DA connected")
142
-
143
- generate_uns_json(TAGS)
144
-
145
- print("[*] Publishing data every %ds (Ctrl+C to stop)..." % POLL_INTERVAL)
146
- print("-" * 50)
147
-
148
- while running:
149
- try:
150
- results = opc.read(TAGS)
151
- except Exception as e:
152
- print("[!] OPC read error: %s, reconnecting..." % str(e))
153
- try:
154
- opc.close()
155
- except:
156
- pass
157
- opc = OpenOPC.client()
158
- opc.connect(OPC_SERVER, OPC_HOST)
159
- continue
160
-
161
- for tag_name, value, quality, timestamp in results:
162
- topic = make_topic(tag_name)
163
-
164
- if isinstance(quality, str):
165
- quality_code = 0 if quality == 'Good' else 1
166
- else:
167
- quality_code = 0 if quality is not None and int(quality) >= 192 else 1
168
-
169
- if value is None:
170
- out_value = 0.0
171
- elif isinstance(value, (int, float)):
172
- out_value = float(value)
173
- else:
174
- try:
175
- out_value = float(value)
176
- except (ValueError, TypeError):
177
- out_value = str(value)
178
-
179
- payload = json.dumps({
180
- 'value': out_value,
181
- 'quality': quality_code,
182
- 'timestamp': str(timestamp),
183
- }, ensure_ascii=False)
184
- mqtt_client.publish(topic, payload, retain=True)
185
-
186
- ts = time.strftime('%H:%M:%S')
187
- print("[%s] Published %d tags" % (ts, len(results)))
188
-
189
- time.sleep(POLL_INTERVAL)
190
-
191
- except Exception as e:
192
- print("\n[!] Fatal error: %s" % str(e))
193
- print(" 1. Is MQTT broker running? ")
194
- print(" 2. Is OPC server reachable?")
195
- print(" 3. Run as Administrator if DCOM access is needed.")
196
- sys.exit(1)
197
-
198
- finally:
199
- if opc:
200
- try:
201
- opc.close()
202
- except:
203
- pass
204
- print("[-] OPC DA disconnected")
205
- if mqtt_client:
206
- mqtt_client.loop_stop()
207
- mqtt_client.disconnect()
208
- print("[-] MQTT disconnected")
209
-
210
- print("[*] Bridge stopped.")
211
-
212
-
213
- if __name__ == "__main__":
214
- run_bridge()
package/patch-debug.js DELETED
@@ -1,13 +0,0 @@
1
- const fs = require('fs');
2
- const file = '/usr/src/node-red/node_modules/node-dcom/dcom/rpc/security/ntlmauthentication.js';
3
- let code = fs.readFileSync(file, 'utf8');
4
- code = code.replace(
5
- 'let target = null;',
6
- 'let target = null;\n console.log("[NTLM-DBG] domain=" + info.domain + " user=" + this.credentials.username);'
7
- );
8
- code = code.replace(
9
- "if (target == '') {",
10
- "console.log('[NTLM-DBG] target=' + target);\n if (target == '') {"
11
- );
12
- fs.writeFileSync(file, code);
13
- console.log('Patched.');