lmstack 2 éve
szülő
commit
6515bfe68e

+ 478 - 0
DataPreProcessMGMC.py

@@ -0,0 +1,478 @@
+from os import defpath
+import pandas as pd
+import numpy as np
+import pdb
+from numba import jit
+from LIB.BACKEND import Tools
+
+class DataPreProcess:
+    def __init__(self):
+        self.tools = Tools.Tools()
+        pass
+
+    # def data_split(self, dfin, drive_interval_threshold=120, charge_interval_threshold=300, 
+    #                            drive_stand_threshold=120, charge_stand_threshold=300,
+    #                            default_time_threshold = 300, drive_time_threshold=300, charge_time_threshold=300, 
+    #                            stand_time_threshold = 1800):
+    #     '''
+    #     数据分段函数,会调用_data_split_by_status和_data_split_by_time函数。
+    #     其中_data_split_by_status 将数据分为charge、drive、stand、和none段;
+    #     _data_split_by_time 将每个段内的数据,根据时间跳变继续分段。
+
+    #     '''
+    def time_filter(self, df_bms, df_gps):
+        df_bms.drop_duplicates(subset=['时间戳'], keep='first', inplace=True)
+        df_gps.drop_duplicates(subset=['时间戳'], keep='first', inplace=True)
+        df_bms = df_bms.reset_index(drop=True)
+        df_gps = df_gps.reset_index(drop=True)
+        return df_bms, df_gps
+
+    def data_split_by_status(self, dfin, drive_interval_threshold=120, charge_interval_threshold=300, 
+                                    drive_stand_threshold=120, charge_stand_threshold=300):
+        '''
+        # 数据预处理分段, 将原始数据段分为 charge、drive、stand、none段
+        # 状态判断
+        # 1、drive:(状态为2或3 且 存在电流>0 ) 或 (电流持续为0 且 持续时间<阈值 且 上一段数据为行车)
+        # 2、charge:(状态为2或3 且 不存在电流>0 ) 或 (电流持续为0 且 持续时间<阈值 且 上一段数据为充电)
+        # 3、stand:(电流持续为0 且 是数据段的第一段) 或 (电流持续为0 且 持续时间>阈值)
+        # 4、none: 其他
+
+        --------------输入参数-------------:
+        drive_interval_threshold: 行车段拼接阈值,如果两段行车的间隔时间小于该值,则两段行车合并。
+        charge_interval_threshold: 充电段拼接阈值,如果两段充电的间隔时间小于该值,则两段充电合并。
+        drive_stand_threshold: 静置段合并至行车段阈值,如果静置时间小于该值,则合并到上一段的行车中。
+        charge_stand_threshold: 静置段合并至充电段阈值,如果静置时间小于该值,则合并到上一段的充电中。
+
+        --------------输出-----------------:
+        在原始数据后面,增加data_split_by_crnt, data_split_by_status, data_status 三列
+        data_split_by_crnt: 按电流分段的序号
+        data_split_by_status:按电流和状态分段的序号
+        data_status: 状态标识
+        '''
+        # 首先根据电流是否为0 ,将数据分段
+        df = dfin.copy()
+        df['时间戳'] = pd.to_datetime(df['时间戳'])
+        
+        crnt_zero_or_not = df['总电流[A]']==0
+        last_crnt_flag = crnt_zero_or_not[0]
+        temp = 1
+        group_id = [temp]
+        for cur_crnt_flag in crnt_zero_or_not[1:]:
+            if last_crnt_flag ^ cur_crnt_flag:
+                temp = temp + 1
+            last_crnt_flag = cur_crnt_flag
+            group_id.append(temp)
+        df['data_split_by_crnt'] = group_id
+
+        # 然后判断每个段内的 充电状态及电流=0持续时长,决定当前状态
+        temp = 1
+        last_status = ""
+        status_id = []
+        status_list = []
+        data_number_list = sorted(list(set(df['data_split_by_crnt'])))
+
+        for data_number in data_number_list:
+            df_sel = df[df['data_split_by_crnt'] == data_number]
+            origin_index = list(df_sel.index)
+            df_sel = df_sel.reset_index(drop=True)
+            temp_2 = 0
+            # 如果当前数据段的电流非0,则可能分为charge、drive或none段
+            if df_sel.loc[0,'总电流[A]'] != 0:
+                # 电流 分段中可能存在状态变化的时刻, 内部根据状态进行分段.
+                # 该数据段内部,根据bms状态信号进行二次分段
+                status_drive_or_not = df_sel['充电状态']==3
+                last_status_flag = status_drive_or_not[0]
+                temp_2 = 0
+                group_id_2 = [temp_2]
+                for cur_status_flag in status_drive_or_not[1:]:
+                    if last_status_flag ^ cur_status_flag:
+                        temp_2 = temp_2 + 1
+                    last_status_flag = cur_status_flag
+                    group_id_2.append(temp_2)
+                
+                # 遍历二次状态分段
+                temp_2 = 0
+                last_status_2 = last_status
+                df_sel['index'] = group_id_2
+                data_number_list_2 = sorted(list(set(group_id_2)))
+                for data_number_2 in data_number_list_2:
+                    
+                    df_sel_2 = df_sel[df_sel['index'] == data_number_2]
+                    df_sel_2 = df_sel_2.reset_index(drop=True)
+                    
+                    # 根据bms状态 及 电流符号决定是charge还是drive
+                    # 如果状态为2或3, 且电流均>=0 则记为充电
+                    if df_sel_2.loc[0, '充电状态'] in [2, 3] and len(df_sel_2[df_sel_2['总电流[A]'] < 0]) == 0: 
+                        cur_status = 'charge'
+                    # 如果状态为2或3,且存在电流<0 则记为行车
+                    elif df_sel_2.loc[0, '充电状态'] in [2, 3] and len(df_sel_2[df_sel_2['总电流[A]'] < 0]) > 0: 
+                        cur_status = 'drive'
+                    # 否则 记为none
+                    else:
+                        cur_status = 'none'
+                    status_list.extend([cur_status] * len(df_sel_2))
+                    
+                    # 状态id号与前面电流为0的相同状态进行合并, 均判断应不应该与上一段合并    
+                    if origin_index[0] == 0: # 如果是所有数据的起始段数据,则直接赋值id号
+                        status_id.extend([temp + temp_2]*len(df_sel_2))
+                    
+                    else: # 判断是否与上一段数据合并
+                        deltaT = (df.loc[origin_index[0], '时间戳'] - df.loc[origin_index[0]-1, '时间戳']).total_seconds()
+                        # 如果 状态一致, 且 间隔时间小于阈值,则合并
+                        if last_status_2 == 'drive' and cur_status == last_status_2 and deltaT < drive_interval_threshold: 
+                            temp_2 = temp_2 - 1  
+                            status_id.extend([temp + temp_2]*len(df_sel_2)) 
+                        # 如果状态一致, 且 间隔时间小于阈值,则合并
+                        elif last_status_2 == 'charge' and cur_status == last_status_2 and deltaT < charge_interval_threshold: 
+                            temp_2 = temp_2 - 1  
+                            status_id.extend([temp + temp_2]*len(df_sel_2)) 
+                        else:
+                            status_id.extend([temp + temp_2]*len(df_sel_2))
+                    temp_2 = temp_2 + 1
+                    last_status_2 = status_list[-1]
+                temp_2 = temp_2 - 1
+            else:
+                # 如果当前数据段的电流为0,则可能分为stand,charge、drive或none段
+                if origin_index[0] == 0: # 如果是数据的起始,则无论长短,都认为是stand
+                    status_id.extend([temp]*len(df_sel))
+                    status_list.extend(['stand'] * len(df_sel))
+                else: # 不是数据的起始
+                    cur_deltaT = (df.loc[origin_index[-1], '时间戳'] - df.loc[origin_index[0], '时间戳']).total_seconds()
+                    if last_status == 'charge': # 如果上一个状态为充电
+                        if cur_deltaT < charge_stand_threshold: # 如果本次电流为0的持续时间小于 阈值,则合并
+                            status_list.extend(['charge'] * len(df_sel))
+                            temp = temp - 1  
+                            status_id.extend([temp]*len(df_sel))
+                        else: # 否则超过了阈值,记为stand
+                            status_id.extend([temp]*len(df_sel))
+                            status_list.extend(['stand'] * len(df_sel))
+                    elif last_status == 'drive': # 如果上一个状态为行车
+                        if cur_deltaT < drive_stand_threshold: # 如果本次电流为0的持续时间小于 阈值,则合并
+                            status_list.extend(['drive'] * len(df_sel))
+                            temp = temp - 1  
+                            status_id.extend([temp]*len(df_sel))
+                        else: # 否则超过了阈值,记为stand
+                            status_id.extend([temp]*len(df_sel))
+                            status_list.extend(['stand'] * len(df_sel))
+                    elif last_status == 'none': # 如果上一个状态未知
+                        status_id.extend([temp] * len(df_sel))
+                        status_list.extend(['stand'] * len(df_sel))
+            temp = temp + temp_2 + 1
+            last_status = status_list[-1] # 上一组状态
+        df['data_split_by_status'] = status_id
+        df['data_status'] = status_list
+        return df
+    def data_split_by_time(self, dfin, default_time_threshold = 300, drive_time_threshold=300, charge_time_threshold=300, 
+                                        stand_time_threshold = 1800):
+        '''
+        # 该函数用来解决数据丢失问题导致的分段序号异常,
+        # 将经过data_split_by_status分段后的数据,每个段内两行数据的时间跳变如果超过阈值,则继续分为两段
+
+        --------------输入参数-------------:
+        dfin:  调用data_split_by_status之后的函数
+        default_time_threshold: 默认时间阈值,如果状态内部时间跳变大于该值,则划分为两段
+        drive_time_threshold: 行车时间阈值,如果行车状态内部时间跳变大于该值,则划分为两段
+        charge_time_threshold: 充电时间阈值,如果充电状态内部时间跳变大于该值,则划分为两段
+        stand_time_threshold:静置时间阈值,如果静置状态内部时间跳变大于该值,则划分为两段
+
+        --------------输出-----------------:
+        在输入数据后面,增加data_split_by_status_time 一列
+        data_split_by_status_time: 按照状态和时间分段后的序号
+        '''  
+        data_id = []
+        temp = 1
+        data_number_list = sorted(list(set(dfin['data_split_by_status'])))
+        for data_number in data_number_list:
+            # if data_number == 1203:
+            #     pdb.set_trace()
+            status = list(dfin[dfin['data_split_by_status']==data_number]['data_status'])[0]
+            cur_indexes = dfin[dfin['data_split_by_status']==data_number].index
+            
+            time_array = np.array(dfin[dfin['data_split_by_status']==data_number]['时间戳'])
+            time_diff = np.diff(time_array)
+            time_diff = time_diff.astype(np.int64)
+            time_interval = default_time_threshold
+            if status == 'drive':
+                time_interval = drive_time_threshold
+            elif status == 'charge':
+                time_interval = charge_time_threshold
+            elif status == 'stand':
+                time_interval = stand_time_threshold
+            time_diff_index = (np.argwhere(((time_diff/1e9) > time_interval)==True))[:,0]
+            time_diff_origin_index = cur_indexes[time_diff_index]+1
+            if len(time_diff_index) == 0:
+                data_id.extend([temp] * len(cur_indexes))
+                temp += 1
+            else:
+                last_index = cur_indexes[0]
+                for index, cur_index in enumerate(time_diff_origin_index):
+                    if index == len(time_diff_origin_index)-1: # 如果是最后一个index,则
+                        data_id.extend([temp]* (cur_index-last_index))
+                        last_index = cur_index
+                        temp += 1
+                        data_id.extend([temp]* (cur_indexes[-1]-last_index+1))
+                    else:
+                        data_id.extend([temp]* (cur_index-last_index))
+                    last_index = cur_index
+                    temp += 1
+        dfin['data_split_by_status_time'] = data_id
+        return dfin
+    def combine_drive_stand(self, dfin):
+        '''
+        合并放电和静置段:将两次充电之间的所有数据段合并为一段, 状态分为 charge 和not charge
+        ---------------输入----------
+        dfin: 调用data_split_by_status()后输出的bms数据
+
+        ---------------输出----------
+        在输入数据后面,增加data_split_by_status_after_combine, data_status_after_combine 两列
+        data_split_by_status_after_combine: 将两次充电间的数据合并后的段序号
+        data_status_after_combine: 每段数据的状态标识
+        '''
+        df = dfin.copy()
+        data_split_by_status_1 = []
+        data_status_1 = []
+        number = 1
+        first_flag = True
+        data_number_list = sorted(list(set(df['data_split_by_status_time'])))
+        for data_number in data_number_list:
+            status = list(df[df['data_split_by_status_time']==data_number]['data_status'])
+            cur_status = status[0]
+            if first_flag:
+                first_flag = False
+            elif (last_status not in ['charge'] and cur_status in ['charge']) or (last_status in ['charge'] and cur_status not in ['charge']):
+                number += 1
+
+            data_split_by_status_1.extend([number]*len(status))
+            if cur_status in ['charge']:
+                data_status_1.extend(['charge']*len(status))
+            else:
+                data_status_1.extend(['not charge']*len(status))
+
+            last_status = cur_status
+        df['data_split_by_status_after_combine'] = data_split_by_status_1
+        df['data_status_after_combine'] = data_status_1
+
+        return df
+
+    def cal_stand_time(self, dfin):
+        '''
+        # 计算静置时间
+        # 将每次行车或充电的前后静置时间,赋值给stand_time 列, 单位为分钟
+
+        ----------------输入参数---------
+        dfin: 调用data_split_by_status()后输出的bms数据
+
+        ----------------输出参数----------
+        在输入数据后面,增加stand_time列
+        stand_time : 在行车段或充电段的起止两个位置处,表明开始前和结束后的静置时长,单位为分钟
+
+        '''
+        df = dfin.copy()
+        stand_time = []
+        first_flag = True
+        data_number_list = sorted(list(set(df['data_split_by_status_time'])))
+        for index, data_number in enumerate(data_number_list):
+            status = list(df[df['data_split_by_status_time']==data_number]['data_status'])
+            time =  list(df[df['data_split_by_status_time']==data_number]['时间戳'])
+            cur_status = status[0]
+            cur_delta_time = (time[-1]-time[0]).total_seconds() / 60.0 # 分钟
+            if len(status) >= 2:
+                if first_flag:
+                    first_flag = False
+                    if index < len(data_number_list)-1:
+                        if cur_status in ['charge', 'drive']:
+                            next_status = list(df[df['data_split_by_status_time']==data_number_list[index+1]]['data_status'])[0]
+                            stand_time.extend([None]*(len(status)-1))
+                            if next_status == 'stand':
+                                next_time =  list(df[df['data_split_by_status_time']==data_number_list[index+1]]['时间戳'])
+                                stand_time.extend([(next_time[-1]-next_time[0]).total_seconds() / 60.0])
+                            else:
+                                stand_time.extend([0])
+                        else:
+                            stand_time.extend([None]*len(status))
+                    else:
+                        stand_time.extend([None]*len(status))      
+                else:
+                    if cur_status in ['charge', 'drive']:
+                        if last_status == 'stand':
+                            stand_time.extend([last_delta_time])
+                        else:
+                            stand_time.extend([0])
+                        stand_time.extend([None]*(len(status)-2))
+                        if index < len(data_number_list)-1:
+                            next_status = list(df[df['data_split_by_status_time']==data_number_list[index+1]]['data_status'])[0]
+                            if next_status == 'stand':
+                                next_time =  list(df[df['data_split_by_status_time']==data_number_list[index+1]]['时间戳'])
+                                stand_time.extend([(next_time[-1]-next_time[0]).total_seconds() / 60.0])
+                            else:
+                                stand_time.extend([0])
+                        else:
+                            stand_time.extend([None])
+                    else:
+                        stand_time.extend([None]*len(status))
+                        
+            else:
+                stand_time.extend([None])
+            last_status = cur_status
+            last_delta_time = cur_delta_time
+        df['stand_time'] = stand_time
+        return df
+
+    # 输入GPS数据,返回本段数据的累积里程,及平均时速(如果两点之间)
+    @jit
+    def _cal_odo_speed(self, lat_list, long_list, time_list):
+        '''
+        输入:经度列表, 纬度列表, 时间列表;
+        输出:每两个经纬度坐标之间的距离,以及速度 的数组
+        '''
+        dis_array = []
+        speed_array = []
+ 
+        for i in range(len(lat_list)-1):
+            dis = self.tools.cal_distance(lat_list[i],long_list[i], lat_list[i+1],long_list[i+1])
+            dis_array.append(dis)
+            deltaT = abs(time_list[i] - time_list[i+1]).total_seconds()
+            speed_array.append(dis * 3600.0/deltaT)
+        return np.array(dis_array), np.array(speed_array)
+
+    def gps_data_judge(self, df_bms, df_gps, time_diff_thre=300, odo_sum_thre=200, drive_spd_thre=80, parking_spd_thre=2):
+        '''
+        GPS数据可靠性判断函数(基于combine前的分段)
+
+        GPS数据出现以下情况时,判定为不可靠:
+        1)如果该段对应的地理位置数据 少于2 个,则认为不可靠
+        2)如果截取的GPS数据的起止时间,与BMS数据段的起止时间相差超过阈值,则认为不可靠
+        3)如果行车段 累积里程超过阈值,车速超过阈值
+        4) 如果非行车段 车速超过阈值
+
+        --------------输入参数--------------:
+        time_diff_thre: 时间差阈值
+        odo_sum_thre: 累积里程阈值
+        drive_spd_thre: 行车车速阈值
+        parking_spd_thre: 非行车状态车速阈值
+
+        --------------输出参数--------------:
+        df_bms 增加一列gps_rely, 表明对应的GPS数据是否可靠。
+                1:可靠
+                <0: 表示不可靠的原因
+        df_gps 增加两列odo, speed, 分别表示前后两点间的距离和速度
+        '''
+        df_gps['时间戳'] = pd.to_datetime(df_gps['时间戳'])
+        res_record = {'drive':0, 'charge':0, 'stand':0, 'none':0, 'total':0}
+        rely_list = []
+        df_gps['odo'] = [None] * len(df_gps)
+        df_gps['speed'] = [None] * len(df_gps)
+        data_number_list = sorted(list(set(df_bms['data_split_by_status_time'])))
+        for data_number in data_number_list[:]:
+            df_sel = df_bms[df_bms['data_split_by_status_time'] == data_number]
+            df_sel = df_sel.reset_index(drop=True)
+            df_sel_gps = df_gps[(df_gps['时间戳']>=df_sel.loc[0,'时间戳']) & (df_gps['时间戳']<=df_sel.loc[len(df_sel)-1,'时间戳'])]
+            origin_index = list(df_sel_gps.index)
+            df_sel_gps = df_sel_gps.reset_index(drop=True)
+            # 如果当前段数据对应的地理位置数据少于2个
+            if len(df_sel_gps) <= 1:
+                rely_list.extend([-1]*len(df_sel))
+                res_record[str(df_sel.loc[0, 'data_status'])] = res_record[str(df_sel.loc[0, 'data_status'])] + 1
+                continue
+            # 如果GPS 起止时间段和BMS数据相差超过阈值
+            if abs(df_sel_gps.loc[0, '时间戳'] - df_sel.loc[0,'时间戳']).total_seconds() > time_diff_thre or \
+               abs(df_sel_gps.loc[len(df_sel_gps)-1, '时间戳'] - df_sel.loc[len(df_sel)-1,'时间戳']).total_seconds() > time_diff_thre:
+                rely_list.extend([-2]*len(df_sel))
+                res_record[str(df_sel.loc[0, 'data_status'])] = res_record[str(df_sel.loc[0, 'data_status'])] + 1
+                continue
+
+            # 计算该段数据每两点之间的里程以及速度
+            dis_array, speed_array = self._cal_odo_speed(df_sel_gps['纬度'], df_sel_gps['经度'], df_sel_gps['时间戳'])
+            # 如果 累积里程异常 或 平均车速异常 或两点间车速异常
+            avg_speed = np.sum(dis_array) *3600.0 / abs(df_sel_gps.loc[0, '时间戳'] - df_sel_gps.loc[len(df_sel_gps)-1, '时间戳']).total_seconds()
+            if np.sum(dis_array) > odo_sum_thre or avg_speed > drive_spd_thre or (speed_array > drive_spd_thre).any():
+                rely_list.extend([-3]*len(df_sel))
+                res_record[str(df_sel.loc[0, 'data_status'])] = res_record[str(df_sel.loc[0, 'data_status'])] + 1
+                continue
+            
+            # 如果停车,且 平均时速超过阈值,则不可靠
+            if (str(df_sel.loc[0, 'data_status']) == 'charge' or str(df_sel.loc[0, 'data_status']) == 'stand') and avg_speed > parking_spd_thre :
+                rely_list.extend([-4]*len(df_sel))
+                res_record[str(df_sel.loc[0, 'data_status'])] = res_record[str(df_sel.loc[0, 'data_status'])] + 1
+                continue
+            # 剩下的记录为可靠
+            rely_list.extend([1]*len(df_sel))
+            df_gps.loc[origin_index[1:], 'odo'] = dis_array
+            df_gps.loc[origin_index[1:], 'speed'] = speed_array
+        df_bms['gps_rely'] = rely_list
+        res_record['total'] = (res_record['drive'] + res_record['charge'] + res_record['stand'] + res_record['none'] )/df_bms['data_split_by_status_time'].max()
+        if len(set(df_bms[df_bms['data_status']=='drive']['data_split_by_status_time'])) > 0:
+            res_record['drive'] = (res_record['drive'])/len(set(df_bms[df_bms['data_status']=='drive']['data_split_by_status_time']))
+        if len(set(df_bms[df_bms['data_status']=='charge']['data_split_by_status_time'])) > 0:
+            res_record['charge'] = (res_record['charge'])/len(set(df_bms[df_bms['data_status']=='charge']['data_split_by_status_time']))
+        if len(set(df_bms[df_bms['data_status']=='stand']['data_split_by_status_time'])) > 0:
+            res_record['stand'] = (res_record['stand'])/len(set(df_bms[df_bms['data_status']=='stand']['data_split_by_status_time']))
+        if len(set(df_bms[df_bms['data_status']=='none']['data_split_by_status_time'])) > 0:
+            res_record['none'] = (res_record['none'])/len(set(df_bms[df_bms['data_status']=='none']['data_split_by_status_time']))
+        return df_bms, df_gps, res_record
+
+
+    def data_gps_judge_after_combine(self, df_bms, df_gps, time_diff_thre=600, odo_sum_thre=200, drive_spd_thre=80, parking_spd_thre=2):
+        '''
+        GPS数据可靠性判断函数2 (基于combine后的分段) 判别方式同data_gps_judge
+        '''
+        df_gps['时间戳'] = pd.to_datetime(df_gps['时间戳'])
+        res_record = {'not charge':0, 'charge':0, 'total':0} # 不可靠的比例
+
+        rely_list = []
+        df_gps['odo_after_combine'] = [None] * len(df_gps)
+        df_gps['speed_after_combine'] = [None] * len(df_gps)
+ 
+        data_number_list = sorted(list(set(df_bms['data_split_by_status_after_combine'])))
+        for data_number in data_number_list[:]:
+            df_sel = df_bms[df_bms['data_split_by_status_after_combine'] == data_number]
+            df_sel = df_sel.reset_index(drop=True)
+
+            # 尝试采用drive段的开始和结束时间选择GPS数据,因为stand时GPS数据可能存在丢失,影响里程的计算
+            df_sel_drive = df_sel[df_sel['data_status']=='drive'] # 
+            df_sel_drive = df_sel_drive.reset_index(drop=True)
+            if df_sel_drive.empty:
+                df_sel_1 = df_sel
+            else:
+                df_sel_1 = df_sel_drive
+            df_sel_gps = df_gps[(df_gps['时间戳']>=df_sel_1.loc[0,'时间戳']) & (df_gps['时间戳']<=df_sel_1.loc[len(df_sel_1)-1,'时间戳'])]
+            origin_index = list(df_sel_gps.index)
+            df_sel_gps = df_sel_gps.reset_index(drop=True)
+            # 如果当前段数据对应的地理位置数据少于2个
+            if len(df_sel_gps) <= 1:
+                rely_list.extend([-1]*len(df_sel))
+                res_record[str(df_sel.loc[0, 'data_status_after_combine'])] = res_record[str(df_sel.loc[0, 'data_status_after_combine'])] + 1
+                continue
+            # 如果GPS 起止时间段和BMS数据相差超过阈值
+            if abs(df_sel_gps.loc[0, '时间戳'] - df_sel_1.loc[0,'时间戳']).total_seconds() > time_diff_thre or \
+                abs(df_sel_gps.loc[len(df_sel_gps)-1, '时间戳'] - df_sel_1.loc[len(df_sel_1)-1,'时间戳']).total_seconds() > time_diff_thre:
+                rely_list.extend([-2]*len(df_sel))
+                res_record[str(df_sel.loc[0, 'data_status_after_combine'])] = res_record[str(df_sel.loc[0, 'data_status_after_combine'])] + 1
+                continue
+
+            # 计算该段数据每两点之间的里程以及速度
+            dis_array, speed_array = self._cal_odo_speed(df_sel_gps['纬度'], df_sel_gps['经度'], df_sel_gps['时间戳'])
+            # 如果 累积里程异常 或 平均车速异常 或两点间车速异常
+            avg_speed = np.sum(dis_array) *3600.0 / abs(df_sel_gps.loc[0, '时间戳'] - df_sel_gps.loc[len(df_sel_gps)-1, '时间戳']).total_seconds()
+            if np.sum(dis_array) > odo_sum_thre or avg_speed > drive_spd_thre or (speed_array > drive_spd_thre).any():
+                rely_list.extend([-3]*len(df_sel))
+                res_record[str(df_sel.loc[0, 'data_status_after_combine'])] = res_record[str(df_sel.loc[0, 'data_status_after_combine'])] + 1
+                continue
+            
+            # 如果充电,且 平均时速超过阈值,则不可靠
+            if str(df_sel.loc[0, 'data_status_after_combine']) == 'charge' and avg_speed > parking_spd_thre:
+                rely_list.extend([-4]*len(df_sel))
+                res_record[str(df_sel.loc[0, 'data_status_after_combine'])] = res_record[str(df_sel.loc[0, 'data_status_after_combine'])] + 1
+                continue
+            # 剩下的记录为可靠
+            rely_list.extend([1]*len(df_sel))
+            df_gps.loc[origin_index[1:], 'odo_after_combine'] = dis_array
+            df_gps.loc[origin_index[1:], 'speed_after_combine'] = speed_array
+        df_bms['gps_rely_after_combine'] = rely_list
+        res_record['total'] = (res_record['not charge'] + res_record['charge'])/df_bms['data_split_by_status_after_combine'].max()
+        if len(set(df_bms[df_bms['data_status_after_combine']=='not charge']['data_split_by_status_after_combine'])) > 0:
+            res_record['not charge'] = (res_record['not charge'])/len(set(df_bms[df_bms['data_status_after_combine']=='not charge']['data_split_by_status_after_combine']))
+        if len(set(df_bms[df_bms['data_status_after_combine']=='charge']['data_split_by_status_after_combine'])) > 0 :
+            res_record['charge'] = (res_record['charge'])/len(set(df_bms[df_bms['data_status_after_combine']=='charge']['data_split_by_status_after_combine']))
+        return df_bms, df_gps, res_record
+        

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 33 - 0
LIB/BACKEND/OPENAPI/main.ipynb


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

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

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

