C#与PLC变量在数据交互上的一些探索
1. 基于C#的数据交互
A. 用静态变量存储PLC的交互数据
-
UI和PLC变量如下:
-
新建static类
internal static class data_yamltest_exchange
{
public static byte byte_var;
public static short word_var;
public static bool bool_var;
}
- 数据交互,这里的数据二次封装过的,基于S7协议封装的,这里不深究
//task1
Task.Run(() =>
{
while (true)
{
if (my_plc_status)
{
//read from plc
data_yamltest_exchange.bool_var = my_s7.bool_rw_value(132, my_plc_iDB, 4, "r", 0); ;
//write to plc
my_s7.write_read_value("word", 132, my_plc_iDB, 2, "w", data_yamltest_exchange.word_var, 0, 1);
my_s7.write_read_value("byte", 132, my_plc_iDB, 0, "w", data_yamltest_exchange.byte_var, 0, 1);
}
}
});
- UI后端代码,这里用了两个委托来完成radioButton的显示
public partial class _6Yaml_test : UserControl
{
public _6Yaml_test()
{
InitializeComponent();
this.textBox1.Text = "0";
this.textBox2.Text = "0";
}
string val = null;
bool status = false;
private void _6Yaml_test_Load(object sender, EventArgs e)
{
Task.Run(() =>
{
setTextCallBack sV = new setTextCallBack(setValue);
setRadioCallBack rV = new setRadioCallBack(setStatus);
while (true)
{
if (data_yamltest_exchange.bool_var == true)
{
status = true;
val = "bool have been enabled";
radioButton1.Invoke(sV, val);
radioButton1.Invoke(rV, status);
}
else
{
status = false;
val = "bool have been disabled";
radioButton1.Invoke(sV, val);
radioButton1.Invoke(rV, status);
}
try
{
data_yamltest_exchange.word_var = Convert.ToInt16(textBox2.Text.ToString());
data_yamltest_exchange.byte_var = Convert.ToByte(textBox1.Text.ToString());
}
catch { break; };
}
});
}
//cross-thread delivery
//1.delegate type
public delegate void setTextCallBack(string val);
public delegate void setRadioCallBack(bool status);
//2.delegate function
public void setValue(string val)
{
radioButton1.Text=val;
}
public void setStatus(bool status)
{
radioButton1.Checked = status;
}
}
- 这个方法里,PLC读出的数据存储在自建的静态类里,要写入PLC变量的值也由自建的静态类给出,C#各个线程都可以访问该静态类,避免了各个线程和类之间传值的复杂和繁琐。
B. 用yaml交互
- 安装
YamlDoNet
库
- 引用命名空间
using YamlDotNet.Serialization;
-
新建文件夹,新建
.yaml
文件,建立yaml结构
-
读取yaml文件方法
//yaml_read
public T Yaml_read<T>(string file_path)
{
var deserializer = new DeserializerBuilder().Build();
T my_yamlObject;
using (var reader = new StreamReader(file_path))
{
my_yamlObject=deserializer.Deserialize<T>(reader);
}
return my_yamlObject;
}
- 写入yaml文件方法
//yaml_write
public void Yaml_write(object obj, string file_path)
{
var serializer = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build();
var my_yaml = serializer.Serialize(obj);
File.WriteAllText(file_path, my_yaml);
}
- 建立和yaml结构一致的class结构,这是重点
- 用
[YamlMember(Alias)=""]
来指定yaml文件中的结构名 [YamlIgnore]
用于在序列化或者反序列化的时候忽略掉它[YamlMember(Order = 1)]
用于指定序列化的顺序{get;set;}
用于设置yaml结构的属性,公有属性
。yaml文件的结构本身就相当于被保护起来的一个私有字段
。- yaml文件中不要用table键缩进,会报错,缩进一般使用空格,敲两个或者四个空格
- yaml内容的结构名,和实际程序里面必须一一对应,一字不差
- 用
- 建立yaml文件需要的程序结构样例
//yaml结构
public class yaml_data
{
public class yaml_test_data1
{
[YamlMember(Alias = "Byte_var")]
public byte Byte_var { get; set; }
[YamlMember(Alias = "Word_var")]
public short Word_var { get; set; }
[YamlMember(Alias = "Bool_var")]
public bool Bool_var { get; set; }
}
public class yaml_test_data2
{
[YamlMember(Alias = "Int_var")]
public int Int_var { get; set; }
[YamlMember(Alias = "Char_var")]
public char Char_var { get; set; }
}
// Class II
public class yamal_class_collection
{
[YamlMember(Alias = "test_data1")]
public yaml_test_data1 test_data1 { get; set; }
[YamlMember(Alias = "test_data2")]
public yaml_test_data2 test_data2 { get; set; }
}
//Class I
[YamlMember(Alias="data_collection")]
public yamal_class_collection data_collection { get; set; }
}
- 建立yaml文件内容,注意上下对应
data_collection: #Class I 一级class名称,和程序对应
test_data1: #Class II 二级class名称,和程序对应
Byte_var: 0x10 #各个字段
Word_var: 32
Bool_var: false
test_data2: #Class II 二级class名称,和程序对应
Int_var: 99
Char_var: 'a'
-
找了个以前的截图,只为说明yaml文件的结构如何做,示意如图:
-
引用yaml,在窗体load事件中读取yaml文件默认配置(读取到初始化的值)
yaml_data my_yaml_data = new yaml_data();
Config_yaml my_yaml= new Config_yaml();
my_yaml_data = my_yaml.Yaml_read<yaml_data>("C:\\Users\\testyaml.yaml");