TimescaleWriter.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading.Tasks;
  4. using NLog;
  5. using Npgsql;
  6. using NpgsqlTypes;
  7. using OpenHardwareMonitor.Hardware;
  8. namespace OhmGraphite
  9. {
  10. class TimescaleWriter : IWriteMetrics
  11. {
  12. private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
  13. private readonly string _connStr;
  14. private readonly string _localHost;
  15. private NpgsqlConnection _conn;
  16. private bool _failure = true;
  17. public TimescaleWriter(string connStr, string localHost)
  18. {
  19. _connStr = connStr;
  20. _localHost = localHost;
  21. _conn = new NpgsqlConnection(_connStr);
  22. }
  23. public async Task ReportMetrics(DateTime reportTime, IEnumerable<ReportedValue> sensors)
  24. {
  25. try
  26. {
  27. if (_failure)
  28. {
  29. _conn.Close();
  30. _conn = new NpgsqlConnection(_connStr);
  31. Logger.Debug("New timescale connection");
  32. await _conn.OpenAsync();
  33. using (var cmd = new NpgsqlCommand(
  34. "CREATE TABLE IF NOT EXISTS ohm_stats (" +
  35. " time TIMESTAMPTZ NOT NULL," +
  36. " host TEXT,hardware TEXT," +
  37. " hardware_type TEXT," +
  38. " identifier TEXT," +
  39. " sensor TEXT," +
  40. " sensor_type TEXT," +
  41. " sensor_index INT," +
  42. " value REAL" +
  43. ");" +
  44. "" +
  45. @"SELECT create_hypertable('ohm_stats', 'time', if_not_exists => TRUE);" +
  46. "CREATE INDEX IF NOT EXISTS idx_ohm_host ON ohm_stats (host);" +
  47. "CREATE INDEX IF NOT EXISTS idx_ohm_identifier ON ohm_stats (identifier);",
  48. _conn))
  49. {
  50. await cmd.ExecuteNonQueryAsync();
  51. }
  52. }
  53. using (var cmd = new NpgsqlCommand(
  54. "INSERT INTO ohm_stats " +
  55. "(time, host, hardware, hardware_type, identifier, sensor, sensor_index, value) VALUES " +
  56. "(@time, @host, @hardware, @hardware_type, @identifier, @sensor, @sensor_index, @value)",
  57. _conn))
  58. {
  59. // Note that all parameters must be set before calling Prepare()
  60. // they are part of the information transmitted to PostgreSQL
  61. // and used to effectively plan the statement. You must also set
  62. // the DbType or NpgsqlDbType on your parameters to unambiguously
  63. // specify the data type (setting the value isn't support)
  64. cmd.Parameters.Add("time", NpgsqlDbType.TimestampTz);
  65. cmd.Parameters.Add("host", NpgsqlDbType.Text);
  66. cmd.Parameters.Add("hardware", NpgsqlDbType.Text);
  67. cmd.Parameters.Add("hardware_type", NpgsqlDbType.Text);
  68. cmd.Parameters.Add("identifier", NpgsqlDbType.Text);
  69. cmd.Parameters.Add("sensor", NpgsqlDbType.Text);
  70. cmd.Parameters.Add("sensor_type", NpgsqlDbType.Text);
  71. cmd.Parameters.Add("value", NpgsqlDbType.Real);
  72. cmd.Parameters.Add("sensor_index", NpgsqlDbType.Integer);
  73. await cmd.PrepareAsync();
  74. foreach (var sensor in sensors)
  75. {
  76. cmd.Parameters["time"].Value = reportTime;
  77. cmd.Parameters["host"].Value = _localHost;
  78. cmd.Parameters["hardware"].Value = sensor.Hardware;
  79. cmd.Parameters["hardware_type"].Value = Enum.GetName(typeof(HardwareType), sensor.HardwareType);
  80. cmd.Parameters["identifier"].Value = sensor.Identifier;
  81. cmd.Parameters["sensor"].Value = sensor.Sensor;
  82. cmd.Parameters["sensor_type"].Value = sensor.SensorType;
  83. cmd.Parameters["value"].Value = sensor.Value;
  84. cmd.Parameters["sensor_index"].Value = sensor.SensorIndex;
  85. await cmd.ExecuteNonQueryAsync();
  86. }
  87. }
  88. _failure = false;
  89. }
  90. catch (Exception)
  91. {
  92. _failure = true;
  93. throw;
  94. }
  95. }
  96. }
  97. }