/// <summary> /// Constructor. /// </summary> /// <param name="client">DLMS Client.</param> /// <param name="media">Media.</param> public GXDLMSReader(GXDLMSSecureClient client, IGXMedia media, ILogger logger) { if (logger.IsEnabled(LogLevel.Trace) || logger.IsEnabled(LogLevel.Debug)) { Trace = TraceLevel.Verbose; } else if (logger.IsEnabled(LogLevel.Information)) { Trace = TraceLevel.Info; } else if (logger.IsEnabled(LogLevel.Warning)) { Trace = TraceLevel.Warning; } else if (logger.IsEnabled(LogLevel.Error) || logger.IsEnabled(LogLevel.Critical)) { Trace = TraceLevel.Error; } else { Trace = TraceLevel.Off; } Media = media; Client = client; _logger = logger; }
/// <summary> /// Constructor. /// </summary> /// <param name="client">DLMS Client.</param> /// <param name="media">Media.</param> public GXDLMSReader(GXDLMSSecureClient client, IGXMedia media, TraceLevel trace, ILogger logger) { Trace = trace; Media = media; Client = client; _logger = logger; }
/// <summary> /// Constructor. /// </summary> /// <param name="client">DLMS Client.</param> /// <param name="media">Media.</param> /// <param name="trace">Trace level.</param> /// <param name="invocationCounter">Logical name of invocation counter.</param> /// <param name="iec">Is optical head used.</param> public GXDLMSReader(GXDLMSSecureClient client, IGXMedia media, TraceLevel trace, string invocationCounter) { Trace = trace; Media = media; Client = client; InvocationCounter = invocationCounter; }
/// <summary> /// Agree on global unicast encryption key. /// </summary> /// <param name="client"> DLMS client that is used to generate action.</param> /// <param name="key"> List of keys.</param> /// <returns>Generated action.</returns> public byte[][] KeyAgreement(GXDLMSSecureClient client) { if (Version == 0) { throw new ArgumentException("Public and private key isn't implemented for version 0."); } if (client.Ciphering.EphemeralKeyPair.Value == null) { throw new ArgumentException("Invalid Ephemeral key."); } if (client.Ciphering.SigningKeyPair.Value == null) { throw new ArgumentException("Invalid Signiture key."); } GXByteBuffer bb = new GXByteBuffer(); //Add Ephemeral public key. bb.Set(client.Ciphering.EphemeralKeyPair.Value.RawValue, 1, client.Ciphering.EphemeralKeyPair.Value.RawValue.Length - 1); //Add signature. byte[] sign = GXSecure.GetEphemeralPublicKeySignature(0, client.Ciphering.EphemeralKeyPair.Value, client.Ciphering.SigningKeyPair.Key); bb.Set(sign); List <KeyValuePair <GlobalKeyType, byte[]> > list = new List <KeyValuePair <GlobalKeyType, byte[]> >(); list.Add(new KeyValuePair <GlobalKeyType, byte[]>(GlobalKeyType.UnicastEncryption, bb.Array())); return(KeyAgreement(client, list)); }
byte[] IGXDLMSBase.Invoke(GXDLMSSettings settings, ValueEventArgs e) { if (e.Index == 1) { if (Version == 0) { SecurityPolicy0 = (SecurityPolicy0)e.Parameters; } else { SecurityPolicy = (SecurityPolicy)e.Parameters; } } else if (e.Index == 2) { try { foreach (List <object> item in e.Parameters as List <object> ) { GlobalKeyType type = (GlobalKeyType)Convert.ToInt32(item[0]); byte[] data = (byte[])item[1]; switch (type) { case GlobalKeyType.UnicastEncryption: settings.Cipher.BlockCipherKey = GXDLMSSecureClient.Decrypt(settings.Kek, data); break; case GlobalKeyType.BroadcastEncryption: //Invalid type e.Error = ErrorCode.ReadWriteDenied; break; case GlobalKeyType.Authentication: //if settings.Cipher is null non secure server is used. settings.Cipher.AuthenticationKey = GXDLMSSecureClient.Decrypt(settings.Kek, data); break; case GlobalKeyType.Kek: settings.Kek = GXDLMSSecureClient.Decrypt(settings.Kek, data); break; default: //Invalid type e.Error = ErrorCode.ReadWriteDenied; break; } } } catch (Exception) { e.Error = ErrorCode.ReadWriteDenied; } } else { e.Error = ErrorCode.ReadWriteDenied; } //Return standard reply. return(null); }
/// <summary> /// Constructor. /// </summary> /// <param name="client">DLMS Client.</param> /// <param name="media">Media.</param> /// <param name="trace">Trace level.</param> /// <param name="invocationCounter">Logical name of invocation counter.</param> /// <param name="iec">Is optical head used.</param> public GXDLMSReader(GXDLMSSecureClient client, IGXMedia media, TraceLevel trace, string invocationCounter, bool useOpticalHead) { Trace = trace; Media = media; Client = client; InvocationCounter = invocationCounter; UseOpticalHead = useOpticalHead; }
public GXNotifyClient(bool useLogicalNameReferencing, int interfaceType, string systemTitle, string blockCipherKey) { Notify = new GXReplyData(); Reply = new GXByteBuffer(); Client = new GXDLMSSecureClient(useLogicalNameReferencing, -1, -1, Authentication.None, null, (InterfaceType)interfaceType); Client.Ciphering.SystemTitle = GXCommon.HexToBytes(systemTitle); Client.Ciphering.BlockCipherKey = GXCommon.HexToBytes(blockCipherKey); DataReceived = DateTime.MinValue; }
/// <summary> /// Constructor. /// </summary> public GXCommunicatation(GXDLMSSecureClient dlms, IGXMedia media, bool initializeIEC, Authentication authentication, string password) { Client = dlms; Media = media; InitializeIEC = initializeIEC; Client.Authentication = authentication; Client.Password = ASCIIEncoding.ASCII.GetBytes(password); //Delete trace file if exists. if (File.Exists("trace.txt")) { File.Delete("trace.txt"); } }
/// <summary> /// Constructor. /// </summary> /// <param name="client">DLMS Client.</param> /// <param name="media">Media.</param> /// <param name="logger">Logger.</param> /// <param name="trace">Trace level.</param> public GXDLMSReader( GXDLMSSecureClient client, IGXMedia media, ILogger logger, TraceLevel trace, int wt, int retry, UInt64 deviceId) { WaitTime = wt * 1000; RetryCount = retry; Trace = trace; Media = media; Client = client; _logger = logger; DeviceId = deviceId; }
public void Close() { if (Media != null && Client != null) { try { Console.WriteLine("Disconnecting from the meter."); GXReplyData reply = new GXReplyData(); ReadDLMSPacket(Client.DisconnectRequest(), reply); Media.Close(); } catch { } Media = null; Client = null; } }
byte[] IGXDLMSBase.Invoke(GXDLMSSettings settings, ValueEventArgs e) { if (e.Index == 2) { foreach (object tmp in e.Parameters as object[]) { object[] item = tmp as object[]; GlobalKeyType type = (GlobalKeyType)Convert.ToInt32(item[0]); byte[] data = (byte[])item[1]; switch (type) { case GlobalKeyType.UnicastEncryption: case GlobalKeyType.BroadcastEncryption: //Invalid type e.Error = ErrorCode.ReadWriteDenied; break; case GlobalKeyType.Authentication: //if settings.Cipher is null non secure server is used. settings.Cipher.AuthenticationKey = GXDLMSSecureClient.Decrypt(settings.Kek, data); break; case GlobalKeyType.Kek: settings.Kek = GXDLMSSecureClient.Decrypt(settings.Kek, data); break; default: //Invalid type e.Error = ErrorCode.ReadWriteDenied; break; } } //Return standard reply. return(null); } else { e.Error = ErrorCode.ReadWriteDenied; return(null); } }
/// <summary> /// Updates one or more global keys. /// </summary> /// <param name="client">DLMS client that is used to generate action.</param> /// <param name="kek">Master key, also known as Key Encrypting Key.</param> /// <param name="list">List of Global key types and keys.</param> /// <returns>Generated action.</returns> public byte[][] GlobalKeyTransfer(GXDLMSClient client, byte[] kek, List <KeyValuePair <GlobalKeyType, byte[]> > list) { if (list == null || list.Count == 0) { throw new ArgumentException("Invalid list. It is empty."); } GXByteBuffer bb = new GXByteBuffer(); bb.SetUInt8(DataType.Array); bb.SetUInt8((byte)list.Count); byte[] tmp; foreach (KeyValuePair <GlobalKeyType, byte[]> it in list) { bb.SetUInt8(DataType.Structure); bb.SetUInt8(2); GXCommon.SetData(client.Settings, bb, DataType.Enum, it.Key); tmp = GXDLMSSecureClient.Encrypt(kek, it.Value); GXCommon.SetData(client.Settings, bb, DataType.OctetString, tmp); } return(client.Method(this, 2, bb.Array(), DataType.Array)); }
/// <summary> /// Start to use new keys after reply is generated. /// </summary> /// <param name="settings">DLMS settings.</param> /// <param name="e"></param> internal void ApplyKeys(GXDLMSSettings settings, ValueEventArgs e) { try { foreach (List <object> item in e.Parameters as List <object> ) { GlobalKeyType type = (GlobalKeyType)Convert.ToInt32(item[0]); byte[] data = (byte[])item[1]; switch (type) { case GlobalKeyType.UnicastEncryption: settings.Cipher.BlockCipherKey = GXDLMSSecureClient.Decrypt(settings.Kek, data); break; case GlobalKeyType.BroadcastEncryption: //Invalid type e.Error = ErrorCode.ReadWriteDenied; break; case GlobalKeyType.Authentication: //if settings.Cipher is null non secure server is used. settings.Cipher.AuthenticationKey = GXDLMSSecureClient.Decrypt(settings.Kek, data); break; case GlobalKeyType.Kek: settings.Kek = GXDLMSSecureClient.Decrypt(settings.Kek, data); break; default: //Invalid type e.Error = ErrorCode.ReadWriteDenied; break; } } } catch (Exception) { e.Error = ErrorCode.ReadWriteDenied; } }
/// <summary> /// Close connection to the meter. /// </summary> public void Close() { if (Media != null && Client != null) { try { if (Trace > TraceLevel.Info) { Console.WriteLine("Disconnecting from the meter."); } GXReplyData reply = new GXReplyData(); try { if ((Client.ConnectionState & ConnectionState.Dlms) != 0 && (Client.InterfaceType == InterfaceType.WRAPPER || Client.Ciphering.Security != (byte)Security.None)) { ReadDataBlock(Client.ReleaseRequest(), reply); } } catch (Exception ex) { //All meters don't support Release. Console.WriteLine("Release failed. " + ex.Message); } reply.Clear(); if (Client.ConnectionState != 0) { ReadDLMSPacket(Client.DisconnectRequest(), reply); } Media.Close(); } catch { } Media = null; Client = null; } }
/// <summary> /// Close connection to the meter. /// </summary> public void Close() { if (Media != null && Client != null) { try { if (Trace > TraceLevel.Info) { Console.WriteLine("Disconnecting from the meter."); } GXReplyData reply = new GXReplyData(); try { //Release is call only for secured connections. //All meters are not supporting Release and it's causing problems. if (Client.InterfaceType == InterfaceType.WRAPPER || (Client.InterfaceType == InterfaceType.HDLC && Client.Ciphering.Security != (byte)Security.None)) { ReadDataBlock(Client.ReleaseRequest(), reply); } } catch (Exception ex) { //All meters don't support Release. Console.WriteLine("Release failed. " + ex.Message); } reply.Clear(); ReadDLMSPacket(Client.DisconnectRequest(), reply); Media.Close(); } catch { } Media = null; Client = null; } }
private async void DoWork(object ínfo) { //Give some time DB server to start up. Thread.Sleep(1000); GetNextTaskResponse ret = null; System.Net.Http.HttpResponseMessage response; //Don't wait reply. It might that DB server is not up yet. if (ínfo != null) { using (response = await client.PostAsJsonAsync(Startup.ServerAddress + "/api/reader/AddReader", new AddReader() { Reader = ínfo as GXReaderInfo })) { Helpers.CheckStatus(response); } } _logger.LogInformation("Reader Service is started."); while (!_cancellationToken.IsCancellationRequested) { try { using (response = await client.PostAsJsonAsync(Startup.ServerAddress + "/api/task/GetNextTask", new GetNextTask())) { Helpers.CheckStatus(response); ret = await response.Content.ReadAsAsync <GetNextTaskResponse>(); } if (ret.Tasks != null) { int pos = 0; GXDevice dev; GXDLMSSecureClient cl; using (response = await client.PostAsJsonAsync(Startup.ServerAddress + "/api/device/ListDevices", new ListDevices() { Ids = new[] { ret.Tasks[0].Object.DeviceId } })) { Helpers.CheckStatus(response); ListDevicesResponse r = await response.Content.ReadAsAsync <ListDevicesResponse>(); if (r.Devices == null || r.Devices.Length == 0) { continue; } dev = r.Devices[0]; } IGXMedia media; if (string.Compare(dev.MediaType, typeof(GXNet).FullName, true) == 0) { media = new GXNet(); } else if (string.Compare(dev.MediaType, typeof(GXSerial).FullName, true) == 0) { media = new GXSerial(); } else if (string.Compare(dev.MediaType, typeof(GXTerminal).FullName, true) == 0) { media = new GXTerminal(); } else { Type type = Type.GetType(dev.MediaType); if (type == null) { string ns = ""; pos = dev.MediaType.LastIndexOf('.'); if (pos != -1) { ns = dev.MediaType.Substring(0, pos); } foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { if (assembly.GetName().Name == ns) { if (assembly.GetType(dev.MediaType, false, true) != null) { type = assembly.GetType(dev.MediaType); } } } } if (type == null) { throw new Exception("Invalid media type: " + dev.MediaType); } media = (IGXMedia)Activator.CreateInstance(type); } if (media == null) { throw new Exception("Unknown media type '" + dev.MediaType + "'."); } media.Settings = dev.MediaSettings; GXDLMSReader reader; //Read frame counter from the meter. if (dev.Security != 0) { cl = new GXDLMSSecureClient(dev.UseLogicalNameReferencing, 16, dev.PhysicalAddress, Authentication.None, null, (InterfaceType)dev.InterfaceType); reader = new GXDLMSReader(cl, media, _logger); media.Open(); reader.InitializeConnection(); //Read Innovation counter. GXDLMSData d = new GXDLMSData(dev.FrameCounter); reader.Read(d, 2); dev.InvocationCounter = 1 + Convert.ToUInt32(d.Value); reader.Disconnect(); media.Close(); } cl = new GXDLMSSecureClient(dev.UseLogicalNameReferencing, dev.ClientAddress, dev.PhysicalAddress, (Authentication)dev.Authentication, dev.Password, (InterfaceType)dev.InterfaceType); if (dev.HexPassword != null && dev.HexPassword.Length != 0) { cl.Password = dev.HexPassword; } cl.UseUtc2NormalTime = dev.UtcTimeZone; cl.Standard = dev.Standard; cl.Ciphering.SystemTitle = GXCommon.HexToBytes(dev.ClientSystemTitle); if (cl.Ciphering.SystemTitle != null && cl.Ciphering.SystemTitle.Length == 0) { cl.Ciphering.SystemTitle = null; } cl.Ciphering.BlockCipherKey = GXCommon.HexToBytes(dev.BlockCipherKey); if (cl.Ciphering.BlockCipherKey != null && cl.Ciphering.BlockCipherKey.Length == 0) { cl.Ciphering.BlockCipherKey = null; } cl.Ciphering.AuthenticationKey = GXCommon.HexToBytes(dev.AuthenticationKey); if (cl.Ciphering.AuthenticationKey != null && cl.Ciphering.AuthenticationKey.Length == 0) { cl.Ciphering.AuthenticationKey = null; } cl.ServerSystemTitle = GXCommon.HexToBytes(dev.DeviceSystemTitle); if (cl.ServerSystemTitle != null && cl.ServerSystemTitle.Length == 0) { cl.ServerSystemTitle = null; } cl.Ciphering.InvocationCounter = dev.InvocationCounter; cl.Ciphering.Security = (Security)dev.Security; reader = new GXDLMSReader(cl, media, _logger); media.Open(); reader.InitializeConnection(); pos = 0; int count = ret.Tasks.Length; foreach (GXTask task in ret.Tasks) { ++pos; try { GXDLMSObject obj = GXDLMSClient.CreateObject((ObjectType)task.Object.ObjectType); obj.LogicalName = task.Object.LogicalName; obj.ShortName = task.Object.ShortName; if (task.TaskType == TaskType.Write) { if (obj.LogicalName == "0.0.1.1.0.255" && task.Index == 2) { cl.UpdateValue(obj, task.Index, GXDateTime.ToUnixTime(DateTime.UtcNow)); } else { cl.UpdateValue(obj, task.Index, GXDLMSTranslator.XmlToValue(task.Data)); } reader.Write(obj, task.Index); } else if (task.TaskType == TaskType.Action) { reader.Method(obj, task.Index, GXDLMSTranslator.XmlToValue(task.Data), DataType.None); } else if (task.TaskType == TaskType.Read) { //Reading the meter. if (task.Object.Attributes[0].DataType != 0) { obj.SetDataType(task.Index, (DataType)task.Object.Attributes[0].DataType); } if (task.Object.Attributes[0].UIDataType != 0) { obj.SetUIDataType(task.Index, (DataType)task.Object.Attributes[0].UIDataType); } Reader.Read(_logger, client, reader, task, media, obj); if (task.Object.Attributes[0].DataType == 0) { task.Object.Attributes[0].DataType = (int)obj.GetDataType(task.Index); if (task.Object.Attributes[0].UIDataType == 0) { task.Object.Attributes[0].UIDataType = (int)obj.GetUIDataType(task.Index); } UpdateDatatype u = new UpdateDatatype() { Items = new GXAttribute[] { task.Object.Attributes[0] } }; response = client.PostAsJsonAsync(Startup.ServerAddress + "/api/Object/UpdateDatatype", u).Result; Helpers.CheckStatus(response); } } } catch (Exception ex) { task.Result = ex.Message; AddError error = new AddError(); error.Error = new GXError() { DeviceId = dev.Id, Error = "Failed to " + task.TaskType + " " + task.Object.LogicalName + ":" + task.Index + ". " + ex.Message }; _logger.LogError(error.Error.Error); using (response = await client.PostAsJsonAsync(Startup.ServerAddress + "/api/error/AddError", error)) { Helpers.CheckStatus(response); await response.Content.ReadAsAsync <AddErrorResponse>(); } } task.End = DateTime.Now; //Close connection after last task is executed. //This must done because there might be new task to execute. if (count == pos) { try { reader.Close(); } catch (Exception ex) { task.Result = ex.Message; AddError error = new AddError(); error.Error = new GXError() { DeviceId = dev.Id, Error = "Failed to close the connection. " + ex.Message }; _logger.LogError(error.Error.Error); using (response = await client.PostAsJsonAsync(Startup.ServerAddress + "/api/error/AddError", error)) { Helpers.CheckStatus(response); await response.Content.ReadAsAsync <AddErrorResponse>(); } } } //Update execution time. response = client.PostAsJsonAsync(Startup.ServerAddress + "/api/task/TaskReady", new TaskReady() { Tasks = new GXTask[] { task } }).Result; Helpers.CheckStatus(response); } } else { try { using (response = await client.PostAsJsonAsync(Startup.ServerAddress + "/api/task/WaitChange", new WaitChange() { Change = TargetType.Tasks, Time = lastUpdated, WaitTime = _waitTime })) { Helpers.CheckStatus(response); { WaitChangeResponse r = await response.Content.ReadAsAsync <WaitChangeResponse>(); if (r.Time > lastUpdated) { lastUpdated = r.Time; } } } } catch (Exception ex) { if (!_cancellationToken.IsCancellationRequested) { break; } _cancellationToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(10)); } } } catch (Exception ex) { //If app is closing. if (_cancellationToken.IsCancellationRequested) { break; } if (ret == null) { _logger.LogError("Failed to connect to the DB server."); } else { AddError error = new AddError(); error.Error = new GXError() { DeviceId = ret.Tasks[0].Object.DeviceId, Error = "Failed to " + ret.Tasks[0].TaskType + " " + ret.Tasks[0].Object.LogicalName + ":" + ret.Tasks[0].Index + ". " + ex.Message }; using (response = await client.PostAsJsonAsync(Startup.ServerAddress + "/api/error/AddError", error)) { if (ret.Tasks != null) { DateTime now = DateTime.Now; foreach (GXTask it in ret.Tasks) { it.Result = ex.Message; it.End = now; } response = await client.PostAsJsonAsync(Startup.ServerAddress + "/api/task/TaskReady", new TaskReady() { Tasks = ret.Tasks }); } } } _logger.LogError(ex.Message); } } }
/// <summary> /// Update ephemeral keys. /// </summary> /// <param name="value"></param> /// <returns>List of Parsed key id and GUAK. This is for debugging purpose.</returns> public List <KeyValuePair <GlobalKeyType, byte[]> > UpdateEphemeralKeys(GXDLMSSecureClient client, byte[] value) { return(UpdateEphemeralKeys(client, new GXByteBuffer(value))); }
/// <summary> /// Update ephemeral keys. /// </summary> /// <param name="client">DLMS Client.</param> /// <param name="value">Received reply from the server.</param> /// <returns>List of Parsed key id and GUAK. This is for debugging purpose.</returns> public List <KeyValuePair <GlobalKeyType, byte[]> > UpdateEphemeralKeys(GXDLMSSecureClient client, GXByteBuffer value) { if (client == null) { throw new ArgumentNullException(nameof(client)); } if (value.GetUInt8() != (byte)DataType.Array) { throw new ArgumentOutOfRangeException("Invalid tag."); } GXEcdsa c = new GXEcdsa(client.Ciphering.EphemeralKeyPair.Key); int count = GXCommon.GetObjectCount(value); List <KeyValuePair <GlobalKeyType, byte[]> > list = new List <KeyValuePair <GlobalKeyType, byte[]> >(); for (int pos = 0; pos != count; ++pos) { if (value.GetUInt8() != (byte)DataType.Structure) { throw new ArgumentOutOfRangeException("Invalid tag."); } if (value.GetUInt8() != 2) { throw new ArgumentOutOfRangeException("Invalid length."); } if (value.GetUInt8() != (byte)DataType.Enum) { throw new ArgumentOutOfRangeException("Invalid key id data type."); } int keyId = value.GetUInt8(); if (keyId > 4) { throw new ArgumentOutOfRangeException("Invalid key type."); } if (value.GetUInt8() != (byte)DataType.OctetString) { throw new ArgumentOutOfRangeException("Invalid tag."); } if (GXCommon.GetObjectCount(value) != 128) { throw new ArgumentOutOfRangeException("Invalid length."); } //Get ephemeral public key server. GXByteBuffer key = new GXByteBuffer(); key.SetUInt8(4); key.Set(value, 64); GXPublicKey targetEphemeralKey = GXPublicKey.FromRawBytes(key.Array()); //Get ephemeral public key signature server. byte[] signature = new byte[64]; value.Get(signature); key.SetUInt8(0, (byte)keyId); //Verify signature. if (!GXSecure.ValidateEphemeralPublicKeySignature(key.Array(), signature, client.Ciphering.SigningKeyPair.Value)) { throw new GXDLMSCipherException("Invalid signature."); } byte[] z = c.GenerateSecret(targetEphemeralKey); System.Diagnostics.Debug.WriteLine("Shared secret:" + GXCommon.ToHex(z, true)); GXByteBuffer kdf = new GXByteBuffer(); kdf.Set(GXSecure.GenerateKDF(client.SecuritySuite, z, AlgorithmId.AesGcm128, client.Ciphering.SystemTitle, client.Settings.SourceSystemTitle, null, null)); System.Diagnostics.Debug.WriteLine("KDF:" + kdf.ToString()); list.Add(new KeyValuePair <GlobalKeyType, byte[]>((GlobalKeyType)keyId, kdf.SubArray(0, 16))); } //Update ephemeral keys. foreach (KeyValuePair <GlobalKeyType, byte[]> it in list) { switch (it.Key) { case GlobalKeyType.UnicastEncryption: client.Settings.EphemeralBlockCipherKey = it.Value; break; case GlobalKeyType.BroadcastEncryption: client.Settings.EphemeralBroadcastBlockCipherKey = it.Value; break; case GlobalKeyType.Authentication: client.Settings.EphemeralAuthenticationKey = it.Value; break; case GlobalKeyType.Kek: client.Settings.EphemeralKek = it.Value; break; } } return(list); }
byte[] IGXDLMSBase.Invoke(GXDLMSSettings settings, ValueEventArgs e) { if (e.Index == 1) { SecurityPolicy = (SecurityPolicy)e.Parameters; } else if (e.Index == 2) { try { foreach (List <object> item in e.Parameters as List <object> ) { GlobalKeyType type = (GlobalKeyType)Convert.ToInt32(item[0]); byte[] data = (byte[])item[1]; //if settings.Cipher is null non secure server is used. //Keys are take in action after reply is generated. switch (type) { case GlobalKeyType.UnicastEncryption: GXDLMSSecureClient.Decrypt(settings.Kek, data); break; case GlobalKeyType.BroadcastEncryption: //Invalid type e.Error = ErrorCode.ReadWriteDenied; break; case GlobalKeyType.Authentication: GXDLMSSecureClient.Decrypt(settings.Kek, data); break; case GlobalKeyType.Kek: GXDLMSSecureClient.Decrypt(settings.Kek, data); break; default: //Invalid type e.Error = ErrorCode.ReadWriteDenied; break; } } } catch (Exception) { e.Error = ErrorCode.ReadWriteDenied; } } else if (e.Index == 3) { // key_agreement try { List <Object> tmp = (List <Object>)(e.Parameters as List <Object>)[0]; byte keyId = (byte)tmp[0]; if (keyId != 0) { e.Error = ErrorCode.InconsistentClass; } else { byte[] data = (byte[])tmp[0]; // ephemeral public key GXByteBuffer data2 = new GXByteBuffer(65); data2.SetUInt8(keyId); data2.Set(data, 0, 64); GXByteBuffer sign = new GXByteBuffer(); sign.Set(data, 64, 64); GXPublicKey pk = null; string subject = SystemTitleToSubject(settings.SourceSystemTitle); foreach (GXx509Certificate it in settings.Cipher.Certificates) { if ((it.KeyUsage & KeyUsage.DigitalSignature) != 0 && it.Subject == subject) { pk = it.PublicKey; break; } } if (pk == null) //TODO:|| !GXSecure.ValidateEphemeralPublicKeySignature(data2.Array(), sign.Array(), pk)) { e.Error = ErrorCode.InconsistentClass; settings.TargetEphemeralKey = null; } else { settings.TargetEphemeralKey = GXPublicKey.FromRawBytes(data2.SubArray(1, 64)); // Generate ephemeral keys. KeyValuePair <GXPrivateKey, GXPublicKey> eKpS = settings.Cipher.EphemeralKeyPair; if (eKpS.Key == null) { eKpS = GXEcdsa.GenerateKeyPair(GetEcc(SecuritySuite)); settings.Cipher.EphemeralKeyPair = eKpS; } // Generate shared secret. return(null); } } } catch (Exception) { e.Error = ErrorCode.InconsistentClass; } } else if (e.Index == 4) { // generate_key_pair CertificateType key = (CertificateType)(int)e.Parameters; KeyValuePair <GXPrivateKey, GXPublicKey> value = GXEcdsa.GenerateKeyPair(GetEcc(SecuritySuite)); switch (key) { case CertificateType.DigitalSignature: settings.Cipher.SigningKeyPair = value; break; case CertificateType.KeyAgreement: settings.Cipher.KeyAgreementKeyPair = value; break; default: e.Error = ErrorCode.InconsistentClass; break; } } else if (e.Index == 5) { // generate_certificate_request CertificateType key = (CertificateType)(int)e.Parameters; try { KeyValuePair <GXPrivateKey, GXPublicKey> kp = default(KeyValuePair <GXPrivateKey, GXPublicKey>); switch (key) { case CertificateType.DigitalSignature: kp = settings.Cipher.SigningKeyPair; break; case CertificateType.KeyAgreement: kp = settings.Cipher.KeyAgreementKeyPair; break; default: break; } if (kp.Key != null) { GXPkcs10 pkc10 = GXPkcs10.CreateCertificateSigningRequest(kp, SystemTitleToSubject(settings.Cipher.SystemTitle)); return(pkc10.Encoded); } else { e.Error = ErrorCode.ReadWriteDenied; } } catch (Exception) { e.Error = ErrorCode.ReadWriteDenied; } } else if (e.Index == 6) { // import_certificate GXx509Certificate cert = new GXx509Certificate((byte[])e.Parameters); if (cert.KeyUsage == 0) { // At least one bit must be used. e.Error = ErrorCode.InconsistentClass; } else { settings.Cipher.Certificates.Add(cert); } } else if (e.Index == 7) { // export_certificate List <Object> tmp = (List <Object>)e.Parameters; short type = (short)tmp[0]; GXx509Certificate cert = null; lock (settings.Cipher.Certificates) { if (type == 0) { tmp = (List <Object>)tmp[1]; cert = FindCertificateByEntity(settings, (CertificateEntity)tmp[0], (CertificateType)tmp[1], (byte[])tmp[2]); } else if (type == 1) { tmp = (List <Object>)tmp[1]; cert = FindCertificateBySerial(settings, (byte[])tmp[1], ASCIIEncoding.ASCII.GetString((byte[])tmp[2])); } if (cert == null) { e.Error = ErrorCode.InconsistentClass; } else { return(cert.Encoded); } } } else if (e.Index == 8) { // remove_certificate List <Object> tmp = (List <Object>)((List <object>)e.Parameters)[0]; short type = (short)tmp[0]; GXx509Certificate cert = null; lock (settings.Cipher.Certificates) { if (type == 0) { cert = FindCertificateByEntity(settings, (CertificateEntity)tmp[1], (CertificateType)tmp[2], (byte[])tmp[3]); } else if (type == 1) { cert = FindCertificateBySerial(settings, (byte[])tmp[1], ASCIIEncoding.ASCII.GetString((byte[])tmp[2])); } if (cert == null) { e.Error = ErrorCode.InconsistentClass; } else { settings.Cipher.Certificates.Remove(cert); } } } else { e.Error = ErrorCode.ReadWriteDenied; } //Return standard reply. return(null); }
/// <summary> /// Constructor. /// </summary> public GXDLMSDevice() { GXDLMSSecureClient client = new GXDLMSSecureClient(); Objects = client.Objects; }
static void Main(string[] args) { IGXMedia media = null; GXCommunicatation comm = null; try { TextWriter logFile = new StreamWriter(File.Open("LogFile.txt", FileMode.Create)); //////////////////////////////////////// //Handle command line parameters. String sn, client = "", server = "", id = "", host = "", port = "", pw = ""; bool trace = false, iec = true; bool hdlc = true, ln = true; Authentication auth = Authentication.None; foreach (string it in args) { String item = it.Trim().ToLower(); if (string.Compare(item, "/u", true) == 0)//Update { //Get latest manufacturer settings from Gurux web server. GXManufacturerCollection.UpdateManufactureSettings(); } else if (item.StartsWith("/sn="))//Serial number. { sn = item.Replace("/sn=", ""); } else if (string.Compare(item, "/wrapper", true) == 0)//Wrapper is used. { hdlc = false; } else if (item.StartsWith("/r="))//referencing { id = item.Replace("/r=", ""); } else if (item.StartsWith("/m="))//Manufacturer { id = item.Replace("/m=", ""); } else if (item.StartsWith("/client="))//Client address { client = item.Replace("/client=", ""); } else if (item.StartsWith("/server="))//Server address { server = item.Replace("/server=", ""); } else if (item.StartsWith("/h=")) //Host { host = item.Replace("/h=", ""); } else if (item.StartsWith("/p="))// TCP/IP Port { media = new Gurux.Net.GXNet(); port = item.Replace("/p=", ""); } else if (item.StartsWith("/sp="))//Serial Port { port = item.Replace("/sp=", ""); media = new GXSerial(); } else if (item.StartsWith("/t"))//Are messages traced. { trace = true; } else if (item.StartsWith("/s="))//Start { String tmp = item.Replace("/s=", ""); iec = string.Compare(tmp, "dlms", true) != 0; } else if (item.StartsWith("/a="))//Authentication { auth = (Authentication)Enum.Parse(typeof(Authentication), it.Trim().Replace("/a=", "")); } else if (item.StartsWith("/pw="))//Password { pw = it.Trim().Replace("/pw=", ""); } else { ShowHelp(); return; } } if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(port) || (media is GXNet && string.IsNullOrEmpty(host))) { ShowHelp(); return; } //////////////////////////////////////// //Initialize connection settings. if (media is GXSerial) { GXSerial serial = media as GXSerial; serial.PortName = port; if (iec) { serial.BaudRate = 300; serial.DataBits = 7; serial.Parity = System.IO.Ports.Parity.Even; serial.StopBits = System.IO.Ports.StopBits.One; } else { serial.BaudRate = 9600; serial.DataBits = 8; serial.Parity = System.IO.Ports.Parity.None; serial.StopBits = System.IO.Ports.StopBits.One; } } else if (media is GXNet) { Gurux.Net.GXNet net = media as GXNet; net.Port = Convert.ToInt32(port); net.HostName = host; net.Protocol = Gurux.Net.NetworkType.Tcp; } else { throw new Exception("Unknown media type."); } //////////////////////////////////////// //Update manufacturer depended settings. GXManufacturerCollection Manufacturers = new GXManufacturerCollection(); GXManufacturerCollection.ReadManufacturerSettings(Manufacturers); GXManufacturer man = Manufacturers.FindByIdentification(id); if (man == null) { throw new Exception("Unknown manufacturer: " + id); } GXDLMSSecureClient dlms = new GXDLMSSecureClient(); //Update Obis code list so we can get right descriptions to the objects. dlms.CustomObisCodes = man.ObisCodes; comm = new GXCommunicatation(dlms, media, iec, auth, pw); comm.Trace = trace; comm.InitializeConnection(man); GXDLMSObjectCollection objects = null; string path = host.Replace('.', '_') + "_" + port.ToString() + ".xml"; List <Type> extraTypes = new List <Type>(Gurux.DLMS.GXDLMSClient.GetObjectTypes()); extraTypes.Add(typeof(GXDLMSAttributeSettings)); extraTypes.Add(typeof(GXDLMSAttribute)); XmlSerializer x = new XmlSerializer(typeof(GXDLMSObjectCollection), extraTypes.ToArray()); //You can save association view, but make sure that it is not change. //Save Association view to the cache so it is not needed to retrieve every time. /* * if (File.Exists(path)) * { * try * { * using (Stream stream = File.Open(path, FileMode.Open)) * { * Console.WriteLine("Get available objects from the cache."); * objects = x.Deserialize(stream) as GXDLMSObjectCollection; * stream.Close(); * } * } * catch (Exception ex) * { * if (File.Exists(path)) * { * File.Delete(path); * } * throw ex; * } * } * else */ { Console.WriteLine("Get available objects from the device."); objects = comm.GetAssociationView(); GXDLMSObjectCollection objs = objects.GetObjects(new ObjectType[] { ObjectType.Register, ObjectType.ExtendedRegister, ObjectType.DemandRegister }); Console.WriteLine("Read scalers and units from the device."); if ((dlms.NegotiatedConformance & Conformance.MultipleReferences) != 0) { List <KeyValuePair <GXDLMSObject, int> > list = new List <KeyValuePair <GXDLMSObject, int> >(); foreach (GXDLMSObject it in objs) { if (it is GXDLMSRegister) { list.Add(new KeyValuePair <GXDLMSObject, int>(it, 3)); } if (it is GXDLMSDemandRegister) { list.Add(new KeyValuePair <GXDLMSObject, int>(it, 4)); } } comm.ReadList(list); } else { //Read values one by one. foreach (GXDLMSObject it in objs) { try { if (it is GXDLMSRegister) { Console.WriteLine(it.Name); comm.Read(it, 3); } if (it is GXDLMSDemandRegister) { Console.WriteLine(it.Name); comm.Read(it, 4); } } catch { //Actaric SL7000 can return error here. Continue reading. } } } //Read Profile Generic columns first. foreach (GXDLMSObject it in objects.GetObjects(ObjectType.ProfileGeneric)) { try { Console.WriteLine(it.Name); comm.Read(it, 3); GXDLMSObject[] cols = (it as GXDLMSProfileGeneric).GetCaptureObject(); TraceLine(logFile, "Profile Generic " + it.Name + " Columns:"); StringBuilder sb = new StringBuilder(); bool First = true; foreach (GXDLMSObject col in cols) { if (!First) { sb.Append(" | "); } First = false; sb.Append(col.Name); sb.Append(" "); sb.Append(col.Description); } TraceLine(logFile, sb.ToString()); } catch (Exception ex) { TraceLine(logFile, "Err! Failed to read columns:" + ex.Message); //Continue reading. } } try { using (Stream stream = File.Open(path, FileMode.Create)) { TextWriter writer = new StreamWriter(stream); x.Serialize(writer, objects); writer.Close(); stream.Close(); } } catch (Exception ex) { if (File.Exists(path)) { File.Delete(path); } throw ex; } Console.WriteLine("--- Available objects ---"); foreach (GXDLMSObject it in objects) { Console.WriteLine(it.Name + " " + it.Description); } } foreach (GXDLMSObject it in objects) { // Profile generics are read later because they are special cases. // (There might be so lots of data and we so not want waste time to read all the data.) if (it is GXDLMSProfileGeneric) { continue; } if (!(it is IGXDLMSBase)) { //If interface is not implemented. //Example manufacturer spesific interface. Console.WriteLine("Unknown Interface: " + it.ObjectType.ToString()); continue; } TraceLine(logFile, "-------- Reading " + it.GetType().Name + " " + it.Name + " " + it.Description); foreach (int pos in (it as IGXDLMSBase).GetAttributeIndexToRead()) { try { object val = comm.Read(it, pos); //If data is array. if (val is byte[]) { val = GXCommon.ToHex((byte[])val, true); } else if (val is Array) { string str = ""; for (int pos2 = 0; pos2 != (val as Array).Length; ++pos2) { if (str != "") { str += ", "; } if ((val as Array).GetValue(pos2) is byte[]) { str += GXCommon.ToHex((byte[])(val as Array).GetValue(pos2), true); } else { str += (val as Array).GetValue(pos2).ToString(); } } val = str; } else if (val is System.Collections.IList) { string str = "["; bool empty = true; foreach (object it2 in val as System.Collections.IList) { if (!empty) { str += ", "; } empty = false; if (it2 is byte[]) { str += GXCommon.ToHex((byte[])it2, true); } else { str += it2.ToString(); } } str += "]"; val = str; } TraceLine(logFile, "Index: " + pos + " Value: " + val); } catch (Exception ex) { TraceLine(logFile, "Error! Index: " + pos + " " + ex.Message); } } } //Find profile generics and read them. foreach (GXDLMSObject it in objects.GetObjects(ObjectType.ProfileGeneric)) { TraceLine(logFile, "-------- Reading " + it.GetType().Name + " " + it.Name + " " + it.Description); long entriesInUse = Convert.ToInt64(comm.Read(it, 7)); long entries = Convert.ToInt64(comm.Read(it, 8)); TraceLine(logFile, "Entries: " + entriesInUse + "/" + entries); //If there are no columns or rows. if (entriesInUse == 0 || (it as GXDLMSProfileGeneric).CaptureObjects.Count == 0) { continue; } //All meters are not supporting parameterized read. if ((dlms.NegotiatedConformance & (Conformance.ParameterizedAccess | Conformance.SelectiveAccess)) != 0) { try { //Read first row from Profile Generic. object[] rows = comm.ReadRowsByEntry(it as GXDLMSProfileGeneric, 1, 1); StringBuilder sb = new StringBuilder(); foreach (object[] row in rows) { foreach (object cell in row) { if (cell is byte[]) { sb.Append(GXCommon.ToHex((byte[])cell, true)); } else { sb.Append(Convert.ToString(cell)); } sb.Append(" | "); } sb.Append("\r\n"); } Trace(logFile, sb.ToString()); } catch (Exception ex) { TraceLine(logFile, "Error! Failed to read first row: " + ex.Message); //Continue reading. } } //All meters are not supporting parameterized read. if ((dlms.NegotiatedConformance & (Conformance.ParameterizedAccess | Conformance.SelectiveAccess)) != 0) { try { //Read last day from Profile Generic. object[] rows = comm.ReadRowsByRange(it as GXDLMSProfileGeneric, DateTime.Now.Date, DateTime.MaxValue); StringBuilder sb = new StringBuilder(); foreach (object[] row in rows) { foreach (object cell in row) { if (cell is byte[]) { sb.Append(GXCommon.ToHex((byte[])cell, true)); } else { sb.Append(Convert.ToString(cell)); } sb.Append(" | "); } sb.Append("\r\n"); } Trace(logFile, sb.ToString()); } catch (Exception ex) { TraceLine(logFile, "Error! Failed to read last day: " + ex.Message); //Continue reading. } } } logFile.Flush(); logFile.Close(); } catch (Exception ex) { if (comm != null) { comm.Close(); } Console.WriteLine(ex.Message); if (!System.Diagnostics.Debugger.IsAttached) { Console.ReadKey(); } } finally { if (comm != null) { comm.Close(); } if (System.Diagnostics.Debugger.IsAttached) { Console.WriteLine("Ended. Press any key to continue."); Console.ReadKey(); } } }
/// <summary> /// Constructor. /// </summary> /// <param name="client">DLMS Client.</param> /// <param name="media">Media.</param> public GXDLMSReader(GXDLMSSecureClient client, IGXMedia media, TraceLevel trace) { Trace = trace; Media = media; Client = client; }
/// <summary> /// Read data from the meter. /// </summary> private static void ReadMeter(object parameter) { GXDLMSReader reader = null; System.Net.Http.HttpClient httpClient = Helpers.client; using (GXNet media = (GXNet)parameter) { try { var config = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: true) .Build(); ListenerOptions listener = config.GetSection("Listener").Get <ListenerOptions>(); GXDLMSObjectCollection objects = new GXDLMSObjectCollection(); GXDLMSSecureClient client = new GXDLMSSecureClient(listener.UseLogicalNameReferencing, listener.ClientAddress, listener.ServerAddress, (Authentication)listener.Authentication, listener.Password, (InterfaceType)listener.Interface); reader = new GXDLMSReader(client, media, _logger); GXDLMSData ldn = new GXDLMSData("0.0.42.0.0.255"); ldn.SetUIDataType(2, DataType.String); reader.InitializeConnection(); reader.Read(ldn, 2); Console.WriteLine("Meter connected: " + ldn.Value); //Find device. GXDevice dev = null; ListDevicesResponse devs = null; { using (System.Net.Http.HttpResponseMessage response = httpClient.PostAsJsonAsync(Startup.ServerAddress + "/api/device/ListDevices", new ListDevices() { Name = (string)ldn.Value }).Result) { Helpers.CheckStatus(response); devs = response.Content.ReadAsAsync <ListDevicesResponse>().Result; } //If device is unknown. if (devs.Devices.Length == 0) { if (listener.DefaultDeviceTemplate == 0) { string str = "Unknown Meter try to connect to the Gurux.DLMS.AMI server: " + ldn.Value; Console.WriteLine(str); AddSystemError info = new AddSystemError(); info.Error = new GXSystemError() { Error = str }; using (System.Net.Http.HttpResponseMessage response = httpClient.PostAsJsonAsync(Startup.ServerAddress + "/api/SystemError/AddSystemError", info).Result) { Helpers.CheckStatus(response); } return; } ListDeviceTemplates lt = new ListDeviceTemplates() { Ids = new UInt64[] { listener.DefaultDeviceTemplate } }; using (System.Net.Http.HttpResponseMessage response = httpClient.PostAsJsonAsync(Startup.ServerAddress + "/api/template/ListDeviceTemplates", lt).Result) { Helpers.CheckStatus(response); ListDeviceTemplatesResponse ret = response.Content.ReadAsAsync <ListDeviceTemplatesResponse>().Result; if (ret.Devices.Length != 1) { throw new Exception("DefaultDeviceTemplate value is invalid: " + listener.DefaultDeviceTemplate); } dev = new GXDevice(); GXDevice.Copy(dev, ret.Devices[0]); dev.Name = Convert.ToString(ldn.Value); dev.TemplateId = listener.DefaultDeviceTemplate; dev.Manufacturer = ret.Devices[0].Name; } dev.Dynamic = true; UpdateDevice update = new UpdateDevice(); update.Device = dev; using (System.Net.Http.HttpResponseMessage response = httpClient.PostAsJsonAsync(Startup.ServerAddress + "/api/device/UpdateDevice", update).Result) { Helpers.CheckStatus(response); UpdateDeviceResponse r = response.Content.ReadAsAsync <UpdateDeviceResponse>().Result; dev.Id = r.DeviceId; } using (System.Net.Http.HttpResponseMessage response = httpClient.PostAsJsonAsync(Startup.ServerAddress + "/api/device/ListDevices", new ListDevices() { Ids = new UInt64[] { dev.Id } }).Result) { Helpers.CheckStatus(response); devs = response.Content.ReadAsAsync <ListDevicesResponse>().Result; } } else if (devs.Devices.Length != 1) { throw new Exception("There are multiple devices with same name: " + ldn.Value); } else { dev = devs.Devices[0]; if (dev.Security != Security.None) { Console.WriteLine("Reading frame counter."); GXDLMSData fc = new GXDLMSData(listener.InvocationCounter); reader.Read(fc, 2); dev.InvocationCounter = 1 + Convert.ToUInt32(fc.Value); Console.WriteLine("Device ID: " + dev.Id + " LDN: " + (string)ldn.Value); Console.WriteLine("Frame counter: " + dev.FrameCounter); } GetNextTaskResponse ret; using (System.Net.Http.HttpResponseMessage response = httpClient.PostAsJsonAsync(Startup.ServerAddress + "/api/task/GetNextTask", new GetNextTask() { Listener = true, DeviceId = dev.Id }).Result) { Helpers.CheckStatus(response); ret = response.Content.ReadAsAsync <GetNextTaskResponse>().Result; } if (ret.Tasks == null || ret.Tasks.Length == 0) { Console.WriteLine("No tasks to execute"); } else { Console.WriteLine("Task count: " + ret.Tasks.Length); if (client.ClientAddress != dev.ClientAddress || dev.Security != Security.None) { reader.Release(); reader.Disconnect(); client = new GXDLMSSecureClient(dev.UseLogicalNameReferencing, dev.ClientAddress, dev.PhysicalAddress, (Authentication)dev.Authentication, dev.Password, dev.InterfaceType); client.UtcTimeZone = dev.UtcTimeZone; client.Standard = (Standard)dev.Standard; if (dev.Conformance != 0) { client.ProposedConformance = (Conformance)dev.Conformance; } client.Priority = dev.Priority; client.ServiceClass = dev.ServiceClass; client.Ciphering.SystemTitle = GXCommon.HexToBytes(dev.ClientSystemTitle); client.Ciphering.BlockCipherKey = GXCommon.HexToBytes(dev.BlockCipherKey); client.Ciphering.AuthenticationKey = GXCommon.HexToBytes(dev.AuthenticationKey); client.ServerSystemTitle = GXCommon.HexToBytes(dev.DeviceSystemTitle); client.Ciphering.InvocationCounter = dev.InvocationCounter; client.Ciphering.Security = (Security)dev.Security; reader = new GXDLMSReader(client, media, _logger); reader.InitializeConnection(); } List <GXValue> values = new List <GXValue>(); foreach (GXTask task in ret.Tasks) { GXDLMSObject obj = GXDLMSClient.CreateObject((ObjectType)task.Object.ObjectType); obj.LogicalName = task.Object.LogicalName; try { if (task.TaskType == TaskType.Write) { if (obj.LogicalName == "0.0.1.1.0.255" && task.Index == 2) { client.UpdateValue(obj, task.Index, GXDateTime.ToUnixTime(DateTime.UtcNow)); } else { client.UpdateValue(obj, task.Index, GXDLMSTranslator.XmlToValue(task.Data)); } reader.Write(obj, task.Index); } else if (task.TaskType == TaskType.Action) { reader.Method(obj, task.Index, GXDLMSTranslator.XmlToValue(task.Data), DataType.None); } else if (task.TaskType == TaskType.Read) { Reader.Reader.Read(null, httpClient, reader, task, media, obj); } } catch (Exception ex) { task.Result = ex.Message; AddError error = new AddError(); error.Error = new GXError() { DeviceId = dev.Id, Error = "Failed to " + task.TaskType + " " + task.Object.LogicalName + ":" + task.Index + ". " + ex.Message }; using (System.Net.Http.HttpResponseMessage response = httpClient.PostAsJsonAsync(Startup.ServerAddress + "/api/error/AddError", error).Result) { Helpers.CheckStatus(response); response.Content.ReadAsAsync <AddErrorResponse>(); } } using (System.Net.Http.HttpResponseMessage response = httpClient.PostAsJsonAsync(Startup.ServerAddress + "/api/task/TaskReady", new TaskReady() { Tasks = new GXTask[] { task } }).Result) { Helpers.CheckStatus(response); } } } } } } catch (Exception ex) { try { AddSystemError info = new AddSystemError(); info.Error = new GXSystemError() { Error = ex.Message }; using (System.Net.Http.HttpResponseMessage response = httpClient.PostAsJsonAsync(Startup.ServerAddress + "/api/SystemError/AddSystemError", info).Result) { Helpers.CheckStatus(response); } } catch (Exception ex2) { } } finally { if (reader != null) { reader.Close(); } } } }
public GXDLMSCommunicator(GXDLMSDevice parent, Gurux.Common.IGXMedia media) { this.parent = parent; this.media = media; client = new GXDLMSSecureClient(); }