From 479bd272e960af5724ac838a0da7eb5107fe4dc8 Mon Sep 17 00:00:00 2001 From: Steve Dodd Date: Sat, 1 Jul 2023 21:39:11 +0100 Subject: [PATCH] Simplify handle_client loop / indent, check for EOF when reading from socket. --- scripts/server.py | 473 +++++++++++++++++++++++----------------------- 1 file changed, 238 insertions(+), 235 deletions(-) diff --git a/scripts/server.py b/scripts/server.py index fc851c6..4e2953e 100755 --- a/scripts/server.py +++ b/scripts/server.py @@ -341,276 +341,279 @@ def handle_client(conn, addr): global EXCLUDE_LIST # print(f"[NEW CONNECTION] {addr} connected.") - connected = True - while connected: + while True: msg_length = conn.recv(HEADER).decode(FORMAT) - if msg_length: - msg_length = int(msg_length) - msg = conn.recv(msg_length).decode(FORMAT) - if msg == DISCONNECT_MESSAGE: - connected = False - else: - # print(f"[{addr}] {msg}") + if not msg_length: + break - args = type('', (), {})() + msg_length = int(msg_length) + msg = conn.recv(msg_length).decode(FORMAT) + if not msg: + break + if msg == DISCONNECT_MESSAGE: + break - args.i = '' - args.o = '' - args.birdweather_id = '99999' - args.include_list = 'null' - args.exclude_list = 'null' - args.overlap = 0.0 - args.week = -1 - args.sensitivity = 1.25 - args.min_conf = 0.70 - args.lat = -1 - args.lon = -1 + # print(f"[{addr}] {msg}") - for line in msg.split('||'): - inputvars = line.split('=') - if inputvars[0] == 'i': - args.i = inputvars[1] - elif inputvars[0] == 'o': - args.o = inputvars[1] - elif inputvars[0] == 'birdweather_id': - args.birdweather_id = inputvars[1] - elif inputvars[0] == 'include_list': - args.include_list = inputvars[1] - elif inputvars[0] == 'exclude_list': - args.exclude_list = inputvars[1] - elif inputvars[0] == 'overlap': - args.overlap = float(inputvars[1]) - elif inputvars[0] == 'week': - args.week = int(inputvars[1]) - elif inputvars[0] == 'sensitivity': - args.sensitivity = float(inputvars[1]) - elif inputvars[0] == 'min_conf': - args.min_conf = float(inputvars[1]) - elif inputvars[0] == 'lat': - args.lat = float(inputvars[1]) - elif inputvars[0] == 'lon': - args.lon = float(inputvars[1]) + args = type('', (), {})() - # Load custom species lists - INCLUDED and EXCLUDED - if not args.include_list == 'null': - INCLUDE_LIST = loadCustomSpeciesList(args.include_list) - else: - INCLUDE_LIST = [] + args.i = '' + args.o = '' + args.birdweather_id = '99999' + args.include_list = 'null' + args.exclude_list = 'null' + args.overlap = 0.0 + args.week = -1 + args.sensitivity = 1.25 + args.min_conf = 0.70 + args.lat = -1 + args.lon = -1 - if not args.exclude_list == 'null': - EXCLUDE_LIST = loadCustomSpeciesList(args.exclude_list) - else: - EXCLUDE_LIST = [] + for line in msg.split('||'): + inputvars = line.split('=') + if inputvars[0] == 'i': + args.i = inputvars[1] + elif inputvars[0] == 'o': + args.o = inputvars[1] + elif inputvars[0] == 'birdweather_id': + args.birdweather_id = inputvars[1] + elif inputvars[0] == 'include_list': + args.include_list = inputvars[1] + elif inputvars[0] == 'exclude_list': + args.exclude_list = inputvars[1] + elif inputvars[0] == 'overlap': + args.overlap = float(inputvars[1]) + elif inputvars[0] == 'week': + args.week = int(inputvars[1]) + elif inputvars[0] == 'sensitivity': + args.sensitivity = float(inputvars[1]) + elif inputvars[0] == 'min_conf': + args.min_conf = float(inputvars[1]) + elif inputvars[0] == 'lat': + args.lat = float(inputvars[1]) + elif inputvars[0] == 'lon': + args.lon = float(inputvars[1]) - birdweather_id = args.birdweather_id + # Load custom species lists - INCLUDED and EXCLUDED + if not args.include_list == 'null': + INCLUDE_LIST = loadCustomSpeciesList(args.include_list) + else: + INCLUDE_LIST = [] - # Read audio data & handle errors - try: - audioData = readAudioData(args.i, args.overlap) + if not args.exclude_list == 'null': + EXCLUDE_LIST = loadCustomSpeciesList(args.exclude_list) + else: + EXCLUDE_LIST = [] - except (NameError, TypeError) as e: - print(f"Error with the following info: {e}") - open('~/BirdNET-Pi/analyzing_now.txt', 'w').close() + birdweather_id = args.birdweather_id - finally: - pass + # Read audio data & handle errors + try: + audioData = readAudioData(args.i, args.overlap) - # Get Date/Time from filename in case Pi gets behind - # now = datetime.now() - full_file_name = args.i - # print('FULL FILENAME: -' + full_file_name + '-') - file_name = Path(full_file_name).stem + except (NameError, TypeError) as e: + print(f"Error with the following info: {e}") + open('~/BirdNET-Pi/analyzing_now.txt', 'w').close() - # Get the RSTP stream identifier from the filename if it exists - RTSP_ident_for_fn = "" - RTSP_ident = re.search("RTSP_[0-9]+-", file_name) - if RTSP_ident is not None: - RTSP_ident_for_fn = RTSP_ident.group() + finally: + pass - # Find and remove the identifier for the RSTP stream url it was from that is added when more than one - # RSTP stream is recorded simultaneously, in order to make the filenames unique as filenames are all - # generated at the same time - file_name = re.sub("RTSP_[0-9]+-", "", file_name) + # Get Date/Time from filename in case Pi gets behind + # now = datetime.now() + full_file_name = args.i + # print('FULL FILENAME: -' + full_file_name + '-') + file_name = Path(full_file_name).stem - # Now we can read the date and time as normal - # First portion of the filename contaning the date in Y m d - file_date = file_name.split('-birdnet-')[0] - # Second portion of the filename containing the time in H:M:S - file_time = file_name.split('-birdnet-')[1] - # Join the date and time together to get a complete string representing when the audio was recorded - date_time_str = file_date + ' ' + file_time - date_time_obj = datetime.datetime.strptime(date_time_str, '%Y-%m-%d %H:%M:%S') - # print('Date:', date_time_obj.date()) - # print('Time:', date_time_obj.time()) - print('Date-time:', date_time_obj) - now = date_time_obj - current_date = now.strftime("%Y-%m-%d") - current_time = now.strftime("%H:%M:%S") - current_iso8601 = now.astimezone(get_localzone()).isoformat() + # Get the RSTP stream identifier from the filename if it exists + RTSP_ident_for_fn = "" + RTSP_ident = re.search("RTSP_[0-9]+-", file_name) + if RTSP_ident is not None: + RTSP_ident_for_fn = RTSP_ident.group() - week_number = int(now.strftime("%V")) - week = max(1, min(week_number, 48)) + # Find and remove the identifier for the RSTP stream url it was from that is added when more than one + # RSTP stream is recorded simultaneously, in order to make the filenames unique as filenames are all + # generated at the same time + file_name = re.sub("RTSP_[0-9]+-", "", file_name) - sensitivity = max(0.5, min(1.0 - (args.sensitivity - 1.0), 1.5)) + # Now we can read the date and time as normal + # First portion of the filename contaning the date in Y m d + file_date = file_name.split('-birdnet-')[0] + # Second portion of the filename containing the time in H:M:S + file_time = file_name.split('-birdnet-')[1] + # Join the date and time together to get a complete string representing when the audio was recorded + date_time_str = file_date + ' ' + file_time + date_time_obj = datetime.datetime.strptime(date_time_str, '%Y-%m-%d %H:%M:%S') + # print('Date:', date_time_obj.date()) + # print('Time:', date_time_obj.time()) + print('Date-time:', date_time_obj) + now = date_time_obj + current_date = now.strftime("%Y-%m-%d") + current_time = now.strftime("%H:%M:%S") + current_iso8601 = now.astimezone(get_localzone()).isoformat() - # Process audio data and get detections - detections = analyzeAudioData(audioData, args.lat, args.lon, week, sensitivity, args.overlap) + week_number = int(now.strftime("%V")) + week = max(1, min(week_number, 48)) - # Write detections to output file - min_conf = max(0.01, min(args.min_conf, 0.99)) - writeResultsToFile(detections, min_conf, args.o) + sensitivity = max(0.5, min(1.0 - (args.sensitivity - 1.0), 1.5)) - ############################################################################### - ############################################################################### + # Process audio data and get detections + detections = analyzeAudioData(audioData, args.lat, args.lon, week, sensitivity, args.overlap) - soundscape_uploaded = False + # Write detections to output file + min_conf = max(0.01, min(args.min_conf, 0.99)) + writeResultsToFile(detections, min_conf, args.o) - # Write detections to Database - myReturn = '' - for i in detections: - myReturn += str(i) + '-' + str(detections[i][0]) + '\n' + ############################################################################### + ############################################################################### - with open(userDir + '/BirdNET-Pi/BirdDB.txt', 'a') as rfile: - for d in detections: - species_apprised_this_run = [] - for entry in detections[d]: - if entry[1] >= min_conf and ((entry[0] in INCLUDE_LIST or len(INCLUDE_LIST) == 0) - and (entry[0] not in EXCLUDE_LIST or len(EXCLUDE_LIST) == 0) - and (entry[0] in PREDICTED_SPECIES_LIST or len(PREDICTED_SPECIES_LIST) == 0)): - # Write to text file. - rfile.write(str(current_date) + ';' + str(current_time) + ';' + entry[0].replace('_', ';').split("/")[0] + ';' - + str(entry[1]) + ";" + str(args.lat) + ';' + str(args.lon) + ';' + str(min_conf) + ';' + str(week) + ';' - + str(args.sensitivity) + ';' + str(args.overlap) + '\n') + soundscape_uploaded = False - # Write to database - Date = str(current_date) - Time = str(current_time) - species = entry[0].split("/")[0] - Sci_Name, Com_Name = species.split('_') - score = entry[1] - Confidence = str(round(score * 100)) - Lat = str(args.lat) - Lon = str(args.lon) - Cutoff = str(args.min_conf) - Week = str(args.week) - Sens = str(args.sensitivity) - Overlap = str(args.overlap) - Com_Name = Com_Name.replace("'", "") - File_Name = Com_Name.replace(" ", "_") + '-' + Confidence + '-' + \ - Date.replace("/", "-") + '-birdnet-' + RTSP_ident_for_fn + Time + audiofmt + # Write detections to Database + myReturn = '' + for i in detections: + myReturn += str(i) + '-' + str(detections[i][0]) + '\n' - # Connect to SQLite Database - for attempt_number in range(3): - try: - con = sqlite3.connect(DB_PATH) - cur = con.cursor() - cur.execute("INSERT INTO detections VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", (Date, Time, - Sci_Name, Com_Name, str(score), Lat, Lon, Cutoff, Week, Sens, Overlap, File_Name)) + with open(userDir + '/BirdNET-Pi/BirdDB.txt', 'a') as rfile: + for d in detections: + species_apprised_this_run = [] + for entry in detections[d]: + if entry[1] >= min_conf and ((entry[0] in INCLUDE_LIST or len(INCLUDE_LIST) == 0) + and (entry[0] not in EXCLUDE_LIST or len(EXCLUDE_LIST) == 0) + and (entry[0] in PREDICTED_SPECIES_LIST or len(PREDICTED_SPECIES_LIST) == 0)): + # Write to text file. + rfile.write(str(current_date) + ';' + str(current_time) + ';' + entry[0].replace('_', ';').split("/")[0] + ';' + + str(entry[1]) + ";" + str(args.lat) + ';' + str(args.lon) + ';' + str(min_conf) + ';' + str(week) + ';' + + str(args.sensitivity) + ';' + str(args.overlap) + '\n') - con.commit() - con.close() - break - except BaseException: - print("Database busy") - time.sleep(2) + # Write to database + Date = str(current_date) + Time = str(current_time) + species = entry[0].split("/")[0] + Sci_Name, Com_Name = species.split('_') + score = entry[1] + Confidence = str(round(score * 100)) + Lat = str(args.lat) + Lon = str(args.lon) + Cutoff = str(args.min_conf) + Week = str(args.week) + Sens = str(args.sensitivity) + Overlap = str(args.overlap) + Com_Name = Com_Name.replace("'", "") + File_Name = Com_Name.replace(" ", "_") + '-' + Confidence + '-' + \ + Date.replace("/", "-") + '-birdnet-' + RTSP_ident_for_fn + Time + audiofmt - # Apprise of detection if not already alerted this run. - if not entry[0] in species_apprised_this_run: - settings_dict = config_to_settings(userDir + '/BirdNET-Pi/scripts/thisrun.txt') - sendAppriseNotifications(species, - str(score), - str(round(score * 100)), - File_Name, - Date, - Time, - Week, - Lat, - Lon, - Cutoff, - Sens, - Overlap, - settings_dict, - DB_PATH) - species_apprised_this_run.append(entry[0]) + # Connect to SQLite Database + for attempt_number in range(3): + try: + con = sqlite3.connect(DB_PATH) + cur = con.cursor() + cur.execute("INSERT INTO detections VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", (Date, Time, + Sci_Name, Com_Name, str(score), Lat, Lon, Cutoff, Week, Sens, Overlap, File_Name)) - print(str(current_date) + - ';' + - str(current_time) + - ';' + - entry[0].replace('_', ';') + - ';' + - str(entry[1]) + - ';' + - str(args.lat) + - ';' + - str(args.lon) + - ';' + - str(min_conf) + - ';' + - str(week) + - ';' + - str(args.sensitivity) + - ';' + - str(args.overlap) + - ';' + - File_Name + - '\n') + con.commit() + con.close() + break + except BaseException: + print("Database busy") + time.sleep(2) - if birdweather_id != "99999": - try: + # Apprise of detection if not already alerted this run. + if not entry[0] in species_apprised_this_run: + settings_dict = config_to_settings(userDir + '/BirdNET-Pi/scripts/thisrun.txt') + sendAppriseNotifications(species, + str(score), + str(round(score * 100)), + File_Name, + Date, + Time, + Week, + Lat, + Lon, + Cutoff, + Sens, + Overlap, + settings_dict, + DB_PATH) + species_apprised_this_run.append(entry[0]) - if soundscape_uploaded is False: - # POST soundscape to server - soundscape_url = 'https://app.birdweather.com/api/v1/stations/' + \ - birdweather_id + \ - '/soundscapes' + \ - '?timestamp=' + \ - current_iso8601 + print(str(current_date) + + ';' + + str(current_time) + + ';' + + entry[0].replace('_', ';') + + ';' + + str(entry[1]) + + ';' + + str(args.lat) + + ';' + + str(args.lon) + + ';' + + str(min_conf) + + ';' + + str(week) + + ';' + + str(args.sensitivity) + + ';' + + str(args.overlap) + + ';' + + File_Name + + '\n') - with open(args.i, 'rb') as f: - wav_data = f.read() - gzip_wav_data = gzip.compress(wav_data) - response = requests.post(url=soundscape_url, data=gzip_wav_data, headers={'Content-Type': 'application/octet-stream', - 'Content-Encoding': 'gzip'}) - print("Soundscape POST Response Status - ", response.status_code) - sdata = response.json() - soundscape_id = sdata['soundscape']['id'] - soundscape_uploaded = True + if birdweather_id != "99999": + try: - # POST detection to server - detection_url = "https://app.birdweather.com/api/v1/stations/" + birdweather_id + "/detections" - start_time = d.split(';')[0] - end_time = d.split(';')[1] - post_begin = "{ " - now_p_start = now + datetime.timedelta(seconds=float(start_time)) - current_iso8601 = now_p_start.astimezone(get_localzone()).isoformat() - post_timestamp = "\"timestamp\": \"" + current_iso8601 + "\"," - post_lat = "\"lat\": " + str(args.lat) + "," - post_lon = "\"lon\": " + str(args.lon) + "," - post_soundscape_id = "\"soundscapeId\": " + str(soundscape_id) + "," - post_soundscape_start_time = "\"soundscapeStartTime\": " + start_time + "," - post_soundscape_end_time = "\"soundscapeEndTime\": " + end_time + "," - post_commonName = "\"commonName\": \"" + entry[0].split('_')[1].split("/")[0] + "\"," - post_scientificName = "\"scientificName\": \"" + entry[0].split('_')[0] + "\"," + if soundscape_uploaded is False: + # POST soundscape to server + soundscape_url = 'https://app.birdweather.com/api/v1/stations/' + \ + birdweather_id + \ + '/soundscapes' + \ + '?timestamp=' + \ + current_iso8601 - if model == "BirdNET_GLOBAL_6K_V2.4_Model_FP16": - post_algorithm = "\"algorithm\": " + "\"2p4\"" + "," - else: - post_algorithm = "\"algorithm\": " + "\"alpha\"" + "," + with open(args.i, 'rb') as f: + wav_data = f.read() + gzip_wav_data = gzip.compress(wav_data) + response = requests.post(url=soundscape_url, data=gzip_wav_data, headers={'Content-Type': 'application/octet-stream', + 'Content-Encoding': 'gzip'}) + print("Soundscape POST Response Status - ", response.status_code) + sdata = response.json() + soundscape_id = sdata['soundscape']['id'] + soundscape_uploaded = True - post_confidence = "\"confidence\": " + str(entry[1]) - post_end = " }" + # POST detection to server + detection_url = "https://app.birdweather.com/api/v1/stations/" + birdweather_id + "/detections" + start_time = d.split(';')[0] + end_time = d.split(';')[1] + post_begin = "{ " + now_p_start = now + datetime.timedelta(seconds=float(start_time)) + current_iso8601 = now_p_start.astimezone(get_localzone()).isoformat() + post_timestamp = "\"timestamp\": \"" + current_iso8601 + "\"," + post_lat = "\"lat\": " + str(args.lat) + "," + post_lon = "\"lon\": " + str(args.lon) + "," + post_soundscape_id = "\"soundscapeId\": " + str(soundscape_id) + "," + post_soundscape_start_time = "\"soundscapeStartTime\": " + start_time + "," + post_soundscape_end_time = "\"soundscapeEndTime\": " + end_time + "," + post_commonName = "\"commonName\": \"" + entry[0].split('_')[1].split("/")[0] + "\"," + post_scientificName = "\"scientificName\": \"" + entry[0].split('_')[0] + "\"," - post_json = post_begin + post_timestamp + post_lat + post_lon + post_soundscape_id + post_soundscape_start_time + \ - post_soundscape_end_time + post_commonName + post_scientificName + post_algorithm + post_confidence + post_end - print(post_json) - response = requests.post(detection_url, json=json.loads(post_json)) - print("Detection POST Response Status - ", response.status_code) - except BaseException: - print("Cannot POST right now") - conn.send(myReturn.encode(FORMAT)) + if model == "BirdNET_GLOBAL_6K_V2.4_Model_FP16": + post_algorithm = "\"algorithm\": " + "\"2p4\"" + "," + else: + post_algorithm = "\"algorithm\": " + "\"alpha\"" + "," - # time.sleep(3) + post_confidence = "\"confidence\": " + str(entry[1]) + post_end = " }" + + post_json = post_begin + post_timestamp + post_lat + post_lon + post_soundscape_id + post_soundscape_start_time + \ + post_soundscape_end_time + post_commonName + post_scientificName + post_algorithm + post_confidence + post_end + print(post_json) + response = requests.post(detection_url, json=json.loads(post_json)) + print("Detection POST Response Status - ", response.status_code) + except BaseException: + print("Cannot POST right now") + conn.send(myReturn.encode(FORMAT)) + + # time.sleep(3) conn.close()