Cоединение отсутствует...

Научно-Производственное Объединение «Каскад-ГРУП»

428000, Россия, Чувашская Республика, г. Чебоксары, пр. Машиностроителей, д. 1 КГ

Телефон: (8352) 22-34-32,
Факс: (8352) 63-48-38

E-mail: abc@kaskad-asu.com

Реализация протокола в SoftLogic-системе «KLogic»

Разработка драйвера начинается с добавления поддержки протокола и устройств в инструментальную среду разработки «KLogic IDE». Для этого нужно сделать следующее:

  1. Добавить описание протокола в файл KLData\prot_mod_uni.xml в следующем виде (XML-формат):
    <Object Type="Protocol">
      <Settings>
        <sName>[UNI] Водосчетчики АС-001</sName>
        <sFullName>Протокол опроса водосчетчиков АС-001</sFullName>
        <sProtGroupName>Водосчетчики</sProtGroupName>
        <Controller>DECONT</Controller>
        <Controller>I-7000</Controller>
        <Controller>I-8000</Controller>
        <Controller>WKLOGIC</Controller>
        <Controller>None-target</Controller>
        <Controller>Теконик P06</Controller>
        <Controller>MOXA IA240</Controller>
        <Controller>MOXA</Controller>
        <TSName>UniTabSheet</TSName>
        <isExternalProt>True</isExternalProt>
        <isCounterProt>False</isCounterProt>
        <isVKTProt>False</isVKTProt>
        <ProtCode>34</ProtCode>
        <Properties>
          <Prop Id="1" Name="COM порт" Descr="Номер COM-порта контроллера, к которому подключены водосчетчики" Type="BYTE" Init="1"/>
          <Prop Id="2" Name="Период опроса, мсек" Descr="Периодичность опроса (в милисекундах) контроллером водосчетчиков" Type="DWORD" Init="1000"/>
          <Prop Id="3" Name="Количество повторных запросов" Descr="Количество повторных запросов при сбое по линии связи" Type="BYTE" Init="3"/>
          <Prop Id="4" Name="Тайм-аут по обмену" Descr="Тайм-аут по обмену (в мсек)" Type="DWORD" Init="2000"/>
          <Prop Id="5" Name="Тайм-аут между байтами" Descr="Тайм-аут между байтами (в мсек)" Type="DWORD" Init="20"/>
        </Properties>
      </Settings>
    </Object>

 

Здесь нужно изменить содержимое следующих секций:

  • sName – название протокола.
  • sFullName – полное наименование протокола (описание).
  • sProtGroupName – группа, к которой относится протокол.
  • Controller – наименование контроллера (см. файл KLData\cntrlrs.xml), к которому данный протокол применим. Должна быть по крайней мере одна секция с контроллером None-target для возможности разработки и отладки протокола под Windows.
  • ProtCode – уникальный код протокола, по которому он идентифицируется в бинарной конфигурации.
  • Properties – данная секция описывает свойства конкретного протокола, их состав целиком определятся программистом. В программном коде обращение к конкретному свойству будет осуществляться по его номеру.

Добавление протокола [UNI] Водосчетчики АС-001

 

Свойства протокола [UNI] Водосчетсики АС-001

2. Описать модули, работающие по данному протоколу, в *.io-файле (XML-формат):

<?xml version="1.0" encoding="windows-1251"?>
<!-- АС-001 -->
<KLogicModules>
  <Module Id="76">
    <Name>АС-001</Name>
    <CfgName>AS_001_Cfg</CfgName>
    <Descr>Водосчетчик ультразвуковой АС-001</Descr>
    <Protocol>[UNI] Водосчетчики АС-001</Protocol>
    <Properties>
      <Prop Id="1" Name="Адрес модуля" Descr="Сетевой номер водосчетчика АС-001" Type="BYTE" Init="0"/>
    </Properties>
    <TagProperties>
      <Prop IdStr="Id" Name="Идентификатор" Type="WORD" Access="H"/>
    </TagProperties>
    <TagTree>
      <Group Name="Измерения">
        <Tag Name="Расход м3/час" Descr="Текущий расход жидкости через прибор" Measure="м3/час" Type="AIF" Id="1"/>
        <Tag Name="Объем м3" Descr="Накопленный объем жидкости" Measure="м3" Type="AIF" Id="2"/>
        <Tag Name="Время наработки" Descr="Время работы прибора в часах" Measure="час" Type="AIF" Id="3"/>
        <Tag Name="Состояние прибора" Descr="True - работа, False - ошибка" Type="DI" Id="4"/>
      </Group>
      <Group Name="Служебные параметры">
        <Tag Name="Условный диаметр" Descr="Условный диаметр водосчетчика" Type="AOF" Id="5"/>
        <Tag Name="Отсутствие связи" Descr="True - связь отсутствует, False - присутствует" Type="DI" Id="6"/>
        <Tag Name="Опрос отключен" Descr="True - опрос отключен, False - включен" Type="DO" Id="7"/>
        <Tag Name="Время опроса" Descr="Время одного цикла опроса" Measure="мсек" Type="AII" Id="8"/>
      </Group>
    </TagTree>
  </Module>
