Category Archives: Programming

Displaying Plate Numbers on ALPR Cameras

I’ve been running OpenALPR on Unraid for about 6 months. Since January I’ve been working on a tool that replaces the limited closed-source OpenALPR web ui with my own OpenALPR Webhook Processor. I pointed the OpenALPR agent at my tool instead of the OpenALPR web ui. The agent sends a JSON payload defined here.

After the JSON payload is processed it is displayed through an Angular web app running on Asp.net Core backend. It uses a SQLite database to store plate information, and retrieves images from the agent on the fly. It records a small amount of statistics, allows searching based on make/model and plate numbers including regular expressions, and can both ignore and alert on plates.

The home plate screen

The tool can also manage individual cameras day/night toggling and zoom/focus based on the camera GPS location:

Modifying ALPR and regular cameras

When the tool receives a webhook from the agent, it parses the plate information and overlays the text on the camera:

The agent tries to determine the make/model of the vehicle, it’s not always correct (especially at night), but it does get a fair amount of accurate results.

So far it has been working pretty well! I am currently working on a method for “scraping” the agent’s debug web ui for information instead of using webhooks. This will be useful for people with the “Homeowner” membership that requires the webhook to come from the OpenALPR cloud website.

Validating configuration on startup in ASP.Net core 2.2

The ability to validate configurations in Startup.cs was pushed from .NET Core 2.2 to 3.0. In the mean time I still needed to validate our configurations when they were loaded to ensure all transforms performed correctly and no configuration fields were missing. I found a few solutions online for this, but they involved middleware like Andrew Lock’s solution or using the services.Configure pattern like Vidar Kongsli’s solution. I needed something simpler and easier for developers to implement.

Configuring services in Startup.cs looked like

var cognitoConfiguration = configuration.GetSection("Aws-Cognito").Get<AwsCognitoConfiguration>();
           services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
   .AddJwtBearer(options =>
   {
      options.Audience = cognitoConfiguration.UserPoolClientId;
      options.Authority = cognitoConfiguration.AuthorityUrl.ToString();
   });

But issues can arise with this approach, for instance if UserPoolClientId isn’t populated, or if the entire 'Aws-Cognito' section was missing from the appsettings.json. A simple approach is to create an extension method for the IConfiguration class:

public static class ConfigurationExtensionMethods
{
     public static T GetSectionAndValidate<T>(this IConfiguration configuration, string sectionName)
         where T : DriveConfiguration
     {
         var results = configuration.GetSection(sectionName).Get<T>();

         results.CheckConfigurationValid();

         return results;
     }
 }

Modify all of the configuration POCO’s to use an abstract class for the validation:

public abstract class DriveConfiguration
{
    public void CheckConfigurationValid()
    {
        var results = new List<ValidationResult>();

        var isValid = Validator.TryValidateObject(
            this,
            new ValidationContext(this),
            results,
            true);

        if (!isValid)
        {
            var errorMessage = $"Missing configuration for {this.GetType().Name}: ";

            errorMessage += string
                .Join(", ", results
                    .Select(x => x.ErrorMessage));

            throw new ArgumentException(errorMessage);
        }
    }
}

A configuration POCO then uses theSystem.ComponentModel.DataAnnotations namespace to add validation attributes like [Required] or [EmailAddress]:

public class AwsCognitoConfiguration : DriveConfiguration
{
   [Required]
   public string UserPoolClientId { get; set; }

   [Required]
   public Uri AuthorityUrl { get; set; }
}

in Startup.cs getting the configuration changes to look like:

var cognitoConfiguration = _configuration.GetSectionAndValidate<AwsCognitoConfiguration>("Aws-Cognito");

When the GetSectionAndValidate<T>() executes, it populates the configuration POCO from appsettings.json and then fires off the validation. Aside from using the extension method, nothing else needs to be done in order for all configurations in Startup.cs to be validated automatically! Exceptions look like

System.ArgumentException: 'Missing configuration for CognitoConfiguration: The UserPoolClientId is required'

As you can see, it’s very easy to see what value is missing from what part of appsettings.json. This validation can be used for a health check, or even cause launch failures that an ECS cluster can respond to.

.Net Core 2.2 comes with a good base set of validators, but writing your own validator attributes is as easy as implementing ValidationAttribute:

[AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
public class AwsAccessKeyValidatorAttribute : ValidationAttribute
{
    public AwsAccessKeyValidatorAttribute()
        : base("The {0} field must be a valid AWS Access Key")
    {
    }

    public override bool IsValid(object value)
    {
        return value is string && value.ToString().Trim().Length == 20;
    }
}