在组态软件中开发脚本功能的方法

news2024/11/24 2:48:49

一、概述

大多数的组态软件都具有脚本功能,脚本可能是VBS、Lua、C#等语言,也可能是厂家自定义的一种语言。通过脚本,组态软件可以实现非常灵活的功能。

脚本的功能,基本可以定义为:读入外部数据,改变可视化元素。外部数据,最重要的是采集到的数据,也可能是用户创建的配置文件。而改变可视化元素,则包括创建、改变位置、改变状态、删除等操作。

本文通过一个示例,介绍在组态软件中实现脚本功能的方法。示例有以下假定:

开发语言C#
脚本语言C#
软件类型WPF

二、示例描述

本文所使用的示例是简单且具有代表性的。本文所使用的脚本为:

string x = GetParameter("A"); //读取参数A,作为X位移
string y = GetParameter("B"); //读取参数B,作为Y位移

string id = CreateComponent("rect"); //在画布中创建一个矩形
MoveComponent(id, x, y); //将矩形移动到参数所定义的位置

可以看出,脚本首先读入外部数据(通过GetParameter方法),然后对可视化元素进行了改变(通过CreateComponent创建,通过MoveComponent移动)。读者在此肯定会提出疑问,GetParameter这些函数是哪里来的?这些是脚本跟主程序交互的API,我们会在一个类中事先定义好,然后传递到脚本中,下文会有详细介绍。

然后我们看软件的界面,如下图所示:

 界面左侧,有两个框输入两个参数,然后是输入脚本内容,还有一个运行按钮。在右侧,则是一个画布,用于绘制脚本产生的效果。

三、开发过程

3.1 项目准备

1、新建一个WPF工程,然后通过NuGet获取Microsoft.CodeAnalysis.CSharp.Scripting包。

2、在WPF中创建好界面需要的内容(也就是各输入框、按钮、Canvas)。

3.2 书写实际执行代码

对应于脚本,实际执行的代码包括获取参数、创建矩形和移动矩形。在本示例中,我们全在MainWindow.cs中实现功能。

private string GetParameter(string name)
{
    switch (name)
    {
        case "A":
            return ParaA.Text;
        case "B":
            return ParaB.Text;
        default:
            return "";
    }
}

private string CreateComponent(string type)
{
    if (type == "rect")
    {
        Random rand = new Random();
        string id = $"com{rand.Next(10000):D4}";
        var rect = new Rectangle() { Name = id, Width = 200, Height = 100, Fill = Brushes.Red };
        RegisterName(id, rect);
        DisplayCanvas.Children.Add(rect);
        return id;
    }
    return "";
}

private void MoveComponent(string para)
{
    string[] parts = para.Split(',');
    string id = parts[0];
    double x = double.Parse(parts[1]);
    double y = double.Parse(parts[2]);

    var rect = FindName(id) as Rectangle;
    Canvas.SetLeft(rect, x);
    Canvas.SetTop(rect, y);
}

GetParameter方法,根据参数的内容,返回相应输入框的内容。在实际情况中,也可能是根据采集数据的ID,获取采集数据的值。

CreateComponent方法,创建了一个红色矩形,添加到画布中,并返回了矩形的Name。

MoveComponent方法,假定参数的格式是"id,x,y",分解后,找到矩形并移动它。

3.3 书写脚本交互类

也就是定义脚本与主程序交互的API。本示例中,没有传入数据对象,而是直接传入函数委托,这样的脚本中可以主动拉取数据或推送动作。

新建一个ScriptInteract类,内容如下所示:

public class ScriptInteract
{
    private Func<string, string, string> Callback;

    public ScriptInteract(Func<string, string, string> callback)
    {
        Callback = callback;
    }

    public string GetParameter(string name)
    {
        return Callback("GetParameter", name);
    }

    public string CreateComponent(string type)
    {
        return Callback("CreateComponent", type);
    }

    public void MoveComponent(string id, string x, string y)
    {
        Callback("MoveComponent", $"{id},{x},{y}");
    }
}

由于API很多,为每一个API方法创建一个委托过于繁琐,故只创建一个通用委托,输入函数名和参数,返回一个字符串。

可以看到,上文中的脚本所调用的“未知函数”,就是ScriptInteract类中的方法。

3.4 主程序与交互类的关联

交互类中只有一个通用委托,我们在MainWindow.cs中定义此委托的实现:

private string Callback(string func, string para)
{
    switch (func)
    {
        case "GetParameter":
            return GetParameter(para);
        case "CreateComponent":
            return CreateComponent(para);
        case "MoveComponent":
            MoveComponent(para);
            return "";
        default:
            return "";
    }
}

