import streamlit as st import yaml import time import os from opcua_connector import opcua_connector # Load config early for password if present try: _APP_CFG = yaml.safe_load(open('./cfg.yaml')) or {} except Exception: _APP_CFG = {} OPCcon = opcua_connector(_APP_CFG) # 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 st.markdown(""" """, unsafe_allow_html=True) # Initialize session state if 'opcua_connector' not in st.session_state: st.session_state.opcua_connector = None 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(OPCcon) -> bool: try: if OPCcon is None: return False return OPCcon.check_connection() == 1 except Exception: return False if st.session_state.opcua_connector and is_opcua_connected(st.session_state.opcua_connector): if st.session_state.connection_status != "Connected": st.session_state.connection_status = "Connected" else: if st.session_state.connection_status != "Disconnected": st.session_state.connection_status = "Disconnected" def initialize_connection(): """Initialize OPC UA connection""" try: # Connect and check status OPCcon.connect() if OPCcon.check_connection() == 1: st.session_state.opcua_connector = OPCcon 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(button_name): """Execute button action with proper error handling""" if button_name == 'INIT': OPCcon.adapt_access_rights() print(f"🔧 INIT button pressed - Access rights adapted") else: OPCcon.press_btn(button_name) print(f"🔘 {button_name} button pressed") return True # 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..."): initialize_connection() st.rerun() # Disconnect button if st.button("🔌 Disconnect", use_container_width=True): if st.session_state.opcua_connector: try: # Disconnect OPC UA using the new method st.session_state.opcua_connector.disconnect() print("🔌 Disconnected from OPC UA server") except Exception as e: print(f"âš ī¸ Warning during disconnect: {e}") st.session_state.opcua_connector = None 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_CFG.get('cred', {}).get('dashboard', {}) or {}).get('password') or os.getenv('DASHBOARD_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('
', 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): execute_button_action('INIT') with btn_col2: if st.button("â–ļī¸\n\nSTART", key="start_btn", use_container_width=True, disabled=buttons_disabled): execute_button_action('BTN_START') with btn_col3: if st.button("âšī¸\n\nSTOP", key="stop_btn", use_container_width=True, disabled=buttons_disabled): execute_button_action('BTN_STOP') with btn_col4: if st.button("🔄\n\nRESET", key="reset_btn", use_container_width=True, disabled=buttons_disabled): execute_button_action('BTN_RESET') st.markdown('
', unsafe_allow_html=True) # Footer removed for minimal UI