MC200编程手册(Python)
简介
pycncapi是一个Python库,用以使用Python编程语言实现与维控科技旗下的MC系列运动控制器通讯。
✨-特性
- 支持通过以太网与MC控制器通讯
- 支持读取和设置控制器参数
- 支持发送运动指令
- 支持获取控制器状态信息
⏳版本
pycncapi目前发布v1.0.0版本
v1.0.0
- 完成控制类指令的开发,支持对控制的急停、使能控制
- 完成点动类指令的开发,支持对控制器的点动控制
- 完成运动类指令的开发,支持对控制器的运动控制
- 完成参数类指令的开发,支持对控制器的参数读写
- 完成状态类指令的开发,支持对控制器的状态读取
快速开始
📦安装
使用pip安装pycncapi:
Bashpip install pycncapi -i http://pypi.we-con.com.cn/simple --trusted-host pypi.we-con.com.cn
🔨使用
Pythonfrom pycncapi import CncCtrlApi
接口介绍
控制器连接
connect
- 参数: ip: str, port: int
- 返回值: bool
- 功能: 连接到控制器
disconnect
- 参数: 无
- 返回值: bool
- 功能: 断开与控制器的连接
控制器控制
estop
- 参数: state: bool
- 返回值: bool
- 功能: 设置控制器的急停状态
enable
- 参数: state: bool
- 返回值: bool
- 功能: 设置控制器的使能状态
setOpMode
- 参数: mode: int
- 返回值: bool
- 功能: 设置控制器的点动状态
- 参数值: 0: MANUAL,1: AUTO,2: MDI
运动控制
continuousJog
- 参数: axis: int, speed: float
- 返回值: bool
- 功能: 连续点动,控制关节按照指定速度运动。需要调用stopJog停止运动。
- 参数解释: axis: 关节号,speed: 速度
incrementJog
- 参数: axis: int, speed: float, distance: float
- 返回值: bool
- 返回值: bool
- 功能: 增量点动,控制关节按照指定速度运动指定距离。可以调用stopJog停止运动。
- 参数解释: axis: 关节号,speed: 速度, distance: 距离
stopJog
- 参数: axis: int
- 返回值: bool
- 功能: 停止点动
- 参数解释: axis: 关节号
executeMdi
- 参数: mdi: str
- 返回值: bool
- 功能: 执行MDI指令
- 参数解释: mdi: MDI指令
G代码
downloadGcode
- 参数: file_path: str
- 返回值: bool
- 功能: 下载G代码
- 参数解释: file_path: G代码文件路径
- 注意: 本接口将自动将g代码文件下载至控制器中,并且以原文件名存储,后续调用openProgram仅需传入文件名即可。
resetProgram
- 参数: 无
- 返回值: bool
- 功能: 复位G代码载入状态
openProgram
- 参数: file_name: str
- 返回值: bool
- 功能: 打开G代码
- 参数解释: file_name: G代码文件名。
Note
注意: 本接口仅需传入下载的G代码文件名即可。一定要是底层存在的文件名。
runProgram
- 参数: line: int
- 返回值: bool
- 功能: 运行G代码
- 参数解释: line: 指定行运行。
stopProgram
- 参数: 无
- 返回值: bool
- 功能: 停止G代码
pauseProgram
- 参数: 无
- 返回值: bool
- 功能: 暂停G代码
resumeProgram
stepProgram
- 参数: 无
- 返回值: bool
- 功能: 单步运行G代码。调用一次,执行一行。
状态获取
getMotionState
- 参数: 无
- 返回值: int
- 功能: 获取运动状态
- 返回值解释: 1: 急停,2: 急停复位,3: 未使能,4: 使能
getInterpState
- 参数: 无
- 返回值: bool
- 功能: 获取系统状态
- 返回值解释: 1: 空闲, 2: 加载文件中, 3: 暂停, 4: 等待
getOpMode
- 参数: 无
- 返回值: bool
- 功能: 获取操作模式
- 返回值解释: 0: MANUAL,1: AUTO,2: MDI
getCurCoordinate
- 参数: 无
- 返回值: list[float]
- 功能: 获取当前坐标
- 返回值解释: 返回一个列表,列表中包含当前坐标值。
getCurVel
- 参数: 无
- 返回值: float
- 功能: 获取当前速度
示例代码
连接控制器
Python |
---|
| from pycncapi import CncCtrlApi
api = CncCtrlApi()
if api.connect('192.168.1.100', 9995):
print('Connected to controller')
else:
print('Failed to connect to controller')
|
设置控制器状态
Python |
---|
| from pycncapi import CncCtrlApi
api = CncCtrlApi()
if api.connect('192.168.1.100', 9995):
if api.estop(False):
print('Estop reset')
if api.enable(True):
print('Enable set')
if api.setOpMode(1):
print('OpMode set')
else:
print('Failed to connect to controller')
|
控制Jog
以下示例将控制机器X轴按照100的速度运动100的距离
Python |
---|
| from pycncapi import CncCtrlApi
api = CncCtrlApi()
axis = 0
speed = 100
distance = 100
if api.connect('192.168.1.100', 9995):
if api.estop(False): # 急停复位
if api.enable(True): # 使能
# 控制点动
api.incrementJog(axis, speed, distance)
else:
print('Failed to connect to controller')
|
控制运行G代码
以下示例将控制机器打开机器内部名为test.ngc
的G代码并且运行
Python |
---|
| from pycncapi import CncCtrlApi
api = CncCtrlApi()
axis = 0
speed = 100
distance = 100
if api.connect('192.168.1.100', 9995):
if api.estop(False): # 急停复位
if api.enable(True): # 使能
# 打开指定G代码文件
if api.openProgram('test.ngc'):
api.runProgram() # 开始运行
else:
print('Failed to connect to controller')
|
从零开始设计一个简单的UI控制
这里以windows
上使用VSCode
作为ide开发举例,其余平台请类比开发。
安装pyqt5
使用PyQt5需要用到两个包:pyqt5和pyqt5-tools。如果想在原生环境中安装,那么在命令行中使用pip即可安装:
Bash |
---|
| pip install PyQt5
pip install PyQt5-tools
|
为VSCode配置扩展
在VSC中点击「Extensions 拓展」,搜索「PYQT Integration」并安装。

