Browse Source

Merge remote-tracking branch 'origin/dev' into pro

qingfeng 2 years ago
parent
commit
fc8e63cc4d
100 changed files with 18987 additions and 346 deletions
  1. 478 0
      DataPreProcessMGMC.py
  2. 4 4
      LIB/BACKEND/DBManager.py
  3. 2 0
      LIB/BACKEND/DEPLOY/build.cmd
  4. 181 1
      LIB/BACKEND/DataPreProcess.py
  5. 2 2
      LIB/BACKEND/Log.py
  6. 98 0
      LIB/BACKEND/OPENAPI/OpenApi.py
  7. 33 0
      LIB/BACKEND/OPENAPI/main.ipynb
  8. BIN
      LIB/BACKEND/__pycache__/DBManager.cpython-38.pyc
  9. BIN
      LIB/BACKEND/__pycache__/Log.cpython-38.pyc
  10. 35 0
      LIB/FRONTEND/AlgoTest/Algo1/V_1_0_0/Dockerfile
  11. 13 0
      LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/CoreAlgo/core.py
  12. 34 0
      LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/Main/Dockerfile
  13. 13 0
      LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/Main/config-dev.ini
  14. 13 0
      LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/Main/config-pro.ini
  15. 13 0
      LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/Main/config-test.ini
  16. 151 0
      LIB/MIDDLE/AlgoDemo/Demo/V_1_0_0/Main/main.py
  17. 132 0
      LIB/MIDDLE/Anomaly_Detection/V1_0_0/anomalyPCA.py
  18. 42 0
      LIB/MIDDLE/Anomaly_Detection/V1_0_0/main_anomalyPCA.py
  19. 110 0
      LIB/MIDDLE/Anomaly_Detection/V1_0_0/main_detection.py
  20. BIN
      LIB/MIDDLE/Anomaly_Detection/V1_0_0/sn-20210903.xlsx
  21. 699 0
      LIB/MIDDLE/CellStateEstimation/BatDiag/V1_0_0/CBMSBatDiag.py
  22. 164 0
      LIB/MIDDLE/CellStateEstimation/BatDiag/main.py
  23. 175 159
      LIB/MIDDLE/CellStateEstimation/BatSafetyAlarm/V1_0_1/CBMSSafetyAlarm.py
  24. 49 30
      LIB/MIDDLE/CellStateEstimation/BatSafetyAlarm/main.py
  25. 5 5
      LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/CBMSBatInterShort.py
  26. 97 15
      LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/CBMSSafetyWarning.py
  27. 3 4
      LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/VoltStray.py
  28. 26 16
      LIB/MIDDLE/CellStateEstimation/Common/V1_0_1/BatParam.py
  29. BIN
      LIB/MIDDLE/CellStateEstimation/Common/V1_0_1/__pycache__/BatParam.cpython-38.pyc
  30. BIN
      LIB/MIDDLE/CellStateEstimation/Common/V1_0_1/__pycache__/log.cpython-38.pyc
  31. 1 1
      LIB/MIDDLE/CellStateEstimation/Common/V1_0_1/log.py
  32. 423 0
      LIB/MIDDLE/InfoChrgDrive/Charge/V1_0_0/coreV0.py
  33. 40 0
      LIB/MIDDLE/InfoChrgDrive/Charge/gps.csv
  34. 116 0
      LIB/MIDDLE/InfoChrgDrive/Charge/main_V0.py
  35. BIN
      LIB/MIDDLE/InfoChrgDrive/Charge/sn-20210903.xlsx
  36. BIN
      LIB/MIDDLE/InfoChrgDrive/Charge/输出表单.xlsx
  37. 51 0
      LIB/MIDDLE/SaftyCenter/DataDiag_Static/BMSuploaderrortest.py
  38. 21 9
      LIB/MIDDLE/SaftyCenter/DataDiag_Static/SC_BMSUploadError.py
  39. 4 4
      LIB/MIDDLE/SaftyCenter/DataDiag_Static/SC_CtrlSafty.py
  40. 84 84
      LIB/MIDDLE/SaftyCenter/DataDiag_Static/SC_SamplingSafty.py
  41. 16 10
      LIB/MIDDLE/SaftyCenter/DataDiag_Static/main.py
  42. 417 0
      LIB/MIDDLE/SaftyCenter/DataDiag_Static/main_2.ipynb
  43. 177 0
      LIB/MIDDLE/SaftyCenter/DataDiag_Static/main_2.py
  44. 127 0
      LIB/MIDDLE/SaftyCenter/Low_Soc_Alarm/dev.ipynb
  45. 2 2
      LIB/MIDDLE/SaftyCenter/Low_Soc_Alarm/low_soc_alarm_sta.py
  46. 198 0
      LIB/MIDDLE/SaftyCenter/Offline/dev.ipynb
  47. 321 0
      LIB/MIDDLE/odo/DailyMileageEstimation/V1_0_4/calcul_mileage.py
  48. 42 0
      LIB/MIDDLE/odo/DailyMileageEstimation/V1_0_4/main_daily_mileage.py
  49. 26 0
      LIB/MIDDLE/odo/DailyMileageEstimation/V1_0_4/main_daily_mileage_sn.py
  50. 39 0
      LIB/MIDDLE/odo/DailyMileageEstimation/V1_0_4/main_mileage.py
  51. BIN
      LIB/MIDDLE/odo/DailyMileageEstimation/V1_0_4/sn-20210903.xlsx
  52. BIN
      LIB/MIDDLE/odo/DailyMileageEstimation/V1_0_4/每日里程表单.xlsx
  53. 356 0
      LIB/MIDDLE/odo/DailyMileageEstimation/V1_0_4_SimpleVehicle/cal_mileage.py
  54. 42 0
      LIB/MIDDLE/odo/DailyMileageEstimation/V1_0_4_SimpleVehicle/main_daily_mileage.py
  55. BIN
      LIB/SOH数据.xlsx
  56. BIN
      SOH数据.xlsx
  57. 802 0
      USER/SPF/01qixiang/01BatSoh/CBMSBatSoh copy.py
  58. 880 0
      USER/SPF/01qixiang/01BatSoh/CBMSBatSoh.py
  59. 102 0
      USER/SPF/01qixiang/01BatSoh/main.py
  60. 389 0
      USER/SPF/01qixiang/02BatUniform/CBMSBatUniform.py
  61. 89 0
      USER/SPF/01qixiang/02BatUniform/main.py
  62. 589 0
      USER/SPF/01qixiang/03BatInterShort/CBMSBatInterShort.py
  63. 92 0
      USER/SPF/01qixiang/03BatInterShort/main.py
  64. 630 0
      USER/SPF/01qixiang/04BatSoc/CBMSBatSoc.py
  65. 140 0
      USER/SPF/01qixiang/04BatSoc/main.py
  66. 539 0
      USER/SPF/01qixiang/05BatDiag/CBMSBatDiag.py
  67. 213 0
      USER/SPF/01qixiang/05BatDiag/DataStatistics.py
  68. 36 0
      USER/SPF/01qixiang/05BatDiag/DiagDataMerge.py
  69. 73 0
      USER/SPF/01qixiang/05BatDiag/SC_CtrlSafty.py
  70. 356 0
      USER/SPF/01qixiang/05BatDiag/SC_SamplingSafty.py
  71. 502 0
      USER/SPF/01qixiang/05BatDiag/deploy.py
  72. 134 0
      USER/SPF/01qixiang/05BatDiag/main_test.py
  73. 262 0
      USER/SPF/01qixiang/06BatSafetyAlarm/CBMSSafetyAlarm.py
  74. BIN
      USER/SPF/01qixiang/06BatSafetyAlarm/__pycache__/CBMSSafetyAlarm.cpython-38.pyc
  75. 248 0
      USER/SPF/01qixiang/06BatSafetyAlarm/deploy - 副本.py
  76. 161 0
      USER/SPF/01qixiang/06BatSafetyAlarm/main copy 2.py
  77. 161 0
      USER/SPF/01qixiang/06BatSafetyAlarm/main.py
  78. 906 0
      USER/SPF/01qixiang/07BatSafetyWarning copy/CBMSBatInterShort.py
  79. 506 0
      USER/SPF/01qixiang/07BatSafetyWarning copy/CBMSBatUniform.py
  80. 265 0
      USER/SPF/01qixiang/07BatSafetyWarning copy/CBMSSafetyWarning.py
  81. 266 0
      USER/SPF/01qixiang/07BatSafetyWarning copy/VoltStray.py
  82. 279 0
      USER/SPF/01qixiang/07BatSafetyWarning copy/main.py
  83. 906 0
      USER/SPF/01qixiang/07BatSafetyWarning/CBMSBatInterShort.py
  84. 506 0
      USER/SPF/01qixiang/07BatSafetyWarning/CBMSBatUniform.py
  85. 265 0
      USER/SPF/01qixiang/07BatSafetyWarning/CBMSSafetyWarning.py
  86. 266 0
      USER/SPF/01qixiang/07BatSafetyWarning/VoltStray.py
  87. 279 0
      USER/SPF/01qixiang/07BatSafetyWarning/main.py
  88. 699 0
      USER/SPF/01qixiang/08BatQuality/CBMSBatDiag.py
  89. 165 0
      USER/SPF/01qixiang/08BatQuality/main.py
  90. 145 0
      USER/SPF/01qixiang/09Behave/BehaveStatistics.py
  91. 96 0
      USER/SPF/01qixiang/09Behave/CBMSBatBehave.py
  92. 99 0
      USER/SPF/01qixiang/09Behave/main.py
  93. 0 0
      USER/SPF/01qixiang/101Log/log_diag.txt
  94. 371 0
      USER/SPF/01qixiang/10Parameter/BatParam.py
  95. 92 0
      USER/SPF/01qixiang/BatParam.py
  96. BIN
      USER/SPF/01qixiang/USER - 快捷方式.lnk
  97. 111 0
      USER/SPF/01qixiang/test.py
  98. 554 0
      USER/SPF/03hezhong/00BatInterShort/CBMSBatInterShort.py
  99. 65 0
      USER/SPF/03hezhong/00BatInterShort/main.py
  100. 673 0
      USER/SPF/03hezhong/01BatSoh/CBMSBatSoh.py

+ 478 - 0
DataPreProcessMGMC.py

@@ -0,0 +1,478 @@
+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
+        

+ 4 - 4
LIB/BACKEND/DBManager.py

@@ -172,15 +172,15 @@ class DBManager():
     @staticmethod
     def _convert_to_dataframe_gps(data, mode=0):
         if mode == 0:
-            if data['info']['subType'] == 1:
+            # 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]','海拔','航向'])
+            # 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)

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

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

+ 181 - 1
LIB/BACKEND/DataPreProcess.py

@@ -15,6 +15,7 @@ import numpy as np
 import pdb
 from numba import jit
 from LIB.BACKEND import Tools
+from LIB.BACKEND.OPENAPI import OpenApi
 
 class DataPreProcess:
     def __init__(self):
@@ -173,6 +174,142 @@ class DataPreProcess:
         df['data_split_by_status'] = status_id
         df['data_status'] = status_list
         return df
