Kaynağa Gözat

Merge branch 'dev' of http://git.fast-fun.cn:92/lmstack/data_analyze_platform into dev

Eric412V 3 yıl önce
ebeveyn
işleme
de80447c81

+ 32 - 11
LIB/MIDDLE/CellStateEstimation/BatSafetyAlarm/V1_0_1/CBMSSafetyAlarm.py

@@ -42,7 +42,7 @@ class SafetyAlarm:
     
     #寻找当前行数据的所有温度值...................................................................................
     def _celltemp_get(self,num):   
-        celltemp = list(self.df_bms.loc[num,self.celltemp_name])
+        celltemp = np.array(self.df_bms.loc[num,self.celltemp_name])
         return celltemp
 
     #获取当前行所有电压数据............................................................................................
@@ -93,7 +93,7 @@ class SafetyAlarm:
 
                         delttime=(time2-time1).total_seconds()
                         celltemp_rate=(max(temp2-temp1)*60)/delttime    #计算最大温升速率
-                        if celltemp_rate>self.param.TrwTempRate and self.param.CellTempLwLmt<min(temp1) and max(temp1)<self.param.CellTempUpLmt:
+                        if celltemp_rate>self.param.TrwTempRate and self.param.CellTempLwLmt<min(temp1) and max(temp1)<self.param.CellTempUpLmt and max(temp2-temp1)>3:
                             celltemprise=1
                         else:
                             pass
@@ -115,19 +115,41 @@ class SafetyAlarm:
                 pass
             
             #电压诊断功能.........................................................................................................................................
-            cellvolt=self._cellvolt_get(i)
-            cellvolt2=np.array(cellvolt)
-            cellvoltmin=min(cellvolt)
-            cellvoltmax=max(cellvolt)
-            cellvoltmin_index=cellvolt.index(cellvoltmin)
-            cellvoltmax_index=cellvolt.index(cellvoltmax)
+            if i<1:
+                cellvolt2=self._cellvolt_get(i)
+                cellvoltmin2=min(cellvolt2)
+                cellvoltmax2=max(cellvolt2)
+                cellvoltmin_index2=list(cellvolt2).index(cellvoltmin2)
+                cellvoltmax_index2=list(cellvolt2).index(cellvoltmax2)
+                if not self.df_bms_ram.empty:
+                    cellvolt1=np.array(self.df_bms_ram.iloc[-1]['cellvolt'])
+                    cellvoltmin1=min(cellvolt1)
+                    cellvoltmax1=max(cellvolt1)
+                    cellvoltmin_index1=list(cellvolt1).index(cellvoltmin1)
+                    cellvoltmax_index1=list(cellvolt1).index(cellvoltmax1)
+                else:
+                    cellvoltmin1=cellvoltmin2
+                    cellvoltmax1=cellvoltmax2
+                    cellvoltmin_index1=cellvoltmin_index2
+                    cellvoltmax_index1=cellvoltmax_index2
+            else:
+                cellvolt2=self._cellvolt_get(i)
+                cellvoltmin2=min(cellvolt2)
+                cellvoltmax2=max(cellvolt2)
+                cellvoltmin_index2=list(cellvolt2).index(cellvoltmin2)
+                cellvoltmax_index2=list(cellvolt2).index(cellvoltmax2)
+                cellvolt1=self._cellvolt_get(i-1)
+                cellvoltmin1=min(cellvolt1)
+                cellvoltmax1=max(cellvolt1)
+                cellvoltmin_index1=list(cellvolt1).index(cellvoltmin1)
+                cellvoltmax_index1=list(cellvolt1).index(cellvoltmax1)
 
             #电压有效性..........................................................................................................................................
-            if (cellvoltmin<2 and cellvoltmax>4.5 and (cellvoltmax_index-cellvoltmin_index)==1) or cellvoltmin<0.01:   #电压断线故障进入
+            if (cellvoltmin2<2 and cellvoltmax2>4.5 and abs(cellvoltmax_index2-cellvoltmin_index2)==1) or cellvoltmin2<0.01 or (cellvoltmin1<2 and cellvoltmax1>4.5 and abs(cellvoltmax_index1-cellvoltmin_index1)==1) or cellvoltmin1<0.01:   #电压断线故障进入
                 cellvoltvalid=0
             else:
                 cellvoltvalid=1
-   
+
             if cellvoltvalid==1:
                 #单体电压跌落诊断...........................................................................................................................
                 if i<1:
@@ -135,7 +157,6 @@ class SafetyAlarm:
                         time1=self.df_bms_ram.iloc[-1]['time']
                         time2=self.bmstime[i]
                         delttime=(time2-time1).total_seconds()
-                        cellvolt1=np.array(self.df_bms_ram.iloc[-1]['cellvolt'])
                         if delttime<310:
                             if self.packcrnt[i]<0.5 and max(cellvolt1-cellvolt2)>self.param.TrwCellVoltFall:
                                 cellvoltfall=1

+ 20 - 20
LIB/MIDDLE/CellStateEstimation/BatSafetyAlarm/main.py

@@ -1,6 +1,5 @@
 import CBMSSafetyAlarm
 import pymysql
-from urllib import parse
 import datetime
 import pandas as pd
 import multiprocessing
@@ -13,7 +12,7 @@ from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import log
 #...................................电池包电芯安全诊断函数......................................................................................................................
 def diag_cal(sn_list, df_bms_ram):
 
-    start=time.time()
+    # 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')
@@ -23,13 +22,13 @@ def diag_cal(sn_list, df_bms_ram):
     host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
     port=3306
     user='qx_algo_readonly'
-    password = parse.quote_plus('qx@123456')
+    password = 'qx@123456'
 
     #读取故障结果库中code==119且end_time='0000-00-00 00:00:00'...............................
     db='safety_platform'
