OpenALPR Webhook Processor for IP Cameras

Thank you so much @mlapaglia for this... This is pretty darn amazing I love it..



Quick question I have two LPRs.. Do I just add another camera in config file ?
 
  • Like
Reactions: biggen
Yeah this is really the answer here. OpenALPR already sends this stuff anywhere you want via webhooks and its payload. To be able to parse .json as it comes in and inject the bits you want directly to the DB... Man that is sweet!
 
  • Love
Reactions: tech101
Just noticed, Since I am running Windows Docker version. When I sign out seems like it stops working. Anything I can do when Signed out the thing still can keep working Seems like the container/app stops upon signing out windows account.
 
  • Like
Reactions: biggen
i have added a swagger endpoint to the service, go to `ipaddress:5000`to get to it:

1609822180168.png

Now that the plates are saving to the database this get method should return them in the call. no need to install a sql engine or run a bunch of sprocs, it's all handled by the service.
 
  • Like
  • Love
Reactions: biggen and tech101
Very cool! Is there anyway to make the port configurable? I'm already running a service on port 5000 on that machine. Also, is it possible to make this db searchable? Like for a given day or time?

This is amazing work. Seriously. The community will love this here.
 
Last edited:
I'm getting an Exit 139 when trying to fire it up with the new image. Is that processor.db supposed to be a file? Because its actually creating it as a directory called processor.db and not a file. The directory inside is empty. Here are my settings:

Bash:
#!/bin/bash
docker run -d \
--name=openalprwebhookprocessor \
--net=bridge \
-v /home/joe/webhook_alpr/app/appsettings.json:/app/appsettings.json \
-v /home/joe/webhook_alpr/app/processor.db:/app/processor.db \
-p 3859:80 \
mlapaglia/openalprwebhookprocessor

JSON:
{
  "AllowedHosts": "*",
  "Cameras": {
    "Cameras": [
      {
        "Manufacturer": "Dahua",
        "OpenAlprCameraId": 123456789,
        "Password": "password",
        "UpdateOverlayTextUrl": "http://10.200.200.14/cgi-bin/configManager.cgi?action=setConfig&VideoWidget[0].CustomTitle[1].Text=",
        "Username": "admin"
      }
    ]
  },
  "ConnectionStrings": {
    "ProcessorContext": "Data Source=processor.db"
  },
  "WebRequestLoggingEnabled": false
}

