当前位置: 首页 > article >正文

智能控制基础应用-C#Codesys共享内存实现数据高速交互

智能控制基础应用-C#&Codesys共享内存实现数据高速交互

文章目录

  • 智能控制基础应用-C#&Codesys共享内存实现数据高速交互
  • 前言
  • 一、 Codesys共享内存程序实现
  • 二、Python共享内存程序实现
    • 2.1 界面说明
  • 三、功能测试
  • 注意事项


前言

共享内存是一种高效的进程间通信(IPC)机制,允许不同的进程访问同一块内存区域。共享内存本质就是建立一块供协作进程共享的内存区域,多个进程可以透过此共享区域读取或者写入数据来交换信息;
资源下载连接

一、 Codesys共享内存程序实现

1.1 库管理
1 查找库文件的名称
项目中需要使用到共享内存相关的函数,在不清楚这些函数在那个库文件下的情况下,可以通过在库管理工具中进行查找,通过【工具】-【库】,可以打开对应的库文件管理工具。
在这里插入图片描述1.2 查找
在库文件管理工具中,点击查找按钮,可以实现对需要引用的函数或者库的查找,点击查找后,将出现1.3所示。
在这里插入图片描述
1.3 函数或库搜索
在查找库的搜索栏内,输入需要应用的函数或者库的名称,比如在本项目中,我们需要用sharedMemory相关的函数,我们直接键入sharedMemory,将出现如下内容,这表明,我们需要用的函数,在SysShm库中存在
在这里插入图片描述
1.4 Codesys的库管理器
在项目管理界面,双击库管理器工具,系统将打开库管理器。
在这里插入图片描述1.5 导入库
在库管理器中,点击添加按钮,将打开添加库的界面,在此界面中,输入我们需要的库的关键字,比如sysshm,将过滤相关的内容,选中后,点击确认即可完成导入。
在这里插入图片描述
1.2 添加数据结构

在【应用】选项卡点击右键,选择【添加对象】,选择添加数据块[DUT]
在这里插入图片描述
选择数据的【类型】为结构-Structure类型,修改名称为str_SharedIn和str_SharedOut点击打开即可。
在这里插入图片描述
定义的结构体数据中,增加数据结构声明如下:
共享输入的内存,str_SharedIn的声明:

TYPE str_SharedIn :
STRUCT
	bIN:BOOL; //布尔数据
	iIN:DINT; //整型数据
	fIN:LREAL; //浮点数据
END_STRUCT
END_TYPE

共享的输出内存,str_SharedIn的声明:

TYPE str_SharedOut :
STRUCT
	bIN:BOOL; //布尔数据
	iIN:DINT; //整型数据
	fIN:LREAL; //浮点数据
END_STRUCT
END_TYPE

全局变量定义
在【应用】上点击右键,选择【添加对象】,添加【全局变量列表】,即可实现添加全部变量
在这里插入图片描述需要添加的全局变量为写入的参数和读取的参数。具体定义如下:

VAR_GLOBAL
	GetPara: Str_ParaFromHMI;
	SetPara: Str_ParaToHMI;
END_VAR

1.2 函数实现
在函数中,对变量格式有特殊定义,因此函数中部分变量的定义如下:

PROGRAM POU_1
VAR
	bStart: BOOL:= FALSE;
	ReadHandle: RTS_IEC_HANDLE:= RTS_INVALID_HANDLE;
	WriteHandle: RTS_IEC_HANDLE:= RTS_INVALID_HANDLE;
	ulPhysicalAddressRead: __UXINT:= 0;
	ulPhysicalAddressWrite: __UXINT:= 0;
	ulSizeRead: __UXINT:= 1024;
	ulSizeWrite: __UXINT:= 1024;
	ResultRead: ARRAY[0..2] OF RTS_IEC_RESULT;		//返回运行错误码
	ResultWrite: ARRAY[0..2] OF RTS_IEC_RESULT;
	
	SMRead: __UXINT;
	SMWrite: __UXINT;
	ulOffsetRead: __UXINT:= 0;
	ulOffsetWrite: __UXINT:= 0;
	
	shNameRead: STRING:= 'CODESYS_MEMORY_READ';		//声明共享内存的读取内存名称
	shNameWrite: STRING:= 'CODESYS_MEMORY_WRITE';	//声明共享内存的写入内存名称

END_VAR

主函数

