CODESYS 输出日志 Log

目录

1.库SysFile

2.定义声明

3.日志输出实现


1.库SysFile

2.定义声明

定义文件个数,文件行数限制 以及触发变量,每次触发则写一行数据

FUNCTION_BLOCK LogOutput
VAR_INPUT
	//输入文件个数 默认是5 不超过100个
	iFileNum           : INT := 100;
	//路径以及文件名,不含后缀
	sFileName	       : STRING(255) := 'Info';  
	//文本行数  默认是50000 ,不超过50000个
	NumberTextLines    : INT := 50000;
	//上升沿生效
	bEnable            : BOOL;
	//文件操作//写一行数据
	bWriteLine	       : BOOL := FALSE;
	//最新的输出日志文本行     
	logLineText      : STRING;
END_VAR
VAR_OUTPUT
	//创建一定个数的文件完成标志
   	bCreateFileDone    : BOOL;
	//写当前文件数据完成标志
	bWriteFileDone     : BOOL;
END_VAR
VAR
	sFileFolder       : STRING(255) := 'D:\Logs\'; 
    //文件后缀
	suffix             : STRING(255) := '.log';
    //当前文件名路径
    sFileNameMid	   : STRING(255) := '';  
	//字符串数组库
	asCurrentFileName  : ARRAY[1..100] OF STRING(255) ;  
    abFileSingle	   : ARRAY[1..100] OF BOOL ; 
	//文件操作
	iFilesStatus       : INT;
	iWriteDelay        : INT;
	//当前文件序号
	iCurrentFileNum    : INT := 1;
	//当前文件行
	iCurrentTextline   : INT := 0;
	//计数器
	iCounter           : INT;
	//计算器2
	iCounter2          : INT;
	//当前文件计算
	iCurrentCounterFile: INT := 1;
	//创建文件个数标志位
	//iCreateFileNum     : INT;
	//delete 计算器
	iDeleteCounter     : INT;
	//复位文件返回字节数
	lwCopyWord         : LWORD;
	//时间处理
	sLine			   : STRING(255);
	sTime		       : STRING;
    sTimeT1            : STRING;
	//边沿检测
    EnableR_TRTG       : R_TRIG; 
	WriteFilesR_TRIG   : R_TRIG; 
	//Test
    lrCounterDataTest  : LREAL;
	//判断当前文件延时
    iDelayHasFile      : INT;
	Total_Num: INT;
END_VAR

3.日志输出实现

定义文件操作

{attribute 'qualified_only'}
{attribute 'strict'}
TYPE FilesOpStatus :
(
	InitFile := 0,          //初始化文件
	Hasfiles := 10,         //检查文件是否存在
	CreateFiles   := 20,    //创建文件
	ReadFilesData := 30,    //读取文件数据
	WriteFilesData:= 40,    //写入文件数据
	WriteFileReady:= 50,    //写文件准备状态
    CreateFilesError:= 100  //创建文件失败
);
END_TYPE

日志内容输出,可以自行定义日志输出的样式

字符串拼装的方式

