/// <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(settings, invokeID, Command.GetResponse, 3, null, bb, 0xFF); GXDLMS.GetLNPdu(p, replyData); }
/// <summary> /// Read DLMS Data from the device. /// </summary> /// <param name="data">Data to send.</param> /// <returns>Received data.</returns> public void ReadDLMSPacket(byte[] data, GXReplyData reply) { if (data == null && !reply.IsStreaming()) { return; } GXReplyData notify = new GXReplyData(); reply.Error = 0; object eop = (byte)0x7E; //In network connection terminator is not used. if (Client.InterfaceType == InterfaceType.WRAPPER && Media is GXNet) { eop = null; } int pos = 0; bool succeeded = false; ReceiveParameters <byte[]> p = new ReceiveParameters <byte[]>() { Eop = eop, WaitTime = WaitTime, }; if (eop == null) { p.Count = 8; } else { p.Count = 5; } GXByteBuffer rd = new GXByteBuffer(); lock (Media.Synchronous) { while (!succeeded && pos != 3) { if (!reply.IsStreaming()) { WriteTrace("TX:\t" + DateTime.Now.ToLongTimeString() + "\t" + GXCommon.ToHex(data, true)); Media.Send(data, null); } succeeded = Media.Receive(p); if (!succeeded) { if (++pos >= RetryCount) { throw new Exception("Failed to receive reply from the device in given time."); } //If Eop is not set read one byte at time. if (p.Eop == null) { p.Count = 1; } //Try to read again... System.Diagnostics.Debug.WriteLine("Data send failed. Try to resend " + pos.ToString() + "/3"); } } rd = new GXByteBuffer(p.Reply); try { pos = 0; //Loop until whole COSEM packet is received. while (!Client.GetData(rd, reply, notify)) { p.Reply = null; if (notify.Data.Data != null) { //Handle notify. if (!notify.IsMoreData) { //Show received push message as XML. string xml; GXDLMSTranslator t = new GXDLMSTranslator(TranslatorOutputType.SimpleXml); t.DataToXml(notify.Data, out xml); Console.WriteLine(xml); notify.Clear(); } continue; } else if (p.Eop == null) { p.Count = Client.GetFrameSize(rd); } while (!Media.Receive(p)) { if (++pos >= RetryCount) { throw new Exception("Failed to receive reply from the device in given time."); } //If echo. if (rd == null || rd.Size == data.Length) { Media.Send(data, null); } //Try to read again... System.Diagnostics.Debug.WriteLine("Data send failed. Try to resend " + pos.ToString() + "/3"); } rd.Set(p.Reply); } } catch (Exception ex) { WriteTrace("RX:\t" + DateTime.Now.ToLongTimeString() + "\t" + rd); throw ex; } } WriteTrace("RX:\t" + DateTime.Now.ToLongTimeString() + "\t" + rd); if (reply.Error != 0) { if (reply.Error == (short)ErrorCode.Rejected) { Thread.Sleep(1000); ReadDLMSPacket(data, reply); } } }
/// <summary> /// Execute basic tests. /// </summary> /// <param name="settings">Settings.</param> /// <param name="output">Generated output.</param> public static bool Basic(GXSettings settings, GXOutput output) { Reader.GXDLMSReader reader = new Reader.GXDLMSReader(settings.client, settings.media, settings.trace, settings.iec); reader.WaitTime = settings.WaitTime; try { settings.media.Open(); } catch (System.Net.Sockets.SocketException e) { //If failed to make connection to the meter. output.Errors.Add("Failed to connect to the meter: " + e.Message); output.MakeReport(); return(false); } if (settings.trace > TraceLevel.Error) { Console.WriteLine("------------------------------------------------------------"); Console.WriteLine("Initialize connection."); } reader.InitializeConnection(); if (settings.trace > TraceLevel.Error) { Console.WriteLine("Get association view."); } reader.GetAssociationView(false); if (settings.client.UseLogicalNameReferencing) { output.PreInfo.Add("Testing using Logical Name referencing."); } else { output.PreInfo.Add("Testing using Short Name referencing."); } output.PreInfo.Add("Authentication level: " + settings.client.Authentication); output.PreInfo.Add("Total amount of objects: " + settings.client.Objects.Count.ToString()); if (settings.client.UseLogicalNameReferencing) { if (settings.trace > TraceLevel.Error) { Console.WriteLine("Finding Logical Device Name and SAP."); } GXDLMSObject ldn = settings.client.Objects.FindByLN(ObjectType.None, "0.0.42.0.0.255"); GXDLMSObjectCollection saps = settings.client.Objects.GetObjects(ObjectType.SapAssignment); if (ldn == null && saps.Count == 0) { output.Errors.Add("Logical Device Name or SAP is not implemented. Read more: GB: 4.1.8.4."); } if (settings.trace > TraceLevel.Error) { if (ldn != null) { reader.Read(ldn, 2); output.PreInfo.Add("Meter Logical Device Name is: " + (ldn as GXDLMSData).Value.ToString() + "."); } if (saps.Count != 0) { output.PreInfo.Add("SAP is not implemented."); } } } //Check OBIS codes. foreach (GXDLMSObject it in settings.client.Objects) { if (it.Description == "Invalid") { output.Errors.Add("Invalid OBIS code " + it.LogicalName + " for <a target=\"_blank\" href=http://www.gurux.fi/" + it.GetType().FullName + ">" + it.ObjectType + "</a>."); if (settings.trace > TraceLevel.Warning) { Console.WriteLine("------------------------------------------------------------"); Console.WriteLine(it.LogicalName + ": Invalid OBIS code."); } } } //Read structures of Cosem objects. List <KeyValuePair <string, List <GXDLMSXmlPdu> > > cosemTests = new List <KeyValuePair <string, List <GXDLMSXmlPdu> > >(); GXDLMSTranslator translator = new GXDLMSTranslator(TranslatorOutputType.SimpleXml); foreach (string it in GetTests()) { using (Stream stream = typeof(Program).Assembly.GetManifestResourceStream(it)) using (StreamReader sr = new StreamReader(stream)) { XmlDocument doc = new XmlDocument(); doc.LoadXml(sr.ReadToEnd()); XmlNodeList list = doc.SelectNodes("/Messages/GetRequest/GetRequestNormal"); ObjectType ot = ObjectType.None; foreach (XmlNode node in list) { ot = (ObjectType)int.Parse(node.SelectNodes("AttributeDescriptor/ClassId")[0].Attributes["Value"].Value); //If this object type is skipped. if (settings.excludedObjects.Contains(ot)) { output.Info.Add("Skipping " + ot.ToString() + " object types."); break; } int index = int.Parse(node.SelectNodes("AttributeDescriptor/AttributeId")[0].Attributes["Value"].Value); //Update logical name. foreach (GXDLMSObject obj in settings.client.Objects.GetObjects(ot)) { if ((obj.GetAccess(index) & AccessMode.Read) != 0) { string tmp = GXCommon.ToHex(LogicalNameToBytes(obj.LogicalName), false); foreach (XmlNode n in list) { XmlAttribute ln = n.SelectNodes("AttributeDescriptor/InstanceId")[0].Attributes["Value"]; ln.Value = tmp; } cosemTests.Add(new KeyValuePair <string, List <GXDLMSXmlPdu> >(ot.ToString(), settings.client.LoadXml(doc.InnerXml))); } } break; } } } foreach (KeyValuePair <string, List <GXDLMSXmlPdu> > it in cosemTests) { try { Execute(settings, reader, it.Key, it.Value, output); } catch (Exception ex) { if (settings.trace > TraceLevel.Off) { Console.WriteLine("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); Console.WriteLine(ex.Message); } } } List <ObjectType> unknownDataTypes = new List <ObjectType>(); foreach (GXDLMSObject o in settings.client.Objects) { if (!unknownDataTypes.Contains(o.ObjectType)) { bool found = false; foreach (KeyValuePair <string, List <GXDLMSXmlPdu> > t in cosemTests) { if (o.ObjectType.ToString() == t.Key) { found = true; break; } } if (!found) { unknownDataTypes.Add(o.ObjectType); output.Warnings.Add("<a target=\"_blank\" href=http://www.gurux.fi/" + o.GetType().FullName + ">" + o.ObjectType + "</a> is not tested."); } } } //Check InactivityTimeout. bool fail = false; int rc = reader.RetryCount; int wt = reader.WaitTime; int inactivityTimeout; if (settings.client.InterfaceType == InterfaceType.HDLC) { GXDLMSHdlcSetup s = (GXDLMSHdlcSetup)settings.client.Objects.GetObjects(ObjectType.IecHdlcSetup)[0]; s.InactivityTimeout = 0; reader.Read(s, 8); inactivityTimeout = s.InactivityTimeout; output.PreInfo.Add("HdlcSetup default inactivity timeout value is " + inactivityTimeout + " seconds."); if ((s.GetAccess(8) & AccessMode.Write) != 0) { //Wait second. s.InactivityTimeout = 1; reader.Write(s, 8); Thread.Sleep(2000); try { reader.WaitTime = 1000; reader.RetryCount = 0; reader.Read(s, 8); } catch (Exception) { //This should fails. fail = true; } reader.InitializeConnection(); s.InactivityTimeout = inactivityTimeout; reader.Write(s, 8); if (!fail) { output.Errors.Add("HdlcSetup failed. InactivityTimeout don't work."); } } else { output.PreInfo.Add("HdlcSetup inactivity timeout is not tested."); } } else { GXDLMSTcpUdpSetup s = (GXDLMSTcpUdpSetup)settings.client.Objects.GetObjects(ObjectType.TcpUdpSetup)[0]; s.InactivityTimeout = 0; reader.Read(s, 6); inactivityTimeout = s.InactivityTimeout; output.PreInfo.Add("TcpUdpSetup default inactivity timeout value is " + inactivityTimeout + " seconds."); if ((s.GetAccess(6) & AccessMode.Write) != 0) { //Wait second. s.InactivityTimeout = 1; reader.Write(s, 6); Thread.Sleep(2000); try { reader.WaitTime = 1000; reader.RetryCount = 0; reader.Read(s, 6); } catch (Exception) { //This should fails. fail = true; } reader.InitializeConnection(); s.InactivityTimeout = inactivityTimeout; reader.Write(s, 6); if (!fail) { output.Errors.Add("TcpUdpSetup failed. InactivityTimeout don't work."); } } else { output.PreInfo.Add("TcpUdpSetup inactivity timeout is not tested."); } } reader.WaitTime = wt; reader.RetryCount = rc; output.MakeReport(); return(true); }
/// <summary> /// Read all objects from the meter. /// </summary> /// <remarks> /// It's not normal to read all data from the meter. This is just an example. /// </remarks> public void GetReadOut() { foreach (GXDLMSObject it in Client.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. if (Trace > TraceLevel.Error) { Console.WriteLine("Unknown Interface: " + it.ObjectType.ToString()); } continue; } if (Trace > TraceLevel.Warning) { Console.WriteLine("-------- Reading " + it.GetType().Name + " " + it.Name + " " + it.Description); } foreach (int pos in (it as IGXDLMSBase).GetAttributeIndexToRead(true)) { try { object val = Read(it, pos); ShowValue(val, pos); } catch (Exception ex) { Console.WriteLine("Error! Index: " + pos + " " + ex.Message); } } } //Find profile generics and read them. foreach (GXDLMSObject it in Client.Objects.GetObjects(ObjectType.ProfileGeneric)) { //If trace is info. if (Trace > TraceLevel.Warning) { Console.WriteLine("-------- Reading " + it.GetType().Name + " " + it.Name + " " + it.Description); } long entriesInUse = Convert.ToInt64(Read(it, 7)); long entries = Convert.ToInt64(Read(it, 8)); //If trace is info. if (Trace > TraceLevel.Warning) { Console.WriteLine("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 ((Client.NegotiatedConformance & (Gurux.DLMS.Enums.Conformance.ParameterizedAccess | Gurux.DLMS.Enums.Conformance.SelectiveAccess)) != 0) { try { //Read first row from Profile Generic. object[] rows = ReadRowsByEntry(it as GXDLMSProfileGeneric, 1, 1); //If trace is info. if (Trace > TraceLevel.Warning) { 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"); } } } catch (Exception ex) { Console.WriteLine("Error! Failed to read first row: " + ex.Message); //Continue reading. } } //All meters are not supporting parameterized read. if ((Client.NegotiatedConformance & (Gurux.DLMS.Enums.Conformance.ParameterizedAccess | Gurux.DLMS.Enums.Conformance.SelectiveAccess)) != 0) { try { //Read last day from Profile Generic. object[] rows = ReadRowsByRange(it as GXDLMSProfileGeneric, DateTime.Now.Date, DateTime.MaxValue); //If trace is info. if (Trace > TraceLevel.Warning) { 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"); } } } catch (Exception ex) { Console.WriteLine("Error! Failed to read last day: " + ex.Message); //Continue reading. } } } }