//内存初始化
IF NOT bStart THEN
	ReadHandle:= SysSharedMemoryCreate(pszName:= shNameRead, ulPhysicalAddress:= ulPhysicalAddressRead, pulSize:= ADR(ulSizeRead), pResult:= ADR(ResultRead[0]));
	WriteHandle:= SysSharedMemoryCreate(pszName:= shNameWrite, ulPhysicalAddress:= ulPhysicalAddressWrite, pulSize:= ADR(ulSizeWrite), pResult:= ADR(ResultWrite[0]));
	IF RTS_INVALID_HANDLE <> ReadHandle AND RTS_INVALID_HANDLE <> WriteHandle THEN
		bStart:= TRUE;
	END_IF
END_IF
 
//读取数据
IF RTS_INVALID_HANDLE <> ReadHandle THEN
	SMRead:= SysSharedMemoryRead(
	hShm:= ReadHandle, 					//读取内存的设备句柄
	ulOffset:= ulOffsetRead,			//读取数据的偏移地址 
	pbyData:= ADR(Glob_VAR.GetPara), 		//指向读取数据的缓冲区
	ulSize:= SIZEOF(str_SharedIn), 	//读取数据的字节大小	
	pResult:= ADR(ResultRead[1]));		//返回执行的错误码
END_IF
 
//写入数据
IF RTS_INVALID_HANDLE <> WriteHandle THEN
	SMWrite:= SysSharedMemoryWrite(
	hShm:= WriteHandle, 				//写入内存的设备句柄
	ulOffset:= ulOffsetWrite, 			//写入数据的偏移地址
	pbyData:= ADR(Glob_VAR.SetPara), 		//指向写入数据的缓冲区
	ulSize:= SIZEOF(str_SharedOut), 	//写入数据的字节大小
	pResult:= ADR(ResultWrite[2]));		//返回执行的错误码
END_IF

任务配置
在项目管理界面,选择【任务配置】-【Task】,系统将弹出【Task】的管理界面
在这里插入图片描述
在【Task】管理界面,点击【添加调用】,选择正确的POU程序块,系统即实现runtime和应用程序调用的关联
在这里插入图片描述

二、Python共享内存程序实现

2.1 界面说明

