支持协议:
FinsTcp/S7/EtherNet/OPC UA/Secs/Gem/Modbus/WebSocket等,支持各类PLC
软件界面截图:动态多语言管理、菜单管理、数据源管理、对接表管理、软件版本号等



操作视频
软件功能
先上源码截图

在App.axaml.cs增加启动画面
cs
public override async void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
// 显示启动画面
_splashScreen = new SplashScreen();
_splashScreen.Show();
await InitializeApplicationAsync();
await Dispatcher.UIThread.InvokeAsync(() =>
{
var main = new MainWindow()
{
DataContext = new MainWindowViewModel()
};
desktop.MainWindow = main;
main.Show();
_splashScreen.Close();
});
}
base.OnFrameworkInitializationCompleted();
}
OPC Server
cs
ApplicationInstance application = new ApplicationInstance();
application.ConfigSectionName = "OpcUaServer";
await application.LoadApplicationConfigurationAsync(false);
await application.CheckApplicationInstanceCertificatesAsync(false, 0);
var server = new StandardServer();
var nodeManagerFactory = new NodeManagerFactory();
server.AddNodeManager(nodeManagerFactory);
await application.StartAsync(server);
OPC结构体解析
cs
/// <summary>
/// 读取结构体
/// </summary>
public class StructureDto
{
public string Address { get; set; }
public string Tag { get; set; }
public StructureField StructureField { get; set; }
public StructureDefinition StructureDefinition { get; set; }
public BuiltInType BuiltInType { get; set; }
public ExpandedNodeId TypeId { get; set; }
public NodeId NodeId { get; set; }
}
读取结构体
cs
/// <summary>
/// 读取结构体
/// </summary>
/// <param name="address"></param>
/// <param name="session"></param>
/// <param name="opcNs"></param>
/// <returns></returns>
public static object ReadNoteStructure(string address, ISession session, string opcNs = null)
{
var arr = address.Split('.');
if (arr.Length > 0)
{
if (opcNs == null)
{
opcNs = ns;
}
string tag = $"{opcNs};s={arr[0]}";
var readDtReq = new ReadValueId
{
NodeId = tag,
AttributeId = Attributes.DataType
};
var readReqs = new ReadValueIdCollection { readDtReq };
session.Read(null, 0, TimestampsToReturn.Neither, readReqs, out DataValueCollection dtResults, out DiagnosticInfoCollection diag);
var dataTypeId = dtResults[0].Value as NodeId;
var readDefReq = new ReadValueId
{
NodeId = dataTypeId,
AttributeId = Attributes.DataTypeDefinition
};
var readValue = new ReadValueId
{
NodeId = readDtReq.NodeId,
AttributeId = Attributes.Value
};
session.Read(null, 0, TimestampsToReturn.Neither, new ReadValueIdCollection { readDefReq, readValue }, out DataValueCollection defResults, out DiagnosticInfoCollection diag2);
ExtensionObject dataExt = null;
if (defResults.Count >= 2)
{
dataExt = defResults[1].Value as ExtensionObject;
}
if (defResults[0].Value is ExtensionObject defExt)
{
if (defExt.Body is StructureDefinition structDef)
{
if (!nodes.ContainsKey(tag))
{
nodes.Add(tag, structDef);
}
foreach (var field in structDef.Fields)
{
string key = $"{arr[0]}.{field.Name}";
if (!OpcStructureHelper.structDefCache.ContainsKey(key))
{
StructureDto structureDto = new StructureDto();
structureDto.Address = key;
structureDto.Tag = tag;
structureDto.StructureDefinition = structDef;
structureDto.StructureField = field;
structureDto.TypeId = dataExt.TypeId;
structureDto.NodeId = dataTypeId;
structureDto.BuiltInType = (BuiltInType)Convert.ToInt32(field.DataType.Identifier);
OpcStructureHelper.structDefCache.Add(key, structureDto);
}
}
}
if (defResults.Count >= 2 && dataExt != null)
{
return dataExt.Body;
}
}
}
return null;
}
写入结构体(无法真正写入结构体单个值,只能先读整体字节流,再改,再写入,达到修改目的)
cs
public static bool WriteValue(string key, object value)
{
object val = null;
if (structDefCache.ContainsKey(key))
{
val = ReadValue(structDefCache[key].Tag);
}
else
{
val = ReadNoteStructure(key, session);
}
if (structDefCache.ContainsKey(key) && val != null)
{
if (val is byte[] dataValue)
{
var vals = DecodeWithStructure(session.MessageContext, structDefCache[key].StructureDefinition, dataValue);
byte[] newRaw;
using (var ms2 = new MemoryStream())
{
var encoder = new BinaryEncoder(ms2, session.MessageContext, true);
var wkey = structDefCache[key].StructureField.Name;
foreach (var item in vals)
{
if (item.Key == wkey)
{
WriteBuiltinValue(structDefCache[key].StructureDefinition, encoder, item.Key, value);
}
else
{
WriteBuiltinValue(structDefCache[key].StructureDefinition, encoder, item.Key, item.Value);
}
}
encoder.Close();
newRaw = ms2.ToArray();
}
var newExt = new ExtensionObject(structDefCache[key].TypeId, newRaw);
var write = new WriteValue
{
NodeId = structDefCache[key].Tag,
AttributeId = Attributes.Value,
Value = new DataValue(new Variant(newExt))
};
session.Write(null, new WriteValueCollection { write }, out StatusCodeCollection results, out DiagnosticInfoCollection diag);
if (StatusCode.IsGood(results[0]))
{
return true;
}
}
}
return false;
}
cs
public static Dictionary<string, object> DecodeWithStructure(IServiceMessageContext context, StructureDefinition structDef, byte[] raw)
{
var result = new Dictionary<string, object>();
using (var ms = new MemoryStream(raw))
{
var decoder = new BinaryDecoder(ms, context);
foreach (var field in structDef.Fields)
{
var value = DecodeField(field, decoder);
result[field.Name] = value;
}
}
return result;
}
cs
private static object DecodeField(StructureField field, BinaryDecoder decoder)
{
// 判断是否数组:ValueRank >= 0 表示数组 (ValueRank 可能为 ValueRanks.OneDimension = 1)
bool isArray = field.ValueRank >= 0;
// 判断是否内置类型(namespaceIndex == 0)
bool isBuiltin = field.DataType != null && field.DataType.NamespaceIndex == 0;
BuiltInType builtIn = BuiltInType.Null;
if (isBuiltin)
{
builtIn = (BuiltInType)Convert.ToInt32(field.DataType.Identifier);
}
if (isArray)
{
// UA Binary 数组先编码为 Int32 长度(-1 表示 null)
int length = decoder.ReadInt32(field.Name);
if (length < 0) return null;
var list = new List<object>(length);
for (int i = 0; i < length; i++)
{
if (isBuiltin)
{
list.Add(ReadBuiltinValue(builtIn, decoder, field.Name));
}
else
{
// 自定义结构:数组元素以 ExtensionObject 形式编码
// var xo = decoder.ReadExtensionObject(field.Name);
//if (xo == null) list.Add(null);
//else list.Add(DecodeExtensionObjectRecursive(xo));
}
}
return list.ToArray(); // 或返回 List<object>,按需改
}
else
{
if (isBuiltin)
{
return ReadBuiltinValue(builtIn, decoder, field.Name);
}
else
{
// 自定义结构(标量)通常以 ExtensionObject 编码
var xo = decoder.ReadExtensionObject(field.Name);
return null;
}
}
}
PLC数据监控操作
1.下载对接表

2.修改IP地址,对接读写数据地址,PLC为取读地址,MES为写入地址

修改后,保存,再上传即可。

点"对接数据监控"即可查看PLC实时数据

下载链接

下载链接为编译后运行,只提供windows版本,源码获取在第三张截图里。
链接: https://pan.baidu.com/s/1TgdBTry5AhVsIDniL3ShfQ?pwd=r7xm 提取码: r7xm