安装完成后,点击「PYQT Integration」的齿轮,选择「Extension Settings」,进入该拓展的设置页面。

在设置页面,我们指定一些PyQt工具的路径,以便于在VSC中启动这些工具,便捷地执行PyQt操作。
- 首先指定Pyuic的路径(前文说到,Pyuic是一个将我们设计的GUI界面的 .ui 文件转换为 .py 文件的工具),在「Pyqt-integration > Pyuic: Cmd」,输入安装PyQt5的虚拟环境中Pyuic的绝对路径:
比如我的电脑是:
Text OnlyD:\_A_1_Soft\Python\311\Scripts\pyuic5
- 指定QT designer的路径(前文说到,QT designer就是QT用来设计GUI的软件,通过拖拽组件可以快速构建GUI),在「Pyqt-integration > Qtdesigner: Path」,输入虚拟环境中QT designer的绝对路径:
比如我的电脑是:
Text OnlyD:\_A_1_Soft\Python\311\Lib\site-packages\qt5_applications\Qt\bin\designer

设计UI
在文件夹栏空白处点击右键,点击「PYQT: New Form」,即可启动Qt Designer软件,开始设计项目的GUI界面。

按照qt编程的方式,创建一个窗体设计

拖拽各种组件,设计项目的GUI。Qt Designer的使用方法不是本文的重点,不展开。

设计完成后,点击「文件 > 另存为」,把所设计的 .ui文件保存到项目目录下。保存完成后即可关闭Qt Designer。
生成工程文件
现在回到VSCode,可以看到项目目录下刚才所保存的xxx.ui
文件。文件里是类似html5的代码,我们不需要管。
在文件夹栏右击xxx.ui
文件,点击「PYQT: Compile Form」,即可(调用Pyuic工具)将xxx.ui
文件转换为Ui_xxx.py
文件。转换完成后,可以在目录看到新生成的Ui_xxx.py
文件。(复习一下:这个文件的命名格式可以按照上文的方法指定。)

成功后会生成文件并且右下角会有成功提示

