From 177288317c4192f4c18d837bd0b4b33abda00b5b Mon Sep 17 00:00:00 2001 From: slf Date: Tue, 4 Mar 2025 11:19:53 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=B5=E6=B1=A0=E7=9F=AD=E8=B7=AF=E8=AF=95?= =?UTF-8?q?=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + app/api/Server.py | 1304 +++++++++++++++++++++++++++++++++ app/api/ShortCircuit.py | 704 ++++++++++++++++++ app/api/__init__.py | 26 + app/conf/Setting.py | 44 ++ app/lib/AlchemyJsonEncoder.py | 29 + app/lib/Camera.py | 82 +++ app/lib/Log.py | 59 ++ app/lib/ModBus.py | 292 ++++++++ app/lib/RecordData.py | 113 +++ app/lib/StaticData.py | 145 ++++ app/lib/Utils.py | 479 ++++++++++++ app/lib/VK70xUMCDAQ.py | 204 ++++++ app/lib/usb.py | 46 ++ app/lib/websocket_manageer.py | 20 + app/models/Base_model.py | 5 + app/models/Business.py | 382 ++++++++++ app/models/DateEntity.py | 304 ++++++++ app/models/Repository.py | 216 ++++++ app/models/__init__.py | 1 + app/utils/__init__.py | 0 app/utils/excel_util.py | 409 +++++++++++ app/validators/api.py | 123 ++++ main.py | 50 ++ requirements.txt | 32 + run.bat | 7 + start.bat | 4 + start.bat.lnk | Bin 0 -> 846 bytes start.lnk | Bin 0 -> 1360 bytes 29 files changed, 5081 insertions(+) create mode 100644 README.md create mode 100644 app/api/Server.py create mode 100644 app/api/ShortCircuit.py create mode 100644 app/api/__init__.py create mode 100644 app/conf/Setting.py create mode 100644 app/lib/AlchemyJsonEncoder.py create mode 100644 app/lib/Camera.py create mode 100644 app/lib/Log.py create mode 100644 app/lib/ModBus.py create mode 100644 app/lib/RecordData.py create mode 100644 app/lib/StaticData.py create mode 100644 app/lib/Utils.py create mode 100644 app/lib/VK70xUMCDAQ.py create mode 100644 app/lib/usb.py create mode 100644 app/lib/websocket_manageer.py create mode 100644 app/models/Base_model.py create mode 100644 app/models/Business.py create mode 100644 app/models/DateEntity.py create mode 100644 app/models/Repository.py create mode 100644 app/models/__init__.py create mode 100644 app/utils/__init__.py create mode 100644 app/utils/excel_util.py create mode 100644 app/validators/api.py create mode 100644 main.py create mode 100644 requirements.txt create mode 100644 run.bat create mode 100644 start.bat create mode 100644 start.bat.lnk create mode 100644 start.lnk diff --git a/README.md b/README.md new file mode 100644 index 0000000..ac79c33 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +电池短路试验 diff --git a/app/api/Server.py b/app/api/Server.py new file mode 100644 index 0000000..2801335 --- /dev/null +++ b/app/api/Server.py @@ -0,0 +1,1304 @@ +# import os +# import sys +# import ctypes +# import asyncio +# import datetime +# import psutil +# import requests +# import pathlib +# from apscheduler.schedulers.asyncio import AsyncIOScheduler +# from app.lib.VK70xUMCDAQ import VK70xUMCDAQ +# +# from app.conf.Setting import settings +# +# from app.lib.RecordData import ExperimentData +# from app.models import * +# from app.lib.Utils import Utils +# from app.lib.Utils import format_status_data +# from fastapi import APIRouter, Request, WebSocket, WebSocketDisconnect, Response +# from app.lib.ModBus import ModbusConnection, IDLE, ZHIFENG +# from sqlalchemy import and_, desc +# from app.models.Business import PageParam, BllUser, BllRole, BllExperiment, BllExperimentRecord, BllSpecimen, \ +# BllExperimentConfig, BllWaitDisinfSpecimen, BllTerminal, BllUselog +# from app.models.DateEntity import EntitySpecimen, EntityExperiment, EntityWaitDisinfSpecimen, EntityExperimentConfig, \ +# EntityTerminal +# from app.validators.api import * +# from app.lib.StaticData import * +# from app.lib.websocket_manageer import ConnectionManager +# from app.lib.Log import logger +# from app.utils.excel_util import ExcelExport +# import io +# from app.lib import usb +# import httpx +# +# shiyan_time = 0 +# END_TIME = None +# START_EXPERIMENT_ID = None +# +# router = APIRouter(prefix='/api') +# +# +# @router.get('/get_terminal_info', summary='获取系统信息') +# async def get_terminal_info(request: Request): +# terminal_info = BllTerminal().findEntity(EntityTerminal.is_enabled == 0) +# return Utils.true_return(terminal_info, desc="获取成功!") +# +# +# @router.post('/add_user', summary='创建用户') +# async def add_user(request: Request, body: SignUpdateModel): +# """ +# 创建用户 +# :body SignUpdateModel +# :return: +# """ +# user = BllUser().findEntity(and_(EntityUser.user_name == body.username, EntityUser.is_enabled == 0)) +# if user: +# return Utils.false_return('', desc="账号已注册!") +# entity = EntityUser() +# entity.user_id = str(Utils.get_uuid()) +# entity.user_pwd = Utils.MD5(body.password) +# entity.user_name = body.username +# entity.real_name = body.name +# entity.role_id = body.role_id +# entity.user_sex = body.sex +# entity.update_time = Utils.get_time() +# BllUser().insert(entity) +# return Utils.true_return('', desc="创建成功!") +# +# +# @router.post('/login', summary='用户登录') +# async def login(request: Request, body: SignInModel): +# """ +# 用户登录 +# :body SignInModel +# :return: +# """ +# user = BllUser().login(body.username, body.password) +# if user: +# CurrentUser["username"] = user.user_name +# CurrentUser["user_id"] = user.user_id +# return Utils.true_return(user, desc="登录成功!") +# return Utils.false_return('', desc="登录失败,账号与密码不匹配!") +# +# +# @router.post('/user_list', summary='用户列表') +# async def user_list(request: Request, body: UserModel): +# """ +# 用户列表 +# :return: +# """ +# queryOrm = BllUser().getUserList() +# pageparam = PageParam(body.page_no, body.page_size) +# user_list = BllUser().queryPage(queryOrm, pageparam) +# pagination = { +# "total_count": pageparam.totalRecords, +# "page_no": body.page_no, +# "page_size": body.page_size +# } +# return Utils.true_return_pagination(pagination, user_list, desc="获取成功!") +# +# +# @router.put('/{user_id}', summary='编辑用户') +# async def update_user(user_id: str, body: SignUpdateModel): +# """ +# 编辑用户 +# :param user_id: str +# :body SignUpdateModel +# :return: +# """ +# user = BllUser().findEntity(and_(EntityUser.user_id == user_id, EntityUser.is_enabled == 0)) +# if user: +# user.user_pwd = Utils.MD5(body.password) +# user.user_name = body.username +# user.real_name = body.name +# user.role_id = body.role_id +# user.user_sex = body.sex +# user.update_time = Utils.get_time() +# BllUser().update(user) +# return Utils.true_return(user, desc="编辑成功!") +# return Utils.false_return('', desc="查不到该账号!") +# +# +# @router.delete('/delete_user/{user_id}', summary='删除用户') +# async def del_user(user_id: str): +# """ +# 删除用户 +# :param user_id: str +# :return: +# """ +# user = BllUser().findEntity(and_(EntityUser.user_id == user_id, EntityUser.is_enabled == 0)) +# if user: +# user.is_enabled = 1 +# user.update_time = Utils.get_time() +# BllUser().update(user) +# return Utils.true_return('', desc="删除成功!") +# return Utils.false_return('', desc="查不到该账号!") +# +# +# @router.get('/get_role_list', summary='获取角色列表') +# async def get_role_list(request: Request): +# """ +# 获取角色列表 +# :return: +# """ +# role_list = BllRole().findList(EntityRole.is_enabled == 0).all() +# data_list = [{"role_name": i.role_name, "role_id": i.role_id} for i in role_list] +# return Utils.true_return(data_list, desc="查询成功!") +# +# +# @router.post('/get_specimen_list', summary='获取证物数据') +# async def get_specimen_data(request: Request, body: SpecimenSearchModel): +# queryOrm = BllSpecimen().get_specimen_list(body) +# pageparam = PageParam(body.page_no, body.page_size) +# specimen_list = BllSpecimen().queryPage(queryOrm, pageparam) +# for specimen in specimen_list: +# specimen.experiment_list = [i.to_dict() for i in specimen.experiments] +# +# pagination = { +# "total_count": pageparam.totalRecords, +# "page_no": body.page_no, +# "page_size": body.page_size +# } +# return Utils.true_return_pagination(pagination, specimen_list, desc="获取成功!") +# +# +# @router.get('/test_data', summary='测试接口') +# async def get_specimen_data(type: int = None, open: int = 1): +# if type: +# if type == 1: +# print('open door-------------') +# await open_door(open) +# elif type == 2: +# print('config_v1-------------') +# await config_v1() +# elif type == 3: +# print('get_sensor_data-------------') +# await get_sensor_data() +# elif type == 4: +# print('get_device_state-------------') +# await get_device_state() +# +# return Utils.true_return('', desc="获取成功!") +# +# +# @router.get('/test_request', summary='测试接口') +# async def test_request(): +# global END_TIME +# END_TIME = datetime.datetime.now() +# # wait_obj = BllWaitDisinfSpecimen().findEntity( +# # EntityWaitDisinfSpecimen.specimen_id == "2bd90215-b91e-11ef-ad8c-2c6dc175c6a5") +# # wait_obj.done = 1 +# # BllWaitDisinfSpecimen().update(wait_obj) +# +# return Utils.true_return('', desc="获取成功!") +# +# +# async def get_device_state(): +# url = "http://100.64.0.1/api/cabinet/v1/state" +# +# try: +# # 发送GET请求 +# async with httpx.AsyncClient() as client: +# response = await client.get(url, timeout=4) +# +# # 检查响应状态码 +# if response.status_code == 200: +# # 解析响应JSON数据 +# data = response.json() +# +# # 获取各个设备的状态 +# led = data.get("led") +# door = data.get("door") +# fans = data.get("fans") +# compressor = data.get("compressor") +# heating = data.get("heating") +# ozonizer = data.get("ozonizer") +# disinfectant = data.get("disinfectant") +# pump = data.get("pump") +# work_time = data.get("work_time") +# sensor = data.get("sensor") +# +# # 打印设备状态 +# print(f"原始值: {data}") +# print(f"LED 状态: {'亮' if led == 1 else '灭'}") +# print(f"门状态: {'开' if door == 1 else '关'}") +# print(f"风机状态: {'开' if fans == 1 else '关'}") +# print(f"压缩机状态: {'开' if compressor == 1 else '关'}") +# print(f"加热管状态: {'开' if heating == 1 else '关'}") +# print(f"臭氧发生器状态: {'开' if ozonizer == 1 else '关'}") +# print(f"紫外线消毒灯状态: {'开' if disinfectant == 1 else '关'}") +# print(f"水泵状态: {'开' if pump == 1 else '关'}") +# print(f"工作时间: {work_time}") +# +# # 检查传感器状态,注意 bit0, bit1, bit2 +# sensor_status = f"温湿度传感器: {'异常' if (sensor & 0b001) else '正常'}, " +# sensor_status += f"VOC传感器: {'异常' if (sensor & 0b010) else '正常'}, " +# sensor_status += f"臭氧检测仪: {'异常' if (sensor & 0b100) else '正常'}" +# print(sensor_status) +# +# else: +# print(f"请求失败,状态码:{response.status_code}") +# +# except httpx.RequestError as e: +# print(f"请求失败:{e}") +# +# +# async def get_sensor_data(): +# url = "http://100.64.0.1/api/cabinet/v1/sensor" +# +# try: +# # 发送GET请求 +# async with httpx.AsyncClient() as client: +# response = await client.get(url, timeout=4) +# +# # 检查响应状态码 +# if response.status_code == 200: +# # 解析响应JSON数据 +# data = response.json() +# temperature = data.get("temperature") +# humidity = data.get("humidity") +# voc = data.get("voc") +# ozone = data.get("ozone") +# +# # 打印返回的传感器数据 +# print(f"温度: {temperature} °C") +# print(f"湿度: {humidity} %") +# print(f"VOC: {voc} ppm") +# print(f"臭氧: {ozone} ppb") +# else: +# print(f"请求失败,状态码:{response.status_code}") +# +# except httpx.RequestError as e: +# print(f"请求失败:{e}") +# +# +# async def config_v1(): +# # 定义接口URL +# url = "http://100.64.0.1:80/api/cabinet/v1/setting" # 替换为实际服务器地址 +# +# # 定义请求数据 +# payload = { +# "temperature": 20, # 温度设置(18~30℃) +# "humidity": 50, # 湿度设置(40~70%RH) +# "ozone": 1, # 臭氧值(1为开启,0为关闭) +# "time": 30, # 消毒时长(单位:分钟) +# "mode": 2, # 消毒模式(0:关闭, 1:臭氧, 2:紫外线消毒) +# # "light_config": 2 # LED模式(0:常关, 1:常开, 2:自动模式) +# } +# +# # 定义请求头 +# headers = { +# "Content-Type": "application/json" # 确保以 JSON 格式发送数据 +# } +# +# # 发送POST请求 +# try: +# async with httpx.AsyncClient() as client: +# response = await client.post(url, json=payload) +# +# # 如果请求成功,打印响应数据 +# if response: +# print("响应状态码:", response.status_code) +# print("响应数据:", response.json()) # 打印返回的 JSON 数据 +# +# except httpx.RequestError as e: +# print(f"下位机通讯失败: {e}") +# +# +# @router.post('/insertSpecimen', summary='新增证物') +# async def insertSpecimen(request: Request, body: SpecimenModel): +# ''' +# +# specimen_id = Column(String(50), comment="物品id", primary_key=True) +# specimen_name = Column(String(50), comment="物品名称") +# specimen_code = Column(String(50), comment="物品编号") +# specimen_type = Column(String(50), comment="物品类型") +# specimen_origin = Column(String(255), comment="物品来源") +# specimen_desc = Column(String(500), comment="物品描述") +# storage_location = Column(String(255), comment="存放位置") +# remark = Column(String(500), comment="备注") +# rfid = Column(String(50), comment="条码") +# +# ''' +# SpecimenInsertDict.update(body.items) +# spkwargs_ = SpecimenInsertDict +# specimen_id = str(Utils.get_uuid()) +# +# specimen = BllSpecimen().findEntity(and_(EntitySpecimen.specimen_code == spkwargs_["specimen_code"], +# EntitySpecimen.specimen_name == spkwargs_["specimen_name"], +# EntitySpecimen.is_enabled == 0)) +# if specimen: +# return Utils.false_return('', desc="相同名称及编号的物证已存在!") +# +# if spkwargs_.get("rfid"): +# specimen = BllSpecimen().findEntity(and_(EntitySpecimen.rfid == spkwargs_.get("rfid"), +# EntitySpecimen.specimen_id != specimen_id, +# EntitySpecimen.is_enabled == 0)) +# if specimen: +# specimen.rfid_state = 2 +# specimen.update_time = Utils.get_time() +# specimen.rfid = '' +# BllSpecimen().update(specimen) +# +# spkwargs_["specimen_id"] = specimen_id +# if spkwargs_.get("rfid"): +# spkwargs_['rfid_state'] = 1 +# else: +# spkwargs_['rfid_state'] = 2 +# +# spkwargs_["user_id"] = CurrentUser["user_id"] +# spkwargs_["user_name"] = CurrentUser["username"] +# BllSpecimen().insertSpecimen(**spkwargs_) +# data = { +# 'specimen_id': specimen_id, +# } +# +# return Utils.true_return(data, desc="新增成功!") +# +# +# @router.post('/check_rfid', summary='RFID绑定') +# async def check_rfid(request: Request, rfid: str, specimen_id: str): +# if specimen_id: +# specimen = BllSpecimen().findEntity(and_(EntitySpecimen.specimen_id == specimen_id, +# EntitySpecimen.is_enabled == 0)) +# if not specimen: +# return Utils.false_return(data='', desc="查不到该物证!") +# if specimen.rfid: +# if specimen.rfid == rfid: +# return Utils.false_return(data='', desc="该物证已绑定该条码,是否需要解绑?") +# return Utils.false_return(data='', desc="该物证已绑定其他条码,确定需要更换新的条码吗?") +# +# specimen = BllSpecimen().findEntity(and_(EntitySpecimen.rfid == rfid, +# EntitySpecimen.specimen_id != specimen_id, +# EntitySpecimen.is_enabled == 0)) +# if specimen: +# return Utils.true_return(data='', desc="该条码已经被其他物证绑定,确定要绑定到该物证上吗?") +# +# else: +# specimen = BllSpecimen().findEntity(and_(EntitySpecimen.rfid == rfid, +# EntitySpecimen.is_enabled == 0)) +# if specimen: +# return Utils.false_return(data='', desc="该条码已经被其他物证绑定,确定要绑定到该物证上吗?") +# +# return Utils.true_return(data='', desc="可正常绑定!") +# +# +# @router.post('/updateSpecimen', summary='编辑证物') +# async def updateSpecimen(request: Request, body: SpecimenModel): +# SpecimenInsertDict.update(body.items) +# spkwargs_ = SpecimenInsertDict +# specimen_id = spkwargs_["specimen_id"] +# if spkwargs_.get("rfid"): +# specimen = BllSpecimen().findEntity(and_(EntitySpecimen.rfid == spkwargs_.get("rfid"), +# EntitySpecimen.specimen_id != specimen_id, +# EntitySpecimen.is_enabled == 0)) +# if specimen: +# specimen.rfid_state = 2 +# specimen.update_time = Utils.get_time() +# specimen.rfid = '' +# BllSpecimen().update(specimen) +# +# specimen = BllSpecimen().findEntity(EntitySpecimen.specimen_id == specimen_id) +# if specimen: +# specimen.specimen_name = spkwargs_["specimen_name"] +# specimen.specimen_type = spkwargs_["specimen_type"] +# specimen.specimen_desc = spkwargs_["specimen_desc"] +# specimen.specimen_code = spkwargs_["specimen_code"] +# specimen.specimen_origin = spkwargs_["specimen_origin"] +# specimen.storage_location = spkwargs_["storage_location"] +# if spkwargs_.get("rfid"): +# specimen.rfid_state = 1 +# else: +# specimen.rfid_state = 2 +# specimen.rfid = spkwargs_["rfid"] +# specimen.update_time = Utils.get_time() +# BllSpecimen().update(specimen) +# return Utils.true_return('', desc="编辑成功!") +# return Utils.false_return('', desc="查不到该条数据!") +# +# +# @router.post('/deleteSpecimen', summary='删除证物') +# async def deleteSpecimen(request: Request, specimen_id: str): +# """ +# 删除证物 +# :param specimen_id: str +# :return: +# """ +# specimen = BllSpecimen().findEntity(EntitySpecimen.specimen_id == specimen_id) +# if specimen: +# specimen.is_enabled = 1 +# specimen.update_time = Utils.get_time() +# BllSpecimen().update(specimen) +# return Utils.true_return('', desc="删除成功!") +# +# +# @router.post('/storage', summary='入库') +# async def storage(request: Request, body: InStorageModel): +# """ +# specimen_ids 证物id +# storage_type 1 入库,2 入库并消毒 +# """ +# specimen_ids = body.specimen_ids +# storage_type = body.storage_type +# specimens = BllSpecimen().findList(EntitySpecimen.specimen_id.in_(specimen_ids)).all() +# +# if storage_type == 2: +# for specimen in specimens: +# specimen_id = specimen.specimen_id +# wait_disinf_specimen = BllWaitDisinfSpecimen().findEntity( +# and_(EntityWaitDisinfSpecimen.specimen_id == specimen_id, +# EntityWaitDisinfSpecimen.done == 0, +# EntityWaitDisinfSpecimen.is_enabled == 0, +# EntityWaitDisinfSpecimen.user_id == CurrentUser["user_id"])) +# if not wait_disinf_specimen: +# insertdata = { +# 'record_id': str(Utils.get_uuid()), +# 'specimen_id': specimen_id, +# 'specimen_name': specimen.specimen_name, +# 'specimen_code': specimen.specimen_code, +# 'specimen_type': specimen.specimen_type, +# 'specimen_origin': specimen.specimen_origin, +# 'specimen_desc': specimen.specimen_desc, +# 'storage_location': specimen.storage_location, +# 'rfid': specimen.rfid, +# 'remark': specimen.remark, +# 'user_id': CurrentUser["user_id"], +# 'user_name': CurrentUser["username"], +# 'done': 0, +# 'disinf_type': 1, +# } +# BllWaitDisinfSpecimen().insert_wait_disinf_specimen(**insertdata) +# for specimen in specimens: +# kwarge = { +# 'specimen_id': specimen.specimen_id, +# 'inventory_status': 1 # 在库 +# } +# BllSpecimen().updateSpecimen(**kwarge) +# await create_use_log(specimen.specimen_id, '入库') +# return Utils.true_return('', desc="入库成功!") +# +# +# @router.post('/warehouse_out', summary='出库') +# async def storage(request: Request, body: InStorageModel): +# """ +# specimen_ids 证物id +# storage_type 1 出库,2 出库并消毒 +# """ +# specimen_ids = body.specimen_ids +# storage_type = body.storage_type +# specimens = BllSpecimen().findList(EntitySpecimen.specimen_id.in_(specimen_ids)).all() +# if storage_type == 2: +# for specimen in specimens: +# specimen_id = specimen.specimen_id +# wait_disinf_specimen = BllWaitDisinfSpecimen().findEntity( +# and_(EntityWaitDisinfSpecimen.specimen_id == specimen_id, +# EntityWaitDisinfSpecimen.done == 0, +# EntityWaitDisinfSpecimen.is_enabled == 0, +# EntityWaitDisinfSpecimen.user_id == CurrentUser["user_id"])) +# if not wait_disinf_specimen: +# insertdata = { +# 'record_id': str(Utils.get_uuid()), +# 'specimen_id': specimen_id, +# 'specimen_name': specimen.specimen_name, +# 'specimen_code': specimen.specimen_code, +# 'specimen_type': specimen.specimen_type, +# 'specimen_origin': specimen.specimen_origin, +# 'specimen_desc': specimen.specimen_desc, +# 'storage_location': specimen.storage_location, +# 'rfid': specimen.rfid, +# 'remark': specimen.remark, +# 'user_id': CurrentUser["user_id"], +# 'user_name': CurrentUser["username"], +# 'done': 0, +# 'disinf_type': 2, +# } +# BllWaitDisinfSpecimen().insert_wait_disinf_specimen(**insertdata) +# for specimen in specimens: +# specimen_id = specimen.specimen_id +# kwarge = { +# 'specimen_id': specimen_id, +# 'inventory_status': 2 +# } +# BllSpecimen().updateSpecimen(**kwarge) +# await create_use_log(specimen.specimen_id, '出库') +# return Utils.true_return('', desc="出库成功!") +# +# +# @router.post('/addIntoWaitDisinf', summary='添加待消毒') +# async def addIntoWaitDisinf(request: Request, body: InStorageModel): +# ''' +# specimen_ids 证物ids +# ''' +# specimen_ids = body.specimen_ids +# specimen_list = BllSpecimen().findEntity(EntitySpecimen.specimen_id.in_(specimen_ids)) +# if not specimen_list: +# return Utils.false_return('', desc="无此证物!") +# for specimen in specimen_list: +# specimen_id = specimen.specimen_id +# wait_disinf_specimen = BllWaitDisinfSpecimen().findEntity( +# and_(EntityWaitDisinfSpecimen.specimen_id == specimen_id, +# EntityWaitDisinfSpecimen.done == 0, +# EntityWaitDisinfSpecimen.is_enabled == 0, +# EntityWaitDisinfSpecimen.user_id == CurrentUser["user_id"])) +# if not wait_disinf_specimen: +# insertdata = { +# 'record_id': str(Utils.get_uuid()), +# 'specimen_id': specimen_id, +# 'specimen_name': specimen.specimen_name, +# 'specimen_code': specimen.specimen_code, +# 'specimen_type': specimen.specimen_type, +# 'specimen_origin': specimen.specimen_origin, +# 'specimen_desc': specimen.specimen_desc, +# 'storage_location': specimen.storage_location, +# 'rfid': specimen.rfid, +# 'remark': specimen.remark, +# 'user_id': CurrentUser["user_id"], +# 'user_name': CurrentUser["username"], +# 'done': 0, +# 'disinf_type': 3, # 其他 +# } +# await BllWaitDisinfSpecimen().insert_wait_disinf_specimen(**insertdata) +# await create_use_log(specimen.specimen_id, '加入待消毒清单') +# return Utils.true_return('', desc="添加成功!") +# +# +# @router.post('/getWaitDisinfSpecimenList', summary='获取待消毒列表') +# async def getWaitDisinfSpecimenList(request: Request, body: WaitSpecimenSearchModel): +# queryOrm = BllWaitDisinfSpecimen().get_wait_disinf_specimen_list(body) +# pageparam = PageParam(body.page_no, body.page_size) +# waitspecimen_list = BllWaitDisinfSpecimen().queryPage(queryOrm, pageparam) +# pagination = { +# "total_count": pageparam.totalRecords, +# "page_no": body.page_no, +# "page_size": body.page_size +# } +# return Utils.true_return_pagination(pagination, waitspecimen_list, desc="获取成功!") +# +# +# @router.post('/insertConfig', summary='新增消毒配置') +# async def insertConfig(request: Request, body: SpecimenConfigModel): +# ''' +# config_id = Column(String(), comment="id",primary_key=True) +# ozone_disinf = Column(Integer, comment="臭氧消毒") +# uv_disinf = Column(Integer, comment="紫外线消毒") +# ozone_conc = Column(String(50), comment="臭氧浓度") +# disinf_time = Column(String(50), comment="消毒时间") +# temperature = Column(String(50), comment="温度") +# humidity = Column(String(50), comment="湿度") +# created_time = Column(String(50), default=Utils.get_time(), comment="创建时间") +# update_time = Column(String(50), comment="更新时间") +# user_id = Column(String(50), comment="创建人") +# user_name = Column(String(50), comment="用户昵称") +# +# config_name = Column(String(50), comment="配置名称") +# config_desc = Column(String(500), comment="配置描述") +# ''' +# ConfigInsertDict.update(body.items) +# spkwargs_ = ConfigInsertDict +# config_id = str(Utils.get_uuid()) +# +# spkwargs_["config_id"] = config_id +# spkwargs_["user_id"] = CurrentUser["user_id"] +# spkwargs_["user_name"] = CurrentUser["username"] +# BllExperimentConfig().insertConfig(**spkwargs_) +# data = { +# 'config_id': config_id, +# } +# +# return Utils.true_return(data, desc="新增成功!") +# +# +# @router.post('/getConfigList', summary='获取消毒配置') +# async def getConfigList(request: Request): +# body = { +# 'user_id': CurrentUser["user_id"] +# } +# specimen_list = BllExperimentConfig().get_config_list(body).all() +# return Utils.true_return(specimen_list, desc="查询成功!") +# +# +# @router.post('/updateConfig', summary='更新消毒配置') +# async def updateConfig(request: Request, body: SpecimenConfigModel): +# ConfigInsertDict.update(body.items) +# spkwargs_ = ConfigInsertDict +# config_id = spkwargs_["config_id"] +# config = BllExperimentConfig().findEntity(EntityExperimentConfig.config_id == config_id) +# if config: +# config.ozone_disinf = spkwargs_.get("ozone_disinf") +# config.uv_disinf = spkwargs_.get("uv_disinf") +# config.ozone_conc = spkwargs_.get("ozone_conc") +# config.disinf_time = spkwargs_.get("disinf_time") +# config.temperature = spkwargs_.get("temperature") +# config.humidity = spkwargs_.get("humidity") +# config.config_name = spkwargs_.get("config_name") +# config.config_desc = spkwargs_.get("config_desc") +# config.update_time = Utils.get_time() +# BllExperimentConfig().update(config) +# return Utils.true_return('', desc="更新成功!") +# return Utils.false_return('', desc="无此配置!") +# +# +# @router.post('/deleteConfig', summary='删除配置') +# async def deleteConfig(request: Request, config_id: str): +# config = BllExperimentConfig().findEntity(EntityExperimentConfig.config_id == config_id) +# if config: +# config.is_enabled = 1 +# BllExperimentConfig().update(config) +# return Utils.true_return('', desc='删除成功') +# return Utils.false_return('', desc="无此配置!") +# +# +# @router.post('/insertExperiment', summary='新增消毒流程') +# async def insertExperiment(request: Request, body: InsertModel): +# ''' +# +# experiment_id = Column(String(50), comment="消毒流程id", primary_key=True) +# +# ozone_disinf = Column(Integer, comment="臭氧消毒") +# uv_disinf = Column(Integer, comment="紫外线消毒") +# ozone_conc = Column(String(50), comment="臭氧浓度") +# disinf_time = Column(String(50), comment="消毒时间") +# temperature = Column(String(50), comment="浓度") +# humidity = Column(String(50), comment="湿度") +# +# remark = Column(String(500), comment="备注信息") +# created_time = Column(String(50), default=Utils.get_time(), comment="创建时间") +# update_time = Column(String(50), comment="更新时间") +# +# user_id = Column(String(50), comment="创建人") +# user_name = Column(String(50), comment="用户昵称") +# is_enabled = Column(Integer, default=0, comment="是否禁用或删除") +# state = Column(Integer, default=0, comment="状态") +# +# specimens = relationship('EntitySpecimen', secondary=specimen_experiment, back_populates='experiments',comment="证物") +# +# ''' +# InsertDict.update(body.items) +# kwargs_ = InsertDict +# experiment_id = str(Utils.get_uuid()) +# +# kwargs_["experiment_id"] = experiment_id +# kwargs_["user_id"] = CurrentUser["user_id"] +# kwargs_["user_name"] = CurrentUser["username"] +# kwargs_["name"] = "证人证物等{}件证物".format(len(kwargs_["specimens"])) +# BllExperiment().insertExperiment(**kwargs_) +# +# data = { +# "experiment_id": kwargs_["experiment_id"], +# } +# +# # await startExperiment(request, experiment_id) +# +# return Utils.true_return(data, desc="新增成功!") +# +# +# @router.get('/startExperiment/{experiment_id}', summary='开始消毒流程') +# async def startExperiment(request: Request, experiment_id: str): +# experiment_obj = BllExperiment().findEntity( +# and_(EntityExperiment.experiment_id == experiment_id, EntityExperiment.is_enabled == 0)) +# if not experiment_obj: +# return Utils.false_return('', desc="查不到该实验!") +# kwargs_ = { +# "experiment_id": experiment_id, +# } +# global START_EXPERIMENT_ID +# START_EXPERIMENT_ID = experiment_id +# r = ExperimentData(experiment_id) +# r.init() +# mode = experiment_obj.ozone_disinf + experiment_obj.uv_disinf * 2 +# params = { +# "temperature": int(experiment_obj.temperature), +# "humidity": int(experiment_obj.humidity), +# "ozone": int(experiment_obj.ozone_conc), +# "time": int(experiment_obj.disinf_time), +# "mode": mode +# } +# +# url = f'http://100.64.0.1/api/cabinet/v1/setting' +# print('开始消毒设置下位机参数param:::', params) +# async with httpx.AsyncClient() as client: +# try: +# response = await client.post(url, json=params) +# except Exception: +# return Utils.false_return(code=400, desc='下位机通讯失败') +# if not response: +# return Utils.false_return('', desc="试验失败,下位机通讯异常!") +# print('开始消毒设置下位机响应:::', response.json()) +# return Utils.true_return('', desc="成功") +# +# @router.get('/stop', summary='停止消毒流程') +# async def stop(): +# url = f'http://100.64.0.1/api/cabinet/v1/stop' +# async with httpx.AsyncClient() as client: +# try: +# response = await client.get(url, timeout=4) +# data = response.json() +# return Utils.true_return('', desc=data) +# except Exception as e: +# return Utils.false_return(code=400, desc='下位机通讯失败') +# if not response: +# return Utils.false_return('', desc="试验失败,下位机通讯异常!") +# print('停止消毒设置下位机响应:::', response.json()) +# return Utils.true_return('', desc="成功") +# +# +# @router.post('/updateExperiment', summary='更新试验流程,保存试验结果') +# async def updateExperiment(request: Request, body: UpdateModel): +# """ +# Args: +# request (Request): _description_ +# """ +# experiment_obj = BllExperiment().findEntity( +# and_(EntityExperiment.experiment_id == body.id, EntityExperiment.is_enabled == 0)) +# if not experiment_obj: +# return Utils.false_return('', desc="查不到该实验!") +# body.items['experiment_id'] = body.id +# obj = BllExperiment().updateExperiment(**body.items) +# specimen = BllSpecimen().findEntity(EntitySpecimen.specimen_id == experiment_obj.specimen_id) +# if obj.test_result == 1: +# if not specimen.min_ignition_energy: +# ct = { +# 'specimen_id': specimen.specimen_id, +# 'min_ignition_energy': obj.ignition_energy +# } +# BllSpecimen().updateSpecimen(**ct) +# elif specimen.min_ignition_energy and float(obj.ignition_energy) < specimen.min_ignition_energy: +# ct = { +# 'specimen_id': specimen.specimen_id, +# 'min_ignition_energy': obj.ignition_energy +# } +# BllSpecimen().updateSpecimen(**ct) +# r = ExperimentData(body.id) +# r.finish() +# return Utils.true_return(obj, desc="成功") +# +# +# @router.post('/stopExperiment', summary='消毒流程结束') +# async def stopExperiment(request: Request, body: StopModel): +# experiment = BllExperiment().findEntity( +# and_(EntityExperiment.experiment_id == body.id, EntityExperiment.is_enabled == 0)) +# if not experiment: +# return Utils.false_return('', desc="查不到该消毒!") +# +# # 试验完成 待消毒清单 done = 1 specimens +# # 物证 disinfection_state = 1 +# +# # 结束试验都传0 +# params = { +# "temperature": 20, # 温度设置(18~30℃) +# "humidity": 50, # 湿度设置(40~70%RH) +# "ozone": 1, # 臭氧设置(1为开启,0为关闭) +# "time": 30, # 消毒时长(单位:分钟) +# "mode": 2, # 消毒模式(0:关闭, 1:臭氧, 2:紫外线消毒) +# "light_config": 2 # LED模式(0:常关, 1:常开, 2:自动模式) +# } +# +# print('结束消毒设置下位机参数param:::', params) +# url = f'http://100.64.0.1/api/cabinet/v1/setting' +# async with httpx.AsyncClient() as client: +# try: +# response = await client.post(url, json=params) +# except Exception: +# return Utils.false_return(code=400, desc='下位机通讯失败') +# if not response: +# return Utils.false_return('', desc="试验失败,下位机通讯异常!") +# +# print('结束消毒设置下位机响应:::', dict(response)) +# return Utils.true_return('', desc="消毒结束!") +# +# +# async def create_use_log(specimen_id, operation_type): +# specimen = BllSpecimen().findEntity(EntitySpecimen.specimen_id == specimen_id) +# kwargs_ = { +# "user_id": CurrentUser["user_id"], +# "user_name": CurrentUser["username"], +# "specimen_id": specimen_id, +# "specimen_code": specimen.specimen_code, +# "specimen_name": specimen.specimen_name, +# "operation_type": operation_type, +# } +# BllUselog().insert_log(**kwargs_) +# +# +# @router.post('/get_experiment_list', summary='获取所有消毒流程') +# async def get_all_experiment(request: Request, body: SearchWordModel): +# """ +# 获取所有试验 +# :body SearchWordModel +# :return: +# """ +# +# pageparam = PageParam(body.page_no, body.page_size) +# queryOrm = BllExperiment().getExperimentList(body) +# experiment_list = BllExperiment().queryPage(queryOrm, pageparam) +# for experiment in experiment_list: +# experiment.specimen = [i.to_dict() for i in experiment.specimens] +# pagination = { +# "total_count": pageparam.totalRecords, +# "page_no": body.page_no, +# "page_size": body.page_size +# } +# return Utils.true_return_pagination(pagination, experiment_list, desc="获取成功!") +# +# +# @router.get('/experiment_info', summary='获取试验流程详情') +# async def experiment_info(request: Request, experiment_id: str): +# """ +# 获取正式试验详情 +# :return: +# """ +# experiment_obj = BllExperiment().findEntity(EntityExperiment.experiment_id == experiment_id) +# base_data = ExperimentData(experiment_id).get_raw_data() +# data = { +# 'base_data': base_data, +# 'experiment_obj': experiment_obj, +# } +# return Utils.true_return(data=data, desc="获取成功!") +# +# +# @router.get('/get_temperature') +# async def get_temperature(): +# try: +# url = f'http://100.64.0.1/api/cabinet/v1/sensor' +# print('下位机获取温度通信:::') +# async with httpx.AsyncClient() as client: +# response = await client.get(url, timeout=4) +# environment_info = response.json() +# print('下位机获取温度通信:::', environment_info) +# return Utils.true_return(data=environment_info, desc="获取成功!") +# except Exception as e: +# return Utils.false_return(code=400, desc=f"下位机获取温度通信失败,{str(e)}") +# +# +# @router.get('/get_machine_state') +# async def get_machine_state(): +# try: +# url = f'http://100.64.0.1/api/cabinet/v1/state' +# # print('开始获取下位机状态值:::') +# async with httpx.AsyncClient() as client: +# response = await client.get(url, timeout=4) +# status = response.json() +# # print('结束获取下位机状态值:::', status) +# if status: +# url = "http://100.64.0.1/api/cabinet/v1/sensor" +# response_sensor = await client.get(url, timeout=4) +# status_sensor = response_sensor.json() +# temperature = status_sensor.get("temperature") +# humidity = status_sensor.get("humidity") +# voc = status_sensor.get("voc") +# ozone = status_sensor.get("ozone") +# # print('结束获取下位机传感器状态值:::', status_sensor) +# before_status = StatusDict["status"] +# StatusDict["status"] = status.get('state') +# StatusDict["led"] = status.get("led") +# StatusDict["door"] = status.get("door") +# StatusDict["fans"] = status.get("fans") +# StatusDict["compressor"] = status.get("compressor") +# StatusDict["heating"] = status.get("heating") +# StatusDict["ozonizer"] = status.get("ozonizer") +# StatusDict["disinfectant"] = status.get("disinfectant") +# StatusDict["pump"] = status.get("pump") +# StatusDict["work_time"] = status.get("work_time") if status.get("work_time") else 20 +# StatusDict["pump"] = status.get("pump") +# sensor = status.get("sensor") +# StatusDict["temperature_sensor"] = sensor & 0b001 +# StatusDict["voc_sensor"] = sensor & 0b010 +# StatusDict["ozone_sensor"] = sensor & 0b100 +# +# StatusDict["temperature"] = temperature +# StatusDict["humidity"] = humidity +# StatusDict["voc"] = voc +# StatusDict["ozone"] = ozone +# # print('当前下位机状态值字典::::', dict(StatusDict)) +# +# # print('当前设置臭氧、温度::::', InsertDict["ozone_conc"], InsertDict["temperature"]) +# +# # 臭氧超出设置的值5以上返回错误 +# if status.get('state') == 2 and InsertDict["ozone_conc"] and voc > float(InsertDict["ozone_conc"]) - 5: +# return Utils.false_return(desc="臭氧浓度超出设置上限!") +# +# # 温度超出设置的值5以上返回错误 +# if status.get('state') == 2 and InsertDict["temperature"] and voc > float( +# InsertDict["temperature"]) - 5: +# return Utils.false_return(desc="温度超出设置上限!") +# # 消毒完成时修改待消毒状态 +# print('消毒状态::::', status.get('state'), before_status) +# if before_status == 2 and status.get('state') == 0: +# await open_door(0) +# global END_TIME , START_EXPERIMENT_ID +# END_TIME = datetime.datetime.now() # 记录试验开始时间 +# print('消毒完成:::', START_EXPERIMENT_ID) +# wait_obj = BllWaitDisinfSpecimen().findEntity( +# EntityWaitDisinfSpecimen.specimen_id == START_EXPERIMENT_ID) +# wait_obj.done = 1 +# BllWaitDisinfSpecimen().update(wait_obj) +# +# return Utils.true_return(data=dict(StatusDict), desc="获取成功!") +# +# # try: +# # if ExperimentData.current_obj: +# # pre_status = ExperimentData.current_obj.get_last_status()["status"] # 上一次状态 +# # if status.status != pre_status: +# # pre_status_data = ExperimentData.current_obj.get_last_status() +# # current_work_status = await get_current_work_status(ExperimentData.id) +# # StatusDict['work_status'] = current_work_status +# # if current_work_status != 99: +# # status_data = ExperimentData.current_obj.update_status(status) # 试验数据记录 +# # now_status = status['status'] # 本次状态 +# # curr_result = await update_experiment(ExperimentData.id, pre_status, now_status, pre_status_data) +# # +# # except Exception as e: +# # logger.exception(e) +# +# except Exception as e: +# return Utils.false_return(code=400, desc=f"下位机获取温度通信失败,{str(e)}") +# +# +# @router.get('/open_door', summary='打开门') +# async def open_door(open: int = None, confirm: int = 0): +# if StatusDict["status"] and StatusDict["status"] == 2: +# return Utils.false_return(desc=f"实验中不能开门!") +# if open > 0 and confirm == 0 and END_TIME: +# # 获取当前时间 +# current_time = datetime.datetime.now() +# +# # 计算时间差 +# time_diff = current_time - END_TIME +# # 判断时间差是否小于等于 30 分钟 +# if time_diff > datetime.timedelta(seconds=0): +# remaining_minutes =30 - int(time_diff.total_seconds() // 60) # 剩余的分钟数 +# if remaining_minutes <= 30 and remaining_minutes >= 0: +# return Utils.false_return(desc=f"结束试验后等待30分钟才能开门,剩余{remaining_minutes}分钟") +# +# # 定义请求头 +# headers = { +# "Content-Type": "application/json" # 确保以 JSON 格式发送数据 +# } +# params = { +# "open": open +# } +# url = 'http://100.64.0.1/api/cabinet/v1/door' +# +# async with httpx.AsyncClient() as client: +# try: +# logger.info(f"Request URL: {url}") +# logger.info(f"Request Headers: {headers}") +# logger.info(f"Request Body: {params}") +# +# response = await client.post(url, json=params, headers=headers) +# +# response.raise_for_status() # 如果响应码是 4xx/5xx,会抛出异常 +# +# # 解析响应 +# response_data = response.json() +# +# # 打印响应数据 +# print('响应数据:', response_data) +# +# if response_data.get("code") == 200: +# print("操作成功!门已开/关。") +# elif response_data.get("code") == 405: +# print("参数错误或操作失败,请检查请求数据。") +# else: +# print("未知响应:", response_data) +# except Exception as e: +# logger.error(f"Request failed: {e}") +# return {"code": 400, "desc": "下位机通讯失败"} +# +# if response.status_code != 200: +# logger.warning("Communication with the lower machine failed.") +# return {"code": 503, "desc": "试验失败,下位机通讯异常!"} +# +# return {"code": 200, "desc": "门已成功打开"} +# +# +# @router.get('/open_light', summary='打开门') +# async def open_light(open: int = None): +# # 定义请求头 +# headers = { +# "Content-Type": "application/json" # 确保以 JSON 格式发送数据 +# } +# params = { +# "light": open +# } +# url = 'http://100.64.0.1/api/cabinet/v1/led' +# +# async with httpx.AsyncClient() as client: +# try: +# logger.info(f"Request URL: {url}") +# logger.info(f"Request Headers: {headers}") +# logger.info(f"Request Body: {params}") +# +# response = await client.post(url, json=params, headers=headers) +# +# response.raise_for_status() # 如果响应码是 4xx/5xx,会抛出异常 +# +# # 解析响应 +# response_data = response.json() +# +# # 打印响应数据 +# print('响应数据:', response_data) +# +# if response_data.get("code") == 200: +# print("操作成功!灯已开/关。") +# elif response_data.get("code") == 405: +# print("参数错误或操作失败,请检查请求数据。") +# else: +# print("未知响应:", response_data) +# except Exception as e: +# logger.error(f"Request failed: {e}") +# return {"code": 400, "desc": "下位机通讯失败"} +# +# if response.status_code != 200: +# logger.warning("Communication with the lower machine failed.") +# return {"code": 503, "desc": "试验失败,下位机通讯异常!"} +# +# return {"code": 200, "desc": "灯已成功打开"} +# +# +# @router.get("/shut_down", summary="关闭服务器") +# def shut_down(request: Request): +# # 提升权限 +# # if not ctypes.windll.shell32.IsUserAnAdmin(): +# # os.execl(sys.executable, sys.executable, *sys.argv) +# response = modbus_server.StopExperiment(**StopDict) +# if not response: +# return Utils.false_return('', desc="复位失败,下位机通讯异常!") +# # 关闭计算机 +# os.system("shutdown -s -t 0") +# return Utils.true_return("", desc="关机成功") +# +# +# # 获取当前试验状态 +# async def get_current_work_status(experiment_id: str): +# experiment = BllExperiment().findEntity( +# and_(EntityExperiment.experiment_id == experiment_id, EntityExperiment.is_enabled == 0)) +# if not experiment: +# return Utils.false_return('', desc="实验不存在!") +# return experiment.work_status +# +# +# async def update_experiment(experiment_id: str, pre_status: int, now_status: int, pre_status_data): +# # if now_status == pre_status: +# # return '' +# +# experiment_obj = BllExperiment().findEntity( +# and_(EntityExperiment.experiment_id == experiment_id, EntityExperiment.is_enabled == 0)) +# max_pressure = experiment_obj.max_pressure if experiment_obj.max_pressure else 0 +# if experiment_obj.max_pressure < float(StatusDict['s_pressure'] or 0): +# max_pressure = float(StatusDict['s_pressure'] or 0) +# max_temp = experiment_obj.max_temp if experiment_obj.max_temp else 0 +# if experiment_obj.max_temp < float(StatusDict['temperature'] or 0): +# max_temp = float(StatusDict['temperature'] or 0) +# +# if now_status == 5 and pre_status != 5: +# kk = { +# 'experiment_id': experiment_id, +# 'max_temp': max_temp, +# 'max_pressure': max_pressure, +# 'work_status': 99, +# } +# BllExperiment().updateExperiment(**kk) +# else: +# kk = { +# 'experiment_id': experiment_id, +# 'max_temp': max_temp, +# 'max_pressure': max_pressure, +# 'work_status': now_status, +# } +# BllExperiment().updateExperiment(**kk) +# +# specimen = BllSpecimen().findEntity( +# and_(EntitySpecimen.specimen_id == experiment_obj.specimen_id, EntitySpecimen.is_enabled == 0)) +# max_pressure = specimen.max_pressure if specimen.max_pressure else 0 +# if specimen.max_pressure < float(StatusDict['s_pressure'] or 0): +# max_pressure = float(StatusDict['s_pressure'] or 0) +# max_temp = specimen.max_temp if specimen.max_temp else 0 +# if specimen.max_temp < float(StatusDict['temperature'] or 0): +# max_temp = float(StatusDict['temperature'] or 0) +# kk = { +# 'specimen_id': specimen.id, +# 'max_temp': max_temp, +# 'max_pressure': max_pressure, +# } +# BllSpecimen().updateSpecimen(**kk) +# +# +# @router.post("/export_table", summary="导出报表按样品") +# async def export_table(request: Request, body: ExportTableModel): +# """报表导出 +# +# Args: +# request (Request): _description_ +# body (ExportTableModel): +# specimen_id: 样品id +# download_type: 下载类型(usb:导出到u盘),传空为返回到浏览器 +# """ +# specimen_obj = BllSpecimen().findEntity(EntitySpecimen.specimen_id == body.specimen_id) +# +# experiments = BllExperiment().findList(EntityExperiment.specimen_id == body.specimen_id).all() +# +# all_data = [] +# file_name = "" +# specimen_data = { +# '样品名称': specimen_obj.name, +# '试验次数': specimen_obj.experiment_times, +# '最高温度': specimen_obj.max_temp, +# '最高压力': specimen_obj.max_pressure, +# '最后试验时间': specimen_obj.update_time, +# } +# +# base_data_title = [ +# '实验时间', '实验人', '样品名称', '当前次数', '粉尘重量g', '点火能量', '喷粉压力', '最大温度', '结果' +# ] +# data = { +# "file_name": file_name, +# "specimen_data": specimen_data, +# "sheet_title": specimen_obj.created_time.replace(":", "-"), +# "title_list": ["{}试验报告".format(specimen_obj.name)], +# "base_data_title": base_data_title +# } +# base_data_list = [] +# for experiment in experiments: +# base_data = { +# "experiment_id": experiment.experiment_id, +# "样品名称": experiment.name, +# "实验人": experiment.user_name, +# "实验时间": experiment.created_time, +# "当前次数": experiment.experiment_times, +# "粉尘重量g": experiment.specimen_quantity, +# "点火能量": experiment.ignition_energy, +# "喷粉压力": experiment.powdered_coating_pressure, +# "最大温度": experiment.max_temp, +# "结果": experiment.test_result, +# 'other_data': ExperimentData(experiment.experiment_id).get_raw_data() +# +# } +# base_data_list.append(base_data) +# data['base_data_list'] = base_data_list +# all_data.append(data) +# +# excel_obj = ExcelExport(file_name, all_data, '') +# binary, encoded_filename = excel_obj.experiment_table() +# if isinstance(binary, int): +# if binary == 404: +# return Utils.false_return(desc="U盘未找到") +# return Utils.true_return(desc="导出成功") +# +# # 导出到浏览器 +# return Response(content=binary.getvalue(), +# media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", +# headers={'Content-Disposition': f'attachment; filename={encoded_filename}'}) +# +# +# @router.get("/exit", summary="退出服务") +# def exit_server(request: Request): +# """退出服务""" +# for proc in psutil.process_iter(['pid', 'name']): +# print(proc.name()) +# if proc.name() == "chrome.exe": +# try: +# proc.kill() +# print(f"进程 {proc.pid} 已被终止。") +# except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess) as e: +# print(f"无法终止进程 {proc.pid}:{e}") +# sys.exit(0) +# +# +# @router.get('/userfaceLogin', ) +# def userfaceLogin(request: Request, id: str): +# try: +# user = BllUser().facelogin(id) +# end_time = datetime.datetime.strptime('2082-08-21 01:01', '%Y-%m-%d %H:%M') +# now_time = datetime.datetime.now() +# if now_time > end_time or os.path.exists('gq.txt'): +# if not os.path.exists('gq.txt'): +# gqFile = open('gq.txt', 'w+', encoding="utf-8") +# gqFile.close() +# return Utils.false_return('', "程序已过期!", 0) +# if user is None: +# return Utils.false_return('', "用户名或密码错误!", 0) +# else: +# return Utils.true_return(user, "登录成功!", 1) +# except Exception as e: +# print("设备初始化失败,请重启仪器") +# return Utils.false_return('', '设备初始化失败,请重启仪器。' + str(e), 0) +# +# +# @router.post('/sendMsg', summary='向下位机发送命令') +# async def sendMsg(request: Request, body: SendMesModel): +# """ +# 向下位机发送命令 +# body (SendMesModel): +# name: str, ZHIFENG +# status: int, +# 0001 开始制粉 +# 0002 制粉完成 +# """ +# # 制粉指令 +# if body.name == "ZHIFENG": +# response = modbus_server.Send_Mes(Addr=ZHIFENG, status=body.status) +# else: +# return Utils.false_return('', desc="指令错误!") +# if response != None: +# return Utils.true_return('', desc="发送成功!") +# else: +# return Utils.false_return('', desc="发送失败!") +# +# +# async def check_last_disinfection_time(): +# """ +# 放松下位机指令 +# """ +# specimen_list = BllSpecimen().findList(EntitySpecimen.is_enabled == 0).all() +# for specimen in specimen_list: +# disinfection_state = 0 +# if specimen.last_disinf_time: +# today = datetime.datetime.now() +# last_disinf_datetime = datetime.datetime.strptime(specimen.last_disinf_time, "%Y-%m-%d %H:%M:%S") +# # 计算两个日期之间的差异 +# difference = today - last_disinf_datetime +# # 获取差异的天数 +# days_difference = difference.days +# +# ''' +# 1:三天内已消毒:在3天之内有消毒记录; +# 2:一周内已消毒:在7天之内有消毒记录; +# 3:一月内已消毒:在30天之内有消毒记录; +# 4:三月内已消毒:在90天之内有消毒记录; +# 5:半年内已消毒:在180天之内有消毒记录; +# 6:一年内已消毒:在365天之内有消毒记录; +# 7:超过一年未消毒:最近一次消毒已超过365天 +# ''' +# if days_difference <= 3: +# disinfection_state = 1 +# elif days_difference <= 7: +# disinfection_state = 2 +# elif days_difference <= 30: +# disinfection_state = 3 +# elif days_difference <= 90: +# disinfection_state = 4 +# elif days_difference <= 180: +# disinfection_state = 5 +# elif days_difference <= 365: +# disinfection_state = 6 +# else: +# disinfection_state = 7 +# +# specimen.disinfection_state = disinfection_state +# BllSpecimen().update(specimen) +# +# return +# +# +# # def start_empty_scheduler(): +# # """启动定时任务,每天0点执行一次""" +# # scheduler = AsyncIOScheduler(timezone=datetime.timezone('Asia/Shanghai')) +# # # scheduler.add_job(empty_drug_job, 'interval', seconds=30) +# # scheduler.add_job(check_last_disinfection_time, 'cron', hour=0, minute=0) +# # scheduler.start() +# +# +# modbus_server = ModbusConnection() +# manager = ConnectionManager() +# # vk70_server = VK70xUMCDAQ() diff --git a/app/api/ShortCircuit.py b/app/api/ShortCircuit.py new file mode 100644 index 0000000..6575812 --- /dev/null +++ b/app/api/ShortCircuit.py @@ -0,0 +1,704 @@ +import asyncio +import io +import os +import datetime +import os +import sqlite3 +import sys +import time + +import psutil +from fastapi import APIRouter, Request, Response, BackgroundTasks +from sqlalchemy import and_, desc + +from app.lib import StaticData, usb +from app.lib.Camera import CvCamera +from app.lib.ModBus import ModbusConnection +from app.lib.StaticData import * +from app.lib.Utils import Utils +from app.lib.websocket_manageer import ConnectionManager +from app.models import * +from app.models.Business import PageParam, BllUser, BllRole, BllDeviceConfig, BllExperimentSpecimen, \ + BllExperimentInfo, BllChannel, BllExperimentReport +from app.models.DateEntity import ExperimentSpecimen, ExperimentInfo, Channel, ExperimentReport +from app.utils.excel_util import ExcelExport +from app.validators.api import * + +shiyan_time = 0 +END_TIME = None +START_EXPERIMENT_ID = None + +router = APIRouter(prefix='/api/shortcircuit', tags=['短路实验']) + +#视频查询 +class VideoModel(BaseModel): + id: str + + + + + + +@router.post('/add_user', summary='创建用户') +async def add_user(request: Request, body: SignUpdateModel): + """ + 创建用户 + :body SignUpdateModel + :return: + """ + user = BllUser().findEntity(and_(EntityUser.user_name == body.username, EntityUser.is_enabled == 0)) + if user: + return Utils.false_return('', desc="账号已注册!") + entity = EntityUser() + entity.user_id = str(Utils.get_uuid()) + entity.user_pwd = Utils.MD5(body.password) + entity.user_name = body.username + entity.real_name = body.name + entity.role_id = body.role_id + entity.user_sex = body.sex + entity.update_time = Utils.get_time() + BllUser().insert(entity) + return Utils.true_return('', desc="创建成功!") + + +@router.post('/login', summary='用户登录') +async def login(request: Request, body: SignInModel): + """ + 用户登录 + :body SignInModel + :return: + """ + user = BllUser().login(body.username, body.password) + if user: + CurrentUser["username"] = user.user_name + CurrentUser["user_id"] = user.user_id + return Utils.true_return(user, desc="登录成功!") + return Utils.false_return('', desc="登录失败,账号与密码不匹配!") + + +@router.post('/user_list', summary='用户列表') +async def user_list(request: Request, body: UserModel): + """ + 用户列表 + :return: + """ + queryOrm = BllUser().getUserList() + pageparam = PageParam(body.page_no, body.page_size) + user_list = BllUser().queryPage(queryOrm, pageparam) + pagination = { + "total_count": pageparam.totalRecords, + "page_no": body.page_no, + "page_size": body.page_size + } + return Utils.true_return_pagination(pagination, user_list, desc="获取成功!") + + +@router.put('/{user_id}', summary='编辑用户') +async def update_user(user_id: str, body: SignUpdateModel): + """ + 编辑用户 + :param user_id: str + :body SignUpdateModel + :return: + """ + user = BllUser().findEntity(and_(EntityUser.user_id == user_id, EntityUser.is_enabled == 0)) + if user: + user.user_pwd = Utils.MD5(body.password) + user.user_name = body.username + user.real_name = body.name + user.role_id = body.role_id + user.user_sex = body.sex + user.update_time = Utils.get_time() + BllUser().update(user) + return Utils.true_return(user, desc="编辑成功!") + return Utils.false_return('', desc="查不到该账号!") + + +@router.delete('/delete_user/{user_id}', summary='删除用户') +async def del_user(user_id: str): + """ + 删除用户 + :param user_id: str + :return: + """ + user = BllUser().findEntity(and_(EntityUser.user_id == user_id, EntityUser.is_enabled == 0)) + if user: + user.is_enabled = 1 + user.update_time = Utils.get_time() + BllUser().update(user) + return Utils.true_return('', desc="删除成功!") + return Utils.false_return('', desc="查不到该账号!") + + +@router.get('/get_role_list', summary='获取角色列表') +async def get_role_list(request: Request): + """ + 获取角色列表 + :return: + """ + role_list = BllRole().findList(EntityRole.is_enabled == 0).all() + data_list = [{"role_name": i.role_name, "role_id": i.role_id} for i in role_list] + return Utils.true_return(data_list, desc="查询成功!") + + + +@router.get("/shut_down", summary="关闭服务器") +def shut_down(request: Request): + # 提升权限 + # if not ctypes.windll.shell32.IsUserAnAdmin(): + # os.execl(sys.executable, sys.executable, *sys.argv) + kwargs_ = { + "channel_list": ['全部'], + } + response = modbus_server.StopExperiment(kwargs_) + camera.release() + if not response: + return Utils.false_return('', desc="复位失败,下位机通讯异常!") + # 关闭计算机 + os.system("shutdown -s -t 0") + return Utils.true_return("", desc="关机成功") + + +@router.get("/exit", summary="退出服务") +def exit_server(request: Request): + """退出服务""" + for proc in psutil.process_iter(['pid', 'name']): + print(proc.name()) + if proc.name() == "chrome.exe": + try: + proc.kill() + print(f"进程 {proc.pid} 已被终止。") + except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess) as e: + print(f"无法终止进程 {proc.pid}:{e}") + sys.exit(0) + + +@router.get('/userfaceLogin', ) +def userfaceLogin(request: Request, id: str): + try: + user = BllUser().facelogin(id) + end_time = datetime.datetime.strptime('2082-08-21 01:01', '%Y-%m-%d %H:%M') + now_time = datetime.datetime.now() + if now_time > end_time or os.path.exists('gq.txt'): + if not os.path.exists('gq.txt'): + gqFile = open('gq.txt', 'w+', encoding="utf-8") + gqFile.close() + return Utils.false_return('', "程序已过期!", 0) + if user is None: + return Utils.false_return('', "用户名或密码错误!", 0) + else: + return Utils.true_return(user, "登录成功!", 1) + except Exception as e: + print("设备初始化失败,请重启仪器") + return Utils.false_return('', '设备初始化失败,请重启仪器。' + str(e), 0) + +@router.get('/getConfig' ,summary="获取设备配置") +async def getConfig(request: Request ): + config_info = dict(StaticData.DeviceConfig) + config_info.update(StaticData.DeviceStatus) + return config_info + +@router.get('/getChannelList' ,summary="获取通道列表") +async def getChannelList(request: Request ,status :int = 0): + return [item for item in StaticData.ChannelDatas if item.status == status] + +@router.post('/updateConfig' ,summary="更新设备配置") +async def updateConfig(request: Request , body :DeviceConfigModel): + StaticData.DeviceConfig.update(body) + device_config_list = BllDeviceConfig().findAllList() + device_config = device_config_list[0] + # 假设 device_config 是从数据库查询出来的模型实例 + for key, value in body.__dict__.items(): + if hasattr(device_config, key): # 确保属性存在 + setattr(device_config, key, value) + BllDeviceConfig().update(device_config) + return StaticData.DeviceConfig + +@router.get('/getChannelInfo' ,summary="获取通道详情") +async def getChannelInfo(request: Request ,channel_id :str): + chnanels = [item for item in StaticData.ChannelDatas if item.id == channel_id] + experiment_ids = [item.experiment_id for item in StaticData.ChannelDatas if item.id == channel_id and hasattr(item, 'experiment_id')] + report_list = [] + if len(experiment_ids) > 0: + report_list = BllExperimentReport().findList(and_(ExperimentReport.channel_id == channel_id, ExperimentReport.experiment_id == experiment_ids[0])).all() + result = { + "base_info": chnanels[0], + "report_list": report_list + } + return result + +@router.get('/getStatus', summary="获取下位机状态") +async def getStatus(request: Request, background_tasks: BackgroundTasks): + """ + 该路由会返回一个立即的响应,但不会阻塞周期性任务的执行。 + """ + background_tasks.add_task(run_periodic_task) # 启动后台任务 + return {"message": "已启动周期性任务,每30秒调用一次下位机状态获取。"} + + +@router.get('/experimentOver' ,summary="停止试验") +async def experimentOver(request: Request ,experiment_id :str, cause :str = ''): + + path = Utils.get_db_path() + print(path) + # 获取数据库连接(假设你使用 sqlite) + conn = sqlite3.connect(path) # 数据库路径 + cursor = conn.cursor() + + + try: + # 开始事务 + conn.execute("BEGIN TRANSACTION") + # 更新实验信息 状态 0:未开始 1:进行中 2:已完成 3:已取消 + curr_time = Utils.get_time() + experimentInfo = BllExperimentInfo().findEntity(ExperimentInfo.id == experiment_id) + if experimentInfo.state != 1: + return Utils.false_return('', '试验未开始!') + channel_list = [item.name for item in StaticData.ChannelDatas if hasattr(item, 'experiment_id') and item.experiment_id == experiment_id] + kwargs_ = { + "channel_list": channel_list, + } + modbus_server.StopExperiment(kwargs_) + experimentInfo.state = 3 + experimentInfo.end_time = curr_time + experimentInfo.desc = f'手动停止试验,原因:{cause}' + BllExperimentInfo().update(experimentInfo) + # 更新通道状态为空闲 + channel_ids = experimentInfo.channel_id.split(',') + channel_experiment_dict = {experimentInfo.channel_id: None} + await update_channel_status(channel_ids, curr_time,0,channel_experiment_dict,None) + + # 提交事务 + conn.commit() + + except ChannelStatusError as e: + # 捕获自定义异常并返回给前端 + conn.rollback() # 回滚事务 + print(f'发生异常:::::::{e}') + return Utils.false_return('', str(e)) + + except Exception as e: + # 捕获非自定义异常(如数据库错误等) + conn.rollback() # 回滚事务 + print(f'发生异常:::::::{e}') + # 返回一个通用错误信息给前端 + return Utils.false_return('', f"发生了系统错误: {str(e)}") + + finally: + # 确保关闭数据库连接 + conn.close() + + + return Utils.true_return('', '试验已停止!') + + +# @router.post('/startExperiment' ,summary="开始实验") +# async def startExperiment(request: Request , body :ExperimentParamModel): +# try: +# curr_time = Utils.get_time() +# # 调用检查通道状态的方法 +# check_channel_status(body.channel_ids, StaticData.ChannelDatas) +# # md5值 样品名称+电池型号+样品编号+电池规格+电池厂家+备注 +# await check_specimen_exist(body) +# # 保存实验信息 +# await save_experiment_info(body) +# +# # 更新通道状态为正在实验 +# channel_ids = body.channel_ids.split(',') +# await update_channel_status(channel_ids, curr_time,1) +# +# +# +# except ChannelStatusError as e: +# # 捕获异常并返回给前端 +# return Utils.false_return('', str(e)) +# +# +# return None + +@router.post('/startExperiment', summary="开始实验") +async def startExperiment(request: Request, body: ExperimentParamModel): + path = Utils.get_db_path() + print(path) + # 获取数据库连接(假设你使用 sqlite) + conn = sqlite3.connect(path) # 数据库路径 + cursor = conn.cursor() + + + try: + # 开始事务 + conn.execute("BEGIN TRANSACTION") + + curr_time = Utils.get_time() + timestamp = time.time() + # 调用检查通道状态的方法 + check_channel_status(body.channel_ids, StaticData.ChannelDatas) + # 下位机发送指令 + channel_ids = body.channel_ids.split(',') + channel_list = [item.name for item in StaticData.ChannelDatas if channel_ids.__contains__(item.id)] + kwargs_ = { + "channel_list": channel_list, + "temperature": body.temperature, + "work_time": body.expose_time, + } + modbus_server.Experiment(kwargs_) + # md5值 样品名称+电池型号+样品编号+电池规格+电池厂家+备注 + await check_specimen_exist(body) + # 保存实验信息 + await save_experiment_info(body) + + # 更新通道状态为正在实验 + channel_ids = body.channel_ids.split(',') + await update_channel_status(channel_ids, curr_time,1,body.channel_experiment_dict,timestamp) + + # 提交事务 + conn.commit() + + except ChannelStatusError as e: + # 捕获自定义异常并返回给前端 + conn.rollback() # 回滚事务 + print(f'发生异常:::::::{e}') + return Utils.false_return('', str(e)) + + except Exception as e: + # 捕获非自定义异常(如数据库错误等) + conn.rollback() # 回滚事务 + print(f'发生异常:::::::{e}') + # 返回一个通用错误信息给前端 + return Utils.false_return('', f"发生了系统错误: {str(e)}") + + finally: + # 确保关闭数据库连接 + conn.close() + + return Utils.true_return('', '实验已开始!') + + + + +@router.get('/getAllSpecimen' ,summary="获取所有样本") +async def getAllSpecimen(request: Request ): + experiment_specimen_list = BllExperimentSpecimen().findAllList() + + return experiment_specimen_list + + +@router.post('/pageExperimentInfo' ,summary="分页获取试验信息") +async def getAllSpecimen(request: Request ,body: SearchWordModel): + pageparam = PageParam(body.page_no, body.page_size) + queryOrm = BllExperimentInfo().getExperimentList(body) + experiment_list = BllExperimentInfo().queryPage(queryOrm, pageparam) + pagination = { + "total_count": pageparam.totalRecords, + "page_no": body.page_no, + "page_size": body.page_size + } + return Utils.true_return_pagination(pagination, experiment_list, desc="获取成功!") + +@router.get('/getExperimentInfo' ,summary="获取实验详情") +async def getExperimentInfo(request: Request ,experiment_id :str): + experiment_info = BllExperimentInfo().findEntity(ExperimentInfo.id == experiment_id) + report_list = BllExperimentReport().findList((ExperimentReport.experiment_id == experiment_id)).all() + result = { + "base_info": experiment_info, + "report_list": report_list + } + + return result + + +@router.get('/mergeGate' ,summary="合闸") +async def mergeGate(request: Request ,channel_id :str): + # experiment_specimen_list = BllExperimentSpecimen.findAllList() + + return None + +# 保存报表数据 +async def save_report(): + curr_time = time.time() + for item in StaticData.ChannelDatas: + if not hasattr(item, 'timestamp') or not item.timestamp: + continue + time_ratio = round(float((curr_time - item.timestamp)/60), 1) + if (item.status == 1 or item.status == 2) and item.id not in StaticData.StopIds: # 如果通道状态为正在实验或已实验,且通道id不在停止列表中 + if item.status == 2 : + StaticData.StopIds.append(item.id) + new_id = str(Utils.get_uuid()) + experiment_report = ExperimentReport(id= new_id, experiment_id=item.experiment_id, + channel_id=item.id, + time=time_ratio, experiment_temperature=item.temperature, + battery_voltage=item.voltage, battery_electricity=item.electricity + ) + BllExperimentReport().insert(experiment_report) + return None + + + +@router.get('/updateDoor' ,summary="修改门状态") +async def updateDoor(request: Request ,status :int): + if StaticData.DeviceConfig['door_status'] == status: + return Utils.false_return('', "门状态未改变", 0) + + StaticData.DeviceConfig['door_status'] = status + device_config_list = BllDeviceConfig().findAllList() + device_config = device_config_list[0] + device_config.door_status = status + BllDeviceConfig().update(device_config) + + return Utils.true_return('', "修改成功") + + +# 获取指定日期的文件列表 +@router.get("/files") +async def get_files_by_date(): + current_date = datetime.datetime.now() + date = current_date.strftime("%Y-%m-%d") + pid_dir = f"/data/video" + if not os.path.exists(pid_dir): + return Utils.false_return(code=400, desc=f'没有找到{pid_dir}的目录') + files = os.listdir(pid_dir) + result = [] + for file in files: + if file.startswith(date.replace('-', '')) and file.endswith(".mp4"): + result.append(file) + result.sort(reverse=True) + return Utils.true_return(data=result) + +#实时相机显示 +@router.get('/camera_img/') +def camera_img(): + return Response(camera.generate(), + mimetype = "multipart/x-mixed-replace; boundary=frame") + +@router.post("/view_video", summary="查看视频") +async def view_video(request: Request, body: VideoModel): + """查看视频""" + record_id = body.id + + record_obj = BllExperimentInfo().findEntity(and_(BllExperimentInfo.id == body.id)) + if not record_obj: + return Utils.false_return('', desc="查不到该实验!") + + video_path = 'http://127.0.0.1:8088/videoFold' + '/' + record_id + '.mp4' + return Utils.true_return(video_path, desc="查看成功") + + +@router.post("/export_video", summary="导出视频") +async def export_video(request: Request, body: VideoModel): + """ + 导出视频 + id 使用 试验id + """ + if not body.id: + return Utils.false_return("", desc="未选择导出文件") + try: + # 检查U盘挂载路径 + upath = Utils.getUDiskPath() + if not upath: + return Utils.false_return("", desc="U盘未正确挂载") + + source_file = 'D:/Project/UI/videoFold' + '/' + body.id + '.mp4' + # 检查源文件是否存在 + if not os.path.isfile(source_file): + return Utils.false_return("", desc="指定的视频文件不存在") + # 复制视频文件到U盘 + with open(source_file, mode="rb") as file: + file_content = file.read() + buffer = io.BytesIO(file_content) + usb.put_in(body.id + '.mp4', buffer) + + return Utils.true_return("", desc="导出成功") + except Exception as e: + return Utils.false_return("", desc="导出失败") + + +@router.post("/export_table", summary="导出报表") +async def export_table(request: Request, body: ExportTableModel): + """报表导出 + + Args: + request (Request): _description_ + body (ExportTableModel): + ids: 实验id列表 + download_type: 下载类型(usb:导出到u盘),传空为返回到浏览器 + """ + experiment_ids = body.specimen_id.split(',') + experiments = BllExperimentInfo().findList(ExperimentInfo.id.in_(experiment_ids)).all() + + all_data = [] + file_name = "" + + base_data_title = [ + '实验时间', '实验人', '样品名称', '设置温度', '暴露时间', '实验结果' + ] + data = { + "specimen_data": {}, + "file_name": file_name, + "sheet_title": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S").replace(":", "-"), + "title_list": ["BAT05-A电池短路试验报告"], + "base_data_title": base_data_title + } + base_data_list = [] + other_data = [] + if len(experiment_ids) == 1: + experiment_info = BllExperimentInfo().findEntity(ExperimentInfo.id == experiment_ids[0]) + other_data = BllExperimentReport().findList(and_(ExperimentReport.experiment_id == experiment_ids[0],ExperimentReport.channel_id == experiment_info.channel_id)).all() + + for experiment in experiments: + base_data = { + "experiment_id": experiment.id, + "样品名称": experiment.specimen_name, + "实验人": experiment.user_name, + "实验时间": experiment.created_time, + "设置温度": experiment.temperature, + "暴露时间": experiment.expose_time, + "实验结果": '成功' if experiment.state == 2 else '失败', + 'other_data': other_data + } + base_data_list.append(base_data) + data['base_data_list'] = base_data_list + all_data.append(data) + + excel_obj = ExcelExport(file_name, all_data, body.download_type) + binary, encoded_filename = excel_obj.experiment_table() + if isinstance(binary, int): + if binary != 404: + return Response(content=binary.getvalue(), + media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + headers={'Content-Disposition': f'attachment; filename={encoded_filename}'}) + return Utils.false_return(desc=encoded_filename) + + # 导出到浏览器 + return Response(content=binary.getvalue(), + media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + headers={'Content-Disposition': f'attachment; filename={encoded_filename}'}) + + + + +async def app_start(): + # 项目启动执行 初始化项目配置 + device_config_list = BllDeviceConfig().findAllList() + StaticData.DeviceConfig.update(device_config_list[0].__dict__) + # 初始化通道信息 + channel_list = BllChannel().findAllList() + StaticData.ChannelDatas = channel_list + # 调用下位机 + asyncio.create_task(run_periodic_task()) + + +async def run_periodic_task(): + while True: + print("开始执行任务-------------") + modbus_server.get_status() # 调用获取状态的方法 + await save_report() + await asyncio.sleep(30) # 每隔30秒执行一次 + + +modbus_server = ModbusConnection() +manager = ConnectionManager() +camera =CvCamera() +camera.release() +# vk70_server = VK70xUMCDAQ() + + + + + +async def update_channel_status(channel_ids, curr_time,status,channel_experiment_dict,timestamp): + channel_list = BllChannel().findList(Channel.id.in_(channel_ids)).all() + channel_dict = {item.id : item for item in StaticData.ChannelDatas } + for channel in channel_list: + # 更新通道状态为正在实验 + channel.status = status + channel.last_start_time = curr_time + BllChannel().update(channel) + channel_dict.get(channel.id).status = status + channel_dict.get(channel.id).last_start_time = curr_time + channel_dict.get(channel.id).experiment_id = channel_experiment_dict.get(channel.id) + channel_dict.get(channel.id).timestamp = timestamp + if channel.id in StaticData.StopIds: + StaticData.StopIds.remove(channel.id) + + +async def save_experiment_info(body): + channel_ids = body.channel_ids.split(',') + for channel_id in channel_ids: + new_id = str(Utils.get_uuid()) + experiment_info = ExperimentInfo(id= new_id, specimen_id=body.experiment_specimen_id, + channel_id=channel_id, + specimen_name=body.specimen_name, battery_type=body.battery_type, + specimen_num=body.specimen_num, + battery_specification=body.battery_specification, + battery_manufacturers=body.battery_manufacturers, + temperature=body.temperature, expose_time=body.expose_time, put_time=body.put_time, + short_circuit_param=body.short_circuit_param, user_id=CurrentUser['user_id'], + user_name=CurrentUser['username'], + state=1) + # 保存实验信息 + BllExperimentInfo().insert(experiment_info) + body.channel_experiment_dict.update({channel_id: new_id}) + camera.start(record_id=new_id) + + + + +class ChannelStatusError(Exception): + def __init__(self, message): + self.message = message + super().__init__(self.message) + +def check_channel_status(channel_ids, channel_datas): + # 将channel_ids字符串转换为列表 + channel_ids = channel_ids.split(',') + + # 获取 status == 0 的 id 集合 + wait_ids = [item.id for item in channel_datas if item.status == 0] + + # 判断选择的通道是否为空 + if len(channel_ids) <= 0: + raise ChannelStatusError("请选择通道") + + # 判断是否所有通道都在实验中 + if len(wait_ids) == 0: + raise ChannelStatusError("所有通道都在实验中,请等待") + + # 判断每个通道是否处于空闲状态 + for channel_id in channel_ids: + if channel_id not in wait_ids: + raise ChannelStatusError(f"通道 {channel_id} 不在空闲状态,请重新选择") + + # 如果都通过了检查,返回 True 或其他成功状态 + return True + + + +async def check_specimen_exist(body): + # 拼接字符串生成 MD5 + source_str = body.specimen_name + body.battery_type + body.specimen_num + body.battery_specification + body.battery_manufacturers + md5_upper = Utils.get_md5(source_str).upper() + + # 查询数据库中的样本 + experiment_specimen = BllExperimentSpecimen().findEntity(ExperimentSpecimen.md5 == md5_upper) + + # 如果样本不存在,则创建样本 + if experiment_specimen : + # 将新创建的样本 ID 赋值给 body + body.experiment_specimen_id = experiment_specimen.id + else: + new_id = Utils.get_uuid() + body.experiment_specimen_id = new_id + experiment_specimen = ExperimentSpecimen( + id=str(new_id), + md5=md5_upper, + specimen_name=body.specimen_name, + battery_type=body.battery_type, + specimen_num=body.specimen_num, + battery_specification=body.battery_specification, + battery_manufacturers=body.battery_manufacturers, + user_id=CurrentUser['user_id'], + user_name=CurrentUser['username'] + ) + # 异步插入新样本 + BllExperimentSpecimen().insert(experiment_specimen) + diff --git a/app/api/__init__.py b/app/api/__init__.py new file mode 100644 index 0000000..a885df5 --- /dev/null +++ b/app/api/__init__.py @@ -0,0 +1,26 @@ +from fastapi import FastAPI +from app.api import Server, ShortCircuit +from app.api.ShortCircuit import app_start + + +def register_blueprints(app): + ''' + registration route (module) + ''' + # app.include_router(Server.router, tags=["API接口"]) + app.include_router(ShortCircuit.router, tags=["电池短路API接口"]) + +async def lifespan(app): + # 应用启动时执行 + await app_start() + + # 在应用关闭时执行的代码 + yield # 等待应用关闭 + +def create_app(): + app = FastAPI(lifespan=lifespan) + + #注册路由 + register_blueprints(app) + + return app diff --git a/app/conf/Setting.py b/app/conf/Setting.py new file mode 100644 index 0000000..67274ce --- /dev/null +++ b/app/conf/Setting.py @@ -0,0 +1,44 @@ +import os +from pydantic import BaseSettings + + +class Settings(BaseSettings): + NAME: str = "DG16-D" + DEBUG: bool = False + RELOAD: bool = False + ENV: str = "dev" + SERVER_HOST: str = "0.0.0.0" + SERVER_POST: int = 8099 + LOGS_DIR: str = "logs" + RESOURCE_PATH: str = "resource" + TEMP_PATH: str = "temp" + DB_DIR: str = "db" + TEM: list = [40, 60, 80, 100] # 计算温度点 + BASE_PATH: str = os.path.dirname(os.path.dirname((os.path.abspath(__file__)))) + ''' + 数据库的配置 + ''' + SQLALCHEMY_DATABASE_URI: str = 'sqlite:///app/db/Data.db' + ''' + 分页信息配置 + ''' + PAGE_SIZE: int = 10 + PAGE_MIN_SIZE: int = 6 + PAGE_DEFAULT_OFFSET: int = 1 + ''' + modbus-rtu连接参数 + ''' + PORT: str = "COM1" # 串口号 + BAUDRATE: int = 9600 # 波特率 + BYTESIZE: int = 8 # 数据位 + STOPBITS: int = 1 # 停止位 + TIMEOUT: int = 5 # 超时时间 + + MACHINE: int = 1 # 设备 1沒有没有摄像头,2有摄像头 + + +settings = Settings() +for i in [settings.LOGS_DIR, settings.TEMP_PATH, settings.RESOURCE_PATH, settings.DB_DIR]: + dir = os.path.join(settings.BASE_PATH, i) + if not os.path.exists(dir): + os.mkdir(dir) \ No newline at end of file diff --git a/app/lib/AlchemyJsonEncoder.py b/app/lib/AlchemyJsonEncoder.py new file mode 100644 index 0000000..fa918a1 --- /dev/null +++ b/app/lib/AlchemyJsonEncoder.py @@ -0,0 +1,29 @@ +from sqlalchemy.ext.declarative import DeclarativeMeta +import datetime +import json + +#sqlalchemy对象编码转换类 +class AlchemyEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj.__class__, DeclarativeMeta): + #SQLAlchemy类 + fields = {} + for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']: + data = obj.__getattribute__(field) + try: + json.dumps(data) # 序列化对象 + fields[field] = data + except TypeError: # 添加了对datetime的处理 + if isinstance(data, datetime.datetime): + fields[field] = data.isoformat() + elif isinstance(data, datetime.date): + fields[field] = data.isoformat() + elif isinstance(data, datetime.timedelta): + fields[field] = (datetime.datetime.min + data).time().isoformat() + else: + fields[field] = None + # Json编码字典 + return fields + + return json.JSONEncoder.default(self, obj) + diff --git a/app/lib/Camera.py b/app/lib/Camera.py new file mode 100644 index 0000000..6ae7b60 --- /dev/null +++ b/app/lib/Camera.py @@ -0,0 +1,82 @@ +import os +import cv2 +import threading + +outputFrame = None +lock = threading.Lock() + +class CvCamera(): + def __init__(self): + # 获取当前相机 + self.cap = None + self.workFlag =False + self.record_id =None + self.video_path = 'D:/Project/UI/videoFold' + self.fourcc = cv2.VideoWriter_fourcc(*'XVID') + if not os.path.exists(self.video_path) or not os.path.isdir(self.video_path): + os.makedirs(self.video_path) + + + #打开相机 + def start(self,record_id=None): + try: + if self.workFlag == False: + self.record_id =record_id + self.cap = cv2.VideoCapture(0) + self.workFlag = True + # 单独线程供相机使用 + t = threading.Thread(target=self.videoWork) + t.start() + except Exception as e: + print(e) + + + # 释放资源 + def release(self): + global outputFrame + try: + self.workFlag = False + self.record_id = None + if (self.cap): + outputFrame=None + self.cap.release() + except: + pass + + + # 相机图像分析 + def videoWork(self): + global outputFrame, lock + self.cap.set(cv2.CAP_PROP_FPS, 30) + out = cv2.VideoWriter(self.video_path + '/' + self.record_id + '.mp4', self.fourcc, 20.0, (640, 480)) + while (self.workFlag): + try: + ret, frame = self.cap.read() + # cv2.imshow('image',frame) + # cv2.waitKey(0) + with lock: + outputFrame = frame.copy() + out.write(frame) + except Exception as e: + print(e) + + + # 输出图像 + def generate(self): + # 抓取对输出帧和锁定变量的全局引用 + global outputFrame, lock + # 循环访问输出流中的帧 + while True: + # 等到锁被获取 + with lock: + # 检查输出帧是否可用,否则跳过 + # 循环的迭代 + if outputFrame is None: + continue + # 以 JPEG 格式对帧进行编码 + (flag, encodedImage) = cv2.imencode(".jpg", outputFrame) + # 确保帧已成功编码 + if not flag: + continue + # 以字节格式生成输出帧 + yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + bytearray(encodedImage) + b'\r\n') \ No newline at end of file diff --git a/app/lib/Log.py b/app/lib/Log.py new file mode 100644 index 0000000..2550f5b --- /dev/null +++ b/app/lib/Log.py @@ -0,0 +1,59 @@ +import os +import logging.config +import time +from app.conf.Setting import settings + + +# 日志格式 +format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' +log_dir = os.path.join(settings.BASE_PATH, settings.LOGS_DIR) # log文件的目录 +log_name = time.strftime("%Y-%m-%d") + ".log" # log文件名 + +# log文件的全路径 +logfile_path = os.path.join(log_dir, log_name) + +# log配置字典 +LOGGING_DICT = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'standard': { + 'format': format + }, + 'simple': { + 'format': format + }, + }, + 'filters': {}, + 'handlers': { + # 打印到终端的日志 + 'console': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', # 打印到屏幕 + 'formatter': 'simple' + }, + # 打印到文件的日志,收集info及以上的日志 + 'default': { + 'level': 'INFO', + 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件 + 'formatter': 'standard', + 'filename': logfile_path, # 日志文件 + 'maxBytes': 1024 * 1024 * 20, # 日志大小 5M + 'backupCount': 5, + 'encoding': 'utf-8' + }, + }, + 'loggers': { + '': { + # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕 + 'handlers': ['default', 'console'], + 'level': 'DEBUG', + 'propagate': True, # 向上(更高level的logger)传递 + }, + }, +} + + +logging.config.dictConfig(LOGGING_DICT) +# 导入上面定义的logging配置 +logger = logging.getLogger() diff --git a/app/lib/ModBus.py b/app/lib/ModBus.py new file mode 100644 index 0000000..35ddcf9 --- /dev/null +++ b/app/lib/ModBus.py @@ -0,0 +1,292 @@ +import time + +import serial +import struct +import modbus_tk.defines as cst + +from app.lib import StaticData +from app.lib.StaticData import DeviceStatus +from app.lib.Utils import Utils +from modbus_tk import modbus_rtu +from app.conf.Setting import settings +from app.lib.Log import logger + + +''' +下位机地址定义 +''' +EXPERIMENT = 801 #试验操作地址位 +STATUS_ADDR_BEGIN =3701 #获取状态信息地址位 + +ELECT_SWITCH =102 #电磁阀 + +''' +工作模式定义 +''' +STATUS_ADDR_LENGTH =24 #获取状态长度 + + +State_dict={ + "0": "空闲", + "1": "升温阶段", + "2": "结果判定中", +} + +class Status(object): + ''' + 状态数据结构 + ''' + + status: int = None #状态 0 1 2 + status_text: str = None #状态 + temp1: str = None #铜炉中心温度 + temp2: str = None #加热铜块温度 + temp3: str = None #样品温度 + temp4: str = None #自燃温度 + sy_work_time: str = None + switch_state = None + result = None + + + def keys(self): + return ["status", "status_text", "temp1", "temp2", "temp3", "temp4", "sy_work_time", "switch_state", "result"] + + def __getitem__(self, key): + return getattr(self,key) + + +class ModbusConnection(object): + ''' + Modbus连接管理器 + ''' + + def __init__(self): + try: + self.serial =serial.Serial(port=settings.PORT,baudrate=settings.BAUDRATE, bytesize=settings.BYTESIZE, stopbits=settings.STOPBITS) + self.master = modbus_rtu.RtuMaster(self.serial) + self.master.set_timeout(1.0) + except Exception as e: + logger.error(f"连接串口异常: {e}") + + + def parser_status(self,data_bytes): + ''' + 解析状态 + ''' + channel1_status =struct.unpack('>H', data_bytes[0:2])[0] + channel2_status =struct.unpack('>H', data_bytes[2:4])[0] + channel3_status =struct.unpack('>H', data_bytes[4:6])[0] + channel4_status =struct.unpack('>H', data_bytes[6:8])[0] + channel5_status =struct.unpack('>H', data_bytes[8:10])[0] + + channel1_voltage =float(int(struct.unpack('>H', data_bytes[10:12])[0]) /10) #电压 /10获取小数 + channel2_voltage =float(int(struct.unpack('>H', data_bytes[12:14])[0]) /10) + channel3_voltage =float(int(struct.unpack('>H', data_bytes[14:16])[0]) /10) + channel4_voltage =float(int(struct.unpack('>H', data_bytes[16:18])[0]) /10) + channel5_voltage =float(int(struct.unpack('>H', data_bytes[18:20])[0]) /10) + + channel1_electricity =float(int(struct.unpack('>H', data_bytes[20:22])[0]) /10) #电流 + channel2_electricity =float(int(struct.unpack('>H', data_bytes[22:24])[0]) /10) + channel3_electricity =float(int(struct.unpack('>H', data_bytes[24:26])[0]) /10) + channel4_electricity =float(int(struct.unpack('>H', data_bytes[26:28])[0]) /10) + channel5_electricity =float(int(struct.unpack('>H', data_bytes[28:30])[0]) /10) + + channel1_temperature =float(int(struct.unpack('>H', data_bytes[30:32])[0]) /10) #温度 + channel2_temperature =float(int(struct.unpack('>H', data_bytes[32:34])[0]) /10) + channel3_temperature =float(int(struct.unpack('>H', data_bytes[34:36])[0]) /10) + channel4_temperature =float(int(struct.unpack('>H', data_bytes[36:38])[0]) /10) + channel5_temperature =float(int(struct.unpack('>H', data_bytes[38:40])[0]) /10) + + zfm_temperature =float(int(struct.unpack('>H', data_bytes[40:42])[0]) /10) # 蒸发皿温度 + xt_temperature =float(int(struct.unpack('>H', data_bytes[42:44])[0]) /10) # 箱体温度 + kzzt_temperature =int(struct.unpack('>H', data_bytes[44:46])[0]) # 温度控制状态 + exception_status =int(struct.unpack('>H', data_bytes[46:48])[0]) # 异常状态1 bit0--继电器通讯失败 bit2--加热功能异常 bit3--制冷功能异常 + + StaticData.DeviceStatus.zfm_temperature = zfm_temperature + StaticData.DeviceStatus.xt_temperature = xt_temperature + StaticData.DeviceStatus.kzzt_temperature = kzzt_temperature + StaticData.DeviceStatus.exception_status = exception_status + + for item in StaticData.ChannelDatas: + if item.name == '通道一': + item.status = channel1_status + item.voltage = channel1_voltage + item.electricity = channel1_electricity + item.temperature = channel1_temperature + elif item.name == '通道二': + item.status = channel2_status + item.voltage = channel2_voltage + item.electricity = channel2_electricity + item.temperature = channel2_temperature + elif item.name == '通道三': + item.status = channel3_status + item.voltage = channel3_voltage + item.electricity = channel3_electricity + item.temperature = channel3_temperature + elif item.name == '通道四': + item.status = channel4_status + item.voltage = channel4_voltage + item.electricity = channel4_electricity + item.temperature = channel4_temperature + elif item.name == '通道五': + item.status = channel5_status + item.voltage = channel5_voltage + item.electricity = channel5_electricity + item.temperature = channel5_temperature + + + + + return None + + + # def parser_status(self,data_bytes): + # ''' + # 解析状态 + # ''' + # status.status =struct.unpack('>H', data_bytes[0:2])[0] + # + # status.temp1 = round(struct.unpack('>f', data_bytes[8:10] + data_bytes[6:8])[0],2) + # status.temp2 = round(struct.unpack('>f', data_bytes[12:14] + data_bytes[10:12])[0],2) + # status.temp3 = round(struct.unpack('>f', data_bytes[16:18] + data_bytes[14:16])[0],2) + # status.temp4 = round(struct.unpack('>f', data_bytes[20:22] + data_bytes[18:20])[0],2) + # + # status.sy_work_time = round(struct.unpack('>f', data_bytes[24:26] + data_bytes[22:24])[0],2) + # status.switch_state = struct.unpack('>H', data_bytes[26:28])[0] + # status.result = struct.unpack('>H', data_bytes[28:30])[0] + # return status + + + def Experiment(self,**kwargs_): + ''' + 试验操作 + ''' + channel_dict = {'通道一':1, '通道二':10, '通道三':100, '通道四':1000, '通道五':10000 ,'全部': 11111} + channel_list = kwargs_["channel_list"] + channel = 0 + for name in channel_list: + channel += channel_dict.get(name) + work_time = int(kwargs_["work_time"] * 60) + + place_holding = struct.pack('>H', int(0))#0 占位符 + d_work_status = struct.pack('>H', 1)#1开始试验0停止试验 + d_sy_time = struct.pack('>H', work_time) # 实验时间 + channel_list = struct.pack('>H', int(channel)) # 通道有效位 00111就代表前3个通道有效 关闭时通道位也传1 模式传0 + temperature = struct.pack('>H', int(float(kwargs_["temperature"]) * 10)) # 目标温度 比如511,就是51.1度 + temperature_control = struct.pack('>H', int(kwargs_["temperature_control"] or 0)) # 是否开启控温 0关闭 1开启 + + + output = ( + d_work_status + + d_sy_time + + channel_list + + temperature + + temperature_control + ) + print(f'开始实验参数::::::::::{d_work_status},{d_sy_time},{channel_list},{temperature},{temperature_control}') + vl_output = Utils.bytes_to_int_list(output) + try: + res = self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, starting_address=EXPERIMENT, output_value=vl_output) # [11,12,13,12] + except Exception as e: + print(f'试验操作异常: {e}') + res = None + print(f'开始实验返回值:::::::::::::{res}') + return res + + # def Experiment(self,**kwargs_): + # ''' + # 试验操作 + # ''' + # place_holding = struct.pack('>H', int(0))#0 占位符 + # d_work_status = struct.pack('>H', int(kwargs_["work_status"]))#1开始试验2停止试验 + # elect_switch = struct.pack('>H', int(kwargs_["elect_switch"])) + # + # d_natural_temp = struct.pack('>f', float(kwargs_["natural_temp"])) + # d_sy_time = struct.pack('>f', float(kwargs_["max_work_time"] or 30)) + # + # output = ( + # d_work_status + # + elect_switch + # + place_holding + # + d_natural_temp[2:] + d_natural_temp[:2] + # + place_holding + place_holding + # + d_sy_time[2:] + d_sy_time[:2] + # ) + # vl_output = Utils.bytes_to_int_list(output) + # try: + # res = self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, starting_address=EXPERIMENT, output_value=vl_output) # [11,12,13,12] + # except Exception as e: + # print(f'试验操作异常: {e}') + # res = None + # return res + + def StopExperiment(self, **kwargs_): + ''' + 停止试验操作 + ''' + channel_dict = {'通道一':1, '通道二':10, '通道三':100, '通道四':1000, '通道五':10000 ,'全部': 11111} + channel_list = kwargs_["channel_list"] + + + d_work_status = struct.pack('>H', 0)#1开始试验0停止试验 + channel_list = struct.pack('>H', int(channel_dict.get(channel_list[0]))) # 通道有效位 00111就代表前3个通道有效 关闭时通道位也传1 模式传0 00001 一通道 00010 二通道 00100 三通道 01000 四通道 10000 五通道 + print(f'结束实验参数::::::::::{d_work_status},{channel_list}') + try: + res = self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, starting_address=(EXPERIMENT+2), output_value=Utils.bytes_to_int_list(channel_list)) + + time.sleep(0.2) + res = self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, starting_address=EXPERIMENT, output_value=Utils.bytes_to_int_list(d_work_status)) + except Exception as e: + res = None + print(f'结束实验返回值:::::::::::::{res}') + return res + + def Send_Mes(self,Addr,status): + ''' + Addr 地址位 + ''' + + d_0 = struct.pack('>H', status) + output = (d_0) + vl_output = Utils.bytes_to_int_list(output) + + try: + res =self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS,starting_address=Addr, output_value=vl_output) + except Exception as e: + print(e) + res = None + return res + + def get_status(self): + ''' + 获取状态 + ''' + try: + status = self.master.execute( + 1, + cst.READ_INPUT_REGISTERS, + STATUS_ADDR_BEGIN, + STATUS_ADDR_LENGTH + ) + except Exception as e: + logger.error(f"获取状态异常: {e}") + return None + print('status:::::::::::::',status) # [12,4,4] + print(':int_list_to_bytes:::::',status) # [0xdd,0xll] + print('parser_status:::::::::::::',self.parser_status(Utils.int_list_to_bytes(status))) # + + return self.parser_status(Utils.int_list_to_bytes(status)) + + +modbus_obj = ModbusConnection() +# 获取状态 +modbus_obj.get_status() + +# 开始实验 +# kwargs_={} +# kwargs_["work_status"] = 1 +# kwargs_["elect_switch"] = 0 +# kwargs_["natural_temp"] = 40 +# kwargs_["max_work_time"] = 30 +# +# modbus_obj.Experiment(**kwargs_) diff --git a/app/lib/RecordData.py b/app/lib/RecordData.py new file mode 100644 index 0000000..61ab23d --- /dev/null +++ b/app/lib/RecordData.py @@ -0,0 +1,113 @@ +import csv +import json +import os +import pathlib +import shutil + +from app.conf.Setting import settings +# from app.lib.StaticData import ServerStatus + + +class RecordStatus: + """记录当然实验工作状态""" + + def __init__(self): + self.file = pathlib.Path(settings.BASE_PATH, settings.TEMP_PATH, "last_experiment.json") + + def load(self): + if not self.file.exists(): + return {} + return json.loads(self.file.read_text()) + + def update(self, work_time, status): + data = { + "work_time": work_time, + "status": status + } + self.file.write_text(json.dumps(data)) + + def clear(self): + if self.file.exists(): + self.file.unlink() + + +class ExperimentData: + """实验数据""" + + current_obj = None + id = None + + def __init__(self, _id): + self._id = _id + self.dir_path = pathlib.Path(os.path.join(settings.BASE_PATH, settings.TEMP_PATH, self._id)) + self.file_path = self.dir_path.joinpath("experiment_data.csv") + self.status_file = self.dir_path.joinpath("last_status.json") + + def init(self): + if not self.dir_path.exists(): + print(f"create dir {self.dir_path}") + self.dir_path.mkdir(parents=True, exist_ok=True) + + self.file_path.touch(exist_ok=True) + self.status_file.touch(exist_ok=True) + ExperimentData.current_obj = self + ExperimentData.id = self._id + + def update_status(self, status_data): + + format_value = (f'{status_data["status"]},{status_data["status_text"]},' + f'{status_data["s_pressure"]},{status_data["temperature"]},' + f'{status_data["test_time"]}\n') + + with open(self.file_path, "a+") as f: + f.write(format_value) + + data = { + "status": status_data["status"], + "status_text": status_data["status_text"], + "s_pressure": status_data["s_pressure"], + "temperature": status_data["temperature"], + "test_time": status_data["test_time"] + } + + self.status_file.write_text(json.dumps(data)) + + def get_last_status(self): + if not self.status_file.exists(): + return {'status': 0} # 设置的初始值 + + raw = self.status_file.read_text() + if not raw: + return {'status': 0} # 设置的初始值 + return json.loads(self.status_file.read_text()) + + def finish(self): + ExperimentData.current_obj = None + + def remove(self): + if self.dir_path.exists(): + print(f"remove dir {self.dir_path}") + shutil.rmtree(self.dir_path) + + def judging_status(self, status_obj, work_time): + """根据时间判断状态""" + cur_status = status_obj[0][0] + for status, time in status_obj: + if int(work_time) <= int(time): + return cur_status + else: + cur_status = status + + return cur_status + + def get_raw_data(self): + result = [] + + if not self.file_path.exists(): + return result + + with open(self.file_path, "r") as f: + reader = csv.reader(f) + for row in reader: + result.append(row) + return result \ No newline at end of file diff --git a/app/lib/StaticData.py b/app/lib/StaticData.py new file mode 100644 index 0000000..1782d28 --- /dev/null +++ b/app/lib/StaticData.py @@ -0,0 +1,145 @@ +from enum import Enum + +# 短路设备设置 +DeviceConfig = { + "box_temperature": 0.00, # 设备温度 + "surface_temperature": 0.00, # 表面温度 + "door_status": 0, # 门状态 0关闭 1开启 + "light_status": 0, # 灯光状态 0关闭 1开启 + "fan_status": 0, # 外排风状态 0关闭 1开启 + "warning_light_status": 0, # 报警灯状态 0关闭 1开启 + "lock_status": 0, # 锁状态 0关闭 1开启 +} + +DeviceStatus = { + "zfm_temperature": 0.00, # 蒸发皿温度 + "xt_temperature": 0.00, # 箱体温度 + "kzzt_temperature": 10, # 温度控制状态 + "exception_status": 10, # 异常状态1 bit0--继电器通讯失败 bit2--加热功能异常 bit3--制冷功能异常 +} + +ChannelDatas = list # 通道数据 + +StopIds = [] # 实验结束的数据 + + + +# 登录用户信息 +CurrentUser = { + "username": '', # 用户名 + "user_id": '', # 用户id +} + +SpecimenInsertDict = { + "specimen_id": "", # 证物id + "specimen_name": "", # 证物名称 + "specimen_code": "", + "specimen_type": "", + "specimen_origin": "", + "specimen_desc": "", + "storage_location": "", + "remark": "", + "rfid": "", +} + +ConfigInsertDict = { + "config_id": "", + "ozone_disinf": "", + "uv_disinf": "", + "ozone_conc": "", + "disinf_time": "", + "temperature": "", + "humidity": "", + "config_name": "", + "config_desc": "", +} + +# 新增试验数据 +InsertDict = { + "experiment_id": "", # 试验id + "user_id": "", # 用户id(可传空) + "user_name": "", # 用户名(可传空) + + "ozone_disinf": "", + "uv_disinf": "", + "ozone_conc": "", + "disinf_time": "", + "temperature": "", + "humidity": "", + "remark":"", + "specimens":"" + +} + + + + +# 下位机状态 +StatusDict = { + "status": '', # 消毒状态 0未消毒 1预处理 2消毒中 + "led": "", #led灯状态 0为灯灭 1为灯亮 + "door": "", # 门状态 bit0表示外门bit1表示内门 0为关门 000001为开门 + "fans": "", # 风机状态 0为关 1为开 + "compressor": "", # 压缩机状态 0为关 1为开 + "heating": "", # 加热管状态 0为关 1为开 + "ozonizer": "", # 臭氧发生器状态 0为关 1为开 + "disinfectant": "", # 紫外线消毒灯状态 0为关 1为开 + "pump": "", # 水泵状态 0为关 1为开 + "work_time": "", # 工作时间(预处理阶段后正式消毒时间) + "temperature_sensor": "", # 温湿度传感器 0为正常 1为异常 + "voc_sensor": "", # voc传感器 0为正常 1为异常 + "ozone_sensor": "", # 臭氧 0为正常 1为异常 + "temperature": "", # 温度 + "humidity": "", # 湿度值 + "voc": "", # voc + "ozone": "", #臭氧 +} + + +# 新增试验记录数据 +InsertRecordDict = { + "record_id": "", + "experiment_id": "", + "user_id": "", + "user_name": "", +} + +# 结束试验 +StopDict = { + "work_status": "", +} + + +class ServerStatus: + """下位机工作状态""" + + IDLE = 0 # 试验空闲 + CONTROL_TEMP = 1 # 实验配气 + INDICAT_POWDER_PRODUCT = 2 # 粉尘喷粉 + POWDER_PRODUCT = 3 # 粉尘点火中 + JUDGMENT_RESULT = 4 # 试验判断 + FINISH = 5 # 试验停止复位 + Remote_Ignition = 6 # 遥控点火 + Pressure_Relief = 7 # 泄压 + Test_Charge = 8 # 试验充能 + + + @classmethod + def finish_status(cls): + return [cls.IDLE] + +# 查表法对应数值的1/k,b值 +data_lists = [-6.458, -6.457, -6.456, -6.455, -6.453, -6.452, -6.45, -6.448, -6.446, -6.444, -6.441, -6.438, -6.435, -6.432, -6.429, -6.425, -6.421, -6.417, -6.413, -6.408, -6.404, -6.399, -6.393, -6.388, -6.382, -6.377, -6.37, -6.364, -6.358, -6.351, -6.344, -6.337, -6.329, -6.322, -6.314, -6.306, -6.297, -6.289, -6.28, -6.271, -6.262, -6.252, -6.243, -6.233, -6.223, -6.213, -6.202, -6.192, -6.181, -6.17, -6.158, -6.147, -6.135, -6.123, -6.111, -6.099, -6.087, -6.074, -6.061, -6.048, -6.035, -6.021, -6.007, -5.994, -5.98, -5.965, -5.951, -5.936, -5.922, -5.907, -5.891, -5.876, -5.861, -5.845, -5.829, -5.813, -5.797, -5.78, -5.763, -5.747, -5.73, -5.713, -5.695, -5.678, -5.66, -5.642, -5.624, -5.606, -5.588, -5.569, -5.55, -5.531, -5.512, -5.493, -5.474, -5.454, -5.435, -5.415, -5.395, -5.374, -5.354, -5.333, -5.313, -5.292, -5.271, -5.25, -5.228, -5.207, -5.185, -5.163, -5.141, -5.119, -5.097, -5.074, -5.052, -5.029, -5.006, -4.983, -4.96, -4.936, -4.913, -4.889, -4.865, -4.841, -4.817, -4.793, -4.768, -4.744, -4.719, -4.694, -4.669, -4.644, -4.618, -4.593, -4.567, -4.542, -4.516, -4.49, -4.463, -4.437, -4.411, -4.384, -4.357, -4.33, -4.303, -4.276, -4.249, -4.221, -4.194, -4.166, -4.138, -4.11, -4.082, -4.054, -4.025, -3.997, -3.968, -3.939, -3.911, -3.882, -3.852, -3.823, -3.794, -3.764, -3.734, -3.705, -3.675, -3.645, -3.614, -3.584, -3.554, -3.523, -3.492, -3.462, -3.431, -3.4, -3.368, -3.337, -3.306, -3.274, -3.243, -3.211, -3.179, -3.147, -3.115, -3.083, -3.05, -3.018, -2.986, -2.953, -2.92, -2.887, -2.854, -2.821, -2.788, -2.755, -2.721, -2.688, -2.654, -2.62, -2.587, -2.553, -2.519, -2.485, -2.45, -2.416, -2.382, -2.347, -2.312, -2.278, -2.243, -2.208, -2.173, -2.138, -2.103, -2.067, -2.032, -1.996, -1.961, -1.925, -1.889, -1.854, -1.818, -1.782, -1.745, -1.709, -1.673, -1.637, -1.6, -1.564, -1.527, -1.49, -1.453, -1.417, -1.38, -1.343, -1.305, -1.268, -1.231, -1.194, -1.156, -1.119, -1.081, -1.043, -1.006, -0.968, -0.93, -0.892, -0.854, -0.816, -0.778, -0.739, -0.701, -0.663, -0.624, -0.586, -0.547, -0.508, -0.47, -0.431, -0.392, -0.353, -0.314, -0.275, -0.236, -0.197, -0.157, -0.118, -0.079, -0.039, 0, 0.039, 0.079, 0.119, 0.158, 0.198, 0.238, 0.277, 0.317, 0.357, 0.397, 0.437, 0.477, 0.517, 0.557, 0.597, 0.637, 0.677, 0.718, 0.758, 0.798, 0.838, 0.879, 0.919, 0.96, 1, 1.041, 1.081, 1.122, 1.163, 1.203, 1.244, 1.285, 1.326, 1.366, 1.407, 1.448, 1.489, 1.53, 1.571, 1.612, 1.653, 1.694, 1.735, 1.776, 1.817, 1.858, 1.899, 1.941, 1.982, 2.023, 2.064, 2.106, 2.147, 2.188, 2.23, 2.271, 2.312, 2.354, 2.395, 2.436, 2.478, 2.519, 2.561, 2.602, 2.644, 2.685, 2.727, 2.768, 2.81, 2.851, 2.893, 2.934, 2.976, 3.017, 3.059, 3.1, 3.142, 3.184, 3.225, 3.267, 3.308, 3.35, 3.391, 3.433, 3.474, 3.516, 3.557, 3.599, 3.64, 3.682, 3.723, 3.765, 3.806, 3.848, 3.889, 3.931, 3.972, 4.013, 4.055, 4.096, 4.138, 4.179, 4.22, 4.262, 4.303, 4.344, 4.385, 4.427, 4.468, 4.509, 4.55, 4.591, 4.633, 4.674, 4.715, 4.756, 4.797, 4.838, 4.879, 4.92, 4.961, 5.002, 5.043, 5.084, 5.124, 5.165, 5.206, 5.247, 5.288, 5.328, 5.369, 5.41, 5.45, 5.491, 5.532, 5.572, 5.613, 5.653, 5.694, 5.735, 5.775, 5.815, 5.856, 5.896, 5.937, 5.977, 6.017, 6.058, 6.098, 6.138, 6.179, 6.219, 6.259, 6.299, 6.339, 6.38, 6.42, 6.46, 6.5, 6.54, 6.58, 6.62, 6.66, 6.701, 6.741, 6.781, 6.821, 6.861, 6.901, 6.941, 6.981, 7.021, 7.06, 7.1, 7.14, 7.18, 7.22, 7.26, 7.3, 7.34, 7.38, 7.42, 7.46, 7.5, 7.54, 7.579, 7.619, 7.659, 7.699, 7.739, 7.779, 7.819, 7.859, 7.899, 7.939, 7.979, 8.019, 8.059, 8.099, 8.138, 8.178, 8.218, 8.258, 8.298, 8.338, 8.378, 8.418, 8.458, 8.499, 8.539, 8.579, 8.619, 8.659, 8.699, 8.739, 8.779, 8.819, 8.86, 8.9, 8.94, 8.98, 9.02, 9.061, 9.101, 9.141, 9.181, 9.222, 9.262, 9.302, 9.343, 9.383, 9.423, 9.464, 9.504, 9.545, 9.585, 9.626, 9.666, 9.707, 9.747, 9.788, 9.828, 9.869, 9.909, 9.95, 9.991, 10.031, 10.072, 10.113, 10.153, 10.194, 10.235, 10.276, 10.316, 10.357, 10.398, 10.439, 10.48, 10.52, 10.561, 10.602, 10.643, 10.684, 10.725, 10.766, 10.807, 10.848, 10.889, 10.93, 10.971, 11.012, 11.053, 11.094, 11.135, 11.176, 11.217, 11.259, 11.3, 11.341, 11.382, 11.423, 11.465, 11.506, 11.547, 11.588, 11.63, 11.671, 11.712, 11.753, 11.795, 11.836, 11.877, 11.919, 11.96, 12.001, 12.043, 12.084, 12.126, 12.167, 12.209, 12.25, 12.291, 12.333, 12.374, 12.416, 12.457, 12.499, 12.54, 12.582, 12.624, 12.665, 12.707, 12.748, 12.79, 12.831, 12.873, 12.915, 12.956, 12.998, 13.04, 13.081, 13.123, 13.165, 13.206, 13.248, 13.29, 13.331, 13.373, 13.415, 13.457, 13.498, 13.54, 13.582, 13.624, 13.665, 13.707, 13.749, 13.791, 13.833, 13.874, 13.916, 13.958, 14, 14.042, 14.084, 14.126, 14.167, 14.209, 14.251, 14.293, 14.335, 14.377, 14.419, 14.461, 14.503, 14.545, 14.587, 14.629, 14.671, 14.713, 14.755, 14.797, 14.839, 14.881, 14.923, 14.965, 15.007, 15.049, 15.091, 15.133, 15.175, 15.217, 15.259, 15.301, 15.343, 15.385, 15.427, 15.469, 15.511, 15.554, 15.596, 15.638, 15.68, 15.722, 15.764, 15.806, 15.849, 15.891, 15.933, 15.975, 16.017, 16.059, 16.102, 16.144, 16.186, 16.228, 16.27, 16.313, 16.355, 16.397, 16.439, 16.482, 16.524, 16.566, 16.608, 16.651, 16.693, 16.735, 16.778, 16.82, 16.862, 16.904, 16.947, 16.989, 17.031, 17.074, 17.116, 17.158, 17.201, 17.243, 17.285, 17.328, 17.37, 17.413, 17.455, 17.497, 17.54, 17.582, 17.624, 17.667, 17.709, 17.752, 17.794, 17.837, 17.879, 17.921, 17.964, 18.006, 18.049, 18.091, 18.134, 18.176, 18.218, 18.261, 18.303, 18.346, 18.388, 18.431, 18.473, 18.516, 18.558, 18.601, 18.643, 18.686, 18.728, 18.771, 18.813, 18.856, 18.898, 18.941, 18.983, 19.026, 19.068, 19.111, 19.154, 19.196, 19.239, 19.281, 19.324, 19.366, 19.409, 19.451, 19.494, 19.537, 19.579, 19.622, 19.664, 19.707, 19.75, 19.792, 19.835, 19.877, 19.92, 19.962, 20.005, 20.048, 20.09, 20.133, 20.175, 20.218, 20.261, 20.303, 20.346, 20.389, 20.431, 20.474, 20.516, 20.559, 20.602, 20.644, 20.687, 20.73, 20.772, 20.815, 20.857, 20.9, 20.943, 20.985, 21.028, 21.071, 21.113, 21.156, 21.199, 21.241, 21.284, 21.326, 21.369, 21.412, 21.454, 21.497, 21.54, 21.582, 21.625, 21.668, 21.71, 21.753, 21.796, 21.838, 21.881, 21.924, 21.966, 22.009, 22.052, 22.094, 22.137, 22.179, 22.222, 22.265, 22.307, 22.35, 22.393, 22.435, 22.478, 22.521, 22.563, 22.606, 22.649, 22.691, 22.734, 22.776, 22.819, 22.862, 22.904, 22.947, 22.99, 23.032, 23.075, 23.117, 23.16, 23.203, 23.245, 23.288, 23.331, 23.373, 23.416, + 23.458, 23.501, 23.544, 23.586, 23.629, 23.671, 23.714, 23.757, 23.799, 23.842, 23.884, 23.927, 23.97, 24.012, 24.055, 24.097, 24.14, 24.182, 24.225, 24.267, 24.31, 24.353, 24.395, 24.438, 24.48, 24.523, 24.565, 24.608, 24.65, 24.693, 24.735, 24.778, 24.82, 24.863, 24.905, 24.948, 24.99, 25.033, 25.075, 25.118, 25.16, 25.203, 25.245, 25.288, 25.33, 25.373, 25.415, 25.458, 25.5, 25.543, 25.585, 25.627, 25.67, 25.712, 25.755, 25.797, 25.84, 25.882, 25.924, 25.967, 26.009, 26.052, 26.094, 26.136, 26.179, 26.221, 26.263, 26.306, 26.348, 26.39, 26.433, 26.475, 26.517, 26.56, 26.602, 26.644, 26.687, 26.729, 26.771, 26.814, 26.856, 26.898, 26.94, 26.983, 27.025, 27.067, 27.109, 27.152, 27.194, 27.236, 27.278, 27.32, 27.363, 27.405, 27.447, 27.489, 27.531, 27.574, 27.616, 27.658, 27.7, 27.742, 27.784, 27.826, 27.869, 27.911, 27.953, 27.995, 28.037, 28.079, 28.121, 28.163, 28.205, 28.247, 28.289, 28.332, 28.374, 28.416, 28.458, 28.5, 28.542, 28.584, 28.626, 28.668, 28.71, 28.752, 28.794, 28.835, 28.877, 28.919, 28.961, 29.003, 29.045, 29.087, 29.129, 29.171, 29.213, 29.255, 29.297, 29.338, 29.38, 29.422, 29.464, 29.506, 29.548, 29.589, 29.631, 29.673, 29.715, 29.757, 29.798, 29.84, 29.882, 29.924, 29.965, 30.007, 30.049, 30.09, 30.132, 30.174, 30.216, 30.257, 30.299, 30.341, 30.382, 30.424, 30.466, 30.507, 30.549, 30.59, 30.632, 30.674, 30.715, 30.757, 30.798, 30.84, 30.881, 30.923, 30.964, 31.006, 31.047, 31.089, 31.13, 31.172, 31.213, 31.255, 31.296, 31.338, 31.379, 31.421, 31.462, 31.504, 31.545, 31.586, 31.628, 31.669, 31.71, 31.752, 31.793, 31.834, 31.876, 31.917, 31.958, 32, 32.041, 32.082, 32.124, 32.165, 32.206, 32.247, 32.289, 32.33, 32.371, 32.412, 32.453, 32.495, 32.536, 32.577, 32.618, 32.659, 32.7, 32.742, 32.783, 32.824, 32.865, 32.906, 32.947, 32.988, 33.029, 33.07, 33.111, 33.152, 33.193, 33.234, 33.275, 33.316, 33.357, 33.398, 33.439, 33.48, 33.521, 33.562, 33.603, 33.644, 33.685, 33.726, 33.767, 33.808, 33.848, 33.889, 33.93, 33.971, 34.012, 34.053, 34.093, 34.134, 34.175, 34.216, 34.257, 34.297, 34.338, 34.379, 34.42, 34.46, 34.501, 34.542, 34.582, 34.623, 34.664, 34.704, 34.745, 34.786, 34.826, 34.867, 34.908, 34.948, 34.989, 35.029, 35.07, 35.11, 35.151, 35.192, 35.232, 35.273, 35.313, 35.354, 35.394, 35.435, 35.475, 35.516, 35.556, 35.596, 35.637, 35.677, 35.718, 35.758, 35.798, 35.839, 35.879, 35.92, 35.96, 36, 36.041, 36.081, 36.121, 36.162, 36.202, 36.242, 36.282, 36.323, 36.363, 36.403, 36.443, 36.484, 36.524, 36.564, 36.604, 36.644, 36.685, 36.725, 36.765, 36.805, 36.845, 36.885, 36.925, 36.965, 37.006, 37.046, 37.086, 37.126, 37.166, 37.206, 37.246, 37.286, 37.326, 37.366, 37.406, 37.446, 37.486, 37.526, 37.566, 37.606, 37.646, 37.686, 37.725, 37.765, 37.805, 37.845, 37.885, 37.925, 37.965, 38.005, 38.044, 38.084, 38.124, 38.164, 38.204, 38.243, 38.283, 38.323, 38.363, 38.402, 38.442, 38.482, 38.522, 38.561, 38.601, 38.641, 38.68, 38.72, 38.76, 38.799, 38.839, 38.878, 38.918, 38.958, 38.997, 39.037, 39.076, 39.116, 39.155, 39.195, 39.235, 39.274, 39.314, 39.353, 39.393, 39.432, 39.471, 39.511, 39.55, 39.59, 39.629, 39.669, 39.708, 39.747, 39.787, 39.826, 39.866, 39.905, 39.944, 39.984, 40.023, 40.062, 40.101, 40.141, 40.18, 40.219, 40.259, 40.298, 40.337, 40.376, 40.415, 40.455, 40.494, 40.533, 40.572, 40.611, 40.651, 40.69, 40.729, 40.768, 40.807, 40.846, 40.885, 40.924, 40.963, 41.002, 41.042, 41.081, 41.12, 41.159, 41.198, 41.237, 41.276, 41.315, 41.354, 41.393, 41.431, 41.47, 41.509, 41.548, 41.587, 41.626, 41.665, 41.704, 41.743, 41.781, 41.82, 41.859, 41.898, 41.937, 41.976, 42.014, 42.053, 42.092, 42.131, 42.169, 42.208, 42.247, 42.286, 42.324, 42.363, 42.402, 42.44, 42.479, 42.518, 42.556, 42.595, 42.633, 42.672, 42.711, 42.749, 42.788, 42.826, 42.865, 42.903, 42.942, 42.98, 43.019, 43.057, 43.096, 43.134, 43.173, 43.211, 43.25, 43.288, 43.327, 43.365, 43.403, 43.442, 43.48, 43.518, 43.557, 43.595, 43.633, 43.672, 43.71, 43.748, 43.787, 43.825, 43.863, 43.901, 43.94, 43.978, 44.016, 44.054, 44.092, 44.13, 44.169, 44.207, 44.245, 44.283, 44.321, 44.359, 44.397, 44.435, 44.473, 44.512, 44.55, 44.588, 44.626, 44.664, 44.702, 44.74, 44.778, 44.816, 44.853, 44.891, 44.929, 44.967, 45.005, 45.043, 45.081, 45.119, 45.157, 45.194, 45.232, 45.27, 45.308, 45.346, 45.383, 45.421, 45.459, 45.497, 45.534, 45.572, 45.61, 45.647, 45.685, 45.723, 45.76, 45.798, 45.836, 45.873, 45.911, 45.948, 45.986, 46.024, 46.061, 46.099, 46.136, 46.174, 46.211, 46.249, 46.286, 46.324, 46.361, 46.398, 46.436, 46.473, 46.511, 46.548, 46.585, 46.623, 46.66, 46.697, 46.735, 46.772, 46.809, 46.847, 46.884, 46.921, 46.958, 46.995, 47.033, 47.07, 47.107, 47.144, 47.181, 47.218, 47.256, 47.293, 47.33, 47.367, 47.404, 47.441, 47.478, 47.515, 47.552, 47.589, 47.626, 47.663, 47.7, 47.737, 47.774, 47.811, 47.848, 47.884, 47.921, 47.958, 47.995, 48.032, 48.069, 48.105, 48.142, 48.179, 48.216, 48.252, 48.289, 48.326, 48.363, 48.399, 48.436, 48.473, 48.509, 48.546, 48.582, 48.619, 48.656, 48.692, 48.729, 48.765, 48.802, 48.838, 48.875, 48.911, 48.948, 48.984, 49.021, 49.057, 49.093, 49.13, 49.166, 49.202, 49.239, 49.275, 49.311, 49.348, 49.384, 49.42, 49.456, 49.493, 49.529, 49.565, 49.601, 49.637, 49.674, 49.71, 49.746, 49.782, 49.818, 49.854, 49.89, 49.926, 49.962, 49.998, 50.034, 50.07, 50.106, 50.142, 50.178, 50.214, 50.25, 50.286, 50.322, 50.358, 50.393, 50.429, 50.465, 50.501, 50.537, 50.572, 50.608, 50.644, 50.68, 50.715, 50.751, 50.787, 50.822, 50.858, 50.894, 50.929, 50.965, 51, 51.036, 51.071, 51.107, 51.142, 51.178, 51.213, 51.249, 51.284, 51.32, 51.355, 51.391, 51.426, 51.461, 51.497, 51.532, 51.567, 51.603, 51.638, 51.673, 51.708, 51.744, 51.779, 51.814, 51.849, 51.885, 51.92, 51.955, 51.99, 52.025, 52.06, 52.095, 52.13, 52.165, 52.2, 52.235, 52.27, 52.305, 52.34, 52.375, 52.41, 52.445, 52.48, 52.515, 52.55, 52.585, 52.62, 52.654, 52.689, 52.724, 52.759, 52.794, 52.828, 52.863, 52.898, 52.932, 52.967, 53.002, 53.037, 53.071, 53.106, 53.14, 53.175, 53.21, 53.244, 53.279, 53.313, 53.348, 53.382, 53.417, 53.451, 53.486, 53.52, 53.555, 53.589, 53.623, 53.658, 53.692, 53.727, 53.761, 53.795, 53.83, 53.864, 53.898, 53.932, 53.967, 54.001, 54.035, 54.069, 54.104, 54.138, 54.172, 54.206, 54.24, 54.274, 54.308, 54.343, 54.377, 54.411, 54.445, 54.479, 54.513, 54.547, 54.581, 54.615, 54.649, 54.683, 54.717, 54.751, 54.785, 54.819, 54.852] +k_list = [0.001, 0.001, 0.001, 0.002, 0.001, 0.002, 0.002, 0.002, 0.002, 0.003, 0.003, 0.003, 0.003, 0.003, 0.004, 0.004, 0.004, 0.004, 0.005, 0.004, 0.005, 0.006, 0.005, 0.006, 0.005, 0.007, 0.006, 0.006, 0.007, 0.007, 0.007, 0.008, 0.007, 0.008, 0.008, 0.009, 0.008, 0.009, 0.009, 0.009, 0.01, 0.009, 0.01, 0.01, 0.01, 0.011, 0.01, 0.011, 0.011, 0.012, 0.011, 0.012, 0.012, 0.012, 0.012, 0.012, 0.013, 0.013, 0.013, 0.013, 0.014, 0.014, 0.013, 0.014, 0.015, 0.014, 0.015, 0.014, 0.015, 0.016, 0.015, 0.015, 0.016, 0.016, 0.016, 0.016, 0.017, 0.017, 0.016, 0.017, 0.017, 0.018, 0.017, 0.018, 0.018, 0.018, 0.018, 0.018, 0.019, 0.019, 0.019, 0.019, 0.019, 0.019, 0.02, 0.019, 0.02, 0.02, 0.021, 0.02, 0.021, 0.02, 0.021, 0.021, 0.021, 0.022, 0.021, 0.022, 0.022, 0.022, 0.022, 0.022, 0.023, 0.022, 0.023, 0.023, 0.023, 0.023, 0.024, 0.023, 0.024, 0.024, 0.024, 0.024, 0.024, 0.025, 0.024, 0.025, 0.025, 0.025, 0.025, 0.026, 0.025, 0.026, 0.025, 0.026, 0.026, 0.027, 0.026, 0.026, 0.027, 0.027, 0.027, 0.027, 0.027, 0.027, 0.028, 0.027, 0.028, 0.028, 0.028, 0.028, 0.028, 0.029, 0.028, 0.029, 0.029, 0.028, 0.029, 0.03, 0.029, 0.029, 0.03, 0.03, 0.029, 0.03, 0.03, 0.031, 0.03, 0.03, 0.031, 0.031, 0.03, 0.031, 0.031, 0.032, 0.031, 0.031, 0.032, 0.031, 0.032, 0.032, 0.032, 0.032, 0.032, 0.033, 0.032, 0.032, 0.033, 0.033, 0.033, 0.033, 0.033, 0.033, 0.033, 0.034, 0.033, 0.034, 0.034, 0.033, 0.034, 0.034, 0.034, 0.035, 0.034, 0.034, 0.035, 0.035, 0.034, 0.035, 0.035, 0.035, 0.035, 0.035, 0.036, 0.035, 0.036, 0.035, 0.036, 0.036, 0.035, 0.036, 0.036, 0.037, 0.036, 0.036, 0.036, 0.037, 0.036, 0.037, 0.037, 0.037, 0.036, 0.037, 0.037, 0.038, 0.037, 0.037, 0.037, 0.038, 0.037, 0.038, 0.038, 0.037, 0.038, 0.038, 0.038, 0.038, 0.038, 0.038, 0.039, 0.038, 0.038, 0.039, 0.038, 0.039, 0.039, 0.038, 0.039, 0.039, 0.039, 0.039, 0.039, 0.039, 0.039, 0.04, 0.039, 0.039, 0.04, 0.039, 0.039, 0.04, 0.04, 0.039, 0.04, 0.04, 0.039, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.041, 0.04, 0.04, 0.04, 0.041, 0.04, 0.041, 0.04, 0.041, 0.04, 0.041, 0.041, 0.04, 0.041, 0.041, 0.041, 0.04, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.042, 0.041, 0.041, 0.041, 0.042, 0.041, 0.041, 0.042, 0.041, 0.041, 0.042, 0.041, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.041, 0.042, 0.041, 0.042, 0.041, 0.041, 0.042, 0.041, 0.041, 0.041, 0.042, 0.041, 0.041, 0.041, 0.041, 0.042, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.04, 0.041, 0.041, 0.041, 0.041, 0.04, 0.041, 0.041, 0.04, 0.041, 0.041, 0.04, 0.041, 0.04, 0.041, 0.041, 0.04, 0.04, 0.041, 0.04, 0.041, 0.04, 0.04, 0.041, 0.04, 0.04, 0.041, 0.04, 0.04, 0.04, 0.04, 0.041, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.041, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.039, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.039, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.039, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.041, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.041, 0.04, 0.04, 0.04, 0.04, 0.041, 0.04, 0.04, 0.04, 0.041, 0.04, 0.04, 0.041, 0.04, 0.04, 0.041, 0.04, 0.041, 0.04, 0.041, 0.04, 0.041, 0.04, 0.041, 0.04, 0.041, 0.04, 0.041, 0.041, 0.04, 0.041, 0.041, 0.04, 0.041, 0.041, 0.041, 0.04, 0.041, 0.041, 0.041, 0.041, 0.04, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.042, 0.041, 0.041, 0.041, 0.041, 0.042, 0.041, 0.041, 0.041, 0.042, 0.041, 0.041, 0.041, 0.042, 0.041, 0.041, 0.042, 0.041, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.042, 0.041, 0.042, 0.042, 0.041, 0.042, 0.042, 0.041, 0.042, 0.042, 0.041, 0.042, 0.042, 0.042, 0.041, 0.042, 0.042, 0.042, 0.041, 0.042, 0.042, 0.042, 0.042, 0.041, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.041, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.043, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.043, 0.042, 0.042, 0.042, 0.042, 0.042, 0.043, 0.042, 0.042, 0.042, 0.042, 0.043, 0.042, 0.042, 0.042, 0.043, 0.042, 0.042, 0.042, 0.043, 0.042, 0.042, 0.043, 0.042, 0.042, 0.042, 0.043, 0.042, 0.042, 0.043, 0.042, 0.042, 0.043, 0.042, 0.042, 0.043, 0.042, 0.043, 0.042, 0.042, 0.043, 0.042, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.042, 0.043, 0.043, 0.042, + 0.043, 0.043, 0.042, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.042, 0.043, 0.042, 0.043, 0.042, 0.043, 0.042, 0.042, 0.043, 0.042, 0.043, 0.042, 0.042, 0.043, 0.042, 0.042, 0.043, 0.042, 0.042, 0.043, 0.042, 0.042, 0.043, 0.042, 0.042, 0.043, 0.042, 0.042, 0.043, 0.042, 0.042, 0.042, 0.043, 0.042, 0.042, 0.042, 0.043, 0.042, 0.042, 0.042, 0.042, 0.043, 0.042, 0.042, 0.042, 0.042, 0.043, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.043, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.043, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.041, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.042, 0.041, 0.042, 0.042, 0.042, 0.042, 0.042, 0.041, 0.042, 0.042, 0.042, 0.042, 0.041, 0.042, 0.042, 0.042, 0.041, 0.042, 0.042, 0.041, 0.042, 0.042, 0.042, 0.041, 0.042, 0.042, 0.041, 0.042, 0.042, 0.041, 0.042, 0.041, 0.042, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.042, 0.041, 0.041, 0.042, 0.041, 0.041, 0.042, 0.041, 0.041, 0.042, 0.041, 0.041, 0.042, 0.041, 0.041, 0.042, 0.041, 0.041, 0.041, 0.042, 0.041, 0.041, 0.041, 0.041, 0.042, 0.041, 0.041, 0.041, 0.041, 0.041, 0.042, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.041, 0.04, 0.041, 0.041, 0.041, 0.041, 0.041, 0.04, 0.041, 0.041, 0.041, 0.041, 0.04, 0.041, 0.041, 0.041, 0.04, 0.041, 0.041, 0.04, 0.041, 0.041, 0.04, 0.041, 0.041, 0.04, 0.041, 0.041, 0.04, 0.041, 0.04, 0.041, 0.04, 0.041, 0.041, 0.04, 0.041, 0.04, 0.041, 0.04, 0.041, 0.04, 0.041, 0.04, 0.04, 0.041, 0.04, 0.041, 0.04, 0.04, 0.041, 0.04, 0.041, 0.04, 0.04, 0.041, 0.04, 0.04, 0.041, 0.04, 0.04, 0.04, 0.041, 0.04, 0.04, 0.04, 0.041, 0.04, 0.04, 0.04, 0.04, 0.041, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.041, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.039, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.039, 0.04, 0.04, 0.04, 0.04, 0.039, 0.04, 0.04, 0.04, 0.039, 0.04, 0.04, 0.04, 0.039, 0.04, 0.04, 0.039, 0.04, 0.04, 0.039, 0.04, 0.039, 0.04, 0.04, 0.039, 0.04, 0.039, 0.04, 0.039, 0.04, 0.04, 0.039, 0.04, 0.039, 0.04, 0.039, 0.039, 0.04, 0.039, 0.04, 0.039, 0.04, 0.039, 0.039, 0.04, 0.039, 0.04, 0.039, 0.039, 0.04, 0.039, 0.039, 0.039, 0.04, 0.039, 0.039, 0.04, 0.039, 0.039, 0.039, 0.039, 0.04, 0.039, 0.039, 0.039, 0.039, 0.04, 0.039, 0.039, 0.039, 0.039, 0.039, 0.039, 0.039, 0.039, 0.039, 0.04, 0.039, 0.039, 0.039, 0.039, 0.039, 0.039, 0.039, 0.039, 0.039, 0.038, 0.039, 0.039, 0.039, 0.039, 0.039, 0.039, 0.039, 0.039, 0.038, 0.039, 0.039, 0.039, 0.039, 0.039, 0.038, 0.039, 0.039, 0.039, 0.038, 0.039, 0.039, 0.039, 0.038, 0.039, 0.039, 0.038, 0.039, 0.039, 0.038, 0.039, 0.038, 0.039, 0.039, 0.038, 0.039, 0.038, 0.039, 0.038, 0.039, 0.038, 0.039, 0.038, 0.039, 0.038, 0.039, 0.038, 0.039, 0.038, 0.039, 0.038, 0.038, 0.039, 0.038, 0.038, 0.039, 0.038, 0.038, 0.039, 0.038, 0.038, 0.039, 0.038, 0.038, 0.038, 0.039, 0.038, 0.038, 0.038, 0.038, 0.038, 0.039, 0.038, 0.038, 0.038, 0.038, 0.038, 0.038, 0.038, 0.038, 0.039, 0.038, 0.038, 0.038, 0.038, 0.038, 0.038, 0.038, 0.038, 0.037, 0.038, 0.038, 0.038, 0.038, 0.038, 0.038, 0.038, 0.038, 0.037, 0.038, 0.038, 0.038, 0.038, 0.037, 0.038, 0.038, 0.038, 0.037, 0.038, 0.038, 0.037, 0.038, 0.038, 0.037, 0.038, 0.038, 0.037, 0.038, 0.037, 0.038, 0.038, 0.037, 0.038, 0.037, 0.038, 0.037, 0.038, 0.037, 0.038, 0.037, 0.037, 0.038, 0.037, 0.038, 0.037, 0.037, 0.038, 0.037, 0.037, 0.038, 0.037, 0.037, 0.038, 0.037, 0.037, 0.037, 0.037, 0.038, 0.037, 0.037, 0.037, 0.037, 0.037, 0.038, 0.037, 0.037, 0.037, 0.037, 0.037, 0.037, 0.037, 0.037, 0.037, 0.037, 0.037, 0.037, 0.037, 0.037, 0.037, 0.037, 0.036, 0.037, 0.037, 0.037, 0.037, 0.037, 0.036, 0.037, 0.037, 0.037, 0.036, 0.037, 0.037, 0.037, 0.036, 0.037, 0.037, 0.036, 0.037, 0.036, 0.037, 0.037, 0.036, 0.037, 0.036, 0.037, 0.036, 0.037, 0.036, 0.037, 0.036, 0.037, 0.036, 0.036, 0.037, 0.036, 0.036, 0.037, 0.036, 0.036, 0.037, 0.036, 0.036, 0.036, 0.037, 0.036, 0.036, 0.036, 0.036, 0.037, 0.036, 0.036, 0.036, 0.036, 0.036, 0.036, 0.036, 0.036, 0.036, 0.036, 0.036, 0.036, 0.036, 0.036, 0.036, 0.036, 0.036, 0.036, 0.036, 0.035, 0.036, 0.036, 0.036, 0.036, 0.035, 0.036, 0.036, 0.036, 0.035, 0.036, 0.036, 0.035, 0.036, 0.036, 0.035, 0.036, 0.035, 0.036, 0.035, 0.036, 0.035, 0.036, 0.035, 0.036, 0.035, 0.036, 0.035, 0.036, 0.035, 0.035, 0.036, 0.035, 0.035, 0.036, 0.035, 0.035, 0.035, 0.036, 0.035, 0.035, 0.035, 0.036, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035, 0.035, 0.034, 0.035, 0.035, 0.035, 0.035, 0.034, 0.035, 0.035, 0.034, 0.035, 0.035, 0.035, 0.034, 0.035, 0.034, 0.035, 0.035, 0.034, 0.035, 0.034, 0.035, 0.034, 0.035, 0.034, 0.035, 0.034, 0.035, 0.034, 0.034, 0.035, 0.034, 0.035, 0.034, 0.034, 0.035, 0.034, 0.034, 0.034, 0.035, 0.034, 0.034, 0.034, 0.035, 0.034, 0.034, 0.034, 0.034, 0.034, 0.034, 0.035, 0.034, 0.034, 0.034, 0.034, 0.034, 0.034, 0.034, 0.034, 0.034, 0.034, 0.034, 0.034, 0.034, 0.034, 0.033, 0.034] +b_list = [6188, 6188, 6188, 2960.5, 6187, 2961, 2961, 2961, 2961, 1887, 1887, 1887, 1887, 1887, 1351.25, 1351.25, 1351.25, 1351.25, 1030.6, 1351, 1030.8, 817.5, 1030.6, 817.6666667, 1030.4, 666, 817.6666667, 817.6666667, 666.2857143, 666.2857143, 666.2857143, 553.125, 666.1428571, 553.25, 553.25, 465.6666667, 553.125, 465.7777778, 465.7777778, 465.7777778, 396.2, 465.6666667, 396.3, 396.3, 396.3, 339.8181818, 396.2, 339.9090909, 339.9090909, 293.1666667, 339.8181818, 293.25, 293.25, 293.25, 293.25, 293.25, 254.2307692, 254.2307692, 254.2307692, 254.2307692, 221.0714286, 221.0714286, 254.0769231, 221.1428571, 192.6666667, 221.0714286, 192.7333333, 221, 192.8, 168.1875, 192.7333333, 192.7333333, 168.3125, 168.3125, 168.3125, 168.3125, 147, 147, 168.1875, 147.0588235, 147.0588235, 128.3888889, 147, 128.4444444, 128.4444444, 128.4444444, 128.4444444, 128.4444444, 112.1052632, 112.1052632, 112.1052632, 112.1052632, 112.1052632, 112.1052632, 97.7, 112.0526316, 97.75, 97.75, 84.9047619, 97.7, 84.95238095, 97.65, 85, 85, 85, 73.63636364, 84.95238095, 73.68181818, 73.68181818, 73.68181818, 73.68181818, 73.68181818, 63.60869565, 73.63636364, 63.65217391, 63.65217391, 63.65217391, 63.65217391, 54.66666667, 63.60869565, 54.70833333, 54.70833333, 54.70833333, 54.70833333, 54.70833333, 46.72, 54.66666667, 46.76, 46.76, 46.76, 46.76, 39.61538462, 46.72, 39.65384615, 46.68, 39.69230769, 39.69230769, 33.2962963, 39.65384615, 39.65384615, 33.37037037, 33.37037037, 33.37037037, 33.37037037, 33.37037037, 33.37037037, 27.75, 33.33333333, 27.78571429, 27.78571429, 27.78571429, 27.78571429, 27.78571429, 22.79310345, 27.75, 22.82758621, 22.82758621, 27.67857143, 22.86206897, 18.4, 22.82758621, 22.82758621, 18.46666667, 18.46666667, 22.75862069, 18.5, 18.5, 14.58064516, 18.46666667, 18.46666667, 14.64516129, 14.64516129, 18.4, 14.67741935, 14.67741935, 11.25, 14.64516129, 14.64516129, 11.3125, 14.61290323, 11.34375, 11.34375, 11.34375, 11.34375, 11.34375, 8.424242424, 11.3125, 11.3125, 8.484848485, 8.484848485, 8.484848485, 8.484848485, 8.484848485, 8.484848485, 8.484848485, 6.029411765, 8.454545455, 6.058823529, 6.058823529, 8.393939394, 6.088235294, 6.088235294, 6.088235294, 4, 6.058823529, 6.058823529, 4.057142857, 4.057142857, 6, 4.085714286, 4.085714286, 4.085714286, 4.085714286, 4.085714286, 2.416666667, 4.057142857, 2.444444444, 4.028571429, 2.472222222, 2.472222222, 3.971428571, 2.5, 2.5, 1.162162162, 2.472222222, 2.472222222, 2.472222222, 1.243243243, 2.444444444, 1.27027027, 1.27027027, 1.27027027, 2.361111111, 1.297297297, 1.297297297, 0.342105263, 1.27027027, 1.27027027, 1.27027027, 0.421052632, 1.243243243, 0.447368421, 0.447368421, 1.189189189, 0.473684211, 0.473684211, 0.473684211, 0.473684211, 0.473684211, 0.473684211, -0.051282051, 0.447368421, 0.447368421, 0, 0.421052632, 0.025641026, 0.025641026, 0.368421053, 0.051282051, 0.051282051, 0.051282051, 0.051282051, 0.051282051, 0.051282051, 0.051282051, -0.075, 0.025641026, 0.025641026, -0.025, 0, 0, 0.025, 0.025, -0.051282051, 0.05, 0.05, -0.102564103, 0.075, 0.075, 0.075, 0.075, 0.075, 0.075, 0.075, 0.075, 0.075, 0.075, 0.487804878, 0.05, 0.05, 0.05, 0.56097561, 0.025, 0.585365854, 0, 0.609756098, -0.025, 0.634146341, 0.634146341, -0.075, 0.658536585, 0.658536585, 0.658536585, -0.15, 0.682926829, 0.682926829, 0.682926829, 0.682926829, 0.682926829, 0.682926829, 0.682926829, 0.682926829, 0.682926829, 0.682926829, 0.682926829, 0.682926829, 0.682926829, 1.785714286, 0.658536585, 0.658536585, 0.658536585, 1.857142857, 0.634146341, 0.634146341, 1.904761905, 0.609756098, 0.609756098, 1.952380952, 0.585365854, 0.585365854, 2, 0.56097561, 2.023809524, 0.536585366, 2.047619048, 0.512195122, 2.071428571, 0.487804878, 2.095238095, 0.463414634, 2.119047619, 0.43902439, 2.142857143, 0.414634146, 2.166666667, 0.390243902, 2.19047619, 2.19047619, 0.341463415, 2.214285714, 0.317073171, 2.238095238, 0.292682927, 2.261904762, 0.268292683, 2.285714286, 0.243902439, 2.30952381, 0.219512195, 2.333333333, 0.195121951, 2.357142857, 0.170731707, 2.380952381, 0.146341463, 2.404761905, 0.12195122, 0.12195122, 2.452380952, 0.097560976, 2.476190476, 0.073170732, 0.073170732, 2.523809524, 0.048780488, 0.048780488, 0.048780488, 2.595238095, 0.024390244, 0.024390244, 0.024390244, 0.024390244, 2.69047619, 1.02318e-12, -1.44951e-12, 1.03739e-12, -1.47793e-12, 1.06581e-12, -1.49214e-12, 1.08002e-12, 1.08002e-12, -1.54898e-12, 1.10845e-12, -1.56319e-12, -3.1, 0.024390244, 0.024390244, 0.024390244, 0.024390244, -3.2, 0.048780488, 0.048780488, -3.25, 0.073170732, 0.073170732, -3.3, 0.097560976, -3.325, 0.12195122, 0.12195122, -3.375, -3.375, 0.170731707, -3.4, 0.195121951, -3.425, -3.425, 0.243902439, -3.45, -3.45, 0.292682927, -3.475, -3.475, -3.475, -3.475, 0.390243902, -3.5, -3.5, -3.5, -3.5, -3.5, -3.5, -3.5, 0.56097561, -3.525, -3.525, -3.525, -3.525, -3.525, -3.525, -3.525, -3.525, -8.025641026, -3.5, -3.5, -3.5, -3.5, -3.5, -3.5, -3.5, -3.5, -3.5, -3.5, -3.5, -3.5, -8.333333333, -3.475, -3.475, -3.475, -3.475, -3.475, -3.475, -3.475, -3.475, -3.475, -3.475, -3.475, -3.475, -3.475, -8.666666667, -3.45, -3.45, -3.45, -3.45, -3.45, -3.45, -3.45, -3.45, 1.707317073, -3.475, -3.475, -3.475, -3.475, -3.475, -3.475, -3.475, -3.475, 1.902439024, -3.5, -3.5, -3.5, -3.5, 2, -3.525, -3.525, -3.525, 2.073170732, -3.55, -3.55, 2.12195122, -3.575, -3.575, 2.170731707, -3.6, 2.195121951, -3.625, 2.219512195, -3.65, 2.243902439, -3.675, 2.268292683, -3.7, 2.292682927, -3.725, 2.317073171, 2.317073171, -3.775, 2.341463415, 2.341463415, -3.825, 2.365853659, 2.365853659, 2.365853659, -3.9, 2.390243902, 2.390243902, 2.390243902, 2.390243902, -4, 2.414634146, 2.414634146, 2.414634146, 2.414634146, 2.414634146, 2.414634146, 2.414634146, 2.414634146, 2.414634146, 2.414634146, 2.414634146, 2.414634146, 2.414634146, 2.414634146, 2.414634146, 2.414634146, 2.414634146, 8.928571429, 2.390243902, 2.390243902, 2.390243902, 2.390243902, 9.023809524, 2.365853659, 2.365853659, 2.365853659, 9.095238095, 2.341463415, 2.341463415, 2.341463415, 9.166666667, 2.317073171, 2.317073171, 9.214285714, 2.292682927, 2.292682927, 9.261904762, 2.268292683, 9.285714286, 2.243902439, 9.30952381, 2.219512195, 2.219512195, 9.357142857, 2.195121951, 9.380952381, 2.170731707, 9.404761905, 2.146341463, 9.428571429, 9.428571429, 2.097560976, 9.452380952, 2.073170732, 9.476190476, 2.048780488, 9.5, 9.5, 2, 9.523809524, 9.523809524, 1.951219512, 9.547619048, 9.547619048, 1.902439024, 9.571428571, 9.571428571, 1.853658537, 9.595238095, 9.595238095, 9.595238095, 1.780487805, 9.619047619, 9.619047619, 9.619047619, 1.707317073, 9.642857143, 9.642857143, 9.642857143, 9.642857143, 1.609756098, 9.666666667, 9.666666667, 9.666666667, 9.666666667, 9.666666667, 9.666666667, 1.463414634, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 9.69047619, 18.27906977, 9.666666667, 9.666666667, 9.666666667, 9.666666667, 9.666666667, 9.666666667, 18.41860465, 9.642857143, 9.642857143, 9.642857143, 9.642857143, 9.642857143, 18.53488372, 9.619047619, 9.619047619, 9.619047619, 9.619047619, 18.62790698, 9.595238095, 9.595238095, 9.595238095, 18.69767442, 9.571428571, 9.571428571, 9.571428571, 18.76744186, 9.547619048, 9.547619048, 18.81395349, 9.523809524, 9.523809524, 9.523809524, 18.88372093, 9.5, 9.5, 18.93023256, 9.476190476, 9.476190476, 18.97674419, 9.452380952, 9.452380952, 19.02325581, 9.428571429, 19.04651163, 9.404761905, 9.404761905, 19.09302326, 9.380952381, 9.380952381, 19.13953488, 9.357142857, 19.1627907, 9.333333333, 19.18604651, 9.30952381, 9.30952381, 19.23255814, 9.285714286, 19.25581395, 9.261904762, 19.27906977, 9.238095238, 9.238095238, 19.3255814, 9.214285714, 19.34883721, 9.19047619, 19.37209302, 9.166666667, 19.39534884, 9.142857143, 19.41860465, 9.119047619, 19.44186047, 9.095238095, 19.46511628, 9.071428571, 19.48837209, 9.047619048, 19.51162791, 9.023809524, 19.53488372, 9, 19.55813953, 19.55813953, 8.952380952, 19.58139535, 8.928571429, 19.60465116, 8.904761905, 19.62790698, 8.880952381, 19.65116279, 19.65116279, 8.833333333, 19.6744186, 8.80952381, 19.69767442, 19.69767442, 8.761904762, 19.72093023, 8.738095238, 19.74418605, 8.714285714, 19.76744186, 19.76744186, 8.666666667, 19.79069767, 8.642857143, 19.81395349, 19.81395349, 8.595238095, 19.8372093, 19.8372093, 8.547619048, 19.86046512, 8.523809524, 19.88372093, 19.88372093, 8.476190476, 19.90697674, 19.90697674, 8.428571429, 19.93023256, 8.404761905, 19.95348837, 19.95348837, 8.357142857, 19.97674419, 19.97674419, 8.30952381, 20, 20, 8.261904762, 20.02325581, 8.238095238, 20.04651163, 20.04651163, 8.19047619, 20.06976744, 20.06976744, 8.142857143, 20.09302326, 20.09302326, 8.095238095, 20.11627907, 20.11627907, 8.047619048, 20.13953488, 20.13953488, 8, 20.1627907, 20.1627907, 7.952380952, 20.18604651, 7.928571429, 20.20930233, 20.20930233, 7.880952381, 20.23255814, 20.23255814, 7.833333333, 20.25581395, 20.25581395, 7.785714286, 20.27906977, 20.27906977, 7.738095238, 20.30232558, 7.714285714, 20.3255814, 20.3255814, 7.666666667, 20.34883721, 20.34883721, 7.619047619, 20.37209302, 7.595238095, 20.39534884, 20.39534884, 7.547619048, 20.41860465, 20.41860465, 7.5, 20.44186047, 7.476190476, 20.46511628, 20.46511628, 7.428571429, 20.48837209, 7.404761905, 20.51162791, 20.51162791, 7.357142857, 20.53488372, 7.333333333, 20.55813953, 20.55813953, 7.285714286, 20.58139535, 7.261904762, 20.60465116, 7.238095238, 20.62790698, 7.214285714, 20.65116279, 20.65116279, 7.166666667, 20.6744186, 7.142857143, 20.69767442, 7.119047619, 20.72093023, 7.095238095, + 20.74418605, 7.071428571, 20.76744186, 7.047619048, 20.79069767, 7.023809524, 20.81395349, 7, 20.8372093, 6.976190476, 20.86046512, 6.952380952, 20.88372093, 6.928571429, 20.90697674, 6.904761905, 20.93023256, 6.880952381, 20.95348837, 6.857142857, 20.97674419, 6.833333333, 6.833333333, 21.02325581, 6.809523809, 21.04651163, 6.785714286, 21.06976744, 6.761904762, 6.761904762, 21.11627907, 6.738095238, 21.13953488, 6.714285714, 6.714285714, 21.18604651, 6.69047619, 6.69047619, 21.23255814, 6.666666667, 6.666666667, 21.27906977, 6.642857143, 6.642857143, 21.3255814, 6.619047619, 6.619047619, 21.37209302, 6.595238095, 6.595238095, 21.41860465, 6.571428571, 6.571428571, 6.571428571, 21.48837209, 6.547619048, 6.547619048, 6.547619048, 21.55813953, 6.523809524, 6.523809524, 6.523809524, 6.523809524, 21.65116279, 6.5, 6.5, 6.5, 6.5, 21.74418605, 6.476190476, 6.476190476, 6.476190476, 6.476190476, 6.476190476, 6.476190476, 21.88372093, 6.452380952, 6.452380952, 6.452380952, 6.452380952, 6.452380952, 6.452380952, 6.452380952, 6.452380952, 6.452380952, 6.452380952, 22.11627907, 6.428571429, 6.428571429, 6.428571429, 6.428571429, 6.428571429, 6.428571429, 6.428571429, 6.428571429, 6.428571429, 6.428571429, 6.428571429, -10.29268293, 6.452380952, 6.452380952, 6.452380952, 6.452380952, 6.452380952, 6.452380952, 6.452380952, 6.452380952, 6.452380952, 6.452380952, 6.452380952, -10.56097561, 6.476190476, 6.476190476, 6.476190476, 6.476190476, 6.476190476, -10.68292683, 6.5, 6.5, 6.5, 6.5, -10.7804878, 6.523809524, 6.523809524, 6.523809524, -10.85365854, 6.547619048, 6.547619048, -10.90243902, 6.571428571, 6.571428571, 6.571428571, -10.97560976, 6.595238095, 6.595238095, -11.02439024, 6.619047619, 6.619047619, -11.07317073, 6.642857143, -11.09756098, 6.666666667, 6.666666667, -11.14634146, 6.690476191, -11.17073171, 6.714285714, -11.19512195, 6.738095238, -11.2195122, 6.761904762, -11.24390244, 6.785714286, -11.26829268, 6.80952381, -11.29268293, 6.833333333, -11.31707317, 6.857142857, -11.34146341, 6.880952381, -11.36585366, 6.904761905, -11.3902439, -11.3902439, 6.952380952, -11.41463415, -11.41463415, 7, -11.43902439, -11.43902439, 7.047619048, -11.46341463, -11.46341463, 7.095238095, -11.48780488, -11.48780488, 7.142857143, -11.51219512, -11.51219512, -11.51219512, 7.214285714, -11.53658537, -11.53658537, -11.53658537, -11.53658537, 7.309523809, -11.56097561, -11.56097561, -11.56097561, -11.56097561, -11.56097561, 7.428571428, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -11.58536585, -32.2, -11.56097561, -11.56097561, -11.56097561, -11.56097561, -11.56097561, -32.325, -11.53658537, -11.53658537, -11.53658537, -11.53658537, -32.425, -11.51219512, -11.51219512, -11.51219512, -32.5, -11.48780488, -11.48780488, -32.55, -11.46341463, -11.46341463, -32.6, -11.43902439, -11.43902439, -32.65, -11.41463415, -11.41463415, -32.7, -11.3902439, -32.725, -11.36585366, -32.75, -11.34146341, -11.34146341, -32.8, -11.31707317, -32.825, -11.29268293, -32.85, -11.26829268, -32.875, -11.24390244, -32.9, -32.9, -11.19512195, -32.925, -11.17073171, -32.95, -32.95, -11.12195122, -32.975, -11.09756098, -33, -33, -11.04878049, -33.025, -33.025, -11, -33.05, -33.05, -33.05, -10.92682927, -33.075, -33.075, -33.075, -10.85365854, -33.1, -33.1, -33.1, -33.1, -10.75609756, -33.125, -33.125, -33.125, -33.125, -33.125, -33.125, -33.125, -10.58536585, -33.15, -33.15, -33.15, -33.15, -33.15, -33.15, -33.15, -33.15, -33.15, -33.15, -33.15, -33.15, -33.15, -33.15, -33.15, -33.15, -33.15, -57.30769231, -33.125, -33.125, -33.125, -33.125, -33.125, -33.125, -33.125, -57.48717949, -33.1, -33.1, -33.1, -33.1, -57.58974359, -33.075, -33.075, -33.075, -57.66666667, -33.05, -33.05, -33.05, -57.74358974, -33.025, -33.025, -57.79487179, -33, -33, -57.84615385, -32.975, -57.87179487, -32.95, -32.95, -57.92307692, -32.925, -57.94871795, -32.9, -57.97435897, -32.875, -32.875, -58.02564103, -32.85, -58.05128205, -32.825, -58.07692308, -58.07692308, -32.775, -58.1025641, -32.75, -58.12820513, -32.725, -58.15384615, -58.15384615, -32.675, -58.17948718, -32.65, -58.20512821, -58.20512821, -32.6, -58.23076923, -58.23076923, -58.23076923, -32.525, -58.25641026, -58.25641026, -32.475, -58.28205128, -58.28205128, -58.28205128, -58.28205128, -32.375, -58.30769231, -58.30769231, -58.30769231, -58.30769231, -32.275, -58.33333333, -58.33333333, -58.33333333, -58.33333333, -58.33333333, -58.33333333, -58.33333333, -58.33333333, -58.33333333, -32.05, -58.35897436, -58.35897436, -58.35897436, -58.35897436, -58.35897436, -58.35897436, -58.35897436, -58.35897436, -58.35897436, -86.28947368, -58.33333333, -58.33333333, -58.33333333, -58.33333333, -58.33333333, -58.33333333, -58.33333333, -58.33333333, -86.5, -58.30769231, -58.30769231, -58.30769231, -58.30769231, -58.30769231, -86.63157895, -58.28205128, -58.28205128, -58.28205128, -86.71052632, -58.25641026, -58.25641026, -58.25641026, -86.78947368, -58.23076923, -58.23076923, -86.84210526, -58.20512821, -58.20512821, -86.89473684, -58.17948718, -86.92105263, -58.15384615, -58.15384615, -86.97368421, -58.12820513, -87, -58.1025641, -87.02631579, -58.07692308, -87.05263158, -58.05128205, -87.07894737, -58.02564103, -87.10526316, -58, -87.13157895, -57.97435897, -87.15789474, -57.94871795, -87.18421053, -87.18421053, -57.8974359, -87.21052632, -87.21052632, -57.84615385, -87.23684211, -87.23684211, -57.7948718, -87.26315789, -87.26315789, -57.74358974, -87.28947368, -87.28947368, -87.28947368, -57.66666667, -87.31578947, -87.31578947, -87.31578947, -87.31578947, -87.31578947, -57.53846154, -87.34210526, -87.34210526, -87.34210526, -87.34210526, -87.34210526, -87.34210526, -87.34210526, -87.34210526, -57.33333333, -87.36842105, -87.36842105, -87.36842105, -87.36842105, -87.36842105, -87.36842105, -87.36842105, -87.36842105, -119.2432432, -87.34210526, -87.34210526, -87.34210526, -87.34210526, -87.34210526, -87.34210526, -87.34210526, -87.34210526, -119.4594595, -87.31578947, -87.31578947, -87.31578947, -87.31578947, -119.5675676, -87.28947368, -87.28947368, -87.28947368, -119.6486486, -87.26315789, -87.26315789, -119.7027027, -87.23684211, -87.23684211, -119.7567568, -87.21052632, -87.21052632, -119.8108108, -87.18421053, -119.8378378, -87.15789474, -87.15789474, -119.8918919, -87.13157895, -119.9189189, -87.10526316, -119.9459459, -87.07894737, -119.972973, -87.05263158, -120, -120, -87, -120.027027, -86.97368421, -120.0540541, -120.0540541, -86.92105263, -120.0810811, -120.0810811, -86.86842105, -120.1081081, -120.1081081, -86.81578947, -120.1351351, -120.1351351, -120.1351351, -120.1351351, -86.71052632, -120.1621622, -120.1621622, -120.1621622, -120.1621622, -120.1621622, -86.57894737, -120.1891892, -120.1891892, -120.1891892, -120.1891892, -120.1891892, -120.1891892, -120.1891892, -120.1891892, -120.1891892, -120.1891892, -120.1891892, -120.1891892, -120.1891892, -120.1891892, -120.1891892, -120.1891892, -156.1111111, -120.1621622, -120.1621622, -120.1621622, -120.1621622, -120.1621622, -156.25, -120.1351351, -120.1351351, -120.1351351, -156.3333333, -120.1081081, -120.1081081, -120.1081081, -156.4166667, -120.0810811, -120.0810811, -156.4722222, -120.0540541, -156.5, -120.027027, -120.027027, -156.5555556, -120, -156.5833333, -119.972973, -156.6111111, -119.9459459, -156.6388889, -119.9189189, -156.6666667, -119.8918919, -156.6944444, -156.6944444, -119.8378378, -156.7222222, -156.7222222, -119.7837838, -156.75, -156.75, -119.7297297, -156.7777778, -156.7777778, -156.7777778, -119.6486486, -156.8055556, -156.8055556, -156.8055556, -156.8055556, -119.5405405, -156.8333333, -156.8333333, -156.8333333, -156.8333333, -156.8333333, -156.8333333, -156.8333333, -156.8333333, -156.8333333, -156.8333333, -156.8333333, -156.8333333, -156.8333333, -156.8333333, -156.8333333, -156.8333333, -156.8333333, -156.8333333, -156.8333333, -196.8, -156.8055556, -156.8055556, -156.8055556, -156.8055556, -196.9142857, -156.7777778, -156.7777778, -156.7777778, -197, -156.75, -156.75, -197.0571429, -156.7222222, -156.7222222, -197.1142857, -156.6944444, -197.1428571, -156.6666667, -197.1714286, -156.6388889, -197.2, -156.6111111, -197.2285714, -156.5833333, -197.2571429, -156.5555556, -197.2857143, -156.5277778, -197.3142857, -197.3142857, -156.4722222, -197.3428571, -197.3428571, -156.4166667, -197.3714286, -197.3714286, -197.3714286, -156.3333333, -197.4, -197.4, -197.4, -156.25, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -197.4285714, -241.6470588, -197.4, -197.4, -197.4, -197.4, -241.7647059, -197.3714286, -197.3714286, -241.8235294, -197.3428571, -197.3428571, -197.3428571, -241.9117647, -197.3142857, -241.9411765, -197.2857143, -197.2857143, -242, -197.2571429, -242.0294118, -197.2285714, -242.0588235, -197.2, -242.0882353, -197.1714286, -242.1176471, -197.1428571, -242.1470588, -242.1470588, -197.0857143, -242.1764706, -197.0571429, -242.2058824, -242.2058824, -197, -242.2352941, -242.2352941, -242.2352941, -196.9142857, -242.2647059, -242.2647059, -242.2647059, -196.8285714, -242.2941176, -242.2941176, -242.2941176, -242.2941176, -242.2941176, -242.2941176, -196.6571429, -242.3235294, -242.3235294, -242.3235294, -242.3235294, -242.3235294, -242.3235294, -242.3235294, -242.3235294, -242.3235294, -242.3235294, -242.3235294, -242.3235294, -242.3235294, -242.3235294, -291.1818182, -242.2941176] + + +Flag = { + "BaoYa_Flag": False, + "SHIYAN_Flag": False, + "temp_before_fire": None, + "temp_before_fire1": None +} diff --git a/app/lib/Utils.py b/app/lib/Utils.py new file mode 100644 index 0000000..65fbee4 --- /dev/null +++ b/app/lib/Utils.py @@ -0,0 +1,479 @@ +import uuid +import os +import json +import struct +import hashlib +import random +import copy +from idlelib.iomenu import encoding + +import psutil +import platform +import datetime +import numpy as np +from app.lib.Log import logger +from app.lib.AlchemyJsonEncoder import * +from app.conf.Setting import settings +import matplotlib.pyplot as plt +from scipy.optimize import curve_fit + + +system_name = platform.system() + + +"""通用工具类""" +class Utils(object): + def __init__(self, *args, **kwargs): + return super().__init__(*args, **kwargs) + + #MD5加密 + def MD5(str): + hl = hashlib.md5() + hl.update(str.encode(encoding='utf-8')) + return hl.hexdigest() + + #获取uuid + def get_uuid(): + return str(uuid.uuid1()) + + def str_to_time(str): + return datetime.datetime.strptime(str, '%Y-%m-%d %H:%M:%S') + + #获取当前时间 + def get_time(): + return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + #请求成功返回 + def true_return(data='', desc='请求成功', code=200): + data = { + "code": code, + "desc": desc, + "data": data + } + return data + + def get_md5(input_str: str, encoding: str = 'utf-8') -> str: + """ + 获取字符串的 MD5 哈希值 + + Args: + input_str: 输入字符串 + encoding: 编码方式(默认UTF-8) + + Returns: + 32位小写十六进制MD5哈希值 + """ + # 创建MD5哈希对象 + md5_hash = hashlib.md5() + # 将字符串编码为字节并更新哈希对象 + md5_hash.update(input_str.encode(encoding)) + # 返回十六进制哈希值 + return md5_hash.hexdigest() + + + def get_db_path(): + """ + + Returns: + 数据库文件路径 + """ + # 获取当前文件所在目录的绝对路径(即 'app/api') + base_dir = os.path.dirname(os.path.abspath(__file__)) + + # 获取项目根目录(假设 'app' 目录在项目根目录下) + project_root = os.path.dirname(base_dir) # 这个将得到 'instrument_short_circuit' + + # 拼接数据库文件路径 + db_path = os.path.join(project_root, 'db', 'Data.db') + return db_path + + + #分页请求成功返回 + def true_return_pagination(pagination=None, data=None, desc='请求成功', code=200): + data = { + "pagination": pagination, + "code": code, + "desc": desc, + "data": data + } + return data + + #请求失败返回 + def false_return(data='', desc='请求失败', code=-1): + data = { + "code": code, + "desc": desc, + "data": data + } + return data + + #生成统一格式接口数据 + def ResultData(status,message,data=""): + return json.dumps({"status":status,"message":message,"data":data},cls=AlchemyEncoder) + + #按时间生成随机文件名 + def getFileName(): + nowTime = datetime.datetime.now().strftime("%Y%m%d%H%M%S") + randomNum = random.randint(0,100) + if randomNum <= 10: + randomNum = str(0) + str(randomNum) + uniqueNum = str(nowTime) + str(randomNum) + return uniqueNum + + #获取当前插入U盘路径 + def getUDiskPath(): + if system_name == 'Windows': + disk_list = psutil.disk_partitions() + # 获取U盘路径 + u_path = [disk.device for disk in disk_list if disk.opts == 'rw,removable'] + if u_path: + return u_path[0] + elif system_name == 'Linux': + import pyudev + context = pyudev.Context() + removable = [device for device in context.list_devices(subsystem='block', DEVTYPE='disk') if device.attributes.asstring('removable') == "1"] + for device in removable: + partitions = [device.device_node for device in context.list_devices(subsystem='block', DEVTYPE='partition', parent=device)] + for p in psutil.disk_partitions(): + if p.device in partitions: + return p.mountpoint + return "" + + #创建文件夹 + def mkdir(path): + folder = os.path.exists(path) + if not folder: + os.makedirs(path) + + #int列表转换为字节流 + def int_list_to_bytes(int_list): + byte_stream = b'' + for num in int_list: + byte_stream += struct.pack('>H', num) + return byte_stream + + #字节流转换为int列表 + def bytes_to_int_list(byte_stream): + int_list = [] + for i in range(0, byte_stream.__len__(), 2): + int_list.append(struct.unpack('>H', byte_stream[i:i+2])[0]) + + return int_list + + #获取某比特的值 + def get_bit(data: int, bit: int) -> int: + if data & (1 << bit): + return 1 + else: + return 0 + + #获取某比特的值,并转换为布尔值 + def get_bit_bool(data: int, bit: int) -> bool: + if data & (1 << bit): + return True + else: + return False + + #修改某比特的值,并返回新值 + def set_bit(number: int, pos: int, value: int): + new_number = None + if value == 1: + new_number = number | (1 << pos) + elif value == 0: + new_number = number & ~(1 << pos) + return new_number + + + #获取温度数据 + def str_to_list(str_list): + if str_list: + str_list = str_list[:-1].strip('[]') + result = [list(map(eval, sublist.split(','))) for sublist in str_list.split('],[')] + return result + return [] + + + #上升速率,下降率,data数据列表,num时间间隔,tag(1罐体2炉体),t=0返回x轴为温度,反之为时间 + def calculate_temp_rate(data,num,tag,t=0): + result = [] + x=[] + y1 = [] + y2 = [] + for i in range(0, len(data)-num, 30): + time_interval = data[i+num][0] - data[i][0] + if time_interval == 0: + continue + temp_rate = (data[i+num][1] - data[i][1]) / time_interval * 3600 + if t==0: + result.append([data[i+num][tag],abs(temp_rate)]) + else: + result.append([data[i+num][0],data[i+num][tag],abs(temp_rate)]) + x.append(data[i+num][0]) + y1.append(data[i+num][tag]) + y2.append(abs(temp_rate)) + p_fit = Utils.get_quadratic_curve(result) + return result,p_fit,x,y1,y2 + + + # 定义二次函数模型 + def quadratic_func(x, a, b, c): + return a * x**2 + b * x + c + + #二次函数导出公式 + def derivative_func(x, a, b): + return 2* a * x + b + + #比热计算公式Cp2 + def specific_heat_func(E2, K, C,M2,H): + return 3600*(E2+K)/C*M2-H/M2 + + #发热率计算公式 + def heat_generation_rate_func(M2, Cp2, H, D ,K): + return ((M2*Cp2+H)*D/3600-K)/M2 + + + #获取二次曲线p_fit为二次曲线系数 + def get_quadratic_curve(data): + x_data = np.array([i[0] for i in data]) + y_data = np.array([i[1] for i in data]) + degree = 2 # 二次多项式拟合 + p_fit = np.polyfit(x_data, y_data, degree) + y_fit = np.polyval(p_fit, x_data) + + # plt.figure() + # plt.scatter(x_data, y_data, label='Original Data') + # plt.plot(x_data, y_fit, 'r-', label='Fitted Curve') + # plt.xlabel('x') + # plt.ylabel('y') + # plt.title('Polynomial Fitting') + # plt.legend() + # plt.grid() + # plt.show() + return p_fit + + # 热容量计算公式 + def heat_capacity_func(E1, A, B, M1,Cp1): + # print(type(E1), type(A), type(B), type(M1),type(Cp1)) + return (3600*E1)/(A+B)-(M1*Cp1) + + #热损失计算公式 + def heat_loss_func(H, A, M1,Cp1): + return A*(H+M1*Cp1)/3600 + + # 计算杜瓦瓶热容量 + def calculate_heat_capacity(data, data1, E1, M1, Cp1): + capacity = 0 + result, p_fit, x, r1, r2 = Utils.calculate_temp_rate(data, 30, 2) + result1, p_fit1, x, r1, r2 = Utils.calculate_temp_rate(data1, 30, 2) + for i in range(len(result)): + A = abs(Utils.quadratic_func(result[i][0], p_fit[0], p_fit[1], p_fit[2])) + B = abs(Utils.quadratic_func(result[i][0], p_fit1[0], p_fit1[1], p_fit1[2])) + H = abs(Utils.heat_capacity_func(E1, A, B, M1, Cp1)) + capacity += H + + return round(capacity/len(result), 2) + + # 计算热容量及热损失(上升数据与下降数据不等长) + def calculate_heat_capacity_loss(data,data1,E1,M1,Cp1): + capacity=[] + capacity_sum = 0 + loss=[] + temp_data=[]#点位上升速率 + temp_data1=[]#点位下降速率 + result,p_fit,x,r1,r2 = Utils.calculate_temp_rate(data,30,1) + result1,p_fit1,x,r1,r2 = Utils.calculate_temp_rate(data1,30,1) + for i in range(len(result)): + A = abs(Utils.quadratic_func(result[i][0],p_fit[0],p_fit[1],p_fit[2])) + B = abs(Utils.quadratic_func(result[i][0],p_fit1[0],p_fit1[1],p_fit1[2])) + H = abs(Utils.heat_capacity_func(E1, A, B, M1,Cp1)) + capacity.append([result[i][0],H]) + loss.append([result[i][0],Utils.heat_loss_func(H, A, M1,Cp1)]) + for n in settings.TEM: + A = abs(Utils.quadratic_func(n,p_fit[0],p_fit[1],p_fit[2])) + B = abs(Utils.quadratic_func(n,p_fit1[0],p_fit1[1],p_fit1[2])) + temp_data.append(round(A, 4)) + temp_data1.append(round(B, 4)) + H = abs(Utils.heat_capacity_func(E1, A, B, M1, Cp1)) + capacity_sum += H + + capacity_value = round(capacity_sum/len(settings.TEM), 2) + return result,p_fit,result1,p_fit1,capacity,loss,temp_data,temp_data1,capacity_value + + #获取K热损失H热容量2次曲线 + def get_K_func(loss,capacity): + p_fit = Utils.get_quadratic_curve(loss) + p_fit1 = Utils.get_quadratic_curve(capacity) + return p_fit,p_fit1 + + + # 计算比热Cp2与发热率data加热data1自加热 + def calculate_heat_generation_rate(data,data1,data2,E2,M2,loss,capacity): + Cp2_list=[] + Qt_list=[] + r2,p_fit2,x,r1,r2 = Utils.calculate_temp_rate(data1,30,1)#校准上升率曲线 + r3,p_fit3,x,r1,r2 = Utils.calculate_temp_rate(data2,30,1)#试验自加热斜率曲线 + p_fit,p_fit1 =Utils.get_K_func(loss,capacity)#热损失,热容量2次曲线 + # print(p_fit,p_fit1,p_fit2,p_fit3) + result = data2 + for i in range(len(result)): + C =Utils.quadratic_func(result[i][0],p_fit2[0],p_fit2[1],p_fit2[2]) + K =Utils.quadratic_func(result[i][0],p_fit[0],p_fit[1],p_fit[2]) + H =Utils.quadratic_func(result[i][0],p_fit1[0],p_fit1[1],p_fit1[2]) + D =Utils.quadratic_func(result[i][0],p_fit3[0],p_fit3[1],p_fit3[2]) + Cp2 =Utils.specific_heat_func(E2, K, C,M2,H) + Qt =Utils.heat_generation_rate_func(M2, Cp2, H, D ,K) + Cp2_list.append([result[i][0],Cp2]) + Qt_list.append([result[i][1],Qt]) + return Cp2_list,Qt_list + + +def format_status_data(status_data): + result_data = copy.deepcopy(status_data) + # if "g_temp" in result_data: + # result_data["g_temp"] = round(status_data["g_temp"], 2) + # + # if "l_temp" in result_data: + # result_data["l_temp"] = round(status_data["l_temp"], 2) + # + # if "last_g_temp" in result_data: + # result_data["last_g_temp"] = round(status_data["last_g_temp"], 2) + + return result_data + + +def sample_by_interval(arr, interval=30): + if len(arr) <= interval: + return arr + + indices = range(0, len(arr), interval) + sampled_array = [arr[i] for i in indices] + return sampled_array + + +class AlgorithmUtils: + + @staticmethod + def heat_loss_func(H, A, M1,Cp1): + return A*(H+M1*Cp1)/3600 + + @staticmethod + def heat_capacity_func(E1, A, B, M1,Cp1): + return (3600*E1)/(A+B)-(M1*Cp1) + + # 定义二次函数模型 + @staticmethod + def quadratic_func(x, a, b, c): + return a * x**2 + b * x + c + + @staticmethod + def specific_heat_func(E2, K, C,M2,H): + return 3600*(E2+K)/C*M2-H/M2 + + @staticmethod + def heat_generation_rate_func(M2, Cp2, H, D ,K): + return ((M2*Cp2+H)*D/3600-K)/M2 + + @staticmethod + def get_quadratic_curve(data): + x_data = np.array([i[0] for i in data]) + y_data = np.array([i[1] for i in data]) + degree = 2 # 二次多项式拟合 + return np.polyfit(x_data, y_data, degree) + + + def capacity_specify_temperature(self, jz_raw_data, rise_p_fit, drop_p_fit, E1, M1, Cp1): + """计算特定温度下的值""" + capacity, loss, capacity_p_fit, loss_p_fit = self.capacity_loss(jz_raw_data, rise_p_fit, drop_p_fit, E1, M1, Cp1) + + rise_temp_values = [] + drop_temp_values = [] + capacity_values = [] + for t in [40, 60, 80, 100]: + B = self.quadratic_func(t, rise_p_fit[0], rise_p_fit[1], rise_p_fit[2]) # 升温速率 + rise_temp_values.append(round(B, 4)) + + A = self.quadratic_func(t, drop_p_fit[0], drop_p_fit[1], drop_p_fit[2]) # 降温 + drop_temp_values.append(round(A, 4)) + + H = self.heat_capacity_func(E1, A, B, M1, Cp1) + capacity_values.append(H) # 杜瓦瓶热容量 + + capacity_value = sum(capacity_values)/len(capacity_values) + return capacity, loss, rise_temp_values, drop_temp_values, capacity_value + + def calculate_temp_rate(self, data, num=30): + result = [] + + for i in range(0, len(data)-num, num): + time_interval = int(data[i+num][0]) - int(data[i][0]) + if time_interval == 0: + continue + temp_rate = (float(data[i+num][1]) - float(data[i][1])) / time_interval * 3600 + result.append([float(data[i+num][1]), abs(temp_rate)]) + + return result + + def time_temp_rate(self, raw_data, num=60): + r = [] + x = [] + y1 = [] + y2 = [] + for i in range(0, len(raw_data) - num, num): + time_interval = int(raw_data[i + num][0]) - int(raw_data[i][0]) + if time_interval == 0: + continue + temp_rate = (float(raw_data[i + num][1]) - float(raw_data[i][1])) / time_interval * 3600 + + r.append([int(raw_data[i+num][0]), float(raw_data[i+num][1]), abs(temp_rate)]) + x.append(int(raw_data[i + num][0])) + y1.append(float(raw_data[i + num][1])) + y2.append(abs(temp_rate)) + + return r, x, y1, y2 + + def capacity_loss(self, jz_raw_data, rise_p_fit, drop_p_fit, E1, M1, Cp1): + """计算热容量和热损失曲线""" + capacity = [] + loss = [] + raw_data = self.calculate_temp_rate(jz_raw_data, num=60) + for item in raw_data: + B = self.quadratic_func(item[0], rise_p_fit[0], rise_p_fit[1], rise_p_fit[2]) # 升温速率 + A = self.quadratic_func(item[0], drop_p_fit[0], drop_p_fit[1], drop_p_fit[2]) # 降温速率 TODO + H = abs(self.heat_capacity_func(E1, A, B, M1, Cp1)) # 杜瓦瓶热容量 + K = self.heat_loss_func(H, A, M1, Cp1) # 热损失 + capacity.append([item[0], H]) + loss.append([item[0], K]) + + capacity_p_fit = self.get_quadratic_curve(capacity) + loss_p_fit = self.get_quadratic_curve(loss) + return capacity, loss, capacity_p_fit, loss_p_fit + + def calculate_heat_generation_rate(self, zs_data, capacity_p_fit, loss_p_fit, E2, M2): + + # 自加速 + self_heatingTemp_data = zs_data.get("self_heatingTemp") + self_heatingTemp_rate = self.calculate_temp_rate(self_heatingTemp_data, num=2) + self_heatingTemp_p_fit = self.get_quadratic_curve(self_heatingTemp_rate) + # 样品 + riseTemp_data = zs_data.get("riseTemp") + riseTemp_rate = self.calculate_temp_rate(riseTemp_data, num=60) + rise_p_fit = self.get_quadratic_curve(riseTemp_rate) + + Cp2_list = [] + Qt_list = [] + for item in self_heatingTemp_rate: + C = self.quadratic_func(item[0],rise_p_fit[0],rise_p_fit[1],rise_p_fit[2]) + K = self.quadratic_func(item[0],loss_p_fit[0],loss_p_fit[1],loss_p_fit[2]) + H = self.quadratic_func(item[0],capacity_p_fit[0],capacity_p_fit[1],capacity_p_fit[2]) + D = self.quadratic_func(item[0],self_heatingTemp_p_fit[0],self_heatingTemp_p_fit[1],self_heatingTemp_p_fit[2]) + Cp2 = self.specific_heat_func(E2, K, C,M2,H) + Qt = self.heat_generation_rate_func(M2, Cp2, H, D,K) + Cp2_list.append([item[0],Cp2]) + Qt_list.append([item[0],Qt]) + + return Cp2_list, Qt_list + + class ChannelStatusError(Exception): + def __init__(self, message): + self.message = message + super().__init__(self.message) diff --git a/app/lib/VK70xUMCDAQ.py b/app/lib/VK70xUMCDAQ.py new file mode 100644 index 0000000..4fa61c0 --- /dev/null +++ b/app/lib/VK70xUMCDAQ.py @@ -0,0 +1,204 @@ +import os +import time +import json +import ctypes +import threading +from ctypes import * +from datetime import datetime +from app.conf.Setting import settings +from app.lib.Utils import Utils +from app.lib.StaticData import data_lists, StatusDict, Flag +from app.lib.Log import logger + +class VK70xUMCDAQ(): + + def __init__(self): + self.Record ='' # 试验id + self.pressure=None # 压力 + self.temperature=None # 温度 + self.theadPID = None # 采集线程 + self.recordFlag = False # 是否开始记录及开启N采集模式 + self.recordIndex = 0 # 当前记录数量 + self.recordTemp1Data = [] # 温度值,压力值 + # self.recordTemp2Data = [] # 压力值 + self.origin_data = [] + + def save_data(self): + dir_path = os.path.join(settings.BASE_PATH, f"resource/{self.Record}/") + if not os.path.exists(dir_path): + os.mkdir(dir_path) + + with open(os.path.join(dir_path, "temp.json"), 'w', encoding="utf-8") as f: + f.write(str(self.recordTemp1Data)) + + with open(os.path.join(dir_path, "origin_data.json"), "w", encoding="utf-8") as f1: + f1.write(str(self.origin_data)) + + logger.debug("数据保存完成") + + def start(self): + self.theadPID = None + time.sleep(1) + if not self.theadPID: + self.theadPID = threading.Thread(target=self.startThread) + self.theadPID.start() + + def startRecord(self): + self.recordTemp1Data = [] + # self.recordTemp2Data = [] + self.recordIndex = 0 + self.recordFlag = True + + def stopRecord(self): + self.recordFlag = False + + def read(self): + return self.pressure, self.temperature + + def startThread(self): + vk70xnhdll = windll.LoadLibrary('./VK70xUMC_DAQ2.dll') + ret = vk70xnhdll.USBDev_Open()#打开usb端口 + connectedclientnum = c_long(0) + keepLoopRunning = c_long(0) + + if self.recordFlag: + SAMPLINGFREQUENCY =settings.SAMPLINGFREQUENCY + else: + SAMPLINGFREQUENCY =settings.SAMPLINGFREQUENCY1 + + if ret < 0: + logger.error('USB 端口异常或已被占用!') + return + + keepLoopRunning = 1 + while keepLoopRunning: + time.sleep(0.1) + ret = vk70xnhdll.USBDev_Get_ConnectedClientNumbers(byref(connectedclientnum)) # 读取已连接采集卡数量 + logger.debug(f'连接到当前服务器的DAQ设备数: {connectedclientnum.value}') + if connectedclientnum.value > 0: + keepLoopRunning = 0 + + time.sleep(0.1) + if connectedclientnum.value>0: + logger.debug('准备安装DAQ') + keepLoopRunning = 1 + #VK70xUMC_InitializeAll方法参数 + paraInitialize = (ctypes.c_int32 * 20)() + for i in range(20): + paraInitialize[i] = 0 + paraInitialize[0] = 0x22 # 采样命令 + paraInitialize[1] = SAMPLINGFREQUENCY # 采样率 + # paraInitialize[1] = 20000 # 采样率 + paraInitialize[12] = 1 # 通道1压力开启IEPE + paraInitialize[4] = 1 # 通道1压力-5V~5V + paraInitialize[5] = 5 # 通道2温度-100mV~100mV + + ret = vk70xnhdll.VK70xUMC_InitializeAll(0, paraInitialize, 20) #初始化参数 + if ret < 0: + keepLoopRunning = 0 + logger.error(f'参数初始化失败! {ret}') + else: + logger.debug(f'参数初始化成功! {ret}') + + time.sleep(0.1) + ret = vk70xnhdll.VK70xUMC_Set_AdditionalFeature(0, 23, 2, 0) #设置IO3为输入状态 + if ret < 0: + keepLoopRunning = 0 + logger.error(f'IO3输入状态设置失败! {ret}') + else: + logger.debug(f'IO3输入状态设置成功! {ret}') + + time.sleep(0.1) + if self.recordFlag == False: + ret = vk70xnhdll.VK70xUMC_Set_AdditionalFeature(0, 21, 1, 0) #设置IO1为高电平 + if ret < 0: + keepLoopRunning = 0 + logger.error(f'设置IO1为高电平失败! {ret}') + else: + logger.debug(f'设置IO1为高电平成功! {ret}') + + time.sleep(0.1) + ret = vk70xnhdll.VK70xUMC_StartSampling(0)#开始连续采样 + else: + ret = vk70xnhdll.VK70xUMC_Set_AdditionalFeature(0, 21, 0, 0) # 设置IO1为低电平 + if ret < 0: + keepLoopRunning = 0 + logger.error(f'设置IO1为低电平失败! {ret}') + else: + logger.debug(f'设置IO1为低电平成功! {ret}') + + ret = vk70xnhdll.VK70xUMC_StartSampling_NPoints(0,settings.GETPOINTS) # 固定采样总数N + + if ret < 0: + keepLoopRunning = 0 + logger.error(f'采集异常! {ret}') + else: + logger.debug(f'开始采集! {ret}') + + num = SAMPLINGFREQUENCY * 4 + data_buffer = (c_double * num)() + warning = 0 + ending = 0 + while keepLoopRunning and Flag["SHIYAN_Flag"]: + time.sleep(0.1) + collect_num = vk70xnhdll.VK70xUMC_GetFourChannel(0, data_buffer, SAMPLINGFREQUENCY) # 读取采样数据 + datas = [data_buffer[i:i+4] for i in range(0, len(data_buffer), 4)] + + IO4_buffer = (c_int * 1)(3) + ret = vk70xnhdll.VK70xUMC_Get_IO4(0, IO4_buffer) # 读取IO4高低电平 + if ret < 0: + warning += 1 + if warning >= 10: + keepLoopRunning = 0 + logger.error(f'获取IO4电平异常! {ret}') + + logger.debug(f"数据长度: {len(datas)}, 采样点数:{collect_num}, IO4电平: {IO4_buffer[0]}, recordFlag: {self.recordFlag}") + + if IO4_buffer[0] == 0: + if self.recordFlag == False:#低电平,连续采集(切换模式) + logger.debug("==================== 切换采集模式 ================") + keepLoopRunning = 0 + self.startRecord() + self.start() + break + + if collect_num > 0: + for i in range(int(collect_num)): + burnTem = datas[i] + pressure = burnTem[0] # 压力 + temperature = burnTem[1] # 温度 + + # 转换数据 + data, k, b, index, temperature = Utils.binary_search(data_lists, 0, len(data_lists)-1, temperature * 1000) + + self.temperature = temperature + self.pressure = pressure + + if StatusDict['temperature']: + self.temperature = temperature + float(StatusDict['temperature']) + self.pressure = pressure / settings.K * 1000 + + if IO4_buffer[0] == 0 and self.recordFlag == True: + self.recordIndex += 1 + self.origin_data.append(burnTem) + self.recordTemp1Data.append([self.recordIndex, self.temperature, self.pressure]) + + if self.recordFlag == True and collect_num == 0: # N采集,采集数量0(采集结束关闭) + ending += 1 + if ending >= 2: + logger.debug(f'采集数量: {self.recordIndex}') + self.save_data() + keepLoopRunning = 0 + self.stopRecord() + + + time.sleep(0.1) + ret = vk70xnhdll.VK70xUMC_StopSampling(0) # 停止当前采集卡采样 + time.sleep(0.1) + self.theadPID = None + logger.debug('关闭端口!') + + +if __name__ =='__main__': + tempCollect=VK70xUMCDAQ() + tempCollect.start() diff --git a/app/lib/usb.py b/app/lib/usb.py new file mode 100644 index 0000000..b85e384 --- /dev/null +++ b/app/lib/usb.py @@ -0,0 +1,46 @@ +import getpass +import glob +import platform +from io import BytesIO +from os import path + +import psutil + +system_name = platform.system() + + +class DeviceNotFound(Exception): + pass + + +def fetch(): + """获取U盘中的Excel文件""" + if system_name == 'Windows': + usbs = [disk.device for disk in psutil.disk_partitions() if disk.opts == 'rw,removable'] + if not usbs: + raise DeviceNotFound() + usb = usbs[0] + files = glob.glob(f'{usb}/*.xlsx') + glob.glob(f'{usb}/*.xls') + return [{"name": path.basename(f), "file": f} for f in files] + + usbs = [item for item in psutil.disk_partitions() if item.mountpoint.startswith(f'/media/{getpass.getuser()}')] + if not usbs: + raise DeviceNotFound() + usb = usbs[0] + files = glob.glob(f'{usb.mountpoint}/*.xlsx') + glob.glob(f'{usb.mountpoint}/*.xls') + return [{"name": path.basename(f), "file": f} for f in files] + + +def put_in(filename: str, bin: BytesIO): + if system_name == 'Windows': + usbs = [disk.device for disk in psutil.disk_partitions() if disk.opts == 'rw,removable'] + if not usbs: + raise DeviceNotFound() + path_file = path.join(usbs[0], filename) + else: + usbs = [item for item in psutil.disk_partitions() if item.mountpoint.startswith(f'/media/{getpass.getuser()}')] + if not usbs: + raise DeviceNotFound() + path_file = path.join(usbs[0].mountpoint, filename) + with open(path_file, 'wb') as f: + f.write(bin.getbuffer()) diff --git a/app/lib/websocket_manageer.py b/app/lib/websocket_manageer.py new file mode 100644 index 0000000..3d087b1 --- /dev/null +++ b/app/lib/websocket_manageer.py @@ -0,0 +1,20 @@ +from fastapi import WebSocket + + +class ConnectionManager: + def __init__(self): + self.active_connections: list[WebSocket] = [] + + async def connect(self, websocket: WebSocket): + await websocket.accept() + self.active_connections.append(websocket) + + def disconnect(self, websocket: WebSocket): + self.active_connections.remove(websocket) + + async def send_data(self, message: dict, websocket: WebSocket): + await websocket.send_json(message) + + async def broadcast(self, message: str): + for connection in self.active_connections: + await connection.send_text(message) \ No newline at end of file diff --git a/app/models/Base_model.py b/app/models/Base_model.py new file mode 100644 index 0000000..dd18c0c --- /dev/null +++ b/app/models/Base_model.py @@ -0,0 +1,5 @@ +from sqlalchemy.ext.declarative import declarative_base + +Base = declarative_base() + +__all__ = ["Base"] \ No newline at end of file diff --git a/app/models/Business.py b/app/models/Business.py new file mode 100644 index 0000000..9c2b3aa --- /dev/null +++ b/app/models/Business.py @@ -0,0 +1,382 @@ +from app.models.Repository import OrmBase +from app.models.DateEntity import EntityUser, EntityRole, DeviceConfig, ExperimentSpecimen, ExperimentInfo, \ + ExperimentReport, Channel +from app.lib.Utils import * +from sqlalchemy import and_, or_, desc, text + + +# 分页参数模型 +class PageParam(object): + def __init__(self, curPage, pageRows, totalRecords=None, orderType=None, orderField=None): + self.orderType = orderType # 排序类型 asc=正序 desc=倒序 + self.orderField = orderField # 排序字段 + self.pageRows = pageRows # 单页行数 + self.curPage = curPage # 当前页码 + self.totalRecords = totalRecords # 总页数 + + +# 用户逻辑类 +class BllUser(OrmBase): + + def __init__(self, entityType=EntityUser): + super().__init__(entityType) + + # 用户登录 + def login(self, username, password): + return self.findEntity(and_(EntityUser.user_name == username, EntityUser.user_pwd == Utils.MD5(password), EntityUser.is_enabled == 0)) + + def facelogin(self, id): + return self.findEntity(EntityUser.user_id == id) + + # 获取用户列表 + def getUserList(self): + return self.findList(EntityUser.is_enabled == 0) + + # 获取用户详情信息 + def getUserInfo(self, userId): + return self.findEntity(userId) + +# 设备配置类 +class BllDeviceConfig(OrmBase): + + def __init__(self, entityType=DeviceConfig): + super().__init__(entityType) + + # 获取设置详情信息 + def getInfo(self): + return self.findEntity() + + +# 实验信息类 +class BllExperimentInfo(OrmBase): + + def __init__(self, entityType=ExperimentInfo): + super().__init__(entityType) + + # 获取试验列表 + def getExperimentList(self, search_word): + orm_query = self.findList(ExperimentInfo.is_enabled == 0) + if search_word.name: + orm_query = orm_query.filter(ExperimentInfo.specimen_name.like(f"%{search_word.name}%")) + if search_word.date: + orm_query = orm_query.filter(ExperimentInfo.created_time.contains(search_word.date)) + if search_word.experiment_id: + orm_query = orm_query.filter(ExperimentInfo.id == search_word.experiment_id) + # if search_word.state: + # orm_query = orm_query.filter(ExperimentInfo.state == search_word.state) + return orm_query.order_by(desc(ExperimentInfo.created_time)) + +# 实验报表类 +class BllExperimentReport(OrmBase): + + def __init__(self, entityType=ExperimentReport): + super().__init__(entityType) + + +# 通道类 +class BllChannel(OrmBase): + + def __init__(self, entityType=Channel): + super().__init__(entityType) + + +# 材料类 +class BllExperimentSpecimen(OrmBase): + + def __init__(self, entityType=ExperimentSpecimen): + super().__init__(entityType) + + + + + +# 角色逻辑类 +class BllRole(OrmBase): + + def __init__(self, entityType=EntityRole): + super().__init__(entityType) + + # 获取角色列表 + + def getRoleList(self): + return self.findList(EntityRole.is_enabled == 0).all() +# +# +# class BllSpecimen(OrmBase): +# def __init__(self, entityType=EntitySpecimen): +# super().__init__(entityType) +# +# def insertSpecimen(self, **kwargs): +# entity = EntitySpecimen() +# entity.specimen_id = kwargs["specimen_id"] +# entity.user_id = kwargs["user_id"] +# entity.user_name = kwargs["user_name"] +# +# entity.specimen_name = kwargs["specimen_name"] +# entity.specimen_code = kwargs["specimen_code"] +# entity.specimen_type = kwargs["specimen_type"] +# entity.specimen_origin = kwargs["specimen_origin"] +# entity.specimen_desc = kwargs["specimen_desc"] +# entity.storage_location = kwargs["storage_location"] +# +# entity.remark = kwargs["remark"] +# entity.rfid = kwargs["rfid"] +# entity.created_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") +# entity.update_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") +# +# entity.is_enabled = 0 +# self.insert(entity) +# +# def updateSpecimen(self, **kwargs): +# entity = self.findEntity(EntitySpecimen.specimen_id == kwargs["specimen_id"]) +# if "rfid" in kwargs: +# entity.rfid = kwargs.get("rfid") +# entity.rfid_state = 1 if kwargs.get("rfid") else 2 +# if "inventory_status" in kwargs: +# entity.inventory_status = kwargs.get("inventory_status") +# if "disinfection_state" in kwargs: +# entity.disinfection_state = kwargs.get("disinfection_state") +# +# entity.update_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") +# self.update(entity) +# return entity +# +# def get_specimen_list(self, search_word): +# orm_query = self.findList(EntitySpecimen.is_enabled == 0) +# if search_word.specimen_name: +# orm_query = orm_query.filter(EntitySpecimen.specimen_name.like(f"%{search_word.specimen_name}%")) +# if hasattr(search_word, 'specimen_id') and search_word.specimen_id: +# orm_query = orm_query.filter(EntitySpecimen.specimen_id == search_word.specimen_id) +# if hasattr(search_word, 'record_id') and search_word.record_id: +# orm_query = orm_query.filter(EntitySpecimen.specimen_id == search_word.record_id) +# if hasattr(search_word, 'date') and search_word.date: +# orm_query = orm_query.filter(EntitySpecimen.created_time.contains(search_word.date)) +# return orm_query.order_by(desc(EntitySpecimen.update_time)) +# +# def delSpecimen(self, specimen_id): +# entity = self.findEntity(EntitySpecimen.specimen_id == specimen_id) +# entity.is_enabled = 1 +# self.update(entity) +# +# +# class BllWaitDisinfSpecimen(OrmBase): +# def __init__(self, entityType=EntityWaitDisinfSpecimen): +# super().__init__(entityType) +# +# def insert_wait_disinf_specimen(self, **kwargs): +# entity = EntityWaitDisinfSpecimen() +# entity.user_id = kwargs["user_id"] +# entity.user_name = kwargs["user_name"] +# entity.record_id = kwargs["record_id"] +# entity.specimen_id = kwargs["specimen_id"] +# entity.specimen_name = kwargs["specimen_name"] +# entity.specimen_code = kwargs["specimen_code"] +# entity.specimen_desc = kwargs["specimen_desc"] +# entity.specimen_origin = kwargs["specimen_origin"] +# entity.specimen_type = kwargs["specimen_type"] +# entity.storage_location = kwargs["storage_location"] +# entity.remark = kwargs["remark"] +# entity.rfid = kwargs["rfid"] +# entity.disinf_type = kwargs["disinf_type"] +# entity.created_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") +# entity.update_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") +# entity.is_enabled = 0 +# self.insert(entity) +# +# def get_wait_disinf_specimen_list(self, search_word): +# orm_query = self.findList(and_(EntityWaitDisinfSpecimen.is_enabled == 0, +# EntityWaitDisinfSpecimen.done == 0)) +# if hasattr(search_word, 'user_id') and search_word.user_id: +# orm_query = orm_query.filter(EntityWaitDisinfSpecimen.user_id == search_word.user_id) +# return orm_query.order_by(desc(EntityWaitDisinfSpecimen.update_time)) +# +# +# +# class BllExperimentConfig(OrmBase): +# +# def __init__(self, entityType=EntityExperimentConfig): +# super().__init__(entityType) +# +# def insertConfig(self, **kwargs): +# entity = EntityExperimentConfig() +# entity.config_id = kwargs["config_id"] +# entity.user_id = kwargs["user_id"] +# entity.user_name = kwargs["user_name"] +# +# entity.ozone_disinf = kwargs["ozone_disinf"] +# entity.uv_disinf = kwargs["uv_disinf"] +# entity.ozone_conc = kwargs["ozone_conc"] +# entity.disinf_time = kwargs["disinf_time"] +# entity.temperature = kwargs["temperature"] +# entity.humidity = kwargs["humidity"] +# +# entity.config_name = kwargs["config_name"] +# entity.config_desc = kwargs["config_desc"] +# +# entity.created_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") +# entity.update_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") +# self.insert(entity) +# +# def get_config_list(self, search_word): +# orm_query = self.findList(EntityExperimentConfig.is_enabled == 0) +# if hasattr(search_word, 'user_id') and search_word.user_id: +# orm_query = orm_query.filter(EntityExperimentConfig.user_id == search_word.user_id) +# return orm_query.order_by(desc(EntityExperimentConfig.update_time)) +# +# +# class BllExperiment(OrmBase): +# def __init__(self, entityType=EntityExperiment): +# super().__init__(entityType) +# +# def insertExperiment(self, **kwargs): +# specimen_ids = kwargs["specimens"] +# specimens = BllSpecimen().findList(and_(EntitySpecimen.specimen_id.in_(specimen_ids), EntitySpecimen.is_enabled == 0)).all() +# +# entity = EntityExperiment() +# entity.experiment_id = kwargs["experiment_id"] +# entity.name = kwargs["name"] +# entity.user_id = kwargs["user_id"] +# entity.user_name = kwargs["user_name"] +# +# entity.ozone_disinf = kwargs["ozone_disinf"] +# entity.uv_disinf = kwargs["uv_disinf"] +# entity.ozone_conc = kwargs["ozone_conc"] +# entity.disinf_time = kwargs["disinf_time"] +# entity.temperature = kwargs["temperature"] +# entity.humidity = kwargs["humidity"] +# +# entity.created_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") +# entity.update_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") +# entity.is_enabled = 0 +# +# for specimen in specimens: +# from sqlalchemy.orm import object_session +# current_session = object_session(specimen) +# if current_session is not None: +# current_session.expunge(specimen) +# entity.specimens.append(specimen) +# +# self.insert(entity) +# +# def updateExperiment(self, **kwargs): +# entity = self.findEntity(kwargs["experiment_id"]) +# entity.update_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") +# +# self.update(entity) +# return entity +# +# # 获取试验列表 +# def getExperimentList(self, search_word): +# orm_query = self.findList(EntityExperiment.is_enabled == 0) +# if search_word.name: +# orm_query = orm_query.filter(EntityExperiment.name.like(f"%{search_word.name}%")) +# if search_word.date: +# orm_query = orm_query.filter(EntityExperiment.created_time.contains(search_word.date)) +# if search_word.experiment_id: +# orm_query = orm_query.filter(EntityExperiment.experiment_id == search_word.experiment_id) +# return orm_query.order_by(desc(EntityExperiment.update_time)) +# +# # 删除单次试验 +# def delExperiment(self, experiment_id): +# entity = self.findEntity(EntityExperiment.experiment_id == experiment_id) +# entity.is_enabled = 1 +# self.update(entity) +# +# # 查询最后一次试验 +# def getLastExperiment(self): +# orm_query = self.findList(EntityExperiment.is_enabled == 0) +# return orm_query.order_by(desc(EntityExperiment.created_time)).first() +# +# # 试验记录逻辑类 +# +# +# class BllExperimentRecord(OrmBase): +# +# def __init__(self, entityType=EntityExperimentRecord): +# super().__init__(entityType) +# +# # 新增试验记录信息 +# +# def insertExperimentRecord(self, **kwargs): +# entity = EntityExperimentRecord() +# entity.record_id = kwargs["record_id"] +# entity.experiment_id = kwargs["experiment_id"] +# entity.user_id = kwargs["user_id"] +# entity.user_name = kwargs["user_name"] +# +# entity.initial_press = kwargs["initial_press"] +# entity.final_press = kwargs["final_press"] +# entity.leakage_rate = kwargs["leakage_rate"] +# entity.dijici = kwargs["dijici"] +# +# entity.is_enabled = 0 +# entity.created_time = kwargs['created_time'] if "created_time" in kwargs else datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") +# entity.update_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") +# self.insert(entity) +# +# # 更新试验记录信息 +# def updateExperimentRecord(self, **kwargs): +# entity = self.findEntity(EntityExperimentRecord.record_id == kwargs["record_id"]) +# if entity: +# entity.update_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") +# self.update(entity) +# +# # 获取单条试验记录信息 +# def getExperimentRecord_by_record_id(self, record_id): +# data = self.findEntity(EntityExperimentRecord.record_id == record_id) +# return data +# +# # 获取单条试验记录信息 +# def getExperimentRecord_by_experiment_id(self, experiment_id): +# data = self.findEntity(EntityExperimentRecord.experiment_id == experiment_id) +# return data +# +# # 删除单条记录 +# def delExperimentRecord(self, record_id): +# entity = self.findEntity(EntityExperimentRecord.record_id == record_id) +# entity.is_enabled = 1 +# self.update(entity) +# +# +# class BllUselog(OrmBase): +# def __init__(self, entityType=EntityUseLog): +# super().__init__(entityType) +# +# def insert_log(self, **kwargs): +# entity = EntityUseLog() +# entity.user_id = kwargs["user_id"] +# entity.user_name = kwargs["user_name"] +# entity.specimen_id = kwargs["specimen_id"] +# entity.specimen_code = kwargs["specimen_code"] +# entity.specimen_name = kwargs["specimen_name"] +# entity.operation_type = kwargs["operation_type"] +# entity.record_id = Utils.get_uuid() +# entity.created_time = Utils.get_time() +# entity.update_time = Utils.get_time() +# self.insert(entity) +# +# +# class BllTerminal(OrmBase): +# def __init__(self, entityType=EntityTerminal): +# super().__init__(entityType) +# +# def insert_terminal(self, **kwargs): +# entity = EntityTerminal() +# entity.name = kwargs["name"] +# entity.title = kwargs["title"] +# entity.login_count = kwargs["login_count"] +# entity.index_of_user = kwargs["index_of_user"] +# entity.id = Utils.get_uuid() +# entity.created_time = Utils.get_time() +# entity.update_time = Utils.get_time() +# self.insert(entity) +# +# def update_terminal(self, **kwargs): +# entity = self.findEntity(EntityTerminal.id == kwargs["id"]) +# entity.name = kwargs["name"] +# entity.title = kwargs["title"] +# entity.login_count = kwargs["login_count"] +# entity.index_of_user = kwargs["index_of_user"] +# entity.update_time = Utils.get_time() +# self.update(entity) +# diff --git a/app/models/DateEntity.py b/app/models/DateEntity.py new file mode 100644 index 0000000..d04c5cc --- /dev/null +++ b/app/models/DateEntity.py @@ -0,0 +1,304 @@ +import datetime +from app.lib.Utils import Utils +from app.models.Base_model import Base +from sqlalchemy import Column, String, Integer, Float,Table, ForeignKey +from sqlalchemy.orm import relationship, backref + + +# 用户表 +class EntityUser(Base): + __tablename__ = 'user' + + user_id = Column(String(50), primary_key=True, comment="用户id") + user_name = Column(String(50), comment="用户昵称") + user_pwd = Column(String(50), comment="密码") + real_name = Column(String(50), comment="真实姓名") + user_sex = Column(Integer, comment="用户性别") + role_id = Column(String(50), comment="角色id") + user_phone = Column(String(50), comment="手机号") + remark = Column(String(500), comment="备用字段") + created_time = Column(String(50), default=Utils.get_time(), comment="用户创建时间") + update_time = Column(String(50), comment="用户更新时间") + is_enabled = Column(Integer, default=0, comment="是否禁用或删除") + + +# 设备配置表 +class DeviceConfig(Base): + __tablename__ = 'device_config' + + id = Column(String(50), primary_key=True, comment="id") + box_temperature = Column(Float, comment="设备温度") + surface_temperature = Column(Float, comment="表面温度") + door_status = Column(Integer, comment="门状态") + light_status = Column(Integer, comment="灯光状态") + fan_status = Column(Integer, comment="风扇状态") + warning_light_status = Column(Integer, comment="报警灯状态") + lock_status = Column(Integer, comment="锁状态") + + +# 材料表 +class ExperimentSpecimen(Base): + __tablename__ = "experiment_specimen" + + id = Column(String(50), comment="id", primary_key=True) + specimen_name = Column(String(50), comment="样品名称") + battery_type = Column(String(50), comment="电池型号") + specimen_num = Column(String(50), comment="样品编号") + battery_specification = Column(String(255), comment="电池规格") + battery_manufacturers = Column(String(500), comment="电池厂家") + remark = Column(String(500), comment="备注") + + created_time = Column(String(50), default=Utils.get_time(), comment="创建时间") + update_time = Column(String(50), comment="更新时间") + user_id = Column(String(50), comment="创建人") + user_name = Column(String(50), comment="用户昵称") + + is_enabled = Column(Integer, default=0, comment="是否禁用或删除") + md5 = Column(String(256), comment="md5值 样品名称+电池型号+样品编号+电池规格+电池厂家+备注") + + +# 实验信息表 +class ExperimentInfo(Base): + __tablename__ = "experiment_info" + + id = Column(String(50), comment="id", primary_key=True) + specimen_name = Column(String(50), comment="样品名称") + battery_type = Column(String(50), comment="电池型号") + specimen_num = Column(String(50), comment="样品编号") + battery_specification = Column(String(255), comment="电池规格") + battery_manufacturers = Column(String(500), comment="电池厂家") + remark = Column(String(500), comment="备注") + temperature = Column(Float, comment="设置温度") + expose_time = Column(Float, comment="暴露时间") + put_time = Column(Float, comment="放置时间") + short_circuit_param = Column(Float, comment="短路参数") + desc = Column(String(500), comment="描述") + + created_time = Column(String(50), default=Utils.get_time(), comment="创建时间") + update_time = Column(String(50), comment="更新时间") + end_time = Column(String(50), comment="结束时间") + user_id = Column(String(50), comment="创建人") + user_name = Column(String(50), comment="用户昵称") + + is_enabled = Column(Integer, default=0, comment="是否禁用或删除") + state = Column(Integer, default=0, comment="状态 0:未开始 1:进行中 2:已完成 3:已取消") + + specimen_id = Column(String(50), comment="材料表id") + channel_id = Column(String(256), comment="通道表id") + + +# 实验记录报表 +class ExperimentReport(Base): + __tablename__ = "experiment_report" + + id = Column(String(50), comment="id", primary_key=True) + experiment_id = Column(String(50), comment="实验表id") + channel_id = Column(String(50), comment="通道表id") + time = Column(Integer, default=0, comment="进行时间") + experiment_temperature = Column(Float, comment="实验温度") + surface_temperature = Column(Float, comment="表面温度") + battery_voltage = Column(Float, comment="电池电压") + battery_electricity = Column(Float, comment="电池电流") + recharge_capacity = Column(Float, comment="充电容量") + discharging_capacity = Column(Float, comment="放电容量") + internal_resistance = Column(Float, comment="内阻") + + + +# 通道表 +class Channel(Base): + __tablename__ = "channel" + + id = Column(String(50), comment="id", primary_key=True) + name = Column(String(50), comment="通道名称") + status = Column(Integer, comment="状态 0:未使用 1:使用中") + last_start_time = Column(String(50), comment="最后开始时间") + + + + + + # 角色表 +class EntityRole(Base): + __tablename__ = 'role' + + role_id = Column(String(50), primary_key=True, comment="角色id") + role_name = Column(String(50), comment="角色名称") + role_code = Column(String(50), comment="角色代号") + role_level = Column(Integer, comment="角色等级") + remark = Column(String(500), comment="备用字段") + created_time = Column(String(50), default=Utils.get_time(), comment="角色创建时间") + update_time = Column(String(50), comment="角色更新时间") + is_enabled = Column(Integer, default=0, comment="是否禁用或删除") + + +specimen_experiment = Table('specimen_experiment', Base.metadata, + Column('specimen_id', Integer, ForeignKey('specimen.specimen_id')), + Column('experiment_id', Integer, ForeignKey('experiment.experiment_id')) + ) + + +# # 证物表 +# class EntitySpecimen(Base): +# __tablename__ = "specimen" +# +# specimen_id = Column(String(50), comment="物品id", primary_key=True) +# specimen_name = Column(String(50), comment="物品名称") +# specimen_code = Column(String(50), comment="物品编号") +# specimen_type = Column(String(50), comment="物品类型") +# specimen_origin = Column(String(255), comment="物品来源") +# specimen_desc = Column(String(500), comment="物品描述") +# storage_location = Column(String(255), comment="存放位置") +# remark = Column(String(500), comment="备注") +# rfid = Column(String(50), comment="条码") +# +# inventory_status = Column(Integer, comment="库存状态") # 1,在库,2,出库 +# rfid_state = Column(Integer, comment="条码状态") # 1,已绑定;2,未绑定 +# ''' +# 0:未消毒:没有消毒过; +# 1:三天内已消毒:在3天之内有消毒记录; +# 2:一周内已消毒:在7天之内有消毒记录; +# 3:一月内已消毒:在30天之内有消毒记录; +# 4:三月内已消毒:在90天之内有消毒记录; +# 5:半年内已消毒:在180天之内有消毒记录; +# 6:一年内已消毒:在365天之内有消毒记录; +# 7:超过一年未消毒:最近一次消毒已超过365天 +# ''' +# disinfection_state = Column(Integer, comment="消毒状态") +# +# created_time = Column(String(50), default=Utils.get_time(), comment="创建时间") +# update_time = Column(String(50), comment="更新时间") +# user_id = Column(String(50), comment="创建人") +# user_name = Column(String(50), comment="用户昵称") +# +# is_enabled = Column(Integer, default=0, comment="是否禁用或删除") +# +# last_disinf_time = Column(String(50), comment="最新一次消毒时间") +# +# experiments = relationship('EntityExperiment', secondary=specimen_experiment, back_populates='specimens') +# +# +# def to_dict(self): +# return dict(id=self.specimen_id, specimen_name = self.specimen_name, specimen_code=self.specimen_code, +# specimen_type=self.specimen_type, specimen_origin=self.specimen_origin, +# specimen_desc=self.specimen_desc, storage_location=self.storage_location, +# remark=self.remark, rfid=self.rfid, inventory_status=self.inventory_status, +# rfid_state=self.rfid_state, disinfection_state=self.disinfection_state, +# last_disinf_time = self.last_disinf_time, +# created_time=self.created_time, user_name=self.user_name) +# +# class EntityUseLog(Base): +# __tablename__ = "use_log" +# record_id = Column(String(50), comment="记录id",primary_key=True) +# specimen_id = Column(String(50), comment="物品id") +# specimen_name = Column(String(50), comment="物品名称") +# specimen_code = Column(String(50), comment="物品编号") +# operation_type = Column(String(50), comment="操作类型") +# created_time = Column(String(50), default=Utils.get_time(), comment="创建时间") +# update_time = Column(String(50), comment="更新时间") +# user_id = Column(String(50), comment="创建人") +# user_name = Column(String(50), comment="用户昵称") +# +# +# class EntityTerminal(Base): +# __tablename__ = "terminals" +# +# id = Column(String(50), comment="id",primary_key=True) +# name = Column(String(50), comment="名称") +# title = Column(String(50), comment="标题") +# login_count = Column(Integer, comment="核验账号数") +# index_of_user = Column(Integer, comment="以第几人为使用者") +# is_enabled = Column(Integer, default=0, comment="是否禁用或删除") +# +# created_time = Column(String(50), default=Utils.get_time(), comment="创建时间") +# update_time = Column(String(50), comment="更新时间") +# +# +# +# class EntityWaitDisinfSpecimen(Base): +# __tablename__ = "wait_disinf_specimen" +# ''' +# 待消毒物品记录 因一个证物可以在同一时间段被消毒多次 +# ''' +# +# record_id = Column(String(50), comment="记录id",primary_key=True) +# specimen_id = Column(String(50), comment="物品id") +# +# specimen_name = Column(String(50), comment="物品名称") +# specimen_code = Column(String(50), comment="物品编号") +# specimen_type = Column(String(50), comment="物品类型") +# specimen_origin = Column(String(255), comment="物品来源") +# specimen_desc = Column(String(500), comment="物品描述") +# storage_location = Column(String(255), comment="存放位置") +# remark = Column(String(500), comment="备注") +# rfid = Column(String(50), comment="条码") +# +# created_time = Column(String(50), default=Utils.get_time(), comment="创建时间") +# update_time = Column(String(50), comment="更新时间") +# user_id = Column(String(50), comment="创建人") +# user_name = Column(String(50), comment="用户昵称") +# is_enabled = Column(Integer, default=0, comment="是否禁用或删除") +# done = Column(Integer, default=0, comment="是否完成") # 0 未完成,1 已完成 +# disinf_type = Column(Integer, comment="消毒类型") # 1 入库消毒,2 出库消毒,3 其他 +# +# +# # 试验配置表 每个员工可以自定多个配置,以方便后续消毒操作 +# class EntityExperimentConfig(Base): +# __tablename__ = "experiment_config" +# +# config_id = Column(String(), comment="id",primary_key=True) +# ozone_disinf = Column(Integer, comment="臭氧消毒") +# uv_disinf = Column(Integer, comment="紫外线消毒") +# ozone_conc = Column(String(50), comment="臭氧浓度") +# disinf_time = Column(String(50), comment="消毒时间") +# temperature = Column(String(50), comment="温度") +# humidity = Column(String(50), comment="湿度") +# created_time = Column(String(50), default=Utils.get_time(), comment="创建时间") +# update_time = Column(String(50), comment="更新时间") +# user_id = Column(String(50), comment="创建人") +# user_name = Column(String(50), comment="用户昵称") +# +# config_name = Column(String(50), comment="配置名称") +# config_desc = Column(String(500), comment="配置描述") +# is_enabled = Column(Integer, default=0, comment="是否禁用或删除") +# +# +# # 试验表 +# class EntityExperiment(Base): +# __tablename__ = "experiment" +# +# experiment_id = Column(String(50), comment="消毒流程id", primary_key=True) +# name = Column(String(50), comment="名称") +# +# ozone_disinf = Column(Integer, comment="臭氧消毒") +# uv_disinf = Column(Integer, comment="紫外线消毒") +# ozone_conc = Column(String(50), comment="臭氧浓度") +# disinf_time = Column(String(50), comment="消毒时间") +# temperature = Column(String(50), comment="浓度") +# humidity = Column(String(50), comment="湿度") +# +# remark = Column(String(500), comment="备注信息") +# created_time = Column(String(50), default=Utils.get_time(), comment="创建时间") +# update_time = Column(String(50), comment="更新时间") +# +# user_id = Column(String(50), comment="创建人") +# user_name = Column(String(50), comment="用户昵称") +# is_enabled = Column(Integer, default=0, comment="是否禁用或删除") +# state = Column(Integer, default=0, comment="状态") +# +# specimens = relationship('EntitySpecimen', secondary=specimen_experiment, back_populates='experiments') +# +# def to_dict(self): +# return dict(id=self.experiment_id, ozone_disinf=self.ozone_disinf, uv_disinf=self.uv_disinf, +# ozone_conc=self.ozone_conc, disinf_time=self.disinf_time, temperature=self.temperature, +# humidity=self.humidity, remark=self.remark, created_time=self.created_time, user_name=self.user_name) +# +# +# # 试验记录信息表 +# class EntityExperimentRecord(Base): +# __tablename__ = "experiment_record" +# +# record_id = Column(String(50), comment="记录id", primary_key=True) +# update_time = Column(String(50), comment="试验记录更新时间") +# is_enabled = Column(Integer, default=0, comment="是否禁用或删除") + diff --git a/app/models/Repository.py b/app/models/Repository.py new file mode 100644 index 0000000..6fd8848 --- /dev/null +++ b/app/models/Repository.py @@ -0,0 +1,216 @@ +import traceback +import paginate_sqlalchemy +from sqlalchemy import create_engine, desc +from app.conf.Setting import settings +from sqlalchemy.orm import sessionmaker +from sqlalchemy.sql import func +from sqlalchemy.sql.elements import BinaryExpression, BooleanClauseList +from app.lib.Log import logger + +class OrmBase(object): + # 初始化数据库连接 + engine = create_engine(settings.SQLALCHEMY_DATABASE_URI,connect_args={'check_same_thread': False}, pool_size=100) + # 创建session工厂 + DBSession = sessionmaker(bind=engine, autoflush=False, autocommit=False, expire_on_commit=True) + # 创建session对象 + session = DBSession() + # 事务标识 + transFlag = False + + def __init__(self, entityType): + super().__init__() + # 对象类型 + self.entityType = entityType + + # 开始事务 + def beginTrans(self): + self.session = self.DBSession() + self.transFlag = True + + # 提交事务 + def commitTrans(self): + try: + self.transFlag = False + self.session.commit() + except Exception as e: + logger.error(traceback.format_exc()) + self.session.rollback() + raise e + finally: + self.session.close() + + # 新增数据 + def insert(self, entity): + try: + if (not self.transFlag): + self.session = self.DBSession() + self.session.add(entity) + if (not self.transFlag): + self.session.commit() + return True + except Exception as e: + logger.error(traceback.format_exc()) + self.session.rollback() + raise e + finally: + self.session.close() + + # 新增多行数据 + def insert_many(self, entity_list): + try: + if (not self.transFlag): + self.session = self.DBSession() + self.session.bulk_save_objects(entity_list) + if (not self.transFlag): + self.session.commit() + return True + except Exception as e: + logger.error(traceback.format_exc()) + self.session.rollback() + raise e + finally: + self.session.close() + + # 更新数据 + def update(self, entity): + try: + if (not self.transFlag): + self.session = self.DBSession() + self.session.merge(entity) + if (not self.transFlag): + self.session.commit() + except Exception as e: + logger.error(traceback.format_exc()) + self.session.rollback() + raise e + finally: + self.session.close() + + # 删除操作 + def delete(self, where): + try: + if (not self.transFlag): + self.session = self.DBSession() + self.session.query(self.entityType).filter(where).delete() + if (not self.transFlag): + self.session.commit() + except Exception as e: + logger.error(traceback.format_exc()) + self.session.rollback() + raise e + finally: + self.session.close() + + # 查询单个实体 + def findEntity(self, *where): + try: + self.session = self.DBSession() + if (type(*where) is BinaryExpression or type(*where) is BooleanClauseList): + return self.session.query(self.entityType).filter(*where).first() + else: + return self.session.query(self.entityType).get(where) + except Exception as e: + logger.error(traceback.format_exc()) + self.session.rollback() + raise e + finally: + self.session.close() + + def findLastEntity(self, order_by_field, *where): + try: + self.session = self.DBSession() + if (type(*where) is BinaryExpression or type(*where) is BooleanClauseList): + return self.session.query(self.entityType).filter(*where).order_by(desc(order_by_field)).first() + else: + return self.session.query(self.entityType).order_by(desc(order_by_field)).get(where) + except Exception as e: + logger.error(traceback.format_exc()) + self.session.rollback() + raise e + finally: + self.session.close() + + # 查询实体列表 + def findList(self, *where): + try: + self.session = self.DBSession() + return self.session.query(self.entityType).filter(*where) + except Exception as e: + logger.error(traceback.format_exc()) + self.session.rollback() + raise e + finally: + self.session.close() + + # 查询所有实体列表 + def findAllList(self): + try: + self.session = self.DBSession() + return self.session.query(self.entityType).all() + except Exception as e: + logger.error(traceback.format_exc()) + self.session.rollback() + raise e + finally: + self.session.close() + + # 查询分页 + def queryPage(self, orm_query, pageParam): + try: + page = paginate_sqlalchemy.SqlalchemyOrmPage( + orm_query, page=pageParam.curPage, items_per_page=pageParam.pageRows, db_session=self.DBSession) + pageParam.totalRecords = page.item_count + return page.items + except Exception as e: + logger.error(traceback.format_exc()) + raise e + + # 查询数量 + def findCount(self, *where): + try: + self.session = self.DBSession() + return self.session.query(func.count('*')).select_from(self.entityType).filter(*where).scalar() + except Exception as e: + logger.error(traceback.format_exc()) + self.session.rollback() + raise e + finally: + self.session.close() + + # 查询最大数 + def findMax(self, prop, *where): + try: + self.session = self.DBSession() + return self.session.query(func.max(prop)).select_from(self.entityType).filter(*where).scalar() + except Exception as e: + logger.error(traceback.format_exc()) + self.session.rollback() + raise e + finally: + self.session.close() + + # 执行Sql语句 + def execute(self, sql, *agrs): + try: + self.session = self.DBSession() + return self.session.execute(sql, *agrs) + except Exception as e: + logger.error(traceback.format_exc()) + self.session.rollback() + raise e + finally: + self.session.close() + + # 执行Sql语句 + def executeNoParam(self, sql): + try: + self.session = self.DBSession() + self.session.execute(sql) + if (not self.transFlag): + self.session.commit() + except Exception as e: + logger.error(traceback.format_exc()) + self.session.rollback() + raise e + finally: + self.session.close() diff --git a/app/models/__init__.py b/app/models/__init__.py new file mode 100644 index 0000000..64e6acd --- /dev/null +++ b/app/models/__init__.py @@ -0,0 +1 @@ +from .DateEntity import EntityUser,EntityRole diff --git a/app/utils/__init__.py b/app/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/utils/excel_util.py b/app/utils/excel_util.py new file mode 100644 index 0000000..6b3122f --- /dev/null +++ b/app/utils/excel_util.py @@ -0,0 +1,409 @@ +import urllib.parse +import psutil +import os +import time +import random +import getpass +import platform +from io import BytesIO +from os import path +from app.conf.Setting import settings +import openpyxl +from openpyxl import Workbook +from openpyxl.styles import Alignment +from openpyxl.drawing.image import Image +from openpyxl.styles import Font +from datetime import datetime +import matplotlib.pyplot as plt +from app.lib.Utils import Utils +from openpyxl.styles import PatternFill, Border, Side + +system_name = platform.system() + + +class ExcelExport(): + def __init__(self, file_name, data, download_type): + self.file_name = file_name + self.data = data + self.download_type = download_type + self.book = Workbook() + self.side = Side(style='thin', color='000000') # 颜色可以是RGB值或者颜色名称 + self.border = Border(left=self.side, right=self.side, top=self.side, bottom=self.side) + + def set_title(self, sheet): + """设置标题""" + for i in range(1, 13): + sheet.column_dimensions[openpyxl.utils.get_column_letter(i)].width = 15 + sheet.merge_cells(f'A1:L1') + # sheet.merge_cells(f'A1:{chr(64 + self.culumn)}1') + cell = sheet.cell(row=1, column=1) + cell.value = self.file_name + cell.alignment = Alignment(horizontal='center', vertical='center') + sheet.row_dimensions[1].height = 40 + # 设置背景色 + fill = PatternFill(start_color="D9E1F4", end_color="D9E1F4", fill_type="solid") + sheet['A1'].fill = fill + # sheet['A1'].border = self.border + + def set_column_title(self, sheet, experiment_type): + sheet.merge_cells(f'A2:A8') + cell = sheet.cell(row=2, column=1) + cell.value = '基本信息' + cell.alignment = Alignment(horizontal='center', vertical='center') + if experiment_type == '1': + sheet.merge_cells(f'A9:A12') + else: + sheet.merge_cells(f'A9:A13') + cell = sheet.cell(row=9, column=1) + cell.value = '报告信息' + cell.alignment = Alignment(horizontal='center', vertical='center') + fill = PatternFill(start_color="D9D9D9", end_color="D9D9D9", fill_type="solid") + sheet['A2'].fill = fill + sheet['A9'].fill = fill + # sheet['A2'].border = self.border + # sheet['A9'].border = self.border + + def set_columns(self, sheet, title_list): + """设置列名""" + for i in range(len(title_list)): + sheet.merge_cells(f'B{i + 2}:E{i + 2}') + sheet.merge_cells(f'F{i + 2}:I{i + 2}') + cell = sheet.cell(row=i + 2, column=2) + cell.value = title_list[i] + cell.alignment = Alignment(horizontal='center', vertical='center') + + def set_temp(self, sheet, data_item): + """设置40,60,80,100""" + temp = ['校准物质40℃升温速率', '校准物质60℃升温速率', '校准物质80℃升温速率', '校准物质100℃升温速率'] + temp1 = ['校准物质40℃降温速率', '校准物质60℃降温速率', '校准物质80℃降温速率', '校准物质100℃降温速率'] + for i in range(len(temp)): + sheet.merge_cells(f'B{i + 9}:C{i + 9}') + sheet.merge_cells(f'D{i + 9}:E{i + 9}') + sheet.merge_cells(f'F{i + 9}:G{i + 9}') + sheet.merge_cells(f'H{i + 9}:I{i + 9}') + cell = sheet.cell(row=i + 9, column=2) + cell.value = temp[i] + cell.alignment = Alignment(horizontal='center', vertical='center') + cell = sheet.cell(row=i + 9, column=4) + cell.value = data_item["temp_data"][i] + cell.alignment = Alignment(horizontal='center', vertical='center') + cell1 = sheet.cell(row=i + 9, column=6) + cell1.value = temp1[i] + cell1.alignment = Alignment(horizontal='center', vertical='center') + cell = sheet.cell(row=i + 9, column=8) + cell.value = data_item["temp_data1"][i] + cell.alignment = Alignment(horizontal='center', vertical='center') + + def insert_data(self, sheet, data_item, key_list): + """插入数据""" + + for i in range(len(key_list)): + cell = sheet.cell(row=i + 2, column=6) + cell.value = data_item[key_list[i]] + cell.alignment = Alignment(horizontal='center', vertical='center') + + # def insert_chart(self, sheet,chart_result,ro): + # """插入图表""" + # + # + # plt.rcParams['xtick.direction'] = 'in' # 将x周的刻度线方向设置向内 + # plt.rcParams['ytick.direction'] = 'in' # 将y轴的刻度方向设置向内 + # ax1_color = '#1f77b4' + # ax2_color = '#d62728' + # ax3_color = '#573627' + # + # data = chart_result + # + # x_data = [int(float(i[-1])) for i in data] # 时间 + # y_data = [round(float(i[2]), 2) for i in data] # 压力 + # y_data2 = [round(float(i[3]), 2) for i in data] # 温度 + # # y_data3 = [round(float(i[-5] or 0) + 250, 2) for i in data] # 热表面温度 + 250 + # # 创建主轴的图表 + # fig, ax1 = plt.subplots() + # ax1.plot(x_data, y_data, 'b-', label='["压力","时间"]', linewidth=1, color=ax1_color) + # ax2 = ax1.twinx() + # ax1.plot(x_data, y_data2, 'b-', label='["温度","时间"]', linewidth=1, color=ax2_color) + # # ax1.plot(x_data, y_data3, 'b-', label='["高热表面温度250","时间"]', linewidth=1, color=ax3_color) + # plt.title('试验数据') + # + # ax1.set_xlabel('时间') + # ax1.set_ylabel('压力') + # ax1.legend(loc='upper left') + # ax2.legend(loc='upper right') + # + # + # # 图表标题 + # # 设置中文字体 + # plt.rcParams['font.sans-serif'] = ['SimHei'] + # # 设置负号字体 + # plt.rcParams['axes.unicode_minus'] = False + # # 保存 Matplotlib 图表为图像文件 + # chart_file_path = path.join(settings.BASE_PATH, f'resource/{int(time.time() * 1000)}.png') + # print(chart_file_path) + # plt.savefig(chart_file_path, dpi=400) + # # 将图像插入到 Excel 工作表中 + # img = Image(chart_file_path) + # img.width = 960 + # img.height = 400 + # sheet.add_image(img, 'B' + str(ro+2)) + + def insert_chart(self, sheet,chart_result,ro,type): + """插入图表""" + + + plt.rcParams['xtick.direction'] = 'in' # 将x周的刻度线方向设置向内 + plt.rcParams['ytick.direction'] = 'in' # 将y轴的刻度方向设置向内 + # ax1_color = '#1f77b4' + # ax2_color = '#d62728' + # ax3_color = '#573627' + + data = chart_result + + x_data = [int(float(i.time)) for i in data] # 时间 + if type == 1: + y_data = [round(float(i.surface_temperature), 2) for i in data] # 温度1 表面温度 + ax_color = '#1f77b4' + label='["表面温度","时间"]' + ylabel = '表面温度' + elif type == 2: + y_data = [round(float(i.battery_voltage), 2) for i in data] # 温度2 电池电压 + ax_color = '#d62728' + label='["电池电压","时间"]' + ylabel = '电池电压' + else: + y_data = [round(float(i.battery_electricity), 2) for i in data] # 温度3 电池电流 + ax_color = '#573627' + label='["电池电流","时间"]' + ylabel = '电池电流' + # y_data = [round(float(i.surface_temperature), 2) for i in data] # 温度1 表面温度 + # y_data2 = [round(float(i.battery_voltage), 2) for i in data] # 温度2 电池电压 + # y_data3 = [round(float(i.battery_electricity), 2) for i in data] # 温度3 电池电流 + # 创建主轴的图表 + fig, ax1 = plt.subplots() + ax1.plot(x_data, y_data, 'b-', label=label, linewidth=1, color=ax_color) + # ax1.plot(x_data, y_data2, 'b-', label='["电池电压","时间"]', linewidth=1, color=ax2_color) + # ax1.plot(x_data, y_data3, 'b-', label='["电池电流","时间"]', linewidth=1, color=ax3_color) + plt.title('试验数据') + + ax1.set_xlabel('时间') + ax1.set_ylabel(ylabel) + ax1.legend(loc='upper left') + + + # 图表标题 + # 设置中文字体 + plt.rcParams['font.sans-serif'] = ['SimHei'] + # 设置负号字体 + plt.rcParams['axes.unicode_minus'] = False + # 保存 Matplotlib 图表为图像文件 + chart_file_path = path.join(settings.BASE_PATH, f'resource/{int(time.time() * 1000)}.png') + print(chart_file_path) + plt.savefig(chart_file_path, dpi=400) + # 将图像插入到 Excel 工作表中 + img = Image(chart_file_path) + img.width = 960 + img.height = 400 + sheet.add_image(img, 'E' + str(ro+2)) + + ro+=20 + + # def insert_chart(self, sheet,chart_result,ro): + # + # + # + # + # # 设置全局字体和刻度线方向 + # plt.rcParams['xtick.direction'] = 'in' # x轴刻度线方向 + # plt.rcParams['ytick.direction'] = 'in' # y轴刻度线方向 + # plt.rcParams['font.sans-serif'] = ['SimHei'] # 中文字体 + # plt.rcParams['axes.unicode_minus'] = False # 负号显示 + # + # # 假设这是你获取的实验数据 + # data = chart_result + # x_data = [int(float(i.time)) for i in data] # 时间 + # y_data = [round(float(i.surface_temperature), 2) for i in data] # 表面温度 + # y_data2 = [round(float(i.battery_voltage), 2) for i in data] # 电池电压 + # y_data3 = [round(float(i.battery_electricity), 2) for i in data] # 电池电流 + # + # # 创建图表并设置主轴 + # fig, ax1 = plt.subplots() + # + # # 绘制第一个数据系列:表面温度 + # ax1.set_xlabel('时间') + # ax1.set_ylabel('表面温度 (°C)', color='#1f77b4') + # ax1.plot(x_data, y_data, label='表面温度', color='#1f77b4', linewidth=1) + # ax1.tick_params(axis='y', labelcolor='#1f77b4') + # + # # 设置第一个 y 轴的范围(根据数据调整) + # ax1.set_ylim([min(y_data) - 1, max(y_data) + 1]) # 适当调整范围,避免线条挤压 + # + # # 创建第二个 y 轴:电池电压 + # ax2 = ax1.twinx() # 创建第二个 y 轴 + # ax2.set_ylabel('电池电压 (V)', color='#d62728') + # ax2.plot(x_data, y_data2, label='电池电压', color='#d62728', linewidth=1) + # ax2.tick_params(axis='y', labelcolor='#d62728') + # + # # 设置第二个 y 轴的范围(根据数据调整) + # ax2.set_ylim([min(y_data2) - 0.5, max(y_data2) + 0.5]) # 适当调整范围 + # + # # 将第二个 y 轴移到左侧 + # ax2.spines['left'].set_position(('outward', 60)) # 将第二个 y 轴移到外面 + # + # # 创建第三个 y 轴:电池电流 + # ax3 = ax1.twinx() # 创建第三个 y 轴 + # ax3.spines['left'].set_position(('outward', 320)) # 将第三个 y 轴移到外面 + # ax3.set_ylabel('电池电流 (A)', color='#573627') + # ax3.plot(x_data, y_data3, label='电池电流', color='#573627', linewidth=1) + # ax3.tick_params(axis='y', labelcolor='#573627') + # + # # 设置第三个 y 轴的范围(根据数据调整) + # ax3.set_ylim([min(y_data3) - 0.1, max(y_data3) + 0.1]) # 适当调整范围 + # + # # 图表标题 + # plt.title('试验数据') + # + # # 图例 + # fig.tight_layout() # 自动调整子图间距 + # ax1.legend(loc='upper left') + # + # # 保存图表为图片 + # chart_file_path = path.join(settings.BASE_PATH, f'resource/{int(time.time() * 1000)}.png') + # print(chart_file_path) + # plt.savefig(chart_file_path, dpi=400) + # + # # 将图像插入到 Excel 工作表中 + # img = Image(chart_file_path) + # img.width = 960 + # img.height = 400 + # sheet.add_image(img, 'D' + str(ro + 2)) + + + + def experiment_table(self): + """试验报表""" + for item in self.data: + base_data_title = item['base_data_title'] + base_data_list = item['base_data_list'] + sheet_title = item['sheet_title'] + title_list = item['title_list'] + specimen_data = item['specimen_data'] + sheet = self.book.create_sheet(str(sheet_title)) # 创建工作表用试验时间作工作表名称 + + self.set_title(sheet) # 设置标题 + for i in range(len(title_list)): + sheet.merge_cells(f'A{i + 1}:L{i + 1}') + cell = sheet.cell(row=i+1, column=1) + cell.value = title_list[i] + cell.alignment = Alignment(horizontal='center', vertical='center') + if i == 0: + cell.font = Font(size=22, bold=True) + ro = 2 + + for key, value in specimen_data.items(): + cell = sheet.cell(row=ro, column=1) + cell.value = key + # sheet.merge_cells(f'B{ro + 1}:E{ro + 1}') + cell_b = sheet.cell(row=ro, column=2) + cell_b.value = value + ro += 1 + ro +=1 + for index,value in enumerate(base_data_title): + if value: + cell = sheet.cell(row=ro, column=index+1) + cell.value = value + cell.alignment = Alignment(horizontal='center', vertical='center') + ro += 1 + for i in base_data_list: + for index, value in enumerate(base_data_title): + cell = sheet.cell(row=ro, column=index + 1) + cell.value = i[base_data_title[index]] + cell.alignment = Alignment(horizontal='center', vertical='center') + ro += 1 + + # 在数据行下方增加一行,并填充新的列标题('实验时间', '表面温度', '电池电压', '电池电流') + new_row_title = ['实验时间', '表面温度', '电池电压', '电池电流'] + + # 填充新的一行数据标题,紧接数据区域下方 + for index, value in enumerate(new_row_title): + cell = sheet.cell(row=ro+4, column=index + 1) + cell.value = value + cell.alignment = Alignment(horizontal='center', vertical='center') # 可选,居中对齐标题 + + fill_ro = ro+5 + # 填充额外数据('实验时间', '表面温度', '电池电压', '电池电流') + for i in base_data_list[0]['other_data']: + cell_css= Alignment(horizontal='center', vertical='center') # 可选,居中对齐数据 + time_cell = sheet.cell(row=fill_ro, column= 1) + time_cell.value = i.time # 时间 + time_cell.alignment = cell_css + hxwd_cell = sheet.cell(row=fill_ro, column= 2) + hxwd_cell.value = i.surface_temperature # 表面温度 + hxwd_cell.alignment = cell_css + ypwd_cell = sheet.cell(row=fill_ro, column= 3) + ypwd_cell.value = i.battery_voltage # 电池电压 + ypwd_cell.alignment = cell_css + dcdl_cell = sheet.cell(row=fill_ro, column= 4) + dcdl_cell.value = i.battery_electricity # 电池电流 + dcdl_cell.alignment = cell_css + fill_ro += 1 # 每填充一行数据后,增加行号 + + for i in base_data_list: + result_row_data = i['other_data'] + if result_row_data: + char_data =[] + for row in result_row_data: + # if row[0] in [4,'4']: + char_data.append(row) + # 循环三次、表面温度、电池电压、电池电流 + type = 1 + for i in range(3): + self.insert_chart(sheet, char_data, ro,type) + type += 1 + ro += 20 + + + # 获取所有工作表的列表 + sheets = self.book.sheetnames + # 删除第一个工作表 + first_sheet_name = sheets[0] + self.book.remove(self.book[first_sheet_name]) + + # 导出 + return self.export() + + def export(self): + """ + 导出 + """ + binary = BytesIO() + self.book.save(binary) + self.book.close() + binary.seek(0) + filename = f"{datetime.now().strftime('%Y%m%d%H%M%S')}-{self.file_name}-报表.xlsx" + if self.download_type == 'usb': + try: + self.put_in(filename, binary) + return 200, "导出成功" + except Exception as e: + print(e) + return 404, "U盘未找到" + encoded_filename = urllib.parse.quote(filename) + + return binary, encoded_filename + + def put_in(self, filename: str, bin: BytesIO): + if system_name == 'Windows': + usbs = [disk.device for disk in psutil.disk_partitions() if disk.opts == 'rw,removable'] + if not usbs: + raise Exception() + # with open(f"D:\\{filename}", 'wb') as f: + # f.write(bin.getbuffer()) + # return + path_file = path.join(usbs[0], filename) + else: + usbs = [item for item in psutil.disk_partitions() if item.mountpoint.startswith(f'/media/{getpass.getuser()}')] + if not usbs: + raise Exception() + path_file = path.join(usbs[0].mountpoint, filename) + with open(path_file, 'wb') as f: + f.write(bin.getbuffer()) diff --git a/app/validators/api.py b/app/validators/api.py new file mode 100644 index 0000000..8d37f74 --- /dev/null +++ b/app/validators/api.py @@ -0,0 +1,123 @@ +from pydantic import BaseModel + + +#用户注册,编辑 +class SignUpdateModel(BaseModel): + username: str + name: str + role_id: str + password: str + sex: int + +#用户登录 +class SignInModel(BaseModel): + username: str + password: str + +#用户分页列表 +class UserModel(BaseModel): + page_no: int = 1 + page_size: int = 5 + +#下位机发送指令 +class SendMesModel(BaseModel): + name: str + status:int = 0 + +#新增试验 +class InsertModel(BaseModel): + items: dict + + +class ContinueModel(BaseModel): + id: str + step: str + +class UpdateModel(BaseModel): + id: str + items: dict + +#停止试验 +class StopModel(BaseModel): + id: str + test_result: int = 0 + + +class SpecimenSearchModel(BaseModel): + page_no: int = 1 + page_size: int = 5 + specimen_name: str = None + specimen_code: str = None + specimen_id: str = None + rfid: str = None + + +class WaitSpecimenSearchModel(BaseModel): + page_no: int = 1 + page_size: int = 5 + record_id: str = None + specimen_name: str = None + specimen_code: str = None + specimen_id: str = None + rfid: str = None + + +#报表查询 +class SearchWordModel(BaseModel): + page_no: int = 1 + page_size: int = 5 + name: str = None + experiment_id: str = None + record_id: str = None + date: str = None + +class CombustionLimitModel(BaseModel): + specimen_id: str + ct_value: str + combustion_type: str + + +#报表导出 +class ExportTableModel(BaseModel): + specimen_id: str + download_type: str + + +# 获取实验数据 +class DataModel(BaseModel): + id: str + + +class SpecimenModel(BaseModel): + items: dict + + +class SpecimenConfigModel(BaseModel): + items: dict + + +class InStorageModel(BaseModel): + specimen_ids: list + storage_type: int + + +class DeviceConfigModel(BaseModel): + light_status: int # 0-关闭 1-开启 + fan_status: int # 0-关闭 1-开启 + warning_light_status: int = 0# 0-关闭 1-开启 + lock_status: int # 0-关闭 1-开启 + + +class ExperimentParamModel(BaseModel): + temperature: float # 设置温度 + expose_time: float # 暴露时间 + put_time: float # 放置时间 + short_circuit_param: float = 0.0 # 短路参数 + specimen_name: str # 样品名称 + battery_type: str # 电池型号 + specimen_num: str # 样品编号 + battery_specification: str # 电池规格 + battery_manufacturers: str # 电池厂家 + channel_ids: str # 通道ids 逗号分隔 + experiment_specimen_id: str = '' # 材料id + channel_experiment_dict : dict = {} # 通道实验参数 diff --git a/main.py b/main.py new file mode 100644 index 0000000..50a9fc9 --- /dev/null +++ b/main.py @@ -0,0 +1,50 @@ +import os + +from app.api.ShortCircuit import app_start +from app.conf.Setting import settings +import uvicorn +from fastapi import Request +from app.api import create_app +from app.lib.Utils import Utils +from fastapi.encoders import jsonable_encoder +from fastapi.exceptions import RequestValidationError +from fastapi.responses import JSONResponse +from app.models import DateEntity +from app.models.Repository import OrmBase +from fastapi.middleware.cors import CORSMiddleware + +app = create_app() +DateEntity.Base.metadata.create_all(bind=OrmBase.engine)#创建表结构 + +app.add_middleware( + CORSMiddleware, + # allow_origins=["*"], # 允许所有源,也可以指定具体源 + allow_origins=["http://127.0.0.1:8000"], # 前端的源地址 + allow_credentials=True, + allow_methods=["*"], # 允许所有方法 + allow_headers=["*"], # 允许所有头 +) + +@app.exception_handler(RequestValidationError) +async def validation_exception_handler(request: Request, exc: RequestValidationError): + return JSONResponse( + status_code=200, + content=jsonable_encoder(Utils.false_return(desc='参数错误')), + ) + + +@app.middleware("http") +async def before_request(request, call_next): + response = await call_next(request) + return response + + +# @app.on_event("startup") +# async def startup(): +# # pass +# await app_start() + + + +if __name__ == '__main__': + uvicorn.run(app="main:app",host=settings.SERVER_HOST,port=settings.SERVER_POST,reload=settings.RELOAD) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f2d6361 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,32 @@ +aiomysql==0.2.0 +annotated-types==0.6.0 +anyio==4.3.0 +click==8.1.7 +colorama==0.4.6 +exceptiongroup==1.2.0 +fastapi==0.110.1 +greenlet==3.0.3 +h11==0.14.0 +idna==3.6 +logzero==1.7.0 +modbus-tk==1.1.3 +paginate==0.5.6 +paginate-sqlalchemy==0.3.1 +psutil==5.9.8 +pydantic==1.9.1 +pydantic_core==2.16.3 +PyMySQL==1.1.0 +pyserial==3.5 +sniffio==1.3.1 +SQLAlchemy==2.0.29 +starlette==0.37.2 +typing_extensions==4.11.0 +uvicorn==0.29.0 +websockets==12.0 +openpyxl==3.1.2 +matplotlib==3.7.5 +scipy==1.10.1 +opencv-python~=4.10.0.84 +numpy~=1.24.4 +pillow~=10.4.0 +APScheduler~=3.10.4 \ No newline at end of file diff --git a/run.bat b/run.bat new file mode 100644 index 0000000..a0c8942 --- /dev/null +++ b/run.bat @@ -0,0 +1,7 @@ +@echo off +start /min cmd /k "conda activate py10 && D:&&cd DG16-D\&& python main.py" +start "" "C:\Program Files\Google\Chrome\Application\chrome" --start-fullscreen http://127.0.0.1:8088" +D:&&cd dist\ && python -m http.server 8088" + + +exit \ No newline at end of file diff --git a/start.bat b/start.bat new file mode 100644 index 0000000..8381291 --- /dev/null +++ b/start.bat @@ -0,0 +1,4 @@ +@echo off +::start python D:\DG21-E\main.py +start C:\Users\Administrator\AppData\Local\Google\Chrome\Application\chrome --start-fullscreen http://127.0.0.1 +exit \ No newline at end of file diff --git a/start.bat.lnk b/start.bat.lnk new file mode 100644 index 0000000000000000000000000000000000000000..3023e4cab445df4c1306101bb985f6758709363b GIT binary patch literal 846 zcmeZaU|?VrVFHp23`Nd$7ov`V z5lX|T>kJ|c@&OP0FDSgS$`n|1gM&faz+kQ2B0`P6fH}#M?dp7=Rvz_$LP83?Z^kO#yEF<{XSVuMVB zsQmg3oF+hgP&9xX`_^FZEoX*K_vJq>2rns4Q_=*Ar+^fI91AiI*{2E&8eso}6oEh} z5Q7X^mX)kwJN1&_)@uw0{BKHJumpA@2 D8KkQa literal 0 HcmV?d00001 diff --git a/start.lnk b/start.lnk new file mode 100644 index 0000000000000000000000000000000000000000..d08833b48b0a925f5565ad38a7cf200687c94790 GIT binary patch literal 1360 zcma)6Ye>=BPv(2pqCwUDM zW0&(7{!lgfsEIrjpb&}Rq)^R*>V&iZkdhp&V$kc?ZztNhM7Lei^{!h^_j1}za!@Pu zg5)JX=?T**m8!VK*3%s33^lp^9<8^<8;zH}6V8j0uBm?2V!Y9?Z*-=U8+snnLbnCr zc!|n^j0HZo`2d$afC^wO`MHWQC0e_Kkj#Z#xgxuBMS`KN(AJV`Nxd{ju7CFCiit`^ z7Al6uO7rp%X(KL57Up}v$Nvr;KCrK3FB1z$GMjBEy}0jJcoEX#$g9Fa~^D{-@k z_qv*>PlNUYz1*cc2n=ZQwKP%rwa z=4#F3fyrre@BWu@Mtytu{N6j7=hHuux`uw8QIu&uuQZ;E*P(#^L1^n7m5!gn(Z0R}F~uSteeS7tRFuKw~eDnsSy+X)aGf=>%B#Q=7BoNqLcFWKMN0yW2l@0)FYG zu`u5X2Xe}JEd=H>tfL})1@IW~am! zZ|tpS1wy9g7a2