خانه درباره ما خدمات پروژه‌ها وبلاگ تماس با ما
خانه پروژه‌ها سامانه نمایش شناوران (AIS)

📡 AisFeedParser

مستندات فنی جامع - سامانه دریافت، پردازش و ذخیره‌سازی داده‌های AIS
نسخه ۱.۰ | سکوی .NET 8 | انتشار خرداد ۱۴۰۵
📌

نمای کلی پروژه

AisFeedParser یک سامانه با کارایی بالا برای دریافت، تجزیه و ذخیره‌سازی خوراک داده‌های AIS (سیستم شناسایی خودکار) است. این پروژه با زبان C# و سکوی .NET 8 توسعه یافته و از معماری تمیز (Clean Architecture) پیروی می‌کند.

قابلیت‌های اصلی

  • دریافت جمله‌های NMEA با پروتکل‌های TCP و UDP
  • تجزیه پیام‌های AIS انواع ۱-۵، ۱۸-۱۹، ۲۴، ۲۷
  • ذخیره‌سازی لاگ‌های خام ساعتی برای بازپخش (Replay)
  • ذخیره‌سازی داده‌ها در SQLite (توسعه) و SQL Server (تولید)
  • ارائه REST API برای پرس‌وجوی داده‌های کشتی‌ها
  • پایش سلامت ارائه‌دهندگان AIS

فناوری‌های به‌کار رفته

فناورینسخهکاربرد
.NET8.0پلتفرم اجرایی
ASP.NET Core8.0وب API و Swagger
Entity Framework Core9.0.8ORM و Migrations
Serilog9.0.0ثبت ساختاریافته رویدادها
xUnit2.6.6چارچوب آزمون واحد
SQL Server / SQLiteپایگاه داده
Azure DevOpsCI/CD
Dockerظرف‌سازی و استقرار

ساختار پروژه

AisFeedParser/
├── src/
│   ├── AisFeedParser.Domain/          # موجودیت‌ها، رمزگشا، تجزیه NMEA
│   ├── AisFeedParser.Application/     # موارد استفاده، DTOها، انتزاع‌ها
│   ├── AisFeedParser.Infrastructure/  # ماندگاری، سرویس‌ها، مخزن‌ها
│   └── AisFeedParser.Api/             # کنترلرهای ASP.NET Core، میان‌افزارها
├── tests/
│   ├── AisFeedParser.Domain.Tests/    # آزمون‌های رمزگشا
│   └── AisFeedParser.Application.Tests/
├── docs/                              # مستندات فنی (فارسی و انگلیسی)
├── openspec/                          # مشخصات رسمی و پیشنهادات تغییر
└── ais_temp.txt                       # نمونه داده NMEA
🏗️

معماری سیستم

این پروژه از معماری تمیز (Clean Architecture) یا معماری لایه‌ای به سبک DDD-lite پیروی می‌کند. وابستگی‌ها از بیرونی‌ترین لایه (API) به سمت داخلی‌ترین لایه (Domain) جهت‌گیری شده‌اند. هر لایه فقط به لایه داخلی‌تر از خود وابسته است.

نمودار معماری

AisFeedParser.Api

AisFeedParser.Application

AisFeedParser.Domain

AisFeedParser.Infrastructure

🔵 لایه Presentation (API)

کنترلرهای ASP.NET Core، Swagger، میان‌افزارها، پیکربندی برنامه.

🟢 لایه Application

موارد استفاده (Use Cases)، DTOها، واسط‌های انتزاعی، تزریق وابستگی.

🟠 لایه Domain

موجودیت‌های اصلی، رمزگشای AIS، تجزیه‌کننده NMEA (بدون وابستگی بیرونی).

🟣 لایه Infrastructure

پیاده‌سازی ماندگاری، سرویس‌های پس‌زمینه، مخزن‌ها، Channel‌ها.

الگوهای طراحی به‌کار رفته

الگومکان استفادهتوضیح
Pipeline / Producer-Consumerخط لوله اصلیIngestor → Channel → Parser → Channel → Writer
BackgroundServiceهمه سرویس‌های پس‌زمینهاجرای طولانی‌مدت با IHostedService
Channel-based Backpressureاتصال میان سرویس‌هاSystem.Threading.Channels با DropOldest
Strategyانتخاب نوع WriterSQLite vs SQL Server بر اساس پیکربندی
Repositoryدسترسی به دادهEF + In-Memory پیاده‌سازی‌ها
Use Caseلایه کاربردکلاس‌های سرویس تزریقی
OptionsپیکربندیIOptions<T> با binding قوی
DI Extension Methodsهر لایهAddXxx() برای ثبت سرویس‌ها
Exponential Backoffاتصال مجددتاخیر تصاعدی با jitter
Bulk InsertSqlBulkWriterSqlBulkCopy با DataTable
📚

