diff --git a/source/data_analy.py b/source/data_analy.py deleted file mode 100644 index f2512f1..0000000 --- a/source/data_analy.py +++ /dev/null @@ -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}') \ No newline at end of file diff --git a/source/data_analysis.py b/source/data_analysis.py new file mode 100644 index 0000000..e5fc380 --- /dev/null +++ b/source/data_analysis.py @@ -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}') \ No newline at end of file diff --git a/source/device/DeviceMQTT.py b/source/device/DeviceMQTT.py index 2f9f074..45dd2fd 100644 --- a/source/device/DeviceMQTT.py +++ b/source/device/DeviceMQTT.py @@ -20,7 +20,7 @@ class DeviceMQTT: def __init__(self, broker, port, account=None, device_id=None, callbacks=None, **kwargs): """ 设备初始化 """ 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._subscribe(device_id) @@ -37,7 +37,7 @@ class DeviceMQTT: self._frame_maker = lambda self: '' self._frame_parser = lambda self, frame: '' - self._message = {} + self._message = [] self.output = { 'result': False, 'code_func': 0x00, @@ -80,14 +80,14 @@ class DeviceMQTT: reconnect_count += 1 print("Reconnect failed after %s attempts. Exiting...", reconnect_count) def on_message(client, userdata, msg): - self._message = msg + self._message.append(msg) print(f"Received message from `{msg.topic}` topic") client = mqtt_client.Client(client_id=self.client_id, callback_api_version=mqtt_client.CallbackAPIVersion.VERSION2) if account is not None: 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_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.connect(broker, port) return client @@ -99,11 +99,15 @@ class DeviceMQTT: 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_read = f"ctiot/upload/7/0101/{device_id}/#" + self.client.subscribe(topic_read) + self.device_id = device_id self.topic = (topic_send, topic_read) - self.client.subscribe(topic_read) def __send(self, msg: bytearray, topic=None) -> bool: """ 发布消息 """ @@ -113,7 +117,7 @@ class DeviceMQTT: "deviceId": self.device_id, "isSubDevice": 0, "requestType": 0, - "messageId": "463g3ga53j4k25o9w", + "messageId": f"debug-{self.client_id[18:]}", "timestamp": int(time.time()), "functionId": "168493059", "inputs": [ @@ -127,23 +131,24 @@ class DeviceMQTT: return result[0] == 1 def __read(self, timeout=None) -> bytes: - self._message = None + self._message.clear() timeout = timeout if timeout is not None else 1 self.client.loop_start() time.sleep(timeout) self.client.loop_stop() - if self._message: - """ 报文接收成功 """ - message_data = json.loads(self._message.payload) - 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) - return bytearray(frame_recv) - else: - return b'' + for message in self._message: + message_data = json.loads(message.payload) + if message_data['messageId'] == f"debug-{self.client_id[18:]}": + """ 报文接收成功 """ + frame = " ".join((message_data['modbus_msg'][2*i:2*(i+1)] for i in range(len(message_data['modbus_msg'])//2))) + frame = ByteConv.trans_str_to_list(frame) + return bytearray(frame) + return b'' def __read_frame(self) ->bool: """ 读取报文并解析帧 """ + frame_recv = b'' try: frame_recv = self.__read(timeout=self.time_out) self.output = self._frame_parser(frame_recv) diff --git a/source/device/LaminaAdapter.py b/source/device/LaminaAdapter.py index 4fad488..f722aba 100644 --- a/source/device/LaminaAdapter.py +++ b/source/device/LaminaAdapter.py @@ -17,6 +17,9 @@ ParamMap_LaminaAdapter = { # 5 - addr # 6 - float 0x00: ["硬件版本识别电压", 2, 1000], + 0x01: ["输出电容电电压", 2, 10], + 0x02: ["参考电压", 2, 10], + 0x0E: ["故障字1", 1], 0x0F: ["故障字2", 1], 0x10: ["MPPT工作状态", 1], @@ -34,8 +37,7 @@ ParamMap_LaminaAdapter = { 0x1D: ["开关机状态", 1], 0x1E: ["电池电压", 2, 10], 0x1F: ["并机功率限值", 3, 1000], - 0x21: ["输出电容电电压", 2, 10], - 0x22: ["参考电压", 2, 10], + 0x21: ["输出限功率电压阈值", 2, 10], 0x50: ["启停控制命令" , 2], 0x51: ["故障清除命令" , 2],