Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to new datamodel #216

Open
wants to merge 24 commits into
base: develop
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
run: |
coverage run -m pytest tests/
coverage xml
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
with:
name: coverage-main
path: ./coverage.xml
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ lock:
cd src; poetry export -f requirements.txt --without-hashes --output requirements.txt

# Build the docker
build-app:
build-app:
$(info if you need to update the client change the hash in the .toml and use make lock before)
docker build . -t pyronear/pyro-engine:latest

build-lib:
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ classifiers = [
dynamic = ["version"]
dependencies = [
"ultralytics==8.2.50",
"pyroclient @ git+https://github.com/pyronear/pyro-api.git@767be30a781b52b29d68579d543e3f45ac8c4713#egg=pyroclient&subdirectory=client",
"opencv-python",
"pyroclient @ git+https://github.com/pyronear/pyro-api.git@a46f5a00869049ffd1a8bb920ac685e44f18deb5#egg=pyroclient&subdirectory=client",
"requests>=2.20.0,<3.0.0",
"tqdm>=4.62.0",
"huggingface_hub==0.23.1",
Expand Down
1 change: 1 addition & 0 deletions pyroengine/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .logger_config import logger # Ensure logger is initialized first

Check notice on line 1 in pyroengine/__init__.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

pyroengine/__init__.py#L1

'.logger_config.logger' imported but unused (F401)
from .core import *
from . import engine, sensors, utils
from .version import __version__
36 changes: 19 additions & 17 deletions pyroengine/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# See LICENSE or go to <https://opensource.org/licenses/Apache-2.0> for full license details.

import asyncio
import logging
import time
from datetime import datetime
from typing import Any, List
Expand All @@ -13,15 +12,13 @@
import urllib3

from .engine import Engine
from .logger_config import logger
from .sensors import ReolinkCamera

__all__ = ["SystemController", "is_day_time"]

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# Configure logging
logging.basicConfig(format="%(asctime)s | %(levelname)s: %(message)s", level=logging.INFO, force=True)


def is_day_time(cache, frame, strategy, delta=0):
"""
Expand Down Expand Up @@ -73,7 +70,7 @@ async def capture_camera_image(camera: ReolinkCamera, image_queue: asyncio.Queue
try:
if camera.cam_type == "ptz":
for idx, pose_id in enumerate(camera.cam_poses):
cam_id = f"{camera.ip_address}_{pose_id}"
cam_id = f"{camera.ip_address}"
frame = camera.capture()
# Move camera to the next pose to avoid waiting
next_pos_id = camera.cam_poses[(idx + 1) % len(camera.cam_poses)]
Expand All @@ -91,7 +88,7 @@ async def capture_camera_image(camera: ReolinkCamera, image_queue: asyncio.Queue
if not is_day_time(None, frame, "ir"):
return False
except Exception as e:
logging.exception(f"Error during image capture from camera {cam_id}: {e}")
logger.exception(f"Error during image capture from camera {cam_id}: {e}")
return True


Expand Down Expand Up @@ -145,17 +142,21 @@ async def analyze_stream(self, image_queue: asyncio.Queue) -> None:
try:
self.engine.predict(frame, cam_id)
except Exception as e:
logging.error(f"Error running prediction: {e}")
logger.error(f"Error running prediction: {e}")
finally:
image_queue.task_done() # Mark the task as done

async def night_mode(self) -> bool:
"""
Checks if it is nighttime for any camera.

Returns:
bool: True if it is daytime for all cameras, False otherwise.
Checks and updates the day_time attribute based on the current frame.
"""
try:
frame = self.cameras[0].capture()
if frame is not None:
self.day_time = is_day_time(None, frame, "ir")
except Exception as e:
logger.exception(f"Exception during initial day time check: {e}")

for camera in self.cameras:
cam_id = camera.ip_address
try:
Expand All @@ -175,7 +176,7 @@ async def night_mode(self) -> bool:
if not is_day_time(None, frame, "ir"):
return False
except Exception as e:
logging.exception(f"Error during image capture from camera {cam_id}: {e}")
logger.exception(f"Error during image capture from camera {cam_id}: {e}")
return True

async def run(self, period: int = 30, send_alerts: bool = True) -> bool:
Expand Down Expand Up @@ -208,14 +209,14 @@ async def run(self, period: int = 30, send_alerts: bool = True) -> bool:
# Process alerts
if send_alerts:
try:
self.engine._process_alerts()
self.engine._process_alerts(self.cameras)
except Exception as e:
logging.error(f"Error processing alerts: {e}")
logger.exception(f"Error processing alerts: {e}")

return self.is_day

except Exception as e:
logging.warning(f"Analyze stream error: {e}")
logger.warning(f"Analyze stream error: {e}")
return True

async def main_loop(self, period: int, send_alerts: bool = True) -> None:
Expand All @@ -229,15 +230,16 @@ async def main_loop(self, period: int, send_alerts: bool = True) -> None:
while True:
start_ts = time.time()
await self.run(period, send_alerts)

if not self.is_day:
while not await self.night_mode():
logging.info("Nighttime detected by at least one camera, sleeping for 1 hour.")
logger.info("Nighttime detected by at least one camera, sleeping for 1 hour.")
await asyncio.sleep(3600) # Sleep for 1 hour
else:
# Sleep only once all images are processed
loop_time = time.time() - start_ts
sleep_time = max(period - (loop_time), 0)
logging.info(f"Loop run under {loop_time:.2f} seconds, sleeping for {sleep_time:.2f}")
logger.info(f"Loop run under {loop_time:.2f} seconds, sleeping for {sleep_time:.2f}")
await asyncio.sleep(sleep_time)

def __repr__(self) -> str:
Expand Down
Loading
Loading