لایه‌های معماری

لایه Presentation (API) — AisFeedParser.Api

نقطه ورود برنامه. شامل:

فایلمسئولیت
Program.csراه‌اندازی Serilog، اجرای Startup
Startup.csثبت سرویس‌ها و میان‌افزارها
Controllers/VesselsController.csAPI کشتی‌ها: latest, data
Controllers/VesselProfilesController.csAPI پروفایل کشتی‌ها
Controllers/AdminController.csAPI مدیریتی: reconnect, last5
Controllers/StatusController.csAPI وضعیت: dashboard HTML, summary
Controllers/AppController.csAPI برنامه: ایجاد و پرس‌وجوی کشتی

لایه Application — AisFeedParser.Application

موارد استفاده و انتزاع‌های کسب‌وکار:

فایلمسئولیت
UseCases/CreateOrGetVessel.csایجاد یا دریافت کشتی
UseCases/GetLatestVesselsByMmsi.csدریافت آخرین کشتی‌ها گروه‌بندی شده بر MMSI
UseCases/GetLatestVesselData.csدریافت داده کشتی در بازه زمانی
UseCases/GetVesselProfileByMmsi.csدریافت پروفایل یک کشتی
UseCases/SearchVesselProfilesByName.csجستجوی پروفایل بر اساس نام
Abstractions/IAisWriter.csواسط نوشتن دسته‌ای داده
Abstractions/IAisProfileUpdater.csواسط به‌روزرسانی پروفایل
Abstractions/IAisSourceSupervisor.csواسط اتصال مجدد
Abstractions/IVesselRepository.csواسط مخزن کشتی
Abstractions/IVesselProfileReadRepository.csواسط مخزن پروفایل
DTOs/VesselDto.csDTO ساده کشتی
DTOs/VesselLatestDataDto.csDTO غنی با خواص Effective*
DTOs/VesselProfileDto.csDTO پروفایل
Models/AisParsedRow.csرکورد پارس شده کامل (۹۹ پارامتر)

لایه Domain — AisFeedParser.Domain

هسته برنامه بدون وابستگی خارجی:

فایلمسئولیت
Ais/AisDecoder.csرمزگشای بیتی AIS (۳۸۹ خط)
Ais/AisMessage.csمدل پیام رمزگشایی شده (۷۹ خاصیت)
Ais/Nmea.csتجزیه جمله NMEA
Entities/Vessel.csموجودیت کشتی
Entities/VesselProfile.csپروفایل تجمیعی کشتی

لایه Infrastructure — AisFeedParser.Infrastructure

پیاده‌سازی ماندگاری و سرویس‌های زیرساختی:

سرویسمسئولیت
MultiSocketIngestor.csدریافت TCP/UDP چندگانه با failover
ParserService.csتجزیه NMEA و رمزگشایی AIS
SqliteWriter.csنوشتن دسته‌ای در SQLite
SqlBulkWriter.csنوشتن حجیم در SQL Server (۱۰۱۴ خط)
HourlyFileWriter.csنوشتن لاگ خام ساعتی
RawLogReplayer.csبازپخش لاگ‌های خام
ProviderStatusMonitor.csپایش سلامت ارائه‌دهنده
VesselProfileUpdater.csبه‌روزرسانی پروفایل کشتی
AppDbMigrator.csمهاجرت خودکار پایگاه داده
AisDataRepository.csپرس‌وجوی داده با ADO.NET خام
EfVesselRepository.csمخزن کشتی با EF Core
EfVesselProfileReadRepository.csمخزن پروفایل با EF Core
MultipartBuffer.csبافر پیام‌های چندبخشی
ExponentialBackoff.csتاخیر تصاعدی تصادفی
HostConnectionTracker.csرهگیری آخرین داده دریافتی
🔀

خط لوله داده (Data Pipeline)

داده از ارائه‌دهنده AIS وارد شده و از یک خط لوله ناهمزمان عبور می‌کند. هر مرحله از طریق Channelهای bounded با سیاست DropOldest به مرحله بعد متصل است.

نمودار خط لوله

📡 AIS Provider
TCP/UDP
🔌 MultiSocket
Ingestor
📁 HourlyFile
Writer
╰→
⚙️ ParserService
N Workers
🗄️ Writer
SQLite / SQL Server

