Bläddra i källkod

Merge branch 'dev' of lmstack/data_analyze_platform into master

lmstack 3 år sedan
förälder
incheckning
eb0df594fa
42 ändrade filer med 2971 tillägg och 224 borttagningar
  1. 5 0
      .gitignore
  2. 4 2
      LIB/BACKEND/DBManager.py
  3. 1 1
      LIB/BACKEND/DataPreProcess.py
  4. 1 1
      LIB/BACKEND/Log.py
  5. 1 1
      LIB/BACKEND/Tools.py
  6. 0 102
      LIB/FRONTEND/SignalMonitor/create_table.py
  7. 43 82
      LIB/FRONTEND/SignalMonitor/main.py
  8. 1 1
      LIB/FRONTEND/day_sta.py
  9. 127 0
      LIB/FRONTEND/odo/CalDist.py
  10. 68 0
      LIB/FRONTEND/odo/CalDist_Batch.py
  11. 77 0
      LIB/FRONTEND/odo/GpsRank.py
  12. 159 0
      LIB/FRONTEND/odo/ProcessDfBms.py
  13. 139 0
      LIB/FRONTEND/odo/ProcessDfGps.py
  14. 293 0
      LIB/FRONTEND/odo/UpdtFct.py
  15. 28 0
      LIB/FRONTEND/odo/UpdtFct_Main.py
  16. BIN
      LIB/FRONTEND/odo/asset_table.xlsx
  17. 66 0
      LIB/FRONTEND/odo/main_1.py
  18. 309 0
      LIB/FRONTEND/soh/LFPSoh 20210711.py
  19. 173 0
      LIB/FRONTEND/soh/NCMSoh 20210716.py
  20. 34 0
      LIB/FRONTEND/soh/main.py
  21. 6 0
      LIB/FRONTEND/soh/soh表头及数据类型.xlsx
  22. BIN
      LIB/FRONTEND/soh/骑享资产梳理-20210621.xlsx
  23. 1 1
      LIB/MIDDLE/IndexStaByOneCycle.py
  24. 1 1
      LIB/MIDDLE/IndexStaByPeriod.py
  25. 49 24
      LIB/MIDDLE/SignalMonitor.py
  26. 125 0
      LIB/MIDDLE/odo/CalDist.py
  27. 68 0
      LIB/MIDDLE/odo/CalDist_Batch.py
  28. 77 0
      LIB/MIDDLE/odo/GpsRank.py
  29. 159 0
      LIB/MIDDLE/odo/ProcessDfBms.py
  30. 139 0
      LIB/MIDDLE/odo/ProcessDfGps.py
  31. 293 0
      LIB/MIDDLE/odo/UpdtFct.py
  32. 28 0
      LIB/MIDDLE/odo/UpdtFct_Main.py
  33. BIN
      LIB/MIDDLE/odo/__pycache__/CalDist.cpython-38.pyc
  34. 304 0
      LIB/MIDDLE/soh/LFPSoh_20210711.py
  35. 169 0
      LIB/MIDDLE/soh/NCMSoh_20210716.py
  36. 17 2
      demo.ipynb
  37. 1 1
      函数说明/DBManager.html
  38. 1 1
      函数说明/DataPreProcess.html
  39. 1 1
      函数说明/IndexStaByOneCycle.html
  40. 1 1
      函数说明/IndexStaByPeriod.html
  41. 1 1
      函数说明/Log.html
  42. 1 1
      函数说明/Tools.html

+ 5 - 0
.gitignore

@@ -5,6 +5,7 @@
 /.vscode/
 /*/__pycache__/*.*
 /*/*/__pycache__/*.pyc
+/*/*/*/__pycache__/*.pyc
 /*.ipynb
 /*.csv
 *.doc
@@ -16,3 +17,7 @@
 # *.*
 /CONFIGURE/PathSetting.py
 *.log
+/demo.ipynb
+/*/*/deploy.py
+/*/*/*/deploy.py
+

+ 4 - 2
LIB/BACKEND/DBManager.py

@@ -3,7 +3,7 @@
 
 预留:后期若改用通过访问数据库的形式进行数据的获取,则本文件负责数据库的连接,sql指令的执行,数据获取等功能。
 '''
-__author__ = 'Wang Liming'
+__author__ = 'lmstack'
 
 import time
 import datetime
@@ -221,9 +221,11 @@ class DBManager():
                 try:
                     data_blocks = np.concatenate((data_blocks,data_block),axis=0)
                 except Exception as e: 
-                    if 'all the input array dimensions except for the concatenation axis must match exactly' in str(e):
+                    if 'all the input array dimensions for the concatenation axis must match exactly' in str(e) or \
+                    'all the input array dimensions except for the concatenation axis must match exactly'  in str(e):
                         pass
                     else:
+                        pdb.set_trace()
                         raise e
   
                 # print('\r'+str(i),end=" ")

+ 1 - 1
LIB/BACKEND/DataPreProcess.py

@@ -2,7 +2,7 @@
 数据预处理类
 
 '''
-__author__ = 'Wang Liming'
+__author__ = 'lmstack'
 
 
 # import CONFIGURE.PathSetting as PathSetting

+ 1 - 1
LIB/BACKEND/Log.py

@@ -2,7 +2,7 @@
 Log类
 
 '''
-__author__ = 'Wang Liming'
+__author__ = 'lmstack'
 
 
 import logging

+ 1 - 1
LIB/BACKEND/Tools.py

@@ -2,7 +2,7 @@
 工具类
 
 '''
-__author__ = 'Wang Liming'
+__author__ = 'lmstack'
 
 
 # import CONFIGURE.PathSetting as PathSetting

+ 0 - 102
LIB/FRONTEND/SignalMonitor/create_table.py

@@ -1,102 +0,0 @@
-'''
-定义表的结构,并在数据库中创建对应的数据表
-'''
-__author__ = 'Wang Liming'
-
-from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy import Column, String, create_engine, Integer, DateTime, BigInteger, FLOAT
-from urllib import parse
-Base = declarative_base()
-
-
-class BmsLastDataDay(Base):
-    __tablename__ = "bms_last_data_day"
-    __table_args__ = ({'comment': '电池信号监控---每天最后一条bms数据'})  # 添加索引和表注释
-
-    id = Column(Integer, primary_key=True, autoincrement=True, comment="主键")
-    sn = Column(String(64), comment="sn")
-    current = Column(FLOAT, comment="电流")
-    time_stamp = Column(DateTime, comment="时间戳")
-    pack_state = Column(Integer, comment="电池状态")
-    line_state = Column(Integer, comment="信号状态")
-
-
-    def __init__(self, sn, current, time_stamp, pack_state, line_state):
-        self.sn = sn
-        self.current = current
-        self.time_stamp = time_stamp
-        self.pack_state = pack_state
-        self.line_state = line_state
-
-class GpsLastDataDay(Base):
-    __tablename__ = "gps_last_data_day"
-    __table_args__ = ({'comment': '电池信号监控---每天最后一条gps数据'})  # 添加索引和表注释
-
-    id = Column(Integer, primary_key=True, autoincrement=True, comment="主键")
-    sn = Column(String(64), comment="sn")
-    time_stamp = Column(DateTime, comment="时间戳")
-    pack_state = Column(Integer, comment="电池状态")
-    line_state = Column(Integer, comment="信号状态")
-
-
-    def __init__(self, sn, time_stamp, pack_state, line_state):
-        self.sn = sn
-        self.time_stamp = time_stamp
-        self.pack_state = pack_state
-        self.line_state = line_state
-
-class GpsSignalMonitor(Base):
-    __tablename__ = "gps_signal_monitor"
-    __table_args__ = ({'comment': 'gps信号监控'})  # 添加索引和表注释
-
-    id = Column(Integer, primary_key=True, autoincrement=True, comment="主键")
-    sn = Column(String(64), comment="sn")
-    start_time = Column(DateTime, comment="开始时间")
-    end_time = Column(DateTime, comment="结束时间")
-    offline_time = Column(DateTime, comment="离线时间")
-    pack_state = Column(Integer, comment="电池状态")
-    line_state = Column(Integer, comment="信号状态")
-
-
-    def __init__(self, sn, start_time, end_time, off_line_time, pack_state, line_state):
-        self.sn = sn
-        self.start_time = start_time
-        self.end_time = end_time
-        self.off_line_time = off_line_time
-        self.pack_state = pack_state
-        self.line_state = line_state
-
-class BmsSignalMonitor(Base):
-    __tablename__ = "bms_signal_monitor"
-    __table_args__ = ({'comment': 'bms信号监控'})  # 添加索引和表注释
-
-    id = Column(Integer, primary_key=True, autoincrement=True, comment="主键")
-    sn = Column(String(64), comment="sn")
-    start_time = Column(DateTime, comment="开始时间")
-    end_time = Column(DateTime, comment="结束时间")
-    offline_time = Column(DateTime, comment="离线时间")
-    pack_state = Column(Integer, comment="电池状态")
-    line_state = Column(Integer, comment="信号状态")
-
-
-    def __init__(self, sn, start_time, end_time, off_line_time, pack_state, line_state):
-        self.sn = sn
-        self.start_time = start_time
-        self.end_time = end_time
-        self.off_line_time = off_line_time
-        self.pack_state = pack_state
-        self.line_state = line_state
-
-# 执行该文件,创建表格到对应的数据库中
-if __name__ == "__main__":
-    host = 'rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
-    port = 3306
-    user = 'qx_cas'
-    password = parse.quote_plus('Qx@123456')
-    database = 'qx_cas'
-
-    db_engine = create_engine(
-        "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
-            user, password, host, port, database
-        ))
-    Base.metadata.create_all(db_engine)

+ 43 - 82
LIB/FRONTEND/SignalMonitor/main.py

@@ -13,91 +13,52 @@ from urllib import parse
 
 dbManager = DBManager.DBManager()
 if __name__ == "__main__":
-    try:
-        # 数据库配置
-        host = 'rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com'
-        port = 3306
-        user = 'qx_cas'
-        password = parse.quote_plus('Qx@123456')
-        database = 'qx_cas'
-
-        db_engine = create_engine(
-            "mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
-                user, password, host, port, database
-            ))
-        print("mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8".format(
-                user, password, host, port, database
-            ))
-        DbSession = sessionmaker(bind=db_engine)
-        
-        # 日志配置
-        log = Log.Mylog(log_name='signal_monitor', log_level = 'info')
-        log.set_file_hl(file_name='info.log', log_level='info')
-        log.set_file_hl(file_name='error.log', log_level='error')
-        logger = log.get_logger()
-
-        logger.info("pid is + {}".format(os.getpid))
-
-        # 读取sn列表
-        df_sn = pd.read_csv('sn_list.csv')
-        df_sn = df_sn.reset_index(drop=True)
-        df_sn['StartTime'] = pd.to_datetime(df_sn['StartTime'])
-        df_sn['EndTime'] = pd.to_datetime(df_sn['EndTime'])
-        df_sn['ExitTime'] = pd.to_datetime(df_sn['ExitTime'])
-        signalMonitor = SignalMonitor.SignalMonitor()
-        
-        cal_period = 24    # 计算间隔,单位h
-        for i in range(0, len(df_sn['sn'])):    # 遍历SN号
-            sn = [df_sn.loc[i,'sn']]
-            if not (sn[0][0:2] == 'PK' or sn[0][0:2] == 'MG' or sn[0][0:2] == 'UD'):
+    # 读取sn列表
+    df_sn = pd.read_csv('sn_list.csv')
+    df_sn = df_sn.reset_index(drop=True)
+    df_sn['StartTime'] = pd.to_datetime(df_sn['StartTime'])
+    df_sn['EndTime'] = pd.to_datetime(df_sn['EndTime'])
+    df_sn['ExitTime'] = pd.to_datetime(df_sn['ExitTime'])
+    signalMonitor = SignalMonitor.SignalMonitor()
+    
+    cal_period = 24    # 计算间隔,单位h
+    for i in range(0, len(df_sn['sn'])):    # 遍历SN号
+        sn = [df_sn.loc[i,'sn']]
+        if not (sn[0][0:2] == 'PK' or sn[0][0:2] == 'MG' or sn[0][0:2] == 'UD'):
+            continue
+        st = df_sn.loc[i, 'StartTime']
+        if df_sn.loc[i, 'Service'] == 0:
+            if pd.isnull(df_sn.loc[i, 'EndTime']) and pd.isnull(df_sn.loc[i, 'ExitTime']):
                 continue
-            st = df_sn.loc[i, 'StartTime']
-            if df_sn.loc[i, 'Service'] == 0:
-                if pd.isnull(df_sn.loc[i, 'EndTime']) and pd.isnull(df_sn.loc[i, 'ExitTime']):
-                    continue
-                elif pd.isnull(df_sn.loc[i, 'ExitTime']):
-                    et = df_sn.loc[i, 'EndTime']
-                else:
-                    et = df_sn.loc[i, 'ExitTime']
-            elif pd.isnull(df_sn.loc[i, 'EndTime']):
-                otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
-                et = pd.to_datetime(otherStyleTime)
-            else:
+            elif pd.isnull(df_sn.loc[i, 'ExitTime']):
                 et = df_sn.loc[i, 'EndTime']
-            df_last_state = pd.DataFrame(
-                columns=['sn', 'current', 'Timestamp', 'PackState', 'LineState'])    # 每日最后BMS数据
-            df_last_state_gps = pd.DataFrame(
-                columns=['sn', 'Timestamp', 'PackState', 'LineState'])    # 每日最后GPS数据
-            while st < et:
-                df_res = pd.DataFrame(columns=[
-                                    'sn', 'PackState', 'LineState', 'StartTime', 'EndTime', 'OfflineTime'])    # 初始化BMS信号统计数据
-                df_res_gps = pd.DataFrame(columns=[
-                                        'sn', 'PackState', 'LineState', 'StartTime', 'EndTime', 'OfflineTime'])    # 初始化GPS信号统计数据
-                df_res, df_state, df_last_state = signalMonitor.get_bms_offline_stat(
-                    sn, st, et, df_res, df_last_state, cal_period)    # 计算每日BMS信号统计数据
-                df_res_gps, df_last_state_gps = signalMonitor.get_gps_offline_stat(
-                    sn, st, et, df_state, df_res_gps, df_last_state_gps, cal_period)    # 计算每日GPS信号统计数据
-            
-                # 数据入库
-                df_tosql = df_res_gps.copy()
-                df_tosql.columns = ['sn', 'pack_state', 'line_state', 'start_time', 'end_time', 'offline_time']
-                df_tosql.loc[df_tosql.index[-1:]].to_sql("gps_signal_monitor",con=db_engine, if_exists="append",index=False)
-
-                df_tosql = df_res.copy()
-                df_tosql.columns = ['sn', 'pack_state', 'line_state', 'start_time', 'end_time', 'offline_time']
-                df_tosql.loc[df_res.index[-1:]].to_sql("bms_signal_monitor",con=db_engine, if_exists="append",index=False)
-                st = st + datetime.timedelta(hours=cal_period)
+            else:
+                et = df_sn.loc[i, 'ExitTime']
+        elif pd.isnull(df_sn.loc[i, 'EndTime']):
+            otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
+            et = pd.to_datetime(otherStyleTime)
+        else:
+            et = df_sn.loc[i, 'EndTime']
+        df_last_state = pd.DataFrame(
+            columns=['sn', 'current', 'Timestamp', 'PackState', 'LineState'])    # 每日最后BMS数据
+        df_last_state_gps = pd.DataFrame(
+            columns=['sn', 'Timestamp', 'PackState', 'LineState', 'latitude', 'longitude'])    # 每日最后GPS数据
+        df_res = pd.DataFrame(columns=[
+                                'sn', 'PackState', 'LineState', 'StartTime', 'EndTime', 'OfflineTime'])    # 初始化BMS信号统计数据
+        df_res_gps = pd.DataFrame(columns=[
+                                    'sn', 'PackState', 'LineState', 'StartTime', 'EndTime', 'OfflineTime', 'latitude', 'longitude'])    # 初始化GPS信号统计数据
+        while st < et:
 
+            df_res, df_state, df_last_state = signalMonitor.get_bms_offline_stat(
+                sn, st, et, df_res, df_last_state, cal_period)    # 计算每日BMS信号统计数据
+            df_res_gps, df_last_state_gps = signalMonitor.get_gps_offline_stat(
+                sn, st, et, df_state, df_res_gps, df_last_state_gps, cal_period)    # 计算每日GPS信号统计数据
             # 数据入库
-                df_tosql = df_last_state.copy()
-                df_tosql.columns = ['sn', 'current', 'time_stamp', 'pack_state', 'line_state']
-                df_tosql.to_sql("bms_last_data_day",con=db_engine, if_exists="append",index=False)
+            st = st + datetime.timedelta(hours=cal_period)
+        SignalMonitor._file_write(r'D:\result_04.xls', df_res)    # BMS信号统计数据入库
+        SignalMonitor._file_write(r'D:\result_05.xls', df_res_gps)    # GPS信号统计数据入库
 
-                df_tosql = df_last_state_gps.copy()
-                df_tosql.columns = ['sn', 'time_stamp', 'pack_state', 'line_state']
-                df_tosql.to_sql("gps_last_data_day",con=db_engine, if_exists="append",index=False)
+        SignalMonitor._file_write(r'D:\result_06.xls', df_last_state)
+        SignalMonitor._file_write(r'D:\result_07.xls', df_last_state_gps)
 
-            logger.info("{} {} Success!".format(sn, str(st)))
-    except Exception as e:
-        logger.error(traceback.format_exc)
-        logger.error(u"任务运行错误", exc_info=True)
+    # 数据入库

+ 1 - 1
LIB/FRONTEND/day_sta.py

@@ -1,4 +1,4 @@
-__author__ = 'Wang Liming'
+__author__ = 'lmstack'
 
 # 每日指标统计函数
 # import CONFIGURE.PathSetting as PathSetting

+ 127 - 0
LIB/FRONTEND/odo/CalDist.py

@@ -0,0 +1,127 @@
+from math import radians, cos, sin, asin, sqrt
+import pandas as pd
+import numpy as np
+from datetime import datetime
+from datetime import timedelta
+
+from GpsRank import *
+from ProcessDfBms import *
+from ProcessDfGps import *
+
+from LIB.BACKEND import DBManager
+
+import DBManager
+#####################################配置环境分割线#################################################
+
+def GetDistInfo(input_sn,input_starttime,input_endtime):
+
+    #####################################配置参数分割线#################################################
+    dbManager = DBManager.DBManager()
+    data_raw = dbManager.get_data(sn=input_sn, start_time=input_starttime, 
+        end_time=input_endtime)
+    #拆包预处理
+    df_bms_raw=data_raw['bms']
+    df_gps_raw=data_raw['gps']
+    df_bms=preprocess_Df_Bms(df_bms_raw)
+    df_gps=preprocess_Df_Gps(df_gps_raw)
+    
+    #####################################数据预处理分割线#################################################
+
+    # mode: 0:正常取数; 1:7255 取数
+    if input_sn[0:2] == 'UD' or input_sn[0:2] == 'MG':
+        mode = 1
+    else:
+        mode = 0
+    #获取状态表,mode默认为0,mode=1放电时电流为负,mode=0充电时电流为正
+
+    df_bms_drive_timetable=get_bms_drive_timetable(df_bms,mode)
+    df_gps_drive_cycle_accum=pd.DataFrame()
+    if len(df_bms_drive_timetable)>0:
+        for index in range(len(df_bms_drive_timetable)):
+            #筛选drivecycle数据
+            drive_start_time=df_bms_drive_timetable.loc[index,'drive_start_time']#开始时间
+            drive_end_time=df_bms_drive_timetable.loc[index,'drive_end_time']#结束时间
+
+            time_condition=(df_gps['time']>drive_start_time)&(df_gps['time']<drive_end_time)#时间判断条件
+            df_gps_drive_cycle=df_gps.loc[time_condition,:].copy()
+            df_gps_drive_cycle=df_gps_drive_cycle.reset_index(drop=True)#重置index
+            #计算drivecycle GPS累计里程,存入表格
+            condition_a=df_gps_drive_cycle['deltatime']>60*3
+            condition_b=(df_gps_drive_cycle['deltatime']>90*1)&(df_gps_drive_cycle['distance']>1000)
+            drive_cycle_dist_array=df_gps_drive_cycle.loc[~(condition_a|condition_b),'distance'].values
+            drive_cycle_dist_array=drive_cycle_dist_array[np.where((drive_cycle_dist_array>=1)&(drive_cycle_dist_array<1000))[0]]
+            gps_dist=drive_cycle_dist_array.sum()
+            df_bms_drive_timetable.loc[index,'gps_dist']=gps_dist#得到GPS路径
+            #计算头-尾的空缺时间段对应的预估SOC
+            if len(df_gps_drive_cycle)>2:
+                gps_starttime=df_gps_drive_cycle.loc[1,'time']#gps开始时间
+                gps_endtime=df_gps_drive_cycle.loc[df_gps_drive_cycle.index[-1],'time']#gps结束时间
+                #从drive_start_time到gps开始时间,使用SOC计算的里程
+                #gps结束时间到drive_end_time,使用SOC计算的里程
+                unrecorded_odo_head=cal_deltasoc(df_bms,drive_start_time,gps_starttime)
+                unrecorded_odo_tail=cal_deltasoc(df_bms,gps_endtime,drive_end_time)
+            else:
+                #计算数据丢失行unrecordeodo
+                unrecorded_odo_head=cal_deltasoc(df_bms,drive_start_time,drive_end_time)
+                unrecorded_odo_tail=0
+            #计算中间的预估SOC
+            predict_dist=cal_unrecorded_gps(df_gps_drive_cycle,df_bms)
+            #计算总的预估SOC
+            totaldist=predict_dist+unrecorded_odo_head+ unrecorded_odo_tail#得到GPS路径
+            df_bms_drive_timetable.loc[index,'predict_dist']=totaldist
+    else :
+        pass
+
+    #####################################统计行驶里程End#################################################
+    #打印输出结果#
+    index_list=list(range(len(df_bms_drive_timetable)))
+
+    dist_gps=0
+    dist_predict=0
+    day_start_time=''#当日开始时间
+    day_end_time=''#当日结束时间
+    day_start_soc=0#当日开始soc
+    day_end_soc=0#当日结束soc
+    day_min_soc=101#当日最低soc
+    drive_accum_soc=0#累计使用SOC
+
+    if len(df_bms_drive_timetable)>0:
+        #开始行
+        day_start_soc=df_bms_drive_timetable.loc[1,'drive_start_soc']#开始soc
+        day_start_time=df_bms_drive_timetable.loc[1,'drive_start_time']#开始时间
+        #结束行
+        day_end_time=df_bms_drive_timetable.loc[len(df_bms_drive_timetable)-1,'drive_end_time']#结束时间
+        day_end_soc=df_bms_drive_timetable.loc[len(df_bms_drive_timetable)-1,'drive_end_soc']#结束soc
+
+    for index in index_list:
+        '''汇总里程'''
+        dist_gps+=df_bms_drive_timetable.loc[index,'gps_dist']/1000#计算GPS里程
+        dist_predict+=df_bms_drive_timetable.loc[index,'predict_dist']#计算预估里程
+        drive_start_soc=df_bms_drive_timetable.loc[index,'drive_start_soc']#驾驶周期开始的soc
+        drive_end_soc=df_bms_drive_timetable.loc[index,'drive_end_soc']#驾驶周期结束的soc
+        day_min_soc=min(day_min_soc,drive_start_soc,drive_end_soc)
+
+        delta_soc=drive_start_soc-drive_end_soc#驾驶周期SOC变化量
+        drive_accum_soc+=abs(delta_soc)#所有drive cycle累计消耗的soc
+
+    # gps_score=get_df_gps_score(input_starttime,input_endtime,df_gps)
+    # gps_score=round(gps_score,1)
+    #计算总里程
+    dist_gps=round(dist_gps,3)
+    dist_predict=round(dist_predict,3)
+    dist_all=round(dist_gps+dist_predict,3)
+    #输出统计结果
+    # print ('为您查询到,从'+input_starttime+'到'+input_endtime+'时间段内:')
+    # print('SOC变化量:'+str(df_bms['bmspacksoc'].max()-df_bms['bmspacksoc'].min())+' %')
+    # print('行驶总里程:'+str(dist_all)+' km')
+
+    return {'SN':input_sn,'range':dist_all,'accum_soc':drive_accum_soc,'day_start_soc':day_start_soc,
+    'day_end_soc':day_end_soc,'day_start_time':day_start_time,'day_end_time':day_end_time,
+    'day_min_soc':day_min_soc}
+    # print('其中GPS信号在线时里程:'+str(dist_gps)+' km')
+    # print('其中GPS信号掉线时预估里程:'+str(dist_predict)+' km')
+    # print('GPS信号质量评分为:'+str(gps_score),'分\n')
+
+    #####################################打印结果End#################################################
+
+

