【数控系统】第二章 LinuxCNC源码介绍
文件结构
LinuxCNC的主要代码结构如下所示。
src
    emc
        canterp(命令解释器)
        ini(操作的配置文件)
        kinematics(运动规划)
        motion(运动控制器)
        nml_intf(NML实现)
        rs274ngc(g代码解释器)
        task(任务控制器)
        tp(轨迹规划)
        usr_intf(GUI接口)
    hal(硬件抽象层)
    libnml(rcslib实现)canterp(命令解释器)
语法解析, 读取, 执行等, 对刀具信息、 主轴转速、 开始、 停止等信息进行判断和执行工作
ini(操作的配置文件)
- emcIniFile
- iniaxis (轴类型,轴单元,最大最小位置限值,误差限值,最大速度,加速度,加加速度等)
- inihal (定义HAL中的新的管脚,包括了各轴中的INI参数管脚,以及TRAJ中的INI参数管脚)
- inijoint
- inispindle
- initraj
kinematics(运动规划)
包含了不同的运动控制模块(五轴、 三轴、 六轴等等)、插补以及速度控制的轨迹规划文件
motion(运动控制器)
通过用户空间获取命令, 执行不同的动作, 这些命令结构体为 emcmotCommand->command, 而全部动作值都保存为 EMCMOT_COMMAND 在 motion.h 中。
nml_intf(NML实现)
对于 NML 消息机制传递和更新的定义
task(任务控制器)
emctask (EMC_TASK 类模式和状态管理, 主要是针对 G 代码和 M 代码)
emctaskmain (周期性的调用 emcTaskPlan() 和 emcTaskExecute())
emcTaskPlan:读取指令,根据模式和状态决定下一步动作,根据 EMC_TASK_STATE、EMC_TASK_MODE、EMC_TASK_INTERP 执行动作。
emcTaskExecute:等运动和IO状态,如果执行结束,获取下一个执行指令,根据 EMC_TASK_EXEC 执行状态停止、等待、获取下一个执行指令。
数据结构
cmd_code_t (motion.h):命令类型
EMCMOT_ABORT = 1,	/* abort all motion */
EMCMOT_ENABLE,		/* enable servos for active joints */
EMCMOT_DISABLE,		/* disable servos for active joints */
EMCMOT_PAUSE,		/* pause motion */
EMCMOT_RESUME,		/* resume motion */
……
cmd_status_t (motion.h):状态类型
EMCMOT_COMMAND_OK = 0,			/* cmd honored */
EMCMOT_COMMAND_UNKNOWN_COMMAND,	/* cmd not understood */
EMCMOT_COMMAND_INVALID_COMMAND,	/* cmd can't be handled now */
EMCMOT_COMMAND_INVALID_PARAMS,	/* bad cmd params */
EMCMOT_COMMAND_BAD_EXEC			/* error trying to initiate */
emc.hh:NML数据类型
// types for EMC_TASK mode
enum class EMC_TASK_MODE {
    MANUAL = 1,
    AUTO = 2,
    MDI = 3
};
// types for EMC_TASK state
enum class EMC_TASK_STATE {
    ESTOP = 1,
    ESTOP_RESET = 2,
    OFF = 3,
    ON = 4
};
// types for EMC_TASK interpState
enum class EMC_TASK_INTERP {
    IDLE = 1,
    READING = 2,
    PAUSED = 3,
    WAITING = 4
};
// types for EMC_TASK execState
enum class EMC_TASK_EXEC {
    ERROR = 1,
    DONE = 2,
    WAITING_FOR_MOTION = 3,
    WAITING_FOR_MOTION_QUEUE = 4,
    WAITING_FOR_IO = 5,
    WAITING_FOR_MOTION_AND_IO = 7,
    WAITING_FOR_DELAY = 8,
    WAITING_FOR_SYSTEM_CMD = 9,
    WAITING_FOR_SPINDLE_ORIENTED = 10
};任务模式
EMC_TASK_MODE
-  MANUAL 手动模式 直接通过控制面板上的按钮或旋钮来直接控制机床的动作。在这种模式下,操作者可以进行如移动轴、设置零点、测试刀具等基本操作。 
-  AUTO 自动模式 按照事先编写的加工程序自动完成一系列加工任务。只需要启动程序,机床就会按照既定的顺序和参数进行加工,直到整个加工过程结束。 
-  MDI 手动数据输入(Manual Data Input) 允许操作员直接通过控制面板上的键盘输入单个命令来控制机床的动作。这种模式主要用于执行简单的测试或调试任务,例如移动刀具到特定位置、检查机床状态或者进行短小的加工程序测试。 
任务状态
EMC_TASK_STATE
-  STOP 停止 
-  RESET 复位 
-  OFF 关闭 
-  ON 打开 
命令类型
EMC_TASK_INTERP
-  运动类型 -  直线 
-  圆弧 
-  快速定位 
-  螺旋线 
-  NURBS 
-  渐开线 
-  …… 
 