مراحل خط لوله

مرحلهورودیخروجیتوضیح
MultiSocketIngestor پکت‌های TCP/UDP Channel<AisRawMessage> اتصال به میزبان‌های primary/passive، خط‌خوانی، prepend اطلاعات مبدأ
HourlyFileWriter AisRawMessage فایل لاگ نوشتن هم‌زمان لاگ خام با فرمت [IP:Port:Transport]
ParserService Channel<AisRawMessage> Channel<AisParsedRow> اعتبارسنجی checksum، مونتاژ چندبخشی، رمزگشایی AIS، به‌روزرسانی پروفایل
SqliteWriter / SqlBulkWriter Channel<AisParsedRow> پایگاه داده نوشتن دسته‌ای با PeriodicTimer (FlushMs) در تراکنش

جزئیات Channel‌ها

// Channel خطوط NMEA خام
Channel<AisRawMessage> lineChannel = Channel.CreateBounded<AisRawMessage>(
    new BoundedChannelOptions(capacity) {
        FullMode = BoundedChannelFullMode.DropOldest
    });

// Channel ردیف‌های پارس شده
Channel<AisParsedRow> parsedChannel = Channel.CreateBounded<AisParsedRow>(
    new BoundedChannelOptions(capacity) {
        FullMode = BoundedChannelFullMode.DropOldest
    });
🧩

لایه دامنه (Domain)

AisDecoder.cs

کلاس استاتیک رمزگشای AIS. شامل ۳۸۹ خط کد. پیمایش بیتی با استفاده از BitArray و متدهای کمکی SixBit()، ToBits()، Get()، ToSignedCoord()، Decode6BitString().

انواع پیام‌های پشتیبانی شده

نوعنامطول بیتفیلدهای کلیدی
۱Position Report Class A۱۶۸Lat, Lon, SOG, COG, Heading, NavStatus
۲Position Report Class A (Assigned)۱۶۸همان نوع ۱
۳Position Report Class A (Response)۱۶۸همان نوع ۱
۴Base Station Report۱۶۸Lat, Lon, UTC Timestamp
۵Static and Voyage Related Data۴۲۴VesselName, CallSign, ShipType, IMO, Dimensions, Destination, ETA, Draught
۱۸Standard Class B CS Position Report۱۶۸Lat, Lon, SOG, COG, UnitType (CS)
۱۹Extended Class B CS Position Report۳۱۲Lat, Lon, SOG, COG, VesselName, ShipType, Dimensions
۲۴Static Data Report۱۶۸Part A: Name, Part B: CallSign, ShipType, Dimensions
۲۷Long Range AIS Broadcast۹۶Lat, Lon, SOG, COG (دقت کمتر)

متدهای کلیدی

// متد اصلی رمزگشایی
public static AisMessage? Parse(string payload, int msgType, int fillBits)

// متدهای کمکی
private static BitArray SixBit(string payload)  // تبدیل payload بیس-۶۴ به بیت
private static uint ToBits(BitArray bits, int start, int length)  // استخراج بیت
private static double? ToSignedCoord(BitArray bits, int start, int length)  // مختصات امضا‌دار
private static string Decode6BitString(BitArray bits, int start, int length)  // رشته ۶ بیتی

AisMessage.cs

مدل پیام رمزگشایی شده با ۷۹ خاصیت init. تمام فیلدهای مشترک و اختصاصی هر نوع پیام پشتیبانی می‌شود:

فیلدهای مشترک

MessageType, Mmsi, RepeatIndicator

موقعیتی (۱-۳, ۱۸-۱۹, ۲۷)

Latitude, Longitude, Sog, Cog, Heading, NavStatus, RotRaw, PosAcc, Raim, Radio

ایستگاه پایه (۴)

UtcYear, UtcMonth, UtcDay, UtcHour, UtcMin, UtcSec

استاتیک/سفر (۵)

VesselName, CallSign, ShipType, IMO, Destination, ETA, Draught, DimBow, DimStern, DimPort, DimStar

داده استاتیک (۲۴)

PartNumber (A/B), VesselName (Part A), CallSign (Part B), ShipType (Part B), VendorId (Part B), DimBow (Part B)

Nmea.cs

تجزیه‌کننده استاتیک جمله‌های NMEA. جمله‌های پشتیبانی شده:

  • !AIVDM / !AIVDO — داده AIS با Checksum
  • $GPGGA — موقعیت GPS
  • $GPVTG — مسیر و سرعت
  • $GPZDA — زمان و تاریخ
  • $PSHI — اختصاصی