可以看到,通过回调函数调用了之前在MainWindow.cs中定义的实际执行函数。

然后把此函数作为参数传入到ScriptInteract对象中即可。

ScriptInteract interact = new ScriptInteract(Callback);

3.5 执行脚本

执行脚本,也就是点击运行按钮的动作。先摆出代码:

private void RunBtn_Click(object sender, RoutedEventArgs e)
{
    ScriptInteract interact = new ScriptInteract(Callback);

    var code = ScriptText.Text;
    var options = ScriptOptions.Default
        .WithImports("System")
        .WithReferences(typeof(ScriptInteract).Assembly);

    var script = CSharpScript.Create(code, options, typeof(ScriptInteract));
    script.RunAsync(interact).Wait();
}

此代码,先创建一个ScriptInteract对象,关联了MainWindow.cs中的回调函数。然后把此参数传给脚本对象。那么,在脚本中就可以调用ScriptInteract所定义的API。

至此,所有功能开发完成。

示例Demo下载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/630780.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Rocketmq面试(六)Rocketmq6种找不到Broker的情况

1.发送消息 Rocketmq Client在发送消息的时候&#xff0c;会根据topic首先从本地缓存获取Broker&#xff0c;获取Broker&#xff0c;如果获取不到&#xff0c;就会到Name Server集群中获取 2.消息偏移量 客户端获取消息偏移量&#xff08;Consume Offset&#xff09;的时候&…

2.2 利用MyBatis实现CRUD操作

一、准备工作 打开MyBatisDemo项目 二、查询表记录 1、在映射器配置文件里引入结果映射元素 在UserMapper.xml文件里创建结果映射元素 将UserMapper接口里抽象方法上的注解暂时注释掉 运行TestUserMapper测试类里的testFindAll()测试方法&#xff0c;查看结果 2、添加…

【图像处理】植物叶识别和分类

一、说明 这是国外某个学生团队尝试用机器学习方法对植物叶进行识别分类的实验。实验给出若干张植物叶图片&#xff0c;针对这些图片&#xff0c;对特征进行测量、提取、重组&#xff0c;最后用机器学习方法实现&#xff1b;该具备一定的参考价值。 现在是我们将图像处理学习应…

生成测试数据的4种方法、5种工具介绍

在软件测试中&#xff0c;测试数据是测试用例的基础&#xff0c;对测试结果的准确性和全面性有着至关重要的影响。 因此&#xff0c;在进行软件测试时&#xff0c;需要生成测试数据以满足测试场景和要求。本文将介绍什么情况下需要生成测试数据&#xff0c;如何生成测试数据&a…

100 行 C++ 代码,教你快速实现视频画面动态分割!

作者&#xff1a; 一去、二三里 个人微信号&#xff1a; iwaleon 微信公众号&#xff1a; 高效程序员 在进行视频或者图像处理时&#xff0c;经常会出现画面分割的场景。 当然了&#xff0c;这里说画面分割是对视频/图像画面的切割&#xff0c;即将同一视频/图像分割成不同的部…

javassist 入门以及dubbo中的使用案例

javassite 入门 概述原理 简单的demo记录方法执行的时间带参数和返回值javassite 占位符 dubbo中的使用代理工厂 JavassistProxyFactory代理类 org.apache.dubbo.common.bytecode.Proxyorg.apache.dubbo.rpc.proxy.InvokerInvocationHandler创建类的工具类 ClassGenerator 概述…

uniapp-ios打包安装测试

我们在做uniapp需要打ios包测试的时候&#xff0c;会有证书私钥密码、证书profile文件、私钥证书三项必填项&#xff0c;这是苹果三件套&#xff0c;必须要有的。就是下图所示 下面说一下如何获取&#xff1a; 一、申请账号 1. 申请Apple id 登录&#xff1a; https://app…

Vue3:组件高级(下)

Vue3&#xff1a;组件高级&#xff08;下&#xff09; Date: May 25, 2023 Sum: ref引用、动态组件、插槽、自定义指令 目标&#xff1a; ◆ 能够知道如何使用 ref 引用 DOM 和组件实例 ◆ 能够知道 $nextTick 的调用时机 ◆ 能够说出 keep-alive 元素的作用 ◆ 能够掌握插…

TiDB亿级数据亚秒响应查询扩缩容

