Полный исходный текст на языке C++ для библиотеки (DLL) с моделями блоков, обменивающихся данными по сети. Библиотека содержит три модели:
- Server – блок, включающий серверные функции в RDS;
- NetSend – блок, передающий данные;
- NetReceive – блок, принимающий данные.
// Передача вещественного числа по сети #include <windows.h> #include <RdsDef.h> // Подготовка описаний сервисных функций #define RDS_SERV_FUNC_BODY GetInterfaceFunctions #include <RdsFunc.h> //========== Главная функция DLL ========== int WINAPI DllMain(HINSTANCE /*hinst*/, unsigned long reason, void* /*lpReserved*/) { if(reason==DLL_PROCESS_ATTACH) // Загрузка DLL { // Получение доступа к функциям RDS if(!GetInterfaceFunctions()) RDS_SERV_ERROR_MSGW // Сообщение: старая версия RDS } return 1; } //========= Конец главной функции ========= //========================================= // Модель блока, включающего сервер //========================================= extern "C" __declspec(dllexport) int RDSCALL Server(int CallMode, RDS_PBLOCKDATA BlockData, LPVOID ExtParam) { int *pConnId; switch(CallMode) { // Инициализация case RDS_BFM_INIT: // Отводим место под личную область данных размером в int BlockData->BlockData=pConnId=new int; // Запускаем сервер и соединяемся с ним *pConnId=rdsNetServer(-1, // Порт по умолчанию "ProgrammersGuide.Server", // Канал FALSE); // Не принимаем данные break; // Очистка case RDS_BFM_CLEANUP: // Личная область данных – целое число pConnId=(int*)(BlockData->BlockData); // Разрываем связь с сервером (он завершится сам) rdsNetCloseConnection(*pConnId); // Удаляем личную область delete pConnId; break; } return RDS_BFR_DONE; } //========================================= //========================================= // Личная область данных блоков приема // и передачи по сети //========================================= class TNetSendRcvData { public: int Mode; // Режим данного блока: прием или передача #define NETSRMODE_SENDER 0 // Передатчик #define NETSRMODE_RECEIVER 1 // Приемник char *ChannelName; // Имя канала int ConnId; // Идентификатор соединения // Переменные состояния блока-передатчика BOOL Connected; // Соединение установлено BOOL DataWaiting; // Передача данных отложена // Функции класса void Connect(void); // Установить соединение void Disconnect(void); // Разорвать соединение void SendValue(double value); // Передать число в канал BOOL ReceiveValue(RDS_NETRECEIVEDDATA *rcv, // Реакция на double *pOut); // пришедшие данные int Setup(char *title); // Функция настройки блока void SaveText(void); // Сохранить параметры void LoadText(char *text); // Загрузить параметры // Конструктор класса TNetSendRcvData(int mode) { ConnId=-1; // Нет соединения Connected=DataWaiting=FALSE; ChannelName=NULL; Mode=mode; // Режим передается в параметре конструктора }; // Деструктор класса ~TNetSendRcvData() { Disconnect(); // Разорвать соединение rdsFree(ChannelName); // Освободить строку имени канала }; }; //========================================= // Установка соединения void TNetSendRcvData::Connect(void) { char *PrefixedName; // Полное имя канала // Если имя канала пустое, соединение невозможно if(ChannelName==NULL || (*ChannelName)==0) return; // Добавляем префикс к имени канала PrefixedName=rdsDynStrCat("ProgrammersGuide.",ChannelName,FALSE); // Устанавливаем соединение с сервером ConnId=rdsNetConnect(NULL, // Сервер по умолчанию -1, // Порт по умолчанию PrefixedName, // Имя канала с префиксом Mode==NETSRMODE_RECEIVER); // Прием данных // Освобождаем динамически отведенную строку rdsFree(PrefixedName); } //========================================= // Разорвать соединение void TNetSendRcvData::Disconnect(void) { if(ConnId!=-1) // Соединение было создано rdsNetCloseConnection(ConnId); // Разорвать // Сбрасываем переменные состояния ConnId=-1; // Соединения больше нет Connected=FALSE; // Связи тоже больше нет } //========================================= // Передать данные void TNetSendRcvData::SendValue(double value) { if(!Connected) // Нет связи с сервером { // Взводим флаг наличия данных, ожидающих передачи DataWaiting=TRUE; return; } // Связь есть – передаем всем блокам канала rdsNetBroadcastData(ConnId, // Соединение RDS_NETSEND_UPDATE|RDS_NETSEND_UDP, // Флаги 0,NULL, // Не передаем целое число и строку &value, // Указатель на данные sizeof(value)); // Размер данных // Сбрасываем флаг ожидания – мы только что передали данные DataWaiting=FALSE; } //========================================= // Прием данных BOOL TNetSendRcvData::ReceiveValue( RDS_NETRECEIVEDDATA *rcv, // Указатель на структуру с данными double *pOut) // Указатель на выход блока { if(rcv==NULL || pOut==NULL) // Нет одного из указателей return FALSE; // Проверяем, есть ли среди принятых данных двоичные, // и равен ли размер этих данных размеру double if(rcv->Buffer==NULL || rcv->BufferSize!=sizeof(double)) return FALSE; // Нет данных или не совпал размер // Копируем принятое числов pOut memcpy(pOut,rcv->Buffer,sizeof(double)); return TRUE; // Приняты правильные данные } //========================================= // Сохранение параметров блока void TNetSendRcvData::SaveText(void) { RDS_HOBJECT ini; // Вспомогательный объект // Создаем вспомогательный объект ini=rdsINICreateTextHolder(TRUE); // Создаем в объекте секцию "[General]" rdsSetObjectStr(ini,RDS_HINI_CREATESECTION,0,"General"); // Записываем в эту секцию имя канала rdsINIWriteString(ini,"Channel",ChannelName); // Сохраняем текст, сформированный объектом, как параметры блока rdsCommandObject(ini,RDS_HINI_SAVEBLOCKTEXT); // Удаляем вспомогательный объект rdsDeleteObject(ini); } //========================================= // Загрузка параметров блока void TNetSendRcvData::LoadText(char *text) { RDS_HOBJECT ini; // Вспомогательный объект char *str; // Создаем вспомогательный объект ini=rdsINICreateTextHolder(TRUE); // Записываем в объект полученный текст с параметрами блока rdsSetObjectStr(ini,RDS_HINI_SETTEXT,0,text); // Начинаем чтение секции "[General]", если она есть if(rdsINIOpenSection(ini,"General")) // Секция есть { // Освобождаем старое имя канала rdsFree(ChannelName); ChannelName=NULL; // Получаем у объекта указатель на строку с именем str=rdsINIReadString(ini,"Channel","",NULL); // Если такая строка есть в тексте, копируем ее в ChannelName if(str) ChannelName=rdsDynStrCopy(str); } // Удаляем вспомогательный объект rdsDeleteObject(ini); // Поскольку имя канала могло измениться, соединяемся с // сервером заново Disconnect(); // Разрываем старое соединение Connect(); // Создаем новое } //========================================= // Настройка параметров // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" int TNetSendRcvData::Setup(char *title) { char *NewName; // Запрос строки у пользователя NewName=rdsInputString( title, // Заголовок окна "Имя канала:", // Текст перед полем ChannelName, // Исходное значение 200); // Ширина поля if(NewName==NULL) // Нажата кнопка "Отмена" return 0; if(ChannelName!=NULL && strcmp(NewName,ChannelName)==0) { // Имя канала не изменилось rdsFree(NewName); // Освобождаем вовращенную строку return 0; } // Освобождаем старое имя канала rdsFree(ChannelName); // Запоминаем новое ChannelName=NewName; // Имя канала изменилось – устанавливаем связь заново Disconnect(); Connect(); return 1; // Параметры блока изменены } //========================================= //========================================= // Блок-передатчик //========================================= extern "C" __declspec(dllexport) int RDSCALL NetSend(int CallMode, RDS_PBLOCKDATA BlockData, LPVOID ExtParam) { // Указатель на личную область данных, приведенный к нужному типу TNetSendRcvData *data=(TNetSendRcvData*)(BlockData->BlockData); // Макроопределения для статических переменных #define pStart ((char *)(BlockData->VarTreeData)) #define Start (*((char *)(pStart))) #define Ready (*((char *)(pStart+RDS_VSZ_S))) #define x (*((double *)(pStart+2*RDS_VSZ_S))) switch(CallMode) { // Инициализация case RDS_BFM_INIT: // Создаем объект класса TNetSendRcvData с // Mode==NETSRMODE_SENDER (передатчик) BlockData->BlockData=new TNetSendRcvData(NETSRMODE_SENDER); break; // Очистка case RDS_BFM_CLEANUP: delete data; break; // Проверка типов переменных case RDS_BFM_VARCHECK: return strcmp((char*)ExtParam,"{SSD}")? RDS_BFR_BADVARSMSG:RDS_BFR_DONE; // Связь с сервером установлена case RDS_BFM_NETCONNECT: // Взводим флаг наличия связи data->Connected=TRUE; // Если были данные, ожидающие отправки, // посылаем значение входа блока if(data->DataWaiting) data->SendValue(x); break; // Связь с сервером разорвана case RDS_BFM_NETDISCONNECT: // Сбрасываем флаг наличия связи data->Connected=FALSE; break; // Запуск расчета case RDS_BFM_STARTCALC: // Если это – первый запуск после сброса, // передаем значение входа if(((RDS_PSTARTSTOPDATA)ExtParam)->FirstStart) data->SendValue(x); break; // Такт расчета case RDS_BFM_MODEL: data->SendValue(x); // Передаем значение входа break; // Вызов настройки // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" case RDS_BFM_SETUP: return data->Setup("Передача double"); // Сохранение параметров в текстовом виде case RDS_BFM_SAVETXT: data->SaveText(); break; // Загрузка параметров в текстовом виде case RDS_BFM_LOADTXT: data->LoadText((char*)ExtParam); break; } return RDS_BFR_DONE; // Отмена макроопределений #undef x #undef Ready #undef Start #undef pStart } //========================================= //========================================= // Блок-приемник //========================================= extern "C" __declspec(dllexport) int RDSCALL NetReceive(int CallMode, RDS_PBLOCKDATA BlockData, LPVOID ExtParam) { // Указатель на личную область данных, приведенный к нужному типу TNetSendRcvData *data=(TNetSendRcvData*)(BlockData->BlockData); // Макроопределения для статических переменных #define pStart ((char *)(BlockData->VarTreeData)) #define Start (*((char *)(pStart))) #define Ready (*((char *)(pStart+RDS_VSZ_S))) #define y (*((double *)(pStart+2*RDS_VSZ_S))) switch(CallMode) { // Инициализация case RDS_BFM_INIT: // Создаем объект класса TNetSendRcvData с // Mode==NETSRMODE_RECEIVER (приемник) BlockData->BlockData= new TNetSendRcvData(NETSRMODE_RECEIVER); break; // Очистка case RDS_BFM_CLEANUP: delete data; break; // Проверка типов переменных case RDS_BFM_VARCHECK: return strcmp((char*)ExtParam,"{SSD}")? RDS_BFR_BADVARSMSG:RDS_BFR_DONE; // По сети получены данные case RDS_BFM_NETDATARECEIVED: if(data->ReceiveValue((RDS_NETRECEIVEDDATA*)ExtParam,&y)) Ready=1; // Если данные верны, взводим флаг готовности // для передачи выхода по связям break; // Вызов настройки // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" case RDS_BFM_SETUP: return data->Setup("Прием double"); // Сохранение параметров в текстовом виде case RDS_BFM_SAVETXT: data->SaveText(); break; // Загрузка параметров в текстовом виде case RDS_BFM_LOADTXT: data->LoadText((char*)ExtParam); break; } return RDS_BFR_DONE; // Отмена макроопределений #undef y #undef Ready #undef Start #undef pStart } //=========================================