اعتبارسنجی XOR Checksum با TryValidate(). مونتاژ پیام‌های چندبخشی (Multi-part) از طریق MultipartBuffer.

موجودیت‌ها (Entities)

موجودیتخاصیت‌هامتدها
Vessel Id (Guid), Mmsi, Name سازنده با اعتبارسنجی MMSI (۹ رقمی)
VesselProfile Mmsi, Name, CallSign, ShipType, Dimensions, IMO, Destination, ETA, Draught, EPFD, LastLat, LastLon, LastCog, LastSog, LastHeading, LastNavStatus, MessageCount UpdateStaticFromType5(), UpdateStaticFromType24(), UpdatePosition()
⚙️

لایه کاربرد (Application)

این لایه شامل موارد استفاده (Use Cases) و واسط‌های انتزاعی است. تمام منطق کسب‌وکار مختص به یک سناریوی خاص در کلاس‌های Use Case کپسوله شده است.

موارد استفاده (Use Cases)

Use Caseورودیخروجیاعتبارسنجی
CreateOrGetVesselstring mmsiVesselMMSI ۹ رقمی
GetLatestVesselsByMmsiint? sinceMinutes (1-1440)List<VesselLatestDataDto>بازه ۱ تا ۱۴۴۰ دقیقه
GetLatestVesselDatastring mmsi, DateTime? from, DateTime? toList<VesselLatestDataDto>حداکثر ۳۰ روز بازه
GetVesselProfileByMmsistring mmsiVesselProfileDto?
SearchVesselProfilesByNamestring namePrefix, int page, int pageSizeVesselProfileSearchResponseحداقل ۳ کاراکتر نام، صفحه‌بندی

AisParsedRow — مدل جامع داده

کلاس AisParsedRow یک رکورد با ۹۹ پارامتر است که تمام داده‌های ممکن از جمله‌های AIS، NMEA و فراداده را در بر می‌گیرد. دارای ۶ متد کارخانه:

متد کارخانهمنبع
FromAis()پیام AIS رمزگشایی شده
FromNmeaGga()جمله $GPGGA
FromNmeaVtg()جمله $GPVTG
FromNmeaZda()جمله $GPZDA
FromPshi()جمله اختصاصی $PSHI
FromRaw()خط خام NMEA (برای خطا)

VesselLatestDataDto — DTO غنی

این DTO داده‌های موقعیتی AIS را با داده‌های پروفایل کشتی ترکیب می‌کند. دارای خواص Effective* است که از پروفایل یا داده مستقیم پر می‌شود:

public sealed record VesselLatestDataDto
{
    // داده مستقیم از AIS
    public long Mmsi { get; init; }
    public int? MsgType { get; init; }
    public double? Lat, Lon, Cog, Sog;
    public int? Heading, NavStatus;

    // خواص ترکیبی (Effective)
    public string? EffectiveName;       // از پروفایل یا داده AIS
    public string? EffectiveCallSign;
    public string? EffectiveShipType;
    public string? EffectiveDestination;
    public string? EffectiveImo;

    // ابعاد محاسبه شده
    public int? OverallLength;  // DimBow + DimStern
    public int? OverallWidth;   // DimPort + DimStar
}
🔧

لایه زیرساخت (Infrastructure)

MultiSocketIngestor — دریافت TCP/UDP

یک سرویس پس‌زمینه که از معماری host groups پشتیبانی می‌کند. هر گروه host شامل یک میزبان primary و چند میزبان passive است:

  • TCP: از TcpClient با keep-alive، بافر خطی، و ارسال اولیه (init send)
  • UDP: از UdpClient با بافر دریافتی قابل تنظیم
  • Failover: در صورت قطع connection میزبان primary، به passive‌ها سوئیچ می‌کند
  • HostConnectionTracker: آخرین زمان دریافت داده را رهگیری می‌کند (پنجره ۶۰ ثانیه)
  • ExponentialBackoff: تاخیر تصاعدی با jitter تصادفی برای اتصال مجدد

ساختار پیکربندی میزبان

{
  "Ais:Hosts": [
    {
      "Host": "ais.example.com",
      "Port": 1234,
      "Transport": "Tcp",
      "Passives": [
        { "Host": "ais2.example.com", "Port": 1234, "Transport": "Tcp" }
      ]
    }
  ]
}

ParserService — تجزیه NMEA

سرویس پس‌زمینه با N کارگر (قابل تنظیم). وظایف:

  1. خواندن خط از lineChannel
  2. اعتبارسنجی Checksum NMEA با Nmea.TryValidate()
  3. مونتاژ پیام‌های چندبخشی با MultipartBuffer
  4. رمزگشایی payload AIS با AisDecoder.Parse()
  5. به‌روزرسانی پروفایل کشتی با IAisProfileUpdater
  6. نوشتن ردیف پارس شده در parsedChannel

SqlBulkWriter — نوشتن حجیم SQL Server

یکی از پیچیده‌ترین سرویس‌ها با ۱۰۱۴ خط کد. ویژگی‌ها:

ویژگیتوضیح
۹ جدول هدفIngestLine, AisPositionReport, AisStaticVoyage, AisStaticData24, AisBaseStationReport, NmeaGga, NmeaVtg, NmeaZda, PshiRaw
SqlBulkCopyنوشتن حجیم با DataTable
Batch Size۳۰۰ ردیف (پیش‌فرض)
Flush Interval۲۰۰ میلی‌ثانیه (PeriodicTimer)
Schema Auto-createCREATE TABLE IF NOT EXISTS
Index Creationایندکس روی Mmsi, MsgType, IngestedAtUtc
Stored Proceduresp_GetLatestVesselsByMmsi
Viewvw_LatestVesselPositions

SqliteWriter — نوشتن SQLite

سرویس ساده‌تر با ۲۳۸ خط. از یک جدول AisJson استفاده می‌کند با ستون‌های کافی برای تمام انواع پیام. مهاجرت خودکار برای ستون‌های جدید پشتیبانی می‌شود. از INSERT INTO پارامترشده با تراکنش استفاده می‌کند.

AisDataRepository — پرس‌وجوی مستقیم

۶۱۰ خط کد با پیاده‌سازی موازی برای SQLite و SQL Server. پرس‌وجوهای اصلی:

  • GetLatestVesselsByMmsiAsync(): استفاده از CTE/ROW_NUMBER (SQLite) یا stored procedure (SQL Server)
  • GetLatestVesselDataAsync(): پرس‌وجوی داده کشتی در بازه زمانی

سایر سرویس‌ها

سرویستوضیح
HourlyFileWriterنوشتن thread-safe لاگ خام ساعتی با قفل
RawLogReplayerبازپخش لاگ‌های خام با throttle (خط/ثانیه)
ProviderStatusMonitorپینگ TCP هر ۳۰ ثانیه
VesselProfileUpdaterبه‌روزرسانی VesselProfile در حافظه
AppDbMigratorمهاجرت خودکار EF و Stored Procedure
🌐

لایه API

این لایه شامل کنترلرهای ASP.NET Core، میان‌افزارها، Swagger و نقطه ورود برنامه است.

نقاط پایانی API

متدمسیرکنترلرتوضیح
GET/api/vessels/latest?sinceMinutes=VesselsControllerآخرین موقعیت کشتی‌ها گروه‌بندی شده بر MMSI
GET/api/vessels/data?mmsi=&from=&to=VesselsControllerداده موقعیتی یک کشتی در بازه زمانی
GET/api/vessel-profiles/{mmsi}VesselProfilesControllerپروفایل یک کشتی
GET/api/vessel-profiles/search?name=&page=&pageSize=VesselProfilesControllerجستجوی پروفایل بر اساس نام
POST/api/appAppControllerایجاد کشتی جدید
GET/api/app/{mmsi}AppControllerدریافت کشتی
POST/api/admin/reconnectAdminControllerاجبار به اتصال مجدد
GET/api/admin/last5AdminController۵ داده آخر
GET/api/admin/last?count=NAdminControllerN داده آخر
GET/api/status/latestStatusControllerآخرین وضعیت JSON
GET/api/status/dashboardStatusControllerداشبورد HTML
GET/api/status/summaryStatusControllerخلاصه وضعیت
GET/api/status/currentStatusControllerوضعیت جاری
GET/api/status/ping-sqlStatusControllerبررسی اتصال پایگاه داده

راه‌اندازی (Startup.cs)

public sealed class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddPresentation();    // کنترلرها، Swagger
        services.AddApplication();     // Use Cases
        services.AddInfrastructure(configuration); // Infrastructure
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseSwagger();
        app.UseSwaggerUI();
        app.UseRouting();
        app.UseEndpoints(endpoints => endpoints.MapControllers());
    }
}

ثبت ساختاریافته (Serilog)

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .WriteTo.File("logs/ais-.log",
        rollingInterval: RollingInterval.Day,
        retainedFileCountLimit: 14,
        fileSizeLimitBytes: 50 * 1024 * 1024)
    .CreateLogger();
🗄️

پایگاه داده

این پروژه از دو نوع پایگاه داده پشتیبانی می‌کند: SQLite برای محیط توسعه و SQL Server برای محیط تولید.

SQLite — جدول AisJson

یک جدول با ستون‌های کافی برای تمام انواع پیام AIS و NMEA:

CREATE TABLE IF NOT EXISTS AisJson (
    Id              INTEGER PRIMARY KEY AUTOINCREMENT,
    IngestedAtUtc   TEXT NOT NULL,
    SourceIp        TEXT,
    Mmsi            INTEGER,
    MsgType         INTEGER,
    Lat             REAL,
    Lon             REAL,
    Cog             REAL,
    Sog             REAL,
    Heading         INTEGER,
    RawLine         TEXT,
    ChecksumOk      INTEGER,
    ParserError     TEXT,
    RepeatIndicator INTEGER,
    NavStatus       INTEGER,
    RotRaw          INTEGER,
    UtcSec          INTEGER,
    Maneuver        INTEGER,
    PosAcc          INTEGER,
    Raim            INTEGER,
    Radio           INTEGER,
    PayloadJson     TEXT,
    Transport       TEXT,
    Port            INTEGER
);

SQL Server — ۹ جدول نرمال شده

جدولتوضیحفیلدهای اصلی
IngestLineردیف خام ورودیId, IngestedAtUtc, SourceIp, Transport, Port, RawLine, ChecksumOk, ParserError
AisPositionReportانواع ۱-۳, ۱۸-۱۹, ۲۷Mmsi, MsgType, Lat, Lon, Sog, Cog, Heading, NavStatus, RotRaw, UtcSec, Maneuver, PosAcc, Raim, Radio
AisStaticVoyageنوع ۵Mmsi, VesselName, CallSign, ShipType, Imo, Destination, Eta, Draught, DimBow, DimStern, DimPort, DimStar
AisStaticData24نوع ۲۴Mmsi, PartNumber, VesselName, CallSign, ShipType, VendorId, DimBow, DimStern, DimPort, DimStar
AisBaseStationReportنوع ۴Mmsi, Lat, Lon, UtcYear, UtcMonth, UtcDay, UtcHour, UtcMin, UtcSec
NmeaGgaGPS $GPGGAMmsiK, Lat, Lon, Altitude, GeoidSep, Hdop, Quality, Satellites
NmeaVtgمسیر $GPVTGMmsiK, CourseTrue, CourseMagnetic, SpeedKnots, SpeedKph
NmeaZdaزمان $GPZDAMmsiK, UtcYear, UtcMonth, UtcDay, UtcHour, UtcMin, UtcSec
PshiRawاختصاصی $PSHIMmsiK, RawData

Stored Procedure — sp_GetLatestVesselsByMmsi

CREATE PROCEDURE sp_GetLatestVesselsByMmsi
    @SinceMinutes INT = 1440
AS
BEGIN
    WITH Ranked AS (
        SELECT *,
            ROW_NUMBER() OVER (PARTITION BY Mmsi ORDER BY IngestedAtUtc DESC) AS rn
        FROM AisPositionReport
        WHERE IngestedAtUtc >= DATEADD(MINUTE, -@SinceMinutes, GETUTCDATE())
    )
    SELECT r.*, vp.VesselName, vp.CallSign, vp.ShipType,
           vp.Destination, vp.Imo, vp.DimBow, vp.DimStern, vp.DimPort, vp.DimStar
    FROM Ranked r
    LEFT JOIN VesselProfiles vp ON r.Mmsi = vp.Mmsi
    WHERE r.rn = 1
    ORDER BY r.IngestedAtUtc DESC;
END

جداول EF Core

  • Vessels — Id, Mmsi, Name
  • VesselProfiles — تمام فیلدهای پروفایل
  • ProviderStatus — وضعیت ارائه‌دهندگان AIS
⚙️

پیکربندی

پیکربندی برنامه در src/AisFeedParser.Api/appsettings.json قرار دارد و از الگوی Options Pattern با کلاس‌های strongly-typed استفاده می‌کند.

ساختار کامل پیکربندی

{
  "Database": {
    "UseInMemoryRepo": false,          // استفاده از مخزن درون‌حافظه‌ای
    "ConnectionString": "Data Source=ais.db"
  },
  "Ais": {
    "Hosts": [                         // میزبان‌های AIS
      {
        "Host": "ais.example.com",
        "Port": 1234,
        "Transport": "Tcp",           // Tcp یا Udp
        "Passives": [ ... ]           // میزبان‌های جایگزین
      }
    ],
    "ParserWorkers": 1,                // تعداد کارگرهای ParserService
    "ChannelCapacity": 50000,          // ظرفیت Channel‌ها
    "InitSend": "HELLO\r\n",           // پیام اولیه TCP
    "RawLogFolder": "raw",             // پوشه لاگ خام
    "Reconnect": {
      "InitialMs": 500,                // تاخیر اولیه اتصال مجدد
      "MaxMs": 30000                   // حداکثر تاخیر
    },
    "Replay": {
      "Enabled": true,                 // فعال‌سازی بازپخش
      "Folder": "raw",                 // پوشه لاگ
      "MaxLinesPerSecond": 1000        // نرخ بازپخش
    }
  },
  "Sql": {
    "Provider": "SqlServer",           // SqlServer یا Sqlite
    "ConnectionString": "...",
    "Writer": {
      "BatchSize": 300,                // اندازه دسته نوشتن
      "FlushMs": 200                   // فاصله فلاش
    }
  },
  "Serilog": {
    "MinimumLevel": "Information",
    "WriteTo": [
      { "Name": "Console" },
      {
        "Name": "File",
        "Args": {
          "path": "logs/ais-.log",
          "rollingInterval": "Day",
          "retainedFileCountLimit": 14,
          "fileSizeLimitBytes": 52428800
        }
      }
    ]
  }
}

کلاس‌های Options

کلاسبخش پیکربندیفیلدهای کلیدی
AisOptions"Ais"Hosts, ParserWorkers, ChannelCapacity, RawLogFolder, Reconnect, Replay
DatabaseOptions"Database"UseInMemoryRepo, ConnectionString
SqlOptions"Sql"Provider, ConnectionString, Writer (BatchSize, FlushMs)
🧪

آزمون‌ها

آزمون‌ها با استفاده از چارچوب xUnit نوشته شده‌اند. در مجموع بیش از ۵۰ آزمون واحد وجود دارد.

AisDecoderTests.cs (۳۵+ آزمون)

دسته آزمونتعدادتوضیح
Type 1/2/3 Position۵+Lat, Lon, SOG, COG, Heading, NavStatus, ROT
Type 4 Base Station۳+مختصات و UTC Timestamp
Type 5 Static/Voyage۵+نام، CallSign, IMO, ابعاد, ETA, پیش‌فرض‌ها
Type 18 Class B۳+موقعیت و نوع CS
Type 19 Extended B۳+موقعیت + داده استاتیک
Type 24 Part A/B۴+نام (Part A), CallSign/ابعاد (Part B)
Type 27 Long Range۳+موقعیت با دقت کمتر
Edge Cases۶+مختصات null, تاریخ نامعتبر, نوع ناشناخته

AisDecoderSampleDataTests.cs (۱۴+ آزمون)

آزمون‌های مبتنی بر داده‌های واقعی از فایل ais_temp.txt:

  • تبدیل payload به بیت
  • مونتاژ پیام‌های چندبخشی
  • محدوده مختصات (Lat: -90..90, Lon: -180..180)
  • اعتبارسنجی ابعاد (غیرمنفی)
  • معیار کارایی: پارس زیر ۱۰۰ میلی‌ثانیه

ابزار کمکی SetBits

// متد کمکی برای تنظیم بیت‌ها در آزمون‌ها
private static string SetBits(string payload, int offset, int length, uint value)
{
    var bits = SixBit(payload);
    for (int i = 0; i < length; i++)
        bits[offset + length - 1 - i] = ((value >> i) & 1) == 1;
    return string.Concat(
        Enumerable.Range(0, bits.Length / 6)
            .Select(i => SixBitChars(
                Enumerable.Range(0, 6).Select(j => bits[i * 6 + j] ? 1 : 0).ToArray()
            ))
    );
}
🚀

استقرار و CI/CD

Azure DevOps Pipeline

# azure-build-pipelines.yml
trigger:
  - main

pool:
  vmImage: 'windows-latest'

steps:
  - task: NuGetToolInstaller@1
  - task: NuGetCommand@2
    inputs:
      restoreSolution: 'AisFeedParser.sln'
  - task: DotNetCoreCLI@2
    inputs:
      command: 'build'
      projects: 'src/**/*.csproj'
  - task: DotNetCoreCLI@2
    inputs:
      command: 'test'
      projects: 'tests/**/*.csproj'
    enabled: false  # فعلاً غیرفعال
  - task: DotNetCoreCLI@2
    inputs:
      command: 'publish'
      publishWebProjects: true
      arguments: '--configuration Release --output $(Build.ArtifactStagingDirectory)'
  - task: PublishBuildArtifacts@1

