/// <summary> /// Generates data notification message. /// </summary> /// <param name="time">Date time. Set To Min or Max if not added</param> /// <param name="objects">List of objects and attribute indexes to notify.</param> /// <returns>Generated data notification message(s).</returns> public byte[][] GenerateDataNotificationMessages(DateTime time, List <KeyValuePair <GXDLMSObject, int> > objects) { if (objects == null) { throw new ArgumentNullException("objects"); } GXByteBuffer buff = new GXByteBuffer(); buff.SetUInt8(DataType.Structure); GXCommon.SetObjectCount(objects.Count, buff); foreach (KeyValuePair <GXDLMSObject, int> it in objects) { AddData(it.Key, it.Value, buff); } byte[][] reply; if (UseLogicalNameReferencing) { GXDLMSLNParameters p = new GXDLMSLNParameters(Settings, 0, Command.DataNotification, 0, null, buff, 0xff); p.time = time; reply = GXDLMS.GetLnMessages(p); } else { GXDLMSSNParameters p = new GXDLMSSNParameters(Settings, Command.DataNotification, 1, 0, buff, null); reply = GXDLMS.GetSnMessages(p); } return(reply); }
///<summary> ///Handle set request. ///</summary> ///<returns> ///Reply to the client. ///</returns> public static void HandleSetRequest(GXDLMSSettings settings, GXDLMSServer server, GXByteBuffer data, GXByteBuffer replyData, GXDLMSTranslatorStructure xml) { //Return error if connection is not established. if (xml == null && settings.Connected == ConnectionState.None && !settings.CanAccess()) { replyData.Set(GXDLMSServer.GenerateConfirmedServiceError(ConfirmedServiceError.InitiateError, ServiceError.Service, (byte)Service.Unsupported)); return; } // Get type. SetRequestType type = (SetRequestType)data.GetUInt8(); // Get invoke ID and priority. byte invoke = data.GetUInt8(); settings.UpdateInvokeId(invoke); // SetRequest normal or Set Request With First Data Block GXDLMSLNParameters p = new GXDLMSLNParameters(null, settings, invoke, Command.SetResponse, (byte)type, null, null, 0); if (xml != null) { xml.AppendStartTag(Command.SetRequest); xml.AppendStartTag(Command.SetRequest, type); //InvokeIdAndPriority xml.AppendLine(TranslatorTags.InvokeId, "Value", xml.IntegerToHex(invoke, 2)); } switch (type) { case SetRequestType.Normal: case SetRequestType.FirstDataBlock: HandleSetRequestNormal(settings, server, data, (byte)type, p, replyData, xml); break; case SetRequestType.WithDataBlock: HanleSetRequestWithDataBlock(settings, server, data, p, replyData, xml); break; case SetRequestType.WithList: HanleSetRequestWithList(settings, invoke, server, data, p, replyData, xml); break; default: System.Diagnostics.Debug.WriteLine("HandleSetRequest failed. Unknown command."); settings.ResetBlockIndex(); p.status = (byte)ErrorCode.HardwareFault; break; } if (xml != null) { xml.AppendEndTag(Command.SetRequest, type); xml.AppendEndTag(Command.SetRequest); return; } GXDLMS.GetLNPdu(p, replyData); }
/// <summary> /// Sends Event Notification or Information Report Request. /// </summary> /// <param name="time">Send time.</param> /// <param name="list">List of COSEM object and attribute index to report.</param> /// <returns>Report request as byte array.</returns> public byte[][] GenerateReport(DateTime time, List <KeyValuePair <GXDLMSObject, int> > list) { if (list == null || list.Count == 0) { throw new ArgumentNullException("list"); } if (UseLogicalNameReferencing && list.Count != 1) { throw new ArgumentException("Only one object can send with Event Notification request."); } GXByteBuffer buff = new GXByteBuffer(); byte[][] reply; if (UseLogicalNameReferencing) { foreach (KeyValuePair <GXDLMSObject, int> it in list) { buff.SetUInt16((ushort)it.Key.ObjectType); buff.Set(GXCommon.LogicalNameToBytes(it.Key.LogicalName)); buff.SetUInt8((byte)it.Value); AddData(it.Key, it.Value, buff); } GXDLMSLNParameters p = new GXDLMSLNParameters(Settings, 0, Command.EventNotification, 0, null, buff, 0xff); p.time = time; reply = GXDLMS.GetLnMessages(p); } else { GXDLMSSNParameters p = new GXDLMSSNParameters(Settings, Command.InformationReport, list.Count, 0xFF, null, buff); foreach (KeyValuePair <GXDLMSObject, int> it in list) { // Add variable type. buff.SetUInt8(VariableAccessSpecification.VariableName); int sn = it.Key.ShortName; sn += (it.Value - 1) * 8; buff.SetUInt16((UInt16)sn); } GXCommon.SetObjectCount(list.Count, buff); foreach (KeyValuePair <GXDLMSObject, int> it in list) { AddData(it.Key, it.Value, buff); } reply = GXDLMS.GetSnMessages(p); } return(reply); }
/// <summary> /// Generates data notification message(s). /// </summary> /// <param name="time">Date time. Set To Min or Max if not added</param> /// <param name="data">Notification body.</param> /// <returns>Generated data notification message(s).</returns> public byte[][] GenerateDataNotificationMessages(DateTime time, GXByteBuffer data) { byte[][] reply; if (UseLogicalNameReferencing) { GXDLMSLNParameters p = new GXDLMSLNParameters(Settings, 0, Command.DataNotification, 0, null, data, 0xff); p.time = time; p.time.Skip |= DateTimeSkips.Ms; reply = GXDLMS.GetLnMessages(p); } else { GXDLMSSNParameters p = new GXDLMSSNParameters(Settings, Command.DataNotification, 1, 0, data, null); reply = GXDLMS.GetSnMessages(p); } return(reply); }
/// <summary> /// Generates data notification message(s). /// </summary> /// <param name="time">Date time. Set To Min or Max if not added</param> /// <param name="data">Notification body.</param> /// <returns>Generated data notification message(s).</returns> public byte[][] GenerateDataNotificationMessages(DateTime time, byte[] data) { byte[][] reply; if (UseLogicalNameReferencing) { GXDLMSLNParameters p = new GXDLMSLNParameters(Settings, 0, Command.DataNotification, 0, null, new GXByteBuffer(data), 0xff); p.time = time; reply = GXDLMS.GetLnMessages(p); } else { GXDLMSSNParameters p = new GXDLMSSNParameters(Settings, Command.DataNotification, 1, 0, new GXByteBuffer(data), null); reply = GXDLMS.GetSnMessages(p); } if ((Settings.ProposedConformance & Conformance.GeneralBlockTransfer) == 0 && reply.Length != 1) { throw new ArgumentException("Data is not fit to one PDU. Use general block transfer."); } return(reply); }
/// <summary> /// Sends Event Notification Request. /// </summary> /// <param name="time">Send time.</param> /// <param name="item">COSEM object and attribute index to report.</param> /// <returns>Report request as byte array.</returns> public byte[][] GenerateEventNotification(DateTime time, KeyValuePair <GXDLMSObject, int> item) { GXByteBuffer buff = new GXByteBuffer(); byte[][] reply; if (UseLogicalNameReferencing) { buff.SetUInt16((ushort)item.Key.ObjectType); buff.Set(GXCommon.LogicalNameToBytes(item.Key.LogicalName)); buff.SetUInt8((byte)item.Value); AddData(item.Key, item.Value, buff); GXDLMSLNParameters p = new GXDLMSLNParameters(null, Settings, 0, Command.EventNotification, 0, null, buff, 0xff, Command.None); p.time = time; reply = GXDLMS.GetLnMessages(p); } else { throw new Exception("Use GenerateInformationReport when Short Name referencing is used."); } return(reply); }
/// <summary> /// Handle get request with list command. /// </summary> /// <param name="data">Received data.</param> private static void GetRequestWithList(GXDLMSSettings settings, GXDLMSServer server, GXByteBuffer data, GXByteBuffer replyData, GXDLMSTranslatorStructure xml) { ValueEventArgs e; GXByteBuffer bb = new GXByteBuffer(); int pos; int cnt = GXCommon.GetObjectCount(data); GXCommon.SetObjectCount(cnt, bb); List<ValueEventArgs> list = new List<ValueEventArgs>(); if (xml != null) { xml.AppendStartTag(TranslatorTags.AttributeDescriptorList, "Qty", xml.IntegerToHex(cnt, 2)); } try { for (pos = 0; pos != cnt; ++pos) { ObjectType ci = (ObjectType)data.GetUInt16(); byte[] ln = new byte[6]; data.Get(ln); short attributeIndex = data.GetUInt8(); // AccessSelection int selection = data.GetUInt8(); int selector = 0; object parameters = null; if (selection != 0) { selector = data.GetUInt8(); GXDataInfo info = new GXDataInfo(); parameters = GXCommon.GetData(settings, data, info); } if (xml != null) { xml.AppendStartTag(TranslatorTags.AttributeDescriptorWithSelection); xml.AppendStartTag(TranslatorTags.AttributeDescriptor); xml.AppendLine(TranslatorTags.ClassId, "Value", xml.IntegerToHex((int)ci, 4)); xml.AppendLine(TranslatorTags.InstanceId, "Value", GXCommon.ToHex(ln, false)); xml.AppendLine(TranslatorTags.AttributeId, "Value", xml.IntegerToHex(attributeIndex, 2)); xml.AppendEndTag(TranslatorTags.AttributeDescriptor); xml.AppendEndTag(TranslatorTags.AttributeDescriptorWithSelection); } else { GXDLMSObject obj = settings.Objects.FindByLN(ci, GXDLMSObject.ToLogicalName(ln)); if (obj == null) { obj = server.NotifyFindObject(ci, 0, GXDLMSObject.ToLogicalName(ln)); } if (obj == null) { // "Access Error : Device reports a undefined object." e = new ValueEventArgs(settings, obj, attributeIndex, 0, 0); e.Error = ErrorCode.UndefinedObject; list.Add(e); } else { if (obj.GetAccess(attributeIndex) == AccessMode.NoAccess) { //Read Write denied. ValueEventArgs arg = new ValueEventArgs(settings, obj, attributeIndex, 0, null); arg.Error = ErrorCode.ReadWriteDenied; list.Add(arg); } else { ValueEventArgs arg = new ValueEventArgs(settings, obj, attributeIndex, selector, parameters); list.Add(arg); } } } } } catch (Exception ex) { if (xml == null) { throw ex; } } if (xml != null) { xml.AppendEndTag(TranslatorTags.AttributeDescriptorList); return; } server.NotifyRead(list.ToArray()); object value; pos = 0; foreach (ValueEventArgs it in list) { try { if (it.Handled) { value = it.Value; } else { value = (it.Target as IGXDLMSBase).GetValue(settings, it); } bb.SetUInt8(it.Error); GXDLMS.AppendData(settings, it.Target, it.Index, bb, value); } catch (Exception) { bb.SetUInt8((byte)ErrorCode.HardwareFault); } if (settings.Index != settings.Count) { server.transaction = new GXDLMSLongTransaction(list.ToArray(), Command.GetRequest, null); } ++pos; } GXDLMSLNParameters p = new GXDLMSLNParameters(settings, Command.GetResponse, 3, null, bb, 0xFF); GXDLMS.GetLNPdu(p, replyData); }
/// <summary> /// Load XML commands from the file. /// </summary> /// <param name="fileName"></param> /// <returns></returns> public byte[][] PduToMessages(GXDLMSXmlPdu pdu) { List <byte[]> messages = new List <byte[]>(); if (pdu.Command == Command.Snrm) { messages.Add(pdu.Data); } else if (pdu.Command == Command.Ua) { messages.Add(pdu.Data); } else if (pdu.Command == Command.DisconnectRequest) { messages.Add(GXDLMS.GetHdlcFrame(Settings, (byte)Command.DisconnectRequest, new GXByteBuffer(pdu.Data))); } else { GXByteBuffer reply; if (Settings.InterfaceType == InterfaceType.WRAPPER) { if (Ciphering.Security != (byte)Security.None) { GXDLMSLNParameters p = new GXDLMSLNParameters(this, Settings, 0, pdu.Command, 0x0, null, null, 0xff, Command.None); reply = new GXByteBuffer(GXDLMS.Cipher0(p, pdu.Data)); } else { reply = new GXByteBuffer(pdu.Data); } } else { if (Ciphering.Security != (byte)Security.None) { GXDLMSLNParameters p = new GXDLMSLNParameters(this, Settings, 0, pdu.Command, 0x0, null, null, 0xff, Command.None); byte[] tmp = GXDLMS.Cipher0(p, pdu.Data); reply = new GXByteBuffer((UInt16)(3 + tmp.Length)); reply.Set(GXCommon.LLCSendBytes); reply.Set(tmp); } else { reply = new GXByteBuffer((UInt16)(3 + pdu.Data.Length)); reply.Set(GXCommon.LLCSendBytes); reply.Set(pdu.Data); } } byte frame = 0; while (reply.Position != reply.Size) { if (Settings.InterfaceType == Enums.InterfaceType.WRAPPER) { messages.Add(GXDLMS.GetWrapperFrame(Settings, pdu.Command, reply)); } else if (Settings.InterfaceType == Enums.InterfaceType.HDLC) { if (pdu.Command == Command.Aarq) { frame = 0x10; } else if (pdu.Command == Command.Aare) { frame = 0x30; } else if (pdu.Command == Command.EventNotification) { frame = 0x13; } messages.Add(GXDLMS.GetHdlcFrame(Settings, frame, reply)); if (reply.Position != reply.Size) { frame = Settings.NextSend(false); } } else if (Settings.InterfaceType == Enums.InterfaceType.PDU) { messages.Add(reply.Array()); break; } else { throw new ArgumentOutOfRangeException("InterfaceType"); } } } return(messages.ToArray()); }
private static void HanleSetRequestWithDataBlock(GXDLMSSettings settings, GXDLMSServer server, GXByteBuffer data, GXDLMSLNParameters p, GXByteBuffer replyData, GXDLMSTranslatorStructure xml) { GXDataInfo reply = new GXDataInfo(); reply.xml = xml; byte lastBlock = data.GetUInt8(); p.multipleBlocks = lastBlock == 0; UInt32 blockNumber = data.GetUInt32(); if (xml == null && blockNumber != settings.BlockIndex) { Debug.WriteLine("HanleSetRequestWithDataBlock failed. Invalid block number. " + settings.BlockIndex + "/" + blockNumber); p.status = (byte)ErrorCode.DataBlockNumberInvalid; } else { settings.IncreaseBlockIndex(); int size = GXCommon.GetObjectCount(data); int realSize = data.Size - data.Position; if (size != realSize) { Debug.WriteLine("HanleSetRequestWithDataBlock failed. Invalid block size."); p.status = (byte)ErrorCode.DataBlockUnavailable; } if (xml != null) { xml.AppendStartTag(TranslatorTags.DataBlock); xml.AppendLine(TranslatorTags.LastBlock, "Value", xml.IntegerToHex(lastBlock, 2)); xml.AppendLine(TranslatorTags.BlockNumber, "Value", xml.IntegerToHex(blockNumber, 8)); xml.AppendLine(TranslatorTags.RawData, "Value", data.RemainingHexString(false)); xml.AppendEndTag(TranslatorTags.DataBlock); return; } server.transaction.data.Set(data); //If all data is received. if (!p.multipleBlocks) { try { object value = GXCommon.GetData(settings, server.transaction.data, reply); if (value is byte[]) { DataType dt = (server.transaction.targets[0].Target as IGXDLMSBase).GetDataType(server.transaction.targets[0].Index); if (dt != DataType.None && dt != DataType.OctetString) { value = GXDLMSClient.ChangeType((byte[])value, dt, settings.UseUtc2NormalTime); } } server.transaction.targets[0].Value = value; server.NotifyWrite(server.transaction.targets); if (!server.transaction.targets[0].Handled && !p.multipleBlocks) { (server.transaction.targets[0].Target as IGXDLMSBase).SetValue(settings, server.transaction.targets[0]); server.NotifyPostWrite(server.transaction.targets); } } catch (Exception) { p.status = (byte)ErrorCode.HardwareFault; } finally { server.transaction = null; } settings.ResetBlockIndex(); } } p.multipleBlocks = true; }
private static void HanleSetRequestWithDataBlock(GXDLMSSettings settings, GXDLMSServer server, GXByteBuffer data, GXDLMSLNParameters p, GXByteBuffer replyData, GXDLMSTranslatorStructure xml) { GXDataInfo reply = new GXDataInfo(); p.multipleBlocks = data.GetUInt8() == 0; UInt32 blockNumber = data.GetUInt32(); if (blockNumber != settings.BlockIndex) { Debug.WriteLine("HanleSetRequestWithDataBlock failed. Invalid block number. " + settings.BlockIndex + "/" + blockNumber); p.status = (byte)ErrorCode.DataBlockNumberInvalid; } else { int size = GXCommon.GetObjectCount(data); int realSize = data.Size - data.Position; if (size != realSize) { Debug.WriteLine("HanleSetRequestWithDataBlock failed. Invalid block size."); p.status = (byte)ErrorCode.DataBlockUnavailable; } server.transaction.data.Set(data); //If all data is received. if (!p.multipleBlocks) { try { object value = GXCommon.GetData(settings, server.transaction.data, reply); if (value is byte[]) { DataType dt = (server.transaction.targets[0].Target as IGXDLMSBase).GetDataType(server.transaction.targets[0].Index); if (dt != DataType.None && dt != DataType.OctetString) { value = GXDLMSClient.ChangeType((byte[])value, dt); } } server.transaction.targets[0].Value = value; server.NotifyWrite(server.transaction.targets); if (!server.transaction.targets[0].Handled && !p.multipleBlocks) { (server.transaction.targets[0].Target as IGXDLMSBase).SetValue(settings, server.transaction.targets[0]); server.NotifyPostWrite(server.transaction.targets); } } catch (Exception) { p.status = (byte)ErrorCode.HardwareFault; } finally { server.transaction = null; } settings.ResetBlockIndex(); } } p.multipleBlocks = true; }
/// <summary> /// Handle get request normal command. /// </summary> /// <param name="data">Received data.</param> private static void GetRequestNormal(GXDLMSSettings settings, byte invokeID, GXDLMSServer server, GXByteBuffer data, GXByteBuffer replyData, GXDLMSTranslatorStructure xml) { ValueEventArgs e = null; GXByteBuffer bb = new GXByteBuffer(); // Get type. ErrorCode status = ErrorCode.Ok; settings.Count = settings.Index = 0; settings.ResetBlockIndex(); // CI ObjectType ci = (ObjectType)data.GetUInt16(); byte[] ln = new byte[6]; data.Get(ln); // Attribute Id byte attributeIndex = data.GetUInt8(); // AccessSelection byte selection = data.GetUInt8(); byte selector = 0; object parameters = null; GXDataInfo info = new GXDataInfo(); if (selection != 0) { selector = data.GetUInt8(); } if (xml != null) { AppendAttributeDescriptor(xml, (int)ci, ln, attributeIndex); if (selection != 0) { info.xml = xml; xml.AppendStartTag(TranslatorTags.AccessSelection); xml.AppendLine(TranslatorTags.AccessSelector, "Value", xml.IntegerToHex(selector, 2)); xml.AppendStartTag(TranslatorTags.AccessParameters); GXCommon.GetData(settings, data, info); xml.AppendEndTag(TranslatorTags.AccessParameters); xml.AppendEndTag(TranslatorTags.AccessSelection); } return; } if (selection != 0) { parameters = GXCommon.GetData(settings, data, info); } GXDLMSObject obj = settings.Objects.FindByLN(ci, GXCommon.ToLogicalName(ln)); if (obj == null) { obj = server.NotifyFindObject(ci, 0, GXCommon.ToLogicalName(ln)); } e = new ValueEventArgs(server, obj, attributeIndex, selector, parameters); e.InvokeId = invokeID; if (obj == null) { // "Access Error : Device reports a undefined object." status = ErrorCode.UndefinedObject; } else { if (server.NotifyGetAttributeAccess(e) == AccessMode.NoAccess) { //Read Write denied. status = ErrorCode.ReadWriteDenied; } else { if (e.Target is GXDLMSProfileGeneric && attributeIndex == 2) { e.RowToPdu = GXDLMS.RowsToPdu(settings, (GXDLMSProfileGeneric)e.Target); } object value; server.NotifyRead(new ValueEventArgs[] { e }); if (e.Handled) { value = e.Value; } else { settings.Count = e.RowEndIndex - e.RowBeginIndex; value = (obj as IGXDLMSBase).GetValue(settings, e); } if (e.ByteArray) { bb.Set((byte[])value); } else { GXDLMS.AppendData(settings, obj, attributeIndex, bb, value); } server.NotifyPostRead(new ValueEventArgs[] { e }); status = e.Error; } } GXDLMSLNParameters p = new GXDLMSLNParameters(null, settings, e.InvokeId, Command.GetResponse, 1, null, bb, (byte)status); GXDLMS.GetLNPdu(p, replyData); if (settings.Count != settings.Index || bb.Size != bb.Position) { server.transaction = new GXDLMSLongTransaction(new ValueEventArgs[] { e }, Command.GetRequest, bb); } }
/// <summary> /// Handle get request with list command. /// </summary> /// <param name="data">Received data.</param> private static void GetRequestWithList(GXDLMSSettings settings, byte invokeID, GXDLMSServer server, GXByteBuffer data, GXByteBuffer replyData, GXDLMSTranslatorStructure xml) { ValueEventArgs e; GXByteBuffer bb = new GXByteBuffer(); int pos; int cnt = GXCommon.GetObjectCount(data); GXCommon.SetObjectCount(cnt, bb); List <ValueEventArgs> list = new List <ValueEventArgs>(); if (xml != null) { xml.AppendStartTag(TranslatorTags.AttributeDescriptorList, "Qty", xml.IntegerToHex(cnt, 2)); } try { for (pos = 0; pos != cnt; ++pos) { ObjectType ci = (ObjectType)data.GetUInt16(); byte[] ln = new byte[6]; data.Get(ln); short attributeIndex = data.GetUInt8(); // AccessSelection int selection = data.GetUInt8(); int selector = 0; object parameters = null; if (selection != 0) { selector = data.GetUInt8(); GXDataInfo info = new GXDataInfo(); parameters = GXCommon.GetData(settings, data, info); } if (xml != null) { xml.AppendStartTag(TranslatorTags.AttributeDescriptorWithSelection); xml.AppendStartTag(TranslatorTags.AttributeDescriptor); xml.AppendComment(ci.ToString()); xml.AppendLine(TranslatorTags.ClassId, "Value", xml.IntegerToHex((int)ci, 4)); xml.AppendComment(GXCommon.ToLogicalName(ln)); xml.AppendLine(TranslatorTags.InstanceId, "Value", GXCommon.ToHex(ln, false)); xml.AppendLine(TranslatorTags.AttributeId, "Value", xml.IntegerToHex(attributeIndex, 2)); xml.AppendEndTag(TranslatorTags.AttributeDescriptor); xml.AppendEndTag(TranslatorTags.AttributeDescriptorWithSelection); } else { GXDLMSObject obj = settings.Objects.FindByLN(ci, GXCommon.ToLogicalName(ln)); if (obj == null) { obj = server.NotifyFindObject(ci, 0, GXCommon.ToLogicalName(ln)); } if (obj == null) { // "Access Error : Device reports a undefined object." e = new ValueEventArgs(server, obj, attributeIndex, 0, 0); e.Error = ErrorCode.UndefinedObject; list.Add(e); } else { ValueEventArgs arg = new ValueEventArgs(server, obj, attributeIndex, selector, parameters); arg.InvokeId = invokeID; if (server.NotifyGetAttributeAccess(arg) == AccessMode.NoAccess) { //Read Write denied. arg.Error = ErrorCode.ReadWriteDenied; list.Add(arg); } else { list.Add(arg); } } } } } catch (Exception ex) { if (xml == null) { throw ex; } } if (xml != null) { xml.AppendEndTag(TranslatorTags.AttributeDescriptorList); return; } server.NotifyRead(list.ToArray()); object value; pos = 0; foreach (ValueEventArgs it in list) { try { if (it.Handled) { value = it.Value; } else { value = (it.Target as IGXDLMSBase).GetValue(settings, it); } bb.SetUInt8(it.Error); if (it.ByteArray) { bb.Set((byte[])value); } else { GXDLMS.AppendData(settings, it.Target, it.Index, bb, value); } invokeID = (byte)it.InvokeId; } catch (Exception) { bb.SetUInt8((byte)ErrorCode.HardwareFault); } if (settings.Index != settings.Count) { server.transaction = new GXDLMSLongTransaction(list.ToArray(), Command.GetRequest, null); } ++pos; } server.NotifyPostRead(list.ToArray()); GXDLMSLNParameters p = new GXDLMSLNParameters(null, settings, invokeID, Command.GetResponse, 3, null, bb, 0xFF); GXDLMS.GetLNPdu(p, replyData); }
/// <summary> /// Check is all data fit to one data block. /// </summary> /// <param name="p">LN parameters.</param> /// <param name="reply">Generated reply.</param> private static void MultipleBlocks(GXDLMSLNParameters p, GXByteBuffer reply, bool ciphering) { //Check is all data fit to one message if data is given. int len = p.data.Size - p.data.Position; if (p.attributeDescriptor != null) { len += p.attributeDescriptor.Size; } if (ciphering) { len += CipheringHeaderSize; } if (!p.multipleBlocks) { //Add command type and invoke and priority. p.multipleBlocks = 2 + reply.Size + len > p.settings.MaxPduSize; } if (p.multipleBlocks) { //Add command type and invoke and priority. p.lastBlock = !(2 + reply.Size + len > p.settings.MaxPduSize); } if (p.lastBlock) { //Add command type and invoke and priority. p.lastBlock = !(2 + reply.Size + len > p.settings.MaxPduSize); } }
///<summary> ///Generates an acknowledgment message, with which the server is informed to send next packets. ///</summary> ///<param name="type"> /// Frame type. /// </param> ///<returns> ///Acknowledgment message as byte array. ///</returns> internal static byte[] ReceiverReady(GXDLMSSettings settings, RequestTypes type) { if (type == RequestTypes.None) { throw new ArgumentException("Invalid receiverReady RequestTypes parameter."); } // Get next frame. if ((type & RequestTypes.Frame) != 0) { byte id = settings.ReceiverReady(); return GetHdlcFrame(settings, id, null); } Command cmd; if (settings.UseLogicalNameReferencing) { if (settings.IsServer) { cmd = Command.GetResponse; } else { cmd = Command.GetRequest; } } else { if (settings.IsServer) { cmd = Command.ReadResponse; } else { cmd = Command.ReadRequest; } } // Get next block. GXByteBuffer bb = new GXByteBuffer(6); if (settings.UseLogicalNameReferencing) { bb.SetUInt32(settings.BlockIndex); } else { bb.SetUInt16((UInt16)settings.BlockIndex); } settings.IncreaseBlockIndex(); byte[][] reply; if (settings.UseLogicalNameReferencing) { GXDLMSLNParameters p = new GXDLMSLNParameters(settings, cmd, (byte)GetCommandType.NextDataBlock, bb, null, 0xff); reply = GXDLMS.GetLnMessages(p); } else { GXDLMSSNParameters p = new GXDLMSSNParameters(settings, cmd, 1, (byte)VariableAccessSpecification.BlockNumberAccess, bb, null); reply = GXDLMS.GetSnMessages(p); } return reply[0]; }
/// <summary> /// Handle get request next data block command. /// </summary> /// <param name="data">Received data.</param> private static void GetRequestNextDataBlock(GXDLMSSettings settings, GXDLMSServer server, GXByteBuffer data, GXByteBuffer replyData, GXDLMSTranslatorStructure xml) { GXByteBuffer bb = new GXByteBuffer(); UInt32 index; // Get block index. index = data.GetUInt32(); if (xml != null) { xml.AppendLine(TranslatorTags.BlockNumber, null, xml.IntegerToHex(index, 8)); return; } if (index != settings.BlockIndex) { Debug.WriteLine("handleGetRequest failed. Invalid block number. " + settings.BlockIndex + "/" + index); GXDLMS.GetLNPdu(new GXDLMSLNParameters(settings, Command.GetResponse, 2, null, bb, (byte)ErrorCode.DataBlockNumberInvalid), replyData); } else { settings.IncreaseBlockIndex(); GXDLMSLNParameters p = new GXDLMSLNParameters(settings, Command.GetResponse, 2, null, bb, (byte)ErrorCode.Ok); //If transaction is not in progress. if (server.transaction == null) { p.status = (byte)ErrorCode.NoLongGetOrReadInProgress; } else { bb.Set(server.transaction.data); bool moreData = settings.Index != settings.Count; if (moreData) { //If there is multiple blocks on the buffer. //This might happen when Max PDU size is very small. if (bb.Size < settings.MaxPduSize) { foreach (ValueEventArgs arg in server.transaction.targets) { object value; if (arg.Handled) { server.NotifyRead(new ValueEventArgs[] { arg }); value = arg.Value; } else { value = (arg.Target as IGXDLMSBase).GetValue(settings, arg); } //Add data. GXDLMS.AppendData(settings, arg.Target, arg.Index, bb, value); } moreData = settings.Index != settings.Count; } } p.multipleBlocks = true; GXDLMS.GetLNPdu(p, replyData); if (moreData || bb.Size - bb.Position != 0) { server.transaction.data = bb; } else { server.transaction = null; } } } }
///<summary> /// Handle action request. ///</summary> ///<param name="Reply"> /// Received data from the client. ///</param> ///<returns> ///Reply. ///</returns> public static void HandleMethodRequest(GXDLMSSettings settings, GXDLMSServer server, GXByteBuffer data, GXDLMSConnectionEventArgs connectionInfo, GXByteBuffer replyData, GXDLMSTranslatorStructure xml) { ErrorCode error = ErrorCode.Ok; GXByteBuffer bb = new GXByteBuffer(); // Get type. ActionRequestType type = (ActionRequestType)data.GetUInt8(); // Get invoke ID and priority. byte invokeId = data.GetUInt8(); // CI ObjectType ci = (ObjectType)data.GetUInt16(); byte[] ln = new byte[6]; data.Get(ln); // Attribute Id byte id = data.GetUInt8(); // Get parameters. object parameters = null; byte selection = data.GetUInt8(); if (xml != null) { xml.AppendStartTag(Command.MethodRequest); if (type == ActionRequestType.Normal) { xml.AppendStartTag(Command.MethodRequest, ActionRequestType.Normal); xml.AppendLine(TranslatorTags.InvokeId, "Value", xml.IntegerToHex(invokeId, 2)); AppendMethodDescriptor(xml, (int)ci, ln, id); if (selection != 0) { //MethodInvocationParameters xml.AppendStartTag(TranslatorTags.MethodInvocationParameters); GXDataInfo di = new GXDataInfo(); di.xml = xml; GXCommon.GetData(settings, data, di); xml.AppendEndTag(TranslatorTags.MethodInvocationParameters); } xml.AppendEndTag(Command.MethodRequest, ActionRequestType.Normal); } xml.AppendEndTag(Command.MethodRequest); return; } if (selection != 0) { GXDataInfo info = new GXDataInfo(); parameters = GXCommon.GetData(settings, data, info); } GXDLMSObject obj = settings.Objects.FindByLN(ci, GXDLMSObject.ToLogicalName(ln)); if (!settings.Connected && (ci != ObjectType.AssociationLogicalName || id != 1)) { replyData.Set(GXDLMSServer.GenerateConfirmedServiceError(ConfirmedServiceError.InitiateError, ServiceError.Service, (byte)Service.Unsupported)); return; } if (obj == null) { obj = server.NotifyFindObject(ci, 0, GXDLMSObject.ToLogicalName(ln)); } if (obj == null) { // Device reports a undefined object. error = ErrorCode.UndefinedObject; } else { if (obj.GetMethodAccess(id) == MethodAccessMode.NoAccess) { error = ErrorCode.ReadWriteDenied; } else { ValueEventArgs e = new ValueEventArgs(settings, obj, id, 0, parameters); server.NotifyAction(new ValueEventArgs[] { e }); byte[] actionReply; if (e.Handled) { actionReply = (byte[])e.Value; } else { actionReply = (obj as IGXDLMSBase).Invoke(settings, e); } //Set default action reply if not given. if (actionReply != null && e.Error == 0) { //Add return parameters bb.SetUInt8(1); //Add parameters error code. bb.SetUInt8(0); GXCommon.SetData(settings, bb, GXCommon.GetValueType(actionReply), actionReply); } else { error = e.Error; //Add return parameters bb.SetUInt8(0); } } } GXDLMSLNParameters p = new GXDLMSLNParameters(settings, Command.MethodResponse, 1, null, bb, (byte)error); GXDLMS.GetLNPdu(p, replyData); //If High level authentication fails. if (!settings.Connected && obj is GXDLMSAssociationLogicalName && id == 1) { server.NotifyInvalidConnection(connectionInfo); } }
///<summary> ///Handle set request. ///</summary> ///<returns> ///Reply to the client. ///</returns> public static void HandleSetRequest(GXDLMSSettings settings, GXDLMSServer server, GXByteBuffer data, GXByteBuffer replyData, GXDLMSTranslatorStructure xml) { //Return error if connection is not established. if (xml == null && !settings.Connected) { replyData.Set(GXDLMSServer.GenerateConfirmedServiceError(ConfirmedServiceError.InitiateError, ServiceError.Service, (byte)Service.Unsupported)); return; } // Get type. SetRequestType type = (SetRequestType)data.GetUInt8(); // Get invoke ID and priority. byte invoke = data.GetUInt8(); // SetRequest normal or Set Request With First Data Block GXDLMSLNParameters p = new GXDLMSLNParameters(settings, Command.SetResponse, (byte)type, null, null, 0); if (xml != null) { xml.AppendStartTag(Command.SetRequest); xml.AppendStartTag(Command.SetRequest, type); //InvokeIdAndPriority xml.AppendLine(TranslatorTags.InvokeId, "Value", xml.IntegerToHex(invoke, 2)); } switch (type) { case SetRequestType.Normal: case SetRequestType.FirstDataBlock: HandleSetRequestNormal(settings, server, data, (byte)type, p, replyData, xml); break; case SetRequestType.WithDataBlock: HanleSetRequestWithDataBlock(settings, server, data, p, replyData, xml); break; default: System.Diagnostics.Debug.WriteLine("HandleSetRequest failed. Unknown command."); settings.ResetBlockIndex(); p.status = (byte)ErrorCode.HardwareFault; break; } if (xml != null) { xml.AppendEndTag(Command.SetRequest, type); xml.AppendEndTag(Command.SetRequest); return; } GXDLMS.GetLNPdu(p, replyData); }
/// <summary> /// Get all Logical name messages. Client uses this to generate messages. /// </summary> /// <param name="p">LN settings.</param> /// <returns>Generated messages.</returns> internal static byte[][] GetLnMessages(GXDLMSLNParameters p) { GXByteBuffer reply = new GXByteBuffer(); List<byte[]> messages = new List<byte[]>(); byte frame = 0; if (p.command == Command.Aarq) { frame = 0x10; } do { GetLNPdu(p, reply); p.lastBlock = true; if (p.attributeDescriptor == null) { ++p.settings.BlockIndex; } if (p.command == Command.Aarq && p.command == Command.GetRequest) { System.Diagnostics.Debug.Assert(!(p.settings.MaxPduSize < reply.Size)); } while (reply.Position != reply.Size) { if (p.settings.InterfaceType == Enums.InterfaceType.WRAPPER) { messages.Add(GXDLMS.GetWrapperFrame(p.settings, reply)); } else if (p.settings.InterfaceType == Enums.InterfaceType.HDLC) { messages.Add(GXDLMS.GetHdlcFrame(p.settings, frame, reply)); frame = 0; } else if (p.settings.InterfaceType == Enums.InterfaceType.PDU) { messages.Add(reply.Array()); frame = 0; break; } else { throw new ArgumentOutOfRangeException("InterfaceType"); } } reply.Clear(); } while (p.data != null && p.data.Position != p.data.Size); return messages.ToArray(); }
/// <summary> /// Get next logical name PDU. /// </summary> /// <param name="p">LN parameters.</param> /// <param name="reply">Generated message.</param> internal static void GetLNPdu(GXDLMSLNParameters p, GXByteBuffer reply) { bool ciphering = p.settings.Cipher != null && p.settings.Cipher.Security != Gurux.DLMS.Enums.Security.None; int len = 0; if (!ciphering && p.settings.InterfaceType == InterfaceType.HDLC) { AddLLCBytes(p.settings, reply); } if (p.command == Command.Aarq) { reply.Set(p.attributeDescriptor); } else { if (p.settings.LnSettings.GeneralBlockTransfer) { reply.SetUInt8((byte)Command.GeneralBlockTransfer); MultipleBlocks(p, reply, ciphering); // Is last block if (!p.lastBlock) { reply.SetUInt8(0); } else { reply.SetUInt8(0x80); } // Set block number sent. reply.SetUInt8(0); // Set block number acknowledged reply.SetUInt8((byte)p.blockIndex); ++p.blockIndex; // Add APU tag. reply.SetUInt8(0); // Add Addl fields reply.SetUInt8(0); } // Add command. reply.SetUInt8((byte)p.command); if (p.command == Command.DataNotification || p.command == Command.AccessRequest || p.command == Command.AccessResponse) { // Add Long-Invoke-Id-And-Priority reply.SetUInt32(GetLongInvokeIDPriority(p.settings)); // Add date time. if (p.time == null || p.time.Value.DateTime == DateTime.MinValue || p.time.Value.DateTime == DateTime.MaxValue || p.time.Value.LocalDateTime == DateTime.MinValue || p.time.Value.LocalDateTime == DateTime.MaxValue) { reply.SetUInt8(DataType.None); } else { // Data is send in octet string. Remove data type. int pos = reply.Size; GXCommon.SetData(p.settings, reply, DataType.OctetString, p.time); reply.Move(pos + 1, pos, reply.Size - pos - 1); } } else { //Get request size can be bigger than PDU size. if (p.command != Command.GetRequest && p.data != null && p.data.Size != 0) { MultipleBlocks(p, reply, ciphering); } //Change Request type if Set request and multiple blocks is needed. if (p.command == Command.SetRequest) { if (p.multipleBlocks) { if (p.requestType == 1) { p.requestType = 2; } else if (p.requestType == 2) { p.requestType = 3; } } } //Change request type If get response and multiple blocks is needed. if (p.command == Command.GetResponse) { if (p.multipleBlocks) { if (p.requestType == 1) { p.requestType = 2; } } } reply.SetUInt8(p.requestType); // Add Invoke Id And Priority. reply.SetUInt8(GetInvokeIDPriority(p.settings)); } //Add attribute descriptor. reply.Set(p.attributeDescriptor); if (p.command != Command.DataNotification && !p.settings.LnSettings.GeneralBlockTransfer) { //If multiple blocks. if (p.multipleBlocks) { // Is last block. if (p.lastBlock) { reply.SetUInt8(1); p.settings.Count = p.settings.Index = 0; } else { reply.SetUInt8(0); } // Block index. reply.SetUInt32(p.blockIndex); ++p.blockIndex; //Add status if reply. if (p.status != 0xFF) { if (p.status != 0 && p.command == Command.GetResponse) { reply.SetUInt8(1); } reply.SetUInt8(p.status); } //Block size. if (p.data != null) { len = p.data.Size - p.data.Position; } else { len = 0; } int totalLength = len + reply.Size; if (ciphering) { totalLength += CipheringHeaderSize; } if (totalLength > p.settings.MaxPduSize) { len = p.settings.MaxPduSize - reply.Size - p.data.Position; if (ciphering) { len -= CipheringHeaderSize; } len -= GXCommon.GetObjectCountSizeInBytes(len); } GXCommon.SetObjectCount(len, reply); reply.Set(p.data, len); } } //Add data that fits to one block. if (len == 0) { //Add status if reply. if (p.status != 0xFF) { if (p.status != 0 && p.command == Command.GetResponse) { reply.SetUInt8(1); } reply.SetUInt8(p.status); } if (p.data != null && p.data.Size != 0) { len = p.data.Size - p.data.Position; //Get request size can be bigger than PDU size. if (p.command != Command.GetRequest && len + reply.Size > p.settings.MaxPduSize) { len = p.settings.MaxPduSize - reply.Size; } reply.Set(p.data, len); } } if (ciphering) { byte[] tmp = p.settings.Cipher.Encrypt((byte)GetGloMessage(p.command), p.settings.Cipher.SystemTitle, reply.Array()); reply.Size = 0; if (p.settings.InterfaceType == InterfaceType.HDLC) { AddLLCBytes(p.settings, reply); } if (p.command == Command.DataNotification) { // Add command. reply.SetUInt8(tmp[0]); // Add system title. GXCommon.SetObjectCount( p.settings.Cipher.SystemTitle.Length, reply); reply.Set(p.settings.Cipher.SystemTitle); // Add data. reply.Set(tmp, 1, tmp.Length - 1); } else { reply.Set(tmp); } } } }
private static void HandleSetRequestNormal(GXDLMSSettings settings, GXDLMSServer server, GXByteBuffer data, byte type, GXDLMSLNParameters p, GXByteBuffer replyData, GXDLMSTranslatorStructure xml) { object value = null; GXDataInfo reply = new GXDataInfo(); // CI ObjectType ci = (ObjectType)data.GetUInt16(); byte[] ln = new byte[6]; data.Get(ln); // Attribute index. byte index = data.GetUInt8(); // Get Access Selection. data.GetUInt8(); if (xml != null) { AppendAttributeDescriptor(xml, (int)ci, ln, index); xml.AppendStartTag(TranslatorTags.Value); GXDataInfo di = new GXDataInfo(); di.xml = xml; value = GXCommon.GetData(settings, data, di); if (!di.Complete) { value = GXCommon.ToHex(data.Data, false, data.Position, data.Size - data.Position); } else if (value is byte[]) { value = GXCommon.ToHex((byte[])value, false); } xml.AppendEndTag(TranslatorTags.Value); return; } if (type == 2) { p.multipleBlocks = data.GetUInt8() == 0; UInt32 blockNumber = data.GetUInt32(); if (blockNumber != settings.BlockIndex) { Debug.WriteLine("HandleSetRequest failed. Invalid block number. " + settings.BlockIndex + "/" + blockNumber); p.status = (byte)ErrorCode.DataBlockNumberInvalid; return; } settings.IncreaseBlockIndex(); int size = GXCommon.GetObjectCount(data); int realSize = data.Size - data.Position; if (size != realSize) { Debug.WriteLine("HandleSetRequest failed. Invalid block size."); p.status = (byte)ErrorCode.DataBlockUnavailable; return; } } if (!p.multipleBlocks) { settings.ResetBlockIndex(); value = GXCommon.GetData(settings, data, reply); } GXDLMSObject obj = settings.Objects.FindByLN(ci, GXDLMSObject.ToLogicalName(ln)); if (obj == null) { obj = server.NotifyFindObject(ci, 0, GXDLMSObject.ToLogicalName(ln)); } // If target is unknown. if (obj == null) { // Device reports a undefined object. p.status = (byte)ErrorCode.UndefinedObject; } else { AccessMode am = obj.GetAccess(index); // If write is denied. if (am != AccessMode.Write && am != AccessMode.ReadWrite) { //Read Write denied. p.status = (byte)ErrorCode.ReadWriteDenied; } else { try { if (value is byte[]) { DataType dt = (obj as IGXDLMSBase).GetDataType(index); if (dt != DataType.None && dt != DataType.OctetString) { value = GXDLMSClient.ChangeType((byte[])value, dt); } } ValueEventArgs e = new ValueEventArgs(settings, obj, index, 0, null); e.Value = value; ValueEventArgs[] list = new ValueEventArgs[] { e }; if (p.multipleBlocks) { server.transaction = new GXDLMSLongTransaction(list, Command.GetRequest, data); } server.NotifyWrite(list); if (e.Error != 0) { p.status = (byte)e.Error; } else if (!e.Handled && !p.multipleBlocks) { (obj as IGXDLMSBase).SetValue(settings, e); } } catch (Exception) { p.status = (byte)ErrorCode.HardwareFault; } } } }
private static void HanleSetRequestWithDataBlock(GXDLMSSettings settings, GXDLMSServer server, GXByteBuffer data, GXDLMSLNParameters p, GXByteBuffer replyData, GXDLMSTranslatorStructure xml) { GXDataInfo reply = new GXDataInfo(); p.multipleBlocks = data.GetUInt8() == 0; UInt32 blockNumber = data.GetUInt32(); if (blockNumber != settings.BlockIndex) { Debug.WriteLine("HanleSetRequestWithDataBlock failed. Invalid block number. " + settings.BlockIndex + "/" + blockNumber); p.status = (byte)ErrorCode.DataBlockNumberInvalid; } else { int size = GXCommon.GetObjectCount(data); int realSize = data.Size - data.Position; if (size != realSize) { Debug.WriteLine("HanleSetRequestWithDataBlock failed. Invalid block size."); p.status = (byte)ErrorCode.DataBlockUnavailable; } server.transaction.data.Set(data); //If all data is received. if (!p.multipleBlocks) { try { object value = GXCommon.GetData(settings, server.transaction.data, reply); if (value is byte[]) { DataType dt = (server.transaction.targets[0].Target as IGXDLMSBase).GetDataType(server.transaction.targets[0].Index); if (dt != DataType.None && dt != DataType.OctetString) { value = GXDLMSClient.ChangeType((byte[])value, dt); } } server.transaction.targets[0].Value = value; server.NotifyWrite(server.transaction.targets); if (!server.transaction.targets[0].Handled && !p.multipleBlocks) { (server.transaction.targets[0].Target as IGXDLMSBase).SetValue(settings, server.transaction.targets[0]); } } catch (Exception) { p.status = (byte)ErrorCode.HardwareFault; } finally { server.transaction = null; } settings.ResetBlockIndex(); } } p.multipleBlocks = true; }
///<summary> /// Handle action request. ///</summary> public static void HandleMethodRequest(GXDLMSSettings settings, GXDLMSServer server, GXByteBuffer data, GXDLMSConnectionEventArgs connectionInfo, GXByteBuffer replyData, GXDLMSTranslatorStructure xml) { ErrorCode error = ErrorCode.Ok; GXByteBuffer bb = new GXByteBuffer(); // Get type. ActionRequestType type = (ActionRequestType)data.GetUInt8(); // Get invoke ID and priority. byte invokeId = data.GetUInt8(); settings.UpdateInvokeId(invokeId); // CI ObjectType ci = (ObjectType)data.GetUInt16(); byte[] ln = new byte[6]; data.Get(ln); // Attribute Id byte id = data.GetUInt8(); // Get parameters. object parameters = null; byte selection = data.GetUInt8(); if (xml != null) { xml.AppendStartTag(Command.MethodRequest); if (type == ActionRequestType.Normal) { xml.AppendStartTag(Command.MethodRequest, ActionRequestType.Normal); xml.AppendLine(TranslatorTags.InvokeId, "Value", xml.IntegerToHex(invokeId, 2)); AppendMethodDescriptor(xml, (int)ci, ln, id); if (selection != 0) { //MethodInvocationParameters xml.AppendStartTag(TranslatorTags.MethodInvocationParameters); GXDataInfo di = new GXDataInfo(); di.xml = xml; GXCommon.GetData(settings, data, di); xml.AppendEndTag(TranslatorTags.MethodInvocationParameters); } xml.AppendEndTag(Command.MethodRequest, ActionRequestType.Normal); } xml.AppendEndTag(Command.MethodRequest); return; } if (selection != 0) { GXDataInfo info = new GXDataInfo(); parameters = GXCommon.GetData(settings, data, info); } GXDLMSObject obj = settings.Objects.FindByLN(ci, GXCommon.ToLogicalName(ln)); if (settings.Connected == ConnectionState.None && !settings.CanAccess() && (ci != ObjectType.AssociationLogicalName || id != 1)) { replyData.Set(GXDLMSServer.GenerateConfirmedServiceError(ConfirmedServiceError.InitiateError, ServiceError.Service, (byte)Service.Unsupported)); return; } if (obj == null) { obj = server.NotifyFindObject(ci, 0, GXCommon.ToLogicalName(ln)); } if (obj == null) { // Device reports a undefined object. error = ErrorCode.UndefinedObject; } else { ValueEventArgs e = new ValueEventArgs(server, obj, id, 0, parameters); e.InvokeId = invokeId; if (server.NotifyGetMethodAccess(e) == MethodAccessMode.NoAccess) { error = ErrorCode.ReadWriteDenied; } else { server.NotifyAction(new ValueEventArgs[] { e }); byte[] actionReply; if (e.Handled) { actionReply = (byte[])e.Value; } else { actionReply = (obj as IGXDLMSBase).Invoke(settings, e); server.NotifyPostAction(new ValueEventArgs[] { e }); } //Set default action reply if not given. if (actionReply != null && e.Error == 0) { //Add return parameters bb.SetUInt8(1); //Add parameters error code. bb.SetUInt8(0); GXCommon.SetData(settings, bb, GXDLMSConverter.GetDLMSDataType(actionReply), actionReply); } else { error = e.Error; //Add return parameters bb.SetUInt8(0); } } invokeId = (byte)e.InvokeId; } GXDLMSLNParameters p = new GXDLMSLNParameters(null, settings, invokeId, Command.MethodResponse, 1, null, bb, (byte)error); GXDLMS.GetLNPdu(p, replyData); //If High level authentication fails. if (obj is GXDLMSAssociationLogicalName && id == 1) { if ((obj as GXDLMSAssociationLogicalName).AssociationStatus == Objects.Enums.AssociationStatus.Associated) { server.NotifyConnected(connectionInfo); settings.Connected |= ConnectionState.Dlms; } else { server.NotifyInvalidConnection(connectionInfo); settings.Connected &= ~ConnectionState.Dlms; } } }
/// <summary> /// Generates data notification message(s). /// </summary> /// <param name="time">Date time. Set To Min or Max if not added</param> /// <param name="data">Notification body.</param> /// <returns>Generated data notification message(s).</returns> public byte[][] GenerateDataNotificationMessages(DateTime time, byte[] data) { byte[][] reply; if (UseLogicalNameReferencing) { GXDLMSLNParameters p = new GXDLMSLNParameters(Settings, Command.DataNotification, 0, null, new GXByteBuffer(data), 0xff); p.time = time; reply = GXDLMS.GetLnMessages(p); } else { GXDLMSSNParameters p = new GXDLMSSNParameters(Settings, Command.DataNotification, 1, 0, new GXByteBuffer(data), null); reply = GXDLMS.GetSnMessages(p); } if (!GeneralBlockTransfer && reply.Length != 1) { throw new ArgumentException("Data is not fit to one PDU. Use general block transfer."); } return reply; }
/// <summary> /// Handle get request next data block command. /// </summary> /// <param name="data">Received data.</param> internal static void GetRequestNextDataBlock(GXDLMSSettings settings, byte invokeID, GXDLMSServer server, GXByteBuffer data, GXByteBuffer replyData, GXDLMSTranslatorStructure xml, bool streaming) { GXByteBuffer bb = new GXByteBuffer(); if (!streaming) { UInt32 index; // Get block index. index = data.GetUInt32(); if (xml != null) { xml.AppendLine(TranslatorTags.BlockNumber, null, xml.IntegerToHex(index, 8)); return; } if (index != settings.BlockIndex) { Debug.WriteLine("handleGetRequest failed. Invalid block number. " + settings.BlockIndex + "/" + index); GXDLMS.GetLNPdu(new GXDLMSLNParameters(null, settings, 0, Command.GetResponse, 2, null, bb, (byte)ErrorCode.DataBlockNumberInvalid), replyData); return; } } settings.IncreaseBlockIndex(); GXDLMSLNParameters p = new GXDLMSLNParameters(null, settings, invokeID, streaming ? Command.GeneralBlockTransfer : Command.GetResponse, 2, null, bb, (byte)ErrorCode.Ok); p.Streaming = streaming; p.WindowSize = settings.WindowSize; //If transaction is not in progress. if (server.transaction == null) { p.status = (byte)ErrorCode.NoLongGetOrReadInProgress; } else { bb.Set(server.transaction.data); bool moreData = settings.Index != settings.Count; if (moreData) { //If there is multiple blocks on the buffer. //This might happen when Max PDU size is very small. if (bb.Size < settings.MaxPduSize) { foreach (ValueEventArgs arg in server.transaction.targets) { object value; server.NotifyRead(new ValueEventArgs[] { arg }); if (arg.Handled) { value = arg.Value; } else { value = (arg.Target as IGXDLMSBase).GetValue(settings, arg); } //Add data. if (arg.ByteArray) { bb.Set((byte[])value); } else { GXDLMS.AppendData(settings, arg.Target, arg.Index, bb, value); } } moreData = settings.Index != settings.Count; } } p.multipleBlocks = true; GXDLMS.GetLNPdu(p, replyData); if (moreData || bb.Size - bb.Position != 0) { server.transaction.data = bb; } else { server.transaction = null; settings.ResetBlockIndex(); } } }
/// <summary> /// Generates data notification message(s). /// </summary> /// <param name="time">Date time. Set To Min or Max if not added</param> /// <param name="data">Notification body.</param> /// <returns>Generated data notification message(s).</returns> public byte[][] GenerateDataNotificationMessages(DateTime time, GXByteBuffer data) { byte[][] reply; if (UseLogicalNameReferencing) { GXDLMSLNParameters p = new GXDLMSLNParameters(Settings, Command.DataNotification, 0, null, data, 0xff); p.time = time; p.time.Skip |= DateTimeSkips.Ms; reply = GXDLMS.GetLnMessages(p); } else { GXDLMSSNParameters p = new GXDLMSSNParameters(Settings, Command.DataNotification, 1, 0, data, null); reply = GXDLMS.GetSnMessages(p); } return reply; }
private static void HandleSetRequestNormal(GXDLMSSettings settings, GXDLMSServer server, GXByteBuffer data, byte type, GXDLMSLNParameters p, GXByteBuffer replyData, GXDLMSTranslatorStructure xml) { object value = null; GXDataInfo reply = new GXDataInfo(); // CI ObjectType ci = (ObjectType)data.GetUInt16(); byte[] ln = new byte[6]; data.Get(ln); // Attribute index. byte index = data.GetUInt8(); // Get Access Selection. data.GetUInt8(); if (type == 2) { byte lastBlock = data.GetUInt8(); p.multipleBlocks = lastBlock == 0; UInt32 blockNumber = data.GetUInt32(); if (blockNumber != settings.BlockIndex) { Debug.WriteLine("HandleSetRequest failed. Invalid block number. " + settings.BlockIndex + "/" + blockNumber); p.status = (byte)ErrorCode.DataBlockNumberInvalid; return; } settings.IncreaseBlockIndex(); int size = GXCommon.GetObjectCount(data); int realSize = data.Size - data.Position; if (size != realSize) { Debug.WriteLine("HandleSetRequest failed. Invalid block size."); p.status = (byte)ErrorCode.DataBlockUnavailable; return; } if (xml != null) { AppendAttributeDescriptor(xml, (int)ci, ln, index); xml.AppendStartTag(TranslatorTags.DataBlock); xml.AppendLine(TranslatorTags.LastBlock, "Value", xml.IntegerToHex(lastBlock, 2)); xml.AppendLine(TranslatorTags.BlockNumber, "Value", xml.IntegerToHex(blockNumber, 8)); xml.AppendLine(TranslatorTags.RawData, "Value", data.RemainingHexString(false)); xml.AppendEndTag(TranslatorTags.DataBlock); } return; } if (xml != null) { AppendAttributeDescriptor(xml, (int)ci, ln, index); xml.AppendStartTag(TranslatorTags.Value); GXDataInfo di = new GXDataInfo(); di.xml = xml; value = GXCommon.GetData(settings, data, di); if (!di.Complete) { value = GXCommon.ToHex(data.Data, false, data.Position, data.Size - data.Position); } else if (value is byte[]) { value = GXCommon.ToHex((byte[])value, false); } xml.AppendEndTag(TranslatorTags.Value); return; } if (!p.multipleBlocks) { settings.ResetBlockIndex(); value = GXCommon.GetData(settings, data, reply); } GXDLMSObject obj = settings.Objects.FindByLN(ci, GXCommon.ToLogicalName(ln)); if (obj == null) { obj = server.NotifyFindObject(ci, 0, GXCommon.ToLogicalName(ln)); } // If target is unknown. if (obj == null) { // Device reports a undefined object. p.status = (byte)ErrorCode.UndefinedObject; } else { ValueEventArgs e = new ValueEventArgs(server, obj, index, 0, null); e.InvokeId = p.InvokeId; AccessMode am = server.NotifyGetAttributeAccess(e); // If write is denied. if (am != AccessMode.Write && am != AccessMode.ReadWrite) { //Read Write denied. p.status = (byte)ErrorCode.ReadWriteDenied; } else { try { if (value is byte[]) { DataType dt = (obj as IGXDLMSBase).GetDataType(index); if (dt != DataType.None && dt != DataType.OctetString && dt != DataType.Structure) { value = GXDLMSClient.ChangeType((byte[])value, dt, settings.UseUtc2NormalTime); } } e.Value = value; ValueEventArgs[] list = new ValueEventArgs[] { e }; if (p.multipleBlocks) { server.transaction = new GXDLMSLongTransaction(list, Command.GetRequest, data); } server.NotifyWrite(list); if (e.Error != 0) { p.status = (byte)e.Error; } else if (!e.Handled && !p.multipleBlocks) { (obj as IGXDLMSBase).SetValue(settings, e); server.NotifyPostWrite(list); if (e.Error != 0) { p.status = (byte)e.Error; } } p.InvokeId = e.InvokeId; } catch (Exception) { p.status = (byte)ErrorCode.HardwareFault; } } } }
/// <summary> /// Generates data notification message. /// </summary> /// <param name="time">Date time. Set To Min or Max if not added</param> /// <param name="objects">List of objects and attribute indexes to notify.</param> /// <returns>Generated data notification message(s).</returns> public byte[][] GenerateDataNotificationMessages(DateTime time, List<KeyValuePair<GXDLMSObject, int>> objects) { if (objects == null) { throw new ArgumentNullException("objects"); } GXByteBuffer buff = new GXByteBuffer(); buff.SetUInt8(DataType.Structure); GXCommon.SetObjectCount(objects.Count, buff); foreach (KeyValuePair<GXDLMSObject, int> it in objects) { AddData(it.Key, it.Value, buff); } byte[][] reply; if (UseLogicalNameReferencing) { GXDLMSLNParameters p = new GXDLMSLNParameters(Settings, Command.DataNotification, 0, null, buff, 0xff); p.time = time; reply = GXDLMS.GetLnMessages(p); } else { GXDLMSSNParameters p = new GXDLMSSNParameters(Settings, Command.DataNotification, 1, 0, buff, null); reply = GXDLMS.GetSnMessages(p); } return reply; }
private static void HanleSetRequestWithList(GXDLMSSettings settings, byte invokeID, GXDLMSServer server, GXByteBuffer data, GXDLMSLNParameters p, GXByteBuffer replyData, GXDLMSTranslatorStructure xml) { ValueEventArgs e; int cnt = GXCommon.GetObjectCount(data); List <ValueEventArgs> list = new List <ValueEventArgs>(); if (xml != null) { xml.AppendStartTag(TranslatorTags.AttributeDescriptorList, "Qty", xml.IntegerToHex(cnt, 2)); } try { for (int pos = 0; pos != cnt; ++pos) { ObjectType ci = (ObjectType)data.GetUInt16(); byte[] ln = new byte[6]; data.Get(ln); short attributeIndex = data.GetUInt8(); // AccessSelection int selection = data.GetUInt8(); int selector = 0; object parameters = null; if (selection != 0) { selector = data.GetUInt8(); GXDataInfo info = new GXDataInfo(); parameters = GXCommon.GetData(settings, data, info); } if (xml != null) { xml.AppendStartTag(TranslatorTags.AttributeDescriptorWithSelection); xml.AppendStartTag(TranslatorTags.AttributeDescriptor); xml.AppendComment(ci.ToString()); xml.AppendLine(TranslatorTags.ClassId, "Value", xml.IntegerToHex((int)ci, 4)); xml.AppendComment(GXCommon.ToLogicalName(ln)); xml.AppendLine(TranslatorTags.InstanceId, "Value", GXCommon.ToHex(ln, false)); xml.AppendLine(TranslatorTags.AttributeId, "Value", xml.IntegerToHex(attributeIndex, 2)); xml.AppendEndTag(TranslatorTags.AttributeDescriptor); xml.AppendEndTag(TranslatorTags.AttributeDescriptorWithSelection); } else { GXDLMSObject obj = settings.Objects.FindByLN(ci, GXCommon.ToLogicalName(ln)); if (obj == null) { obj = server.NotifyFindObject(ci, 0, GXCommon.ToLogicalName(ln)); } if (obj == null) { // "Access Error : Device reports a undefined object." e = new ValueEventArgs(server, obj, attributeIndex, 0, 0); e.Error = ErrorCode.UndefinedObject; list.Add(e); } else { ValueEventArgs arg = new ValueEventArgs(server, obj, attributeIndex, selector, parameters); arg.InvokeId = invokeID; if (server.NotifyGetAttributeAccess(arg) == AccessMode.NoAccess) { //Read Write denied. arg.Error = ErrorCode.ReadWriteDenied; list.Add(arg); } else { list.Add(arg); } } } } cnt = GXCommon.GetObjectCount(data); if (xml != null) { xml.AppendEndTag(TranslatorTags.AttributeDescriptorList); xml.AppendStartTag(TranslatorTags.ValueList, "Qty", xml.IntegerToHex(cnt, 2)); } for (int pos = 0; pos != cnt; ++pos) { GXDataInfo di = new GXDataInfo(); di.xml = xml; if (xml != null && xml.OutputType == TranslatorOutputType.StandardXml) { xml.AppendStartTag(Command.WriteRequest, SingleReadResponse.Data); } object value = GXCommon.GetData(settings, data, di); if (!di.Complete) { value = GXCommon.ToHex(data.Data, false, data.Position, data.Size - data.Position); } else if (value is byte[]) { value = GXCommon.ToHex((byte[])value, false); } if (xml != null && xml .OutputType == TranslatorOutputType.StandardXml) { xml.AppendEndTag(Command.WriteRequest, SingleReadResponse.Data); } } if (xml != null) { xml.AppendEndTag(TranslatorTags.ValueList); } } catch (Exception ex) { if (xml == null) { throw ex; } } }
/// <summary> /// Convert xml to byte array. /// </summary> /// <param name="xml">Converted xml.</param> /// <returns>Converted bytes.</returns> public byte[] XmlToPdu(string xml) { XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); GXDLMSXmlSettings s = new GXDLMSXmlSettings(OutputType, Hex, ShowStringAsHex, tagsByName); ReadAllNodes(doc, s); GXByteBuffer bb = new GXByteBuffer(); GXDLMSLNParameters ln; GXDLMSSNParameters sn; switch (s.command) { case Command.InitiateRequest: case Command.InitiateResponse: break; case Command.ReadRequest: case Command.WriteRequest: case Command.ReadResponse: case Command.WriteResponse: sn = new GXDLMSSNParameters(s.settings, s.command, s.count, s.requestType, s.attributeDescriptor, s.data); GXDLMS.GetSNPdu(sn, bb); break; case Command.GetRequest: case Command.GetResponse: case Command.SetRequest: case Command.SetResponse: case Command.MethodRequest: case Command.MethodResponse: ln = new GXDLMSLNParameters(s.settings, s.command, s.requestType, s.attributeDescriptor, s.data, 0xff); GXDLMS.GetLNPdu(ln, bb); break; case Command.GloGetRequest: case Command.GloGetResponse: case Command.GloSetRequest: case Command.GloSetResponse: case Command.GloMethodRequest: case Command.GloMethodResponse: case Command.GloReadRequest: case Command.GloWriteRequest: case Command.GloReadResponse: case Command.GloWriteResponse: bb.SetUInt8((byte)s.command); GXCommon.SetObjectCount(s.data.Size, bb); bb.Set(s.data); break; case Command.Rejected: break; case Command.Snrm: s.settings.IsServer = false; bb.Set(GXDLMS.GetHdlcFrame(s.settings, (byte)Command.Snrm, null)); break; case Command.Ua: break; case Command.Aarq: case Command.GloInitiateRequest: GXAPDU.GenerateAarq(s.settings, s.settings.Cipher, s.data, bb); break; case Command.Aare: case Command.GloInitiateResponse: GXAPDU.GenerateAARE(s.settings, bb, s.result, s.diagnostic, s.settings.Cipher, s.data); break; case Command.DisconnectRequest: break; case Command.ReleaseRequest: bb.SetUInt8((byte)s.command); bb.SetUInt8(0); break; case Command.ReleaseResponse: bb.SetUInt8((byte)s.command); //Len bb.SetUInt8(3); //BerType bb.SetUInt8(BerType.Context); //Len. bb.SetUInt8(1); bb.SetUInt8(s.reason); break; case Command.ConfirmedServiceError: break; case Command.ExceptionResponse: break; case Command.GeneralBlockTransfer: break; case Command.AccessRequest: ln = new GXDLMSLNParameters(s.settings, s.command, s.requestType, s.attributeDescriptor, s.data, 0xff); GXDLMS.GetLNPdu(ln, bb); break; case Command.AccessResponse: ln = new GXDLMSLNParameters(s.settings, s.command, s.requestType, s.attributeDescriptor, s.data, 0xff); GXDLMS.GetLNPdu(ln, bb); break; case Command.DataNotification: ln = new GXDLMSLNParameters(s.settings, s.command, s.requestType, s.attributeDescriptor, s.data, 0xff); ln.time = s.time; GXDLMS.GetLNPdu(ln, bb); break; case Command.GloGeneralCiphering: break; case Command.GloEventNotificationRequest: break; default: case Command.None: throw new ArgumentException("Invalid command."); } return bb.Array(); }