uschen, ниже представлено моё видение ситуации, тск ab ovo. Я покопался в кодах, плюс соответствующих комментариях их автора на его сайте ( http://www.virtualradarserver.co.uk ), и, надеюсь, удовлетворю Ваше любопытство.
bootblack, можете относиться к моему эссе, как угодно. Возражения рассматриваются, если Вами будут предъявлены аргументы одного уровня с моими суждениями. То есть, с образцами открытого исходного кода, которые противоречат моим выводам. Пустотелые аргументы, типа "по мнению программистов, просмотревших коды", не принимаются.
Поехали!
Сервер VRS может прослушивать поступающие сообщения, - через сетевое подключение или с помощью последовательного порта 30003, - представленные тремя форматами. Принято обозначать их аббревиатурами SBS3, Port30003 и Beast. Последний, ко всему прочему, включает три текстовых формата и один бинарный.
Как бы то ни было, но сообщения всех форматов проходят обработку (прослушивание) с помощью одного и того же объекта (интерфейса) открытого исходного кода (ОИК) - он именуется IListener.
Ссылка (для «заумных» ) на его описание, а также свойства, методы и события (по сути, это только отправная точка, дальше надо копать и копать ):
http://www.virtualradarserver.co.uk/Sou … 7f843b.htm
Но, по сути, этот интерфейс только предоставляет классы ОИК, и всю последующую работу по извлечению байтов и их декодированию осуществляют другие объекты ОИК.
Непосредственно прослушивают входящие каналы, переводя потоки бинарных сообщений в байты (посредством библиотечных классов), интерфейсы ITcpListenerProvider (сетевое подключение) и ISerialListenerProvider (последовательное подключение).
Сответственно, декодирование осуществляют интерфейсы ISbs3MessageBytesExtractor, IPort30003MessageBytesExtractor и IBeastMessageBytesExtractor, тоже с помощью соответствующих библиотечных классов.
В итоге, все полученные сообщения, включая часть из них, не относящихся к формату Port30003 (raw и текстовые Beast), таки ретранслированы в него, и далее реализуется следующая нумерованная цепочка вызовов методов его обработки (ссылку на ОИК я уже предоставлял, поэтому любой желающий – и «заумный»! - может удостовериться в справедливости нижеописанного):
1. private void ProcessPort30003MessageBytes(ExtractedBytes extractedBytes)
///Переводит байты сообщения, полученного по каналу Port30003, в выходной объект сообщения и помещает его в очередь фонового потока.
(...var translatedMessage = _Port30003MessageTranslator.Translate(port30003Message);
... _MessageProcessingAndDispatchQueue.Enqueue(new MessageDispatch() { Port30003MessageEventArgs = new BaseStationMessageEventArgs(translatedMessage)...)
2. ///Вызывается, когда фоновый поток извлекает сообщение из очереди сообщений.
private void MessageQueue_MessageReceived(BaseStationMessageEventArgs args)
(...TrackFlight(args.Message); ...)
3. private void TrackFlight(BaseStationMessage message)
///Либо открывает новую запись в таблице базе данных полётов (если самолёта нет в списке отслеживаемых), либо модернизирует уже существующую. Создаёт переменную метода, содержащую текущее время и дату получения сообщения на обработку.
Новая запись открывается, если в коллекции _FlightMap записей полётов flightRecords не обнаруживается идентификатор самолёта Icao24, содержащийся в полученном сообщении message:
if(!_FlightMap.TryGetValue(message.Icao24, out flightRecords))
Тогда:
(...var localNow = Provider.LocalNow;
... flightRecords.Flight = CreateFlight(localNow, flightRecords.Aircraft.AircraftID, message.Callsign);
...)
Если же самолёт имеется в списке отслеживаемых, поле StartTime таблицы базы FlightsTable данных остаётся неизменным, а вот поле EndTime локальной переменной flight подготавливается к перезаписи в эту таблице, получая текущее значение локального времени:
(...var flight = flightRecords.Flight;
... flight.EndTime = localNow;)
4. private BaseStationFlight CreateFlight(DateTime localNow, int aircraftId, string callsign)
///Подготавливает запись в таблицу полётов базы данных, присваивая полю StartTime локальной переменной result значение текущего локального времени localNow.
(...var result = new BaseStationFlight() {
... StartTime = localNow, ...}
_Database.InsertFlight(result);...)
5. public void InsertFlight(BaseStationFlight flight)
/// Продолжает подготовку записи в базу данных полётов, включая и интересующие нас поля StartTime и EndTime таблицы данных полёта FlightsTable .
(...flight.StartTime = SQLiteDateHelper.Truncate(flight.StartTime);
flight.EndTime = SQLiteDateHelper.Truncate(flight.EndTime)
...)
Последний оператор,
SQLiteDateHelper.Truncate(...)
производит перевод системного формата времени в формат базы данных.
(...public static DateTime Truncate(DateTime dateTime)
return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second)...)
6. private static void UpdateFirstLastValues(BaseStationMessage message, BaseStationFlight flight, FlightRecords flightRecords)
/// Если производится запись в таблицу FlightsTable данных полётов для первого сообщения от самолёта, заполняются все её поля (First и Last), если же сообщение не первое (First поля – не «нулевые»), обновляются лишь те поля, которые отвечают текущему состоянию полёта (Last).
7. if(_Connection != null) flight.FlightID = _FlightTable.Insert(_Connection, _TransactionHelper.Transaction, _DatabaseLog, flight);
///Непосредственно осуществляет запись в таблицу данных полёта FlightsTable и возвращает её (записи) ID, и далее, с этим индификатором, запись вносится в текущую карту отслеживаемых полётов:
_FlightMap.Add(message.Icao24, flightRecords);
Вывод: в полях StartTime и EndTime таблицы FlightsTable базы данных сервера, на самом деле, как и извещают комментарии автора кода в ОИК, – это было доведено мною до общего форумного сведения изначально, - содержится локальное UTC-время фиксации получения сервером первого сообщения типа BaseStationMessageType.Transmission (суть «MSG») от конкретной станции наблюдения (StartTime), и, соответственно, локальное UTC-время фиксации получения сервером последнего сообщения типа «MSG» (EndTime).
То есть, при этом совершенно неважно, с каким «содержанием» (любая из 8 комбинаций передаваемых параметров полёта) было принято первое (последнее) сообщение «MSG», так как идентификатор ICAO24 имеется в любом из них.
И, разумеется, никакого ожидания получения какой бы то ни было «комплектации» сервером «полных» мифических «пакетов» данных некой последовательности сообщений, с последующей фиксацией в поле StartTime таблицы FlightsTable момента возникновения этого несуществующего «события» им не производится.
Чтд.