-    mysql = pymysql.connect (host=host, user=user, password=password, port=port, database=db)
+    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'
+    param='start_time,end_time,product_id,code,level,info,advice'
     tablename='all_fault_info'
     sql =  "select %s from %s where code=119 and end_time='0000-00-00 00:00:00'" %(param,tablename)
     cursor.execute(sql)
@@ -61,10 +60,10 @@ def diag_cal(sn_list, df_bms_ram):
         print(df_bms)
 
         #电池诊断................................................................................................................................................................
-        if not df_bms.empty:
-            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:
+        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() 
 
@@ -75,19 +74,20 @@ def diag_cal(sn_list, df_bms_ram):
 
                 #当前热失控故障写入数据库
                 if not df_diag_res.empty:
-                    with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\06BatSafetyAlarm\热失控.txt','a') as file:
+                    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')
                 
-            #当前热失控已超过一天变为历史故障并更改数据库
-            elif (now_time-df_diag_ram_sn.iloc[-1]['start_time']).total_seconds()>24*3600:
-                df_diag_ram_sn.iloc[-1]['end_time']=now_time
+        #当前热失控已超过一天变为历史故障并更改数据库
+        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_res.iloc[-1]))+'\n')
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\06BatSafetyAlarm\热失控报警.txt','a') as file:
+                    file.write(str(tuple(df_diag_ram_sn.iloc[-1]))+'\n')
 
-        #故障处理........................................................................................................................................................
-        end=time.time()
-        print(end-start)
+        # end=time.time()
+        # print(end-start)
 #...................................................主进程...........................................................................................................
 def mainprocess():
     global SNnums
@@ -120,7 +120,7 @@ if __name__ == "__main__":
     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=[[],['PK504B00100004161']]
+    SNnums=[[], ['PK504B10100004447']]
     
     mylog=log.Mylog('log_diag.txt','error')
     mylog.logcfg()
@@ -130,7 +130,7 @@ if __name__ == "__main__":
 
     #定时任务.......................................................................................................................................................................
     scheduler = BlockingScheduler()
-    scheduler.add_job(mainprocess, 'interval', seconds=60, id='diag_job')
+    scheduler.add_job(mainprocess, 'interval', seconds=10, id='diag_job')
 
     try:  
         scheduler.start()

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

