Initial commit
This commit is contained in:
219
.gitignore
vendored
Normal file
219
.gitignore
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[codz]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
# Pipfile.lock
|
||||
|
||||
# UV
|
||||
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# uv.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
# poetry.lock
|
||||
# poetry.toml
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
||||
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
||||
# pdm.lock
|
||||
# pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# pixi
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
||||
# pixi.lock
|
||||
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
||||
# in the .venv directory. It is recommended not to include this directory in version control.
|
||||
.pixi
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# Redis
|
||||
*.rdb
|
||||
*.aof
|
||||
*.pid
|
||||
|
||||
# RabbitMQ
|
||||
mnesia/
|
||||
rabbitmq/
|
||||
rabbitmq-data/
|
||||
|
||||
# ActiveMQ
|
||||
activemq-data/
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.envrc
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
# .idea/
|
||||
|
||||
# Abstra
|
||||
# Abstra is an AI-powered process automation framework.
|
||||
# Ignore directories containing user credentials, local state, and settings.
|
||||
# Learn more at https://abstra.io/docs
|
||||
.abstra/
|
||||
|
||||
# Visual Studio Code
|
||||
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
||||
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
||||
# you could uncomment the following to ignore the entire vscode folder
|
||||
.vscode/
|
||||
|
||||
# Ruff stuff:
|
||||
.ruff_cache/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
|
||||
# Marimo
|
||||
marimo/_static/
|
||||
marimo/_lsp/
|
||||
__marimo__/
|
||||
|
||||
# Streamlit
|
||||
.streamlit/secrets.toml
|
||||
|
||||
static/*
|
||||
certificates/*
|
||||
28
globalConfigs.py
Normal file
28
globalConfigs.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from pathlib import Path
|
||||
import socket
|
||||
|
||||
# Certificate settings
|
||||
CERT_BASE = Path(__file__).parent
|
||||
CERT = Path(CERT_BASE / f"certificates/peer-certificate.der")
|
||||
PRIVATE_KEY = Path(CERT_BASE / f"certificates/peer-private-key.pem")
|
||||
HOST_NAME = socket.gethostname()
|
||||
CLIENT_APP_URI = f"urn:{HOST_NAME}:client:hmi"
|
||||
|
||||
# OPC UA settings
|
||||
OPC_UA_URL = "opc.tcp://192.168.42.1:4840"
|
||||
OPC_UA_USER = "hmi"
|
||||
OPC_UA_PW = "hmi"
|
||||
#OPC_UA_URL = "opc.tcp://192.168.56.101:4840"
|
||||
#OPC_UA_USER = "twincat"
|
||||
#OPC_UA_PW = "twincat"
|
||||
NAMESPACE = "urn:BeckhoffAutomation:Ua:PLC1"
|
||||
NS_IDX = 0
|
||||
|
||||
CHECK_INTERVAL = 1.0 # seconds
|
||||
RECONNECT_INTERVAL = 5.0 # seconds
|
||||
|
||||
# Publishing interval to the clients (rate limiting)
|
||||
PUBLISH_INTERVAL = 0.1 # 200 ms
|
||||
|
||||
# Path to the nodes list
|
||||
NODE_IDS_FILE_PATH = Path(CERT_BASE / f"nodeList.txt")
|
||||
12
globalData.py
Normal file
12
globalData.py
Normal file
@@ -0,0 +1,12 @@
|
||||
import asyncio
|
||||
from asyncua import Client
|
||||
from asyncua.common.subscription import Subscription
|
||||
|
||||
opc_ua_client: Client
|
||||
clients = {} # {WebSocket: set(subscribed_tags)}
|
||||
node_storage = {}
|
||||
event_storage = {}
|
||||
queue = asyncio.Queue(maxsize=10000)
|
||||
event_queue = asyncio.Queue(maxsize=1000)
|
||||
subscription_handler: Subscription
|
||||
subscribed_nodes = set()
|
||||
115
main.py
Normal file
115
main.py
Normal file
@@ -0,0 +1,115 @@
|
||||
from fastapi import FastAPI #, HTTPException
|
||||
from routers import websocket
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
#from starlette import status
|
||||
#from fastapi.staticfiles import StaticFiles
|
||||
#from models import SemiAutoControl, SemiAutoBatteryStatus
|
||||
|
||||
import asyncio
|
||||
import msgpack
|
||||
|
||||
#from globalData import node_storage, clients
|
||||
import globalData as gd
|
||||
import globalConfigs as gc
|
||||
from opcuaHandler import initOPCUAConnection, connection_watcher
|
||||
|
||||
update_buffer = {}
|
||||
|
||||
async def data_consumer(queue):
|
||||
"""Asynchronous consumer that stores the latest values."""
|
||||
while True:
|
||||
nodeid, val = await queue.get()
|
||||
gd.node_storage[nodeid] = val
|
||||
update_buffer[nodeid] = val
|
||||
queue.task_done()
|
||||
|
||||
async def event_consumer(queue):
|
||||
while True:
|
||||
node_id, event_data = await queue.get()
|
||||
print("Got new event", event_data)
|
||||
if node_id in gd.event_storage:
|
||||
event_data.Message = gd.event_storage[node_id].Message
|
||||
gd.event_storage[node_id].update(event_data)
|
||||
else:
|
||||
gd.event_storage[node_id] = event_data
|
||||
|
||||
print(gd.event_storage)
|
||||
|
||||
for ws in list(gd.clients):
|
||||
print("Sending event:", {"e": {node_id: event_data}})
|
||||
await ws.send_bytes(msgpack.packb({"e": {node_id: event_data}}))
|
||||
queue.task_done()
|
||||
|
||||
async def broadcast_deltas(changes: dict):
|
||||
payload = msgpack.packb({"u": changes})
|
||||
for ws, subscribed_tags in list(gd.clients.items()):
|
||||
relevant = {tag: val for tag, val in changes.items() if tag in subscribed_tags}
|
||||
if relevant:
|
||||
await ws.send_bytes(msgpack.packb({"u": relevant}))
|
||||
|
||||
async def broadcast_loop():
|
||||
global update_buffer
|
||||
while True:
|
||||
await asyncio.sleep(gc.PUBLISH_INTERVAL)
|
||||
|
||||
if not update_buffer:
|
||||
continue
|
||||
|
||||
changes = dict(update_buffer)
|
||||
update_buffer.clear()
|
||||
|
||||
for ws, subscribed_tags in list(gd.clients.items()):
|
||||
relevant = {tag: val for tag, val in changes.items() if tag in subscribed_tags}
|
||||
if relevant:
|
||||
await ws.send_bytes(msgpack.packb({"u": relevant}))
|
||||
|
||||
|
||||
async def lifespan(app: FastAPI):
|
||||
"""
|
||||
FastAPI lifespan context — creates the client and starts the watcher.loading="warning"
|
||||
"""
|
||||
await initOPCUAConnection()
|
||||
|
||||
# Launch the background watcher task for the opc ua connection
|
||||
watcher_task = asyncio.create_task(connection_watcher())
|
||||
|
||||
# Lunch data consumer task for node value changed events
|
||||
consumer_task = asyncio.create_task(data_consumer(gd.queue))
|
||||
event_consumer_task = asyncio.create_task(event_consumer(gd.event_queue))
|
||||
broadcast_task = asyncio.create_task(broadcast_loop())
|
||||
|
||||
|
||||
yield # Application runs while this yields
|
||||
|
||||
# Cleanup on shutdown
|
||||
watcher_task.cancel()
|
||||
broadcast_task.cancel()
|
||||
consumer_task.cancel()
|
||||
event_consumer_task.cancel()
|
||||
try:
|
||||
await watcher_task
|
||||
await consumer_task
|
||||
await broadcast_task
|
||||
await event_consumer_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
|
||||
app = FastAPI(lifespan=lifespan)
|
||||
origins = [
|
||||
"http://localhost:3000",
|
||||
"http://127.0.0.1:3000",
|
||||
# Fügen Sie hier weitere erforderlich Ursprünge hinzu
|
||||
]
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"], # Erlaubt alle HTTP-Methoden (GET, POST, etc.)
|
||||
allow_headers=["*"], # Erlaubt alle Header
|
||||
)
|
||||
app.include_router(websocket.router)
|
||||
#app.include_router(semiAuto.router)
|
||||
|
||||
# Serve the built frontend
|
||||
#app.mount("/assets", StaticFiles(directory="static/assets"), name="assets")
|
||||
14
models.py
Normal file
14
models.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from pydantic import BaseModel
|
||||
from asyncua import ua
|
||||
from typing import Any
|
||||
|
||||
class SemiAutoControl(BaseModel):
|
||||
string: int
|
||||
module: int
|
||||
unit: int
|
||||
enable: bool
|
||||
|
||||
class WriteNodeCommand(BaseModel):
|
||||
nodeId: str
|
||||
nodeValue: Any
|
||||
nodeType: ua.VariantType
|
||||
235
nodeList.txt
Normal file
235
nodeList.txt
Normal file
@@ -0,0 +1,235 @@
|
||||
# Semi auto mode control string 1
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[0].stSemiAutoModul1.xSemiAutoEnableUnit1
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[0].stSemiAutoModul1.xSemiAutoEnableUnit2
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[0].stSemiAutoModul1.xSemiAutoEnableUnit3
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[0].stSemiAutoModul1.xSemiAutoEnableUnit4
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[0].stSemiAutoModul2.xSemiAutoEnableUnit1
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[0].stSemiAutoModul2.xSemiAutoEnableUnit2
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[0].stSemiAutoModul2.xSemiAutoEnableUnit3
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[0].stSemiAutoModul2.xSemiAutoEnableUnit4
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[0].stSemiAutoModul3.xSemiAutoEnableUnit1
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[0].stSemiAutoModul3.xSemiAutoEnableUnit2
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[0].stSemiAutoModul3.xSemiAutoEnableUnit3
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[0].stSemiAutoModul3.xSemiAutoEnableUnit4
|
||||
|
||||
# Semi auto mode control string 2
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[1].stSemiAutoModul1.xSemiAutoEnableUnit1
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[1].stSemiAutoModul1.xSemiAutoEnableUnit2
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[1].stSemiAutoModul1.xSemiAutoEnableUnit3
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[1].stSemiAutoModul1.xSemiAutoEnableUnit4
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[1].stSemiAutoModul2.xSemiAutoEnableUnit1
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[1].stSemiAutoModul2.xSemiAutoEnableUnit2
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[1].stSemiAutoModul2.xSemiAutoEnableUnit3
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[1].stSemiAutoModul2.xSemiAutoEnableUnit4
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[1].stSemiAutoModul3.xSemiAutoEnableUnit1
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[1].stSemiAutoModul3.xSemiAutoEnableUnit2
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[1].stSemiAutoModul3.xSemiAutoEnableUnit3
|
||||
ns=4;s=GVL_SCADA.stSemiAutoBatteryEnable[1].stSemiAutoModul3.xSemiAutoEnableUnit4
|
||||
|
||||
# Auto control mode
|
||||
ns=4;s=GVL_SCADA.eCurrentControlMode
|
||||
ns=4;s=GVL_SCADA.xCanChangeControlMode
|
||||
|
||||
# String 2 - Modul 2 - Unit 1
|
||||
# Spannungsmessung
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stE31.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stE31.sUnit
|
||||
|
||||
# Druck segment
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stP11.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stP11.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stP21.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stP21.sUnit
|
||||
|
||||
# Druck tank
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stP12.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stP12.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stP22.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stP22.sUnit
|
||||
|
||||
# Temperatur
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stT11.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stT11.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stT21.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stT21.sUnit
|
||||
|
||||
# Ventile
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS12.stOpenButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS12.stOpenButton.xRelease
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS12.stCloseButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS12.stCloseButton.xRelease
|
||||
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS22.stOpenButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS22.stOpenButton.xRelease
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS22.stCloseButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS22.stCloseButton.xRelease
|
||||
|
||||
# Pumpen
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS11.stSetpoint.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS11.stProcessValue.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS11.stProcessValue.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS11.stStartButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS11.stStartButton.xRelease
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS11.stStopButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS11.stStopButton.xRelease
|
||||
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS21.stSetpoint.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS21.stProcessValue.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS21.stProcessValue.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS21.stStartButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS21.stStartButton.xRelease
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS21.stStopButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit1.stNS21.stStopButton.xRelease
|
||||
|
||||
# String 2 - Modul 2 - Unit 2
|
||||
# Spannungsmessung
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stE31.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stE31.sUnit
|
||||
|
||||
# Druck segment
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stP11.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stP11.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stP21.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stP21.sUnit
|
||||
|
||||
# Druck tank
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stP12.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stP12.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stP22.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stP22.sUnit
|
||||
|
||||
# Temperatur
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stT11.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stT11.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stT21.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stT21.sUnit
|
||||
|
||||
# Ventile
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS12.stOpenButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS12.stOpenButton.xRelease
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS12.stCloseButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS12.stCloseButton.xRelease
|
||||
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS22.stOpenButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS22.stOpenButton.xRelease
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS22.stCloseButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS22.stCloseButton.xRelease
|
||||
|
||||
# Pumpen
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS11.stSetpoint.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS11.stProcessValue.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS11.stProcessValue.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS11.stStartButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS11.stStartButton.xRelease
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS11.stStopButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS11.stStopButton.xRelease
|
||||
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS21.stSetpoint.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS21.stProcessValue.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS21.stProcessValue.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS21.stStartButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS21.stStartButton.xRelease
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS21.stStopButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit2.stNS21.stStopButton.xRelease
|
||||
|
||||
# String 2 - Modul 2 - Unit 3
|
||||
# Spannungsmessung
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stE31.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stE31.sUnit
|
||||
|
||||
# Druck segment
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stP11.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stP11.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stP21.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stP21.sUnit
|
||||
|
||||
# Druck tank
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stP12.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stP12.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stP22.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stP22.sUnit
|
||||
|
||||
# Temperatur
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stT11.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stT11.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stT21.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stT21.sUnit
|
||||
|
||||
# Ventile
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS12.stOpenButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS12.stOpenButton.xRelease
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS12.stCloseButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS12.stCloseButton.xRelease
|
||||
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS22.stOpenButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS22.stOpenButton.xRelease
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS22.stCloseButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS22.stCloseButton.xRelease
|
||||
|
||||
# Pumpen
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS11.stSetpoint.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS11.stProcessValue.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS11.stProcessValue.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS11.stStartButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS11.stStartButton.xRelease
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS11.stStopButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS11.stStopButton.xRelease
|
||||
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS21.stSetpoint.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS21.stProcessValue.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS21.stProcessValue.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS21.stStartButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS21.stStartButton.xRelease
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS21.stStopButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit3.stNS21.stStopButton.xRelease
|
||||
|
||||
# String 2 - Modul 2 - Unit 4
|
||||
# Spannungsmessung
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stE31.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stE31.sUnit
|
||||
|
||||
# Druck segment
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stP11.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stP11.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stP21.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stP21.sUnit
|
||||
|
||||
# Druck tank
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stP12.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stP12.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stP22.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stP22.sUnit
|
||||
|
||||
# Temperatur
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stT11.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stT11.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stT21.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stT21.sUnit
|
||||
|
||||
# Ventile
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS12.stOpenButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS12.stOpenButton.xRelease
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS12.stCloseButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS12.stCloseButton.xRelease
|
||||
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS22.stOpenButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS22.stOpenButton.xRelease
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS22.stCloseButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS22.stCloseButton.xRelease
|
||||
|
||||
# Pumpen
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS11.stSetpoint.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS11.stProcessValue.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS11.stProcessValue.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS11.stStartButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS11.stStartButton.xRelease
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS11.stStopButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS11.stStopButton.xRelease
|
||||
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS21.stSetpoint.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS21.stProcessValue.rValue
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS21.stProcessValue.sUnit
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS21.stStartButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS21.stStartButton.xRelease
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS21.stStopButton.eFeedback
|
||||
ns=4;s=GVL_SCADA.stHMIInterface[1].stHMIInterfaceModule2.stHMIInterfaceUnit4.stNS21.stStopButton.xRelease
|
||||
45
oldCode.py
Normal file
45
oldCode.py
Normal file
@@ -0,0 +1,45 @@
|
||||
#from fastapi.responses import StreamingResponse, FileResponse
|
||||
|
||||
# @app.get("/events")
|
||||
# async def sse_endpoint():
|
||||
# """SSE endpoint to stream OPC UA updates"""
|
||||
# return StreamingResponse(event_stream(), media_type="text/event-stream")
|
||||
|
||||
# Serve index.html
|
||||
#@app.get("/")
|
||||
#async def serve_frontend():
|
||||
# return FileResponse("static/index.html")
|
||||
|
||||
# @app.get("/modes/semiAuto/{strng}")
|
||||
# async def semiAutoStringEventStream(strng):
|
||||
# return StreamingResponse(strng_semi_auto_data_stream(), media_type="text/event-stream")
|
||||
|
||||
# async def event_stream():
|
||||
# # Send current value immediately if available
|
||||
# if latest_value["value"] is not None:
|
||||
# yield f"data: {json.dumps(latest_value)}\n\n"
|
||||
|
||||
# while True:
|
||||
# await new_value_event.wait() # wait for new value
|
||||
# data = json.dumps(latest_value)
|
||||
# yield f"data: {data}\n\n"
|
||||
# new_value_event.clear() # reset event and wait for next update
|
||||
|
||||
# async def strng_semi_auto_data_stream():
|
||||
# while True:
|
||||
# await new_value_event.wait()
|
||||
# data
|
||||
|
||||
# @app.get("/getMode")
|
||||
# async def set_mode(request: Request):
|
||||
# client = request.app.state.opcua_client
|
||||
# nsidx = request.app.state.ns_idx
|
||||
# #node = client.get_node(f"ns={nsidx};s=GVL_SCADA.eRequestedControlMode")
|
||||
# #node = client.get_node(f"ns={nsidx};s=GVL_SCADA.eCurrentControlMode")
|
||||
# try:
|
||||
# node = client.get_node(f"ns={nsidx};s=GVL_SCADA.stHMIInterface[0].stHMIInterfaceModule3.stHMIInterfaceUnit1.stP11.rValue")
|
||||
# value = await node.read_value()
|
||||
# print(value)
|
||||
# return {"node_id": "GVL_SCADA.eCurrentControlMode", "value": value}
|
||||
# except:
|
||||
# return {"node_id": "GVL_SCADA.eCurrentControlMode", "value": 0.0}
|
||||
191
opcuaHandler.py
Normal file
191
opcuaHandler.py
Normal file
@@ -0,0 +1,191 @@
|
||||
import os
|
||||
|
||||
from asyncua import Client, ua, Node
|
||||
from asyncua.crypto.security_policies import SecurityPolicyBasic256Sha256
|
||||
from asyncua.crypto.cert_gen import setup_self_signed_certificate
|
||||
from cryptography.x509.oid import ExtendedKeyUsageOID
|
||||
|
||||
import asyncio
|
||||
|
||||
import globalConfigs as gc
|
||||
import globalData as gd
|
||||
|
||||
|
||||
new_value_event = asyncio.Event() # signals when a new value arrives
|
||||
|
||||
class SubHandler:
|
||||
"""
|
||||
Subscription Handler. To receive events from server for a subscription
|
||||
This class is just a sample class. Whatever class having these methods can be used
|
||||
"""
|
||||
|
||||
def __init__(self, queue, event_queue):
|
||||
self.queue = queue
|
||||
self.event_queue = event_queue
|
||||
|
||||
def datachange_notification(self, node: Node, val, data):
|
||||
"""
|
||||
called for every datachange notification from server
|
||||
"""
|
||||
self.queue.put_nowait((node.nodeid.to_string(), val))
|
||||
|
||||
def event_notification(self, event: ua.EventNotificationList):
|
||||
"""
|
||||
called for every event notification from server
|
||||
"""
|
||||
# print("=== Event empfangen ===")
|
||||
# print("NodeId:", event.NodeId.Identifier)
|
||||
# print("Severity:", event.Severity)
|
||||
# print("Message:", event.Message.Text)
|
||||
# print("ActiveState:", getattr(event, "ActiveState/Id"))
|
||||
# print("ActiveStateTransTime:", getattr(event, "ActiveState/TransitionTime"))
|
||||
# print("ConfirmedState:", getattr(event, "ConfirmedState/Id"))
|
||||
# print("ConfirmedStateTransTime:", getattr(event, "ConfirmedState/TransitionTime"))
|
||||
# print("Retain:", event.Retain)
|
||||
# print("------------------------")
|
||||
|
||||
event_data = {
|
||||
"Severity": event.Severity,
|
||||
"Message": event.Message.Text,
|
||||
"ActiveState": getattr(event, "ActiveState/Id"),
|
||||
"ConfirmedState": getattr(event, "ConfirmedState/Id"),
|
||||
"ActiveStateTransTime": getattr(event, "ActiveState/TransitionTime").isoformat(),
|
||||
"ConfirmedStateTransTime": getattr(event, "ConfirmedState/TransitionTime").isoformat(),
|
||||
"Retain": event.Retain
|
||||
}
|
||||
print("Got event")
|
||||
self.event_queue.put_nowait((event.NodeId.Identifier, event_data))
|
||||
|
||||
|
||||
|
||||
def status_change_notification(self, status: ua.StatusChangeNotification):
|
||||
"""
|
||||
called for every status change notification from server
|
||||
"""
|
||||
print("status_notification %s", status)
|
||||
|
||||
# def load_node_ids():
|
||||
# if not gc.NODE_IDS_FILE_PATH.exists():
|
||||
# return []
|
||||
|
||||
# with open(gc.NODE_IDS_FILE_PATH, "r", encoding="utf-8") as f:
|
||||
# node_ids = [line.strip() for line in f if line.strip() and not line.startswith("#")]
|
||||
|
||||
# if not node_ids:
|
||||
# raise ValueError(f"No node IDs found in {gc.NODE_IDS_FILE_PATH}")
|
||||
# return node_ids
|
||||
|
||||
# async def createSubscriptions():
|
||||
|
||||
# subscription = await gd.opc_ua_client.create_subscription(250, SubHandler(gd.queue))
|
||||
# node_ids = load_node_ids()
|
||||
# nodes = [gd.opc_ua_client.get_node(f"{nid}") for nid in node_ids]
|
||||
# await subscription.subscribe_data_change(nodes)
|
||||
|
||||
async def connection_watcher2():
|
||||
await initOPCUAConnection()
|
||||
|
||||
connected = False
|
||||
|
||||
while not connected:
|
||||
try:
|
||||
# In the first round we can use the normal connect
|
||||
await gd.opc_ua_client.connect()
|
||||
connected = True
|
||||
except Exception:
|
||||
print(f"Reconnecting in {gc.RECONNECT_INTERVAL} seconds")
|
||||
await asyncio.sleep(gc.RECONNECT_INTERVAL)
|
||||
|
||||
|
||||
# Create subscriptions
|
||||
gd.subscription_handler = await gd.opc_ua_client.create_subscription(250, SubHandler(gd.queue, gd.event_queue))
|
||||
|
||||
# Create an event subscription
|
||||
event_logger_node = gd.opc_ua_client.get_node("ns=8;s=eventlogger")
|
||||
await gd.subscription_handler.subscribe_events(event_logger_node, evtypes=[ua.ObjectIds.BaseEventType, "ns=5;i=4000"])
|
||||
|
||||
# Periodically check for connection
|
||||
state = 0
|
||||
while True:
|
||||
match (state):
|
||||
case 0:
|
||||
try:
|
||||
await gd.opc_ua_client.connect_sessionless()
|
||||
await gd.opc_ua_client.create_session()
|
||||
except Exception:
|
||||
|
||||
|
||||
await asyncio.sleep(gc.CHECK_INTERVAL)
|
||||
try:
|
||||
await gd.opc_ua_client.check_connection()
|
||||
except ua.uaerrors._auto.BadConnectionClosed:
|
||||
connected = False
|
||||
print(f"Reconnecting in {gc.RECONNECT_INTERVAL} seconds")
|
||||
await asyncio.sleep(gc.RECONNECT_INTERVAL)
|
||||
|
||||
|
||||
|
||||
async def connection_watcher():
|
||||
"""
|
||||
Background task to keep checking OPC UA client connection.
|
||||
Reconnects automatically if disconnected.
|
||||
"""
|
||||
while True:
|
||||
try:
|
||||
gd.opc_ua_client = Client(url=gc.OPC_UA_URL)
|
||||
|
||||
gd.opc_ua_client.application_uri = gc.CLIENT_APP_URI
|
||||
await gd.opc_ua_client.set_security(
|
||||
SecurityPolicyBasic256Sha256,
|
||||
certificate=str(gc.CERT),
|
||||
private_key=str(gc.PRIVATE_KEY)
|
||||
)
|
||||
gd.opc_ua_client.set_user(gc.OPC_UA_USER)
|
||||
gd.opc_ua_client.set_password(gc.OPC_UA_PW)
|
||||
|
||||
async with gd.opc_ua_client:
|
||||
print("Connected")
|
||||
#print("Creating subscriptions")
|
||||
#await createSubscriptions()
|
||||
gd.subscription_handler = await gd.opc_ua_client.create_subscription(250, SubHandler(gd.queue, gd.event_queue))
|
||||
event_logger_node = gd.opc_ua_client.get_node("ns=8;s=eventlogger")
|
||||
await gd.subscription_handler.subscribe_events(event_logger_node, evtypes=[ua.ObjectIds.BaseEventType, "ns=5;i=4000"])
|
||||
print("Registered events")
|
||||
while True:
|
||||
await asyncio.sleep(gc.CHECK_INTERVAL)
|
||||
await gd.opc_ua_client.check_connection() # Throws a exception if connection is lost
|
||||
except ua.uaerrors._auto.BadConnectionClosed:
|
||||
print("Lost connection")
|
||||
print(f"Reconnecting in {gc.RECONNECT_INTERVAL} seconds")
|
||||
await asyncio.sleep(gc.RECONNECT_INTERVAL)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
break
|
||||
|
||||
|
||||
async def initOPCUAConnection():
|
||||
if (not os.path.isfile(gc.CERT)) or (not os.path.isfile(gc.PRIVATE_KEY)):
|
||||
await setup_self_signed_certificate(
|
||||
gc.PRIVATE_KEY,
|
||||
gc.CERT,
|
||||
gc.CLIENT_APP_URI,
|
||||
gc.HOST_NAME,
|
||||
[ExtendedKeyUsageOID.CLIENT_AUTH],
|
||||
{
|
||||
"countryName": "DE",
|
||||
"stateOrProvinceName": "NRW",
|
||||
"localityName": "HMI backend",
|
||||
"organizationName": "Heisig GmbH",
|
||||
},
|
||||
)
|
||||
|
||||
gd.opc_ua_client = Client(url=gc.OPC_UA_URL)
|
||||
|
||||
gd.opc_ua_client.application_uri = gc.CLIENT_APP_URI
|
||||
await gd.opc_ua_client.set_security(
|
||||
SecurityPolicyBasic256Sha256,
|
||||
certificate=str(gc.CERT),
|
||||
private_key=str(gc.PRIVATE_KEY)
|
||||
)
|
||||
gd.opc_ua_client.set_user(gc.OPC_UA_USER)
|
||||
gd.opc_ua_client.set_password(gc.OPC_UA_PW)
|
||||
0
requirements.txt
Normal file
0
requirements.txt
Normal file
0
routers/__init__.py
Normal file
0
routers/__init__.py
Normal file
62
routers/websocket.py
Normal file
62
routers/websocket.py
Normal file
@@ -0,0 +1,62 @@
|
||||
from fastapi import WebSocket, APIRouter
|
||||
import msgpack
|
||||
import globalData as gd
|
||||
|
||||
from asyncua import ua
|
||||
from models import WriteNodeCommand
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.websocket("/ws")
|
||||
async def websocket_endpoint(ws: WebSocket):
|
||||
global clients
|
||||
await ws.accept()
|
||||
gd.clients[ws] = set()
|
||||
|
||||
try:
|
||||
while True:
|
||||
# empfange binäre Daten vom Client
|
||||
raw = await ws.receive_bytes()
|
||||
data = msgpack.unpackb(raw, raw=False)
|
||||
|
||||
# Subscription handling
|
||||
if "subscribe" in data:
|
||||
gd.clients[ws].update(data["subscribe"])
|
||||
diff_set = gd.subscribed_nodes.symmetric_difference(data["subscribe"])
|
||||
nodes = [gd.opc_ua_client.get_node(f"{nid}") for nid in diff_set]
|
||||
|
||||
#await gd.subscription_handler.subscribe_data_change(nodes)
|
||||
|
||||
initial_values = {
|
||||
nid: val
|
||||
for nid in gd.node_storage
|
||||
if (val := gd.node_storage.get(nid)) is not None
|
||||
}
|
||||
|
||||
initial_events = {
|
||||
nid: val
|
||||
for nid in gd.event_storage
|
||||
if (val := gd.event_storage.get(nid)) is not None
|
||||
}
|
||||
|
||||
#if initial_values:
|
||||
await ws.send_bytes(msgpack.packb({"initial": {"values": initial_values, "events": initial_events}}))
|
||||
|
||||
elif "unsubscribe" in data:
|
||||
gd.clients[ws].pop(data["unsubscribe"])
|
||||
|
||||
elif "command" in data:
|
||||
await handle_command(data["command"])
|
||||
except Exception:
|
||||
if ws in gd.clients:
|
||||
del gd.clients[ws]
|
||||
|
||||
async def handle_command(cmd):
|
||||
#print(f"Received command: {cmd}")
|
||||
try:
|
||||
wnc = WriteNodeCommand.model_validate(cmd)
|
||||
node = gd.opc_ua_client.get_node(cmd["nodeId"])
|
||||
dv = ua.DataValue(ua.Variant(wnc.nodeValue, wnc.nodeType))
|
||||
await node.write_value(dv)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
Reference in New Issue
Block a user