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