shared Memory mit XML

  • VB.NET

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von Nils_Kr.

    shared Memory mit XML

    Hi,

    ich benutze ein Programm, dass eine Logfile als xml entweder auf die Festplatte speichert, oder aber als sharedMemory in den Hauptspeicher ablegt.

    Mit Using file = MemoryMappedFile.OpenExisting("testFileExport") finde ich die Datei und kann sie auch öffnen. Allerdings habe ich keinerlei Ahnung,
    wie ich die File ausgelesen bekomme.

    So sieht die file aus, wenn man sie auf der Platte speichert:

    XML-Quellcode

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <LogDataExport xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    3. <name>testFileExport</name>
    4. <exportTime>2017-08-21T19:49:05.3065429+02:00</exportTime>
    5. <logdata>
    6. <LogDataSet>
    7. <t>2017-08-21T19:49:05.208</t>
    8. <value>30</value>
    9. <name>GPU Temperature</name>
    10. <unit>°C</unit>
    11. <valueType>Temperature</valueType>
    12. <device>System</device>
    13. </LogDataSet>
    14. <LogDataSet>
    15. <t>2017-08-21T19:49:04.796</t>
    16. <value>26.28</value>
    17. <name>waterTemp</name>
    18. <unit>°C</unit>
    19. <valueType>Temperature</valueType>
    20. <device>aquaero</device>
    21. </LogDataSet>
    22. </logdata>
    23. </LogDataExport>


    Jemand eine Idee, wie ich da rangehen kann?
    Option strict = on

    If it's stupid and it works it ain't stupid.
    Inzwischen bekomme ich die file schon in einen String:

    XML-Quellcode

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <LogDataExport xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    3. <name>testFileExport</name>
    4. <exportTime>2017-08-21T22:05:26.6299923+02:00</exportTime>
    5. <logdata>
    6. <LogDataSet>
    7. <t>2017-08-21T22:05:26.626</t>
    8. <value>31</value>
    9. <name>GPU Temperature</name>
    10. <unit>°C</unit>
    11. <valueType>Temperature</valueType>
    12. <device>System</device>
    13. </LogDataSet>
    14. <LogDataSet>
    15. <t>2017-08-21T22:05:25.818</t>
    16. <value>26.56</value>
    17. <name>waterTemp</name>
    18. <unit>°C</unit>
    19. <valueType>Temperature</valueType>
    20. <device>aquaero</device>
    21. </LogDataSet>
    22. </logdata>
    23. </LogDataExport>


    Allerdings sind wie man sieht noch kleine Fehler drinne. (Die ersten drei Zeichen & bei °C)

    VB.NET-Quellcode

    1. Try
    2. Dim mmf As MemoryMappedFile = MemoryMappedFile.CreateOrOpen("testFileExport", 1000)
    3. Dim accessor As MemoryMappedViewAccessor = mmf.CreateViewAccessor()
    4. Dim byteValue As Byte
    5. Dim index As Integer = 0
    6. Dim message As New StringBuilder()
    7. Do
    8. byteValue = accessor.ReadByte(index)
    9. If byteValue <> 0 Then
    10. Dim chr As Char = ChrW(byteValue)
    11. message.Append(chr)
    12. End If
    13. index += 1
    14. Loop While byteValue <> 0
    15. RichTextBox1.Text = message.ToString
    16. Catch filenotfound As Exception
    17. Debug.WriteLine(filenotfound.Message)
    18. End Try


    Liege ich richtig damit, dass die Fehler dadurch entstehen, dass ich die bytevalues in einen Ascii-Char umwandle?

    PS: Wahrscheinlich nein, aber wo genau der Fehler ist, weiß ich jetzt auch nicht. chrw ist komisch.

    PS2: Angeblich kommt chrw mit Unicode klar. Woher kommen dann die Lesefehler? ?(
    Option strict = on

    If it's stupid and it works it ain't stupid.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Nils_Kr“ ()

     ist ein fehlinterpretiertes BOM - eine Angabe zum Encoding der Datei. BOMs sind sehr nützlich und vermeiden Lesfehler bei Sonderzeichen u.ä..
    Nutze vorgesehene Einlese-Technologie, die sollte damit richtig umgehen können.

    VB.NET-Quellcode

    1. Public Shared Function GetXDoc() As XDocument
    2. Using mmf = MemoryMappedFile.CreateOrOpen("testFileExport", 1000)
    3. Return XDocument.Load(mmf.CreateViewStream)
    4. End Using
    5. End Function
    6. '...
    7. Richtextbox1.Text = GetXDoc.ToString()
    Und lass TryCatches weg, solange du noch am Entwickeln bist.
    Fehlermeldungen sind äusserst wertvoll beim Entwickeln - mit unqualifizierten TryCatchens schiesst man sich nur pausenlos ins Knie.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Gerade getestet, bekomme folgende Meldung:
    An unhandled exception of type 'System.Xml.XmlException' occurred in System.Xml.dll
    "Additional information: '.', hexidezimaler Wert 0x00, ist ein ungültiges Zeichen. Zeile 1, Position 1."

    PS: Ich habe die Xml in den Anhang gepackt.
    Dateien
    Option strict = on

    If it's stupid and it works it ain't stupid.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Nils_Kr“ ()

    source.roslyn.codeplex.com/#Mi…rageServiceFactory.cs,288
    Die skippen für den TextReader einfach ein char->2 bytes. Kannst ja dasselbe machen.
    Wundert mich jedoch, dass die den wert nicht verwenden^^
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Bei XDocument.Load() kann ich doch keine bytes überspringen oder? Wenn ich mit dem Accessor byteweise einlese und mit chrW() übersetzte gibt es ja auch leider mitten im Document lesefehler.

    Ich habe jetzt übrigens mal die auf der Platte liegende xml mit XDocument.Load() eingelesen, das geht ohne jegliche Probleme ?(

    VB.NET-Quellcode

    1. Dim xmlFile = XDocument.Load("C:\temp\testFileExport.xml")
    2. Console.WriteLine(xmlFile)


    Dann habe nochmal erneut mit XDocument.Load() die MMF eingelesen und es kommt ein Fehler an in Zeile 23 an Position 17. Nur hat die File in der letzten Zeile nur 16 Zeichen. Der Ladebefehl scheint also nicht zu wissen, wann die Datei zu Ende ist. Jemand eine Idee woran das liegen kann?

    PS: Sowohl die xml auf der Festplatte, als die im Ram sind 759 Bytes groß.
    Option strict = on

    If it's stupid and it works it ain't stupid.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Nils_Kr“ ()

    jo - da simmer ja enttäsucht.
    Dass die XDocument.Load - Methode, die doch einen Stream erwartet, diesen dennoch nicht vernünftig lesen kann - tss!
    probierma mit zwischengeschaltetem TextReader:

    VB.NET-Quellcode

    1. Public Shared Function GetXDoc() As XDocument
    2. Using mmf = MemoryMappedFile.CreateOrOpen("testFileExport", 1000), rd = new StreamReader(mmf.CreateViewStream)
    3. Return XDocument.Load(rd)
    4. End Using
    5. End Function

    Nils_Kr schrieb:

    Zeile 23 Zeichen 17


    Ich vermute, dass da am Ende noch ein Null-Byte dranhängt. Wo kommen die Daten denn eigentlich her? Also warum eine MemoryMappedFile und nicht einfach... ein Byte-Array oder so?
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Bei dem Programm, dass die File erstellt, handelt es sich um die AquaSuite von aquacomputer. Das ist die Software für eine programmierbare Lüftersteuerung. Basiert auf Net4.0.

    Darin gibt es eine Funktion, die jede Sekunde die aktuellen Lüftergeschwindigkeiten etc. in eine XML schreibt, diese können dann von einem selbst erstellten Tool ausgelesen werden. Das Programm bietet die Funktion, die xml entweder in dem Ram, oder auf einer Festplatte abzulegen.

    Das Auslesen der XML von der Festplatte funktioniert ohne Probleme. Aus Neugier / Experimentierfreude würde ich es aber auch gerne hinbekommen, die Datei direkt aus dem Ram zu lesen.
    Ich habe inzwischen drei Varianten getestet. Wenn man byteweise ausliest und mit chrw() übersetzt, kommt es zu keiner Fehlermeldung, aber es gibt gelegentlich Übersetzungsfehler. xdocument.load() hat gestern über ein ungültiges Zeichen an Stelle 1 gemeckert, heute ist es an der letzten Stelle.

    Die Variante hier, meldet sowohl heute, als auch gestern über ein unzulässiges Zeichen an der letzten Stelle.

    VB.NET-Quellcode

    1. Dim mmf As MemoryMappedFile = MemoryMappedFile.CreateOrOpen("testFileExport", 1000000)
    2. Using stream As MemoryMappedViewStream = mmf.CreateViewStream()
    3. Dim reader As XmlReader = XmlReader.Create(New StreamReader(stream, Encoding.UTF8))
    4. While reader.Read()
    5. End While
    6. End Using


    Es macht übrigens keinerlei Unterschied, welche capacity man bei .CreateOrOpen() einträgt, solange diese > 0 ist.
    Option strict = on

    If it's stupid and it works it ain't stupid.
    Ich würde jetzt einfach mal probieren, was passiert, wenn Du vor dem Null-Byte aufhörst. Ich hab dafür mal auf die Schnelle diese Klasse gebastelt:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.IO
    2. Public Class StopAtNullStream
    3. Inherits Stream
    4. Dim BaseStream As Stream
    5. Dim IsStopped As Boolean = False
    6. Public Sub New(NewBaseStream As Stream)
    7. BaseStream = NewBaseStream
    8. End Sub
    9. #Region "Stuff"
    10. Public Overrides ReadOnly Property CanRead As Boolean
    11. Get
    12. Return BaseStream.CanRead
    13. End Get
    14. End Property
    15. Public Overrides ReadOnly Property CanSeek As Boolean
    16. Get
    17. Return BaseStream.CanSeek
    18. End Get
    19. End Property
    20. Public Overrides ReadOnly Property CanWrite As Boolean
    21. Get
    22. Return BaseStream.CanWrite
    23. End Get
    24. End Property
    25. Public Overrides Sub Flush()
    26. BaseStream.Flush()
    27. End Sub
    28. Public Overrides ReadOnly Property Length As Long
    29. Get
    30. Return BaseStream.Length
    31. End Get
    32. End Property
    33. Public Overrides Property Position As Long
    34. Get
    35. Return BaseStream.Position
    36. End Get
    37. Set(value As Long)
    38. BaseStream.Position = value
    39. End Set
    40. End Property
    41. Public Overrides Function Seek(offset As Long, origin As System.IO.SeekOrigin) As Long
    42. Return BaseStream.Seek(offset, origin)
    43. End Function
    44. Public Overrides Sub SetLength(value As Long)
    45. BaseStream.SetLength(value)
    46. End Sub
    47. Public Overrides Sub Write(buffer() As Byte, offset As Integer, count As Integer)
    48. BaseStream.Write(buffer, offset, count)
    49. End Sub
    50. #End Region
    51. Public Overrides Function Read(buffer() As Byte, offset As Integer, count As Integer) As Integer
    52. Dim ReadBytes = BaseStream.Read(buffer, offset, count)
    53. For i = offset To offset + Math.Min(count, ReadBytes) - 1
    54. If buffer(i) = 0 Then
    55. IsStopped = True
    56. Return i - offset
    57. End If
    58. Next
    59. Return ReadBytes
    60. End Function
    61. End Class

    Verwende sie so:

    VB.NET-Quellcode

    1. Dim mmf As MemoryMappedFile = MemoryMappedFile.CreateOrOpen("testFileExport", 1000000)
    2. Using stream As MemoryMappedViewStream = mmf.CreateViewStream()
    3. Using stopStream As New StopAtNullStream(stream)
    4. Dim reader As XmlReader = XmlReader.Create(New StreamReader(stopStream, Encoding.UTF8))
    5. While reader.Read()
    6. End While
    7. End Using
    8. End Using

    Also sozusagen über den eigentlichen Quell-Stream "drüberlegen". Der StopAtNullStream hört beim ersten Null-Byte, das er findet, auf. Dadurch sollten die Reader niemals das Null-Byte zu sehen bekommen.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @Nils_Kr
    Ich würde es dabei nicht belassen. Das war eher als Test gedacht. Da wir jetzt wissen, dass es am Null-Byte liegt, können wir effizienter suchen:

    VB.NET-Quellcode

    1. 'MemoryMappedFile öffnen.
    2. Dim File = MemoryMappedFile.CreateOrOpen(...)
    3. 'Alle Bytes rauslesen.
    4. Dim Bytes As Byte()
    5. Using MemoryStream As New MemoryStream
    6. Using View = File.CreateViewStream
    7. View.CopyTo(MemoryStream)
    8. End Using
    9. Bytes = MemoryStream.ToArray
    10. End Using
    11. 'Länge bis zum ersten Null-Byte herausfinden (oder die ganze Länge, wenn keines vorhanden ist).
    12. Dim LengthToZeroByte = Bytes.Length
    13. For i = 0 To Bytes.Length - 1
    14. If Bytes(i) = 0 Then
    15. LengthToZeroByte = i
    16. Exit For
    17. End If
    18. Next
    19. 'Den ausgewählten Bereich nach UTF-8 dekodieren.
    20. Dim RawXml = System.Text.Encoding.UTF8.GetString(Bytes, 0, LengthToZeroByte)
    21. 'Parsen.
    22. Dim Document = XElement.Parse(RawXml)

    Beachte: Sollte im XML irgendwo ein Null-Byte vorkommen, dann würde das (und auch der StopAtNullStream von oben) falsche Ergebnisse liefern. Da Null-Bytes in XML allerdings offiziell nicht gültig sind, sollte das kein Problem sein.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    Niko Ortner schrieb:

    @Nils_Kr
    Ich würde es dabei nicht belassen. Das war eher als Test gedacht.


    Das Ganze ist sowieso nur ein Test. Aquasuite nutzt HWInfo für einen Teil der Daten, daher muss man beide Programme laufen haben. HWInfo kann alle relevanten Daten im Alleingang erfassen und in dem Ram schreiben.
    Allerdings ist dieses Interface nicht frei verfügbar, sondern man muss sein Projekt dem Entwickler vorstellen, um die Dokumentation zu erhalten. Daher baue ich gerade einen Prototypen, um etwas zeigen zu können.

    Von daher ist deine Testklasse fürs Erste vollkommen ausreichend, danke nochmal.
    Option strict = on

    If it's stupid and it works it ain't stupid.