Я таймаут считаю с момента завершения отправки запроса до схождения контрольной суммы ответа. Точнее, это частный случай слежения за автоматом. Тот же таймер следит и за отправкой запроса. Короче, вот псевдокод с некоторыми упрощениями:
Код:
(* Срабатывает, если автомат долго находится в одном состоянии *)
сторожевой_таймер(IN := состояние = _состояние, PT := T#100ms);
_состояние := состояние;
IF NOT сторожевой_таймер.IN THEN
счётчик := 0; (* Чтобы в автомате не забывать это триста раз делать *)
END_IF
IF сторожевой_таймер.Q THEN (* Случился таймаут *)
состояние := таймаут;
END_IF
CASE состояние OF
начало:
открыть_порт;
настроить_порт;
состояние := отправлять_запрос;
отправлять_запрос:
(* Сюда вставить ещё код формирования запроса *)
(* Как правило, запрос отправляется целиком сразу *)
счётчик := счётчик + в_порт(буфер, счётчик, размер_буфера - счётчик);
IF счётчик = размер_буфера THEN
состояние := ждать_ответ;
END_IF
ждать_ответ:
(* Сначала читаем до того места, где указан или может быть вычислен размер ответа *)
(* Не забываем, что ответ может приходить частями *)
IF счётчик < до_маркера_размера THEN
счётчик := счётчик + из_порта(буфер, счётчик, до_маркера_размера - счётчик);
ELSE (* Потом читаем остаток вычисленного размера *)
счётчик := счётчик + из_порта(буфер, счётчик, размер_пакета - счётчик);
IF счётчик = размер_пакета AND сходится_сумма THEN
состояние := успех;
END_IF
END_IF
успех:
результат := из_буфера;
состояние := отправлять_запрос;
таймаут:
состояние := отправлять_запрос;
END_CASE
По вкусу это оформляется функциональным блоком, открывание порта выносится за пределы, вызывающая сторона просто смотрит на "состояние" и по своему усмотрению активирует такие блоки один за другим.