Формат файла описания загрузки

Файл конфигурации загрузки представляет собой текстовый файл в формате ASCII с разделителями строк символами с кодами 0D или 0D0A. Данный файл сообщает диспетчеру РМ информацию о том, какие именно расчётные модули необходимо загрузить, под какими именами алгоритмов и с каким интервалом их необходимо вызывать в процессе работы прибора.

Файл представляет собой таблицу, где на каждой строке описывается способ вызова расчётного модуля. Строки имеют следующий формат:

<имя исполняемого файла расчётного модуля> <имя алгоритма> <интервал вызова в мсек> <к-во вызовов (повторов) за один интервал вызова данного расчётного модуля>

….. и так на остальных строках. Разделитель слов в строке – пробел.

Пример текста в файле:

сalc algo1 60 1
сalc algo2 120 2
Это означает, что первым выполняется расчётный модуль загружаемый из исполняемого файла calc с именем алгоритма algo1, который вызывается 1 раз за 60 мсек, а вторым выполняется расчётный модуль загружаемый из исполняемого файла calc с именем алгоритма algo2, который вызывается 2 раза за 120 мсек.
Примечание: такты (периоды) должны быть кратными (то есть такты выполнения должны быть в целое количество раз больше или равно минимальному среди указанных тактов алгоритмов)! Минимальный временной интервал определяется автоматически исходя из файла конфигурации загрузки как минимум по всем заданным тактам. Такт не может быть задан меньше 1 мсек.
Исходный код функции takt_work, где находится цикл вызовов приведён ниже. График работы в соответствии с указанной (в примере выше) конфигурацией:

Процессы в данной версии исполняемой среды выполняются последовательно, в соответствии с тем как они описаны в файле конфигурации загрузки. При этом если у процессов установлен разный такт выполнения, то общий такт будет равен минимальному из заданных, но алгоритм у которого период выполнения задан больше будет выполняться не на каждом шаге. Т.е например для приведённого примера algo1 будет выполняться на каждом шаге, а algo2 – через шаг. Время, выводимое для отладчика (см. описание сетевого протокола GdbServer) равно: (минимальный из заданных шагов)*(к-во циклов диспетчера). При вызовах run-функции в расчётном модуле время равно 0, поскольку в явном виде нигде там не используется.

Такты выполнения расчётных модулей являются задаваемой и постоянной величиной. Для более подробной информации – см. исходный код DispExemod\main.c, функция takt_work. Ниже приведён фрагмент данной функции с кодом расчёта условного модельного времени:

void takt_work(void)
{
 int count = 0;
 int k = 0;
 uint64_t cycle1 = 0;
 uint64_t cycle2 = 0;
 uint64_t ncycles = 0;
 uint64_t cps = 0;
 struct timespec req = { 0 };
 double delta_scan = 0.0;
 double time_sleep = 0.0;
 char cmd[1] = {0};
 char rep[1] = {0};
 /*Цикл посылки сообщений для тестирования*/
 while (1)
 {
 /*Количество циклов процессора до начала обработки бд*/
 cycle1 = ClockCycles( );
 /*Будем запускать на выполнение расчетные модули время
 * которых пришло
 */
 for (count = 0; count < ptr_header->number_exemod; count++)
 {
 /*Текущий счетчик в 0 значит время пришло*/
 if (ptr_exemod[count].tek_time == 0)
 {
 /*Отправим на выполнение расчетный модуль стоько
 * сколько он должен выполняться за один такт
 */
 for (k = 0; k < ptr_exemod[count].num_work; k++)
 {
 cmd[0] = 0x01;
 MsgSend(ptr_exemod[count].coid, cmd, sizeof(cmd), rep, sizeof(rep));
 } /*for (k = 0; k < ptr_exemod[count].num_work; k++)*/
 /*Восстановим текущее время*/
 ptr_exemod[count].tek_time = ptr_exemod[count].takt_mod;
 } /*if (ptr_exemod[count].tek_time == 0)*/
 } /*for (count = 0; count < ptr_header->number_exemod; count++)*/
 /*Количество циклов процессора после обработки бд*/
 cycle2 = ClockCycles( );
 /*Количество циклов ушедшее на обработку бд*/
 ncycles = cycle2 - cycle1;
 /*Сколько циклов в секунде*/
 cps = SYSPAGE_ENTRY(qtime)->cycles_per_sec;
 /*Время затраченное на обработку бд*/
 delta_scan = (1000.0 * ((double) ncycles / cps));
 /*Время сна, с компенсацией времени на предыдущем шаге*/
 time_sleep = (double) ptr_header->takt - delta_scan;
 /*Спим оставшееся время до начала следующего такта*/
 nsec2timespec(&req, (uint64_t) ( time_sleep * 1000000L));
 /*Спим до начала следующего такта*/
 if (nanosleep(&req, NULL) == -1)
 {
 perror("nanosleep");
 } /*nanosleep.....*/
 /*Еще один такт прошел уменьшим время ожидания запуска
 * расчетных модулей
 */
 sheduler_takt();
 //Это счётчик своих тактов синхронизатора, по нему считаем время ptr_header – это главная общая область памяти диспетчера /header
 ptr_header->takt_counter = ptr_header->takt_counter + 1;
 } /*while (1)*/
}

Далее при выводе времени на клиент (то есть в графическую оболочку) используется следующий код (GdbServer.c функция packet_send):

int packet_send(void)
{
 header_packet header = {0};
 signal_addr var_signal_addr;
 struct queue_ *pkt = NULL;
 int sum = 0;
 int nbytes = 0;
 double f = 0;
 unsigned char *ptr_buf_packet = buf_packet + sizeof(header);
//Это код подготовки значения модельного времени прибора 
//для вывода его на схеме в графической оболочке в режиме отладки 
/*Шаг интегрирования - в секундах !!!*/
 header.fStep = ptr_header->takt*0.001;
//Это собственно время, выводимое в оболочке
 header.time_connect = ptr_header->takt_counter*header.fStep;
. . . . . . . . . . . . . .