Форум » ПРОГРАММИРОВАНИЕ SCADAPack32 » Текущие вопросы программирования на С++(вопросы к Astilya) » Ответить

Текущие вопросы программирования на С++(вопросы к Astilya)

Радик: есть подозрение, что процессор SH3(SCADAPack32) не может обращаться к памяти по невыравненным адресам, если заставить, то генерит исключение, после чего срабатывает сторожевой таймер, перезагружающий контроллер. [pre2] UCHAR s[16]; UCHAR *s1; UINT16 *s2, *s3; ... s1 = &s; s2 = (UINT16) s1; s1++; s3 = (UINT16) s1; *s1 = 0x12; // работает всегда *s2 = 0x1234; // упадет или здесь *s3 = 0x1234; // или здесь... ведь один из них нечетный[/pre2] astilya, каково Ваше мнение по этому поводу?

Ответов - 10

Astilya: Радик пишет: есть подозрение, что процессор SH3(SCADAPack32) не может обращаться к памяти по невыравненным адресам, если заставить, то генерит исключение, после чего срабатывает сторожевой таймер, перезагружающий контроллер. Во первых не процесор, а компилятор. UCHAR s[16]; UCHAR *s1; UINT16 *s2, *s3; ... s1 = &s; s2 = (UINT16) s1; s1++; s3 = (UINT16) s1; *s1 = 0x12; // работает всегда *s2 = 0x1234; // упадет или здесь *s3 = 0x1234; // или здесь... ведь один из них нечетный Странно, что у Вас на приведенный код компилятор не ругается...при наших тестах приведенный Вами код компилироваться отказывался. Более корректный код: s1 = s; // или так s1 = (UCHAR *)&s s2 = (UINT16 *)s1; s1++; s3 = (UINT16 *) s1; Подтверждаю: при попытке записи в нечетный байт происходит перезапуск контроллера. Впрочем, неудачным, скорее, является подход к решению задачи, нежели особенности компилятора. Могу посоветовать два варианта: 1) Использовать для записи двубайтного (или более) значения в массив из однобайтных элементов функцию memcpy 2) Если все-таки по каким-то причинам необходима запись "вручную", то делать ее побайтно, а не по два байта.

Радик: код я не компилил, привел как пояснение ситуации, там действительно нужно поставить s1 = &s[0]; А вот фраза про "компилятор, а не процессор" настораживает. Стало быть процессор не против, и срабатывает какая-то run-time проверка? На мой взгляд, маловероятно, C++ как правило не страдает маниакальным контролем за исполнением в run-time. Поскольку это 32 разрядная система, наверняка есть защищенный режим, операционка в какой-то момент включает запрет на обращения по невыровненным адресам, потом когда получает исключение - выполняет перезагрузку. При чем здесь компилятор? Насчет неудачного подхода... ситуация возникла при попытке написать обработчик новой команды Modbus, там дается ссылка на буфер ответа uchar * response, а в процессе формирования ответа данные берутся из базы через dbase(). И вполне безобидное на Intel платформе [pre2] * response ++; UINT16* data = (UINT16*) response; for ( .... ){ * data = dbase(....); data++; } [/pre2] поставило в тупик на пару дней, пока я не стал разбираться из принципа. Считаю что такая особенность должна быть описана в руководстве на CTools32 как одна из типовых ошибок при программировании на С++, и должны быть четко расписаны причины такого поведения процессора\контроллера или ОС. Ну чем не "грабли"?

Astilya: Радик, прошу прощения за некоторый тай-аут. Хочется ответить максимально полно, а это потребует времени. Вопрос пока не закрыт, в ближайшие дни постараюсь ответить по делу.


Radix: Ничего страшного, проблема решена обходным путем - промежуточная область для чтения из базы, потом побайтный перенос. Вам сообщаю с целью обратить внимание на возможность улучшить документацию CTools32. Радик, в командировке Radix

Astilya: "есть подозрение, что процессор SH3(SCADAPack32) не может обращаться к памяти по невыравненным адресам, если заставить, то генерит исключение, после чего срабатывает сторожевой таймер, перезагружающий контроллер." Ответ: Компания Controlmicrosystems подтвердила, что есть такая особенность. Будут выяснять у производителя т.е. у Hitachi, в чем тут дело. Посоветовали использовать масив UCHAR, а не UINT16. так будет логичние. Наша тех. поддержка согласна с таким решением. "Насчет неудачного подхода... ситуация возникла при попытке написать обработчик новой команды Modbus, там дается ссылка на буфер ответа uchar * response, а в процессе формирования ответа данные берутся из базы через dbase(). И вполне безобидное на Intel платформе " Для решения вашей задачи рекомендуем использовать функцию installModbusHandler из библиотеки CTools. Не стоит искать грабли, есть готовое, проверенное решение Если не хочется использовать готовое, тогда может для этих целей использовать структуру? Заголовок пакета поля UCHAR, а поле данных UINT16. Сделать union структуры и массива UCHAR[sizeof(структура)], при формировании ответа или обработки полученных данных работать с структурой, а при отправке/приеме работать с массивом UCHAR.