目录 1 查看数据分布2 当前集群部署拓扑3 扩容TiKV节点3.1 编写扩容脚本3.2 执行扩容命令3.2.1 命令格式3.2.2 执行命令 3.3 验证扩容信息3.3.1 查看节点信息3.3.2 通过dashboard查看 4 缩容TiKV节点4.1 查看节点信息4.2 执行缩容操作4.2.1 缩容命令4.2.2 执行命令 4.3 验证缩容…

Redis集群(分布式缓存):详解持久化、主从同步原理、哨兵机制、Cluster分片集群,实现高并发高可用

0、引言 单机式Redis存在以下问题&#xff0c;因此需要Redis集群化来解决这些问题 1、持久化 1.1 RDB&#xff08;Redis Database Backup file &#xff09;持久化 Redis数据快照&#xff0c;简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后&#xff0c…

CSS 布局备忘录

CSS 布局 元素布局display:blockdisplay:inlinedisplay:inline-blockdisplay:inheritdisplay:none Position 布局Flex 布局父元素属性flex-directionflex-wrapflex-flowjustify-contentalign-itemsalign-content 子元素属性orderflex-growflex-shrinkflex-basisfelxalign-self …

电商--抢购总结

文章目录 业务流程业务难点技术难点技术方案技术方向具体落地客户端流控网关流控容器流控后端接口流控数据库流控 流控总结优化读取加速异步化流程处理系统扩容 压测监控 总结参考文献 业务流程 客户端抢购流程中会涉及到商品数据的读取用于商品展示&#xff0c;运营活动数据的…

MM32F3273G8P火龙果开发板MindSDK开发教程8 - MutilButton的移植

MM32F3273G8P火龙果开发板MindSDK开发教程8 - MutilButton的移植 1、MutilButton简介 MultiButton 是一个小巧简单易用的事件驱动型按键驱动模块&#xff0c;可无限量扩展按键&#xff0c;按键事件的回调异步处理方式可以简化你的程序结构&#xff0c;去除冗余的按键处理硬编…

NodeJS SessionToken验证⑧

文章目录 ✨文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持&#x1f618;前言登录鉴权Cookie&Session ExpressSession中间件 MVC演示登录鉴权JSON Web Token (JWT) Jsonwebtoken参数sign 方法verify 方法 封装JsonWebToke…

北邮22信通:第六章查找:BST树表(代码超详细逐步图解)

北邮22信通一枚~ 跟随课程进度每周更新数据结构与算法的代码和文章 持续关注作者 解锁更多邮苑信通专属代码~ 获取更多文章 请访问专栏&#xff1a; 北邮22信通_青山如墨雨如画的博客-CSDN博客 目录 讲解 1.构造函数 2.析构函数 3.查询函数 4.删除操作 &#xf…

全新出品!阿里 P5 工程师~P8 架构师晋升路线揭秘

阿里巴巴终于公开了从初级程序员到架构师的学习路线图&#xff0c;这里相对应的基本上就是从P5到P8的晋升体系&#xff01;今天老师将会带着大家从初级程序员开始一点点分享整个晋升体系&#xff01; 职级&#xff1a;初级程序员 薪资&#xff1a;6-12K 开发年限&#xff1a;0-…

PureComponent和Component的区别和底层处理机制

PureComponent和Component都是React中的组件类&#xff0c;但它们在实现细节和使用上有些差别。 Component是React中定义组件的基类&#xff0c;它的shouldComponentUpdate方法默认返回true&#xff0c;也就是说&#xff0c;每次调用setState或forceUpdate方法都会引发组件重新…

代码随想录第55天

1.判断子序列&#xff1a; 动态规划五部曲分析如下&#xff1a; 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 dp[i][j] 表示以下标i-1为结尾的字符串s&#xff0c;和以下标j-1为结尾的字符串t&#xff0c;相同子序列的长度为dp[i][j]。 注意这里是判断s是否…

百度新闻源调整:自媒体权重降低,官方媒体优势突显

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 根据黑猫发稿的消息&#xff0c;自6月1日起&#xff0c;百度新闻源取消了大部分自媒体新闻源的收录&#xff0c;包括搜狐自媒体、企鹅号、网易号&#xff0c;甚至百度自己的百家号也受到了影响。 …

X2000 Linux 低功耗

一、进入休眠 当系统启动后&#xff0c;在命令终端输入&#xff1a; echo mem> /sys/power/state 即可立即进入休眠&#xff0c;功耗也随之降低。 二、配置中断唤醒GPIO 1、确认kernel默认配置文件 进入到/tools/iconfigtool/IConfigToolApp/路径下&#xff0c;执行./…