# 1. 存在的问题

当前只能对一个灯光控制模块进行控制,但是由于产线中的灯光有两组,需要对两个灯光控制模块同时控制,因此需要对插件 PluginAlarm 的 Plugin.cs 进行修改。以及目前插件中的参数并不能实现自动保存,只有手动在 config.cs 代码中添加相应项才能实现对应参数的保存和读取。

# 2. 思路

# 2.1 灯光部分:

在插件代码中添加灯光控制器的对象,并且在增益算法中添加对新增灯光调整。

# 2.2 参数保存部分:

每个插件都存在 Attributes 用于存储当前插件下的控制参数,同时在 config.cs 中定义 Settings 类用来辅助存储插件的控制参数和主程序的控制参数。然后由 config 模块实现运行时 Settings 装载,Settings 实例在保存过程中会被序列化为 json 字符串,然后保存到本地配置文件中,而在读取时会反序列化本地配置文件生成运行时 Settings 实例,当配置文件不存在时,会根据生成默认值的配置文件,当本地配置文件中没有对应的配置项时,会使用预先输入的字段作为默认值。

# 3. 实现

# 3.1 灯光

在对应控件的 Plugins.cs 中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class AutoGain :IPlugin
{
...;
//主窗体控件
Control MainWnd;

//LightController light;
//改为
LightController lightA;
LightController lightB;

CameraController camera;
...;
}

在后续使用 light 的地方都替换为 lightA 和 lightB

1
2
3
4
5
6
7
//光源调整
lightA.Intensity(1, intensity);
lightB.Intensity(1, intensity);
lightA.Intensity(2, intensity);
lightB.Intensity(2, intensity);
Console.WriteLine($"Set intensity:{intensity}");

接下来要关心的就是这些信息要如何自动保存和读取了

# 3.2 配置模块更新

# 3.2.1 配置文件结果预览

历时三天终于完成插件参数读取和保存功能,先放个目前配置文件的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
"version": "0.1.1119.02",
"Parameters": {
"languageflag": 0,
"threshold": 20.0,
"sensitivity": 90.0,
"ImageFormat": "bmp",
"th_black_gray_l": 20.0,
"thNum": 1,
"save_raw": false,
"save_200": true,
"save_500": false
},
"PluginParameters": {
"AutoGain": {
"COM_LIGHT_A": "COM4",
"COM_LIGHT_B": "COM6",
"LightLntensity": 125,
"CameraGain": 0.5,
"CameraAutoGain": false,
"LightAutoAdjust": false
},
"Alarm": {
"Enable": false,
"PortName": "COM3",
"BaudRate": 34800,
"Duration": 2000
}
}
}

现在是以 json 格式化保存各种参数,包括主程序的参数和各个插件功能的参数,其格式定义如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"version": "0.1.1119.02", //版本信息
"Parameters": { //主程序参数
//参数名:参数值
"languageflag": 0,
"threshold": 20.0,
...,
},
"PluginParameters": { //插件参数
"AutoGain": { //插件名
"COM_LIGHT_A": "COM4",
"COM_LIGHT_B": "COM6",
...,
},
"Alarm": { //插件名
"Enable": false,
"PortName": "COM3",
...,
}
}
}

实现方式:

每个插件都存在 Attributes 用于存储当前插件下的控制参数,同时在 config.cs 中定义 Settings 类用来辅助存储插件的控制参数和主程序的控制参数。然后由 config 模块实现运行时 Settings 装载,Settings 实例在保存过程中会被序列化为 json 字符串,然后保存到本地配置文件中,而在读取时会反序列化本地配置文件生成运行时 Settings 实例,当配置文件不存在时,会根据 AddAttr()中的 def 参数生成默认值的配置文件,当本地配置文件中没有对应的配置项时,会使用的 AddAttr()中的 def 参数作为该字段的默认值。

# 3.2.2 Settings 类定义

Settings 类的成员变量主要为主程序的配置项 Parameters 和 插件的配置项 PluginParameters

1
2
3
4
5
6
7
8
9
10
public class Settings
{
public string Version; //版本号
//主程序的配置项
public Dictionary<string, object> Parameters = new Dictionary<string, object>();

//插件的配置项
public Dictionary<string, Dictionary<string, object>> PluginParameters = new Dictionary<string, Dictionary<string, object>>();

}

# 3.2.3 PluginTookits 模块

完善 PluginTookits 模块中的工具,便于插件加载和存储参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/// <summary>
/// 添加string类型参数
/// </summary>
/// <param name="key">键名</param>
/// <param name="value">对应值</param>
/// <param name="def">默认值</param>
public static void AddAttr(Dictionary<string, object> Attributes, string key, ref string value, string def)
{
...;
}
//其他类型的重载
public static void AddAttr(Dictionary<string, object> Attributes, string key, ref int value, int def){
...;
}
public static void AddAttr(Dictionary<string, object> Attributes, string key, ref bool value, bool def)
{
...;
}


