RWE SmartHome: Auswerten der Sensor- und Aktorparameter

Derzeit arbeite ich in meiner Frezeit an einem neuen Projekt: der grafischen Auswertung aller Sensor- und Aktorparameter im RWE SmartHome. Im Gegensatz zu vergleichbaren Projekten, rufe ich die Werte selber in einem cron-Job direkt aus dem RWE SmartHome Portal ab.

Die Fertigstellung der Anwendung ist noch in weiter Ferne, trotzdem möchte ich hier bereits vorab einige beispielhafte Auswertungen zeigen:

1
Darstellung 1: Ausgangssituation; Übersicht über alle Sensoren im eigenen Diagramm

weiterlesen

log4net: GetAllAppenders()

Returns a List of the IAppender Interface, which actually contracts:


string Name { get; set; }
void Close();
void DoAppend(LoggingEvent loggingEvent);

 
        public static List<log4net.Appender.IAppender> GetAllAppenders()
        {
            List<log4net.Appender.IAppender> appenders = new List<log4net.Appender.IAppender>();

            log4net.Repository.Hierarchy.Hierarchy h = (log4net.Repository.Hierarchy.Hierarchy)log4net.LogManager.GetRepository();
            appenders.AddRange(h.Root.Appenders.Cast<log4net.Appender.IAppender>());

            foreach (log4net.Repository.Hierarchy.Logger logger in h.GetCurrentLoggers())
            {
                appenders.AddRange(logger.Appenders.Cast<log4net.Appender.IAppender>());
            }

            return appenders;
        }

I recommend to check (or at least output) all appenders at the beginning of each project you use log4net!

Das BGB als XML


(Verwendungsbeispiel)

Wer, aus welchen Gründen auch immer, das bürgerliche Gesetzbuch in seine Anwendung implementieren will, der wird sich über das “BGB als XML” freuen.

Download als .zip-Archiv
Download als .xml (ca. 3,3 MB)

Dank des universellen XML-Formats kann das BGB plattform- und programmiersprachenunabhängig eingebunden werden. Der einfachheit halber enthält es kein Inhaltsverzeichnis und keine Hierarchie. Es beschränkt sich lediglich auf die Normen und handelt sich um die aktuellste Ausgabe zum Zeitpunkt der Erstellung (Juli 2012).

So schaut beispielsweise ein XML-Knoten (“Norm”) aus:

    
      (1) Der Finder kann von dem Empfangsberechtigten einen Finderlohn
verlangen. Der Finderlohn beträgt von dem Werte der Sache bis zu 500
Euro fünf vom Hundert, von dem Mehrwert drei vom Hundert, bei Tieren
drei vom Hundert. Hat die Sache nur für den Empfangsberechtigten einen
Wert, so ist der Finderlohn nach billigem Ermessen zu bestimmen.
(2) Der Anspruch ist ausgeschlossen, wenn der Finder die Anzeigepflicht
verletzt oder den Fund auf Nachfrage verheimlicht.
      Neugefasst durch Bek. v. 2.1.2002 I 42
      § 971
      Finderlohn
    

Wer seine Anwendung ohnehin in C# .NET schreibt, der kann direkt meinen Deserializer und die dazugehörige Wrapperklasse verwenden:

 
[Serializable]
    public class BGB
    {
        private List<Norm> _Normen;

        public List<Norm> Normen
        {
            get { return _Normen; }
            set { _Normen = value; }
        }

        [Serializable]
        public class Norm
        {
            private string _text;

            public string Text
            {
                get { return _text; }
                set { _text = value; }
            }

            private string _appendix;

            public string Appendix
            {
                get { return _appendix; }
                set { _appendix = value; }
            }

            private string _paragraph;

            public string Paragraph
            {
                get { return _paragraph; }
                set { _paragraph = value; }
            }

            private string _title;

            public string Title
            {
                get { return _title; }
                set { _title = value; }
            }
        }

        public BGB()
        {
            _Normen = new List<Norm>();
        }

        public void AddNorm(Norm Norm)
        {
            _Normen.Add(Norm);
        }
    }