</KLogicModules>

 

См. файл KLData\_example.io_ для получения справки по заполнению.

Свободные коды протоколов и идентификаторы модулей могут быть определены с помощью экспорта списка поддержанных протоколов и модулей.

Добавление модуля АС-001

Свойства модуля АС-001

Экспорт списка протоколов и модулей

На этом этапе уже можно попробовать добавить в KLogic IDE новый протокол и модули, посмотреть, все ли верно описано.

Вторым шагом необходимо создать заготовку нового протокола в коде исполнительной системы. Для этого нужно:

1. Добавить код протокола в файл main.h следующим образом:

#define SERIAL_PROT_AS001UNI       34   // протокол опроса водосчетчиков АС001 (универсальный формат)

 

2. Добавить поддержку протокола в файл serial.c:

#ifdef USE_AS001UNI_SERIAL_TASK
  #include "io/serial/as001.h"
#endif
...
PROT ArrProt[] =  {
    ...
    #ifdef USE_AS001UNI_SERIAL_TASK
    { SERIAL_PROT_AS001UNI, 0, AS001UNI_InitOperations },
    #endif
    ...
};

 

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

Кроме того, как можно заметить, используется директива условной компиляции, которая указывает, включать поддержку протокола в исполнительную систему, или нет. Необходимо включить эту директиву в config-файле вашего рабочего проекта, например, platform\win32\config-win-console.h:

#define USE_AS001UNI_SERIAL_TASK

3. Создать заголовочный файл протокола (as001.h):

#ifndef AS001_H
#define AS001_H
 
#include "fb/task.h"
#include "io/serial.h"
 
// Структура модуля
typedef struct _as001uni_module {
    WORD    fst;            // Номер первого параметра модуля в массиве pParams
    WORD    quan;           // Количество параметров в модуле
    char*   name;           // Имя модуля (10 байт)
} ALIGN_DWORD as001uni_module;
 
extern int AS001UNI_InitOperations(SERIAL_TASK_CB* pSerialTasksCB);
 
#endif // #ifndef AS001_H

 

4. Создать файл исходного кода протокола (as001uni.c):

#include "ser_api.h"
#include "io/serial/as001.h"
 
#ifdef USE_AS001UNI_SERIAL_TASK
 
// Прототипы функций задачи
int AS001UNI_GetSizeCfgModule(void);
int AS001UNI_InitSerialTask(SERIAL_TASK_CB*);
int AS001UNI_SetCfgModules(SERIAL_TASK_CB*, BYTE R_HUGE_PTR);
int AS001UNI_SerialTaskOneStep(SERIAL_TASK_CB*);
 
// Имя протокола
static char* AS001UNI_protocol_name = "AS001UNI";
 
int AS001UNI_InitOperations(SERIAL_TASK_CB* pSerialTasksCB) {
  // Указатель на функцию определения размера модуля
  pSerialTasksCB->operations.get_size_cfg_module = (void*)AS001UNI_GetSizeCfgModule;
  // Указатель на функцию конфигурирования модулей
  pSerialTasksCB->operations.set_cfg_modules     = (void*)AS001UNI_SetCfgModules;
  // Указатель на функцию конфигурирования протокола
  pSerialTasksCB->operations.init_serial_task    = (void*)AS001UNI_InitSerialTask;
  // Указатель на функцию выполнения одного цикла задачи
  pSerialTasksCB->operations.one_step            = (void*)AS001UNI_SerialTaskOneStep;
  // Указатель на название протокола
  pSerialTasksCB->operations.protocol_name       = AS001UNI_protocol_name;
 
  return 0;
  }
 
int AS001UNI_GetSizeCfgModule(void) {
  return sizeof(as001uni_module);
  }
 
