Maschinendaten auswerten

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 2 Antworten in diesem Thema. Der letzte Beitrag () ist von Haudruferzappeltnoch.

    Maschinendaten auswerten

    Hallo,

    (es geht eigentlich nicht um den Datenbankteil, aber da ich in dem Projekt ein DataSet verwende habe ich das Thema hierhin gepackt.)

    Ich hab hier ein Programm, das an sich funktioniert, bei dem ich mich aber recht abgemüht hab, und auch arg viel rechnen muss. Daher hoffe ich, dass ihr mir noch ein wenig Ratschläge dazu geben könntet.

    Das Programm verarbeitet Statusdaten einer automatisierten Maschine mit vier Stationen. Jede Station hat zwei Stati SubStatus und LEDStatus.
    Die Änderung eines Status verursacht einen Eintrag in der Datenquelle mit Zeitstempel, den Stati, und der Station (Das macht das Programm nicht, es liest diese nur aus)
    In dem Projekt habe ich damit ihr auch Beispielquelldaten habt (falls ihr die denn braucht), als Datenquelle eine csv genommen (aufbereitet.csv). (Diese Version läuft auch nur mit dem Start Button. Das Load-Event ist nur für den "Normalbetrieb")

    Was das Programm ermitteln soll ist die Dauer, die die Maschine nicht gelaufen ist.
    Wann die Maschine nicht läuft ist erstmal Definitionssache.
    Momentan ist es so definiert: Da jede Statusänderung erfasst wird, und Statusänderungen während des Maschinenablaufs erfolgen, soll keine Statusänderung von Minute x bis Minute x+1 eine Minute Stillstand bedeuten.

    Daher habe ich erstmal eine Tabelle dtAvgMinute erzeugt, die die Minuten des Tages denn überhaupt erstmal darstellt (Quelldaten sind ja nur Zeitpunkte):

    VB.NET-Quellcode

    1. For i = -1 To 1439
    2. DS1.dtAvgMinute.AdddtAvgMinuteRow(datum.AddMinutes(-i), -99, -99, id)
    3. Next i

    id ist die jeweilige Stationsnr der Maschine. Und berechne für die Quelldaten, die noch eine Dummy-Spalte Dauer beinhalten die Dauer, die zwischen zwei Statusänderungen lag.

    Die AverageData Methode soll dann aus der Dauer und den Stati, einen Durchschnittsstatus bilden für eine jeweilige Tagesminute einer Station.
    Also Beispiel (Eintrag um 12:00:00 Status 3, um 12:00:09 Status 0, um 12:01:10 Status 3) ergibt für die ganze Minute 12:00 den Durschnittlichen Status 0.45
    Diese komischen Durchschnittswerte haben den Vorteil, dass sie verraten ob in der Minute etwas passiert ist, wenn sich die Stati nämlich ändern kommen wilde Nachkommastellen dabei rum. Nur bei Stillstand gibt es schöne Ganzzahlen.

    In der EvaluateStationStop definiere ich genau das. Ganzzahlergebnis heißt die Station hat gestoppt. Da die Maschine aus vier Stationen besteht, die aufeinander auch warten müssen, heißt ein Stop einer Station also noch lange nicht ein Stop der ganzen Maschine. Daher fülle ich hier eine Tabelle dtStopMinuten mit den Stationen, die zu den jeweiligen Tagesminuten gestoppt waren.
    Dann werden nur noch die Minuten gezählt wo alle Stationen gestoppt sind.

    VB.NET-Quellcode

    1. Dim filterStop = From t In DS1.dtStopMinuten Where t.Station1 AndAlso t.Station2 AndAlso t.Station3 AndAlso t.Station4


    Als letzte Abwandlung werden die Stopminuten schichtweise ermittelt, denn die Maschine hat ja auch Stopzeiten wenn theoretisch gar keine Produktion vorgesehen ist.

    Das Form ist übrigens nur zum Reingucken und Tüfteln gewesen. Eine Oberfläche braucht es eigentlich nicht. Es spuckt hintendrein das Ergebnis auch als csv wieder aus. Für die Beispiel-Quelldaten ergeben sich 94, 97, 52 Stop Minuten
    Tja also wie gesagt an sich tuts schon, aber es sind natürlich auch offensichtliche Lücken drin. Eine StopMinute zählt z.b. nur wenn sie von 12:00:00 bis 12:01:00 geht, nicht aber wenn sie von 12:00:30 bis 12:01:30 geht, was recht willkürlich ist, aufgrund der Ungenauigkeit der ganzen Angelegenheit (Definition Stillstand) allerdings nicht wirklich ins Gewicht fällt. Ich hätte aber Interesse dran noch ein wenig zu basteln. Allerdings weiß ich nich wo ich es anpacken sollte.

    Viele Grüße

    PS: Der Code auch hier nochmal:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Public Class Form1
    3. Private datum As DateTime
    4. Private StundenTagesAnfang As Single = 22.0 '22:00 fängt Nachtschicht an
    5. Private csv1 As New List(Of Csvline)
    6. Private Function GetToDo() As List(Of DateTime)
    7. Dim hold As New List(Of DateTime)
    8. Try
    9. Dim holddatum = DateTime.Parse(File.ReadAllText("StopInsert.txt")).AddDays(1)
    10. While holddatum < DateTime.Today.AddDays(-1).AddHours(StundenTagesAnfang)
    11. hold.Add(holddatum)
    12. holddatum = holddatum.AddDays(1)
    13. End While
    14. hold.Add(DateTime.Today.AddDays(-1).AddHours(StundenTagesAnfang))
    15. Return hold
    16. Catch ex As Exception
    17. File.WriteAllText("log.txt", ex.ToString)
    18. hold.Add(DateTime.Today.AddDays(-1).AddHours(StundenTagesAnfang))
    19. Return hold
    20. End Try
    21. End Function
    22. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    23. If Not Boolean.Parse(File.ReadAllText("AutoAufruf.txt")) Then
    24. Exit Sub
    25. End If
    26. Dim ToDo = GetToDo()
    27. For Each element In ToDo
    28. datum = element
    29. GetData()
    30. For id As Byte = 1 To 4
    31. AverageData(id)
    32. Next
    33. EvaluateStationStop()
    34. EvaluateDailyResult()
    35. Next
    36. Application.Exit()
    37. End Sub
    38. Private Sub btStart_Click(sender As Object, e As EventArgs) Handles btStart.Click
    39. 'datum = DateTime.Today.AddDays(-1).AddHours(StundenTagesAnfang) 'Sonst für tagesaktuellen Gebrauch
    40. datum = DateTime.Parse("2022-04-14T22:00:00")
    41. GetData()
    42. For id As Byte = 1 To 4
    43. AverageData(id)
    44. Next
    45. EvaluateStationStop()
    46. EvaluateDailyResult()
    47. End Sub
    48. Private Sub GetData()
    49. Dim hold As String() = File.ReadAllLines("aufbereitet.csv")
    50. For i = 1 To hold.Count - 1
    51. csv1.Add(New Csvline(hold(i).Split(";"c)))
    52. Next
    53. dgvStation.DataSource = DtAufbereitetBindingSource
    54. DtAufbereitetBindingSource.DataSource = csv1
    55. DtAufbereitetBindingSource.ResetBindings(False)
    56. 'Sonst mit Sql Datenquelle da.Fill(DS1.dtAufbereitet)
    57. Dim bis As DateTime
    58. Dim von As DateTime
    59. DS1.dtAvgMinute.Clear()
    60. Dim linquery As List(Of Csvline)
    61. Dim id As Byte
    62. For n As Byte = 1 To 4
    63. id = n
    64. For i = -1 To 1439
    65. DS1.dtAvgMinute.AdddtAvgMinuteRow(datum.AddMinutes(-i), -99, -99, id)
    66. Next i
    67. linquery = (From t In csv1 Where t.StationsID = id Order By t.Zeitstempel Descending).ToList
    68. If linquery.Count = 0 Then
    69. Continue For
    70. End If
    71. von = linquery(0).Zeitstempel
    72. 'erster Eintrag default
    73. Dim diff = TimeSpan.FromSeconds(99999) : linquery(0).Dauer = diff.TotalSeconds
    74. 'andere Einträge
    75. For i = 1 To linquery.Count - 1
    76. bis = linquery(i - 1).Zeitstempel
    77. von = linquery(i).Zeitstempel
    78. diff = bis - von
    79. linquery(i).Dauer = diff.TotalSeconds
    80. Next
    81. Next
    82. End Sub
    83. Private Sub AverageData(ID As Byte)
    84. Dim k As Integer = 0
    85. Dim avgSub As Double
    86. Dim avgLED As Double
    87. Dim linqAvgMinute = (From t In DS1.dtAvgMinute Where t.StationsID = ID Order By t.Zeit Descending).ToList
    88. Dim linqAufbereitet = (From t In csv1 Where t.StationsID = ID Order By t.Zeitstempel Descending).ToList
    89. Dim AktDatenzeile As Csvline
    90. Dim AktEvalzeile As DataSet1.dtAvgMinuteRow
    91. Dim NextEvalzeile As DataSet1.dtAvgMinuteRow
    92. For i = 0 To linqAvgMinute.Count - 2
    93. avgSub = 0
    94. avgLED = 0
    95. AktEvalzeile = linqAvgMinute(i)
    96. NextEvalzeile = linqAvgMinute(i + 1)
    97. For j = k To linqAufbereitet.Count - 1
    98. AktDatenzeile = linqAufbereitet(j)
    99. If AktDatenzeile.Zeitstempel >= NextEvalzeile.Zeit Then
    100. If AktDatenzeile.Zeitstempel >= AktEvalzeile.Zeit Then
    101. 'MessageBox.Show($"Berechnung frühzeitig beendet: Letzter Datenpunkt {AktDatenzeile.Zeit.ToString}")
    102. Exit Sub
    103. Else
    104. If AktDatenzeile.Zeitstempel.AddSeconds(AktDatenzeile.Dauer) >= AktEvalzeile.Zeit Then
    105. avgSub += (AktEvalzeile.Zeit - AktDatenzeile.Zeitstempel).TotalSeconds * AktDatenzeile.SubStatus / 60
    106. avgLED += (AktEvalzeile.Zeit - AktDatenzeile.Zeitstempel).TotalSeconds * AktDatenzeile.LEDStatus / 60
    107. Else
    108. avgSub += AktDatenzeile.SubStatus * AktDatenzeile.Dauer / 60
    109. avgLED += AktDatenzeile.LEDStatus * AktDatenzeile.Dauer / 60
    110. End If
    111. End If
    112. Else
    113. If AktDatenzeile.Zeitstempel.AddSeconds(AktDatenzeile.Dauer) >= AktEvalzeile.Zeit.AddMinutes(1) Then
    114. AktEvalzeile.SubStatus = AktDatenzeile.SubStatus
    115. AktEvalzeile.LEDStatus = AktDatenzeile.LEDStatus
    116. k = j
    117. Exit For
    118. ElseIf AktDatenzeile.Zeitstempel.AddSeconds(AktDatenzeile.Dauer) >= AktEvalzeile.Zeit Then
    119. k = j
    120. Exit For
    121. Else
    122. If AktDatenzeile.Zeitstempel.AddSeconds(AktDatenzeile.Dauer) >= NextEvalzeile.Zeit Then
    123. avgSub += (AktDatenzeile.Zeitstempel.AddSeconds(AktDatenzeile.Dauer) - NextEvalzeile.Zeit).TotalSeconds * AktDatenzeile.SubStatus / 60
    124. avgLED += (AktDatenzeile.Zeitstempel.AddSeconds(AktDatenzeile.Dauer) - NextEvalzeile.Zeit).TotalSeconds * AktDatenzeile.LEDStatus / 60
    125. k = j
    126. Exit For
    127. Else
    128. k = j
    129. Exit For
    130. MessageBox.Show($"Unmögliche Verarbeitung 2: Kontrolliere Daten {AktDatenzeile.Zeitstempel.ToString}")
    131. End If
    132. End If
    133. End If
    134. Next j
    135. NextEvalzeile.SubStatus = avgSub
    136. NextEvalzeile.LEDStatus = avgLED
    137. Next i
    138. End Sub
    139. Private Sub EvaluateStationStop()
    140. ''Filter auf Stop-Definitionen1:
    141. ''SubStatus = 8 oder -9 => Warte auf Material
    142. ''LEDStatus = 1 => Handbetrieb, normalerweise nur zum Anhalten genutzt
    143. ''LEDStatus = 4 => Not-Aus
    144. 'Dim filterStopzeit = From t In DS1.dtAvgMinute Where t.SubStatus = 8 OrElse t.SubStatus = -9 OrElse t.LEDStatus = 1 OrElse t.LEDStatus = 4 Order By t.Zeit Descending, t.StationsID Select t.Zeit, t.StationsID
    145. 'Filter auf Stop-Definitionen2:
    146. 'Stati bleiben unverändert
    147. Dim filterStopzeit = From t In DS1.dtAvgMinute Where t.SubStatus - Math.Floor(t.SubStatus) = 0 AndAlso t.LEDStatus - Math.Floor(t.LEDStatus) = 0 Order By t.Zeit Descending, t.StationsID Select t.Zeit, t.StationsID
    148. Dim recentlyAddedTime As DateTime = DateTime.Now.AddDays(2) 'Irgendein Datum in der Zukunft, wird dann garantiert überschrieben
    149. Dim update As DataSet1.dtStopMinutenRow
    150. For Each element In filterStopzeit
    151. If recentlyAddedTime = element.Zeit Then
    152. update = (From t In DS1.dtStopMinuten Where t.Zeit = recentlyAddedTime)(0)
    153. Select Case element.StationsID
    154. Case 1
    155. update.Station1 = True
    156. Case 2
    157. update.Station2 = True
    158. Case 3
    159. update.Station3 = True
    160. Case 4
    161. update.Station4 = True
    162. End Select
    163. Else
    164. recentlyAddedTime = element.Zeit
    165. Select Case element.StationsID
    166. Case 1
    167. DS1.dtStopMinuten.AdddtStopMinutenRow(recentlyAddedTime, True, False, False, False)
    168. Case 2
    169. DS1.dtStopMinuten.AdddtStopMinutenRow(recentlyAddedTime, False, True, False, False)
    170. Case 3
    171. DS1.dtStopMinuten.AdddtStopMinutenRow(recentlyAddedTime, False, False, True, False)
    172. Case 4
    173. DS1.dtStopMinuten.AdddtStopMinutenRow(recentlyAddedTime, False, False, False, True)
    174. End Select
    175. End If
    176. Next
    177. 'Dim test = New List(Of DataSet1.dtStopMinutenRow)
    178. 'For Each element In DS1.dtStopMinuten
    179. ' test.Add(element)
    180. 'Next
    181. ''Ausgabe csv
    182. 'For Each element In DS1.dtStopMinuten
    183. ' File.AppendAllText("ausgabe2.csv", $"{element.Zeit.ToString};{element.Station1.ToString};{element.Station2.ToString};{element.Station3.ToString};{element.Station4.ToString}{vbLf}")
    184. 'Next
    185. End Sub
    186. Private Sub EvaluateDailyResult()
    187. If DateTime.Parse(File.ReadAllText("StopInsert.txt")) >= DateTime.Now.AddDays(-1) Then
    188. Exit Sub
    189. End If
    190. Dim Gesquery, Pausequery As EnumerableRowCollection(Of DataSet1.dtStopMinutenRow)
    191. Dim sys As New SchichtSys(datum)
    192. Dim ZeitSumme As Integer
    193. Dim PauseSumme As Integer
    194. Dim filterStop = From t In DS1.dtStopMinuten Where t.Station1 AndAlso t.Station2 AndAlso t.Station3 AndAlso t.Station4
    195. Dim csv2 As New List(Of String)
    196. For Each schicht As SchichtSys.Schicht In System.Enum.GetValues(GetType(SchichtSys.Schicht))
    197. sys.SetSchicht(schicht)
    198. Gesquery = From t In filterStop Where t.Zeit >= sys.Schichtbeginn AndAlso t.Zeit < sys.Schichtende
    199. Pausequery = From t In filterStop Where t.Zeit >= sys.Pausenbeginn AndAlso t.Zeit < sys.Pausenende
    200. ZeitSumme = Gesquery.Count
    201. PauseSumme = Pausequery.Count
    202. ''hier sonst auch Sql Insert
    203. csv2.Add($"{datum.AddHours(-StundenTagesAnfang).ToString("yyyy-MM-ddTHH:mm:ss")};{sys.ChoiceToString},{ZeitSumme};{PauseSumme})")
    204. Next
    205. File.WriteAllLines("ergebnis.csv", csv2)
    206. File.WriteAllText("StopInsert.txt", datum.ToString)
    207. End Sub
    208. End Class
    209. Friend Class Csvline
    210. Property Zeitstempel As DateTime
    211. Property Dauer As Double
    212. Property SubStatus As Short
    213. Property Integral As Double
    214. Property LEDStatus As Short
    215. Property StationsID As Byte
    216. Sub New(line As String())
    217. Zeitstempel = DateTime.Parse(line(0))
    218. Dauer = Double.Parse(line(1))
    219. SubStatus = Short.Parse(line(2))
    220. Integral = Double.Parse(line(3))
    221. LEDStatus = Short.Parse(line(4))
    222. StationsID = Byte.Parse(line(5))
    223. End Sub
    224. End Class

    Dateien

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

    man könnte auch ganz anders denken:
    man könnte alle Zeitpunkte aller Status-Änderungen aller Stationen in eine Liste schmeissen.
    Dann schiebt man ein "Zeitfenster" von mw. 120s Breite sekundenweise durch die Liste, und erhält so pro Sekunde die Anzahl von Status-Änderungen in den nächsten zwei Minuten.
    Sowas könnte man in einem Diagramm darstellen, und die Kurve die "Aktivität" der Maschine nennen.

    Anders gesagt: Die "Aktivität" der Maschine zu einem beliebigen Zeitpunkt scheint mir sinnvoll definiert als die Anzahl StatusÄnderungen pro Minute zu diesem Zeitpunkt.