@@ -0,0 +1,674 @@
+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,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.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.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=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=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), 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)
+
+    #计算每个电芯的均衡时长..........................................................................................................................
+    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
+        else:
+            standingtime=df_ram_last3.loc[0,'standingtime']
+            standingtime1=df_ram_last3.loc[0,'standingtime1']
+            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._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={}   #获取每个电芯的均衡时间
+                            deltsoc_last, cellsoc_last=self._celldeltsoc_get(i,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*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, cellsoc_now=self._celldeltsoc_get(i,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._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={}   #获取每个电芯的均衡时间
+                            deltsoc_last1, cellsoc_last1=self._celldeltsoc_get(i,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*12:
+                                deltsoc_now1, cellsoc_now1=self._celldeltsoc_get(i,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]
+
+        #返回计算结果
+        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
+        else:
+            standingtime=df_ram_last3.loc[0,'standingtime']
+            standingtime1=df_ram_last3.loc[0,'standingtime1']
+            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._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={}  #获取每个电芯的均衡时间
+                            deltsoc_last, cellsoc_last=self._celldeltsoc_get(i,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*10:
+                    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, cellsoc_now=self._celldeltsoc_get(i, 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._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, cellsoc_last1=self._celldeltsoc_get(i,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(i,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*12:
+                                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]<40:
+                        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/3 and self.packcrnt[i+1]>self.param.Capacity/3):  #如果充电过程中时间间隔>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>10:
+                            chrg_end=i+1
+                            charging=0
+
+                            #计算漏电流值...................................................................
+                            if firsttime2==1:
+                                dict_baltime={}
+                                deltAs_last2=self._cellDeltAs_get(chrg_start,chrg_end,dict_baltime)
+                                time_last2=self.bmstime[chrg_end]
+                                df_ram_last2.loc[0]=[self.sn,time_last2,deltAs_last2]    #更新RAM信息
+                            else:
+                                dict_baltime=self._bal_time(dict_bal2)   #获取每个电芯的均衡时间
+                                deltAs_now2=self._cellDeltAs_get(chrg_start,chrg_end,dict_baltime)  #获取每个电芯的As差
+                                time_now2=self.bmstime[chrg_end]
+                                df_ram_last2.loc[0]=[self.sn,time_now2,deltAs_now2]    #更新RAM信息
+
+                                list_sub2=deltAs_now2-deltAs_last2
+                                list_pud2=-1000/(time_now2-time_last2).total_seconds()
+                                leak_current2=list_sub2*list_pud2
+                                # leak_current=np.array(leak_current)
+                                leak_current2=np.round(leak_current2,3)
+                                leak_current2=list(leak_current2)
+
+                                df_res.loc[len(df_res)]=[time_last2,time_now2,self.sn,3,str(leak_current2),str(dict_baltime)]  #计算结果存入Dataframe
+                                deltAs_last2=deltAs_now2
+                                time_last2=time_now2
+                                dict_bal2={}
+
+                        else:
+                            charging=0
+                            continue
+                    elif 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]
+
+        #返回结果
+        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

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

@@ -0,0 +1,89 @@
+import pandas as pd
+import numpy as np
+import datetime
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam
+
+class SafetyWarning:
+    def __init__(self,sn,celltype,df_short,df_liplated,df_uniform):  #参数初始化
+
+        self.sn=sn
+        self.celltype=celltype
+        self.param=BatParam.BatParam(celltype)
+        self.df_short=df_short
+        self.df_liplated=df_liplated
+        self.df_uniform=df_uniform
+    
+    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=datetime.datetime.now()
+        time_sp='0000-00-00 00:00:00'
+
+        #参数初始化
+        shortfault=0
+        liplatedfault=0
+        uniformfault=0
+
+        for i in range(self.param.CellVoltNums):
+
+            #漏电流故障判断...........................................................................
+            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("]", '')
+                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)==2 and (index_list[1]-index_list[0])==1:
+                    shortfault=1
+                elif len(index_list)>3:
+                    shortfault=1
+                else:
+                    shortfault=0
+            
+            #析锂故障判断...............................................................................
+            if not self.df_liplated.empty:
+                liplated=self.df_liplated['liplated_amount']
+                liplated=liplated.str.replace("[", '')
+                liplated=liplated.str.replace("]", '')
+                self.df_liplated['liplated_amount'+str(i+1)]=liplated.map(lambda x:eval(x.split(',')[i]))
+
+                if max(self.df_liplated['liplated_amount'+str(i+1)])>30:
+                    liplatedfault=1
+                else:
+                    liplatedfault=0
+            else:
+                liplatedfault=0
+
+            #电芯SOC排名判断.............................................................................
+            if not self.df_uniform.empty:
+                if (i+1) in self.df_uniform['cellmin_num'].tolist():
+                    uniformfault=1
+                else:
+                    uniformfault=0
+            else:
+                uniformfault=0
+            
+            #电池热安全预警
+            if shortfault==1 and (liplatedfault==1 or uniformfault==1):
+                faultcode=110
+                faultlv=4
+                faultinfo='电芯{}发生热失控安全预警'.format(i+1)
+                faultadvice='联系用户远离电池,立刻召回电池'
+                df_res.loc[0]=[time, time_sp, self.sn, faultcode, faultlv, faultinfo, faultadvice]
+            else:
+                pass
+            
+        return df_res

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

@@ -0,0 +1,194 @@
+import pandas as pd
+import pymysql
+from LIB.BACKEND import DBManager, Log
+from apscheduler.schedulers.blocking import BlockingScheduler
+import time, datetime
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import DBDownload
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import log
+import CBMSBatInterShort
+import CBMSSafetyWarning
+
+#电池热安全预警核心算法函数......................................................................................................................
+def saftywarning_cal():
+    global SNnums
+    global df_warning_ram
+    global df_warning_ram1
+    global df_warning_ram2
+    global df_warning_ram3
+    global df_lfp_ram
+    global now_time
+
+    now_time=datetime.datetime.now()
+    start_time=now_time-datetime.timedelta(hours=12)
+    start_time1=now_time-datetime.timedelta(days=7)
+    start_time2=now_time-datetime.timedelta(days=90)
+    start_time=start_time.strftime('%Y-%m-%d %H:%M:%S')
+    start_time1=start_time1.strftime('%Y-%m-%d %H:%M:%S')
+    end_time=now_time.strftime('%Y-%m-%d %H:%M:%S')
+
+    #数据库配置
+    host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port=3306
+    user='qx_read'
+    password='Qx@123456'
+
+    #读取故障结果库中code==110且end_time='0000-00-00 00:00:00'...............................
+    db='safety_platform'
+    mysql = pymysql.connect (host=host, user=user, password=password, port=port, database=db)
+    cursor = mysql.cursor()
+    param='start_time,end_time,product_id,code,level,info,advice'
+    tablename='all_fault_info'
+    sql =  "select %s from %s where code=110 and end_time='0000-00-00 00:00:00'" %(param,tablename)
+    cursor.execute(sql)
+    res = cursor.fetchall()
+    df_fault_ram= pd.DataFrame(res,columns=param.split(','))
+    cursor.close()
+    mysql.close()
+
+    for sn in SNnums:
+        if 'PK500' in sn:
+            celltype=1 #6040三元电芯
+        elif 'PK502' in sn:
+            celltype=2 #4840三元电芯
+        elif 'K504B' in sn:
+            celltype=99    #60ah林磷酸铁锂电芯
+        elif 'MGMLXN750' in sn:
+            celltype=3 #力信50ah三元电芯
+        elif 'MGMCLN750' or 'UD' in sn: 
+            celltype=4 #CATL 50ah三元电芯
+        else:
+            print('SN:{},未找到对应电池类型!!!'.format(sn))
+            continue
+            # sys.exit()
+
+        #读取原始数据库数据........................................................................................................................................................
+        dbManager = DBManager.DBManager()
+        df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms'])
+        df_bms = df_data['bms']
+        # tim=start_time.replace(':','_')
+        # df_bms.to_csv(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\98Download\\'+sn+'_BMS_'+tim+'.csv',encoding='GB18030')
+
+        #读取结果数据库数据........................................................................................................................................................
+        db='qx_cas'
+        mode=1
+        tablename='cellstateestimation_soh'
+        DBRead=DBDownload.DBDownload(host, port, db, user, password,mode)
+        with DBRead as DBRead:
+            df_soh=DBRead.getdata('time_st,sn,soh,cellsoh', tablename=tablename, sn=sn, timename='time_sp', st=start_time, sp=end_time)
+
+        if not df_bms.empty:
+            #电池内短路计算................................................................................................................................................................
+            df_warning_ram_sn=df_warning_ram[df_warning_ram['sn']==sn]
+            df_warning_ram_sn1=df_warning_ram1[df_warning_ram1['sn']==sn]
+            df_warning_ram_sn2=df_warning_ram2[df_warning_ram2['sn']==sn]
+            df_warning_ram_sn3=df_warning_ram3[df_warning_ram3['sn']==sn]
+            df_warning_ram_sn.reset_index(inplace=True,drop=True)     #重置索引
+            df_warning_ram_sn1.reset_index(inplace=True,drop=True)     #重置索引
+            df_warning_ram_sn2.reset_index(inplace=True,drop=True)     #重置索引
+            df_warning_ram_sn3.reset_index(inplace=True,drop=True)     #重置索引
+            if celltype>50:
+                df_lfp_ram=df_lfp_ram.reindex(columns=df_bms.columns.tolist()+['sn'])
+                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()
+
+            BatShort=CBMSBatInterShort.BatInterShort(sn,celltype,df_bms,df_soh,df_warning_ram_sn,df_warning_ram_sn1,df_warning_ram_sn2,df_warning_ram_sn3,df_lfp_ram_sn)
+            df_short_res, df_ram_res, df_ram_res1, df_ram_res2, df_ram_res3, df_ram_res4=BatShort.intershort() 
+
+            if not df_short_res.empty:
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\07BatSafetyWarning\内短路.txt','a') as file:
+                    file.write(str(df_short_res)+'\n')
+
+            #内短路ram处理.........................................................
+            df_warning_ram=df_warning_ram.drop(df_warning_ram[df_warning_ram.sn==sn].index)
+            df_warning_ram1=df_warning_ram1.drop(df_warning_ram1[df_warning_ram1.sn==sn].index)
+            df_warning_ram2=df_warning_ram2.drop(df_warning_ram2[df_warning_ram2.sn==sn].index)
+            df_warning_ram3=df_warning_ram3.drop(df_warning_ram3[df_warning_ram3.sn==sn].index)
+
+            df_warning_ram=pd.concat([df_warning_ram,df_ram_res],ignore_index=True)
+            df_warning_ram1=pd.concat([df_warning_ram1,df_ram_res1],ignore_index=True)
+            df_warning_ram2=pd.concat([df_warning_ram2,df_ram_res2],ignore_index=True)
+            df_warning_ram3=pd.concat([df_warning_ram3,df_ram_res3],ignore_index=True)
+            
+            if celltype>50:
+                df_lfp_ram=df_lfp_ram.drop(df_lfp_ram[df_lfp_ram.sn==sn].index)
+                df_lfp_ram=pd.concat([df_lfp_ram,df_ram_res4],ignore_index=True)
+            
+
+        #电池热安全预警..............................................................................................................................................................
+        #读取内短路、析锂和一致性结果数据库数据
+        db='qx_cas'
+        mode=2
+        tablename1='cellstateestimation_intershort'
+        tablename2='mechanism_liplated'   #析锂表单
+        tablename3='cellstateestimation_uniform_socvoltdiff'
+        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,cellmin_num', tablename=tablename3, sn=sn, timename='time', st=start_time1, sp=end_time)
+        
+        #获取sn的故障RAM
+        df_fault_ram_sn=df_fault_ram[df_fault_ram['product_id']==sn]
+        
+        #热安全预警
+        if df_fault_ram_sn.empty:
+            BatWarning=CBMSSafetyWarning.SafetyWarning(sn,celltype,df_short,df_liplated,df_uniform)
+            df_warning_res=BatWarning.diag()
+            #当前热失控故障写入数据库
+            if not df_warning_res.empty:
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\07BatSafetyWarning\热失控预警.txt','a') as file:
+                    file.write(str(tuple(df_warning_res.iloc[-1]))+'\n')
+        
+        else:
+            fault_time=datetime.datetime.strptime(df_fault_ram_sn.iloc[-1]['start_time'], '%Y-%m-%d %H:%M:%S')
+            if (now_time-fault_time).total_seconds()>24*3600:   #df_warning_end历史故障筛选并更改数据库故障结束时间
+                df_fault_ram_sn['end_time']=end_time
+                df_fault_ram_sn['Batpos']=1
+                with open(r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\07BatSafetyWarning\热失控预警.txt','a') as file:
+                    file.write(str(tuple(df_warning_res.iloc[-1]))+'\n')
+
+
+#...............................................主函数起定时作用.......................................................................................................................
+if __name__ == "__main__":
+    
+    excelpath=r'D:\Platform\platform_python\data_analyze_platform\USER\spf\01qixiang\sn-20210903.xlsx'
+    SNdata_6060 = pd.read_excel(excelpath, sheet_name='科易6060')
+    SNdata_6040 = pd.read_excel(excelpath, sheet_name='科易6040')
+    SNdata_4840 = pd.read_excel(excelpath, sheet_name='科易4840')
+    SNdata_L7255 = pd.read_excel(excelpath, sheet_name='格林美-力信7255')
+    SNdata_C7255 = pd.read_excel(excelpath, sheet_name='格林美-CATL7255')
+    SNdata_U7255 = pd.read_excel(excelpath, sheet_name='优旦7255')
+    SNnums_6060=SNdata_6060['SN号'].tolist()
+    SNnums_6040=SNdata_6040['SN号'].tolist()
+    SNnums_4840=SNdata_4840['SN号'].tolist()
+    SNnums_L7255=SNdata_L7255['SN号'].tolist()
+    SNnums_C7255=SNdata_C7255['SN号'].tolist()
+    SNnums_U7255=SNdata_U7255['SN号'].tolist()
+    SNnums=SNnums_L7255 + SNnums_C7255 + SNnums_U7255, SNnums_6040 + SNnums_4840 + SNnums_6060
+    SNnums=['MGMCLN750N215N155']
+    
+    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'])
+    df_lfp_ram=pd.DataFrame()
+
+    now_time='2021-10-15 00:00:00'
+    now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+
+    #定时任务.......................................................................................................................................................................
+    scheduler = BlockingScheduler()
+    scheduler.add_job(saftywarning_cal, 'interval', hours=12)
+
+    try:  
+        scheduler.start()
+    except Exception as e:
+        scheduler.shutdown()
+        print(repr(e))
+        mylog.logopt(e)

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

@@ -23,13 +23,13 @@ class BatParam:
         self.TrwCellVoltLow=1.5
         self.TrwPackVoltFall=1.5
 
-        self.SocJump=3
+        self.SocJump=10
         self.SocClamp=0.1
         self.SocLow=3
         self.SocDiff=20
 
-        self.SohLow=65
-        self.SohDiff=20
+        self.SohLow=70
+        self.SohDiff=15
 
         self.OcvWeight_Temp=[-30,-20,-10,0,10,20,30,40,50]
         self.OcvWeight_StandingTime=[0,500,600,1200,1800,3600,7200,10800]
@@ -72,9 +72,9 @@ class BatParam:
             self.PackChgOc=-40
             self.PackDisOc=200
 
-            self.LeakCurrentLv1=10
-            self.LeakCurrentLv2=20
-            self.LeakCurrentLv3=50
+            self.LeakCurrentLv1=-10
+            self.LeakCurrentLv2=-20
+            self.LeakCurrentLv3=-50
 
         elif celltype==2: #4840
             self.Capacity = 41
@@ -104,9 +104,9 @@ class BatParam:
             self.PackChgOc=-40
             self.PackDisOc=200
 
-            self.LeakCurrentLv1=10
-            self.LeakCurrentLv2=20
-            self.LeakCurrentLv3=50
+            self.LeakCurrentLv1=-10
+            self.LeakCurrentLv2=-20
+            self.LeakCurrentLv3=-50
 
         elif celltype==3:   #力信50ah三元电芯
             self.Capacity = 51
@@ -136,9 +136,9 @@ class BatParam:
             self.PackChgOc=-40
             self.PackDisOc=200
 
-            self.LeakCurrentLv1=10
-            self.LeakCurrentLv2=20
-            self.LeakCurrentLv3=50
+            self.LeakCurrentLv1=-10
+            self.LeakCurrentLv2=-20
+            self.LeakCurrentLv3=-50
 
         elif celltype==4:   #CATL 50ah三元电芯
             self.Capacity = 50
@@ -169,9 +169,9 @@ class BatParam:
             self.PackChgOc=-40
             self.PackDisOc=200
 
-            self.LeakCurrentLv1=10
-            self.LeakCurrentLv2=20
-            self.LeakCurrentLv3=50
+            self.LeakCurrentLv1=-10
+            self.LeakCurrentLv2=-20
+            self.LeakCurrentLv3=-50
 
         elif celltype==99:   #60ah磷酸铁锂电芯
             self.Capacity = 54
@@ -212,9 +212,9 @@ class BatParam:
             self.PackChgOc=-40
             self.PackDisOc=200
 
-            self.LeakCurrentLv1=20
-            self.LeakCurrentLv2=50
-            self.LeakCurrentLv3=100
+            self.LeakCurrentLv1=-20
+            self.LeakCurrentLv2=-50
+            self.LeakCurrentLv3=-100
         else:
             print('未找到对应电池编号!!!')
             # sys.exit()

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

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

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


+ 221 - 0
LIB/MIDDLE/SaftyCenter/Liplated/BatParam.py

@@ -0,0 +1,221 @@
+
+#定义电池参数
+class BatParam:
+    def __init__(self,celltype):
+
+        #公用参数................................................................................................................................................
+        self.CellTempUpLmt=119
+        self.CellTempLwLmt=-39
+        self.CellTempHighLv1=45
+        self.CellTempHighLv2=50
+        self.CellTempLowLv1=-20
+        self.CellTempLowLv2=-25
+        self.CellTempDiffLv1=10
+        self.CellTempDiffLv2=15
+        self.CellTempRate=5
+
+       #热失控参数
+        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.SocJump=3
+        self.SocClamp=0.1
+        self.SocLow=3
+        self.SocDiff=20
+
+        self.SohLow=65
+        self.SohDiff=20
+
+        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]]
+
+
+        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.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=-40
+            self.PackDisOc=200
+
+            self.LeakCurrentLv1=10
+            self.LeakCurrentLv2=20
+            self.LeakCurrentLv3=50
+
+        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.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=-40
+            self.PackDisOc=200
+
+            self.LeakCurrentLv1=10
+            self.LeakCurrentLv2=20
+            self.LeakCurrentLv3=50
+
+        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.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=-40
+            self.PackDisOc=200
+
+            self.LeakCurrentLv1=10
+            self.LeakCurrentLv2=20
+            self.LeakCurrentLv3=50
+
+        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]
+            self.LookTab_OCV = [3.152, 	3.397, 	3.438, 	3.481, 	3.523, 	3.560, 	3.586, 	3.604, 	3.620, 	3.638, 	3.661, 	3.693, 	3.748, 	3.803, 	3.853, 	3.903, 	3.953, 	4.006, 	4.063, 	4.121, 	4.183, 4.253]
+
+            self.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=-40
+            self.PackDisOc=200
+
+            self.LeakCurrentLv1=10
+            self.LeakCurrentLv2=20
+            self.LeakCurrentLv3=50
+
+        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=4
+            self.OtherTempNums=5
+            self.FullChrgSoc=98
+            self.PeakSoc=59
+            self.PeakVoltLowLmt=3.35
+            self.PeakVoltUpLmt=3.4
+            self.PeakCellVolt=[3.362,3.363,3.365,3.366,3.367]
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0.00, 	2.40, 	6.38, 	10.37, 	14.35, 	18.33, 	22.32, 	26.30, 	30.28, 	35.26, 	40.24, 	45.22, 	50.20, 	54.19, 	58.17, 	60.16, 	65.14, 	70.12, 	75.10, 	80.08, 	84.06, 	88.05, 	92.03, 	96.02, 	100.00]
+            self.LookTab_OCV = [2.7151,	3.0298,	3.1935,	3.2009,	3.2167,	3.2393,	3.2561,	3.2703,	3.2843,	3.2871,	3.2874,	3.2868,	3.2896,	3.2917,	3.2967,	3.3128,	3.3283,	3.3286,	3.3287,	3.3288,	3.3289,	3.3296,	3.3302,	3.3314,	3.3429]
+
+            self.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=-40
+            self.PackDisOc=200
+
+            self.LeakCurrentLv1=20
+            self.LeakCurrentLv2=50
+            self.LeakCurrentLv3=100
+        else:
+            print('未找到对应电池编号!!!')
+            # sys.exit()
+