JSON:
{"log":"[12:51:33 WRN] 'AddEntityFramework*' was called on the service provider, but 'UseInternalServiceProvider' wasn't called in the DbContext options configuration. Remove the 'AddEntityFramework*' call as in most cases it's not needed and might cause conflicts with other products and services registered in the same service provider.\n","stream":"stdout","time":"2021-01-05T12:51:33.500913201Z"}
{"log":"[12:51:34 INF] Entity Framework Core 3.1.10 initialized 'ProcessorContext' using provider 'Microsoft.EntityFrameworkCore.Sqlite' with options: None\n","stream":"stdout","time":"2021-01-05T12:51:34.05881382Z"}
{"log":"[12:51:34 FTL] Application startup exception\n","stream":"stdout","time":"2021-01-05T12:51:34.235941313Z"}
{"log":"Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 10: 'disk I/O error'.\n","stream":"stdout","time":"2021-01-05T12:51:34.235988251Z"}
{"log":"   at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db)\n","stream":"stdout","time":"2021-01-05T12:51:34.235997163Z"}
{"log":"   at Microsoft.Data.Sqlite.SqliteConnection.Open()\n","stream":"stdout","time":"2021-01-05T12:51:34.236004498Z"}
{"log":"   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenDbConnection(Boolean errorsExpected)\n","stream":"stdout","time":"2021-01-05T12:51:34.236012511Z"}
{"log":"   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.Open(Boolean errorsExpected)\n","stream":"stdout","time":"2021-01-05T12:51:34.236019163Z"}
{"log":"   at Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal.SqliteDatabaseCreator.Exists()\n","stream":"stdout","time":"2021-01-05T12:51:34.236025584Z"}
{"log":"   at Microsoft.EntityFrameworkCore.Migrations.HistoryRepository.Exists()\n","stream":"stdout","time":"2021-01-05T12:51:34.236032089Z"}
{"log":"   at Microsoft.EntityFrameworkCore.Migrations.HistoryRepository.GetAppliedMigrations()\n","stream":"stdout","time":"2021-01-05T12:51:34.236038568Z"}
{"log":"   at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.GetAppliedMigrations(DatabaseFacade databaseFacade)\n","stream":"stdout","time":"2021-01-05T12:51:34.236045129Z"}
{"log":"   at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.GetPendingMigrations(DatabaseFacade databaseFacade)\n","stream":"stdout","time":"2021-01-05T12:51:34.236051676Z"}
{"log":"   at OpenAlprWebhookProcessor.Startup.MigrateDb(IApplicationBuilder app) in /src/OpenAlprWebhookProcessor/Startup.cs:line 97\n","stream":"stdout","time":"2021-01-05T12:51:34.236058512Z"}
{"log":"   at OpenAlprWebhookProcessor.Startup.Configure(IApplicationBuilder app, IWebHostEnvironment env) in /src/OpenAlprWebhookProcessor/Startup.cs:line 73\n","stream":"stdout","time":"2021-01-05T12:51:34.236065356Z"}
{"log":"   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)\n","stream":"stdout","time":"2021-01-05T12:51:34.236074459Z"}
{"log":"   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)\n","stream":"stdout","time":"2021-01-05T12:51:34.236081306Z"}
{"log":"   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)\n","stream":"stdout","time":"2021-01-05T12:51:34.236088195Z"}
{"log":"   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.\u003c\u003ec__DisplayClass4_0.\u003cBuild\u003eb__0(IApplicationBuilder builder)\n","stream":"stdout","time":"2021-01-05T12:51:34.236094966Z"}
{"log":"   at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.\u003c\u003ec__DisplayClass13_0.\u003cUseStartup\u003eb__2(IApplicationBuilder app)\n","stream":"stdout","time":"2021-01-05T12:51:34.23610533Z"}
{"log":"   at Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterBuilderStartupFilter.\u003c\u003ec__DisplayClass0_0.\u003cConfigure\u003eg__MiddlewareFilterBuilder|0(IApplicationBuilder builder)\n","stream":"stdout","time":"2021-01-05T12:51:34.236112875Z"}
{"log":"   at Microsoft.AspNetCore.HostFilteringStartupFilter.\u003c\u003ec__DisplayClass0_0.\u003cConfigure\u003eb__0(IApplicationBuilder app)\n","stream":"stdout","time":"2021-01-05T12:51:34.236121639Z"}
{"log":"   at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)\n","stream":"stdout","time":"2021-01-05T12:51:34.236150155Z"}
{"log":"Unhandled exception. Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 10: 'disk I/O error'.\n","stream":"stderr","time":"2021-01-05T12:51:34.246509833Z"}
{"log":"   at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db)\n","stream":"stderr","time":"2021-01-05T12:51:34.246539389Z"}
{"log":"   at Microsoft.Data.Sqlite.SqliteConnection.Open()\n","stream":"stderr","time":"2021-01-05T12:51:34.246544685Z"}
{"log":"   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenDbConnection(Boolean errorsExpected)\n","stream":"stderr","time":"2021-01-05T12:51:34.246548684Z"}
{"log":"   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.Open(Boolean errorsExpected)\n","stream":"stderr","time":"2021-01-05T12:51:34.246552633Z"}
{"log":"   at Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal.SqliteDatabaseCreator.Exists()\n","stream":"stderr","time":"2021-01-05T12:51:34.246557296Z"}
{"log":"   at Microsoft.EntityFrameworkCore.Migrations.HistoryRepository.Exists()\n","stream":"stderr","time":"2021-01-05T12:51:34.246563737Z"}
{"log":"   at Microsoft.EntityFrameworkCore.Migrations.HistoryRepository.GetAppliedMigrations()\n","stream":"stderr","time":"2021-01-05T12:51:34.246570218Z"}
{"log":"   at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.GetAppliedMigrations(DatabaseFacade databaseFacade)\n","stream":"stderr","time":"2021-01-05T12:51:34.246576675Z"}
{"log":"   at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.GetPendingMigrations(DatabaseFacade databaseFacade)\n","stream":"stderr","time":"2021-01-05T12:51:34.246582661Z"}
{"log":"   at OpenAlprWebhookProcessor.Startup.MigrateDb(IApplicationBuilder app) in /src/OpenAlprWebhookProcessor/Startup.cs:line 97\n","stream":"stderr","time":"2021-01-05T12:51:34.246590031Z"}
{"log":"   at OpenAlprWebhookProcessor.Startup.Configure(IApplicationBuilder app, IWebHostEnvironment env) in /src/OpenAlprWebhookProcessor/Startup.cs:line 73\n","stream":"stderr","time":"2021-01-05T12:51:34.246597118Z"}
{"log":"   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)\n","stream":"stderr","time":"2021-01-05T12:51:34.246603986Z"}
{"log":"   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)\n","stream":"stderr","time":"2021-01-05T12:51:34.246610478Z"}
{"log":"   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)\n","stream":"stderr","time":"2021-01-05T12:51:34.246619058Z"}
{"log":"   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.\u003c\u003ec__DisplayClass4_0.\u003cBuild\u003eb__0(IApplicationBuilder builder)\n","stream":"stderr","time":"2021-01-05T12:51:34.24662573Z"}
{"log":"   at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.\u003c\u003ec__DisplayClass13_0.\u003cUseStartup\u003eb__2(IApplicationBuilder app)\n","stream":"stderr","time":"2021-01-05T12:51:34.246633517Z"}
{"log":"   at Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterBuilderStartupFilter.\u003c\u003ec__DisplayClass0_0.\u003cConfigure\u003eg__MiddlewareFilterBuilder|0(IApplicationBuilder builder)\n","stream":"stderr","time":"2021-01-05T12:51:34.246640762Z"}
{"log":"   at Microsoft.AspNetCore.HostFilteringStartupFilter.\u003c\u003ec__DisplayClass0_0.\u003cConfigure\u003eb__0(IApplicationBuilder app)\n","stream":"stderr","time":"2021-01-05T12:51:34.246652006Z"}
{"log":"   at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)\n","stream":"stderr","time":"2021-01-05T12:51:34.246658787Z"}
{"log":"   at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)\n","stream":"stderr","time":"2021-01-05T12:51:34.246664799Z"}
{"log":"   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)\n","stream":"stderr","time":"2021-01-05T12:51:34.246699291Z"}
{"log":"   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)\n","stream":"stderr","time":"2021-01-05T12:51:34.246708412Z"}
{"log":"   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)\n","stream":"stderr","time":"2021-01-05T12:51:34.246714918Z"}
{"log":"   at OpenAlprWebhookProcessor.Program.Main(String[] args) in /src/OpenAlprWebhookProcessor/Program.cs:line 20\n","stream":"stderr","time":"2021-01-05T12:51:34.246721109Z"}
 
