Как сделать ваш встроенный код C невосприимчивым к изменениям требований, не добавляя при этом слишком много накладных расходов и сложности? - PullRequest
9 голосов
/ 12 июня 2009

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

Какие типы C-конструкций вы обычно используете для достижения лучшего из обоих миров (гибкость и реконфигурируемость без потери эффективности)?

Если у вас есть время, пожалуйста, продолжайте читать, чтобы точно понять, о чем я говорю.

Когда я разрабатывал встроенное ПО для контроллеров подушек безопасности, у нас была проблема, заключающаяся в том, что нам приходилось менять некоторые части кода каждый раз, когда заказчик передумал в отношении конкретных требований. Например, комбинация условий и событий, которые могли бы вызвать срабатывание подушки безопасности, менялась каждые две недели во время разработки. Мы ненавидели так часто менять этот кусок кода.

В то время я присутствовал на конференции по встраиваемым системам и услышал блестящую презентацию Стивена Меллора под названием «Как справиться с изменяющимися требованиями». Вы можете прочитать статью здесь (они регистрируют вас, но это бесплатно).

Основная идея этого заключалась в том, чтобы реализовать поведение ядра в вашем коде, но сконфигурировать конкретные детали в форме данных. Данные - это то, что вы можете легко изменить, и они могут даже программироваться в EEPROM или другом разделе флэш-памяти.

Эта идея звучала замечательно, чтобы решить нашу проблему. Я поделился этим со своим коллегой, и мы сразу же приступили к переработке некоторых модулей программного обеспечения.

При попытке использовать эту идею в нашем коде мы столкнулись с некоторыми трудностями в реальной реализации. Наши конструкции кода стали ужасно тяжелыми и сложными для встроенной системы с ограниченными возможностями.

Чтобы проиллюстрировать это, я остановлюсь на примере, который я упомянул выше. Вместо того, чтобы иметь кучу операторов if, чтобы решить, находилась ли комбинация входов в состоянии, которое требовало развертывания подушки безопасности, мы перешли на большую таблицу таблиц. Некоторые из условий не были тривиальными, поэтому мы использовали много указателей на функции, чтобы иметь возможность вызывать множество маленьких вспомогательных функций, которые каким-то образом решали некоторые из условий. У нас было несколько уровней косвенности, и все стало трудно понять. Короче говоря, мы использовали много памяти, времени выполнения и сложность кода. Отладка не была простой. Босс заставил нас изменить некоторые вещи обратно, потому что модули становились слишком тяжелыми (и он, возможно, был прав!).

PS: Есть такой же вопрос в SO, но похоже, что фокус другой. Адаптация к меняющимся требованиям бизнеса?

Ответы [ 7 ]

3 голосов
/ 12 июня 2009

В качестве еще одной точки зрения на изменение требований ... требования входят в построение кода . Так почему бы не принять мета-подход к этому:

  1. Отдельные части программы, которые могут измениться
  2. Создайте скрипт, который склеит части источника вместе

Таким образом, вы поддерживаете совместимые логические блоки в C ... и затем соединяете эти совместимые части вместе в конце:

/* {conditions_for_airbag_placeholder} */
if( require_deployment)
     trigger_gas_release()

Тогда поддерживайте независимые условия:

/* VAG Condition */
if( poll_vag_collision_event() )
    require_deployment=1

и еще один

/* Ford Conditions */
if( ford_interrupt( FRONT_NEARSIDE_COLLISION )) 
    require_deploymen=1

Ваш скрипт сборки может выглядеть так:

BUILD airbag_deployment_logic.c WITH vag_events
TEST airbag_deployment_blob WITH vag_event_emitter

Мышление действительно громко. Таким образом, вы получите жесткий двоичный двоичный объект без чтения в конфигурации. Это похоже на использование оверлеев http://en.wikipedia.org/wiki/Overlay_(programming), но во время компиляции.

2 голосов
/ 25 июня 2009

Я не делаю код невосприимчивым к изменениям требований как таковым, но я всегда отмечаю раздел кода, который реализует требование, помещая уникальную строку в комментарий Имея теги требований, я могу легко искать этот код, когда требование требует изменения. Эта практика также удовлетворяет процессу CMMI.

