meian
This commit is contained in:
114
meian/devices/common_model.py
Normal file
114
meian/devices/common_model.py
Normal file
@@ -0,0 +1,114 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
@File : common_model
|
||||
@Author : xuxingchen
|
||||
@Version : 1.0
|
||||
@Contact : xuxingchen@sinochem.com
|
||||
@Desc : 公用的数据结构
|
||||
"""
|
||||
import threading
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
# 错误码
|
||||
class ErrorCode(Enum):
|
||||
SUCCESS = (0, "全部完成下发")
|
||||
PART_SUCCESS = (10, "部分完成下发")
|
||||
FAILURE = (20, "一个都没下发成功")
|
||||
INPUT_TYPE_ERROR = (30, "入参数据类型异常")
|
||||
NEVER_POST_FACE = (40, "用户未下发过人脸")
|
||||
UNKNOWN = (90, "未知异常")
|
||||
|
||||
def __new__(cls, code, message):
|
||||
obj = object.__new__(cls)
|
||||
obj._value_ = code
|
||||
obj.message = message
|
||||
return obj
|
||||
|
||||
|
||||
class BaseInfo(BaseModel):
|
||||
def check(self):
|
||||
for attr in self.__dict__.keys():
|
||||
# if property can be null, default value should not be set to None
|
||||
if self.__dict__[attr] is None:
|
||||
raise ValueError(f"{attr} not allowed to be set to None")
|
||||
|
||||
|
||||
class UserData:
|
||||
def __init__(self):
|
||||
self.topic: Optional[str] = None
|
||||
self.topics: list = []
|
||||
self.table_handler = None
|
||||
self.code = None
|
||||
self.token = None
|
||||
self.status: dict = {}
|
||||
self.clients: dict = {}
|
||||
self.client_id = None
|
||||
self.lock = threading.Lock() # 添加一个锁用于线程同步
|
||||
|
||||
def set_topic(self, value: str):
|
||||
with self.lock:
|
||||
self.topic = value
|
||||
|
||||
def set_topics(self, value: list):
|
||||
with self.lock:
|
||||
self.topics = value
|
||||
|
||||
def set_table_handler(self, value):
|
||||
with self.lock:
|
||||
self.table_handler = value
|
||||
|
||||
def set_code(self, value):
|
||||
with self.lock:
|
||||
self.code = value
|
||||
|
||||
def set_token(self, value):
|
||||
with self.lock:
|
||||
self.token = value
|
||||
|
||||
def set_status_add(self, key, value):
|
||||
with self.lock:
|
||||
self.status[key] = value
|
||||
|
||||
def set_status_remove(self, key):
|
||||
with self.lock:
|
||||
if self.status and key in self.status.keys():
|
||||
self.status.pop(key)
|
||||
|
||||
def get_status(self, key):
|
||||
if self.status and key in self.status.keys():
|
||||
return self.status[key]
|
||||
|
||||
def set_clients(self, value: dict):
|
||||
with self.lock:
|
||||
self.clients = value
|
||||
|
||||
def set_client_add(self, key, value):
|
||||
with self.lock:
|
||||
self.clients[key] = value
|
||||
|
||||
def set_client_id(self, key):
|
||||
with self.lock:
|
||||
self.client_id = key
|
||||
|
||||
|
||||
class Project(BaseModel):
|
||||
project_name: str
|
||||
device_id: str
|
||||
subscribe_topic: str
|
||||
publish_topic: str
|
||||
gateway_id: str
|
||||
gateway_sct: str
|
||||
register_type: int
|
||||
|
||||
|
||||
class UserInfo(BaseModel):
|
||||
user_id: str
|
||||
device_id: str
|
||||
name: str
|
||||
user_type: int # 0:业主 1:访客
|
||||
qrcode: str = None # 当使用二维码时,该值就是美安设备对应的userid
|
||||
face_url: str = None
|
||||
730
meian/devices/common_service.py
Normal file
730
meian/devices/common_service.py
Normal file
@@ -0,0 +1,730 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
@File : common_service
|
||||
@Author : xuxingchen
|
||||
@Version : 2.0
|
||||
@Contact : xuxingchen@sinochem.com
|
||||
@Desc : 业务逻辑服务层
|
||||
"""
|
||||
import json
|
||||
import time
|
||||
import traceback
|
||||
import requests
|
||||
|
||||
import devices.meian_model as meian_data_entity
|
||||
import devices.yunfu_model as yunfu_data_entity
|
||||
|
||||
import logger
|
||||
from logger import Logger, speed_ms
|
||||
from devices.meian_db import DeviceTable, RegisterTable, GatewayTable, HeartBeatTable, UserInfoTable, RecordTable
|
||||
from devices.common_model import ErrorCode, UserData, UserInfo
|
||||
from utils import generate_token, to_obj, datetime_to_timestamp, generate_time_token, encode_to_base64, \
|
||||
extract_building_unit, extract_number
|
||||
from config import TIMEOUT_SECOND, SLEEP_TIME, BACKEND_URL, BACKEND_ID, BACKEND_SECRET, GATEWAY_CONFIG
|
||||
|
||||
|
||||
class BaseService:
|
||||
def __init__(self):
|
||||
self.table_handler = None
|
||||
self.topic = None
|
||||
self.clients = None
|
||||
|
||||
def set_meta(self, userdata: UserData):
|
||||
self.table_handler = userdata.table_handler
|
||||
self.topic = userdata.topic
|
||||
self.clients = userdata.clients
|
||||
|
||||
def handle(self, **kwargs):
|
||||
"""must be `override`"""
|
||||
pass
|
||||
|
||||
|
||||
def get_owner_house_floor(user_id, project_code: str, device_position_desc: str) -> int:
|
||||
"""根据用户ID获取用户房产信息,提取楼层信息并返回
|
||||
|
||||
1. 该功能只支持业主使用,访客查询不到,默认返回
|
||||
"""
|
||||
token_url = f"{BACKEND_URL}/v2/accesskey_auth"
|
||||
owner_info_url = f"{BACKEND_URL}/v3/realty-master-data/owners/{user_id}"
|
||||
house_info_url = f"{BACKEND_URL}/v3/realty-master-data/houses"
|
||||
|
||||
# get token
|
||||
data = {'id': BACKEND_ID, 'secret': BACKEND_SECRET}
|
||||
token_response = requests.post(token_url, json=data)
|
||||
if token_response.status_code == 200:
|
||||
logger.Logger.debug(f"token 请求成功,响应内容:{token_response.json()}")
|
||||
token = token_response.json()["access_token"]
|
||||
else:
|
||||
logger.Logger.error(f"token 请求失败,状态码: {token_response.status_code}")
|
||||
raise Exception(f"token 请求失败,状态码: {token_response.status_code}")
|
||||
# get house ids
|
||||
headers = {
|
||||
"Accept": "application/json",
|
||||
"Access-Token": token,
|
||||
"Content-Type": "application/json;charset=UTF-8"
|
||||
}
|
||||
owner_info_response = requests.get(owner_info_url, headers=headers)
|
||||
if owner_info_response.status_code == 200:
|
||||
owner_info = owner_info_response.json()
|
||||
logger.Logger.debug(f"owner_info 请求成功,响应内容:{owner_info}")
|
||||
# 根据 project_code 获取 project_id
|
||||
if GATEWAY_CONFIG[project_code][2] == "" or owner_info["data"]["project_id"] == GATEWAY_CONFIG[project_code][2]:
|
||||
house_ids = owner_info["data"]["house_ids"]
|
||||
else:
|
||||
logger.Logger.error(f"project id 不匹配,"
|
||||
f"预置:{GATEWAY_CONFIG[project_code][2]}, 用户信息所属:{owner_info['data']['project_id']}")
|
||||
raise Exception(f"project id 不匹配")
|
||||
elif owner_info_response.status_code == 404 and owner_info_response.json()["msg"] == "OWNER_NOT_EXISTS":
|
||||
logger.Logger.warn(f"owner_info 请求成功,但用户ID不存在,非住户")
|
||||
return 1
|
||||
else:
|
||||
logger.Logger.error(f"token 请求失败,状态码: {token_response.status_code}")
|
||||
raise Exception(f"token 请求失败,状态码: {token_response.status_code}")
|
||||
|
||||
# 结合设备注册备注 获取楼层信息
|
||||
data = {
|
||||
"query": {
|
||||
"id": {"$in": house_ids}
|
||||
}
|
||||
}
|
||||
house_info_response = requests.post(house_info_url, json=data, headers=headers)
|
||||
if house_info_response.status_code == 200:
|
||||
logger.Logger.debug(f"house_info 请求成功,响应内容:{house_info_response.json()}")
|
||||
building_number, unit_number = extract_building_unit(device_position_desc)
|
||||
house_info = house_info_response.json()["data"]["list"]
|
||||
floor = 1
|
||||
for house_item in house_info:
|
||||
building_name = extract_number(house_item["building_name"]).zfill(2)
|
||||
unit_name = extract_number(house_item["unit_name"]).zfill(2)
|
||||
if building_name == building_number and unit_name == unit_number:
|
||||
floor = int(house_item["floor_name"])
|
||||
break
|
||||
else:
|
||||
logger.Logger.error(f"house_info 请求失败,状态码: {token_response.status_code}")
|
||||
raise Exception(f"house_info 请求失败,状态码: {token_response.status_code}")
|
||||
|
||||
return floor
|
||||
|
||||
|
||||
class Services:
|
||||
"""业务逻辑入口"""
|
||||
|
||||
class HeartBeatService(BaseService):
|
||||
def handle(self, msg_obj: meian_data_entity.HeartBeat):
|
||||
# Logger.debug("设备心跳已接收,正在执行上线 ...", log_path=None)
|
||||
last_time = HeartBeatTable.get_last_time(self.table_handler, msg_obj.device_id)
|
||||
# 若无心跳记录或者距离上次心跳记录时间达到5分钟
|
||||
if (last_time is None or
|
||||
(time.time() - float(
|
||||
HeartBeatTable.get_last_time(self.table_handler, msg_obj.device_id))) > (60. * 5.)):
|
||||
status = HeartBeatTable.update(self.table_handler, msg_obj, self.topic)
|
||||
if status:
|
||||
# 执行上线操作
|
||||
project_code = DeviceTable.get_project_code(self.table_handler, msg_obj.device_id)
|
||||
if project_code:
|
||||
gateway_id, _ = GatewayTable.get_gateway(self.table_handler, project_code)
|
||||
if gateway_id is None:
|
||||
Logger.error("网关不存在")
|
||||
return -1
|
||||
aiot_id = DeviceTable.get_aiot_id(self.table_handler, msg_obj.device_id)
|
||||
if aiot_id is None:
|
||||
Logger.warn("设备未注册,不执行上线操作")
|
||||
return -1
|
||||
topic = f"/jmlink/{aiot_id}/comm/online"
|
||||
topic_resp = f"/jmlink/{aiot_id}/comm/online_resp"
|
||||
s = time.time()
|
||||
# 获取客户端
|
||||
client_1 = self.clients[gateway_id][0]
|
||||
userdata = self.clients[gateway_id][1]
|
||||
userdata.set_status_add("status", False)
|
||||
userdata.set_status_add("start_timestamp", time.time())
|
||||
try:
|
||||
# 订阅回传
|
||||
client_1.subscribe(topic_resp)
|
||||
Logger.debug(f"subscribe topics: {topic_resp}")
|
||||
# 发布事件
|
||||
token = generate_token()
|
||||
userdata.set_token(token)
|
||||
online_json = json.dumps(yunfu_data_entity.Online(messageId=token).__dict__)
|
||||
client_1.publish(topic, online_json)
|
||||
while True:
|
||||
if userdata.status["status"]:
|
||||
# 拿到结果后写入到数据库中更新数据
|
||||
if "code" in userdata.status["response"].keys():
|
||||
error_code = userdata.status["response"]["code"]
|
||||
if error_code == 0:
|
||||
Logger.info(f"设备 - {aiot_id} 完成上线")
|
||||
else:
|
||||
Logger.error(
|
||||
f"{topic_resp} 返回错误码: {error_code}, "
|
||||
f"{yunfu_data_entity.error_code[error_code]}")
|
||||
break
|
||||
if time.time() - userdata.status["start_timestamp"] > TIMEOUT_SECOND: # 超时跳出
|
||||
Logger.debug(
|
||||
f"等待回复超时: {speed_ms(userdata.status['start_timestamp'])}ms")
|
||||
break
|
||||
# Logger.debug(f"{userdata.status} waiting for {topic_resp} ... ")
|
||||
time.sleep(SLEEP_TIME)
|
||||
finally:
|
||||
# 属性复位
|
||||
client_1.unsubscribe(topic_resp)
|
||||
# Logger.debug(f"移除订阅: {topic_resp}")
|
||||
userdata.set_token(None)
|
||||
userdata.set_status_remove("response")
|
||||
# Logger.debug(f"{speed_ms(s)}ms")
|
||||
else:
|
||||
Logger.warn("上线请求非法,默认无操作")
|
||||
else:
|
||||
Logger.debug("未达到重新上线阈值,默认无操作")
|
||||
pass
|
||||
|
||||
class RegisterService(BaseService):
|
||||
def handle(self, msg_obj: meian_data_entity.Register):
|
||||
Logger.debug("RegisterService Handle ...")
|
||||
status = RegisterTable.update(self.table_handler, msg_obj, self.topic)
|
||||
if status:
|
||||
if not DeviceTable.get_device_register_type(self.table_handler, msg_obj.device_id):
|
||||
Logger.debug("Execute register ...")
|
||||
project_code = DeviceTable.get_project_code(self.table_handler, msg_obj.device_id)
|
||||
if project_code:
|
||||
gateway_id, _ = GatewayTable.get_gateway(self.table_handler, project_code)
|
||||
if gateway_id is None:
|
||||
Logger.error("网关不存在")
|
||||
return -1
|
||||
topic = f"/jmlink/{gateway_id}/comm/sub/register"
|
||||
topic_resp = f"/jmlink/{gateway_id}/comm/sub/register_resp"
|
||||
# 将连接信息(网关&网关secret)和回传信息发送给回传监听进程
|
||||
s = time.time()
|
||||
# 获取客户端
|
||||
client_1 = self.clients[gateway_id][0]
|
||||
userdata = self.clients[gateway_id][1]
|
||||
userdata.set_status_add("status", False)
|
||||
userdata.set_status_add("start_timestamp", time.time())
|
||||
try:
|
||||
# 订阅回传
|
||||
client_1.subscribe(topic_resp)
|
||||
Logger.debug(f"subscribe topics: {topic_resp}")
|
||||
# 发布事件
|
||||
token = generate_token()
|
||||
userdata.set_token(token)
|
||||
register_json = json.dumps(yunfu_data_entity.CommRegister(
|
||||
messageId=token,
|
||||
params=yunfu_data_entity.RegisterParam(
|
||||
deviceName=msg_obj.device_id,
|
||||
displayName=f"美安-{msg_obj.device_id}-{msg_obj.device_position_desc}"
|
||||
).__dict__
|
||||
).__dict__)
|
||||
Logger.debug(register_json)
|
||||
client_1.publish(topic, register_json)
|
||||
while True:
|
||||
if userdata.status["status"]:
|
||||
# 拿到结果后写入到数据库中更新数据
|
||||
if "data" in userdata.status["response"].keys():
|
||||
resp = userdata.status["response"]["data"][0]
|
||||
if resp["code"] == 0:
|
||||
DeviceTable.update_aiot_id(self.table_handler, resp["deviceName"],
|
||||
resp["deviceId"])
|
||||
Logger.info(f"设备 - {msg_obj.device_id} 完成注册")
|
||||
|
||||
# 每次完成注册后刷新订阅信息
|
||||
Logger.debug(f"刷新订阅列表中 ...")
|
||||
sub_aiot_id_list = json.loads(
|
||||
GatewayTable.get_registered_sub_aiot_id(self.table_handler, gateway_id)[
|
||||
0])
|
||||
current_topics = set(
|
||||
[f"/jmlink/{aiot_id}/tml/service/call" for aiot_id in sub_aiot_id_list])
|
||||
last_topics = set(userdata.topics)
|
||||
topics_to_subscribe = current_topics - last_topics
|
||||
topics_to_unsubscribe = last_topics - current_topics
|
||||
if topics_to_subscribe or topics_to_unsubscribe:
|
||||
# 订阅缺少的主题
|
||||
for topic in list(topics_to_subscribe):
|
||||
client_1.subscribe(topic)
|
||||
Logger.debug(f"新增订阅: {topic}")
|
||||
# 取消订阅多余的主题
|
||||
for topic in list(topics_to_unsubscribe):
|
||||
client_1.unsubscribe(topic)
|
||||
Logger.debug(f"移除订阅: {topic}")
|
||||
else:
|
||||
Logger.debug(f"订阅列表无变化")
|
||||
Logger.debug(f"订阅列表刷新完成 ✅")
|
||||
else:
|
||||
Logger.error(
|
||||
f"{topic_resp} 返回错误码: {resp['code']}, "
|
||||
f"{yunfu_data_entity.error_code[resp['code']]}")
|
||||
break
|
||||
if time.time() - userdata.status["start_timestamp"] > TIMEOUT_SECOND: # 超时跳出
|
||||
Logger.debug(
|
||||
f"等待回复超时: {speed_ms(userdata.status['start_timestamp'])}ms")
|
||||
break
|
||||
# Logger.debug(f"{userdata.status} waiting for {topic_resp} ... ")
|
||||
time.sleep(SLEEP_TIME) # 实际运行中设为0.1,100ms检测一次
|
||||
finally:
|
||||
client_1.unsubscribe(topic_resp)
|
||||
Logger.debug(f"移除订阅: {topic_resp}")
|
||||
userdata.set_token(None)
|
||||
userdata.set_status_remove("response")
|
||||
Logger.debug(f"{speed_ms(s)}ms")
|
||||
else:
|
||||
Logger.error("网关配置表中不存在设备关联的项目信息")
|
||||
else:
|
||||
Logger.debug("设备已注册过,默认无操作")
|
||||
|
||||
class PushRtAccessRecordService(BaseService):
|
||||
def handle(self, msg_obj: meian_data_entity.PushRtAccessRecord):
|
||||
Logger.debug("Execute open_event ...")
|
||||
# 若存在通行记录,但人员信息表中不存在人员信息,则将通行记录本地保存留用
|
||||
if msg_obj.user_id.startswith("old-"): # 历史遗留在设备中的人员id应当以old-开头进行插入
|
||||
try:
|
||||
RecordTable.add(self.table_handler, msg_obj)
|
||||
Logger.info("通行事件本地记录成功")
|
||||
return 1
|
||||
except Exception as e:
|
||||
Logger.error(f"{type(e).__name__}, {e}")
|
||||
if logger.DEBUG:
|
||||
traceback.print_exc()
|
||||
# 查询子设备ID
|
||||
aiot_id = DeviceTable.get_aiot_id(self.table_handler, msg_obj.device_id)
|
||||
if aiot_id is None:
|
||||
Logger.warn(f"Device - {aiot_id} is not registered")
|
||||
return -1
|
||||
topic = f"/jmlink/{aiot_id}/tml/event/post"
|
||||
topic_resp = f"/jmlink/{aiot_id}/tml/event/post_resp"
|
||||
project_code = DeviceTable.get_project_code(self.table_handler, msg_obj.device_id)
|
||||
if project_code:
|
||||
gateway_id, _ = GatewayTable.get_gateway(self.table_handler, project_code)
|
||||
# 将连接信息(网关&网关secret)和回传信息发送给回传监听进程
|
||||
s = time.time()
|
||||
# 获取客户端
|
||||
client_1 = self.clients[gateway_id][0]
|
||||
userdata = self.clients[gateway_id][1]
|
||||
userdata.set_status_add("status", False)
|
||||
userdata.set_status_add("start_timestamp", time.time())
|
||||
# 订阅回传
|
||||
client_1.subscribe(topic_resp)
|
||||
Logger.debug(f"subscribe topics: {topic_resp}")
|
||||
try:
|
||||
# 数据转换
|
||||
open_event = yunfu_data_entity.OpenEvent()
|
||||
open_event.datetime = str(datetime_to_timestamp(msg_obj.time))
|
||||
open_event.type = yunfu_data_entity.OpenEventType.__dict__[msg_obj.access_mode.upper()].value
|
||||
open_event.certificate_type = yunfu_data_entity.OpenEventCertificateType.__dict__[
|
||||
msg_obj.access_mode.upper()].value
|
||||
# 若是二维码通信,根据二维码获取用户信息,若是人脸通信,根据userid获取用户信息
|
||||
if msg_obj.access_mode == "qrCode":
|
||||
user = UserInfoTable.get_user_by_qrcode(self.table_handler, msg_obj.user_id, msg_obj.device_id)
|
||||
open_event.code = user[0]
|
||||
open_event.certificate = user[0]
|
||||
name = user[1]
|
||||
else:
|
||||
name = UserInfoTable.get_name(self.table_handler, msg_obj.user_id, msg_obj.device_id)
|
||||
open_event.code = msg_obj.user_id
|
||||
open_event.certificate = msg_obj.user_id
|
||||
open_event.name = name
|
||||
open_event.check()
|
||||
token = generate_token()
|
||||
userdata.set_token(token)
|
||||
event_json = json.dumps(yunfu_data_entity.EventPost(
|
||||
messageId=token, eventCode="open_event",
|
||||
params=open_event.__dict__
|
||||
).__dict__)
|
||||
# 发布事件
|
||||
client_1.publish(topic, event_json)
|
||||
# 等待回传
|
||||
while True:
|
||||
if userdata.status["status"]:
|
||||
if "code" in userdata.status["response"].keys():
|
||||
if userdata.status["response"]["code"] != 0:
|
||||
error_code = userdata.status['response']['errorCode']
|
||||
Logger.error(f"{topic_resp} 返回错误码: {error_code}, "
|
||||
f"{meian_data_entity.error_code[error_code]}")
|
||||
else:
|
||||
Logger.debug("通行事件推送成功")
|
||||
break
|
||||
if time.time() - userdata.status["start_timestamp"] > TIMEOUT_SECOND: # 超时跳出
|
||||
Logger.error("等待回复超时")
|
||||
break
|
||||
# Logger.debug(f"{userdata.status} waiting for {topic_resp} ... ")
|
||||
time.sleep(SLEEP_TIME)
|
||||
finally:
|
||||
# 属性复位
|
||||
client_1.unsubscribe(topic_resp)
|
||||
Logger.debug(f"移除订阅: {topic_resp}")
|
||||
userdata.set_token(None)
|
||||
userdata.set_status_remove("response")
|
||||
Logger.debug(f"{speed_ms(s)}ms")
|
||||
|
||||
class GetQrCodeService(BaseService):
|
||||
"""获取访客二维码
|
||||
|
||||
1. 将接受的用户信息记录追加或更新到本地DB
|
||||
2. 根据时间戳和访客id生成一个token字符串,作为唯一识别id
|
||||
3. 将生成的二维码下置给指定设备
|
||||
4. 将唯一识别id作为二维码字符串返回给平台
|
||||
"""
|
||||
|
||||
def handle(self, msg: dict):
|
||||
Logger.debug("Execute Get QrCode ...")
|
||||
code = ErrorCode.SUCCESS
|
||||
device_id = DeviceTable.get_device_id(self.table_handler, self.topic.split("/tml")[0].split("link/")[1])
|
||||
project_code = DeviceTable.get_project_code(self.table_handler, device_id)
|
||||
gateway_id, _ = GatewayTable.get_gateway(self.table_handler, project_code)
|
||||
success_aiot_list = []
|
||||
aiot_ids = []
|
||||
qrcode = None
|
||||
try:
|
||||
# 根据时间戳和访客id生成一个token字符串,作为唯一识别id
|
||||
qrcode = generate_time_token(msg["params"]["user_id"])
|
||||
# 将生成的二维码下置给指定设备
|
||||
aiot_ids = json.loads(msg["params"]["device_ids"])
|
||||
s = time.time()
|
||||
for aiot_id in aiot_ids: # 一般情况 aiot_ids 里应该只有一个值
|
||||
# 查询对应aiot_id实际的美安设备id和订阅主题
|
||||
device_id = DeviceTable.get_device_id(self.table_handler, aiot_id)
|
||||
if device_id is None:
|
||||
continue
|
||||
|
||||
# 将接受的用户信息记录追加或更新到本地DB
|
||||
user_type = 0 if msg["serviceCode"] != "get_visitor_qrcode" else 1 # 业主0 访客1,身份类型在插入后就不会再被更新
|
||||
device_position_desc = RegisterTable.get_device_position_desc(self.table_handler, device_id)
|
||||
if user_type == 1 or device_position_desc is None:
|
||||
floor = 1
|
||||
else:
|
||||
floor = get_owner_house_floor(msg["params"]["user_id"], project_code, device_position_desc)
|
||||
user_info = UserInfo(
|
||||
user_id=msg["params"]["user_id"],
|
||||
device_id=device_id,
|
||||
name=msg["params"]["name"],
|
||||
user_type=user_type
|
||||
)
|
||||
UserInfoTable.update(self.table_handler, user_info)
|
||||
|
||||
topic, topic_resp = DeviceTable.get_device_topic(self.table_handler, device_id)
|
||||
# 获取客户端
|
||||
client_0 = self.clients["center"][0]
|
||||
userdata_0 = self.clients["center"][1]
|
||||
userdata_0.set_status_add("status", False)
|
||||
userdata_0.set_status_add("start_timestamp", time.time())
|
||||
try:
|
||||
Logger.debug(f"subscribe topics: {topic_resp}")
|
||||
# 构建消息体
|
||||
token = generate_token() # 正式运行应使用 generate_token()
|
||||
userdata_0.set_token(token)
|
||||
userdata_0.set_code("qrCodeDownState")
|
||||
qrcode_json = json.dumps(meian_data_entity.QrCodeInfo(
|
||||
dataType="qrCodeInfo",
|
||||
deviceId=device_id,
|
||||
token=token,
|
||||
userId=qrcode,
|
||||
qrCode=encode_to_base64(qrcode),
|
||||
floor=floor
|
||||
).__dict__)
|
||||
# 发布事件
|
||||
client_0.publish(topic, qrcode_json)
|
||||
# 等待回传
|
||||
while True:
|
||||
if userdata_0.status["status"]:
|
||||
if "errorCode" in userdata_0.status["response"].keys():
|
||||
if userdata_0.status["response"]["errorCode"] != 0:
|
||||
error_code = userdata_0.status['response']['errorCode']
|
||||
Logger.error(f"{topic_resp} 返回错误码: {error_code}, "
|
||||
f"{meian_data_entity.error_code[error_code]}")
|
||||
else:
|
||||
Logger.info(f"设备 - {device_id} 二维码 - {qrcode} 完成下置")
|
||||
UserInfoTable.update_qrcode(self.table_handler,
|
||||
user_info.user_id, device_id, qrcode)
|
||||
success_aiot_list.append(aiot_id)
|
||||
break
|
||||
if time.time() - userdata_0.status["start_timestamp"] > TIMEOUT_SECOND: # 超时跳出
|
||||
Logger.error("等待回复超时")
|
||||
break
|
||||
# Logger.debug(f"{userdata_0.status} waiting for {topic_resp} ... ")
|
||||
time.sleep(SLEEP_TIME)
|
||||
finally:
|
||||
userdata_0.set_token(None)
|
||||
userdata_0.set_code(None)
|
||||
userdata_0.set_status_remove("response")
|
||||
Logger.debug(f"{speed_ms(s)}ms")
|
||||
except Exception as e:
|
||||
Logger.error(f"{type(e).__name__}, {e}")
|
||||
if type(e).__name__ == "TypeError":
|
||||
code = ErrorCode.INPUT_TYPE_ERROR
|
||||
else:
|
||||
code = ErrorCode.UNKNOWN
|
||||
|
||||
# 将唯一识别id作为二维码字符串返回给平台
|
||||
# 获取客户端
|
||||
client_1 = self.clients[gateway_id][0]
|
||||
userdata = self.clients[gateway_id][1]
|
||||
userdata.set_status_add("status", False)
|
||||
userdata.set_status_add("start_timestamp", time.time())
|
||||
try:
|
||||
if code == ErrorCode.SUCCESS:
|
||||
if set(success_aiot_list) == set(aiot_ids): # 全部完成下发
|
||||
Logger.debug("指令完成下发")
|
||||
elif success_aiot_list: # 完成一部分下发
|
||||
code = ErrorCode.PART_SUCCESS
|
||||
Logger.warn(f"部分成功下发: {success_aiot_list}")
|
||||
UserInfoTable.update_qrcode(self.table_handler, msg["params"]["user_id"], project_code, qrcode)
|
||||
elif len(success_aiot_list) == 0: # 一个都没下发
|
||||
code = ErrorCode.FAILURE
|
||||
Logger.error("无指令完成下发")
|
||||
aiot_topic = self.topic + "_resp"
|
||||
return_json = json.dumps(yunfu_data_entity.EventPostResp(
|
||||
messageId=msg["messageId"],
|
||||
requestTime=msg["time"],
|
||||
serviceCode=msg["serviceCode"],
|
||||
data=yunfu_data_entity.QrCodeResp(code=code.value, message=code.message, qrcode=qrcode).__dict__
|
||||
).__dict__)
|
||||
client_1.publish(aiot_topic, return_json)
|
||||
finally:
|
||||
userdata.set_token(None)
|
||||
userdata.set_status_remove("response")
|
||||
|
||||
class AddFaceService(BaseService):
|
||||
def handle(self, msg: dict):
|
||||
Logger.debug("Execute Add Face ...")
|
||||
code = ErrorCode.SUCCESS
|
||||
device_id = DeviceTable.get_device_id(self.table_handler, self.topic.split("/tml")[0].split("link/")[1])
|
||||
project_code = DeviceTable.get_project_code(self.table_handler, device_id)
|
||||
gateway_id, _ = GatewayTable.get_gateway(self.table_handler, project_code)
|
||||
success_aiot_list = []
|
||||
aiot_ids = json.loads(msg["params"]["device_ids"])
|
||||
user_id = msg["params"]["user_id"]
|
||||
face_url = f"{msg['params']['face_url']}?x-oss-process=image/resize,m_pad,h_960,w_540,color_FFFFFF"
|
||||
try:
|
||||
# 将人脸下置给指定设备
|
||||
s = time.time()
|
||||
for aiot_id in aiot_ids:
|
||||
# 查询对应aiot_id实际的美安设备id和订阅主题
|
||||
device_id = DeviceTable.get_device_id(self.table_handler, aiot_id)
|
||||
if device_id is None:
|
||||
continue
|
||||
|
||||
# 将接受的用户信息记录追加或更新到本地DB
|
||||
user_info = UserInfo(
|
||||
user_id=user_id,
|
||||
device_id=device_id,
|
||||
name=msg["params"]["name"],
|
||||
user_type=0
|
||||
)
|
||||
UserInfoTable.update(self.table_handler, user_info)
|
||||
|
||||
topic, topic_resp = DeviceTable.get_device_topic(self.table_handler, device_id)
|
||||
# 获取客户端
|
||||
client_0 = self.clients["center"][0]
|
||||
userdata_0 = self.clients["center"][1]
|
||||
userdata_0.set_status_add("status", False)
|
||||
userdata_0.set_status_add("start_timestamp", time.time())
|
||||
try:
|
||||
Logger.debug(f"subscribe topics: {topic_resp}")
|
||||
# 构建消息体
|
||||
token = generate_token() # 正式运行应使用 generate_token()
|
||||
userdata_0.set_token(token)
|
||||
userdata_0.set_code("faceDownState")
|
||||
device_position_desc = RegisterTable.get_device_position_desc(self.table_handler, device_id)
|
||||
if device_position_desc is None:
|
||||
floor = 1
|
||||
else:
|
||||
floor = get_owner_house_floor(user_id, project_code, device_position_desc)
|
||||
face_json = json.dumps(meian_data_entity.FaceInfo(
|
||||
dataType="faceInfo",
|
||||
deviceId=device_id,
|
||||
token=token,
|
||||
userId=user_id,
|
||||
faceUrl=face_url,
|
||||
floor=floor
|
||||
).__dict__)
|
||||
# 发布事件
|
||||
client_0.publish(topic, face_json)
|
||||
Logger.debug(f"{face_json} ==> {topic}")
|
||||
# 等待回传
|
||||
while True:
|
||||
if userdata_0.status["status"]:
|
||||
if "errorCode" in userdata_0.status["response"].keys():
|
||||
if userdata_0.status["response"]["errorCode"] != 0:
|
||||
error_code = userdata_0.status['response']['errorCode']
|
||||
Logger.error(f"{topic_resp} 返回错误码: {error_code}, "
|
||||
f"{meian_data_entity.error_code[error_code]}")
|
||||
else:
|
||||
Logger.info(f"设备 - {device_id} 用户({user_id}) 人脸完成下置")
|
||||
UserInfoTable.update_face_url(self.table_handler, user_id, device_id, face_url)
|
||||
success_aiot_list.append(aiot_id)
|
||||
break
|
||||
if time.time() - userdata_0.status["start_timestamp"] > TIMEOUT_SECOND: # 超时跳出
|
||||
Logger.error("等待回复超时")
|
||||
break
|
||||
# Logger.debug(f"{userdata_0.status} waiting for {topic_resp} ... ")
|
||||
time.sleep(SLEEP_TIME)
|
||||
finally:
|
||||
userdata_0.set_token(None)
|
||||
userdata_0.set_code(None)
|
||||
userdata_0.set_status_remove("response")
|
||||
Logger.debug(f"{speed_ms(s)}ms")
|
||||
except Exception as e:
|
||||
Logger.error(f"{type(e).__name__}, {e}")
|
||||
if type(e).__name__ == "TypeError":
|
||||
code = ErrorCode.INPUT_TYPE_ERROR
|
||||
else:
|
||||
code = ErrorCode.UNKNOWN
|
||||
|
||||
# 将下置结果返回给平台
|
||||
# 获取客户端
|
||||
client_1 = self.clients[gateway_id][0]
|
||||
userdata = self.clients[gateway_id][1]
|
||||
userdata.set_status_add("status", False)
|
||||
userdata.set_status_add("start_timestamp", time.time())
|
||||
try:
|
||||
if code == ErrorCode.SUCCESS:
|
||||
if set(success_aiot_list) == set(aiot_ids): # 全部完成下发
|
||||
Logger.info("指令完成下发")
|
||||
elif success_aiot_list: # 完成一部分下发
|
||||
code = ErrorCode.PART_SUCCESS
|
||||
Logger.warn(f"部分成功下发: {success_aiot_list}")
|
||||
else: # 一个都没下发
|
||||
code = ErrorCode.FAILURE
|
||||
Logger.error("无指令完成下发")
|
||||
aiot_topic = self.topic + "_resp"
|
||||
return_json = json.dumps(yunfu_data_entity.EventPostResp(
|
||||
messageId=msg["messageId"],
|
||||
requestTime=msg["time"],
|
||||
serviceCode=msg["serviceCode"],
|
||||
data=yunfu_data_entity.AddFaceResp(code=code.value, message=code.message).__dict__
|
||||
).__dict__)
|
||||
client_1.publish(aiot_topic, return_json)
|
||||
finally:
|
||||
userdata.set_token(None)
|
||||
userdata.set_status_remove("response")
|
||||
|
||||
class DeleteFaceService(BaseService):
|
||||
def handle(self, msg: dict):
|
||||
Logger.debug("Execute Delete Face ...")
|
||||
code = ErrorCode.SUCCESS
|
||||
device_id = DeviceTable.get_device_id(self.table_handler, self.topic.split("/tml")[0].split("link/")[1])
|
||||
project_code = DeviceTable.get_project_code(self.table_handler, device_id)
|
||||
gateway_id, _ = GatewayTable.get_gateway(self.table_handler, project_code)
|
||||
success_aiot_list = []
|
||||
aiot_ids = json.loads(msg["params"]["device_ids"])
|
||||
user_id = msg["params"]["user_id"]
|
||||
try:
|
||||
s = time.time()
|
||||
# 将人脸删除指令下发指定设备
|
||||
for aiot_id in aiot_ids:
|
||||
# 查询对应aiot_id实际的美安设备id和订阅主题
|
||||
device_id = DeviceTable.get_device_id(self.table_handler, aiot_id)
|
||||
if device_id is None:
|
||||
continue
|
||||
# 本地DB判断人脸信息是否存在
|
||||
if UserInfoTable.exists_face_url(self.table_handler, user_id, device_id):
|
||||
topic, topic_resp = DeviceTable.get_device_topic(self.table_handler, device_id)
|
||||
# 获取客户端
|
||||
client_0 = self.clients["center"][0]
|
||||
userdata_0 = self.clients["center"][1]
|
||||
userdata_0.set_status_add("status", False)
|
||||
userdata_0.set_status_add("start_timestamp", time.time())
|
||||
try:
|
||||
Logger.debug(f"subscribe topics: {topic_resp}")
|
||||
# 构建消息体
|
||||
token = generate_token() # 正式运行应使用 generate_token()
|
||||
userdata_0.set_token(token)
|
||||
userdata_0.set_code("deleteUserState")
|
||||
face_json = json.dumps(meian_data_entity.DeleteUser(
|
||||
dataType="deleteUser",
|
||||
deviceId=device_id,
|
||||
token=token,
|
||||
userId=user_id
|
||||
).__dict__)
|
||||
# 发布事件
|
||||
client_0.publish(topic, face_json)
|
||||
# 等待回传
|
||||
while True:
|
||||
if userdata_0.status["status"]:
|
||||
if "errorCode" in userdata_0.status["response"].keys():
|
||||
if userdata_0.status["response"]["errorCode"] != 0:
|
||||
error_code = userdata_0.status['response']['errorCode']
|
||||
Logger.error(f"{topic_resp} 返回错误码: {error_code}, "
|
||||
f"{meian_data_entity.error_code[error_code]}")
|
||||
else:
|
||||
Logger.info(f"设备 - {device_id} 移除用户({user_id})人脸")
|
||||
UserInfoTable.update_face_url(self.table_handler, user_id, device_id, None)
|
||||
success_aiot_list.append(aiot_id)
|
||||
break
|
||||
if time.time() - userdata_0.status["start_timestamp"] > TIMEOUT_SECOND: # 超时跳出
|
||||
Logger.error("等待回复超时")
|
||||
break
|
||||
# Logger.debug(f"{userdata_0.status} waiting for {topic_resp} ... ")
|
||||
time.sleep(SLEEP_TIME)
|
||||
finally:
|
||||
userdata_0.set_token(None)
|
||||
userdata_0.set_code(None)
|
||||
userdata_0.set_status_remove("response")
|
||||
else:
|
||||
code = ErrorCode.NEVER_POST_FACE
|
||||
Logger.warn(f"本地数据库中未发现下发过人脸信息,默认无操作")
|
||||
Logger.debug(f"{speed_ms(s)}ms")
|
||||
except Exception as e:
|
||||
Logger.error(f"{type(e).__name__}, {e}")
|
||||
if type(e).__name__ == "TypeError":
|
||||
code = ErrorCode.INPUT_TYPE_ERROR
|
||||
else:
|
||||
code = ErrorCode.UNKNOWN
|
||||
|
||||
# 将下置结果返回给平台
|
||||
# 获取客户端
|
||||
client_1 = self.clients[gateway_id][0]
|
||||
userdata = self.clients[gateway_id][1]
|
||||
userdata.set_status_add("status", False)
|
||||
userdata.set_status_add("start_timestamp", time.time())
|
||||
try:
|
||||
if code == ErrorCode.SUCCESS:
|
||||
if set(success_aiot_list) == set(aiot_ids): # 全部完成下发
|
||||
Logger.info("指令完成下发")
|
||||
elif success_aiot_list: # 完成一部分下发
|
||||
code = ErrorCode.PART_SUCCESS
|
||||
Logger.warn(f"部分成功下发: {success_aiot_list}")
|
||||
else: # 一个都没下发
|
||||
code = ErrorCode.FAILURE
|
||||
Logger.error("无指令完成下发")
|
||||
aiot_topic = self.topic + "_resp"
|
||||
return_json = json.dumps(yunfu_data_entity.EventPostResp(
|
||||
messageId=msg["messageId"],
|
||||
requestTime=msg["time"],
|
||||
serviceCode=msg["serviceCode"],
|
||||
data=yunfu_data_entity.DelFaceResp(code=code.value, message=code.message).__dict__
|
||||
).__dict__)
|
||||
client_1.publish(aiot_topic, return_json)
|
||||
finally:
|
||||
userdata.set_token(None)
|
||||
userdata.set_status_remove("response")
|
||||
|
||||
|
||||
def auto_service(msg: dict, userdata: UserData):
|
||||
"""跟据dataType自动化加载不同的服务层"""
|
||||
if "dataType" in msg.keys():
|
||||
entity_name = msg["dataType"][0].upper() + msg["dataType"][1:]
|
||||
if entity_name in meian_data_entity.__dict__.keys():
|
||||
Logger.debug(f"Target service name: {entity_name}")
|
||||
# 转为服务层数据实体
|
||||
entity_type = meian_data_entity.__dict__[entity_name]
|
||||
msg_obj = to_obj(msg, entity_type)
|
||||
# 构建服务层实体并进行调用
|
||||
service_name = entity_name + "Service"
|
||||
if service_name in Services.__dict__.keys():
|
||||
servicer = Services.__dict__[service_name]()
|
||||
servicer.set_meta(userdata)
|
||||
servicer.handle(msg_obj)
|
||||
elif "serviceCode" in msg.keys() and "params" in msg.keys(): # 校验是aiot平台服务调用下发
|
||||
service_map = {
|
||||
"get_owner_qrcode": "GetQrCodeService",
|
||||
"get_visitor_qrcode": "GetQrCodeService",
|
||||
"add_face": "AddFaceService",
|
||||
"del_face": "DeleteFaceService"
|
||||
}
|
||||
# 构建服务层实体并进行调用
|
||||
if msg["serviceCode"] in service_map.keys():
|
||||
service_name = service_map[msg["serviceCode"]]
|
||||
servicer = Services.__dict__[service_name]()
|
||||
servicer.set_meta(userdata)
|
||||
servicer.handle(msg)
|
||||
else:
|
||||
pass
|
||||
702
meian/devices/meian_db.py
Normal file
702
meian/devices/meian_db.py
Normal file
@@ -0,0 +1,702 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
@File : meian_db
|
||||
@Author : xuxingchen
|
||||
@Version : 1.0
|
||||
@Contact : xuxingchen@sinochem.com
|
||||
@Desc : meian database crud
|
||||
"""
|
||||
import csv
|
||||
import json
|
||||
import os.path
|
||||
import sqlite3
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from logger import Logger, new_dc
|
||||
from datetime import datetime
|
||||
from devices.meian_model import HeartBeat, Register, PushRtAccessRecord
|
||||
from devices.common_model import UserInfo
|
||||
from utils import datetime_to_timestamp
|
||||
from config import ENV_TYPE, DEBUG
|
||||
|
||||
|
||||
class SQLiteDatabaseEngine:
|
||||
def __init__(self, db_path: str = "demo.db") -> None:
|
||||
self.sqlite3 = sqlite3
|
||||
self.db_path = db_path
|
||||
self.connection = None
|
||||
self.cursor = None
|
||||
self.connect()
|
||||
|
||||
def __del__(self):
|
||||
# self.disconnect()
|
||||
pass
|
||||
|
||||
def connect(self):
|
||||
"""连接SQLite 数据库(如果数据库不存在则会自动创建)"""
|
||||
self.connection = self.sqlite3.connect(self.db_path)
|
||||
self.cursor = self.connection.cursor()
|
||||
Logger.init(new_dc(f"🔗 SQLite - {self.db_path} has connect successfully! 🔗", "[1;32m"))
|
||||
|
||||
def disconnect(self):
|
||||
try:
|
||||
self.cursor.close()
|
||||
self.connection.close()
|
||||
except Exception as e:
|
||||
if type(e).__name__ != "ProgrammingError":
|
||||
Logger.error(f"{type(e).__name__}, {e}")
|
||||
Logger.info(new_dc(f"🔌 Disconnect from SQLite - {self.db_path}! 🔌", "[1m"))
|
||||
|
||||
def exist(self, table_name):
|
||||
self.cursor.execute(
|
||||
f"SELECT name FROM sqlite_master WHERE type='table' AND name=?",
|
||||
(table_name,),
|
||||
)
|
||||
result = self.cursor.fetchone()
|
||||
if result:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class BaseTable:
|
||||
def __init__(self, connection=None, cursor=None):
|
||||
self.connection = connection
|
||||
self.cursor = cursor
|
||||
|
||||
def set(self, connection, cursor):
|
||||
self.connection = connection
|
||||
self.cursor = cursor
|
||||
|
||||
def execute(self, sql: str, params: tuple = ()):
|
||||
self.cursor.execute(sql, params)
|
||||
self.connection.commit()
|
||||
|
||||
def executemany(self, sql: str, params: list[tuple]):
|
||||
self.cursor.executemany(sql, params)
|
||||
self.connection.commit()
|
||||
|
||||
def query(self, sql: str, params: tuple = ()):
|
||||
self.cursor.execute(sql, params)
|
||||
|
||||
|
||||
class DeviceTable(BaseTable):
|
||||
@staticmethod
|
||||
def check(table_handler: BaseTable):
|
||||
"""检测是否存在当前表,并根据devices.csv开始数据初始化"""
|
||||
table_handler.query("SELECT name FROM sqlite_master WHERE type='table' AND name='device'")
|
||||
if table_handler.cursor.fetchone() is None:
|
||||
table_handler.execute(
|
||||
f"""
|
||||
CREATE TABLE device (
|
||||
device_id TEXT,
|
||||
project_code TEXT,
|
||||
subscribe_topic TEXT,
|
||||
publish_topic TEXT,
|
||||
aiot_id TEXT NULL,
|
||||
register_type INTEGER default 0,
|
||||
PRIMARY KEY (device_id)
|
||||
)
|
||||
"""
|
||||
)
|
||||
init_config_path = os.path.join(os.path.dirname((os.path.abspath("__file__"))), "data",
|
||||
"_devices.csv" if ENV_TYPE != 2 else "devices.csv")
|
||||
if os.path.exists(init_config_path):
|
||||
with open(init_config_path, newline='', encoding="utf8") as csvfile:
|
||||
csvreader = csv.reader(csvfile)
|
||||
head = next(csvreader)
|
||||
data = []
|
||||
if len(head) == 5:
|
||||
for row in csvreader:
|
||||
register_type = 0 if row[4] == '' else 1
|
||||
data.append(tuple([i.strip() for i in row] + [register_type]))
|
||||
|
||||
table_handler.executemany(
|
||||
f"""
|
||||
INSERT INTO device
|
||||
(project_code, device_id, subscribe_topic, publish_topic, aiot_id, register_type)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (device_id) DO NOTHING
|
||||
""",
|
||||
data
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_topics(table_handler):
|
||||
table_handler.query(
|
||||
"""
|
||||
SELECT (
|
||||
SELECT json_group_array(subscribe_topic)
|
||||
FROM (
|
||||
SELECT DISTINCT subscribe_topic
|
||||
FROM device
|
||||
)
|
||||
) AS subscribe_topics,
|
||||
(
|
||||
SELECT json_group_array(publish_topic)
|
||||
FROM (
|
||||
SELECT DISTINCT publish_topic
|
||||
FROM device
|
||||
)
|
||||
) AS publish_topics;
|
||||
"""
|
||||
)
|
||||
res = table_handler.cursor.fetchall()
|
||||
if res:
|
||||
return res[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def check_device_id(table_handler, topic, device_id):
|
||||
"""device in `publish_topic` and `device_id` column"""
|
||||
table_handler.query(
|
||||
f"""
|
||||
SELECT DISTINCT device_id from device where publish_topic = '{topic}' and device_id = '{device_id}'
|
||||
"""
|
||||
)
|
||||
if len(table_handler.cursor.fetchall()) > 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_device_topic(table_handler, device_id):
|
||||
"""获取对应设备的订阅主题"""
|
||||
table_handler.query(
|
||||
f"""
|
||||
SELECT subscribe_topic, publish_topic from device where device_id = '{device_id}'
|
||||
"""
|
||||
)
|
||||
res = table_handler.cursor.fetchall()
|
||||
if res:
|
||||
return res[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_device_register_type(table_handler, device_id):
|
||||
table_handler.query(
|
||||
f"""
|
||||
SELECT register_type from device where device_id = '{device_id}'
|
||||
"""
|
||||
)
|
||||
res = table_handler.cursor.fetchall()
|
||||
if res and res[0][0] == 1:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_aiot_id(table_handler, device_id):
|
||||
table_handler.query(
|
||||
f"""
|
||||
SELECT aiot_id from device where device_id = '{device_id}' and register_type = 1
|
||||
"""
|
||||
)
|
||||
res = table_handler.cursor.fetchall()
|
||||
if res:
|
||||
return res[0][0]
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_device_id(table_handler, aiot_id):
|
||||
table_handler.query(
|
||||
f"""
|
||||
SELECT device_id from device where aiot_id = '{aiot_id}' and register_type = 1
|
||||
"""
|
||||
)
|
||||
res = table_handler.cursor.fetchall()
|
||||
if res:
|
||||
return res[0][0]
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_device_ids(table_handler):
|
||||
"""获取所有设备的ID"""
|
||||
table_handler.query(
|
||||
f"""
|
||||
SELECT device_id from device
|
||||
"""
|
||||
)
|
||||
res = table_handler.cursor.fetchall()
|
||||
if res:
|
||||
return res
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_project_code(table_handler, device_id):
|
||||
table_handler.query(
|
||||
f"""
|
||||
SELECT project_code from device where device_id = '{device_id}'
|
||||
"""
|
||||
)
|
||||
res = table_handler.cursor.fetchall()
|
||||
if res:
|
||||
return res[0][0]
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def update_aiot_id(table_handler: BaseTable, device_id, aiot_id):
|
||||
table_handler.execute(
|
||||
f"""
|
||||
UPDATE device SET aiot_id = '{aiot_id}', register_type = 1 WHERE device_id = '{device_id}'
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
class GatewayTable(BaseTable):
|
||||
@staticmethod
|
||||
def check(table_handler: BaseTable):
|
||||
"""检测是否存在当前表,并根据gateway.json开始数据初始化"""
|
||||
table_handler.query("SELECT name FROM sqlite_master WHERE type='table' AND name='gateway'")
|
||||
if table_handler.cursor.fetchone() is None:
|
||||
table_handler.execute(
|
||||
f"""
|
||||
CREATE TABLE gateway (
|
||||
gateway_id TEXT NULL,
|
||||
project_code TEXT,
|
||||
gateway_sct TEXT NULL,
|
||||
register_type INTEGER default 0,
|
||||
PRIMARY KEY (gateway_id)
|
||||
)
|
||||
"""
|
||||
)
|
||||
init_config_path = os.path.join(os.path.dirname(os.path.abspath("__file__")), "data",
|
||||
"_gateways.json" if ENV_TYPE != 2 else "gateways.json")
|
||||
if os.path.exists(init_config_path):
|
||||
with open(init_config_path, "r", encoding="utf8") as f:
|
||||
try:
|
||||
gateway_config = json.load(f)
|
||||
data = []
|
||||
for project_code, gateway_info in gateway_config.items():
|
||||
data.append((project_code, gateway_info[0], gateway_info[1], 1))
|
||||
table_handler.executemany(
|
||||
f"""
|
||||
INSERT INTO gateway (project_code, gateway_id, gateway_sct, register_type) VALUES (?, ?, ?, ?)
|
||||
ON CONFLICT (gateway_id) DO NOTHING
|
||||
""",
|
||||
data
|
||||
)
|
||||
except Exception as e:
|
||||
Logger.error(f"{type(e).__name__}, {e}")
|
||||
if DEBUG:
|
||||
traceback.print_exc()
|
||||
|
||||
@staticmethod
|
||||
def get_gateway(table_handler, project_code):
|
||||
table_handler.query(
|
||||
f"""
|
||||
SELECT gateway_id, gateway_sct from gateway where project_code = '{project_code}'
|
||||
"""
|
||||
)
|
||||
res = table_handler.cursor.fetchall()
|
||||
if res:
|
||||
return res[0]
|
||||
else:
|
||||
return None, None
|
||||
|
||||
@staticmethod
|
||||
def get_registered_gateway(table_handler):
|
||||
table_handler.query(
|
||||
f"""
|
||||
SELECT gateway_id, gateway_sct from gateway where register_type = 1
|
||||
"""
|
||||
)
|
||||
res = table_handler.cursor.fetchall()
|
||||
if res:
|
||||
return res
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_project_code_list(table_handler):
|
||||
table_handler.query(
|
||||
f"""
|
||||
SELECT DISTINCT project_code from gateway
|
||||
"""
|
||||
)
|
||||
res = table_handler.cursor.fetchall()
|
||||
if res:
|
||||
return res
|
||||
else:
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def get_registered_sub_aiot_id(table_handler, gateway_id):
|
||||
"""查询已注册网关下的所有子设备在aiot平台的设备id"""
|
||||
table_handler.query(
|
||||
f"""
|
||||
SELECT json_group_array(aiot_id) FROM device WHERE project_code IN
|
||||
(SELECT DISTINCT project_code FROM gateway
|
||||
WHERE register_type = 1 and gateway_id = '{gateway_id}')
|
||||
AND register_type = 1
|
||||
"""
|
||||
)
|
||||
res = table_handler.cursor.fetchall()
|
||||
if res:
|
||||
return res[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class HeartBeatTable(BaseTable):
|
||||
@staticmethod
|
||||
def check(table_handler: BaseTable):
|
||||
table_handler.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS heart_beat (
|
||||
device_id TEXT,
|
||||
factory_id TEXT,
|
||||
last_heart_beat TEXT,
|
||||
PRIMARY KEY (device_id)
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def delete(table_handler: BaseTable, obj):
|
||||
table_handler.execute("DELETE FROM heart_beat WHERE device_id=?", (obj.device_id,))
|
||||
|
||||
@staticmethod
|
||||
def update(table_handler: BaseTable, obj: HeartBeat, topic: str):
|
||||
if DeviceTable.check_device_id(table_handler, topic, obj.device_id):
|
||||
time_stamp = str(int(time.time()))
|
||||
table_handler.execute(
|
||||
"""
|
||||
INSERT INTO heart_beat (device_id, factory_id, last_heart_beat)
|
||||
VALUES (?, ?, ?)
|
||||
ON CONFLICT (device_id)
|
||||
DO UPDATE SET
|
||||
last_heart_beat=?
|
||||
""",
|
||||
(obj.device_id, obj.factory_id, time_stamp, time_stamp),
|
||||
)
|
||||
return True
|
||||
else:
|
||||
if DEBUG:
|
||||
Logger.warn(
|
||||
f"device_id - {obj.device_id} is invalid in {topic}, operation was not performed"
|
||||
)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_last_time(table_handler: BaseTable, device_id):
|
||||
table_handler.query(
|
||||
"""
|
||||
SELECT last_heart_beat
|
||||
FROM heart_beat
|
||||
WHERE device_id = ?
|
||||
""",
|
||||
(device_id,),
|
||||
)
|
||||
res = table_handler.cursor.fetchall()
|
||||
if res:
|
||||
return res[0][0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class RegisterTable(BaseTable):
|
||||
@staticmethod
|
||||
def check(table_handler: BaseTable):
|
||||
table_handler.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS register (
|
||||
device_id TEXT,
|
||||
factory_id TEXT,
|
||||
device_type TEXT,
|
||||
device_position_code TEXT,
|
||||
device_position_desc TEXT,
|
||||
last_register_timestamp TEXT,
|
||||
PRIMARY KEY (device_id)
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
def drop(self):
|
||||
self.execute("drop table register")
|
||||
|
||||
def delete(self, obj):
|
||||
self.execute(
|
||||
"DELETE FROM register WHERE device_id=? and factory_id=?",
|
||||
(
|
||||
obj.device_id,
|
||||
obj.factory_id,
|
||||
),
|
||||
)
|
||||
|
||||
def insert(self, obj, topic: str):
|
||||
if DeviceTable.check_device_id(self, topic, obj.device_id):
|
||||
time_stamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
|
||||
self.execute(
|
||||
"""
|
||||
INSERT INTO register
|
||||
(device_id, factory_id, device_type, device_position_code, device_position_desc,
|
||||
last_register_timestamp)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
obj.device_id,
|
||||
obj.factory_id,
|
||||
obj.device_type,
|
||||
obj.device_position_code,
|
||||
obj.device_position_desc,
|
||||
time_stamp,
|
||||
),
|
||||
)
|
||||
return True
|
||||
else:
|
||||
if DEBUG:
|
||||
Logger.warn(
|
||||
f"device_id - {obj.device_id} is invalid in {topic}, operation was not performed"
|
||||
)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def update(table_handler, obj: Register, topic: str):
|
||||
if DeviceTable.check_device_id(table_handler, topic, obj.device_id):
|
||||
time_stamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
|
||||
table_handler.execute(
|
||||
"""
|
||||
INSERT INTO register (device_id, factory_id, device_type, device_position_code, device_position_desc,
|
||||
last_register_timestamp)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (device_id)
|
||||
DO UPDATE SET
|
||||
device_type=?, device_position_code=?, device_position_desc=?, last_register_timestamp=?
|
||||
""",
|
||||
(
|
||||
obj.device_id,
|
||||
obj.factory_id,
|
||||
obj.device_type,
|
||||
obj.device_position_code,
|
||||
obj.device_position_desc,
|
||||
time_stamp,
|
||||
obj.device_type,
|
||||
obj.device_position_code,
|
||||
obj.device_position_desc,
|
||||
time_stamp,
|
||||
),
|
||||
)
|
||||
return True
|
||||
else:
|
||||
if DEBUG:
|
||||
Logger.warn(
|
||||
f"device_id - {obj.device_id} is invalid in {topic}, operation was not performed"
|
||||
)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_device_position_desc(table_handler: BaseTable, device_id: str):
|
||||
"""根据device_id获取设备的空间描述信息"""
|
||||
table_handler.query(
|
||||
"""
|
||||
SELECT device_position_desc
|
||||
FROM register
|
||||
WHERE device_id = ?
|
||||
""",
|
||||
(device_id,),
|
||||
)
|
||||
res = table_handler.cursor.fetchall()
|
||||
if res:
|
||||
return res[0][0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class UserInfoTable(BaseTable):
|
||||
@staticmethod
|
||||
def check(table_handler: BaseTable):
|
||||
table_handler.query("SELECT name FROM sqlite_master WHERE type='table' AND name='user_info'")
|
||||
if table_handler.cursor.fetchone() is None:
|
||||
table_handler.execute(
|
||||
f"""
|
||||
CREATE TABLE user_info (
|
||||
user_id TEXT,
|
||||
device_id TEXT,
|
||||
name TEXT,
|
||||
user_type INTEGER,
|
||||
qrcode TEXT NULL,
|
||||
face_url TEXT NULL,
|
||||
create_timestamp TEXT,
|
||||
update_timestamp TEXT,
|
||||
PRIMARY KEY (user_id, device_id)
|
||||
)
|
||||
"""
|
||||
)
|
||||
table_handler.execute(
|
||||
f"""
|
||||
CREATE INDEX idx_user_info_qrcode ON user_info(qrcode);
|
||||
"""
|
||||
)
|
||||
init_config_path = os.path.join(os.path.dirname(os.path.abspath("__file__")), "data",
|
||||
"_users.csv" if ENV_TYPE != 2 else "users.csv")
|
||||
if os.path.exists(init_config_path):
|
||||
with open(init_config_path, newline='', encoding="utf8") as csvfile:
|
||||
csvreader = csv.reader(csvfile)
|
||||
head = next(csvreader)
|
||||
data = []
|
||||
if len(head) == 4:
|
||||
for row in csvreader:
|
||||
user_id = row[0].strip()
|
||||
name = row[1].strip()
|
||||
user_type = 0 if row[2].strip() == "业主" else 1
|
||||
timestamp = str(int(time.time() * 1000))
|
||||
device_id = row[3].strip()
|
||||
data.append((user_id, name, user_type, timestamp, timestamp, device_id))
|
||||
table_handler.executemany(
|
||||
f"""
|
||||
INSERT INTO user_info
|
||||
(user_id, name, user_type, create_timestamp, update_timestamp, device_id)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (user_id, device_id) DO NOTHING
|
||||
""",
|
||||
data
|
||||
)
|
||||
elif len(head) == 5:
|
||||
for row in csvreader:
|
||||
user_id = row[0].strip()
|
||||
name = row[1].strip()
|
||||
user_type = 0 if row[2].strip() == "业主" else 1
|
||||
timestamp = str(int(time.time() * 1000))
|
||||
device_id = row[3].strip()
|
||||
face_url = row[4].strip()
|
||||
data.append((user_id, name, user_type, timestamp, timestamp, device_id, face_url))
|
||||
table_handler.executemany(
|
||||
f"""
|
||||
INSERT INTO user_info
|
||||
(user_id, name, user_type, create_timestamp, update_timestamp, device_id, face_url)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (user_id, device_id) DO NOTHING
|
||||
""",
|
||||
data
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def delete_redundancy_1(table_handler, timestamp):
|
||||
"""用于在每天清理无效的冗余访客数据"""
|
||||
table_handler.execute(f"DELETE FROM user_info WHERE user_type=1 and update_timestamp < {timestamp}")
|
||||
|
||||
@staticmethod
|
||||
def delete_redundancy_0(table_handler, timestamp):
|
||||
"""用于在每天清理无效的冗余业主数据"""
|
||||
table_handler.execute(f"DELETE FROM user_info WHERE user_type=0 "
|
||||
f"and (face_url is NULL or face_url = '') and update_timestamp < {timestamp}")
|
||||
|
||||
@staticmethod
|
||||
def update(table_handler, obj: UserInfo):
|
||||
time_stamp = str(int(time.time() * 1000))
|
||||
table_handler.execute(
|
||||
"""
|
||||
INSERT INTO user_info (user_id, name, user_type, create_timestamp, update_timestamp, device_id)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (user_id, device_id)
|
||||
DO UPDATE SET
|
||||
name=?, update_timestamp=?
|
||||
""",
|
||||
(obj.user_id, obj.name, obj.user_type, time_stamp, time_stamp, obj.device_id,
|
||||
obj.name, time_stamp),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def select_all(table_handler):
|
||||
table_handler.execute("SELECT * FROM user_info")
|
||||
return table_handler.cursor.fetchall()
|
||||
|
||||
@staticmethod
|
||||
def get_name(table_handler, user_id, device_id):
|
||||
table_handler.query(
|
||||
"""
|
||||
SELECT name
|
||||
FROM user_info
|
||||
WHERE user_id = ? AND device_id = ?
|
||||
""",
|
||||
(user_id, device_id),
|
||||
)
|
||||
res = table_handler.cursor.fetchall()
|
||||
if res:
|
||||
return res[0][0]
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_user_by_qrcode(table_handler, qrcode, device_id):
|
||||
table_handler.query(
|
||||
"""
|
||||
SELECT user_id, name
|
||||
FROM user_info
|
||||
WHERE qrcode = ? AND device_id = ?
|
||||
""",
|
||||
(qrcode, device_id),
|
||||
)
|
||||
res = table_handler.cursor.fetchall()
|
||||
if res:
|
||||
return res[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def update_qrcode(table_handler, user_id, device_id, qrcode):
|
||||
time_stamp = str(int(time.time() * 1000))
|
||||
table_handler.execute(
|
||||
f"""
|
||||
UPDATE user_info SET qrcode = ?, update_timestamp = ? WHERE user_id = ? and device_id = ?
|
||||
""",
|
||||
(qrcode, time_stamp, user_id, device_id)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def update_face_url(table_handler, user_id, device_id, face_url):
|
||||
time_stamp = str(int(time.time() * 1000))
|
||||
table_handler.execute(
|
||||
f"""
|
||||
UPDATE user_info SET face_url = ?, update_timestamp = ? WHERE user_id = ? and device_id = ?
|
||||
""",
|
||||
(face_url, time_stamp, user_id, device_id)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def exists_face_url(table_handler, user_id, device_id):
|
||||
"""判断人脸地址是否存在且不为空"""
|
||||
table_handler.query(
|
||||
f"""
|
||||
SELECT face_url FROM user_info WHERE user_id = ? AND device_id = ?
|
||||
AND face_url IS NOT NULL AND face_url != ''
|
||||
""",
|
||||
(user_id, device_id)
|
||||
)
|
||||
res = table_handler.cursor.fetchall()
|
||||
if res and len(res[0]) > 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class RecordTable(BaseTable):
|
||||
@staticmethod
|
||||
def check(table_handler: BaseTable):
|
||||
table_handler.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS record (
|
||||
user_id TEXT,
|
||||
device_id TEXT,
|
||||
record_datetime TEXT
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def add(table_handler: BaseTable, obj: PushRtAccessRecord):
|
||||
table_handler.execute(
|
||||
"""
|
||||
INSERT INTO record (user_id, device_id, record_datetime)
|
||||
VALUES (?, ?, ?)
|
||||
""",
|
||||
(obj.user_id, obj.device_id, str(datetime_to_timestamp(obj.time))),
|
||||
)
|
||||
98
meian/devices/meian_model.py
Normal file
98
meian/devices/meian_model.py
Normal file
@@ -0,0 +1,98 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
@File : meian_model
|
||||
@Author : xuxingchen
|
||||
@Version : 1.0
|
||||
@Contact : xuxingchen@sinochem.com
|
||||
@Desc : Data Entity
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
# 错误码
|
||||
error_code = {
|
||||
0: "成功",
|
||||
-1: "图片下载超时",
|
||||
-2: "图片下载失败",
|
||||
-3: "用户ID不存在",
|
||||
-4: "参数无效",
|
||||
-5: "注册失败",
|
||||
-6: "用户ID已存在",
|
||||
-7: "无效人脸",
|
||||
-8: "内部错误",
|
||||
-9: "图片解码失败,图像尺寸宽高像素应为540*960",
|
||||
-10: "添加人脸失败",
|
||||
-11: "人脸图像无特征",
|
||||
-12: "查询离线通行记录失败",
|
||||
-13: "设备忙",
|
||||
-14: "rf_id_not_exist",
|
||||
-15: "rf_id_existed",
|
||||
-16: "add_rf_id_failed or rm_rf_id_failed",
|
||||
-17: "get_rf_id_reader_mode_failed",
|
||||
-18: "set_rf_id_reader_mode_failed",
|
||||
-19: "添加人脸操作不支持",
|
||||
}
|
||||
|
||||
|
||||
class BaseInfo(BaseModel):
|
||||
data_type: str = None
|
||||
|
||||
def check(self):
|
||||
for attr in self.__dict__.keys():
|
||||
# if property can be null, default value should not be set to None
|
||||
if self.__dict__[attr] is None:
|
||||
raise ValueError(f"{attr} not allowed to be set to None")
|
||||
|
||||
|
||||
class BaseRequest(BaseInfo):
|
||||
device_id: str = None
|
||||
token: str = None
|
||||
|
||||
|
||||
class BaseResponse(BaseInfo):
|
||||
factory_id: str = None
|
||||
error_code: int = None
|
||||
token: str = None
|
||||
|
||||
|
||||
class HeartBeat(BaseInfo):
|
||||
device_id: str = None
|
||||
factory_id: str = None
|
||||
|
||||
|
||||
class PushRtAccessRecord(HeartBeat):
|
||||
time: str = None
|
||||
user_id: str = None
|
||||
access_mode: str = None
|
||||
|
||||
|
||||
class Register(BaseRequest):
|
||||
factory_id: str = None # 厂商唯一标识
|
||||
device_type: int = None # 0:面板机,1:梯控机
|
||||
device_position_code: str = None
|
||||
device_position_desc: str = None
|
||||
|
||||
|
||||
class FaceInfo(BaseModel):
|
||||
dataType: str
|
||||
deviceId: str
|
||||
token: str
|
||||
userId: str
|
||||
faceUrl: str
|
||||
floor: int
|
||||
|
||||
|
||||
class QrCodeInfo(BaseModel):
|
||||
dataType: str
|
||||
deviceId: str
|
||||
token: str
|
||||
userId: str
|
||||
qrCode: str
|
||||
floor: int
|
||||
|
||||
|
||||
class DeleteUser(BaseModel):
|
||||
dataType: str
|
||||
deviceId: str
|
||||
token: str
|
||||
userId: str
|
||||
137
meian/devices/yunfu_model.py
Normal file
137
meian/devices/yunfu_model.py
Normal file
@@ -0,0 +1,137 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
@File : yunfu_model
|
||||
@Author : xuxingchen
|
||||
@Version : 1.0.1
|
||||
@Contact : xuxingchen@sinochem.com
|
||||
@Desc : 云服的数据模型
|
||||
"""
|
||||
import os
|
||||
import time
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, field_validator, Field
|
||||
from enum import Enum
|
||||
|
||||
from utils import get_sign
|
||||
|
||||
error_code = {
|
||||
0: "成功",
|
||||
401: "参数错误,原因可能是:必填参数缺失或格式错误",
|
||||
402: "云端错误。请稍后再试",
|
||||
403: "mq解析失败",
|
||||
404: "发送kafka异常",
|
||||
602: "通过deviceUuid获取redis中DeviceInfoCacheVO缓存失败",
|
||||
40014: "设备不存在"
|
||||
}
|
||||
|
||||
|
||||
class MeianProduct:
|
||||
if int(os.environ.get("ENV_TYPE", 0)) != 2:
|
||||
id: str = "4bb9a56bfe9*********2ee99bac6d"
|
||||
secret: str = "9b0159********abe57c10ea665ddd"
|
||||
else:
|
||||
id: str = "a2ce31a40b********230ccc860e"
|
||||
secret: str = "03970a**********28b84e8437"
|
||||
|
||||
|
||||
class OpenEventType(Enum):
|
||||
# 此处的取名应当与美安设备的accessMode能够一一对应
|
||||
FACE = 8
|
||||
QRCODE = 10
|
||||
OFFLINE = 11
|
||||
|
||||
|
||||
class OpenEventCertificateType(Enum):
|
||||
# 此处的取名应当与美安设备的accessMode能够一一对应
|
||||
FACE = 2
|
||||
PASSWORD = 3
|
||||
QRCODE = 4
|
||||
|
||||
|
||||
class BaseInfo(BaseModel):
|
||||
def check(self):
|
||||
for attr in self.__dict__.keys():
|
||||
# if property can be null, default value should not be set to None
|
||||
if self.__dict__[attr] is None:
|
||||
raise ValueError(f"{attr} not allowed to be set to None")
|
||||
|
||||
|
||||
class CommRegister(BaseInfo):
|
||||
messageId: str = None
|
||||
version: str = "1.0"
|
||||
time: int = int(time.time() * 1000)
|
||||
params: dict = None
|
||||
|
||||
|
||||
class EventPost(BaseInfo):
|
||||
messageId: str = None
|
||||
version: str = "1.0"
|
||||
time: int = int(time.time() * 1000)
|
||||
ext: dict = {
|
||||
"ack": 1
|
||||
}
|
||||
eventCode: str = None
|
||||
params: dict = None
|
||||
|
||||
|
||||
class EventPostResp(BaseInfo):
|
||||
messageId: str
|
||||
requestTime: int
|
||||
time: int = int(time.time() * 1000)
|
||||
code: int = 0
|
||||
message: str = "success"
|
||||
serviceCode: str
|
||||
data: dict = None
|
||||
|
||||
|
||||
class OpenEvent(BaseInfo):
|
||||
type: OpenEventType | int = None
|
||||
user_picture: str = "default"
|
||||
code: str = "default"
|
||||
name: str = None
|
||||
certificate_type: OpenEventCertificateType | int = None
|
||||
certificate: str = None
|
||||
result: int = 1
|
||||
error_code: int = 1
|
||||
enter_type: int = 1
|
||||
datetime: str = None
|
||||
|
||||
|
||||
class RegisterParam(BaseInfo):
|
||||
productId: str = MeianProduct.id
|
||||
deviceName: str = None
|
||||
displayName: str = None
|
||||
sign: Optional[str] = Field(default=None, validate_default=True)
|
||||
|
||||
@field_validator("sign")
|
||||
def gen_sign(cls, value, values):
|
||||
productId = values.data.get("productId")
|
||||
deviceName = values.data.get("deviceName")
|
||||
return get_sign(f"deviceName{deviceName}productId{productId}", MeianProduct.secret)
|
||||
|
||||
|
||||
class Online(BaseInfo):
|
||||
messageId: str
|
||||
version: str = "1.0"
|
||||
time: int = int(time.time() * 1000)
|
||||
cleanSession: str = "true"
|
||||
productModel: str = "default"
|
||||
chipModel: str = "default"
|
||||
otaVersion: str = ""
|
||||
|
||||
|
||||
class QrCodeResp(BaseInfo):
|
||||
code: int
|
||||
message: str
|
||||
qrcode: str
|
||||
|
||||
|
||||
class AddFaceResp(BaseInfo):
|
||||
code: int
|
||||
message: str
|
||||
|
||||
|
||||
class DelFaceResp(BaseInfo):
|
||||
code: int
|
||||
message: str
|
||||
Reference in New Issue
Block a user