ok, i'm going to move the configuration and db to a new folder, so we will only need to map that one directory instead of every file individually

docker run -d \
--name=openalprwebhookprocessor \
--net=bridge \
-v /app/config/:/app/config/ \
-p 3859:80 \
mlapaglia/openalprwebhookprocessor
 
  • Like
  • Love
Reactions: tech101 and biggen
So it now creates the .db file in the appropriate directory, but I can't get the web page to load at ipaddress:5000. I just get a connection refused. I turned stopped my other service that was running at port 5000 for testing yours.

Bash:
#!/bin/bash

docker run -d \
--name=openalprwebhookprocessor \
--net=bridge \
-v /home/joe/webhook_alpr/app/config/:/app/config \
-p 3859:80 \
mlapaglia/openalprwebhookprocessor

JSON:
{
  "AllowedHosts": "*",
  "Cameras": {
    "Cameras": [
      {
        "Manufacturer": "Dahua",
        "OpenAlprCameraId": 123456789,
        "Password": "password",
        "UpdateOverlayTextUrl": "http://10.200.200.14/cgi-bin/configManager.cgi?action=setConfig&VideoWidget[0].CustomTitle[1].Text=",
        "Username": "admin"
      }
    ]
  },
  "ConnectionStrings": {
    "ProcessorContext": "Data Source=config/processor.db"
  },
  "WebRequestLoggingEnabled": false
}

