Skip to main content

使用脚本访问CODESYS功能

所有对象和命令CODESYS提供的脚本也可在“scriptengine " Python 模块。每当脚本启动时,隐式<code>from scriptengine import *</code>结果。这样可以轻松访问CODESYS。但是,如果您的脚本导入需要访问的模块CODESYS API,那么这些模块必须导入模块scriptengine他们自己。

下表列出了 Python 脚本中可用作入口点的主要对象(类别)。有关入口点的完整文档,请参阅 API 参考文档CODESYS脚本引擎: CODESYS 脚本 API

对象

描述

system

访问一般CODESYS功能

例子:

  • 退出CODESYS

  • 处理通用用户界面

  • 访问消息内存(包括编译器消息)

  • 控制延迟和进度条

projects

访问CODESYS项目作为对象树,将三个导航器视图(设备、POU、模块)组合在一个项目树中

还允许加载、创建、保存和关闭项目

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

online

访问在线功能

例子:

  • 登录设备和应用程序

  • 访问数据管理(用户名、密码)

  • 网络扫描的性能

  • 网关管理

librarymanager

允许管理库存储库以及查看、安装和删除库

device_repository

处理设备存储库;导入和导出设备描述

modulerepository

管理CODESYS Application Composer模块和CODESYS Application Composer存储库

请参阅以下具体示例脚本,了解访问方法CODESYS功能。有关详细信息,请参阅CODESYS脚本引擎: CODESYS 脚本 API

示例:打印当前项目的设备树

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

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

22. 例子:PrintDeviceTree.py
# encoding:utf-8
# We enable the new python 3 print syntax
from __future__ import print_function

# Prints out all devices in the currently open project.

print("--- Printing the devices of the project: ---")

# Define the printing function. This function starts with the
# so called "docstring" which is the recommended way to document
# functions in python.
def print_tree(treeobj, depth=0):
    """ Print a device and all its children

    Arguments:
    treeobj -- the object to print
    depth -- The current depth within the tree (default 0).

    The argument 'depth' is used by recursive call and
    should not be supplied by the user.
    """

    # if the current object is a device, we print the name and device identification.
    if treeobj.is_device:
        name = treeobj.get_name(False)
        deviceid = treeobj.get_device_identification()
        print("{0}- {1} {2}".format("--"*depth, name, deviceid))

    # we recursively call the print_tree function for the child objects.
    for child in treeobj.get_children(False):
        print_tree(child, depth+1)

# We iterate over all top level objects and call the print_tree function for them.
for obj in projects.primary.get_children():
    print_tree(obj)

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

设备树(来自“设备”视图)显示在消息视图中,并且所有非设备对象都被忽略:

_cds_script_messages_print_device_tree.png


示例:读取变量

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

23. 例子:ReadVariable.py
# encoding:utf-8
from __future__ import print_function

# close open project if necessary:
if projects.primary:
    projects.primary.close()

# opens project
proj = projects.open(r"D:\data\projects\Ampel.project")

# set "Ampel.project" to active application
app = proj.active_application
onlineapp = online.create_online_application(app)

# login to device
onlineapp.login(OnlineChangeOption.Try, True)

# set status of application to "run", if not in "run"
if not onlineapp.application_state == ApplicationState.run:
    onlineapp.start()

# wait 1 second
system.delay(1000)

# read value of iVar1
value = onlineapp.read_value("PLC_PRG.iVar1")

# display value in message view or command line
print(value)

# log out from device and close "Ampel.project"
onlineapp.logout()
proj.close()

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

要使用该脚本,您需要修改路径、电子邮件地址和 SMTP 服务器的名称以适合您的环境。

24. 例子:MailVariables.py
# encoding:utf-8
from __future__ import print_function

# Close current project if necessary and open "ScriptTest.project"
if not projects.primary == None:
    projects.primary.close()
project = projects.open("D:\\Data\\projects\\scriptTest.project")

# retrieve active application
application = project.active_application

# create online application
online_application = online.create_online_application(application)

# login to application.
online_application.login(OnlineChangeOption.Try, True)

# start PLC if necessary
if not online_application.application_state == ApplicationState.run:
    online_application.start()

# wait 2 seconds
system.delay(2000)

# open recipe file to read values.
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

# read values from the controllerd
watch_values = online_application.read_values(watch_expressions)

print watch_values

# open output file to write 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")

# Close files
recipe_input_file.close()
recipe_output_file.close()

# send Email
# import respective libraries
import smtplib
from email.mime.text import MIMEText

#open output file
recipe_output_file = open("D:\\Data\\projects\\RecipeOutput.txt", "r")
mail = MIMEText(recipe_output_file.read())
recipe_output_file.close()

#email address sender and recipient
fromm = "info@example.com"
to = "info@example.com"

# set sender and recipient
mail["Subject"] = "Attention value has changed"
mail["From"] = fromm
mail["To"] = to

# send email
smtp = smtplib.SMTP("name of smtp server")
smtp.sendmail(fromm, [to], mail.as_string())
smtp.quit()

# logout and close application
online_application.logout()
project.close()




示例:创建和编辑 POU

脚本CreateDut.py创建对象MyStructMyAlias, 和MyUnionCODESYS项目。文件夹DataTypes已经存在。

25. 例子:CreateDut.py
# encoding: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]