Der (De-)Serializer:

 
public class Serializer
    {
        public void SerializeToXMLString<T>(T XMLObj, Stream s, Encoding encoding, bool removeNamespace)
        {
            try
            {
                XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
                XmlTextWriter xmlSink = new XmlTextWriter(s, encoding);
                xmlSink.Formatting = Formatting.Indented;

                if (removeNamespace)
                {
                    XmlSerializerNamespaces xs = new XmlSerializerNamespaces();
                    xs.Add("", "");
                    xmlSerializer.Serialize(xmlSink, XMLObj, xs);
                }
                else
                    xmlSerializer.Serialize(xmlSink, XMLObj);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);

                if (ex.InnerException != null)
                    Console.WriteLine(ex.InnerException);
            }
        }

        public void SerializeObjectToXMLFile<T>(T XMLObj, string Filename, Encoding encoding, bool removeNamespace)
        {
            FileStream fs = new FileStream(Filename, FileMode.Create);
            SerializeToXMLString<T>(XMLObj, fs, encoding, removeNamespace);
        }

        public void SerializeObjectToXMLFile<T>(T o, string Filename)
        {
            SerializeObjectToXMLFile<T>(o, Filename, Encoding.Unicode, true);
        }

        private MemoryStream GetUTF16MS(string xml)
        {
            // Encode the XML string in a UTF-8 byte array
            byte[] encodedString = Encoding.Unicode.GetBytes(xml);

            // Put the byte array into a stream and rewind it to the beginning
            MemoryStream ms = new MemoryStream(encodedString);
            ms.Flush();
            ms.Position = 0;
            return ms;
        }

        private bool DeserializeFromXMLString<T>(string XML, ref T myObject, bool allow_log)
        {
            if (XML == string.Empty)
                return false;

            XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
            MemoryStream ms = GetUTF16MS(XML);

            try
            {
                XmlReader xmlReader = XmlReader.Create(ms);
                while (xmlReader.Read())
                { }

                ms.Position = 0;
            }
            catch (Exception ex)
            {
                if (allow_log)
                    Console.WriteLine(ex.Message);

                return false;
            }

            //StringReader sr = new StringReader(XML);
            //myObject = (T)xmlSerializer.Deserialize(sr);
            myObject = (T)xmlSerializer.Deserialize(ms);

            return true;
        }

        public bool deserializeObjectByXML<T>(string data, ref T myObject, bool allow_log)
        {
            return DeserializeFromXMLString(data, ref myObject, allow_log);
        }
    }

Pass a list of Integers to a Stored Procedure in T-SQL

To pass a list of Integers from .NET C# to a Stored Procedure in your MS SQL Server, take a look on the following article:

  1. Serializer
  2. Create list of Integers
  3. Create Stored Procedure

Serializer

First of all, you need to implement a simple and reliable Object-to-XML Serializer:

 
        public string serializeObjectToXML<T>(T o)
        {
            return SerializeToXMLString<T>(o, Encoding.Unicode, true);
        }

        public string SerializeToXMLString<T>(T XMLObj, Encoding encoding, bool removeNamespace)
        {
            MemoryStream memStrm = new MemoryStream();
            SerializeToXMLString(XMLObj, memStrm, encoding, removeNamespace);
            memStrm.Position = 0;
            var sr = new StreamReader(memStrm);
            return sr.ReadToEnd();
        }

        public void SerializeToXMLString<T>(T XMLObj, Stream s, Encoding encoding, bool removeNamespace)
        {
            try
            {
                XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
                XmlTextWriter xmlSink = new XmlTextWriter(s, encoding);
                xmlSink.Formatting = Formatting.Indented;

                if (removeNamespace)
                {
                    XmlSerializerNamespaces xs = new XmlSerializerNamespaces();
                    xs.Add("", "");
                    xmlSerializer.Serialize(xmlSink, XMLObj, xs);
                }
                else
                    xmlSerializer.Serialize(xmlSink, XMLObj);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);

                if (ex.InnerException != null)
                    Console.WriteLine(ex.InnerException);
            }
        }

        private static string GetXMLIntList(List<int> JobList)
        {
            Serializer s = new Serializer();
            return s.serializeObjectToXML(JobList);
        }

Create list of Integers

Next step: create a list of Integers and serialize it to an XML-String.

List<int> ListOfInteger = new List<int>() { 1, 2, 3, 4, 5, 100, 101 };
string xml_ListOfInteger = GetXMLIntList(ListOfInteger);

The content of xml_ListOfInteger is now:



  1
  2
  3
  4
  5
  100
  101

Create Stored Procedure

Create your Stored Procedure and declare the XML document as a parameter. The following example returns the list directly.

CREATE PROCEDURE [dbo].[GetValues]
@xml nvarchar(max)
AS
BEGIN
SET NOCOUNT ON;

DECLARE @idoc int;
DECLARE @doc nvarchar(max)
SET @doc = CONVERT(nvarchar(max),@xml);

EXEC sp_xml_preparedocument @idoc OUTPUT, @doc

SELECT Value FROM OPENXML(@idoc, 'ArrayOfInt/int')
WITH (
Value int 'text()'
)
END

