.net core使用时序数据库InfluxDB2.0及以上版本

文章目录

  • 前言
  • 一、InfluxDB时序数据库是什么?
  • 二、可视化软件使用
  • 三、代码操作
    • 1.引入库
    • 2.创建所需的实体
    • 3.连接
    • 4.数据写入
    • 5.数据读取
  • 总结


前言

时序数据库针对的业务场景并没有关系数据库广,生态相对不是那么好。对我们.net core来说更少了 网上搜关于InfluxDB的资料有是有,但是大都是2.0以下的,很坑的是现在新版本都不支持SQL语法了连接方式也是加了Token,网上的资料大都废了,搞的我钻研了挺久。

下面我只介绍代码写法和软件用法就不说安装了 安装比较简单。


一、InfluxDB时序数据库是什么?

基本概念
一、与传统数据库中的名词做比较

.net core使用时序数据库InfluxDB2.0及以上版本_第1张图片

二、InfluxDB中独有的概念
Point
Point由时间戳(time)、数据(field)、标签(tags)组成。
Point相当于传统数据库里的一行数据,如下表所示:
.net core使用时序数据库InfluxDB2.0及以上版本_第2张图片
二、数据模型建议
需要用来检索的字段设为Tags

二、可视化软件使用

一、服务端启后,访问127.0.0.1:8086(第一次访问需要创建账号等)
.net core使用时序数据库InfluxDB2.0及以上版本_第3张图片
二、依次点Data->Buckets->Create Bucket(创建数据库)
.net core使用时序数据库InfluxDB2.0及以上版本_第4张图片
三、获取鉴权Token连接的时候使用(注意设置读写权限)
.net core使用时序数据库InfluxDB2.0及以上版本_第5张图片

三、代码操作

1.引入库

NuGet安装:InfluxDB.Client、InfluxDB.Client.Core
.net core使用时序数据库InfluxDB2.0及以上版本_第6张图片

2.创建所需的实体

代码如下(示例):

 public interface IDataStorage
    {
        public Guid EquipId { get; set; }

        public string KeyName { get; set; }

        public DateTime DateTime { get; set; }

        public DataType Type { get; set; }

        public bool? Value_Boolean { get; set; }
        public string Value_String { get; set; }
        public long? Value_Long { get; set; }
        public DateTime? Value_DateTime { get; set; }
        public double? Value_Double { get; set; }
        public string Value_Json { get; set; }
        public string Value_XML { get; set; }
        public byte[] Value_Binary { get; set; }
    }
    
    /// 
    /// 数据保存模型
    /// 
    public class TelemetryData: IDataStorage
    {
        public Guid EquipId { get; set; }

        public string KeyName { get; set; }
        public DateTime DateTime { get; set; }
        public DataType Type { get; set; }
        public bool? Value_Boolean { get; set; }
        public string Value_String { get; set; }
        public long? Value_Long { get; set; }
        public DateTime? Value_DateTime { get; set; }
        public double? Value_Double { get; set; }
        public string Value_Json { get; set; }
        public string Value_XML { get; set; }
        public byte[] Value_Binary { get; set; }
    }
    /// 
    /// 输入数据模型
    /// 
   public class PlayloadInput
    {
        /// 
        /// 时序时间
        /// 
        public DateTime ts { get; set; } = DateTime.Now;
        /// 
        ///装置ID
        /// 
        public Guid EquipId { get; set; }
        /// 
        /// 数据内容
        /// 
        public Dictionary<string, object> MsgBody { get; set; }
    }
    /// 
    /// 返回数据模型
    /// 
    public class PlayloadOutput
    {
        public string KeyName { get; set; }

        public DateTime DateTime { get; set; }
        public DataType DataType { get; set; }
        public object Value { get; set; }
    }  
    //数据类型
    public enum DataType
    {
        Boolean,
        String,
        Long,
        Double,
        Json,
        XML,
        Binary,
        DateTime
    }
    //聚合函数
    public enum Aggregate
    {
        /// 
        /// 不使用
        /// 
        None,
        /// 
        /// 平均数
        /// 
        Mean,
        /// 
        /// 中值
        /// 
        Median,
        /// 
        /// 最后一个值
        /// 
        Last,
        /// 
        /// 第一个值
        /// 
        First,
        /// 
        /// 最大
        /// 
        Max,
        /// 
        /// 最小
        /// 
        Min,
        /// 
        /// 合计
        /// 
        Sum,
        /// 
        /// 条数
        /// 
        Count

    }