+ 164 - 0
LIB/MIDDLE/SaftyCenter/Liplated/Li_plated.py

@@ -0,0 +1,164 @@
+import pandas as pd
+import numpy as np
+import datetime
+import matplotlib as plt
+from scipy.signal import savgol_filter
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam
+
+class Liplated_test:
+    def __init__(self,sn,celltype,df_bms):  #参数初始化
+
+        self.sn=sn
+        self.celltype=celltype
+        self.param=BatParam.BatParam(celltype)
+        self.df_bms=pd.DataFrame(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)]
+        self.bmssta = df_bms['充电状态']
+    #定义加权滤波函数..................................................................................................
+    def moving_average(interval, windowsize):
+        window = np.ones(int(windowsize)) / float(windowsize)
+        re = np.convolve(interval, window, 'same')
+        return re
+#.............................................析锂检测............................................................................
+    def liplated_detect(self):
+        #----------------------------------------筛选充电后静置数据------------------------------------------------------------
+        chrgr_rest_data_temp = self.df_bms.loc[((self.df_bms['充电状态'] == 0) & (self.df_bms['SOC[%]'] > 98) & (self.df_bms['总电流[A]'] == 0)) | 
+                                               ((self.df_bms['充电状态'] == 2) & (self.df_bms['SOC[%]'] > 98) & (self.df_bms['总电流[A]'] == 0))]#接近慢充后静置数据
+        df_lipltd_result = pd.DataFrame(columns=['sn','time','liplated','liplated_amount'])
+        if not chrgr_rest_data_temp.empty:
+            chrgr_rest_data = chrgr_rest_data_temp.reset_index(drop=True)
+            temp_rest_time = chrgr_rest_data['时间戳']
+            rest_time = pd.to_datetime(temp_rest_time)
+            delta_time = (np.diff(rest_time)/pd.Timedelta(1, 'min'))#计算时间差的分钟数
+            pos = np.where(delta_time > 30)#静置数据分段,大于30min时,认为是两个静置过程
+            splice_num = []
+            if len(pos[0]) >= 1:
+                pos_ful_tem = np.insert(pos, 0, 0)
+                pos_len = len(pos_ful_tem)
+                data_len = len(rest_time)
+                pos_ful = np.insert(pos_ful_tem, pos_len, data_len-1)
+                for item in range(0,len(pos_ful)-1):
+                    splice_num.extend(item*np.ones(pos_ful[item +1]-pos_ful[item]))
+                splice_num = np.insert(splice_num, 0, 0)
+            else:
+                splice_num = np.zeros(len(temp_rest_time))
+                pos_ful = np.array([0])
+            chrgr_rest_data['chrgr_rest'] = splice_num
+            #---------------------------判断数据点数大于30的数据段,对电压微分并画图--------------------------------------------
+            cellvolt_list = self.cellvolt_name
+            chrgr_rest_check_data = chrgr_rest_data.drop(['GSM信号','故障等级','故障代码','绝缘电阻','总电流[A]','总电压[V]','充电状态','单体压差','SOC[%]'],axis=1,inplace=False)
+            chrgr_rest_check_data.fillna(value=0)
+            df_rest_volt_diffdt = pd.DataFrame()
+            df_rest_volt_smooth = pd.DataFrame()
+            k = 0
+            for j in range(0,len(pos_ful)-1):#len(pos_ful)-1#有几段充电后静置数据
+                df_test_rest_data = chrgr_rest_check_data.loc[chrgr_rest_check_data['chrgr_rest'] == j]
+                df_rest_volt_smooth = pd.DataFrame()
+                df_test_rest_time = pd.to_datetime(df_test_rest_data['时间戳'],format='%Y-%m-%d %H:%M:%S')
+                df_test_rest_time = df_test_rest_time.reset_index(drop=True)
+                df_data_length = len(df_test_rest_time)
+                if (df_data_length > 30) & ((df_test_rest_time[df_data_length - 1] - df_test_rest_time[0])/pd.Timedelta(1, 'min') > 40):#静置时间大于40min
+                    df_test_rest_time_dif_temp = np.diff(df_test_rest_time)/pd.Timedelta(1, 'min')
+                    num_list = []
+                    data_jump_pos = np.where(df_test_rest_time_dif_temp > 3)
+                    if len(data_jump_pos[0]) > 0:
+                        if data_jump_pos[0][0] > 100:
+                            for i in range(0,data_jump_pos[0][0],15):##采样密集时每隔10行取数据
+                                num_list.append(i)
+                            df_rest_data_temp = df_test_rest_data.iloc[num_list]
+                            df_test_rest_data_choose = pd.DataFrame(df_rest_data_temp)
+                            df_test_rest_data_choose_else = df_test_rest_data.iloc[data_jump_pos[0][0]+1:len(df_test_rest_data)-1]
+                            df_rest_data_recon_temp = df_test_rest_data_choose.append(df_test_rest_data_choose_else)
+                            df_rest_data_recon =df_rest_data_recon_temp.reset_index(drop=True)
+                        else:
+                            df_rest_data_recon = df_test_rest_data
+                    else:
+                        df_rest_data_recon = df_test_rest_data
+                    df_rest_time = pd.to_datetime(df_rest_data_recon['时间戳'],format='%Y-%m-%d %H:%M:%S')
+                    df_rest_time = df_rest_time.reset_index(drop=True)
+                    df_rest_time_dif_temp = np.diff(df_rest_time)/pd.Timedelta(1, 'min')
+                    df_rest_volt = df_rest_data_recon[cellvolt_list]
+                    for item in cellvolt_list:
+                        window_temp = int(len(df_rest_volt[item])/3)
+                        if window_temp%2:#滤波函数的窗口长度需为奇数
+                            window = window_temp
+                        else:
+                            window = window_temp - 1
+                        step = min(int(window/3),5)
+                        df_volt_smooth = savgol_filter(df_rest_volt[item],window,step)
+                        df_rest_volt_smooth[item] = df_volt_smooth
+                    df_test_rest_volt_diff_temp = np.diff(df_rest_volt_smooth,axis=0)
+                    df_test_rest_time_dif = pd.DataFrame(df_rest_time_dif_temp)
+                    df_test_rest_volt_diff = pd.DataFrame(df_test_rest_volt_diff_temp)
+                    df_test_rest_volt_diffdt_temp = np.divide(df_test_rest_volt_diff,df_test_rest_time_dif)
+                    df_test_rest_volt_diffdt = pd.DataFrame(df_test_rest_volt_diffdt_temp)
+                    df_test_rest_volt_diffdt = df_test_rest_volt_diffdt.append(df_test_rest_volt_diffdt.iloc[len(df_test_rest_volt_diffdt)-1])
+                    df_test_rest_volt_diffdt.columns = cellvolt_list
+                    if len(df_test_rest_volt_diffdt) > 25:
+                        for item in cellvolt_list:
+                            df_volt_diffdt_smooth = savgol_filter(df_test_rest_volt_diffdt[item],13,3)
+                            df_test_rest_volt_diffdt[item] = df_volt_diffdt_smooth
+                        df_test_rest_volt_diffdt['chrgr_rest'] = k
+                        df_test_rest_volt_diffdt['时间戳'] = list(df_rest_time)
+                        k = k+1
+                        df_rest_volt_diffdt = df_rest_volt_diffdt.append(df_test_rest_volt_diffdt)
+                        df_rest_volt_diffdt.reset_index()
+            #--------------------------------------------------------确认是否析锂----------------------------------------------------------------------------
+            # df_lipltd_data = pd.DataFrame(columns=['sn','date','liplated'])
+            for item in range(0,k):
+                lipltd_confirm = []
+                lipltd_amount = []
+                df_check_liplated_temp = df_rest_volt_diffdt.loc[df_rest_volt_diffdt['chrgr_rest'] == item].reset_index(drop = True)
+                df_lipltd_volt_temp = df_check_liplated_temp[cellvolt_list]
+                df_lipltd_volt_len = len(df_lipltd_volt_temp)
+                df_data_temp_add = df_lipltd_volt_temp.iloc[df_lipltd_volt_len-4:df_lipltd_volt_len-1]
+                df_lipltd_volt_temp_add = df_lipltd_volt_temp.append(df_data_temp_add)
+                df_lipltd_volt_temp_dif = np.diff(df_lipltd_volt_temp_add,axis=0)#电压一次微分,计算dv/dt
+                df_lipltd_volt_temp_dif = pd.DataFrame(df_lipltd_volt_temp_dif)
+                df_lipltd_volt_temp_dif.columns = cellvolt_list
+                df_lipltd_volt_temp_difdif = np.diff(df_lipltd_volt_temp_dif,axis=0)#电压二次微分,判断升降
+                df_lipltd_volt_temp_difdif = pd.DataFrame(df_lipltd_volt_temp_difdif)
+                df_lipltd_volt_temp_difdif.columns = cellvolt_list
+                df_lipltd_volt_temp_difdif_temp = df_lipltd_volt_temp_difdif
+                df_lipltd_volt_temp_difdif_temp[df_lipltd_volt_temp_difdif_temp >= 0] = 1
+                df_lipltd_volt_temp_difdif_temp[df_lipltd_volt_temp_difdif_temp < 0] = -1
+                df_lipltd_volt_temp_difdifdif = np.diff(df_lipltd_volt_temp_difdif_temp,axis=0)#三次微分,利用-2,2判断波分和波谷
+                df_lipltd_volt_difdifdif = pd.DataFrame(df_lipltd_volt_temp_difdifdif)
+                df_lipltd_volt_difdifdif.columns = cellvolt_list
+                df_lipltd_volt_difdifdif['chrgr_rest'] = k
+                df_lipltd_volt_difdifdif['时间戳'] = list(df_check_liplated_temp['时间戳'])
+                df_lipltd_volt_difdifdif = df_lipltd_volt_difdifdif.reset_index(drop = True)
+                df_lipltd_data_temp = df_lipltd_volt_difdifdif.loc[df_lipltd_volt_difdifdif['时间戳'] < (df_check_liplated_temp['时间戳'][0] + datetime.timedelta(minutes=90))]
+                for cell_name in cellvolt_list:#对每个电芯判断
+                    df_check_plated_data = df_lipltd_data_temp[cell_name]
+                    peak_pos = np.where(df_check_plated_data == -2)
+                    bot_pos = np.where(df_check_plated_data == 2)
+                    if len(peak_pos[0]) & len(bot_pos[0]):
+                        if (peak_pos[0][0] > bot_pos[0][0]) & (df_lipltd_volt_temp_dif[cell_name][peak_pos[0][0] + 1] < 0):
+                            lipltd_confirm.append(1)#1为析锂,0为非析锂
+                            lipltd_amount.append((df_check_liplated_temp['时间戳'][bot_pos[0][0] + 2] - df_check_liplated_temp['时间戳'][0])/pd.Timedelta(1, 'min'))
+                        else:
+                            lipltd_confirm.append(0)
+                            lipltd_amount.append(0)
+                    else:
+                        lipltd_confirm.append(0)
+                        lipltd_amount.append(0)
+                if any(lipltd_confirm) & (max(lipltd_amount) > 5):
+                    df_lipltd_confir_temp = pd.DataFrame({"sn":[self.sn], "time":[df_check_liplated_temp['时间戳'][0]], "liplated":[str(lipltd_confirm)], "liplated_amount":[str(lipltd_amount)]})
+                    df_lipltd_result = df_lipltd_result.append(df_lipltd_confir_temp)
+                    df_lipltd_result = df_lipltd_result.reset_index(drop = True)
+                    df_lipltd_result.sort_values(by = ['time'], axis = 0, ascending=True,inplace=True)#对故障信息按照时间进行排序
+        # df_lipltd_data.to_csv(r'D:\Work\Code_write\data_analyze_platform\USER\lzx\01算法开发\02析锂检测\liplated\算法开发_检测\滤波后图片\析锂.csv',index=False,encoding='GB18030')
+        #返回诊断结果...........................................................................................................
+        if not df_lipltd_result.empty:
+            return df_lipltd_result
+        else:
+            return pd.DataFrame()
+
+