C#: TimeSpan serialisieren

Möchte man mit einer Klasse einen TimeSpan serialisieren, muss man sich eines kleinen Wrappers behelfen, da andernfalls der TimeSpan-Wert falsch serialisiert und so beim Deserialisieren mit “00:00:00” überschrieben wird. Das kann zu äußerst unangenehmen Nebeneffekten führen und mir ist es unbegreiflich warum TimeSpans nicht von Haus aus sauber serialisiert werden können.

Binde dazu das folgende struct in deine Klasse ein, die serialisiert werden soll:

 
        public struct TimeSpan : IXmlSerializable
        {
            private System.TimeSpan _value;

            public static implicit operator TimeSpan(System.TimeSpan value)
            {
                return new TimeSpan { _value = value };
            }

            public static implicit operator System.TimeSpan(TimeSpan value)
            {
                return value._value;
            }

            public XmlSchema GetSchema()
            {
                return null;
            }

            public void ReadXml(XmlReader reader)
            {
                _value = System.TimeSpan.Parse(reader.ReadElementContentAsString());
            }

            public void WriteXml(XmlWriter writer)
            {
                writer.WriteValue(_value.ToString());
            }
        }

Diese Wrapperklasse stammt ursprünglich hier her: , funktionierte allerdings nicht richtig bei mir, da der reader-Aufruf “ReadContentAsString()” eine Exception warf. Die obige Klasse funktioniert bei mir fehlerfrei, auch in Verbindung mit dem Entity Framework.

Und so schaut das ganze als serialisiertes XML aus:

    <ServiceRuntimeDefinitionService>
      <SerializedServiceRuntimeDefinition>
        <SeralizedServiceRuntimeDaysList />
        <Description />
        <RuntimeName>DummyRuntime</RuntimeName>
        <Start>13:00:00</Start>
        <End>14:00:00</End>
        <ToleranceStart>00:10:00</ToleranceStart>
        <ToleranceEnd>00:10:00</ToleranceEnd>
        <DefaultAVG>00:01:00</DefaultAVG>
        <Duration>2.08:29:35.2500000</Duration>
      </SerializedServiceRuntimeDefinition>

General problems with Npgsql.NET

1. Make sure all attachements are delivered with your software build. Npgsql may come with some additional program librarys (especially security.mono.dll and npgsql.dll).
2. Implement the following procedures: ExecuteScalar() instead of retrieving a whole dataset for single value return querys.
3. Try to increase the connection pooling minimum value of the sql connection. You may override that setting in the connection string. See “user’s manual” of npgsql for further information.
4. Npgsql seems not to be thread-safe. Adding “Thread.Sleep()” to procedures where exception are randomly thrown can decrease the number of errors resulting from asynchronous thread operations.

Membership.GetUser() gibt null zurück

Es ist die ungünstigste Situation die man sich nur denken kann: am Quelltext wurden marginale Änderungen gemacht, woraufhin die ASP.NET Membership Authentication auf dem Remote Test System, aber nicht mehr lokal funktioniert. Auch nach mehrmaligem Clean/Build/Publish und dem akribischen prüfen der web.conf ändert sich dieses Verhalten nicht. Membership.GetUser() gibt immer null zurück.
Warum dieses Verhalten jetzt und nicht zuvor auftritt ist mir immernoch unklar, aber ich fand nach vielen Eigenversuchen, in einem Forum einen Snippet der das Problem bei mir behebt:

 
MembershipUser user = Membership.GetUser(username);
GenericIdentity identity = new GenericIdentity(user.UserName);
RolePrincipal principal = new RolePrincipal(identity);
System.Threading.Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;

Quelle.

Meine Lieblingserweiterungen in C#/LINQ

Im Laufe der Zeit habe ich mir ein kleines Repertoire an Extensions und Hilfsfunktionen in C# und LINQ angesammelt. Hier meine Favoriten:

1. ForEach()
2. IsEmpty()
3. GetSQLDate()
4. Stopwatch extensions
5. StringBuilder extensions
6. Merging lists
7. Left/Mid/Right

1. ForEach()

 
        public static void ForEach<T> (this IEnumerable<T>  enumeration, Action<T>  action)
        {
            foreach (T item in enumeration)
            {
                action(item);
            }
        }

Beispiel:

 
int[] foo = { 1, 2, 3, 4, 5 };
foo.ForEach(p =>  p *= p);
// foo enthält nun: { 1, 4, 9, 16, 25 }

(Bitte verschont mich mit Kommentaren bzgl. des Gebrauchs von foreach-Schleifen.)


