2024-10-24 09:04:38 +08:00

703 lines
23 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- 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))),
)