二次开发
二次开发
Lua开发
Lua开发界面
SFB支持内嵌Lua开发环境,支持Lua编程、运行、单步调试、变量查看。支持Lua XXX版本。
Lua与SFB场景的交互原理:通过内置信号量函数实现。
- 界面介绍
点击【菜单栏】-【信号】-【Lua脚本】按钮,打开Lua脚本管理窗口。
- 功能栏:功能栏包含操作、调试两项
- 工作区:显示多个Lua脚本的目录
- 编辑区:对已打开的脚本文件进行编辑
- 日志输出:显示运行输出日志
- 操作功能
脚本的文件基础操作功能如下:
- 调试功能
脚本文件程序的调试功能如下:
SFBLua接口说明
SFB实现的Lua自定义接口说明:
- void print(string content)
功能:打印输出
参数:content,字符串,即要输出的字符内容;
返回:无
- void timedelay(int ms)
功能:延时等待
参数:ms,整数,时间长度,单位毫秒;
返回:无
- bool readbool(int index)
功能:读开关量
参数:index,整数,索引值,不应该大于开关量的最大值8191;
返回:开关量的值;
- bool writebool(int index, bool value)
功能:写开关量
参数:index,整数,索引值,不应该大于开关量的最大值8191;
value, 布尔,待写入的值;
返回:写入的值
- int readint(int index)
功能:读数字量
参数:index,整数,索引值,不应该大于数字量的最大值8191;
返回:数字量的值;
- int writeint(int index, int value)
功能:写数字量
参数:index,整数,索引值,不应该大于数字量的最大值8191;
value, 整数,待写入的值;
返回:写入的值
- float readfloat(int index)
功能:读浮点量
参数:index,整数,索引值,不应该大于浮点量的最大值8191;
返回:浮点量的值;
- float writefloat(int index, float value)
功能:写浮点量
参数:index,整数,索引值,不应该大于浮点量的最大值8191;
value, 浮点数,待写入的值;
返回:写入的值
- TcpListener listenPortInit(int port, bool stopSame)
功能:TCP端口监听初始化
参数:port,整数,端口号,端口号不允许重复以及与系统端口冲突,否则会异常;
stopSame, 布尔,是否先停止与输入端口相同的监听再创建新的端口监听;
返回:已经创建的端口监听对象;
- void listenPortStart(TcpListener lis)
功能:启动TCP端口监听
参数:lis,TCP端口监听对象;
返回:无
- TcpClient listenConnect(TcpListener lis)
功能:侦听客户端连接,此接口会等待直到有客户端连接才返回;
参数:lis,TCP端口监听对象;
返回:连接到此监听的TCP端口的客户端;
- void listenPortStop(TcpListener lis)
功能:停止TCP端口监听
参数:lis,TCP端口监听对象;
返回:无
Python开发
SFB支持Pythoh插件环境,支持Pythoh编程、运行、单步调试、变量查看。支持Pythoh XXX版本。
Pythoh与SFB场景的交互原理:通过内置信号量函数实现。
- 界面介绍
点击【菜单栏】-【应用】-【Python】按钮,打开Python脚本处理器窗口。
- 功能栏:功能栏包含运行时管理、项目管理、依赖管理及运行状态管理
- 工作区:以树状结构显示多个Python项目的目录结构及项目运行状态
- 编辑区:对已打开的文件进行编辑
- 日志输出:显示插件输出的日志及通过内置日志输出函数打印内容
- 运行时管理
- 点击“运行时”按钮打开运行时管理窗口
- 通过下拉选项,选择已安装的运行时版本
- 也可通过输入版本号,点击安装来下载未安装版本的运行时,等待安装完成
- 项目管理
- 点击新建项目,在弹出窗口输入项目名称,创建一个新的Python工程目录
- 选择要添加新文件夹的项目或文件夹,点击新建文件夹,在弹出窗口输入文件夹名称,创建一个新的文件夹
- 选择要添加新文件的项目或文件夹,点击新建文件,在弹出窗口输入文件名称并选择文件类型,创建一个新的文件
- 在树形结构中,选中某一个节点,点击删除或在右键菜单中选择删除,可删除该项目/文件夹/文件
- 选择一个Python项目,点击依赖管理,打开模块管理窗口
- 在模块输入框输入模块名称,如:panda,如果要指定模块版本,在版本输入框输入版本号,留空则安装最新版本
- 在列表中选中已安装的模块,点击卸载则删除该模块
- 运行状态管理
- 选中想要运行或停止的Python项目,点击运行当前或停止当前来控制项目状态
- 点击运行所有,则会所有勾选使能的项目
- 点击停止所有,则会停止所有已运行的项目
- SFB进入运行状态时,所有勾选使能的项目也会随之运行;SFB退出运行状态时,所有项目也会随之停止
插件开发应用
SFB插件介绍
- SFB 插件简介
插件是遵循一定规范的应用程序接口编写出来的程序,只能在程序规定的系统平台下,不能脱离指定的平台单独运行。我们常见的插件有 WEB 浏览器插件,如 Flash 插件、Office插件、Visual Studio 插件、Eclipse 插件。
SFB 插件是遵循 SFB 插件开发规范,编写的应用程序,插件只能运行在 SFB 平台下。
- MEF 框架简介
MEF(Managed Extensibility Framework)是微软为大型应用程序(比如 Visual Studio)提供的一个功能扩展框架,通过一个混合层提升了灵活性、维护性和可测试性。
SFB 基于 MEF 插件框架,为第三方开发者提供插件扩展服务,在不改变 SFB 程序的情况下,扩展 SFB 功能。
- 开发前准备
SFB 插件开发,首先要获取 SFB 插件开发必备的动态库,该动态库位于 SFB 安装目录的“PluginDLL”目录。如果需要了解 SFB 的安装,请参考《SFB 使用说明书》软件安装章节。
SFB 插件开发,目前仅支持 C#语言,.net Framwerow 4.6.2 开发环境。强烈建议采用安装 Visual studo 2017,同时包含.net Framwerow 4.6.2 组件方式安装插件开发环境。
插件开发
MEF 基础知识
SFB 插件开发,需掌握基础 C#开发知识外,还需对 MEF 开发框架的基本原理有所了解,至少应掌握导入(Import)、导出(Export)、组合容器(CompositionContainer)的基本概念和2简单使用。
- MEF 基本原理
MEF 的工作原理是对部件(Part)的需求(Import)和供应(Export)。 每当应用对某部件(Part)有需求(Import)时,MEF 组合引擎(CompositionContainer)将在我们指定的位置(Catalog)查找遵循共同的契约(Contract)的供应(Export),并自动创建这些部件的实例。
- MEF 基本概念
⚫ Part(部件): Part 是一个可以在应用程序中进行导入或导出操作的对象(可以是
类、方法、属性)
⚫ Catalog(仓库): 协助从程序集或目录中发现有效的部件的对象;
⚫ Contract(契约): 导入和导出的部件之间的一些约定(接口或预先定义的数据类型,如字符),部件通过这些约定进行通信;
⚫ Import 特性(导入): 声明导入部件的需求;
⚫ ImportMany 特性: 支持导入多个部件的导入声明;
⚫ Export (导出): Import 声明导入部件需求,Export 则声明实现这些需求。
⚫ Compose(组合): 将匹配的导入与导出组合在一起。
更详细的 MEF 知识,请参考
https://docs.microsoft.com/en-us/dotnet/framework/mef/
扩展 SFB 工具栏
SFB 插件,通常是以 SFB 工具栏功能按钮的形式,展示给用户。SFB 允许第三方在【应用】工具栏分页下,添加工具栏分组和工具栏按钮。其中,工具栏按钮,必须包含在工具栏分组之中。
通过下面的示例,在 SFB 工具栏【应用】分页中,添加【我的扩展】工具栏分组和【我的按钮】功能按钮;点击【我的按钮】功能按钮,显示消息提示框。
SFB 工具栏
- 扩展工具栏分组
⚫ 首先,在 Visual Studio 中,创建一个新的控制台应用程序项目,此处注意:实现接口的程序集必须以“Plugin.”开头,如“Plugin.Tool.dll”
新建工程
⚫ 从 SFB 安装根目录中,拷贝“PluginDLL”文件夹至工程目录下
⚫ 引用 SFB 插件开发动态库
在控制台项目中,添加对“PluginDLL”文件夹中,SFB.PluginContract.dll 和
SFB.PluginUtility.dll 两个动态库的引用
引用动态库
⚫ 引用 MEF 框架 System.ComponentModel.Composition 程序集的引用
MEF 程序集
项目引用
⚫ 新建 RibbonGroup 类,继承并实现 IRibbonGroupBox 接口在ibbonGroup 类,除了继承 IRibbonGroupBox,还声明了导出(Export)特征,并标记契约类型为 IRibbonGroupBox,因此该类成为 MEF 的部件(Part)。
实现 IRibbonGroupBox 契约的部件,将由于 MEF 组合至 SFB,SFB 读取相关信息,并创建工具栏分组。
⚫ 将工程修改为类库,并发布
由于 SFB 只搜索.DLL 扩展名的动态库,因此将项目修改为“类库”,编译生成动态库
⚫ 部署 SFB 扩展插件 PluginTool6
在 SFB 插件安装目录(安装目录下 Plugin 目录),新建“PluginTool”目录,然后将PluginTool 项目生成的动态库,拷贝至该目录
⚫ 运行 SFB,加载插件
运行 SFB 后,在 SFB“应用”工具栏分页中,已添加“我的扩展”工具栏分组
注意事项:
- 工具栏分组的父级编码,即 ParentUUID,是由 SFB 定义的;必须与 SFB 定义的 UUID 保持一致。
- SFB 只能从扩展名为.DLL 的动态库中,查找插件,因此插件必须是以.DLL 形式。
- SFB 只查找位于安装目录下 Plugin 目录中的插件。
- 扩展工具栏按钮
SFB 通过工具栏分组,对 SFB 工具栏上的按钮进行分组管理;而工具栏按钮,是 SFB 与用户交互功能按钮。通过插件形式扩展的的工具栏按钮,必须依附于特定的工具栏分组。
在前一章节,我们已经具备了一个名为“MyPlugin”的工具栏按钮分组,在 SFB 工具栏上显示为“我的扩展”。通过下面的示例,我们在“我的扩展”按钮分组中,添加名为“MyButton”的按钮,在 SFB 工具栏上显示为“我的按钮”点击后显示一个 Windows 窗口。
扩展 SFB 工具栏分组时,接口中包含一个按钮集合 List<IRibbonButton > Buttons;SFB 会根据该集合中对按钮的描述,创建新的工具栏按钮添加到指定的工具栏分组中。
⚫ 定义实现 IRibbonButton 接口类
新建 PluginButton,实现 IRibbonButton;
⚫ 修改 RibbonGroup 类,生成按钮集合
⚫ 编译后,更新插件
更新插件,同时将两个 png 图片,保存在该插件安装目录下
⚫ 运行 SFB
注意事项:
- 按钮关联的命令,采用了 SFB.PluginUtility 命名空间中定义的命令相关类PluginCommand;使用该类,必须引用 SFB.PluginUtility.DLL 程序集。可以为按钮指定其它实现 ICommand 接口的命令,SFB 并不强制要求使用 PluginCommand。
- 按钮的图标,采用的是相对路径;路径格式为:插件安装根目录名称\图标名称.扩展名
SFB信号量读写
SFB 通过导出(Export)SFB.PluginContract 命名空间中的 ISFBDataAccess 契约,为插件开发者提供 SFB 信号量读写服务。插件开发者,如需读写 SFB 信号量,只需导入(Import)即可。
SFB 中实现导出(Exprot)ISFBDataAccess 契约的部件,位于IFB.PluginExtension.DLL动态库,因此必须将 SFB.PluginExtension.DLL 动态库包含在插件目录中。
下面通过修改前一章节的示例,演示如何读写 SFB 信号量。
⚫ 新建读写 SFB 信号类 SFBDataAccess,并导入 ISFBDataAccess 契约
⚫ SFBDataAccess 添加读写整型量接口 ReadIntValue,WritteIntValue
⚫ 组合实现了契约的部件
通过 import 特征,声明需要导入(Import)实现 ISFBDataAccess 契约的导出(Export);由于 SFB 在 SFB.PluginExtension.DLL 动态库中,实现了 ISFBDataAccess 契约的部件。因此,我们通过组合容器(CompositionContainer)导入该部件;组合过程,在构造函数中实现
⚫ 新建 Winform 窗口,接收用户输入,并显示输入
⚫ 添加读取与显示控件
控件类型
名称
用途
NumericUpDown
numIntIndex
设置整型量索引
NumericUpDown
numIntValue
显示读取的整型量或设置写入的整型量
Button
btnReadInt
执行整型量读取动作
Button
btnWritteInt
执行整型量写入动作
⚫显示信号量读写窗口
通过前几步,已经实现了信号量的读写。为了通过点击按钮,显示信号量读写窗口。我们修改 12.3.2 章节中实现的 RibbonGroup 类
⚫ 编译并更新 SFB 插件
⚫ 运行,并测试 SFB 信号量读写
注意事项:
- SFB 信号量读写需求,并不是通过静态引用 SFB.PluginExtension.DLL 动态库实现的,而是由组合容器导入符合契约的部件实现;代码中指定组合容器查找到的路径为插件所在路径,因此,即使没有引用 SFB.PluginExtension.DLL 动态库动态库,在部署插件时,插件部署目录必须存在 SFB.PluginExtension.DLL 动态库,否则组合容器查找不到合适的部件,导入会失败,导致读写功能无法实现。
响应SFB回调
SFB.PluginContract 程序集中的 ISFBAction 契约,定义了 SFB 回调契约。所有导出(Export)该契约的部件,都会被 SFB 导入,并在 SFB 执行期间,调用相应的契约接口。
我们通过在 SFB 打开工程时,获取工程所在路径为示例,演示如何应用 ISFBAction 契约。
⚫ 在 PluginTool 项目中,新增 SFBAction 类SFBAction 类,实现 ISFBAction,并声明为实现 ISFBAction 契约的导出;
⚫ LoadedProject 接口方法中,获取工程路径
⚫ 编译,并更新插件
⚫ 运行 SFB,每次打开 SFB 工程后,会提示当前工程路径
更多的 SFB 回调契约,请查看第 3 章 SFB 插件契约部分
SFB插件契约
IRibbonGroupBox
通过 IRibbonGroupBox 契约,实现对 SFB 工具栏的扩展
- 定义
命名空间 SFB.PluginContract
程序集:SFB.PluginContract.DLL
- UUID 属性
string UUID { get; }
工具栏分组全局唯一编码,相同 UUID 的插件会被分到同一个组中
- ParentUUID 属性
string ParentUUID { get; }
工具栏分组父级全局唯一编码,即工具栏分页全局唯一编码
- Index 属性
int Index { get; }
工具栏分组排序索引
- Header 属性
string Header { get; }
工具栏分组标题
- Buttons
List<IRibbonButton > Buttons { get; }
工具栏分组中的按钮集合;按钮实现 IRibbonButton 接口
ISFBDataAccess
通过 ISFBDataAccess 契约,实现对 SFB 信号量读写操作。
- 定义
命名空间 SFB.PluginContract
程序集:SFB.PluginContract.DLL
- GetBoolMemoryByIndex 方法
⚫ 定义
bool? GetBoolMemoryByIndex(int index);
读取 SFB 开关量指定索引位置的值
⚫ 参数
Index int
index 表示读取信号量地址16
⚫ 返回
bool?
读取失败,返回空值;
- SetBoolMemoryByIndex 方法
⚫ 定义
bool SetBoolMemoryByIndex(int index, bool value);
设置 SFB 开关量指定索引位置的值
⚫ 参数
Index int
index 表示读取信号量地址
Value bool
Value 表示新设置的值
⚫ 返回
bool
设置成功,返回 True,否则返回 False;
- GetIntMemoryByIndex 方法
⚫ 定义
bool GetIntMemoryByIndex(int index,out int value);
读取 SFB 数字量指定索引位置的值
⚫ 参数
Index int
index 表示读取信号量地址
Value int
Value 表示读取的值
⚫ 返回
bool
读取成功,返回 True,否则返回 False;
- SetIntMemoryByIndex 方法
⚫ 定义
bool SetIntMemoryByIndex(int index, int value);
设置 SFB 数字量指定索引位置的值
⚫ 参数
Index int
index 表示设置信号量地址
Value int
Value 表示新设置的值
⚫ 返回
bool
设置成功,返回 True,否则返回 False;
- GetFloatMemoryByIndex 方法
⚫ 定义
bool GetFloatMemoryByIndex(int index, out float value);
读取 SFB 浮点量指定索引位置的值
⚫ 参数
Index int
index 表示读取信号量地址
Value float
Value 表示读取的值
⚫ 返回
bool
读取成功,返回 True,否则返回 False;
- SetFloatMemoryByIndex 方法
⚫ 定义
bool SetFloatMemoryByIndex(int index, float value);
设置 SFB 浮点量指定索引位置的值
⚫ 参数
Index int
index 表示设置信号量地址
Value float
Value 表示新设置的值
⚫ 返回
bool
设置成功,返回 True,否则返回 False;
ISFBAction
ISFBAction 契约,定义了 SFB 回调接口;实现 ISFBAction 契约的部件,SFB 在运行期间,会调用部件的
- 定义
命名空间 SFB.PluginContract19
程序集:SFB.PluginContract.DLL
- 方法
⚫ 定义
void Inition(string version);
SFB 初始化完成时调用方法
⚫ 参数
Version string
Version 表示 SFB 版本号
⚫ 返回
Void
- StateChanged
⚫ 定义
void StateChanged(bool isRunning);
SFB 运行状态改变时调用方法
⚫ 参数
isRunning bool
isRunning 表示 SFB 是否处于运行状态;True 表示运行状态;False 表示非运行状态
⚫ 返回
Void
- CreateProject
⚫ 定义
void CreateProject(string projectPath);20
SFB 创建新工程时调用方法
⚫ 参数
projectPath string
projectPath 表示 SFB 新建工程的目录路径
⚫ 返回
Void
- SavingProject
⚫ 定义
void SavingProject();
SFB 保存工程前调用的方法
⚫ 参数
无
⚫ 返回
Void
- SavedProject
⚫ 定义
void SavedProject();
SFB 保完成工程保存调用的方法
⚫ 参数
无
⚫ 返回
Void21
- LoadedProject
⚫ 定义
void LoadedProject(string projectPath);
SFB 工程加载完成时调用的方法
⚫ 参数
projectPath string
projectPath 表示 SFB 加载工程的目录路径
⚫ 返回
Void
- ClosedProject
⚫ 定义
void ClosedProject();
SFB 关闭工程时调用的方法
⚫ 参数
无
⚫ 返回
Void
- Closing
⚫ 定义
void Closing();
SFB 正在关闭时调用的方法
⚫ 参数
无
⚫ 返回
Void
- Closed
⚫ 定义
void Closed();
SFB 关闭前调用的方法
⚫ 参数
无
⚫ 返回
Void
SFB 插件开发约定
工具栏编码约定
SFB 插件开发规则约定,通过插件扩展的工具栏按钮分组,只能通过唯一识别码,添加到指定的工具栏分页上;识别码错误,将无法创建。
当前版本,SFB 仅开放应用分页;插件开发者可以在此分页上添加工具栏按钮分组。
工具栏元素
唯一识别码
【应用】分页
PluginInApplication
插件部置路径约定
⚫ SFB 插件路径位于 SFB 安装路径根目录下的 Plugin 目录
⚫ 所有的插件,应包含单独的安装文件夹;假设有 PluginTool 插件,则该插件所有动态库应部署在:SFB 安装路径根目录\Plugin\PluginTool 路径下
插件动态库版本号
SFB 插件开发规则约定,SFB 插件开发动态库的版本,通过 SFB.PluginContract 动态库,SFB.PluginContract 命名空间的PluginContractVersion 静态类的 Version 属性获取。
工具栏按钮接口
SFB 插件开发规则约定,所有添加至工具栏分组中的按钮,都必须实现 IRibbonButton接口,否则无法识别IRibbonButton 定义在 SFB.PluginContract 动态库,SFB.PluginContract 命名空间。
- UUID 属性
string UUID { get; }
全局唯一编码
- ParentUUID
string ParentUUID { get; }
父级全局唯一编码
- Index
int Index { get; }
按钮排序索引
- Header
string Header { get; }
按钮标题内容24
- IconUri 属性
string IconUri { get; }
按钮小图标路径; 该路径为相对于 SFB 插件部署路径的相对路径
- LargIconUri 属性
string LargIconUri { get; }
按钮大图标路径; 该路径为相对于 SFB 插件部署路径的相对路径
- ExcutComand 属性
ICommand ExcutComand { get; }
按钮绑定的命令
- ToolTip 属性
string ToolTip { get; }
按钮提示信息