opcuaCOM_PY/app.py
Eduard Gerlitz 9ee5ab82c5 v1.0 .
2025-09-08 11:21:36 +02:00

140 lines
4.5 KiB
Python

import streamlit as st
import yaml
import time
import os
from opcua_connector import opcua_connector
class app:
def __init__(self):
self.app_cfg = yaml.safe_load(open('./cfg.yaml')) or {}
self.OPCon = opcua_connector(self.app_cfg)
def initialize_connection(self):
"""Initialize OPC UA connection"""
try:
self.OPCon.connect()
if self.OPCon.check_connection() == 1:
st.session_state.connection_status = "Connected"
print("🎉 Connection initialized successfully")
return True
st.session_state.connection_status = "Disconnected"
return False
except Exception as e:
print(f"❌ Failed to initialize connection: {e}")
st.session_state.connection_status = "Disconnected"
return False
def execute_button_action(self, button_name):
"""Execute button action with proper error handling"""
if button_name == 'INIT':
self.OPCon.adapt_access_rights()
print(f"🔧 INIT button pressed - Access rights adapted")
else:
self.OPCon.press_btn(button_name)
print(f"🔘 {button_name} button pressed")
return True
# Load config early for password if present
app = app()
# Page configuration
st.set_page_config(
page_title="OPC UA Robot Control Dashboard",
page_icon="🤖",
layout="wide",
initial_sidebar_state="expanded"
)
# Custom CSS for better styling (external stylesheet)
with open('styles.css') as f:
st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True)
# Initialize session state
if 'connection_status' not in st.session_state:
st.session_state.connection_status = "Disconnected"
if 'authenticated' not in st.session_state:
st.session_state.authenticated = False
# Re-validate connection status each render (do this before rendering UI)
def is_opcua_connected(conn) -> bool:
try:
return bool(conn and conn.check_connection() == 1)
except Exception:
return False
st.session_state.connection_status = (
"Connected" if is_opcua_connected(app.OPCon) else "Disconnected"
)
# Sidebar
with st.sidebar:
st.write(f"Status: {st.session_state.connection_status}")
# Initialize button
if st.button("🔌 Initialize Connection", use_container_width=True):
with st.spinner("Initializing connection..."):
app.initialize_connection()
st.rerun()
# Disconnect button
if st.button("🔌 Disconnect", use_container_width=True):
try:
app.OPCon.disconnect()
print("🔌 Disconnected from OPC UA server")
except Exception as e:
print(f"⚠️ Warning during disconnect: {e}")
st.session_state.connection_status = "Disconnected"
print("🔌 Connection reset")
st.rerun()
st.divider()
# Password gate
# Prefer cfg.yaml cred.dashboard.password, else env DASHBOARD_PASSWORD, else 'admin'
expected_password = (
(app.app_cfg.get('cred', {}).get('dashboard', {}) or {}).get('password')
or 'admin'
)
ui_locked = not st.session_state.authenticated
if ui_locked:
pwd = st.text_input("Enter Dashboard Password", type="password")
if st.button("Login", use_container_width=True):
if pwd == expected_password:
st.session_state.authenticated = True
st.rerun()
else:
st.error("Wrong password")
if not ui_locked:
# Button container
st.markdown('<div class="button-container">', unsafe_allow_html=True)
# Create columns for buttons
btn_col1, btn_col2, btn_col3, btn_col4 = st.columns(4)
buttons_disabled = st.session_state.connection_status != "Connected"
with btn_col1:
if st.button("🔧\n\nINIT", key="init_btn", use_container_width=True, disabled=buttons_disabled):
app.execute_button_action('INIT')
with btn_col2:
if st.button("▶️\n\nSTART", key="start_btn", use_container_width=True, disabled=buttons_disabled):
app.execute_button_action('BTN_START')
with btn_col3:
if st.button("⏹️\n\nSTOP", key="stop_btn", use_container_width=True, disabled=buttons_disabled):
app.execute_button_action('BTN_STOP')
with btn_col4:
if st.button("🔄\n\nRESET", key="reset_btn", use_container_width=True, disabled=buttons_disabled):
app.execute_button_action('BTN_RESET')
st.markdown('</div>', unsafe_allow_html=True)
# Footer removed for minimal UI