+ 68 - 0
LIB/FRONTEND/odo/CalDist_Batch.py

@@ -0,0 +1,68 @@
+from math import radians, cos, sin, asin, sqrt
+import pandas as pd
+import numpy as np
+from datetime import datetime
+from datetime import timedelta
+
+from GpsRank import *
+from ProcessDfBms import *
+from ProcessDfGps import *
+from CalDist import *
+from LIB.BACKEND import DBManager
+import pdb
+
+asset_table_path='D:\\work\\Qixiang\\data_analyze_platform\\pengmin\\AllCarDist\\asset_table.xlsx'
+drive_info_path='D:\\work\\Qixiang\\data_analyze_platform\\pengmin\\AllCarDist\\drive_info.xlsx'
+asset_sheet_num=1
+usecols_list=[4,5]
+
+asset_table=pd.read_excel(asset_table_path,sheet_name=asset_sheet_num,skiprows=1,usecols=usecols_list)
+SN_list=asset_table['SN号'].values.tolist()
+print('从6060sheet读取到:'+str(len(SN_list))+'行')
+asset_table=asset_table.rename(columns={'SN号':'SN','状态':'state'})
+
+asset_table.set_index(["SN"],inplace=True)
+col_name=asset_table.columns.tolist()
+col_name.extend(['range','accum_soc','day_start_soc','day_end_soc','day_start_time','day_end_time'])
+asset_table=asset_table.reindex(columns=col_name)
+
+start_hour='00:00:00'#每日查询最早时间
+end_hour='23:59:00'#每日查询最晚时间
+
+
+date_index=pd.date_range('2021-07-31','2021-07-31')
+for date in date_index:
+    '''遍历日期'''
+
+    str_date=str(date)[:10]
+    input_starttime=str_date+' '+start_hour
+    input_endtime=str_date+' '+end_hour
+    test_day=str_date[5:10]#月-日,用于建立sheet
+    drive_info_path='D:\\work\\Qixiang\\data_analyze_platform\\pengmin\\AllCarDist\\6060\\drive_info'+test_day+'_50_end_'+'.xlsx'
+
+    print(input_starttime)
+
+    drive_info_aday=pd.DataFrame()
+    SN_list_short=SN_list#先选择了0:50,50:end
+
+    for SN in SN_list_short:
+        '''遍历SN号'''
+        SN=SN.strip('\t')
+        SN=SN.strip('\n')
+
+        try:
+            range=GetDistInfo(SN,input_starttime,input_endtime)
+            range_df=pd.DataFrame([range])
+            drive_info_aday=pd.concat([drive_info_aday,range_df],axis=0)
+
+        except:
+            print(SN+' '+test_day+'fail')
+        else:
+            pass
+            #print(SN+' '+test_day+'success')
+
+    drive_info_aday.to_excel(drive_info_path,sheet_name=test_day)#sheet名称为testday
+    
+    
+
+

+ 77 - 0
LIB/FRONTEND/odo/GpsRank.py

@@ -0,0 +1,77 @@
+import pandas as pd
+import numpy as np
+from datetime import datetime
+from datetime import timedelta
+
+def cal_gps_score(df):
+    '''在获取信号,优、良、合格、掉线的比例之后,计算gps的总评分'''
+    score=0
+    for index in range(len(df)):
+        time_percent=df.loc[index,'累计时间占比']
+        if df.loc[index,'GPS质量']=='优':
+            score+=time_percent*0
+        elif df.loc[index,'GPS质量']=='良':
+            score+=time_percent*0.3
+        elif df.loc[index,'GPS质量']=='合格':
+            score+=time_percent*0.5
+        elif df.loc[index,'GPS质量']=='掉线':
+            score+=time_percent*1
+    return (1-score)*100
+
+def gps_rank(df_gps_signal_table,df_gps,signal_rank,dist_factor):
+    '''gps信号质量分析函数,需要输入表格,df_gps,信号等级,权重'''
+    gps_signal_condition=(df_gps['gps_signal']==signal_rank)
+    dist=df_gps.loc[gps_signal_condition,'distance'].values.sum()
+    deltatime=df_gps.loc[gps_signal_condition,'deltatime'].values.sum()
+    df_gps_signal_table_condition=(df_gps_signal_table['gps_signal']==signal_rank)
+    df_gps_signal_table.loc[df_gps_signal_table_condition,'accum_distance']=dist/1000
+    df_gps_signal_table.loc[df_gps_signal_table_condition,'accum_deltatime']=deltatime
+    df_gps_signal_table.loc[df_gps_signal_table_condition,'accum_distance_factor']=dist/1000*dist_factor
+    return df_gps_signal_table
+
+def get_df_gps_score(starttime,endtime,df_gps):
+    '''对df_gps中的gps质量进行评分,返回一个数值'''
+    test_start_time=starttime#'2021-05-29 17:16:39'
+    test_end_time=endtime#'2021-05-29 20:08:08'
+
+    test_time_condition=(df_gps['time']>test_start_time)&(df_gps['time']<test_end_time)
+    df_gps_test=df_gps.loc[test_time_condition,:].copy()
+    df_gps_test=df_gps_test.reset_index(drop=True)#重置index
+    #按照deltatime打标签
+    gps_deltatime_bins=[0,30,60,120,10000]#优-良-合格-掉线
+    name=['优','良','合格','掉线']
+    df_gps_test['gps_signal']=pd.cut(df_gps_test['deltatime'], gps_deltatime_bins,labels=name)
+    df_gps_test['gps_signal'].value_counts()
+    #声明一个gps信号按类别统计table
+    df_gps_signal_table=pd.DataFrame()
+    df_gps_signal_table['gps_signal']=df_gps_test['gps_signal'].value_counts().index.tolist()
+    df_gps_signal_table['num']=df_gps_test['gps_signal'].value_counts().values.tolist()
+
+    #分类进行统计
+    df_gps_signal_table=gps_rank(df_gps_signal_table,df_gps_test,'优',1.00)
+    df_gps_signal_table=gps_rank(df_gps_signal_table,df_gps_test,'良',1.05)
+    df_gps_signal_table=gps_rank(df_gps_signal_table,df_gps_test,'合格',1.2)
+    df_gps_signal_table=gps_rank(df_gps_signal_table,df_gps_test,'掉线',1)
+
+    #次数占比,时间占比
+    all_num=df_gps_signal_table['num'].sum()
+    df_gps_signal_table['num_percent']=df_gps_signal_table['num']/all_num
+    all_accum_deltatime=df_gps_signal_table['accum_deltatime'].sum()
+    df_gps_signal_table['accum_deltatime_percent']=df_gps_signal_table['accum_deltatime']/all_accum_deltatime
+
+    #选择参数
+    df_gps_signal_table=df_gps_signal_table[['gps_signal','num','num_percent','accum_distance',
+                                            'accum_distance_factor','accum_deltatime','accum_deltatime_percent']]
+    df_gps_signal_table=df_gps_signal_table.rename(columns={'gps_signal':'GPS质量','num':'数量','num_percent':'数量占比',
+                                                        'accum_distance':'累计里程','accum_distance_factor':'累计里程修正值',
+                                                        'accum_deltatime':'累计时间','accum_deltatime_percent':'累计时间占比'})
+
+    df_gps_signal_table.loc[:,['GPS质量','累计时间','累计时间占比']]
+    gps_score=cal_gps_score(df_gps_signal_table)#调用函数计算gps评分
+    
+    #输出结果,评分
+    #print('From '+test_start_time+'  to '+test_end_time)
+    #print('GPS信号质量评分:'+str(gps_score))
+
+    return gps_score
+

+ 159 - 0
LIB/FRONTEND/odo/ProcessDfBms.py

@@ -0,0 +1,159 @@
+import pandas as pd
+import numpy as np
+from datetime import datetime
+from datetime import timedelta
+
+def get_bms_drive_timetable(df_bms,battery_mode):
+    '''对df_bms进行处理,得到行车的时间表。'''
+
+    #####################step1 先使用电流做充电状态的判断#############################################
+    if battery_mode==0:#mode=0,电流为正代表放电
+        condition_chrg=df_bms['bmspackcrnt']<0##根据电流,挑选充电状态
+        df_bms.loc[condition_chrg,'bscsta']='chrg'
+        condition_drive=df_bms['bmspackcrnt']>0.01##根据电流,挑选行驶状态
+        df_bms.loc[condition_drive,'bscsta']='drive'
+        df_bms.loc[~(condition_drive|condition_chrg),'bscsta']='idle'#静置状态
+    else :#mode=1,电流为负代表放电
+        condition_chrg=df_bms['bmspackcrnt']>0##根据电流,挑选充电状态
+        df_bms.loc[condition_chrg,'bscsta']='chrg'
+        condition_drive=df_bms['bmspackcrnt']<-0.01##根据电流,挑选行驶状态
+        df_bms.loc[condition_drive,'bscsta']='drive'
+        df_bms.loc[~(condition_drive|condition_chrg),'bscsta']='idle'#静置状态
+
+    #####################step2 对drive进行debounce,进入时立即进入,退出时debounce,5分钟。##########
+    index=0
+    debounce_row=10#debounce判断持续10行
+    debounce_time=300#debounce判断持续300秒
+    #对上一步初步状态进行二次处理
+    while index<(len(df_bms)-debounce_row):
+        mode_0=df_bms.loc[index,'bscsta']
+        mode_1=df_bms.loc[index+1,'bscsta']
+        #如果发现了边界行,则进行debounce判断
+        if (mode_0=='drive')&(mode_1!='drive'):#如果从drive变为idle
+            accum_subtime=0#累计时间初始化
+
+            for sub_index in range(debounce_row):#往下处理10行
+                sub_time=df_bms.loc[index+sub_index,'deltatime']
+                accum_subtime+=sub_time
+                #如果累计时间不到300秒,则设置为drive
+                if accum_subtime<debounce_time:
+                    df_bms.loc[index+sub_index,'bscsta']='drive'
+            index=index+debounce_row#处理10行以后的数据
+        #如果从idle变为drivemode,则将idle变为drive,包容前一行
+        elif (mode_0!='drive')&(mode_1=='drive'): 
+            df_bms.loc[index,'bscsta']='drive'
+            index=index+1
+        else:
+            index=index+1
+
+
+    #######################step3 对drivemode的时间进行分段###########################################
+    not_drive_flg=0#初始化
+    #输出drivemode的时间段,包含开始时间、结束时间
+    df_bms_drive_timetable_index=0
+    df_bms_drive_timetable=pd.DataFrame([],columns={'drive_start_time','drive_end_time',
+                                                    'gps_dist','predict_dist','drive_start_soc','drive_end_soc'})
+    for index in range(len(df_bms)):
+        temp_bscsta=df_bms.loc[index,'bscsta']
+        
+        if (temp_bscsta=='drive')&(not_drive_flg==0):
+            drive_start_time=df_bms.loc[index,'time']
+            not_drive_flg=1
+            df_bms_drive_timetable.loc[df_bms_drive_timetable_index,'drive_start_time']=drive_start_time
+            #startsoc
+            drive_start_soc=df_bms.loc[index,'bmspacksoc']
+            df_bms_drive_timetable.loc[df_bms_drive_timetable_index,'drive_start_soc']=drive_start_soc
+
+        elif (temp_bscsta!='drive')&(not_drive_flg==1):
+            drive_end_time=df_bms.loc[index,'time']
+            not_drive_flg=0
+            df_bms_drive_timetable.loc[df_bms_drive_timetable_index,'drive_end_time']=drive_end_time
+            #endsoc
+            drive_end_soc=df_bms.loc[index,'bmspacksoc']
+            df_bms_drive_timetable.loc[df_bms_drive_timetable_index,'drive_end_soc']=drive_end_soc
+            df_bms_drive_timetable_index+=1#index++
+
+    #删除时间信息不齐全的行
+    df_bms_drive_timetable=df_bms_drive_timetable.dropna(subset=['drive_end_time','drive_start_time'])
+    
+    return df_bms_drive_timetable
+
+
+def read_df_bms(path):
+    '''从路径中读取df_bms,进行预处理'''
+    df_bms=pd.read_csv(path, encoding='gbk')#编码方式gbk
+    #筛选表头,重命名
+    bms_columns=['时间戳','总电流[A]','总电压[V]','SOC[%]']
+    df_bms=df_bms.loc[:,bms_columns].copy()
+    df_bms.rename(columns = {"时间戳": "time", "总电流[A]": "bmspackcrnt", 
+                             "总电压[V]": "bmspackvol", "SOC[%]": "bmspacksoc"},inplace=True)#表头替换
+    #时间格式调整
+    df_bms['time']=df_bms['time'].apply(lambda x:datetime.strptime(x,'%Y-%m-%d %H:%M:%S'))#时间格式调整
+    #进行预处理
+    df_bms=df_add_deltatime(df_bms)#增加deltatime列 
+    return df_bms
+
+def preprocess_Df_Bms(df_bms):
+    '''对获得的df_bms,进行预处理'''
+    #筛选表头,重命名
+    bms_columns=['时间戳','总电流[A]','总电压[V]','SOC[%]']
+    df_bms=df_bms.loc[:,bms_columns].copy()
+    df_bms.rename(columns = {"时间戳": "time", "总电流[A]": "bmspackcrnt", 
+                             "总电压[V]": "bmspackvol", "SOC[%]": "bmspacksoc"},inplace=True)#表头替换
+    #删除空行
+    df_bms=df_bms.dropna(subset=['time'])
+    #删除时间重复的行,保留第一次出现的行
+    df_bms=df_bms.drop_duplicates(subset=['time'],keep='first')
+    #时间格式调整
+    df_bms['time']=df_bms['time'].apply(lambda x:datetime.strptime(x,'%Y-%m-%d %H:%M:%S'))#时间格式调整
+    #进行预处理
+    df_bms=df_add_deltatime(df_bms)#增加deltatime列 
+    return df_bms
+
+
+def df_add_deltatime(df_in):
+    '''Add a columns:deltatime,input df must have time column.'''
+    for i in range(len(df_in)):
+        #首行默认为0
+        if i==0:
+            df_in.loc[i,'deltatime']=0
+        #从第二行开始,计算i行到i-1行,GPS距离之差
+        else:
+            time1=df_in.loc[i-1,'time']
+            time2=df_in.loc[i,'time']
+            deltatime=time_interval(time1,time2)#计算时间差,返回单位为秒。
+            df_in.loc[i,'deltatime']=deltatime
+    return df_in
+
+
+def time_interval(time1,time2):
+    """
+    Calculate the time interval between two times,
+    return the seconds
+    """
+    deltatime=time2-time1
+    return deltatime.seconds
+
+
+def cal_deltasoc(df_bms,start_time,end_time):
+    '''输入开始时间和结束时间,返回deltasoc,此处将deltasoc*1既等效为unrecorded_odo.'''
+    time_condition=(df_bms['time']>start_time)&(df_bms['time']<end_time)
+    df_bms_sub=df_bms.loc[time_condition,:].copy()
+    if len(df_bms_sub)>=2:
+        
+        df_bms_head=df_bms_sub.head(1).copy()#首行
+        df_bms_startsoc=df_bms_head['bmspacksoc'].values[0]
+        df_bms_tail=df_bms_sub.tail(1).copy()#尾行
+        df_bms_endsoc=df_bms_tail['bmspacksoc'].values[0]
+        delta_soc=df_bms_startsoc-df_bms_endsoc
+        
+        if delta_soc>0:
+            #如果df_bms出现时间不连续,则先计算deltasoc,deltasoc每变化1,续驶里程增加1,
+            unrecorded_odo=delta_soc*1
+            #print('From '+str(start_time)+' to  '+str(end_time)+' soc decrease:  '+str(delta_soc))
+        else:
+            unrecorded_odo=0#如果deltasoc不大于0,说明在充电,或者静置不动    
+    #如果行数少于2,无法计算
+    else:
+        unrecorded_odo=0
+    return unrecorded_odo

+ 139 - 0
LIB/FRONTEND/odo/ProcessDfGps.py

@@ -0,0 +1,139 @@
+import pandas as pd
+import numpy as np
+from datetime import datetime
+from datetime import timedelta
+from ProcessDfBms import *
+from math import radians, cos, sin, asin, sqrt
+
+def cal_unrecorded_gps(df_in,df_bms):
+    '''筛选出现gps时间断点的数据,用df_bms数据补齐,df_in为df_gps表格。'''
+    #未记录到的odo总和
+    accum_unrecorded_odo=0
+
+    #设置丢失的判断条件,获得信息丢失行的index
+    condition1=df_in['deltatime']>60*3#时间间隔大于3分钟。说明数据掉线了。
+    condition2=(df_in['deltatime']>90*1)&(df_in['distance']>1000)#时间间隔大于*分钟,且Distance间隔大于*,代表掉线了。
+    signal_start_list=df_in.loc[condition1|condition2,:].index.to_list()#信息丢失行
+    #如果第0行属于信息丢失行,则删除,因为需要index-1行
+    try:
+        signal_start_list.remove(0)
+    except:
+        pass
+    else:
+        pass
+    #筛选出所有GPS信号丢失,对应的开始时间-结束时间对。
+    if len(signal_start_list)>0:
+        signal_end_list=[num-1 for num in signal_start_list]#信息丢失行的前一行,此处可能如果是首行,可能会有bug。
+        pick_gps_list=[0]+signal_start_list+signal_end_list+[len(df_in)-1]#首行+尾行+信号开始行+信号结束行
+        pick_gps_list=sorted(pick_gps_list)#重新排序
+
+    #有出现信号断点的行,则进行以下计算。
+    if len(signal_start_list)>0:
+        #针对每个时间对,计算unrecorded odo
+        for start_time_index,end_time_index in zip(signal_start_list,signal_end_list):
+            last_end_time=df_in.loc[end_time_index,'time']
+            this_start_time=df_in.loc[start_time_index,'time']
+            #print('gps signal loss from: '+str(last_end_time)+'-to-'+str(this_start_time))
+            #使用cal_delatasoc计算预估里程
+            unrecorded_odo=cal_deltasoc(df_bms,last_end_time,this_start_time)
+            accum_unrecorded_odo+=unrecorded_odo
+        #print('accum_unrecorded_odo:'+str(accum_unrecorded_odo))
+    else:
+        pass
+    
+    return accum_unrecorded_odo
+
+
+def df_add_avgspeed(df_in):
+    '''Add a columns:avgspeed ,input df must have deltatime,distance column.'''
+    for i in range(len(df_in)):
+        #首行默认为0
+        if i==0:
+            df_in.loc[i,'avgspeed']=0
+        #从第二行开始,计算平均速度
+        else:
+            deltatime=df_in.loc[i,'deltatime']
+            distance=df_in.loc[i,'distance']
+            avgspeed=(distance/1000)/(deltatime/3600)
+            df_in.loc[i,'avgspeed']=avgspeed
+    return df_in
+
+
+def read_df_gps(path):
+    df_gps=pd.read_csv(path, encoding='gbk')#编码方式gbk
+    #重置表头
+    df_gps.rename(columns = {"时间戳": "time", "纬度":"lat", "经度":"lng", 
+                             "卫星数":"sat_num", "海拔m":"height","速度[km/h]":"speed"},  inplace=True)
+    #时间格式调整
+    df_gps['time']=pd.to_datetime(df_gps['time'])
+    #对gps进行清洗
+    df_gps=df_add_distance(df_gps)#增加distance列
+    condition=df_gps['distance']<20000#删除GPS漂移过远的点,可能为GPS错误值
+    df_gps=df_gps.loc[condition,:].copy()#删除condition中,avgspd过大的部分,很可能伴随着GPS的漂移。
+    df_gps=df_gps.reset_index(drop=True)#重置index
+    #进行预处理
+    df_gps=df_add_distance(df_gps)#增加distance列,再算一次distance
+    df_gps=df_add_deltatime(df_gps)#增加deltatime列
+    df_gps=df_add_avgspeed(df_gps)#增加avgspeed列
+
+    #df_gps.to_excel('df_gps.xlsx',sheet_name='Sheet1')
+    return df_gps
+
+def preprocess_Df_Gps(df_gps):
+    '''对Df_Gps进行预处理'''
+    #重置表头
+    df_gps.rename(columns = {"时间戳": "time", "纬度":"lat", "经度":"lng", 
+                             "卫星数":"sat_num", "海拔m":"height","速度[km/h]":"speed"},  inplace=True)
+    #删除含有空数据的行
+    df_gps=df_gps.dropna(subset=['time','lat','lng'])
+    #删除时间重复的行,保留第一次出现的行
+    df_gps=df_gps.drop_duplicates(subset=['time'],keep='first')
+    #时间格式调整
+    df_gps['time']=pd.to_datetime(df_gps['time'])
+    
+    #对gps进行清洗
+    df_gps=df_add_distance(df_gps)#增加distance列
+    condition=df_gps['distance']<20000#删除GPS漂移过远的点,可能为GPS错误值
+    df_gps=df_gps.loc[condition,:].copy()#删除condition中,avgspd过大的部分,很可能伴随着GPS的漂移。
+    df_gps=df_gps.reset_index(drop=True)#重置index
+    #进行预处理
+    df_gps=df_add_distance(df_gps)#增加distance列,再算一次distance
+    df_gps=df_add_deltatime(df_gps)#增加deltatime列
+    df_gps=df_gps.loc[df_gps['deltatime']>0.01,:].copy()#删除deltatime=0的列,两个时间戳相同,无法求速度。
+    df_gps=df_add_avgspeed(df_gps)#增加avgspeed列
+
+    #df_gps.to_excel('df_gps.xlsx',sheet_name='Sheet1')
+    return df_gps
+
+
+def df_add_distance(df_in):
+    '''Add a columns:distance,input df must have lng,lat columns.'''
+    for i in range(len(df_in)):
+        #首行默认为0
+        if i==0:
+            df_in.loc[i,'distance']=0
+        #从第二行开始,计算i行到i-1行,GPS距离之差
+        else:
+            lon1=df_in.loc[i-1,'lng']
+            lat1=df_in.loc[i-1,'lat']
+            lon2=df_in.loc[i,'lng']
+            lat2=df_in.loc[i,'lat']
+            distance=haversine(lon1,lat1,lon2,lat2)#haversine公式计算距离差
+            df_in.loc[i,'distance']=distance    
+    return df_in
+
+
+def haversine(lon1, lat1, lon2, lat2):
+    """
+    Calculate the great circle distance between two points 
+    on the earth (specified in decimal degrees)
+    """
+    # 将十进制度数转化为弧度
+    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
+    # haversine公式
+    dlon = lon2 - lon1 
+    dlat = lat2 - lat1 
+    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
+    c = 2 * asin(sqrt(a)) 
+    r = 6371 # 地球平均半径,单位为公里
+    return c * r * 1000

