first commit

This commit is contained in:
2026-08-02 23:33:31 +02:00
commit fd62224511
41 changed files with 2348 additions and 0 deletions
+210
View File
@@ -0,0 +1,210 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Text;
namespace BlubbFish.IoT.Pocsag.BosServerDatatypes.Influx {
public abstract class AInfluxMessage {
public String ToInfluxString() {
Type measurementType = this.GetType();
PropertyInfoColumn[] cache = measurementType.GetProperties().Select(property => new PropertyInfoColumn { Column = (Column)property.GetCustomAttribute(typeof(Column)), Property = property }).Where(propertyInfo => propertyInfo.Column != null).ToArray();
Measurement measurementAttribute = (Measurement)measurementType.GetCustomAttribute(typeof(Measurement));
PropertyInfoColumn measurementColumn = cache.SingleOrDefault(p => p.Column.IsMeasurement);
PointData point = new PointData(measurementAttribute.Name);
foreach(PropertyInfoColumn propertyInfo in cache) {
if(propertyInfo.Column.IsMeasurement) {
continue;
}
Object value = propertyInfo.Property.GetValue(this);
if(value == null) {
continue;
}
String name = propertyInfo.Column.Name;
if(propertyInfo.Column.IsTag && !String.IsNullOrEmpty(value.ToString())) {
point.Tag(name, value.ToString());
} else if(propertyInfo.Column.IsTimestamp) {
if(value is DateTime date) {
point.Timestamp(date);
}
} else {
point.PutField(name, value);
}
}
return point.ToLineProtocol();
}
}
internal class PropertyInfoColumn {
internal PropertyInfo Property;
internal Column Column;
}
public class PointData {
private static readonly DateTime EpochStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private readonly String _measurementName;
private ImmutableSortedDictionary<String, String> _tags = ImmutableSortedDictionary<String, String>.Empty;
private ImmutableSortedDictionary<String, Object> _fields = ImmutableSortedDictionary<String, Object>.Empty;
private BigInteger _time;
public PointData(String measurementName) => this._measurementName = measurementName;
internal void PutField(String name, Object value) => this._fields = this._fields.Add(name, value);
internal void Tag(String name, String value) => this._tags = this._tags.Add(name, value);
internal void Timestamp(DateTime timestamp) => this._time = timestamp.ToUniversalTime().Subtract(EpochStart).Ticks * 100;
internal String ToLineProtocol() {
StringBuilder sb = new StringBuilder();
this.EscapeKey(sb, this._measurementName, false);
this.AppendTags(sb);
if(!this.AppendFields(sb)) {
return "";
}
this.AppendTime(sb);
return sb.ToString();
}
private void AppendTags(StringBuilder sb) {
foreach(KeyValuePair<String, String> keyValue in this._tags) {
_ = sb.Append(',');
this.EscapeKey(sb, keyValue.Key);
_ = sb.Append('=');
this.EscapeKey(sb, keyValue.Value);
}
_ = sb.Append(' ');
}
private Boolean AppendFields(StringBuilder sb) {
Boolean appended = false;
foreach(KeyValuePair<String, Object> keyValue in this._fields) {
String key = keyValue.Key;
Object value = keyValue.Value;
if(this.IsNotDefined(value)) {
continue;
}
this.EscapeKey(sb, key);
_ = sb.Append('=');
if(value is Double || value is Single) {
_ = sb.Append(((IConvertible)value).ToString(CultureInfo.InvariantCulture));
} else if(value is UInt32 || value is UInt64 || value is UInt16) {
_ = sb.Append(((IConvertible)value).ToString(CultureInfo.InvariantCulture));
_ = sb.Append('u');
} else if(value is Byte || value is Int32 || value is Int64 || value is SByte || value is Int16) {
_ = sb.Append(((IConvertible)value).ToString(CultureInfo.InvariantCulture));
_ = sb.Append('i');
} else if(value is Boolean b) {
_ = sb.Append(b ? "true" : "false");
} else if(value is String s) {
_ = sb.Append('"');
this.EscapeValue(sb, s);
_ = sb.Append('"');
} else if(value is IConvertible c) {
_ = sb.Append(c.ToString(CultureInfo.InvariantCulture));
} else {
_ = sb.Append('"');
this.EscapeValue(sb, value.ToString());
_ = sb.Append('"');
}
_ = sb.Append(',');
appended = true;
}
if(appended) {
_ = sb.Remove(sb.Length - 1, 1);
}
return appended;
}
private void AppendTime(StringBuilder sb) {
_ = sb.Append(' ');
_ = sb.Append(this._time.ToString(CultureInfo.InvariantCulture));
}
private void EscapeKey(StringBuilder sb, String key, Boolean escapeEqual = true) {
foreach(Char c in key) {
switch(c) {
case '\n':
_ = sb.Append("\\n");
continue;
case '\r':
_ = sb.Append("\\r");
continue;
case '\t':
_ = sb.Append("\\t");
continue;
case ' ':
case ',':
_ = sb.Append("\\");
break;
case '=':
if(escapeEqual) {
_ = sb.Append("\\");
}
break;
}
_ = sb.Append(c);
}
}
private void EscapeValue(StringBuilder sb, String value) {
foreach(Char c in value) {
switch(c) {
case '\\':
case '\"':
_ = sb.Append("\\");
break;
}
_ = sb.Append(c);
}
}
private Boolean IsNotDefined(Object value) => value == null
|| value is Double d && (Double.IsInfinity(d) || Double.IsNaN(d))
|| value is Single f && (Single.IsInfinity(f) || Single.IsNaN(f));
}
[AttributeUsage(AttributeTargets.Class)]
public sealed class Measurement : Attribute {
public String Name {
get;
}
public Measurement(String name) => this.Name = name;
}
[AttributeUsage(AttributeTargets.Property)]
public sealed class Column : Attribute {
public String Name {
get;
}
public Boolean IsMeasurement {
get; set;
}
public Boolean IsTag {
get; set;
}
public Boolean IsTimestamp {
get; set;
}
public Column() {
}
public Column(String name) => this.Name = name;
}
}
+86
View File
@@ -0,0 +1,86 @@
using System;
using BlubbFish.IoT.Pocsag.BosServerDatatypes.Mqtt;
namespace BlubbFish.IoT.Pocsag.BosServerDatatypes.Influx {
[Measurement("bosmon")]
public class Bosmon : AInfluxMessage {
[Column("address", IsTag = true)]
public UInt64 Address {
get;
}
[Column("function", IsTag = true)]
public String Function {
get;
}
[Column("sendonline", IsTag = true)]
public Boolean SendOnline {
get;
}
[Column("isprivate", IsTag = true)]
public Boolean Isprivate {
get;
}
[Column("withgeo", IsTag = true)]
public Boolean Withgeo {
get;
}
[Column("art")]
public String Art {
get;
}
[Column("name")]
public String Name {
get;
}
[Column("text")]
public String Text {
get;
}
[Column(IsTimestamp = true)]
public DateTime Time {
get;
}
[Column("latitude")]
public Double Lat {
get;
}
[Column("longitude")]
public Double Lon {
get;
}
public Bosmon(PocsagParsed p, Boolean parseTime = false) {
if(UInt64.TryParse(p.Raw.Address, out UInt64 adress)) {
this.Address = adress;
}
this.Function = p.Raw.Function;
this.SendOnline = p.Sendonline;
this.Isprivate = p.Isprivate;
this.Art = p.Funktion;
this.Name = p.Name;
this.Text = p.Raw.Message;
this.Time = parseTime ? p.Date : DateTime.Now;
if(p.Geo != null) {
this.Lat = p.Geo.Latitude;
this.Lon = p.Geo.Longitude;
this.Withgeo = true;
} else {
this.Lat = 0;
this.Lon = 0;
this.Withgeo = false;
}
}
}
}