# 使用脚本访问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()