Docker — استقرار ظرف‌سازی شده

Dockerfile (API)

چندمرحله‌ای: build با SDK 8.0، اجرا با aspnet 8.0.

docker-compose.yml

محیط SQL Server محلی برای توسعه و آزمایش.

متغیرهای محیطی

متغیرتوضیح
CONNECTIONSTRINGS__SQLرشته اتصال SQL Server
AIS__HOSTS__0__HOSTمیزبان AIS
ASPNETCORE_ENVIRONMENTمحیط اجرا (Development/Production)

پیکربندی محیط‌ها

محیطپایگاه دادهفایل پیکربندی
توسعه (Development)SQLite (ais.db)appsettings.Development.json
تولید (Production)SQL Serverappsettings.Production.json
📋

توسعه مبتنی بر مشخصات

پیشنهادات تغییر (Changes)

پیشنهادوضعیتتوضیح
CHANGE-001-GROUP-BY-MMSI-LATEST تکمیل گروه‌بندی آخرین داده‌ها بر MMSI
PHASE1_DECODER_ENHANCEMENT تکمیل بهبود رمزگشای فاز ۱
PHASE2_VESSEL_PROFILES تکمیل پروفایل کشتی فاز ۲
PHASE3_API_ENHANCEMENTS تکمیل بهبود API فاز ۳
PHASE4_PERFORMANCE تکمیل بهینه‌سازی کارایی فاز ۴
add-vessel-api-endpoints در حال اجرا افزودن نقاط پایانی API کشتی
add-vessel-profiles در حال اجرا افزودن پروفایل کشتی
optimize-vessel-performance در حال اجرا بهینه‌سازی کارایی کشتی

گردش کار OpenSpec

  1. Proposal: ایجاد proposal در openspec/changes/<نام>/proposal.md
  2. Implementation: پیاده‌سازی مطابق proposal + به‌روزرسانی spec
  3. Archive: بایگانی change در openspec/changes/<نام>/

فایل‌های .windsurf/workflows/ شامل خودکارسازی‌های windsurf برای این گردش کار هستند.

📎

پیوست

راهنمای شروع سریع (Quick Start)

# ۱. کلون پروژه
git clone <repository-url>
cd ais-feed-parser

# ۲. باز کردن در Visual Studio
AisFeedParser.sln

# ۳. اجرا با SQLite (پیش‌فرض)
cd src/AisFeedParser.Api
dotnet run

# ۴. اجرا با SQL Server (نیازمند Docker)
docker-compose up -d     # راه‌اندازی SQL Server
dotnet run --environment Production

ساختار فایل‌های مستندات

فایلزبانتوضیح
README.mdانگلیسیراهنمای اصلی
README.fa.mdفارسیراهنمای فارسی
docs/Architecture.mdانگلیسیمعماری سیستم
docs/Configuration.mdانگلیسیراهنمای پیکربندی
docs/DataModel.mdانگلیسیمدل داده
docs/Deployment.mdانگلیسیراهنمای استقرار
docs/Operations.mdانگلیسیراهنمای عملیاتی
docs/ReplayAndRecovery.mdانگلیسیبازپخش و بازیابی
docs/Troubleshooting.mdانگلیسیرفع مشکلات
docs/fa/*.fa.mdفارسیترجمه فارسی تمام مستندات
docs/fa/FULL_DOCUMENTATION.mdفارسیمستندات کامل فارسی

خلاصه فایل‌ها و آمار

معیارمقدار
تعداد کل فایل‌های سورس۶۰+ فایل
خطوط کد تخمینی (C#)۵,۵۰۰+ خط
تعداد پروژه‌ها۶ پروژه (۴ اصلی + ۲ تست)
تعداد کنترلرهای API۵ کنترلر
تعداد سرویس‌های پس‌زمینه۶ سرویس
تعداد انواع پیام AIS پشتیبانی شده۱۰ نوع
تعداد آزمون‌های واحد۵۰+ آزمون
پایگاه داده پشتیبانی شده۲ نوع (SQLite, SQL Server)

داده‌های تماس و منابع

  • CI/CD: Azure DevOps Pipelines
  • ظرف‌سازی: Docker + Docker Compose
  • مستندات: پوشه docs/ و docs/fa/