Parcourir la source

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

su-lmstack il y a 3 ans
Parent
commit
7de390e60f

+ 15 - 9
LIB/BACKEND/DBManager.py

@@ -84,19 +84,23 @@ class DBManager():
         i = 0
         while 1:
             try:
-                r = requests.get(url, stream=True, timeout=100,  headers={'Connection':'close'})
+                r = requests.get(url, stream=True, timeout=(0.1, 1000),  headers={'Connection':'keep-alive', 'Accept':'*/*', 'Accept-Encoding':'gzip,deflate,br'})
                 break
             except requests.exceptions.RequestException as e:
                 if (i == 0):
                     print()
                 print('\r' + 'Server Error, retry {}......'.format(str(i)), end=" ")
-                time.sleep(5)
+                time.sleep(0.1)
                 i+=1
         # print(r.content)
-        # pdb.set_trace()    
+        # pdb.set_trace()
+        data = [] 
         for line in r.iter_lines():
+
             if line:
-                yield json.loads(line)
+                data.append(json.loads(line))
+                # yield json.loads(line)
+        return data
 
     @staticmethod
     def _convert_to_dataframe_bms(data, mode=0):
@@ -197,6 +201,7 @@ class DBManager():
     
     @staticmethod
     def _get_data(urls,type_name,mode=0):
+        data = DBManager._download_json_data(urls)
         if type_name == 'bms':
             if mode == 0:
                 name_const = ['时间戳','GSM信号','故障等级','故障代码','总电流[A]','总电压[V]', '外电压', '总输出状态', '上锁状态', '充电状态','加热状态',
@@ -208,7 +213,8 @@ class DBManager():
             CellTNum = 0
             OtherTNumm = 0
             st = time.time()
-            for line in DBManager._download_json_data(urls):
+            
+            for line in data:
                 et = time.time()
                 try:
                     if i==0:
@@ -250,7 +256,7 @@ class DBManager():
             elif mode == 1:
                 df_all = pd.DataFrame(columns=['时间戳','定位类型', '纬度','经度','速度[km/h]','有效位'])
             
-            for line in DBManager._download_json_data(urls):
+            for line in data:
                 df_add = DBManager._convert_to_dataframe_gps(line, mode)
                 df_all = df_all.append(df_add,ignore_index=True)
             if not df_all.empty:
@@ -261,7 +267,7 @@ class DBManager():
                 df_all = pd.DataFrame(columns=['时间戳','加热目标温度', '加热超时','租赁开始时间','租赁天数','bms上传周期','gps上传周期'])
             elif mode == 1:
                 df_all = pd.DataFrame()
-            for line in DBManager._download_json_data(urls):
+            for line in data:
                 df_add = DBManager._convert_to_dataframe_system(line, mode)
                 df_all = df_all.append(df_add,ignore_index=True)
             if not df_all.empty:
@@ -274,13 +280,13 @@ class DBManager():
                                                '累计高温充电电量', '累计高温充电能量'])
             elif mode == 1:
                 df_all = pd.DataFrame(columns=['时间戳','累计充电电量','累计充电能量','累计放电电量','累计放电能量', '累积里程'])
-            for line in DBManager._download_json_data(urls):
+            for line in data:
                 df_add = DBManager._convert_to_dataframe_accum(line, mode)
                 df_all = df_all.append(df_add,ignore_index=True)
             if not df_all.empty:
                 df_all.loc[:,'时间戳'] = df_all.loc[:,'时间戳'].apply(lambda x:time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(int(x)/1000)))
             return df_all