int AS001UNI_SetCfgModules(SERIAL_TASK_CB* pSerialTasksCB, BYTE R_HUGE_PTR pModulesCfg) {
  return 0;
  }
 
int AS001UNI_InitSerialTask(SERIAL_TASK_CB* pSerialTaskCB) {
  return 0;
  }
 
int AS001UNI_SerialTaskOneStep(SERIAL_TASK_CB* pSTaskCB) {
  return 0;
  }
 
#endif // #ifdef USE_AS001UNI_SERIAL_TASK

 

Теперь исполнительная система может быть скомпилирована без ошибок, шаблон протокола создан.

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

int AS001UNI_SetCfgModules(SERIAL_TASK_CB* pSerialTasksCB, BYTE R_HUGE_PTR pModulesCfg) {
  as001uni_module *mod;
  int m, i;
  BYTE *cfg;
 
  mod = pSerialTasksCB->pModules;
  // Цикл по модулям
  for (m = 0; m<pSerialTasksCB->QuanModules; m++, mod++) {
     // Считываем настройки текущего модуля
    cfg = GetModuleCfg(pSerialTasksCB, m);
 
    // Получаем память
    memset(mod, 0, sizeof(as001uni_module));
 
    mod->fst = READ_WORD(cfg);       // Номер первого параметра модуля в массиве pParams
    mod->quan = READ_WORD(cfg + 2);  // Количество параметров в модуле
    mod->name = cfg + 4;             // Код протокола (имя)
 
    // Сетевой номер водосчетчика
    mod->Addr = GetModuleWordProp(cfg, 1, 0x0);
 
    // Номера служебных каналов модуля
    mod->pDiametrIdx = -1;
    mod->pConnectIdx = -1;
    mod->pDontSpeakIdx = -1;
 
    // Заполняем теги модуля
    mod->tags = RMemGet(sizeof(WORD)*mod->quan);
 
    if (!mod->tags) {
      PRINT("Error RMemGet for pTagsId!"); CR;
      return -1;
    }
 
    // Получаем конфигурацию тегов
    cfg = GetModuleTagsCfg(pSerialTasksCB, m);
    for (i = 0; i < mod->quan; i++) {
      // Запоминаем id переданных каналов
      mod->tags[i] = READ_WORD(cfg);
      cfg += 2;
      if (mod->tags[i] == tiDiametr)    // Условный диаметр
        mod->pDiametrIdx = i;
      if (mod->tags[i] == tiConnect)    // Отсутствие связи
        mod->pConnectIdx = i;
      if (mod->tags[i] == tiDontSpeak)  // Опрос отключен
        mod->pDontSpeakIdx = i;
    }
  }
 
  return 0;
  }

 

Чтение свойств модулей происходит по номерам, определенным в io-файле. Используются функции API:

PROP_TYPE GetModuleProp(BYTE* pModuleCfg, BYTE PropID, void** ppProperty);
WORD GetModuleWordProp(BYTE* pModuleCfg, BYTE PropID, WORD DefVal);
DWORD GetModuleDwordProp(BYTE* pModuleCfg, BYTE PropID, DWORD DefVal);

 

Первая функция универсальна, возвращает тип свойства и позволяет получить указатель на данные. Остальные две позволяют прочитать свойство модуля с приведением типа к WORD и DWORD.

Конфигурация тегов модуля сквозная:

Первое свойство первого тега модуля
...
Последнее свойство первого тега модуля
...
Первое свойство последнего тега модуля
...
Последнее свойство последнего тега модуля

 

Для получения значений нужно пользоваться макросами READ_WORD, READ_DWORD, READ_FLOAT, READ_LONG, сдвигая после указатель на нужное количество байт.

