using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using SimConnect.Attribute;
namespace SimConnect.Concrete
{
///
/// Defines a SimConnect variable.
///
public struct SimConnectVariable
{
///
/// The name of the SimConnect variable
///
public string VariableName;
///
/// The SimConnect data type. If null, the SimConnect data type will be determined from the .NET type.
///
public SimConnectDataType DataType;
///
/// The requested units for the value. See the SimConnect documentation for available units
///
public string UnitsName;
///
/// For integer and floating point types, determines how much the value must change before an update is sent
///
public float Epsilon;
};
///
/// Creates a SimConnect definition from properties annotated with the SimConnectVariable attribute.
///
public class SimConnectDefinition
{
private readonly Type type;
private delegate void SetterProc(object instance, BinaryReader data);
private struct SimConnectVariableProperty
{
public SimConnectVariable Variable;
public SetterProc Setter;
}
///
/// Provides access to the parsed variables.
///
public IEnumerable Variables => variables.Select(v => v.Variable);
private readonly List variables;
///
/// Creates an instance of a SimConnectDefinition based on the provided class.
///
/// The class type used for the definition
public SimConnectDefinition(Type type)
{
if (!type.IsClass)
throw new InvalidOperationException($"{type.FullName} is not a class type");
this.type = type;
variables = ParseVariables(type);
if (variables.Count == 0)
throw new InvalidOperationException($"At least one property of {type.FullName} should be annotated with the SimConnectVariable attribute");
}
///
/// Parses the SimConnect data stream to an object according to this definition.
///
/// The SimConnect data stream
/// An instance of the type for this definition
public object ParseData(Stream data)
{
var reader = new BinaryReader(data);
var instance = Activator.CreateInstance(type);
foreach (var variable in variables)
variable.Setter(instance, reader);
return instance;
}
private static List ParseVariables(IReflect type)
{
var result = new List();
foreach (var member in type.GetMembers(BindingFlags.Public | BindingFlags.Instance)
.Where(m => m.MemberType == MemberTypes.Field || m.MemberType == MemberTypes.Property))
{
var variableAttribute = member.GetCustomAttribute();
if (variableAttribute == null)
continue;
var dataType = variableAttribute.DataType;
var setter = GetSetter(member, ref dataType);
if (!dataType.HasValue)
throw new InvalidOperationException($"DataType could not be determined for member {member.Name}");
result.Add(new SimConnectVariableProperty
{
Variable = new SimConnectVariable
{
VariableName = variableAttribute.VariableName,
DataType = dataType.Value,
UnitsName = variableAttribute.UnitsName,
Epsilon = variableAttribute.Epsilon
},
Setter = setter
});
}
return result;
}
private readonly struct SimConnectTypeMapping
{
public readonly Type Type;
public readonly SimConnectDataType SimConnectDataType;
public readonly bool IsDefaultForTypeForType;
public readonly Func Converter;
public SimConnectTypeMapping(Type type, SimConnectDataType simConnectDataType, bool isDefaultForType, Func converter)
{
Type = type;
SimConnectDataType = simConnectDataType;
IsDefaultForTypeForType = isDefaultForType;
Converter = converter;
}
}
private static readonly List SimConnectTypeMappings = new List
{
{ new SimConnectTypeMapping(typeof(double), SimConnectDataType.Float64, true, reader => reader.ReadDouble()) },
{ new SimConnectTypeMapping(typeof(float), SimConnectDataType.Float32, true, reader => reader.ReadSingle()) },
{ new SimConnectTypeMapping(typeof(int), SimConnectDataType.Int32, true, reader => reader.ReadInt32()) },
{ new SimConnectTypeMapping(typeof(uint), SimConnectDataType.Int32, true, reader => reader.ReadUInt32()) },
{ new SimConnectTypeMapping(typeof(long), SimConnectDataType.Int64, true, reader => reader.ReadInt64()) },
{ new SimConnectTypeMapping(typeof(ulong), SimConnectDataType.Int64, true, reader => reader.ReadUInt64()) }
};
private static SetterProc GetSetter(MemberInfo member, ref SimConnectDataType? dataType)
{
Type valueType;
Action