Add .gitignore, update app.py and config.py, remove stale docs

This commit is contained in:
2026-04-23 16:04:35 +03:00
parent c6dcdc068d
commit 35fb6b93ad
6 changed files with 677 additions and 531 deletions

20
.gitignore vendored Normal file
View File

@@ -0,0 +1,20 @@
# Python
__pycache__/
*.py[cod]
*.pyo
# Runtime data (device-specific, contains IPs and sensitive config)
data/config.txt
data/device_info.txt
data/log.txt
data/tag.txt
data/idmasa.txt
# Offline wheel cache (large, OS-specific)
Files/reposytory/
Files/system_packages/
# Misc
.lgd-nfy0
.DS_Store
Thumbs.db

View File

@@ -1,186 +0,0 @@
# Prezenta App - Robust Dependency Management System
## Overview
The app.py has been updated to include a comprehensive, self-contained dependency management system that automatically checks and installs required packages from a local repository. This makes the system completely offline-capable and resilient to network issues.
## Key Features
### 🔧 **Automatic Dependency Installation**
- **Self-contained**: No need for separate launcher scripts or shell scripts
- **Offline capability**: Installs packages from local `./Files/reposytory` directory
- **Smart detection**: Checks if packages are already installed before attempting installation
- **Fallback mechanisms**: Multiple installation methods (pip, apt, local wheels)
### 🛡️ **Robust Error Handling**
- **Network resilience**: Handles socket errors gracefully with file-based fallbacks
- **Import safety**: Safe import functions that don't crash the app
- **Graceful degradation**: App continues to work even if some optional features fail
- **Comprehensive logging**: All operations are logged for debugging
### 📦 **Supported Packages**
The system automatically manages these packages:
- `rdm6300` - RFID reader library (critical)
- `requests` - HTTP library (critical)
- `aiohttp` - Async HTTP library (optional)
- `flask` - Web server for command interface (optional)
- `gpiozero` - GPIO control (falls back to dummy if not available)
- All required dependencies (urllib3, certifi, charset_normalizer, etc.)
## How It Works
### 1. **Startup Dependency Check**
When the app starts, it:
1. Checks each required package using `importlib.util.find_spec()`
2. Identifies missing packages
3. Attempts installation from local wheel files in `./Files/reposytory`
4. Falls back to pip/apt if needed
5. Continues execution even if some packages fail to install
### 2. **Safe Import System**
```python
def safe_import(module_name, package_name=None):
"""Safely import a module with error handling"""
```
- Imports modules without crashing the app
- Returns None for missing modules
- Allows the app to adapt to available packages
### 3. **Network Error Resilience**
- Device hostname/IP saved to `./data/device_info.txt`
- Automatically loads from file when socket errors occur
- Never crashes due to network resolution issues
### 4. **Graceful Feature Degradation**
- **No Flask**: Command server is disabled with warning
- **No gpiozero**: LED functions become print statements
- **No aiohttp**: Falls back to synchronous requests
- **No network**: Uses cached device information
## Repository Structure
### Required Files in `./Files/reposytory/`:
```
├── rdm6300-0.1.1-py3-none-any.whl
├── requests-2.32.3-py3-none-any.whl
├── aiohttp-3.11.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
├── flask-*.whl (optional)
├── urllib3-2.3.0-py3-none-any.whl
├── certifi-2025.1.31-py3-none-any.whl
├── charset_normalizer-3.4.1-py3-none-any.whl
├── idna-3.10-py3-none-any.whl
├── multidict-6.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
├── aiosignal-1.3.2-py2.py3-none-any.whl
├── frozenlist-1.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
├── attrs-25.3.0-py3-none-any.whl
├── yarl-1.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
├── aiohappyeyeballs-2.6.1-py3-none-any.whl
└── propcache-0.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
```
## Installation Process
### Automatic (Recommended)
Simply run the app - dependencies will be installed automatically:
```bash
cd /home/pi/Desktop/prezenta
python3 app.py
```
### Manual Testing
Test the dependency system:
```bash
cd /home/pi/Desktop/prezenta
python3 test_dependencies.py
```
## Folder Structure
The prezenta folder has been streamlined for maximum simplicity:
```
prezenta/
├── app.py # Main application (self-contained)
├── config.py # Configuration module
├── test_dependencies.py # Testing and validation script
├── README_DEPENDENCIES.md # This documentation
├── data/ # Application data
│ ├── device_info.txt # Network configuration cache (auto-created)
│ ├── idmasa.txt # Device identifier
│ └── tag.txt # Backup data
└── Files/ # Local package repository
└── reposytory/ # Wheel files for offline installation
├── rdm6300-*.whl
├── requests-*.whl
├── aiohttp-*.whl
└── ... (all dependencies)
```
### Removed Files (No Longer Needed)
- ~~`launcher.py`~~ - App is now self-launching
- ~~`libraries.sh`~~ - Dependency installation integrated into app.py
- ~~`check_dependencies.py`~~ - Functionality moved to app.py
## Features Added
### 1. **Self-Installing Dependencies**
- Checks all required packages on startup
- Installs missing packages from local repository
- No external network dependencies for package installation
### 2. **Network Error Recovery**
- Handles `socket.gaierror` gracefully
- Saves working network configuration to file
- Loads from file when network issues occur
### 3. **Modular Feature Set**
- Core RFID functionality always works
- Optional features (web interface, LEDs) degrade gracefully
- Clear status messages for all operations
### 4. **Enhanced Logging**
- Dependency check results logged
- Installation attempts logged
- Network fallback operations logged
## Troubleshooting
### Common Issues
1. **"Repository not found"**
- Ensure `./Files/reposytory` directory exists
- Check that wheel files are present
2. **"Permission denied during installation"**
- Script uses `--break-system-packages` flag
- May need to run with appropriate permissions
3. **"rdm6300 is required"**
- This is critical - app will exit if rdm6300 can't be installed
- Check that `rdm6300-0.1.1-py3-none-any.whl` exists in repository
4. **"Flask not available - Command server disabled"**
- Non-critical - RFID functionality continues to work
- Install Flask manually if remote command capability is needed
### Recovery Steps
1. **Check repository**: Ensure all wheel files are present
2. **Test installation**: Run `python3 test_dependencies.py`
3. **Manual install**: Install missing packages manually with pip
4. **Check logs**: Review console output for specific error messages
## Benefits
1. **Zero-configuration deployment**: Just copy files and run
2. **Offline operation**: No internet required after initial setup
3. **Fault tolerance**: Continues working even with partial failures
4. **Easy maintenance**: All dependencies managed automatically
5. **Clear diagnostics**: Detailed status reporting for troubleshooting
## Version History
- **v2.4**: Added robust dependency management and network error handling
- **v2.3**: Added remote command execution capabilities
- **v2.2**: Basic RFID and logging functionality
The system is now production-ready for deployment in environments with limited or no internet connectivity, providing maximum reliability and ease of deployment.

