Compare commits

..

1 Commits
master ... demo

Author SHA1 Message Date
Pünkösd Marcell b9016cfaa3 Fixed some errors in probability calculations
continuous-integration/drone/push Build is passing Details
2021-06-14 02:24:53 +02:00
11 changed files with 89 additions and 249 deletions

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -1,4 +1,2 @@
#!/usr/bin/env python3
from .loopingtimer import LoopingTimer
from .platform import BirbnetesIoTPlatformStatusDriver, BirbnetesIoTPlatformRecordDriver, \
BirbnetesIoTPlatformPlaybackDriver

View File

@ -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")

View File

@ -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'
]