Например, в документе с требованиями:

Ниже приведен список Требования, связанные с RST:

  • [RST001] Джульетта ДОЛЖНА запускать RST с 5-минутной задержкой при включении зажигания выключен.

И в коде:

/* Delay for RST when ignition is turned off [RST001] */
#define IGN_OFF_RST_DELAY 5

...snip...

                        /* Start RST with designated delay [RST001] */
                        if (IS_ROMEO_ON())
                        {
                            rst_set_timer(IGN_OFF_RST_DELAY);
                        }
2 голосов
/ 12 июня 2009

Что вы пытаетесь сохранить точно? Усилие кода переработать? Красная лента выпуска версии программного обеспечения?

Возможно, что изменение кода достаточно простое и, возможно, проще, чем изменение данных в таблицах. Перемещение часто меняющейся логики из кода в данные полезно только в том случае, если по какой-то причине требуется меньше усилий для изменения данных, чем для кода. Это может быть правдой, если изменения лучше выражать в форме данных (например, числовые параметры, хранящиеся в EEPROM). Или это может быть правдой, если запросы клиента требуют выпуска новой версии программного обеспечения, а создание новой версии программного обеспечения является дорогостоящей процедурой (большое количество документов или, возможно, OTP-чипы, сожженные производителем чипов).

Модульность - очень хороший принцип для подобных вещей. Похоже, вы уже делаете это в некоторой степени. Полезно стремиться изолировать часто меняющийся код на как можно меньшую область и стараться, чтобы остальная часть кода («вспомогательные» функции) была отдельной (модульной) и максимально стабильной.

2 голосов
/ 12 июня 2009

Наша система подразделяется на множество компонентов с открытой конфигурацией и контрольными точками. Существует файл конфигурации, который читается при запуске, который фактически помогает нам создавать экземпляры компонентов, прикреплять их друг к другу и настраивать их поведение.

Это очень похоже на OO, в C, со случайным взломом для реализации чего-то вроде наследования.

В мире защиты / авионики обновления программного обеспечения очень строго контролируются, и вы не можете просто обновить ПО для устранения проблем ... однако по какой-то причудливой причине вы можете обновить файл конфигурации без серьезной борьбы. Поэтому было чертовски полезно, чтобы мы могли указывать большую часть нашей реализации в этих файлах конфигурации.

В этом нет волшебства, просто хорошее разделение интересов при разработке системы и немного предвидения со стороны разработчиков.

1 голос
/ 12 июня 2009

Для адаптации к изменяющимся требованиям я бы сконцентрировался на том, чтобы сделать код модульным и легко изменяемым, например. используя макросы или встроенные функции для параметров, которые могут измениться.

W.r.t. Конфигурация, которая может быть изменена независимо от кода, я надеюсь, что параметры, которые можно перенастраивать, также указаны в требованиях. Специально для таких важных для безопасности вещей, как контроллеры подушек безопасности.

1 голос
/ 12 июня 2009

Полагаю, вы могли бы указать несколько допустимых вариантов поведения на основе байта или слова данных, которые вы можете извлечь из EEPROM или порта ввода-вывода, если необходимо, а затем создать общий код для обработки всех возможных событий, описанных байт.

Например, если у вас есть байт, который определяет требования для освобождения подушки безопасности, это может быть что-то вроде:

Бит 0: заднее столкновение

Бит 1: скорость выше 55 миль в час (бонусные баллы за обобщение значения скорости!)

Бит 2: пассажир в автомобиле

...

Etc

Затем вы вводите еще один байт, который сообщает, какие события произошли, и сравниваете их. Если они одинаковы, выполните вашу команду, если нет - не выполняйте.

0 голосов
/ 12 июня 2009

Использование динамического языка может быть спасением, если у вас есть память и мощность процессора для него.

Попросите С поговорить с оборудованием, а затем передайте известный набор событий на язык, такой как Lua. Пусть скрипт Lua проанализирует событие и обратный вызов соответствующей функции (ей).

После того, как ваш код на C работает нормально, вам не нужно будет снова его трогать, пока не изменится оборудование. Вся бизнес-логика становится частью сценария, который, на мой взгляд, намного проще создавать, изменять и поддерживать.

...