菜单

使用脚本访问MetaFacture功能 ​

MetaFacture提供脚本的所有对象和命令也在Python模块“scriptengine”中。每当启动脚本时,导入from scriptengine import *的结果。这样可以轻松访问MetaFacture。但是,如果你的脚本导入了需要访问MetaFactureAPIs的模块,则这些模块必须自己导入模块scriptengine。

在下表中,你将找到可以在Python脚本中用作入口点的主要对象(类别)。有关入口点的全面文档,请参阅MetaFactureScriptEngine的API参考文档。

对象 描述
系统

访问常规的MetaFacture功能,例如:

  • 退出MetaFacture
  • 处理常规用户界面
  • 访问消息存储器(包括编译器消息)
  • 控制延迟和进度条。
工程

以对象树的形式访问MetaFacture工程,该对象树将一个项目树中的三个导航器视图(设备,POU,模块)组合在一起。还允许加载,创建,保存和关闭工程。

对于工程中的大多数对象,都有一些具有详细功能的特殊方法,例如编译,访问ST POU,导出,导入,设备配置等。

在线

访问在线功能,例如:

  • 登录设备和应用程序
  • 管理访问数据(用户名,密码)
  • 网络扫描的性能
  • 网关管理
库管理器 允许管理库存储库以及查看,安装和删除库。
设备存储库 设备存储库的处理; 导入和导出设备描述。
模块存储 MetaFacture Application Composer模块和MetaFacture Application Composer存储库的管理。

 

有关访问MetaFacture功能的方法,请参见以下特定的示例脚本。有关详细信息,请参阅MetaFactureScriptEngine的API参考文档。

 


例如:打印当前工程的设备树​

脚本PrintDeviceTree.py是在工程中导航的示例。它创建打开工程中所有设备的分层显示的输出。

加载包含一些设备对象的工程并执行脚本。

例如:PrintDeviceTree.py

# 编码:utf-8
# 我们启用了新的python 3打印语法
from __future__ import print_function

# 打印出当前打开的项目中的所有设备。

打印("——打印项目的设备: ---")

# 定义打印功能。此功能以
# 所谓的“ docstring”,这是推荐的记录方式
# python中的功能.
def print_tree(treeobj, depth=0): 
    “”“打印设备及其所有子设备
    
     参数:
    treeobj -- 要打印的对象
    depth -- 树中的当前深度(默认值为0)。 
    
    递归调用使用参数“ depth”
     不应由用户提供。
    """
    
    # 如果当前对象是设备,我们将打印名称和设备标识。
    if treeobj.is_device: 
        名称 = treeobj.get_name(False)
        设备 = treeobj.get_device_identification()
        print("{0}- {1} {2}".format("--"*depth, name, deviceid))    
    
    # 我们为子对象递归调用print_tree函数。
    for child in treeobj.get_children(False): 
        print_tree(child, depth+1)

# 我们遍历所有顶级对象,并为它们调用print_tree函数。
for obj in projects.primary.get_children(): 
    print_tree(obj)

print("--- Script finished.---")
 
设备树(在“设备”视图中)显示在消息视图中,所有非设备对象均被忽略:

 


示例:变量的读取​

脚本ReadVariable.py登录到设备并在必要时启动应用程序。然后,读取并输出变量PLC_PRG.iVar1的值。要尝试该脚本,你必须修改工程路径和变量名称。

如:ReadVariable.py

# 编码:utf-8
from __future__ import print_function

# 如有必要,关闭打开的工程:
if projects.primary: 
    projects.primary.close()

# 打开工程
proj = projects.open(r"D:\data\projects\Ampel.project")

# 将“ Ampel.project”设置为活动应用程序
app = proj.active_application            
onlineapp = online.create_online_application(app)

# 登录到设备
onlineapp.login(OnlineChangeOption.Try, True)            

# 将应用程序的状态设置为“运行”,如果不在“运行”中
if not onlineapp.application_state == ApplicationState.run: 
    onlineapp.start()

# 延时1秒
system.delay(1000)            

# 读取变量iVar1
value = onlineapp.read_value("PLC_PRG.iVar1")            

# 在消息视图或命令行中显示值
print(value)              

从设备退出并关闭“ Ampel.project”
onlineapp.logout()            
proj.close()

在脚本ReadVariable.py的扩展中,脚本MailVariables.py从配方文件加载变量和表达式,并从控制器读取它们的当前值。然后将这些值写回到同一文件。另外,使用Python SMTP库发送带有附件的电子邮件,该附件包含所有变量的列表。

要使用脚本,你必须修改环境的SMTP服务器的路径,电子邮件地址和名称。

示例:MailVariables.py

# 编码:utf-8
from __future__ import print_function

# 必要时关闭当前项目,然后打开“ ScriptTest.project”
if not projects.primary == None: 
    projects.primary.close()
project = projects.open("D:\\Data\\projects\\scriptTest.project")

# 检索活动的应用程序
application = project.active_application

# 创建在线申请
online_application = online.create_online_application(application)

# 登录到应用程序。
online_application.login(OnlineChangeOption.Try, True)

# 必要时启动PLC
if not online_application.application_state == ApplicationState.run: 
    online_application.start()

# 延时2秒
system.delay(2000)

# 打开配方文件以读取值。
recipe_input_file = open("D:\\Data\\projects\\RecipeInput.txt", "r")

watch_expressions = []

for watch_expression in recipe_input_file: 
    watch_expressions.append(watch_expression.strip())

print watch_expressions

# 从控制器读取值
watch_values = online_application.read_values(watch_expressions)

print watch_values

# 打开输出文件以写入值
recipe_output_file = open("D:\\Data\\projects\\RecipeOutput.txt", "w")
for i in range(len(watch_expressions)): 
    recipe_output_file.write(watch_expressions[i])
    recipe_output_file.write(" = ")
    recipe_output_file.write(watch_values[i])
    recipe_output_file.write("\n")

# 关闭文件
recipe_input_file.close()
recipe_output_file.close()

# 发送邮件
# 导入各自的库
import smtplib
from email.mime.text import MIMEText

#打开输出文件
recipe_output_file = open("D:\\Data\\projects\\RecipeOutput.txt", "r")
mail = MIMEText(recipe_output_file.read())
recipe_output_file.close()

#电子邮件地址发件人和收件人
fromm = "info@example.com"
to = "info@example.com"

# 设置发件人和收件人
mail["Subject"] = "Attention value has changed"
mail["From"] = fromm
mail["To"] = to

# 发送邮件
smtp = smtplib.SMTP("name of smtp server")
smtp.sendmail(fromm, [to], mail.as_string())
smtp.quit()

退出并关闭应用程序
online_application.logout()
project.close()

 


例如:创建和编辑POU​

脚本CreateDut.py在MetaFacture工程中创建对象MyStruct,MyAlias和MyUnion。文件夹DataTypes已经存在。

CreateDut.py

# 编码:utf-8
from __future__ import print_function

STRUCT_CONTENT = """\
    a : BOOL;
    b : BIT;
    c : BIT;
