调试更改+挡板电机添加线程和配置文件
This commit is contained in:
@ -1,210 +1,217 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
# @Time : 2026/1/9 10:45
|
||||
# @Time : 2026/3/16 15:00
|
||||
# @Author : reenrr
|
||||
# @File : RK1106_server.py
|
||||
# @Desc : RK1106服务端,等待工控机调用 通信为JSON格式
|
||||
# @Desc : RK1106服务端(类形式,多线程),等待工控机调用 通信为JSON格式
|
||||
'''
|
||||
import socket
|
||||
import logging
|
||||
import sys
|
||||
import json
|
||||
import threading
|
||||
|
||||
from stepper_motor import motor_start, align_wire, cleanup, motor_stop
|
||||
|
||||
|
||||
# ------------日志配置(终端+文件双输出)--------------
|
||||
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服务端----------
|
||||
HOST = "192.168.0.100"
|
||||
HOST = "192.168.5.100"
|
||||
PORT = 8888
|
||||
CONFIG_FILE = "motor_config.json"
|
||||
|
||||
# 全局参数缓存
|
||||
MOTOR_CONFIG = {
|
||||
"speed": 2500, # 默认速度
|
||||
"cycle": 10.0, # 默认旋转圈数
|
||||
}
|
||||
|
||||
def handle_setting(para_type: str, para_value: str) ->dict:
|
||||
class RK1106Server:
|
||||
"""
|
||||
处理客户端发送的参数设置指令(cmd:"setting"),更新全局电机配置
|
||||
:param para_type: 要设置的参数类型,仅支持"speed"或"cycle"
|
||||
:param para_value: 参数值字符串,将尝试转换为int(speed)或float(cycle)
|
||||
:return: 标准化相应字典dict,如:
|
||||
{
|
||||
"Result": "1"表示成功,"0"表示失败,
|
||||
"ErrMsg": str # 成功提示或错误详情
|
||||
}
|
||||
RK1106服务端类
|
||||
在初始化时加载配置,提供配置管理和指令处理功能
|
||||
"""
|
||||
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 __init__(self):
|
||||
"""初始化服务端,加载配置"""
|
||||
self.config = self._load_config()
|
||||
logging.info(f"服务初始化完成,加载配置:{self.config}")
|
||||
|
||||
def _load_config(self):
|
||||
"""
|
||||
从JSON文件加载电机配置
|
||||
:return: 加载的配置字典
|
||||
"""
|
||||
try:
|
||||
with open(CONFIG_FILE, "r") as f:
|
||||
return json.load(f)
|
||||
except FileNotFoundError:
|
||||
logging.warning(f"配置文件 {CONFIG_FILE} 未找到,将使用默认配置")
|
||||
return {"speed": 2500, "cycle": 10.0}
|
||||
except json.JSONDecodeError:
|
||||
logging.error(f"配置文件 {CONFIG_FILE} 格式错误")
|
||||
return {"speed": 2500, "cycle": 10.0}
|
||||
|
||||
def _save_config(self):
|
||||
"""
|
||||
将电机配置保存到JSON文件
|
||||
"""
|
||||
try:
|
||||
with open(CONFIG_FILE, "w") as f:
|
||||
json.dump(self.config, f, indent=4, ensure_ascii=False)
|
||||
logging.info(f"配置已保存:{self.config}")
|
||||
except IOError as e:
|
||||
logging.error(f"配置文件 {CONFIG_FILE} 保存失败: {str(e)}")
|
||||
|
||||
def handle_setting(self, para_type: str, para_value: str) -> dict:
|
||||
"""
|
||||
处理客户端发送的参数设置指令(cmd:"setting"),更新电机配置
|
||||
:param para_type: 要设置的参数类型,仅支持"speed"或"cycle"
|
||||
:param para_value: 参数值字符串,将尝试转换为int(speed)或float(cycle)
|
||||
:return: 标准化相应字典dict
|
||||
"""
|
||||
try:
|
||||
if para_type == "speed":
|
||||
self.config["speed"] = int(para_value)
|
||||
elif para_type == "cycle":
|
||||
self.config["cycle"] = float(para_value)
|
||||
else:
|
||||
return {"Result": "0", "ErrMsg": f"不支持的参数类型: {para_type}"}
|
||||
|
||||
# 保存到文件
|
||||
self._save_config()
|
||||
return {"Result": "1", "ErrMsg": "设置成功"}
|
||||
except ValueError as e:
|
||||
return {"Result": "0", "ErrMsg": f"参数值格式错误: {str(e)}"}
|
||||
|
||||
def handle_start(self, para_type: str, para_value: str) -> dict:
|
||||
"""
|
||||
处理启动电机指令(cmd: "start"),使用当前配置运行电机
|
||||
:param para_type:为"direction"时,使用"para_value"作为临时方向
|
||||
:param para_value:为0或1
|
||||
:return: 标准化相应字典dict
|
||||
"""
|
||||
try:
|
||||
if para_type == "direction":
|
||||
direction = int(para_value)
|
||||
if direction not in (0, 1):
|
||||
return {"Result": "0", "ErrMsg": "方向必须为0或1"}
|
||||
|
||||
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"],
|
||||
motor_start(speed=self.config["speed"],
|
||||
cycle=self.config["cycle"],
|
||||
direction=direction)
|
||||
dir_str = "正向" if direction == 1 else "负向"
|
||||
return {"Result": "1", "ErrMsg": f"电机启动成功({dir_str})"}
|
||||
|
||||
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(self) -> dict:
|
||||
"""
|
||||
处理停止电机指令(cmd: "stop")
|
||||
:return: 标准化相应字典dict
|
||||
"""
|
||||
try:
|
||||
motor_stop()
|
||||
return {"Result": "1", "ErrMsg": "电机停止指令已发送"}
|
||||
except Exception as e:
|
||||
return {"Result": "0", "ErrMsg": f"电机停止失败:{str(e)}"}
|
||||
|
||||
def handle_align(self) -> dict:
|
||||
"""
|
||||
处理线条对齐(挡板一来一回)
|
||||
:return: dict
|
||||
"""
|
||||
try:
|
||||
align_wire(self.config['speed'], self.config['cycle'])
|
||||
return {"Result": "1", "ErrMsg": "线条对齐指令已发送"}
|
||||
except Exception as e:
|
||||
return {"Result": "0", "ErrMsg": "线条对齐失败"}
|
||||
|
||||
def parse_json_command(self, data: str) -> dict:
|
||||
"""
|
||||
解析客户端发送的原始JSON字符串指令,并分发至对应处理函数
|
||||
:param data: 客户端发送的原始JSON字符串
|
||||
:return dict:标准化响应字典
|
||||
"""
|
||||
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 self.handle_setting(para_type, para_value)
|
||||
elif cmd == "start":
|
||||
return self.handle_start(para_type, para_value)
|
||||
elif cmd == "stop":
|
||||
if para_type == "none" and para_value == "none":
|
||||
return self.handle_stop()
|
||||
else:
|
||||
return {"Result": "0", "ErrMsg": "stop指令参数必须为none"}
|
||||
elif cmd == "alignment":
|
||||
if para_type == "none" and para_value == "none":
|
||||
return self.handle_align()
|
||||
else:
|
||||
return {"Result": "0", "ErrMsg": "alignment指令参数必须为none"}
|
||||
else:
|
||||
return {"Result": "0", "ErrMsg": "start 指令仅支持'direction'"}
|
||||
except Exception as e:
|
||||
return {"Result": "0", "ErrMsg": f"电机启动失败:{str(e)}"}
|
||||
return {"Result": "0", "ErrMsg": f"未知指令:{cmd}"}
|
||||
|
||||
def run_server(self):
|
||||
"""
|
||||
启动TCP服务端,监听指定端口,接收工控机连接并循环处理JSON指令
|
||||
"""
|
||||
# 创建TCP socket
|
||||
server_socket = None
|
||||
conn = None
|
||||
|
||||
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)}"}
|
||||
try:
|
||||
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
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},等待工控机连接...")
|
||||
|
||||
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": "线条对齐失败"}
|
||||
while True: # 持续接受新连接
|
||||
try:
|
||||
# 等待工控机连接
|
||||
conn, addr = server_socket.accept()
|
||||
logging.info(f"[1106] 工控机已连接:{addr}")
|
||||
|
||||
def parse_json_command(data: str) -> dict:
|
||||
"""
|
||||
解析客户端发送的原始JSON字符串指令,并分发至对应处理函数
|
||||
:param data: 客户端发送的原始JSON字符串
|
||||
:return dict:标准化响应字典
|
||||
"""
|
||||
try:
|
||||
cmd_obj = json.loads(data.strip())
|
||||
except json.JSONDecodeError as e:
|
||||
return {"Result": "0", "ErrMsg": f"JSON 格式错误: {str(e)}"}
|
||||
# 循环接收指令
|
||||
while True:
|
||||
# 接收指令(最大1024字节)
|
||||
data = conn.recv(1024).decode()
|
||||
if not data:
|
||||
logging.warning("客户端断开连接")
|
||||
break
|
||||
|
||||
cmd = cmd_obj.get("cmd", "").strip()
|
||||
para_type = cmd_obj.get("para_type", "").strip()
|
||||
para_value = cmd_obj.get("para_value", "").strip()
|
||||
logging.info(f"\n[1106] 收到工控机指令:{data}")
|
||||
|
||||
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}"}
|
||||
# 解析指令
|
||||
response_dict = self.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("客户端连接已关闭,等待新连接...")
|
||||
|
||||
# ----------对外接口----------
|
||||
def server():
|
||||
"""启动TCP服务端,监听指定端口,接收工控机连接并循环处理JSON指令"""
|
||||
# 创建TCP socket
|
||||
server_socket = None
|
||||
conn = None
|
||||
|
||||
try:
|
||||
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
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},等待工控机连接...")
|
||||
|
||||
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 已释放")
|
||||
except KeyboardInterrupt:
|
||||
logging.info("\n收到 Ctrl+C,正在关闭服务...")
|
||||
finally:
|
||||
if server_socket:
|
||||
server_socket.close()
|
||||
logging.info("服务已停止,监听 socket 已释放")
|
||||
|
||||
# ----------测试接口----------
|
||||
if __name__ == "__main__":
|
||||
server()
|
||||
|
||||
SERVER = RK1106Server()
|
||||
SERVER.run_server()
|
||||
|
||||
253
RK1106/RK1106_server_test.py
Normal file
253
RK1106/RK1106_server_test.py
Normal file
@ -0,0 +1,253 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
# @Time : 2026/3/16 15:00
|
||||
# @Author : reenrr
|
||||
# @File : RK1106_server_test.py
|
||||
# @Desc : RK1106服务端(类形式,多线程),等待工控机调用 通信为JSON格式 --待测试,加了线程
|
||||
'''
|
||||
import socket
|
||||
import logging
|
||||
import json
|
||||
import threading
|
||||
|
||||
from stepper_motor import motor_start, align_wire, cleanup, motor_stop
|
||||
|
||||
|
||||
# --------配置TCP服务端----------
|
||||
HOST = "192.168.5.100"
|
||||
PORT = 8888
|
||||
CONFIG_FILE = "motor_config.json"
|
||||
|
||||
|
||||
class RK1106Server:
|
||||
"""
|
||||
RK1106服务端类
|
||||
在初始化时加载配置,提供配置管理和指令处理功能
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""初始化服务端,加载配置"""
|
||||
self.config = self._load_config()
|
||||
self.current_thread = None
|
||||
logging.info(f"服务初始化完成,加载配置:{self.config}")
|
||||
|
||||
def _load_config(self):
|
||||
"""
|
||||
从JSON文件加载电机配置
|
||||
:return: 加载的配置字典
|
||||
"""
|
||||
try:
|
||||
with open(CONFIG_FILE, "r") as f:
|
||||
return json.load(f)
|
||||
except FileNotFoundError:
|
||||
logging.warning(f"配置文件 {CONFIG_FILE} 未找到,将使用默认配置")
|
||||
return {"speed": 2500, "cycle": 10.0}
|
||||
except json.JSONDecodeError:
|
||||
logging.error(f"配置文件 {CONFIG_FILE} 格式错误")
|
||||
return {"speed": 2500, "cycle": 10.0}
|
||||
|
||||
def _save_config(self):
|
||||
"""
|
||||
将电机配置保存到JSON文件
|
||||
"""
|
||||
try:
|
||||
with open(CONFIG_FILE, "w") as f:
|
||||
json.dump(self.config, f, indent=4, ensure_ascii=False)
|
||||
logging.info(f"配置已保存:{self.config}")
|
||||
except IOError as e:
|
||||
logging.error(f"配置文件 {CONFIG_FILE} 保存失败: {str(e)}")
|
||||
|
||||
def _execute_motor_command(self, cmd_func: callable, *args, **kwargs):
|
||||
"""
|
||||
在单独的线程中执行电机命令
|
||||
:param cmd_func: 要执行的电机命令函数
|
||||
:param args: 传递给cmd_func的位置参数
|
||||
:param kwargs: 传递给cmd_func的关键字参数
|
||||
"""
|
||||
try:
|
||||
result = cmd_func(*args, **kwargs)
|
||||
logging.info(f"电机命令执行完成: {result}")
|
||||
except Exception as e:
|
||||
logging.error(f"执行电机命令时出错: {str(e)}")
|
||||
|
||||
def handle_setting(self, para_type: str, para_value: str) -> dict:
|
||||
"""
|
||||
处理客户端发送的参数设置指令(cmd:"setting"),更新电机配置
|
||||
:param para_type: 要设置的参数类型,仅支持"speed"或"cycle"
|
||||
:param para_value: 参数值字符串,将尝试转换为int(speed)或float(cycle)
|
||||
:return: 标准化相应字典dict
|
||||
"""
|
||||
try:
|
||||
if para_type == "speed":
|
||||
self.config["speed"] = int(para_value)
|
||||
elif para_type == "cycle":
|
||||
self.config["cycle"] = float(para_value)
|
||||
else:
|
||||
return {"Result": "0", "ErrMsg": f"不支持的参数类型: {para_type}"}
|
||||
|
||||
# 保存到文件
|
||||
self._save_config()
|
||||
return {"Result": "1", "ErrMsg": "设置成功"}
|
||||
except ValueError as e:
|
||||
return {"Result": "0", "ErrMsg": f"参数值格式错误: {str(e)}"}
|
||||
|
||||
def handle_start(self, para_type: str, para_value: str) -> dict:
|
||||
"""
|
||||
处理启动电机指令(cmd: "start"),使用当前配置运行电机
|
||||
:param para_type:为"direction"时,使用"para_value"作为临时方向
|
||||
:param para_value:为0或1
|
||||
:return: 标准化相应字典dict
|
||||
"""
|
||||
try:
|
||||
if para_type == "direction":
|
||||
direction = int(para_value)
|
||||
if direction not in (0, 1):
|
||||
return {"Result": "0", "ErrMsg": "方向必须为0或1"}
|
||||
|
||||
# 在新线程中启动电机
|
||||
thread = threading.Thread(
|
||||
target=self._execute_motor_command,
|
||||
args=(motor_start,),
|
||||
kwargs={
|
||||
"speed": self.config["speed"],
|
||||
"cycle": self.config["cycle"],
|
||||
"direction": direction
|
||||
}
|
||||
)
|
||||
thread.start()
|
||||
|
||||
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(self) -> dict:
|
||||
"""
|
||||
处理停止电机指令(cmd: "stop")
|
||||
:return: 标准化相应字典dict
|
||||
"""
|
||||
try:
|
||||
# 在新线程中停止电机
|
||||
thread = threading.Thread(
|
||||
target=self._execute_motor_command,
|
||||
args=(motor_stop,)
|
||||
)
|
||||
thread.start()
|
||||
return {"Result": "1", "ErrMsg": "电机停止指令已发送"}
|
||||
except Exception as e:
|
||||
return {"Result": "0", "ErrMsg": f"电机停止失败:{str(e)}"}
|
||||
|
||||
def handle_align(self) -> dict:
|
||||
"""
|
||||
处理线条对齐(挡板一来一回)
|
||||
:return: dict
|
||||
"""
|
||||
try:
|
||||
# 在新线程中对齐线条
|
||||
thread = threading.Thread(
|
||||
target=self._execute_motor_command,
|
||||
args=(align_wire,),
|
||||
kwargs={
|
||||
"speed": self.config['speed'],
|
||||
"cycle": self.config['cycle']
|
||||
}
|
||||
)
|
||||
thread.start()
|
||||
return {"Result": "1", "ErrMsg": "线条对齐指令已发送"}
|
||||
except Exception as e:
|
||||
return {"Result": "0", "ErrMsg": "线条对齐失败"}
|
||||
|
||||
def parse_json_command(self, data: str) -> dict:
|
||||
"""
|
||||
解析客户端发送的原始JSON字符串指令,并分发至对应处理函数
|
||||
:param data: 客户端发送的原始JSON字符串
|
||||
:return dict:标准化响应字典
|
||||
"""
|
||||
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 self.handle_setting(para_type, para_value)
|
||||
elif cmd == "start":
|
||||
return self.handle_start(para_type, para_value)
|
||||
elif cmd == "stop":
|
||||
if para_type == "none" and para_value == "none":
|
||||
return self.handle_stop()
|
||||
else:
|
||||
return {"Result": "0", "ErrMsg": "stop指令参数必须为none"}
|
||||
elif cmd == "alignment":
|
||||
if para_type == "none" and para_value == "none":
|
||||
return self.handle_align()
|
||||
else:
|
||||
return {"Result": "0", "ErrMsg": "alignment指令参数必须为none"}
|
||||
else:
|
||||
return {"Result": "0", "ErrMsg": f"未知指令:{cmd}"}
|
||||
|
||||
def run_server(self):
|
||||
"""
|
||||
启动TCP服务端,监听指定端口,接收工控机连接并循环处理JSON指令
|
||||
"""
|
||||
# 创建TCP socket
|
||||
server_socket = None
|
||||
conn = None
|
||||
|
||||
try:
|
||||
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
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},等待工控机连接...")
|
||||
|
||||
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 = self.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 已释放")
|
||||
|
||||
# ----------测试接口----------
|
||||
if __name__ == "__main__":
|
||||
SERVER = RK1106Server()
|
||||
SERVER.run_server()
|
||||
BIN
RK1106/image.png
Normal file
BIN
RK1106/image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
4
RK1106/motor_config.json
Normal file
4
RK1106/motor_config.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"speed": 2500,
|
||||
"cycle": 10.0
|
||||
}
|
||||
@ -8,4 +8,7 @@
|
||||

