powered by simpleCommunicator - 2.0.37     © 2025 Programmizd 02
Форумы / Языки программирования семейства Pascal [закрыт для гостей] / Уроки из ошибки
Модераторы: s62
7 сообщений из 7, страница 1 из 1
Уроки из ошибки
    #576792
s62
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Модератор темы
Нашел сегодня причину ошибки и исправил. Наверное можно сказать, что среди причин ошибки было:
- неправильное наследование, не идеальное, скажем так)
- невнимательность - скопипастил и не посмотрел,
- особенности чтения из сокета по протоколу UDP.

В общем есть обмен с одним устройством. Раньше его подключали за преобразователем портов, так что к преобразователю подключались по TCP-IP, а от того шло по RS-232. Для програмы - всё равно, что просто по TCP-IP. Потом к этому устройству стали подключаться напрямую по UDP. В принципе почти всё одно и то же, есть несколько отличий. Ну, при подключении создаем сокет для UDP например.
Вместо
Код: Delphi
1.
FClientSocket := Socket(AF_Inet, SOCK_STREAM, 0);
вызываем
Код: Delphi
1.
FClientSocket := Socket(AF_Inet, SOCK_DGRAM, 0);
И в первом случае, не прервалось ли подключение, можно проверить просто - открыт ли сокет. А во втором случае, в UDP ведь нет понятия подключения, так что надо как-то проверять на уровне приложения. Например если сколько-то подряд раз не получили ответ от устройства, можно считать, что связь нарушена.

В общем я написал класс, реализующий обмен с устройством по UDP как наследника класса, который обменивался данными по TCP. У них есть предок с самыми общими методами, некоторыми - абстрактными. Но от этого предка есть наследник и могут быть другие и для железок других производителей. А тут надо было бы наверное сделать ещё промежуточный класс для этой конкретной железки со всеми общими методами, а от него уже унаследовать один для TCP, один для UDP. Я сделал, как меньше труда :) В принципе это задачи решило, дальше иерархия не планируется.
Но вот в другой программе тоже нужен был такой обмен, скопипастил один старый вариант. А там в одном из методов, переписанных (override), сначала вызывается метод предка (inherited). А потом уже обмен по UDP. НО! не надо было вызывать метод предка, надо было бы вызвать метод предка предка (такую манипуляцию Паскаль напрямую вроде не позволяет), чтобы зачистить кое-какие структуры, или впрямую (как я сейчас сделал) очистить их. В более новой версии основной программы этой фигни нет, а тут она была. Получилось, что во-первых два раза запрос и чтение ответа делалось.
Но что более важно, из-за чего ошибка была. В TCP протокол обеспечивает доставку пакета, причем, если не ошибаюсь, может его разбить, доставить по частям. И читать из сокета мы можем кусочками, по сколько хотим. Соответственно, там читался сначала заголовок пакета, в котором указана общая длина данных, а потом читалось остальное - нужное количество байт.
А при UDP такого делать нельзя. В UDP приходит датаграмма целиком и её надо читать целиком. В документации на функцию recv написано:
Цитата 
[игнорируется]
Для сокетов без подключения (тип SOCK_DGRAM или других сокетов, ориентированных на сообщения), данные извлекаются из первой датаграммы (сообщения) из адреса назначения, указанного функцией connect .

Если датаграмма или сообщение больше указанного буфера, буфер заполняется первой частью датаграммы, а recv создает ошибку WSAEMSGSIZE. Для ненадежных протоколов (например, UDP) лишние данные теряются;
Соответственно, когда у меня для UDP сокета вызывался метод его TCP-предка, вот эту ошибку я и получал. Убрал inherited, добавил код очистки структур и всё заработало, как надо. Спектр стал нормально сниматься. Формат пакетов известен, в том числе длина, так что можно сразу пытаться читать весь пакет.
Вот.
...
s62:
Пользователь запросил модераторские права на тему.
Изменено: 17.10.2023, 22:58 - s62
Рейтинг: 0 / 0
Уроки из ошибки
    #576798
