Sfoglia il codice sorgente

开发过程管理

qingfeng 2 anni fa
parent
commit
935a31c23c
66 ha cambiato i file con 18490 aggiunte e 4 eliminazioni
  1. 4 4
      LIB/MIDDLE/CellStateEstimation/Common/V1_0_1/BatParam.py
  2. 802 0
      USER/SPF/WORK/01qixiang/01BatSoh/CBMSBatSoh copy.py
  3. 880 0
      USER/SPF/WORK/01qixiang/01BatSoh/CBMSBatSoh.py
  4. 102 0
      USER/SPF/WORK/01qixiang/01BatSoh/main.py
  5. 389 0
      USER/SPF/WORK/01qixiang/02BatUniform/CBMSBatUniform.py
  6. 89 0
      USER/SPF/WORK/01qixiang/02BatUniform/main.py
  7. 589 0
      USER/SPF/WORK/01qixiang/03BatInterShort/CBMSBatInterShort.py
  8. 92 0
      USER/SPF/WORK/01qixiang/03BatInterShort/main.py
  9. 630 0
      USER/SPF/WORK/01qixiang/04BatSoc/CBMSBatSoc.py
  10. 140 0
      USER/SPF/WORK/01qixiang/04BatSoc/main.py
  11. 539 0
      USER/SPF/WORK/01qixiang/05BatDiag/CBMSBatDiag.py
  12. 213 0
      USER/SPF/WORK/01qixiang/05BatDiag/DataStatistics.py
  13. 36 0
      USER/SPF/WORK/01qixiang/05BatDiag/DiagDataMerge.py
  14. 73 0
      USER/SPF/WORK/01qixiang/05BatDiag/SC_CtrlSafty.py
  15. 356 0
      USER/SPF/WORK/01qixiang/05BatDiag/SC_SamplingSafty.py
  16. 502 0
      USER/SPF/WORK/01qixiang/05BatDiag/deploy.py
  17. 134 0
      USER/SPF/WORK/01qixiang/05BatDiag/main_test.py
  18. 262 0
      USER/SPF/WORK/01qixiang/06BatSafetyAlarm/CBMSSafetyAlarm.py
  19. 248 0
      USER/SPF/WORK/01qixiang/06BatSafetyAlarm/deploy - 副本.py
  20. 161 0
      USER/SPF/WORK/01qixiang/06BatSafetyAlarm/main copy 2.py
  21. 161 0
      USER/SPF/WORK/01qixiang/06BatSafetyAlarm/main.py
  22. 906 0
      USER/SPF/WORK/01qixiang/07BatSafetyWarning copy/CBMSBatInterShort.py
  23. 506 0
      USER/SPF/WORK/01qixiang/07BatSafetyWarning copy/CBMSBatUniform.py
  24. 265 0
      USER/SPF/WORK/01qixiang/07BatSafetyWarning copy/CBMSSafetyWarning.py
  25. 266 0
      USER/SPF/WORK/01qixiang/07BatSafetyWarning copy/VoltStray.py
  26. 279 0
      USER/SPF/WORK/01qixiang/07BatSafetyWarning copy/main.py
  27. 906 0
      USER/SPF/WORK/01qixiang/07BatSafetyWarning/CBMSBatInterShort.py
  28. 506 0
      USER/SPF/WORK/01qixiang/07BatSafetyWarning/CBMSBatUniform.py
  29. 265 0
      USER/SPF/WORK/01qixiang/07BatSafetyWarning/CBMSSafetyWarning.py
  30. 266 0
      USER/SPF/WORK/01qixiang/07BatSafetyWarning/VoltStray.py
  31. 279 0
      USER/SPF/WORK/01qixiang/07BatSafetyWarning/main.py
  32. 699 0
      USER/SPF/WORK/01qixiang/08BatQuality/CBMSBatDiag.py
  33. 165 0
      USER/SPF/WORK/01qixiang/08BatQuality/main.py
  34. 145 0
      USER/SPF/WORK/01qixiang/09Behave/BehaveStatistics.py
  35. 96 0
      USER/SPF/WORK/01qixiang/09Behave/CBMSBatBehave.py
  36. 99 0
      USER/SPF/WORK/01qixiang/09Behave/main.py
  37. 371 0
      USER/SPF/WORK/01qixiang/10Parameter/BatParam.py
  38. 92 0
      USER/SPF/WORK/01qixiang/BatParam.py
  39. BIN
      USER/SPF/WORK/01qixiang/USER - 快捷方式.lnk
  40. 111 0
      USER/SPF/WORK/01qixiang/test.py
  41. 554 0
      USER/SPF/WORK/03hezhong/00BatInterShort/CBMSBatInterShort.py
  42. 65 0
      USER/SPF/WORK/03hezhong/00BatInterShort/main.py
  43. 132 0
      USER/SPF/WORK/03hezhong/01BatSoh/BatParam.py
  44. 673 0
      USER/SPF/WORK/03hezhong/01BatSoh/CBMSBatSoh.py
  45. 61 0
      USER/SPF/WORK/03hezhong/01BatSoh/DBDownload.py
  46. 24 0
      USER/SPF/WORK/03hezhong/01BatSoh/log.py
  47. 58 0
      USER/SPF/WORK/03hezhong/01BatSoh/main.py
  48. 132 0
      USER/SPF/WORK/03hezhong/02BatUniform/BatParam.py
  49. 387 0
      USER/SPF/WORK/03hezhong/02BatUniform/CBMSBatUniform.py
  50. 24 0
      USER/SPF/WORK/03hezhong/02BatUniform/log.py
  51. 58 0
      USER/SPF/WORK/03hezhong/02BatUniform/main.py
  52. 582 0
      USER/SPF/WORK/03hezhong/03BatInterShort/CBMSBatInterShort.py
  53. 56 0
      USER/SPF/WORK/03hezhong/03BatInterShort/main.py
  54. 156 0
      USER/SPF/WORK/03hezhong/04BatDiag/BatParam.py
  55. 1006 0
      USER/SPF/WORK/03hezhong/04BatDiag/CBMSBatDiag copy 2.py
  56. 103 0
      USER/SPF/WORK/03hezhong/04BatDiag/CBMSBatDiag copy.py
  57. 991 0
      USER/SPF/WORK/03hezhong/04BatDiag/CBMSBatDiag.py
  58. 24 0
      USER/SPF/WORK/03hezhong/04BatDiag/log.py
  59. 86 0
      USER/SPF/WORK/03hezhong/04BatDiag/main.py
  60. 129 0
      USER/SPF/WORK/03hezhong/05BatChrg/BatParam.py
  61. 276 0
      USER/SPF/WORK/03hezhong/05BatChrg/CBMSBatChrg.py
  62. 24 0
      USER/SPF/WORK/03hezhong/05BatChrg/log.py
  63. 58 0
      USER/SPF/WORK/03hezhong/05BatChrg/main.py
  64. 132 0
      USER/SPF/WORK/03hezhong/Common/BatParam.py
  65. 24 0
      USER/SPF/WORK/03hezhong/Common/log.py
  66. 51 0
      USER/SPF/WORK/test.ipynb

+ 4 - 4
LIB/MIDDLE/CellStateEstimation/Common/V1_0_1/BatParam.py

@@ -34,8 +34,8 @@ class BatParam:
         self.mk_z=-5
         self.mk_z=-5
         self.mk_Tau=-0.7
         self.mk_Tau=-0.7
         self.mk_slope=-0.1
         self.mk_slope=-0.1
-        self.mk_s=-260
-        self.mk_svar=1600
+        self.mk_s=-200
+        self.mk_svar=2600
 
 
         self.OcvWeight_Temp=[-30,-20,-10,0,10,20,30,40,50]
         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_StandingTime=[0,500,600,1200,1800,3600,7200,10800]
@@ -294,7 +294,7 @@ class BatParam:
             self.CellTempDiffLv2=15   
             self.CellTempDiffLv2=15   
 
 
             # self.TrwVoltRate=-8
             # self.TrwVoltRate=-8
-            self.mk_slope=-0.8
+            self.mk_slope=-0.6
             
             
             
             
             self.DifVolGap = 3
             self.DifVolGap = 3
@@ -349,7 +349,7 @@ class BatParam:
             self.CellTempDiffLv2=32   
             self.CellTempDiffLv2=32   
 
 
             # self.TrwVoltRate=-8 
             # self.TrwVoltRate=-8 
-            self.mk_slope=-0.3
+            self.mk_slope=-0.6
             
             
                                    
                                    
             self.DifVolGap = 3
             self.DifVolGap = 3

+ 802 - 0
USER/SPF/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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
+        

+ 248 - 0
USER/SPF/WORK/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/WORK/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/WORK/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):
+
+    start=time.time()
+    now_time=datetime.datetime.now() #-datetime.timedelta(seconds=3600*24+3600*14.6)
+    start_time=now_time-datetime.timedelta(seconds=180)
+    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-10-31 09:30:30'
+    end_time='2021-10-31 09:31:30'
+
+    #数据库配置
+    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
+    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:\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=[['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=['sn','time','safetywarning1','safetywarning2'])
+    df_alarm_ram2=pd.DataFrame(columns=['sn','time','safetywarning1','safetywarning2'])
+
+    mainprocess()
+    #定时任务.......................................................................................................................................................................
+    scheduler = BlockingScheduler()
+    scheduler.add_job(mainprocess, 'interval', seconds=180, id='diag_job')
+
+    try:  
+        scheduler.start()
+    except Exception as e:
+        scheduler.shutdown()
+        print(repr(e))
+        mylog.logopt(e)

+ 906 - 0
USER/SPF/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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/WORK/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)

+ 371 - 0
USER/SPF/WORK/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/WORK/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/WORK/01qixiang/USER - 快捷方式.lnk


+ 111 - 0
USER/SPF/WORK/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/WORK/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/WORK/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

+ 132 - 0
USER/SPF/WORK/03hezhong/01BatSoh/BatParam.py

@@ -0,0 +1,132 @@
+
+#定义电池参数
+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 = 103
+            self.PackFullChrgVolt=69.99
+            self.CellFullChrgVolt=3.5
+            self.OcvInflexionBelow=3.285
+            self.OcvInflexion2=3.296
+            self.OcvInflexion3=3.328
+            self.OcvInflexionAbove=3.4
+            self.CellVoltNums=20
+            self.CellTempNums=4
+            self.FullChrgSoc=98
+            self.PeakSoc=59
+            self.PeakVoltLowLmt=3.35
+            self.PeakVoltUpLmt=3.4
+            self.PeakCellVolt=[3.362,3.363,3.365,3.366,3.367]
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0.00, 	2.40, 	6.38, 	10.37, 	14.35, 	18.33, 	22.32, 	26.30, 	30.28, 	35.26, 	40.24, 	45.22, 	50.20, 	54.19, 	58.17, 	60.16, 	65.14, 	70.12, 	75.10, 	80.08, 	84.06, 	88.05, 	92.03, 	96.02, 	100.00]
+            self.LookTab_OCV = [2.7151,	3.0298,	3.1935,	3.2009,	3.2167,	3.2393,	3.2561,	3.2703,	3.2843,	3.2871,	3.2874,	3.2868,	3.2896,	3.2917,	3.2967,	3.3128,	3.3283,	3.3286,	3.3287,	3.3288,	3.3289,	3.3296,	3.3302,	3.3314,	3.3429]
+        
+        elif celltype==100:   #CATL 102ah三元电芯
+            self.Capacity = 102
+            self.PackFullChrgVolt=80
+            self.CellFullChrgVolt=4.2
+            self.CellVoltNums=96
+            self.CellTempNums=16
+            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]
+            self.LookTab_OCV = [3.397,	3.429,	3.462,	3.507,	3.551,	3.575,	3.600,	3.617,	3.633,	3.658,	3.683,	3.735,	3.786,	3.836,	3.887,	3.939,	3.992,	4.053,	4.113,	4.195,	4.277]
+
+            self.CellTempHighLv1=45
+            self.CellTempHighLv2=50
+            self.CellTempLowLv1=-20
+            self.CellTempLowLv2=-25
+            self.CellTempDiffLv1=10
+            self.CellTempDiffLv2=15
+            self.CellTempRate=0.99
+
+            self.CellOvLv1=4.3
+            self.CellOvLv2=4.35
+            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=1.5*self.Capacity
+            self.PackDisChgOc=3*self.Capacity
+
+            self.SocJump=3
+            self.SocClamp=3
+
+        else:
+            print('未找到对应电池编号!!!')
+            # sys.exit()
+

+ 673 - 0
USER/SPF/WORK/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()
+
+

+ 61 - 0
USER/SPF/WORK/03hezhong/01BatSoh/DBDownload.py

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

+ 24 - 0
USER/SPF/WORK/03hezhong/01BatSoh/log.py

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

+ 58 - 0
USER/SPF/WORK/03hezhong/01BatSoh/main.py

@@ -0,0 +1,58 @@
+import CBMSBatSoh
+import log
+#coding=utf-8
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+from sqlalchemy import create_engine
+import time, datetime
+import os
+
+dbManager = DBManager.DBManager()
+if __name__ == "__main__":
+    filepath=r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\98Download'
+    files=os.listdir(filepath)
+
+    # now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+    # now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+    # start_time=now_time-datetime.timedelta(days=30)
+    # end_time=str(now_time)
+    # start_time=str(start_time)
+
+    #log信息配置
+    mylog=log.Mylog('log.txt','error')
+    mylog.logcfg()
+
+    for filename in files:
+        try:
+            # filename=files[3]
+            sn=filename[7:17]
+            print(filename)
+            celltype=100
+            start_time='2021-04-01 19:12:26'
+            end_time='2021-09-02 19:12:26'
+            df_volt = pd.read_excel(filepath+'\\'+filename,sheet_name='单体电池电压数据')
+            df_temp = pd.read_excel(filepath+'\\'+filename,sheet_name='单体电池温度数据')
+            df_bms = pd.read_excel(filepath+'\\'+filename,sheet_name='整车数据')
+            df_volt.columns=df_volt.loc[0].astype('str')
+            df_temp.columns=df_temp.loc[0].astype('str')
+            df_volt=df_volt.drop(0,axis=0)
+            df_temp=df_temp.drop(0,axis=0)
+            df_volt=df_volt.reset_index(drop=True)
+            df_temp=df_temp.reset_index(drop=True)
+            
+            df_accum=pd.DataFrame()
+
+            # df_volt.to_excel(r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\99Result\\'+'CBMS_v_'+sn+'.xlsx')
+            # df_temp.to_excel(r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\99Result\\'+'CBMS_t_'+sn+'.xlsx')
+            # print(df_volt['1.0'])
+
+            BatSoh=CBMSBatSoh.BatSoh(sn,celltype,df_bms,df_volt,df_temp,df_accum)
+            df_res=BatSoh.batsoh()
+            df_res.to_excel(r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\99Result\\'+'CBMS_SOH_'+sn+'.xlsx')
+            print(sn,'done!!!')
+             
+        except IndexError as e:
+            print(repr(e))
+            mylog.logopt(sn,e)
+            pass

+ 132 - 0
USER/SPF/WORK/03hezhong/02BatUniform/BatParam.py

@@ -0,0 +1,132 @@
+
+#定义电池参数
+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 = 103
+            self.PackFullChrgVolt=69.99
+            self.CellFullChrgVolt=3.5
+            self.OcvInflexionBelow=3.285
+            self.OcvInflexion2=3.296
+            self.OcvInflexion3=3.328
+            self.OcvInflexionAbove=3.4
+            self.CellVoltNums=20
+            self.CellTempNums=4
+            self.FullChrgSoc=98
+            self.PeakSoc=59
+            self.PeakVoltLowLmt=3.35
+            self.PeakVoltUpLmt=3.4
+            self.PeakCellVolt=[3.362,3.363,3.365,3.366,3.367]
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0.00, 	2.40, 	6.38, 	10.37, 	14.35, 	18.33, 	22.32, 	26.30, 	30.28, 	35.26, 	40.24, 	45.22, 	50.20, 	54.19, 	58.17, 	60.16, 	65.14, 	70.12, 	75.10, 	80.08, 	84.06, 	88.05, 	92.03, 	96.02, 	100.00]
+            self.LookTab_OCV = [2.7151,	3.0298,	3.1935,	3.2009,	3.2167,	3.2393,	3.2561,	3.2703,	3.2843,	3.2871,	3.2874,	3.2868,	3.2896,	3.2917,	3.2967,	3.3128,	3.3283,	3.3286,	3.3287,	3.3288,	3.3289,	3.3296,	3.3302,	3.3314,	3.3429]
+        
+        elif celltype==100:   #CATL 102ah三元电芯
+            self.Capacity = 102
+            self.PackFullChrgVolt=80
+            self.CellFullChrgVolt=4.2
+            self.CellVoltNums=96
+            self.CellTempNums=16
+            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]
+            self.LookTab_OCV = [3.397,	3.429,	3.462,	3.507,	3.551,	3.575,	3.600,	3.617,	3.633,	3.658,	3.683,	3.735,	3.786,	3.836,	3.887,	3.939,	3.992,	4.053,	4.113,	4.195,	4.277]
+
+            self.CellTempHighLv1=45
+            self.CellTempHighLv2=50
+            self.CellTempLowLv1=-20
+            self.CellTempLowLv2=-25
+            self.CellTempDiffLv1=10
+            self.CellTempDiffLv2=15
+            self.CellTempRate=0.99
+
+            self.CellOvLv1=4.3
+            self.CellOvLv2=4.35
+            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=1.5*self.Capacity
+            self.PackDisChgOc=3*self.Capacity
+
+            self.SocJump=3
+            self.SocClamp=3
+
+        else:
+            print('未找到对应电池编号!!!')
+            # sys.exit()
+

+ 387 - 0
USER/SPF/WORK/03hezhong/02BatUniform/CBMSBatUniform.py

