OPC function change
This commit is contained in:
@@ -14,14 +14,11 @@ namespace Modbus.Net.Opc
|
||||
/// </summary>
|
||||
/// <param name="tagGeter">如何通过BaseMachine和AddressUnit构造Opc的标签</param>
|
||||
/// <param name="machine">调用这个编码器的设备</param>
|
||||
/// <param name="seperator">每两个标签之间用什么符号隔开,默认为/</param>
|
||||
public AddressFormaterOpc(Func<BaseMachine<TMachineKey, TUnitKey>, AddressUnit<TUnitKey>, string[]> tagGeter,
|
||||
BaseMachine<TMachineKey, TUnitKey> machine,
|
||||
char seperator = '/')
|
||||
BaseMachine<TMachineKey, TUnitKey> machine)
|
||||
{
|
||||
Machine = machine;
|
||||
TagGeter = tagGeter;
|
||||
Seperator = seperator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -35,11 +32,6 @@ namespace Modbus.Net.Opc
|
||||
/// </summary>
|
||||
protected Func<BaseMachine<TMachineKey, TUnitKey>, AddressUnit<TUnitKey>, string[]> TagGeter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 分割符
|
||||
/// </summary>
|
||||
public char Seperator { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// 编码地址
|
||||
/// </summary>
|
||||
|
||||
@@ -3,7 +3,9 @@ using Hylasoft.Opc.Da;
|
||||
using Hylasoft.Opc.Ua;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using URL = Opc.URL;
|
||||
|
||||
namespace Modbus.Net.Opc
|
||||
{
|
||||
@@ -76,6 +78,13 @@ namespace Modbus.Net.Opc
|
||||
/// <param name="serverUrl">Url address of Opc UA server</param>
|
||||
public MyDaClient(Uri serverUrl) : base(serverUrl)
|
||||
{
|
||||
var url = new URL(serverUrl.OriginalString)
|
||||
{
|
||||
Scheme = serverUrl.Scheme,
|
||||
HostName = serverUrl.Host
|
||||
};
|
||||
|
||||
typeof(DaClient).GetField("_url", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(this, url);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -115,7 +124,7 @@ namespace Modbus.Net.Opc
|
||||
/// <summary>
|
||||
/// Tag of a node
|
||||
/// </summary>
|
||||
public string[] Tag { get; set; }
|
||||
public string Tag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tag splitter of a node
|
||||
|
||||
@@ -23,12 +23,17 @@
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<Platforms>AnyCPU;x86</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>bin\Debug\Modbus.Net.Opc.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
||||
<DocumentationFile>bin\Debug\Modbus.Net.Opc.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="H.Opc" Version="0.9.3" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
using Hylasoft.Opc.Common;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Modbus.Net.Opc
|
||||
@@ -25,20 +22,13 @@ namespace Modbus.Net.Opc
|
||||
/// </summary>
|
||||
protected IClientExtend Client;
|
||||
|
||||
/// <summary>
|
||||
/// 是否开启正则匹配
|
||||
/// </summary>
|
||||
protected bool RegexOn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="host">服务端url</param>
|
||||
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||
protected OpcConnector(string host, bool isRegexOn)
|
||||
protected OpcConnector(string host)
|
||||
{
|
||||
ConnectionToken = host;
|
||||
RegexOn = isRegexOn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -102,14 +92,11 @@ namespace Modbus.Net.Opc
|
||||
{
|
||||
if (message.IsRead)
|
||||
{
|
||||
var split = message.Split;
|
||||
var tag = message.Tag;
|
||||
var rootDirectory = await Client.ExploreFolderAsync("");
|
||||
var answerTag = await SearchTag(tag, split, 0, rootDirectory);
|
||||
if (answerTag != null)
|
||||
if (tag != null)
|
||||
{
|
||||
var result = await Client.ReadAsync<object>(answerTag);
|
||||
logger.LogDebug($"Opc Machine {ConnectionToken} Read Opc tag {answerTag} for value {result.Value}");
|
||||
var result = await Client.ReadAsync<object>(tag);
|
||||
logger.LogInformation($"Opc Machine {ConnectionToken} Read Opc tag {tag} for value {result.Value}");
|
||||
return new OpcParamOut
|
||||
{
|
||||
Success = true,
|
||||
@@ -125,17 +112,14 @@ namespace Modbus.Net.Opc
|
||||
else
|
||||
{
|
||||
var tag = message.Tag;
|
||||
var split = message.Split;
|
||||
var value = message.SetValue;
|
||||
|
||||
var rootDirectory = await Client.ExploreFolderAsync("");
|
||||
var answerTag = await SearchTag(tag, split, 0, rootDirectory);
|
||||
if (answerTag != null)
|
||||
;
|
||||
if (tag != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Client.WriteAsync(answerTag, value);
|
||||
logger.LogDebug($"Opc Machine {ConnectionToken} Write Opc tag {answerTag} for value {value}");
|
||||
await Client.WriteAsync(tag, value);
|
||||
logger.LogInformation($"Opc Machine {ConnectionToken} Write Opc tag {tag} for value {value}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -167,30 +151,6 @@ namespace Modbus.Net.Opc
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 搜索标签
|
||||
/// </summary>
|
||||
/// <param name="tags">标签</param>
|
||||
/// <param name="split">分隔符</param>
|
||||
/// <param name="deep">递归深度(第几级标签)</param>
|
||||
/// <param name="nodes">当前搜索的节点</param>
|
||||
/// <returns>搜索到的标签</returns>
|
||||
private async Task<string> SearchTag(string[] tags, char split, int deep, IEnumerable<Node> nodes)
|
||||
{
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
var currentTag = node.Tag.Substring(node.Tag.LastIndexOf(split) + 1);
|
||||
if (RegexOn && Regex.IsMatch(currentTag, tags[deep]) || !RegexOn && currentTag == tags[deep])
|
||||
{
|
||||
if (deep == tags.Length - 1) return node.Tag;
|
||||
var subDirectories = await Client.ExploreFolderAsync(node.Tag);
|
||||
var answerTag = await SearchTag(tags, split, deep + 1, subDirectories);
|
||||
if (answerTag != null) return answerTag;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool Connect()
|
||||
{
|
||||
try
|
||||
|
||||
@@ -17,8 +17,7 @@ namespace Modbus.Net.Opc
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="host">Opc DA 服务地址</param>
|
||||
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||
protected OpcDaConnector(string host, bool isRegexOn) : base(host, isRegexOn)
|
||||
protected OpcDaConnector(string host) : base(host)
|
||||
{
|
||||
Client = new MyDaClient(new Uri(ConnectionToken));
|
||||
}
|
||||
@@ -27,13 +26,12 @@ namespace Modbus.Net.Opc
|
||||
/// 根据服务地址生成DA单例
|
||||
/// </summary>
|
||||
/// <param name="host">Opc DA 服务地址</param>
|
||||
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||
/// <returns>Opc DA 连接器实例</returns>
|
||||
public static OpcDaConnector Instance(string host, bool isRegexOn)
|
||||
public static OpcDaConnector Instance(string host)
|
||||
{
|
||||
if (!_instances.ContainsKey(host))
|
||||
{
|
||||
var connector = new OpcDaConnector(host, isRegexOn);
|
||||
var connector = new OpcDaConnector(host);
|
||||
_instances.Add(host, connector);
|
||||
}
|
||||
return _instances[host];
|
||||
|
||||
@@ -9,17 +9,13 @@ namespace Modbus.Net.Opc
|
||||
{
|
||||
private readonly string _host;
|
||||
|
||||
private readonly bool _isRegexOn;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="host">Opc DA服务地址</param>
|
||||
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||
public OpcDaProtocol(string host, bool isRegexOn)
|
||||
public OpcDaProtocol(string host)
|
||||
{
|
||||
_host = host;
|
||||
_isRegexOn = isRegexOn;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +25,7 @@ namespace Modbus.Net.Opc
|
||||
/// <returns>是否连接成功</returns>
|
||||
public override async Task<bool> ConnectAsync()
|
||||
{
|
||||
ProtocolLinker = new OpcDaProtocolLinker(_host, _isRegexOn);
|
||||
ProtocolLinker = new OpcDaProtocolLinker(_host);
|
||||
if (!await ProtocolLinker.ConnectAsync())
|
||||
return false;
|
||||
return true;
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||
public OpcDaProtocolLinker(bool isRegexOn) : this(ConfigurationReader.GetValueDirect("OpcDa", "Host"), isRegexOn)
|
||||
public OpcDaProtocolLinker() : this(ConfigurationReader.GetValueDirect("OpcDa", "Host"))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -17,10 +16,9 @@
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="host">Opc DA服务地址</param>
|
||||
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||
public OpcDaProtocolLinker(string host, bool isRegexOn)
|
||||
public OpcDaProtocolLinker(string host)
|
||||
{
|
||||
BaseConnector = OpcDaConnector.Instance(host, isRegexOn);
|
||||
BaseConnector = OpcDaConnector.Instance(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,11 @@ namespace Modbus.Net.Opc
|
||||
/// <param name="connectionType">连接类型</param>
|
||||
/// <param name="connectionString">连接地址</param>
|
||||
/// <param name="getAddresses">需要读写的地址</param>
|
||||
/// <param name="isRegexOn">开启正则匹配</param>
|
||||
public OpcMachine(TKey id, OpcType connectionType, string connectionString, IEnumerable<AddressUnit<TUnitKey>> getAddresses, bool isRegexOn = false)
|
||||
public OpcMachine(TKey id, OpcType connectionType, string connectionString, IEnumerable<AddressUnit<TUnitKey>> getAddresses)
|
||||
: base(id, getAddresses, true)
|
||||
{
|
||||
BaseUtility = new OpcUtility(connectionType, connectionString, isRegexOn);
|
||||
BaseUtility = new OpcUtility(connectionType, connectionString);
|
||||
AddressFormater = new AddressFormaterOpc<TKey, TUnitKey>((machine, unit) => { return new string[] { unit.Area }; }, this);
|
||||
((OpcUtility)BaseUtility).GetSeperator +=
|
||||
() => ((AddressFormaterOpc<TKey, TUnitKey>)AddressFormater).Seperator;
|
||||
AddressCombiner = new AddressCombinerSingle<TUnitKey>();
|
||||
AddressCombinerSet = new AddressCombinerSingle<TUnitKey>();
|
||||
}
|
||||
|
||||
@@ -26,22 +26,15 @@
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="tag">标签</param>
|
||||
/// <param name="split">分隔符</param>
|
||||
public ReadRequestOpcInputStruct(string[] tag, char split)
|
||||
public ReadRequestOpcInputStruct(string tag)
|
||||
{
|
||||
Tag = tag;
|
||||
Split = split;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标签
|
||||
/// </summary>
|
||||
public string[] Tag { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 分隔符
|
||||
/// </summary>
|
||||
public char Split { get; }
|
||||
public string Tag { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -82,7 +75,6 @@
|
||||
{
|
||||
IsRead = true,
|
||||
Tag = r_message.Tag,
|
||||
Split = r_message.Split
|
||||
};
|
||||
}
|
||||
|
||||
@@ -111,24 +103,17 @@
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="tag">标签</param>
|
||||
/// <param name="split">分隔符</param>
|
||||
/// <param name="setValue">写入的数据</param>
|
||||
public WriteRequestOpcInputStruct(string[] tag, char split, object setValue)
|
||||
public WriteRequestOpcInputStruct(string tag, object setValue)
|
||||
{
|
||||
Tag = tag;
|
||||
Split = split;
|
||||
SetValue = setValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标签
|
||||
/// </summary>
|
||||
public string[] Tag { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 分隔符
|
||||
/// </summary>
|
||||
public char Split { get; }
|
||||
public string Tag { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 写入的数据
|
||||
@@ -174,7 +159,6 @@
|
||||
{
|
||||
IsRead = false,
|
||||
Tag = r_message.Tag,
|
||||
Split = r_message.Split,
|
||||
SetValue = r_message.SetValue
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,8 +17,7 @@ namespace Modbus.Net.Opc
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="host">Opc UA 服务地址</param>
|
||||
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||
protected OpcUaConnector(string host, bool isRegexOn) : base(host, isRegexOn)
|
||||
protected OpcUaConnector(string host) : base(host)
|
||||
{
|
||||
Client = new MyUaClient(new Uri(ConnectionToken));
|
||||
}
|
||||
@@ -27,13 +26,12 @@ namespace Modbus.Net.Opc
|
||||
/// 根据地址获取UA连接器单例
|
||||
/// </summary>
|
||||
/// <param name="host">Opc UA服务地址</param>
|
||||
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||
/// <returns>Opc UA实例</returns>
|
||||
public static OpcUaConnector Instance(string host, bool isRegexOn)
|
||||
public static OpcUaConnector Instance(string host)
|
||||
{
|
||||
if (!_instances.ContainsKey(host))
|
||||
{
|
||||
var connector = new OpcUaConnector(host, isRegexOn);
|
||||
var connector = new OpcUaConnector(host);
|
||||
_instances.Add(host, connector);
|
||||
}
|
||||
return _instances[host];
|
||||
|
||||
@@ -9,17 +9,13 @@ namespace Modbus.Net.Opc
|
||||
{
|
||||
private readonly string _host;
|
||||
|
||||
private readonly bool _isRegexOn;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="host">Opc UA服务地址</param>
|
||||
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||
public OpcUaProtocol(string host, bool isRegexOn)
|
||||
public OpcUaProtocol(string host)
|
||||
{
|
||||
_host = host;
|
||||
_isRegexOn = isRegexOn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -28,7 +24,7 @@ namespace Modbus.Net.Opc
|
||||
/// <returns>是否连接成功</returns>
|
||||
public override async Task<bool> ConnectAsync()
|
||||
{
|
||||
ProtocolLinker = new OpcUaProtocolLinker(_host, _isRegexOn);
|
||||
ProtocolLinker = new OpcUaProtocolLinker(_host);
|
||||
if (!await ProtocolLinker.ConnectAsync()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||
public OpcUaProtocolLinker(bool isRegexOn) : this(ConfigurationReader.GetValueDirect("OpcUa", "Host"), isRegexOn)
|
||||
public OpcUaProtocolLinker() : this(ConfigurationReader.GetValueDirect("OpcUa", "Host"))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -17,10 +16,9 @@
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="host">Opc UA服务地址</param>
|
||||
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||
public OpcUaProtocolLinker(string host, bool isRegexOn)
|
||||
public OpcUaProtocolLinker(string host)
|
||||
{
|
||||
BaseConnector = OpcUaConnector.Instance(host, isRegexOn);
|
||||
BaseConnector = OpcUaConnector.Instance(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,8 +30,6 @@ namespace Modbus.Net.Opc
|
||||
|
||||
private OpcType _opcType;
|
||||
|
||||
private bool IsRegexOn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 协议类型
|
||||
/// </summary>
|
||||
@@ -46,35 +44,27 @@ namespace Modbus.Net.Opc
|
||||
//Da协议
|
||||
case OpcType.Da:
|
||||
{
|
||||
Wrapper = new OpcDaProtocol(ConnectionString, IsRegexOn);
|
||||
Wrapper = new OpcDaProtocol(ConnectionString);
|
||||
break;
|
||||
}
|
||||
//Ua协议
|
||||
case OpcType.Ua:
|
||||
{
|
||||
Wrapper = new OpcUaProtocol(ConnectionString, IsRegexOn);
|
||||
Wrapper = new OpcUaProtocol(ConnectionString);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取分隔符
|
||||
/// </summary>
|
||||
/// <returns>分隔符</returns>
|
||||
public delegate char GetSeperatorDelegate();
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="connectionType">连接类型</param>
|
||||
/// <param name="connectionString">连接地址</param>
|
||||
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||
public OpcUtility(int connectionType, string connectionString, bool isRegexOn = false) : base(0, 0)
|
||||
public OpcUtility(int connectionType, string connectionString) : base(0, 0)
|
||||
{
|
||||
ConnectionString = connectionString;
|
||||
IsRegexOn = isRegexOn;
|
||||
OpcType = (OpcType)connectionType;
|
||||
AddressTranslator = new AddressTranslatorOpc();
|
||||
}
|
||||
@@ -84,11 +74,9 @@ namespace Modbus.Net.Opc
|
||||
/// </summary>
|
||||
/// <param name="connectionType">连接类型</param>
|
||||
/// <param name="connectionString">连接地址</param>
|
||||
/// <param name="isRegexOn">是否开启正则匹配</param>
|
||||
public OpcUtility(OpcType connectionType, string connectionString, bool isRegexOn = false) : base(0, 0)
|
||||
public OpcUtility(OpcType connectionType, string connectionString) : base(0, 0)
|
||||
{
|
||||
ConnectionString = connectionString;
|
||||
IsRegexOn = isRegexOn;
|
||||
OpcType = connectionType;
|
||||
AddressTranslator = new AddressTranslatorOpc();
|
||||
}
|
||||
@@ -98,11 +86,6 @@ namespace Modbus.Net.Opc
|
||||
/// </summary>
|
||||
public override Endian Endian => Endian.BigEndianLsb;
|
||||
|
||||
/// <summary>
|
||||
/// 获取分隔符
|
||||
/// </summary>
|
||||
public event GetSeperatorDelegate GetSeperator;
|
||||
|
||||
/// <summary>
|
||||
/// 设置连接方式(Opc忽略该函数)
|
||||
/// </summary>
|
||||
@@ -122,8 +105,7 @@ namespace Modbus.Net.Opc
|
||||
{
|
||||
try
|
||||
{
|
||||
var split = GetSeperator?.Invoke() ?? '/';
|
||||
var readRequestOpcInputStruct = new ReadRequestOpcInputStruct(startAddress.Split('\r'), split);
|
||||
var readRequestOpcInputStruct = new ReadRequestOpcInputStruct(startAddress);
|
||||
var readRequestOpcOutputStruct =
|
||||
await
|
||||
Wrapper.SendReceiveAsync<ReadRequestOpcOutputStruct>(Wrapper[typeof(ReadRequestOpcProtocol)],
|
||||
@@ -159,9 +141,8 @@ namespace Modbus.Net.Opc
|
||||
{
|
||||
try
|
||||
{
|
||||
var split = GetSeperator?.Invoke() ?? '/';
|
||||
var writeRequestOpcInputStruct =
|
||||
new WriteRequestOpcInputStruct(startAddress.Split('\r'), split, setContents[0]);
|
||||
new WriteRequestOpcInputStruct(startAddress, setContents[0]);
|
||||
var writeRequestOpcOutputStruct =
|
||||
await
|
||||
Wrapper.SendReceiveAsync<WriteRequestOpcOutputStruct>(Wrapper[typeof(WriteRequestOpcProtocol)],
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Quartz.Util;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace MachineJob
|
||||
{
|
||||
return (level, func, exception, parameters) =>
|
||||
{
|
||||
if (level >= Quartz.Logging.LogLevel.Info && func != null)
|
||||
if (func != null)
|
||||
{
|
||||
Console.WriteLine("[" + DateTime.Now.ToLongTimeString() + "] [" + level + "] " + func(), parameters);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user