Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 0 additions & 88 deletions flow_events.log

This file was deleted.

5 changes: 4 additions & 1 deletion pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ addopts = -v
testpaths =
tests
python_files = test_*.py
pythonpath = openscada_lite
pythonpath = openscada_lite
[coverage:run]
omit =
*/__init__.py
2 changes: 1 addition & 1 deletion src/openscada_lite/modules/animation/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ async def list_svgs():
},
)
async def svg(filename: str):
logger.debug(f"Requested SVG file: {filename}")
logger.debug(f"Requested SVG file: {filename}")
logger.debug(f"SVG directory: {self.svg_folder}")
file = Path(self.svg_folder) / filename
if file.exists():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ def __init__(self, server_name: str) -> None:
# Track last known relay status per relay key (RELAY_1, RELAY_2)
self._relay_status: dict[str, str] = {}

self._demo: bool = True # Default to demo mode

# --------------------------------------------------
# Lifecycle
# --------------------------------------------------
Expand All @@ -73,7 +75,20 @@ def initialize(self, config: dict) -> None:
self._subscriptions = config.get("subscriptions", [])
self._publish_templates = config.get("publish", {})

self._demo = config.get("demo", True) # Default to True

async def connect(self) -> None:
if self._demo:
self._connected = True
if self._status_listener and self._loop:
await self._status_listener(
DriverConnectStatus(
driver_name=self._server_name,
status="online",
)
)
return

self._loop = asyncio.get_running_loop()

self._client = mqtt.Client(client_id=self._config.get("client_id"))
Expand All @@ -97,6 +112,17 @@ async def connect(self) -> None:
self._client.loop_start()

async def disconnect(self) -> None:
if self._demo:
self._connected = False
if self._status_listener and self._loop:
await self._status_listener(
DriverConnectStatus(
driver_name=self._server_name,
status="offline",
)
)
return

if self._client:
self._client.loop_stop()
self._client.disconnect()
Expand All @@ -110,6 +136,30 @@ def subscribe(self, datapoints: List[Datapoint]) -> None:
pass

async def send_command(self, data: SendCommandMsg) -> None:
if self._demo:
# Simulate immediate feedback and status update
if self._feedback_listener:
await self._feedback_listener(
CommandFeedbackMsg(
command_id=data.command_id,
datapoint_identifier=data.datapoint_identifier,
feedback="OK",
value=data.value,
timestamp=datetime.datetime.now(),
)
)
# Simulate status update
relay_key = data.datapoint_identifier.split("@", 1)[1].replace("_CMD", "")
if self._value_listener:
await self._value_listener(
RawTagUpdateMsg(
datapoint_identifier=f"{self._server_name}@{relay_key}_STATUS",
value=data.value,
timestamp=datetime.datetime.now(),
)
)
return

if not self._client or not self._loop:
return

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,12 @@ def __init__(self):
driver_cls = DRIVER_REGISTRY.get(cfg["driver_class"])
if not driver_cls:
raise ValueError(f"Unknown driver class: {cfg['driver_class']}")
driver_instance: DriverProtocol = driver_cls(cfg['name'])
# Instantiate driver using config name as server identifier; no connection_info required
try:
driver_instance: DriverProtocol = driver_cls(cfg["name"]) # Preferred signature
except TypeError:
# Fallback for drivers that accept kwargs
driver_instance = driver_cls(server_name=cfg["name"]) # NOSONAR
driver_instance.initialize(cfg.get("params", {}))
driver_instance.subscribe(datapoint_objs)
self.driver_instances[cfg["name"]] = driver_instance
Expand Down
2 changes: 1 addition & 1 deletion tests/test_animation_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def dummy_config(monkeypatch, tmp_path):

# Patch Config methods used by AnimationService
monkeypatch.setattr(Config, "get_svg_files", lambda self: ["tank.svg"])
monkeypatch.setattr(Config, "_get_svg_folder", lambda self: str(svg_dir))
monkeypatch.setattr(Config, "get_svg_folder", lambda self: str(svg_dir))
monkeypatch.setattr(Config, "get_animations", lambda self: dummy_animations)
monkeypatch.setattr(
Config,
Expand Down
12 changes: 11 additions & 1 deletion tests/test_command_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def allow_all_security(monkeypatch):
def run_server():
import subprocess
import time
import socket
from pathlib import Path

# Ensure SCADA_CONFIG_PATH is set
Expand All @@ -57,7 +58,16 @@ def run_server():
],
env=os.environ.copy(), # Pass the current environment variables to the subprocess
)
time.sleep(2) # Give the server time to start
# Wait for server readiness (avoid race conditions)
start = time.time()
while True:
try:
with socket.create_connection(("127.0.0.1", 5000), timeout=0.5):
break
except OSError:
if time.time() - start > 10:
raise RuntimeError("Server on 127.0.0.1:5000 did not start within 10s")
time.sleep(0.2)
yield
process.terminate()
process.wait()
Expand Down
12 changes: 11 additions & 1 deletion tests/test_datapoint_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
def run_server():
import subprocess
import time
import socket
import os

# Ensure SCADA_CONFIG_PATH is set
Expand All @@ -33,7 +34,16 @@ def run_server():
],
env=os.environ.copy(), # Pass the current environment variables to the subprocess
)
time.sleep(2) # Give the server time to start
# Wait for server readiness (avoid race conditions)
start = time.time()
while True:
try:
with socket.create_connection(("127.0.0.1", 5001), timeout=0.5):
break
except OSError:
if time.time() - start > 10:
raise RuntimeError("Server on 127.0.0.1:5001 did not start within 10s")
time.sleep(0.2)
yield
process.terminate()
process.wait()
Expand Down
3 changes: 3 additions & 0 deletions tests/test_mqtt_tasmota_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ async def test_mqtt_connect_emits_status_and_subscribes(monkeypatch):
{"topic": "stat/{device}/RESULT", "type": "feedback"},
],
"publish": {"command": "cmnd/{device}/{power}"},
"demo": False,
}
)

Expand Down Expand Up @@ -115,6 +116,7 @@ async def test_mqtt_toggle_publishes_opposite_and_feedback(monkeypatch):
],
"publish": {"command": "cmnd/{device}/{power}"},
"command_timeout": 2,
"demo": False,
}
)

Expand Down Expand Up @@ -174,6 +176,7 @@ async def test_mqtt_toggle_unknown_state_sends_toggle(monkeypatch):
"relay_mapping": {"RELAY_2": "POWER2"},
"subscriptions": [],
"publish": {"command": "cmnd/{device}/{power}"},
"demo": False,
}
)

Expand Down