int AS001UNI_InitSerialTask(SERIAL_TASK_CB* pSerialTaskCB) {
  as001uni_prot *prt;
 
  // Получаем "чистую" память
  prt = GetCleanMem(sizeof(as001uni_prot));
  if (!prt) return -1;
 
  // Инициализируем конфигурацию протокола
  pSerialTaskCB->pProtocolSettings = (void*)prt;
 
  // Номер СОМ-порта контроллера, к которому подключены водосчетчики (default: СОМ1)
  prt->Port = GetDriverWordProp(pSerialTaskCB, 1, 1);
  // Периодичность опроса (в мсек) контроллером водосчетчиков (default: 1 сек)
  prt->Period = GetDriverWordProp(pSerialTaskCB, 2, 1000);
  pSerialTaskCB->pHeader->Period = prt->Period;
  // Количество повторных запросов при сбое по линии связи (default: 0)
  prt->Retries = GetDriverWordProp(pSerialTaskCB, 3, 0);
  // Таймаут по обмену (в мсек) (default: 2 сек)
  prt->Taut = GetDriverWordProp(pSerialTaskCB, 4, 2000);
  pSerialTaskCB->Taut = prt->Taut;
  // Таймаут между байтами (в мсек) (default: 20 мсек)
  prt->TautBit = GetDriverWordProp(pSerialTaskCB, 5, 20);
  pSerialTaskCB->TautBit = prt->TautBit;
 
  // Устанавливаем размеры буферов
  pSerialTaskCB->SizeSendBuf = 32;
  pSerialTaskCB->SizeRecvBuf = 32;
 
  // Иициализируем буфер отправки
  pSerialTaskCB->SendBuf = RMemGet(pSerialTaskCB->SizeSendBuf + pSerialTaskCB->SizeRecvBuf);
 
  if (pSerialTaskCB->SendBuf == NULL) return (-1);
 
  pSerialTaskCB->RecvBuf = pSerialTaskCB->SendBuf + pSerialTaskCB->SizeSendBuf;
 
  return 0;
  }

 

Чтение свойств протокола происходит по номерам, определенным в файле prot_mod_uni.xml. Аналогично чтению свойств модулей, используются функции API:

PROP_TYPE GetDriverProp(SERIAL_TASK_CB *pSerialTaskCB, BYTE PropID, void** ppProperty);
WORD GetDriverWordProp(SERIAL_TASK_CB *pSerialTaskCB, BYTE PropID, WORD DefVal);
char* GetDriverStringProp(SERIAL_TASK_CB *pSerialTaskCB, BYTE PropID, char *DefVal);
DWORD GetDriverDwordProp(SERIAL_TASK_CB *pSerialTaskCB, BYTE PropID, DWORD DefVal);

 

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

