Статьи Королевства Дельфи

       

Часть IV


, часть I
, часть II
, часть III
Вернуться к разделу
аммах для Win32 в среде C++» Титова Олега (за что ему очередное спасибо)

Следующей важной после DCB управляющей структурой является COMMTIMEOUTS. Она определяет параметры временных задержек при приеме и передаче. Значения, задаваемые полями этой структуры, оказывают большое влияние на работу функций чтения/записи.

COMMTIMEOUTS

Описание в эквиваленте C

typedef struct _COMMTIMEOUTS { DWORD ReadIntervalTimeout; DWORD ReadTotalTimeoutMultiplier; DWORD ReadTotalTimeoutConstant; DWORD WriteTotalTimeoutMultiplier; DWORD WriteTotalTimeoutConstant; } COMMTIMEOUTS,*LPCOMMTIMEOUTS;

Параметры :

ReadIntervalTimeoutМаксимальное время, в миллисекундах, допустимое между двумя последовательными символами считываемыми с коммуникационной линии. Во время операции чтения временной период начинает отсчитываться с момента приема первого символа. Если интервал между двумя последовательными символами превысит заданое значение, операция чтения завершается и все данные, накопленые в буфере, передаются в программу. Нулевое значение данного поля означает, что данный тайм-аут не используется. Значение MAXDWORD, вместе с нулевыми значениями полей ReadTotalTimeoutConstant и ReadTotalTimeoutMultiplier, означает немедленный возврат из операции чтения с передачей уже принятого символа, даже если ни одного символа не было получено из линии. ReadTotalTimeoutMultiplierЗадает множитель, в миллисекундах, используемый для вычисления общего тайм-аута операции чтения. Для каждой операции чтения данное значение умножается на количество запрошеных для чтения символов ReadTotalTimeoutConstantЗадает константу, в миллисекундах, используемую для вычисления общего тайм-аута операции чтения. Для каждой операции чтения данное значение прибавляется к результату умножения ReadTotalTimeoutMultiplier на количество запрошеных для чтения символов. Нулевое значение полей ReadTotalTimeoutMultiplier и ReadTotalTimeoutConstant означает, что общий тайм-аут для операции чтения не используется. WriteTotalTimeoutMultiplierЗадает множитель, в миллисекундах, используемый для вычисления общего тайм-аута операции записи. Для каждой операции записи данное значение умножается на количество записываемых символов. WriteTotalTimeoutConstantЗадает константу, в миллисекундах, используемую для вычисления общего тайм-аута операции записи. Для каждой операции записи данное значение прибавляется к результату умножения WriteTotalTimeoutMultiplier на количество записываемых символов. Нулевое значение полей WriteTotalTimeoutMultiplier и WriteTotalTimeoutConstant означает, что общий тайм-аут для операции записи не используется. По тайм-аутам обычно возникает много вопросов. Поэтому попробую объяснить подробнее. Пусть мы считываем 50 символов из порта со скоростью 9600. При этом используется 8 бит на символ, дополнение до четности и один стоповый бит. Таким образом на один символ в физической линии приходится 11 бит (включая стартовый бит). 50 символов на скорости 9600 будут приниматься 50 * 11 / 9600 = 0.0572916 секунд, или примерно 57.3 миллисекунды, при условии нулевого интервала между приемом последовательных символов. Если интервал между символами составляет примерно половину времени передачи одного символа, т.е. 0.5 миллисекунд, то время приема будет 50 * 11 / 9600 + 49 * 0.0005 = 0.0817916 секунд, или примерно 82 миллисекунды. Если в процессе чтения прошло более 82 миллисекунд, то мы вправе предположить, что произошла ошибка в работе внешнего устройства и прекратить считывание избежав тем самым зависания программы. Это и есть общий тайм-аут операции чтения. Аналогично существует и общий там-аут операции записи.


Если тайм- аут при чтении понятен, то тайм-аут при записи вызывает недоумение. В самом деле, что нам мешает передавать? Управление потоком! Внешнее устройство может использовать, например, аппаратное управление потоком. При этом пропадание питания во внешнем устройстве заставит компьютер приостановить передачу данных. Если не контролировать тайм-аут возможно точно такое же зависание компьютера, как и при операции чтения.

