lmstack 3 năm trước cách đây
commit
2b7e6008ad
100 tập tin đã thay đổi với 15312 bổ sung0 xóa
  1. 442 0
      LIB/BACKEND/DBManager.py
  2. 2 0
      LIB/BACKEND/DEPLOY/build.cmd
  3. 489 0
      LIB/BACKEND/DataPreProcess.py
  4. 61 0
      LIB/BACKEND/Log.py
  5. 72 0
      LIB/BACKEND/OPENAPI/OpenApi.py
  6. 74 0
      LIB/BACKEND/Tools.py
  7. 35 0
      LIB/FRONTEND/AlgoTest/Algo1/V_1_0_0/Dockerfile
  8. 60 0
      LIB/FRONTEND/DrivingRange/main.py
  9. 27 0
      LIB/FRONTEND/LeakCurrent/main.py
  10. 64 0
      LIB/FRONTEND/SignalMonitor/main.py
  11. 294 0
      LIB/FRONTEND/day_sta.py
  12. 157 0
      LIB/FRONTEND/deltsoc/LFPDeltSoc20210804.py
  13. 4 0
      LIB/FRONTEND/deltsoc/detaSOC表头及数据类型.xlsx
  14. 27 0
      LIB/FRONTEND/deltsoc/main.py
  15. BIN
      LIB/FRONTEND/deltsoc/骑享资产梳理-20210621.xlsx
  16. 127 0
      LIB/FRONTEND/odo/CalDist.py
  17. 68 0
      LIB/FRONTEND/odo/CalDist_Batch.py
  18. 77 0
      LIB/FRONTEND/odo/GpsRank.py
  19. 159 0
      LIB/FRONTEND/odo/ProcessDfBms.py
  20. 139 0
      LIB/FRONTEND/odo/ProcessDfGps.py
  21. 293 0
      LIB/FRONTEND/odo/UpdtFct.py
  22. 28 0
      LIB/FRONTEND/odo/UpdtFct_Main.py
  23. BIN
      LIB/FRONTEND/odo/asset_table.xlsx
  24. 66 0
      LIB/FRONTEND/odo/main_1.py
  25. 106 0
      LIB/FRONTEND/other/bat_user_relation/main.py
  26. 309 0
      LIB/FRONTEND/soh/LFPSoh 20210711.py
  27. 173 0
      LIB/FRONTEND/soh/NCMSoh 20210716.py
  28. 34 0
      LIB/FRONTEND/soh/main.py
  29. 6 0
      LIB/FRONTEND/soh/soh表头及数据类型.xlsx
  30. BIN
      LIB/FRONTEND/soh/骑享资产梳理-20210621.xlsx
  31. 13 0
      LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/CoreAlgo/core.py
  32. 34 0
      LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/Main/Dockerfile
  33. 13 0
      LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/Main/config-dev.ini
  34. 13 0
      LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/Main/config-pro.ini
  35. 13 0
      LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/Main/config-test.ini
  36. 141 0
      LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/Main/main.py
  37. 132 0
      LIB/MIDDLE/Anomaly_Detection/V1_0_0/anomalyPCA.py
  38. 42 0
      LIB/MIDDLE/Anomaly_Detection/V1_0_0/main_anomalyPCA.py
  39. 110 0
      LIB/MIDDLE/Anomaly_Detection/V1_0_0/main_detection.py
  40. BIN
      LIB/MIDDLE/Anomaly_Detection/V1_0_0/sn-20210903.xlsx
  41. 699 0
      LIB/MIDDLE/CellStateEstimation/BatDiag/V1_0_0/CBMSBatDiag.py
  42. 164 0
      LIB/MIDDLE/CellStateEstimation/BatDiag/main.py
  43. 262 0
      LIB/MIDDLE/CellStateEstimation/BatSafetyAlarm/V1_0_1/CBMSSafetyAlarm.py
  44. 159 0
      LIB/MIDDLE/CellStateEstimation/BatSafetyAlarm/main.py
  45. 905 0
      LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/CBMSBatInterShort.py
  46. 506 0
      LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/CBMSBatUniform.py
  47. 262 0
      LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/CBMSSafetyWarning.py
  48. BIN
      LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/Uniform表单.xlsx
  49. 246 0
      LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/VoltStray.py
  50. BIN
      LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/电压变化率离群度表单.xlsx
  51. 227 0
      LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/main.py
  52. 134 0
      LIB/MIDDLE/CellStateEstimation/Common/BatParam.py
  53. 64 0
      LIB/MIDDLE/CellStateEstimation/Common/DBDowload/DBDownload.py
  54. 64 0
      LIB/MIDDLE/CellStateEstimation/Common/DBDowload/V1.0.1/DBDownload.py
  55. 64 0
      LIB/MIDDLE/CellStateEstimation/Common/DBDownload.py
  56. 363 0
      LIB/MIDDLE/CellStateEstimation/Common/V1_0_1/BatParam.py
  57. 56 0
      LIB/MIDDLE/CellStateEstimation/Common/V1_0_1/DBDownload.py
  58. 25 0
      LIB/MIDDLE/CellStateEstimation/Common/V1_0_1/log.py
  59. 24 0
      LIB/MIDDLE/CellStateEstimation/Common/log.py
  60. 562 0
      LIB/MIDDLE/CellStateEstimation/InterShort/V1_0_0/CBMSBatInterShort.py
  61. BIN
      LIB/MIDDLE/CellStateEstimation/InterShort/V1_0_0/InterShort表单.xlsx
  62. 89 0
      LIB/MIDDLE/CellStateEstimation/InterShort/main.py
  63. 723 0
      LIB/MIDDLE/CellStateEstimation/SOH/V1_0_0/CBMSBatSoh.py
  64. BIN
      LIB/MIDDLE/CellStateEstimation/SOH/V1_0_0/SOH表单.xlsx
  65. 89 0
      LIB/MIDDLE/CellStateEstimation/SOH/main.py
  66. 370 0
      LIB/MIDDLE/CellStateEstimation/Uniform/V1_0_0/CBMSBatUniform.py
  67. BIN
      LIB/MIDDLE/CellStateEstimation/Uniform/V1_0_0/Uniform表单.xlsx
  68. 81 0
      LIB/MIDDLE/CellStateEstimation/Uniform/main.py
  69. BIN
      LIB/MIDDLE/CellStateEstimation/算法运行周期.xlsx
  70. 422 0
      LIB/MIDDLE/DrivingRange/UpdtFct.py
  71. 12 0
      LIB/MIDDLE/DrivingRange/UpdtFctTable.py
  72. 11 0
      LIB/MIDDLE/DrivingRange/UpdtFctTableNewest.py
  73. 16 0
      LIB/MIDDLE/DrivingRange/UpdtVehElecRng.py
  74. BIN
      LIB/MIDDLE/DrivingRange/计算续驶里程程序介绍.docx
  75. 90 0
      LIB/MIDDLE/ExcessTemp/V1_0_0/ExcessTemp.py
  76. BIN
      LIB/MIDDLE/ExcessTemp/V1_0_0/温度过高预警表单.xlsx
  77. 396 0
      LIB/MIDDLE/IndexStaByOneCycle.py
  78. 346 0
      LIB/MIDDLE/IndexStaByPeriod.py
  79. 75 0
      LIB/MIDDLE/OutlierDetection/V_1_0_2/anomalyPCA.py
  80. 40 0
      LIB/MIDDLE/OutlierDetection/V_1_0_2/main_anomalyPCA.py
  81. 173 0
      LIB/MIDDLE/OutlierDetection/VoltOutlier/V_1_0_0/sta.py
  82. 49 0
      LIB/MIDDLE/OutlierDetection/VoltOutlier/V_1_0_1/anomalySparsePCA.py
  83. 33 0
      LIB/MIDDLE/OutlierDetection/VoltOutlier/V_1_0_1/main_anomaly.py
  84. BIN
      LIB/MIDDLE/OutlierDetection/VoltOutlier/V_1_0_1/输出表单.xlsx
  85. 881 0
      LIB/MIDDLE/OutlierDetection/VoltOutlier/main.ipynb
  86. 89 0
      LIB/MIDDLE/RemainChargeTime/V1_0_0/MakeDataSet_History.py
  87. BIN
      LIB/MIDDLE/RemainChargeTime/V1_0_0/历史剩余充电时间表单.xlsx
  88. 439 0
      LIB/MIDDLE/SaftyCenter/CellValueDiag/V1_0_0/SC_SamplingSafty.py
  89. 194 0
      LIB/MIDDLE/SaftyCenter/CellValueDiag/main.py
  90. 75 0
      LIB/MIDDLE/SaftyCenter/Common/DBDownload.py
  91. 63 0
      LIB/MIDDLE/SaftyCenter/Common/FeiShuData.py
  92. 150 0
      LIB/MIDDLE/SaftyCenter/Common/QX_BatteryParam.py
  93. 51 0
      LIB/MIDDLE/SaftyCenter/DataDiag_Static/BMSuploaderrortest.py
  94. 539 0
      LIB/MIDDLE/SaftyCenter/DataDiag_Static/CBMSBatDiag.py
  95. 213 0
      LIB/MIDDLE/SaftyCenter/DataDiag_Static/DataStatistics.py
  96. 36 0
      LIB/MIDDLE/SaftyCenter/DataDiag_Static/DiagDataMerge.py
  97. 115 0
      LIB/MIDDLE/SaftyCenter/DataDiag_Static/SC_BMSUploadError.py
  98. 73 0
      LIB/MIDDLE/SaftyCenter/DataDiag_Static/SC_CtrlSafty.py
  99. 360 0
      LIB/MIDDLE/SaftyCenter/DataDiag_Static/SC_SamplingSafty.py
  100. 329 0
      LIB/MIDDLE/SaftyCenter/DataDiag_Static/main.py

+ 442 - 0
LIB/BACKEND/DBManager.py

@@ -0,0 +1,442 @@
+'''
+暂时采用http方式获取历史数据。
+
+预留:后期若改用通过访问数据库的形式进行数据的获取,则本文件负责数据库的连接,sql指令的执行,数据获取等功能。
+'''
+__author__ = 'lmstack'
+
+import time
+import datetime
+import time
+import pandas as pd
+import numpy as np
+import json
+import requests
+import pymysql
+import pdb
+from collections import Counter
+
+
+
+class DBManager():
+
+    def __init__(self, host='', port='', auth='', db='', username='', password=''):
+        self.host = host
+        self.port = port
+        self.auth = auth
+        self.db = db
+        self.username = username
+        self.password = password
+        pass
+
+    def __enter__(self):
+        self.connect()
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.close()
+
+    def connect(self):
+        conn_success_flag = 0
+        while not conn_success_flag:
+            try:
+                self.conn = pymysql.connect(host=self.host, port=self.port, user=self.user, password=self.password, database=self.db)
+            except Exception as e:
+                conn_success_flag = 0
+                print("数据库连接失败 :{}".format(e))
+                time.sleep(5)
+            else:
+                conn_success_flag = 1
+                self.cursor = self.conn.cursor()
+    def close(self):
+        try:
+            self.conn.close()
+        except Exception as e:
+            print(e)
+        else:
+            print('数据库已断开连接')
+
+    def add(table, keyvalue):
+        fields_str = ''
+        values_str = ''
+        for k,v in keyvalue.items():
+
+            fields_str += k+' '
+        sql = 'insert into table {} ({}) values ({})'.format(table, fields_str, values_str)
+
+
+    # 以下各个函数实现 通过http方式获取数据
+    @staticmethod
+    def _get_var_name(cellnum,Tempnum,Othernum):
+        temp = []
+        for i in range(cellnum):
+            temp.append('单体电压'+str(i+1))
+        for i in range(Tempnum):
+            temp.append('单体温度'+str(i+1))
+        for i in range(Othernum):
+            temp.append('其他温度'+str(i+1))
+        return temp
+
+    @staticmethod
+    def _download_json_data(url):
+        '''
+        返回json数据的生成器,一次一行
+        '''
+        i = 0
+        while 1:
+            try:
+                r = requests.get(url, stream=True, timeout=(0.1, 1000),  headers={'Connection':'keep-alive', 'Accept':'*/*', 'Accept-Encoding':'gzip,deflate,br'})
+                break
+            except requests.exceptions.RequestException as e:
+                # if (i == 0):
+                #     print()
+                # print('\r' + 'Server Error, retry {}......'.format(str(i)), end=" ")
+                time.sleep(0.1)
+                i+=1    
+        # print(r.content)
+        # pdb.set_trace()
+        data = [] 
+        for line in r.iter_lines():
+
+            if line:
+                data.append(json.loads(line))
+                # yield json.loads(line)
+        return data
+
+    @staticmethod
+    def _convert_to_dataframe_bms(data, mode=0, CellUNum=0, CellTNum=0, OtherTNum=0):
+        CellU = []
+        CellT = []
+        OtherT = []
+        CellU_Num = CellUNum
+        CellT_Num = CellTNum
+        OtherT_Num = OtherTNum
+        # try:
+        #     CellU_Num = len(data['ffBatteryStatus']['cellVoltageList'])
+        #     CellU = np.array(data['ffBatteryStatus']['cellVoltageList']) * 1000
+        #     CellU = CellU.tolist()
+        # except:
+        #     CellU_Num = 0
+        # try:
+        #     CellT_Num = len(data['ffBatteryStatus']['cellTempList'])
+        #     CellU.extend(data['ffBatteryStatus']['cellTempList'])
+        # except:
+        #     CellT_Num = 0
+
+        # try:
+        #     OtherT_Num = len(data['ffBatteryStatus']['otherTempList'])
+        #     CellU.extend(data['ffBatteryStatus']['otherTempList'])
+        # except:
+        #     OtherT_Num = 0
+   
+        CellU = np.array(data['ffBatteryStatus'].get('cellVoltageList',[]))
+        CellT = data['ffBatteryStatus'].get('cellTempList',[])
+        OtherT = data['ffBatteryStatus'].get('otherTempList',[])
+        if (len(CellU) != CellU_Num or len(CellT) != CellT_Num or len(OtherT) != OtherT_Num):
+            return pd.DataFrame()
+        CellU = CellU * 1000
+
+
+        # for i in range(CellU_Num):
+        #     CellU.append(data['ffBatteryStatus']['cellVoltageList'][i]*1000)
+        # for i in range(CellT_Num):
+        #     CellU.append(data['ffBatteryStatus']['cellTempList'][i])
+        # for i in range(OtherT_Num):
+        #     CellU.append(data['ffBatteryStatus']['otherTempList'][i])
+        if mode == 0:
+            data_len = 16
+            
+            # data_block = np.array([data['info']['obdTime'],data['ffBatteryStatus']['rssi'],data['ffBatteryStatus']['errorLevel'],data['ffBatteryStatus']['errorCode']
+            #         ,data['ffBatteryStatus']['current'],data['ffBatteryStatus']['voltageInner'],data['ffBatteryStatus']['voltageOutter'],
+            #         data['ffBatteryStatus']['totalOutputState'],data['ffBatteryStatus']['lockedState'],
+            #         data['ffBatteryStatus']['chargeState'],data['ffBatteryStatus']['heatState'],data['ffBatteryStatus']['cellVoltageDiff']
+            #         ,data['ffBatteryStatus']['soc'],data['ffBatteryStatus']['soh'],data['ffBatteryStatus']['cellVolBalance']]).reshape(1,data_len)
+            data_block = np.array([data['info']['obdTime'],data['ffBatteryStatus'].get('rssi',None),data['ffBatteryStatus'].get('errorLevel', None),data['ffBatteryStatus'].get('errorCode', None), 
+            data['ffBatteryStatus'].get('current',None),data['ffBatteryStatus'].get('voltageInner', None),data['ffBatteryStatus'].get('voltageOutter', None),
+                    data['ffBatteryStatus'].get('totalOutputState', None),data['ffBatteryStatus'].get('lockedState', None),
+                    data['ffBatteryStatus'].get('chargeState', None),data['ffBatteryStatus'].get('heatState', None),data['ffBatteryStatus'].get('cellVoltageDiff', None)
+                    ,data['ffBatteryStatus'].get('soc', None),data['ffBatteryStatus'].get('soh', None),data['ffBatteryStatus'].get('cellVolBalance', None),data['ffBatteryStatus'].get('insResis', None)]).reshape(1,data_len)
+        elif mode == 1:
+            data_len = 12
+            
+            data_block = np.array([data['info']['obdTime'],data['ffBatteryStatus'].get('rssi',None)
+            ,data['ffBatteryStatus'].get('errorLevel', None),data['ffBatteryStatus'].get('errorCode', None),data['ffBatteryStatus'].get('switchState', None)
+            ,data['ffBatteryStatus'].get('current',None),data['ffBatteryStatus'].get('voltageInner', None),data['ffBatteryStatus'].get('chargeState', None),
+            data['ffBatteryStatus'].get('cellVoltageDiff', None),data['ffBatteryStatus'].get('soc', None),data['ffBatteryStatus'].get('soh', None),data['ffBatteryStatus'].get('insResis', None)]).reshape(1,data_len)
+        data_block = np.append(data_block,CellU)
+        data_block = np.append(data_block,CellT)
+        data_block = np.append(data_block,OtherT)
+        data_block = data_block.reshape(1,len(data_block))
+        return data_block
+
+    @staticmethod
+    def _convert_to_dataframe_gps(data, mode=0):
+        if mode == 0:
+            if data['info']['subType'] == 1:
+                data_block = np.array([data['info'].get('obdTime',None),data['ffGps'].get('locationType',None), data['ffGps'].get('satellites',None),
+                                       data['ffGps'].get('latitude',None),data['ffGps'].get('longitude',None),data['ffGps'].get('speed',None), 
+                                       data['ffGps'].get('altitude',None), data['ffGps'].get('direction', None), data['ffGps'].get('mileage',None)]).reshape(1,9)
+                df = pd.DataFrame(
+                    columns=['时间戳','定位类型', '卫星数','纬度','经度','速度[km/h]','海拔','航向', '里程/m'],data=data_block)
+            elif data['info']['subType'] == 2:
+                df = pd.DataFrame(
+                    columns=['时间戳','定位类型', '卫星数','纬度','经度','速度[km/h]','海拔','航向'])
+        if mode == 1:
+                data_block = np.array([data['info']['obdTime'],data['ffGps']['locationType'],data['ffGps']['latitude'],data['ffGps']['longitude']
+                    ,data['ffGps']['speed'], data['ffGps']['isValid']]).reshape(1,6)
+                df = pd.DataFrame(
+                    columns=['时间戳','定位类型', '纬度','经度','速度[km/h]','有效位'],data=data_block)
+        return df
+
+    
+    @staticmethod
+    def _convert_to_dataframe_system(data, mode=0):
+        if mode == 0:
+            
+            data_block = np.array([data['info'].get('obdTime', None),data['ffSystemInfo'].get('heatTargetTemp', None), data['ffSystemInfo'].get('heatTimeout',None),
+                                    time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(int(data['ffSystemInfo'].get('rentalStartTime'))/1000)),
+                                    data['ffSystemInfo'].get('rentalPeriodDays',None),data['ffSystemInfo'].get('bmsInterval',None), 
+                                    data['ffSystemInfo'].get('gpsInterval', None)]).reshape(1,7)
+            df = pd.DataFrame(
+                columns=['时间戳','加热目标温度', '加热超时','租赁开始时间','租赁天数','bms上传周期','gps上传周期'],data=data_block)
+        if mode == 1:
+                df = pd.DataFrame()
+
+        return df
+    
+    @staticmethod
+    def _convert_to_dataframe_accum(data, mode=0):
+        if mode == 0:
+            
+            data_block = np.array([data['info'].get('obdTime',None),data['ffBatteryAccum'].get('SOH_AlgUnexTime',None), data['ffBatteryAccum'].get('CHG_AHaccum',None),
+                                    data['ffBatteryAccum'].get('CHG_PHaccum',None), data['ffBatteryAccum'].get('DSG_AHaccum',None),
+                                    data['ffBatteryAccum'].get('DSG_PHaccum',None),data['ffBatteryAccum'].get('OverTemp_CHG_AHaccum',None), 
+                                    data['ffBatteryAccum'].get('OverTemp_CHG_PHaccum',None)]).reshape(1,8)
+            df = pd.DataFrame(
+                columns=['时间戳','SOH未标定时间', '累计充电电量','累计充电能量','累计放电电量','累计放电能量',
+                                               '累计高温充电电量', '累计高温充电能量'],data=data_block)
+
+        if mode == 1:
+                data_block = np.array([data['info'].get('obdTime',None), data['ffBatteryAccum'].get('CHG_AHaccum',None),
+                                    data['ffBatteryAccum'].get('CHG_PHaccum',None), data['ffBatteryAccum'].get('DSG_AHaccum',None),
+                                    data['ffBatteryAccum'].get('DSG_PHaccum',None),data['ffBatteryAccum'].get('totalMileage',None)]).reshape(1,6)
+                df = pd.DataFrame(
+                    columns=['时间戳','累计充电电量','累计充电能量','累计放电电量','累计放电能量', '累积里程'],data=data_block)
+        return df
+    
+    
+    
+    @staticmethod
+    def _get_data(urls,type_name,mode=0):
+        data = DBManager._download_json_data(urls)
+        if type_name == 'bms':
+            if mode == 0:
+                name_const = ['时间戳','GSM信号','故障等级','故障代码','总电流[A]','总电压[V]', '外电压', '总输出状态', '上锁状态', '充电状态','加热状态',
+                              '单体压差', 'SOC[%]','SOH[%]','单体均衡状态', '绝缘电阻']
+            elif mode == 1:
+                name_const = ['时间戳','GSM信号','故障等级', '故障代码','开关状态', '总电流[A]','总电压[V]','充电状态', '单体压差', 'SOC[%]','SOH[%]', '绝缘电阻']
+            i=0
+            st = time.time()
+            
+            # 计算本次最大电芯数量
+            CellUNum = 0
+            CellTNum = 0
+            OtherTNum = 0
+            
+            cellUNumList = []
+            cellTNumList = []
+            otherTNumList = []
+
+            for line in data:
+                temp = len(line['ffBatteryStatus'].get('cellVoltageList', []))
+                cellUNumList.append(temp)
+                # if (temp > CellUNum):
+                #     CellUNum = temp
+                temp = len(line['ffBatteryStatus'].get('cellTempList', []))
+                cellTNumList.append(temp)
+                # if (temp > CellTNum):
+                #     CellTNum = temp
+                temp = len(line['ffBatteryStatus'].get('otherTempList', []))
+                otherTNumList.append(temp)
+                # if (temp > OtherTNum):
+                #     OtherTNum = temp
+ 
+            if (len(data)>0):
+                result = Counter(cellUNumList)
+                CellUNum = max(result, key=result.get)
+                
+                result = Counter(cellTNumList)
+                CellTNum = max(result, key=result.get)
+
+                result = Counter(otherTNumList)
+                OtherTNum = max(result, key=result.get)
+
+                       
+            data_blocks = pd.DataFrame()
+            for line in data:
+                et = time.time()
+                try:
+                    if i==0:
+                        data_blocks = DBManager._convert_to_dataframe_bms(line, mode, CellUNum, CellTNum, OtherTNum)
+                        if (len(data_blocks)>0):
+                            i+=1
+                        continue
+                except Exception as e:
+                    # print(e)
+                    continue
+                    
+                try:
+                    data_block = DBManager._convert_to_dataframe_bms(line, mode, CellUNum, CellTNum, OtherTNum)
+                except Exception as e:
+                    # print(e)
+                    continue
+           
+                try:
+                    if (len(data_block)>0):
+                        data_blocks = np.concatenate((data_blocks,data_block),axis=0)
+                except Exception as e:
+                    print(e)
+                    if 'all the input array dimensions for the concatenation axis must match exactly' in str(e) or \
+                    'all the input array dimensions except for the concatenation axis must match exactly'  in str(e):
+                        pass
+                    else:
+                        raise e
+                i+=1
+  
+            name_var = DBManager._get_var_name(CellUNum,CellTNum,OtherTNum)
+            name_const.extend(name_var)
+            columns_name = name_const
+            if i==0:
+                data_blocks = []
+
+            df_all = pd.DataFrame(columns=columns_name,data=data_blocks)
+
+                
+            if not df_all.empty:
+                df_all.loc[:,'时间戳'] = df_all.loc[:,'时间戳'].apply(lambda x:time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(int(x)/1000)))
+            return df_all
+        elif type_name =='gps':
+            if mode == 0:
+                df_all = pd.DataFrame(columns=['时间戳','定位类型', '卫星数','纬度','经度','速度[km/h]','海拔','航向'])
+            elif mode == 1:
+                df_all = pd.DataFrame(columns=['时间戳','定位类型', '纬度','经度','速度[km/h]','有效位'])
+            
+            for line in data:
+                df_add = DBManager._convert_to_dataframe_gps(line, mode)
+                df_all = df_all.append(df_add,ignore_index=True)
+            if not df_all.empty:
+                df_all.loc[:,'时间戳'] = df_all.loc[:,'时间戳'].apply(lambda x:time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(int(x)/1000)))
+            return df_all
+        elif type_name =='system':
+            if mode == 0:
+                df_all = pd.DataFrame(columns=['时间戳','加热目标温度', '加热超时','租赁开始时间','租赁天数','bms上传周期','gps上传周期'])
+            elif mode == 1:
+                df_all = pd.DataFrame()
+            for line in data:
+                df_add = DBManager._convert_to_dataframe_system(line, mode)
+                df_all = df_all.append(df_add,ignore_index=True)
+            if not df_all.empty:
+                df_all.loc[:,'时间戳'] = df_all.loc[:,'时间戳'].apply(lambda x:time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(int(x)/1000)))
+            return df_all
+        
+        elif type_name =='accum':
+            if mode == 0:
+                df_all = pd.DataFrame(columns=['时间戳','SOH未标定时间', '累计充电电量','累计充电能量','累计放电电量','累计放电能量',
+                                               '累计高温充电电量', '累计高温充电能量'])
+            elif mode == 1:
+                df_all = pd.DataFrame(columns=['时间戳','累计充电电量','累计充电能量','累计放电电量','累计放电能量', '累积里程'])
+            for line in data:
+                df_add = DBManager._convert_to_dataframe_accum(line, mode)
+                df_all = df_all.append(df_add,ignore_index=True)
+            if not df_all.empty:
+                df_all.loc[:,'时间戳'] = df_all.loc[:,'时间戳'].apply(lambda x:time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(int(x)/1000)))
+            return df_all
+    def get_data(self, url='http://172.16.126.13/store/load?dataType={}&limit=0&sn={}', sn='', start_time='', end_time='', 
+                 data_groups=['bms', 'gps']):
+        '''
+        获取指定 sn 和起止日期的bms和gps数据.
+        添加了重试机制。
+
+        --------------输入参数------------
+        url:数据获取url, 可采用默认值
+        sn: str, 电池sn号
+        start_time: str, 开始时间
+        end_time: str, 结束时间
+        data_groups:  选择需要获取的数据组,可填入多个字符串(默认只获取bms和gps数据)
+                    bms: bms数据
+                    gps:gps数据
+                    system:system数据
+                    accum:accum数据
+        
+
+        --------------输出参数------------
+        df_data: {'bms':dataframe, 'gps':dataframe, 'system':dataframe, ;accum':dataframe}
+        '''
+        
+        if len(set(data_groups) - (set(data_groups) and set(['bms', 'gps', 'system', 'accum']))) > 0:
+            raise Exception("data_groups 参数错误")
+        
+        
+        # mode: 0:正常取数; 1:7255 取数
+        if sn[0:2] == 'UD' or sn[0:2] == 'MG':
+            mode = 1
+        else:
+            mode = 0
+        bms_all_data = pd.DataFrame()
+        gps_all_data = pd.DataFrame()
+        system_all_data = pd.DataFrame()
+        accum_all_data = pd.DataFrame()
+        maxnum = (datetime.datetime.strptime(end_time, "%Y-%m-%d %H:%M:%S") - datetime.datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")).days +1
+        print("### start to get data {} from {} to {}".format(sn, start_time, end_time))
+        # 为避免chunkEncodingError错误,数据每天获取一次,然后将每天的数据合并,得到最终的数据
+        for j in  range(int(maxnum)):
+            timefrom = datetime.datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")+ datetime.timedelta(days=j)
+            timeto = datetime.datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")+ datetime.timedelta(days=j+1)
+            #滴滴的数据sub=0
+            if timefrom.strftime('%Y-%m-%d %H:%M:%S') >= end_time:
+                break
+            elif timeto.strftime('%Y-%m-%d %H:%M:%S') > end_time:
+                timeto = datetime.datetime.strptime(end_time, '%Y-%m-%d %H:%M:%S')
+            #print('{}_{}_----getting data----'.format(sn, timefrom))
+            bms_data = pd.DataFrame()
+            gps_data = pd.DataFrame()
+            system_data = pd.DataFrame()
+            accum_data = pd.DataFrame()
+            while True:
+                try:
+                    print('\r' + "# get data from {} to {}.........".format(str(timefrom), str(timeto)), end=" ")
+                    for data_group in data_groups:
+                        if data_group == 'bms':
+                            file_url = url.format(12, sn) + "&from="+timefrom.strftime('%Y-%m-%d %H:%M:%S')+"&to="+timeto.strftime('%Y-%m-%d %H:%M:%S')
+                            bms_data = DBManager._get_data(file_url,'bms',mode)
+                        if data_group == 'gps':
+                            file_url = url.format(16, sn) + "&from="+timefrom.strftime('%Y-%m-%d %H:%M:%S')+"&to="+timeto.strftime('%Y-%m-%d %H:%M:%S')
+                            gps_data = DBManager._get_data(file_url,'gps',mode)
+                        if data_group == 'system':
+                            file_url = url.format(13, sn) + "&from="+timefrom.strftime('%Y-%m-%d %H:%M:%S')+"&to="+timeto.strftime('%Y-%m-%d %H:%M:%S')
+                            system_data = DBManager._get_data(file_url,'system',mode)
+                        if data_group == 'accum':
+                            file_url = url.format(23, sn) + "&from="+timefrom.strftime('%Y-%m-%d %H:%M:%S')+"&to="+timeto.strftime('%Y-%m-%d %H:%M:%S')
+                            accum_data = DBManager._get_data(file_url,'accum',mode)
+                except Exception as e:
+                    if 'Connection broken' in str(e):
+                        continue
+                    elif 'Number of manager items must equal union of block items' in str(e):
+                        break
+                    else:
+                        raise Exception
+                else:
+                    bms_all_data = pd.concat([bms_all_data, bms_data], ignore_index=True)
+                    gps_all_data = pd.concat([gps_all_data, gps_data], ignore_index=True)
+                    system_all_data = pd.concat([system_all_data, system_data], ignore_index=True)
+                    accum_all_data = pd.concat([accum_all_data, accum_data], ignore_index=True)
+                    break
+
+        bms_all_data = bms_all_data.reset_index(drop=True)
+        gps_all_data = gps_all_data.reset_index(drop=True)
+        system_all_data = system_all_data.reset_index(drop=True)
+        accum_all_data = accum_all_data.reset_index(drop=True)
+        print('\nall data-getting done, bms_count is {}, gps_count is {}, system_count is {}, accum_count is {} \n'.format(
+            str(len(bms_all_data)), str(len(gps_all_data)), str(len(system_all_data)), str(len(accum_all_data))))
+        return {'bms':bms_all_data, 'gps':gps_all_data, 'system':system_all_data, 'accum':accum_all_data}
+

+ 2 - 0
LIB/BACKEND/DEPLOY/build.cmd

@@ -0,0 +1,2 @@
+::  本脚本用于构建测试和生产环境中的python基础镜像
+

+ 489 - 0
LIB/BACKEND/DataPreProcess.py

@@ -0,0 +1,489 @@
+'''
+数据预处理类
+
+'''
+__author__ = 'lmstack'
+
+
+# import CONFIGURE.PathSetting as PathSetting
+# import sys
+# sys.path.append(PathSetting.backend_path)
+# from LIB.BACKEND
+from os import defpath
+import pandas as pd
+import numpy as np
+import pdb
+from numba import jit
+from LIB.BACKEND import Tools
+
+class DataPreProcess:
+    def __init__(self):
+        self.tools = Tools.Tools()
+        pass
+
+    # def data_split(self, dfin, drive_interval_threshold=120, charge_interval_threshold=300, 
+    #                            drive_stand_threshold=120, charge_stand_threshold=300,
+    #                            default_time_threshold = 300, drive_time_threshold=300, charge_time_threshold=300, 
+    #                            stand_time_threshold = 1800):
+    #     '''
+    #     数据分段函数,会调用_data_split_by_status和_data_split_by_time函数。
+    #     其中_data_split_by_status 将数据分为charge、drive、stand、和none段;
+    #     _data_split_by_time 将每个段内的数据,根据时间跳变继续分段。
+
+    #     '''
+    def time_filter(self, df_bms, df_gps):
+        df_bms.drop_duplicates(subset=['时间戳'], keep='first', inplace=True)
+        df_gps.drop_duplicates(subset=['时间戳'], keep='first', inplace=True)
+        df_bms = df_bms.reset_index(drop=True)
+        df_gps = df_gps.reset_index(drop=True)
+        return df_bms, df_gps
+
+    def data_split_by_status(self, dfin, drive_interval_threshold=120, charge_interval_threshold=300, 
+                                    drive_stand_threshold=120, charge_stand_threshold=300):
+        '''
+        # 数据预处理分段, 将原始数据段分为 charge、drive、stand、none段
+        # 状态判断
+        # 1、drive:(状态为2或3 且 存在电流>0 ) 或 (电流持续为0 且 持续时间<阈值 且 上一段数据为行车)
+        # 2、charge:(状态为2或3 且 不存在电流>0 ) 或 (电流持续为0 且 持续时间<阈值 且 上一段数据为充电)
+        # 3、stand:(电流持续为0 且 是数据段的第一段) 或 (电流持续为0 且 持续时间>阈值)
+        # 4、none: 其他
+
+        --------------输入参数-------------:
+        drive_interval_threshold: 行车段拼接阈值,如果两段行车的间隔时间小于该值,则两段行车合并。
+        charge_interval_threshold: 充电段拼接阈值,如果两段充电的间隔时间小于该值,则两段充电合并。
+        drive_stand_threshold: 静置段合并至行车段阈值,如果静置时间小于该值,则合并到上一段的行车中。
+        charge_stand_threshold: 静置段合并至充电段阈值,如果静置时间小于该值,则合并到上一段的充电中。
+
+        --------------输出-----------------:
+        在原始数据后面,增加data_split_by_crnt, data_split_by_status, data_status 三列
+        data_split_by_crnt: 按电流分段的序号
+        data_split_by_status:按电流和状态分段的序号
+        data_status: 状态标识
+        '''
+        # 首先根据电流是否为0 ,将数据分段
+        df = dfin.copy()
+        df['时间戳'] = pd.to_datetime(df['时间戳'])
+        
+        crnt_zero_or_not = df['总电流[A]']==0
+        last_crnt_flag = crnt_zero_or_not[0]
+        temp = 1
+        group_id = [temp]
+        for cur_crnt_flag in crnt_zero_or_not[1:]:
+            if last_crnt_flag ^ cur_crnt_flag:
+                temp = temp + 1
+            last_crnt_flag = cur_crnt_flag
+            group_id.append(temp)
+        df['data_split_by_crnt'] = group_id
+
+        # 然后判断每个段内的 充电状态及电流=0持续时长,决定当前状态
+        temp = 1
+        last_status = ""
+        status_id = []
+        status_list = []
+        data_number_list = sorted(list(set(df['data_split_by_crnt'])))
+
+        for data_number in data_number_list:
+            df_sel = df[df['data_split_by_crnt'] == data_number]
+            origin_index = list(df_sel.index)
+            df_sel = df_sel.reset_index(drop=True)
+            temp_2 = 0
+            # 如果当前数据段的电流非0,则可能分为charge、drive或none段
+            if df_sel.loc[0,'总电流[A]'] != 0:
+                # 电流 分段中可能存在状态变化的时刻, 内部根据状态进行分段.
+                # 该数据段内部,根据bms状态信号进行二次分段
+                status_drive_or_not = df_sel['充电状态']==3
+                last_status_flag = status_drive_or_not[0]
+                temp_2 = 0
+                group_id_2 = [temp_2]
+                for cur_status_flag in status_drive_or_not[1:]:
+                    if last_status_flag ^ cur_status_flag:
+                        temp_2 = temp_2 + 1
+                    last_status_flag = cur_status_flag
+                    group_id_2.append(temp_2)
+                
+                # 遍历二次状态分段
+                temp_2 = 0
+                last_status_2 = last_status
+                df_sel['index'] = group_id_2
+                data_number_list_2 = sorted(list(set(group_id_2)))
+                for data_number_2 in data_number_list_2:
+                    
+                    df_sel_2 = df_sel[df_sel['index'] == data_number_2]
+                    df_sel_2 = df_sel_2.reset_index(drop=True)
+                    
+                    # 根据bms状态 及 电流符号决定是charge还是drive
+                    # 如果状态为2或3, 且电流均<=0 则记为充电
+                    if df_sel_2.loc[0, '充电状态'] in [2, 3] and len(df_sel_2[df_sel_2['总电流[A]'] > 0]) == 0: 
+                        cur_status = 'charge'
+                    # 如果状态为2或3,且存在电流>0 则记为行车
+                    elif df_sel_2.loc[0, '充电状态'] in [2, 3] and len(df_sel_2[df_sel_2['总电流[A]'] > 0]) > 0: 
+                        cur_status = 'drive'
+                    # 否则 记为none
+                    else:
+                        cur_status = 'none'
+                    status_list.extend([cur_status] * len(df_sel_2))
+                    
+                    # 状态id号与前面电流为0的相同状态进行合并, 均判断应不应该与上一段合并    
+                    if origin_index[0] == 0: # 如果是所有数据的起始段数据,则直接赋值id号
+                        status_id.extend([temp + temp_2]*len(df_sel_2))
+                    
+                    else: # 判断是否与上一段数据合并
+                        deltaT = (df.loc[origin_index[0], '时间戳'] - df.loc[origin_index[0]-1, '时间戳']).total_seconds()
+                        # 如果 状态一致, 且 间隔时间小于阈值,则合并
+                        if last_status_2 == 'drive' and cur_status == last_status_2 and deltaT < drive_interval_threshold: 
+                            temp_2 = temp_2 - 1  
+                            status_id.extend([temp + temp_2]*len(df_sel_2)) 
+                        # 如果状态一致, 且 间隔时间小于阈值,则合并
+                        elif last_status_2 == 'charge' and cur_status == last_status_2 and deltaT < charge_interval_threshold: 
+                            temp_2 = temp_2 - 1  
+                            status_id.extend([temp + temp_2]*len(df_sel_2)) 
+                        else:
+                            status_id.extend([temp + temp_2]*len(df_sel_2))
+                    temp_2 = temp_2 + 1
+                    last_status_2 = status_list[-1]
+                temp_2 = temp_2 - 1
+            else:
+                # 如果当前数据段的电流为0,则可能分为stand,charge、drive或none段
+                if origin_index[0] == 0: # 如果是数据的起始,则无论长短,都认为是stand
+                    status_id.extend([temp]*len(df_sel))
+                    status_list.extend(['stand'] * len(df_sel))
+                else: # 不是数据的起始
+                    cur_deltaT = (df.loc[origin_index[-1], '时间戳'] - df.loc[origin_index[0], '时间戳']).total_seconds()
+                    if last_status == 'charge': # 如果上一个状态为充电
+                        if cur_deltaT < charge_stand_threshold: # 如果本次电流为0的持续时间小于 阈值,则合并
+                            status_list.extend(['charge'] * len(df_sel))
+                            temp = temp - 1  
+                            status_id.extend([temp]*len(df_sel))
+                        else: # 否则超过了阈值,记为stand
+                            status_id.extend([temp]*len(df_sel))
+                            status_list.extend(['stand'] * len(df_sel))
+                    elif last_status == 'drive': # 如果上一个状态为行车
+                        if cur_deltaT < drive_stand_threshold: # 如果本次电流为0的持续时间小于 阈值,则合并
+                            status_list.extend(['drive'] * len(df_sel))
+                            temp = temp - 1  
+                            status_id.extend([temp]*len(df_sel))
+                        else: # 否则超过了阈值,记为stand
+                            status_id.extend([temp]*len(df_sel))
+                            status_list.extend(['stand'] * len(df_sel))
+                    elif last_status == 'none': # 如果上一个状态未知
+                        status_id.extend([temp] * len(df_sel))
+                        status_list.extend(['stand'] * len(df_sel))
+            temp = temp + temp_2 + 1
+            last_status = status_list[-1] # 上一组状态
+        df['data_split_by_status'] = status_id
+        df['data_status'] = status_list
+        return df
+    def data_split_by_time(self, dfin, default_time_threshold = 300, drive_time_threshold=300, charge_time_threshold=300, 
+                                        stand_time_threshold = 1800):
+        '''
+        # 该函数用来解决数据丢失问题导致的分段序号异常,
+        # 将经过data_split_by_status分段后的数据,每个段内两行数据的时间跳变如果超过阈值,则继续分为两段
+
+        --------------输入参数-------------:
+        dfin:  调用data_split_by_status之后的函数
+        default_time_threshold: 默认时间阈值,如果状态内部时间跳变大于该值,则划分为两段
+        drive_time_threshold: 行车时间阈值,如果行车状态内部时间跳变大于该值,则划分为两段
+        charge_time_threshold: 充电时间阈值,如果充电状态内部时间跳变大于该值,则划分为两段
+        stand_time_threshold:静置时间阈值,如果静置状态内部时间跳变大于该值,则划分为两段
+
+        --------------输出-----------------:
+        在输入数据后面,增加data_split_by_status_time 一列
+        data_split_by_status_time: 按照状态和时间分段后的序号
+        '''  
+        data_id = []
+        temp = 1
+        data_number_list = sorted(list(set(dfin['data_split_by_status'])))
+        for data_number in data_number_list:
+            # if data_number == 1203:
+            #     pdb.set_trace()
+            status = list(dfin[dfin['data_split_by_status']==data_number]['data_status'])[0]
+            cur_indexes = dfin[dfin['data_split_by_status']==data_number].index
+            
+            time_array = np.array(dfin[dfin['data_split_by_status']==data_number]['时间戳'])
+            time_diff = np.diff(time_array)
+            time_diff = time_diff.astype(np.int64)
+            time_interval = default_time_threshold
+            if status == 'drive':
+                time_interval = drive_time_threshold
+            elif status == 'charge':
+                time_interval = charge_time_threshold
+            elif status == 'stand':
+                time_interval = stand_time_threshold
+            time_diff_index = (np.argwhere(((time_diff/1e9) > time_interval)==True))[:,0]
+            time_diff_origin_index = cur_indexes[time_diff_index]+1
+            if len(time_diff_index) == 0:
+                data_id.extend([temp] * len(cur_indexes))
+                temp += 1
+            else:
+                last_index = cur_indexes[0]
+                for index, cur_index in enumerate(time_diff_origin_index):
+                    if index == len(time_diff_origin_index)-1: # 如果是最后一个index,则
+                        data_id.extend([temp]* (cur_index-last_index))
+                        last_index = cur_index
+                        temp += 1
+                        data_id.extend([temp]* (cur_indexes[-1]-last_index+1))
+                    else:
+                        data_id.extend([temp]* (cur_index-last_index))
+                    last_index = cur_index
+                    temp += 1
+        dfin['data_split_by_status_time'] = data_id
+        return dfin
+    def combine_drive_stand(self, dfin):
+        '''
+        合并放电和静置段:将两次充电之间的所有数据段合并为一段, 状态分为 charge 和not charge
+        ---------------输入----------
+        dfin: 调用data_split_by_status()后输出的bms数据
+
+        ---------------输出----------
+        在输入数据后面,增加data_split_by_status_after_combine, data_status_after_combine 两列
+        data_split_by_status_after_combine: 将两次充电间的数据合并后的段序号
+        data_status_after_combine: 每段数据的状态标识
+        '''
+        df = dfin.copy()
+        data_split_by_status_1 = []
+        data_status_1 = []
+        number = 1
+        first_flag = True
+        data_number_list = sorted(list(set(df['data_split_by_status_time'])))
+        for data_number in data_number_list:
+            status = list(df[df['data_split_by_status_time']==data_number]['data_status'])
+            cur_status = status[0]
+            if first_flag:
+                first_flag = False
+            elif (last_status not in ['charge'] and cur_status in ['charge']) or (last_status in ['charge'] and cur_status not in ['charge']):
+                number += 1
+
+            data_split_by_status_1.extend([number]*len(status))
+            if cur_status in ['charge']:
+                data_status_1.extend(['charge']*len(status))
+            else:
+                data_status_1.extend(['not charge']*len(status))
+
+            last_status = cur_status
+        df['data_split_by_status_after_combine'] = data_split_by_status_1
+        df['data_status_after_combine'] = data_status_1
+
+        return df
+
+    def cal_stand_time(self, dfin):
+        '''
+        # 计算静置时间
+        # 将每次行车或充电的前后静置时间,赋值给stand_time 列, 单位为分钟
+
+        ----------------输入参数---------
+        dfin: 调用data_split_by_status()后输出的bms数据
+
+        ----------------输出参数----------
+        在输入数据后面,增加stand_time列
+        stand_time : 在行车段或充电段的起止两个位置处,表明开始前和结束后的静置时长,单位为分钟
+
+        '''
+        df = dfin.copy()
+        stand_time = []
+        first_flag = True
+        data_number_list = sorted(list(set(df['data_split_by_status_time'])))
+        for index, data_number in enumerate(data_number_list):
+            status = list(df[df['data_split_by_status_time']==data_number]['data_status'])
+            time =  list(df[df['data_split_by_status_time']==data_number]['时间戳'])
+            cur_status = status[0]
+            cur_delta_time = (time[-1]-time[0]).total_seconds() / 60.0 # 分钟
+            if len(status) >= 2:
+                if first_flag:
+                    first_flag = False
+                    if index < len(data_number_list)-1:
+                        if cur_status in ['charge', 'drive']:
+                            next_status = list(df[df['data_split_by_status_time']==data_number_list[index+1]]['data_status'])[0]
+                            stand_time.extend([None]*(len(status)-1))
+                            if next_status == 'stand':
+                                next_time =  list(df[df['data_split_by_status_time']==data_number_list[index+1]]['时间戳'])
+                                stand_time.extend([(next_time[-1]-next_time[0]).total_seconds() / 60.0])
+                            else:
+                                stand_time.extend([0])
+                        else:
+                            stand_time.extend([None]*len(status))
+                    else:
+                        stand_time.extend([None]*len(status))      
+                else:
+                    if cur_status in ['charge', 'drive']:
+                        if last_status == 'stand':
+                            stand_time.extend([last_delta_time])
+                        else:
+                            stand_time.extend([0])
+                        stand_time.extend([None]*(len(status)-2))
+                        if index < len(data_number_list)-1:
+                            next_status = list(df[df['data_split_by_status_time']==data_number_list[index+1]]['data_status'])[0]
+                            if next_status == 'stand':
+                                next_time =  list(df[df['data_split_by_status_time']==data_number_list[index+1]]['时间戳'])
+                                stand_time.extend([(next_time[-1]-next_time[0]).total_seconds() / 60.0])
+                            else:
+                                stand_time.extend([0])
+                        else:
+                            stand_time.extend([None])
+                    else:
+                        stand_time.extend([None]*len(status))
+                        
+            else:
+                stand_time.extend([None])
+            last_status = cur_status
+            last_delta_time = cur_delta_time
+        df['stand_time'] = stand_time
+        return df
+
+    # 输入GPS数据,返回本段数据的累积里程,及平均时速(如果两点之间)
+    @jit
+    def _cal_odo_speed(self, lat_list, long_list, time_list):
+        '''
+        输入:经度列表, 纬度列表, 时间列表;
+        输出:每两个经纬度坐标之间的距离,以及速度 的数组
+        '''
+        dis_array = []
+        speed_array = []
+ 
+        for i in range(len(lat_list)-1):
+            dis = self.tools.cal_distance(lat_list[i],long_list[i], lat_list[i+1],long_list[i+1])
+            dis_array.append(dis)
+            deltaT = abs(time_list[i] - time_list[i+1]).total_seconds()
+            speed_array.append(dis * 3600.0/deltaT)
+        return np.array(dis_array), np.array(speed_array)
+
+    def gps_data_judge(self, df_bms, df_gps, time_diff_thre=300, odo_sum_thre=200, drive_spd_thre=80, parking_spd_thre=2):
+        '''
+        GPS数据可靠性判断函数(基于combine前的分段)
+
+        GPS数据出现以下情况时,判定为不可靠:
+        1)如果该段对应的地理位置数据 少于2 个,则认为不可靠
+        2)如果截取的GPS数据的起止时间,与BMS数据段的起止时间相差超过阈值,则认为不可靠
+        3)如果行车段 累积里程超过阈值,车速超过阈值
+        4) 如果非行车段 车速超过阈值
+
+        --------------输入参数--------------:
+        time_diff_thre: 时间差阈值
+        odo_sum_thre: 累积里程阈值
+        drive_spd_thre: 行车车速阈值
+        parking_spd_thre: 非行车状态车速阈值
+
+        --------------输出参数--------------:
+        df_bms 增加一列gps_rely, 表明对应的GPS数据是否可靠。
+                1:可靠
+                <0: 表示不可靠的原因
+        df_gps 增加两列odo, speed, 分别表示前后两点间的距离和速度
+        '''
+        df_gps['时间戳'] = pd.to_datetime(df_gps['时间戳'])
+        res_record = {'drive':0, 'charge':0, 'stand':0, 'none':0, 'total':0}
+        rely_list = []
+        df_gps['odo'] = [None] * len(df_gps)
+        df_gps['speed'] = [None] * len(df_gps)
+        data_number_list = sorted(list(set(df_bms['data_split_by_status_time'])))
+        for data_number in data_number_list[:]:
+            df_sel = df_bms[df_bms['data_split_by_status_time'] == data_number]
+            df_sel = df_sel.reset_index(drop=True)
+            df_sel_gps = df_gps[(df_gps['时间戳']>=df_sel.loc[0,'时间戳']) & (df_gps['时间戳']<=df_sel.loc[len(df_sel)-1,'时间戳'])]
+            origin_index = list(df_sel_gps.index)
+            df_sel_gps = df_sel_gps.reset_index(drop=True)
+            # 如果当前段数据对应的地理位置数据少于2个
+            if len(df_sel_gps) <= 1:
+                rely_list.extend([-1]*len(df_sel))
+                res_record[str(df_sel.loc[0, 'data_status'])] = res_record[str(df_sel.loc[0, 'data_status'])] + 1
+                continue
+            # 如果GPS 起止时间段和BMS数据相差超过阈值
+            if abs(df_sel_gps.loc[0, '时间戳'] - df_sel.loc[0,'时间戳']).total_seconds() > time_diff_thre or \
+               abs(df_sel_gps.loc[len(df_sel_gps)-1, '时间戳'] - df_sel.loc[len(df_sel)-1,'时间戳']).total_seconds() > time_diff_thre:
+                rely_list.extend([-2]*len(df_sel))
+                res_record[str(df_sel.loc[0, 'data_status'])] = res_record[str(df_sel.loc[0, 'data_status'])] + 1
+                continue
+
+            # 计算该段数据每两点之间的里程以及速度
+            dis_array, speed_array = self._cal_odo_speed(df_sel_gps['纬度'], df_sel_gps['经度'], df_sel_gps['时间戳'])
+            # 如果 累积里程异常 或 平均车速异常 或两点间车速异常
+            avg_speed = np.sum(dis_array) *3600.0 / abs(df_sel_gps.loc[0, '时间戳'] - df_sel_gps.loc[len(df_sel_gps)-1, '时间戳']).total_seconds()
+            if np.sum(dis_array) > odo_sum_thre or avg_speed > drive_spd_thre or (speed_array > drive_spd_thre).any():
+                rely_list.extend([-3]*len(df_sel))
+                res_record[str(df_sel.loc[0, 'data_status'])] = res_record[str(df_sel.loc[0, 'data_status'])] + 1
+                continue
+            
+            # 如果停车,且 平均时速超过阈值,则不可靠
+            if (str(df_sel.loc[0, 'data_status']) == 'charge' or str(df_sel.loc[0, 'data_status']) == 'stand') and avg_speed > parking_spd_thre :
+                rely_list.extend([-4]*len(df_sel))
+                res_record[str(df_sel.loc[0, 'data_status'])] = res_record[str(df_sel.loc[0, 'data_status'])] + 1
+                continue
+            # 剩下的记录为可靠
+            rely_list.extend([1]*len(df_sel))
+            df_gps.loc[origin_index[1:], 'odo'] = dis_array
+            df_gps.loc[origin_index[1:], 'speed'] = speed_array
+        df_bms['gps_rely'] = rely_list
+        res_record['total'] = (res_record['drive'] + res_record['charge'] + res_record['stand'] + res_record['none'] )/df_bms['data_split_by_status_time'].max()
+        if len(set(df_bms[df_bms['data_status']=='drive']['data_split_by_status_time'])) > 0:
+            res_record['drive'] = (res_record['drive'])/len(set(df_bms[df_bms['data_status']=='drive']['data_split_by_status_time']))
+        if len(set(df_bms[df_bms['data_status']=='charge']['data_split_by_status_time'])) > 0:
+            res_record['charge'] = (res_record['charge'])/len(set(df_bms[df_bms['data_status']=='charge']['data_split_by_status_time']))
+        if len(set(df_bms[df_bms['data_status']=='stand']['data_split_by_status_time'])) > 0:
+            res_record['stand'] = (res_record['stand'])/len(set(df_bms[df_bms['data_status']=='stand']['data_split_by_status_time']))
+        if len(set(df_bms[df_bms['data_status']=='none']['data_split_by_status_time'])) > 0:
+            res_record['none'] = (res_record['none'])/len(set(df_bms[df_bms['data_status']=='none']['data_split_by_status_time']))
+        return df_bms, df_gps, res_record
+
+
+    def data_gps_judge_after_combine(self, df_bms, df_gps, time_diff_thre=600, odo_sum_thre=200, drive_spd_thre=80, parking_spd_thre=2):
+        '''
+        GPS数据可靠性判断函数2 (基于combine后的分段) 判别方式同data_gps_judge
+        '''
+        df_gps['时间戳'] = pd.to_datetime(df_gps['时间戳'])
+        res_record = {'not charge':0, 'charge':0, 'total':0} # 不可靠的比例
+
+        rely_list = []
+        df_gps['odo_after_combine'] = [None] * len(df_gps)
+        df_gps['speed_after_combine'] = [None] * len(df_gps)
+ 
+        data_number_list = sorted(list(set(df_bms['data_split_by_status_after_combine'])))
+        for data_number in data_number_list[:]:
+            df_sel = df_bms[df_bms['data_split_by_status_after_combine'] == data_number]
+            df_sel = df_sel.reset_index(drop=True)
+
+            # 尝试采用drive段的开始和结束时间选择GPS数据,因为stand时GPS数据可能存在丢失,影响里程的计算
+            df_sel_drive = df_sel[df_sel['data_status']=='drive'] # 
+            df_sel_drive = df_sel_drive.reset_index(drop=True)
+            if df_sel_drive.empty:
+                df_sel_1 = df_sel
+            else:
+                df_sel_1 = df_sel_drive
+            df_sel_gps = df_gps[(df_gps['时间戳']>=df_sel_1.loc[0,'时间戳']) & (df_gps['时间戳']<=df_sel_1.loc[len(df_sel_1)-1,'时间戳'])]
+            origin_index = list(df_sel_gps.index)
+            df_sel_gps = df_sel_gps.reset_index(drop=True)
+            # 如果当前段数据对应的地理位置数据少于2个
+            if len(df_sel_gps) <= 1:
+                rely_list.extend([-1]*len(df_sel))
+                res_record[str(df_sel.loc[0, 'data_status_after_combine'])] = res_record[str(df_sel.loc[0, 'data_status_after_combine'])] + 1
+                continue
+            # 如果GPS 起止时间段和BMS数据相差超过阈值
+            if abs(df_sel_gps.loc[0, '时间戳'] - df_sel_1.loc[0,'时间戳']).total_seconds() > time_diff_thre or \
+                abs(df_sel_gps.loc[len(df_sel_gps)-1, '时间戳'] - df_sel_1.loc[len(df_sel_1)-1,'时间戳']).total_seconds() > time_diff_thre:
+                rely_list.extend([-2]*len(df_sel))
+                res_record[str(df_sel.loc[0, 'data_status_after_combine'])] = res_record[str(df_sel.loc[0, 'data_status_after_combine'])] + 1
+                continue
+
+            # 计算该段数据每两点之间的里程以及速度
+            dis_array, speed_array = self._cal_odo_speed(df_sel_gps['纬度'], df_sel_gps['经度'], df_sel_gps['时间戳'])
+            # 如果 累积里程异常 或 平均车速异常 或两点间车速异常
+            avg_speed = np.sum(dis_array) *3600.0 / abs(df_sel_gps.loc[0, '时间戳'] - df_sel_gps.loc[len(df_sel_gps)-1, '时间戳']).total_seconds()
+            if np.sum(dis_array) > odo_sum_thre or avg_speed > drive_spd_thre or (speed_array > drive_spd_thre).any():
+                rely_list.extend([-3]*len(df_sel))
+                res_record[str(df_sel.loc[0, 'data_status_after_combine'])] = res_record[str(df_sel.loc[0, 'data_status_after_combine'])] + 1
+                continue
+            
+            # 如果充电,且 平均时速超过阈值,则不可靠
+            if str(df_sel.loc[0, 'data_status_after_combine']) == 'charge' and avg_speed > parking_spd_thre:
+                rely_list.extend([-4]*len(df_sel))
+                res_record[str(df_sel.loc[0, 'data_status_after_combine'])] = res_record[str(df_sel.loc[0, 'data_status_after_combine'])] + 1
+                continue
+            # 剩下的记录为可靠
+            rely_list.extend([1]*len(df_sel))
+            df_gps.loc[origin_index[1:], 'odo_after_combine'] = dis_array
+            df_gps.loc[origin_index[1:], 'speed_after_combine'] = speed_array
+        df_bms['gps_rely_after_combine'] = rely_list
+        res_record['total'] = (res_record['not charge'] + res_record['charge'])/df_bms['data_split_by_status_after_combine'].max()
+        if len(set(df_bms[df_bms['data_status_after_combine']=='not charge']['data_split_by_status_after_combine'])) > 0:
+            res_record['not charge'] = (res_record['not charge'])/len(set(df_bms[df_bms['data_status_after_combine']=='not charge']['data_split_by_status_after_combine']))
+        if len(set(df_bms[df_bms['data_status_after_combine']=='charge']['data_split_by_status_after_combine'])) > 0 :
+            res_record['charge'] = (res_record['charge'])/len(set(df_bms[df_bms['data_status_after_combine']=='charge']['data_split_by_status_after_combine']))
+        return df_bms, df_gps, res_record
+        

+ 61 - 0
LIB/BACKEND/Log.py

@@ -0,0 +1,61 @@
+'''
+Log类
+
+'''
+__author__ = 'lmstack'
+
+
+import logging
+from logging import handlers
+import os
+
+class Mylog:
+
+    def __init__(self, log_name='this_log', log_level = ''):
+        self.logger = logging.getLogger(log_name)
+        self._set_log_level(logging.INFO)
+        if len(log_level) > 0:
+            self._set_log_level(log_level)
+
+    def get_logger(self):
+        return self.logger
+    
+    def set_file_hl(self, file_name='all.log', log_level='info', size=1, backupCount=10):
+        fh = handlers.RotatingFileHandler(file_name, maxBytes=size, backupCount=backupCount)
+        fh_formatter = logging.Formatter('%(asctime)s:%(created)f:%(name)s:%(module)s:%(funcName)s:%(levelname)s:%(message)s')
+        fh.setFormatter(fh_formatter)
+        if len(log_level) > 0:
+            if log_level == 'debug':
+                fh.setLevel(logging.DEBUG)
+            if log_level == 'info':
+                fh.setLevel(logging.INFO)
+            if log_level == 'warning':
+                fh.setLevel(logging.WARNING)
+            if log_level == 'error':
+                fh.setLevel(logging.ERROR)
+        self.logger.addHandler(fh)
+
+    def set_stream_hl(self, log_level='info'):
+        sh = logging.StreamHandler()
+        sh_formatter = logging.Formatter('%(asctime)s:%(created)f:%(name)s:%(module)s:%(funcName)s:%(levelname)s:%(message)s')
+        sh.setFormatter(sh_formatter)
+        if len(log_level) > 0:
+            if log_level == 'debug':
+                sh.setLevel(logging.DEBUG)
+            if log_level == 'info':
+                sh.setLevel(logging.INFO)
+            if log_level == 'warning':
+                sh.setLevel(logging.WARNING)
+            if log_level == 'error':
+                sh.setLevel(logging.ERROR)
+        self.logger.addHandler(sh)  
+
+    def _set_log_level(self, log_level):
+        if log_level == 'debug':
+            self.logger.setLevel(logging.DEBUG)
+        if log_level == 'info':
+            self.logger.setLevel(logging.INFO)
+        if log_level == 'warning':
+            self.logger.setLevel(logging.WARNING)
+        if log_level == 'error':
+            self.logger.setLevel(logging.ERROR)

+ 72 - 0
LIB/BACKEND/OPENAPI/OpenApi.py

@@ -0,0 +1,72 @@
+'''
+数据预处理类
+
+'''
+__author__ = 'lmstack'
+
+import pandas as pd
+
+import requests
+import json
+
+class OpenApi:
+    def __init__(self):
+        pass
+
+    '''
+    获取 运维系统中故障相关的信息
+    '''
+    def get_device_fault(self, start_time="", end_time=""):
+        # start_time = "2021-12-30 00:00:00"
+        # end_time = "2021-12-31 00:00:00"
+        url = 'http://open.li-ai.com.cn/admin/v1/deviceFault'
+        headers = {'Content-Type':"application/json; charset=utf-8","token":"2A26DD0ADE53456D928562A17131A3B5"}
+        data = {}
+        if start_time != "":
+            data['startTime'] = start_time
+        if end_time != "":
+            data['endTime'] = end_time
+        response = requests.post(url,  json=data, headers=headers)
+        # df = pd.DataFrame(columns=['sn', 'imei','faultTime', 'updateTime', 'overTime', 'faultStatus', 'questionType', 'questionTypeText', 'childProblem','childProblemText'])
+        df = pd.DataFrame(json.loads(response.text)["data"])
+        # for d in json.loads(response.text)["data"]:
+        #     df = df.append(d, ignore_index=True)
+        return df
+    
+    '''
+    获取 资产相关信息
+    '''
+    def get_asset(self, sn=""):
+        # start_time = "2021-12-30 00:00:00"
+        # end_time = "2021-12-31 00:00:00"
+        url = 'http://open.li-ai.com.cn/admin/v1/deviceDetailInfo'
+        headers = {'Content-Type':"application/json; charset=utf-8","token":"2A26DD0ADE53456D928562A17131A3B5"}
+        data = {"id":sn}
+        response = requests.post(url,  json=data, headers=headers)
+        # df = pd.DataFrame(columns=['sn', 'imei','faultTime', 'updateTime', 'overTime', 'faultStatus', 'questionType', 'questionTypeText', 'childProblem','childProblemText'])
+        df = pd.DataFrame(json.loads(response.text)["data"])
+        if len(df)>0:
+            df2 = pd.DataFrame(list(df['basicAssetDto']))
+            df2 = df2.drop(['imei','id', 'deviceType', 'deviceTypeText'],axis=1,errors='ignore')
+            df3 = df.drop(['basicAssetDto'],axis=1)
+            df = df2.join(df3.set_index('sn'),on='sn')      
+        return df
+    
+        '''
+    获取 iotp数据
+    '''
+    def get_last_data(self, sn=""):
+        # start_time = "2021-12-30 00:00:00"
+        # end_time = "2021-12-31 00:00:00"
+        url = 'http://open.li-ai.com.cn/admin/v1/capacityAndLocation'
+        headers = {'Content-Type':"application/json; charset=utf-8","token":"2A26DD0ADE53456D928562A17131A3B5"}
+        data = {"id":sn}
+        response = requests.post(url,  json=data, headers=headers)
+        df = pd.DataFrame(json.loads(response.text)["data"])
+        if len(df)>0:
+            df2 = pd.DataFrame(list(df['batteryLocationDto']))
+            df3 = pd.DataFrame(list(df['batteryAccumDto']))
+            df2 = df2.drop(['id','updateTime'],axis=1,errors='ignore')
+            df3 = df3.drop(['id','updateTime'],axis=1,errors='ignore')
+            df = df2.join(df3.set_index('sn'),on='sn')     
+        return df

+ 74 - 0
LIB/BACKEND/Tools.py

@@ -0,0 +1,74 @@
+'''
+工具类
+
+'''
+__author__ = 'lmstack'
+
+
+# import CONFIGURE.PathSetting as PathSetting
+# import sys
+# sys.path.append(PathSetting.backend_path)
+from LIB.BACKEND import DBManager
+# import DBManager
+import pandas as pd
+import numpy as np
+import math
+from numba import jit
+import os
+import datetime
+import pdb
+import warnings
+warnings.filterwarnings('ignore')
+
+class Tools():
+
+    def __init__(self):
+        pass
+
+    # 数据下载
+    def data_download(self, write_path='', sn='', start_time='', end_time='', data_groups=['bms', 'gps']):
+        '''
+        数据下载函数
+        --------------输入参数------------
+        write_path: 文件保存路径,不需要指定文件名
+        sn: str, 电池sn号
+        start_time: str, 开始时间
+        end_time: str, 结束时间
+        gps_switch: True:获取gps数据; False:不获取gps数据
+        mode: 0:正常取数; 1:7255 取数
+
+        --------------输出参数------------
+        bms_data: 获取到的bms数据
+        gps_data: 获取到的gps数据
+        '''
+        dbManager = DBManager.DBManager()
+        print('downloading......')
+        df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=data_groups)
+        for data_group in data_groups:
+            df_data[data_group].to_csv(os.path.join(write_path, '{}_{}_from_{}_to_{}.csv'.format(data_group, sn, start_time[0:10], end_time[0:10])), index=False, encoding='GB2312')
+        print('downloading success!')
+
+    # 根据经纬度计算距离
+    @jit
+    def cal_distance(self, latitude1, longitude1,latitude2, longitude2,radius=6371):  
+        '''
+        输入两个地点的经纬度,输出两点间的距离
+        '''
+        radLat1 = (math.pi/180)*latitude1  
+        radLat2 = (math.pi/180)*latitude2  
+        radLng1 = (math.pi/180)*longitude1  
+        radLng2= (math.pi/180)*longitude2      
+        d=2*math.asin(math.sqrt(math.pow(math.sin((radLat1-radLat2)/2.0),2)+math.cos(radLat1)*math.cos(radLat2)*math.pow(math.sin((radLng1-radLng2)/2.0),2)))*radius
+        return d
+
+    # 输入GPS数据, 返回本段数据中GPS 位置相距最远的距离
+    # @jit
+    # def cal_max_dis(self, lat, long):
+    #     count = len(lat)
+    #     dis_mat=np.zeros([count,count])
+    #     for i in range(count):
+    #         for j in range(i,count):
+    #             dis_mat[i][j]=self.cal_distance(lat[i],long[i], lat[j],long[j])
+                
+    #     max_dis = dis_mat.max()
+    #     return max_dis

+ 35 - 0
LIB/FRONTEND/AlgoTest/Algo1/V_1_0_0/Dockerfile

@@ -0,0 +1,35 @@
+# 基础镜像
+FROM python:3.8
+
+# 作者,版本
+LABEL author="lm"  
+LABEL version="1.0.0"
+
+# 环境变量参数
+ENV TZ="Asia/Shanghai"
+
+# 安装用到的python 第三方库
+RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple \
+    sqlalchemy \
+    pandas==1.3.4 \
+    pymysql apscheduler \
+    scipy \
+    cryptography \
+    numpy==1.20.3 \
+    requests
+
+#RUN rm -r /usr/bin
+#RUN rm -r /bin
+
+# 配置算法路径
+ARG ALGO_PATH=LIB/MIDDLE/AlgoTest/Algo1
+# 配置版本路径
+ARG ALGO_VERSION=V_1_0_0
+
+# 复制文件文件到 容器
+ADD LIB/BACKEND/ /LIB/BACKEND
+ADD ${ALGO_PATH}/${ALGO_VERSION}/ /${ALGO_VERSION}/
+ADD ${ALGO_PATH}/main.py /main.py
+WORKDIR /
+
+CMD ["python", "main.py"]

+ 60 - 0
LIB/FRONTEND/DrivingRange/main.py

@@ -0,0 +1,60 @@
+#coding=utf-8
+import os
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+from LIB.MIDDLE import SignalMonitor
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+import time, datetime
+import traceback
+from LIB.MIDDLE.DrivingRange import UpdtFct
+import pymysql
+
+from urllib import parse
+
+dbManager = DBManager.DBManager()
+if __name__ == "__main__":
+    #建立引擎
+    db_engine = create_engine(str(r"mysql+pymysql://%s:" + '%s' + "@%s/%s") % ('root', 'pengmin', 'localhost', 'qixiangdb'))
+    #连接到qx数据库
+    db_qx = pymysql.connect(
+            host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com',
+            user='qx_read',
+            password='Qx@123456',#Qx@123456
+            database='qx_cas',
+            charset='utf8'
+        )
+    #连接到本地数据库,输出物
+    db_local = pymysql.connect(
+            host='localhost',
+            user='root',
+            password='pengmin',
+            database='qixiangdb',
+            charset='utf8'
+        )
+
+    #调度周期:每天运行一次。
+
+    #更新所有sn,连读多日的factor,如果start_date和end_date相隔一天,代表更新start_date的factor。
+    start_date="2021-07-23"
+    end_date="2021-07-28"
+    UpdtFct.updtAllSnFct(start_date,end_date, db_engine, db_local, db_qx)
+    df_newest = UpdtFct.updtNewestFctTb(db_local, db_engine)
+    #按照日期排序,只保留最近的一天,输出factor_unique_df,方法为replace。
+    #本函数,每天需要运行一次,用于更新factor。
+    df_newest.to_sql("tb_sn_factor_newest",con=db_engine,chunksize=10000,\
+        if_exists='replace',index=False)
+
+    #调度周期:程序每5分钟运行一次
+
+    #更新剩余里程,每5min一次,几秒钟运行结束。
+    test_time=datetime.datetime.now()#当前系统时间
+    input_time=datetime.datetime.strftime(test_time,'%Y-%m-%d %H:%M:%S')
+
+    # input_time='2021-07-29 11:59:00'#手动设定一个时间
+
+    #函数每5min调度一次,input_time为当前时间,更新tb_sn_factor_soc_range表格
+    df_range = UpdtFct.updtVehElecRng(db_qx, db_local, db_engine, input_time)
+    df_range.to_sql("tb_sn_factor_soc_range",con=db_engine,chunksize=10000,\
+         if_exists='replace',index=False)    

+ 27 - 0
LIB/FRONTEND/LeakCurrent/main.py

@@ -0,0 +1,27 @@
+#coding=utf-8
+import os
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+from LIB.MIDDLE import SignalMonitor
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+import time, datetime
+import traceback
+from LIB.MIDDLE.LeakCurrent import LFPLeakCurrent20210812 as LFPLeakCurrent
+
+from urllib import parse
+
+dbManager = DBManager.DBManager()
+if __name__ == "__main__":
+    SNdata_6060 = pd.read_excel('骑享资产梳理-20210621.xlsx', sheet_name='6060')
+    SNnums_6060=SNdata_6060['SN号']
+    now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+    now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+    start_time=now_time-datetime.timedelta(days=31)
+    end_time=str(now_time)
+    start_time=str(start_time)
+
+    for sn in SNnums_6060.tolist():
+        res = LFPLeakCurrent.cal_LFPLeakCurrent(sn, end_time, start_time)
+        res.to_csv('BMS_LeakCurrent_'+sn+'.csv',encoding='GB18030')

+ 64 - 0
LIB/FRONTEND/SignalMonitor/main.py

@@ -0,0 +1,64 @@
+#coding=utf-8
+import os
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+from LIB.MIDDLE import SignalMonitor
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+import time, datetime
+import traceback
+
+from urllib import parse
+
+dbManager = DBManager.DBManager()
+if __name__ == "__main__":
+    # 读取sn列表
+    df_sn = pd.read_csv('sn_list.csv')
+    df_sn = df_sn.reset_index(drop=True)
+    df_sn['StartTime'] = pd.to_datetime(df_sn['StartTime'])
+    df_sn['EndTime'] = pd.to_datetime(df_sn['EndTime'])
+    df_sn['ExitTime'] = pd.to_datetime(df_sn['ExitTime'])
+    signalMonitor = SignalMonitor.SignalMonitor()
+    
+    cal_period = 24    # 计算间隔,单位h
+    for i in range(0, len(df_sn['sn'])):    # 遍历SN号
+        sn = [df_sn.loc[i,'sn']]
+        if not (sn[0][0:2] == 'PK' or sn[0][0:2] == 'MG' or sn[0][0:2] == 'UD'):
+            continue
+        st = df_sn.loc[i, 'StartTime']
+        if df_sn.loc[i, 'Service'] == 0:
+            if pd.isnull(df_sn.loc[i, 'EndTime']) and pd.isnull(df_sn.loc[i, 'ExitTime']):
+                continue
+            elif pd.isnull(df_sn.loc[i, 'ExitTime']):
+                et = df_sn.loc[i, 'EndTime']
+            else:
+                et = df_sn.loc[i, 'ExitTime']
+        elif pd.isnull(df_sn.loc[i, 'EndTime']):
+            otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
+            et = pd.to_datetime(otherStyleTime)
+        else:
+            et = df_sn.loc[i, 'EndTime']
+        df_last_state = pd.DataFrame(
+            columns=['sn', 'current', 'Timestamp', 'PackState', 'LineState'])    # 每日最后BMS数据
+        df_last_state_gps = pd.DataFrame(
+            columns=['sn', 'Timestamp', 'PackState', 'LineState', 'latitude', 'longitude'])    # 每日最后GPS数据
+        df_res = pd.DataFrame(columns=[
+                                'sn', 'PackState', 'LineState', 'StartTime', 'EndTime', 'OfflineTime'])    # 初始化BMS信号统计数据
+        df_res_gps = pd.DataFrame(columns=[
+                                    'sn', 'PackState', 'LineState', 'StartTime', 'EndTime', 'OfflineTime', 'latitude', 'longitude'])    # 初始化GPS信号统计数据
+        while st < et:
+
+            df_res, df_state, df_last_state = signalMonitor.get_bms_offline_stat(
+                sn, st, et, df_res, df_last_state, cal_period)    # 计算每日BMS信号统计数据
+            df_res_gps, df_last_state_gps = signalMonitor.get_gps_offline_stat(
+                sn, st, et, df_state, df_res_gps, df_last_state_gps, cal_period)    # 计算每日GPS信号统计数据
+            # 数据入库
+            st = st + datetime.timedelta(hours=cal_period)
+        SignalMonitor._file_write(r'D:\result_04.xls', df_res)    # BMS信号统计数据入库
+        SignalMonitor._file_write(r'D:\result_05.xls', df_res_gps)    # GPS信号统计数据入库
+
+        SignalMonitor._file_write(r'D:\result_06.xls', df_last_state)
+        SignalMonitor._file_write(r'D:\result_07.xls', df_last_state_gps)
+
+    # 数据入库

+ 294 - 0
LIB/FRONTEND/day_sta.py

@@ -0,0 +1,294 @@
+<<<<<<< HEAD
+<<<<<<< HEAD
+__author__ = 'lmstack'
+
+# 每日指标统计函数
+import CONFIGURE.PathSetting as PathSetting
+import sys
+sys.path.append(PathSetting.backend_path)
+sys.path.append(PathSetting.middle_path)
+import DBManager
+import Tools
+import DataPreProcess
+import IndexStaByPeriod
+import Log
+import IndexStaByPeriod
+import importlib
+import datetime
+import os
+import pandas as pd
+import time
+importlib.reload(IndexStaByPeriod)
+
+dbManager = DBManager.DBManager()
+dataPrePro = DataPreProcess.DataPreProcess()
+indexPerSta = IndexStaByPeriod.IndexStaByPeriod()
+
+# log 文件配置
+myLog = Log.Mylog('day_sta')
+myLog.set_file_hl(file_name=r'D:\Platform\platform\FRONTEND\day_sta\day_sta.log', log_level='info')
+myLog.set_stream_hl(log_level='info')
+logger = myLog.get_logger()
+logger.info(str(os.getpid()))
+
+# sn文件读取
+sn_list = list(pd.read_excel('D:\Platform\platform\苏州电池列表.xlsx')['sn'])
+sn_list.extend(list(pd.read_excel('D:\Platform\platform\骑享北京6040电池包统计更新20210407.xlsx')['SN号']))
+
+sn = sn_list[0]
+
+# 字段设置及结果文件生成
+columns = ['sn', 'time',  'sumDriveTime', 'sumDriveSoc', 'sumDriveAh', 'sumDriveEnergy']
+st = datetime.datetime.strptime('00:00:00', '%H:%M:%S')
+for i in range(96):
+    et = st + datetime.timedelta(minutes=15)
+    columns.append(st.strftime('%H:%M') + '-' + et.strftime('%H:%M'))
+    st = et
+result_path = r'D:\Platform\platform\FRONTEND\day_sta\result.csv'
+df_res = pd.DataFrame(columns=columns)
+if not os.path.exists(result_path):
+    df_res.to_csv(result_path, index=False)
+    
+# 时间范围设置
+start_time = '{} 00:00:00'.format('2020-01-01')
+end_time = '{} 00:00:00'.format('2021-06-01')
+sta_days = (datetime.datetime.strptime(end_time, '%Y-%m-%d %H:%M:%S') - datetime.datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S')).days
+
+count= 0
+sn_result = {}
+for sn in sn_list[:]:
+    count += 1
+    logger.info('{} start,  {}/{} '.format(sn, str(count), str(len(sn_list))))
+    if sn[2:5] == '500':
+        cap = 40
+    elif sn[2:5] == '504':
+        cap = 55
+    else:
+        logger.info('{} cap error'.format(sn))
+        cap = None
+        continue
+        
+    sn_result.update({'sn':sn})
+    logger.info('{} :{} to {} start'.format(sn, str(start_time), str(end_time)))
+    
+    # 获取数据
+    df_bms, df_gps = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, gps_switch=True, mode=0)
+    if df_bms.empty:
+        continue
+    # 数据预处理
+    
+    # 时间完全相同的数据仅保留一行
+    df_bms_pro, df_gps_pro = dataPrePro.time_filter(df_bms, df_gps)
+    
+    # bms数据按照电流和状态分段, 然后在状态分段内部,根据时间跳变继续分段(解决段内数据丢失)
+    df_bms_pro = dataPrePro.data_split_by_status(df_bms_pro)
+    df_bms_pro = dataPrePro.data_split_by_time(df_bms_pro)
+
+    # bms数据将两次充电间的状态合并
+    df_bms_pro = dataPrePro.combine_drive_stand(df_bms_pro)
+    # bms 数据计算行车和充电开始前后的静置时间
+    df_bms_pro = dataPrePro.cal_stand_time(df_bms_pro)
+    # gps 数据可靠性判断, 并增加里程和速度至gps数据(根据未合并的数据段判断)
+    df_bms_pro, df_gps_pro, res_record= dataPrePro.gps_data_judge(df_bms_pro, df_gps_pro)
+    # gps 数据可靠性判断, 并增加里程和速度至gps数据(根据已合并的数据段判断)
+    df_bms_pro, df_gps_pro, res_record= dataPrePro.data_gps_judge_after_combine(df_bms_pro, df_gps_pro)
+    
+    for sta_day in range(sta_days):
+        try:
+            st_ = datetime.datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S') + datetime.timedelta(days=sta_day)
+            et_ =datetime.datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S') + datetime.timedelta(days=sta_day+1)
+
+        # 按天统计指标
+            sn_result.update({'time':st_.strftime('%Y-%m-%d')})
+
+            df_bms_period = df_bms_pro[(df_bms_pro['时间戳'] > st_.strftime('%Y-%m-%d %H:%M:%S')) & (df_bms_pro['时间戳'] <= et_.strftime('%Y-%m-%d %H:%M:%S'))]
+            #df_gps_period = df_gps_pro[(df_gps_pro['时间戳'] > st_.strftime('%Y-%m-%d %H:%M:%S')) & (df_gps_pro['时间戳'] <= et_.strftime('%Y-%m-%d %H:%M:%S'))]
+            sn_result.update({'sumDriveTime':[indexPerSta.drive_time_sta(df_bms_period)]})
+            sn_result.update({'sumDriveSoc':[indexPerSta.drive_soc_sta(df_bms_period)]})
+            sn_result.update({'sumDriveAh':[indexPerSta.drive_capacity_sta(cap, df_bms_period)]})
+            sn_result.update({'sumDriveEnergy':[indexPerSta.drive_energy_sta(cap, df_bms_period)]})
+
+            # 每天间隔15分钟 统计一次
+            for i in range(96):
+                cur_result = []
+                st__ = st_ + datetime.timedelta(minutes=15 * i)
+                et__ = st_ + datetime.timedelta(minutes=15 * (i+1))
+                df_bms_period = df_bms_pro[(df_bms_pro['时间戳'] > st__.strftime('%Y-%m-%d %H:%M:%S')) & (df_bms_pro['时间戳'] <= et__.strftime('%Y-%m-%d %H:%M:%S'))]
+                #df_gps_period = df_gps_pro[(df_gps_pro['时间戳'] > st__.strftime('%Y-%m-%d %H:%M:%S')) & (df_gps_pro['时间戳'] <= et__.strftime('%Y-%m-%d %H:%M:%S'))]
+                cur_result.append(indexPerSta.drive_time_sta(df_bms_period))
+                cur_result.append(indexPerSta.drive_soc_sta(df_bms_period))
+                cur_result.append(indexPerSta.drive_capacity_sta(cap, df_bms_period))
+                cur_result.append(indexPerSta.drive_energy_sta(cap, df_bms_period))
+                key = st__.strftime('%H:%M') + '-' + et__.strftime('%H:%M')
+                sn_result.update({key:[cur_result]})
+            df_cur_res = pd.DataFrame(sn_result)
+            df_cur_res = df_cur_res[columns]
+
+            # 防止写入结果时,结果文件被打开
+            write_flag = False
+            while not write_flag:
+                try:
+                    df_cur_res.to_csv(result_path, mode='a+', index=False,  header=False)
+                except PermissionError as e:
+                    logger.info('{} error:{}'.format(sn, str(e)))
+                    time.sleep(10)
+                    continue
+                else:
+                    write_flag = True 
+        except Exception as e:
+                logger.info('{} {}_{} error: {}'.format(sn, st_.strftime('%Y-%m-%d %H:%M:%S'), et_.strftime('%Y-%m-%d %H:%M:%S'), str(e)))
+                continue
+        else:
+            continue
+    logger.info('{} done,  {}/{} '.format(sn, str(count), str(len(sn_list))))
+ 
+        
+=======
+__author__ = 'Wang Liming'
+=======
+__author__ = 'lmstack'
+>>>>>>> 0fdacae7667a378900d95748e2f53901ada95b8c
+
+# 每日指标统计函数
+# import CONFIGURE.PathSetting as PathSetting
+# import sys
+# sys.path.append(PathSetting.backend_path)
+# sys.path.append(PathSetting.middle_path)
+from LIB.BACKEND import DBManager, Log, DataPreProcess
+from LIB.MIDDLE import IndexStaByPeriod, IndexStaBOneCycle
+from LIB.BACKEND import DBManager
+from LIB.BACKEND import DBManager
+from LIB.BACKEND import DBManager
+from LIB.BACKEND import DBManager
+
+import importlib
+import datetime
+import os
+import pandas as pd
+import time
+importlib.reload(IndexStaByPeriod)
+
+dbManager = DBManager.DBManager()
+dataPrePro = DataPreProcess.DataPreProcess()
+indexPerSta = IndexStaByPeriod.IndexStaByPeriod()
+
+# log 文件配置
+myLog = Log.Mylog('day_sta')
+myLog.set_file_hl(file_name=r'D:\Platform\platform\FRONTEND\day_sta\day_sta.log', log_level='info')
+myLog.set_stream_hl(log_level='info')
+logger = myLog.get_logger()
+logger.info(str(os.getpid()))
+
+# sn文件读取
+sn_list = list(pd.read_excel('D:\Platform\platform\苏州电池列表.xlsx')['sn'])
+sn_list.extend(list(pd.read_excel('D:\Platform\platform\骑享北京6040电池包统计更新20210407.xlsx')['SN号']))
+
+sn = sn_list[0]
+
+# 字段设置及结果文件生成
+columns = ['sn', 'time',  'sumDriveTime', 'sumDriveSoc', 'sumDriveAh', 'sumDriveEnergy']
+st = datetime.datetime.strptime('00:00:00', '%H:%M:%S')
+for i in range(96):
+    et = st + datetime.timedelta(minutes=15)
+    columns.append(st.strftime('%H:%M') + '-' + et.strftime('%H:%M'))
+    st = et
+result_path = r'D:\Platform\platform\FRONTEND\day_sta\result.csv'
+df_res = pd.DataFrame(columns=columns)
+if not os.path.exists(result_path):
+    df_res.to_csv(result_path, index=False)
+    
+# 时间范围设置
+start_time = '{} 00:00:00'.format('2020-01-01')
+end_time = '{} 00:00:00'.format('2021-06-01')
+sta_days = (datetime.datetime.strptime(end_time, '%Y-%m-%d %H:%M:%S') - datetime.datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S')).days
+
+count= 0
+sn_result = {}
+for sn in sn_list[:]:
+    count += 1
+    logger.info('{} start,  {}/{} '.format(sn, str(count), str(len(sn_list))))
+    if sn[2:5] == '500':
+        cap = 40
+    elif sn[2:5] == '504':
+        cap = 55
+    else:
+        logger.info('{} cap error'.format(sn))
+        cap = None
+        continue
+        
+    sn_result.update({'sn':sn})
+    logger.info('{} :{} to {} start'.format(sn, str(start_time), str(end_time)))
+    
+    # 获取数据
+    df_bms, df_gps = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, gps_switch=True, mode=0)
+    if df_bms.empty:
+        continue
+    # 数据预处理
+    
+    # 时间完全相同的数据仅保留一行
+    df_bms_pro, df_gps_pro = dataPrePro.time_filter(df_bms, df_gps)
+    
+    # bms数据按照电流和状态分段, 然后在状态分段内部,根据时间跳变继续分段(解决段内数据丢失)
+    df_bms_pro = dataPrePro.data_split_by_status(df_bms_pro)
+    df_bms_pro = dataPrePro.data_split_by_time(df_bms_pro)
+
+    # bms数据将两次充电间的状态合并
+    df_bms_pro = dataPrePro.combine_drive_stand(df_bms_pro)
+    # bms 数据计算行车和充电开始前后的静置时间
+    df_bms_pro = dataPrePro.cal_stand_time(df_bms_pro)
+    # gps 数据可靠性判断, 并增加里程和速度至gps数据(根据未合并的数据段判断)
+    df_bms_pro, df_gps_pro, res_record= dataPrePro.gps_data_judge(df_bms_pro, df_gps_pro)
+    # gps 数据可靠性判断, 并增加里程和速度至gps数据(根据已合并的数据段判断)
+    df_bms_pro, df_gps_pro, res_record= dataPrePro.data_gps_judge_after_combine(df_bms_pro, df_gps_pro)
+    
+    for sta_day in range(sta_days):
+        try:
+            st_ = datetime.datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S') + datetime.timedelta(days=sta_day)
+            et_ =datetime.datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S') + datetime.timedelta(days=sta_day+1)
+
+        # 按天统计指标
+            sn_result.update({'time':st_.strftime('%Y-%m-%d')})
+
+            df_bms_period = df_bms_pro[(df_bms_pro['时间戳'] > st_.strftime('%Y-%m-%d %H:%M:%S')) & (df_bms_pro['时间戳'] <= et_.strftime('%Y-%m-%d %H:%M:%S'))]
+            #df_gps_period = df_gps_pro[(df_gps_pro['时间戳'] > st_.strftime('%Y-%m-%d %H:%M:%S')) & (df_gps_pro['时间戳'] <= et_.strftime('%Y-%m-%d %H:%M:%S'))]
+            sn_result.update({'sumDriveTime':[indexPerSta.drive_time_sta(df_bms_period)]})
+            sn_result.update({'sumDriveSoc':[indexPerSta.drive_soc_sta(df_bms_period)]})
+            sn_result.update({'sumDriveAh':[indexPerSta.drive_capacity_sta(cap, df_bms_period)]})
+            sn_result.update({'sumDriveEnergy':[indexPerSta.drive_energy_sta(cap, df_bms_period)]})
+
+            # 每天间隔15分钟 统计一次
+            for i in range(96):
+                cur_result = []
+                st__ = st_ + datetime.timedelta(minutes=15 * i)
+                et__ = st_ + datetime.timedelta(minutes=15 * (i+1))
+                df_bms_period = df_bms_pro[(df_bms_pro['时间戳'] > st__.strftime('%Y-%m-%d %H:%M:%S')) & (df_bms_pro['时间戳'] <= et__.strftime('%Y-%m-%d %H:%M:%S'))]
+                #df_gps_period = df_gps_pro[(df_gps_pro['时间戳'] > st__.strftime('%Y-%m-%d %H:%M:%S')) & (df_gps_pro['时间戳'] <= et__.strftime('%Y-%m-%d %H:%M:%S'))]
+                cur_result.append(indexPerSta.drive_time_sta(df_bms_period))
+                cur_result.append(indexPerSta.drive_soc_sta(df_bms_period))
+                cur_result.append(indexPerSta.drive_capacity_sta(cap, df_bms_period))
+                cur_result.append(indexPerSta.drive_energy_sta(cap, df_bms_period))
+                key = st__.strftime('%H:%M') + '-' + et__.strftime('%H:%M')
+                sn_result.update({key:[cur_result]})
+            df_cur_res = pd.DataFrame(sn_result)
+            df_cur_res = df_cur_res[columns]
+
+            # 防止写入结果时,结果文件被打开
+            write_flag = False
+            while not write_flag:
+                try:
+                    df_cur_res.to_csv(result_path, mode='a+', index=False,  header=False)
+                except PermissionError as e:
+                    logger.info('{} error:{}'.format(sn, str(e)))
+                    time.sleep(10)
+                    continue
+                else:
+                    write_flag = True 
+        except Exception as e:
+                logger.info('{} {}_{} error: {}'.format(sn, st_.strftime('%Y-%m-%d %H:%M:%S'), et_.strftime('%Y-%m-%d %H:%M:%S'), str(e)))
+                continue
+        else:
+            continue
+    logger.info('{} done,  {}/{} '.format(sn, str(count), str(len(sn_list))))
+ 
+        
+>>>>>>> 65a87ae16013552e359df047df19f46fc4e6eb08

+ 157 - 0
LIB/FRONTEND/deltsoc/LFPDeltSoc20210804.py

@@ -0,0 +1,157 @@
+# 获取数据
+from LIB.BACKEND import DBManager
+
+import os
+import pandas as pd
+import numpy as np
+import datetime
+# import matplotlib.pyplot as plt
+#参数初始化
+Capacity = 53.6
+PackFullChrgVolt=69.99
+CellFullChrgVolt=3.6
+CellVoltNums=20
+CellTempNums=4
+FullChrgSoc=98
+PeakSoc=57
+
+#获取数据时间段
+def cal_deltsoc(sn, end_time, start_time):
+    end_time = end_time
+    strat_time = start_time
+    SNnum=str(sn)
+
+    sn = sn
+    st = strat_time
+    et = end_time
+
+
+    dbManager = DBManager.DBManager()
+    df_data = dbManager.get_data(sn=sn, start_time=st, end_time=et, data_groups=['bms'])
+    df_bms = df_data['bms']
+    # 计算电芯Soc差
+    packcrnt = df_bms['总电流[A]']
+    packvolt = df_bms['总电压[V]']
+    SOC = df_bms['SOC[%]']
+    SOH = df_bms['SOH[%]']
+    bmsstat = (df_bms['充电状态']).astype(int)
+    time = pd.to_datetime(df_bms['时间戳'], format='%Y-%m-%d %H:%M:%S')
+
+    # 筛选充电数据
+    ChgStart = []
+    ChgEnd = []
+
+    for i in range(3, len(time) - 3):
+        if i==3 and bmsstat[i]==2 and bmsstat[i+1]==2 and bmsstat[i+2]==2:
+            ChgStart.append(i)
+        elif bmsstat[i-2]!=2 and bmsstat[i-1]!=2 and bmsstat[i]==2:
+            ChgStart.append(i)
+        elif bmsstat[i-1]==2 and bmsstat[i]!=2 and bmsstat[i+1]!=2:
+            ChgEnd.append(i)
+        elif i == (len(time) - 4) and bmsstat[len(bmsstat)-1] == 2 and bmsstat[len(bmsstat)-2] == 2:
+            ChgEnd.append(len(time)-1)
+
+    # 筛选充电起始Soc<46%,电芯温度>15℃的数据
+    if ChgStart:
+        ChgStartValid = []
+        ChgEndValid = []
+
+        for i in range(min(len(ChgStart),len(ChgEnd))):
+            # 获取最小温度值
+            celltemp = []
+            for j in range(1, CellTempNums + 1):
+                s = str(j)
+                temp = df_bms['单体温度' + s]
+                celltemp.append(temp[ChgEnd[i]])
+
+            if SOC[ChgStart[i]] < 46 and SOC[ChgEnd[i]]>80 and min(celltemp) > 10:
+                if ((time[ChgEnd[i]]-time[ChgStart[i]]).total_seconds())/(ChgEnd[i]-ChgStart[i])<30:
+                    ChgStartValid.append(ChgStart[i])
+                    ChgEndValid.append(ChgEnd[i])
+
+    # 计算充电每个单体到达DVDQ峰值的Ah差
+    # 定义滑动平均滤波函数
+    def np_move_avg(a, n, mode="same"):
+        return (np.convolve(a, np.ones((n,)) / n, mode=mode))
+
+
+    # 定义函数:切片Soc>50且Soc<80,并寻找峰值返回峰值点的时间
+    def data_search(t, soc, cellvolt1, packcrnt1):
+        cellvolt2 = np_move_avg(cellvolt1, 5, mode="same")
+        Soc = 0
+        Ah = 0
+        Volt = [cellvolt2[0]]
+        DV_Volt = []
+        DQ_Ah = []
+        DVDQ = []
+        time1 = []
+        soc1 = []
+        soc2 = []
+
+        for m in range(1, len(t)):
+            Step = (t[m] - t[m - 1]).total_seconds()
+            Soc = Soc - packcrnt1[m] * Step * 100 / (3600 * Capacity)
+            Ah = Ah - packcrnt1[m] * Step / 3600
+            if (cellvolt2[m] - Volt[-1]) > 0.9 and Ah>0:
+                DQ_Ah.append(Ah)
+                Volt.append(cellvolt2[m])
+                DV_Volt.append(Volt[-1] - Volt[-2])
+                DVDQ.append((DV_Volt[-1]) / DQ_Ah[-1])
+                Ah = 0
+                time1.append(t[m])
+                soc1.append(Soc)
+                soc2.append(soc[m])
+        df_Data1 = pd.DataFrame({'Time': time1,
+                                'SOC': soc2,
+                                'DVDQ': DVDQ,
+                                'AhSOC': soc1})
+
+        df_Data1 = df_Data1[(df_Data1['SOC'] > 50) & (df_Data1['SOC'] < 75)]
+        # 寻找峰值点,且峰值点个数>3
+        if len(df_Data1['DVDQ'])>2:
+            PeakIndex = df_Data1['DVDQ'].idxmax()
+            df_Data2 = df_Data1[
+                (df_Data1['SOC'] > (df_Data1['SOC'][PeakIndex] - 0.5)) & (df_Data1['SOC'] < (df_Data1['SOC'][PeakIndex] + 0.5))]
+            if len(df_Data2) > 3:
+                return df_Data1['AhSOC'][PeakIndex]
+            else:
+                df_Data1 = df_Data1.drop([PeakIndex])
+                PeakIndex = df_Data1['DVDQ'].idxmax()
+                return df_Data1['AhSOC'][PeakIndex]
+
+
+    # 计算最大最小Soc差
+    if ChgStartValid:
+        DetaSoc2 = []
+        DetaSoc=[]
+        DetaSoc_SN=[]
+        DetaSoc_time=[]
+        for i in range(len(ChgStartValid)):
+            DetaSoc1 = []
+            for j in range(1, CellVoltNums + 1):
+                s = str(j)
+                cellvolt = df_bms['单体电压' + s]
+                cellvolt = list(cellvolt[ChgStartValid[i]:ChgEndValid[i]])
+                Time = list(time[ChgStartValid[i]:ChgEndValid[i]])
+                Packcrnt = list(packcrnt[ChgStartValid[i]:ChgEndValid[i]])
+                SOC1 = list(SOC[ChgStartValid[i]:ChgEndValid[i]])
+                a = data_search(Time, SOC1, cellvolt, Packcrnt)
+                if a:
+                    DetaSoc1.append(a)  # 计算到达峰值点的累计Soc
+            if DetaSoc1:
+                DetaSoc2.append(max(DetaSoc1) - min(DetaSoc1))
+
+        DetaSocMean = np.mean(DetaSoc2)
+        DetaSoc.append(DetaSocMean)
+        DetaSoc_SN.append(SNnum)
+        DetaSoc_time.append(time[ChgStartValid[-1]])
+
+
+        result_DetaSoc={'time':DetaSoc_time,
+                        'SN号':DetaSoc_SN,
+                        'Soc差':DetaSoc}
+        Result_DetaSoc=pd.DataFrame(result_DetaSoc)
+        return Result_DetaSoc
+    return pd.DataFrame()
+
+

+ 4 - 0
LIB/FRONTEND/deltsoc/detaSOC表头及数据类型.xlsx

@@ -0,0 +1,4 @@
+±íÍ·	Ãû³Æ	Êý¾ÝÀàÐÍ
+time	time	timestamps
+SNºÅ	sn	str
+Soc²î	deta_soc	float64

+ 27 - 0
LIB/FRONTEND/deltsoc/main.py

@@ -0,0 +1,27 @@
+#coding=utf-8
+import os
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+from LIB.MIDDLE import SignalMonitor
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+import time, datetime
+import traceback
+from LIB.MIDDLE.DeltSoc import LFPDeltSoc20210804 as LFPDeltSoc
+
+from urllib import parse
+
+dbManager = DBManager.DBManager()
+if __name__ == "__main__":
+    SNdata_6060 = pd.read_excel('骑享资产梳理-20210621.xlsx', sheet_name='6060')
+    SNnums_6060=SNdata_6060['SN号']
+    now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+    now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+    start_time=now_time-datetime.timedelta(days=31)
+    end_time=str(now_time)
+    start_time=str(start_time)
+
+    for sn in SNnums_6060.tolist():
+        res = LFPDeltSoc.cal_deltsoc(sn, end_time, start_time)
+        res.to_csv('BMS_DetaSoc_'+sn+'.csv',encoding='GB18030')

BIN
LIB/FRONTEND/deltsoc/骑享资产梳理-20210621.xlsx


+ 127 - 0
LIB/FRONTEND/odo/CalDist.py

@@ -0,0 +1,127 @@
+from math import radians, cos, sin, asin, sqrt
+import pandas as pd
+import numpy as np
+from datetime import datetime
+from datetime import timedelta
+
+from GpsRank import *
+from ProcessDfBms import *
+from ProcessDfGps import *
+
+from LIB.BACKEND import DBManager
+
+import DBManager
+#####################################配置环境分割线#################################################
+
+def GetDistInfo(input_sn,input_starttime,input_endtime):
+
+    #####################################配置参数分割线#################################################
+    dbManager = DBManager.DBManager()
+    data_raw = dbManager.get_data(sn=input_sn, start_time=input_starttime, 
+        end_time=input_endtime)
+    #拆包预处理
+    df_bms_raw=data_raw['bms']
+    df_gps_raw=data_raw['gps']
+    df_bms=preprocess_Df_Bms(df_bms_raw)
+    df_gps=preprocess_Df_Gps(df_gps_raw)
+    
+    #####################################数据预处理分割线#################################################
+
+    # mode: 0:正常取数; 1:7255 取数
+    if input_sn[0:2] == 'UD' or input_sn[0:2] == 'MG':
+        mode = 1
+    else:
+        mode = 0
+    #获取状态表,mode默认为0,mode=1放电时电流为负,mode=0充电时电流为正
+
+    df_bms_drive_timetable=get_bms_drive_timetable(df_bms,mode)
+    df_gps_drive_cycle_accum=pd.DataFrame()
+    if len(df_bms_drive_timetable)>0:
+        for index in range(len(df_bms_drive_timetable)):
+            #筛选drivecycle数据
+            drive_start_time=df_bms_drive_timetable.loc[index,'drive_start_time']#开始时间
+            drive_end_time=df_bms_drive_timetable.loc[index,'drive_end_time']#结束时间
+
+            time_condition=(df_gps['time']>drive_start_time)&(df_gps['time']<drive_end_time)#时间判断条件
+            df_gps_drive_cycle=df_gps.loc[time_condition,:].copy()
+            df_gps_drive_cycle=df_gps_drive_cycle.reset_index(drop=True)#重置index
+            #计算drivecycle GPS累计里程,存入表格
+            condition_a=df_gps_drive_cycle['deltatime']>60*3
+            condition_b=(df_gps_drive_cycle['deltatime']>90*1)&(df_gps_drive_cycle['distance']>1000)
+            drive_cycle_dist_array=df_gps_drive_cycle.loc[~(condition_a|condition_b),'distance'].values
+            drive_cycle_dist_array=drive_cycle_dist_array[np.where((drive_cycle_dist_array>=1)&(drive_cycle_dist_array<1000))[0]]
+            gps_dist=drive_cycle_dist_array.sum()
+            df_bms_drive_timetable.loc[index,'gps_dist']=gps_dist#得到GPS路径
+            #计算头-尾的空缺时间段对应的预估SOC
+            if len(df_gps_drive_cycle)>2:
+                gps_starttime=df_gps_drive_cycle.loc[1,'time']#gps开始时间
+                gps_endtime=df_gps_drive_cycle.loc[df_gps_drive_cycle.index[-1],'time']#gps结束时间
+                #从drive_start_time到gps开始时间,使用SOC计算的里程
+                #gps结束时间到drive_end_time,使用SOC计算的里程
+                unrecorded_odo_head=cal_deltasoc(df_bms,drive_start_time,gps_starttime)
+                unrecorded_odo_tail=cal_deltasoc(df_bms,gps_endtime,drive_end_time)
+            else:
+                #计算数据丢失行unrecordeodo
+                unrecorded_odo_head=cal_deltasoc(df_bms,drive_start_time,drive_end_time)
+                unrecorded_odo_tail=0
+            #计算中间的预估SOC
+            predict_dist=cal_unrecorded_gps(df_gps_drive_cycle,df_bms)
+            #计算总的预估SOC
+            totaldist=predict_dist+unrecorded_odo_head+ unrecorded_odo_tail#得到GPS路径
+            df_bms_drive_timetable.loc[index,'predict_dist']=totaldist
+    else :
+        pass
+
+    #####################################统计行驶里程End#################################################
+    #打印输出结果#
+    index_list=list(range(len(df_bms_drive_timetable)))
+
+    dist_gps=0
+    dist_predict=0
+    day_start_time=''#当日开始时间
+    day_end_time=''#当日结束时间
+    day_start_soc=0#当日开始soc
+    day_end_soc=0#当日结束soc
+    day_min_soc=101#当日最低soc
+    drive_accum_soc=0#累计使用SOC
+
+    if len(df_bms_drive_timetable)>0:
+        #开始行
+        day_start_soc=df_bms_drive_timetable.loc[1,'drive_start_soc']#开始soc
+        day_start_time=df_bms_drive_timetable.loc[1,'drive_start_time']#开始时间
+        #结束行
+        day_end_time=df_bms_drive_timetable.loc[len(df_bms_drive_timetable)-1,'drive_end_time']#结束时间
+        day_end_soc=df_bms_drive_timetable.loc[len(df_bms_drive_timetable)-1,'drive_end_soc']#结束soc
+
+    for index in index_list:
+        '''汇总里程'''
+        dist_gps+=df_bms_drive_timetable.loc[index,'gps_dist']/1000#计算GPS里程
+        dist_predict+=df_bms_drive_timetable.loc[index,'predict_dist']#计算预估里程
+        drive_start_soc=df_bms_drive_timetable.loc[index,'drive_start_soc']#驾驶周期开始的soc
+        drive_end_soc=df_bms_drive_timetable.loc[index,'drive_end_soc']#驾驶周期结束的soc
+        day_min_soc=min(day_min_soc,drive_start_soc,drive_end_soc)
+
+        delta_soc=drive_start_soc-drive_end_soc#驾驶周期SOC变化量
+        drive_accum_soc+=abs(delta_soc)#所有drive cycle累计消耗的soc
+
+    # gps_score=get_df_gps_score(input_starttime,input_endtime,df_gps)
+    # gps_score=round(gps_score,1)
+    #计算总里程
+    dist_gps=round(dist_gps,3)
+    dist_predict=round(dist_predict,3)
+    dist_all=round(dist_gps+dist_predict,3)
+    #输出统计结果
+    # print ('为您查询到,从'+input_starttime+'到'+input_endtime+'时间段内:')
+    # print('SOC变化量:'+str(df_bms['bmspacksoc'].max()-df_bms['bmspacksoc'].min())+' %')
+    # print('行驶总里程:'+str(dist_all)+' km')
+
+    return {'SN':input_sn,'range':dist_all,'accum_soc':drive_accum_soc,'day_start_soc':day_start_soc,
+    'day_end_soc':day_end_soc,'day_start_time':day_start_time,'day_end_time':day_end_time,
+    'day_min_soc':day_min_soc}
+    # print('其中GPS信号在线时里程:'+str(dist_gps)+' km')
+    # print('其中GPS信号掉线时预估里程:'+str(dist_predict)+' km')
+    # print('GPS信号质量评分为:'+str(gps_score),'分\n')
+
+    #####################################打印结果End#################################################
+
+

+ 68 - 0
LIB/FRONTEND/odo/CalDist_Batch.py

@@ -0,0 +1,68 @@
+from math import radians, cos, sin, asin, sqrt
+import pandas as pd
+import numpy as np
+from datetime import datetime
+from datetime import timedelta
+
+from GpsRank import *
+from ProcessDfBms import *
+from ProcessDfGps import *
+from CalDist import *
+from LIB.BACKEND import DBManager
+import pdb
+
+asset_table_path='D:\\work\\Qixiang\\data_analyze_platform\\pengmin\\AllCarDist\\asset_table.xlsx'
+drive_info_path='D:\\work\\Qixiang\\data_analyze_platform\\pengmin\\AllCarDist\\drive_info.xlsx'
+asset_sheet_num=1
+usecols_list=[4,5]
+
+asset_table=pd.read_excel(asset_table_path,sheet_name=asset_sheet_num,skiprows=1,usecols=usecols_list)
+SN_list=asset_table['SN号'].values.tolist()
+print('从6060sheet读取到:'+str(len(SN_list))+'行')
+asset_table=asset_table.rename(columns={'SN号':'SN','状态':'state'})
+
+asset_table.set_index(["SN"],inplace=True)
+col_name=asset_table.columns.tolist()
+col_name.extend(['range','accum_soc','day_start_soc','day_end_soc','day_start_time','day_end_time'])
+asset_table=asset_table.reindex(columns=col_name)
+
+start_hour='00:00:00'#每日查询最早时间
+end_hour='23:59:00'#每日查询最晚时间
+
+
+date_index=pd.date_range('2021-07-31','2021-07-31')
+for date in date_index:
+    '''遍历日期'''
+
+    str_date=str(date)[:10]
+    input_starttime=str_date+' '+start_hour
+    input_endtime=str_date+' '+end_hour
+    test_day=str_date[5:10]#月-日,用于建立sheet
+    drive_info_path='D:\\work\\Qixiang\\data_analyze_platform\\pengmin\\AllCarDist\\6060\\drive_info'+test_day+'_50_end_'+'.xlsx'
+
+    print(input_starttime)
+
+    drive_info_aday=pd.DataFrame()
+    SN_list_short=SN_list#先选择了0:50,50:end
+
+    for SN in SN_list_short:
+        '''遍历SN号'''
+        SN=SN.strip('\t')
+        SN=SN.strip('\n')
+
+        try:
+            range=GetDistInfo(SN,input_starttime,input_endtime)
+            range_df=pd.DataFrame([range])
+            drive_info_aday=pd.concat([drive_info_aday,range_df],axis=0)
+
+        except:
+            print(SN+' '+test_day+'fail')
+        else:
+            pass
+            #print(SN+' '+test_day+'success')
+
+    drive_info_aday.to_excel(drive_info_path,sheet_name=test_day)#sheet名称为testday
+    
+    
+
+

+ 77 - 0
LIB/FRONTEND/odo/GpsRank.py

@@ -0,0 +1,77 @@
+import pandas as pd
+import numpy as np
+from datetime import datetime
+from datetime import timedelta
+
+def cal_gps_score(df):
+    '''在获取信号,优、良、合格、掉线的比例之后,计算gps的总评分'''
+    score=0
+    for index in range(len(df)):
+        time_percent=df.loc[index,'累计时间占比']
+        if df.loc[index,'GPS质量']=='优':
+            score+=time_percent*0
+        elif df.loc[index,'GPS质量']=='良':
+            score+=time_percent*0.3
+        elif df.loc[index,'GPS质量']=='合格':
+            score+=time_percent*0.5
+        elif df.loc[index,'GPS质量']=='掉线':
+            score+=time_percent*1
+    return (1-score)*100
+
+def gps_rank(df_gps_signal_table,df_gps,signal_rank,dist_factor):
+    '''gps信号质量分析函数,需要输入表格,df_gps,信号等级,权重'''
+    gps_signal_condition=(df_gps['gps_signal']==signal_rank)
+    dist=df_gps.loc[gps_signal_condition,'distance'].values.sum()
+    deltatime=df_gps.loc[gps_signal_condition,'deltatime'].values.sum()
+    df_gps_signal_table_condition=(df_gps_signal_table['gps_signal']==signal_rank)
+    df_gps_signal_table.loc[df_gps_signal_table_condition,'accum_distance']=dist/1000
+    df_gps_signal_table.loc[df_gps_signal_table_condition,'accum_deltatime']=deltatime
+    df_gps_signal_table.loc[df_gps_signal_table_condition,'accum_distance_factor']=dist/1000*dist_factor
+    return df_gps_signal_table
+
+def get_df_gps_score(starttime,endtime,df_gps):
+    '''对df_gps中的gps质量进行评分,返回一个数值'''
+    test_start_time=starttime#'2021-05-29 17:16:39'
+    test_end_time=endtime#'2021-05-29 20:08:08'
+
+    test_time_condition=(df_gps['time']>test_start_time)&(df_gps['time']<test_end_time)
+    df_gps_test=df_gps.loc[test_time_condition,:].copy()
+    df_gps_test=df_gps_test.reset_index(drop=True)#重置index
+    #按照deltatime打标签
+    gps_deltatime_bins=[0,30,60,120,10000]#优-良-合格-掉线
+    name=['优','良','合格','掉线']
+    df_gps_test['gps_signal']=pd.cut(df_gps_test['deltatime'], gps_deltatime_bins,labels=name)
+    df_gps_test['gps_signal'].value_counts()
+    #声明一个gps信号按类别统计table
+    df_gps_signal_table=pd.DataFrame()
+    df_gps_signal_table['gps_signal']=df_gps_test['gps_signal'].value_counts().index.tolist()
+    df_gps_signal_table['num']=df_gps_test['gps_signal'].value_counts().values.tolist()
+
+    #分类进行统计
+    df_gps_signal_table=gps_rank(df_gps_signal_table,df_gps_test,'优',1.00)
+    df_gps_signal_table=gps_rank(df_gps_signal_table,df_gps_test,'良',1.05)
+    df_gps_signal_table=gps_rank(df_gps_signal_table,df_gps_test,'合格',1.2)
+    df_gps_signal_table=gps_rank(df_gps_signal_table,df_gps_test,'掉线',1)
+
+    #次数占比,时间占比
+    all_num=df_gps_signal_table['num'].sum()
+    df_gps_signal_table['num_percent']=df_gps_signal_table['num']/all_num
+    all_accum_deltatime=df_gps_signal_table['accum_deltatime'].sum()
+    df_gps_signal_table['accum_deltatime_percent']=df_gps_signal_table['accum_deltatime']/all_accum_deltatime
+
+    #选择参数
+    df_gps_signal_table=df_gps_signal_table[['gps_signal','num','num_percent','accum_distance',
+                                            'accum_distance_factor','accum_deltatime','accum_deltatime_percent']]
+    df_gps_signal_table=df_gps_signal_table.rename(columns={'gps_signal':'GPS质量','num':'数量','num_percent':'数量占比',
+                                                        'accum_distance':'累计里程','accum_distance_factor':'累计里程修正值',
+                                                        'accum_deltatime':'累计时间','accum_deltatime_percent':'累计时间占比'})
+
+    df_gps_signal_table.loc[:,['GPS质量','累计时间','累计时间占比']]
+    gps_score=cal_gps_score(df_gps_signal_table)#调用函数计算gps评分
+    
+    #输出结果,评分
+    #print('From '+test_start_time+'  to '+test_end_time)
+    #print('GPS信号质量评分:'+str(gps_score))
+
+    return gps_score
+

+ 159 - 0
LIB/FRONTEND/odo/ProcessDfBms.py

@@ -0,0 +1,159 @@
+import pandas as pd
+import numpy as np
+from datetime import datetime
+from datetime import timedelta
+
+def get_bms_drive_timetable(df_bms,battery_mode):
+    '''对df_bms进行处理,得到行车的时间表。'''
+
+    #####################step1 先使用电流做充电状态的判断#############################################
+    if battery_mode==0:#mode=0,电流为正代表放电
+        condition_chrg=df_bms['bmspackcrnt']<0##根据电流,挑选充电状态
+        df_bms.loc[condition_chrg,'bscsta']='chrg'
+        condition_drive=df_bms['bmspackcrnt']>0.01##根据电流,挑选行驶状态
+        df_bms.loc[condition_drive,'bscsta']='drive'
+        df_bms.loc[~(condition_drive|condition_chrg),'bscsta']='idle'#静置状态
+    else :#mode=1,电流为负代表放电
+        condition_chrg=df_bms['bmspackcrnt']>0##根据电流,挑选充电状态
+        df_bms.loc[condition_chrg,'bscsta']='chrg'
+        condition_drive=df_bms['bmspackcrnt']<-0.01##根据电流,挑选行驶状态
+        df_bms.loc[condition_drive,'bscsta']='drive'
+        df_bms.loc[~(condition_drive|condition_chrg),'bscsta']='idle'#静置状态
+
+    #####################step2 对drive进行debounce,进入时立即进入,退出时debounce,5分钟。##########
+    index=0
+    debounce_row=10#debounce判断持续10行
+    debounce_time=300#debounce判断持续300秒
+    #对上一步初步状态进行二次处理
+    while index<(len(df_bms)-debounce_row):
+        mode_0=df_bms.loc[index,'bscsta']
+        mode_1=df_bms.loc[index+1,'bscsta']
+        #如果发现了边界行,则进行debounce判断
+        if (mode_0=='drive')&(mode_1!='drive'):#如果从drive变为idle
+            accum_subtime=0#累计时间初始化
+
+            for sub_index in range(debounce_row):#往下处理10行
+                sub_time=df_bms.loc[index+sub_index,'deltatime']
+                accum_subtime+=sub_time
+                #如果累计时间不到300秒,则设置为drive
+                if accum_subtime<debounce_time:
+                    df_bms.loc[index+sub_index,'bscsta']='drive'
+            index=index+debounce_row#处理10行以后的数据
+        #如果从idle变为drivemode,则将idle变为drive,包容前一行
+        elif (mode_0!='drive')&(mode_1=='drive'): 
+            df_bms.loc[index,'bscsta']='drive'
+            index=index+1
+        else:
+            index=index+1
+
+
+    #######################step3 对drivemode的时间进行分段###########################################
+    not_drive_flg=0#初始化
+    #输出drivemode的时间段,包含开始时间、结束时间
+    df_bms_drive_timetable_index=0
+    df_bms_drive_timetable=pd.DataFrame([],columns={'drive_start_time','drive_end_time',
+                                                    'gps_dist','predict_dist','drive_start_soc','drive_end_soc'})
+    for index in range(len(df_bms)):
+        temp_bscsta=df_bms.loc[index,'bscsta']
+        
+        if (temp_bscsta=='drive')&(not_drive_flg==0):
+            drive_start_time=df_bms.loc[index,'time']
+            not_drive_flg=1
+            df_bms_drive_timetable.loc[df_bms_drive_timetable_index,'drive_start_time']=drive_start_time
+            #startsoc
+            drive_start_soc=df_bms.loc[index,'bmspacksoc']
+            df_bms_drive_timetable.loc[df_bms_drive_timetable_index,'drive_start_soc']=drive_start_soc
+
+        elif (temp_bscsta!='drive')&(not_drive_flg==1):
+            drive_end_time=df_bms.loc[index,'time']
+            not_drive_flg=0
+            df_bms_drive_timetable.loc[df_bms_drive_timetable_index,'drive_end_time']=drive_end_time
+            #endsoc
+            drive_end_soc=df_bms.loc[index,'bmspacksoc']
+            df_bms_drive_timetable.loc[df_bms_drive_timetable_index,'drive_end_soc']=drive_end_soc
+            df_bms_drive_timetable_index+=1#index++
+
+    #删除时间信息不齐全的行
+    df_bms_drive_timetable=df_bms_drive_timetable.dropna(subset=['drive_end_time','drive_start_time'])
+    
+    return df_bms_drive_timetable
+
+
+def read_df_bms(path):
+    '''从路径中读取df_bms,进行预处理'''
+    df_bms=pd.read_csv(path, encoding='gbk')#编码方式gbk
+    #筛选表头,重命名
+    bms_columns=['时间戳','总电流[A]','总电压[V]','SOC[%]']
+    df_bms=df_bms.loc[:,bms_columns].copy()
+    df_bms.rename(columns = {"时间戳": "time", "总电流[A]": "bmspackcrnt", 
+                             "总电压[V]": "bmspackvol", "SOC[%]": "bmspacksoc"},inplace=True)#表头替换
+    #时间格式调整
+    df_bms['time']=df_bms['time'].apply(lambda x:datetime.strptime(x,'%Y-%m-%d %H:%M:%S'))#时间格式调整
+    #进行预处理
+    df_bms=df_add_deltatime(df_bms)#增加deltatime列 
+    return df_bms
+
+def preprocess_Df_Bms(df_bms):
+    '''对获得的df_bms,进行预处理'''
+    #筛选表头,重命名
+    bms_columns=['时间戳','总电流[A]','总电压[V]','SOC[%]']
+    df_bms=df_bms.loc[:,bms_columns].copy()
+    df_bms.rename(columns = {"时间戳": "time", "总电流[A]": "bmspackcrnt", 
+                             "总电压[V]": "bmspackvol", "SOC[%]": "bmspacksoc"},inplace=True)#表头替换
+    #删除空行
+    df_bms=df_bms.dropna(subset=['time'])
+    #删除时间重复的行,保留第一次出现的行
+    df_bms=df_bms.drop_duplicates(subset=['time'],keep='first')
+    #时间格式调整
+    df_bms['time']=df_bms['time'].apply(lambda x:datetime.strptime(x,'%Y-%m-%d %H:%M:%S'))#时间格式调整
+    #进行预处理
+    df_bms=df_add_deltatime(df_bms)#增加deltatime列 
+    return df_bms
+
+
+def df_add_deltatime(df_in):
+    '''Add a columns:deltatime,input df must have time column.'''
+    for i in range(len(df_in)):
+        #首行默认为0
+        if i==0:
+            df_in.loc[i,'deltatime']=0
+        #从第二行开始,计算i行到i-1行,GPS距离之差
+        else:
+            time1=df_in.loc[i-1,'time']
+            time2=df_in.loc[i,'time']
+            deltatime=time_interval(time1,time2)#计算时间差,返回单位为秒。
+            df_in.loc[i,'deltatime']=deltatime
+    return df_in
+
+
+def time_interval(time1,time2):
+    """
+    Calculate the time interval between two times,
+    return the seconds
+    """
+    deltatime=time2-time1
+    return deltatime.seconds
+
+
+def cal_deltasoc(df_bms,start_time,end_time):
+    '''输入开始时间和结束时间,返回deltasoc,此处将deltasoc*1既等效为unrecorded_odo.'''
+    time_condition=(df_bms['time']>start_time)&(df_bms['time']<end_time)
+    df_bms_sub=df_bms.loc[time_condition,:].copy()
+    if len(df_bms_sub)>=2:
+        
+        df_bms_head=df_bms_sub.head(1).copy()#首行
+        df_bms_startsoc=df_bms_head['bmspacksoc'].values[0]
+        df_bms_tail=df_bms_sub.tail(1).copy()#尾行
+        df_bms_endsoc=df_bms_tail['bmspacksoc'].values[0]
+        delta_soc=df_bms_startsoc-df_bms_endsoc
+        
+        if delta_soc>0:
+            #如果df_bms出现时间不连续,则先计算deltasoc,deltasoc每变化1,续驶里程增加1,
+            unrecorded_odo=delta_soc*1
+            #print('From '+str(start_time)+' to  '+str(end_time)+' soc decrease:  '+str(delta_soc))
+        else:
+            unrecorded_odo=0#如果deltasoc不大于0,说明在充电,或者静置不动    
+    #如果行数少于2,无法计算
+    else:
+        unrecorded_odo=0
+    return unrecorded_odo

+ 139 - 0
LIB/FRONTEND/odo/ProcessDfGps.py

@@ -0,0 +1,139 @@
+import pandas as pd
+import numpy as np
+from datetime import datetime
+from datetime import timedelta
+from ProcessDfBms import *
+from math import radians, cos, sin, asin, sqrt
+
+def cal_unrecorded_gps(df_in,df_bms):
+    '''筛选出现gps时间断点的数据,用df_bms数据补齐,df_in为df_gps表格。'''
+    #未记录到的odo总和
+    accum_unrecorded_odo=0
+
+    #设置丢失的判断条件,获得信息丢失行的index
+    condition1=df_in['deltatime']>60*3#时间间隔大于3分钟。说明数据掉线了。
+    condition2=(df_in['deltatime']>90*1)&(df_in['distance']>1000)#时间间隔大于*分钟,且Distance间隔大于*,代表掉线了。
+    signal_start_list=df_in.loc[condition1|condition2,:].index.to_list()#信息丢失行
+    #如果第0行属于信息丢失行,则删除,因为需要index-1行
+    try:
+        signal_start_list.remove(0)
+    except:
+        pass
+    else:
+        pass
+    #筛选出所有GPS信号丢失,对应的开始时间-结束时间对。
+    if len(signal_start_list)>0:
+        signal_end_list=[num-1 for num in signal_start_list]#信息丢失行的前一行,此处可能如果是首行,可能会有bug。
+        pick_gps_list=[0]+signal_start_list+signal_end_list+[len(df_in)-1]#首行+尾行+信号开始行+信号结束行
+        pick_gps_list=sorted(pick_gps_list)#重新排序
+
+    #有出现信号断点的行,则进行以下计算。
+    if len(signal_start_list)>0:
+        #针对每个时间对,计算unrecorded odo
+        for start_time_index,end_time_index in zip(signal_start_list,signal_end_list):
+            last_end_time=df_in.loc[end_time_index,'time']
+            this_start_time=df_in.loc[start_time_index,'time']
+            #print('gps signal loss from: '+str(last_end_time)+'-to-'+str(this_start_time))
+            #使用cal_delatasoc计算预估里程
+            unrecorded_odo=cal_deltasoc(df_bms,last_end_time,this_start_time)
+            accum_unrecorded_odo+=unrecorded_odo
+        #print('accum_unrecorded_odo:'+str(accum_unrecorded_odo))
+    else:
+        pass
+    
+    return accum_unrecorded_odo
+
+
+def df_add_avgspeed(df_in):
+    '''Add a columns:avgspeed ,input df must have deltatime,distance column.'''
+    for i in range(len(df_in)):
+        #首行默认为0
+        if i==0:
+            df_in.loc[i,'avgspeed']=0
+        #从第二行开始,计算平均速度
+        else:
+            deltatime=df_in.loc[i,'deltatime']
+            distance=df_in.loc[i,'distance']
+            avgspeed=(distance/1000)/(deltatime/3600)
+            df_in.loc[i,'avgspeed']=avgspeed
+    return df_in
+
+
+def read_df_gps(path):
+    df_gps=pd.read_csv(path, encoding='gbk')#编码方式gbk
+    #重置表头
+    df_gps.rename(columns = {"时间戳": "time", "纬度":"lat", "经度":"lng", 
+                             "卫星数":"sat_num", "海拔m":"height","速度[km/h]":"speed"},  inplace=True)
+    #时间格式调整
+    df_gps['time']=pd.to_datetime(df_gps['time'])
+    #对gps进行清洗
+    df_gps=df_add_distance(df_gps)#增加distance列
+    condition=df_gps['distance']<20000#删除GPS漂移过远的点,可能为GPS错误值
+    df_gps=df_gps.loc[condition,:].copy()#删除condition中,avgspd过大的部分,很可能伴随着GPS的漂移。
+    df_gps=df_gps.reset_index(drop=True)#重置index
+    #进行预处理
+    df_gps=df_add_distance(df_gps)#增加distance列,再算一次distance
+    df_gps=df_add_deltatime(df_gps)#增加deltatime列
+    df_gps=df_add_avgspeed(df_gps)#增加avgspeed列
+
+    #df_gps.to_excel('df_gps.xlsx',sheet_name='Sheet1')
+    return df_gps
+
+def preprocess_Df_Gps(df_gps):
+    '''对Df_Gps进行预处理'''
+    #重置表头
+    df_gps.rename(columns = {"时间戳": "time", "纬度":"lat", "经度":"lng", 
+                             "卫星数":"sat_num", "海拔m":"height","速度[km/h]":"speed"},  inplace=True)
+    #删除含有空数据的行
+    df_gps=df_gps.dropna(subset=['time','lat','lng'])
+    #删除时间重复的行,保留第一次出现的行
+    df_gps=df_gps.drop_duplicates(subset=['time'],keep='first')
+    #时间格式调整
+    df_gps['time']=pd.to_datetime(df_gps['time'])
+    
+    #对gps进行清洗
+    df_gps=df_add_distance(df_gps)#增加distance列
+    condition=df_gps['distance']<20000#删除GPS漂移过远的点,可能为GPS错误值
+    df_gps=df_gps.loc[condition,:].copy()#删除condition中,avgspd过大的部分,很可能伴随着GPS的漂移。
+    df_gps=df_gps.reset_index(drop=True)#重置index
+    #进行预处理
+    df_gps=df_add_distance(df_gps)#增加distance列,再算一次distance
+    df_gps=df_add_deltatime(df_gps)#增加deltatime列
+    df_gps=df_gps.loc[df_gps['deltatime']>0.01,:].copy()#删除deltatime=0的列,两个时间戳相同,无法求速度。
+    df_gps=df_add_avgspeed(df_gps)#增加avgspeed列
+
+    #df_gps.to_excel('df_gps.xlsx',sheet_name='Sheet1')
+    return df_gps
+
+
+def df_add_distance(df_in):
+    '''Add a columns:distance,input df must have lng,lat columns.'''
+    for i in range(len(df_in)):
+        #首行默认为0
+        if i==0:
+            df_in.loc[i,'distance']=0
+        #从第二行开始,计算i行到i-1行,GPS距离之差
+        else:
+            lon1=df_in.loc[i-1,'lng']
+            lat1=df_in.loc[i-1,'lat']
+            lon2=df_in.loc[i,'lng']
+            lat2=df_in.loc[i,'lat']
+            distance=haversine(lon1,lat1,lon2,lat2)#haversine公式计算距离差
+            df_in.loc[i,'distance']=distance    
+    return df_in
+
+
+def haversine(lon1, lat1, lon2, lat2):
+    """
+    Calculate the great circle distance between two points 
+    on the earth (specified in decimal degrees)
+    """
+    # 将十进制度数转化为弧度
+    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
+    # haversine公式
+    dlon = lon2 - lon1 
+    dlat = lat2 - lat1 
+    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
+    c = 2 * asin(sqrt(a)) 
+    r = 6371 # 地球平均半径,单位为公里
+    return c * r * 1000

+ 293 - 0
LIB/FRONTEND/odo/UpdtFct.py

@@ -0,0 +1,293 @@
+import pandas as pd
+import pymysql
+from sqlalchemy import create_engine
+import datetime
+
+#建立引擎
+engine = create_engine(str(r"mysql+mysqldb://%s:" + '%s' + "@%s/%s") % ('root', 'pengmin', 'localhost', 'qixiangdb'))
+
+conn_qx = pymysql.connect(
+        host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com',
+        user='qx_cas',
+        password='Qx@123456',#Qx@123456
+        database='qx_cas',
+        charset='utf8'
+    )
+
+conn_local = pymysql.connect(
+        host='localhost',
+        user='root',
+        password='pengmin',
+        database='qixiangdb',
+        charset='utf8'
+    )
+
+def getNextSoc(start_soc):
+    '''输入当前的soc,寻找目标soc函数'''
+    if start_soc>80:
+        next_soc=80
+    elif start_soc>60:
+        next_soc=60
+    elif start_soc>40:
+        next_soc=40
+    elif start_soc>20:
+        next_soc=20
+    else:
+        next_soc=1
+    return next_soc
+
+def updtSnFct(sn_factor_df,end_soc,delta_range,range_soc):
+    '''输入当前的soc区间段,里程变量量,soc变化量,输出新的df
+    sn_factor_df为dataframe,delta_range单位为km,range_soc单位为km/persoc'''
+    if end_soc==80:
+        updtFctByCol(sn_factor_df,'a0',delta_range,range_soc)
+    elif end_soc==60:
+        updtFctByCol(sn_factor_df,'a1',delta_range,range_soc)
+    elif end_soc==40:
+        updtFctByCol(sn_factor_df,'a2',delta_range,range_soc)
+    elif end_soc==20:
+        updtFctByCol(sn_factor_df,'a3',delta_range,range_soc)
+    elif end_soc<20:
+        updtFctByCol(sn_factor_df,'a4',delta_range,range_soc)
+    return sn_factor_df
+
+def updtFctByCol(sn_factor_df,colmun_name,delta_range,range_soc):
+    '''更新制定列的factor,sn_factor_df为dataframe,新的系数更新到第一行。delta_range单位为km,
+    range_soc单位为km/persoc,默认按照100km更新续驶里程权重'''
+    range_soc_old=sn_factor_df.loc[0,colmun_name]#读取第0行的老factor
+    debounce_range=200#更新权重
+    new_factor=range_soc*((delta_range)/debounce_range)+range_soc_old*(1-(delta_range)/debounce_range)
+    #在第1行,存储新的factor
+    sn_factor_df.loc[1,colmun_name]=new_factor
+    return sn_factor_df
+
+def updtTodayFct(factor_input,sn_day_df):
+    '''更新今日的Factor***'''
+    sn_factor_df_last=factor_input
+    start_soc=sn_day_df.loc[0,'soc']
+    next_soc=getNextSoc(start_soc)
+    start_range=sn_day_df.loc[0,'vehodo']
+    sn=sn_day_df.loc[0,'name']
+
+    for index in range(len(sn_day_df)-1):
+    #寻找分割点,
+        index_soc=sn_day_df.loc[index,'soc']#当前行soc
+        next_index_soc=sn_day_df.loc[index+1,'soc']#下一行soc
+
+        if (index_soc>=next_soc)&(next_index_soc<next_soc):#当前行高,下一行低
+            delta_soc_tonext=start_soc-next_soc#两个距离点的soc差,单位为%
+            delta_range_tonext=sn_day_df.loc[index,'vehodo']-start_range#两个时间点的距离差,单位为m
+            delta_range_tonext_km=delta_range_tonext/1000#两个时间点的距离差,单位为km
+            range_soc_tonext=(delta_range_tonext/1000)/delta_soc_tonext#单位soc可行驶的公里数
+            print(sn+'start_soc: '+str(start_soc),'next_soc: '+str(next_soc),'delta_vehodo; '+str(round(delta_range_tonext_km,3))
+            +'km'+' range_soc:'+str(round(range_soc_tonext,3)))
+
+            if (delta_range_tonext_km)>1:
+                sn_factor_df_last=updtSnFct(sn_factor_df_last,next_soc,delta_range_tonext_km,range_soc_tonext)
+            
+            start_soc=next_index_soc#变更开始soc
+            next_soc=getNextSoc(start_soc)#变更结束soc
+            start_range=sn_day_df.loc[index+1,'vehodo']#变更开始里程    
+
+    return sn_factor_df_last
+
+def snDayDfPreProcess(sn_day_df):
+    '''预处理,判断是否在dirvemode,获取drivemode条件下的累计行驶距离。
+    增加delta_soc列,drive_flg列,vehodo列'''
+    sn_day_df=sn_day_df.reset_index(drop=True)#重置index
+    #增加列,计算delta_soc
+    for index in range(len(sn_day_df)):
+        if index==0:
+            sn_day_df.loc[index,'delta_soc']=0
+        else:
+            sn_day_df.loc[index,'delta_soc']=sn_day_df.loc[index,'soc']-sn_day_df.loc[index-1,'soc']
+    #增加列,判断是否在drive状态
+    drive_flg=False
+    accum_distance=0
+    for index in range(len(sn_day_df)):
+        if index==0:
+            sn_day_df.loc[index,'drive_status']=drive_flg
+            sn_day_df.loc[index,'vehodo']=0
+        else:
+            if (sn_day_df.loc[index,'delta_soc']<-0.1)|\
+                ((sn_day_df.loc[index,'delta_soc']<=0)&(sn_day_df.loc[index,'distance']>500)):#soc处于下降状态,说明在drive
+                drive_flg=True#置true
+            elif sn_day_df.loc[index,'delta_soc']>0.1:#soc处于上升状态,说明不在drive
+                drive_flg=False#置false
+                accum_distance=0#清零
+            sn_day_df.loc[index,'drive_flg']=drive_flg
+            accum_distance+=sn_day_df.loc[index,'distance']#对行驶里程进行累加
+            sn_day_df.loc[index,'vehodo']=accum_distance
+    #筛选所有的drive信息行
+    sn_day_drive_df=sn_day_df.loc[sn_day_df['drive_flg']==True,:]
+    sn_day_drive_df=sn_day_drive_df.reset_index(drop=True)#重置index
+    
+    return sn_day_drive_df 
+
+def updtAllSnFct(start_date,end_date):
+    '''计算开始时间到结束时间的,所有sn的factor'''
+    start_date_datetime=datetime.datetime.strptime(start_date,'%Y-%m-%d')#开始时间
+    end_date_datetime=datetime.datetime.strptime(end_date,'%Y-%m-%d')#开始时间
+    delta_day=(end_date_datetime-start_date_datetime).days#间隔天数
+    i=1
+    while i<=delta_day:
+        end_date=(start_date_datetime+datetime.timedelta(days=i)).strftime("%Y-%m-%d")
+        updtAllSnTodayFct(start_date,end_date)#调用函数
+        print('update all sn factor from '+start_date+" to "+end_date)
+        start_date=end_date
+        i+=1#自加
+
+def updtAllSnTodayFct(start_date,end_date):
+    ''''更新今天所有sn的factorx信息,start_date和end_date相隔一天。此处还可优化'''
+    start_date_str="'"+start_date+"'"
+    end_date_str="'"+end_date+"'"
+    sql_cmd="select * from drive_info where time between "+start_date_str+" and "+end_date_str+" and distance!=0;"
+    range_soc_df = pd.read_sql(sql_cmd, conn_qx)#使用read_sql方法查询qx数据库
+
+    #筛选出所有当日数据之后,筛选当日有更新的sn
+    today_sn_list=range_soc_df['name'].unique().tolist()#[:100]#先一次更新5个
+    #建立空的dataframe,用于承接所有更新的factor信息
+    today_sn_fct_df=pd.DataFrame([],columns=['sn','date','a0','a1','a2','a3','a4'])
+
+    for sn in today_sn_list:
+        #寻找factor_df,里面是否有sn号,如果没有sn对应信息,则新增信息。
+        sn_str="'"+sn+"'"
+        sql_cmd2="select sn,date,a0,a1,a2,a3,a4 from tb_sn_factor where date<"+start_date_str+" and sn="+sn_str
+        #此处可以限定每次查询的数量,例如不高于5行
+        factor_df=pd.read_sql(sql_cmd2, conn_local)#使用read_sql方法查询local数据库
+
+        #按照sn号和日期进行去重,避免运行时重复产生factor数据,保留第一次出现的行。
+        factor_df=factor_df.drop_duplicates(subset=['sn','date'],keep='first')
+
+        if len(factor_df)==0:
+            #如果没有搜索到factor历史数据,则声明一个新的进行初始化
+            start_date_datetime=datetime.datetime.strptime(start_date,'%Y-%m-%d')
+            yesterday=(start_date_datetime+datetime.timedelta(days=-1)).strftime("%Y-%m-%d")
+            #为sn申请一个新的factor,初始值为1
+            factor_df=pd.DataFrame({'sn':sn,'date':yesterday,'a0':[1],'a1':[1],'a2':[1],'a3':[1],'a4':[1]})
+        sn_factor_df=factor_df.loc[factor_df['sn']==sn,:]#筛选sn对应的factor
+        sn_factor_df=sn_factor_df.sort_values(by='date',ascending='True')#按照日期排序
+
+        sn_factor_df_last=sn_factor_df.tail(1).copy()#寻找最后一行,代表最近日期
+        sn_factor_df_last=sn_factor_df_last.append(sn_factor_df_last)#新增加一行,用于存储新的factor
+        sn_factor_df_last=sn_factor_df_last.reset_index(drop=True)#重置index
+        sn_factor_df_last.loc[1,'date']=start_date#更改后一行的date为当前日期
+        #筛选对应车辆的信息
+        condition_sn=(range_soc_df['name']==sn)
+        sn_day_df=range_soc_df.loc[condition_sn,:].copy()
+        sn_day_df=sn_day_df.reset_index(drop=True)
+        #使用updtTodayFct函数更新今天的factor
+        if len(sn_day_df)>=2:
+            #使用process函数,进行预处理
+            sn_day_df=snDayDfPreProcess(sn_day_df)#预处理函数
+            if len(sn_day_df)>=2:
+                sn_factor_df_new=updtTodayFct(sn_factor_df_last,sn_day_df)#
+                today_sn_fct_df=today_sn_fct_df.append(sn_factor_df_new.loc[1,:])#筛选第一行,进行拼接,最后写入到数据库中
+    
+    #将today_sn_fct_df写入到数据库中,今天所有factor更新的系数,一次写入。
+    if len(today_sn_fct_df)>=1:
+        today_sn_fct_df.to_sql('tb_sn_factor',con=engine,chunksize=10000,if_exists='append',index=False)
+
+def updtOneSnFct(sn,start_date,end_date):
+    '''计算开始时间到结束时间的,一个sn的所有factor'''
+    start_date_datetime=datetime.datetime.strptime(start_date,'%Y-%m-%d')#开始时间
+    end_date_datetime=datetime.datetime.strptime(end_date,'%Y-%m-%d')#开始时间
+    delta_day=(end_date_datetime-start_date_datetime).days#间隔天数
+    i=1
+    while i<=delta_day:
+        end_date=(start_date_datetime+datetime.timedelta(days=i)).strftime("%Y-%m-%d")
+        updtOneSnTodayFct(sn,start_date,end_date)#调用函数
+        print('update one sn factor from '+start_date+" to "+end_date)
+        start_date=end_date
+        i+=1#自加
+
+def updtOneSnTodayFct(sn,start_date,end_date):
+    start_date_str="'"+start_date+"'"
+    end_date_str="'"+end_date+"'"
+    sn_str="'"+sn+"'"
+    sql_cmd="select * from drive_info where time between "+start_date_str+" and "+end_date_str+\
+    " and distance!=0 and name="+sn_str
+    range_soc_df = pd.read_sql(sql_cmd, conn_qx)#使用read_sql方法查询qx数据库
+
+    if len(range_soc_df)>0:
+        #筛选出所有当日数据之后,筛选当日有更新的sn
+        today_sn_list=range_soc_df['name'].unique().tolist()
+        #建立空的dataframe,用于承接所有更新的factor信息
+        today_sn_fct_df=pd.DataFrame([],columns=['sn','date','a0','a1','a2','a3','a4'])
+
+        for sn in today_sn_list:
+            #寻找factor_df,里面是否有sn号,如果没有sn对应信息,则新增信息。
+            sn_str="'"+sn+"'"
+            sql_cmd2="select sn,date,a0,a1,a2,a3,a4 from tb_sn_factor where date<"+start_date_str+" and sn="+sn_str
+            factor_df=pd.read_sql(sql_cmd2, conn_local)#使用read_sql方法查询local数据库
+
+            #按照sn号和日期进行去重,避免运行时重复产生factor数据,保留第一次出现的行。
+            factor_df=factor_df.drop_duplicates(subset=['sn','date'],keep='first')
+
+            if len(factor_df)==0:
+                #如果没有搜索到factor历史数据,则声明一个新的进行初始化
+                start_date_datetime=datetime.datetime.strptime(start_date,'%Y-%m-%d')
+                yesterday=(start_date_datetime+datetime.timedelta(days=-1)).strftime("%Y-%m-%d")
+                factor_df=pd.DataFrame({'sn':sn,'date':yesterday,'a0':[1],'a1':[1],'a2':[1],'a3':[1],'a4':[1]})
+                today_sn_fct_df=today_sn_fct_df.append(factor_df.loc[0,:])#将初始化的行记录到数据库
+
+            sn_factor_df=factor_df.loc[factor_df['sn']==sn,:]#筛选sn对应的factor
+            
+            sn_factor_df=sn_factor_df.sort_values(by='date',ascending='True')#按照日期排序
+            sn_factor_df_last=sn_factor_df.tail(1).copy()#寻找最后一行,代表最近日期
+            sn_factor_df_last=sn_factor_df_last.append(sn_factor_df_last)#新增加一行,用于存储新的factor
+            sn_factor_df_last=sn_factor_df_last.reset_index(drop=True)#重置index
+            sn_factor_df_last.loc[1,'date']=start_date#更改后一行的date为当前日期
+            #筛选对应车辆的信息
+            condition_sn=(range_soc_df['name']==sn)
+            sn_day_df=range_soc_df.loc[condition_sn,:].copy()
+            sn_day_df=sn_day_df.reset_index(drop=True)
+            #使用updtTodayFct函数更新今天的factor
+            if len(sn_day_df)>=2:
+                #使用process函数,进行预处理
+                sn_day_df=snDayDfPreProcess(sn_day_df)#!!!!!!!!!!!增加
+                if len(sn_day_df)>=2:
+                    sn_factor_df_new=updtTodayFct(sn_factor_df_last,sn_day_df)#
+                    today_sn_fct_df=today_sn_fct_df.append(sn_factor_df_new.loc[1,:])#筛选第一行,进行拼接,最后写入到数据库中
+        
+        # #将today_sn_fct_df写入到数据库中
+        if len(today_sn_fct_df)>=1:
+            today_sn_fct_df.to_sql('tb_sn_factor',con=engine,chunksize=10000,if_exists='append',index=False)
+            # print(sn+' factor will be update in table tb_sn_factor!')
+        return today_sn_fct_df
+
+
+
+
+
+# def updtASnTodayFct(start_date,end_date,today_sn_list):
+
+#     sql_cmd="select * from qixiang_test where time>='"+start_date+"' and time<='"+end_date+"'"
+#     range_soc_df = pd.read_sql(sql_cmd, conn)#使用read_sql方法查询数据库
+
+#     sql_cmd2="select sn,date,a0,a1,a2,a3,a4 from tb_sn_factor where date<'"+start_date+"'"
+#     factor_df=pd.read_sql(sql_cmd2, conn)#使用read_sql方法查询数据库
+
+#     #筛选出所有当日数据之后,筛选当日有更新的sn
+#     # today_sn_list=range_soc_df['sn'].unique().tolist()
+#     # today_sn_list=today_sn_list[:10]#更新若干个
+#     #建立空的dataframe,用于承接所有更新的factor信息
+#     today_sn_fct_df=pd.DataFrame([],columns=['sn','date','a0','a1','a2','a3','a4'])
+
+#     for sn in today_sn_list:
+#         sn_factor_df=factor_df.loc[factor_df['sn']==sn,:]#筛选sn对应的factor
+#         sn_factor_df=sn_factor_df.sort_values(by='date',ascending='True')#按照日期排序
+#         sn_factor_df_last=sn_factor_df.tail(1).copy()#寻找最后一行,代表最近日期
+#         sn_factor_df_last=sn_factor_df_last.append(sn_factor_df_last)#新增加一行,用于存储新的factor
+#         sn_factor_df_last=sn_factor_df_last.reset_index(drop=True)#重置index
+#         sn_factor_df_last.loc[1,'date']=start_date#更改后一行的date为当前日期
+#         #筛选对应车辆的信息
+#         condition_sn=(range_soc_df['sn']==sn)
+#         sn_day_df=range_soc_df.loc[condition_sn,:].copy()
+#         sn_day_df=sn_day_df.reset_index(drop=True)
+#         #使用updtTodayFct函数更新今天的factor
+#         sn_factor_df_new=updtTodayFct(sn_factor_df_last,sn_day_df)
+#         today_sn_fct_df=today_sn_fct_df.append(sn_factor_df_new.loc[1,:])#筛选第一行,进行拼接,最后写入到数据库中
+    
+#     #将today_sn_fct_df写入到数据库中
+#     today_sn_fct_df.to_sql('tb_sn_factor',con=engine,chunksize=10000,if_exists='append',index=False)

+ 28 - 0
LIB/FRONTEND/odo/UpdtFct_Main.py

@@ -0,0 +1,28 @@
+import pandas as pd
+import pymysql
+from sqlalchemy import create_engine
+import datetime
+from UpdtFct import *
+
+
+conn_qx = pymysql.connect(
+        host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com',
+        user='qx_cas',
+        password='Qx@123456',#Qx@123456
+        database='qx_cas',
+        charset='utf8'
+    )
+
+conn_local = pymysql.connect(
+        host='localhost',
+        user='root',
+        password='pengmin',
+        database='qixiangdb',
+        charset='utf8'
+    )
+
+#指定开始时间,结束时间,更新所有sn的factor
+start_date="2021-07-18"
+end_date="2021-08-01"
+
+updtAllSnFct(start_date,end_date)

BIN
LIB/FRONTEND/odo/asset_table.xlsx


+ 66 - 0
LIB/FRONTEND/odo/main_1.py

@@ -0,0 +1,66 @@
+#coding=utf-8
+# 计算里程
+from math import radians, cos, sin, asin, sqrt
+import pandas as pd
+import numpy as np
+from datetime import datetime
+from datetime import timedelta
+
+from GpsRank import *
+from ProcessDfBms import *
+from ProcessDfGps import *
+from LIB.MIDDLE.odo.CalDist import *
+from LIB.BACKEND import DBManager
+import pdb
+
+asset_table_path='asset_table.xlsx'
+# drive_info_path='D:\\work\\Qixiang\\data_analyze_platform\\pengmin\\AllCarDist\\drive_info.xlsx'
+asset_sheet_num=1
+usecols_list=[4,5]
+
+asset_table=pd.read_excel(asset_table_path,sheet_name=asset_sheet_num,skiprows=1,usecols=usecols_list)
+SN_list=asset_table['SN号'].values.tolist()
+print('从6060sheet读取到:'+str(len(SN_list))+'行')
+asset_table=asset_table.rename(columns={'SN号':'SN','状态':'state'})
+
+asset_table.set_index(["SN"],inplace=True)
+col_name=asset_table.columns.tolist()
+col_name.extend(['range','accum_soc','day_start_soc','day_end_soc','day_start_time','day_end_time'])
+asset_table=asset_table.reindex(columns=col_name)
+
+start_hour='00:00:00'#每日查询最早时间
+end_hour='23:59:00'#每日查询最晚时间
+
+
+date_index=pd.date_range('2021-07-31','2021-07-31')
+for date in date_index:
+    '''遍历日期'''
+
+    str_date=str(date)[:10]
+    input_starttime=str_date+' '+start_hour
+    input_endtime=str_date+' '+end_hour
+    test_day=str_date[5:10]#月-日,用于建立sheet
+    drive_info_path='6060\\drive_info'+test_day+'_50_end_'+'.xlsx'
+
+    print(input_starttime)
+
+    drive_info_aday=pd.DataFrame()
+    SN_list_short=SN_list#先选择了0:50,50:end
+
+    for SN in SN_list_short:
+        '''遍历SN号'''
+        SN=SN.strip('\t')
+        SN=SN.strip('\n')
+
+        try:
+            range=GetDistInfo(SN,input_starttime,input_endtime)
+            range_df=pd.DataFrame([range])
+            drive_info_aday=pd.concat([drive_info_aday,range_df],axis=0)
+
+        except:
+            print(SN+' '+test_day+'fail')
+        else:
+            pass
+            #print(SN+' '+test_day+'success')
+
+    drive_info_aday.to_excel(drive_info_path,sheet_name=test_day)#sheet名称为testday

+ 106 - 0
LIB/FRONTEND/other/bat_user_relation/main.py

@@ -0,0 +1,106 @@
+from sqlalchemy import create_engine
+import pandas as pd
+
+# 连接数据库
+host='rm-bp10j10qy42bzy0q7.mysql.rds.aliyuncs.com'
+port=3306
+db='qixiang_manage'
+user='qx_query'
+password='@Qx_query'
+engine = create_engine('mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(user,password, host, str(port),db))
+sql = "select * from py_battery_rent"
+df_rent = pd.read_sql_query(sql, engine)
+sql = "select * from py_battery_rent_change"
+df_rent_change = pd.read_sql_query(sql, engine)
+
+
+# 统计截止日期:2021年07月25日 14:00:00
+'''
+ 预处理:
+ df_rent:
+    1)删除测试电池 GY开头 以及位数不对的电池
+    2)删除user_id为空的行
+    3) 删除 qrcode为空的行
+    4)删除pay_stat 等于3 的未支付订单
+    4) 时间戳=0 的值置None
+ df_rent_change:
+    1)删除测试电池 GY开头 以及位数不对的电池
+    2)删除qrcode和new_qrcode为空的行
+'''
+# df_rent = pd.read_csv("data_rent.csv",sep=',',encoding="ANSI")
+# print(len(df_rent))
+
+df_rent = df_rent.dropna(axis=0, how='any', subset=['user_id', 'qrcode'], inplace=False)
+df_rent = df_rent[~(df_rent['pay_stat']==3)]
+df_rent['id'] = df_rent['id'].apply(lambda x:str(int(x)) if not pd.isnull(x) else None)
+df_rent['return_time'] = df_rent['return_time'].apply(lambda x:x+3600*8 if x!=0 else None)
+df_rent['pay_time'] = df_rent['pay_time'].apply(lambda x:x+3600*8 if x!=0 else None)
+df_rent['get_time'] = df_rent['get_time'].apply(lambda x:x+3600*8 if x!=0 else None)
+df_rent['end_time'] = df_rent['end_time'].apply(lambda x:x+3600*8 if x!=0 else None)
+df_rent['addtime'] = pd.to_datetime(df_rent['addtime'].values,unit='s')
+df_rent['pay_time'] = pd.to_datetime(df_rent['pay_time'].values,unit='s')
+df_rent['get_time'] = pd.to_datetime(df_rent['get_time'].values,unit='s')
+df_rent['end_time'] = pd.to_datetime(df_rent['end_time'].values,unit='s')
+df_rent['return_time'] = pd.to_datetime(df_rent['return_time'].values,unit='s')
+df_rent['addtime'] = df_rent['addtime'].apply(lambda x:x.strftime("%Y-%m-%d %H:%M:%S"))
+df_rent['pay_time'] = df_rent['pay_time'].apply(lambda x:x.strftime("%Y-%m-%d %H:%M:%S") if not pd.isna(x) else x)
+df_rent['get_time'] = df_rent['get_time'].apply(lambda x:x.strftime("%Y-%m-%d %H:%M:%S") if not pd.isna(x) else x)
+df_rent['end_time'] = df_rent['end_time'].apply(lambda x:x.strftime("%Y-%m-%d %H:%M:%S") if not pd.isna(x) else x)
+df_rent['return_time'] = df_rent['return_time'].apply(lambda x:x.strftime("%Y-%m-%d %H:%M:%S") if not pd.isna(x) else x)
+df_rent = df_rent.reset_index(drop=True)
+print(len(df_rent))
+
+# df_rent_change = pd.read_csv("data_rent_change.csv",sep=',',encoding="ANSI")
+print(len(df_rent_change))
+df_rent_change = df_rent_change.dropna(axis=0, how='any', subset=['new_qrcode', 'qrcode'], inplace=False)
+print(len(df_rent_change))
+df_rent_change = df_rent_change.reset_index(drop=True)
+df_rent_change['create_time'] = df_rent_change['create_time'].apply(lambda x:x+3600*8 if x!=0 else None)
+df_rent_change['create_time'] = pd.to_datetime(df_rent_change['create_time'].values,unit='s')
+df_rent_change['create_time'] = df_rent_change['create_time'].apply(lambda x:x.strftime("%Y-%m-%d %H:%M:%S"))
+# 将更换电池的信息,补充至rent中, 旧电池添加一条租用记录和归还记录, 并将订单的pay_time 改为电池更换时间, 
+df_groups = df_rent_change.groupby("rent_id")
+for name, df_group in df_groups:
+    df_group = df_group.sort_values("create_time")
+    df_group = df_group.reset_index(drop=True)
+    for i in range(0, len(df_group)):
+        df_rent = df_rent.append(pd.DataFrame({'addtime':[df_group.loc[i,'create_time']],'qrcode':[df_group.loc[i,'qrcode']], 'return_time':[df_group.loc[i,'create_time']],'user_id':[df_group.loc[i,'user_id']], 'f_id':[df_group.loc[i,'f_id']]}))
+
+        df_rent = df_rent.append(pd.DataFrame({'addtime':[df_rent.loc[df_rent[(df_rent['id']==str(int(df_group.loc[i,'rent_id'])))].index,'pay_time'].values[0]],
+                'qrcode':[df_group.loc[i,'qrcode']], 'pay_time':[df_rent.loc[df_rent[(df_rent['id']==str(int(df_group.loc[i,'rent_id'])))].index,'pay_time'].values[0]], 
+                'user_id':[df_group.loc[i,'user_id']], 'f_id':[df_group.loc[i,'f_id']]}))
+  
+        df_rent.loc[df_rent[(df_rent['id']==str(int(df_group.loc[i,'rent_id'])))].index,'pay_time'] = df_group.loc[i,'create_time']
+
+# 生成用来排序的时间列
+df_rent = df_rent.reset_index(drop=True)
+df_rent['sort_time'] = [None] * len(df_rent)
+for i in range(0, len(df_rent)):
+   df_rent.loc[i, 'sort_time'] =  df_rent.loc[i, 'pay_time'] if not pd.isnull(df_rent.loc[i, 'pay_time']) else df_rent.loc[i, 'return_time']
+df_rent['sort_time'] = pd.to_datetime(df_rent['sort_time'])
+# df_rent.to_csv('ttt.csv')
+df = df_rent.copy()
+df_res = pd.DataFrame(columns=['sn', 'st', 'et', 'user_id', 'agent_id'])
+df_groups = df.groupby("qrcode")
+for name, df_group in df_groups:
+    
+    # 根据sn分组后的电池,首先按照记录时间排序,然后判断用户id是否发生变化,
+    df_group = df_group.sort_values("sort_time") # 按照本条记录的生成时间排序
+    df_group = df_group.reset_index(drop=True)
+    sn = name
+    user_id = df_group.loc[0, 'user_id']
+    st =  df_group.loc[0, 'pay_time']
+    et = None
+    for i in range(1,len(df_group)):
+        if df_group.loc[i, 'user_id'] == user_id:
+            continue
+        else:
+            et = df_group.loc[i-1, 'return_time'] if not pd.isnull(df_group.loc[i-1, 'return_time']) else None
+            df_res = df_res.append(pd.DataFrame({'sn':[sn], 'st':[st], 'et':[et], 'user_id':[user_id], 'agent_id':[df_group.loc[i-1, 'f_id']]}), ignore_index=True)
+            user_id = df_group.loc[i, 'user_id']
+            st =  df_group.loc[i, 'pay_time']
+            et = None
+    et = df_group.loc[len(df_group)-1, 'return_time'] if not pd.isnull(df_group.loc[len(df_group)-1, 'return_time']) else None
+    df_res = df_res.append(pd.DataFrame({'sn':[sn], 'st':[st], 'et':[et], 'user_id':[user_id], 'agent_id':[df_group.loc[len(df_group)-1, 'f_id']]}), ignore_index=True)
+df_res.to_csv('result.csv')
+    

+ 309 - 0
LIB/FRONTEND/soh/LFPSoh 20210711.py

@@ -0,0 +1,309 @@
+# 获取数据
+from LIB.BACKEND import DBManager
+
+import os
+import pandas as pd
+import numpy as np
+import datetime
+# import matplotlib.pyplot as plt
+
+#参数输入
+Capacity = 54
+PackFullChrgVolt=69.99
+CellFullChrgVolt=3.5
+CellVoltNums=20
+CellTempNums=4
+FullChrgSoc=98
+PeakSoc=57
+# #40Ah-OCV
+# LookTab_SOC = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100]
+# LookTab_OCV = [3.3159, 3.4502, 3.4904, 3.5277, 3.5590, 3.5888, 3.6146, 3.6312, 3.6467, 3.6642, 3.6865, 3.7171, 3.7617,
+#                3.8031, 3.8440, 3.8888, 3.9376, 3.9891, 4.0451, 4.1068, 4.1830]
+#55Ah-OCV
+LookTab_SOC = [0.00, 	2.40, 	6.38, 	10.37, 	14.35, 	18.33, 	22.32, 	26.30, 	30.28, 	35.26, 	40.24, 	45.22, 	50.20, 	54.19, 	58.17, 	60.16, 	65.14, 	70.12, 	75.10, 	80.08, 	84.06, 	88.05, 	92.03, 	96.02, 	100.00]
+LookTab_OCV = [2.7151,	3.0298,	3.1935,	3.2009,	3.2167,	3.2393,	3.2561,	3.2703,	3.2843,	3.2871,	3.2874,	3.2868,	3.2896,	3.2917,	3.2967,	3.3128,	3.3283,	3.3286,	3.3287,	3.3288,	3.3289,	3.3296,	3.3302,	3.3314,	3.3429]
+
+#定义滑动滤波函数
+def np_move_avg(a, n, mode="same"):
+    return (np.convolve(a, np.ones((n,)) / n, mode=mode))
+
+#参数初始化
+dvdq_soh=[]
+dvdq_soh_err=[]
+bms_soh=[]
+dvdq_time=[]
+dvdq_sohcfd=[]
+sn_list=[]
+
+#获取数据时间段
+now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+start_time=now_time-datetime.timedelta(days=30)
+end_time=str(now_time)
+strat_time=str(start_time)
+
+#输入一个含有‘SN号’的xlsx
+SNdata = pd.read_excel('骑享资产梳理-20210621.xlsx', sheet_name='6060')
+SNnums=SNdata['SN号']
+for k in range(15):
+    
+    SNnum=str(SNnums[k])
+    sn = SNnum
+    st = '2021-07-06 00:00:00'
+    et = '2021-07-07 20:00:00'
+
+    dbManager = DBManager.DBManager()
+    df_data = dbManager.get_data(sn=sn, start_time=st, end_time=et, data_groups=['bms'])
+    data = df_data['bms']
+
+    packcrnt=data['总电流[A]']
+    packvolt=data['总电压[V]']
+    SOC=data['SOC[%]']
+    SOH=data['SOH[%]']
+    bmsstat=data['充电状态']
+    time= pd.to_datetime(data['时间戳'], format='%Y-%m-%d %H:%M:%S')
+
+    #第一步:筛选充电数据
+    ChgStart=[]
+    ChgEnd=[]
+    for i in range(3, len(time) - 3):
+        if i==3 and bmsstat[i]==2 and bmsstat[i+1]==2 and bmsstat[i+2]==2:
+            ChgStart.append(i)
+        elif bmsstat[i-2]!=2 and bmsstat[i-1]!=2 and bmsstat[i]==2:
+            ChgStart.append(i)
+        elif bmsstat[i-1]==2 and bmsstat[i]!=2 and bmsstat[i+1]!=2:
+            ChgEnd.append(i)
+        elif i == (len(time) - 4) and bmsstat[len(bmsstat)-1] == 2 and bmsstat[len(bmsstat)-2] == 2:
+            ChgEnd.append(len(time)-1)
+
+    #第二步:筛选充电起始Soc<48%,电芯温度>15℃,且满充的数据
+    ChgStartValid1=[]
+    ChgEndValid1=[]
+    ChgStartValid2=[]
+    ChgEndValid2=[]
+
+    for i in range(min(len(ChgStart),len(ChgEnd))):
+
+        #获取最小温度值
+        celltemp = []
+        for j in range(1, CellTempNums+1):
+            s = str(j)
+            temp = data['单体温度' + s]
+            celltemp.append(temp[ChgEnd[i]])
+
+        #寻找最大电压值
+        cellvolt = []
+        for j in range(1, CellVoltNums+1):
+            s = str(j)
+            volt = max(data['单体电压' + s][ChgStart[i]:ChgEnd[i]]/1000)
+            cellvolt.append(volt)
+
+        #筛选满足2点法计算的数据
+        StandingTime=0
+        if max(cellvolt)>CellFullChrgVolt and SOC[ChgStart[i]]<30 and min(celltemp)>5:
+            for k in reversed(range(ChgStart[i])):
+                if abs(packcrnt[k - 2]) < 0.01:
+                    StandingTime = StandingTime + (time[k] - time[k-1]).total_seconds()
+                    if StandingTime > 600:  # 筛选静置时间>10min
+                        ChgStartValid1.append(ChgStart[i])
+                        ChgEndValid1.append(ChgEnd[i])
+                        break
+                else:
+                    break
+
+        #筛选满足DV/DQ方法的数据
+        if max(cellvolt)>CellFullChrgVolt and SOC[ChgStart[i]]<45 and min(celltemp)>5:
+            if ((time[ChgEnd[i]]-time[ChgStart[i]]).total_seconds())/(ChgEnd[i]-ChgStart[i])<60:
+                ChgStartValid2.append(ChgStart[i])
+                ChgEndValid2.append(ChgEnd[i])
+
+    #第三步:计算充电Soc和Soh
+
+    # 两点法计算soh
+    Soc=[]
+    Time=[]
+    Soc_Err=[]
+    Bms_Soc=[]
+
+    Soh1=[]
+    Time1=[]
+    Bms_Soh1=[]
+    Soh_Err1=[]
+
+    for i in range(len(ChgStartValid1)):
+
+        #寻找最大电压值
+        cellvolt = []
+        for j in range(1, CellVoltNums+1):
+            s = str(j)
+            volt = max(data['单体电压' + s])
+            cellvolt.append(volt)
+        voltmax_index = cellvolt.index(max(cellvolt)) + 1
+        cellvolt = data['单体电压' + str(voltmax_index)] / 1000
+
+        #soc
+        Soc.append(np.interp(cellvolt[ChgStartValid1[i]-3],LookTab_OCV,LookTab_SOC))
+        Time.append(time[ChgStartValid1[i]-3])
+        Bms_Soc.append(SOC[ChgStartValid1[i]-3])
+        Soc_Err.append(Bms_Soc[-1]-Soc[-1])
+
+        #soh
+        Ocv_Soc=np.interp(cellvolt[ChgStartValid1[i]-3],LookTab_OCV,LookTab_SOC)
+        Ah=0
+
+        for j in range(ChgStartValid1[i],ChgEndValid1[i]):
+            #计算soc
+            Step=(time[j]-time[j-1]).total_seconds()
+            Time.append(time[j])
+            Bms_Soc.append(SOC[j])
+            if Soc[-1]-(packcrnt[j]*Step*100)/(3600*Capacity)<100:
+                Soc.append(Soc[-1]-(packcrnt[j]*Step*100)/(3600*Capacity))
+            else:
+                Soc.append(100)
+            Soc_Err.append(Bms_Soc[-1] - Soc[-1])
+
+            #两点法计算soh
+            Ah=Ah-packcrnt[j]*Step/3600
+        Soh1.append(Ah*100/((FullChrgSoc-Ocv_Soc)*0.01*Capacity))
+        Bms_Soh1.append(SOH[i])
+        Soh_Err1.append(Bms_Soh1[-1]-Soh1[-1])
+        Time1.append(time[ChgStartValid1[i]])
+
+    # DV/DQ法计算soh
+    Soh2=[]
+    Time2=[]
+    Bms_Soh2=[]
+    Soh_Err2=[]
+    SohCfd = []
+    sn_list=[]
+
+    for i in range(len(ChgStartValid2)):
+
+        #寻找最大电压值
+        cellvolt1 = []
+        cellvolt=[]
+        for j in range(1, CellVoltNums+1):
+            s = str(j)
+            volt = data['单体电压' + s]
+            cellvolt1.append(volt[ChgEndValid2[i]])
+        voltmax1_index = cellvolt1.index(max(cellvolt1)) + 1
+        cellvolt1 = data['单体电压' + str(voltmax1_index)] / 1000
+
+        #电压采用滑动平均滤波
+        cellvolt=np_move_avg(cellvolt1, 3, mode="same")
+
+
+        #参数赋初始值
+        Ah = 0
+        Volt = cellvolt[ChgStartValid2[i]]
+        DV_Volt=[]
+        DQ_Ah = []
+        DVDQ = []
+        time2 = []
+        soc2 = []
+        Ah_tatal=[0]
+        xvolt=[]
+        #计算DV和DQ值
+        for j in range(ChgStartValid2[i],ChgEndValid2[i]):
+            Step=(time[j+1]-time[j]).total_seconds()
+            Ah=Ah-packcrnt[j]*Step/3600
+            if (cellvolt[j]-Volt)>0.0009 and Ah>0:
+                Ah_tatal.append(Ah_tatal[-1]+Ah)
+                DQ_Ah.append(Ah)
+                DV_Volt.append(cellvolt[j]-Volt)
+                DVDQ.append((DV_Volt[-1])/DQ_Ah[-1])
+                xvolt.append(cellvolt[j])
+                Volt=cellvolt[j]
+                Ah = 0
+                time2.append(time[j])
+                soc2.append(SOC[j])
+
+        #切片Soc>50且Soc<80
+        Data1 = pd.DataFrame({'SOC': soc2,
+                                'DVDQ': DVDQ,
+                                'Ah_tatal': Ah_tatal[:-1],
+                                'DQ_Ah':DQ_Ah,
+                                'DV_Volt':DV_Volt,
+                                'XVOLT':xvolt})
+
+        Data1=Data1[(Data1['SOC']>50) & (Data1['SOC']<80)]
+
+        #寻找峰值并计算Soh和置信度
+        # 获取最小温度值
+        celltemp = []
+        for j in range(1, CellTempNums+1):
+            s = str(j)
+            temp = data['单体温度' + s]
+            celltemp.append(temp[ChgStartValid2[i]])
+        if len(Data1['DVDQ'])>1:
+            PeakIndex=Data1['DVDQ'].idxmax()
+            #筛选峰值点附近±0.5%SOC内的数据
+            Data2=Data1[(Data1['SOC']>(Data1['SOC'][PeakIndex]-0.5)) & (Data1['SOC']<(Data1['SOC'][PeakIndex]+0.5))]
+            if len(Data2)>2:
+                Ah_tatal1 = Data1['Ah_tatal']
+                DVDQ = Data1['DVDQ']
+                soc2 = Data1['SOC']
+                xvolt = Data1['XVOLT']
+                if soc2[PeakIndex]>50 and soc2[PeakIndex]<80:
+                    DVDQ_SOH=(Ah_tatal[-1]-Ah_tatal1[PeakIndex]) * 100 / ((FullChrgSoc - PeakSoc) * 0.01 * Capacity)
+                    if DVDQ_SOH<95:
+                        DVDQ_SOH=DVDQ_SOH*0.3926+58.14
+                    if DVDQ_SOH>70 and DVDQ_SOH<120:
+                        Soh2.append(DVDQ_SOH)
+                        Bms_Soh2.append(SOH[ChgStartValid2[i]])
+                        Soh_Err2.append(Bms_Soh2[-1] - Soh2[-1])
+                        Time2.append(time[ChgStartValid2[i]])
+                        sn_list.append(SNnum)
+
+                        #计算置信度
+                        if min(celltemp)<10:
+                            SohCfd.append(50)
+                        elif min(celltemp)<20:
+                            SohCfd.append(80)
+                        else:
+                            SohCfd.append(100)
+            else:
+                Data1=Data1.drop([PeakIndex])
+                PeakIndex = Data1['DVDQ'].idxmax()
+                Data2 = Data1[(Data1['SOC'] > (Data1['SOC'][PeakIndex] - 0.5)) & (Data1['SOC'] < (Data1['SOC'][PeakIndex] + 0.5))]
+                if len(Data2) > 3:
+                    Ah_tatal1 = Data1['Ah_tatal']
+                    DVDQ = Data1['DVDQ']
+                    soc2 = Data1['SOC']
+                    xvolt = Data1['XVOLT']
+                    if soc2[PeakIndex]>50 and soc2[PeakIndex]<80:
+                        DVDQ_SOH=(Ah_tatal[-1]-Ah_tatal1[PeakIndex]) * 100 / ((FullChrgSoc - PeakSoc) * 0.01 * Capacity)
+                        if DVDQ_SOH<95:
+                            DVDQ_SOH=DVDQ_SOH*0.3926+58.14
+                        if DVDQ_SOH>70 and DVDQ_SOH<120:
+                            Soh2.append(DVDQ_SOH)
+                            Bms_Soh2.append(SOH[ChgStartValid2[i]])
+                            Soh_Err2.append(Bms_Soh2[-1] - Soh2[-1])
+                            Time2.append(time[ChgStartValid2[i]])
+                            sn_list.append(SNnum)
+
+                            #计算置信度
+                            if min(celltemp)<10:
+                                SohCfd.append(50)
+                            elif min(celltemp)<20:
+                                SohCfd.append(80)
+                            else:
+                                SohCfd.append(100)
+
+    #处理数据
+    if len(Soh2)>5:
+        Soh2=np_move_avg(Soh2,5,mode="valid")
+        result_soh2={'时间': Time2[4::],
+            'SN号':sn_list[4::],
+            'BMS_SOH': Bms_Soh2[4::],
+            'SOH': Soh2,
+            'SOH误差': Soh_Err2[4::]}
+    else:
+        result_soh2={'时间': Time2,
+            'SN号':sn_list,
+            'BMS_SOH': Bms_Soh2,
+            'SOH': Soh2,
+            'SOH误差': Soh_Err2}
+    #第四步:将数据存入Excel
+    Result_Soh2=pd.DataFrame(result_soh2)
+    Result_Soh2.to_csv('BMS_SOH_'+SNnum+'.csv',encoding='GB18030')

+ 173 - 0
LIB/FRONTEND/soh/NCMSoh 20210716.py

@@ -0,0 +1,173 @@
+# 获取数据
+from LIB.BACKEND import DBManager
+
+import os
+import pandas as pd
+import numpy as np
+import datetime
+# import matplotlib.pyplot as plt
+
+#参数输入
+Capacity = 41
+PackFullChrgVolt=69.99
+CellFullChrgVolt=3.5
+CellVoltNums=17
+CellTempNums=4
+FullChrgSoc=98
+PeakSoc=57
+# #40Ah-OCV
+LookTab_SOC = [0,	3.534883489,	8.358178409,	13.18141871,	18.00471528,	22.82796155,	27.65123833,	32.47444668,	37.29772717,	42.12099502,	46.94423182,	51.76744813,	56.59070685,	61.4139927,	66.23719857,	71.0604667,	75.88373853,	80.70702266,	85.5302705,	90.35352009,	95.17676458,	100]
+LookTab_OCV = [3.3159,	3.4384,	3.4774,	3.5156,	3.5478,	3.5748,	3.6058,	3.6238,	3.638,	3.6535,	3.6715,	3.6951,	3.7279,	3.7757,	3.8126,	3.8529,	3.8969,	3.9446,	3.9946,	4.0491,	4.109,	4.183]
+# #55Ah-OCV
+# LookTab_SOC = [0.00, 	2.40, 	6.38, 	10.37, 	14.35, 	18.33, 	22.32, 	26.30, 	30.28, 	35.26, 	40.24, 	45.22, 	50.20, 	54.19, 	58.17, 	60.16, 	65.14, 	70.12, 	75.10, 	80.08, 	84.06, 	88.05, 	92.03, 	96.02, 	100.00]
+# LookTab_OCV = [2.7151,	3.0298,	3.1935,	3.2009,	3.2167,	3.2393,	3.2561,	3.2703,	3.2843,	3.2871,	3.2874,	3.2868,	3.2896,	3.2917,	3.2967,	3.3128,	3.3283,	3.3286,	3.3287,	3.3288,	3.3289,	3.3296,	3.3302,	3.3314,	3.3429]
+
+#参数初始化
+Soh3=[]
+Time3=[]
+Bms_Soh3=[]
+Soh_Err3=[]
+sn_list=[]
+
+#获取数据时间段
+now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+start_time=now_time-datetime.timedelta(days=31)
+end_time=str(now_time)
+strat_time=str(start_time)
+
+#输入一个含有‘SN号’的xlsx
+SNdata = pd.read_excel('骑享资产梳理-20210621.xlsx', sheet_name='6040骑享')
+SNnums=SNdata['SN号']
+for k in range(len(SNnums)):
+    SNnum=str(SNnums[k])
+
+    sn = SNnum
+    st = strat_time
+    et = end_time
+
+    dbManager = DBManager.DBManager()
+    df_data = dbManager.get_data(sn=sn, start_time=st, end_time=et, data_groups=['bms'])
+    data = df_data['bms']
+    # print(data)
+
+    packcrnt=data['总电流[A]']
+    packvolt=data['总电压[V]']
+    SOC=data['SOC[%]']
+    SOH=data['SOH[%]']
+    bmsstat=data['充电状态']
+    time= pd.to_datetime(data['时间戳'], format='%Y-%m-%d %H:%M:%S')
+
+    #第一步:筛选充电数据
+    if len(packcrnt)>100:
+        ChgStart=[]
+        ChgEnd=[]
+        for i in range(3, len(time) - 3):
+            if i==3 and bmsstat[i]==2 and bmsstat[i+1]==2 and bmsstat[i+2]==2:
+                ChgStart.append(i)
+            elif bmsstat[i-2]!=2 and bmsstat[i-1]!=2 and bmsstat[i]==2:
+                ChgStart.append(i)
+            elif bmsstat[i-1]==2 and bmsstat[i]!=2 and bmsstat[i+1]!=2:
+                ChgEnd.append(i-1)
+            elif i == (len(time) - 4) and bmsstat[len(bmsstat)-1] == 2 and bmsstat[len(bmsstat)-2] == 2:
+                ChgEnd.append(len(time)-2)
+
+        #第二步:筛选充电起始Soc<45% & SOC>85%,电芯温度>5℃
+        ChgStartValid1=[]
+        ChgEndValid1=[]
+        ChgStartValid2=[]
+        ChgEndValid2=[]
+        StandingNum=[]
+
+        for i in range(min(len(ChgStart),len(ChgEnd))):
+
+            #获取最小温度值
+            celltemp = []
+            for j in range(1, CellTempNums+1):
+                s = str(j)
+                temp = data['单体温度' + s]
+                celltemp.append(temp[ChgEnd[i]])
+            
+            #去除电流0点   
+            for k in range(ChgStart[i],ChgEnd[i]):
+                if packcrnt[k]<-0.5 and packcrnt[k+1]>-0.5 and packcrnt[k+2]>-0.5 and packcrnt[k+3]>-0.5:
+                    ChgEnd[i]=k
+            
+            #计算最大packvolt
+            if len(packvolt[ChgStart[i]:ChgEnd[i]])>0:
+                packvoltMAX=max(packvolt[ChgStart[i]:ChgEnd[i]])
+
+                #筛选满足2点法计算的数据
+                StandingTime=0
+                StandingTime1=0
+                StandingTime2=0
+                if SOC[ChgEnd[i]]>85 and SOC[ChgStart[i]]<45 and min(celltemp)>5:
+                    for m in range(min(len(packcrnt)-ChgEnd[i]-2,ChgStart[i]-2)):
+                        if abs(packcrnt[ChgStart[i] - m - 1]) < 0.1:
+                            StandingTime = StandingTime + (time[ChgStart[i] - m] - time[ChgStart[i] - m - 1]).total_seconds()
+                        if abs(packcrnt[ChgEnd[i] + m + 1]) < 0.1:
+                            StandingTime1 = StandingTime1 + (time[ChgEnd[i] + m + 1] - time[ChgEnd[i] + m]).total_seconds()
+                        if StandingTime > 900 and StandingTime1>900 and ((time[ChgEnd[i]]-time[ChgStart[i]]).total_seconds())/(ChgEnd[i]-ChgStart[i])<60:  #筛选静置时间>15min且慢充过程丢失数据少
+                            ChgStartValid1.append(ChgStart[i])
+                            ChgEndValid1.append(ChgEnd[i])
+                            StandingNum.append(m)
+                            break
+                        if abs(packcrnt[ChgStart[i] - m - 2])>0.5 and abs(packcrnt[ChgEnd[i] + m + 2])>0.5:
+                            break
+
+        # 计算soh
+        Soh1=[]
+        Soh2=[]
+        Time1=[]
+        Bms_Soh1=[]
+        Soh_Err1=[]
+        sn_list1=[]
+        #两点法计算Soh
+        if len(ChgStartValid1)>0:
+            for i in range(len(ChgStartValid1)):
+                #计算Ah
+                Ah=0
+                for j in range(ChgStartValid1[i],ChgEndValid1[i]):
+                    Step=(time[j+1]-time[j]).total_seconds()
+                    Ah=Ah-packcrnt[j+1]*Step/3600
+                #计算每个电芯的Soh
+                for j in range(1, CellVoltNums+1):
+                    s = str(j)
+                    cellvolt = data['单体电压' + s]/1000
+                    OCVStart=cellvolt[ChgStartValid1[i]-2]
+                    OCVEnd=cellvolt[ChgEndValid1[i]+StandingNum[i]]
+                    #soh
+                    Ocv_Soc1=np.interp(OCVStart,LookTab_OCV,LookTab_SOC)
+                    Ocv_Soc2=np.interp(OCVEnd,LookTab_OCV,LookTab_SOC)
+                    Soh2.append(Ah*100/((Ocv_Soc2-Ocv_Soc1)*0.01*Capacity))
+                Soh1.append(np.mean(Soh2))
+                Bms_Soh1.append(SOH[ChgStartValid1[i]])
+                Soh_Err1.append(Bms_Soh1[-1]-Soh1[-1])
+                Time1.append(time[ChgStartValid1[i]])
+                sn_list1.append(SNnum)
+       
+            # Soh3.append(np.mean(Soh1))
+            # Bms_Soh3.append(np.mean(Bms_Soh1))
+            # Soh_Err3.append(np.mean(Soh_Err1))
+            # Time3.append(time[ChgStartValid1[-1]])
+            # sn_list.append(SNnum)
+
+        #第四步:将数据存入Excel
+            result_soh2={'时间': Time1,
+                'SN号': sn_list1,
+                'BMS_SOH': Bms_Soh1,
+                'SOH': Soh1,
+                'SOH误差': Soh_Err1}
+
+            Result_Soh2=pd.DataFrame(result_soh2)
+            Result_Soh2.to_csv('BMS_SOH_'+SNnum+'.csv',encoding='GB18030')
+
+#     result_soh1={'时间': Time3,
+#         'SN号':sn_list,
+#         'BMS_SOH': Bms_Soh3,
+#         'SOH': Soh3,
+#         'SOH误差': Soh_Err3}
+
+# Result_Soh1=pd.DataFrame(result_soh1)
+# print(Result_Soh1)
+# Result_Soh1.to_csv('BMS_SOH_'+'6040'+'.csv',encoding='GB18030')

+ 34 - 0
LIB/FRONTEND/soh/main.py

@@ -0,0 +1,34 @@
+#coding=utf-8
+import os
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+from LIB.MIDDLE import SignalMonitor
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+import time, datetime
+import traceback
+from LIB.MIDDLE.soh import NCMSoh_20210716 as NCMSoh
+from LIB.MIDDLE.soh import LFPSoh_20210711 as LFPSoh
+
+from urllib import parse
+
+dbManager = DBManager.DBManager()
+if __name__ == "__main__":
+    SNdata_6040 = pd.read_excel('骑享资产梳理-20210621.xlsx', sheet_name='6040骑享')
+    SNdata_6060 = pd.read_excel('骑享资产梳理-20210621.xlsx', sheet_name='6060')
+    SNnums_6060=SNdata_6060['SN号']
+    SNnums_6040=SNdata_6040['SN号']
+    now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+    now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+    start_time=now_time-datetime.timedelta(days=31)
+    end_time=str(now_time)
+    start_time=str(start_time)
+
+    for sn in SNnums_6040.tolist():
+        res = NCMSoh.cal_soh(sn, end_time, start_time)
+        res.to_csv('BMS_SOH_'+sn+'.csv',encoding='GB18030')
+    
+    for sn in SNnums_6060.tolist():
+        res = LFPSoh.cal_soh(sn, end_time, start_time)
+        res.to_csv('BMS_SOH_'+sn+'.csv',encoding='GB18030')

+ 6 - 0
LIB/FRONTEND/soh/soh表头及数据类型.xlsx

@@ -0,0 +1,6 @@
+表头	名称	数据类型
+时间	time	timestamps
+SN号	sn	str
+BMS_SOH	bms_soh	float64
+SOH	soh	float64
+SOH误差	soh_err	float64

BIN
LIB/FRONTEND/soh/骑享资产梳理-20210621.xlsx


+ 13 - 0
LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/CoreAlgo/core.py

@@ -0,0 +1,13 @@
+
+from LIB.BACKEND import DBManager # 以相对路径的方式从LIB开始引入
+import pandas as pd
+
+
+class CalSor:
+    
+    def __init__(self):
+        pass
+    
+    def calSor(self, sn,df_bms, celltype):
+        i = len(df_bms)
+        return pd.DataFrame({'data':[i],'sn':[sn]})

+ 34 - 0
LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/Main/Dockerfile

@@ -0,0 +1,34 @@
+# 基础镜像
+FROM python:3.8
+
+# 作者 (需要修改)
+LABEL author="lm"  
+
+
+# 环境变量参数
+ENV TZ="Asia/Shanghai"
+ENV PYTHONPATH="/"
+
+# 安装用到的python 第三方库 (需要修改)
+RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple \
+    sqlalchemy \
+    pandas==1.3.4 \
+    pymysql apscheduler \
+    scipy \
+    cryptography \
+    numpy==1.20.3 \
+    requests
+
+#RUN rm -r /usr/bin
+#RUN rm -r /bin
+
+# 配置算法路径 (需要修改)
+ARG ALGO_PATH=LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/
+
+
+# 复制文件文件到 容器
+ADD LIB/BACKEND/ /LIB/BACKEND
+ADD ${ALGO_PATH}/ /${ALGO_PATH}/
+WORKDIR ${ALGO_PATH}/Main/
+
+CMD ["python", "main.py"]

+ 13 - 0
LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/Main/config-dev.ini

@@ -0,0 +1,13 @@
+[Mysql-1]
+host = localhost
+port = 3306
+db = test
+user = root
+password = Qx123456
+
+[Mysql-2]
+host = localhost
+port = 3306
+db = test
+user = root
+password = Qx123456

+ 13 - 0
LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/Main/config-pro.ini

@@ -0,0 +1,13 @@
+[Mysql-1]
+host = 120.25.223.1
+port = 4901
+db = test
+user = root
+password = 123456
+
+[Mysql-2]
+host = 120.25.223.1
+port = 4901
+db = test
+user = root
+password = 123456

+ 13 - 0
LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/Main/config-test.ini

@@ -0,0 +1,13 @@
+[Mysql-1]
+host = 192.168.0.43
+port = 3306
+db = test
+user = root
+password = Qx123456
+
+[Mysql-2]
+host = 192.168.0.43
+port = 3306
+db = test
+user = root
+password = Qx123456

+ 141 - 0
LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/Main/main.py

@@ -0,0 +1,141 @@
+# -*- coding: UTF-8 -*-
+
+import pandas as pd
+import time
+from sqlalchemy import create_engine
+import os
+import configparser
+import pymysql
+import traceback
+import datetime
+from LIB.BACKEND import DBManager,Log # 以相对路径的方式从LIB开始引入!!!!!
+from LIB.MIDDLE.AlgoDemo.Demo.V_1_0_0.CoreAlgo import core # 以相对路径的方式从LIB开始引入!!!!!
+from LIB.BACKEND.OPENAPI import OpenApi
+
+if __name__ == '__main__':
+    
+    
+    # 环境变量配置(通过环境变量确定当前程序运行在开发、测试、生产环境)
+    env_dist = os.environ
+    cur_env = env_dist.get("CURENV", 'dev') # 默认为开发环境
+    
+    # 读取配置文件 (该部分请不要修改)
+    cf = configparser.ConfigParser()
+    if cur_env == 'dev':
+        cf.read("config-dev.ini")
+    elif cur_env == 'test':
+        cf.read("config-test.ini")
+    elif cur_env == 'pro':
+        cf.read("config-pro.ini")
+    
+    # 解析配置文件 (该部分请按照实际需求修改)
+    host1 = cf.get("Mysql-1", 'host')
+    port1 = int(cf.get("Mysql-1", 'port'))
+    db1 = cf.get("Mysql-1", 'db')
+    user1 = cf.get("Mysql-1", 'user')
+    password1 = cf.get("Mysql-1", 'password')
+    
+    host2 = cf.get("Mysql-2", 'host')
+    port2 = int(cf.get("Mysql-2", 'port'))
+    db2 = cf.get("Mysql-2", 'db')
+    user2 = cf.get("Mysql-2", 'user')
+    password2 = cf.get("Mysql-2", 'password')
+    
+    # 日志配置(按照该配置,每次运行时可自动生成运行日期的文件夹, 会在与main.py同级的, 该部分可按照实际需求修改)
+    now_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()).replace(":","_")
+    log_path = 'log/' + now_str
+    if not os.path.exists(log_path):
+        os.makedirs(log_path)
+    log = Log.Mylog(log_name='cal_sor', log_level = 'info')
+    log.set_file_hl(file_name='{}/info.log'.format(log_path), log_level='info', size=1024* 1024 * 100)
+    log.set_file_hl(file_name='{}/error.log'.format(log_path), log_level='error', size=1024* 1024 * 100)
+    log.set_stream_hl("error") # 打印错误日志
+    logger = log.get_logger()
+    logger.info("{}, 算法开始".format(str(os.getpid())))
+    
+    try:    
+        # 连接数据库的两种方式
+        # 方式一:新dataframe写入数据库时,采用该方式可以不需要写sql语句;
+        #    该方式无法对数据库进行修改;
+        
+        db_engine_1 = create_engine(
+            "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+                user1, password1, host1, port1, db1
+            ))
+        
+        # 方式二:该方式可以通过写update SQL语句,对数据库中的数据进行修改
+        conn = pymysql.connect(host=host2, port=port2, user=user2, password=password2, database=db2)
+        cursor = conn.cursor()
+
+        # 准备算法数据
+        
+        # 原始数据时间设置
+        end_time=datetime.datetime.now()
+        start_time=end_time-datetime.timedelta(seconds=300)
+        start_time=start_time.strftime('%Y-%m-%d %H:%M:%S')
+        end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+        
+        # 从开放平台获取资产列表及相关信息
+        openApi = OpenApi.OpenApi()
+        df_all_sn = openApi.get_asset()
+        calSor = core.CalSor(); # 算法初始化
+        
+        # 遍历资产列表,获取数据和参数,输入算法中
+        for index in range(0, df_all_sn.index[-1]):
+            
+            try:# 解析得到厂家、sn、协议类型、电芯参数等输入数据
+                factory = df_all_sn.loc[index,'factory']
+                sn = df_all_sn.loc[index,'sn']
+                imei = df_all_sn.loc[index,'imei']
+                protocolType = df_all_sn.loc[index,'protocolType'] # 协议类型(3:32960,10:科易,13:优旦)
+                if imei[5:9] == 'N640':
+                    celltype=1 #6040三元电芯
+                elif imei[5:9] == 'N440':
+                    celltype=2 #4840三元电芯
+                elif imei[5:9] == 'L660':
+                    celltype=99 # 6060锂电芯
+                elif imei[3:5] == 'LX' and imei[5:9] == 'N750':    
+                    celltype=3 #力信 50ah三元电芯
+                elif imei[3:5] == 'CL' and imei[5:9] == 'N750': 
+                    celltype=4 #CATL 50ah三元电芯
+                elif imei[1:3] == 'JM': # 金茂
+                    celltype=100 #CATL 50ah三元电芯
+
+                # 获取电池历史数据
+                logger.info("{} start".format(sn))
+                dbManager = DBManager.DBManager()
+                df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms', 'gps', 'accum', 'system'])
+                df_bms = df_data['bms']
+                
+                # 获取数据库原始数据
+                df_ram = pd.read_sql("select * from test_tb where sn ='{}'".format(sn), db_engine_1)
+                
+                # 调用核心算法 
+                df_res = calSor.calSor(sn, df_bms,celltype);
+                
+                # 算法结果入库
+                # 新增
+                if (len(df_ram) == 0):
+                    df_res.to_sql("test_tb",con=db_engine_1, if_exists="append",index=False)
+                    
+                else:   
+                # 修改
+                    sql = '''update test_tb set data={} where sn='{}' '''.format(df_res['data'].values[0], (df_res['sn'].values[0]))
+                    cursor.execute(sql)
+                    conn.commit()
+                logger.info("{} done".format(sn))
+            except Exception as e :
+                logger.error(traceback.format_exc)
+                logger.error(str(e))
+                logger.error(u"{} :{},{} 任务运行错误\n".format(sn,start_time,end_time), exc_info=True)
+            
+    except Exception as e :
+        logger.error(traceback.format_exc)
+        logger.error(str(e))
+        logger.error(u"任务运行错误2\n", exc_info=True)
+        
+    # 释放数据库资源
+    cursor.close()
+    conn.close()
+    db_engine_1.dispose()
+

+ 132 - 0
LIB/MIDDLE/Anomaly_Detection/V1_0_0/anomalyPCA.py

@@ -0,0 +1,132 @@
+#热失控预警:PCA异常指数
+
+import pandas as pd
+import numpy as np
+from scipy.signal import savgol_filter
+from sklearn.preprocessing import RobustScaler
+from sklearn.decomposition import PCA
+
+#筛选特征
+def makedataset1(df_data):
+    df_data=df_data.drop(['Unnamed: 0','总电流[A]','GSM信号','外电压','SOH[%]','开关状态','充电状态','故障等级','故障代码','绝缘电阻','上锁状态','加热状态','单体均衡状态','总输出状态'],axis=1,errors='ignore')
+    df_data=df_data.drop(["单体温度"+str(i) for i in range(1,5)],axis=1,errors='ignore')
+    df_data=df_data.drop(["其他温度"+str(i) for i in range(1,7)],axis=1,errors='ignore')
+    listV=[s for s in list(df_data) if '单体电压' in s]
+    for i in range(1,len(listV)+1):
+        df_data=df_data[(df_data['单体电压'+str(i)]>2200) & (df_data['单体电压'+str(i)]<4800)]
+    df_data=df_data[df_data['SOC[%]']>20]
+    df_data['时间']=[df_data.loc[i,'时间戳'][0:15] for i in df_data.index]
+    df_data=df_data.drop('时间戳',axis=1)
+    data_set=df_data.groupby('时间').mean(False)
+    for k in data_set.columns:
+        data_set[k]=savgol_filter(data_set[k],3,2)
+    return data_set
+
+#新建统计特征
+def makedataset2(df_data):
+    data_set=makedataset1(df_data)
+    listV=[s for s in list(df_data) if '单体电压' in s]
+    data_set["最低单体电压"]=data_set[["单体电压"+str(i) for i in range(1,len(listV)+1)]].min(axis=1)
+    data_set["最高单体电压"]=data_set[["单体电压"+str(i) for i in range(1,len(listV)+1)]].max(axis=1)
+    data_set["平均单体电压"]=data_set[["单体电压"+str(i) for i in range(1,len(listV)+1)]].mean(axis=1)
+    data_set["最大单体压差"]=[data_set.loc[k,"最高单体电压"]-data_set.loc[k,"最低单体电压"] for k in data_set.index]
+    data_set["低压差"]=[data_set.loc[k,"平均单体电压"]-data_set.loc[k,"最低单体电压"] for k in data_set.index]
+    data_set=data_set.drop(["单体电压"+str(i) for i in range(1,len(listV)+1)],axis=1)
+    return data_set
+
+#标准化
+def process(data_set):
+    features=data_set.columns
+    sX=RobustScaler(copy=True)
+    data_set2=data_set.copy()
+    data_set2.loc[:,features]=sX.fit_transform(data_set2[features])
+    return data_set2
+
+#异常指数函数
+def anomalyScores(originalDF,reducedDF):
+    loss=np.sum((np.array(originalDF)-np.array(reducedDF))**2,axis=1)
+    loss=pd.Series(data=loss,index=originalDF.index)
+    loss=(loss-np.min(loss))/(np.max(loss)-np.min(loss))
+    return loss
+
+#建立PCA模型
+def anomalyPCA(x_train_pro):
+    n_components=4
+    whiten=True
+    random_state=2
+    pca=PCA(n_components=n_components,whiten=whiten,random_state=random_state)
+    pca.fit(x_train_pro)
+    return pca
+
+#判断PCA异常指数
+def transform(df_data_pro,model,df_data):
+    #降维
+    X_train=model.transform(df_data_pro)
+    X_train=pd.DataFrame(data=X_train,index=df_data_pro.index)
+    #还原
+    X_train_inverse=model.inverse_transform(X_train)
+    X_train_inverse=pd.DataFrame(data=X_train_inverse,index=df_data_pro.index)
+    #异常指数
+    anomalyScoresModel=anomalyScores(df_data_pro,X_train_inverse)
+    df_data2=df_data.copy()
+    if len(anomalyScoresModel)>15:
+        anomalyScoresModel=savgol_filter(anomalyScoresModel,15,3)
+        df_data2['anomalyScores_'+str(model)]=anomalyScoresModel
+    else:
+        df_data2['anomalyScores_'+str(model)]=0
+    return df_data2
+
+#判断离群
+def detect_outliers(data,pred,threshold=3):
+    anomaly=data['anomalyScores_PCA(n_components=4, random_state=2, whiten=True)']
+    anomalypred=pred['anomalyScores_PCA(n_components=4, random_state=2, whiten=True)']
+    mean_d=np.mean(anomaly.values)
+    std_d=np.std(anomaly.values)
+    max_score=np.max(anomaly.values)
+    outliers2=pd.DataFrame()
+    for k in anomalypred.index:
+        z_score= (anomalypred[k]-mean_d)/std_d
+        if (np.abs(z_score) >threshold) & (anomalypred[k]>max_score):
+            outliers2=outliers2.append(pred[anomalypred.values==anomalypred[k]])
+    return outliers2
+
+#训练模型
+def train_model(data_train):
+    x_train1=makedataset1(data_train) 
+    x_train2=makedataset2(data_train)  
+    x_train_pro1=process(x_train1) 
+    x_train_pro2=process(x_train2) 
+    pca1=anomalyPCA(x_train_pro1) 
+    pca2=anomalyPCA(x_train_pro2) 
+    res1=transform(x_train_pro1,pca1,x_train1)
+    res2=transform(x_train_pro2,pca2,x_train2)
+    return pca1,pca2,res1,res2
+
+#预测
+def prediction(data_test,pca1,pca2):
+    x_test1=makedataset1(data_test) 
+    x_test2=makedataset2(data_test) 
+    x_test_pro1=process(x_test1) 
+    x_test_pro2=process(x_test2) 
+    pred1=transform(x_test_pro1,pca1,x_test1)
+    pred2=transform(x_test_pro2,pca2,x_test2)
+    return pred1,pred2
+
+def boxplot_fill(res2):
+    col=res2['低压差']
+    # 计算iqr:数据四分之三分位值与四分之一分位值的差
+    iqr=col.quantile(0.75)-col.quantile(0.25)
+    # 根据iqr计算异常值判断阈值
+    u_th=col.quantile(0.75) + 2*iqr # 上界
+    return u_th
+
+#判定异常
+def check_anomaly(outliers1,outliers2,res2):
+    outliers=pd.merge(outliers1,outliers2,on='时间')
+    outliers=outliers[outliers['SOC[%]_x']>50]
+    outliers=outliers.drop(['总电压[V]_y','单体压差_y','SOC[%]_y'],axis=1)
+    u_th=boxplot_fill(res2)
+    outliers=outliers[outliers['低压差']>u_th]
+    return outliers
+    
+

+ 42 - 0
LIB/MIDDLE/Anomaly_Detection/V1_0_0/main_anomalyPCA.py

@@ -0,0 +1,42 @@
+#热失控预警:PCA异常指数
+#训练模型
+
+from LIB.BACKEND import DBManager
+dbManager = DBManager.DBManager()
+from LIB.MIDDLE.CellStateEstimation.Common import log
+import pandas as pd
+from anomalyPCA import *
+import joblib
+import datetime
+
+dataSOH = pd.read_excel('sn-20210903.xlsx',sheet_name='sn-20210903')
+fileNames = dataSOH['sn']
+fileNames = list(fileNames)
+l = len(fileNames)
+
+now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')   #type: str
+now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')     #type: datetime
+start_time=now_time-datetime.timedelta(days=365)
+end_time=str(now_time)
+start_time=str(start_time)
+
+mylog=log.Mylog('log.txt','error')
+mylog.logcfg()
+
+for k in range(l): 
+    try: 
+        sn = fileNames[k]
+        df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms'])
+        data_train = df_data['bms']
+        
+        if len(data_train)>0:
+            pca1,pca2,res1,res2=train_model(data_train) 
+            joblib.dump(pca1,'pca1_'+sn+'.m')  
+            joblib.dump(pca2,'pca2_'+sn+'.m')  
+            res1.to_csv('res1_'+sn+'.csv',encoding='gbk')
+            res2.to_csv('res2_'+sn+'.csv',encoding='gbk')
+    
+    except Exception as e:
+        print(repr(e))
+        mylog.logopt(sn,e)
+        pass

+ 110 - 0
LIB/MIDDLE/Anomaly_Detection/V1_0_0/main_detection.py

@@ -0,0 +1,110 @@
+#热失控预警:PCA异常指数
+#预测及异常预警
+
+from LIB.BACKEND import DBManager
+
+dbManager = DBManager.DBManager()
+import datetime
+
+import joblib
+import pandas as pd
+import pymysql
+from LIB.MIDDLE.CellStateEstimation.Common import log
+
+from anomalyPCA import *
+
+dataSOH = pd.read_excel('sn-20210903.xlsx',sheet_name='sn-20210903')
+fileNames = dataSOH['sn']
+fileNames = list(fileNames)
+l = len(fileNames)
+
+now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')   #type: str
+now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')     #type: datetime
+start_time=now_time-datetime.timedelta(hours=6)
+end_time=str(now_time)
+start_time=str(start_time)
+
+mylog=log.Mylog('log.txt','error')
+mylog.logcfg()
+
+#数据库配置
+host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+port=3306
+user='qx_algo_readonly'
+password = 'qx@123456'
+
+#读取故障结果库中code==119且end_time='0000-00-00 00:00:00'...............................
+db='safety_platform'
+mysql = pymysql.connect (host=host, port=port, user=user, password=password, database=db)
+cursor = mysql.cursor()
+param='start_time,end_time,product_id,code,level,info,advice'
+tablename='all_fault_info'
+sql =  "select %s from %s where code='C493' and end_time='0000-00-00 00:00:00'" %(param,tablename)
+cursor.execute(sql)
+res = cursor.fetchall()
+df_diag_ram= pd.DataFrame(res,columns=param.split(','))
+cursor.close()
+mysql.close()
+
+anomalies=pd.DataFrame()
+df_res=pd.DataFrame(columns=['start_time','end_time','product_id','code','level','info','advice'])
+for k in range(l): 
+    try: 
+        sn = fileNames[k]
+        df_diag_ram_sn=df_diag_ram[df_diag_ram['product_id']==sn]
+        df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time ,data_groups=['bms'])
+        data_test = df_data['bms']
+        data_test=data_test[data_test['SOC[%]']>20]
+        if len(data_test)>15:
+            pca1 = joblib.load('pca1_'+sn+'.m')  
+            pca2 = joblib.load('pca2_'+sn+'.m') 
+            res1 = pd.read_csv('res1_'+sn+'.csv',encoding='gbk')
+            res2 = pd.read_csv('res2_'+sn+'.csv',encoding='gbk')
+            pred1,pred2=prediction(data_test,pca1,pca2)
+            pred1=pred1.reset_index()
+            pred2=pred2.reset_index()
+            outliers1=detect_outliers(res1,pred1,threshold=30)
+            outliers2=detect_outliers(res2,pred2,threshold=16)
+            if (len(outliers1)>0) & (len(outliers2)>0):
+                outliers=check_anomaly(outliers1,outliers2,res2)
+                if len(outliers)>5:
+                    outliers['sn']=sn
+                    outliers=outliers.reset_index()
+                    anomalies=anomalies.append(outliers)
+                    u_th=boxplot_fill(res2)
+                    outliers3=pred2[pred2['低压差']>u_th]
+                    if df_diag_ram_sn.empty: 
+                        product_id=sn
+                        start_time=outliers.loc[0,'时间']
+                        start_time=start_time+'0:00'
+                        if outliers.loc[outliers.index[-1],'时间'] == pred1.loc[pred1.index[-1],'时间']:
+                            end_time='0000-00-00 00:00:00'
+                        elif outliers1.loc[outliers1.index[-1],'时间'] == outliers3.loc[outliers3.index[-1],'时间']:
+                            end_time='0000-00-00 00:00:00'
+                        else:
+                            end_time=outliers.loc[outliers.index[-1],'时间']
+                            end_time=end_time+'0:00'
+                        code='C493'
+                        level=4
+                        info='热失控预警'
+                        advice='建议返厂维修'
+                        df_res=df_res.append({'start_time':start_time, 'end_time':end_time,'product_id':product_id, 'code':code, 'level':level, 'info':info,'advice':advice},ignore_index=True)
+                        with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\06BatSafetyAlarm\热失控报警.txt','a') as file:
+                            file.write(str(tuple(df_res.iloc[-1]))+'\n')    
+                    else:
+                        if outliers.loc[outliers.index[-1],'时间'] == pred1.loc[pred1.index[-1],'时间']:
+                            end_time='0000-00-00 00:00:00'
+                        elif outliers1.loc[outliers1.index[-1],'时间'] == outliers3.loc[outliers3.index[-1],'时间']:
+                            end_time='0000-00-00 00:00:00'
+                        else:
+                            end_time=outliers.loc[outliers.index[-1],'时间']
+                            end_time=end_time+'0:00'
+                            df_diag_ram_sn['end_time']=end_time
+                            with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\06BatSafetyAlarm\热失控报警.txt','a') as file:
+                                file.write(str(tuple(df_diag_ram_sn.iloc[-1]))+'\n')
+                        
+           
+    except Exception as e:
+        print(repr(e))
+        mylog.logopt(sn,e)
+        pass 

BIN
LIB/MIDDLE/Anomaly_Detection/V1_0_0/sn-20210903.xlsx


+ 699 - 0
LIB/MIDDLE/CellStateEstimation/BatDiag/V1_0_0/CBMSBatDiag.py

@@ -0,0 +1,699 @@
+import pandas as pd
+import numpy as np
+import datetime
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam
+
+class BatDiag:
+    def __init__(self,sn,celltype,df_bms,df_diag_ram_sn,df_bms_ram_sn,df_soh,df_uniform):  #参数初始化
+
+        self.sn=sn
+        self.celltype=celltype
+        self.param=BatParam.BatParam(celltype)
+        self.df_bms=df_bms
+        self.df_uniform=df_uniform
+        self.df_soh=df_soh
+        self.df_bms_ram=df_bms_ram_sn.copy()
+        self.df_diag_ram=df_diag_ram_sn.copy()
+        df_bms['时间戳']=pd.to_datetime(df_bms['时间戳'], format='%Y-%m-%d %H:%M:%S')
+
+        if not df_bms_ram_sn.empty:
+            self.df_bms=self.df_bms[self.df_bms['时间戳'] > df_bms_ram_sn.iloc[-1]['time']]    #滤除原始数据中的重复数据
+            self.df_bms.reset_index(inplace=True,drop=True)     #重置索引
+        
+        self.packcrnt=df_bms['总电流[A]']*self.param.PackCrntDec
+        self.packvolt=df_bms['总电压[V]']
+        self.bmstime= df_bms['时间戳']
+        self.bms_soc=df_bms['SOC[%]']
+
+        self.cellvolt_name=['单体电压'+str(x) for x in range(1,self.param.CellVoltNums+1)]
+        # othertemp=['其他温度'+str(x) for x in range(1,self.param.OtherTempNums+1)]
+        self.celltemp_name=['单体温度'+str(x) for x in range(1,self.param.CellTempNums+1)]
+        # self.celltemp_name=celltemp+othertemp
+
+    
+    def diag(self):
+        if self.celltype<=50:
+            df_res1,df_res2=self._bat_diag()
+            return df_res1,df_res2    
+        else:
+            df_res1,df_res2=self._bat_diag()
+            return df_res1,df_res2
+
+    
+    #寻找当前行数据的所有温度值...................................................................................
+    def _celltemp_get(self,num):   
+        celltemp = list(self.df_bms.loc[num,self.celltemp_name])
+        return celltemp
+
+    #获取当前行所有电压数据............................................................................................
+    def _cellvolt_get(self,num): 
+        cellvolt = list(self.df_bms.loc[num,self.cellvolt_name]/1000)
+        return cellvolt
+
+    #..........................................电池诊断功能..................................................................
+    def _bat_diag(self):
+
+        column_name=['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice']
+        df_res=pd.DataFrame(columns=column_name)
+
+        end_time='0000-00-00 00:00:00'
+        ah_accum=0  #SOC卡滞初始参数
+        as_chg=0    #过流诊断初始参数
+        as_dis=0    #过流诊断初始参数
+        cellvoltvalid=1
+        voltdsc_time=0
+        voltdsc_count=0
+        voltfail_time=0
+        voltfail_count=0
+        voltloose_time=0
+        voltloose_count=0
+        voltover_time=0
+        voltunder_time=0
+        cov_time=0
+        cuv_time=0
+        cdv_time=0
+        pov_time=0
+        puv_time=0 
+        ot_time=0
+        ut_time=0
+        dt_time=0
+            
+        for i in range(len(self.df_bms)):
+        
+            #电压诊断功能.........................................................................................................................................
+            if i<1:
+                cellvolt2=self._cellvolt_get(i)
+                cellvoltmin2=min(cellvolt2)
+                cellvoltmax2=max(cellvolt2)
+                cellvoltmin_index2=cellvolt2.index(cellvoltmin2)
+                cellvoltmax_index2=cellvolt2.index(cellvoltmax2)
+                if not self.df_bms_ram.empty:
+                    time1=self.df_bms_ram.iloc[-1]['time']
+                    time2=self.bmstime[i]
+                    cellvolt1=self.df_bms_ram.iloc[-1]['cellvolt']
+                    cellvoltmin1=min(cellvolt1)
+                    cellvoltmax1=max(cellvolt1)
+                    cellvoltmin_index1=cellvolt1.index(cellvoltmin1)
+                    cellvoltmax_index1=cellvolt1.index(cellvoltmax1)
+                else:
+                    cellvolt1=cellvolt2
+                    time1=self.bmstime[i]
+                    time2=self.bmstime[i]
+                    cellvoltmin1=cellvoltmin2
+                    cellvoltmax1=cellvoltmax2
+                    cellvoltmin_index1=cellvoltmin_index2
+                    cellvoltmax_index1=cellvoltmax_index2
+            else:
+                time1=self.bmstime[i-1]
+                time2=self.bmstime[i]
+                cellvolt2=self._cellvolt_get(i)
+                cellvoltmin2=min(cellvolt2)
+                cellvoltmax2=max(cellvolt2)
+                cellvoltmin_index2=cellvolt2.index(cellvoltmin2)
+                cellvoltmax_index2=cellvolt2.index(cellvoltmax2)
+                cellvolt1=self._cellvolt_get(i-1)
+                cellvoltmin1=min(cellvolt1)
+                cellvoltmax1=max(cellvolt1)
+                cellvoltmin_index1=cellvolt1.index(cellvoltmin1)
+                cellvoltmax_index1=cellvolt1.index(cellvoltmax1)
+
+            #电压采样断线..........................................................................................................................................
+            if not 'C308' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if (cellvoltmin2<2 and cellvoltmax2>4.5 and abs(cellvoltmax_index2-cellvoltmin_index2)==1) and (cellvoltmin1<2 and cellvoltmax1>4.5 and abs(cellvoltmax_index1-cellvoltmin_index1)==1):   #电压断线故障进入
+                    cellvoltvalid=0
+                    voltdsc_time=voltdsc_time+(time2-time1).total_seconds()
+                    if voltdsc_time>self.param.volt_time:    #持续时间
+                        time=self.bmstime[i]
+                        code='C308'
+                        faultlv=3
+                        faultinfo='电芯{}和{}电压采样断线'.format(cellvoltmin_index2+1, cellvoltmax_index2+1)
+                        faultadvice='通知用户更换电池,电池返厂维修'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                    else:
+                        pass
+                elif (cellvoltmin2<2 and cellvoltmax2>4.5 and abs(cellvoltmax_index2-cellvoltmin_index2)==1) and (cellvoltmin1>2.5 and cellvoltmax1<4.3):   #连续跳变
+                    cellvoltvalid=0
+                    voltdsc_count=voltdsc_count+1
+                    if voltdsc_count>=3:
+                        time=self.bmstime[i]
+                        code='C308'
+                        faultlv=3
+                        faultinfo='电芯{}和{}电压采样断线'.format(cellvoltmin_index2+1, cellvoltmax_index2+1)
+                        faultadvice='通知用户更换电池,电池返厂维修'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                    else:
+                        pass
+                else:
+                    voltdsc_time=0
+            else:
+                cellvoltvalid=0
+                if cellvoltmin2>2 and cellvoltmax2<4.5 and cellvoltmin1>2 and cellvoltmax1<4.5:
+                    cellvoltvalid=1
+                    time=self.bmstime[i]
+                    self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C308'].index, 'end_time'] = time
+                else:
+                    pass
+            
+            #电压采样系统失效.............................................................................................................
+            if not 'C309' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if (cellvoltmin2<1 and cellvoltmin1<1) or (cellvoltmax2>5 and cellvoltmax1>5):
+                    cellvoltvalid=0
+                    voltfail_time=voltfail_time+(time2-time1).total_seconds()
+                    if voltfail_time>self.param.volt_time:    #持续时间
+                        time=self.bmstime[i]
+                        code='C309'
+                        faultlv=3
+                        faultinfo='电芯电压采样系统失效'
+                        faultadvice='通知用户更换电池,电池返厂维修'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                    else:
+                        pass
+                elif (cellvoltmin2<1 and cellvoltmin1>2.5) or  (cellvoltmax2>5 and cellvoltmax1<4.5):   #连续跳变
+                    cellvoltvalid=0
+                    voltfail_count=voltfail_count+1
+                    if voltfail_count>=3:
+                        time=self.bmstime[i]
+                        code='C309'
+                        faultlv=3
+                        faultinfo='电芯电压采样系统失效'
+                        faultadvice='通知用户更换电池,电池返厂维修'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                    else:
+                        pass
+                else:
+                    voltfail_time=0
+            else:
+                cellvoltvalid=0
+                if cellvoltmin2>2.5 and cellvoltmax2<4.5 and cellvoltmin1>2.5 and cellvoltmax1<4.5:
+                    cellvoltvalid=1
+                    time=self.bmstime[i]
+                    self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C309'].index, 'end_time'] = time
+                else:
+                    pass
+            
+            #电压采样线松动.................................................................................................................
+            if not 'C208' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if cellvoltmin2>2 and cellvoltmax2<4.5 and cellvoltmin1>2 and cellvoltmax1<4.5:
+                    cellvolt1_std=np.std(cellvolt1)
+                    cellvolt1_mean=np.mean(cellvolt1)
+                    cellvolt1_3sigma=(np.array(cellvolt1)-cellvolt1_mean)/cellvolt1_std
+                    cellvolt2_std=np.std(cellvolt2)
+                    cellvolt2_mean=np.mean(cellvolt2)
+                    cellvolt2_3sigma=(np.array(cellvolt2)-cellvolt2_mean)/cellvolt2_std
+                    if (min(cellvolt1_3sigma)<-3 and max(cellvolt1_3sigma)>3) and (min(cellvolt2_3sigma)<-3 and max(cellvolt2_3sigma)>3):   #连续发生
+                        cellvoltvalid=0
+                        voltloose_time=voltloose_time+(time2-time1).total_seconds()
+                        if voltloose_time>self.param.volt_time:
+                            time=self.bmstime[i]
+                            code='C208'
+                            faultlv=3
+                            faultinfo='电芯电压{}和{}采样松动'.format(cellvoltmin_index2+1, cellvoltmax_index2+1)
+                            faultadvice='通知技术人员介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    elif (min(cellvolt1_3sigma)>-3 and max(cellvolt1_3sigma)<3) and (min(cellvolt2_3sigma)<-3 and max(cellvolt2_3sigma)>3): #连续跳变
+                        cellvoltvalid=0
+                        voltloose_count=voltloose_count+1
+                        if voltloose_count>=3:
+                            time=self.bmstime[i]
+                            code='C208'
+                            faultlv=3
+                            faultinfo='电芯电压{}和{}采样松动'.format(cellvoltmin_index2+1, cellvoltmax_index2+1)
+                            faultadvice='通知技术人员介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        voltloose_time=0
+                else:
+                    voltloose_time=0
+            else:
+                cellvoltvalid=0
+                cellvolt1_std=np.std(cellvolt1)
+                cellvolt1_mean=np.mean(cellvolt1)
+                cellvolt1_3sigma=(np.array(cellvolt1)-cellvolt1_mean)/cellvolt1_std
+                cellvolt2_std=np.std(cellvolt2)
+                cellvolt2_mean=np.mean(cellvolt2)
+                cellvolt2_3sigma=(np.array(cellvolt2)-cellvolt2_mean)/cellvolt2_std
+                if(min(cellvolt1_3sigma)>-3 or max(cellvolt1_3sigma)<3) and (min(cellvolt2_3sigma)>-3 or max(cellvolt2_3sigma)<3):
+                    cellvoltvalid=1
+                    time=self.bmstime[i]
+                    self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C208'].index, 'end_time'] = time
+                else:
+                    pass
+        
+            if cellvoltvalid==1:
+                #继电器粘连.............................................................................................................................................
+                if not 'C310' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if cellvoltmax2>self.param.CellOvLv2 and cellvoltmax1>self.param.CellOvLv2 and self.packcrnt[i]<0:
+                        voltover_time=voltover_time+(time2-time1).total_seconds()
+                        if voltover_time>self.param.volt_time:
+                            time=self.bmstime[i]
+                            code='C310'
+                            faultlv=3
+                            faultinfo='充电继电器粘连'
+                            faultadvice='通知技术人员介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        voltover_time=0
+                else:
+                    if cellvoltmax2<self.param.CellOvLv2-0.05 and cellvoltmax1<self.param.CellOvLv2-0.05:
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C310'].index, 'end_time'] = time
+                    else:
+                        pass
+            
+                if not 'C311' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if cellvoltmin2<self.param.CellUvLv2 and cellvoltmin1<self.param.CellUvLv2 and self.packcrnt[i]>0:
+                        voltunder_time=voltunder_time+(time2-time1).total_seconds()
+                        if voltunder_time>self.param.volt_time:
+                            time=self.bmstime[i]
+                            code='C311'
+                            faultlv=3
+                            faultinfo='放电继电器粘连'
+                            faultadvice='通知技术人员介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        voltunder_time=0
+                else:
+                    if cellvoltmin2>self.param.CellUvLv2+0.05 and cellvoltmin1>self.param.CellOvLv2+0.05:
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C311'].index, 'end_time'] = time
+                    else:
+                        pass
+                
+                #电芯过压.............................................................................................................................................
+                if not 'C501' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if cellvoltmax2>self.param.CellOvLv2 and cellvoltmax1>self.param.CellOvLv2 and self.packcrnt[i]<0:  #二级过压进入
+                        cov_time=cov_time+(time2-time1).total_seconds()
+                        if cov_time>self.param.volt_time:
+                            time=self.bmstime[i]
+                            code='C501'
+                            faultlv=5
+                            faultinfo='电芯{}过压'.format(cellvolt1.index(cellvoltmax1)+1)
+                            faultadvice='联系用户立即停止充电,并通知技术运维人员'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        cov_time=0
+                else:   #ram当前故障中有该故障,则判断是否退出该故障
+                    if cellvoltmax2<self.param.CellOvLv1-0.05 and cellvoltmax1<self.param.CellOvLv1-0.05:   #二级过压故障恢复
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C501'].index, 'end_time'] = time
+                    else:
+                        pass
+                
+                #欠压诊断.................................................................................................................
+                if not 'C202' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if cellvoltmin2<self.param.CellUvLv2 and cellvoltmin1<self.param.CellUvLv2:  #二级欠压
+                        cuv_time=cuv_time+(time2-time1).total_seconds()
+                        if cuv_time>self.param.volt_time:
+                            time=self.bmstime[i]
+                            code='C202'
+                            faultlv=2
+                            faultinfo='电芯{}欠压'.format(cellvolt1.index(cellvoltmin1)+1)
+                            faultadvice='联系用户询问用车场景,技术介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        cuv_time=0
+                else:
+                    if cellvoltmin2>self.param.CellUvLv1+0.1 and cellvoltmin1>self.param.CellUvLv1+0.1:
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C202'].index, 'end_time'] = time
+                    else:
+                        pass
+             
+                #电芯压差大.....................................................................................................................................................
+                if not 'C104' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if (cellvoltmax2-cellvoltmin2)>self.param.CellVoltDiffLv2 and (cellvoltmax1-cellvoltmin1)>self.param.CellVoltDiffLv2:  #二级电芯压差
+                        cdv_time=cdv_time+(time2-time1).total_seconds()
+                        if cdv_time>self.param.volt_time:
+                            time=self.bmstime[i]
+                            code='C104'
+                            faultlv=0
+                            faultinfo='电芯{}和{}压差大'.format(cellvolt1.index(cellvoltmax1)+1,cellvolt1.index(cellvoltmin1)+1)
+                            faultadvice='技术介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        cdv_time=0
+                else:
+                    if (cellvoltmax2-cellvoltmin2)<self.param.CellVoltDiffLv1-0.05 and (cellvoltmax1-cellvoltmin1)<self.param.CellVoltDiffLv1-0.05: #二级欠压恢复
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C104'].index, 'end_time'] = time
+                    else:
+                        pass
+            else:
+                cov_time=0
+                cuv_time=0
+                cdv_time=0
+            
+            #电池包诊断.....................................................................................................................................
+            if i<1:
+                if not self.df_bms_ram.empty:
+                    packvolt1=self.df_bms_ram.iloc[-1]['packvolt']
+                    packvolt2=self.packvolt[i]
+                    time1=self.df_bms_ram.iloc[-1]['time']
+                    time2=self.bmstime[i]
+                else:
+                    packvolt1=self.packvolt[i]
+                    packvolt2=self.packvolt[i]
+                    time1=self.bmstime[i]
+                    time2=self.bmstime[i]
+            else:
+                packvolt1=self.packvolt[i-1]
+                packvolt2=self.packvolt[i]
+                time1=self.bmstime[i-1]
+                time2=self.bmstime[i]
+            if packvolt1<2*self.param.CellVoltNums or packvolt2>4.5*self.param.CellVoltNums:   #电池包电压有效性
+                packvoltvalid=0
+            else:
+                packvoltvalid=1
+
+            if packvoltvalid==1:
+                if not 'C502' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if packvolt1>self.param.PackVoltOvLv2 and packvolt2>self.param.PackVoltOvLv2 and self.packcrnt[i]<0:   #电池包过压二级进入
+                        pov_time=pov_time+(time2-time1).total_seconds()
+                        if pov_time>self.param.volt_time:
+                            time=self.bmstime[i]
+                            code='C502'
+                            faultlv=5
+                            faultinfo='电池包过压'
+                            faultadvice='联系用户立即停止充电,并通知技术运维人员'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        pov_time=0
+                else:
+                    if packvolt1<self.param.PackVoltOvLv1-0.05*self.param.CellVoltNums and packvolt2<self.param.PackVoltOvLv1-0.05*self.param.CellVoltNums: #电池包过压二级恢复
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C502'].index, 'end_time'] = time
+                    else:
+                        pass
+          
+                if not 'C203' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if packvolt1<self.param.PackVoltUvLv2 and packvolt2<self.param.PackVoltUvLv2:   #电池包二级欠压进入
+                        puv_time=puv_time+(time2-time1).total_seconds()
+                        if puv_time>self.param.volt_time:
+                            time=self.bmstime[i]
+                            code='C203'
+                            faultlv=2
+                            faultinfo='电池包欠压'
+                            faultadvice='联系用户询问用车场景,技术介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        puv_time=0
+                else:
+                    if packvolt1>self.param.PackVoltUvLv1+0.1*self.param.CellVoltNums and packvolt2>self.param.PackVoltUvLv1+0.1*self.param.CellVoltNums:   #电池包二级欠压恢复
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C203'].index, 'end_time'] = time
+                    else:
+                        pass
+            else:
+                pov_time=0
+                puv_time=0 
+            
+            #温度诊断功能.............................................................................................................
+            if i<1:
+                if not self.df_bms_ram.empty:
+                    celltemp1=self.df_bms_ram.iloc[-1]['celltemp']
+                    celltemp2=self._celltemp_get(i)
+                    time1=self.df_bms_ram.iloc[-1]['time']
+                    time2=self.bmstime[i]
+                else:
+                    celltemp1=self._celltemp_get(i)
+                    celltemp2=self._celltemp_get(i)
+                    time1=self.bmstime[i]
+                    time2=self.bmstime[i]
+            else:
+                celltemp1=self._celltemp_get(i-1)
+                celltemp2=self._celltemp_get(i)
+                time1=self.bmstime[i-1]
+                time2=self.bmstime[i]
+            celltempmin1=min(celltemp1)
+            celltempmin2=min(celltemp2)
+            celltempmax1=max(celltemp1)
+            celltempmax2=max(celltemp2)
+            #温度有效性判断..........................................................................
+            if celltempmax1>self.param.CellTempUpLmt or celltempmin1<self.param.CellTempLwLmt:
+                celltempvalid=0
+            else:  
+                celltempvalid=1
+           
+            if celltempvalid==1:
+                #过温判断.............................................................................................................
+                if not 'C302' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if celltempmax1>self.param.CellTempHighLv2 and celltempmax2>self.param.CellTempHighLv2:    #二级高温进入
+                        ot_time=ot_time+(time2-time1).total_seconds()
+                        if ot_time>self.param.temp_time:
+                            time=self.bmstime[i]
+                            code='C302'
+                            faultlv=3
+                            faultinfo='温度{}高温'.format(celltemp1.index(celltempmax1)+1)
+                            faultadvice='技术介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        ot_time=0
+                else:   #ram当前故障中有该故障,则判断是否退出该故障
+                    if celltempmax1<self.param.CellTempHighLv1-5 and celltempmax2<self.param.CellTempHighLv1-5:    #二级高温恢复
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C302'].index, 'end_time'] = time
+                    else:
+                        pass
+            
+                #欠温判断.................................................................................................................
+                if not 'C102' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if celltempmin1<self.param.CellTempLowLv2 and celltempmin2<self.param.CellTempLowLv2:  #二级低温进入
+                        ut_time=ut_time+(time2-time1).total_seconds()
+                        if ut_time>self.param.temp_time:
+                            time=self.bmstime[i]
+                            code='C102'
+                            faultlv=1
+                            faultinfo='温度{}低温'.format(celltemp1.index(celltempmin1)+1)
+                            faultadvice='技术介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        ut_time=0
+                else:   #ram当前故障中有该故障,则判断是否退出该故障
+                    if celltempmax1>self.param.CellTempLowLv1+2 and celltempmax2>self.param.CellTempLowLv1+2:    #二级高温恢复
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C102'].index, 'end_time'] = time
+                    else:
+                        pass
+              
+                #温差判断.............................................................................................................................
+                if not 'C103' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if (celltempmax1-celltempmin1)>self.param.CellTempDiffLv2 and (celltempmax2-celltempmin2)>self.param.CellTempDiffLv2:  #二级温差进入
+                        dt_time=dt_time+(time2-time1).total_seconds()
+                        if dt_time>self.param.temp_time:
+                            time=self.bmstime[i]
+                            code='C103'
+                            faultlv=1
+                            faultinfo='温度{}和{}温差大'.format(celltemp1.index(celltempmax1)+1,celltemp1.index(celltempmin1)+1)
+                            faultadvice='技术介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        dt_time=0
+                else:   #ram当前故障中有该故障,则判断是否退出该故障
+                    if (celltempmax1-celltempmin1)<self.param.CellTempDiffLv1-2 and (celltempmax2-celltempmin2)>self.param.CellTempDiffLv1-2:  #二级温差恢复
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C103'].index, 'end_time'] = time
+                    else:
+                        pass
+            
+            else:
+                ot_time=0
+                ut_time=0
+                dt_time=0
+            
+            #电流过流诊断.......................................................................................................................
+            if i>0.5:
+                step=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                if step<120 and self.packcrnt[i]>self.param.PackDisOc and self.packcrnt[i-1]>self.param.PackDisOc:
+                    as_dis=as_dis+(self.packcrnt[i]-self.param.PackDisOc)*step    #ah累计
+                elif step<120 and self.packcrnt[i]<self.param.PackChgOc and self.packcrnt[i-1]<self.param.PackChgOc:
+                    as_chg=as_chg+(self.param.PackDisOc-self.packcrnt[i])*step    #ah累计
+                else:
+                    as_dis=0
+                    as_chg=0
+                
+                if not 'C306' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if as_dis>100:
+                        time=self.bmstime[i]
+                        code='C306'
+                        faultlv=3
+                        faultinfo='电池包放电过流'
+                        faultadvice='联系用户询问用车场景,技术介入诊断'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                    else:
+                        pass
+                else:
+                    if self.packcrnt[i]<self.param.PackDisOc-10:
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C306'].index, 'end_time'] = time
+                    else:
+                        pass
+                
+                if not 'C305' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if as_chg>100:
+                        time=self.bmstime[i]
+                        code='C305'
+                        faultlv=3
+                        faultinfo='电池包充电过流'
+                        faultadvice='联系用户询问用车场景,技术介入诊断'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                    else:
+                        pass
+                else:
+                    if self.packcrnt[i]>self.param.PackChgOc+10:
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C305'].index, 'end_time'] = time
+                    else:
+                        pass
+            
+            #SOC卡滞、跳变诊断................................................................................................
+            if i<1:
+                bmssoc_st=float(self.bms_soc[i])
+                bmssoc_last=float(self.bms_soc[i])
+                bmssoc_now=float(self.bms_soc[i])
+            else:
+                step=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                if step<120:
+                    ah_accum=ah_accum-self.packcrnt[i]*step/3600    #ah累计
+                else:
+                    pass
+                #SOC卡滞............................................................................................................
+                if abs(ah_accum)>self.param.Capacity*0.1:   
+                    bmssoc_now=float(self.bms_soc[i])
+                    if not 'C106' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                        if abs(bmssoc_now-bmssoc_st)<self.param.SocClamp:   #SOC卡滞故障进入
+                            time=self.bmstime[i]
+                            code='C106'
+                            faultlv=0
+                            faultinfo='电池SOC卡滞'
+                            faultadvice='技术介入诊断,检修电池BMS软件'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        if abs(bmssoc_now-bmssoc_st)>self.param.SocClamp:   #SOC卡滞故障退出
+                            time=self.bmstime[i]
+                            self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C106'].index, 'end_time'] = time
+                        else:
+                            pass
+                    bmssoc_st=bmssoc_now
+                    ah_accum=0
+                else:
+                    pass
+
+                #SOC跳变....................................................................................................................
+                bmssoc_last=float(self.bms_soc[i-1])
+                bmssoc_now=float(self.bms_soc[i])
+                if not 'C107' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if step<70 and abs(bmssoc_now-bmssoc_last)>self.param.SocJump:  #SOC跳变进入
+                        time=self.bmstime[i]
+                        code='C107'
+                        faultlv=0
+                        faultinfo='电池SOC跳变{}%'.format(bmssoc_now-bmssoc_last)
+                        faultadvice='技术介入诊断,检修电池BMS软件'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                    else:
+                        pass
+                else:
+                    if abs(bmssoc_now-bmssoc_st)<self.param.SocJump:    #SOC跳变故障退出
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C107'].index, 'end_time'] = time
+                    else:
+                        pass
+        
+        #SOC一致性故障报警..........................................................................................................
+        if not self.df_uniform.empty:
+            cellsoc_diff=self.df_uniform.loc[0,'cellsoc_diff']
+            if not 'C201' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if cellsoc_diff>self.param.SocDiff: #SOC一致性差故障进入
+                    time=self.bmstime[0]
+                    code='C201'
+                    faultlv=0
+                    faultinfo='电芯{}和{}SOC差过大:{}'.format(int(self.df_uniform.loc[0,'cellmin_num']),int(self.df_uniform.loc[0,'cellmax_num']),cellsoc_diff)
+                    faultadvice='技术介入诊断'
+                    self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                else:
+                    pass
+            else:
+                if cellsoc_diff<self.param.SocDiff: #SOC一致性差故障恢复
+                    time=self.bmstime[0]
+                    self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C201'].index, 'end_time'] = time
+        else:
+            cellsoc_diff=3
+
+        #容量过低和一致性故障报警................................................................................................
+        if not self.df_soh.empty:
+            soh=self.df_soh.loc[0,'soh']
+            cellsoh=eval(self.df_soh.loc[0,'cellsoh'])
+            cellsoh=np.array(cellsoh)
+            cellsoh_lowindex=np.argwhere(cellsoh<self.param.SohLow)
+            cellsoh_lowindex=cellsoh_lowindex+1
+            if self.celltype==1 or self.celltype==2 or self.celltype==3 or self.celltype==4: 
+                cellsoh_diff=np.max(cellsoh)-np.min(cellsoh)
+                if not 'C204' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if soh<self.param.SohLow:   #soh过低故障进入
+                        time=self.bmstime[0]
+                        code='C204'
+                        faultlv=2
+                        faultinfo='电池包容量过低:电芯{}'.format(cellsoh_lowindex)
+                        faultadvice='检修电池,更换容量过低的电芯或模组'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                    else:
+                        pass
+                else:
+                    if soh>self.param.SohLow+2:   #soh过低故障恢复
+                        time=self.bmstime[0]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C204'].index, 'end_time'] = time
+                    else:
+                        pass
+
+                if not 'C205' in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if cellsoh_diff>self.param.SohDiff:
+                        time=self.bmstime[0]
+                        code='C205'
+                        faultlv=2
+                        faultinfo='电池包容量一致性差:电芯{}'.format(cellsoh_lowindex)
+                        faultadvice='检修电池,更换容量过低的电芯或模组'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                    else:
+                        pass
+                else:
+                    if cellsoh_diff<self.param.SohDiff-2:
+                        time=self.bmstime[0]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C205'].index, 'end_time'] = time
+                    else:
+                        pass
+            else:
+                pass
+        else:
+            cellsoh_diff=5
+        
+        #ram更新.......................................................................
+        df_bms_ram=pd.DataFrame(columns=['time', 'sn', 'packvolt', 'cellvolt', 'celltemp'])
+        df_bms_ram.loc[0]=[self.bmstime[0], self.sn, packvolt2, cellvolt2, celltemp2]
+
+        return self.df_diag_ram, df_bms_ram
+

+ 164 - 0
LIB/MIDDLE/CellStateEstimation/BatDiag/main.py

@@ -0,0 +1,164 @@
+import CBMSBatDiag
+import pymysql
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager
+from sqlalchemy import create_engine
+from urllib import parse
+import datetime, time
+from apscheduler.schedulers.blocking import BlockingScheduler
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import DBDownload
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import log
+
+#...................................电池包电芯安全诊断函数......................................................................................................................
+def diag_cal():
+    global SNnums, df_bms_ram
+
+    start=time.time()
+    now_time=datetime.datetime.now()
+    start_time=now_time-datetime.timedelta(seconds=310)
+    start_time=start_time.strftime('%Y-%m-%d %H:%M:%S')
+    end_time=now_time.strftime('%Y-%m-%d %H:%M:%S')
+
+    #数据库配置
+    host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port=3306
+    db='safety_platform'
+    user='qx_read'
+    password='Qx@123456'
+
+    #读取故障结果库中当前故障......................................................
+    param='start_time,end_time,product_id,code,level,info,advice'
+    tablename='all_fault_info_copy'
+    mysql = pymysql.connect (host=host, user=user, password=password, port=port, database=db)
+    cursor = mysql.cursor()
+    sql =  "select %s from %s where end_time='0000-00-00 00:00:00'" %(param,tablename)
+    cursor.execute(sql)
+    res = cursor.fetchall()
+    df_diag_ram= pd.DataFrame(res,columns=param.split(','))
+    
+
+    db_res_engine = create_engine(
+        "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+            user, parse.quote_plus(password), host, port, db
+        ))
+
+    mylog=log.Mylog('log_diag.txt','error')
+    mylog.logcfg()
+
+    for sn in SNnums:
+        try:
+            if 'PK500' in sn:
+                celltype=1 #6040三元电芯
+            elif 'PK502' in sn:
+                celltype=2 #4840三元电芯
+            elif 'K504B' in sn:
+                celltype=99    #60ah林磷酸铁锂电芯
+            elif 'MGMLXN750' in sn:
+                celltype=3 #力信50ah三元电芯
+            elif ('MGMCLN750' in sn) or ('UD' in sn): 
+                celltype=4 #CATL 50ah三元电芯
+            else:
+                print('SN:{},未找到对应电池类型!!!'.format(sn))
+                continue
+                # sys.exit()
+
+            #读取原始数据库数据........................................................................................................................................................
+            dbManager = DBManager.DBManager()
+            df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms'])
+            df_bms = df_data['bms']
+
+            #读取结果数据库数据........................................................................................................................................................
+            db='qx_cas'
+            mode=1
+            tablename1='cellstateestimation_soh'
+            tablename2='cellstateestimation_uniform_socvoltdiff'
+            # tablename3='cellstateestimation_soc'
+            DBRead=DBDownload.DBDownload(host, port, db, user, password,mode)
+            with DBRead as DBRead:
+                df_soh=DBRead.getdata('time_st,sn,soh,cellsoh', tablename=tablename1, sn=sn, timename='time_sp', st=start_time, sp=end_time)
+                df_uniform=DBRead.getdata('time,sn,cellsoc_diff,cellmin_num,cellmax_num', tablename=tablename2, sn=sn, timename='time', st=start_time, sp=end_time)
+                # df_soc=DBRead.getdata('time','sn','packsoc', tablename=tablename3, sn=sn)
+
+            #电池诊断................................................................................................................................................................
+            if not df_bms.empty:
+                df_diag_ram_sn=df_diag_ram[df_diag_ram['product_id']==sn]
+                df_diag_ram_sn.reset_index(inplace=True,drop=True)
+                df_bms_ram_sn=df_bms_ram[df_bms_ram['sn']==sn]
+                df_bms_ram_sn.reset_index(inplace=True,drop=True)
+                
+                df_bms=df_bms.dropna(axis=0,subset=['总电流[A]','SOH[%]','SOC[%]','总电压[V]']) #去除有空值的行
+                df_bms.reset_index(inplace=True,drop=True)     #重置索引
+
+                batquality=CBMSBatDiag.BatDiag(sn,celltype,df_bms,df_diag_ram_sn,df_bms_ram_sn,df_soh,df_uniform)
+                df_diag_res, df_bms_res=batquality.diag()   #获取电池故障结果和电池评分结果
+
+                #更新bms的ram数据
+                sn_index=df_bms_ram.loc[df_bms_ram['sn']==sn].index
+                df_bms_ram=df_bms_ram.drop(index=sn_index)
+                df_bms_ram=df_bms_ram.append(df_bms_res)
+
+                #历史故障筛选并更改数据库故障结束时间.........................................................
+                if not df_diag_res.empty:
+                    df_diag_new = pd.concat([df_diag_res,df_diag_ram_sn,df_diag_ram_sn]).drop_duplicates(subset=['start_time','code'],keep=False)#此次判断中新增故障
+                    df_diag_new.reset_index(inplace=True,drop=True)  #重置索引
+                    df_diag_end=pd.concat([df_diag_res,df_diag_new,df_diag_new]).drop_duplicates(subset=['start_time','code'],keep=False)#此次判断中新增故障
+                    df_diag_end=df_diag_end[df_diag_end['end_time'] != '0000-00-00 00:00:00']
+                    df_diag_end.reset_index(inplace=True,drop=True)  #重置索引
+
+                    if not df_diag_end.empty:   #变为历史故障更改数据库
+                        for j in range(len(df_diag_end)):
+                            cursor.execute('''
+                                        update all_fault_info_copy set update_time='{}',end_time='{}', Batpos={} where product_id='{}' and end_time='0000-00-00 00:00:00' and code='{}'
+                                        '''.format(datetime.datetime.now(), df_diag_end.loc[j,'end_time'], 1 ,sn, df_diag_end.loc[j,'code']))
+                            mysql.commit()
+
+                    #新增故障筛选并存入数据库.....................................................................
+                    if not df_diag_new.empty:  #新增写入数据库
+                        df_diag_new.to_sql("all_fault_info_copy",con=db_res_engine, if_exists="append",index=False)
+
+            end=time.time()
+            print(end-start)  
+            
+        except Exception as e:
+            print(repr(e))
+            mylog.logopt(e)
+
+    cursor.close()
+    mysql.close()
+
+#...............................................主函数起定时作用.......................................................................................................................
+if __name__ == "__main__":
+    
+    excelpath=r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\sn-20210903.xlsx'
+    SNdata_6060 = pd.read_excel(excelpath, sheet_name='科易6060')
+    SNdata_6040 = pd.read_excel(excelpath, sheet_name='科易6040')
+    SNdata_4840 = pd.read_excel(excelpath, sheet_name='科易4840')
+    SNdata_L7255 = pd.read_excel(excelpath, sheet_name='格林美-力信7255')
+    SNdata_C7255 = pd.read_excel(excelpath, sheet_name='格林美-CATL7255')
+    SNdata_U7255 = pd.read_excel(excelpath, sheet_name='优旦7255')
+    SNnums_6060=SNdata_6060['SN号'].tolist()
+    SNnums_6040=SNdata_6040['SN号'].tolist()
+    SNnums_4840=SNdata_4840['SN号'].tolist()
+    SNnums_L7255=SNdata_L7255['SN号'].tolist()
+    SNnums_C7255=SNdata_C7255['SN号'].tolist()
+    SNnums_U7255=SNdata_U7255['SN号'].tolist()
+    SNnums=SNnums_L7255 + SNnums_C7255 + SNnums_U7255 + SNnums_6040 + SNnums_4840 + SNnums_6060
+    
+    mylog=log.Mylog('log_diag.txt','error')
+    mylog.logcfg()
+
+    #参数初始化
+    df_bms_ram=pd.DataFrame(columns=['time', 'sn', 'packvolt', 'cellvolt', 'celltemp'])
+
+    diag_cal()
+    #定时任务.......................................................................................................................................................................
+    scheduler = BlockingScheduler()
+    scheduler.add_job(diag_cal, 'interval', seconds=300)
+
+    try:  
+        scheduler.start()
+    except Exception as e:
+        scheduler.shutdown()
+        print(repr(e))
+        mylog.logopt(e)

+ 262 - 0
LIB/MIDDLE/CellStateEstimation/BatSafetyAlarm/V1_0_1/CBMSSafetyAlarm.py

@@ -0,0 +1,262 @@
+import pandas as pd
+import numpy as np
+import datetime
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam
+
+class SafetyAlarm:
+    def __init__(self,sn,celltype,df_bms,df_bms_ram_sn,df_alarm_ram_sn):  #参数初始化
+
+        self.sn=sn
+        self.celltype=celltype
+        self.param=BatParam.BatParam(celltype)
+        self.df_bms=df_bms
+        self.df_ram_bms=df_bms_ram_sn.copy()
+        df_bms['时间戳']=pd.to_datetime(df_bms['时间戳'], format='%Y-%m-%d %H:%M:%S')
+        self.df_ram_alarm=df_alarm_ram_sn.copy()
+
+        if (not df_bms_ram_sn.empty) and (not self.df_bms.empty):
+            self.df_bms=self.df_bms[self.df_bms['时间戳'] > df_bms_ram_sn.iloc[-1]['time']]    #滤除原始数据中的重复数据
+            self.df_bms.reset_index(inplace=True,drop=True)    #重置索引
+        
+        self.packcrnt=df_bms['总电流[A]']*self.param.PackCrntDec
+        self.packvolt=df_bms['总电压[V]']
+        self.bmstime= df_bms['时间戳']
+
+        self.cellvolt_name=['单体电压'+str(x) for x in range(1,self.param.CellVoltNums+1)]
+        # othertemp=['其他温度'+str(x) for x in range(1,self.param.OtherTempNums+1)]
+        self.celltemp_name=['单体温度'+str(x) for x in range(1,self.param.CellTempNums+1)]
+        # self.celltemp_name=celltemp+othertemp
+
+    
+    def safety_alarm_diag(self):
+        if self.celltype<=50:
+            df_res=self._alarm_diag()
+            return df_res    
+        else:
+            df_res=self._alarm_diag()
+            return df_res
+        
+
+    #定义滑动滤波函数.............................................................................................
+    def _np_move_avg(self,a, n, mode="same"): 
+        return (np.convolve(a, np.ones((n,)) / n, mode=mode))
+    
+    #寻找当前行数据的所有温度值...................................................................................
+    def _celltemp_get(self,num):   
+        celltemp = np.array(self.df_bms.loc[num,self.celltemp_name])
+        return celltemp
+
+    #获取当前行所有电压数据............................................................................................
+    def _cellvolt_get(self,num): 
+        cellvolt = np.array(self.df_bms.loc[num,self.cellvolt_name]/1000)
+        return cellvolt
+
+    #..........................................三元电池诊断功能..................................................................
+    def _alarm_diag(self):
+
+        df_res=pd.DataFrame(columns=['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice'])
+
+        end_time='0000-00-00 00:00:00'
+        time_now=datetime.datetime.now()
+        celltemprise=0
+        celltemphigh=0
+        cellvoltfall=0
+        packvoltfall=0
+
+        if not self.df_ram_alarm.empty:
+            safetywarning1=self.df_ram_alarm.iloc[-1]['safetywarning1']
+            safetywarning2=self.df_ram_alarm.iloc[-1]['safetywarning2']
+        else:
+            safetywarning1=0
+            safetywarning2=0
+
+        if not self.df_bms.empty:
+            for i in range(len(self.df_bms)):
+                #温度诊断功能.............................................................................................................
+                if i<1:
+                    if not self.df_ram_bms.empty:
+                        temp1=self.df_ram_bms.iloc[-1]['celltemp']
+                        temp2=self._celltemp_get(i)
+                        time1=self.df_ram_bms.iloc[-1]['time']
+                        time2=self.bmstime[i]
+                    else:
+                        temp1=self._celltemp_get(i)
+                        temp2=self._celltemp_get(i)
+                        time1=self.bmstime[i]-datetime.timedelta(seconds=10)
+                        time2=self.bmstime[i]
+                else:
+                    temp1=self._celltemp_get(i-1)
+                    temp2=self._celltemp_get(i)
+                    time1=self.bmstime[i-1]
+                    time2=self.bmstime[i]
+                
+                temp1=temp1[np.where((temp1<self.param.CellTempUpLmt) & (temp1>self.param.CellTempLwLmt))]
+                temp2=temp2[np.where((temp2<self.param.CellTempUpLmt) & (temp2>self.param.CellTempLwLmt))]
+                
+                #温度有效性判断...........................................................................
+                if len(temp1)>0.5 and len(temp2)>0.5 and len(temp1)==len(temp2):
+                    celltempvalid=1
+                else:   #不作处理
+                    celltempvalid=0
+
+                if celltempvalid==1:
+                    celltempmax=max(temp2)
+                    #过温判断............................................................................................................................
+                    if celltempmax>self.param.TrwTempHigh:
+                        celltemphigh=1
+                    else:
+                        pass
+                
+                    
+                    #温升判断.............................................................................................................................
+                    delttime=(time2-time1).total_seconds()
+                    if delttime>1:
+                        celltemp_rate=(max(temp2-temp1)*60)/delttime    #计算最大温升速率
+                    else:
+                        celltemp_rate=0
+                    if celltemp_rate>self.param.TrwTempRate and self.param.CellTempLwLmt<min(temp1) and max(temp1)<self.param.CellTempUpLmt:
+                        celltemprise=celltemprise+1
+                    else:
+                        pass
+                
+                else:
+                    pass
+                
+                #电压诊断功能.........................................................................................................................................
+                if i<1:
+                    cellvolt2=self._cellvolt_get(i)
+                    time2=self.bmstime[i]
+                    if not self.df_ram_bms.empty:
+                        cellvolt1=self.df_ram_bms.iloc[-1]['cellvolt']
+                        time1=self.df_ram_bms.iloc[-1]['time']
+                    else:
+                        cellvolt1=cellvolt2
+                        time1=time2
+                else:
+                    cellvolt2=self._cellvolt_get(i)
+                    cellvolt1=self._cellvolt_get(i-1)
+                    time2=self.bmstime[i]
+                    time1=self.bmstime[i-1]
+                    
+
+                #电压有效性..........................................................................................................................................
+                cellvoltmin2=min(cellvolt2)
+                cellvoltmax2=max(cellvolt2)
+                cellvoltmin_index2=list(cellvolt2).index(cellvoltmin2)
+                cellvoltmax_index2=list(cellvolt2).index(cellvoltmax2)
+                cellvoltmin1=min(cellvolt1)
+                cellvoltmax1=max(cellvolt1)
+                cellvoltmin_index1=list(cellvolt1).index(cellvoltmin1)
+                cellvoltmax_index1=list(cellvolt1).index(cellvoltmax1)
+                
+                if (cellvoltmin2<2 and cellvoltmax2>4.5 and abs(cellvoltmax_index2-cellvoltmin_index2)==1) or (cellvoltmin1<2 and cellvoltmax1>4.5 and abs(cellvoltmax_index1-cellvoltmin_index1)==1):   #电压断线故障进入
+                    cellvoltvalid=0
+                else:
+                    cellvolt1=cellvolt1[np.where((cellvolt1>0.01) & (cellvolt1<4.5))]
+                    cellvolt2=cellvolt2[np.where((cellvolt2>0.01) & (cellvolt2<4.5))]
+                    if len(cellvolt1)>1 and len(cellvolt2)>1 and len(cellvolt1)==len(cellvolt2):
+                        cellvoltvalid=1
+                    else:
+                        cellvoltvalid=0
+                
+                if cellvoltvalid==1:
+                    #单体电压跌落诊断...........................................................................................................................
+                    delttime=(time2-time1).total_seconds()
+                    if delttime<360:
+                        if self.packcrnt[i]<0.5 and max(cellvolt1-cellvolt2)>self.param.TrwCellVoltFall:
+                            cellvoltfall=cellvoltfall+1
+                        elif self.packcrnt[i]>0.5 and max(cellvolt1-cellvolt2)-self.packcrnt[i]*0.01>self.param.TrwCellVoltFall:
+                            cellvoltfall=cellvoltfall+1
+                        else:
+                            pass
+                    else:
+                        pass
+                else:
+                    pass
+                    
+                #电池包诊断.....................................................................................................................................
+                packvolt2=self.packvolt[i]
+                #电池包电压有效性............................................................................................................................
+                if self.param.CellVoltNums<packvolt2<self.param.CellVoltNums*4.5:
+                    packvoltvalid=1
+                else:
+                    packvoltvalid=0
+                
+                if packvoltvalid==1:
+                    if i<1:
+                        if not self.df_ram_bms.empty:
+                            time1=self.df_ram_bms.iloc[-1]['time']
+                            time2=self.bmstime[i]
+                            delttime=(time2-time1).total_seconds()
+                            packvolt1=self.df_ram_bms.iloc[-1]['packvolt']
+                        
+                            if delttime<360:
+                                if self.packcrnt[i]<5 and (packvolt1-packvolt2)>self.param.TrwPackVoltFall and self.param.CellVoltNums<packvolt1<self.param.CellVoltNums*4.5:
+                                    packvoltfall=packvoltfall+1
+                                else:
+                                    pass
+                            else:
+                                pass
+                        else:
+                            pass
+                    else:
+                        packvolt1=self.packvolt[i-1]
+                        if self.packcrnt[i]<5 and (packvolt1-packvolt2)>self.param.TrwPackVoltFall and self.param.CellVoltNums<packvolt1<self.param.CellVoltNums*4.5:
+                            packvoltfall=packvoltfall+1
+                        else:
+                            pass
+                else:
+                    pass
+            
+             #热失控故障判断........................................................................................................................
+            df_bms_ram=pd.DataFrame(columns=['time', 'sn', 'packvolt',  'cellvolt', 'celltemp'])
+            df_bms_ram.loc[0]=[self.bmstime[0], self.sn, packvolt2, cellvolt2, temp2]
+            df_ram_alarm=self.df_ram_alarm
+            trwtemp=max(celltemprise,celltemphigh)
+            trwcellvolt=cellvoltfall
+            trwpackvolt=packvoltfall
+            trw_array=np.array([trwtemp, trwcellvolt, trwpackvolt])
+
+            if np.sum(trw_array)>3.5 and np.sum(trw_array>0.5)>1.5:
+                fltcode='C599'
+                df_res.loc[0]=[self.bmstime[len(self.bmstime)-1], end_time, self.sn, fltcode, 5, '电池发生热失控', '联系用户立即远离电池,并通知技术人员介入']
+                df_ram_alarm=pd.DataFrame(columns=['sn','time','safetywarning1','safetywarning2'])
+            elif safetywarning1>2.5 and np.sum(trw_array)>1.5 and np.sum(trw_array>0.5)>1.5:
+                fltcode='C599'
+                df_res.loc[0]=[self.bmstime[len(self.bmstime)-1], end_time, self.sn, fltcode, 5, '电池发生热失控', '联系用户立即远离电池,并通知技术人员介入']
+                df_ram_alarm=pd.DataFrame(columns=['sn','time','safetywarning1','safetywarning2'])
+            else:
+                df_res=pd.DataFrame()
+                #更新df_ram_alarm信息
+                if np.sum(trw_array)>1.5 and np.sum(trw_array>0.5)>1.5:
+                    safetywarning1=3
+                    df_ram_alarm.loc[0]=[self.sn,time2,safetywarning1,safetywarning2]
+                else:
+                    safetywarning1=0
+                    df_ram_alarm=pd.DataFrame(columns=['sn','time','safetywarning1','safetywarning2'])
+                if max(cellvolt2)>5 or min(cellvolt2)<1:
+                    safetywarning2=3
+                    df_ram_alarm.loc[0]=[self.sn,time2,safetywarning1,safetywarning2]
+                elif max(cellvolt2)<4.5 and min(cellvolt2)>2.5:
+                    safetywarning2=0
+                    if safetywarning1==0:
+                        df_ram_alarm=pd.DataFrame(columns=['sn','time','safetywarning1','safetywarning2'])
+                    else:
+                        df_ram_alarm.loc[0]=[self.sn,time2,safetywarning1,safetywarning2]
+                else:
+                    df_ram_alarm.loc[0]=[self.sn,time2,safetywarning1,safetywarning2]
+            return df_res, df_bms_ram, df_ram_alarm
+            
+        else:
+            df_ram_alarm=self.df_ram_alarm
+            df_ram_bms=self.df_ram_bms
+            if (safetywarning1>2.5 or safetywarning2>2.5) and (time_now-df_ram_alarm.iloc[-1]['time']).total_seconds()>120:
+                fltcode='C599'
+                time_now=time_now.strftime('%Y-%m-%d %H:%M:%S')
+                time_now=datetime.datetime.strptime(time_now,'%Y-%m-%d %H:%M:%S')
+                df_res.loc[0]=[time_now, end_time, self.sn, fltcode, 5, '电池发生热失控', '联系用户立即远离电池,并通知技术人员介入']
+                df_ram_alarm=pd.DataFrame(columns=['sn','time','safetywarning1','safetywarning2'])
+            else:
+                df_res=pd.DataFrame()
+            return df_res, df_ram_bms, df_ram_alarm
+        

+ 159 - 0
LIB/MIDDLE/CellStateEstimation/BatSafetyAlarm/main.py

@@ -0,0 +1,159 @@
+import CBMSSafetyAlarm
+import pymysql
+import datetime
+import pandas as pd
+import multiprocessing
+from LIB.BACKEND import DBManager, Log
+import time, datetime
+from apscheduler.schedulers.blocking import BlockingScheduler
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import log
+
+
+#...................................电池包电芯安全诊断函数......................................................................................................................
+def diag_cal(sn_list, df_bms_ram, df_alarm_ram):
+
+    start=time.time()
+    now_time=datetime.datetime.now() #-datetime.timedelta(seconds=3600*24+3600*14.6)
+    start_time=now_time-datetime.timedelta(seconds=70)
+    start_time=start_time.strftime('%Y-%m-%d %H:%M:%S')
+    end_time=now_time.strftime('%Y-%m-%d %H:%M:%S')
+
+    #数据库配置
+    host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port=3306
+    user='qx_algo_readonly'
+    password = 'qx@123456'
+
+    #读取故障结果库中code=='C599'且end_time='0000-00-00 00:00:00'...............................
+    db='safety_platform'
+    mysql = pymysql.connect (host=host, port=port, user=user, password=password, database=db)
+    cursor = mysql.cursor()
+    param='start_time,end_time,product_id,code,level,info,advice'
+    tablename='all_fault_info'
+    sql =  "select %s from %s where code='C599' and end_time='0000-00-00 00:00:00'" %(param,tablename)
+    cursor.execute(sql)
+    res = cursor.fetchall()
+    df_diag_ram= pd.DataFrame(res,columns=param.split(','))
+    cursor.close()
+    mysql.close()
+
+    for sn in sn_list:
+        if 'PK500' in sn:
+            celltype=1 #6040三元电芯
+        elif 'PK502' in sn:
+            celltype=2 #4840三元电芯
+        elif 'K504B' in sn:
+            celltype=99    #60ah林磷酸铁锂电芯
+        elif 'MGMLXN750' in sn:
+            celltype=3 #力信50ah三元电芯
+        elif 'MGMCLN750' or 'UD' in sn: 
+            celltype=4 #CATL 50ah三元电芯
+        else:
+            print('SN:{},未找到对应电池类型!!!'.format(sn))
+            continue
+            # sys.exit()
+
+        #读取原始数据库数据........................................................................................................................................................
+        dbManager = DBManager.DBManager()
+        df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms'])
+        df_bms = df_data['bms']
+        # print(df_bms)
+
+        #电池诊断................................................................................................................................................................
+        df_diag_ram_sn=df_diag_ram[df_diag_ram['product_id']==sn]
+        df_bms_ram_sn=df_bms_ram[df_bms_ram['sn']==sn]
+        df_alarm_ram_sn=df_alarm_ram[df_alarm_ram['sn']==sn]
+        if df_diag_ram_sn.empty:   
+            SafetyAlarm=CBMSSafetyAlarm.SafetyAlarm(sn,celltype,df_bms, df_bms_ram_sn, df_alarm_ram_sn)
+            df_diag_res, df_bms_res, df_ram_res=SafetyAlarm.safety_alarm_diag() 
+
+            #更新bms的ram数据
+            sn_index=df_bms_ram.loc[df_bms_ram['sn']==sn].index
+            df_bms_ram=df_bms_ram.drop(index=sn_index)
+            df_bms_ram=df_bms_ram.append(df_bms_res)
+
+            sn_index=df_alarm_ram.loc[df_alarm_ram['sn']==sn].index
+            df_alarm_ram=df_alarm_ram.drop(index=sn_index)
+            df_alarm_ram=df_alarm_ram.append(df_ram_res)
+
+            #当前热失控故障写入数据库
+            if not df_diag_res.empty:
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\06BatSafetyAlarm\热失控报警.txt','a') as file:
+                    file.write(str(tuple(df_diag_res.iloc[-1]))+'\n')
+              
+        #当前热失控已超过一天变为历史故障并更改数据库
+        else:
+            fault_time=datetime.datetime.strptime(df_diag_ram_sn.iloc[-1]['start_time'], '%Y-%m-%d %H:%M:%S')
+            if (now_time-fault_time).total_seconds()>24*3600:
+                df_diag_ram_sn['end_time']=end_time
+                df_diag_ram_sn['Batpos']=1
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\06BatSafetyAlarm\热失控报警.txt','a') as file:
+                    file.write(str(tuple(df_diag_ram_sn.iloc[-1]))+'\n')
+
+        end=time.time()
+        print(end-start)
+    
+    return df_bms_ram,df_alarm_ram
+#...................................................主进程...........................................................................................................
+def mainprocess():
+    global df_bms_ram1, df_bms_ram2, df_alarm_ram1, df_alarm_ram2, SNnums
+    process = 2
+    pool = multiprocessing.Pool(processes = process)
+
+    res_list=[]
+    ram_list1=[df_bms_ram1, df_bms_ram2]
+    ram_list2=[df_alarm_ram1, df_alarm_ram1]
+    for i in range(process):
+        sn_list = SNnums[i]
+        df_bms_ram=ram_list1[i]
+        df_alarm_ram=ram_list2[i]
+        df_res=pool.apply_async(diag_cal, (sn_list,df_bms_ram,df_alarm_ram)).get()
+        res_list.append(df_res)
+    
+    pool.close()
+    pool.join()
+    df_bms_ram1=res_list[0][0]
+    df_bms_ram2=res_list[1][0]
+    df_alarm_ram1=res_list[0][1]
+    df_alarm_ram2=res_list[1][1]
+
+
+#...............................................主函数起定时作用.......................................................................................................................
+if __name__ == "__main__":
+    
+    excelpath=r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\sn-20210903.xlsx'
+    SNdata_6060 = pd.read_excel(excelpath, sheet_name='科易6060')
+    SNdata_6040 = pd.read_excel(excelpath, sheet_name='科易6040')
+    SNdata_4840 = pd.read_excel(excelpath, sheet_name='科易4840')
+    SNdata_L7255 = pd.read_excel(excelpath, sheet_name='格林美-力信7255')
+    SNdata_C7255 = pd.read_excel(excelpath, sheet_name='格林美-CATL7255')
+    SNdata_U7255 = pd.read_excel(excelpath, sheet_name='优旦7255')
+    SNnums_6060=SNdata_6060['SN号'].tolist()
+    SNnums_6040=SNdata_6040['SN号'].tolist()
+    SNnums_4840=SNdata_4840['SN号'].tolist()
+    SNnums_L7255=SNdata_L7255['SN号'].tolist()
+    SNnums_C7255=SNdata_C7255['SN号'].tolist()
+    SNnums_U7255=SNdata_U7255['SN号'].tolist()
+    SNnums=[SNnums_L7255 + SNnums_C7255 + SNnums_U7255 + SNnums_4840, SNnums_6040 + SNnums_6060]
+    # SNnums=[['MGMLXN750N2189008','PK504B10100004328'], ['PK500A20100000752','PK504B10100004387']]
+    
+    mylog=log.Mylog('log_diag.txt','error')
+    mylog.logcfg()
+    
+    #参数初始化
+    df_bms_ram1=pd.DataFrame(columns=['time', 'sn', 'packvolt', 'cellvolt', 'celltemp'])
+    df_bms_ram2=pd.DataFrame(columns=['time', 'sn', 'packvolt', 'cellvolt', 'celltemp'])
+    df_alarm_ram1=pd.DataFrame(columns=['sn','time','safetywarning1','safetywarning2'])
+    df_alarm_ram2=pd.DataFrame(columns=['sn','time','safetywarning1','safetywarning2'])
+
+    mainprocess()
+    #定时任务.......................................................................................................................................................................
+    scheduler = BlockingScheduler()
+    scheduler.add_job(mainprocess, 'interval', seconds=60, id='diag_job')
+
+    try:  
+        scheduler.start()
+    except Exception as e:
+        scheduler.shutdown()
+        print(repr(e))
+        mylog.logopt(e)

+ 905 - 0
LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/CBMSBatInterShort.py

@@ -0,0 +1,905 @@
+import pandas as pd
+import numpy as np
+import matplotlib.pyplot as plt
+import datetime
+# from pymysql import paramstyle
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam
+
+class BatInterShort():
+    def __init__(self,sn,celltype,df_bms,df_soh,df_last,df_last1,df_last2,df_last3,df_lfp):  #参数初始化
+
+        if (not df_lfp.empty) and celltype>50:
+            df_lfp.drop(['sn'],axis=1)
+            df_bms=pd.concat([df_lfp, df_bms], ignore_index=True)
+        else:
+            pass
+
+        self.sn=sn
+        self.celltype=celltype
+        self.param=BatParam.BatParam(celltype)
+        self.packcrnt=df_bms['总电流[A]']*self.param.PackCrntDec
+        self.packvolt=df_bms['总电压[V]']
+        self.bms_soc=df_bms['SOC[%]']
+        df_bms['time']=pd.to_datetime(df_bms['时间戳'], format='%Y-%m-%d %H:%M:%S')
+        self.bmstime= df_bms['time']
+
+        self.df_bms=df_bms
+        self.df_soh=df_soh
+        self.df_last=df_last
+        self.df_last1=df_last1
+        self.df_last2=df_last2
+        self.df_last3=df_last3
+        self.df_lfp=df_lfp
+
+        self.cellvolt_name=['单体电压'+str(x) for x in range(1,self.param.CellVoltNums+1)]
+        self.celltemp_name=['单体温度'+str(x) for x in range(1,self.param.CellTempNums+1)]
+    
+    def intershort(self):
+        if self.celltype<=50:
+            df_res, df_ram_last, df_ram_last1, df_ram_last3=self._ncm_intershort()
+            return df_res, df_ram_last, df_ram_last1,self.df_last2, df_ram_last3,self.df_lfp
+            
+        else:
+            df_res, df_ram_last, df_ram_last1, df_ram_last2, df_ram_last3, df_ram_lfp=self._lfp_intershort()
+            return df_res, df_ram_last, df_ram_last1, df_ram_last2, df_ram_last3, df_ram_lfp
+
+
+    #定义滑动滤波函数....................................................................................
+    def _np_move_avg(self,a, n, mode="same"): 
+        return (np.convolve(a, np.ones((n,)) / n, mode=mode))
+    
+    #寻找当前行数据的最小温度值.............................................................................
+    def _celltemp_weight(self,num):   
+        celltemp = list(self.df_bms.loc[num,self.celltemp_name])
+        celltemp=min(celltemp)
+        self.celltemp=celltemp
+        if self.celltype==99:
+            if celltemp>=25:
+                self.tempweight=1
+                self.StandardStandingTime=4800
+            elif celltemp>=15:
+                self.tempweight=0.6
+                self.StandardStandingTime=7200
+            elif celltemp>=5:
+                self.tempweight=0.
+                self.StandardStandingTime=10800
+            else:
+                self.tempweight=0.1
+                self.StandardStandingTime=10800
+        else:
+            if celltemp>=25:
+                self.tempweight=1
+                self.StandardStandingTime=4800
+            elif celltemp>=15:
+                self.tempweight=0.8
+                self.StandardStandingTime=7200
+            elif celltemp>=5:
+                self.tempweight=0.6
+                self.StandardStandingTime=7200
+            else:
+                self.tempweight=0.2
+                self.StandardStandingTime=10800
+
+    #获取前半个小时每个电压的平均值........................................................................................
+    def _avgvolt_get(self,num): 
+        time_now=self.df_bms.loc[num, 'time']
+        time_last=time_now-datetime.timedelta(seconds=1800)
+        df_volt=self.df_bms[(self.df_bms['time']>=time_last) & (self.df_bms['time']<=time_now)]
+        df_volt=df_volt[self.cellvolt_name]
+        cellvolt_std=df_volt.std(axis=0)
+        if len(df_volt)>2 and max(cellvolt_std)<1.5:
+            cellvolt_sum=df_volt.sum(0)-df_volt.max(0)-df_volt.min(0)
+            cellvolt_mean=cellvolt_sum/(len(df_volt)-2)
+            cellvolt=cellvolt_mean/1000
+        elif len(df_volt)==2:
+            # df_volt=pd.DataFrame(df_volt,dtype=np.float)
+            if max(abs(df_volt.iloc[1]-df_volt.iloc[0]))<3:
+                cellvolt=df_volt.mean(0)/1000
+            else:
+                cellvolt=pd.DataFrame()
+        else:
+            cellvolt=pd.DataFrame()
+        return cellvolt
+    
+    #获取单个电压值.................................................................................................
+    def _singlevolt_get(self,num,series,mode):  #mode==1取当前行单体电压值,mode==2取某个单体所有电压值
+        s=str(series)
+        if mode==1:
+            singlevolt=self.df_bms.loc[num,'单体电压' + s]/1000
+            return singlevolt
+        else:
+            singlevolt=self.df_bms['单体电压' + s]/1000
+            return singlevolt
+
+    #获取当前行所有电压数据........................................................................................
+    def _cellvolt_get(self,num):
+        cellvolt = np.array(self.df_bms.loc[num,self.cellvolt_name])/1000
+        return cellvolt
+
+    #获取当前行所有soc差...........................................................................................
+    def _celldeltsoc_get(self,cellvolt_list,dict_baltime,capacity): 
+        cellsoc=[]
+        celldeltsoc=[]
+        for j in range(self.param.CellVoltNums):   #获取每个电芯电压对应的SOC值
+            cellvolt=cellvolt_list[j]
+            ocv_soc=np.interp(cellvolt,self.param.LookTab_OCV,self.param.LookTab_SOC)
+            if j+1 in dict_baltime.keys():
+                ocv_soc=ocv_soc+dict_baltime[j+1]*self.param.BalCurrent/(capacity*3600)   #补偿均衡电流
+            else:
+                pass
+            cellsoc.append(ocv_soc)
+        
+        if self.celltype==1 or self.celltype==2:
+            consum_num=7
+            cellsoc1=cellsoc[:self.param.CellVoltNums-consum_num]    #切片,将bms耗电的电芯和非耗电的电芯分离开
+            cellsocmean1=(sum(cellsoc1)-max(cellsoc1)-min(cellsoc1))/(len(cellsoc1)-2)
+            cellsoc2=cellsoc[self.param.CellVoltNums-consum_num:]
+            cellsocmean2=(sum(cellsoc2)-max(cellsoc2)-min(cellsoc2))/(len(cellsoc2)-2)
+            
+            for j in range(len(cellsoc)):   #计算每个电芯的soc差
+                if j<self.param.CellVoltNums-consum_num:
+                    celldeltsoc.append(cellsoc[j]-cellsocmean1)
+                else:
+                    celldeltsoc.append(cellsoc[j]-cellsocmean2)
+            return np.array(celldeltsoc), np.array(cellsoc)
+        
+        elif self.celltype==99:
+            consum_num=10
+            cellsoc1=cellsoc[:self.param.CellVoltNums-consum_num]    #切片,将bms耗电的电芯和非耗电的电芯分离开
+            cellsocmean1=(sum(cellsoc1)-max(cellsoc1)-min(cellsoc1))/(len(cellsoc1)-2)
+            cellsoc2=cellsoc[self.param.CellVoltNums-consum_num:]
+            cellsocmean2=(sum(cellsoc2)-max(cellsoc2)-min(cellsoc2))/(len(cellsoc2)-2)
+            
+            for j in range(len(cellsoc)):   #计算每个电芯的soc差
+                if j<self.param.CellVoltNums-consum_num:
+                    celldeltsoc.append(cellsoc[j]-cellsocmean1)
+                else:
+                    celldeltsoc.append(cellsoc[j]-cellsocmean2)
+            return np.array(celldeltsoc), np.array(cellsoc)
+
+        else:
+            cellsocmean=(sum(cellsoc)-max(cellsoc)-min(cellsoc))/(len(cellsoc)-2)
+            for j in range(len(cellsoc)):   #计算每个电芯的soc差
+                celldeltsoc.append(cellsoc[j]-cellsocmean)
+            return np.array(celldeltsoc), np.array(cellsoc)
+ 
+    #获取所有电芯的As差
+    def _cellDeltAs_get(self,chrg_st,chrg_end,dict_baltime):
+        cellAs=[]
+        celldeltAs=[]
+        for j in range(1, self.param.CellVoltNums+1):   #获取每个电芯电压>峰值电压的充入As数
+            if j in dict_baltime.keys():    #补偿均衡电流
+                As=-self.param.BalCurrent*dict_baltime[j]
+            else:    
+                As=0
+            As_tatol=0
+            symbol=0
+            for m in range(chrg_st+1,chrg_end):
+                As=As-self.packcrnt[m]*(self.bmstime[m]-self.bmstime[m-1]).total_seconds()
+                if symbol<5:
+                    if self.df_bms.loc[m,'单体电压'+str(j)]/1000>self.param.PeakCellVolt[symbol]:
+                        As_tatol=As_tatol+As
+                        symbol=symbol+1
+                    else:
+                        continue
+                else:
+                    cellAs.append(As_tatol/5)
+                    break
+        
+        if self.celltype==99:
+            consum_num=10
+            cellAs1=cellAs[:self.param.CellVoltNums-consum_num]    #切片,将bms耗电的电芯和非耗电的电芯分离开
+            cellAsmean1=(sum(cellAs1)-max(cellAs1)-min(cellAs1))/(len(cellAs1)-2)
+            cellAs2=cellAs[self.param.CellVoltNums-consum_num:]
+            cellAsmean2=(sum(cellAs2)-max(cellAs2)-min(cellAs2))/(len(cellAs2)-2)
+            
+            for j in range(len(cellAs)):   #计算每个电芯的soc差
+                if j<self.param.CellVoltNums-consum_num:
+                    celldeltAs.append(cellAs[j]-cellAsmean1)
+                else:
+                    celldeltAs.append(cellAs[j]-cellAsmean2)
+        else:
+            cellAsmean=(sum(cellAs)-max(cellAs)-min(cellAs))/(len(cellAs)-2)
+            for j in range(len(cellAs)):   #计算每个电芯的soc差
+                celldeltAs.append(cellAs[j]-cellAsmean)
+            
+        return np.array(celldeltAs)
+    
+    #寻找DVDQ的峰值点,并返回..........................................................................................................................
+    def _dvdq_peak(self, time, soc, cellvolt, packcrnt):
+        cellvolt = self._np_move_avg(cellvolt, 3, mode="same")
+        Soc = 0
+        Ah = 0
+        Volt = cellvolt[0]
+        DV_Volt = []
+        DQ_Ah = []
+        DVDQ = []
+        time1 = []
+        soc1 = []
+        soc2 = []
+        xvolt=[]
+
+        for m in range(1, len(time)):
+            Step = (time[m] - time[m - 1]).total_seconds()
+            Soc = Soc - packcrnt[m] * Step * 100 / (3600 * self.param.Capacity)
+            Ah = Ah - packcrnt[m] * Step / 3600
+            if (cellvolt[m]-Volt)>0.0015 and Ah>0:
+                DQ_Ah.append(Ah)
+                DV_Volt.append(cellvolt[m]-Volt)
+                DVDQ.append((DV_Volt[-1])/Ah)
+                xvolt.append(cellvolt[m])
+                Volt=cellvolt[m]
+                Ah = 0
+                soc1.append(Soc)
+                time1.append(time[m])
+                soc2.append(soc[m])
+
+        #切片,去除前后10min的数据
+        df_Data1 = pd.DataFrame({'time': time1,
+                                'SOC': soc2,
+                                'DVDQ': DVDQ,
+                                'AhSoc': soc1,
+                                'DQ_Ah':DQ_Ah,
+                                'DV_Volt':DV_Volt,
+                                'XVOLT':xvolt})
+        start_time=df_Data1.loc[0,'time']
+        start_time=start_time+datetime.timedelta(seconds=900)
+        end_time=df_Data1.loc[len(time1)-1,'time']
+        end_time=end_time-datetime.timedelta(seconds=1200)
+        if soc2[0]<36:
+            df_Data1=df_Data1[(df_Data1['SOC']>40) & (df_Data1['SOC']<80)]
+        else:
+            df_Data1=df_Data1[(df_Data1['time']>start_time) & (df_Data1['SOC']<80)]
+        df_Data1=df_Data1[(df_Data1['XVOLT']>self.param.PeakVoltLowLmt) & (df_Data1['XVOLT']<self.param.PeakVoltUpLmt)]
+
+        # print(packcrnt[int(len(time)/2)], min(self.celltemp))
+        # ax1 = plt.subplot(3, 1, 1)
+        # plt.plot(df_Data1['SOC'],df_Data1['DQ_Ah'],'g*-')
+        # plt.xlabel('SOC/%')
+        # plt.ylabel('DQ_Ah')
+        # plt.legend()
+        # ax1 = plt.subplot(3, 1, 2)
+        # plt.plot(df_Data1['SOC'],df_Data1['XVOLT'],'y*-')
+        # plt.xlabel('SOC/%')
+        # plt.ylabel('Volt/V')
+        # plt.legend()
+        # ax1 = plt.subplot(3, 1, 3)
+        # plt.plot(df_Data1['SOC'], df_Data1['DVDQ'], 'r*-')
+        # plt.xlabel('SOC/%')
+        # plt.ylabel('DV/DQ')
+        # plt.legend()
+        # # plt.show()
+
+        if len(df_Data1)>2:     #寻找峰值点,且峰值点个数>2
+            PeakIndex = df_Data1['DVDQ'].idxmax()
+            df_Data2 = df_Data1[(df_Data1['SOC'] > (df_Data1['SOC'][PeakIndex] - 0.5)) & (df_Data1['SOC'] < (df_Data1['SOC'][PeakIndex] + 0.5))]
+            if len(df_Data2) > 1 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.015:
+                return df_Data1['AhSoc'][PeakIndex]
+            else:
+                df_Data1 = df_Data1.drop([PeakIndex])
+                PeakIndex = df_Data1['DVDQ'].idxmax()
+                df_Data2 = df_Data1[(df_Data1['SOC'] > (df_Data1['SOC'][PeakIndex] - 0.5)) & (df_Data1['SOC'] < (df_Data1['SOC'][PeakIndex] + 0.5))]
+                if len(df_Data2) > 1 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.015:
+                    return df_Data1['AhSoc'][PeakIndex]
+                else:
+                    return 0
+        else:
+            return 0
+
+    #计算每个电芯的均衡时长..........................................................................................................................
+    def _bal_time(self,dict_bal):
+        dict_baltime={}
+        dict_baltime1={}
+        for key in dict_bal:
+            count=1
+            x=eval(key)
+            while x>0:
+                if x & 1==1:    #判断最后一位是否为1
+                    if count in dict_baltime.keys():
+                        dict_baltime[count] = dict_baltime[count] + dict_bal[key]
+                    else:
+                        dict_baltime[count] = dict_bal[key]
+                else:
+                    pass
+                count += 1
+                x >>= 1    #右移一位
+        
+        dict_baltime=dict(sorted(dict_baltime.items(),key=lambda dict_baltime:dict_baltime[0]))
+        for key in dict_baltime:    #解析均衡的电芯编号
+            if self.celltype==1:    #科易6040
+                if key<14:
+                    dict_baltime1[key]=dict_baltime[key]
+                elif key<18:
+                    dict_baltime1[key-1]=dict_baltime[key]
+                else:
+                    dict_baltime1[key-3]=dict_baltime[key]
+            elif self.celltype==1:    #科易4840
+                if key<4:
+                    dict_baltime1[key-1]=dict_baltime[key]
+                elif key<8:
+                    dict_baltime1[key-1]=dict_baltime[key]
+                elif key<14:
+                    dict_baltime1[key-3]=dict_baltime[key]
+                elif key<18:
+                    dict_baltime1[key-4]=dict_baltime[key]
+                else:
+                    dict_baltime1[key-6]=dict_baltime[key]
+            else:
+                dict_baltime1=dict_baltime
+        return dict_baltime1
+
+    #三元电池的内短路电流计算...........................................................................................................................................................
+    def _ncm_intershort(self):
+        df_res=pd.DataFrame(columns=['time_st', 'time_sp', 'sn', 'method','short_current','baltime'])
+        df_ram_last=self.df_last
+        df_ram_last1=self.df_last1
+        df_ram_last3=self.df_last3
+
+        #容量初始化
+        if self.df_soh.empty:
+            batsoh=(self.df_bms.loc[0,'SOH[%]'])
+            capacity=self.param.Capacity*batsoh/100
+        else:
+            batsoh=self.df_soh.loc[len(self.df_soh)-1,'soh']
+            capacity=self.param.Capacity*batsoh/100
+
+        #参数初始化
+        if df_ram_last.empty:  
+            firsttime=1
+            dict_bal={}
+        else:
+            deltsoc_last=df_ram_last.loc[0,'deltsoc']
+            cellsoc_last=df_ram_last.loc[0,'cellsoc']
+            time_last=df_ram_last.loc[0,'time']
+            firsttime=0
+            dict_bal={}
+        if df_ram_last1.empty:
+            firsttime1=1
+            dict_bal1={}
+        else:
+            deltsoc_last1=df_ram_last1.loc[0,'deltsoc1']
+            time_last1=df_ram_last1.loc[0,'time1']
+            firsttime1=0
+            dict_bal1={}
+        if df_ram_last3.empty:
+            standingtime=0
+            standingtime1=0
+            standingtime2=0
+        else:
+            standingtime=df_ram_last3.loc[0,'standingtime']
+            standingtime1=df_ram_last3.loc[0,'standingtime1']
+            standingtime2=df_ram_last3.loc[0,'standingtime2']
+            dict_bal1={}
+            if abs(self.packcrnt[0])<0.01 and standingtime>1 and standingtime1>1:
+                standingtime=standingtime+(self.bmstime[0]-df_ram_last3.loc[0,'time3']).total_seconds()
+                standingtime1=standingtime1+(self.bmstime[0]-df_ram_last3.loc[0,'time3']).total_seconds()
+            else:
+                pass
+
+        for i in range(1,len(self.df_bms)-1):
+
+            if firsttime1==0:   #满电静置算法--计算均衡状态对应的均衡时间
+                try:
+                    balstat=int(self.df_bms.loc[i,'单体均衡状态'])
+                    if balstat>0.5:
+                        bal_step=(self.bmstime[i+1]-self.bmstime[i]).total_seconds()  #均衡步长
+                        bal_step=int(bal_step)
+                        if str(balstat) in dict_bal1.keys():
+                            dict_bal1[str(balstat)]=dict_bal1[str(balstat)]+bal_step
+                        else:
+                            dict_bal1[str(balstat)]=bal_step
+                    else:
+                        pass
+                except:
+                    dict_bal1={}
+            else:
+                pass
+
+            if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1:     
+                delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                standingtime=standingtime+delttime
+                standingtime1=standingtime1+delttime
+                self._celltemp_weight(i)
+
+                #长时间静置法计算内短路-开始.....................................................................................................................................
+                if firsttime==1:    
+                    if standingtime>self.StandardStandingTime*2:      #静置时间满足要求
+                        standingtime=0
+                        cellvolt_now=self._avgvolt_get(i)
+                        if not cellvolt_now.empty:
+                            cellvolt_min=min(cellvolt_now)
+                            cellvolt_max=max(cellvolt_now)
+                            # cellvolt_last=self._avgvolt_get(i-1)
+                            # deltvolt=max(abs(cellvolt_now-cellvolt_last))
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+
+                            if 2<cellvolt_min<4.5 and 2<cellvolt_max<4.5 and (45<cellsoc_max or cellsoc_max<30) and (45<cellsoc_min or cellsoc_min<30): 
+                                dict_baltime={}   #获取每个电芯的均衡时间
+                                deltsoc_last, cellsoc_last=self._celldeltsoc_get(cellvolt_now,dict_baltime,capacity)
+                                time_last=self.bmstime[i]
+                                firsttime=0
+                                df_ram_last.loc[0]=[self.sn,time_last,deltsoc_last,cellsoc_last]   #更新RAM信息
+                    else:
+                        pass                
+                elif standingtime>3600*12:
+                    standingtime=0
+                    cellvolt_now=self._avgvolt_get(i)
+                    if not cellvolt_now.empty:
+                        cellvolt_min=min(cellvolt_now)
+                        cellvolt_max=max(cellvolt_now)
+                        # cellvolt_last=self._avgvolt_get(i-1)
+                        # deltvolt=max(abs(cellvolt_now-cellvolt_last))
+                        cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                        cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                        
+                        if 2<cellvolt_min<4.5 and 2<cellvolt_max<4.5 and (45<cellsoc_max or cellsoc_max<30) and (45<cellsoc_min or cellsoc_min<30):
+                            dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                            deltsoc_now, cellsoc_now=self._celldeltsoc_get(cellvolt_now,dict_baltime,capacity)
+                            time_now=self.bmstime[i]
+                            if -5<max(cellsoc_now-cellsoc_last)<5:
+                                df_ram_last.loc[0]=[self.sn,time_now,deltsoc_now,cellsoc_now] #更新RAM信息
+                                
+                                list_sub=deltsoc_now-deltsoc_last
+                                list_pud=(0.01*capacity*3600*1000)/(time_now-time_last).total_seconds()
+                                leak_current=list_sub*list_pud
+                                # leak_current=np.array(leak_current)
+                                leak_current=np.round(leak_current,3)
+                                leak_current=list(leak_current)
+                                
+                                df_res.loc[len(df_res)]=[time_last,time_now,self.sn,1,str(leak_current),str(dict_baltime)]  #计算结果存入Dataframe
+                                time_last=time_now  #更新时间
+                                deltsoc_last=deltsoc_now    #更新soc差
+                                dict_bal={}
+                            else:
+                                firsttime=1
+                else: 
+                    try:  
+                        balstat=int(self.df_bms.loc[i,'单体均衡状态'])
+                        if balstat>0.5:
+                            bal_step=(self.bmstime[i+1]-self.bmstime[i]).total_seconds()  #均衡步长
+                            bal_step=int(bal_step)
+                            if str(balstat) in dict_bal.keys():
+                                dict_bal[str(balstat)]=dict_bal[str(balstat)]+bal_step
+                            else:
+                                dict_bal[str(balstat)]=bal_step
+                        else:
+                            pass
+                    except:
+                        dict_bal={}
+
+                #满电静置法计算内短路-开始.....................................................................................................................................................
+                if self.StandardStandingTime<standingtime1:  
+                    standingtime1=0
+                    cellvolt_now1=self._avgvolt_get(i)
+                    if not cellvolt_now1.empty:
+                        cellvolt_max1=max(cellvolt_now1)
+                        cellvolt_min1=min(cellvolt_now1)
+                        # cellvolt_last1=self._avgvolt_get(i-1)
+                        # deltvolt1=max(abs(cellvolt_now1-cellvolt_last1))
+                        cellsoc_now1=np.interp(cellvolt_max1,self.param.LookTab_OCV,self.param.LookTab_SOC)
+
+                        if cellsoc_now1>self.param.FullChrgSoc-10 and 2<cellvolt_min1<4.5 and 2<cellvolt_max1<4.5:
+                            if firsttime1==1:
+                                dict_baltime1={}   #获取每个电芯的均衡时间
+                                deltsoc_last1, cellsoc_last1=self._celldeltsoc_get(cellvolt_now1,dict_baltime1,capacity)
+                                time_last1=self.bmstime[i]
+                                firsttime1=0
+                                df_ram_last1.loc[0]=[self.sn,time_last1,deltsoc_last1]    #更新RAM信息
+                            else:
+                                dict_baltime1=self._bal_time(dict_bal1)   #获取每个电芯的均衡时间
+                                time_now1=self.bmstime[i]
+                                if (time_now1-time_last1).total_seconds()>3600*20:
+                                    deltsoc_now1, cellsoc_now1=self._celldeltsoc_get(cellvolt_now1,dict_baltime1,capacity)
+                                    df_ram_last1.loc[0]=[self.sn,time_now1,deltsoc_now1] #更新RAM信息
+
+                                    list_sub1=deltsoc_now1-deltsoc_last1
+                                    list_pud1=(0.01*capacity*3600*1000)/(time_now1-time_last1).total_seconds()
+                                    leak_current1=list_sub1*list_pud1
+                                    # leak_current1=np.array(leak_current1)
+                                    leak_current1=np.round(leak_current1,3)
+                                    leak_current1=list(leak_current1)
+                                    
+                                    df_res.loc[len(df_res)]=[time_last1,time_now1,self.sn,2,str(leak_current1),str(dict_baltime1)]  #计算结果存入Dataframe
+                                    time_last1=time_now1  #更新时间
+                                    deltsoc_last1=deltsoc_now1    #更新soc差
+                                    dict_bal1={}
+                                else:
+                                    pass
+                    else:
+                        pass
+                else:   
+                    pass
+
+            else:
+                df_ram_last=pd.DataFrame(columns=['sn','time','deltsoc','cellsoc'])   #电流>0,清空上次静置的SOC差
+                dict_bal={} 
+                firsttime=1
+                standingtime=0
+                standingtime1=0
+                pass
+        
+        #更新RAM的standingtime
+        df_ram_last3.loc[0]=[self.sn,self.bmstime[len(self.bmstime)-1],standingtime,standingtime1,standingtime2]
+
+        #返回计算结果
+        if df_res.empty:    
+            return pd.DataFrame(), df_ram_last, df_ram_last1, df_ram_last3
+        else:
+            return df_res, df_ram_last, df_ram_last1, df_ram_last3
+
+    #磷酸铁锂电池内短路计算程序.............................................................................................................................
+    def _lfp_intershort(self):
+        column_name=['time_st', 'time_sp', 'sn', 'method','short_current','baltime']
+        df_res=pd.DataFrame(columns=column_name)
+        df_ram_last=self.df_last
+        df_ram_last1=self.df_last1
+        df_ram_last2=self.df_last2
+        df_ram_last3=self.df_last3
+        df_ram_lfp=pd.DataFrame(columns=self.df_bms.columns.tolist())
+
+        #容量初始化
+        if self.df_soh.empty:
+            batsoh=(self.df_bms.loc[0,'SOH[%]'])
+            capacity=self.param.Capacity*batsoh/100
+        else:
+            batsoh=self.df_soh.loc[len(self.df_soh)-1,'soh']
+            capacity=self.param.Capacity*batsoh/100
+        #参数初始化
+        if df_ram_last.empty:  
+            firsttime=1
+            dict_bal={}
+        else:
+            deltsoc_last=df_ram_last.loc[0,'deltsoc']
+            cellsoc_last=df_ram_last.loc[0,'cellsoc']
+            time_last=df_ram_last.loc[0,'time']
+            firsttime=0
+            dict_bal={}
+        if df_ram_last1.empty:
+            firsttime1=1
+            dict_bal1={}
+        else:
+            deltsoc_last1=df_ram_last1.loc[0,'deltsoc1']
+            time_last1=df_ram_last1.loc[0,'time1']
+            firsttime1=0
+            dict_bal1={}
+        if df_ram_last2.empty:
+            firsttime2=1
+            charging=0
+            dict_bal2={}
+        else:
+            deltAs_last2=df_ram_last2.loc[0,'deltAs2']
+            time_last2=df_ram_last2.loc[0,'time2']
+            firsttime2=0
+            charging=0
+            dict_bal2={}
+        if df_ram_last3.empty:
+            standingtime=0
+            standingtime1=0
+            standingtime2=0
+        else:
+            standingtime=df_ram_last3.loc[0,'standingtime']
+            standingtime1=df_ram_last3.loc[0,'standingtime1']
+            standingtime2=df_ram_last3.loc[0,'standingtime2']
+            dict_bal1={}
+            if abs(self.packcrnt[0])<0.01 and standingtime>1 and standingtime1>1:
+                standingtime=standingtime+(self.bmstime[0]-df_ram_last3.loc[0,'time3']).total_seconds()
+                standingtime1=standingtime1+(self.bmstime[0]-df_ram_last3.loc[0,'time3']).total_seconds()
+            else:
+                pass
+
+        for i in range(1,len(self.df_bms)-1):
+
+            #静置法计算内短路..........................................................................................................................
+            if firsttime1==0:   #满电静置算法--计算均衡状态对应的均衡时间
+                try:
+                    balstat=int(self.df_bms.loc[i,'单体均衡状态'])
+                    if balstat>0.5:
+                        bal_step=(self.bmstime[i+1]-self.bmstime[i]).total_seconds()  #均衡步长
+                        bal_step=int(bal_step)
+                        if str(balstat) in dict_bal1.keys():
+                            dict_bal1[str(balstat)]=dict_bal1[str(balstat)]+bal_step
+                        else:
+                            dict_bal1[str(balstat)]=bal_step
+                    else:
+                        pass
+                except:
+                    dict_bal1={}
+            else:
+                pass
+
+            if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1:     
+                delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                standingtime=standingtime+delttime
+                standingtime1=standingtime1+delttime
+                self._celltemp_weight(i)
+
+                #长时间静置法计算内短路-开始.....................................................................................................................................
+                if firsttime==1:    
+                    if standingtime>self.StandardStandingTime:      #静置时间满足要求
+                        standingtime=0
+                        cellvolt_now=self._avgvolt_get(i)
+                        if not cellvolt_now.empty:
+                            cellvolt_min=min(cellvolt_now)
+                            cellvolt_max=max(cellvolt_now)
+                            # cellvolt_last=self._avgvolt_get(i-1)
+                            # deltvolt=max(abs(cellvolt_now-cellvolt_last))
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+
+                            if cellsoc_max<self.param.SocInflexion1-2 and 12<cellsoc_min:
+                                dict_baltime={}  #获取每个电芯的均衡时间
+                                deltsoc_last, cellsoc_last=self._celldeltsoc_get(cellvolt_now,dict_baltime,capacity)
+                                time_last=self.bmstime[i]
+                                firsttime=0
+                                df_ram_last.loc[0]=[self.sn,time_last,deltsoc_last,cellsoc_last]   #更新RAM信息
+                            else:
+                                pass
+                    else:
+                        pass                
+                elif standingtime>3600*12:
+                    standingtime=0
+                    cellvolt_now=self._avgvolt_get(i)
+                    if not cellvolt_now.empty:
+                        cellvolt_min=min(cellvolt_now)
+                        cellvolt_max=max(cellvolt_now)
+                        # cellvolt_last=np.array(self._avgvolt_get(i-1))
+                        # deltvolt=max(abs(cellvolt_now-cellvolt_last))
+                        cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                        cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+
+                        if cellsoc_max<self.param.SocInflexion1-2 and 12<cellsoc_min:
+                            dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                            deltsoc_now, cellsoc_now=self._celldeltsoc_get(cellvolt_now, dict_baltime,capacity)    #获取每个电芯的SOC差
+                            time_now=self.bmstime[i]
+                            if -5<max(cellsoc_now-cellsoc_last)<5:
+                                df_ram_last.loc[0]=[self.sn,time_now,deltsoc_now,cellsoc_now]   #更新RAM信息
+
+                                list_sub=deltsoc_now-deltsoc_last
+                                list_pud=(0.01*capacity*3600*1000)/(time_now-time_last).total_seconds()
+                                leak_current=list_sub*list_pud
+                                # leak_current=np.array(leak_current)
+                                leak_current=np.round(leak_current,3)
+                                leak_current=list(leak_current)
+                                
+                                df_res.loc[len(df_res)]=[time_last,time_now,self.sn,1,str(leak_current),str(dict_baltime)]  #计算结果存入Dataframe
+                                time_last=time_now  #更新时间
+                                deltsoc_last=deltsoc_now    #更新soc差
+                                dict_bal={}
+                            else:
+                                firsttime=1
+                        else:
+                            pass
+                else: 
+                    try:  
+                        balstat=int(self.df_bms.loc[i,'单体均衡状态'])
+                        if balstat>0.5:
+                            bal_step=(self.bmstime[i+1]-self.bmstime[i]).total_seconds()  #均衡步长
+                            bal_step=int(bal_step)
+                            if str(balstat) in dict_bal.keys():
+                                dict_bal[str(balstat)]=dict_bal[str(balstat)]+bal_step
+                            else:
+                                dict_bal[str(balstat)]=bal_step
+                        else:
+                            pass
+                    except:
+                        dict_bal={}
+
+                #非平台区间静置法计算内短路-开始.....................................................................................................................................................
+                if standingtime1>self.StandardStandingTime: 
+                    standingtime1=0
+                    cellvolt_now1=self._avgvolt_get(i)
+                    if not cellvolt_now1.empty:
+                        cellvolt_max1=max(cellvolt_now1)
+                        cellvolt_min1=min(cellvolt_now1)
+                        # cellvolt_last1=self._avgvolt_get(i-1)
+                        # deltvolt1=max(abs(cellvolt_now1-cellvolt_last1))
+                        cellsoc_max1=np.interp(cellvolt_max1,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                        cellsoc_min1=np.interp(cellvolt_min1,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                    
+                        if cellsoc_max1<self.param.SocInflexion1-2 and 12<cellsoc_min1:
+                            if firsttime1==1:
+                                dict_baltime1=self._bal_time(dict_bal1)   #获取每个电芯的均衡时间
+                                deltsoc_last1, cellsoc_last1=self._celldeltsoc_get(cellvolt_now1,dict_baltime1,capacity)
+                                time_last1=self.bmstime[i]
+                                firsttime1=0
+                                df_ram_last1.loc[0]=[self.sn,time_last1,deltsoc_last1]    #更新RAM信息
+                            else:
+                                dict_baltime1=self._bal_time(dict_bal1)   #获取每个电芯的均衡时间
+                                deltsoc_now1, cellsoc_now1=self._celldeltsoc_get(cellvolt_now1,dict_baltime1,capacity)
+                                time_now1=self.bmstime[i]
+                                df_ram_last1.loc[0]=[self.sn,time_now1,deltsoc_now1]    #更新RAM信息
+
+                                if (time_now1-time_last1).total_seconds()>3600*24:
+                                    list_sub1=deltsoc_now1-deltsoc_last1
+                                    list_pud1=(0.01*capacity*3600*1000)/(time_now1-time_last1).total_seconds()
+                                    leak_current1=list_sub1*list_pud1
+                                    # leak_current1=np.array(leak_current1)
+                                    leak_current1=np.round(leak_current1,3)
+                                    leak_current1=list(leak_current1)
+                                    
+                                    df_res.loc[len(df_res)]=[time_last1,time_now1,self.sn,2,str(leak_current1),str(dict_baltime1)]  #计算结果存入Dataframe
+                                    time_last1=time_now1  #更新时间
+                                    deltsoc_last1=deltsoc_now1    #更新soc差
+                                    dict_bal1={}
+                                else:
+                                    pass
+                        else:
+                            pass
+                else:   
+                    pass
+
+            else:
+                df_ram_last=pd.DataFrame(columns=['sn','time','deltsoc','cellsoc'])   #电流>0,清空上次静置的SOC差
+                dict_bal={} 
+                firsttime=1
+                standingtime=0
+                standingtime1=0
+                pass
+
+            #获取充电数据——开始..............................................................................................................
+            try:
+                balstat=int(self.df_bms.loc[i,'单体均衡状态'])  #统计均衡状态
+                if balstat>0.5:
+                    bal_step=(self.bmstime[i+1]-self.bmstime[i]).total_seconds()  #均衡步长
+                    bal_step=int(bal_step)
+                    if str(balstat) in dict_bal2.keys():
+                        dict_bal2[str(balstat)]=dict_bal2[str(balstat)]+bal_step
+                    else:
+                        dict_bal2[str(balstat)]=bal_step
+                else:
+                    pass
+            except:
+                dict_bal2={}
+
+            #判断充电状态
+            if self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and self.packcrnt[i-1]<=-1:
+                if charging==0:
+                    if self.bms_soc[i]<41:
+                        cellvolt_now=self._cellvolt_get(i)
+                        if min(cellvolt_now)<self.param.CellFullChrgVolt-0.15:
+                            charging=1
+                            chrg_start=i
+                        else:
+                            pass
+                    else:
+                        pass
+
+                else: #充电中
+                    cellvolt_now=self._cellvolt_get(i)
+                    if (self.bmstime[i+1]-self.bmstime[i]).total_seconds()>180 or (self.packcrnt[i]>self.param.Capacity/2 and self.packcrnt[i+1]>self.param.Capacity/2):  #如果充电过程中时间间隔>180s,则舍弃该次充电
+                        charging=0
+                        continue
+                    elif min(cellvolt_now)>self.param.CellFullChrgVolt-0.13:   #电压>满充电压-0.13V,即3.37V
+                        self._celltemp_weight(i)
+                        if i-chrg_start>10 and self.celltemp>20:
+                            chrg_end=i+1
+                            charging=0
+
+                            #计算漏电流值...................................................................
+                            if firsttime2==1:
+                                dict_baltime={}
+                                deltAs_last2=self._cellDeltAs_get(chrg_start,chrg_end,dict_baltime)
+                                time_last2=self.bmstime[chrg_end]
+                                df_ram_last2.loc[0]=[self.sn,time_last2,deltAs_last2]    #更新RAM信息
+                            else:
+                                dict_baltime=self._bal_time(dict_bal2)   #获取每个电芯的均衡时间
+                                deltAs_now2=self._cellDeltAs_get(chrg_start,chrg_end,dict_baltime)  #获取每个电芯的As差
+                                time_now2=self.bmstime[chrg_end]
+                                df_ram_last2.loc[0]=[self.sn,time_now2,deltAs_now2]    #更新RAM信息
+
+                                list_sub2=deltAs_now2-deltAs_last2
+                                list_pud2=-1000/(time_now2-time_last2).total_seconds()
+                                leak_current2=list_sub2*list_pud2
+                                # leak_current=np.array(leak_current)
+                                leak_current2=np.round(leak_current2,3)
+                                leak_current2=list(leak_current2)
+
+                                df_res.loc[len(df_res)]=[time_last2,time_now2,self.sn,3,str(leak_current2),str(dict_baltime)]  #计算结果存入Dataframe
+                                deltAs_last2=deltAs_now2
+                                time_last2=time_now2
+                                dict_bal2={}
+
+                        else:
+                            charging=0
+                            continue
+                    # elif min(cellvolt_now)>self.param.CellFullChrgVolt-0.1:   #电压>满充电压
+                    #     self._celltemp_weight(i)
+                    #     if i-chrg_start>10 and self.celltemp>10:
+                    #         chrg_end=i+1
+                    #         charging=0
+
+                    #         #计算漏电流值...................................................................
+                    #         if firsttime2==1:
+                    #             dict_baltime={}
+                    #             peaksoc_list=[]
+                    #             for j in range(1, self.param.CellVoltNums + 1):
+                    #                 cellvolt = self._singlevolt_get(i,j,2)  #取单体电压j的所有电压值
+                    #                 cellvolt = list(cellvolt[chrg_start:chrg_end])
+                    #                 time = list(self.bmstime[chrg_start:chrg_end])
+                    #                 packcrnt = list(self.packcrnt[chrg_start:chrg_end])
+                    #                 soc = list(self.bms_soc[chrg_start:chrg_end])
+                    #                 peaksoc = self._dvdq_peak(time, soc, cellvolt, packcrnt)
+                    #                 if peaksoc>1:
+                    #                     peaksoc_list.append(peaksoc)
+                    #                 else:
+                    #                     break
+                    #             if len(peaksoc_list)==self.param.CellVoltNums:
+                    #                 celldeltsoc=[]
+                    #                 consum_num=10
+                    #                 cellsoc1=peaksoc_list[:self.param.CellVoltNums-consum_num]    #切片,将bms耗电的电芯和非耗电的电芯分离开
+                    #                 cellsocmean1=(sum(cellsoc1)-max(cellsoc1)-min(cellsoc1))/(len(cellsoc1)-2)
+                    #                 cellsoc2=peaksoc_list[self.param.CellVoltNums-consum_num:]
+                    #                 cellsocmean2=(sum(cellsoc2)-max(cellsoc2)-min(cellsoc2))/(len(cellsoc2)-2)
+                                    
+                    #                 for j in range(len(peaksoc_list)):   #计算每个电芯的soc差
+                    #                     if j<self.param.CellVoltNums-consum_num:
+                    #                         celldeltsoc.append(peaksoc_list[j]-cellsocmean1)
+                    #                     else:
+                    #                         celldeltsoc.append(peaksoc_list[j]-cellsocmean2)
+                    #                 deltsoc_last2=celldeltsoc
+                    #                 time_last2=self.bmstime[chrg_end]
+                    #                 df_ram_last2.loc[0]=[self.sn,time_last2,deltsoc_last2]    #更新RAM信息
+                    #         else:
+                    #             dict_baltime=self._bal_time(dict_bal2)   #获取每个电芯的均衡时间
+                    #             peaksoc_list=[]
+                    #             for j in range(1, self.param.CellVoltNums + 1):
+                    #                 cellvolt = self._singlevolt_get(i,j,2)  #取单体电压j的所有电压值
+                    #                 cellvolt = list(cellvolt[chrg_start:chrg_end])
+                    #                 time = list(self.bmstime[chrg_start:chrg_end])
+                    #                 packcrnt = list(self.packcrnt[chrg_start:chrg_end])
+                    #                 soc = list(self.bms_soc[chrg_start:chrg_end])
+                    #                 peaksoc = self._dvdq_peak(time, soc, cellvolt, packcrnt)
+                    #                 if peaksoc>1:
+                    #                     peaksoc_list.append(peaksoc)
+                    #                 else:
+                    #                     break
+                    #             if len(peaksoc_list)==self.param.CellVoltNums:
+                    #                 celldeltsoc=[]
+                    #                 consum_num=10
+                    #                 cellsoc1=peaksoc_list[:self.param.CellVoltNums-consum_num]    #切片,将bms耗电的电芯和非耗电的电芯分离开
+                    #                 cellsocmean1=(sum(cellsoc1)-max(cellsoc1)-min(cellsoc1))/(len(cellsoc1)-2)
+                    #                 cellsoc2=peaksoc_list[self.param.CellVoltNums-consum_num:]
+                    #                 cellsocmean2=(sum(cellsoc2)-max(cellsoc2)-min(cellsoc2))/(len(cellsoc2)-2)
+                                    
+                    #                 for j in range(len(peaksoc_list)):   #计算每个电芯的soc差
+                    #                     if j<self.param.CellVoltNums-consum_num:
+                    #                         celldeltsoc.append(peaksoc_list[j]-cellsocmean1)
+                    #                     else:
+                    #                         celldeltsoc.append(peaksoc_list[j]-cellsocmean2)
+                    #                 deltsoc_now2=celldeltsoc
+                    #                 time_now2=self.bmstime[chrg_end]
+                    #                 df_ram_last2.loc[0]=[self.sn,time_now2,deltsoc_now2]    #更新RAM信息
+
+                    #                 list_sub2=deltsoc_now2-deltsoc_last2
+                    #                 list_pud2=(0.01*capacity*3600*1000)/(time_now2-time_last2).total_seconds()
+                    #                 leak_current2=list_sub2*list_pud2
+                    #                 leak_current2=np.round(leak_current2,3)
+                    #                 leak_current2=list(leak_current2)
+
+                    #                 df_res.loc[len(df_res)]=[time_last2,time_now2,self.sn,3,str(leak_current2),str(dict_baltime)]  #计算结果存入Dataframe
+                    #                 deltsoc_last2=deltsoc_now2
+                    #                 time_last2=time_now2
+                    #                 dict_bal2={}
+                        
+                    #     else:
+                    #         charging=0
+                    #         continue
+
+                    elif i==len(self.df_bms)-2:  #数据中断后仍在充电,将前段充电数据写入RAM
+                        df_ram_lfp=self.df_bms.iloc[chrg_start:]
+                        df_ram_lfp['sn']=self.sn
+                    else:
+                        pass
+            else:
+                pass
+
+    
+        #更新RAM
+        df_ram_last3.loc[0]=[self.sn,self.bmstime[len(self.bmstime)-1],standingtime,standingtime1,standingtime2]
+
+        #返回结果
+        if df_res.empty:    
+            return pd.DataFrame(), df_ram_last, df_ram_last1, df_ram_last2, df_ram_last3,df_ram_lfp
+        else:
+            return df_res, df_ram_last, df_ram_last1, df_ram_last2, df_ram_last3, df_ram_lfp

+ 506 - 0
LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/CBMSBatUniform.py

@@ -0,0 +1,506 @@
+import pandas as pd
+import numpy as np
+import datetime
+from LIB.MIDDLE.CellStateEstimation.Common import BatParam
+
+class BatUniform():
+    def __init__(self,sn,celltype,df_bms,df_uniform,df_last3,df_lfp1):  #参数初始化
+
+        if (not df_lfp1.empty) and celltype>50:
+            df_lfp1.drop(['sn'],axis=1)
+            df_bms=pd.concat([df_lfp1, df_bms], ignore_index=True)
+        else:
+            pass
+        
+        self.sn=sn
+        self.celltype=celltype
+        self.param=BatParam.BatParam(celltype)
+        self.df_bms=df_bms
+        self.packcrnt=df_bms['总电流[A]']*self.param.PackCrntDec
+        self.packvolt=df_bms['总电压[V]']
+        self.bms_soc=df_bms['SOC[%]']
+        self.bmstime= pd.to_datetime(df_bms['时间戳'], format='%Y-%m-%d %H:%M:%S')
+        # df_uniform['time']=pd.to_datetime(df_uniform['time'], format='%Y-%m-%d %H:%M:%S')
+
+        self.df_uniform=df_uniform
+        self.df_last3=df_last3
+        self.df_lfp1=df_lfp1
+
+        self.cellvolt_name=['单体电压'+str(x) for x in range(1,self.param.CellVoltNums+1)]
+        self.celltemp_name=['单体温度'+str(x) for x in range(1,self.param.CellTempNums+1)]
+
+    def batuniform(self):
+        if self.celltype<50:
+            df_res, df_ram_last3=self._ncm_uniform()
+            return df_res, df_ram_last3, self.df_lfp1
+        else:
+            df_res, df_ram_last3, df_ram_lfp1=self._lfp_uniform()
+            return df_res, df_ram_last3, df_ram_lfp1
+    
+    #定义滑动滤波函数........................................................................................................................................
+    def _np_move_avg(self,a, n, mode="same"): 
+        return (np.convolve(a, np.ones((n,)) / n, mode=mode))
+    
+    #寻找当前行数据的最小温度值................................................................................................................................
+    def _celltemp_weight(self,num):   
+        celltemp = list(self.df_bms.loc[num,self.celltemp_name])
+        celltemp.remove(min(celltemp))
+        self.celltemp=celltemp
+        if self.celltype>50:
+            if min(celltemp)>=25:
+                self.tempweight=1
+                self.StandardStandingTime=2400
+            elif min(celltemp)>=15:
+                self.tempweight=0.6
+                self.StandardStandingTime=3600
+            elif min(celltemp)>=5:
+                self.tempweight=0.2
+                self.StandardStandingTime=4800
+            else:
+                self.tempweight=0.1
+                self.StandardStandingTime=7200
+        else:
+            if min(celltemp)>=25:
+                self.tempweight=1
+                self.StandardStandingTime=1800
+            elif min(celltemp)>=15:
+                self.tempweight=0.8
+                self.StandardStandingTime=2400
+            elif min(celltemp)>=5:
+                self.tempweight=0.6
+                self.StandardStandingTime=3600
+            else:
+                self.tempweight=0.2
+                self.StandardStandingTime=7200
+        
+    #获取当前行所有电压数据............................................................................................................................
+    def _cellvolt_get(self,num):
+        cellvolt = np.array(self.df_bms.loc[num,self.cellvolt_name])/1000
+        return cellvolt
+    
+    #获取单个电压值.................................................................................................
+    def _singlevolt_get(self,num,series,mode):  #mode==1取当前行单体电压值,mode==2取某个单体所有电压值
+        s=str(series)
+        if mode==1:
+            singlevolt=self.df_bms.loc[num,'单体电压' + s]/1000
+            return singlevolt
+        else:
+            singlevolt=self.df_bms['单体电压' + s]/1000
+            return singlevolt
+
+    #寻找DVDQ的峰值点,并返回..........................................................................................................................
+    def _dvdq_peak(self, time, soc, cellvolt, packcrnt):
+        cellvolt = self._np_move_avg(cellvolt, 3, mode="same")
+        Soc = 0
+        Ah = 0
+        Volt = cellvolt[0]
+        DV_Volt = []
+        DQ_Ah = []
+        DVDQ = []
+        time1 = []
+        soc1 = []
+        soc2 = []
+        xvolt=[]
+
+        for m in range(1, len(time)):
+            Step = (time[m] - time[m - 1]).total_seconds()
+            Soc = Soc - packcrnt[m] * Step * 100 / (3600 * self.param.Capacity)
+            Ah = Ah - packcrnt[m] * Step / 3600
+            if (cellvolt[m]-Volt)>0.0019 and Ah>0:
+                DQ_Ah.append(Ah)
+                DV_Volt.append(cellvolt[m]-Volt)
+                DVDQ.append((DV_Volt[-1])/Ah)
+                xvolt.append(cellvolt[m])
+                Volt=cellvolt[m]
+                Ah = 0
+                soc1.append(Soc)
+                time1.append(time[m])
+                soc2.append(soc[m])
+
+        #切片,去除前后10min的数据
+        df_Data1 = pd.DataFrame({'time': time1,
+                                'SOC': soc2,
+                                'DVDQ': DVDQ,
+                                'AhSoc': soc1,
+                                'DQ_Ah':DQ_Ah,
+                                'DV_Volt':DV_Volt,
+                                'XVOLT':xvolt})
+        start_time=df_Data1.loc[0,'time']
+        start_time=start_time+datetime.timedelta(seconds=900)
+        end_time=df_Data1.loc[len(time1)-1,'time']
+        end_time=end_time-datetime.timedelta(seconds=1200)
+        if soc2[0]<36:
+            df_Data1=df_Data1[(df_Data1['SOC']>40) & (df_Data1['SOC']<80)]
+        else:
+            df_Data1=df_Data1[(df_Data1['time']>start_time) & (df_Data1['SOC']<80)]
+        df_Data1=df_Data1[(df_Data1['XVOLT']>self.param.PeakVoltLowLmt) & (df_Data1['XVOLT']<self.param.PeakVoltUpLmt)]
+
+        # print(packcrnt[int(len(time)/2)], min(self.celltemp))
+        # ax1 = plt.subplot(3, 1, 1)
+        # plt.plot(df_Data1['SOC'],df_Data1['DQ_Ah'],'g*-')
+        # plt.xlabel('SOC/%')
+        # plt.ylabel('DQ_Ah')
+        # plt.legend()
+        # ax1 = plt.subplot(3, 1, 2)
+        # plt.plot(df_Data1['SOC'],df_Data1['XVOLT'],'y*-')
+        # plt.xlabel('SOC/%')
+        # plt.ylabel('Volt/V')
+        # plt.legend()
+        # ax1 = plt.subplot(3, 1, 3)
+        # plt.plot(df_Data1['SOC'], df_Data1['DVDQ'], 'r*-')
+        # plt.xlabel('SOC/%')
+        # plt.ylabel('DV/DQ')
+        # plt.legend()
+        # # plt.show()
+
+        if len(df_Data1)>2:     #寻找峰值点,且峰值点个数>2
+            PeakIndex = df_Data1['DVDQ'].idxmax()
+            df_Data2 = df_Data1[(df_Data1['SOC'] > (df_Data1['SOC'][PeakIndex] - 0.5)) & (df_Data1['SOC'] < (df_Data1['SOC'][PeakIndex] + 0.5))]
+            if len(df_Data2) > 2 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.019:
+                return df_Data1['AhSoc'][PeakIndex]
+            else:
+                df_Data1 = df_Data1.drop([PeakIndex])
+                PeakIndex = df_Data1['DVDQ'].idxmax()
+                df_Data2 = df_Data1[(df_Data1['SOC'] > (df_Data1['SOC'][PeakIndex] - 0.5)) & (df_Data1['SOC'] < (df_Data1['SOC'][PeakIndex] + 0.5))]
+                if len(df_Data2) > 2 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.019:
+                    return df_Data1['AhSoc'][PeakIndex]
+                else:
+                    return 0
+        else:
+            return 0
+ 
+    #三元电池一致性计算.................................................................................................................................
+    def _ncm_uniform(self):
+        column_name=['time','sn','cellsoc_diff','cellvolt_diff','cellmin_num','cellmax_num','cellvolt_rank']
+        df_res=pd.DataFrame(columns=column_name)
+
+        df_ram_last3=self.df_last3
+        if df_ram_last3.empty:
+            standingtime=0
+            standingtime1=0
+            standingtime2=0
+        else:
+            standingtime=df_ram_last3.loc[0,'standingtime']
+            standingtime1=df_ram_last3.loc[0,'standingtime1']
+            standingtime2=df_ram_last3.loc[0,'standingtime2']
+            if abs(self.packcrnt[0])<0.01 and standingtime2>1:
+                standingtime2=standingtime2+(self.bmstime[0]-df_ram_last3.loc[0,'time3']).total_seconds()
+            else:
+                pass
+        
+
+        for i in range(1,len(self.df_bms)-2):
+
+            if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1:     #电流为0
+                delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                standingtime2=standingtime2+delttime
+                self._celltemp_weight(i)     #获取不同温度对应的静置时间
+
+                if standingtime2>self.StandardStandingTime:      #静置时间满足要求
+                    if abs(self.packcrnt[i+2]) >= 0.1:
+                        standingtime2=0  
+                        cellvolt_now=self._cellvolt_get(i)
+                        cellvolt_min=min(cellvolt_now)
+                        cellvolt_max=max(cellvolt_now)
+                        cellvolt_last=self._cellvolt_get(i-1)
+                        deltvolt=max(abs(cellvolt_now-cellvolt_last))                
+                        if 3<cellvolt_min<4.5 and 3<cellvolt_max<4.5 and deltvolt<0.005:
+                            cellvolt_sort=np.argsort(cellvolt_now)
+                            cellvolt_rank=list(np.argsort(cellvolt_sort)+1)
+                            cellmin_num=list(cellvolt_now).index(cellvolt_min)+1
+                            cellmax_num=list(cellvolt_now).index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num, str(cellvolt_rank)]
+                    elif standingtime2>3600*6:
+                        standingtime2=0
+                        cellvolt_now=self._cellvolt_get(i)
+                        cellvolt_min=min(cellvolt_now)
+                        cellvolt_max=max(cellvolt_now)
+                        cellvolt_last=self._cellvolt_get(i-1)
+                        deltvolt=max(abs(cellvolt_now-cellvolt_last))                  
+                        
+                        if 3<cellvolt_min<4.5 and 3<cellvolt_max<4.5 and deltvolt<0.005:
+                            cellvolt_sort=np.argsort(cellvolt_now)
+                            cellvolt_rank=list(np.argsort(cellvolt_sort)+1)
+                            cellmin_num=list(cellvolt_now).index(cellvolt_min)+1
+                            cellmax_num=list(cellvolt_now).index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num, str(cellvolt_rank)]
+                    elif i>=len(self.df_bms)-3:
+                        standingtime2=0
+                        cellvolt_now=self._cellvolt_get(i)
+                        cellvolt_min=min(cellvolt_now)
+                        cellvolt_max=max(cellvolt_now)
+                        cellvolt_last=self._cellvolt_get(i-1)
+                        deltvolt=max(abs(cellvolt_now-cellvolt_last))     
+                        
+                        if 3<cellvolt_min<4.5 and 3<cellvolt_max<4.5 and deltvolt<0.005:
+                            cellvolt_sort=np.argsort(cellvolt_now)
+                            cellvolt_rank=list(np.argsort(cellvolt_sort)+1)
+                            cellmin_num=list(cellvolt_now).index(cellvolt_min)+1
+                            cellmax_num=list(cellvolt_now).index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num, str(cellvolt_rank)]
+                        break
+                    else:
+                        continue
+                else:
+                    continue
+            else:
+                standingtime2=0
+                continue
+        
+        #更新RAM的standingtime
+        df_ram_last3.loc[0]=[self.sn,self.bmstime[len(self.bmstime)-1],standingtime,standingtime1,standingtime2]
+
+        if df_res.empty:    #返回计算结果
+            return pd.DataFrame(), df_ram_last3
+        else:
+            return df_res, df_ram_last3
+
+    #磷酸铁锂电池一致性计算.........................................................................................................................
+    def _lfp_uniform(self):
+        column_name=['time','sn','cellsoc_diff','cellvolt_diff','cellmin_num','cellmax_num','cellvolt_rank']
+        df_res=pd.DataFrame(columns=column_name)
+        df_ram_lfp1=pd.DataFrame(columns=self.df_bms.columns.tolist())
+        chrg_start=[]
+        chrg_end=[]
+        charging=0
+
+        df_ram_last3=self.df_last3
+        if df_ram_last3.empty:
+            standingtime=0
+            standingtime1=0
+            standingtime2=0
+        else:
+            standingtime=df_ram_last3.loc[0,'standingtime']
+            standingtime1=df_ram_last3.loc[0,'standingtime1']
+            standingtime2=df_ram_last3.loc[0,'standingtime2']
+            if abs(self.packcrnt[0])<0.01 and standingtime2>1:
+                standingtime2=standingtime2+(self.bmstime[0]-df_ram_last3.loc[0,'time3']).total_seconds()
+            else:
+                pass
+
+        for i in range(2,len(self.df_bms)-2):
+
+            #静置电压法计算电芯一致性
+            if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1:     #电流为0
+                delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                standingtime2=standingtime2+delttime
+                self._celltemp_weight(i)     #获取不同温度对应的静置时间
+
+                if standingtime2>self.StandardStandingTime:      #静置时间满足要求
+                    if abs(self.packcrnt[i+2]) >= 0.1:     
+                        standingtime2=0  
+                        cellvolt_now=self._cellvolt_get(i)
+                        cellvolt_min=min(cellvolt_now)
+                        cellvolt_max=max(cellvolt_now)
+                        cellvolt_last=self._cellvolt_get(i-1)
+                        deltvolt=max(abs(cellvolt_now-cellvolt_last))  
+        
+                        if 2 < cellvolt_max < self.param.OcvInflexionBelow-0.002 and 2<cellvolt_min<4.5 and deltvolt<0.005: 
+                            cellvolt_sort=np.argsort(cellvolt_now)
+                            cellvolt_rank=list(np.argsort(cellvolt_sort)+1)               
+                            cellmin_num=list(cellvolt_now).index(cellvolt_min)+1
+                            cellmax_num=list(cellvolt_now).index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num, str(cellvolt_rank)]
+                        # elif 2<cellvolt_max<4.5 and 2<cellvolt_min<4.5 and deltvolt<0.005: 
+                        #     cellvolt_sort=np.argsort(cellvolt_now)
+                        #     cellvolt_rank=list(np.argsort(cellvolt_sort)+1)
+                        #     if not df_res.empty:
+                        #         df_res.loc[len(df_res)]=df_res.loc[len(df_res)-1]
+                        #         df_res.loc[len(df_res)-1,'cellvolt_rank']=str(cellvolt_rank)
+                        #         df_res.loc[len(df_res)-1,'time']=self.bmstime[i]
+                        #     elif not self.df_uniform.empty:
+                        #         df_res.loc[len(df_res)]=self.df_uniform.iloc[-1]
+                        #         df_res.loc[len(df_res)-1,'cellvolt_rank']=str(cellvolt_rank)
+                        #         df_res.loc[len(df_res)-1,'time']=self.bmstime[i]
+                        #     else:
+                        #         pass
+                    elif standingtime2>3600*6:
+                        standingtime2=0
+                        cellvolt_now=self._cellvolt_get(i)
+                        cellvolt_min=min(cellvolt_now)
+                        cellvolt_max=max(cellvolt_now)
+                        cellvolt_last=self._cellvolt_get(i-1)
+                        deltvolt=max(abs(cellvolt_now-cellvolt_last))                  
+                        
+                        if 2 < cellvolt_max < self.param.OcvInflexionBelow-0.002 and 2<cellvolt_min<4.5 and deltvolt<0.005: 
+                            cellvolt_sort=np.argsort(cellvolt_now)
+                            cellvolt_rank=list(np.argsort(cellvolt_sort)+1)
+                            cellmin_num=list(cellvolt_now).index(cellvolt_min)+1
+                            cellmax_num=list(cellvolt_now).index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num, str(cellvolt_rank)]
+                        # elif 2<cellvolt_max<4.5 and 2<cellvolt_min<4.5 and deltvolt<0.005: 
+                        #     cellvolt_sort=np.argsort(cellvolt_now)
+                        #     cellvolt_rank=list(np.argsort(cellvolt_sort)+1)
+                        #     if not df_res.empty:
+                        #         df_res.loc[len(df_res)]=df_res.loc[len(df_res)-1]
+                        #         df_res.loc[len(df_res)-1,'cellvolt_rank']=str(cellvolt_rank)
+                        #         df_res.loc[len(df_res)-1,'time']=self.bmstime[i]
+                        #     elif not self.df_uniform.empty:
+                        #         df_res.loc[len(df_res)]=self.df_uniform.iloc[-1]
+                        #         df_res.loc[len(df_res)-1,'cellvolt_rank']=str(cellvolt_rank)
+                        #         df_res.loc[len(df_res)-1,'time']=self.bmstime[i]
+                        #     else:
+                        #         pass
+                    
+                    elif i>=len(self.df_bms)-3:
+                        standingtime2=0
+                        cellvolt_now=self._cellvolt_get(i)
+                        cellvolt_min=min(cellvolt_now)
+                        cellvolt_max=max(cellvolt_now)
+                        cellvolt_last=self._cellvolt_get(i-1)
+                        deltvolt=max(abs(cellvolt_now-cellvolt_last)) 
+                        if 2 < cellvolt_max < self.param.OcvInflexionBelow-0.002 and 2<cellvolt_min<4.5 and deltvolt<0.003:
+                            cellvolt_sort=np.argsort(cellvolt_now)
+                            cellvolt_rank=list(np.argsort(cellvolt_sort)+1) 
+                            cellmin_num=list(cellvolt_now).index(cellvolt_min)+1
+                            cellmax_num=list(cellvolt_now).index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num, str(cellvolt_rank)]
+                        # elif 2<cellvolt_max<4.5 and 2<cellvolt_min<4.5 and deltvolt<0.005: 
+                        #     cellvolt_sort=np.argsort(cellvolt_now)
+                        #     cellvolt_rank=list(np.argsort(cellvolt_sort)+1)
+                        #     if not df_res.empty:
+                        #         df_res.loc[len(df_res)]=df_res.loc[len(df_res)-1]
+                        #         df_res.loc[len(df_res)-1,'cellvolt_rank']=str(cellvolt_rank)
+                        #         df_res.loc[len(df_res)-1,'time']=self.bmstime[i]
+                        #     elif not self.df_uniform.empty:
+                        #         df_res.loc[len(df_res)]=self.df_uniform.iloc[-1]
+                        #         df_res.loc[len(df_res)-1,'cellvolt_rank']=str(cellvolt_rank)
+                        #         df_res.loc[len(df_res)-1,'time']=self.bmstime[i]
+                        #     else:
+                        #         pass
+                    else:
+                        pass
+                else:
+                    pass
+            else:
+                standingtime2=0
+                pass   
+
+            #获取DVDQ算法所需数据——开始............................................................................................................
+            if charging==0: #判断充电开始
+                if self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and self.packcrnt[i+2]<=-1 and self.bms_soc[i]<40:     #充电开始
+                    charging=1
+                    if len(chrg_start)>len(chrg_end):
+                        chrg_start[-1]=i
+                    else:
+                        chrg_start.append(i)
+                else:
+                    pass
+
+            else: #充电中
+                if (self.bmstime[i+1]-self.bmstime[i]).total_seconds()>180 or (self.packcrnt[i]<-self.param.Capacity/2 and self.packcrnt[i+1]<-self.param.Capacity/2):  #如果充电过程中时间间隔>180s,则舍弃该次充电
+                    chrg_start.remove(chrg_start[-1])
+                    charging=0
+                    continue
+                elif self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and  self.packcrnt[i+2]>-1:  #判断电流波动时刻
+                    cellvolt_now=self._cellvolt_get(i+1)
+                    if max(cellvolt_now)>self.param.CellFullChrgVolt:   #电压>满充电压
+                        chrg_end.append(i+1)
+                        charging=0
+                        continue
+                    else:
+                        pass
+                elif self.packcrnt[i+1]>-0.1 and self.packcrnt[i+2]>-0.1:   #判断充电结束
+                    charging=0
+                    if len(chrg_start)>len(chrg_end):
+                        if self.bms_soc[i]>90:
+                            chrg_end.append(i)
+                        else:
+                            chrg_start.remove(chrg_start[-1])
+                            continue
+                    else:
+                        continue
+                elif i==len(self.packcrnt)-3 and self.packcrnt[i+1]<-1 and self.packcrnt[i+2]<-1:
+                    charging=0
+                    if len(chrg_start)>len(chrg_end) and self.bms_soc[i]>90:   #soc>90
+                        chrg_end.append(i)
+                        continue
+                    else:
+                        df_ram_lfp1=self.df_bms.iloc[chrg_start[-1]:]
+                        df_ram_lfp1['sn']=self.sn
+                        chrg_start.remove(chrg_start[-1])
+                        continue
+                else:
+                    continue   
+
+        if chrg_end:    #DVDQ方法计算soc差
+            peaksoc_list=[]
+            for i in range(len(chrg_end)):
+                peaksoc_list = []
+                self._celltemp_weight(chrg_start[i])
+                if min(self.celltemp)>10:
+                    for j in range(1, self.param.CellVoltNums + 1):
+                        cellvolt = self._singlevolt_get(i,j,2)  #取单体电压j的所有电压值
+                        cellvolt = list(cellvolt[chrg_start[i]:chrg_end[i]])
+                        time = list(self.bmstime[chrg_start[i]:chrg_end[i]])
+                        packcrnt = list(self.packcrnt[chrg_start[i]:chrg_end[i]])
+                        soc = list(self.bms_soc[chrg_start[i]:chrg_end[i]])
+                        peaksoc = self._dvdq_peak(time, soc, cellvolt, packcrnt)
+                        if peaksoc>1:
+                            peaksoc_list.append(peaksoc)    #计算到达峰值点的累计Soc
+                        else:
+                            pass
+                    if len(peaksoc_list)>self.param.CellVoltNums/2:
+                        peaksoc_max=max(peaksoc_list)
+                        peaksoc_min=min(peaksoc_list)
+                        peaksoc_maxnum=peaksoc_list.index(peaksoc_min)+1
+                        peaksoc_minnum=peaksoc_list.index(peaksoc_max)+1
+                        cellsoc_diff=peaksoc_max-peaksoc_min
+                        cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                        if not df_res.empty:
+                            cellvolt_rank=df_res.iloc[-1]['cellvolt_rank']
+                            df_res.loc[len(df_res)]=[self.bmstime[chrg_start[i]], self.sn, cellsoc_diff, 0, peaksoc_minnum, peaksoc_maxnum, cellvolt_rank]
+                        elif not self.df_uniform.empty:
+                            cellvolt_rank=self.df_uniform.iloc[-1]['cellvolt_rank']
+                            df_res.loc[len(df_res)]=[self.bmstime[chrg_start[i]], self.sn, cellsoc_diff, 0, peaksoc_minnum, peaksoc_maxnum, cellvolt_rank]
+                        else:
+                            pass
+                    else:
+                        pass
+                else:
+                    pass
+
+        #更新RAM的standingtime
+        df_ram_last3.loc[0]=[self.sn,self.bmstime[len(self.bmstime)-1],standingtime,standingtime1,standingtime2]
+        
+        if df_res.empty:
+            return pd.DataFrame(), df_ram_last3, df_ram_lfp1
+        else:
+            df_res.sort_values(by='time', ascending=True, inplace=True)
+            return df_res, df_ram_last3, df_ram_lfp1

+ 262 - 0
LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/CBMSSafetyWarning.py

@@ -0,0 +1,262 @@
+import pandas as pd
+import numpy as np
+import datetime
+import time
+from matplotlib import pyplot as plt
+import pymannkendall as mk
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam
+
+class SafetyWarning:
+    def __init__(self,sn,celltype,df_short,df_uniform,OutLineVol_Rate,df_soh):  #参数初始化
+
+        self.sn=sn
+        self.celltype=celltype
+        self.param=BatParam.BatParam(celltype)
+        self.df_short=df_short
+        self.df_uniform=df_uniform
+        self.OutLineVol_Rate=OutLineVol_Rate
+        self.df_soh=df_soh
+    
+    def diag(self):
+        if self.celltype<=50:
+            df_res=self._warning_diag()
+            return df_res    
+        else:
+            df_res=self._warning_diag()
+            return df_res
+        
+
+    #电池热安全预警诊断功能.................................................................................................
+    def _warning_diag(self):
+
+        df_res=pd.DataFrame(columns=['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice'])
+
+        time_now=datetime.datetime.now()
+        time_now=time_now.strftime('%Y-%m-%d %H:%M:%S')
+        time_sp='0000-00-00 00:00:00'
+
+        #参数初始化.......................................
+        voltsigmafault=0
+        uniformfault=0
+        cellshortfault=0
+        volt_rate=[]
+        R2_list=[]
+        voltsigmafault_list=[]
+        uniformfault_list=[]
+        mk_trend_list=[]
+        mk_p_list=[]
+        mk_z_list=[]
+        mk_Tau_list=[]
+        mk_slope_list=[]
+        mk_s_list=[]
+        mk_svar_list=[]
+
+        if not self.df_short.empty:
+            short_current=self.df_short['short_current']
+            short_current=short_current.str.replace("[", '')
+            short_current=short_current.str.replace("]", '')
+        
+        if not self.OutLineVol_Rate.empty:
+            volt_column = ['单体电压'+str(i) for i in range(1,self.param.CellVoltNums+1)]
+            self.OutLineVol_Rate['VolChng_Uni']=self.OutLineVol_Rate['VolChng_Uni'].str.replace("[","")
+            self.OutLineVol_Rate['VolChng_Uni']=self.OutLineVol_Rate['VolChng_Uni'].str.replace("]","")
+            self.OutLineVol_Rate['VolOl_Uni']=self.OutLineVol_Rate['VolOl_Uni'].str.replace("[","")
+            self.OutLineVol_Rate['VolOl_Uni']=self.OutLineVol_Rate['VolOl_Uni'].str.replace("]","")
+            Volt_3Sigma=self.OutLineVol_Rate['VolOl_Uni'].str.split(',',expand=True)
+            Volt_3Sigma.columns=volt_column
+
+            #电压变化率
+            VoltChange=self.OutLineVol_Rate['VolChng_Uni'].str.split(',',expand=True)
+            VoltChange.columns=volt_column
+            VoltChange['time']=self.OutLineVol_Rate['time']
+            VoltChange = VoltChange.reset_index(drop=True)
+            xtime1=VoltChange['time']
+            time0=time.mktime(VoltChange.loc[0,'time'].timetuple())
+            for i in range(0,len(VoltChange)):
+                VoltChange.loc[i,'time']=(time.mktime(VoltChange.loc[i,'time'].timetuple())-time0)/36000
+            
+        #计算漏电流离群度
+        if not self.df_short.empty:
+            self.df_short['cellshort_sigma']=0
+            for i in range(len(self.df_short)):
+                cellshort=eval(self.df_short.loc[i,'short_current'])
+                cellshort_std=np.std(cellshort)
+                cellshort_mean=np.mean(cellshort)
+                self.df_short.loc[i,'cellshort_sigma']=str(list((cellshort-cellshort_mean)/cellshort_std))
+            
+    
+        if not self.df_uniform.empty:
+            cellvolt_rank=self.df_uniform['cellvolt_rank']
+            cellvolt_rank=cellvolt_rank.str.replace("[", '')
+            cellvolt_rank=cellvolt_rank.str.replace("]", '')
+    
+        for i in range(self.param.CellVoltNums):
+            #漏电流故障判断...........................................................................
+            if not self.df_short.empty:
+                self.df_short['cellshort'+str(i+1)]=short_current.map(lambda x:eval(x.split(',')[i]))
+                cellshort=self.df_short['cellshort'+str(i+1)]
+                index_list=cellshort[cellshort<self.param.LeakCurrentLv2].index
+                if len(index_list)>1:
+                    for j in range(1,len(index_list)):
+                        if index_list[j]-index_list[j-1]==1:
+                            cellshort_sigma1=eval(self.df_short.loc[index_list[j],'cellshort_sigma'])
+                            cellshort_sigma2=eval(self.df_short.loc[index_list[j-1],'cellshort_sigma'])
+                            if cellshort_sigma1[i]<-3 or cellshort_sigma2[i]<-3:
+                                cellshortfault=1                       
+                        else:
+                            pass
+                else:
+                    pass
+            
+            #电压变化率及电压离群度.................................................................................
+            if not self.OutLineVol_Rate.empty and VoltChange.iloc[-1]['time']*36000>18*3600 and len(VoltChange)>5:
+
+                VoltChange[volt_column[i]]=VoltChange[volt_column[i]].map(lambda x:eval(x))
+                y=VoltChange[volt_column[i]]
+                volt3sigma=np.array(Volt_3Sigma[volt_column[i]].map(lambda x:eval(x)))
+                volt3sigma_sum=np.sum(volt3sigma<-3)
+                #电压变化率
+                a1,b1=np.polyfit(VoltChange['time'].tolist(),y.tolist(),1)
+                y1=a1*VoltChange['time']+b1
+                y_mean=y.mean()
+                R2=1-(np.sum((y1-y)**2))/(np.sum((y-y_mean)**2))
+                R2_list.append(R2)
+                
+                volt_rate.append(a1)
+                # plt.plot(xtime1,y1,label='单体电压'+str(i+1))
+                # plt.xlabel('时间', fontsize=25)
+                # plt.ylabel('SOC差', fontsize=25)
+                # plt.xticks(fontsize=20)
+                # plt.yticks(fontsize=20)
+                # plt.scatter( xtime1,VoltChange[volt_column[i]],marker='o')
+                # plt.legend(bbox_to_anchor=(1, 0), loc=3, fontsize=16)
+                # plt.title(self.celltype)
+                # plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
+                # plt.rcParams['axes.unicode_minus']=False #用来正常显示负号  
+                # # plt.show()
+                
+                if volt3sigma_sum>len(volt3sigma)/2:
+                    voltsigmafault=1
+                else:
+                    voltsigmafault=0
+                voltsigmafault_list.append(voltsigmafault)
+                
+                #mana-kendell趋势检验
+                mk_res=mk.regional_test(np.array(y)) 
+                mk_trend_list.append(mk_res.trend)
+                mk_p_list.append(mk_res.p)
+                mk_z_list.append(mk_res.z)
+                mk_Tau_list.append(mk_res.Tau)
+                mk_slope_list.append(mk_res.slope)
+                mk_s_list.append(mk_res.s)
+                mk_svar_list.append(mk_res.var_s)
+                """
+                trend:趋势;
+                h:有无趋势;
+                p:趋势的显著水平,越小趋势越明显;
+                z:检验统计量,正代表随时间增大趋势,负代表随时间减小趋势;
+                Tau:反映两个序列的相关性,接近1的值表示强烈的正相关,接近-1的值表示强烈的负相关;
+                s:Mann-Kendal的分数,如果S是一个正数,那么后一部分的观测值相比之前的观测值会趋向于变大;如果S是一个负数,那么后一部分的观测值相比之前的观测值会趋向于变小
+                slope:趋势斜率
+                """
+                # print('单体电压{}:\n'.format(i+1), mk_res)
+
+            else:
+                volt_rate.append(0)
+                R2_list.append(0)
+                voltsigmafault_list.append(0)
+                mk_trend_list.append(0)
+                mk_p_list.append(0)
+                mk_z_list.append(0)
+                mk_Tau_list.append(0)
+                mk_slope_list.append(0)
+                mk_s_list.append(0)
+                mk_svar_list.append(0)
+
+            #电芯SOC排名判断.............................................................................
+            if not self.df_uniform.empty:
+                self.df_uniform['cellvolt_rank'+str(i+1)]=cellvolt_rank.map(lambda x:eval(x.split(',')[i]))
+                
+                if max(self.df_uniform['cellvolt_rank'+str(i+1)])<5:
+                    uniformfault=1
+                else:
+                    uniformfault=0
+            else:
+                uniformfault=0
+            uniformfault_list.append(uniformfault)
+            
+            #漏电流热失控预警确认........................................................
+            if cellshortfault==1:
+                faultcode=110
+                faultlv=4
+                faultinfo='电芯{}发生热失控安全预警'.format(i+1)
+                faultadvice='1联系用户远离电池,立刻召回电池'
+                df_res.loc[0]=[time_now, time_sp, self.sn, faultcode, faultlv, faultinfo, faultadvice]
+                break
+            else:
+                pass  
+        
+        # plt.show()   
+        #电池电压变化率离群度计算...............................................................................
+        volt_rate_std=np.std(volt_rate)
+        volt_rate_mean=np.mean(volt_rate)
+        volt_rate_3sigma=(np.array(volt_rate)-volt_rate_mean)/volt_rate_std
+        
+        #mk离群度计算
+        mk_slope_std=np.std(mk_slope_list)
+        mk_slope_mean=np.mean(mk_slope_list)
+        mk_slope_3sigma=(np.array(mk_slope_list)-mk_slope_mean)/mk_slope_std
+
+        # mk_s_std=np.std(mk_s_list)
+        # mk_s_mean=np.mean(mk_s_list)
+        # mk_s_3sigma=(np.array(mk_s_list)-mk_s_mean)/mk_s_std 
+
+        mk_z_std=np.std(mk_z_list)
+        mk_z_mean=np.mean(mk_z_list)
+        mk_z_3sigma=(np.array(mk_z_list)-mk_z_mean)/mk_z_std  
+
+        if not self.df_soh.empty and self.celltype<50:
+            cellsoh=eval(self.df_soh.loc[0,'cellsoh'])
+            cellsoh_std=np.std(cellsoh)
+            cellsoh_mean=np.mean(cellsoh)
+            cellsoh_3sigma=((np.array(cellsoh)-cellsoh_mean)/cellsoh_std)
+        else:
+            cellsoh_3sigma=[0]*self.param.CellVoltNums
+        
+        #电压/SOC变化率
+        # for i in range(len(volt_rate)):
+        #     if volt_rate[i]<self.param.TrwVoltRate and volt_rate_3sigma[i]<-3 and abs(cellsoh_3sigma[i])<2.5 and  voltsigmafault_list[i]==1:
+        #         faultcode=110
+        #         faultlv=4
+        #         faultinfo='电芯{}发生热失控安全预警'.format(i+1)
+        #         faultadvice='2联系用户远离电池,立刻召回电池'
+        #         df_res.loc[0]=[time_now, time_sp, self.sn, faultcode, faultlv, faultinfo, faultadvice]
+        #     else:
+        #         pass
+
+        #mana-kendall趋势检测
+        for i in range(len(mk_p_list)):
+            #适用动态工况判断
+            if mk_trend_list[i]=='decreasing' and mk_p_list[i]<self.param.mk_p and mk_z_list[i]<self.param.mk_z and mk_Tau_list[i]<self.param.mk_Tau and mk_slope_3sigma[i]<-3 and mk_slope_list[i]<self.param.mk_slope and abs(cellsoh_3sigma[i])<2.5 and volt_rate_3sigma[i]<-3:
+                faultcode=110
+                faultlv=4
+                faultinfo='电芯{}发生热失控安全预警'.format(i+1)
+                faultadvice='2联系用户远离电池,立刻召回电池'
+                df_res.loc[0]=[time_now, time_sp, self.sn, faultcode, faultlv, faultinfo, faultadvice]
+            #适用静态工况判断
+            elif self.celltype<=50 and mk_trend_list[i]=='decreasing' and mk_p_list[i]<self.param.mk_p and mk_z_list[i]<-6 and (mk_Tau_list[i]<-0.9 or mk_z_3sigma[i]<-3) and mk_slope_3sigma[i]<-3.5 and mk_slope_list[i]<-0.025 and volt_rate_3sigma[i]<-3:
+                faultcode=110
+                faultlv=4
+                faultinfo='电芯{}发生热失控安全预警'.format(i+1)
+                faultadvice='2联系用户远离电池,立刻召回电池'
+                df_res.loc[0]=[time_now, time_sp, self.sn, faultcode, faultlv, faultinfo, faultadvice]
+            elif self.celltype>50 and mk_trend_list[i]=='decreasing' and mk_p_list[i]<self.param.mk_p and mk_z_list[i]<-6 and (mk_Tau_list[i]<-0.9 or mk_z_3sigma[i]<-3) and mk_slope_3sigma[i]<-3.5 and mk_slope_list[i]<-0.25 and volt_rate_3sigma[i]<-3:
+                faultcode=110
+                faultlv=4
+                faultinfo='电芯{}发生热失控安全预警'.format(i+1)
+                faultadvice='2联系用户远离电池,立刻召回电池'
+                df_res.loc[0]=[time_now, time_sp, self.sn, faultcode, faultlv, faultinfo, faultadvice]
+            else:
+                pass
+
+        return df_res

BIN
LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/Uniform表单.xlsx


+ 246 - 0
LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/VoltStray.py

@@ -0,0 +1,246 @@
+from re import X
+import pandas as pd
+import numpy as np
+from pandas.core.frame import DataFrame
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam
+import pandas as pd
+# 计算充电过程
+def preprocess(df):
+    # 滤除前后电压存在一增一减的情况(采样异常)
+    pass
+
+# 计算SOC变化率  
+def cal_volt_change(dfin, volt_column):
+    
+    df = dfin.copy()
+    df_volt_rolling = df[volt_column] 
+
+    df_volt_rolling_sum=df_volt_rolling.sum(1)-df_volt_rolling.max(1)
+    df_volt_rolling_sum=df_volt_rolling_sum-df_volt_rolling.min(1)
+    mean1 = df_volt_rolling_sum/(len(volt_column)-2)
+    df_volt_rolling_norm = df_volt_rolling.sub(mean1, axis=0)#.div(std,axis=0)
+    df_volt_rolling_norm = df_volt_rolling_norm.reset_index(drop=True)#和均值的距离
+
+    return df_volt_rolling_norm
+
+# 计算电压离群  
+def cal_volt_sigma(dfin, volt_column):
+    
+    df = dfin.copy()
+    df_volt_rolling = df[volt_column] 
+    
+    mean1=df_volt_rolling.mean(axis=1)
+    std = df_volt_rolling.std(axis=1)
+    std = std.replace(0,0.000001)
+    df_volt_rolling = df_volt_rolling.sub(mean1, axis=0).div(std,axis=0)
+    df_volt_rolling = df_volt_rolling.reset_index(drop=True)#分布
+
+    return df_volt_rolling
+
+
+# # 计算电压变化量的偏离度    
+# def cal_voltdiff_uniform(dfin, volt_column, window=10, step=5, window2=10, step2=5,threshold=3):
+    
+#     df = dfin.copy()
+#     time_list = dfin['time'].tolist()
+
+#     # 电压滤波
+#     df_volt = df[volt_column] 
+#     df_volt_rolling = df_volt.rolling(window).mean()[window-1::step]  # 滑动平均值
+#     time_list = time_list[window-1::step] 
+
+#     # 计算电压变化量的绝对值(# 计算前后的差值的绝对值,  时间列-1)
+#     df_volt_diff = abs(df_volt_rolling.diff()[1:])
+#     df_volt_diff = df_volt_diff.reset_index(drop=True)
+#     time_list = time_list[1:]
+
+#     # 压差归一化(偏离度)
+#     # mean = df_volt_diff.mean(axis=1)
+#     # std = df_volt_diff.std(axis=1)
+#     # df_voltdiff_norm = df_volt_diff.sub(mean, axis=0).div(std,axis=0)
+#     df_voltdiff_norm = df_volt_diff.copy()
+
+#     # 压差偏离度滑动平均滤波
+#     df_voltdiff_rolling = df_voltdiff_norm.rolling(window2).mean()[window2-1::step2]  # 滑动平均值
+#     time_list = time_list[window2-1::step2]
+#     df_voltdiff_rolling_sum=df_voltdiff_rolling.sum(1)-df_voltdiff_rolling.max(1)
+#     df_voltdiff_rolling_sum=df_voltdiff_rolling_sum-df_voltdiff_rolling.min(1)
+#     mean = df_voltdiff_rolling_sum/(len(volt_column)-2)
+#     std = df_voltdiff_rolling.std(axis=1)
+#     # mean = [np.array(sorted(x)[1:-1]).mean() for x in df_voltdiff_rolling.values]
+#     # std = [np.array(sorted(x)[1:-1]).std() for x in df_voltdiff_rolling.values]
+#     df_voltdiff_rolling_norm = df_voltdiff_rolling.sub(mean, axis=0)#.div(std,axis=0)
+#     df_voltdiff_rolling_norm = df_voltdiff_rolling_norm.reset_index(drop=True)
+#     return df_voltdiff_rolling_norm, time_list
+def main(sn,df_bms,celltype):
+    param=BatParam.BatParam(celltype)
+    df_bms['总电流[A]']=df_bms['总电流[A]']*param.PackCrntDec
+    df_bms.rename(columns = {'总电流[A]':'PackCrnt'}, inplace=True)
+    df_bms['time']=pd.to_datetime(df_bms['时间戳'], format='%Y-%m-%d %H:%M:%S')
+    volt_column = ['单体电压'+str(i) for i in range(1,param.CellVoltNums+1)]
+    columns=['time']+volt_column
+    df_bms=df_bms[(df_bms['SOC[%]']>10)]
+    df_bms=df_bms[(df_bms['PackCrnt']<1)]
+    # df_chrg=df_bms[(df_bms['PackCrnt']<-1)]
+
+    #电压/SOC变化率计算
+    if celltype<50:
+        df_ori = df_bms[columns]
+        df = df_ori.drop_duplicates(subset=['time']) # 删除时间相同的数据
+        df= df.set_index('time')
+        df=df[(df[volt_column]>2000) & (df[volt_column]<4500)]
+        df[volt_column]=pd.DataFrame(df[volt_column],dtype=np.float)
+        df=df.resample('H').mean()  #取一个小时的平均值
+        df=df.dropna(how='any')
+        time_list1=df.index.tolist()
+    
+        fun=lambda x: np.interp(x/1000, param.LookTab_OCV, param.LookTab_SOC)
+        df_soc=df.applymap(fun)
+
+        VolChng = cal_volt_change(df_soc,volt_column)
+    else:
+        # df_bms=df_bms[(df_bms['PackCrnt']>-0.1) & (df_bms['PackCrnt']<0.1)]
+        df_ori = df_bms[columns]
+        df = df_ori.drop_duplicates(subset=['time']) # 删除时间相同的数据
+        df= df.set_index('time')
+        df=df[(df[volt_column]>3200) & (df[volt_column]<3400)]
+        df[volt_column]=pd.DataFrame(df[volt_column],dtype=np.float)
+        df=df.resample('H').mean()  #取一个小时的平均值
+        df=df.dropna(how='any')
+        time_list1=df.index.tolist()
+
+        VolChng = cal_volt_change(df,volt_column)
+    
+
+    VolSigma = cal_volt_sigma(df,volt_column)
+
+    OutLineVol=DataFrame(columns=['time','sn','VolOl_Uni','VolChng_Uni'])
+
+    #电压变化率和离群度计算
+    if len(VolChng)>5 and len(VolSigma)>5:
+        VolChng['time'] = time_list1
+        VolChng= VolChng.set_index('time')
+        VolChng_Uni_result=VolChng.values.tolist()#改
+        
+        VolSigma['time'] = time_list1
+        VolSigma= VolSigma.set_index('time')
+        VolOl_Uni_result=VolSigma.values.tolist()#改
+       
+        for i in range(0,len(VolChng)):
+            OutLineVol.loc[i,'VolOl_Uni']=str(list(np.around(VolOl_Uni_result[i],decimals=2)))
+            OutLineVol.loc[i,'VolChng_Uni']=str(list(np.around(VolChng_Uni_result[i],decimals=2)))
+        OutLineVol=OutLineVol[~OutLineVol['VolOl_Uni'].str.contains('nan')]
+        OutLineVol=OutLineVol[~OutLineVol['VolChng_Uni'].str.contains('nan')]
+        OutLineVol=OutLineVol.applymap((lambda x:''.join(x.split()) if type(x) is str else x)) 
+        OutLineVol=OutLineVol.reset_index(drop=True) 
+        OutLineVol['time']= VolSigma.index
+        OutLineVol['sn']=sn
+
+    return(OutLineVol)
+    # this_alarm = {}
+    # df_alarm = df_voltdiff_rolling_norm[abs(df_voltdiff_rolling_norm)>threshold].dropna(how='all')
+    # for index in df_alarm.index:
+    #     df_temp = df_alarm.loc[index, :].dropna(how='all', axis=0)
+    #     this_alarm.update({df_cell_volt.loc[index+1, 'date']:[str(df_temp.keys().tolist()), str([round(x, 2) for x in df_temp.values.tolist()])]})
+    # df_alarm1 = pd.DataFrame(this_alarm)
+
+
+    # return pd.DataFrame(df_alarm1.values.T, index=df_alarm1.columns, columns=df_alarm1.index), pd.DataFrame(df_alarm2.values.T, index=df_alarm2.columns, columns=df_alarm2.index)
+
+    # # 孤立森林算法
+    # def iso_froest(df):
+
+    #     #1. 生成训练数据
+    #     rng = np.random.RandomState(42)
+    #     X = 0.3 * rng.randn(100, 2) #生成100 行,2 列
+    #     X_train = np.r_[X + 2, X - 2]
+    #     print(X_train)
+    #     # 产生一些异常数据
+    #     X_outliers = rng.uniform(low=-4, high=4, size=(20, 2))
+    #     iForest= IsolationForest(contamination=0.1)
+    #     iForest = iForest.fit(X_train)
+    #     #预测
+    #     pred = iForest.predict(X_outliers)
+    #     print(pred)
+    #     # [-1 1 -1 -1 -1 -1 -1 1 -
+
+# # 计算相关系数
+# def cal_coff(df):
+#     cc_mean = np.mean(df, axis=0)  #axis=0,表示按列求均值 ——— 即第一维
+#     cc_std = np.std(df, axis=0)
+#     cc_zscore = (df-cc_mean)/cc_std   #标准化
+#     cc_zscore = cc_zscore.dropna(axis=0, how='any')
+
+#     cc_zscore_corr = cc_zscore.corr(method='spearman')
+    
+#     result = []
+#     for i in range(len(cc_zscore_corr)):
+#         v = abs(np.array((sorted(cc_zscore_corr.iloc[i]))[2:-1])).mean() # 去掉1 和两个最小值后求均值
+#         result.append(v)
+#     return cc_zscore_corr, result
+
+
+# def instorage(sn, df_voltdiff_result, df_volt_result):
+    
+#     df_all_result = pd.DataFrame(columns=['sn', 'time', 'cellnum', 'value', 'type'])
+    
+#     value_list = []
+#     cellnum_list = []
+#     time_list = []
+#     type_list = []
+#     df_result = df_voltdiff_result.copy().drop(columns='time')
+#     time_list_1 = df_voltdiff_result['time']
+#     df_result = df_result[(df_result>3) | (df_result<-3)].dropna(axis=0, how='all').dropna(axis=1, how='all')
+#     for column in df_result.columns:
+#         df = df_result[[column]].dropna(axis=0, how='all')
+#         value_list.extend(df[column].tolist())
+#         cellnum_list.extend([column]*len(df))
+#         time_list.extend([time_list_1[x] for x in df.index])
+#     length_1 = len(value_list)
+    
+
+
+#     df_result = df_volt_result.copy().drop(columns='time')
+#     time_list_2 = df_volt_result['time']
+#     df_result = df_result[(df_result>3) | (df_result<-3)].dropna(axis=0, how='all').dropna(axis=1, how='all')
+#     for column in df_result.columns:
+#         df = df_result[[column]].dropna(axis=0, how='all')
+#         value_list.extend(df[column].tolist())
+#         cellnum_list.extend([column]*len(df))
+#         time_list.extend([time_list_2[x] for x in df.index])
+
+#     length_2 = len(value_list) - length_1
+#     type_list.extend(['电压变化量离群'] * length_1)
+#     type_list.extend(['电压离群'] * length_2)
+#     df_all_result['sn'] = [sn] * len(value_list)
+#     df_all_result['cellnum'] = cellnum_list
+#     df_all_result['value'] = value_list
+#     df_all_result['time'] = time_list
+#     df_all_result['type'] = type_list
+#     return df_all_result
+
+# # 报警.如果在某个窗口内,有超过ratio个的点,偏离度超过threshold, 则报警
+# def alarm(dfin, volt_column, alarm_window=10, alarm_ratio=0.8, alarm_threshold=2.5):
+
+    
+#     time_list = dfin['time'].tolist()
+#     df_result = dfin[volt_column].copy()
+#     alarm_result = pd.DataFrame(columns=['type', 'num', 'alarm_time'])      
+#     df_result_1 = df_result.copy()
+#     df_result_1[df_result_1<alarm_threshold] = 0
+#     df_result_1[df_result_1>alarm_threshold] = 1    
+#     df_result_1 = df_result_1.rolling(alarm_window).sum()
+#     for column in volt_column:
+#         if len(df_result_1[df_result_1[column]>alarm_window * alarm_ratio])>0:
+#             alarm_result = alarm_result.append({'type':'1', 'num':column, 'alarm_time':time_list[df_result_1[df_result_1[column]>alarm_window * alarm_ratio].index[0]]}, ignore_index=True)
+
+#     # time_list = time_list[window-1::step] 
+
+#     df_result_2 = df_result.copy()
+#     df_result_2[df_result_2>-alarm_threshold] = 0
+#     df_result_2[df_result_2<-alarm_threshold] = 1
+#     df_result_2 = df_result_2.rolling(alarm_window).sum()
+#     for column in volt_column:
+#         if len(df_result_2[df_result_2[column]>alarm_window * alarm_ratio])>0:
+#             alarm_result = alarm_result.append({'type':'2', 'num':column, 'alarm_time':time_list[df_result_2[df_result_2[column]>alarm_window * alarm_ratio].index[0]]}, ignore_index=True)
+#     return alarm_result

BIN
LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/电压变化率离群度表单.xlsx


+ 227 - 0
LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/main.py

@@ -0,0 +1,227 @@
+import pandas as pd
+import pymysql
+from LIB.BACKEND import DBManager, Log
+from apscheduler.schedulers.blocking import BlockingScheduler
+import datetime
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import DBDownload
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import log
+import CBMSBatInterShort
+import CBMSBatUniform
+import VoltStray
+import CBMSSafetyWarning
+
+#电池热安全预警核心算法函数......................................................................................................................
+def saftywarning_cal():
+    global SNnums
+    global df_warning_ram
+    global df_warning_ram1
+    global df_warning_ram2
+    global df_warning_ram3
+    global df_lfp_ram
+    global df_lfp_ram1
+
+    pd.set_option('display.width', 300) # 设置字符显示宽度
+    pd.set_option('display.max_rows', None) # 设置显示最大行
+    pd.set_option('display.max_columns', None) # 设置显示最大列,None为显示所有列
+    
+    # start=time.time()
+    now_time=datetime.datetime.now()
+    start_time=now_time-datetime.timedelta(hours=6)
+    start_time1=now_time-datetime.timedelta(days=7)
+    start_time2=now_time-datetime.timedelta(days=3)
+    start_time3=now_time-datetime.timedelta(days=1)
+    start_time=start_time.strftime('%Y-%m-%d %H:%M:%S')
+    start_time1=start_time1.strftime('%Y-%m-%d %H:%M:%S')
+    start_time2=start_time2.strftime('%Y-%m-%d %H:%M:%S')
+    start_time3=start_time3.strftime('%Y-%m-%d %H:%M:%S')
+    end_time=now_time.strftime('%Y-%m-%d %H:%M:%S')
+
+
+    #数据库配置
+    host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port=3306
+    user='qx_read'
+    password='Qx@123456'
+
+    #读取故障结果库中code==110且end_time='0000-00-00 00:00:00'...............................
+    db='safety_platform'
+    mysql = pymysql.connect (host=host, user=user, password=password, port=port, database=db)
+    cursor = mysql.cursor()
+    param='start_time,end_time,product_id,code,level,info,advice'
+    tablename='all_fault_info'
+    sql =  "select %s from %s where code=110 and end_time='0000-00-00 00:00:00'" %(param,tablename)
+    cursor.execute(sql)
+    res = cursor.fetchall()
+    df_fault_ram= pd.DataFrame(res,columns=param.split(','))
+    cursor.close()
+    mysql.close()
+
+    for sn in SNnums:
+        if 'PK500' in sn:
+            celltype=1 #6040三元电芯
+        elif 'PK502' in sn:
+            celltype=2 #4840三元电芯
+        elif 'K504B' in sn:
+            celltype=99    #60ah林磷酸铁锂电芯
+        elif 'MGMLXN750' in sn:
+            celltype=3 #力信50ah三元电芯
+        elif 'MGMCLN750' or 'UD' in sn: 
+            celltype=4 #CATL 50ah三元电芯
+        else:
+            print('SN:{},未找到对应电池类型!!!'.format(sn))
+            continue
+            # sys.exit()
+
+        #读取原始数据库数据........................................................................................................................................................
+        dbManager = DBManager.DBManager()
+        df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms'])
+        df_bms = df_data['bms']
+        # print(df_bms)
+        # df_bms.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\98Download\\'+sn+'_BMS_'+tim+'.csv',encoding='GB18030')
+
+        #读取结果数据库数据........................................................................................................................................................
+        db='qx_cas'
+        mode=1
+        DBRead=DBDownload.DBDownload(host, port, db, user, password,mode)
+        with DBRead as DBRead:
+            df_soh=DBRead.getdata('time_st,sn,soh,cellsoh', tablename='cellstateestimation_soh', sn=sn, timename='time_sp', st=start_time, sp=end_time)
+            df_uniform=DBRead.getdata('time,sn,cellsoc_diff,cellvolt_diff,cellmin_num,cellmax_num,cellvolt_rank', tablename='cellstateestimation_uniform_socvoltdiff', sn=sn, timename='time', st=start_time1, sp=end_time)
+            df_uniform.loc[0,'cellvolt_rank']=str([1]*20)
+            df_voltsigma=pd.DataFrame()
+        if not df_bms.empty:
+            #ram处理...............................................................................................................
+            df_warning_ram_sn=df_warning_ram[df_warning_ram['sn']==sn]
+            df_warning_ram_sn1=df_warning_ram1[df_warning_ram1['sn']==sn]
+            df_warning_ram_sn2=df_warning_ram2[df_warning_ram2['sn']==sn]
+            df_warning_ram_sn3=df_warning_ram3[df_warning_ram3['sn']==sn]
+            df_warning_ram_sn.reset_index(inplace=True,drop=True)     #重置索引
+            df_warning_ram_sn1.reset_index(inplace=True,drop=True)     #重置索引
+            df_warning_ram_sn2.reset_index(inplace=True,drop=True)     #重置索引
+            df_warning_ram_sn3.reset_index(inplace=True,drop=True)     #重置索引
+            if celltype>50 and (not df_lfp_ram.empty):
+                df_lfp_ram_sn=df_lfp_ram[df_lfp_ram['sn']==sn]
+                df_lfp_ram_sn.reset_index(inplace=True,drop=True)     #重置索引
+            else:
+                df_lfp_ram_sn=pd.DataFrame()
+                df_lfp_ram=pd.DataFrame(columns=df_bms.columns.tolist()+['sn'])
+            if celltype>50 and (not df_lfp_ram1.empty):
+                df_lfp_ram_sn1=df_lfp_ram1[df_lfp_ram1['sn']==sn]
+                df_lfp_ram_sn1.reset_index(inplace=True,drop=True)     #重置索引
+            else:
+                df_lfp_ram_sn1=pd.DataFrame()
+                df_lfp_ram1=pd.DataFrame(columns=df_bms.columns.tolist()+['sn'])
+
+            #内短路计算..................................................................................................................................................
+            BatShort=CBMSBatInterShort.BatInterShort(sn,celltype,df_bms,df_soh,df_warning_ram_sn,df_warning_ram_sn1,df_warning_ram_sn2,df_warning_ram_sn3,df_lfp_ram_sn)
+            df_short_res, df_ram_res, df_ram_res1, df_ram_res2, df_ram_res3, df_ram_res4=BatShort.intershort() 
+            if not df_short_res.empty:
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\07BatSafetyWarning\内短路.txt','a') as file:
+                    file.write(str(df_short_res)+'\n')
+            
+            #静置电压排名..................................................................................................................................................
+            BatUniform=CBMSBatUniform.BatUniform(sn,celltype,df_bms,df_uniform,df_ram_res3,df_lfp_ram_sn1)
+            df_rank_res, df_ram_res3, df_ram_res5=BatUniform.batuniform()
+            if not df_rank_res.empty:
+                df_uniform=df_rank_res
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\07BatSafetyWarning\电压排名.txt','a') as file:
+                    file.write(str(df_rank_res)+'\n')
+            
+            #电压离群.....................................................................................................................................................
+            df_voltsigma=VoltStray.main(sn,df_bms,celltype)
+            if not df_voltsigma.empty:
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\07BatSafetyWarning\电压离群.txt','a') as file:
+                    file.write(str(df_voltsigma)+'\n')
+
+            #ram处理................................................................................................................
+            df_warning_ram=df_warning_ram.drop(df_warning_ram[df_warning_ram.sn==sn].index)
+            df_warning_ram1=df_warning_ram1.drop(df_warning_ram1[df_warning_ram1.sn==sn].index)
+            df_warning_ram2=df_warning_ram2.drop(df_warning_ram2[df_warning_ram2.sn==sn].index)
+            df_warning_ram3=df_warning_ram3.drop(df_warning_ram3[df_warning_ram3.sn==sn].index)
+
+            df_warning_ram=pd.concat([df_warning_ram,df_ram_res],ignore_index=True)
+            df_warning_ram1=pd.concat([df_warning_ram1,df_ram_res1],ignore_index=True)
+            df_warning_ram2=pd.concat([df_warning_ram2,df_ram_res2],ignore_index=True)
+            df_warning_ram3=pd.concat([df_warning_ram3,df_ram_res3],ignore_index=True)
+            
+            if celltype>50:
+                df_lfp_ram=df_lfp_ram.drop(df_lfp_ram[df_lfp_ram.sn==sn].index)
+                df_lfp_ram=pd.concat([df_lfp_ram,df_ram_res4],ignore_index=True)
+                df_lfp_ram1=df_lfp_ram1.drop(df_lfp_ram1[df_lfp_ram1.sn==sn].index)
+                df_lfp_ram1=pd.concat([df_lfp_ram1,df_ram_res5],ignore_index=True)
+            
+
+        #电池热安全预警..............................................................................................................................................................
+        #读取内短路、析锂和一致性结果数据库数据
+        db='qx_cas'
+        mode=2
+        tablename1='cellstateestimation_intershort'
+        tablename3='cellstateestimation_uniform_socvoltdiff'
+        tablename4='outlier_voltchangeratio'   #电压离群表单
+        DBRead=DBDownload.DBDownload(host, port, db, user, password,mode)
+        with DBRead as DBRead:
+            df_short=DBRead.getdata('time_sp,sn,short_current', tablename=tablename1, sn=sn, timename='time_sp', st=start_time1, sp=end_time)
+            df_uniform=DBRead.getdata('time,sn,cellsoc_diff,cellvolt_diff,cellmin_num,cellmax_num,cellvolt_rank', tablename=tablename3, sn=sn, timename='time', st=start_time2, sp=end_time)
+            df_voltsigma=DBRead.getdata('time,sn,VolOl_Uni,VolChng_Uni', tablename=tablename4, sn=sn, timename='time', st=start_time3, sp=end_time)
+
+        #获取sn的故障RAM
+        df_fault_ram_sn=df_fault_ram[df_fault_ram['product_id']==sn]
+        
+        #热安全预警
+        if df_fault_ram_sn.empty:
+            BatWarning=CBMSSafetyWarning.SafetyWarning(sn,celltype,df_short,df_uniform,df_voltsigma,df_soh)
+            df_warning_res=BatWarning.diag()
+            #当前热失控故障写入数据库
+            if not df_warning_res.empty:
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\07BatSafetyWarning\热失控预警.txt','a',encoding="utf-8") as file:
+                    file.write(str(tuple(df_warning_res.iloc[-1]))+'\n')
+        
+        else:
+            fault_time=datetime.datetime.strptime(df_fault_ram_sn.iloc[-1]['start_time'], '%Y-%m-%d %H:%M:%S')
+            if (now_time-fault_time).total_seconds()>3*24*3600:   #df_warning_end历史故障筛选并更改数据库故障结束时间
+                df_fault_ram_sn['end_time']=end_time
+                df_fault_ram_sn['Batpos']=1
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\07BatSafetyWarning\热失控预警.txt','a',encoding="utf-8") as file:
+                    file.write(str(tuple(df_warning_res.iloc[-1]))+'\n')
+
+
+#...............................................主函数起定时作用.......................................................................................................................
+if __name__ == "__main__":
+    
+    excelpath=r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\sn-20210903.xlsx'
+    SNdata_6060 = pd.read_excel(excelpath, sheet_name='科易6060')
+    SNdata_6040 = pd.read_excel(excelpath, sheet_name='科易6040')
+    SNdata_4840 = pd.read_excel(excelpath, sheet_name='科易4840')
+    SNdata_L7255 = pd.read_excel(excelpath, sheet_name='格林美-力信7255')
+    SNdata_C7255 = pd.read_excel(excelpath, sheet_name='格林美-CATL7255')
+    SNdata_U7255 = pd.read_excel(excelpath, sheet_name='优旦7255')
+    SNnums_6060=SNdata_6060['SN号'].tolist()
+    SNnums_6040=SNdata_6040['SN号'].tolist()
+    SNnums_4840=SNdata_4840['SN号'].tolist()
+    SNnums_L7255=SNdata_L7255['SN号'].tolist()
+    SNnums_C7255=SNdata_C7255['SN号'].tolist()
+    SNnums_U7255=SNdata_U7255['SN号'].tolist()
+    SNnums=SNnums_L7255 + SNnums_C7255 + SNnums_U7255 + SNnums_6040 + SNnums_4840 + SNnums_6060
+    SNnums=['MGMCLN750N215I091']
+    
+    mylog=log.Mylog('log_warning.txt','error')
+    mylog.logcfg()
+
+    #............................模块运行前,先读取数据库中所有结束时间为0的数据,需要从数据库中读取...................................
+    df_warning_ram=pd.DataFrame(columns=['sn','time','deltsoc','cellsoc'])
+    df_warning_ram1=pd.DataFrame(columns=['sn','time1','deltsoc1'])
+    df_warning_ram2=pd.DataFrame(columns=['sn','time2','deltAs2'])
+    df_warning_ram3=pd.DataFrame(columns=['sn','time3','standingtime','standingtime1','standingtime2'])
+    df_lfp_ram=pd.DataFrame()
+    df_lfp_ram1=pd.DataFrame()
+
+
+    #定时任务.......................................................................................................................................................................
+    scheduler = BlockingScheduler()
+    scheduler.add_job(saftywarning_cal, 'interval', hours=6)
+
+    try:  
+        scheduler.start()
+    except Exception as e:
+        scheduler.shutdown()
+        print(repr(e))
+        mylog.logopt(e)

+ 134 - 0
LIB/MIDDLE/CellStateEstimation/Common/BatParam.py

@@ -0,0 +1,134 @@
+
+#定义电池参数
+from types import CellType
+import sys
+
+class BatParam:
+
+    def __init__(self,celltype):
+
+        # if 'PK500' in sn:
+        #     self.celltype=1 #6040三元电芯
+        # elif 'PK502' in sn:
+        #     self.celltype=2 #4840三元电芯
+        # elif 'PK504' in sn:
+        #     self.celltype=99    #60ah林磷酸铁锂电芯
+        # elif 'MGMLXN750' in sn:
+        #     self.celltype=3 #力信50ah三元电芯
+        # elif 'MGMCLN750' in sn: 
+        #     self.celltype=4 #CATL 50ah三元电芯
+        # else:
+        #     print('未找到对应电池编号!!!')
+        #     sys.exit()
+
+        if celltype==1: #6040
+            self.Capacity = 41
+            self.PackFullChrgVolt=69.99
+            self.CellFullChrgVolt=4.2
+            self.CellVoltNums=17
+            self.CellTempNums=4
+            self.FullChrgSoc=98
+            self.PeakSoc=57
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0,	    3.5348,	8.3581,	13.181,	18.004,	22.827,	27.651,	32.474,	37.297,	42.120,	46.944,	51.767,	56.590,	61.413,	66.237,	71.060,	75.883,	80.707,	85.530,	90.353,	95.176,	100,   105]
+            self.LookTab_OCV = [3.3159,	3.4384,	3.4774,	3.5156,	3.5478,	3.5748,	3.6058,	3.6238,	3.638,	3.6535,	3.6715,	3.6951,	3.7279,	3.7757,	3.8126,	3.8529,	3.8969,	3.9446,	3.9946,	4.0491,	4.109,	4.183, 4.263]
+        
+        elif celltype==2: #4840
+            self.Capacity = 41
+            self.PackFullChrgVolt=69.99
+            self.CellFullChrgVolt=4.2
+            self.CellVoltNums=14
+            self.CellTempNums=4
+            self.FullChrgSoc=98
+            self.PeakSoc=57
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0,	    3.5348,	8.3581,	13.181,	18.004,	22.827,	27.651,	32.474,	37.297,	42.120,	46.944,	51.767,	56.590,	61.413,	66.237,	71.060,	75.883,	80.707,	85.530,	90.353,	95.176,	100,   105]
+            self.LookTab_OCV = [3.3159,	3.4384,	3.4774,	3.5156,	3.5478,	3.5748,	3.6058,	3.6238,	3.638,	3.6535,	3.6715,	3.6951,	3.7279,	3.7757,	3.8126,	3.8529,	3.8969,	3.9446,	3.9946,	4.0491,	4.109,	4.183, 4.263]
+        
+        elif celltype==3:   #力信50ah三元电芯
+            self.Capacity = 51
+            self.PackFullChrgVolt=80
+            self.CellFullChrgVolt=4.2
+            self.CellVoltNums=20
+            self.CellTempNums=4
+            self.FullChrgSoc=98
+            self.PeakSoc=57
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0,	    5,	    10,	    15,	    20,	    25,	    30,	    35,	    40,	    45,	    50,	    55,	    60,	    65,	    70,	    75,	    80,	    85,	    90,	    95,	    100,   105]
+            self.LookTab_OCV = [3.357, 	3.455, 	3.493, 	3.540, 	3.577, 	3.605, 	3.622, 	3.638, 	3.655, 	3.677, 	3.707, 	3.757, 	3.815, 	3.866, 	3.920, 	3.976, 	4.036, 	4.099, 	4.166, 	4.237, 	4.325, 4.415]
+        
+        elif celltype==4:   #CATL 50ah三元电芯
+            self.Capacity = 50
+            self.PackFullChrgVolt=80
+            self.CellFullChrgVolt=4.2
+            self.CellVoltNums=20
+            self.CellTempNums=2
+            self.FullChrgSoc=98
+            self.PeakSoc=57
+            self.PackCrntDec=-1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0,	    5,	    10,	    15,	    20,	    25,	    30,	    35,	    40,	    45,	    50,	    55,	    60,	    65,	    70,	    75,	    80,	    85,	    90,	    95,	    100,   105]
+            self.LookTab_OCV = [3.152, 	3.397, 	3.438, 	3.481, 	3.523, 	3.560, 	3.586, 	3.604, 	3.620, 	3.638, 	3.661, 	3.693, 	3.748, 	3.803, 	3.853, 	3.903, 	3.953, 	4.006, 	4.063, 	4.121, 	4.183, 4.253]
+        
+        elif celltype==99:   #60ah磷酸铁锂电芯
+            self.Capacity = 54
+            self.PackFullChrgVolt=69.99
+            self.CellFullChrgVolt=3.5
+            self.OcvInflexionBelow=3.285
+            self.OcvInflexion2=3.296
+            self.OcvInflexion3=3.328
+            self.OcvInflexionAbove=3.4
+            self.CellVoltNums=20
+            self.CellTempNums=4
+            self.FullChrgSoc=98
+            self.PeakSoc=59
+            self.PeakVoltLowLmt=3.35
+            self.PeakVoltUpLmt=3.4
+            self.PeakCellVolt=[3.362,3.363,3.365,3.366,3.367]
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0.00, 	2.40, 	6.38, 	10.37, 	14.35, 	18.33, 	22.32, 	26.30, 	30.28, 	35.26, 	40.24, 	45.22, 	50.20, 	54.19, 	58.17, 	60.16, 	65.14, 	70.12, 	75.10, 	80.08, 	84.06, 	88.05, 	92.03, 	96.02, 	100.00]
+            self.LookTab_OCV = [2.7151,	3.0298,	3.1935,	3.2009,	3.2167,	3.2393,	3.2561,	3.2703,	3.2843,	3.2871,	3.2874,	3.2868,	3.2896,	3.2917,	3.2967,	3.3128,	3.3283,	3.3286,	3.3287,	3.3288,	3.3289,	3.3296,	3.3302,	3.3314,	3.3429]
+        
+        elif celltype==100:
+            self.Capacity = 228*2
+            self.PackFullChrgVolt=3.65*192
+            self.CellFullChrgVolt=3.5
+            self.OcvInflexionBelow=3.285
+            self.OcvInflexion2=3.296
+            self.OcvInflexion3=3.328
+            self.OcvInflexionAbove=3.4
+            self.CellVoltNums=384
+            self.CellTempNums=64
+            self.FullChrgSoc=98
+            self.PeakSoc=59
+            self.PeakVoltLowLmt=3.35
+            self.PeakVoltUpLmt=3.4
+            self.PeakCellVolt=[3.362,3.363,3.365,3.366,3.367]
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0.00, 	2.40, 	6.38, 	10.37, 	14.35, 	18.33, 	22.32, 	26.30, 	30.28, 	35.26, 	40.24, 	45.22, 	50.20, 	54.19, 	58.17, 	60.16, 	65.14, 	70.12, 	75.10, 	80.08, 	84.06, 	88.05, 	92.03, 	96.02, 	100.00]
+            self.LookTab_OCV = [2.7151,	3.0298,	3.1935,	3.2009,	3.2167,	3.2393,	3.2561,	3.2703,	3.2843,	3.2871,	3.2874,	3.2868,	3.2896,	3.2917,	3.2967,	3.3128,	3.3283,	3.3286,	3.3287,	3.3288,	3.3289,	3.3296,	3.3302,	3.3314,	3.3429]
+
+            
+            
+            self.CellOvLv1=3.75
+            self.CellOvLv2=3.8
+            self.CellUvLv1=2.3
+            self.CellUvLv2=2.2
+            self.CellVoltDiffLv1=0.6
+            self.CellVoltDiffLv2=1
+            self.PackVoltOvLv1=self.CellOvLv1*self.CellVoltNums/2
+            self.PackVoltOvLv2=self.CellOvLv2*self.CellVoltNums/2
+            self.PackVoltUvLv1=self.CellUvLv1*self.CellVoltNums/2
+            self.PackVoltUvLv2=self.CellUvLv2*self.CellVoltNums/2
+
+            self.PackChgOc=-800
+            self.PackDisOc=800        
+        else:
+            print('未找到对应电池编号!!!')
+            # sys.exit()
+

+ 64 - 0
LIB/MIDDLE/CellStateEstimation/Common/DBDowload/DBDownload.py

@@ -0,0 +1,64 @@
+import pymysql
+import time
+import pandas as pd
+
+class DBDownload:
+
+    def __init__(self, host='', port='', db='', user='', password='', mode=''):
+        self.host = host
+        self.port = port
+        self.db = db
+        self.user = user
+        self.password = password
+        self.mode=mode
+        pass
+
+    def __enter__(self):
+        self.connect()
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.close()
+
+    def connect(self):
+        conn_success_flag = 0
+        while not conn_success_flag:
+            try:
+                self.conn = pymysql.connect(host=self.host, port=self.port, user=self.user, password=self.password, database=self.db)
+            except Exception as e:
+                conn_success_flag = 0
+                print("数据库连接失败 :{}".format(e))
+                time.sleep(5)
+            else:
+                conn_success_flag = 1
+                print('数据库连接成功!')
+                self.cursor = self.conn.cursor()
+
+    def getdata(self,*param,tablename,sn,timename,st,sp):
+        print('数据获取中......')
+        param=list(param)
+        str=''
+        for i in range(len(param)):
+            if i<1:
+                str=str+param[i]
+            else:
+                str=str+','+param[i]
+        if self.mode==1:
+            self.cursor.execute("select %s from %s where sn='%s' order by id desc limit 1" %(str,tablename,sn))
+        else:
+            self.cursor.execute("select %s from %s where sn='%s' and %s between '%s' and '%s'" %(str,tablename,sn,timename,st,sp))
+        res = self.cursor.fetchall()
+        df_res = pd.DataFrame(res, columns=param)
+        df_res = df_res.reset_index(drop=True)
+        return(df_res)
+
+    def close(self):
+        try:
+            self.cursor.close()
+            self.conn.close()
+        except Exception as e:
+            print(e)
+        else:
+            print('数据库已断开连接!')
+    
+    

+ 64 - 0
LIB/MIDDLE/CellStateEstimation/Common/DBDowload/V1.0.1/DBDownload.py

@@ -0,0 +1,64 @@
+import pymysql
+import time
+import pandas as pd
+
+class DBDownload:
+
+    def __init__(self, host='', port='', db='', user='', password='', mode=''):
+        self.host = host
+        self.port = port
+        self.db = db
+        self.user = user
+        self.password = password
+        self.mode=mode
+        pass
+
+    def __enter__(self):
+        self.connect()
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.close()
+
+    def connect(self):
+        conn_success_flag = 0
+        while not conn_success_flag:
+            try:
+                self.conn = pymysql.connect(host=self.host, port=self.port, user=self.user, password=self.password, database=self.db)
+            except Exception as e:
+                conn_success_flag = 0
+                print("数据库连接失败 :{}".format(e))
+                time.sleep(5)
+            else:
+                conn_success_flag = 1
+                print('数据库连接成功!')
+                self.cursor = self.conn.cursor()
+
+    def getdata(self,*param,tablename,sn,timename,st,sp):
+        print('数据获取中......')
+        param=list(param)
+        str=''
+        for i in range(len(param)):
+            if i<1:
+                str=str+param[i]
+            else:
+                str=str+','+param[i]
+        if self.mode==1:
+            self.cursor.execute("select %s from %s where sn='%s' order by id desc limit 1" %(str,tablename,sn))
+        else:
+            self.cursor.execute("select %s from %s where sn='%s' and %s between '%s' and '%s'" %(str,tablename,sn,timename,st,sp))
+        res = self.cursor.fetchall()
+        df_res = pd.DataFrame(res, columns=param)
+        df_res = df_res.reset_index(drop=True)
+        return(df_res)
+
+    def close(self):
+        try:
+            self.cursor.close()
+            self.conn.close()
+        except Exception as e:
+            print(e)
+        else:
+            print('数据库已断开连接!')
+    
+    

+ 64 - 0
LIB/MIDDLE/CellStateEstimation/Common/DBDownload.py

@@ -0,0 +1,64 @@
+import pymysql
+import time
+import pandas as pd
+
+class DBDownload:
+
+    def __init__(self, host='', port='', db='', user='', password='', mode=''):
+        self.host = host
+        self.port = port
+        self.db = db
+        self.user = user
+        self.password = password
+        self.mode=mode
+        pass
+
+    def __enter__(self):
+        self.connect()
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.close()
+
+    def connect(self):
+        conn_success_flag = 0
+        while not conn_success_flag:
+            try:
+                self.conn = pymysql.connect(host=self.host, port=self.port, user=self.user, password=self.password, database=self.db)
+            except Exception as e:
+                conn_success_flag = 0
+                print("数据库连接失败 :{}".format(e))
+                time.sleep(5)
+            else:
+                conn_success_flag = 1
+                print('数据库连接成功!')
+                self.cursor = self.conn.cursor()
+
+    def getdata(self,*param,tablename,sn,timename,st,sp):
+        print('数据获取中......')
+        param=list(param)
+        str=''
+        for i in range(len(param)):
+            if i<1:
+                str=str+param[i]
+            else:
+                str=str+','+param[i]
+        if self.mode==1:
+            self.cursor.execute("select %s from %s where sn='%s' order by id desc limit 1" %(str,tablename,sn))
+        else:
+            self.cursor.execute("select %s from %s where sn='%s' and %s between '%s' and '%s'" %(str,tablename,sn,timename,st,sp))
+        res = self.cursor.fetchall()
+        df_res = pd.DataFrame(res, columns=param)
+        df_res = df_res.reset_index(drop=True)
+        return(df_res)
+
+    def close(self):
+        try:
+            self.cursor.close()
+            self.conn.close()
+        except Exception as e:
+            print(e)
+        else:
+            print('数据库已断开连接!')
+    
+    

+ 363 - 0
LIB/MIDDLE/CellStateEstimation/Common/V1_0_1/BatParam.py

@@ -0,0 +1,363 @@
+
+#定义电池参数
+class BatParam:
+    def __init__(self,celltype):
+
+        #公用参数................................................................................................................................................
+        #热失控参数
+        self.TrwTempHigh=60
+        self.TrwTempRate=20
+        self.TrwTempDiff=15
+        self.TrwCellVoltDiff=2.5
+        self.TrwCellVoltFall=1
+        self.TrwCellVoltLow=1.5
+        self.TrwPackVoltFall=1.5
+        
+        #故障诊断参数
+        self.CellTempUpLmt=119
+        self.CellTempLwLmt=-39
+        self.CellTempRate=5
+
+        self.temp_time=60
+        self.volt_time=60
+
+        self.SocJump=10
+        self.SocClamp=0.1
+        self.SocLow=3
+        self.SocDiff=20
+
+        self.SohLow=70
+        self.SohDiff=15
+
+        #mana-kendall趋势检验参数
+        self.mk_p=0.001
+        self.mk_z=-5
+        self.mk_Tau=-0.7
+        self.mk_slope=-0.1
+        self.mk_s=-260
+        self.mk_svar=1600
+
+        self.OcvWeight_Temp=[-30,-20,-10,0,10,20,30,40,50]
+        self.OcvWeight_StandingTime=[0,500,600,1200,1800,3600,7200,10800]
+        self.OcvWeight            =[[0,0,  0,  0,    0,   0.1,0.3, 1],
+                                    [0,0,  0,  0,    0,   0.1,0.3, 1],
+                                    [0,0,  0,  0,    0,   0.2,0.5, 1],
+                                    [0,0,  0,  0,    0.2, 0.4,0.8, 1],
+                                    [0,0,  0,  0.1,  0.3, 0.6,1,   1],
+                                    [0,0,  0.1,0.2,  0.5, 0.8,1,   1],
+                                    [0,0,  0.1,0.3,  0.6, 1,  1,   1],
+                                    [0,0,  0.1,0.3,  0.7, 1,  1,   1],
+                                    [0,0,  0.2,0.3,  0.8, 1,  1,   1]]
+
+        #采样诊断参数
+        self.CellMaxUSBTemp=55
+        self.AllowChgMinTemp=0
+        self.AllowDsChgTemp=-5
+        self.AvgVolGap=1
+        self.AvgCellTempGap=10
+        self.PackOTlmt=65
+        self.PackUTlmt=-20
+        self.OtherOTlmt=91
+        self.OtherUTlmt=-20        
+        self.FaultCount=100
+        
+        if celltype==1: #6040
+            self.Capacity = 41
+            self.PackFullChrgVolt=69.99
+            self.CellFullChrgVolt=4.2
+            self.CellFullChrgCrnt=-15
+            self.CellVoltNums=17
+            self.CellTempNums=3
+            self.OtherTempNums=5
+            self.FullChrgSoc=98
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0,	    3.5348,	8.3581,	13.181,	18.004,	22.827,	27.651,	32.474,	37.297,	42.120,	46.944,	51.767,	56.590,	61.413,	66.237,	71.060,	75.883,	80.707,	85.530,	90.353,	95.176,	100,   105]
+            self.LookTab_OCV = [3.3159,	3.4384,	3.4774,	3.5156,	3.5478,	3.5748,	3.6058,	3.6238,	3.638,	3.6535,	3.6715,	3.6951,	3.7279,	3.7757,	3.8126,	3.8529,	3.8969,	3.9446,	3.9946,	4.0491,	4.109,	4.183, 4.263]
+
+            self.CellOvLv1=4.2
+            self.CellOvLv2=4.25
+            self.CellUvLv1=2.8
+            self.CellUvLv2=2.5
+            self.CellVoltDiffLv1=0.3
+            self.CellVoltDiffLv2=0.5
+            self.PackVoltOvLv1=self.CellOvLv1*self.CellVoltNums
+            self.PackVoltOvLv2=self.CellOvLv2*self.CellVoltNums
+            self.PackVoltUvLv1=self.CellUvLv1*self.CellVoltNums
+            self.PackVoltUvLv2=self.CellUvLv2*self.CellVoltNums
+
+            self.PackChgOc=-40
+            self.PackDisOc=200
+
+            self.LeakCurrentLv1=-10
+            self.LeakCurrentLv2=-15
+            self.LeakCurrentLv3=-50
+            self.CellTempHighLv1=45
+            self.CellTempHighLv2=50
+            self.CellTempLowLv1=-20
+            self.CellTempLowLv2=-25
+            self.CellTempDiffLv1=10
+            self.CellTempDiffLv2=15
+
+            # self.TrwVoltRate=-1
+            
+            self.DifVolGap = 3
+            self.CellOVlmt=5
+            self.CellUVlmt=2
+            self.CantChrgVol=3
+            self.AvgOtherTempGap=99
+        elif celltype==2: #4840
+            self.Capacity = 41
+            self.PackFullChrgVolt=69.99
+            self.CellFullChrgVolt=4.2
+            self.CellFullChrgCrnt=-15
+            self.CellVoltNums=14
+            self.CellTempNums=3
+            self.OtherTempNums=4
+            self.FullChrgSoc=98
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0,	    3.5348,	8.3581,	13.181,	18.004,	22.827,	27.651,	32.474,	37.297,	42.120,	46.944,	51.767,	56.590,	61.413,	66.237,	71.060,	75.883,	80.707,	85.530,	90.353,	95.176,	100,   105]
+            self.LookTab_OCV = [3.3159,	3.4384,	3.4774,	3.5156,	3.5478,	3.5748,	3.6058,	3.6238,	3.638,	3.6535,	3.6715,	3.6951,	3.7279,	3.7757,	3.8126,	3.8529,	3.8969,	3.9446,	3.9946,	4.0491,	4.109,	4.183, 4.263]
+
+            self.CellOvLv1=4.2
+            self.CellOvLv2=4.25
+            self.CellUvLv1=2.8
+            self.CellUvLv2=2.5
+            self.CellVoltDiffLv1=0.3
+            self.CellVoltDiffLv2=0.5
+            self.PackVoltOvLv1=self.CellOvLv1*self.CellVoltNums
+            self.PackVoltOvLv2=self.CellOvLv2*self.CellVoltNums
+            self.PackVoltUvLv1=self.CellUvLv1*self.CellVoltNums
+            self.PackVoltUvLv2=self.CellUvLv2*self.CellVoltNums
+
+            self.PackChgOc=-40
+            self.PackDisOc=200
+
+            self.LeakCurrentLv1=-10
+            self.LeakCurrentLv2=-15
+            self.LeakCurrentLv3=-50
+            self.CellTempHighLv1=45
+            self.CellTempHighLv2=50
+            self.CellTempLowLv1=-20
+            self.CellTempLowLv2=-25
+            self.CellTempDiffLv1=10
+            self.CellTempDiffLv2=15
+
+            # self.TrwVoltRate=-1
+            
+            self.DifVolGap = 3
+            self.CellOVlmt=5
+            self.CellUVlmt=2
+            self.CantChrgVol=3
+            self.AvgOtherTempGap=99   
+        elif celltype==3:   #力信50ah三元电芯
+            self.Capacity = 51
+            self.PackFullChrgVolt=80
+            self.CellFullChrgVolt=4.2
+            self.CellFullChrgCrnt=-15
+            self.CellVoltNums=20
+            self.CellTempNums=3
+            self.OtherTempNums=4
+            self.FullChrgSoc=98
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0,	    5,	    10,	    15,	    20,	    25,	    30,	    35,	    40,	    45,	    50,	    55,	    60,	    65,	    70,	    75,	    80,	    85,	    90,	    95,	    100,   105]
+            self.LookTab_OCV = [3.357, 	3.455, 	3.493, 	3.540, 	3.577, 	3.605, 	3.622, 	3.638, 	3.655, 	3.677, 	3.707, 	3.757, 	3.815, 	3.866, 	3.920, 	3.976, 	4.036, 	4.099, 	4.166, 	4.237, 	4.325, 4.415]
+
+            self.CellOvLv1=4.2
+            self.CellOvLv2=4.25
+            self.CellUvLv1=2.8
+            self.CellUvLv2=2.5
+            self.CellVoltDiffLv1=0.3
+            self.CellVoltDiffLv2=0.5
+            self.PackVoltOvLv1=self.CellOvLv1*self.CellVoltNums
+            self.PackVoltOvLv2=self.CellOvLv2*self.CellVoltNums
+            self.PackVoltUvLv1=self.CellUvLv1*self.CellVoltNums
+            self.PackVoltUvLv2=self.CellUvLv2*self.CellVoltNums
+
+            self.PackChgOc=-40
+            self.PackDisOc=200
+
+            self.LeakCurrentLv1=-10
+            self.LeakCurrentLv2=-15
+            self.LeakCurrentLv3=-50
+            self.CellTempHighLv1=45
+            self.CellTempHighLv2=50
+            self.CellTempLowLv1=-20
+            self.CellTempLowLv2=-25
+            self.CellTempDiffLv1=10
+            self.CellTempDiffLv2=15  
+
+            # self.TrwVoltRate=-1
+            
+            self.DifVolGap = 3
+            self.CellOVlmt=5
+            self.CellUVlmt=2
+            self.CantChrgVol=3
+            self.AvgOtherTempGap=40         
+        elif celltype==4:   #CATL 50ah三元电芯
+            self.Capacity = 50
+            self.PackFullChrgVolt=80
+            self.CellFullChrgVolt=4.2
+            self.CellFullChrgCrnt=-15
+            self.CellVoltNums=20
+            self.CellTempNums=2
+            self.OtherTempNums=0
+            self.FullChrgSoc=98
+            self.PeakSoc=57
+            self.PackCrntDec=-1
+            self.BalCurrent=0.015
+
+            self.LookTab_SOC = [0,	    5,	    10,	    15,	    20,	    25,	    30,	    35,	    40,	    45,	    50,	    55,	    60,	    65,	    70,	    75,	    80,	    85,	    90,	    95,	    100,   105,     110]
+            self.LookTab_OCV = [3.152, 	3.397, 	3.438, 	3.481, 	3.523, 	3.560, 	3.586, 	3.604, 	3.620, 	3.638, 	3.661, 	3.693, 	3.748, 	3.803, 	3.853, 	3.903, 	3.953, 	4.006, 	4.063, 	4.121, 	4.183, 4.253,  4.50]
+
+
+            self.CellOvLv1=4.2
+            self.CellOvLv2=4.25
+            self.CellUvLv1=2.8
+            self.CellUvLv2=2.5
+            self.CellVoltDiffLv1=0.3
+            self.CellVoltDiffLv2=0.5
+            self.PackVoltOvLv1=self.CellOvLv1*self.CellVoltNums
+            self.PackVoltOvLv2=self.CellOvLv2*self.CellVoltNums
+            self.PackVoltUvLv1=self.CellUvLv1*self.CellVoltNums
+            self.PackVoltUvLv2=self.CellUvLv2*self.CellVoltNums
+
+            self.PackChgOc=-40
+            self.PackDisOc=200
+
+            self.LeakCurrentLv1=-10
+            self.LeakCurrentLv2=-15
+            self.LeakCurrentLv3=-50
+            self.CellTempHighLv1=45
+            self.CellTempHighLv2=50
+            self.CellTempLowLv1=-20
+            self.CellTempLowLv2=-25
+            self.CellTempDiffLv1=10
+            self.CellTempDiffLv2=15   
+
+            # self.TrwVoltRate=-1    
+            
+            self.DifVolGap = 3
+            self.CellOVlmt=5
+            self.CellUVlmt=2
+            self.CantChrgVol=3
+            self.AvgOtherTempGap=40    
+        elif celltype==99:   #60ah磷酸铁锂电芯
+            self.Capacity = 54
+            self.PackFullChrgVolt=69.99
+            self.CellFullChrgVolt=3.5
+            self.CellFullChrgCrnt=-20
+            self.OcvInflexionBelow=3.281
+            self.OcvInflexion2=3.296
+            self.OcvInflexion3=3.328
+            self.OcvInflexionAbove=3.4
+            self.SocInflexion1=30
+            self.SocInflexion2=60
+            self.SocInflexion3=70
+            self.CellVoltNums=20
+            self.CellTempNums=3
+            self.OtherTempNums=5
+            self.FullChrgSoc=98
+            self.PeakSoc=59
+            self.PeakVoltLowLmt=3.35
+            self.PeakVoltUpLmt=3.4
+            self.PeakCellVolt=[3.362,3.363,3.365,3.366,3.367]
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0.00, 	2.40, 	6.38, 	10.37, 	14.35, 	18.33, 	22.32, 	26.30, 	30.28, 	35.26, 	40.24, 	45.22, 	50.20, 	54.19, 	58.17, 	60.16, 	65.14, 	70.12, 	75.10, 	80.08, 	84.06, 	88.05, 	92.03, 	96.02, 	100.00, 105]
+            self.LookTab_OCV = [2.7151,	3.0298,	3.1935,	3.2009,	3.2167,	3.2393,	3.2561,	3.2703,	3.2843,	3.2871,	3.2874,	3.2868,	3.2896,	3.2917,	3.2967,	3.3128,	3.3283,	3.3286,	3.3287,	3.3288,	3.3289,	3.3296,	3.3302,	3.3314,	3.3429, 3.6]
+
+            self.CellOvLv1=3.68
+            self.CellOvLv2=3.7
+            self.CellUvLv1=2.1
+            self.CellUvLv2=2
+            self.CellVoltDiffLv1=0.6
+            self.CellVoltDiffLv2=1
+            self.PackVoltOvLv1=self.CellOvLv1*self.CellVoltNums
+            self.PackVoltOvLv2=self.CellOvLv2*self.CellVoltNums
+            self.PackVoltUvLv1=self.CellUvLv1*self.CellVoltNums
+            self.PackVoltUvLv2=self.CellUvLv2*self.CellVoltNums
+
+            self.PackChgOc=-60
+            self.PackDisOc=200
+
+            self.LeakCurrentLv1=-20
+            self.LeakCurrentLv2=-30
+            self.LeakCurrentLv3=-100
+            self.CellTempHighLv1=45
+            self.CellTempHighLv2=50
+            self.CellTempLowLv1=-20
+            self.CellTempLowLv2=-25
+            self.CellTempDiffLv1=10
+            self.CellTempDiffLv2=15   
+
+            # self.TrwVoltRate=-8
+            self.mk_slope=-0.8
+            
+            
+            self.DifVolGap = 3
+            self.CellOVlmt=4
+            self.CellUVlmt=2
+            self.CantChrgVol=2.6
+            self.AvgOtherTempGap=99                    
+        elif celltype==100:
+            self.Capacity = 228*2
+            self.PackFullChrgVolt=3.65*192
+            self.CellFullChrgVolt=3.5
+            self.OcvInflexionBelow=3.285
+            self.OcvInflexion2=3.296
+            self.OcvInflexion3=3.328
+            self.OcvInflexionAbove=3.4
+            self.CellVoltNums=384
+            self.CellTempNums=64
+            self.OtherTempNums=0
+            self.FullChrgSoc=98
+            self.PeakSoc=59
+            self.PeakVoltLowLmt=3.35
+            self.PeakVoltUpLmt=3.4
+            self.PeakCellVolt=[3.362,3.363,3.365,3.366,3.367]
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0.00, 	2.40, 	6.38, 	10.37, 	14.35, 	18.33, 	22.32, 	26.30, 	30.28, 	35.26, 	40.24, 	45.22, 	50.20, 	54.19, 	58.17, 	60.16, 	65.14, 	70.12, 	75.10, 	80.08, 	84.06, 	88.05, 	92.03, 	96.02, 	100.00]
+            self.LookTab_OCV = [2.7151,	3.0298,	3.1935,	3.2009,	3.2167,	3.2393,	3.2561,	3.2703,	3.2843,	3.2871,	3.2874,	3.2868,	3.2896,	3.2917,	3.2967,	3.3128,	3.3283,	3.3286,	3.3287,	3.3288,	3.3289,	3.3296,	3.3302,	3.3314,	3.3429]
+            
+            self.CellOvLv1=3.75
+            self.CellOvLv2=3.8
+            
+            self.CellUvLv1=2.3
+            self.CellUvLv2=2.2
+            self.CellVoltDiffLv1=0.6
+            self.CellVoltDiffLv2=1
+            self.PackVoltOvLv1=self.CellOvLv1*self.CellVoltNums/2
+            self.PackVoltOvLv2=self.CellOvLv2*self.CellVoltNums/2
+            self.PackVoltUvLv1=self.CellUvLv1*self.CellVoltNums/2
+            self.PackVoltUvLv2=self.CellUvLv2*self.CellVoltNums/2
+
+            self.PackChgOc=-800
+            self.PackDisOc=800
+
+            self.LeakCurrentLv1=-20
+            self.LeakCurrentLv2=-50
+            self.LeakCurrentLv3=-100
+            self.CellTempHighLv1=65
+            self.CellTempHighLv2=67
+            self.CellTempLowLv1=-30
+            self.CellTempLowLv2=-35
+            self.CellTempDiffLv1=28
+            self.CellTempDiffLv2=32   
+
+            # self.TrwVoltRate=-8 
+            self.mk_slope=-0.3
+            
+                                   
+            self.DifVolGap = 3
+            self.CellOVlmt=4
+            self.CellUVlmt=2
+            self.CantChrgVol=2.6
+            self.AvgOtherTempGap=40
+ 
+        else:
+            print('未找到对应电池编号!!!')
+            # sys.exit()

+ 56 - 0
LIB/MIDDLE/CellStateEstimation/Common/V1_0_1/DBDownload.py

@@ -0,0 +1,56 @@
+import pymysql
+import time
+import pandas as pd
+
+class DBDownload:
+
+    def __init__(self, host='', port='', db='', user='', password='', mode=''):
+        self.host = host
+        self.port = port
+        self.db = db
+        self.user = user
+        self.password = password
+        self.mode=mode
+        pass
+
+    def __enter__(self):
+        self.connect()
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.close()
+
+    def connect(self):
+        conn_success_flag = 0
+        while not conn_success_flag:
+            try:
+                self.conn = pymysql.connect(host=self.host, port=self.port, user=self.user, password=self.password, database=self.db)
+            except Exception as e:
+                conn_success_flag = 0
+                print("数据库连接失败 :{}".format(e))
+                time.sleep(5)
+            else:
+                conn_success_flag = 1
+                print('数据库连接成功!')
+                self.cursor = self.conn.cursor()
+
+    def getdata(self,param,tablename,sn,timename,st,sp):
+        print('数据获取中......')
+        if self.mode==1:
+            self.cursor.execute("select %s from %s where sn='%s' order by id desc limit 1" %(param,tablename,sn))
+        else:
+            self.cursor.execute("select %s from %s where sn='%s' and %s between '%s' and '%s'" %(param,tablename,sn,timename,st,sp))
+        res = self.cursor.fetchall()
+        df_res = pd.DataFrame(res, columns=param.split(','))
+        return(df_res)
+
+    def close(self):
+        try:
+            self.cursor.close()
+            self.conn.close()
+        except Exception as e:
+            print(e)
+        else:
+            print('数据库已断开连接!')
+    
+    

+ 25 - 0
LIB/MIDDLE/CellStateEstimation/Common/V1_0_1/log.py

@@ -0,0 +1,25 @@
+import logging
+import traceback
+
+class Mylog:
+
+    def __init__(self,log_name,log_level):
+        self.name=log_name
+        self.level=log_level
+    
+    def logcfg(self):
+        if len(self.level) > 0:
+            if self.level == 'debug':
+                Level=logging.DEBUG
+            elif self.level == 'info':
+                Level=logging.INFO
+            elif self.level == 'warning':
+                Level=logging.WARNING
+            else:
+                Level=logging.ERROR
+        logging.basicConfig(filename=r'D:\Develop\User\Songpengfei\data_analyze_platform\WORK\101log\\'+self.name, level=Level,format='%(asctime)s - %(levelname)s - %(message)s')
+
+
+    def logopt(self,*info):
+        logging.error(info)
+        logging.error(traceback.format_exc())

+ 24 - 0
LIB/MIDDLE/CellStateEstimation/Common/log.py

@@ -0,0 +1,24 @@
+import logging
+import traceback
+
+class Mylog:
+
+    def __init__(self,log_name,log_level):
+        self.name=log_name
+        self.level=log_level
+    
+    def logcfg(self):
+        if len(self.level) > 0:
+            if self.level == 'debug':
+                Level=logging.DEBUG
+            elif self.level == 'info':
+                Level=logging.INFO
+            elif self.level == 'warning':
+                Level=logging.WARNING
+            else:
+                Level=logging.ERROR
+        logging.basicConfig(filename=self.name, level=Level,format='%(asctime)s - %(levelname)s - %(message)s')
+
+    def logopt(self,*info):
+        logging.error(info)
+        logging.error(traceback.format_exc())

+ 562 - 0
LIB/MIDDLE/CellStateEstimation/InterShort/V1_0_0/CBMSBatInterShort.py

@@ -0,0 +1,562 @@
+import pandas as pd
+import numpy as np
+import matplotlib.pyplot as plt
+from pymysql import paramstyle
+from LIB.MIDDLE.CellStateEstimation.Common import BatParam
+
+class BatInterShort:
+    def __init__(self,sn,celltype,df_bms,df_soh):  #参数初始化
+
+        self.sn=sn
+        self.celltype=celltype
+        self.param=BatParam.BatParam(celltype)
+        self.df_bms=df_bms
+        self.packcrnt=df_bms['总电流[A]']*self.param.PackCrntDec
+        self.packvolt=df_bms['总电压[V]']
+        self.bms_soc=df_bms['SOC[%]']
+        self.bmstime= pd.to_datetime(df_bms['时间戳'], format='%Y-%m-%d %H:%M:%S')
+
+        self.df_soh=df_soh
+    
+    def intershort(self):
+        if self.celltype==1 or self.celltype==2 or self.celltype==3 or self.celltype==4:
+            df_res=self._ncm_intershort()
+            return df_res
+            
+        elif self.celltype==99:
+            df_res=self._lfp_intershort()
+            return df_res
+        
+        else:
+            return pd.DataFrame()
+
+    #定义滑动滤波函数....................................................................................
+    def _np_move_avg(self,a, n, mode="same"): 
+        return (np.convolve(a, np.ones((n,)) / n, mode=mode))
+    
+    #寻找当前行数据的最小温度值.............................................................................
+    def _celltemp_weight(self,num):   
+        celltemp = []
+        for j in range(1, self.param.CellTempNums+1):
+            s = str(j)
+            celltemp.append(self.df_bms.loc[num,'单体温度' + s])
+        celltemp=np.mean(celltemp)
+        self.celltemp=celltemp
+        if self.celltype==99:
+            if celltemp>=20:
+                self.tempweight=1
+                self.StandardStandingTime=3600
+            elif celltemp>=10:
+                self.tempweight=0.6
+                self.StandardStandingTime=7200
+            elif celltemp>=5:
+                self.tempweight=0.
+                self.StandardStandingTime=7200
+            else:
+                self.tempweight=0.1
+                self.StandardStandingTime=10800
+        else:
+            if celltemp>=20:
+                self.tempweight=1
+                self.StandardStandingTime=3600
+            elif celltemp>=10:
+                self.tempweight=0.8
+                self.StandardStandingTime=3600
+            elif celltemp>=5:
+                self.tempweight=0.6
+                self.StandardStandingTime=7200
+            else:
+                self.tempweight=0.2
+                self.StandardStandingTime=10800
+
+    #获取当前行所有电压数据........................................................................................
+    def _cellvolt_get(self,num): 
+        cellvolt=[]
+        for j in range(1, self.param.CellVoltNums+1): 
+            s = str(j)
+            cellvolt.append(self.df_bms.loc[num,'单体电压' + s]/1000)
+        return cellvolt
+
+    #获取当前行所有soc差...........................................................................................
+    def _celldeltsoc_get(self,num,dict_baltime,capacity): 
+        cellsoc=[]
+        celldeltsoc=[]
+        for j in range(1, self.param.CellVoltNums+1):   #获取每个电芯电压对应的SOC值
+            cellvolt=self.df_bms.loc[num,'单体电压' + str(j)]/1000
+            ocv_soc=np.interp(cellvolt,self.param.LookTab_OCV,self.param.LookTab_SOC)
+            if j in dict_baltime.keys():
+                ocv_soc=ocv_soc+dict_baltime[j]*self.param.BalCurrent/(capacity*3600)   #补偿均衡电流
+            else:
+                pass
+            cellsoc.append(ocv_soc)
+        
+        if self.celltype==1 or self.celltype==2:
+            consum_num=7
+            cellsoc1=cellsoc[:self.param.CellVoltNums-consum_num]    #切片,将bms耗电的电芯和非耗电的电芯分离开
+            cellsocmean1=(sum(cellsoc1)-max(cellsoc1)-min(cellsoc1))/(len(cellsoc1)-2)
+            cellsoc2=cellsoc[self.param.CellVoltNums-consum_num:]
+            cellsocmean2=(sum(cellsoc2)-max(cellsoc2)-min(cellsoc2))/(len(cellsoc2)-2)
+            
+            for j in range(len(cellsoc)):   #计算每个电芯的soc差
+                if j<self.param.CellVoltNums-consum_num:
+                    celldeltsoc.append(cellsoc[j]-cellsocmean1)
+                else:
+                    celldeltsoc.append(cellsoc[j]-cellsocmean2)
+            return celldeltsoc
+
+        else:
+            cellsocmean=(sum(cellsoc)-max(cellsoc)-min(cellsoc))/(len(cellsoc)-2)
+            for j in range(len(cellsoc)):   #计算每个电芯的soc差
+                celldeltsoc.append(cellsoc[j]-cellsocmean)
+            return celldeltsoc
+ 
+    #获取所有电芯的As差
+    def _cellDeltAs_get(self,chrg_st,chrg_end,dict_baltime):
+        cellAs=[]
+        celldeltAs=[]
+        for j in range(1, self.param.CellVoltNums+1):   #获取每个电芯电压>峰值电压的充入As数
+            if j in dict_baltime.keys():    #补偿均衡电流
+                As=-self.param.BalCurrent*dict_baltime[j]
+            else:    
+                As=0
+            As_tatol=0
+            symbol=0
+            for m in range(chrg_st+1,chrg_end):
+                As=As-self.packcrnt[m]*(self.bmstime[m]-self.bmstime[m-1]).total_seconds()
+                if symbol<5:
+                    if self.df_bms.loc[m,'单体电压'+str(j)]/1000>self.param.PeakCellVolt[symbol]:
+                        As_tatol=As_tatol+As
+                        symbol=symbol+1
+                    else:
+                        continue
+                else:
+                    cellAs.append(As_tatol/5)
+                    break
+        
+        if self.celltype==99:
+            consum_num=10
+            cellAs1=cellAs[:self.param.CellVoltNums-consum_num]    #切片,将bms耗电的电芯和非耗电的电芯分离开
+            cellAsmean1=(sum(cellAs1)-max(cellAs1)-min(cellAs1))/(len(cellAs1)-2)
+            cellAs2=cellAs[self.param.CellVoltNums-consum_num:]
+            cellAsmean2=(sum(cellAs2)-max(cellAs2)-min(cellAs2))/(len(cellAs2)-2)
+            
+            for j in range(len(cellAs)):   #计算每个电芯的soc差
+                if j<self.param.CellVoltNums-consum_num:
+                    celldeltAs.append(cellAs[j]-cellAsmean1)
+                else:
+                    celldeltAs.append(cellAs[j]-cellAsmean2)
+        else:
+            cellAsmean=(sum(cellAs)-max(cellAs)-min(cellAs))/(len(cellAs)-2)
+            for j in range(len(cellAs)):   #计算每个电芯的soc差
+                celldeltAs.append(cellAs[j]-cellAsmean)
+            
+        return celldeltAs
+
+    #计算每个电芯的均衡时长..........................................................................................................................
+    def _bal_time(self,dict_bal):
+        dict_baltime={}
+        dict_baltime1={}
+        for key in dict_bal:
+            count=1
+            x=eval(key)
+            while x>0:
+                if x & 1==1:    #判断最后一位是否为1
+                    if count in dict_baltime.keys():
+                        dict_baltime[count] = dict_baltime[count] + dict_bal[key]
+                    else:
+                        dict_baltime[count] = dict_bal[key]
+                else:
+                    pass
+                count += 1
+                x >>= 1    #右移一位
+        
+        dict_baltime=dict(sorted(dict_baltime.items(),key=lambda dict_baltime:dict_baltime[0]))
+        for key in dict_baltime:    #解析均衡的电芯编号
+            if self.celltype==1:    #科易6040
+                if key<14:
+                    dict_baltime1[key]=dict_baltime[key]
+                elif key<18:
+                    dict_baltime1[key-1]=dict_baltime[key]
+                else:
+                    dict_baltime1[key-3]=dict_baltime[key]
+            elif self.celltype==1:    #科易4840
+                if key<4:
+                    dict_baltime1[key-1]=dict_baltime[key]
+                elif key<8:
+                    dict_baltime1[key-1]=dict_baltime[key]
+                elif key<14:
+                    dict_baltime1[key-3]=dict_baltime[key]
+                elif key<18:
+                    dict_baltime1[key-4]=dict_baltime[key]
+                else:
+                    dict_baltime1[key-6]=dict_baltime[key]
+            else:
+                dict_baltime1=dict_baltime
+        return dict_baltime1
+
+    #三元电池的内短路电流计算...........................................................................................................................................................
+    def _ncm_intershort(self):
+        column_name=['time_st', 'time_sp', 'sn', 'method','short_current','baltime']
+        df_res=pd.DataFrame(columns=column_name)
+        
+        if not self.df_bms.empty:
+            if self.df_soh.empty:
+                batsoh=self.df_bms.loc[0,'SOH[%]']
+                capacity=self.param.Capacity*batsoh/100
+            else:
+                batsoh=self.df_soh.loc[len(self.df_soh)-1,'soh']
+                capacity=self.param.Capacity*batsoh/100
+            standingtime=0
+            standingtime1=0
+            firsttime=1
+            firsttime1=1
+            dict_bal={}
+            dict_bal1={}
+
+            for i in range(2,len(self.df_bms)-2):
+
+                if firsttime1==0:   #满电静置算法--计算均衡状态对应的均衡时间
+                    try:
+                        balstat=int(self.df_bms.loc[i,'单体均衡状态'])
+                        if balstat>0.5:
+                            bal_step=(self.bmstime[i+1]-self.bmstime[i]).total_seconds()  #均衡步长
+                            bal_step=int(bal_step)
+                            if str(balstat) in dict_bal1.keys():
+                                dict_bal1[str(balstat)]=dict_bal1[str(balstat)]+bal_step
+                            else:
+                                dict_bal1[str(balstat)]=bal_step
+                        else:
+                            pass
+                    except:
+                        dict_bal1={}
+                else:
+                    pass
+
+                if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1:     
+                    delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                    standingtime=standingtime+delttime
+                    standingtime1=standingtime1+delttime
+                    self._celltemp_weight(i)
+
+                    #静置法计算内短路-开始.....................................................................................................................................
+                    if firsttime==1:    
+                        if standingtime>self.StandardStandingTime:      #静置时间满足要求
+                            dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                            deltsoc_last=self._celldeltsoc_get(i,dict_baltime,capacity)
+                            time_last=self.bmstime[i]
+                            firsttime=0
+                            standingtime=0
+                        else:
+                            pass                
+                    elif standingtime>3600*12:
+                        standingtime=0
+                        dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                        deltsoc_now=self._celldeltsoc_get(i,dict_baltime,capacity)
+                        time_now=self.bmstime[i]
+                        
+                        list_sub=[a-b for a, b in zip(deltsoc_now, deltsoc_last)]
+                        list_pud=[0.01*capacity*3600*1000/(time_now-time_last).total_seconds()]*self.param.CellVoltNums
+                        leak_current=[a*b for a,b in zip(list_sub,list_pud)]
+                        leak_current=np.array(leak_current)
+                        leak_current=np.round(leak_current,3)
+                        leak_current=list(leak_current)
+                        
+                        df_res.loc[len(df_res)]=[time_last,time_now,self.sn,1,str(leak_current),str(dict_baltime)]  #计算结果存入Dataframe
+                        time_last=time_now  #更新时间
+                        deltsoc_last=deltsoc_now    #更新soc差
+                        dict_bal={}
+                    else: 
+                        try:  
+                            balstat=int(self.df_bms.loc[i,'单体均衡状态'])
+                            if balstat>0.5:
+                                bal_step=(self.bmstime[i+1]-self.bmstime[i]).total_seconds()  #均衡步长
+                                bal_step=int(bal_step)
+                                if str(balstat) in dict_bal.keys():
+                                    dict_bal[str(balstat)]=dict_bal[str(balstat)]+bal_step
+                                else:
+                                    dict_bal[str(balstat)]=bal_step
+                            else:
+                                pass
+                        except:
+                            dict_bal={}
+
+                    #满电静置法计算内短路-开始.....................................................................................................................................................
+                    if standingtime1>self.StandardStandingTime:  
+                        if abs(self.packcrnt[i+2]) >= 0.1:
+                            standingtime1=0
+                            cellvolt_now1=self._cellvolt_get(i)
+                            cellsoc_now1=np.interp(max(cellvolt_now1),self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            if cellsoc_now1>=self.param.FullChrgSoc-3:
+                                if firsttime1==1:
+                                    dict_baltime1=self._bal_time(dict_bal1)   #获取每个电芯的均衡时间
+                                    deltsoc_last1=self._celldeltsoc_get(i,dict_baltime1,capacity)
+                                    time_last1=self.bmstime[i]
+                                    firsttime1=0
+                                else:
+                                    dict_baltime1=self._bal_time(dict_bal1)   #获取每个电芯的均衡时间
+                                    time_now1=self.bmstime[i]
+                                    if (time_now1-time_last1).total_seconds()>3600*24:
+                                        deltsoc_now1=self._celldeltsoc_get(i,dict_baltime1,capacity)
+
+                                        list_sub1=[a-b for a, b in zip(deltsoc_now1, deltsoc_last1)]
+                                        list_pud1=[0.01*capacity*3600*1000/(time_now1-time_last1).total_seconds()]*self.param.CellVoltNums
+                                        leak_current1=[a*b for a,b in zip(list_sub1,list_pud1)]
+                                        leak_current1=np.array(leak_current1)
+                                        leak_current1=np.round(leak_current1,3)
+                                        leak_current1=list(leak_current1)
+                                        
+                                        df_res.loc[len(df_res)]=[time_last1,time_now1,self.sn,2,str(leak_current1),str(dict_baltime1)]  #计算结果存入Dataframe
+                                        time_last1=time_now1  #更新时间
+                                        deltsoc_last1=deltsoc_now1    #更新soc差
+                                        dict_bal1={}
+                                    else:
+                                        pass
+                            else:
+                                pass
+                        else:
+                            pass
+                    else:   
+                        pass
+
+                else:
+                    dict_bal={} 
+                    firsttime=1
+                    standingtime=0
+                    standingtime1=0
+                    pass
+
+        if df_res.empty:    #返回计算结果
+            return pd.DataFrame()
+        else:
+            return df_res
+
+    #磷酸铁锂电池内短路计算程序.............................................................................................................................
+    def _lfp_intershort(self):
+        column_name=['time_st', 'time_sp', 'sn', 'method','short_current','baltime']
+        df_res=pd.DataFrame(columns=column_name)
+        if not self.df_bms.empty:
+            if self.df_soh.empty:
+                batsoh=self.df_bms.loc[0,'SOH[%]']
+                capacity=self.param.Capacity*batsoh/100
+            else:
+                batsoh=self.df_soh.loc[len(self.df_soh)-1,'soh']
+                capacity=self.param.Capacity*batsoh/100
+            standingtime=0
+            standingtime1=0
+            firsttime=1
+            firsttime1=1
+            dict_bal={}
+            dict_bal1={}
+
+            chrg_start=[]
+            chrg_end=[]
+            dict_bal_list=[]
+            charging=0
+            dict_bal3={}
+
+            for i in range(3,len(self.df_bms)-3):
+
+                #静置法计算内短路..........................................................................................................................
+                if firsttime1==0:   #满电静置算法--计算均衡状态对应的均衡时间
+                    try:
+                        balstat=int(self.df_bms.loc[i,'单体均衡状态'])
+                        if balstat>0.5:
+                            bal_step=(self.bmstime[i+1]-self.bmstime[i]).total_seconds()  #均衡步长
+                            bal_step=int(bal_step)
+                            if str(balstat) in dict_bal1.keys():
+                                dict_bal1[str(balstat)]=dict_bal1[str(balstat)]+bal_step
+                            else:
+                                dict_bal1[str(balstat)]=bal_step
+                        else:
+                            pass
+                    except:
+                        dict_bal1={}
+                else:
+                    pass
+
+                if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1:     
+                    delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                    standingtime=standingtime+delttime
+                    standingtime1=standingtime1+delttime
+                    self._celltemp_weight(i)
+
+                    #静置法计算内短路-开始.....................................................................................................................................
+                    if firsttime==1:    
+                        if standingtime>self.StandardStandingTime:      #静置时间满足要求
+                            cellvolt=self._cellvolt_get(i)
+                            if max(cellvolt)<self.param.OcvInflexionBelow-0.002:
+                                dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                                deltsoc_last=self._celldeltsoc_get(i,dict_baltime,capacity)
+                                time_last=self.bmstime[i]
+                                firsttime=0
+                                standingtime=0
+                            else:
+                                pass
+                        else:
+                            pass                
+                    elif standingtime>3600*12:
+                        cellvolt=self._cellvolt_get(i)
+                        if max(cellvolt)<self.param.OcvInflexionBelow-0.002:
+                            standingtime=0
+                            dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                            deltsoc_now=self._celldeltsoc_get(i, dict_baltime,capacity)    #获取每个电芯的SOC差
+                            time_now=self.bmstime[i]
+
+                            list_sub=[a-b for a, b in zip(deltsoc_now, deltsoc_last)]
+                            list_pud=[0.01*capacity*3600*1000/(time_now-time_last).total_seconds()]*self.param.CellVoltNums
+                            leak_current=[a*b for a,b in zip(list_sub,list_pud)]
+                            leak_current=np.array(leak_current)
+                            leak_current=np.round(leak_current,3)
+                            leak_current=list(leak_current)
+                            
+                            df_res.loc[len(df_res)]=[time_last,time_now,self.sn,1,str(leak_current),str(dict_baltime)]  #计算结果存入Dataframe
+                            time_last=time_now  #更新时间
+                            deltsoc_last=deltsoc_now    #更新soc差
+                            dict_bal={}
+                        else:
+                            pass
+                    else: 
+                        try:  
+                            balstat=int(self.df_bms.loc[i,'单体均衡状态'])
+                            if balstat>0.5:
+                                bal_step=(self.bmstime[i+1]-self.bmstime[i]).total_seconds()  #均衡步长
+                                bal_step=int(bal_step)
+                                if str(balstat) in dict_bal.keys():
+                                    dict_bal[str(balstat)]=dict_bal[str(balstat)]+bal_step
+                                else:
+                                    dict_bal[str(balstat)]=bal_step
+                            else:
+                                pass
+                        except:
+                            dict_bal={}
+
+                    #非平台区间静置法计算内短路-开始.....................................................................................................................................................
+                    if standingtime1>self.StandardStandingTime: 
+                        if abs(self.packcrnt[i+2]) >= 0.1: 
+                            standingtime1=0
+                            cellvolt_now1=self._cellvolt_get(i)
+                            if max(cellvolt_now1)<self.param.OcvInflexionBelow-0.002:
+                                if firsttime1==1:
+                                    dict_baltime1=self._bal_time(dict_bal1)   #获取每个电芯的均衡时间
+                                    deltsoc_last1=self._celldeltsoc_get(i,dict_baltime1,capacity)
+                                    time_last1=self.bmstime[i]
+                                    firsttime1=0
+                                else:
+                                    dict_baltime1=self._bal_time(dict_bal1)   #获取每个电芯的均衡时间
+                                    deltsoc_now1=self._celldeltsoc_get(i,dict_baltime1,capacity)
+                                    time_now1=self.bmstime[i]
+
+                                    time_now1=self.bmstime[i]
+                                    if abs(max(deltsoc_now1)-max(deltsoc_last1))<10 and (time_now1-time_last1).total_seconds()>3600*24:
+                                        list_sub1=[a-b for a, b in zip(deltsoc_now1, deltsoc_last1)]
+                                        list_pud1=[0.01*capacity*3600*1000/(time_now1-time_last1).total_seconds()]*self.param.CellVoltNums
+                                        leak_current1=[a*b for a,b in zip(list_sub1,list_pud1)]
+                                        leak_current1=np.array(leak_current1)
+                                        leak_current1=np.round(leak_current1,3)
+                                        leak_current1=list(leak_current1)
+                                    
+                                        
+                                        df_res.loc[len(df_res)]=[time_last1,time_now1,self.sn,2,str(leak_current1),str(dict_baltime1)]  #计算结果存入Dataframe
+                                        time_last1=time_now1  #更新时间
+                                        deltsoc_last1=deltsoc_now1    #更新soc差
+                                        dict_bal1={}
+                                    else:
+                                        pass
+                                    
+                            else:
+                                pass
+                        else:
+                            pass
+                    else:   
+                        pass
+
+                else:
+                    dict_bal={} 
+                    firsttime=1
+                    standingtime=0
+                    standingtime1=0
+                    pass
+
+                #获取充电数据——开始..............................................................................................................
+                try:
+                    balstat=int(self.df_bms.loc[i,'单体均衡状态'])  #统计均衡状态
+                    if balstat>0.5:
+                        bal_step=(self.bmstime[i+1]-self.bmstime[i]).total_seconds()  #均衡步长
+                        bal_step=int(bal_step)
+                        if str(balstat) in dict_bal3.keys():
+                            dict_bal3[str(balstat)]=dict_bal3[str(balstat)]+bal_step
+                        else:
+                            dict_bal3[str(balstat)]=bal_step
+                    else:
+                        pass
+                except:
+                    dict_bal3={}
+
+                if charging==0:
+                    
+                    if self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and self.packcrnt[i+2]<=-1 and self.bms_soc[i]<40:     #判断充电开始
+                        cellvolt_now=self._cellvolt_get(i)
+                        if min(cellvolt_now)<self.param.CellFullChrgVolt-0.15:
+                            charging=1
+                            if len(chrg_start)>len(chrg_end):
+                                chrg_start[-1]=i
+                            else:
+                                chrg_start.append(i)
+                        else:
+                            pass
+                    else:
+                        pass
+
+                else: #充电中
+                    if (self.bmstime[i+1]-self.bmstime[i]).total_seconds()>180:  #如果充电过程中时间间隔>180s,则舍弃该次充电
+                        chrg_start.remove(chrg_start[-1])
+                        charging=0
+                        continue
+                    elif self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and  self.packcrnt[i+2]<-1:  
+                        cellvolt_now=self._cellvolt_get(i)
+                        if min(cellvolt_now)>self.param.CellFullChrgVolt-0.13:   #电压>满充电压-0.13V,即3.37V
+                            self._celltemp_weight(i)
+                            if i-chrg_start[-1]>10 and self.celltemp>10:
+                                chrg_end.append(i+1)
+                                dict_bal_list.append(dict_bal3)
+                                dict_bal3={}
+                                charging=0                       
+                                continue
+                            else:
+                                chrg_start.remove(chrg_start[-1])
+                                charging=0
+                                continue
+                        else:
+                            pass
+                    else:
+                        pass   
+            
+            #基于充电数据计算单体电芯的漏电流..........................................................................................................
+            if len(chrg_end)>1:    
+                for i in range(len(chrg_end)):
+                    if i<1:
+                        dict_baltime={}
+                        deltAs_last=self._cellDeltAs_get(chrg_start[i],chrg_end[i],dict_baltime)
+                        time_last=self.bmstime[chrg_end[i]]
+                    else:
+                        dict_baltime=self._bal_time(dict_bal_list[i])   #获取每个电芯的均衡时间
+                        deltAs_now=self._cellDeltAs_get(chrg_start[i],chrg_end[i],dict_baltime)  #获取每个电芯的As差
+                        time_now=self.bmstime[chrg_end[i]]
+
+                        list_sub=[a-b for a, b in zip(deltAs_now, deltAs_last)]
+                        list_pud=[-1000/(time_now-time_last).total_seconds()]*self.param.CellVoltNums
+                        leak_current=[a*b for a,b in zip(list_sub,list_pud)]
+                        leak_current=np.array(leak_current)
+                        leak_current=np.round(leak_current,3)
+                        leak_current=list(leak_current)
+
+                        df_res.loc[len(df_res)]=[time_last,time_now,self.sn,3,str(leak_current),str(dict_baltime)]  #计算结果存入Dataframe
+                        deltAs_last=deltAs_now
+                        time_last=time_now
+            else:
+                pass
+
+        if df_res.empty:
+            return pd.DataFrame()
+        else:
+            return df_res

BIN
LIB/MIDDLE/CellStateEstimation/InterShort/V1_0_0/InterShort表单.xlsx


+ 89 - 0
LIB/MIDDLE/CellStateEstimation/InterShort/main.py

@@ -0,0 +1,89 @@
+import CBMSBatInterShort
+import log
+
+#coding=utf-8
+import sys
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+from sqlalchemy import create_engine
+import time, datetime
+from LIB.MIDDLE.CellStateEstimation.Common import DBDownload
+
+dbManager = DBManager.DBManager()
+if __name__ == "__main__":
+    excelpath=r'D:\Platform\platform_python\data_analyze_platform\USER\01qixiang\sn-20210903.xlsx'
+    SNdata_6060 = pd.read_excel(excelpath, sheet_name='科易6060')
+    SNdata_6040 = pd.read_excel(excelpath, sheet_name='科易6040')
+    SNdata_4840 = pd.read_excel(excelpath, sheet_name='科易4840')
+    SNdata_L7255 = pd.read_excel(excelpath, sheet_name='格林美-力信7255')
+    SNdata_C7255 = pd.read_excel(excelpath, sheet_name='格林美-CATL7255')
+    SNdata_U7255 = pd.read_excel(excelpath, sheet_name='优旦7255')
+    SNnums_6060=SNdata_6060['SN号'].tolist()
+    SNnums_6040=SNdata_6040['SN号'].tolist()
+    SNnums_4840=SNdata_4840['SN号'].tolist()
+    SNnums_L7255=SNdata_L7255['SN号'].tolist()
+    SNnums_C7255=SNdata_C7255['SN号'].tolist()
+    SNnums_U7255=SNdata_U7255['SN号'].tolist()
+    SNnums= SNnums_6060 + SNnums_L7255 + SNnums_C7255 + SNnums_6040 + SNnums_4840 + SNnums_U7255
+    
+    now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+    now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+    start_time=now_time-datetime.timedelta(days=30)
+    end_time=str(now_time)
+    start_time=str(start_time)
+
+    #log信息配置
+    mylog=log.Mylog('log.txt','error')
+    mylog.logcfg()
+
+    for sn in SNnums:
+        try:
+            if 'PK500' in sn:
+                celltype=1 #6040三元电芯
+            elif 'PK502' in sn:
+                celltype=2 #4840三元电芯
+            elif 'K504B' in sn:
+                celltype=99    #60ah林磷酸铁锂电芯
+            elif 'MGMLXN750' in sn:
+                celltype=3 #力信50ah三元电芯
+            elif 'MGMCLN750' or 'UD' in sn: 
+                celltype=4 #CATL 50ah三元电芯
+            else:
+                print('SN:{},未找到对应电池类型!!!'.format(sn))
+                continue
+                # sys.exit()
+            
+            sn='PK50201A000002039'
+            celltype=2
+            start_time='2021-05-02 09:12:26'
+            end_time='2021-06-03 19:12:26'
+            # df_bms= pd.read_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\01qixiang\98Download\\'+'BMS_'+sn+'.csv',encoding='GB18030')
+
+            #......................................................................读取原始数据库数据..................................................................................
+            dbManager = DBManager.DBManager()
+            df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms'])
+            df_bms = df_data['bms']
+            # df_bms.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\01qixiang\99Result\\''BMS_'+sn+'.csv',encoding='GB18030')
+
+            #.....................................................................读取结果数据库数据.............................................................................
+            host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+            port=3306
+            db='qx_cas'
+            user='qx_read'
+            password='Qx@123456'
+            tablename='cellstateestimation_soh'
+            DBRead=DBDownload.DBDownload(host, port, db, user, password)
+            with DBRead as DBRead:
+                df_soh=DBRead.getdata('time_st','time_sp','sn','method','soh','cellsoh', tablename=tablename, sn=sn)
+            
+            #...........................................................................计算漏电流............................................................................
+            BatInterShort=CBMSBatInterShort.BatInterShort(sn,celltype,df_bms,df_soh)
+            df_res=BatInterShort.intershort()
+            df_res.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\01qixiang\99Result\\'+'CBMS_Short_'+sn+'_1.csv',encoding='GB18030')
+            # print('done!!!')
+        
+        except Exception as e:
+            print(repr(e))
+            mylog.logopt(sn,e)
+            pass

+ 723 - 0
LIB/MIDDLE/CellStateEstimation/SOH/V1_0_0/CBMSBatSoh.py

@@ -0,0 +1,723 @@
+import pandas as pd
+import numpy as np
+import datetime
+import bisect
+import matplotlib.pyplot as plt
+from LIB.MIDDLE.CellStateEstimation.Common import BatParam
+
+class BatSoh:
+    def __init__(self,sn,celltype,df_bms,df_accum,df_soh):  #参数初始化
+
+        self.sn=sn
+        self.celltype=celltype
+        self.param=BatParam.BatParam(celltype)
+        self.df_bms=df_bms
+        self.packcrnt=df_bms['总电流[A]']*self.param.PackCrntDec
+        self.packvolt=df_bms['总电压[V]']
+        self.bms_soc=df_bms['SOC[%]']
+        self.bms_soh=df_bms['SOH[%]']
+        self.bmsstat=df_bms['充电状态']
+        self.bmstime= pd.to_datetime(df_bms['时间戳'], format='%Y-%m-%d %H:%M:%S')
+
+        self.df_accum=df_accum
+        self.accumtime=pd.to_datetime(df_accum['时间戳'], format='%Y-%m-%d %H:%M:%S')
+
+        self.df_soh=df_soh
+
+    def batsoh(self):
+        if self.celltype==1 or self.celltype==2 or self.celltype==3 or self.celltype==4:
+            df_res=self._ncmsoh_twopoint()
+            return df_res
+            
+        elif self.celltype==99:
+            df_res=self._lfpsoh()
+            return df_res
+
+        else:
+            return pd.DataFrame()
+
+    #定义滑动滤波函数.........................................................................................................................
+    def _np_move_avg(self,a, n, mode="same"):
+        return (np.convolve(a, np.ones((n,)) / n, mode=mode))
+
+    #筛选充电数据..............................................................................................................................
+    def _chrgdata(self):    
+        self.ChgStart=[]
+        self.ChgEnd=[]
+        if len(self.packvolt)>100:
+            for i in range(3, len(self.bmstime) - 3):
+                if i==3 and self.bmsstat[i]==2 and self.bmsstat[i+1]==2 and self.bmsstat[i+2]==2:
+                    self.ChgStart.append(i)
+                elif self.bmsstat[i-2]!=2 and self.bmsstat[i-1]!=2 and self.bmsstat[i]==2:
+                    self.ChgStart.append(i)
+                elif self.bmsstat[i-1]==2 and self.bmsstat[i]!=2 and self.bmsstat[i+1]!=2:
+                    self.ChgEnd.append(i-1)
+                elif i == (len(self.bmstime) - 4) and self.bmsstat[len(self.bmsstat)-1] == 2 and self.bmsstat[len(self.bmsstat)-2] == 2:
+                    self.ChgEnd.append(len(self.bmstime)-2)
+    
+    #寻找当前行数据的最小温度值.................................................................................................................
+    def _celltemp_weight(self,num):   
+        celltemp = []
+        for j in range(1, self.param.CellTempNums+1):
+            s = str(j)
+            celltemp.append(self.df_bms.loc[num,'单体温度' + s])
+        celltemp.remove(min(celltemp))
+        self.celltemp=celltemp
+        if self.celltype==99:
+            if min(celltemp)>=20:
+                self.tempweight=1
+                self.StandardStandingTime=1800
+            elif min(celltemp)>=10:
+                self.tempweight=0.5
+                self.StandardStandingTime=3600
+            elif min(celltemp)>=5:
+                self.tempweight=0.2
+                self.StandardStandingTime=7200
+            else:
+                self.tempweight=0
+                self.StandardStandingTime=10800
+        else:
+            if min(celltemp)>=20:
+                self.tempweight=1
+                self.StandardStandingTime=1200
+            elif min(celltemp)>=10:
+                self.tempweight=0.8
+                self.StandardStandingTime=1800
+            elif min(celltemp)>=5:
+                self.tempweight=0.3
+                self.StandardStandingTime=3600
+            else:
+                self.tempweight=0.1
+                self.StandardStandingTime=7200
+
+    #获取SOC差对应的SOH权重值...................................................................................................................
+    def _deltsoc_weight(self,deltsoc):   
+        if deltsoc>60:   
+            deltsoc_weight=1
+        elif deltsoc>50:
+            deltsoc_weight=0.9
+        elif deltsoc>40:
+            deltsoc_weight=0.7
+        elif deltsoc>30:
+            deltsoc_weight=0.5
+        elif deltsoc>20:
+            deltsoc_weight=0.3
+        else:
+            deltsoc_weight=0
+        return deltsoc_weight
+
+    #获取当前行所有电压数据......................................................................................................................
+    def _cellvolt_get(self,num): 
+        cellvolt=[]
+        for j in range(1, self.param.CellVoltNums+1): 
+            s = str(j)
+            cellvolt.append(self.df_bms.loc[num,'单体电压' + s]/1000)
+        return cellvolt
+    
+    #dvdq方法计算soh...........................................................................................................................
+    def _dvdq_soh(self, chrg_st, chrg_end,cellvolt):    
+        Ah = 0  #参数赋初始值
+        Volt = cellvolt[chrg_st]
+        DV_Volt=[]
+        DQ_Ah = []
+        DVDQ = []
+        time2 = []
+        soc2 = []
+        Ah_tatal=[0]
+        xvolt=[]
+        #计算DV和DQ值
+        for j in range(chrg_st,chrg_end):
+            Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
+            Ah=Ah-self.packcrnt[j]*Step/3600
+            if (cellvolt[j]-Volt)>0.0019 and Ah>0:
+                Ah_tatal.append(Ah_tatal[-1]+Ah)
+                DQ_Ah.append(Ah)
+                DV_Volt.append(cellvolt[j]-Volt)
+                DVDQ.append((DV_Volt[-1])/DQ_Ah[-1])
+                xvolt.append(cellvolt[j])
+                Volt=cellvolt[j]
+                Ah = 0
+                time2.append(self.bmstime[j])
+                soc2.append(self.bms_soc[j])
+
+        #切片,去除前后10min的数据
+        df_Data1 = pd.DataFrame({'time': time2,
+                                'SOC': soc2,
+                                'DVDQ': DVDQ,
+                                'Ah_tatal': Ah_tatal[:-1],
+                                'DQ_Ah':DQ_Ah,
+                                'DV_Volt':DV_Volt,
+                                'XVOLT':xvolt})
+        start_time=df_Data1.loc[0,'time']
+        start_time=start_time+datetime.timedelta(seconds=900)
+        end_time=df_Data1.loc[len(time2)-1,'time']
+        end_time=end_time-datetime.timedelta(seconds=1200)
+        if soc2[0]<36:
+            df_Data1=df_Data1[(df_Data1['SOC']>40) & (df_Data1['SOC']<80)]
+        else:
+            df_Data1=df_Data1[(df_Data1['time']>start_time) & (df_Data1['SOC']<80)]
+        df_Data1=df_Data1[(df_Data1['XVOLT']>self.param.PeakVoltLowLmt) & (df_Data1['XVOLT']<self.param.PeakVoltUpLmt)]
+
+        # self._celltemp_weight(int((chrg_st+chrg_end)/2))
+        # print(self.packcrnt[int((chrg_st+chrg_end)/2)], min(self.celltemp))
+        # ax1 = plt.subplot(3, 1, 1)
+        # plt.plot(df_Data1['XVOLT'],df_Data1['DVDQ'],'r*-')
+        # plt.xlabel('Volt/V')
+        # plt.ylabel('DV/DQ')
+        # plt.legend()
+        # ax1 = plt.subplot(3, 1, 2)
+        # plt.plot(df_Data1['SOC'],df_Data1['XVOLT'],'y*-')
+        # plt.xlabel('SOC/%')
+        # plt.ylabel('Volt/V')
+        # plt.legend()
+        # ax1 = plt.subplot(3, 1, 3)
+        # plt.plot(df_Data1['SOC'], df_Data1['DVDQ'], 'r*-')
+        # plt.xlabel('SOC/%')
+        # plt.ylabel('DV/DQ')
+        # plt.legend()
+        # plt.show()
+
+        #寻找峰值并计算Soh
+        if len(df_Data1)>1:
+            PeakIndex=df_Data1['DVDQ'].idxmax()
+            #筛选峰值点附近±0.5%SOC内的数据
+            df_Data2=df_Data1[(df_Data1['SOC']>(df_Data1['SOC'][PeakIndex]-0.5)) & (df_Data1['SOC']<(df_Data1['SOC'][PeakIndex]+0.5))]
+            if len(df_Data2)>1 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.019:
+                Ah_tatal1 = df_Data1['Ah_tatal']
+                DVDQ = df_Data1['DVDQ']
+                soc2 = df_Data1['SOC']
+                xvolt = df_Data1['XVOLT']
+                if soc2[PeakIndex]>40 and soc2[PeakIndex]<80:
+                    cellsoh_init=(Ah_tatal[-1]-Ah_tatal1[PeakIndex]) * 100 / ((self.param.FullChrgSoc - self.param.PeakSoc) * 0.01 * self.param.Capacity)
+                    if cellsoh_init<95:
+                        cellsoh_init=cellsoh_init*0.3926+58.14
+                        return cellsoh_init
+                    else:
+                        return cellsoh_init
+                else:
+                    return 0
+            else:
+                df_Data1=df_Data1.drop([PeakIndex])
+                PeakIndex = df_Data1['DVDQ'].idxmax()
+                df_Data2 = df_Data1[(df_Data1['SOC'] > (df_Data1['SOC'][PeakIndex] - 0.5)) & (df_Data1['SOC'] < (df_Data1['SOC'][PeakIndex] + 0.5))]
+                if len(df_Data2) > 1 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.019:
+                    Ah_tatal1 = df_Data1['Ah_tatal']
+                    DVDQ = df_Data1['DVDQ']
+                    soc2 = df_Data1['SOC']
+                    xvolt = df_Data1['XVOLT']
+                    if soc2[PeakIndex]>40 and soc2[PeakIndex]<80:
+                        cellsoh_init=(Ah_tatal[-1]-Ah_tatal1[PeakIndex]) * 100 / ((self.param.FullChrgSoc - self.param.PeakSoc) * 0.01 * self.param.Capacity)
+                        if cellsoh_init<95:
+                            cellsoh_init=cellsoh_init*0.3926+58.14
+                            return cellsoh_init
+                        else:
+                            return cellsoh_init
+                    else:
+                        return 0
+                else:
+                    return 0
+        else:
+            return 0
+                
+    #NCM充电数据soh计算.........................................................................................................................
+    def _ncmsoh_chrg(self):      
+        self._chrgdata()
+        ChgStartValid=[]
+        ChgEndValid=[]
+        tempweightlist=[]
+        for i in range(min(len(self.ChgStart),len(self.ChgEnd))): 
+            self._celltemp_weight(self.ChgEnd[i])               #获取温度对应的静置时间及权重            
+            for k in range(self.ChgStart[i],self.ChgEnd[i]):   #去除电流0点
+                if self.packcrnt[k]<-0.5 and self.packcrnt[k+1]>-0.5 and self.packcrnt[k+2]>-0.5 and self.packcrnt[k+3]>-0.5:
+                    self.ChgEnd[i]=k
+   
+            #筛选满足2点法计算的数据
+            StandingTime=0
+            StandingTime1=0
+            if self.bms_soc[self.ChgEnd[i]]>70 and self.bms_soc[self.ChgStart[i]]<50:
+                for m in range(min(len(self.packcrnt)-self.ChgEnd[i]-2,self.ChgStart[i]-2)):
+                    if abs(self.packcrnt[self.ChgStart[i] - m - 1]) < 0.5:
+                        StandingTime = StandingTime + (self.bmstime[self.ChgStart[i] - m] - self.bmstime[self.ChgStart[i] - m - 1]).total_seconds()
+                    if abs(self.packcrnt[self.ChgEnd[i] + m + 1]) < 0.5:
+                        StandingTime1 = StandingTime1 + (self.bmstime[self.ChgEnd[i] + m + 1] - self.bmstime[self.ChgEnd[i] + m]).total_seconds()
+                    if StandingTime > self.StandardStandingTime and StandingTime1>self.StandardStandingTime and ((self.bmstime[self.ChgEnd[i]]-self.bmstime[self.ChgStart[i]]).total_seconds())/(self.ChgEnd[i]-self.ChgStart[i])<60:  #筛选静置时间>15min且慢充过程丢失数据少
+                        if abs(self.packcrnt[self.ChgEnd[i] + m + 2])>0.5 or m==len(self.packcrnt)-self.ChgEnd[i]-3: #如果电流<0.5,继续寻找充电后的静置电压,直到末尾
+                            ChgStartValid.append(self.ChgStart[i])
+                            ChgEndValid.append(self.ChgEnd[i]+m)
+                            tempweightlist.append(self.tempweight)
+                            break
+                    if abs(self.packcrnt[self.ChgStart[i] - m - 2])>0.5 and abs(self.packcrnt[self.ChgEnd[i] + m + 2])>0.5:
+                        break
+
+        if len(ChgStartValid)>0:   #两点法计算Soh
+            df_res=pd.DataFrame(columns=('time','sn','soh','soh1'))
+            soh2=[]
+            if not self.df_soh.empty:  #获取数据库中上次计算的Soh值
+                soh_init=list(self.df_soh['soh'])[-1]
+            else:
+                soh_init=list(self.bms_soh)[-1]
+
+            for i in range(len(ChgStartValid)):
+                Ah=0
+                for j in range(ChgStartValid[i],ChgEndValid[i]):  #计算Ah
+                    Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
+                    Ah=Ah-self.packcrnt[j+1]*Step/3600
+
+                for j in range(1, self.param.CellVoltNums+1):    #计算每个电芯的Soh
+                    s = str(j)
+                    OCVStart=self.df_bms.loc[ChgStartValid[i]-2,'单体电压' + s]/1000
+                    OCVEnd=self.df_bms.loc[ChgEndValid[i]-1,'单体电压' + s]/1000
+                    #soh
+                    ocv_Soc1=np.interp(OCVStart,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                    ocv_Soc2=np.interp(OCVEnd,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                
+                    soh2.append(Ah*100/((ocv_Soc2-ocv_Soc1)*0.01*self.param.Capacity))
+                soh1=np.mean(soh2)
+                delt_ocv_soc=ocv_Soc2-ocv_Soc1
+                self._deltsoc_weight(delt_ocv_soc)
+                soh_res=soh_init*(1-self.deltsoc_weight*tempweightlist[i])+soh1*self.deltsoc_weight*tempweightlist[i]
+                soh_init=soh_res
+                df_res.loc[i]=[self.bmstime[ChgStartValid[i]],self.sn,soh_res,soh1]
+            
+            return df_res
+        return pd.DataFrame()
+
+    #两点法计算三元SOH.........................................................................................................................
+    def _ncmsoh_twopoint(self):
+        standingpoint_st=[]
+        standingpoint_sp=[]
+        tempweightlist=[]
+        standingtime=0
+        for i in range(3,len(self.df_bms)-3):
+
+            if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1:     #电流为0
+                delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                standingtime=standingtime+delttime
+                self._celltemp_weight(i)     #获取不同温度对应的静置时间
+
+                if standingtime>self.StandardStandingTime:      #静置时间满足要求
+                    if standingpoint_st:                        
+                        if len(standingpoint_st)>len(standingpoint_sp):     #开始时刻已获取,结束时刻未获取
+                            cellvolt_now=self._cellvolt_get(i-1)     #获取当前行电压数据
+                            minocv_socnow=np.interp(min(cellvolt_now),self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_st=self._cellvolt_get(standingpoint_st[-1])   #获取开始时刻静置后的电压数据
+                            minocv_socst=np.interp(min(cellvolt_st),self.param.LookTab_OCV,self.param.LookTab_SOC)
+
+                            if abs(minocv_socst-minocv_socnow)>=40:   #当前时刻SOC与开始时刻SOC差>=40
+                                if abs(self.packcrnt[i+2])>=0.1:    #如果下一时刻电流>=0.5,则压入当前索引
+                                    standingpoint_sp.append(i)
+                                    standingpoint_st.append(i)
+                                    tempweightlist.append(self.tempweight)
+                                    standingtime=0
+                                    continue
+                                else:
+                                    if standingtime>3600 or i==len(self.df_bms)-2:   #仍处于静置,但静置时间>1h,则直接获取sp时刻,或者到了数据末尾
+                                        standingpoint_sp.append(i)
+                                        tempweightlist.append(self.tempweight)
+                                        continue
+                            else:
+                                if abs(self.packcrnt[i+2])>=0.1:
+                                    standingtime=0
+                                    if  minocv_socst<50 and minocv_socnow<minocv_socst:
+                                        standingpoint_st[-1]=i
+                                        continue
+                                elif abs(self.packcrnt[i+2])>=0.1:
+                                    standingtime=0
+                                    if minocv_socst>=50 and minocv_socnow>minocv_socst:
+                                        standingpoint_st[-1]=i
+                                        continue
+                                else:
+                                    continue
+                        else:
+                            if abs(self.packcrnt[i+2])>=0.1:
+                                standingpoint_st.append(i)
+                                standingtime=0
+                                continue
+                            else:
+                                continue
+                    else:
+                        if abs(self.packcrnt[i+2])>0.1:
+                            standingpoint_st.append(i)
+                            standingtime=0
+                            continue
+                        else:
+                            continue
+                else:
+                    continue
+            else:
+                standingtime=0
+                continue
+
+        #计算SOH......................................................................................................................
+        if standingpoint_sp:
+            column_name=['time_st','time_sp','sn','method','soh','cellsoh']
+            df_res=pd.DataFrame(columns=column_name)
+
+            for i in range(len(standingpoint_sp)):
+                cellocv_st=self._cellvolt_get(standingpoint_st[i])    #获取静置点所有电芯的电压
+                cellocv_sp=self._cellvolt_get(standingpoint_sp[i])
+                accumtime=self.accumtime.to_list()  #累计量的时间列表
+                timepoint_bms_st=self.bmstime[standingpoint_st[i]]   #获取静置点的时间
+                timepoint_bms_sp=self.bmstime[standingpoint_sp[i]]
+                timepoint_accum_st=bisect.bisect(accumtime,timepoint_bms_st)   #获取最接近静置点时间的累计量时间点
+                timepoint_accum_sp=bisect.bisect(accumtime,timepoint_bms_sp)
+                if timepoint_accum_sp>=len(accumtime):  #防止指针超出数据范围
+                    timepoint_accum_sp=len(accumtime)-1
+                
+                ah_packcrnt_dis=0
+                ah_packcrnt_chg=0
+                for j in range(standingpoint_st[i]+2,standingpoint_sp[i]): #计算累计Ah
+                    Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
+                    if self.packcrnt[j+1]>=0:
+                        ah_packcrnt_dis=ah_packcrnt_dis+self.packcrnt[j+1]*Step
+                    else:
+                        ah_packcrnt_chg=ah_packcrnt_chg-self.packcrnt[j+1]*Step  
+                ah_packcrnt_chg=ah_packcrnt_chg/3600
+                ah_packcrnt_dis=ah_packcrnt_dis/3600
+                ah_packcrnt=ah_packcrnt_chg-ah_packcrnt_dis     #两个静置点的总累计AH,负值代表放电,正值代表充电
+     
+                ah_accum_dis=self.df_accum.loc[timepoint_accum_sp,'累计放电电量']-self.df_accum.loc[timepoint_accum_st,'累计放电电量']  #两个静置点之间的放电电量
+                ah_accum_chg=self.df_accum.loc[timepoint_accum_sp,'累计充电电量']-self.df_accum.loc[timepoint_accum_st,'累计充电电量']  #两个静置点之间的充电电量
+                ah_accum_tatol=ah_accum_chg-ah_accum_dis  #两个静置点的总累计AH,负值代表放电,正值代表充电
+                ah_accum=ah_packcrnt
+
+                delt_days=(self.bmstime[standingpoint_sp[i]]-self.bmstime[standingpoint_st[i]]).total_seconds()/(3600*24)
+                if delt_days<=1: #两次时间间隔对计算结果的影响
+                    soh_weight1=1
+                elif delt_days<=2:
+                    soh_weight1=0.7
+                elif delt_days<=3:
+                    soh_weight1=0.4
+                else:
+                    soh_weight1=0
+                
+                if ah_packcrnt_dis<self.param.Capacity: #放电ah数对结果的影响
+                    soh_weight1=(1-ah_packcrnt_dis/(self.param.Capacity*1.5))*soh_weight1
+                else:
+                    soh_weight1=0.1
+            
+                if self.param.Capacity**0.7*0.4 < abs(ah_accum_tatol) < self.param.Capacity:    #累计量的权重
+                    if abs(ah_accum_tatol-ah_packcrnt)<self.param.Capacity/20:
+                        soh_weight1=soh_weight1*1
+                    elif abs(ah_accum_tatol-ah_packcrnt) < self.param.Capacity/10:
+                        soh_weight1=soh_weight1*0.8
+                    else:
+                        soh_weight1=soh_weight1*0.5
+                else:
+                    if self.param.Capacity*0.7*0.4< abs(ah_packcrnt) <self.param.Capacity:
+                        soh_weight1=soh_weight1*0.3
+                    else:
+                        soh_weight1=0
+
+                cellsoh=[]
+                for j in range(self.param.CellVoltNums):    #计算每个电芯的SOH值
+                    ocv_soc1=np.interp(cellocv_st[j],self.param.LookTab_OCV,self.param.LookTab_SOC)
+                    ocv_soc2=np.interp(cellocv_sp[j],self.param.LookTab_OCV,self.param.LookTab_SOC)
+                    delt_ocv_soc=ocv_soc2-ocv_soc1
+                    delt_ocv_soc_weight=self._deltsoc_weight(abs(delt_ocv_soc))
+                    soh_weight=soh_weight1*tempweightlist[i]*delt_ocv_soc_weight*0.5
+                    cellsoh_init=ah_accum*100/((ocv_soc2-ocv_soc1)*0.01*self.param.Capacity)
+
+                    if cellsoh_init>55 and cellsoh_init<120:    #判断soh值的有效区间
+                        if len(df_res)<1:
+                            if not self.df_soh.empty and 55<self.df_soh.loc[len(self.df_soh)-1,'soh']<120:
+                                cellsoh_last=eval(self.df_soh.loc[len(self.df_soh)-1,'cellsoh'])
+                                if soh_weight>1/abs(cellsoh_init-cellsoh_last[j]):
+                                    soh_weight=1/abs(cellsoh_init-cellsoh_last[j])
+                                    cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last[j]*(1-soh_weight)
+                                else:
+                                    cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last[j]*(1-soh_weight)
+                            else:
+                                cellsoh_cal=cellsoh_init*soh_weight+100*(1-soh_weight)
+                        else:
+                            cellsoh_last=eval(df_res.loc[len(df_res)-1,'cellsoh'])
+                            if soh_weight>1/abs(cellsoh_init-cellsoh_last[j]):
+                                soh_weight=1/abs(cellsoh_init-cellsoh_last[j])
+                                cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last[j]*(1-soh_weight)
+                            else:
+                                cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last[j]*(1-soh_weight)
+                        cellsoh_cal=eval(format(cellsoh_cal,'.1f'))
+                        cellsoh.append(cellsoh_cal)
+                    else:
+                        cellsoh=[]
+                        break
+                if cellsoh and 55<min(cellsoh)<120:
+                    soh=min(cellsoh)
+                    soh_list=[timepoint_bms_st, timepoint_bms_sp, self.sn, 1, soh, str(cellsoh)]
+                    df_res.loc[len(df_res)]=soh_list
+                else:
+                    continue
+            if df_res.empty:
+                return pd.DataFrame()
+            else:
+                return df_res
+        return pd.DataFrame()
+
+    #两点法和DVDQ法计算磷酸铁锂电池SOH..................................................................................................................
+    def _lfpsoh(self):
+        standingpoint_st=[]
+        standingpoint_sp=[]
+        tempweightlist1=[]
+        cellmaxvolt_number1=[]
+        standingtime=0
+        chrg_start=[]
+        chrg_end=[]
+        tempweightlist2=[]
+        cellmaxvolt_number2=[]
+        charging=0
+
+        for i in range(3,len(self.df_bms)-3):
+
+            #获取两点法法所需数据-开始.................................................................................................................
+            if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1:     #判断非平台区静置状态
+                delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                standingtime=standingtime+delttime
+                self._celltemp_weight(i)     #获取不同温度对应的静置时间
+
+                if standingtime>self.StandardStandingTime:  #静置时间满足要求
+                    if abs(self.packcrnt[i+2])>=0.1:  #下一时刻电流>0.1A
+                        standingtime=0
+                        cellvolt_now=self._cellvolt_get(i)
+                        if max(cellvolt_now)<self.param.OcvInflexionBelow:      #当前最大电芯电压<OCV下拐点
+                            if standingpoint_st:
+                                if len(standingpoint_st)>len(standingpoint_sp):
+                                    if self.packcrnt[standingpoint_st[-1]]<-1:     #判断上一次静置点的是否为满充
+                                        standingpoint_sp.append(i)
+                                        standingpoint_st.append(i)
+                                        tempweightlist1.append(self.tempweight)
+                                    else:
+                                        standingpoint_st[-1]=i
+                                        tempweightlist1[-1]=self.tempweight
+                                else:
+                                    standingpoint_st.append(i)
+                                    tempweightlist1.append(self.tempweight)
+                            else:
+                                standingpoint_st.append(i)
+                                tempweightlist1.append(self.tempweight)
+                        else:
+                            pass
+                    else:
+                        pass
+                else:
+                    pass
+            
+            elif self.packcrnt[i]<=-1 and self.packcrnt[i-1]<=-1 and self.packcrnt[i+1]<=-1 and self.packcrnt[i+2]>-1:    #判读满充状态
+                standingtime=0
+                self._celltemp_weight(i)
+                cellvolt_now=self._cellvolt_get(i)
+                if max(cellvolt_now)>self.param.CellFullChrgVolt:
+                    if standingpoint_st:
+                        if len(standingpoint_st)>len(standingpoint_sp):
+                            if abs(self.packcrnt[standingpoint_st[-1]])<0.5:     #判断上一次静置点是否为下拐点
+                                standingpoint_sp.append(i)
+                                standingpoint_st.append(i)
+                                tempweightlist1.append(self.tempweight)
+                                cellmaxvolt_number1.append(cellvolt_now.index(max(cellvolt_now))) #获取最大电压索引
+                                cellmaxvolt_number1.append(cellvolt_now.index(max(cellvolt_now))) #获取最大电压索引
+                            else:
+                                standingpoint_st[-1]=i
+                                tempweightlist1[-1]=self.tempweight
+                                cellmaxvolt_number1[-1]=cellvolt_now.index(max(cellvolt_now))
+                    else:
+                        standingpoint_st.append(i)
+                        tempweightlist1.append(self.tempweight)
+                        cellmaxvolt_number1.append(cellvolt_now.index(max(cellvolt_now)))
+                else:
+                    pass
+            
+            else:
+                standingtime=0
+                pass
+
+            #获取DVDQ算法所需数据——开始.............................................................................................................
+            if charging==0:
+                if self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and self.packcrnt[i+2]<=-1 and self.bms_soc[i]<40:     #充电开始
+                    self._celltemp_weight(i)
+                    charging=1
+                    if len(chrg_start)>len(chrg_end):
+                        chrg_start[-1]=i
+                        tempweightlist2[-1]=self.tempweight
+                    else:
+                        chrg_start.append(i)
+                        tempweightlist2.append(self.tempweight)
+                else:
+                    pass
+
+            else: #充电中
+                if (self.bmstime[i+1]-self.bmstime[i]).total_seconds()>180:  #如果充电过程中时间间隔>180s,则舍弃该次充电
+                    chrg_start.remove(chrg_start[-1])
+                    tempweightlist2.remove(tempweightlist2[-1])
+                    charging=0
+                    continue
+                elif self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and  self.packcrnt[i+2]>-1:  #判断电流波动时刻
+                    cellvolt_now=self._cellvolt_get(i+1)
+                    if max(cellvolt_now)>self.param.CellFullChrgVolt:   #电压>满充电压
+                        chrg_end.append(i+1)
+                        cellmaxvolt_number2.append(cellvolt_now.index(max(cellvolt_now)))   #获取最大电压索引
+                        charging=0
+                        continue
+                    else:
+                        pass
+                elif self.packcrnt[i+1]>-0.1 and self.packcrnt[i+2]>-0.1:   #判断充电结束
+                    charging=0
+                    if len(chrg_start)>len(chrg_end):
+                        chrg_start.remove(chrg_start[-1])
+                        tempweightlist2.remove(tempweightlist2[-1])
+                        continue
+                    else:
+                        continue
+                elif i==len(self.packcrnt)-4 and self.packcrnt[i+1]<-1 and self.packcrnt[i+2]<-1:
+                    charging=0
+                    if len(chrg_start)>len(chrg_end):
+                        cellvolt_now=self._cellvolt_get(i)
+                        if max(cellvolt_now)>self.param.CellFullChrgVolt:   #电压>满充电压
+                            chrg_end.append(i)
+                            cellmaxvolt_number2.append(cellvolt_now.index(max(cellvolt_now)))   #获取最大电压索引
+                            continue
+                        else:
+                            chrg_start.remove(chrg_start[-1])
+                            tempweightlist2.remove(tempweightlist2[-1])
+                            continue
+                    else:
+                        continue  
+                else:
+                    continue 
+        
+        #开始计算SOH.............................................................................................................................................
+        if standingpoint_sp or chrg_end:       
+            column_name=['time_st','time_sp','sn','method','soh','cellsoh','weight']
+            df_res=pd.DataFrame(columns=column_name)
+        
+            #两点法计算SOH........................................................................................................................................
+            if standingpoint_sp:    
+                for i in range(len(standingpoint_sp)):  #判断为满充点或者下拐点
+                    if self.packcrnt[standingpoint_sp[i]]<=-1:
+                        cellocv_st=self._cellvolt_get(standingpoint_st[i])    
+                        ocv_soc1=np.interp(cellocv_st[cellmaxvolt_number1[i]],self.param.LookTab_OCV,self.param.LookTab_SOC)
+                        ocv_soc2=self.param.FullChrgSoc
+                    else:
+                        cellocv_sp=self._cellvolt_get(standingpoint_sp[i])
+                        ocv_soc1=self.param.FullChrgSoc
+                        ocv_soc2=np.interp(cellocv_sp[cellmaxvolt_number1[i]],self.param.LookTab_OCV,self.param.LookTab_SOC)
+
+                    cellocv_sp=self._cellvolt_get(standingpoint_sp[i])
+                    accumtime=self.accumtime.to_list()  #累计量的时间列表
+                    timepoint_bms_st=self.bmstime[standingpoint_st[i]]   #获取静置点的时间
+                    timepoint_bms_sp=self.bmstime[standingpoint_sp[i]]
+                    timepoint_accum_st=bisect.bisect(accumtime,timepoint_bms_st)   #获取最接近静置点时间的累计量时间点
+                    timepoint_accum_sp=bisect.bisect(accumtime,timepoint_bms_sp)
+                    if timepoint_accum_sp>=len(accumtime):  #防止指针超出数据范围
+                        timepoint_accum_sp=len(accumtime)-1
+                    
+                    ah_packcrnt_dis=0
+                    ah_packcrnt_chg=0
+                    for j in range(standingpoint_st[i]+2,standingpoint_sp[i]+1): #计算累计Ah
+                        Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
+                        if self.packcrnt[j+1]>=0:
+                            ah_packcrnt_dis=ah_packcrnt_dis+self.packcrnt[j+1]*Step
+                        else:
+                            ah_packcrnt_chg=ah_packcrnt_chg-self.packcrnt[j+1]*Step
+                    ah_packcrnt_chg=ah_packcrnt_chg/3600
+                    ah_packcrnt_dis=ah_packcrnt_dis/3600          
+                    ah_packcrnt=ah_packcrnt_chg-ah_packcrnt_dis     #两个静置点的总累计AH,负值代表放电,正值代表充电
+        
+                    ah_accum_dis=self.df_accum.loc[timepoint_accum_sp,'累计放电电量']-self.df_accum.loc[timepoint_accum_st,'累计放电电量']  #两个静置点之间的放电电量
+                    ah_accum_chg=self.df_accum.loc[timepoint_accum_sp,'累计充电电量']-self.df_accum.loc[timepoint_accum_st,'累计充电电量']  #两个静置点之间的充电电量
+                    ah_accum_tatol=ah_accum_chg-ah_accum_dis  #两个静置点的总累计AH,负值代表放电,正值代表充电
+                    ah_accum=ah_accum_tatol
+
+                    delt_days=(self.bmstime[standingpoint_sp[i]]-self.bmstime[standingpoint_st[i]]).total_seconds()/(3600*24)
+                    if delt_days<=1: #两次时间间隔对计算结果的影响
+                        soh_weight=1
+                    elif delt_days<=2:
+                        soh_weight=0.7
+                    elif delt_days<=3:
+                        soh_weight=0.4
+                    else:
+                        soh_weight=0
+                    
+                    if self.param.Capacity*0.65*0.7 < abs(ah_packcrnt) < self.param.Capacity:    #累计量的权重
+                        if abs(ah_accum_tatol-ah_packcrnt)<self.param.Capacity/20:
+                            soh_weight=soh_weight*1
+                        elif abs(ah_accum_tatol-ah_packcrnt)<self.param.Capacity/10:
+                            soh_weight=soh_weight*0.8
+                        else:
+                            soh_weight=soh_weight*0.5
+                    else:
+                        if self.param.Capacity*0.65*0.7 < abs(ah_accum) < self.param.Capacity:
+                            soh_weight=soh_weight*0.3
+                        else:
+                            soh_weight=0
+
+                    delt_ocv_soc=ocv_soc2-ocv_soc1
+                    delt_ocv_soc_weight=self._deltsoc_weight(abs(delt_ocv_soc))
+                    soh_weight=soh_weight*tempweightlist1[i]*delt_ocv_soc_weight*0.5
+                    cellsoh_init=ah_accum*100/((ocv_soc2-ocv_soc1)*0.01*self.param.Capacity)
+
+                    if cellsoh_init>60 and cellsoh_init<115:    #判断soh值的有效区间
+                        soh_list=[timepoint_bms_st, timepoint_bms_sp, self.sn, 1, cellsoh_init, cellsoh_init,soh_weight]
+                        df_res.loc[len(df_res)]=soh_list
+                    else:
+                        pass
+            else:
+                pass
+
+            #DVDQ法计算SOH.......................................................................................................................................
+            if chrg_end:
+                for i in range(len(chrg_end)):
+                    cellvolt_max = self.df_bms['单体电压' + str(cellmaxvolt_number2[i]+1)] / 1000     #获取最大电压
+                    cellvolt=self._np_move_avg(cellvolt_max, 3, mode="same")     #对电压进行滑动平均滤
+
+                    cellsoh_init=self._dvdq_soh(chrg_start[i],chrg_end[i],cellvolt)     #dvdq计算soh
+
+                    soh_weight=tempweightlist2[i]*0.25
+                    if cellsoh_init>60 and cellsoh_init<115:    #判断soh值的有效区间
+                        soh_list=[self.bmstime[chrg_start[i]], self.bmstime[chrg_end[i]], self.sn, 2, cellsoh_init, str(cellsoh_init),soh_weight]
+                        df_res.loc[len(df_res)]=soh_list
+                    else:
+                        pass
+            
+            #对SOH结果进行滤波处理................................................................................................................................
+            if df_res.empty:
+                return pd.DataFrame()
+            else:
+                df_res=df_res.sort_values(by='time_st',ascending=True)
+                df_res.index=range(len(df_res))
+                for i in range(len(df_res)):
+                    cellsoh_init=df_res.loc[i,'soh']
+                    soh_weight=df_res.loc[i,'weight']
+                    if i<1:
+                        if not self.df_soh.empty and 60<self.df_soh.loc[len(self.df_soh)-1,'soh']<115:
+                            cellsoh_last=self.df_soh.loc[len(self.df_soh)-1,'soh']
+                            if soh_weight>1/abs(cellsoh_init-cellsoh_last):
+                                soh_weight=1/abs(cellsoh_init-cellsoh_last)
+                                cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
+                            else:
+                                cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
+                        else:
+                            cellsoh_cal=cellsoh_init*soh_weight+100*(1-soh_weight)
+                        cellsoh_cal=eval(format(cellsoh_cal,'.1f'))
+                        if 60<cellsoh_cal<115:
+                            df_res.loc[i,'soh']=cellsoh_cal
+                            df_res.loc[i,'cellsoh']=str(cellsoh_cal)
+                        else:
+                            df_res=df_res.drop(i,axis=0)
+                    else:
+                        cellsoh_last=df_res.loc[i-1,'soh']
+                        if soh_weight>1/abs(cellsoh_init-cellsoh_last):
+                            soh_weight=1/abs(cellsoh_init-cellsoh_last)
+                            cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
+                        else:
+                            cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
+                        cellsoh_cal=eval(format(cellsoh_cal,'.1f'))
+                        if 60<cellsoh_cal<115:
+                            df_res.loc[i,'soh']=cellsoh_cal
+                            df_res.loc[i,'cellsoh']=str(cellsoh_cal)
+                        else:
+                            df_res=df_res.drop(i,axis=0)
+                df_res=df_res.drop('weight',axis=1)
+                return df_res
+
+        return pd.DataFrame()
+
+

BIN
LIB/MIDDLE/CellStateEstimation/SOH/V1_0_0/SOH表单.xlsx


+ 89 - 0
LIB/MIDDLE/CellStateEstimation/SOH/main.py

@@ -0,0 +1,89 @@
+import CBMSBatSoh
+import log
+#coding=utf-8
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+from sqlalchemy import create_engine
+import time, datetime
+from LIB.MIDDLE.CellStateEstimation.Common import DBDownload
+
+dbManager = DBManager.DBManager()
+if __name__ == "__main__":
+    excelpath=r'D:\Platform\platform_python\data_analyze_platform\USER\01qixiang\sn-20210903.xlsx'
+    SNdata_6060 = pd.read_excel(excelpath, sheet_name='科易6060')
+    SNdata_6040 = pd.read_excel(excelpath, sheet_name='科易6040')
+    SNdata_4840 = pd.read_excel(excelpath, sheet_name='科易4840')
+    SNdata_L7255 = pd.read_excel(excelpath, sheet_name='格林美-力信7255')
+    SNdata_C7255 = pd.read_excel(excelpath, sheet_name='格林美-CATL7255')
+    SNdata_U7255 = pd.read_excel(excelpath, sheet_name='优旦7255')
+    SNnums_6060=SNdata_6060['SN号'].tolist()
+    SNnums_6040=SNdata_6040['SN号'].tolist()
+    SNnums_4840=SNdata_4840['SN号'].tolist()
+    SNnums_L7255=SNdata_L7255['SN号'].tolist()
+    SNnums_C7255=SNdata_C7255['SN号'].tolist()
+    SNnums_U7255=SNdata_U7255['SN号'].tolist()
+    SNnums= SNnums_6060 + SNnums_L7255 + SNnums_C7255 + SNnums_6040 + SNnums_4840 + SNnums_U7255
+
+    now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+    now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+    start_time=now_time-datetime.timedelta(days=30)
+    end_time=str(now_time)
+    start_time=str(start_time)
+
+    #log信息配置
+    mylog=log.Mylog('log.txt','error')
+    mylog.logcfg()
+
+    for sn in SNnums:
+        try:
+            if 'PK500' in sn:
+                celltype=1 #6040三元电芯
+            elif 'PK502' in sn:
+                celltype=2 #4840三元电芯
+            elif 'PK504' in sn:
+                celltype=99    #60ah林磷酸铁锂电芯
+            elif 'MGMLXN750' in sn:
+                celltype=3 #力信50ah三元电芯
+            elif 'MGMCLN750' in sn: 
+                celltype=4 #CATL 50ah三元电芯
+            else:
+                print('未找到对应电池编号!!!\n',sn)
+                # sys.exit()
+            
+            sn='PK504B10100004353'
+            celltype=99
+            start_time='2021-04-01 19:12:26'
+            end_time='2021-06-02 19:12:26'
+            # df_bms= pd.read_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\01qixiang\98Download\\'+'BMS_'+sn+'.csv',encoding='GB18030')
+            # df_accum= pd.read_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\01qixiang\98Download\\'+'BMS_accum_'+sn+'.csv',encoding='GB18030')
+            
+            #..........................................................................读取原始数据库数据...........................................................................
+            dbManager = DBManager.DBManager()
+            df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms','accum'])
+            df_bms = df_data['bms']
+            df_accum=df_data['accum']
+            # df_bms.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\01qixiang\98Download\\'+'BMS_'+sn+'.csv',encoding='GB18030')
+            # df_accum.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\01qixiang\98Download\\'+'BMS_accum_'+sn+'.csv',encoding='GB18030')
+
+            #..........................................................................读取结果数据库数据.........................................................................
+            host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+            port=3306
+            db='qx_cas'
+            user='qx_read'
+            password='Qx@123456'
+            tablename='cellstateestimation_soh'
+            DBRead=DBDownload.DBDownload(host, port, db, user, password)
+            with DBRead as DBRead:
+                df_soh=DBRead.getdata('time_st','time_sp','sn','method','soh','cellsoh', tablename=tablename, sn=sn)
+
+            #..............................................................................计算soh...................................................................................
+            BatSoh=CBMSBatSoh.BatSoh(sn,celltype,df_bms,df_accum,df_soh)
+            df_res=BatSoh.batsoh()
+            df_res.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\01qixiang\99Result\\'+'CBMS_SOH'+sn+'_1.csv',encoding='GB18030')
+            # print('done!!!')
+             
+        except IndexError as e:
+            print(repr(e))
+            mylog.logopt(sn,e)
+            pass

+ 370 - 0
LIB/MIDDLE/CellStateEstimation/Uniform/V1_0_0/CBMSBatUniform.py

@@ -0,0 +1,370 @@
+import pandas as pd
+import numpy as np
+import datetime
+import bisect
+import matplotlib.pyplot as plt
+from LIB.MIDDLE.CellStateEstimation.Common import BatParam
+
+class BatUniform:
+    def __init__(self,sn,celltype,df_bms):  #参数初始化
+
+        self.sn=sn
+        self.celltype=celltype
+        self.param=BatParam.BatParam(celltype)
+        self.df_bms=df_bms
+        self.packcrnt=df_bms['总电流[A]']*self.param.PackCrntDec
+        self.packvolt=df_bms['总电压[V]']
+        self.bms_soc=df_bms['SOC[%]']
+        self.bmstime= pd.to_datetime(df_bms['时间戳'], format='%Y-%m-%d %H:%M:%S')
+
+    def batuniform(self):
+        if self.celltype==1 or self.celltype==2 or self.celltype==3 or self.celltype==4:
+            df_res=self._ncm_uniform()
+            return df_res
+                     
+        elif self.celltype==99:
+            df_res=self._lfp_uniform()
+            return df_res
+        
+        else:
+            return pd.DataFrame()
+    
+    #定义滑动滤波函数........................................................................................................................................
+    def _np_move_avg(self,a, n, mode="same"): 
+        return (np.convolve(a, np.ones((n,)) / n, mode=mode))
+    
+    #寻找当前行数据的最小温度值................................................................................................................................
+    def _celltemp_weight(self,num):   
+        celltemp = []
+        for j in range(1, self.param.CellTempNums+1):
+            s = str(j)
+            celltemp.append(self.df_bms.loc[num,'单体温度' + s])
+        celltemp.remove(min(celltemp))
+        self.celltemp=celltemp
+        if self.celltype==99:
+            if min(celltemp)>=20:
+                self.tempweight=1
+                self.StandardStandingTime=2400
+            elif min(celltemp)>=10:
+                self.tempweight=0.6
+                self.StandardStandingTime=3600
+            elif min(celltemp)>=5:
+                self.tempweight=0.
+                self.StandardStandingTime=3600
+            else:
+                self.tempweight=0.1
+                self.StandardStandingTime=7200
+        else:
+            if min(celltemp)>=20:
+                self.tempweight=1
+                self.StandardStandingTime=1800
+            elif min(celltemp)>=10:
+                self.tempweight=0.8
+                self.StandardStandingTime=2400
+            elif min(celltemp)>=5:
+                self.tempweight=0.6
+                self.StandardStandingTime=3600
+            else:
+                self.tempweight=0.2
+                self.StandardStandingTime=7200
+        
+    #获取当前行所有电压数据............................................................................................................................
+    def _cellvolt_get(self,num):
+        cellvolt=[]
+        for j in range(1, self.param.CellVoltNums+1): 
+            s = str(j)
+            cellvolt.append(self.df_bms.loc[num,'单体电压' + s]/1000)
+        return(cellvolt)
+
+    #寻找DVDQ的峰值点,并返回..........................................................................................................................
+    def _dvdq_peak(self, time, soc, cellvolt, packcrnt):
+        cellvolt = self._np_move_avg(cellvolt, 3, mode="same")
+        Soc = 0
+        Ah = 0
+        Volt = cellvolt[0]
+        DV_Volt = []
+        DQ_Ah = []
+        DVDQ = []
+        time1 = []
+        soc1 = []
+        soc2 = []
+        xvolt=[]
+
+        for m in range(1, len(time)):
+            Step = (time[m] - time[m - 1]).total_seconds()
+            Soc = Soc - packcrnt[m] * Step * 100 / (3600 * self.param.Capacity)
+            Ah = Ah - packcrnt[m] * Step / 3600
+            if (cellvolt[m]-Volt)>0.0019 and Ah>0:
+                DQ_Ah.append(Ah)
+                DV_Volt.append(cellvolt[m]-Volt)
+                DVDQ.append((DV_Volt[-1])/DQ_Ah[-1])
+                xvolt.append(cellvolt[m])
+                Volt=cellvolt[m]
+                Ah = 0
+                soc1.append(Soc)
+                time1.append(time[m])
+                soc2.append(soc[m])
+
+        #切片,去除前后10min的数据
+        df_Data1 = pd.DataFrame({'time': time1,
+                                'SOC': soc2,
+                                'DVDQ': DVDQ,
+                                'AhSoc': soc1,
+                                'DQ_Ah':DQ_Ah,
+                                'DV_Volt':DV_Volt,
+                                'XVOLT':xvolt})
+        start_time=df_Data1.loc[0,'time']
+        start_time=start_time+datetime.timedelta(seconds=900)
+        end_time=df_Data1.loc[len(time1)-1,'time']
+        end_time=end_time-datetime.timedelta(seconds=1200)
+        if soc2[0]<36:
+            df_Data1=df_Data1[(df_Data1['SOC']>40) & (df_Data1['SOC']<80)]
+        else:
+            df_Data1=df_Data1[(df_Data1['time']>start_time) & (df_Data1['SOC']<80)]
+        df_Data1=df_Data1[(df_Data1['XVOLT']>self.param.PeakVoltLowLmt) & (df_Data1['XVOLT']<self.param.PeakVoltUpLmt)]
+
+        # print(packcrnt[int(len(time)/2)], min(self.celltemp))
+        # ax1 = plt.subplot(3, 1, 1)
+        # plt.plot(df_Data1['XVOLT'],df_Data1['DVDQ'],'g*-')
+        # plt.xlabel('Volt/V')
+        # plt.ylabel('DV/DQ')
+        # plt.legend()
+        # ax1 = plt.subplot(3, 1, 2)
+        # plt.plot(df_Data1['SOC'],df_Data1['XVOLT'],'y*-')
+        # plt.xlabel('SOC/%')
+        # plt.ylabel('Volt/V')
+        # plt.legend()
+        # ax1 = plt.subplot(3, 1, 3)
+        # plt.plot(df_Data1['SOC'], df_Data1['DVDQ'], 'r*-')
+        # plt.xlabel('SOC/%')
+        # plt.ylabel('DV/DQ')
+        # plt.legend()
+        # plt.show()
+
+        if len(df_Data1)>2:     #寻找峰值点,且峰值点个数>2
+            PeakIndex = df_Data1['DVDQ'].idxmax()
+            df_Data2 = df_Data1[(df_Data1['SOC'] > (df_Data1['SOC'][PeakIndex] - 0.5)) & (df_Data1['SOC'] < (df_Data1['SOC'][PeakIndex] + 0.5))]
+            if len(df_Data2) > 2 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.019:
+                return df_Data1['AhSoc'][PeakIndex]
+            else:
+                df_Data1 = df_Data1.drop([PeakIndex])
+                PeakIndex = df_Data1['DVDQ'].idxmax()
+                df_Data2 = df_Data1[(df_Data1['SOC'] > (df_Data1['SOC'][PeakIndex] - 0.5)) & (df_Data1['SOC'] < (df_Data1['SOC'][PeakIndex] + 0.5))]
+                if len(df_Data2) > 2 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.019:
+                    return df_Data1['AhSoc'][PeakIndex]
+                else:
+                    return 0
+        else:
+            return 0
+ 
+    #三元电池一致性计算.................................................................................................................................
+    def _ncm_uniform(self):
+        column_name=['time','sn','cellsoc_diff','cellvolt_diff','cellmin_num','cellmax_num']
+        df_res=pd.DataFrame(columns=column_name)
+        standingtime=0
+
+        for i in range(1,len(self.df_bms)-2):
+
+            if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1:     #电流为0
+                delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                standingtime=standingtime+delttime
+                self._celltemp_weight(i)     #获取不同温度对应的静置时间
+
+                if standingtime>self.StandardStandingTime:      #静置时间满足要求
+                    if abs(self.packcrnt[i+2]) >= 0.1:
+                        standingtime=0                    
+                        cellvolt_now=self._cellvolt_get(i)     #获取当前行电压数据
+                        cellvolt_min=min(cellvolt_now)
+                        cellvolt_max=max(cellvolt_now)
+                        if 3<cellvolt_min<4.5 and 3<cellvolt_max<4.5:
+                            cellmin_num=cellvolt_now.index(cellvolt_min)+1
+                            cellmax_num=cellvolt_now.index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num]
+                    elif standingtime>3600*12:
+                        standingtime=0                    
+                        cellvolt_now=self._cellvolt_get(i)     #获取当前行电压数据
+                        cellvolt_min=min(cellvolt_now)
+                        cellvolt_max=max(cellvolt_now)
+                        if 3<cellvolt_min<4.5 and 3<cellvolt_max<4.5:
+                            cellmin_num=cellvolt_now.index(cellvolt_min)+1
+                            cellmax_num=cellvolt_now.index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num]
+                    elif i>=len(self.df_bms)-4:
+                        standingtime=0
+                        cellvolt_now=self._cellvolt_get(i)     #获取当前行电压数据
+                        cellvolt_min=min(cellvolt_now)
+                        cellvolt_max=max(cellvolt_now)
+                        if 3<cellvolt_min<4.5 and 3<cellvolt_max<4.5:
+                            cellmin_num=cellvolt_now.index(cellvolt_min)+1
+                            cellmax_num=cellvolt_now.index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num]
+                        break
+                    else:
+                        continue
+                else:
+                    continue
+            else:
+                standingtime=0
+                continue
+
+        if df_res.empty:    #返回计算结果
+            return pd.DataFrame()
+        else:
+            return df_res
+
+    #磷酸铁锂电池一致性计算.........................................................................................................................
+    def _lfp_uniform(self):
+        column_name=['time','sn','cellsoc_diff','cellvolt_diff','cellmin_num','cellmax_num']
+        df_res=pd.DataFrame(columns=column_name)
+        standingtime=0
+        chrg_start=[]
+        chrg_end=[]
+        charging=0
+
+        for i in range(3,len(self.df_bms)-3):
+
+            #静置电压法计算电芯一致性
+            if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1:     #电流为0
+                delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                standingtime=standingtime+delttime
+                self._celltemp_weight(i)     #获取不同温度对应的静置时间
+
+                if standingtime>self.StandardStandingTime:      #静置时间满足要求
+                    cellvolt_now=self._cellvolt_get(i)     #获取当前行电压数据
+                    cellvolt_min=min(cellvolt_now)
+                    cellvolt_max=max(cellvolt_now)
+                    if abs(self.packcrnt[i+2]) >= 0.1:     
+                        standingtime=0  
+                        if 2 < cellvolt_max < self.param.OcvInflexionBelow-0.002:                 
+                            cellmin_num=cellvolt_now.index(cellvolt_min)+1
+                            cellmax_num=cellvolt_now.index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num]
+                    elif i>=len(self.df_bms)-4:
+                        standingtime=0
+                        if 2 < cellvolt_max < self.param.OcvInflexionBelow-0.002:
+                            cellmin_num=cellvolt_now.index(cellvolt_min)+1
+                            cellmax_num=cellvolt_now.index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num]
+                    else:
+                        pass
+                else:
+                    pass
+            else:
+                standingtime=0
+                pass   
+
+            #获取DVDQ算法所需数据——开始............................................................................................................
+            if charging==0: #判断充电开始
+                if self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and self.packcrnt[i+2]<=-1 and self.bms_soc[i]<40:     #充电开始
+                    charging=1
+                    if len(chrg_start)>len(chrg_end):
+                        chrg_start[-1]=i
+                    else:
+                        chrg_start.append(i)
+                else:
+                    pass
+
+            else: #充电中
+                if (self.bmstime[i+1]-self.bmstime[i]).total_seconds()>180:  #如果充电过程中时间间隔>180s,则舍弃该次充电
+                    chrg_start.remove(chrg_start[-1])
+                    charging=0
+                    continue
+                elif self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and  self.packcrnt[i+2]>-1:  #判断电流波动时刻
+                    cellvolt_now=self._cellvolt_get(i+1)
+                    if max(cellvolt_now)>self.param.CellFullChrgVolt:   #电压>满充电压
+                        chrg_end.append(i+1)
+                        charging=0
+                        continue
+                    else:
+                        pass
+                elif self.packcrnt[i+1]>-0.1 and self.packcrnt[i+2]>-0.1:   #判断充电结束
+                    charging=0
+                    if len(chrg_start)>len(chrg_end):
+                        if self.bms_soc[i]>90:
+                            chrg_end.append(i)
+                        else:
+                            chrg_start.remove(chrg_start[-1])
+                            continue
+                    else:
+                        continue
+                elif i==len(self.packcrnt)-4 and self.packcrnt[i+1]<-1 and self.packcrnt[i+2]<-1:
+                    charging=0
+                    if len(chrg_start)>len(chrg_end):
+                        if self.bms_soc[i]>90:   #soc>90
+                            chrg_end.append(i)
+                            continue
+                        else:
+                            chrg_start.remove(chrg_start[-1])
+                            continue
+                    else:
+                        continue
+                else:
+                    continue   
+
+        if chrg_end:    #DVDQ方法计算soc差
+            peaksoc_list=[]
+            for i in range(len(chrg_end)):
+                peaksoc_list = []
+                self._celltemp_weight(chrg_start[i])
+                if min(self.celltemp)>10:
+                    for j in range(1, self.param.CellVoltNums + 1):
+                        s = str(j)
+                        cellvolt = self.df_bms['单体电压' + s]/1000
+                        cellvolt = list(cellvolt[chrg_start[i]:chrg_end[i]])
+                        time = list(self.bmstime[chrg_start[i]:chrg_end[i]])
+                        packcrnt = list(self.packcrnt[chrg_start[i]:chrg_end[i]])
+                        soc = list(self.bms_soc[chrg_start[i]:chrg_end[i]])
+                        peaksoc = self._dvdq_peak(time, soc, cellvolt, packcrnt)
+                        if peaksoc>1:
+                            peaksoc_list.append(peaksoc)    #计算到达峰值点的累计Soc
+                        else:
+                            pass
+                    if len(peaksoc_list)>self.param.CellVoltNums/2:
+                        peaksoc_max=max(peaksoc_list)
+                        peaksoc_min=min(peaksoc_list)
+                        peaksoc_maxnum=peaksoc_list.index(peaksoc_min)+1
+                        peaksoc_minnum=peaksoc_list.index(peaksoc_max)+1
+                        cellsoc_diff=peaksoc_max-peaksoc_min
+                        cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                        df_res.loc[len(df_res)]=[self.bmstime[chrg_start[i]], self.sn, cellsoc_diff, 0, peaksoc_minnum, peaksoc_maxnum]
+                    else:
+                        pass
+                    plt.show()
+                else:
+                    pass
+
+        if df_res.empty:
+            return pd.DataFrame()
+        else:
+            df_res.sort_values(by='time', ascending=True, inplace=True)
+            return df_res

BIN
LIB/MIDDLE/CellStateEstimation/Uniform/V1_0_0/Uniform表单.xlsx


+ 81 - 0
LIB/MIDDLE/CellStateEstimation/Uniform/main.py

@@ -0,0 +1,81 @@
+import CBMSBatUniform
+import log
+
+#coding=utf-8
+import os
+import sys
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+import time, datetime
+from urllib import parse
+from LIB.MIDDLE.CellStateEstimation.Common import DBDownload
+
+dbManager = DBManager.DBManager()
+if __name__ == "__main__":
+    excelpath=r'D:\Platform\platform_python\data_analyze_platform\USER\01qixiang\sn-20210903.xlsx'
+    SNdata_6060 = pd.read_excel(excelpath, sheet_name='科易6060')
+    SNdata_6040 = pd.read_excel(excelpath, sheet_name='科易6040')
+    SNdata_4840 = pd.read_excel(excelpath, sheet_name='科易4840')
+    SNdata_L7255 = pd.read_excel(excelpath, sheet_name='格林美-力信7255')
+    SNdata_C7255 = pd.read_excel(excelpath, sheet_name='格林美-CATL7255')
+    SNdata_U7255 = pd.read_excel(excelpath, sheet_name='优旦7255')
+    SNnums_6060=SNdata_6060['SN号'].tolist()
+    SNnums_6040=SNdata_6040['SN号'].tolist()
+    SNnums_4840=SNdata_4840['SN号'].tolist()
+    SNnums_L7255=SNdata_L7255['SN号'].tolist()
+    SNnums_C7255=SNdata_C7255['SN号'].tolist()
+    SNnums_U7255=SNdata_U7255['SN号'].tolist()
+    SNnums= SNnums_6060 + SNnums_L7255 + SNnums_C7255 + SNnums_6040 + SNnums_4840 + SNnums_U7255
+
+    now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+    now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+    start_time=now_time-datetime.timedelta(days=20)
+    end_time=str(now_time)
+    start_time=str(start_time)
+
+    #log信息配置
+    mylog=log.Mylog('log.txt','error')
+    mylog.logcfg()
+
+    for sn in SNnums:
+        try:
+            if 'PK500' in sn:
+                celltype=1 #6040三元电芯
+            elif 'PK502' in sn:
+                celltype=2 #4840三元电芯
+            elif 'K504B' in sn:
+                celltype=99    #60ah林磷酸铁锂电芯
+            elif 'MGMLXN750' in sn:
+                celltype=3 #力信50ah三元电芯
+            elif 'MGMCLN750' or 'UD' in sn: 
+                celltype=4 #CATL 50ah三元电芯
+            else:
+                print('SN:{},未找到对应电池类型!!!'.format(sn))
+                continue
+                # sys.exit()
+            
+            # sn='PK504B10100004386'
+            # celltype=99
+            # start_time='2021-6-21 9:49:37'
+            # end_time='2021-6-22 19:49:37'
+            # df_bms= pd.read_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\01qixiang\98Download\\'+'BMS_'+sn+'.csv',encoding='GB18030')
+
+            #.....................................................................读取原始数据库数据...........................................................................
+            dbManager = DBManager.DBManager()
+            df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms'])
+            df_bms = df_data['bms']
+            # df_bms.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\01qixiang\98Download\\'+'BMS_'+sn+'.csv',encoding='GB18030')
+
+            #.........................................................................计算一致性..............................................................................
+            BatUniform=CBMSBatUniform.BatUniform(sn,celltype,df_bms)
+            df_res=BatUniform.batuniform()
+            df_res.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\01qixiang\99Result\\'+'CBMS_Uniform'+sn+'_3.csv',encoding='GB18030')
+            # print('done!!!')
+        
+        except IndexError as e:
+            print(repr(e))
+            mylog.logopt(sn,e)
+            pass

BIN
LIB/MIDDLE/CellStateEstimation/算法运行周期.xlsx


+ 422 - 0
LIB/MIDDLE/DrivingRange/UpdtFct.py

@@ -0,0 +1,422 @@
+import pandas as pd
+import pymysql
+from sqlalchemy import create_engine
+import datetime
+from sqlalchemy.orm import sessionmaker
+import pdb
+
+# #建立引擎
+# engine = create_engine(str(r"mysql+mysqldb://%s:" + '%s' + "@%s/%s") % ('root', 'pengmin', 'localhost', 'qixiangdb'))
+# #连接到qx数据库
+# conn_qx = pymysql.connect(
+#         host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com',
+#         user='qx_cas',
+#         password='Qx@123456',#Qx@123456
+#         database='qx_cas',
+#         charset='utf8'
+#     )
+# #连接到本地数据库,输出物
+# conn_local = pymysql.connect(
+#         host='localhost',
+#         user='root',
+#         password='pengmin',
+#         database='qixiangdb',
+#         charset='utf8'
+#     )
+
+#计算下一个soc
+def getNextSoc(start_soc):
+    '''输入当前的soc,寻找目标soc函数'''
+    if start_soc>80:
+        next_soc=80
+    elif start_soc>60:
+        next_soc=60
+    elif start_soc>40:
+        next_soc=40
+    elif start_soc>20:
+        next_soc=20
+    else:
+        next_soc=5#下一次目标soc
+    return next_soc
+#更新全部5个区间段的factor
+def updtSnFct(sn_factor_df,end_soc,delta_range,range_soc):
+    '''输入当前的soc区间段,里程变量量,soc变化量,输出新的df
+    sn_factor_df为dataframe,delta_range单位为km,range_soc单位为km/persoc'''
+    if end_soc==80:
+        updtFctByCol(sn_factor_df,'a0',delta_range,range_soc)
+    elif end_soc==60:
+        updtFctByCol(sn_factor_df,'a1',delta_range,range_soc)
+    elif end_soc==40:
+        updtFctByCol(sn_factor_df,'a2',delta_range,range_soc)
+    elif end_soc==20:
+        updtFctByCol(sn_factor_df,'a3',delta_range,range_soc)
+    elif end_soc<20:
+        updtFctByCol(sn_factor_df,'a4',delta_range,range_soc)
+    return sn_factor_df
+#更新一列的factor
+def updtFctByCol(sn_factor_df,colmun_name,delta_range,range_soc):
+    '''更新制定列的factor,sn_factor_df为dataframe,新的系数更新到第一行。delta_range单位为km,
+    range_soc单位为km/persoc,默认按照100km更新续驶里程权重'''
+    range_soc_old=sn_factor_df.loc[0,colmun_name]#读取第0行的老factor
+    debounce_range=100#更新权重
+    new_factor=range_soc*((delta_range)/debounce_range)+range_soc_old*(1-delta_range/debounce_range)
+    #在第1行,存储新的factor
+    sn_factor_df.loc[1,colmun_name]=new_factor
+    return sn_factor_df
+#更新今日的factor
+def updtTodayFct(factor_input,sn_day_df):
+    '''更新今日的Factor***'''
+    sn_factor_df_last=factor_input
+    start_soc=sn_day_df.loc[0,'soc']#首行soc
+    next_soc=getNextSoc(start_soc)#下一个目标soc
+    start_range=sn_day_df.loc[0,'vehodo']#首行vehodo
+    sn=sn_day_df.loc[0,'name']#sn号
+
+    for index in range(len(sn_day_df)-1):
+    #寻找分割点,
+        index_soc=sn_day_df.loc[index,'soc']#当前行soc
+        next_index_soc=sn_day_df.loc[index+1,'soc']#下一行soc
+
+        if (index_soc>=next_soc)&(next_index_soc<next_soc):
+            #当前行soc>目标soc,下一行低soc<目标soc,说明到达了分割点80-60-40-20
+            delta_soc_tonext=start_soc-next_soc#两个距离点的soc差,单位为%
+            delta_range_tonext=sn_day_df.loc[index,'vehodo']-start_range#两个时间点的距离差,单位为m
+            delta_range_tonext_km=delta_range_tonext/1000#两个时间点的距离差,单位为km
+            range_soc_tonext=(delta_range_tonext_km)/delta_soc_tonext#单位soc可行驶的公里数
+
+            # print(sn+'start_soc: '+str(start_soc),'next_soc: '+str(next_soc),'delta_vehodo; '+str(round(delta_range_tonext_km,3))
+            # +'km'+' range_soc:'+str(round(range_soc_tonext,3)))#调试用语句,看单次factor变化量
+
+            if (delta_range_tonext_km>1)&(delta_range_tonext_km<5*delta_soc_tonext):
+                #里程变化量>1km。且<5倍的soc变化量,大于此值认为不合理。
+                sn_factor_df_last=updtSnFct(sn_factor_df_last,next_soc,delta_range_tonext_km,range_soc_tonext)
+            
+            start_soc=next_index_soc#变更开始soc
+            next_soc=getNextSoc(start_soc)#变更结束soc
+            start_range=sn_day_df.loc[index+1,'vehodo']#变更开始里程    
+
+    return sn_factor_df_last
+#对driveinfo进行预处理
+def snDayDfPreProcess(sn_day_df):
+    '''预处理,判断是否在dirvemode,获取drivemode条件下的累计行驶距离。
+    增加delta_soc列,drive_flg列,vehodo列'''
+    sn_day_df=sn_day_df.reset_index(drop=True)#重置index
+    #增加列,计算delta_soc
+    for index in range(len(sn_day_df)):
+        if index==0:
+            sn_day_df.loc[index,'delta_soc']=0
+        else:
+            sn_day_df.loc[index,'delta_soc']=sn_day_df.loc[index,'soc']-sn_day_df.loc[index-1,'soc']
+    #增加列,判断是否在drive状态
+    drive_flg=False
+    accum_distance=0
+    for index in range(len(sn_day_df)):
+        if index==0:
+            sn_day_df.loc[index,'drive_status']=drive_flg
+            sn_day_df.loc[index,'vehodo']=0
+        else:
+            if (sn_day_df.loc[index,'delta_soc']<-0.1)|\
+                ((sn_day_df.loc[index,'delta_soc']<=0)&(sn_day_df.loc[index,'distance']>500)):#soc处于下降状态,说明在drive
+                drive_flg=True#置true
+            elif sn_day_df.loc[index,'delta_soc']>0.1:#soc处于上升状态,说明不在drive
+                drive_flg=False#置false
+                accum_distance=0#清零
+            sn_day_df.loc[index,'drive_flg']=drive_flg
+            accum_distance+=sn_day_df.loc[index,'distance']#对行驶里程进行累加
+            sn_day_df.loc[index,'vehodo']=accum_distance
+    #筛选所有的drive信息行
+    sn_day_drive_df=sn_day_df.loc[sn_day_df['drive_flg']==True,:]
+    #按时间进行一次筛选,此处丢弃了晚上0点以后的行车数据
+
+    sn_day_drive_df=sn_day_drive_df.reset_index(drop=True)#重置index
+    
+    return sn_day_drive_df 
+
+
+#更新所有sn,连读多天的的factor
+def updtAllSnFct(start_date,end_date, db_engine, db_local, db_qx, sn_table_name='tb_sn_factor'):
+    '''计算开始时间到结束时间的,所有sn的factor'''
+    start_date_datetime=datetime.datetime.strptime(start_date,'%Y-%m-%d')#开始时间
+    end_date_datetime=datetime.datetime.strptime(end_date,'%Y-%m-%d')#开始时间
+    delta_day=(end_date_datetime-start_date_datetime).days#间隔天数
+    i=1
+    while i<=delta_day:
+        end_date=(start_date_datetime+datetime.timedelta(days=i)).strftime("%Y-%m-%d")
+        updtAllSnTodayFct(start_date,end_date, db_engine, db_local, db_qx, sn_table_name)#调用函数
+        # print('update all sn factor from '+start_date+" to "+end_date)
+        start_date=end_date
+        i+=1#自加
+
+#更新所有sn,一天的factor
+def updtAllSnTodayFct(start_date,end_date, db_engine, db_local, db_qx, sn_table_name):
+    ''''更新今天所有sn的factorx信息,start_date和end_date相隔一天。此处还可优化'''
+    # conn_local = pymysql.connect(
+    #     host='localhost',
+    #     user='root',
+    #     password='pengmin',
+    #     database='qixiangdb',
+    #     charset='utf8'
+    #     )
+    
+    start_date_str="'"+start_date+"'"
+    end_date_str="'"+end_date+"'"
+    sql_cmd="select * from drive_info where time between "+start_date_str+" and "+end_date_str+" and distance!=0;"
+    range_soc_df = pd.read_sql(sql_cmd, db_qx)#使用read_sql方法查询qx数据库
+
+    #筛选出所有当日数据之后,筛选当日有更新的sn
+    today_sn_list=range_soc_df['name'].unique().tolist()#[:100]#先一次更新5个
+    #建立空的dataframe,用于承接所有更新的factor信息
+    today_sn_fct_df=pd.DataFrame([],columns=['sn','date','a0','a1','a2','a3','a4'])
+
+    for sn in today_sn_list:
+        #寻找factor_df,里面是否有sn号,如果没有sn对应信息,则新增信息。
+        sn_str="'"+sn+"'"
+        update_today_factor_flg=True
+        sql_cmd3="select sn,date,a0,a1,a2,a3,a4 from {} where date=".format(sn_table_name)+start_date_str+" and sn="+sn_str
+        factor_today_df=pd.read_sql(sql_cmd3, db_local)#使用read_sql方法查询local数据库
+        if len(factor_today_df)>=1:
+            # print(sn+' '+start_date_str+' factor exist in table! Factor not update.')
+            update_today_factor_flg=False
+
+        sql_cmd2="select sn,date,a0,a1,a2,a3,a4 from {} where date<".format(sn_table_name)+start_date_str+" and sn="+sn_str
+        #此处可以限定每次查询的数量,例如不高于5行
+        factor_df=pd.read_sql(sql_cmd2, db_local)#使用read_sql方法查询local数据库
+
+        #按照sn号和日期进行去重,避免运行时重复产生factor数据,保留第一次出现的行。
+        factor_df=factor_df.drop_duplicates(subset=['sn','date'],keep='first')
+
+        if len(factor_df)==0:
+            #如果没有搜索到factor历史数据,则声明一个新的进行初始化
+            start_date_datetime=datetime.datetime.strptime(start_date,'%Y-%m-%d')
+            yesterday=(start_date_datetime+datetime.timedelta(days=-1)).strftime("%Y-%m-%d")
+            #为sn申请一个新的factor,初始值为1
+            factor_df=pd.DataFrame({'sn':sn,'date':yesterday,'a0':[1],'a1':[1],'a2':[1],'a3':[1],'a4':[1]})
+        sn_factor_df=factor_df.loc[factor_df['sn']==sn,:]#筛选sn对应的factor
+        sn_factor_df=sn_factor_df.sort_values(by='date',ascending='True')#按照日期排序
+
+        sn_factor_df_last=sn_factor_df.tail(1).copy()#寻找最后一行,代表最近日期
+        sn_factor_df_last=sn_factor_df_last.append(sn_factor_df_last)#新增加一行,用于存储新的factor
+        sn_factor_df_last=sn_factor_df_last.reset_index(drop=True)#重置index
+        sn_factor_df_last.loc[1,'date']=start_date#更改后一行的date为当前日期
+        #筛选对应车辆的信息
+        condition_sn=(range_soc_df['name']==sn)
+        sn_day_df=range_soc_df.loc[condition_sn,:].copy()
+        sn_day_df=sn_day_df.reset_index(drop=True)
+        #使用updtTodayFct函数更新今天的factor
+        if len(sn_day_df)>=2:
+            #使用process函数,进行预处理
+            sn_day_df=snDayDfPreProcess(sn_day_df)#预处理函数
+            # 临时措施,删除每天晚上0点以后的数据,5点以前的数据,防止对驾驶cycle判断产生影响。
+            day_start_time=datetime.datetime.strptime(start_date,'%Y-%m-%d')
+            day_morning_time=day_start_time+datetime.timedelta(hours=5)
+            morning_time_str=day_morning_time.strftime('%Y-%m-%d %H:%M:%S')
+            sn_day_df=sn_day_df.loc[sn_day_df['time']>morning_time_str,:]#去除掉了每天晚上0点以后的数据,短期措施
+            sn_day_df=sn_day_df.reset_index(drop=True)#重置index
+
+            if len(sn_day_df)>=2:
+                sn_factor_df_new=updtTodayFct(sn_factor_df_last,sn_day_df)#
+                if (len(sn_factor_df_new)>=2)&(update_today_factor_flg):#如果factor
+                    today_sn_fct_df=today_sn_fct_df.append(sn_factor_df_new.loc[1,:])#筛选第一行,进行拼接,最后写入到数据库中
+    
+    #将today_sn_fct_df写入到数据库中,今天所有factor更新的系数,一次写入。
+    if len(today_sn_fct_df)>=1:
+        today_sn_fct_df.to_sql(sn_table_name,con=db_engine,chunksize=10000,if_exists='append',index=False)
+
+#更新一个sn,连续多天的factor
+def updtOneSnFct(sn,start_date,end_date,db_engine, db_local, db_qx, sn_table_name='tb_sn_factor'):
+    '''计算开始时间到结束时间的,一个sn的所有factor。
+    重复多次调用,updtOneSnTodayFct。
+    '''
+    start_date_datetime=datetime.datetime.strptime(start_date,'%Y-%m-%d')#开始时间
+    end_date_datetime=datetime.datetime.strptime(end_date,'%Y-%m-%d')#开始时间
+    delta_day=(end_date_datetime-start_date_datetime).days#间隔天数
+    i=1
+    while i<=delta_day:
+        end_date=(start_date_datetime+datetime.timedelta(days=i)).strftime("%Y-%m-%d")
+        # print('update one '+sn+'factor from '+start_date+" to "+end_date)
+        updtOneSnTodayFct(sn,start_date,end_date,db_engine, db_local, db_qx, sn_table_name)#调用函数,更新当日的factor。
+        start_date=end_date
+        i+=1#自加
+#更新一个sn,一天的factor
+def updtOneSnTodayFct(sn,start_date,end_date,db_engine, db_local, db_qx, sn_table_name):
+    '''更新一个sn,一天的factor。'''
+    #重新建立连接,更新数据库
+    # conn_local = pymysql.connect(
+    #     host='localhost',
+    #     user='root',
+    #     password='pengmin',
+    #     database='qixiangdb',
+    #     charset='utf8'
+    #     )
+
+    start_date_str="'"+start_date+"'"
+    end_date_str="'"+end_date+"'"
+    sn_str="'"+sn+"'"
+    sql_cmd="select * from drive_info where time between "+start_date_str+" and "+end_date_str+\
+    " and distance!=0 and name="+sn_str
+    range_soc_df = pd.read_sql(sql_cmd, db_qx)#使用read_sql方法查询qx数据库
+
+    if len(range_soc_df)>0:
+        #筛选出所有当日数据之后,筛选当日有更新的sn
+        today_sn_list=range_soc_df['name'].unique().tolist()
+        #建立空的dataframe,用于承接所有更新的factor信息
+        today_sn_fct_df=pd.DataFrame([],columns=['sn','date','a0','a1','a2','a3','a4'])
+
+        for sn in today_sn_list:
+            #寻找factor_df,里面是否有sn号,如果没有sn对应信息,则新增信息。
+            sn_str="'"+sn+"'"
+
+            update_today_factor_flg=True
+            sql_cmd3="select sn,date,a0,a1,a2,a3,a4 from {} where date=".format(sn_table_name)+start_date_str+" and sn="+sn_str
+            factor_today_df=pd.read_sql(sql_cmd3, db_local)#使用read_sql方法查询local数据库
+            if len(factor_today_df)>=1:
+                # print(sn+' '+start_date_str+' factor exist in table! Factor not update.')
+                update_today_factor_flg=False
+
+            sql_cmd2="select sn,date,a0,a1,a2,a3,a4 from {} where date<=".format(sn_table_name)+start_date_str+" and sn="+sn_str
+            factor_df=pd.read_sql(sql_cmd2, db_local)#使用read_sql方法查询local数据库
+            #按照sn号和日期进行去重,避免运行时重复产生factor数据,保留第一次出现的行。
+            factor_df=factor_df.drop_duplicates(subset=['sn','date'],keep='first')
+            # pdb.set_trace()
+            if len(factor_df)==0:
+                #如果没有搜索到factor历史数据,则声明一个新的进行初始化
+                start_date_datetime=datetime.datetime.strptime(start_date,'%Y-%m-%d')
+                yesterday=(start_date_datetime+datetime.timedelta(days=-1)).strftime("%Y-%m-%d")
+                factor_df=pd.DataFrame({'sn':sn,'date':yesterday,'a0':[1],'a1':[1],'a2':[1],'a3':[1],'a4':[1]})
+                today_sn_fct_df=today_sn_fct_df.append(factor_df.loc[0,:])#将初始化的行记录到数据库
+
+            sn_factor_df=factor_df.loc[factor_df['sn']==sn,:]#筛选sn对应的factor
+            sn_factor_df=sn_factor_df.sort_values(by='date',ascending='True')#按照日期排序
+
+            sn_factor_df_last=sn_factor_df.tail(1).copy()#寻找最后一行,代表最近日期
+            sn_factor_df_last=sn_factor_df_last.append(sn_factor_df_last)#新增加一行,用于存储新的factor
+            sn_factor_df_last=sn_factor_df_last.reset_index(drop=True)#重置index
+            sn_factor_df_last.loc[1,'date']=start_date#更改后一行的date为当前日期
+            #筛选对应车辆的信息
+            condition_sn=(range_soc_df['name']==sn)
+            sn_day_df=range_soc_df.loc[condition_sn,:].copy()
+            sn_day_df=sn_day_df.reset_index(drop=True)
+            #使用updtTodayFct函数更新今天的factor
+            if len(sn_day_df)>=2:
+                #使用process函数,进行预处理
+                sn_day_df=snDayDfPreProcess(sn_day_df)#!!!!!!!!!!!增加
+                # 临时措施,删除每天晚上0点以后的数据,5点以前的数据,防止对驾驶cycle判断产生影响。
+                day_start_time=datetime.datetime.strptime(start_date,'%Y-%m-%d')
+                day_morning_time=day_start_time+datetime.timedelta(hours=5)
+                morning_time_str=day_morning_time.strftime('%Y-%m-%d %H:%M:%S')
+                sn_day_df=sn_day_df.loc[sn_day_df['time']>morning_time_str,:]#去除掉了每天晚上0点以后的数据,短期措施
+                sn_day_df=sn_day_df.reset_index(drop=True)#重置index
+
+                if len(sn_day_df)>=2:
+                    sn_factor_df_new=updtTodayFct(sn_factor_df_last,sn_day_df)#更新fator的主函数
+
+                    if (len(sn_factor_df_new)>=2)&(update_today_factor_flg):#如果今日factor没有更新
+                        today_sn_fct_df=today_sn_fct_df.append(sn_factor_df_new.loc[1,:])#筛选第一行,进行拼接,最后写入到数据库中
+        
+        # #将today_sn_fct_df写入到数据库中
+        if len(today_sn_fct_df)>=1:
+            today_sn_fct_df.to_sql(sn_table_name,con=db_engine,chunksize=10000,if_exists='append',index=False)
+            # print(sn+' factor will be update in table tb_sn_factor!')
+        return sn_factor_df_new
+
+
+#更新最新的factor,一天调用一次。
+def updtNewestFctTb(current_time, db_local, sn_table_name='tb_sn_factor'):
+
+    '''更新tb_sn_factor_newest,只保留最新日期的factor。
+    从tb_sn_factor中,筛选最新的日期。
+    函数每天运行一次,从tb_sn_factor中筛选最新日期的factor。'''
+
+    current_time=current_time#当前时间
+    current_time_str=current_time.strftime('%Y-%m-%d %H:%M:%S')#时间格式化为字符串,年-月-日 时-分-秒
+    current_time_str="'"+current_time_str+"'"
+
+    sql_cmd_4="select sn,date,a0,a1,a2,a3,a4 from {} where date<".format(sn_table_name)+current_time_str
+    factor_all_df = pd.read_sql(sql_cmd_4, db_local)#使用read_sql方法查询qx数据库
+    #筛选今天之前的所有factor,只保留最近的一天。
+    sn_list=factor_all_df['sn'].unique().tolist()#筛选sn序列
+    newest_sn_fct_df=pd.DataFrame([],columns=['sn','date','a0','a1','a2','a3','a4'])#声明空df
+
+    for sn in sn_list:
+        condition_sn=(factor_all_df['sn']==sn)
+        factor_pick_df=factor_all_df.loc[condition_sn,:]#按照sn进行筛选
+        factor_pick_df=factor_pick_df.sort_values(by='date')#按照日期排序
+        factor_last_df=factor_pick_df.tail(1)#选择最后日期
+        newest_sn_fct_df=newest_sn_fct_df.append(factor_last_df)#拼接到空df中
+    
+
+    #按照日期排序,只保留最近的一天,输出factor_unique_df,方法为replace。
+    #本函数,每天需要运行一次,用于更新factor。
+    # newest_sn_fct_df.to_sql(sn_newest_table_name,con=db_engine,chunksize=10000,\
+    #     if_exists='replace',index=False)
+    return newest_sn_fct_df
+#使用factor和soc推荐剩余续驶里程
+def calDistFromFct(input_df):
+    '''根据sn-time-soc-a0-a1-a2-a3-a4,使用factor正向计算计算VehElecRng。'''
+    row_df=input_df.copy()
+    soc=row_df['soc']#获取soc
+    factor=[]
+    factor.append(row_df['a4'])#0~20之间的factor
+    factor.append(row_df['a3'])#20~40之间的factor
+    factor.append(row_df['a2'])#40~60之间的factor
+    factor.append(row_df['a1'])#60~80之间的factor
+    factor.append(row_df['a0'])#80~100之间的factor
+
+    gap=20
+    yushu=soc%gap#余数部分
+    zhengshu=soc//gap#整数部分
+    i=0
+    range=0
+    while i<zhengshu:
+        dur_factor=factor[i]#当前权重
+        range+=dur_factor*gap#分段累加里程
+        i=i+1
+    if yushu>0.01:#避免soc=100时报错
+        range=range+yushu*factor[zhengshu]#最后把余项对应的里程加上
+    row_df['vehelecrng']=range#给VehElecRng列赋值
+    return row_df
+#更新当前时间对应的里程,每5min调用一次
+def updtVehElecRng(db_qx, db_local, sn_newest_table_name='tb_sn_factor_newest', input_time='2021-07-29 12:01:00'):
+    '''更新续驶里程,到tb_sn_factor_soc_range。
+    部署时设置每5min更新一次。
+    '''
+    #设置一个时间作为结束时间
+    # current_time=datetime.datetime.now()
+    current_time_raw=input_time#当前时间
+    current_time=datetime.datetime.strptime(current_time_raw,'%Y-%m-%d %H:%M:%S')#字符串转时间
+
+    #结束时间往前4min,59s,作为起始时间
+    before6min_time_str=(current_time+datetime.timedelta(minutes=-4,seconds=-59)).strftime('%Y-%m-%d %H:%M:%S')#6min前
+    before6min_time_str="'"+before6min_time_str+"'"
+    current_time_str=current_time.strftime('%Y-%m-%d %H:%M:%S')#时间格式化为字符串
+    current_time_str="'"+current_time_str+"'"
+
+    #从drive_info里面读取,该时间段内的name,time,soc三列
+    sql_cmd="select name,time,soc from drive_info where time between "+before6min_time_str+" and "+current_time_str
+    # print(sql_cmd)
+    range_soc_df = pd.read_sql(sql_cmd, db_qx)#使用read_sql方法查询qx数据库
+    range_soc_df.rename(columns={'name':'sn'},inplace=True)#将name列重命名为sn列
+
+    #任务2,从tb_sn_factor_newest里面读取最新的factor,获取距离今天最近的一个factor list
+    sql_cmd_1="select sn,a0,a1,a2,a3,a4 from {}".format(sn_newest_table_name)
+    # print(sql_cmd_1)
+    sn_factor_newest_df_raw = pd.read_sql(sql_cmd_1, db_local)#使用read_sql方法查询qx数据库
+
+    #任务3,将range_soc_df和sn_factor_newest_df_raw,双表合并成为一个新表格。
+    sn_soc_factor_df=pd.merge(range_soc_df,sn_factor_newest_df_raw,how='left',on='sn')
+    sn_soc_factor_df.fillna(1,inplace=True)#如果range_soc_df中有sn号,但sn_factor_newest_df_raw中没有。用1填充。
+    # sn_soc_factor_df.head()
+    #填充完成后,sn-time-soc-a0-a1-a2-a3-a4都已经齐全。
+
+    #任务4,调用函数,将VehElecRng计算出来
+    sn_soc_factor_range_df=pd.DataFrame([],columns=['sn','time','soc','a0','a1','a2','a3','a4','vehelecrng'])
+    for index in sn_soc_factor_df.index.tolist():
+        input_df=sn_soc_factor_df.loc[index,:]#挑选
+        sn_soc_factor_range_row=calDistFromFct(input_df)#计算VehElecRng
+        sn_soc_factor_range_df=sn_soc_factor_range_df.append(sn_soc_factor_range_row)#拼接
+
+    ##任务5,将sn_soc_factor_range_df写入到tb_sn_factor_soc_range中,使用替换关系。
+    # sn_soc_factor_range_df.to_sql(range_table_name,con=db_engine,chunksize=10000,\
+    #     if_exists='replace',index=False)    
+    return sn_soc_factor_range_df
+

+ 12 - 0
LIB/MIDDLE/DrivingRange/UpdtFctTable.py

@@ -0,0 +1,12 @@
+import pandas as pd
+import pymysql
+from sqlalchemy import create_engine
+import datetime
+from UpdtFct import *
+
+#调度周期:每天运行一次。
+
+#更新所有sn,连读多日的factor,如果start_date和end_date相隔一天,代表更新start_date的factor。
+start_date="2021-07-23"
+end_date="2021-07-28"
+updtAllSnFct(start_date,end_date)

+ 11 - 0
LIB/MIDDLE/DrivingRange/UpdtFctTableNewest.py

@@ -0,0 +1,11 @@
+import pandas as pd
+import pymysql
+from sqlalchemy import create_engine
+import datetime
+from UpdtFct import *
+
+#调度周期:在UpdtFctTable运行结束之后,运行一次,不需要输入参数。
+
+#更新factor到最新状态,只保留最新的。
+
+updtNewestFctTb()

+ 16 - 0
LIB/MIDDLE/DrivingRange/UpdtVehElecRng.py

@@ -0,0 +1,16 @@
+import pandas as pd
+import pymysql
+from sqlalchemy import create_engine
+import datetime
+from UpdtFct import *
+
+#调度周期:程序每5分钟运行一次
+
+#更新剩余里程,每5min一次,几秒钟运行结束。
+test_time=datetime.datetime.now()#当前系统时间
+input_time=datetime.datetime.strftime(test_time,'%Y-%m-%d %H:%M:%S')
+
+# input_time='2021-07-29 11:59:00'#手动设定一个时间
+
+#函数每5min调度一次,input_time为当前时间,更新tb_sn_factor_soc_range表格
+updtVehElecRng(input_time)

BIN
LIB/MIDDLE/DrivingRange/计算续驶里程程序介绍.docx


+ 90 - 0
LIB/MIDDLE/ExcessTemp/V1_0_0/ExcessTemp.py

@@ -0,0 +1,90 @@
+from LIB.BACKEND import DBManager
+dbManager = DBManager.DBManager()
+import pandas as pd
+import numpy as np
+import datetime
+import seaborn as sns
+import matplotlib.pyplot as plt
+
+now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')   #type: str
+now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')     #type: datetime
+start_time=now_time-datetime.timedelta(minutes=1)
+end_time=str(now_time)
+start_time=str(start_time)
+
+def makedataset(cellname):
+    dataset = pd.DataFrame()
+    for k in range(len(cellname)):
+        sn = cellname[k]
+        datasn = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms'])
+        datasn = datasn['bms']
+        datasn['SN号']=sn
+        if len(datasn)>0:
+            datasn=datasn.iloc[-1]
+            dataset=dataset.append(datasn)
+    return dataset
+
+fileNamesPK504 = pd.read_excel('sn-20210903.xlsx',sheet_name='科易6060')
+fileNamesPK500 = pd.read_excel('sn-20210903.xlsx',sheet_name='科易6040')
+fileNamesPK502 = pd.read_excel('sn-20210903.xlsx',sheet_name='科易4840')
+fileNamesMGML = pd.read_excel('sn-20210903.xlsx',sheet_name='格林美-力信7255')
+fileNamesMGMC = pd.read_excel('sn-20210903.xlsx',sheet_name='格林美-CATL7255')
+fileNamesUDO = pd.read_excel('sn-20210903.xlsx',sheet_name='优旦7255')
+
+dataPK504=makedataset(list(fileNamesPK504['SN号']))
+dataPK500=makedataset(list(fileNamesPK500['SN号']))
+dataPK502=makedataset(list(fileNamesPK502['SN号']))
+dataMGML=makedataset(list(fileNamesMGML['SN号']))
+dataMGMC=makedataset(list(fileNamesMGMC['SN号']))
+dataUDO=makedataset(list(fileNamesUDO['SN号']))
+
+dataPK504=dataPK504[['SN号','单体温度1','单体温度2','单体温度3','单体温度4','其他温度2','其他温度4','其他温度5']]
+dataPK500=dataPK500[['SN号','单体温度1','单体温度2','单体温度3','单体温度4','其他温度2','其他温度3','其他温度4','其他温度5']]
+dataPK502=dataPK502[['SN号','单体温度1','单体温度2','单体温度3','单体温度4','其他温度2','其他温度3','其他温度4','其他温度5']]
+dataMGML=dataMGML[['SN号','单体温度1','单体温度2','单体温度3','单体温度4','其他温度1','其他温度3','其他温度4','其他温度5','其他温度6']]
+dataMGMC=dataMGMC[['SN号','单体温度1','单体温度2']]
+dataUDO=dataUDO[['SN号','单体温度1','单体温度2']]
+
+datatotal1=pd.concat([dataPK504,dataPK500,dataPK502])
+datatotal2=pd.concat([dataMGMC,dataUDO])
+
+def calculavg(datacell):
+    avg_temp=[]
+    max_temp=[]
+    min_temp=[]
+    for i in range(len(datacell)):
+        avg_temp.append(np.mean(datacell.iloc[i,1:]))
+        max_temp.append(max(datacell.iloc[i,1:]))
+        min_temp.append(min(datacell.iloc[i,1:]))
+    datacell['平均温度']=avg_temp
+    datacell['最高温度']=max_temp
+    datacell['最低温度']=min_temp
+    return datacell
+
+datatotal1=calculavg(datatotal1)
+datatotal2=calculavg(datatotal2)
+dataMGML=calculavg(dataMGML)
+datatotal1=datatotal1[datatotal1['最低温度']>-40]
+datatotal2=datatotal2[datatotal2['最低温度']>-40]
+dataMGML=dataMGML[dataMGML['最低温度']>-40]
+datatotal1=datatotal1.reset_index(drop=True)
+datatotal2=datatotal2.reset_index(drop=True)
+dataMGML=dataMGML.reset_index(drop=True)
+
+def boxplot_fill(col,a):
+    # 计算iqr:数据四分之三分位值与四分之一分位值的差
+    iqr = col.quantile(0.75)-col.quantile(0.25)
+    # 根据iqr计算异常值判断阈值
+    u_th = col.quantile(0.75) + a*iqr # 上界
+    l_th = col.quantile(0.25) - a*iqr # 下界
+    # 定义转换函数:如果数字大于上界则用上界值填充,小于下界则用下界值填充。
+    return l_th,u_th
+
+uptemp_out1=list(boxplot_fill(datatotal1['最高温度'],2.5))[1]
+uptemp_out2=list(boxplot_fill(datatotal2['最高温度'],5))[1]
+uptemp_out3=list(boxplot_fill(dataMGML['最高温度'],3.5))[1]
+
+anomalies1 = datatotal1[(datatotal1['最高温度']>uptemp_out1) & (datatotal1['最高温度']<120) & (datatotal1['最高温度']!=datatotal1['单体温度4']) & (datatotal1['单体温度4']<50) & (datatotal1['最高温度']!=datatotal1['其他温度2']) & (datatotal1['其他温度2']<50)]
+anomalies2 = datatotal2[(datatotal2['最高温度']>uptemp_out2) & (datatotal2['最高温度']<120)]
+anomalies3 = dataMGML[(dataMGML['最高温度']>uptemp_out3) & (dataMGML['最高温度']<120)]
+anomalies=pd.concat([anomalies1,anomalies2,anomalies3])

BIN
LIB/MIDDLE/ExcessTemp/V1_0_0/温度过高预警表单.xlsx


+ 396 - 0
LIB/MIDDLE/IndexStaByOneCycle.py

@@ -0,0 +1,396 @@
+<<<<<<< HEAD
+'''
+基于单一状态(一次行车、一次静置、一次充电)的指标统计库
+
+'''
+__author__ = 'lmstack'
+
+import CONFIGURE.PathSetting as PathSetting
+import sys
+sys.path.append(PathSetting.backend_path)
+import datetime
+import Tools
+import pandas as pd
+import numpy as np
+
+class IndexStaByOneCycle():
+    def __init__(self):
+        pass
+
+    def odo_sta(self, odo_array):
+        '''
+        一次行车行驶里程统计
+        ---------输入参数------------
+        odo_array : 一次行车对应的odo数据
+        ---------输出参数------------
+        如果gps 可信,则输出本次行车的累积行驶里程;
+        否则,输出None
+        '''
+        odo_array = odo_array[~pd.isnull(odo_array)]
+        if len(odo_array) > 0:
+            return np.sum(odo_array)
+        else:
+            return None
+
+    def capacity_sta(self, cap, soc_array, soh_array):
+        '''
+        一个cycle净累积ah统计
+        ---------输入参数------------
+        cap : 标称容量
+        soc_array : 一个cycle对应的soc数据
+        soh_array : 一个cycle对应的soh数据
+        ---------输出参数------------
+        本次行车的累积ah
+        '''
+        soc_array = soc_array[~pd.isnull(soc_array)]
+        soh_array = soh_array[~pd.isnull(soh_array)]
+        if len(soc_array) > 0 and len(soh_array) > 0 :
+            return (soc_array[0] - soc_array[-1]) * np.mean(soh_array) * cap / 100.0 / 100.0
+        else:
+            return None
+    def energy_sta(self, cap, soc_array, soh_array, volt_array):
+        '''
+        一个cycle净累积能量统计
+        ---------输入参数------------
+        cap : 标称容量
+        soc_array : 一个cycle对应的soc数据
+        soh_array : 一个cycle对应的soh数据
+        volt_array : 一个cycle对应的volt数据
+        ---------输出参数------------
+        本次行车的累积能量
+        '''
+        soc_array = soc_array[~pd.isnull(soc_array)]
+        soh_array = soh_array[~pd.isnull(soh_array)]
+        volt_array = volt_array[~pd.isnull(volt_array)]
+        if len(soc_array) > 0 and len(soh_array) > 0 and len(volt_array)>0:
+            cap = self.capacity_sta(cap, soc_array, soh_array)
+            return cap * np.mean(volt_array) / 1000.0
+        else:
+            return None
+
+
+
+    def acc_time_sta(self, time_array):
+        '''
+        一个cycle的持续时间
+        ---------输入参数------------
+        time_array : 一次cycle对应的time数据
+        ---------输出参数------------
+        本cycle的持续时间,单位 h
+        '''
+        time_array = time_array[~pd.isnull(time_array)]
+        if len(time_array) > 0:
+            return float(abs(time_array[0] - time_array[-1]))/1e9/3600.0
+        else:
+            return None
+
+    def mean_temp_sta(self, temp_array):
+        '''
+        一个cycle的单体平均温度
+        ---------输入参数------------
+        temp_array : 一个cycle对应的某个单体的temp数据
+        ---------输出参数------------
+        本cycle的单体平均温度
+        '''
+        temp_array = temp_array[~pd.isnull(temp_array)]
+        if len(temp_array) > 0:
+            return np.mean(temp_array)
+        else:
+            return None
+
+    def temp_change_rate_sta(self, time_array, temp_array):
+        '''
+        一个cycle的单体温度变化率
+        ---------输入参数------------
+        time_array : 一个cycle对应的time数据
+        temp_array : 一个cycle对应的temp数据
+        ---------输出参数------------
+        本cycle的单体温度变化率
+        '''
+        time_array = time_array[~pd.isnull(time_array)]
+        temp_array = temp_array[~pd.isnull(temp_array)]
+        if len(temp_array) > 0 and len(time_array) > 0:
+            return abs(temp_array[0] - temp_array[-1])/\
+                (float(abs(time_array[0] - time_array[-1]))/1e9/3600.0)
+        else:
+            return None
+
+    def dischrg_max_pwr_sta(self, volt_array, crnt_array):
+        '''
+        一个cycle的放电功率最大值
+        ---------输入参数------------
+        volt_array : 一个cycle对应的volt数据
+        crnt_array : 一个cycle对应的crnt数据
+        ---------输出参数------------
+        本cycle的放电功率最大值
+        '''
+        volt_array = volt_array[~pd.isnull(volt_array)]
+        crnt_array = crnt_array[~pd.isnull(crnt_array)]
+        if len(volt_array) > 0 and len(crnt_array) > 0:
+            pwr = volt_array * crnt_array / 1000.0
+            pwr = pwr[pwr > 0]
+            return np.max(pwr)
+        else:
+            return None
+
+    def chrg_max_pwr_sta(self, volt_array, crnt_array):
+        '''
+        一个cycle的充电功率最大值
+        ---------输入参数------------
+        volt_array : 一个cycle对应的volt数据
+        crnt_array : 一个cycle对应的crnt数据
+        ---------输出参数------------
+        本cycle的充电功率最大值
+        '''
+        volt_array = volt_array[~pd.isnull(volt_array)]
+        crnt_array = crnt_array[~pd.isnull(crnt_array)]
+        if len(volt_array) > 0 and len(crnt_array) > 0:
+            pwr = volt_array * crnt_array / 1000.0
+            pwr = pwr[pwr < 0]
+            return np.max(abs(pwr))
+        else:
+            return None
+
+    def regen_count_sta(self, crnt_array):
+        '''
+        一个行车cycle的regen 行数
+        ---------输入参数------------
+        crnt_array : 一个行车cycle对应的crnt数据
+        ---------输出参数------------
+        本行车cycle的regen行数, 总行数
+        '''
+        crnt_array = crnt_array[~pd.isnull(crnt_array)]
+        if  len(crnt_array) > 0:
+            return len(crnt_array[crnt_array < -1]), len(crnt_array)
+        else:
+            return None, None
+
+    def speed_sta(self, sum_odo, acc_time, speed_array):
+        '''
+        一个行车cycle的平均速度和最大瞬时速度
+        ---------输入参数------------
+        sum_odo : 一个行车cycle对应的累积odo数据
+        acc_time : 一个行车cycle对应的累积time数据
+        speed_array : 一个行车cycle对应的speed数据
+        ---------输出参数------------
+        本行车cycle的平均速度和最大速度
+        '''
+        speed_array = speed_array[~pd.isnull(speed_array)]
+        if  ~pd.isnull(sum_odo) and ~pd.isnull(acc_time) and len(speed_array) > 0:
+            return sum_odo/acc_time, np.max(speed_array)
+        else:
+            return None, None
+
+    def speed_sta(self, sum_odo, acc_time, speed_array):
+        '''
+        一个行车cycle的平均速度和最大瞬时速度
+        ---------输入参数------------
+        sum_odo : 一个行车cycle对应的累积odo数据
+        acc_time : 一个行车cycle对应的累积time数据
+        speed_array : 一个行车cycle对应的speed数据
+        ---------输出参数------------
+        本行车cycle的平均速度和最大速度
+        '''
+        speed_array = speed_array[~pd.isnull(speed_array)]
+        if  ~pd.isnull(sum_odo) and ~pd.isnull(acc_time) and len(speed_array) > 0:
+            return sum_odo/acc_time, np.max(speed_array)
+        else:
+=======
+'''
+基于单一状态(一次行车、一次静置、一次充电)的指标统计库
+
+'''
+__author__ = 'lmstack'
+
+# import CONFIGURE.PathSetting as PathSetting
+# import sys
+# sys.path.append(PathSetting.backend_path)
+# import datetime
+# import Tools
+import pandas as pd
+import numpy as np
+
+class IndexStaByOneCycle():
+    def __init__(self):
+        pass
+
+    def odo_sta(self, odo_array):
+        '''
+        一次行车行驶里程统计
+        ---------输入参数------------
+        odo_array : 一次行车对应的odo数据
+        ---------输出参数------------
+        如果gps 可信,则输出本次行车的累积行驶里程;
+        否则,输出None
+        '''
+        odo_array = odo_array[~pd.isnull(odo_array)]
+        if len(odo_array) > 0:
+            return np.sum(odo_array)
+        else:
+            return None
+
+    def capacity_sta(self, cap, soc_array, soh_array):
+        '''
+        一个cycle净累积ah统计
+        ---------输入参数------------
+        cap : 标称容量
+        soc_array : 一个cycle对应的soc数据
+        soh_array : 一个cycle对应的soh数据
+        ---------输出参数------------
+        本次行车的累积ah
+        '''
+        soc_array = soc_array[~pd.isnull(soc_array)]
+        soh_array = soh_array[~pd.isnull(soh_array)]
+        if len(soc_array) > 0 and len(soh_array) > 0 :
+            return (soc_array[0] - soc_array[-1]) * np.mean(soh_array) * cap / 100.0 / 100.0
+        else:
+            return None
+    def energy_sta(self, cap, soc_array, soh_array, volt_array):
+        '''
+        一个cycle净累积能量统计
+        ---------输入参数------------
+        cap : 标称容量
+        soc_array : 一个cycle对应的soc数据
+        soh_array : 一个cycle对应的soh数据
+        volt_array : 一个cycle对应的volt数据
+        ---------输出参数------------
+        本次行车的累积能量
+        '''
+        soc_array = soc_array[~pd.isnull(soc_array)]
+        soh_array = soh_array[~pd.isnull(soh_array)]
+        volt_array = volt_array[~pd.isnull(volt_array)]
+        if len(soc_array) > 0 and len(soh_array) > 0 and len(volt_array)>0:
+            cap = self.capacity_sta(cap, soc_array, soh_array)
+            return cap * np.mean(volt_array) / 1000.0
+        else:
+            return None
+
+
+
+    def acc_time_sta(self, time_array):
+        '''
+        一个cycle的持续时间
+        ---------输入参数------------
+        time_array : 一次cycle对应的time数据
+        ---------输出参数------------
+        本cycle的持续时间,单位 h
+        '''
+        time_array = time_array[~pd.isnull(time_array)]
+        if len(time_array) > 0:
+            return float(abs(time_array[0] - time_array[-1]))/1e9/3600.0
+        else:
+            return None
+
+    def mean_temp_sta(self, temp_array):
+        '''
+        一个cycle的单体平均温度
+        ---------输入参数------------
+        temp_array : 一个cycle对应的某个单体的temp数据
+        ---------输出参数------------
+        本cycle的单体平均温度
+        '''
+        temp_array = temp_array[~pd.isnull(temp_array)]
+        if len(temp_array) > 0:
+            return np.mean(temp_array)
+        else:
+            return None
+
+    def temp_change_rate_sta(self, time_array, temp_array):
+        '''
+        一个cycle的单体温度变化率
+        ---------输入参数------------
+        time_array : 一个cycle对应的time数据
+        temp_array : 一个cycle对应的temp数据
+        ---------输出参数------------
+        本cycle的单体温度变化率
+        '''
+        time_array = time_array[~pd.isnull(time_array)]
+        temp_array = temp_array[~pd.isnull(temp_array)]
+        if len(temp_array) > 0 and len(time_array) > 0:
+            return abs(temp_array[0] - temp_array[-1])/\
+                (float(abs(time_array[0] - time_array[-1]))/1e9/3600.0)
+        else:
+            return None
+
+    def dischrg_max_pwr_sta(self, volt_array, crnt_array):
+        '''
+        一个cycle的放电功率最大值
+        ---------输入参数------------
+        volt_array : 一个cycle对应的volt数据
+        crnt_array : 一个cycle对应的crnt数据
+        ---------输出参数------------
+        本cycle的放电功率最大值
+        '''
+        volt_array = volt_array[~pd.isnull(volt_array)]
+        crnt_array = crnt_array[~pd.isnull(crnt_array)]
+        if len(volt_array) > 0 and len(crnt_array) > 0:
+            pwr = volt_array * crnt_array / 1000.0
+            pwr = pwr[pwr > 0]
+            return np.max(pwr)
+        else:
+            return None
+
+    def chrg_max_pwr_sta(self, volt_array, crnt_array):
+        '''
+        一个cycle的充电功率最大值
+        ---------输入参数------------
+        volt_array : 一个cycle对应的volt数据
+        crnt_array : 一个cycle对应的crnt数据
+        ---------输出参数------------
+        本cycle的充电功率最大值
+        '''
+        volt_array = volt_array[~pd.isnull(volt_array)]
+        crnt_array = crnt_array[~pd.isnull(crnt_array)]
+        if len(volt_array) > 0 and len(crnt_array) > 0:
+            pwr = volt_array * crnt_array / 1000.0
+            pwr = pwr[pwr < 0]
+            return np.max(abs(pwr))
+        else:
+            return None
+
+    def regen_count_sta(self, crnt_array):
+        '''
+        一个行车cycle的regen 行数
+        ---------输入参数------------
+        crnt_array : 一个行车cycle对应的crnt数据
+        ---------输出参数------------
+        本行车cycle的regen行数, 总行数
+        '''
+        crnt_array = crnt_array[~pd.isnull(crnt_array)]
+        if  len(crnt_array) > 0:
+            return len(crnt_array[crnt_array < -1]), len(crnt_array)
+        else:
+            return None, None
+
+    def speed_sta(self, sum_odo, acc_time, speed_array):
+        '''
+        一个行车cycle的平均速度和最大瞬时速度
+        ---------输入参数------------
+        sum_odo : 一个行车cycle对应的累积odo数据
+        acc_time : 一个行车cycle对应的累积time数据
+        speed_array : 一个行车cycle对应的speed数据
+        ---------输出参数------------
+        本行车cycle的平均速度和最大速度
+        '''
+        speed_array = speed_array[~pd.isnull(speed_array)]
+        if  ~pd.isnull(sum_odo) and ~pd.isnull(acc_time) and len(speed_array) > 0:
+            return sum_odo/acc_time, np.max(speed_array)
+        else:
+            return None, None
+
+    def speed_sta(self, sum_odo, acc_time, speed_array):
+        '''
+        一个行车cycle的平均速度和最大瞬时速度
+        ---------输入参数------------
+        sum_odo : 一个行车cycle对应的累积odo数据
+        acc_time : 一个行车cycle对应的累积time数据
+        speed_array : 一个行车cycle对应的speed数据
+        ---------输出参数------------
+        本行车cycle的平均速度和最大速度
+        '''
+        speed_array = speed_array[~pd.isnull(speed_array)]
+        if  ~pd.isnull(sum_odo) and ~pd.isnull(acc_time) and len(speed_array) > 0:
+            return sum_odo/acc_time, np.max(speed_array)
+        else:
+>>>>>>> 65a87ae16013552e359df047df19f46fc4e6eb08
+            return None, None

+ 346 - 0
LIB/MIDDLE/IndexStaByPeriod.py

@@ -0,0 +1,346 @@
+<<<<<<< HEAD
+'''
+基于某个周期(一天,一周...)的指标统计库
+
+'''
+__author__ = 'lmstack'
+
+import CONFIGURE.PathSetting as PathSetting
+import sys
+sys.path.append(PathSetting.backend_path)
+sys.path.append(PathSetting.middle_path)
+import datetime
+import Tools
+import pandas as pd
+import numpy as np
+import IndexStaByOneCycle
+
+class IndexStaByPeriod():
+    def __init__(self):
+        self.indexStaByOneCycle = IndexStaByOneCycle.IndexStaByOneCycle()
+        pass
+
+    def drive_odo_sta(self, df_bms, df_gps):
+        '''
+        计算周期内行车累积行驶里程
+        ---------输入参数------------
+        df_bms : 一段周期内的预处理后的bms数据
+        df_gps : 一段周期内的预处理后的gps数据
+        ---------输出参数------------
+        sum_odo : 累积里程, 如果该周期内gps均无效,则返回None
+        invalid_rate : 该周期内gps无效的bms数据行所占比例
+        '''
+        invalid_count = 0
+        total_count = 0
+        sum_odo = 0
+        data_number_list = sorted(list(set(df_bms[(df_bms['data_status'].isin(['drive']))]['data_split_by_status_after_combine'])))
+        if len(data_number_list) == 0:
+            return {'sum_odo':0, 'invalid_rate':0}
+        for data_number in data_number_list[:]:
+            df_sel_bms = df_bms[df_bms['data_split_by_status_after_combine'] == data_number]
+            df_sel_bms = df_sel_bms.reset_index(drop=True)
+
+            total_count += len(df_sel_bms)
+            if df_sel_bms.loc[0, 'gps_rely'] != 1:
+                invalid_count += len(df_sel_bms)
+                continue
+            else:
+                df_sel_gps = df_gps[(df_gps['时间戳']>df_sel_bms.loc[0,'时间戳']) & (df_gps['时间戳']<df_sel_bms.loc[len(df_sel_bms)-1,'时间戳'])]
+                df_sel_gps = df_sel_gps.reset_index(drop=True)
+                odo = self.indexStaByOneCycle.odo_sta(np.array(df_sel_gps['odo']))
+                if not pd.isnull(odo):
+                    sum_odo += odo
+        invalid_rate = invalid_count/total_count
+        return {'sum_odo':sum_odo, 'invalid_rate':invalid_rate}
+    
+    
+    #该函数未完成, TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+    def _energy_consump_sta(self, cap, df_bms, df_gps):
+        '''
+        计算周期内百公里能耗
+        ---------输入参数------------
+        df_bms : 本周期内的bms数据
+        df_gps : 本周期内的gps数据
+        ---------输出参数------------
+        本周期内的百公里能耗
+        '''
+        if not df_bms.empty and not df_gps.empty:
+            # 计算能耗
+            energy_sum = 0
+            data_number_list = sorted(list(set(df_bms[(df_bms['data_status'].isin(['drive']))]['data_split_by_status_time'])))
+            for data_number in data_number_list[:]:
+                df_sel_bms = df_bms[df_bms['data_split_by_status_time'] == data_number]
+                df_sel_bms = df_sel_bms.reset_index(drop=True)
+                soc_array = np.array(df_sel_bms['SOC[%]'])
+                soh_array = np.array(df_sel_bms['SOH[%]'])
+                volt_array = np.array(df_sel_bms['总电压[V]'])
+                energy = self.indexStaByOneCycle.energy_sta(cap, soc_array, soh_array, volt_array)
+                if not pd.isnull(energy):
+                    energy_sum += energy
+            # 计算里程
+            pass # TODO!!!!!!!!!!!!!!!!!!!!!
+            return 0
+        else:
+            return None
+
+    def drive_soc_sta(self, df_bms):
+        '''
+        计算周期内行车净累积soc
+        ---------输入参数------------
+        cap : 标称容量
+        df_bms : 一段周期内的预处理后的bms数据
+        df_gps : 一段周期内的预处理后的gps数据
+        ---------输出参数------------
+        sum_ah : 本周期的净累积soc
+       '''   
+
+        sum_soc = 0
+        data_number_list = sorted(list(set(df_bms[(df_bms['data_status'].isin(['drive']))]['data_split_by_status_time'])))
+        if len(data_number_list) == 0:
+            return sum_soc
+        for data_number in data_number_list[:]:
+            df_sel_bms = df_bms[df_bms['data_split_by_status_time'] == data_number]
+            df_sel_bms = df_sel_bms.reset_index(drop=True)
+            sum_soc += abs(df_sel_bms.loc[0, 'SOC[%]'] - df_sel_bms.loc[len(df_sel_bms)-1, 'SOC[%]'])
+        return sum_soc
+
+    def drive_time_sta(self, df_bms):
+        '''
+        计算周期内累计行车时长/h
+        ---------输入参数------------
+        cap : 标称容量
+        df_bms : 一段周期内的预处理后的bms数据
+        df_gps : 一段周期内的预处理后的gps数据
+        ---------输出参数------------
+        sum_ah : 本周期的累计行车时长
+       '''   
+
+        sum_time = 0
+        data_number_list = sorted(list(set(df_bms[(df_bms['data_status'].isin(['drive']))]['data_split_by_status_time'])))
+        if len(data_number_list) == 0:
+            return sum_time
+        for data_number in data_number_list[:]:
+            df_sel_bms = df_bms[df_bms['data_split_by_status_time'] == data_number]
+            df_sel_bms = df_sel_bms.reset_index(drop=True)
+            sum_time += (df_sel_bms.loc[len(df_sel_bms)-1, '时间戳'] - df_sel_bms.loc[0, '时间戳']).total_seconds()
+        return sum_time / 3600.0
+
+    def drive_capacity_sta(self, cap, df_bms):
+        '''
+        计算周期内行车净累积ah
+        ---------输入参数------------
+        cap : 标称容量
+        df_bms : 一段周期内的预处理后的bms数据
+        df_gps : 一段周期内的预处理后的gps数据
+        ---------输出参数------------
+        sum_ah : 本周期的净累积ah
+       '''   
+
+        sum_ah = 0
+        data_number_list = sorted(list(set(df_bms[(df_bms['data_status'].isin(['drive']))]['data_split_by_status_time'])))
+        if len(data_number_list) == 0:
+            return sum_ah
+        for data_number in data_number_list[:]:
+            df_sel_bms = df_bms[df_bms['data_split_by_status_time'] == data_number]
+            df_sel_bms = df_sel_bms.reset_index(drop=True)
+            soc_array = np.array(df_sel_bms['SOC[%]'])
+            soh_array = np.array(df_sel_bms['SOH[%]'])
+            sum_ah += self.indexStaByOneCycle.capacity_sta(cap, soc_array, soh_array)
+        return sum_ah
+
+    def drive_energy_sta(self, cap, df_bms):
+        '''
+        计算周期内行车净累积能量
+        ---------输入参数------------
+        cap : 标称容量
+        df_bms : 一段周期内的预处理后的bms数据
+        df_gps : 一段周期内的预处理后的gps数据
+        ---------输出参数------------
+        sum_ah : 本周期的净累积能量
+       '''   
+
+        sum_energy = 0
+        data_number_list = sorted(list(set(df_bms[(df_bms['data_status'].isin(['drive']))]['data_split_by_status_time'])))
+        if len(data_number_list) == 0:
+            return sum_energy
+        for data_number in data_number_list[:]:
+            df_sel_bms = df_bms[df_bms['data_split_by_status_time'] == data_number]
+            df_sel_bms = df_sel_bms.reset_index(drop=True)
+            soc_array = np.array(df_sel_bms['SOC[%]'])
+            soh_array = np.array(df_sel_bms['SOH[%]'])
+            volt_array = np.array(df_sel_bms['总电压[V]'])
+            sum_energy += self.indexStaByOneCycle.energy_sta(cap, soc_array, soh_array, volt_array)
+=======
+'''
+基于某个周期(一天,一周...)的指标统计库
+
+'''
+__author__ = 'lmstack'
+
+# import CONFIGURE.PathSetting as PathSetting
+# import sys
+# sys.path.append(PathSetting.backend_path)
+# sys.path.append(PathSetting.middle_path)
+import datetime
+import Tools
+import pandas as pd
+import numpy as np
+from LIB.MIDDLE import IndexStaByOneCycle
+
+class IndexStaByPeriod():
+    def __init__(self):
+        self.indexStaByOneCycle = IndexStaByOneCycle.IndexStaByOneCycle()
+        pass
+
+    def drive_odo_sta(self, df_bms, df_gps):
+        '''
+        计算周期内行车累积行驶里程
+        ---------输入参数------------
+        df_bms : 一段周期内的预处理后的bms数据
+        df_gps : 一段周期内的预处理后的gps数据
+        ---------输出参数------------
+        sum_odo : 累积里程, 如果该周期内gps均无效,则返回None
+        invalid_rate : 该周期内gps无效的bms数据行所占比例
+        '''
+        invalid_count = 0
+        total_count = 0
+        sum_odo = 0
+        data_number_list = sorted(list(set(df_bms[(df_bms['data_status'].isin(['drive']))]['data_split_by_status_after_combine'])))
+        if len(data_number_list) == 0:
+            return {'sum_odo':0, 'invalid_rate':0}
+        for data_number in data_number_list[:]:
+            df_sel_bms = df_bms[df_bms['data_split_by_status_after_combine'] == data_number]
+            df_sel_bms = df_sel_bms.reset_index(drop=True)
+
+            total_count += len(df_sel_bms)
+            if df_sel_bms.loc[0, 'gps_rely'] != 1:
+                invalid_count += len(df_sel_bms)
+                continue
+            else:
+                df_sel_gps = df_gps[(df_gps['时间戳']>df_sel_bms.loc[0,'时间戳']) & (df_gps['时间戳']<df_sel_bms.loc[len(df_sel_bms)-1,'时间戳'])]
+                df_sel_gps = df_sel_gps.reset_index(drop=True)
+                odo = self.indexStaByOneCycle.odo_sta(np.array(df_sel_gps['odo']))
+                if not pd.isnull(odo):
+                    sum_odo += odo
+        invalid_rate = invalid_count/total_count
+        return {'sum_odo':sum_odo, 'invalid_rate':invalid_rate}
+    
+    
+    #该函数未完成, TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+    def _energy_consump_sta(self, cap, df_bms, df_gps):
+        '''
+        计算周期内百公里能耗
+        ---------输入参数------------
+        df_bms : 本周期内的bms数据
+        df_gps : 本周期内的gps数据
+        ---------输出参数------------
+        本周期内的百公里能耗
+        '''
+        if not df_bms.empty and not df_gps.empty:
+            # 计算能耗
+            energy_sum = 0
+            data_number_list = sorted(list(set(df_bms[(df_bms['data_status'].isin(['drive']))]['data_split_by_status_time'])))
+            for data_number in data_number_list[:]:
+                df_sel_bms = df_bms[df_bms['data_split_by_status_time'] == data_number]
+                df_sel_bms = df_sel_bms.reset_index(drop=True)
+                soc_array = np.array(df_sel_bms['SOC[%]'])
+                soh_array = np.array(df_sel_bms['SOH[%]'])
+                volt_array = np.array(df_sel_bms['总电压[V]'])
+                energy = self.indexStaByOneCycle.energy_sta(cap, soc_array, soh_array, volt_array)
+                if not pd.isnull(energy):
+                    energy_sum += energy
+            # 计算里程
+            pass # TODO!!!!!!!!!!!!!!!!!!!!!
+            return 0
+        else:
+            return None
+
+    def drive_soc_sta(self, df_bms):
+        '''
+        计算周期内行车净累积soc
+        ---------输入参数------------
+        cap : 标称容量
+        df_bms : 一段周期内的预处理后的bms数据
+        df_gps : 一段周期内的预处理后的gps数据
+        ---------输出参数------------
+        sum_ah : 本周期的净累积soc
+       '''   
+
+        sum_soc = 0
+        data_number_list = sorted(list(set(df_bms[(df_bms['data_status'].isin(['drive']))]['data_split_by_status_time'])))
+        if len(data_number_list) == 0:
+            return sum_soc
+        for data_number in data_number_list[:]:
+            df_sel_bms = df_bms[df_bms['data_split_by_status_time'] == data_number]
+            df_sel_bms = df_sel_bms.reset_index(drop=True)
+            sum_soc += abs(df_sel_bms.loc[0, 'SOC[%]'] - df_sel_bms.loc[len(df_sel_bms)-1, 'SOC[%]'])
+        return sum_soc
+
+    def drive_time_sta(self, df_bms):
+        '''
+        计算周期内累计行车时长/h
+        ---------输入参数------------
+        cap : 标称容量
+        df_bms : 一段周期内的预处理后的bms数据
+        df_gps : 一段周期内的预处理后的gps数据
+        ---------输出参数------------
+        sum_ah : 本周期的累计行车时长
+       '''   
+
+        sum_time = 0
+        data_number_list = sorted(list(set(df_bms[(df_bms['data_status'].isin(['drive']))]['data_split_by_status_time'])))
+        if len(data_number_list) == 0:
+            return sum_time
+        for data_number in data_number_list[:]:
+            df_sel_bms = df_bms[df_bms['data_split_by_status_time'] == data_number]
+            df_sel_bms = df_sel_bms.reset_index(drop=True)
+            sum_time += (df_sel_bms.loc[len(df_sel_bms)-1, '时间戳'] - df_sel_bms.loc[0, '时间戳']).total_seconds()
+        return sum_time / 3600.0
+
+    def drive_capacity_sta(self, cap, df_bms):
+        '''
+        计算周期内行车净累积ah
+        ---------输入参数------------
+        cap : 标称容量
+        df_bms : 一段周期内的预处理后的bms数据
+        df_gps : 一段周期内的预处理后的gps数据
+        ---------输出参数------------
+        sum_ah : 本周期的净累积ah
+       '''   
+
+        sum_ah = 0
+        data_number_list = sorted(list(set(df_bms[(df_bms['data_status'].isin(['drive']))]['data_split_by_status_time'])))
+        if len(data_number_list) == 0:
+            return sum_ah
+        for data_number in data_number_list[:]:
+            df_sel_bms = df_bms[df_bms['data_split_by_status_time'] == data_number]
+            df_sel_bms = df_sel_bms.reset_index(drop=True)
+            soc_array = np.array(df_sel_bms['SOC[%]'])
+            soh_array = np.array(df_sel_bms['SOH[%]'])
+            sum_ah += self.indexStaByOneCycle.capacity_sta(cap, soc_array, soh_array)
+        return sum_ah
+
+    def drive_energy_sta(self, cap, df_bms):
+        '''
+        计算周期内行车净累积能量
+        ---------输入参数------------
+        cap : 标称容量
+        df_bms : 一段周期内的预处理后的bms数据
+        df_gps : 一段周期内的预处理后的gps数据
+        ---------输出参数------------
+        sum_ah : 本周期的净累积能量
+       '''   
+
+        sum_energy = 0
+        data_number_list = sorted(list(set(df_bms[(df_bms['data_status'].isin(['drive']))]['data_split_by_status_time'])))
+        if len(data_number_list) == 0:
+            return sum_energy
+        for data_number in data_number_list[:]:
+            df_sel_bms = df_bms[df_bms['data_split_by_status_time'] == data_number]
+            df_sel_bms = df_sel_bms.reset_index(drop=True)
+            soc_array = np.array(df_sel_bms['SOC[%]'])
+            soh_array = np.array(df_sel_bms['SOH[%]'])
+            volt_array = np.array(df_sel_bms['总电压[V]'])
+            sum_energy += self.indexStaByOneCycle.energy_sta(cap, soc_array, soh_array, volt_array)
+>>>>>>> 65a87ae16013552e359df047df19f46fc4e6eb08
+        return sum_energy

+ 75 - 0
LIB/MIDDLE/OutlierDetection/V_1_0_2/anomalyPCA.py

@@ -0,0 +1,75 @@
+import pandas as pd
+import numpy as np
+from scipy.signal import savgol_filter
+from sklearn.preprocessing import RobustScaler
+from sklearn.decomposition import PCA
+import matplotlib.pyplot as plt
+
+def makedataset(df_data):
+    df_data=df_data.drop(['Unnamed: 0','总电流[A]','GSM信号','外电压','单体压差','SOH[%]','开关状态','充电状态','故障等级','故障代码','绝缘电阻','上锁状态','加热状态','单体均衡状态','总输出状态'],axis=1,errors='ignore')
+    for i in range(1,21):
+        df_data=df_data[(df_data['单体电压'+str(i)]>2200) & (df_data['单体电压'+str(i)]<4800)]
+    df_data=df_data[df_data['SOC[%]']>12]
+    df_data['时间']=[df_data.loc[i,'时间戳'][0:15] for i in df_data.index]
+    df_data=df_data.groupby('时间').mean()
+    for k in df_data.columns:
+        df_data[k]=savgol_filter(df_data[k],3,2)
+    return df_data
+
+def process(data_set):
+    features=data_set.columns
+    sX=RobustScaler(copy=True)
+    data_set2=data_set.copy()
+    data_set2.loc[:,features]=sX.fit_transform(data_set2[features])
+    return data_set2
+
+def anomalyScores(originalDF,reducedDF):
+    loss=np.sum((np.array(originalDF)-np.array(reducedDF))**2,axis=1)
+    loss=pd.Series(data=loss,index=originalDF.index)
+    loss=(loss-np.min(loss))/(np.max(loss)-np.min(loss))
+    return loss
+
+def anomalyPCA(x_train_pro):
+    n_components=4
+    whiten=True
+    random_state=2
+    pca=PCA(n_components=n_components,whiten=whiten,random_state=random_state)
+    pca.fit(x_train_pro)
+    return pca
+
+def transform(df_data_pro,model,df_data):
+    #降维
+    X_train=model.transform(df_data_pro)
+    X_train=pd.DataFrame(data=X_train,index=df_data_pro.index)
+    #还原
+    X_train_inverse=model.inverse_transform(X_train)
+    X_train_inverse=pd.DataFrame(data=X_train_inverse,index=df_data_pro.index)
+    #异常指数
+    anomalyScoresModel=anomalyScores(df_data_pro,X_train_inverse)
+    anomalyScoresModel=savgol_filter(anomalyScoresModel,15,3)
+    df_data2=df_data.copy()
+    df_data2['anomalyScores_'+str(model)]=anomalyScoresModel
+    return df_data2
+
+def detect_outliers(data,threshold=3):
+    anomaly=data['anomalyScores_PCA(n_components=4, random_state=2, whiten=True)']
+    mean_d=np.mean(anomaly.values)
+    std_d=np.std(anomaly.values)
+    outliers=pd.DataFrame()
+    for k in anomaly.index:
+        z_score= (anomaly[k]-mean_d)/std_d
+        if np.abs(z_score) >threshold:
+            outliers=outliers.append(data[anomaly.values==anomaly[k]])
+    return outliers
+
+def detect_outliers2(data,pred,threshold=3):
+    anomaly=data['anomalyScores_PCA(n_components=4, random_state=2, whiten=True)']
+    anomalypred=pred['anomalyScores_PCA(n_components=4, random_state=2, whiten=True)']
+    mean_d=np.mean(anomaly.values)
+    std_d=np.std(anomaly.values)
+    outliers2=pd.DataFrame()
+    for k in anomalypred.index:
+        z_score= (anomalypred[k]-mean_d)/std_d
+        if np.abs(z_score) >threshold:
+            outliers2=outliers2.append(pred[anomalypred.values==anomalypred[k]])
+    return outliers2

+ 40 - 0
LIB/MIDDLE/OutlierDetection/V_1_0_2/main_anomalyPCA.py

@@ -0,0 +1,40 @@
+from LIB.BACKEND import DBManager
+dbManager = DBManager.DBManager()
+from LIB.MIDDLE.CellStateEstimation.Common import log
+import pandas as pd
+import anomalyPCA
+
+dataSOH = pd.read_excel('sn-20210903.xlsx',sheet_name='格林美-CATL7255')
+fileNames = dataSOH['sn']
+fileNames = list(fileNames)
+l = len(fileNames)
+
+mylog=log.Mylog('log.txt','error')
+mylog.logcfg()
+
+for k in range(l): 
+    try: 
+        sn = fileNames[k]
+        df_data = dbManager.get_data(sn=sn, start_time='2021-06-01 00:00:00', end_time='2021-10-01 00:00:00', data_groups=['bms'])
+        data_train = df_data['bms']
+        df_data = dbManager.get_data(sn=sn, start_time='2021-10-01 00:00:00', end_time='2021-11-01 00:00:00', data_groups=['bms'])
+        data_test = df_data['bms']
+        if len(data_train)>0:
+            x_train=anomalyPCA.makedataset(data_train)  
+            x_train_pro=anomalyPCA.process(x_train) 
+            pca=anomalyPCA.anomalyPCA(x_train_pro) 
+            res=anomalyPCA.transform(x_train_pro,pca,x_train)
+            res.to_csv('res'+sn+'.csv',encoding='gbk')
+        if len(data_test)>0: 
+            x_test=anomalyPCA.makedataset(data_test)  
+            x_test_pro=anomalyPCA.process(x_test) 
+            pred=anomalyPCA.transform(x_test_pro,pca,x_test) 
+            pred.to_csv('pred'+sn+'.csv',encoding='gbk')
+            outliers=anomalyPCA.detect_outliers(pred,threshold=0.95)
+            outliers2=anomalyPCA.detect_outliers2(res,pred,threshold=36)
+            outliers.to_csv('outliers'+sn+'.csv',encoding='gbk')
+            outliers2.to_csv('outliers2'+sn+'.csv',encoding='gbk')
+    except Exception as e:
+        print(repr(e))
+        mylog.logopt(sn,e)
+        pass 

+ 173 - 0
LIB/MIDDLE/OutlierDetection/VoltOutlier/V_1_0_0/sta.py

@@ -0,0 +1,173 @@
+import pandas as pd
+import pdb
+from sklearn.ensemble import IsolationForest
+import numpy as np
+
+# 计算充电过程
+def preprocess(df):
+    # 滤除前后电压存在一增一减的情况(采样异常)
+    pass
+
+# 计算电压的偏离度    
+def cal_volt_uniform(dfin, volt_column, window=10, step=5, threshold=3):
+    
+    df = dfin.copy()
+    time_list = dfin['time'].tolist()
+
+    # 电压滤波
+    df_volt = df[volt_column] 
+    df_volt_rolling = df_volt.rolling(window).mean()[window-1::step]  # 滑动平均值
+    time_list = time_list[window-1::step] 
+
+    # 电压偏离度
+    mean = df_volt_rolling.mean(axis=1)
+    std = df_volt_rolling.std(axis=1)
+    # mean = [np.array(sorted(x)[1:-1]).mean() for x in df_volt_rolling.values]
+    # std = [np.array(sorted(x)[1:-1]).std() for x in df_volt_rolling.values]
+    df_volt_rolling_norm = df_volt_rolling.sub(mean, axis=0).div(std,axis=0)
+    df_volt_rolling_norm = df_volt_rolling_norm.reset_index(drop=True)
+    return df_volt_rolling_norm, time_list
+
+
+# 计算电压变化量的偏离度    
+def cal_voltdiff_uniform(dfin, volt_column, window=10, step=5, window2=10, step2=5,threshold=3):
+    
+    df = dfin.copy()
+    time_list = dfin['time'].tolist()
+
+    # 电压滤波
+    df_volt = df[volt_column] 
+    df_volt_rolling = df_volt.rolling(window).mean()[window-1::step]  # 滑动平均值
+    time_list = time_list[window-1::step] 
+
+    # 计算电压变化量的绝对值(# 计算前后的差值的绝对值,  时间列-1)
+    df_volt_diff = abs(df_volt_rolling.diff()[1:])
+    df_volt_diff = df_volt_diff.reset_index(drop=True)
+    time_list = time_list[1:]
+
+    # 压差归一化(偏离度)
+    # mean = df_volt_diff.mean(axis=1)
+    # std = df_volt_diff.std(axis=1)
+    # df_voltdiff_norm = df_volt_diff.sub(mean, axis=0).div(std,axis=0)
+    df_voltdiff_norm = df_volt_diff.copy()
+
+    # 压差偏离度滑动平均滤波
+    df_voltdiff_rolling = df_voltdiff_norm.rolling(window2).mean()[window2-1::step2]  # 滑动平均值
+    time_list = time_list[window2-1::step2] 
+    mean = df_voltdiff_rolling.mean(axis=1)
+    std = df_voltdiff_rolling.std(axis=1)
+    # mean = [np.array(sorted(x)[1:-1]).mean() for x in df_voltdiff_rolling.values]
+    # std = [np.array(sorted(x)[1:-1]).std() for x in df_voltdiff_rolling.values]
+    df_voltdiff_rolling_norm = df_voltdiff_rolling.sub(mean, axis=0).div(std,axis=0)
+    df_voltdiff_rolling_norm = df_voltdiff_rolling_norm.reset_index(drop=True)
+    return df_voltdiff_rolling_norm, time_list
+
+
+
+    # this_alarm = {}
+    # df_alarm = df_voltdiff_rolling_norm[abs(df_voltdiff_rolling_norm)>threshold].dropna(how='all')
+    # for index in df_alarm.index:
+    #     df_temp = df_alarm.loc[index, :].dropna(how='all', axis=0)
+    #     this_alarm.update({df_cell_volt.loc[index+1, 'date']:[str(df_temp.keys().tolist()), str([round(x, 2) for x in df_temp.values.tolist()])]})
+    # df_alarm1 = pd.DataFrame(this_alarm)
+
+
+    # return pd.DataFrame(df_alarm1.values.T, index=df_alarm1.columns, columns=df_alarm1.index), pd.DataFrame(df_alarm2.values.T, index=df_alarm2.columns, columns=df_alarm2.index)
+
+    # 孤立森林算法
+    def iso_froest(df):
+
+        #1. 生成训练数据
+        rng = np.random.RandomState(42)
+        X = 0.3 * rng.randn(100, 2) #生成100 行,2 列
+        X_train = np.r_[X + 2, X - 2]
+        print(X_train)
+        # 产生一些异常数据
+        X_outliers = rng.uniform(low=-4, high=4, size=(20, 2))
+        iForest= IsolationForest(contamination=0.1)
+        iForest = iForest.fit(X_train)
+        #预测
+        pred = iForest.predict(X_outliers)
+        print(pred)
+        # [-1 1 -1 -1 -1 -1 -1 1 -
+
+# 计算相关系数
+def cal_coff(df):
+    cc_mean = np.mean(df, axis=0)  #axis=0,表示按列求均值 ——— 即第一维
+    cc_std = np.std(df, axis=0)
+    cc_zscore = (df-cc_mean)/cc_std   #标准化
+    cc_zscore = cc_zscore.dropna(axis=0, how='any')
+
+    cc_zscore_corr = cc_zscore.corr(method='spearman')
+    
+    result = []
+    for i in range(len(cc_zscore_corr)):
+        v = abs(np.array((sorted(cc_zscore_corr.iloc[i]))[2:-1])).mean() # 去掉1 和两个最小值后求均值
+        result.append(v)
+    return cc_zscore_corr, result
+
+
+def instorage(sn, df_voltdiff_result, df_volt_result):
+    
+    df_all_result = pd.DataFrame(columns=['sn', 'time', 'cellnum', 'value', 'type'])
+    
+    value_list = []
+    cellnum_list = []
+    time_list = []
+    type_list = []
+    df_result = df_voltdiff_result.copy().drop(columns='time')
+    time_list_1 = df_voltdiff_result['time']
+    df_result = df_result[(df_result>3) | (df_result<-3)].dropna(axis=0, how='all').dropna(axis=1, how='all')
+    for column in df_result.columns:
+        df = df_result[[column]].dropna(axis=0, how='all')
+        value_list.extend(df[column].tolist())
+        cellnum_list.extend([column]*len(df))
+        time_list.extend([time_list_1[x] for x in df.index])
+    length_1 = len(value_list)
+    
+
+
+    df_result = df_volt_result.copy().drop(columns='time')
+    time_list_2 = df_volt_result['time']
+    df_result = df_result[(df_result>3) | (df_result<-3)].dropna(axis=0, how='all').dropna(axis=1, how='all')
+    for column in df_result.columns:
+        df = df_result[[column]].dropna(axis=0, how='all')
+        value_list.extend(df[column].tolist())
+        cellnum_list.extend([column]*len(df))
+        time_list.extend([time_list_2[x] for x in df.index])
+
+    length_2 = len(value_list) - length_1
+    type_list.extend(['电压变化量离群'] * length_1)
+    type_list.extend(['电压离群'] * length_2)
+    df_all_result['sn'] = [sn] * len(value_list)
+    df_all_result['cellnum'] = cellnum_list
+    df_all_result['value'] = value_list
+    df_all_result['time'] = time_list
+    df_all_result['type'] = type_list
+    return df_all_result
+
+# 报警.如果在某个窗口内,有超过ratio个的点,偏离度超过threshold, 则报警
+def alarm(dfin, volt_column, alarm_window=10, alarm_ratio=0.8, alarm_threshold=2.5):
+
+    
+    time_list = dfin['time'].tolist()
+    df_result = dfin[volt_column].copy()
+    alarm_result = pd.DataFrame(columns=['type', 'num', 'alarm_time'])      
+    df_result_1 = df_result.copy()
+    df_result_1[df_result_1<alarm_threshold] = 0
+    df_result_1[df_result_1>alarm_threshold] = 1    
+    df_result_1 = df_result_1.rolling(alarm_window).sum()
+    for column in volt_column:
+        if len(df_result_1[df_result_1[column]>alarm_window * alarm_ratio])>0:
+            alarm_result = alarm_result.append({'type':'1', 'num':column, 'alarm_time':time_list[df_result_1[df_result_1[column]>alarm_window * alarm_ratio].index[0]]}, ignore_index=True)
+
+    # time_list = time_list[window-1::step] 
+
+    df_result_2 = df_result.copy()
+    df_result_2[df_result_2>-alarm_threshold] = 0
+    df_result_2[df_result_2<-alarm_threshold] = 1
+    df_result_2 = df_result_2.rolling(alarm_window).sum()
+    for column in volt_column:
+        if len(df_result_2[df_result_2[column]>alarm_window * alarm_ratio])>0:
+            alarm_result = alarm_result.append({'type':'2', 'num':column, 'alarm_time':time_list[df_result_2[df_result_2[column]>alarm_window * alarm_ratio].index[0]]}, ignore_index=True)
+    return alarm_result

+ 49 - 0
LIB/MIDDLE/OutlierDetection/VoltOutlier/V_1_0_1/anomalySparsePCA.py

@@ -0,0 +1,49 @@
+from sklearn.decomposition import PCA
+import pandas as pd
+from sklearn.preprocessing import StandardScaler
+import numpy as np
+import matplotlib.pyplot as plt
+import seaborn as sns 
+from sklearn.decomposition import SparsePCA
+import datetime
+from scipy.signal import savgol_filter
+
+#异常指数
+def anomalyScores(originalDF,reducedDF):
+    loss=np.sum((np.array(originalDF)-np.array(reducedDF))**2,axis=1)
+    loss=pd.Series(data=loss,index=originalDF.index)
+    loss=(loss-np.min(loss))/(np.max(loss)-np.min(loss))
+    return loss
+
+
+#判定异常
+def anomaly(df_data):
+    #数据预处理
+    df_data=df_data.drop(['Unnamed: 0','GSM信号','外电压','单体压差','SOH[%]','开关状态','充电状态','故障等级','故障代码','绝缘电阻','上锁状态','加热状态','单体均衡状态','总输出状态'],axis=1,errors='ignore')
+    df_data['时间']=[df_data.loc[i,'时间戳'][0:13] for i in range(len(df_data))] 
+    df_data=df_data.groupby('时间').mean()
+    data_set=df_data.drop(['时间戳','时间'],axis=1,errors='ignore')
+    features=data_set.columns
+    sX=StandardScaler(copy=True)
+    data_set.loc[:,features]=sX.fit_transform(data_set[features])
+    #稀疏PCA异常检测
+    n_components=20
+    alpha=0.0001
+    random_state=10001
+    n_jobs=-1
+    #降维
+    sparsePCA=SparsePCA(n_components=n_components,alpha=alpha,random_state=random_state,n_jobs=n_jobs)
+    sparsePCA.fit(data_set.loc[:,:])
+    X_train_sparsePCA=sparsePCA.transform(data_set)
+    X_train_sparsePCA=pd.DataFrame(data=X_train_sparsePCA,index=data_set.index)
+    #还原
+    X_train_sparsePCA_inverse=np.array(X_train_sparsePCA).dot(sparsePCA.components_)+np.array(data_set.mean(axis=0))
+    X_train_sparsePCA_inverse=pd.DataFrame(data=X_train_sparsePCA_inverse,index=data_set.index)
+    #异常指数
+    anomalyScoressparsePCA=anomalyScores(data_set,X_train_sparsePCA_inverse)
+    anomalyScoressparsePCA=savgol_filter(anomalyScoressparsePCA,101,3)
+    df_data['anomalyScoressparsePCA']=anomalyScoressparsePCA
+
+    return df_data
+
+                                                                                                                                                                                                                                                                                                                                                                

+ 33 - 0
LIB/MIDDLE/OutlierDetection/VoltOutlier/V_1_0_1/main_anomaly.py

@@ -0,0 +1,33 @@
+from LIB.MIDDLE.CellStateEstimation.Common import log
+from LIB.BACKEND import DBManager
+dbManager = DBManager.DBManager()
+import pandas as pd
+import anomalySparsePCA
+
+dataSOH = pd.read_excel('sn-20210903.xlsx',sheet_name='格林美-CATL7255')
+fileNames = dataSOH['sn']
+fileNames = list(fileNames)
+l = len(fileNames)
+
+#log信息配置
+mylog=log.Mylog('log.txt','error')
+mylog.logcfg()
+
+data_res=pd.DataFrame()
+for k in range(3):
+    try:
+        sn = fileNames[k]
+        df_data = dbManager.get_data(sn=sn, start_time='2021-01-01 00:00:00', end_time='2021-11-01 00:00:00', data_groups=['bms'])
+        data_bms = df_data['bms']
+
+        #...............训练模型............................................................................
+        if len(data_bms['时间戳'])>0:
+            data_stat=anomalySparsePCA.anomaly(data_bms)
+            data_stat.to_csv('result_'+sn+'.csv',encoding='gbk')
+            data_stat['sn']=sn
+            data_res=data_res.append(data_stat)
+              
+    except Exception as e:
+        print(repr(e))
+        mylog.logopt(sn,e)
+        pass 

BIN
LIB/MIDDLE/OutlierDetection/VoltOutlier/V_1_0_1/输出表单.xlsx


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 881 - 0
LIB/MIDDLE/OutlierDetection/VoltOutlier/main.ipynb


+ 89 - 0
LIB/MIDDLE/RemainChargeTime/V1_0_0/MakeDataSet_History.py

@@ -0,0 +1,89 @@
+#历史剩余充电时间统计 2021-10
+
+from LIB.BACKEND.Tools import Tools
+from LIB.BACKEND.DataPreProcess import DataPreProcess
+import pandas as pd
+import numpy as np
+import os
+import math
+import datetime
+from time import strftime,gmtime
+from LIB.BACKEND import DBManager
+dbManager = DBManager.DBManager()
+
+#将时间戳由 "%Y-%m-%d %H:%M:%S" 切换为 sec
+def timeconvert(df):  
+    df.index=range(len(df))
+    time=df.时间戳
+    timeInSeries=[]
+    time2=datetime.datetime.strptime(time[0],"%Y-%m-%d %H:%M:%S")
+    for k in range(len(time)):
+        time1=datetime.datetime.strptime(time[k],"%Y-%m-%d %H:%M:%S")    
+        t=(time1-time2)
+        timeInSeries.append(t.days*86400+t.seconds)
+    df.loc[:,'相对时间']=pd.DataFrame(timeInSeries,columns=['相对时间'])
+    return df
+
+#构建新数据表统计剩余充电时间
+def data_handle(df_in):
+    #数据预处理
+    df_in=timeconvert(df_in)  
+    dfOut_temp=DataPreProcess.data_split_by_status(DataPreProcess, df_in, drive_interval_threshold=120, charge_interval_threshold=300,drive_stand_threshold=120, charge_stand_threshold=300)
+    dfOut=dfOut_temp.copy()
+    df_charge=dfOut[dfOut['data_status']=='charge']
+    unique_status_idx=np.unique(df_charge.data_split_by_status.values)
+    #计算剩余充电时间
+    df_statisticsInfo=pd.DataFrame()
+    for n in unique_status_idx:
+        df_charge_buff=df_charge[df_charge.data_split_by_status==n]
+        if(np.max(df_charge_buff['SOC[%]'].values[-1])==100):    #满充状态数据提取    
+            times=df_charge_buff['相对时间'].values
+            times1=times[:-1]
+            times2=times[1:]
+            delta=times2-times1
+            if(np.max(delta)<50):
+                times_inv=[times[-1]-t for t in times ]
+                df_charge_buff.loc[:,'剩余充电时间[sec]']=times_inv
+                times_hms=[]
+                for k in list(range(len(times_inv))):
+                    time_hms=strftime("%H:%M:%S", gmtime(df_charge_buff.loc[:,'剩余充电时间[sec]'].values[k]))
+                    times_hms.append(time_hms)
+                df_charge_buff.loc[:,'剩余充电时间[时分秒]']=times_hms
+                df_dataset_temp=df_charge_buff.drop(['GSM信号','故障等级','故障代码','外电压','绝缘电阻','总输出状态','上锁状态','充电状态','加热状态','单体压差','单体均衡状态','相对时间','data_split_by_crnt','data_split_by_status','data_status'],axis=1)
+                df_statisticsInfo=df_statisticsInfo.append(df_dataset_temp)
+                #简化特征
+                list_col=list(df_statisticsInfo)
+                list1=[s for s in list_col if '单体电压' in s]
+                list2=[s for s in list_col if '单体温度' in s]
+                list3=[s for s in list_col if '其他温度' in s]
+                list_v=['单体电压'+str(i) for i in range(1,len(list1)+1)] 
+                list_T=['单体温度'+str(i) for i in range(1,len(list2)+1)] 
+                A_cellVolt=df_statisticsInfo[list_v].values
+                celVolt_max=np.max(A_cellVolt,axis=1)
+                celVolt_min=np.min(A_cellVolt,axis=1)
+                A_cellTemp=df_statisticsInfo[list_T].values
+                celTemp_max=np.max(A_cellTemp,axis=1)
+                celTemp_min=np.min(A_cellTemp,axis=1) 
+                df_statisticsInfo.loc[:,'最高单体电压[V]']=celVolt_max
+                df_statisticsInfo.loc[:,'最高单体温度[℃]']=celTemp_max
+                df_statisticsInfo.loc[:,'最低单体温度[℃]']=celTemp_min
+                df_statisticsInfo=df_statisticsInfo.drop(list_v,axis=1)
+                df_statisticsInfo=df_statisticsInfo.drop(list_T,axis=1) 
+                df_statisticsInfo=df_statisticsInfo.drop(['其他温度'+str(i) for i in range(1,len(list3)+1)],axis=1)
+    return(df_statisticsInfo)
+
+#Dataset
+data_sn=pd.read_excel('sn-20210903.xlsx',sheet_name='sn-20210903')
+fileNames=data_sn['sn']
+df_dataset=pd.DataFrame()
+for k in range(2): 
+    df_data = dbManager.get_data(sn=fileNames[k], start_time='2021-10-01 00:00:00', end_time='2021-11-01 00:00:00', data_groups=['bms'])
+    if len(df_data)==0:
+        continue
+    dataIn = df_data['bms']
+    dataIn=data_handle(dataIn)
+    dataIn['sn']=fileNames[k]
+    df_dataset=df_dataset.append(dataIn)
+
+df_dataset.to_csv('df_datatest.csv')
+

BIN
LIB/MIDDLE/RemainChargeTime/V1_0_0/历史剩余充电时间表单.xlsx


+ 439 - 0
LIB/MIDDLE/SaftyCenter/CellValueDiag/V1_0_0/SC_SamplingSafty.py

@@ -0,0 +1,439 @@
+import sys
+import numpy as np
+import pandas as pd
+import string
+import os
+from pandas import Series
+from matplotlib import pyplot as plt
+from pandas.core.frame import DataFrame
+from pandas.core.indexes.base import Index
+from LIB.BACKEND import DBManager
+import datetime
+import time
+import string
+
+
+class SamplingSafty:
+    def __init__(self):
+        pass
+    def main(sn,param,bms_info,df_Diag_Ram_in):
+        df_Diag_Ram_Update_inside=DataFrame(columns=['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice'])
+        df_Diag_Ram_Update_inside=df_Diag_Ram_Update_inside.append(df_Diag_Ram_in)
+        global QuitErrCount 
+        FltInfo=DataFrame(columns=['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice'])
+        VolStarkCount=[0 for i in range(param.CellVoltNums)]
+        VolCount=[0 for i in range(param.CellVoltNums)]
+        QuitErrCount=[0 for i in range(param.FaultCount)]
+
+        #--------------该电池的所有当前故障-------------
+
+
+
+        for i in range(0,len(bms_info)):
+            if i==0:
+                bms_infoP=bms_info.loc[i]
+            elif len(bms_info)>=1:
+                    bms_infoP=bms_info.loc[i-1]
+            df_Diag_Ram,VolStarkCount,VolCount=SamplingSafty.VoltSamplingDiag(sn,bms_info.loc[i],bms_infoP,param,VolStarkCount,VolCount,df_Diag_Ram_Update_inside)   
+            df_Diag_Ram=SamplingSafty.TempSamplingDiag(sn,bms_info.loc[i],bms_infoP,param,df_Diag_Ram)
+                # FltInfo=SamplingSafty.CrntSamplingDiag(sn,bms_info.loc[i],FltInfo,param)
+        return df_Diag_Ram
+
+
+
+    def VoltSamplingDiag(sn,bms_infoN,bms_infoP,param,VolStarkCount,VolCount,df_Diag_Ram):
+        InVMaxBatNo=[]
+        InVMinBatNo=[]
+        StackVolNo=[]
+        OutlierVolNo=[]
+        maxVolt=0
+        minVolt=5
+        ErrorFlg=0
+        TotalVol=0
+        #——————————————————————取最高最低电压————————————————————————————————
+        for t in range(0,param.CellVoltNums):
+            BatNo=['单体电压'+str(t+1)]
+            maxVol=max(maxVolt,float(bms_infoN[BatNo])/1000)
+            if maxVol>maxVolt:
+                MaxBatNo=t+1
+                maxVolt=maxVol
+            if float(bms_infoN[BatNo])/1000>param.CellOVlmt:
+                InVMaxBatNo.append(t+1)  
+            else:
+                pass
+            minVol=min(minVolt,float(bms_infoN[BatNo])/1000)
+            if minVol<minVolt:
+                MinBatNo=t+1
+                minVolt=minVol
+            if float(bms_infoN[BatNo])/1000<param.CellUVlmt:
+                InVMinBatNo.append(t+1)
+            else:
+                pass
+            TotalVol=TotalVol+float(bms_infoN[BatNo]/1000)
+        AvgVol=(TotalVol-maxVol-minVol)/float(param.CellVoltNums-2)
+
+
+        #—————————————————————————————电压无效和断线判断———————————————————————
+        if maxVol>param.CellOVlmt:
+            if minVol<param.CellUVlmt:
+                QuitErrCount[10]=0
+                if not 10 in df_Diag_Ram['code'].values.tolist():#如果故障发生当前故障中没有该故障,则压入该故障
+                    df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,10,3,'电池电压采样断线,最高电压为{:.2f}V,电池编号为{},最低电压为{:.2f}V,电池编号为{}'.format(maxVol,MaxBatNo,minVol,MinBatNo),'返厂维修']
+                    ErrorFlg=1            
+                else:#如果故障发生当前故障中有该故障,则不进行操作
+                    pass 
+            else:#如果没有故障,并且当前故障表中有该故障,则判断故障是否结束
+                QuitErrCount[50]=0
+                if 10 in df_Diag_Ram['code'].values.tolist():
+                    QuitErrCount[10]=QuitErrCount[10]+1
+                    if QuitErrCount[10]>6:
+                        end_time=datetime.datetime.now()
+                        end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                        df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']==10].index,['end_time']]=end_time
+                    else:
+                        pass
+                else:
+                    pass
+                if not 50 in df_Diag_Ram['code'].values.tolist():
+                    df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,50,3,str(InVMaxBatNo)+'号电压大于{:.2f}V,采样无效'.format(param.CellOVlmt),'返厂维修']
+                    
+                    ErrorFlg=1
+                else:
+                    pass
+        elif minVol<param.CellUVlmt:
+            QuitErrCount[50]=0
+            if not 50 in df_Diag_Ram['code'].values.tolist():
+                df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,50,3,str(InVMinBatNo)+'号电压小于{:.2f}V,采样无效'.format(param.CellUVlmt),'返厂维修']
+                
+                ErrorFlg=1
+            else:
+                pass
+        else:
+            if 50 in df_Diag_Ram['code'].values.tolist():
+                QuitErrCount[50]=QuitErrCount[50]+1
+                if QuitErrCount[50]>6:
+                    end_time=datetime.datetime.now()
+                    end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                    df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']==50].index,['end_time']]=end_time
+                    
+                else:
+                    pass
+            else:
+                pass
+       #——————————————————————————————————电压卡滞判断—————————————————————————
+        if ErrorFlg==0:
+            for t in range(0,param.CellVoltNums):
+                BatNo=['单体电压'+str(t+1)]
+                AvgVolGap=abs((float(bms_infoN[BatNo]/1000))-AvgVol)
+                if AvgVolGap>param.AvgVolGap and float(bms_infoN[BatNo]/1000)>param.BLVol and float(bms_infoN[BatNo]/1000)<param.CellFullChrgVolt and not 51 in df_Diag_Ram['code']:
+                    if abs(bms_infoN['总电流[A]'])<2:
+                        VolCount[t]=VolCount[t]+1
+                    else:
+                        VolCount[t]=0
+                    if VolCount[t]>10:
+                        OutlierVolNo.append(t+1)
+                    else:
+                        pass
+                if abs(float(bms_infoN[BatNo])-float(bms_infoP[BatNo]))<=0.0001 and not 52 in df_Diag_Ram['code']:
+                    if abs(float(bms_infoN['总电流[A]']))>=10 and not 'PK504' in sn:
+                        VolStarkCount[t]=VolStarkCount[t]+1
+                    elif abs(float(bms_infoN['总电流[A]']))>=15 and 'PK504' in sn:
+                        VolStarkCount[t]=VolStarkCount[t]+1
+                else:
+                    VolStarkCount[t]=0
+                if VolStarkCount[t]>10:
+                    StackVolNo.append(t+1)
+                else:
+                    pass
+            if len(OutlierVolNo)>0:
+                QuitErrCount[51]=0
+                if not 51 in df_Diag_Ram['code'].values.tolist():#如果故障发生当前故障中没有该故障,则压入该故障
+                    df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,51,2,str(OutlierVolNo)+'号电池电压离群','技术介入诊断']
+                    
+                    ErrorFlg=1            
+                else:#如果故障发生当前故障中有该故障,则不进行操作
+                    pass
+            else:
+                if 51 in df_Diag_Ram['code'].values.tolist():
+                    QuitErrCount[51]=QuitErrCount[51]+1
+                    if QuitErrCount[51]>6:
+                        end_time=datetime.datetime.now()
+                        end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                        df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']==51].index,['end_time']]=end_time
+                        
+                    else:
+                        pass
+                else:
+                    pass
+            if len(StackVolNo)>0:
+                QuitErrCount[52]=0
+                if not 52 in df_Diag_Ram['code'].values.tolist():#如果故障发生当前故障中没有该故障,则压入该故障
+                    df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,52,2,str(StackVolNo)+'号电池电压卡滞','技术介入诊断']
+                    
+                    ErrorFlg=1            
+                else:#如果故障发生当前故障中有该故障,则不进行操作
+                    pass
+            else:
+                if 51 in df_Diag_Ram['code'].values.tolist():
+                    QuitErrCount[52]=QuitErrCount[52]+1
+                    if QuitErrCount[52]>6:
+                        end_time=datetime.datetime.now()
+                        end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                        df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']==52].index,['end_time']]=end_time
+                        
+                    else:
+                        pass
+                else:
+                    pass
+        return df_Diag_Ram,VolStarkCount,VolCount
+    def TempSamplingDiag(sn,bms_infoN,bms_infoP,param,df_Diag_Ram):
+        #——————————————————————————————————CellTemp————————————————————————————————————
+        InVMaxBatNo=[]
+        InVMinBatNo=[]
+        OutlierTempNo=[]
+        maxCellTempt=-40
+        minCellTempt=55
+        ErrorFlg=0
+        TotalCellTemp=0
+        for t in range(0,param.CellTempNums):
+            BatNo=['单体温度'+str(t+1)]
+            maxCellTemp=max(maxCellTempt,float(bms_infoN[BatNo]))
+            if maxCellTemp>maxCellTempt:
+                MaxBatNo=t+1
+                maxCellTempt=maxCellTemp
+            if float(bms_infoN[BatNo])>param.PackOTlmt:
+                InVMaxBatNo.append(t+1)                   
+            else:
+                pass
+            minCellTemp=min(minCellTempt,float(bms_infoN[BatNo]))
+            if minCellTemp<minCellTempt:
+                MinBatNo=t+1
+                minCellTempt=minCellTemp
+            if float(bms_infoN[BatNo])<param.PackUTlmt:
+                InVMinBatNo.append(t+1)                   
+            else:
+                pass
+            TotalCellTemp=TotalCellTemp+float(bms_infoN[BatNo])
+        if param.CellTempNums>2:
+            AvgCellTemp=(TotalCellTemp-maxCellTemp-minCellTemp)/float(param.CellTempNums-2)
+        else:
+            AvgCellTemp=TotalCellTemp/float(param.CellTempNums)
+#——————————————————————————————————温度无效,离群和断线判断——————————————————————————————————
+        if maxCellTemp>param.PackOTlmt:
+            if minCellTemp<param.PackUTlmt:
+                QuitErrCount[53]=0
+                if not 53 in df_Diag_Ram['code'].values.tolist():#如果故障发生当前故障中没有该故障,则压入该故障
+                    df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,53,3,'电池温度采样断线,最高温度为{}℃,电池编号为{},最低温度为{}℃,电池编号为{}'.format(maxCellTemp,MaxBatNo,minCellTemp,MinBatNo),'返厂维修']
+                    ErrorFlg=1            
+                else:#如果故障发生当前故障中有该故障,则不进行操作
+                    pass 
+            else:#如果没有故障,并且当前故障表中有该故障,则判断故障是否结束
+                QuitErrCount[2]=0
+                if 53 in df_Diag_Ram['code'].values.tolist():
+                    QuitErrCount[53]=QuitErrCount[53]+1
+                    if QuitErrCount[53]>6:
+                        end_time=datetime.datetime.now()
+                        end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                        df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']==53].index,['end_time']]=end_time
+                        
+                    else:
+                        pass
+                else:
+                    pass
+                if not 2 in df_Diag_Ram['code'].values.tolist():
+                    df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,2,3,str(InVMaxBatNo)+'号温度大于{}℃,采样无效'.format(param.PackOTlmt),'返厂维修']
+                    
+                    ErrorFlg=1
+                else:
+                    pass
+        elif minCellTemp<param.PackUTlmt:
+            QuitErrCount[2]=0
+            if not 2 in df_Diag_Ram['code'].values.tolist():
+                df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,2,3,str(InVMinBatNo)+'号温度小于{}℃,采样无效'.format(param.PackUTlmt),'返厂维修']
+                
+                ErrorFlg=1
+            else:
+                pass
+        else:
+            if 2 in df_Diag_Ram['code'].values.tolist():
+                QuitErrCount[2]=QuitErrCount[2]+1
+                if QuitErrCount[2]>6:
+                    end_time=datetime.datetime.now()
+                    end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                    df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']==2].index,['end_time']]=end_time
+                    
+                else:
+                    pass
+            else:
+                pass
+        if ErrorFlg==0:
+            if param.CellTempNums>2:            
+                for t in range(0,param.CellTempNums):
+                    BatNo=['单体温度'+str(t+1)]
+                    AvgCellTempGap=abs((float(bms_infoN[BatNo]))-AvgCellTemp)
+                    if AvgCellTempGap>param.AvgCellTempGap and float(bms_infoN[BatNo])>param.PackUTlmt and float(bms_infoN[BatNo])<param.PackOTlmt and not 8 in df_Diag_Ram['code']:
+                        OutlierTempNo.append(t+1)
+            else:
+                AvgCellTempGap=maxCellTemp-minCellTemp
+                if AvgCellTempGap>param.AvgCellTempGap and minCellTemp>param.PackUTlmt and maxCellTemp<param.PackOTlmt and not 8 in df_Diag_Ram['code']:
+                    OutlierTempNo=[1,2]
+            if len(OutlierTempNo)>0:
+                QuitErrCount[8]=0
+                if not 8 in df_Diag_Ram['code'].values.tolist():#如果故障发生当前故障中没有该故障,则压入该故障
+                    df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,8,2,str(OutlierTempNo)+'号电池异常温度离群','技术介入诊断']
+                    
+                    ErrorFlg=1            
+                else:#如果故障发生当前故障中有该故障,则不进行操作
+                    pass
+            else:
+                if 8 in df_Diag_Ram['code'].values.tolist():
+                    QuitErrCount[8]=QuitErrCount[8]+1
+                    if QuitErrCount[8]>6:
+                        end_time=datetime.datetime.now()
+                        end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                        df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']==8].index,['end_time']]=end_time
+                        
+                        #df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['sn']==sn and df_Diag_Ram['code']==8].index,['end_time']]=end_time
+                    else:
+                        pass
+                else:
+                    pass
+        #——————————————————————————————————OtherTemp————————————————————————————————————
+        InVMaxOtherTempBatNo=[]
+        InVMinOtherTempBatNo=[]
+        OutlierOtherTempNo=[]
+        maxOtherTempt=-40
+        minOtherTempt=91
+        ErrorFlg=0
+        TotalOtherTemp=0
+        MaxOtherBatNo=0
+        MinOtherBatNo=0
+        if param.OtherTempNums>0:
+            for t in range(0,param.OtherTempNums):
+                BatNo=['其他温度'+str(t+1)]
+                maxOtherTemp=max(maxOtherTempt,float(bms_infoN[BatNo]))
+                if maxOtherTemp>maxOtherTempt:
+                    MaxOtherBatNo=t+1
+                    maxOtherTempt=maxOtherTemp
+                if float(bms_infoN[BatNo])>param.OtherOTlmt:
+                    InVMaxOtherTempBatNo.append(t+1)                   
+                else:
+                    pass
+                minOtherTemp=min(minOtherTempt,float(bms_infoN[BatNo]))
+                if minOtherTemp<minOtherTempt:
+                    MinOtherBatNo=t+1
+                    minOtherTempt=minOtherTemp
+                if float(bms_infoN[BatNo])<param.OtherUTlmt:
+                    InVMinOtherTempBatNo.append(t+1)                   
+                else:
+                    pass
+                TotalOtherTemp=TotalOtherTemp+float(bms_infoN[BatNo])
+            if param.OtherTempNums>2:
+                AvgOtherTemp=(TotalOtherTemp-maxOtherTemp-minOtherTemp)/float(param.OtherTempNums-2)
+            else:
+                AvgOtherTemp=TotalOtherTemp/float(param.OtherTempNums)
+    #——————————————————————————————————其他温度无效,离群和断线判断——————————————————————————————————
+            if maxOtherTemp>param.OtherOTlmt:
+                if minOtherTemp<param.OtherUTlmt:
+                    QuitErrCount[54]=0
+                    if not 54 in df_Diag_Ram['code'].values.tolist():#如果故障发生当前故障中没有该故障,则压入该故障
+                        df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,54,3,'其他温度采样断线,最高温度为{}℃,传感器编号为{},最低温度为{}℃,传感器编号为{}'.format(maxOtherTemp,MaxOtherBatNo,minOtherTemp,MinOtherBatNo),'返厂维修']
+                        
+                        ErrorFlg=1            
+                    else:#如果故障发生当前故障中有该故障,则不进行操作
+                        pass 
+                else:#如果没有故障,并且当前故障表中有该故障,则判断故障是否结束
+                    QuitErrCount[55]=0
+                    if 54 in df_Diag_Ram['code'].values.tolist():
+                        QuitErrCount[54]=QuitErrCount[54]+1
+                        if QuitErrCount[54]>6:
+                            end_time=datetime.datetime.now()
+                            end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                            df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']==54].index,['end_time']]=end_time
+                           
+                        else:
+                            pass
+                    else:
+                        pass
+                    if not 55 in df_Diag_Ram['code'].values.tolist():
+                        df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,55,3,str(InVMaxOtherTempBatNo)+'号传感器温度大于{}℃,采样无效'.format(param.OtherOTlmt),'联系用户核实电池情况,并返厂维修']
+                        
+                        ErrorFlg=1
+                    else:
+                        pass
+            elif minOtherTemp<param.OtherUTlmt:
+                QuitErrCount[55]=0
+                if not 55 in df_Diag_Ram['code'].values.tolist():
+                    df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,55,3,str(InVMinOtherTempBatNo)+'号传感器温度小于{}℃,采样无效'.format(param.OtherUTlmt),'返厂维修']
+                    
+                    ErrorFlg=1
+                else:
+                    pass
+            else:
+                if 55 in df_Diag_Ram['code'].values.tolist():
+                    QuitErrCount[55]=QuitErrCount[55]+1
+                    if QuitErrCount[55]>6:
+                        end_time=datetime.datetime.now()
+                        end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                        df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']==55].index,['end_time']]=end_time
+                        
+                    else:
+                        pass
+                else:
+                    pass
+            if ErrorFlg==0 and param.OtherTempNums>0:
+                if param.OtherTempNums>2:            
+                    for t in range(0,param.OtherTempNums):
+                        BatNo=['其他温度'+str(t+1)]
+                        AvgOtherTempGap=abs((float(bms_infoN[BatNo]))-AvgOtherTemp)
+                        if AvgOtherTempGap>param.AvgOtherTempGap and float(bms_infoN[BatNo])>param.OtherUTlmt and float(bms_infoN[BatNo])<param.OtherOTlmt and not 8 in df_Diag_Ram['code']:
+                            OutlierOtherTempNo.append(t+1)
+                else:
+                    AvgOtherTempGap=maxOtherTemp-minOtherTemp
+                    if AvgOtherTempGap>param.AvgOtherTempGap and minOtherTemp>param.OtherUTlmt and maxOtherTemp<param.OtherOTlmt and not 8 in df_Diag_Ram['code']:
+                        OutlierOtherTempNo=[1,2]
+                if len(OutlierOtherTempNo)>0:
+                    QuitErrCount[56]=0
+                    if not 56 in df_Diag_Ram['code'].values.tolist():#如果故障发生当前故障中没有该故障,则压入该故障
+                        df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,56,2,str(OutlierOtherTempNo)+'号传感器温度异常离群','技术介入诊断']
+                        
+                        ErrorFlg=1            
+                    else:#如果故障发生当前故障中有该故障,则不进行操作
+                        pass
+                else:
+                    if 56 in df_Diag_Ram['code'].values.tolist():
+                            QuitErrCount[56]=QuitErrCount[56]+1
+                            if QuitErrCount[56]>6:
+                                end_time=datetime.datetime.now()
+                                end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                                df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']==56].index,['end_time']]=end_time
+                                
+            if (maxOtherTemp-maxCellTemp)>param.AvgOtherTempGap and  maxOtherTemp<param.OtherOTlmt and maxCellTemp<param.PackOTlmt:
+                QuitErrCount[56]=0
+                if not 56 in df_Diag_Ram['code'].values.tolist():
+                    df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,56,2,str(OutlierOtherTempNo)+'号传感器温度异常离群','技术立即介入诊断']
+                    
+                    ErrorFlg=1
+            else:
+                if 56 in df_Diag_Ram['code'].values.tolist():
+                        QuitErrCount[56]=QuitErrCount[56]+1
+                        if QuitErrCount[56]>6:
+                            end_time=datetime.datetime.now()
+                            end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                            df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']==56].index,['end_time']]=end_time
+                            
+            if (maxCellTemp-maxOtherTemp)>param.AvgOtherTempGap and  maxOtherTemp<param.OtherOTlmt and maxCellTemp<param.PackOTlmt and not 8 in df_Diag_Ram['code']:
+                QuitErrCount[8]=0
+                if not 8 in df_Diag_Ram['code'].values.tolist():
+                    df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,8,2,str(OutlierTempNo)+'号电芯温度异常离群','技术立即介入诊断']
+                    
+                    ErrorFlg=1
+            else:
+                if 8 in df_Diag_Ram['code'].values.tolist():
+                        QuitErrCount[8]=QuitErrCount[8]+1
+                        if QuitErrCount[8]>6:
+                            end_time=datetime.datetime.now()
+                            end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                            df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']==8].index,['end_time']]=end_time                   
+        return df_Diag_Ram
+   

+ 194 - 0
LIB/MIDDLE/SaftyCenter/CellValueDiag/main.py

@@ -0,0 +1,194 @@
+import CBMSBatDiag
+import QX_BatteryParam
+from SC_SamplingSafty import SamplingSafty
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+from sqlalchemy import create_engine
+import time, datetime
+from apscheduler.schedulers.blocking import BlockingScheduler
+from LIB.MIDDLE.CellStateEstimation.Common import DBDownload
+from LIB.MIDDLE.CellStateEstimation.Common import log
+from pandas.core.frame import DataFrame
+import datacompy
+
+
+
+#...................................电池包电芯安全诊断函数......................................................................................................................
+def diag_cal():
+    global SNnums
+    global df_Diag_Ram
+
+    start=time.time()
+    end_time=datetime.datetime.now()
+    start_time=end_time-datetime.timedelta(seconds=120)
+    start_time=start_time.strftime('%Y-%m-%d %H:%M:%S')
+    end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+
+    for sn in SNnums:
+        if 'PK500' in sn:
+            celltype=1 #6040三元电芯
+        elif 'PK502' in sn:
+            celltype=2 #4840三元电芯
+        elif 'K504B' in sn:
+            celltype=99    #60ah林磷酸铁锂电芯
+        elif 'MGMLXN750' in sn:
+            celltype=3 #力信50ah三元电芯
+        elif 'MGMCLN750' or 'UD' in sn: 
+            celltype=4 #CATL 50ah三元电芯
+        else:
+            print('SN:{},未找到对应电池类型!!!'.format(sn))
+            continue
+            # sys.exit()
+        param=QX_BatteryParam.BatteryInfo(celltype)     
+        # sn='PK50201A000002039'
+        # celltype=2
+        # start_time='2021-05-02 09:12:26'
+        # end_time='2021-06-03 19:12:26'
+        # # df_bms= pd.read_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\01qixiang\98Download\\'+'BMS_'+sn+'.csv',encoding='GB18030')
+
+        #读取原始数据库数据........................................................................................................................................................
+        dbManager = DBManager.DBManager()
+        df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms'])
+        df_bms = df_data['bms']
+        # df_bms.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\01qixiang\99Result\\''BMS_'+sn+'.csv',encoding='GB18030')
+
+        #读取结果数据库数据........................................................................................................................................................
+        host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+        port=3306
+        db='qx_cas'
+        user='qx_read'
+        password='Qx@123456'
+        mode=1
+        tablename1='cellstateestimation_soh'
+        tablename2='cellstateestimation_uniform_socvoltdiff'
+        tablename3='cellstateestimation_soc'
+        DBRead=DBDownload.DBDownload(host, port, db, user, password,mode)
+        with DBRead as DBRead:
+            df_soh=DBRead.getdata('time_st','sn','soh', 'cellsoh', tablename=tablename1, sn=sn, timename='time_sp', st=start_time, sp=end_time)
+            df_uniform=DBRead.getdata('time','sn','cellsoc_diff','cellmin_num','cellmax_num', tablename=tablename2, sn=sn, timename='time', st=start_time, sp=end_time)
+            # df_soc=DBRead.getdata('time','sn','packsoc', tablename=tablename3, sn=sn)
+
+        #电池诊断................................................................................................................................................................
+        #BatDiag=CBMSBatDiag.BatDiag(sn,celltype,df_bms, df_soh, df_uniform)
+        #df_res=BatDiag.diag()
+        #df_Diag_Ram_old=df_Diag_Ram
+        df_Diag_Ram_Update=DataFrame(columns=['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice'])
+        CellFltInfo=DataFrame(columns=['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice'])
+        if not df_bms.empty:
+            CellFltInfo=df_Diag_Ram[df_Diag_Ram['product_id']==sn]
+            df_Diag_Ram_Update=SamplingSafty.main(sn,param,df_bms,CellFltInfo)       
+        if not df_Diag_Ram_Update.empty:
+            sn_index=df_Diag_Ram[df_Diag_Ram['product_id']==sn].index
+            df_Diag_Ram=df_Diag_Ram.drop(index=sn_index)
+            df_Diag_Ram=df_Diag_Ram.append(df_Diag_Ram_Update)
+            df_Diag_Ram.reset_index(inplace=True,drop=True)
+
+        Diag_Ram_Dif=datacompy.Compare(df_Diag_Ram_Update,CellFltInfo,join_columns=['product_id','end_time','code'])
+        Diag_Ram_Dif=Diag_Ram_Dif.df1_unq_rows    
+        if len(Diag_Ram_Dif)>0:
+            Diag_Ram_Dif_New=Diag_Ram_Dif[Diag_Ram_Dif['end_time']=='0000-00-00 00:00:00']
+            Diag_Ram_Dif_Finish=df_Diag_Ram[df_Diag_Ram['end_time']!='0000-00-00 00:00:00']
+            if len(Diag_Ram_Dif_New)>0:
+                result=pd.read_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\sxq\SaftyCenter_CODE_V1_1\result.csv',encoding='gbk')
+                result=result.append(Diag_Ram_Dif_New)
+                result.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\sxq\SaftyCenter_CODE_V1_1\result.csv',index=False,encoding='GB18030')
+            if len(Diag_Ram_Dif_Finish)>0:
+                result=pd.read_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\sxq\SaftyCenter_CODE_V1_1\result.csv',encoding='gbk')
+                Diag_Ram_Dif_Finish=Diag_Ram_Dif_Finish.reset_index(drop=True)
+                for i in range(0,len(Diag_Ram_Dif_Finish)):
+                    result.loc[result[result[result['product_id']==Diag_Ram_Dif_Finish.loc[i,'product_id']]['code']==Diag_Ram_Dif_Finish.loc[i,'code']].index,'end_time']=Diag_Ram_Dif_Finish.loc[i,'end_time']
+                result.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\sxq\SaftyCenter_CODE_V1_1\result.csv',index=False,encoding='GB18030')
+        end=time.time()
+        print(end-start)
+        # print(df_soh)
+            
+#...................................电池包电芯安全诊断函数......................................................................................................................
+def shortdiag_cal():
+    global SNnums
+    global df_Diag_Ram
+    start=time.time()
+    end_time=datetime.datetime.now()
+    start_time=end_time-datetime.timedelta(days=30)
+    start_time=start_time.strftime('%Y-%m-%d %H:%M:%S')
+    end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+
+    for sn in SNnums:
+        if 'PK500' in sn:
+            celltype=1 #6040三元电芯
+        elif 'PK502' in sn:
+            celltype=2 #4840三元电芯
+        elif 'K504B' in sn:
+            celltype=99    #60ah林磷酸铁锂电芯
+        elif 'MGMLXN750' in sn:
+            celltype=3 #力信50ah三元电芯
+        elif 'MGMCLN750' or 'UD' in sn: 
+            celltype=4 #CATL 50ah三元电芯
+        else:
+            print('SN:{},未找到对应电池类型!!!'.format(sn))
+            continue
+            # sys.exit()
+
+        #读取结果数据库数据........................................................................................................................................................
+        host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+        port=3306
+        db='qx_cas'
+        user='qx_read'
+        password='Qx@123456'
+        mode=2
+        tablename1='cellstateestimation_intershort'
+        DBRead=DBDownload.DBDownload(host, port, db, user, password, mode)  #mode==1取数据库最后一行数据
+        with DBRead as DBRead:
+            df_short=DBRead.getdata('time_sp','sn','short_current', tablename=tablename1, sn=sn, timename='time_sp', st=start_time, sp=end_time)  
+        
+        #电池诊断................................................................................................................................................................
+        ShortDiag=CBMSBatDiag.ShortDiag(sn,celltype, df_short)
+        df_res=ShortDiag.shortdiag()
+        # df_res.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\01qixiang\99Result\\'+'CBMS_diag_'+sn+'.csv',encoding='GB18030')
+        
+        print(df_res)
+
+        end=time.time()
+        print(end-start)
+        # print(df_soh)
+        
+
+#...............................................主函数.......................................................................................................................
+if __name__ == "__main__":
+
+    excelpath=r'D:\Platform\platform_python\data_analyze_platform\USER\sxq\SaftyCenter_CODE_V1_1\sn-20210903.xlsx'
+    SNdata_6060 = pd.read_excel(excelpath, sheet_name='科易6060')
+    SNdata_6040 = pd.read_excel(excelpath, sheet_name='科易6040')
+    SNdata_4840 = pd.read_excel(excelpath, sheet_name='科易4840')
+    SNdata_L7255 = pd.read_excel(excelpath, sheet_name='格林美-力信7255')
+    SNdata_C7255 = pd.read_excel(excelpath, sheet_name='格林美-CATL7255')
+    SNdata_U7255 = pd.read_excel(excelpath, sheet_name='优旦7255')
+    SNnums_6060=SNdata_6060['SN号'].tolist()
+    SNnums_6040=SNdata_6040['SN号'].tolist()
+    SNnums_4840=SNdata_4840['SN号'].tolist()
+    SNnums_L7255=SNdata_L7255['SN号'].tolist()
+    SNnums_C7255=SNdata_C7255['SN号'].tolist()
+    SNnums_U7255=SNdata_U7255['SN号'].tolist()
+    SNnums=SNnums_L7255 + SNnums_C7255 + SNnums_6040 + SNnums_4840 + SNnums_U7255+ SNnums_6060
+    #SNnums=['MGMCLN750N215I005','PK504B10100004341','PK504B00100004172','MGMLXN750N2189014']
+    #SNnums=['MGMLXN750N2189014']
+    
+    mylog=log.Mylog('log_diag.txt','error')
+    mylog.logcfg()
+    #............................模块运行前,先读取数据库中所有结束时间为0的数据,需要从数据库中读取................
+    result=pd.read_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\sxq\SaftyCenter_CODE_V1_1\result.csv',encoding='gbk')
+    
+    df_Diag_Ram=result[result['end_time']=='0000-00-00 00:00:00']
+    print('----------------输入--------')
+    print(df_Diag_Ram)
+    #定时任务.......................................................................................................................................................................
+    scheduler = BlockingScheduler()
+    scheduler.add_job(diag_cal, 'interval', seconds=120, id='diag_job')
+    scheduler.add_job(shortdiag_cal, 'interval', days=7, id='shortdiag_job')
+
+    try:  
+        scheduler.start()
+    except Exception as e:
+        scheduler.shutdown()
+        print(repr(e))
+        mylog.logopt(e)

+ 75 - 0
LIB/MIDDLE/SaftyCenter/Common/DBDownload.py

@@ -0,0 +1,75 @@
+import pymysql
+import time
+import pandas as pd
+
+class DBDownload:
+
+    def __init__(self, host='', port='', db='', user='', password='', mode=''):
+        self.host = host
+        self.port = port
+        self.db = db
+        self.user = user
+        self.password = password
+        self.mode=mode
+        pass
+
+    def __enter__(self):
+        self.connect()
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.close()
+
+    def connect(self):
+        conn_success_flag = 0
+        while not conn_success_flag:
+            try:
+                self.conn = pymysql.connect(host=self.host, port=self.port, user=self.user, password=self.password, database=self.db)
+            except Exception as e:
+                conn_success_flag = 0
+                print("数据库连接失败 :{}".format(e))
+                time.sleep(5)
+            else:
+                conn_success_flag = 1
+                print('数据库连接成功!')
+                self.cursor = self.conn.cursor()
+
+    def getdata(self,*param,tablename,sn,timename,st,sp,factory):
+        print('数据获取中......')
+        param=list(param)
+        str=''
+        for i in range(len(param)):
+            if i<1:
+                str=str+param[i]
+            else:
+                str=str+','+param[i]
+        if self.mode==1:
+            self.cursor.execute("select %s from %s where sn='%s' order by id desc limit 1" %(str,tablename,sn))
+        elif self.mode==0:
+            if len(sn)>1:
+                self.cursor.execute("select %s from %s where sn='%s' and %s between '%s' and '%s'" %(str,tablename,sn,timename,st,sp))
+            else:
+                self.cursor.execute("select %s from %s where %s between '%s' and '%s'" %(str,tablename,timename,st,sp))
+        elif self.mode==2:
+            self.cursor.execute("select %s from %s where factory='%s'" %(str,tablename,factory))
+        elif self.mode==3:
+            self.cursor.execute("select %s from %s" %(str,tablename))
+        elif self.mode==4:
+            self.cursor.execute("select %s from %s where qrcode='%s' order by id desc limit 1" %(str,tablename,sn))
+        elif self.mode==5:
+            self.cursor.execute("select %s from %s where info='%s'" %(str,tablename,timename))
+        res = self.cursor.fetchall()
+        df_res = pd.DataFrame(res, columns=param)
+        df_res = df_res.reset_index(drop=True)
+        return(df_res)
+
+    def close(self):
+        try:
+            self.cursor.close()
+            self.conn.close()
+        except Exception as e:
+            print(e)
+        else:
+            print('数据库已断开连接!')
+    
+    

+ 63 - 0
LIB/MIDDLE/SaftyCenter/Common/FeiShuData.py

@@ -0,0 +1,63 @@
+from numpy import empty
+import pandas as pd
+from pandas.core.frame import DataFrame
+
+import requests
+import json
+import datetime,time
+import numpy as np
+
+
+
+def getFeiShuDATA():
+    print('飞书数据获取中......')
+    Columns=["状态", "发生时间","代理商","电池编码","用户信息","业务分类A","业务分类B","操作","信息来源", "内容描述", "客服处理时间","客服处理结果","记录人","当前处理人","处理部门","跟进记录","问题类型A","问题类型B","事件处理完结时间","运维紧急程度","维修信息","返厂时间"]
+    # 获取token
+
+    app_id = 'cli_a1e503bb3d78100c'
+    app_secret = 'oZBbGSRYsf9sXy8t8e8kbhVLciekyvMt'
+    url = 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal'
+
+    headers = {'Content-Type':"application/json; charset=utf-8"}
+    datas = json.dumps({'app_id':app_id, 'app_secret':app_secret})
+    response = requests.post(url, data=datas, headers=headers)
+    auth_token = json.loads(response.text)['tenant_access_token']
+
+    # 获取数据
+    app_token = 'bascnpaW50pJsCNd1AyIRFIc24b'
+    table_id = 'tblo3wnR2HFWI6rA'
+    sheet_id = 'vewZh94xDC'
+    url = 'https://open.feishu.cn/open-apis/bitable/v1/apps/{}/tables/{}/records'.format(app_token, table_id)
+    headers = {"Authorization":"Bearer {}".format(auth_token)}
+
+
+    df_file = pd.DataFrame(columns=Columns)
+    timesort=['发生时间','客服处理时间','事件处理完结时间','返厂时间']
+    count=0
+    GotPageToken=str()
+
+    while True:
+        # 筛选条件加载这里
+        datas = {'view_id':sheet_id, 'page_size':100, 'text_field_as_array':"true", 'page_token':GotPageToken,'field_names':'["状态", "发生时间","代理商","电池编码","用户信息","业务分类A","业务分类B","操作","信息来源", "内容描述", "客服处理时间","客服处理结果","记录人","当前处理人","处理部门","跟进记录","问题类型A","问题类型B","事件处理完结时间","运维紧急程度","维修信息","返厂时间"]'}
+        try:
+            response = requests.get(url, params=datas, headers=headers)
+            #print(response.text)
+            results = json.loads(response.text)['data']['items']
+            GotPageToken=json.loads(response.text)['data']['page_token']
+
+            for r in results:
+                df_file = df_file.append([r['fields']], ignore_index=True)
+        except:
+            break
+
+    for i in range(0,len(df_file)):
+        for t in range(0,len(timesort)):
+            if not df_file.loc[i,timesort[t]] is np.nan and not df_file.loc[i,timesort[t]] is None:
+                timeTemp= float(df_file.loc[i,timesort[t]])
+                timeTTmp=time.localtime(timeTemp/1000) 
+                df_file.loc[i,timesort[t]] = time.strftime('%Y-%m-%d %H:%M:%S',timeTTmp)
+    for col in Columns:
+        df_file[col]=df_file[col].map(lambda x:str(x).lstrip('[{\'text\': ').rstrip('\', \'type\': \'text\'}]').replace('\\n\', \'type\': \'text\'}, {\'text\': \'',' '))
+    df_file.to_excel('df_file.xlsx')
+    print('飞书数据获取完成')
+    return df_file

+ 150 - 0
LIB/MIDDLE/SaftyCenter/Common/QX_BatteryParam.py

@@ -0,0 +1,150 @@
+class BatteryInfo():
+    def __init__(self,celltype):
+        self.CellMaxUSBTemp=55
+        self.AllowChgMinTemp=0
+        self.AllowDsChgTemp=-5
+        self.AvgVolGap=1
+        self.AvgCellTempGap=10
+        
+        self.PackOTlmt=65
+        self.PackUTlmt=-20
+        self.OtherOTlmt=91
+        self.OtherUTlmt=-20        
+        self.FaultCount=100
+
+
+
+        if celltype==1: #6040
+            self.Capacity = 41
+            self.PackFullChrgVolt=69.99
+            self.CellFullChrgVolt=4.2
+            self.CellVoltNums=17
+            self.CellTempNums=3
+            self.OtherTempNums=5
+            self.FullChrgSoc=98
+            self.PeakSoc=57
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0,	    3.5348,	8.3581,	13.181,	18.004,	22.827,	27.651,	32.474,	37.297,	42.120,	46.944,	51.767,	56.590,	61.413,	66.237,	71.060,	75.883,	80.707,	85.530,	90.353,	95.176,	100,   105]
+            self.LookTab_OCV = [3.3159,	3.4384,	3.4774,	3.5156,	3.5478,	3.5748,	3.6058,	3.6238,	3.638,	3.6535,	3.6715,	3.6951,	3.7279,	3.7757,	3.8126,	3.8529,	3.8969,	3.9446,	3.9946,	4.0491,	4.109,	4.183, 4.263]
+            self.DifVolGap = 3
+            self.CellOVlmt=5
+            self.CellUVlmt=2
+            self.CantChrgVol=3
+            self.AvgOtherTempGap=99
+          
+        elif celltype==2: #4840
+            self.Capacity = 41
+            self.PackFullChrgVolt=69.99
+            self.CellFullChrgVolt=4.2
+            self.CellVoltNums=14
+            self.CellTempNums=3
+            self.OtherTempNums=5            
+            self.FullChrgSoc=98
+            self.PeakSoc=57
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0,	    3.5348,	8.3581,	13.181,	18.004,	22.827,	27.651,	32.474,	37.297,	42.120,	46.944,	51.767,	56.590,	61.413,	66.237,	71.060,	75.883,	80.707,	85.530,	90.353,	95.176,	100,   105]
+            self.LookTab_OCV = [3.3159,	3.4384,	3.4774,	3.5156,	3.5478,	3.5748,	3.6058,	3.6238,	3.638,	3.6535,	3.6715,	3.6951,	3.7279,	3.7757,	3.8126,	3.8529,	3.8969,	3.9446,	3.9946,	4.0491,	4.109,	4.183, 4.263]
+            self.DifVolGap = 3
+            self.CellOVlmt=5
+            self.CellUVlmt=2
+            self.CantChrgVol=3
+            self.AvgOtherTempGap=99        
+
+        elif  celltype==3:   #力信50ah三元电芯
+            self.Capacity = 51
+            self.PackFullChrgVolt=80
+            self.CellFullChrgVolt=4.2
+            self.CellVoltNums=20
+            self.CellTempNums=4
+            self.OtherTempNums=1
+            self.FullChrgSoc=98
+            self.PeakSoc=57
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0,	    5,	    10,	    15,	    20,	    25,	    30,	    35,	    40,	    45,	    50,	    55,	    60,	    65,	    70,	    75,	    80,	    85,	    90,	    95,	    100,   105]
+            self.LookTab_OCV = [3.357, 	3.455, 	3.493, 	3.540, 	3.577, 	3.605, 	3.622, 	3.638, 	3.655, 	3.677, 	3.707, 	3.757, 	3.815, 	3.866, 	3.920, 	3.976, 	4.036, 	4.099, 	4.166, 	4.237, 	4.325, 4.415]
+            self.DifVolGap = 3
+            self.CellOVlmt=5
+            self.CellUVlmt=2
+            self.CantChrgVol=3
+            self.AvgOtherTempGap=40
+
+        elif celltype==4:   #CATL 50ah三元电芯
+            self.Capacity = 50
+            self.PackFullChrgVolt=80
+            self.CellFullChrgVolt=4.2
+            self.CellVoltNums=20
+            self.CellTempNums=2
+            self.OtherTempNums=0
+            self.FullChrgSoc=98
+            self.PeakSoc=57
+            self.PackCrntDec=-1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0,	    5,	    10,	    15,	    20,	    25,	    30,	    35,	    40,	    45,	    50,	    55,	    60,	    65,	    70,	    75,	    80,	    85,	    90,	    95,	    100,   105]
+            self.LookTab_OCV = [3.152, 	3.397, 	3.438, 	3.481, 	3.523, 	3.560, 	3.586, 	3.604, 	3.620, 	3.638, 	3.661, 	3.693, 	3.748, 	3.803, 	3.853, 	3.903, 	3.953, 	4.006, 	4.063, 	4.121, 	4.183, 4.253]
+            self.DifVolGap = 3
+            self.CellOVlmt=5
+            self.CellUVlmt=2
+            self.CantChrgVol=3
+            self.AvgOtherTempGap=40
+          
+
+        elif celltype==99:   #60ah磷酸铁锂电芯
+            self.Capacity = 54
+            self.PackFullChrgVolt=69.99
+            self.CellFullChrgVolt=3.5
+            self.OcvInflexionBelow=3.285
+            self.OcvInflexion2=3.296
+            self.OcvInflexion3=3.328
+            self.OcvInflexionAbove=3.4
+            self.CellVoltNums=20
+            self.CellTempNums=3
+            self.OtherTempNums=5 
+            self.FullChrgSoc=98
+            self.PeakSoc=59
+            self.PeakVoltLowLmt=3.35
+            self.PeakVoltUpLmt=3.4
+            self.PeakCellVolt=[3.362,3.363,3.365,3.366,3.367]
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0.00, 	2.40, 	6.38, 	10.37, 	14.35, 	18.33, 	22.32, 	26.30, 	30.28, 	35.26, 	40.24, 	45.22, 	50.20, 	54.19, 	58.17, 	60.16, 	65.14, 	70.12, 	75.10, 	80.08, 	84.06, 	88.05, 	92.03, 	96.02, 	100.00]
+            self.LookTab_OCV = [2.7151,	3.0298,	3.1935,	3.2009,	3.2167,	3.2393,	3.2561,	3.2703,	3.2843,	3.2871,	3.2874,	3.2868,	3.2896,	3.2917,	3.2967,	3.3128,	3.3283,	3.3286,	3.3287,	3.3288,	3.3289,	3.3296,	3.3302,	3.3314,	3.3429]
+            self.DifVolGap = 3
+            self.CellOVlmt=4
+            self.CellUVlmt=2
+            self.CantChrgVol=2.6
+            self.AvgOtherTempGap=99
+ 
+
+        elif celltype==100:
+            self.Capacity = 228*2
+            self.PackFullChrgVolt=3.65*192
+            self.CellFullChrgVolt=3.5
+            self.OcvInflexionBelow=3.285
+            self.OcvInflexion2=3.296
+            self.OcvInflexion3=3.328
+            self.OcvInflexionAbove=3.4
+            self.CellVoltNums=384
+            self.CellTempNums=64
+            self.OtherTempNums=0
+            self.FullChrgSoc=98
+            self.PeakSoc=59
+            self.PeakVoltLowLmt=3.35
+            self.PeakVoltUpLmt=3.4
+            self.PeakCellVolt=[3.362,3.363,3.365,3.366,3.367]
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0.00, 	2.40, 	6.38, 	10.37, 	14.35, 	18.33, 	22.32, 	26.30, 	30.28, 	35.26, 	40.24, 	45.22, 	50.20, 	54.19, 	58.17, 	60.16, 	65.14, 	70.12, 	75.10, 	80.08, 	84.06, 	88.05, 	92.03, 	96.02, 	100.00]
+            self.LookTab_OCV = [2.7151,	3.0298,	3.1935,	3.2009,	3.2167,	3.2393,	3.2561,	3.2703,	3.2843,	3.2871,	3.2874,	3.2868,	3.2896,	3.2917,	3.2967,	3.3128,	3.3283,	3.3286,	3.3287,	3.3288,	3.3289,	3.3296,	3.3302,	3.3314,	3.3429]
+            self.DifVolGap = 3
+            self.CellOVlmt=4
+            self.CellUVlmt=2
+            self.CantChrgVol=2.6
+            self.AvgOtherTempGap=40
+ 
+
+        else:
+            print('未找到对应电池编号!!!')
+            # sys.exit()

+ 51 - 0
LIB/MIDDLE/SaftyCenter/DataDiag_Static/BMSuploaderrortest.py

@@ -0,0 +1,51 @@
+
+__author__ = 'lmstack'
+#coding=utf-8
+import os
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+import time, datetime
+import dateutil.relativedelta
+import traceback
+from LIB.MIDDLE.CellStateEstimation.Common import log
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import DBDownload as DBDownload
+import time, datetime
+from pandas.core.frame import DataFrame
+from apscheduler.schedulers.blocking import BlockingScheduler
+from LIB.MIDDLE.SaftyCenter.DataDiag_Static.DataStatistics import DataSta
+from LIB.MIDDLE.SaftyCenter.DataDiag_Static.SC_CtrlSafty import CtrlSafty
+from LIB.MIDDLE.SaftyCenter.DataDiag_Static.DiagDataMerge import DiagDataMerge
+from LIB.MIDDLE.SaftyCenter.DataDiag_Static.SC_SamplingSafty import SamplingSafty
+from LIB.MIDDLE.SaftyCenter.DataDiag_Static.SC_BMSUploadError import BMSReportError
+from LIB.MIDDLE.SaftyCenter.DataDiag_Static import CBMSBatDiag
+from LIB.MIDDLE.SaftyCenter.Common import DBDownload as DBDw
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam as QX_BatteryParam
+from urllib import parse
+import pymysql
+
+# 故障映射表
+host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+port=3306
+db='algo_dict'
+user='qx_algo_rw'
+password=parse.quote_plus('qx@123456')
+db_engine = create_engine(
+    "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+        user, password, host, port, db
+    ))
+
+errorcode_map = pd.read_sql("select * from faultcode_map", db_engine)
+db_engine.dispose()
+sn = 'PK504B10100004349'
+start_time = '2021-12-30 18:00:04'
+end_time = '2021-12-30 18:15:04'
+dbManager = DBManager.DBManager()
+df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms'])
+df_bms = df_data['bms']
+df_bms=df_bms.dropna(subset=['总电流[A]'])
+df_bms=df_bms.reset_index(drop=True)
+df_Diag_Batdiag_update=BMSReportError.main(sn,df_bms,pd.DataFrame(),1,errorcode_map)
+print(df_Diag_Batdiag_update)

+ 539 - 0
LIB/MIDDLE/SaftyCenter/DataDiag_Static/CBMSBatDiag.py

@@ -0,0 +1,539 @@
+import pandas as pd
+import numpy as np
+import datetime
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam
+
+class BatDiag:
+    def __init__(self,sn,celltype,df_bms,df_soh,df_uniform,df_diag_Ram):  #参数初始化
+
+        self.sn=sn
+        self.celltype=celltype
+        self.param=BatParam.BatParam(celltype)
+        self.df_bms=df_bms
+        self.df_soh=df_soh
+        self.df_uniform=df_uniform
+        self.df_diag_ram=df_diag_Ram
+        self.packcrnt=df_bms['总电流[A]']*self.param.PackCrntDec
+        self.packvolt=df_bms['总电压[V]']
+        self.bms_soc=df_bms['SOC[%]']
+        self.bmstime= pd.to_datetime(df_bms['时间戳'], format='%Y-%m-%d %H:%M:%S')
+        self.bmsfault1=self.df_bms['故障代码']
+        # self.bmsfault2=self.df_bms['alarm2'].tolist()
+        # self.bmsfault3=self.df_bms['alarm3'].tolist()
+        # self.bmsfault4=self.df_bms['fault'].tolist()
+
+        self.cellvolt_name=['单体电压'+str(x) for x in range(1,self.param.CellVoltNums+1)]
+        self.celltemp_name=['单体温度'+str(x) for x in range(1,self.param.CellTempNums+1)]
+    
+    def diag(self):
+        if self.celltype<=50:
+            df_res=self._ncm_diag()
+            return df_res    
+        else:
+            df_res=self._ncm_diag()
+            return df_res
+        
+
+    #定义滑动滤波函数.............................................................................................
+    def _np_move_avg(self,a, n, mode="same"): 
+        return (np.convolve(a, np.ones((n,)) / n, mode=mode))
+    
+    #寻找当前行数据的所有温度值...................................................................................
+    def _celltemp_get(self,num):   
+        celltemp = list(self.df_bms.loc[num,self.celltemp_name])
+        return celltemp
+
+    #获取当前行所有电压数据............................................................................................
+    def _cellvolt_get(self,num): 
+        cellvolt = list(self.df_bms.loc[num,self.cellvolt_name]/1000)
+        return cellvolt
+
+    #..........................................三元电池诊断功能..................................................................
+    def _ncm_diag(self):
+
+        bmssoc_st=float(self.bms_soc[0])    #SOC卡滞初始参数
+        ah_accum=0  #SOC卡滞初始参数
+        as_chg=0    #过流诊断初始参数
+        as_dis=0    #过流诊断初始参数
+        # time1=self.bmstime[0]   #温升速率初始参数
+        # temp1=np.array(self._celltemp_get(0))   #温升速率初始参数
+        # temprate_cnt=0
+        ot_time=0
+        ut_time=0
+        dt_time=0
+        cov_time=0
+        cuv_time=0
+        cdv_time=0
+        pov_time=0
+        puv_time=0
+        
+        end_time='0000-00-00 00:00:00'
+            
+        for i in range(1,len(self.df_bms)):
+            
+            #温度诊断功能.............................................................................................................
+            celltemp0=self._celltemp_get(i-1)
+            celltemp1=self._celltemp_get(i)
+            celltempmin0=min(celltemp0)
+            celltempmin1=min(celltemp1)
+            celltempmax0=max(celltemp0)
+            celltempmax1=max(celltemp1)
+            #温度有效性判断..........................................................................
+            if celltempmax0>self.param.CellTempUpLmt or celltempmin0<self.param.CellTempLwLmt:
+                celltempvalid=0
+            else:  
+                celltempvalid=1
+           
+            if celltempvalid==1:
+                #过温判断.............................................................................................................
+                if not 4 in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if celltempmax0>self.param.CellTempHighLv2 and celltempmax1>self.param.CellTempHighLv2:    #二级高温进入
+                        ot_time=ot_time+(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                        if ot_time>self.param.temp_time:
+                            time=self.bmstime[i]
+                            code=4
+                            faultlv=3
+                            faultinfo='温度{}高温二级'.format(celltemp1.index(celltempmax1)+1)
+                            faultadvice='技术介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        ot_time=0
+                else:   #ram当前故障中有该故障,则判断是否退出该故障
+                    if celltempmax0<self.param.CellTempHighLv1-5 and celltempmax1<self.param.CellTempHighLv1-5:    #二级高温恢复
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==4].index, 'end_time'] = time
+                    else:
+                        pass
+            
+                #欠温判断.................................................................................................................
+                if not 6 in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if celltempmin0<self.param.CellTempLowLv2 and celltempmin1<self.param.CellTempLowLv2:  #二级低温进入
+                        ut_time=ut_time+(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                        if ut_time>self.param.temp_time:
+                            time=self.bmstime[i]
+                            code=6
+                            faultlv=3
+                            faultinfo='温度{}低温二级'.format(celltemp1.index(celltempmin1)+1)
+                            faultadvice='技术介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        ut_time=0
+                else:   #ram当前故障中有该故障,则判断是否退出该故障
+                    if celltempmax0>self.param.CellTempLowLv1+2 and celltempmax1>self.param.CellTempLowLv1+2:    #二级高温恢复
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==6].index, 'end_time'] = time
+                    else:
+                        pass
+              
+                #温差判断.............................................................................................................................
+                if not 8 in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if (celltempmax0-celltempmin0)>self.param.CellTempDiffLv2 and (celltempmax1-celltempmin1)>self.param.CellTempDiffLv2:  #二级温差进入
+                        dt_time=dt_time+(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                        if dt_time>self.param.temp_time:
+                            time=self.bmstime[i]
+                            code=8
+                            faultlv=3
+                            faultinfo='温度{}和{}温差大二级'.format(celltemp1.index(celltempmax1)+1,celltemp1.index(celltempmin1)+1)
+                            faultadvice='技术介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        dt_time=0
+                else:   #ram当前故障中有该故障,则判断是否退出该故障
+                    if (celltempmax0-celltempmin0)<self.param.CellTempDiffLv1-2 and (celltempmax1-celltempmax0)>self.param.CellTempDiffLv1-2:  #二级温差恢复
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==8].index, 'end_time'] = time
+                    else:
+                        pass
+
+            #     #温升判断
+            #     time2=self.bmstime[i]
+            #     delttime=(time2-time1).total_seconds()
+            #     if delttime>20:
+            #         temp2=np.array(self._celltemp_get(i))
+            #         celltemp_rate=round((max(temp2-temp1)*60)/delttime,2)    #计算最大温升速率
+            #         temp1=temp2 #更新初始温度
+            #         time1=time2 #更新初始时间
+            #         if not 9 in list(self.df_diag_ram['code']):#当前故障中没有该故障,则判断是否发生该故障
+            #             if celltemp_rate>self.param.CellTempRate:
+            #                 temprate_cnt=temprate_cnt+1
+            #                 if temprate_cnt>2:  #温升故障进入
+            #                     time=self.bmstime[i]
+            #                     code=9
+            #                     faultlv=3
+            #                     faultinfo='温升速率过快:{}℃/min'.format(celltemp_rate)
+            #                     faultadvice='技术介入诊断'
+            #                     self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+            #                 else:
+            #                     pass
+            #             else:   #ram当前故障中有该故障,则判断是否退出该故障
+            #                pass
+            #         else:
+            #             if celltemp_rate<self.param.CellTempRate-1: #温升故障恢复
+            #                 time=self.bmstime[i]
+            #                 self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==9].index, 'end_time'] = time
+            #     else:
+            #         pass
+            
+            else:
+                ot_time=0
+                ut_time=0
+                dt_time=0
+                    
+            #电压诊断功能.................................................................................................
+            cellvolt0=self._cellvolt_get(i-1)
+            cellvolt1=self._cellvolt_get(i)
+            cellvoltmin0=min(cellvolt0)
+            cellvoltmax0=max(cellvolt0)
+            cellvoltmin1=min(cellvolt1)
+            cellvoltmax1=max(cellvolt1)
+            #电压断线诊断...................................................................................................
+            if (cellvoltmin0<2 and cellvoltmax0>4.5) or cellvoltmin0<0.1 or cellvoltmax0>5:
+                cellvoltvalid=0
+            else:
+                cellvoltvalid=1
+            
+            if cellvoltvalid==1:
+                #过压诊断.............................................................................................................
+                if not 12 in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if cellvoltmax0>self.param.CellOvLv2 and cellvoltmax1>self.param.CellOvLv2:  #二级过压进入
+                        cov_time=cov_time+(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                        if cov_time>self.param.volt_time:
+                            time=self.bmstime[i]
+                            code=12
+                            faultlv=4
+                            faultinfo='电芯{}过压二级'.format(cellvolt1.index(cellvoltmax1)+1)
+                            faultadvice='联系用户询问用车场景,技术介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        cov_time=0
+                else:   #ram当前故障中有该故障,则判断是否退出该故障
+                    if cellvoltmax0<self.param.CellOvLv1-0.05 and cellvoltmax1<self.param.CellOvLv1-0.05:   #二级过压故障恢复
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==12].index, 'end_time'] = time
+                    else:
+                        pass
+              
+
+                #欠压诊断.................................................................................................................
+                if not 14 in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if cellvoltmin0<self.param.CellUvLv2 and cellvoltmin1<self.param.CellUvLv2:  #二级欠压
+                        cuv_time=cuv_time+(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                        if cuv_time>self.param.volt_time:
+                            time=self.bmstime[i]
+                            code=14
+                            faultlv=3
+                            faultinfo='电芯{}欠压二级'.format(cellvolt1.index(cellvoltmin1)+1)
+                            faultadvice='联系用户询问用车场景,技术介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        cuv_time=0
+                else:
+                    if cellvoltmin0>self.param.CellUvLv1+0.1 and cellvoltmin1>self.param.CellUvLv1+0.1:
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==14].index, 'end_time'] = time
+                    else:
+                        pass
+             
+                #电芯压差大.....................................................................................................................................................
+                if not 16 in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if (cellvoltmax0-cellvoltmin0)>self.param.CellVoltDiffLv2 and (cellvoltmax1-cellvoltmin1)>self.param.CellVoltDiffLv2:  #二级电芯压差
+                        cdv_time=cdv_time+(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                        if cdv_time>self.param.volt_time:
+                            time=self.bmstime[i]
+                            code=16
+                            faultlv=3
+                            faultinfo='电芯{}和{}压差大二级'.format(cellvolt1.index(cellvoltmax1)+1,cellvolt1.index(cellvoltmin1)+1)
+                            faultadvice='技术介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        cdv_time=0
+                else:
+                    if (cellvoltmax0-cellvoltmin0)<self.param.CellVoltDiffLv1-0.05 and (cellvoltmax1-cellvoltmin1)<self.param.CellVoltDiffLv1-0.05: #二级欠压恢复
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==16].index, 'end_time'] = time
+                    else:
+                        pass
+            else:
+                cov_time=0
+                cuv_time=0
+                cdv_time=0
+                
+            #电池包诊断.....................................................................................................................................
+            if self.packvolt[i-1]<2*self.param.CellVoltNums and self.packvolt[i]>4.5*self.param.CellVoltNums:   #电池包电压有效性
+                packvoltvalid=0
+            else:
+                packvoltvalid=1
+
+            if packvoltvalid==1:
+                if not 18 in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if self.packvolt[i-1]>self.param.PackVoltOvLv2 and self.packvolt[i]>self.param.PackVoltOvLv2:   #电池包过压二级进入
+                        pov_time=pov_time+(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                        if pov_time>self.param.volt_time:
+                            time=self.bmstime[i]
+                            code=18
+                            faultlv=4
+                            faultinfo='电池包过压二级'
+                            faultadvice='联系用户询问用车场景,技术介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        pov_time=0
+                else:
+                    if self.packvolt[i-1]<self.param.PackVoltOvLv1-0.05*self.param.CellVoltNums and self.packvolt[i]<self.param.PackVoltOvLv1-0.05*self.param.CellVoltNums: #电池包过压二级恢复
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==18].index, 'end_time'] = time
+                    else:
+                        pass
+          
+                if not 20 in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if self.packvolt[i-1]<self.param.PackVoltUvLv2 and self.packvolt[i]<self.param.PackVoltUvLv2:   #电池包二级欠压进入
+                        puv_time=puv_time+(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                        if puv_time>self.param.volt_time:
+                            time=self.bmstime[i]
+                            code=20
+                            faultlv=3
+                            faultinfo='电池包欠压二级'
+                            faultadvice='联系用户询问用车场景,技术介入诊断'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                        else:
+                            pass
+                    else:
+                        puv_time=0
+                else:
+                    if self.packvolt[i-1]>self.param.PackVoltUvLv1+0.1*self.param.CellVoltNums and self.packvolt[i]>self.param.PackVoltUvLv1+0.1*self.param.CellVoltNums:   #电池包二级欠压恢复
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==20].index, 'end_time'] = time
+                    else:
+                        pass
+            else:
+                pov_time=0
+                puv_time=0
+            
+            #电流过流诊断.......................................................................................................................
+            step=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+            if step<120 and self.packcrnt[i]>self.param.PackDisOc and self.packcrnt[i-1]>self.param.PackDisOc:
+                as_dis=as_dis+(self.packcrnt[i]-self.param.PackDisOc)*step    #ah累计
+            elif step<120 and self.packcrnt[i]<self.param.PackChgOc and self.packcrnt[i-1]<self.param.PackChgOc:
+                as_chg=as_chg+(self.param.PackDisOc-self.packcrnt[i])*step    #ah累计
+            else:
+                as_dis=0
+                as_chg=0
+            
+            if not 22 in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if as_dis>100:
+                    time=self.bmstime[i]
+                    code=22
+                    faultlv=3
+                    faultinfo='电池包放电过流'
+                    faultadvice='联系用户询问用车场景,技术介入诊断'
+                    self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                else:
+                    pass
+            else:
+                if self.packcrnt[i]<self.param.PackDisOc-10:
+                    time=self.bmstime[i]
+                    self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==22].index, 'end_time'] = time
+                else:
+                    pass
+            
+            if not 21 in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if as_chg>100:
+                    time=self.bmstime[i]
+                    code=21
+                    faultlv=3
+                    faultinfo='电池包充电过流'
+                    faultadvice='联系用户询问用车场景,技术介入诊断'
+                    self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                else:
+                    pass
+            else:
+                if self.packcrnt[i]>self.param.PackChgOc+10:
+                    time=self.bmstime[i]
+                    self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==21].index, 'end_time'] = time
+                else:
+                    pass
+
+            #SOC卡滞、跳变诊断................................................................................................
+            step=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+            if step<120:
+                ah_accum=ah_accum-self.packcrnt[i]*step/3600    #ah累计
+            else:
+                pass
+            #SOC卡滞............................................................................................................
+            if abs(ah_accum)>self.param.Capacity*0.1:   
+                bmssoc_now=float(self.bms_soc[i])
+                if not 27 in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if abs(bmssoc_now-bmssoc_st)<self.param.SocClamp:   #SOC卡滞故障进入
+                        time=self.bmstime[i]
+                        code=27
+                        faultlv=1
+                        faultinfo='电池SOC卡滞'
+                        faultadvice='技术介入诊断,检修电池BMS软件'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                    else:
+                        pass
+                else:
+                    if abs(bmssoc_now-bmssoc_st)>self.param.SocClamp:   #SOC卡滞故障退出
+                        time=self.bmstime[i]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==27].index, 'end_time'] = time
+                    else:
+                        pass
+                bmssoc_st=bmssoc_now
+                ah_accum=0
+            else:
+                pass
+
+            #SOC跳变....................................................................................................................
+            bmssoc_last=float(self.bms_soc[i-1])
+            bmssoc_now=float(self.bms_soc[i])
+            if not 28 in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if step<30 and abs(bmssoc_now-bmssoc_last)>self.param.SocJump:  #SOC跳变进入
+                    time=self.bmstime[i]
+                    code=28
+                    faultlv=1
+                    faultinfo='电池SOC跳变'
+                    faultadvice='技术介入诊断,检修电池BMS软件'
+                    self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                else:
+                    pass
+            else:
+                if abs(bmssoc_now-bmssoc_st)<self.param.SocJump:    #SOC跳变故障退出
+                    time=self.bmstime[i]
+                    self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==28].index, 'end_time'] = time
+                else:
+                    pass
+
+            # #SOC过低故障报警............................................................................................................
+            # if not 26 in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+            #     if self.bms_soc[i-1]<self.param.SocLow and self.bms_soc[i]<self.param.SocLow:   #SOC过低故障进入
+            #         time=self.bmstime[i]
+            #         code=26
+            #         faultlv=1
+            #         faultinfo='电池包电量过低'
+            #         faultadvice='联系用户,请立刻充电'
+            #         self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+            #     else:
+            #         pass
+            # else:   
+            #     if self.bms_soc[i-1]>self.param.SocLow and self.bms_soc[i]>self.param.SocLow:   #SOC过低故障退出
+            #         time=self.bmstime[i]
+            #         self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==26].index, 'end_time'] = time
+            #     else:
+            #         pass
+
+            # #BMS故障报警........................................................................................................
+            # if not 1 in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+            #     if self.bmsfault1[i-1] is None or self.bmsfault1[i] is None:
+            #         self.bmsfault1[i-1]=0
+            #         self.bmsfault1[i]=0
+            #     if self.bmsfault1[i-1]>0 or self.bmsfault1[i]>0:   #BMS故障进入
+            #         time=self.bmstime[0]
+            #         code=1
+            #         faultlv=2
+            #         faultinfo='BMS故障报警:{}'.format(self.bmsfault1[i-1])
+            #         faultadvice='技术介入诊断'
+            #         self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+            #     else:
+            #         pass
+            # else:
+            #     if self.bmsfault1[i-1]==0 and self.bmsfault1[i]==0:   #BMS故恢复
+            #         time=self.bmstime[i]
+            #         self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==1].index, 'end_time'] = time
+        
+        
+        #SOC一致性故障报警..........................................................................................................
+        if not self.df_uniform.empty:
+            cellsoc_diff=self.df_uniform.loc[0,'cellsoc_diff']
+            if not 25 in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if cellsoc_diff>self.param.SocDiff: #SOC一致性差故障进入
+                    time=self.bmstime[0]
+                    code=25
+                    faultlv=1
+                    faultinfo='电芯{}和{}SOC差过大:{}'.format(self.df_uniform.loc[0,'cellmin_num'],self.df_uniform.loc[0,'cellmax_num'],cellsoc_diff)
+                    faultadvice='技术介入诊断'
+                    self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                else:
+                    pass
+            else:
+                if cellsoc_diff<self.param.SocDiff: #SOC一致性差故障恢复
+                    time=self.bmstime[0]
+                    self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==25].index, 'end_time'] = time
+        else:
+            cellsoc_diff=3
+
+        #容量过低和一致性故障报警................................................................................................
+        if not self.df_soh.empty:
+            soh=self.df_soh.loc[0,'soh']
+            cellsoh=eval(self.df_soh.loc[0,'cellsoh'])
+            cellsoh=np.array(cellsoh)
+            cellsoh_lowindex=np.argwhere(cellsoh<self.param.SohLow)
+            cellsoh_lowindex=cellsoh_lowindex+1
+            if self.celltype==1 or self.celltype==2 or self.celltype==3 or self.celltype==4: 
+                cellsoh_diff=np.max(cellsoh)-np.min(cellsoh)
+                if not 23 in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if soh<self.param.SohLow:   #soh过低故障进入
+                        time=self.bmstime[0]
+                        code=23
+                        faultlv=1
+                        faultinfo='电池包容量过低:电芯{}'.format(cellsoh_lowindex)
+                        faultadvice='检修电池,更换容量过低的电芯或模组'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                    else:
+                        pass
+                else:
+                    if soh>self.param.SohLow+2:   #soh过低故障恢复
+                        time=self.bmstime[0]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==23].index, 'end_time'] = time
+                    else:
+                        pass
+
+                if not 24 in list(self.df_diag_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if cellsoh_diff>self.param.SohDiff:
+                        time=self.bmstime[0]
+                        code=24
+                        faultlv=1
+                        faultinfo='电池包容量一致性差:电芯{}'.format(cellsoh_lowindex)
+                        faultadvice='检修电池,更换容量过低的电芯或模组'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, faultadvice]
+                    else:
+                        pass
+                else:
+                    if cellsoh_diff<self.param.SohDiff-2:
+                        time=self.bmstime[0]
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']==24].index, 'end_time'] = time
+                    else:
+                        pass
+            else:
+                pass
+        else:
+            cellsoh_diff=5
+        
+        # #电池健康度评分.....................................................................................................
+        # health_state=soh*0.6+(100-cellsoh_diff)*0.2+(100-cellsoc_diff)*0.2
+        # if health_state>100:
+        #     health_state=100
+        # elif health_state<0:
+        #     health_state=0
+        # else:
+        #     pass
+        # health_state=eval(format(health_state,'.1f'))
+
+        #返回诊断结果...........................................................................................................
+        df_res=self.df_diag_ram
+        if not df_res.empty:
+            return df_res
+        else:
+            return pd.DataFrame()

+ 213 - 0
LIB/MIDDLE/SaftyCenter/DataDiag_Static/DataStatistics.py

@@ -0,0 +1,213 @@
+import numpy as np
+import pandas as pd
+from LIB.MIDDLE.SaftyCenter.Common import FeiShuData
+from LIB.MIDDLE.SaftyCenter.Common import DBDownload
+import time, datetime
+from datetime import timedelta
+from pandas.core.frame import DataFrame
+from apscheduler.schedulers.blocking import BlockingScheduler
+
+class DataSta():
+    def __init__(self) -> None:
+        pass
+    def SaftyWarningSta(df_fltinfo,start_time,end_time,factory_info):
+        if  '骑享' in factory_info:
+            df_fltinfo=df_fltinfo[~df_fltinfo['product_id'].str.contains('TJMCL')]
+            start_time=start_time+str(' 00:00:00')
+            start_time=datetime.datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S')
+            start_time=start_time+datetime.timedelta(days=1)
+            start_time=start_time.strftime('%Y-%m-%d %H:%M:%S')
+            SftyPlt_Data_Total=len(df_fltinfo)#总报警数
+            CS_Warning_Total_Finish=df_fltinfo[df_fltinfo['batpos']==1]
+            CS_Warning_Total_Finish_Count=len(CS_Warning_Total_Finish) #平台报警总运维数
+            SftyPlt_Data_day=len(df_fltinfo[df_fltinfo['start_time']>=start_time])#日、周总报警数
+            CS_Warning_day_Finish=CS_Warning_Total_Finish[CS_Warning_Total_Finish['start_time']>=start_time]#日/周运维数
+            CS_Warning_day_Finish_Count=len(CS_Warning_day_Finish)
+            SftyPlt_EmgcyData_day=df_fltinfo[df_fltinfo['start_time']>=start_time] #紧急报警数
+            SftyPlt_EmgcyData_day=SftyPlt_EmgcyData_day[SftyPlt_EmgcyData_day['level']>3]
+            SftyPlt_EmgcyData_day_Count=len(SftyPlt_EmgcyData_day)
+            SftyPlt_EmgcyData_day_Finish=SftyPlt_EmgcyData_day[SftyPlt_EmgcyData_day['batpos']==1]
+            SftyPlt_EmgcyData_day_Finish_Count=len(SftyPlt_EmgcyData_day_Finish)
+            if int(SftyPlt_Data_day)>0:
+                OprationManageRate=round(float(CS_Warning_day_Finish_Count/SftyPlt_Data_day)*100,2)
+            else:
+                OprationManageRate=100
+            if int(SftyPlt_EmgcyData_day_Count)>0:
+                OprationManageEmgcyRate=round(float(SftyPlt_EmgcyData_day_Finish_Count/SftyPlt_EmgcyData_day_Count)*100,2)
+            else:
+                OprationManageEmgcyRate=100
+            PK504FltData_Count=len(df_fltinfo[df_fltinfo['product_id'].str.contains('PK504')])
+            PK502FltData_Count=len(df_fltinfo[df_fltinfo['product_id'].str.contains('PK502')])
+            PK500FltData_Count=len(df_fltinfo[df_fltinfo['product_id'].str.contains('PK500')])
+            MGMCLFltData_Count=len(df_fltinfo[df_fltinfo['product_id'].str.contains('MGMCL')])
+            MGMLXFltData_Count=len(df_fltinfo[df_fltinfo['product_id'].str.contains('MGMLX')])
+            # print('总报警数=',SftyPlt_Data_Total)
+            # print('总完成运维数=',CS_Warning_Total_Finish_Count)
+            # print('周报警数=',SftyPlt_Data_day)
+            # print('周完成运维数=',CS_Warning_day_Finish_Count)
+            # print('周紧急报警数=',SftyPlt_EmgcyData_day)
+            # print('周紧急完成运维数=',SftyPlt_EmgcyData_day_Finish_Count)
+            # print('周运维率=',OprationManageRate)
+            # print('周紧急运维率=',OprationManageEmgcyRate)
+            # print('PK504总报警=',PK504FltData_Count)
+            # print('PK502总报警=',PK502FltData_Count)
+            # print('PK500总报警=',PK500FltData_Count)
+            # print('格林美总报警=',MGMCLFltData_Count)
+            # print('自研总报警=',MGMLXFltData_Count)
+            FltAlarmInfo=DataFrame(columns=['SftyPlt_Data_Total','CS_Warning_Total_Finish_Count','SftyPlt_Data_day','CS_Warning_day_Finish_Count','SftyPlt_EmgcyData_day','SftyPlt_EmgcyData_day_Finish_Count','OprationManageRate','OprationManageEmgcyRate'])
+            Celltype=DataFrame(columns=['PK504','PK502','PK500','MGMCL','MGMLX'])
+            FltAlarmInfo.loc[0]=[SftyPlt_Data_Total,CS_Warning_Total_Finish_Count,SftyPlt_Data_day,CS_Warning_day_Finish_Count,SftyPlt_EmgcyData_day_Count,SftyPlt_EmgcyData_day_Finish_Count,OprationManageRate,OprationManageEmgcyRate]
+            Celltype.loc[0]=[PK504FltData_Count,PK502FltData_Count,PK500FltData_Count,MGMCLFltData_Count,MGMLXFltData_Count]
+        elif '金茂换电' in factory_info:
+            df_fltinfo=df_fltinfo[df_fltinfo['product_id'].str.contains('TJMCL')]
+            start_time=start_time+str(' 00:00:00')
+            start_time=datetime.datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S')
+            start_time=start_time+datetime.timedelta(days=1)
+            start_time=start_time.strftime('%Y-%m-%d %H:%M:%S')
+            SftyPlt_Data_Total=len(df_fltinfo)#总报警数
+            CS_Warning_Total_Finish=df_fltinfo[df_fltinfo['batpos']==1]
+            CS_Warning_Total_Finish_Count=len(CS_Warning_Total_Finish) #平台报警总运维数
+            SftyPlt_Data_day=len(df_fltinfo[df_fltinfo['start_time']>=start_time])#日、周总报警数
+            CS_Warning_day_Finish=CS_Warning_Total_Finish[CS_Warning_Total_Finish['start_time']>=start_time]#日/周运维数
+            CS_Warning_day_Finish_Count=len(CS_Warning_day_Finish)
+            SftyPlt_EmgcyData_day=df_fltinfo[df_fltinfo['start_time']>=start_time] #紧急报警数
+            SftyPlt_EmgcyData_day=SftyPlt_EmgcyData_day[SftyPlt_EmgcyData_day['level']>3]
+            SftyPlt_EmgcyData_day_Count=len(SftyPlt_EmgcyData_day)
+            SftyPlt_EmgcyData_day_Finish=SftyPlt_EmgcyData_day[SftyPlt_EmgcyData_day['batpos']==1]
+            SftyPlt_EmgcyData_day_Finish_Count=len(SftyPlt_EmgcyData_day_Finish)
+            if int(SftyPlt_Data_day)>0:
+                OprationManageRate=round(float(CS_Warning_day_Finish_Count/SftyPlt_Data_day)*100,2)
+            else:
+                OprationManageRate=100
+            if int(SftyPlt_EmgcyData_day_Count)>0:
+                OprationManageEmgcyRate=round(float(SftyPlt_EmgcyData_day_Finish_Count/SftyPlt_EmgcyData_day_Count)*100,2)
+            else:
+                OprationManageEmgcyRate=100
+            TJMCLFltData_Count=len(df_fltinfo[df_fltinfo['product_id'].str.contains('TJMCL')])
+            # print('总报警数=',SftyPlt_Data_Total)
+            # print('总完成运维数=',CS_Warning_Total_Finish_Count)
+            # print('周报警数=',SftyPlt_Data_day)
+            # print('周完成运维数=',CS_Warning_day_Finish_Count)
+            # print('周紧急报警数=',SftyPlt_EmgcyData_day)
+            # print('周紧急完成运维数=',SftyPlt_EmgcyData_day_Finish_Count)
+            # print('周运维率=',OprationManageRate)
+            # print('周紧急运维率=',OprationManageEmgcyRate)
+            # print('PK504总报警=',PK504FltData_Count)
+            # print('PK502总报警=',PK502FltData_Count)
+            # print('PK500总报警=',PK500FltData_Count)
+            # print('格林美总报警=',MGMCLFltData_Count)
+            # print('自研总报警=',MGMLXFltData_Count)
+            FltAlarmInfo=DataFrame(columns=['SftyPlt_Data_Total','CS_Warning_Total_Finish_Count','SftyPlt_Data_day','CS_Warning_day_Finish_Count','SftyPlt_EmgcyData_day','SftyPlt_EmgcyData_day_Finish_Count','OprationManageRate','OprationManageEmgcyRate'])
+            Celltype=DataFrame(columns=['TJMCL'])
+            FltAlarmInfo.loc[0]=[SftyPlt_Data_Total,CS_Warning_Total_Finish_Count,SftyPlt_Data_day,CS_Warning_day_Finish_Count,SftyPlt_EmgcyData_day_Count,SftyPlt_EmgcyData_day_Finish_Count,OprationManageRate,OprationManageEmgcyRate]
+            Celltype.loc[0]=[TJMCLFltData_Count]
+        else:
+            FltAlarmInfo=DataFrame(columns=['SftyPlt_Data_Total','CS_Warning_Total_Finish_Count','SftyPlt_Data_day','CS_Warning_day_Finish_Count','SftyPlt_EmgcyData_day','SftyPlt_EmgcyData_day_Finish_Count','OprationManageRate','OprationManageEmgcyRate'])
+            Celltype=DataFrame(columns=['None'])
+        return FltAlarmInfo,Celltype
+    def WeekInfoSta(df_fltinfo,start_time,end_time):
+        df_fltinfo=df_fltinfo[df_fltinfo['start_time']>=start_time]
+        FaultLvlCount=DataFrame(columns=['level','count'])
+        FaultLvlCount=df_fltinfo.groupby('level').count().T.head(1).T
+        FaultLvlCount=FaultLvlCount.reset_index(drop=False)
+        
+        return FaultLvlCount
+    def SftyWrngClsfy(df_fltinfo):
+        DsnSaftyCode=[]
+        DataSaftyCode=[]
+        SamplingSaftyCode=[2,10,50,53,54,55,56]
+        HvSaftyCode=[]
+        SysSaftyCode=[]
+        CellSaftyCode=[3,5,6,7,8,9,10,11,13,14,15,16,17,19,20,21,15,16,17,19,23,24,25,26,29,30,31,51,119]
+        CtrlSatyCode=[4,12,18,20,21,22,57]
+        StateSaftyCode=[27,28,52]
+        
+        DsnSaftyCodeCount=len(df_fltinfo[df_fltinfo['code'].isin(DsnSaftyCode)])
+        DataSaftyCodeCount=len(df_fltinfo[df_fltinfo['code'].isin(DataSaftyCode)])
+        HvSaftyCodeCount=len(df_fltinfo[df_fltinfo['code'].isin(HvSaftyCode)])
+        SysSaftyCodeCount=len(df_fltinfo[df_fltinfo['code'].isin(SysSaftyCode)])
+        SamplingSatyCount=len(df_fltinfo[df_fltinfo['code'].isin(SamplingSaftyCode)])
+        CellSaftyCount=len(df_fltinfo[df_fltinfo['code'].isin(CellSaftyCode)])
+        CtrlSaftyCodeCount=len(df_fltinfo[df_fltinfo['code'].isin(CtrlSatyCode)])
+        StateSaftyCodeCount=len(df_fltinfo[df_fltinfo['code'].isin(StateSaftyCode)])
+        SatftyCount=DataFrame(columns=['DsnSaftyCodeCount','DataSaftyCodeCount','HvSaftyCodeCount','SysSaftyCodeCount','SamplingSatyCount','CellSaftyCount','CtrlSaftyCodeCount','StateSaftyCodeCount'])
+        SatftyCount.loc[0]=[DsnSaftyCodeCount,DataSaftyCodeCount,HvSaftyCodeCount,SysSaftyCodeCount,SamplingSatyCount,CellSaftyCount,CtrlSaftyCodeCount,StateSaftyCodeCount]
+        # print('设计安全分数为',DsnSaftyCodeCount)
+        # print('数据安全分数为',DataSaftyCodeCount)
+        # print('高压安全分数为',HvSaftyCodeCount)
+        # print('系统安全分数为',SysSaftyCodeCount)
+        # print('采样安全分数为',SamplingSatyCount)
+        # print('电芯安全分数为',CellSaftyCount)
+        # print('控制安全分数为',CtrlSaftyCodeCount)
+        # print('状态安全分数为',StateSaftyCodeCount)
+        # print('---------------------------------------------------------------------------')
+        return SatftyCount
+    def AccumInfo(df_last_accum,df_FirstDataTime,end_time,factory_info):
+        if '骑享' in factory_info:
+            df_last_accum=df_last_accum[~df_last_accum['devcode'].str.contains('TJMCL')]
+            MaxAccumAh=df_last_accum['dsg_ahaccum'].max()
+            TotalAccumAh=df_last_accum['dsg_ahaccum'].sum()
+            PK504_Ah=df_last_accum[df_last_accum['devcode'].str.contains('PK504')]
+            PK504_Ah_Max=(PK504_Ah['dsg_ahaccum'].max())/60
+            PK502_Ah=df_last_accum[df_last_accum['devcode'].str.contains('PK502')]
+            PK502_Ah_Max=(PK502_Ah['dsg_ahaccum'].max())/40
+            PK500_Ah=df_last_accum[df_last_accum['devcode'].str.contains('PK500')]
+            PK500_Ah_Max=(PK500_Ah['dsg_ahaccum'].max())/40
+            MGMCL_Ah=df_last_accum[df_last_accum['devcode'].str.contains('MGMCL')]
+            MGMCL_Ah_Max=(MGMCL_Ah['dsg_ahaccum'].max())/50
+            MGMLX_Ah=df_last_accum[df_last_accum['devcode'].str.contains('MGMLX')]
+            MGMLX_Ah_Max=(MGMLX_Ah['dsg_ahaccum'].max())/50
+            MaxCycle=round(max(PK504_Ah_Max,PK502_Ah_Max,PK500_Ah_Max,MGMCL_Ah_Max,MGMLX_Ah_Max),0)
+            deltatimeMax=0
+            deltatime=DataFrame(columns=['runningdate'])
+            df_FirstDataTime=df_FirstDataTime[~df_FirstDataTime['sn'].str.contains('TJMCL')]
+            df_FirstDataTime=df_FirstDataTime.dropna(how='any')
+            df_FirstDataTime=df_FirstDataTime.reset_index(drop=False)
+            for i in range(0,len(df_FirstDataTime)):
+                End_time=datetime.datetime.now()
+                df_FirstDataTime.loc[i,'first_data_time']=df_FirstDataTime.loc[i,'first_data_time'].strftime('%Y-%m-%d')
+                df_FirstDataTime.loc[i,'first_data_time']=datetime.datetime.strptime(df_FirstDataTime.loc[i,'first_data_time'],'%Y-%m-%d')
+                deltatime.loc[i,'runningdate']=(End_time-df_FirstDataTime.loc[i,'first_data_time'])
+                deltatime.loc[i,'runningdate']= deltatime.loc[i,'runningdate'].total_seconds()/3600
+            MaxRunningHour=int(deltatime['runningdate'].max())
+            TotalRunHour=int(deltatime['runningdate'].sum())
+        elif '金茂换电' in factory_info:
+            df_last_accum=df_last_accum[df_last_accum['devcode'].str.contains('TJMCL')]
+            df_last_accum=df_last_accum[df_last_accum['dsg_ahaccum']<429496729]
+            MaxAccumAh=df_last_accum['dsg_ahaccum'].max()
+            TotalAccumAh=df_last_accum['dsg_ahaccum'].sum()
+            TJMCL_Ah=df_last_accum[df_last_accum['devcode'].str.contains('TJMCL')]
+            TJMCL_Ah_Max=(TJMCL_Ah['dsg_ahaccum'].max())/(228*2)
+            MaxCycle=round(TJMCL_Ah_Max,0)
+            deltatimeMax=0
+            deltatime=DataFrame(columns=['runningdate'])
+            df_FirstDataTime=df_FirstDataTime[df_FirstDataTime['sn'].str.contains('TJMCL')]
+            df_FirstDataTime=df_FirstDataTime.dropna(how='any')
+            df_FirstDataTime=df_FirstDataTime.reset_index(drop=False)
+            for i in range(0,len(df_FirstDataTime)):
+                End_time=datetime.datetime.now()
+                df_FirstDataTime.loc[i,'first_data_time']=df_FirstDataTime.loc[i,'first_data_time'].strftime('%Y-%m-%d')
+                df_FirstDataTime.loc[i,'first_data_time']=datetime.datetime.strptime(df_FirstDataTime.loc[i,'first_data_time'],'%Y-%m-%d')
+                deltatime.loc[i,'runningdate']=(End_time-df_FirstDataTime.loc[i,'first_data_time'])
+                deltatime.loc[i,'runningdate']= deltatime.loc[i,'runningdate'].total_seconds()/3600
+            MaxRunningHour=int(deltatime['runningdate'].max())
+            TotalRunHour=int(deltatime['runningdate'].sum())
+        return MaxAccumAh,TotalAccumAh,MaxCycle,MaxRunningHour,TotalRunHour
+
+    def FltBatPosition(df_last_pos,df_fltinfo,factory_info):
+        df_Diag_Ram=df_fltinfo[df_fltinfo['batpos']==0].drop_duplicates(subset=['product_id'],keep=False)
+        all_location_info=DataFrame(columns=['factory','product_id','lat','lon'])
+        df_last_pos.rename(columns={'devcode':'product_id'},inplace=True)
+        df_Diag_Ram_Pos_a=pd.concat([df_Diag_Ram,df_last_pos]).drop_duplicates(subset=['product_id'],keep='first')
+        df_Diag_Ram_Pos_b=pd.concat([df_Diag_Ram,df_last_pos]).drop_duplicates(subset=['product_id'],keep=False)
+        df_Diag_Ram_Pos=df_Diag_Ram_Pos_a.append(df_Diag_Ram_Pos_b).drop_duplicates(subset=['product_id'],keep=False)
+        df_Diag_Ram_Pos=df_Diag_Ram_Pos.reset_index(drop=True)
+        for i in range(0,len(df_Diag_Ram_Pos)):
+            df_last_pos_list=df_last_pos['product_id'].values.tolist()
+            if  str(df_Diag_Ram_Pos.loc[i,'product_id']) in df_last_pos_list:
+                pos_la_temp=df_last_pos[df_last_pos['product_id']==(df_Diag_Ram_Pos.loc[i,'product_id'])].reset_index(drop=True)
+                all_location_info.loc[i,'lat']=pos_la_temp.loc[0,'latitude']
+                all_location_info.loc[i,'lon']=pos_la_temp.loc[0,'longitude']
+                all_location_info.loc[i,'factory']=factory_info
+                all_location_info.loc[i,'product_id']=df_Diag_Ram_Pos.loc[i,'product_id']
+        return all_location_info

+ 36 - 0
LIB/MIDDLE/SaftyCenter/DataDiag_Static/DiagDataMerge.py

@@ -0,0 +1,36 @@
+import pandas as pd
+import datacompy
+
+
+class DiagDataMerge():
+    def DetaMerge(df_Diag_Ram_sn,df_Diag_Batdiag_update,df_OprtnSta,df_Diag_Ram_sn_else):
+        df_Diag_Ram_add = pd.DataFrame()
+        df_Diag_Ram_Update_change = pd.DataFrame()
+        
+        df_Diag_Cal_finish = pd.DataFrame(columns=['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice','Batpos'])
+        df_Diag_Cal_unfinish = pd.DataFrame(columns=['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice','Batpos'])
+        print('处理前',df_Diag_Cal_unfinish)
+        if not df_Diag_Batdiag_update.empty:
+            #------------------------------合并两者故障,并将同一sn号下的车辆故障放一起----------------------------------------------
+            df_Diag_Cal_finish = df_Diag_Batdiag_update[df_Diag_Batdiag_update['end_time'] != '0000-00-00 00:00:00']
+            df_Diag_Cal_unfinish = df_Diag_Batdiag_update[df_Diag_Batdiag_update['end_time'] == '0000-00-00 00:00:00']
+            df_Diag_Cal_finish['Batpos'] = [1]*len(df_Diag_Cal_finish)
+            if len(df_OprtnSta):
+                if df_OprtnSta.loc[0,'status'] !=1:#0禁用 1正常 2故障 3返修 4 损毁 5丢失已赔偿,6丢失未赔偿
+                    if df_Diag_Cal_unfinish['level'].max()>3:
+                        if df_OprtnSta.loc[0,'status'] ==3:
+                            df_Diag_Cal_unfinish['Batpos'] = [1]*len(df_Diag_Cal_unfinish)
+                        else:
+                            df_Diag_Cal_unfinish['Batpos'] = [0]*len(df_Diag_Cal_unfinish)
+                    else:
+                        df_Diag_Cal_unfinish['Batpos'] = [1]*len(df_Diag_Cal_unfinish)
+                else:
+                    df_Diag_Cal_unfinish['Batpos'] = [0]*len(df_Diag_Cal_unfinish)
+                    
+        print('处理后',df_Diag_Cal_unfinish)
+        df_Diag_Cal_Update=pd.concat([df_Diag_Cal_finish,df_Diag_Cal_unfinish])    
+        df_Diag_Ram_add = pd.concat([df_Diag_Cal_Update,df_Diag_Ram_sn,df_Diag_Ram_sn]).drop_duplicates(subset=['start_time','code'],keep=False)#此次判断中新增故障
+        df_Diag_Ram_Update_old = pd.concat([df_Diag_Cal_Update,df_Diag_Ram_add,df_Diag_Ram_add]).drop_duplicates(subset=['start_time','code'],keep=False)#此次判断中新增故障
+        df_Diag_Ram_Update_change = pd.concat([df_Diag_Ram_Update_old,df_Diag_Ram_sn,df_Diag_Ram_sn]).drop_duplicates(subset=['start_time','end_time','code','Batpos'],keep=False)#此次判断中新增故障
+        # df_Diag_Ram = pd.concat([df_Diag_Ram_sn_else,df_Diag_Cal_unfinish])    
+        return df_Diag_Ram_add,df_Diag_Ram_Update_change

+ 115 - 0
LIB/MIDDLE/SaftyCenter/DataDiag_Static/SC_BMSUploadError.py

@@ -0,0 +1,115 @@
+import sys
+import numpy as np
+import pandas as pd
+import string
+import os
+from pandas import Series
+from matplotlib import pyplot as plt
+from pandas.core.frame import DataFrame
+from pandas.core.indexes.base import Index
+from LIB.BACKEND import DBManager
+import datetime
+import time
+import string
+import re
+import math
+
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+from urllib import parse
+import pymysql
+
+class BMSReportError:
+    def __init__(self):
+        pass
+    def main(sn,bms_info,df_Diag_Ram_in,FactoryType,errorcode_map):
+        newCode_total=[]
+        if len(df_Diag_Ram_in):
+            df_Diag_Ram_in['code']=df_Diag_Ram_in['code'].apply(str)
+            df_Diag_Ram_BMS=df_Diag_Ram_in[df_Diag_Ram_in['code'].str.contains('B')].copy()
+            df_Diag_Ram_Cloud=df_Diag_Ram_in[df_Diag_Ram_in['code'].str.contains('C')].copy()
+        else:
+            df_Diag_Ram_BMS=DataFrame(columns=['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice'])
+            df_Diag_Ram_Cloud=DataFrame(columns=['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice'])
+        if len(bms_info):
+            df_bms=bms_info[['时间戳','故障等级','故障代码']]
+            newCode=[]
+            for i in range (0,len(df_bms)):
+                #拆解故障码,PK专用
+                if FactoryType ==1:
+                    code=df_bms.loc[i,'故障代码']
+                    newCode=[]
+                    if not pd.isnull(code):
+                        if code>2:
+                            num=math.log(code)/math.log(2)
+                            power=int(num)
+                            nCode=int(math.pow(2,power))
+                            newCode.append(nCode)
+                            code=code-nCode
+                            while(code>0):
+                                num=float(math.log(code)/math.log(2))
+                                power=int(num)
+                                nCode=int(math.pow(2,power))
+                                newCode.append(nCode)
+                                code=code-nCode
+                        elif code>0:
+                            newCode.append(code)
+                        else:
+                            newCode=[]
+                    else:
+                        newCode=[]
+                else:
+                    newCode=df_bms.loc[i,'故障代码']
+                    if not pd.isnull(newCode) and newCode!=0:
+                        newCode=[newCode]
+                    else:
+                        newCode=[]                        
+                        
+            # 数据库配置
+
+                if  len(newCode):
+                    for j in range(0,len(newCode)):
+                        # host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+                        # port=3306
+                        # database='algo_dict'
+                        # user='qx_algo_readonly'
+                        # password='qx@123456'
+
+                        # db_engine = create_engine("mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(user, password, host, port, database))
+                        # DbSession = sessionmaker(bind=db_engine)
+
+                        # errorcode_map = pd.read_sql("select * from faultcode_map", db_engine)
+                        # if 'K50' in sn:
+                        #     FactoryType=1
+                        # elif 'MGMCL' in sn or 'UD' in sn:
+                        #     FactoryType=2
+                        # else:
+                        #     FactoryType=3
+                        
+                             
+                        code = newCode[j] # 终端故障码
+                        platform_code = errorcode_map[(errorcode_map['protocol']==FactoryType)&(errorcode_map['end_errorcode']==str(code))]['platform_errorcode']                   
+
+                        # db_engine.dispose()
+                        if len(platform_code) == 0:
+                            pass
+                        else:                       
+                            newCode_total.append(platform_code.values[0])
+                            newCode[j]=platform_code.values[0]
+                            if not platform_code.values[0] in df_Diag_Ram_BMS['code'].values.tolist():
+                                df_Diag_Ram_BMS.loc[len(df_Diag_Ram_BMS)]=[df_bms.loc[i,'时间戳'],'0000-00-00 00:00:00',sn,platform_code.values[0],df_bms.loc[i,'故障等级'],'','']
+                            else:
+                                pass
+        if len(df_Diag_Ram_BMS):
+            df_Diag_Ram_BMS=df_Diag_Ram_BMS.reset_index(drop=True)
+            for k in range(0,len(df_Diag_Ram_BMS)):
+                newCode_total=list(set(newCode_total))
+                if not df_Diag_Ram_BMS.loc[k,'code'] in newCode_total:
+                    end_time=datetime.datetime.now()
+                    end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                    df_Diag_Ram_BMS.loc[k,'end_time']=end_time
+                else:
+                    pass
+        df_Diag_Ram=df_Diag_Ram_BMS.append(df_Diag_Ram_Cloud)
+        df_Diag_Ram=df_Diag_Ram.reset_index(drop=True)
+        return df_Diag_Ram

+ 73 - 0
LIB/MIDDLE/SaftyCenter/DataDiag_Static/SC_CtrlSafty.py

@@ -0,0 +1,73 @@
+import sys
+import numpy as np
+import pandas as pd
+import string
+import os
+from pandas import Series
+from matplotlib import pyplot as plt
+from pandas.core.frame import DataFrame
+from pandas.core.indexes.base import Index
+from LIB.BACKEND import DBManager
+import datetime
+import time
+import string
+import re
+
+
+
+class CtrlSafty:
+    def __init__(self):
+        pass
+    def main(sn,param,bms_info,df_Diag_Ram_in):
+        df_Diag_Ram_Update_inside=DataFrame(columns=['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice'])
+        df_Diag_Ram_Update_inside=df_Diag_Ram_Update_inside.append(df_Diag_Ram_in)
+        global QuitErrCount 
+        VolStarkCount=[0 for i in range(param.CellVoltNums)]
+        VolCount=0
+        QuitErrCount=[0 for i in range(param.FaultCount)]
+
+        #--------------该电池的所有当前故障-------------
+
+
+
+
+        df_Diag_Ram_Update_inside=CtrlSafty.TempCtrlDiag(sn,bms_info,param,df_Diag_Ram_Update_inside) 
+        return df_Diag_Ram_Update_inside
+
+
+
+    def TempCtrlDiag(sn,bms_info,param,df_Diag_Ram):
+        ErrorFlg=0
+        CellTempMax=0
+        OtherTempMax=0
+
+        CellTempNum=['单体温度'+str(i) for i in range(1,param.CellTempNums+1)]
+        if 'MXMLX' in sn:
+            OtherTempNum=['其他温度1','其他温度3','其他温度4','其他温度5']
+        else:
+            OtherTempNum=['其他温度'+str(i) for i in range(1,param.OtherTempNums+1)]
+        
+        CellTempMax=bms_info[bms_info[CellTempNum]>(param.CellMaxUSBTemp-5)]
+        CellTempMax=CellTempMax[CellTempMax<(param.CellMaxUSBTemp+5)].dropna(axis=0,how='all')
+        OtherTempMax=bms_info[bms_info[OtherTempNum]>(param.OtherOTlmt-10)]
+        OtherTempMax=OtherTempMax[OtherTempMax[OtherTempNum]<(param.OtherOTlmt+5)].dropna(axis=0,how='all')
+        if len(CellTempMax) and len(OtherTempMax):
+            QuitErrCount[57]=0
+            if not 'C357' in df_Diag_Ram['code'].values.tolist():#如果故障发生当前故障中没有该故障,则压入该故障
+                df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_info.loc[len(bms_info)-1,'时间戳'],'0000-00-00 00:00:00',sn,'C357',3,'电芯温度高','立即联系用户确认电池状态']
+                ErrorFlg=1            
+            else:#如果故障发生当前故障中有该故障,则不进行操作
+                pass
+        else:
+            if 'C357' in df_Diag_Ram['code'].values.tolist():
+                QuitErrCount[57]=QuitErrCount[57]+1
+                if QuitErrCount[57]>3:
+                    QuitErrCount[57]=4
+                    end_time=datetime.datetime.now()
+                    end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                    df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C357'].index,['end_time']]=end_time
+                else:
+                    pass
+            else:
+                pass
+        return df_Diag_Ram

+ 360 - 0
LIB/MIDDLE/SaftyCenter/DataDiag_Static/SC_SamplingSafty.py

@@ -0,0 +1,360 @@
+import sys
+import numpy as np
+import pandas as pd
+import string
+import os
+from pandas import Series
+from matplotlib import pyplot as plt
+from pandas.core.frame import DataFrame
+from pandas.core.indexes.base import Index
+from LIB.BACKEND import DBManager
+import datetime
+import time
+import string
+import re
+
+
+
+class SamplingSafty:
+    def __init__(self):
+        pass
+    def main(sn,param,bms_info,df_Diag_Ram_in):
+        df_Diag_Ram_Update_inside=DataFrame(columns=['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice'])
+        df_Diag_Ram_Update_inside=df_Diag_Ram_Update_inside.append(df_Diag_Ram_in)
+        global QuitErrCount 
+        VolStarkCount=[0 for i in range(param.CellVoltNums)]
+        VolCount=0
+        QuitErrCount=[0 for i in range(param.FaultCount)]
+
+        #--------------该电池的所有当前故障-------------
+
+
+
+        for i in range(0,len(bms_info)):
+            if i==0:
+                bms_infoP=bms_info.loc[i]
+            elif len(bms_info)>=1:
+                    bms_infoP=bms_info.loc[i-1]
+            df_Diag_Ram_Update_inside,VolStarkCount,VolCount=SamplingSafty.VoltSamplingDiag(sn,bms_info.loc[i],bms_infoP,param,VolStarkCount,VolCount,df_Diag_Ram_Update_inside)   
+            df_Diag_Ram_Update_inside=SamplingSafty.TempSamplingDiag(sn,bms_info.loc[i],bms_infoP,param,df_Diag_Ram_Update_inside)
+                # FltInfo=SamplingSafty.CrntSamplingDiag(sn,bms_info.loc[i],FltInfo,param)
+        return df_Diag_Ram_Update_inside
+
+
+
+    def VoltSamplingDiag(sn,bms_infoN,bms_infoP,param,VolStarkCount,VolCount,df_Diag_Ram):
+        InVMaxBatNo=[]
+        InVMinBatNo=[]
+        StackVolNo=[]
+        OutlierVolNo=[]
+        ErrorFlg=0
+        #——————————————————————取最高最低电压————————————————————————————————
+
+        VoltageNum=['单体电压'+str(i) for i in range(1,param.CellVoltNums+1)]
+        CellVoltage=bms_infoN[VoltageNum]/1000
+        CellVoltageP=bms_infoP[VoltageNum]/1000
+        MaxVolt=CellVoltage.max()
+        MinVolt=CellVoltage.min()
+        MaxVoltNum=CellVoltage[MaxVolt==CellVoltage].index
+        MinVoltNum=CellVoltage[MinVolt==CellVoltage].index
+        InVMaxBatNo=CellVoltage[CellVoltage>=param.CellOVlmt].index
+        InVMinBatNo=CellVoltage[CellVoltage<=param.CellUVlmt].index
+        if param.CellVoltNums>2:
+            AvgVol=(CellVoltage.sum()-MaxVolt-MinVolt)/(param.CellVoltNums-2)
+        else:
+            AvgVol=CellVoltage.mean()
+        #—————————————————————————————电压无效和断线判断———————————————————————
+        if len(InVMaxBatNo):
+            if len(InVMinBatNo):
+                QuitErrCount[10]=0
+                if not 'C310' in df_Diag_Ram['code'].values.tolist():#如果故障发生当前故障中没有该故障,则压入该故障
+                    df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,'C310',3,'电池电压采样断线,最高电压为{:.2f}V,电池编号为{},最低电压为{:.2f}V,电池编号为{}'.format(MaxVolt,InVMaxBatNo.values,MinVolt,InVMinBatNo.values),'返厂维修']
+                    ErrorFlg=1  
+                else:#如果故障发生当前故障中有该故障,则不进行操作
+                    pass                 
+            else:#如果没有故障,并且当前故障表中有该故障,则判断故障是否结束
+                QuitErrCount[50]=0
+                if 'C310' in df_Diag_Ram['code'].values.tolist():
+                    QuitErrCount[10]=QuitErrCount[10]+1
+                    if QuitErrCount[10]>3:
+                        QuitErrCount[10]=4
+                        end_time=datetime.datetime.now()
+                        end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                        df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C310'].index,['end_time']]=end_time
+                    else:
+                        pass
+                else:
+                    pass
+                if not 'C350' in df_Diag_Ram['code'].values.tolist():
+                    df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,'C350',3,str(InVMaxBatNo.values)+'号电压大于{:.2f}V,采样无效'.format(param.CellOVlmt),'返厂维修']
+                    ErrorFlg=1
+                else:
+                    pass
+        elif len(InVMinBatNo):
+            QuitErrCount[50]=0
+            if not 'C350' in df_Diag_Ram['code'].values.tolist():
+                df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,'C350',3,str(InVMinBatNo.values)+'号电压小于{:.2f}V,采样无效'.format(param.CellUVlmt),'返厂维修']
+                ErrorFlg=1
+            else:
+                pass
+        else:
+            if 'C350' in df_Diag_Ram['code'].values.tolist():
+                QuitErrCount[50]=QuitErrCount[50]+1
+                if QuitErrCount[50]>3:
+                    QuitErrCount[50]=4
+                    end_time=datetime.datetime.now()
+                    end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                    df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']==50].index,['end_time']]=end_time                   
+                else:
+                    pass
+            else:
+                pass                                      
+       #——————————————————————————————————电压卡滞和离群判断—————————————————————————
+        if ErrorFlg==0:
+            AvgVolGap=CellVoltage-AvgVol
+            OutlierVolNo=AvgVolGap[AvgVolGap>=param.AvgVolGap].index
+            if len(OutlierVolNo) and abs(bms_infoN['总电流[A]'])<2 and not 'C251' in df_Diag_Ram['code']:
+                VolCount=VolCount+1
+                QuitErrCount[51]=0
+                if VolCount>10:
+                    VolCount=11
+                    if not 'C251' in df_Diag_Ram['code'].values.tolist():#如果故障发生当前故障中没有该故障,则压入该故障
+                        df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,'C251',2,str(OutlierVolNo.values)+'号电池电压离群','技术介入诊断']
+                        ErrorFlg=1            
+                    else:#如果故障发生当前故障中有该故障,则不进行操作
+                        pass                
+            else:
+                VolCount=0
+                if 'C251' in df_Diag_Ram['code'].values.tolist():
+                    QuitErrCount[51]=QuitErrCount[51]+1
+                    if QuitErrCount[51]>3:
+                        QuitErrCount[51]=4
+                        end_time=datetime.datetime.now()
+                        end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                        df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C251'].index,['end_time']]=end_time                
+                    else:
+                        pass
+                else:
+                    pass
+            # -------------------卡滞逻辑未加-------------------------------------
+            # if (abs(float(bms_infoN['总电流[A]']))>=10 and not 'PK504' in sn) or (abs(float(bms_infoN['总电流[A]']))>=15 and 'PK504' in sn):
+            #     StackVolNo=CellVoltage[abs(CellVoltage-CellVoltageP)<=0.0001].index
+            #     NotStackVolNo=CellVoltage[abs(CellVoltage-CellVoltageP)>0.0001].index
+            #     if len(StackVolNo) and not 52 in df_Diag_Ram['code']:
+            #         StackVolNo=[int(s) for s in StackVolNo.str.replace(r'[^0-9]','').tolist()]
+            #         NotStackVolNo=[int(s) for s in NotStackVolNo.str.replace(r'[^0-9]','').tolist()]
+            #         for i in StackVolNo:
+            #             VolStarkCount[i-1]=VolStarkCount[i-1]+1
+            #         for i in NotStackVolNo:
+            #             VolStarkCount[i-1]=0
+            #     if [s for s in VolStarkCount]>10:
+            #         StacVolNo=
+
+        return df_Diag_Ram,VolStarkCount,VolCount
+
+    def TempSamplingDiag(sn,bms_infoN,bms_infoP,param,df_Diag_Ram):
+        InVMaxBatNo=[]
+        InVMinBatNo=[]
+        StackVolNo=[]
+        OutlierVolNo=[]
+        ErrorFlg=0
+        #——————————————————————Cell取最高最低温度————————————————————————————————
+        TempNum=['单体温度'+str(i) for i in range(1,param.CellTempNums+1)]
+        CellTemp=bms_infoN[TempNum]
+        CellTempP=bms_infoP[TempNum]
+        maxCellTemp=CellTemp.max()
+        minCellTemp=CellTemp.min()
+        MaxVoltNum=CellTemp[maxCellTemp==CellTemp].index
+        MinVoltNum=CellTemp[minCellTemp==CellTemp].index
+        InVMaxTempBatNo=CellTemp[CellTemp>=param.PackOTlmt].index
+        InVMinTempBatNo=CellTemp[CellTemp<=param.PackUTlmt].index
+        if param.CellTempNums>2:
+            AvgCellTemp=(CellTemp.sum()-maxCellTemp-minCellTemp)/(param.CellTempNums-2)
+        else:
+            AvgCellTemp=CellTemp.mean()        
+        #——————————————————————温度无效,离群和断线判断———————————————————————
+        if len(InVMaxTempBatNo):
+            if len(InVMinTempBatNo):
+                QuitErrCount[53]=0
+                if not 'C353' in df_Diag_Ram['code'].values.tolist():#如果故障发生当前故障中没有该故障,则压入该故障
+                    df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,'C353',3,'电池温度采样断线,最高温度为{}℃,电池编号为{},最低温度为{}℃,电池编号为{}'.format(maxCellTemp,InVMaxTempBatNo.values,minCellTemp,InVMinTempBatNo.values),'返厂维修']
+                    ErrorFlg=1 
+                else:#如果故障发生当前故障中有该故障,则不进行操作
+                    pass                 
+            else:#如果没有故障,并且当前故障表中有该故障,则判断故障是否结束
+                QuitErrCount[2]=0
+                if 'C353' in df_Diag_Ram['code'].values.tolist():
+                    QuitErrCount[53]=QuitErrCount[53]+1
+                    if QuitErrCount[53]>3:
+                        QuitErrCount[53]=4
+                        end_time=datetime.datetime.now()
+                        end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                        df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C353'].index,['end_time']]=end_time
+                    else:
+                        pass
+                else:
+                    pass
+                if not 'C302' in df_Diag_Ram['code'].values.tolist():
+                    df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,'C302',3,str(InVMaxTempBatNo.values)+'号温度大于{}℃,采样无效'.format(param.PackOTlmt),'联系用户核实电池温度情况,并返厂维修']
+                    ErrorFlg=1
+                else:
+                    pass
+        elif len(InVMinTempBatNo):
+            QuitErrCount[2]=0
+            if not 'C302' in df_Diag_Ram['code'].values.tolist():
+                df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,'C302',3,str(InVMinTempBatNo.values)+'号温度小于{}℃,采样无效'.format(param.PackUTlmt),'返厂维修']
+                ErrorFlg=1
+            else:
+                pass
+        else:
+            if 'C302' in df_Diag_Ram['code'].values.tolist():
+                QuitErrCount[2]=QuitErrCount[2]+1
+                if QuitErrCount[2]>3:
+                    QuitErrCount[2]=4
+                    end_time=datetime.datetime.now()
+                    end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                    df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C302'].index,['end_time']]=end_time                   
+                else:
+                    pass
+            else:
+                pass             
+        if ErrorFlg==0:
+            AvgCellTempGap=abs(CellTemp-AvgCellTemp)
+            OutlierTempNo=AvgCellTempGap[AvgCellTempGap>=param.AvgCellTempGap].index
+            if len(OutlierTempNo):
+                QuitErrCount[8]=0
+                if not 'C108' in df_Diag_Ram['code'].values.tolist():#如果故障发生当前故障中没有该故障,则压入该故障
+                    df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,'C108',1,str(OutlierTempNo.values)+'号电池异常温度离群','技术介入诊断']
+                    ErrorFlg=1            
+                else:#如果故障发生当前故障中有该故障,则不进行操作
+                    pass
+            else:
+                if 'C108' in df_Diag_Ram['code'].values.tolist():
+                    QuitErrCount[8]=QuitErrCount[8]+1
+                    if QuitErrCount[8]>3:
+                        QuitErrCount[8]=4
+                        end_time=datetime.datetime.now()
+                        end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                        df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C108'].index,['end_time']]=end_time
+                    else:
+                        pass
+                else:
+                    pass
+        #——————————————————————————————————OtherTemp————————————————————————————————————
+        if param.OtherTempNums>0:
+            OtherTempNum=['其他温度'+str(i) for i in range(1,param.OtherTempNums+1)] 
+            OtherTemp=bms_infoN[OtherTempNum]
+            if 'MGMLX' in sn:
+                OtherTemp['其他温度2']=OtherTemp['其他温度1']
+            else:
+                pass
+            OtherTemppP=bms_infoP[OtherTempNum]
+            maxOtherTemp=OtherTemp.max()
+            minOtherTemp=OtherTemp.min()
+            MaxVoltNum=OtherTemp[maxOtherTemp==OtherTemp].index
+            MinVoltNum=OtherTemp[minOtherTemp==OtherTemp].index
+            InVMaxOtherTempBatNo=OtherTemp[OtherTemp>=param.OtherOTlmt].index
+            InVMinOtherTempBatNo=OtherTemp[OtherTemp<=param.OtherUTlmt].index
+            if param.OtherTempNums>2:
+                AvgOtherTemp=(OtherTemp.sum()-maxOtherTemp-minOtherTemp)/(param.OtherTempNums-2)
+            else:
+                AvgOtherTemp=OtherTemp.mean()        
+            #——————————————————————温度无效,离群和断线判断———————————————————————
+            if len(InVMaxOtherTempBatNo):
+                if len(InVMinOtherTempBatNo):
+                    QuitErrCount[54]=0
+                    if not 'C354' in df_Diag_Ram['code'].values.tolist():#如果故障发生当前故障中没有该故障,则压入该故障
+                        df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,'C354',3,'其他温度采样断线,最高温度为{}℃,传感器编号为{},最低温度为{}℃,传感器编号为{}'.format(maxOtherTemp,InVMaxOtherTempBatNo.values,minOtherTemp,InVMinOtherTempBatNo.values),'返厂维修']
+                        ErrorFlg=1 
+                    else:#如果故障发生当前故障中有该故障,则不进行操作
+                        pass                 
+                else:#如果没有故障,并且当前故障表中有该故障,则判断故障是否结束
+                    QuitErrCount[55]=0
+                    if 'C354' in df_Diag_Ram['code'].values.tolist():
+                        QuitErrCount[54]=QuitErrCount[54]+1
+                        if QuitErrCount[54]>3:
+                            QuitErrCount[54]=4
+                            end_time=datetime.datetime.now()
+                            end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                            df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C354'].index,['end_time']]=end_time
+                        else:
+                            pass
+                    else:
+                        pass
+                    if not 'C355' in df_Diag_Ram['code'].values.tolist():
+                        df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,'C355',3,'传感器温度大于{}℃,采样无效'.format(param.OtherOTlmt),'联系用户核实电池温度情况,并返厂维修']
+                        ErrorFlg=1
+                    else:
+                        pass
+            elif len(InVMinTempBatNo):
+                QuitErrCount[55]=0
+                if not 'C355' in df_Diag_Ram['code'].values.tolist():
+                    df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,'C355',3,'传感器温度小于{}℃,采样无效'.format(param.OtherUTlmt),'返厂维修']
+                    ErrorFlg=1
+                else:
+                    pass
+            else:
+                if 'C355' in df_Diag_Ram['code'].values.tolist():
+                    QuitErrCount[55]=QuitErrCount[55]+1
+                    if QuitErrCount[55]>3:
+                        QuitErrCount[55]=4
+                        end_time=datetime.datetime.now()
+                        end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                        df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C355'].index,['end_time']]=end_time                   
+                    else:
+                        pass
+                else:
+                    pass             
+            if ErrorFlg==0:
+                AvgOtherTempGap=abs(OtherTemp-AvgOtherTemp)
+                OutlierOtherTempNo=AvgOtherTempGap[AvgOtherTempGap>=param.AvgOtherTempGap].index
+                if len(OutlierOtherTempNo):
+                    QuitErrCount[56]=0
+                    if not 'C156' in df_Diag_Ram['code'].values.tolist():#如果故障发生当前故障中没有该故障,则压入该故障
+                        df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,'C156',1,'传感器温度异常离群','技术介入诊断']
+                        ErrorFlg=1            
+                    else:#如果故障发生当前故障中有该故障,则不进行操作
+                        pass
+                else:
+                    if 'C156' in df_Diag_Ram['code'].values.tolist():
+                        QuitErrCount[56]=QuitErrCount[56]+1
+                        if QuitErrCount[56]>3:
+                            QuitErrCount[56]=4
+                            end_time=datetime.datetime.now()
+                            end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                            df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C156'].index,['end_time']]=end_time
+                        else:
+                            pass
+                    else:
+                        pass
+
+                if (maxOtherTemp-maxCellTemp)>param.AvgOtherTempGap and  maxOtherTemp<param.OtherOTlmt and maxCellTemp<param.PackOTlmt:
+                    QuitErrCount[56]=0
+                    if not 'C156' in df_Diag_Ram['code'].values.tolist():
+                        df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,'C156',1,'传感器温度异常离群','技术立即介入诊断']
+                        ErrorFlg=1
+                else:
+                    if 'C156' in df_Diag_Ram['code'].values.tolist():
+                            QuitErrCount[56]=QuitErrCount[56]+1
+                            if QuitErrCount[56]>3:
+                                QuitErrCount[56]=4
+                                end_time=datetime.datetime.now()
+                                end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                                df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C156'].index,['end_time']]=end_time
+                                
+                if (maxCellTemp-maxOtherTemp)>param.AvgOtherTempGap and  maxOtherTemp<param.OtherOTlmt and maxCellTemp<param.PackOTlmt and not 8 in df_Diag_Ram['code']:
+                    QuitErrCount[8]=0
+                    if not 'C108' in df_Diag_Ram['code'].values.tolist():
+                        df_Diag_Ram.loc[len(df_Diag_Ram)]=[bms_infoN['时间戳'],'0000-00-00 00:00:00',sn,'C108',1,str(OutlierTempNo.values)+'号电芯温度异常离群','技术立即介入诊断']
+                        
+                        ErrorFlg=1
+                else:
+                    if 'C108' in df_Diag_Ram['code'].values.tolist():
+                            QuitErrCount[8]=QuitErrCount[8]+1
+                            if QuitErrCount[8]>3:
+                                QuitErrCount[8]=4
+                                end_time=datetime.datetime.now()
+                                end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+                                df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C108'].index,['end_time']]=end_time              
+
+        return df_Diag_Ram

+ 329 - 0
LIB/MIDDLE/SaftyCenter/DataDiag_Static/main.py

@@ -0,0 +1,329 @@
+import CBMSBatDiag
+from SC_SamplingSafty import SamplingSafty
+from DataStatistics import DataSta
+from DiagDataMerge import DiagDataMerge
+from SC_CtrlSafty import CtrlSafty
+from SC_BMSUploadError import BMSReportError
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+from sqlalchemy import create_engine
+import time, datetime
+from apscheduler.schedulers.blocking import BlockingScheduler
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import DBDownload as DBDownload
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import log
+from pandas.core.frame import DataFrame
+import datacompy
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam as QX_BatteryParam
+from LIB.MIDDLE.SaftyCenter.Common import DBDownload as DBDw
+
+#...................................电池包电芯安全诊断函数......................................................................................................................
+def diag_cal():
+    task_on=1
+    global SNnums
+    global start
+    factory_info=['骑享','金茂换电']
+    df_Diag_Ram=DataFrame(columns=['start_time','end_time','product_id','code','level','info','advice','Batpos'])
+    #..................................设置时间..........................................................  
+    start=time.time()
+    end_time=datetime.datetime.now()
+    start_time=end_time-datetime.timedelta(seconds=900)#10分钟跑一次,一次取10分钟数据
+    start_time=start_time.strftime('%Y-%m-%d %H:%M:%S')
+    end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+
+
+    
+    host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port=3306
+    db='safety_platform'
+    user='qx_algo_readonly'
+    password='qx@123456'
+    mode=2
+    tablename2='all_fault_info'
+    DBRead = DBDw.DBDownload(host, port, db, user, password,mode)
+    with DBRead as DBRead:
+        for i in range (0,len(factory_info)):
+            df_Diag_Ram_t = DBRead.getdata('start_time','end_time','product_id','code','level','info','advice','Batpos',tablename=tablename2,factory=factory_info[i],sn='',timename='',st='',sp='')
+            df_Diag_Ram=df_Diag_Ram.append(df_Diag_Ram_t)
+    df_Diag_Ram=df_Diag_Ram.dropna(axis=1,how='any')
+    df_Diag_Ram=df_Diag_Ram.reset_index(drop=True)
+    
+    for sn in SNnums:#SN遍历
+        print(sn)
+        if 'PK500' in sn:
+            celltype=1 #6040三元电芯
+            FactoryType=1
+        elif 'PK502' in sn:
+            celltype=2 #4840三元电芯
+            FactoryType=1
+        elif 'K504B' in sn:
+            celltype=99    #60ah林磷酸铁锂电芯
+            FactoryType=1
+        elif 'MGMLXN750' in sn:
+            celltype=3 #力信50ah三元电芯
+            FactoryType=3
+        elif ('MGMCLN750' or 'UD') in sn: 
+            celltype=4 #CATL 50ah三元电芯
+            FactoryType=2
+        elif 'TJMCL'in sn:
+            celltype=100 #CATL 50ah三元电芯
+            FactoryType=0
+        else:
+            print('SN:{},未找到对应电池类型!!!'.format(sn))
+            continue
+            # sys.exit()
+        param=QX_BatteryParam.BatParam(celltype)   
+        #读取原始数据库数据........................................................................................................................................................
+        dbManager = DBManager.DBManager()
+        df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms'])
+        df_bms = df_data['bms']
+        if not df_bms.empty:
+            df_bms=df_bms.dropna(subset=['总电流[A]'])
+            df_bms=df_bms.reset_index(drop=True)
+
+        #读取结果数据库数据........................................................................................................................................................
+        host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+        port=3306
+        db='qx_cas'
+        user='qx_algo_readonly'
+        password='qx@123456'
+        mode=1
+        tablename1='cellstateestimation_soh'
+        tablename2='cellstateestimation_uniform_socvoltdiff'       
+        DBRead=DBDownload.DBDownload(host, port, db, user, password,mode)
+        with DBRead as DBRead:
+            df_soh=DBRead.getdata('time_st,sn,soh,cellsoh', tablename=tablename1, sn=sn, timename='time_sp', st=start_time, sp=end_time)
+            df_uniform=DBRead.getdata('time,sn,cellsoc_diff,cellmin_num,cellmax_num', tablename=tablename2, sn=sn, timename='time', st=start_time, sp=end_time)
+
+        #读取电池当前运营状态.....................................................................................................................................................
+        host='rm-bp10j10qy42bzy0q7.mysql.rds.aliyuncs.com'
+        port=3306
+        db='qixiang_manage'
+        user='qx_query'
+        password='@Qx_query'
+        mode=4
+        tablename1='py_battery'     
+        DBRead=DBDw.DBDownload(host, port, db, user, password,mode)
+        with DBRead as DBRead:
+            df_OprtnSta=DBRead.getdata('qrcode','status', tablename=tablename1, sn=sn, timename='', st='', sp='',factory='')#取最后一条运营状态
+        errorcode_map=DataFrame()
+
+
+
+
+
+        #电池诊断.....................................................................................................................................................................
+        CellFltInfo=DataFrame(columns=['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice'])
+        df_Diag_Ram_sn = df_Diag_Ram.loc[df_Diag_Ram['product_id']==sn]#历史故障
+        df_Diag_Ram_sn_else = df_Diag_Ram.loc[df_Diag_Ram['product_id']!=sn]#sn之外的故障
+        CellFltInfo = df_Diag_Ram_sn.drop('Batpos',axis=1)
+        #获取当前故障电池的历史故障信息↑↑↑↑↑↑↑↑↑↑↑.......................................................................................................................................
+        if not df_bms.empty:
+            df_Diag_Batdiag_update_xq=SamplingSafty.main(sn,param,df_bms,CellFltInfo)#采样安全   
+            df_Diag_Batdiag_update_xq2=CtrlSafty.main(sn,param,df_bms,df_Diag_Batdiag_update_xq)#控制安全
+            #batDiag=CBMSBatDiag.BatDiag(sn,celltype,df_bms, df_soh, df_uniform, df_Diag_Batdiag_update_xq2)#鹏飞计算
+            #df_Diag_Batdiag_update2=batDiag.diag()
+            #df_Diag_Batdiag_update=BMSReportError.main(sn,df_bms,df_Diag_Batdiag_update2,FactoryType,errorcode_map)
+            df_Diag_Batdiag_update=BMSReportError.main(sn,df_bms,df_Diag_Batdiag_update_xq2,FactoryType,errorcode_map)
+            # df_Diag_Batdiag_update=BatDiag.diag() 
+        else:
+            df_Diag_Batdiag_update_xq=DataFrame(columns=['start_time','end_time','product_id','code','level','info','advice','Batpos'])
+            df_Diag_Batdiag_update_xq2=DataFrame(columns=['start_time','end_time','product_id','code','level','info','advice','Batpos'])
+            #df_Diag_Batdiag_update2=DataFrame(columns=['start_time','end_time','product_id','code','level','info','advice','Batpos'])
+            df_Diag_Batdiag_update=DataFrame(columns=['start_time','end_time','product_id','code','level','info','advice','Batpos'])
+        df_Diag_Ram_add,df_Diag_Ram_Update=DiagDataMerge.DetaMerge(df_Diag_Ram_sn,df_Diag_Batdiag_update,df_OprtnSta,df_Diag_Ram_sn_else)
+    task_on=0        
+#.................................统计程序...............................
+def DaTa_Sta_Week_Task():
+    task_on=1
+    factory_info=['骑享','金茂换电']
+    all_period_fault_info=DataFrame(columns=['factory','week','level1_count','level2_count','level3_count','level4_count','level5_count','solve_rate'])
+    df_fltinfopt=DataFrame(columns=['product_id','level','code','start_time','end_time','batpos','factory'])
+    for i in range (0,len(factory_info)):
+        host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+        port=3306
+        db='safety_platform'
+        user='qx_algo_readonly'
+        password='qx@123456'
+        mode=2
+        tablename1='all_fault_info'
+        DBRead=DBDw.DBDownload(host, port, db, user, password,mode)
+        with DBRead as DBRead:
+            df_fltinfo=DBRead.getdata('product_id','level','code','start_time','end_time','batpos','factory',tablename=tablename1,factory=factory_info[i],sn='',timename='',st='',sp='')#dbdownload经过了改编
+        df_fltinfopt=df_fltinfopt.append(df_fltinfo)
+        df_fltinfopt=df_fltinfopt.reset_index(drop=True)
+        print(df_fltinfopt)
+#............................获取数据................................
+    for j in range(0,len(factory_info)):
+        toweek='Week'+time.strftime('%W')
+        df_fltinfo=df_fltinfopt[df_fltinfopt['factory']==factory_info[j]]
+        #............................获取时间................................      
+        end_time=datetime.datetime.now()
+        # end_time=datetime.datetime.strptime(end_time,'%Y-%m-%d')
+        start_time_week=end_time-datetime.timedelta(days=7)
+        start_time_week=start_time_week.strftime('%Y-%m-%d')
+        end_time=end_time.strftime('%Y-%m-%d')
+        FltAlarmInfo,Celltype=DataSta.SaftyWarningSta(df_fltinfo,start_time_week,end_time,factory_info[j])
+        FaultLvlCount=DataSta.WeekInfoSta(df_fltinfo,start_time_week,end_time)
+        for i in range(1,6):
+            if not FaultLvlCount[FaultLvlCount['level']==i]['product_id'].empty:
+                all_period_fault_info.loc[j,'level'+str(i)+'_count']=int(FaultLvlCount[FaultLvlCount['level']==i]['product_id'].values)
+            else:
+                all_period_fault_info.loc[j,'level'+str(i)+'_count']=int(0)
+        all_period_fault_info.loc[j,'factory']=factory_info[j]
+        all_period_fault_info.loc[j,'week']=toweek
+        all_period_fault_info.loc[j,'solve_rate']=FltAlarmInfo.loc[0,'OprationManageRate']
+        all_period_fault_info.fillna(0,inplace=False)
+        print(all_period_fault_info)
+        task_on=0
+def DaTa_Sta_Minutes_Task():
+    task_on=1
+    factory_info=['骑享','金茂换电']
+    #............................获取数据................................
+    host='172.16.121.236'
+    port=3306
+    db='fastfun'
+    user='readonly'
+    password='Fast1234'
+    mode=3
+    tablename1='ff_battery_accum'
+    tablename2='ff_location'
+    DBRead=DBDw.DBDownload(host, port, db, user, password,mode)
+    with DBRead as DBRead:
+        df_last_accum=DBRead.getdata('devcode','dsg_phaccum','dsg_ahaccum',tablename=tablename1,factory='',sn='',timename='',st='',sp='')#dbdownload经过了改编
+        df_last_pos=DBRead.getdata('devcode','latitude','longitude',tablename=tablename2,factory='',sn='',timename='',st='',sp='')#dbdownload经过了改编
+        
+        
+    host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port=3306
+    db='qx_cas'
+    user='qx_algo_readonly'
+    password='qx@123456'
+    mode=3
+    tablename2='bat_first_data_time'
+    DBRead=DBDw.DBDownload(host, port, db, user, password,mode)
+    with DBRead as DBRead:
+        df_FirstDataTime=DBRead.getdata('sn','first_data_time',tablename=tablename2,factory='',sn='',timename='',st='',sp='')#dbdownload经过了改编
+        
+        
+
+    #............................获取时间................................
+    end_time=datetime.datetime.now()
+    # end_time=datetime.datetime.strptime(end_time,'%Y-%m-%d')
+    start_time=end_time-datetime.timedelta(days=1)
+    start_time=start_time.strftime('%Y-%m-%d')
+    end_time=end_time.strftime('%Y-%m-%d')
+    #............................执行程序................................
+    all_statistic_info=DataFrame(columns=['factory','total_alarm','alarm_total_today','alarm_not_close_today','alarm_close_today','alarm_uregent_total_today','alarm_uregent_close_today','alarm_uregent_not_close_today','alarm_close_total','run_time_total','dischrg_total','odo_total','max_dischrg_one','max_runtime_one','max_cycle_one','max_odo_one','alarm_total','cell_type','cell_type_count','cell_safety_risk_count','data_safety_risk_count','status_safety_risk_count','hv_safety_risk_count','system_safety_risk_count','sample_safety_risk_count','controller_safety_risk_count','design_safety_risk_count'])
+    for j in range(0,len(factory_info)):
+        host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+        port=3306
+        db='safety_platform'
+        user='qx_algo_readonly'
+        password='qx@123456'
+        mode=2
+        tablename1='all_fault_info'
+        DBRead=DBDw.DBDownload(host, port, db, user, password,mode)
+        with DBRead as DBRead:
+            df_fltinfo=DBRead.getdata('product_id','level','code','start_time','batpos',tablename=tablename1,factory=factory_info[j],sn='',timename='',st='',sp='')#dbdownload经过了改编
+        FltAlarmInfo,Celltype=DataSta.SaftyWarningSta(df_fltinfo,start_time,end_time,factory_info[j])
+        SatftyCount=DataSta.SftyWrngClsfy(df_fltinfo)
+        MaxAccumAh,TotalAccumAh,MaxCycle,MaxRunningHour,TotalRunHour=DataSta.AccumInfo(df_last_accum,df_FirstDataTime,end_time,factory_info[j])
+        all_location_info=DataSta.FltBatPosition(df_last_pos,df_fltinfo,factory_info[j])
+
+       
+        all_statistic_info.loc[j,'factory']=factory_info[j]
+        all_statistic_info.loc[j,'total_alarm']=FltAlarmInfo.loc[0,'SftyPlt_Data_Total']
+        all_statistic_info.loc[j,'alarm_total_today']=FltAlarmInfo.loc[0,'SftyPlt_Data_day']
+        all_statistic_info.loc[j,'alarm_close_today']=FltAlarmInfo.loc[0,'CS_Warning_day_Finish_Count']
+        all_statistic_info.loc[j,'alarm_not_close_today']=FltAlarmInfo.loc[0,'SftyPlt_Data_day']-FltAlarmInfo.loc[0,'CS_Warning_day_Finish_Count']
+        all_statistic_info.loc[j,'alarm_uregent_total_today']=FltAlarmInfo.loc[0,'SftyPlt_EmgcyData_day']
+        all_statistic_info.loc[j,'alarm_uregent_close_today']=FltAlarmInfo.loc[0,'SftyPlt_EmgcyData_day_Finish_Count']
+        all_statistic_info.loc[j,'alarm_uregent_not_close_today']=FltAlarmInfo.loc[0,'SftyPlt_EmgcyData_day']-FltAlarmInfo.loc[0,'SftyPlt_EmgcyData_day_Finish_Count']
+        all_statistic_info.loc[j,'run_time_total']=TotalRunHour
+        all_statistic_info.loc[j,'dischrg_total']=TotalAccumAh
+        all_statistic_info.loc[j,'odo_total']=0
+        all_statistic_info.loc[j,'max_dischrg_one']=MaxAccumAh
+        all_statistic_info.loc[j,'max_runtime_one']=MaxRunningHour
+        all_statistic_info.loc[j,'max_cycle_one']=MaxCycle
+        all_statistic_info.loc[j,'max_odo_one']=0
+        all_statistic_info.loc[j,'alarm_close_total']=FltAlarmInfo.loc[0,'CS_Warning_Total_Finish_Count']
+        all_statistic_info.loc[j,'alarm_total']=FltAlarmInfo.loc[0,'SftyPlt_Data_Total']
+        CellType=Celltype.columns.tolist()
+        CellType=','.join(CellType) 
+        all_statistic_info.loc[0,'cell_type']=str(CellType)
+        CellTypeCount=Celltype.loc[0].tolist()
+        CellTypeCount=[str(x) for x in CellTypeCount]
+        CellTypeCount=','.join(CellTypeCount) 
+        all_statistic_info.loc[j,'cell_type_count']=str(CellTypeCount)
+        all_statistic_info.loc[j,'cell_safety_risk_count']=SatftyCount.loc[0,'CellSaftyCount']
+        all_statistic_info.loc[j,'data_safety_risk_count']=SatftyCount.loc[0,'DataSaftyCodeCount']
+        all_statistic_info.loc[j,'status_safety_risk_count']=SatftyCount.loc[0,'StateSaftyCodeCount']
+        all_statistic_info.loc[j,'hv_safety_risk_count']=SatftyCount.loc[0,'HvSaftyCodeCount']
+        all_statistic_info.loc[j,'system_safety_risk_count']=SatftyCount.loc[0,'SysSaftyCodeCount']
+        all_statistic_info.loc[j,'sample_safety_risk_count']=SatftyCount.loc[0,'SamplingSatyCount']
+        all_statistic_info.loc[j,'controller_safety_risk_count']=SatftyCount.loc[0,'CtrlSaftyCodeCount']
+        all_statistic_info.loc[j,'design_safety_risk_count']=SatftyCount.loc[0,'DsnSaftyCodeCount']
+        end=time.time()
+        #print(end-start)
+        task_on=0
+    print(all_statistic_info)
+    all_statistic_info.to_excel('all_statistic_info.xlsx')
+
+
+#...............................................主函数.......................................................................................................................
+if __name__ == "__main__":
+    global SNnums
+    global task_on
+    
+    task_on=0
+    excelpath=r'D:\ZLWORK\code\data_analyze_platform\USER\eric\SaftyCenter_CODE_V1\sn-20210903.xlsx'
+    SNdata_6060 = pd.read_excel(excelpath, sheet_name='科易6060')
+    SNdata_6040 = pd.read_excel(excelpath, sheet_name='科易6040')
+    SNdata_4840 = pd.read_excel(excelpath, sheet_name='科易4840')
+    SNdata_L7255 = pd.read_excel(excelpath, sheet_name='格林美-力信7255')
+    SNdata_C7255 = pd.read_excel(excelpath, sheet_name='格林美-CATL7255')
+    SNdata_U7255 = pd.read_excel(excelpath, sheet_name='优旦7255')
+    SNnums_6060=SNdata_6060['SN号'].tolist()
+    SNnums_6040=SNdata_6040['SN号'].tolist()
+    SNnums_4840=SNdata_4840['SN号'].tolist()
+    SNnums_L7255=SNdata_L7255['SN号'].tolist()
+    SNnums_C7255=SNdata_C7255['SN号'].tolist()
+    SNnums_U7255=SNdata_U7255['SN号'].tolist()
+    SNnums=SNnums_L7255 + SNnums_C7255 + SNnums_6040 + SNnums_4840 + SNnums_U7255+ SNnums_6060
+    #SNnums=['TJMCL120502305038','TJMCL120502305032','TJMCL120502305022','TJMCL120502305026','TJMCL120502305032','TJMCL120502305044','TJMCL120502305048','TJMCL120502305012','TJMCL120502305010']
+    SNnums = ['PK504B10100004341'] #SNnums_6040
+    
+    mylog=log.Mylog('log.txt','error')
+    mylog.logcfg()
+    #............................模块运行前,先读取数据库中所有结束时间为0的数据,需要从数据库中读取................
+    host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port=3306
+    db='safety_platform'
+    user='qx_algo_readonly'
+    password='qx@123456'
+    mode=2
+    tablename2='all_fault_info'
+    DBRead = DBDw.DBDownload(host, port, db, user, password,mode)
+    with DBRead as DBRead:
+        df_Diag_Ram = DBRead.getdata('start_time','end_time','product_id','code','level','info','advice','Batpos','factory',tablename=tablename2,factory='',sn='',timename='',st='',sp='')
+    # result=pd.read_csv(r'D:\Work\Code_write\data_analyze_platform\USER\01Screen_Problem\result.csv',encoding='gbk')
+
+    #定时任务.......................................................................................................................................................................
+    scheduler = BlockingScheduler()
+    diag_cal()
+    DaTa_Sta_Minutes_Task()
+    DaTa_Sta_Week_Task()  
+
+    if task_on==0:
+        scheduler.add_job(DaTa_Sta_Week_Task, 'interval', days=7, id='Week_Task')
+        scheduler.add_job(diag_cal, 'interval', seconds=900, id='diag_job')
+        scheduler.add_job(DaTa_Sta_Minutes_Task, 'interval', seconds=180, id='Hour_Task')
+
+    try:
+
+        scheduler.start()
+    except Exception as e:
+        scheduler.shutdown()
+        print(repr(e))
+        mylog.logopt(e)

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác