基础计划的扩展

<< 点击显示目录 >>

主页  mappVision帮助助手 > 机器视觉帮助 > mapp Vision  > 编程 > > 核心库 > ViBase  > 用例 > 产品转换用例 > 创建程序 >

基础计划的扩展

对于示例人机界面应用程序的高级功能(例如信息头和自动选择机器视觉应用程序),仍需对基本程序进行以下扩展,但两个变体之间存在某些差异:

扩展文件 Variables.var:

VAR
IsIdle : BOOL;
InfoText : 引用字符串[160];
RisingEdgeDetector : r_trig;
ApplicationListIndex : UINT;
OldApplicationListIndex : UINT;
IsAnApplicationSelected : BOOL;
DoGenerateList : BOOL;
DoLoadApplication : BOOL;
DoClearData : BOOLBOOL;
ListApplication : ViBaseListApplication;
LoadApplication : ViBaseLoadApplication;
VisionApplicationName : STRING[50];
应用程序列表 :ARRAY[0..9] OF ViBaseFormatPlainTextType;
VAR

变量
IsIdle : BOOL;
InfoText : 引用字符串[160];
RisingEdgeDetector : r_trig;
ApplicationListIndex : UINT;
ListRefreshCounter : USINT;
IsAnApplicationSelected : BOOL;
DoGenerateList : BOOL;
DoLoadApplication : BOOL;
DoClearData : BOOLBOOL;
ListApplication : ViBaseListApplication;
LoadApplication : ViBaseLoadApplication;
VisionApplicationName : STRING[50];
应用程序列表 :ARRAY[0..9] OF ViBaseFormatItemCollectionType;
结束

带有小部件 Table 的变量 A

带有下拉框部件的变量 B

扩展文件 Init.st 中的初始化子程序:

程序 _INIT
InfoText ACCESS ADR('请按下 "刷新列表 "按钮');
LoadApplication.Name := ADR(VisionApplicationName);
LoadApplication.MpLink := ADR(gCamera);
ListApplication.MpLink := ADR(gCamera);
ListApplication.List := ADR(ApplicationList);
ListApplication.ListLen := SIZEOF(ApplicationList);
ListApplication.Format := viBASE_FORMAT_PLAIN_TEXT;
结束程序

程序 _INIT
InfoText ACCESS ADR('请按下 "刷新列表 "按钮');
LoadApplication.Name := ADR(VisionApplicationName);
LoadApplication.MpLink := ADR(gCamera);
ListApplication.MpLink := ADR(gCamera);
ListApplication.List := ADR(ApplicationList);
ListApplication.ListLen := SIZEOF(ApplicationList);
ListApplication.Format := viBASE_FORMAT_ITEMCOLLECTION;
结束程序

带有部件 Table 的变量 A

带有下拉框小部件的变体 B

扩展文件 Cyclic.st 中的循环子程序:

程序 _cyclic 
IF DoClearData THEN
DoClearData := FALSE;
InfoText ACCESS ADR('请按下 "刷新列表 "按钮');
(*重置*)
ListApplication.Execute := FALSE;
LoadApplication.Execute := FALSE;
brsmemset(ADR(ApplicationList), 0, SIZEOF(ApplicationList));
brsmemset(ADR(VisionApplicationName), 0, SIZEOF(VisionApplicationName));
 
ELSIF DoGenerateList THEN
IF ListApplication.Execute THEN
(在上一次执行后重置功能块*)。
ListApplication.Execute := FALSE;
ELSE
DoGenerateList := FALSE;
ListApplication.Execute := TRUE;
END_IF 
 
ELSIF DoLoadApplication THEN
IF LoadApplication.Execute THEN
(在上次执行后重置功能块*)。
LoadApplication.Execute := FALSE;
否则
DoLoadApplication := FALSE;
LoadApplication.Execute := TRUE;
END_IF
END_IF
ListApplication();
LoadApplication().RisingEdgeDetector(
RisingEdgeDetector( CLK:=ListApplication.Done );
IF RisingEdgeDetector.Q THEN
(刷新列表 -> 通过 OPC-UA 值绑定选择索引 0*)
OldApplicationListIndex := 0;
ApplicationListIndex := 0;
brsstrcpy(ADR(VisionApplicationName),ADR(ApplicationList[ApplicationListIndex]));
END_IF
IF ApplicationListIndex <> OldApplicationListIndex THEN
(检测到列表选择更改*)
brsstrcpy(ADR(VisionApplicationName),ADR(ApplicationList[ApplicationListIndex]));
OldApplicationListIndex := ApplicationListIndex;
END_IF
IsAnApplicationSelected := brsstrcmp(ADR(VisionApplicationName), ADR('')) <> 0;
IsIdle := TRUE;
IF ListApplication.Error OR LoadApplication.Error THEN
InfoText ACCESS ADR('Error! Please study the logbook.');
ELSIF ListApplication.Busy THEN
InfoText ACCESS ADR('Please wait while the list is refreshing.');
IsIdle := FALSE;
ELSIF LoadApplication.Busy THEN
InfoText ACCESS ADR('Please wait while the Vision Application is being loaded.');
IsIdle := FALSE;
ELSIF ListApplication.Done THEN
IF ApplicationListIndex = 0 THEN
InfoText ACCESS ADR('当前活动的机器视觉应用程序已选定,请按下 "加载应用程序 "按钮重新加载或选择其他视觉应用程序');
ELSIF IsAnApplicationSelected THEN
InfoText ACCESS ADR('请按 "加载应用程序 "按钮加载所选的机器视觉应用程序');
否则
InfoText ACCESS ADR('请选择一个 Vision 应用程序');
END_IF
END_IF 
 
结束程序

 

带部件表的变量 A

循环程序 
IF DoClearData THEN
DoClearData := FALSE;
InfoText ACCESS ADR('请按下 "刷新列表 "按钮');
(*重置*)
ListApplication.Execute := FALSE;
LoadApplication.Execute := FALSE;
brsmemset(ADR(ApplicationList), 0, SIZEOF(ApplicationList));
brsmemset(ADR(VisionApplicationName), 0, SIZEOF(VisionApplicationName));
 
ELSIF DoGenerateList THEN
IF ListApplication.Execute THEN
(在上一次执行后重置功能块*)。
ListApplication.Execute := FALSE;
ELSE
DoGenerateList := FALSE;
ListApplication.Execute := TRUE;
END_IF 
 
ELSIF DoLoadApplication THEN
IF LoadApplication.Execute THEN
(在上次执行后重置功能块*)。
LoadApplication.Execute := FALSE;
否则
DoLoadApplication := FALSE;
LoadApplication.Execute := TRUE;
END_IF
END_IF
ListApplication();
LoadApplication().RisingEdgeDetector(
 
RisingEdgeDetector( CLK:=ListApplication.Done );
IF RisingEdgeDetector.Q THEN
(*list refreshed -> select index 0 via OPC-UA value changed event binding.*) (*list refreshed -> select index 0 via OPC-UA value changed event binding.*)
ListRefreshCounter := ListRefreshCounter + 1;
(ListRefreshCounter的整数溢出没有问题*)。
END_IF
IsAnApplicationSelected := brsstrcmp(ADR(VisionApplicationName), ADR('')) <> 0;
 
IsIdle := TRUE;
 
IF ListApplication.Error OR LoadApplication.Error THEN
InfoText ACCESS ADR('Error! Please study the logbook.');
ELSIF ListApplication.Busy THEN
InfoText ACCESS ADR('Please wait while the list is refreshing.');
IsIdle := FALSE;
ELSIF LoadApplication.Busy THEN
InfoText ACCESS ADR('Please wait while the Vision Application is being loaded.');
IsIdle := FALSE;
ELSIF ListApplication.Done THEN
IF ApplicationListIndex = 0 THEN
InfoText ACCESS ADR('当前活动的机器视觉应用程序已被选中。\n请按下 "加载应用程序 "按钮重新加载或选择其他视觉应用程序。');
ELSIF IsAnApplicationSelected THEN
InfoText ACCESS ADR('请按下 "加载应用程序 "按钮加载所选的机器视觉应用程序');
否则
InfoText ACCESS ADR('请选择一个 Vision 应用程序');
END_IF
END_IF 
结束程序

 

带有小部件 DropDownBox 的变体 B

这里的基本思想是

IsIdle : BOOL

当 ViBase 功能块处于 "忙 "状态时,该标志为 FALSE。

下一节将介绍如何使用 OPC UA 绑定将 IsIdle 与 GroupBox widget GroupLoadVa 的属性 enable 相连,从而在更新列表或加载所选视觉应用程序时锁定/解锁示例 HMI 应用程序的可操作性。

InfoText:引用字符串[120]

该动态变量根据程序状态访问字符串常量。

下一节将介绍如何通过 OPC UA 值绑定将 InfoText 链接到 TextOutput widget StateMachineTitle,以及如何根据情况在示例人机界面应用程序的信息标题中显示引用的文本。

ApplicationListIndex:UINT

该变量反映了当前在示例人机界面应用程序中所选列表条目的索引。

选择第一个条目时,用户会通过 InfoText 知道当前选择的是机器视觉应用程序。

下一节将介绍如何使用 OPC UA 值绑定将 ApplicationListIndex

与变体 A 中表格部件 VAListTableDemo 的属性 selectedRow 属性绑定

或在变体 B 中与下拉框部件 VAListItemCollectionDemo 的属性 selectedIndex 链接

这样,后台程序就可以访问示例人机界面应用程序中选择的列表条目。

此外,还需要以下辅助变量来检测后端程序中选定表格行的变化,并仅对带有 Widget Table 的变体 A 做出反应:

OldApplicationListIndex:UINT

循环检查该变量的值是否与 ApplicationListIndex 不同。如果 widget Table 中所选项目发生变化,就会出现这种情况。

然后,数组变量 ApplicationList 中的 ApplicationListIndex 字符串将被复制到字符串变量 VisionApplicationName 中,OldApplicationListIndex 将被设置为等于 ApplicationListIndex。

IsAnApplicationSelected : BOOL

如果字符串变量 VisionApplicationName 为空,例如因为仍有一个空列表或选择了变体 A 中 widget Table 的一个空条目,则此布尔标志为 FALSE。在这种情况下,功能块 ViBaseLoadApplication 的输入 Name 不会设置有效值。

下一节将介绍如何通过 OPC UA 值绑定将 IsAnApplicationSelected 与 PushButton widget LoadSelectedApplication 的属性 enable 相连,从而使示例 HMI 应用程序中的按钮 "加载应用程序 "只有在非空条目被选中时才能按下。

上升沿检测器:R_TRIG

B&R 库 "runtime "的功能块 R_TRIG 实例用于检测功能块 ViBaseListApplication 输出 Done 上的正边,即列表是否已填充或更新。

应自动选择带有机器视觉应用程序的第一个条目。

对于带有部件表的变体 A,实现方法如下:

ApplicationListIndex(和 OldApplicationListIndex)设置为 0。

数组变量 ApplicationList 的第一个字符串被复制到字符串变量 VisionApplicationName 中。

对于带有窗口小部件 DropDownBox 的变体 B 来说,这一点要复杂一些,因为窗口小部件在更新时的行为与窗口小部件 Table 不同。添加了以下辅助变量:

ListRefreshCounter : USINT

每次更新列表后,该计数变量都会递增。

下一节将介绍如何使用事件绑定来定义对 ListRefreshCounter 值变化的响应,即在示例人机界面应用程序中自动选择下拉框部件 VAListItemCollectionDemo 的第一个条目。

每递增 256 次就会出现一次整数溢出。这不是问题,因为反应的只是值的变化,而不是绝对值。


For advanced features of the sample HMI application (e.g. for the info header and automatic selection of the machine vision application), the following extensions of the base program are still necessary with certain differences for the two variants:

Extension of file Variables.var:

VAR
    IsIdle : BOOL;
    InfoText : REFERENCE TO STRING[160];
    RisingEdgeDetector : r_trig;
    ApplicationListIndex : UINT;
    OldApplicationListIndex : UINT;
    IsAnApplicationSelected : BOOL; 
    DoGenerateList : BOOL;
    DoLoadApplication : BOOL;
    DoClearData : BOOL;
    ListApplication : ViBaseListApplication;
    LoadApplication : ViBaseLoadApplication;
    VisionApplicationName : STRING[50];
    ApplicationList : ARRAY[0..9] OF ViBaseFormatPlainTextType;
END_VAR

VAR
    IsIdle : BOOL;
    InfoText : REFERENCE TO STRING[160];
    RisingEdgeDetector : r_trig;
    ApplicationListIndex : UINT;
    ListRefreshCounter : USINT;
    IsAnApplicationSelected : BOOL;
    DoGenerateList : BOOL;
    DoLoadApplication : BOOL;
    DoClearData : BOOL;
    ListApplication : ViBaseListApplication;
    LoadApplication : ViBaseLoadApplication;
    VisionApplicationName : STRING[50];
    ApplicationList : ARRAY[0..9] OF ViBaseFormatItemCollectionType;
END_VAR

Variant A with widget Table

Variant B with widget DropDownBox

Extension of the initialization subroutine in file Init.st:

PROGRAM _INIT
    InfoText ACCESS ADR('Please push the Button "Refresh List".');
    LoadApplication.Name := ADR(VisionApplicationName);
    LoadApplication.MpLink := ADR(gCamera);
    ListApplication.MpLink := ADR(gCamera);        
    ListApplication.List := ADR(ApplicationList);
    ListApplication.ListLen := SIZEOF(ApplicationList);
    ListApplication.Format := viBASE_FORMAT_PLAIN_TEXT;
END_PROGRAM

PROGRAM _INIT
    InfoText ACCESS ADR('Please push the Button "Refresh List".');
    LoadApplication.Name := ADR(VisionApplicationName);
    LoadApplication.MpLink := ADR(gCamera);
    ListApplication.MpLink := ADR(gCamera);        
    ListApplication.List := ADR(ApplicationList);
    ListApplication.ListLen := SIZEOF(ApplicationList);
    ListApplication.Format := viBASE_FORMAT_ITEMCOLLECTION;
END_PROGRAM

Variant A with widget Table

Variant B with widget DropDownBox

Extension of the cyclic subroutine in file Cyclic.st:

PROGRAM _CYCLIC        
    IF DoClearData THEN
        DoClearData := FALSE;
  InfoText ACCESS ADR('Please push the Button "Refresh List".');          
        (*Reset*)
        ListApplication.Execute := FALSE;
        LoadApplication.Execute := FALSE;
        brsmemset(ADR(ApplicationList), 0, SIZEOF(ApplicationList));
        brsmemset(ADR(VisionApplicationName), 0,  SIZEOF(VisionApplicationName));
                
    ELSIF DoGenerateList THEN
        IF ListApplication.Execute THEN
            (*Reset Function block after previous execution*)
            ListApplication.Execute := FALSE;
        ELSE
            DoGenerateList := FALSE;
            ListApplication.Execute := TRUE;
        END_IF    
        
    ELSIF DoLoadApplication THEN
        IF LoadApplication.Execute THEN
            (*Reset Function block after previous execution*)
            LoadApplication.Execute := FALSE;
        ELSE
            DoLoadApplication := FALSE;
            LoadApplication.Execute := TRUE;
        END_IF
    END_IF
    ListApplication();
    LoadApplication();
    RisingEdgeDetector( CLK:=ListApplication.Done );
    IF RisingEdgeDetector.Q THEN
         (*list refreshed -> select index 0 via OPC-UA value binding*)
         OldApplicationListIndex := 0;
         ApplicationListIndex := 0;
         brsstrcpy(ADR(VisionApplicationName),ADR(ApplicationList[ApplicationListIndex]));
     END_IF
    IF ApplicationListIndex <> OldApplicationListIndex THEN
        (*list selection change detected*)
        brsstrcpy(ADR(VisionApplicationName), ADR(ApplicationList[ApplicationListIndex]));
        OldApplicationListIndex := ApplicationListIndex;
     END_IF
    IsAnApplicationSelected := brsstrcmp(ADR(VisionApplicationName), ADR('')) <> 0;   
    IsIdle := TRUE;
    IF ListApplication.Error OR LoadApplication.Error THEN
        InfoText ACCESS ADR('Error! Please study the logbook.');
    ELSIF ListApplication.Busy THEN
        InfoText ACCESS ADR('Please wait while the list is refreshing.');
        IsIdle := FALSE;
    ELSIF LoadApplication.Busy THEN
        InfoText ACCESS ADR('Please wait while the Vision Application is being loaded.');
        IsIdle := FALSE;
    ELSIF ListApplication.Done THEN
        IF ApplicationListIndex = 0 THEN
            InfoText ACCESS ADR('The currently active Machine Vision Application is selected.Please push the Button "Load Application" to reload it or select another Vision Application.');
        ELSIF IsAnApplicationSelected THEN
            InfoText ACCESS ADR('Please push the Button "Load Application" to load the selected Vision Application.');
        ELSE
            InfoText ACCESS ADR('Please select a Vision Application');
        END_IF
    END_IF   
   
END_PROGRAM

 

Variant A with widget Table

PROGRAM _CYCLIC        
    IF DoClearData THEN
        DoClearData := FALSE;
        InfoText ACCESS ADR('Please push the Button "Refresh List".');    
       (*Reset*)
        ListApplication.Execute := FALSE;
        LoadApplication.Execute := FALSE;
        brsmemset(ADR(ApplicationList), 0, SIZEOF(ApplicationList));
        brsmemset(ADR(VisionApplicationName), 0,  SIZEOF(VisionApplicationName));
                
    ELSIF DoGenerateList THEN
        IF ListApplication.Execute THEN
            (*Reset Function block after previous execution*)
            ListApplication.Execute := FALSE;
        ELSE
            DoGenerateList := FALSE;
            ListApplication.Execute := TRUE;
        END_IF    
        
    ELSIF DoLoadApplication THEN
        IF LoadApplication.Execute THEN
            (*Reset Function block after previous execution*)
            LoadApplication.Execute := FALSE;
        ELSE
            DoLoadApplication := FALSE;
            LoadApplication.Execute := TRUE;
        END_IF
    END_IF
    ListApplication();
    LoadApplication();
   
    RisingEdgeDetector( CLK:=ListApplication.Done );
    IF RisingEdgeDetector.Q THEN
        (*list refreshed -> select index 0 via OPC-UA value changed event binding.*)
        ListRefreshCounter := ListRefreshCounter + 1;
        (*eventual integer overflow of ListRefreshCounter is no problem*)
      END_IF
    IsAnApplicationSelected := brsstrcmp(ADR(VisionApplicationName), ADR('')) <> 0;  
  
    IsIdle := TRUE;
 
    IF ListApplication.Error OR LoadApplication.Error THEN
         InfoText ACCESS ADR('Error! Please study the logbook.');
    ELSIF ListApplication.Busy THEN
         InfoText ACCESS ADR('Please wait while the list is refreshing.');
         IsIdle := FALSE;
    ELSIF LoadApplication.Busy THEN
         InfoText ACCESS ADR('Please wait while the Vision Application is being loaded.');
         IsIdle := FALSE;
    ELSIF ListApplication.Done THEN
        IF ApplicationListIndex = 0 THEN
            InfoText ACCESS ADR('The currently active Machine Vision Application is selected.\nPlease push the Button "Load Application" to reload it or select another Vision Application.');
        ELSIF IsAnApplicationSelected THEN
            InfoText ACCESS ADR('Please push the Button "Load Application" to load the selected Vision Application.');
        ELSE
            InfoText ACCESS ADR('Please select a Vision Application');
        END_IF
    END_IF    
END_PROGRAM

 

Variant B with widget DropDownBox

Underlying ideas here:

IsIdle : BOOL

This flag is FALSE while the ViBase function blocks are in state Busy.

The next section describes how IsIdle is linked to attribute enable of GroupBox widget GroupLoadVa using an OPC UA binding, thus locking/unlocking the operability of the sample HMI application while the list is being updated or while the selected vision application is being loaded.

InfoText : REFERENCE TO STRING[120]

This dynamic variable accesses a string constant depending on the state of the program.

The next section describes how InfoText is linked to TextOutput widget StateMachineTitle by an OPC UA value binding and how the referenced texts are displayed in the info header of the sample HMI application based on the situation.

ApplicationListIndex: UINT

This variable reflects the index of the list entry currently selected in the sample HMI application.

When selecting the first entry, the user is informed via InfoText that the current machine vision application is selected.

The next section describes how ApplicationListIndex is linked using an OPC UA value binding

with attribute selectedRow attribute of Table widget VAListTableDemo in variant A

or with attribute selectedIndex of DropDownBox widgets VAListItemCollectionDemo in variant B

and thus the list entry selected in the sample HMI application is made accessible to the back-end program.

The following auxiliary variable is additionally required to detect a change of the selected table row in the back-end program and to react to it only for variant A with widget Table:

OldApplicationListIndex: UINT

A cyclic check takes place as to whether the value of this variable is different from ApplicationListIndex. This occurs if the selected item in widget Table has changed.

The string of array variable ApplicationList for ApplicationListIndex is then copied to string variable VisionApplicationName, and OldApplicationListIndex is set equal to ApplicationListIndex.

IsAnApplicationSelected : BOOL

This Boolean flag is FALSE if string variable VisionApplicationName is empty, e.g. because there is still an empty list or an empty entry of widget Table in variant A was selected. In this case, no valid value is set on input Name of function block ViBaseLoadApplication.

The next section describes how IsAnApplicationSelected is linked to attribute enable of PushButton widget LoadSelectedApplication by an OPC UA value binding so that button "Load application" in the sample HMI application can only be pressed when a non-empty entry is selected.

RisingEdgeDetector: r_trig

This instance of function block R_TRIG of B&R library "runtime" is used to detect a positive edge on output Done of function block ViBaseListApplication, i.e. if the list has been filled or updated.

The first entry with the machine vision application should be selected automatically.

This is implemented for variant A with widget Table as follows:

ApplicationListIndex (and OldApplicationListIndex) is set to value 0.

The first string of array variable ApplicationList is copied to string variable VisionApplicationName.

This is a bit more complicated for variant B with widget DropDownBox since the widget behaves differently that widget Table widget on updates. The following auxiliary variable is added:

ListRefreshCounter : USINT

This counter variable is incremented after each update of the list.

The next section describes how an event binding is used to define that the response to a value change of ListRefreshCounter is the automatic selection of the first entry of DropDownBox widget VAListItemCollectionDemo in the sample HMI application.

An integer overflow occurs after every 256th incrementation. This is not problematic because only value changes are reacted to, not absolute values.