Spaces:
Sleeping
Sleeping
| import asyncio | |
| import threading | |
| import socket | |
| from http.server import HTTPServer, BaseHTTPRequestHandler | |
| import pandas as pd | |
| import requests | |
| from asyncua import Server, ua | |
| # ========================= | |
| # CONFIGURATION | |
| # ========================= | |
| BIND_IP = "::" # IPv6 wildcard | |
| OPCUA_PORT = 4840 | |
| HTTP_PORT = 7860 | |
| CSV_FILE = "data.csv" | |
| ROW_INTERVAL = 2 # seconds | |
| # ========================= | |
| # PUBLIC IP DETECTION (IPv6) | |
| # ========================= | |
| def get_public_ip(): | |
| try: | |
| # This returns IPv6 if available | |
| return requests.get("https://api64.ipify.org", timeout=5).text.strip() | |
| except Exception: | |
| return "::1" | |
| PUBLIC_IP = get_public_ip() | |
| # Wrap IPv6 in brackets for URL usage | |
| def format_host(ip): | |
| if ":" in ip: | |
| return f"[{ip}]" | |
| return ip | |
| FORMATTED_BIND = format_host(BIND_IP) | |
| FORMATTED_PUBLIC = format_host(PUBLIC_IP) | |
| ENDPOINT = f"opc.tcp://{FORMATTED_BIND}:{OPCUA_PORT}/csv-opcua-server/" | |
| PUBLIC_ENDPOINT = f"opc.tcp://{FORMATTED_PUBLIC}:{OPCUA_PORT}/csv-opcua-server/" | |
| # ========================= | |
| # IPV6 HTTP SERVER | |
| # ========================= | |
| class IPv6HTTPServer(HTTPServer): | |
| address_family = socket.AF_INET6 | |
| class HealthHandler(BaseHTTPRequestHandler): | |
| def do_GET(self): | |
| self.send_response(200) | |
| self.send_header("Content-type", "text/plain") | |
| self.end_headers() | |
| self.wfile.write(b"OK - Server is reachable via browser\n") | |
| def log_message(self, format, *args): | |
| return | |
| def start_http_server(): | |
| httpd = IPv6HTTPServer((BIND_IP, HTTP_PORT), HealthHandler) | |
| print(f"HTTP server running on http://{FORMATTED_PUBLIC}:{HTTP_PORT}") | |
| httpd.serve_forever() | |
| # ========================= | |
| # MAIN ASYNC OPC UA SERVER | |
| # ========================= | |
| async def main(): | |
| df = pd.read_csv(CSV_FILE) | |
| if df.empty: | |
| raise ValueError("CSV file is empty") | |
| server = Server() | |
| await server.init() | |
| server.set_endpoint(ENDPOINT) | |
| server.set_server_name("IPv6 CSV OPC UA Async Server") | |
| uri = "http://example.org/public-csv-opcua" | |
| idx = await server.register_namespace(uri) | |
| objects = server.nodes.objects | |
| csv_obj = await objects.add_object(idx, "CSV_Data") | |
| variables = {} | |
| for col in df.columns: | |
| var = await csv_obj.add_variable( | |
| idx, | |
| col, | |
| float(df[col].iloc[0]), | |
| ua.VariantType.Double, | |
| ) | |
| await var.set_writable() | |
| variables[col] = var | |
| print("====================================") | |
| print(" OPC UA SERVER RUNNING (IPv6)") | |
| print(f" Internal bind : {ENDPOINT}") | |
| print(f" Public access : {PUBLIC_ENDPOINT}") | |
| print(f" Publishing 1 row every {ROW_INTERVAL} seconds") | |
| print("====================================") | |
| row_index = 0 | |
| row_count = len(df) | |
| async with server: | |
| while True: | |
| row = df.iloc[row_index] | |
| print(f"Publishing row {row_index + 1}/{row_count}") | |
| for col, var in variables.items(): | |
| value = float(row[col]) | |
| await var.write_value(value) | |
| print(f" {col} = {value}") | |
| row_index = (row_index + 1) % row_count | |
| await asyncio.sleep(ROW_INTERVAL) | |
| # ========================= | |
| # START EVERYTHING | |
| # ========================= | |
| if __name__ == "__main__": | |
| threading.Thread(target=start_http_server, daemon=True).start() | |
| asyncio.run(main()) | |