@@ -0,0 +1,177 @@
+import CBMSBatDiag
+from SC_SamplingSafty import SamplingSafty
+from DataStatistics import DataSta
+from DiagDataMerge import DiagDataMerge
+from SC_CtrlSafty import CtrlSafty
+from SC_BMSUploadError import BMSReportError
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+from sqlalchemy import create_engine
+import time, datetime
+from apscheduler.schedulers.blocking import BlockingScheduler
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import DBDownload as DBDownload
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import log
+from pandas.core.frame import DataFrame
+import datacompy
+from LIB.MIDDLE.CellStateEstimation.Common.V1_0_1 import BatParam as QX_BatteryParam
+from LIB.MIDDLE.SaftyCenter.Common import DBDownload as DBDw
+
+#...................................电池包电芯安全诊断函数......................................................................................................................
+def diag_cal():
+    task_on=1
+    global SNnums
+    global start
+    #..................................设置时间..........................................................  
+    start=time.time()
+    end_time=datetime.datetime.now()
+    start_time=end_time-datetime.timedelta(seconds=900)#10分钟跑一次,一次取10分钟数据
+    start_time=start_time.strftime('%Y-%m-%d %H:%M:%S')
+    end_time=end_time.strftime('%Y-%m-%d %H:%M:%S')
+
+
+    
+    host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port=3306
+    db='safety_platform'
+    user='qx_algo_readonly'
+    password='qx@123456'
+    mode=2
+    tablename2='all_fault_info_copy'
+    DBRead = DBDw.DBDownload(host, port, db, user, password,mode)
+    with DBRead as DBRead:
+        df_Diag_Ram = DBRead.getdata('start_time','end_time','product_id','code','level','info','advice','Batpos',tablename=tablename2,factory='',sn='',timename='',st='',sp='')
+    df_Diag_Ram=df_Diag_Ram.dropna(axis=1,how='any')
+    df_Diag_Ram=df_Diag_Ram.reset_index(drop=True)
+    
+    
+    for sn in SNnums:#SN遍历
+        print(sn)
+        
+        start_time = '2021-12-30 18:00:04'
+        end_time = '2021-12-30 18:15:04'
+        if 'PK500' in sn:
+            celltype=1 #6040三元电芯
+            FactoryType=1
+        elif 'PK502' in sn:
+            celltype=2 #4840三元电芯
+            FactoryType=1
+        elif 'K504B' in sn:
+            celltype=99    #60ah林磷酸铁锂电芯
+            FactoryType=1
+        elif 'MGMLXN750' in sn:
+            celltype=3 #力信50ah三元电芯
+            FactoryType=3
+        elif ('MGMCLN750' or 'UD') in sn: 
+            celltype=4 #CATL 50ah三元电芯
+            FactoryType=2
+        elif 'TJMCL'in sn:
+            celltype=100 #CATL 50ah三元电芯
+            FactoryType=0
+        else:
+            print('SN:{},未找到对应电池类型!!!'.format(sn))
+            continue
+            # sys.exit()
+        
+        param=QX_BatteryParam.BatParam(celltype)   
+        #读取原始数据库数据........................................................................................................................................................
+        dbManager = DBManager.DBManager()
+        df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms'])
+        df_bms = df_data['bms']
+        df_bms=df_bms.dropna(subset=['总电流[A]'])
+        df_bms=df_bms.reset_index(drop=True)
+        print(df_bms)
+        #读取结果数据库数据........................................................................................................................................................
+        host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+        port=3306
+        db='qx_cas'
+        user='qx_algo_readonly'
+        password='qx@123456'
+        mode=1
+        tablename1='cellstateestimation_soh'
+        tablename2='cellstateestimation_uniform_socvoltdiff'       
+        DBRead=DBDownload.DBDownload(host, port, db, user, password,mode)
+        with DBRead as DBRead:
+            df_soh=DBRead.getdata('time_st,sn,soh,cellsoh', tablename=tablename1, sn=sn, timename='time_sp', st=start_time, sp=end_time)
+            df_uniform=DBRead.getdata('time,sn,cellsoc_diff,cellmin_num,cellmax_num', tablename=tablename2, sn=sn, timename='time', st=start_time, sp=end_time)
+
+        #读取电池当前运营状态.....................................................................................................................................................
+        host='rm-bp10j10qy42bzy0q7.mysql.rds.aliyuncs.com'
+        port=3306
+        db='qixiang_manage'
+        user='qx_query'
+        password='@Qx_query'
+        mode=4
+        tablename1='py_battery'     
+        DBRead=DBDw.DBDownload(host, port, db, user, password,mode)
+        with DBRead as DBRead:
+            df_OprtnSta=DBRead.getdata('qrcode','status', tablename=tablename1, sn=sn, timename='', st='', sp='',factory='')#取最后一条运营状态
+        errorcode_map=DataFrame()
+
+
+
+
+
+        #电池诊断.....................................................................................................................................................................
+        CellFltInfo=DataFrame(columns=['start_time', 'end_time', 'product_id', 'code', 'level', 'info','advice'])
+        df_Diag_Ram_sn = df_Diag_Ram.loc[df_Diag_Ram['product_id']==sn]#历史故障
+        df_Diag_Ram_sn_else = df_Diag_Ram.loc[df_Diag_Ram['product_id']!=sn]#sn之外的故障
+        CellFltInfo = df_Diag_Ram_sn.drop('Batpos',axis=1)
+        #获取当前故障电池的历史故障信息↑↑↑↑↑↑↑↑↑↑↑.......................................................................................................................................
+        if not df_bms.empty:
+            df_Diag_Batdiag_update_xq=SamplingSafty.main(sn,param,df_bms,CellFltInfo)#采样安全   
+            df_Diag_Batdiag_update_xq2=CtrlSafty.main(sn,param,df_bms,df_Diag_Batdiag_update_xq)#控制安全
+            batDiag=CBMSBatDiag.BatDiag(sn,celltype,df_bms, df_soh, df_uniform, df_Diag_Batdiag_update_xq2)#鹏飞计算
+            df_Diag_Batdiag_update2=batDiag.diag()
+            df_Diag_Batdiag_update=BMSReportError.main(sn,df_bms,df_Diag_Batdiag_update2,FactoryType,errorcode_map)
+            # df_Diag_Batdiag_update=BatDiag.diag() 
+        else:
+            df_Diag_Batdiag_update_xq=DataFrame(columns=['start_time','end_time','product_id','code','level','info','advice','Batpos'])
+            df_Diag_Batdiag_update_xq2=DataFrame(columns=['start_time','end_time','product_id','code','level','info','advice','Batpos'])
+            df_Diag_Batdiag_update2=DataFrame(columns=['start_time','end_time','product_id','code','level','info','advice','Batpos'])
+            df_Diag_Batdiag_update=DataFrame(columns=['start_time','end_time','product_id','code','level','info','advice','Batpos'])
+        df_Diag_Ram_add,df_Diag_Ram_Update=DiagDataMerge.DetaMerge(df_Diag_Ram_sn,df_Diag_Batdiag_update,df_OprtnSta,df_Diag_Ram_sn_else)
+    task_on=0        
+
+
+#...............................................主函数.......................................................................................................................
+if __name__ == "__main__":
+    global SNnums
+    global task_on
+    
+    task_on=0
+    SNnums = ['PK504B10100004349']
+    
+    mylog=log.Mylog('log.txt','error')
+
+    #............................模块运行前,先读取数据库中所有结束时间为0的数据,需要从数据库中读取................
+    host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+    port=3306
+    db='safety_platform'
+    user='qx_algo_readonly'
+    password='qx@123456'
+    mode=2
+    tablename2='all_fault_info'
+    DBRead = DBDw.DBDownload(host, port, db, user, password,mode)
+    with DBRead as DBRead:
+        df_Diag_Ram = DBRead.getdata('start_time','end_time','product_id','code','level','info','advice','Batpos','factory',tablename=tablename2,factory='',sn='',timename='',st='',sp='')
+    # result=pd.read_csv(r'D:\Work\Code_write\data_analyze_platform\USER\01Screen_Problem\result.csv',encoding='gbk')
+
+    #定时任务.......................................................................................................................................................................
+    scheduler = BlockingScheduler()
+    diag_cal()
+    # DaTa_Sta_Minutes_Task()
+    # DaTa_Sta_Week_Task()  
+
+    # if task_on==0:
+    #     scheduler.add_job(DaTa_Sta_Week_Task, 'interval', days=7, id='Week_Task')
+    #     scheduler.add_job(diag_cal, 'interval', seconds=900, id='diag_job')
+    #     scheduler.add_job(DaTa_Sta_Minutes_Task, 'interval', seconds=180, id='Hour_Task')
+
+    # try:
+
+    #     scheduler.start()
+    # except Exception as e:
+    #     scheduler.shutdown()
+    #     print(repr(e))
+    #     mylog.logopt(e)

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

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

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

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