Radix: Astilya пишет: Для решения вашей задачи рекомендуем использовать функцию installModbusHandler из библиотеки CTools. Не стоит искать грабли, есть готовое, проверенное решение это и делали. Если делается структура, то поля UCHAR не выравниваются, UINT16 выравниваются на границу слова, т.е. на четные адреса, а UINT32 на границу двойных слов. Таким образом, легко нарваться на изменение общего размера структуры из-за скрытых промежутков, которые компилятор вставляет при выравнивании полей. например [pre2]typedef struct { uchar a1; UINT16 a2; uchar a3 UINT32 a4; } TMyStruc;[/pre2] будет занимать в памяти не 8 байт, а все 12, т.к после a1 будет вставлен пустой байт, а после а3 целых 3 байта с целью выравнивания. Так что структурами писать не получается - если не знать особенностей доступа к типам данных, можно найти себе большие проблемы. Не хватает функций чтения из базы побайтно, аналога memcpy(uchar *dst, *uchar src, sizeof_t count ), например dbtomemcpy(uchar *dst, UINT16 start, UINT16 regcount) - перенос куска памяти из базы данных в память (и обратную функцию). Вся наша жизнь - борьба с особенностями компиляторов, языков, контроллеров и операционок, и хваленая переносимость С\С++ не более чем миф - код превращается в сплошные #ifdef ... #endif c native кодом под конкретную платформу. Добавить в раздел "Введение" или "Разработка приложений на СТооls32" небольшой раздел с описанием этих вещей - и как много вопросов и напрасно потраченных часов отладки не было бы вообще. А что ожидает меня при переходе на SCADAPack2(350\357)? Какие там ещё будут грабли по переносу кода?

Astilya: "Если делается структура, то поля UCHAR не выравниваются, UINT16 выравниваются на границу слова, т.е. на четные адреса, а UINT32 на границу двойных слов. Таким образом, легко нарваться на изменение общего размера структуры из-за скрытых промежутков, которые компилятор вставляет при выравнивании полей. например typedef struct { uchar a1; UINT16 a2; uchar a3 UINT32 a4; } TMyStruc; будет занимать в памяти не 8 байт, а все 12, т.к после a1 будет вставлен пустой байт, а после а3 целых 3 байта с целью выравнивания. Так что структурами писать не получается - если не знать особенностей доступа к типам данных, можно найти себе большие проблемы." На мой взгляд особенности доступа к типам данных при программировании знать надо. Как иначе-то программировать? Что касаемо остального вышеизложенного, то обращаю Ваше внимание на возможность использования "pragma pack" и "pragma unpack". Не хватает функций чтения из базы побайтно А зачем, если адресное пространство модбас двухбайтовое, да и протокол работает с словами, а не с байтами...? Стандартных функций С, не относящихся к библиотеке CTools, более чем достаточно для работы с разными типами данных. Можно делать свои собственные наработки на их базе. >и как много вопросов и напрасно потраченных часов отладки не было бы вообще. Это называется - приобретение опыта. В данном случае опыта програмирования контроллеров SCADAPack. Наверно, процесс этот неизбежен при таком плотном режиме работы, как у Вас, с любой техникой, не только с нашей. В описание библиотеки приведены примеры работы с библиотекой. Этого достаточно. Я не уверен, что смогу убедить Вас, но сам остаюсь на прежней точке зрения - большинство описанных Вами граблей относятся не к оборудованию, а к стилю программирования. С одной стороны, я понимаю Ваше желание делать так, как Вам максимально удобно, а значит быстро и качественно. С другой - знаю, что те же задачи можно решать иными подходами, более четко ориентиируясь на предоставляемые С и С-Tools в частности возможности. Кроме того, очевидно, что все возможные комбинации использования команд языка описать невозможно. сплошные #ifdef ... #endif есть абсолютно в любом исходном коде - начиная от операционных систем, заканчивая частными задачами. Это не является плюсом или минусом оборудования CMI. Это данность и, как Вы верно отметили, наша жизнь. Поэтому предложить я хотел бы следующее. Мне кажется, нет смысла "затачивать" документацию под каждого конкретного пользователя с учетом индивидуального стиля его работы. Пользователей множество, стилей не меньше. Руководство разрастется в полное собрание сочинений в десятках томов. Рациональней, мне думается, искать компромиссное решение, которое будет являться оптимальным как с точки зрения использования функционала контроллера, так и с точки зрения Ваших навыков, умений и опыта. И тут мы в чем-то безусловно можем Вам помочь, подсказывая те или иные варианты решения стоящих перед Вами задач.

Радик: Ок. Кому будет нужно, тот поймет из нашего обсуждения, о чем речь. А кому не нужно... Astilya пишет: Я не уверен, что смогу убедить Вас, но сам остаюсь на прежней точке зрения - большинство описанных Вами граблей относятся не к оборудованию, а к стилю программирования. да и не надо убеждать - грабли не важно чьи, главное что они под ногами. У меня нет цели свести все к недостаткам CMI или ещё какого производителя - пытаюсь просто рассказать, где и на чем можно найти себе проблемы, в том числе и в стиле программирования.

Astilya: Полностью согласен с Вами. Делиться опытом очень полезно. Я, в свою очередь, пытаюсь предложить альтернативы тем граблям, которые Вы обнаружили. В общем, можем продолжать и дальше. Надеюсь, кому-то да поможем.

Радик: Astilya , прошу проконсультировать Dogmeat`а по опциям RxControl и TxControl настройки COM портов в теме Правильное подключение телемеханики



полная версия страницы