2. IsEmpty()

 
        public static Boolean IsEmpty<T> (this IEnumerable<T>  source)
        {
            try
            {
                if (source == null)
                    return true;
                return !source.Any();
            }
            catch
            {
                return true;
            }
        }

Beispiel:

 
int[] foo = { 1, 2, 3, 4, 5 };
bool bar;

bar = foo.IsEmpty();
// bar ist nun false
foo = null;
bar = foo.IsEmpty();
// bar ist nun true

3. GetSQLDate()
Wandelt ein Datumswert in das SQL-Server Standardformat um.

 
        public static string GetSQLDate(this DateTime? _d)
        {
            if (_d.HasValue)
                return string.Concat("'", _d.Value.ToString("yyyy-MM-dd HH:mm:ss"), "'");
            else
                return "NULL";
        }

Beispiel:

 
DateTime foo = DateTime.Now;
string SQL = string.Format("SELECT ID FROM dbo.dummy_table WHERE datetime={0};", foo.GetSQLDate());

/*
Wäre "foo" ein DateTime?, also nullable, würde nach "NULL" gesucht werden. Das ist bei SELECT-Abfragen ggf.
unerwünscht, aber bei INSERT-Abfragen durchaus gewollt.
*/

4. Stopwatch extensions

 
        public static string StopAndGetSecondsString(this Stopwatch stopwatch)
        {
            stopwatch.Stop();
            double Seconds = stopwatch.ElapsedTicks / (float)Stopwatch.Frequency;
            return String.Format("{0:0.0000}", Seconds);
        }

        public static void Restart(this Stopwatch stopwatch)
        {
            stopwatch.Stop();
            stopwatch.Reset();
            stopwatch.Start();
        }

        public static Stopwatch GetStartedStopwatch()
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            return stopwatch;
        }

Beispiel:

 
Stopwatch sw = GetStartedStopwatch(); // Stopwatch läuft bereits!

// Zeitintensive Operationen hier einfügen...

Console.WriteLine(sw.StopAndGetSecondsString()); // Stoppen und Ausgabe auf Console.

sw.Restart(); // Zurücksetzen und wieder starten.

5. StringBuilder extensions

 
        public static StringBuilder AppendAllElements(this StringBuilder _S, IEnumerable<string>  _IE)
        {
            _IE.ForEach(s =>  _S.AppendLine(s));
            return _S;
        }

Beispiel:

 
StringBuilder sb = new StringBuilder();
List<string>  s = new List<string> () { "a", "b", "c", "d", "e" };

sb.AppendAllElements(s); // füge alle Objekte aus "s" an den StringBuilder (zeilengetrennt!)

Console.WriteLine(sb.ToString()); // Ausgabe auf die Console

6. Merging lists

 
        public static IEnumerable<string>  Merge(this IEnumerable<IEnumerable<string> >  enumeration)
        {
            List<string>  _l = new List<string> ();
            foreach (IEnumerable<string>  item in enumeration)
            {
                _l.AddRange(item);
            }
            return _l.AsEnumerable();
        }

Beispiel:

 
List<List<string> >  s1 = new List<List<string> > (); // Jagged bzw. mehrdimensionale Liste!
List<string>  s2 = new List<string> () { "a", "b", "c", "d", "e" };
List<string>  s3 = new List<string> () { "D", "1", "V", "F", "Q" };
s1.Add(s2);
s1.Add(s3);

List<string>  s4 = s1.Merge().ToList();
// s4 enthält nun: 
// "a", "b", "c", "d", "e", "D", "1", "V", "F", "Q"

Wie ich später herausgefunden habe, geht das ganze auch viel einfacher ohne Extension, nur mit SelectMany() :

Beispiel:

 
List<List<string> >  s1 = new List<List<string> > (); // Jagged bzw. mehrdimensionale Liste!
List<string>  s2 = new List<string> () { "a", "b", "c", "d", "e" };
List<string>  s3 = new List<string> () { "D", "1", "V", "F", "Q" };
s1.Add(s2);
s1.Add(s3);

List<string>  s4 = s1.SelectMany(x =>  x).ToList();
// s4 enthält nun: 
// "a", "b", "c", "d", "e", "D", "1", "V", "F", "Q"

7. Left/Mid/Right
Stringoperationen für alle VB-Umsteiger:

 
        public static string Left(this string param, int length)
        {
            return param.Substring(0, length);
        }

        public static string Right(this string param, int length)
        {
            return param.Substring(param.Length - length, length);
        }

        public static string Mid(this string param, int startIndex, int length)
        {
            return param.Substring(startIndex, length); ;
        }

        public static string Mid(this string param, int startIndex)
        {
            return param.Substring(startIndex);
        }