2026-01-09 17:52:52 +08:00
|
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
'''
|
|
|
|
|
|
# @Time : 2026/1/9 10:45
|
|
|
|
|
|
# @Author : reenrr
|
|
|
|
|
|
# @File : RK1106_server.py
|
2026-02-06 15:16:17 +08:00
|
|
|
|
# @Desc : RK1106服务端,等待工控机调用 通信为JSON格式
|
2026-01-09 17:52:52 +08:00
|
|
|
|
'''
|
|
|
|
|
|
import socket
|
|
|
|
|
|
import logging
|
|
|
|
|
|
import sys
|
2026-02-06 15:16:17 +08:00
|
|
|
|
import json
|
2026-01-09 17:52:52 +08:00
|
|
|
|
|
2026-02-06 15:16:17 +08:00
|
|
|
|
from stepper_motor import motor_start, align_wire, cleanup, motor_stop
|
2026-01-09 17:52:52 +08:00
|
|
|
|
|
2026-02-06 15:16:17 +08:00
|
|
|
|
|
|
|
|
|
|
# ------------日志配置(终端+文件双输出)--------------
|
2026-01-09 17:52:52 +08:00
|
|
|
|
logging.basicConfig(
|
|
|
|
|
|
level=logging.INFO,
|
|
|
|
|
|
format='%(asctime)s - %(levelname)s - %(message)s',
|
|
|
|
|
|
# 核心新增:日志文件配置
|
|
|
|
|
|
handlers=[
|
|
|
|
|
|
# 1. 文件处理器:保存到.log文件
|
|
|
|
|
|
logging.FileHandler(
|
|
|
|
|
|
"RK1106_server.log", # Buildroot推荐路径,临时测试可改/tmp/1106_server.log
|
|
|
|
|
|
mode='a', # 追加模式(不会覆盖历史日志)
|
|
|
|
|
|
encoding='utf-8' # 防止中文乱码(必加)
|
|
|
|
|
|
),
|
|
|
|
|
|
# 2. 终端处理器:输出到控制台
|
|
|
|
|
|
logging.StreamHandler(sys.stdout)
|
|
|
|
|
|
]
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# --------配置TCP服务端----------
|
2026-02-06 15:16:17 +08:00
|
|
|
|
HOST = "192.168.0.100"
|
2026-01-09 17:52:52 +08:00
|
|
|
|
PORT = 8888
|
|
|
|
|
|
|
2026-02-06 15:16:17 +08:00
|
|
|
|
# 全局参数缓存
|
|
|
|
|
|
MOTOR_CONFIG = {
|
|
|
|
|
|
"speed": 2500, # 默认速度
|
|
|
|
|
|
"cycle": 10.0, # 默认旋转圈数
|
2026-01-09 17:52:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 15:16:17 +08:00
|
|
|
|
def handle_setting(para_type: str, para_value: str) ->dict:
|
|
|
|
|
|
"""
|
|
|
|
|
|
处理客户端发送的参数设置指令(cmd:"setting"),更新全局电机配置
|
|
|
|
|
|
:param para_type: 要设置的参数类型,仅支持"speed"或"cycle"
|
|
|
|
|
|
:param para_value: 参数值字符串,将尝试转换为int(speed)或float(cycle)
|
|
|
|
|
|
:return: 标准化相应字典dict,如:
|
|
|
|
|
|
{
|
|
|
|
|
|
"Result": "1"表示成功,"0"表示失败,
|
|
|
|
|
|
"ErrMsg": str # 成功提示或错误详情
|
|
|
|
|
|
}
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if para_type == "speed":
|
|
|
|
|
|
MOTOR_CONFIG["speed"] = int(para_value)
|
|
|
|
|
|
elif para_type == "cycle":
|
|
|
|
|
|
MOTOR_CONFIG["cycle"] = float(para_value)
|
|
|
|
|
|
else:
|
|
|
|
|
|
return {"Result": "0", "ErrMsg": f"不支持的参数类型: {para_type}"}
|
|
|
|
|
|
return {"Result": "1", "ErrMsg": "设置成功"}
|
|
|
|
|
|
except ValueError as e:
|
|
|
|
|
|
return {"Result": "0", "ErrMsg": f"参数值格式错误: {str(e)}"}
|
|
|
|
|
|
|
|
|
|
|
|
def handle_start(para_type: str, para_value: str) -> dict:
|
|
|
|
|
|
"""
|
|
|
|
|
|
处理启动电机指令(cmd: "start"),使用当前MOTOR_CONFIG配置运行电机
|
|
|
|
|
|
:param para_type:为"direction"时,使用"para_value"作为临时方向
|
|
|
|
|
|
:param para_value:为0或1
|
|
|
|
|
|
:return: 标准化相应字典dict,如:
|
|
|
|
|
|
{
|
|
|
|
|
|
"Result": "1"表示成功,"0"表示失败,
|
|
|
|
|
|
"ErrMsg": str #执行结果或异常信息
|
|
|
|
|
|
}
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if para_type == "direction":
|
|
|
|
|
|
direction = int(para_value)
|
|
|
|
|
|
if direction not in (0,1):
|
|
|
|
|
|
return {"Result": "0", "ErrMsg": "方向必须为0或1"}
|
|
|
|
|
|
|
|
|
|
|
|
motor_start(speed=MOTOR_CONFIG["speed"],
|
|
|
|
|
|
cycle=MOTOR_CONFIG["cycle"],
|
|
|
|
|
|
direction=direction)
|
|
|
|
|
|
dir_str = "正向" if direction == 1 else "负向"
|
|
|
|
|
|
return {"Result": "1", "ErrMsg": f"电机启动成功({dir_str})"}
|
|
|
|
|
|
else:
|
|
|
|
|
|
return {"Result": "0", "ErrMsg": "start 指令仅支持'direction'"}
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return {"Result": "0", "ErrMsg": f"电机启动失败:{str(e)}"}
|
|
|
|
|
|
|
|
|
|
|
|
def handle_stop() -> dict:
|
|
|
|
|
|
"""
|
|
|
|
|
|
处理停止电机指令(cmd: "stop")
|
|
|
|
|
|
:return: 标准化相应字典dict,如:
|
|
|
|
|
|
{
|
|
|
|
|
|
"Result": "1"表示成功,"0"表示失败,
|
|
|
|
|
|
"ErrMsg": str #执行结果或异常信息
|
|
|
|
|
|
}
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
motor_stop()
|
|
|
|
|
|
return {"Result": "1", "ErrMsg": "电机已停止"}
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return {"Result": "0", "ErrMsg": f"电机停止失败:{str(e)}"}
|
|
|
|
|
|
|
|
|
|
|
|
def handle_align() -> dict:
|
|
|
|
|
|
"""
|
|
|
|
|
|
处理线条对齐(挡板一来一回)
|
|
|
|
|
|
:return: dict
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
align_wire(MOTOR_CONFIG['speed'], MOTOR_CONFIG['cycle'])
|
|
|
|
|
|
return {"Result": "1", "ErrMsg": "处理线条对齐"}
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return {"Result": "0", "ErrMsg": "线条对齐失败"}
|
|
|
|
|
|
|
|
|
|
|
|
def parse_json_command(data: str) -> dict:
|
2026-01-09 17:52:52 +08:00
|
|
|
|
"""
|
2026-02-06 15:16:17 +08:00
|
|
|
|
解析客户端发送的原始JSON字符串指令,并分发至对应处理函数
|
|
|
|
|
|
:param data: 客户端发送的原始JSON字符串
|
|
|
|
|
|
:return dict:标准化响应字典
|
2026-01-09 17:52:52 +08:00
|
|
|
|
"""
|
2026-02-06 15:16:17 +08:00
|
|
|
|
try:
|
|
|
|
|
|
cmd_obj = json.loads(data.strip())
|
|
|
|
|
|
except json.JSONDecodeError as e:
|
|
|
|
|
|
return {"Result": "0", "ErrMsg": f"JSON 格式错误: {str(e)}"}
|
|
|
|
|
|
|
|
|
|
|
|
cmd = cmd_obj.get("cmd", "").strip()
|
|
|
|
|
|
para_type = cmd_obj.get("para_type", "").strip()
|
|
|
|
|
|
para_value = cmd_obj.get("para_value", "").strip()
|
|
|
|
|
|
|
|
|
|
|
|
if cmd == "setting":
|
|
|
|
|
|
return handle_setting(para_type, para_value)
|
|
|
|
|
|
elif cmd == "start":
|
|
|
|
|
|
return handle_start(para_type, para_value)
|
|
|
|
|
|
elif cmd == "stop":
|
|
|
|
|
|
if para_type == "none" and para_value == "none":
|
|
|
|
|
|
return handle_stop()
|
|
|
|
|
|
else:
|
|
|
|
|
|
return {"Result": "0", "ErrMsg": "stop指令参数必须为none"}
|
|
|
|
|
|
elif cmd == "alignment":
|
|
|
|
|
|
if para_type == "none" and para_value == "none":
|
|
|
|
|
|
return handle_align()
|
|
|
|
|
|
else:
|
|
|
|
|
|
return {"Result": "0", "ErrMsg": "alignment指令参数必须为none"}
|
|
|
|
|
|
else:
|
|
|
|
|
|
return {"Result": "0", "ErrMsg": f"未知指令:{cmd}"}
|
|
|
|
|
|
|
2026-01-09 17:52:52 +08:00
|
|
|
|
|
|
|
|
|
|
# ----------对外接口----------
|
|
|
|
|
|
def server():
|
2026-02-06 15:16:17 +08:00
|
|
|
|
"""启动TCP服务端,监听指定端口,接收工控机连接并循环处理JSON指令"""
|
2026-01-09 17:52:52 +08:00
|
|
|
|
# 创建TCP socket
|
2026-02-06 15:16:17 +08:00
|
|
|
|
server_socket = None
|
|
|
|
|
|
conn = None
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
2026-01-09 17:52:52 +08:00
|
|
|
|
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
|
|
|
|
server_socket.bind((HOST, PORT))
|
|
|
|
|
|
server_socket.listen(1) # 只允许1个工控机连接
|
|
|
|
|
|
logging.info(f"[1106] 服务已启动,监听端口:{PORT},等待工控机连接...")
|
|
|
|
|
|
|
2026-02-06 15:16:17 +08:00
|
|
|
|
while True: # 持续接受新连接
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 等待工控机连接
|
|
|
|
|
|
conn, addr = server_socket.accept()
|
|
|
|
|
|
logging.info(f"[1106] 工控机已连接:{addr}")
|
|
|
|
|
|
|
|
|
|
|
|
# 循环接收指令
|
|
|
|
|
|
while True:
|
|
|
|
|
|
# 接收指令(最大1024字节)
|
|
|
|
|
|
data = conn.recv(1024).decode()
|
|
|
|
|
|
if not data:
|
|
|
|
|
|
logging.warning("客户端断开连接")
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
logging.info(f"\n[1106] 收到工控机指令:{data}")
|
|
|
|
|
|
|
|
|
|
|
|
# 解析指令
|
|
|
|
|
|
response_dict = parse_json_command(data)
|
|
|
|
|
|
response_json = json.dumps(response_dict, ensure_ascii=False) + "\n" # 看需不需要加换行符\n
|
|
|
|
|
|
|
|
|
|
|
|
# 发送响应给工控机
|
|
|
|
|
|
conn.sendall(response_json.encode("utf-8"))
|
|
|
|
|
|
logging.info(f"[1106] 已发送响应:{response_json}")
|
|
|
|
|
|
|
|
|
|
|
|
except ConnectionError:
|
|
|
|
|
|
logging.info("客户端异常断开")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logging.info(f"处理连接时发生错误: {e}")
|
|
|
|
|
|
finally:
|
|
|
|
|
|
if conn is not None:
|
|
|
|
|
|
conn.close()
|
|
|
|
|
|
conn = None # 重置,避免重复关闭
|
|
|
|
|
|
logging.info("客户端连接已关闭,等待新连接...")
|
|
|
|
|
|
|
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
|
logging.info("\n收到 Ctrl+C,正在关闭服务...")
|
|
|
|
|
|
finally:
|
|
|
|
|
|
if server_socket:
|
|
|
|
|
|
server_socket.close()
|
|
|
|
|
|
logging.info("服务已停止,监听 socket 已释放")
|
2026-01-09 17:52:52 +08:00
|
|
|
|
|
|
|
|
|
|
# ----------测试接口----------
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
server()
|
|
|
|
|
|
|