-  
-  参数设置 -  速度 
-  约束速度 
-  加速度 
-  主轴开 
-  主轴关 
-  轴位置约束(限位) 
-  轴速度约束 
-  轴加速度约束 
-  主轴转速比率 
-  进给速度比率 
-  快速进给速度比率 
-  …… 
 
-  
-  操作 -  停止 
-  电机使能 
-  电机失能 
-  开始 
-  暂停 
-  复位 
 
-  
业务逻辑
简单通俗地说,通过G代码解释器解析得到的指令和人机交互界面下发的指令构成指令队列,任务控制器通过判断允许任务模式、任务状态、命令状态对指令队列进行读取和执行。
g代码解析
rs274ngc_init() //初始化,读取rs274ngc.var和rs274ngc.tool_default文件,坐标,G模态组,M模态组以及其他G代码运行需要的状态量
rs274ngc_read() //读取G代码,然后进行语法和逻辑判断
rs274ngc_execute() //G代码执行src/emc/rs274ngc
setup _setup (interp_internal.cc)	//NC文件解析后的数据
struct setup (interp_internal.hh)
{	
    CANON_MOTION_MODE control_mode; // 快速移动、插补
    DISTANCE_MODE distance_mode;  	// 绝对式、增量式
    double feed_rate;				// 进给速度
    int motion_mode;              	// G-code模式
    double origin_offset_x;      	// g5x x轴偏移
    double origin_offset_y;       	// g5x y轴偏移
    double origin_offset_z;       	// g5x z轴偏移
    #define CONTROLLING_BLOCK(s) ((s).blocks[(s).remap_level])
	#define EXECUTING_BLOCK(s)   ((s).blocks[0])
}
(rs274ngc_interp.hh 声明)
class Interp : public InterpBase
    
