充电桩报文采集
This commit is contained in:
parent
d5b2ccd0b2
commit
0541f45580
303
charging_pile_proxy/order.py
Normal file
303
charging_pile_proxy/order.py
Normal file
@ -0,0 +1,303 @@
|
||||
import socket
|
||||
import struct
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime
|
||||
import binascii
|
||||
import sys
|
||||
import json
|
||||
import paho.mqtt.client as mqtt
|
||||
|
||||
# 设置系统默认编码为UTF-8
|
||||
if sys.version_info[0] == 3:
|
||||
sys.stdout.reconfigure(encoding='utf-8')
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(
|
||||
filename='test.log',
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
encoding='utf-8'
|
||||
)
|
||||
|
||||
|
||||
class MQTTClient:
|
||||
def __init__(self):
|
||||
self.client = mqtt.Client(client_id="GoClientExample", protocol=mqtt.MQTTv311,
|
||||
callback_api_version=mqtt.CallbackAPIVersion.VERSION1)
|
||||
self.client.username_pw_set("emqx_test", "emqx_test")
|
||||
self.client.on_connect = self.on_connect
|
||||
self.client.on_disconnect = self.on_disconnect
|
||||
self.connected = False
|
||||
|
||||
def on_connect(self, client, userdata, flags, rc):
|
||||
if rc == 0:
|
||||
print("Connected to MQTT broker")
|
||||
self.connected = True
|
||||
else:
|
||||
print(f"Failed to connect to MQTT broker with code: {rc}")
|
||||
|
||||
def on_disconnect(self, client, userdata, rc):
|
||||
print("Disconnected from MQTT broker")
|
||||
self.connected = False
|
||||
|
||||
def connect(self):
|
||||
try:
|
||||
self.client.connect("123.6.102.119", 1883, 60)
|
||||
self.client.loop_start()
|
||||
except Exception as e:
|
||||
print(f"MQTT connection error: {str(e)}")
|
||||
|
||||
def publish_message(self, message):
|
||||
try:
|
||||
if self.connected:
|
||||
# 直接发布消息值,无论是列表还是字符串
|
||||
self.client.publish("hejin/charging/log", json.dumps(message), qos=1)
|
||||
else:
|
||||
print("MQTT client not connected")
|
||||
except Exception as e:
|
||||
print(f"MQTT publish error: {str(e)}")
|
||||
|
||||
def disconnect(self):
|
||||
self.client.loop_stop()
|
||||
self.client.disconnect()
|
||||
|
||||
|
||||
class ChargingPileProxyServer:
|
||||
def __init__(self, listen_host='0.0.0.0', listen_port=52461,
|
||||
forward_host='139.9.209.227', forward_port=52461):
|
||||
self.listen_host = listen_host
|
||||
self.listen_port = listen_port
|
||||
self.forward_host = forward_host
|
||||
self.forward_port = forward_port
|
||||
self.server_socket = None
|
||||
self.running = False
|
||||
self.clients = {} # 存储客户端连接
|
||||
self.remote_connections = {} # 存储与远程服务器的连接
|
||||
self.mqtt_client = MQTTClient() # 创建MQTT客户端
|
||||
self.pile_ids = {} # 存储客户端地址与桩号的映射
|
||||
|
||||
def extract_pile_id(self, data):
|
||||
"""从数据包中提取桩号"""
|
||||
try:
|
||||
|
||||
if len(data) > 10: # 确保数据包足够长
|
||||
# 桩号在第5-8个字节
|
||||
pile_id = ''.join([f"{b:02X}" for b in data[3:11]])
|
||||
return pile_id
|
||||
return None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def format_hex_data(self, data):
|
||||
"""格式化十六进制数据显示"""
|
||||
return ' '.join([f"{b:02X}" for b in data])
|
||||
|
||||
def start(self):
|
||||
"""启动代理服务器"""
|
||||
try:
|
||||
self.mqtt_client.connect()
|
||||
|
||||
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self.server_socket.bind((self.listen_host, self.listen_port))
|
||||
self.server_socket.listen(5)
|
||||
self.running = True
|
||||
|
||||
start_msg = f"代理服务器已启动,监听地址: {self.listen_host}:{self.listen_port}"
|
||||
logging.info(start_msg)
|
||||
print(start_msg)
|
||||
self.mqtt_client.publish_message(start_msg)
|
||||
|
||||
while self.running:
|
||||
client_socket, address = self.server_socket.accept()
|
||||
client_msg = f"收到新的客户端连接,地址: {address}"
|
||||
logging.info(client_msg)
|
||||
print(client_msg)
|
||||
self.mqtt_client.publish_message(client_msg)
|
||||
|
||||
client_thread = threading.Thread(target=self.handle_client,
|
||||
args=(client_socket, address))
|
||||
client_thread.daemon = True
|
||||
client_thread.start()
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"代理服务器错误: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
print(error_msg)
|
||||
self.mqtt_client.publish_message(error_msg)
|
||||
finally:
|
||||
if self.server_socket:
|
||||
self.server_socket.close()
|
||||
|
||||
def get_socket_info(self, socket_obj):
|
||||
"""获取socket的本地和远程地址信息"""
|
||||
try:
|
||||
local_address = socket_obj.getsockname()
|
||||
remote_address = socket_obj.getpeername()
|
||||
return local_address, remote_address
|
||||
except:
|
||||
return None, None
|
||||
|
||||
def create_remote_connection(self, client_address):
|
||||
"""创建与远程服务器的连接"""
|
||||
try:
|
||||
remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
remote_socket.connect((self.forward_host, self.forward_port))
|
||||
|
||||
connect_msg = f"已连接到远程服务器 {self.forward_host}:{self.forward_port}"
|
||||
logging.info(connect_msg)
|
||||
print(connect_msg)
|
||||
self.mqtt_client.publish_message(connect_msg)
|
||||
|
||||
self.remote_connections[client_address] = remote_socket
|
||||
return remote_socket
|
||||
except Exception as e:
|
||||
error_msg = f"连接远程服务器失败: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
print(error_msg)
|
||||
self.mqtt_client.publish_message(error_msg)
|
||||
return None
|
||||
|
||||
def forward_data(self, source_socket, destination_socket, source_address):
|
||||
"""转发数据"""
|
||||
try:
|
||||
while self.running:
|
||||
data = source_socket.recv(1024)
|
||||
if not data:
|
||||
break
|
||||
|
||||
#尝试提取桩号
|
||||
if source_socket not in self.remote_connections.values():
|
||||
pile_id = self.extract_pile_id(data)
|
||||
if pile_id: # 只在成功提取到桩号时更新
|
||||
self.pile_ids[source_address] = pile_id
|
||||
|
||||
# 获取源和目标socket的地址信息
|
||||
source_local, source_remote = self.get_socket_info(source_socket)
|
||||
dest_local, dest_remote = self.get_socket_info(destination_socket)
|
||||
|
||||
# 确定数据传输方向
|
||||
is_to_remote = destination_socket in self.remote_connections.values()
|
||||
direction = "发送到远程服务器" if is_to_remote else "发送到充电桩"
|
||||
mqtt_direction = "u" if is_to_remote else "d" # 新增:MQTT消息中的方向标识
|
||||
|
||||
# 获取实际的源IP和目标IP
|
||||
from_ip = source_remote[0] if source_remote else "unknown"
|
||||
to_ip = dest_remote[0] if dest_remote else "unknown"
|
||||
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
||||
|
||||
# 记录转发的数据
|
||||
hex_data = self.format_hex_data(data)
|
||||
# 构建消息数组
|
||||
msg_array = [
|
||||
from_ip, # 源IP地址
|
||||
to_ip, # 目标IP地址
|
||||
current_time, # 当前时间
|
||||
hex_data, # 十六进制数据
|
||||
self.pile_ids.get(source_address, "unknown"), # 桩号
|
||||
mqtt_direction # 传输方向
|
||||
]
|
||||
print(msg_array)
|
||||
|
||||
# 发送到MQTT
|
||||
self.mqtt_client.publish_message(msg_array)
|
||||
|
||||
# 为控制台和日志文件使用格式化的消息
|
||||
formatted_msg = f"[{current_time}] [{from_ip} -> {to_ip}] 转发数据 ({direction}): {hex_data}"
|
||||
logging.info(formatted_msg)
|
||||
print(formatted_msg)
|
||||
|
||||
destination_socket.send(data)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"转发数据错误: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
print(error_msg)
|
||||
self.mqtt_client.publish_message(error_msg)
|
||||
finally:
|
||||
source_socket.close()
|
||||
if source_address in self.remote_connections:
|
||||
self.remote_connections[source_address].close()
|
||||
del self.remote_connections[source_address]
|
||||
if source_address in self.pile_ids:
|
||||
del self.pile_ids[source_address]
|
||||
|
||||
def handle_client(self, client_socket, client_address):
|
||||
"""处理客户端连接"""
|
||||
try:
|
||||
remote_socket = self.create_remote_connection(client_address)
|
||||
if not remote_socket:
|
||||
client_socket.close()
|
||||
return
|
||||
|
||||
forward_thread = threading.Thread(
|
||||
target=self.forward_data,
|
||||
args=(client_socket, remote_socket, client_address)
|
||||
)
|
||||
backward_thread = threading.Thread(
|
||||
target=self.forward_data,
|
||||
args=(remote_socket, client_socket, client_address)
|
||||
)
|
||||
|
||||
forward_thread.daemon = True
|
||||
backward_thread.daemon = True
|
||||
|
||||
forward_thread.start()
|
||||
backward_thread.start()
|
||||
|
||||
forward_thread.join()
|
||||
backward_thread.join()
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"处理客户端连接错误: {str(e)}"
|
||||
logging.error(error_msg)
|
||||
print(error_msg)
|
||||
self.mqtt_client.publish_message(error_msg)
|
||||
finally:
|
||||
if client_address in self.remote_connections:
|
||||
self.remote_connections[client_address].close()
|
||||
del self.remote_connections[client_address]
|
||||
if client_address in self.pile_ids:
|
||||
del self.pile_ids[client_address]
|
||||
client_socket.close()
|
||||
close_msg = f"客户端连接已关闭 {client_address}"
|
||||
logging.info(close_msg)
|
||||
print(close_msg)
|
||||
self.mqtt_client.publish_message(close_msg)
|
||||
|
||||
def stop(self):
|
||||
"""停止代理服务器"""
|
||||
self.running = False
|
||||
if self.server_socket:
|
||||
self.server_socket.close()
|
||||
|
||||
# 关闭所有连接
|
||||
for remote_socket in self.remote_connections.values():
|
||||
remote_socket.close()
|
||||
self.remote_connections.clear()
|
||||
self.pile_ids.clear()
|
||||
|
||||
# 断开MQTT连接
|
||||
self.mqtt_client.disconnect()
|
||||
|
||||
stop_msg = "代理服务器已停止"
|
||||
logging.info(stop_msg)
|
||||
print(stop_msg)
|
||||
self.mqtt_client.publish_message(stop_msg)
|
||||
|
||||
|
||||
def main():
|
||||
server = ChargingPileProxyServer()
|
||||
try:
|
||||
server.start()
|
||||
except KeyboardInterrupt:
|
||||
server.stop()
|
||||
msg = "代理服务器已完全关闭"
|
||||
logging.info(msg)
|
||||
print(msg)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
x
Reference in New Issue
Block a user