@@ -0,0 +1,387 @@
+import pandas as pd
+import numpy as np
+import datetime
+import bisect
+import matplotlib.pyplot as plt
+import BatParam
+
+class BatUniform:
+    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.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 batuniform(self):
+        if self.celltype==1 or self.celltype==2 or self.celltype==3 or self.celltype==4 or self.celltype==100:
+            df_res=self._ncm_uniform()
+            return df_res
+                     
+        elif self.celltype==99:
+            df_res=self._lfp_uniform()
+            return df_res
+        
+        else:
+            return pd.DataFrame()
+    
+    #定义滑动滤波函数........................................................................................................................................
+    def _np_move_avg(self,a, n, mode="same"): 
+        return (np.convolve(a, np.ones((n,)) / n, mode=mode))
+    
+    #寻找当前行数据的最小温度值................................................................................................................................
+    def _celltemp_weight(self,num):   
+        celltemp = []
+        for j in range(1, self.param.CellTempNums+1):
+            s = str(j)
+            celltemp.append(self.df_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.6
+                self.StandardStandingTime=3600
+            elif min(celltemp)>=5:
+                self.tempweight=0.
+                self.StandardStandingTime=3600
+            else:
+                self.tempweight=0.1
+                self.StandardStandingTime=7200
+        else:
+            if min(celltemp)>=20:
+                self.tempweight=1
+                self.StandardStandingTime=1800
+            elif min(celltemp)>=10:
+                self.tempweight=0.8
+                self.StandardStandingTime=2400
+            elif min(celltemp)>=5:
+                self.tempweight=0.6
+                self.StandardStandingTime=3600
+            else:
+                self.tempweight=0.2
+                self.StandardStandingTime=7200
+        
+    #获取当前行所有电压数据............................................................................................................................
+    def _cellvolt_get(self,num):
+        cellvolt=[]
+        for j in range(1, self.param.CellVoltNums+1): 
+            s = str(j)
+            cellvolt.append(self.df_volt.loc[num,s+'.0'])
+        return(cellvolt)
+
+    #寻找DVDQ的峰值点,并返回..........................................................................................................................
+    def _dvdq_peak(self, time, soc, cellvolt, packcrnt):
+        cellvolt = self._np_move_avg(cellvolt, 3, mode="same")
+        Soc = 0
+        Ah = 0
+        Volt = cellvolt[0]
+        DV_Volt = []
+        DQ_Ah = []
+        DVDQ = []
+        time1 = []
+        soc1 = []
+        soc2 = []
+        xvolt=[]
+
+        for m in range(1, len(time)):
+            Step = (time[m] - time[m - 1]).total_seconds()
+            Soc = Soc - packcrnt[m] * Step * 100 / (3600 * self.param.Capacity)
+            Ah = Ah - packcrnt[m] * Step / 3600
+            if (cellvolt[m]-Volt)>0.0019 and Ah>0:
+                DQ_Ah.append(Ah)
+                DV_Volt.append(cellvolt[m]-Volt)
+                DVDQ.append((DV_Volt[-1])/DQ_Ah[-1])
+                xvolt.append(cellvolt[m])
+                Volt=cellvolt[m]
+                Ah = 0
+                soc1.append(Soc)
+                time1.append(time[m])
+                soc2.append(soc[m])
+
+        #切片,去除前后10min的数据
+        df_Data1 = pd.DataFrame({'time': time1,
+                                'SOC': soc2,
+                                'DVDQ': DVDQ,
+                                'AhSoc': soc1,
+                                'DQ_Ah':DQ_Ah,
+                                'DV_Volt':DV_Volt,
+                                'XVOLT':xvolt})
+        start_time=df_Data1.loc[0,'time']
+        start_time=start_time+datetime.timedelta(seconds=900)
+        end_time=df_Data1.loc[len(time1)-1,'time']
+        end_time=end_time-datetime.timedelta(seconds=1200)
+        if soc2[0]<36:
+            df_Data1=df_Data1[(df_Data1['SOC']>40) & (df_Data1['SOC']<80)]
+        else:
+            df_Data1=df_Data1[(df_Data1['time']>start_time) & (df_Data1['SOC']<80)]
+        df_Data1=df_Data1[(df_Data1['XVOLT']>self.param.PeakVoltLowLmt) & (df_Data1['XVOLT']<self.param.PeakVoltUpLmt)]
+
+        # print(packcrnt[int(len(time)/2)], min(self.celltemp))
+        # ax1 = plt.subplot(3, 1, 1)
+        # plt.plot(df_Data1['XVOLT'],df_Data1['DVDQ'],'g*-')
+        # plt.xlabel('Volt/V')
+        # plt.ylabel('DV/DQ')
+        # plt.legend()
+        # ax1 = plt.subplot(3, 1, 2)
+        # plt.plot(df_Data1['SOC'],df_Data1['XVOLT'],'y*-')
+        # plt.xlabel('SOC/%')
+        # plt.ylabel('Volt/V')
+        # plt.legend()
+        # ax1 = plt.subplot(3, 1, 3)
+        # plt.plot(df_Data1['SOC'], df_Data1['DVDQ'], 'r*-')
+        # plt.xlabel('SOC/%')
+        # plt.ylabel('DV/DQ')
+        # plt.legend()
+        # plt.show()
+
+        if len(df_Data1)>2:     #寻找峰值点,且峰值点个数>2
+            PeakIndex = df_Data1['DVDQ'].idxmax()
+            df_Data2 = df_Data1[(df_Data1['SOC'] > (df_Data1['SOC'][PeakIndex] - 0.5)) & (df_Data1['SOC'] < (df_Data1['SOC'][PeakIndex] + 0.5))]
+            if len(df_Data2) > 2 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.019:
+                return df_Data1['AhSoc'][PeakIndex]
+            else:
+                df_Data1 = df_Data1.drop([PeakIndex])
+                PeakIndex = df_Data1['DVDQ'].idxmax()
+                df_Data2 = df_Data1[(df_Data1['SOC'] > (df_Data1['SOC'][PeakIndex] - 0.5)) & (df_Data1['SOC'] < (df_Data1['SOC'][PeakIndex] + 0.5))]
+                if len(df_Data2) > 2 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.019:
+                    return df_Data1['AhSoc'][PeakIndex]
+                else:
+                    return 0
+        else:
+            return 0
+ 
+    #三元电池一致性计算.................................................................................................................................
+    def _ncm_uniform(self):
+        column_name=['time','sn','cellsoc_diff','cellvolt_diff','cellmin_num','cellmax_num']
+        df_res=pd.DataFrame(columns=column_name)
+        standingtime=0
+
+        for i in range(1,len(self.df_volt)-2):
+
+            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_last=self._cellvolt_get(i-1)
+                        cellvolt_min=min(cellvolt_now)
+                        cellvolt_max=max(cellvolt_now)
+                        cellvolt_maxlast=max(cellvolt_last)
+                        if 3<cellvolt_min<4.5 and 3<cellvolt_max<4.5 and abs(cellvolt_maxlast-cellvolt_max)<0.003:
+                            cellmin_num=cellvolt_now.index(cellvolt_min)+1
+                            cellmax_num=cellvolt_now.index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num]
+                    elif standingtime>3600*12:
+                        standingtime=0                    
+                        cellvolt_now=self._cellvolt_get(i)     #获取当前行电压数据
+                        cellvolt_last=self._cellvolt_get(i-1)
+                        cellvolt_min=min(cellvolt_now)
+                        cellvolt_max=max(cellvolt_now)
+                        cellvolt_maxlast=max(cellvolt_last)
+                        if 3<cellvolt_min<4.5 and 3<cellvolt_max<4.5 and abs(cellvolt_maxlast-cellvolt_max)<0.003:
+                            cellmin_num=cellvolt_now.index(cellvolt_min)+1
+                            cellmax_num=cellvolt_now.index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num]
+                    elif i>=len(self.df_volt)-4:
+                        standingtime=0
+                        cellvolt_now=self._cellvolt_get(i)     #获取当前行电压数据
+                        cellvolt_last=self._cellvolt_get(i-1)
+                        cellvolt_min=min(cellvolt_now)
+                        cellvolt_max=max(cellvolt_now)
+                        cellvolt_maxlast=max(cellvolt_last)
+                        if 3<cellvolt_min<4.5 and 3<cellvolt_max<4.5 and abs(cellvolt_maxlast-cellvolt_max)<0.003:
+                            cellmin_num=cellvolt_now.index(cellvolt_min)+1
+                            cellmax_num=cellvolt_now.index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num]
+                        break
+                    else:
+                        continue
+                else:
+                    continue
+            else:
+                standingtime=0
+                continue
+
+        if df_res.empty:    #返回计算结果
+            return pd.DataFrame()
+        else:
+            return df_res
+
+    #磷酸铁锂电池一致性计算.........................................................................................................................
+    def _lfp_uniform(self):
+        column_name=['time','sn','cellsoc_diff','cellvolt_diff','cellmin_num','cellmax_num']
+        df_res=pd.DataFrame(columns=column_name)
+        standingtime=0
+        chrg_start=[]
+        chrg_end=[]
+        charging=0
+
+        for i in range(3,len(self.df_volt)-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:      #静置时间满足要求
+                    cellvolt_now=self._cellvolt_get(i)     #获取当前行电压数据
+                    cellvolt_last=self._cellvolt_get(i-1)
+                    cellvolt_min=min(cellvolt_now)
+                    cellvolt_max=max(cellvolt_now)
+                    cellvolt_maxlast=max(cellvolt_last)
+                    if abs(self.packcrnt[i+1]) >= 0.1 and abs(cellvolt_maxlast-cellvolt_max)<0.003:     
+                        standingtime=0  
+                        if 2 < cellvolt_max < self.param.OcvInflexionBelow-0.02:                 
+                            cellmin_num=cellvolt_now.index(cellvolt_min)+1
+                            cellmax_num=cellvolt_now.index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num]
+                    elif i>=len(self.df_volt)-4:
+                        standingtime=0
+                        if 2 < cellvolt_max < self.param.OcvInflexionBelow-0.02:
+                            cellmin_num=cellvolt_now.index(cellvolt_min)+1
+                            cellmax_num=cellvolt_now.index(cellvolt_max)+1
+                            cellsoc_min=np.interp(cellvolt_min,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellsoc_max=np.interp(cellvolt_max,self.param.LookTab_OCV,self.param.LookTab_SOC)
+                            cellvolt_diff=(cellvolt_max-cellvolt_min)*1000
+                            cellsoc_diff=cellsoc_max-cellsoc_min
+                            cellsoc_diff=eval(format(cellsoc_diff,'.1f'))
+                            cellvolt_diff=eval(format(cellvolt_diff,'.0f'))
+                            df_res.loc[len(df_res)]=[self.bmstime[i], self.sn, cellsoc_diff, cellvolt_diff, cellmin_num, cellmax_num]
+                    else:
+                        pass
+                else:
+                    pass
+            else:
+                standingtime=0
+                pass
+
+            #获取DVDQ算法所需数据——开始............................................................................................................
+            if charging==0: #判断充电开始
+                if self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and self.packcrnt[i+2]<=-1 and float(self.bms_soc[i].strip('%'))<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/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+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 float(self.bms_soc[i].strip('%'))>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 float(self.bms_soc[i].strip('%'))>90:   #soc>90
+                            chrg_end.append(i)
+                            continue
+                        else:
+                            chrg_start.remove(chrg_start[-1])
+                            continue
+                    else:
+                        continue
+                else:
+                    continue   
+
+        # if chrg_end:    #DVDQ方法计算soc差
+        #     peaksoc_list=[]
+        #     for i in range(len(chrg_end)):
+        #         peaksoc_list = []
+        #         self._celltemp_weight(chrg_start[i])
+        #         if min(self.celltemp)>10:
+        #             for j in range(1, self.param.CellVoltNums + 1):
+        #                 s = str(j)
+        #                 cellvolt = self.df_volt[s+'.0']
+        #                 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

+ 24 - 0
USER/SPF/WORK/03hezhong/02BatUniform/log.py

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

+ 58 - 0
USER/SPF/WORK/03hezhong/02BatUniform/main.py

@@ -0,0 +1,58 @@
+import CBMSBatUniform
+import log
+#coding=utf-8
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+from sqlalchemy import create_engine
+import time, datetime
+import os
+
+dbManager = DBManager.DBManager()
+if __name__ == "__main__":
+    filepath=r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\98Download'
+    files=os.listdir(filepath)
+
+    # now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+    # now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+    # start_time=now_time-datetime.timedelta(days=30)
+    # end_time=str(now_time)
+    # start_time=str(start_time)
+
+    #log信息配置
+    mylog=log.Mylog('log.txt','error')
+    mylog.logcfg()
+
+    for filename in files:
+        try:
+            # filename=files[8]
+            print(filename)
+            sn=filename[7:17]
+            celltype=100
+            start_time='2021-04-01 19:12:26'
+            end_time='2021-09-02 19:12:26'
+            df_volt = pd.read_excel(filepath+'\\'+filename,sheet_name='单体电池电压数据')
+            df_temp = pd.read_excel(filepath+'\\'+filename,sheet_name='单体电池温度数据')
+            df_bms = pd.read_excel(filepath+'\\'+filename,sheet_name='整车数据')
+            df_volt.columns=df_volt.loc[0].astype('str')
+            df_temp.columns=df_temp.loc[0].astype('str')
+            df_volt=df_volt.drop(0,axis=0)
+            df_temp=df_temp.drop(0,axis=0)
+            df_volt=df_volt.reset_index(drop=True)
+            df_temp=df_temp.reset_index(drop=True)
+            
+            df_accum=pd.DataFrame()
+
+            # df_volt.to_excel(r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\99Result\\'+'CBMS_v_'+sn+'.xlsx')
+            # df_temp.to_excel(r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\99Result\\'+'CBMS_t_'+sn+'.xlsx')
+            # print(df_volt['1.0'])
+
+            BatUniform=CBMSBatUniform.BatUniform(sn,celltype,df_bms,df_volt,df_temp,df_accum)
+            df_res=BatUniform.batuniform()
+            df_res.to_excel(r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\99Result\\'+'CBMS_Uniform_'+sn+'.xlsx')
+            print(sn,'done!!!')
+             
+        except IndexError as e:
+            print(repr(e))
+            mylog.logopt(sn,e)
+            pass

+ 582 - 0
USER/SPF/WORK/03hezhong/03BatInterShort/CBMSBatInterShort.py

@@ -0,0 +1,582 @@
+import pandas as pd
+import numpy as np
+import matplotlib.pyplot as plt
+import BatParam
+
+class BatInterShort:
+    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.df_bms=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.bmsstat=df_bms['充电状态']
+        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 intershort(self):
+        if self.celltype==1 or self.celltype==2 or self.celltype==3 or self.celltype==4 or self.celltype==100:
+            df_res=self._ncm_intershort()
+            return df_res
+            
+        elif self.celltype==99:
+            df_res=self._lfp_intershort()
+            return df_res
+        
+        else:
+            return pd.DataFrame()
+
+    #定义滑动滤波函数....................................................................................
+    def _np_move_avg(self,a, n, mode="same"): 
+        return (np.convolve(a, np.ones((n,)) / n, mode=mode))
+    
+    #寻找当前行数据的最小温度值.............................................................................
+    def _celltemp_weight(self,num):   
+        celltemp = []
+        for j in range(1, self.param.CellTempNums+1):
+            s = str(j)
+            celltemp.append(self.df_temp.loc[num,s+'.0'])
+        celltemp=np.mean(celltemp)
+        self.celltemp=celltemp
+        if self.celltype==99:
+            if celltemp>=20:
+                self.tempweight=1
+                self.StandardStandingTime=3600
+            elif celltemp>=10:
+                self.tempweight=0.6
+                self.StandardStandingTime=7200
+            elif celltemp>=5:
+                self.tempweight=0.
+                self.StandardStandingTime=7200
+            else:
+                self.tempweight=0.1
+                self.StandardStandingTime=10800
+        else:
+            if celltemp>=20:
+                self.tempweight=1
+                self.StandardStandingTime=3600
+            elif celltemp>=10:
+                self.tempweight=0.8
+                self.StandardStandingTime=3600
+            elif celltemp>=5:
+                self.tempweight=0.6
+                self.StandardStandingTime=7200
+            else:
+                self.tempweight=0.2
+                self.StandardStandingTime=10800
+
+    #获取当前行所有电压数据........................................................................................
+    def _cellvolt_get(self,num): 
+        cellvolt=[]
+        for j in range(1, self.param.CellVoltNums+1): 
+            s = str(j)
+            cellvolt.append(self.df_volt.loc[num, s+'.0'])
+        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_volt.loc[num,str(j)+'.0']
+            ocv_soc=np.interp(cellvolt,self.param.LookTab_OCV,self.param.LookTab_SOC)
+            if j in dict_baltime.keys():
+                ocv_soc=ocv_soc+dict_baltime[j]*self.param.BalCurrent/(capacity*3600)   #补偿均衡电流
+            else:
+                pass
+            cellsoc.append(ocv_soc)
+        
+        if self.celltype==1 or self.celltype==2:
+            consum_num=7
+            cellsoc1=cellsoc[:self.param.CellVoltNums-consum_num]    #切片,将bms耗电的电芯和非耗电的电芯分离开
+            cellsocmean1=(sum(cellsoc1)-max(cellsoc1)-min(cellsoc1))/(len(cellsoc1)-2)
+            cellsoc2=cellsoc[self.param.CellVoltNums-consum_num:]
+            cellsocmean2=(sum(cellsoc2)-max(cellsoc2)-min(cellsoc2))/(len(cellsoc2)-2)
+            
+            for j in range(len(cellsoc)):   #计算每个电芯的soc差
+                if j<self.param.CellVoltNums-consum_num:
+                    celldeltsoc.append(cellsoc[j]-cellsocmean1)
+                else:
+                    celldeltsoc.append(cellsoc[j]-cellsocmean2)
+            return celldeltsoc
+
+        else:
+            cellsocmean=(sum(cellsoc)-max(cellsoc)-min(cellsoc))/(len(cellsoc)-2)
+            for j in range(len(cellsoc)):   #计算每个电芯的soc差
+                celldeltsoc.append(cellsoc[j]-cellsocmean)
+            return celldeltsoc
+ 
+    #获取所有电芯的As差
+    def _cellDeltAs_get(self,chrg_st,chrg_end,dict_baltime):
+        cellAs=[]
+        celldeltAs=[]
+        for j in range(1, self.param.CellVoltNums+1):   #获取每个电芯电压>峰值电压的充入As数
+            if j in dict_baltime.keys():    #补偿均衡电流
+                As=-self.param.BalCurrent*dict_baltime[j]
+            else:    
+                As=0
+            As_tatol=0
+            symbol=0
+            for m in range(chrg_st,chrg_end):
+                As=As-self.packcrnt[m]*(self.bmstime[m]-self.bmstime[m-1]).total_seconds()
+                if symbol<5:
+                    if self.df_volt.loc[m, str(j)+'.0']/1000>self.param.PeakCellVolt[symbol]:
+                        As_tatol=As_tatol+As
+                        symbol=symbol+1
+                    else:
+                        continue
+                else:
+                    cellAs.append(As_tatol/5)
+                    break
+        if cellAs:
+            cellAsmean=(sum(cellAs)-max(cellAs)-min(cellAs))/(len(cellAs)-2)
+            for j in range(len(cellAs)):   #计算每个电芯的soc差
+                celldeltAs.append(cellAs[j]-cellAsmean)
+            
+        return celldeltAs
+
+    #计算每个电芯的均衡时长..........................................................................................................................
+    def _bal_time(self,dict_bal):
+        dict_baltime={}
+        dict_baltime1={}
+        for key in dict_bal:
+            count=1
+            x=eval(key)
+            while x>0:
+                if x & 1==1:    #判断最后一位是否为1
+                    if count in dict_baltime.keys():
+                        dict_baltime[count] = dict_baltime[count] + dict_bal[key]
+                    else:
+                        dict_baltime[count] = dict_bal[key]
+                else:
+                    pass
+                count += 1
+                x >>= 1    #右移一位
+        
+        dict_baltime=dict(sorted(dict_baltime.items(),key=lambda dict_baltime:dict_baltime[0]))
+        for key in dict_baltime:    #解析均衡的电芯编号
+            if self.celltype==1:    #科易6040
+                if key<14:
+                    dict_baltime1[key]=dict_baltime[key]
+                elif key<18:
+                    dict_baltime1[key-1]=dict_baltime[key]
+                else:
+                    dict_baltime1[key-3]=dict_baltime[key]
+            elif self.celltype==1:    #科易4840
+                if key<4:
+                    dict_baltime1[key-1]=dict_baltime[key]
+                elif key<8:
+                    dict_baltime1[key-1]=dict_baltime[key]
+                elif key<14:
+                    dict_baltime1[key-3]=dict_baltime[key]
+                elif key<18:
+                    dict_baltime1[key-4]=dict_baltime[key]
+                else:
+                    dict_baltime1[key-6]=dict_baltime[key]
+            else:
+                dict_baltime1=dict_baltime
+        return dict_baltime1
+
+    #三元电池的内短路电流计算...........................................................................................................................................................
+    def _ncm_intershort(self):
+        column_name=['time_st', 'time_sp', 'sn', 'method','short_current','baltime']
+        df_res=pd.DataFrame(columns=column_name)
+
+        df_res1=pd.DataFrame()
+        
+        if not self.df_volt.empty:
+            capacity=self.param.Capacity
+
+            standingtime=0
+            standingtime1=0
+            firsttime=1
+            firsttime1=1
+            dict_bal={}
+            dict_bal1={}
+
+            for i in range(2,len(self.df_volt)-2):
+
+                if firsttime1==0:   #满电静置算法--计算均衡状态对应的均衡时间
+                    try:
+                        balstat=int(self.df_bms.loc[i,'单体均衡状态'])
+                        if balstat>0.5:
+                            bal_step=(self.bmstime[i+1]-self.bmstime[i]).total_seconds()  #均衡步长
+                            bal_step=int(bal_step)
+                            if str(balstat) in dict_bal1.keys():
+                                dict_bal1[str(balstat)]=dict_bal1[str(balstat)]+bal_step
+                            else:
+                                dict_bal1[str(balstat)]=bal_step
+                        else:
+                            pass
+                    except:
+                        dict_bal1={}
+                else:
+                    pass
+
+                if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1:     
+                    delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                    standingtime=standingtime+delttime
+                    standingtime1=standingtime1+delttime
+                    self._celltemp_weight(i)
+
+                    #静置法计算内短路-开始.....................................................................................................................................
+                    if firsttime==1:    
+                        if standingtime>self.StandardStandingTime:      #静置时间满足要求
+                            dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                            deltsoc_last=self._celldeltsoc_get(i,dict_baltime,capacity)
+                            time_last=self.bmstime[i]
+                            firsttime=0
+                            standingtime=0
+                        else:
+                            pass                
+                    elif standingtime>3600*12:
+                        cellvolt=self._cellvolt_get(i)
+                        if 3<max(cellvolt)<4.5:
+                            standingtime=0
+                            dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                            deltsoc_now=self._celldeltsoc_get(i,dict_baltime,capacity)
+                            time_now=self.bmstime[i]
+                            
+                            list_sub=[a-b for a, b in zip(deltsoc_now, deltsoc_last)]
+                            list_pud=[0.01*capacity*3600*1000/(time_now-time_last).total_seconds()]*self.param.CellVoltNums
+                            leak_current=[a*b for a,b in zip(list_sub,list_pud)]
+                            leak_current=np.array(leak_current)
+                            leak_current=np.round(leak_current,3)
+                            leak_current=list(leak_current)
+                            
+                            df_res.loc[len(df_res)]=[time_last,time_now,self.sn,1,str(leak_current),str(dict_baltime)]  #计算结果存入Dataframe
+                            df_res1[time_now]=leak_current
+                            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:
+                        cellvolt=self._cellvolt_get(i)
+                        if 3<max(cellvolt)<4.5:  
+                            if abs(self.packcrnt[i+2]) >= 0.1:
+                                standingtime1=0
+                                cellvolt_now1=self._cellvolt_get(i)
+                                cellsoc_now1=np.interp(max(cellvolt_now1),self.param.LookTab_OCV,self.param.LookTab_SOC)
+                                if cellsoc_now1>=self.param.FullChrgSoc-50:
+                                    if firsttime1==1:
+                                        dict_baltime1=self._bal_time(dict_bal1)   #获取每个电芯的均衡时间
+                                        deltsoc_last1=self._celldeltsoc_get(i,dict_baltime1,capacity)
+                                        time_last1=self.bmstime[i]
+                                        firsttime1=0
+                                    else:
+                                        dict_baltime1=self._bal_time(dict_bal1)   #获取每个电芯的均衡时间
+                                        time_now1=self.bmstime[i]
+                                        if (time_now1-time_last1).total_seconds()>3600*24:
+                                            deltsoc_now1=self._celldeltsoc_get(i,dict_baltime1,capacity)
+
+                                            list_sub1=[a-b for a, b in zip(deltsoc_now1, deltsoc_last1)]
+                                            list_pud1=[0.01*capacity*3600*1000/(time_now1-time_last1).total_seconds()]*self.param.CellVoltNums
+                                            leak_current1=[a*b for a,b in zip(list_sub1,list_pud1)]
+                                            leak_current1=np.array(leak_current1)
+                                            leak_current1=np.round(leak_current1,3)
+                                            leak_current1=list(leak_current1)
+                                            
+                                            df_res.loc[len(df_res)]=[time_last1,time_now1,self.sn,2,str(leak_current1),str(dict_baltime1)]  #计算结果存入Dataframe
+                                            df_res1[time_now1]=leak_current1
+                                            time_last1=time_now1  #更新时间
+                                            deltsoc_last1=deltsoc_now1    #更新soc差
+                                            dict_bal1={}
+                                        else:
+                                            pass
+                                else:
+                                    pass
+                            else:
+                                pass
+                    else:   
+                        pass
+
+                else:
+                    dict_bal={} 
+                    firsttime=1
+                    standingtime=0
+                    standingtime1=0
+                    pass
+            #内短路结果画图.................................................................................................    
+            if not df_res1.empty:
+                ax=df_res1.plot(marker='*',figsize=(10,5))
+                plt.xlabel('电芯序列')
+                plt.ylabel('内短路指数')
+                plt.title(str(self.sn))
+                plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
+                plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
+                plt.legend()
+                plt.show()
+                fig = ax.get_figure()
+                fig.savefig('./'+str(self.sn)+'漏电流1.png')
+
+        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)
+        df_res1=pd.DataFrame()
+
+        if not self.df_bms.empty:
+            capacity=self.param.Capacity
+            standingtime=0
+            standingtime1=0
+            firsttime=1
+            firsttime1=1
+            dict_bal={}
+            dict_bal1={}
+
+            chrg_start=[]
+            chrg_end=[]
+            dict_bal_list=[]
+            charging=0
+
+            for i in range(3,len(self.df_bms)-3):
+
+                #静置法计算内短路..........................................................................................................................
+                if firsttime1==0:   #满电静置算法--计算均衡状态对应的均衡时间
+                    try:
+                        balstat=int(self.df_bms.loc[i,'单体均衡状态'])
+                        if balstat>0.5:
+                            bal_step=(self.bmstime[i+1]-self.bmstime[i]).total_seconds()  #均衡步长
+                            bal_step=int(bal_step)
+                            if str(balstat) in dict_bal1.keys():
+                                dict_bal1[str(balstat)]=dict_bal1[str(balstat)]+bal_step
+                            else:
+                                dict_bal1[str(balstat)]=bal_step
+                        else:
+                            pass
+                    except:
+                        dict_bal1={}
+                else:
+                    pass
+
+                if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1:     
+                    delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
+                    standingtime=standingtime+delttime
+                    standingtime1=standingtime1+delttime
+                    self._celltemp_weight(i)
+
+                    #静置法计算内短路-开始.....................................................................................................................................
+                    if firsttime==1:    
+                        if standingtime>self.StandardStandingTime:      #静置时间满足要求
+                            cellvolt=self._cellvolt_get(i)
+                            if max(cellvolt)<self.param.OcvInflexionBelow-0.002 and 3<max(cellvolt)<4.5:
+                                dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                                deltsoc_last=self._celldeltsoc_get(i,dict_baltime,capacity)
+                                time_last=self.bmstime[i]
+                                firsttime=0
+                                standingtime=0
+                            else:
+                                pass
+                        else:
+                            pass                
+                    elif standingtime>3600*12:
+                        cellvolt=self._cellvolt_get(i)
+                        if max(cellvolt)<self.param.OcvInflexionBelow-0.002 and 3<max(cellvolt)<4.5:
+                            standingtime=0
+                            dict_baltime=self._bal_time(dict_bal)   #获取每个电芯的均衡时间
+                            deltsoc_now=self._celldeltsoc_get(i, dict_baltime,capacity)    #获取每个电芯的SOC差
+                            time_now=self.bmstime[i]
+
+                            list_sub=[a-b for a, b in zip(deltsoc_now, deltsoc_last)]
+                            list_pud=[0.01*capacity*3600*1000/(time_now-time_last).total_seconds()]*self.param.CellVoltNums
+                            leak_current=[a*b for a,b in zip(list_sub,list_pud)]
+                            leak_current=np.array(leak_current)
+                            leak_current=np.round(leak_current,3)
+                            leak_current=list(leak_current)
+                            
+                            df_res.loc[len(df_res)]=[time_last,time_now,self.sn,1,str(leak_current),str(dict_baltime)]  #计算结果存入Dataframe
+                            df_res1[time_now]=leak_current
+                            time_last=time_now  #更新时间
+                            deltsoc_last=deltsoc_now    #更新soc差
+                            dict_bal={}
+                        else:
+                            pass
+                    else: 
+                        try:  
+                            balstat=int(self.df_bms.loc[i,'单体均衡状态'])
+                            if balstat>0.5:
+                                bal_step=(self.bmstime[i+1]-self.bmstime[i]).total_seconds()  #均衡步长
+                                bal_step=int(bal_step)
+                                if str(balstat) in dict_bal.keys():
+                                    dict_bal[str(balstat)]=dict_bal[str(balstat)]+bal_step
+                                else:
+                                    dict_bal[str(balstat)]=bal_step
+                            else:
+                                pass
+                        except:
+                            dict_bal={}
+
+                    #非平台区间静置法计算内短路-开始.....................................................................................................................................................
+                    if standingtime1>self.StandardStandingTime: 
+                        if abs(self.packcrnt[i+2]) >= 0.1: 
+                            standingtime1=0
+                            cellvolt_now1=self._cellvolt_get(i)
+                            if max(cellvolt_now1)<self.param.OcvInflexionBelow-0.002 and 3<max(cellvolt)<4.5:
+                                if firsttime1==1:
+                                    dict_baltime1=self._bal_time(dict_bal1)   #获取每个电芯的均衡时间
+                                    deltsoc_last1=self._celldeltsoc_get(i,dict_baltime1,capacity)
+                                    time_last1=self.bmstime[i]
+                                    firsttime1=0
+                                else:
+                                    dict_baltime1=self._bal_time(dict_bal1)   #获取每个电芯的均衡时间
+                                    deltsoc_now1=self._celldeltsoc_get(i,dict_baltime1,capacity)
+                                    time_now1=self.bmstime[i]
+
+                                    time_now1=self.bmstime[i]
+                                    if abs(max(deltsoc_now1)-max(deltsoc_last1))<10 and (time_now1-time_last1).total_seconds()>3600*24:
+                                        list_sub1=[a-b for a, b in zip(deltsoc_now1, deltsoc_last1)]
+                                        list_pud1=[0.01*capacity*3600*1000/(time_now1-time_last1).total_seconds()]*self.param.CellVoltNums
+                                        leak_current1=[a*b for a,b in zip(list_sub1,list_pud1)]
+                                        leak_current1=np.array(leak_current1)
+                                        leak_current1=np.round(leak_current1,3)
+                                        leak_current1=list(leak_current1)
+                                    
+                                        
+                                        df_res.loc[len(df_res)]=[time_last1,time_now1,self.sn,2,str(leak_current1),str(dict_baltime1)]  #计算结果存入Dataframe
+                                        df_res1[time_now1]=leak_current1
+                                        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 float(self.bms_soc[i].strip('%'))<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=[a-b for a, b in zip(deltAs_now, deltAs_last)]
+                        list_pud=[-1000/(time_now-time_last).total_seconds()]*self.param.CellVoltNums
+                        leak_current=[a*b for a,b in zip(list_sub,list_pud)]
+                        leak_current=np.array(leak_current)
+                        leak_current=np.round(leak_current,3)
+                        leak_current=list(leak_current)
+
+                        df_res.loc[len(df_res)]=[time_last,time_now,self.sn,3,str(leak_current),str(dict_baltime)]  #计算结果存入Dataframe
+                        deltAs_last=deltAs_now
+                        time_last=time_now
+            else:
+                pass
+            #漏电流结果画图...........................................................................................
+            if not df_res1.empty:
+                ax=df_res1.plot(marker='*',figsize=(10,5))
+                plt.xlabel('电芯序列')
+                plt.ylabel('内短路指数')
+                plt.title(str(self.sn))
+                plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
+                plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
+                plt.legend()
+                plt.show()
+                fig = ax.get_figure()
+                fig.savefig('./'+str(self.sn)+'漏电流1.png')
+        if df_res.empty:
+            return pd.DataFrame()
+        else:
+            return df_res

+ 56 - 0
USER/SPF/WORK/03hezhong/03BatInterShort/main.py

@@ -0,0 +1,56 @@
+import CBMSBatInterShort
+import log
+#coding=utf-8
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager
+from sqlalchemy import create_engine
+import datetime
+import os
+
+dbManager = DBManager.DBManager()
+if __name__ == "__main__":
+    filepath=r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\98Download'
+    files=os.listdir(filepath)
+
+    # now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+    # now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+    # start_time=now_time-datetime.timedelta(days=30)
+    # end_time=str(now_time)
+    # start_time=str(start_time)
+
+    #log信息配置
+    mylog=log.Mylog('log.txt','error')
+    mylog.logcfg()
+
+    for filename in files:
+        try:
+            # filename=files[7]
+            sn=filename[7:-5]
+            print(sn)
+            celltype=100
+            df_volt = pd.read_excel(filepath+'\\'+filename,sheet_name='单体电池电压数据')
+            df_temp = pd.read_excel(filepath+'\\'+filename,sheet_name='单体电池温度数据')
+            df_bms = pd.read_excel(filepath+'\\'+filename,sheet_name='整车数据')
+            df_volt.columns=df_volt.loc[0].astype('str')
+            df_temp.columns=df_temp.loc[0].astype('str')
+            df_volt=df_volt.drop(0,axis=0)
+            df_temp=df_temp.drop(0,axis=0)
+            df_volt=df_volt.reset_index(drop=True)
+            df_temp=df_temp.reset_index(drop=True)
+            
+            df_accum=pd.DataFrame()
+
+            # df_volt.to_excel(r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\99Result\\'+'CBMS_v_'+sn+'.xlsx')
+            # df_temp.to_excel(r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\99Result\\'+'CBMS_t_'+sn+'.xlsx')
+            # print(df_volt['1.0'])
+
+            BatInterShort=CBMSBatInterShort.BatInterShort(sn,celltype,df_bms,df_volt,df_temp,df_accum)
+            df_res=BatInterShort.intershort()
+            df_res.to_excel(r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\99Result\\'+'CBMS_InterShort_'+sn+'.xlsx')
+            print(sn,'done!!!')
+             
+        except IndexError as e:
+            print(repr(e))
+            mylog.logopt(sn,e)
+            pass

+ 156 - 0
USER/SPF/WORK/03hezhong/04BatDiag/BatParam.py

@@ -0,0 +1,156 @@
+
+#定义电池参数
+from types import CellType
+import sys
+
+class BatParam:
+
+    def __init__(self,celltype):
+
+       #故障诊断参数
+        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
+
+        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]
+
+            self.CellTempHighLv1=45
+            self.CellTempHighLv2=50
+            self.CellTempLowLv1=-20
+            self.CellTempLowLv2=-25
+            self.CellTempDiffLv1=10
+            self.CellTempDiffLv2=15
+            self.CellTempRate=0.99
+
+            self.CellOvLv1=3.65
+            self.CellOvLv2=3.7
+            self.CellUvLv1=2.1
+            self.CellUvLv2=2.0
+            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=-1.5*self.Capacity
+            self.PackDisOc=3*self.Capacity
+
+            self.SocJump=10
+            self.SocClamp=10
+        
+        elif celltype==100:   #CATL 102ah三元电芯
+            self.Capacity = 102
+            self.PackFullChrgVolt=80
+            self.CellFullChrgVolt=4.2
+            self.CellVoltNums=96
+            self.CellTempNums=16
+            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]
+            self.LookTab_OCV = [3.397,	3.429,	3.462,	3.507,	3.551,	3.575,	3.600,	3.617,	3.633,	3.658,	3.683,	3.735,	3.786,	3.836,	3.887,	3.939,	3.992,	4.053,	4.113,	4.195,	4.277]
+
+            self.CellTempHighLv1=45
+            self.CellTempHighLv2=50
+            self.CellTempLowLv1=-20
+            self.CellTempLowLv2=-25
+            self.CellTempDiffLv1=10
+            self.CellTempDiffLv2=15
+            self.CellTempRate=0.99
+
+            self.CellOvLv1=4.3
+            self.CellOvLv2=4.35
+            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=-1.5*self.Capacity
+            self.PackDisOc=3*self.Capacity
+
+            self.SocJump=10
+            self.SocClamp=10
+
+        else:
+            print('未找到对应电池编号!!!')
+            # sys.exit()
+

