This commit is contained in:
2024-10-24 09:04:38 +08:00
parent 2a18f10bcf
commit 641c34b1b3
20 changed files with 2817 additions and 0 deletions

702
meian/devices/meian_db.py Normal file
View 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))),
)