+ 293 - 0
LIB/FRONTEND/odo/UpdtFct.py

@@ -0,0 +1,293 @@
+import pandas as pd
+import pymysql
+from sqlalchemy import create_engine
+import datetime
+
+#建立引擎
+engine = create_engine(str(r"mysql+mysqldb://%s:" + '%s' + "@%s/%s") % ('root', 'pengmin', 'localhost', 'qixiangdb'))
+
+conn_qx = pymysql.connect(
+        host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com',
+        user='qx_cas',
+        password='Qx@123456',#Qx@123456
+        database='qx_cas',
+        charset='utf8'
+    )
+
+conn_local = pymysql.connect(
+        host='localhost',
+        user='root',
+        password='pengmin',
+        database='qixiangdb',
+        charset='utf8'
+    )
+
+def getNextSoc(start_soc):
+    '''输入当前的soc,寻找目标soc函数'''
+    if start_soc>80:
+        next_soc=80
+    elif start_soc>60:
+        next_soc=60
+    elif start_soc>40:
+        next_soc=40
+    elif start_soc>20:
+        next_soc=20
+    else:
+        next_soc=1
+    return next_soc
+
+def updtSnFct(sn_factor_df,end_soc,delta_range,range_soc):
+    '''输入当前的soc区间段,里程变量量,soc变化量,输出新的df
+    sn_factor_df为dataframe,delta_range单位为km,range_soc单位为km/persoc'''
+    if end_soc==80:
+        updtFctByCol(sn_factor_df,'a0',delta_range,range_soc)
+    elif end_soc==60:
+        updtFctByCol(sn_factor_df,'a1',delta_range,range_soc)
+    elif end_soc==40:
+        updtFctByCol(sn_factor_df,'a2',delta_range,range_soc)
+    elif end_soc==20:
+        updtFctByCol(sn_factor_df,'a3',delta_range,range_soc)
+    elif end_soc<20:
+        updtFctByCol(sn_factor_df,'a4',delta_range,range_soc)
+    return sn_factor_df
+
+def updtFctByCol(sn_factor_df,colmun_name,delta_range,range_soc):
+    '''更新制定列的factor,sn_factor_df为dataframe,新的系数更新到第一行。delta_range单位为km,
+    range_soc单位为km/persoc,默认按照100km更新续驶里程权重'''
+    range_soc_old=sn_factor_df.loc[0,colmun_name]#读取第0行的老factor
+    debounce_range=200#更新权重
+    new_factor=range_soc*((delta_range)/debounce_range)+range_soc_old*(1-(delta_range)/debounce_range)
+    #在第1行,存储新的factor
+    sn_factor_df.loc[1,colmun_name]=new_factor
+    return sn_factor_df
+
+def updtTodayFct(factor_input,sn_day_df):
+    '''更新今日的Factor***'''
+    sn_factor_df_last=factor_input
+    start_soc=sn_day_df.loc[0,'soc']
+    next_soc=getNextSoc(start_soc)
+    start_range=sn_day_df.loc[0,'vehodo']
+    sn=sn_day_df.loc[0,'name']
+
+    for index in range(len(sn_day_df)-1):
+    #寻找分割点,
+        index_soc=sn_day_df.loc[index,'soc']#当前行soc
+        next_index_soc=sn_day_df.loc[index+1,'soc']#下一行soc
+
+        if (index_soc>=next_soc)&(next_index_soc<next_soc):#当前行高,下一行低
+            delta_soc_tonext=start_soc-next_soc#两个距离点的soc差,单位为%
+            delta_range_tonext=sn_day_df.loc[index,'vehodo']-start_range#两个时间点的距离差,单位为m
+            delta_range_tonext_km=delta_range_tonext/1000#两个时间点的距离差,单位为km
+            range_soc_tonext=(delta_range_tonext/1000)/delta_soc_tonext#单位soc可行驶的公里数
+            print(sn+'start_soc: '+str(start_soc),'next_soc: '+str(next_soc),'delta_vehodo; '+str(round(delta_range_tonext_km,3))
+            +'km'+' range_soc:'+str(round(range_soc_tonext,3)))
+
+            if (delta_range_tonext_km)>1:
+                sn_factor_df_last=updtSnFct(sn_factor_df_last,next_soc,delta_range_tonext_km,range_soc_tonext)
+            
+            start_soc=next_index_soc#变更开始soc
+            next_soc=getNextSoc(start_soc)#变更结束soc
+            start_range=sn_day_df.loc[index+1,'vehodo']#变更开始里程    
+
+    return sn_factor_df_last
+
+def snDayDfPreProcess(sn_day_df):
+    '''预处理,判断是否在dirvemode,获取drivemode条件下的累计行驶距离。
+    增加delta_soc列,drive_flg列,vehodo列'''
+    sn_day_df=sn_day_df.reset_index(drop=True)#重置index
+    #增加列,计算delta_soc
+    for index in range(len(sn_day_df)):
+        if index==0:
+            sn_day_df.loc[index,'delta_soc']=0
+        else:
+            sn_day_df.loc[index,'delta_soc']=sn_day_df.loc[index,'soc']-sn_day_df.loc[index-1,'soc']
+    #增加列,判断是否在drive状态
+    drive_flg=False
+    accum_distance=0
+    for index in range(len(sn_day_df)):
+        if index==0:
+            sn_day_df.loc[index,'drive_status']=drive_flg
+            sn_day_df.loc[index,'vehodo']=0
+        else:
+            if (sn_day_df.loc[index,'delta_soc']<-0.1)|\
+                ((sn_day_df.loc[index,'delta_soc']<=0)&(sn_day_df.loc[index,'distance']>500)):#soc处于下降状态,说明在drive
+                drive_flg=True#置true
+            elif sn_day_df.loc[index,'delta_soc']>0.1:#soc处于上升状态,说明不在drive
+                drive_flg=False#置false
+                accum_distance=0#清零
+            sn_day_df.loc[index,'drive_flg']=drive_flg
+            accum_distance+=sn_day_df.loc[index,'distance']#对行驶里程进行累加
+            sn_day_df.loc[index,'vehodo']=accum_distance
+    #筛选所有的drive信息行
+    sn_day_drive_df=sn_day_df.loc[sn_day_df['drive_flg']==True,:]
+    sn_day_drive_df=sn_day_drive_df.reset_index(drop=True)#重置index
+    
+    return sn_day_drive_df 
+
+def updtAllSnFct(start_date,end_date):
+    '''计算开始时间到结束时间的,所有sn的factor'''
+    start_date_datetime=datetime.datetime.strptime(start_date,'%Y-%m-%d')#开始时间
+    end_date_datetime=datetime.datetime.strptime(end_date,'%Y-%m-%d')#开始时间
+    delta_day=(end_date_datetime-start_date_datetime).days#间隔天数
+    i=1
+    while i<=delta_day:
+        end_date=(start_date_datetime+datetime.timedelta(days=i)).strftime("%Y-%m-%d")
+        updtAllSnTodayFct(start_date,end_date)#调用函数
+        print('update all sn factor from '+start_date+" to "+end_date)
+        start_date=end_date
+        i+=1#自加
+
+def updtAllSnTodayFct(start_date,end_date):
+    ''''更新今天所有sn的factorx信息,start_date和end_date相隔一天。此处还可优化'''
+    start_date_str="'"+start_date+"'"
+    end_date_str="'"+end_date+"'"
+    sql_cmd="select * from drive_info where time between "+start_date_str+" and "+end_date_str+" and distance!=0;"
+    range_soc_df = pd.read_sql(sql_cmd, conn_qx)#使用read_sql方法查询qx数据库
+
+    #筛选出所有当日数据之后,筛选当日有更新的sn
+    today_sn_list=range_soc_df['name'].unique().tolist()#[:100]#先一次更新5个
+    #建立空的dataframe,用于承接所有更新的factor信息
+    today_sn_fct_df=pd.DataFrame([],columns=['sn','date','a0','a1','a2','a3','a4'])
+
+    for sn in today_sn_list:
+        #寻找factor_df,里面是否有sn号,如果没有sn对应信息,则新增信息。
+        sn_str="'"+sn+"'"
+        sql_cmd2="select sn,date,a0,a1,a2,a3,a4 from tb_sn_factor where date<"+start_date_str+" and sn="+sn_str
+        #此处可以限定每次查询的数量,例如不高于5行
+        factor_df=pd.read_sql(sql_cmd2, conn_local)#使用read_sql方法查询local数据库
+
+        #按照sn号和日期进行去重,避免运行时重复产生factor数据,保留第一次出现的行。
+        factor_df=factor_df.drop_duplicates(subset=['sn','date'],keep='first')
+
+        if len(factor_df)==0:
+            #如果没有搜索到factor历史数据,则声明一个新的进行初始化
+            start_date_datetime=datetime.datetime.strptime(start_date,'%Y-%m-%d')
+            yesterday=(start_date_datetime+datetime.timedelta(days=-1)).strftime("%Y-%m-%d")
+            #为sn申请一个新的factor,初始值为1
+            factor_df=pd.DataFrame({'sn':sn,'date':yesterday,'a0':[1],'a1':[1],'a2':[1],'a3':[1],'a4':[1]})
+        sn_factor_df=factor_df.loc[factor_df['sn']==sn,:]#筛选sn对应的factor
+        sn_factor_df=sn_factor_df.sort_values(by='date',ascending='True')#按照日期排序
+
+        sn_factor_df_last=sn_factor_df.tail(1).copy()#寻找最后一行,代表最近日期
+        sn_factor_df_last=sn_factor_df_last.append(sn_factor_df_last)#新增加一行,用于存储新的factor
+        sn_factor_df_last=sn_factor_df_last.reset_index(drop=True)#重置index
+        sn_factor_df_last.loc[1,'date']=start_date#更改后一行的date为当前日期
+        #筛选对应车辆的信息
+        condition_sn=(range_soc_df['name']==sn)
+        sn_day_df=range_soc_df.loc[condition_sn,:].copy()
+        sn_day_df=sn_day_df.reset_index(drop=True)
+        #使用updtTodayFct函数更新今天的factor
+        if len(sn_day_df)>=2:
+            #使用process函数,进行预处理
+            sn_day_df=snDayDfPreProcess(sn_day_df)#预处理函数
+            if len(sn_day_df)>=2:
+                sn_factor_df_new=updtTodayFct(sn_factor_df_last,sn_day_df)#
+                today_sn_fct_df=today_sn_fct_df.append(sn_factor_df_new.loc[1,:])#筛选第一行,进行拼接,最后写入到数据库中
+    
+    #将today_sn_fct_df写入到数据库中,今天所有factor更新的系数,一次写入。
+    if len(today_sn_fct_df)>=1:
+        today_sn_fct_df.to_sql('tb_sn_factor',con=engine,chunksize=10000,if_exists='append',index=False)
+
+def updtOneSnFct(sn,start_date,end_date):
+    '''计算开始时间到结束时间的,一个sn的所有factor'''
+    start_date_datetime=datetime.datetime.strptime(start_date,'%Y-%m-%d')#开始时间
+    end_date_datetime=datetime.datetime.strptime(end_date,'%Y-%m-%d')#开始时间
+    delta_day=(end_date_datetime-start_date_datetime).days#间隔天数
+    i=1
+    while i<=delta_day:
+        end_date=(start_date_datetime+datetime.timedelta(days=i)).strftime("%Y-%m-%d")
+        updtOneSnTodayFct(sn,start_date,end_date)#调用函数
+        print('update one sn factor from '+start_date+" to "+end_date)
+        start_date=end_date
+        i+=1#自加
+
+def updtOneSnTodayFct(sn,start_date,end_date):
+    start_date_str="'"+start_date+"'"
+    end_date_str="'"+end_date+"'"
+    sn_str="'"+sn+"'"
+    sql_cmd="select * from drive_info where time between "+start_date_str+" and "+end_date_str+\
+    " and distance!=0 and name="+sn_str
+    range_soc_df = pd.read_sql(sql_cmd, conn_qx)#使用read_sql方法查询qx数据库
+
+    if len(range_soc_df)>0:
+        #筛选出所有当日数据之后,筛选当日有更新的sn
+        today_sn_list=range_soc_df['name'].unique().tolist()
+        #建立空的dataframe,用于承接所有更新的factor信息
+        today_sn_fct_df=pd.DataFrame([],columns=['sn','date','a0','a1','a2','a3','a4'])
+
+        for sn in today_sn_list:
+            #寻找factor_df,里面是否有sn号,如果没有sn对应信息,则新增信息。
+            sn_str="'"+sn+"'"
+            sql_cmd2="select sn,date,a0,a1,a2,a3,a4 from tb_sn_factor where date<"+start_date_str+" and sn="+sn_str
+            factor_df=pd.read_sql(sql_cmd2, conn_local)#使用read_sql方法查询local数据库
+
+            #按照sn号和日期进行去重,避免运行时重复产生factor数据,保留第一次出现的行。
+            factor_df=factor_df.drop_duplicates(subset=['sn','date'],keep='first')
+
+            if len(factor_df)==0:
+                #如果没有搜索到factor历史数据,则声明一个新的进行初始化
+                start_date_datetime=datetime.datetime.strptime(start_date,'%Y-%m-%d')
+                yesterday=(start_date_datetime+datetime.timedelta(days=-1)).strftime("%Y-%m-%d")
+                factor_df=pd.DataFrame({'sn':sn,'date':yesterday,'a0':[1],'a1':[1],'a2':[1],'a3':[1],'a4':[1]})
+                today_sn_fct_df=today_sn_fct_df.append(factor_df.loc[0,:])#将初始化的行记录到数据库
+
+            sn_factor_df=factor_df.loc[factor_df['sn']==sn,:]#筛选sn对应的factor
+            
+            sn_factor_df=sn_factor_df.sort_values(by='date',ascending='True')#按照日期排序
+            sn_factor_df_last=sn_factor_df.tail(1).copy()#寻找最后一行,代表最近日期
+            sn_factor_df_last=sn_factor_df_last.append(sn_factor_df_last)#新增加一行,用于存储新的factor
+            sn_factor_df_last=sn_factor_df_last.reset_index(drop=True)#重置index
+            sn_factor_df_last.loc[1,'date']=start_date#更改后一行的date为当前日期
+            #筛选对应车辆的信息
+            condition_sn=(range_soc_df['name']==sn)
+            sn_day_df=range_soc_df.loc[condition_sn,:].copy()
+            sn_day_df=sn_day_df.reset_index(drop=True)
+            #使用updtTodayFct函数更新今天的factor
+            if len(sn_day_df)>=2:
+                #使用process函数,进行预处理
+                sn_day_df=snDayDfPreProcess(sn_day_df)#!!!!!!!!!!!增加
+                if len(sn_day_df)>=2:
+                    sn_factor_df_new=updtTodayFct(sn_factor_df_last,sn_day_df)#
+                    today_sn_fct_df=today_sn_fct_df.append(sn_factor_df_new.loc[1,:])#筛选第一行,进行拼接,最后写入到数据库中
+        
+        # #将today_sn_fct_df写入到数据库中
+        if len(today_sn_fct_df)>=1:
+            today_sn_fct_df.to_sql('tb_sn_factor',con=engine,chunksize=10000,if_exists='append',index=False)
+            # print(sn+' factor will be update in table tb_sn_factor!')
+        return today_sn_fct_df
+
+
+
+
+
+# def updtASnTodayFct(start_date,end_date,today_sn_list):
+
+#     sql_cmd="select * from qixiang_test where time>='"+start_date+"' and time<='"+end_date+"'"
+#     range_soc_df = pd.read_sql(sql_cmd, conn)#使用read_sql方法查询数据库
+
+#     sql_cmd2="select sn,date,a0,a1,a2,a3,a4 from tb_sn_factor where date<'"+start_date+"'"
+#     factor_df=pd.read_sql(sql_cmd2, conn)#使用read_sql方法查询数据库
+
+#     #筛选出所有当日数据之后,筛选当日有更新的sn
+#     # today_sn_list=range_soc_df['sn'].unique().tolist()
+#     # today_sn_list=today_sn_list[:10]#更新若干个
+#     #建立空的dataframe,用于承接所有更新的factor信息
+#     today_sn_fct_df=pd.DataFrame([],columns=['sn','date','a0','a1','a2','a3','a4'])
+
+#     for sn in today_sn_list:
+#         sn_factor_df=factor_df.loc[factor_df['sn']==sn,:]#筛选sn对应的factor
+#         sn_factor_df=sn_factor_df.sort_values(by='date',ascending='True')#按照日期排序
+#         sn_factor_df_last=sn_factor_df.tail(1).copy()#寻找最后一行,代表最近日期
+#         sn_factor_df_last=sn_factor_df_last.append(sn_factor_df_last)#新增加一行,用于存储新的factor
+#         sn_factor_df_last=sn_factor_df_last.reset_index(drop=True)#重置index
+#         sn_factor_df_last.loc[1,'date']=start_date#更改后一行的date为当前日期
+#         #筛选对应车辆的信息
+#         condition_sn=(range_soc_df['sn']==sn)
+#         sn_day_df=range_soc_df.loc[condition_sn,:].copy()
+#         sn_day_df=sn_day_df.reset_index(drop=True)
+#         #使用updtTodayFct函数更新今天的factor
+#         sn_factor_df_new=updtTodayFct(sn_factor_df_last,sn_day_df)
+#         today_sn_fct_df=today_sn_fct_df.append(sn_factor_df_new.loc[1,:])#筛选第一行,进行拼接,最后写入到数据库中
+    
+#     #将today_sn_fct_df写入到数据库中
+#     today_sn_fct_df.to_sql('tb_sn_factor',con=engine,chunksize=10000,if_exists='append',index=False)

+ 28 - 0
LIB/FRONTEND/odo/UpdtFct_Main.py

@@ -0,0 +1,28 @@
+import pandas as pd
+import pymysql
+from sqlalchemy import create_engine
+import datetime
+from UpdtFct import *
+
+
+conn_qx = pymysql.connect(
+        host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com',
+        user='qx_cas',
+        password='Qx@123456',#Qx@123456
+        database='qx_cas',
+        charset='utf8'
+    )
+
+conn_local = pymysql.connect(
+        host='localhost',
+        user='root',
+        password='pengmin',
+        database='qixiangdb',
+        charset='utf8'
+    )
+
+#指定开始时间,结束时间,更新所有sn的factor
+start_date="2021-07-18"
+end_date="2021-08-01"
+
+updtAllSnFct(start_date,end_date)

BIN
LIB/FRONTEND/odo/asset_table.xlsx


+ 66 - 0
LIB/FRONTEND/odo/main_1.py