"""

UNION_WHOLE = """\
TYPE MyUnion : 
UNION
    Zahl : INT;
    Prozent : MyAlias;
    Bits : MyStruct;
END_UNION
END_TYPE
"""

proj = projects.primary

folder = proj.find('DataTypes', recursive = True)[0]

# 创建一个结构DUT,并将变量列表插入到右侧
# 放在第二行第0行(行编号从第0行开始)
struktur = folder.create_dut('MyStruct') # DutType.Structure is the default
struktur.textual_declaration.insert(2, 0, STRUCT_CONTENT)

# 别名类型通过基本类型获取其“内容”,该基本类型将最终结束
# 作为声明部分中的一行:
# TYPE MyAlias : INT (0..100); END_TYPE
bereich = folder.create_dut('MyAlias', DutType.Alias, "INT (0..100)")

与其将变量注入到现有的声明中,
# 也可以只替换完整的声明部分,包括
# 样板代码。
union = folder.create_dut('MyUnion', DutType.Union)
union.textual_declaration.replace(UNION_WHOLE)

 


例如:用户界面/与用户的交互​

在某些情况下,脚本必须与用户进行交互。我们为最常见的交互提供了一些简单的API。示例脚本System_UI_Test.py显示了这方面的所有可能功能。

示例:System_UI_Test.py

# 编码:utf-8
from __future__ import print_function

"""Performs some tests on the messagestore and UI."""

print("Some Error, Warning and Information popups:")
system.ui.error("Fatal error: Everything is OK.:-)")
system.ui.warning("Your bank account is surprisingly low")
system.ui.info("Just for your information: 42")

print("Now, we ask the user something.")
res = system.ui.prompt("Do you like this?", PromptChoice.YesNo, PromptResult.Yes);
print("The user selected '%s'" % res)

print("Now, the user can choose between custom options:")
res = system.ui.choose("Please choose:", ("First", 2, 7.5, "Something else"))
print("The user selected option '%s'" % str(res)) # res is a tuple

print("Now, the user can choose several options:")
res = system.ui.select_many("Please select one or more options", PromptChoice.OKCancel, PromptResult.OK, ("La Premiere", "The Second", "Das Dritte"))
print("The returned result is: '%s'" % str(res)) # res is a tuple

print("Now, the user can select files and directories")
res = system.ui.open_file_dialog("Choose multiple files:", filter="Text files (*.txt)|*.txt|Image Files(*.BMP;*.JPG;*.GIF)|*.BMP;*.JPG;*.GIF|All files (*.*)|*.*", filter_index = 0, multiselect=True)
print("The user did choose: '%s'" % str(res)) # res is a tuple as multiselect is true.

res = system.ui.save_file_dialog("Choose a file to save:", filter="Text files (*.txt)|*.txt|Image Files(*.BMP;*.JPG;*.GIF)|*.BMP;*.JPG;*.GIF|All files (*.*)|*.*", filter_index = 0)
print("The user did choose: '%s'" % res)

res = system.ui.browse_directory_dialog("Choose a directory", path="C:\\")
print("The user did choose: '%s'" % res)

print("Now we query a single line string")
res = system.ui.query_string("What's your name?")
print("Nice to meet you, dear %s."% res)

print("Now we query a multi line string")
res = system.ui.query_string("Please tell me a nice story about your life!", multi_line=True)
if (res): 
    print("Huh, that has been a long text, at least %s characters!"% len(res))
else: 
    print("Hey, don't be lazy!")

print("Username and passwort prompts...")
res = system.ui.query_password("Please enter your favourite password!", cancellable=True)
if res: 
    print("Huh, it's very careless to tell me your favourite password '%s'!"% res)
else: 
    print("Ok, if you don't want...")
    
res = system.ui.query_credentials("Now, for real...")
if res: 
    print("Username '%s' and password '%s'" % res) # res is a 2-tuple
else: 
    print("Sigh...")

 


例如:操作工程信息对象

在脚本ProjectInfoExample.py中,我们在 工程信息对象中设置了一些信息。最重要的信息项,例如标题版本,具有明确的属性。但是,你可以通过dictionary语法读取和写入任何其他信息字段。例如,对于库工程的属性推荐的那些。

下面的示例似乎有些不切实际,但是在构建服务器中使用了类似的代码,这些服务器创建,测试并可能发布自动库工程和其他工程。ScriptEngine是创建CI(持续集成)和CD(持续交付)系统的关键元素之一。

例如:ProjectInfoExample.py

# 编码:utf-8
from __future__ import print_function

proj = projects.load("D:\Some.library")

info = proj.get_project_info()

# 设置一些值
info.company = "Test Library Ltd"
info.title = "Script Test Project"
info.version = (0, 8, 15, 4711)
info.default_namespace = "testlibrary"
info.author = "Python von Scriptinger"

# 库工具链中推荐的一些值
info.values["DefaultNamespace"] = "testlibrary"
info.values["Placeholder"] = "testlibrary"
info.values["DocFormat"] = "reStructuredText"

# 现在我们设置一个自定义/供应商特定的值。
info.values["SpecialDeviceId"] = "PLC0815_4711"

# 启用访问器功能的生成,因此IEC
# 应用程序可以在信息屏幕中显示版本。
info.change_accessor_generation(True)

# 并将库设置为发布
info.released = True;

proj.save()

 


 例如:调用外部命令并导入PLCOpenXML文件​

示例脚本DeviceImportFromSVN.py从外部程序(在本例中为SVN客户端)获取PLCOpenXML文件,并将其导入到新创建的MetaFacture工程中。

要使用脚本,你必须修改环境的路径。

例如:DeviceImportFromSVN.py

# 编码:utf-8
# 通过命令行svn客户端从Subversion导入PLCOpenXML中的设备。

# 我们启用了新的python 3打印语法
from __future__ import print_function

import sys, os

# 一些变量定义:
SVNEXE = r"C:\Program Files\Subversion\bin\svn.exe"
XMLURL = "file:///D:/testrepo/testfolder/TestExport.xml"
PROJECT = r"D:\test.project"

# 清理所有打开的工程:
if projects.primary: 
    projects.primary.close()

# 从Subversion获取plcopenxml数据。 
# 我们将程序的输出捕获到xmldata变量中。
# “ with”结构会自动为我们关闭打开的管道。
with os.popen('"' + SVNEXE + '" cat ' + XMLURL, 'r') as pipe: 
    xmldata = pipe.read()

# 创建一个新的工程:
proj = projects.create(PROJECT)

# 将数据导入工程。
proj.import_xml(xmldata, False)

# 最后保存。:-)
proj.save()

print("--- Script finished.---")

 


高级的例子:从SVN调用库并将其安装在MetaFacture​

以下示例脚本可以作为CT(连续测试)环境的一部分执行库的调用和安装,以便可以对其进行测试。除了标准MetaFacture以外,还必须使用有效许可证安装的MetaFacture SVN附加组件。

示例

import tempfile

if projects.primary: 
    projects.primary.close()

tempdir = tempfile.mkdtemp()
URL = "svn://localhost/testrepo/trunk/SvnTestLibrary/"

proj = svn.checkout(URL, tempdir, "testlibrary", as_library=True)
proj.save()

repo = librarymanager.repositories[0]
librarymanager.install_library(proj.path, repo, True)

proj.close()

最近修改: 2025-07-24