//目录拼接当前日期
sFileFolder:=concat('D:\Logs\',LEFT(GetTimeS(),10));
sFileFolder:=concat(sFileFolder,'\');
//启动信号
EnableR_TRTG(CLK:= bEnable, Q=> );
IF EnableR_TRTG.Q THEN
	iFilesStatus := FilesOpStatus.InitFile;
END_IF

//限制
IF iFileNum > 100 THEN
	iFileNum := 100;
END_IF

IF NumberTextLines > 50000 THEN
	NumberTextLines := 50000;
END_IF

IF NOT(bEnable) THEN
	RETURN;
END_IF

//文件操作状态机
CASE iFilesStatus   OF 
	FilesOpStatus.InitFile: //初始化文件
	bCreateFileDone := FALSE;
	iCurrentCounterFile := 1;
				
	//当前文件标志位复位
	FOR iCounter := 1 TO iFileNum BY 1 DO
		abFileSingle[iCounter] := FALSE;
	END_FOR

    iFilesStatus := FilesOpStatus.Hasfiles;
	FilesOpStatus.Hasfiles:
	//文件都存在的情况下
	IF bCreateFileDone THEN
		//删除最早生产的文件
		DeleteFile(asCurrentFileName[1]);
						
		//依次类推,将最新的往前移
		FOR iDeleteCounter := 1 TO (iFileNum - 1) BY 1 DO
			CopyFile(asCurrentFileName[iDeleteCounter],asCurrentFileName[iDeleteCounter + 1],ADR(lwCopyWord));
			//删除
			DeleteFile(asCurrentFileName[iDeleteCounter + 1]);
		END_FOR
				
		 //最后一个默认是最新的文件
		iCurrentCounterFile := iFileNum;
		//字符串处理---最后一个文件为最新的文件
		//目录拼接 
		asCurrentFileName[iCurrentCounterFile] := concat(sFileFolder, sFileName);
		//文件数拼接
		asCurrentFileName[iCurrentCounterFile] := concat(asCurrentFileName[iCurrentCounterFile], INT_TO_STRING(iCurrentCounterFile));
		//后缀拼接
		asCurrentFileName[iCurrentCounterFile] := concat(asCurrentFileName[iCurrentCounterFile],suffix);
					
		//当前文件
		sFileNameMid := asCurrentFileName[iCurrentCounterFile];
		//下一状态机
		iFilesStatus := FilesOpStatus.CreateFiles;
	ELSE
		//字符串处理---最后一个文件为最新的文件
		//目录拼接 
		asCurrentFileName[iCurrentCounterFile] := concat(sFileFolder, sFileName);
		//文件数拼接
		asCurrentFileName[iCurrentCounterFile] := concat(asCurrentFileName[iCurrentCounterFile], INT_TO_STRING(iCurrentCounterFile));			
		//后缀拼接
		asCurrentFileName[iCurrentCounterFile] := concat(asCurrentFileName[iCurrentCounterFile],suffix);		
		//当前文件
		sFileNameMid := asCurrentFileName[iCurrentCounterFile];
		 //当前文件是否存在
		IF HasFile(asCurrentFileName[iCurrentCounterFile]) THEN
			//文件是否存在标志
			IF NOT(abFileSingle[iCurrentCounterFile]) THEN
				abFileSingle[iCurrentCounterFile] := TRUE;
			END_IF
		END_IF
		//延时判断
		iDelayHasFile := iDelayHasFile + 1;
		IF  iDelayHasFile > 15 THEN
			iDelayHasFile := 0;
			IF (abFileSingle[iCurrentCounterFile]) THEN
				iCurrentCounterFile := iCurrentCounterFile + 1;  
				//创建文件完成
				IF iCurrentCounterFile = (iFileNum + 1)  THEN
					bCreateFileDone := TRUE;
				END_IF
			ELSE
				iFilesStatus := FilesOpStatus.CreateFiles;				
			END_IF
		END_IF
	END_IF
    FilesOpStatus.CreateFiles:    //创建文件,并写入相关标题
	//标题六列名称
	sLine := '开始记录日志$r$n';//尾部加换行符
				 
	//创建文件及标题,已经有文件则覆盖
	WriteFile(sFileName:= sFileNameMid, pbyBuffer:= ADR(sLine), ulSize:= len(sLine), WriteMode:= EWriteMode.eWriteCover);
	            
	//延时等待标题写完成
	iWriteDelay := iWriteDelay + 1;
	IF iWriteDelay > 10 THEN
		iWriteDelay := 0;
		iFilesStatus := FilesOpStatus.ReadFilesData;
	END_IF
	FilesOpStatus.ReadFilesData:  //读取文件数据
	//延时计数
	iFilesStatus := FilesOpStatus.WriteFileReady;
	//获取当前文件的行数
	iCurrentTextline :=  0;
	FilesOpStatus.WriteFilesData: //写入文件数据
	//write data
	//'Date,  Total_Num,  OK_Num,  NG_Num,  ErrorScrewID, ErrorID$r$n' 					 //复位
	//获取时间标题
	sTime := GetTimeS();
					 
	sLine := '[';
	sLine := concat(sLine , sTime);
	sLine := concat(sLine , ']');
	//日志行信息
	sLine := concat(sLine,logLineText);					
	//结束符号
	sLine := concat(sLine , '$r$n');
					 
	//向文件尾部写数据
	WriteFile(sFileName:= sFileNameMid, pbyBuffer:= ADR(sLine), ulSize:= len(sLine), WriteMode:= EWriteMode.eWriteAdd);
					
	iCurrentTextline := iCurrentTextline + 1 ; 
	//文件内容填充完成
	IF iCurrentTextline >= NumberTextLines THEN
		iFilesStatus := FilesOpStatus.Hasfiles;
	ELSE
		iFilesStatus := FilesOpStatus.WriteFileReady;
	END_IF
					 
	//写文件内容完成
	bWriteFileDone := TRUE;
	FilesOpStatus.WriteFileReady:
	WriteFilesR_TRIG(CLK:= bWriteLine, Q=> );
	//启动
	IF  WriteFilesR_TRIG.Q THEN 
		//写文件内容完成
		bWriteFileDone := FALSE;
		iFilesStatus := FilesOpStatus.WriteFilesData;
	END_IF         
	FilesOpStatus.CreateFilesError: //写入文件数据
END_CASE

<think>好的,我现在需要处理用户关于CODESYS消息显示的方法和错误处理的查询。首先,我得回忆一下CODESYS的消息处理机制。用户可能指的是如何在运行时显示消息,比如调试信息或错误提示。根据引用中的信息,用户可能遇到变量显示错误的问题,比如显示为???,这可能与数据类型或读取方式有关[^1][^2]。 首先,我应该考虑CODESYS中常见的消息显示方法,比如使用日志功能、弹出窗口或HMI元素。用户可能需要知道如何通过代码生成消息,并确保它们正确显示。另外,错误处理方面,比如如何处理消息无法显示或格式错误的情况,也需要涵盖。例如,引用中提到Bool类型在CODESYS中定义为1个字节,可能需要特殊处理来正确读取二进制位,这可能影响消息中的变量显示[^2]。 接下来,我需要整理步骤,确保覆盖消息显示的基本方法和常见错误。例如,使用SysMessage库来记录消息,或者通过HMI控件绑定变量。错误处理部分要包括数据类型不匹配、作用域问题以及在线调试时的显示错误,如变量显示为???,这可能是因为变量未被正确上传到调试器[^1]。 还要考虑用户可能的后续问题,比如消息持久化、多语言支持或性能影响。根据引用,CODESYS Automation Server Connector用于项目同步和边缘网关配置,这可能与消息的远程显示有关[^3],但用户当前问题可能更基础。 最后,生成相关问题时要围绕消息显示的方法、错误类型、调试技巧和优化策略,确保覆盖用户可能感兴趣的扩展内容。需要检查引用内容是否适用,并正确标注来源。</think>### CODESYS消息显示方法与错误处理 #### 一、消息显示方法 1. **系统日志功能** 使用$SysMessage$库记录运行时消息,消息会显示在CODESYS开发环境的**Message View**或设备日志中。例如: ```pascal SysMessage.LogMessage('传感器数据异常: ' + REAL_TO_STRING(sensorValue), MT_INFO); ``` 支持消息级别(MT_INFO/MT_WARNING/MT_ERROR)[^3]。 2. **HMI控件绑定** 在可视化界面中添加$TextList$或$MessageBox$控件,通过变量绑定实时显示消息: ```pascal hmiMessageBox.Text := '电机过热,温度:' + INT_TO_STRING(temp); ``` 3. **调试输出** 使用$TRACE$指令在在线调试时输出变量值到**Watch窗口**: ```pascal TRACE('当前计数器值:%d', counter); ``` #### 二、常见错误与处理 1. **消息不显示** - **作用域问题**:确保消息生成代码在周期调用的任务中执行 - **过滤设置**:检查**Message View**的过滤条件是否屏蔽了当前消息级别 2. **变量显示异常** - **数据类型错误**:如BOOL变量需按字节读取二进制位 ```pascal bFlag: BYTE; // 定义字节变量 flag1 := (bFlag AND 16#01) <> 0; // 读取第0位 ``` - **符号未上传**:在线调试时变量显示为`???`,需在工程设置中勾选**Upload symbols**选项[^1] 3. **格式化错误** 使用类型转换函数避免字符串拼接错误: ```pascal // 错误示例 SysMessage.LogMessage('坐标值:' + coord); // 直接拼接REAL类型会报错 // 正确示例 SysMessage.LogMessage('坐标值:' + REAL_TO_STRING(coord)); ``` #### 三、调试技巧 1. 通过**Cross Reference**(Ctrl+Shift+F12)查找消息调用位置 2. 在**Device Logger**中配置消息持久化存储路径 3. 使用$IFDEF DEBUG$条件编译调试消息: ```pascal {$IFDEF DEBUG} SysMessage.LogMessage('调试信息'); {$ENDIF} ```
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rotion_深

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值