编写主代码
新建一个main.py
编写调用代码
Python |
---|
| from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QTimer, QObject
import sys, os
import tkinter as tk
from tkinter import filedialog
from pycncapi import CncCtrlApi
import Ui_test
if __name__ == "__main__":
app = QApplication(sys.argv)
MainWindow = QMainWindow()
ui = Ui_test.Ui_Form()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
|
测试下
点击
即可测试是否正常,如果正常即可看到如下界面

继续实现
简单编写下所有控件的实现代码
cnc_gui.py |
---|
| import sys, os
import tkinter as tk
from tkinter import filedialog
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QTimer, QObject
import pycncapi
import Ui_test
class CncState:
def __init__(self):
self.estop = True
self.enable = False
self.ready = False
self.connected = False
self.mdiHistory = []
self.jogdir = 1
self.jogaxis = 0
cncState = CncState()
cncCtrl = pycncapi.CncCtrlApi()
def prepareGcode(success: bool):
if success:
ui.btnRun.setEnabled(True)
ui.btnStep.setEnabled(True)
else:
ui.btnRun.setEnabled(False)
ui.btnStop.setEnabled(False)
ui.btnPause.setEnabled(False)
ui.btnStep.setEnabled(False)
ui.btnResume.setEnabled(False)
def runGcode(run: bool):
if run:
ui.btnRun.setEnabled(False)
ui.btnStop.setEnabled(True)
ui.btnPause.setEnabled(True)
ui.btnStep.setEnabled(False)
ui.btnResume.setEnabled(False)
else:
ui.btnRun.setEnabled(True)
ui.btnStop.setEnabled(False)
ui.btnPause.setEnabled(False)
ui.btnStep.setEnabled(True)
def pauseGcode(pause: bool):
if pause:
ui.btnPause.setEnabled(False)
ui.btnResume.setEnabled(True)
else:
ui.btnPause.setEnabled(True)
ui.btnResume.setEnabled(False)
def btnConnect_clicked():
if not cncState.connected:
ip = ui.ipaddr.text()
port = int(ui.port.text())
if cncCtrl.connect(ip, port):
cncState.connected = True
ui.btnConnect.setText('Disconnect')
ui.btnConnect.setStyleSheet("background-color: rgb(38, 255, 38);")
else:
print(f'connection [{ip}:{port}] failed')
else:
cncCtrl.disconnect()
cncState.connected = False
ui.btnConnect.setText('Connect')
ui.btnConnect.setStyleSheet("")
def btnEstop_clicked():
if cncState.connected:
if cncState.estop:
print('reset estop')
cncCtrl.estop(False)
cncState.estop = False
ui.btnEnable.setEnabled(True)
ui.btnEstop.setStyleSheet("background-color: rgb(38, 255, 38);")
else:
print('estop')
cncCtrl.estop(True)
cncState.estop = True
cncState.enable = False
ui.btnEnable.setEnabled(False)
ui.btnEnable.setText("Enable")
ui.btnEnable.setStyleSheet("")
ui.status.setStyleSheet("border-radius:10px;background-color:red;")
ui.btnEstop.setStyleSheet("background-color: rgb(255, 106, 0);")
else:
print('CNC not connected')
def btnEnable_clicked():
if cncState.enable:
print('disable')
cncCtrl.enable(False)
cncState.enable = False
ui.btnEnable.setText("Enable")
ui.btnEnable.setStyleSheet("")
ui.status.setStyleSheet("border-radius:10px;background-color:red;")
else:
print('enable')
cncCtrl.enable(True)
cncState.enable = True
cncState.ready = True
ui.btnEnable.setText("Disable")
ui.btnEnable.setStyleSheet("background-color: rgb(38, 255, 38);")
ui.status.setStyleSheet("border-radius:10px;background-color:green;")
def btnExecMdi_clicked():
if cncState.ready and (ui.mdiCmd.text() != ''):
print('execute MDI ', ui.mdiCmd.text())
cncCtrl.executeMdi(ui.mdiCmd.text())
cncState.mdiHistory.append(ui.mdiCmd.text())
ui.mdiHistorys.setPlainText('\n'.join(cncState.mdiHistory))
ui.mdiCmd.clear()
else:
print('CNC not ready')
def btnPos_pressed():
if cncState.ready:
if ui.radioX.isChecked():
print('move x')
cncState.jogaxis = 0
elif ui.radioY.isChecked():
print('move y')
cncState.jogaxis = 1
elif ui.radioZ.isChecked():
print('move z')
cncState.jogaxis = 2
elif ui.radioA.isChecked():
print('move a')
cncState.jogaxis = 3
elif ui.radioC.isChecked():
print('move c')
cncState.jogaxis = 4
cncCtrl.continuousJog(cncState.jogaxis, 100)
else:
print('CNC not ready')
def btnPos_released():
if cncState.ready:
cncCtrl.stopJog(cncState.jogaxis)
else:
print('CNC not ready')
def btnNeg_pressed():
if cncState.ready:
if ui.radioX.isChecked():
print('move x')
cncState.jogaxis = 0
elif ui.radioY.isChecked():
print('move y')
cncState.jogaxis = 1
elif ui.radioZ.isChecked():
print('move z')
cncState.jogaxis = 2
elif ui.radioA.isChecked():
print('move a')
cncState.jogaxis = 3
elif ui.radioC.isChecked():
print('move c')
cncState.jogaxis = 4
cncCtrl.continuousJog(cncState.jogaxis, -100)
else:
print('CNC not ready')
def btnNeg_released():
if cncState.ready:
cncCtrl.stopJog(cncState.jogaxis)
else:
print('CNC not ready')
def openprogram_clicked():
if cncState.ready:
filetypes = [('GCode', '*.txt *.nc *.ngc'), ('All', '*.*')]
file_path = filedialog.askopenfilename(filetypes=filetypes)
if file_path:
file_name = os.path.basename(file_path)
print('open program ', file_path)
if cncCtrl.downloadGcode(file_path):
if cncCtrl.openProgram(file_name):
ui.labGcodeFile.setText(file_name)
prepareGcode(True)
else:
ui.labGcodeFile.setText(f'open gcode [{file_name}] failed!')
else:
print('download failed')
ui.labGcodeFile.setText(f'download [{file_name}] failed!')
else:
print('CNC not ready')
def runprogram_clicked():
if cncState.ready:
if cncCtrl.runProgram():
runGcode(True)
else:
print('run failed')
def stopprogram_clicked():
if cncState.ready:
if cncCtrl.stopProgram():
runGcode(False)
else:
print('stop failed')
class Worker(QObject):
def __init__(self, x, y, z, a, c, f):
super().__init__()
self.x = x
self.y = y
self.z = z
self.a = a
self.c = c
self.f = f
def updatePos(self):
if cncState.ready:
pos_list = cncCtrl.getCurCoordinate()
feed = cncCtrl.getCurVel()
self.x.setText(str(f'{pos_list[0]:.3f}'))
self.y.setText(str(f'{pos_list[1]:.3f}'))
self.z.setText(str(f'{pos_list[2]:.3f}'))
self.a.setText(str(f'{pos_list[3]:.3f}'))
self.c.setText(str(f'{pos_list[5]:.3f}'))
self.f.setText(str(f'{feed:.3f}'))
if __name__ == "__main__":
app = QApplication(sys.argv)
MainWindow = QMainWindow()
ui = Ui_test.Ui_Form()
ui.setupUi(MainWindow)
prepareGcode(False)
ui.btnConnect.clicked.connect(btnConnect_clicked)
ui.btnEnable.setEnabled(False)
ui.btnEstop.clicked.connect(btnEstop_clicked)
ui.btnEnable.clicked.connect(btnEnable_clicked)
ui.btnExecMdi.clicked.connect(btnExecMdi_clicked)
ui.btnPos.pressed.connect(btnPos_pressed)
ui.btnPos.released.connect(btnPos_released)
ui.btnNeg.pressed.connect(btnNeg_pressed)
ui.btnNeg.released.connect(btnNeg_released)
ui.btnOpen.clicked.connect(openprogram_clicked)
ui.btnRun.clicked.connect(runprogram_clicked)
ui.btnStop.clicked.connect(stopprogram_clicked)
worker = Worker(ui.pos_x, ui.pos_y, ui.pos_z, ui.pos_a, ui.pos_c, ui.feed)
timer = QTimer()
timer.timeout.connect(worker.updatePos)
timer.start(100)
MainWindow.show()
sys.exit(app.exec_())
|
show time