3.连接

粘贴从可视化软件复制的Token

   private const string cfg = "http://192.168.1.163:8086/?org=fjzn&bucket=fjzn&token=ozt7aE6xGIuEdZX6hbyLtV7n461KSKAbHnPMuPgvrDIcH5pRxfKAvZtR_c0wFCmRcDsoP2qRMNefbHMAMzeyvA==&&latest=-72h";

4.数据写入

       public async Task<bool> StoreTelemetryAsync(PlayloadInput msg)
        {
            bool result = false;
            try
            {
                List<PointData> lst = new List<PointData>();
                msg.MsgBody.ToList().ForEach(kp =>
                {
                    if (kp.Value != null)
                    {
                        TelemetryData tdata = new TelemetryData() { DateTime = msg.ts, EquipId = msg.EquipId, KeyName = kp.Key, Value_DateTime = new DateTime(1970, 1, 1) };
                        tdata.CheckType(kp);
                        var point = PointData.Measurement(nameof(TelemetryData))
                                    .Tag("EquipId", tdata.EquipId.ToString());
                        switch (tdata.Type)
                        {
                            case DataType.Boolean:
                                if (tdata.Value_Boolean.HasValue) point = point.Field(tdata.KeyName, tdata.Value_Boolean.Value);
                                break;
                            case DataType.String:
                                point = point.Field(tdata.KeyName, tdata.Value_String);
                                break;
                            case DataType.Long:
                                if (tdata.Value_Long.HasValue) point = point.Field(tdata.KeyName, tdata.Value_Long.Value);
                                break;
                            case DataType.Double:
                                if (tdata.Value_Double.HasValue) point = point.Field(tdata.KeyName, tdata.Value_Double.Value);
                                break;
                            case DataType.Json:
                                point = point.Field(tdata.KeyName, tdata.Value_Json);
                                break;
                            case DataType.XML:
                                point = point.Field(tdata.KeyName, tdata.Value_XML);
                                break;
                            case DataType.Binary:
                                point = point.Field(tdata.KeyName, Hex.ToHexString(tdata.Value_Binary));
                                break;
                            case DataType.DateTime:
                                point = point.Field(tdata.KeyName, tdata.Value_DateTime.GetValueOrDefault().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalMilliseconds);
                                break;
                            default:
                                break;
                        }
                        if (point.HasFields())
                        {
                            point = point.Timestamp(msg.ts.ToUniversalTime(), WritePrecision.Ns);
                            lst.Add(point);
                        }
                    }
                });

                using InfluxDBClient influx = InfluxDBClientFactory.Create(cfg);             
                var writeApi = influx.GetWriteApiAsync();
                //await writeApi.WritePointsAsync(lst, bucket: _bucket, org: _org);//指定组织和Db
                await writeApi.WritePointsAsync(lst);
                Console.WriteLine($"数据入库完成,共数据{lst.Count}条");   
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString(), $"{msg.EquipId}数据处理失败{ex.Message} {ex.InnerException?.Message} ");
            }
            return result;
        }
   public static class DataExtension
    {
        /// 
        /// 数据类型判断
        /// 
        /// 
        /// 
        /// 
        internal static void CheckType<T>(this T tdata, KeyValuePair<string, object> kp) where T : IDataStorage
        {
            var tc = Type.GetTypeCode(kp.Value.GetType());

            switch (tc)
            {
                case TypeCode.Boolean:
                    tdata.Type = DataType.Boolean;
                    tdata.Value_Boolean = (bool)kp.Value;
                    break;
                case TypeCode.Single:
                    tdata.Type = DataType.Double;
                    tdata.Value_Double = double.Parse(kp.Value.ToString(), System.Globalization.NumberStyles.Float);
                    break;
                case TypeCode.Double:
                case TypeCode.Decimal:
                    tdata.Type = DataType.Double;
                    tdata.Value_Double = (double)kp.Value;
                    break;

                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                case TypeCode.Byte:
                case TypeCode.SByte:
                    tdata.Type = DataType.Long;
                    tdata.Value_Long = (Int64)Convert.ChangeType(kp.Value, TypeCode.Int64);
                    break;
                case TypeCode.String:
                case TypeCode.Char:
                    tdata.Type = DataType.String;
                    tdata.Value_String = (string)kp.Value;
                    break;
                case TypeCode.DateTime:
                    tdata.Type = DataType.DateTime;
                    tdata.Value_DateTime = ((DateTime)kp.Value);
                    break;
                case TypeCode.DBNull:
                case TypeCode.Empty:
                    break;
                case TypeCode.Object:
                default:
                    if (kp.Value.GetType() == typeof(byte[]))
                    {
                        tdata.Type = DataType.Binary;
                        tdata.Value_Binary = (byte[])kp.Value;
                    }
                    else if (kp.Value.GetType() == typeof(System.Xml.XmlDocument))
                    {
                        tdata.Type = DataType.XML;
                        tdata.Value_XML = ((System.Xml.XmlDocument)kp.Value).InnerXml;
                    }
                    else if (kp.Value.GetType() == typeof(System.Text.Json.JsonElement))
                    {
                        var kvx = kp.Value as System.Text.Json.JsonElement?;
                        if (kvx.HasValue)
                        {
                            switch (kvx.Value.ValueKind)
                            {
                                case System.Text.Json.JsonValueKind.Undefined:
                                case System.Text.Json.JsonValueKind.Object:
                                    break;
                                case System.Text.Json.JsonValueKind.Array:
                                    break;
                                case System.Text.Json.JsonValueKind.String:
                                    tdata.Type = DataType.String;
                                    tdata.Value_String = kvx.Value.GetString();
                                    break;
                                case System.Text.Json.JsonValueKind.Number:
                                    tdata.Type = DataType.Double;
                                    tdata.Value_Double = kvx.Value.GetDouble();
                                    break;
                                case System.Text.Json.JsonValueKind.True:
                                case System.Text.Json.JsonValueKind.False:
                                    tdata.Type = DataType.Boolean;
                                    tdata.Value_Boolean = kvx.Value.GetBoolean();
                                    break;
                                case System.Text.Json.JsonValueKind.Null:
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                    else
                    {
                        tdata.Type = DataType.Json;
                        tdata.Value_Json = Newtonsoft.Json.JsonConvert.SerializeObject(kp.Value);
                    }
                    break;
            }
        }
    }
 

1.调用测试

  Dictionary<string, object> keys = new Dictionary<string, object>();
                keys.Add("TimeStamp", i);
                keys.Add("EqCode", i);
                keys.Add("StartTime", i);
                keys.Add("EndTime", i);
                keys.Add("KeepSeconds", i);
                keys.Add("KeepWarCount", i);
                keys.Add("WarName", i);
                keys.Add("WarLevel", i);
                keys.Add("WarType", i);
                keys.Add("WarRmk", i);
                keys.Add("Value", i);
                keys.Add("Threshold", i);
                ///写入测试
                var rlt = await influx.StoreTelemetryAsync(new PlayloadInput()
                {
                    EquipId = new Guid("01735c40-8a67-48a2-86cd-0839990bede8"),
                    MsgBody = keys
                });

查看结果
.net core使用时序数据库InfluxDB2.0及以上版本_第7张图片
.net core使用时序数据库InfluxDB2.0及以上版本_第8张图片

5.数据读取

//指定Tags
public async Task<List<TelemetryDataDto>> GetTelemetryLatest(Guid equipId)
        {
            using InfluxDBClient influx = InfluxDBClientFactory.Create(cfg);
            var query = influx.GetQueryApi();
           var exp = @$"	
from(bucket: ""{_bucket}"")
|> range(start: {_latest})
  |> filter(fn: (r) => r[""_measurement""] == ""{nameof(TelemetryData)}"")
  |> filter(fn: (r) => r[""EquipId""] == ""{equipId}"")
  |> last()";
            var v = query.QueryAsync(exp);
            return FluxToDtoAsync(v);
        }
public Task<List<PlayloadOutput>> GetTelemetryLatest(Guid equipId, List<string> keys)
        {
           using InfluxDBClient influx = InfluxDBClientFactory.Create(cfg);
            var query = influx.GetQueryApi();
            var kvs = from k in keys
                      select $"r[\"_field\"] == \"{k}\"";
            string exp = @$"	
from(bucket: ""{_bucket}"")
|> range(start: {_latest})
|> filter(fn: (r) => r[""_measurement""] == ""{nameof(TelemetryData)}"")
|> filter(fn: (r) => r[""EquipId""] == ""{equipId}"")
|> filter(fn: (r) => {string.Join(" or ", kvs)})
|> group(columns: [""_field""])
|> last()";
            var v = query.QueryAsync(exp);
            return FluxToDtoAsync(v);
        }  
 public Task<List<PlayloadOutput>> LoadTelemetryAsync(Guid equipId, List<string> keys, DateTime begin, DateTime end, TimeSpan every, Aggregate aggregate)
        {
using InfluxDBClient influx = InfluxDBClientFactory.Create(cfg);
            var query = influx.GetQueryApi();
            var sb = new StringBuilder();
            sb.AppendLine(@$"from(bucket: ""{_bucket}"")");
            sb.AppendLine($"|> range(start: {begin:o},stop:{end:o})");
            sb.AppendLine(@$"|> filter(fn: (r) => r[""_measurement""] == ""{nameof(TelemetryData)}"")");
            sb.AppendLine(@$"|> filter(fn: (r) => r[""EquipId""] == ""{equipId}"")");
            if (keys.Count>0)
            {
                var kvs = from k in keys
                          select $"r[\"_field\"] == \"{k}\"";
                sb.AppendLine(@$"|> filter(fn: (r) => {string.Join(" or ", kvs)})");            
            }
          
            if (every > TimeSpan.Zero&& aggregate != Aggregate.Count)
            {
                sb.AppendLine(@$"|> group(columns: [""_field""])");
                sb.AppendLine($@"|> aggregateWindow(every: {(long)every.TotalMilliseconds}ms, fn: {Enum.GetName(aggregate).ToLower()}, createEmpty: false)");
                sb.AppendLine(@$"|> yield(name: ""{Enum.GetName(aggregate).ToLower()}"")");
            }
            else if (aggregate == Aggregate.Count)//统计个数不需要间隔时间 所以单独处理
            {
                sb.AppendLine(@$"|> count(column:""_value"")");
                sb.AppendLine(@$"|> yield()");
            }
            else
            {
                sb.AppendLine(@$"|> yield()");
            }
            Console.WriteLine(sb.ToString());
            var v =  query.QueryAsync(sb.ToString(), _org);
            return FluxToDtoAsync(v);
        }
#region 私有的
private async Task<List<PlayloadOutput>> FluxToDtoAsync(Task<List<FluxTable>> v)
   {
            List<PlayloadOutput> dt = new List<PlayloadOutput>();
            (await v)?.ForEach(ft =>
            {
                ft.Records.ForEach(fr =>
                {
                    dt.Add(new PlayloadOutput()
                    {
                        KeyName = fr.GetField(),
                        DateTime = fr.GetTimeInDateTime().GetValueOrDefault(DateTime.MinValue).ToLocalTime(),
                        Value = fr.GetValue(),
                        DataType = InfluxType(ft.Columns.Find(fv => fv.Label == "_value")?.DataType)
                    });
                });
            });
            return dt;
}
DataType InfluxType(string itype)
        {
            DataType data = DataType.String;
            switch (itype)
            {
                case "long":
                    data = DataType.Long;
                    break;
                case "double":
                    data = DataType.Double;
                    break;
                case "boolean":
                case "bool":
                    data = DataType.Boolean;
                    break;
                case "dateTime:RFC3339":
                    data = DataType.DateTime;
                    break;
                case "string":
                default:
                    data = DataType.String;
                    break;
            }
            return data;
        }
        #endregion

//扩展

//值判断过滤
from(bucket: "fjzn")
  |> range(start:-72h)
  |> filter(fn: (r) => r["_measurement"] == "TelemetryData")
  |> filter(fn: (r) => r["EquipId"] == "01735c40-8a67-48a2-86cd-0839990bede7")
  |> filter(fn: (r) => (r["_field"] == "EndTime" and r._value>37) or r["_field"] == "EqCode")
from(bucket: "fjzn")
  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
  |> filter(fn: (r) => r["_measurement"] == "TelemetryData")
  |> unique(column:"EquipId")
  |> drop(columns:["EquipId"])
  |> aggregateWindow(every: 5m, fn: sum, createEmpty: false)
  |> yield(name: "sum")

总结

就写到这吧没空写了,要是有人有什么问题就评论一下,如果还有国产数据库TDengine(Todelin)的需求,我也可以写一下,为.net core生态贡献自己的绵薄之力。参考IoTSharp(开源地址:https://gitee.com/dotnetchina/IoTSharp)

你可能感兴趣的:(InfluxDB2.0,InfluxDB,.netcore,时序数据库,c#,database)