# Create a struct DUT and insert the list of variables just into the right
# place in line two, row 0 (line numbering starts with line 0)
struktur = folder.create_dut('MyStruct') # DutType.Structure is the default
struktur.textual_declaration.insert(2, 0, STRUCT_CONTENT)

# Alias types get their "content" via the base type, which will just end up
# as one line in the declaration part:
# TYPE MyAlias : INT (0..100); END_TYPE
bereich = folder.create_dut('MyAlias', DutType.Alias, "INT (0..100)")

# Instead of injecting the variables into the existing declaration,
# one can also just replace the complete declaration part, including the
# boilerplate code.
union = folder.create_dut('MyUnion', DutType.Union)
union.textual_declaration.replace(UNION_WHOLE)


示例:用户界面/与用户的交互

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

26. 例子:System_UI_Test.py
# encoding: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!")


示例:操作项目信息对象

在脚本中ProjectInfoExample.py,我们在项目信息对象。最重要的信息项,例如标题版本,具有显式属性。但是,您可以通过dictionary语法。例如,推荐用于库项目属性的语法。

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

27. 例子:ProjectInfoExample.py
# encoding:utf-8
from __future__ import print_function

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

info = proj.get_project_info()

# Set some values
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"

# some values recommended in the library toolchain
info.values["DefaultNamespace"] = "testlibrary"
info.values["Placeholder"] = "testlibrary"
info.values["DocFormat"] = "reStructuredText"

# now we set a custom / vendor specific value.
info.values["SpecialDeviceId"] = "PLC0815_4711"

# Enable generation of Accessor functions, so the IEC
# application can display the version in an info screen.
info.change_accessor_generation(True)

# And set the library to released
info.released = True;

proj.save()


示例:调用外部命令并导入 PLCopenXML 文件

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

要使用该脚本,您需要修改环境的路径。

28. 例子:DeviceImportFromSVN.py
# encoding:utf-8
# Imports a Device in PLCopenXML from Subversion via command line svn client.

# We enable the new python 3 print syntax
from __future__ import print_function

import sys, os

# some variable definitions:
SVNEXE = r"C:\Program Files\Subversion\bin\svn.exe"
XMLURL = "file:///D:/testrepo/testfolder/TestExport.xml"
PROJECT = r"D:\test.project"

# clean up any open project:
if projects.primary:
    projects.primary.close()

# Fetch the plcopenxml data from subversion.
# We'll catch the output of the program into the xmldata variable.
# The 'with' construct automatically closes the open pipe for us.
with os.popen('"' + SVNEXE + '" cat ' + XMLURL, 'r') as pipe:
    xmldata = pipe.read()

# create a new project:
proj = projects.create(PROJECT)

# import the data into the project.
proj.import_xml(xmldata, False)

# and finally save. :-)
proj.save()

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


示例:项目归档

功能open_archive()save_archive()是用于提取项目存档或将项目另存为项目存档的脚本函数。对于这两个函数,ArchiveCategories可以指定。

  • 为了这,open_archive()接受参数categories_to_extract

  • save_archive()接受参数additional_categories

ArchiveCategories是项目档案类别,在以下情况下与项目档案一起提取open_archive()或与项目档案一起归档,如果save_archive()

例子:open_archive()

open_archive() 的各种示例,带有参数categories_to_extract介绍如下:

默认行为categories_to_extract=None

如果categories_to_extract没有明确指定,则参数设置为None . 所有类别均从项目档案中拉出并提取。

29. 例子
# Default
projects.open_archive(stArchiveFile=r"D:\Tests\Test.projectarchive", stProjectPath=r"D:\Tests", bOverwrite=True)


空列表categories_to_extract=[]

如果参数categories_to_extract明确指定了一个空列表,那么就不会从项目档案中提取任何类别。

30. 例子
# Passing empty list
projects.open_archive(stArchiveFile=r"D:\Tests\Test.projectarchive", stProjectPath=r"D:\Tests", bOverwrite=True, categories_to_extract=[])


多个类别

参数中可以指定多个类别categories_to_extract。也仅提取指定的类别。

31. 例子
# Multiple categories
projects.open_archive(stArchiveFile=r"D:\Tests\Test.projectarchive", stProjectPath=r"D:\Tests", bOverwrite=True, categories_to_extract=[ArchiveCategories.libraries, ArchiveCategories.devices])


可能的错误

如果在categories_to_extract目前不存在的CODESYS安装时,会显示错误消息。无法提取项目档案,除非安装缺少的类别或从脚本中的参数中删除该类别。

例子:save_archive()

各种例子save_archive()使用参数additional_categories介绍如下:

默认行为

如果additional_categories如果没有明确设置,则默认类别CODESYS界面也已存档。

没有类别additional_categories=None

如果additional_categories明确设置为None在脚本中,则不会存档任何类别。

多个类别

参数中可以指定多个类别additional_categories。只有指定的类别才会被包含在档案中。

32. 例子
# Muliple categories
projects.save_archive(path=r"D:\Tests", comment="A new project", additional_files=[], additional_categories=[ArchiveCategories.libraries, ArchiveCategories.devices])


高级示例:从 SVN 调用库并将其安装在 CODESYS 中

以下示例脚本可以在 CT(持续测试)环境中执行库的调用和安装,以便对其进行测试。除了标准之外,CODESYS, 这CODESYS SVN附加组件还必须使用有效许可证进行安装。

33. 例子
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()