+ 1006 - 0
USER/SPF/WORK/03hezhong/04BatDiag/CBMSBatDiag copy 2.py

@@ -0,0 +1,1006 @@
+import pandas as pd
+import numpy as np
+import bisect
+import datetime
+import BatParam
+
+class BatDiag:
+    def __init__(self,sn,celltype,df_bms,df_volt,df_temp,df_diag,df_diag_ram, df_soh, df_sor):  #参数初始化
+
+        self.sn=sn
+        self.celltype=celltype
+
+        self.param=BatParam.BatParam(self.celltype)
+        self.df_volt=df_volt
+        self.df_temp=df_temp
+        self.df_soh=df_soh
+        self.df_sor=df_sor
+        self.packcrnt=(df_volt['可充电储能装置电流(A)'].astype('float'))*self.param.PackCrntDec
+        self.packvolt=df_volt['可充电储能装置电压(V)'].astype('float')
+        self.bmssoc=df_bms['SOC'].map(lambda x:x.strip('%'))
+        self.HIVLLk=df_diag['高压互锁状态']
+        self.ISO=df_diag['绝缘']
+        self.enmtemp=df_bms['EnmTemp']
+        # self.bms_soh=df_volt['SOH[%]']
+        self.bmstime= pd.to_datetime(df_volt['上报时间'], format='%Y-%m-%d %H:%M:%S')
+        self.param.CellTempNums=int(df_temp.loc[5,'可充电储能温度探针个数'])
+        self.df_diag_ram=df_diag_ram
+        
+        self.cellvolt_name=[str(x)+'.0' for x in range(1,self.param.CellVoltNums+1)]
+        self.celltemp_name=[str(x)+'.0' 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 _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 _celltemp_rate(self,num):
+        celltemp_rate={}
+        if num>0:
+            for j in range(1, self.param.CellTempNums+1):
+                time_now=self.bmstime[num]
+                for k in range(1,num):
+                    if (time_now-self.bmstime[num-k]).total_seconds()>60:
+                        num_last=num-k
+                        celltemp_now=self.df_temp.loc[num,str(j)+'.0']
+                        celltemp_last=self.df_temp.loc[num_last,str(j)+'.0']
+                        time_last=self.bmstime[num_last]
+                        temp_rate=(celltemp_now-celltemp_last)*60/(time_now-time_last).total_seconds()
+                        if temp_rate>self.param.CellTempRate:
+                            celltemp_rate[j]=temp_rate
+                        break
+                    else:
+                        pass
+        else:
+            pass
+        return celltemp_rate
+
+    #..........................................三元电池诊断功能..................................................................
+    def _ncm_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
+        voltstray_time=0
+        tempstray_time=0
+        packvoltvalid_time=0
+        tempvalid_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
+        
+        sor=eval(self.df_sor.loc[0,'sor'])
+        sor_mean=np.mean(sor)
+            
+        for i in range(1,len(self.df_volt)-1):
+            df_diag_now=self.df_diag_ram[self.df_diag_ram['end_time']=='0000-00-00 00:00:00']
+        
+            #电压诊断功能.........................................................................................................................................
+            if i<=1:
+                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)
+                
+                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
+                
+                celltemp1=self._celltemp_get(i-1)
+                celltemp2=self._celltemp_get(i)
+                celltempmin1=min(celltemp1)
+                celltempmin2=min(celltemp2)
+                celltempmax1=max(celltemp1)
+                celltempmax2=max(celltemp2)
+                
+                celltemp1_std=np.std(celltemp1)
+                celltemp1_mean=np.mean(celltemp1)
+                celltemp1_3sigma=(np.array(celltemp1)-celltemp1_mean)/celltemp1_std
+                celltemp2_std=np.std(celltemp2)
+                celltemp2_mean=np.mean(celltemp2)
+                celltemp2_3sigma=(np.array(celltemp2)-celltemp2_mean)/celltemp2_std
+            else:
+                time1=self.bmstime[i-1]
+                time2=self.bmstime[i]
+                cellvolt1=cellvolt2
+                cellvoltmin1=cellvoltmin2
+                cellvoltmax1=cellvoltmax2
+                cellvoltmin_index1=cellvoltmin_index2
+                cellvoltmax_index1=cellvoltmax_index2
+                cellvolt2=self._cellvolt_get(i)
+                cellvoltmin2=min(cellvolt2)
+                cellvoltmax2=max(cellvolt2)
+                cellvoltmin_index2=cellvolt2.index(cellvoltmin2)
+                cellvoltmax_index2=cellvolt2.index(cellvoltmax2)
+                
+                cellvolt1_std=cellvolt2_std
+                cellvolt1_mean=cellvolt2_mean
+                cellvolt1_3sigma=cellvolt2_3sigma
+                cellvolt2_std=np.std(cellvolt2)
+                cellvolt2_mean=np.mean(cellvolt2)
+                cellvolt2_3sigma=(np.array(cellvolt2)-cellvolt2_mean)/cellvolt2_std
+                
+            #电压采样断线..........................................................................................................................................
+            if not 'C308' in list(df_diag_now['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='电芯电压无效'
+                        reason='电芯电压采样线断线'
+                        faultlocation='电芯{}'.format([cellvoltmin_index2+1, cellvoltmax_index2+1])
+                        faultadvice='召回电池包,进行检修'
+                        influence='失去对电芯电压监测'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                    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='电芯电压无效'
+                        reason='电芯电压采样线断线'
+                        faultlocation='电芯{}'.format([cellvoltmin_index2+1, cellvoltmax_index2+1])
+                        faultadvice='召回电池包,进行检修'
+                        influence='失去对电芯电压监测'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                    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']=='C309'].index[-1], 'end_time'] = time
+                else:
+                    pass
+            
+            #电压采样系统失效.............................................................................................................
+            if not 'C309' in list(df_diag_now['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 and (not 'C308' in list(df_diag_now['code'])):    #持续时间
+                        time=self.bmstime[i]
+                        code='C309'
+                        faultlv=3
+                        faultinfo='电芯电压无效'
+                        reason='电芯电压采样电路异常'
+                        faultlocation='电芯{}'.format(list(np.argwhere(np.array(cellvolt2)<1)+1)+list(np.argwhere(np.array(cellvolt2)>5)+1))
+                        faultadvice='召回电池包,进行检修'
+                        influence='失去对电芯电压监测'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                    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 and (not 'C308' in list(df_diag_now['code'])):
+                        time=self.bmstime[i]
+                        code='C309'
+                        faultlv=3
+                        faultinfo='电芯电压无效'
+                        reason='数据通讯异常'
+                        faultlocation='电芯{}'.format(list(np.argwhere(np.array(cellvolt2)<1)+1)+list(np.argwhere(np.array(cellvolt2)>5)+1))
+                        faultadvice='检修数据传输链路'
+                        influence='失去对电芯电压监测'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason,  faultlocation, faultadvice, influence]
+                    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[-1], 'end_time'] = time
+                else:
+                    pass
+            
+            #电压采样线松动.................................................................................................................
+            if not 'C208' in list(df_diag_now['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if cellvoltmin2>2 and cellvoltmax2<4.5 and cellvoltmin1>2 and cellvoltmax1<4.5:
+                    if (min(cellvolt1_3sigma)<-3 and max(cellvolt1_3sigma)>3) and (min(cellvolt2_3sigma)<-3 and max(cellvolt2_3sigma)>3) and (cellvoltmax2-cellvoltmin2)>0.2 and (cellvoltmax1-cellvoltmin1)>0.2:   #连续发生
+                        cellvoltvalid=0
+                        voltloose_time=voltloose_time+(time2-time1).total_seconds()
+                        if voltloose_time>self.param.volt_time:
+                            time=self.bmstime[i]
+                            code='C208'
+                            faultinfo='电芯电压无效'
+                            reason='电芯电压采样线松动'
+                            faultlocation='电芯{}'.format([cellvoltmin_index2+1, cellvoltmax_index2+1])
+                            faultadvice='召回电池包,进行检修'
+                            influence='失去对电芯电压监测'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                        else:
+                            pass
+                    elif (min(cellvolt1_3sigma)>-3 and max(cellvolt1_3sigma)<3) and (min(cellvolt2_3sigma)<-3 and max(cellvolt2_3sigma)>3) and (cellvoltmax2-cellvoltmin2)>0.2 and (cellvoltmax1-cellvoltmin1)>0.2: #连续跳变
+                        cellvoltvalid=0
+                        voltloose_count=voltloose_count+1
+                        if voltloose_count>=3:
+                            time=self.bmstime[i]
+                            code='C208'
+                            faultlv=3
+                            faultinfo='电压电压无效'
+                            reason='电芯电压采样线松动'
+                            faultlocation='电芯{}'.format([cellvoltmin_index2+1, cellvoltmax_index2+1])
+                            faultadvice='召回电池包,进行检修'
+                            influence='失去对电芯电压监测'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                        else:
+                            pass
+                    else:
+                        voltloose_time=0
+                else:
+                    voltloose_time=0
+            else:
+                if(cellvoltmax2-cellvoltmin2)<0.1 and (cellvoltmax1-cellvoltmin1)<0.1:
+                    cellvoltvalid=1
+                    time=self.bmstime[i]
+                    self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C208'].index[-1], 'end_time'] = time
+                else:
+                    pass
+
+            #电芯电压诊断............................................................................................................................................    
+            if cellvoltvalid==1:                
+                #电芯过压.............................................................................................................................................
+                if not 'C401' in list(df_diag_now['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if cellvoltmax2>self.param.CellOvLv2 and cellvoltmax1>self.param.CellOvLv2:  #二级过压进入
+                        cov_time=cov_time+(time2-time1).total_seconds()
+                        if cov_time>self.param.volt_time:
+                            time=self.bmstime[i]
+                            code='C401'
+                            faultlv=4
+                            faultinfo='电芯过压'
+                            faultlocation='电芯{}'.format(list(np.argwhere(np.array(cellvolt2)>self.param.CellOvLv2)+1))
+                            influence='长时间过压会导致电池析锂,存在电池安全与寿命衰减过快风险'
+                            cellocvmax=cellvoltmax2-self.packcrnt[i]*sor_mean*2 #内阻反推OCV
+                            cellsocmax=np.interp(cellocvmax,self.param.LookTab_OCV,self.param.LookTab_SOC)  #ocv反查表得到SOC
+                            if self.bmssoc[i]<90 and self.packcrnt[i]>-self.param.Capacity/10 and self.packcrnt[i-1]>-self.param.Capacity/10:
+                                reason='BMS计算SOC偏低'
+                                faultadvice='优化SOC算法'
+                            elif self.bmssoc[i]<cellsocmax-10:
+                                reason='BMS计算SOC偏低'
+                                faultadvice='优化SOC算法'
+                            elif self.packcrnt[i-1]<-self.param.Capacity/2 or self.packcrnt[i]<-self.param.Capacity/2:
+                                reason='充电电流过大'
+                                faultadvice='优化充放电功率限值'
+                            else:
+                                reason='1.BMS软件策略BUG,2.继电器粘连'
+                                faultadvice='检修电池包'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                        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']=='C401'].index[-1], 'end_time'] = time
+                    else:
+                        pass
+                
+                #欠压诊断.................................................................................................................
+                if not 'C202' in list(df_diag_now['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='电芯{}欠压'
+                            faultlocation='电芯{}'.format([cellvoltmin_index2+1, cellvoltmax_index2+1])
+                            influence='欠压可能导致电池过放,严重过放会导致负极集流体溶解,进而发生内短路风险'
+                            cellocvmin=cellvoltmin2+-self.packcrnt[i]*sor_mean*2 #内阻反推OCV
+                            cellsocmin=np.interp(cellocvmin,self.param.LookTab_OCV,self.param.LookTab_SOC)  #ocv反查表得到SOC
+                            if self.bmssoc[i]>10 and self.packcrnt[i]<self.param.Capacity/10 and self.packcrnt[i-1]<self.param.Capacity/10:
+                                reason='BMS计算SOC偏高'
+                                faultadvice='优化SOC算法'
+                            elif self.bmssoc[i]>cellsocmin+10:
+                                reason='BMS计算SOC偏高'
+                                faultadvice='优化SOC算法'
+                            elif self.packcrnt[i-1]>self.param.Capacity/2 or self.packcrnt[i]>self.param.Capacity/2:
+                                reason='放电电流过大'
+                                faultadvice='优化充放电功率限值'
+                            else:
+                                reason='1.BMS软件策略BUG,2.继电器粘连'
+                                faultadvice='检修电池包'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                        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[-1], 'end_time'] = time
+                    else:
+                        pass
+             
+                #电芯压差大.....................................................................................................................................................
+                if not 'C104' in list(df_diag_now['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='电芯电压一致性差'
+                            faultlocation='电芯{}'.format(list(np.argwhere(np.array(cellvolt2)<self.param.CellUvLv2)+1))
+                            faultadvice='更换模组'
+                            influence='容量/内阻/自放电不一致,影响电池充放电性能'
+                            if (not self.df_soh.empty) and (self.df_soh.loc[0,'cellsohmax']-self.df_soh.loc[0,'cellsohmin'])>5:
+                                reason='电芯容量一致性差'
+                            elif ('C316' in list(df_diag_now['code'])) or ('C317' in list(df_diag_now['code'])):
+                                reason='电芯内阻一致性差'
+                            elif 'C490' in list(df_diag_now['code']):
+                                reason='电芯自放电异常'
+                            elif celltempmin1<-5 and celltempmin2<-5 and abs(self.packcrnt[i-1])>self.param.Capacity/10 and abs(self.packcrnt[i])>self.param.Capacity/10:
+                                reason='电芯低温性能差'
+                                faultadvice='优化电芯低温性能'
+                            elif self.bmssoc[i-1]<3 and self.self.bmssoc[i]<3:
+                                reason='SOC过低'
+                                faultadvice='通知用户充电'
+                            else:
+                                reason='BMS均衡逻辑异常'
+                                faultadvice='优化均衡策略'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                        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[-1], 'end_time'] = time
+                    else:
+                        pass
+                
+                #电芯电压离群.......................................................................................................................................
+                if not 'C206' in list(df_diag_now['code']):
+                    if min(cellvolt1_3sigma)<-4 and min(cellvolt2_3sigma)<-4 and cellvolt2_mean-cellvoltmin2>0.05: 
+                        cellvoltvalid=0
+                        voltstray_time=voltstray_time+(time2-time1).total_seconds()
+                        if voltstray_time>self.param.volt_time:
+                            time=self.bmstime[i]
+                            code='C206'
+                            faultlv=2
+                            faultinfo='电芯电压离群'
+                            faultlocation='电芯{}'.format([cellvoltmin_index2+1])
+                            faultadvice='更换模组'
+                            influence='容量/内阻/自放电不一致,影响电池充放电性能'
+                            if (not self.df_soh.empty) and (self.df_soh.loc[0,'cellsohmax']-self.df_soh.loc[0,'cellsohmin'])>5:
+                                reason='电芯容量一致性差'
+                            elif ('C316' in list(df_diag_now['code'])) or ('C317' in list(df_diag_now['code'])):
+                                reason='电芯内阻一致性差'
+                            elif 'C490' in list(df_diag_now['code']):
+                                reason='电芯自放电异常'
+                            elif celltempmin1<-5 and celltempmin2<-5 and abs(self.packcrnt[i-1])>self.param.Capacity/10 and abs(self.packcrnt[i])>self.param.Capacity/10:
+                                reason='电芯低温性能差'
+                                faultadvice='优化电芯低温性能'
+                            elif self.bmssoc[i-1]<3 and self.self.bmssoc[i]<3:
+                                reason='SOC过低'
+                                faultadvice='通知用户充电'
+                            else:
+                                reason='BMS均衡逻辑异常'
+                                faultadvice='优化均衡策略'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                        else:
+                            pass
+                    elif max(cellvolt1_3sigma)>4 and max(cellvolt2_3sigma)>4 and cellvoltmax2-cellvolt2_mean>0.05:   #连续发生
+                        cellvoltvalid=0
+                        voltstray_time=voltstray_time+(time2-time1).total_seconds()
+                        if voltstray_time>self.param.volt_time:
+                            time=self.bmstime[i]
+                            code='C206'
+                            faultlv=2
+                            faultinfo='电芯电压离群'
+                            faultlocation='电芯{}'.format([cellvoltmax_index2+1])
+                            faultadvice='更换模组'
+                            influence='容量/内阻/自放电不一致,影响电池充放电性能'
+                            if (not self.df_soh.empty) and (self.df_soh.loc[0,'cellsohmax']-self.df_soh.loc[0,'cellsohmin'])>5:
+                                reason='电芯容量一致性差'
+                            elif ('C316' in list(df_diag_now['code'])) or ('C317' in list(df_diag_now['code'])):
+                                reason='电芯内阻一致性差'
+                            elif 'C490' in list(df_diag_now['code']):
+                                reason='电芯自放电异常'
+                            else:
+                                reason='BMS均衡逻辑异常'
+                                faultadvice='优化均衡策略'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                        else:
+                            pass
+                    else:
+                        voltstray_time=0
+                else:
+                    if min(cellvolt1_3sigma)>-3 and min(cellvolt2_3sigma)>-3 and max(cellvolt1_3sigma)<3 and max(cellvolt2_3sigma)<3:
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C206'].index[-1], 'end_time'] = self.bmstime[i]
+                    else:
+                        pass
+                    
+            else:
+                cov_time=0
+                cuv_time=0
+                cdv_time=0
+                voltstray_time=0
+            
+            #电池包诊断.....................................................................................................................................
+            packvolt1=self.packvolt[i-1]
+            packvolt2=self.packvolt[i]
+            time1=self.bmstime[i-1]
+            time2=self.bmstime[i]
+            if not 'C304' in list(df_diag_now['code']):
+                if packvolt2<2*self.param.CellVoltNums or packvolt2>4.5*self.param.CellVoltNums or (cellvoltvalid==1 and abs(packvolt2-sum(cellvolt2))>10):   #电池包电压有效性
+                    packvoltvalid=0
+                    packvoltvalid_time=packvoltvalid_time+(time2-time1).total_seconds()
+                    if packvoltvalid_time>self.param.volt_time:
+                        time=self.bmstime[i]
+                        code='304'
+                        faultlv=3
+                        faultinfo='电池包电压无效'
+                        reason='1.电池包电压采样电路异常,2.数据通讯异常'
+                        faultlocation='电池包电压'
+                        faultadvice='召回电池包,进行检修'
+                        influence='失去对电池包电压监测'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                    else:
+                        pass
+                else:
+                    packvoltvalid=1
+                    packvoltvalid_time=0   
+            else:
+                packvoltvalid=1
+                packvoltvalid_time=0
+                if packvolt1>2.2*self.param.CellVoltNums or packvolt2<4.3*self.param.CellVoltNums:
+                    self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C304'].index[-1], 'end_time'] = self.bmstime[i]
+                else:
+                    pass
+
+            if packvoltvalid==1:
+                if not 'C402' in list(df_diag_now['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='C402'
+                            faultlv=4
+                            faultinfo='电池包过压'
+                            faultlocation='电池包电压'
+                            influence='长时间过压会导致电池析锂,存在电池安全与寿命衰减过快风险'
+                            cellocvmax=cellvoltmax2-self.packcrnt[i]*sor_mean*2 #内阻反推OCV
+                            cellsocmax=np.interp(cellocvmax,self.param.LookTab_OCV,self.param.LookTab_SOC)  #ocv反查表得到SOC
+                            if self.bmssoc[i]<90 and self.packcrnt[i]>-self.param.Capacity/10 and self.packcrnt[i-1]>-self.param.Capacity/10:
+                                reason='BMS计算SOC偏低'
+                                faultadvice='优化SOC算法'
+                            elif self.bmssoc[i]<cellsocmax-10:
+                                reason='BMS计算SOC偏低'
+                                faultadvice='优化SOC算法'
+                            elif self.packcrnt[i-1]<-self.param.Capacity/2 or self.packcrnt[i]<-self.param.Capacity/2:
+                                reason='充电电流过大'
+                                faultadvice='优化充放电功率限值'
+                            else:
+                                reason='1.BMS软件策略BUG,2.继电器粘连'
+                                faultadvice='检修电池包'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                        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']=='C402'].index[-1], 'end_time'] = time
+                    else:
+                        pass
+          
+                if not 'C203' in list(df_diag_now['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='电池包欠压'
+                            faultlocation='电池包电压'
+                            faultadvice='禁止放电'
+                            influence='欠压可能导致电池过放,严重过放会导致负极集流体溶解,进而发生内短路风险'
+                            cellocvmin=cellvoltmin2+-self.packcrnt[i]*sor_mean*2 #内阻反推OCV
+                            cellsocmin=np.interp(cellocvmin,self.param.LookTab_OCV,self.param.LookTab_SOC)  #ocv反查表得到SOC
+                            if self.bmssoc[i]>10 and self.packcrnt[i]<self.param.Capacity/10 and self.packcrnt[i-1]<self.param.Capacity/10:
+                                reason='BMS计算SOC偏高'
+                                faultadvice='优化SOC算法'
+                            elif self.bmssoc[i]>cellsocmin+10:
+                                reason='BMS计算SOC偏高'
+                                faultadvice='优化SOC算法'
+                            elif self.packcrnt[i-1]>self.param.Capacity/2 or self.packcrnt[i]>self.param.Capacity/2:
+                                reason='放电电流过大'
+                                faultadvice='优化充放电功率限值'
+                            else:
+                                reason='1.BMS软件策略BUG,2.继电器粘连'
+                                faultadvice='检修电池包'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                        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[-1], 'end_time'] = time
+                    else:
+                        pass
+            else:
+                pov_time=0
+                puv_time=0 
+                packvoltvalid_time=0
+                
+            #温度有效性判断................................................................................................................................................
+            if not 'C301' in list(df_diag_now['code']):
+                if celltempmax2>self.param.CellTempUpLmt or celltempmin2<self.param.CellTempLwLmt or max(np.array(celltemp2)-np.array(celltemp1))>10 or min(np.array(celltemp2)-np.array(celltemp1))<-10:
+                    celltempvalid=0
+                    tempvalid_time=tempvalid_time+(time2-time1).total_seconds()
+                    if tempvalid_time>self.param.temp_time:
+                        time=self.bmstime[i]
+                        code='301'
+                        faultlv=3
+                        faultinfo='电芯温度无效'
+                        reason='1.温度采样电路异常,2.数据通讯异常'
+                        faultlocation='温度探针{}'.format(list(np.argwhere(np.array(celltemp2)<self.param.CellTempLwLmt)+1)+list(np.argwhere(np.array(celltemp2)>self.param.CellTempUpLmt)+1))
+                        faultadvice='召回电池包,进行检修'
+                        influence='失去对电池温度监测'
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                    else:
+                        pass
+                else:
+                    celltempvalid=1
+                    tempvalid_time=0   
+            else:
+                celltempvalid=1
+                tempvalid_time=0
+                if celltempmax1<self.param.CellTempUpLmt or packvolt2<4.3*self.param.CellVoltNums:
+                    self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C304'].index[-1], 'end_time'] = self.bmstime[i]
+                else:
+                    pass
+           
+            if celltempvalid==1:
+                #过温判断.............................................................................................................
+                if not 'C302' in list(df_diag_now['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='电芯温度过高'
+                            faultlocation='温度探针{}'.format(list(np.argwhere(np.array(celltemp2)>self.param.CellTempHighLv2)+1))
+                            faultadvice='禁止充放电,并开启电池冷却功能'
+                            influence='高温下充放电,SEI膜增长加速,导致容量衰减过快,温度过高则存在热失控风险'
+                            if max(celltemp1_3sigma)>3 and max(celltemp2_3sigma)>3:
+                                reason='Busbar连接异常'  
+                                faultadvice='检修电池包'
+                            elif sum(self.packcrnt[:i]*self.packvolt[:i])/(1000*(i+1))>50:
+                                reason='电池输出功率过大'
+                                faultadvice='优化电池充放电功率限值'
+                            else:
+                                reason='1.冷却液温度过高,2.冷却水泵异常,3.Busbar连接异常'
+                                faultadvice='检修电池包'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                        else:
+                            pass
+                    else:
+                        ot_time=0
+                else:   #ram当前故障中有该故障,则判断是否退出该故障
+                    if celltempmax1<self.param.CellTempHighLv1-5 and celltempmax2<self.param.CellTempHighLv1-5:    #二级高温恢复
+                        self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C302'].index[-1], 'end_time'] = self.bmstime[i]
+                    else:
+                        pass
+            
+                #欠温判断.................................................................................................................
+                if not 'C102' in list(df_diag_now['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='电芯温度过低'
+                            faultlocation='温度探针{}'.format(list(np.argwhere(np.array(celltemp2)<self.param.CellTempLowLv2)+1))
+                            influence='低温下充电,会导致析锂,存在电池安全与寿命衰减过快风险'
+                            if self.enmtemp[i-1]-celltempmin1>10 and self.enmtemp[i]-celltempmin2>10:
+                                reason='温度检测系统异常' 
+                                faultadvice='检修电池包' 
+                            elif self.bmssoc[i-1]<5 and self.bmssoc[i]<5:
+                                reason='电池包SOC过低'
+                                faultadvice='通知用户进行充电'
+                            else:
+                                reason='PTC加热系统异常'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                        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[-1], 'end_time'] = time
+                    else:
+                        pass
+              
+                #温差判断.............................................................................................................................
+                if not 'C103' in list(df_diag_now['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='电芯温差过大'
+                            faultlocation='温度探针{}'.format([celltemp2.index(celltempmin2)+1,celltemp2.index(celltempmax2)+1])
+                            influence='存在电芯的老化速率不一致的风险'
+                            if self.enmtemp[i-1]>0 and self.enmtemp>0 and (('C316' in list(df_diag_now['code'])) or ('C317' in list(df_diag_now['code']))):
+                                reason='电芯内阻不一致' 
+                                faultadvice='更换模组'
+                            elif self.enmtemp[i-1]>10 and self.enmtemp>10:
+                                reason='Busbar连接异常' 
+                                faultadvice='检修电池包'
+                            elif self.enmtemp[i-1]<-10 and self.enmtemp<-10:
+                                reason='环境温度过低'
+                                faultadvice='合理优化低温充放电功率限值,并开启水泵'
+                            else:
+                                reason='电池包冷却/加热系统设计不合理'
+                                faultadvice='合理优化电池包冷却/加热系统'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                        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[-1], 'end_time'] = time
+                    else:
+                        pass
+                
+                #温度离群判断.............................................................................................................................
+                if not 'C105' in list(df_diag_now['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if max(celltemp1_3sigma)>3 and max(celltemp2_3sigma)>3:  
+                        tempstray_time=tempstray_time+(time2-time1).total_seconds()
+                        if tempstray_time>self.param.temp_time:
+                            time=self.bmstime[i]
+                            code='C105'
+                            faultlv=1
+                            faultinfo='电芯温度离群'
+                            faultlocation='温度探针{}'.format([celltemp2.index(celltempmax2)+1])
+                            influence='存在电芯的老化速率不一致的风险'
+                            if self.enmtemp[i-1]>0 and self.enmtemp>0 and (('C316' in list(df_diag_now['code'])) or ('C317' in list(df_diag_now['code']))):
+                                reason='电芯内阻不一致' 
+                                faultadvice='更换模组'
+                            elif self.enmtemp[i-1]>10 and self.enmtemp>10:
+                                reason='Busbar连接异常' 
+                                faultadvice='检修电池包'
+                            elif self.enmtemp[i-1]<-10 and self.enmtemp<-10:
+                                reason='环境温度过低'
+                                faultadvice='合理优化低温充放电功率限值,并开启水泵'
+                            else:
+                                reason='电池包冷却/加热系统设计不合理'
+                                faultadvice='合理优化电池包冷却/加热系统'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                    elif min(celltemp1_3sigma)<-3 and min(celltemp2_3sigma)<-3:  
+                        tempstray_time=tempstray_time+(time2-time1).total_seconds()
+                        if tempstray_time>self.param.temp_time:
+                            time=self.bmstime[i]
+                            code='C105'
+                            faultlv=1
+                            faultinfo='电芯温度离群'
+                            faultlocation='温度探针{}'.format([celltemp2.index(celltempmin2)+1])
+                            influence='存在电芯的老化速率不一致的风险'
+                            if self.enmtemp[i-1]>0 and self.enmtemp>0 and (('C316' in list(df_diag_now['code'])) or ('C317' in list(df_diag_now['code']))):
+                                reason='电芯内阻不一致' 
+                                faultadvice='更换模组'
+                            elif self.enmtemp[i-1]>10 and self.enmtemp>10:
+                                reason='Busbar连接异常' 
+                                faultadvice='检修电池包'
+                            elif self.enmtemp[i-1]<-10 and self.enmtemp<-10:
+                                reason='环境温度过低'
+                                faultadvice='合理优化低温充放电功率限值,并开启水泵'
+                            else:
+                                reason='电池包冷却/加热系统设计不合理'
+                                faultadvice='合理优化电池包冷却/加热系统'
+                            self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                        else:
+                            pass
+                    else:
+                        tempstray_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']=='C105'].index[-1], 'end_time'] = time
+                    else:
+                        pass            
+            else:
+                ot_time=0
+                ut_time=0
+                dt_time=0
+                tempstray_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(df_diag_now['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if as_dis>100:
+                        time=self.bmstime[i]
+                        code='C306'
+                        faultlv=3
+                        faultinfo='电池放电过流'
+                        faultlocation='电池包'
+                        influence='长时间过流会导致电池欠压及温升过快'
+                        if cellvoltmin1<self.param.CellUvLv2 and cellvoltmin2<self.param.CellUvLv2:
+                            reason='BMS控制策略异常'
+                            faultadvice='优化BMS控制策略'
+                        else:
+                            reason='1.充电器异常,2.继电器粘连'
+                            faultadvice='停止放电'  
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                    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[-1], 'end_time'] = time
+                    else:
+                        pass
+                
+                if not 'C305' in list(df_diag_now['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if as_chg>100:
+                        time=self.bmstime[i]
+                        code='C305'
+                        faultlv=3
+                        faultinfo='电池充电过流'
+                        faultlocation='电池包'
+                        influence='过流会导致电池析锂,存在电池安全与寿命衰减过快风险'
+                        if cellvoltmax1>self.param.CellOvLv2 and cellvoltmax2>self.param.CellOvLv2:
+                            reason='BMS控制策略异常'
+                            faultadvice='优化BMS控制策略'
+                        else:
+                            reason='1.充电器异常,2.继电器粘连'
+                            faultadvice='停止放电'  
+                        self.df_diag_ram.loc[len(self.df_diag_ram)]=[time, end_time, self.sn, code, faultlv, faultinfo, reason, faultlocation, faultadvice, influence]
+                    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[-1], 'end_time'] = time
+                    else:
+                        pass
+            
+            #SOC卡滞、跳变诊断................................................................................................
+            if i<2:
+                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(df_diag_now['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[-1], '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(df_diag_now['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[-1], 'end_time'] = time
+                else:
+                    pass
+            
+            #绝缘故障检测
+            if not 'C315' in list(df_diag_now['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if self.ISO[i]=='异常' and self.ISO[i-1]=='异常':
+                    time=self.bmstime[i]
+                    code='C315'
+                    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.ISO[i]=='正常' and self.ISO[i-1]=='正常':
+                    time=self.bmstime[i]
+                    self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C315'].index[-1], 'end_time'] = time
+                else:
+                    pass
+            
+            #高压互锁检测
+            if not 'C209' in list(df_diag_now['code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if self.HIVLLk[i]=='异常' and self.HIVLLk[i-1]=='异常':
+                    time=self.bmstime[i]
+                    code='C209'
+                    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:
+                if self.HIVLLk[i]=='正常' and self.HIVLLk[i-1]=='正常':
+                    time=self.bmstime[i]
+                    self.df_diag_ram.loc[self.df_diag_ram[self.df_diag_ram['code']=='C209'].index[-1], '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(df_diag_now['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[-1], '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(df_diag_now['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[-1], 'end_time'] = time
+        #             else:
+        #                 pass
+
+        #         if not 'C205' in list(df_diag_now['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[-1], 'end_time'] = time
+        #             else:
+        #                 pass
+        #     else:
+        #         pass
+        # else:
+        #     cellsoh_diff=5
+        
+
+
+        return self.df_diag_ram
+                
+
+
+
+                
+
+
+                
+
+

+ 103 - 0
USER/SPF/WORK/03hezhong/04BatDiag/CBMSBatDiag copy.py

@@ -0,0 +1,103 @@
+import pandas as pd
+import numpy as np
+
+class BatDiag:
+    def __init__(self, df_bms, df_soh, df_sor):  #参数初始化
+
+        self.df_bms=df_bms
+        self.vin=df_bms['VIN']
+        self.model=df_bms['VehModel']
+        self.sn=df_bms['SN']
+        self.end_time='0000-00-00 00:00:00'
+        
+        self.cellvolt_name=['CellVolt'+str(x) for x in range(1,self.param.CellVoltNums+1)]
+        self.celltemp_name=['CellTemp'+str(x) for x in range(1,self.param.CellTempNums+1)]
+    
+    #电芯电压无效故障........................................................................................................................................................
+    def cellvoltvalid(self,df_ram,df_param):
+        # df_res=pd.DataFrame(columns=['start_time', 'end_time', 'vin', 'sn', 'model', 'fault_code', 'fault_reason', 'fault_advice', 'fault_location'])
+        df_volt=self.df_bms[['Time']+self.cellvolt_name]
+        volt_mean=df_volt[self.cellvolt_name].mean(axis=1)
+        volt_std=df_volt[self.cellvolt_name].std(axis=1)
+        volt_std = volt_std.replace(0,0.000001)
+        df_stray=df[self.cellvolt_name].sub(volt_mean,axis=0).div(volt_std,axis=0)
+        if df_ram.empty:
+            #电压断线
+            df=df_volt[((df_volt[self.cellvolt_name]<2.5).any(1)) & ((df_volt[self.cellvolt_name]>4.5).any(1))]
+            if not df.empty:
+                list_of_df = np.split(df, np.flatnonzero(np.diff(df.index) != 1) + 1)
+                df=max(list_of_df, key=len, default='')
+                if df.iloc[-1]['Time']-df.iloc[0]['Time']>df_param:
+                    cellvolt=np.array(df.iloc[0][self.cellvolt_name])
+                    cellvoltmin_index=cellvolt.index(min(cellvolt))+1
+                    cellvoltmax_index=cellvolt.index(max(cellvolt))+1
+                    if abs(cellvoltmax_index-cellvoltmin_index)==1:
+                        time=df.iloc[0]['Time']
+                        faultcode='C309'
+                        faultlv=3
+                        faultinfo='电芯电压无效'
+                        reason='电芯电压采样线断线'
+                        faultlocation='电芯{}'.format([cellvoltmin_index,cellvoltmax_index])
+                        faultadvice='召回电池包,进行检修'
+                        influence='失去对电芯电压监测'
+                        df_ram.loc[0]=[time, self.end_time, self.vin, self.sn, self.model, faultcode, reason, faultadvice, faultlocation]
+                    else:
+                        pass
+                else:
+                    pass
+            else:
+                pass
+            #电压超限
+            if df_ram.empty:
+                df=df_volt[((df_volt[self.cellvolt_name]<1).any(1)) | ((df_volt[self.cellvolt_name]>5).any(1))]
+                if not df.empty:
+                    list_of_df = np.split(df, np.flatnonzero(np.diff(df.index) != 1) + 1)
+                    df=max(list_of_df, key=len, default='')
+                    if df.iloc[-1]['Time']-df.iloc[0]['Time']>df_param:
+                        cellvolt=np.array(df.iloc[0][self.cellvolt_name])
+                        time=df.iloc[0]['Time']
+                        faultcode='C309'
+                        faultlv=3
+                        faultinfo='电芯电压无效'
+                        reason='电芯电压采样电路异常'
+                        faultlocation='电芯{}'.format(list(np.argwhere(cellvolt)<1+1)+list(np.argwhere(cellvolt)>5+1))
+                        faultadvice='召回电池包,进行检修'
+                        influence='失去对电芯电压监测'
+                        df_ram.loc[0]=[time, self.vin, self.sn, self.model, faultcode, reason, faultadvice, faultlocation]
+                    else:
+                        pass
+                else:
+                    pass
+            else:
+                pass
+            #电压采样松动
+            if df_ram.empty:
+                df=df_stray[((df_stray[self.cellvolt_name]<-4).any(1)) & ((df_stray[self.cellvolt_name]>4).any(1))]
+                if not df.empty:
+                    list_of_df = np.split(df, np.flatnonzero(np.diff(df.index) != 1) + 1)
+                    df=max(list_of_df, key=len, default='')
+                    if df.iloc[-1]['Time']-df.iloc[0]['Time']>df_param:
+                        cellvolt=np.array(df.iloc[0][self.cellvolt_name])
+                        time=df.iloc[0]['Time']
+                        faultcode='C309'
+                        faultlv=3
+                        faultinfo='电芯电压无效'
+                        reason='电芯电压采样线松动'
+                        faultlocation='电芯{}'.format(list(np.argwhere(cellvolt)<1+1)+list(np.argwhere(cellvolt)>5+1))
+                        faultadvice='召回电池包,进行检修'
+                        influence='失去对电芯电压监测'
+                        df_ram.loc[0]=[time, self.vin, self.sn, self.model, faultcode, reason, faultadvice, faultlocation]
+                    else:
+                        pass
+                else:
+                    pass
+            else:
+                pass
+            
+        
+        return df_ram
+    
+
+            
+    
+

+ 991 - 0
USER/SPF/WORK/03hezhong/04BatDiag/CBMSBatDiag.py

@@ -0,0 +1,991 @@
+import pandas as pd
+import numpy as np
+import bisect
+import datetime
+import BatParam
+
+class BatDiag:
+    def __init__(self, df_bms):  #参数初始化
+
+        self.df_bms=df_bms
+        self.vin=df_bms['VIN']
+        self.model=df_bms['VehModel']
+        self.sn=df_bms['SN']
+        
+        self.packcrnt=df_bms['PackCrnt']
+        self.packvolt=df_bms['PackVolt']
+        self.bmssoc=df_bms['PackSoc']
+        self.isor=df_bms['InsulationRss']
+        self.enmtemp=df_bms['EnmTemp']
+        self.bmsstate=df_bms['BMSSta'].fillna(0)
+        self.bmstime= pd.to_datetime(df_bms['Time'], format='%Y-%m-%d %H:%M:%S')
+        CellVoltNums=df_bms['CellVoltTotalCount']
+        CellTempNums=df_bms['CellMinTempNum']
+        
+        self.cellvolt_name=['CellVolt'+str(x) for x in range(1,CellVoltNums+1)]
+        self.celltemp_name=['CellTemp'+str(x) for x in range(1,CellTempNums+1)]
+    
+   #寻找当前行数据的所有温度值...................................................................................
+    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 diag(self, df_diag_ram, df_soh, df_sor, df_adjust_param, df_pack_param, df_algo_param):
+        
+        # df_res=pd.DataFrame(columns=['start_time', 'end_time', 'vin', 'sn', 'model', 'fault_code', 'fault_reason', 'fault_advice', 'fault_location'])
+        bmssoc_st=self.bmssoc[0]
+        celltemp_stnum=0
+        end_time='0000-00-00 00:00:00'
+        ah_accum=0  #SOC卡滞初始参数
+        cellvoltvalid=1
+        voltdsc_time=0
+        voltfail_time=0
+        voltloose_time=0
+        voltstray_time=0
+        tempstray_time=0
+        packvoltvalid_time=0
+        tempvalid_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
+        rt_time=0
+        disoc_time=0
+        chgoc_time=0
+        isor_time=0
+        
+        sor=eval(df_sor.loc[0,'sor'])
+        sor_mean=np.mean(sor)
+            
+        for i in range(1,len(self.df_volt)-1):
+            df_diag_now=df_diag_ram[df_diag_ram['end_time']=='0000-00-00 00:00:00']
+        
+            #电压诊断功能.........................................................................................................................................
+            if i<=1:
+                time1=self.bmstime[i-1]
+                time2=self.bmstime[i]
+                cellvolt2=self._cellvolt_get(i)
+                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)
+                
+                cellvolt1_std=np.std(cellvolt1)
+                cellvolt1_mean=np.mean(cellvolt1)
+                cellvolt1_3sigma=(cellvolt1-cellvolt1_mean)/cellvolt1_std
+                cellvolt2_std=np.std(cellvolt2)
+                cellvolt2_mean=np.mean(cellvolt2)
+                cellvolt2_3sigma=(cellvolt2-cellvolt2_mean)/cellvolt2_std
+                
+                celltemp1=self._celltemp_get(i-1)
+                celltemp2=self._celltemp_get(i)
+                celltempmin1=min(celltemp1)
+                celltempmin2=min(celltemp2)
+                celltempmax1=max(celltemp1)
+                celltempmax2=max(celltemp2)
+                
+                celltemp1_std=np.std(celltemp1)
+                celltemp1_mean=np.mean(celltemp1)
+                celltemp1_3sigma=(np.array(celltemp1)-celltemp1_mean)/celltemp1_std
+                celltemp2_std=np.std(celltemp2)
+                celltemp2_mean=np.mean(celltemp2)
+                celltemp2_3sigma=(np.array(celltemp2)-celltemp2_mean)/celltemp2_std
+            else:
+                time1=self.bmstime[i-1]
+                time2=self.bmstime[i]
+                cellvolt1=cellvolt2
+                cellvoltmin1=cellvoltmin2
+                cellvoltmax1=cellvoltmax2
+                cellvoltmin_index1=cellvoltmin_index2
+                cellvoltmax_index1=cellvoltmax_index2
+                cellvolt2=self._cellvolt_get(i)
+                cellvoltmin2=min(cellvolt2)
+                cellvoltmax2=max(cellvolt2)
+                cellvoltmin_index2=list(cellvolt2).index(cellvoltmin2)
+                cellvoltmax_index2=list(cellvolt2).index(cellvoltmax2)
+                
+                cellvolt1_std=cellvolt2_std
+                cellvolt1_mean=cellvolt2_mean
+                cellvolt1_3sigma=cellvolt2_3sigma
+                cellvolt2_std=np.std(cellvolt2)
+                cellvolt2_mean=np.mean(cellvolt2)
+                cellvolt2_3sigma=(cellvolt2-cellvolt2_mean)/cellvolt2_std
+                
+            #电芯电压无效-1..........................................................................................................................................
+            if not 'C309' in list(df_diag_now['fault_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>df_adjust_param.loc[1,'confirm_time']:    #持续时间
+                        fault_code='C309'
+                        # faultlv=3
+                        # faultinfo='电芯电压无效'
+                        fault_reason='电芯电压采样线断线'
+                        fault_location='电芯{}'.format([cellvoltmin_index2+1, cellvoltmax_index2+1])
+                        fault_advice='召回电池包,进行检修'
+                        # influence='失去对电芯电压监测'
+                        df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                        voltdsc_time=0
+                    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_time=voltdsc_time+(time2-time1).total_seconds()
+                    if voltdsc_time>df_adjust_param.loc[1,'confirm_time']:
+                        fault_code='C309'
+                        # faultlv=3
+                        # faultinfo='电芯电压无效'
+                        fault_reason='电芯电压采样线断线'
+                        fault_location='电芯{}'.format([cellvoltmin_index2+1, cellvoltmax_index2+1])
+                        fault_advice='召回电池包,进行检修'
+                        # influence='失去对电芯电压监测'
+                        df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                        voltdsc_time=0
+                    else:
+                        pass
+                #电芯电压超限
+                elif ((cellvoltmin2<1 and cellvoltmin1<1) or (cellvoltmax2>5 and cellvoltmax1>5)):
+                    cellvoltvalid=0
+                    voltdsc_time=0
+                    voltfail_time=voltfail_time+(time2-time1).total_seconds()
+                    if voltfail_time>df_adjust_param.loc[1,'confirm_time']:    #持续时间
+                        fault_code='C309'
+                        # faultlv=3
+                        # faultinfo='电芯电压无效'
+                        fault_reason='电芯电压采样电路异常'
+                        fault_location='电芯{}'.format(list(np.argwhere(cellvolt2<1)+1)+list(np.argwhere(cellvolt2>5)+1))
+                        fault_advice='召回电池包,进行检修'
+                        # influence='失去对电芯电压监测'
+                        df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                        voltfail_time=0
+                    else:
+                        pass
+                elif (cellvoltmin2<1 and cellvoltmin1>2.5) or  (cellvoltmax2>5 and cellvoltmax1<4.5):   #连续跳变
+                    cellvoltvalid=0
+                    voltdsc_time=0
+                    voltfail_count=voltfail_count+1
+                    if voltfail_count>=3:
+                        fault_code='C309'
+                        # faultlv=3
+                        # faultinfo='电芯电压无效'
+                        fault_reason='数据通讯异常'
+                        fault_location='电芯{}'.format(list(np.argwhere(cellvolt2<1)+1)+list(np.argwhere(cellvolt2>5)+1))
+                        fault_advice='检修数据传输链路'
+                        # influence='失去对电芯电压监测'
+                        df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                        voltfail_time=0
+                    else:
+                        pass
+                #电芯电压松动
+                elif cellvoltmin2>2 and cellvoltmax2<4.5 and cellvoltmin1>2 and cellvoltmax1<4.5:
+                    voltdsc_time=0
+                    voltfail_time=0
+                    if (min(cellvolt1_3sigma)<-4 and max(cellvolt1_3sigma)>4) and (min(cellvolt2_3sigma)<-4 and max(cellvolt2_3sigma)>4) and (cellvoltmax2-cellvoltmin2)>0.15 and (cellvoltmax1-cellvoltmin1)>0.15:   #连续发生
+                        cellvoltvalid=0
+                        voltloose_time=voltloose_time+(time2-time1).total_seconds()
+                        if voltloose_time>df_adjust_param.loc[1,'confirm_time']:
+                            fault_code='C309'
+                            # faultlv=3
+                            # faultinfo='电芯电压无效'
+                            fault_reason='电芯电压采样线松动'
+                            fault_location='电芯{}'.format([cellvoltmin_index2+1, cellvoltmax_index2+1])
+                            fault_advice='召回电池包,进行检修'
+                            # influence='失去对电芯电压监测'
+                            df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                            voltloose_time=0
+                        else:
+                            pass
+                    else:
+                        voltloose_time=0
+                else:
+                    voltdsc_time=0
+                    voltfail_time=0
+                    voltloose_time=0
+            else:
+                cellvoltvalid=0
+                if cellvoltmin2>2 and cellvoltmax2<4.5 and cellvoltmin1>2 and cellvoltmax1<4.5 and (min(cellvolt1_3sigma)>-4 and max(cellvolt1_3sigma)<4) and (min(cellvolt2_3sigma)>-4 and max(cellvolt2_3sigma)<4):
+                    voltfail_time=voltfail_time+(time2-time1).total_seconds()
+                    if voltfail_time>df_adjust_param.loc[1,'remove_time']:
+                        cellvoltvalid=1
+                        df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C309'].index[-1], 'end_time'] = time2
+                        voltfail_time=0
+                else:
+                    voltfail_time=0
+
+            #电芯电压诊断............................................................................................................................................    
+            if cellvoltvalid==1:                
+                #电芯过压-2.............................................................................................................................................
+                if not 'C401' in list(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if cellvoltmax2>df_adjust_param.loc[2,'threshold'] and cellvoltmax1>df_adjust_param.loc[2,'threshold']:  #二级过压进入
+                        cov_time=cov_time+(time2-time1).total_seconds()
+                        if cov_time>df_adjust_param.loc[2,'confirm_time']:
+                            fault_code='C401'
+                            # faultlv=4
+                            # faultinfo='电芯过压'
+                            fault_location='电芯{}'.format(list(np.argwhere(cellvolt2>df_adjust_param.loc[2,'threshold'])+1))
+                            # influence='长时间过压会导致电池析锂,存在电池安全与寿命衰减过快风险'
+                            cellocvmax=cellvoltmax2-self.packcrnt[i]*sor_mean*2 #内阻反推OCV
+                            cellsocmax=np.interp(cellocvmax,df_pack_param.iloc[0]['charge_ocv_v'],df_pack_param.iloc[0]['charge_ocv_soc'])  #ocv反查表得到SOC
+                            if self.bmssoc[i]<90 and self.packcrnt[i]>-df_pack_param.iloc[0]['capacity']/10 and self.packcrnt[i-1]>-df_pack_param.iloc[0]['capacity']/10:
+                                fault_reason='BMS计算SOC偏低'
+                                fault_advice='优化SOC算法'
+                            elif self.bmssoc[i]<cellsocmax-10:
+                                fault_reason='BMS计算SOC偏低'
+                                fault_advice='优化SOC算法'
+                            elif self.packcrnt[i-1]<-df_pack_param.iloc[0]['capacity']/2 or self.packcrnt[i]<-df_pack_param.iloc[0]['capacity']/2:
+                                fault_reason='充电电流过大'
+                                fault_advice='优化充放电功率限值'
+                            else:
+                                fault_reason='1.BMS软件策略BUG,2.继电器粘连'
+                                fault_advice='检修电池包'
+                            df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                            cov_time=0
+                        else:
+                            pass
+                    else:
+                        cov_time=0
+                else:   #ram当前故障中有该故障,则判断是否退出该故障
+                    if cellvoltmax2<df_adjust_param.loc[2,'threshold']-0.05 and cellvoltmax1<df_adjust_param.loc[2,'threshold']-0.05:   #二级过压故障恢复
+                        cov_time=cov_time+(time2-time1).total_seconds()
+                        if cov_time>df_adjust_param.loc[2,'confirm_time']:
+                            df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C401'].index[-1], 'end_time'] = time2
+                            cov_time=0
+                        else:
+                            pass
+                    else:
+                        cov_time=0
+                
+                #欠压诊断-3.................................................................................................................
+                if not 'C202' in list(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if cellvoltmin2<df_adjust_param.loc[3,'threshold'] and cellvoltmin1<df_adjust_param.loc[3,'threshold']:  #二级欠压
+                        cuv_time=cuv_time+(time2-time1).total_seconds()
+                        if cuv_time>df_adjust_param.loc[3,'confirm_time']:
+                            fault_code='C202'
+                            # faultlv=2
+                            # faultinfo='电芯{}欠压'
+                            fault_location='电芯{}'.format(list(np.argwhere(cellvolt2<df_adjust_param.loc[4,'threshold'])+1))
+                            # influence='欠压可能导致电池过放,严重过放会导致负极集流体溶解,进而发生内短路风险'
+                            cellocvmin=cellvoltmin2+-self.packcrnt[i]*sor_mean*2 #内阻反推OCV
+                            cellsocmin=np.interp(cellocvmin,df_pack_param.iloc[0]['discharge_ocv_v'],df_pack_param.iloc[0]['discharge_ocv_soc'])  #ocv反查表得到SOC
+                            if self.bmssoc[i]>10 and self.packcrnt[i]<df_pack_param.iloc[0]['capacity']/10 and self.packcrnt[i-1]<df_pack_param.iloc[0]['capacity']/10:
+                                fault_reason='BMS计算SOC偏高'
+                                fault_advice='优化SOC算法'
+                            elif self.bmssoc[i]>cellsocmin+10:
+                                fault_reason='BMS计算SOC偏高'
+                                fault_advice='优化SOC算法'
+                            elif self.packcrnt[i-1]>df_pack_param.iloc[0]['capacity']/2 or self.packcrnt[i]>df_pack_param.iloc[0]['capacity']/2:
+                                fault_reason='放电电流过大'
+                                fault_advice='优化充放电功率限值'
+                            else:
+                                fault_reason='1.BMS软件策略BUG,2.继电器粘连'
+                                fault_advice='检修电池包'
+                            df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                            cuv_time=0
+                        else:
+                            pass
+                    else:
+                        cuv_time=0
+                else:
+                    if cellvoltmin2>df_adjust_param.loc[3,'threshold']+0.1 and cellvoltmin1>df_adjust_param.loc[3,'threshold']+0.1:
+                        cuv_time=cuv_time+(time2-time1).total_seconds()
+                        if cuv_time>df_adjust_param.loc[3,'confirm_time']:
+                            df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C202'].index[-1], 'end_time'] = time2
+                            cuv_time=0
+                        else:
+                            pass
+                    else:
+                        cuv_time=0
+             
+                #电芯压差大-4.....................................................................................................................................................
+                if not 'C104' in list(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if (cellvoltmax2-cellvoltmin2)>df_adjust_param.loc[4,'threshold'] and (cellvoltmax1-cellvoltmin1)>df_adjust_param.loc[4,'threshold']:  #二级电芯压差
+                        cdv_time=cdv_time+(time2-time1).total_seconds()
+                        if cdv_time>df_adjust_param.loc[4,'confirm_time']:
+                            fault_code='C104'
+                            # faultlv=1
+                            # faultinfo='电芯电压一致性差'
+                            fault_location='电芯{}'.format([cellvoltmin_index2+1, cellvoltmax_index2+1])
+                            fault_advice='更换模组'
+                            # influence='容量/内阻/自放电不一致,影响电池充放电性能'
+                            if (not df_soh.empty) and (df_soh.loc[0,'cellsohmax']-df_soh.loc[0,'cellsohmin'])>5:
+                                fault_reason='电芯容量一致性差'
+                            elif ('C316' in list(df_diag_now['fault_code'])) or ('C317' in list(df_diag_now['fault_code'])):
+                                fault_reason='电芯内阻一致性差'
+                            elif 'C490' in list(df_diag_now['fault_code']):
+                                fault_reason='电芯自放电异常'
+                            elif celltempmin1<-5 and celltempmin2<-5 and abs(self.packcrnt[i-1])>df_pack_param.iloc[0]['capacity']/10 and abs(self.packcrnt[i])>df_pack_param.iloc[0]['capacity']/10:
+                                fault_reason='电芯低温性能差'
+                                fault_advice='优化电芯低温性能'
+                            elif self.bmssoc[i-1]<3 and self.self.bmssoc[i]<3:
+                                fault_reason='SOC过低'
+                                fault_advice='通知用户充电'
+                            else:
+                                fault_reason='BMS均衡逻辑异常'
+                                fault_advice='优化均衡策略'
+                            df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                            cdv_time=0
+                        else:
+                            pass
+                    else:
+                        cdv_time=0
+                else:
+                    if (cellvoltmax2-cellvoltmin2)<df_adjust_param.loc[4,'threshold']-0.05 and (cellvoltmax1-cellvoltmin1)<df_adjust_param.loc[4,'threshold']-0.05: 
+                        cdv_time=cdv_time+(time2-time1).total_seconds()
+                        if cdv_time>df_adjust_param.loc[4,'confirm_time']:
+                            df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C104'].index[-1], 'end_time'] = time2
+                            cdv_time=0
+                        else:
+                            pass
+                    else:
+                        cdv_time=0
+                
+                #电芯电压离群-5.......................................................................................................................................
+                if not 'C206' in list(df_diag_now['fault_code']):
+                    if (min(cellvolt1_3sigma)<-4 and min(cellvolt2_3sigma)<-4 and cellvolt2_mean-cellvoltmin2>0.02) or (max(cellvolt1_3sigma)>4 and max(cellvolt2_3sigma)>4 and cellvoltmax2-cellvolt2_mean>0.02): 
+                        voltstray_time=voltstray_time+(time2-time1).total_seconds()
+                        if voltstray_time>df_adjust_param.loc[5,'confirm_time']:
+                            fault_code='C206'
+                            # faultlv=2
+                            # faultinfo='电芯电压离群'
+                            fault_location='电芯{}'.format([cellvoltmin_index2+1, cellvoltmax_index2+1])
+                            fault_advice='更换模组'
+                            # influence='容量/内阻/自放电不一致,影响电池充放电性能'
+                            if (not df_soh.empty) and (df_soh.loc[0,'cellsohmax']-df_soh.loc[0,'cellsohmin'])>5:
+                                fault_reason='电芯容量一致性差'
+                            elif ('C316' in list(df_diag_now['fault_code'])) or ('C317' in list(df_diag_now['fault_code'])):
+                                fault_reason='电芯内阻一致性差'
+                            elif 'C490' in list(df_diag_now['fault_code']):
+                                fault_reason='电芯自放电异常'
+                            elif celltempmin1<-5 and celltempmin2<-5 and abs(self.packcrnt[i-1])>df_pack_param.iloc[0]['capacity']/10 and abs(self.packcrnt[i])>df_pack_param.iloc[0]['capacity']/10:
+                                fault_reason='电芯低温性能差'
+                                fault_advice='优化电芯低温性能'
+                            elif self.bmssoc[i-1]<3 and self.self.bmssoc[i]<3:
+                                fault_reason='SOC过低'
+                                fault_advice='通知用户充电'
+                            else:
+                                fault_reason='BMS均衡逻辑异常'
+                                fault_advice='优化均衡策略'
+                            df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                            voltstray_time=0
+                        else:
+                            pass
+                    else:
+                        voltstray_time=0
+                else:
+                    if min(cellvolt1_3sigma)>-3 and min(cellvolt2_3sigma)>-3 and max(cellvolt1_3sigma)<3 and max(cellvolt2_3sigma)<3:
+                        voltstray_time=voltstray_time+(time2-time1).total_seconds()
+                        if voltstray_time>df_adjust_param.loc[4,'confirm_time']:
+                            df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C206'].index[-1], 'end_time'] = time2
+                            voltstray_time=0
+                        else:
+                            pass
+                    else:
+                        voltstray_time=0
+                    
+            else:
+                cov_time=0
+                cuv_time=0
+                cdv_time=0
+                voltstray_time=0
+            
+            #电池包诊断-6.....................................................................................................................................
+            packvolt1=self.packvolt[i-1]
+            packvolt2=self.packvolt[i]
+            if not 'C304' in list(df_diag_now['fault_code']):
+                if packvolt2<2*df_pack_param.iloc[0]['cellvoltnum'] or packvolt2>4.5*df_pack_param.iloc[0]['cellvoltnum'] or (cellvoltvalid==1 and abs(packvolt2-sum(cellvolt2))>10):   #电池包电压有效性
+                    packvoltvalid=0
+                    packvoltvalid_time=packvoltvalid_time+(time2-time1).total_seconds()
+                    if packvoltvalid_time>df_adjust_param.loc[6,'confirm_time']:
+                        fault_code='304'
+                        # faultlv=3
+                        # faultinfo='电池包电压无效'
+                        fault_reason='1.电池包电压采样电路异常,2.数据通讯异常'
+                        fault_location='电池包电压'
+                        fault_advice='召回电池包,进行检修'
+                        # influence='失去对电池包电压监测'
+                        df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                        packvoltvalid_time=0
+                    else:
+                        pass
+                else:
+                    packvoltvalid=1
+                    packvoltvalid_time=0   
+            else:
+                packvoltvalid=0
+                if packvolt1>2.2*df_pack_param.iloc[0]['cellvoltnum'] and packvolt2<4.3*df_pack_param.iloc[0]['cellvoltnum']:
+                    packvoltvalid_time=packvoltvalid_time+(time2-time1).total_seconds()
+                    if packvoltvalid_time>df_adjust_param.loc[6,'confirm_time']:
+                        packvoltvalid=1
+                        df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C304'].index[-1], 'end_time'] = time2
+                        packvoltvalid_time=0
+                    else:
+                        pass
+                else:
+                    packvoltvalid_time=0
+
+            if packvoltvalid==1:
+                #电池包过压-7...................................................................................................
+                if not 'C402' in list(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if packvolt1>df_adjust_param.loc[7,'threshold'] and packvolt2>df_adjust_param.loc[7,'threshold']:   #电池包过压二级进入
+                        pov_time=pov_time+(time2-time1).total_seconds()
+                        if pov_time>df_adjust_param.loc[7,'confirm_time']:
+                            fault_code='C402'
+                            # faultlv=4
+                            # faultinfo='电池包过压'
+                            fault_location='电池包电压'
+                            # influence='长时间过压会导致电池析锂,存在电池安全与寿命衰减过快风险'
+                            cellocvmax=cellvoltmax2-self.packcrnt[i]*sor_mean*2 #内阻反推OCV
+                            cellsocmax=np.interp(cellocvmax,df_pack_param.iloc[0]['charge_ocv_v'],df_pack_param.iloc[0]['charge_ocv_soc'])  #ocv反查表得到SOC
+                            if self.bmssoc[i]<90 and self.packcrnt[i]>-df_pack_param.iloc[0]['capacity']/10 and self.packcrnt[i-1]>-df_pack_param.iloc[0]['capacity']/10:
+                                fault_reason='BMS计算SOC偏低'
+                                fault_advice='优化SOC算法'
+                            elif self.bmssoc[i]<cellsocmax-10:
+                                fault_reason='BMS计算SOC偏低'
+                                fault_advice='优化SOC算法'
+                            elif self.packcrnt[i-1]<-df_pack_param.iloc[0]['capacity']/2 or self.packcrnt[i]<-df_pack_param.iloc[0]['capacity']/2:
+                                fault_reason='充电电流过大'
+                                fault_advice='优化充放电功率限值'
+                            else:
+                                fault_reason='1.BMS软件策略BUG,2.继电器粘连'
+                                fault_advice='检修电池包'
+                            df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                            pov_time=0
+                        else:
+                            pass
+                    else:
+                        pov_time=0
+                else:
+                    if packvolt1<df_adjust_param.loc[7,'threshold']-10 and packvolt2<df_adjust_param.loc[7,'threshold']-10: #电池包过压二级恢复
+                        pov_time=pov_time+(time2-time1).total_seconds()
+                        if pov_time>df_adjust_param.loc[7,'confirm_time']:
+                            df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C402'].index[-1], 'end_time'] = time2
+                            pov_time=0
+                        else:
+                            pass
+                    else:
+                        pov_time=0
+                #电池包欠压-8.......................................................................................................................................
+                if not 'C203' in list(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if packvolt1<df_adjust_param.loc[8,'threshold'] and packvolt2<df_adjust_param.loc[7,'threshold']:   #电池包二级欠压进入
+                        puv_time=puv_time+(time2-time1).total_seconds()
+                        if puv_time>df_adjust_param.loc[8,'confirm_time']:
+                            fault_code='C203'
+                            # faultlv=2
+                            # faultinfo='电池包欠压'
+                            fault_location='电池包电压'
+                            # influence='欠压可能导致电池过放,严重过放会导致负极集流体溶解,进而发生内短路风险'
+                            cellocvmin=cellvoltmin2+-self.packcrnt[i]*sor_mean*2 #内阻反推OCV
+                            cellsocmin=np.interp(cellocvmin,df_pack_param.iloc[0]['discharge_ocv_v'],df_pack_param.iloc[0]['discharge_ocv_soc'])  #ocv反查表得到SOC
+                            if self.bmssoc[i]>10 and self.packcrnt[i]<df_pack_param.iloc[0]['capacity']/10 and self.packcrnt[i-1]<df_pack_param.iloc[0]['capacity']/10:
+                                fault_reason='BMS计算SOC偏高'
+                                fault_advice='优化SOC算法'
+                            elif self.bmssoc[i]>cellsocmin+10:
+                                fault_reason='BMS计算SOC偏高'
+                                fault_advice='优化SOC算法'
+                            elif self.packcrnt[i-1]>df_pack_param.iloc[0]['capacity']/2 or self.packcrnt[i]>df_pack_param.iloc[0]['capacity']/2:
+                                fault_reason='放电电流过大'
+                                fault_advice='优化充放电功率限值'
+                            else:
+                                fault_reason='1.BMS软件策略BUG,2.继电器粘连'
+                                fault_advice='检修电池包'
+                            df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                            puv_time=0
+                        else:
+                            pass
+                    else:
+                        puv_time=0
+                else:
+                    if packvolt1>df_adjust_param.loc[8,'threshold']+10 and packvolt2>df_adjust_param.loc[8,'threshold']+10:
+                        puv_time=puv_time+(time2-time1).total_seconds()
+                        if puv_time>df_adjust_param.loc[8,'remove_time']: 
+                            df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C203'].index[-1], 'end_time'] = time2
+                            puv_time=0
+                        else:
+                            pass
+                    else:
+                        puv_time=0
+            else:
+                pov_time=0
+                puv_time=0 
+                packvoltvalid_time=0
+                
+            #温度有效性判断-9................................................................................................................................................
+            if not 'C301' in list(df_diag_now['fault_code']):
+                if celltempmax2>int(eval(df_algo_param.loc[9,'global_param'])['tempuplmt']) or celltempmin2<int(eval(df_algo_param.loc[9,'global_param'])['templwlmt']):
+                    celltempvalid=0
+                    tempvalid_time=tempvalid_time+(time2-time1).total_seconds()
+                    if tempvalid_time>df_adjust_param.loc[9,'confirm_time']:
+                        fault_code='301'
+                        # faultlv=3
+                        # faultinfo='电芯温度无效'
+                        fault_reason='1.温度采样电路异常,2.数据通讯异常'
+                        fault_location='温度探针{}'.format(list(np.argwhere(np.array(celltemp2)<int(eval(df_algo_param.loc[9,'global_param'])['templwlmt']))+1)+list(np.argwhere(np.array(celltemp2)>int(eval(df_algo_param.loc[9,'global_param'])['tempuplmt']))+1))
+                        fault_advice='召回电池包,进行检修'
+                        # influence='失去对电池温度监测'
+                        df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                        tempvalid_time=0
+                    else:
+                        pass
+                else:
+                    celltempvalid=1
+                    tempvalid_time=0   
+            else:
+                celltempvalid=0
+                if -25<celltempmax1<60 and -25<celltempmax2<60:
+                    tempvalid_time=tempvalid_time+(time2-time1).total_seconds()
+                    if tempvalid_time>df_adjust_param.loc[9,'remove_time']:
+                        df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C304'].index[-1], 'end_time'] = time2
+                        tempvalid_time=0
+                    else:
+                        pass
+                else:
+                    tempvalid_time=0
+           
+            if celltempvalid==1:
+                #过温判断-10.............................................................................................................
+                if not 'C302' in list(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if celltempmax1>df_adjust_param.loc[10,'threshold'] and celltempmax2>df_adjust_param.loc[10,'threshold']:    #二级高温进入
+                        ot_time=ot_time+(time2-time1).total_seconds()
+                        if ot_time>df_adjust_param.loc[10,'confirm_time']:
+                            fault_code='C302'
+                            # faultlv=3
+                            # faultinfo='电芯温度过高'
+                            fault_location='温度探针{}'.format(list(np.argwhere(np.array(celltemp2)>df_adjust_param.loc[10,'threshold'])+1))
+                            fault_advice='禁止充放电,并开启电池冷却功能'
+                            # influence='高温下充放电,SEI膜增长加速,导致容量衰减过快,温度过高则存在热失控风险'
+                            if max(celltemp1_3sigma)>3 and max(celltemp2_3sigma)>3:
+                                fault_reason='Busbar连接异常'  
+                                fault_advice='检修电池包'
+                            elif sum(self.packcrnt[:i]*self.packvolt[:i])/(1000*(i+1))>50:
+                                fault_reason='电池持续输出大功率'
+                                fault_advice='优化电池充放电功率限值'
+                            else:
+                                fault_reason='1.冷却液温度过高,2.冷却水泵异常,3.Busbar连接异常'
+                                fault_advice='检修电池包'
+                            df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                            ot_time=0
+                        else:
+                            pass
+                    else:
+                        ot_time=0
+                else:   #ram当前故障中有该故障,则判断是否退出该故障
+                    if celltempmax1<df_adjust_param.loc[10,'threshold']-5 and celltempmax2<df_adjust_param.loc[10,'threshold']-5:
+                        ot_time=ot_time+(time2-time1).total_seconds()
+                        if ot_time>df_adjust_param.loc[10,'remove_time']:
+                            df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C302'].index[-1], 'end_time'] = time2
+                            ot_time=0
+                        else:
+                            pass
+                    else:
+                        ot_time=0
+            
+                #欠温判断-11.................................................................................................................
+                if not 'C102' in list(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if celltempmin1<df_adjust_param.loc[11,'threshold'] and celltempmin2<df_adjust_param.loc[11,'threshold']:  #二级低温进入
+                        ut_time=ut_time+(time2-time1).total_seconds()
+                        if ut_time>df_adjust_param.loc[11,'confirm_time']:
+                            fault_code='C102'
+                            # faultlv=1
+                            # faultinfo='电芯温度过低'
+                            fault_location='温度探针{}'.format(list(np.argwhere(np.array(celltemp2)<df_adjust_param.loc[11,'threshold'])+1))
+                            # influence='低温下充电,会导致析锂,存在电池安全与寿命衰减过快风险'
+                            if self.enmtemp[i-1]-celltempmin1>10 and self.enmtemp[i]-celltempmin2>10:
+                                fault_reason='温度检测系统异常' 
+                                fault_advice='检修电池包' 
+                            elif self.bmssoc[i-1]<5 and self.bmssoc[i]<5:
+                                fault_reason='电池包SOC过低'
+                                fault_advice='通知用户进行充电'
+                            else:
+                                fault_reason='PTC加热系统异常'
+                            df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                            ut_time=0
+                        else:
+                            pass
+                    else:
+                        ut_time=0
+                else:   #ram当前故障中有该故障,则判断是否退出该故障
+                    if celltempmax1>df_adjust_param.loc[11,'threshold']+2 and celltempmax2>df_adjust_param.loc[11,'threshold']+2:    #二级高温恢复
+                        ut_time=ut_time+(time2-time1).total_seconds()
+                        if ut_time>df_adjust_param.loc[11,'remove_time']:
+                            df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C102'].index[-1], 'end_time'] = time2
+                            ut_time=0
+                        else:
+                            pass
+                    else:
+                        ut_time=0
+              
+                #温差判断-12.............................................................................................................................
+                if not 'C103' in list(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if (celltempmax1-celltempmin1)>df_adjust_param.loc[12,'threshold'] and (celltempmax2-celltempmin2)>df_adjust_param.loc[12,'threshold']:  #二级温差进入
+                        dt_time=dt_time+(time2-time1).total_seconds()
+                        if dt_time>df_adjust_param.loc[12,'confirm_time']:
+                            fault_code='C103'
+                            # faultlv=1
+                            # faultinfo='电芯温差过大'
+                            fault_location='温度探针{}'.format([list(celltemp2).index(celltempmin2)+1,list(celltemp2).index(celltempmax2)+1])
+                            # influence='存在电芯的老化速率不一致的风险'
+                            if self.enmtemp[i-1]>0 and self.enmtemp>0 and (('C316' in list(df_diag_now['fault_code'])) or ('C317' in list(df_diag_now['fault_code']))):
+                                fault_reason='电芯内阻不一致' 
+                                fault_advice='更换模组'
+                            elif self.enmtemp[i-1]>10 and self.enmtemp>10:
+                                fault_reason='Busbar连接异常' 
+                                fault_advice='检修电池包'
+                            elif self.enmtemp[i-1]<-10 and self.enmtemp<-10:
+                                fault_reason='环境温度过低'
+                                fault_advice='合理优化低温充放电功率限值,并开启水泵'
+                            else:
+                                fault_reason='电池包冷却/加热系统设计不合理'
+                                fault_advice='合理优化电池包冷却/加热系统'
+                            df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                            dt_time=0
+                        else:
+                            pass
+                    else:
+                        dt_time=0
+                else:   #ram当前故障中有该故障,则判断是否退出该故障
+                    if (celltempmax1-celltempmin1)<df_adjust_param.loc[12,'threshold']-5 and (celltempmax2-celltempmin2)>df_adjust_param.loc[12,'threshold']-5:  #二级温差恢复
+                        dt_time=dt_time+(time2-time1).total_seconds()
+                        if dt_time>df_adjust_param.loc[12,'remove_time']:
+                            df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C103'].index[-1], 'end_time'] = time2
+                            dt_time=0
+                        else:
+                            pass
+                    else:
+                        dt_time=0
+                
+                #温度离群判断-13.............................................................................................................................
+                if not 'C105' in list(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if (max(celltemp1_3sigma)>3 and max(celltemp2_3sigma)>3) or (min(celltemp1_3sigma)<-3 and min(celltemp2_3sigma)<-3):  
+                        tempstray_time=tempstray_time+(time2-time1).total_seconds()
+                        if tempstray_time>df_adjust_param.loc[13,'confirm_time']:
+                            fault_code='C105'
+                            # faultlv=1
+                            # faultinfo='电芯温度离群'
+                            fault_location='温度探针{}'.format([list(celltemp2).index(celltempmax2)+1,list(celltemp2).index(celltempmin2)+1])
+                            # influence='存在电芯的老化速率不一致的风险'
+                            if self.enmtemp[i-1]>0 and self.enmtemp>0 and (('C316' in list(df_diag_now['fault_code'])) or ('C317' in list(df_diag_now['fault_code']))):
+                                fault_reason='电芯内阻不一致' 
+                                fault_advice='更换模组'
+                            elif self.enmtemp[i-1]>10 and self.enmtemp>10:
+                                fault_reason='Busbar连接异常' 
+                                fault_advice='检修电池包'
+                            elif self.enmtemp[i-1]<-10 and self.enmtemp<-10:
+                                fault_reason='环境温度过低'
+                                fault_advice='合理优化低温充放电功率限值,并开启水泵'
+                            else:
+                                fault_reason='电池包冷却/加热系统设计不合理'
+                                fault_advice='合理优化电池包冷却/加热系统'
+                            df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                            tempstray_time=0
+                    else:
+                        tempstray_time=0
+                else:   #ram当前故障中有该故障,则判断是否退出该故障
+                    if (max(celltemp1_3sigma)<3 and max(celltemp2_3sigma)<3) or (min(celltemp1_3sigma)>-3 and min(celltemp2_3sigma)>-3):
+                        tempstray_time=tempstray_time+(time2-time1).total_seconds()
+                        if tempstray_time>df_adjust_param.loc[13,'remove_time']:
+                            df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C105'].index[-1], 'end_time'] = time2
+                            tempstray_time=0
+                        else:
+                            pass
+                    else:
+                        tempstray_time=0 
+                
+                #温升速率-14...............................................................................................................................
+                rtac_time=(time2-self.bmstime[celltemp_stnum]).total_seconds()
+                if rtac_time>60:
+                    celltemp_st=np.array(self._celltemp_get(celltemp_stnum))
+                    celltemprate=(np.array(celltemp2)-celltemp_st)/(rtac_time*60)
+                    celltemp_stnum=celltemp_stnum+1
+                    if not 'C303' in list(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+                        if max(celltemprate)>df_adjust_param.loc[14,'threshold']:
+                            rt_time=rt_time+(time2-time1).total_seconds()
+                            if rt_time>df_adjust_param.loc[14,'confirm_time']:
+                                fault_code='C303'
+                                # faultlv=3
+                                # faultinfo='电芯温升过快'
+                                fault_location='温度探针{}'.format((np.argwhere(celltemp_st>df_adjust_param.loc[14,'threshold'])+1).tolist())
+                                # influence='温升速率过快,存在热失控风险'
+                                if max(celltemp1_3sigma)>3 and max(celltemp2_3sigma)>3:
+                                    fault_reason='Busbar连接异常'  
+                                    fault_advice='检修电池包'
+                                elif sum(self.packcrnt[:i]*self.packvolt[:i])/(1000*(i+1))>50:
+                                    fault_reason='电池持续输出大功率'
+                                    fault_advice='优化电池充放电功率限值'
+                                else:
+                                    fault_reason='1.冷却液温度过高,2.冷却水泵异常,3.Busbar连接异常'
+                                    fault_advice='检修电池包'
+                                df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                                rt_time=0
+                            else:
+                                pass
+                        else:
+                            rt_time=0
+                    else:   #ram当前故障中有该故障,则判断是否退出该故障
+                        if max(celltemprate)<2:
+                            rt_time=rt_time+(time2-time1).total_seconds()
+                            if rt_time<df_adjust_param.loc[14,'remove_time']:
+                                df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='303'].index[-1], 'end_time'] = time2
+                                rt_time=0
+                            else:
+                                pass
+                        else:
+                            rt_time=0
+            
+            else:
+                ot_time=0
+                ut_time=0
+                dt_time=0
+                rt_time=0
+                tempstray_time=0
+            
+            #放电过流诊断-16.......................................................................................................................           
+            if not 'C306' in list(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if self.packcrnt[i]>df_adjust_param.loc[16,'threshold'] and self.packcrnt[i-1]>df_adjust_param.loc[16,'threshold']:
+                    disoc_time=disoc_time+(time2-time1).total_seconds()
+                    if disoc_time>df_adjust_param.loc[16,'confirm_time']:
+                        fault_code='C306'
+                        # faultlv=3
+                        # faultinfo='电池放电过流'
+                        fault_location='电池包'
+                        # influence='长时间过流会导致电池欠压及温升过快'
+                        if cellvoltmin1<df_adjust_param.loc[3,'threshold']+0.5 and cellvoltmin2<df_adjust_param.loc[3,'threshold']+0.5:
+                            fault_reason='BMS控制策略异常'
+                            fault_advice='优化BMS控制策略'
+                        else:
+                            fault_reason='1.充电器异常,2.继电器粘连'
+                            fault_advice='停止放电'  
+                        df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                        disoc_time=0
+                    else:
+                        pass
+                else:
+                    disoc_time=0
+            else:
+                if self.packcrnt[i]<df_adjust_param.loc[16,'threshold']-10 and self.packcrnt[i-1]<df_adjust_param.loc[16,'threshold']-10:
+                    disoc_time=disoc_time+(time2-time1).total_seconds()
+                    if disoc_time>df_adjust_param.loc[16,'remove_time']:
+                        df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C306'].index[-1], 'end_time'] = time2
+                        disoc_time=0
+                    else:
+                        pass
+                else:
+                    disoc_time=0
+            #充电过流-15.................................................................................
+            if not 'C305' in list(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if self.packcrnt[i]<df_adjust_param.loc[15,'threshold'] and self.packcrnt[i-1]<df_adjust_param.loc[15,'threshold']:
+                    chgoc_time=chgoc_time+(time2-time1).total_seconds()
+                    if chgoc_time>df_adjust_param.loc[15,'confirm_time']:
+                        fault_code='C305'
+                        # faultlv=3
+                        # faultinfo='电池充电过流'
+                        fault_location='电池包'
+                        # influence='过流会导致电池析锂,存在电池安全与寿命衰减过快风险'
+                        if cellvoltmax1>df_adjust_param.loc[2,'threshold']-0.1 and cellvoltmax2>df_adjust_param.loc[2,'threshold']-0.1:
+                            fault_reason='BMS控制策略异常'
+                            fault_advice='优化BMS控制策略'
+                        else:
+                            fault_reason='1.充电器异常,2.继电器粘连'
+                            fault_advice='停止放电'  
+                        df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                        chgoc_time=0
+                    else:
+                        pass
+            else:
+                if self.packcrnt[i]>df_adjust_param.loc[15,'threshold']+10 and self.packcrnt[i-1]>df_adjust_param.loc[15,'threshold']+10:
+                    chgoc_time=chgoc_time+(time2-time1).total_seconds()
+                    if chgoc_time>df_adjust_param.loc[15,'remove_time']:
+                        df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C305'].index[-1], 'end_time'] = time2
+                        chgoc_time=0
+                    else:
+                        pass
+                else:
+                    chgoc_time=0
+            
+            #SOC故障诊断........................................................................................................................
+            #SOC卡滞-18       
+                if not 'C106' in list(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    step=(time2-time1).total_seconds()
+                    if step<120:
+                        ah_accum=ah_accum-self.packcrnt[i]*step/3600    #ah累计
+                    if abs(ah_accum)>df_pack_param.iloc[0]['capacity']*0.1:   
+                        bmssoc_now=self.bmssoc[i]  
+                        if abs(bmssoc_now-bmssoc_st)<df_adjust_param.loc[18,'threshold']:   #SOC卡滞故障进入
+                            fault_code='C106'
+                            # faultlv=1
+                            # faultinfo='电池SOC卡滞'
+                            fault_location='电池包SOC'
+                            if cellvoltvalid==0:
+                                fault_reason='电压采样异常'
+                                fault_advice='检查电芯电压采样系统'
+                            else:
+                                fault_reason='BMS软件SOC估算异常'
+                                fault_advice='检修BMS软件'
+                            df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                        else:
+                            pass
+                        bmssoc_st=bmssoc_now
+                        ah_accum=0
+                else:
+                    if abs(bmssoc_now-bmssoc_st)>df_adjust_param.loc[18,'threshold']:   #SOC卡滞故障退出
+                        df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C106'].index[-1], 'end_time'] = time2
+                    else:
+                        pass
+
+            #SOC跳变-17....................................................................................................................
+            bmssoc_last=self.bmssoc[i-1]
+            bmssoc_now=self.bmssoc[i]
+            if not 'C107' in list(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if step<70 and abs(bmssoc_now-bmssoc_last)>df_adjust_param.loc[17,'threshold']:  #SOC跳变进入
+                    fault_code='C107'
+                    # faultlv=1
+                    # faultinfo='电池SOC跳变'
+                    fault_location='电池包SOC'
+                    if cellvoltvalid==0:
+                        fault_reason='电压采样异常'
+                        fault_advice='检查电芯电压采样系统'
+                    else:
+                        fault_reason='BMS软件SOC估算异常'
+                        fault_advice='检修BMS软件'
+                    df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                else:
+                    pass
+            else:
+                if abs(bmssoc_now-bmssoc_last)<df_adjust_param.loc[17,'threshold']:    #SOC跳变故障退出
+                    df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C107'].index[-1], 'end_time'] = time2
+                else:
+                    pass
+            
+            #绝缘故障检测
+            if not 'C315' in list(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+                if self.isor[i-1]<df_adjust_param.loc[19,'threshold'] and self.isor[i]<df_adjust_param.loc[19,'threshold']:
+                    isor_time=isor_time+(time2-time1).total_seconds()
+                    if isor_time>df_adjust_param.loc[19,'confirm_time']:
+                        fault_code='C315'
+                        # faultlv=3
+                        # faultinfo='绝缘异常'
+                        fault_advice='召回车辆,检修高压线路/接插件'
+                        if self.isor[i-1]<5 and self.isor[i]<5:
+                            fault_reason='绝缘检测系统异常'
+                            fault_location='绝缘检测系统'
+                        elif self.bmsstate[i-1]==2 or self.bmsstate[i-1]==3:
+                            fault_reason='充电桩绝缘异常'
+                            fault_location='充电桩'
+                            fault_advice='通知用户使用其他充电桩进行充电'
+                        elif self.vehstate[i-1]!=2 and self.vehstate[i]!=2:
+                            fault_reason='电池包内高压线路/接插件异常'
+                            fault_location='电池包内高压线路/接插件'
+                        else:
+                            fault_reason='电池包外高压线路/接插件异常'
+                            fault_location='电池包外高压线路/接插件'
+                        df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                        isor_time=0
+                    else:
+                        pass
+                else:
+                    isor_time=0
+            else:
+                if self.isor[i-1]>df_adjust_param.loc[19,'threshold']+100 and self.isor[i]>df_adjust_param.loc[19,'threshold']+100:
+                    isor_time=isor_time+(time2-time1).total_seconds()
+                    if isor_time>df_adjust_param.loc[19,'remove_time']:
+                        df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C315'].index[-1], 'end_time'] = time2
+                        isor_time=0
+                    else:
+                        pass
+                else:
+                    isor_time=0
+        
+        # #SOC一致性故障报警..........................................................................................................
+        # if not self.df_uniform.empty:
+        #     cellsoc_diff=self.df_uniform.loc[0,'cellsoc_diff']
+        #     if not 'C201' in list(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+        #         if cellsoc_diff>self.param.SocDiff: #SOC一致性差故障进入
+        #             time=self.bmstime[0]
+        #             fault_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)
+        #             fault_advice='技术介入诊断'
+        #             df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.sn, fault_code, faultlv, faultinfo, fault_advice]
+        #         else:
+        #             pass
+        #     else:
+        #         if cellsoc_diff<self.param.SocDiff: #SOC一致性差故障恢复
+        #             time=self.bmstime[0]
+        #             df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C201'].index[-1], 'end_time'] = time2
+        # else:
+        #     cellsoc_diff=3
+
+        #容量过低和一致性故障报警-20-21................................................................................................
+        if not df_soh.empty:
+            soh=df_soh.loc[0,'soh']
+            cellsoh=eval(df_soh.loc[0,'cellsoh'])
+            cellsoh=np.array(cellsoh)
+            cellsoh_lowindex=np.argwhere(cellsoh<df_adjust_param.loc[20,'threshold']).tolist()
+            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(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if soh<df_adjust_param.loc[20,'threshold']:   #soh过低故障进入
+                        fault_code='C204'
+                        # faultlv=2
+                        # faultinfo='电池包容量过低'
+                        fault_location='电芯{}'.format(cellsoh_lowindex)
+                        fault_advice='更换容量过低的模组/电池包'
+                        fault_reason=''
+                        df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                    else:
+                        pass
+                else:
+                    if soh>df_adjust_param.loc[20,'threshold']+2:   #soh过低故障恢复
+                        df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C204'].index[-1], 'end_time'] = time2
+                    else:
+                        pass
+
+                if not 'C205' in list(df_diag_now['fault_code']):  #当前故障中没有该故障,则判断是否发生该故障
+                    if cellsoh_diff>df_adjust_param.loc[21,'threshold']:
+                        fault_code='C205'
+                        # faultlv=2
+                        # faultinfo='电池包容量一致性差'
+                        fault_advice='检修电池,更换容量过低的电芯或模组'
+                        fault_location='电芯{}'.format(cellsoh_lowindex)
+                        fault_advice='更换容量过低的模组/电池包'
+                        fault_reason=''
+                        df_diag_ram.loc[len(df_diag_ram)]=[time2, end_time, self.vin, self.sn, self.model, fault_code, fault_reason, fault_advice, fault_location]
+                    else:
+                        pass
+                else:
+                    if cellsoh_diff<df_adjust_param.loc[21,'threshold']-2:
+                        df_diag_ram.loc[df_diag_ram[df_diag_ram['fault_code']=='C205'].index[-1], 'end_time'] = time2
+                    else:
+                        pass
+            else:
+                pass
+        else:
+            cellsoh_diff=5
+
+        return df_diag_ram
+                
+
+
+
+                
+
+
+                
+
+

+ 24 - 0
USER/SPF/WORK/03hezhong/04BatDiag/log.py

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

+ 86 - 0
USER/SPF/WORK/03hezhong/04BatDiag/main.py

@@ -0,0 +1,86 @@
+import CBMSBatDiag
+import log
+import pymysql
+from apscheduler.schedulers.blocking import BlockingScheduler
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+import time, datetime
+import os
+
+def diag_cal():
+    filepath=r'D:\Develop\User\Songpengfei\data_analyze_platform\WORK\03hezhong\98Data'
+    files=os.listdir(filepath)
+
+    # 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)
+    
+    #数据库配置
+    host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port=3306
+    user='hz_dev'
+    password='Qx@123456'
+
+    #读取故障结果库中code==110且end_time='0000-00-00 00:00:00'...............................
+    db='hz_dev'
+    mysql = pymysql.connect (host=host, user=user, password=password, port=port, database=db)
+    cursor = mysql.cursor()
+    param='algo_id,pack_code,threshold,confirm_time,remove_time,param'
+    sql =  "select %s from %s" %(param,'algo_adjustable_param')
+    cursor.execute(sql)
+    res = cursor.fetchall()
+    df_adjust_param= pd.DataFrame(res,columns=param.split(','))
+    
+    param='algo_id,is_activate,global_param'
+    sql =  "select %s from %s" %(param,'algo_list')
+    cursor.execute(sql)
+    res = cursor.fetchall()
+    df_algo_param= pd.DataFrame(res,columns=param.split(','))
+    df_algo_param=df_algo_param.set_index('algo_id')
+    
+    param='pack_code,param'
+    sql =  "select %s from %s" %(param,'algo_pack_param')
+    cursor.execute(sql)
+    res = cursor.fetchall()
+    df_pack_param= pd.DataFrame(res,columns=param.split(','))
+    df_pack_param['param']=df_pack_param['param'].map(lambda x:eval(x))
+    a=df_pack_param['param'].values.tolist()
+    df_pack_param=pd.DataFrame(a)
+    
+    cursor.close()
+    mysql.close()
+
+    #log信息配置
+    mylog=log.Mylog('log.txt','error')
+    mylog.logcfg()
+
+    for filename in files:
+        try:
+            df_bms = pd.read_excel(filepath+'\\'+filename)
+            df_diag_ram=pd.DataFrame(columns=['start_time', 'end_time', 'vin', 'sn', 'model', 'fault_code', 'fault_reason', 'fault_advice', 'fault_location'])
+            df_soh=pd.DataFrame()
+            df_sor=pd.DataFrame()
+
+            BatDiag=CBMSBatDiag.BatDiag(df_bms)
+            df_res=BatDiag.diag(df_diag_ram, df_soh, df_sor, df_adjust_param, df_pack_param, df_algo_param)
+            if not df_res.empty:
+                with open('故障结果.txt','a') as file:
+                    file.write(str(df_res)+'\n')
+             
+        except Exception as e:
+            print(repr(e))
+            mylog.logopt(e)
+            pass
+    
+if __name__ == "__main__":
+
+    scheduler = BlockingScheduler()
+    scheduler.add_job(diag_cal, 'interval', seconds=60)
+
+    try:  
+        scheduler.start()
+    except:
+        scheduler.shutdown()

+ 129 - 0
USER/SPF/WORK/03hezhong/05BatChrg/BatParam.py

@@ -0,0 +1,129 @@
+
+#定义电池参数
+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]
+        
+        elif celltype==100:   #CATL 102ah三元电芯
+            self.Capacity = 102
+            self.PackFullChrgVolt=80
+            self.CellFullChrgVolt=4.2
+            self.CellVoltNums=96
+            self.CellTempNums=16
+            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]
+            self.LookTab_OCV = [3.397,	3.429,	3.462,	3.507,	3.551,	3.575,	3.600,	3.617,	3.633,	3.658,	3.683,	3.735,	3.786,	3.836,	3.887,	3.939,	3.992,	4.053,	4.113,	4.195,	4.277]
+
+            self.CellTempHighLv1=45
+            self.CellTempHighLv2=50
+            self.CellTempLowLv1=-20
+            self.CellTempLowLv2=-25
+            self.CellTempDiffLv1=10
+            self.CellTempDiffLv2=15
+            self.CellTempRate=0.99
+
+            self.CellOvLv1=4.3
+            self.CellOvLv2=4.35
+            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=1.5*self.Capacity
+            self.PackDisChgOc=3*self.Capacity
+
+            self.SocJump=3
+            self.SocClamp=3
+
+        else:
+            print('未找到对应电池编号!!!')
+            # sys.exit()
+

+ 276 - 0
USER/SPF/WORK/03hezhong/05BatChrg/CBMSBatChrg.py

@@ -0,0 +1,276 @@
+import pandas as pd
+import numpy as np
+import bisect
+import datetime
+import matplotlib.pyplot as plt
+import matplotlib.dates as mdates
+import BatParam
+
+class BatChrg:
+    def __init__(self,sn,celltype,df_bms,df_volt,df_temp,df_accum):  #参数初始化
+
+        self.sn=sn
+        self.celltype=celltype
+        self.param=BatParam.BatParam(celltype)
+        self.df_volt=df_volt
+        self.df_temp=df_temp
+        self.df_bms=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.bmsstat=df_bms['充电状态']
+        self.bmstime= pd.to_datetime(df_volt['上报时间'], format='%Y-%m-%d %H:%M:%S')
+        self.param.CellVoltNums=int(df_volt.loc[5,'单体电池总数'])
+        self.param.CellTempNums=int(df_temp.loc[5,'可充电储能温度探针个数'])
+        self.param.PackVoltOvLv1=self.param.CellOvLv1*self.param.CellVoltNums
+        self.param.PackVoltOvLv2=self.param.CellOvLv2*self.param.CellVoltNums
+        self.param.PackVoltUvLv1=self.param.CellUvLv1*self.param.CellVoltNums
+        self.param.PackVoltUvLv2=self.param.CellUvLv2*self.param.CellVoltNums
+    
+    def chrg(self):
+        if self.celltype==1 or self.celltype==2 or self.celltype==3 or self.celltype==4 or self.celltype==100:
+            df_res=self._ncm_chrg()
+            return df_res
+            
+        elif self.celltype==99:
+            df_res=self._lfp_diag()
+            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_get(self,num):   
+        celltemp = []
+        for j in range(1, self.param.CellTempNums+1):
+            celltemp.append(self.df_temp.loc[num,str(j)+'.0'])
+        return celltemp
+
+    #获取当前行所有电压数据........................................................................................
+    def _cellvolt_get(self,num): 
+        cellvolt=[]
+        for j in range(1, self.param.CellVoltNums+1): 
+            cellvolt.append(self.df_volt.loc[num, str(j)+'.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.002 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=600)
+        # 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['time']<end_time)]
+        # df_Data1=df_Data1[(df_Data1['XVOLT']>self.param.PeakVoltLowLmt) & (df_Data1['XVOLT']<self.param.PeakVoltUpLmt)]
+
+        # plt.figure()
+        # 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(2, 1, 1)
+        plt.plot(df_Data1['SOC'],df_Data1['XVOLT'],'y*-')
+        plt.xlabel('SOC/%')
+        plt.ylabel('Volt/V')
+        plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
+        plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
+        plt.legend()
+        ax1 = plt.subplot(2, 1, 2)
+        plt.plot(df_Data1['SOC'], df_Data1['DVDQ'], 'r*-')
+        plt.xlabel('SOC/%')
+        plt.ylabel('析锂指标')
+        plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
+        plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
+        plt.legend()
+        # plt.show()
+
+    #..........................................三元电池充电画像.....................。。。......................................
+    def _ncm_chrg(self):
+        
+        
+        if not self.df_volt.empty:
+            self._chrgdata()
+            print(self.ChgStart,self.ChgEnd)
+            # name=str(self.sn)
+            # with pd.ExcelWriter(r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\99Result\\'+name+'.xlsx') as writer:
+            
+            for i in range(len(self.ChgStart)):
+                # df_chgvolt=pd.DataFrame()
+                # chrg_time=self.df_volt['上报时间'][self.ChgStart[i]:self.ChgEnd[i]]
+                # chrg_soc=self.df_bms['SOC'][self.ChgStart[i]:self.ChgEnd[i]]
+                # chrg_crnt=self.df_bms['总电流(A)'][self.ChgStart[i]:self.ChgEnd[i]]
+                for k in range(1, self.param.CellVoltNums+1):
+                    s = str(k)
+                    cellvolt = self.df_volt[s+'.0']
+                    self._dvdq_soh(self.ChgStart[i],self.ChgEnd[i],cellvolt)     #dvdq计算soh
+                plt.show()
+                    
+                    # plt.figure(figsize=(20,10))
+                    # ax1 = plt.subplot(3, 1, 1)
+                    # plt.plot(chrg_soc,'y*-')
+                    # plt.ylabel('SOC/A')
+                    # plt.legend()
+                    # plt.title(str(self.sn)+str(self.df_volt.loc[self.ChgStart[i],'上报时间']))
+
+                    # ax2 = plt.subplot(3, 1, 2)
+                    # plt.plot(chrg_crnt,'y*-')
+                    # plt.ylabel('Crnt/A')
+                    # plt.legend()
+
+                    # for j in range(1, self.param.CellTempNums+1):
+                    #     celltemp=self.df_temp[str(j)+'.0'][self.ChgStart[i]:self.ChgEnd[i]]
+                    #     ax3 = plt.subplot(3, 1, 3)
+                    #     plt.plot(celltemp,'y*-')
+                    #     plt.xlabel('time')
+                    #     plt.ylabel('Temp/℃')
+                    #     plt.legend()
+                    # plt.savefig('./'+str(self.sn)+str(i)+'电流温度.png')
+                    
+                    # plt.figure()
+                    # for j in range(1, self.param.CellVoltNums+1): 
+                    #     cellvolt=self.df_volt[str(j)+'.0'][self.ChgStart[i]:self.ChgEnd[i]]
+                    #     df_chgvolt[j]=cellvolt
+                    # df_chgvolt.plot(linestyle=':',marker='*', figsize=(30,10))
+                    # plt.title(str(self.sn)+str(self.df_volt.loc[self.ChgStart[i],'上报时间']))
+                    # plt.savefig('./'+str(self.sn)+str(i)+'电压.png')
+
+
+                    #     if j <13:
+                    #         color1='r'
+                    #     elif j<25:
+                    #         color1='orange'
+                    #     elif j<37:
+                    #         color1='y'
+                    #     elif j<49:
+                    #         color1='g'
+                    #     elif j<61:
+                    #         color1='c'
+                    #     elif j<73:
+                    #         color1='b'
+                    #     elif j<85:
+                    #         color1='m'
+                    #     else:
+                    #         color1='pink'
+
+                    #     cellvolt=self.df_volt[str(j)+'.0'][self.ChgStart[i]:self.ChgEnd[i]]
+                    #     # ax1 = plt.subplot(3, 1, 1)
+                    #     plt.plot(chrg_time,cellvolt,color=color1,linestyle='-',marker='*')
+                    # plt.xlabel('SOC/%')
+                    # plt.ylabel('Volt/V')
+                    # plt.legend(loc='upper left')
+                    # plt.title(str(self.sn)+str(self.df_volt.loc[self.ChgStart[i],'上报时间']))
+
+                    # 标准差计算及电芯电压排序...................................................................................
+            #         std_error=[]
+            #         df_rank=pd.DataFrame(columns=list(range(1,self.param.CellVoltNums+1)))
+            #         for j in range(self.ChgStart[i],self.ChgEnd[i]):
+            #             cellvolt=self._cellvolt_get(j)
+            #             std_error.append(np.std(cellvolt,ddof=1))   #标准差计算
+
+            #             cellvolt=pd.Series(cellvolt)
+            #             cellvolt_rank=(cellvolt.rank(method='min')).tolist()
+            #             df_rank.loc[len(df_rank)]=cellvolt_rank
+            #         for j in range(1,self.param.CellVoltNums+1):
+            #             df_rank[j].plot(linestyle='-',marker='*')
+            #             # plt.legend()
+            #             # plt.title(str(self.sn)+str(self.df_volt.loc[self.ChgStart[i],'上报时间']))
+            #             # plt.savefig('./'+str(self.sn)+str(i)+'电压排名.png')
+            #             # plt.show() 
+                    
+            #         sheetname=str(self.df_volt.loc[self.ChgStart[i],'上报时间']).replace(':','-')
+            #         df_rank.to_excel(writer,sheet_name=sheetname)
+                
+            # writer.save()
+            # writer.close()
+
+            
+                # plt.figure(figsize=(20,10))
+                # plt.plot(std_error,'r*-')
+                # plt.xlabel('time')
+                # plt.ylabel('std_error')
+                # plt.legend()
+                # plt.title(str(self.sn)+str(self.df_volt.loc[self.ChgStart[i],'上报时间']))
+                # plt.savefig('./'+str(self.sn)+str(i)+'标准差.png')
+
+
+                
+
+                
+                    
+               
+        
+        return pd.DataFrame()
+                
+
+
+
+                
+
+
+                
+
+

+ 24 - 0
USER/SPF/WORK/03hezhong/05BatChrg/log.py

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

+ 58 - 0
USER/SPF/WORK/03hezhong/05BatChrg/main.py

@@ -0,0 +1,58 @@
+import CBMSBatChrg
+import log
+#coding=utf-8
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+from sqlalchemy import create_engine
+import time, datetime
+import os
+
+dbManager = DBManager.DBManager()
+if __name__ == "__main__":
+    filepath=r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\98Download'
+    files=os.listdir(filepath)
+
+    # now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+    # now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+    # start_time=now_time-datetime.timedelta(days=30)
+    # end_time=str(now_time)
+    # start_time=str(start_time)
+
+    #log信息配置
+    mylog=log.Mylog('log.txt','error')
+    mylog.logcfg()
+
+    for filename in files:
+        try:
+            # filename=files[0]
+            print(filename)
+            sn=filename[0:23]
+            celltype=100
+            start_time='2021-04-01 19:12:26'
+            end_time='2021-09-02 19:12:26'
+            df_volt = pd.read_excel(filepath+'\\'+filename,sheet_name='单体电池电压数据')
+            df_temp = pd.read_excel(filepath+'\\'+filename,sheet_name='单体电池温度数据')
+            df_bms = pd.read_excel(filepath+'\\'+filename,sheet_name='整车数据')
+            df_volt.columns=df_volt.loc[0].astype('str')
+            df_temp.columns=df_temp.loc[0].astype('str')
+            df_volt=df_volt.drop(0,axis=0)
+            df_temp=df_temp.drop(0,axis=0)
+            df_volt=df_volt.reset_index(drop=True)
+            df_temp=df_temp.reset_index(drop=True)
+            
+            df_accum=pd.DataFrame()
+
+            # df_volt.to_excel(r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\99Result\\'+'CBMS_v_'+sn+'.xlsx')
+            # df_temp.to_excel(r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\99Result\\'+'CBMS_t_'+sn+'.xlsx')
+            # print(df_volt['1.0'])
+
+            BatChrg=CBMSBatChrg.BatChrg(sn,celltype,df_bms,df_volt,df_temp,df_accum)
+            df_res=BatChrg.chrg()
+            # df_res.to_excel(r'D:\00WorkSpace\01Python\data_analyze_platform\USER\03hezhong\99Result\\'+'CBMS_diag_'+sn+'.xlsx')
+            print(sn,'done!!!')
+             
+        except IndexError as e:
+            print(repr(e))
+            mylog.logopt(sn,e)
+            pass

+ 132 - 0
USER/SPF/WORK/03hezhong/Common/BatParam.py

@@ -0,0 +1,132 @@
+
+#定义电池参数
+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 = 103
+            self.PackFullChrgVolt=69.99
+            self.CellFullChrgVolt=3.5
+            self.OcvInflexionBelow=3.285
+            self.OcvInflexion2=3.296
+            self.OcvInflexion3=3.328
+            self.OcvInflexionAbove=3.4
+            self.CellVoltNums=20
+            self.CellTempNums=4
+            self.FullChrgSoc=98
+            self.PeakSoc=59
+            self.PeakVoltLowLmt=3.35
+            self.PeakVoltUpLmt=3.4
+            self.PeakCellVolt=[3.362,3.363,3.365,3.366,3.367]
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0.00, 	2.40, 	6.38, 	10.37, 	14.35, 	18.33, 	22.32, 	26.30, 	30.28, 	35.26, 	40.24, 	45.22, 	50.20, 	54.19, 	58.17, 	60.16, 	65.14, 	70.12, 	75.10, 	80.08, 	84.06, 	88.05, 	92.03, 	96.02, 	100.00]
+            self.LookTab_OCV = [2.7151,	3.0298,	3.1935,	3.2009,	3.2167,	3.2393,	3.2561,	3.2703,	3.2843,	3.2871,	3.2874,	3.2868,	3.2896,	3.2917,	3.2967,	3.3128,	3.3283,	3.3286,	3.3287,	3.3288,	3.3289,	3.3296,	3.3302,	3.3314,	3.3429]
+        
+        elif celltype==100:   #CATL 102ah三元电芯
+            self.Capacity = 102
+            self.PackFullChrgVolt=80
+            self.CellFullChrgVolt=4.2
+            self.CellVoltNums=96
+            self.CellTempNums=16
+            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]
+            self.LookTab_OCV = [3.397,	3.429,	3.462,	3.507,	3.551,	3.575,	3.600,	3.617,	3.633,	3.658,	3.683,	3.735,	3.786,	3.836,	3.887,	3.939,	3.992,	4.053,	4.113,	4.195,	4.277]
+
+            self.CellTempHighLv1=45
+            self.CellTempHighLv2=50
+            self.CellTempLowLv1=-20
+            self.CellTempLowLv2=-25
+            self.CellTempDiffLv1=10
+            self.CellTempDiffLv2=15
+            self.CellTempRate=0.99
+
+            self.CellOvLv1=4.3
+            self.CellOvLv2=4.35
+            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=1.5*self.Capacity
+            self.PackDisChgOc=3*self.Capacity
+
+            self.SocJump=3
+            self.SocClamp=3
+
+        else:
+            print('未找到对应电池编号!!!')
+            # sys.exit()
+

+ 24 - 0
USER/SPF/WORK/03hezhong/Common/log.py

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

+ 51 - 0
USER/SPF/WORK/test.ipynb

@@ -0,0 +1,51 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "ename": "ValueError",
+     "evalue": "max() arg is an empty sequence",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[1;31mValueError\u001b[0m                                Traceback (most recent call last)",
+      "\u001b[1;32m<ipython-input-1-809ffaf4ba67>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[0ma\u001b[0m\u001b[1;33m=\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----> 2\u001b[1;33m \u001b[0mmax\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ma\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
+      "\u001b[1;31mValueError\u001b[0m: max() arg is an empty sequence"
+     ]
+    }
+   ],
+   "source": [
+    "a=[]\n",
+    "max(a)"
+   ]
+  }
+ ],
+ "metadata": {
+  "interpreter": {
+   "hash": "d0c7845e02611e9f7e86f701534f4ecc3f03f09c41a0ab0ebe5642ee661f4878"
+  },
+  "kernelspec": {
+   "display_name": "Python 3.8.5 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.5"
+  },
+  "orig_nbformat": 4
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}