
Библиотека для простого и быстрого создания программ для MetaTrader (Часть XX): Создание и хранение ресурсов программы
Содержание
- Концепция
- Класс-генератор файлов
- Класс-коллекция ресурсов программы
- Тестирование доступа к автоматически созданным файлам
- Что дальше
Концепция
Часто при создании программы, нам требуется использовать звуки и изображения. В языке MQL есть несколько возможностей использования таких
данных, и все они связаны с необходимостью
загружать файлы из файловой песочницы терминала. Если в конечном результате
предполагается скомпилированный файл, то достаточно
подключить файл как ресурс и избавиться от необходимости передавать
дополнительные файлы для работы программы. Такой способ хорошо подходит для размещения программ в
Маркете mql5.com, так как там требуется размещение только исполняемого файла, но этот
способ совсем не подходит для размещения исходного кода в
CodeBase mql5.com, так как звуковые *.wav и графические файлы в формате *.bmp туда разместить
нельзя. А без них исходный код неполноценен.
Как же быть в такой ситуации? Вывод очевиден: хранить данные всех требуемых файлов в исходном коде в подключаемых файлах *.mqh в виде
бинарных массивов. А далее в самой программе, при её запуске создавать из имеющихся наборов данных все необходимые для работы программы
файлы в нужных папках. Таким образом, при запуске такого кода программа сама создаст все требуемые для своей работы файлы, разложит их по
папкам и будет корректно работать. Для пользователя программы этот процесс пройдёт практически незамеченным, ну лишь немного
увеличится время первого запуска, необходимое для создания и записи отсутствующих файлов.
Для работы с файловыми данными мы создадим два класса:
- класс-генератор файлов из заранее подготовленных данных,
- класс для работы со списком созданных файлов — коллекцией объектов-описателей файлов.
Класс для работы с уже созданными файлами будет иметь в своём составе список всех созданных файлов и методы доступа к этим файлам по данным, хранящимся в списке. По сути — в списке будут храниться простые описания созданных файлов (имя файла и описание файла), и по этим данным по запросу мы всегда сможем получить путь к физическому файлу для работы с ним.
Класс-генератор файлов
Начнём с файла Datas.mqh. В него добавим требуемые сообщения, которые могут выводиться при работе создаваемых классов.
В перечисление индексов текстовых сообщений добавим константы, указывающие на место расположения новых сообщений в массиве текстовых данных библиотеки:
//+------------------------------------------------------------------+ //| Список индексов текстовых сообщений библиотеки | //+------------------------------------------------------------------+ enum ENUM_MESSAGES_LIB { MSG_LIB_PARAMS_LIST_BEG=ERR_USER_ERROR_FIRST, // Начало списка параметров MSG_LIB_PARAMS_LIST_END, // Конец списка параметров MSG_LIB_PROP_NOT_SUPPORTED, // Свойство не поддерживается MSG_LIB_PROP_NOT_SUPPORTED_MQL4, // Свойство не поддерживается в MQL4 MSG_LIB_PROP_NOT_SUPPORTED_POSITION, // Свойство не поддерживается у позиции MSG_LIB_PROP_NOT_SUPPORTED_PENDING, // Свойство не поддерживается у отложенного ордера MSG_LIB_PROP_NOT_SUPPORTED_MARKET, // Свойство не поддерживается у маркет-ордера MSG_LIB_PROP_NOT_SUPPORTED_MARKET_HIST, // Свойство не поддерживается у исторического маркет-ордера MSG_LIB_PROP_NOT_SET, // Значение не задано MSG_LIB_PROP_EMPTY, // Отсутствует MSG_LIB_SYS_ERROR, // Ошибка MSG_LIB_SYS_NOT_SYMBOL_ON_SERVER, // Ошибка. Такого символа нет на сервере MSG_LIB_SYS_FAILED_PUT_SYMBOL, // Не удалось поместить в обзор рынка. Ошибка: MSG_LIB_SYS_NOT_GET_PRICE, // Не удалось получить текущие цены. Ошибка: MSG_LIB_SYS_NOT_GET_MARGIN_RATES, // Не удалось получить коэффициенты взимания маржи. Ошибка: MSG_LIB_SYS_NOT_GET_DATAS, // Не удалось получить данные MSG_LIB_SYS_FAILED_CREATE_STORAGE_FOLDER, // Не удалось создать папку хранения файлов. Ошибка: MSG_LIB_SYS_FAILED_ADD_ACC_OBJ_TO_LIST, // Ошибка. Не удалось добавить текущий объект-аккаунт в список-коллекцию MSG_LIB_SYS_FAILED_CREATE_CURR_ACC_OBJ, // Ошибка. Не удалось создать объект-аккаунт с данными текущего счёта MSG_LIB_SYS_FAILED_OPEN_FILE_FOR_WRITE, // Не удалось открыть для записи файл MSG_LIB_SYS_INPUT_ERROR_NO_SYMBOL, // Ошибка входных данных: нет символа MSG_LIB_SYS_FAILED_CREATE_SYM_OBJ, // Не удалось создать объект-символ MSG_LIB_SYS_FAILED_ADD_SYM_OBJ, // Не удалось добавить символ MSG_LIB_SYS_NOT_GET_CURR_PRICES, // Не удалось получить текущие цены по символу события MSG_LIB_SYS_EVENT_ALREADY_IN_LIST, // Такое событие уже есть в списке MSG_LIB_SYS_FILE_RES_ALREADY_IN_LIST, // Такой файл уже создан и добавлен в список: MSG_LIB_SYS_FAILED_CREATE_RES_LINK, // Ошибка. Не удалось создать объект-указатель на файл ресурса MSG_LIB_SYS_ERROR_ALREADY_CREATED_COUNTER, // Ошибка. Уже создан счётчик с идентификатором MSG_LIB_SYS_FAILED_CREATE_COUNTER, // Не удалось создать счётчик таймера MSG_LIB_SYS_FAILED_CREATE_TEMP_LIST, // Ошибка создания временного списка MSG_LIB_SYS_ERROR_NOT_MARKET_LIST, // Ошибка. Список не является списком рыночной коллекции MSG_LIB_SYS_ERROR_NOT_HISTORY_LIST, // Ошибка. Список не является списком исторической коллекции MSG_LIB_SYS_FAILED_ADD_ORDER_TO_LIST, // Не удалось добавить ордер в список MSG_LIB_SYS_FAILED_ADD_DEAL_TO_LIST, // Не удалось добавить сделку в список MSG_LIB_SYS_FAILED_ADD_CTRL_ORDER_TO_LIST, // Не удалось добавить контрольный ордер MSG_LIB_SYS_FAILED_ADD_CTRL_POSITION_TO_LIST, // Не удалось добавить контрольую позицию MSG_LIB_SYS_FAILED_ADD_MODIFIED_ORD_TO_LIST, // Не удалось добавить модифицированный ордер в список изменённых ордеров MSG_LIB_SYS_NO_TICKS_YET, // Ещё не было тиков MSG_LIB_SYS_FAILED_CREATE_OBJ_STRUCT, // Не удалось создать структуру объекта MSG_LIB_SYS_FAILED_WRITE_UARRAY_TO_FILE, // Не удалось записать uchar-массив в файл MSG_LIB_SYS_FAILED_LOAD_UARRAY_FROM_FILE, // Не удалось загрузить uchar-массив из файла MSG_LIB_SYS_FAILED_CREATE_OBJ_STRUCT_FROM_UARRAY, // Не удалось создать структуру объекта из uchar-массива MSG_LIB_SYS_FAILED_SAVE_OBJ_STRUCT_TO_UARRAY, // Не удалось сохранить структуру объекта в uchar-массив, ошибка MSG_LIB_SYS_ERROR_INDEX, // Ошибка. Значение "index" должно быть в пределах 0 - 3 MSG_LIB_SYS_ERROR_FAILED_CONV_TO_LOWERCASE, // Не удалось преобразовать строку в нижний регистр, ошибка MSG_LIB_SYS_ERROR_EMPTY_STRING, // Ошибка. Строка предопределённых символов пустая, будет использоваться MSG_LIB_SYS_FAILED_PREPARING_SYMBOLS_ARRAY, // Не удалось подготовить массив используемых символов. Ошибка MSG_LIB_SYS_INVALID_ORDER_TYPE, // Не правильный тип ордера:
И в массив текстовых сообщений впишем соответствующие константам индексов тексты на русском и английском языках:
//+------------------------------------------------------------------+ string messages_library[][TOTAL_LANG]= { {"Начало списка параметров","The beginning of the event parameter list"}, {"Конец списка параметров","End of the parameter list"}, {"Свойство не поддерживается","Property is not support"}, {"Свойство не поддерживается в MQL4","Property is not supported in MQL4"}, {"Свойство не поддерживается у позиции","Property not supported for position"}, {"Свойство не поддерживается у отложенного ордера","The property is not supported for a pending order"}, {"Свойство не поддерживается у маркет-ордера","The property is not supported for a market-order"}, {"Свойство не поддерживается у исторического маркет-ордера","The property is not supported for a history market-order"}, {"Значение не задано","Value not set"}, {"Отсутствует","Not set"}, {"Ошибка ","Error "}, {"Ошибка. Такого символа нет на сервере","Error. There is no such symbol on the server"}, {"Не удалось поместить в обзор рынка. Ошибка: ","Failed to put in the market watch. Error: "}, {"Не удалось получить текущие цены. Ошибка: ","Could not get current prices. Error: "}, {"Не удалось получить коэффициенты взимания маржи. Ошибка: ","Failed to get margin rates. Error: "}, {"Не удалось получить данные ","Failed to get data of "}, {"Не удалось создать папку хранения файлов. Ошибка: ","Could not create file storage folder. Error: "}, {"Ошибка. Не удалось добавить текущий объект-аккаунт в список-коллекцию","Error. Failed to add current account object to collection list"}, {"Ошибка. Не удалось создать объект-аккаунт с данными текущего счёта","Error. Failed to create an account object with current account data"}, {"Не удалось открыть для записи файл ","Could not open file for writing: "}, {"Ошибка входных данных: нет символа ","Input error: no "}, {"Не удалось создать объект-символ ","Failed to create symbol object "}, {"Не удалось добавить символ ","Failed to add "}, {"Не удалось получить текущие цены по символу события ","Failed to get current prices by event symbol "}, {"Такое событие уже есть в списке","This event is already in the list"}, {"Такой файл уже создан и добавлен в список: ","This file has already been created and added to the list: "}, {"Ошибка. Не удалось создать объект-указатель на файл ресурса","Error. Failed to create resource file link object"}, {"Ошибка. Уже создан счётчик с идентификатором ","Error. Already created a counter with id "}, {"Не удалось создать счётчик таймера ","Failed to create timer counter "}, {"Ошибка создания временного списка","Error creating temporary list"}, {"Ошибка. Список не является списком рыночной коллекции","Error. The list is not a list of the market collection"}, {"Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection"}, {"Не удалось добавить ордер в список","Could not add order to the list"}, {"Не удалось добавить сделку в список","Could not add deal to the list"}, {"Не удалось добавить контрольный ордер ","Failed to add a control order "}, {"Не удалось добавить контрольую позицию ","Failed to add a control position "}, {"Не удалось добавить модифицированный ордер в список изменённых ордеров","Could not add modified order to the list of modified orders"}, {"Ещё не было тиков","No ticks yet"}, {"Не удалось создать структуру объекта","Could not create object structure"}, {"Не удалось записать uchar-массив в файл","Could not write uchar array to file"}, {"Не удалось загрузить uchar-массив из файла","Could not load uchar array from file"}, {"Не удалось создать структуру объекта из uchar-массива","Could not create object structure from uchar array"}, {"Не удалось сохранить структуру объекта в uchar-массив, ошибка ","Failed to save object structure to uchar array, error "}, {"Ошибка. Значение \"index\" должно быть в пределах 0 - 3","Error. The \"index\" value must be between 0 - 3"}, {"Не удалось преобразовать строку в нижний регистр, ошибка ","Failed to convert string to lowercase, error "}, {"Ошибка. Строка предопределённых символов пустая, будет использоваться ","Error. String of predefined symbols is empty, the symbol will be used: "}, {"Не удалось подготовить массив используемых символов. Ошибка ","Failed to create an array of used characters. Error "}, {"Не правильный тип ордера: ","Invalid order type: "},
Следует обратить внимание, что последовательность расположения текстов в массиве должна точно
соответствовать последовательности объявления констант индексов в перечислении.
Для работы класса-генератора файлов необходимо создать базы данных, из которых класс будет брать данные о звуковых файлах и файлах изображений. Такими данными должны являться unsigned char-массивы. Для их создания требуется наличие звуковых (*.wav) и растровых (*.bmp) файлов, которые мы будем хранить в исходниках библиотеки. Для примера я уже подготовил несколько тестовых данных. Звуковые и растровые данные будут храниться каждый в своём подключаемом файле.
Создадим в корневом каталоге библиотеки \MQL5\Include\DoEasy\ подключаемый файл DataSND.mqh и сразу впишем названия массивов данных тех звуковых файлов, которые будем размещать в массивах (впрочем, можно и после дописать названия массивов данных — они нужны лишь для быстрого поиска в листинге места объявления массива с данными того или иного файла, так как массивы могут быть большими, но не более 16 мегабайт):
//+------------------------------------------------------------------+ //| DataSND.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" //+------------------------------------------------------------------+ //| Звуки | //+------------------------------------------------------------------+ /* sound_array_coin_01 // Звук упавшей монетки 1 sound_array_coin_02 // Звук упавших монеток sound_array_coin_03 // Звук монеток sound_array_coin_04 // Звук упавшей монетки 2 //--- sound_array_click_01 // Звук щелчка по кнопке 1 sound_array_click_02 // Звук щелчка по кнопке 2 sound_array_click_03 // Звук щелчка по кнопке 3 //--- sound_array_cash_machine_01 // Звук кассового аппарата 1 */ //+------------------------------------------------------------------+
Для вставки файла в исходный текст програмы можно воспользоваться пунктом меню "Правка --> Вставить --> Файл в виде бинарного массива":
После выбора данного пункта откроется окно выбора файла, в котором необходимо найти заранее подготовленный файл для загрузки его данных в массив. Массив будет создан автоматически на основании имени выбранного файла (пример не полный так как бинарных данных много):
//+------------------------------------------------------------------+ //| DataSND.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" //+------------------------------------------------------------------+ //| Звуки | //+------------------------------------------------------------------+ /* sound_array_coin_01 // Звук упавшей монетки 1 sound_array_coin_02 // Звук упавших монеток sound_array_coin_03 // Звук монеток sound_array_coin_04 // Звук упавшей монетки 2 //--- sound_array_click_01 // Звук щелчка по кнопке 1 sound_array_click_02 // Звук щелчка по кнопке 2 sound_array_click_03 // Звук щелчка по кнопке 3 //--- sound_array_cash_machine_01 // Звук кассового аппарата 1 */ //+------------------------------------------------------------------+ //| Звук упавшей монетки 01 | //+------------------------------------------------------------------+ unsigned char sound_array_coin_01[]= { 0x52,0x49,0x46,0x46,0x1E,0x50,0x09,0x00,0x57,0x41,0x56,0x45,0x66,0x6D,0x74,0x20, 0x12,0x00,0x00,0x00,0x03,0x00,0x02,0x00,0x44,0xAC,0x00,0x00,0x20,0x62,0x05,0x00, 0x08,0x00,0x20,0x00,0x00,0x00,0x64,0x61,0x74,0x61,0x28,0x35,0x09,0x00,0x11,0xBA, 0x20,0xBB,0x11,0xBA,0x20,0xBB,0x74,0x81,0x62,0xBB,0x74,0x81,0x62,0xBB,0xE9,0x37, 0x4D,0xBB,0xE9,0x37,0x4D,0xBB,0x7C,0x41,0x13,0xBB,0x7C,0x41,0x13,0xBB,0x8F,0xE4, 0x84,0xBA,0x8F,0xE4,0x84,0xBA,0x51,0x6D,0x05,0x3A,0x51,0x6D,0x05,0x3A,0xE1,0xC7, 0x85,0x3A,0xE1,0xC7,0x85,0x3A,0xF5,0xA9,0xA1,0x39,0xF5,0xA9,0xA1,0x39,0xD6,0xF7, 0x25,0xBA,0xD6,0xF7,0x25,0xBA,0x88,0x38,0x5B,0xBA,0x88,0x38,0x5B,0xBA,0xC8,0x31, 0x05,0x39,0xC8,0x31,0x05,0x39,0x62,0xA0,0xF8,0x39,0x62,0xA0,0xF8,0x39,0x62,0xE5, 0x39,0xBA,0x62,0xE5,0x39,0xBA,0x8A,0xAA,0x8B,0xBA,0x8A,0xAA,0x8B,0xBA,0xDC,0xF9, 0x0B,0xBA,0xDC,0xF9,0x0B,0xBA,0xEA,0x27,0x97,0xBA,0xEA,0x27,0x97,0xBA,0xA1,0x5E, 0xDA,0xBA,0xA1,0x5E,0xDA,0xBA,0x96,0x5B,0x56,0xBA,0x96,0x5B,0x56,0xBA,0x13,0xB9, 0xA6,0xB8,0x13,0xB9,0xA6,0xB8,0x3B,0xFD,0x39,0xB7,0x3B,0xFD,0x39,0xB7,0x05,0x39, 0x37,0xB9,0x05,0x39,0x37,0xB9,0x7D,0xED,0x18,0xBA,0x7D,0xED,0x18,0xBA,0x73,0xDD, 0x8A,0xBA,0x73,0xDD,0x8A,0xBA,0xD1,0x83,0xD4,0xBA,0xD1,0x83,0xD4,0xBA,0xFF,0x94, 0x0C,0xBB,0xFF,0x94,0x0C,0xBB,0xAF,0x13,0xF6,0xBA,0xAF,0x13,0xF6,0xBA,0x5A,0x27, 0x4A,0xBA,0x5A,0x27,0x4A,0xBA,0x68,0xA7,0xA1,0x39,0x68,0xA7,0xA1,0x39,0xBE,0x9B, 0x32,0x3A,0xBE,0x9B,0x32,0x3A,0xFD,0x21,0x0C,0x3A,0xFD,0x21,0x0C,0x3A,0x36,0xFF, 0x21,0x3A,0x36,0xFF,0x21,0x3A,0xB5,0xA2,0x36,0x3A,0xB5,0xA2,0x36,0x3A,0xBB,0x73, 0xE2,0xB9,0xBB,0x73,0xE2,0xB9,0x16,0xDA,0x16,0xBB,0x16,0xDA,0x16,0xBB,0x41,0x70, 0x23,0xBB,0x41,0x70,0x23,0xBB,0xA9,0xC6,0x34,0xBA,0xA9,0xC6,0x34,0xBA,0x78,0x88, 0x35,0x37,0x78,0x88,0x35,0x37,0xFB,0x4C,0xA9,0xBA,0xFB,0x4C,0xA9,0xBA,0xDA,0xAA,
Так как данные для массива полностью повторяют данные файла, то такие массивы получаются довольно-таки большими. Именно поэтому я и вписал
заранее названия создаваемых массивов — чтобы быстро переходить к началу каждого в листинге при помощи поиска
Ctrl+F.
Теперь остаётся только добавить в листинг этого файла требуемое количество массивов звуковых данных. Я уже создал несколько тестовых звуков, и так как файл получился большим, то приводить здесь его листинг не имеет смысла — его можно посмотреть самостоятельно в прикреплённых в конце статьи файлах библиотеки.
И точно таким же образом создадим файл с растровыми данными с именем DataIMG.mqh. В него я тоже уже внёс два массива с изображением двухцветной лампочки-светодиода: в одном массиве — данные с изображением светодиода зелёного цвета, в другом — красного:
//+------------------------------------------------------------------+ //| DataIMG.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" //+------------------------------------------------------------------+ //| Изображения | //+------------------------------------------------------------------+ /* img_array_spot_green // Зелёный светодиод 16x16, 32 бит img_array_spot_red // Красный светодиод 16x16, 32 бит */ //+------------------------------------------------------------------+ //| Изображение "Зелёный светодиод" 32 бит, альфа | //+------------------------------------------------------------------+ unsigned char img_array_spot_green[]= { 0x42,0x4D,0x38,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00, 0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x20,0x00,0x00,0x00, 0x00,0x00,0x02,0x04,0x00,0x00,0xC3,0x0E,0x00,0x00,0xC3,0x0E,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
...
0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00 }; //+------------------------------------------------------------------+ //| Изображение "Красный светодиод" 32 бит, альфа | //+------------------------------------------------------------------+ unsigned char img_array_spot_red[]= { 0x42,0x4D,0x38,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00, 0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x20,0x00,0x00,0x00, 0x00,0x00,0x02,0x04,0x00,0x00,0xC3,0x0E,0x00,0x00,0xC3,0x0E,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
И точно так же, как и в примере со звуковыми данными я не привожу здесь полный листинг получившегося файла.
Чтобы бинарные данные файлов были доступны в библиотеке, подключим файлы с данными к файлу Defines.mqh:
//+------------------------------------------------------------------+ //| Defines.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include "DataSND.mqh" #include "DataIMG.mqh" #include "Datas.mqh" #ifdef __MQL4__ #include "ToMQL4.mqh" #endif //+------------------------------------------------------------------+
В блоке макроподстановок файла Defines.mqh впишем макроподстановку с указанием папки расположения ресурсных данных библиотеки:
//+------------------------------------------------------------------+ //| Макроподстановки | //+------------------------------------------------------------------+ //--- "Описание функции с номером строки ошибки" #define DFUN_ERR_LINE (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Стр. " : ", Line ")+(string)__LINE__+": ") #define DFUN (__FUNCTION__+": ") // "Описание функции" #define COUNTRY_LANG ("Russian") // Язык страны #define END_TIME (D'31.12.3000 23:59:59') // Конечная дата для запросов данных истории счёта #define TIMER_FREQUENCY (16) // Минимальная частота таймера библиотеки в милисекундах //--- Параметры таймера коллекции ордеров и сделок #define COLLECTION_ORD_PAUSE (250) // Пауза таймера коллекции ордеров и сделок в милисекундах #define COLLECTION_ORD_COUNTER_STEP (16) // Шаг приращения счётчика таймера коллекции ордеров и сделок #define COLLECTION_ORD_COUNTER_ID (1) // Идентификатор счётчика таймера коллекции ордеров и сделок //--- Параметры таймера коллекции аккаунтов #define COLLECTION_ACC_PAUSE (1000) // Пауза таймера коллекции аккаунтов в милисекундах #define COLLECTION_ACC_COUNTER_STEP (16) // Шаг приращения счётчика таймера аккаунтов #define COLLECTION_ACC_COUNTER_ID (2) // Идентификатор счётчика таймера аккаунтов //--- Параметры таймера1 коллекции символов #define COLLECTION_SYM_PAUSE1 (100) // Пауза таймера1 коллекции символов в милисекундах (для сканирования символов в обзоре рынка) #define COLLECTION_SYM_COUNTER_STEP1 (16) // Шаг приращения счётчика таймера1 символов #define COLLECTION_SYM_COUNTER_ID1 (3) // Идентификатор счётчика таймера1 символов //--- Параметры таймера2 коллекции символов #define COLLECTION_SYM_PAUSE2 (300) // Пауза таймера2 коллекции символов в милисекундах (для событий списка символов в обзоре рынка) #define COLLECTION_SYM_COUNTER_STEP2 (16) // Шаг приращения счётчика таймера2 символов #define COLLECTION_SYM_COUNTER_ID2 (4) // Идентификатор счётчика таймера2 символов //--- Идентификаторы списков коллекций #define COLLECTION_HISTORY_ID (0x7779) // Идентификатор списка исторической коллекции #define COLLECTION_MARKET_ID (0x777A) // Идентификатор списка рыночной коллекции #define COLLECTION_EVENTS_ID (0x777B) // Идентификатор списка коллекции событий #define COLLECTION_ACCOUNT_ID (0x777C) // Идентификатор списка коллекции аккаунтов #define COLLECTION_SYMBOLS_ID (0x777D) // Идентификатор списка коллекции символов //--- Параметры данных для файловых операций #define DIRECTORY ("DoEasy\\") // Каталог библиотеки для расположения папок объектов классов #define RESOURCE_DIR ("DoEasy\\Resource\\") // Каталог библиотеки для расположения папок ресурсов //--- Параметры символов #define CLR_DEFAULT (0xFF000000) // Цвет по умолчанию #define SYMBOLS_COMMON_TOTAL (1000) // Общее количество рабочих символов //+------------------------------------------------------------------+
Внутри подпапки библиотеки Resource\ будут автоматически создаваться папки Sounds и Images для создания и хранения в них файлов звуков и изображений соответственно.
Так как нам при создании файлов из подготовленных массивов потребуется указать расширение создаваемого файла, то чтобы указать методу
создания файла какой именно файл создаётся, и в какую из двух папок его разместить, нам потребуется перечисление типов файлов, данные
которых будут записываться в бинарные массивы
.
В самом конце листинга Defines.mqh впишем нужное перечисление:
//+------------------------------------------------------------------+ //| Данные для работы с ресурсными данными программы | //+------------------------------------------------------------------+ enum ENUM_FILE_TYPE { FILE_TYPE_WAV, // wav-файл FILE_TYPE_BMP, // bmp-файл }; //+------------------------------------------------------------------+
Так как все файлы ресурсов библиотеки будем располагать в двух папках Sounds и Images, которые в свою очередь будем размещать в каталог MQL5\Files\
терминала, то нам необходимо подправить метод PlaySound() класса
CMessage. Откроем файл \MQL5\Include\DoEasy\ Services\Message.mqh и
внесём
корректировку пути файла в метод PlaySound():
//+------------------------------------------------------------------+ //| Воспроизводит звуковой файл | //+------------------------------------------------------------------+ bool CMessage::PlaySound(const string file_name) { bool res=::PlaySound("\\Files\\"+file_name); CMessage::m_global_error=(res ? ERR_SUCCESS : ::GetLastError()); return res; } //+------------------------------------------------------------------+
Здесь мы для проигрывания файла указываем подпапку \Files\ терминала, так как мы будем хранить все данные относительно
MQL5\ и этой папки, а остальной путь к файлу у нас будет прописываться при создании объекта-описания файла и передаваться в этот метод
параметром
file_name.
Этого на данный момент достаточно для создания необходимых нам классов.
Теперь создадим в папке \MQL5\Include\DoEasy\Services\ новый класс CFileGen в файле FileGen.mqh:
//+------------------------------------------------------------------+ //| FileGen.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include "..\\Services\DELib.mqh" //+------------------------------------------------------------------+ //| Класс генератора файлов | //+------------------------------------------------------------------+ class CFileGen { private: static string m_folder_name; // Имя папки хранения файлов ресурсов библиотеки static string m_subfolder; // Имя подпапки хранения звуковых или растровых файлов static string m_name; // Имя файла static int m_handle; // Хэндл файла //--- Устанавливает имя (1) файла, (2) подпапки static void SetName(const ENUM_FILE_TYPE file_type,const string file_name); static void SetSubFolder(const ENUM_FILE_TYPE file_type); //--- Возвращает расширение файла по его типу static string Extension(const ENUM_FILE_TYPE file_type); public: //--- Возвращает (1) установленное имя, (2) флаг наличия файла в каталоге ресурсов static string Name(void) { return CFileGen::m_name; } static bool IsExist(const ENUM_FILE_TYPE file_type,const string file_name); //--- Создаёт файл из массива данных static bool Create(const ENUM_FILE_TYPE file_type,const string file_name,const uchar &file_data_array[]); }; //+------------------------------------------------------------------+
К файлу сразу же подключена библиотека сервисных функций DELib.mqh,
так как к ней уже подключены Defines.mqh и Message.mqh, необходимые для работы класса:
//+------------------------------------------------------------------+ //| DELib.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" #property strict // Нужно для mql4 //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include "..\Defines.mqh" #include "Message.mqh" #include "TimerCounter.mqh" //+------------------------------------------------------------------+
Все переменные-члены класса и его методы озаглавлены в коде, и описывать их назначение не имеет смысла — там всё понятно. Рассмотрим
реализацию методов.
Так как переменные-члены класса статические, то они
требуют явной инициализации:
//+------------------------------------------------------------------+ //| Инициализация статических переменных | //+------------------------------------------------------------------+ string CFileGen::m_folder_name=RESOURCE_DIR; string CFileGen::m_subfolder="\\"; string CFileGen::m_name=NULL; int CFileGen::m_handle=INVALID_HANDLE; //+------------------------------------------------------------------+
Метод создания файла из массива данных:
//+------------------------------------------------------------------+ //| Создаёт файл из массива данных | //+------------------------------------------------------------------+ bool CFileGen::Create(const ENUM_FILE_TYPE file_type,const string file_name,const uchar &file_data_array[]) { //--- Установим имя файла, состоящее из пути к файлу, его имени и расширения CFileGen::SetName(file_type,file_name); //--- Если такой файл уже существует - возвращаем false if(::FileIsExist(CFileGen::m_name)) return false; //--- Открываем файл с созданным именем для записи CFileGen::m_handle=::FileOpen(CFileGen::m_name,FILE_WRITE|FILE_BIN); //--- Если файл создать не удалось, получаем код ошибки, вывоим сообщение об ошибке открытия файла и возвращаем false if(CFileGen::m_handle==INVALID_HANDLE) { int err=::GetLastError(); ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_OPEN_FILE_FOR_WRITE),"\"",CFileGen::m_name,"\". ",CMessage::Text(MSG_LIB_SYS_ERROR),"\"",CMessage::Text(err),"\" ",CMessage::Retcode(err)); return false; } //--- Записываем в бинарный файл содержимое массива file_data_array[], закрываем файл и возвращаем true ::FileWriteArray(CFileGen::m_handle,file_data_array); ::FileClose(CFileGen::m_handle); return true; } //+------------------------------------------------------------------+
Метод использует стандартную функцию FileWriteArray(),
позволяющую записывать в бинарный файл массивы любых данных кроме строковых.
В метод передаются тип записываемого файла (звук или
изображение),
имя будущего файла и массив
с набором бинарных данных создаваемого файла.
Все действия, осуществляемые методом, прописаны в его листинге — они просты
и понятны, поэтому не будем останавливаться на подробном их разборе.
Метод, возвращающий флаг наличия файла в каталоге ресурсов:
//+------------------------------------------------------------------+ //| Возвращает флаг наличия файла в каталоге ресурсов | //+------------------------------------------------------------------+ bool CFileGen::IsExist(const ENUM_FILE_TYPE file_type,const string file_name) { CFileGen::SetName(file_type,file_name); return ::FileIsExist(CFileGen::m_name); } //+------------------------------------------------------------------+
В метод передаётся тип записываемого файла и имя файла, наличие которого необходимо проверить. Затем устанавливается имя файла, состоящее из пути к файлу, его имени и расширения и при помощи функции FileIsExist() возвращается результат проверки существования файла с таким именем.
Метод, устанавливающий имя файла, состоящее из пути к файлу, его имени и расширения:
//+------------------------------------------------------------------+ //| Устанавливает имя файла | //+------------------------------------------------------------------+ void CFileGen::SetName(const ENUM_FILE_TYPE file_type,const string file_name) { CFileGen::SetSubFolder(file_type); CFileGen::m_name=CFileGen::m_folder_name+CFileGen::m_subfolder+file_name+CFileGen::Extension(file_type); } //+------------------------------------------------------------------+
В метод передаётся тип записываемого файла и имя
файла,из которых метод соберёт полное имя файла, включающее папку
хранения файлов библиотеки, подпапку по расширению файла,
создаваемую методом
SetSubFolder(), переданное в метод имя
(
file_name) и расширение файла, создаваемое методом Extension()
по типу файла (звук или изображение). Полученный результат
записывается в переменную-член класса
m_name.
Метод для установки имени подпапки в зависимости от типа файла (звук или изображение):
//+------------------------------------------------------------------+ //| Устанавливает имя подпапки | //+------------------------------------------------------------------+ void CFileGen::SetSubFolder(const ENUM_FILE_TYPE file_type) { CFileGen::m_subfolder=(file_type==FILE_TYPE_BMP ? "Images\\" : file_type==FILE_TYPE_WAV ? "Sounds\\" : ""); } //+------------------------------------------------------------------+
В метод передаётся тип файла, и в зависимости от этого типа в
переменную-член класса
m_subfolder записывается наименование подпапки Images\
или Sounds\.
Метод, возвращающий расширение файла по его типу:
//+------------------------------------------------------------------+ //| Возвращает расширение файла по его типу | //+------------------------------------------------------------------+ string CFileGen::Extension(const ENUM_FILE_TYPE file_type) { string ext=::StringSubstr(::EnumToString(file_type),10); if(!::StringToLower(ext)) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_CONV_TO_LOWERCASE),CMessage::Retcode(::GetLastError())); return "."+ext; } //+------------------------------------------------------------------+
В метод передаётся тип файла. Затем из текстового представления
(
EnumToString()) переданной в метод константы перечисления
ENUM_FILE_TYPE
извлекается расширение при помощи функции StringSubstr()
(для звукового файла из строки "FILE_TYPE_WAV" извлекается подстрока "WAV", для растрового файла — "BMP"), затем все символы извлечённой
подстроки с расширением файла
преобразуются в строчные при помощи StringToLower(),
к строке (перед ней) добавляется знак "точка" (.) и полученный
результат возвращается из метода.
На этом создание класса-генератора файлов завершено.
Теперь у нас есть возможность хранить в исходном коде бинарные данные любых звуковых и растровых файлов и автоматически при запуске программы
создавать из этих данных полноценные файлы, которые будут размещены в соответствующие папки, из которых библиотека будет получать эти
файлы и использовать их по назначению.
Но для удобного их получения нам необходимо как-то описать имеющиеся в наличии файлы и иметь к ним быстрый и удобный доступ. Для этого мы
создадим класс-коллекцию ресурсов программы. Но это будет не такая коллекция, которые мы создаём в библиотеке — списки указателей на
объекты коллекций, а коллекция объектов-описаний файлов.
Для каждого созданного физического файла мы будем создавать объект-описатель этого файла, в котором будет прописано его имя, путь и
описание. И получать доступ к физическим файлам мы будем по этим объектам-описателям. А описанием файла в таких объектах будет любое
текстовое описание, которое мы будем добавлять к объекту-описателю для каждого конкретного файла.
Например, для звука щелчка мышью можно описание сделать в виде строки "Звук щелчка мышью 01", или "Клик 01", или.., да что угодно — как
удобно, так и описываем созданные файлы. А затем, для получения описателя требуемого файла, мы просто ищем его по нашему описанию, вводя
его в параметры поиска. Метод поиска вернёт нам индекс объекта-описателя файла, и по нему мы получим свойства уже физичесого файла.
Класс-коллекция ресурсов программы
В папке библиотеки \MQL5\Include\DoEasy\Collections\ создадим новый класс CResourceCollection в файле ResourceCollection.mqh. Прямо в листинге этого нового класса напишем ещё один класс — класс объекта-описателя, экземпляры которого мы будем создавать для каждого нового файла и добавлять в коллекцию описателей:
//+------------------------------------------------------------------+ //| ResourceCollection.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include <Arrays\ArrayObj.mqh> #include "..\\Services\\FileGen.mqh" //+------------------------------------------------------------------+ //| Класс объекта-описантеля файла ресурса библиотеки | //+------------------------------------------------------------------+ class CResObj : public CObject { private: string m_file_name; // Путь + имя файла + расширение string m_description; // Текстовое описание файла public: //--- Устанавливает (1) имя файла, (2) описание файла void FileName(const string name) { this.m_file_name=name; } void Description(const string descr) { this.m_description=descr; } //--- Возвращает (1) имя файла, (2) описание файла string FileName(void) const { return this.m_file_name; } string Description(void) const { return this.m_description; } //--- Сравнивает объекты CResObj между собой по свойствам (для поиска равных объектов-ресурсов) bool IsEqual(const CResObj* compared_obj) const { return this.Compare(compared_obj,0)==0; } //--- Сравнивает объекты CResObj между собой по всем свойствам (для сортировки) virtual int Compare(const CObject *node,const int mode=0) const; //--- Конструктор CResObj(void){;} }; //+------------------------------------------------------------------+ //| Сравнивает объекты CResObj между собой | //+------------------------------------------------------------------+ int CResObj::Compare(const CObject *node,const int mode=0) const { const CResObj *obj_compared=node; if(mode==0) return(this.m_file_name>obj_compared.m_file_name ? 1 : this.m_file_name<obj_compared.m_file_name ? -1 : 0); else return(this.m_description>obj_compared.m_description ? 1 : this.m_description<obj_compared.m_description ? -1 : 0); } //+------------------------------------------------------------------+
К листингу сразу же подключим необходимые файлы — класс динамического массива указателей на экземпляры класса CObject и его наследников стандартной библиотеки и класс-генератор файлов библиотеки CFileGen.
Чтобы объект-описатель можно было хранить в списке CArrayObj, нам необходимо этот объект сделать
наследником базового класса стандартной библиотеки CObject.
Все переменные-члены класса и методы класса описаны в коде в комментариях, и не будем останавливаться на подробном разборе их назначения.
Уточню только, что виртуальный метод Compare() сравнивает
поля двух объектов-описателей по умолчанию по имени файла. При равенстве сравниваемых полей двух объектов, метод возвращает ноль. Если
требуется сравнить объекты по описанию файла, то режим
mode нужно задать равным 1.
А метод
IsEqual(), возвращающий флаг равенства объектов, сравнивает объекты только по имени файла (ведь двух файлов с одинаковыми
именами в одной папке быть не может). Метод возвращает результат работы метода Compare() с режимом mode по умолчанию (0), что
соответствует сравнению по имени файла.
Напишем класс-коллекцию объектов-описателей файлов ресурсов программы:
//+------------------------------------------------------------------+ //| Класс-коллекция объектов-описателей файлов ресурсов | //+------------------------------------------------------------------+ class CResourceCollection { private: //--- Список указателей на объекты-описатели CArrayObj m_list_dscr_obj; //--- Создаёт объект-описатель файла и добавляет его в список bool CreateFileDescrObj(const string file_name,const string descript); //--- Добавляет в список объектов-описателей новый объект bool AddToList(CResObj* element); public: //--- Создаёт файл и добавляет его описание в список bool CreateFile(const ENUM_FILE_TYPE file_type,const string file_name,const string descript,const uchar &file_data_array[]); //--- Возвращает (1) список указателей на объекты-описатели, (2) индекс объекта-описателя файла по описанию CArrayObj *GetList(void) { return &this.m_list_dscr_obj; } int GetIndexResObjByDescription(const string file_description); //--- Конструктор CResourceCollection(void); }; //+------------------------------------------------------------------+
Здесь так же описано назначение каждого метода прямо в листинге, поэтому сразу перейдём к разбору методов класса.
За пределами тела класса напишем конструктор класса:
//+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CResourceCollection::CResourceCollection() { this.m_list_dscr_obj.Clear(); this.m_list_dscr_obj.Sort(); } //+------------------------------------------------------------------+
Здесь: очищается список объектов-описателей и списку ставится флаг сортировки по имени файла (по умолчанию 0).
Метод создания файла и добавления объекта-описателя в список-коллекцию:
//+------------------------------------------------------------------+ //| Создаёт файл и добавляет его объект-описатель в список | //+------------------------------------------------------------------+ bool CResourceCollection::CreateFile(const ENUM_FILE_TYPE file_type,const string file_name,const string descript,const uchar &file_data_array[]) { if(!CFileGen::Create(file_type,file_name,file_data_array)) { if(!::FileIsExist(CFileGen::Name())) return false; } return this.CreateFileDescrObj(file_type,CFileGen::Name(),descript); } //+------------------------------------------------------------------+
В метод передаются тип создаваемого файла (звук или изображение), имя файла, описание файла и ссылка на бинарный массив данных файла, из которого файл будет создан.
Если файл создать не удалось при помощи метода Create() класса CFileGen, и файл
действительно отсутствует (он может уже существовать, и по этой причине повторно создан быть не может), то возвращаем
false.
Если же файла не было, и теперь он создан, то возвращаем
из метода результат работы метода создания объекта-описателя и добавления его в список-коллекцию объектов-описателей файлов.
Метод создания объекта-описателя файла и добавления его в список-коллекцию:
//+------------------------------------------------------------------+ //| Создаёт объект-описатель файла и добавляет его в список | //+------------------------------------------------------------------+ bool CResourceCollection::CreateFileDescrObj(const string file_name,const string descript) { CResObj *res_dscr=new CResObj(); if(res_dscr==NULL) { Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_RES_LINK)); return false; } res_dscr.FileName(file_name); res_dscr.Description(descript); if(!this.AddToList(res_dscr)) { delete res_dscr; return false; } return true; } //+------------------------------------------------------------------+
В метод передаются имя файла и его описание.
Создаём новый объект-описатель файла, и если
создать объект не удалось, то выведем об этом сообщение в журнал и вернём false.
Далее
устанавливаем для вновь созданного объекта-описателя имя файла
и
описание файла и добавляем
его в список-коллекцию. Если объект добавить не удалось, то объект должен быть уничтожен во-избежание утечки памяти — удаляем
объект и возвращаем false.
Иначе
возвращаем true
— объект успешно создан и добавлен в коллекцию.
Метод, добавляющий новый объект-описатель в список-коллекцию:
//+------------------------------------------------------------------+ //| Добавляет в список объектов-описателей файлов новый объект | //+------------------------------------------------------------------+ bool CResourceCollection::AddToList(CResObj *element) { this.m_list_dscr_obj.Sort(); if(this.m_list_dscr_obj.Search(element)>WRONG_VALUE) return false; return this.m_list_dscr_obj.Add(element); } //+------------------------------------------------------------------+
В метод передаётся объект-описатель, списку-коллекции
устанавливается флаг сортировки по имени файла, и если объект
с таким именем уже есть в списке, то возвращаем false.
Иначе — возвращаем
результат работы
метода добавления объекта в список.
Метод, возвращающий индекс объекта-описателя в списке-коллекции по описанию файла:
//+------------------------------------------------------------------+ //| Возвращает индекс объекта-описателя файла по описанию файла | //+------------------------------------------------------------------+ int CResourceCollection::GetIndexResObjByDescription(const string file_description) { CResObj *obj=new CResObj(); if(obj==NULL) return WRONG_VALUE; obj.Description(file_description); this.m_list_dscr_obj.Sort(1); int index=this.m_list_dscr_obj.Search(obj); delete obj; return index; } //+------------------------------------------------------------------+
В метод передаётся описание файла, по которому необходимо найти
объект-описатель.
Создаём временный экземпляр объекта-описателя и в
его поле описания устанавливаем переданное в метод описание файла.
Списку-коллекции
устанавливаем флаг сортировки по описанию файла (
1) и получаем
индекс объекта-описателя, у которого поле описания содержит искомый текст.
Обязательно
удаляем временный объект и возвращаем полученный индекс
(-1 в случае, если объекта с таким описанием нет в списке-коллекции).
Класс коллекции объектов-описателей файлов готов.
Теперь нам необходимо добавить несколько методов в базовый объект библиотеки CEngine.
Откроем файл \MQL5\Include\DoEasy\Engine.mqh и
подключим к
нему файл коллекции объектов-описателей:
//+------------------------------------------------------------------+ //| Engine.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include "Services\TimerCounter.mqh" #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Collections\AccountsCollection.mqh" #include "Collections\SymbolsCollection.mqh" #include "Collections\ResourceCollection.mqh" //+------------------------------------------------------------------+
Создадим новый объект-коллекцию ресурсов программы (коллекция описателей файлов):
//+------------------------------------------------------------------+ //| Класс-основа библиотеки | //+------------------------------------------------------------------+ class CEngine : public CObject { private: CHistoryCollection m_history; // Коллекция исторических ордеров и сделок CMarketCollection m_market; // Коллекция рыночных ордеров и сделок CEventsCollection m_events; // Коллекция событий CAccountsCollection m_accounts; // Коллекция аккаунтов CSymbolsCollection m_symbols; // Коллекция символов CResourceCollection m_resource; // Список ресурсов CArrayObj m_list_counters; // Список счётчиков таймера int m_global_error; // Код глобальной ошибки bool m_first_start; // Флаг первого запуска bool m_is_hedge; // Флаг хедж-счёта bool m_is_tester; // Флаг работы в тестере bool m_is_market_trade_event; // Флаг торгового события на счёте bool m_is_history_trade_event; // Флаг торгового события в истории счёта bool m_is_account_event; // Флаг события изменения аккаунта bool m_is_symbol_event; // Флаг события изменения свойств символа ENUM_TRADE_EVENT m_last_trade_event; // Последнее торговое событие на счёте int m_last_account_event; // Последнее событие в свойствах счёта int m_last_symbol_event; // Последнее событие в свойствах символа //--- Возвращает индекс счётчика по id
и добавим три новых метода для работы с коллекцией ресурсов программы:
//--- Таймер void OnTimer(void); //--- Устанавливает список используемых символов bool SetUsedSymbols(const string &array_symbols[]) { return this.m_symbols.SetUsedSymbols(array_symbols);} //--- Создаёт файл ресурса bool CreateFile(const ENUM_FILE_TYPE file_type,const string file_name,const string descript,const uchar &file_data_array[]) { return this.m_resource.CreateFile(file_type,file_name,descript,file_data_array); } //--- Возвращает список ссылок на ресурсы CArrayObj *GetListResource(void) { return this.m_resource.GetList(); } int GetIndexResObjByDescription(const string file_name) { return this.m_resource.GetIndexResObjByDescription(file_name); } //--- Возвращает (1) милисекунды, (2) причину, (3) источник события из его long-значения ushort EventMSC(const long lparam) const { return this.LongToUshortFromByte(lparam,0); } ushort EventReason(const long lparam) const { return this.LongToUshortFromByte(lparam,1); } ushort EventSource(const long lparam) const { return this.LongToUshortFromByte(lparam,2); } //--- Конструктор/Деструктор CEngine(); ~CEngine(); }; //+------------------------------------------------------------------+
Методы имеют те же названия, что и методы в классе-коллекции объектов-описателей и вызывают одноимённые методы из этого класса.
Для тестирования созданных классов создадим все файлы по имеющимся данным в подключаемых файлах DataSND.mqh и DataIMG.mqh в бинарных
массивах. В журнал "Эксперты" выведем результаты создания файлов из бинарных массивов и выведем содержание получившегося
списка-коллекции объектов-описателей файлов. Также проиграем один из звуков и выведем в правый нижний угол экрана изображение,
состоящее из двух созданных файлов-изображений красного и зелёного светодиодов.
Тестирование доступа к автоматически созданным файлам
Возьмём советник TestDoEasyPart19.mq5 из прошлой статьи и сохраним его в
новую папку \MQL5\Experts\TestDoEasy\
Part20\ под новым именем TestDoEasyPart20.mq5.
Так как файлы должны создаваться во время первого запуска программы, то и работу с классами по созданию ресурсов программы организуем в обработчике OnInit(). В нём же и протестируем полученный результат.
Добавим в обработчик OnInit(), в самый его конец, такой блок кода:
//--- Установка параметров торгового класса CTrade #ifdef __MQL5__ trade.SetDeviationInPoints(slippage); trade.SetExpertMagicNumber(magic_number); trade.SetTypeFillingBySymbol(Symbol()); trade.SetMarginMode(); trade.LogLevel(LOG_LEVEL_NO); #endif //--- Создание и проверка файлов ресурсов Print("\n",TextByLanguage("--- Проверка успешности создания файлов ---","--- Verifying that the files were created ---")); string dscr=TextByLanguage("Проверка существования файла: ","Checking the existence of a file: "); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_01",TextByLanguage("Звук упавшей монетки 1","The sound of a falling coin 1"),sound_array_coin_01); if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_coin_01")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_02",TextByLanguage("Звук упавших монеток","Sound fallen coins"),sound_array_coin_02); if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_coin_02")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_03",TextByLanguage("Звук монеток","Sound of coins"),sound_array_coin_03); if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_coin_03")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_04",TextByLanguage("Звук упавшей монетки 2","The sound of a falling coin 2"),sound_array_coin_04); if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_coin_04")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_01",TextByLanguage("Звук щелчка по кнопке 1","Click on the button sound 1"),sound_array_click_01); if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_click_01")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_02",TextByLanguage("Звук щелчка по кнопке 2","Click on the button sound 1"),sound_array_click_02); if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_click_02")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_03",TextByLanguage("Звук щелчка по кнопке 3","Click on the button sound 1"),sound_array_click_03); if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_click_03")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_WAV,"sound_array_cash_machine_01",TextByLanguage("Звук кассового аппарата","The sound of the cash machine"),sound_array_cash_machine_01); if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_cash_machine_01")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_green",TextByLanguage("Изображение \"Зелёный светодиод\"","Image \"Green Spot lamp\""),img_array_spot_green); if(CFileGen::IsExist(FILE_TYPE_BMP,"img_array_spot_green")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_red",TextByLanguage("Изображение \"Красный светодиод\"","Image \"Red Spot lamp\""),img_array_spot_red); if(CFileGen::IsExist(FILE_TYPE_BMP,"img_array_spot_red")) Print(dscr+CFileGen::Name(),": OK"); //--- Проверка списка описания файлов Print("\n",TextByLanguage("--- Проверка списка описания файлов ---","--- Checking the file description list ---")); CArrayObj* list_res=engine.GetListResource(); if(list_res!=NULL) { //--- В цикле просмотрим весь список описаний файлов for(int i=0;i<list_res.Total();i++) { CResObj *res=list_res.At(i); if(res==NULL) continue; //--- Распечатаем в журнал пути к файлам и описание файлов string type=(StringFind(res.FileName(),"\\Sounds\\")>0 ? TextByLanguage("Звук ","Sound ") : TextByLanguage("Изображение ","Image ")); Print(type,string(i+1),": ",TextByLanguage("Имя файла :","File name: "),res.FileName()," (",res.Description(),")"); //--- Если текущее описание соответствует звуку упавшей монетки 1, то проиграем этот звук if(res.Description()==TextByLanguage("Звук упавшей монетки 1","The sound of a falling coin 1")) { CMessage::PlaySound(res.FileName()); } } //--- Создание изображения красно-зелёного светодиода //--- Получим индексы описаний изображений красного и зелёного светодиодов int index_r=engine.GetIndexResObjByDescription(TextByLanguage("Изображение \"Красный светодиод\"","Image \"Red Spot lamp\"")); int index_g=engine.GetIndexResObjByDescription(TextByLanguage("Изображение \"Зелёный светодиод\"","Image \"Green Spot lamp\"")); if(index_g>WRONG_VALUE && index_r>WRONG_VALUE) { //--- Получим из списка два объекта с описанием файлов CResObj *res_g=list_res.At(index_g); CResObj *res_r=list_res.At(index_r); if(res_g==NULL || res_r==NULL) { Print(TextByLanguage("Не удалось получить данные с описанием файла изображения","Failed to get image file description data")); return(INIT_SUCCEEDED); } //--- Создадим на основе файлов изображений кнопку long chart_ID=ChartID(); string name=prefix+"RedGreenSpot"; if(ObjectCreate(chart_ID,name,OBJ_BITMAP_LABEL,0,0,0)) { ObjectSetString(chart_ID,name,OBJPROP_BMPFILE,0,"\\Files\\"+res_g.FileName()); // Изображение для нажатой кнопки ObjectSetString(chart_ID,name,OBJPROP_BMPFILE,1,"\\Files\\"+res_r.FileName()); // Изображение для отжатой кнопки ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,CORNER_RIGHT_LOWER); ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,ANCHOR_RIGHT_LOWER); ObjectSetInteger(chart_ID,name,OBJPROP_STATE,true); ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,16); ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,16); ObjectSetInteger(chart_ID,name,OBJPROP_XOFFSET,0); ObjectSetInteger(chart_ID,name,OBJPROP_YOFFSET,0); ObjectSetInteger(chart_ID,name,OBJPROP_BACK,false); ObjectSetInteger(chart_ID,name,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); ChartRedraw(chart_ID); } } } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Здесь мы организовали создание файлов из данных, находящихся в бинарных массивах в исходних кодах библиотеки. После создания каждого
очередного файла проверяем его существование и распечатываем в журнал результат. После создания всех файлов и их описателей, проверяем
полный список всех описателей файлов, добавленных в коллекцию на этапе создания файлов.
В листинге описаны все действия — оставим их для самостоятельного разбора.
После компиляции советника он выведет в журнал записи о результатах создания файлов, проиграет звук упавшей монетки и выведет в правый нижний угол экрана рисунок светодиода, состоящий из двух изображений. Переключить изображения можно щелчком мыши по изображению светодиода (ведь по сути — это кнопка, имеющая два состояния вкл/выкл)
Как видим — всё создаётся, в журнал выводятся сообщения об успешном создании файлов, изображение светодиода переключает цвет при щелчке по
изображению, а если открыть каталог данных терминала (в терминале пункт меню Файл --> Открыть каталог данных), зайти в папку
MQL5\Files\DoEasy\Resource\, то там будет две подпапки — Images и Sounds, в которых лежат все только что созданные файлы.
Что дальше
Со следующей статьи мы открываем новый раздел библиотеки — торговые классы и всё с ними связанное.
Ниже прикреплены все файлы текущей версии библиотеки и файлы тестового советника. Их можно скачать и протестировать всё
самостоятельно.
При возникновении вопросов, замечаний и пожеланий, вы можете озвучить их в комментариях к статье.
Статьи этой серии:
Часть 1. Концепция, организация данных
Часть
2. Коллекция исторических ордеров и сделок
Часть 3. Коллекция рыночных ордеров и
позиций, организация поиска
Часть 4. Торговые события. Концепция
Часть 5. Классы и коллекция торговых событий. Отправка событий в программу
Часть 6. События на счёте с типом неттинг
Часть
7. События срабатывания StopLimit-ордеров, подготовка функционала для регистрации событий модификации ордеров и позиций
Часть
8. События модификации ордеров и позиций
Часть 9. Совместимость с MQL4 -
Подготовка данных
Часть 10. Совместимость с MQL4 - События открытия позиций и
активации отложенных ордеров
Часть 11. Совместимость с MQL4 - События закрытия
позиций
Часть 12. Класс объекта "аккаунт", коллекция объектов-аккаунтов
Часть 13. События объекта "аккаунт"
Часть
14. Объект "Символ"
Часть 15. Коллекция объектов-символов
Часть
16. События коллекции символов
Часть 17. Интерактивность объектов библиотеки
Часть 18. Интерактивность объекта-аккаунт и любых других объектов библиотеки
Часть 19. Класс сообщений библиотеки





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
У Вас 1 байт кодируется 5 символами ("0xNN,").
Для плотной упаковки бинарных данных в текст используют Base64. У меня в тесте получилось 1.36 символа на байт.
Вот пример
len1=50, len2=68, result=VGhlIHF1aWNrIGJyb3duICBmb3ggIGp1bXBzICBvdmVyICB0aGUgIGxhenkgIGRvZwA=
У Вас 1 байт кодируется 5 символами ("0xNN,").
Для плотной упаковки бинарных данных в текст используют Base64. У меня в тесте получилось 1.36 символа на байт.
Вот пример
len1=50, len2=68, result=VGhlIHF1aWNrIGJyb3duICBmb3ggIGp1bXBzICBvdmVyICB0aGUgIGxhenkgIGRvZwA=
Так тут же о другом...
А, имеете в виду, что у Вас данные для компиляции, и в ex5 будут занимать 1:1. Да, здесь упаковывать не нужно.
А, имеете в виду, что у Вас данные для компиляции, и в ex5 будут занимать 1:1. Да, здесь упаковывать не нужно.