638 lines
33 KiB
Python
638 lines
33 KiB
Python
import time
|
|
import logging
|
|
import datetime
|
|
import requests
|
|
import numpy as np
|
|
import pandas as pd
|
|
from pathlib import Path
|
|
import pandas as pd
|
|
import matplotlib.pyplot as plt
|
|
import matplotlib.colors as mcolors
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy import MetaData, Table, Column, String, Float, Integer, DateTime
|
|
|
|
API_URL = "https://energy-iot.chinatowercom.cn/api/device/device/historyPerformance"
|
|
|
|
API_HEADER = {
|
|
"accept": "application/json, text/plain, */*",
|
|
"Accept-Encoding": "gzip, deflate, br, zstd",
|
|
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
|
"Connection": "keep-alive",
|
|
"Content-Length": "170",
|
|
"content-type": "application/json;charset=UTF-8",
|
|
"Cookie": "HWWAFSESID=455f2793ca86a3aaf0; HWWAFSESTIME=1734509454212; dc04ed2361044be8a9355f6efb378cf2=WyIyODM4MDM2MDcxIl0",
|
|
"authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiIl0sInVzZXJfbmFtZSI6IndlYl9tYW5hZ2V8d2FuZ2xlaTQiLCJzY29wZSI6WyJhbGwiXSwiZXhwIjoxNzM0NjYzNDQ5LCJ1c2VySWQiOjI0Mjg1LCJqdGkiOiJhZmZhNmY1My05ZDA4LTQ2ODUtODU3MS05YzA5ODAxMGJjZWYiLCJjbGllbnRfaWQiOiJ3ZWJfbWFuYWdlIn0.q0X4qrgL4wRUTZL8c_5oTIUGW0Lsivxw8pYQ1iMIqLnyJrUeS7IQKNRavMhc4NEdQ9uG6ZgFVHIj80HbzH8DHCxssCPLdv9_TXksI5podU2aU6Vjh6AaN1THFYAE2uflj1saBnQ5_gKiK0-cAcXDeJNSt_u6Cd9hI1ejEUPPzO_hLg-NLzch7yIB-HPvhoDNnl0n5pSYoQpT8XaKT14HezL3VQrLII69Vme38S2dMmmkiAeIyhHQi56kXZ11K45Lu5bHXv6fDg2Mfr9VgVuTleZldiO69BAmG0h1-HqTQuGE39jtGWrrCnFduRZR6VsaOWWJy3qyqUbXWMOli1Yy1g",
|
|
"Host": "energy-iot.chinatowercom.cn",
|
|
"Origin": "https://energy-iot.chinatowercom.cn",
|
|
"Sec-Fetch-Dest": "empty",
|
|
"Sec-Fetch-Mode": "cors",
|
|
"Sec-Fetch-Site": "same-origin",
|
|
"sec-ch-ua": "\"Microsoft Edge\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"",
|
|
"sec-ch-ua-mobile": "?0",
|
|
"sec-ch-ua-platform": "Windows",
|
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0",
|
|
}
|
|
|
|
SemaMap_adapter = {
|
|
'apt_facturer': ('0305113001', 'adapter', False, "厂家"),
|
|
'apt_version': ('0305114001', 'adapter', False, "软件版本"),
|
|
'apt_model': ('0305115001', 'adapter', False, "型号"),
|
|
'apt_status': ('0305116001', 'adapter', False, "开关机状态"),
|
|
'apt_temp': ('0305117001', 'adapter', True, "温度"),
|
|
'apt_volt_in': ('0305118001', 'adapter', True, "输入电压"),
|
|
'apt_curr_in': ('0305119001', 'adapter', True, "输入电流"),
|
|
'apt_volt_out': ('0305120001', 'adapter', True, "输出电压"),
|
|
'apt_curr_out': ('0305121001', 'adapter', True, "输出电流"),
|
|
'apt_power_out': ('0305122001', 'adapter', True, "输出功率"),
|
|
}
|
|
semamap_combiner = {
|
|
'cbr_IMSI': ('0305102001', 'combiner', False, "IMSI"),
|
|
'cbr_ICCID': ('0305103001', 'combiner', False, "SIM卡ICCID"),
|
|
'cbr_MSISDN': ('0305104001', 'combiner', False, "MSISDN"),
|
|
'cbr_dev_type': ('0305101001', 'combiner', False, "系统类型"),
|
|
'cbr_facturer': ('0305107001', 'combiner', False, "汇流箱厂家"),
|
|
'cbr_model': ('0305108001', 'combiner', False, "汇流箱型号"),
|
|
'cbr_ver_software': ('0305105001', 'combiner', False, "软件版本"),
|
|
'cbr_ver_hardware': ('0305106001', 'combiner', False, "硬件版本"),
|
|
'cbr_power_total': ('0305109001', 'combiner', True, "系统总功率"),
|
|
'cbr_energy_total': ('0305110001', 'combiner', True, "系统累计发电量"),
|
|
'cbr_energy_daily': ('0305111001', 'combiner', True, "系统日发电量"),
|
|
}
|
|
SemaMap_meter = {
|
|
'mtr_id': ('0305123001', 'meter', False, "电表号"),
|
|
'mtr_volt': ('0305124001', 'meter', True, "直流电压"),
|
|
'mtr_curr': ('0436101001', 'meter', True, "直流总电流"),
|
|
'mtr_power': ('0436102001', 'meter', True, "总有功功率"),
|
|
'mtr_energy_total': ('0305125001', 'meter', True, "总有功电能"),
|
|
'mtr_energy_daily': ('0305126001', 'meter', True, "日有功电能"),
|
|
'mtr_energy_total_T': ('0305127001', 'meter', True, "尖时段总正向有功电能"),
|
|
'mtr_energy_total_P': ('0305128001', 'meter', True, "峰时段总正向有功电能"),
|
|
'mtr_energy_total_F': ('0305129001', 'meter', True, "平时段总正向有功电能"),
|
|
'mtr_energy_total_V': ('0305130001', 'meter', True, "谷时段总正向有功电能"),
|
|
'mtr_energy_daily_T': ('0305131001', 'meter', True, "尖时段日正向有功电能"),
|
|
'mtr_energy_daily_P': ('0305132001', 'meter', True, "峰时段日正向有功电能"),
|
|
'mtr_energy_daily_F': ('0305133001', 'meter', True, "平时段日正向有功电能"),
|
|
'mtr_energy_daily_V': ('0305134001', 'meter', True, "谷时段日正向有功电能"),
|
|
}
|
|
|
|
Sema_Map = {
|
|
'cbr_IMSI': ('0305102001', 'TTE0101', False, "IMSI"),
|
|
'cbr_ICCID': ('0305103001', 'TTE0101', False, "SIM卡ICCID"),
|
|
'cbr_MSISDN': ('0305104001', 'TTE0101', False, "MSISDN"),
|
|
'cbr_dev_type': ('0305101001', 'TTE0101', False, "系统类型"),
|
|
'cbr_facturer': ('0305107001', 'TTE0101', False, "汇流箱厂家"),
|
|
'cbr_model': ('0305108001', 'TTE0101', False, "汇流箱型号"),
|
|
'cbr_ver_software': ('0305105001', 'TTE0101', False, "软件版本"),
|
|
'cbr_ver_hardware': ('0305106001', 'TTE0101', False, "硬件版本"),
|
|
'cbr_power_total': ('0305109001', 'TTE0101', True, "系统总功率"),
|
|
'cbr_energy_total': ('0305110001', 'TTE0101', True, "系统累计发电量"),
|
|
'cbr_energy_daily': ('0305111001', 'TTE0101', True, "系统日发电量"),
|
|
'apt_facturer': ('0305113001', 'TTE0102', False, "厂家"),
|
|
'apt_version': ('0305114001', 'TTE0102', False, "软件版本"),
|
|
'apt_model': ('0305115001', 'TTE0102', False, "型号"),
|
|
'apt_status': ('0305116001', 'TTE0102', False, "开关机状态"),
|
|
'apt_volt_in': ('0305118001', 'TTE0102', True, "输入电压"),
|
|
'apt_curr_in': ('0305119001', 'TTE0102', True, "输入电流"),
|
|
'apt_volt_out': ('0305120001', 'TTE0102', True, "输出电压"),
|
|
'apt_curr_out': ('0305121001', 'TTE0102', True, "输出电流"),
|
|
'apt_power_out': ('0305122001', 'TTE0102', True, "输出功率"),
|
|
'apt_temp': ('0305117001', 'TTE0102', True, "温度"),
|
|
'mtr_id': ('0305123001', 'TTE0103', False, "电表号"),
|
|
'mtr_volt': ('0305124001', 'TTE0103', True, "直流电压"),
|
|
'mtr_curr': ('0436101001', 'TTE0103', True, "直流总电流"),
|
|
'mtr_energy_total': ('0305125001', 'TTE0103', True, "总有功电能"),
|
|
'mtr_energy_daily': ('0305126001', 'TTE0103', True, "日有功电能"),
|
|
'mtr_power': ('0436102001', 'TTE0103', True, "总有功功率"),
|
|
'mtr_energy_total_T': ('0305127001', 'TTE0103', True, "尖时段总正向有功电能"),
|
|
'mtr_energy_total_P': ('0305128001', 'TTE0103', True, "峰时段总正向有功电能"),
|
|
'mtr_energy_total_F': ('0305129001', 'TTE0103', True, "平时段总正向有功电能"),
|
|
'mtr_energy_total_V': ('0305130001', 'TTE0103', True, "谷时段总正向有功电能"),
|
|
'mtr_energy_daily_T': ('0305131001', 'TTE0103', True, "尖时段日正向有功电能"),
|
|
'mtr_energy_daily_P': ('0305132001', 'TTE0103', True, "峰时段日正向有功电能"),
|
|
'mtr_energy_daily_F': ('0305133001', 'TTE0103', True, "平时段日正向有功电能"),
|
|
'mtr_energy_daily_V': ('0305134001', 'TTE0103', True, "谷时段日正向有功电能"),
|
|
}
|
|
|
|
API_Map = {
|
|
'refreshToken': ['https://energy-iot.chinatowercom.cn/api/auth/refreshToken', None],
|
|
'search_stn': ['https://energy-iot.chinatowercom.cn/api/device/station/list', None],
|
|
'search_dev': ['https://energy-iot.chinatowercom.cn/api/device/device/page', None],
|
|
'dev_info': ['https://energy-iot.chinatowercom.cn/api/device/device/devInfo', None],
|
|
'perf_real': ['https://energy-iot.chinatowercom.cn/api/device/device/perfReal', None],
|
|
'history': ['https://energy-iot.chinatowercom.cn/api/device/device/historyPerformance', [SemaMap_adapter, SemaMap_meter]],
|
|
'page': ['https://energy-iot.chinatowercom.cn/api/device/device/page', None],
|
|
'station': ['https://energy-iot.chinatowercom.cn/api/device/station/detail/', None],
|
|
}
|
|
|
|
class Lamina_Data(object):
|
|
""" 叠光主站数据分析 """
|
|
def __init__(self, database="sqlite:///:memory", header=None):
|
|
""" 初始化 """
|
|
self.engine = create_engine(database)
|
|
|
|
metadata = MetaData()
|
|
metadata.reflect(bind=self.engine)
|
|
if 'history' not in metadata.tables:
|
|
history_table = Table(
|
|
'history', metadata,
|
|
Column('dev', String(50)),
|
|
Column('mid', String(50)),
|
|
Column('time', DateTime),
|
|
Column('value', Float)
|
|
)
|
|
metadata.create_all(self.engine)
|
|
|
|
if 'log' not in metadata.tables:
|
|
log_table = Table(
|
|
'log', metadata,
|
|
Column('dev', String(50)),
|
|
Column('mid', String(50)),
|
|
Column('Timestamp_start', DateTime),
|
|
Column('Timestamp_end', DateTime),
|
|
)
|
|
metadata.create_all(self.engine)
|
|
|
|
self.data = {
|
|
'history': pd.read_sql_table('history', self.engine),
|
|
}
|
|
|
|
self.api_origin = {
|
|
'domain': 'https://energy-iot.chinatowercom.cn/api',
|
|
'header': API_HEADER,
|
|
}
|
|
|
|
def save_history_data(self):
|
|
""" 保存历史数据 """
|
|
|
|
data_memory = self.data['history']
|
|
data_file = pd.read_sql_table('history', self.engine)
|
|
|
|
merged_df = pd.merge(data_memory, data_file[['dev', 'mid', 'time']], on=['dev', 'mid', 'time'], how='left', indicator=True)
|
|
filtered_data_memory = merged_df[merged_df['_merge'] == 'left_only'].drop(columns='_merge')
|
|
filtered_data_memory.to_sql('history', self.engine, if_exists='append', index=False)
|
|
|
|
logging.critical(f"成功插入 {len(filtered_data_memory)} 条数据")
|
|
|
|
return len(filtered_data_memory)
|
|
|
|
def save_data(func):
|
|
""" 保存函数返回数据 """
|
|
def wrapper(*args, **kwds):
|
|
self: Lamina_Data = args[0]
|
|
result = func(*args, **kwds)
|
|
if isinstance(result, pd.DataFrame):
|
|
if result.shape[0] != 0:
|
|
self.data['history'] = pd.concat([self.data['history'], result], ignore_index=True)
|
|
return result
|
|
return wrapper
|
|
|
|
@save_data
|
|
def get_history_data_by_net(self, device_id, data_type, time_start:int, time_end:int, header=None):
|
|
""" 读取信号量历史数据, 返回接口json数据 """
|
|
if header is None:
|
|
header = self.api_origin['header']
|
|
|
|
body = {
|
|
"businessType": "7",
|
|
"startTimestamp": int(time_start * 1000),
|
|
"endTimestamp": int(time_end * 1000),
|
|
"deviceCode": f"{device_id}",
|
|
"mid": f"{data_type[0]}",
|
|
"pageNum": 1,
|
|
"pageSize": 10,
|
|
"total": 0
|
|
}
|
|
|
|
req = requests.post(API_URL, json=body, headers=header)
|
|
json_data = req.json()
|
|
if json_data['code'] == 200:
|
|
""" 数据读取成功 """
|
|
print(f"Get data success, mid={data_type[0]}, len={len(json_data['data'])}")
|
|
table_data = pd.DataFrame(json_data['data'], columns=['collectTime', 'mid', 'midName', 'value'])
|
|
table_data['dev'] = device_id
|
|
table_data['time'] = pd.to_datetime(table_data.collectTime)
|
|
table_data['value'] = pd.to_numeric(table_data.value)
|
|
return table_data[['dev', 'mid', 'time', 'value']]
|
|
else:
|
|
print(f"Get data fail, code={json_data['code']}, msg=\n\t{json_data['message']}")
|
|
raise ValueError(f"{json_data['message']}")
|
|
|
|
def get_real_data_by_net(self, device_id, fsu_id=None, header=None):
|
|
""" 读取设备当前数据, 返回接口json数据 """
|
|
if header is None:
|
|
header = self.api_origin['header']
|
|
|
|
body = {
|
|
"businessType": "7",
|
|
"devType": device_id[3:7],
|
|
"deviceCodes": device_id,
|
|
"type": "遥测"
|
|
}
|
|
|
|
if device_id[3:7] != "0101":
|
|
if fsu_id is None:
|
|
raise ValueError(f"Missing required parameters: fsu_id={fsu_id}")
|
|
body["fsuCode"] = fsu_id
|
|
|
|
req = requests.post(API_Map['perf_real'][0], json=body, headers=header)
|
|
json_data = req.json()
|
|
if json_data['code'] == 200:
|
|
""" 数据读取成功 """
|
|
print(f"Get data success, len={len(json_data['data'])}")
|
|
table_data = pd.DataFrame(json_data['data'])
|
|
column_name = sorted(table_data.columns)
|
|
table_data['dev'] = device_id
|
|
table_data['time'] = table_data['updateTime'].apply(lambda x: int(time.mktime(time.strptime(x, r"%Y-%m-%d %H:%M:%S"))))
|
|
table_data = table_data[['time', 'dev', *column_name]].drop(columns='updateTime')
|
|
return table_data
|
|
else:
|
|
print(f"Get data fail, code={json_data['code']}, msg=\n\t{json_data['message']}")
|
|
raise ValueError(f"{json_data['message']}")
|
|
|
|
def get_devinfo_data_by_net(self, device_id, data_type, time_start:int, time_end:int, header=None):
|
|
""" 读取设备信息, 返回接口json数据 """
|
|
if header is None:
|
|
header = self.api_origin['header']
|
|
|
|
body = {
|
|
"businessType": "7",
|
|
"id": int(data_type),
|
|
}
|
|
|
|
req = requests.post(API_Map['dev_info'][0], json=body, headers=header)
|
|
json_data = req.json()
|
|
if json_data['code'] == 200:
|
|
""" 数据读取成功 """
|
|
print(f"Get data success, len={len(json_data['data'])}")
|
|
table_data = pd.DataFrame(json_data['data'])
|
|
return table_data
|
|
else:
|
|
print(f"Get data fail, code={json_data['code']}, msg=\n\t{json_data['message']}")
|
|
raise ValueError(f"{json_data['message']}")
|
|
|
|
def spider_device(self, device_id:str, time_start:int, time_end:int):
|
|
""" 爬取设备数据 """
|
|
result = {}
|
|
key_list = list(filter(lambda x: Sema_Map[x][1] == device_id[:7] and Sema_Map[x][2], Sema_Map.keys()))
|
|
data_device = pd.DataFrame([], columns=['time', 'device', *map(lambda s: s.split('_', 1)[1], key_list)])
|
|
for key in key_list:
|
|
result[key] = self.get_history_data_by_net(device_id, Sema_Map[key], time_start, time_end)
|
|
if data_device.empty:
|
|
data_device.time = result[key].time
|
|
data_device.device = device_id
|
|
data_device[key[4:]] = result[key].value.apply(float)
|
|
return data_device
|
|
|
|
def spider_search_devices(self, device_id:str, header=None):
|
|
if header is None:
|
|
header = self.api_origin['header']
|
|
|
|
body = {
|
|
"devType": "",
|
|
"accessPointId": "",
|
|
"pageNum": 1,
|
|
"pageSize": 10,
|
|
"businessType": "7",
|
|
"devCode": device_id,
|
|
"deptIds": []
|
|
}
|
|
|
|
req = requests.post(API_Map['search_dev'][0], json=body, headers=header)
|
|
json_data = req.json()
|
|
if json_data['code'] != 200:
|
|
""" 数据读取失败 """
|
|
print(f"Get data fail, code={json_data['code']}, msg=\n\t{json_data['message']}")
|
|
return ""
|
|
elif search_dev := json_data['rows']:
|
|
print(f"Search device success, len={len(search_dev)}")
|
|
return search_dev[0]['stationCode']
|
|
else:
|
|
print(f"Search device fail.")
|
|
return ""
|
|
|
|
def spider_search_station(self, name:str, header=None):
|
|
if header is None:
|
|
header = self.api_origin['header']
|
|
|
|
body = {
|
|
"pageNum": 1,
|
|
"pageSize": 10,
|
|
"provinceId": "",
|
|
"cityId": "",
|
|
"countId": "",
|
|
"name": name,
|
|
"code": "",
|
|
"rsSource": "",
|
|
"businessType": "7",
|
|
"status": "",
|
|
"onlineStatus": "",
|
|
"maintenancePerson": "",
|
|
"deptIds": []
|
|
}
|
|
|
|
req = requests.post(API_Map['search_stn'][0], json=body, headers=header)
|
|
json_data = req.json()
|
|
if json_data['code'] != 200:
|
|
""" 数据读取失败 """
|
|
print(f"Get data fail, code={json_data['code']}, msg=\n\t{json_data['message']}")
|
|
return ""
|
|
elif search_stn := json_data['rows']:
|
|
print(f"Search station success, len={len(search_stn)}")
|
|
return search_stn[0]['code']
|
|
else:
|
|
print(f"Search station fail.")
|
|
return ""
|
|
|
|
def spider_station(self, search:str, time_start:int, time_end:int, header=None):
|
|
""" 爬取站点数据 """
|
|
if header is None:
|
|
header = self.api_origin['header']
|
|
|
|
if search[:3] == "TTE":
|
|
""" 设备编号 """
|
|
station_id = self.spider_search_devices(search, header=header)
|
|
else:
|
|
""" 站点名称 """
|
|
station_id = self.spider_search_station(search, header=header)
|
|
|
|
if station_id == "":
|
|
print(f"Search station fail.")
|
|
return {'result': False}
|
|
|
|
body = {
|
|
"businessType": "7",
|
|
"stationCode": station_id,
|
|
}
|
|
time.sleep(0.5)
|
|
|
|
print(f"Get Data for Station: {station_id}")
|
|
req = requests.post(API_Map['page'][0], json=body, headers=header)
|
|
json_data = req.json()
|
|
if json_data['code'] != 200:
|
|
""" 数据读取失败 """
|
|
print(f"Get data fail, code={json_data['code']}, msg=\n\t{json_data['message']}")
|
|
return ""
|
|
|
|
dev_meter = []
|
|
dev_adapter = []
|
|
dev_combiner = []
|
|
dev_info = []
|
|
try:
|
|
for dev in sorted(json_data['rows'], key=lambda x: x['devCode']):
|
|
print(f"Dev: {dev['devTypeName']}, id={dev['devCode']}")
|
|
time.sleep(0.5)
|
|
fsu_id = dev['parentCode'] if 'parentCode' in dev.keys() else None
|
|
dev_info.append(self.get_real_data_by_net(dev['devCode'], fsu_id, header=header))
|
|
time.sleep(0.5)
|
|
match dev['devType']:
|
|
case "0101":
|
|
fsu_id = dev['devCode']
|
|
dev_combiner.append(self.spider_device(dev['devCode'], time_start, time_end))
|
|
case "0102":
|
|
dev_adapter.append(self.spider_device(dev['devCode'], time_start, time_end))
|
|
case "0103":
|
|
dev_meter.append(self.spider_device(dev['devCode'], time_start, time_end))
|
|
self.save_history_data()
|
|
except Exception as e:
|
|
print(f"Get data fail, msg=\n\t{e}")
|
|
return {'result': False, 'token': e.args[0]}
|
|
result = {
|
|
'result': True,
|
|
'station': station_id,
|
|
'information': pd.concat(dev_info, ignore_index=True),
|
|
'combiner': pd.concat(dev_combiner, ignore_index=True),
|
|
'adapter': pd.concat(dev_adapter, ignore_index=True),
|
|
'meter': pd.concat(dev_meter, ignore_index=True),
|
|
}
|
|
print(f"Station Done.")
|
|
return result
|
|
|
|
def save_station_by_file1(data_lamina: Lamina_Data):
|
|
""" 依据文件爬取所需站点数据 """
|
|
time_start = datetime.datetime(2024, 12, 24, 0, 0, 0)
|
|
time_end = datetime.datetime(2024, 12, 26, 0, 0, 0)
|
|
time_start_timestamp = time.mktime(time_start.timetuple())
|
|
time_end_timestamp = time.mktime(time_end.timetuple())
|
|
stations = pd.read_excel(Path(r'C:\Users\wrqal\Documents\Obsidian Vault\附件\25号0发电适配器.xlsx'))
|
|
output_file = Path(r'result/output.xlsx')
|
|
if output_file.exists():
|
|
finished_station = pd.read_excel(output_file, sheet_name=None)
|
|
finished_station["Station"]['station'] = finished_station["Station"]['station'].astype('str')
|
|
finished_station["Adatper"]['station'] = finished_station["Adatper"]['station'].astype('str')
|
|
finished_station["Meter"]['station'] = finished_station["Meter"]['station'].astype('str')
|
|
merged_df = pd.merge(stations['点位名称'], finished_station['Station']['点位名称'], how='left', indicator=True)
|
|
remain_station = merged_df[merged_df['_merge'] == 'left_only'].drop(columns='_merge')
|
|
else:
|
|
remain_station = stations['点位名称']
|
|
|
|
dataset = []
|
|
for name in remain_station['点位名称']:
|
|
print(f"Station: {name}")
|
|
data = data_lamina.spider_station(name, time_start_timestamp, time_end_timestamp)
|
|
if data['result']:
|
|
dataset.append(data)
|
|
print(f"Done.")
|
|
|
|
# 使用 ExcelWriter 将多个 DataFrame 保存到不同的工作表中
|
|
df_station = pd.DataFrame([], columns=['station', '点位名称'])
|
|
df_station.station = [data['station'] for data in dataset]
|
|
df_station.点位名称 = remain_station['点位名称'][:len(dataset)].values
|
|
df_adapter = pd.concat([data['adapter'].assign(station=data['station']) for data in dataset], ignore_index=True)
|
|
df_meter = pd.concat([data['meter'].assign(station=data['station']) for data in dataset], ignore_index=True)
|
|
column_adapter = ['time', 'station', *df_adapter.columns[1:-1]]
|
|
column_meter = ['time', 'station', *df_meter.columns[1:-1]]
|
|
if output_file.exists():
|
|
""" 连接文件 """
|
|
df_station = pd.concat([finished_station['Station'], df_station], ignore_index=True)
|
|
df_adapter = pd.concat([finished_station['Adatper'], df_adapter], ignore_index=True)
|
|
df_meter = pd.concat([finished_station['Meter'], df_meter], ignore_index=True)
|
|
with pd.ExcelWriter(output_file) as writer:
|
|
df_station.to_excel(writer, sheet_name='Station', index=False)
|
|
df_adapter.to_excel(writer, sheet_name='Adatper', index=False, columns=column_adapter)
|
|
df_meter.to_excel(writer, sheet_name='Meter', index=False, columns=column_meter)
|
|
|
|
print(f"数据已成功保存到 {output_file}")
|
|
|
|
|
|
def save_station_by_file2(data_lamina: Lamina_Data, file_path):
|
|
""" 依据文件爬取所需站点数据 """
|
|
file_input = Path(file_path)
|
|
file_output = file_input.parent / (file_input.stem + '_output.xlsx')
|
|
df_input = pd.read_excel(file_input)
|
|
if file_output.exists():
|
|
finished_station = pd.read_excel(file_output, sheet_name=None)
|
|
finished_station["Station"]['station'] = finished_station["Station"]['station'].astype('str')
|
|
finished_station["Adatper"]['station'] = finished_station["Adatper"]['station'].astype('str')
|
|
finished_station["Meter"]['station'] = finished_station["Meter"]['station'].astype('str')
|
|
merged_df = pd.merge(df_input['点位名称'], finished_station['Station']['点位名称'], how='left', indicator=True)
|
|
remain_station = merged_df[merged_df['_merge'] == 'left_only'].drop(columns='_merge')
|
|
else:
|
|
remain_station = df_input
|
|
|
|
dataset = []
|
|
df_input = df_input.set_index('点位名称')
|
|
for name in remain_station['点位名称']:
|
|
logging.info(f"Station: {name}")
|
|
time_start_timestamp = df_input['开始时间'][name].tz_localize('Asia/Shanghai').timestamp()
|
|
time_end_timestamp = df_input['结束时间'][name].tz_localize('Asia/Shanghai').timestamp()
|
|
data = data_lamina.spider_station(name, time_start_timestamp, time_end_timestamp)
|
|
if data['result']:
|
|
dataset.append(data)
|
|
analysis_info1(data)
|
|
plt.waitforbuttonpress()
|
|
elif data['token']:
|
|
""" Token 失效 """
|
|
data_lamina.api_origin['header']['authorization'] = data['token']
|
|
|
|
logging.info(f"All Station Done.")
|
|
|
|
# 使用 ExcelWriter 将多个 DataFrame 保存到不同的工作表中
|
|
df_station = pd.DataFrame([], columns=['station', '点位名称'])
|
|
df_station.station = [data['station'] for data in dataset]
|
|
df_station.点位名称 = remain_station['点位名称'][:len(dataset)].values
|
|
df_adapter = pd.concat([data['adapter'].assign(station=data['station']) for data in dataset], ignore_index=True)
|
|
df_meter = pd.concat([data['meter'].assign(station=data['station']) for data in dataset], ignore_index=True)
|
|
column_adapter = ['time', 'station', *df_adapter.columns[1:-1]]
|
|
column_meter = ['time', 'station', *df_meter.columns[1:-1]]
|
|
if file_output.exists():
|
|
""" 连接文件 """
|
|
df_station = pd.concat([finished_station['Station'], df_station], ignore_index=True)
|
|
df_adapter = pd.concat([finished_station['Adatper'], df_adapter], ignore_index=True)
|
|
df_meter = pd.concat([finished_station['Meter'], df_meter], ignore_index=True)
|
|
with pd.ExcelWriter(file_output) as writer:
|
|
df_station.to_excel(writer, sheet_name='Station', index=False)
|
|
df_adapter.to_excel(writer, sheet_name='Adatper', index=False, columns=column_adapter)
|
|
df_meter.to_excel(writer, sheet_name='Meter', index=False, columns=column_meter)
|
|
|
|
logging.info(f"数据已成功保存到 {file_output}")
|
|
return result
|
|
|
|
def analysis_info(df_station: pd.DataFrame):
|
|
""" 站点Log数据分析 """
|
|
map_mid = {}
|
|
for k, v in SemaMap_adapter.items():
|
|
map_mid[v[0]] = v[3]
|
|
for k, v in SemaMap_meter.items():
|
|
map_mid[v[0]] = v[3]
|
|
map_dev = {
|
|
'TTE0101': 'Combiner',
|
|
'TTE0102': 'Adapter',
|
|
'TTE0103': 'Meter',
|
|
}
|
|
data = df_station.assign(
|
|
timestamp = lambda df: pd.to_datetime(df['time'], unit='s', utc=True).apply(lambda x: x.tz_convert('Asia/Shanghai')),
|
|
type = lambda df: df['dev'].apply(lambda x: map_dev[x[:7]]),
|
|
date = lambda df: df['timestamp'].apply(lambda x: x.date()),
|
|
name = lambda df: df['mid'].map(map_mid),
|
|
value = lambda df: pd.to_numeric(df['value'])
|
|
)
|
|
data_daliy = data.loc[(data['dev'] == 'TTE0102DX2406272727') & (data['date'] == np.datetime64('2024-12-25')) & (data['type'] == 'Adapter')]
|
|
fig, axes = plt.subplots(3, 2)
|
|
axes = axes.flatten()
|
|
i = 0
|
|
for name, df_plot in data_daliy.set_index('timestamp').sort_index()[['name', 'value']].groupby('name'):
|
|
df_plot.plot(ax=axes[i], title=name)
|
|
i += 1
|
|
plt.show()
|
|
|
|
def analysis_info1(data_station: dict):
|
|
""" 站点spider返回数据分析 """
|
|
define_dev = {
|
|
'TTE0101': ('combiner', (3,1)),
|
|
'TTE0102': ('adapter', (3,2)),
|
|
'TTE0103': ('meter', (3,2)),
|
|
}
|
|
# 创建双色颜色过渡
|
|
color_map = mcolors.LinearSegmentedColormap.from_list("mycmap", ["blue", "red"])
|
|
alpha = 0.5
|
|
|
|
for dev_id in data_station['information']['dev'].unique():
|
|
data_dev = data_station['information'].loc[data_station['information']['dev'] == dev_id]
|
|
history_dev = data_station[define_dev[dev_id[:7]][0]].loc[
|
|
lambda df: df['device'] == dev_id
|
|
].assign(
|
|
date = lambda df: df['time'].apply(lambda x: x.date()),
|
|
id_group = lambda df: df['date'].apply(lambda x: x.toordinal()).diff().fillna(0).cumsum(),
|
|
)
|
|
logging.debug(f"Device: {dev_id}")
|
|
logging.debug(history_dev.head())
|
|
fig, axs = plt.subplots(*define_dev[dev_id[:7]][1])
|
|
axs = axs.flatten()
|
|
for date, group in history_dev.groupby('date'):
|
|
# 计算当天的起始时间
|
|
start_time = pd.Timestamp(date)
|
|
# 调整时间索引,使其从当天的起始时间开始
|
|
adjusted_time = group['time'] - start_time
|
|
|
|
# 计算颜色和不透明度
|
|
color = color_map(group['id_group'].min() / history_dev['id_group'].max())
|
|
for index, key in enumerate(history_dev.columns[2:-2]):
|
|
if index >= len(axs):
|
|
break
|
|
group.set_index(adjusted_time)[key].plot(ax=axs[index], label=str(date), color=color, alpha=alpha)
|
|
axs[index-2].set_title(f"{key.replace('_', ' ').title()}")
|
|
|
|
fig.suptitle(f"{data_station['station']}_{define_dev[dev_id[:7]][0].title()} Device: {dev_id}", fontsize=16)
|
|
plt.show()
|
|
plt.savefig(Path(f"result\Analysis\{data_station['station']}_{dev_id}.png"))
|
|
|
|
if __name__=='__main__':
|
|
""" 主体调用流程 """
|
|
# plt中文显示
|
|
plt.rcParams['font.sans-serif'] = ['SimHei']
|
|
# 坐标轴负数显示
|
|
plt.rcParams['axes.unicode_minus'] = False
|
|
|
|
if not hasattr(__builtins__,"__IPYTHON__") and 0:
|
|
import pickle
|
|
path_data1 = Path(r"result\Analysis\station_data2.pkl")
|
|
with open(path_data1, 'rb') as f:
|
|
loaded_data = pickle.load(f)
|
|
analysis_info1(loaded_data)
|
|
|
|
if hasattr(__builtins__,"__IPYTHON__"):
|
|
path_db = '../result/chinatowercom.db'
|
|
else:
|
|
path_db = 'result/chinatowercom.db'
|
|
|
|
if not (file_db:= Path(path_db)).exists():
|
|
file_db.touch()
|
|
|
|
API_HEADER['Cookie'] = "HWWAFSESTIME=1737167522632; HWWAFSESID=6cb0288b7bc75e5a66; dc04ed2361044be8a9355f6efb378cf2=WyIzNTI0NjE3OTgzIl0"
|
|
API_HEADER['authorization'] = 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiIl0sInVzZXJfbmFtZSI6IndlYl9tYW5hZ2V8d2FuZ2xlaTQiLCJzY29wZSI6WyJhbGwiXSwiZXhwIjoxNzM3NDI3OTAwLCJ1c2VySWQiOjI0Mjg1LCJqdGkiOiIwYzliOTk2ZC01OTU3LTQ5MjMtOTAzNC03YzlkMDQ4YWU3MzQiLCJjbGllbnRfaWQiOiJ3ZWJfbWFuYWdlIn0.JcPqvOoVv06gi7l_ownVl1ubwDn1dYgkqB082gjrQlHqveXpyqeiF6MUUjlhcUFgNArttog9ZnI82jqmiBfSOkc-gdjvM-AHUvXc3DRN4dvlY9eXdeDeMTLFQh5rfmlYHEd9fgI7eRBvLAiUbDpiNuxyU2N2VV72vxGdvp5f1GeUPEmLj6lwBch5L2sWSYi2p9PwCBYX0sm5EwnL--nui1Iv2PHNos02y4h_m2C-x96L3chXV-h_vKoRWrztiEX6O40zaNwzlIcm_rSmX6GEOF4darGB9hU7aFzKBfM4wTcj-ZKae7dx3ttkuc1HD_eFL8xpDr0pvWycFzrgSlLtkw'
|
|
data_lamina = Lamina_Data('sqlite:///' + path_db)
|
|
|
|
# 依据站点内设备爬取整个站点的实时与历史数据
|
|
# today = datetime.datetime.today()
|
|
# yesterday = today - datetime.timedelta(days=30)
|
|
# today_midnight = today.replace(hour=0, minute=0, second=0, microsecond=0)
|
|
# yesterday_midnight = yesterday.replace(hour=0, minute=0, second=0, microsecond=0)
|
|
# today_midnight_timestamp = time.mktime(today_midnight.timetuple())
|
|
# yesterday_midnight_timestamp = time.mktime(yesterday_midnight.timetuple())
|
|
# data = data_lamina.spider_station("乐亭后庞河村", yesterday_midnight_timestamp, today_midnight_timestamp)
|
|
|
|
# 读取站点历史数据
|
|
# save_station_by_file1(data_lamina)
|
|
result = save_station_by_file2(data_lamina, "result\station_Q0120.xlsx")
|
|
|
|
# 网站令牌更新
|
|
body = {
|
|
"appId": "pjxNHUmFrMuj82pJenTmEc3Uvzj1cAO/qXs3zKMTjsG7Quk59cyjBCQM4miupyXv1At4e3deTn1cF9c4/WveDaeJCwEB+Dslom9yufrVPziOmRrQj1iAo8QVWSUnT1k70soDst+JN6japzOt7vjibru0uS/xezHrhuLSyNxkqzs=",
|
|
"refreshToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiIl0sInVzZXJfbmFtZSI6IndlYl9tYW5hZ2V8d2FuZ2xlaTQiLCJzY29wZSI6WyJhbGwiXSwiYXRpIjoiNGU4NTY1NTAtZDE5Ni00YjY4LWI1OGYtMzBkOTY0YjIyOGNkIiwiZXhwIjoxNzM1OTcyNTA0LCJ1c2VySWQiOjI0Mjg1LCJqdGkiOiIwMTY1NDg1MC1mZjIwLTRkMzQtYTQ4ZC03NmRiZTk3MmQ3YWQiLCJjbGllbnRfaWQiOiJ3ZWJfbWFuYWdlIn0.PFT8JlTvWay1GUI5TC2Ht25rZWkAnQT3nxs-dOcAVIN9To06rG8EDspZ5eFxmNuEraurNxHCOLPfQZ-bCzJ8ywlA747PyJxyMPBhRhgXSDHYHX7ZqHEUdQdQo_Wkf75I8ko8_szchyhItjtgDUCzud9TlxKeuBQuerpYV8tkUVWobp4ulnnHEg0kqZFDeXrI-84Lyy-kodCDI-r3KuMBC5Rvbce0hqMcs2l-2U7M-V7LUT2VhBEvQd8l_Agx8hqWcK-d-dMVhlNjcvcb0AKmcX845D0bD5tKVKim_5JX4Er9-NANzSmgO0SRnsFVuxHhXiNqSkTB7pIdyi9r-ui23Q",
|
|
"accessToken": API_HEADER['authorization']
|
|
}
|
|
body1 = {
|
|
"appId": "ePUxoRrHClb7+Wxk7NAJpiJhoVAcJbZ5NPJEak8ZTFrETrfA0JAIjbqiDuaow1Jdyg1FLjUAwlBXrLoKh514oTTZSp1U91ewVj+8ZvNi2vtbQkU03WdyxyHXiyTNjC88O1JRm13hRnIm1vRMoxsudm8CPCpUIsU9yYABZ+/w3A4=",
|
|
"refreshToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiIl0sInVzZXJfbmFtZSI6IndlYl9tYW5hZ2V8d2FuZ2xlaTQiLCJzY29wZSI6WyJhbGwiXSwiYXRpIjoiNGU4NTY1NTAtZDE5Ni00YjY4LWI1OGYtMzBkOTY0YjIyOGNkIiwiZXhwIjoxNzM1OTcyNTA0LCJ1c2VySWQiOjI0Mjg1LCJqdGkiOiIwMTY1NDg1MC1mZjIwLTRkMzQtYTQ4ZC03NmRiZTk3MmQ3YWQiLCJjbGllbnRfaWQiOiJ3ZWJfbWFuYWdlIn0.PFT8JlTvWay1GUI5TC2Ht25rZWkAnQT3nxs-dOcAVIN9To06rG8EDspZ5eFxmNuEraurNxHCOLPfQZ-bCzJ8ywlA747PyJxyMPBhRhgXSDHYHX7ZqHEUdQdQo_Wkf75I8ko8_szchyhItjtgDUCzud9TlxKeuBQuerpYV8tkUVWobp4ulnnHEg0kqZFDeXrI-84Lyy-kodCDI-r3KuMBC5Rvbce0hqMcs2l-2U7M-V7LUT2VhBEvQd8l_Agx8hqWcK-d-dMVhlNjcvcb0AKmcX845D0bD5tKVKim_5JX4Er9-NANzSmgO0SRnsFVuxHhXiNqSkTB7pIdyi9r-ui23Q",
|
|
"accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiIl0sInVzZXJfbmFtZSI6IndlYl9tYW5hZ2V8d2FuZ2xlaTQiLCJzY29wZSI6WyJhbGwiXSwiZXhwIjoxNzM1OTA5NTE1LCJ1c2VySWQiOjI0Mjg1LCJqdGkiOiI0ZTg1NjU1MC1kMTk2LTRiNjgtYjU4Zi0zMGQ5NjRiMjI4Y2QiLCJjbGllbnRfaWQiOiJ3ZWJfbWFuYWdlIn0.KxGBpvuPIP3CHfVT41wxE_v9vlHNC9GL6sfaIta8cI2qlMpTCVg9dg-4DgPlXuMrtI0YzrSbAywCQmFLGcBgh3HD_UuIAH-k3Y8__osZEgc4bUcJ58W-uukuEu3MEwbV6ZcxTq7dxf3iqu9aXGrawYY_iL-jIRH1v8Zcr4qUPA9Mlzl8LvZdzZ05XgntbxE8IQRmt1M5rWdWLV4tvbUEYR5eDGs3az0w-MFXQ8qNHo8KLJc68WvbilmOMWkhK2k_xQQTdNx_jPktjYfClZa6l9-6rYAb5MMqwt77fY0_JE87u3w5YbU_GRyBI2mjnJe1qKdMjUEpQwWqt3DLJWLe7Q"
|
|
}
|
|
req = requests.post(API_Map['refreshToken'][0], json=body1, headers=API_HEADER)
|
|
|
|
# data = sim_data_apt(('2024-10-1 00:00:00', '2024-10-1 12:00:00'))
|
|
# chart_apt(data)
|
|
|
|
if not hasattr(__builtins__,"__IPYTHON__"):
|
|
table_apt = data_lamina.graphs_adapter('TTE0102DX2406180988', '2024-11-23 00:00:00', '2024-11-26 00:00:00')
|
|
while True:
|
|
plt.waitforbuttonpress()
|