Общий тайм-аут зависит от количества участвующих в операции чтения/записи символов и среднего времени передачи одного символа с учетом межсимвольного интервала. Если символов много, например 1000, то на общем времени выполнения операции начинают сказываться колебания времени затрачиваемого на один символ или времени межсимвольного интервала. Поэтому тайм-ауты в структуре COMMTIMEOUTS задаются двумя величинами. Таким образом формула для вычисления общего тайм-аута операции, например чтения, выглядит так NumOfChar * ReadTotalTimeoutMultiplier + ReadTotalTimeoutConstant, где NumOfChar это число символов запрошеных для операции чтения.

Для операции чтения, кроме общего тайм-аута на всю операцию, задается так же тайм-аут на интервал между двумя последовательными символами. Точнее это интервал между началами двух последовательных символов. В это значение входит и время передачи самого символа.

Теперь небольшой пример.
ReadTotalTimeoutMultiplier = 2, ReadTotalTimeoutConstant = 1, ReadIntervalTimeout = 1,
считывается 250 символов. Если операция чтения завершится за 250 * 2 + 1 = 501 миллисекунду, то будет считано все сообщение. Если операция чтения не завершится за 501 миллисекунду, то она все равно будет завершена. При этом будут возвращены символы, прием которых завершился до истечения тайм-аута операции. Остальные символы могут быть получены следеющей операцией чтения. Если между началами двух последовательных символов пройдет более 1 миллисекунды, то операция чтения так же будет завершена.

Надеюсь, что теперь тайм-ауты не будут вызывать у Вас затруднений. Для завершения темы тайм-аутов рассмотрим один частный случай. Если поля ReadIntervalTimeout и ReadTotalTimeoutMultiplier установлены в MAXDWORD, а ReadTotalTimeoutConstant больше нуля и меньше MAXDWORD, то выполнение операции чтения подчиняется следующим правилам:
  • Если в буфере есть символы, то чтение немедленно завершается и возвращается символ из буфера;
  • Если в буфере нет символов, то операция чтения будет ожидать появления любого символа, после чего она немедленно завершится;
  • Если в течении времени, заданого полем ReadTotalTimeoutConstant, не будет принято ни одного символа, оперция чтения завершится по тайм- ауту.
Теперь вам ясно назначение структуры _COMMTIMEOUTS ,как и для заполнения структуры DCB, для COMMTIMEOUTS существуют функции считывания и установки значений. Это функции GetCommTimeouts и SetCommTimeouts:



GetCommTimeouts
функция находится в файле kernel32.dll



Функция GetCommTimeouts считывает структуру _COMMTIMEOUTS с указанного порта и заполняет параметры значениями по умолчанию.

Описание в эквиваленте C

BOOL GetCommTimeouts( HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts );

Параметры :

HFileДескриптор указывающий на порт (этот дескриптор может быть создан с помощью CreateFile, OpenFile) LpCommTimeoutsСтруктура LPCOMMTIMEOUTS

Возвращаемое значение :

Если функция выполняется успешно, возвращаемое значение - TRUE иначе возвращаемое значение - FALSE . При возникновении ошибки код ошибки можно получить используя GetLastError

Ну и соответственно -

SetCommTimeouts
функция находится в файле kernel32.dll

Функция SetCommTimeouts устанавливает тайм-ауты порта

Описание в эквиваленте C

BOOL SetCommTimeouts( HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts );

Параметры :

HFileДескриптор указывающий на порт (этот дескриптор может быть создан с помощью CreateFile, OpenFile) LpCommTimeoutsСтруктура LPCOMMTIMEOUTS

Возвращаемое значение :

Если функция выполняется успешно, возвращаемое значение - TRUE иначе возвращаемое значение - FALSE . При возникновении ошибки код ошибки можно получить используя GetLastError

Параметры этих функций очевидны. Хочется отметить что установку тайм-аутов можно производить как до установки параметров порта, так и после, т.е последовательность вызова функций SetCommState и SetCommTimeouts не имеет значения. Главное, что бы все настройки были завершены до начала ввода/вывода информации.

В продолжении статьи хочется добавить еще оду функцию использование которой несомненно очень нужно для получения данных о портах. Представьте ситуацию, когда под COM1 может скрываться вовсе не привычный порт RS-232, а что нибудь другое. Или порт может не позволять задавать скорость более 9600 бод.