s62
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Модератор темы
s62 [игнорируется] 

А у нас в офисе, когда есть собранный макет, бывает по-разному собран - или датчик подключен через преобразователь портов, или напрямую по UDP. Я когда эту штуку писал, было как раз собрано в первом варианте. Я потестил, всё вроде работает ок. А на фабрике наш сотрудник начал использовать - не работает (там - второй вариант подключения). Я тоже посмотрел, да, прекращается измерение, непонятно почему. Ну, не катастрофично, т.к. именно это долго делали в другой программе, и так и тут продолжилось. Хорошо, что на днях появился программный эмулятор железки. Запустил с ним в офисе и в отладчике Delphi увидел, что в одной команде непонятно почему вдруг работает функция от TCP. Ну, увидел потом это ненужное там inherited.
...
Изменено: 17.10.2023, 23:20 - s62
Рейтинг: 1 / 0
Нравится: Неуловимый Джо
Уроки из ошибки
    #576810
s62
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Модератор темы
s62 [игнорируется] 
Хм, может я нарушаю соглашение о неразглашении, что написал вот это на форуме... Ладно, уже не удалить, вроде особо чувствительной информации и тайн тут не было.
...
Изменено: 17.10.2023, 23:29 - s62
Рейтинг: 0 / 0
Уроки из ошибки
    #576819
Неуловимый Джо
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
[игнорирует гостей]
s62  17.10.2023, 23:16
[игнорируется]
Ну, увидел потом это ненужное там inherited.
такие мелочи трудно отлавливать конечно.
...
Рейтинг: 0 / 0
Горбатый ёж:
Пользователь назначен модератором темы: s62.
Уроки из ошибки
    #578340
Неуловимый Джо
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
[игнорирует гостей]
У меня недавно из подобного было.
Ошибка оказалась, что я когда создавал TObjectDictionary указал что он владеет объектами, то есть сам будет их освобождать.
А потом где-то в коде еще напрямую освободил какой-то из элементов.

Ну подобное. имею в виду что сразу не поймешь из-за чего ошибка.
...
Рейтинг: 1 / 0
Нравится: s62
Период между сообщениями больше года.
Уроки из ошибки
    #1312567
s62
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Модератор темы
Сегодня чуть не отправил в коммит программу с ошибкой, которую сегодня же внес. Ну, так, наверное не очень интерсно, но сразу даже не понял, что там к чему. Было if выражение then выражение else выражение. Для отладки и тестирования с внешней железкой добавил запись в лог кое-чего, что от неё приходит.
Стало if выражение then begin выражение; выражение; end else выражение.
Потом убрал назад всё, но забыл убрать точку с запятой. Т.е. стало

if выражение then выражение; else выражение.

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

Через какое-то время до меня доходит. Это же всё - внутри конструкции case. А в case после перечисления всех случаев ещё может идти как раз else, для всех остальных случаев, и она как раз идет после точки с запятой, типа
Код: Delphi
1.
2.
3.
4.
5.
6.
case i of
  0: do1;
  1: do2;
  3: do3;
  else do4;
end;
Т.е. у меня, когда точка с запятой, то смысл этого else поменялся.

Вот реальный код:
Код: Delphi
1.
2.
3.
4.
5.
6.
7.
8.
              case CmdCode of
                ...
                ...
                RdCoils: if DevExp.CheckCoilRegistersAddresses(RelStartReg, RegCount) then
                           DevExp.MakeSendCoilData(RelStartReg, RegCount, ModbusBuf, Len)
                         else
                           Reason := erWrongAddr;
              end;
И вот тут перед else была сегодня временно точка с запятой. Спасибо гиту, что указал на отличие в этом месте. )
...
Изменено: Вчера, 16:55 - s62
Рейтинг: 0 / 0
7 сообщений из 7, страница 1 из 1
Форумы / Языки программирования семейства Pascal [закрыт для гостей] / Уроки из ошибки
Модераторы: s62
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]