CODESYS Development System > 使用脚本 > 创建一个Python脚本 > 使用脚本访问CODESYS功能 |
CODESYS提供脚本的所有对象和命令也在Python模块“scriptengine”中。每当启动脚本时,导入<code>from scriptengine import *</code>的结果。这样可以轻松访问CODESYS。但是,如果你的脚本导入了需要访问CODESYSAPIs的模块,则这些模块必须自己导入模块scriptengine。
在下表中,你将找到可以在Python脚本中用作入口点的主要对象(类别)。有关入口点的全面文档,请参阅CODESYSScriptEngine的API参考文档。
对象 | 描述 |
---|---|
系统 | 访问常规的CODESYS功能,例如:
|
工程 | 以对象树的形式访问CODESYS工程,该对象树将一个项目树中的三个导航器视图(设备,POU,模块)组合在一起。还允许加载,创建,保存和关闭工程。 对于工程中的大多数对象,都有一些具有详细功能的特殊方法,例如编译,访问ST POU,导出,导入,设备配置等。 |
在线 | 访问在线功能,例如:
|
库管理器 | 允许管理库存储库以及查看,安装和删除库。 |
设备存储库 | 设备存储库的处理; 导入和导出设备描述。 |
模块存储 | CODESYS Application Composer模块和CODESYS Application Composer存储库的管理。 |
有关访问CODESYS功能的方法,请参见以下特定的示例脚本。有关详细信息,请参阅CODESYSScriptEngine的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 = "[email protected]"
to = "[email protected]"
# 设置发件人和收件人
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()
脚本CreateDut.py在CODESYS工程中创建对象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()
示例脚本DeviceImportFromSVN.py从外部程序(在本例中为SVN客户端)获取PLCOpenXML文件,并将其导入到新创建的CODESYS工程中。
要使用脚本,你必须修改环境的路径。
.例如: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.---")
以下示例脚本可以作为CT(连续测试)环境的一部分执行库的调用和安装,以便可以对其进行测试。除了标准CODESYS以外,还必须使用有效许可证安装的CODESYS 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()