v1.0 .
This commit is contained in:
parent
88eb33fde4
commit
9ee5ab82c5
176
app.py
176
app.py
@ -4,13 +4,38 @@ import time
|
|||||||
import os
|
import os
|
||||||
from opcua_connector import opcua_connector
|
from opcua_connector import opcua_connector
|
||||||
|
|
||||||
# Load config early for password if present
|
class app:
|
||||||
try:
|
def __init__(self):
|
||||||
_APP_CFG = yaml.safe_load(open('./cfg.yaml')) or {}
|
self.app_cfg = yaml.safe_load(open('./cfg.yaml')) or {}
|
||||||
except Exception:
|
self.OPCon = opcua_connector(self.app_cfg)
|
||||||
_APP_CFG = {}
|
|
||||||
|
|
||||||
OPCcon = opcua_connector(_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
|
# Page configuration
|
||||||
st.set_page_config(
|
st.set_page_config(
|
||||||
@ -20,130 +45,28 @@ st.set_page_config(
|
|||||||
initial_sidebar_state="expanded"
|
initial_sidebar_state="expanded"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Custom CSS for better styling
|
# Custom CSS for better styling (external stylesheet)
|
||||||
st.markdown("""
|
with open('styles.css') as f:
|
||||||
<style>
|
st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True)
|
||||||
.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;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
""", unsafe_allow_html=True)
|
|
||||||
|
|
||||||
# Initialize session state
|
# 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:
|
if 'connection_status' not in st.session_state:
|
||||||
st.session_state.connection_status = "Disconnected"
|
st.session_state.connection_status = "Disconnected"
|
||||||
if 'authenticated' not in st.session_state:
|
if 'authenticated' not in st.session_state:
|
||||||
st.session_state.authenticated = False
|
st.session_state.authenticated = False
|
||||||
|
|
||||||
# Re-validate connection status each render (do this before rendering UI)
|
# Re-validate connection status each render (do this before rendering UI)
|
||||||
def is_opcua_connected(OPCcon) -> bool:
|
def is_opcua_connected(conn) -> bool:
|
||||||
try:
|
try:
|
||||||
if OPCcon is None:
|
return bool(conn and conn.check_connection() == 1)
|
||||||
return False
|
|
||||||
return OPCcon.check_connection() == 1
|
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if st.session_state.opcua_connector and is_opcua_connected(st.session_state.opcua_connector):
|
st.session_state.connection_status = (
|
||||||
if st.session_state.connection_status != "Connected":
|
"Connected" if is_opcua_connected(app.OPCon) else "Disconnected"
|
||||||
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
|
# Sidebar
|
||||||
with st.sidebar:
|
with st.sidebar:
|
||||||
@ -152,22 +75,16 @@ with st.sidebar:
|
|||||||
# Initialize button
|
# Initialize button
|
||||||
if st.button("🔌 Initialize Connection", use_container_width=True):
|
if st.button("🔌 Initialize Connection", use_container_width=True):
|
||||||
with st.spinner("Initializing connection..."):
|
with st.spinner("Initializing connection..."):
|
||||||
initialize_connection()
|
app.initialize_connection()
|
||||||
st.rerun()
|
st.rerun()
|
||||||
|
|
||||||
# Disconnect button
|
# Disconnect button
|
||||||
if st.button("🔌 Disconnect", use_container_width=True):
|
if st.button("🔌 Disconnect", use_container_width=True):
|
||||||
if st.session_state.opcua_connector:
|
|
||||||
try:
|
try:
|
||||||
|
app.OPCon.disconnect()
|
||||||
# Disconnect OPC UA using the new method
|
|
||||||
st.session_state.opcua_connector.disconnect()
|
|
||||||
print("🔌 Disconnected from OPC UA server")
|
print("🔌 Disconnected from OPC UA server")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"⚠️ Warning during disconnect: {e}")
|
print(f"⚠️ Warning during disconnect: {e}")
|
||||||
|
|
||||||
st.session_state.opcua_connector = None
|
|
||||||
st.session_state.connection_status = "Disconnected"
|
st.session_state.connection_status = "Disconnected"
|
||||||
print("🔌 Connection reset")
|
print("🔌 Connection reset")
|
||||||
st.rerun()
|
st.rerun()
|
||||||
@ -179,8 +96,7 @@ with st.sidebar:
|
|||||||
# Password gate
|
# Password gate
|
||||||
# Prefer cfg.yaml cred.dashboard.password, else env DASHBOARD_PASSWORD, else 'admin'
|
# Prefer cfg.yaml cred.dashboard.password, else env DASHBOARD_PASSWORD, else 'admin'
|
||||||
expected_password = (
|
expected_password = (
|
||||||
(_APP_CFG.get('cred', {}).get('dashboard', {}) or {}).get('password')
|
(app.app_cfg.get('cred', {}).get('dashboard', {}) or {}).get('password')
|
||||||
or os.getenv('DASHBOARD_PASSWORD')
|
|
||||||
or 'admin'
|
or 'admin'
|
||||||
)
|
)
|
||||||
ui_locked = not st.session_state.authenticated
|
ui_locked = not st.session_state.authenticated
|
||||||
@ -204,19 +120,19 @@ if not ui_locked:
|
|||||||
|
|
||||||
with btn_col1:
|
with btn_col1:
|
||||||
if st.button("🔧\n\nINIT", key="init_btn", use_container_width=True, disabled=buttons_disabled):
|
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:
|
with btn_col2:
|
||||||
if st.button("▶️\n\nSTART", key="start_btn", use_container_width=True, disabled=buttons_disabled):
|
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:
|
with btn_col3:
|
||||||
if st.button("⏹️\n\nSTOP", key="stop_btn", use_container_width=True, disabled=buttons_disabled):
|
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:
|
with btn_col4:
|
||||||
if st.button("🔄\n\nRESET", key="reset_btn", use_container_width=True, disabled=buttons_disabled):
|
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('</div>', unsafe_allow_html=True)
|
st.markdown('</div>', unsafe_allow_html=True)
|
||||||
|
|
||||||
|
|||||||
68
styles.css
Normal file
68
styles.css
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user