BIN
LIB/SOH数据.xlsx


BIN
SOH数据.xlsx


+ 166 - 0
USER/WLM/Li_plated.py

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

+ 2 - 0
USER/WLM/__pycache__/test.py

@@ -0,0 +1,2 @@
+from LIB.BACKEND import DBManager
+print(1) 

+ 170 - 0
USER/WLM/cell_outlier.py

@@ -0,0 +1,170 @@
+import pandas as pd
+import pdb
+from sklearn.ensemble import IsolationForest
+import numpy as np
+
+# 计算充电过程
+def preprocess(df):
+    # 滤除前后电压存在一增一减的情况(采样异常)
+    pass
+
+# 计算电压的偏离度    
+def cal_volt_uniform(dfin, volt_column, window=10, step=5, threshold=3):
+    
+    df = dfin.copy()
+    time_list = dfin['time'].tolist()
+
+    # 电压滤波
+    df_volt = df[volt_column] 
+    df_volt_rolling = df_volt.rolling(window).mean()[window-1::step]  # 滑动平均值
+    time_list = time_list[window-1::step] 
+
+    # 电压偏离度
+    mean = df_volt_rolling.mean(axis=1)
+    std = df_volt_rolling.std(axis=1)
+    df_volt_rolling_norm = df_volt_rolling.sub(mean, axis=0).div(std,axis=0)
+    df_volt_rolling_norm = df_volt_rolling_norm.reset_index(drop=True)
+    return df_volt_rolling_norm, time_list
+
+
+# 计算电压变化量的偏离度    
+def cal_voltdiff_uniform(dfin, volt_column, window=10, step=5, window2=10, step2=5,threshold=3):
+    
+    df = dfin.copy()
+    time_list = dfin['time'].tolist()
+
+    # 电压滤波
+    df_volt = df[volt_column] 
+    df_volt_rolling = df_volt.rolling(window).mean()[window-1::step]  # 滑动平均值
+    time_list = time_list[window-1::step] 
+
+    # 计算电压变化量的绝对值(# 计算前后的差值的绝对值,  时间列-1)
+    df_volt_diff = abs(df_volt_rolling.diff()[1:])
+    df_volt_diff = df_volt_diff.reset_index(drop=True)
+    time_list = time_list[1:]
+
+    # 压差归一化(偏离度)
+    # mean = df_volt_diff.mean(axis=1)
+    # std = df_volt_diff.std(axis=1)
+    # df_voltdiff_norm = df_volt_diff.sub(mean, axis=0).div(std,axis=0)
+    df_voltdiff_norm = df_volt_diff.copy()
+
+    # 压差偏离度滑动平均滤波
+    df_voltdiff_rolling = df_voltdiff_norm.rolling(window2).mean()[window2-1::step2]  # 滑动平均值
+    time_list = time_list[window2-1::step2] 
+    mean = df_voltdiff_rolling.mean(axis=1)
+    std = df_voltdiff_rolling.std(axis=1)
+    df_voltdiff_rolling_norm = df_voltdiff_rolling.sub(mean, axis=0).div(std,axis=0)
+    df_voltdiff_rolling_norm = df_voltdiff_rolling_norm.reset_index(drop=True)
+    return df_voltdiff_rolling_norm, time_list
+
+
+
+    # this_alarm = {}
+    # df_alarm = df_voltdiff_rolling_norm[abs(df_voltdiff_rolling_norm)>threshold].dropna(how='all')
+    # for index in df_alarm.index:
+    #     df_temp = df_alarm.loc[index, :].dropna(how='all', axis=0)
+    #     this_alarm.update({df_cell_volt.loc[index+1, 'date']:[str(df_temp.keys().tolist()), str([round(x, 2) for x in df_temp.values.tolist()])]})
+    # df_alarm1 = pd.DataFrame(this_alarm)
+
+
+    # return pd.DataFrame(df_alarm1.values.T, index=df_alarm1.columns, columns=df_alarm1.index), pd.DataFrame(df_alarm2.values.T, index=df_alarm2.columns, columns=df_alarm2.index)
+
+    # 孤立森林算法
+    def iso_froest(df):
+
+        #1. 生成训练数据
+        rng = np.random.RandomState(42)
+        X = 0.3 * rng.randn(100, 2) #生成100 行,2 列
+        X_train = np.r_[X + 2, X - 2]
+        print(X_train)
+        # 产生一些异常数据
+        X_outliers = rng.uniform(low=-4, high=4, size=(20, 2))
+        iForest= IsolationForest(contamination=0.1)
+        iForest = iForest.fit(X_train)
+        #预测
+        pred = iForest.predict(X_outliers)
+        print(pred)
+        # [-1 1 -1 -1 -1 -1 -1 1 -
+
+# 计算相关系数
+def cal_coff(df):
+    cc_mean = np.mean(df, axis=0)  #axis=0,表示按列求均值 ——— 即第一维
+    cc_std = np.std(df, axis=0)
+    cc_zscore = (df-cc_mean)/cc_std   #标准化
+    cc_zscore = cc_zscore.dropna(axis=0, how='any')
+
+    cc_zscore_corr = cc_zscore.corr(method='spearman')
+    
+    result = []
+    for i in range(len(cc_zscore_corr)):
+        v = abs(np.array((sorted(cc_zscore_corr.iloc[i]))[2:-1])).mean() # 去掉1 和两个最小值后求均值
+        result.append(v)
+    return cc_zscore_corr, result
+
+# 输出偏离的情况,记录到数据库
+
+def instorage(sn, df_voltdiff_result, df_volt_result):
+
+    df_all_result = pd.DataFrame(columns=['sn', 'time', 'cellnum', 'value', 'type'])
+    
+    value_list = []
+    cellnum_list = []
+    time_list = []
+    type_list = []
+    df_result = df_voltdiff_result.copy()
+    time_list_1 = df_voltdiff_result['time']
+    df_result = df_result[(df_result>3) | (df_result<-3)].dropna(axis=0, how='all').dropna(axis=1, how='all')
+    for column in df_result.columns:
+        df = df_result[[column]].dropna(axis=0, how='all')
+        value_list.extend(df[column].tolist())
+        cellnum_list.extend([column]*len(df))
+        time_list.extend([time_list_1[x] for x in df.index])
+    length_1 = len(value_list)
+    
+
+
+    df_result = df_volt_result.copy()
+    time_list_2 = df_volt_result['time']
+    df_result = df_result[(df_result>3) | (df_result<-3)].dropna(axis=0, how='all').dropna(axis=1, how='all')
+    for column in df_result.columns:
+        df = df_result[[column]].dropna(axis=0, how='all')
+        value_list.extend(df[column].tolist())
+        cellnum_list.extend([column]*len(df))
+        time_list.extend([time_list_2[x] for x in df.index])
+
+    length_2 = len(value_list) - length_1
+    type_list.extend(['电压变化量离群'] * length_1)
+    type_list.extend(['电压离群'] * length_2)
+    df_all_result['sn'] = [sn] * len(value_list)
+    df_all_result['cellnum'] = cellnum_list
+    df_all_result['value'] = value_list
+    df_all_result['time'] = time_list
+    df_all_result['type'] = type_list
+    return df_all_result
+
+# 报警.如果在某个窗口内,有超过ratio个的点,偏离度超过threshold, 则报警
+def alarm(dfin, volt_column, alarm_window=10, alarm_ratio=0.8, alarm_threshold=2.5):
+
+    
+    time_list = dfin['time'].tolist()
+    df_result = dfin[volt_column].copy()
+    alarm_result = pd.DataFrame(columns=['type', 'num', 'alarm_time'])      
+    df_result_1 = df_result.copy()
+    df_result_1[df_result_1<alarm_threshold] = 0
+    df_result_1[df_result_1>alarm_threshold] = 1    
+    df_result_1 = df_result_1.rolling(alarm_window).sum()
+    for column in volt_column:
+        if len(df_result_1[df_result_1[column]>alarm_window * alarm_ratio])>0:
+            alarm_result = alarm_result.append({'type':'1', 'num':column, 'alarm_time':time_list[df_result_1[df_result_1[column]>alarm_window * alarm_ratio].index[0]]}, ignore_index=True)
+
+    # time_list = time_list[window-1::step] 
+
+    df_result_2 = df_result.copy()
+    df_result_2[df_result_2>-alarm_threshold] = 0
+    df_result_2[df_result_2<-alarm_threshold] = 1
+    df_result_2 = df_result_2.rolling(alarm_window).sum()
+    for column in volt_column:
+        if len(df_result_2[df_result_2[column]>alarm_window * alarm_ratio])>0:
+            alarm_result = alarm_result.append({'type':'2', 'num':column, 'alarm_time':time_list[df_result_2[df_result_2[column]>alarm_window * alarm_ratio].index[0]]}, ignore_index=True)
+    return alarm_result