/// <summary>
/// 保存插件的属性字典
/// </summary>
/// <param name="mainWnd"></param>
/// <param name="target"></param>
/// <param name="pluginName">插件名</param>
/// <param name="config"></param>
/// <param name="type"></param>
public static void savAttr(object mainWnd, Dictionary<string, object> target, string pluginName, string config = "cfg", string type = "PluginParameters")
{
...;
}

/// <summary>
/// 加载插件的属性字典
/// </summary>
/// <param name="Attributes"></param>
/// <param name="MainWnd"></param>
/// <param name="name"></param>
public static void LoadAttr(ref Dictionary<string, object> Attributes, object MainWnd, string name)
{
...;
}

调用的时候,只需要在项目中添加 PluginToolkits 的引用就能使用 Toolkits.AddAttr ()、 Toolkits.savAttr ()、 Toolkits.LoadAttr () 等相关工具函数。

实现框架功能之后,还需要在插件中进行调用和实现。

# 3.2.3 主程序参数加载和保存

在 config 模块中,修改 load 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public void load(string file = "./config.json")
{
if (file.Length > 0 && File.Exists(file))
{
log._.Info($"loading configuration from file {file}");
//文件存在则读取配置
string configString = "";
using (StreamReader sr = File.OpenText(file))
{
configString = sr.ReadToEnd();
}

//写入配置文件 覆盖写入
settings = JsonConvert.DeserializeObject<Settings>(configString);

Toolkits.AddAttr(settings.Parameters, "languageflag", ref languageflag, Default.Language);
...; //其他主程序配置项

Parameters = settings.Parameters;
PluginParameters = settings.PluginParameters;

}
else
{
//如果文件不存在则使用默认值 并且以默认值生成一个配置文件
log._.Warn($"File {file} not exists, using default configuration");
load_default();
settings = new Settings();
save();
}
}

修改 save () 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void save(string file = "./config.json")
{
...;

log._.Info($"saving configuration to file {file}");

settings.Version = version; //版本信息
settings.Parameters = Parameters; //主程序参数
settings.PluginParameters = PluginParameters; //插件参数
//序列化Settings配置项
string SettingsString = JsonConvert.SerializeObject(settings, Formatting.Indented);
Console.WriteLine("writing settings ... ");
log._.Info("writing settings ... ");
//写入配置文件 覆盖写入
using (FileStream fs = File.Open(file, FileMode.Create, FileAccess.Write, FileShare.None))
{
byte[] content = new UTF8Encoding(true).GetBytes(SettingsString);
fs.Write(content, 0, content.Length);
}

log._.Info("save configuration complete.");
completed = true;
...;

}

而在 load_default () 中

1
2
3
4
5
6
7
8
9
10
11
private void load_default()
{
// 配置文件不存在,加载默认配置
Toolkits.AddAttr(Parameters, "languageflag", ref languageflag, Default.Language);
Toolkits.AddAttr(Parameters, "threshold", ref threshold, Default.threshold);
...; //其他config中需要保存的变量

//初始化插件参数字典
PluginParameters = new Dictionary<string, Dictionary<string, object>>();

}

# 3.2.4 插件参数加载和保存

插件实现加载参数的过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void Init(Control control = null)
{

MainWnd = control;
//添加对配置项的引用
Dictionary<string, object> dic = new Dictionary<string, object>();
Toolkits.LoadAttr(ref dic,MainWnd, Name);
Attributes = dic;

Toolkits.AddAttr(Attributes, "AutoGainVersion", ref version, Info.Version);
Attributes["AutoGainVersion"] = Info.Version;

//读取并应用配置中的Attributes
Toolkits.AddAttr(Attributes, "LightCOM_A", ref COM_LIGHT_A, "COMx");
Toolkits.AddAttr(Attributes, "LightCOM_B", ref COM_LIGHT_B, "COMx");
Toolkits.AddAttr(Attributes, "LightLntensity", ref intensity, 125);
Toolkits.AddAttr(Attributes, "ThresholdMean", ref mean, 130);
Toolkits.AddAttr(Attributes, "CameraGain", ref gain, 0.5);
...;
}

插件实现保存参数的过程:

1
2
3
4
5
public void OnDestory()
{
Toolkits.savAttr(MainWnd, Attributes, Name);
...;
}

当框架实现支持之后,插件很容易就实现了参数的保存和读取,之后修改某些数值就不需要修改源代码和编译重新编译,只用在配置文件中修改对应字段的数值就可以了。