@@ -0,0 +1,66 @@
+#coding=utf-8
+# 计算里程
+from math import radians, cos, sin, asin, sqrt
+import pandas as pd
+import numpy as np
+from datetime import datetime
+from datetime import timedelta
+
+from GpsRank import *
+from ProcessDfBms import *
+from ProcessDfGps import *
+from LIB.MIDDLE.odo.CalDist import *
+from LIB.BACKEND import DBManager
+import pdb
+
+asset_table_path='asset_table.xlsx'
+# drive_info_path='D:\\work\\Qixiang\\data_analyze_platform\\pengmin\\AllCarDist\\drive_info.xlsx'
+asset_sheet_num=1
+usecols_list=[4,5]
+
+asset_table=pd.read_excel(asset_table_path,sheet_name=asset_sheet_num,skiprows=1,usecols=usecols_list)
+SN_list=asset_table['SN号'].values.tolist()
+print('从6060sheet读取到:'+str(len(SN_list))+'行')
+asset_table=asset_table.rename(columns={'SN号':'SN','状态':'state'})
+
+asset_table.set_index(["SN"],inplace=True)
+col_name=asset_table.columns.tolist()
+col_name.extend(['range','accum_soc','day_start_soc','day_end_soc','day_start_time','day_end_time'])
+asset_table=asset_table.reindex(columns=col_name)
+
+start_hour='00:00:00'#每日查询最早时间
+end_hour='23:59:00'#每日查询最晚时间
+
+
+date_index=pd.date_range('2021-07-31','2021-07-31')
+for date in date_index:
+    '''遍历日期'''
+
+    str_date=str(date)[:10]
+    input_starttime=str_date+' '+start_hour
+    input_endtime=str_date+' '+end_hour
+    test_day=str_date[5:10]#月-日,用于建立sheet
+    drive_info_path='6060\\drive_info'+test_day+'_50_end_'+'.xlsx'
+
+    print(input_starttime)
+
+    drive_info_aday=pd.DataFrame()
+    SN_list_short=SN_list#先选择了0:50,50:end
+
+    for SN in SN_list_short:
+        '''遍历SN号'''
+        SN=SN.strip('\t')
+        SN=SN.strip('\n')
+
+        try:
+            range=GetDistInfo(SN,input_starttime,input_endtime)
+            range_df=pd.DataFrame([range])
+            drive_info_aday=pd.concat([drive_info_aday,range_df],axis=0)
+
+        except:
+            print(SN+' '+test_day+'fail')
+        else:
+            pass
+            #print(SN+' '+test_day+'success')
+
+    drive_info_aday.to_excel(drive_info_path,sheet_name=test_day)#sheet名称为testday

+ 309 - 0
LIB/FRONTEND/soh/LFPSoh 20210711.py

@@ -0,0 +1,309 @@
+# 获取数据
+from LIB.BACKEND import DBManager
+
+import os
+import pandas as pd
+import numpy as np
+import datetime
+# import matplotlib.pyplot as plt
+
+#参数输入
+Capacity = 54
+PackFullChrgVolt=69.99
+CellFullChrgVolt=3.5
+CellVoltNums=20
+CellTempNums=4
+FullChrgSoc=98
+PeakSoc=57
+# #40Ah-OCV
+# LookTab_SOC = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100]
+# LookTab_OCV = [3.3159, 3.4502, 3.4904, 3.5277, 3.5590, 3.5888, 3.6146, 3.6312, 3.6467, 3.6642, 3.6865, 3.7171, 3.7617,
+#                3.8031, 3.8440, 3.8888, 3.9376, 3.9891, 4.0451, 4.1068, 4.1830]
+#55Ah-OCV
+LookTab_SOC = [0.00, 	2.40, 	6.38, 	10.37, 	14.35, 	18.33, 	22.32, 	26.30, 	30.28, 	35.26, 	40.24, 	45.22, 	50.20, 	54.19, 	58.17, 	60.16, 	65.14, 	70.12, 	75.10, 	80.08, 	84.06, 	88.05, 	92.03, 	96.02, 	100.00]
+LookTab_OCV = [2.7151,	3.0298,	3.1935,	3.2009,	3.2167,	3.2393,	3.2561,	3.2703,	3.2843,	3.2871,	3.2874,	3.2868,	3.2896,	3.2917,	3.2967,	3.3128,	3.3283,	3.3286,	3.3287,	3.3288,	3.3289,	3.3296,	3.3302,	3.3314,	3.3429]
+
+#定义滑动滤波函数
+def np_move_avg(a, n, mode="same"):
+    return (np.convolve(a, np.ones((n,)) / n, mode=mode))
+
+#参数初始化
+dvdq_soh=[]
+dvdq_soh_err=[]
+bms_soh=[]
+dvdq_time=[]
+dvdq_sohcfd=[]
+sn_list=[]
+
+#获取数据时间段
+now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+now_time=datetime.datetime.strptime(now_time,'%Y-%m-%d %H:%M:%S')
+start_time=now_time-datetime.timedelta(days=30)
+end_time=str(now_time)
+strat_time=str(start_time)
+
+#输入一个含有‘SN号’的xlsx
+SNdata = pd.read_excel('骑享资产梳理-20210621.xlsx', sheet_name='6060')
+SNnums=SNdata['SN号']
+for k in range(15):
+    
+    SNnum=str(SNnums[k])
+    sn = SNnum
+    st = '2021-07-06 00:00:00'
+    et = '2021-07-07 20:00:00'
+
+    dbManager = DBManager.DBManager()
+    df_data = dbManager.get_data(sn=sn, start_time=st, end_time=et, data_groups=['bms'])
+    data = df_data['bms']
+
+    packcrnt=data['总电流[A]']
+    packvolt=data['总电压[V]']
+    SOC=data['SOC[%]']
+    SOH=data['SOH[%]']
+    bmsstat=data['充电状态']
+    time= pd.to_datetime(data['时间戳'], format='%Y-%m-%d %H:%M:%S')
+
+    #第一步:筛选充电数据
+    ChgStart=[]
+    ChgEnd=[]
+    for i in range(3, len(time) - 3):
+        if i==3 and bmsstat[i]==2 and bmsstat[i+1]==2 and bmsstat[i+2]==2:
+            ChgStart.append(i)
+        elif bmsstat[i-2]!=2 and bmsstat[i-1]!=2 and bmsstat[i]==2:
+            ChgStart.append(i)
+        elif bmsstat[i-1]==2 and bmsstat[i]!=2 and bmsstat[i+1]!=2:
+            ChgEnd.append(i)
+        elif i == (len(time) - 4) and bmsstat[len(bmsstat)-1] == 2 and bmsstat[len(bmsstat)-2] == 2:
+            ChgEnd.append(len(time)-1)
+
+    #第二步:筛选充电起始Soc<48%,电芯温度>15℃,且满充的数据
+    ChgStartValid1=[]
+    ChgEndValid1=[]
+    ChgStartValid2=[]
+    ChgEndValid2=[]
+
+    for i in range(min(len(ChgStart),len(ChgEnd))):
+
+        #获取最小温度值
+        celltemp = []
+        for j in range(1, CellTempNums+1):
+            s = str(j)
+            temp = data['单体温度' + s]
+            celltemp.append(temp[ChgEnd[i]])
+
+        #寻找最大电压值
+        cellvolt = []
+        for j in range(1, CellVoltNums+1):
+            s = str(j)
+            volt = max(data['单体电压' + s][ChgStart[i]:ChgEnd[i]]/1000)
+            cellvolt.append(volt)
+
+        #筛选满足2点法计算的数据
+        StandingTime=0
+        if max(cellvolt)>CellFullChrgVolt and SOC[ChgStart[i]]<30 and min(celltemp)>5:
+            for k in reversed(range(ChgStart[i])):
+                if abs(packcrnt[k - 2]) < 0.01:
+                    StandingTime = StandingTime + (time[k] - time[k-1]).total_seconds()
+                    if StandingTime > 600:  # 筛选静置时间>10min
+                        ChgStartValid1.append(ChgStart[i])
+                        ChgEndValid1.append(ChgEnd[i])
+                        break
+                else:
+                    break
+
+        #筛选满足DV/DQ方法的数据
+        if max(cellvolt)>CellFullChrgVolt and SOC[ChgStart[i]]<45 and min(celltemp)>5:
+            if ((time[ChgEnd[i]]-time[ChgStart[i]]).total_seconds())/(ChgEnd[i]-ChgStart[i])<60:
+                ChgStartValid2.append(ChgStart[i])
+                ChgEndValid2.append(ChgEnd[i])
+
+    #第三步:计算充电Soc和Soh
+
+    # 两点法计算soh
+    Soc=[]
+    Time=[]
+    Soc_Err=[]
+    Bms_Soc=[]
+
+    Soh1=[]
+    Time1=[]
+    Bms_Soh1=[]
+    Soh_Err1=[]
+
+    for i in range(len(ChgStartValid1)):
+
+        #寻找最大电压值
+        cellvolt = []
+        for j in range(1, CellVoltNums+1):
+            s = str(j)
+            volt = max(data['单体电压' + s])
+            cellvolt.append(volt)
+        voltmax_index = cellvolt.index(max(cellvolt)) + 1
+        cellvolt = data['单体电压' + str(voltmax_index)] / 1000
+
+        #soc
+        Soc.append(np.interp(cellvolt[ChgStartValid1[i]-3],LookTab_OCV,LookTab_SOC))
+        Time.append(time[ChgStartValid1[i]-3])
+        Bms_Soc.append(SOC[ChgStartValid1[i]-3])
+        Soc_Err.append(Bms_Soc[-1]-Soc[-1])
+
+        #soh
+        Ocv_Soc=np.interp(cellvolt[ChgStartValid1[i]-3],LookTab_OCV,LookTab_SOC)
+        Ah=0
+
+        for j in range(ChgStartValid1[i],ChgEndValid1[i]):
+            #计算soc
+            Step=(time[j]-time[j-1]).total_seconds()
+            Time.append(time[j])
+            Bms_Soc.append(SOC[j])
+            if Soc[-1]-(packcrnt[j]*Step*100)/(3600*Capacity)<100:
+                Soc.append(Soc[-1]-(packcrnt[j]*Step*100)/(3600*Capacity))
+            else:
+                Soc.append(100)
+            Soc_Err.append(Bms_Soc[-1] - Soc[-1])
+
+            #两点法计算soh
+            Ah=Ah-packcrnt[j]*Step/3600
+        Soh1.append(Ah*100/((FullChrgSoc-Ocv_Soc)*0.01*Capacity))
+        Bms_Soh1.append(SOH[i])
+        Soh_Err1.append(Bms_Soh1[-1]-Soh1[-1])
+        Time1.append(time[ChgStartValid1[i]])
+
+    # DV/DQ法计算soh
+    Soh2=[]
+    Time2=[]
+    Bms_Soh2=[]
+    Soh_Err2=[]
+    SohCfd = []
+    sn_list=[]
+
+    for i in range(len(ChgStartValid2)):
+
+        #寻找最大电压值
+        cellvolt1 = []
+        cellvolt=[]
+        for j in range(1, CellVoltNums+1):
+            s = str(j)
+            volt = data['单体电压' + s]
+            cellvolt1.append(volt[ChgEndValid2[i]])
+        voltmax1_index = cellvolt1.index(max(cellvolt1)) + 1
+        cellvolt1 = data['单体电压' + str(voltmax1_index)] / 1000
+
+        #电压采用滑动平均滤波
+        cellvolt=np_move_avg(cellvolt1, 3, mode="same")
+
+
+        #参数赋初始值
+        Ah = 0
+        Volt = cellvolt[ChgStartValid2[i]]
+        DV_Volt=[]
+        DQ_Ah = []
+        DVDQ = []
+        time2 = []
+        soc2 = []
+        Ah_tatal=[0]
+        xvolt=[]
+        #计算DV和DQ值
+        for j in range(ChgStartValid2[i],ChgEndValid2[i]):
+            Step=(time[j+1]-time[j]).total_seconds()
+            Ah=Ah-packcrnt[j]*Step/3600
+            if (cellvolt[j]-Volt)>0.0009 and Ah>0:
+                Ah_tatal.append(Ah_tatal[-1]+Ah)
+                DQ_Ah.append(Ah)
+                DV_Volt.append(cellvolt[j]-Volt)
+                DVDQ.append((DV_Volt[-1])/DQ_Ah[-1])
+                xvolt.append(cellvolt[j])
+                Volt=cellvolt[j]
+                Ah = 0
+                time2.append(time[j])
+                soc2.append(SOC[j])
+
+        #切片Soc>50且Soc<80
+        Data1 = pd.DataFrame({'SOC': soc2,
+                                'DVDQ': DVDQ,
+                                'Ah_tatal': Ah_tatal[:-1],
+                                'DQ_Ah':DQ_Ah,
+                                'DV_Volt':DV_Volt,
+                                'XVOLT':xvolt})
+
+        Data1=Data1[(Data1['SOC']>50) & (Data1['SOC']<80)]
+
+        #寻找峰值并计算Soh和置信度
+        # 获取最小温度值
+        celltemp = []
+        for j in range(1, CellTempNums+1):
+            s = str(j)
+            temp = data['单体温度' + s]
+            celltemp.append(temp[ChgStartValid2[i]])
+        if len(Data1['DVDQ'])>1:
+            PeakIndex=Data1['DVDQ'].idxmax()
+            #筛选峰值点附近±0.5%SOC内的数据
+            Data2=Data1[(Data1['SOC']>(Data1['SOC'][PeakIndex]-0.5)) & (Data1['SOC']<(Data1['SOC'][PeakIndex]+0.5))]
+            if len(Data2)>2:
+                Ah_tatal1 = Data1['Ah_tatal']
+                DVDQ = Data1['DVDQ']
+                soc2 = Data1['SOC']
+                xvolt = Data1['XVOLT']
+                if soc2[PeakIndex]>50 and soc2[PeakIndex]<80:
+                    DVDQ_SOH=(Ah_tatal[-1]-Ah_tatal1[PeakIndex]) * 100 / ((FullChrgSoc - PeakSoc) * 0.01 * Capacity)
+                    if DVDQ_SOH<95:
+                        DVDQ_SOH=DVDQ_SOH*0.3926+58.14
+                    if DVDQ_SOH>70 and DVDQ_SOH<120:
+                        Soh2.append(DVDQ_SOH)
+                        Bms_Soh2.append(SOH[ChgStartValid2[i]])
+                        Soh_Err2.append(Bms_Soh2[-1] - Soh2[-1])
+                        Time2.append(time[ChgStartValid2[i]])
+                        sn_list.append(SNnum)
+
+                        #计算置信度
+                        if min(celltemp)<10:
+                            SohCfd.append(50)
+                        elif min(celltemp)<20:
+                            SohCfd.append(80)
+                        else:
+                            SohCfd.append(100)
+            else:
+                Data1=Data1.drop([PeakIndex])
+                PeakIndex = Data1['DVDQ'].idxmax()
+                Data2 = Data1[(Data1['SOC'] > (Data1['SOC'][PeakIndex] - 0.5)) & (Data1['SOC'] < (Data1['SOC'][PeakIndex] + 0.5))]
+                if len(Data2) > 3:
+                    Ah_tatal1 = Data1['Ah_tatal']
+                    DVDQ = Data1['DVDQ']
+                    soc2 = Data1['SOC']
+                    xvolt = Data1['XVOLT']
+                    if soc2[PeakIndex]>50 and soc2[PeakIndex]<80:
+                        DVDQ_SOH=(Ah_tatal[-1]-Ah_tatal1[PeakIndex]) * 100 / ((FullChrgSoc - PeakSoc) * 0.01 * Capacity)
+                        if DVDQ_SOH<95:
+                            DVDQ_SOH=DVDQ_SOH*0.3926+58.14
+                        if DVDQ_SOH>70 and DVDQ_SOH<120:
+                            Soh2.append(DVDQ_SOH)
+                            Bms_Soh2.append(SOH[ChgStartValid2[i]])
+                            Soh_Err2.append(Bms_Soh2[-1] - Soh2[-1])
+                            Time2.append(time[ChgStartValid2[i]])
+                            sn_list.append(SNnum)
+
+                            #计算置信度
+                            if min(celltemp)<10:
+                                SohCfd.append(50)
+                            elif min(celltemp)<20:
+                                SohCfd.append(80)
+                            else:
+                                SohCfd.append(100)
+
+    #处理数据
+    if len(Soh2)>5:
+        Soh2=np_move_avg(Soh2,5,mode="valid")
+        result_soh2={'时间': Time2[4::],
+            'SN号':sn_list[4::],
+            'BMS_SOH': Bms_Soh2[4::],
+            'SOH': Soh2,
+            'SOH误差': Soh_Err2[4::]}
+    else:
+        result_soh2={'时间': Time2,
+            'SN号':sn_list,
+            'BMS_SOH': Bms_Soh2,
+            'SOH': Soh2,
+            'SOH误差': Soh_Err2}
+    #第四步:将数据存入Excel
+    Result_Soh2=pd.DataFrame(result_soh2)
+    Result_Soh2.to_csv('BMS_SOH_'+SNnum+'.csv',encoding='GB18030')

+ 173 - 0
LIB/FRONTEND/soh/NCMSoh 20210716.py

@@ -0,0 +1,173 @@
+# 获取数据
+from LIB.BACKEND import DBManager
+
+import os
+import pandas as pd
+import numpy as np
+import datetime
+# import matplotlib.pyplot as plt
+
+#参数输入
+Capacity = 41
+PackFullChrgVolt=69.99
+CellFullChrgVolt=3.5
+CellVoltNums=17
+CellTempNums=4
+FullChrgSoc=98
+PeakSoc=57
+# #40Ah-OCV
+LookTab_SOC = [0,	3.534883489,	8.358178409,	13.18141871,	18.00471528,	22.82796155,	27.65123833,	32.47444668,	37.29772717,	42.12099502,	46.94423182,	51.76744813,	56.59070685,	61.4139927,	66.23719857,	71.0604667,	75.88373853,	80.70702266,	85.5302705,	90.35352009,	95.17676458,	100]
+LookTab_OCV = [3.3159,	3.4384,	3.4774,	3.5156,	3.5478,	3.5748,	3.6058,	3.6238,	3.638,	3.6535,	3.6715,	3.6951,	3.7279,	3.7757,	3.8126,	3.8529,	3.8969,	3.9446,	3.9946,	4.0491,	4.109,	4.183]
+# #55Ah-OCV
+# LookTab_SOC = [0.00, 	2.40, 	6.38, 	10.37, 	14.35, 	18.33, 	22.32, 	26.30, 	30.28, 	35.26, 	40.24, 	45.22, 	50.20, 	54.19, 	58.17, 	60.16, 	65.14, 	70.12, 	75.10, 	80.08, 	84.06, 	88.05, 	92.03, 	96.02, 	100.00]
+# LookTab_OCV = [2.7151,	3.0298,	3.1935,	3.2009,	3.2167,	3.2393,	3.2561,	3.2703,	3.2843,	3.2871,	3.2874,	3.2868,	3.2896,	3.2917,	3.2967,	3.3128,	3.3283,	3.3286,	3.3287,	3.3288,	3.3289,	3.3296,	3.3302,	3.3314,	3.3429]
+
+#参数初始化
+Soh3=[]
+Time3=[]
+Bms_Soh3=[]
+Soh_Err3=[]
+sn_list=[]
+
+#获取数据时间段
+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=31)
+end_time=str(now_time)
+strat_time=str(start_time)
+
+#输入一个含有‘SN号’的xlsx
+SNdata = pd.read_excel('骑享资产梳理-20210621.xlsx', sheet_name='6040骑享')
+SNnums=SNdata['SN号']
+for k in range(len(SNnums)):
+    SNnum=str(SNnums[k])
+
+    sn = SNnum
+    st = strat_time
+    et = end_time
+
+    dbManager = DBManager.DBManager()
+    df_data = dbManager.get_data(sn=sn, start_time=st, end_time=et, data_groups=['bms'])
+    data = df_data['bms']
+    # print(data)
+
+    packcrnt=data['总电流[A]']
+    packvolt=data['总电压[V]']
+    SOC=data['SOC[%]']
+    SOH=data['SOH[%]']
+    bmsstat=data['充电状态']
+    time= pd.to_datetime(data['时间戳'], format='%Y-%m-%d %H:%M:%S')
+
+    #第一步:筛选充电数据
+    if len(packcrnt)>100:
+        ChgStart=[]
+        ChgEnd=[]
+        for i in range(3, len(time) - 3):
+            if i==3 and bmsstat[i]==2 and bmsstat[i+1]==2 and bmsstat[i+2]==2:
+                ChgStart.append(i)
+            elif bmsstat[i-2]!=2 and bmsstat[i-1]!=2 and bmsstat[i]==2:
+                ChgStart.append(i)
+            elif bmsstat[i-1]==2 and bmsstat[i]!=2 and bmsstat[i+1]!=2:
+                ChgEnd.append(i-1)
+            elif i == (len(time) - 4) and bmsstat[len(bmsstat)-1] == 2 and bmsstat[len(bmsstat)-2] == 2:
+                ChgEnd.append(len(time)-2)
+
+        #第二步:筛选充电起始Soc<45% & SOC>85%,电芯温度>5℃
+        ChgStartValid1=[]
+        ChgEndValid1=[]
+        ChgStartValid2=[]
+        ChgEndValid2=[]
+        StandingNum=[]
+
+        for i in range(min(len(ChgStart),len(ChgEnd))):
+
+            #获取最小温度值
+            celltemp = []
+            for j in range(1, CellTempNums+1):
+                s = str(j)
+                temp = data['单体温度' + s]
+                celltemp.append(temp[ChgEnd[i]])
+            
+            #去除电流0点   
+            for k in range(ChgStart[i],ChgEnd[i]):
+                if packcrnt[k]<-0.5 and packcrnt[k+1]>-0.5 and packcrnt[k+2]>-0.5 and packcrnt[k+3]>-0.5:
+                    ChgEnd[i]=k
+            
+            #计算最大packvolt
+            if len(packvolt[ChgStart[i]:ChgEnd[i]])>0:
+                packvoltMAX=max(packvolt[ChgStart[i]:ChgEnd[i]])
+
+                #筛选满足2点法计算的数据
+                StandingTime=0
+                StandingTime1=0
+                StandingTime2=0
+                if SOC[ChgEnd[i]]>85 and SOC[ChgStart[i]]<45 and min(celltemp)>5:
+                    for m in range(min(len(packcrnt)-ChgEnd[i]-2,ChgStart[i]-2)):
+                        if abs(packcrnt[ChgStart[i] - m - 1]) < 0.1:
+                            StandingTime = StandingTime + (time[ChgStart[i] - m] - time[ChgStart[i] - m - 1]).total_seconds()
+                        if abs(packcrnt[ChgEnd[i] + m + 1]) < 0.1:
+                            StandingTime1 = StandingTime1 + (time[ChgEnd[i] + m + 1] - time[ChgEnd[i] + m]).total_seconds()
+                        if StandingTime > 900 and StandingTime1>900 and ((time[ChgEnd[i]]-time[ChgStart[i]]).total_seconds())/(ChgEnd[i]-ChgStart[i])<60:  #筛选静置时间>15min且慢充过程丢失数据少
+                            ChgStartValid1.append(ChgStart[i])
+                            ChgEndValid1.append(ChgEnd[i])
+                            StandingNum.append(m)
+                            break
+                        if abs(packcrnt[ChgStart[i] - m - 2])>0.5 and abs(packcrnt[ChgEnd[i] + m + 2])>0.5:
+                            break
+
+        # 计算soh
+        Soh1=[]
+        Soh2=[]
+        Time1=[]
+        Bms_Soh1=[]
+        Soh_Err1=[]
+        sn_list1=[]
+        #两点法计算Soh
+        if len(ChgStartValid1)>0:
+            for i in range(len(ChgStartValid1)):
+                #计算Ah
+                Ah=0
+                for j in range(ChgStartValid1[i],ChgEndValid1[i]):
+                    Step=(time[j+1]-time[j]).total_seconds()
+                    Ah=Ah-packcrnt[j+1]*Step/3600
+                #计算每个电芯的Soh
+                for j in range(1, CellVoltNums+1):
+                    s = str(j)
+                    cellvolt = data['单体电压' + s]/1000
+                    OCVStart=cellvolt[ChgStartValid1[i]-2]
+                    OCVEnd=cellvolt[ChgEndValid1[i]+StandingNum[i]]
+                    #soh
+                    Ocv_Soc1=np.interp(OCVStart,LookTab_OCV,LookTab_SOC)
+                    Ocv_Soc2=np.interp(OCVEnd,LookTab_OCV,LookTab_SOC)
+                    Soh2.append(Ah*100/((Ocv_Soc2-Ocv_Soc1)*0.01*Capacity))
+                Soh1.append(np.mean(Soh2))
+                Bms_Soh1.append(SOH[ChgStartValid1[i]])
+                Soh_Err1.append(Bms_Soh1[-1]-Soh1[-1])
+                Time1.append(time[ChgStartValid1[i]])
+                sn_list1.append(SNnum)
+       
+            # Soh3.append(np.mean(Soh1))
+            # Bms_Soh3.append(np.mean(Bms_Soh1))
+            # Soh_Err3.append(np.mean(Soh_Err1))
+            # Time3.append(time[ChgStartValid1[-1]])
+            # sn_list.append(SNnum)
+
+        #第四步:将数据存入Excel
+            result_soh2={'时间': Time1,
+                'SN号': sn_list1,
+                'BMS_SOH': Bms_Soh1,
+                'SOH': Soh1,
+                'SOH误差': Soh_Err1}
+
+            Result_Soh2=pd.DataFrame(result_soh2)
+            Result_Soh2.to_csv('BMS_SOH_'+SNnum+'.csv',encoding='GB18030')
+
+#     result_soh1={'时间': Time3,
+#         'SN号':sn_list,
+#         'BMS_SOH': Bms_Soh3,
+#         'SOH': Soh3,
+#         'SOH误差': Soh_Err3}
+
+# Result_Soh1=pd.DataFrame(result_soh1)
+# print(Result_Soh1)
+# Result_Soh1.to_csv('BMS_SOH_'+'6040'+'.csv',encoding='GB18030')