此项目中规划的设计界面如下,需要实现两个内容:

  1. 读取共享内存的数据并在界面中显示
  2. 将界面中设置的共享内存数据写入到codesys
    在这里插入图片描述
    界面设计函数如下:
 /// <summary>
 ///  Required method for Designer support - do not modify
 ///  the contents of this method with the code editor.
 /// </summary>
 private void InitializeComponent()
 {
     textbox_iRead = new TextBox();
     picbox_bWrite = new Label();
     textbox_fRead = new TextBox();
     iRead = new Label();
     fRead = new Label();
     picbox_bRead = new Label();
     label1 = new Label();
     label2 = new Label();
     textbox_iWrite = new TextBox();
     textbox_fWrite = new TextBox();
     btnWrite = new Button();
     label3 = new Label();
     bWrite = new Label();
     ReadFromCodesys = new Label();
     WriteToCodesys = new Label();
     SuspendLayout();
     // 
     // textbox_iRead
     // 
     textbox_iRead.Location = new Point(75, 70);
     textbox_iRead.Name = "textbox_iRead";
     textbox_iRead.Size = new Size(100, 23);
     textbox_iRead.TabIndex = 0;
     // 
     // picbox_bWrite
     // 
     picbox_bWrite.AutoSize = true;
     picbox_bWrite.BackColor = SystemColors.ButtonShadow;
     picbox_bWrite.ForeColor = SystemColors.ControlDark;
     picbox_bWrite.Location = new Point(284, 37);
     picbox_bWrite.Name = "picbox_bWrite";
     picbox_bWrite.Size = new Size(44, 17);
     picbox_bWrite.TabIndex = 1;
     picbox_bWrite.Text = "         ";
     // 
     // textbox_fRead
     // 
     textbox_fRead.Location = new Point(75, 109);
     textbox_fRead.Name = "textbox_fRead";
     textbox_fRead.Size = new Size(100, 23);
     textbox_fRead.TabIndex = 2;
     // 
     // iRead
     // 
     iRead.AutoSize = true;
     iRead.Location = new Point(26, 65);
     iRead.Name = "iRead";
     iRead.Size = new Size(41, 17);
     iRead.TabIndex = 3;
     iRead.Text = "iRead";
     // 
     // fRead
     // 
     fRead.AutoSize = true;
     fRead.Location = new Point(26, 109);
     fRead.Name = "fRead";
     fRead.Size = new Size(42, 17);
     fRead.TabIndex = 3;
     fRead.Text = "fRead";
     // 
     // picbox_bRead
     // 
     picbox_bRead.AutoSize = true;
     picbox_bRead.BackColor = SystemColors.ButtonShadow;
     picbox_bRead.Location = new Point(75, 37);
     picbox_bRead.Name = "picbox_bRead";
     picbox_bRead.Size = new Size(40, 17);
     picbox_bRead.TabIndex = 1;
     picbox_bRead.Text = "        ";
     // 
     // label1
     // 
     label1.AutoSize = true;
     label1.Location = new Point(226, 65);
     label1.Name = "label1";
     label1.Size = new Size(42, 17);
     label1.TabIndex = 3;
     label1.Text = "iWrite";
     // 
     // label2
     // 
     label2.AutoSize = true;
     label2.Location = new Point(225, 109);
     label2.Name = "label2";
     label2.Size = new Size(43, 17);
     label2.TabIndex = 3;
     label2.Text = "fWrite";
     // 
     // textbox_iWrite
     // 
     textbox_iWrite.Location = new Point(284, 70);
     textbox_iWrite.Name = "textbox_iWrite";
     textbox_iWrite.Size = new Size(100, 23);
     textbox_iWrite.TabIndex = 0;
     textbox_iWrite.Text = "0";
     // 
     // textbox_fWrite
     // 
     textbox_fWrite.Location = new Point(284, 109);
     textbox_fWrite.Name = "textbox_fWrite";
     textbox_fWrite.Size = new Size(100, 23);
     textbox_fWrite.TabIndex = 2;
     textbox_fWrite.Text = "0";
     // 
     // btnWrite
     // 
     btnWrite.Location = new Point(284, 141);
     btnWrite.Name = "btnWrite";
     btnWrite.Size = new Size(100, 23);
     btnWrite.TabIndex = 4;
     btnWrite.Text = "Write2Mem";
     btnWrite.UseVisualStyleBackColor = true;
     btnWrite.Click += btnWrite_Click;
     // 
     // label3
     // 
     label3.AutoSize = true;
     label3.Location = new Point(26, 37);
     label3.Name = "label3";
     label3.Size = new Size(46, 17);
     label3.TabIndex = 3;
     label3.Text = "bRead";
     // 
     // bWrite
     // 
     bWrite.AutoSize = true;
     bWrite.Location = new Point(225, 37);
     bWrite.Name = "bWrite";
     bWrite.Size = new Size(47, 17);
     bWrite.TabIndex = 3;
     bWrite.Text = "bWrite";
     // 
     // ReadFromCodesys
     // 
     ReadFromCodesys.AutoSize = true;
     ReadFromCodesys.Location = new Point(26, 9);
     ReadFromCodesys.Name = "ReadFromCodesys";
     ReadFromCodesys.Size = new Size(125, 17);
     ReadFromCodesys.TabIndex = 3;
     ReadFromCodesys.Text = "Read From Codesys";
     // 
     // WriteToCodesys
     // 
     WriteToCodesys.AutoSize = true;
     WriteToCodesys.Location = new Point(226, 9);
     WriteToCodesys.Name = "WriteToCodesys";
     WriteToCodesys.Size = new Size(111, 17);
     WriteToCodesys.TabIndex = 3;
     WriteToCodesys.Text = "Write To Codesys";
     // 
     // Form1
     // 
     AutoScaleDimensions = new SizeF(7F, 17F);
     AutoScaleMode = AutoScaleMode.Font;
     ClientSize = new Size(401, 176);
     Controls.Add(btnWrite);
     Controls.Add(label2);
     Controls.Add(fRead);
     Controls.Add(bWrite);
     Controls.Add(label1);
     Controls.Add(WriteToCodesys);
     Controls.Add(ReadFromCodesys);
     Controls.Add(label3);
     Controls.Add(iRead);
     Controls.Add(textbox_fWrite);
     Controls.Add(textbox_fRead);
     Controls.Add(picbox_bRead);
     Controls.Add(picbox_bWrite);
     Controls.Add(textbox_iWrite);
     Controls.Add(textbox_iRead);
     Name = "Form1";
     Text = "C# & Codesys Shared Memory";
     Load += Form1_Load;
     ResumeLayout(false);
     PerformLayout();
 }

 #endregion

 private TextBox textbox_iRead;
 private Label picbox_bWrite;
 private TextBox textbox_fRead;
 private Label iRead;
 private Label fRead;
 private Label picbox_bRead;
 private Label label1;
 private Label label2;
 private TextBox textbox_iWrite;
 private TextBox textbox_fWrite;
 private Button btnWrite;
 private Label label3;
 private Label bWrite;
 private Label ReadFromCodesys;
 private Label WriteToCodesys;