(rs274ngc_pre.cc 实现)
//初始化setup
int Interp::init()
//打开NC文件
int Interp::open(const char *filename){
    _setup.file_pointer = fopen(filename, "r");
}
//将NC文件/指令转换成setup/代码,检查语法与逻辑
int Interp::_read(const char *command){
    block_pointer eblock = &EXECUTING_BLOCK(_setup);
    read_status = read_text(command,_setup.file_pointer,_setup.linetext,_setup.blocktext, &_setup.line_length);
    parse_line;
}
//
int Interp::_execute(const char *command){
    block_pointer eblock = &EXECUTING_BLOCK(_setup);
    if (_setup.line_length != 0) { 
        block_pointer cblock = &CONTROLLING_BLOCK(_setup);
        status = execute_block(cblock, &_setup);
        	write_canon_state_tag
                write_state_tag //将setup写到StateTag
        status = read(0);  // int Interp::_read(const char *command)
        write_g_codes(eblock, &_setup);
        write_m_codes(eblock, &_setup);
        write_settings(&_setup);
    }
}     canon两种实现,一个是emccanon,一个是saicanon。
src/emc/nml_intf/canon.hh
src/emc/task/emccanon
src/emc/sai/saicanon
NML通信
NML是一个代码开源的可用于多进程通信的库,采用共享内存和管道结合的方式以C++代码实现的一个通信库。NML通信库具有的特性:
1、通过一个ACSII文本配置文件对通信进行配置,仅配置通信参数不需要修改程序。
2、拥有灵活的通信实现方式,队列,一对多,收发状态同步返回等。
3、远程进程可以实现像访问本地共享内存一样,实现和远程进程的数据通讯。
update() //用于将数据传输到共享内存中,是通信数据必须实现的方法。
MyMsgFormat() //通信的格式函数,这个函数在创建NML Msg对象的构造函数必须传入。发布指令
emcTaskPlan:读取指令,根据模式和状态决定下一步动作,根据 EMC_TASK_STATE、EMC_TASK_MODE、EMC_TASK_INTERP 执行动作。
以下代码只截取自动挡模式介绍。
static RCS_CMD_CHANNEL *emcCommandBuffer = 0; //指令缓存区
static RCS_CMD_MSG *emcCommand = 0;	//指令指针
NML_INTERP_LIST interp_list; (interpl.cc)
emcTaskPlan(emctaskmain.cc)
    case EMC_TASK_STATE::ON: //电机使能
		case EMC_TASK_MODE::AUTO: //自动挡
			case EMC_TASK_PLAN_EXECUTE_TYPE:
				emcTaskIssueCommand(emcCommand);
					case EMC_TRAJ_LINEAR_MOVE_TYPE: //直线
						emcTrajLinearMove
                            usrmotWriteEmcmotCommand(emcmot_command_t * c)
                    case EMC_TASK_PLAN_RUN_TYPE:
						emcStatus->task.interpState = EMC_TASK_INTERP::READING;
					case EMC_TASK_PLAN_INIT_TYPE: //初始化,读取NC文件
            			emcTaskPlanInit(emctask.cc) 
                    case EMC_TASK_PLAN_EXECUTE_TYPE:
						emcStatus->task.interpState = EMC_TASK_INTERP::READING;		
						emcTaskPlanExecute
			case EMC_TASK_INTERP::READING: //预读
				readahead_reading //解析NC文件
                    emcTaskPlanCommand((char *) &emcStatus->task.command);
                        strcpy(cmd, interp.command(buf, LINELEN));指令状态
emcTaskExecute:等运动和IO状态,如果执行结束,获取下一个执行指令,根据 EMC_TASK_EXEC 执行状态停止、等待、获取下一个执行指令。
static RCS_STAT_CHANNEL *emcStatusBuffer = 0; //状态缓存区
EMC_STAT *emcStatus = 0;
emcTaskCommand; //存储待发送的命令消息
    
emcTaskExecute(emctaskmain.cc)
    case EMC_TASK_EXEC::ERROR://错误
    case EMC_TASK_EXEC::DONE: //执行完成
		if(不存在 emcTaskCommand) 
            emcTaskCommand = interp_list.get(); //获取新的指令
			if(emcStatus->motion.traj.queueFull 队列满了)
                emcStatus->task.execState = EMC_TASK_EXEC::WAITING_FOR_MOTION_QUEUE;
            else
            	emcTaskCheckPreconditions
    	else 
            emcTaskCheckPostconditions
	case EMC_TASK_EXEC::WAITING_FOR_MOTION_QUEUE://等待
	case EMC_TASK_EXEC::WAITING_FOR_MOTION:
	case EMC_TASK_EXEC::WAITING_FOR_IO:
	case EMC_TASK_EXEC::WAITING_FOR_MOTION_AND_IO:参考资料
《LinuxCNC开放式 数控平台系统分析及应用》史步海 丁川 著
LINUXCNC源程序概略笔记
LinuxCNC学习(一)RS274NGC的使用
LinuxCNC学习(二)RS274NGC的源码分析
LinuxCNC学习(四)NML的学习