View File

@@ -1,88 +0,0 @@
# Repository Updates Summary
## Date: August 14, 2025
### Changes Made
#### 1. App.py Version 2.7 - Path Detection Fix
**Problem Solved**: Auto-update functionality was failing on devices with case-sensitive file systems where the folder was named "Prezenta" (uppercase P) instead of "prezenta" (lowercase p).
**Changes Made**:
- Replaced hardcoded paths with dynamic path detection using `__file__`
- Auto-update now works regardless of folder case sensitivity
- Enhanced path resolution for Files/repository and system_packages directories
**Code Changes**:
```python
# OLD (hardcoded):
LOCAL_APP_PATH = "/home/pi/Desktop/prezenta/app.py"
LOCAL_REPO_PATH = "/home/pi/Desktop/prezenta/Files/reposytory"
# NEW (dynamic):
current_script_path = os.path.abspath(__file__)
local_base_dir = os.path.dirname(current_script_path)
LOCAL_APP_PATH = current_script_path
LOCAL_REPO_PATH = os.path.join(local_base_dir, "Files", "reposytory")
```
#### 2. Server.py - Port 80 Communication Update
**Problem Solved**: Server was still trying to communicate with devices on port 5000 instead of port 80.
**Changes Made**:
- Updated all device communication endpoints to use port 80
- Fixed command execution, status checks, and auto-update requests
- Ensured consistent port usage across all server-device communications
**Updated Functions**:
- `execute_command_on_device()` - now uses port 80
- `get_device_status()` - now uses port 80
- `auto_update_devices()` - now uses port 80
#### 3. Repository Structure Updates
**Added New Files**:
- `README_DEPENDENCIES.md` - Comprehensive dependency documentation
- `setup_port_capability.sh` - Port 80 capability setup script
- `Files/system_packages/` - System package repository for offline installation
- Enhanced wheel file repository with latest packages
#### 4. Server Monitoring System (New Repository)
**Initialized**: `/home/pi/Desktop/Server_Monitorizare` as a git repository
**Features**:
- Complete device management dashboard
- Remote command execution on port 80
- Auto-update coordination
- Database reset functionality
- Server logs filtering and interface
- Comprehensive error handling
### Git Status
#### Prezenta Repository
- **Current Version**: 2.7
- **Latest Commit**: `6975e18 - v2.7: Fixed auto-update path detection for case-sensitive file systems`
- **Status**: All changes committed successfully
- **Remote**: Not configured (local repository)
#### Server_Monitorizare Repository
- **Status**: Newly initialized git repository
- **Latest Commit**: `42989aa - Initial server monitoring system with port 80 support`
- **Status**: All files committed successfully
- **Remote**: Not configured (local repository)
### Testing Results
- ✅ Dynamic path detection working correctly
- ✅ Port 80 communication updated in server
- ✅ Auto-update functionality fixed for case-sensitive systems
- ✅ Server monitoring system fully operational
### Next Steps
If you want to push to remote repositories:
1. Configure remote origins for both repositories
2. Push commits to remote branches
3. Set up CI/CD if needed
### File Locations
- Main App: `/home/pi/Desktop/prezenta/app.py` (v2.7)
- Server: `/home/pi/Desktop/Server_Monitorizare/server.py`
- Documentation: This summary and README_DEPENDENCIES.md

454
app.py
View File