int AS001UNI_SerialTaskOneStep(SERIAL_TASK_CB* pSTaskCB) {
  int i, j, Err, Adr;
  as001uni_module *mod;
  RPARAM *Signl, *PrSv, *StatOpr;
  int GlIndDyIdx;
  ZGLKADR *Otv;
  ulong t;
 
  #ifdef USE_TIMESTAMP
  RTIME Timestamp;
  #endif
 
  // Инициализация каналов
  // Указатель на структуру модулей
  mod = (as001uni_module*)pSTaskCB->pModules;
  // Цикл по модулям
  for (i = 0; i < pSTaskCB->QuanModules; i++, mod++) {
    // Инициализация канала "Отсутствие связи"
    if (mod->pConnectIdx != -1) {
      PrSv = pSTaskCB->pParams + mod->fst + mod->pConnectIdx;
      PrSv->Quality = OPC_QUALITY_GOOD;
      PrSv->Value.Boolean = RTRUE;  // По-умолчанию пишем True, связи нет
      }
    // Цикл по каналам модуля
    for (j = 0; j < mod->quan; j++) {
      if (j != mod->pDiametrIdx && j != mod->pConnectIdx && j != mod->pDontSpeakIdx) {
        Signl = pSTaskCB->pParams + mod->fst + j;
        Signl->Quality = 003;  // Ошибка открытия ресурса (СОМ-порт, сокет и т.п.)
        }
      }
    }
 
  // Инициализируем COM-порт
  if (AS001UNI_InitComPort(pSTaskCB)) return -1;
 
  // Запоминаем текущее значение времени
  t = GetTickCountMs();
 
  // Указатель на структуру модулей
  mod = (as001uni_module*)pSTaskCB->pModules;
  // Кадр ответа
  Otv = (ZGLKADR*)pSTaskCB->RecvBuf;
 
  // Цикл по модулям
  for (i = 0; i < pSTaskCB->QuanModules; i++, mod++) {
    // Запрос состояния прибора
    mod->RamDev[0] = 0;
    Err = AS001UNI_Obmen(pSTaskCB, mod, 0);
    if (!Err) {
      mod->RamDev[0] = Otv->Byte5;
      // Канал "Отсутствие связи с устройством"
      if (mod->pConnectIdx != -1) {
        PrSv->Quality = OPC_QUALITY_GOOD;
        PrSv->Value.Boolean = RFALSE;
        }
      }
 
    StatOpr = pSTaskCB->pParams + mod->fst + mod->pDontSpeakIdx;
    // Проверка канала "Опрос отключен"
    if (mod->pDontSpeakIdx != -1 && StatOpr->Quality == OPC_QUALITY_GOOD && StatOpr->Value.Boolean) {
      for (j = 0; j < mod->quan; j++) {
        if (j != mod->pDiametrIdx && j != mod->pConnectIdx && j != mod->pDontSpeakIdx) {
          Signl = pSTaskCB->pParams + mod->fst + j;
          Signl->Quality = 001;  // Модуль(устройство) блокирован(о) для опроса
          }
        }
      // Переходим к следующему модулю
      continue;
      }
 
    // Инициализация канала "Условный диаметр"
    if (mod->pDiametrIdx != -1 && Signl->Quality != OPC_QUALITY_GOOD)
      mod->IndDy = 0;
    else {
      Signl = pSTaskCB->pParams + mod->fst + mod->pDiametrIdx;
      mod->IndDy = AS001UNI_ProvDy(Signl);
      }
 
    // Запрос условного диаметра (в случае необходимости)
    if (!mod->IndDy) {
      Err = AS001UNI_Obmen(pSTaskCB, mod, 11);
      if (!Err) {
        if (Otv->Byte5 < 1 || Otv->Byte5 > 7) continue;
        mod->IndDy = Otv->Byte5;
        if (mod->pDiametrIdx != -1) {
          Signl->Quality = OPC_QUALITY_GOOD;
          GlIndDyIdx = mod->fst + mod->pDiametrIdx;
          #ifdef USE_TIMESTAMP
          if (!pSTaskCB->operations.use_timestamp)
            RGetDateTime(&Timestamp);
          #endif
          LOCK_GLOBAL_ARRAY();
          // Пишем условный диаметр напрямую в глобальный массив
          iWriteFloat(&GlParam(pSTaskCB->pListParams[GlIndDyIdx].NumParamInGlobalArray), (float)Dy[mod->IndDy]);
          // Определяемся с временной меткой
          #ifdef USE_TIMESTAMP
          if (pSTaskCB->operations.use_timestamp)
            pGlobArrayTime[pSTaskCB->pListParams[GlIndDyIdx].NumParamInGlobalArray] = pSTaskCB->pTimestamps[GlIndDyIdx];
          else
            pGlobArrayTime[pSTaskCB->pListParams[GlIndDyIdx].NumParamInGlobalArray] = Timestamp;
          #endif
          // Передаем в задачу МЭК для анализа изменений параметров
          #ifdef USE_IEC_TASK
          #ifndef IPC_ANY
          IecTaskProcessOneParam(pSTaskCB->pListParams[GlIndDyIdx].NumParamInGlobalArray);
          #endif
          #endif
          UNLOCK_GLOBAL_ARRAY();
          }
        }
      // Если и тут облом, переходим к следующему модулю
      else continue;
      }
 
    mod->KoefQ = KoefQ[mod->IndDy];
    mod->KoefF = KoefF[mod->IndDy];
 
    // Цикл по каналам модуля
    for (j = 0; j < mod->quan; j++) {
      Signl = pSTaskCB->pParams + mod->fst + j;
      switch(mod->tags[j]) {
        case tiConsumption:  // Текущий расход
          Signl->Quality = 047;  // Ошибка считывания параметра из устройства
 
          // Читаем данные
          Err = AS001UNI_Obmen(pSTaskCB, mod, 1);  // 1, 2 байты
          if (Err) break;
          mod->RamDev[1] = Otv->Byte5;
          mod->RamDev[2] = Otv->Byte4;
 
          // Выводим значение
          Signl->Quality = OPC_QUALITY_GOOD;
          Signl->Value.Float = (float)(READ_WORD(mod->RamDev + 1)) / mod->KoefQ;
          Signl->Value.Float *= 3.6;  // л/сек --> м3/час
          break;
        case tiV:  // Накопленный объем
          Signl->Quality = 047;  // Ошибка считывания параметра из устройства
 
          // Читаем данные
          Err = AS001UNI_Obmen(pSTaskCB, mod, 5);  // 6 байт
          if (Err) break;
          mod->RamDev[6] = Otv->Byte4;
          Err = AS001UNI_Obmen(pSTaskCB, mod, 7);  // 7, 8 байты
          if (Err) break;
          mod->RamDev[7] = Otv->Byte5;
          mod->RamDev[8] = Otv->Byte4;
          Err = AS001UNI_Obmen(pSTaskCB, mod, 9);  // 9, 10 байты
          if (Err) break;
          mod->RamDev[9] = Otv->Byte5;
          mod->RamDev[10] = Otv->Byte4;
 
          Signl->Quality = OPC_QUALITY_GOOD;
          Signl->Value.Float = AS001UNI_PreobrStrFloat(mod->RamDev + 6, 5) * mod->KoefF;
          Signl->Value.Float *= 0.001;  // л --> м3
          break;
        case tiT:  // Время наработки
          Signl->Quality = 047;  // Ошибка считывания параметра из устройства
 
          // Читаем данные
          Err = AS001UNI_Obmen(pSTaskCB, mod, 3);  // 3, 4 байты
          if (Err) break;
          mod->RamDev[3] = Otv->Byte5;
          mod->RamDev[4] = Otv->Byte4;
          Err = AS001UNI_Obmen(pSTaskCB, mod, 5);  // 5 байт
          if (Err) break;
          mod->RamDev[5] = Otv->Byte5;
 
          // Выводим значение
          Signl->Quality = OPC_QUALITY_GOOD;
          Signl->Value.Float = AS001UNI_PreobrStrFloat(mod->RamDev + 3, 3) * 0.1;
          break;
        case tiState:  // Статус прибора
          Signl->Quality = OPC_QUALITY_GOOD;
          Signl->Value.Boolean = mod->RamDev[0] ? 1 : 0;
          break;
        case tiWorkTime:  // Время опроса
          Signl->Quality = OPC_QUALITY_GOOD;
          Signl->Value.Integer = GetTickCountMs() - t;
          break;
        }
      }
    }
 
  AS001UNI_DeInitComPort(pSTaskCB);
 
  return 0;
  }

 

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

