import pandas as pd
import numpy as np
import datetime
import bisect
import matplotlib.pyplot as plt
from LIB.MIDDLE.CellStateEstimation.Common import BatParam

class BatSoh:
    def __init__(self,sn,celltype,df_bms,df_accum,df_soh):  #参数初始化

        self.sn=sn
        self.celltype=celltype
        self.param=BatParam.BatParam(celltype)
        self.df_bms=df_bms
        self.packcrnt=df_bms['总电流[A]']*self.param.PackCrntDec
        self.packvolt=df_bms['总电压[V]']
        self.bms_soc=df_bms['SOC[%]']
        self.bms_soh=df_bms['SOH[%]']
        self.bmsstat=df_bms['充电状态']
        self.bmstime= pd.to_datetime(df_bms['时间戳'], format='%Y-%m-%d %H:%M:%S')

        self.df_accum=df_accum
        self.accumtime=pd.to_datetime(df_accum['时间戳'], format='%Y-%m-%d %H:%M:%S')

        self.df_soh=df_soh

    def batsoh(self):
        if self.celltype==1 or self.celltype==2 or self.celltype==3 or self.celltype==4:
            df_res=self._ncmsoh_twopoint()
            return df_res
            
        elif self.celltype==99:
            df_res=self._lfpsoh()
            return df_res

        else:
            return pd.DataFrame()

    #定义滑动滤波函数.........................................................................................................................
    def _np_move_avg(self,a, n, mode="same"):
        return (np.convolve(a, np.ones((n,)) / n, mode=mode))

    #筛选充电数据..............................................................................................................................
    def _chrgdata(self):    
        self.ChgStart=[]
        self.ChgEnd=[]
        if len(self.packvolt)>100:
            for i in range(3, len(self.bmstime) - 3):
                if i==3 and self.bmsstat[i]==2 and self.bmsstat[i+1]==2 and self.bmsstat[i+2]==2:
                    self.ChgStart.append(i)
                elif self.bmsstat[i-2]!=2 and self.bmsstat[i-1]!=2 and self.bmsstat[i]==2:
                    self.ChgStart.append(i)
                elif self.bmsstat[i-1]==2 and self.bmsstat[i]!=2 and self.bmsstat[i+1]!=2:
                    self.ChgEnd.append(i-1)
                elif i == (len(self.bmstime) - 4) and self.bmsstat[len(self.bmsstat)-1] == 2 and self.bmsstat[len(self.bmsstat)-2] == 2:
                    self.ChgEnd.append(len(self.bmstime)-2)
    
    #寻找当前行数据的最小温度值.................................................................................................................
    def _celltemp_weight(self,num):   
        celltemp = []
        for j in range(1, self.param.CellTempNums+1):
            s = str(j)
            celltemp.append(self.df_bms.loc[num,'单体温度' + s])
        celltemp.remove(min(celltemp))
        self.celltemp=celltemp
        if self.celltype==99:
            if min(celltemp)>=20:
                self.tempweight=1
                self.StandardStandingTime=1800
            elif min(celltemp)>=10:
                self.tempweight=0.5
                self.StandardStandingTime=3600
            elif min(celltemp)>=5:
                self.tempweight=0.2
                self.StandardStandingTime=7200
            else:
                self.tempweight=0
                self.StandardStandingTime=10800
        else:
            if min(celltemp)>=20:
                self.tempweight=1
                self.StandardStandingTime=1200
            elif min(celltemp)>=10:
                self.tempweight=0.8
                self.StandardStandingTime=1800
            elif min(celltemp)>=5:
                self.tempweight=0.3
                self.StandardStandingTime=3600
            else:
                self.tempweight=0.1
                self.StandardStandingTime=7200

    #获取SOC差对应的SOH权重值...................................................................................................................
    def _deltsoc_weight(self,deltsoc):   
        if deltsoc>60:   
            deltsoc_weight=1
        elif deltsoc>50:
            deltsoc_weight=0.9
        elif deltsoc>40:
            deltsoc_weight=0.7
        elif deltsoc>30:
            deltsoc_weight=0.5
        elif deltsoc>20:
            deltsoc_weight=0.3
        else:
            deltsoc_weight=0
        return deltsoc_weight

    #获取当前行所有电压数据......................................................................................................................
    def _cellvolt_get(self,num): 
        cellvolt=[]
        for j in range(1, self.param.CellVoltNums+1): 
            s = str(j)
            cellvolt.append(self.df_bms.loc[num,'单体电压' + s]/1000)
        return cellvolt
    
    #dvdq方法计算soh...........................................................................................................................
    def _dvdq_soh(self, chrg_st, chrg_end,cellvolt):    
        Ah = 0  #参数赋初始值
        Volt = cellvolt[chrg_st]
        DV_Volt=[]
        DQ_Ah = []
        DVDQ = []
        time2 = []
        soc2 = []
        Ah_tatal=[0]
        xvolt=[]
        #计算DV和DQ值
        for j in range(chrg_st,chrg_end):
            Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
            Ah=Ah-self.packcrnt[j]*Step/3600
            if (cellvolt[j]-Volt)>0.0019 and Ah>0:
                Ah_tatal.append(Ah_tatal[-1]+Ah)
                DQ_Ah.append(Ah)
                DV_Volt.append(cellvolt[j]-Volt)
                DVDQ.append((DV_Volt[-1])/DQ_Ah[-1])
                xvolt.append(cellvolt[j])
                Volt=cellvolt[j]
                Ah = 0
                time2.append(self.bmstime[j])
                soc2.append(self.bms_soc[j])

        #切片，去除前后10min的数据
        df_Data1 = pd.DataFrame({'time': time2,
                                'SOC': soc2,
                                'DVDQ': DVDQ,
                                'Ah_tatal': Ah_tatal[:-1],
                                'DQ_Ah':DQ_Ah,
                                'DV_Volt':DV_Volt,
                                'XVOLT':xvolt})
        start_time=df_Data1.loc[0,'time']
        start_time=start_time+datetime.timedelta(seconds=900)
        end_time=df_Data1.loc[len(time2)-1,'time']
        end_time=end_time-datetime.timedelta(seconds=1200)
        if soc2[0]<36:
            df_Data1=df_Data1[(df_Data1['SOC']>40) & (df_Data1['SOC']<80)]
        else:
            df_Data1=df_Data1[(df_Data1['time']>start_time) & (df_Data1['SOC']<80)]
        df_Data1=df_Data1[(df_Data1['XVOLT']>self.param.PeakVoltLowLmt) & (df_Data1['XVOLT']<self.param.PeakVoltUpLmt)]

        # self._celltemp_weight(int((chrg_st+chrg_end)/2))
        # print(self.packcrnt[int((chrg_st+chrg_end)/2)], min(self.celltemp))
        # ax1 = plt.subplot(3, 1, 1)
        # plt.plot(df_Data1['XVOLT'],df_Data1['DVDQ'],'r*-')
        # plt.xlabel('Volt/V')
        # plt.ylabel('DV/DQ')
        # plt.legend()
        # ax1 = plt.subplot(3, 1, 2)
        # plt.plot(df_Data1['SOC'],df_Data1['XVOLT'],'y*-')
        # plt.xlabel('SOC/%')
        # plt.ylabel('Volt/V')
        # plt.legend()
        # ax1 = plt.subplot(3, 1, 3)
        # plt.plot(df_Data1['SOC'], df_Data1['DVDQ'], 'r*-')
        # plt.xlabel('SOC/%')
        # plt.ylabel('DV/DQ')
        # plt.legend()
        # plt.show()

        #寻找峰值并计算Soh
        if len(df_Data1)>1:
            PeakIndex=df_Data1['DVDQ'].idxmax()
            #筛选峰值点附近±0.5%SOC内的数据
            df_Data2=df_Data1[(df_Data1['SOC']>(df_Data1['SOC'][PeakIndex]-0.5)) & (df_Data1['SOC']<(df_Data1['SOC'][PeakIndex]+0.5))]
            if len(df_Data2)>1 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.019:
                Ah_tatal1 = df_Data1['Ah_tatal']
                DVDQ = df_Data1['DVDQ']
                soc2 = df_Data1['SOC']
                xvolt = df_Data1['XVOLT']
                if soc2[PeakIndex]>40 and soc2[PeakIndex]<80:
                    cellsoh_init=(Ah_tatal[-1]-Ah_tatal1[PeakIndex]) * 100 / ((self.param.FullChrgSoc - self.param.PeakSoc) * 0.01 * self.param.Capacity)
                    if cellsoh_init<95:
                        cellsoh_init=cellsoh_init*0.3926+58.14
                        return cellsoh_init
                    else:
                        return cellsoh_init
                else:
                    return 0
            else:
                df_Data1=df_Data1.drop([PeakIndex])
                PeakIndex = df_Data1['DVDQ'].idxmax()
                df_Data2 = df_Data1[(df_Data1['SOC'] > (df_Data1['SOC'][PeakIndex] - 0.5)) & (df_Data1['SOC'] < (df_Data1['SOC'][PeakIndex] + 0.5))]
                if len(df_Data2) > 1 and df_Data1.loc[PeakIndex,'XVOLT']<self.param.PeakVoltUpLmt-0.019:
                    Ah_tatal1 = df_Data1['Ah_tatal']
                    DVDQ = df_Data1['DVDQ']
                    soc2 = df_Data1['SOC']
                    xvolt = df_Data1['XVOLT']
                    if soc2[PeakIndex]>40 and soc2[PeakIndex]<80:
                        cellsoh_init=(Ah_tatal[-1]-Ah_tatal1[PeakIndex]) * 100 / ((self.param.FullChrgSoc - self.param.PeakSoc) * 0.01 * self.param.Capacity)
                        if cellsoh_init<95:
                            cellsoh_init=cellsoh_init*0.3926+58.14
                            return cellsoh_init
                        else:
                            return cellsoh_init
                    else:
                        return 0
                else:
                    return 0
        else:
            return 0
                
    #NCM充电数据soh计算.........................................................................................................................
    def _ncmsoh_chrg(self):      
        self._chrgdata()
        ChgStartValid=[]
        ChgEndValid=[]
        tempweightlist=[]
        for i in range(min(len(self.ChgStart),len(self.ChgEnd))): 
            self._celltemp_weight(self.ChgEnd[i])               #获取温度对应的静置时间及权重            
            for k in range(self.ChgStart[i],self.ChgEnd[i]):   #去除电流0点
                if self.packcrnt[k]<-0.5 and self.packcrnt[k+1]>-0.5 and self.packcrnt[k+2]>-0.5 and self.packcrnt[k+3]>-0.5:
                    self.ChgEnd[i]=k
   
            #筛选满足2点法计算的数据
            StandingTime=0
            StandingTime1=0
            if self.bms_soc[self.ChgEnd[i]]>70 and self.bms_soc[self.ChgStart[i]]<50:
                for m in range(min(len(self.packcrnt)-self.ChgEnd[i]-2,self.ChgStart[i]-2)):
                    if abs(self.packcrnt[self.ChgStart[i] - m - 1]) < 0.5:
                        StandingTime = StandingTime + (self.bmstime[self.ChgStart[i] - m] - self.bmstime[self.ChgStart[i] - m - 1]).total_seconds()
                    if abs(self.packcrnt[self.ChgEnd[i] + m + 1]) < 0.5:
                        StandingTime1 = StandingTime1 + (self.bmstime[self.ChgEnd[i] + m + 1] - self.bmstime[self.ChgEnd[i] + m]).total_seconds()
                    if StandingTime > self.StandardStandingTime and StandingTime1>self.StandardStandingTime and ((self.bmstime[self.ChgEnd[i]]-self.bmstime[self.ChgStart[i]]).total_seconds())/(self.ChgEnd[i]-self.ChgStart[i])<60:  #筛选静置时间>15min且慢充过程丢失数据少
                        if abs(self.packcrnt[self.ChgEnd[i] + m + 2])>0.5 or m==len(self.packcrnt)-self.ChgEnd[i]-3: #如果电流<0.5,继续寻找充电后的静置电压，直到末尾
                            ChgStartValid.append(self.ChgStart[i])
                            ChgEndValid.append(self.ChgEnd[i]+m)
                            tempweightlist.append(self.tempweight)
                            break
                    if abs(self.packcrnt[self.ChgStart[i] - m - 2])>0.5 and abs(self.packcrnt[self.ChgEnd[i] + m + 2])>0.5:
                        break

        if len(ChgStartValid)>0:   #两点法计算Soh
            df_res=pd.DataFrame(columns=('time','sn','soh','soh1'))
            soh2=[]
            if not self.df_soh.empty:  #获取数据库中上次计算的Soh值
                soh_init=list(self.df_soh['soh'])[-1]
            else:
                soh_init=list(self.bms_soh)[-1]

            for i in range(len(ChgStartValid)):
                Ah=0
                for j in range(ChgStartValid[i],ChgEndValid[i]):  #计算Ah
                    Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
                    Ah=Ah-self.packcrnt[j+1]*Step/3600

                for j in range(1, self.param.CellVoltNums+1):    #计算每个电芯的Soh
                    s = str(j)
                    OCVStart=self.df_bms.loc[ChgStartValid[i]-2,'单体电压' + s]/1000
                    OCVEnd=self.df_bms.loc[ChgEndValid[i]-1,'单体电压' + s]/1000
                    #soh
                    ocv_Soc1=np.interp(OCVStart,self.param.LookTab_OCV,self.param.LookTab_SOC)
                    ocv_Soc2=np.interp(OCVEnd,self.param.LookTab_OCV,self.param.LookTab_SOC)
                
                    soh2.append(Ah*100/((ocv_Soc2-ocv_Soc1)*0.01*self.param.Capacity))
                soh1=np.mean(soh2)
                delt_ocv_soc=ocv_Soc2-ocv_Soc1
                self._deltsoc_weight(delt_ocv_soc)
                soh_res=soh_init*(1-self.deltsoc_weight*tempweightlist[i])+soh1*self.deltsoc_weight*tempweightlist[i]
                soh_init=soh_res
                df_res.loc[i]=[self.bmstime[ChgStartValid[i]],self.sn,soh_res,soh1]
            
            return df_res
        return pd.DataFrame()

    #两点法计算三元SOH.........................................................................................................................
    def _ncmsoh_twopoint(self):
        standingpoint_st=[]
        standingpoint_sp=[]
        tempweightlist=[]
        standingtime=0
        for i in range(3,len(self.df_bms)-3):

            if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1:     #电流为0
                delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
                standingtime=standingtime+delttime
                self._celltemp_weight(i)     #获取不同温度对应的静置时间

                if standingtime>self.StandardStandingTime:      #静置时间满足要求
                    if standingpoint_st:                        
                        if len(standingpoint_st)>len(standingpoint_sp):     #开始时刻已获取，结束时刻未获取
                            cellvolt_now=self._cellvolt_get(i-1)     #获取当前行电压数据
                            minocv_socnow=np.interp(min(cellvolt_now),self.param.LookTab_OCV,self.param.LookTab_SOC)
                            cellvolt_st=self._cellvolt_get(standingpoint_st[-1])   #获取开始时刻静置后的电压数据
                            minocv_socst=np.interp(min(cellvolt_st),self.param.LookTab_OCV,self.param.LookTab_SOC)

                            if abs(minocv_socst-minocv_socnow)>=40:   #当前时刻SOC与开始时刻SOC差>=40
                                if abs(self.packcrnt[i+2])>=0.1:    #如果下一时刻电流>=0.5，则压入当前索引
                                    standingpoint_sp.append(i)
                                    standingpoint_st.append(i)
                                    tempweightlist.append(self.tempweight)
                                    standingtime=0
                                    continue
                                else:
                                    if standingtime>3600 or i==len(self.df_bms)-2:   #仍处于静置，但静置时间>1h，则直接获取sp时刻，或者到了数据末尾
                                        standingpoint_sp.append(i)
                                        tempweightlist.append(self.tempweight)
                                        continue
                            else:
                                if abs(self.packcrnt[i+2])>=0.1:
                                    standingtime=0
                                    if  minocv_socst<50 and minocv_socnow<minocv_socst:
                                        standingpoint_st[-1]=i
                                        continue
                                elif abs(self.packcrnt[i+2])>=0.1:
                                    standingtime=0
                                    if minocv_socst>=50 and minocv_socnow>minocv_socst:
                                        standingpoint_st[-1]=i
                                        continue
                                else:
                                    continue
                        else:
                            if abs(self.packcrnt[i+2])>=0.1:
                                standingpoint_st.append(i)
                                standingtime=0
                                continue
                            else:
                                continue
                    else:
                        if abs(self.packcrnt[i+2])>0.1:
                            standingpoint_st.append(i)
                            standingtime=0
                            continue
                        else:
                            continue
                else:
                    continue
            else:
                standingtime=0
                continue

        #计算SOH......................................................................................................................
        if standingpoint_sp:
            column_name=['time_st','time_sp','sn','method','soh','cellsoh']
            df_res=pd.DataFrame(columns=column_name)

            for i in range(len(standingpoint_sp)):
                cellocv_st=self._cellvolt_get(standingpoint_st[i])    #获取静置点所有电芯的电压
                cellocv_sp=self._cellvolt_get(standingpoint_sp[i])
                accumtime=self.accumtime.to_list()  #累计量的时间列表
                timepoint_bms_st=self.bmstime[standingpoint_st[i]]   #获取静置点的时间
                timepoint_bms_sp=self.bmstime[standingpoint_sp[i]]
                timepoint_accum_st=bisect.bisect(accumtime,timepoint_bms_st)   #获取最接近静置点时间的累计量时间点
                timepoint_accum_sp=bisect.bisect(accumtime,timepoint_bms_sp)
                if timepoint_accum_sp>=len(accumtime):  #防止指针超出数据范围
                    timepoint_accum_sp=len(accumtime)-1
                
                ah_packcrnt_dis=0
                ah_packcrnt_chg=0
                for j in range(standingpoint_st[i]+2,standingpoint_sp[i]): #计算累计Ah
                    Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
                    if self.packcrnt[j+1]>=0:
                        ah_packcrnt_dis=ah_packcrnt_dis+self.packcrnt[j+1]*Step
                    else:
                        ah_packcrnt_chg=ah_packcrnt_chg-self.packcrnt[j+1]*Step  
                ah_packcrnt_chg=ah_packcrnt_chg/3600
                ah_packcrnt_dis=ah_packcrnt_dis/3600
                ah_packcrnt=ah_packcrnt_chg-ah_packcrnt_dis     #两个静置点的总累计AH，负值代表放电，正值代表充电
     
                ah_accum_dis=self.df_accum.loc[timepoint_accum_sp,'累计放电电量']-self.df_accum.loc[timepoint_accum_st,'累计放电电量']  #两个静置点之间的放电电量
                ah_accum_chg=self.df_accum.loc[timepoint_accum_sp,'累计充电电量']-self.df_accum.loc[timepoint_accum_st,'累计充电电量']  #两个静置点之间的充电电量
                ah_accum_tatol=ah_accum_chg-ah_accum_dis  #两个静置点的总累计AH，负值代表放电，正值代表充电
                ah_accum=ah_packcrnt

                delt_days=(self.bmstime[standingpoint_sp[i]]-self.bmstime[standingpoint_st[i]]).total_seconds()/(3600*24)
                if delt_days<=1: #两次时间间隔对计算结果的影响
                    soh_weight1=1
                elif delt_days<=2:
                    soh_weight1=0.7
                elif delt_days<=3:
                    soh_weight1=0.4
                else:
                    soh_weight1=0
                
                if ah_packcrnt_dis<self.param.Capacity: #放电ah数对结果的影响
                    soh_weight1=(1-ah_packcrnt_dis/(self.param.Capacity*1.5))*soh_weight1
                else:
                    soh_weight1=0.1
            
                if self.param.Capacity**0.7*0.4 < abs(ah_accum_tatol) < self.param.Capacity:    #累计量的权重
                    if abs(ah_accum_tatol-ah_packcrnt)<self.param.Capacity/20:
                        soh_weight1=soh_weight1*1
                    elif abs(ah_accum_tatol-ah_packcrnt) < self.param.Capacity/10:
                        soh_weight1=soh_weight1*0.8
                    else:
                        soh_weight1=soh_weight1*0.5
                else:
                    if self.param.Capacity*0.7*0.4< abs(ah_packcrnt) <self.param.Capacity:
                        soh_weight1=soh_weight1*0.3
                    else:
                        soh_weight1=0

                cellsoh=[]
                for j in range(self.param.CellVoltNums):    #计算每个电芯的SOH值
                    ocv_soc1=np.interp(cellocv_st[j],self.param.LookTab_OCV,self.param.LookTab_SOC)
                    ocv_soc2=np.interp(cellocv_sp[j],self.param.LookTab_OCV,self.param.LookTab_SOC)
                    delt_ocv_soc=ocv_soc2-ocv_soc1
                    delt_ocv_soc_weight=self._deltsoc_weight(abs(delt_ocv_soc))
                    soh_weight=soh_weight1*tempweightlist[i]*delt_ocv_soc_weight*0.5
                    cellsoh_init=ah_accum*100/((ocv_soc2-ocv_soc1)*0.01*self.param.Capacity)

                    if cellsoh_init>55 and cellsoh_init<120:    #判断soh值的有效区间
                        if len(df_res)<1:
                            if not self.df_soh.empty and 55<self.df_soh.loc[len(self.df_soh)-1,'soh']<120:
                                cellsoh_last=eval(self.df_soh.loc[len(self.df_soh)-1,'cellsoh'])
                                if soh_weight>1/abs(cellsoh_init-cellsoh_last[j]):
                                    soh_weight=1/abs(cellsoh_init-cellsoh_last[j])
                                    cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last[j]*(1-soh_weight)
                                else:
                                    cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last[j]*(1-soh_weight)
                            else:
                                cellsoh_cal=cellsoh_init*soh_weight+100*(1-soh_weight)
                        else:
                            cellsoh_last=eval(df_res.loc[len(df_res)-1,'cellsoh'])
                            if soh_weight>1/abs(cellsoh_init-cellsoh_last[j]):
                                soh_weight=1/abs(cellsoh_init-cellsoh_last[j])
                                cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last[j]*(1-soh_weight)
                            else:
                                cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last[j]*(1-soh_weight)
                        cellsoh_cal=eval(format(cellsoh_cal,'.1f'))
                        cellsoh.append(cellsoh_cal)
                    else:
                        cellsoh=[]
                        break
                if cellsoh and 55<min(cellsoh)<120:
                    soh=min(cellsoh)
                    soh_list=[timepoint_bms_st, timepoint_bms_sp, self.sn, 1, soh, str(cellsoh)]
                    df_res.loc[len(df_res)]=soh_list
                else:
                    continue
            if df_res.empty:
                return pd.DataFrame()
            else:
                return df_res
        return pd.DataFrame()

    #两点法和DVDQ法计算磷酸铁锂电池SOH..................................................................................................................
    def _lfpsoh(self):
        standingpoint_st=[]
        standingpoint_sp=[]
        tempweightlist1=[]
        cellmaxvolt_number1=[]
        standingtime=0
        chrg_start=[]
        chrg_end=[]
        tempweightlist2=[]
        cellmaxvolt_number2=[]
        charging=0

        for i in range(3,len(self.df_bms)-3):

            #获取两点法法所需数据-开始.................................................................................................................
            if abs(self.packcrnt[i]) < 0.1 and abs(self.packcrnt[i-1]) < 0.1 and abs(self.packcrnt[i+1]) < 0.1:     #判断非平台区静置状态
                delttime=(self.bmstime[i]-self.bmstime[i-1]).total_seconds()
                standingtime=standingtime+delttime
                self._celltemp_weight(i)     #获取不同温度对应的静置时间

                if standingtime>self.StandardStandingTime:  #静置时间满足要求
                    if abs(self.packcrnt[i+2])>=0.1:  #下一时刻电流>0.1A
                        standingtime=0
                        cellvolt_now=self._cellvolt_get(i)
                        if max(cellvolt_now)<self.param.OcvInflexionBelow:      #当前最大电芯电压<OCV下拐点
                            if standingpoint_st:
                                if len(standingpoint_st)>len(standingpoint_sp):
                                    if self.packcrnt[standingpoint_st[-1]]<-1:     #判断上一次静置点的是否为满充
                                        standingpoint_sp.append(i)
                                        standingpoint_st.append(i)
                                        tempweightlist1.append(self.tempweight)
                                    else:
                                        standingpoint_st[-1]=i
                                        tempweightlist1[-1]=self.tempweight
                                else:
                                    standingpoint_st.append(i)
                                    tempweightlist1.append(self.tempweight)
                            else:
                                standingpoint_st.append(i)
                                tempweightlist1.append(self.tempweight)
                        else:
                            pass
                    else:
                        pass
                else:
                    pass
            
            elif self.packcrnt[i]<=-1 and self.packcrnt[i-1]<=-1 and self.packcrnt[i+1]<=-1 and self.packcrnt[i+2]>-1:    #判读满充状态
                standingtime=0
                self._celltemp_weight(i)
                cellvolt_now=self._cellvolt_get(i)
                if max(cellvolt_now)>self.param.CellFullChrgVolt:
                    if standingpoint_st:
                        if len(standingpoint_st)>len(standingpoint_sp):
                            if abs(self.packcrnt[standingpoint_st[-1]])<0.5:     #判断上一次静置点是否为下拐点
                                standingpoint_sp.append(i)
                                standingpoint_st.append(i)
                                tempweightlist1.append(self.tempweight)
                                cellmaxvolt_number1.append(cellvolt_now.index(max(cellvolt_now))) #获取最大电压索引
                                cellmaxvolt_number1.append(cellvolt_now.index(max(cellvolt_now))) #获取最大电压索引
                            else:
                                standingpoint_st[-1]=i
                                tempweightlist1[-1]=self.tempweight
                                cellmaxvolt_number1[-1]=cellvolt_now.index(max(cellvolt_now))
                    else:
                        standingpoint_st.append(i)
                        tempweightlist1.append(self.tempweight)
                        cellmaxvolt_number1.append(cellvolt_now.index(max(cellvolt_now)))
                else:
                    pass
            
            else:
                standingtime=0
                pass

            #获取DVDQ算法所需数据——开始.............................................................................................................
            if charging==0:
                if self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and self.packcrnt[i+2]<=-1 and self.bms_soc[i]<40:     #充电开始
                    self._celltemp_weight(i)
                    charging=1
                    if len(chrg_start)>len(chrg_end):
                        chrg_start[-1]=i
                        tempweightlist2[-1]=self.tempweight
                    else:
                        chrg_start.append(i)
                        tempweightlist2.append(self.tempweight)
                else:
                    pass

            else: #充电中
                if (self.bmstime[i+1]-self.bmstime[i]).total_seconds()>180:  #如果充电过程中时间间隔>180s，则舍弃该次充电
                    chrg_start.remove(chrg_start[-1])
                    tempweightlist2.remove(tempweightlist2[-1])
                    charging=0
                    continue
                elif self.packcrnt[i]<=-1 and self.packcrnt[i+1]<=-1 and  self.packcrnt[i+2]>-1:  #判断电流波动时刻
                    cellvolt_now=self._cellvolt_get(i+1)
                    if max(cellvolt_now)>self.param.CellFullChrgVolt:   #电压>满充电压
                        chrg_end.append(i+1)
                        cellmaxvolt_number2.append(cellvolt_now.index(max(cellvolt_now)))   #获取最大电压索引
                        charging=0
                        continue
                    else:
                        pass
                elif self.packcrnt[i+1]>-0.1 and self.packcrnt[i+2]>-0.1:   #判断充电结束
                    charging=0
                    if len(chrg_start)>len(chrg_end):
                        chrg_start.remove(chrg_start[-1])
                        tempweightlist2.remove(tempweightlist2[-1])
                        continue
                    else:
                        continue
                elif i==len(self.packcrnt)-4 and self.packcrnt[i+1]<-1 and self.packcrnt[i+2]<-1:
                    charging=0
                    if len(chrg_start)>len(chrg_end):
                        cellvolt_now=self._cellvolt_get(i)
                        if max(cellvolt_now)>self.param.CellFullChrgVolt:   #电压>满充电压
                            chrg_end.append(i)
                            cellmaxvolt_number2.append(cellvolt_now.index(max(cellvolt_now)))   #获取最大电压索引
                            continue
                        else:
                            chrg_start.remove(chrg_start[-1])
                            tempweightlist2.remove(tempweightlist2[-1])
                            continue
                    else:
                        continue  
                else:
                    continue 
        
        #开始计算SOH.............................................................................................................................................
        if standingpoint_sp or chrg_end:       
            column_name=['time_st','time_sp','sn','method','soh','cellsoh','weight']
            df_res=pd.DataFrame(columns=column_name)
        
            #两点法计算SOH........................................................................................................................................
            if standingpoint_sp:    
                for i in range(len(standingpoint_sp)):  #判断为满充点或者下拐点
                    if self.packcrnt[standingpoint_sp[i]]<=-1:
                        cellocv_st=self._cellvolt_get(standingpoint_st[i])    
                        ocv_soc1=np.interp(cellocv_st[cellmaxvolt_number1[i]],self.param.LookTab_OCV,self.param.LookTab_SOC)
                        ocv_soc2=self.param.FullChrgSoc
                    else:
                        cellocv_sp=self._cellvolt_get(standingpoint_sp[i])
                        ocv_soc1=self.param.FullChrgSoc
                        ocv_soc2=np.interp(cellocv_sp[cellmaxvolt_number1[i]],self.param.LookTab_OCV,self.param.LookTab_SOC)

                    cellocv_sp=self._cellvolt_get(standingpoint_sp[i])
                    accumtime=self.accumtime.to_list()  #累计量的时间列表
                    timepoint_bms_st=self.bmstime[standingpoint_st[i]]   #获取静置点的时间
                    timepoint_bms_sp=self.bmstime[standingpoint_sp[i]]
                    timepoint_accum_st=bisect.bisect(accumtime,timepoint_bms_st)   #获取最接近静置点时间的累计量时间点
                    timepoint_accum_sp=bisect.bisect(accumtime,timepoint_bms_sp)
                    if timepoint_accum_sp>=len(accumtime):  #防止指针超出数据范围
                        timepoint_accum_sp=len(accumtime)-1
                    
                    ah_packcrnt_dis=0
                    ah_packcrnt_chg=0
                    for j in range(standingpoint_st[i]+2,standingpoint_sp[i]+1): #计算累计Ah
                        Step=(self.bmstime[j+1]-self.bmstime[j]).total_seconds()
                        if self.packcrnt[j+1]>=0:
                            ah_packcrnt_dis=ah_packcrnt_dis+self.packcrnt[j+1]*Step
                        else:
                            ah_packcrnt_chg=ah_packcrnt_chg-self.packcrnt[j+1]*Step
                    ah_packcrnt_chg=ah_packcrnt_chg/3600
                    ah_packcrnt_dis=ah_packcrnt_dis/3600          
                    ah_packcrnt=ah_packcrnt_chg-ah_packcrnt_dis     #两个静置点的总累计AH，负值代表放电，正值代表充电
        
                    ah_accum_dis=self.df_accum.loc[timepoint_accum_sp,'累计放电电量']-self.df_accum.loc[timepoint_accum_st,'累计放电电量']  #两个静置点之间的放电电量
                    ah_accum_chg=self.df_accum.loc[timepoint_accum_sp,'累计充电电量']-self.df_accum.loc[timepoint_accum_st,'累计充电电量']  #两个静置点之间的充电电量
                    ah_accum_tatol=ah_accum_chg-ah_accum_dis  #两个静置点的总累计AH，负值代表放电，正值代表充电
                    ah_accum=ah_accum_tatol

                    delt_days=(self.bmstime[standingpoint_sp[i]]-self.bmstime[standingpoint_st[i]]).total_seconds()/(3600*24)
                    if delt_days<=1: #两次时间间隔对计算结果的影响
                        soh_weight=1
                    elif delt_days<=2:
                        soh_weight=0.7
                    elif delt_days<=3:
                        soh_weight=0.4
                    else:
                        soh_weight=0
                    
                    if self.param.Capacity*0.65*0.7 < abs(ah_packcrnt) < self.param.Capacity:    #累计量的权重
                        if abs(ah_accum_tatol-ah_packcrnt)<self.param.Capacity/20:
                            soh_weight=soh_weight*1
                        elif abs(ah_accum_tatol-ah_packcrnt)<self.param.Capacity/10:
                            soh_weight=soh_weight*0.8
                        else:
                            soh_weight=soh_weight*0.5
                    else:
                        if self.param.Capacity*0.65*0.7 < abs(ah_accum) < self.param.Capacity:
                            soh_weight=soh_weight*0.3
                        else:
                            soh_weight=0

                    delt_ocv_soc=ocv_soc2-ocv_soc1
                    delt_ocv_soc_weight=self._deltsoc_weight(abs(delt_ocv_soc))
                    soh_weight=soh_weight*tempweightlist1[i]*delt_ocv_soc_weight*0.5
                    cellsoh_init=ah_accum*100/((ocv_soc2-ocv_soc1)*0.01*self.param.Capacity)

                    if cellsoh_init>60 and cellsoh_init<115:    #判断soh值的有效区间
                        soh_list=[timepoint_bms_st, timepoint_bms_sp, self.sn, 1, cellsoh_init, cellsoh_init,soh_weight]
                        df_res.loc[len(df_res)]=soh_list
                    else:
                        pass
            else:
                pass

            #DVDQ法计算SOH.......................................................................................................................................
            if chrg_end:
                for i in range(len(chrg_end)):
                    cellvolt_max = self.df_bms['单体电压' + str(cellmaxvolt_number2[i]+1)] / 1000     #获取最大电压
                    cellvolt=self._np_move_avg(cellvolt_max, 3, mode="same")     #对电压进行滑动平均滤

                    cellsoh_init=self._dvdq_soh(chrg_start[i],chrg_end[i],cellvolt)     #dvdq计算soh

                    soh_weight=tempweightlist2[i]*0.25
                    if cellsoh_init>60 and cellsoh_init<115:    #判断soh值的有效区间
                        soh_list=[self.bmstime[chrg_start[i]], self.bmstime[chrg_end[i]], self.sn, 2, cellsoh_init, str(cellsoh_init),soh_weight]
                        df_res.loc[len(df_res)]=soh_list
                    else:
                        pass
            
            #对SOH结果进行滤波处理................................................................................................................................
            if df_res.empty:
                return pd.DataFrame()
            else:
                df_res=df_res.sort_values(by='time_st',ascending=True)
                df_res.index=range(len(df_res))
                for i in range(len(df_res)):
                    cellsoh_init=df_res.loc[i,'soh']
                    soh_weight=df_res.loc[i,'weight']
                    if i<1:
                        if not self.df_soh.empty and 60<self.df_soh.loc[len(self.df_soh)-1,'soh']<115:
                            cellsoh_last=self.df_soh.loc[len(self.df_soh)-1,'soh']
                            if soh_weight>1/abs(cellsoh_init-cellsoh_last):
                                soh_weight=1/abs(cellsoh_init-cellsoh_last)
                                cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
                            else:
                                cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
                        else:
                            cellsoh_cal=cellsoh_init*soh_weight+100*(1-soh_weight)
                        cellsoh_cal=eval(format(cellsoh_cal,'.1f'))
                        if 60<cellsoh_cal<115:
                            df_res.loc[i,'soh']=cellsoh_cal
                            df_res.loc[i,'cellsoh']=str(cellsoh_cal)
                        else:
                            df_res=df_res.drop(i,axis=0)
                    else:
                        cellsoh_last=df_res.loc[i-1,'soh']
                        if soh_weight>1/abs(cellsoh_init-cellsoh_last):
                            soh_weight=1/abs(cellsoh_init-cellsoh_last)
                            cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
                        else:
                            cellsoh_cal=cellsoh_init*soh_weight + cellsoh_last*(1-soh_weight)
                        cellsoh_cal=eval(format(cellsoh_cal,'.1f'))
                        if 60<cellsoh_cal<115:
                            df_res.loc[i,'soh']=cellsoh_cal
                            df_res.loc[i,'cellsoh']=str(cellsoh_cal)
                        else:
                            df_res=df_res.drop(i,axis=0)
                df_res=df_res.drop('weight',axis=1)
                return df_res

        return pd.DataFrame()