@@ -129,8 +129,258 @@ except ImportError as e:
def jsonify(data):
return data
import configparser
import json
def load_config():
"""
Load application configuration from data/config.txt (INI format).
Falls back to legacy file (device_info.txt) and hardcoded defaults.
"""
config_path = "./data/config.txt"
defaults = {
"chrome_url": "http://10.76.140.17/iweb_v2/index.php/traceability/production",
"chrome_local_url": "",
"chrome_insecure_origin": "http://10.76.140.17",
"card_post_base_url": "https://dataswsibiusb01.sibiusb.harting.intra/RO_Quality_PRD/api/record",
"server_log_url": "http://rpi-ansible:80/logs",
"update_host": "rpi-ansible",
"update_user": "pi",
"internet_check_host": "10.76.140.17",
"device_name": "notconfig",
"device_hostname": "unknown-device",
"device_ip": "127.0.0.1",
"device_location": "",
"device_info_reviewed_at": "1970-01-01T00:00:00",
"last_synced": "1970-01-01T00:00:00",
}
cfg = dict(defaults)
parser = configparser.ConfigParser()
if os.path.exists(config_path):
try:
parser.read(config_path)
if parser.has_section("chrome"):
if parser.has_option("chrome", "chrome_url"):
cfg["chrome_url"] = parser.get("chrome", "chrome_url")
if parser.has_option("chrome", "chrome_local_url"):
cfg["chrome_local_url"] = parser.get("chrome", "chrome_local_url")
if parser.has_option("chrome", "chrome_insecure_origin"):
cfg["chrome_insecure_origin"] = parser.get("chrome", "chrome_insecure_origin")
if parser.has_section("card_api"):
if parser.has_option("card_api", "base_url"):
cfg["card_post_base_url"] = parser.get("card_api", "base_url")
if parser.has_section("server"):
if parser.has_option("server", "log_url"):
cfg["server_log_url"] = parser.get("server", "log_url")
if parser.has_option("server", "update_host"):
cfg["update_host"] = parser.get("server", "update_host")
if parser.has_option("server", "update_user"):
cfg["update_user"] = parser.get("server", "update_user")
if parser.has_option("server", "internet_check_host"):
cfg["internet_check_host"] = parser.get("server", "internet_check_host")
if parser.has_section("device"):
if parser.has_option("device", "work_place"):
cfg["device_name"] = parser.get("device", "work_place")
elif parser.has_option("device", "name"):
cfg["device_name"] = parser.get("device", "name")
if parser.has_option("device", "hostname"):
cfg["device_hostname"] = parser.get("device", "hostname")
if parser.has_option("device", "ip"):
cfg["device_ip"] = parser.get("device", "ip")
if parser.has_option("device", "info_reviewed_at"):
cfg["device_info_reviewed_at"] = parser.get("device", "info_reviewed_at")
if parser.has_option("device", "location"):
cfg["device_location"] = parser.get("device", "location")
if parser.has_section("meta"):
if parser.has_option("meta", "last_synced"):
cfg["last_synced"] = parser.get("meta", "last_synced")
print(f"\u2713 Configuration loaded from {config_path}")
except Exception as e:
print(f"Warning: Could not parse {config_path}: {e}. Using defaults.")
else:
# Fall back to legacy individual files
try:
with open("./data/device_info.txt", "r") as f:
lines = f.read().strip().split('\n')
if len(lines) >= 1 and lines[0].strip():
cfg["device_hostname"] = lines[0].strip()
if len(lines) >= 2 and lines[1].strip():
cfg["device_ip"] = lines[1].strip()
except Exception:
pass
print("config.txt not found - using legacy files and defaults")
return cfg
# ---------------------------------------------------------------------------
# Server config sync (runs once at startup)
# ---------------------------------------------------------------------------
def _get_mac_address():
"""Return the MAC address of the primary network interface."""
for iface in ['eth0', 'wlan0', 'eth1']:
mac_path = f'/sys/class/net/{iface}/address'
try:
if os.path.exists(mac_path):
with open(mac_path) as f:
mac = f.read().strip()
if mac and mac != '00:00:00:00:00:00':
return mac
except Exception:
continue
# Fallback: derive from UUID node
import uuid as _uuid
raw = _uuid.getnode()
return ':'.join(f'{(raw >> i) & 0xff:02x}' for i in range(40, -1, -8))
def _write_config_from_server(new_cfg):
"""Overwrite data/config.txt with config received from the server."""
import configparser as _cp
p = _cp.ConfigParser()
p.add_section("chrome")
p.set("chrome", "chrome_url", new_cfg.get("chrome_url", ""))
p.set("chrome", "chrome_local_url", new_cfg.get("chrome_local_url", ""))
p.set("chrome", "chrome_insecure_origin", new_cfg.get("chrome_insecure_origin", ""))
p.add_section("card_api")
p.set("card_api", "base_url", new_cfg.get("card_api_base_url", ""))
p.add_section("server")
p.set("server", "log_url", new_cfg.get("server_log_url", ""))
p.set("server", "update_host", new_cfg.get("update_host", ""))
p.set("server", "update_user", new_cfg.get("update_user", ""))
p.set("server", "internet_check_host", new_cfg.get("internet_check_host", ""))
p.add_section("device")
p.set("device", "work_place", new_cfg.get("device_name", "notconfig"))
p.set("device", "hostname", new_cfg.get("hostname", ""))
p.set("device", "ip", new_cfg.get("device_ip", ""))
p.set("device", "location", new_cfg.get("location") or new_cfg.get("device_location", ""))
p.set("device", "info_reviewed_at", new_cfg.get("info_reviewed_at") or "1970-01-01T00:00:00")
p.add_section("meta")
sync_ts = new_cfg.get("config_updated_at") or datetime.now().isoformat()
p.set("meta", "last_synced", sync_ts)
os.makedirs("./data", exist_ok=True)
with open("./data/config.txt", "w") as f:
f.write("# WMT Application Configuration\n")
f.write(f"# Synced from server: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
p.write(f)
print(f"\u2713 config.txt written from server (server ts: {sync_ts})")
def sync_config_with_server():
"""
Called once at startup.
- Derives base URL from server_log_url in APP_CONFIG.
- If server config is newer than last_synced → pull and overwrite config.txt, reload APP_CONFIG.
- Otherwise → send a device-info update request to the server.
Returns True if config was pulled from server.
"""
global APP_CONFIG
try:
import requests as _req
import urllib.parse as _up
parsed = _up.urlparse(APP_CONFIG.get("server_log_url", "http://rpi-ansible:80/logs"))
server_base = f"{parsed.scheme}://{parsed.netloc}"
mac = _get_mac_address()
last_synced_str = APP_CONFIG.get("last_synced", "1970-01-01T00:00:00")
try:
last_synced = datetime.fromisoformat(last_synced_str)
except Exception:
last_synced = datetime(1970, 1, 1)
local_info_reviewed_str = APP_CONFIG.get("device_info_reviewed_at", "1970-01-01T00:00:00")
try:
local_info_reviewed = datetime.fromisoformat(local_info_reviewed_str)
except Exception:
local_info_reviewed = datetime(1970, 1, 1)
print(f"Checking server config (MAC={mac}, last_synced={last_synced_str}, info_reviewed_at={local_info_reviewed_str}) ...")
# --- Step 1: get server timestamp ---
ts_resp = _req.get(
f"{server_base}/api/wmt/config/timestamp",
params={"mac": mac}, timeout=5
)
ts_resp.raise_for_status()
ts_data = ts_resp.json()
server_ts_str = ts_data.get("latest_updated_at", "1970-01-01T00:00:00")
try:
server_ts = datetime.fromisoformat(server_ts_str)
except Exception:
server_ts = datetime(1970, 1, 1)
server_info_reviewed_str = ts_data.get("device_info_reviewed_at") or "1970-01-01T00:00:00"
try:
server_info_reviewed = datetime.fromisoformat(server_info_reviewed_str)
except Exception:
server_info_reviewed = datetime(1970, 1, 1)
# Pull if global settings are newer OR if admin has reviewed device info more recently
needs_pull = server_ts > last_synced or server_info_reviewed > local_info_reviewed
if needs_pull:
# --- Step 2a: pull config ---
print(f"Server config is newer ({server_ts_str}), pulling ...")
cfg_resp = _req.get(f"{server_base}/api/wmt/config/{mac}", timeout=5)
cfg_resp.raise_for_status()
_write_config_from_server(cfg_resp.json())
APP_CONFIG = load_config()
print("\u2705 Config synced from server.")
return True
else:
# --- Step 2b: push device info ---
print("Local config is current, sending device info to server ...")
try:
_hostname = socket.gethostname()
_ip = socket.gethostbyname(_hostname)
except Exception:
_hostname = APP_CONFIG.get("device_hostname", "")
_ip = APP_CONFIG.get("device_ip", "")
_req.post(
f"{server_base}/api/wmt/config/update_request",
json={
"mac_address": mac,
"device_name": APP_CONFIG.get("device_name", ""),
"hostname": _hostname,
"device_ip": _ip,
"client_config_mtime": last_synced_str,
"client_info_reviewed_at": local_info_reviewed_str,
},
timeout=5,
)
print("\u2705 Device info update request sent.")
return False
except Exception as e:
print(f"Config sync skipped (server unreachable or error): {e}")
return False
# Load global application configuration
APP_CONFIG = load_config()
# Attempt to sync with server at startup (non-blocking failures are logged and ignored)
sync_config_with_server()
# Early configuration mode detection (before heavy initialization)
def early_launch_configuration_mode():
"""
@@ -139,9 +389,11 @@ def early_launch_configuration_mode():
try:
print("🔧 Configuration mode detected - launching Screen.html in Chromium")
# Get absolute path to Screen.html
current_dir = os.path.dirname(os.path.abspath(__file__))
screen_html_path = os.path.join(current_dir, "Files", "Screen.html")
# Path to Screen.html relative to the WMT working directory
screen_html_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Files", "Screen.html")
screen_html_relative = "./Files/Screen.html"
if os.path.exists(screen_html_relative):
screen_html_path = os.path.abspath(screen_html_relative)
if not os.path.exists(screen_html_path):
print(f"❌ Screen.html not found at: {screen_html_path}")
@@ -149,11 +401,11 @@ def early_launch_configuration_mode():
print(f"📄 Loading Screen.html from: {screen_html_path}")
# Terminate any existing Chromium processes
chromium_process_name = "chromium"
# Local log only in configuration mode
logging.info("Refreshing Chromium process (configuration mode)")
# Local log only in configuration mode
logging.info("Refreshing Chromium process (configuration mode)")
try:
subprocess.run(["pkill", "-f", chromium_process_name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
time.sleep(5) # Wait for processes to terminate
@@ -168,11 +420,12 @@ def early_launch_configuration_mode():
except Exception as e:
logging.error(f"Failed to refresh Chromium process (configuration mode): {e}")
return False
print("✅ Configuration mode launched successfully")
print(" Network connectivity checks disabled")
print(" Server status messages disabled")
print("🔧 Device running in configuration mode")
print("🔧 To exit configuration mode, change idmasa.txt content to device name")
print("🔧 To exit configuration mode, change work_place in config.txt")
return True
except FileNotFoundError:
@@ -189,8 +442,7 @@ print("CONFIGURATION MODE DETECTION")
print("=" * 60)
try:
with open("./data/idmasa.txt", "r") as f:
name = f.readline().strip() or "noconfig"
name = APP_CONFIG.get("device_name", "noconfig")
print(f"✓ Device name loaded: {name}")
# Check if device is in configuration mode
@@ -202,7 +454,7 @@ try:
print("🚀 Configuration mode active - application will run in setup mode")
print("⚠️ Network connectivity checks are DISABLED")
print("⚠️ Server status messages are DISABLED")
print("🔧 Change idmasa.txt to device name to exit configuration mode")
print("🔧 Change config.txt [device] name to exit configuration mode")
# Set global flag for configuration mode
CONFIGURATION_MODE = True
@@ -216,20 +468,8 @@ try:
print("✅ Device in normal operation mode")
CONFIGURATION_MODE = False
except FileNotFoundError:
print("Warning: idmasa.txt not found, using default 'noconfig'")
name = "noconfig"
CONFIGURATION_MODE = False
# Create the file with default value
try:
os.makedirs("./data", exist_ok=True)
with open("./data/idmasa.txt", "w") as f:
f.write("noconfig")
print("✓ Created default idmasa.txt file")
except Exception as e:
print(f"Could not create idmasa.txt: {e}")
except Exception as e:
print(f"Error reading idmasa.txt: {e}")
print(f"Error in configuration mode detection: {e}")
name = "noconfig"
CONFIGURATION_MODE = False
@@ -305,7 +545,6 @@ def check_system_requirements():
# 2. Check required files and create defaults if missing
required_files = {
'./data/idmasa.txt': 'noconfig',
'./data/log.txt': '',
'./data/tag.txt': '',
'./data/device_info.txt': 'unknown-device\n127.0.0.1\n'
@@ -609,7 +848,19 @@ def get_device_info():
except Exception as file_error:
print(f"Could not load from file: {file_error}")
# Final fallback if everything fails
# Final fallback: use values from APP_CONFIG (config.txt [device] section)
try:
cfg_hostname = APP_CONFIG.get("device_hostname", "")
cfg_ip = APP_CONFIG.get("device_ip", "")
if cfg_hostname and cfg_hostname != "unknown-device":
hostname = cfg_hostname
device_ip = cfg_ip or "127.0.0.1"
print(f"Loaded from config.txt - Hostname: {hostname}, IP: {device_ip}")
return hostname, device_ip
except Exception:
pass
# Absolute last resort defaults
print("All methods failed - Using default values")
hostname = hostname or "unknown-device"
device_ip = "127.0.0.1"
@@ -652,29 +903,42 @@ def delete_old_logs():
else:
log_info_with_server(f"Log file does not exist: {log_file}")
# Function to read the name (idmasa) from the file
# Function to read the work place name from config
def read_name_from_file():
try:
with open("./data/idmasa.txt", "r") as file:
n_masa = file.readline().strip()
n_masa = APP_CONFIG.get("device_name", "")
if n_masa:
return n_masa
except FileNotFoundError:
logging.error("File ./data/idmasa.txt not found.")
return "unknown"
except Exception:
pass
return "unknown"
def _get_os_version() -> str:
"""Read OS pretty-name from /etc/os-release (Raspberry Pi OS, etc.)."""
try:
with open("/etc/os-release") as f:
for line in f:
if line.startswith("PRETTY_NAME="):
return line.split("=", 1)[1].strip().strip('"')
except Exception:
pass
return ""
# Function to send logs to a remote server for the Server_monitorizare APP
def send_log_to_server(log_message, n_masa, hostname, device_ip):
host = hostname
device = device_ip
try:
log_data = {
"hostname": str(host),
"device_ip": str(device),
"hostname": str(hostname),
"device_ip": str(device_ip),
"nume_masa": str(n_masa),
"log_message": str(log_message)
"log_message": str(log_message),
# Device metadata keeps the server record up to date automatically
"device_type": "Raspberry Pi",
"os_version": _get_os_version(),
"location": APP_CONFIG.get("device_location", ""),
"mac_address": _get_mac_address(),
}
server_url = "http://rpi-ansible:80/logs" # Replace with your server's URL
server_url = APP_CONFIG.get("server_log_url", "http://rpi-ansible:80/logs")
print(log_data) # Debugging: Print log_data to verify its contents
response = requests.post(server_url, json=log_data, timeout=5)
response.raise_for_status()
@@ -683,7 +947,7 @@ def send_log_to_server(log_message, n_masa, hostname, device_ip):
logging.error("Failed to send log to server: %s", e)
# Wrapper for logging.info to also send logs to the server Monitorizare APP
def log_info_with_server(message):
n_masa = read_name_from_file() # Read name (idmasa) from the file
n_masa = read_name_from_file() # Read work place name from config
formatted_message = f"{message} (n_masa: {n_masa})" # Format the message
logging.info(formatted_message) # Log the formatted message
@@ -706,6 +970,7 @@ def execute_system_command(command):
allowed_commands = [
"sudo apt update",
"sudo apt upgrade -y",
"sudo apt update && sudo apt upgrade -y", # Combined update and upgrade
"sudo apt autoremove -y",
"sudo apt autoclean",
"sudo reboot",
@@ -811,9 +1076,9 @@ if FLASK_AVAILABLE:
Checks version, downloads newer files if available, and restarts the device
"""
try:
# Configuration
SERVER_HOST = "rpi-ansible"
SERVER_USER = "pi"
# Configuration (read from APP_CONFIG, falling back to hardcoded defaults)
SERVER_HOST = APP_CONFIG.get("update_host", "rpi-ansible")
SERVER_USER = APP_CONFIG.get("update_user", "pi")
SERVER_PASSWORD = "Initial01!"
SERVER_APP_PATH = "/home/pi/Desktop/prezenta/app.py"
SERVER_REPO_PATH = "/home/pi/Desktop/prezenta/Files/reposytory"
@@ -990,6 +1255,76 @@ sudo reboot
except:
pass
@command_app.route('/update_config', methods=['POST'])
def update_config_endpoint():
"""
Update configuration from Server_Monitorizare_v2.
Accepts a JSON body with sections matching config.txt structure.
Example body: {"chrome": {"chrome_url": "http://..."}}
"""
global APP_CONFIG
ALLOWED_SECTIONS = {
"chrome": ["chrome_url", "chrome_local_url", "chrome_insecure_origin"],
"card_api": ["base_url"],
"server": ["log_url", "update_host", "update_user", "internet_check_host"],
"device": ["name", "hostname", "ip"],
}
try:
data = request.json
if not data:
return jsonify({"error": "JSON body required"}), 400
config_path = "./data/config.txt"
parser = configparser.ConfigParser()
if os.path.exists(config_path):
parser.read(config_path)
updated_keys = []
for section, allowed_keys in ALLOWED_SECTIONS.items():
if section in data and isinstance(data[section], dict):
if not parser.has_section(section):
parser.add_section(section)
for key in allowed_keys:
if key in data[section]:
parser.set(section, key, str(data[section][key]))
updated_keys.append(f"{section}.{key}")
os.makedirs("./data", exist_ok=True)
with open(config_path, "w") as f:
f.write("# WMT Application Configuration\n")
f.write(f"# Last updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
parser.write(f)
APP_CONFIG = load_config()
log_info_with_server(f"Configuration updated via API: {updated_keys}")
return jsonify({"status": "success", "updated_keys": updated_keys}), 200
except Exception as e:
log_info_with_server(f"Error updating config: {str(e)}")
return jsonify({"error": f"Failed to update configuration: {str(e)}"}), 500
@command_app.route('/reload_config', methods=['POST'])
def reload_config_endpoint():
"""
Reload configuration from data/config.txt into memory without restarting.
"""
global APP_CONFIG
try:
APP_CONFIG = load_config()
log_info_with_server("Configuration reloaded via API")
return jsonify({
"status": "success",
"message": "Configuration reloaded",
"chrome_url": APP_CONFIG.get("chrome_url"),
"card_post_base_url": APP_CONFIG.get("card_post_base_url"),
"server_log_url": APP_CONFIG.get("server_log_url"),
"device_name": APP_CONFIG.get("device_name"),
}), 200
except Exception as e:
return jsonify({"error": f"Failed to reload config: {str(e)}"}), 500
def start_command_server():
"""
Start the Flask server with enhanced port handling and fallback
@@ -1100,7 +1435,7 @@ def post_backup_data():
# Function to check internet connection
def check_internet_connection():
hostname = "10.76.140.17"
hostname = APP_CONFIG.get("internet_check_host", "10.76.140.17")
cmd_block_wifi = 'sudo rfkill block wifi'
cmd_unblock_wifi = 'sudo rfkill unblock wifi'
log_info_with_server('Internet connection check loaded')
@@ -1133,10 +1468,11 @@ def check_internet_connection():
time.sleep(5) # Wait for processes to terminate
# Relaunch Chromium
url = "10.76.140.17/iweb_v2/index.php/traceability/production"
url = APP_CONFIG.get("chrome_url", "http://10.76.140.17/iweb_v2/index.php/traceability/production")
chrome_insecure = APP_CONFIG.get("chrome_insecure_origin", "http://10.76.140.17")
subprocess.Popen(
["chromium", "--test-type", "--noerrors", "--kiosk", "--start-fullscreen",
"--unsafely-treat-insecure-origin-as-secure=http://10.76.140.17", url],
f"--unsafely-treat-insecure-origin-as-secure={chrome_insecure}", url],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL, start_new_session=True
)
log_info_with_server("Chromium process restarted successfully.")
@@ -1156,8 +1492,9 @@ else:
# Launch Chromium with the specified URLs (only if not in configuration mode)
if not CONFIGURATION_MODE:
url = "10.76.140.17/iweb_v2/index.php/traceability/production" # pentru cazul in care raspberiul nu are sistem de prezenta
subprocess.Popen(["chromium", "--test-type", "--noerrors", "--kiosk", "--start-fullscreen", "--unsafely-treat-insecure-origin-as-secure=http://10.76.140.17", url], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL, start_new_session=True)
url = APP_CONFIG.get("chrome_url", "http://10.76.140.17/iweb_v2/index.php/traceability/production")
chrome_insecure = APP_CONFIG.get("chrome_insecure_origin", "http://10.76.140.17")
subprocess.Popen(["chromium", "--test-type", "--noerrors", "--kiosk", "--start-fullscreen", f"--unsafely-treat-insecure-origin-as-secure={chrome_insecure}", url], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL, start_new_session=True)
print("✅ Main application browser launched")
else:
print("🔧 Configuration mode: Main application browser launch DISABLED")
@@ -1246,23 +1583,14 @@ except Exception as e:
# Initialize table name/ID
print("Initializing device configuration...")
name = "idmasa"
name = APP_CONFIG.get("device_name", "noconfig")
logging.info("LED controls initialized")
logging.info("Variabila Id Masa A fost initializata ")
# Device name is already loaded during early configuration detection
# Use the existing name variable or reload if needed
if 'name' not in globals():
try:
with open("./data/idmasa.txt", "r") as f:
name = f.readline().strip() or "noconfig"
print(f"✓ Device name reloaded: {name}")
if not CONFIGURATION_MODE:
log_info_with_server(f"Device name initialized: {name}")
except Exception as e:
print(f"Error reloading device name: {e}")
name = "noconfig"
print(f"✓ Device name set from config: {name}")
if not CONFIGURATION_MODE:
log_info_with_server(f"Device name initialized: {name}")
logging.info(name)
#clasa reader
@@ -1275,7 +1603,8 @@ class Reader(rdm6300.BaseReader):
return
afisare = time.strftime("%Y-%m-%d&%H:%M:%S")
date = f'https://dataswsibiusb01.sibiusb.harting.intra/RO_Quality_PRD/api/record/{name}/{card.value}/1/{afisare}\n'
_base = APP_CONFIG.get("card_post_base_url", "https://dataswsibiusb01.sibiusb.harting.intra/RO_Quality_PRD/api/record")
date = f'{_base}/{name}/{card.value}/1/{afisare}\n'
info = date
if name == "noconfig":
led1.on()
@@ -1294,7 +1623,8 @@ class Reader(rdm6300.BaseReader):
return
afisare = time.strftime("%Y-%m-%d&%H:%M:%S")
date = f'https://dataswsibiusb01.sibiusb.harting.intra/RO_Quality_PRD/api/record/{name}/{card.value}/0/{afisare}\n'
_base = APP_CONFIG.get("card_post_base_url", "https://dataswsibiusb01.sibiusb.harting.intra/RO_Quality_PRD/api/record")
date = f'{_base}/{name}/{card.value}/0/{afisare}\n'
info = date
if name == "noconfig":
led1.off()

432
config.py
View File

@@ -1,203 +1,273 @@
# config py este gandit pentru a verifica configurarile raspberiului
# verifica hostname-ul si il afiseaza
# verifica ce nume are masa si o afiseaza
# verifica daca masa este configurata cu sistem de prezenta operator
# verifica daca imprimanta este instalata corespunzator
# WMT Device Configuration Tool
# Reads/writes data/config.txt (INI format).
# On save, bumps [meta] last_synced to NOW so app.py will push an
# update-request to Server_Monitorizare_v2 on next startup.
import os
import time
import socket
import subprocess
import configparser
from datetime import datetime
import os, time, socket, cups, subprocess
import FreeSimpleGUI as psg
import FreeSimpleGUI as sg
from multiprocessing import Process
CONFIG_PATH = "./data/config.txt"
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
# import socket
host = socket.gethostname() # luam numele de host
def set_printer(): # importam codul python pentru printer
print("Cheking printer")
# acest segment acceseaza fisierul local idmas.txt si verifica ce informatii sunt acolo
# in mod normal acolo salvam numele mesei care trebuie sa corespunda cu numele imprimantei
f = open("./data/idmasa.txt","r") # deschid fisierul in care am salvat numele printerului
test = f.readlines() # citesc toate liniile
f.close()
# in cazul in care avem fisier corupt il resetam la original
def _get_os_version():
try:
idmasa = test[0]
except IndexError:
idmasa = "0"
print(idmasa)
# citim lista de printere disponibila
conn = cups.Connection ()
printers = conn.getPrinters ()
printer_stat=0
for printer in printers:
print (printer, printers[printer]["device-uri"])
if printer == idmasa:
printer_stat = 1
with open("/etc/os-release") as f:
for line in f:
if line.startswith("PRETTY_NAME="):
return line.split("=", 1)[1].strip().strip('"')
except Exception:
pass
return "unknown"
if printer_stat == 1:
print("Printer is Ok")
else:
print("Printer is not ok")
p_Name = idmasa
print("Installing the new printer")
time.sleep(2)
cmd = "sudo /usr/sbin/lpadmin -p "+idmasa+" -E -v usb://CITIZEN/CT-S310II?serial=00000000 -m CTS310II.ppd"
os.popen(cmd)
time.sleep(2)
print("Printer Was Installed")
def _get_local_ip():
try:
h = socket.gethostname()
return socket.gethostbyname(h)
except Exception:
return "127.0.0.1"
def _read_config():
"""Return a dict with all device-relevant fields from config.txt."""
p = configparser.ConfigParser()
p.read(CONFIG_PATH)
return {
"device_name": p.get("device", "name", fallback="notconfig"),
"hostname": p.get("device", "hostname", fallback=socket.gethostname()),
"device_ip": p.get("device", "ip", fallback=_get_local_ip()),
"location": p.get("device", "location", fallback=""),
"info_reviewed_at":p.get("device", "info_reviewed_at", fallback="1970-01-01T00:00:00"),
# preserve other sections verbatim
"chrome_url": p.get("chrome", "chrome_url", fallback=""),
"chrome_local_url": p.get("chrome", "chrome_local_url", fallback=""),
"chrome_insecure_origin":p.get("chrome", "chrome_insecure_origin",fallback=""),
"card_api_base_url": p.get("card_api", "base_url", fallback=""),
"server_log_url": p.get("server", "log_url", fallback=""),
"update_host": p.get("server", "update_host", fallback=""),
"update_user": p.get("server", "update_user", fallback=""),
"internet_check_host": p.get("server", "internet_check_host", fallback=""),
"last_synced": p.get("meta", "last_synced", fallback="1970-01-01T00:00:00"),
}
def _write_config(cfg):
"""Write all fields back to config.txt and bump last_synced to NOW."""
p = configparser.ConfigParser()
p.add_section("chrome")
p.set("chrome", "chrome_url", cfg.get("chrome_url", ""))
p.set("chrome", "chrome_local_url", cfg.get("chrome_local_url", ""))
p.set("chrome", "chrome_insecure_origin",cfg.get("chrome_insecure_origin", ""))
p.add_section("card_api")
p.set("card_api", "base_url", cfg.get("card_api_base_url", ""))
p.add_section("server")
p.set("server", "log_url", cfg.get("server_log_url", ""))
p.set("server", "update_host", cfg.get("update_host", ""))
p.set("server", "update_user", cfg.get("update_user", ""))
p.set("server", "internet_check_host", cfg.get("internet_check_host", ""))
p.add_section("device")
p.set("device", "name", cfg["device_name"])
p.set("device", "hostname", cfg["hostname"])
p.set("device", "ip", cfg["device_ip"])
p.set("device", "location", cfg.get("location", ""))
p.set("device", "info_reviewed_at", cfg.get("info_reviewed_at", "1970-01-01T00:00:00"))
# Bump last_synced → tells app.py local config is newer → push update request
now_ts = datetime.now().isoformat(timespec="seconds")
p.add_section("meta")
p.set("meta", "last_synced", now_ts)
os.makedirs("./data", exist_ok=True)
with open(CONFIG_PATH, "w") as f:
f.write("# WMT Application Configuration\n")
f.write(f"# Last updated by config.py: {now_ts}\n\n")
p.write(f)
# Keep legacy idmasa.txt in sync for older code paths
with open("./data/idmasa.txt", "w") as f:
f.write(cfg["device_name"])
print(f"✓ config.txt saved (last_synced={now_ts})")
def _set_printer(device_name):
"""Configure CUPS printer to match the device/masa name."""
try:
import cups
conn = cups.Connection()
printers = conn.getPrinters()
for printer in printers:
print(printer, printers[printer]["device-uri"])
time.sleep(1)
print("Printerwas seted to "+idmasa+"")
print("Test printer App Closed")
# partea acesata se ocupa de verificarea existentei celor doua fisiere pentru a nu avea erori
# verificam daca exista fisierul care identifica masa
path = './data/idmasa.txt'
isFile = os.path.isfile(path) #verifica existenta fisierului prin bolean Tru/false
if not isFile:
# print(path)# nu se face nimic pentru ca exista fisierul
fp = open("./data/idmasa.txt", 'w') # cream fisier
fp.write('noconfig') # scriem in fisier prima line pentru a avea un punct de pornire
fp.close() # inchidem fisierul
# verificam fisierul de prezenta pe baza acestuia stim daca locul de munca este configurat cu card de prezenta sau nu
path1 = './data/idmasa.txt' #verificare existenta al doilea fisier
isFile = os.path.isfile(path1)# verifica existenta fisierului
#urmeaza sa citim fisierele pentru a crea cateva variabile
# prima variabila este idmasa
f = open("./data/idmasa.txt","r") # deschid fisierul
name = f.readlines() # citesc toate liniile
f.close() # inchid fisierul
try:
idmasa = name[0]# se verifica daca exista informatie in text
except IndexError:
idmasa = "noconfig"# daca nu exista informatie in text setam variabila
n_config = 0
#incepem sa definim primele functii din configurare
def notokfunction(): # este functie pentru butonul cancel din formular
global n_config
msg1 = "Id masa a fost actualizat la: "+ idmasa +"" ## pregatim mesajul pentru fereastra pop up
n_config = 1
msg2 = "Slotul Pentru cartela este configurat by default" # pregatim mesajul pentru fereastra pop up
layout = [[sg.Text(msg1)], [sg.Text(msg2)], [sg.Button("Ok")]]
window = sg.Window("Configurari", layout)
while True:
event, values = window.read()
if event == "Ok" or event == sg.WIN_CLOSED:
break
window.close()
#am inchis functia notok
#functia pentru butonul ok din formular
def okfunction():
global n_config
if idmasa == config1: # variabila config 1 este preluata din formular
msg1 = "Masa este setat corect: "+ idmasa +"" # se printeaza mesaj ca nu se actualizeaza id de masa
# print(msg1)
else:
f = open("./data/idmasa.txt","w") # deschidem fisierul config in mod scriere
L = config1
f.write(L) # actualizam linia cu noua valuare din config
f.close() # inchidem fisierul
msg1 = "Id masa a fost actualizat la: "+ config1 +"" # pregatim mesajul pentru fereastra pop up
n_config = 0
#
# definim fereastra pentru ok asemena cu functia notok
layout = [[sg.Text(msg1)], [sg.Output(size=(40, 15))], [sg.Button("Ok")]]
window = sg.Window("Configurari", layout)
while True:
event, values = window.read()
# End program if user closes window or
# presses the OK button
if event == "Ok" or event == sg.WIN_CLOSED:
break
if nook == 1:
notokfunction()
n_config = 1
time.sleep(2)
#asteptam 10 secunde si pornim functia de setare printer
set_printer()
#verificam daca hostul corespunde cu ce este in formular
time.sleep(2)
if host == host_conf:
print("Host name ok")
else:
print("Host name not ok")
time.sleep(2)
print("Update Hostname" )
cmd = "sudo hostnamectl set-hostname "+host_conf+"" # comanda sa schimbam hostnameul
if device_name in printers:
print(f"Printer '{device_name}' is already configured.")
return
print(f"Installing printer '{device_name}' ...")
cmd = (f"sudo /usr/sbin/lpadmin -p {device_name} -E "
f"-v usb://CITIZEN/CT-S310II?serial=00000000 -m CTS310II.ppd")
os.popen(cmd)
print("Os hostname updated")
time.sleep(2)
print("System will reboot in 5 seconds")
time.sleep(1)
print("System will reboot in 4 seconds")
time.sleep(1)
print("System will reboot in 3 seconds")
time.sleep(1)
print("System will reboot in 2 seconds")
time.sleep(1)
print("System will reboot in 1 seconds")
time.sleep(3)
print(f"Printer '{device_name}' installed.")
except Exception as e:
print(f"Printer setup skipped: {e}")
def _update_hostname(new_hostname):
"""Change system hostname if it differs from the current one."""
current = socket.gethostname()
if current == new_hostname:
print(f"Hostname unchanged: {new_hostname}")
return
print(f"Updating hostname: {current}{new_hostname}")
subprocess.run(["sudo", "hostnamectl", "set-hostname", new_hostname], check=False)
print("Hostname updated. A reboot is recommended.")
def _show_result(title, lines):
"""Simple modal popup."""
layout = [[sg.Text(line)] for line in lines] + [[sg.Button("OK")]]
w = sg.Window(title, layout, modal=True)
while True:
ev, _ = w.read()
if ev in (sg.WIN_CLOSED, "OK"):
break
w.close()
# ---------------------------------------------------------------------------
# Main GUI
# ---------------------------------------------------------------------------
def main():
cfg = _read_config()
os_ver = _get_os_version()
local_ip = _get_local_ip()
sg.theme("Dark Grey 9")
sg.set_options(font=("Arial Bold", 15))
LABEL_W = 22
INPUT_W = 38
layout = [
[sg.Text("WMT Device Configuration", font=("Arial Bold", 18),
justification="center", expand_x=True, pad=(0, 12))],
# ── Editable fields ──────────────────────────────────────
[sg.HorizontalSeparator()],
[sg.Text("── Device Identity ──", font=("Arial Bold", 13),
text_color="#aaaaaa", pad=(0, 6))],
[sg.Text("Hostname", size=(LABEL_W, 1)),
sg.InputText(cfg["hostname"], key="-HOSTNAME-", size=(INPUT_W, 1))],
[sg.Text("Loc de Munca / Masa", size=(LABEL_W, 1)),
sg.InputText(cfg["device_name"], key="-MASA-", size=(INPUT_W, 1))],
[sg.Text("Location", size=(LABEL_W, 1)),
sg.InputText(cfg.get("location", ""), key="-LOCATION-",
size=(INPUT_W, 1),
tooltip="Physical location, e.g. Floor 2, Line A, Masa-01")],
# ── Read-only system info ─────────────────────────────────
[sg.HorizontalSeparator()],
[sg.Text("── System Info (read-only) ──", font=("Arial Bold", 13),
text_color="#aaaaaa", pad=(0, 6))],
[sg.Text("OS Version", size=(LABEL_W, 1)),
sg.InputText(os_ver, key="-OS-", size=(INPUT_W, 1),
disabled=True, text_color="#888888")],
[sg.Text("IP Address", size=(LABEL_W, 1)),
sg.InputText(local_ip, key="-IP-", size=(INPUT_W, 1),
disabled=True, text_color="#888888")],
[sg.Text("Last Synced", size=(LABEL_W, 1)),
sg.InputText(cfg["last_synced"], key="-SYNC-", size=(INPUT_W, 1),
disabled=True, text_color="#888888")],
# ── Status output ─────────────────────────────────────────
[sg.HorizontalSeparator()],
[sg.Multiline("", key="-OUT-", size=(62, 5), disabled=True,
autoscroll=True, background_color="#1a1a1a",
text_color="#cccccc", font=("Courier", 12))],
# ── Buttons ───────────────────────────────────────────────
[sg.Button("Save & Apply", key="-SAVE-", button_color=("white", "#2980b9"), size=(18, 1)),
sg.Button("Cancel", key="-CANCEL-", size=(12, 1))],
]
window = sg.Window("WMT Configuration", layout, size=(720, 540), finalize=True)
def log(msg):
window["-OUT-"].update(disabled=False)
window["-OUT-"].print(msg)
window["-OUT-"].update(disabled=True)
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, "-CANCEL-"):
break
if event == "-SAVE-":
new_hostname = values["-HOSTNAME-"].strip()
new_masa = values["-MASA-"].strip()
new_location = values["-LOCATION-"].strip()
if not new_hostname or not new_masa:
_show_result("Validation Error",
["Hostname and Loc de Munca / Masa are required."])
continue
# Update cfg dict with edited values
cfg["hostname"] = new_hostname
cfg["device_name"] = new_masa
cfg["location"] = new_location
cfg["device_ip"] = local_ip
# 1. Save config.txt (bumps last_synced → triggers server update request)
try:
_write_config(cfg)
log("✓ config.txt saved. last_synced updated to NOW.")
log(" → app.py will push update request to server on next start.")
except Exception as e:
log(f"✗ Failed to save config.txt: {e}")
# 2. Update hostname if changed
try:
_update_hostname(new_hostname)
log(f"✓ Hostname: {new_hostname}")
except Exception as e:
log(f"✗ Hostname update error: {e}")
# 3. Configure printer
try:
_set_printer(new_masa)
log(f"✓ Printer check done for '{new_masa}'.")
except Exception as e:
log(f" Printer: {e}")
log("")
log("Done. Reboot recommended if hostname changed.")
window.close()
# incepem initializarea feresteri ptrincipale gui
sg.theme('dark grey 9') #alegem tema dark grey
psg.set_options(font=('Arial Bold', 16)) # setam fontul
#setarile de layout
layout = [
[sg.Text('Host_Name', size=(20,1)),sg.InputText(default_text= ""+host+"" , enable_events=False)],
[sg.Text('Loc De Munca', size=(20,1)),sg.InputText(default_text=""+idmasa+"", enable_events=False)],
[sg.Button('Ok'), sg.Button('Cancel')]
]
# setam window
window = psg.Window('Form', layout, size=(800,190),finalize=True)
# citim si configuram widgetul pentru butoanele 1 si 0 din randul
while True:
nook = 0 # cream o variabila care sa ne spuna daca a fost setao butonul ok sau nu
event, values = window.read() # citim valorile intr-o lista numita values
host_conf= values[0] # atribuim primul item din lista variabilei Host config
# aceasta variabila o vom folosi pentru a scrie sa nu noul hostname
print(host_conf)
if event == sg.WIN_CLOSED or event == 'Cancel':
nook = 1 # daca se da cancel setam variabila nook la 1
n_config = 1 # setam variabila n_config la 1
break
config1 = values[1] # atribuim lui config 1 valuarea din campul Loc de munca care a fost scris cu Id masa
# pornim functi care scrie valorile config in fisiere ok function
okfunction()
# si inchidem formularul
if __name__ == "__main__":
main()
# semnalam ca s-a terminat afisarea formularului
break
#inchidem formularul
window.close()
# daca variabila nook este 1
time.sleep(2)

View File