Signl = pSTaskCB->pParams + mod->fst + j;

Здесь mod->fst – номер первого тега модуля в массиве pParams, j – порядковый номер тега в модуле. Характер тега определяется по ассоциативному массиву mod->tags, в котором хранятся все свойства тегов, вычитанные при инициализации модуля.

Сам тег представлен структурой вида:

typedef struct _RPARAM
{
    #ifdef BIG_ENDIAN_VERSION
    unsigned short Type:3;
    unsigned short Flags:5;
    unsigned short Quality:8;
    #else
    unsigned short Quality:8;
    unsigned short Flags:5;
    unsigned short Type:3;
    #endif
    ALIGN_DWORD union Value {
        BYTE      Byte[4];
        WORD      Word[2];
        RFLOAT    Float;
        RBOOLEAN  Boolean;
        RINTEGER  Integer;
        RDATETIME DateTime;
        DWORD     IpAddr;
        #ifdef USE_STRING
        RSTRING   String;
        #endif
        } Value;
}ALIGN_DWORD RPARAM;

 

В данной структуре обязательно заполнение полей качества и значения. Хорошее качество равно 192 (OPC_QUALITY_GOOD), остальные значения отведены под коды ошибок (см. файл CodeErr.h).

При реализации функций обмена по COM-порту или через TCP сокет можно как использовать низкоуровневые функции API из файла ser_api.c, так и универсальные, описанные в файле io\serial.c (в этом случае поля структуры SERIAL_TASK_CB, относящиеся к связи, должны быть заполнены корректно):

/*
 * Функция захватывает и инициализирует последовательный порт или создаёт сокет и устанавливает соединение
 */
int Init_Port(SERIAL_TASK_CB* pSerialTaskCB, DWORD IPAddr, WORD TCPPort, DWORD TautMutex);
/*
 * Функция посылает пакет в COM-порт или в сокет
 */
int Send_Port(SERIAL_TASK_CB* pSerialTaskCB, BYTE* SendBuf, int ChSend);
/*
 * Функция принимает пакет из COM-порта или из сокета
 */
int Recv_Port(SERIAL_TASK_CB* pSerialTaskCB, BYTE* RecvBuf, int* ChRecv, DWORD TimeOut_ms);
/*
 * Функция посылает запрос и принимает ответ из COM-порта или из сокета
 */
int Obmen_Port(SERIAL_TASK_CB* pSerialTaskCB, BYTE* SendBuf, int ChSend, BYTE* RecvBuf, int* ChRecv, ulong TautObmen);
/*
 * Функция очищает COM-порт (для сокета никаких действий не производится)
 */
void Clear_Port(SERIAL_TASK_CB* pSerialTaskCB);
/*
 * Функция освобождает последовательный порт или закрывает сокет
 */
void Deinit_Port(SERIAL_TASK_CB* pSerialTaskCB);

Читайте также