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

Реализация протокола в 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);

ТЕХНИЧЕСКАЯ ПОДДЕРЖКА

+7 (8352) 22-34-32
support@kaskad-asu.com