remove fbox and change h-opc to offical package
This commit is contained in:
@@ -1,24 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Modbus.Net.FBox
|
|
||||||
{
|
|
||||||
public class AddressCombinerFBox : AddressCombiner
|
|
||||||
{
|
|
||||||
public override IEnumerable<CommunicationUnit> Combine(IEnumerable<AddressUnit> addresses)
|
|
||||||
{
|
|
||||||
return (from address in addresses
|
|
||||||
select
|
|
||||||
new CommunicationUnit()
|
|
||||||
{
|
|
||||||
Area = address.Area,
|
|
||||||
Address = address.Address,
|
|
||||||
GetCount = 1,
|
|
||||||
DataType = address.DataType
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Modbus.Net.FBox
|
|
||||||
{
|
|
||||||
public class AddressFormaterFBox : AddressFormater
|
|
||||||
{
|
|
||||||
public override string FormatAddress(string area, int address)
|
|
||||||
{
|
|
||||||
return area + " " + address;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Modbus.Net.FBox
|
|
||||||
{
|
|
||||||
public class AddressTranslatorFBox : AddressTranslator
|
|
||||||
{
|
|
||||||
protected Dictionary<string, int> AreaCodeDictionary;
|
|
||||||
|
|
||||||
public AddressTranslatorFBox()
|
|
||||||
{
|
|
||||||
AreaCodeDictionary = new Dictionary<string, int>
|
|
||||||
{
|
|
||||||
{"LW", 0},
|
|
||||||
{"V", 1},
|
|
||||||
{"VW", 2},
|
|
||||||
{"VD", 3},
|
|
||||||
{"V.B", 4},
|
|
||||||
{"I", 5},
|
|
||||||
{"IW", 6},
|
|
||||||
{"ID", 7},
|
|
||||||
{"I.B", 8},
|
|
||||||
{"Q", 9},
|
|
||||||
{"QW", 10},
|
|
||||||
{"QD", 11},
|
|
||||||
{"Q.B", 12},
|
|
||||||
{"M", 13},
|
|
||||||
{"MW", 14},
|
|
||||||
{"MD", 15},
|
|
||||||
{"M.B", 16},
|
|
||||||
{"0X", 20},
|
|
||||||
{"1X", 21},
|
|
||||||
{"2X", 22},
|
|
||||||
{"3X", 23},
|
|
||||||
{"4X", 24},
|
|
||||||
{"SD", 30},
|
|
||||||
{"D", 31},
|
|
||||||
{"T_word", 32},
|
|
||||||
{"C_word", 33},
|
|
||||||
{"DB", 10000},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public override AddressDef AddressTranslate(string address, bool isRead)
|
|
||||||
{
|
|
||||||
var tmpAddress = address.Trim().ToUpper();
|
|
||||||
if (tmpAddress.Substring(0, 2) == "DB")
|
|
||||||
{
|
|
||||||
var addressSplit = tmpAddress.Split(' ');
|
|
||||||
if (addressSplit.Length != 2) throw new FormatException();
|
|
||||||
addressSplit[0] = addressSplit[0].Substring(2);
|
|
||||||
return new AddressDef()
|
|
||||||
{
|
|
||||||
Area = int.Parse(addressSplit[0]) + AreaCodeDictionary["DB"],
|
|
||||||
Address = int.Parse(addressSplit[1])
|
|
||||||
};
|
|
||||||
}
|
|
||||||
var tmpAddressArray = (tmpAddress.Split(' '));
|
|
||||||
string head = tmpAddressArray[0];
|
|
||||||
string tail = tmpAddressArray[1];
|
|
||||||
return
|
|
||||||
new AddressDef()
|
|
||||||
{
|
|
||||||
Area = AreaCodeDictionary[head],
|
|
||||||
Address = int.Parse(tail)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetAreaName(int code)
|
|
||||||
{
|
|
||||||
if (code < 10000)
|
|
||||||
return AreaCodeDictionary.FirstOrDefault(p => p.Value == code).Key;
|
|
||||||
else
|
|
||||||
return AreaCodeDictionary.FirstOrDefault(p => p.Value == code - code%10000).Key + code%10000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Modbus.Net.FBox
|
|
||||||
{
|
|
||||||
public enum SignalRServer
|
|
||||||
{
|
|
||||||
FBoxServer = 0,
|
|
||||||
DelianServer = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Constants
|
|
||||||
{
|
|
||||||
public SignalRServer SignalRServer = SignalRServer.FBoxServer;
|
|
||||||
|
|
||||||
public string BaseAddress
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
switch (SignalRServer)
|
|
||||||
{
|
|
||||||
case SignalRServer.FBoxServer:
|
|
||||||
{
|
|
||||||
return "https://account.flexem.com/core";
|
|
||||||
}
|
|
||||||
case SignalRServer.DelianServer:
|
|
||||||
{
|
|
||||||
return "https://id.data.hzdelian.com/core";
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
return "https://account.flexem.com/core";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string AuthorizeEndpoint => BaseAddress + "/connect/authorize";
|
|
||||||
|
|
||||||
public string LogoutEndpoint => BaseAddress + "/connect/endsession";
|
|
||||||
|
|
||||||
public string TokenEndpoint => BaseAddress + "/connect/token";
|
|
||||||
|
|
||||||
public string UserInfoEndpoint => BaseAddress + "/connect/userinfo";
|
|
||||||
|
|
||||||
public string IdentityTokenValidationEndpoint => BaseAddress + "/connect/identitytokenvalidation";
|
|
||||||
|
|
||||||
public string TokenRevocationEndpoint => BaseAddress + "/connect/revocation";
|
|
||||||
|
|
||||||
public string AspNetWebApiSampleApi
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
switch (SignalRServer)
|
|
||||||
{
|
|
||||||
case SignalRServer.FBoxServer:
|
|
||||||
{
|
|
||||||
return "http://fbox360.com/api/client/";
|
|
||||||
}
|
|
||||||
case SignalRServer.DelianServer:
|
|
||||||
{
|
|
||||||
return "http://wl.data.hzdelian.com/api/client/";
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
return "http://fbox360.com/api/client/";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,286 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Modbus.Net.FBox
|
|
||||||
{
|
|
||||||
public class BoxGroup
|
|
||||||
{
|
|
||||||
[JsonProperty("boxRegs")]
|
|
||||||
public List<BoxReg> BoxRegs { get; set; }
|
|
||||||
[JsonProperty("children")]
|
|
||||||
public List<BoxGroup> Children { get; set; }
|
|
||||||
[JsonProperty("id")]
|
|
||||||
public int Id { get; set; }
|
|
||||||
[JsonProperty("name")]
|
|
||||||
public string Name { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BoxReg
|
|
||||||
{
|
|
||||||
[JsonProperty("box")]
|
|
||||||
public Box Box { get; set; }
|
|
||||||
[JsonProperty("id")]
|
|
||||||
public int Id { get; set; }
|
|
||||||
[JsonProperty("alias")]
|
|
||||||
public string Alias { get; set; }
|
|
||||||
[JsonProperty("registrationDate")]
|
|
||||||
public DateTime RegistrationDate { get; set; }
|
|
||||||
[JsonProperty("Favorite")]
|
|
||||||
public bool Favorite { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Box
|
|
||||||
{
|
|
||||||
[JsonProperty("commServer")]
|
|
||||||
public CommServer CommServer { get; set; }
|
|
||||||
[JsonProperty("id")]
|
|
||||||
public int Id { get; set; }
|
|
||||||
[JsonProperty("uid")]
|
|
||||||
public string Uid { get; set; }
|
|
||||||
[JsonProperty("boxNo")]
|
|
||||||
public string BoxNo { get; set; }
|
|
||||||
[JsonProperty("connectionState")]
|
|
||||||
public int ConnectionState { get; set; }
|
|
||||||
[JsonProperty("allowedCommServerIds")]
|
|
||||||
public List<int> AllowedCommserverIds { get; set; }
|
|
||||||
[JsonProperty("currentSessionID")]
|
|
||||||
public int CurrentSessionId { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CommServer
|
|
||||||
{
|
|
||||||
[JsonProperty("id")]
|
|
||||||
public int Id { get; set; }
|
|
||||||
[JsonProperty("serverId")]
|
|
||||||
public int ServerId { get; set; }
|
|
||||||
[JsonProperty("apiBaseUrl")]
|
|
||||||
public string ApiBaseUrl { get; set; }
|
|
||||||
[JsonProperty("signalrUrl")]
|
|
||||||
public string SignalRUrl { get; set; }
|
|
||||||
[JsonProperty("state")]
|
|
||||||
public int State { get; set; }
|
|
||||||
[JsonProperty("disabled")]
|
|
||||||
public bool Disabled { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GetValue
|
|
||||||
{
|
|
||||||
[JsonProperty("id")]
|
|
||||||
public string Id { get; set; }
|
|
||||||
[JsonProperty("status")]
|
|
||||||
public int Status { get; set; }
|
|
||||||
[JsonProperty("value")]
|
|
||||||
public double? Value { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DMonGroup
|
|
||||||
{
|
|
||||||
[JsonProperty("id")]
|
|
||||||
public int Id { get; set; }
|
|
||||||
[JsonProperty("name")]
|
|
||||||
public string Name { get; set; }
|
|
||||||
[JsonProperty("dMonEntries")]
|
|
||||||
public List<DMonEntry> DMonEntries { get; set; }
|
|
||||||
[JsonProperty("uid")]
|
|
||||||
public string Uid { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DMonEntry
|
|
||||||
{
|
|
||||||
[JsonProperty("src")]
|
|
||||||
public DMonSource Source { get; set; }
|
|
||||||
[JsonProperty("id")]
|
|
||||||
public int Id { get; set; }
|
|
||||||
[JsonProperty("uid")]
|
|
||||||
public string Uid { get; set; }
|
|
||||||
[JsonProperty("fracDigits")]
|
|
||||||
public int FracDigits { get; set; }
|
|
||||||
[JsonProperty("intDigits")]
|
|
||||||
public int IntDigits { get; set; }
|
|
||||||
[JsonProperty("padLeft")]
|
|
||||||
public bool PadLeft { get; set; }
|
|
||||||
[JsonProperty("padRight")]
|
|
||||||
public bool PadRight { get; set; }
|
|
||||||
[JsonProperty("updateInterval")]
|
|
||||||
public int UpdateInterval { get; set; }
|
|
||||||
[JsonProperty("privilege")]
|
|
||||||
public int Privilege { get; set; }
|
|
||||||
[JsonProperty("name")]
|
|
||||||
public string Name { get; set; }
|
|
||||||
[JsonProperty("desc")]
|
|
||||||
public string Desc { get; set; }
|
|
||||||
[JsonProperty("dataType")]
|
|
||||||
public int DataType { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DMonSource
|
|
||||||
{
|
|
||||||
[JsonProperty("id")]
|
|
||||||
public int Id { get; set; }
|
|
||||||
[JsonProperty("uid")]
|
|
||||||
public string Uid { get; set; }
|
|
||||||
[JsonProperty("UpdateInterval")]
|
|
||||||
public int UpdateInterval { get; set; }
|
|
||||||
[JsonProperty("isDMon")]
|
|
||||||
public bool IsDMon { get; set; }
|
|
||||||
[JsonProperty("isAlarm")]
|
|
||||||
public bool IsAlarm { get; set; }
|
|
||||||
[JsonProperty("flag")]
|
|
||||||
public int Flag { get; set; }
|
|
||||||
[JsonProperty("regWidth")]
|
|
||||||
public int RegWidth { get; set; }
|
|
||||||
[JsonProperty("regId")]
|
|
||||||
public int RegId { get; set; }
|
|
||||||
[JsonProperty("mainAddr")]
|
|
||||||
public int MainAddr { get; set; }
|
|
||||||
[JsonProperty("subAddr")]
|
|
||||||
public int SubAddr { get; set; }
|
|
||||||
[JsonProperty("subIndex")]
|
|
||||||
public int SubIndex { get; set; }
|
|
||||||
[JsonProperty("serverId")]
|
|
||||||
public int ServerId { get; set; }
|
|
||||||
[JsonProperty("portNo")]
|
|
||||||
public int PortNo { get; set; }
|
|
||||||
[JsonProperty("stationNo")]
|
|
||||||
public int StationNo { get; set; }
|
|
||||||
[JsonProperty("deviceId")]
|
|
||||||
public int DeviceId { get; set; }
|
|
||||||
[JsonProperty("ip")]
|
|
||||||
public string Ip { get; set; }
|
|
||||||
[JsonProperty("port")]
|
|
||||||
public int Port { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DeviceSpecSource
|
|
||||||
{
|
|
||||||
[JsonProperty("id")]
|
|
||||||
public int Id { get; set; }
|
|
||||||
[JsonProperty("name")]
|
|
||||||
public string Name { get; set; }
|
|
||||||
[JsonProperty("defaultStationNo")]
|
|
||||||
public int DefaultStationNo { get; set; }
|
|
||||||
[JsonProperty("minStationNo")]
|
|
||||||
public int MinStationNo { get; set; }
|
|
||||||
[JsonProperty("maxStationNo")]
|
|
||||||
public int MaxStationNo { get; set; }
|
|
||||||
[JsonProperty("class")]
|
|
||||||
public int Class { get; set; }
|
|
||||||
[JsonProperty("comPortParams")]
|
|
||||||
public ComPortParam ComPortParams { get; set; }
|
|
||||||
[JsonProperty("ethParams")]
|
|
||||||
public EthParam EthParams { get; set; }
|
|
||||||
[JsonProperty("byteOrders")]
|
|
||||||
public ByteOrder ByteOrders { get; set; }
|
|
||||||
[JsonProperty("supportedPlcs")]
|
|
||||||
public List<string> SupportedPlcs { get; set; }
|
|
||||||
[JsonProperty("regs")]
|
|
||||||
public List<AddressTypeReg> Regs { get; set; }
|
|
||||||
[JsonProperty("boardcastNo")]
|
|
||||||
public int BoardcastNo { get; set; }
|
|
||||||
[JsonProperty("mfr")]
|
|
||||||
public string Mfr { get; set; }
|
|
||||||
[JsonProperty("connType")]
|
|
||||||
public int ConnType { get; set; }
|
|
||||||
[JsonProperty("driverFileMd5")]
|
|
||||||
public string DriverFileMd5 { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ComPortParam
|
|
||||||
{
|
|
||||||
[JsonProperty("baudRate")]
|
|
||||||
public int BaudRate { get; set; }
|
|
||||||
[JsonProperty("dataBits")]
|
|
||||||
public int DataBits { get; set; }
|
|
||||||
[JsonProperty("stopBits")]
|
|
||||||
public int StopBits { get; set; }
|
|
||||||
[JsonProperty("parity")]
|
|
||||||
public int Parity { get; set; }
|
|
||||||
[JsonProperty("workingMode")]
|
|
||||||
public int WorkingMode { get; set; }
|
|
||||||
[JsonProperty("plcResponseTimeout")]
|
|
||||||
public int PlcResponseTimeout { get; set; }
|
|
||||||
[JsonProperty("protocalTimeout1")]
|
|
||||||
public int ProtocalTimeout1 { get; set; }
|
|
||||||
[JsonProperty("protocalTimeout2")]
|
|
||||||
public int ProtocalTimeout2 { get; set; }
|
|
||||||
[JsonProperty("maxPacketsWordReg")]
|
|
||||||
public int MaxPacketsWordReg { get; set; }
|
|
||||||
[JsonProperty("maxPacketsBitReg")]
|
|
||||||
public int MaxPacketsBitReg { get; set; }
|
|
||||||
[JsonProperty("assembleIntervalBitReg")]
|
|
||||||
public int AssembleIntervalBitReg { get; set; }
|
|
||||||
[JsonProperty("listRead")]
|
|
||||||
public bool ListRead { get; set; }
|
|
||||||
[JsonProperty("maxList")]
|
|
||||||
public int MaxList { get; set; }
|
|
||||||
[JsonProperty("protocalInterval")]
|
|
||||||
public int ProtocalInterval { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class EthParam
|
|
||||||
{
|
|
||||||
[JsonProperty("ip")]
|
|
||||||
public string Ip { get; set; }
|
|
||||||
[JsonProperty("port")]
|
|
||||||
public int Port { get; set; }
|
|
||||||
[JsonProperty("plcResponseTimeout")]
|
|
||||||
public int PlcResponseTimeout { get; set; }
|
|
||||||
[JsonProperty("protocalTimeout1")]
|
|
||||||
public int ProtocalTimeout1 { get; set; }
|
|
||||||
[JsonProperty("protocalTimeout2")]
|
|
||||||
public int ProtocalTimeout2 { get; set; }
|
|
||||||
[JsonProperty("maxPacketsWordReg")]
|
|
||||||
public int MaxPacketsWordReg { get; set; }
|
|
||||||
[JsonProperty("maxPacketsBitReg")]
|
|
||||||
public int MaxPacketsBitReg { get; set; }
|
|
||||||
[JsonProperty("assembleIntervalBitReg")]
|
|
||||||
public int AssembleIntervalBitReg { get; set; }
|
|
||||||
[JsonProperty("listRead")]
|
|
||||||
public bool ListRead { get; set; }
|
|
||||||
[JsonProperty("maxList")]
|
|
||||||
public int MaxList { get; set; }
|
|
||||||
[JsonProperty("protocalInterval")]
|
|
||||||
public int ProtocalInterval { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ByteOrder
|
|
||||||
{
|
|
||||||
[JsonProperty("u16")]
|
|
||||||
public int U16 { get; set; }
|
|
||||||
[JsonProperty("u32")]
|
|
||||||
public int U32 { get; set; }
|
|
||||||
[JsonProperty("float")]
|
|
||||||
public int Float { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AddressTypeReg
|
|
||||||
{
|
|
||||||
[JsonProperty("id")]
|
|
||||||
public int Id { get; set; }
|
|
||||||
[JsonProperty("name")]
|
|
||||||
public string Name { get; set; }
|
|
||||||
[JsonProperty("ioWidth")]
|
|
||||||
public int IoWidth { get; set; }
|
|
||||||
[JsonProperty("minMainAddr")]
|
|
||||||
public int MinMainAddr { get; set; }
|
|
||||||
[JsonProperty("maxMainAddr")]
|
|
||||||
public int MaxMainAddr { get; set; }
|
|
||||||
[JsonProperty("mainAddrType")]
|
|
||||||
public int MainAddrType { get; set; }
|
|
||||||
[JsonProperty("subAddrType")]
|
|
||||||
public int SubAddrType { get; set; }
|
|
||||||
[JsonProperty("isBigEndian")]
|
|
||||||
public bool IsBigEndian { get; set; }
|
|
||||||
[JsonProperty("subAddrLen")]
|
|
||||||
public int SubAddrLen { get; set; }
|
|
||||||
[JsonProperty("subIndexType")]
|
|
||||||
public int SubIndexType { get; set; }
|
|
||||||
[JsonProperty("minSubIndex")]
|
|
||||||
public int MinSubIndex { get; set; }
|
|
||||||
[JsonProperty("maxSubIndex")]
|
|
||||||
public int MaxSubIndex { get; set; }
|
|
||||||
[JsonProperty("hasSubIndex")]
|
|
||||||
public bool HasSubIndex { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,677 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNet.SignalR.Client;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using IdentityModel.Client;
|
|
||||||
|
|
||||||
namespace Modbus.Net.FBox
|
|
||||||
{
|
|
||||||
public struct SignalRSigninMsg
|
|
||||||
{
|
|
||||||
public string ClientId { get; set; }
|
|
||||||
public string ClientSecret { get; set; }
|
|
||||||
public string UserId { get; set; }
|
|
||||||
public string Password { get; set; }
|
|
||||||
public string SigninAdditionalValues { get; set; }
|
|
||||||
public SignalRServer SignalRServer { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FBoxConnector : BaseConnector
|
|
||||||
{
|
|
||||||
private TokenClient _oauth2;
|
|
||||||
private string _refreshToken;
|
|
||||||
|
|
||||||
private HttpClient _httpClient { get; set; }
|
|
||||||
private HttpClient _httpClient2 { get; set; }
|
|
||||||
private HubConnection _hubConnection { get; set; }
|
|
||||||
protected SignalRSigninMsg Msg { get; }
|
|
||||||
private DMonGroup _dataGroup { get; set; }
|
|
||||||
private string _groupUid { get; set; }
|
|
||||||
private string _boxUid { get; set; }
|
|
||||||
private string _boxNo { get; set; }
|
|
||||||
private int _boxSessionId { get; set; }
|
|
||||||
private int _connectionState { get; set; }
|
|
||||||
private Dictionary<string, double> _data { get; set; }
|
|
||||||
private Dictionary<string, Type> _dataType { get; set; }
|
|
||||||
|
|
||||||
private DateTime _timeStamp { get; set; } = DateTime.MinValue;
|
|
||||||
|
|
||||||
public override string ConnectionToken { get; }
|
|
||||||
|
|
||||||
//private AsyncLock _lock = new AsyncLock();
|
|
||||||
|
|
||||||
private Timer _timer;
|
|
||||||
|
|
||||||
private string MachineId => ConnectionToken.Split(',')[0];
|
|
||||||
|
|
||||||
private string LocalSequence => ConnectionToken.Split(',')[1];
|
|
||||||
|
|
||||||
private bool _connected;
|
|
||||||
public override bool IsConnected => _connected;
|
|
||||||
|
|
||||||
private Constants _constants;
|
|
||||||
private Constants Constants => _constants ?? (_constants = new Constants());
|
|
||||||
|
|
||||||
public FBoxConnector(string machineId, string localSequence, SignalRSigninMsg msg)
|
|
||||||
{
|
|
||||||
Constants.SignalRServer = msg.SignalRServer;
|
|
||||||
//System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
|
|
||||||
ConnectionToken = machineId + "," + localSequence;
|
|
||||||
Msg = msg;
|
|
||||||
_data = new Dictionary<string, double>();
|
|
||||||
_dataType = new Dictionary<string, Type>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void ChangeToken(object sender)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var tokenResponse = await _oauth2.RequestRefreshTokenAsync(_refreshToken);
|
|
||||||
_refreshToken = tokenResponse.RefreshToken;
|
|
||||||
_httpClient.SetBearerToken(tokenResponse.AccessToken);
|
|
||||||
|
|
||||||
_httpClient2.SetBearerToken(tokenResponse.AccessToken);
|
|
||||||
|
|
||||||
_hubConnection.Headers["Authorization"] = "Bearer " + tokenResponse.AccessToken;
|
|
||||||
await _hubConnection.Start();
|
|
||||||
await
|
|
||||||
_httpClient2.PostAsync(
|
|
||||||
"dmon/group/" + _dataGroup.Uid + "/start", null);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Retoken failed." + e.Message);
|
|
||||||
}
|
|
||||||
Console.WriteLine("Retoken success.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Connect()
|
|
||||||
{
|
|
||||||
return AsyncHelper.RunSync(ConnectAsync);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<bool> ConnectAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
_oauth2 = new TokenClient(
|
|
||||||
Constants.TokenEndpoint,
|
|
||||||
Msg.ClientId,
|
|
||||||
Msg.ClientSecret
|
|
||||||
);
|
|
||||||
var tokenResponse = await _oauth2.RequestResourceOwnerPasswordAsync
|
|
||||||
(
|
|
||||||
Msg.UserId,
|
|
||||||
Msg.Password,
|
|
||||||
Msg.SigninAdditionalValues
|
|
||||||
);
|
|
||||||
if (tokenResponse != null)
|
|
||||||
{
|
|
||||||
_refreshToken = tokenResponse.RefreshToken;
|
|
||||||
if (await CallService(Msg, tokenResponse.AccessToken))
|
|
||||||
{
|
|
||||||
await
|
|
||||||
_httpClient2.PostAsync(
|
|
||||||
"dmon/group/" + _dataGroup.Uid + "/start", null);
|
|
||||||
_connected = true;
|
|
||||||
_timer = new Timer(ChangeToken, null, 3600*1000*4, 3600*1000*4);
|
|
||||||
Console.WriteLine("SignalR Connected success");
|
|
||||||
_connectionState = 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_connectionState = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_oauth2 = null;
|
|
||||||
Console.WriteLine("SignalR Connected failed " + e.Message);
|
|
||||||
Clear();
|
|
||||||
_connectionState = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<bool> CallService(SignalRSigninMsg msg, string token)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var guid = Guid.NewGuid().ToString();
|
|
||||||
|
|
||||||
var baseAddress = Constants.AspNetWebApiSampleApi;
|
|
||||||
|
|
||||||
_httpClient = new HttpClient
|
|
||||||
{
|
|
||||||
BaseAddress = new Uri(baseAddress)
|
|
||||||
};
|
|
||||||
|
|
||||||
_httpClient.SetBearerToken(token);
|
|
||||||
|
|
||||||
//var response = await _httpClient.GetStringAsync("device/spec");
|
|
||||||
|
|
||||||
//List<DeviceSpecSource> deviceSpecs = JsonConvert.DeserializeObject<List<DeviceSpecSource>>(response);
|
|
||||||
//deviceSpecs = deviceSpecs.OrderBy(p => p.Id).ToList();
|
|
||||||
|
|
||||||
|
|
||||||
var response = await _httpClient.GetStringAsync("boxgroup");
|
|
||||||
|
|
||||||
List<BoxGroup> boxGroups = JsonConvert.DeserializeObject<List<BoxGroup>>(response);
|
|
||||||
if (boxGroups == null) return false;
|
|
||||||
|
|
||||||
foreach (var boxGroup in boxGroups)
|
|
||||||
{
|
|
||||||
var boxes = boxGroup.BoxRegs;
|
|
||||||
if (boxes == null) continue;
|
|
||||||
foreach (var box in boxes)
|
|
||||||
{
|
|
||||||
var sessionId = box.Box.CurrentSessionId;
|
|
||||||
var baseUrl = box.Box.CommServer.ApiBaseUrl;
|
|
||||||
var signalrUrl = box.Box.CommServer.SignalRUrl;
|
|
||||||
var boxUid = box.Box.Uid;
|
|
||||||
var boxNo = box.Box.BoxNo;
|
|
||||||
|
|
||||||
if (boxNo != MachineId) continue;
|
|
||||||
|
|
||||||
_httpClient2 = new HttpClient
|
|
||||||
{
|
|
||||||
BaseAddress = new Uri(baseUrl)
|
|
||||||
};
|
|
||||||
_httpClient2.SetBearerToken(token);
|
|
||||||
_httpClient2.DefaultRequestHeaders.Add("X-FBox-ClientId", guid);
|
|
||||||
|
|
||||||
response = await _httpClient2.GetStringAsync("box/" + box.Box.Uid + "/dmon/def/grouped");
|
|
||||||
|
|
||||||
|
|
||||||
List<DMonGroup> dataGroups = JsonConvert.DeserializeObject<List<DMonGroup>>(response);
|
|
||||||
foreach (var dataGroup in dataGroups)
|
|
||||||
{
|
|
||||||
if (dataGroup.Name == LocalSequence)
|
|
||||||
{
|
|
||||||
_dataGroup = dataGroup;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_boxSessionId = sessionId;
|
|
||||||
_boxNo = boxNo;
|
|
||||||
|
|
||||||
_hubConnection = new HubConnection(signalrUrl);
|
|
||||||
_hubConnection.Headers.Add("Authorization", "Bearer " + token);
|
|
||||||
_hubConnection.Headers.Add("X-FBox-ClientId", guid);
|
|
||||||
_hubConnection.Headers.Add("X-FBox-Session", sessionId.ToString());
|
|
||||||
|
|
||||||
IHubProxy dataHubProxy = _hubConnection.CreateHubProxy("clientHub");
|
|
||||||
dataHubProxy.On<int, List<GetValue>>("dMonUpdateValue",
|
|
||||||
(boxSessionId, values) =>
|
|
||||||
{
|
|
||||||
|
|
||||||
//#if DEBUG
|
|
||||||
//Console.WriteLine($"Box session {boxSessionId} return at {DateTime.Now}");
|
|
||||||
//#endif
|
|
||||||
|
|
||||||
_timeStamp = DateTime.Now;
|
|
||||||
|
|
||||||
foreach (var value in values)
|
|
||||||
{
|
|
||||||
if (value.Status != 0)
|
|
||||||
{
|
|
||||||
lock (_data)
|
|
||||||
{
|
|
||||||
var dMonEntry =
|
|
||||||
_dataGroup.DMonEntries.FirstOrDefault(
|
|
||||||
p => p.Uid == value.Id);
|
|
||||||
if (dMonEntry != null)
|
|
||||||
{
|
|
||||||
if (_data.ContainsKey(dMonEntry.Desc))
|
|
||||||
{
|
|
||||||
_data.Remove(dMonEntry.Desc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
lock (_data)
|
|
||||||
{
|
|
||||||
if (_dataGroup.DMonEntries.Any(p => p.Uid == value.Id))
|
|
||||||
{
|
|
||||||
if (_data == null)
|
|
||||||
{
|
|
||||||
_data = new Dictionary<string, double>();
|
|
||||||
}
|
|
||||||
|
|
||||||
var dMonEntry = _dataGroup.DMonEntries.FirstOrDefault(
|
|
||||||
p => p.Uid == value.Id);
|
|
||||||
|
|
||||||
if (value.Value.HasValue && dMonEntry != null)
|
|
||||||
{
|
|
||||||
if (_data.ContainsKey(dMonEntry.Desc))
|
|
||||||
{
|
|
||||||
_data[dMonEntry.Desc] = value.Value.Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_data.Add(dMonEntry.Desc, value.Value.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
dataHubProxy.On<int, string, int, int>("boxConnectionStateChanged",
|
|
||||||
async (newConnectionToken, getBoxUid, oldStatus, newStatus) =>
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
Console.WriteLine(
|
|
||||||
$"Box uid {getBoxUid} change state at {DateTime.Now} new connectionToken {newConnectionToken} newStatus {newStatus}");
|
|
||||||
#endif
|
|
||||||
sessionId = newConnectionToken;
|
|
||||||
_boxSessionId = sessionId;
|
|
||||||
|
|
||||||
//_connectionState = newStatus;
|
|
||||||
|
|
||||||
if (!IsConnected || _httpClient2 == null || _hubConnection == null)
|
|
||||||
{
|
|
||||||
Clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (
|
|
||||||
!_httpClient2.DefaultRequestHeaders.TryAddWithoutValidation("X-FBox-Session",
|
|
||||||
sessionId.ToString()))
|
|
||||||
{
|
|
||||||
_httpClient2.DefaultRequestHeaders.Remove("X-FBox-Session");
|
|
||||||
}
|
|
||||||
_httpClient2.DefaultRequestHeaders.Add("X-FBox-Session", sessionId.ToString());
|
|
||||||
|
|
||||||
|
|
||||||
_hubConnection.Headers["X-FBox-Session"] = sessionId.ToString();
|
|
||||||
await _hubConnection.Start();
|
|
||||||
_data.Clear();
|
|
||||||
//if (newStatus == 1)
|
|
||||||
//{
|
|
||||||
//if (IsConnected)
|
|
||||||
//{
|
|
||||||
await
|
|
||||||
_httpClient2.PostAsync(
|
|
||||||
"dmon/group/" + _dataGroup.Uid + "/start", null);
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
//lock (_data)
|
|
||||||
//{
|
|
||||||
//_data.Clear();
|
|
||||||
//}
|
|
||||||
//await DisconnectAsync();
|
|
||||||
//_connected = false;
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine("SignalR boxSessionId change error: " + ex.Message);
|
|
||||||
await DisconnectAsync();
|
|
||||||
_connected = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_hubConnection.Error += async ex =>
|
|
||||||
{
|
|
||||||
Console.WriteLine(@"SignalR error: {0}", ex.Message);
|
|
||||||
await DisconnectAsync();
|
|
||||||
_connected = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
_hubConnection.Closed += () =>
|
|
||||||
{
|
|
||||||
_hubConnection.Dispose();
|
|
||||||
_connected = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
ServicePointManager.DefaultConnectionLimit = 10;
|
|
||||||
|
|
||||||
if (_dataGroup == null) return false;
|
|
||||||
|
|
||||||
var groupUid = _dataGroup.Uid;
|
|
||||||
var groupName = _dataGroup.Name;
|
|
||||||
|
|
||||||
if (groupName != "(Default)" && groupName != "默认组")
|
|
||||||
{
|
|
||||||
_groupUid = groupUid;
|
|
||||||
}
|
|
||||||
if (groupName != "(Default)" && groupName != "默认组")
|
|
||||||
{
|
|
||||||
_boxUid = boxUid;
|
|
||||||
}
|
|
||||||
|
|
||||||
_dataType = new Dictionary<string, Type>();
|
|
||||||
if (_dataGroup.DMonEntries != null)
|
|
||||||
{
|
|
||||||
foreach (var dMonEntry in _dataGroup.DMonEntries)
|
|
||||||
{
|
|
||||||
Type type;
|
|
||||||
switch (dMonEntry.DataType)
|
|
||||||
{
|
|
||||||
//位
|
|
||||||
case 0:
|
|
||||||
{
|
|
||||||
type = typeof (bool);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//16位无符号
|
|
||||||
case 1:
|
|
||||||
{
|
|
||||||
type = typeof (ushort);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//16位有符号
|
|
||||||
case 2:
|
|
||||||
{
|
|
||||||
type = typeof (short);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//32位无符号
|
|
||||||
case 11:
|
|
||||||
{
|
|
||||||
type = typeof (uint);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//32位有符号
|
|
||||||
case 12:
|
|
||||||
{
|
|
||||||
type = typeof (int);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//16位BCD
|
|
||||||
case 3:
|
|
||||||
{
|
|
||||||
type = typeof (short);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//32位BCD
|
|
||||||
case 13:
|
|
||||||
{
|
|
||||||
type = typeof (int);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//浮点数
|
|
||||||
case 16:
|
|
||||||
{
|
|
||||||
type = typeof (float);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//16位16进制
|
|
||||||
case 4:
|
|
||||||
{
|
|
||||||
type = typeof (short);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//32位16进制
|
|
||||||
case 14:
|
|
||||||
{
|
|
||||||
type = typeof (int);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//16位2进制
|
|
||||||
case 5:
|
|
||||||
{
|
|
||||||
type = typeof (short);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//32位2进制
|
|
||||||
case 15:
|
|
||||||
{
|
|
||||||
type = typeof (int);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
type = typeof (short);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_dataType.ContainsKey(dMonEntry.Desc))
|
|
||||||
{
|
|
||||||
_dataType.Add(dMonEntry.Desc, type);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_dataType[dMonEntry.Desc] = type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await _hubConnection.Start();
|
|
||||||
await dataHubProxy.Invoke("updateClientId", guid);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Disconnect()
|
|
||||||
{
|
|
||||||
return AsyncHelper.RunSync(DisconnectAsync);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> DisconnectAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_httpClient2 != null)
|
|
||||||
{
|
|
||||||
await
|
|
||||||
_httpClient2.PostAsync(
|
|
||||||
"dmon/group/" + _groupUid + "/stop",
|
|
||||||
null);
|
|
||||||
}
|
|
||||||
Clear();
|
|
||||||
Console.WriteLine("SignalR Disconnect success");
|
|
||||||
_connectionState = 0;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Console.WriteLine("SignalR Disconnect failed " + e.Message);
|
|
||||||
Clear();
|
|
||||||
_connectionState = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void Clear()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_hubConnection != null)
|
|
||||||
{
|
|
||||||
await Task.Run(() => _hubConnection.Stop(TimeSpan.FromSeconds(10)));
|
|
||||||
_hubConnection = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
_hubConnection = null;
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_httpClient != null)
|
|
||||||
{
|
|
||||||
_httpClient.Dispose();
|
|
||||||
_httpClient = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
//ignore
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_httpClient2 != null)
|
|
||||||
{
|
|
||||||
_httpClient2.Dispose();
|
|
||||||
_httpClient2 = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
//ignore
|
|
||||||
}
|
|
||||||
if (_data != null)
|
|
||||||
{
|
|
||||||
lock (_data)
|
|
||||||
{
|
|
||||||
_data.Clear();
|
|
||||||
_dataType.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_timeStamp = DateTime.MinValue;
|
|
||||||
_connected = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_timer != null)
|
|
||||||
{
|
|
||||||
_timer.Dispose();
|
|
||||||
_timer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
//ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool SendMsgWithoutReturn(byte[] message)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task<bool> SendMsgWithoutReturnAsync(byte[] message)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override byte[] SendMsg(byte[] message)
|
|
||||||
{
|
|
||||||
return AsyncHelper.RunSync(() => SendMsgAsync(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<byte[]> SendMsgAsync(byte[] message)
|
|
||||||
{
|
|
||||||
if (_httpClient == null)
|
|
||||||
{
|
|
||||||
await DisconnectAsync();
|
|
||||||
_connected = false;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_hubConnection.State == ConnectionState.Disconnected)
|
|
||||||
{
|
|
||||||
await _hubConnection.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
var formater = new AddressFormaterFBox();
|
|
||||||
var translator = new AddressTranslatorFBox();
|
|
||||||
|
|
||||||
byte[] ans;
|
|
||||||
|
|
||||||
//if (_connectionState != 1)
|
|
||||||
//{
|
|
||||||
//await DisconnectAsync();
|
|
||||||
//_connected = false;
|
|
||||||
//Console.WriteLine($"Return Value Rejected with connectionToken {ConnectionToken}");
|
|
||||||
//return null;
|
|
||||||
//}
|
|
||||||
|
|
||||||
if (_timeStamp == DateTime.MinValue)
|
|
||||||
{
|
|
||||||
return Encoding.ASCII.GetBytes("NoData");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DateTime.Now - _timeStamp > TimeSpan.FromMinutes(2))
|
|
||||||
{
|
|
||||||
Console.WriteLine("SignalR Timeout: {0} {1} {2}", _timeStamp, DateTime.Now, ConnectionToken);
|
|
||||||
if (_connectionState != 1)
|
|
||||||
{
|
|
||||||
await DisconnectAsync();
|
|
||||||
_connected = false;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Dictionary<string, double> machineDataValue;
|
|
||||||
lock (_data)
|
|
||||||
{
|
|
||||||
machineDataValue = _data.ToDictionary(pair => pair.Key, pair => pair.Value);
|
|
||||||
}
|
|
||||||
var machineDataType = _dataType;
|
|
||||||
if (machineDataType == null || machineDataType.Count == 0)
|
|
||||||
{
|
|
||||||
await DisconnectAsync();
|
|
||||||
_connected = false;
|
|
||||||
Console.WriteLine($"Return Value Rejected with connectionToken {ConnectionToken}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (machineDataValue == null || machineDataValue.Count == 0)
|
|
||||||
{
|
|
||||||
return Encoding.ASCII.GetBytes("NoData");
|
|
||||||
}
|
|
||||||
|
|
||||||
int pos = 0;
|
|
||||||
int area = BigEndianValueHelper.Instance.GetInt(message, ref pos);
|
|
||||||
int address = BigEndianValueHelper.Instance.GetInt(message, ref pos);
|
|
||||||
//short count = BigEndianValueHelper.Instance.GetShort(message, ref pos);
|
|
||||||
object[] dataAns = new object[1];
|
|
||||||
try
|
|
||||||
{
|
|
||||||
dataAns[0] =
|
|
||||||
Convert.ChangeType(
|
|
||||||
machineDataValue[formater.FormatAddress(translator.GetAreaName(area), address)],
|
|
||||||
machineDataType[formater.FormatAddress(translator.GetAreaName(area), address)]);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
if (machineDataValue.Count != machineDataType.Count)
|
|
||||||
{
|
|
||||||
await
|
|
||||||
_httpClient2.PostAsync(
|
|
||||||
"dmon/group/" + _dataGroup.Uid + "/start", null);
|
|
||||||
}
|
|
||||||
return Encoding.ASCII.GetBytes("NoData");
|
|
||||||
//dataAns[0] =
|
|
||||||
//Convert.ChangeType(
|
|
||||||
//0,
|
|
||||||
//machineDataType[formater.FormatAddress(translator.GetAreaName(area), address)]);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ans = BigEndianValueHelper.Instance.ObjectArrayToByteArray(dataAns);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ans;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Modbus.Net.FBox
|
|
||||||
{
|
|
||||||
public class FBoxMachine : BaseMachine
|
|
||||||
{
|
|
||||||
public FBoxMachine(FBoxType fBoxType, string connectionString, string localSequence, SignalRSigninMsg msg,
|
|
||||||
IEnumerable<AddressUnit> getAddresses, bool keepConnect) : base(getAddresses, keepConnect)
|
|
||||||
{
|
|
||||||
AddressFormater = new AddressFormaterFBox();
|
|
||||||
AddressCombiner = new AddressCombinerFBox();
|
|
||||||
BaseUtility = new FBoxUtility(fBoxType, connectionString, localSequence, CommunicateAddresses, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Modbus.Net;
|
|
||||||
using Modbus.Net.FBox;
|
|
||||||
|
|
||||||
namespace Modbus.Net.FBox
|
|
||||||
{
|
|
||||||
public abstract class FBoxProtocal : BaseProtocal
|
|
||||||
{
|
|
||||||
public override bool Connect()
|
|
||||||
{
|
|
||||||
return ProtocalLinker.Connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<bool> ConnectAsync()
|
|
||||||
{
|
|
||||||
return await ProtocalLinker.ConnectAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ReadRequestFBoxInputStruct : InputStruct
|
|
||||||
{
|
|
||||||
public ReadRequestFBoxInputStruct(string startAddress, ushort getCount, AddressTranslator addressTranslator)
|
|
||||||
{
|
|
||||||
var address = addressTranslator.AddressTranslate(startAddress, true);
|
|
||||||
Address = address.Address;
|
|
||||||
Area = address.Area;
|
|
||||||
GetCount = getCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Area { get; set; }
|
|
||||||
public int Address { get; set; }
|
|
||||||
public ushort GetCount { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class ReadRequestFBoxOutputStruct : OutputStruct
|
|
||||||
{
|
|
||||||
public ReadRequestFBoxOutputStruct(byte[] value)
|
|
||||||
{
|
|
||||||
GetValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] GetValue { get; private set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ReadRequestFBoxProtocal : SpecialProtocalUnit
|
|
||||||
{
|
|
||||||
public override byte[] Format(InputStruct message)
|
|
||||||
{
|
|
||||||
var r_message = (ReadRequestFBoxInputStruct) message;
|
|
||||||
return Format(r_message.Area, r_message.Address, r_message.GetCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override OutputStruct Unformat(byte[] messageBytes, ref int pos)
|
|
||||||
{
|
|
||||||
var values = new byte[messageBytes.Length];
|
|
||||||
Array.Copy(messageBytes, pos, values, 0, messageBytes.Length);
|
|
||||||
return new ReadRequestFBoxOutputStruct(values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Modbus.Net.FBox
|
|
||||||
{
|
|
||||||
public class FBoxProtocalLinker : ProtocalLinker
|
|
||||||
{
|
|
||||||
protected FBoxProtocalLinker(string machineId, string localSequence, SignalRSigninMsg msg)
|
|
||||||
{
|
|
||||||
_baseConnector = new FBoxConnector(machineId, localSequence, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CheckRight(byte[] content)
|
|
||||||
{
|
|
||||||
if (content != null && content.Length == 6 && Encoding.ASCII.GetString(content) == "NoData")
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return base.CheckRight(content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Modbus.Net.FBox
|
|
||||||
{
|
|
||||||
public class FBoxSignalRProtocal : FBoxProtocal
|
|
||||||
{
|
|
||||||
public FBoxSignalRProtocal(string machineId, string localSequence, SignalRSigninMsg msg)
|
|
||||||
{
|
|
||||||
ProtocalLinker = new FBoxSignalRProtocalLinker(machineId, localSequence, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Modbus.Net.FBox
|
|
||||||
{
|
|
||||||
public class FBoxSignalRProtocalLinker : FBoxProtocalLinker
|
|
||||||
{
|
|
||||||
public FBoxSignalRProtocalLinker(string machineId, string localSequence, SignalRSigninMsg msg) : base(machineId, localSequence, msg)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Modbus.Net.FBox
|
|
||||||
{
|
|
||||||
public enum FBoxType
|
|
||||||
{
|
|
||||||
AddressSync = 0,
|
|
||||||
CommunicationTagSync = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FBoxUtility :BaseUtility
|
|
||||||
{
|
|
||||||
private FBoxType _fboxType;
|
|
||||||
private SignalRSigninMsg SigninMsg { get; set; }
|
|
||||||
|
|
||||||
public string LocalSequence { get; set; }
|
|
||||||
|
|
||||||
protected IEnumerable<CommunicationUnit> CommunicationUnits { get; set; }
|
|
||||||
public FBoxType ConnectionType
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _fboxType;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_fboxType = value;
|
|
||||||
switch (_fboxType)
|
|
||||||
{
|
|
||||||
case FBoxType.AddressSync:
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
case FBoxType.CommunicationTagSync:
|
|
||||||
{
|
|
||||||
Wrapper = new FBoxSignalRProtocal(ConnectionString, LocalSequence, SigninMsg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FBoxUtility(FBoxType fBoxType, string connectionString, string localSequence, IEnumerable<CommunicationUnit> communicationUnits, SignalRSigninMsg msg)
|
|
||||||
{
|
|
||||||
ConnectionString = connectionString;
|
|
||||||
LocalSequence = localSequence;
|
|
||||||
CommunicationUnits = communicationUnits.AsEnumerable();
|
|
||||||
SigninMsg = msg;
|
|
||||||
|
|
||||||
ConnectionType = fBoxType;
|
|
||||||
AddressTranslator = new AddressTranslatorFBox();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetConnectionType(int connectionType)
|
|
||||||
{
|
|
||||||
ConnectionType = (FBoxType) connectionType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<GetDataReturnDef> GetDatasAsync(byte belongAddress, byte masterAddress, string startAddress, int getByteCount)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var readRequestFBoxInputStruct = new ReadRequestFBoxInputStruct(startAddress, (ushort)getByteCount, AddressTranslator);
|
|
||||||
var readRequestSiemensOutputStruct =
|
|
||||||
await
|
|
||||||
Wrapper.SendReceiveAsync(Wrapper[typeof(ReadRequestFBoxProtocal)], readRequestFBoxInputStruct) as ReadRequestFBoxOutputStruct;
|
|
||||||
return new GetDataReturnDef()
|
|
||||||
{
|
|
||||||
ReturnValue = readRequestSiemensOutputStruct?.GetValue,
|
|
||||||
IsLittleEndian = Wrapper[typeof (ReadRequestFBoxProtocal)].IsLittleEndian
|
|
||||||
};
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task<bool> SetDatasAsync(byte belongAddress, byte masterAddress, string startAddress, object[] setContents)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
|
||||||
<PropertyGroup>
|
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProjectGuid>{492A9231-4184-4CFF-8C01-72B8F73CDACA}</ProjectGuid>
|
|
||||||
<OutputType>Library</OutputType>
|
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
|
||||||
<RootNamespace>Modbus.Net.FBox</RootNamespace>
|
|
||||||
<AssemblyName>Modbus.Net.FBox</AssemblyName>
|
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
|
||||||
<FileAlignment>512</FileAlignment>
|
|
||||||
<TargetFrameworkProfile />
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\Debug\</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<OutputPath>bin\Release\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="IdentityModel, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\IdentityModel.1.10.0\lib\net45\IdentityModel.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Microsoft.AspNet.SignalR.Client, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\Microsoft.AspNet.SignalR.Client.2.2.0\lib\net45\Microsoft.AspNet.SignalR.Client.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System" />
|
|
||||||
<Reference Include="System.Core" />
|
|
||||||
<Reference Include="System.IdentityModel" />
|
|
||||||
<Reference Include="System.Xml.Linq" />
|
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
|
||||||
<Reference Include="Microsoft.CSharp" />
|
|
||||||
<Reference Include="System.Data" />
|
|
||||||
<Reference Include="System.Net.Http" />
|
|
||||||
<Reference Include="System.Xml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Include="AddressCombinerFBox.cs" />
|
|
||||||
<Compile Include="AddressFormaterFBox.cs" />
|
|
||||||
<Compile Include="AddressTranslatorFBox.cs" />
|
|
||||||
<Compile Include="Constants.cs" />
|
|
||||||
<Compile Include="Entity.cs" />
|
|
||||||
<Compile Include="FBoxConnector.cs" />
|
|
||||||
<Compile Include="FBoxMachine.cs" />
|
|
||||||
<Compile Include="FBoxProtocal.cs" />
|
|
||||||
<Compile Include="FBoxProtocalLinker.cs" />
|
|
||||||
<Compile Include="FBoxSignalRProtocal.cs" />
|
|
||||||
<Compile Include="FBoxSignalRProtocalLinker.cs" />
|
|
||||||
<Compile Include="FBoxUtility.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="app.config" />
|
|
||||||
<None Include="Modbus.Net.FBox.nuspec">
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
</None>
|
|
||||||
<None Include="packages.config" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Modbus.Net\Modbus.Net.csproj">
|
|
||||||
<Project>{124ebef2-8960-4447-84cf-1d683b1ef7cc}</Project>
|
|
||||||
<Name>Modbus.Net</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
|
||||||
<Target Name="BeforeBuild">
|
|
||||||
</Target>
|
|
||||||
<Target Name="AfterBuild">
|
|
||||||
</Target>
|
|
||||||
-->
|
|
||||||
</Project>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<package >
|
|
||||||
<metadata>
|
|
||||||
<id>Modbus.Net.FBox</id>
|
|
||||||
<version>1.1.2</version>
|
|
||||||
<title>Modbus.Net.FBox</title>
|
|
||||||
<authors>Chris L.(Luo Sheng)</authors>
|
|
||||||
<owners>Hangzhou Delian Information and Science Technology Co., Ltd.</owners>
|
|
||||||
<licenseUrl>https://github.com/parallelbgls/Modbus.Net/blob/master/LICENSE.md</licenseUrl>
|
|
||||||
<projectUrl>https://github.com/parallelbgls/Modbus.Net/tree/master/Modbus.Net/Modbus.Net.FBox</projectUrl>
|
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
|
||||||
<description>Modbus.Net FBox Implementation</description>
|
|
||||||
<copyright>Copyright 2015 Hangzhou Delian Science and Technology Co.,Ltd.</copyright>
|
|
||||||
<tags>hardware communicate protocal fbox Delian</tags>
|
|
||||||
<dependencies>
|
|
||||||
<dependency id="Modbus.Net" version="1.1.0"></dependency>
|
|
||||||
<dependency id="IdentityModel" version="1.10.0"></dependency>
|
|
||||||
<dependency id="Microsoft.AspNet.SignalR.Client" version="2.2.0"></dependency>
|
|
||||||
</dependencies>
|
|
||||||
</metadata>
|
|
||||||
<files>
|
|
||||||
<file src="bin\Release\Modbus.Net.FBox.dll" target="lib\net45\Modbus.Net.FBox.dll" />
|
|
||||||
</files>
|
|
||||||
</package>
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
// 有关程序集的一般信息由以下
|
|
||||||
// 控制。更改这些特性值可修改
|
|
||||||
// 与程序集关联的信息。
|
|
||||||
[assembly: AssemblyTitle("Modbus.Net.FBox")]
|
|
||||||
[assembly: AssemblyDescription("")]
|
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("")]
|
|
||||||
[assembly: AssemblyProduct("Modbus.Net.FBox")]
|
|
||||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
|
||||||
[assembly: AssemblyTrademark("")]
|
|
||||||
[assembly: AssemblyCulture("")]
|
|
||||||
|
|
||||||
//将 ComVisible 设置为 false 将使此程序集中的类型
|
|
||||||
//对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型,
|
|
||||||
//请将此类型的 ComVisible 特性设置为 true。
|
|
||||||
[assembly: ComVisible(false)]
|
|
||||||
|
|
||||||
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
|
|
||||||
[assembly: Guid("492a9231-4184-4cff-8c01-72b8f73cdaca")]
|
|
||||||
|
|
||||||
// 程序集的版本信息由下列四个值组成:
|
|
||||||
//
|
|
||||||
// 主版本
|
|
||||||
// 次版本
|
|
||||||
// 生成号
|
|
||||||
// 修订号
|
|
||||||
//
|
|
||||||
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
|
|
||||||
// 方法是按如下所示使用“*”: :
|
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
|
||||||
[assembly: AssemblyVersion("1.1.0")]
|
|
||||||
[assembly: AssemblyFileVersion("1.1.0")]
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<configuration>
|
|
||||||
<runtime>
|
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
|
||||||
<dependentAssembly>
|
|
||||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
|
||||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
|
|
||||||
</dependentAssembly>
|
|
||||||
</assemblyBinding>
|
|
||||||
</runtime>
|
|
||||||
</configuration>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<packages>
|
|
||||||
<package id="IdentityModel" version="1.10.0" targetFramework="net45" />
|
|
||||||
<package id="Microsoft.AspNet.SignalR.Client" version="2.2.0" targetFramework="net452" />
|
|
||||||
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net45" />
|
|
||||||
</packages>
|
|
||||||
BIN
Modbus.Net/Modbus.Net.OPC.1.1.1.nupkg
Normal file
BIN
Modbus.Net/Modbus.Net.OPC.1.1.1.nupkg
Normal file
Binary file not shown.
@@ -1,8 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Hylasoft.Opc.Common;
|
||||||
|
using Hylasoft.Opc.Da;
|
||||||
|
using Opc;
|
||||||
using Opc.Da;
|
using Opc.Da;
|
||||||
|
|
||||||
namespace Hylasoft.Opc.Da
|
namespace Modbus.Net.OPC
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -25,7 +28,7 @@ namespace Hylasoft.Opc.Da
|
|||||||
public OpcValueResult Read(string tag)
|
public OpcValueResult Read(string tag)
|
||||||
{
|
{
|
||||||
var item = new Item { ItemName = tag };
|
var item = new Item { ItemName = tag };
|
||||||
var result = _server.Read(new[] { item })[0];
|
var result = Server.Read(new[] { item })[0];
|
||||||
CheckResult(result, tag);
|
CheckResult(result, tag);
|
||||||
return new OpcValueResult()
|
return new OpcValueResult()
|
||||||
{
|
{
|
||||||
@@ -46,5 +49,13 @@ namespace Hylasoft.Opc.Da
|
|||||||
public MyDaClient(Uri serverUrl) : base(serverUrl)
|
public MyDaClient(Uri serverUrl) : base(serverUrl)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void CheckResult(IResult result, string tag)
|
||||||
|
{
|
||||||
|
if (result == null)
|
||||||
|
throw new OpcException("The server replied with an empty response");
|
||||||
|
if (result.ResultID.ToString() != "S_OK")
|
||||||
|
throw new OpcException(string.Format("Invalid response from the server. (Response Status: {0}, Opc Tag: {1})", result.ResultID, tag));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,23 +31,33 @@
|
|||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Opc.Ua.Client">
|
<Reference Include="h-opc, Version=0.6.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages_local\Opc.Ua.Client.dll</HintPath>
|
<HintPath>..\packages\H.Opc.0.7.0\lib\h-opc.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Opc.Ua.Configuration">
|
<Reference Include="Opc.Ua.Client, Version=1.2.334.4, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages_local\Opc.Ua.Configuration.dll</HintPath>
|
<HintPath>..\packages\H.Opc.0.7.0\lib\Opc.Ua.Client.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Opc.Ua.Core">
|
<Reference Include="Opc.Ua.Configuration, Version=1.2.334.4, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages_local\Opc.Ua.Core.dll</HintPath>
|
<HintPath>..\packages\H.Opc.0.7.0\lib\Opc.Ua.Configuration.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="OpcComRcw">
|
<Reference Include="Opc.Ua.Core, Version=1.2.334.4, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages_local\OpcComRcw.dll</HintPath>
|
<HintPath>..\packages\H.Opc.0.7.0\lib\Opc.Ua.Core.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="OpcNetApi">
|
<Reference Include="OpcComRcw, Version=2.0.105.1, Culture=neutral, PublicKeyToken=9a40e993cbface53, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages_local\OpcNetApi.dll</HintPath>
|
<HintPath>..\packages\H.Opc.0.7.0\lib\OpcComRcw.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="OpcNetApi.Com">
|
<Reference Include="OpcNetApi, Version=2.1.105.1, Culture=neutral, PublicKeyToken=9a40e993cbface53, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages_local\OpcNetApi.Com.dll</HintPath>
|
<HintPath>..\packages\H.Opc.0.7.0\lib\OpcNetApi.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="OpcNetApi.Com, Version=2.1.105.1, Culture=neutral, PublicKeyToken=9a40e993cbface53, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\H.Opc.0.7.0\lib\OpcNetApi.Com.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
@@ -71,10 +81,6 @@
|
|||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\h-opc\h-opc.csproj">
|
|
||||||
<Project>{4f43b6f0-0c32-4c34-978e-9b8b5b0b6e80}</Project>
|
|
||||||
<Name>h-opc</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
<ProjectReference Include="..\ModBus.Net\Modbus.Net.csproj">
|
<ProjectReference Include="..\ModBus.Net\Modbus.Net.csproj">
|
||||||
<Project>{124ebef2-8960-4447-84cf-1d683b1ef7cc}</Project>
|
<Project>{124ebef2-8960-4447-84cf-1d683b1ef7cc}</Project>
|
||||||
<Name>Modbus.Net</Name>
|
<Name>Modbus.Net</Name>
|
||||||
@@ -82,6 +88,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="Modbus.Net.OPC.nuspec" />
|
<None Include="Modbus.Net.OPC.nuspec" />
|
||||||
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Modbus.Net.OPC</id>
|
<id>Modbus.Net.OPC</id>
|
||||||
<version>1.1.0</version>
|
<version>1.1.1</version>
|
||||||
<title>Modbus.Net.OPC</title>
|
<title>Modbus.Net.OPC</title>
|
||||||
<authors>Chris L.(Luo Sheng)</authors>
|
<authors>Chris L.(Luo Sheng)</authors>
|
||||||
<owners>Hangzhou Delian Information and Science Technology</owners>
|
<owners>Hangzhou Delian Information and Science Technology</owners>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<tags>hardware communicate protocal OPC DA Delian</tags>
|
<tags>hardware communicate protocal OPC DA Delian</tags>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency id="Modbus.Net" version="1.1.0" />
|
<dependency id="Modbus.Net" version="1.1.0" />
|
||||||
<dependency id="Modbus.Net.h-opc" version="0.4.0" />
|
<dependency id="H.Opc" version="0.7.0" />
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
|
|||||||
4
Modbus.Net/Modbus.Net.OPC/packages.config
Normal file
4
Modbus.Net/Modbus.Net.OPC/packages.config
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="H.Opc" version="0.7.0" targetFramework="net45" />
|
||||||
|
</packages>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 14
|
# Visual Studio 14
|
||||||
VisualStudioVersion = 14.0.24720.0
|
VisualStudioVersion = 14.0.25420.1
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NA200H.UI.ConsoleApp", "NA200H.UI.Console\NA200H.UI.ConsoleApp.csproj", "{D06F6A34-93F6-4139-B485-8F5686E4E2C9}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NA200H.UI.ConsoleApp", "NA200H.UI.Console\NA200H.UI.ConsoleApp.csproj", "{D06F6A34-93F6-4139-B485-8F5686E4E2C9}"
|
||||||
EndProject
|
EndProject
|
||||||
@@ -19,16 +19,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
..\README.md = ..\README.md
|
..\README.md = ..\README.md
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "h-opc", "h-opc\h-opc.csproj", "{4F43B6F0-0C32-4C34-978E-9B8B5B0B6E80}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modbus.Net.Modbus", "Modbus.Net.Modbus\Modbus.Net.Modbus.csproj", "{FDCA72BA-6D06-4DE0-B873-C11C4AC853AD}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modbus.Net.Modbus", "Modbus.Net.Modbus\Modbus.Net.Modbus.csproj", "{FDCA72BA-6D06-4DE0-B873-C11C4AC853AD}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modbus.Net.Siemens", "Modbus.Net.Siemens\Modbus.Net.Siemens.csproj", "{6258F9D9-0DF4-497F-9F3B-6D2F6F752A21}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modbus.Net.Siemens", "Modbus.Net.Siemens\Modbus.Net.Siemens.csproj", "{6258F9D9-0DF4-497F-9F3B-6D2F6F752A21}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modbus.Net.OPC", "Modbus.Net.OPC\Modbus.Net.OPC.csproj", "{97F5A329-357A-4813-BAAE-58E71CC6FA87}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modbus.Net.OPC", "Modbus.Net.OPC\Modbus.Net.OPC.csproj", "{97F5A329-357A-4813-BAAE-58E71CC6FA87}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modbus.Net.FBox", "Modbus.Net.FBox\Modbus.Net.FBox.csproj", "{492A9231-4184-4CFF-8C01-72B8F73CDACA}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -55,10 +51,6 @@ Global
|
|||||||
{02CE1787-291A-47CB-A7F7-929DA1D920A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{02CE1787-291A-47CB-A7F7-929DA1D920A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{02CE1787-291A-47CB-A7F7-929DA1D920A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{02CE1787-291A-47CB-A7F7-929DA1D920A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{02CE1787-291A-47CB-A7F7-929DA1D920A2}.Release|Any CPU.Build.0 = Release|Any CPU
|
{02CE1787-291A-47CB-A7F7-929DA1D920A2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{4F43B6F0-0C32-4C34-978E-9B8B5B0B6E80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{4F43B6F0-0C32-4C34-978E-9B8B5B0B6E80}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{4F43B6F0-0C32-4C34-978E-9B8B5B0B6E80}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{4F43B6F0-0C32-4C34-978E-9B8B5B0B6E80}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{FDCA72BA-6D06-4DE0-B873-C11C4AC853AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{FDCA72BA-6D06-4DE0-B873-C11C4AC853AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{FDCA72BA-6D06-4DE0-B873-C11C4AC853AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{FDCA72BA-6D06-4DE0-B873-C11C4AC853AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{FDCA72BA-6D06-4DE0-B873-C11C4AC853AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{FDCA72BA-6D06-4DE0-B873-C11C4AC853AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@@ -71,10 +63,6 @@ Global
|
|||||||
{97F5A329-357A-4813-BAAE-58E71CC6FA87}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{97F5A329-357A-4813-BAAE-58E71CC6FA87}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{97F5A329-357A-4813-BAAE-58E71CC6FA87}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{97F5A329-357A-4813-BAAE-58E71CC6FA87}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{97F5A329-357A-4813-BAAE-58E71CC6FA87}.Release|Any CPU.Build.0 = Release|Any CPU
|
{97F5A329-357A-4813-BAAE-58E71CC6FA87}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{492A9231-4184-4CFF-8C01-72B8F73CDACA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{492A9231-4184-4CFF-8C01-72B8F73CDACA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{492A9231-4184-4CFF-8C01-72B8F73CDACA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{492A9231-4184-4CFF-8C01-72B8F73CDACA}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
namespace Hylasoft.Opc.Common
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Useful extension methods for OPC Clients
|
|
||||||
/// </summary>
|
|
||||||
public static class ClientExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Reads a tag from the OPC. If for whatever reason the read fails (Tag doesn't exist, server not available) returns a default value
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="client">the opc client to use for the read</param>
|
|
||||||
/// <param name="tag">The fully qualified identifier of the tag</param>
|
|
||||||
/// <param name="defaultValue">the default value to read if the read fails</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static T ReadOrdefault<T>(this IClient<Node> client, string tag, T defaultValue = default(T))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return client.Read<T>(tag);
|
|
||||||
}
|
|
||||||
catch (OpcException)
|
|
||||||
{
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Hylasoft.Opc.Common
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Client interface to perform basic Opc tasks, like discovery, monitoring, reading/writing tags,
|
|
||||||
/// </summary>
|
|
||||||
public interface IClient<out TNode> : IDisposable
|
|
||||||
where TNode : Node
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Connect the client to the OPC Server
|
|
||||||
/// </summary>
|
|
||||||
void Connect();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current status of the OPC Client
|
|
||||||
/// </summary>
|
|
||||||
OpcStatus Status { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read a tag
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of tag to read</typeparam>
|
|
||||||
/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
|
||||||
/// E.g: the tag `foo.bar` reads the tag `bar` on the folder `foo`</param>
|
|
||||||
/// <returns>The value retrieved from the OPC</returns>
|
|
||||||
T Read<T>(string tag);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Write a value on the specified opc tag
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of tag to write on</typeparam>
|
|
||||||
/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
|
||||||
/// E.g: the tag `foo.bar` writes on the tag `bar` on the folder `foo`</param>
|
|
||||||
/// <param name="item"></param>
|
|
||||||
void Write<T>(string tag, T item);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Monitor the specified tag for changes
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">the type of tag to monitor</typeparam>
|
|
||||||
/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
|
||||||
/// E.g: the tag `foo.bar` monitors the tag `bar` on the folder `foo`</param>
|
|
||||||
/// <param name="callback">the callback to execute when the value is changed.
|
|
||||||
/// The first parameter is the new value of the node, the second is an `unsubscribe` function to unsubscribe the callback</param>
|
|
||||||
void Monitor<T>(string tag, Action<T, Action> callback);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Finds a node on the Opc Server
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
|
||||||
/// E.g: the tag `foo.bar` finds the tag `bar` on the folder `foo`</param>
|
|
||||||
/// <returns>If there is a tag, it returns it, otherwise it throws an </returns>
|
|
||||||
TNode FindNode(string tag);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the root node of the server
|
|
||||||
/// </summary>
|
|
||||||
TNode RootNode { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Explore a folder on the Opc Server
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
|
||||||
/// E.g: the tag `foo.bar` finds the sub nodes of `bar` on the folder `foo`</param>
|
|
||||||
/// <returns>The list of sub-nodes</returns>
|
|
||||||
IEnumerable<TNode> ExploreFolder(string tag);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read a tag asynchronusly
|
|
||||||
/// </summary>
|
|
||||||
Task<T> ReadAsync<T>(string tag);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Write a value on the specified opc tag asynchronously
|
|
||||||
/// </summary>
|
|
||||||
Task WriteAsync<T>(string tag, T item);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Finds a node on the Opc Server asynchronously
|
|
||||||
/// </summary>
|
|
||||||
Task<Node> FindNodeAsync(string tag);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Explore a folder on the Opc Server asynchronously
|
|
||||||
/// </summary>
|
|
||||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
|
|
||||||
Justification = "Task")]
|
|
||||||
Task<IEnumerable<Node>> ExploreFolderAsync(string tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Hylasoft.Opc.Common
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Base class representing a node on the OPC server
|
|
||||||
/// </summary>
|
|
||||||
public abstract class Node
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the displayed name of the node
|
|
||||||
/// </summary>
|
|
||||||
public string Name { get; protected set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the dot-separated fully qualified tag of the node
|
|
||||||
/// </summary>
|
|
||||||
public string Tag { get; protected set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the parent node. If the node is root, returns null
|
|
||||||
/// </summary>
|
|
||||||
public Node Parent { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new node
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">the name of the node</param>
|
|
||||||
/// <param name="parent">The parent node</param>
|
|
||||||
protected Node(string name, Node parent = null)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
Parent = parent;
|
|
||||||
if (parent != null && !string.IsNullOrEmpty(parent.Tag))
|
|
||||||
Tag = parent.Tag + '.' + name;
|
|
||||||
else
|
|
||||||
Tag = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Overrides ToString()
|
|
||||||
/// </summary>
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Tag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
using Opc.Ua;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
|
|
||||||
namespace Hylasoft.Opc.Common
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Identifies an exception occurred during OPC Communication
|
|
||||||
/// </summary>
|
|
||||||
[Serializable]
|
|
||||||
public class OpcException : Exception
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initialize a new instance of the OpcException class
|
|
||||||
/// </summary>
|
|
||||||
public OpcException()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialize a new instance of the OpcException class
|
|
||||||
/// </summary>
|
|
||||||
public OpcException(string message)
|
|
||||||
: base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns an (optional) associated OPC UA StatusCode for the exception.
|
|
||||||
/// </summary>
|
|
||||||
public StatusCode? Status { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialize a new instance of the OpcException class
|
|
||||||
/// </summary>
|
|
||||||
public OpcException(string message, StatusCode status)
|
|
||||||
: base(message)
|
|
||||||
{
|
|
||||||
Status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialize a new instance of the OpcException class
|
|
||||||
/// </summary>
|
|
||||||
public OpcException(string message, Exception inner)
|
|
||||||
: base(message, inner)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialize a new instance of the OpcException class
|
|
||||||
/// </summary>
|
|
||||||
protected OpcException(SerializationInfo info, StreamingContext context)
|
|
||||||
: base(info, context)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the System.Runtime.Serialization.SerializationInfo with information about the exception.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">The System.Runtime.Serialization.SerializationInfo that holds the serialized object data about the exception being thrown.</param>
|
|
||||||
/// <param name="context">The System.Runtime.Serialization.StreamingContext that contains contextual information about the source or destination.</param>
|
|
||||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
|
||||||
{
|
|
||||||
base.GetObjectData(info, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
namespace Hylasoft.Opc.Common
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Identifies the status of an OPC connector
|
|
||||||
/// </summary>
|
|
||||||
public enum OpcStatus
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The client is not connected
|
|
||||||
/// </summary>
|
|
||||||
NotConnected,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The client is connected
|
|
||||||
/// </summary>
|
|
||||||
Connected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using Hylasoft.Opc.Common;
|
|
||||||
using Opc;
|
|
||||||
using Factory = OpcCom.Factory;
|
|
||||||
using OpcDa = Opc.Da;
|
|
||||||
|
|
||||||
namespace Hylasoft.Opc.Da
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Client Implementation for DA
|
|
||||||
/// </summary>
|
|
||||||
public partial class DaClient : IClient<DaNode>
|
|
||||||
{
|
|
||||||
private readonly URL _url;
|
|
||||||
protected OpcDa.Server _server;
|
|
||||||
private long _sub;
|
|
||||||
private readonly IDictionary<string, DaNode> _nodesCache = new Dictionary<string, DaNode>();
|
|
||||||
|
|
||||||
// default monitor interval in Milliseconds
|
|
||||||
private const int DefaultMonitorInterval = 100;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialize a new Data Access Client
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="serverUrl">the url of the server to connect to</param>
|
|
||||||
public DaClient(Uri serverUrl)
|
|
||||||
{
|
|
||||||
_url = new URL(serverUrl.AbsolutePath)
|
|
||||||
{
|
|
||||||
Scheme = serverUrl.Scheme,
|
|
||||||
HostName = serverUrl.Host
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#region interface methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Connect the client to the OPC Server
|
|
||||||
/// </summary>
|
|
||||||
public void Connect()
|
|
||||||
{
|
|
||||||
if (Status == OpcStatus.Connected)
|
|
||||||
return;
|
|
||||||
_server = new OpcDa.Server(new Factory(), _url);
|
|
||||||
_server.Connect();
|
|
||||||
var root = new DaNode(string.Empty, string.Empty);
|
|
||||||
RootNode = root;
|
|
||||||
AddNodeToCache(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current status of the OPC Client
|
|
||||||
/// </summary>
|
|
||||||
public OpcStatus Status
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_server == null || _server.GetStatus().ServerState != OpcDa.serverState.running)
|
|
||||||
return OpcStatus.NotConnected;
|
|
||||||
return OpcStatus.Connected;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read a tag
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of tag to read</typeparam>
|
|
||||||
/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
|
||||||
/// E.g: the tag `foo.bar` reads the tag `bar` on the folder `foo`</param>
|
|
||||||
/// <returns>The value retrieved from the OPC</returns>
|
|
||||||
public T Read<T>(string tag)
|
|
||||||
{
|
|
||||||
var item = new OpcDa.Item { ItemName = tag };
|
|
||||||
var result = _server.Read(new[] { item })[0];
|
|
||||||
CheckResult(result, tag);
|
|
||||||
|
|
||||||
return (T)result.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Write a value on the specified opc tag
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of tag to write on</typeparam>
|
|
||||||
/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
|
||||||
/// E.g: the tag `foo.bar` writes on the tag `bar` on the folder `foo`</param>
|
|
||||||
/// <param name="item"></param>
|
|
||||||
public void Write<T>(string tag, T item)
|
|
||||||
{
|
|
||||||
var itmVal = new OpcDa.ItemValue
|
|
||||||
{
|
|
||||||
ItemName = tag,
|
|
||||||
Value = item
|
|
||||||
};
|
|
||||||
var result = _server.Write(new[] { itmVal })[0];
|
|
||||||
CheckResult(result, tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Monitor the specified tag for changes
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">the type of tag to monitor</typeparam>
|
|
||||||
/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
|
||||||
/// E.g: the tag `foo.bar` monitors the tag `bar` on the folder `foo`</param>
|
|
||||||
/// <param name="callback">the callback to execute when the value is changed.
|
|
||||||
/// The first parameter is the new value of the node, the second is an `unsubscribe` function to unsubscribe the callback</param>
|
|
||||||
public void Monitor<T>(string tag, Action<T, Action> callback)
|
|
||||||
{
|
|
||||||
var subItem = new OpcDa.SubscriptionState
|
|
||||||
{
|
|
||||||
Name = (++_sub).ToString(CultureInfo.InvariantCulture),
|
|
||||||
Active = true,
|
|
||||||
UpdateRate = DefaultMonitorInterval
|
|
||||||
};
|
|
||||||
var sub = _server.CreateSubscription(subItem);
|
|
||||||
|
|
||||||
// I have to start a new thread here because unsubscribing
|
|
||||||
// the subscription during a datachanged event causes a deadlock
|
|
||||||
Action unsubscribe = () => new Thread(o =>
|
|
||||||
_server.CancelSubscription(sub)).Start();
|
|
||||||
|
|
||||||
sub.DataChanged += (handle, requestHandle, values) =>
|
|
||||||
callback((T)values[0].Value, unsubscribe);
|
|
||||||
sub.AddItems(new[] { new OpcDa.Item { ItemName = tag } });
|
|
||||||
sub.SetEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Finds a node on the Opc Server
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
|
||||||
/// E.g: the tag `foo.bar` finds the tag `bar` on the folder `foo`</param>
|
|
||||||
/// <returns>If there is a tag, it returns it, otherwise it throws an </returns>
|
|
||||||
public DaNode FindNode(string tag)
|
|
||||||
{
|
|
||||||
// if the tag already exists in cache, return it
|
|
||||||
if (_nodesCache.ContainsKey(tag))
|
|
||||||
return _nodesCache[tag];
|
|
||||||
|
|
||||||
// try to find the tag otherwise
|
|
||||||
var item = new OpcDa.Item { ItemName = tag };
|
|
||||||
var result = _server.Read(new[] { item })[0];
|
|
||||||
CheckResult(result, tag);
|
|
||||||
var node = new DaNode(item.ItemName, item.ItemName, RootNode);
|
|
||||||
AddNodeToCache(node);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the root node of the server
|
|
||||||
/// </summary>
|
|
||||||
public DaNode RootNode { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Explore a folder on the Opc Server
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
|
||||||
/// E.g: the tag `foo.bar` finds the sub nodes of `bar` on the folder `foo`</param>
|
|
||||||
/// <returns>The list of sub-nodes</returns>
|
|
||||||
public IEnumerable<DaNode> ExploreFolder(string tag)
|
|
||||||
{
|
|
||||||
var parent = FindNode(tag);
|
|
||||||
OpcDa.BrowsePosition p;
|
|
||||||
var nodes = _server.Browse(new ItemIdentifier(parent.Tag), new OpcDa.BrowseFilters(), out p)
|
|
||||||
.Select(t => new DaNode(t.Name, t.ItemName, parent))
|
|
||||||
.ToList();
|
|
||||||
//add nodes to cache
|
|
||||||
foreach (var node in nodes)
|
|
||||||
AddNodeToCache(node);
|
|
||||||
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_server != null)
|
|
||||||
_server.Dispose();
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a node to the cache using the tag as its key
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">the node to add</param>
|
|
||||||
protected void AddNodeToCache(DaNode node)
|
|
||||||
{
|
|
||||||
if (!_nodesCache.ContainsKey(node.Tag))
|
|
||||||
_nodesCache.Add(node.Tag, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static void CheckResult(IResult result, string tag)
|
|
||||||
{
|
|
||||||
if (result == null)
|
|
||||||
throw new OpcException("The server replied with an empty response");
|
|
||||||
if (result.ResultID.ToString() != "S_OK")
|
|
||||||
throw new OpcException(string.Format("Invalid response from the server. (Response Status: {0}, Opc Tag: {1})", result.ResultID, tag));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Hylasoft.Opc.Common;
|
|
||||||
using OpcDa = Opc.Da;
|
|
||||||
|
|
||||||
namespace Hylasoft.Opc.Da
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Client Implementation for DA
|
|
||||||
/// </summary>
|
|
||||||
public partial class DaClient
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Read a tag asynchronusly
|
|
||||||
/// </summary>
|
|
||||||
public async Task<T> ReadAsync<T>(string tag)
|
|
||||||
{
|
|
||||||
return await Task.Run(() => Read<T>(tag));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Write a value on the specified opc tag asynchronously
|
|
||||||
/// </summary>
|
|
||||||
public async Task WriteAsync<T>(string tag, T item)
|
|
||||||
{
|
|
||||||
await Task.Run(() => Write(tag, item));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Finds a node on the Opc Server asynchronously
|
|
||||||
/// </summary>
|
|
||||||
public async Task<Node> FindNodeAsync(string tag)
|
|
||||||
{
|
|
||||||
return await Task.Run(() => FindNode(tag));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Explore a folder on the Opc Server asynchronously
|
|
||||||
/// </summary>
|
|
||||||
public async Task<IEnumerable<Node>> ExploreFolderAsync(string tag)
|
|
||||||
{
|
|
||||||
return await Task.Run(() => ExploreFolder(tag));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
using Hylasoft.Opc.Common;
|
|
||||||
|
|
||||||
namespace Hylasoft.Opc.Da
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a node to be used specifically for OPC DA
|
|
||||||
/// </summary>
|
|
||||||
public class DaNode : Node
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Instantiates a DaNode class
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">the name of the node</param>
|
|
||||||
/// <param name="tag"></param>
|
|
||||||
/// <param name="parent">The parent node</param>
|
|
||||||
public DaNode(string name, string tag, Node parent = null)
|
|
||||||
: base(name, parent)
|
|
||||||
{
|
|
||||||
Tag = tag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
|
||||||
<metadata>
|
|
||||||
<id>Modbus.Net.h-opc</id>
|
|
||||||
<version>0.4.0</version>
|
|
||||||
<title>Modbus.Net.h-opc</title>
|
|
||||||
<authors>Chris L.(Luo Sheng)</authors>
|
|
||||||
<owners>Hangzhou Delian Information and Science Technology</owners>
|
|
||||||
<licenseUrl>https://github.com/hylasoft-usa/h-opc/blob/master/LICENSE</licenseUrl>
|
|
||||||
<projectUrl>https://github.com/parallelbgls/Modbus.Net/tree/master/Modbus.Net/h-opc</projectUrl>
|
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
|
||||||
<description>h-opc package derived from https://github.com/hylasoft-usa/h-opc
|
|
||||||
And I changed a little word to make the extend method available.</description>
|
|
||||||
<copyright>Copyright 2015 Hangzhou Delian Science and Technology Co.,Ltd.</copyright>
|
|
||||||
<tags>h-opc</tags>
|
|
||||||
</metadata>
|
|
||||||
|
|
||||||
<files>
|
|
||||||
<file src="bin\Release\h-opc.dll" target="lib\net45\h-opc.dll" />
|
|
||||||
<file src="..\packages_local\Opc.Ua.Client.dll" target="lib\net45\Opc.Ua.Client.dll" />
|
|
||||||
<file src="..\packages_local\Opc.Ua.Configuration.dll" target="lib\net45\Opc.Ua.Configuration.dll" />
|
|
||||||
<file src="..\packages_local\Opc.Ua.Core.dll" target="lib\net45\Opc.Ua.Core.dll" />
|
|
||||||
<file src="..\packages_local\OpcComRcw.dll" target="lib\net45\OpcComRcw.dll" />
|
|
||||||
<file src="..\packages_local\OpcNetApi.Com.dll" target="lib\net45\OpcNetApi.Com.dll" />
|
|
||||||
<file src="..\packages_local\OpcNetApi.dll" target="lib\net45\OpcNetApi.dll" />
|
|
||||||
</files>
|
|
||||||
</package>
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
|
||||||
// set of attributes. Change these attribute values to modify the information
|
|
||||||
// associated with an assembly.
|
|
||||||
[assembly: AssemblyTitle("h-opc")]
|
|
||||||
[assembly: AssemblyDescription("")]
|
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("hylasoft")]
|
|
||||||
[assembly: AssemblyProduct("h-opc")]
|
|
||||||
[assembly: AssemblyCopyright(" ")]
|
|
||||||
[assembly: AssemblyTrademark("")]
|
|
||||||
[assembly: AssemblyCulture("")]
|
|
||||||
|
|
||||||
// Setting ComVisible to false makes the types in this assembly not visible
|
|
||||||
// to COM components. If you need to access a type in this assembly from
|
|
||||||
// COM, set the ComVisible attribute to true on that type.
|
|
||||||
[assembly: ComVisible(false)]
|
|
||||||
|
|
||||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
|
||||||
[assembly: Guid("cec4db47-9316-4591-a7e7-35118fb1e375")]
|
|
||||||
|
|
||||||
// Version information for an assembly consists of the following four values:
|
|
||||||
//
|
|
||||||
// Major Version
|
|
||||||
// Minor Version
|
|
||||||
// Build Number
|
|
||||||
// Revision
|
|
||||||
//
|
|
||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
|
||||||
// by using the '*' as shown below:
|
|
||||||
// [assembly: AssemblyVersion("0.4.0")]
|
|
||||||
[assembly: AssemblyVersion("0.4.0")]
|
|
||||||
[assembly: AssemblyFileVersion("0.4.0")]
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<StyleCopSettings Version="105" >
|
|
||||||
<GlobalSettings>
|
|
||||||
<BooleanProperty Name="RulesEnabledByDefault">False</BooleanProperty>
|
|
||||||
</GlobalSettings>
|
|
||||||
</StyleCopSettings>
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using Opc.Ua;
|
|
||||||
using System;
|
|
||||||
using Opc.Ua.Client;
|
|
||||||
|
|
||||||
namespace Hylasoft.Opc.Ua
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// List of static utility methods
|
|
||||||
/// </summary>
|
|
||||||
internal static class ClientUtils
|
|
||||||
{
|
|
||||||
// TODO I didn't write these methods. I should rewrite it once I understand whtat it does, beacuse it looks crazy
|
|
||||||
|
|
||||||
public static EndpointDescription SelectEndpoint(Uri discoveryUrl, bool useSecurity)
|
|
||||||
{
|
|
||||||
var configuration = EndpointConfiguration.Create();
|
|
||||||
configuration.OperationTimeout = 5000;
|
|
||||||
EndpointDescription endpointDescription1 = null;
|
|
||||||
using (var discoveryClient = DiscoveryClient.Create(discoveryUrl, configuration))
|
|
||||||
{
|
|
||||||
var endpoints = discoveryClient.GetEndpoints(null);
|
|
||||||
foreach (var endpointDescription2 in endpoints.Where(endpointDescription2 => endpointDescription2.EndpointUrl.StartsWith(discoveryUrl.Scheme)))
|
|
||||||
{
|
|
||||||
if (useSecurity)
|
|
||||||
{
|
|
||||||
if (endpointDescription2.SecurityMode == MessageSecurityMode.None)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (endpointDescription2.SecurityMode != MessageSecurityMode.None)
|
|
||||||
continue;
|
|
||||||
if (endpointDescription1 == null)
|
|
||||||
endpointDescription1 = endpointDescription2;
|
|
||||||
if (endpointDescription2.SecurityLevel > endpointDescription1.SecurityLevel)
|
|
||||||
endpointDescription1 = endpointDescription2;
|
|
||||||
}
|
|
||||||
if (endpointDescription1 == null)
|
|
||||||
{
|
|
||||||
if (endpoints.Count > 0)
|
|
||||||
endpointDescription1 = endpoints[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var uri = Utils.ParseUri(endpointDescription1.EndpointUrl);
|
|
||||||
if (uri != null && uri.Scheme == discoveryUrl.Scheme)
|
|
||||||
endpointDescription1.EndpointUrl = new UriBuilder(uri)
|
|
||||||
{
|
|
||||||
Host = discoveryUrl.DnsSafeHost,
|
|
||||||
Port = discoveryUrl.Port
|
|
||||||
}.ToString();
|
|
||||||
return endpointDescription1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ReferenceDescriptionCollection Browse(Session session, NodeId nodeId)
|
|
||||||
{
|
|
||||||
var desc = new BrowseDescription
|
|
||||||
{
|
|
||||||
NodeId = nodeId,
|
|
||||||
BrowseDirection = BrowseDirection.Forward,
|
|
||||||
IncludeSubtypes = true,
|
|
||||||
NodeClassMask = 0U,
|
|
||||||
ResultMask = 63U,
|
|
||||||
};
|
|
||||||
return Browse(session, desc, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ReferenceDescriptionCollection Browse(Session session, BrowseDescription nodeToBrowse, bool throwOnError)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var descriptionCollection = new ReferenceDescriptionCollection();
|
|
||||||
var nodesToBrowse = new BrowseDescriptionCollection { nodeToBrowse };
|
|
||||||
BrowseResultCollection results;
|
|
||||||
DiagnosticInfoCollection diagnosticInfos;
|
|
||||||
session.Browse(null, null, 0U, nodesToBrowse, out results, out diagnosticInfos);
|
|
||||||
ClientBase.ValidateResponse(results, nodesToBrowse);
|
|
||||||
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToBrowse);
|
|
||||||
while (!StatusCode.IsBad(results[0].StatusCode))
|
|
||||||
{
|
|
||||||
for (var index = 0; index < results[0].References.Count; ++index)
|
|
||||||
descriptionCollection.Add(results[0].References[index]);
|
|
||||||
if (results[0].References.Count == 0 || results[0].ContinuationPoint == null)
|
|
||||||
return descriptionCollection;
|
|
||||||
var continuationPoints = new ByteStringCollection();
|
|
||||||
continuationPoints.Add(results[0].ContinuationPoint);
|
|
||||||
session.BrowseNext(null, false, continuationPoints, out results, out diagnosticInfos);
|
|
||||||
ClientBase.ValidateResponse(results, continuationPoints);
|
|
||||||
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, continuationPoints);
|
|
||||||
}
|
|
||||||
throw new ServiceResultException(results[0].StatusCode);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (throwOnError)
|
|
||||||
throw new ServiceResultException(ex, 2147549184U);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
using Hylasoft.Opc.Common;
|
|
||||||
using OpcF = Opc.Ua;
|
|
||||||
|
|
||||||
namespace Hylasoft.Opc.Ua
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Class with extension methods for OPC UA
|
|
||||||
/// </summary>
|
|
||||||
public static class NodeExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Converts an OPC Foundation node to an Hylasoft OPC UA Node
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">The node to convert</param>
|
|
||||||
/// <param name="parent">the parent node (optional)</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
internal static UaNode ToHylaNode(this OpcF.ReferenceDescription node, Node parent = null)
|
|
||||||
{
|
|
||||||
var name = node.DisplayName.ToString();
|
|
||||||
var nodeId = node.NodeId.ToString();
|
|
||||||
return new UaNode(name, nodeId, parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,558 +0,0 @@
|
|||||||
using Hylasoft.Opc.Common;
|
|
||||||
using Opc.Ua;
|
|
||||||
using Opc.Ua.Client;
|
|
||||||
using Opc.Ua.Configuration;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Hylasoft.Opc.Ua
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Client Implementation for UA
|
|
||||||
/// </summary>
|
|
||||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling",
|
|
||||||
Justification = "Doesn't make sense to split this class")]
|
|
||||||
public class UaClient : IClient<UaNode>
|
|
||||||
{
|
|
||||||
private readonly UaClientOptions _options = new UaClientOptions();
|
|
||||||
private readonly Uri _serverUrl;
|
|
||||||
protected Session _session;
|
|
||||||
|
|
||||||
private readonly IDictionary<string, UaNode> _nodesCache = new Dictionary<string, UaNode>();
|
|
||||||
private readonly IDictionary<string, IList<UaNode>> _folderCache = new Dictionary<string, IList<UaNode>>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a server object
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="serverUrl">the url of the server to connect to</param>
|
|
||||||
public UaClient(Uri serverUrl)
|
|
||||||
{
|
|
||||||
_serverUrl = serverUrl;
|
|
||||||
Status = OpcStatus.NotConnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Options to configure the UA client session
|
|
||||||
/// </summary>
|
|
||||||
public UaClientOptions Options
|
|
||||||
{
|
|
||||||
get { return _options; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void PostInitializeSession()
|
|
||||||
{
|
|
||||||
var node = _session.NodeCache.Find(ObjectIds.ObjectsFolder);
|
|
||||||
RootNode = new UaNode(string.Empty, node.NodeId.ToString());
|
|
||||||
AddNodeToCache(RootNode);
|
|
||||||
Status = OpcStatus.Connected;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Connect the client to the OPC Server
|
|
||||||
/// </summary>
|
|
||||||
public void Connect()
|
|
||||||
{
|
|
||||||
if (Status == OpcStatus.Connected)
|
|
||||||
return;
|
|
||||||
_session = InitializeSession(_serverUrl);
|
|
||||||
_session.KeepAlive += SessionKeepAlive;
|
|
||||||
_session.SessionClosing += SessionClosing;
|
|
||||||
PostInitializeSession();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SessionKeepAlive(Session session, KeepAliveEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.CurrentState != ServerState.Running)
|
|
||||||
{
|
|
||||||
if (Status == OpcStatus.Connected)
|
|
||||||
{
|
|
||||||
Status = OpcStatus.NotConnected;
|
|
||||||
NotifyServerConnectionLost();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (e.CurrentState == ServerState.Running)
|
|
||||||
{
|
|
||||||
if (Status == OpcStatus.NotConnected)
|
|
||||||
{
|
|
||||||
Status = OpcStatus.Connected;
|
|
||||||
NotifyServerConnectionRestored();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SessionClosing(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
Status = OpcStatus.NotConnected;
|
|
||||||
NotifyServerConnectionLost();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reconnect the OPC session
|
|
||||||
/// </summary>
|
|
||||||
public void ReConnect()
|
|
||||||
{
|
|
||||||
if (Status != OpcStatus.Connected)
|
|
||||||
return;
|
|
||||||
Status = OpcStatus.NotConnected;
|
|
||||||
_session.Reconnect();
|
|
||||||
Status = OpcStatus.Connected;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new OPC session, based on the current session parameters.
|
|
||||||
/// </summary>
|
|
||||||
public void RecreateSession()
|
|
||||||
{
|
|
||||||
if (Status != OpcStatus.Connected)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Status = OpcStatus.NotConnected;
|
|
||||||
_session = Session.Recreate(_session);
|
|
||||||
PostInitializeSession();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current status of the OPC Client
|
|
||||||
/// </summary>
|
|
||||||
public OpcStatus Status { get; private set; }
|
|
||||||
|
|
||||||
|
|
||||||
private ReadValueIdCollection BuildReadValueIdCollection(string tag, uint attributeId)
|
|
||||||
{
|
|
||||||
var n = FindNode(tag, RootNode);
|
|
||||||
var readValue = new ReadValueId
|
|
||||||
{
|
|
||||||
NodeId = n.NodeId,
|
|
||||||
AttributeId = attributeId
|
|
||||||
};
|
|
||||||
return new ReadValueIdCollection { readValue };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read a tag
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of tag to read</typeparam>
|
|
||||||
/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
|
||||||
/// E.g: the tag `foo.bar` reads the tag `bar` on the folder `foo`</param>
|
|
||||||
/// <returns>The value retrieved from the OPC</returns>
|
|
||||||
public T Read<T>(string tag)
|
|
||||||
{
|
|
||||||
var nodesToRead = BuildReadValueIdCollection(tag, Attributes.Value);
|
|
||||||
DataValueCollection results;
|
|
||||||
DiagnosticInfoCollection diag;
|
|
||||||
_session.Read(
|
|
||||||
requestHeader: null,
|
|
||||||
maxAge: 0,
|
|
||||||
timestampsToReturn: TimestampsToReturn.Neither,
|
|
||||||
nodesToRead: nodesToRead,
|
|
||||||
results: out results,
|
|
||||||
diagnosticInfos: out diag);
|
|
||||||
var val = results[0];
|
|
||||||
|
|
||||||
CheckReturnValue(val.StatusCode);
|
|
||||||
return (T)val.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read a tag asynchronously
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of tag to read</typeparam>
|
|
||||||
/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
|
||||||
/// E.g: the tag `foo.bar` reads the tag `bar` on the folder `foo`</param>
|
|
||||||
/// <returns>The value retrieved from the OPC</returns>
|
|
||||||
public Task<T> ReadAsync<T>(string tag)
|
|
||||||
{
|
|
||||||
var nodesToRead = BuildReadValueIdCollection(tag, Attributes.Value);
|
|
||||||
|
|
||||||
// Wrap the ReadAsync logic in a TaskCompletionSource, so we can use C# async/await syntax to call it:
|
|
||||||
var taskCompletionSource = new TaskCompletionSource<T>();
|
|
||||||
_session.BeginRead(
|
|
||||||
requestHeader: null,
|
|
||||||
maxAge: 0,
|
|
||||||
timestampsToReturn: TimestampsToReturn.Neither,
|
|
||||||
nodesToRead: nodesToRead,
|
|
||||||
callback: ar =>
|
|
||||||
{
|
|
||||||
DataValueCollection results;
|
|
||||||
DiagnosticInfoCollection diag;
|
|
||||||
var response = _session.EndRead(
|
|
||||||
result: ar,
|
|
||||||
results: out results,
|
|
||||||
diagnosticInfos: out diag);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
CheckReturnValue(response.ServiceResult);
|
|
||||||
CheckReturnValue(results[0].StatusCode);
|
|
||||||
var val = results[0];
|
|
||||||
taskCompletionSource.TrySetResult((T)val.Value);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
taskCompletionSource.TrySetException(ex);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
asyncState: null);
|
|
||||||
|
|
||||||
return taskCompletionSource.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private WriteValueCollection BuildWriteValueCollection(string tag, uint attributeId, object dataValue)
|
|
||||||
{
|
|
||||||
var n = FindNode(tag, RootNode);
|
|
||||||
var writeValue = new WriteValue
|
|
||||||
{
|
|
||||||
NodeId = n.NodeId,
|
|
||||||
AttributeId = attributeId,
|
|
||||||
Value = { Value = dataValue }
|
|
||||||
};
|
|
||||||
return new WriteValueCollection { writeValue };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Write a value on the specified opc tag
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of tag to write on</typeparam>
|
|
||||||
/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
|
||||||
/// E.g: the tag `foo.bar` writes on the tag `bar` on the folder `foo`</param>
|
|
||||||
/// <param name="item">The value for the item to write</param>
|
|
||||||
public void Write<T>(string tag, T item)
|
|
||||||
{
|
|
||||||
var nodesToWrite = BuildWriteValueCollection(tag, Attributes.Value, item);
|
|
||||||
|
|
||||||
StatusCodeCollection results;
|
|
||||||
DiagnosticInfoCollection diag;
|
|
||||||
_session.Write(
|
|
||||||
requestHeader: null,
|
|
||||||
nodesToWrite: nodesToWrite,
|
|
||||||
results: out results,
|
|
||||||
diagnosticInfos: out diag);
|
|
||||||
|
|
||||||
CheckReturnValue(results[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Write a value on the specified opc tag asynchronously
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of tag to write on</typeparam>
|
|
||||||
/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
|
||||||
/// E.g: the tag `foo.bar` writes on the tag `bar` on the folder `foo`</param>
|
|
||||||
/// <param name="item">The value for the item to write</param>
|
|
||||||
public Task WriteAsync<T>(string tag, T item)
|
|
||||||
{
|
|
||||||
var nodesToWrite = BuildWriteValueCollection(tag, Attributes.Value, item);
|
|
||||||
|
|
||||||
// Wrap the WriteAsync logic in a TaskCompletionSource, so we can use C# async/await syntax to call it:
|
|
||||||
var taskCompletionSource = new TaskCompletionSource<T>();
|
|
||||||
_session.BeginWrite(
|
|
||||||
requestHeader: null,
|
|
||||||
nodesToWrite: nodesToWrite,
|
|
||||||
callback: ar =>
|
|
||||||
{
|
|
||||||
StatusCodeCollection results;
|
|
||||||
DiagnosticInfoCollection diag;
|
|
||||||
var response = _session.EndWrite(
|
|
||||||
result: ar,
|
|
||||||
results: out results,
|
|
||||||
diagnosticInfos: out diag);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
CheckReturnValue(response.ServiceResult);
|
|
||||||
CheckReturnValue(results[0]);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
taskCompletionSource.TrySetException(ex);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
asyncState: null);
|
|
||||||
return taskCompletionSource.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Monitor the specified tag for changes
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">the type of tag to monitor</typeparam>
|
|
||||||
/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
|
||||||
/// E.g: the tag `foo.bar` monitors the tag `bar` on the folder `foo`</param>
|
|
||||||
/// <param name="callback">the callback to execute when the value is changed.
|
|
||||||
/// The first parameter is the new value of the node, the second is an `unsubscribe` function to unsubscribe the callback</param>
|
|
||||||
public void Monitor<T>(string tag, Action<T, Action> callback)
|
|
||||||
{
|
|
||||||
var node = FindNode(tag);
|
|
||||||
|
|
||||||
var sub = new Subscription
|
|
||||||
{
|
|
||||||
PublishingInterval = _options.DefaultMonitorInterval,
|
|
||||||
PublishingEnabled = true,
|
|
||||||
DisplayName = tag,
|
|
||||||
Priority = byte.MaxValue
|
|
||||||
};
|
|
||||||
|
|
||||||
var item = new MonitoredItem
|
|
||||||
{
|
|
||||||
StartNodeId = node.NodeId,
|
|
||||||
AttributeId = Attributes.Value,
|
|
||||||
DisplayName = tag,
|
|
||||||
SamplingInterval = _options.DefaultMonitorInterval,
|
|
||||||
};
|
|
||||||
sub.AddItem(item);
|
|
||||||
_session.AddSubscription(sub);
|
|
||||||
sub.Create();
|
|
||||||
sub.ApplyChanges();
|
|
||||||
|
|
||||||
item.Notification += (monitoredItem, args) =>
|
|
||||||
{
|
|
||||||
var p = (MonitoredItemNotification)args.NotificationValue;
|
|
||||||
var t = p.Value.WrappedValue.Value;
|
|
||||||
Action unsubscribe = () =>
|
|
||||||
{
|
|
||||||
sub.RemoveItems(sub.MonitoredItems);
|
|
||||||
sub.Delete(true);
|
|
||||||
_session.RemoveSubscription(sub);
|
|
||||||
sub.Dispose();
|
|
||||||
};
|
|
||||||
callback((T)t, unsubscribe);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Explore a folder on the Opc Server
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
|
||||||
/// E.g: the tag `foo.bar` finds the sub nodes of `bar` on the folder `foo`</param>
|
|
||||||
/// <returns>The list of sub-nodes</returns>
|
|
||||||
public IEnumerable<UaNode> ExploreFolder(string tag)
|
|
||||||
{
|
|
||||||
IList<UaNode> nodes;
|
|
||||||
_folderCache.TryGetValue(tag, out nodes);
|
|
||||||
if (nodes != null)
|
|
||||||
return nodes;
|
|
||||||
|
|
||||||
var folder = FindNode(tag);
|
|
||||||
nodes = ClientUtils.Browse(_session, folder.NodeId)
|
|
||||||
.GroupBy(n => n.NodeId) //this is to select distinct
|
|
||||||
.Select(n => n.First())
|
|
||||||
.Where(n => n.NodeClass == NodeClass.Variable || n.NodeClass == NodeClass.Object)
|
|
||||||
.Select(n => n.ToHylaNode(folder))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
//add nodes to cache
|
|
||||||
_folderCache.Add(tag, nodes);
|
|
||||||
foreach (var node in nodes)
|
|
||||||
AddNodeToCache(node);
|
|
||||||
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Explores a folder asynchronously
|
|
||||||
/// </summary>
|
|
||||||
public async Task<IEnumerable<Common.Node>> ExploreFolderAsync(string tag)
|
|
||||||
{
|
|
||||||
return await Task.Run(() => ExploreFolder(tag));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Finds a node on the Opc Server
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tag">The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name.
|
|
||||||
/// E.g: the tag `foo.bar` finds the tag `bar` on the folder `foo`</param>
|
|
||||||
/// <returns>If there is a tag, it returns it, otherwise it throws an </returns>
|
|
||||||
public UaNode FindNode(string tag)
|
|
||||||
{
|
|
||||||
// if the tag already exists in cache, return it
|
|
||||||
if (_nodesCache.ContainsKey(tag))
|
|
||||||
return _nodesCache[tag];
|
|
||||||
|
|
||||||
// try to find the tag otherwise
|
|
||||||
var found = FindNode(tag, RootNode);
|
|
||||||
if (found != null)
|
|
||||||
{
|
|
||||||
AddNodeToCache(found);
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
// throws an exception if not found
|
|
||||||
throw new OpcException(string.Format("The tag \"{0}\" doesn't exist on the Server", tag));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Find node asynchronously
|
|
||||||
/// </summary>
|
|
||||||
public async Task<Common.Node> FindNodeAsync(string tag)
|
|
||||||
{
|
|
||||||
return await Task.Run(() => FindNode(tag));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the root node of the server
|
|
||||||
/// </summary>
|
|
||||||
public UaNode RootNode { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_session != null)
|
|
||||||
{
|
|
||||||
_session.RemoveSubscriptions(_session.Subscriptions.ToList());
|
|
||||||
_session.Close();
|
|
||||||
_session.Dispose();
|
|
||||||
}
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void CheckReturnValue(StatusCode status)
|
|
||||||
{
|
|
||||||
if (!StatusCode.IsGood(status))
|
|
||||||
throw new OpcException(string.Format("Invalid response from the server. (Response Status: {0})", status), status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a node to the cache using the tag as its key
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">the node to add</param>
|
|
||||||
protected void AddNodeToCache(UaNode node)
|
|
||||||
{
|
|
||||||
if (!_nodesCache.ContainsKey(node.Tag))
|
|
||||||
_nodesCache.Add(node.Tag, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Crappy method to initialize the session. I don't know what many of these things do, sincerely.
|
|
||||||
/// </summary>
|
|
||||||
private Session InitializeSession(Uri url)
|
|
||||||
{
|
|
||||||
var certificateValidator = new CertificateValidator();
|
|
||||||
certificateValidator.CertificateValidation += (sender, eventArgs) =>
|
|
||||||
{
|
|
||||||
if (ServiceResult.IsGood(eventArgs.Error))
|
|
||||||
eventArgs.Accept = true;
|
|
||||||
else if ((eventArgs.Error.StatusCode.Code == StatusCodes.BadCertificateUntrusted) && _options.AutoAcceptUntrustedCertificates)
|
|
||||||
eventArgs.Accept = true;
|
|
||||||
else
|
|
||||||
throw new OpcException(string.Format("Failed to validate certificate with error code {0}: {1}", eventArgs.Error.Code, eventArgs.Error.AdditionalInfo), eventArgs.Error.StatusCode);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Build the application configuration
|
|
||||||
var appInstance = new ApplicationInstance
|
|
||||||
{
|
|
||||||
ApplicationType = ApplicationType.Client,
|
|
||||||
ConfigSectionName = _options.ConfigSectionName,
|
|
||||||
ApplicationConfiguration = new ApplicationConfiguration
|
|
||||||
{
|
|
||||||
ApplicationUri = url.ToString(),
|
|
||||||
ApplicationName = _options.ApplicationName,
|
|
||||||
ApplicationType = ApplicationType.Client,
|
|
||||||
CertificateValidator = certificateValidator,
|
|
||||||
SecurityConfiguration = new SecurityConfiguration
|
|
||||||
{
|
|
||||||
AutoAcceptUntrustedCertificates = _options.AutoAcceptUntrustedCertificates
|
|
||||||
},
|
|
||||||
TransportQuotas = new TransportQuotas
|
|
||||||
{
|
|
||||||
OperationTimeout = 600000,
|
|
||||||
MaxStringLength = 1048576,
|
|
||||||
MaxByteStringLength = 1048576,
|
|
||||||
MaxArrayLength = 65535,
|
|
||||||
MaxMessageSize = 4194304,
|
|
||||||
MaxBufferSize = 65535,
|
|
||||||
ChannelLifetime = 300000,
|
|
||||||
SecurityTokenLifetime = 3600000
|
|
||||||
},
|
|
||||||
ClientConfiguration = new ClientConfiguration
|
|
||||||
{
|
|
||||||
DefaultSessionTimeout = 60000,
|
|
||||||
MinSubscriptionLifetime = 10000
|
|
||||||
},
|
|
||||||
DisableHiResClock = true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Assign a application certificate (when specified)
|
|
||||||
if (_options.ApplicationCertificate != null)
|
|
||||||
appInstance.ApplicationConfiguration.SecurityConfiguration.ApplicationCertificate = new CertificateIdentifier(_options.ApplicationCertificate);
|
|
||||||
|
|
||||||
// Find the endpoint to be used
|
|
||||||
var endpoints = ClientUtils.SelectEndpoint(url, _options.UseMessageSecurity);
|
|
||||||
|
|
||||||
// Create the OPC session:
|
|
||||||
var session = Session.Create(
|
|
||||||
configuration: appInstance.ApplicationConfiguration,
|
|
||||||
endpoint: new ConfiguredEndpoint(
|
|
||||||
collection: null,
|
|
||||||
description: endpoints,
|
|
||||||
configuration: EndpointConfiguration.Create(applicationConfiguration: appInstance.ApplicationConfiguration)),
|
|
||||||
updateBeforeConnect: false,
|
|
||||||
checkDomain: false,
|
|
||||||
sessionName: _options.SessionName,
|
|
||||||
sessionTimeout: _options.SessionTimeout,
|
|
||||||
identity: null,
|
|
||||||
preferredLocales: new string[] { });
|
|
||||||
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Finds a node starting from the specified node as the root folder
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tag">the tag to find</param>
|
|
||||||
/// <param name="node">the root node</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private UaNode FindNode(string tag, UaNode node)
|
|
||||||
{
|
|
||||||
var folders = tag.Split('.');
|
|
||||||
var head = folders.FirstOrDefault();
|
|
||||||
UaNode found;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var subNodes = ExploreFolder(node.Tag);
|
|
||||||
found = subNodes.Single(n => n.Name == head);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
throw new OpcException(string.Format("The tag \"{0}\" doesn't exist on folder \"{1}\"", head, node.Tag), ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return folders.Length == 1
|
|
||||||
? found // last node, return it
|
|
||||||
: FindNode(string.Join(".", folders.Except(new[] { head })), found); // find sub nodes
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void NotifyServerConnectionLost()
|
|
||||||
{
|
|
||||||
if (ServerConnectionLost != null)
|
|
||||||
ServerConnectionLost(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void NotifyServerConnectionRestored()
|
|
||||||
{
|
|
||||||
if (ServerConnectionRestored != null)
|
|
||||||
ServerConnectionRestored(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This event is raised when the connection to the OPC server is lost.
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler ServerConnectionLost;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This event is raised when the connection to the OPC server is restored.
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler ServerConnectionRestored;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
|
|
||||||
namespace Hylasoft.Opc.Ua
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// This class defines the configuration options for the setup of the UA client session
|
|
||||||
/// </summary>
|
|
||||||
public class UaClientOptions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies the (optional) certificate for the applicaiton to connect to the server
|
|
||||||
/// </summary>
|
|
||||||
public X509Certificate2 ApplicationCertificate { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies the ApplicationName for the client application.
|
|
||||||
/// </summary>
|
|
||||||
public string ApplicationName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Should untrusted certificates be silently accepted by the client?
|
|
||||||
/// </summary>
|
|
||||||
public bool AutoAcceptUntrustedCertificates { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies the ConfigSectionName for the client configuration.
|
|
||||||
/// </summary>
|
|
||||||
public string ConfigSectionName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// default monitor interval in Milliseconds.
|
|
||||||
/// </summary>
|
|
||||||
public int DefaultMonitorInterval { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies a name to be associated with the created sessions.
|
|
||||||
/// </summary>
|
|
||||||
public string SessionName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies the timeout for the sessions.
|
|
||||||
/// </summary>
|
|
||||||
public uint SessionTimeout { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Specify whether message exchange should be secured.
|
|
||||||
/// </summary>
|
|
||||||
public bool UseMessageSecurity { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
internal UaClientOptions()
|
|
||||||
{
|
|
||||||
// Initialize default values:
|
|
||||||
ApplicationName = "h-opc-client";
|
|
||||||
AutoAcceptUntrustedCertificates = true;
|
|
||||||
ConfigSectionName = "h-opc-client";
|
|
||||||
DefaultMonitorInterval = 100;
|
|
||||||
SessionName = "h-opc-client";
|
|
||||||
SessionTimeout = 60000U;
|
|
||||||
UseMessageSecurity = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
using Hylasoft.Opc.Common;
|
|
||||||
|
|
||||||
namespace Hylasoft.Opc.Ua
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a node to be used specifically for OPC UA
|
|
||||||
/// </summary>
|
|
||||||
public class UaNode : Node
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The UA Id of the node
|
|
||||||
/// </summary>
|
|
||||||
public string NodeId { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Instantiates a UaNode class
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">the name of the node</param>
|
|
||||||
/// <param name="nodeId">The UA Id of the node</param>
|
|
||||||
/// <param name="parent">The parent node</param>
|
|
||||||
internal UaNode(string name, string nodeId, Node parent = null)
|
|
||||||
: base(name, parent)
|
|
||||||
{
|
|
||||||
NodeId = nodeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
|
||||||
<PropertyGroup>
|
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProjectGuid>{4F43B6F0-0C32-4C34-978E-9B8B5B0B6E80}</ProjectGuid>
|
|
||||||
<OutputType>Library</OutputType>
|
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
|
||||||
<RootNamespace>Hylasoft.Opc</RootNamespace>
|
|
||||||
<AssemblyName>h-opc</AssemblyName>
|
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
|
||||||
<FileAlignment>512</FileAlignment>
|
|
||||||
<StyleCopOverrideSettingsFile>Settings.StyleCop</StyleCopOverrideSettingsFile>
|
|
||||||
<BuildToolsStyleCopVersion>4.7.44.0</BuildToolsStyleCopVersion>
|
|
||||||
<TargetFrameworkProfile />
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\Debug\</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
<StyleCopEnabled>False</StyleCopEnabled>
|
|
||||||
<DocumentationFile>bin\Debug\h-opc.XML</DocumentationFile>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<OutputPath>bin\Release\</OutputPath>
|
|
||||||
<DefineConstants>TRACE;CODE_ANALYSIS</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<StyleCopEnabled>True</StyleCopEnabled>
|
|
||||||
<StyleCopTreatErrorsAsWarnings>False</StyleCopTreatErrorsAsWarnings>
|
|
||||||
<DocumentationFile>bin\Release\h-opc.XML</DocumentationFile>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="Opc.Ua.Client">
|
|
||||||
<HintPath>..\packages_local\Opc.Ua.Client.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Opc.Ua.Configuration">
|
|
||||||
<HintPath>..\packages_local\Opc.Ua.Configuration.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Opc.Ua.Core">
|
|
||||||
<HintPath>..\packages_local\Opc.Ua.Core.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="OpcComRcw">
|
|
||||||
<HintPath>..\packages_local\OpcComRcw.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="OpcNetApi">
|
|
||||||
<HintPath>..\packages_local\OpcNetApi.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="OpcNetApi.Com">
|
|
||||||
<HintPath>..\packages_local\OpcNetApi.Com.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System" />
|
|
||||||
<Reference Include="System.Core" />
|
|
||||||
<Reference Include="System.Xml.Linq" />
|
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
|
||||||
<Reference Include="Microsoft.CSharp" />
|
|
||||||
<Reference Include="System.Data" />
|
|
||||||
<Reference Include="System.Xml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Include="Common\ClientExtensions.cs" />
|
|
||||||
<Compile Include="Common\Node.cs" />
|
|
||||||
<Compile Include="Common\IClient.cs" />
|
|
||||||
<Compile Include="Common\OpcException.cs" />
|
|
||||||
<Compile Include="Common\OpcStatus.cs" />
|
|
||||||
<Compile Include="Da\DaClient_async.cs" />
|
|
||||||
<Compile Include="Da\DaClient.cs" />
|
|
||||||
<Compile Include="Da\DaNode.cs" />
|
|
||||||
<Compile Include="Ua\ClientUtils.cs" />
|
|
||||||
<Compile Include="Ua\NodeExtensions.cs" />
|
|
||||||
<Compile Include="Ua\UaClientOptions.cs" />
|
|
||||||
<Compile Include="Ua\UaNode.cs" />
|
|
||||||
<Compile Include="Ua\UaClient.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="Modbus.Net.h-opc.nuspec">
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
</None>
|
|
||||||
<None Include="packages.config" />
|
|
||||||
<None Include="Settings.StyleCop">
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup Label="StyleCopAdditionalAddinPaths">
|
|
||||||
<StyleCopAdditionalAddinPaths Include="..\packages\BuildTools.StyleCopPlus.4.7.49.4\tools">
|
|
||||||
<Visible>false</Visible>
|
|
||||||
</StyleCopAdditionalAddinPaths>
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
|
||||||
<Import Project="..\packages\BuildTools.StyleCop.4.7.49.0\tools\StyleCop.targets" Condition="Exists('..\packages\BuildTools.StyleCop.4.7.49.0\tools\StyleCop.targets')" />
|
|
||||||
<Target Name="packages_BuildTools_StyleCop_4_7_49_0_tools_StyleCop_targets" Condition="$(StyleCopOutputFile)==''" BeforeTargets="BeforeBuild">
|
|
||||||
<Error Text="BuildTools_StyleCop - the BuildTools_StyleCop package has not been restored.
If you are running this from an IDE, make sure NuGet Package Restore has been enabled, then reload the solution and re-run the build.
If you are running this from the command line, run the build again.
If this is a CI server, you may want to make sure NuGet Package Restore runs before your build with:
 msbuild solution.sln /t:restorepackages" />
|
|
||||||
</Target>
|
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
|
||||||
<Target Name="BeforeBuild">
|
|
||||||
</Target>
|
|
||||||
<Target Name="AfterBuild">
|
|
||||||
</Target>
|
|
||||||
-->
|
|
||||||
</Project>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<packages>
|
|
||||||
<package id="BuildTools.StyleCop" version="4.7.49.0" targetFramework="net45" />
|
|
||||||
<package id="BuildTools.StyleCopPlus" version="4.7.49.4" targetFramework="net45" />
|
|
||||||
</packages>
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,3 +0,0 @@
|
|||||||
## External Packages
|
|
||||||
|
|
||||||
this folder contains all the dependency assemblies which are not handled by NuGet
|
|
||||||
Reference in New Issue
Block a user