2.2 库函数声明
本项目中需要使用系统自带的MenoryMappedFiles来实现对共享内存空间的访问,同时,为了实时数据读取,需要使用多线程实现。

using System.IO.MemoryMappedFiles;      //引用共享内存命名空间
using System.Threading;
using System.Runtime.InteropServices;

2.3 结构体定义
需要保证C#设置的结构体和codesys中设置的结构体一致。
定义了两个结构体,一个结构体读取codesys值,一个结构体从codesys中获取实际数据。

 struct StrFromCodesys
 {
     internal bool bOut;
     internal int iOut;
     internal double fOut;
 }
 struct StrToCodesys
 {
     internal bool bIn;
     internal int iIn;
     internal double fIn;
 }

2.4 实例化变量
项目需要实例化的内容如下所示:

MemoryMappedFile MMF_Write, MMF_Read;
MemoryMappedViewAccessor AccessorWrite, AccessorRead;
StrFromCodesys ParaFromCodesys;
StrToCodesys ParaToCodesys;

2.5 函数初始化

初始化函数如下,注意其中的变量名需要要Codesys的一致,如“Global\CODESYS_MEMORY_READ”。

private void InitMemory()
 {
     try
     {
         MMF_Write = MemoryMappedFile.OpenExisting("Global\\" + "CODESYS_MEMORY_READ", MemoryMappedFileRights.ReadWrite);
         AccessorWrite = MMF_Write.CreateViewAccessor(0, Marshal.SizeOf(typeof(StrToCodesys)), MemoryMappedFileAccess.ReadWrite);

         MMF_Read = MemoryMappedFile.OpenExisting("Global\\" + "CODESYS_MEMORY_WRITE", MemoryMappedFileRights.ReadWrite);
         AccessorRead = MMF_Read.CreateViewAccessor(0, Marshal.SizeOf(typeof(StrFromCodesys)), MemoryMappedFileAccess.ReadWrite);
     }
     catch (Exception ex)
     {
         if (MMF_Write == null)
         {
             MMF_Write = MemoryMappedFile.CreateOrOpen("Global\\CODESYS_MEMORY_READ", 1024);
             if (MMF_Write != null)
             {
                 AccessorWrite = MMF_Write.CreateViewAccessor(1, Marshal.SizeOf(typeof(StrToCodesys)), MemoryMappedFileAccess.Write);
             }
         }
         if (MMF_Read == null)
         {
             MMF_Read = MemoryMappedFile.CreateOrOpen("Global\\CODESYS_MEMORY_WRITE", 1024);
             if (MMF_Read != null)
             {
                 AccessorRead = MMF_Read.CreateViewAccessor(0, Marshal.SizeOf(typeof(StrFromCodesys)), MemoryMappedFileAccess.Read);
             }
         }
         MessageBox.Show(ex.Message);
     }
 }

2.6 数据读取
基于参考网页的运行情况下,可能会出现窗体没有显示的情况,这是由于循环导致的进程杜塞,为了避免这一问题,数据读取采用的后台进程的处理方式,程序如下:

