modbus_MediaPipe/MQTT_project/MQTT_sub_Module.py

117 lines
3.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# pip install paho-mqtt
import json
import re
import time
from paho.mqtt import client as mqtt
# ========= 使用者設定 =========
BROKER_HOST = "169.254.11.130"
BROKER_PORT = 1886
TOPIC = "topic_plc_and_py_for_AXIS"
QOS = 1 # 建議測試用 1
# ============================
# 全域狀態(方便觀察最新值)
state = {
"BUTTON_Y": None,
"Forward_RPM": None,
"UPDown_RPM": None,
}
def _robust_json_parse(raw: str):
"""
將 PLC 端常見的非標準 JSON 嘗試修正後解析為 dict。
例:'{Forward_RPM:1500,UPDown_RPM:800}' -> {'Forward_RPM':1500,'UPDown_RPM':800}
也會把 TRUE/FALSE -> true/false
"""
s = raw.strip()
# 去掉最外層引號(若有)
if (s.startswith("'") and s.endswith("'")) or (s.startswith('"') and s.endswith('"')):
s = s[1:-1].strip()
# PLC 常見布林大寫 -> JSON 規範小寫
s = s.replace("TRUE", "true").replace("FALSE", "false")
# 自動補 key 的雙引號:{key: -> {"key":
s = re.sub(r'([{,]\s*)([A-Za-z_]\w*)\s*:', r'\1"\2":', s)
# 轉單引號為雙引號(若 payload 用了單引號包字串)
# 放在補 key 後,避免 key 的引號再被替換
s = s.replace("'", '"')
return json.loads(s)
def on_connect(client: mqtt.Client, userdata, flags, reason_code, properties=None):
"""
連上 Broker 後自動訂閱 Topic。
paho-mqtt 2.x 的 on_connect 第四個參數是 reason_code非 rc名稱略有不同。
"""
if reason_code == 0:
print(f"✅ 已連線到 MQTT Broker {BROKER_HOST}:{BROKER_PORT}")
client.subscribe(TOPIC, qos=QOS)
print(f"📡 已訂閱 Topic: {TOPIC}QoS={QOS}")
else:
print(f"❌ 連線失敗reason_code={reason_code}")
def on_message(client: mqtt.Client, userdata, msg: mqtt.MQTTMessage):
payload_raw = msg.payload.decode("utf-8", errors="replace")
print(f"\n📥 收到原始訊息:{payload_raw}")
# 先試標準 JSON失敗再用容錯解析
try:
data = json.loads(payload_raw)
except json.JSONDecodeError:
try:
data = _robust_json_parse(payload_raw)
except Exception as e:
print(f"❌ 無法解析為 JSON{payload_raw} ;錯誤:{e}")
return
# 只更新我們關心的欄位(有傳才改)
for key in ("BUTTON_Y", "Forward_RPM", "UPDown_RPM"):
if key in data:
state[key] = data[key]
print(
f"✅ 解析後資料:{data}\n"
f"📊 目前狀態BUTTON_Y={state['BUTTON_Y']}, "
f"Forward_RPM={state['Forward_RPM']}, UPDown_RPM={state['UPDown_RPM']}"
)
def on_disconnect(client: mqtt.Client, userdata, reason_code, properties=None):
print(f"🔌 已斷線reason_code={reason_code}")
def sub_to_plc(broker_host: str, broker_port: int, topic: str):
client = mqtt.Client(client_id="py_sub_plc_test", protocol=mqtt.MQTTv311)
# 如需帳密或 TLS在這裡設定
# client.username_pw_set("user", "pass")
# client.tls_set(ca_certs="ca.pem")
# 自動重連延遲paho-mqtt 2.x
client.reconnect_delay_set(min_delay=1, max_delay=10)
client.on_connect = on_connect
client.on_message = on_message
client.on_disconnect = on_disconnect
# 先連線,再進入 loop
client.connect(broker_host, broker_port, keepalive=60)
try:
# loop_forever 會阻塞執行緒,內建自動重連
client.loop_forever()
except KeyboardInterrupt:
print("\n🛑 收到中斷訊號,準備斷線...")
finally:
try:
client.disconnect()
except Exception:
pass
if __name__ == "__main__":
print("🚀 啟動 MQTT 訂閱器...")
print(f"Broker={BROKER_HOST}:{BROKER_PORT}, Topic={TOPIC}, QoS={QOS}")
sub_to_plc(BROKER_HOST, BROKER_PORT, TOPIC)