Исчерпывающая информация о возможностях коммуникационного устройства и драйвера содержится в структуре COMMPROP:
COMMPROP
typedef struct _COMMPROP { WORD wPacketLength; // Задает размер, в байтах, // структуры COMMPROP WORD wPacketVersion; // Номер версии DWORD dwServiceMask; // Битовая маска DWORD dwReserved1; // reserved DWORD dwMaxTxQueue; // max буфер передачи DWORD dwMaxRxQueue; // max буфер приема DWORD dwMaxBaud; // max допустимая скорость обмена DWORD dwProvSubType; // Тип коммуникац.порта DWORD dwProvCapabilities; // Возможности перед.устройства DWORD dwSettableParams; // Допустимые для изменения // параметры DWORD dwSettableBaud; // Допустимый набор скоростей // обмена WORD wSettableData; // допустимые длины символов WORD wSettableStopParity; // Допуст.кол-во стоповых бит DWORD dwCurrentTxQueue; // Текущий размер буфер передачи DWORD dwCurrentRxQueue; // Текущий размер буфер приема DWORD dwProvSpec1; // Устройство-зависимые данные DWORD dwProvSpec2; // Устройство-зависимые данные WCHAR wcProvChar[1]; // Устройство-зависимые данные } COMMPROP; Поля этой структуры описывают все возможности драйвера. Вы не можете выйти за пределы этих возможностей. Вот какое значение имеют поля:



Параметры :

wPacketLengthЗадает размер, в байтах, структуры COMMPROP wPacketVersionНомер версии структуры. dwServiceMask Битовая маска. Для коммуникационных устройств всегда SP_SERIALCOMM, включая модемы. dwReserved1Зарезервировано Microsoft dwMaxTxQueueМаксимальный размер, в байтах, внутреннего буфера передачи драйвера. Нулевое значение свидетельствует об отсутствии ограничения. dwMaxRxQueueМаксимальный размер, в байтах, внутреннего буфера приема драйвера. Нулевое значение свидетельствует об отсутствии ограничения. dwMaxBaudМаксимально допустимая скорость обмена, в битах в секунду (бпс). Возможны следующие значения данного поля:
  • BAUD_075 75 бпс.
  • BAUD_110 110 бпс.
  • BAUD_134_5 134.5 бпс.
  • BAUD_150 150 бпс.
  • BAUD_300 300 бпс.
  • BAUD_600 600 бпс.
  • BAUD_1200 1200 бпс.
  • BAUD_1800 1800 бпс.
  • BAUD_2400 2400 бпс.
  • BAUD_4800 4800 бпс.
  • BAUD_7200 7200 бпс.
  • BAUD_9600 9600 бпс.
  • BAUD_14400 14400 бпс.
  • BAUD_19200 19200 бпс.
  • BAUD_38400 38400 бпс.
  • BAUD_56K 56K бпс.
  • BAUD_57600 57600 бпс.
  • BAUD_115200 115200 бпс.
  • BAUD_128K 128K бпс.
  • BAUD_USER Допускается программирование скорости обмена
dwProvSubTypeТип коммуникационного порта. Возможны следующие значения данного поля:
  • PST_FAX Факс
  • PST_LAT LAT протокол
  • PST_MODEM Модем
  • PST_NETWORK_BRIDGE Сетевой мост PST_PARALLELPORT Параллельный порт
  • PST_RS232 Последовательный порт RS-232
  • PST_RS422 Порт RS-422
  • PST_RS423 Порт RS-423
  • PST_RS449 Порт RS-449
  • PST_SCANNER Сканнер
  • PST_TCPIP_TELNET Протокол TCP/IP TelnetR PST_UNSPECIFIED Неизвестное устройство
  • PST_X25 Устройство стандарта X.25
dwProvCapabilitiesБитовая маска. Определяет возможности предоставляемые устройством. Возможны следующие значения:
  • PCF_16BITMODE Поддерживается специальный 16-битный режим.
  • PCF_DTRDSR Поддерживаются сигналы DTR/DSR. PCF_INTTIMEOUTS Поддерживается межсимвольный тайм-аут.
  • PCF_PARITY_CHECK Поддерживается контроль четности. PCF_RLSD Поддерживается определение наличия сигнала в приемной линии.
  • PCF_RTSCTS Поддерживаются сигналы RTS/CTS.
  • PCF_SETXCHAR Поддерживаются задаваемые символы XON/XOFF.
  • PCF_SPECIALCHARS Поддерживаются спецсимволы.
  • PCF_TOTALTIMEOUTS Поддерживаются общие тайм-ауты (ожидаемое время).
  • PCF_XONXOFF Поддерживается программное (XON/XOFF) управление потоком.
  • PCF_XONXOFF Поддерживается программное (XON/XOFF) управление потоком
dwSettableParamsБитовая маска. Определяет допустимые для изменения параметры. Возможны следующие значения:
  • SP_BAUD Скорость обмена.
  • SP_DATABITS Бит в символе.
  • SP_HANDSHAKING Рукопожатие (управление потоком).
  • SP_PARITY Четность.
  • SP_PARITY_CHECK Контроль четности.
  • SP_RLSD Детектирование наличия сигнала в приемной линии.
  • SP_STOPBITS Количество стоповых бит.
dwSettableBaudБитовая маска. Определяет допустимый набор скоростей обмена. Допустимые для данного поля значения указаны в описании поля dwMaxBaud. wSettableDataБитовая маска. Определяет допустимые длины символов, в битах. Возможны следующие значения:
  • DATABITS_5 5 бит
  • DATABITS_6 6 бит
  • DATABITS_7 7 бит
  • DATABITS_8 8 бит
  • DATABITS_16 16 бит
  • DATABITS_16Х Специальный широкий канал через аппаратную последовательную линию
wSettableStopParity Битовая маска. Определяет допустимое количество стоповых бит и режимы четности. Возможны следующие значения:
  • STOPBITS_10 Один стоповый бит
  • STOPBITS_15 Полтора стоповыx бита
  • STOPBITS_20 Два стоповых бита
  • PARITY_NONE Без четности
  • PARITY_ODD Доплнение до нечетности
  • PARITY_EVEN Дополнение до четности
  • PARITY_MARK Бит четности всегда "1"
  • PARITY_SPACE Бит четности всегда "0"
DwCurrentTxQueueОпределяет текущий размер, в байтах, внутренней очереди передачи драйвера. Нулевое значение свидетельствует о недоступности данного параметра. DwCurrentRxQueue Определяет текущий размер, в байтах, внутренней очереди приема драйвера. Нулевое значение свидетельствует о недоступности данного параметра. dwProvSpec1Устройство-зависимые данные. Программа должна игнорировать содержимое данного поля, за исключением случаев, когда Вы точно знаете формат этих данных. Занесите в данное поле значение COMMPROP_INITIALIZED, если поле wPacketLength уже содержит правильное значение. DwProvSpec2Устройство-зависимые данные. Программа должна игнорировать содержимое данного поля, за исключением случаев, когда Вы точно знаете формат этих данных. wcProvCharУстройство-зависимые данные. Программа должна игнорировать содержимое данного поля, за исключением случаев, когда Вы точно знаете формат этих данных. Информация хранящаяся в структуре COMMPROP требуется редко, так как чаще всего точно известно с каким типом портов будет работать программа.



Остановлюсь чуть подробнее на описании некоторых полей. Поле wPacketLength играет несколько иную роль, чем поле DCBlength структуры DCB, хотя из его описания это не следует. Секрет прост. Поле wcProvChar, расположеное в конце структуры, может содержать, а может и не содержать, данных. Вы не в состоянии это узнать не запросив информацию. В свою очередь, перед запросом информации Вы должны выделить (и обнулить) память под структуру COMMPROP. Поэтому последовательность шагов для получения всей информации следующая:
  • Выделить память под структуру COMMPROP.
  • Запросить информацию у системы вызвав функцию GetCommProperties.
  • Если поле wPacketLength содержит значение большее sizeof(COMMPROP), то имеется дополнительная информация. Для ее получения измените размер ранее выделенного блока памяти, новый размер должен быть равен значению занесенному системой в поле wPacketLength. Установите в поле wProvSpec1 значение COMMPROP_INITIALIZED, это будет означать, что выделен достаточный блок памяти для получения дополнительной информации. Повторно вызовите функцию GetCommProperties.
Чаще всего дополнительная информация представлена в виде структуры MODEMDEVCAPS, которая размещается на месте поля wcProvChar, если поле dwProvSubType содержит значение PST_MODEM.

, часть I
, часть II
, часть III


25 апреля 2001 г.
Специально для


Содержание раздела