private void ReadData()
{
    Thread readThread = new Thread(new ThreadStart(() =>
    {
        while (true)
        {
            try
            {
                Thread.Sleep(5);
                AccessorRead.Read(0, out ParaFromCodesys); // 读取 Codesys 的数据
                Thread.Sleep(5);

                // 使用 Invoke 安全更新 UI
                this.Invoke((Action)(() =>
                {
                    if (ParaFromCodesys.bOut)
                        picbox_bRead.BackColor = Color.Green;
                    else
                        picbox_bRead.BackColor = Color.Gray;

                    textbox_iRead.Text = ParaFromCodesys.iOut.ToString("0");
                    textbox_fRead.Text = ParaFromCodesys.fOut.ToString("0.000");
                }));
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    }));
    readThread.IsBackground = true; // 设置为后台线程
    readThread.Start(); // 启动线程
}

2.7 数据写程序
更新数据写程序中对bool型变量采取的是取反的操作方式,数据来源于实例化的结构体变量,程序如下:

 private void btnWriteB()
 {
     ParaToCodesys.bIn = !ParaToCodesys.bIn;
     AccessorWrite.Write(0, ref ParaToCodesys);
     if (ParaToCodesys.bIn == true)
     {
         picbox_bWrite.BackColor = Color.Green;
     }
     else
     {
         picbox_bWrite.BackColor = Color.Red;
     }
 }

2.8 数据赋值和写数据
通过按钮触发数据的更新和数据写入共享内存的操作,对应的代码如下,其中需要特别注意的是浮点数的处理,需要采用双精度转换避免精度误差的出现。
代码实现如下;

private void btnWrite_Click(object sender, EventArgs e)
{
    ParaToCodesys.bIn = true;
    ParaToCodesys.iIn = int.Parse(textbox_iWrite.Text);
    //避免数据精度问题,采用双精度方式转换
    ParaToCodesys.fIn = double.Parse(textbox_fWrite.Text);
    btnWriteB();
}

三、功能测试

通过运行对应的C#程序,在界面中可以看到对应的数据,点击write2Men按钮,界面的设置值将更新到Codesys共享的内存中,Codesys的数据如下所示。
在这里插入图片描述

注意事项

共享内存是一种更加接近底层、更加高效的IPC机制,特别适合大量数据的传输。如果涉及到有性能要求的进程间通信的场景,可以考虑使用共享内存。

但是,为了能够更好地使用共享内存,还需要注意以下事项:
1、数据一致性问题

共享内存并不自动提供同步机制,因此需要通过其他方法,比如multiprocessing.Lock等,来确保数据的一致性,尤其是在多个进程同时读写共享内存的场景中。

2、主动内存管理

在使用共享内存时,需要确保在使用完成后,显式调用close()方法和unlink()方法,从而确保内存资源被正确释放。unlink()方法调用后,会删除共享内存的标识符,使其不可用。

如果没有显式调用相应的方法,可能会导致内存泄露的问题发生。

3、数据结构

由于共享内存更偏向于底层的存储,内部更多的是字节存储,在实际使用中,需要选择恰当的数据结构,对共享内存进行映射,从而更急灵活地使用共享内存。

4、性能考虑

尽管共享内存比其他IPC机制性能更好,但是,如果没有进行正确的使用,可能会导致复杂性和性能的问题。此外,在使用中,需要仔细考虑数据的读写模式和共享内存区域的空间大小。

5、跨平台兼容性

Python的共享内存在不同操作系统上的实现可能会略有不同,所以在移植到其他平台时,需要进行充分的测试,以保证程序能够正常运行。

参考:
Python并发编程:一文搞懂为什么以及如何使用共享内存
共享内存 - C#与CoDeSys通讯


http://www.kler.cn/a/558517.html

相关文章:

  • 十、OSG学习笔记-多线程(OpenThreads)
  • android 网络防护 手机网络安全怎么防
  • ArcGIS Pro在洪水淹没分析中的应用与实践
  • 全面汇总windows进程通信(二)
  • MT7628基于原厂的SDK包, 修改ra1网卡的MAC方法。
  • 基于SpringBoot的二手交易系统
  • Hive中的分区和桶的概念及其作用
  • 《论边缘计算及其应用》审题技巧 - 系统架构设计师
  • 从人机环境系统智能角度看传统IP的全球化二次创作法则
  • 解决数据库建表错误:ERROR 1064 (42000) You have an error in your SQL
  • 网络安全营运周报
  • 线程的分离属性、互斥锁、信号量
  • 【Python爬虫(51)】深入剖析Scrapy框架:解锁高效爬虫的核心奥秘
  • MySQL 单表访问方法详解
  • Baklib知识中台重塑企业知识管理
  • 2025吐槽季第一弹---腾讯云EO边缘安全加速平台服务
  • Linux 命令大全完整版(11)
  • Vue学习教程-15自定义指令
  • DeepSeek核心技术全景解析:架构革新与工程突破
  • 力扣-贪心-45 跳跃游戏