+    def data_split_by_status_forMGMCUD02(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):
         '''
@@ -486,4 +623,47 @@ class DataPreProcess:
         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
-        
+    
+    
+    
+    '''
+    为故障数据打标签
+    sn: 电池编码
+    df_bms: 本电池编码对应的bms数据
+    '''
+    def data_fault_tag(self, sn='', df_bms=pd.DataFrame()):
+        if sn =='':
+            raise Exception("请输入sn")
+        o = OpenApi.OpenApi()
+        df_fault = o.get_omp_fatult_tag(sn=sn)
+        df_data = df_bms.copy()
+        df_data['fault_tag'] = None
+        if len(df_fault) > 0:
+            # 遍历本sn的故障
+            for index in df_fault.index:
+                startT = df_fault.loc[index, 'faultTime']
+                endT = df_fault.loc[index, 'endTime']
+                tagTypeText = df_fault.loc[index, 'tagTypeText']
+                childTagText = df_fault.loc[index, 'childTagText']
+                tagType = df_fault.loc[index, 'tagType']
+                childTag = df_fault.loc[index, 'childTag']
+                # 故障标签拼接
+                new_tag = []
+                if pd.isnull(tagType) or len(tagTypeText) <= 0:
+                    continue
+                elif pd.isnull(childTag) or len(childTagText) <= 0:
+                    new_tag.append(tagTypeText)
+                else:
+                    for child in childTagText.split(";"):
+                        new_tag.append(tagTypeText+':' + child)
+                        
+                # 原始数据对应故障时间的index
+                for index2 in df_data[(df_data['时间戳']>=startT) & (df_data['时间戳']<endT)].index:
+                    old_tag = df_data.loc[index2, 'fault_tag']
+                    if pd.isnull(old_tag) or len(old_tag) <= 0:
+                        df_data.loc[index2,'fault_tag'] = ';'.join(new_tag)
+                    else:
+                        tag = old_tag.split(";")
+                        tag.extend(new_tag)
+                        df_data.loc[index2,'fault_tag'] = ';'.join(list(set(tag)))
+        return df_data

+ 2 - 2
LIB/BACKEND/Log.py

@@ -20,8 +20,8 @@ class Mylog:
     def get_logger(self):
         return self.logger
     
-    def set_file_hl(self, file_name='all.log', log_level='info', size=1):
-        fh = handlers.RotatingFileHandler(file_name, maxBytes=size, backupCount=10)
+    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:

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

@@ -0,0 +1,98 @@
+'''
+数据预处理类
+
+'''
+__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
+    
+    '''
+    获取 运维故障标签 数据
+    '''
+    def get_omp_fatult_tag(self, sn="", start_time='', end_time=''): 
+        url = 'http://open.li-ai.com.cn/admin/v1/queryFaultMsg'
+        headers = {'Content-Type':"application/json; charset=utf-8","token":"2A26DD0ADE53456D928562A17131A3B5"}
+        data = {}
+        if sn != "":
+            data['sn'] = sn
+        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"])
+        # 剔除 [58,59,110,119,C490,C599,C307]
+        if len(df)>0: 
+            df = df[~df['faultCode'].isin(["58","59",'110',"119","C490","C599","C307"])]
+            df = df[df['endTime']!='0000-00-00 00:00:00']
+        # for d in json.loads(response.text)["data"]:
+        #     df = df.append(d, ignore_index=True)   
+        return df
+    
+    

File diff suppressed because it is too large
+ 33 - 0
LIB/BACKEND/OPENAPI/main.ipynb


BIN
LIB/BACKEND/__pycache__/DBManager.cpython-38.pyc


BIN
LIB/BACKEND/__pycache__/Log.cpython-38.pyc


+ 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"]

+ 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

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

@@ -0,0 +1,151 @@
+# -*- 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:    
+        # 创建数据库连接池
+        
+        db_engine_1 = create_engine(
+            "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+                user1, password1, host1, port1, db1
+            ))
+        Session = sessionmaker(bind=db_engine)
+        # 取数示例
+        sql = "select * from table"
+        df_diag_ram = pd.read_sql(sql, db_engine)
+        
+        # 修改数据库示例
+        # session = Session()
+        # sql = 'insert into table (data) values (:data)'
+        # params = {'data':1}
+        # session.execute(sql,params=params)
+        
+        sql = "update all_fault_info_copy set update_time=:time,end_time=:end_time where product_id=:sn and end_time=:end_time and code=:code"
+        params = {"time":datetime.datetime.now(), "end_time":end_time, "sn":sn, "code":'C493'}
+        session.execute(sql,params=params)
+        session.commit()
+        session.close()
+
+
+
+        # 准备算法数据
+        
+        # 原始数据时间设置
+        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:   
+                # 修改
+                    pass
+                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=4
+                        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=4
+                            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)

+ 175 - 159
LIB/MIDDLE/CellStateEstimation/BatSafetyAlarm/V1_0_1/CBMSSafetyAlarm.py

@@ -4,18 +4,19 @@ import datetime
 from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam
 
 class SafetyAlarm:
-    def __init__(self,sn,celltype,df_bms,df_bms_ram):  #参数初始化
+    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_bms_ram=df_bms_ram
+        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.empty:
-            self.df_bms=self.df_bms[self.df_bms['时间戳'] > df_bms_ram.iloc[-1]['time']]    #滤除原始数据中的重复数据
-            self.df_bms.reset_index(inplace=True,drop=True)     #重置索引
+        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]']
@@ -27,12 +28,12 @@ class SafetyAlarm:
         # self.celltemp_name=celltemp+othertemp
 
     
-    def diag(self):
+    def safety_alarm_diag(self):
         if self.celltype<=50:
-            df_res=self._ncm_diag()
+            df_res=self._alarm_diag()
             return df_res    
         else:
-            df_res=self._ncm_diag()
+            df_res=self._alarm_diag()
             return df_res
         
 
@@ -47,200 +48,215 @@ class SafetyAlarm:
 
     #获取当前行所有电压数据............................................................................................
     def _cellvolt_get(self,num): 
-        cellvolt = list(self.df_bms.loc[num,self.cellvolt_name]/1000)
+        cellvolt = np.array(self.df_bms.loc[num,self.cellvolt_name]/1000)
         return cellvolt
 
     #..........................................三元电池诊断功能..................................................................
-    def _ncm_diag(self):
+    def _alarm_diag(self):
 
-        column_name=['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice']
-        df_res=pd.DataFrame(columns=column_name)
+        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
-        cellvoltdiff=0
         packvoltfall=0
-            
-        for i in range(len(self.df_bms)):
-            
-            #温度诊断功能.............................................................................................................
-            temp2=np.array(self._celltemp_get(i))
-            celltempmin=min(temp2)
-            celltempmax=max(temp2)
-            #温度有效性判断...........................................................................
-            if celltempmax>self.param.CellTempUpLmt or celltempmin<self.param.CellTempLwLmt:
-                celltempvalid=0
-            else:   #不作处理
-                celltempvalid=1
 
-            if celltempvalid==1:
-                
-                #过温判断............................................................................................................................
-                if celltempmax>self.param.TrwTempHigh:
-                    celltemphigh=1
-                else:
-                    pass
-            
-                
-                #温升判断.............................................................................................................................
+        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_bms_ram.empty:
-                        time1=self.df_bms_ram.iloc[-1]['time']
+                    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]
-                        temp1=np.array(self.df_bms_ram.iloc[-1]['celltemp'])
-
-                        delttime=(time2-time1).total_seconds()
-                        celltemp_rate=(max(temp2-temp1)*60)/delttime    #计算最大温升速率
-                        if celltemp_rate>self.param.TrwTempRate and self.param.CellTempLwLmt<min(temp1) and max(temp1)<self.param.CellTempUpLmt and max(temp2-temp1)>3:
-                            celltemprise=1
-                        else:
-                            pass
                     else:
-                        pass
+                        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=np.array(self._celltemp_get(i-1))
+                
+                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()
-                    celltemp_rate=(max(temp2-temp1)*60)/delttime    #计算最大温升速率
+                    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=1
+                        celltemprise=celltemprise+1
                     else:
                         pass
-            
-            else:
-                pass
-            
-            #电压诊断功能.........................................................................................................................................
-            if i<1:
-                cellvolt2=self._cellvolt_get(i)
-                cellvoltmin2=min(cellvolt2)
-                cellvoltmax2=max(cellvolt2)
-                cellvoltmin_index2=list(cellvolt2).index(cellvoltmin2)
-                cellvoltmax_index2=list(cellvolt2).index(cellvoltmax2)
-                if not self.df_bms_ram.empty:
-                    cellvolt1=np.array(self.df_bms_ram.iloc[-1]['cellvolt'])
-                    cellvoltmin1=min(cellvolt1)
-                    cellvoltmax1=max(cellvolt1)
-                    cellvoltmin_index1=list(cellvolt1).index(cellvoltmin1)
-                    cellvoltmax_index1=list(cellvolt1).index(cellvoltmax1)
+                
                 else:
-                    cellvoltmin1=cellvoltmin2
-                    cellvoltmax1=cellvoltmax2
-                    cellvoltmin_index1=cellvoltmin_index2
-                    cellvoltmax_index1=cellvoltmax_index2
-            else:
-                cellvolt2=self._cellvolt_get(i)
+                    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)
-                cellvolt1=self._cellvolt_get(i-1)
                 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 cellvoltmin2<0.01 or (cellvoltmin1<2 and cellvoltmax1>4.5 and abs(cellvoltmax_index1-cellvoltmin_index1)==1) or cellvoltmin1<0.01:   #电压断线故障进入
-                cellvoltvalid=0
-            else:
-                cellvoltvalid=1
-
-            if cellvoltvalid==1:
-                #单体电压跌落诊断...........................................................................................................................
-                if i<1:
-                    if not self.df_bms_ram.empty:
-                        time1=self.df_bms_ram.iloc[-1]['time']
-                        time2=self.bmstime[i]
-                        delttime=(time2-time1).total_seconds()
-                        if delttime<310:
-                            if self.packcrnt[i]<0.5 and max(cellvolt1-cellvolt2)>self.param.TrwCellVoltFall:
-                                cellvoltfall=1
-                            elif self.packcrnt[i]>0.5 and max(cellvolt1-cellvolt2)-self.packcrnt[i]*0.01>self.param.TrwCellVoltFall:
-                                cellvoltfall=1
-                            else:
-                                pass
-                        else:
-                            if min(cellvolt2)<self.param.TrwCellVoltLow:  #电压跌落至<1.5V
-                                cellvoltfall=1
-                            else:
-                                pass
-                    else:
-                        pass
+                
+                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:
-                    delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
-                    cellvolt1=np.array(self._cellvolt_get(i-1))
-                    if self.packcrnt[i]<0.5 and max(cellvolt1-cellvolt2)>self.param.TrwCellVoltFall:
-                        cellvoltfall=1
-                    elif self.packcrnt[i]>0.5 and max(cellvolt1-cellvolt2)-self.packcrnt[i]*0.01>self.param.TrwCellVoltFall:
-                        cellvoltfall=1
+                    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:
-                        pass
+                        cellvoltvalid=0
                 
-                #压差诊断........................................................................................................................................
-                if (max(cellvolt2)-min(cellvolt2))>self.param.TrwCellVoltDiff:
-                    cellvoltdiff=1
+                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
-            
-            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_bms_ram.empty:
-                        time1=self.df_bms_ram.iloc[-1]['time']
-                        time2=self.bmstime[i]
-                        delttime=(time2-time1).total_seconds()
-                        packvolt1=self.df_bms_ram.iloc[-1]['packvolt']
                     
-                        if delttime<310:
-                            if self.packcrnt[i]<5 and (packvolt1-packvolt2)>self.param.TrwPackVoltFall and self.param.CellVoltNums<packvolt1<self.param.CellVoltNums*4.5:
-                                packvoltfall=1
+                #电池包诊断.....................................................................................................................................
+                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:
-                        pass
+                        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:
-                    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=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, list(cellvolt2), list(temp2)]
-        if celltemprise==1 or celltemphigh==1:
-            trwtemp=1
-        else:
-            trwtemp=0
+                    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 cellvoltfall==1 or cellvoltdiff==1:
-            trwcellvolt=1
+            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:
-            trwcellvolt=0
+            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
         
-        if trwtemp+trwcellvolt+packvoltfall>1.5:
-            fltcode=119
-            df_res.loc[0]=[self.bmstime[0], end_time, self.sn, fltcode, 5, '电池发生热失控', '立刻远离电池']
-            return df_res, df_bms_ram
-        else:
-            return pd.DataFrame(), df_bms_ram

+ 49 - 30
LIB/MIDDLE/CellStateEstimation/BatSafetyAlarm/main.py

@@ -10,10 +10,10 @@ from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import log
 
 
 #...................................电池包电芯安全诊断函数......................................................................................................................
-def diag_cal(sn_list, df_bms_ram):
+def diag_cal(sn_list, df_bms_ram, df_alarm_ram):
 
-    # start=time.time()
-    now_time=datetime.datetime.now()
+    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')
@@ -24,13 +24,13 @@ def diag_cal(sn_list, df_bms_ram):
     user='qx_algo_readonly'
     password = 'qx@123456'
 
-    #读取故障结果库中code==119且end_time='0000-00-00 00:00:00'...............................
+    #读取故障结果库中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=119 and end_time='0000-00-00 00:00:00'" %(param,tablename)
+    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(','))
@@ -57,26 +57,30 @@ def diag_cal(sn_list, df_bms_ram):
         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)
+        # 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:   
-            if not df_bms.empty:
-                SafetyAlarm=CBMSSafetyAlarm.SafetyAlarm(sn,celltype,df_bms, df_bms_ram_sn)
-                df_diag_res, df_bms_res=SafetyAlarm.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:
-                    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')
-                
+            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')
@@ -86,21 +90,32 @@ def diag_cal(sn_list, df_bms_ram):
                 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)
+        end=time.time()
+        print(end-start)
+    
+    return df_bms_ram,df_alarm_ram
 #...................................................主进程...........................................................................................................
 def mainprocess():
-    global SNnums
-    global df_bms_ram
+    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]
-        pool.apply_async(diag_cal, (sn_list,df_bms_ram))
-
+        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]
 
 
 #...............................................主函数起定时作用.......................................................................................................................
@@ -119,18 +134,22 @@ if __name__ == "__main__":
     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=[[], ['PK504B10100004447']]
+    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_ram=pd.DataFrame(columns=['time', 'sn', 'packvolt', 'cellvolt', 'celltemp'])
+    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=10, id='diag_job')
+    scheduler.add_job(mainprocess, 'interval', seconds=60, id='diag_job')
 
     try:  
         scheduler.start()

+ 5 - 5
LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/CBMSBatInterShort.py

@@ -120,11 +120,11 @@ class BatInterShort():
     def _celldeltsoc_get(self,cellvolt_list,dict_baltime,capacity): 
         cellsoc=[]
         celldeltsoc=[]
-        for j in range(self.param.CellVoltNums):    #获取每个电芯电压对应的SOC值
+        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 in dict_baltime.keys():
-                ocv_soc=ocv_soc+dict_baltime[j]*self.param.BalCurrent/(capacity*3600)   #补偿均衡电流
+                ocv_soc=ocv_soc+dict_baltime[j+1]*self.param.BalCurrent/(capacity*3600)   #补偿均衡电流
             else:
                 pass
             cellsoc.append(ocv_soc)
@@ -337,7 +337,7 @@ class BatInterShort():
 
         #容量初始化
         if self.df_soh.empty:
-            batsoh=self.df_bms.loc[0,'SOH[%]']
+            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']
@@ -540,7 +540,7 @@ class BatInterShort():
 
         #容量初始化
         if self.df_soh.empty:
-            batsoh=self.df_bms.loc[0,'SOH[%]']
+            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']
@@ -639,7 +639,7 @@ class BatInterShort():
                         pass                
                 elif standingtime>3600*12:
                     standingtime=0
-                    cellvolt_now=np.array(self._avgvolt_get(i))
+                    cellvolt_now=self._avgvolt_get(i)
                     if not cellvolt_now.empty:
                         cellvolt_min=min(cellvolt_now)
                         cellvolt_max=max(cellvolt_now)

+ 97 - 15
LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/CBMSSafetyWarning.py

@@ -3,6 +3,7 @@ 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:
@@ -31,6 +32,7 @@ class SafetyWarning:
         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'
 
         #参数初始化.......................................
@@ -41,6 +43,13 @@ class SafetyWarning:
         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']
@@ -80,8 +89,7 @@ class SafetyWarning:
             cellvolt_rank=self.df_uniform['cellvolt_rank']
             cellvolt_rank=cellvolt_rank.str.replace("[", '')
             cellvolt_rank=cellvolt_rank.str.replace("]", '')
-
-        # plt.figure()
+    
         for i in range(self.param.CellVoltNums):
             #漏电流故障判断...........................................................................
             if not self.df_short.empty:
@@ -103,11 +111,11 @@ class SafetyWarning:
             #电压变化率及电压离群度.................................................................................
             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)
                 #电压变化率
-                VoltChange[volt_column[i]]=VoltChange[volt_column[i]].map(lambda x:eval(x))
-                y=VoltChange[volt_column[i]]
                 a1,b1=np.polyfit(VoltChange['time'].tolist(),y.tolist(),1)
                 y1=a1*VoltChange['time']+b1
                 y_mean=y.mean()
@@ -115,22 +123,55 @@ class SafetyWarning:
                 R2_list.append(R2)
                 
                 volt_rate.append(a1)
-                # plt.plot(xtime1,y1,label=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(loc='best')
-                # plt.title('单体电压'+str(i+1))
+                # 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()
+                # # 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(voltsigmafault)
+                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:
@@ -151,13 +192,29 @@ class SafetyWarning:
                 faultinfo='电芯{}发生热失控安全预警'.format(i+1)
                 faultadvice='1联系用户远离电池,立刻召回电池'
                 df_res.loc[0]=[time_now, time_sp, self.sn, faultcode, faultlv, faultinfo, faultadvice]
+                break
             else:
-                pass       
+                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)
@@ -165,8 +222,35 @@ class SafetyWarning:
             cellsoh_3sigma=((np.array(cellsoh)-cellsoh_mean)/cellsoh_std)
         else:
             cellsoh_3sigma=[0]*self.param.CellVoltNums
-        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:
+        
+        #电压/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)
@@ -174,7 +258,5 @@ class SafetyWarning:
                 df_res.loc[0]=[time_now, time_sp, self.sn, faultcode, faultlv, faultinfo, faultadvice]
             else:
                 pass
-        
-        # plt.show()
 
         return df_res

+ 3 - 4
LIB/MIDDLE/CellStateEstimation/BatSafetyWarning/V1_0_1/VoltStray.py

@@ -79,8 +79,8 @@ def main(sn,df_bms,celltype):
     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_standing=df_bms[(df_bms['PackCrnt']>-0.1) & (df_bms['PackCrnt']<0.1)]
+    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变化率计算
@@ -99,7 +99,6 @@ def main(sn,df_bms,celltype):
 
         VolChng = cal_volt_change(df_soc,volt_column)
     else:
-        df_bms=df_bms[df_bms['SOC[%]']<95]
         # 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']) # 删除时间相同的数据
@@ -117,7 +116,7 @@ def main(sn,df_bms,celltype):
 
     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')

+ 26 - 16
LIB/MIDDLE/CellStateEstimation/Common/V1_0_1/BatParam.py

@@ -29,6 +29,14 @@ class BatParam:
         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=-200
+        self.mk_svar=2600
+
         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],
@@ -67,8 +75,8 @@ class BatParam:
             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.3
-            self.CellOvLv2=4.35
+            self.CellOvLv1=4.2
+            self.CellOvLv2=4.25
             self.CellUvLv1=2.8
             self.CellUvLv2=2.5
             self.CellVoltDiffLv1=0.3
@@ -91,7 +99,7 @@ class BatParam:
             self.CellTempDiffLv1=10
             self.CellTempDiffLv2=15
 
-            self.TrwVoltRate=-1
+            # self.TrwVoltRate=-1
             
             self.DifVolGap = 3
             self.CellOVlmt=5
@@ -112,8 +120,8 @@ class BatParam:
             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.3
-            self.CellOvLv2=4.35
+            self.CellOvLv1=4.2
+            self.CellOvLv2=4.25
             self.CellUvLv1=2.8
             self.CellUvLv2=2.5
             self.CellVoltDiffLv1=0.3
@@ -136,7 +144,7 @@ class BatParam:
             self.CellTempDiffLv1=10
             self.CellTempDiffLv2=15
 
-            self.TrwVoltRate=-1
+            # self.TrwVoltRate=-1
             
             self.DifVolGap = 3
             self.CellOVlmt=5
@@ -157,8 +165,8 @@ class BatParam:
             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.3
-            self.CellOvLv2=4.35
+            self.CellOvLv1=4.2
+            self.CellOvLv2=4.25
             self.CellUvLv1=2.8
             self.CellUvLv2=2.5
             self.CellVoltDiffLv1=0.3
@@ -181,7 +189,7 @@ class BatParam:
             self.CellTempDiffLv1=10
             self.CellTempDiffLv2=15  
 
-            self.TrwVoltRate=-1
+            # self.TrwVoltRate=-1
             
             self.DifVolGap = 3
             self.CellOVlmt=5
@@ -201,12 +209,12 @@ class BatParam:
             self.PackCrntDec=-1
             self.BalCurrent=0.015
 
-            self.LookTab_SOC = [-5,   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.0,  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.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.3
-            self.CellOvLv2=4.35
+            self.CellOvLv1=4.2
+            self.CellOvLv2=4.25
             self.CellUvLv1=2.8
             self.CellUvLv2=2.5
             self.CellVoltDiffLv1=0.3
@@ -229,7 +237,7 @@ class BatParam:
             self.CellTempDiffLv1=10
             self.CellTempDiffLv2=15   
 
-            self.TrwVoltRate=-1    
+            # self.TrwVoltRate=-1    
             
             self.DifVolGap = 3
             self.CellOVlmt=5
@@ -285,7 +293,8 @@ class BatParam:
             self.CellTempDiffLv1=10
             self.CellTempDiffLv2=15   
 
-            self.TrwVoltRate=-8
+            # self.TrwVoltRate=-8
+            self.mk_slope=-0.6
             
             
             self.DifVolGap = 3
@@ -339,7 +348,8 @@ class BatParam:
             self.CellTempDiffLv1=28
             self.CellTempDiffLv2=32   
 
-            self.TrwVoltRate=-8  
+            # self.TrwVoltRate=-8 
+            self.mk_slope=-0.6
             
                                    
             self.DifVolGap = 3

BIN
LIB/MIDDLE/CellStateEstimation/Common/V1_0_1/__pycache__/BatParam.cpython-38.pyc


BIN
LIB/MIDDLE/CellStateEstimation/Common/V1_0_1/__pycache__/log.cpython-38.pyc


+ 1 - 1
LIB/MIDDLE/CellStateEstimation/Common/V1_0_1/log.py

@@ -17,7 +17,7 @@ class Mylog:
                 Level=logging.WARNING
             else:
                 Level=logging.ERROR
-        logging.basicConfig(filename=r'D:\Platform\platform_python\data_analyze_platform\USER\spf\101log\\'+self.name, level=Level,format='%(asctime)s - %(levelname)s - %(message)s')
+        logging.basicConfig(filename=r'D:\01WorkSpace\python\data_analyze_platform\USER\SPF\01qixiang\101Log\\'+self.name, level=Level,format='%(asctime)s - %(levelname)s - %(message)s')
 
 
     def logopt(self,*info):

+ 423 - 0
LIB/MIDDLE/InfoChrgDrive/Charge/V1_0_0/coreV0.py

@@ -0,0 +1,423 @@
+
+import datetime
+import pandas as pd   
+import numpy as np
+from DataPreProcess import DataPreProcess
+import math
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam
+import requests
+import re
+
+
+def process(data_ori_temp,cellvolt_list,celltemp_name,sn):
+    data_ori_temp=data_ori_temp.drop(['GSM信号','故障等级','故障代码','开关状态','绝缘电阻','定位类型','速度[km/h]','有效位','外电压','总输出状态','上锁状态','加热状态','航向'],axis=1,errors='ignore')
+    if sn[:4]=='MGML':
+        for k in range(len(data_ori_temp)):
+            if data_ori_temp.loc[k,'经度']==0:
+                data_ori_temp.loc[k,'经度']=np.nan
+            if data_ori_temp.loc[k,'纬度']==0:
+                data_ori_temp.loc[k,'纬度']=np.nan
+        data_ori_temp=data_ori_temp.drop(['其他温度2','其他温度6'],axis=1)
+        celltemp_name.remove('其他温度2')
+        celltemp_name.remove('其他温度6')
+    data_ori_temp.fillna(method ='pad', inplace = True , axis = 0)
+    data_ori_delnone=data_ori_temp.fillna(method ='backfill', axis = 0)
+    if data_ori_delnone.loc[0,'经度'] is None:
+        data_ori_delnone['经度']=116.417
+        data_ori_delnone['纬度']=39.917
+    data_ori_delnone = data_ori_delnone.dropna(axis = 1)
+    for name_col in cellvolt_list:
+        data_ori_delnone = data_ori_delnone.drop(data_ori_delnone[(data_ori_delnone[name_col] < 2000)].index)
+    for name_te in celltemp_name:
+        data_ori_delnone = data_ori_delnone.drop(data_ori_delnone[(data_ori_delnone[name_te] < -20)].index)
+    df_data=data_ori_delnone.reset_index(drop = True)
+    return df_data
+
+def city(df_sts_chrg,gpscity):
+    listcity=[]
+    data_sta=df_sts_chrg.reset_index(drop=True)
+    for i in range(len(data_sta)):
+        dist=[]
+        for j in range(len(gpscity)):
+            x1=data_sta.loc[i,'经度']
+            x2=gpscity.loc[j,'经度']
+            y1=data_sta.loc[i,'纬度']
+            y2=gpscity.loc[j,'纬度']
+            distance=math.sqrt((x1-x2)**2+(y1-y2)**2)
+            dist.append(distance)
+        jmin=dist.index(np.min(dist))
+        city=gpscity.loc[jmin,'城市']
+        listcity.append(city)
+    data_sta['city']=listcity
+    return data_sta
+
+def gpstemp_new(data_sta):
+    headers = {
+        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36',
+    }
+    if data_sta.loc[0,'city'][:2]=='北京':
+        response = requests.get('http://www.weather.com.cn/html/weather/101010100.shtml',headers=headers)
+    elif data_sta.loc[0,'city'][:2]=='苏州':
+        response = requests.get('http://www.weather.com.cn/html/weather/101190401.shtml',headers=headers)
+    response.encoding = response.apparent_encoding
+    # 时间,需要反转一下,因为最后一条数据对应第一个
+    x_time = re.findall('"od21":"(.*?)"',response.text)
+    x_time.reverse()
+    # 温度
+    temp = re.findall('"od22":"(.*?)"',response.text)
+    temp.reverse()
+    data_sta['气温']=''
+    for i in range(len(data_sta)):
+        for time in x_time:
+            if str(data_sta.loc[i,'时间戳'])[11:13]==time:
+                k=x_time.index(time)
+                if temp[k]!='':
+                    temp0=float(temp[k])
+                    data_sta.loc[i,'气温']=temp0    
+                else:
+                    if k>0:     
+                        data_sta.loc[i,'气温']=data_sta.loc[i-1,'气温']  
+                    else:   
+                        data_sta.loc[i,'气温']=data_sta.loc[i+1,'气温']
+    return data_sta
+
+def defcelltype(sn):
+    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: 
+        celltype=4 #CATL 50ah三元电芯
+    elif 'UD' in sn:
+        celltype=4 #CATL 50ah三元电芯
+    elif 'TJMCL' in sn: 
+        celltype=100 #金茂电芯
+    else:
+        print('SN:{},未找到对应电池类型!!!'.format(sn))
+    return celltype
+
+def stat_chrg_st(df_splice_data,param):
+    cellvolt_list = [s for s in list(df_splice_data) if '单体电压' in s] 
+    celltemp_name = [s for s in list(df_splice_data) if '温度' in s] 
+    df_time_temp = pd.to_datetime(df_splice_data['时间戳'])
+    delta_time = round((df_time_temp.iloc[-1] - df_time_temp.iloc[0])/pd.Timedelta(1, 'hours'), 3)
+    df_soc_temp = df_splice_data['SOC[%]']
+    soc_st=df_soc_temp.iloc[0]
+    soc_end=df_soc_temp.iloc[-1]
+    delta_soc = round((df_soc_temp.iloc[-1] - df_soc_temp.iloc[0]), 3)
+    if delta_soc >= 3:
+        rate_chrg = round(delta_soc/(100*delta_time), 2)   #电流的等效倍率
+    else:
+        rate_chrg = 0
+    if rate_chrg > 0.3:
+        stat_flg = 1#快充
+    elif rate_chrg > 0:
+        stat_flg = 2#慢充
+    else:
+        stat_flg = 0
+    if soc_end > 98:
+        full_chg_flg = 1#满充
+    else:
+        full_chg_flg = 0
+    listc=list(df_splice_data['总电流[A]'])
+    for k in listc:
+        if k>300:
+            listc.remove(k)
+    max_crnt = np.max(listc)   #最大有效电流
+    rate_chrg2=round(max_crnt/param.Capacity,2)
+    df_volt = df_splice_data['总电压[V]']
+    df_volt_end = df_volt.iloc[-1]   #截止电压
+    df_volt_st = df_volt.iloc[0]   #起始电压
+    #delta_volt = round((df_volt.iloc[-1] - df_volt.iloc[0]), 3)   #压变
+    df_diffvolt = df_splice_data['单体压差']
+    diffvolt_end = df_diffvolt .iloc[-1]   #截止压差
+    diffvolt_st = df_diffvolt .iloc[0]   #起始压差
+    #delta_diffvolt = round((df_diffvolt.iloc[-1] - df_diffvolt.iloc[0]), 3)  #压差变化
+    df_tem_temp = df_splice_data[celltemp_name]
+    df_tem_max = df_tem_temp.max()  #每列最大
+    df_tem_min = df_tem_temp.min()  #每列最小
+    df_tem_mean = df_tem_temp.mean()    #每列均值
+    tem_delt = max(df_tem_max - df_tem_min)    #最大温变
+    tem_mean=round(np.mean(df_tem_mean),1)  #平均温度
+    df_tem_max2 = df_tem_temp.max(axis=1)  #每行最大
+    #temp_st_max=df_tem_max2.iloc[0]  #起始最大温度
+    #temp_end_max=df_tem_max2.iloc[-1]  #结束最大温度
+    df_tem_min2 = df_tem_temp.min(axis=1)  #每行最小
+    #temp_st_min=df_tem_min2.iloc[0]  #起始最小温度
+    #temp_end_min=df_tem_min2.iloc[-1]  #结束最小温度  
+    df_tem_mean = df_tem_temp.mean(axis=1)  #每行平均
+    temp_st_mean=df_tem_mean.iloc[0]  #起始平均温度
+    temp_end_mean=df_tem_mean.iloc[-1]  #结束平均温度   
+    difftem_max = max(df_tem_max2 - df_tem_min2)    #最大温差
+    df_ovchrg_data = df_splice_data.loc[(df_splice_data['总电流[A]'] >= param.FullChrgSoc) & \
+        (df_splice_data['总电压[V]'] >= param.PackFullChrgVolt) & \
+        (df_splice_data['SOC[%]'] >= param.FullChrgSoc) & \
+        (df_splice_data[cellvolt_list].max(axis = 1) >= 1000*param.CellFullChrgVolt) & \
+        (df_splice_data['总电流[A]'] < 0) & (df_splice_data['总电压[V]'] <= param.CellVoltNums*5) & \
+        (df_splice_data['SOC[%]'] <= 200)]
+    if not df_ovchrg_data.empty:
+        ovchrg_flg = 1
+        df_ovchrg_data.reset_index(drop = True, inplace = True)
+        df_ovchrg_time = df_ovchrg_data['时间戳']
+        df_ovchrg_crnt = df_ovchrg_data['总电流[A]']
+        np_dff_time = np.diff(df_ovchrg_time)/pd.Timedelta(1, 'hours')
+        np_dff_time_new = np.append(np_dff_time, 0)
+        np_chrg_ah = np.multiply(np.array(np_dff_time_new),np.array(df_ovchrg_crnt))
+        ovchrg_prop = round(100*np_chrg_ah.sum()/param.Capacity, 3)
+    else:
+        ovchrg_flg = 0
+        ovchrg_prop = 0
+    df_sts_chrg_temp = pd.DataFrame({"sn":[df_splice_data.loc[0,'sn']], "time_st":[df_time_temp.iloc[0]], "time_end":[df_time_temp.iloc[-1]], "status":[df_splice_data.loc[0,'data_status']],"delta_time":[delta_time],
+                                    "soc_st":[soc_st], "soc_end":[soc_end], "volt_st":df_volt_st,"volt_end":df_volt_end,"diffvolt_st":[diffvolt_st],"diffvolt_end":[diffvolt_end],"temp_max":[max(df_tem_max)],
+                                    "temp_min":[min(df_tem_min)], "temp_incr":[tem_delt],"temp_mean":[tem_mean],"temp_st_mean":[temp_st_mean],"temp_end_mean":[temp_end_mean],"difftem_max":[difftem_max],
+                                    "meancrnt":[rate_chrg], "max_meancrnt":[rate_chrg2],"sts_flg":[stat_flg], "full_chrg_flg":[full_chg_flg],"ovchrg_flg":[ovchrg_flg],"ovchrg_prop":[ovchrg_prop],
+                                    "gps_lon":[round(df_splice_data.loc[0, '经度'], 3)], "gps_lat":[round(df_splice_data.loc[0, '纬度'], 3)],"city":[df_splice_data.loc[0,'city']],"airtemp_st":[df_splice_data.loc[0,'气温']],
+                                    "airtemp_end":[df_splice_data.loc[len(df_splice_data)-1,'气温']]})
+    return df_sts_chrg_temp
+
+
+
+def gpstemp(data_sta2,temp):
+    data_sta=data_sta2.copy()
+    maxtemp1=[]
+    mintemp1=[]
+    maxtemp2=[]
+    mintemp2=[]
+    for k in range(len(data_sta)):
+        city=data_sta.loc[k,'city']
+        date1=str(data_sta.loc[k,'time_st'])[:10]
+        date2=str(data_sta.loc[k,'time_end'])[:10]
+        for l in range(len(temp)):
+            if (date1==temp.loc[l,'日期'][:10]) & (city==temp.loc[l,'城市']):
+                t1=float(temp.loc[l,'最高气温'][:-1])
+                t2=float(temp.loc[l,'最低气温'][:-1])
+                maxtemp1.append(t1)
+                mintemp1.append(t2)
+            if (date2==temp.loc[l,'日期'][:10]) & (city==temp.loc[l,'城市']):
+                t3=float(temp.loc[l,'最高气温'][:-1])
+                t4=float(temp.loc[l,'最低气温'][:-1])
+                maxtemp2.append(t3)
+                mintemp2.append(t4)
+            
+    data_sta['开始最高气温']=maxtemp1
+    data_sta['开始最低气温']=mintemp1
+    data_sta['结束最高气温']=maxtemp2
+    data_sta['结束最低气温']=mintemp2
+    return data_sta
+
+def gpstemp2(data_sta3):
+    data_sta=data_sta3.copy()
+    temp11=[]
+    temp22=[]
+    for k in range(len(data_sta)):
+        time1=str(data_sta.loc[k,'time_st'])[11:13]
+        time2=str(data_sta.loc[k,'time_end'])[11:13]    
+        if time1 in ['00','01','02','03','04','05']:
+            temp11.append(data_sta.loc[k,'开始最低气温'])
+        elif time1 in ['06','23','07','22']:
+            temp11.append(min(data_sta.loc[k,'开始最低气温']+2,data_sta.loc[k,'开始最高气温']))
+        elif time1 in ['08','09','21','20']:
+            temp11.append(min(data_sta.loc[k,'开始最低气温']+5,data_sta.loc[k,'开始最高气温']))
+        elif time1 in ['10','11','19','18']:
+            temp11.append(min(data_sta.loc[k,'开始最低气温']+9,data_sta.loc[k,'开始最高气温']))
+        else:
+            temp11.append(data_sta.loc[k,'开始最高气温'])
+        if time2 in ['00','01','02','03','04','05']:
+            temp22.append(data_sta.loc[k,'结束最低气温'])
+        elif time2 in ['06','23','07','22']:
+            temp22.append(min(data_sta.loc[k,'开始最低气温']+2,data_sta.loc[k,'开始最高气温']))
+        elif time2 in ['08','09','21','20']:
+            temp22.append(min(data_sta.loc[k,'开始最低气温']+5,data_sta.loc[k,'开始最高气温']))
+        elif time2 in ['10','11','19','18']:
+            temp22.append(min(data_sta.loc[k,'开始最低气温']+9,data_sta.loc[k,'开始最高气温']))
+        else:
+            temp22.append(data_sta.loc[k,'开始最高气温'])
+    data_sta['airtemp_st']=temp11
+    data_sta['airtemp_end']=temp22
+    return data_sta
+
+def newsplit(data_new):  #重新分段:根据时间差和经纬度
+    data_split=pd.DataFrame()
+    splice_num = np.unique(data_new['data_split_by_status_time']) 
+    for item in splice_num: 
+        df_splice_data = data_new[data_new['data_split_by_status_time'] == item]
+        df_splice_data.reset_index(inplace = True, drop = True) 
+        data_split=data_split.append(df_splice_data.loc[[0,len(df_splice_data)-1],:])
+    data_split=data_split.reset_index(drop = True)
+    data_split2=data_split.copy()
+    for k in range(1,len(data_split)):
+        if (data_split.loc[k,'data_split_by_status_time']==data_split.loc[k-1,'data_split_by_status_time']) & \
+            (data_split.loc[k,'data_status']=='charge') & \
+            (data_split.loc[k,'SOC[%]']==data_split.loc[k-1,'SOC[%]']):
+            data_split2=data_split2.drop([k-1,k])
+        if (data_split.loc[k,'data_split_by_status_time']==data_split.loc[k-1,'data_split_by_status_time']) & \
+            (data_split.loc[k,'data_status']=='drive') & \
+            (round(data_split.loc[k,'纬度'],2)==round(data_split.loc[k-1,'纬度'],2)) & \
+            (round(data_split.loc[k,'经度'],2)==round(data_split.loc[k-1,'经度'],2)) :
+            data_split2=data_split2.drop([k-1,k])
+    data_split2=data_split2.reset_index(drop = True)
+    data_split3=data_split2.copy()
+    for k in range(1,len(data_split2)):
+        if (data_split2.loc[k,'data_split_by_status_time']!=data_split2.loc[k-1,'data_split_by_status_time']) & \
+            (data_split2.loc[k,'data_status']==data_split2.loc[k-1,'data_status']) & \
+            ((data_split2.loc[k,'时间戳']-data_split2.loc[k-1,'时间戳']).total_seconds()/60<10):
+            data_split3.loc[k+1,'data_split_by_status_time']=data_split3.loc[k-1,'data_split_by_status_time']
+            data_split3=data_split3.drop([k-1,k])
+    data_split3=data_split3.reset_index(drop = True)
+    return data_split3
+
+def chrgst_time(data_split):   #重新计算前后闲置时长
+    for k in range(1,len(data_split)):
+        if (data_split.loc[k,'data_status']=='charge') & (data_split.loc[k-1,'data_status']=='stand'):
+            delta_time_f=(data_split.loc[k-1,'时间戳']-data_split.loc[k-2,'时间戳']).total_seconds()/60
+            data_split.loc[k,'stand_time']=delta_time_f
+        if (data_split.loc[k,'data_status']=='charge') & (data_split.loc[k-1,'data_status']=='drive'):
+            delta_time_f=0
+            data_split.loc[k,'stand_time']=delta_time_f
+        if (data_split.loc[k,'data_status']=='stand') & (data_split.loc[k-1,'data_status']=='charge'):
+            delta_time_b=(data_split.loc[k+1,'时间戳']-data_split.loc[k,'时间戳']).total_seconds()/60
+            data_split.loc[k-1,'stand_time']=delta_time_b
+        if (data_split.loc[k,'data_status']=='drive') & (data_split.loc[k-1,'data_status']=='charge'):
+            delta_time_b=0
+            data_split.loc[k-1,'stand_time']=delta_time_b     
+    return data_split
+
+def chrgdr(data_split):   #根据充电信息结果合并相连同状态
+    chrg=pd.DataFrame()
+    if data_split.loc[0,'data_status']=='charge':
+        chrg=chrg.append(data_split.iloc[k]) 
+
+    for k in range(1,len(data_split)-1):
+        if data_split.loc[k,'data_status']=='charge':
+            if data_split.loc[k-1,'data_status']=='stand':
+                chrg=chrg.append(data_split.iloc[k-2:k+1]) 
+            else: 
+                chrg=chrg.append(data_split.iloc[k]) 
+        if data_split.loc[k,'data_status']=='stand':
+            if data_split.loc[k-1,'data_status']=='charge':
+                chrg=chrg.append(data_split.iloc[k:k+2])    
+    return chrg
+
+def makedf_chrgdr(df_splice_chrg,data_new):   #每个status取所有时间戳的数据
+    time1=df_splice_chrg.loc[0,'时间戳']
+    time2=df_splice_chrg.loc[1,'时间戳']
+    status=df_splice_chrg.loc[0,'data_status']
+    split=df_splice_chrg.loc[0,'data_split_by_status_time']
+    index1=data_new[(data_new['时间戳']==time1) & (data_new['data_status']==status)].index.tolist()[0]
+    index2=data_new[(data_new['时间戳']==time2) & (data_new['data_status']==status)].index.tolist()[-1]
+    df_chrg=data_new.iloc[index1:index2+1]
+    df_chrg['data_split_by_status_time']=split
+    df_chrg.reset_index(drop=True,inplace=True)
+    return df_chrg
+
+def change_new(df_sts_chrg,chrg_last):
+    df_sts_chrg.reset_index(drop=True,inplace=True)
+    time_last=chrg_last['time_end']
+    time_first=df_sts_chrg.loc[0,'time_st']
+    time_last=datetime.datetime.strptime(time_last,'%Y-%m-%d %H:%M:%S')
+    if (chrg_last['status']==df_sts_chrg.loc[0,'status']) & ((time_first-time_last).total_seconds()/60<10) & \
+        (round(chrg_last['gps_lat'],2)==round(df_sts_chrg.loc[0,'gps_lat'],2)) & \
+        (round(chrg_last['gps_lon'],2)==round(df_sts_chrg.loc[0,'gps_lon'],2)) :
+        chrg_last['time_end']=df_sts_chrg.loc[0,'time_end']
+        chrg_last['delta_time']=chrg_last['delta_time']+df_sts_chrg.loc[0,'delta_time']
+        chrg_last['soc_end']=df_sts_chrg.loc[0,'soc_end']
+        chrg_last['volt_end']=df_sts_chrg.loc[0,'volt_end']
+        chrg_last['diffvolt_end']=df_sts_chrg.loc[0,'diffvolt_end']
+        chrg_last['temp_max']=np.max([df_sts_chrg.loc[0,'temp_max'],chrg_last['temp_max']])
+        chrg_last['temp_min']=np.min([df_sts_chrg.loc[0,'temp_min'],chrg_last['temp_min']])
+        chrg_last['temp_incr']=df_sts_chrg.loc[0,'temp_incr']+chrg_last['temp_incr']
+        chrg_last['temp_mean']=round(np.mean([df_sts_chrg.loc[0,'temp_mean'],chrg_last['temp_mean']]),1)
+        #chrg_last['temp_end_max']=df_sts_chrg.loc[0,'temp_end_max']
+        #chrg_last['temp_end_min']=df_sts_chrg.loc[0,'temp_end_min']
+        chrg_last['temp_end_mean']=df_sts_chrg.loc[0,'temp_end_mean']
+        chrg_last['difftem_max']=np.max([df_sts_chrg.loc[0,'difftem_max'],chrg_last['difftem_max']])
+        delta_soc=chrg_last['soc_end']-chrg_last['soc_st']
+        if delta_soc >= 3:
+            rate_chrg =round(delta_soc/(100*chrg_last['delta_time']), 2)   #电流的等效倍率
+        else:
+            rate_chrg = 0
+        if rate_chrg > 0.3:
+            stat_flg = 1#快充
+        elif rate_chrg > 0:
+            stat_flg = 2#慢充
+        else:
+            stat_flg = 0
+        if chrg_last['soc_end'] > 98:
+            full_chg_flg = 1#满充
+        else:
+            full_chg_flg = 0
+        chrg_last['meancrnt']= rate_chrg
+        chrg_last['max_meancrnt']=np.max([df_sts_chrg.loc[0,'max_meancrnt'],chrg_last['max_meancrnt']])
+        chrg_last['sts_flg']= stat_flg
+        chrg_last['full_chrg_flg']= full_chg_flg
+        chrg_last['ovchrg_flg']= np.max([df_sts_chrg.loc[0,'ovchrg_flg'],chrg_last['ovchrg_flg']])
+        chrg_last['ovchrg_prop']=df_sts_chrg.loc[0,'ovchrg_prop']+chrg_last['ovchrg_prop']
+        chrg_last['standtime_b']=df_sts_chrg.loc[0,'standtime_b']
+        chrg_last['airtemp_end']=df_sts_chrg.loc[0,'airtemp_end']
+        chrg_last=pd.DataFrame(chrg_last).T
+        df_sts_chrg=df_sts_chrg.drop([0])
+
+    else:
+        chrg_last=pd.DataFrame()
+    return df_sts_chrg,chrg_last
+
+def sep_chrg_dr(df_merge,sn,gpscity):
+    cellvolt_list = [s for s in list(df_merge) if '单体电压' in s] 
+    celltemp_name = [s for s in list(df_merge) if '温度' in s] 
+    df_data=process(df_merge,cellvolt_list,celltemp_name,sn)
+    celltype=defcelltype(sn)
+    param=BatParam.BatParam(celltype)       
+    df_data['总电流[A]']=param.PackCrntDec*df_data['总电流[A]']
+    data_new=DataPreProcess.data_split_by_status(df_data,df_data)
+    data_new=DataPreProcess.data_split_by_time(data_new,data_new) 
+    data_new=DataPreProcess.cal_stand_time(data_new,data_new)
+    data_new.reset_index(inplace = True, drop = True)
+    data_sta=city(data_new,gpscity)
+    data_sta=data_sta.reset_index(drop=True)
+    data_sta2=gpstemp_new(data_sta)
+    data_sta2.to_csv('data_sta2.csv')
+    data_split=newsplit(data_sta2)
+    data_split.to_csv('data_split.csv')
+    data_split=chrgst_time(data_split)
+   
+    chrg=chrgdr(data_split)
+    return chrg,data_sta2,param
+
+def mkdf_chrg(chrg,data_new,param):
+    splice_num= np.unique(chrg['data_split_by_status_time'])  
+    df_sts_chrg=pd.DataFrame()     
+    for item in splice_num: 
+        df_splice_chrg = chrg[chrg['data_split_by_status_time'] == item] 
+        df_splice_chrg.reset_index(inplace = True, drop = True)
+        df_chrg=makedf_chrgdr(df_splice_chrg,data_new)
+    
+        df_sts_chrg_temp=stat_chrg_st(df_chrg,param)
+        
+        if df_splice_chrg.loc[0,'data_status'] =='charge':
+            stand_dur_f=df_splice_chrg.loc[0,'stand_time']
+            stand_dur_b=df_splice_chrg.loc[1,'stand_time']
+        else:
+            stand_dur_f=0
+            stand_dur_b=0 
+        df_sts_chrg_temp['standtime_f']=round(stand_dur_f,3)
+        df_sts_chrg_temp['standtime_b']=round(stand_dur_b,3)
+        df_sts_chrg=df_sts_chrg.append(df_sts_chrg_temp)
+    return df_sts_chrg
+
+def pro_output(df_merge,sn,gpscity,chrg_last):
+    new=pd.DataFrame()
+    change=pd.DataFrame()
+    if len(df_merge)>0:
+        chrg,data_new,param=sep_chrg_dr(df_merge,sn,gpscity)
+        chrg.to_csv('chrg.csv')
+        if len(chrg)>0:
+            data_sta=mkdf_chrg(chrg, data_new,param)
+            data_sta.to_csv('data_sta.csv')
+            new=data_sta.copy() 
+            if len(chrg_last)>0:
+                new,change=change_new(data_sta,chrg_last)
+    return new,change

+ 40 - 0
LIB/MIDDLE/InfoChrgDrive/Charge/gps.csv

@@ -0,0 +1,40 @@
+,城市,经度,纬度
+0,北京市市中⼼,116.417,39.917
+1,上海市区,121.433,34.5
+2,天津市区,117.2,39.133
+3,⾹港,114.1,22.2
+4,⼴州,113.233,23.167
+5,珠海,113.517,22.3
+6,深圳,114.067,22.617
+7,杭州,120.2,30.267
+8,重庆市区,106.45,29.567
+9,青岛,120.333,36.067
+10,厦门,118.1,24.467
+11,福州,119.3,26.083
+12,兰州,103.733,36.033
+13,贵阳,106.717,26.567
+14,长沙,113.0,28.217
+15,南京,118.783,32.05
+16,南昌,115.9,28.683
+17,沈阳,123.383,41.8
+18,太原,112.533,37.867
+19,成都,104.067,30.667
+20,拉萨,91.0,29.6
+21,乌鲁⽊齐,87.683,43.767
+22,昆明,102.733,25.05
+23,西安,108.95,34.267
+24,西宁,101.75,36.567
+25,银川,106.267,38.467
+26,兰浩特,122.083,46.067
+27,哈尔滨,126.633,45.75
+28,长春,125.35,43.883
+29,武汉,114.317,30.517
+30,郑州,113.65,34.767
+31,⽯家庄,114.483,38.033
+32,三亚,109.5,18.2
+33,海⼝,110.35,20.017
+34,澳门,113.5,22.2
+35,苏州,120.37,31.19
+36,苏州,120.37,31.19
+37,苏州,120.37,31.19
+38,苏州,120.37,31.19

+ 116 - 0
LIB/MIDDLE/InfoChrgDrive/Charge/main_V0.py

@@ -0,0 +1,116 @@
+from coreV0 import *
+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
+from LIB.MIDDLE.CellStateEstimation.Common import log
+
+#...................................充电技术指标统计函数......................................................................................................................
+def diag_cal():
+    global SNnums
+
+    start=time.time()
+    now_time=datetime.datetime.now()
+    start_time=now_time-datetime.timedelta(hours=24)
+    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='qx_cas'
+    user='qx_read'
+    password='Qx@123456'
+
+    #读取结果库数据......................................................
+    param='sn,time_st,time_end,status,delta_time,soc_st,soc_end,volt_st,volt_end,diffvolt_st,diffvolt_end, \
+        temp_max,temp_min,temp_incr,temp_mean,temp_st_mean,temp_end_mean,difftem_max,meancrnt,max_meancrnt, \
+        sts_flg,full_chrg_flg,ovchrg_flg,ovchrg_prop,gps_lon,gps_lat,standtime_f,standtime_b,city,airtemp_st,airtemp_end'
+    tablename='algo_charge_info'
+    mysql = pymysql.connect (host=host, user=user, password=password, port=port, database=db)
+    cursor = mysql.cursor()
+    sql =  "select %s from %s where time_end='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_info_charge.txt','error')
+    mylog.logcfg()
+
+    for sn in SNnums:
+        try:
+            
+            #读取原始数据库数据........................................................................................................................................................
+            dbManager = DBManager.DBManager()
+            df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms','gps'])
+            df_bms = df_data['bms']
+            df_gps = df_data['gps']
+
+            #读取城市天气数据........................................................................................................................................................
+            gpscity=pd.read_csv('gps.csv')
+            
+
+            #调用主函数................................................................................................................................................................
+            
+            if not df_bms.empty:   #BMS数据非空
+                df_merge=pd.merge(df_bms,df_gps,how='outer',sort=True)
+                df_merge['sn']=sn
+                df_diag_ram_sn=pd.Series()
+                if not df_diag_ram.empty:   #结果库非空
+                    df_diag_ram_sn=df_diag_ram[df_diag_ram['sn']==sn]
+                    if not df_diag_ram_sn.empty:   #该sn相关结果非空
+                        df_diag_ram_sn.reset_index(inplace=True,drop=True)
+                df_diag_new,df_diag_change=pro_output(df_merge,sn,gpscity,df_diag_ram_sn)
+                if not df_diag_change.empty:   #需变更的结果非空
+                    cursor.execute("DELETE FROM algo_charge_info WHERE time_end = '0000-00-00 00:00:00' and sn='{}'".format(sn))
+                    mysql.commit()
+                    df_diag_change.to_sql("algo_charge_info",con=db_res_engine, if_exists="append",index=False)
+
+                #新增结果存入结果库.....................................................................
+                if not df_diag_new.empty:  #需新增的结果非空
+                    df_diag_new.to_sql("algo_charge_info",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'
+    excelpath='sn-20210903.xlsx'
+    dataSOH = pd.read_excel('sn-20210903.xlsx',sheet_name='sn-20210903')
+    SNnums = list(dataSOH['sn'])
+    
+    mylog=log.Mylog('log_info_charge.txt','error')
+    mylog.logcfg()
+
+    diag_cal()
+    #定时任务.......................................................................................................................................................................
+    scheduler = BlockingScheduler()
+    scheduler.add_job(diag_cal, 'interval', hours=24)
+
+    try:  
+        scheduler.start()
+    except Exception as e:
+        scheduler.shutdown()
+        print(repr(e))
+        mylog.logopt(e)

BIN
LIB/MIDDLE/InfoChrgDrive/Charge/sn-20210903.xlsx


BIN
LIB/MIDDLE/InfoChrgDrive/Charge/输出表单.xlsx


+ 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)

+ 21 - 9
LIB/MIDDLE/SaftyCenter/DataDiag_Static/SC_BMSUploadError.py

@@ -18,7 +18,7 @@ from sqlalchemy import create_engine
 from sqlalchemy.orm import sessionmaker
 from urllib import parse
 import pymysql
-
+from urllib import parse
 class BMSReportError:
     def __init__(self):
         pass
@@ -52,10 +52,8 @@ class BMSReportError:
                                 nCode=int(math.pow(2,power))
                                 newCode.append(nCode)
                                 code=code-nCode
-                                newCode_total.append(nCode)
                         elif code>0:
-                            newCode=newCode.append(code)
-                            newCode_total.append(code)
+                            newCode.append(code)
                         else:
                             newCode=[]
                     else:
@@ -63,7 +61,6 @@ class BMSReportError:
                 else:
                     newCode=df_bms.loc[i,'故障代码']
                     if not pd.isnull(newCode) and newCode!=0:
-                        newCode_total.append(newCode)
                         newCode=[newCode]
                     else:
                         newCode=[]                        
@@ -82,7 +79,7 @@ class BMSReportError:
                         # DbSession = sessionmaker(bind=db_engine)
 
                         # errorcode_map = pd.read_sql("select * from faultcode_map", db_engine)
-                        # if 'k50' in sn:
+                        # if 'K50' in sn:
                         #     FactoryType=1
                         # elif 'MGMCL' in sn or 'UD' in sn:
                         #     FactoryType=2
@@ -91,17 +88,30 @@ class BMSReportError:
                         
                              
                         code = newCode[j] # 终端故障码
-                        platform_code = errorcode_map[(errorcode_map['protocol']==FactoryType)&(errorcode_map['end_errorcode']==str(code))]['platform_errorcode']                   
+                        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:
+                        else:                       
+                            host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+                            port=3306
+                            db='qx_cas'
+                            user='qx_algo_readonly'
+                            password = parse.quote_plus('qx@123456')                   
+                            db_engine_1 = create_engine("mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(user, password, host, port, db))
+                            df_fidinfo = pd.read_sql("select * from fid_info ", db_engine_1)
+                            faltinfo=df_fidinfo[df_fidinfo['errCode']==platform_code.values[0]]['errChinese'].values[0]
+                            faltsugst=df_fidinfo[df_fidinfo['errCode']==platform_code.values[0]]['treatment'].values[0]
+                                                  
+                            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,'故障等级'],'','']
+                                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,'故障等级'],faltinfo,faltsugst]
                             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:
@@ -112,4 +122,6 @@ class BMSReportError:
                     pass
         df_Diag_Ram=df_Diag_Ram_BMS.append(df_Diag_Ram_Cloud)
         df_Diag_Ram=df_Diag_Ram.reset_index(drop=True)
+        if len(df_Diag_Ram):
+            print(df_Diag_Ram)
         return df_Diag_Ram

+ 4 - 4
LIB/MIDDLE/SaftyCenter/DataDiag_Static/SC_CtrlSafty.py

@@ -53,19 +53,19 @@ class CtrlSafty:
         OtherTempMax=OtherTempMax[OtherTempMax[OtherTempNum]<(param.OtherOTlmt+5)].dropna(axis=0,how='all')
         if len(CellTempMax) and len(OtherTempMax):
             QuitErrCount[57]=0
-            if not 57 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,57,4,'电芯温度高','立即联系用户确认电池状态']
+            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 57 in df_Diag_Ram['code'].values.tolist():
+            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']==57].index,['end_time']]=end_time
+                    df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C357'].index,['end_time']]=end_time
                 else:
                     pass
             else:

+ 84 - 84
LIB/MIDDLE/SaftyCenter/DataDiag_Static/SC_SamplingSafty.py

@@ -65,73 +65,73 @@ class SamplingSafty:
             AvgVol=CellVoltage.mean()
         #—————————————————————————————电压无效和断线判断———————————————————————
         if len(InVMaxBatNo):
-            if len(InVMinBatNo):
-                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,2,'电池电压采样断线,最高电压为{:.2f}V,电池编号为{},最低电压为{:.2f}V,电池编号为{}'.format(MaxVolt,InVMaxBatNo.values,MinVolt,InVMinBatNo.values),'返厂维修']
-                    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]>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']==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,2,str(InVMaxBatNo.values)+'号电压大于{:.2f}V,采样无效'.format(param.CellOVlmt),'返厂维修']
-                    ErrorFlg=1
-                else:
-                    pass
-        elif len(InVMinBatNo):
-            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,2,str(InVMinBatNo.values)+'号电压小于{:.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]>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:
+        #     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 51 in df_Diag_Ram['code']:
+            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 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.values)+'号电池电压离群','技术介入诊断']
+                    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 51 in df_Diag_Ram['code'].values.tolist():
+                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']==51].index,['end_time']]=end_time                
+                        df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C251'].index,['end_time']]=end_time                
                     else:
                         pass
                 else:
@@ -176,44 +176,44 @@ class SamplingSafty:
         if len(InVMaxTempBatNo):
             if len(InVMinTempBatNo):
                 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,2,'电池温度采样断线,最高温度为{}℃,电池编号为{},最低温度为{}℃,电池编号为{}'.format(maxCellTemp,InVMaxTempBatNo.values,minCellTemp,InVMinTempBatNo.values),'返厂维修']
+                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 53 in df_Diag_Ram['code'].values.tolist():
+                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']==53].index,['end_time']]=end_time
+                        df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C353'].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,2,str(InVMaxTempBatNo.values)+'号温度大于{}℃,采样无效'.format(param.PackOTlmt),'联系用户核实电池温度情况,并返厂维修']
+                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 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,2,str(InVMinTempBatNo.values)+'号温度小于{}℃,采样无效'.format(param.PackUTlmt),'返厂维修']
+            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 2 in df_Diag_Ram['code'].values.tolist():
+            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']==2].index,['end_time']]=end_time                   
+                    df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C302'].index,['end_time']]=end_time                   
                 else:
                     pass
             else:
@@ -223,19 +223,19 @@ class SamplingSafty:
             OutlierTempNo=AvgCellTempGap[AvgCellTempGap>=param.AvgCellTempGap].index
             if len(OutlierTempNo):
                 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.values)+'号电池异常温度离群','技术介入诊断']
+                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 8 in df_Diag_Ram['code'].values.tolist():
+                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']==8].index,['end_time']]=end_time
+                        df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C108'].index,['end_time']]=end_time
                     else:
                         pass
                 else:
@@ -263,44 +263,44 @@ class SamplingSafty:
             if len(InVMaxOtherTempBatNo):
                 if len(InVMinOtherTempBatNo):
                     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,2,'其他温度采样断线,最高温度为{}℃,传感器编号为{},最低温度为{}℃,传感器编号为{}'.format(maxOtherTemp,InVMaxOtherTempBatNo.values,minOtherTemp,InVMinOtherTempBatNo.values),'返厂维修']
+                    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 54 in df_Diag_Ram['code'].values.tolist():
+                    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']==54].index,['end_time']]=end_time
+                            df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C354'].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,2,'传感器温度大于{}℃,采样无效'.format(param.OtherOTlmt),'联系用户核实电池温度情况,并返厂维修']
+                    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 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,2,'传感器温度小于{}℃,采样无效'.format(param.OtherUTlmt),'返厂维修']
+                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 55 in df_Diag_Ram['code'].values.tolist():
+                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']==55].index,['end_time']]=end_time                   
+                        df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C355'].index,['end_time']]=end_time                   
                     else:
                         pass
                 else:
@@ -310,19 +310,19 @@ class SamplingSafty:
                 OutlierOtherTempNo=AvgOtherTempGap[AvgOtherTempGap>=param.AvgOtherTempGap].index
                 if len(OutlierOtherTempNo):
                     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,'传感器温度异常离群','技术介入诊断']
+                    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 56 in df_Diag_Ram['code'].values.tolist():
+                    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']==56].index,['end_time']]=end_time
+                            df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C156'].index,['end_time']]=end_time
                         else:
                             pass
                     else:
@@ -330,31 +330,31 @@ class SamplingSafty:
 
                 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,'传感器温度异常离群','技术立即介入诊断']
+                    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 56 in df_Diag_Ram['code'].values.tolist():
+                    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']==56].index,['end_time']]=end_time
+                                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 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.values)+'号电芯温度异常离群','技术立即介入诊断']
+                    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 8 in df_Diag_Ram['code'].values.tolist():
+                    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']==8].index,['end_time']]=end_time              
+                                df_Diag_Ram.loc[df_Diag_Ram[df_Diag_Ram['code']=='C108'].index,['end_time']]=end_time              
 
         return df_Diag_Ram

+ 16 - 10
LIB/MIDDLE/SaftyCenter/DataDiag_Static/main.py

@@ -22,6 +22,8 @@ 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()
@@ -40,7 +42,9 @@ def diag_cal():
     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',tablename=tablename2,factory='',sn='',timename='',st='',sp='')
+        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)
     
@@ -73,9 +77,10 @@ def diag_cal():
         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)
-        print(df_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
@@ -116,14 +121,15 @@ def diag_cal():
         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)
+            #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_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        
@@ -285,8 +291,8 @@ if __name__ == "__main__":
     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 = ['MGMLXN750N218N004'] #SNnums_6040
+    #SNnums=['TJMCL120502305038','TJMCL120502305032','TJMCL120502305022','TJMCL120502305026','TJMCL120502305032','TJMCL120502305044','TJMCL120502305048','TJMCL120502305012','TJMCL120502305010']
+    SNnums = ['PK504B10100004341'] #SNnums_6040
     
     mylog=log.Mylog('log.txt','error')
     mylog.logcfg()

+ 417 - 0
LIB/MIDDLE/SaftyCenter/DataDiag_Static/main_2.ipynb

@@ -0,0 +1,417 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "### start to get data PK504B10100004349 from 2021-12-30 18:00:04 to 2021-12-30 18:15:04\n",
+      "# get data from 2021-12-30 18:00:04 to 2021-12-30 18:15:04......... \n",
+      "all data-getting done, bms_count is 41, gps_count is 0, system_count is 0, accum_count is 0 \n",
+      "\n"
+     ]
+    }
+   ],
+   "source": [
+    "\n",
+    "__author__ = 'lmstack'\n",
+    "#coding=utf-8\n",
+    "import os\n",
+    "import datetime\n",
+    "import pandas as pd\n",
+    "from LIB.BACKEND import DBManager, Log\n",
+    "from sqlalchemy import create_engine\n",
+    "from sqlalchemy.orm import sessionmaker\n",
+    "import time, datetime\n",
+    "import dateutil.relativedelta\n",
+    "import traceback\n",
+    "from LIB.MIDDLE.CellStateEstimation.Common import log\n",
+    "from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import DBDownload as DBDownload\n",
+    "import time, datetime\n",
+    "from pandas.core.frame import DataFrame\n",
+    "from apscheduler.schedulers.blocking import BlockingScheduler\n",
+    "from LIB.MIDDLE.SaftyCenter.DataDiag_Static.DataStatistics import DataSta\n",
+    "from LIB.MIDDLE.SaftyCenter.DataDiag_Static.SC_CtrlSafty import CtrlSafty\n",
+    "from LIB.MIDDLE.SaftyCenter.DataDiag_Static.DiagDataMerge import DiagDataMerge\n",
+    "from LIB.MIDDLE.SaftyCenter.DataDiag_Static.SC_SamplingSafty import SamplingSafty\n",
+    "from LIB.MIDDLE.SaftyCenter.DataDiag_Static.SC_BMSUploadError import BMSReportError\n",
+    "from LIB.MIDDLE.SaftyCenter.DataDiag_Static import CBMSBatDiag\n",
+    "from LIB.MIDDLE.SaftyCenter.Common import DBDownload as DBDw\n",
+    "from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam as QX_BatteryParam\n",
+    "from urllib import parse\n",
+    "import pymysql\n",
+    "\n",
+    "# 故障映射表\n",
+    "host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'\n",
+    "port=3306\n",
+    "db='algo_dict'\n",
+    "user='qx_algo_rw'\n",
+    "password=parse.quote_plus('qx@123456')\n",
+    "db_engine = create_engine(\n",
+    "    \"mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8\".format(\n",
+    "        user, password, host, port, db\n",
+    "    ))\n",
+    "\n",
+    "errorcode_map = pd.read_sql(\"select * from faultcode_map\", db_engine)\n",
+    "db_engine.dispose()\n",
+    "sn = 'PK504B10100004349'\n",
+    "start_time = '2021-12-30 18:00:04'\n",
+    "end_time = '2021-12-30 18:15:04'\n",
+    "dbManager = DBManager.DBManager()\n",
+    "df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms'])\n",
+    "df_bms = df_data['bms']\n",
+    "df_bms=df_bms.dropna(subset=['总电流[A]'])\n",
+    "df_bms=df_bms.reset_index(drop=True)\n",
+    "df_Diag_Batdiag_update=BMSReportError.main(sn,df_bms,pd.DataFrame(),1,errorcode_map)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<div>\n",
+       "<style scoped>\n",
+       "    .dataframe tbody tr th:only-of-type {\n",
+       "        vertical-align: middle;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe tbody tr th {\n",
+       "        vertical-align: top;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe thead th {\n",
+       "        text-align: right;\n",
+       "    }\n",
+       "</style>\n",
+       "<table border=\"1\" class=\"dataframe\">\n",
+       "  <thead>\n",
+       "    <tr style=\"text-align: right;\">\n",
+       "      <th></th>\n",
+       "      <th>start_time</th>\n",
+       "      <th>end_time</th>\n",
+       "      <th>product_id</th>\n",
+       "      <th>code</th>\n",
+       "      <th>level</th>\n",
+       "      <th>info</th>\n",
+       "      <th>advice</th>\n",
+       "    </tr>\n",
+       "  </thead>\n",
+       "  <tbody>\n",
+       "    <tr>\n",
+       "      <th>0</th>\n",
+       "      <td>2021-12-30 18:01:21</td>\n",
+       "      <td>2022-01-05 20:24:31</td>\n",
+       "      <td>PK504B10100004349</td>\n",
+       "      <td>B021</td>\n",
+       "      <td>0</td>\n",
+       "      <td></td>\n",
+       "      <td></td>\n",
+       "    </tr>\n",
+       "  </tbody>\n",
+       "</table>\n",
+       "</div>"
+      ],
+      "text/plain": [
+       "            start_time             end_time         product_id  code level  \\\n",
+       "0  2021-12-30 18:01:21  2022-01-05 20:24:31  PK504B10100004349  B021     0   \n",
+       "\n",
+       "  info advice  \n",
+       "0              "
+      ]
+     },
+     "execution_count": 2,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "df_Diag_Batdiag_update"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'\n",
+    "port=3306\n",
+    "database='algo_dict'\n",
+    "user='qx_algo_readonly'\n",
+    "password='qx@123456'\n",
+    "\n",
+    "db_engine = create_engine(\"mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8\".format(user, password, host, port, database))\n",
+    "DbSession = sessionmaker(bind=db_engine)\n",
+    "\n",
+    "errorcode_map = pd.read_sql(\"select * from faultcode_map\", db_engine)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'\n",
+    "port=3306\n",
+    "db='algo_dict'\n",
+    "user='qx_algo_rw'\n",
+    "password=parse.quote_plus('qx@123456')\n",
+    "db_engine = create_engine(\n",
+    "    \"mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8\".format(\n",
+    "        user, password, host, port, db\n",
+    "    ))\n",
+    "\n",
+    "errorcode_map2 = pd.read_sql(\"select * from faultcode_map\", db_engine)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<div>\n",
+       "<style scoped>\n",
+       "    .dataframe tbody tr th:only-of-type {\n",
+       "        vertical-align: middle;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe tbody tr th {\n",
+       "        vertical-align: top;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe thead th {\n",
+       "        text-align: right;\n",
+       "    }\n",
+       "</style>\n",
+       "<table border=\"1\" class=\"dataframe\">\n",
+       "  <thead>\n",
+       "    <tr style=\"text-align: right;\">\n",
+       "      <th></th>\n",
+       "      <th>start_time</th>\n",
+       "      <th>end_time</th>\n",
+       "      <th>product_id</th>\n",
+       "      <th>code</th>\n",
+       "      <th>level</th>\n",
+       "      <th>info</th>\n",
+       "      <th>advice</th>\n",
+       "    </tr>\n",
+       "  </thead>\n",
+       "  <tbody>\n",
+       "    <tr>\n",
+       "      <th>0</th>\n",
+       "      <td>2021-12-30 18:01:21</td>\n",
+       "      <td>2021-12-30 21:55:31</td>\n",
+       "      <td>PK504B10100004349</td>\n",
+       "      <td>B021</td>\n",
+       "      <td>0</td>\n",
+       "      <td></td>\n",
+       "      <td></td>\n",
+       "    </tr>\n",
+       "  </tbody>\n",
+       "</table>\n",
+       "</div>"
+      ],
+      "text/plain": [
+       "            start_time             end_time         product_id  code level  \\\n",
+       "0  2021-12-30 18:01:21  2021-12-30 21:55:31  PK504B10100004349  B021     0   \n",
+       "\n",
+       "  info advice  \n",
+       "0              "
+      ]
+     },
+     "execution_count": 2,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "df_Diag_Batdiag_update"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<div>\n",
+       "<style scoped>\n",
+       "    .dataframe tbody tr th:only-of-type {\n",
+       "        vertical-align: middle;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe tbody tr th {\n",
+       "        vertical-align: top;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe thead th {\n",
+       "        text-align: right;\n",
+       "    }\n",
+       "</style>\n",
+       "<table border=\"1\" class=\"dataframe\">\n",
+       "  <thead>\n",
+       "    <tr style=\"text-align: right;\">\n",
+       "      <th></th>\n",
+       "      <th>id</th>\n",
+       "      <th>protocol</th>\n",
+       "      <th>end_errorcode</th>\n",
+       "      <th>platform_errorcode</th>\n",
+       "    </tr>\n",
+       "  </thead>\n",
+       "  <tbody>\n",
+       "    <tr>\n",
+       "      <th>0</th>\n",
+       "      <td>1</td>\n",
+       "      <td>1</td>\n",
+       "      <td>1</td>\n",
+       "      <td>B003</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>1</th>\n",
+       "      <td>2</td>\n",
+       "      <td>1</td>\n",
+       "      <td>2</td>\n",
+       "      <td>B006</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>2</th>\n",
+       "      <td>3</td>\n",
+       "      <td>1</td>\n",
+       "      <td>4</td>\n",
+       "      <td>B009</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>3</th>\n",
+       "      <td>4</td>\n",
+       "      <td>1</td>\n",
+       "      <td>8</td>\n",
+       "      <td>B012</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>4</th>\n",
+       "      <td>5</td>\n",
+       "      <td>1</td>\n",
+       "      <td>16</td>\n",
+       "      <td>B015</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>...</th>\n",
+       "      <td>...</td>\n",
+       "      <td>...</td>\n",
+       "      <td>...</td>\n",
+       "      <td>...</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>75</th>\n",
+       "      <td>99</td>\n",
+       "      <td>3</td>\n",
+       "      <td>163</td>\n",
+       "      <td>B090</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>76</th>\n",
+       "      <td>100</td>\n",
+       "      <td>3</td>\n",
+       "      <td>164</td>\n",
+       "      <td>B091</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>77</th>\n",
+       "      <td>101</td>\n",
+       "      <td>3</td>\n",
+       "      <td>68</td>\n",
+       "      <td>B112</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>78</th>\n",
+       "      <td>102</td>\n",
+       "      <td>3</td>\n",
+       "      <td>69</td>\n",
+       "      <td>B112</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>79</th>\n",
+       "      <td>103</td>\n",
+       "      <td>3</td>\n",
+       "      <td>1</td>\n",
+       "      <td>B117</td>\n",
+       "    </tr>\n",
+       "  </tbody>\n",
+       "</table>\n",
+       "<p>80 rows × 4 columns</p>\n",
+       "</div>"
+      ],
+      "text/plain": [
+       "     id  protocol end_errorcode platform_errorcode\n",
+       "0     1         1             1               B003\n",
+       "1     2         1             2               B006\n",
+       "2     3         1             4               B009\n",
+       "3     4         1             8               B012\n",
+       "4     5         1            16               B015\n",
+       "..  ...       ...           ...                ...\n",
+       "75   99         3           163               B090\n",
+       "76  100         3           164               B091\n",
+       "77  101         3            68               B112\n",
+       "78  102         3            69               B112\n",
+       "79  103         3             1               B117\n",
+       "\n",
+       "[80 rows x 4 columns]"
+      ]
+     },
+     "execution_count": 4,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "errorcode_map2"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "interpreter": {
+   "hash": "b3ba2566441a7c06988d0923437866b63cedc61552a5af99d1f4fb67d367b25f"
+  },
+  "kernelspec": {
+   "display_name": "Python 3.8.8 64-bit ('base': conda)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.8.8"
+  },
+  "orig_nbformat": 4
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}

+ 177 - 0
LIB/MIDDLE/SaftyCenter/DataDiag_Static/main_2.py

@@ -0,0 +1,177 @@
+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
+    #..................................设置时间..........................................................  
+    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_copy'
+    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',tablename=tablename2,factory='',sn='',timename='',st='',sp='')
+    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)
+        
+        start_time = '2021-12-30 18:00:04'
+        end_time = '2021-12-30 18:15:04'
+        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']
+        df_bms=df_bms.dropna(subset=['总电流[A]'])
+        df_bms=df_bms.reset_index(drop=True)
+        print(df_bms)
+        #读取结果数据库数据........................................................................................................................................................
+        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=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        
+
+
+#...............................................主函数.......................................................................................................................
+if __name__ == "__main__":
+    global SNnums
+    global task_on
+    
+    task_on=0
+    SNnums = ['PK504B10100004349']
+    
+    mylog=log.Mylog('log.txt','error')
+
+    #............................模块运行前,先读取数据库中所有结束时间为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)

+ 127 - 0
LIB/MIDDLE/SaftyCenter/Low_Soc_Alarm/dev.ipynb

@@ -0,0 +1,127 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 28,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import numpy as np\n",
+    "import pandas as pd\n",
+    "import datetime,time\n",
+    "from sqlalchemy import create_engine\n",
+    "from sqlalchemy.orm import sessionmaker\n",
+    "from urllib import parse\n",
+    "import pymysql\n",
+    "host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'\n",
+    "port=3306\n",
+    "db='qx_cas'\n",
+    "user='qx_algo_rw'\n",
+    "password='qx@123456'\n",
+    "\n",
+    "db_qxcas_engine = create_engine(\n",
+    "    \"mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8\".format(\n",
+    "        user, parse.quote_plus(password), host, port, db\n",
+    "    ))\n",
+    "\n",
+    "db = 'safety_platform'\n",
+    "conn = pymysql.connect(host=host, port=port, user=user, password=password, database=db)\n",
+    "cursor = conn.cursor()\n",
+    "db_sp_engine = create_engine(\n",
+    "    \"mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8\".format(\n",
+    "        user, parse.quote_plus(password), host, port, db\n",
+    "    ))\n",
+    "sql = \"select sn, DATE_FORMAT(time, '%%Y-%%m-%%d %%H:%%i:%%s') as time, level from lowsoc_info where add_time >DATE_FORMAT( DATE_SUB(CURDATE(), INTERVAL 0 DAY), '%%Y-%%m-%%d 00:00:00')\"+ \\\n",
+    "    \" and  add_time <DATE_FORMAT( DATE_SUB(CURDATE(), INTERVAL 0 DAY), '%%Y-%%m-%%d 23:59:59')\"           \n",
+    "df_lowsocalarm_lastday = pd.read_sql(sql, db_qxcas_engine)\n",
+    "sn_lowsoc = df_lowsocalarm_lastday['sn'].tolist()\n",
+    "\n",
+    "sql = \"select sn, DATE_FORMAT(add_time, '%%Y-%%m-%%d %%H:%%i:%%s') as time, level, last_time from offline_info where add_time >= DATE_FORMAT( DATE_SUB(CURDATE(), INTERVAL 0 DAY), '%%Y-%%m-%%d 00:00:00')\"+ \\\n",
+    "    \" and add_time <= DATE_FORMAT( DATE_SUB(CURDATE(), INTERVAL 0 DAY), '%%Y-%%m-%%d 23:59:59')\"           \n",
+    "df_offlinealarm_lastday = pd.read_sql(sql, db_qxcas_engine)\n",
+    "sn_offline = df_offlinealarm_lastday['sn'].tolist()\n",
+    "\n",
+    "sql = \"select * from all_fault_info_copy1 where end_time='{}' and code={}\".format('0000-00-00 00:00:00', 58)\n",
+    "df_all_alarm_info = pd.read_sql(sql, db_sp_engine)\n",
+    "sn_allalarm = df_all_alarm_info['product_id'].tolist()\n",
+    "\n",
+    "# 遍历昨日报警和报警列表,如果 1:sn存在于昨日报警,但不存在与报警allinfo里,则添加一条新的报警记录到allinfo里;\n",
+    "#                           2:如果sn存在于allinfo但不存在于昨日报警,也不存在于昨日离线,则记录故障为结束;\n",
+    "#                           3: 如果sn在两个表中都存在,但级别不同,则更新级别(离线暂时不考虑级别)。\n",
+    "sn_new = list(set(sn_lowsoc)-set(sn_allalarm))\n",
+    "sn_close = list(set(sn_allalarm)-set(sn_lowsoc)-set(sn_offline))\n",
+    "# sn_cross = list(set(sn_lowsoc) & set(sn_allalarm))\n",
+    "# 新增\n",
+    "df_new = pd.DataFrame(columns=['add_time', 'product_id', 'start_time', 'end_time', 'code'])\n",
+    "now = datetime.datetime.now()\n",
+    "for sn in sn_new:\n",
+    "    df = df_lowsocalarm_lastday[df_lowsocalarm_lastday['sn']==sn]\n",
+    "    df_new = df_new.append({'add_time':now, 'product_id':sn, 'start_time':df['time'].values[0], 'end_time':'0000-00-00 00:00:00', 'code':58}, ignore_index=True)\n",
+    "if not df_new.empty:\n",
+    "    df_new.to_sql('all_fault_info_copy1', db_sp_engine, if_exists='append', index=False)\n",
+    "\n",
+    "# 修改\n",
+    "now = datetime.datetime.now()\n",
+    "for sn in sn_close:\n",
+    "    df = df_lowsocalarm_lastday[df_lowsocalarm_lastday['sn']==sn]\n",
+    "    df1 = df_all_alarm_info[df_all_alarm_info['product_id']==sn]\n",
+    "    sql = ''' update all_fault_info_copy1 set update_time='{}', end_time='{}' where product_id = '{}' and start_time = '{}' and code = {}'''.format \\\n",
+    "                (now, datetime.datetime.strftime(now,\"%Y-%m-%d %H:%M:%S\"), sn, pd.to_datetime(df1['start_time'].values[0]), df1['code'].values[0])\n",
+    "    cursor.execute(sql)\n",
+    "    conn.commit()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 23,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "[]"
+      ]
+     },
+     "execution_count": 23,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "sn_close"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "interpreter": {
+   "hash": "b3ba2566441a7c06988d0923437866b63cedc61552a5af99d1f4fb67d367b25f"
+  },
+  "kernelspec": {
+   "display_name": "Python 3.8.8 64-bit ('base': conda)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.8.8"
+  },
+  "orig_nbformat": 4
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}

+ 2 - 2
LIB/MIDDLE/SaftyCenter/Low_Soc_Alarm/low_soc_alarm_sta.py

@@ -20,7 +20,7 @@ def low_soc_alarm_sta(df_sns, db_qxcas_engine, conn, db_sp_engine):
     df_offlinealarm_lastday = pd.read_sql(sql, db_qxcas_engine)
     sn_offline = df_offlinealarm_lastday['sn'].tolist()
 
-    sql = "select * from all_fault_info where end_time='{}' and code={}".format('0000-00-00 00:00:00', 58)
+    sql = "select * from all_fault_info where end_time='{}' and code='{}'".format('0000-00-00 00:00:00', 'C307')
     df_all_alarm_info = pd.read_sql(sql, db_sp_engine)
     sn_allalarm = df_all_alarm_info['product_id'].tolist()
 
@@ -36,7 +36,7 @@ def low_soc_alarm_sta(df_sns, db_qxcas_engine, conn, db_sp_engine):
     for sn in sn_new:
         df = df_lowsocalarm_lastday[df_lowsocalarm_lastday['sn']==sn]
         factory = df_sns[df_sns['sn']==sn]['factory'].values[0]
-        df_new = df_new.append({'add_time':now, 'product_id':sn, 'factory':factory, 'start_time':df['time'].values[0], 'end_time':'0000-00-00 00:00:00', 'info':"低电量报警", 'code':58, 'level':1}, ignore_index=True)
+        df_new = df_new.append({'add_time':now, 'product_id':sn, 'factory':factory, 'start_time':df['time'].values[0], 'end_time':'0000-00-00 00:00:00', 'info':"低电量报警", 'code':'C307', 'level':3}, ignore_index=True)
     if not df_new.empty:
         df_new.to_sql('all_fault_info', db_sp_engine, if_exists='append', index=False)
 

+ 198 - 0
LIB/MIDDLE/SaftyCenter/Offline/dev.ipynb

@@ -0,0 +1,198 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 42,
+   "metadata": {},
+   "outputs": [
+    {
+     "ename": "ValueError",
+     "evalue": "unsupported format character 'Y' (0x59) at index 35",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[1;31mValueError\u001b[0m                                Traceback (most recent call last)",
+      "\u001b[1;32m<ipython-input-42-def20a744eb0>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m     44\u001b[0m \u001b[0msql\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;34m\"select sn, DATE_FORMAT(add_time, '%Y-%%m-%%d %%H:%%i:%%s') as time, level, last_time from offline_info where add_time >= DATE_FORMAT( DATE_SUB(CURDATE(), INTERVAL 0 DAY), '%%Y-%%m-%%d 00:00:00')\"\u001b[0m\u001b[1;33m+\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m\\\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     45\u001b[0m     \u001b[1;34m\" and add_time <= DATE_FORMAT( DATE_SUB(CURDATE(), INTERVAL 0 DAY), '%%Y-%%m-%%d 23:59:59')\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 46\u001b[1;33m \u001b[0mdf_offlinealarm_lastday\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mpd\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mread_sql\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0msql\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mdb_qxcas_engine\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     47\u001b[0m \u001b[0msn_offline\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mdf_offlinealarm_lastday\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'sn'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtolist\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     48\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
+      "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\pandas\\io\\sql.py\u001b[0m in \u001b[0;36mread_sql\u001b[1;34m(sql, con, index_col, coerce_float, params, parse_dates, columns, chunksize)\u001b[0m\n\u001b[0;32m    519\u001b[0m         )\n\u001b[0;32m    520\u001b[0m     \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 521\u001b[1;33m         return pandas_sql.read_query(\n\u001b[0m\u001b[0;32m    522\u001b[0m             \u001b[0msql\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    523\u001b[0m             \u001b[0mindex_col\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mindex_col\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
+      "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\pandas\\io\\sql.py\u001b[0m in \u001b[0;36mread_query\u001b[1;34m(self, sql, index_col, coerce_float, parse_dates, params, chunksize)\u001b[0m\n\u001b[0;32m   1306\u001b[0m         \u001b[0margs\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0m_convert_params\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0msql\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1307\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 1308\u001b[1;33m         \u001b[0mresult\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m   1309\u001b[0m         \u001b[0mcolumns\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mresult\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mkeys\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1310\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
+      "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\pandas\\io\\sql.py\u001b[0m in \u001b[0;36mexecute\u001b[1;34m(self, *args, **kwargs)\u001b[0m\n\u001b[0;32m   1174\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0mexecute\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1175\u001b[0m         \u001b[1;34m\"\"\"Simple passthrough to SQLAlchemy connectable\"\"\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 1176\u001b[1;33m         \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mconnectable\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mexecution_options\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m   1177\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1178\u001b[0m     def read_table(\n",
+      "\u001b[1;32m<string>\u001b[0m in \u001b[0;36mexecute\u001b[1;34m(self, statement, *multiparams, **params)\u001b[0m\n",
+      "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\sqlalchemy\\util\\deprecations.py\u001b[0m in \u001b[0;36mwarned\u001b[1;34m(fn, *args, **kwargs)\u001b[0m\n\u001b[0;32m    388\u001b[0m         \u001b[1;32mif\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0mskip_warning\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    389\u001b[0m             \u001b[0m_warn_with_version\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mmessage\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mversion\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mwtype\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mstacklevel\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m3\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 390\u001b[1;33m         \u001b[1;32mreturn\u001b[0m \u001b[0mfn\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    391\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    392\u001b[0m     \u001b[0mdoc\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mfunc\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__doc__\u001b[0m \u001b[1;32mis\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[1;32mNone\u001b[0m \u001b[1;32mand\u001b[0m \u001b[0mfunc\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__doc__\u001b[0m \u001b[1;32mor\u001b[0m \u001b[1;34m\"\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
+      "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\sqlalchemy\\engine\\base.py\u001b[0m in \u001b[0;36mexecute\u001b[1;34m(self, statement, *multiparams, **params)\u001b[0m\n\u001b[0;32m   3035\u001b[0m         \"\"\"\n\u001b[0;32m   3036\u001b[0m         \u001b[0mconnection\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mconnect\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mclose_with_result\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mTrue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 3037\u001b[1;33m         \u001b[1;32mreturn\u001b[0m \u001b[0mconnection\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mstatement\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m*\u001b[0m\u001b[0mmultiparams\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mparams\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m   3038\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   3039\u001b[0m     @util.deprecated_20(\n",
+      "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\sqlalchemy\\engine\\base.py\u001b[0m in \u001b[0;36mexecute\u001b[1;34m(self, statement, *multiparams, **params)\u001b[0m\n\u001b[0;32m   1183\u001b[0m             )\n\u001b[0;32m   1184\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 1185\u001b[1;33m             return self._exec_driver_sql(\n\u001b[0m\u001b[0;32m   1186\u001b[0m                 \u001b[0mstatement\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1187\u001b[0m                 \u001b[0mmultiparams\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
+      "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\sqlalchemy\\engine\\base.py\u001b[0m in \u001b[0;36m_exec_driver_sql\u001b[1;34m(self, statement, multiparams, params, execution_options, future)\u001b[0m\n\u001b[0;32m   1482\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1483\u001b[0m         \u001b[0mdialect\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdialect\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 1484\u001b[1;33m         ret = self._execute_context(\n\u001b[0m\u001b[0;32m   1485\u001b[0m             \u001b[0mdialect\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1486\u001b[0m             \u001b[0mdialect\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mexecution_ctx_cls\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_init_statement\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
+      "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\sqlalchemy\\engine\\base.py\u001b[0m in \u001b[0;36m_execute_context\u001b[1;34m(self, dialect, constructor, statement, parameters, execution_options, *args, **kw)\u001b[0m\n\u001b[0;32m   1746\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1747\u001b[0m         \u001b[1;32mexcept\u001b[0m \u001b[0mBaseException\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 1748\u001b[1;33m             self._handle_dbapi_exception(\n\u001b[0m\u001b[0;32m   1749\u001b[0m                 \u001b[0me\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mstatement\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mparameters\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcursor\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1750\u001b[0m             )\n",
+      "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\sqlalchemy\\engine\\base.py\u001b[0m in \u001b[0;36m_handle_dbapi_exception\u001b[1;34m(self, e, statement, parameters, cursor, context)\u001b[0m\n\u001b[0;32m   1931\u001b[0m                 )\n\u001b[0;32m   1932\u001b[0m             \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 1933\u001b[1;33m                 \u001b[0mutil\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mraise_\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mexc_info\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mwith_traceback\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mexc_info\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m   1934\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1935\u001b[0m         \u001b[1;32mfinally\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
+      "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\sqlalchemy\\util\\compat.py\u001b[0m in \u001b[0;36mraise_\u001b[1;34m(***failed resolving arguments***)\u001b[0m\n\u001b[0;32m    209\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    210\u001b[0m         \u001b[1;32mtry\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 211\u001b[1;33m             \u001b[1;32mraise\u001b[0m \u001b[0mexception\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    212\u001b[0m         \u001b[1;32mfinally\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    213\u001b[0m             \u001b[1;31m# credit to\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
+      "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\sqlalchemy\\engine\\base.py\u001b[0m in \u001b[0;36m_execute_context\u001b[1;34m(self, dialect, constructor, statement, parameters, execution_options, *args, **kw)\u001b[0m\n\u001b[0;32m   1703\u001b[0m                             \u001b[1;32mbreak\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1704\u001b[0m                 \u001b[1;32mif\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0mevt_handled\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 1705\u001b[1;33m                     self.dialect.do_execute(\n\u001b[0m\u001b[0;32m   1706\u001b[0m                         \u001b[0mcursor\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mstatement\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mparameters\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1707\u001b[0m                     )\n",
+      "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\sqlalchemy\\engine\\default.py\u001b[0m in \u001b[0;36mdo_execute\u001b[1;34m(self, cursor, statement, parameters, context)\u001b[0m\n\u001b[0;32m    690\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    691\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0mdo_execute\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcursor\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mstatement\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mparameters\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 692\u001b[1;33m         \u001b[0mcursor\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mstatement\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mparameters\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    693\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    694\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0mdo_execute_no_params\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcursor\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mstatement\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
+      "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\pymysql\\cursors.py\u001b[0m in \u001b[0;36mexecute\u001b[1;34m(self, query, args)\u001b[0m\n\u001b[0;32m    144\u001b[0m             \u001b[1;32mpass\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    145\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 146\u001b[1;33m         \u001b[0mquery\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmogrify\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mquery\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0margs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    147\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    148\u001b[0m         \u001b[0mresult\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_query\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mquery\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
+      "\u001b[1;32mC:\\ProgramData\\Anaconda3\\lib\\site-packages\\pymysql\\cursors.py\u001b[0m in \u001b[0;36mmogrify\u001b[1;34m(self, query, args)\u001b[0m\n\u001b[0;32m    123\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    124\u001b[0m         \u001b[1;32mif\u001b[0m \u001b[0margs\u001b[0m \u001b[1;32mis\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 125\u001b[1;33m             \u001b[0mquery\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mquery\u001b[0m \u001b[1;33m%\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_escape_args\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mconn\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    126\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    127\u001b[0m         \u001b[1;32mreturn\u001b[0m \u001b[0mquery\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
+      "\u001b[1;31mValueError\u001b[0m: unsupported format character 'Y' (0x59) at index 35"
+     ]
+    }
+   ],
+   "source": [
+    "import numpy as np\n",
+    "import pandas as pd\n",
+    "import datetime,time\n",
+    "from sqlalchemy import create_engine\n",
+    "from sqlalchemy.orm import sessionmaker\n",
+    "from urllib import parse\n",
+    "import pymysql\n",
+    "\n",
+    "host='172.16.121.236'\n",
+    "port=3306\n",
+    "db='fastfun'\n",
+    "user='readonly'\n",
+    "password='Fast1234'\n",
+    "\n",
+    "db_iotp_engine = create_engine(\n",
+    "    \"mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8\".format(\n",
+    "        user, parse.quote_plus(password), host, port, db\n",
+    "    ))\n",
+    "\n",
+    "host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'\n",
+    "port=3306\n",
+    "db='qx_cas'\n",
+    "user='qx_algo_rw'\n",
+    "password='qx@123456'\n",
+    "\n",
+    "db_qxcas_engine = create_engine(\n",
+    "    \"mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8\".format(\n",
+    "        user, parse.quote_plus(password), host, port, db\n",
+    "    ))\n",
+    "\n",
+    "db = 'safety_platform'\n",
+    "conn = pymysql.connect(host=host, port=port, user=user, password=password, database=db)\n",
+    "cursor = conn.cursor()\n",
+    "db_sp_engine = create_engine(\n",
+    "    \"mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8\".format(\n",
+    "        user, parse.quote_plus(password), host, port, db\n",
+    "    ))\n",
+    "\n",
+    "# sql = \"select sn, DATE_FORMAT(time, '%%Y-%%m-%%d %%H:%%i:%%s') as time, level from lowsoc_info where time >DATE_FORMAT( DATE_SUB(CURDATE(), INTERVAL 1 DAY), '%%Y-%%m-%%d 00:00:00')\"+ \\\n",
+    "#     \" and  time <DATE_FORMAT( DATE_SUB(CURDATE(), INTERVAL 1 DAY), '%%Y-%%m-%%d 23:59:59')\"           \n",
+    "# df_lowsocalarm_lastday = pd.read_sql(sql, db_qxcas_engine)\n",
+    "# sn_lowsoc = df_lowsocalarm_lastday['sn'].tolist()\n",
+    "\n",
+    "sql = \"select sn, DATE_FORMAT(add_time, '%Y-%m-%d %H:%i:%s') as time, level, last_time from offline_info where add_time >= DATE_FORMAT( DATE_SUB(CURDATE(), INTERVAL 0 DAY), '%Y-%m-%d 00:00:00')\"+ \\\n",
+    "    \" and add_time <= DATE_FORMAT( DATE_SUB(CURDATE(), INTERVAL 0 DAY), '%Y-%m-%d 23:59:59')\"           \n",
+    "df_offlinealarm_lastday = pd.read_sql(sql, db_qxcas_engine)\n",
+    "sn_offline = df_offlinealarm_lastday['sn'].tolist()\n",
+    "\n",
+    "sql = \"select * from all_fault_info_copy1 where end_time='{}' and code={}\".format('0000-00-00 00:00:00', 59)\n",
+    "df_all_alarm_info = pd.read_sql(sql, db_sp_engine)\n",
+    "sn_allalarm = df_all_alarm_info['product_id'].tolist()\n",
+    "\n",
+    "# 遍历昨日报警和报警列表,如果 1:sn存在于昨日报警,但不存在与报警allinfo里,则添加一条新的报警记录到allinfo里;\n",
+    "#                           2:如果sn存在于allinfo但不存在于昨日报警,则记录故障为结束;\n",
+    "#                           3: 如果sn在两个表中都存在,但级别不同,则更新级别(离线暂时不考虑级别)。\n",
+    "sn_new = list(set(sn_offline)-set(sn_allalarm))\n",
+    "sn_close = list(set(sn_allalarm)-set(sn_offline))\n",
+    "# sn_cross = list(set(sn_offline) & set(sn_allalarm))\n",
+    "\n",
+    "# 新增\n",
+    "df_new = pd.DataFrame(columns=['add_time', 'product_id', 'start_time', 'end_time', 'code'])\n",
+    "now = datetime.datetime.now()\n",
+    "for sn in sn_new:\n",
+    "    df = df_offlinealarm_lastday[df_offlinealarm_lastday['sn']==sn]\n",
+    "    df_new = df_new.append({'add_time':now, 'product_id':sn, 'start_time':df['time'].values[0], 'end_time':'0000-00-00 00:00:00', 'code':59}, ignore_index=True)\n",
+    "if not df_new.empty:\n",
+    "    df_new.to_sql('all_fault_info_copy1', db_sp_engine, if_exists='append', index=False)\n",
+    "\n",
+    "# 修改\n",
+    "now = datetime.datetime.now()\n",
+    "for sn in sn_close:\n",
+    "    df = df_offlinealarm_lastday[df_offlinealarm_lastday['sn']==sn]\n",
+    "    df1 = df_all_alarm_info[df_all_alarm_info['product_id']==sn]\n",
+    "    sql = \"select * from ff_battery_status where devcode = '{}'\".format(sn)\n",
+    "    df_on = pd.read_sql(sql, db_iotp_engine)\n",
+    "    sql = ''' update all_fault_info_copy1 set update_time='{}', end_time='{}' where product_id = '{}' and start_time = '{}' and code = {}'''.format \\\n",
+    "                (now, pd.to_datetime(df_on['status_time'].values[0]), sn, pd.to_datetime(df1['start_time'].values[0]), df1['code'].values[0])\n",
+    "    cursor.execute(sql)\n",
+    "    conn.commit()\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import numpy as np\n",
+    "import pandas as pd\n",
+    "import datetime,time\n",
+    "from sqlalchemy import create_engine\n",
+    "from sqlalchemy.orm import sessionmaker\n",
+    "from urllib import parse\n",
+    "import pymysql\n",
+    "host='rm-bp10j10qy42bzy0q7.mysql.rds.aliyuncs.com'\n",
+    "port=3306\n",
+    "db='qixiang_manage'\n",
+    "user='qx_query'\n",
+    "password='@Qx_query'\n",
+    "\n",
+    "conn = pymysql.connect(host=host, port=port, user=user, password=password, database=db)\n",
+    "cursor = conn.cursor()\n",
+    "cursor.execute(\"select qrcode from py_battery where status=3\")\n",
+    "res = cursor.fetchall()\n",
+    "ignore_sns = pd.DataFrame(res, columns=['sn'])['sn'].tolist()\n",
+    "conn.close()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "303"
+      ]
+     },
+     "execution_count": 11,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "df_alarm_lastday"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "interpreter": {
+   "hash": "b3ba2566441a7c06988d0923437866b63cedc61552a5af99d1f4fb67d367b25f"
+  },
+  "kernelspec": {
+   "display_name": "Python 3.8.8 64-bit ('base': conda)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.8.8"
+  },
+  "orig_nbformat": 4
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}

+ 321 - 0
LIB/MIDDLE/odo/DailyMileageEstimation/V1_0_4/calcul_mileage.py

@@ -0,0 +1,321 @@
+import pandas as pd
+import numpy as np
+import os
+import math
+import datetime
+import time
+
+#将时间戳由 "%Y-%m-%d %H:%M:%S" 切换为 sec
+def timeconvert(df_in,column_name):  
+    df=df_in.copy()
+    df.index=range(len(df))
+    time=df[column_name]
+    timeInSeries=[]
+    time2=datetime.datetime.strptime(str(time[0]),"%Y-%m-%d %H:%M:%S")
+    for k in range(len(time)):
+        time1=datetime.datetime.strptime(str(time[k]),"%Y-%m-%d %H:%M:%S")    
+        t=(time1-time2)
+        timeInSeries.append(t.days*86400+t.seconds)
+    df.loc[:,'相对时间[s]']=pd.DataFrame(timeInSeries,columns=['相对时间[s]'])
+    return df
+
+#计算累积能量
+def cal_accumKwh(df_in):
+    df_in1=df_in.copy()
+    df1=df_in1[['总电流[A]','总电压[V]','相对时间[s]']]
+    df1=df1.dropna(axis=0,how='any')
+    I=df1['总电流[A]'].values
+    V=df1['总电压[V]'].values
+    t=df1['相对时间[s]'].values
+    accumAh=[0.0]
+    for k in range(1,len(I)):
+        accumAh_temp=(t[k]-t[k-1])*((I[k]+I[k-1])/(2*3600))*(V[k]+V[k-1])/2/1000
+        accumAh.append(accumAh[-1]+accumAh_temp)
+    df1.loc[:,'累积能量[Kwh]']=accumAh
+    df_out=pd.merge(df_in1,df1[['累积能量[Kwh]']],how='left',left_index=True, right_index=True)
+    return(df_out)
+
+#将时间格式化为整数
+def str_data_to_num(str_data):
+    # 格式时间成毫秒
+    strptime = time.strptime(str_data,"%Y-%m-%d %H:%M:%S")
+    # print("strptime",strptime)
+    mktime = int(time.mktime(strptime)*1000)
+    # print("mktime",mktime)
+    return mktime
+
+def df_date_To_int(df_in):
+    df=df_in.copy()
+    for k in range(len(df)):
+        df.loc[k,'绝对时间[ms]']=str_data_to_num(df['时间戳'][k])
+    return df
+
+#根据经纬度获取两点之间的距离
+def cal_dis_meters(radius,latitude1, longitude1,latitude2, longitude2):  
+    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
+
+#第一部分距离计算
+def cal_distance1(part1,avg_cost):
+    part1.index=[i for i in range(len(part1))]
+    part1=part1.copy()
+    part1.loc[0,'△距离']=0
+    AccumEnergy1=part1['累积能量[Kwh]'].values
+    for k1 in range(1,len(part1)):
+        if (AccumEnergy1[k1]-AccumEnergy1[k1-1])>0:
+            part1.loc[k1,'△距离']=(AccumEnergy1[k1]-AccumEnergy1[k1-1])*avg_cost
+            part1.loc[k1,'方法']=4
+        else:
+            part1.loc[k1,'△距离']=0
+            part1.loc[k1,'方法']=5
+    return part1
+
+#第二部分计算
+def cal_distance2(part2,avg_cost):
+    part2_gps=part2[part2['纬度']>0]
+    part2_gps.index=[i for i in range(len(part2_gps))]
+    times_list=part2_gps['相对时间[s]'].values
+    lat=part2_gps['纬度'].values
+    lng=part2_gps['经度'].values
+    AccumEnergy2=part2_gps['累积能量[Kwh]'].values
+    part2_gps=part2_gps.copy()
+    part2_gps.loc[0,'△距离']=0
+    for k2 in range(1,len(part2_gps)):
+        delta_energy=AccumEnergy2[k2]-AccumEnergy2[k2-1]
+        delta_span=cal_dis_meters(6378.137,lat[k2], lng[k2],lat[k2-1], lng[k2-1])
+        v_spd=3600*delta_span/(times_list[k2]-times_list[k2-1]) 
+        if times_list[k2]-times_list[k2-1]<60:
+            if v_spd>70 :
+                part2_gps.loc[k2,'△距离']=delta_energy*avg_cost
+                part2_gps.loc[k2,'方法']=1_1
+            else:
+                part2_gps.loc[k2,'△距离']=delta_span
+                part2_gps.loc[k2,'方法']=1_2
+        else:            
+            if delta_energy<=0:
+                part2_gps.loc[k2,'△距离']=0
+                part2_gps.loc[k2,'方法']=2
+            else:
+                if v_spd>70:
+                    part2_gps.loc[k2,'△距离']=delta_energy*avg_cost
+                    part2_gps.loc[k2,'方法']=3_1
+                else:
+                    part2_gps.loc[k2,'△距离']=max(delta_span,delta_energy*avg_cost)
+                    part2_gps.loc[k2,'方法']=3_2
+    return part2_gps
+
+#第三部分距离计算
+def cal_distance3(part3,avg_cost):
+    part3.index=[i for i in range(len(part3))]
+    part3=part3.copy()
+    part3.loc[0,'△距离']=0
+    AccumEnergy3=part3['累积能量[Kwh]'].values
+    for k3 in range(1,len(part3)):
+        if (AccumEnergy3[k3]-AccumEnergy3[k3-1])>0:
+            part3.loc[k3,'△距离']=(AccumEnergy3[k3]-AccumEnergy3[k3-1])*avg_cost
+            part3.loc[k3,'方法']=4
+        else:
+            part3.loc[k3,'△距离']=0
+            part3.loc[k3,'方法']=5
+    return part3
+
+def real_odo(df_in,avg_cost):
+    # df_handle=df_in.copy()
+    df=timeconvert(df_in,"时间戳")#计算相对时间
+    df=cal_accumKwh(df)#计算累积能量
+    positive_lat_index=df[df['纬度']>0].index
+    if len(positive_lat_index)>2:
+        first_index=positive_lat_index[0]
+        end_index=positive_lat_index[-1]
+        #将数据分割为有第一个GPS数据之前(part1)、
+        #有最后一个GPS数据之后(part3)以及
+        #有第一个GPS数据和最后一个GPS数据中间的数据(part3)
+        part1=df[0:first_index+1]
+        part2=df[first_index:end_index+1]
+        part3=df[end_index:-1]
+        part1=cal_distance1(part1,avg_cost)
+        part2_gps=cal_distance2(part2,avg_cost)
+        part3=cal_distance3(part3,avg_cost) 
+        df_out=pd.concat([part1,part2_gps[1:],part3[1:]])
+    else:
+        part1=df
+        part2=df[0:0]
+        part3=df[0:0]
+        part1=cal_distance1(part1,avg_cost)
+        df_out=part1.copy()
+    
+    #计算累积里程
+    df_out.loc[0,'累积里程[km]']=0
+    df_out.index=[i for i in range(len(df_out))]
+
+    for k in range(1,len(df_out)):
+        df_out.loc[k,'累积里程[km]']=df_out.loc[k-1,'累积里程[km]']+df_out.loc[k,'△距离']
+    return(df_out)
+
+def calcul_mileage(sn,data_bms,data_gps):
+
+    #合并两张表格
+    df_bms=data_bms.copy()
+    df_gps=data_gps.copy()
+    #删除纬度小于10的点
+    for k in range(1,len(df_gps)):
+        if df_gps.loc[k,'纬度']<10:
+            df_gps=df_gps.drop(k)
+    df_bms.set_index(["时间戳"], inplace=True)
+    df_gps.set_index(["时间戳"], inplace=True)
+    merge_df1=pd.merge(df_bms, df_gps,how='outer', left_index=True, right_index=True)
+    merge_df1.loc[:,'时间戳']=merge_df1.index
+    merge_df1.index=[i for i in range(len(merge_df1))]
+
+    #参数定义
+    cost_min=34 #最小能耗km/kwh
+    cost_max=45 #最高能耗km/kwh
+
+    df_input=merge_df1[['时间戳','总电流[A]', '总电压[V]','SOC[%]','充电状态','纬度', '经度']]
+    #电流电压数据修复
+    df_input=timeconvert(df_input,"时间戳")#计算相对时间
+    related_times=df_input['相对时间[s]'].values
+    lat_list=df_input['纬度'].values
+    lng_list=df_input['经度'].values
+    #Vlt_list=df_input['总电压[V]'].values
+    for k in range(1,len(df_input)):
+        if math.isnan(df_input.loc[k,'总电流[A]']):
+            if related_times[k]-related_times[k-1]<5:
+                df_input.loc[k,'总电流[A]']=df_input.loc[k-1,'总电流[A]']
+                df_input.loc[k,'总电压[V]']=df_input.loc[k-1,'总电压[V]']
+                df_input.loc[k,'修复']=1
+            elif lat_list[k-1]>0:
+                delta_odo=cal_dis_meters(6378.137,lat_list[k], lng_list[k],lat_list[k-1], lng_list[k-1])
+                df_input.loc[k,'总电流[A]']=1000*3600*(delta_odo/cost_min)/70/(related_times[k]-related_times[k-1])
+                df_input.loc[k,'总电压[V]']=70
+                df_input.loc[k,'修复']=2
+            elif related_times[k]-related_times[k-1]<30:
+                df_input.loc[k,'总电流[A]']=df_input.loc[k-1,'总电流[A]']
+                df_input.loc[k,'总电压[V]']=df_input.loc[k-1,'总电压[V]']
+                df_input.loc[k,'修复']=3
+            else:
+                df_input=df_input.drop(k)
+
+    df_input.index=[i for i in range(len(df_input))]
+    df_input['日期']=[df_input.loc[i,'时间戳'][0:10] for i in range(len(df_input))]     
+    date_list=np.unique(df_input['日期'].values)
+
+    #list_result=[]
+    input_res=pd.DataFrame()
+    res = pd.DataFrame()
+    df_input_copy=df_input.copy()
+    for date_str in date_list:
+        df_input_copy=df_input[df_input['日期']==date_str]
+        data_len=len(df_input_copy)
+        time_list=df_input_copy['时间戳'].values
+        #初始化参数
+        start_time=datetime.datetime.strptime(time_list[0], "%Y-%m-%d %H:%M:%S")
+        last_time=time_list[0]
+        #reset_time=start_time
+        start_index=0
+        end_index=1
+        cal_count=0
+        #df_deltaData_last=df_input_copy[0:0]
+        #初始化输出
+        df_result=pd.DataFrame()
+        
+        df_result.loc[0,'时间']=start_time
+        df_result.loc[0,'时间']=str(df_result.loc[0,'时间'])
+        df_result.loc[0,'时间'] =df_result.loc[0,'时间'].rpartition(':')[0]
+        
+        df_result.loc[0,'累积里程[km]']=0
+        df_result.loc[0,'能耗[km/kwh]']=cost_min
+        #process_store=[]
+        avg_cost=34
+        cross_odo=0
+        for k in range(1,data_len):
+            
+            target_Time=start_time+datetime.timedelta(minutes=5)
+            timenow=datetime.datetime.strptime(time_list[k], "%Y-%m-%d %H:%M:%S")
+            if target_Time>=timenow:
+                delta_time=(target_Time-timenow).seconds
+                cal_flag=0
+                if delta_time<60:
+                    end_index=k
+                    cal_flag=1
+            else:        
+                end_index=k
+                cal_flag=1
+            
+            if cal_flag==1:
+                cal_count=cal_count+1
+                
+                df_deltaData=df_input_copy[max(start_index-100,0):end_index+1]
+                avg_cost=max(cost_min,df_result.loc[cal_count-1,'能耗[km/kwh]'])
+                #avg_cost=cost_min
+                df_span=real_odo(df_deltaData,avg_cost)
+                st_str=df_span['时间戳'].values[0]
+                st_time=datetime.datetime.strptime(st_str, "%Y-%m-%d %H:%M:%S")
+                last_time_s=datetime.datetime.strptime(last_time, "%Y-%m-%d %H:%M:%S")
+                if last_time_s>=st_time:
+                    last_time=last_time
+                    cross_odo=cross_odo
+                else:
+                    last_time=start_time
+                    cross_odo=0
+                delta_odo=df_span['累积里程[km]'].values[-1]-df_span[df_span['时间戳']==str(last_time)]['累积里程[km]'].values[0]
+                delta_energy=df_span['累积能量[Kwh]'].values[-1]-df_span[df_span['时间戳']==str(last_time)]['累积能量[Kwh]'].values[0]
+                df_result.loc[cal_count,'时间']=timenow
+                df_result.loc[cal_count,'时间']=str(df_result.loc[cal_count,'时间'])
+                df_result.loc[cal_count,'时间'] = df_result.loc[cal_count,'时间'].rpartition(':')[0]
+                
+                df_result.loc[cal_count,'累积里程[km]']=df_result.loc[cal_count-1,'累积里程[km]']+delta_odo-cross_odo
+                if delta_energy>0:
+                    df_result.loc[cal_count,'能耗[km/kwh]']=min(max(delta_odo/delta_energy,cost_min),cost_max)
+                else:
+                    df_result.loc[cal_count,'能耗[km/kwh]']=cost_min
+                
+                # df_span[df_span['纬度']>0][-1:]
+                if len(df_span[df_span['纬度']>0])>0:
+                    last_gps=df_span[df_span['纬度']>0][-1:]
+                    idx=last_gps.index.values[0]
+                    last_time=df_span.loc[idx,'时间戳']        
+                    cross_odo=df_span['累积里程[km]'].values[-1]-df_span.loc[idx,'累积里程[km]']
+                else:
+                    last_time=df_span['时间戳'].values[-1]        
+                    cross_odo=0
+                
+                start_index=end_index
+                start_time=timenow
+                cal_flag=0
+    
+        res=res.append(df_result)
+        input_res=input_res.append(df_input_copy)
+
+    l= len(input_res['时间戳'])
+    for k in range(l):
+        input_res.loc[k,'时间戳'] =input_res.loc[k,'时间戳'].rpartition(':')[0]
+
+    res.index=[i for i in range(len(res))]
+    res['日期']=[res.loc[i,'时间'][0:10] for i in range(len(res))]     
+
+    merge_res=pd.DataFrame()
+    for date_str in date_list:
+        input_res_copy=input_res[input_res['日期']==date_str]
+        input_res_copy=input_res_copy.drop_duplicates('时间戳')
+        res_copy=res[res['日期']==date_str]
+        df_merge=pd.DataFrame()
+        df_merge=pd.merge(input_res_copy,res_copy,left_on='时间戳',right_on='时间',how='right')
+        merge_res=merge_res.append(df_merge)
+
+    merge_res['sn']=sn
+    merge_res=merge_res[['时间戳','sn','总电流[A]','总电压[V]','SOC[%]','充电状态','纬度','经度','能耗[km/kwh]','累积里程[km]']]
+    merge_res=merge_res.rename(columns={'累积里程[km]':'每日累积里程[km]'})
+
+    return merge_res
+
+
+
+
+
+
+

+ 42 - 0
LIB/MIDDLE/odo/DailyMileageEstimation/V1_0_4/main_daily_mileage.py

@@ -0,0 +1,42 @@
+from LIB.BACKEND import DBManager
+import calcul_mileage
+from LIB.MIDDLE.CellStateEstimation.Common import log
+import datetime
+import pandas as pd
+
+dbManager = DBManager.DBManager()
+
+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=1)
+end_time=str(now_time)
+start_time=str(start_time)
+
+dataSOH = pd.read_excel('sn-20210903.xlsx',sheet_name='sn-20210903')
+fileNames = dataSOH['sn']
+fileNames = list(fileNames)
+l = len(fileNames)
+
+#log信息配置
+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','gps'])
+        data_bms = df_data['bms']
+        data_gps = df_data['gps']
+        if sn[:4] in ['MGMC','UDO2']:
+            data_bms['总电流[A]'] = -data_bms['总电流[A]']
+
+        #...............每日累积里程............................................................................
+        if len(data_bms['时间戳'])>0 and len(data_gps['时间戳'])>0:
+            df_res = calcul_mileage.calcul_mileage(sn,data_bms,data_gps)
+            df_res.to_csv('Mileage_'+sn+'.csv')
+        
+    except Exception as e:
+        print(repr(e))
+        mylog.logopt(sn,e)
+        pass

+ 26 - 0
LIB/MIDDLE/odo/DailyMileageEstimation/V1_0_4/main_daily_mileage_sn.py

@@ -0,0 +1,26 @@
+from LIB.BACKEND import DBManager
+import calcul_mileage
+from LIB.MIDDLE.CellStateEstimation.Common import log
+import datetime
+import pandas as pd
+
+dbManager = DBManager.DBManager()
+
+#now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')   #type: str
+now_time='2021-12-27 00:00:00'
+now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')     #type: datetime
+start_time=now_time-datetime.timedelta(days=1)
+end_time=str(now_time)
+start_time=str(start_time)
+
+sn='MGMCLN750N215N180'
+df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms','gps'])
+data_bms = df_data['bms']
+data_gps = df_data['gps']
+if sn[:4] in ['MGMC','UDO2']:
+    data_bms['总电流[A]'] = -data_bms['总电流[A]']
+
+        #...............每日累积里程............................................................................
+if len(data_bms['时间戳'])>0 and len(data_gps['时间戳'])>0:
+    df_res = calcul_mileage.calcul_mileage(sn,data_bms,data_gps)
+    df_res.to_csv('Mileage_'+sn+'.csv')

+ 39 - 0
LIB/MIDDLE/odo/DailyMileageEstimation/V1_0_4/main_mileage.py

@@ -0,0 +1,39 @@
+
+from LIB.BACKEND import DBManager
+import calcul_mileage
+from LIB.MIDDLE.CellStateEstimation.Common import log
+import datetime
+import pandas as pd
+
+dbManager = DBManager.DBManager()
+
+dataSOH = pd.read_excel('sn-20210903.xlsx',sheet_name='sn-20210903')
+fileNames = dataSOH['sn']
+fileNames = list(fileNames)
+l = len(fileNames)
+
+#log信息配置
+mylog=log.Mylog('log.txt','error')
+mylog.logcfg()
+
+month='202110'
+
+for k in range(l):
+    try:
+        sn = fileNames[k]
+
+        df_data = dbManager.get_data(sn=sn, start_time='2021-12-01 00:00:00', end_time='2022-01-01 00:00:00', data_groups=['bms','gps'])
+        data_bms = df_data['bms']
+        data_gps = df_data['gps']
+        if sn[:4] in ['MGMC','UDO2']:
+            data_bms['总电流[A]'] = -data_bms['总电流[A]']
+
+        #...............每日累积里程............................................................................
+        if len(data_bms['时间戳'])>0 and len(data_gps['时间戳'])>0:
+            df_res = calcul_mileage.calcul_mileage(sn,data_bms,data_gps)
+            df_res.to_csv('Mileage_'+sn+'_'+month+'.csv')
+        
+    except Exception as e:
+        print(repr(e))
+        mylog.logopt(sn,e)
+        pass

BIN
LIB/MIDDLE/odo/DailyMileageEstimation/V1_0_4/sn-20210903.xlsx


BIN
LIB/MIDDLE/odo/DailyMileageEstimation/V1_0_4/每日里程表单.xlsx


+ 356 - 0
LIB/MIDDLE/odo/DailyMileageEstimation/V1_0_4_SimpleVehicle/cal_mileage.py

@@ -0,0 +1,356 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Sat Sep 25 23:26:27 2021
+
+@author: admin
+"""
+import pandas as pd
+import numpy as np
+from LIB.BACKEND.DataPreProcess import DataPreProcess
+import os
+import math
+import datetime
+import time
+
+#将时间戳由 "%Y-%m-%d %H:%M:%S" 切换为 sec
+def timeconvert(df_in,column_name):  
+    df=df_in.copy()
+    df.index=range(len(df))
+    time=df[column_name]
+    timeInSeries=[]
+    time2=datetime.datetime.strptime(str(time[0]),"%Y-%m-%d %H:%M:%S")
+    for k in range(len(time)):
+        time1=datetime.datetime.strptime(str(time[k]),"%Y-%m-%d %H:%M:%S")    
+        t=(time1-time2)
+        timeInSeries.append(t.days*86400+t.seconds)
+    df.loc[:,'相对时间[s]']=pd.DataFrame(timeInSeries,columns=['相对时间[s]'])
+    return df
+
+#计算累积能量
+def cal_accumKwh(df_in):
+    df_in1=df_in.copy()
+    df1=df_in1[['总电流[A]','总电压[V]','相对时间[s]']]
+    df1=df1.dropna(axis=0,how='any')
+    I=df1['总电流[A]'].values
+    V=df1['总电压[V]'].values
+    t=df1['相对时间[s]'].values
+    accumAh=[0.0]
+    for k in range(1,len(I)):
+        accumAh_temp=(t[k]-t[k-1])*((I[k]+I[k-1])/(2*3600))*(V[k]+V[k-1])/2/1000
+        accumAh.append(accumAh[-1]+accumAh_temp)
+    df1.loc[:,'累积能量[Kwh]']=accumAh
+    df_out=pd.merge(df_in1,df1[['累积能量[Kwh]']],how='left',left_index=True, right_index=True)
+    return(df_out)
+
+#将时间格式化为整数
+def str_data_to_num(str_data):
+    # 格式时间成毫秒
+    strptime = time.strptime(str_data,"%Y-%m-%d %H:%M:%S")
+    # print("strptime",strptime)
+    mktime = int(time.mktime(strptime)*1000)
+    # print("mktime",mktime)
+    return mktime
+
+def df_date_To_int(df_in):
+    df=df_in.copy()
+    for k in range(len(df)):
+        df.loc[k,'绝对时间[ms]']=str_data_to_num(df['时间戳'][k])
+    return df
+
+#根据经纬度获取两点之间的距离
+def cal_dis_meters(radius,latitude1, longitude1,latitude2, longitude2):  
+    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
+
+def cal_gps_distance(df_in):
+    #根据gps数据计算△距离1和△距离2
+    #△距离1:直接根据两次的经度和纬度计算得到的马氏距离
+    #△距离2:当两次上报经纬度的时间间隔<60sec时,如果车辆为行驶状态则使用两次的经纬度求得的马氏距离,
+    #如果车辆不为行驶状态,则为0.
+    df_gpsOnly=df_in[df_in['纬度']>0]
+    index_list=df_gpsOnly.index
+    pos_list=df_gpsOnly[['纬度','经度']].values
+    time_list=df_in['相对时间[s]'].values
+    # Energy_list=df_in['累积能量[Kwh]'].values
+    df_gpsOnly.loc[index_list[0],'△距离1']=0
+    df_gpsOnly.loc[index_list[0],'△距离2']=0
+    for k in range(1,len(pos_list)):
+        latitude1=pos_list[k-1][0]
+        longitude1=pos_list[k-1][1]
+        latitude2=pos_list[k][0]
+        longitude2=pos_list[k][1]
+        dlt_odo=cal_dis_meters(6378.137,latitude1, longitude1,latitude2, longitude2)
+        df_gpsOnly.loc[index_list[k],'△距离1']=dlt_odo    
+        if time_list[index_list[k]]-time_list[index_list[k-1]]<60:#两次上传GPS数据时间间隔小于60sec
+            if df_gpsOnly.loc[index_list[k],'data_status']=='drive' :   
+                df_gpsOnly.loc[index_list[k],'△距离2']=dlt_odo
+            elif df_gpsOnly.loc[index_list[k],'data_status']=='none' :
+                df_gpsOnly.loc[index_list[k],'△距离2']=dlt_odo
+            else:
+                df_gpsOnly.loc[index_list[k],'△距离2']=0
+    return(df_gpsOnly)
+
+
+def real_odo(df_in,avg_cost):
+    # df_handle=df_in.copy()
+    df=timeconvert(df_in,"时间戳")#计算相对时间
+    df=cal_accumKwh(df)#计算累积能量
+
+    positive_lat_index=df[df['纬度']>0].index
+    if len(positive_lat_index)>2:
+        first_index=positive_lat_index[0]
+        end_index=positive_lat_index[-1]
+        #将数据分割为有第一个GPS数据之前(part1)、
+        #有最后一个GPS数据之后(part3)以及
+        #有第一个GPS数据和最后一个GPS数据中间的数据(part3)
+        part1=df[0:first_index+1]
+        part2=df[first_index:end_index+1]
+        part3=df[end_index:-1]
+        
+        #第一部分距离计算    
+        part1.index=[i for i in range(len(part1))]
+        part1.loc[0,'△距离']=0
+        AccumEnergy1=part1['累积能量[Kwh]'].values
+        for k1 in range(1,len(part1)):
+            if (AccumEnergy1[k1]-AccumEnergy1[k1-1])>0:
+                part1.loc[k1,'△距离']=(AccumEnergy1[k1]-AccumEnergy1[k1-1])*avg_cost
+                part1.loc[k1,'方法']=4
+            else:
+                part1.loc[k1,'△距离']=0
+                part1.loc[k1,'方法']=5
+        
+        #第二部分计算
+        part2_gps=part2[part2['纬度']>0]
+        part2_gps.index=[i for i in range(len(part2_gps))]
+        times_list=part2_gps['相对时间[s]'].values
+        lat=part2_gps['纬度'].values
+        lng=part2_gps['经度'].values
+        AccumEnergy2=part2_gps['累积能量[Kwh]'].values
+        part2_gps.loc[0,'△距离']=0
+        for k2 in range(1,len(part2_gps)):
+            delta_energy=AccumEnergy2[k2]-AccumEnergy2[k2-1]
+            delta_span=cal_dis_meters(6378.137,lat[k2], lng[k2],lat[k2-1], lng[k2-1])
+            v_spd=3600*delta_span/(times_list[k2]-times_list[k2-1])        
+            if times_list[k2]-times_list[k2-1]<60 and not delta_energy<=0:
+                if v_spd>50 :
+                    part2_gps.loc[k2,'△距离']=delta_energy*avg_cost
+                    part2_gps.loc[k2,'方法']=1_1
+                else:
+                    part2_gps.loc[k2,'△距离']=delta_span
+                    part2_gps.loc[k2,'方法']=1_2
+            else:            
+                if delta_energy<=0:
+                    part2_gps.loc[k2,'△距离']=0
+                    part2_gps.loc[k2,'方法']=2
+                else:
+                    if v_spd>50:
+                        part2_gps.loc[k2,'△距离']=delta_energy*avg_cost
+                        part2_gps.loc[k2,'方法']=3_1
+                    else:
+                        part2_gps.loc[k2,'△距离']=max(delta_span,delta_energy*avg_cost)
+                        part2_gps.loc[k2,'方法']=3_2
+    
+                    
+        #第三部分距离计算    
+        part3.index=[i for i in range(len(part3))]
+        part3.loc[0,'△距离']=0
+        AccumEnergy3=part3['累积能量[Kwh]'].values
+        for k3 in range(1,len(part3)):
+            if (AccumEnergy3[k3]-AccumEnergy3[k3-1])>0:
+                part3.loc[k3,'△距离']=(AccumEnergy3[k3]-AccumEnergy3[k3-1])*avg_cost
+                part3.loc[k3,'方法']=4
+            else:
+                part3.loc[k3,'△距离']=0
+                part3.loc[k3,'方法']=5
+        df_out=pd.concat([part1,part2_gps[1:],part3[1:]])
+    else:
+        part1=df
+        part2=df[0:0]
+        part3=df[0:0]
+        
+        #第一部分距离计算    
+        part1.index=[i for i in range(len(part1))]
+        part1.loc[0,'△距离']=0
+        AccumEnergy1=part1['累积能量[Kwh]'].values
+        for k1 in range(1,len(part1)):
+            if (AccumEnergy1[k1]-AccumEnergy1[k1-1])>0:
+                part1.loc[k1,'△距离']=(AccumEnergy1[k1]-AccumEnergy1[k1-1])*avg_cost
+                part1.loc[k1,'方法']=4
+            else:
+                part1.loc[k1,'△距离']=0
+                part1.loc[k1,'方法']=5
+        df_out=part1.copy()
+    
+    #计算累积里程
+    df_out.loc[0,'累积里程[km]']=0
+    df_out.index=[i for i in range(len(df_out))]
+
+    for k in range(1,len(df_out)):
+        df_out.loc[k,'累积里程[km]']=df_out.loc[k-1,'累积里程[km]']+df_out.loc[k,'△距离']
+    return(df_out)
+
+def calcul_mileage(sn,data_bms,data_gps):
+
+    #合并两张表格
+    df_bms=data_bms.copy()
+    df_gps=data_gps.copy()
+    #删除纬度小于10的点
+    for k in range(1,len(df_gps)):
+        if df_gps.loc[k,'纬度']<10:
+            df_gps=df_gps.drop(k)
+    df_bms.set_index(["时间戳"], inplace=True)
+    df_gps.set_index(["时间戳"], inplace=True)
+    merge_df1=pd.merge(df_bms, df_gps,how='outer', left_index=True, right_index=True)
+    merge_df1.loc[:,'时间戳']=merge_df1.index
+    merge_df1.index=[i for i in range(len(merge_df1))]
+
+    #参数定义
+    cost_min=34 #最小能耗km/kwh
+    cost_max=45 #最高能耗km/kwh
+
+    df_input=merge_df1[['时间戳','总电流[A]', '总电压[V]','SOC[%]','充电状态','纬度', '经度']]
+    #电流电压数据修复
+    df_input=timeconvert(df_input,"时间戳")#计算相对时间
+    related_times=df_input['相对时间[s]'].values
+    lat_list=df_input['纬度'].values
+    lng_list=df_input['经度'].values
+    Vlt_list=df_input['总电压[V]'].values
+    for k in range(1,len(df_input)):
+        if math.isnan(df_input.loc[k,'总电流[A]']):
+            if related_times[k]-related_times[k-1]<5:
+                df_input.loc[k,'总电流[A]']=df_input.loc[k-1,'总电流[A]']
+                df_input.loc[k,'总电压[V]']=df_input.loc[k-1,'总电压[V]']
+                df_input.loc[k,'修复']=1
+            elif lat_list[k-1]>0:
+                delta_odo=cal_dis_meters(6378.137,lat_list[k], lng_list[k],lat_list[k-1], lng_list[k-1])
+                df_input.loc[k,'总电流[A]']=1000*3600*(delta_odo/cost_min)/70/(related_times[k]-related_times[k-1])
+                df_input.loc[k,'总电压[V]']=70
+                df_input.loc[k,'修复']=2
+            elif related_times[k]-related_times[k-1]<30:
+                df_input.loc[k,'总电流[A]']=df_input.loc[k-1,'总电流[A]']
+                df_input.loc[k,'总电压[V]']=df_input.loc[k-1,'总电压[V]']
+                df_input.loc[k,'修复']=3
+            else:
+                df_input=df_input.drop(k)
+
+    df_input.index=[i for i in range(len(df_input))]
+    df_input['日期']=[df_input.loc[i,'时间戳'][0:10] for i in range(len(df_input))]     
+    date_list=np.unique(df_input['日期'].values)
+
+    list_result=[]
+    input_res=pd.DataFrame()
+    res = pd.DataFrame()
+    df_input_copy=df_input.copy()
+    for date_str in date_list:
+        df_input_copy=df_input[df_input['日期']==date_str]
+        data_len=len(df_input_copy)
+        time_list=df_input_copy['时间戳'].values
+        #初始化参数
+        start_time=datetime.datetime.strptime(time_list[0], "%Y-%m-%d %H:%M:%S")
+        last_time=time_list[0]
+        reset_time=start_time
+        start_index=0
+        end_index=1
+        cal_count=0
+        df_deltaData_last=df_input_copy[0:0]
+        #初始化输出
+        df_result=pd.DataFrame()
+        
+        df_result.loc[0,'时间']=start_time
+        df_result.loc[0,'时间']=str(df_result.loc[0,'时间'])
+        df_result.loc[0,'时间'] =df_result.loc[0,'时间'].rpartition(':')[0]
+        
+        df_result.loc[0,'累积里程[km]']=0
+        df_result.loc[0,'能耗[km/kwh]']=cost_min
+        process_store=[]
+        avg_cost=34
+        cross_odo=0
+        for k in range(1,data_len):
+            
+            target_Time=start_time+datetime.timedelta(minutes=5)
+            timenow=datetime.datetime.strptime(time_list[k], "%Y-%m-%d %H:%M:%S")
+            if target_Time>=timenow:
+                delta_time=(target_Time-timenow).seconds
+                cal_flag=0
+                if delta_time<60:
+                    end_index=k
+                    cal_flag=1
+            else:        
+                end_index=k
+                cal_flag=1
+            
+            if cal_flag==1:
+                cal_count=cal_count+1
+                
+                df_deltaData=df_input_copy[max(start_index-100,0):end_index+1]
+                avg_cost=max(cost_min,df_result.loc[cal_count-1,'能耗[km/kwh]'])
+                #avg_cost=cost_min
+                df_span=real_odo(df_deltaData,avg_cost)
+                st_str=df_span['时间戳'].values[0]
+                st_time=datetime.datetime.strptime(st_str, "%Y-%m-%d %H:%M:%S")
+                last_time_s=datetime.datetime.strptime(last_time, "%Y-%m-%d %H:%M:%S")
+                if last_time_s>=st_time:
+                    last_time=last_time
+                    cross_odo=cross_odo
+                else:
+                    last_time=start_time
+                    cross_odo=0
+                delta_odo=df_span['累积里程[km]'].values[-1]-df_span[df_span['时间戳']==str(last_time)]['累积里程[km]'].values[0]
+                delta_energy=df_span['累积能量[Kwh]'].values[-1]-df_span[df_span['时间戳']==str(last_time)]['累积能量[Kwh]'].values[0]
+                df_result.loc[cal_count,'时间']=timenow
+                df_result.loc[cal_count,'时间']=str(df_result.loc[cal_count,'时间'])
+                df_result.loc[cal_count,'时间'] = df_result.loc[cal_count,'时间'].rpartition(':')[0]
+                
+                df_result.loc[cal_count,'累积里程[km]']=df_result.loc[cal_count-1,'累积里程[km]']+delta_odo-cross_odo
+                if delta_energy>0:
+                    df_result.loc[cal_count,'能耗[km/kwh]']=min(max(delta_odo/delta_energy,cost_min),cost_max)
+                else:
+                    df_result.loc[cal_count,'能耗[km/kwh]']=cost_min
+                
+                # df_span[df_span['纬度']>0][-1:]
+                if len(df_span[df_span['纬度']>0])>0:
+                    last_gps=df_span[df_span['纬度']>0][-1:]
+                    idx=last_gps.index.values[0]
+                    last_time=df_span.loc[idx,'时间戳']        
+                    cross_odo=df_span['累积里程[km]'].values[-1]-df_span.loc[idx,'累积里程[km]']
+                else:
+                    last_time=df_span['时间戳'].values[-1]        
+                    cross_odo=0
+                
+                start_index=end_index
+                start_time=timenow
+                cal_flag=0
+    
+        res=res.append(df_result)
+        input_res=input_res.append(df_input_copy)
+
+    l= len(input_res['时间戳'])
+    for k in range(l):
+        input_res.loc[k,'时间戳'] =input_res.loc[k,'时间戳'].rpartition(':')[0]
+
+    res.index=[i for i in range(len(res))]
+    res['日期']=[res.loc[i,'时间'][0:10] for i in range(len(res))]     
+
+    merge_res=pd.DataFrame()
+    for date_str in date_list:
+        input_res_copy=input_res[input_res['日期']==date_str]
+        input_res_copy=input_res_copy.drop_duplicates('时间戳')
+        res_copy=res[res['日期']==date_str]
+        df_merge=pd.DataFrame()
+        df_merge=pd.merge(input_res_copy,res_copy,left_on='时间戳',right_on='时间',how='right')
+        merge_res=merge_res.append(df_merge)
+
+    merge_res['sn']=sn
+    merge_res=merge_res[['时间戳','sn','总电流[A]','总电压[V]','SOC[%]','充电状态','纬度','经度','能耗[km/kwh]','累积里程[km]']]
+    merge_res=merge_res.rename(columns={'累积里程[km]':'每日累积里程[km]'})
+
+    return merge_res
+
+
+
+
+

+ 42 - 0
LIB/MIDDLE/odo/DailyMileageEstimation/V1_0_4_SimpleVehicle/main_daily_mileage.py

@@ -0,0 +1,42 @@
+from LIB.BACKEND import DBManager
+import cal_mileage
+from LIB.MIDDLE.CellStateEstimation.Common import log
+import datetime
+import pandas as pd
+
+dbManager = DBManager.DBManager()
+
+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=1)
+end_time=str(now_time)
+start_time=str(start_time)
+
+# dataSOH = pd.read_excel('sn-20210903.xlsx',sheet_name='sn-20210903')
+# fileNames = dataSOH['sn']
+# fileNames = list(fileNames)
+# l = len(fileNames)
+
+#log信息配置
+mylog=log.Mylog('log.txt','error')
+mylog.logcfg()
+
+
+
+try:
+    sn = 'MGMCLN750N215N180'
+    start_time='2021-12-26 00:00:00'
+    end_time='2021-12-26 23:59:59'
+    df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms','gps'])
+    data_bms = df_data['bms']
+    data_gps = df_data['gps']
+
+    #...............每日累积里程............................................................................
+    if len(data_bms['时间戳'])>0 and len(data_gps['时间戳'])>0:
+        df_res = cal_mileage.calcul_mileage(sn,data_bms,data_gps)
+        df_res.to_csv('Mileage_'+sn+'.csv')
+    
+except Exception as e:
+    print(repr(e))
+    mylog.logopt(sn,e)
+    pass

BIN
LIB/SOH数据.xlsx


BIN
SOH数据.xlsx


+ 802 - 0
USER/SPF/01qixiang/01BatSoh/CBMSBatSoh copy.py

@@ -0,0 +1,802 @@
+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
+
+        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 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 = 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.3
+                self.StandardStandingTime=7200
+            else:
+                self.tempweight=0.1
+                self.StandardStandingTime=10800
+        else:
+            if min(celltemp)>=25:
+                self.tempweight=1
+                self.StandardStandingTime=1800
+            elif min(celltemp)>=15:
+                self.tempweight=0.8
+                self.StandardStandingTime=3600
+            elif min(celltemp)>=5:
+                self.tempweight=0.3
+                self.StandardStandingTime=4800
+            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
+        elif deltsoc>20:
+            deltsoc_weight=0
+        else:
+            deltsoc_weight=0
+        return deltsoc_weight
+
+    #获取当前行所有电压数据......................................................................................................................
+    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方法计算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.0015 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)>2:
+            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.015:
+                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.015:
+                    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:      #静置时间满足要求
+                    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.003:    #前后两次电压波动<3mV
+                        if standingpoint_st:
+                            if len(standingpoint_st)>len(standingpoint_sp):     #开始时刻已获取,结束时刻未获取
+                                minocv_socnow=np.interp(cellvolt_min,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:
+                    continue
+            else:
+                standingtime=0
+                continue
+
+        #计算SOH......................................................................................................................
+        if standingpoint_sp:
+            column_name=['time_st','time_sp','sn','method','bmssoh','packsoh','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]+1,standingpoint_sp[i]): #计算累计Ah
+                    Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
+                    if Step<120:
+                        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
+
+                #计算每个电芯的SOH值
+                cellsoh=[]
+                for j in range(self.param.CellVoltNums):    
+                    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 and soh_weight>0.05:    #判断soh值的有效区间
+                        if len(df_res)<1:
+                            if not self.df_soh.empty:
+                                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
+                        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
+                #计算电池包SOH
+                ocv_soc1=np.interp(min(cellocv_st),self.param.LookTab_OCV,self.param.LookTab_SOC)
+                ocv_soc2=np.interp(max(cellocv_sp),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
+                packsoh_init=ah_accum*100/((ocv_soc2-ocv_soc1)*0.01*self.param.Capacity)
+
+                if packsoh_init>55 and packsoh_init<120 and soh_weight>0.05:    #判断soh值的有效区间
+                    if len(df_res)<1:
+                        if not self.df_soh.empty:
+                            packsoh_last=self.df_soh.loc[len(self.df_soh)-1,'packsoh']
+                            if soh_weight>1/abs(packsoh_init-packsoh_last):
+                                soh_weight=1/abs(packsoh_init-packsoh_last)
+                                packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                            else:
+                                packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                        else:
+                            packsoh=packsoh_init
+                    else:
+                        packsoh_last=df_res.loc[len(df_res)-1,'packsoh']
+                        if soh_weight>1/abs(packsoh_init-packsoh_last):
+                                soh_weight=1/abs(packsoh_init-packsoh_last)
+                                packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                        else:
+                            packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                    packsoh=eval(format(packsoh,'.1f'))
+                else:
+                    packsoh=0
+                    continue
+
+                if cellsoh and 55<min(cellsoh)<120 and 50<packsoh<120:
+                    soh=min(cellsoh)
+                    bmssoh=self.bms_soh[standingpoint_sp[i]]
+                    soh_list=[timepoint_bms_st, timepoint_bms_sp, self.sn, 1, bmssoh, packsoh, 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)
+                        cellvolt_max=max(cellvolt_now)
+                        cellvolt_min=min(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:      #当前最大电芯电压<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).tolist()
+                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 or (self.packcrnt[i]<-self.param.Capacity/2 and self.packcrnt[i+1]<-self.param.Capacity/2):  #如果充电过程中时间间隔>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).tolist()
+                    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).tolist()
+                        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','bmssoh','packsoh','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:  #计算单体电芯soh
+                        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_packsoc1=np.interp(min(cellocv_st),self.param.LookTab_OCV,self.param.LookTab_SOC)
+                        ocv_soc2=self.param.FullChrgSoc
+                        ocv_packsoc2=ocv_soc2
+                    else:
+                        cellocv_sp=self._cellvolt_get(standingpoint_sp[i])
+                        ocv_soc1=self.param.FullChrgSoc
+                        ocv_packsoc1=ocv_soc1
+                        ocv_soc2=np.interp(cellocv_sp[cellmaxvolt_number1[i]],self.param.LookTab_OCV,self.param.LookTab_SOC)
+                        ocv_packsoc2=np.interp(min(cellocv_sp),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]+1,standingpoint_sp[i]+1): #计算累计Ah
+                        Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
+                        if Step<120:
+                            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_packsoc=ocv_packsoc2-ocv_packsoc1
+                    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/(delt_ocv_soc*0.01*self.param.Capacity)
+                    packsoh_init=ah_accum*100/(delt_ocv_packsoc*0.01*self.param.Capacity)
+                    bmssoh=self.bms_soh[standingpoint_sp[i]]
+
+                    if 55<cellsoh_init<120 and 50<packsoh_init<120:    #判断soh值的有效区间
+                        soh_list=[timepoint_bms_st, timepoint_bms_sp, self.sn, 1, bmssoh, packsoh_init, cellsoh_init, str(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)):
+                    cellsoh=[]
+                    for j in range(1, self.param.CellVoltNums + 1):
+                        cellvolt1 = self._singlevolt_get(i,j,2)  #取单体电压j的所有电压值
+                        cellvolt=self._np_move_avg(cellvolt1, 3, mode="same")     #对电压进行滑动平均滤
+                        cellsoh_init=self._dvdq_soh(chrg_start[i],chrg_end[i],cellvolt)     #dvdq计算soh
+                        cellsoh.append(cellsoh_init)
+                    
+                    soh_weight=tempweightlist2[i]*0.3
+                    cellsoh_init=cellsoh[cellmaxvolt_number2[i]+1]
+                    cellsoh_new=[k for k in cellsoh if 50<k<120]
+                    packsoh_init=min(cellsoh_new)
+                    bmssoh=self.bms_soh[chrg_end[i]]
+                    if 55<cellsoh_init<120:    #判断soh值的有效区间
+                        soh_list=[self.bmstime[chrg_start[i]], self.bmstime[chrg_end[i]], self.sn, 2, bmssoh, packsoh_init, 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']
+                    packsoh_init=df_res.loc[i,'packsoh']
+                    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']
+                            packsoh_last=self.df_soh.loc[len(self.df_soh)-1,'packsoh']
+                            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)
+                                packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                            else:
+                                cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
+                                packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                        else:
+                            cellsoh_cal=cellsoh_init
+                            packsoh=packsoh_init
+                        cellsoh_cal=eval(format(cellsoh_cal,'.1f'))
+                        packsoh=eval(format(packsoh,'.1f'))
+                        if 55<cellsoh_cal<120:
+                            df_res.loc[i,'soh']=cellsoh_cal
+                            df_res.loc[i,'cellsoh']=str(cellsoh_cal)
+                            df_res.loc[i,'packsoh']=packsoh
+                        else:
+                            df_res=df_res.drop(i,axis=0)
+                    else:
+                        cellsoh_last=df_res.loc[i-1,'soh']
+                        packsoh_last=df_res.loc[i-1,'packsoh']
+                        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)
+                            packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                        else:
+                            cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
+                            packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                        cellsoh_cal=eval(format(cellsoh_cal,'.1f'))
+                        packsoh=eval(format(packsoh,'.1f'))
+                        if 55<cellsoh_cal<120:
+                            df_res.loc[i,'soh']=cellsoh_cal
+                            df_res.loc[i,'cellsoh']=str([cellsoh_cal])
+                            df_res.loc[i,'packsoh']=packsoh
+                        else:
+                            df_res=df_res.drop(i,axis=0)
+                df_res=df_res.drop('weight',axis=1)
+                return df_res
+
+        return pd.DataFrame()
+
+

+ 880 - 0
USER/SPF/01qixiang/01BatSoh/CBMSBatSoh.py

@@ -0,0 +1,880 @@
+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
+
+        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 batsoh(self):
+        if self.celltype<=50:
+            df_res=self._ncmsoh_chrg()
+            return df_res
+            
+        else:
+            df_res=self._lfpsoh()
+            return df_res
+
+    #定义滑动滤波函数.........................................................................................................................
+    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.df_bms)>10:
+            charging=0
+            for i in range(3, len(self.bmstime) - 3):
+                if charging==0:
+                    if self.packcrnt[i]<=-0.1 and self.packcrnt[i+1]<=-0.1:
+                        self.ChgStart.append(i-1)
+                        charging=1
+                    else:
+                        pass
+                else:
+                    if self.packcrnt[i]>-0.1 and self.packcrnt[i+1]>-0.1:
+                        self.ChgEnd.append(i)
+                        charging=0
+                    else:
+                        pass
+    
+    #寻找当前行数据的最小温度值.................................................................................................................
+    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=4800
+            elif min(celltemp)>=5:
+                self.tempweight=0.3
+                self.StandardStandingTime=7200
+            else:
+                self.tempweight=0.1
+                self.StandardStandingTime=10800
+        else:
+            if min(celltemp)>=25:
+                self.tempweight=1
+                self.StandardStandingTime=1800
+            elif min(celltemp)>=15:
+                self.tempweight=0.8
+                self.StandardStandingTime=3600
+            elif min(celltemp)>=5:
+                self.tempweight=0.4
+                self.StandardStandingTime=7200
+            else:
+                self.tempweight=0.2
+                self.StandardStandingTime=10800
+
+    #获取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.5
+        elif deltsoc>30:
+            deltsoc_weight=0.2
+        elif deltsoc>20:
+            deltsoc_weight=0
+        else:
+            deltsoc_weight=0
+        return deltsoc_weight
+
+    #获取当前行所有电压数据......................................................................................................................
+    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方法计算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.0015 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)>2:
+            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.015:
+                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.015:
+                    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])               #获取温度对应的静置时间及权重            
+
+            #筛选满足2点法计算的数据
+            StandingTime=0
+            StandingTime1=0
+            for m in range(self.ChgStart[i]-2):
+                if abs(self.packcrnt[self.ChgStart[i] - m - 1]) < 0.1 and abs(self.packcrnt[self.ChgStart[i] - m]) < 0.1:
+                    StandingTime = StandingTime + (self.bmstime[self.ChgStart[i] - m] - self.bmstime[self.ChgStart[i] - m - 1]).total_seconds()
+                    if StandingTime > self.StandardStandingTime:
+                        ChgStartValid.append(self.ChgStart[i]-1)
+                        tempweightlist.append(self.tempweight)
+                        break
+                else:
+                    break
+            
+            if len(ChgStartValid)>len(ChgEndValid):
+                for m in range(len(self.packcrnt)-self.ChgEnd[i]-2):
+                    if abs(self.packcrnt[self.ChgEnd[i] + m + 1]) < 0.1 and abs(self.packcrnt[self.ChgEnd[i] + m]) < 0.1:
+                        StandingTime1 = StandingTime1 + (self.bmstime[self.ChgEnd[i] + m + 1] - self.bmstime[self.ChgEnd[i] + m]).total_seconds()
+                        if StandingTime1>self.StandardStandingTime:
+                            ChgEndValid.append(self.ChgEnd[i]+m)
+                            break
+                    else:
+                        break
+            if len(ChgStartValid)>len(ChgEndValid):
+                del(ChgStartValid[-1])
+
+        #计算SOH......................................................................................................................
+        if ChgEndValid:
+            column_name=['time_st','time_sp','sn','method','bmssoh','soh','cellsoh']
+            df_res=pd.DataFrame(columns=column_name)
+
+            for i in range(len(ChgEndValid)):
+                cellocv_st=self._cellvolt_get(ChgStartValid[i])    #获取静置点所有电芯的电压
+                cellocv_sp=self._cellvolt_get(ChgEndValid[i])
+
+                timepoint_bms_st=self.bmstime[ChgStartValid[i]]   #获取静置点的时间
+                timepoint_bms_sp=self.bmstime[ChgEndValid[i]]
+                
+                ah_packcrnt_dis=0
+                ah_packcrnt_chg=0
+                for j in range(ChgStartValid[i]+1,ChgEndValid[i]): #计算累计Ah
+                    if self.packcrnt[j+1]<-0.1 and self.packcrnt[j]<-0.1:
+                        Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
+                        if Step<300:
+                            ah_packcrnt_chg=ah_packcrnt_chg-self.packcrnt[j+1]*Step  
+                        else:
+                            ah_packcrnt_chg=0
+                            break
+                
+                ah_packcrnt_chg=ah_packcrnt_chg/3600
+                ah_packcrnt_dis=0
+                ah_packcrnt=ah_packcrnt_chg-ah_packcrnt_dis     #两个静置点的总累计AH,负值代表放电,正值代表充电
+                
+
+                #计算每个电芯的SOH值
+                cellsoh=[]
+                for j in range(self.param.CellVoltNums):    
+                    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=tempweightlist[i]*delt_ocv_soc_weight
+                    cellsoh_init=ah_packcrnt*100/((ocv_soc2-ocv_soc1)*0.01*self.param.Capacity)
+
+                    if cellsoh_init>55 and cellsoh_init<120 and soh_weight>0.05:    #判断soh值的有效区间
+                        if len(df_res)<1:
+                            if not self.df_soh.empty:
+                                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
+                        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
+                #计算电池包SOH
+                # ocv_soc1=np.interp(min(cellocv_st),self.param.LookTab_OCV,self.param.LookTab_SOC)
+                # ocv_soc2=np.interp(max(cellocv_sp),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
+                # packsoh_init=ah_accum*100/((ocv_soc2-ocv_soc1)*0.01*self.param.Capacity)
+
+                # if packsoh_init>55 and packsoh_init<120 and soh_weight>0.05:    #判断soh值的有效区间
+                #     if len(df_res)<1:
+                #         if not self.df_soh.empty:
+                #             packsoh_last=self.df_soh.loc[len(self.df_soh)-1,'packsoh']
+                #             if soh_weight>1/abs(packsoh_init-packsoh_last):
+                #                 soh_weight=1/abs(packsoh_init-packsoh_last)
+                #                 packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                #             else:
+                #                 packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                #         else:
+                #             packsoh=packsoh_init
+                #     else:
+                #         packsoh_last=df_res.loc[len(df_res)-1,'packsoh']
+                #         if soh_weight>1/abs(packsoh_init-packsoh_last):
+                #                 soh_weight=1/abs(packsoh_init-packsoh_last)
+                #                 packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                #         else:
+                #             packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                #     packsoh=eval(format(packsoh,'.1f'))
+                # else:
+                #     packsoh=0
+                #     continue
+
+                if cellsoh and 55<min(cellsoh)<120:
+                    soh=min(cellsoh)
+                    bmssoh=self.bms_soh[ChgEndValid[i]]
+                    soh_list=[timepoint_bms_st, timepoint_bms_sp, self.sn, 1, bmssoh, 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()
+
+    #两点法计算三元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:      #静置时间满足要求
+                    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.003:    #前后两次电压波动<3mV
+                        if standingpoint_st:
+                            if len(standingpoint_st)>len(standingpoint_sp):     #开始时刻已获取,结束时刻未获取
+                                minocv_socnow=np.interp(cellvolt_min,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)>30:   #当前时刻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:
+                    continue
+            else:
+                standingtime=0
+                continue
+
+        #计算SOH......................................................................................................................
+        if standingpoint_sp:
+            column_name=['time_st','time_sp','sn','method','bmssoh','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]+1,standingpoint_sp[i]): #计算累计Ah
+                    Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
+                    if Step<120:
+                        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
+
+                #计算每个电芯的SOH值
+                cellsoh=[]
+                for j in range(self.param.CellVoltNums):    
+                    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.8
+                    cellsoh_init=ah_accum*100/((ocv_soc2-ocv_soc1)*0.01*self.param.Capacity)
+
+                    if cellsoh_init>55 and cellsoh_init<120 and soh_weight>0.05:    #判断soh值的有效区间
+                        if len(df_res)<1:
+                            if not self.df_soh.empty:
+                                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
+                        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
+                #计算电池包SOH
+                # ocv_soc1=np.interp(min(cellocv_st),self.param.LookTab_OCV,self.param.LookTab_SOC)
+                # ocv_soc2=np.interp(max(cellocv_sp),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
+                # packsoh_init=ah_accum*100/((ocv_soc2-ocv_soc1)*0.01*self.param.Capacity)
+
+                # if packsoh_init>55 and packsoh_init<120 and soh_weight>0.05:    #判断soh值的有效区间
+                #     if len(df_res)<1:
+                #         if not self.df_soh.empty:
+                #             packsoh_last=self.df_soh.loc[len(self.df_soh)-1,'packsoh']
+                #             if soh_weight>1/abs(packsoh_init-packsoh_last):
+                #                 soh_weight=1/abs(packsoh_init-packsoh_last)
+                #                 packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                #             else:
+                #                 packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                #         else:
+                #             packsoh=packsoh_init
+                #     else:
+                #         packsoh_last=df_res.loc[len(df_res)-1,'packsoh']
+                #         if soh_weight>1/abs(packsoh_init-packsoh_last):
+                #                 soh_weight=1/abs(packsoh_init-packsoh_last)
+                #                 packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                #         else:
+                #             packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                #     packsoh=eval(format(packsoh,'.1f'))
+                # else:
+                #     packsoh=0
+                #     continue
+
+                if cellsoh and 55<min(cellsoh)<120:
+                    soh=min(cellsoh)
+                    bmssoh=self.bms_soh[standingpoint_sp[i]]
+                    soh_list=[timepoint_bms_st, timepoint_bms_sp, self.sn, 1, bmssoh, 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.6 and abs(self.packcrnt[i-1]) < 0.6:     #判断非平台区静置状态
+                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+1])>=0.6:  #下一时刻电流>0.1A
+                        standingtime=0
+                        cellvolt_now=self._cellvolt_get(i)
+                        cellvolt_max=max(cellvolt_now)
+                        cellvolt_min=min(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:      #当前最大电芯电压<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]>-100:    #判读满充状态
+                standingtime=0
+                self._celltemp_weight(i)
+                cellvolt_now=self._cellvolt_get(i).tolist()
+                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 or (self.packcrnt[i]<-50 and self.packcrnt[i+1]<-50):  #如果充电过程中时间间隔>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).tolist()
+                    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).tolist()
+                        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','bmssoh','packsoh','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:  #计算单体电芯soh
+                        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_packsoc1=np.interp(min(cellocv_st),self.param.LookTab_OCV,self.param.LookTab_SOC)
+                        ocv_soc2=self.param.FullChrgSoc
+                        ocv_packsoc2=ocv_soc2
+                    else:
+                        cellocv_sp=self._cellvolt_get(standingpoint_sp[i])
+                        ocv_soc1=self.param.FullChrgSoc
+                        ocv_packsoc1=ocv_soc1
+                        ocv_soc2=np.interp(cellocv_sp[cellmaxvolt_number1[i]],self.param.LookTab_OCV,self.param.LookTab_SOC)
+                        ocv_packsoc2=np.interp(min(cellocv_sp),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]+1,standingpoint_sp[i]+1): #计算累计Ah
+                        Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
+                        if Step<300:
+                            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_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_packsoc=ocv_packsoc2-ocv_packsoc1
+                    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/(delt_ocv_soc*0.01*self.param.Capacity)
+                    packsoh_init=ah_accum*100/(delt_ocv_packsoc*0.01*self.param.Capacity)
+                    bmssoh=self.bms_soh[standingpoint_sp[i]]
+
+                    if 55<cellsoh_init<120 and 50<packsoh_init<120:    #判断soh值的有效区间
+                        soh_list=[timepoint_bms_st, timepoint_bms_sp, self.sn, 1, bmssoh, packsoh_init, cellsoh_init, str(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)):
+                    cellsoh=[]
+                    for j in range(1, self.param.CellVoltNums + 1):
+                        cellvolt1 = self._singlevolt_get(i,j,2)  #取单体电压j的所有电压值
+                        cellvolt=self._np_move_avg(cellvolt1, 3, mode="same")     #对电压进行滑动平均滤
+                        cellsoh_init=self._dvdq_soh(chrg_start[i],chrg_end[i],cellvolt)     #dvdq计算soh
+                        cellsoh.append(cellsoh_init)
+                    
+                    soh_weight=tempweightlist2[i]*0.3
+                    cellsoh_init=cellsoh[cellmaxvolt_number2[i]+1]
+                    cellsoh_new=[k for k in cellsoh if 50<k<120]
+                    packsoh_init=min(cellsoh_new)
+                    bmssoh=self.bms_soh[chrg_end[i]]
+                    if 55<cellsoh_init<120:    #判断soh值的有效区间
+                        soh_list=[self.bmstime[chrg_start[i]], self.bmstime[chrg_end[i]], self.sn, 2, bmssoh, packsoh_init, 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']
+                    packsoh_init=df_res.loc[i,'packsoh']
+                    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']
+                            packsoh_last=self.df_soh.loc[len(self.df_soh)-1,'packsoh']
+                            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)
+                                packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                            else:
+                                cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
+                                packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                        else:
+                            cellsoh_cal=cellsoh_init
+                            packsoh=packsoh_init
+                        cellsoh_cal=eval(format(cellsoh_cal,'.1f'))
+                        packsoh=eval(format(packsoh,'.1f'))
+                        if 55<cellsoh_cal<120:
+                            df_res.loc[i,'soh']=cellsoh_cal
+                            df_res.loc[i,'cellsoh']=str(cellsoh_cal)
+                            df_res.loc[i,'packsoh']=packsoh
+                        else:
+                            df_res=df_res.drop(i,axis=0)
+                    else:
+                        cellsoh_last=df_res.loc[i-1,'soh']
+                        packsoh_last=df_res.loc[i-1,'packsoh']
+                        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)
+                            packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                        else:
+                            cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
+                            packsoh=packsoh_init*soh_weight + packsoh_last*(1-soh_weight)
+                        cellsoh_cal=eval(format(cellsoh_cal,'.1f'))
+                        packsoh=eval(format(packsoh,'.1f'))
+                        if 55<cellsoh_cal<120:
+                            df_res.loc[i,'soh']=cellsoh_cal
+                            df_res.loc[i,'cellsoh']=str([cellsoh_cal])
+                            df_res.loc[i,'packsoh']=packsoh
+                        else:
+                            df_res=df_res.drop(i,axis=0)
+                df_res=df_res.drop('weight',axis=1)
+                return df_res
+
+        return pd.DataFrame()
+
+

+ 102 - 0
USER/SPF/01qixiang/01BatSoh/main.py

@@ -0,0 +1,102 @@
+import CBMSBatSoh
+#coding=utf-8
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+import time, datetime
+import os
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import DBDownload
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import log
+
+dbManager = DBManager.DBManager()
+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')
+    # SNdata_ZKHD=pd.read_excel(excelpath, sheet_name='重卡换电')
+    # 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_ZKHD=SNdata_ZKHD['SN号'].tolist()
+    # SNnums= SNnums_ZKHD
+    SNdata_6040 = pd.read_excel(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\CBMS_SOH_26040.xlsx')
+    SNnums=SNdata_6040['sn'].tolist()
+    SNnums=['PK50001A100000545']
+
+    # filepath=r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\98Download'
+    # files=os.listdir(filepath)
+    # files=[files[5]]
+
+    end_time=datetime.datetime.now()-datetime.timedelta(seconds=10)
+    start_time=end_time-datetime.timedelta(seconds=1200)
+    start_time=start_time.strftime('%Y-%m-%d %H:%M:%S')
+    end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+    start_time='2020-10-31 00:49:37'
+    end_time='2021-12-31 19:49:37'
+
+    #log信息配置............................................................................................................................................................................
+    mylog=log.Mylog('log_soh.txt','error')
+    mylog.logcfg()
+
+    for sn in SNnums:
+        # sn=filesname[:-4]
+        print(sn)
+        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:
+                celltype=100
+                print('未找到对应电池编号!!!\n',sn)
+                # sys.exit()
+
+            # df_bms= pd.read_csv(filepath+'\\'+filesname,encoding='GB18030')
+            # df_accum= pd.read_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\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'])
+            df_bms = df_data['bms']
+            df_bms=df_bms.dropna(subset=['总电流[A]'])
+            df_bms.reset_index(inplace=True,drop=True)     #重置索引
+            df_bms.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\98Download\\'+sn+'.csv',encoding='GB18030')
+            df_accum=pd.DataFrame()
+
+            #..........................................................................读取结果数据库数据.........................................................................
+            host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+            port=3306
+            db='qx_cas'
+            user='qx_read'
+            password='Qx@123456'
+            mode=1
+            tablename='cellstateestimation_soh'
+            DBRead=DBDownload.DBDownload(host, port, db, user, password,mode)
+            with DBRead as DBRead:
+                df_soh=DBRead.getdata(param='time_st,time_sp,sn,method,soh,cellsoh', tablename=tablename, sn=sn, timename='time_sp', st=start_time, sp=end_time)
+            # print(df_soh)
+            # df_soh=pd.DataFrame()
+
+            #..............................................................................计算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\spf\01qixiang\99Result\\'+'CBMS_SOH'+sn+'.csv',encoding='GB18030')
+            # print('done!!!')
+             
+        except Exception as e:
+            print(repr(e))
+            mylog.logopt(sn,e)
+            pass

+ 389 - 0
USER/SPF/01qixiang/02BatUniform/CBMSBatUniform.py

@@ -0,0 +1,389 @@
+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')
+
+        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=self._ncm_uniform()
+            return df_res
+        else:
+            df_res=self._lfp_uniform()
+            return df_res
+    
+    #定义滑动滤波函数........................................................................................................................................
+    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==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=4800
+            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 = 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']
+        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)
+                        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.003:
+                            
+                            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]
+                    elif standingtime>3600*12:
+                        standingtime=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.003:
+                            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]
+                    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)
+                        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.003:
+                            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]
+                        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:     #电流为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+1]) >= 0.1:     
+                        standingtime=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:                 
+                            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]
+                    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)
+                        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:
+                            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]
+                    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 or (self.packcrnt[i]<-50 and self.packcrnt[i+1]<-50):  #如果充电过程中时间间隔>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):
+                        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'))
+                        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

+ 89 - 0
USER/SPF/01qixiang/02BatUniform/main.py

@@ -0,0 +1,89 @@
+import CBMSBatUniform
+
+#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.V1_0_1 import DBDownload
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import log
+
+dbManager = DBManager.DBManager()
+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_6060 + SNnums_L7255 + SNnums_C7255 + SNnums_6040 + SNnums_4840 + SNnums_U7255
+    SNnums=['PK504B10100004342']
+
+    # filepath=r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\98Download'
+    # files=os.listdir(filepath)
+    # files=[files[5]]
+
+    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)
+    start_time='2021-6-1 9:49:37'
+    end_time='2021-6-15 19:49:37'
+
+    #log信息配置
+    mylog=log.Mylog('log_uniform.txt','error')
+    mylog.logcfg()
+
+    for sn in SNnums:
+        print(sn)
+        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:
+                celltype=100
+                print('SN:{},未找到对应电池类型!!!'.format(sn))
+                # continue
+                # sys.exit()
+            
+            # df_bms= pd.read_csv(filepath+'\\'+filesname,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=df_bms.dropna(subset=['总电流[A]'])
+            df_bms.reset_index(inplace=True,drop=True)     #重置索引
+            # df_bms.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\98Download\\'+'BMS_'+sn+'.csv',encoding='GB18030')
+
+            #.........................................................................计算一致性..............................................................................
+            if not df_bms.empty:
+                BatUniform=CBMSBatUniform.BatUniform(sn,celltype,df_bms)
+                df_res=BatUniform.batuniform()
+                df_res.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\99Result\\'+'CBMS_Uniform'+sn+'.csv',encoding='GB18030')
+            # print('done!!!')
+        
+        except IndexError as e:
+            print(repr(e))
+            mylog.logopt(sn,e)
+            pass

+ 589 - 0
USER/SPF/01qixiang/03BatInterShort/CBMSBatInterShort.py

@@ -0,0 +1,589 @@
+import pandas as pd
+import numpy as np
+import matplotlib.pyplot as plt
+# 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):  #参数初始化
+
+        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
+
+        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=self._ncm_intershort()
+            return df_res
+            
+        elif self.celltype>50:
+            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 = list(self.df_bms.loc[num,self.celltemp_name])
+        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 = np.array(self.df_bms.loc[num,self.cellvolt_name])/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 np.array(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 np.array(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 np.array(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*2:      #静置时间满足要求
+                            standingtime=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_min<4.5 and 2<cellvolt_max<4.5 and deltvolt<0.005: 
+                                dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                                deltsoc_last=self._celldeltsoc_get(i,dict_baltime,capacity)
+                                time_last=self.bmstime[i]
+                                firsttime=0
+                        else:
+                            pass                
+                    elif standingtime>3600*10:
+                        standingtime=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_min<4.5 and 2<cellvolt_max<4.5 and deltvolt<0.005:
+                            dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                            deltsoc_now=self._celldeltsoc_get(i,dict_baltime,capacity)
+                            time_now=self.bmstime[i]
+                            
+                            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: 
+                        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._cellvolt_get(i)
+                        cellvolt_max1=max(cellvolt_now1)
+                        cellvolt_min1=min(cellvolt_now1)
+                        cellvolt_last1=self._cellvolt_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 and deltvolt1<0.005:
+                            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*12:
+                                    deltsoc_now1=self._celldeltsoc_get(i,dict_baltime1,capacity)
+
+                                    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:
+                    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:      #静置时间满足要求
+                            standingtime=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 abs(deltvolt)<0.003:
+                                dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                                deltsoc_last=self._celldeltsoc_get(i,dict_baltime,capacity)
+                                time_last=self.bmstime[i]
+                                firsttime=0
+                            else:
+                                pass
+                        else:
+                            pass                
+                    elif standingtime>3600*12:
+                        standingtime=0
+                        cellvolt_now=np.array(self._cellvolt_get(i))
+                        cellvolt_min=min(cellvolt_now)
+                        cellvolt_max=max(cellvolt_now)
+                        cellvolt_last=np.array(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 abs(deltvolt)<0.003:
+                            dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                            deltsoc_now=self._celldeltsoc_get(i, dict_baltime,capacity)    #获取每个电芯的SOC差
+                            time_now=self.bmstime[i]
+
+                            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:
+                            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)
+                            cellvolt_max1=max(cellvolt_now1)
+                            cellvolt_min1=min(cellvolt_now1)
+                            cellvolt_last1=self._cellvolt_get(i-1)
+                            deltvolt1=max(abs(cellvolt_now1-cellvolt_last1))
+                        
+                            if 2<cellvolt_max1<self.param.OcvInflexionBelow-0.002 and 2<cellvolt_min1<4.5 and deltvolt1<0.005:
+                                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=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:   
+                        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 or (self.packcrnt[i]>self.param.Capacity/3 and self.packcrnt[i+1]>self.param.Capacity/3):  #如果充电过程中时间间隔>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=deltAs_now-deltAs_last
+                        list_pud=-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,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

+ 92 - 0
USER/SPF/01qixiang/03BatInterShort/main.py

@@ -0,0 +1,92 @@
+import CBMSBatInterShort
+
+#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.V1_0_1 import DBDownload
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import log
+
+dbManager = DBManager.DBManager()
+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_6060 + SNnums_L7255 + SNnums_C7255 + SNnums_6040 + SNnums_4840 + SNnums_U7255
+    SNnums=['TJMCL120502305038','TJMCL120502305032','TJMCL120502305022','TJMCL120502305026','TJMCL120502305032','TJMCL120502305044','TJMCL120502305048','TJMCL120502305012','TJMCL120502305010']
+    
+    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)
+    start_time='2021-11-17 00:00:00'
+    end_time='2021-11-23 00:00:00'
+
+    #log信息配置
+    mylog=log.Mylog('log_short.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:
+                celltype=100
+                print('SN:{},未找到对应电池类型!!!'.format(sn))
+                # continue
+                # sys.exit()
+            
+            # 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=df_bms.dropna(subset=['总电流[A]'])
+            df_bms.reset_index(inplace=True,drop=True)     #重置索引
+            df_bms.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\98Download\\'+'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
+            tablename='cellstateestimation_soh'
+            DBRead=DBDownload.DBDownload(host, port, db, user, password, mode)
+            with DBRead as DBRead:
+                df_soh=DBRead.getdata(param='time_st,time_sp,sn,method,soh,cellsoh', tablename=tablename, sn=sn, timename='time_sp', st=start_time, sp=end_time)
+            
+            #...........................................................................计算漏电流............................................................................
+            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\spf\01qixiang\99Result\\'+'CBMS_Short_1_'+sn+'.csv',encoding='GB18030')
+            # print('done!!!')
+        
+        except Exception as e:
+            print(repr(e))
+            mylog.logopt(sn,e)
+            pass

+ 630 - 0
USER/SPF/01qixiang/04BatSoc/CBMSBatSoc.py

@@ -0,0 +1,630 @@
+import pandas as pd
+import numpy as np
+import matplotlib.pyplot as plt
+from pymysql import paramstyle
+from scipy.interpolate import interp2d
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam
+
+class BatSoc():
+    def __init__(self,sn,celltype,df_bms,df_soh,df_ram_sn,df_socdiff):  #参数初始化
+
+        self.sn=sn
+        self.celltype=celltype
+        self.param=BatParam.BatParam(celltype)
+        self.df_soh=df_soh
+        self.df_socdiff=df_socdiff
+        self.df_ram_sn=df_ram_sn
+        df_bms['时间戳']=pd.to_datetime(df_bms['时间戳'], format='%Y-%m-%d %H:%M:%S')
+        self.df_bms=df_bms
+        if not self.df_ram_sn.empty:
+            self.df_bms=self.df_bms[self.df_bms['时间戳'] > self.df_ram_sn.iloc[-1]['time']]    #滤除原始数据中的重复数据
+            self.df_bms.reset_index(inplace=True,drop=True)     #重置索引
+        self.packcrnt=self.df_bms['总电流[A]']*self.param.PackCrntDec
+        self.packvolt=self.df_bms['总电压[V]']
+        self.bms_soc=self.df_bms['SOC[%]']
+        self.bms_soh=self.df_bms['SOH[%]']
+        self.bmstime=self.df_bms['时间戳']
+
+        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 batsoc(self):
+        if self.celltype==1 or self.celltype==2 or self.celltype==3 or self.celltype==4:
+            if not self.df_bms.empty:
+                df_res, df_ram=self._ncm_soc()
+                return df_res, df_ram
+            else:
+                return pd.DataFrame(), pd.DataFrame()
+            
+        elif self.celltype==99:
+            if not self.df_bms.empty:
+                df_res, df_ram=self._lfp_soc()
+                return df_res, df_ram
+            else:
+                return pd.DataFrame(), pd.DataFrame()
+        
+        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 = list(self.df_bms.loc[num,self.celltemp_name])
+        celltemp=np.mean(celltemp)
+        self.celltemp=celltemp
+        if self.celltype==99:
+            if celltemp>=25:
+                self.tempweight=1
+                self.StandardStandingTime=3600
+            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=18000
+        else:
+            if celltemp>=20:
+                self.tempweight=1
+                self.StandardStandingTime=1800
+            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 = list(self.df_bms.loc[num,self.cellvolt_name])/1000
+        return cellvolt
+    
+    #......................................................判断单体电压离散度,并返回OCV值....................................................................................
+    def _ocv_dispersion(self,ocv):
+        cellvolt=self._cellvolt_get(0)
+        max_index=cellvolt.index(max(cellvolt))+1
+        min_index=cellvolt.index(min(cellvolt))+1
+
+        cellvoltmax=(self.df_bms['单体电压'+str(max_index)]/1000).tolist()
+        cellvoltmin=(self.df_bms['单体电压'+str(min_index)]/1000).tolist()
+        if ocv==1:
+            max_std=np.std(cellvoltmax,ddof=1)
+            min_std=np.std(cellvoltmin,ddof=1)
+            if max_std<0.0015 and min_std<0.0015:
+                ocvmin=np.mean(cellvoltmin)
+                ocvmax=np.mean(cellvoltmax)
+                return ocvmin, ocvmax
+            else:
+                return 0, 0
+        else:
+            return np.mean(cellvoltmin), np.mean(cellvoltmax)
+
+    #...........................................................三元电池的soc计算...............................................................................................
+    def _ncm_soc(self):
+        column_name=['time', 'sn', 'bms_soc', 'packsoc', 'socdsp', 'cellsocmin', 'cellsocmax','ocvweight','socstep']
+        df_res=pd.DataFrame(columns=column_name)
+        df_bms_len=len(self.df_bms)
+        rampackcrnt=self.packcrnt[df_bms_len-1]
+        ocvweight=0
+        
+        #获取电池包SOH................................................................................................
+        if self.df_soh.empty:
+            batsoh=self.bms_soh[0]
+            capacity=self.param.Capacity*batsoh/100
+        else:
+            batsoh=self.df_soh.loc[0,'soh']
+            capacity=self.param.Capacity*batsoh/100
+
+        #计算静置时间和累计As量..................................................................................................
+        if not self.df_ram_sn.empty:
+            standingtime=self.df_ram_sn.iloc[-1]['standingtime']
+            as_accum=0
+            for i in range(df_bms_len):
+                if i==0:
+                    step=(self.bmstime[i]-self.df_ram_sn.iloc[-1]['time']).total_seconds()
+                    if abs(self.packcrnt[i])<0.1 and abs(self.df_ram_sn.iloc[-1]['rampackcrnt'])<0.1:
+                        standingtime=standingtime+step
+                    else:
+                        standingtime=0
+                        if step<120:
+                            as_accum=as_accum-self.packcrnt[i]*step
+                        else:
+                            as_accum=as_accum+((self.bms_soc[i]-self.df_ram_sn.iloc[-1]['bms_soc'])*capacity*3600/100)
+                else:
+                    step=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                    if abs(self.packcrnt[i])<0.1 and abs(self.packcrnt[i-1])<0.1:
+                        standingtime=standingtime+step
+                        as_accum=as_accum-self.packcrnt[i]*step
+                    else:
+                        standingtime=0
+                        as_accum=as_accum-self.packcrnt[i]*step
+        else:
+            standingtime=0
+            as_accum=0
+            for i in range(df_bms_len):
+                if i==0:
+                    pass
+                else:
+                    step=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                    if abs(self.packcrnt[i])<0.1 and abs(self.packcrnt[i-1])<0.1:
+                        standingtime=standingtime+step
+                        as_accum=as_accum-self.packcrnt[i]*step
+                    else:
+                        standingtime=0
+                        as_accum=as_accum-self.packcrnt[i]*step
+        
+        if standingtime>3600*24*30:
+            standingtime=3600*24*30
+        else:
+            pass
+        
+        #计算单体最大最小SOC.............................................................................................................................
+        if not self.df_ram_sn.empty:
+            self._celltemp_weight(0)
+            ramcellvoltmin=self.df_ram_sn.iloc[-1]['ramcellvoltmin']
+            ramcellvoltmax=self.df_ram_sn.iloc[-1]['ramcellvoltmax']
+            ramcellsocmin=self.df_ram_sn.iloc[-1]['cellsocmin']
+            ramcellsocmax=self.df_ram_sn.iloc[-1]['cellsocmax']
+            ocvweight_2dlook=interp2d(self.param.OcvWeight_StandingTime, self.param.OcvWeight_Temp, self.param.OcvWeight, kind = 'linear')
+            ocvweight=ocvweight_2dlook(standingtime, self.celltemp)
+            ocvweight=ocvweight[0]
+
+            if ocvweight>0.01:
+                if df_bms_len>1:      #数据字段长度≥2
+                    ocvmin, ocvmax=self._ocv_dispersion(1)
+                    if 2<ocvmin<4.5 and 2<ocvmax<4.5:
+                        ocvsocmin=np.interp(ocvmin,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                        ocvsocmax=np.interp(ocvmax,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                        cellsocmin=ocvsocmin*ocvweight+ramcellsocmin*(1-ocvweight)
+                        cellsocmax=ocvsocmax*ocvweight+ramcellsocmax*(1-ocvweight)
+                        socstep=0
+                    else:
+                        cellsocmin=ramcellsocmin
+                        cellsocmax=ramcellsocmax
+                        socstep=1
+                else:       #数据字段长度==1
+                    cellvolt0=self._cellvolt_get(0)
+                    ocvmax=max(cellvolt0)
+                    ocvmin=min(cellvolt0)
+                    if 2<ocvmin<4.5 and 2<ocvmax<4.5 and abs(ramcellvoltmin-ocvmin)<0.003 and abs(ramcellvoltmax-ocvmax)<0.003:
+                        ocvsocmin=np.interp(ocvmin,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                        ocvsocmax=np.interp(ocvmax,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                        cellsocmin=ocvsocmin*ocvweight+ramcellsocmin*(1-ocvweight)
+                        cellsocmax=ocvsocmax*ocvweight+ramcellsocmax*(1-ocvweight)
+                        socstep=2
+                    else:
+                        cellsocmin=ramcellsocmin
+                        cellsocmax=ramcellsocmax
+                        socstep=3
+                #更新ram电芯电压和ram电流
+                ramcellvoltmin=ocvmin   
+                ramcellvoltmax=ocvmax
+            else:
+                cellsocmin=ramcellsocmin+(as_accum*100)/(3600*capacity)
+                cellsocmax=ramcellsocmax+(as_accum*100)/(3600*capacity)
+                socstep=4
+
+                #更新ram电芯电压
+                cellvolt=self._cellvolt_get(df_bms_len-1)
+                ramcellvoltmin=min(cellvolt)
+                ramcellvoltmax=max(cellvolt)
+
+
+        else:
+            cellsocmin= self.bms_soc[df_bms_len-1]
+            cellsocmax= self.bms_soc[df_bms_len-1]
+            socstep=5
+
+            #更新ram电芯电压
+            cellvolt=self._cellvolt_get(df_bms_len-1)
+            ramcellvoltmin=min(cellvolt)
+            ramcellvoltmax=max(cellvolt)
+        
+        #单体Soc满充修正........................................................................................................................................................................
+        if df_bms_len>1:      #数据字段长度≥2
+            cellvolt0=self._cellvolt_get(df_bms_len-1)
+            cellvolt1=self._cellvolt_get(df_bms_len-2)
+            if self.param.CellFullChrgVolt-0.01<max(cellvolt0)<5 and self.param.CellFullChrgVolt-0.01<max(cellvolt1)<5 and self.param.CellFullChrgCrnt<self.packcrnt[df_bms_len-1]<-0.5 and self.param.CellFullChrgCrnt<self.packcrnt[df_bms_len-2]<-0.5:
+                cellsocmin=cellsocmin+(self.param.FullChrgSoc-cellsocmax)
+                cellsocmax=self.param.FullChrgSoc 
+            else:
+                pass
+        else:
+            cellvolt0=self._cellvolt_get(df_bms_len-1)
+            if self.param.CellFullChrgVolt-0.01<max(cellvolt0)<5  and self.param.CellFullChrgCrnt<self.packcrnt[df_bms_len-1]<-0.5:
+                cellsocmin=cellsocmin+(self.param.FullChrgSoc-cellsocmax)
+                cellsocmax=self.param.FullChrgSoc
+            else:
+                pass
+
+        #计算电池包packsoc..................................................................................................................................................................................
+        cellsocmin1=eval(format(cellsocmin,'.1f'))
+        cellsocmax1=eval(format(cellsocmax,'.1f'))
+        if cellsocmax<cellsocmin:   #防止最大SOC>最小SOC
+            a=cellsocmin
+            cellsocmin=cellsocmax
+            cellsocmax=a
+        else:
+            pass
+
+        if cellsocmax>100:  #限制最大最小SOC的范围
+            cellsocmax=100
+        elif cellsocmax<0:
+            cellsocmax=0
+        else:
+            pass
+        if cellsocmin>100:
+            cellsocmin=100
+        elif cellsocmin<0:
+            cellsocmin=0
+        else:
+            pass
+        
+        alpha=(cellsocmin+cellsocmax)*0.02-2    #blending系数计算
+        if cellsocmax>90 or alpha>1:
+            alpha=1
+        elif cellsocmin<10 or alpha<-1:
+            alpha=-1
+        else:
+            pass
+        packsoc=(cellsocmin+cellsocmax)*0.5+(cellsocmax-cellsocmin)*0.5*alpha
+        packsoc=eval(format(packsoc,'.1f'))
+        socdsp=packsoc
+
+        #输出ram结果....................................................................................................................................................................
+        list_ram=[self.bmstime[df_bms_len-1], self.sn, self.bms_soc[df_bms_len-1], packsoc, cellsocmin, cellsocmax, standingtime, rampackcrnt, ramcellvoltmin, ramcellvoltmax,0,0,ocvweight,as_accum,socstep]
+        df_ram=pd.DataFrame(columns=self.df_ram_sn.columns)
+        df_ram.loc[0]=list_ram
+
+        #输出计算结果........................................................................................................................................................................
+        df_res.loc[0]=[self.bmstime[df_bms_len-1], self.sn, self.bms_soc[df_bms_len-1], packsoc, socdsp, cellsocmin1, cellsocmax1,ocvweight,socstep]
+        return df_res, df_ram
+    
+
+
+    #...........................................................磷酸铁锂电池的soc计算...............................................................................................
+    def _lfp_soc(self):
+        column_name=['time', 'sn', 'bms_soc', 'packsoc', 'socdsp', 'cellsocmin', 'cellsocmax','ocvweight','socstep']
+        df_res=pd.DataFrame(columns=column_name)
+        df_bms_len=len(self.df_bms)
+        rampackcrnt=self.packcrnt[df_bms_len-1]
+        cellsoc_diff=self.df_socdiff.iloc[-1]['cellsoc_diff']
+        
+        #获取电池包SOH................................................................................................
+        if self.df_soh.empty:
+            batsoh=self.bms_soh[0]
+            capacity=self.param.Capacity*batsoh/100
+        else:
+            batsoh=self.df_soh.loc[0,'soh']
+            capacity=self.param.Capacity*batsoh/100
+
+        #计算静置时间和累计As量以及OCV权重值..................................................................................................
+        if not self.df_ram_sn.empty:
+            standingtime=self.df_ram_sn.iloc[-1]['standingtime']
+            ocvweight=self.df_ram_sn.iloc[-1]['ocvweight']
+            as_accum=0
+            for i in range(df_bms_len):
+                if i==0:
+                    step=(self.bmstime[i]-self.df_ram_sn.iloc[-1]['time']).total_seconds()
+                    if abs(self.packcrnt[i])<0.1 and abs(self.df_ram_sn.iloc[-1]['rampackcrnt'])<0.1:
+                        standingtime=standingtime+step
+                    else:
+                        standingtime=0
+                        if step<120:
+                            as_accum=as_accum-self.packcrnt[i]*step
+                        else:
+                            as_accum=as_accum+((self.bms_soc[i]-self.df_ram_sn.iloc[-1]['bms_soc'])*capacity*3600/100)
+                else:
+                    step=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                    if abs(self.packcrnt[i])<0.1 and abs(self.packcrnt[i-1])<0.1:
+                        standingtime=standingtime+step
+                        as_accum=as_accum-self.packcrnt[i]*step
+                    else:
+                        standingtime=0
+                        as_accum=as_accum-self.packcrnt[i]*step
+        
+        else:
+            standingtime=0
+            ocvweight=0.5
+            as_accum=0
+            for i in range(df_bms_len):
+                if i==0:
+                    pass
+                else:
+                    step=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                    if abs(self.packcrnt[i])<0.1 and abs(self.packcrnt[i-1])<0.1:
+                        standingtime=standingtime+step
+                        as_accum=as_accum-self.packcrnt[i]*step
+                    else:
+                        standingtime=0
+                        as_accum=as_accum-self.packcrnt[i]*step
+
+        #静置时间限值..............................................
+        if standingtime>3600*24*30:     
+            standingtime=3600*24*30
+        else:
+            pass
+            
+        #权重值计算.................................................
+        ocvweight=ocvweight-as_accum*10/(capacity*3600)
+        if ocvweight>1:
+            ocvweight=1
+        elif ocvweight<0:
+            ocvweight=0
+        else:
+            pass
+        ocvweight=eval(format(ocvweight,'.2f'))
+        
+        #计算单体最大最小SOC.............................................................................................................................
+        if not self.df_ram_sn.empty:
+            self._celltemp_weight(0)
+            kocellvoltmin=self.df_ram_sn.iloc[-1]['kocellvoltmin']
+            kocellvoltmax=self.df_ram_sn.iloc[-1]['kocellvoltmax']
+            ramcellvoltmin=self.df_ram_sn.iloc[-1]['ramcellvoltmin']
+            ramcellvoltmax=self.df_ram_sn.iloc[-1]['ramcellvoltmax']
+            ramcellsocmin=self.df_ram_sn.iloc[-1]['cellsocmin']
+            ramcellsocmax=self.df_ram_sn.iloc[-1]['cellsocmax']
+            #满足静置时间
+            if standingtime>self.StandardStandingTime: 
+                #数据字段长度≥2............................................................................................................... 
+                if df_bms_len>1:      
+                    ocvmin, ocvmax=self._ocv_dispersion(1)
+                    ocvsocmin=np.interp(ocvmin,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                    ocvsocmax=np.interp(ocvmax,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                    if 2<ocvmin<self.param.OcvInflexionBelow and 2<ocvmax<self.param.OcvInflexionBelow and ocvsocmax<self.param.SocInflexion1:
+                        cellsocmin=ocvsocmin
+                        cellsocmax=ocvsocmax
+                        socstep=0
+                    elif 2<ocvmin<self.param.OcvInflexionBelow and ocvmax>=self.param.OcvInflexionBelow and ocvsocmin<self.param.SocInflexion1:
+                        cellsocmin=ocvsocmin
+                        cellsocmax=ocvsocmin+cellsoc_diff
+                        socstep=1
+                    else:
+                        #第一平台向第二平台修正
+                        if ocvsocmax>self.param.SocInflexion3+5 and ramcellsocmax<self.param.SocInflexion2-5:
+                            if  ocvsocmin>self.param.SocInflexion3+5 and ramcellsocmin<self.param.SocInflexion2-5:
+                                cellsocmin=self.param.SocInflexion3
+                                cellsocmax=cellsocmin+cellsoc_diff
+                                socstep=2
+                            else:
+                                cellsocmax=self.param.SocInflexion3
+                                cellsocmin=cellsocmax-cellsoc_diff
+                                socstep=3
+                        #第一非平台区向第一平台区修正
+                        elif ocvsocmax>self.param.SocInflexion1+5 and ramcellsocmax<self.param.SocInflexion1-2:
+                            if ocvsocmin>self.param.SocInflexion1+5 and ramcellsocmin<self.param.SocInflexion1-2:
+                                cellsocmin=self.param.SocInflexion1
+                                cellsocmax=cellsocmin+cellsoc_diff
+                                socstep=4
+                            else:
+                                cellsocmax=self.param.SocInflexion1
+                                cellsocmin=cellsocmax-cellsoc_diff
+                                socstep=5
+                        #第二平台区向第一平台区修正
+                        elif ocvsocmin<self.param.SocInflexion2-5 and ramcellsocmin>self.param.SocInflexion3+5:
+                            if ocvsocmax<self.param.SocInflexion2-5 and ramcellsocmax>self.param.SocInflexion3+5:
+                                cellsocmax=self.param.SocInflexion2
+                                cellsocmin=cellsocmax-cellsoc_diff
+                                socstep=6
+                            else:
+                                cellsocmin=self.param.SocInflexion2
+                                cellsocmax=cellsocmin+cellsoc_diff
+                                socstep=7
+                        else:
+                            cellsocmin=ramcellsocmin
+                            cellsocmax=ramcellsocmax
+                            socstep=8
+                #数据字段长度==1..............................................................................................
+                else:       
+                    cellvolt0=self._cellvolt_get(0)
+                    ocvmax=max(cellvolt0)
+                    ocvmin=min(cellvolt0)
+                    ocvsocmin=np.interp(ocvmin,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                    ocvsocmax=np.interp(ocvmax,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                    if 2<ocvmin<self.param.OcvInflexionBelow and 2<ocvmax<self.param.OcvInflexionBelow and ocvsocmax<self.param.SocInflexion1:
+                        cellsocmin=ocvsocmin
+                        cellsocmax=ocvsocmax
+                        socstep=9
+                    elif 2<ocvmin<self.param.OcvInflexionBelow and ocvmax>=self.param.OcvInflexionBelow and ocvsocmin<self.param.SocInflexion1:
+                        cellsocmin=ocvsocmin
+                        cellsocmax=ocvsocmin+cellsoc_diff
+                        socstep=10
+                    else:
+                        #第一平台向第二平台修正
+                        if ocvsocmax>self.param.SocInflexion3+5 and ramcellsocmax<self.param.SocInflexion2-5:
+                            if  ocvsocmin>self.param.SocInflexion3+5 and ramcellsocmin<self.param.SocInflexion2-5:
+                                cellsocmin=self.param.SocInflexion3
+                                cellsocmax=cellsocmin+cellsoc_diff
+                                socstep=11
+                            else:
+                                cellsocmax=self.param.SocInflexion3
+                                cellsocmin=cellsocmax-cellsoc_diff
+                                socstep=12
+                        #第一非平台区向第一平台区修正
+                        elif ocvsocmax>self.param.SocInflexion1+5 and ramcellsocmax<self.param.SocInflexion1-2:
+                            if ocvsocmin>self.param.SocInflexion1+5 and ramcellsocmin<self.param.SocInflexion1-2:
+                                cellsocmin=self.param.SocInflexion1
+                                cellsocmax=cellsocmin+cellsoc_diff
+                                socstep=13
+                            else:
+                                cellsocmax=self.param.SocInflexion1
+                                cellsocmin=cellsocmax-cellsoc_diff
+                                socstep=14
+                        #第二平台区向第一平台区修正
+                        elif ocvsocmin<self.param.SocInflexion2-5 and ramcellsocmin>self.param.SocInflexion3+5:
+                            if ocvsocmax<self.param.SocInflexion2-5 and ramcellsocmax>self.param.SocInflexion3+5:
+                                cellsocmax=self.param.SocInflexion2
+                                cellsocmin=cellsocmax-cellsoc_diff
+                                socstep=15
+                            else:
+                                cellsocmin=self.param.SocInflexion2
+                                cellsocmax=cellsocmin+cellsoc_diff
+                                socstep=16
+                        else:
+                            cellsocmin=ramcellsocmin
+                            cellsocmax=ramcellsocmax
+                            socstep=17
+                #更新ram电芯电压
+                ramcellvoltmin=ocvmin   
+                ramcellvoltmax=ocvmax
+            #不满足静置时间,判断电压回弹方向
+            elif standingtime>130 and 2<kocellvoltmax<4.5 and 2<kocellvoltmin<4.5:  
+                cellvoltmin, cellvoltmax=self._ocv_dispersion(0)
+                ocvsocmin1=np.interp(cellvoltmin,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                ocvsocmax1=np.interp(cellvoltmax,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                #更新ram电芯电压
+                ramcellvoltmin=cellvoltmin   
+                ramcellvoltmax=cellvoltmax
+                #最大最小电压向上回弹
+                if 2<cellvoltmin<4.5 and cellvoltmin>kocellvoltmin+0.005 and ocvsocmin1>ramcellsocmin:
+                    if 2<cellvoltmax<4.5 and cellvoltmax>kocellvoltmax+0.005 and ocvsocmax1>ramcellsocmax:
+                        #最大最小电压均在非平台区
+                        if ocvsocmax1<self.param.SocInflexion1:
+                            cellsocmin=ocvsocmin1
+                            cellsocmax=ocvsocmax1
+                            socstep=18
+                        #最小电压均在非平台区,最大电压在平台区
+                        elif ocvsocmin1<self.param.SocInflexion1:
+                            cellsocmin=ocvsocmin1
+                            cellsocmax=cellsocmin+cellsoc_diff
+                            socstep=19
+                        #最大最小电压均在平台区,ramsoc在非平台区
+                        elif ocvsocmin1>self.param.SocInflexion1+5 and ramcellsocmax<self.param.SocInflexion1 and ramcellsocmin<self.param.SocInflexion1:
+                            cellsocmin=self.param.SocInflexion1
+                            cellsocmax=cellsocmin+cellsoc_diff
+                            socstep=20
+                        else:
+                            cellsocmin=ramcellsocmin
+                            cellsocmax=ramcellsocmax
+                            socstep=21
+                    else:
+                        cellsocmin=ramcellsocmin
+                        cellsocmax=ramcellsocmax
+                        socstep=22
+                #最大最小电压向下回弹
+                elif 2<cellvoltmin<4.5 and cellvoltmin<kocellvoltmin-0.005 and ocvsocmin1<ramcellsocmin:
+                    if 2<cellvoltmax<4.5 and cellvoltmax<kocellvoltmax-0.005 and ocvsocmax1>ramcellsocmax:
+                        #最大最小电压均在非平台区
+                        if ocvsocmax1<self.param.SocInflexion1:
+                            cellsocmin=ocvsocmin1
+                            cellsocmax=ocvsocmax1
+                            socstep=23
+                        #最小电压在非平台区,最大电压在平台区
+                        elif ocvsocmin1<self.param.SocInflexion1:
+                            cellsocmin=ocvsocmin1
+                            cellsocmax=cellsocmin+cellsoc_diff
+                            socstep=24
+                        else:
+                            cellsocmin=ramcellsocmin
+                            cellsocmax=ramcellsocmax
+                            socstep=25
+                    else:
+                        cellsocmin=ramcellsocmin
+                        cellsocmax=ramcellsocmax
+                        socstep=26
+                #电压回弹方向一致
+                else:
+                    cellsocmin=ramcellsocmin
+                    cellsocmax=ramcellsocmax
+                    socstep=27
+
+            #更新ko电压
+            elif 70<standingtime<=130:   
+                cellsocmin=ramcellsocmin
+                cellsocmax=ramcellsocmax
+                socstep=28
+                cellvolt=self._cellvolt_get(df_bms_len-1)
+                kocellvoltmin=min(cellvolt)
+                kocellvoltmax=max(cellvolt)
+                ramcellvoltmin=min(cellvolt)
+                ramcellvoltmax=max(cellvolt)
+                
+            else:
+                cellsocmin=ramcellsocmin+(as_accum*100)/(3600*capacity)
+                cellsocmax=ramcellsocmax+(as_accum*100)/(3600*capacity)
+                socstep=29
+                #更新ram电芯电压
+                cellvolt=self._cellvolt_get(df_bms_len-1)
+                kocellvoltmin=0
+                kocellvoltmax=0
+                ramcellvoltmin=min(cellvolt)
+                ramcellvoltmax=max(cellvolt)
+                
+
+        else:
+            cellsocmin= self.bms_soc[df_bms_len-1]-cellsoc_diff/2
+            cellsocmax= self.bms_soc[df_bms_len-1]+cellsoc_diff/2
+            socstep=30
+            #更新ram电芯电压
+            cellvolt=self._cellvolt_get(df_bms_len-1)
+            ramcellvoltmin=min(cellvolt)
+            ramcellvoltmax=max(cellvolt)
+            kocellvoltmin=0
+            kocellvoltmax=0
+        
+        #单体Soc满充修正........................................................................................................................................................................
+        if df_bms_len>1:      #数据字段长度≥2
+            cellvolt0=self._cellvolt_get(df_bms_len-1)
+            cellvolt1=self._cellvolt_get(df_bms_len-2)
+            if self.param.CellFullChrgVolt<max(cellvolt0)<5 and self.param.CellFullChrgVolt<max(cellvolt1)<5 and self.param.CellFullChrgCrnt<self.packcrnt[df_bms_len-1]<-0.5 and self.param.CellFullChrgCrnt<self.packcrnt[df_bms_len-2]<-0.5:
+                cellsocmax=self.param.FullChrgSoc
+                cellsocmin=cellsocmax-cellsoc_diff
+                pass
+        else:
+            cellvolt0=self._cellvolt_get(df_bms_len-1)
+            if self.param.CellFullChrgVolt-0.01<max(cellvolt0)<5  and self.param.CellFullChrgCrnt<self.packcrnt[df_bms_len-1]<-0.5:
+                cellsocmax=self.param.FullChrgSoc
+                cellsocmin=cellsocmax-cellsoc_diff
+            else:
+                pass
+
+        #计算电池包packsoc..................................................................................................................................................................................
+        cellsocmin1=eval(format(cellsocmin,'.1f'))
+        cellsocmax1=eval(format(cellsocmax,'.1f'))
+        if cellsocmax<cellsocmin:   #防止最大SOC>最小SOC
+            a=cellsocmin
+            cellsocmin=cellsocmax
+            cellsocmax=a
+        else:
+            pass
+
+        if cellsocmax>100:  #限制最大最小SOC的范围
+            cellsocmax=100
+        elif cellsocmax<0:
+            cellsocmax=0
+        else:
+            pass
+        if cellsocmin>100:
+            cellsocmin=100
+        elif cellsocmin<0:
+            cellsocmin=0
+        else:
+            pass
+        
+        alpha=(cellsocmin+cellsocmax)*0.02-2    #blending系数计算
+        if cellsocmax>90 or alpha>1:
+            alpha=1
+        elif cellsocmin<10 or alpha<-1:
+            alpha=-1
+        else:
+            pass
+        packsoc=(cellsocmin+cellsocmax)*0.5+(cellsocmax-cellsocmin)*0.5*alpha
+        packsoc=eval(format(packsoc,'.1f'))
+        socdsp=packsoc
+
+        #输出ram结果....................................................................................................................................................................
+        list_ram=[self.bmstime[df_bms_len-1], self.sn, self.bms_soc[df_bms_len-1], packsoc, cellsocmin, cellsocmax, standingtime, rampackcrnt, ramcellvoltmin, ramcellvoltmax,kocellvoltmin,kocellvoltmax,ocvweight,as_accum,socstep]
+        df_ram=pd.DataFrame(columns=self.df_ram_sn.columns)
+        df_ram.loc[0]=list_ram
+
+        #输出计算结果........................................................................................................................................................................
+        df_res.loc[0]=[self.bmstime[df_bms_len-1], self.sn, self.bms_soc[df_bms_len-1], packsoc, socdsp, cellsocmin1, cellsocmax1,ocvweight,socstep]
+        return df_res, df_ram

+ 140 - 0
USER/SPF/01qixiang/04BatSoc/main.py

@@ -0,0 +1,140 @@
+
+#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 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
+
+#...................................SOC计算函数......................................................................................................................
+def soc_cal():
+    import CBMSBatSoc
+    global SNnums
+    global df_ram
+
+    start=time.time()
+    end_time=datetime.datetime.now()-datetime.timedelta(seconds=10)
+    start_time=end_time-datetime.timedelta(seconds=70)
+    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()
+        
+        # sn='PK504B10100003033'
+        # 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\spf\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\spf\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
+        tablename='cellstateestimation_soh'
+        DBRead=DBDownload.DBDownload(host, port, db, user, password, mode)
+        with DBRead as DBRead:
+            df_soh=DBRead.getdata(param='time_st,sn,soh', tablename=tablename, sn=sn, timename='time_sp', st=start_time, sp=end_time)
+        
+        if celltype==99:
+            tablename='cellstateestimation_uniform_socvoltdiff'
+            with DBRead as DBRead:
+                df_socdiff=DBRead.getdata(param='time,sn,cellsoc_diff', tablename=tablename, sn=sn, timename='time', st=start_time, sp=end_time)
+        else:
+            df_socdiff=pd.DataFrame()
+
+        # print(df_bms)
+        # print(df_soh)
+        
+        #socram存储...............................................................................................................................................................
+        if not df_bms.empty:
+            df_ram_sn=df_ram[df_ram['sn']==sn]
+
+            if df_ram_sn.empty:
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\04BatSoc\\'+'soc_'+str(sn)+'.txt','a') as file:
+                    file.write(str(tuple(df_ram_sn.columns))+'\n')
+            
+            BatSoc=CBMSBatSoc.BatSoc(sn,celltype,df_bms,df_soh,df_ram_sn,df_socdiff)
+            df_res, df_ram_sn=BatSoc.batsoc()
+            if not df_ram_sn.empty:
+                sn_index=df_ram.loc[df_ram['sn']==sn].index
+                df_ram=df_ram.drop(index=sn_index)
+                df_ram=df_ram.append(df_ram_sn)
+                df_ram.reset_index(inplace=True,drop=True)     #重置索引
+            
+            # print(df_res)
+            # print(df_ram)
+
+            if not df_ram_sn.empty:
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\04BatSoc\\'+'soc_'+str(sn)+'.txt','a') as file:
+                    file.write(str(tuple(df_ram_sn.iloc[-1]))+'\n')
+                    
+        
+        end=time.time()
+        print(end-start)
+        
+#...............................................主函数.......................................................................................................................
+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_6060 + SNnums_L7255 + SNnums_C7255 + SNnums_6040 + SNnums_4840 + SNnums_U7255
+    SNnums=['UD02030118B4C0046','MGMCLN750N215I005','PK504B10100004327','PK504B10100003032','PK504B00100004172','PK504B10100003033','PK50001A100000222']
+    #
+
+    #log信息配置
+    mylog=log.Mylog('log_soc.txt','error')
+    mylog.logcfg()
+
+    #参数初始化.........................................................................................................................................................................
+    column_name=['time', 'sn', 'bms_soc', 'soc','cellsocmin','cellsocmax','standingtime','rampackcrnt','ramcellvoltmin','ramcellvoltmax','kocellvoltmin','kocellvoltmax','ocvweight','as_accum','socstep']
+    df_ram=pd.DataFrame(columns=column_name)
+
+    scheduler = BlockingScheduler()
+    scheduler.add_job(soc_cal, 'interval', seconds=60, id='soc_job')
+    
+    try:
+        
+        scheduler.start()
+
+    except Exception as e:
+        scheduler.shutdown()
+        print(repr(e))
+        mylog.logopt(e)

+ 539 - 0
USER/SPF/01qixiang/05BatDiag/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
USER/SPF/01qixiang/05BatDiag/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
USER/SPF/01qixiang/05BatDiag/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','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

+ 73 - 0
USER/SPF/01qixiang/05BatDiag/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 57 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,57,4,'电芯温度高','立即联系用户确认电池状态']
+                ErrorFlg=1            
+            else:#如果故障发生当前故障中有该故障,则不进行操作
+                pass
+        else:
+            if 57 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']==57].index,['end_time']]=end_time
+                else:
+                    pass
+            else:
+                pass
+        return df_Diag_Ram

+ 356 - 0
USER/SPF/01qixiang/05BatDiag/SC_SamplingSafty.py

@@ -0,0 +1,356 @@
+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 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,2,'电池电压采样断线,最高电压为{:.2f}V,电池编号为{},最低电压为{:.2f}V,电池编号为{}'.format(MaxVolt,InVMaxBatNo.values,MinVolt,InVMinBatNo.values),'返厂维修']
+                    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]>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']==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,2,str(InVMaxBatNo.values)+'号电压大于{:.2f}V,采样无效'.format(param.CellOVlmt),'返厂维修']
+                    ErrorFlg=1
+                else:
+                    pass
+        elif len(InVMinBatNo):
+            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,2,str(InVMinBatNo.values)+'号电压小于{:.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]>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 51 in df_Diag_Ram['code']:
+                VolCount=VolCount+1
+                QuitErrCount[51]=0
+                if VolCount>10:
+                    VolCount=11
+                    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.values)+'号电池电压离群','技术介入诊断']
+                        ErrorFlg=1            
+                    else:#如果故障发生当前故障中有该故障,则不进行操作
+                        pass                
+            else:
+                VolCount=0
+                if 51 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']==51].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 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,2,'电池温度采样断线,最高温度为{}℃,电池编号为{},最低温度为{}℃,电池编号为{}'.format(maxCellTemp,InVMaxTempBatNo.values,minCellTemp,InVMinTempBatNo.values),'返厂维修']
+                    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]>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']==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,2,str(InVMaxTempBatNo.values)+'号温度大于{}℃,采样无效'.format(param.PackOTlmt),'联系用户核实电池温度情况,并返厂维修']
+                    ErrorFlg=1
+                else:
+                    pass
+        elif len(InVMinTempBatNo):
+            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,2,str(InVMinTempBatNo.values)+'号温度小于{}℃,采样无效'.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]>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']==2].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 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.values)+'号电池异常温度离群','技术介入诊断']
+                    ErrorFlg=1            
+                else:#如果故障发生当前故障中有该故障,则不进行操作
+                    pass
+            else:
+                if 8 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']==8].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]
+            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 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,2,'其他温度采样断线,最高温度为{}℃,传感器编号为{},最低温度为{}℃,传感器编号为{}'.format(maxOtherTemp,InVMaxOtherTempBatNo.values,minOtherTemp,InVMinOtherTempBatNo.values),'返厂维修']
+                        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]>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']==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,2,'传感器温度大于{}℃,采样无效'.format(param.OtherOTlmt),'联系用户核实电池温度情况,并返厂维修']
+                        ErrorFlg=1
+                    else:
+                        pass
+            elif len(InVMinTempBatNo):
+                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,2,'传感器温度小于{}℃,采样无效'.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]>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']==55].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 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,'传感器温度异常离群','技术介入诊断']
+                        ErrorFlg=1            
+                    else:#如果故障发生当前故障中有该故障,则不进行操作
+                        pass
+                else:
+                    if 56 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']==56].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 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,'传感器温度异常离群','技术立即介入诊断']
+                        ErrorFlg=1
+                else:
+                    if 56 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']==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.values)+'号电芯温度异常离群','技术立即介入诊断']
+                        
+                        ErrorFlg=1
+                else:
+                    if 8 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']==8].index,['end_time']]=end_time              
+
+        return df_Diag_Ram

+ 502 - 0
USER/SPF/01qixiang/05BatDiag/deploy.py

@@ -0,0 +1,502 @@
+
+__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 import CBMSBatDiag
+from LIB.MIDDLE.SaftyCenter.Common import DBDownload as DBDw
+from LIB.MIDDLE.SaftyCenter.Common import QX_BatteryParam
+from urllib import parse
+import pymysql
+
+
+#...................................电池包电芯安全诊断函数......................................................................................................................
+def diag_cal():
+    task_on=1
+    global SNnums
+    # global df_Diag_Ram
+    global start
+    global df_sn
+    
+    # 算法参数
+    host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port=3306
+    db='safety_platform'
+    user='qx_algo_rw'
+    password=parse.quote_plus('qx@123456')
+    db_engine = create_engine(
+        "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+            user, password, host, port, db
+        ))
+
+        
+    #............................模块运行前,先读取数据库中所有结束时间为0的数据,需要从数据库中读取................``
+    # print("select start_time, end_time, product_id, code, level, info, advice, factory from {}".format(tablename))
+    try:
+        df_Diag_Ram=pd.read_sql("select start_time, end_time, product_id, code, level, info, advice, Batpos from all_fault_info_copy where end_time='0000-00-00 00:00:00'", db_engine)
+    finally:
+        db_engine.dispose()
+    # df_Diag_Ram=df_Diag_Ram.dropna(axis=1,how='any')
+    # df_Diag_Ram=df_Diag_Ram.reset_index(drop=True)    
+    #读取结果数据库数据........................................................................................................................................................
+    host1='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port1=3306
+    db1='qx_cas'
+    user1='qx_algo_rw'
+    password1='qx@123456'
+    mode=1
+    tablename1='cellstateestimation_soh'
+    tablename2='cellstateestimation_uniform_socvoltdiff'       
+    DBRead=DBDownload.DBDownload(host1, port1, db1, user1, password1,mode)
+    DBRead.connect()
+
+    #读取电池当前运营状态.....................................................................................................................................................
+    host2='rm-bp10j10qy42bzy0q7.mysql.rds.aliyuncs.com'
+    port2=3306
+    db2='qixiang_manage'
+    user2='qx_algo_rw'
+    password2='qx@123456'
+    mode=4
+    tablename3='py_battery'     
+    DBReadOpe=DBDw.DBDownload(host2, port2, db2, user2, password2,mode)
+    DBReadOpe.connect()
+    
+    host3='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port3=3306
+    db3='safety_platform'
+    user3='qx_algo_rw'
+    password3='qx@123456'
+    db_engine = create_engine(
+    "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+        user3, parse.quote_plus(password3), host3, port3, db3
+    ))
+    conn_platform = pymysql.connect(host=host3, port=port3, user=user3, password=password3, database=db3)
+    cursor_platform = conn_platform.cursor()
+    #..................................设置时间..........................................................  
+    start=time.time()
+    end_time=datetime.datetime.now()
+    start_time=end_time-datetime.timedelta(seconds=900)#15分钟跑一次,一次取15分钟数据
+    start_time=start_time.strftime('%Y-%m-%d %H:%M:%S')
+    end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+    logger.info(" diag_cal START!")
+
+    for i in range(0, len(df_sn)):
+        print(df_sn.loc[i, 'imei'])
+        try:
+            if df_sn.loc[i, 'imei'][5:9] == 'N640':
+                celltype=1 #6040三元电芯
+            elif df_sn.loc[i, 'imei'][5:9] == 'N440':
+                celltype=2 #4840三元电芯
+            elif df_sn.loc[i, 'imei'][5:9] == 'L660':
+                celltype=99 # 6060锂电芯
+            elif df_sn.loc[i, 'imei'][3:5] == 'LX' and df_sn.loc[i, 'imei'][5:9] == 'N750':    
+                celltype=3 #力信 50ah三元电芯
+            elif df_sn.loc[i, 'imei'][3:5] == 'CL' and df_sn.loc[i, 'imei'][5:9] == 'N750': 
+                celltype=4 #CATL 50ah三元电芯
+            elif 'TJMCL'in sn: # 金茂
+                celltype=100 #CATL 50ah三元电芯
+            else:
+                logger.info("pid-{} celltype-{} SN: {} SKIP!".format(os.getpid(), "未知", sn))
+                continue
+            sn = df_sn.loc[i, 'sn']
+            factory = df_sn.loc[i, 'factory']
+            logger.info("pid-{} celltype-{} SN: {} START!".format(os.getpid(), celltype, sn))
+            # sys.exit()
+            param=QX_BatteryParam.BatteryInfo(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']
+            df_bms=df_bms.dropna(subset=['总电流[A]'])
+            df_bms=df_bms.reset_index(drop=True)
+            # 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)
+
+
+            # with DBReadOpe as DBReadOpe:
+            df_OprtnSta=DBReadOpe.getdata('qrcode','status', tablename=tablename3, sn=sn, timename='', st='', sp='',factory='')#取最后一条运营状态
+            #电池诊断.....................................................................................................................................................................
+            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_update=batDiag.diag()
+                # 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_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)
+            # df_Diag_Ram.to_csv('df_Diag_Ram.csv')
+            # df_Diag_Ram_add.to_csv('df_Diag_Ram_add.csv')
+            # df_Diag_Ram_Update.to_csv('df_Diag_Ram_add.csv')
+            if len(df_Diag_Ram_add) > 0:#新增故障
+                
+                df_Diag_Ram_add.columns = ['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice', 'Batpos']
+                df_Diag_Ram_add['factory'] = factory
+                df_Diag_Ram_add['add_time'] = datetime.datetime.now()
+                df_Diag_Ram_add.to_sql("all_fault_info_copy",con=db_engine, if_exists="append",index=False)
+                df_Diag_Ram_add.to_csv(r'D:\Work\Code_write\data_analyze_platform\USER\01Screen_Problem\result_add.csv',index=False,encoding='GB18030')
+            if len(df_Diag_Ram_Update) > 0:#更改故障
+                df_Diag_Ram_Update = df_Diag_Ram_Update.reset_index(drop=True)
+                try:
+                    logger.info(df_Diag_Ram_Update)
+                    for i in range(0,len(df_Diag_Ram_Update)):
+                        sql = '''
+                                    update all_fault_info_copy set update_time='{}', end_time='{}', Batpos={} where product_id='{}' and code={} and start_time='{}'
+                                    '''.format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), df_Diag_Ram_Update.loc[i,'end_time'], df_Diag_Ram_Update.loc[i, 'Batpos'],
+                                               df_Diag_Ram_Update.loc[i,'product_id'], df_Diag_Ram_Update.loc[i,'code'], 
+                                               df_Diag_Ram_Update.loc[i,'start_time'])
+                        cursor_platform.execute(sql)
+                        conn_platform.commit()
+                except:
+                    logger.error(traceback.format_exc)
+                    logger.error(u"{} :{},{} 任务运行错误\n".format(sn,start_time,end_time), exc_info=True)
+
+                # df_Diag_Ram_Update_change.to_csv(r'D:\Work\Code_write\data_analyze_platform\USER\01Screen_Problemm\result_change.csv',index=False,encoding='GB18030')
+            end=time.time()
+            logger.info("pid-{} celltype-{} SN: {} DONE!".format(os.getpid(), celltype, 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)
+    
+    DBReadOpe.close()
+    DBRead.close()   
+    cursor_platform.close()
+    conn_platform.close()
+    db_engine.dispose()
+    logger.info(" diag_cal Done!")
+    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'])
+    #............................获取数据................................
+    toweek='Week'+time.strftime('%W')
+    host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port=3306
+    db='safety_platform'
+    user='qx_algo_rw'
+    password='qx@123456'
+    mode=2
+    tablename1='all_fault_info_copy'
+    
+    db_engine = create_engine(
+    "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+        user, parse.quote_plus(password), host, port, db
+    ))
+
+    try:
+        logger.info("DaTa_Sta_Week_Task START!")
+
+        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='',sn='',timename='',st='',sp='')#dbdownload经过了改编
+        for j in range(0,len(factory_info)):
+            df_fltinfo=df_fltinfo[df_fltinfo['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)
+            if not all_period_fault_info.empty:
+                all_period_fault_info.loc[j].to_sql('all_period_fault_info', db_engine, if_exists='append', index=False)
+            
+            logger.info("DaTa_Sta_Week_Task DONE!")
+    except Exception as e:
+        logger.error(traceback.format_exc)
+        logger.error(u" 任务运行错误\n".format(), exc_info=True)
+    finally:
+        db_engine.dispose()
+    task_on=0
+    # all_period_fault_info.to_csv('all_period_fault_info.csv')
+    
+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_rw'
+    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经过了改编
+        
+        
+    host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port=3306
+    db='safety_platform'
+    user='qx_algo_rw'
+    password='qx@123456'
+    mode=2
+    tablename1='all_fault_info_copy'
+    db_engine = create_engine(
+    "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+        user, parse.quote_plus(password), host, port, db
+    ))
+    DBRead=DBDw.DBDownload(host, port, db, user, password,mode)
+    
+        #............................获取时间................................
+    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','run_time_total','dischrg_total','odo_total','max_dischrg_one','max_runtime_one','max_cycle_one','max_odo_one','alarm_close_total','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)):
+        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']
+        # all_statistic_info.to_csv('all_statistic_info.csv')
+    #
+        try:
+            logger.info("DaTa_Sta_Minutes_Task START!")
+            # 更新统计信息
+            conn = pymysql.connect(host=host, port=port, user=user, password=password, database=db)
+            cursor = conn.cursor()
+            sql = ''' update all_statistic_info set update_time='{}', total_alarm={}, alarm_total_today={}, alarm_close_today={}, alarm_not_close_today={},
+                        alarm_uregent_total_today={}, alarm_uregent_close_today={},alarm_uregent_not_close_today={},run_time_total={},dischrg_total={},
+                        odo_total={},max_dischrg_one={},max_runtime_one={},max_cycle_one={},max_odo_one={},alarm_close_total={},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={}
+                        where factory='{}' 
+                        '''.format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), all_statistic_info.loc[j,'total_alarm'], all_statistic_info.loc[j,'alarm_total_today'],all_statistic_info.loc[j,'alarm_close_today'],
+                                all_statistic_info.loc[j,'alarm_not_close_today'],all_statistic_info.loc[j,'alarm_uregent_total_today'],all_statistic_info.loc[j,'alarm_uregent_close_today'],
+                                all_statistic_info.loc[j,'alarm_uregent_not_close_today'],all_statistic_info.loc[j,'run_time_total'],all_statistic_info.loc[j,'dischrg_total'],
+                                all_statistic_info.loc[j,'odo_total'],all_statistic_info.loc[j,'max_dischrg_one'],all_statistic_info.loc[j,'max_runtime_one'],
+                                all_statistic_info.loc[j,'max_cycle_one'],all_statistic_info.loc[j,'max_odo_one'],all_statistic_info.loc[j,'alarm_close_total'],
+                                all_statistic_info.loc[j,'alarm_total'],all_statistic_info.loc[j,'cell_type'],all_statistic_info.loc[j,'cell_type_count'],
+                                all_statistic_info.loc[j,'cell_safety_risk_count'],all_statistic_info.loc[j,'data_safety_risk_count'],all_statistic_info.loc[j,'status_safety_risk_count'],
+                                all_statistic_info.loc[j,'hv_safety_risk_count'],all_statistic_info.loc[j,'system_safety_risk_count'],all_statistic_info.loc[j,'sample_safety_risk_count'],
+                                    all_statistic_info.loc[j,'controller_safety_risk_count'],all_statistic_info.loc[j,'design_safety_risk_count'],all_statistic_info.loc[j,'factory'])
+            cursor.execute(sql)
+            conn.commit()
+            # 更新故障位置信息
+            if not all_location_info.empty:
+                sql = "delete from all_fault_location_info where factory='{}'".format(all_statistic_info.loc[j,'factory'])
+                cursor.execute(sql)
+                all_location_info.to_sql('all_fault_location_info', db_engine, if_exists='append', index=False)
+                conn.commit()
+            logger.info("DaTa_Sta_Minutes_Task DONE!")
+
+        except:
+            logger.error(traceback.format_exc)
+            logger.error(u"{},{} 任务运行错误\n".format(start_time,end_time), exc_info=True)
+        finally:
+            conn.close();
+            db_engine.dispose();
+    task_on=0
+
+
+if __name__ == "__main__":
+    
+    # # 时间设置
+
+    # now_time = datetime.datetime.now()
+    # pre_time = now_time + dateutil.relativedelta.relativedelta(hours=-1)#上小时时间
+    # end_time=datetime.datetime.strftime(now_time,"%Y-%m-%d %H:00:00")
+    # start_time=datetime.datetime.strftime(pre_time,"%Y-%m-%d %H:00:00")
+    
+    # history_run_flag = False # 历史数据运行标志
+    
+
+    # 更新sn列表
+    host='rm-bp10j10qy42bzy0q7.mysql.rds.aliyuncs.com'
+    port=3306
+    db='qixiang_oss'
+    user='qx_algo_rw'
+    password='qx@123456'
+    conn = pymysql.connect(host=host, port=port, user=user, password=password, database=db)
+    cursor = conn.cursor()
+    cursor.execute("select sn, imei, add_time from app_device where status in (1,2,3)")
+    res = cursor.fetchall()
+    df_sn = pd.DataFrame(res, columns=['sn', 'imei', 'add_time'])
+    df_sn = df_sn.reset_index(drop=True)
+    cursor.close()
+    conn.close();
+    df_sn['factory'] = '骑享'
+    
+    db = 'qx_cas'
+    conn = pymysql.connect(host=host, port=port, user=user, password=password, database=db)
+    cursor = conn.cursor()
+    cursor.execute("select sn, factory from sn_list")
+    res = cursor.fetchall()
+    df_sn2 = pd.DataFrame(res, columns=['sn', 'factory'])
+    df_sn2['imei'] = ""
+    df_sn = pd.concat([df_sn, df_sn2], axis=0)
+    df_sn = df_sn.reset_index(drop=True)
+
+    df_sn=df_sn[df_sn['sn']=='PK504B00100004103']
+    df_sn.reset_index(inplace=True,drop=True)
+    # # 数据库配置
+    # host = 'rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    # port = 3306
+    # user = 'qx_cas'
+    # password = parse.quote_plus('Qx@123456')
+    # database = 'qx_cas'
+
+    # db_engine = create_engine(
+    #     "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+    #         user, password, host, port, database
+    #     ))
+    # DbSession = sessionmaker(bind=db_engine)
+    
+    # # 运行历史数据配置
+    
+    # df_first_data_time = pd.read_sql("select * from bat_first_data_time", db_engine)
+
+    
+    # 日志配置
+    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='saftyCenter_DataSta&Static', 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)
+    logger = log.get_logger()
+
+    logger.info("pid is {}".format(os.getpid()))
+    
+    
+     # 算法参数
+    host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port=3306
+    db='safety_platform'
+    user='qx_algo_rw'
+    password=parse.quote_plus('qx@123456')
+    tablename='all_fault_info_copy'
+    db_engine = create_engine(
+        "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+            user, password, host, port, db
+        ))
+
+        
+    #............................模块运行前,先读取数据库中所有结束时间为0的数据,需要从数据库中读取................``
+    # print("select start_time, end_time, product_id, code, level, info, advice, factory from {}".format(tablename))
+    try:
+        df_Diag_Ram=pd.read_sql("select start_time, end_time, product_id, code, level, info, advice, Batpos from all_fault_info_copy", db_engine)
+    finally:
+        db_engine.dispose()
+    # result = result[['start_time', 'end_time', 'product_id', 'code', 'level', 'info', 'advice']]
+    # df_Diag_Ram=result[result['end_time']=='0000-00-00 00:00:00']
+
+    global task_on
+    
+    task_on=0
+    diag_cal()
+    # DaTa_Sta_Minutes_Task()
+    # DaTa_Sta_Week_Task()
+
+    #定时任务.......................................................................................................................................................................
+    scheduler = BlockingScheduler()
+    if task_on==0:
+        scheduler.add_job(diag_cal, 'interval', seconds=900, id='diag_job')
+        scheduler.add_job(DaTa_Sta_Week_Task, 'interval',start_date='2021-11-15 08:00:00', days=7, id='Week_Task')
+        scheduler.add_job(DaTa_Sta_Minutes_Task, 'interval', seconds=180, id='Minutes_Task')
+    try:  
+        scheduler.start()
+    except Exception as e:
+        scheduler.shutdown()
+        logger.error(str(e))
+
+    

+ 134 - 0
USER/SPF/01qixiang/05BatDiag/main_test.py

@@ -0,0 +1,134 @@
+import CBMSBatDiag
+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.V1_0_1 import DBDownload as DBDownload
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import log
+from pandas.core.frame import DataFrame
+from LIB.MIDDLE.SaftyCenter.Common import QX_BatteryParam
+from LIB.MIDDLE.SaftyCenter.Common import DBDownload as DBDw
+from DiagDataMerge import DiagDataMerge
+import pymysql
+
+#...................................电池包电芯安全诊断函数......................................................................................................................
+def diag_cal():
+    task_on=1
+    global SNnums
+    #..................................设置时间..........................................................  
+    start=time.time()
+    end_time=datetime.datetime.now()
+    start_time=end_time-datetime.timedelta(seconds=600)#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
+    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,Batpos'
+    tablename='all_fault_info'
+    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(','))
+    cursor.close()
+    mysql.close()
+
+
+    for sn in SNnums:#SN遍历
+        print(sn)
+        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)   
+        #读取原始数据库数据........................................................................................................................................................
+        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'       
+        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)
+
+
+        #电池诊断.....................................................................................................................................................................
+        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 = pd.concat([df_Diag_Ram,df_Diag_Ram_sn,df_Diag_Ram_sn]).drop_duplicates(subset=['product_id'],keep=False)#sn之外的故障
+        df_Diag_Ram_sn_else=df_Diag_Ram.drop(df_Diag_Ram[df_Diag_Ram.product_id==sn].index)#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)#学琦计算故障   
+            batDiag=CBMSBatDiag.BatDiag(sn,celltype,df_bms, df_soh, df_uniform, CellFltInfo)#鹏飞计算
+            df_Diag_Batdiag_update=batDiag.diag()
+            # df_Diag_Batdiag_update=BatDiag.diag() 
+            if not df_Diag_Batdiag_update.empty:
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\07BatSafetyWarning\电压排名.txt','a') as file:
+                    file.write(str(df_Diag_Batdiag_update)+'\n')
+        else:
+            pass
+
+#...............................................主函数.......................................................................................................................
+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=SNnums_L7255 + SNnums_C7255 + SNnums_6040 + SNnums_4840 + SNnums_U7255+ SNnums_6060
+    # SNnums=['MGMCLN750N215I005','PK504B10100004341','PK504B00100004172','MGMLXN750N2189014']
+    SNnums = ['MGMLXN750N218N004'] #SNnums_6040
+    
+    mylog=log.Mylog('log_diag.txt','error')
+    mylog.logcfg()
+
+    diag_cal()
+
+    #定时任务.......................................................................................................................................................................
+    scheduler = BlockingScheduler()
+    scheduler.add_job(diag_cal, 'interval', seconds=5, id='diag_job')
+
+    try:  
+        scheduler.start()
+    except Exception as e:
+        scheduler.shutdown()
+        print(repr(e))
+        mylog.logopt(e)

+ 262 - 0
USER/SPF/01qixiang/06BatSafetyAlarm/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
+        

BIN
USER/SPF/01qixiang/06BatSafetyAlarm/__pycache__/CBMSSafetyAlarm.cpython-38.pyc


+ 248 - 0
USER/SPF/01qixiang/06BatSafetyAlarm/deploy - 副本.py

@@ -0,0 +1,248 @@
+
+__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 traceback
+from LIB.MIDDLE.CellStateEstimation.Common import log
+import CBMSSafetyAlarm
+
+from LIB.MIDDLE.CellStateEstimation.Common import DBDownload
+from urllib import parse
+import pymysql
+import pdb
+from apscheduler.schedulers.blocking import BlockingScheduler
+import datacompy
+import logging
+import multiprocessing
+#...................................电池包电芯安全诊断函数......................................................................................................................
+def diag_cal(df_sn, df_bms_ram, log_name):
+    
+    # 日志
+    logger = logging.getLogger()
+    fh = logging.FileHandler(log_name + ".log", encoding="utf-8",mode="a")
+    formatter = logging.Formatter("%(asctime)s - %(name)s-%(levelname)s %(message)s")
+    fh.setFormatter(formatter)
+    logger.addHandler(fh)
+    logger.setLevel(logging.INFO)
+    logger.info("pid is {}".format(os.getpid()))
+    
+    # 读取结果数据库
+    host2='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port2=3306
+    db2='safety_platform'
+    user2='qx_algo_rw'
+    password2='qx@123456'
+    
+    db_res_engine = create_engine(
+        "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+            user2, parse.quote_plus(password2), host2, port2, db2
+        ))
+    
+    conn = pymysql.connect(host=host2, port=port2, user=user2, password=password2, database=db2)
+    cursor = conn.cursor()
+    
+    result=pd.read_sql("select start_time, end_time, product_id, code, level, info, advice from all_fault_info where factory = '{}'".format('骑享'), db_res_engine)
+    result = result[['start_time', 'end_time', 'product_id', 'code', 'level', 'info', 'advice']]
+    df_diag_ram=result[(result['end_time']=='0000-00-00 00:00:00') & (result['code']==119)]
+    df_bms_ram=pd.DataFrame(columns=['time', 'sn', 'packvolt', 'cellvolt', 'celltemp'])
+    
+    start=time.time()
+    now_time=datetime.datetime.now()
+    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')
+
+    for i in range(0, len(df_sn)):
+        try:
+            sn = df_sn.loc[i, 'sn']
+            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
+            
+            logger.info("pid-{} celltype-{} SN: {} START!".format(os.getpid(), celltype, sn))
+
+            #读取原始数据库数据........................................................................................................................................................
+            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]
+            if df_diag_ram_sn.empty:   
+                if not df_bms.empty:
+                    SafetyAlarm=CBMSSafetyAlarm.SafetyAlarm(sn,celltype,df_bms, df_bms_ram_sn)
+                    df_diag_res, df_bms_res=SafetyAlarm.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_diag_ram.loc[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_res)
+                    # df_diag_ram.reset_index(inplace=True,drop=True)     #重置索引
+
+                    #当前热失控故障写入数据库
+                    if not df_diag_res.empty:
+                        df_diag_res.columns = ['start_time', 'end_time', 'product_id', 'code', 'level', 'info', 'advice']
+                        df_diag_res['add_time'] = datetime.datetime.now()
+                        df_diag_res['factory'] = '骑享'
+                        df_diag_res.to_sql("all_fault_info",con=db_res_engine, if_exists="append",index=False)
+
+                    
+                #当前热失控已超过1天变为历史故障并写入数据库,并删除原有数据库中的当前故障和ram中的当前故障
+            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
+                
+                    try:
+
+                        cursor.execute('''
+                                    update all_fault_info set update_time='{}',end_time='{}', Batpos={} where product_id='{}' and end_time='0000-00-00 00:00:00' and code={} and factory='骑享'
+                                    '''.format(datetime.datetime.now(), end_time, 1,sn, 119))
+                        conn.commit()
+                        conn.close();
+                    except:
+                        logger.error(traceback.format_exc)
+                        logger.error(u"{} :{},{} 任务运行错误\n".format(sn,start_time,end_time), exc_info=True)
+                        conn.close();
+        except:
+            logger.error(traceback.format_exc)
+            logger.error(u"{} :{},{} 任务运行错误\n".format(sn,start_time,end_time), exc_info=True)
+    cursor.close()
+    conn.close()
+    db_res_engine.dispose()
+    logger.info("pid-{} Done!".format(os.getpid()))
+
+#...................................................主进程...........................................................................................................
+def mainprocess():
+    global df_bms_ram
+    global log_path
+    # 更新sn列表
+    # host='rm-bp10j10qy42bzy0q7.mysql.rds.aliyuncs.com'
+    # port=3306
+    # db='qixiang_oss'
+    # user='qixiang_oss'
+    # password='Qixiang2021'
+    # conn = pymysql.connect(host=host, port=port, user=user, password=password, database=db)
+    # cursor = conn.cursor()
+    # cursor.execute("select sn, imei, add_time from app_device")
+    # res = cursor.fetchall()
+    # df_sn = pd.DataFrame(res, columns=['sn', 'imei', 'add_time'])
+    # df_sn = df_sn.reset_index(drop=True)
+    # cursor.close()
+    # conn.close();
+    df_sn=pd.DataFrame(columns=['sn'])
+    df_sn['sn']=['PK504B10100004434', 'MGMCLN750N215N205', 'PK504B10100004447', 'PK504B10100004379', 'PK504B10100004483']
+    process = 2
+    pool = multiprocessing.Pool(processes = process)
+
+    for i in range(process):
+        sn_list = df_sn[int(len(df_sn)*i/process):int(len(df_sn)*(i+1)/process)]
+        sn_list = sn_list.reset_index(drop=True)
+        log_name = log_path + '/log_' + str(i)
+        pool.apply_async(diag_cal, (sn_list,df_bms_ram,log_name))
+
+    pool.close()
+    pool.join()
+if __name__ == "__main__":
+    
+    # 时间设置
+    # now_time = datetime.datetime.now()
+    # pre_time = now_time + dateutil.relativedelta.relativedelta(days=-1)# 前一日
+    # end_time=datetime.datetime.strftime(now_time,"%Y-%m-%d 00:00:00")
+    # start_time=datetime.datetime.strftime(pre_time,"%Y-%m-%d 00:00:00")
+    
+    history_run_flag = False # 历史数据运行标志
+    
+
+    # # 更新sn列表
+    # host='rm-bp10j10qy42bzy0q7.mysql.rds.aliyuncs.com'
+    # port=3306
+    # db='qixiang_oss'
+    # user='qixiang_oss'
+    # password='Qixiang2021'
+    # conn = pymysql.connect(host=host, port=port, user=user, password=password, database=db)
+    # cursor = conn.cursor()
+    # cursor.execute("select sn, imei, add_time from app_device")
+    # res = cursor.fetchall()
+    # df_sn = pd.DataFrame(res, columns=['sn', 'imei', 'add_time'])
+    # df_sn = df_sn.reset_index(drop=True)
+    # conn.close();
+    
+    # 数据库配置
+    host = 'rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port = 3306
+    user = 'qx_cas'
+    password = parse.quote_plus('Qx@123456')
+    database = 'qx_cas'
+
+    db_engine = create_engine(
+        "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+            user, password, host, port, database
+        ))
+    DbSession = sessionmaker(bind=db_engine)
+    
+    # 运行历史数据配置
+    
+    df_first_data_time = pd.read_sql("select * from bat_first_data_time", db_engine)
+
+    
+    # 日志配置
+    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='batsafetyAlarm', 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)
+    logger = log.get_logger()
+
+    logger.info("pid is {}".format(os.getpid()))
+    
+    # 算法参数
+    host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port=3306
+    db='safety_platform'
+    user='qx_read'
+    password=parse.quote_plus('Qx@123456')
+    tablename='all_fault_info'
+    db_res_engine = create_engine(
+        "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+            user, password, host, port, db
+        ))
+
+        
+    #ram参数初始化........................................................................................................
+    df_bms_ram=pd.DataFrame(columns=['time', 'sn', 'packvolt', 'cellvolt', 'celltemp'])
+
+    #定时任务.......................................................................................................................................................................
+    scheduler = BlockingScheduler()
+    scheduler.add_job(mainprocess, 'interval', seconds=60, id='diag_job')
+
+    try:  
+        scheduler.start()
+    except Exception as e:
+        scheduler.shutdown()
+        logger.error(str(e))
+    

+ 161 - 0
USER/SPF/01qixiang/06BatSafetyAlarm/main copy 2.py

@@ -0,0 +1,161 @@
+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)
+    now_time='2021-10-31 09:31:20'
+    now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+    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=[['MGMCLN750N215N049','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=['time','sn','safetywarning'])
+    df_alarm_ram2=pd.DataFrame(columns=['time','sn','safetywarning'])
+
+    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)

+ 161 - 0
USER/SPF/01qixiang/06BatSafetyAlarm/main.py

@@ -0,0 +1,161 @@
+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,now_time):
+
+    start=time.time()
+    end_time=now_time
+    start_time=now_time-datetime.timedelta(seconds=60)
+    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:\Develop\User\Songpengfei\data_analyze_platform\WORK\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:\Develop\User\Songpengfei\data_analyze_platform\WORK\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, now_time
+    now_time=now_time+datetime.timedelta(seconds=60)
+    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,now_time)).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:\Develop\User\Songpengfei\data_analyze_platform\WORK\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=[['PK504B10100004487'], []]
+    now_time=datetime.datetime.strptime('2022-5-24 19:49:00','%Y-%m-%d %H:%M:%S')
+    
+    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=0.1, id='diag_job')
+
+    try:  
+        scheduler.start()
+    except Exception as e:
+        scheduler.shutdown()
+        print(repr(e))
+        mylog.logopt(e)

+ 906 - 0
USER/SPF/01qixiang/07BatSafetyWarning copy/CBMSBatInterShort.py

@@ -0,0 +1,906 @@
+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:
+                                firsttime2=0
+                                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
USER/SPF/01qixiang/07BatSafetyWarning copy/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

+ 265 - 0
USER/SPF/01qixiang/07BatSafetyWarning copy/CBMSSafetyWarning.py

@@ -0,0 +1,265 @@
+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,df_fault_ram_sn):  #参数初始化
+
+        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
+        self.df_alarm_ram=df_fault_ram_sn.copy()
+    
+    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
+        cellshortfault=[]
+        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.append(1)
+                            else:
+                                cellshortfault.append(0)                    
+                        else:
+                            cellshortfault.append(0)  
+                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=13)
+                plt.title(self.sn)
+                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)
+        
+        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_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
+        
+        #漏电流热失控预警确认.......................................................................................
+        if len(cellshortfault)>1:
+            if not 'C490' in list(self.df_alarm_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if max(cellshortfault)==1:
+                    faultcode='C490'
+                    faultlv=4
+                    faultinfo='电芯{}发生热失控安全预警'.format(cellshortfault.index(1)+1)
+                    faultadvice='请于24h内联系技术人员确认故障'
+                    self.df_alarm_ram.loc[len(self.df_alarm_ram)]=[time_now, time_sp, self.sn, faultcode, faultlv, faultinfo, faultadvice]
+                else:
+                    pass
+            else:
+                if max(cellshortfault)==1:
+                    pass
+                else:
+                    self.df_alarm_ram.loc[self.df_alarm_ram[self.df_alarm_ram['code']=='C490'].index, 'end_time'] = time_now
+        else:
+            pass
+        #mana-kendall趋势检测
+        mk_fault_list=[]
+        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_s_list[i]<self.param.mk_s and mk_svar_list[i]<self.param.mk_svar and mk_slope_3sigma[i]<-3 and mk_slope_list[i]<self.param.mk_slope and volt_rate_3sigma[i]<-3:
+                mk_fault_list.append(1)
+            #适用静态工况判断
+            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_s_list[i]<self.param.mk_s and mk_svar_list[i]<self.param.mk_svar and mk_slope_3sigma[i]<-3.5 and mk_slope_list[i]<-0.03 and volt_rate_3sigma[i]<-3:
+                mk_fault_list.append(1)
+            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.95 or mk_z_3sigma[i]<-3) and mk_s_list[i]<self.param.mk_s and mk_svar_list[i]<self.param.mk_svar and mk_slope_3sigma[i]<-3.5 and mk_slope_list[i]<-0.4 and volt_rate_3sigma[i]<-3:
+                mk_fault_list.append(1)
+            else:
+                mk_fault_list.append(0)
+        
+        if len(mk_fault_list)>1:
+            if not 'C491' in list(self.df_alarm_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if max(mk_fault_list)==1:
+                    faultcode='C491'
+                    faultlv=4
+                    faultinfo='电芯{}发生热失控安全预警'.format(mk_fault_list.index(1)+1)
+                    faultadvice='请于24h内联系技术人员确认故障'
+                    self.df_alarm_ram.loc[len(self.df_alarm_ram)]=[time_now, time_sp, self.sn, faultcode, faultlv, faultinfo, faultadvice]
+                else:
+                    pass
+            else:
+                if max(mk_fault_list)==1:
+                    pass
+                else:
+                    self.df_alarm_ram.loc[self.df_alarm_ram[self.df_alarm_ram['code']=='C491'].index, 'end_time'] = time_now
+        else:
+            pass
+        return self.df_alarm_ram

+ 266 - 0
USER/SPF/01qixiang/07BatSafetyWarning copy/VoltStray.py

@@ -0,0 +1,266 @@
+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,df_soh,celltype,df_last):
+    df_ram=pd.DataFrame(columns=['sn','time4','cellsoc'])
+    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_ram=pd.DataFrame(columns=['sn','time4','cellsoc'])
+        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)
+        if (not df_soh.empty) and len(df_soc)>1:
+            if (not df_last.empty) and (time_list1[0]-df_last.loc[0,'time4']).total_seconds()<12*3600:
+                df_delt_soc1=df_soc-df_last.loc[0,'cellsoc']
+                cellsoh=eval(df_soh.loc[0,'cellsoh'])
+                df_delt_soc2=df_delt_soc1*np.array(cellsoh)/100
+                df_delt_soc=df_delt_soc2-df_delt_soc1
+                df_soc=df_soc+df_delt_soc
+            else:
+                df_delt_soc1=df_soc-df_soc.iloc[0]
+                cellsoh=eval(df_soh.loc[0,'cellsoh'])
+                df_delt_soc2=df_delt_soc1*np.array(cellsoh)/100
+                df_delt_soc=df_delt_soc2-df_delt_soc1
+                df_soc=df_soc+df_delt_soc
+            df_ram.loc[0]=[sn,df_soc.index[-1],list(df_soc.iloc[-1])]
+        else:
+            df_ram=df_last
+
+        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)):
+            if max(VolOl_Uni_result[i])>3 and min(VolOl_Uni_result[i])<-3:
+                pass
+            else:
+                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,df_ram)
+    # 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

+ 279 - 0
USER/SPF/01qixiang/07BatSafetyWarning copy/main.py

@@ -0,0 +1,279 @@
+import pandas as pd
+import pymysql
+from LIB.BACKEND import DBManager, Log
+from apscheduler.schedulers.blocking import BlockingScheduler
+import time, datetime
+from sqlalchemy import create_engine
+from urllib import parse
+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_warning_ram4
+    global df_lfp_ram
+    global df_lfp_ram1
+    global now_time
+
+    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')
+
+    now_time=now_time+datetime.timedelta(hours=6)
+    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_algo_rw'
+    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_copy'
+    sql =  "select %s from %s where end_time='0000-00-00 00:00:00' and level=4" %(param,tablename)
+    cursor.execute(sql)
+    res = cursor.fetchall()
+    df_fault_ram= pd.DataFrame(res,columns=param.split(','))
+    
+    db_res_engine1 = create_engine(
+        "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+            user, parse.quote_plus(password), host, port, db
+        ))
+    
+    db='qx_cas'
+    db_res_engine2 = create_engine(
+        "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+            user, parse.quote_plus(password), host, port, db
+        ))
+
+    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三元电芯
+            elif 'TJMCL'in sn: 
+                celltype=100 #重卡换电
+            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']
+            df_bms=df_bms.dropna(axis=0,subset=['总电流[A]','SOH[%]','SOC[%]']) #去除有空值的行
+            df_bms.reset_index(inplace=True,drop=True)     #重置索引
+            # print(df_bms)
+            # df_bms.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\98Download\\'+sn+'_BMS_'+'.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)
+
+            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_sn4=df_warning_ram4[df_warning_ram4['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)     #重置索引
+                df_warning_ram_sn4.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_res5=BatShort.intershort() 
+                if not df_short_res.empty:
+                    df_short_res.to_sql("cellstateestimation_intershort",con=db_res_engine2, if_exists="append",index=False)
+                
+                #静置电压排名..................................................................................................................................................
+                BatUniform=CBMSBatUniform.BatUniform(sn,celltype,df_bms,df_uniform,df_ram_res3,df_lfp_ram_sn1)
+                df_rank_res, df_ram_res3, df_ram_res6=BatUniform.batuniform()
+                if not df_rank_res.empty:
+                    df_uniform=df_rank_res
+                    df_uniform.to_sql("cellstateestimation_uniform_socvoltdiff",con=db_res_engine2, if_exists="append",index=False)
+                
+                #电压离群.....................................................................................................................................................
+                df_voltsigma,df_ram_res4=VoltStray.main(sn,df_bms,df_soh,celltype,df_warning_ram_sn4)
+                if not df_voltsigma.empty:
+                    df_voltsigma.to_sql("outlier_voltchangeratio",con=db_res_engine2, if_exists="append",index=False)
+
+                #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_ram4=df_warning_ram4.drop(df_warning_ram4[df_warning_ram4.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)
+                df_warning_ram4=pd.concat([df_warning_ram4,df_ram_res4],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_res5],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_res6],ignore_index=True)
+            
+            else:
+                df_voltsigma=pd.DataFrame()
+                
+
+            #电池热安全预警..............................................................................................................................................................
+            #读取内短路、析锂和一致性结果数据库数据
+            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_liplated=DBRead.getdata('time,sn,liplated,liplated_amount', tablename=tablename2, sn=sn, timename='time', st=start_time2, 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)
+                df_uniform=df_uniform.dropna(axis=0,how='any')
+            
+            #获取sn的故障RAM
+            df_fault_ram_sn=df_fault_ram[df_fault_ram['product_id']==sn]
+            df_fault_ram_sn.reset_index(inplace=True,drop=True)  #重置索引
+            
+            #热安全预警
+            BatWarning=CBMSSafetyWarning.SafetyWarning(sn,celltype,df_short,df_uniform,df_voltsigma,df_soh,df_fault_ram_sn)
+            df_warning_res=BatWarning.diag()
+            
+            if not df_warning_res.empty:
+                df_warning_new = pd.concat([df_warning_res,df_fault_ram_sn,df_fault_ram_sn]).drop_duplicates(subset=['start_time','code'],keep=False)#此次判断中新增故障
+                df_warning_new.reset_index(inplace=True,drop=True)  #重置索引
+                df_warning_end=pd.concat([df_warning_res,df_warning_new,df_warning_new]).drop_duplicates(subset=['start_time','code'],keep=False)#此次判断中新增故障
+                df_warning_end=df_warning_end[df_warning_end['end_time'] != '0000-00-00 00:00:00']
+                df_warning_end.reset_index(inplace=True,drop=True)  #重置索引
+            
+                #新增热失控预警写入数据库
+                if not df_warning_new.empty:
+                    df_warning_new.to_sql("all_fault_info_copy",con=db_res_engine1, if_exists="append",index=False)
+                        
+                #结束热失控预警更新数据库
+                if not df_warning_end.empty:
+                    for j in range(len(df_warning_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_warning_end.loc[j,'end_time'], 1 ,sn, df_warning_end.loc[j,'code']))
+                        mysql.commit()
+        
+            end=time.time()
+            print(end-start)
+        except Exception as e:
+            print(repr(e))
+            mylog.logopt(sn+str(e))
+    
+    mysql.close()
+    cursor.close()
+
+#...............................................主函数起定时作用.......................................................................................................................
+if __name__ == "__main__":
+    
+    excelpath=r'D:\Develop\User\Songpengfei\data_analyze_platform\WORK\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_6040 + SNnums_4840 + SNnums_L7255 + SNnums_C7255 + SNnums_U7255
+    SNnums=['MGMCLN750N215N044']
+    
+    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_warning_ram4=pd.DataFrame(columns=['sn','time4','cellsoc'])
+    df_lfp_ram=pd.DataFrame()
+    df_lfp_ram1=pd.DataFrame()
+
+    now_time='2022-2-15 10:00:56'
+    now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+
+    #定时任务.......................................................................................................................................................................
+    # saftywarning_cal()
+    scheduler = BlockingScheduler()
+    scheduler.add_job(saftywarning_cal, 'interval', seconds=6)
+
+    try:  
+        scheduler.start()
+    except Exception as e:
+        scheduler.shutdown()
+        print(repr(e))
+        mylog.logopt(e)

+ 906 - 0
USER/SPF/01qixiang/07BatSafetyWarning/CBMSBatInterShort.py

@@ -0,0 +1,906 @@
+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:
+                                firsttime2=0
+                                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
USER/SPF/01qixiang/07BatSafetyWarning/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

+ 265 - 0
USER/SPF/01qixiang/07BatSafetyWarning/CBMSSafetyWarning.py

@@ -0,0 +1,265 @@
+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,df_fault_ram_sn):  #参数初始化
+
+        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
+        self.df_alarm_ram=df_fault_ram_sn.copy()
+    
+    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
+        cellshortfault=[]
+        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.append(1)
+                            else:
+                                cellshortfault.append(0)                    
+                        else:
+                            cellshortfault.append(0)  
+                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=13)
+                # plt.title(self.sn)
+                # 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)
+        
+        # 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_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
+        
+        #漏电流热失控预警确认.......................................................................................
+        if len(cellshortfault)>1:
+            if not 'C490' in list(self.df_alarm_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if max(cellshortfault)==1:
+                    faultcode='C490'
+                    faultlv=4
+                    faultinfo='电芯{}发生热失控安全预警'.format(cellshortfault.index(1)+1)
+                    faultadvice='请于24h内联系技术人员确认故障'
+                    self.df_alarm_ram.loc[len(self.df_alarm_ram)]=[time_now, time_sp, self.sn, faultcode, faultlv, faultinfo, faultadvice]
+                else:
+                    pass
+            else:
+                if max(cellshortfault)==1:
+                    pass
+                else:
+                    self.df_alarm_ram.loc[self.df_alarm_ram[self.df_alarm_ram['code']=='C490'].index, 'end_time'] = time_now
+        else:
+            pass
+        #mana-kendall趋势检测
+        mk_fault_list=[]
+        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_s_list[i]<self.param.mk_s and mk_svar_list[i]<self.param.mk_svar and mk_slope_3sigma[i]<-3 and mk_slope_list[i]<self.param.mk_slope and volt_rate_3sigma[i]<-3:
+                mk_fault_list.append(1)
+            #适用静态工况判断
+            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_s_list[i]<self.param.mk_s and mk_svar_list[i]<self.param.mk_svar and mk_slope_3sigma[i]<-3.5 and mk_slope_list[i]<-0.03 and volt_rate_3sigma[i]<-3:
+                mk_fault_list.append(1)
+            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.95 or mk_z_3sigma[i]<-3) and mk_s_list[i]<self.param.mk_s and mk_svar_list[i]<self.param.mk_svar and mk_slope_3sigma[i]<-3.5 and mk_slope_list[i]<-0.4 and volt_rate_3sigma[i]<-3:
+                mk_fault_list.append(1)
+            else:
+                mk_fault_list.append(0)
+        
+        if len(mk_fault_list)>1:
+            if not 'C491' in list(self.df_alarm_ram['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if max(mk_fault_list)==1:
+                    faultcode='C491'
+                    faultlv=4
+                    faultinfo='电芯{}发生热失控安全预警'.format(mk_fault_list.index(1)+1)
+                    faultadvice='请于24h内联系技术人员确认故障'
+                    self.df_alarm_ram.loc[len(self.df_alarm_ram)]=[time_now, time_sp, self.sn, faultcode, faultlv, faultinfo, faultadvice]
+                else:
+                    pass
+            else:
+                if max(mk_fault_list)==1:
+                    pass
+                else:
+                    self.df_alarm_ram.loc[self.df_alarm_ram[self.df_alarm_ram['code']=='C491'].index, 'end_time'] = time_now
+        else:
+            pass
+        return self.df_alarm_ram

+ 266 - 0
USER/SPF/01qixiang/07BatSafetyWarning/VoltStray.py

@@ -0,0 +1,266 @@
+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,df_soh,celltype,df_last):
+    df_ram=pd.DataFrame(columns=['sn','time4','cellsoc'])
+    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_ram=pd.DataFrame(columns=['sn','time4','cellsoc'])
+        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)
+        if (not df_soh.empty) and len(df_soc)>1:
+            if (not df_last.empty) and (time_list1[0]-df_last.loc[0,'time4']).total_seconds()<12*3600:
+                df_delt_soc1=df_soc-df_last.loc[0,'cellsoc']
+                cellsoh=eval(df_soh.loc[0,'cellsoh'])
+                df_delt_soc2=df_delt_soc1*np.array(cellsoh)/100
+                df_delt_soc=df_delt_soc2-df_delt_soc1
+                df_soc=df_soc+df_delt_soc
+            else:
+                df_delt_soc1=df_soc-df_soc.iloc[0]
+                cellsoh=eval(df_soh.loc[0,'cellsoh'])
+                df_delt_soc2=df_delt_soc1*np.array(cellsoh)/100
+                df_delt_soc=df_delt_soc2-df_delt_soc1
+                df_soc=df_soc+df_delt_soc
+            df_ram.loc[0]=[sn,df_soc.index[-1],list(df_soc.iloc[-1])]
+        else:
+            df_ram=df_last
+
+        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)):
+            if max(VolOl_Uni_result[i])>3 and min(VolOl_Uni_result[i])<-3:
+                pass
+            else:
+                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,df_ram)
+    # 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

+ 279 - 0
USER/SPF/01qixiang/07BatSafetyWarning/main.py

@@ -0,0 +1,279 @@
+import pandas as pd
+import pymysql
+from LIB.BACKEND import DBManager, Log
+from apscheduler.schedulers.blocking import BlockingScheduler
+import time, datetime
+from sqlalchemy import create_engine
+from urllib import parse
+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_warning_ram4
+    global df_lfp_ram
+    global df_lfp_ram1
+    global now_time
+
+    # 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')
+
+    # now_time=now_time+datetime.timedelta(hours=6)
+    # 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_algo_rw'
+    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_copy'
+    sql =  "select %s from %s where end_time='0000-00-00 00:00:00' and level=4" %(param,tablename)
+    cursor.execute(sql)
+    res = cursor.fetchall()
+    df_fault_ram= pd.DataFrame(res,columns=param.split(','))
+    
+    db_res_engine1 = create_engine(
+        "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+            user, parse.quote_plus(password), host, port, db
+        ))
+    
+    db='qx_cas'
+    db_res_engine2 = create_engine(
+        "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+            user, parse.quote_plus(password), host, port, db
+        ))
+
+    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三元电芯
+            elif 'TJMCL'in sn: 
+                celltype=100 #重卡换电
+            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']
+            df_bms=df_bms.dropna(axis=0,subset=['总电流[A]','SOH[%]','SOC[%]']) #去除有空值的行
+            df_bms.reset_index(inplace=True,drop=True)     #重置索引
+            # print(df_bms)
+            # df_bms.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\98Download\\'+sn+'_BMS_'+'.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)
+
+            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_sn4=df_warning_ram4[df_warning_ram4['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)     #重置索引
+                df_warning_ram_sn4.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_res5=BatShort.intershort() 
+                if not df_short_res.empty:
+                    df_short_res.to_sql("cellstateestimation_intershort",con=db_res_engine2, if_exists="append",index=False)
+                
+                #静置电压排名..................................................................................................................................................
+                BatUniform=CBMSBatUniform.BatUniform(sn,celltype,df_bms,df_uniform,df_ram_res3,df_lfp_ram_sn1)
+                df_rank_res, df_ram_res3, df_ram_res6=BatUniform.batuniform()
+                if not df_rank_res.empty:
+                    df_uniform=df_rank_res
+                    df_uniform.to_sql("cellstateestimation_uniform_socvoltdiff",con=db_res_engine2, if_exists="append",index=False)
+                
+                #电压离群.....................................................................................................................................................
+                df_voltsigma,df_ram_res4=VoltStray.main(sn,df_bms,df_soh,celltype,df_warning_ram_sn4)
+                if not df_voltsigma.empty:
+                    df_voltsigma.to_sql("outlier_voltchangeratio",con=db_res_engine2, if_exists="append",index=False)
+
+                #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_ram4=df_warning_ram4.drop(df_warning_ram4[df_warning_ram4.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)
+                df_warning_ram4=pd.concat([df_warning_ram4,df_ram_res4],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_res5],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_res6],ignore_index=True)
+            
+            else:
+                df_voltsigma=pd.DataFrame()
+                
+
+            #电池热安全预警..............................................................................................................................................................
+            #读取内短路、析锂和一致性结果数据库数据
+            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_liplated=DBRead.getdata('time,sn,liplated,liplated_amount', tablename=tablename2, sn=sn, timename='time', st=start_time2, 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)
+                df_uniform=df_uniform.dropna(axis=0,how='any')
+            
+            #获取sn的故障RAM
+            df_fault_ram_sn=df_fault_ram[df_fault_ram['product_id']==sn]
+            df_fault_ram_sn.reset_index(inplace=True,drop=True)  #重置索引
+            
+            #热安全预警
+            BatWarning=CBMSSafetyWarning.SafetyWarning(sn,celltype,df_short,df_uniform,df_voltsigma,df_soh,df_fault_ram_sn)
+            df_warning_res=BatWarning.diag()
+            
+            if not df_warning_res.empty:
+                df_warning_new = pd.concat([df_warning_res,df_fault_ram_sn,df_fault_ram_sn]).drop_duplicates(subset=['start_time','code'],keep=False)#此次判断中新增故障
+                df_warning_new.reset_index(inplace=True,drop=True)  #重置索引
+                df_warning_end=pd.concat([df_warning_res,df_warning_new,df_warning_new]).drop_duplicates(subset=['start_time','code'],keep=False)#此次判断中新增故障
+                df_warning_end=df_warning_end[df_warning_end['end_time'] != '0000-00-00 00:00:00']
+                df_warning_end.reset_index(inplace=True,drop=True)  #重置索引
+            
+                #新增热失控预警写入数据库
+                if not df_warning_new.empty:
+                    df_warning_new.to_sql("all_fault_info_copy",con=db_res_engine1, if_exists="append",index=False)
+                        
+                #结束热失控预警更新数据库
+                if not df_warning_end.empty:
+                    for j in range(len(df_warning_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_warning_end.loc[j,'end_time'], 1 ,sn, df_warning_end.loc[j,'code']))
+                        mysql.commit()
+        
+            end=time.time()
+            print(end-start)
+        except Exception as e:
+            print(repr(e))
+            mylog.logopt(sn+str(e))
+    
+    mysql.close()
+    cursor.close()
+
+#...............................................主函数起定时作用.......................................................................................................................
+if __name__ == "__main__":
+    
+    excelpath=r'D:\Develop\User\Songpengfei\data_analyze_platform\WORK\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_6040 + SNnums_4840 + SNnums_L7255 + SNnums_C7255 + SNnums_U7255
+    # SNnums=['MGMCLN750N215N044']
+    
+    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_warning_ram4=pd.DataFrame(columns=['sn','time4','cellsoc'])
+    df_lfp_ram=pd.DataFrame()
+    df_lfp_ram1=pd.DataFrame()
+
+    now_time='2022-2-15 10:00:56'
+    now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+
+    #定时任务.......................................................................................................................................................................
+    saftywarning_cal()
+    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)

+ 699 - 0
USER/SPF/01qixiang/08BatQuality/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
+

+ 165 - 0
USER/SPF/01qixiang/08BatQuality/main.py

@@ -0,0 +1,165 @@
+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:
+                celltype=100
+                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[%]']) #去除有空值的行
+                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:\Develop\User\Songpengfei\data_analyze_platform\WORK\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=['TJMCL120502305010']
+    
+    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)

+ 145 - 0
USER/SPF/01qixiang/09Behave/BehaveStatistics.py

@@ -0,0 +1,145 @@
+import CBMSBatBehave
+import pandas as pd
+import time, datetime
+from matplotlib import pyplot as plt
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import log
+
+#...............................................主函数..........................................................................................................
+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')
+    SNdata_ZKHD=pd.read_excel(excelpath, sheet_name='重卡换电')
+    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_ZKHD=SNdata_ZKHD['SN号'].tolist()
+    SNnums=[SNnums_L7255 + SNnums_C7255 + SNnums_U7255 + SNnums_4840, SNnums_6040 + SNnums_6060]
+    SNnums=SNnums_ZKHD
+
+    df_res=pd.read_excel(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\99Result\\'+'CBMS_'+'行为分析'+'.xlsx',index_col=0)
+    df_res['time_st']=pd.to_datetime(df_res['time_st'], format='%Y-%m-%d %H:%M:%S')
+    
+    ax=plt.figure()
+    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三元电芯
+        elif 'TJMCL' in sn:
+            celltype=100 #金茂重卡换电
+        else:
+            print('SN:{},未找到对应电池类型!!!'.format(sn))
+            continue
+            # sys.exit()
+
+        #统计每个电池包每天的换电次数.......................................................................................................................................................
+        df_res_sn=df_res[df_res['sn']==sn]
+        if not df_res_sn.empty:
+            df= df_res_sn.set_index('time_st')
+            df['sum']=1
+            df=df.resample('D').sum()
+
+            x=df.index.tolist()
+            y=df['sum'].tolist()
+            
+            plt.plot(x, y,label=sn,marker='*',markersize=15)
+            plt.title('每个电池包日换电次数',fontsize=18)
+            plt.xlabel('日期', fontsize=15)
+            plt.ylabel('换电次数', fontsize=15)
+            plt.xticks(pd.date_range(x[0],x[-1],freq='3d'),fontsize=13,rotation=45)
+            plt.yticks(fontsize=13)
+            # plt.scatter( xtime1,VoltChange[volt_column[i]],marker='o')
+            plt.legend()
+            plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
+            plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 
+    # ax.savefig(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\99Result\每个电池包日换电次数.png')
+    plt.show()
+
+    #统计所有电池包每天的换电次数........................................................................................................................................
+    df1= df_res.set_index('time_st')
+    df1['sum']=1
+    df1=df1.resample('D').sum()
+    x=df1.index.tolist()
+    y=df1['sum'].tolist()
+    
+    ax1=plt.figure()
+    plt.bar(x,y)
+    plt.title('所有电池包日换电次数',fontsize=18)
+    plt.xlabel('日期', fontsize=15)
+    plt.ylabel('换电次数', fontsize=15)
+    plt.xticks(pd.date_range(x[0],x[-1],freq='3d'),fontsize=13,rotation=45)
+    plt.yticks(fontsize=13)
+    # plt.scatter( xtime1,VoltChange[volt_column[i]],marker='o')
+    plt.legend(bbox_to_anchor=(1, 0), loc=3, fontsize=16)
+    plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
+    plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 
+    for a,b in zip(x,y):
+        plt.text(a, b+0.05, '%.0f' % b, ha='center', va= 'bottom',fontsize=12)
+    # ax1.savefig(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\99Result\所有电池包日换电次数.png')
+    plt.show()
+
+    #统计所有电池包每周的平均换电次数........................................................................................................................................
+    df1= df_res.set_index('time_st')
+    df1['sum']=1
+    df1=df1.resample('D').sum()
+    x=df1.index.tolist()
+    y=(df1['sum']/8).tolist()
+    
+    ax1=plt.figure()
+    plt.plot(x,y,marker='*',markersize=15)
+    plt.title('每个电池包日平均换电次数',fontsize=18)
+    plt.xlabel('日期', fontsize=15)
+    plt.ylabel('换电次数', fontsize=15)
+    plt.xticks(pd.date_range(x[0],x[-1],freq='3d'),fontsize=13,rotation=45)
+    plt.yticks(fontsize=13)
+    # plt.scatter( xtime1,VoltChange[volt_column[i]],marker='o')
+    plt.legend(bbox_to_anchor=(1, 0), loc=3, fontsize=16)
+    plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
+    plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 
+    for a,b in zip(x,y):
+        plt.text(a, b+0.05, '%.1f' % b, ha='center', va= 'bottom',fontsize=12)
+    # ax1.savefig(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\99Result\每个电池包日平均换电次数.png')
+    plt.show()
+
+    #统计每天的换电时间段...........................................................................................................................
+    df_res['time_st']=df_res['time_st'].map(lambda x:x.strftime('%H:%M:%S'))
+    # df_res['time_st']=pd.to_datetime(df_res['time_st'], format='%H:%M:%S')
+    df_res['time_st']=df_res['time_st'].map(lambda x:datetime.datetime.strptime(x,'%H:%M:%S'))
+    df1= df_res.set_index('time_st')
+    df1['sum']=1
+    df1=df1.resample('H').sum()
+    x=range(len(df1))
+    y=(df1['sum']).tolist()
+    
+    ax1=plt.figure()
+    plt.bar(x,y)
+    plt.plot(x,y,marker='*',markersize=13,color='r')
+    plt.title('电池包换电时间段',fontsize=18)
+    plt.xlabel('时间', fontsize=15)
+    plt.ylabel('换电次数', fontsize=15)
+    plt.xticks(range(0,len(df1),1),fontsize=15)
+    plt.yticks(fontsize=15)
+    # plt.scatter( xtime1,VoltChange[volt_column[i]],marker='o')
+    plt.legend(bbox_to_anchor=(1, 0), loc=3, fontsize=16)
+    plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
+    plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 
+    for a,b in zip(x,y):
+        plt.text(a, b+0.05, '%.1f' % b, ha='center', va= 'bottom',fontsize=15)
+    # ax1.savefig(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\99Result\电池包换电时间段.png')
+    plt.show()
+

+ 96 - 0
USER/SPF/01qixiang/09Behave/CBMSBatBehave.py

@@ -0,0 +1,96 @@
+import pandas as pd
+import numpy as np
+import datetime
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam
+
+class BatBehave:
+    def __init__(self,sn,celltype,df_bms):  #参数初始化
+
+        self.sn=sn
+        self.celltype=celltype
+        self.param=BatParam.BatParam(celltype)
+        self.df_bms=df_bms
+
+        df_bms['时间戳']=pd.to_datetime(df_bms['时间戳'], format='%Y-%m-%d %H:%M:%S')
+        
+        self.packcrnt=df_bms['总电流[A]']*self.param.PackCrntDec
+        self.bmstime= df_bms['时间戳']
+        self.packsoc=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 behave(self):
+        if self.celltype<=50:
+            df_res=self._bat_behave()
+            return df_res    
+        else:
+            df_res=self._bat_behave()
+            return df_res
+
+    
+    #寻找当前行数据的所有温度值...................................................................................
+    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 _chrgdata(self):   
+        self.ComeIn=[]
+        self.ChgStart=[]
+        self.ChgEnd=[]
+        self.ComeOut=[]
+        if len(self.df_bms)>10:
+            state=0
+            for i in range(3, len(self.bmstime) - 3):
+                if state==0:
+                    if abs(self.packcrnt[i])<=0.1:
+                        self.ComeIn.append(i)
+                        state=1
+                elif state==1:
+                    if self.packcrnt[i]<=-10 and self.packcrnt[i+1]<=-10:
+                        self.ChgStart.append(i)
+                        state=2
+                    elif self.packcrnt[i]>1:
+                        state=0
+                        self.ComeIn.pop()
+                    else:
+                        pass
+                elif state==2:
+                    if self.packcrnt[i]>-0.1:
+                        state=3
+                        if (self.bmstime[i]-self.bmstime[self.ChgStart[-1]]).total_seconds()>300 and self.packsoc[i]-self.packsoc[self.ChgStart[-1]]>5:
+                            self.ChgEnd.append(i)
+                        else:
+                            state=0
+                            self.ComeIn.pop()
+                            self.ChgStart.pop()
+                    else:
+                        pass
+                else:
+                    if self.packcrnt[i]>1:
+                        state=0
+                        self.ComeOut.append(i)
+                    else:
+                        pass
+        else:
+            pass
+
+    #..........................................电池诊断功能..................................................................
+    def _bat_behave(self):
+        df_res=pd.DataFrame(columns=['sn','time_in', 'time_st', 'time_sp', 'time_out', 'soc_st', 'soc_sp'])
+        self._chrgdata()
+        if len(self.ComeOut)>0:
+            for i in range(len(self.ChgEnd)):
+                df_res.loc[i]=[self.sn, self.bmstime[self.ComeIn[i]], self.bmstime[self.ChgStart[i]], self.bmstime[self.ChgEnd[i]], self.bmstime[self.ComeOut[i]], self.packsoc[self.ChgStart[i]], self.packsoc[self.ChgEnd[i]]]
+            return df_res
+        else:
+            return pd.DataFrame()

+ 99 - 0
USER/SPF/01qixiang/09Behave/main.py

@@ -0,0 +1,99 @@
+import CBMSBatBehave
+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
+
+#...............................................主函数..........................................................................................................
+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')
+    SNdata_ZKHD=pd.read_excel(excelpath, sheet_name='重卡换电')
+    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_ZKHD=SNdata_ZKHD['SN号'].tolist()
+    SNnums=[SNnums_L7255 + SNnums_C7255 + SNnums_U7255 + SNnums_4840, SNnums_6040 + SNnums_6060]
+    SNnums=SNnums_ZKHD
+    
+    mylog=log.Mylog('log_diag.txt','error')
+    mylog.logcfg()
+   
+    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')
+
+    start_time='2021-12-1 00:00:00'
+    end_time='2021-12-30 23:59:59'
+
+    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三元电芯
+            elif 'TJMCL' in sn:
+                celltype=100 #金茂重卡换电
+            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']
+            df_bms=df_bms.dropna(axis=0,subset=['总电流[A]','SOH[%]','SOC[%]']) #去除有空值的行
+            df_bms.reset_index(inplace=True,drop=True)     #重置索引
+            df_bms.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\98Download\\'+sn+'_BMS_'+'.csv',encoding='GB18030')
+            # print(df_bms)
+
+            #用户行为分析................................................................................................................................................................
+            if not df_bms.empty:
+                BatBehave=CBMSBatBehave.BatBehave(sn,celltype,df_bms)
+                df_res=BatBehave.behave()
+
+                #当前热失控故障写入数据库
+                if not df_res.empty:
+                #读取老的excel结果
+                    try:
+                        df_res_old= pd.read_excel(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\99Result\\'+'CBMS_'+'行为分析1'+'.xlsx',index_col=0)
+                    except:
+                        df_res_old=pd.DataFrame()
+                    #新的结果与老结果合并
+                    if df_res_old.empty:
+                        df_res=df_res
+                    else:
+                        df_res=df_res.append(df_res_old)
+                    #合并后的结果存入excel文件
+                    if not df_res.empty:
+                        df_res.to_excel(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\99Result\\'+'CBMS_'+'行为分析1'+'.xlsx')
+                    # print('done!!!')
+                
+
+            end=time.time()
+            print(end-start)
+        except Exception as e:
+            print(repr(e))
+            mylog.logopt(e)

+ 0 - 0
USER/SPF/01qixiang/101Log/log_diag.txt


+ 371 - 0
USER/SPF/01qixiang/10Parameter/BatParam.py

@@ -0,0 +1,371 @@
+import pandas as pd
+
+df_param=pd.DataFrame()
+df_param.index=[1, 2, 3, 4, 99, 100]    #1:6040,2:4840,3:力信50Ah,4:CATL50Ah,99:6060,100:重卡换电
+
+df_param['CellVoltNums']=[17, 14, 20, 20, 20, 384]
+df_param['CellTempNums']=[3, 3, 3, 2, 3, 64]
+
+
+#定义电池参数
+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()

+ 92 - 0
USER/SPF/01qixiang/BatParam.py

@@ -0,0 +1,92 @@
+
+#定义电池参数
+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.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.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.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.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=60.5
+            self.PeakCellVolt=[3.357,3.358,3.359,3.36,3.361]
+            self.PackCrntDec=1
+            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]
+        
+        else:
+            print('未找到对应电池编号!!!')
+            # sys.exit()
+

BIN
USER/SPF/01qixiang/USER - 快捷方式.lnk


+ 111 - 0
USER/SPF/01qixiang/test.py

@@ -0,0 +1,111 @@
+from re import A
+import pandas as pd
+import numpy as np
+from pandas.core.frame import DataFrame
+from scipy import interpolate
+import matplotlib.pyplot as plt
+import bisect
+import datetime,time
+import operator
+from six import b
+import skfuzzy as fuzz
+import skfuzzy.control as ctrl
+import os
+from scipy.interpolate import interp2d
+from apscheduler.schedulers.blocking import BlockingScheduler
+from LIB.MIDDLE.CellStateEstimation.Common import BatParam
+
+# def fun1():
+#     print('job1')
+#     print(datetime.datetime.now())
+#     time.sleep(3)
+# def fun2():
+#     print('job2')
+#     print(datetime.datetime.now())
+#     time.sleep(3)
+
+# scheduler = BlockingScheduler()
+# scheduler.add_job(fun1, 'interval', seconds=5)
+# scheduler.add_job(fun2, 'interval', seconds=5)
+# scheduler.start()
+ 
+# a=8
+# for i in range(a):
+#     with open('soc.txt','a') as file:
+#         file.write(str(a)+'\n')
+#     a=a+1
+
+
+# a={'1':123,'2':234,'3':456}
+# b=list(a.values())
+# print(b)
+# print(b[1])
+
+
+
+# a=pd.DataFrame(columns=['a','b'])
+# a['a']=[1,3,1,2,4]
+# a['b']=[1,2,3,4,5]
+# # a=a.sort_values(by='a',ascending=True)
+# a=a.drop('b',axis=1)
+# print(a)
+
+
+column_name=['time', 'sn', 'short_current']
+df_res=pd.DataFrame(columns=column_name)
+df_res.loc[1]=[1,1.5,1]
+df_res.loc[2]=[1,-2.5,2]
+df_res.loc[3]=[-1,3.5,3]
+df_res.loc[4]=[3,-4.5,4]
+df_res.reset_index(inplace=True,drop=True)     #重置索引
+
+df_res=df_res[(df_res[['time','sn']]>0) & (df_res[['time','sn']]<3)]
+print(df_res)
+
+
+
+
+# column_name=['time', 'sn', 'soc']
+# df_res1=pd.DataFrame(columns=column_name)
+# df_res1.loc[1]=[1,1,3.330]
+# df_res1.loc[2]=[1,2,3.330]
+
+# df=df_res.append(df_res1)
+# print(df)
+
+# df_res1=pd.DataFrame(columns=column_name)
+# df_res1.loc[1]=[1,1,3.330]
+
+# df_res.append(df_res1,ignore_index=True)
+
+# # print(df_res.drop(df_res.loc[df_res['sn']==3].index))
+# print(df_res)
+
+
+# import time
+# import datetime
+# from apscheduler.schedulers.blocking import BlockingScheduler
+
+# def add(a):
+#     a=a+1
+#     return a
+
+
+# def func():
+#     #耗时2S
+#     global a
+#     now = datetime.datetime.now()
+#     ts = now.strftime('%Y-%m-%d %H:%M:%S')
+#     a=add(a)
+#     print('do func2 time:',ts)
+#     print(a)
+#     # time.sleep(5)
+    
+    
+
+# if __name__=="__main__":  
+#     a=1
+#     scheduler = BlockingScheduler()
+#     scheduler.add_job(func, 'interval', seconds=1, id='test_job1')
+ 
+#     scheduler.start()

+ 554 - 0
USER/SPF/03hezhong/00BatInterShort/CBMSBatInterShort.py

@@ -0,0 +1,554 @@
+import pandas as pd
+import numpy as np
+import matplotlib.pyplot as plt
+from USER.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['电流']*self.param.PackCrntDec
+        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
+
+        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=self._ncm_intershort()
+            return df_res
+            
+        elif self.celltype>50:
+            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 = list(self.df_bms.loc[num,self.celltemp_name])
+        celltemp=min(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 = np.array(self.df_bms.loc[num,self.cellvolt_name])
+        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)]
+            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)
+        
+        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)
+ 
+    #获取所有电芯的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)]>self.param.PeakCellVolt[symbol]:
+                        As_tatol=As_tatol+As
+                        symbol=symbol+1
+                    else:
+                        continue
+                else:
+                    cellAs.append(As_tatol/5)
+                    break
+   
+        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)
+
+    #计算每个电芯的均衡时长..........................................................................................................................
+    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,'PackSOH']
+                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]) < 1 and abs(self.packcrnt[i-1]) < 1 and abs(self.packcrnt[i+1]) < 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._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_min<4.5 and 2<cellvolt_max<4.5 and deltvolt<0.003: 
+                                dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                                deltsoc_last=self._celldeltsoc_get(i,dict_baltime,capacity)
+                                time_last=self.bmstime[i]
+                                firsttime=0
+                        else:
+                            pass                
+                    elif standingtime>3600*5:
+                        standingtime=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_min<4.5 and 2<cellvolt_max<4.5 and deltvolt<0.005:
+                            dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                            deltsoc_now=self._celldeltsoc_get(i,dict_baltime,capacity)
+                            time_now=self.bmstime[i]
+                            
+                            list_sub=deltsoc_now-deltsoc_last
+                            list_pud=[0.01*capacity*3600*1000/(time_now-time_last).total_seconds()]*self.param.CellVoltNums
+                            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: 
+                        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 and abs(self.packcrnt[i+2]) >= 1:  
+                        standingtime1=0
+                        cellvolt_now1=self._cellvolt_get(i)
+                        cellvolt_max1=max(cellvolt_now1)
+                        cellvolt_min1=min(cellvolt_now1)
+                        cellvolt_last1=self._cellvolt_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-50 and 2<cellvolt_min1<4.5 and 2<cellvolt_max1<4.5 and deltvolt1<0.005:
+                            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*12:
+                                    deltsoc_now1=self._celldeltsoc_get(i,dict_baltime1,capacity)
+
+                                    list_sub1=deltsoc_now1-deltsoc_last1
+                                    list_pud1=[0.01*capacity*3600*1000/(time_now1-time_last1).total_seconds()]*self.param.CellVoltNums
+                                    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:
+                    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,'PackSOH']
+                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:      #静置时间满足要求
+                            standingtime=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 abs(deltvolt)<0.003:
+                                dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                                deltsoc_last=self._celldeltsoc_get(i,dict_baltime,capacity)
+                                time_last=self.bmstime[i]
+                                firsttime=0
+                            else:
+                                pass
+                        else:
+                            pass                
+                    elif standingtime>3600*12:
+                        standingtime=0
+                        cellvolt_now=np.array(self._cellvolt_get(i))
+                        cellvolt_min=min(cellvolt_now)
+                        cellvolt_max=max(cellvolt_now)
+                        cellvolt_last=np.array(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 abs(deltvolt)<0.003:
+                            dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                            deltsoc_now=self._celldeltsoc_get(i, dict_baltime,capacity)    #获取每个电芯的SOC差
+                            time_now=self.bmstime[i]
+
+                            list_sub=deltsoc_now-deltsoc_last
+                            list_pud=[0.01*capacity*3600*1000/(time_now-time_last).total_seconds()]*self.param.CellVoltNums
+                            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:
+                            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._cellvolt_get(i)
+                        cellvolt_max1=max(cellvolt_now1)
+                        cellvolt_min1=min(cellvolt_now1)
+                        cellvolt_last1=self._cellvolt_get(i-1)
+                        deltvolt1=max(abs(cellvolt_now1-cellvolt_last1))
+                    
+                        if 2<cellvolt_max1<self.param.OcvInflexionBelow-0.002 and 2<cellvolt_min1<4.5 and deltvolt1<0.003:
+                            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=deltsoc_now1-deltsoc_last1
+                                    list_pud1=[0.01*capacity*3600*1000/(time_now1-time_last1).total_seconds()]*self.param.CellVoltNums
+                                    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:
+                    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 or (self.packcrnt[i]>self.param.Capacity/3 and self.packcrnt[i+1]>self.param.Capacity/3):  #如果充电过程中时间间隔>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.11:   #电压>满充电压-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=deltAs_now-deltAs_last
+                        list_pud=[-1000/(time_now-time_last).total_seconds()]*self.param.CellVoltNums
+                        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,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

+ 65 - 0
USER/SPF/03hezhong/00BatInterShort/main.py

@@ -0,0 +1,65 @@
+import CBMSBatInterShort
+import datetime
+import pandas as pd
+import datetime
+from USER.Common import DBDownload
+from USER.Common import log
+
+if __name__ == "__main__":
+
+    SNnums= ['HeZhong']
+
+    end_time=datetime.datetime.now()-datetime.timedelta(seconds=10)
+    start_time=end_time-datetime.timedelta(seconds=12000)
+    start_time=start_time.strftime('%Y-%m-%d %H:%M:%S')
+    end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+
+    #log信息配置............................................................................................................................................................................
+    mylog=log.Mylog('log_short.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()
+
+            celltype=15
+
+            #............................................................读取原始数据库数据...........................................................................
+            df_bms= pd.read_csv(r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\98Download\合众data924T101(1).csv',encoding='GB18030')
+
+            #............................................................读取结果数据库数据.........................................................................
+            # host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+            # port=3306
+            # db='qx_cas'
+            # user='qx_read'
+            # password='Qx@123456'
+            # mode=1
+            # tablename='cellstateestimation_soh'
+            # DBRead=DBDownload.DBDownload(host, port, db, user, password, mode)
+            # with DBRead as DBRead:
+            #     df_soh=DBRead.getdata(param='time_st,time_sp,sn,method,soh,cellsoh', tablename=tablename, sn=sn, timename='time_sp', st=start_time, sp=end_time)
+            df_soh=pd.DataFrame({'time_st':[1],'sn':[2],'soh':[100]})
+            # print(df_soh)
+            
+            #...........................................................................计算漏电流............................................................................
+            BatInterShort=CBMSBatInterShort.BatInterShort(sn,celltype,df_bms,df_soh)
+            df_res=BatInterShort.intershort()
+            df_res.to_excel(r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\99Result\\'+'CBMS_short_'+sn+'_1.xlsx')
+            # print('done!!!')
+        
+        except Exception as e:
+            print(repr(e))
+            mylog.logopt(sn,e)
+            pass

+ 673 - 0
USER/SPF/03hezhong/01BatSoh/CBMSBatSoh.py

@@ -0,0 +1,673 @@
+import pandas as pd
+import numpy as np
+import datetime
+import bisect
+import matplotlib.pyplot as plt
+import BatParam
+
+class BatSoh:
+    def __init__(self,sn,celltype,df_bms,df_volt,df_temp,df_accum):  #参数初始化
+
+        self.sn=sn
+        self.celltype=celltype
+        CellVoltNums=int(df_volt.loc[5,'单体电池总数'])
+        if CellVoltNums==110:
+            self.celltype=99
+        else:
+            self.celltype==100
+        self.param=BatParam.BatParam(self.celltype)
+        self.df_volt=df_volt
+        self.df_temp=df_temp
+        self.bmsstat=df_bms['充电状态']
+        self.packcrnt=(df_volt['可充电储能装置电流(A)'].astype('float'))*self.param.PackCrntDec
+        self.packvolt=df_volt['可充电储能装置电压(V)'].astype('float')
+        self.bms_soc=df_bms['SOC']
+        # self.bms_soh=df_volt['SOH[%]']
+        self.bmstime= pd.to_datetime(df_volt['上报时间'], format='%Y-%m-%d %H:%M:%S')
+        self.param.CellVoltNums=CellVoltNums
+        self.param.CellTempNums=int(df_temp.loc[5,'可充电储能温度探针个数'])
+
+
+
+    def batsoh(self):
+        if self.celltype==1 or self.celltype==2 or self.celltype==3 or self.celltype==4 or self.celltype==100:
+            df_res=self._ncmsoh_chrg()
+            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 _celltemp_weight(self,num):   
+        celltemp = []
+        for j in range(1, self.param.CellTempNums+1):
+            s = str(j)
+            celltemp.append(self.df_temp.loc[num, s+'.0'])
+        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
+                self.StandardStandingTime=7200
+            else:
+                self.tempweight=0
+                self.StandardStandingTime=10800
+        else:
+            if min(celltemp)>=20:
+                self.tempweight=1
+                self.StandardStandingTime=1800
+            elif min(celltemp)>=10:
+                self.tempweight=0.8
+                self.StandardStandingTime=3600
+            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_volt.loc[num, s+'.0'])
+        return cellvolt
+    
+    #筛选充电数据..............................................................................................................................
+    def _chrgdata(self):    
+        self.ChgStart=[]
+        self.ChgEnd=[]
+        if len(self.packvolt)>100:
+            charging=0
+            for i in range(3, len(self.bmstime) - 3):
+                if charging==0:
+                    if i==3 and self.bmsstat[i]=='停车充电' and self.bmsstat[i+1]=='停车充电':
+                        self.ChgStart.append(i)
+                        charging=1
+                    elif self.bmsstat[i-1]!='停车充电' and self.bmsstat[i]=='停车充电':
+                        self.ChgStart.append(i)
+                        charging=1
+                    else:
+                        pass
+                else:
+                    if (self.bmsstat[i-1]=='停车充电' or '充电完成') and self.packcrnt[i]>0:
+                        self.ChgEnd.append(i)
+                        charging=0
+                    elif i == (len(self.bmstime) - 4) and (self.bmsstat[i] == '停车充电' or '充电完成') and self.packcrnt[i]<-1:
+                        self.ChgEnd.append(len(self.bmstime)-2)
+                        charging=0
+    
+    #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(float(self.bms_soc[j].strip('%')))
+
+        #切片,去除前后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
+
+    #两点法计算三元SOH.........................................................................................................................
+    def _ncmsoh_twopoint(self):
+        standingpoint_st=[]
+        standingpoint_sp=[]
+        tempweightlist=[]
+        standingtime=0
+        for i in range(3,len(self.df_volt)-3):
+
+            if abs(self.packcrnt[i]) < 0.2 and abs(self.packcrnt[i-1]) < 0.2 and abs(self.packcrnt[i+1]) < 0.2:     #电流为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)     #获取当前行电压数据
+                            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 3<max(cellvolt_now)<4.5 and 3<min(cellvolt_now)<4.5:
+                                if abs(minocv_socst-minocv_socnow)>=30:   #当前时刻SOC与开始时刻SOC差>=40
+                                    if abs(self.packcrnt[i+2])>=0.2:    #如果下一时刻电流>=0.5,则压入当前索引
+                                        standingpoint_sp.append(i)
+                                        standingpoint_st.append(i)
+                                        tempweightlist.append(self.tempweight)
+                                        standingtime=0
+                                        continue
+                                    else:
+                                        if standingtime>7200 or i==len(self.df_volt)-2:   #仍处于静置,但静置时间>1h,则直接获取sp时刻,或者到了数据末尾
+                                            standingpoint_sp.append(i)
+                                            tempweightlist.append(self.tempweight)
+                                            continue
+                                else:
+                                    if abs(self.packcrnt[i+2])>=0.2:
+                                        standingtime=0
+                                        if  minocv_socst<50 and minocv_socnow<minocv_socst:
+                                            standingpoint_st[-1]=i
+                                            continue
+                                    elif abs(self.packcrnt[i+2])>=0.2:
+                                        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.5:
+                                cellvolt_now=self._cellvolt_get(i) 
+                                if 3<max(cellvolt_now)<4.5 and 3<min(cellvolt_now)<4.5:
+                                    standingpoint_st.append(i)
+                                    standingtime=0
+                                    continue
+                            else:
+                                continue
+                    else:
+                        if abs(self.packcrnt[i+2])>0.5:
+                            cellvolt_now=self._cellvolt_get(i) 
+                            if 3<max(cellvolt_now)<4.5 and 3<min(cellvolt_now)<4.5:
+                                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','detasoh']
+            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 Step<60:
+                        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_init)
+                    # else:
+                    #     cellsoh=[]
+                    #     break
+                if cellsoh:
+                    soh=min(cellsoh)
+                    soh_list=[timepoint_bms_st, timepoint_bms_sp, self.sn, 1, soh, str(cellsoh),max(cellsoh)-min(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()
+    
+    def _ncmsoh_chrg(self):      
+        self._chrgdata()
+        print(self.ChgStart,self.ChgEnd)
+        ChgStartValid=[]
+        ChgEndValid=[]
+        for i in range(min(len(self.ChgStart),len(self.ChgEnd))): 
+            self._celltemp_weight(self.ChgEnd[i])               #获取温度对应的静置时间及权重            
+   
+            #筛选满足2点法计算的数据
+            StandingTime=0
+            StandingTime1=0
+            for m in range(max(len(self.packcrnt)-self.ChgEnd[i]-2,self.ChgStart[i]-2)):
+                if self.ChgStart[i] - m - 1>0 and 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 self.ChgEnd[i] + m + 1<len(self.packcrnt) and 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:  #筛选静置时间>15min且慢充过程丢失数据少
+                    ChgStartValid.append(self.ChgStart[i])
+                    ChgEndValid.append(self.ChgEnd[i]+m)
+                    break
+                if abs(self.packcrnt[self.ChgStart[i] - m - 2])>0.5 and abs(self.packcrnt[self.ChgEnd[i] + m + 2])>0.5:
+                    StandingTime=0
+                    StandingTime1=0
+                    break
+
+        print(ChgStartValid,ChgEndValid)
+        if len(ChgStartValid)>0:   #两点法计算Soh
+            df_res=pd.DataFrame(columns=('time','sn','soh','cellsoh','deltsoh'))
+            soh2=[]
+
+            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()
+                    if Step<120:
+                        Ah=Ah-self.packcrnt[j+1]*Step/3600
+                
+                if Ah>10:
+                    for j in range(1, self.param.CellVoltNums+1):    #计算每个电芯的Soh
+                        s = str(j)
+                        OCVStart=self.df_volt.loc[ChgStartValid[i]-1,s+'.0']
+                        OCVEnd=self.df_volt.loc[ChgEndValid[i],s+'.0']
+                        #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)
+                    delasoh=max(soh2)-min(soh2)
+                    df_res.loc[len(df_res)]=[self.bmstime[ChgStartValid[i]],self.sn,soh1,soh2,delasoh]
+            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_volt)-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 float(self.bms_soc[i].strip('%'))<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 or (self.packcrnt[i]>self.param.Capacity/3 and self.packcrnt[i+1]>self.param.Capacity/3):  #如果充电过程中时间间隔>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:       
+            # self.getdata()  #获取已计算的soh
+            column_name=['time_st','time_sp','sn','method','soh','cellsoh']
+            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 Step<60:
+                            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=ah_packcrnt
+
+                    # 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]
+                        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_volt[ str(cellmaxvolt_number2[i]+1)+'.0']    #获取最大电压
+                    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结果进行滤波处理................................................................................................................................
+            return df_res
+
+        return pd.DataFrame()
+
+

Some files were not shown because too many files changed in this diff