BIN
USER/WLM/data.xlsx


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 83 - 0
USER/WLM/main.ipynb


+ 100 - 0
USER/WLM/main.py

@@ -0,0 +1,100 @@
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+import time, datetime
+from apscheduler.schedulers.blocking import BlockingScheduler
+# import Log
+from pandas.core.frame import DataFrame
+import Li_plated
+
+#...................................电池包电芯安全诊断函数......................................................................................................................
+def cell_platd_test():
+    global SNnums
+    global df_Diag_lipltd
+    start=time.time()
+    now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+    now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+    start_time=now_time-datetime.timedelta(days=3)
+    end_time=str(now_time)
+    start_time=str(start_time)
+    k = 1
+    for sn in SNnums:
+        if 'PK500' in sn:
+            celltype=1 #6040三元电芯
+        elif 'PK502' in sn:
+            celltype=2 #4840三元电芯
+        elif 'K504B' in sn:
+            celltype=99    #60ah林磷酸铁锂电芯
+        elif 'MGMLXN750' in sn:
+            celltype=3 #力信50ah三元电芯
+        elif ('MGMCLN750' or 'UD') in sn: 
+            celltype=4 #CATL 50ah三元电芯
+        elif 'TJMCL' in sn: 
+            celltype=100 #金茂电芯
+        else:
+            print('SN:{},未找到对应电池类型!!!'.format(sn))
+            continue
+            # sys.exit()
+        print('计算的第' + str(k) + '个:' + sn)
+        k = k + 1
+        #读取原始数据库数据........................................................................................................................................................
+        start_time = '2021-12-01 00:00:00'
+        end_time = '2021-12-08 12:00:00'
+        dbManager = DBManager.DBManager()
+        df_data = dbManager.get_data(sn=sn, start_time=start_time, end_time=end_time, data_groups=['bms'])
+        df_bms = df_data['bms']
+        df_Diag_lipltd_add = pd.DataFrame(columns = ['sn','time','liplated', 'liplated_amount'])
+
+        #析锂诊断................................................................................................................................................................
+        if not df_bms.empty:
+            Diag_lipltd_temp = Li_plated.Liplated_test(sn,celltype,df_bms)#析锂检测
+            df_Diag_lipltd_add = Diag_lipltd_temp.liplated_detect()        
+        if not df_Diag_lipltd_add.empty:
+            df_Diag_lipltd_temp = df_Diag_lipltd.append(df_Diag_lipltd_add)
+            df_Diag_lipltd = df_Diag_lipltd_temp.drop_duplicates(subset = ['sn','time'], keep = 'first', inplace = False)
+            df_Diag_lipltd.sort_values(by = ['sn'], axis = 0, ascending=True,inplace=True)#对故障信息按照时间进行排序
+            df_Diag_lipltd.to_csv(r'D:\Work\Code_write\data_analyze_platform\USER\lzx\01算法开发\02析锂检测\liplated\析锂.csv',index=False,encoding='GB18030')
+        end=time.time()
+        print(end-start)
+
+#...............................................主函数.......................................................................................................................
+if __name__ == "__main__":
+    global SNnums
+    global df_Diag_lipltd
+    
+    # excelpath=r'D:\Work\Code_write\data_analyze_platform\USER\lzx\01算法开发\00项目sn号\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=['TJMCL120502305010','TJMCL120502305012','TJMCL120502305048','TJMCL120502305044','TJMCL120502305026','TJMCL120502305022','TJMCL120502305032','TJMCL120502305038']
+    SNnums = ['PK50201A000002053']#['MGMCLN750N215N049'] #SNnums_6040 #SNnums_C7255 #SNnums_6040['MGMCLN750N215N049'] 
+    # SNnums = pd.read_csv(r'D:\Work\Code_write\data_analyze_platform\USER\lzx\01算法开发\02析锂检测\liplated\疑似析锂电池sn.csv',encoding='GB18030')
+    
+    # mylog=log.Mylog('log_diag.txt','error')
+    # mylog.logcfg()
+    #............................模块运行前,先读取数据库中所有结束时间为0的数据,需要从数据库中读取................
+    # df_Diag_lipltd=pd.read_csv(r'D:\Work\Code_write\data_analyze_platform\USER\lzx\01算法开发\02析锂检测\liplated\析锂.csv',encoding='GB18030')
+
+    print('----------------输入--------')
+    print('-------计算中-----------')
+    cell_platd_test()
+    #定时任务.......................................................................................................................................................................
+    scheduler = BlockingScheduler()
+    # scheduler.add_job(cell_platd_test, 'interval', seconds=10, id='diag_job')
+
+    try:  
+        scheduler.start()
+    except Exception as e:
+        scheduler.shutdown()
+        print(repr(e))
+        # mylog.logopt(e)

+ 23 - 0
USER/code_transform.py

@@ -0,0 +1,23 @@
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+from urllib import parse
+import pymysql
+import pandas as pd
+# 数据库配置
+host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
+port=3306
+database='algo_dict'
+user='qx_algo_readonly'
+password='qx@123456'
+
+db_engine = create_engine(
+    "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
+        user, password, host, port, database
+    ))
+DbSession = sessionmaker(bind=db_engine)
+
+errorcode_map = pd.read_sql("select * from faultcode_map", db_engine)
+p=1 # 协议 1:科易,2: 优旦/格林美, 3:自研'
+code = 1 # 终端故障码
+platform_code = errorcode_map[(errorcode_map['protocol']==p)&(errorcode_map['end_errorcode']==str(p))]['platform_errorcode']
+db_engine.dispose()

BIN
soh.xlsx


Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott