Comments 52
Во всех картинках меня напрягают одиночные провода.

Вот так выглядит некластеризованное и кластеризованное решение:



Когда я не вижу вот этой характерной сетчатки на графике, мне становится тревожно.

Блин, три четверти статьи про дублирование механизмов, кода, методы контроля и обработки ошибок — а тут, оказывается, «провода не в сеточку». Текст-то читали?
Читaл. Проблема в том, что если хотя бы в одном месте есть узел, принимающий решение в единственном числе (например, компаратор, который должен сказать, какой из узлов «неправильный» — а сам единственный), то это приводит к точке отказа.

Кстати, ровно то же касается и каналов связи.

И вот что мне поразило глаз — это узлы, к которым идёт единственный канал связи, или которые стоят в единственном числе.
Вы, по-видимому, невнимательно читали.
  • связи так же дублируются. На обзорных схемах это обычно пропускают. Но например схематично это есть на рис 6. для PFC. Не всегда, правда по разным каналам передаётся одни и те же данные, да и это особо не нужно.

    Рассмотрим пример. Возьмём сферический ACE в вакууме от сторонней компании-поставщика электроники контроля. Что и как в остальной системе сделано — неизвестно. В нём, допустим, согласно тех заданию есть помимо внутренних связей для самодиагностики (общение между CC и MC так же продублировано двумя CAN), есть три входных канала ARINC. Назовём их для простоты PFCC(Channel)1, 2 и 3 соответственно. По ним передаются управляющие команды от главных компьютеров управления. А может быть это один компьютер, но вычисляющий трёмя разными способами? Нам не известно, но у нас три шины ARINC. Помимо этого у нас требуется ещё служебная шина ARINC для того, чтобы узнать, что происходит вокруг. Ведь как известно, подчинённые на местах осведомлены о состоянии гораздо лучше руководителей. Назовём такую шину DMC (Data Maintenance Channel). Итак, что мы имеем в итоге?

    У нас есть четыре физически независимых канала. По каждому из которых приходят команды. Рассмотрим следующие ситуации:

    1) Обрыв связи одного из PFCC не вызовет потерю управления. Тем не менее, в худшем случае это не объясняет, отключена ли система и два других дают неверную информацию, либо просто сломан один из каналов. Это решается тем, что: по каждому каналу идёт свой сигнал управления. В идеале они должны совпадать. Однако, PFC так же общаются друг с другом, поэтому знают о состоянии каждого из них. Каждый из PFC отсылает информацию о состоянии себя и других ACE. В этом случае принятие решения будет примерно таким:

    ((PFCC1 Command * PFC1 Valid) + (PFCC2C * PFC2V) + (PFCC3C * PFC3V))/Voter rule = ACE command

    В такой реализации принятие решения перекладывает на ACE. Он по сути является Voter'ом. Однако, в зависимости от режима и архитектуры может быть и такой вариант, когда три PFC приняли решение:

    (Consolidated PFC Command) ^Comparsion rule^ (Ace command) = Ace final command

    В зависимости от режима работы ACE выполняет как самопроверку, так и воплощает собой реализацию «доверяй, но проверяй». Хотя есть режимы и bypass, когда данные ACE могут игнорироваться (за исключением фатальных сценариев).

    И наконец, немаловажным является возможность собственного принятия решения ACE (например по полученным данным от DMC (допустим, там передаются дублированные от пилотов).

    Тогда сообразно усечённым законам управления в ACE:

    (ACE Control Laws Result * ACE Valid) ^Comparsion rule^ (Consolidated PFC Command) ^Comparsion rule^ (Ace command) = Ace final command

    в одном из сложных вариантов.

    Т.е. система будет функционировать в режиме тяжелого отказа. Хотя конечно тут алгоритмы сложнее, они ещё должны учитывать внутреннее состояние, внешнее, режим работы.

    2) Отказ ACE. Это наиболее простой случай, т.к. обычно отказ ACE обнаруживается системой самодиагностики или через Feedback консолидацию от состояния поверхности и положения ручек.

    (Consolidated PFC Command) ^Comparsion Rule^ (Position Status ^Check^ Feedback) => ACE Validity

    В этом случае включается парный ACE при наличии. В случае потери двух ACE (или более, если они продублированы большим числом) — теряется вся поверхность.

    Итого:

    1) в безопасной архитектуре все связи продублированы, в том числе каналы передачи данных, быть может, за исключением связей с некритичными системами и узлами (таких, как единичные датчики, вторичные системы)
    2) архитектура должна избегать «единственного узла» связи, функциональность должна частично дублироваться. Система не содержит внешних, не входящих в вычислители Voter'ов. Решение принимается всегда сообща, в том числе на разных уровнях системы. За исключением специальных режимов, например таких, как Direct Mode.
Компьютерным системам бы такую избыточность всех узлов, не ложились бы датацентры и хостинги… Ну а про качество кода, я думаю, и упоминать не стоит
Вы не поверите… Но некоторые компьютерные системы стоят буквально как самолёты :)
Прочитал статью. Слава богу, в стране есть ещё настоящие инженеры. Спасибо.
подозреваю, что некоторые вещи дублировать не получится. Например, протоколы, по которым системы общаются между собой. Или протоколы, по которым происходит их переключение с одного контура на другой.

Как решается эта проблема? Путём максимального упрощения протоколов чтобы избежать потенциальных ошибок?
В том числе путём упрощения. Например новые версии протоколов ARINC хороши, но не всегда используются, т.к. не атомарно просты, в отличие от топорных и старых. Но всё-таки главными являются решения, о которых я ответил двумя комментариями выше. Для протокола, даже если он единственный, существуют разные сигнатуры для подписей сообщений и\или различная калибровка на разных каналах (скорость, чётность и т.п.).
Почему в последнем примере nTmp & 0x55U, а не nTmp & FIFTYFIVE_U_C? Почему только операция сравнения заменена на EQU_C? С тем же успехом можно проморгать bSuccess == FALSE;

Я не придираюсь, просто интересно, осознанный ли это выбор или «так вышло».
>Почему в последнем примере nTmp & 0x55U, а не nTmp & FIFTYFIVE_U_C?
Потому, что NULL_C в зависимости от компилятора и стандарта может быть заменено, например на NULL или nullptr. А 0x55U в любом компиляторе и стандарте останется 0x55U. Заводить дефайты на все числа от нуля до бесконечности — хлопотное дело.

>Почему только операция сравнения заменена на EQU_C?
Я так думаю, по тем же причинам, почему пишут if (5 != a ). Чтобы в условии не написать присваивание случайно.
>А 0x55U в любом компиляторе и стандарте останется 0x55U
Угу, а при каких условиях TWO_U_C и FOUR_U_C будут не 2 и 4? О том и речь, что хлопотно, но не понятна выборочность.

>Чтобы в условии не написать присваивание случайно
Это-то понятно. Но если мы не доверяем предупреждениям компилятора в сравнении, то почему не боимся делать похожие ошибки: nTmp && 0x55U, bSuccess == FALSE?
Выборочность определяется тем, что NULL — это условность, абстракция, которая может быть 0, 0L, NULL, nullptr в зависимости от обстоятельств. А «55» — это число 55, в любом компиляторе и стандарте, всегда и везде.

На счет «мы не доверяем предупреждениям компилятора» — ага, не доверяем, потому что не все компиляторы и не на всех уровнях их показывают. Почему мы не боимся делать «bSuccess == FALSE» — потому, что символа "==" в коде быть не должно, это может легко проверяться каким-нибудь статическим анализатором. На счёт «nTmp && 0x55U» — нет идей.
Вы совершенно правы.

Тем не менее существуют некоторые послабления. Которые, как в этом swap-примере из-за требований уровня B, а не A. Например, послабление из-за того, что магические числа не всегда надо определять константами, в случае если они используются единожды в коде.

