主站数据分析脚本;
This commit is contained in:
@@ -1,39 +0,0 @@
|
|||||||
import pandas as pd
|
|
||||||
from pathlib import Path
|
|
||||||
from bs4 import BeautifulSoup
|
|
||||||
|
|
||||||
# 1. 读取本地HTML文件
|
|
||||||
file_path = Path(r'D:\WorkingProject\LightStackAdapter\Log\设备测试数据记录-铁塔主站\南和县牧村\Untitled-1.html')
|
|
||||||
html_content = file_path.read_text()
|
|
||||||
|
|
||||||
|
|
||||||
# 2. 解析HTML文件
|
|
||||||
soup = BeautifulSoup(html_content, 'html.parser')
|
|
||||||
|
|
||||||
# 3. 找到表格元素
|
|
||||||
table = soup.find_all('table') # 假设页面中只有一个表格,如果有多个表格,可能需要进一步筛选
|
|
||||||
|
|
||||||
# 4. 提取表格数据
|
|
||||||
data = []
|
|
||||||
headers = []
|
|
||||||
|
|
||||||
# 提取表头
|
|
||||||
header_row = table.find('thead').find('tr')
|
|
||||||
for header in header_row.find_all('th'):
|
|
||||||
headers.append(header.text.strip())
|
|
||||||
|
|
||||||
# 提取数据行
|
|
||||||
for row in table.find('tbody').find_all('tr'):
|
|
||||||
row_data = []
|
|
||||||
for cell in row.find_all(['td', 'th']):
|
|
||||||
row_data.append(cell.text.strip())
|
|
||||||
data.append(row_data)
|
|
||||||
|
|
||||||
# 5. 将数据保存为DataFrame
|
|
||||||
df = pd.DataFrame(data, columns=headers)
|
|
||||||
|
|
||||||
# 6. 将DataFrame保存为CSV文件
|
|
||||||
output_file = 'extracted_table.csv'
|
|
||||||
df.to_csv(output_file, index=False, encoding='utf-8')
|
|
||||||
|
|
||||||
print(f'表格数据已成功提取并保存到 {output_file}')
|
|
||||||
73
source/data_analysis.py
Normal file
73
source/data_analysis.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import requests
|
||||||
|
import pandas as pd
|
||||||
|
from pathlib import Path
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
API_URL = "https://energy-iot.chinatowercom.cn/api/device/device/historyPerformance"
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Accept-Encoding": "gzip, deflate, br, zstd",
|
||||||
|
"Connection": "keep-alive",
|
||||||
|
"Content-Length": "211",
|
||||||
|
"Cookie": "HWWAFSESTIME=1732173820506; HWWAFSESID=1739685743c73769ff; dc04ed2361044be8a9355f6efb378cf2=WyIzNTI0NjE3OTgzIl0",
|
||||||
|
"Host": "energy-iot.chinatowercom.cn",
|
||||||
|
"Origin": "https://energy-iot.chinatowercom.cn",
|
||||||
|
"Sec-Fetch-Dest": "empty",
|
||||||
|
"Sec-Fetch-Mode": "cors",
|
||||||
|
"Sec-Fetch-Site": "same-origin",
|
||||||
|
"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",
|
||||||
|
"accept": "application/json, text/plain, */*",
|
||||||
|
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
||||||
|
"authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiIl0sInVzZXJfbmFtZSI6IndlYl9tYW5hZ2V8d2FuZ2xlaTQiLCJzY29wZSI6WyJhbGwiXSwiZXhwIjoxNzMyMjc4NDA5LCJ1c2VySWQiOjI0Mjg1LCJqdGkiOiJkODE0YTZhYy05YmJmLTQ0ZjQtYWRhYi0wMzAzNjUzNmNhNWIiLCJjbGllbnRfaWQiOiJ3ZWJfbWFuYWdlIn0.VhJaDKwzjekwOCsw_jOF_jvg7sX45okFcxkLyWtbfFVGWVWANhKNhVqj5Dn0Qb3wUXH3e-w74sDN1RI9QADngMOGP_H7aTwI_nukj6VmjpFA7kEtOBwa6ouvPZQMa1qa3UWl21Ac6GoLu14T4TIf4kQAMTdaYAMFrwDAXAkqvIDmKKjZbnDFUjUIcj-J_Y-LfHCEBjtcz7Rp_wMO-PMA5wII6kbcNoSFiYb0djcFQyeBcIUSUTRPixPcTYBkS-IhNrsOePIWlpNYMHbPxZdrZkV4M65BmBn4A9MUjWYHm7iIut8WVMdCXR4Sxp9m0mJHXR_IPWES4O7aBcuMkOmjyw",
|
||||||
|
"content-type": "application/json;charset=UTF-8",
|
||||||
|
"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",
|
||||||
|
}
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"startTimestamp": 1732032000000,
|
||||||
|
"endTimestamp": 1732291199000,
|
||||||
|
"deviceCode": "TTE0102DX2406240497",
|
||||||
|
"mid": "0305120001",
|
||||||
|
"businessType": "7",
|
||||||
|
"pageNum": 2,
|
||||||
|
"pageSize": 5,
|
||||||
|
"total": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# 1. 读取本地HTML文件
|
||||||
|
file_path = Path(r'D:\WorkingProject\LightStackAdapter\Log\设备测试数据记录-铁塔主站\南和县牧村\Untitled-1.html')
|
||||||
|
html_content = file_path.read_text()
|
||||||
|
|
||||||
|
|
||||||
|
# 2. 解析HTML文件
|
||||||
|
soup = BeautifulSoup(html_content, 'html.parser')
|
||||||
|
|
||||||
|
# 3. 找到表格元素
|
||||||
|
table = soup.find_all('table') # 假设页面中只有一个表格,如果有多个表格,可能需要进一步筛选
|
||||||
|
|
||||||
|
# 4. 提取表格数据
|
||||||
|
data = []
|
||||||
|
headers = []
|
||||||
|
|
||||||
|
# 提取表头
|
||||||
|
header_row = table.find('thead').find('tr')
|
||||||
|
for header in header_row.find_all('th'):
|
||||||
|
headers.append(header.text.strip())
|
||||||
|
|
||||||
|
# 提取数据行
|
||||||
|
for row in table.find('tbody').find_all('tr'):
|
||||||
|
row_data = []
|
||||||
|
for cell in row.find_all(['td', 'th']):
|
||||||
|
row_data.append(cell.text.strip())
|
||||||
|
data.append(row_data)
|
||||||
|
|
||||||
|
# 5. 将数据保存为DataFrame
|
||||||
|
df = pd.DataFrame(data, columns=headers)
|
||||||
|
|
||||||
|
# 6. 将DataFrame保存为CSV文件
|
||||||
|
output_file = 'extracted_table.csv'
|
||||||
|
df.to_csv(output_file, index=False, encoding='utf-8')
|
||||||
|
|
||||||
|
print(f'表格数据已成功提取并保存到 {output_file}')
|
||||||
@@ -20,7 +20,7 @@ class DeviceMQTT:
|
|||||||
def __init__(self, broker, port, account=None, device_id=None, callbacks=None, **kwargs):
|
def __init__(self, broker, port, account=None, device_id=None, callbacks=None, **kwargs):
|
||||||
""" 设备初始化 """
|
""" 设备初始化 """
|
||||||
self.topic = None
|
self.topic = None
|
||||||
self.client_id = f'python-DeviceMQTT-{random.randint(0, 1000)}'
|
self.client_id = f'python-DeviceMQTT-{random.randint(0, 10000):04d}'
|
||||||
self.client = self.open_connection(broker, port, account, **kwargs)
|
self.client = self.open_connection(broker, port, account, **kwargs)
|
||||||
self._subscribe(device_id)
|
self._subscribe(device_id)
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ class DeviceMQTT:
|
|||||||
self._frame_maker = lambda self: ''
|
self._frame_maker = lambda self: ''
|
||||||
self._frame_parser = lambda self, frame: ''
|
self._frame_parser = lambda self, frame: ''
|
||||||
|
|
||||||
self._message = {}
|
self._message = []
|
||||||
self.output = {
|
self.output = {
|
||||||
'result': False,
|
'result': False,
|
||||||
'code_func': 0x00,
|
'code_func': 0x00,
|
||||||
@@ -80,14 +80,14 @@ class DeviceMQTT:
|
|||||||
reconnect_count += 1
|
reconnect_count += 1
|
||||||
print("Reconnect failed after %s attempts. Exiting...", reconnect_count)
|
print("Reconnect failed after %s attempts. Exiting...", reconnect_count)
|
||||||
def on_message(client, userdata, msg):
|
def on_message(client, userdata, msg):
|
||||||
self._message = msg
|
self._message.append(msg)
|
||||||
print(f"Received message from `{msg.topic}` topic")
|
print(f"Received message from `{msg.topic}` topic")
|
||||||
|
|
||||||
client = mqtt_client.Client(client_id=self.client_id, callback_api_version=mqtt_client.CallbackAPIVersion.VERSION2)
|
client = mqtt_client.Client(client_id=self.client_id, callback_api_version=mqtt_client.CallbackAPIVersion.VERSION2)
|
||||||
if account is not None:
|
if account is not None:
|
||||||
client.username_pw_set(account[0], account[1])
|
client.username_pw_set(account[0], account[1])
|
||||||
client.on_connect = on_connect if 'func_on_connect' not in kwargs.keys() else kwargs['func_on_connect']
|
client.on_connect = on_connect if 'func_on_connect' not in kwargs.keys() else kwargs['func_on_connect']
|
||||||
client.on_disconnect = on_disconnect if 'func_on_disconnect' not in kwargs.keys() else kwargs['func_on_disconnect']
|
# client.on_disconnect = on_disconnect if 'func_on_disconnect' not in kwargs.keys() else kwargs['func_on_disconnect']
|
||||||
client.on_message = on_message if 'func_on_message' not in kwargs.keys() else kwargs['func_on_message']
|
client.on_message = on_message if 'func_on_message' not in kwargs.keys() else kwargs['func_on_message']
|
||||||
client.connect(broker, port)
|
client.connect(broker, port)
|
||||||
return client
|
return client
|
||||||
@@ -99,11 +99,15 @@ class DeviceMQTT:
|
|||||||
|
|
||||||
def _subscribe(self, device_id):
|
def _subscribe(self, device_id):
|
||||||
""" 订阅主题 """
|
""" 订阅主题 """
|
||||||
|
if self.topic is not None:
|
||||||
|
self.client.unsubscribe(self.topic[1])
|
||||||
|
|
||||||
topic_send = f"ctiot/download/7/0101/{device_id}/function/invoke"
|
topic_send = f"ctiot/download/7/0101/{device_id}/function/invoke"
|
||||||
topic_read = f"ctiot/upload/7/0101/{device_id}/#"
|
topic_read = f"ctiot/upload/7/0101/{device_id}/#"
|
||||||
|
self.client.subscribe(topic_read)
|
||||||
|
|
||||||
self.device_id = device_id
|
self.device_id = device_id
|
||||||
self.topic = (topic_send, topic_read)
|
self.topic = (topic_send, topic_read)
|
||||||
self.client.subscribe(topic_read)
|
|
||||||
|
|
||||||
def __send(self, msg: bytearray, topic=None) -> bool:
|
def __send(self, msg: bytearray, topic=None) -> bool:
|
||||||
""" 发布消息 """
|
""" 发布消息 """
|
||||||
@@ -113,7 +117,7 @@ class DeviceMQTT:
|
|||||||
"deviceId": self.device_id,
|
"deviceId": self.device_id,
|
||||||
"isSubDevice": 0,
|
"isSubDevice": 0,
|
||||||
"requestType": 0,
|
"requestType": 0,
|
||||||
"messageId": "463g3ga53j4k25o9w",
|
"messageId": f"debug-{self.client_id[18:]}",
|
||||||
"timestamp": int(time.time()),
|
"timestamp": int(time.time()),
|
||||||
"functionId": "168493059",
|
"functionId": "168493059",
|
||||||
"inputs": [
|
"inputs": [
|
||||||
@@ -127,23 +131,24 @@ class DeviceMQTT:
|
|||||||
return result[0] == 1
|
return result[0] == 1
|
||||||
|
|
||||||
def __read(self, timeout=None) -> bytes:
|
def __read(self, timeout=None) -> bytes:
|
||||||
self._message = None
|
self._message.clear()
|
||||||
timeout = timeout if timeout is not None else 1
|
timeout = timeout if timeout is not None else 1
|
||||||
self.client.loop_start()
|
self.client.loop_start()
|
||||||
time.sleep(timeout)
|
time.sleep(timeout)
|
||||||
self.client.loop_stop()
|
self.client.loop_stop()
|
||||||
|
|
||||||
if self._message:
|
for message in self._message:
|
||||||
""" 报文接收成功 """
|
message_data = json.loads(message.payload)
|
||||||
message_data = json.loads(self._message.payload)
|
if message_data['messageId'] == f"debug-{self.client_id[18:]}":
|
||||||
frame_recv = " ".join((message_data['modbus_msg'][2*i:2*(i+1)] for i in range(len(message_data['modbus_msg'])//2)))
|
""" 报文接收成功 """
|
||||||
frame_recv = ByteConv.trans_str_to_list(frame_recv)
|
frame = " ".join((message_data['modbus_msg'][2*i:2*(i+1)] for i in range(len(message_data['modbus_msg'])//2)))
|
||||||
return bytearray(frame_recv)
|
frame = ByteConv.trans_str_to_list(frame)
|
||||||
else:
|
return bytearray(frame)
|
||||||
return b''
|
return b''
|
||||||
|
|
||||||
def __read_frame(self) ->bool:
|
def __read_frame(self) ->bool:
|
||||||
""" 读取报文并解析帧 """
|
""" 读取报文并解析帧 """
|
||||||
|
frame_recv = b''
|
||||||
try:
|
try:
|
||||||
frame_recv = self.__read(timeout=self.time_out)
|
frame_recv = self.__read(timeout=self.time_out)
|
||||||
self.output = self._frame_parser(frame_recv)
|
self.output = self._frame_parser(frame_recv)
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ ParamMap_LaminaAdapter = {
|
|||||||
# 5 - addr
|
# 5 - addr
|
||||||
# 6 - float
|
# 6 - float
|
||||||
0x00: ["硬件版本识别电压", 2, 1000],
|
0x00: ["硬件版本识别电压", 2, 1000],
|
||||||
|
0x01: ["输出电容电电压", 2, 10],
|
||||||
|
0x02: ["参考电压", 2, 10],
|
||||||
|
|
||||||
0x0E: ["故障字1", 1],
|
0x0E: ["故障字1", 1],
|
||||||
0x0F: ["故障字2", 1],
|
0x0F: ["故障字2", 1],
|
||||||
0x10: ["MPPT工作状态", 1],
|
0x10: ["MPPT工作状态", 1],
|
||||||
@@ -34,8 +37,7 @@ ParamMap_LaminaAdapter = {
|
|||||||
0x1D: ["开关机状态", 1],
|
0x1D: ["开关机状态", 1],
|
||||||
0x1E: ["电池电压", 2, 10],
|
0x1E: ["电池电压", 2, 10],
|
||||||
0x1F: ["并机功率限值", 3, 1000],
|
0x1F: ["并机功率限值", 3, 1000],
|
||||||
0x21: ["输出电容电电压", 2, 10],
|
0x21: ["输出限功率电压阈值", 2, 10],
|
||||||
0x22: ["参考电压", 2, 10],
|
|
||||||
|
|
||||||
0x50: ["启停控制命令" , 2],
|
0x50: ["启停控制命令" , 2],
|
||||||
0x51: ["故障清除命令" , 2],
|
0x51: ["故障清除命令" , 2],
|
||||||
|
|||||||
Reference in New Issue
Block a user