140 lines
4.5 KiB
Python
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
|