|
||||
GPIO32 --脉冲引脚
|
||||
GPIO33 --方向引脚
|
||||
剩下两个引脚为地线
|
||||
剩下两个引脚为地线
|
||||
|
||||
# 自启动设置:
|
||||

|
||||
@ -7,9 +7,29 @@
|
||||
# @Desc : 线条厂控制步进电机测试 应该不会丢步
|
||||
"""
|
||||
import time
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from periphery import GPIO
|
||||
|
||||
# ------------日志配置(终端+文件双输出)--------------
|
||||
|
||||
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)
|
||||
]
|
||||
)
|
||||
|
||||
# ------------参数配置-------------
|
||||
# 1. 脉冲(PUL)引脚配置 → GPIO32
|
||||
PUL_Pin = 32
|
||||
@ -66,8 +86,8 @@ class StepperMotor:
|
||||
self.pul_gpio.write(False)
|
||||
self.dir_gpio.write(False)
|
||||
|
||||
print(f"✅ PUL引脚初始化完成:{self.pul_pin} 引脚")
|
||||
print(f"✅ DIR引脚初始化完成:{self.dir_pin} 引脚")
|
||||
logging.info(f"✅ PUL引脚初始化完成:{self.pul_pin} 引脚")
|
||||
logging.info(f"✅ DIR引脚初始化完成:{self.dir_pin} 引脚")
|
||||
|
||||
except PermissionError:
|
||||
raise RuntimeError("权限不足!请用sudo运行程序(sudo python xxx.py)")
|
||||
@ -76,10 +96,10 @@ class StepperMotor:
|
||||
|
||||
def _validate_params(self, rounds: float, direction: int) -> bool:
|
||||
if rounds <= 0:
|
||||
print("圈数必须为正数")
|
||||
logging.info("圈数必须为正数")
|
||||
return False
|
||||
if direction not in (0, 1):
|
||||
print("方向必须为0(逆时针)或1(顺时针)")
|
||||
logging.info("方向必须为0(逆时针)或1(顺时针)")
|
||||
return False
|
||||
return True
|
||||
|
||||
@ -97,17 +117,17 @@ class StepperMotor:
|
||||
# 设置旋转方向(DIR电平)
|
||||
if direction == 1: # 顺时针
|
||||
self.dir_gpio.write(self.clockwise_level)
|
||||
print(f"\n=== 顺时针旋转 {rounds} 圈 ===")
|
||||
logging.info(f"\n=== 顺时针旋转 {rounds} 圈 ===")
|
||||
else: # 逆时针
|
||||
self.dir_gpio.write(self.counter_clockwise_level)
|
||||
print(f"\n=== 逆时针旋转 {rounds} 圈 ===")
|
||||
logging.info(f"\n=== 逆时针旋转 {rounds} 圈 ===")
|
||||
|
||||
# 计算总脉冲数和时序(精准控制,避免丢步)
|
||||
total_pulses = int(rounds * self.pulses_per_round)
|
||||
pulse_period = 1.0 / pulse_frequency # 脉冲周期(秒)
|
||||
half_period = pulse_period / 2 # 占空比50%(MA860H最优)
|
||||
|
||||
print(f"总脉冲数:{total_pulses} | 频率:{pulse_frequency}Hz | 周期:{pulse_period * 1000:.2f}ms")
|
||||
logging.info(f"总脉冲数:{total_pulses} | 频率:{pulse_frequency}Hz | 周期:{pulse_period * 1000:.2f}ms")
|
||||
start_time = time.perf_counter() # 高精度计时(避免丢步)
|
||||
|
||||
# 发送脉冲序列(核心:占空比50%的方波)
|
||||
@ -122,13 +142,13 @@ class StepperMotor:
|
||||
# 更新下一个脉冲的起始时间
|
||||
start_time += pulse_period
|
||||
|
||||
print("✅ 旋转完成")
|
||||
logging.info("✅ 旋转完成")
|
||||
|
||||
def stop(self):
|
||||
"""紧急停止(置低脉冲引脚)"""
|
||||
if self.pul_gpio:
|
||||
self.pul_gpio.write(False)
|
||||
print("🛑 电机已停止")
|
||||
logging.info("🛑 电机已停止")
|
||||
|
||||
def close(self):
|
||||
"""释放GPIO资源"""
|
||||
@ -136,12 +156,12 @@ class StepperMotor:
|
||||
if self.pul_gpio:
|
||||
self.pul_gpio.write(False) # 脉冲引脚置低
|
||||
self.pul_gpio.close()
|
||||
print("\n✅ PUL引脚已关闭(电平置低)")
|
||||
logging.info("\n✅ PUL引脚已关闭(电平置低)")
|
||||
|
||||
if self.dir_gpio:
|
||||
self.dir_gpio.write(False) # 方向引脚置低
|
||||
self.dir_gpio.close()
|
||||
print("✅ DIR引脚已关闭(电平置低)")
|
||||
logging.info("✅ DIR引脚已关闭(电平置低)")
|
||||
|
||||
# 重置GPIO对象
|
||||
self.pul_gpio = None
|
||||
@ -163,16 +183,16 @@ def motor_start(speed: int, cycle: float, direction: int):
|
||||
:param direction: 0=负向(逆时针),1=正向(顺时针)
|
||||
"""
|
||||
try:
|
||||
print("\n=== 启动步进电机 ===")
|
||||
logging.info("\n=== 启动步进电机 ===")
|
||||
|
||||
GLOBAL_MOTOR.rotate(pulse_frequency=speed, rounds=cycle, direction=direction)
|
||||
time.sleep(5) # 暂停5秒
|
||||
|
||||
except ImportError:
|
||||
print("\n❌ 缺少依赖:请安装python-periphery")
|
||||
print("命令:pip install python-periphery")
|
||||
logging.info("\n❌ 缺少依赖:请安装python-periphery")
|
||||
logging.info("命令:pip install python-periphery")
|
||||
except Exception as e:
|
||||
print(f"\n❌ 程序异常:{str(e)}")
|
||||
logging.info(f"\n❌ 程序异常:{str(e)}")
|
||||
|
||||
def motor_stop():
|
||||
"""紧急停止(仅停止脉冲,保留实例)"""
|
||||
@ -180,7 +200,8 @@ def motor_stop():
|
||||
if GLOBAL_MOTOR:
|
||||
GLOBAL_MOTOR.stop()
|
||||
except Exception as e:
|
||||
print("停止失败:{e}")
|
||||
logging.info("停止失败:{e}")
|
||||
|
||||
|
||||
def align_wire(speed: int, cycle: float):
|
||||
"""
|
||||
@ -189,20 +210,20 @@ def align_wire(speed: int, cycle: float):
|
||||
:param cycle: 旋转圈数
|
||||
"""
|
||||
try:
|
||||
print("\n=== 启动线条对齐 ===")
|
||||
logging.info("\n=== 启动线条对齐 ===")
|
||||
|
||||
# 靠近电机方向 逆时针
|
||||
GLOBAL_MOTOR.rotate(pulse_frequency=speed, rounds=cycle, direction=0)
|
||||
time.sleep(5) # 暂停5秒
|
||||
time.sleep(2) # 暂停3秒
|
||||
# 远离电机方向 顺时针
|
||||
GLOBAL_MOTOR.rotate(pulse_frequency=speed,rounds=cycle, direction=1)
|
||||
time.sleep(5) # 暂停5秒
|
||||
time.sleep(2) # 暂停3秒
|
||||
|
||||
except ImportError:
|
||||
print("\n❌ 缺少依赖:请安装python-periphery")
|
||||
print("命令:pip install python-periphery")
|
||||
logging.info("\n❌ 缺少依赖:请安装python-periphery")
|
||||
logging.info("命令:pip install python-periphery")
|
||||
except Exception as e:
|
||||
print(f"\n❌ 程序异常:{str(e)}")
|
||||
logging.info(f"\n❌ 程序异常:{str(e)}")
|
||||
|
||||
def cleanup():
|
||||
"""程序退出时统一清理"""
|
||||
@ -212,14 +233,14 @@ def cleanup():
|
||||
|
||||
if __name__ == '__main__':
|
||||
align_wire(speed=2500, cycle=10.0)
|
||||
time.sleep(10) # 电机运动需要的时间
|
||||
time.sleep(5) # 电机运动需要的时间
|
||||
|
||||
# 测试是否电机能停止
|
||||
# # 测试是否电机能停止
|
||||
motor_start(speed=2500, cycle=5.0, direction=0)
|
||||
time.sleep(1) # 电机运动需要的时间
|
||||
motor_stop()
|
||||
motor_stop()
|
||||
time.sleep(0.2)
|
||||
|
||||
while(True): # 防止程序退出
|
||||
|
||||
while True: # 防止程序退出
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user