public static SensorConfig GetConfig(object sb)
        {
            var parameters = new List <SensorParam>();
            var sensorType = sb.GetType().GetCustomAttribute <SensorType>();

            if (sensorType == null)
            {
                throw new Exception($"Sensor Configuration Error: {sb.GetType().ToString()} is missing SensorType Attribute");
            }
            foreach (var info in sb.GetType().GetRuntimeFields().Where(field => field.IsDefined(typeof(SensorParameter), true)))
            {
                if (info.FieldType.IsEnum)
                {
                    var v = info.GetValue(sb);

                    var f = new SensorParam()
                    {
                        Name         = info.Name,
                        Type         = "enum",
                        DefaultValue = Enum.GetName(info.FieldType, v),
                        Values       = Enum.GetNames(info.FieldType),
                    };

                    parameters.Add(f);
                }
                else if (info.FieldType == typeof(Color))
                {
                    var value = info.GetValue(sb);
                    var f     = new SensorParam()
                    {
                        Name         = info.Name,
                        Type         = "color",
                        DefaultValue = value == null ? null : "#" + ColorUtility.ToHtmlStringRGBA((Color)value),
                    };

                    parameters.Add(f);
                }
                else if (info.FieldType.IsGenericType && info.FieldType.GetGenericTypeDefinition() == typeof(List <>))
                {
                    var type = info.FieldType;

                    var f = new SensorParam()
                    {
                        Name = info.Name,
                        Type = type.Name,
                    };

                    parameters.Add(f);
                }
                else
                {
                    if (!Typemap.ContainsKey(info.FieldType))
                    {
                        throw new Exception($"Sensor Configuration Error: {sb.GetType().ToString()} has unsupported type {info.FieldType} for {info.Name} field");
                    }
                    var range = info.GetCustomAttribute <RangeAttribute>();
                    var f     = new SensorParam()
                    {
                        Name         = info.Name,
                        Type         = Typemap[info.FieldType],
                        DefaultValue = info.GetValue(sb),
                        Min          = range != null ? (float?)range.min : null,
                        Max          = range != null ? (float?)range.max : null,
                    };

                    parameters.Add(f);
                }
            }

            return(new SensorConfig()
            {
                Name = sensorType.Name,
                Types = sensorType.RequiredTypes.Select(t => t.ToString()).ToArray(),
                Parameters = parameters,
            });
        }
        public static SensorConfig GetConfig(object sb)
        {
            var parameters = new List <SensorParam>();
            var sensorType = sb.GetType().GetCustomAttribute <SensorType>();

            if (sensorType == null)
            {
                throw new Exception($"Sensor Configuration Error: {sb.GetType().ToString()} is missing SensorType Attribute");
            }

            var sbType = sb.GetType();

            foreach (var info in sbType.GetRuntimeFields().Where(field => field.IsDefined(typeof(SensorParameter), true)))
            {
                if (info.FieldType.IsEnum)
                {
                    int i = (int)info.GetValue(sb);
                    var f = new SensorParam()
                    {
                        Name         = info.Name,
                        Type         = "enum",
                        DefaultValue = Enum.GetNames(info.FieldType)[(int)info.GetValue(sb)],
                        Values       = Enum.GetNames(info.FieldType),
                    };

                    parameters.Add(f);
                }
                else if (info.FieldType == typeof(Color))
                {
                    var value = info.GetValue(sb);
                    var f     = new SensorParam()
                    {
                        Name         = info.Name,
                        Type         = "color",
                        DefaultValue = value == null ? null : "#" + ColorUtility.ToHtmlStringRGBA((Color)value),
                    };

                    parameters.Add(f);
                }
                else if (info.FieldType.IsGenericType && info.FieldType.GetGenericTypeDefinition() == typeof(List <>))
                {
                    var type = info.FieldType;
                    var attr = info.GetCustomAttribute <SensorParameter>();

                    var f = new SensorParam()
                    {
                        Name         = info.Name,
                        Type         = type.Name,
                        DefaultValue = attr?.GetDefaultInstanceJToken(type)
                    };

                    parameters.Add(f);
                }
                else
                {
                    SensorParam f;
                    if (!Typemap.ContainsKey(info.FieldType))
                    {
                        var type = info.FieldType;
                        var attr = info.GetCustomAttribute <SensorParameter>();

                        f = new SensorParam()
                        {
                            Name         = info.Name,
                            Type         = type.Name,
                            DefaultValue = attr?.GetDefaultInstanceJToken(type)
                        };
                    }
                    else
                    {
                        var range = info.GetCustomAttribute <RangeAttribute>();
                        f = new SensorParam()
                        {
                            Name         = info.Name,
                            Type         = Typemap[info.FieldType],
                            DefaultValue = info.GetValue(sb),
                            Min          = range != null ? (float?)range.min : null,
                            Max          = range != null ? (float?)range.max : null,
                        };
                    }

                    parameters.Add(f);
                }
            }

            if (typeof(CameraSensorBase).IsAssignableFrom(sbType))
            {
                const string ppName = DefaultPostprocessingAttribute.PostprocessingFieldName;
                if (parameters.Any(x => x.Name == ppName))
                {
                    throw new Exception($"Serialized `{ppName}` field marked with {nameof(SensorParameter)} attribute conflicts with postprocessing data for {typeof(CameraSensorBase)}.");
                }

                var ppAttr = sbType.GetCustomAttribute <DefaultPostprocessingAttribute>();
                var param  = new SensorParam
                {
                    Name         = ppName,
                    Type         = typeof(List <PostProcessData>).Name,
                    DefaultValue = ppAttr != null?ppAttr.GetDefaultInstanceJToken() : DefaultPostprocessingAttribute.GetEmptyInstanceJToken()
                };

                parameters.Add(param);
            }

            return(new SensorConfig()
            {
                Name = sensorType.Name,
                Types = sensorType.RequiredTypes.Select(t => t.ToString()).ToArray(),
                Parameters = parameters,
            });
        }