用C#构建的电气控制仿真控件库

  • 格式:doc
  • 大小:107.00 KB
  • 文档页数:6

1 用C#构建的电气控制仿真控件库

朱菊香,史东丽,丁金根

(常州铁道高等职业技术学校电气工程系 江苏 常州 213011)

摘 要:用C#语言来实现一组Windows控件库。控件库包含了一些常用的电气设备,如开关、继电器、接触器以及三相异步电动机等电气设备。这些控件与普通的Windows控件一样,可在父窗体上任意拖放,调整属性,控件呈现的外观与电气控制原理图一致,并能通过编程来模拟运行,实现了动态图纸的功能。

关键词:C#;.net Framework;自定义控件;仿真;动态图纸

1 引言

我们在从事与电气控制相关的教学过程中发现,学生即使花很长的时间也未必能读懂一个电气控制系统的原理图。究其原因是普通的原理图不够形象,不能动态地显示出每一时刻系统的状态,从而增加了学生理解的难度。因此,用微软公司的Visual C#制作了一个电气控制系统的仿真控件库,利用这个控件库可方便地制作出能实时仿真的动态原理图。

选用C#是因为它简单、功能强大、类型安全,而且是面向对象的,C#凭借它的许多创新,在保持C++表示形式的同时,实现了应用程序的快速开发。用C#创建自己的控件要比其它高级语言更方便快捷,并可应用于任何一种支持.net的语言(目前Basic、C++、Delphi等均已支持.net)。

2 仿真控件库的总体设计思想

电气控制仿真库就是一组基于.net的Windows控件,它将电气控制系统中常用的接触器、继电器、按钮、开关、电动机等元件的图形符号及其基本功能封装在Windows控件中,在父窗体上放置控件就像在图纸上画出元件图形符号一样。运行时用鼠标点击窗体上按钮或开关的图标来模拟按动动作,就像操作实际器件一样,其余元件通过其编程接口来改变行为,根据相互间的逻辑关系作出准确的动作。

仿真库采用事件驱动的方式来模拟实际器件的动作。如图1所示。

图1 系统框图

由图1可知,整个系统的仿真过程和PLC的运行过程是极其相似的。鼠标点击按钮、开关等输入元件,产生输入元件的状态改变事件,在其事件处理函数中输入元件 电路扫描

与执行 输入状态刷新

输入元件重绘 输入元件映像触点状态映像区 输出元件映像区 输出元件重绘

触点状态触点重绘 状态改变事件

状态

改变事件 状态改变事件

状态

改变事件 2 进行元件图标的重绘和输入元件映像区的更新工作。电路扫描与执行过程则根据输入映像区和触点状态映像区的值并按照元器件之间的连接关系来改变接触器、继电器这些输出元件的状态。执行结束后再刷新输出元件映像区,若状态有改变则重绘输出元件图标并更新触点状态映像区。

针对系统的仿真原理,电气控制仿真控件库应具备如下基本功能:

1、能响应鼠标输入,开放鼠标事件处理函数;

2、能在设计态和运行态方便地设置元件属性;

3、能根据属性值自动刷新图标外观,实现所见即所得功能;

4、能自动判别元件的状态是否改变,并为其配置事件处理函数。

另外,仿真库的图形符号应尽量符合国家标准,要与实际图纸的表示方式一致。

3 仿真控件库的详细设计

C#所使用的Windows 窗体控件是一组基于.NET Framework的可重用组件,根据实现方法的不同,可将自行开发控件分为复合控件、扩展控件和自定义控件三类。其中复合控件就是将多个Windows 控件封装在同一个窗体容器中,也即将若干个Windows 控件的功能合成一个可重新使用的单元,它保留了每个被包含的Windows控件所有固有的功能,可以有选择地公开和绑定它们的属性。由于复合控件继承于UserControl类,它已经供了大量默认的鼠标、键盘处理功能,所以不需要太多额外的开发工作,并且可以在控件表面任意绘图,结合仿真控件库所要实现的基本功能,本人将复合控件定为仿真库的基础控件。

不管是哪一种控件,都包含有属性(Property)、方法(Method)和事件(Event)三部分内容,应当将实际电气元件的特性与功能和控件的相关部分正确地联系起来。以按钮为例,它的名称和编号可归入到“属性”,其图形符号的绘制可纳入到“方法”,而按钮由通到断或由断到通的动作相当于控件的“事件”。现将部分对应关系列表如下:

属性 元件的名称、编号

触点的类型:常开、常闭

触点的状态:断开、闭合

线圈的状态:得电、失电

时间继电器的整定值

事件 按钮的压下与弹起

触点的闭合与由断开

线圈的得电与失电

时间继电器的延时到

方法 元件图形符号的绘制

表1 实际元件与控件的对应关系

上表右栏所列项目中,有些是所有元器件共有的,如名称、编号等,而有的则是某些元器件所特有的,如线圈状态、时间继电器整定值等。根据元器件之间的异同点及相互关系,归纳出如图2所示的控件类继承层次关系(限于篇幅,未图2 类层次图 3 将所有控件类列出)。

3.1 ElecBase类的实现

ElecBase类是所有控件的基类,包含了所有控件的公共特性,因为它本身继承于UserControl类,所以已经具备了一个空白的控件界面。ElecBase只有两个属性,即元件的名称和编号。先为这两个属性指定成员变量,以保存属性值,然后再用get(读取)、set(设置)语句将属性和它们对应的成员变量建立映射关系。要注意的是必须在set分程序内调用Refresh()函数,这样在每次修改属性后都使控件通过调用Paint方法来重新绘制自己的外观以更新显示。代码如下:

protected int m_Index; // 元件编号

protected string m_TypeName; // 类型名称

public int Index {

get { return m_Index; }

set { m_Index = value;

Refresh(); }

}

public string TypeName {

get { return m_TypeName; }

set { m_TypeName = value;

Refresh(); }

}

为了能直观地表示控件是否被选中,需要ElecBase类对鼠标移入、移出事件作简单处理,可控制一bool变量来指示当前鼠标是否在控件区域内。当鼠标滑入控件区域时,控件将显示一红色边框,当鼠标移出控件区域时红色边框消失。Paint的处理函数如下:

private void ElecBass_Paint(object sender, PaintEventArgs e) {

System.Drawing.Pen MyPen = new Pen(Color.Red, 2);

if (m_MouseEnter == true) { // 指示鼠标是否进入控件区域的成员变量

e.Graphics.DrawRectangle(MyPen, 1, 1, Width - 2, Height

- 2);

}

}

由于其它所有的控件均继承于ElecBass,所以诸如NormalCoil 、ElecPart3P等这些控件都自动具备了名称和编号这两个属性,并在鼠标进入控件区域时能显示红色外框。

3.2 ElecPart类的实现

ElectPart在ElecBass的基础上增加了一些属性和事件。添加的属性有OnOffState、LastState、CurrentPass,它们都是bool型属性,添加的事件有OnOpenToClose、OnCloseToClose和OnStateChange。

OnOffState用以指示元件的当前状态, true表示闭合或得电,false表示断开或失电;LastState保存了OnOffState的前一个状态,之所以要添加LastState属性,是因为在图1所示的电路扫描与执行过程中完全以LastState值为运行的依据,这与典型的PLC梯形图扫描算法是类似的。而CurrentPass用来指示是否有电 4 流流过元件。OnOpenToClose、OnCloseToClose和OnStateChange则分别为元件由断到通、由通到断及两者之一发生时的事件。

下面是属性OnOffState的实现代码,其它属性的实现都是雷同的:

protected bool m_OnOffState; // 对于触点On表示闭合,Off表示断开

// 对于线圈On表示得电,Off表示失电

protected bool m_LastState; // 上一个OnOff状态

protected bool m_CurrentPass; // 是否有电流通过

public bool OnOffState {

get { return m_OnOffState; }

set { m_LastState = m_OnOffState;

m_OnOffState = value;

if (m_LastState == false && m_OnOffState == true) {//如果状态由Off转为On

OnOpenToClose(System.EventArgs.Empty); // 则触发OnOpenToClose事件

OnStateChange(System.EventArgs.Empty); // 和OnStateChange事件

}

else if (m_LastState == true && m_OnOffState == false) {//

如果状态由ON转为OFF

m_CurrentPass = false;

OnCloseToOpen(System.EventArgs.Empty); // 则触发OnCloseToClose事件

OnStateChange(System.EventArgs.Empty); // 和OnStateChange事件

}

else { return; }