diff --git a/app.py b/app.py
index ca61a1c..442f9a7 100644
--- a/app.py
+++ b/app.py
@@ -4,13 +4,38 @@ 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
-try:
- _APP_CFG = yaml.safe_load(open('./cfg.yaml')) or {}
-except Exception:
- _APP_CFG = {}
-
-OPCcon = opcua_connector(_APP_CFG)
+app = app()
# Page configuration
st.set_page_config(
@@ -20,130 +45,28 @@ st.set_page_config(
initial_sidebar_state="expanded"
)
-# Custom CSS for better styling
-st.markdown("""
-
-""", unsafe_allow_html=True)
+# Custom CSS for better styling (external stylesheet)
+with open('styles.css') as f:
+ st.markdown(f"", 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:
+def is_opcua_connected(conn) -> bool:
try:
- if OPCcon is None:
- return False
- return OPCcon.check_connection() == 1
+ return bool(conn and conn.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"
+st.session_state.connection_status = (
+ "Connected" if is_opcua_connected(app.OPCon) else "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:
@@ -152,22 +75,16 @@ with st.sidebar:
# Initialize button
if st.button("đ Initialize Connection", use_container_width=True):
with st.spinner("Initializing connection..."):
- initialize_connection()
+ app.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
+ 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()
@@ -179,8 +96,7 @@ with st.sidebar:
# 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')
+ (app.app_cfg.get('cred', {}).get('dashboard', {}) or {}).get('password')
or 'admin'
)
ui_locked = not st.session_state.authenticated
@@ -204,19 +120,19 @@ if not ui_locked:
with btn_col1:
if st.button("đ§\n\nINIT", key="init_btn", use_container_width=True, disabled=buttons_disabled):
- execute_button_action('INIT')
+ app.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')
+ app.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')
+ app.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')
+ app.execute_button_action('BTN_RESET')
st.markdown('', unsafe_allow_html=True)
diff --git a/styles.css b/styles.css
new file mode 100644
index 0000000..f79bc49
--- /dev/null
+++ b/styles.css
@@ -0,0 +1,68 @@
+.main-header {
+ text-align: center;
+ color: #1f77b4;
+ margin-bottom: 2rem;
+}
+.button-container {
+ display: flex;
+ justify-content: center;
+ gap: 2rem;
+ margin: 2rem 0;
+ flex-wrap: wrap;
+}
+.control-button {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 1.5rem;
+ border-radius: 15px;
+ background: linear-gradient(145deg, #f0f2f6, #ffffff);
+ box-shadow: 5px 5px 15px #d1d9e6, -5px -5px 15px #ffffff;
+ transition: all 0.3s ease;
+ min-width: 150px;
+ cursor: pointer;
+}
+.control-button:hover {
+ transform: translateY(-2px);
+ box-shadow: 8px 8px 20px #d1d9e6, -8px -8px 20px #ffffff;
+}
+.status-indicator {
+ padding: 0.5rem 1rem;
+ border-radius: 20px;
+ font-weight: bold;
+ text-align: center;
+ margin: 1rem 0;
+}
+.status-connected {
+ background-color: #d4edda;
+ color: #155724;
+ border: 1px solid #c3e6cb;
+}
+.status-disconnected {
+ background-color: #f8d7da;
+ color: #721c24;
+ border: 1px solid #f5c6cb;
+}
+.log-container {
+ background-color: #f8f9fa;
+ border: 1px solid #dee2e6;
+ border-radius: 5px;
+ padding: 1rem;
+ max-height: 300px;
+ overflow-y: auto;
+}
+.stButton > button {
+ height: 60px !important;
+ font-size: 14px !important;
+ font-weight: bold !important;
+ border-radius: 10px !important;
+ border: 2px solid #e0e0e0 !important;
+ transition: all 0.3s ease !important;
+}
+.stButton > button:hover {
+ transform: translateY(-2px) !important;
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2) !important;
+ border-color: #1f77b4 !important;
+}
+
+