+ 34 - 0
LIB/FRONTEND/soh/main.py

@@ -0,0 +1,34 @@
+#coding=utf-8
+import os
+import datetime
+import pandas as pd
+from LIB.BACKEND import DBManager, Log
+from LIB.MIDDLE import SignalMonitor
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+import time, datetime
+import traceback
+from LIB.MIDDLE.soh import NCMSoh_20210716 as NCMSoh
+from LIB.MIDDLE.soh import LFPSoh_20210711 as LFPSoh
+
+from urllib import parse
+
+dbManager = DBManager.DBManager()
+if __name__ == "__main__":
+    SNdata_6040 = pd.read_excel('骑享资产梳理-20210621.xlsx', sheet_name='6040骑享')
+    SNdata_6060 = pd.read_excel('骑享资产梳理-20210621.xlsx', sheet_name='6060')
+    SNnums_6060=SNdata_6060['SN号']
+    SNnums_6040=SNdata_6040['SN号']
+    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=31)
+    end_time=str(now_time)
+    start_time=str(start_time)
+
+    for sn in SNnums_6040.tolist():
+        res = NCMSoh.cal_soh(sn, end_time, start_time)
+        res.to_csv('BMS_SOH_'+sn+'.csv',encoding='GB18030')
+    
+    for sn in SNnums_6060.tolist():
+        res = LFPSoh.cal_soh(sn, end_time, start_time)
+        res.to_csv('BMS_SOH_'+sn+'.csv',encoding='GB18030')

+ 6 - 0
LIB/FRONTEND/soh/soh表头及数据类型.xlsx

@@ -0,0 +1,6 @@
+表头	名称	数据类型
+时间	time	timestamps
+SN号	sn	str
+BMS_SOH	bms_soh	float64
+SOH	soh	float64
+SOH误差	soh_err	float64

BIN
LIB/FRONTEND/soh/骑享资产梳理-20210621.xlsx


+ 1 - 1
LIB/MIDDLE/IndexStaByOneCycle.py

@@ -2,7 +2,7 @@
 基于单一状态(一次行车、一次静置、一次充电)的指标统计库
 
 '''
-__author__ = 'Wang Liming'
+__author__ = 'lmstack'
 
 # import CONFIGURE.PathSetting as PathSetting
 # import sys

+ 1 - 1
LIB/MIDDLE/IndexStaByPeriod.py

@@ -2,7 +2,7 @@
 基于某个周期(一天,一周...)的指标统计库
 
 '''
-__author__ = 'Wang Liming'
+__author__ = 'lmstack'
 
 # import CONFIGURE.PathSetting as PathSetting
 # import sys

+ 49 - 24
LIB/MIDDLE/SignalMonitor.py

@@ -75,7 +75,7 @@ class SignalMonitor():
         return df_state
     
     @staticmethod
-    def _judge_offline_state_between_messages(sn, PackState_new, PackState_old, Timestamp_new, Timestamp_old, df_res, mode):
+    def _judge_offline_state_between_messages(sn, PackState_new, PackState_old, Timestamp_new, Timestamp_old, lat, long, df_res, mode):
         delta_time = (Timestamp_new - Timestamp_old).total_seconds()
         max_state = max(PackState_new, PackState_old)
         if max_state == 0:
@@ -108,8 +108,12 @@ class SignalMonitor():
             LineState = 2
         
         if LineState > 0:
-            df_res = df_res.append({'sn':sn[0], 'PackState':PackState_new*16+PackState_old, 'LineState':LineState, 'StartTime':Timestamp_old, 
+            if mode == 'BMS':
+                df_res = df_res.append({'sn':sn[0], 'PackState':PackState_new*16+PackState_old, 'LineState':LineState, 'StartTime':Timestamp_old, 
                         'EndTime':Timestamp_new, 'OfflineTime':delta_time}, ignore_index=True)
+            elif mode == 'GPS':
+                df_res = df_res.append({'sn':sn[0], 'PackState':PackState_new*16+PackState_old, 'LineState':LineState, 'StartTime':Timestamp_old, 
+                    'EndTime':Timestamp_new, 'OfflineTime':delta_time, 'latitude':lat, 'longitude':long}, ignore_index=True)
         return LineState, df_res
 
     @staticmethod
@@ -119,17 +123,35 @@ class SignalMonitor():
             df_state.loc[0,'LineState'] = 0
             while index < len(df_state)-1:
                 index = index + 1
-                LineState, df_res = SignalMonitor._judge_offline_state_between_messages(sn, df_state.loc[index, 'PackState'], df_state.loc[index-1, 'PackState'], 
-                df_state.loc[index, 'Timestamp'], df_state.loc[index-1, 'Timestamp'], df_res, mode=mode)
+                if mode == 'BMS':
+                    LineState, df_res = SignalMonitor._judge_offline_state_between_messages(sn, df_state.loc[index, 'PackState'], df_state.loc[index-1, 'PackState'], 
+                    df_state.loc[index, 'Timestamp'], df_state.loc[index-1, 'Timestamp'], None, None,
+                    df_res, mode=mode)
+                elif mode == 'GPS':
+                    LineState, df_res = SignalMonitor._judge_offline_state_between_messages(sn, df_state.loc[index, 'PackState'], df_state.loc[index-1, 'PackState'], 
+                    df_state.loc[index, 'Timestamp'], df_state.loc[index-1, 'Timestamp'], df_state.loc[index-1, 'latitude'], df_state.loc[index-1, 'longitude'],
+                    df_res, mode=mode)
                 df_state.loc[index, 'LineState'] = LineState
         else:
             df_last_info = df_last_state.loc[len(df_last_state) - 1]
-            df_state.loc[0,'LineState'], df_res = SignalMonitor._judge_offline_state_between_messages(sn, df_state.loc[0, 'PackState'], df_last_info['PackState'], 
-                df_state.loc[0, 'Timestamp'], df_last_info['Timestamp'], df_res, mode=mode)
+            if mode == 'BMS':
+                df_state.loc[0,'LineState'], df_res = SignalMonitor._judge_offline_state_between_messages(sn, df_state.loc[0, 'PackState'], df_last_info['PackState'], 
+                    df_state.loc[0, 'Timestamp'], df_last_info['Timestamp'], None, None,
+                    df_res, mode=mode)
+            elif mode == 'GPS':
+                df_state.loc[0,'LineState'], df_res = SignalMonitor._judge_offline_state_between_messages(sn, df_state.loc[0, 'PackState'], df_last_info['PackState'], 
+                    df_state.loc[0, 'Timestamp'], df_last_info['Timestamp'], df_state.loc[0, 'latitude'], df_state.loc[0, 'longitude'],
+                    df_res, mode=mode)
             while index < len(df_state)-1:
                 index = index + 1
-                LineState, df_res = SignalMonitor._judge_offline_state_between_messages(sn, df_state.loc[index, 'PackState'], df_state.loc[index-1, 'PackState'], 
-                df_state.loc[index, 'Timestamp'], df_state.loc[index-1, 'Timestamp'], df_res, mode=mode)
+                if mode == 'BMS':
+                    LineState, df_res = SignalMonitor._judge_offline_state_between_messages(sn, df_state.loc[index, 'PackState'], df_state.loc[index-1, 'PackState'], 
+                    df_state.loc[index, 'Timestamp'], df_state.loc[index-1, 'Timestamp'], None, None,
+                    df_res, mode=mode)
+                elif mode == 'GPS':
+                    LineState, df_res = SignalMonitor._judge_offline_state_between_messages(sn, df_state.loc[index, 'PackState'], df_state.loc[index-1, 'PackState'], 
+                    df_state.loc[index, 'Timestamp'], df_state.loc[index-1, 'Timestamp'], df_state.loc[index-1, 'latitude'], df_state.loc[index-1, 'longitude'],
+                    df_res, mode=mode)
                 df_state.loc[index, 'LineState'] = LineState
         # SignalMonitor._file_write(r'D:\result_03.xls', df_state)
         return df_res
@@ -137,22 +159,22 @@ class SignalMonitor():
     @staticmethod
     def _set_gps_working_states(df_state, df_state_gps):
         for i in range(0, len(df_state_gps)):
-                if df_state_gps.loc[i, 'Timestamp'] <= df_state.loc[0, 'Timestamp']:
-                    df_state_gps.loc[i, 'PackState'] = df_state.loc[0, 'PackState']
-                elif df_state_gps.loc[i, 'Timestamp'] >= df_state.loc[len(df_state)-1, 'Timestamp']:
-                    df_state_gps.loc[i:len(df_state_gps)-1, 'PackState'] = df_state.loc[len(df_state)-1, 'PackState']
-                    break
+            if df_state_gps.loc[i, 'Timestamp'] <= df_state.loc[0, 'Timestamp']:
+                df_state_gps.loc[i, 'PackState'] = df_state.loc[0, 'PackState']
+            elif df_state_gps.loc[i, 'Timestamp'] >= df_state.loc[len(df_state)-1, 'Timestamp']:
+                df_state_gps.loc[i:len(df_state_gps)-1, 'PackState'] = df_state.loc[len(df_state)-1, 'PackState']
+                break
+            else:
+                index0 = max(df_state[df_state['Timestamp'] <= df_state_gps.loc[i, 'Timestamp']].index)
+                index1 = min(df_state[df_state['Timestamp'] >= df_state_gps.loc[i, 'Timestamp']].index)
+                front = (df_state_gps.loc[i, 'Timestamp'] - df_state.loc[index0, 'Timestamp']).total_seconds()
+                back = (df_state.loc[index1, 'Timestamp'] - df_state_gps.loc[i, 'Timestamp']).total_seconds()
+                if front > back:
+                    df_state_gps.loc[i, 'PackState'] = df_state.loc[index1, 'PackState']
+                elif front == back:
+                    df_state_gps.loc[i, 'PackState'] = max(df_state.loc[index1, 'PackState'], df_state.loc[index0, 'PackState'])
                 else:
-                    index0 = max(df_state[df_state['Timestamp'] <= df_state_gps.loc[i, 'Timestamp']].index)
-                    index1 = min(df_state[df_state['Timestamp'] >= df_state_gps.loc[i, 'Timestamp']].index)
-                    front = (df_state_gps.loc[i, 'Timestamp'] - df_state.loc[index0, 'Timestamp']).total_seconds()
-                    back = (df_state.loc[index1, 'Timestamp'] - df_state_gps.loc[i, 'Timestamp']).total_seconds()
-                    if front > back:
-                        df_state_gps.loc[i, 'PackState'] = df_state.loc[index1, 'PackState']
-                    elif front == back:
-                        df_state_gps.loc[i, 'PackState'] = max(df_state.loc[index1, 'PackState'], df_state.loc[index0, 'PackState'])
-                    else:
-                        df_state_gps.loc[i, 'PackState'] = df_state.loc[index0, 'PackState']
+                    df_state_gps.loc[i, 'PackState'] = df_state.loc[index0, 'PackState']
         return df_state_gps
     
     @staticmethod
@@ -209,7 +231,7 @@ class SignalMonitor():
         return df_res,df_state, df_last_state
     
     def get_gps_offline_stat(self,sn, st, et, df_state, df_res_gps, df_last_state_gps, cal_Period=24):    # 计算一段时间内GPS信号统计数据
-        df_state_gps = pd.DataFrame(columns=['sn', 'Timestamp', 'PackState', 'LineState'])
+        df_state_gps = pd.DataFrame(columns=['sn', 'Timestamp', 'PackState', 'LineState', 'latitude', 'longitude'])
         # print("start_time is {}, limit_time is {}".format(st, limit_time))
         end_time = st + datetime.timedelta(hours=cal_Period)    # 结束时间
         start_time_str = st.strftime('%Y-%m-%d %H:%M:%S')
@@ -223,6 +245,9 @@ class SignalMonitor():
 
         df_state_gps['Timestamp'] = df_gps['时间戳']
         df_state_gps['sn'] = sn[0]
+        df_state_gps['latitude'] = df_gps['纬度']
+        df_state_gps['longitude'] = df_gps['经度']
+
 
         if len(df_state_gps) > 0:    # 无数据则不计算    
             df_state_gps = SignalMonitor._set_gps_working_states(df_state, df_state_gps)    # 根据同时间段内BMS状态计算GPS数据对应的BMS状态

+ 125 - 0
LIB/MIDDLE/odo/CalDist.py

@@ -0,0 +1,125 @@
+from math import radians, cos, sin, asin, sqrt
+import pandas as pd
+import numpy as np
+from datetime import datetime
+from datetime import timedelta
+
+from GpsRank import *
+from ProcessDfBms import *
+from ProcessDfGps import *
+
+from LIB.BACKEND import DBManager
+#####################################配置环境分割线#################################################
+
+def GetDistInfo(input_sn,input_starttime,input_endtime):
+
+    #####################################配置参数分割线#################################################
+    dbManager = DBManager.DBManager()
+    data_raw = dbManager.get_data(sn=input_sn, start_time=input_starttime, 
+        end_time=input_endtime)
+    #拆包预处理
+    df_bms_raw=data_raw['bms']
+    df_gps_raw=data_raw['gps']
+    df_bms=preprocess_Df_Bms(df_bms_raw)
+    df_gps=preprocess_Df_Gps(df_gps_raw)
+    
+    #####################################数据预处理分割线#################################################
+
+    # mode: 0:正常取数; 1:7255 取数
+    if input_sn[0:2] == 'UD' or input_sn[0:2] == 'MG':
+        mode = 1
+    else:
+        mode = 0
+    #获取状态表,mode默认为0,mode=1放电时电流为负,mode=0充电时电流为正
+
+    df_bms_drive_timetable=get_bms_drive_timetable(df_bms,mode)
+    df_gps_drive_cycle_accum=pd.DataFrame()
+    if len(df_bms_drive_timetable)>0:
+        for index in range(len(df_bms_drive_timetable)):
+            #筛选drivecycle数据
+            drive_start_time=df_bms_drive_timetable.loc[index,'drive_start_time']#开始时间
+            drive_end_time=df_bms_drive_timetable.loc[index,'drive_end_time']#结束时间
+
+            time_condition=(df_gps['time']>drive_start_time)&(df_gps['time']<drive_end_time)#时间判断条件
+            df_gps_drive_cycle=df_gps.loc[time_condition,:].copy()
+            df_gps_drive_cycle=df_gps_drive_cycle.reset_index(drop=True)#重置index
+            #计算drivecycle GPS累计里程,存入表格
+            condition_a=df_gps_drive_cycle['deltatime']>60*3
+            condition_b=(df_gps_drive_cycle['deltatime']>90*1)&(df_gps_drive_cycle['distance']>1000)
+            drive_cycle_dist_array=df_gps_drive_cycle.loc[~(condition_a|condition_b),'distance'].values
+            drive_cycle_dist_array=drive_cycle_dist_array[np.where((drive_cycle_dist_array>=1)&(drive_cycle_dist_array<1000))[0]]
+            gps_dist=drive_cycle_dist_array.sum()
+            df_bms_drive_timetable.loc[index,'gps_dist']=gps_dist#得到GPS路径
+            #计算头-尾的空缺时间段对应的预估SOC
+            if len(df_gps_drive_cycle)>2:
+                gps_starttime=df_gps_drive_cycle.loc[1,'time']#gps开始时间
+                gps_endtime=df_gps_drive_cycle.loc[df_gps_drive_cycle.index[-1],'time']#gps结束时间
+                #从drive_start_time到gps开始时间,使用SOC计算的里程
+                #gps结束时间到drive_end_time,使用SOC计算的里程
+                unrecorded_odo_head=cal_deltasoc(df_bms,drive_start_time,gps_starttime)
+                unrecorded_odo_tail=cal_deltasoc(df_bms,gps_endtime,drive_end_time)
+            else:
+                #计算数据丢失行unrecordeodo
+                unrecorded_odo_head=cal_deltasoc(df_bms,drive_start_time,drive_end_time)
+                unrecorded_odo_tail=0
+            #计算中间的预估SOC
+            predict_dist=cal_unrecorded_gps(df_gps_drive_cycle,df_bms)
+            #计算总的预估SOC
+            totaldist=predict_dist+unrecorded_odo_head+ unrecorded_odo_tail#得到GPS路径
+            df_bms_drive_timetable.loc[index,'predict_dist']=totaldist
+    else :
+        pass
+
+    #####################################统计行驶里程End#################################################
+    #打印输出结果#
+    index_list=list(range(len(df_bms_drive_timetable)))
+
+    dist_gps=0
+    dist_predict=0
+    day_start_time=''#当日开始时间
+    day_end_time=''#当日结束时间
+    day_start_soc=0#当日开始soc
+    day_end_soc=0#当日结束soc
+    day_min_soc=101#当日最低soc
+    drive_accum_soc=0#累计使用SOC
+
+    if len(df_bms_drive_timetable)>0:
+        #开始行
+        day_start_soc=df_bms_drive_timetable.loc[1,'drive_start_soc']#开始soc
+        day_start_time=df_bms_drive_timetable.loc[1,'drive_start_time']#开始时间
+        #结束行
+        day_end_time=df_bms_drive_timetable.loc[len(df_bms_drive_timetable)-1,'drive_end_time']#结束时间
+        day_end_soc=df_bms_drive_timetable.loc[len(df_bms_drive_timetable)-1,'drive_end_soc']#结束soc
+
+    for index in index_list:
+        '''汇总里程'''
+        dist_gps+=df_bms_drive_timetable.loc[index,'gps_dist']/1000#计算GPS里程
+        dist_predict+=df_bms_drive_timetable.loc[index,'predict_dist']#计算预估里程
+        drive_start_soc=df_bms_drive_timetable.loc[index,'drive_start_soc']#驾驶周期开始的soc
+        drive_end_soc=df_bms_drive_timetable.loc[index,'drive_end_soc']#驾驶周期结束的soc
+        day_min_soc=min(day_min_soc,drive_start_soc,drive_end_soc)
+
+        delta_soc=drive_start_soc-drive_end_soc#驾驶周期SOC变化量
+        drive_accum_soc+=abs(delta_soc)#所有drive cycle累计消耗的soc
+
+    # gps_score=get_df_gps_score(input_starttime,input_endtime,df_gps)
+    # gps_score=round(gps_score,1)
+    #计算总里程
+    dist_gps=round(dist_gps,3)
+    dist_predict=round(dist_predict,3)
+    dist_all=round(dist_gps+dist_predict,3)
+    #输出统计结果
+    # print ('为您查询到,从'+input_starttime+'到'+input_endtime+'时间段内:')
+    # print('SOC变化量:'+str(df_bms['bmspacksoc'].max()-df_bms['bmspacksoc'].min())+' %')
+    # print('行驶总里程:'+str(dist_all)+' km')
+
+    return {'SN':input_sn,'range':dist_all,'accum_soc':drive_accum_soc,'day_start_soc':day_start_soc,
+    'day_end_soc':day_end_soc,'day_start_time':day_start_time,'day_end_time':day_end_time,
+    'day_min_soc':day_min_soc}
+    # print('其中GPS信号在线时里程:'+str(dist_gps)+' km')
+    # print('其中GPS信号掉线时预估里程:'+str(dist_predict)+' km')
+    # print('GPS信号质量评分为:'+str(gps_score),'分\n')
+
+    #####################################打印结果End#################################################
+
+

+ 68 - 0
LIB/MIDDLE/odo/CalDist_Batch.py

@@ -0,0 +1,68 @@
+from math import radians, cos, sin, asin, sqrt
+import pandas as pd
+import numpy as np
+from datetime import datetime
+from datetime import timedelta
+
+from GpsRank import *
+from ProcessDfBms import *
+from ProcessDfGps import *
+from CalDist import *
+from LIB.BACKEND import DBManager
+import pdb
+
+asset_table_path='D:\\work\\Qixiang\\data_analyze_platform\\pengmin\\AllCarDist\\asset_table.xlsx'
+drive_info_path='D:\\work\\Qixiang\\data_analyze_platform\\pengmin\\AllCarDist\\drive_info.xlsx'
+asset_sheet_num=1
+usecols_list=[4,5]
+
+asset_table=pd.read_excel(asset_table_path,sheet_name=asset_sheet_num,skiprows=1,usecols=usecols_list)
+SN_list=asset_table['SN号'].values.tolist()
+print('从6060sheet读取到:'+str(len(SN_list))+'行')
+asset_table=asset_table.rename(columns={'SN号':'SN','状态':'state'})
+
+asset_table.set_index(["SN"],inplace=True)
+col_name=asset_table.columns.tolist()
+col_name.extend(['range','accum_soc','day_start_soc','day_end_soc','day_start_time','day_end_time'])
+asset_table=asset_table.reindex(columns=col_name)
+
+start_hour='00:00:00'#每日查询最早时间
+end_hour='23:59:00'#每日查询最晚时间
+
+
+date_index=pd.date_range('2021-07-31','2021-07-31')
+for date in date_index:
+    '''遍历日期'''
+
+    str_date=str(date)[:10]
+    input_starttime=str_date+' '+start_hour
+    input_endtime=str_date+' '+end_hour
+    test_day=str_date[5:10]#月-日,用于建立sheet
+    drive_info_path='D:\\work\\Qixiang\\data_analyze_platform\\pengmin\\AllCarDist\\6060\\drive_info'+test_day+'_50_end_'+'.xlsx'
+
+    print(input_starttime)
+
+    drive_info_aday=pd.DataFrame()
+    SN_list_short=SN_list#先选择了0:50,50:end
+
+    for SN in SN_list_short:
+        '''遍历SN号'''
+        SN=SN.strip('\t')
+        SN=SN.strip('\n')
+
+        try:
+            range=GetDistInfo(SN,input_starttime,input_endtime)
+            range_df=pd.DataFrame([range])
+            drive_info_aday=pd.concat([drive_info_aday,range_df],axis=0)
+
+        except:
+            print(SN+' '+test_day+'fail')
+        else:
+            pass
+            #print(SN+' '+test_day+'success')
+
+    drive_info_aday.to_excel(drive_info_path,sheet_name=test_day)#sheet名称为testday
+    
+    
+
+

+ 77 - 0
LIB/MIDDLE/odo/GpsRank.py

@@ -0,0 +1,77 @@
+import pandas as pd
+import numpy as np
+from datetime import datetime
+from datetime import timedelta
+
+def cal_gps_score(df):
+    '''在获取信号,优、良、合格、掉线的比例之后,计算gps的总评分'''
+    score=0
+    for index in range(len(df)):
+        time_percent=df.loc[index,'累计时间占比']
+        if df.loc[index,'GPS质量']=='优':
+            score+=time_percent*0
+        elif df.loc[index,'GPS质量']=='良':
+            score+=time_percent*0.3
+        elif df.loc[index,'GPS质量']=='合格':
+            score+=time_percent*0.5
+        elif df.loc[index,'GPS质量']=='掉线':
+            score+=time_percent*1
+    return (1-score)*100
+
+def gps_rank(df_gps_signal_table,df_gps,signal_rank,dist_factor):
+    '''gps信号质量分析函数,需要输入表格,df_gps,信号等级,权重'''
+    gps_signal_condition=(df_gps['gps_signal']==signal_rank)
+    dist=df_gps.loc[gps_signal_condition,'distance'].values.sum()
+    deltatime=df_gps.loc[gps_signal_condition,'deltatime'].values.sum()
+    df_gps_signal_table_condition=(df_gps_signal_table['gps_signal']==signal_rank)
+    df_gps_signal_table.loc[df_gps_signal_table_condition,'accum_distance']=dist/1000
+    df_gps_signal_table.loc[df_gps_signal_table_condition,'accum_deltatime']=deltatime
+    df_gps_signal_table.loc[df_gps_signal_table_condition,'accum_distance_factor']=dist/1000*dist_factor
+    return df_gps_signal_table
+
+def get_df_gps_score(starttime,endtime,df_gps):
+    '''对df_gps中的gps质量进行评分,返回一个数值'''
+    test_start_time=starttime#'2021-05-29 17:16:39'
+    test_end_time=endtime#'2021-05-29 20:08:08'
+
+    test_time_condition=(df_gps['time']>test_start_time)&(df_gps['time']<test_end_time)
+    df_gps_test=df_gps.loc[test_time_condition,:].copy()
+    df_gps_test=df_gps_test.reset_index(drop=True)#重置index
+    #按照deltatime打标签
+    gps_deltatime_bins=[0,30,60,120,10000]#优-良-合格-掉线
+    name=['优','良','合格','掉线']
+    df_gps_test['gps_signal']=pd.cut(df_gps_test['deltatime'], gps_deltatime_bins,labels=name)
+    df_gps_test['gps_signal'].value_counts()
+    #声明一个gps信号按类别统计table
+    df_gps_signal_table=pd.DataFrame()
+    df_gps_signal_table['gps_signal']=df_gps_test['gps_signal'].value_counts().index.tolist()
+    df_gps_signal_table['num']=df_gps_test['gps_signal'].value_counts().values.tolist()
+
+    #分类进行统计
+    df_gps_signal_table=gps_rank(df_gps_signal_table,df_gps_test,'优',1.00)
+    df_gps_signal_table=gps_rank(df_gps_signal_table,df_gps_test,'良',1.05)
+    df_gps_signal_table=gps_rank(df_gps_signal_table,df_gps_test,'合格',1.2)
+    df_gps_signal_table=gps_rank(df_gps_signal_table,df_gps_test,'掉线',1)
+
+    #次数占比,时间占比
+    all_num=df_gps_signal_table['num'].sum()
+    df_gps_signal_table['num_percent']=df_gps_signal_table['num']/all_num
+    all_accum_deltatime=df_gps_signal_table['accum_deltatime'].sum()
+    df_gps_signal_table['accum_deltatime_percent']=df_gps_signal_table['accum_deltatime']/all_accum_deltatime
+
+    #选择参数
+    df_gps_signal_table=df_gps_signal_table[['gps_signal','num','num_percent','accum_distance',
+                                            'accum_distance_factor','accum_deltatime','accum_deltatime_percent']]
+    df_gps_signal_table=df_gps_signal_table.rename(columns={'gps_signal':'GPS质量','num':'数量','num_percent':'数量占比',
+                                                        'accum_distance':'累计里程','accum_distance_factor':'累计里程修正值',
+                                                        'accum_deltatime':'累计时间','accum_deltatime_percent':'累计时间占比'})
+
+    df_gps_signal_table.loc[:,['GPS质量','累计时间','累计时间占比']]
+    gps_score=cal_gps_score(df_gps_signal_table)#调用函数计算gps评分
+    
+    #输出结果,评分
+    #print('From '+test_start_time+'  to '+test_end_time)
+    #print('GPS信号质量评分:'+str(gps_score))
+
+    return gps_score
+

+ 159 - 0
LIB/MIDDLE/odo/ProcessDfBms.py

@@ -0,0 +1,159 @@
+import pandas as pd
+import numpy as np
+from datetime import datetime
+from datetime import timedelta
+
+def get_bms_drive_timetable(df_bms,battery_mode):
+    '''对df_bms进行处理,得到行车的时间表。'''
+
+    #####################step1 先使用电流做充电状态的判断#############################################
+    if battery_mode==0:#mode=0,电流为正代表放电
+        condition_chrg=df_bms['bmspackcrnt']<0##根据电流,挑选充电状态
+        df_bms.loc[condition_chrg,'bscsta']='chrg'
+        condition_drive=df_bms['bmspackcrnt']>0.01##根据电流,挑选行驶状态
+        df_bms.loc[condition_drive,'bscsta']='drive'
+        df_bms.loc[~(condition_drive|condition_chrg),'bscsta']='idle'#静置状态
+    else :#mode=1,电流为负代表放电
+        condition_chrg=df_bms['bmspackcrnt']>0##根据电流,挑选充电状态
+        df_bms.loc[condition_chrg,'bscsta']='chrg'
+        condition_drive=df_bms['bmspackcrnt']<-0.01##根据电流,挑选行驶状态
+        df_bms.loc[condition_drive,'bscsta']='drive'
+        df_bms.loc[~(condition_drive|condition_chrg),'bscsta']='idle'#静置状态
+
+    #####################step2 对drive进行debounce,进入时立即进入,退出时debounce,5分钟。##########
+    index=0
+    debounce_row=10#debounce判断持续10行
+    debounce_time=300#debounce判断持续300秒
+    #对上一步初步状态进行二次处理
+    while index<(len(df_bms)-debounce_row):
+        mode_0=df_bms.loc[index,'bscsta']
+        mode_1=df_bms.loc[index+1,'bscsta']
+        #如果发现了边界行,则进行debounce判断
+        if (mode_0=='drive')&(mode_1!='drive'):#如果从drive变为idle
+            accum_subtime=0#累计时间初始化
+
+            for sub_index in range(debounce_row):#往下处理10行
+                sub_time=df_bms.loc[index+sub_index,'deltatime']
+                accum_subtime+=sub_time
+                #如果累计时间不到300秒,则设置为drive
+                if accum_subtime<debounce_time:
+                    df_bms.loc[index+sub_index,'bscsta']='drive'
+            index=index+debounce_row#处理10行以后的数据
+        #如果从idle变为drivemode,则将idle变为drive,包容前一行
+        elif (mode_0!='drive')&(mode_1=='drive'): 
+            df_bms.loc[index,'bscsta']='drive'
+            index=index+1
+        else:
+            index=index+1
+
+
+    #######################step3 对drivemode的时间进行分段###########################################
+    not_drive_flg=0#初始化
+    #输出drivemode的时间段,包含开始时间、结束时间
+    df_bms_drive_timetable_index=0
+    df_bms_drive_timetable=pd.DataFrame([],columns={'drive_start_time','drive_end_time',
+                                                    'gps_dist','predict_dist','drive_start_soc','drive_end_soc'})
+    for index in range(len(df_bms)):
+        temp_bscsta=df_bms.loc[index,'bscsta']
+        
+        if (temp_bscsta=='drive')&(not_drive_flg==0):
+            drive_start_time=df_bms.loc[index,'time']
+            not_drive_flg=1
+            df_bms_drive_timetable.loc[df_bms_drive_timetable_index,'drive_start_time']=drive_start_time
+            #startsoc
+            drive_start_soc=df_bms.loc[index,'bmspacksoc']
+            df_bms_drive_timetable.loc[df_bms_drive_timetable_index,'drive_start_soc']=drive_start_soc
+
+        elif (temp_bscsta!='drive')&(not_drive_flg==1):
+            drive_end_time=df_bms.loc[index,'time']
+            not_drive_flg=0
+            df_bms_drive_timetable.loc[df_bms_drive_timetable_index,'drive_end_time']=drive_end_time
+            #endsoc
+            drive_end_soc=df_bms.loc[index,'bmspacksoc']
+            df_bms_drive_timetable.loc[df_bms_drive_timetable_index,'drive_end_soc']=drive_end_soc
+            df_bms_drive_timetable_index+=1#index++
+
+    #删除时间信息不齐全的行
+    df_bms_drive_timetable=df_bms_drive_timetable.dropna(subset=['drive_end_time','drive_start_time'])
+    
+    return df_bms_drive_timetable
+
+
+def read_df_bms(path):
+    '''从路径中读取df_bms,进行预处理'''
+    df_bms=pd.read_csv(path, encoding='gbk')#编码方式gbk
+    #筛选表头,重命名
+    bms_columns=['时间戳','总电流[A]','总电压[V]','SOC[%]']
+    df_bms=df_bms.loc[:,bms_columns].copy()
+    df_bms.rename(columns = {"时间戳": "time", "总电流[A]": "bmspackcrnt", 
+                             "总电压[V]": "bmspackvol", "SOC[%]": "bmspacksoc"},inplace=True)#表头替换
+    #时间格式调整
+    df_bms['time']=df_bms['time'].apply(lambda x:datetime.strptime(x,'%Y-%m-%d %H:%M:%S'))#时间格式调整
+    #进行预处理
+    df_bms=df_add_deltatime(df_bms)#增加deltatime列 
+    return df_bms
+
+def preprocess_Df_Bms(df_bms):
+    '''对获得的df_bms,进行预处理'''
+    #筛选表头,重命名
+    bms_columns=['时间戳','总电流[A]','总电压[V]','SOC[%]']
+    df_bms=df_bms.loc[:,bms_columns].copy()
+    df_bms.rename(columns = {"时间戳": "time", "总电流[A]": "bmspackcrnt", 
+                             "总电压[V]": "bmspackvol", "SOC[%]": "bmspacksoc"},inplace=True)#表头替换
+    #删除空行
+    df_bms=df_bms.dropna(subset=['time'])
+    #删除时间重复的行,保留第一次出现的行
+    df_bms=df_bms.drop_duplicates(subset=['time'],keep='first')
+    #时间格式调整
+    df_bms['time']=df_bms['time'].apply(lambda x:datetime.strptime(x,'%Y-%m-%d %H:%M:%S'))#时间格式调整
+    #进行预处理
+    df_bms=df_add_deltatime(df_bms)#增加deltatime列 
+    return df_bms
+
+
+def df_add_deltatime(df_in):
+    '''Add a columns:deltatime,input df must have time column.'''
+    for i in range(len(df_in)):
+        #首行默认为0
+        if i==0:
+            df_in.loc[i,'deltatime']=0
+        #从第二行开始,计算i行到i-1行,GPS距离之差
+        else:
+            time1=df_in.loc[i-1,'time']
+            time2=df_in.loc[i,'time']
+            deltatime=time_interval(time1,time2)#计算时间差,返回单位为秒。
+            df_in.loc[i,'deltatime']=deltatime
+    return df_in
+
+
+def time_interval(time1,time2):
+    """
+    Calculate the time interval between two times,
+    return the seconds
+    """
+    deltatime=time2-time1
+    return deltatime.seconds
+
+
+def cal_deltasoc(df_bms,start_time,end_time):
+    '''输入开始时间和结束时间,返回deltasoc,此处将deltasoc*1既等效为unrecorded_odo.'''
+    time_condition=(df_bms['time']>start_time)&(df_bms['time']<end_time)
+    df_bms_sub=df_bms.loc[time_condition,:].copy()
+    if len(df_bms_sub)>=2:
+        
+        df_bms_head=df_bms_sub.head(1).copy()#首行
+        df_bms_startsoc=df_bms_head['bmspacksoc'].values[0]
+        df_bms_tail=df_bms_sub.tail(1).copy()#尾行
+        df_bms_endsoc=df_bms_tail['bmspacksoc'].values[0]
+        delta_soc=df_bms_startsoc-df_bms_endsoc
+        
+        if delta_soc>0:
+            #如果df_bms出现时间不连续,则先计算deltasoc,deltasoc每变化1,续驶里程增加1,
+            unrecorded_odo=delta_soc*1
+            #print('From '+str(start_time)+' to  '+str(end_time)+' soc decrease:  '+str(delta_soc))
+        else:
+            unrecorded_odo=0#如果deltasoc不大于0,说明在充电,或者静置不动    
+    #如果行数少于2,无法计算
+    else:
+        unrecorded_odo=0
+    return unrecorded_odo

+ 139 - 0
LIB/MIDDLE/odo/ProcessDfGps.py

@@ -0,0 +1,139 @@
+import pandas as pd
+import numpy as np
+from datetime import datetime
+from datetime import timedelta
+from ProcessDfBms import *
+from math import radians, cos, sin, asin, sqrt
+
+def cal_unrecorded_gps(df_in,df_bms):
+    '''筛选出现gps时间断点的数据,用df_bms数据补齐,df_in为df_gps表格。'''
+    #未记录到的odo总和
+    accum_unrecorded_odo=0
+
+    #设置丢失的判断条件,获得信息丢失行的index
+    condition1=df_in['deltatime']>60*3#时间间隔大于3分钟。说明数据掉线了。
+    condition2=(df_in['deltatime']>90*1)&(df_in['distance']>1000)#时间间隔大于*分钟,且Distance间隔大于*,代表掉线了。
+    signal_start_list=df_in.loc[condition1|condition2,:].index.to_list()#信息丢失行
+    #如果第0行属于信息丢失行,则删除,因为需要index-1行
+    try:
+        signal_start_list.remove(0)
+    except:
+        pass
+    else:
+        pass
+    #筛选出所有GPS信号丢失,对应的开始时间-结束时间对。
+    if len(signal_start_list)>0:
+        signal_end_list=[num-1 for num in signal_start_list]#信息丢失行的前一行,此处可能如果是首行,可能会有bug。
+        pick_gps_list=[0]+signal_start_list+signal_end_list+[len(df_in)-1]#首行+尾行+信号开始行+信号结束行
+        pick_gps_list=sorted(pick_gps_list)#重新排序
+
+    #有出现信号断点的行,则进行以下计算。
+    if len(signal_start_list)>0:
+        #针对每个时间对,计算unrecorded odo
+        for start_time_index,end_time_index in zip(signal_start_list,signal_end_list):
+            last_end_time=df_in.loc[end_time_index,'time']
+            this_start_time=df_in.loc[start_time_index,'time']
+            #print('gps signal loss from: '+str(last_end_time)+'-to-'+str(this_start_time))
+            #使用cal_delatasoc计算预估里程
+            unrecorded_odo=cal_deltasoc(df_bms,last_end_time,this_start_time)
+            accum_unrecorded_odo+=unrecorded_odo
+        #print('accum_unrecorded_odo:'+str(accum_unrecorded_odo))
+    else:
+        pass
+    
+    return accum_unrecorded_odo
+
+
+def df_add_avgspeed(df_in):
+    '''Add a columns:avgspeed ,input df must have deltatime,distance column.'''
+    for i in range(len(df_in)):
+        #首行默认为0
+        if i==0:
+            df_in.loc[i,'avgspeed']=0
+        #从第二行开始,计算平均速度
+        else:
+            deltatime=df_in.loc[i,'deltatime']
+            distance=df_in.loc[i,'distance']
+            avgspeed=(distance/1000)/(deltatime/3600)
+            df_in.loc[i,'avgspeed']=avgspeed
+    return df_in
+
+
+def read_df_gps(path):
+    df_gps=pd.read_csv(path, encoding='gbk')#编码方式gbk
+    #重置表头
+    df_gps.rename(columns = {"时间戳": "time", "纬度":"lat", "经度":"lng", 
+                             "卫星数":"sat_num", "海拔m":"height","速度[km/h]":"speed"},  inplace=True)
+    #时间格式调整
+    df_gps['time']=pd.to_datetime(df_gps['time'])
+    #对gps进行清洗
+    df_gps=df_add_distance(df_gps)#增加distance列
+    condition=df_gps['distance']<20000#删除GPS漂移过远的点,可能为GPS错误值
+    df_gps=df_gps.loc[condition,:].copy()#删除condition中,avgspd过大的部分,很可能伴随着GPS的漂移。
+    df_gps=df_gps.reset_index(drop=True)#重置index
+    #进行预处理
+    df_gps=df_add_distance(df_gps)#增加distance列,再算一次distance
+    df_gps=df_add_deltatime(df_gps)#增加deltatime列
+    df_gps=df_add_avgspeed(df_gps)#增加avgspeed列
+
+    #df_gps.to_excel('df_gps.xlsx',sheet_name='Sheet1')
+    return df_gps
+
+def preprocess_Df_Gps(df_gps):
+    '''对Df_Gps进行预处理'''
+    #重置表头
+    df_gps.rename(columns = {"时间戳": "time", "纬度":"lat", "经度":"lng", 
+                             "卫星数":"sat_num", "海拔m":"height","速度[km/h]":"speed"},  inplace=True)
+    #删除含有空数据的行
+    df_gps=df_gps.dropna(subset=['time','lat','lng'])
+    #删除时间重复的行,保留第一次出现的行
+    df_gps=df_gps.drop_duplicates(subset=['time'],keep='first')
+    #时间格式调整
+    df_gps['time']=pd.to_datetime(df_gps['time'])
+    
+    #对gps进行清洗
+    df_gps=df_add_distance(df_gps)#增加distance列
+    condition=df_gps['distance']<20000#删除GPS漂移过远的点,可能为GPS错误值
+    df_gps=df_gps.loc[condition,:].copy()#删除condition中,avgspd过大的部分,很可能伴随着GPS的漂移。
+    df_gps=df_gps.reset_index(drop=True)#重置index
+    #进行预处理
+    df_gps=df_add_distance(df_gps)#增加distance列,再算一次distance
+    df_gps=df_add_deltatime(df_gps)#增加deltatime列
+    df_gps=df_gps.loc[df_gps['deltatime']>0.01,:].copy()#删除deltatime=0的列,两个时间戳相同,无法求速度。
+    df_gps=df_add_avgspeed(df_gps)#增加avgspeed列
+
+    #df_gps.to_excel('df_gps.xlsx',sheet_name='Sheet1')
+    return df_gps
+
+
+def df_add_distance(df_in):
+    '''Add a columns:distance,input df must have lng,lat columns.'''
+    for i in range(len(df_in)):
+        #首行默认为0
+        if i==0:
+            df_in.loc[i,'distance']=0
+        #从第二行开始,计算i行到i-1行,GPS距离之差
+        else:
+            lon1=df_in.loc[i-1,'lng']
+            lat1=df_in.loc[i-1,'lat']
+            lon2=df_in.loc[i,'lng']
+            lat2=df_in.loc[i,'lat']
+            distance=haversine(lon1,lat1,lon2,lat2)#haversine公式计算距离差
+            df_in.loc[i,'distance']=distance    
+    return df_in
+
+
+def haversine(lon1, lat1, lon2, lat2):
+    """
+    Calculate the great circle distance between two points 
+    on the earth (specified in decimal degrees)
+    """
+    # 将十进制度数转化为弧度
+    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
+    # haversine公式
+    dlon = lon2 - lon1 
+    dlat = lat2 - lat1 
+    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
+    c = 2 * asin(sqrt(a)) 
+    r = 6371 # 地球平均半径,单位为公里
+    return c * r * 1000

+ 293 - 0
LIB/MIDDLE/odo/UpdtFct.py

@@ -0,0 +1,293 @@
+import pandas as pd
+import pymysql
+from sqlalchemy import create_engine
+import datetime
+
+#建立引擎
+engine = create_engine(str(r"mysql+mysqldb://%s:" + '%s' + "@%s/%s") % ('root', 'pengmin', 'localhost', 'qixiangdb'))
+
+conn_qx = pymysql.connect(
+        host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com',
+        user='qx_cas',
+        password='Qx@123456',#Qx@123456
+        database='qx_cas',
+        charset='utf8'
+    )
+
+conn_local = pymysql.connect(
+        host='localhost',
+        user='root',
+        password='pengmin',
+        database='qixiangdb',
+        charset='utf8'
+    )
+
+def getNextSoc(start_soc):
+    '''输入当前的soc,寻找目标soc函数'''
+    if start_soc>80:
+        next_soc=80
+    elif start_soc>60:
+        next_soc=60
+    elif start_soc>40:
+        next_soc=40
+    elif start_soc>20:
+        next_soc=20
+    else:
+        next_soc=1
+    return next_soc
+
+def updtSnFct(sn_factor_df,end_soc,delta_range,range_soc):
+    '''输入当前的soc区间段,里程变量量,soc变化量,输出新的df
+    sn_factor_df为dataframe,delta_range单位为km,range_soc单位为km/persoc'''
+    if end_soc==80:
+        updtFctByCol(sn_factor_df,'a0',delta_range,range_soc)
+    elif end_soc==60:
+        updtFctByCol(sn_factor_df,'a1',delta_range,range_soc)
+    elif end_soc==40:
+        updtFctByCol(sn_factor_df,'a2',delta_range,range_soc)
+    elif end_soc==20:
+        updtFctByCol(sn_factor_df,'a3',delta_range,range_soc)
+    elif end_soc<20:
+        updtFctByCol(sn_factor_df,'a4',delta_range,range_soc)
+    return sn_factor_df
+
+def updtFctByCol(sn_factor_df,colmun_name,delta_range,range_soc):
+    '''更新制定列的factor,sn_factor_df为dataframe,新的系数更新到第一行。delta_range单位为km,
+    range_soc单位为km/persoc,默认按照100km更新续驶里程权重'''
+    range_soc_old=sn_factor_df.loc[0,colmun_name]#读取第0行的老factor
+    debounce_range=200#更新权重
+    new_factor=range_soc*((delta_range)/debounce_range)+range_soc_old*(1-(delta_range)/debounce_range)
+    #在第1行,存储新的factor
+    sn_factor_df.loc[1,colmun_name]=new_factor
+    return sn_factor_df
+
+def updtTodayFct(factor_input,sn_day_df):
+    '''更新今日的Factor***'''
+    sn_factor_df_last=factor_input
+    start_soc=sn_day_df.loc[0,'soc']
+    next_soc=getNextSoc(start_soc)
+    start_range=sn_day_df.loc[0,'vehodo']
+    sn=sn_day_df.loc[0,'name']
+
+    for index in range(len(sn_day_df)-1):
+    #寻找分割点,
+        index_soc=sn_day_df.loc[index,'soc']#当前行soc
+        next_index_soc=sn_day_df.loc[index+1,'soc']#下一行soc
+
+        if (index_soc>=next_soc)&(next_index_soc<next_soc):#当前行高,下一行低
+            delta_soc_tonext=start_soc-next_soc#两个距离点的soc差,单位为%
+            delta_range_tonext=sn_day_df.loc[index,'vehodo']-start_range#两个时间点的距离差,单位为m
+            delta_range_tonext_km=delta_range_tonext/1000#两个时间点的距离差,单位为km
+            range_soc_tonext=(delta_range_tonext/1000)/delta_soc_tonext#单位soc可行驶的公里数
+            print(sn+'start_soc: '+str(start_soc),'next_soc: '+str(next_soc),'delta_vehodo; '+str(round(delta_range_tonext_km,3))
+            +'km'+' range_soc:'+str(round(range_soc_tonext,3)))
+
+            if (delta_range_tonext_km)>1:
+                sn_factor_df_last=updtSnFct(sn_factor_df_last,next_soc,delta_range_tonext_km,range_soc_tonext)
+            
+            start_soc=next_index_soc#变更开始soc
+            next_soc=getNextSoc(start_soc)#变更结束soc
+            start_range=sn_day_df.loc[index+1,'vehodo']#变更开始里程    
+
+    return sn_factor_df_last
+
+def snDayDfPreProcess(sn_day_df):
+    '''预处理,判断是否在dirvemode,获取drivemode条件下的累计行驶距离。
+    增加delta_soc列,drive_flg列,vehodo列'''
+    sn_day_df=sn_day_df.reset_index(drop=True)#重置index
+    #增加列,计算delta_soc
+    for index in range(len(sn_day_df)):
+        if index==0:
+            sn_day_df.loc[index,'delta_soc']=0
+        else:
+            sn_day_df.loc[index,'delta_soc']=sn_day_df.loc[index,'soc']-sn_day_df.loc[index-1,'soc']
+    #增加列,判断是否在drive状态
+    drive_flg=False
+    accum_distance=0
+    for index in range(len(sn_day_df)):
+        if index==0:
+            sn_day_df.loc[index,'drive_status']=drive_flg
+            sn_day_df.loc[index,'vehodo']=0
+        else:
+            if (sn_day_df.loc[index,'delta_soc']<-0.1)|\
+                ((sn_day_df.loc[index,'delta_soc']<=0)&(sn_day_df.loc[index,'distance']>500)):#soc处于下降状态,说明在drive
+                drive_flg=True#置true
+            elif sn_day_df.loc[index,'delta_soc']>0.1:#soc处于上升状态,说明不在drive
+                drive_flg=False#置false
+                accum_distance=0#清零
+            sn_day_df.loc[index,'drive_flg']=drive_flg
+            accum_distance+=sn_day_df.loc[index,'distance']#对行驶里程进行累加
+            sn_day_df.loc[index,'vehodo']=accum_distance
+    #筛选所有的drive信息行
+    sn_day_drive_df=sn_day_df.loc[sn_day_df['drive_flg']==True,:]
+    sn_day_drive_df=sn_day_drive_df.reset_index(drop=True)#重置index
+    
+    return sn_day_drive_df 
+
+def updtAllSnFct(start_date,end_date):
+    '''计算开始时间到结束时间的,所有sn的factor'''
+    start_date_datetime=datetime.datetime.strptime(start_date,'%Y-%m-%d')#开始时间
+    end_date_datetime=datetime.datetime.strptime(end_date,'%Y-%m-%d')#开始时间
+    delta_day=(end_date_datetime-start_date_datetime).days#间隔天数
+    i=1
+    while i<=delta_day:
+        end_date=(start_date_datetime+datetime.timedelta(days=i)).strftime("%Y-%m-%d")
+        updtAllSnTodayFct(start_date,end_date)#调用函数
+        print('update all sn factor from '+start_date+" to "+end_date)
+        start_date=end_date
+        i+=1#自加
+
+def updtAllSnTodayFct(start_date,end_date):
+    ''''更新今天所有sn的factorx信息,start_date和end_date相隔一天。此处还可优化'''
+    start_date_str="'"+start_date+"'"
+    end_date_str="'"+end_date+"'"
+    sql_cmd="select * from drive_info where time between "+start_date_str+" and "+end_date_str+" and distance!=0;"
+    range_soc_df = pd.read_sql(sql_cmd, conn_qx)#使用read_sql方法查询qx数据库
+
+    #筛选出所有当日数据之后,筛选当日有更新的sn
+    today_sn_list=range_soc_df['name'].unique().tolist()#[:100]#先一次更新5个
+    #建立空的dataframe,用于承接所有更新的factor信息
+    today_sn_fct_df=pd.DataFrame([],columns=['sn','date','a0','a1','a2','a3','a4'])
+
+    for sn in today_sn_list:
+        #寻找factor_df,里面是否有sn号,如果没有sn对应信息,则新增信息。
+        sn_str="'"+sn+"'"
+        sql_cmd2="select sn,date,a0,a1,a2,a3,a4 from tb_sn_factor where date<"+start_date_str+" and sn="+sn_str
+        #此处可以限定每次查询的数量,例如不高于5行
+        factor_df=pd.read_sql(sql_cmd2, conn_local)#使用read_sql方法查询local数据库
+
+        #按照sn号和日期进行去重,避免运行时重复产生factor数据,保留第一次出现的行。
+        factor_df=factor_df.drop_duplicates(subset=['sn','date'],keep='first')
+
+        if len(factor_df)==0:
+            #如果没有搜索到factor历史数据,则声明一个新的进行初始化
+            start_date_datetime=datetime.datetime.strptime(start_date,'%Y-%m-%d')
+            yesterday=(start_date_datetime+datetime.timedelta(days=-1)).strftime("%Y-%m-%d")
+            #为sn申请一个新的factor,初始值为1
+            factor_df=pd.DataFrame({'sn':sn,'date':yesterday,'a0':[1],'a1':[1],'a2':[1],'a3':[1],'a4':[1]})
+        sn_factor_df=factor_df.loc[factor_df['sn']==sn,:]#筛选sn对应的factor
+        sn_factor_df=sn_factor_df.sort_values(by='date',ascending='True')#按照日期排序
+
+        sn_factor_df_last=sn_factor_df.tail(1).copy()#寻找最后一行,代表最近日期
+        sn_factor_df_last=sn_factor_df_last.append(sn_factor_df_last)#新增加一行,用于存储新的factor
+        sn_factor_df_last=sn_factor_df_last.reset_index(drop=True)#重置index
+        sn_factor_df_last.loc[1,'date']=start_date#更改后一行的date为当前日期
+        #筛选对应车辆的信息
+        condition_sn=(range_soc_df['name']==sn)
+        sn_day_df=range_soc_df.loc[condition_sn,:].copy()
+        sn_day_df=sn_day_df.reset_index(drop=True)
+        #使用updtTodayFct函数更新今天的factor
+        if len(sn_day_df)>=2:
+            #使用process函数,进行预处理
+            sn_day_df=snDayDfPreProcess(sn_day_df)#预处理函数
+            if len(sn_day_df)>=2:
+                sn_factor_df_new=updtTodayFct(sn_factor_df_last,sn_day_df)#
+                today_sn_fct_df=today_sn_fct_df.append(sn_factor_df_new.loc[1,:])#筛选第一行,进行拼接,最后写入到数据库中
+    
+    #将today_sn_fct_df写入到数据库中,今天所有factor更新的系数,一次写入。
+    if len(today_sn_fct_df)>=1:
+        today_sn_fct_df.to_sql('tb_sn_factor',con=engine,chunksize=10000,if_exists='append',index=False)
+
+def updtOneSnFct(sn,start_date,end_date):
+    '''计算开始时间到结束时间的,一个sn的所有factor'''
+    start_date_datetime=datetime.datetime.strptime(start_date,'%Y-%m-%d')#开始时间
+    end_date_datetime=datetime.datetime.strptime(end_date,'%Y-%m-%d')#开始时间
+    delta_day=(end_date_datetime-start_date_datetime).days#间隔天数
+    i=1
+    while i<=delta_day:
+        end_date=(start_date_datetime+datetime.timedelta(days=i)).strftime("%Y-%m-%d")
+        updtOneSnTodayFct(sn,start_date,end_date)#调用函数
+        print('update one sn factor from '+start_date+" to "+end_date)
+        start_date=end_date
+        i+=1#自加
+
+def updtOneSnTodayFct(sn,start_date,end_date):
+    start_date_str="'"+start_date+"'"
+    end_date_str="'"+end_date+"'"
+    sn_str="'"+sn+"'"
+    sql_cmd="select * from drive_info where time between "+start_date_str+" and "+end_date_str+\
+    " and distance!=0 and name="+sn_str
+    range_soc_df = pd.read_sql(sql_cmd, conn_qx)#使用read_sql方法查询qx数据库
+
+    if len(range_soc_df)>0:
+        #筛选出所有当日数据之后,筛选当日有更新的sn
+        today_sn_list=range_soc_df['name'].unique().tolist()
+        #建立空的dataframe,用于承接所有更新的factor信息
+        today_sn_fct_df=pd.DataFrame([],columns=['sn','date','a0','a1','a2','a3','a4'])
+
+        for sn in today_sn_list:
+            #寻找factor_df,里面是否有sn号,如果没有sn对应信息,则新增信息。
+            sn_str="'"+sn+"'"
+            sql_cmd2="select sn,date,a0,a1,a2,a3,a4 from tb_sn_factor where date<"+start_date_str+" and sn="+sn_str
+            factor_df=pd.read_sql(sql_cmd2, conn_local)#使用read_sql方法查询local数据库
+
+            #按照sn号和日期进行去重,避免运行时重复产生factor数据,保留第一次出现的行。
+            factor_df=factor_df.drop_duplicates(subset=['sn','date'],keep='first')
+
+            if len(factor_df)==0:
+                #如果没有搜索到factor历史数据,则声明一个新的进行初始化
+                start_date_datetime=datetime.datetime.strptime(start_date,'%Y-%m-%d')
+                yesterday=(start_date_datetime+datetime.timedelta(days=-1)).strftime("%Y-%m-%d")
+                factor_df=pd.DataFrame({'sn':sn,'date':yesterday,'a0':[1],'a1':[1],'a2':[1],'a3':[1],'a4':[1]})
+                today_sn_fct_df=today_sn_fct_df.append(factor_df.loc[0,:])#将初始化的行记录到数据库
+
+            sn_factor_df=factor_df.loc[factor_df['sn']==sn,:]#筛选sn对应的factor
+            
+            sn_factor_df=sn_factor_df.sort_values(by='date',ascending='True')#按照日期排序
+            sn_factor_df_last=sn_factor_df.tail(1).copy()#寻找最后一行,代表最近日期
+            sn_factor_df_last=sn_factor_df_last.append(sn_factor_df_last)#新增加一行,用于存储新的factor
+            sn_factor_df_last=sn_factor_df_last.reset_index(drop=True)#重置index
+            sn_factor_df_last.loc[1,'date']=start_date#更改后一行的date为当前日期
+            #筛选对应车辆的信息
+            condition_sn=(range_soc_df['name']==sn)
+            sn_day_df=range_soc_df.loc[condition_sn,:].copy()
+            sn_day_df=sn_day_df.reset_index(drop=True)
+            #使用updtTodayFct函数更新今天的factor
+            if len(sn_day_df)>=2:
+                #使用process函数,进行预处理
+                sn_day_df=snDayDfPreProcess(sn_day_df)#!!!!!!!!!!!增加
+                if len(sn_day_df)>=2:
+                    sn_factor_df_new=updtTodayFct(sn_factor_df_last,sn_day_df)#
+                    today_sn_fct_df=today_sn_fct_df.append(sn_factor_df_new.loc[1,:])#筛选第一行,进行拼接,最后写入到数据库中
+        
+        # #将today_sn_fct_df写入到数据库中
+        if len(today_sn_fct_df)>=1:
+            today_sn_fct_df.to_sql('tb_sn_factor',con=engine,chunksize=10000,if_exists='append',index=False)
+            # print(sn+' factor will be update in table tb_sn_factor!')
+        return today_sn_fct_df
+
+
+
+
+
+# def updtASnTodayFct(start_date,end_date,today_sn_list):
+
+#     sql_cmd="select * from qixiang_test where time>='"+start_date+"' and time<='"+end_date+"'"
+#     range_soc_df = pd.read_sql(sql_cmd, conn)#使用read_sql方法查询数据库
+
+#     sql_cmd2="select sn,date,a0,a1,a2,a3,a4 from tb_sn_factor where date<'"+start_date+"'"
+#     factor_df=pd.read_sql(sql_cmd2, conn)#使用read_sql方法查询数据库
+
+#     #筛选出所有当日数据之后,筛选当日有更新的sn
+#     # today_sn_list=range_soc_df['sn'].unique().tolist()
+#     # today_sn_list=today_sn_list[:10]#更新若干个
+#     #建立空的dataframe,用于承接所有更新的factor信息
+#     today_sn_fct_df=pd.DataFrame([],columns=['sn','date','a0','a1','a2','a3','a4'])
+
+#     for sn in today_sn_list:
+#         sn_factor_df=factor_df.loc[factor_df['sn']==sn,:]#筛选sn对应的factor
+#         sn_factor_df=sn_factor_df.sort_values(by='date',ascending='True')#按照日期排序
+#         sn_factor_df_last=sn_factor_df.tail(1).copy()#寻找最后一行,代表最近日期
+#         sn_factor_df_last=sn_factor_df_last.append(sn_factor_df_last)#新增加一行,用于存储新的factor
+#         sn_factor_df_last=sn_factor_df_last.reset_index(drop=True)#重置index
+#         sn_factor_df_last.loc[1,'date']=start_date#更改后一行的date为当前日期
+#         #筛选对应车辆的信息
+#         condition_sn=(range_soc_df['sn']==sn)
+#         sn_day_df=range_soc_df.loc[condition_sn,:].copy()
+#         sn_day_df=sn_day_df.reset_index(drop=True)
+#         #使用updtTodayFct函数更新今天的factor
+#         sn_factor_df_new=updtTodayFct(sn_factor_df_last,sn_day_df)
+#         today_sn_fct_df=today_sn_fct_df.append(sn_factor_df_new.loc[1,:])#筛选第一行,进行拼接,最后写入到数据库中
+    
+#     #将today_sn_fct_df写入到数据库中
+#     today_sn_fct_df.to_sql('tb_sn_factor',con=engine,chunksize=10000,if_exists='append',index=False)

+ 28 - 0
LIB/MIDDLE/odo/UpdtFct_Main.py

@@ -0,0 +1,28 @@
+import pandas as pd
+import pymysql
+from sqlalchemy import create_engine
+import datetime
+from UpdtFct import *
+
+
+conn_qx = pymysql.connect(
+        host='rm-bp10j10qy42bzy0q77o.mysql.rds.aliyuncs.com',
+        user='qx_cas',
+        password='Qx@123456',#Qx@123456
+        database='qx_cas',
+        charset='utf8'
+    )
+
+conn_local = pymysql.connect(
+        host='localhost',
+        user='root',
+        password='pengmin',
+        database='qixiangdb',
+        charset='utf8'
+    )
+
+#指定开始时间,结束时间,更新所有sn的factor
+start_date="2021-07-18"
+end_date="2021-08-01"
+
+updtAllSnFct(start_date,end_date)

BIN
LIB/MIDDLE/odo/__pycache__/CalDist.cpython-38.pyc


+ 304 - 0
LIB/MIDDLE/soh/LFPSoh_20210711.py

@@ -0,0 +1,304 @@
+# 获取数据
+from LIB.BACKEND import DBManager
+
+import os
+import pandas as pd
+import numpy as np
+import datetime
+# import matplotlib.pyplot as plt
+
+#参数输入
+Capacity = 54
+PackFullChrgVolt=69.99
+CellFullChrgVolt=3.5
+CellVoltNums=20
+CellTempNums=4
+FullChrgSoc=98
+PeakSoc=57
+# #40Ah-OCV
+# LookTab_SOC = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100]
+# LookTab_OCV = [3.3159, 3.4502, 3.4904, 3.5277, 3.5590, 3.5888, 3.6146, 3.6312, 3.6467, 3.6642, 3.6865, 3.7171, 3.7617,
+#                3.8031, 3.8440, 3.8888, 3.9376, 3.9891, 4.0451, 4.1068, 4.1830]
+#55Ah-OCV
+LookTab_SOC = [0.00, 	2.40, 	6.38, 	10.37, 	14.35, 	18.33, 	22.32, 	26.30, 	30.28, 	35.26, 	40.24, 	45.22, 	50.20, 	54.19, 	58.17, 	60.16, 	65.14, 	70.12, 	75.10, 	80.08, 	84.06, 	88.05, 	92.03, 	96.02, 	100.00]
+LookTab_OCV = [2.7151,	3.0298,	3.1935,	3.2009,	3.2167,	3.2393,	3.2561,	3.2703,	3.2843,	3.2871,	3.2874,	3.2868,	3.2896,	3.2917,	3.2967,	3.3128,	3.3283,	3.3286,	3.3287,	3.3288,	3.3289,	3.3296,	3.3302,	3.3314,	3.3429]
+
+#定义滑动滤波函数
+def np_move_avg(a, n, mode="same"):
+    return (np.convolve(a, np.ones((n,)) / n, mode=mode))
+
+#参数初始化
+dvdq_soh=[]
+dvdq_soh_err=[]
+bms_soh=[]
+dvdq_time=[]
+dvdq_sohcfd=[]
+sn_list=[]
+
+
+#输入一个含有‘SN号’的xlsx
+def cal_soh(sn, end_time, start_time):
+    #获取数据时间段
+    end_time = end_time
+    strat_time = start_time
+    SNnum=str(sn)
+    sn = SNnum
+    st = start_time
+    et = end_time
+
+    dbManager = DBManager.DBManager()
+    df_data = dbManager.get_data(sn=sn, start_time=st, end_time=et, data_groups=['bms'])
+    data = df_data['bms']
+
+    packcrnt=data['总电流[A]']
+    packvolt=data['总电压[V]']
+    SOC=data['SOC[%]']
+    SOH=data['SOH[%]']
+    bmsstat=data['充电状态']
+    time= pd.to_datetime(data['时间戳'], format='%Y-%m-%d %H:%M:%S')
+
+    #第一步:筛选充电数据
+    ChgStart=[]
+    ChgEnd=[]
+    for i in range(3, len(time) - 3):
+        if i==3 and bmsstat[i]==2 and bmsstat[i+1]==2 and bmsstat[i+2]==2:
+            ChgStart.append(i)
+        elif bmsstat[i-2]!=2 and bmsstat[i-1]!=2 and bmsstat[i]==2:
+            ChgStart.append(i)
+        elif bmsstat[i-1]==2 and bmsstat[i]!=2 and bmsstat[i+1]!=2:
+            ChgEnd.append(i)
+        elif i == (len(time) - 4) and bmsstat[len(bmsstat)-1] == 2 and bmsstat[len(bmsstat)-2] == 2:
+            ChgEnd.append(len(time)-1)
+
+    #第二步:筛选充电起始Soc<48%,电芯温度>15℃,且满充的数据
+    ChgStartValid1=[]
+    ChgEndValid1=[]
+    ChgStartValid2=[]
+    ChgEndValid2=[]
+
+    for i in range(min(len(ChgStart),len(ChgEnd))):
+
+        #获取最小温度值
+        celltemp = []
+        for j in range(1, CellTempNums+1):
+            s = str(j)
+            temp = data['单体温度' + s]
+            celltemp.append(temp[ChgEnd[i]])
+
+        #寻找最大电压值
+        cellvolt = []
+        for j in range(1, CellVoltNums+1):
+            s = str(j)
+            volt = max(data['单体电压' + s][ChgStart[i]:ChgEnd[i]]/1000)
+            cellvolt.append(volt)
+
+        #筛选满足2点法计算的数据
+        StandingTime=0
+        if max(cellvolt)>CellFullChrgVolt and SOC[ChgStart[i]]<30 and min(celltemp)>5:
+            for k in reversed(range(ChgStart[i])):
+                if abs(packcrnt[k - 2]) < 0.01:
+                    StandingTime = StandingTime + (time[k] - time[k-1]).total_seconds()
+                    if StandingTime > 600:  # 筛选静置时间>10min
+                        ChgStartValid1.append(ChgStart[i])
+                        ChgEndValid1.append(ChgEnd[i])
+                        break
+                else:
+                    break
+
+        #筛选满足DV/DQ方法的数据
+        if max(cellvolt)>CellFullChrgVolt and SOC[ChgStart[i]]<45 and min(celltemp)>5:
+            if ((time[ChgEnd[i]]-time[ChgStart[i]]).total_seconds())/(ChgEnd[i]-ChgStart[i])<60:
+                ChgStartValid2.append(ChgStart[i])
+                ChgEndValid2.append(ChgEnd[i])
+
+    #第三步:计算充电Soc和Soh
+
+    # 两点法计算soh
+    Soc=[]
+    Time=[]
+    Soc_Err=[]
+    Bms_Soc=[]
+
+    Soh1=[]
+    Time1=[]
+    Bms_Soh1=[]
+    Soh_Err1=[]
+
+    for i in range(len(ChgStartValid1)):
+
+        #寻找最大电压值
+        cellvolt = []
+        for j in range(1, CellVoltNums+1):
+            s = str(j)
+            volt = max(data['单体电压' + s])
+            cellvolt.append(volt)
+        voltmax_index = cellvolt.index(max(cellvolt)) + 1
+        cellvolt = data['单体电压' + str(voltmax_index)] / 1000
+
+        #soc
+        Soc.append(np.interp(cellvolt[ChgStartValid1[i]-3],LookTab_OCV,LookTab_SOC))
+        Time.append(time[ChgStartValid1[i]-3])
+        Bms_Soc.append(SOC[ChgStartValid1[i]-3])
+        Soc_Err.append(Bms_Soc[-1]-Soc[-1])
+
+        #soh
+        Ocv_Soc=np.interp(cellvolt[ChgStartValid1[i]-3],LookTab_OCV,LookTab_SOC)
+        Ah=0
+
+        for j in range(ChgStartValid1[i],ChgEndValid1[i]):
+            #计算soc
+            Step=(time[j]-time[j-1]).total_seconds()
+            Time.append(time[j])
+            Bms_Soc.append(SOC[j])
+            if Soc[-1]-(packcrnt[j]*Step*100)/(3600*Capacity)<100:
+                Soc.append(Soc[-1]-(packcrnt[j]*Step*100)/(3600*Capacity))
+            else:
+                Soc.append(100)
+            Soc_Err.append(Bms_Soc[-1] - Soc[-1])
+
+            #两点法计算soh
+            Ah=Ah-packcrnt[j]*Step/3600
+        Soh1.append(Ah*100/((FullChrgSoc-Ocv_Soc)*0.01*Capacity))
+        Bms_Soh1.append(SOH[i])
+        Soh_Err1.append(Bms_Soh1[-1]-Soh1[-1])
+        Time1.append(time[ChgStartValid1[i]])
+
+    # DV/DQ法计算soh
+    Soh2=[]
+    Time2=[]
+    Bms_Soh2=[]
+    Soh_Err2=[]
+    SohCfd = []
+    sn_list=[]
+
+    for i in range(len(ChgStartValid2)):
+
+        #寻找最大电压值
+        cellvolt1 = []
+        cellvolt=[]
+        for j in range(1, CellVoltNums+1):
+            s = str(j)
+            volt = data['单体电压' + s]
+            cellvolt1.append(volt[ChgEndValid2[i]])
+        voltmax1_index = cellvolt1.index(max(cellvolt1)) + 1
+        cellvolt1 = data['单体电压' + str(voltmax1_index)] / 1000
+
+        #电压采用滑动平均滤波
+        cellvolt=np_move_avg(cellvolt1, 3, mode="same")
+
+
+        #参数赋初始值
+        Ah = 0
+        Volt = cellvolt[ChgStartValid2[i]]
+        DV_Volt=[]
+        DQ_Ah = []
+        DVDQ = []
+        time2 = []
+        soc2 = []
+        Ah_tatal=[0]
+        xvolt=[]
+        #计算DV和DQ值
+        for j in range(ChgStartValid2[i],ChgEndValid2[i]):
+            Step=(time[j+1]-time[j]).total_seconds()
+            Ah=Ah-packcrnt[j]*Step/3600
+            if (cellvolt[j]-Volt)>0.0009 and Ah>0:
+                Ah_tatal.append(Ah_tatal[-1]+Ah)
+                DQ_Ah.append(Ah)
+                DV_Volt.append(cellvolt[j]-Volt)
+                DVDQ.append((DV_Volt[-1])/DQ_Ah[-1])
+                xvolt.append(cellvolt[j])
+                Volt=cellvolt[j]
+                Ah = 0
+                time2.append(time[j])
+                soc2.append(SOC[j])
+
+        #切片Soc>50且Soc<80
+        Data1 = pd.DataFrame({'SOC': soc2,
+                                'DVDQ': DVDQ,
+                                'Ah_tatal': Ah_tatal[:-1],
+                                'DQ_Ah':DQ_Ah,
+                                'DV_Volt':DV_Volt,
+                                'XVOLT':xvolt})
+
+        Data1=Data1[(Data1['SOC']>50) & (Data1['SOC']<80)]
+
+        #寻找峰值并计算Soh和置信度
+        # 获取最小温度值
+        celltemp = []
+        for j in range(1, CellTempNums+1):
+            s = str(j)
+            temp = data['单体温度' + s]
+            celltemp.append(temp[ChgStartValid2[i]])
+        if len(Data1['DVDQ'])>1:
+            PeakIndex=Data1['DVDQ'].idxmax()
+            #筛选峰值点附近±0.5%SOC内的数据
+            Data2=Data1[(Data1['SOC']>(Data1['SOC'][PeakIndex]-0.5)) & (Data1['SOC']<(Data1['SOC'][PeakIndex]+0.5))]
+            if len(Data2)>2:
+                Ah_tatal1 = Data1['Ah_tatal']
+                DVDQ = Data1['DVDQ']
+                soc2 = Data1['SOC']
+                xvolt = Data1['XVOLT']
+                if soc2[PeakIndex]>50 and soc2[PeakIndex]<80:
+                    DVDQ_SOH=(Ah_tatal[-1]-Ah_tatal1[PeakIndex]) * 100 / ((FullChrgSoc - PeakSoc) * 0.01 * Capacity)
+                    if DVDQ_SOH<95:
+                        DVDQ_SOH=DVDQ_SOH*0.3926+58.14
+                    if DVDQ_SOH>70 and DVDQ_SOH<120:
+                        Soh2.append(DVDQ_SOH)
+                        Bms_Soh2.append(SOH[ChgStartValid2[i]])
+                        Soh_Err2.append(Bms_Soh2[-1] - Soh2[-1])
+                        Time2.append(time[ChgStartValid2[i]])
+                        sn_list.append(SNnum)
+
+                        #计算置信度
+                        if min(celltemp)<10:
+                            SohCfd.append(50)
+                        elif min(celltemp)<20:
+                            SohCfd.append(80)
+                        else:
+                            SohCfd.append(100)
+            else:
+                Data1=Data1.drop([PeakIndex])
+                PeakIndex = Data1['DVDQ'].idxmax()
+                Data2 = Data1[(Data1['SOC'] > (Data1['SOC'][PeakIndex] - 0.5)) & (Data1['SOC'] < (Data1['SOC'][PeakIndex] + 0.5))]
+                if len(Data2) > 3:
+                    Ah_tatal1 = Data1['Ah_tatal']
+                    DVDQ = Data1['DVDQ']
+                    soc2 = Data1['SOC']
+                    xvolt = Data1['XVOLT']
+                    if soc2[PeakIndex]>50 and soc2[PeakIndex]<80:
+                        DVDQ_SOH=(Ah_tatal[-1]-Ah_tatal1[PeakIndex]) * 100 / ((FullChrgSoc - PeakSoc) * 0.01 * Capacity)
+                        if DVDQ_SOH<95:
+                            DVDQ_SOH=DVDQ_SOH*0.3926+58.14
+                        if DVDQ_SOH>70 and DVDQ_SOH<120:
+                            Soh2.append(DVDQ_SOH)
+                            Bms_Soh2.append(SOH[ChgStartValid2[i]])
+                            Soh_Err2.append(Bms_Soh2[-1] - Soh2[-1])
+                            Time2.append(time[ChgStartValid2[i]])
+                            sn_list.append(SNnum)
+
+                            #计算置信度
+                            if min(celltemp)<10:
+                                SohCfd.append(50)
+                            elif min(celltemp)<20:
+                                SohCfd.append(80)
+                            else:
+                                SohCfd.append(100)
+
+    #处理数据
+    if len(Soh2)>5:
+        Soh2=np_move_avg(Soh2,5,mode="valid")
+        result_soh2={'时间': Time2[4::],
+            'SN号':sn_list[4::],
+            'BMS_SOH': Bms_Soh2[4::],
+            'SOH': Soh2,
+            'SOH误差': Soh_Err2[4::]}
+    else:
+        result_soh2={'时间': Time2,
+            'SN号':sn_list,
+            'BMS_SOH': Bms_Soh2,
+            'SOH': Soh2,
+            'SOH误差': Soh_Err2}
+    #第四步:将数据存入Excel
+    Result_Soh2=pd.DataFrame(result_soh2)
+    # Result_Soh2.to_csv('BMS_SOH_'+SNnum+'.csv',encoding='GB18030')
+    return Result_Soh2

+ 169 - 0
LIB/MIDDLE/soh/NCMSoh_20210716.py

@@ -0,0 +1,169 @@
+# 获取数据
+from LIB.BACKEND import DBManager
+
+import os
+import pandas as pd
+import numpy as np
+import datetime
+# import matplotlib.pyplot as plt
+
+#参数输入
+Capacity = 41
+PackFullChrgVolt=69.99
+CellFullChrgVolt=3.5
+CellVoltNums=17
+CellTempNums=4
+FullChrgSoc=98
+PeakSoc=57
+# #40Ah-OCV
+LookTab_SOC = [0,	3.534883489,	8.358178409,	13.18141871,	18.00471528,	22.82796155,	27.65123833,	32.47444668,	37.29772717,	42.12099502,	46.94423182,	51.76744813,	56.59070685,	61.4139927,	66.23719857,	71.0604667,	75.88373853,	80.70702266,	85.5302705,	90.35352009,	95.17676458,	100]
+LookTab_OCV = [3.3159,	3.4384,	3.4774,	3.5156,	3.5478,	3.5748,	3.6058,	3.6238,	3.638,	3.6535,	3.6715,	3.6951,	3.7279,	3.7757,	3.8126,	3.8529,	3.8969,	3.9446,	3.9946,	4.0491,	4.109,	4.183]
+# #55Ah-OCV
+# LookTab_SOC = [0.00, 	2.40, 	6.38, 	10.37, 	14.35, 	18.33, 	22.32, 	26.30, 	30.28, 	35.26, 	40.24, 	45.22, 	50.20, 	54.19, 	58.17, 	60.16, 	65.14, 	70.12, 	75.10, 	80.08, 	84.06, 	88.05, 	92.03, 	96.02, 	100.00]
+# LookTab_OCV = [2.7151,	3.0298,	3.1935,	3.2009,	3.2167,	3.2393,	3.2561,	3.2703,	3.2843,	3.2871,	3.2874,	3.2868,	3.2896,	3.2917,	3.2967,	3.3128,	3.3283,	3.3286,	3.3287,	3.3288,	3.3289,	3.3296,	3.3302,	3.3314,	3.3429]
+
+#参数初始化
+Soh3=[]
+Time3=[]
+Bms_Soh3=[]
+Soh_Err3=[]
+sn_list=[]
+
+#获取数据时间段
+
+def cal_soh(sn, end_time, start_time):
+    end_time = end_time
+    strat_time = start_time
+    SNnum=str(sn)
+
+    sn = sn
+    st = strat_time
+    et = end_time
+
+    dbManager = DBManager.DBManager()
+    df_data = dbManager.get_data(sn=sn, start_time=st, end_time=et, data_groups=['bms'])
+    data = df_data['bms']
+    # print(data)
+
+    packcrnt=data['总电流[A]']
+    packvolt=data['总电压[V]']
+    SOC=data['SOC[%]']
+    SOH=data['SOH[%]']
+    bmsstat=data['充电状态']
+    time= pd.to_datetime(data['时间戳'], format='%Y-%m-%d %H:%M:%S')
+
+    #第一步:筛选充电数据
+    if len(packcrnt)>100:
+        ChgStart=[]
+        ChgEnd=[]
+        for i in range(3, len(time) - 3):
+            if i==3 and bmsstat[i]==2 and bmsstat[i+1]==2 and bmsstat[i+2]==2:
+                ChgStart.append(i)
+            elif bmsstat[i-2]!=2 and bmsstat[i-1]!=2 and bmsstat[i]==2:
+                ChgStart.append(i)
+            elif bmsstat[i-1]==2 and bmsstat[i]!=2 and bmsstat[i+1]!=2:
+                ChgEnd.append(i-1)
+            elif i == (len(time) - 4) and bmsstat[len(bmsstat)-1] == 2 and bmsstat[len(bmsstat)-2] == 2:
+                ChgEnd.append(len(time)-2)
+
+        #第二步:筛选充电起始Soc<45% & SOC>85%,电芯温度>5℃
+        ChgStartValid1=[]
+        ChgEndValid1=[]
+        ChgStartValid2=[]
+        ChgEndValid2=[]
+        StandingNum=[]
+
+        for i in range(min(len(ChgStart),len(ChgEnd))):
+
+            #获取最小温度值
+            celltemp = []
+            for j in range(1, CellTempNums+1):
+                s = str(j)
+                temp = data['单体温度' + s]
+                celltemp.append(temp[ChgEnd[i]])
+            
+            #去除电流0点   
+            for k in range(ChgStart[i],ChgEnd[i]):
+                if packcrnt[k]<-0.5 and packcrnt[k+1]>-0.5 and packcrnt[k+2]>-0.5 and packcrnt[k+3]>-0.5:
+                    ChgEnd[i]=k
+            
+            #计算最大packvolt
+            if len(packvolt[ChgStart[i]:ChgEnd[i]])>0:
+                packvoltMAX=max(packvolt[ChgStart[i]:ChgEnd[i]])
+
+                #筛选满足2点法计算的数据
+                StandingTime=0
+                StandingTime1=0
+                StandingTime2=0
+                if SOC[ChgEnd[i]]>85 and SOC[ChgStart[i]]<45 and min(celltemp)>5:
+                    for m in range(min(len(packcrnt)-ChgEnd[i]-2,ChgStart[i]-2)):
+                        if abs(packcrnt[ChgStart[i] - m - 1]) < 0.1:
+                            StandingTime = StandingTime + (time[ChgStart[i] - m] - time[ChgStart[i] - m - 1]).total_seconds()
+                        if abs(packcrnt[ChgEnd[i] + m + 1]) < 0.1:
+                            StandingTime1 = StandingTime1 + (time[ChgEnd[i] + m + 1] - time[ChgEnd[i] + m]).total_seconds()
+                        if StandingTime > 900 and StandingTime1>900 and ((time[ChgEnd[i]]-time[ChgStart[i]]).total_seconds())/(ChgEnd[i]-ChgStart[i])<60:  #筛选静置时间>15min且慢充过程丢失数据少
+                            ChgStartValid1.append(ChgStart[i])
+                            ChgEndValid1.append(ChgEnd[i])
+                            StandingNum.append(m)
+                            break
+                        if abs(packcrnt[ChgStart[i] - m - 2])>0.5 and abs(packcrnt[ChgEnd[i] + m + 2])>0.5:
+                            break
+
+        # 计算soh
+        Soh1=[]
+        Soh2=[]
+        Time1=[]
+        Bms_Soh1=[]
+        Soh_Err1=[]
+        sn_list1=[]
+        #两点法计算Soh
+        if len(ChgStartValid1)>0:
+            for i in range(len(ChgStartValid1)):
+                #计算Ah
+                Ah=0
+                for j in range(ChgStartValid1[i],ChgEndValid1[i]):
+                    Step=(time[j+1]-time[j]).total_seconds()
+                    Ah=Ah-packcrnt[j+1]*Step/3600
+                #计算每个电芯的Soh
+                for j in range(1, CellVoltNums+1):
+                    s = str(j)
+                    cellvolt = data['单体电压' + s]/1000
+                    OCVStart=cellvolt[ChgStartValid1[i]-2]
+                    OCVEnd=cellvolt[ChgEndValid1[i]+StandingNum[i]]
+                    #soh
+                    Ocv_Soc1=np.interp(OCVStart,LookTab_OCV,LookTab_SOC)
+                    Ocv_Soc2=np.interp(OCVEnd,LookTab_OCV,LookTab_SOC)
+                    Soh2.append(Ah*100/((Ocv_Soc2-Ocv_Soc1)*0.01*Capacity))
+                Soh1.append(np.mean(Soh2))
+                Bms_Soh1.append(SOH[ChgStartValid1[i]])
+                Soh_Err1.append(Bms_Soh1[-1]-Soh1[-1])
+                Time1.append(time[ChgStartValid1[i]])
+                sn_list1.append(SNnum)
+       
+            # Soh3.append(np.mean(Soh1))
+            # Bms_Soh3.append(np.mean(Bms_Soh1))
+            # Soh_Err3.append(np.mean(Soh_Err1))
+            # Time3.append(time[ChgStartValid1[-1]])
+            # sn_list.append(SNnum)
+
+        #第四步:将数据存入Excel
+            result_soh2={'时间': Time1,
+                'SN号': sn_list1,
+                'BMS_SOH': Bms_Soh1,
+                'SOH': Soh1,
+                'SOH误差': Soh_Err1}
+
+            Result_Soh2=pd.DataFrame(result_soh2)
+            # Result_Soh2.to_csv('BMS_SOH_'+SNnum+'.csv',encoding='GB18030')
+            return Result_Soh2
+    return pd.DataFrame()
+
+#     result_soh1={'时间': Time3,
+#         'SN号':sn_list,
+#         'BMS_SOH': Bms_Soh3,
+#         'SOH': Soh3,
+#         'SOH误差': Soh_Err3}
+
+# Result_Soh1=pd.DataFrame(result_soh1)
+# print(Result_Soh1)
+# Result_Soh1.to_csv('BMS_SOH_'+'6040'+'.csv',encoding='GB18030')

+ 17 - 2
demo.ipynb

@@ -116,11 +116,26 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 2,
    "source": [
     "# 生成pydoc 说明文档\r\n",
-    "! python -m pydoc -w LIB\\BACKEND\\Tools.py"
+    "!python -m pydoc -w LIB\\BACKEND\\DataPreProcess.py"
    ],
+   "outputs": [
+    {
+     "output_type": "stream",
+     "name": "stdout",
+     "text": [
+      "problem in LIB\\BACKEND\\DataPreProcess.py - ModuleNotFoundError: No module named 'DBManager'\n"
+     ]
+    }
+   ],
+   "metadata": {}
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "source": [],
    "outputs": [],
    "metadata": {}
   }

+ 1 - 1
函数说明/DBManager.html

@@ -107,5 +107,5 @@ Data descriptors defined here:<br>
 <font color="#ffffff" face="helvetica, arial"><big><strong>Author</strong></big></font></td></tr>
     
 <tr><td bgcolor="#7799ee"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
-<td width="100%">wlm</td></tr></table>
+<td width="100%">lmstack</td></tr></table>
 </body></html>

+ 1 - 1
函数说明/DataPreProcess.html

@@ -147,5 +147,5 @@ Data descriptors defined here:<br>
 <font color="#ffffff" face="helvetica, arial"><big><strong>Author</strong></big></font></td></tr>
     
 <tr><td bgcolor="#7799ee"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
-<td width="100%">wlm</td></tr></table>
+<td width="100%">lmstack</td></tr></table>
 </body></html>

+ 1 - 1
函数说明/IndexStaByOneCycle.html

@@ -141,5 +141,5 @@ Data descriptors defined here:<br>
 <font color="#ffffff" face="helvetica, arial"><big><strong>Author</strong></big></font></td></tr>
     
 <tr><td bgcolor="#7799ee"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
-<td width="100%">wlm</td></tr></table>
+<td width="100%">lmstack</td></tr></table>
 </body></html>

+ 1 - 1
函数说明/IndexStaByPeriod.html

@@ -111,5 +111,5 @@ Data descriptors defined here:<br>
 <font color="#ffffff" face="helvetica, arial"><big><strong>Author</strong></big></font></td></tr>
     
 <tr><td bgcolor="#7799ee"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
-<td width="100%">wlm</td></tr></table>
+<td width="100%">lmstack</td></tr></table>
 </body></html>

+ 1 - 1
函数说明/Log.html

@@ -76,5 +76,5 @@ Data descriptors defined here:<br>
 <font color="#ffffff" face="helvetica, arial"><big><strong>Author</strong></big></font></td></tr>
     
 <tr><td bgcolor="#7799ee"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
-<td width="100%">wlm</td></tr></table>
+<td width="100%">lmstack</td></tr></table>
 </body></html>

+ 1 - 1
函数说明/Tools.html

@@ -82,5 +82,5 @@ Data descriptors defined here:<br>
 <font color="#ffffff" face="helvetica, arial"><big><strong>Author</strong></big></font></td></tr>
     
 <tr><td bgcolor="#7799ee"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
-<td width="100%">wlm</td></tr></table>
+<td width="100%">lmstack</td></tr></table>
 </body></html>