Compare commits
1 Commits
Author | SHA1 | Date |
---|---|---|
Pünkösd Marcell | b9016cfaa3 |
|
@ -11,6 +11,14 @@ steps:
|
|||
sonar_token:
|
||||
from_secret: SONAR_CODE
|
||||
|
||||
- name: sentry
|
||||
image: tormachris/drone-sentry
|
||||
settings:
|
||||
sentry_project: ${DRONE_REPO_NAME}
|
||||
sentry_domain: sentry.kmlabz.com
|
||||
sentry_token:
|
||||
from_secret: SENTRY_TOKEN
|
||||
|
||||
- name: ms-teams
|
||||
image: kuperiu/drone-teams
|
||||
settings:
|
||||
|
|
|
@ -5,25 +5,20 @@ cython
|
|||
|
||||
paho-mqtt
|
||||
|
||||
six
|
||||
deprecation
|
||||
|
||||
cycler~=0.10.0
|
||||
|
||||
cycler==0.10.0
|
||||
deprecation==2.0.7
|
||||
eyeD3==0.9.5
|
||||
filetype==1.0.6
|
||||
hmmlearn==0.2.3
|
||||
joblib~=1.0.1
|
||||
kiwisolver~=1.2.0
|
||||
matplotlib~=3.3.3
|
||||
numpy~=1.20.3
|
||||
joblib==0.14.1
|
||||
kiwisolver==1.2.0
|
||||
matplotlib==3.2.1
|
||||
numpy==1.18.2
|
||||
pyAudioAnalysis==0.3.0
|
||||
pydub==0.23.1
|
||||
pyparsing==2.4.6
|
||||
python-dateutil==2.8.1
|
||||
scikit-learn~=0.24.0
|
||||
scipy~=1.6.2
|
||||
simplejson~=3.17.2
|
||||
|
||||
|
||||
pyAudioAnalysis~=0.3.0
|
||||
tqdm~=4.61.1
|
||||
scikit-learn==0.21.3
|
||||
scipy==1.4.1
|
||||
simplejson==3.17.0
|
||||
six==1.14.0
|
|
@ -1,6 +1,9 @@
|
|||
#!/usr/bin/env python3
|
||||
import random
|
||||
import os
|
||||
import os.path
|
||||
from .abcactuator import AbcActuator
|
||||
from utils import BirbnetesIoTPlatformPlaybackDriver, BirbnetesIoTPlatformStatusDriver
|
||||
from birbnetes_iot_platform_raspberry import BirbnetesIoTPlatformPlaybackDriver, BirbnetesIoTPlatformStatusDriver
|
||||
|
||||
"""
|
||||
Abstract base class for Sender
|
||||
|
|
50
src/app.py
50
src/app.py
|
@ -1,16 +1,13 @@
|
|||
#!/usr/bin/env python3
|
||||
from utils import config
|
||||
# config may modify imports
|
||||
import logging
|
||||
import sentry_sdk
|
||||
from sentry_sdk.integrations.logging import LoggingIntegration
|
||||
from utils import LoopingTimer
|
||||
from utils import config, LoopingTimer
|
||||
from signal_processor import SoundSignalProcessor
|
||||
from utils import BirbnetesIoTPlatformStatusDriver, BirbnetesIoTPlatformRecordDriver
|
||||
from birbnetes_iot_platform_raspberry import BirbnetesIoTPlatformStatusDriver
|
||||
from actuator import Loudspeaker
|
||||
import paho.mqtt.client
|
||||
import json
|
||||
import requests
|
||||
import sys
|
||||
|
||||
"""
|
||||
|
@ -31,7 +28,7 @@ if config.SENTRY_DSN:
|
|||
sentry_sdk.init(
|
||||
dsn=config.SENTRY_DSN,
|
||||
integrations=[sentry_logging],
|
||||
traces_sample_rate=0.0,
|
||||
traces_sample_rate=1.0,
|
||||
send_default_pii=True,
|
||||
release=config.RELEASE_ID,
|
||||
environment=config.RELEASEMODE,
|
||||
|
@ -70,25 +67,18 @@ def mqtt_on_command(client, userdata, message):
|
|||
logging.error(f"MQTT Invalid message recieved: {e}")
|
||||
return
|
||||
|
||||
if msg.get("command") == 'doAlert':
|
||||
userdata.act()
|
||||
cmd = msg.get("command")
|
||||
|
||||
if cmd == 'doAlert':
|
||||
userdata[1].act()
|
||||
|
||||
def do_report():
|
||||
report = {
|
||||
"client": config.DEVICE_ID,
|
||||
"measurements": {
|
||||
"queue": BirbnetesIoTPlatformRecordDriver.get_queue_length()
|
||||
}
|
||||
}
|
||||
elif cmd == 'offline':
|
||||
userdata[0][0].soundpreprocessor.set_fail_on_purpose(True)
|
||||
BirbnetesIoTPlatformStatusDriver.enqueue_pattern('red', [1])
|
||||
|
||||
print("Reporting queue length of", report)
|
||||
|
||||
r = requests.post(config.REPORT_URL, json=report)
|
||||
|
||||
r.raise_for_status()
|
||||
if r.status_code != 201:
|
||||
print(config.REPORT_URL, "Wrong response:", r.status_code)
|
||||
elif cmd == 'online':
|
||||
userdata[0][0].soundpreprocessor.set_fail_on_purpose(False)
|
||||
BirbnetesIoTPlatformStatusDriver.enqueue_pattern('green', [1])
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
@ -99,17 +89,14 @@ def main() -> None:
|
|||
logging.basicConfig(stream=sys.stdout, format="%(asctime)s - %(name)s [%(levelname)s]: %(message)s",
|
||||
level=logging.DEBUG if '--debug' in sys.argv else logging.INFO)
|
||||
BirbnetesIoTPlatformStatusDriver.init()
|
||||
listofabcsignaprocessors = [SoundSignalProcessor()] # <- figuring out of the url of filter/input service
|
||||
listofabcsignaprocessors = [SoundSignalProcessor()]
|
||||
loopingtimer = LoopingTimer(function=timer_tick, tick_args=listofabcsignaprocessors, interval=config.TICK_INTERVAL)
|
||||
loopingtimer.start()
|
||||
|
||||
report_timer = None
|
||||
if config.REPORT_URL:
|
||||
print("Setting up queue length reporting...")
|
||||
report_timer = LoopingTimer(function=do_report, tick_args=None, interval=config.REPORT_INTERVAL)
|
||||
report_timer.start()
|
||||
|
||||
client = paho.mqtt.client.Client(userdata=Loudspeaker(config.ENEMY_SOUNDS), client_id=config.DEVICE_ID)
|
||||
client = paho.mqtt.client.Client(
|
||||
userdata=(listofabcsignaprocessors, Loudspeaker(config.ENEMY_SOUNDS)),
|
||||
client_id=config.DEVICE_ID
|
||||
)
|
||||
client.on_connect = mqtt_on_connect
|
||||
client.on_disconnect = mqtt_on_disconnect
|
||||
client.on_message = mqtt_on_command
|
||||
|
@ -124,9 +111,6 @@ def main() -> None:
|
|||
except KeyboardInterrupt:
|
||||
logging.info("SIGINT recieved! Stopping...")
|
||||
|
||||
if report_timer:
|
||||
report_timer.stop()
|
||||
|
||||
client.disconnect()
|
||||
loopingtimer.stop()
|
||||
BirbnetesIoTPlatformStatusDriver.cleanup()
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
#!/usr/bin/env python3
|
||||
import requests
|
||||
from urllib.parse import urljoin
|
||||
from pyAudioAnalysis.audioTrainTest import load_model, load_model_knn, classifier_wrapper
|
||||
from utils import config
|
||||
from .abcpreprocessor import AbcPreProcessor
|
||||
|
||||
import tempfile
|
||||
import os
|
||||
import logging
|
||||
|
||||
if not config.DISABLE_AI:
|
||||
import tempfile
|
||||
import requests
|
||||
from urllib.parse import urljoin
|
||||
import os
|
||||
from pyAudioAnalysis import audioBasicIO
|
||||
from pyAudioAnalysis import MidTermFeatures
|
||||
import numpy
|
||||
import random
|
||||
|
||||
from pyAudioAnalysis.audioTrainTest import load_model, load_model_knn, classifier_wrapper
|
||||
from pyAudioAnalysis import audioBasicIO
|
||||
from pyAudioAnalysis import MidTermFeatures
|
||||
import numpy
|
||||
|
||||
from utils import BirbnetesIoTPlatformStatusDriver
|
||||
from birbnetes_iot_platform_raspberry import BirbnetesIoTPlatformStatusDriver
|
||||
|
||||
"""
|
||||
Abstract base class for Sender
|
||||
|
@ -27,7 +25,7 @@ __module_name__ = "soundpreprocessor"
|
|||
__version__text__ = "1"
|
||||
|
||||
|
||||
class SoundPreProcessorLegit(AbcPreProcessor):
|
||||
class SoundPreProcessor(AbcPreProcessor):
|
||||
"""
|
||||
SoundPreProcessor class, responsible for detecting birb chirps in sound sample.
|
||||
"""
|
||||
|
@ -89,6 +87,8 @@ class SoundPreProcessorLegit(AbcPreProcessor):
|
|||
|
||||
self._target_id = self._classes.index(target_class_name)
|
||||
|
||||
self._fail_on_purpose = False
|
||||
|
||||
def preprocesssignal(self, file_path: str) -> bool:
|
||||
"""
|
||||
Classify a sound sample.
|
||||
|
@ -129,12 +129,19 @@ class SoundPreProcessorLegit(AbcPreProcessor):
|
|||
)
|
||||
class_id = int(class_id) # faszom
|
||||
|
||||
if self._fail_on_purpose: # titkos hozzávaló
|
||||
if class_id == self._target_id:
|
||||
class_id = random.choice(list(set(range(len(self._classes))) - {self._target_id}))
|
||||
|
||||
logging.debug(
|
||||
f"Sample {file_path} identified as {self._classes[class_id]} with the probablility of {probability[class_id]}"
|
||||
)
|
||||
|
||||
return bool((class_id == self._target_id) and (probability[class_id] > 0.5))
|
||||
|
||||
def set_fail_on_purpose(self, val: bool):
|
||||
self._fail_on_purpose = val
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
os.remove(self._temp_model_name)
|
||||
|
@ -145,18 +152,3 @@ class SoundPreProcessorLegit(AbcPreProcessor):
|
|||
os.remove(self._temp_means_name)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
|
||||
class SoundPreProcessorDummy(AbcPreProcessor):
|
||||
|
||||
def __init__(self):
|
||||
logging.info("AI is disabled! Initializing dummy sound pre-processor...")
|
||||
|
||||
def preprocesssignal(self, file_path) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
if config.DISABLE_AI:
|
||||
SoundPreProcessor = SoundPreProcessorDummy
|
||||
else:
|
||||
SoundPreProcessor = SoundPreProcessorLegit
|
||||
|
|
|
@ -5,10 +5,7 @@ import logging
|
|||
from datetime import datetime
|
||||
import requests
|
||||
from .abcsender import AbcSender
|
||||
from utils import config, LoopingTimer
|
||||
from urllib.parse import urljoin
|
||||
import time
|
||||
from threading import Lock
|
||||
from utils import config
|
||||
|
||||
"""
|
||||
Send a sound sample
|
||||
|
@ -25,118 +22,21 @@ class SoundSender(AbcSender):
|
|||
SoundSender class, responsible for sending sound samples to the cloud.
|
||||
"""
|
||||
|
||||
super_global_multi_sender_preventer = Lock()
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self._feed_url = None
|
||||
self._reassign_timer = None
|
||||
if not config.NO_AUTODISCOVER:
|
||||
retry = 0
|
||||
timeout = 30
|
||||
while True:
|
||||
try:
|
||||
r = requests.post(
|
||||
urljoin(config.API_URL, "/assignment"),
|
||||
json={"device_id": config.DEVICE_ID},
|
||||
timeout=timeout
|
||||
)
|
||||
except requests.exceptions.Timeout:
|
||||
logging.warning(f"/assignment timed out after {timeout} sec! Retrying...")
|
||||
continue
|
||||
|
||||
if r.status_code not in [200, 404]:
|
||||
logging.warning(
|
||||
f"Unexpected status code while acquiring an assignment: {r.status_code}. Retrying..."
|
||||
)
|
||||
time.sleep(5)
|
||||
continue
|
||||
|
||||
if r.status_code == 404:
|
||||
logging.info(
|
||||
"/assignment returned 404 error. Assuming missing capability... Defaulting to use API_URL"
|
||||
)
|
||||
self._feed_url = config.API_URL
|
||||
break
|
||||
|
||||
if r.json()['hard_default']:
|
||||
retry += 1
|
||||
if retry < 10:
|
||||
logging.info(f"Still waiting for non hard-default url... Attempt {retry}/10")
|
||||
time.sleep(6)
|
||||
continue
|
||||
else:
|
||||
logging.info(f"Given up waiting for non hard-default url... accepting hard default.")
|
||||
|
||||
logging.info(f"Assigned to {r.json()['site']}")
|
||||
self._feed_url = r.json()['url']
|
||||
break
|
||||
|
||||
self._reassign_timer = LoopingTimer(config.REASSIGN_INTERVAL, lambda: self.reassign())
|
||||
self._reassign_timer.start()
|
||||
|
||||
# If the above block failed to assign a feed_url
|
||||
if not self._feed_url:
|
||||
logging.info("Using API_URL as feed url")
|
||||
self._feed_url = config.API_URL
|
||||
|
||||
def reassign(self):
|
||||
logging.debug("Asking for reassign...")
|
||||
timeout = 30
|
||||
try:
|
||||
r = requests.post(
|
||||
urljoin(config.API_URL, "/assignment"),
|
||||
json={"device_id": config.DEVICE_ID},
|
||||
timeout=timeout
|
||||
)
|
||||
except requests.exceptions.Timeout:
|
||||
logging.warning(f"/assignment timed out after {timeout} sec! Ignoring...")
|
||||
return
|
||||
|
||||
if r.status_code == 404:
|
||||
logging.debug("/assignment returned 404 status code. Assuming missing capability, and doing nothing...")
|
||||
return
|
||||
|
||||
if r.status_code != 200:
|
||||
logging.debug(f"/assignment returned {r.status_code} status code. Ignoring...")
|
||||
return
|
||||
|
||||
logging.debug(f"Received assignment to {r.json()['site']}")
|
||||
|
||||
if r.json()['url'] != self._feed_url:
|
||||
logging.info(f"Reassigned to {r.json()['site']}!")
|
||||
# Because of GIL and because this is a reference, we don't really have to be careful assigning this
|
||||
self._feed_url = r.json()['url']
|
||||
|
||||
def sendvalue(self, value: str, decision: bool) -> None:
|
||||
"""
|
||||
Send a sound sample to the cloud.
|
||||
value: is the file name
|
||||
decision: if true the sample is sent, if false then not
|
||||
:return:
|
||||
"""
|
||||
if decision:
|
||||
files = {
|
||||
"file": (
|
||||
os.path.basename(value),
|
||||
open(value, 'rb').read(),
|
||||
'audio/wave',
|
||||
{'Content-length': os.path.getsize(value)}
|
||||
),
|
||||
"file": (os.path.basename(value), open(value, 'rb').read(), 'audio/wave',
|
||||
{'Content-length': os.path.getsize(value)}),
|
||||
"description": (
|
||||
None,
|
||||
json.dumps({'date': datetime.now().isoformat(), 'device_id': config.DEVICE_ID}),
|
||||
"application/json"
|
||||
)
|
||||
None, json.dumps({'date': datetime.now().isoformat(), 'device_id': config.DEVICE_ID}),
|
||||
"application/json")
|
||||
}
|
||||
|
||||
with SoundSender.super_global_multi_sender_preventer:
|
||||
r = requests.post(urljoin(self._feed_url, config.FEED_TYPE), files=files)
|
||||
|
||||
r = requests.post(config.API_URL + "/sample", files=files)
|
||||
logging.debug(f"Content: {r.content.decode()}")
|
||||
logging.debug(f"Headers: {r.headers}")
|
||||
r.raise_for_status()
|
||||
|
||||
def __del__(self):
|
||||
if self._reassign_timer:
|
||||
self._reassign_timer.stop()
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
from typing import Optional
|
||||
from .abcsensor import AbcSensor
|
||||
from utils import config, BirbnetesIoTPlatformRecordDriver
|
||||
from utils import config
|
||||
from birbnetes_iot_platform_raspberry import BirbnetesIoTPlatformRecordDriver
|
||||
|
||||
"""
|
||||
Sound sensor high level API
|
||||
|
|
|
@ -7,9 +7,7 @@ from preprocessor import SoundPreProcessor
|
|||
from .abcsignalprocessor import AbcSignalProcessor
|
||||
import os
|
||||
|
||||
from utils import BirbnetesIoTPlatformStatusDriver
|
||||
|
||||
from threading import Lock
|
||||
from birbnetes_iot_platform_raspberry import BirbnetesIoTPlatformStatusDriver
|
||||
|
||||
"""
|
||||
Abstract base class for signalprocessor
|
||||
|
@ -26,8 +24,6 @@ class SoundSignalProcessor(AbcSignalProcessor):
|
|||
SoundSignalProcessor class, responsible for handling the sound signal processor pipeline.
|
||||
"""
|
||||
|
||||
super_multi_signal_processing_preventor_to_make_queue_great_again = Lock()
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Create dependency objects.
|
||||
|
@ -41,29 +37,28 @@ class SoundSignalProcessor(AbcSignalProcessor):
|
|||
Process a sound sample.
|
||||
:return:
|
||||
"""
|
||||
with SoundSignalProcessor.super_multi_signal_processing_preventor_to_make_queue_great_again:
|
||||
soundsample_name = self.soundsensor.getvalue()
|
||||
soundsample_name = self.soundsensor.getvalue()
|
||||
|
||||
if not soundsample_name: # No new sample... nothing to do
|
||||
return
|
||||
if not soundsample_name: # No new sample... nothing to do
|
||||
return
|
||||
|
||||
try:
|
||||
sample_decision = self.soundpreprocessor.preprocesssignal(soundsample_name)
|
||||
except Exception as e:
|
||||
logging.exception(e)
|
||||
BirbnetesIoTPlatformStatusDriver.enqueue_pattern('red', [1, 1, 1])
|
||||
os.unlink(soundsample_name)
|
||||
return
|
||||
try:
|
||||
sample_decision = self.soundpreprocessor.preprocesssignal(soundsample_name)
|
||||
except Exception as e:
|
||||
logging.exception(e)
|
||||
BirbnetesIoTPlatformStatusDriver.enqueue_pattern('red', [1, 1, 1])
|
||||
os.unlink(soundsample_name)
|
||||
return
|
||||
|
||||
if sample_decision:
|
||||
BirbnetesIoTPlatformStatusDriver.enqueue_pattern('green', [1, 0, 1])
|
||||
if sample_decision:
|
||||
BirbnetesIoTPlatformStatusDriver.enqueue_pattern('green', [1, 0, 1])
|
||||
|
||||
try:
|
||||
self.soundsender.sendvalue(soundsample_name, sample_decision)
|
||||
except (ConnectionError, requests.HTTPError) as e:
|
||||
logging.exception(e)
|
||||
BirbnetesIoTPlatformStatusDriver.enqueue_pattern('red', [1, 0, 1, 0, 1])
|
||||
os.unlink(soundsample_name)
|
||||
return
|
||||
try:
|
||||
self.soundsender.sendvalue(soundsample_name, sample_decision)
|
||||
except (ConnectionError, requests.HTTPError) as e:
|
||||
logging.exception(e)
|
||||
BirbnetesIoTPlatformStatusDriver.enqueue_pattern('red', [1, 0, 1, 0, 1])
|
||||
os.unlink(soundsample_name)
|
||||
return
|
||||
|
||||
os.unlink(soundsample_name)
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
#!/usr/bin/env python3
|
||||
from .loopingtimer import LoopingTimer
|
||||
from .platform import BirbnetesIoTPlatformStatusDriver, BirbnetesIoTPlatformRecordDriver, \
|
||||
BirbnetesIoTPlatformPlaybackDriver
|
||||
|
|
|
@ -29,20 +29,4 @@ MQTT_PASSWORD = os.getenv("GUARD_MQTT_PASSWORD", None)
|
|||
|
||||
SVM_MODEL_ID = os.environ.get("SVM_MODEL_ID")
|
||||
|
||||
API_URL = os.environ.get("API_URL", "http://localhost:8080")
|
||||
REPORT_URL = os.environ.get("REPORT_URL", None)
|
||||
REPORT_INTERVAL = float(os.environ.get("REPORT_INTERVAL", 15))
|
||||
|
||||
DISABLE_AI = os.environ.get("DISABLE_AI", 'no').lower() in ['yes', '1', 'true']
|
||||
|
||||
PLATFORM = os.environ.get("PLATFORM", "raspberry")
|
||||
|
||||
FEED_TYPE = os.environ.get("FEED_TYPE", "input") # probably the worst naming the type of the accepting service.
|
||||
|
||||
if FEED_TYPE not in ['input', 'filter']:
|
||||
raise ValueError("FEED_TYPE must be either input or filter")
|
||||
|
||||
# Skip fetching /assignment endpoint
|
||||
NO_AUTODISCOVER = os.environ.get("NO_AUTODISCOVER", 'no').lower() in ['yes', '1', 'true']
|
||||
|
||||
REASSIGN_INTERVAL = int(os.environ.get("REASSIGN_INTERVAL", 120))
|
||||
API_URL = os.environ.get("API_URL", "http://localhost:8080")
|
|
@ -1,20 +0,0 @@
|
|||
from .config import PLATFORM
|
||||
|
||||
print("Loading platform driver:", PLATFORM)
|
||||
|
||||
if PLATFORM == 'raspberry':
|
||||
from birbnetes_iot_platform_raspberry import BirbnetesIoTPlatformStatusDriver, BirbnetesIoTPlatformRecordDriver, \
|
||||
BirbnetesIoTPlatformPlaybackDriver
|
||||
|
||||
elif PLATFORM == 'emulator':
|
||||
from birbnetes_iot_platform_emulator import BirbnetesIoTPlatformStatusDriver, BirbnetesIoTPlatformRecordDriver, \
|
||||
BirbnetesIoTPlatformPlaybackDriver
|
||||
|
||||
else:
|
||||
raise ModuleNotFoundError(f"Could not load platform driver for {PLATFORM}")
|
||||
|
||||
__all__ = [
|
||||
'BirbnetesIoTPlatformStatusDriver',
|
||||
'BirbnetesIoTPlatformRecordDriver',
|
||||
'BirbnetesIoTPlatformPlaybackDriver'
|
||||
]
|
Loading…
Reference in New Issue