-    def get_data(self, url='http://172.16.126.13/store/load?dataType={}&limit=0&sn={}', sn='', start_time='', end_time='', 
+    def get_data(self, url='http://172.16.121.236/store/load?dataType={}&limit=0&sn={}', sn='', start_time='', end_time='', 
                  data_groups=['bms', 'gps']):
         '''
         获取指定 sn 和起止日期的bms和gps数据.

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

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

+ 4 - 0
LIB/MIDDLE/SaftyCenter/CellValueDiag/V1_0_0/SC故障表单.csv

@@ -0,0 +1,4 @@
+start_time,end_time,product_id,code,level,info,advice
+2021-10-17 22:56:07,0,MGMLXN750N2189014,50,3,"[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]号电压小于2.00V,采样无效",返厂维修
+2021-10-17 22:56:07,0,MGMLXN750N2189014,2,3,"[1, 2, 3, 4]号温度小于-20℃,采样无效",返厂维修
+2021-10-17 22:56:07,0,MGMLXN750N2189014,55,3,"[1, 2, 3, 4]号传感器温度小于-20℃,采样无效",返厂维修

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

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

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

@@ -0,0 +1,121 @@
+class BatteryInfo():
+    def __init__(self,celltype):
+        self.CellMaxUSBTemp=55
+        self.AllowChgMinTemp=0
+        self.AllowDsChgTemp=-5
+        self.AvgVolGap=1
+        self.AvgCellTempGap=10
+        self.AvgOtherTempGap=30
+        self.PackOTlmt=65
+        self.PackUTlmt=-20
+        self.OtherOTlmt=91
+        self.OtherUTlmt=-20        
+        self.FaultCount=100
+
+
+
+        if celltype==1: #6040
+            self.Capacity = 41
+            self.PackFullChrgVolt=69.99
+            self.CellFullChrgVolt=4.2
+            self.CellVoltNums=17
+            self.CellTempNums=4
+            self.OtherTempNums=5
+            self.FullChrgSoc=98
+            self.PeakSoc=57
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0,	    3.5348,	8.3581,	13.181,	18.004,	22.827,	27.651,	32.474,	37.297,	42.120,	46.944,	51.767,	56.590,	61.413,	66.237,	71.060,	75.883,	80.707,	85.530,	90.353,	95.176,	100,   105]
+            self.LookTab_OCV = [3.3159,	3.4384,	3.4774,	3.5156,	3.5478,	3.5748,	3.6058,	3.6238,	3.638,	3.6535,	3.6715,	3.6951,	3.7279,	3.7757,	3.8126,	3.8529,	3.8969,	3.9446,	3.9946,	4.0491,	4.109,	4.183, 4.263]
+            self.BLVol = 3
+            self.CellOVlmt=5
+            self.CellUVlmt=2
+            self.CantChrgVol=3
+          
+        elif celltype==2: #4840
+            self.Capacity = 41
+            self.PackFullChrgVolt=69.99
+            self.CellFullChrgVolt=4.2
+            self.CellVoltNums=14
+            self.CellTempNums=4
+            self.OtherTempNums=5            
+            self.FullChrgSoc=98
+            self.PeakSoc=57
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0,	    3.5348,	8.3581,	13.181,	18.004,	22.827,	27.651,	32.474,	37.297,	42.120,	46.944,	51.767,	56.590,	61.413,	66.237,	71.060,	75.883,	80.707,	85.530,	90.353,	95.176,	100,   105]
+            self.LookTab_OCV = [3.3159,	3.4384,	3.4774,	3.5156,	3.5478,	3.5748,	3.6058,	3.6238,	3.638,	3.6535,	3.6715,	3.6951,	3.7279,	3.7757,	3.8126,	3.8529,	3.8969,	3.9446,	3.9946,	4.0491,	4.109,	4.183, 4.263]
+            self.BLVol = 3
+            self.CellOVlmt=5
+            self.CellUVlmt=2
+            self.CantChrgVol=3        
+
+        elif  celltype==3:   #力信50ah三元电芯
+            self.Capacity = 51
+            self.PackFullChrgVolt=80
+            self.CellFullChrgVolt=4.2
+            self.CellVoltNums=20
+            self.CellTempNums=4
+            self.OtherTempNums=1
+            self.FullChrgSoc=98
+            self.PeakSoc=57
+            self.PackCrntDec=1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0,	    5,	    10,	    15,	    20,	    25,	    30,	    35,	    40,	    45,	    50,	    55,	    60,	    65,	    70,	    75,	    80,	    85,	    90,	    95,	    100,   105]
+            self.LookTab_OCV = [3.357, 	3.455, 	3.493, 	3.540, 	3.577, 	3.605, 	3.622, 	3.638, 	3.655, 	3.677, 	3.707, 	3.757, 	3.815, 	3.866, 	3.920, 	3.976, 	4.036, 	4.099, 	4.166, 	4.237, 	4.325, 4.415]
+            self.BLVol = 3
+            self.CellOVlmt=5
+            self.CellUVlmt=2
+            self.CantChrgVol=3
+
+        elif celltype==4:   #CATL 50ah三元电芯
+            self.Capacity = 50
+            self.PackFullChrgVolt=80
+            self.CellFullChrgVolt=4.2
+            self.CellVoltNums=20
+            self.CellTempNums=2
+            self.OtherTempNums=0
+            self.FullChrgSoc=98
+            self.PeakSoc=57
+            self.PackCrntDec=-1
+            self.BalCurrent=0.015
+            self.LookTab_SOC = [0,	    5,	    10,	    15,	    20,	    25,	    30,	    35,	    40,	    45,	    50,	    55,	    60,	    65,	    70,	    75,	    80,	    85,	    90,	    95,	    100,   105]
+            self.LookTab_OCV = [3.152, 	3.397, 	3.438, 	3.481, 	3.523, 	3.560, 	3.586, 	3.604, 	3.620, 	3.638, 	3.661, 	3.693, 	3.748, 	3.803, 	3.853, 	3.903, 	3.953, 	4.006, 	4.063, 	4.121, 	4.183, 4.253]
+            self.BLVol = 3
+            self.CellOVlmt=5
+            self.CellUVlmt=2
+            self.CantChrgVol=3
+          
+
+        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.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.BLVol = 3
+            self.CellOVlmt=4
+            self.CellUVlmt=2
+            self.CantChrgVol=2.6
+ 
+
+
+
+
+        else:
+            print('未找到对应电池编号!!!')
+            # sys.exit()

+ 52 - 0
LIB/MIDDLE/Stray/VoltageStray/V_1_0_0/VoltageStray.py

@@ -0,0 +1,52 @@
+import pandas as pd
+
+class VoltageStray:
+    def __init__(self):  #参数初始化
+        pass
+
+    def cal_vol_uniform(self, df, roll=1, alarm_threshold=5):
+        volt_column = [x for x in df.columns if "单体电压" in x]
+        all_columns = volt_column.copy()
+        all_columns.append("时间戳")
+        df = df[all_columns]
+        df_filt = df.copy()
+        if len(df_filt) > 0:
+
+            # df_filt = df_t.dropna(how='all')
+            df_filt = df_filt.reset_index(drop=True)
+            volt_count = len(volt_column)-1
+            volt_column = volt_column
+            df_cell_volt = df_filt[all_columns]
+            df_cell_volt = df_cell_volt.dropna().reset_index(drop=True)
+            
+
+            # 观察单体电压
+            df_rolling = df_cell_volt.loc[:, volt_column].rolling(roll, center=True, min_periods=1).mean()  # 滑动平均值
+
+            # df_temp_change = df_temp_change.dropna().reset_index(drop=True)
+            # df_temp_change_2 = df_temp_change_2.dropna().reset_index(drop=True)
+
+            mean = df_rolling.mean(axis=1)
+            std = df_rolling.std(axis=1)
+            df_rolling_norm = df_rolling.sub(mean, axis=0).div(std,axis=0)
+
+            # fig = plt.figure(figsize=(20,12))
+            # ax1 = fig.add_subplot(2,1,1)
+            # ax1.set_title("电压 - " + file[0:-5])
+            # ax1.plot(df_rolling_norm)
+
+            # plt.savefig('./res/'+str(count) + '-' + file[0:-5] +"--原始电压-"+ '.png')
+            # plt.close()
+            # plt.clf()
+            # plt.cla()
+
+            all_alarm = {'alarm_time':[], 'alarm_cell':[]}
+            df_alarm = df_rolling_norm[abs(df_rolling_norm)>1].dropna(how='all')
+            for index in df_alarm.index:
+                print(index)
+                df_temp = df_alarm.loc[index, :].dropna(how='all', axis=0)
+                all_alarm['alarm_time'].append(df_cell_volt.loc[index, '时间戳'])
+                alarm_cell = df_temp.keys().tolist()
+                all_alarm['alarm_cell'].append(str([x[4:] for x in alarm_cell]))
+            df_alarm = pd.DataFrame(all_alarm)
+            return df_alarm

+ 15 - 0
LIB/MIDDLE/Stray/VoltageStray/main.py

@@ -0,0 +1,15 @@
+# 获取数据
+import sys
+from LIB.BACKEND import DBManager
+from LIB.MIDDLE.Stray.VoltageStray.V_1_0_0 import VoltageStray
+
+sn = "MGMCLN750N215N116"
+st = '2021-09-17 21:33:07'
+et = '2021-09-17 21:35:07'
+
+dbManager = DBManager.DBManager()
+df_data = dbManager.get_data(sn=sn, start_time=st, end_time=et, data_groups=['bms'])
+# 
+df_bms = df_data['bms']
+voltageStray = VoltageStray.VoltageStray();
+print(voltageStray.cal_vol_uniform(df_bms, 1, 5))

+ 277 - 0
LIB/MIDDLE/odo/DailyMileageEstimation/V1_0_0/daily_mileage.py

@@ -0,0 +1,277 @@
+import pandas as pd
+import numpy as np
+from LIB.BACKEND.DataPreProcess import DataPreProcess
+import os
+import math
+import datetime
+import time
+
+def cal_mileage(sn,data_gps,data_bms):
+
+    
+    #将时间戳由 "%Y-%m-%d %H:%M:%S" 切换为 sec
+    def timeconvert(df_in,column_name):  
+        df=df_in.copy()
+        df.index=range(len(df))
+        time=df[column_name]
+        timeInSeries=[]
+        
+        time2=datetime.datetime.strptime(time[0],"%Y-%m-%d %H:%M:%S")
+        for k in range(len(time)):
+            time1=datetime.datetime.strptime(time[k],"%Y-%m-%d %H:%M:%S")    
+            t=(time1-time2)
+            timeInSeries.append(t.days*86400+t.seconds)
+        df.loc[:,'相对时间[s]']=pd.DataFrame(timeInSeries,columns=['相对时间[s]'])
+        return df
+
+    data_bms=timeconvert(data_bms,'时间戳')
+
+    #计算累积能量
+    def cal_accumKwh(df_in):
+        I=df_in['总电流[A]'].values
+        V=df_in['总电压[V]'].values
+        t=df_in['相对时间[s]'].values
+        accumAh=[0.0]
+        for k in range(1,len(I)):
+            accumAh_temp=(t[k]-t[k-1])*((I[k]+I[k-1])/(2*3600))*(V[k]+V[k-1])/2/1000
+            accumAh.append(accumAh[-1]+accumAh_temp)
+        df_in.loc[:,'累积能量[Kwh]']=accumAh
+        return(df_in)
+
+    data_bms=cal_accumKwh(data_bms)
+
+    #合并两张表格
+    df_bms=data_bms.copy()
+    df_gps=data_gps.copy()
+    df_bms.set_index(["时间戳"], inplace=True)
+    df_gps.set_index(["时间戳"], inplace=True)
+    df_temp = df_bms.append(df_gps)
+    df_temp=df_temp.sort_index(ascending=True)
+    df_temp.loc[:,'时间']=df_temp.index
+    df_temp.index=[k for k in range(len(df_temp))]
+    df_sheetCat=df_temp.copy()
+    df_sheetCat=timeconvert(df_sheetCat,'时间')
+    df_sheetCat=df_sheetCat.rename(columns={'时间':'时间戳'})
+    df_sheetCat=DataPreProcess.data_split_by_status(DataPreProcess,df_sheetCat, drive_interval_threshold=120, charge_interval_threshold=300, drive_stand_threshold=120, charge_stand_threshold=300)
+
+    #数据整理
+    df1=df_sheetCat.copy()
+    #如果前后data_status均不为none,但是本次为none,则将本次data_status设置为与上次相同。
+    data_status=df1['data_status'].values
+    for k in range(1,len(data_status)-1):
+        if data_status[k-1]!='none' and data_status[k+1]!='none':
+            df1.loc[k,'data_status']=data_status[k-1]
+
+    #如果前后两次电流值均不为nan,则将总电流[A]、总电压[V]、SOC[%]设置为与前次相同。
+    data_current=df1['总电流[A]'].values
+    for k in range(1,len(data_current)-1):
+        if not math.isnan(data_current[k-1]) and not math.isnan(data_current[k+1]) and math.isnan(data_current[k]):
+            df1.loc[k,'总电流[A]']=df1.loc[k-1,'总电流[A]']
+            df1.loc[k,'总电压[V]']=df1.loc[k-1,'总电压[V]']
+            df1.loc[k,'SOC[%]']=df1.loc[k-1,'SOC[%]']
+
+    #如果前后两次能量均不是NAN,但是本次为NAN,则将上次的能量指赋值给本次
+    AccumEnergy=df1['累积能量[Kwh]'].values
+    for k in range(1,len(AccumEnergy)-1):
+        if not math.isnan(AccumEnergy[k-1]) and not math.isnan(AccumEnergy[k+1]) and math.isnan(AccumEnergy[k]):
+            df1.loc[k,'累积能量[Kwh]']=AccumEnergy[k-1]
+
+    #删除一些无用列,获取用于计算的数据
+    df_input=df1[['时间戳','总电流[A]', '总电压[V]','SOC[%]','相对时间[s]', '累积能量[Kwh]','纬度', '经度','data_status']]
+    #筛选出有经纬度信息的数据
+    df_gpsOnly=df_input[df_input['纬度']>0]
+
+    #根据经纬度获取两点之间的距离
+    def cal_dis_meters(radius,latitude1, longitude1,latitude2, longitude2):  
+        radLat1 = (math.pi/180)*latitude1  
+        radLat2 = (math.pi/180)*latitude2  
+        radLng1 = (math.pi/180)*longitude1  
+        radLng2= (math.pi/180)*longitude2      
+        d=2*math.asin(math.sqrt(math.pow(math.sin((radLat1-radLat2)/2.0),2)+math.cos(radLat1)*math.cos(radLat2)*math.pow(math.sin((radLng1-radLng2)/2.0),2)))*radius
+        return d
+
+    #根据gps数据计算△距离1和△距离2
+    #△距离1:直接根据两次的经度和纬度计算得到的马氏距离
+    #△距离2:当两次上报经纬度的时间间隔<60sec时,如果车辆为行驶状态则使用两次的经纬度求得的马氏距离,
+    #如果车辆不为行驶状态,则为0.
+    index_list=df_gpsOnly.index
+    pos_list=df_gpsOnly[['纬度','经度']].values
+    time_list=df_input['相对时间[s]'].values
+    Energy_list=df_input['累积能量[Kwh]'].values
+    for k in range(1,len(pos_list)):
+        latitude1=pos_list[k-1][0]
+        longitude1=pos_list[k-1][1]
+        latitude2=pos_list[k][0]
+        longitude2=pos_list[k][1]
+        dlt_odo=cal_dis_meters(6378.137,latitude1, longitude1,latitude2, longitude2)
+        df_gpsOnly.loc[index_list[k],'△距离1']=dlt_odo    
+        if time_list[index_list[k]]-time_list[index_list[k-1]]<60:#两次上传GPS数据时间间隔小于60sec
+            if df_gpsOnly.loc[index_list[k],'data_status']=='drive' :   
+                df_gpsOnly.loc[index_list[k],'△距离2']=dlt_odo
+            elif df_gpsOnly.loc[index_list[k],'data_status']=='none' :
+                df_gpsOnly.loc[index_list[k],'△距离2']=dlt_odo
+            else:
+                df_gpsOnly.loc[index_list[k],'△距离2']=0
+
+    #如果某一段数据中有gps数据但是没有bms数据,里程按照gps数据计算,但是到有gps数据之后,发现累积能量变化不大于0,因此需要将这期间的里程均设置为0km            
+    df_gpsOnly_copy=df_gpsOnly.copy()
+    df_gpsOnly_copy.index=[i for i in range(len(df_gpsOnly))]
+    df_bmsOnly=df_gpsOnly_copy[df_gpsOnly_copy['总电压[V]']>0]
+    energy2=df_bmsOnly['累积能量[Kwh]'].values
+    df_bmsOnly_index=df_bmsOnly.index.values
+    for k in range(1,len(df_bmsOnly)):
+        if not (energy2[k]-energy2[k-1]>0):
+            for n in range(df_bmsOnly_index[k-1],df_bmsOnly_index[k]+1):
+                df_gpsOnly_copy.loc[n,'△距离2']=0
+
+    dis2=df_gpsOnly_copy['△距离2'].values
+    relatedTime=df_gpsOnly_copy['相对时间[s]'].values
+    nan_flag=0
+
+    for k in range(1,len(dis2)):
+        if math.isnan(dis2[k]) and (not math.isnan(dis2[k-1])): #本次△距离2为NAN但是上次不为NAN
+            nan_flag=1
+            ###平均能耗计算
+            st_time=relatedTime[k]-600  #10min之前的时刻点
+            df2=df_gpsOnly_copy[df_gpsOnly_copy['相对时间[s]']>=st_time]
+            df_temp2=df2[df2['相对时间[s]']<=relatedTime[k]]
+            df_temp2=df_temp2[['相对时间[s]','累积能量[Kwh]','△距离2']]
+            df_temp3=df_temp2.dropna(axis=0,how='any') 
+            if len(df_temp3)>5:
+                # st_time=relatedTime[k-1]-600  #10min之前的时刻点
+                # df2=df_gpsOnly_copy[df_gpsOnly_copy['相对时间[s]']>=st_time]
+                # df_temp2=df2[df2['相对时间[s]']<=relatedTime[k]]
+                
+                energy=df_temp3['累积能量[Kwh]'].values
+                dis_temp=df_temp3['△距离2'].values
+                # dis_temp_delet_nan=dis_temp[np.logical_not(np.isnan(dis_temp))]
+                df_gpsOnly_copy.loc[k,'△能量[Kwh]']=energy[-1]-energy[0]
+                df_gpsOnly_copy.loc[k,'累积距离[km]']=dis_temp[1:-1].sum()
+                if energy[-1]-energy[0]>0:
+                    df_gpsOnly_copy.loc[k,'能耗[km/kwh]']=min(max(df_gpsOnly_copy.loc[k,'累积距离[km]']/df_gpsOnly_copy.loc[k,'△能量[Kwh]'],25),50)
+                else:
+                    if dis_temp[1:-1].sum()>0:
+                        df_gpsOnly_copy.loc[k,'能耗[km/kwh]']=30
+                    else:
+                        df_gpsOnly_copy.loc[k,'能耗[km/kwh]']=0
+            else:
+                df_temp2=df_gpsOnly_copy[df_gpsOnly_copy['相对时间[s]']<=relatedTime[k]]
+                df_energyPerKm=df_gpsOnly_copy[df_gpsOnly_copy['能耗[km/kwh]']>0]
+                df_energyPerKm.index=[i for i in range(len(df_energyPerKm))]
+                total_num=len(df_energyPerKm)
+                #统计最近10次的平均能耗,用于补充dis2前后都为nan时刻处的能耗及距离,
+                ##如果不足10次按照实际次数统计
+                if total_num>10:
+                    energycost_acc=0
+                    for i in range(10):
+                        energycost_acc=energycost_acc+df_energyPerKm.loc[total_num-i-1,'能耗[km/kwh]']
+                    avg_energyCost=min(max(energycost_acc/10,25),50)
+                    df_gpsOnly_copy.loc[k,'△能量[Kwh]']=1
+                    df_gpsOnly_copy.loc[k,'累积距离[km]']=avg_energyCost
+                    df_gpsOnly_copy.loc[k,'能耗[km/kwh]']=avg_energyCost
+                elif total_num>0:
+                    avg_energyCost=min(max(df_energyPerKm['能耗[km/kwh]'].values.mean(),25),50)
+                    df_gpsOnly_copy.loc[k,'△能量[Kwh]']=1
+                    df_gpsOnly_copy.loc[k,'累积距离[km]']=avg_energyCost
+                    df_gpsOnly_copy.loc[k,'能耗[km/kwh]']=avg_energyCost
+                else:
+                    df_gpsOnly_copy.loc[k,'△能量[Kwh]']=df_gpsOnly_copy.loc[k-1,'△能量[Kwh]'] 
+                    df_gpsOnly_copy.loc[k,'累积距离[km]']=df_gpsOnly_copy.loc[k-1,'累积距离[km]']
+                    df_gpsOnly_copy.loc[k,'能耗[km/kwh]']=df_gpsOnly_copy.loc[k-1,'能耗[km/kwh]']
+                
+            
+            ####行驶里程估计
+            energy1=df_temp2['累积能量[Kwh]'].values
+            if energy1[-1]-energy1[-2]>0:
+                gps_dis=df_gpsOnly_copy.loc[k,'△距离1']
+                delta_t=df_gpsOnly_copy.loc[k,'相对时间[s]']-df_gpsOnly_copy.loc[k-1,'相对时间[s]']
+                vehspd=gps_dis*3600/delta_t
+                if vehspd>30:
+                    df_gpsOnly_copy.loc[k,'△距离3[km]']=(energy1[-1]-energy1[-2])*df_gpsOnly_copy.loc[k,'能耗[km/kwh]']
+                else:
+                    df_gpsOnly_copy.loc[k,'△距离3[km]']=max((energy1[-1]-energy1[-2])*df_gpsOnly_copy.loc[k,'能耗[km/kwh]'],gps_dis)
+            else:
+                df_gpsOnly_copy.loc[k,'△距离3[km]']=0
+
+            df_gpsOnly_copy.loc[k,'△距离2']=df_gpsOnly_copy.loc[k,'△距离3[km]']
+            
+        elif math.isnan(dis2[k]) and (math.isnan(dis2[k-1])) and nan_flag==1: #本次△距离为NAN上次也为NAN
+            df_energyPerKm=df_gpsOnly_copy[df_gpsOnly_copy['能耗[km/kwh]']>0]
+            df_energyPerKm.index=[i for i in range(len(df_energyPerKm))]
+            total_num=len(df_energyPerKm)
+            #统计最近10次的平均能耗,用于补充dis2前后都为nan时刻处的能耗及距离,
+            ##如果不足10次按照实际次数统计
+            if total_num>10:
+                energycost_acc=0
+                for i in range(10):
+                    energycost_acc=energycost_acc+df_energyPerKm.loc[total_num-i-1,'能耗[km/kwh]']
+                avg_energyCost=min(max(energycost_acc/10,25),50)
+                df_gpsOnly_copy.loc[k,'△能量[Kwh]']=1
+                df_gpsOnly_copy.loc[k,'累积距离[km]']=avg_energyCost
+                df_gpsOnly_copy.loc[k,'能耗[km/kwh]']=avg_energyCost
+            elif total_num>0:
+                avg_energyCost=min(max(df_energyPerKm['能耗[km/kwh]'].values.mean(),25),50)
+                df_gpsOnly_copy.loc[k,'△能量[Kwh]']=1
+                df_gpsOnly_copy.loc[k,'累积距离[km]']=avg_energyCost
+                df_gpsOnly_copy.loc[k,'能耗[km/kwh]']=avg_energyCost
+            else:
+                df_gpsOnly_copy.loc[k,'△能量[Kwh]']=df_gpsOnly_copy.loc[k-1,'△能量[Kwh]'] 
+                df_gpsOnly_copy.loc[k,'累积距离[km]']=df_gpsOnly_copy.loc[k-1,'累积距离[km]']
+                df_gpsOnly_copy.loc[k,'能耗[km/kwh]']=df_gpsOnly_copy.loc[k-1,'能耗[km/kwh]']
+            
+            ##计算行驶里程
+            delta_energy=df_gpsOnly_copy.loc[k,'累积能量[Kwh]']-df_gpsOnly_copy.loc[k-1,'累积能量[Kwh]']
+            if delta_energy>0:
+                gps_dis=df_gpsOnly_copy.loc[k,'△距离1']
+                delta_t=df_gpsOnly_copy.loc[k,'相对时间[s]']-df_gpsOnly_copy.loc[k-1,'相对时间[s]']
+                vehspd=gps_dis*3600/delta_t
+                if vehspd>30:
+                    df_gpsOnly_copy.loc[k,'△距离3[km]']=delta_energy*df_gpsOnly_copy.loc[k,'累积距离[km]']/df_gpsOnly_copy.loc[k,'△能量[Kwh]']
+                else:
+                    df_gpsOnly_copy.loc[k,'△距离3[km]']=max(delta_energy*df_gpsOnly_copy.loc[k,'累积距离[km]']/df_gpsOnly_copy.loc[k,'△能量[Kwh]'],gps_dis)
+            else:
+                df_gpsOnly_copy.loc[k,'△距离3[km]']=0
+            
+            df_gpsOnly_copy.loc[k,'△距离2']=df_gpsOnly_copy.loc[k,'△距离3[km]']
+        else:
+            df_gpsOnly_copy.loc[k,'△能量[Kwh]']=1
+            df_gpsOnly_copy.loc[k,'累积距离[km]']=25
+            df_gpsOnly_copy.loc[k,'能耗[km/kwh]']=25
+            df_gpsOnly_copy.loc[k,'△距离3[km]']=df_gpsOnly_copy.loc[k,'△距离2']
+
+    ###############输出计算结果##################
+    ############################################
+    df_gpsOnly_copy.loc[0,'累积里程[km]']=0
+    df_gpsOnly_copy.loc[0,'日期']=str(df_gpsOnly_copy.loc[0,'时间戳'])[0:10]  
+
+    for k in range(1,len(df_gpsOnly_copy)):
+        df_dis_temp=df_gpsOnly_copy['△距离3[km]'].values[0:k+1]
+        df_dis_temp1=df_dis_temp[np.logical_not(np.isnan(df_dis_temp))]
+        if len(df_dis_temp1)>0:
+            df_gpsOnly_copy.loc[k,'累积里程[km]']=df_dis_temp1.sum()
+        else:
+            df_gpsOnly_copy.loc[k,'累积里程[km]']=df_gpsOnly_copy.loc[k-1,'累积里程[km]']
+        df_gpsOnly_copy.loc[k,'日期']=str(df_gpsOnly_copy.loc[k,'时间戳'])[0:10] 
+
+    datetime1=np.unique(df_gpsOnly_copy['日期'].values)
+    df_result=pd.DataFrame(index=datetime1)
+    list_result=[]
+    list_day_odo=[]
+
+    for k in range(len(datetime1)):
+        df_day=df_gpsOnly_copy[df_gpsOnly_copy['日期']==datetime1[k]]
+        odo_day=df_day['累积里程[km]'].values
+        df_result.loc[datetime1[k],'累积里程[km]']=odo_day[-1]-odo_day[0]
+        df_day['累积里程[km]']=odo_day-odo_day[0]
+        list_day_odo.extend(odo_day-odo_day[0])
+        list_result.append([datetime1[k],df_day])
+
+    df_gpsOnly_copy.loc[:,'每日累积里程[km]']=list_day_odo
+
+    #删除一些无用列,获取用于计算的数据
+    df_output=df_gpsOnly_copy[['时间戳','data_status','SOC[%]','每日累积里程[km]']]
+
+    #添加sn
+    df_output['sn'] = sn
+    return df_output
+

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


+ 41 - 0
LIB/MIDDLE/odo/DailyMileageEstimation/main_daily_mileage.py

@@ -0,0 +1,41 @@
+
+from LIB.BACKEND import DBManager
+import mileage
+from LIB.MIDDLE.CellStateEstimation.Common import log
+import datetime
+import pandas as pd
+
+dbManager = DBManager.DBManager()
+
+now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')   #type: str
+now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')     #type: datetime
+start_time=now_time-datetime.timedelta(days=1)
+end_time=str(now_time)
+start_time=str(start_time)
+
+dataSOH = pd.read_excel('sn-20210903.xlsx',sheet_name='sn-20210903')
+fileNames = dataSOH['sn']
+fileNames = list(fileNames)
+l = len(fileNames)
+
+#log信息配置
+mylog=log.Mylog('log.txt','error')
+mylog.logcfg()
+
+
+for k in range(l):
+    try:
+        sn = fileNames[k]
+        df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms','gps'])
+        data_bms = df_data['bms']
+        data_gps = df_data['gps']
+
+        #...............每日累积里程............................................................................
+        if len(data_bms['时间戳'])>0:
+            df_res = mileage.cal_mileage(sn,data_gps,data_bms)
+            df_res.to_csv('Mileage_'+sn+'.csv')
+        
+    except Exception as e:
+        print(repr(e))
+        mylog.logopt(sn,e)
+        pass