Все требования исходят из Coding Standard. Что и когда применять. В том числе и замена операторов.
На самом деле, такой подход просто позволяет избегать ошибок во время кодирования. Во время сборок код полностью вычищается от Warning'ов компилятора и от error\warning\fail'ов статических анализаторов. А потом код ревью и всё что я писал в прошлой статье.
По-моему большую часть этого кодстайла (который превращает Си в несколько другой язык) можно заменить набором warnings-as-error. И это будет лучше, так как проверяется автоматически. А соответствие кодстайлу надо ревьюить.
В-общем так и есть. Но некоторые ошибки не отлавливают ни компилятор, ни static checker, но тем не менее, некритичные для них ошибки являются ошибками для конечного представления кода сертификационной комиссии.
«Мы работаем для того, чтобы вы не боялись летать»
Анализируя причины падений самолетов могу сказать что страхи порождает не то, как устроена авионика, а то, кто им (самолетом) управляет. Как на самом судне, так и с земли.
За статью спасибо.
Да, частично ЭДСУ позволяет уменьшить человеческий фактор. Например после инцидента над Боденским озером командам электроники о столкновении был присвоен приоритет выше, чем командам человека. К сожалению, любую защиту от дурака более глупый дурак поломает: принудительно перейдет в Direct Mode против Normal, или же попросту выдернет шнур, разрубит топором связку проводов, чтобы не мешала назойливая индикация.
Тут помимо вопроса недоверия к человеческому фактору и вообще исключения человеческого фактора ещё немаловажна проблема обучения персонала, если не управляющего, то контролирующего. Как бы ни была умна и надёжна техника, пока во главе её должны стоять более умные существа — люди разумные.
Программирование сегодня — это гонка разработчиков программ, стремящихся писать программы больше и с лучшей идиотоустойчивостью, и вселенной, которая пытается создавать больших и лучших идиотов. Пока вселенная побеждает.
— Rick Cook
> что ПО управления Boeing 777 написано на ada.
777 был сделан в начале 90х. Пик расцвета Ады в тамошнем ВПК и связанным с ним отраслями.
До СССР краешком дошло, ходил по ящикам какой-то транслятор для СМ4 неизвестно кем сделанный, но как раз в этот момент все навернулось медным тазом.
Вы много времени посвятили описанию как избавится от ошибок, это просто замечательно, однако вы же не можете избавится от всех них? И редкие ошибки остались, так вот как вы их отлавливаете?

На самолёте есть блок который следит за отказами других приборов и логирует их? или вы просто ждёте когда самолёт упадёт и только потом принимаетесь за поиск ошибок?
Есть, обычно для каждого блока электроники есть хранилище ошибок (faults) и предупреждений (warnings) в NVM (None Volatile Memory). Во время наземного обслуживания эти логи загружаются и анализируются. Не совсем уверен насчёт централизованного хранилища (уверен, оно есть — фатальные данные передаются, например в FMS (?)), но данные во время обслуживания загружаются со всего кабинета, и, если надо, то с каждого блока в отдельности. К каждой ошибке прилагается штамп времени, и если есть реализация — выдержка из параметров системы на момент ошибки.

Для некоторых реализаций, когда замечена ошибка или найдена особенность работы системы после её сертификации и во время эксплуатации; могут быть внесены изменения в систему без обновления ПО систем управления. Это так называемое Field Loadable Software. Встречается редко, не затрагивает жизненно важных параметров, правда, но позволяет немного подлатать самолёт до внедрения исправленной версии. Такое обслуживание, конечно, доступно только специальным инженерам по обслуживанию. Обычно из штата самих разработчиков систем управления.
:) Охранники с автоматами. Ну и связка пароля на девайс и пин-код для устройства обслуживания.
C каждой статьей проникаюсь все большим уважением к отрасли! Спасибо!
Гм, а если CanRxMsg_ip != null, и как это водится был передан указатель на освобожденный объект?
Это интересная проблема. Или переполнение стека.
Как одно из возможных решений — Validity Pattern, который при инициализации\уничтожении выставлять уникальное магическое число и стирать его для каждой функции\объекта. Но вообще именно «освобожденных объектов» и вообще динамических объектов для безопасности стараются не использовать. Память выделяется \ освобождается и объекты создаются \ уничтожаются единожды, для надёжности критичные объекты жестко примаплены в девайсе.
Странно, почему не передается reference или smart pointer? Если от второго отказываются из-за производительности, то уж по референсу NULL не передать никак.
1) Ссылок в C нет. Эта конструкция C++. В моём примере и в тех проектах, над которыми я работал, используется Pure C. Возможно, в проектах на C++ эта проблема решена именно ссылками.
2) Smart Pointer — абстракция из стандартной \ boost библиотеки. Их, согласно требованиям авионики использовать нельзя в чистом виде. В виде Validity Pattern реализация может быть похожа на реализацию библиотечного smart pointer.
А с переполнением стека как боретесь? И еще, предусмотрены ли таймауты на выполнение функций, чтобы избежать зацикливания?
Для этого предусмотрены StackMonitor, OverrunMonitor (фреймы не должны перекрывать друг друга).
В коде запрещены конструкции, ожидающие чего-либо без таймаута, за исключением ожидания системных\хардварных прерываний. Во время тестирования всё это проверятся с разным calltree. На это существуют определённые требования к системе — сколько времени должен занимать фрейм, насколько загружен стек и т.п… Обычно все реализации таких мониторов являются предупреждающими событие, т.е. ловят заполнение стека до его переполнение по меткам. Вообще всё, что может рухнуть и поломаться проверяется загодя, до того, как передан сигнал аппаратной ошибки (devision by zero, overflow, stak overflow, underrun, overrun).
Используете ли вы какие-то open source решения для этих целей/выкладываете свои с открытым кодом? Я был бы очень рад например сертифицированному тщательно протестированному StackMonitor-у :)
К сожалению, код является закрытым.
Прецедентов, чтобы какой-либо код открывали — не было. Во всяком случае во время эксплуатации самолёта, во всяком случае официально.

Что касается использования open source решений, как я и писал раньше, если надо использовать что-то стандартное, оно должно быть переписано. Другими словами, просто анализируется существующее решение, оценивается его оптимальность (обычно оно максимально эффективно, но к нему добавляется дополнительная защита и проверки) и затем внедряется с изменениями в виде стиля кода.
Кстати говоря, реализация может существенно отличаться в зависимости от CPU. Хотя и стараются использовать код вторично, это не всегда эффективно. Например, в TMS320C28 есть свои рекомендации и механизма отлавливания Overflow против забития сигнатуры в область стека как c C166.
Вдохновляющая, по сути, статья.
Если уж с таким люди справляются, то в проектах где риска для жизни нет — можно сказать все совсем просто :)

Спасибо!
Уважаемый, объясните мне, пожалуйста — какой смысл жевать кактус и писать всё на этом аде (это даже не С)? 9/10 всего написанного в том примере кода обеспечил бы рантайм какого-нибудь Erlang'а или компилятор какого-нибудь Haskell'а/OCaml'а.
1) Нужен сертифицированный по авиационным стандартам компилятор.
2) Программы, написанные на Haskell, имеют значительный размер объектного кода и невысокую скорость исполнения. У меня нет данных, что написанный на этих языках будет так же хорошо транслироваться в байт-код и будет так же быстр для обеспечения жесткого реального времени ~3-4нс?
3) Отсутствие кодогенераторов, UML-платформы (как минимум сертифицированной) под эти языки.
4) Сейчас не так уж много программистов и тестеров сейчас знаете, работающих на этих языках.
5) Код разный бывает, это лишь один из простейших примеров, призванных показать стилистику.
6) Бедность языка и неуместность для работы с железками (а это 2\3 кода)
>Нужен сертифицированный по авиационным стандартам компилятор.
Вопрос желания.

Откуда ваша цитата не знаю, люди пишут DSL'и, если уж прям hard realtime нужен: en.wikipedia.org/wiki/Atom_(programming_language) Кроме того, в каких именно местах у вас такие требования? К компьютерам, заведующим вентиляцией салона или выпуском шасси (процессами, которые происходят секунды, а то и десятки секунд)?

Что касается кодогенераторов — вон чуть выше пример кодогенерации.

>Сейчас не так уж много программистов и тестеров сейчас знаете, работающих на этих языках.
Это задача о курице и яйце. Усугубляет её ужасающая трудоёмкость написания кода, подобного коду выше: для того, чтобы заменить строителей пирамиды руками на крановщиков, вам не нужны несколько сотен тысяч крановщиков, достаточно десятка.

