Skip to content

MC200编程手册(Python)

简介

pycncapi是一个Python库,用以使用Python编程语言实现与维控科技旗下的MC系列运动控制器通讯。

✨-特性

  • 支持通过以太网与MC控制器通讯
  • 支持读取和设置控制器参数
  • 支持发送运动指令
  • 支持获取控制器状态信息

⏳版本

pycncapi目前发布v1.0.0版本

v1.0.0

  • 完成控制类指令的开发,支持对控制的急停、使能控制
  • 完成点动类指令的开发,支持对控制器的点动控制
  • 完成运动类指令的开发,支持对控制器的运动控制
  • 完成参数类指令的开发,支持对控制器的参数读写
  • 完成状态类指令的开发,支持对控制器的状态读取

快速开始

📦安装

使用pip安装pycncapi:

Bash
pip install pycncapi -i http://pypi.we-con.com.cn/simple --trusted-host pypi.we-con.com.cn

🔨使用

Python
from 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

  • 参数: 无
  • 返回值: bool

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
1
2
3
4
5
6
7
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」并安装。

vscode

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

在设置页面,我们指定一些PyQt工具的路径,以便于在VSC中启动这些工具,便捷地执行PyQt操作。

  1. 首先指定Pyuic的路径(前文说到,Pyuic是一个将我们设计的GUI界面的 .ui 文件转换为 .py 文件的工具),在「Pyqt-integration > Pyuic: Cmd」,输入安装PyQt5的虚拟环境中Pyuic的绝对路径:

比如我的电脑是:

Text Only
D:\_A_1_Soft\Python\311\Scripts\pyuic5
  1. 指定QT designer的路径(前文说到,QT designer就是QT用来设计GUI的软件,通过拖拽组件可以快速构建GUI),在「Pyqt-integration > Qtdesigner: Path」,输入虚拟环境中QT designer的绝对路径:

比如我的电脑是:

Text Only
D:\_A_1_Soft\Python\311\Lib\site-packages\qt5_applications\Qt\bin\designer

设计UI

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

创建一个widget

按照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