JSON:
{"log":"[14:16:05 WRN] 'AddEntityFramework*' was called on the service provider, but 'UseInternalServiceProvider' wasn't called in the DbContext options configuration. Remove the 'AddEntityFramework*' call as in most cases it's not needed and might cause conflicts with other products and services registered in the same service provider.\n","stream":"stdout","time":"2021-01-05T14:16:05.099247322Z"}
{"log":"[14:16:05 INF] Entity Framework Core 3.1.10 initialized 'ProcessorContext' using provider 'Microsoft.EntityFrameworkCore.Sqlite' with options: None\n","stream":"stdout","time":"2021-01-05T14:16:05.714673408Z"}
{"log":"[14:16:05 INF] Executed DbCommand (25ms) [Parameters=[], CommandType='Text', CommandTimeout='30']\n","stream":"stdout","time":"2021-01-05T14:16:05.918464887Z"}
{"log":"PRAGMA journal_mode = 'wal';\n","stream":"stdout","time":"2021-01-05T14:16:05.918544448Z"}
{"log":"[14:16:06 INF] Executed DbCommand (7ms) [Parameters=[], CommandType='Text', CommandTimeout='30']\n","stream":"stdout","time":"2021-01-05T14:16:06.097564684Z"}
{"log":"CREATE TABLE \"__EFMigrationsHistory\" (\n","stream":"stdout","time":"2021-01-05T14:16:06.09759607Z"}
{"log":"    \"MigrationId\" TEXT NOT NULL CONSTRAINT \"PK___EFMigrationsHistory\" PRIMARY KEY,\n","stream":"stdout","time":"2021-01-05T14:16:06.09760497Z"}
{"log":"    \"ProductVersion\" TEXT NOT NULL\n","stream":"stdout","time":"2021-01-05T14:16:06.097612504Z"}
{"log":");\n","stream":"stdout","time":"2021-01-05T14:16:06.097619143Z"}
{"log":"[14:16:06 INF] Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30']\n","stream":"stdout","time":"2021-01-05T14:16:06.106067353Z"}
{"log":"SELECT COUNT(*) FROM \"sqlite_master\" WHERE \"name\" = '__EFMigrationsHistory' AND \"type\" = 'table';\n","stream":"stdout","time":"2021-01-05T14:16:06.106098635Z"}
{"log":"[14:16:06 INF] Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']\n","stream":"stdout","time":"2021-01-05T14:16:06.110761875Z"}
{"log":"SELECT \"MigrationId\", \"ProductVersion\"\n","stream":"stdout","time":"2021-01-05T14:16:06.110791343Z"}
{"log":"FROM \"__EFMigrationsHistory\"\n","stream":"stdout","time":"2021-01-05T14:16:06.110800678Z"}
{"log":"ORDER BY \"MigrationId\";\n","stream":"stdout","time":"2021-01-05T14:16:06.110807712Z"}
{"log":"[14:16:06 INF] Applying migration '20210104162820_initial'.\n","stream":"stdout","time":"2021-01-05T14:16:06.134069015Z"}
{"log":"[14:16:06 INF] Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']\n","stream":"stdout","time":"2021-01-05T14:16:06.170152486Z"}
{"log":"CREATE TABLE \"PlateGroups\" (\n","stream":"stdout","time":"2021-01-05T14:16:06.170182296Z"}
{"log":"    \"Id\" TEXT NOT NULL CONSTRAINT \"PK_PlateGroups\" PRIMARY KEY,\n","stream":"stdout","time":"2021-01-05T14:16:06.170192247Z"}
{"log":"    \"OpenAlprCameraId\" INTEGER NOT NULL,\n","stream":"stdout","time":"2021-01-05T14:16:06.170199399Z"}
{"log":"    \"OpenAlprProcessingTimeMs\" REAL NOT NULL,\n","stream":"stdout","time":"2021-01-05T14:16:06.170205463Z"}
{"log":"    \"IsAlert\" INTEGER NOT NULL,\n","stream":"stdout","time":"2021-01-05T14:16:06.170212115Z"}
{"log":"    \"AlertDescription\" TEXT NULL,\n","stream":"stdout","time":"2021-01-05T14:16:06.170218349Z"}
{"log":"    \"ReceivedOn\" TEXT NOT NULL,\n","stream":"stdout","time":"2021-01-05T14:16:06.170224481Z"}
{"log":"    \"PlateNumber\" TEXT NULL,\n","stream":"stdout","time":"2021-01-05T14:16:06.170230529Z"}
{"log":"    \"PlateJpeg\" TEXT NULL,\n","stream":"stdout","time":"2021-01-05T14:16:06.170236158Z"}
{"log":"    \"PlateConfidence\" REAL NOT NULL\n","stream":"stdout","time":"2021-01-05T14:16:06.170242276Z"}
{"log":");\n","stream":"stdout","time":"2021-01-05T14:16:06.170248771Z"}
{"log":"[14:16:06 INF] Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']\n","stream":"stdout","time":"2021-01-05T14:16:06.170455063Z"}
{"log":"INSERT INTO \"__EFMigrationsHistory\" (\"MigrationId\", \"ProductVersion\")\n","stream":"stdout","time":"2021-01-05T14:16:06.170472882Z"}
{"log":"VALUES ('20210104162820_initial', '3.1.10');\n","stream":"stdout","time":"2021-01-05T14:16:06.170480453Z"}
{"log":"[14:16:06 INF] Now listening on: http://[::]:80\n","stream":"stdout","time":"2021-01-05T14:16:06.321652579Z"}
{"log":"[14:16:06 INF] Application started. Press Ctrl+C to shut down.\n","stream":"stdout","time":"2021-01-05T14:16:06.322029646Z"}
{"log":"[14:16:06 INF] Hosting environment: Production\n","stream":"stdout","time":"2021-01-05T14:16:06.322134683Z"}
{"log":"[14:16:06 INF] Content root path: /app\n","stream":"stdout","time":"2021-01-05T14:16:06.322245201Z"}
 
Its amazing how far you have come with this service. From printing overlays to inputting everything into a DB. Well done!
 
  • Like
Reactions: tech101
Thank you mlapaglia for creating this and improving/adding features so much for this in a very short time :)
 
  • Like
Reactions: biggen