>это лишь один из простейших примеров
Даже он дик.

>Бедность языка
Это ваше экспертное заключение?

>неуместность для работы с железками
Это как?
>>компилятор
>Вопрос желания.
Помимо желания это стоит больших денег и времени, которое тоже преобразуется во время. И без того дорогая разработка будет дороже.

> Atom
это замечательно, что есть такой Haskell. Теперь вокруг него надо выстроить техпроцесс, сертифицировать всё, написать стандарты, что тоже стоит денег. Бизнес не всегда благороден. Что это принесёт в конечном итоге? Через 5? 10 лет? Через 2? 3 проекта? В чём преимущества? Выгоднее ли это того, что есть? Думаю, не самые далёкие люди от техники такой анализ производят.

>Кроме того, в каких именно местах у вас такие требования
Вы точно читали статьи? Системы управления. Решения принимаются в зависимости за микросекунды, чтобы парировать ветер, чтобы избежать ошибки, если дрогнет рука пилота. Да даже если шасси, если они не выпустятся при посадке, то поднимать самолёт может быть поздно (особенно в случае аварии), кондиционирование? Не знаю, какие там системы и какие к ним требования, но при разгерметизации салона за секунды, а то и доли секунд нужно среагировать, чтобы спасти жизнь людям и выпустить те же кислородные маски.

>ужасающая трудоёмкость написания кода
Вы утрируете. Всё просто, что знаешь. Этот код писать просто, если изучить заблаговременно Software Design и Coding Standart. Код схожий и блочный. Только для пущего удобства IDE как в MS VS не хватает.

>>неуместность для работы с железками
>Это как?
Всё ли, относящееся к железу легко написать на них? Как быстро будет это работать? Как haskell работает с Interrupts (насколько я знаю — это наибольшая проблема)? Я знаю очень мало примеров низкоуровнего программирования на этих языках. То, что есть — начало появляться лишь намедни.

То, что используется в авиации — это прошедшие долгие процедуры технологии 5-10 летней давности, тщательно отобранные, задокументированные и разобранные вплоть до бита. Проверенные и надёжные.

Возможно, это изменяется и изменится и они будут применяться, если это будет просто и дёшево. Возможно, у них всё впереди.
Хороший вопрос. Самолёт в стадии разработки. Начало лётных испытаний планировалось на начало текущего лета. Речь идёт о C Series. Когда я над ним работал, его рисунок выглядел именно так, как модель в аэродинамической трубе + рендер с окошечками и бликами. Но в интернете ходят такие фотографии: image
Быть может вы и правы, спорить не буду. Фото взято с aviationnews.
Интересно, спасибо! Подскажите а есть ли какое-то автоматическое тестирование и как оно реализовано? Т.е. как примерно разработчики пишут тесты «если нажать кнопку, ввести неверные данные, то пользователь увидит сообщение об ошибке». А здесь есть что-то подобное, например «тестируем отказ такого-то датчика/привода», «тестируем неверную команду пилота/порыв ветра/приближающийся самолёт», и т.д. Наверняка должно что-то подобное быть, но как именно это происходит с учётом того что тестировать нужно не только код но и в связке с аппаратной частью. Есть ли какие-то требования к тестам?
Спасибо. Я достаточно подробно описал в прошлой статье процессы тестирования и инструменты.
Если интересно поподробнее узнать методологию составления тест кейсов, почитайте о систематическом тестировании.

Что касается ваших примеров — это сценарные тесты, обычно выполняются на полнофункциональной модели самолёта на земле либо уже в полёте на реальном самолёте. Наши лётчики-испытатели умудрялись тестировать «разрыв связи» и «отключение питания» в воздухе для всех систем сразу путём физического насилия, ну как и посадку без флаперонов. Наши — суровые ребята. В Airbus, к примеру, такое редкость.

Если есть подробные вопросы — задавайте, отвечу.
Сурово. Интересно выглядят методы «зачехления» такого острого инструмента, как Си.
К слову «актуаторы» — есть термин «исполнительный механизм», которое и следовало бы употребить :)
Only those users with full accounts are able to leave comments. Log in, please.