def turn(client: SoapClient, login: str, password: str, key1: str, key2: str, key3: str, cmd: str, statistic: Statistic) -> bool: """Функция для поворота камеры в нужную сторону (right, left, up, down) с использованием нужного ws метода. либо ptzserver:command (key1 и key3 != ""), либо ptzclient:command (key1 и key3 == ""). :param client: объект soap клиентя :param login: логин пользователя :param password: пароль пользователя :param key1: имя сервера. Если нужно осуществить поворот с помощью ws метода ptzserver:command, то key1 должен быть обязательно заполнен (!= "") :param key2: имя камеры :param key3: профиль камеры. Если нужно осуществить поворот с помощью ws метода ptzserver:command, то key3 должен быть обязательно заполнен (!= "") :param cmd: направление поворота - right, left, up, down :param statistic: объект класса Statistic :return: флаг успешности поворота """ logger = statistic.get_log().get_logger("scripts/tools/ptz") logger.info( "was called (client: SoapClient, login: str, password: str, key1: str, key2: str, key3: str, cmd: str)" ) logger.debug("with params (client_obj, " + login + ", " + password + ", " + key1 + ", " + key2 + ", " + key3 + ", " + cmd + ")") left = 0 right = 0 up = 0 down = 0 if cmd == "left": left = 70 elif cmd == "right": right = 70 elif cmd == "up": up = 70 elif cmd == "down": down = 70 coordinates = get_coordinates(client, login, password, key2, statistic) if cmd == "up" or cmd == "down": current_coordinate = coordinates["tilt"] else: current_coordinate = coordinates["pan"] statistic.append_info("поворот " + cmd + "...", "ИНФО") logger.info("turning " + cmd) if key1 == "" and key3 == "": ws.ptzclient_command_simple(client, key2, login, password, False, left, right, up, down, 0, 0, False, 0, 0, -1, -1, False) statistic.append_info( "ws 'ptzclient:Command[" + cmd + "=70]' отправлен...", "ПОВОРОТ") logger.info("ws method 'ptzclient:Command[" + cmd + "=70]' was sent") ws.ptzclient_command_simple(client, key2, login, password, True, 0, 0, 0, 0, 0, 0, False, 0, 0, -1, -1, False) # отправка остановки statistic.append_info("ws 'ptzclient:Command[" + cmd + "=0]' отправлен...", "ОСТАНОВКА") logger.info("ws method 'ptzclient:Command[" + cmd + "=0]' was sent") old_coordinate = current_coordinate coordinates = get_coordinates(client, login, password, key2, statistic) if cmd == "up" or cmd == "down": current_coordinate = coordinates["tilt"] else: current_coordinate = coordinates["pan"] if compare_coordinates(old_coordinate, current_coordinate) is False: statistic.append_info("Поворот " + cmd + " успешно выполнен!", "ПОВОРОТ") logger.info("turning " + cmd + " was executed successfully!") return True else: return False
def go_to_coordinate(client: SoapClient, login: str, password: str, key2: str, ws_method: str, cmd: str, coordinate: int, statistic: Statistic, inaccuracy: int = 0) -> bool: """Функция перевода камеры в указанные координаты. :param client: объект soap клиентя :param login: логин пользователя :param password: пароль пользователя :param key2: имя камеры :param ws_method: через какой метод выполнять команду (ptzserver или ptzclient) :param cmd: команда :param coordinate: координаты :param statistic: объект класса Statistic :param inaccuracy точность :return: флаг успешности перехода """ logger = statistic.get_log().get_logger("scripts/tools/ptz") logger.info( "was called (client: SoapClient, login: str, password: str, key2: str, \ cmd: str, coordinate: int, inaccuracy: int = 0)") logger.debug("with params (client_obj, " + login + ", " + password + ", " + key2 + ", " + cmd + ", " + str(coordinate) + ", " + str(inaccuracy) + ")") pan_to = -1 tilt_to = -1 if cmd == "PanTo": pan_to = coordinate elif cmd == "TiltTo": tilt_to = coordinate else: statistic.append_error(cmd, "НЕВАЛИД_КОМАНДА_PTZ", True) if ws_method == "ptzclient": ws.ptzclient_command_simple(client, key2, login, password, False, 0, 0, 0, 0, 0, 0, False, 0, 0, pan_to, tilt_to, False) message = "ws 'ptzclient:Command[" + cmd + "=" + str( coordinate) + "]' отправлен..." elif ws_method == "ptzserver": ws.ptzserver_command_simple(client, key2, login, password, False, 0, 0, 0, 0, 0, 0, False, 0, 0, pan_to, tilt_to, False) message = "ws 'ptzserver:Command[" + cmd + "=" + str( coordinate) + "]' отправлен..." else: statistic.append_error(ws_method, "НЕВАЛИД_МЕТОД_PTZ", True) statistic.append_info(message, "ПЕРЕХОД В КООРДИНАТЫ") logger.info(message) time.sleep(1) coordinates = get_coordinates(client, login, password, key2, statistic) if cmd == "TiltTo": current_coordinate = coordinates["tilt"] else: current_coordinate = coordinates["pan"] if compare_coordinates(coordinate, current_coordinate, inaccuracy): statistic.append_info( cmd + " " + str(coordinate) + " выполнена успешно!", "УСПЕХ") logger.info(cmd + " " + str(coordinate) + " was executed successfully!") return True else: return False
def run_events_test(ws_client: SoapClient, token: str, cam_info: dict, events: tuple, inaccuracy: int, event_type: int, video_source: str, statistic: Statistic) -> None: """Функция запуска проверки наличия событий по детектору. Каждое событие появляется через определенное время от начала видео. Функция, учитывая погрешность, вычисляет интервал времени, в котором должно быть событие и уходит в sleep на это время. После пробуждения проверяет наличие события в этом интервале, используя ws метод ewriter:exec с командой select и нужными фильтрами. :param ws_client: объект класса SoapClient; :param token: токен авторизации; :param cam_info: информация по камере (сервер, имя, профиль); :param events: список событий из входных данных вида: [ { "time": 15, "info": {...} } ] см. подробное описание в тестах по аналитике, которые используют данную функцию; :param inaccuracy: погрешность для вычисления грациц интервала, в которых должно быть событие; :param event_type: номер события; :param video_source: путь к видеоисточнику (нужен для удобства вывода сообщений); :param statistic: объект класса Statistic. """ if _video_start_expecting(ws_client, token, cam_info, statistic) is False: statistic.append_error("Источник: " + video_source + "!", "НЕТ ВИДЕО") return # Заранее высчитываются временные интервалы, # в которых должны находиться события start_server_time: str = ws_common.get_current_server_time(ws_client, token, statistic) date_start_server_time = tools.get_date_from_str(start_server_time, statistic) for event in events: event_time = tools.increment_time(date_start_server_time, event['time']) left_interval_time_utc = tools.decrement_time(event_time, inaccuracy) right_interval_time_utc = tools.increment_time(event_time, inaccuracy) left_interval_time = str(tools.convert_time_from_gtc(left_interval_time_utc))[:-9] right_interval_time = str(tools.convert_time_from_gtc(right_interval_time_utc))[:-9] left_interval_time_utc = str(left_interval_time_utc) right_interval_time_utc = str(right_interval_time_utc) event.update({ "start": left_interval_time_utc, "end": right_interval_time_utc }) previous_sleep = 0 for index, event in enumerate(events): time.sleep(event['time'] - previous_sleep + inaccuracy) result = _check_event_existing(ws_client, token, event['start'], event['end'], event_type, cam_info, statistic) if result[0] == 0: statistic.append_success("#" + str(index + 1) + " по источнику " + video_source + " в интервале с " + left_interval_time + " по " + right_interval_time + "!", "ПОЛУЧЕНО СОБЫТИЕ") _check_event_comment(event['info'], result[1][0], statistic) elif result[0] == -1: statistic.append_error("Событие #" + str(index + 1) + " по источнику " + video_source + " в интервале с " + left_interval_time + " по " + right_interval_time + "!", "НЕТ СОБЫТИЯ") time.sleep(inaccuracy / 2) current_server_time: str = ws_common.get_current_server_time(ws_client, token, statistic) event.update({ "start": start_server_time, "end": current_server_time }) result = _check_event_existing(ws_client, token, event['start'], event['end'], event_type, cam_info, statistic) if result[0] == 0: statistic.append_warn("#" + str(index + 1) + " по источнику " + video_source + " в интервале с " + start_server_time + " по " + current_server_time + "!", "ПОЛУЧЕНО СОБЫТИЕ") _check_event_comment(event['info'], result[1][0], statistic) elif result[0] > 1: statistic.append_error("Событие #" + str(index + 1) + " по источнику " + video_source + " в интервале с " + start_server_time + " по " + current_server_time + "!", "МНОГО СОБЫТИй") else: statistic.append_error( "Событие #" + str(index + 1) + " по источнику " + video_source + " в интервале с " + start_server_time + " по " + current_server_time + "!", "НЕТ СОБЫТИЯ") else: statistic.append_error("Событие #" + str(index + 1) + " по источнику " + video_source + " в интервале с " + left_interval_time + " по " + right_interval_time + "!", "МНОГО СОБЫТИй") previous_sleep = event['time']
def update_cam_json(db_path: str, key1: str, key2: str, key3: str, cam_json: dict, statistic: Statistic) -> int: """Изменение json указанной камеры в БД. :param db_path: путь к БД; :param key1: имя сервера; :param key2: имя камеры; :param key3: профиль камеры; :param cam_json: json (граф) камеры; :param statistic: объект класса Statistic. :return: код ошибки: 0 - все ок. """ logger = statistic.get_log().get_logger("scripts/common/db") logger.info( "was called (db_path: str, key1: str, key2: str, key3: str, cam_json: dict, statistic: Statistic)" ) logger.debug("with params (" + db_path + ", " + key1 + ", " + key2 + ", " + key3 + ", " + str(cam_json) + ", stat_obj)") if not os.path.exists(db_path): statistic.append_error("Файла БД не существует!", "БД", True) try: time.sleep(0.2) conn = sqlite3.connect(db_path) cursor = conn.cursor() db_setname = key1 + "_" + key2 + "_" + key3 db_setvalue = json.dumps(cam_json, indent="\t", ensure_ascii=False) # Учитывается момент с \\/ и заменяется на \/. db_setvalue = db_setvalue.replace("\\\\/", "\\/") sql_cmd = "SELECT setid FROM setting WHERE settypeid = ? AND setname = ?" cursor.execute(sql_cmd, [1, db_setname]) db_setid = cursor.fetchall()[0][0] time.sleep(0.2) sql_cmd = "SELECT sethash FROM setting WHERE setid = ?" cursor.execute(sql_cmd, [str(db_setid)]) db_sethash = cursor.fetchall()[0][0] obj_md5_hash = hashlib.md5(db_setvalue.encode('utf-8')) db_sethash_new = obj_md5_hash.hexdigest() if db_sethash == db_sethash_new: conn.close() logger.info("cam '" + key2 + "' on server '" + key1 + "' already has the same json!") statistic.append_info( "Камера '" + db_setname + "' уже имеет такой json!", "БД") return 0 time.sleep(0.2) sql_cmd = "UPDATE setting SET setvalue = ?, sethash = ? WHERE setid = ?" cursor.execute(sql_cmd, [db_setvalue, db_sethash_new, str(db_setid)]) conn.commit() conn.close() logger.info("db update json for '" + key2 + "' cam was executed successfully") statistic.append_info( "Обновление json камеры '" + db_setname + "' выполнено успешно!", "БД") return 0 except sqlite3.OptimizedUnicode: statistic.append_error("Ошибка выполнения команды!", "БД", True)
def ptzclient_command_simple(client: SoapClient, key2: str, token: str, statistic: Statistic, stop: bool = False, left: int = 0, right: int = 0, up: int = 0, down: int = 0, zoom_in: int = 0, zoom_out: int = 0, query_all: bool = False, go_to_preset: int = 0, set_preset: int = 0, pan_to: int = -1, tilt_to: int = -1, home: bool = False) -> dict: """ :param client: :param key2: :param token: :param statistic :param stop: :param left: :param right: :param up: :param down: :param zoom_in: :param zoom_out: :param query_all: :param go_to_preset: :param set_preset: :param pan_to: :param tilt_to: :param home: :return: """ logger = statistic.get_log().get_logger("scripts/ws/ptz") logger.info( "was called (client: SoapClient, key2: str, token: str, statistic: Statistic, stop: bool, left: int, \ right: int, up: int, down: int, zoom_in: int, zoom_out: int, query_all: bool, \ go_to_preset: int, set_preset: int, pan_to: int, tilt_to: int, home: bool)" ) logger.debug("params (client_obj, " + key2 + ", " + token + ", statistic_obj, " + str(stop) + ", " + str(left) + ", " + str(right) + ", " + str(up) + ", " + str(down) + ", " + str(zoom_in) + ", " + str(zoom_out) + ", " + str(query_all) + ", " + str(go_to_preset) + ", " + str(set_preset) + ", " + str(pan_to) + ", " + str(tilt_to) + ", " + str(home) + ")") params, sysparams, method = pattern.ptzclient_command_simple( key2, token, stop, left, right, up, down, zoom_in, zoom_out, query_all, go_to_preset, set_preset, pan_to, tilt_to, home) response = client.call_method2(method, params, sysparams, [0]) logger.info("ws method ptzclient:Command was executed successfully!") statistic.append_info("ptzclient:Command выполнен успешно", "WS_МЕТОД") return response
def compare_local_servers(string: dict, template_string: dict, local_server_ip: str, statistic: Statistic) -> bool: """Функция сравнивает значения всех ключей поля string из ws метода listener_pinger_get_local_servers с другим таким же словарем. Оба словаря должны быть равны значению ключа string из ws метода listener_pinger_get_local_servers или listener_pinger_get_down_servers :param string: строка ответа; :param template_string: эталонная строка; :param local_server_ip: ip локального сервера; :param statistic: объект класса Statistic. :return: флаг корректности. """ is_correct = True if string == template_string: return is_correct checked_keys: list = [] for template_key in template_string.keys(): if template_key not in string: statistic.append_error( template_key + " в 'string' у " + local_server_ip + "!", "НЕТ КЛЮЧА") continue checked_keys.append(template_key) if template_key == "cams": if template_string["cams"] == string["cams"]: continue if not template_string["cams"] and string["cams"]: statistic.append_warn( "Список камер у сервера " + local_server_ip + " не пустой!", "ЕСТЬ КАМЕРЫ") continue if not string["cams"] and template_string["cams"]: statistic.append_error("Сервер " + local_server_ip + "!", "НЕТ КАМЕР") is_correct = False continue # ищем камеру, которая отсутствует # либо ищем отличие в конкретных ключах у камеры # также ищем какие ключи отсутствуют в шаблоне, но есть по факту for template_cam in template_string["cams"]: # проверка на отсутствие камеры string_cam_index = tools.get_dict_by_keys_values( string["cams"], ["key2", "key3"], [template_cam["key2"], template_cam["key3"]]) if string_cam_index == -1: statistic.append_error( template_cam["key2"] + "(" + template_cam["key3"] + ") на сервере " + local_server_ip + "!", "НЕТ КАМЕРЫ") is_correct = False continue if template_cam == string["cams"][string_cam_index]: continue # поиск отличий в конкретных ключах у камеры for template_cam_key in template_cam: if template_cam_key not in string["cams"][ string_cam_index]: statistic.append_error( template_cam_key + " в " + template_cam + " на сервере " + local_server_ip + "'!", "НЕТ КЛЮЧА ПО КАМЕРЕ") is_correct = False continue current_key_value = string["cams"][string_cam_index][ template_cam_key] if template_cam[ template_cam_key] != current_key_value and template_cam_key != "Status": statistic.append_error( template_cam_key + " в камере " + template_cam["key2"] + "(" + template_cam["key3"] + ") на сервере " + local_server_ip + "!" + " Требуется: " + str(template_cam[template_cam_key]) + "(" + str(current_key_value) + ")!", "НЕВАЛИД ЗНАЧ") is_correct = False continue # поиск отсутствующих ключей в шаблоне for string_cam_key in string["cams"][string_cam_index]: if string_cam_key not in template_cam: statistic.append_warn( string_cam_key + " в камере " + template_cam + " на сервере" + local_server_ip + " в ШАБЛОНЕ!", "НЕТ КЛЮЧА") # ищем камеры, которых нет в шаблоне # но есть по факту for string_cam in string["cams"]: string_cam_is_in_template = False for template_cam in template_string["cams"]: if string_cam["key2"] == template_cam["key2"]: string_cam_is_in_template = True break if string_cam_is_in_template is False: statistic.append_warn( string_cam["key2"] + " на сервере " + local_server_ip + " в ШАБЛОНЕ!", "НЕТ КАМЕРЫ") elif template_key == "archive": if template_string[template_key] == string[template_key]: continue if not template_string[template_key] and string[template_key]: statistic.append_warn("Сервер " + local_server_ip, "ЕСТЬ АРХИВ") #is_correct = False continue if not string[template_key] and template_string[template_key]: statistic.append_error("Сервер " + local_server_ip, "НЕТ АРХИВА") is_correct = False for archive in template_string[template_key][0]: if string[template_key][0].count(archive) > 0: continue else: statistic.append_error( str(archive) + " на сервере " + local_server_ip, "НЕТ АРХИВА") is_correct = False for archive in string[template_key][0]: if template_string[template_key][0].count(archive) > 0: continue else: statistic.append_warn( str(archive) + " на сервере " + local_server_ip, "НЕТ АРХИВА") is_correct = False else: if string[template_key] != template_string[ template_key] and template_key != "down_servers": statistic.append_error( str(string[template_key]) + " по ключу" + template_key + " на сервере " + local_server_ip + "! Требуется: " + str(template_string[template_key]) + "(" + str(string[template_key]) + ")!", "НЕВАЛИД ЗНАЧ") is_correct = False return is_correct
def compare_down_servers(string: dict, template_string: dict, main_server_ip: str, statistic: Statistic) -> bool: """Функция проверки эквивалентности "нижних" серверов :param string: строка ответа; :param template_string: эталонная строка; :param main_server_ip: ip главного сервера; :param statistic: объект класса Statistic. :return: флаг корректности. """ is_correct = True if string == template_string: return is_correct if compare_local_servers(string, template_string, main_server_ip, statistic) is False: statistic.append_info( "Локальный сервер некорректен в ответе 'get_down' на сервере " + main_server_ip, "СРАВНЕНИЕ") is_correct = False if string["down_servers"] == template_string["down_servers"]: return is_correct if not string["down_servers"] and template_string["down_servers"]: statistic.append_error("Сервер " + main_server_ip + "!", "НЕТ НИЖНИХ СЕРВЕРОВ") is_correct = False if not template_string["down_servers"] and string["down_servers"]: statistic.append_warn("Сервер " + main_server_ip + "!", "ЕСТЬ НИЖНИЕ СЕРВЕРА") #is_correct = False # проход по шаблону и поиск отличий down_servers_names: list = get_down_servers_names(string["down_servers"]) for template_down_server in template_string["down_servers"]: template_down_server_name: str = list(template_down_server.keys())[0] statistic.append_info( "Сравнение нижнего сервера " + template_down_server_name + " с шаблоном...", "ИНФО") if template_down_server_name not in down_servers_names: statistic.append_error( template_down_server_name + " на сервере" + main_server_ip + "!", "НЕТ НИЖНЕГО СЕРВЕРА") is_correct = False continue down_server_index = down_servers_names.index(template_down_server_name) down_server_string = string["down_servers"][down_server_index][ template_down_server_name] down_server_template_string = template_down_server[ template_down_server_name] if compare_local_servers( down_server_string, down_server_template_string, template_down_server_name, statistic) is False: statistic.append_error( "Нижний сервер " + template_down_server_name + " не корректен!", "СРАВНЕНИЕ") is_correct = False continue # поиск отсутвующих нижних серверов в шаблоне # тобиш, которые есть по факту, но нет в шаблоне template_down_servers_names: list = get_down_servers_names( template_string["down_servers"]) for down_server_name in down_servers_names: if template_down_servers_names.count(down_server_name) == 0: statistic.append_warn( down_server_name + " на сервере " + main_server_ip + " в ШАБЛОНЕ!", "НЕТ НИЖНЕГО СЕРВЕРА") return is_correct
def get_next_cam_json(path_to_db: str, statistic: Statistic, servers: tuple, cams: tuple, profiles: tuple, previous_info: tuple) -> tuple: """Функция возвращает json следующей камеры, основываясь на предыдущей камеры из списков отфильтрованных камер для текущего сервера из списка отфильтрованных серверов. Первый запуск функции должен быть с параметрами: (путь к бд, статистика, список серверов, список камер, список профилей) :param path_to_db: путь к файлу БД; :param statistic: объект класса Statistic; :param servers: список серверов; :param cams: список камер; :param profiles: список профилей; :param previous_info: информация о предыдущей камеры (тот же контейнер, что эта функция вернула в крайний раз). :return: кортеж: 0 - json камеры, 1 - имя сервера, 2 - имя камеры, 3 - имя профиля. - если вернется {}, "", "", "", [] - то это признак окончания полного прохода - если вернется{}, имя_сервера, "", "", [] - то значит был переход на следующий сервер и список камер нужно получить новый """ logger = statistic.get_log().get_logger("scripts/common/tools") logger.info( "was called (path_to_db: str, statistic: Statistic, servers: tuple, cams: tuple, profiles: tuple," "previous_info: str)") logger.debug("params (" + path_to_db + ", stat_obj, " + str(servers) + ", " + str(cams) + ", " + str(profiles) + ", " + str(previous_info) + ")") if not previous_info: current_server = servers[0] next_cam = get_next_cam(cams, profiles) else: current_server = previous_info[1] next_cam = get_next_cam(cams, profiles, previous_cam=previous_info[2], previous_profile=previous_info[3]) if not cams: statistic.append_error( "Список отфильтрованных камер для сервера " + current_server + " пуст!", "БД") if not next_cam: index_current_server = servers.index(current_server) if index_current_server == len(servers) - 1: return () return {}, servers[index_current_server + 1], "", "" current_cam = next_cam[0] current_profile = next_cam[1] logger.info("current operation: get_cam_json") logger.info("call func db.get_cam_json(path_to_db, key1, key2, key3)") logger.debug("params (" + path_to_db + ", " + current_server + ", " + current_cam + ", " + current_profile + ")") json_cam: dict = db.get_cam_json(path_to_db, current_server, current_cam, current_profile, statistic) return json_cam, current_server, current_cam, current_profile
def check_values(names: List[str], values: list, need_values: list, operations: List[str], statistic: Statistic) -> None: """Проверка на соответствие значений требованиеям. :param names: список имен значений; :param values: список значений; :param need_values: список требований (нужных значений); :param operations: список операций для сравнения значений с требованиями; :param statistic: объект класса Statistic. """ for index, value in enumerate(values): if operations[index] == "!=": if value == need_values[index]: statistic.append_error( "'" + str(value) + "' ключ '" + names[index] + "'... требуется != " + str(need_values[index]), "НЕВАЛИД_ЗНАЧ", True) continue if operations[index] == "==": if value != need_values[index]: statistic.append_error( "'" + str(value) + "' ключ '" + names[index] + "'... требуется == " + str(need_values[index]), "НЕВАЛИД_ЗНАЧ", True) continue if operations[index] == ">": if value <= need_values[index]: statistic.append_error( "'" + str(value) + "' ключ '" + names[index] + "'... требуется > " + str(need_values[index]), "НЕВАЛИД_ЗНАЧ", True) continue if operations[index] == "<": if value >= need_values[index]: statistic.append_error( "'" + str(value) + "' ключ '" + names[index] + "'... требуется < " + str(need_values[index]), "НЕВАЛИД_ЗНАЧ", True) continue if operations[index] == ">=": if value < need_values[index]: statistic.append_error( "'" + str(value) + "' ключ '" + names[index] + "'... требуется >= " + str(need_values[index]), "НЕВАЛИД_ЗНАЧ", True) continue if operations[index] == "<=": if value > need_values[index]: statistic.append_error( "'" + str(value) + "' ключ '" + names[index] + "'... требуется <= " + str(need_values[index]), "НЕВАЛИД_ЗНАЧ", True) continue