+ 94 - 0
LIB/MIDDLE/SaftyCenter/Liplated/main.py

@@ -0,0 +1,94 @@
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+import time, datetime
+from apscheduler.schedulers.blocking import BlockingScheduler
+import log
+from pandas.core.frame import DataFrame
+import Li_plated
+
+#...................................电池包电芯安全诊断函数......................................................................................................................
+def cell_platd_test():
+    global SNnums
+    global df_Diag_lipltd
+    start=time.time()
+    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=3)
+    end_time=str(now_time)
+    start_time=str(start_time)
+    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()
+        #读取原始数据库数据........................................................................................................................................................
+        start_time = '2021-11-15 12:00:00'
+        end_time = '2021-11-18 12:00:00'
+        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_Diag_lipltd_add = pd.DataFrame(columns = ['sn','time','liplated', 'liplated_amount'])
+
+        #析锂诊断................................................................................................................................................................
+        if not df_bms.empty:
+            Diag_lipltd_temp = Li_plated.Liplated_test(sn,celltype,df_bms)#析锂检测
+            df_Diag_lipltd_add = Diag_lipltd_temp.liplated_detect()        
+        if not df_Diag_lipltd_add.empty:
+            df_Diag_lipltd_temp = df_Diag_lipltd.append(df_Diag_lipltd_add)
+            df_Diag_lipltd = df_Diag_lipltd_temp.drop_duplicates(subset = ['sn','time'], keep = 'first', inplace = False)
+            df_Diag_lipltd.sort_values(by = ['sn'], axis = 0, ascending=True,inplace=True)#对故障信息按照时间进行排序
+            df_Diag_lipltd.to_csv(r'D:\Work\Code_write\data_analyze_platform\USER\lzx\01算法开发\02析锂检测\01下载数据\格林美-力信7255\SNnums_6040_liplated_sn.csv',index=False,encoding='GB18030')
+        end=time.time()
+        print(end-start)
+
+#...............................................主函数.......................................................................................................................
+if __name__ == "__main__":
+    global SNnums
+    global df_Diag_lipltd
+    
+    excelpath=r'D:\Work\Code_write\data_analyze_platform\USER\lzx\01算法开发\04故障诊断\01Screen_Problem\sn-20210903.xlsx'
+    SNdata_6060 = pd.read_excel(excelpath, sheet_name='科易6060')
+    SNdata_6040 = pd.read_excel(excelpath, sheet_name='科易6040')
+    SNdata_4840 = pd.read_excel(excelpath, sheet_name='科易4840')
+    SNdata_L7255 = pd.read_excel(excelpath, sheet_name='格林美-力信7255')
+    SNdata_C7255 = pd.read_excel(excelpath, sheet_name='格林美-CATL7255')
+    SNdata_U7255 = pd.read_excel(excelpath, sheet_name='优旦7255')
+    SNnums_6060=SNdata_6060['SN号'].tolist()
+    SNnums_6040=SNdata_6040['SN号'].tolist()
+    SNnums_4840=SNdata_4840['SN号'].tolist()
+    SNnums_L7255=SNdata_L7255['SN号'].tolist()
+    SNnums_C7255=SNdata_C7255['SN号'].tolist()
+    SNnums_U7255=SNdata_U7255['SN号'].tolist()
+    #SNnums=SNnums_L7255 + SNnums_C7255 + SNnums_6040 + SNnums_4840 + SNnums_U7255+ SNnums_6060
+    # SNnums=['MGMCLN750N215I005','PK504B10100004341','PK504B00100004172','MGMLXN750N2189014']
+    SNnums = SNnums_6040 #SNnums_C7255 #SNnums_6040['MGMCLN750N215N049'] 
+    # SNnums = pd.read_csv(r'D:\Work\Code_write\data_analyze_platform\USER\lzx\01算法开发\02析锂检测\liplated\疑似析锂电池sn.csv',encoding='GB18030')
+    
+    mylog=log.Mylog('log_diag.txt','error')
+    mylog.logcfg()
+    #............................模块运行前,先读取数据库中所有结束时间为0的数据,需要从数据库中读取................
+    df_Diag_lipltd=pd.read_csv(r'D:\Work\Code_write\data_analyze_platform\USER\lzx\01算法开发\02析锂检测\01下载数据\格林美-力信7255\析锂.csv',encoding='GB18030')
+
+    print('----------------输入--------')
+    print('-------计算中-----------')
+    #定时任务.......................................................................................................................................................................
+    scheduler = BlockingScheduler()
+    scheduler.add_job(cell_platd_test, 'interval', seconds=10, id='diag_job')
+
+    try:  
+        scheduler.start()
+    except Exception as e:
+        scheduler.shutdown()
+        print(repr(e))
+        mylog.logopt(e)

BIN
LIB/MIDDLE/SaftyCenter/Liplated/析锂.xlsx