QNX.ORG.RU

Разработка => Программирование под QNX => Тема начата: da-nie от Марта 15, 2017, 07:24:20 pm



Название: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Марта 15, 2017, 07:24:20 pm
Купил я недавно такую игрушку, как Flir One Gen 2. Это тепловизионная приставка к смартфону. Смартфонов у меня не водилось и не планируется, покупал чтобы достать датчик Lepton 3 и подключить к микроконтроллеру. Но, как оказалось, это устройство можно подключить к PC по USB собрав кабель micro USB (мама)->USB (папа), что я, собственно, и сделал. На форуме ( www.eevblog.com/forum/thermal-imaging/question-about-flir-one-for-android/?all ) ребята разобрались с протоколом обмена и привели программу для libusb. За это им огромное спасибо (особенно, tomas123). Я на их базе создал своё приложение под Windows. Оказалось, что под Windows ещё ни у кого такая игрушка не завелась - libusb выдаёт ошибку "неверный параметр". Ну и фиг с этой Windows. Под Linux (Mageia 5) всё заработало отлично. Под Raspbian на Raspberry Pi тоже всё работает (и даже количество кадров в секунду выше). Под XUbuntu, правда, libusb подглючивает и часто пишет error pipe, но в целом, данные приходят и картинка собирается. И вот захотелось мне запустить этот тепловизор под QNX.
И вот тут есть нюансы: я с USB знаком буквально неделю и представление имею недостаточное (да, я читал о стеке и как всё это устроено, но полной ясности для меня пока ещё нет). :) С помощью разных книжек, гугла, его переводчика (я слаб в английском) и этого форума, я сегодня сделал обвязку для работы через USB из-под QNX 6.3 SP3. И даже приходят данные. Только вот приходят они как-то странно - в картинку не собираются, но строки штатного ответа тепловизора (там есть текстовые строчки) я там вижу. Тепловизор этот имеет 3 интерфейса и bulk- и control-конечные точки. Изохронных точек нет (а я думал, что есть :) ). Гонял на Celeron 2.4 с 1 Гб ОЗУ - что на работе было для QNX, то и взял.

Я сделал так:

1) Определяем функции подключения и отключения устройства.
2) Делаем usbd_connect.
3) В функции подключения устройства делаем:
3.1) usbd_attach
3.2) usbd_interface_descriptor
3.3) Бегаем по всем конечным точкам интерфейса с помощью usbd_parse_descriptors и для нужных открываем каналы с помощью usbd_open_pipe.
3.4) Для каждого канала выделяем URB с помощью usbd_alloc_urb(NULL);
4) Когда все каналы готовы и все интерфейсы обработаны, начинаем работу с устройством:
4.1) С помощью usbd_alloc выделяем память для буфера данных (у меня один буфер на все команды - или нужен каждой по буферу? Насколько команды usbd_setup_bulk пересекаются?)
4.2) С помощью usbd_setup_vendor готовим команду управления (я так понял, usbd_setup_vendor эквивалента usbd_setup_control, которой уже нет в .h файле). И посылаем её с помощью usbd_io.
4.3) С помощью usbd_setup_bulk готовим обмен с устройством. И запускаем обмен с помощью usbd_io.
4.4) С помощью usbd_urb_status определяем, чем закончился обмен и сколько байт было принятно/передано.
4.5) Повторяем с 4.3 (после команды тепловизор только передаёт нам данные - передавать ему что-либо уже не обязательно).
5) В функции отключения устройства закрываем каналы ( usbd_close_pipe) и отключаем интерфейс ( usbd_detach ). После отключения всех интерфейсов отключаемся от стека USB ( usbd_disconnect ) и освобождаем буфер ( usbd_free ). (Кстати, не обратил внимания, нужно ли после usbd_alloc_urb этот самый URB удались какой-либо командой.  :-\ )

И вот какие вопросы возникли (может быть, кто-нибудь подскажет, а то информации практически ноль):

1) Если функции подключения и отключения асинхронны по отношению к основной программе, то во время работы программы указатели на всякие usbd_device_instance_t мгновенно станут недостоверными при отключении устройства? Как в таком случае быть? Причём, так же легко я потеряю и каналы и URB ( я ведь их освобождаю в функции отключения устройства). Или в функциях подключения/отключения просто вести подсчёт подключённых интерфейсов и заниматься освобождением ресурсов только когда все отключены? Как обычно делают?
2) usbd_setup_vendor имеет в конце указатель на буфер и длину. Длину чего? В libusb подобная функция имеет длину принимаемых данных от устройства в ответ на управляющую команду. В QNX, судя по потому, что если записать в длину 0 ничего не работает, это количество передаваемых байт (не дополнительно, а вообще?). Но зачем эта длина управляющей команде? Я уже определил эту длину, задав тип запроса, индекс и тому подобное.  :-\ Ну ладно, я задал длину равной 8 (вроде как управляющая команда столько и занимает). Ну а на самом деле что она значит и чему должна быть равна?
3) usbd_setup_bulk использует буфер. Какого максимального размера должен быть этот буфер? У меня кроме как 512 хорошо числа больше или меньше не работают. Была надежда на пару мегабайт - так вот, не работает. Или буфер должен быть не более, чем прописано в параметрах конечной точки?
4) Моя программа убивает нафиг io-usb через некоторое время. При этом загрузка процессора 100%. С буфером больше 512 всё сдыхает сразу же. С буфером меньшего размера ещё несколько секунд что-то поработает, но данные не принимаются. С буфером 512 данные принимаются как минимум минуту, но явно что-то по дороге теряется нафиг - картинка не собирается. В чём же тут дело-то?

Я приложил полный проект "драйвера". В нём много отладочной фигни и повторов таких вот отладочных блоков. Я храню все интерфейсы в списке list_SDevice; для каждого из устройств я открываю все каналы и храню там же в SDevice собственным вектором.

Собственно, может быть, кто-нибудь подскажет, как это всё сделать правильно? Спасибо. :)

P.S. А картинки должны получаться вот такие, как ниже. :) Модуль сборки картинки в этом проекте отсутствует - тут я просто сохраняю данные в файл, а потом его расшифровываю отдельной программой.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Марта 16, 2017, 08:08:48 pm
Интересно получается! Если в функции "устройство подключено" ( void InsertionFlirOne(usbd_connection *connect,usbd_device_instance_t *instance) ) просто собирать список из этих самых *instance, а потом, когда все устройства будут подключены их сделать usbd_attach, то происходят странные вещи. Интерфейсы добавлялись последовательно от 0 до 3. А при usbd_attach по списку они идут в обратном порядке (я это вижу по номерам интерфейсов и номерам конечных точек; однако, в список они добавлены в верном порядке).  8) Да ещё и последний не может сделать usbd_attach! Указатели что ли изменяются при добавлении нового устройства?
А ещё я нашёл, что нужно сделать usbd_select_config(). Этого я не сделал. :( Завтра проверю, поможет это или нет.

В общем, в документации где-то было:

Цитировать
How a class driver works

   A class driver typically performs the following operations:

1.   Connect to the USB stack (usbd_connect()) and provide two callbacks:
   one for insertion and one for removal.
2.   In the insertion callback:
   1.Connect to the USB device (usbd_attach()).
   2.Get descriptors (usbd_descriptor()).
   3.Select the configuration (usbd_select_config()) and interface (
      usbd_select_interface()).
   4.Set up communications pipes to the appropriate endpoint (
      usbd_open_pipe()).
3.   In the removal callback, detach from the USB device (usbd_detach()).
4.   Set up all data communications (e.g. reading and writing data, sending
   and receiving control information, etc.) via the usbd_setup_*()
    functions (usbd_setup_bulk(), usbd_setup_interrupt(), etc.).
5.   Initiate data transfer using the usbd_io() function (with completion
   callbacks if required).



Ну и ещё у меня новый вопрос: Зачем нужна память, выделенная usbd_device_extra для usbd_attach? Я так понял (читая старые темы этого форума), это блок пользовательской памяти и её использовать можно как угодно. Можно и не выделять её вовсе (я так и сделал). Верно ли это или эту память нужно выделять обязательно и она участвует в обмене с устройством?


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Марта 17, 2017, 09:41:25 am
Кстати, никто не подскажет, почему я не могу использовать volatile для вектора и как это победить? Никогда не использовал контейнеры stl с volatile.  8) А тут захотелось в контейнер собирать подключённые устройства.

Код:
volatile vector<SDevice> vector_SDevice;
long size=vector_SDevice.size(); - ошибка "passing `volatile std::vector<SDevice,std::allocator<SDevice> >' as `this' argument of `size_t std::vector<SDevice,std::allocator<SDevice> >::size() const' discards qualifiers"

Я так понял, для классов volatile неприменим? У меня VC6 такое не принимает:

Код:
class CTest
{
 unsigned long Variables;
 public:
  long size(void){return(0);}
};

volatile CTest cTest;

long size=cTest.size(); - ошибка.

Интересно, почему так? Я так понимаю, судя по сообщению компилятора, функция класса определена для обычного объекта без модификатора volatile и не может работать с valatile объектом.

Со структурами тоже весело:

Код:
struct SData
{
 long Data;
};

volatile SData sData_Array[100];

SData sData;
sData.Data=0;
sData_Array[0]=sData; - ошибка, не может выполнить =.

Понимаю, что оператор присваивания вызывался, но по факту-то я просто хочу заместить блок памяти. Что же, через memcpy делать это что ли? Или делать так ((SData*)sData_Array)[0]=sData;

Update.

Вот получилась программа, которая даёт 2-3 кадра в секунду. Остальные кадры она просто пропускает - не успевает всё принять. Если играться с временами ожидания или размером буфера, то io-usb быстро дохнет. Ещё я заметил, что поставив в цикл приёма данных delay(1) работать стало стабильнее - меньше вылетаний io-usb. Эта программа уже пишет tga-файлы в каталог. Всего-то выставил в usbd_io таймаут в ноль. :) И сразу стало принимать существенно быстрее. И буфер можно задать в 16384. С таким буфером работает. С большим в 2 раза уже нет.



Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Марта 17, 2017, 06:42:39 pm
Кстати, никто не подскажет, почему я не могу использовать volatile для вектора и как это победить? Никогда не использовал контейнеры stl с volatile.  8) А тут захотелось в контейнер собирать подключённые устройства...

Не совсем понятно для каких целей это вам понадобилось (учитывая что контейнеры stl не реализованы через волатильность).

Цитировать
Я так понял, для классов volatile неприменим? У меня VC6 такое не принимает:

Для объектов классов применим. Но обращатся через волатильный объект можно только к тем полям (переменным/методам и тд) которые объявлены с модификатором volatile.
Почему так? Примерно по той же причине что и ограничения накладываемые на объекты с модификатором const, (но ограничений чуть больше).
Конечно, всегда можно убрать "вызванные" ограничения компилятора через  const_cast, но опять возникает вопрос - зачем тогда вам волатильность?


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Марта 17, 2017, 06:53:44 pm
Я создал структуру подключённых устройств и планировал хранить список таких устройств в контейнере stl. Удобно просто. Но, так как функция подключения USB-устройства асинхронна по отношению к основной программе, нужно компилятору указать, чтобы он читал код "как есть" и не пытался оптимизировать доступ к контейнеру и кэшировать значения. Вот для этого-то я и хотел объявить контейнер volatile.

Цитировать
Для объектов классов применим. Но обращатся через объект можно только к тем полям (переменным/методам и тд) которые объявлены с модификатором volatile. Близкая аналогия с модификатором const, но со доп ограничениями.

Очень плохо. И очень неудобно. Вот есть у меня чей-то класс и я не смогу сделать его с volatile. Собственно, контейнеры это и показали.

Цитировать
Конечно, всегда можно убрать "ограничение вызова" через  const_cast, но опять возникает вопрос - зачем тогда вам волатильность?

Но сам-то объект остаётся при этом volatile. Только вот, интересно что меняется для компилятора в случае такого вот изменения типа непосредственно при обращении к volatile-объекту?


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Марта 17, 2017, 07:51:18 pm
...чтобы он читал код "как есть" и не пытался оптимизировать доступ к контейнеру и кэшировать значения...

В целях "обезопасить себя от гонок" многопоточности? Если только ради этого - то volatile это не инструмент, это иллюзия на инструмент. И чем сложнее пользовательский тип к которому он применяется тем страшнее иллюзия.

Цитировать
Очень плохо. И очень неудобно. Вот есть у меня чей-то класс и я не смогу сделать его с volatile. Собственно, контейнеры это и показали.

Не согласен с Вами и почему - я уже дал ответ выше. Если Вы хотите использовать volatile для объекта какого-то класса, значит Вы точно знаете для чего отключаете оптимизацию и как именно этот класс должен себя вести при этом. В противовес Вашим классам - разработчики библиотек дают общее решение и не могут знать заранее как именно вы собираетесь использовать их классы с волатильностью. Вы рассчитывали что объявив волатильный объект какого-то класса тем самым укажите компилятору применить волатильность ко всем полям/методам данного объекта? Тогда скажите как бы компилятор понял что волатильный и не волатильный объект принадлежит одному типу (допустил бы такое и скомпилировал удачно). Типы бы этих объектов были бы разные так как они бы содержали разный код их реализующий.
Допускаю, что это одна из причин, почему решено было сделать так - из волатильных объектов можно сослатся только на волатильность (либо принудительно const_cast и тогда компилятор умывает руки - вы сами себе доктор)
Я не зря сослался на аналогию с const модификатором - так как оба этих модификатора влияют также на сигнатуру методов.

Цитировать
Но сам-то объект остаётся при этом volatile. Только вот, интересно что меняется для компилятора в случае такого вот изменения типа непосредственно при обращении к volatile-объекту?

const_cast - в большинстве случаев первый звонок к тому что Вы делаете что-то не так и как правило заканчивается UB. Все что делает const_cast - это говорит от Вашего имени компилятору: "я лучше знаю, что это - поэтому я сам справлюсь, даже если прострелю себе ногу"


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Марта 17, 2017, 08:17:04 pm
Цитировать
В целях "обезопасить себя от гонок" многопоточности? Если только ради этого - то volatile это не инструмент, это иллюзия на инструмент. И чем сложнее пользовательский тип к которому он применяется тем страшнее иллюзия.

Да всё это понятно, но при любом раскладе (хоть с мютексом, хоть нет), необходимо объявить общие переменные с модификатором volatile. Мютекс, я, кстати, ещё не ставил - есть у меня подозрение, что эта асинхронная функция останавливает основной поток (я без понятия, как её вызывает QNX). Это я сперва проверю. А то с мютексом можно в неё зайти и больше не выйти.

Цитировать
Не согласен с Вами и почему - я уже дал ответ выше.

Сейчас поясню, что мне не нравится. :) Все эти const нужны для того, чтобы страховать от ошибок программиста. А volatile нужен всего лишь, чтобы программист точно знал, что вот как он написал, так и должен получится код и никакой самодеятельности компилятора не должно быть. И ровно в такой же последовательности этот код  должен выполниться, если volatile используется как барьер. Отсюда выглядит достаточно уродливым ограничение компилятора на контроль сложных типов. Нет, формально всё это логично - объект ведь иного типа. Но вот с точки зрения управляемости объектом выходит ерунда. Для структур и классов компилятор вызывает операцию присваивания и отказывает в компиляции, а для простых типов копирование идёт за милую душу. При этом для меня, создавшего структуру с volatile, это ограничение выглядит уродливым.
А причина уродства в том, что создав объект класса, я жду от компилятора всего того же, что он умеет делать с объектом, плюс отсутствие самодеятельности. То есть, у меня Миша и Коля объекты класса Человек, и я про Колю сказал компилятору, что Коля может сам по себе внезапно менять своё состояние, поэтому компилятору ни к коем случае не нужно что-то про Колю помнить, не спрашивая каждый раз об этом что-то самого Колю. Мне откровенно всё равно, что у компилятора одна функция-метод на все объекты и она - вот незадача - не умеет работать с Колей, за которым нужно следить. Конечно, она, эта функция, внутри оптимизирована без учёта слежения за таким, как Коля. Ну так пусть компилятор создаст вторую функцию, специально для таких вот Коль. Только пусть он от меня эту реализацию скроет.
Впрочем, хрен с ним. :)

Цитировать
const_cast - в большинстве случаев первый звонок к тому что Вы делаете что-то не так и как правило заканчивается UB

В большинстве, но не во всех же. :) Меня больше интересует, что для компиляции означает запись ((SData*)sData_Array)[0]=sData; где sData_Array объявлен volatile. Насколько это соответствует обычному копированию.

Цитировать
Все что делает const_cast - это говорит от Вашего имени компилятору: "я лучше знаю, что это - поэтому я сам справлюсь, даже если прострелю себе ногу"

Так ведь так и должно быть - вы должны лучше компилятора понимать, что вы пишете и как это работает. :) У меня тут полно странных товарищей, которые впишут пустые циклы для микроконтроллера, откомпилируют на -o3 и удивляются странным результатам - "компилятор хреновый, код не работает с оптимизацией, в асмовской трансляции команды отсутствуют". Они лучше компилятора знают, что хотят получить, но не прочли, как это объяснить компилятору и даже не догадываются, что этот компилятор такой вот умный.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Марта 18, 2017, 07:59:27 pm
Цитировать
Да всё это понятно, но при любом раскладе (хоть с мютексом, хоть нет), необходимо объявить общие переменные с модификатором volatile.
Совершенно не обязательно, и более того - для многопоточности совершенно не при любом раскладе. Еще раз, volatile в задачах многопоточности в большинстве случаев иллюзия. Хотите общие переменные между потоками - используйте atomic или другие механизмы синхронизации. При желании найдете точное описание стандарта, но навскидку - вот вам  ссылка  (http://en.cppreference.com/w/cpp/language/cv), в которой вполне себе ясно сказано, что волатильный объект не годится для "общего объекта" между потоками. В теории он бы может и был бы полезен в случае с несколькими потоками, но только если компилятор гарантирует атомарность операций с этим объектом - что в большинстве случаев не так, в добавок и не переносимо. Я уж молчу про сложные типы. В дополнение почитайте что пишут другие (http://alenacpp.blogspot.ru/2006/04/volatile.html)

Цитировать
Сейчас поясню, что мне не нравится. :) Все эти const нужны для того, чтобы страховать от ошибок программиста.

Не только от ошибок. Эти модификаторы влияют на сигнатуру функции. Функция с модификатором и без на конце - это разные методы.
Они составляют так же часть типа для переменных (c volatile в отличии от const есть в этом случае свои ньюансы). Это не просто страховка - это гарантия вызова конкретных методов, способа хранения (разное алацирование в памяти) и т.д. и т.п.

Может узнаете что-то новое (http://cpp.sh/56zsc).

Цитировать
Для структур и классов компилятор вызывает операцию присваивания и отказывает в компиляции

Только для тех типов которые не определили внутри себя методы работы с волатильностью или соответствующие волатильне публичные переменные.


Цитировать
Меня больше интересует, что для компиляции означает запись ((SData*)sData_Array)[0]=sData; где sData_Array объявлен volatile.
sData_Array - у Вас волатильный указатель (на начало массива) и только, о каком волатильном копировании идет речь мне трудно понять.


Цитировать
Так ведь так и должно быть - вы должны лучше компилятора понимать, что вы пишете и как это работает.
Компилятор вправе лучше знать как это будет работать на той платформе под которую он собирает код. Иначе возвращайтесь кодированию в машинных кодах и реализуйте все то что "должно быть" непосредственно в них.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Марта 18, 2017, 09:30:09 pm
Цитировать
Совершенно не обязательно, и более того - для многопоточности совершенно не при любом раскладе.

При любом, за исключением всяких новомодных прибамбасов. И практика работы такого ПО до появления С++ 11 это подтверждает. Особенно, на микроконтроллерах, которые этот самый С++ 11 никогда и не видели. Volatile - необходимое, но не достаточное условие работы в многопоточных системах. Атомарность же обеспечивается объектами синхронизации отдельно - их для этого и придумали. Но без volatile одних объектов синхронизации мало. Вместе же они составляют необходимый и достаточный набор элементов для работы с общими переменными для многопоточности.
Что же касается atomic:

Цитировать
Директива atomic работает быстрее, чем критические секции, поскольку некоторые атомарные операции могут быть напрямую заменены командами процессора. Следовательно, эту директиву желательно применять везде, где требуется защита общей памяти при элементарных операциях. К таким операциям, согласно спецификации OpenMP, относятся операции следующего вида:

x binop= expr
x++
++x
x--
--x

Здесь х - скалярная переменная, expr - выражение со скалярными типами, в котором не присутствует переменная х, binop - не перегруженный оператор +, *, -, /, &, ^, |, <<, или >>. Во всех остальных случаях применять директиву atomic нельзя (это проверяется компилятором).

Между тем, volatile+мютекс охватывают вообще всё, что только можно придумать. :)

Цитировать
Эти модификаторы влияют на сигнатуру функции... Это не просто страховка - это гарантия вызова конкретных методов, способа хранения (разное алацирование в памяти) и т.д. и т.п.  

В исполняемом файле никаких таких вещей нет. Все эти модификаторы живут только внутри компилятора. И нужны они только ему (это не более, чем ключи управления компиляцией). По этой причине все эти гарантии вызовов и определения - проблема компилятора. С моей же стороны нет никакого интереса задумываться, почему компилятор не желает сам следить за тем, какой код ему нужно сделать, а заставляет меня отдельно описывать функцию класса для volatile-элементов.

Цитировать
sData_Array - у Вас волатильный указатель (на начало массива) и только, о каком волатильном копировании идет речь мне трудно понять.

Нет, не volatile-указатель - volatile распространяется на элементы массива. Если в тексте я сделаю преобразование такого массива к обычному (был указатель на volatile-массив, а стал на обычный массив), то при записи по такому указателю не сделает ли компилятор какую-нибудь фишку, решив, что раз массив уже не volatile, то возможно всё. :) Впрочем, думаю, что не сделает - не вижу, что он тут способен испортить.  8)

Цитировать
Компилятор вправе лучше знать как это будет работать на той платформе под которую он собирает код.

Я не об этом (впрочем, асм я оставил без сожаления лет так 20 назад). Как автор программы, не компилятор, а я должен точно знать, как она должна выполняться. Где я могу отдать программу компилятору, а где его нужно направить, объяснить, что вот это понимать следует буквально. И полагаю, вы точно также знаете, как работает ваша программа и знаете это лучше компилятора. Заметьте, не как её оптимизировать, а как она должна выполняться.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Марта 18, 2017, 11:08:49 pm
Цитировать
При любом, за исключением ..

Так как речь идет об ключевых словах C++ - дайте ссылку на стандарт, который бы вам гарантировал что-либо в отношении volatile при multithreding. Ну или требования к переменным, что они должны быть минимум volatile )

Цитировать
Что же касается atomic ... Между тем, volatile+мютекс охватывают вообще всё, что только можно придумать.

Это абсолютно разные подходы - atomic и mutex. Атомарные операции я предложил как вариант, альтернативу volatile переменным, в отличии от которых, атомики честно решают (без блокировок) "разделение" переменной между потоками (и не требуют никаких volatile, да и начиная ++11 входят в состав языка). Но речь даже не в этом - я до сих пор не пойму, чем обычный мютекс с обычной переменной хуже того же мютекса но с volatile переменной (при условии что работа с переменной гарантирована мютексом)? ))

Если не сложно, опишите вообще - что Вы ожидаете от volatile переменной? Если только для того, чтобы она не оптимизировалась/отбрасывалась компилятором - то ок. А если надежда на что-то большее, приведите пример, упрощенный.

Цитировать
В исполняемом файле никаких таких вещей нет. Все эти модификаторы живут только внутри компилятора. И нужны они только ему (это не более, чем ключи управления компиляцией)
В исполняемом файле нет много чего: ни операторов присваивания, конструкторов, деструкторов, наследования, пользовательских типов  и бог весть чего еще нет. Между тем язык заставляет разработчика постоянно следить например - от имени какого типа производится вызов, что будет вызвано - присвоение или конструирование, будет ли умолчательное преобразование и т.д. Тем не менее вы используете язык С++, "играете" по его правилам. Модификатор - языковый а не компилятора, и требование относительно модификатора - это требования языка, а не компилятора. Так что не апеллируйте к исполняемому файлу. Язык - это абстракция, volatile - часть этой абстракции, исполняемый файл - реализация. Я вам уже давал ссылку выше на пример в котором "одинаковые" функции из-за модификаторов "одинаковыми" не являются. Это не просто ключи компилятора (или какой-то там inline) - это средство языка, которое влияет на подход к разработке кода (независимо от конкретного компилятора языка С++)

Цитировать
С моей же стороны нет никакого интереса задумываться, почему компилятор не желает сам следить за тем, какой код ему нужно сделать, а заставляет меня отдельно описывать функцию класса для volatile-элементов

Он не умеет это понимать - от него, мало того что не требует стандарт языка, (понимать так как Вы это видите), стандарт прямо требует обратное Вашим ожиданием поведение. Так что Ваши пожелания - отсылайте к стандарту языка.

Цитировать
Нет, не volatile-указатель - volatile распространяется на элементы массива
Ссылку пожалуйста  на стандарт (или на что либо авторитетное) на основании которой Вы сделали такой вывод, и пример кода. В свою очередь я уверен, что у Вас только указатель волатильный. Вот Вам пример, который как бы намекает на то что, для присвоения волатильного объекта нужно нечто больше нежели оператор присваивания по-умолчанию http://cpp.sh/5hmkk . Кроме того, как только Вы попытаетесь реализовать тривиальный оператор присваивания, скорее всего компилятор намекнет что он не совсем-то ему и подходит и волатильность он отбросит


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Марта 19, 2017, 07:38:57 am
Цитировать
дайте ссылку на стандарт, который бы вам гарантировал что-либо в отношении volatile при multithreding. Ну или требования к переменным, что они должны быть минимум volatile )

А что он должен гарантировать? Volatile гарантирует отсутствие кэширования переменной, что, собственно, и требуется при мультипоточности.

Цитировать
при условии что работа с переменной гарантирована мютексом

А, вы про то, что мютекс является барьером памяти? Ну да, он синхронизирует переменную при захвате и освобождении. Впрочем, я это совсем упустил из вида - мютекс и мютекс, приелось как-то (вчера задумался, а почему я не описал volatile во всех своих многопоточных проектах переменные и почему это работает до сих пор). ::) Да, одного мютекса будет достаточно. Это для микроконтроллеров, где их нет, нужен volatile и всякие аналоги sei() и cli() для запрета и разрешения прерываний.  
Впрочем, интересно, везде ли это работает - обычно, про это мало где написано и вот так специально не освещается (защитите переменную мютексом - и никаких проблем. А вот почему автоматически обеспечивается аналог volatile на границах мютекса почти никогда не упоминается). Тут компилятор был для TMS320 (1967ВЦ1Т), так он даже long a[3]={1,2,3} не выполнял, хотя ошибки компиляции не было. А что в стандарте написано? Всегда гарантируется барьер памяти на объектах синхронизации или варьируется от компилятора к компилятору?

Цитировать
Язык - это абстракция, volatile - часть этой абстракции,

Вот. Абстракция. Следовательно, это нужно только чтобы программист управлял компиляцией и сам себя страховал (отсюда всякие explicit, const и прочие)
Что компилятор не умеет различать и создавать функции сам, понятно.

Цитировать
Ссылку пожалуйста  на стандарт (или на что либо авторитетное) на основании которой Вы сделали такой вывод, и пример кода.

Я не знаю, что для вас авторитетное, но, например, вот тут так и пишут: http://valera.asf.ru/cpp/book/c03_13.html И я других мнений про volatile для массива не встречал. Что касается ссылки на стандарт, я без понятия, где этот стандарт вообще ищется (и есть ли на русском он вообще).
В примере я нифига не понял, что я должен увидеть. Или его надо откомпилировать и прочитать, что напишет компилятор? А что он напишет? Что volatile_b = volatile_a; не может сделать, потому что оператор "=" не задан для volatile-объектов?

А вот про usb, я так полагаю, никто не сможет ничего подсказать? Жалко. Я до сих пор не понимаю, почему io-usb так легко завершается аварийно таким простым "драйвером".


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Марта 19, 2017, 12:46:36 pm
Цитировать
.. что, собственно, и требуется при мультипоточности.
и этого НЕДОСТАТОЧНО для мультипоточности. А механизмы которые полностью удовлетворяют мультипоточности - не требуют volatile. Такие механизмы стали частью языка с ++11, а до этого реализовывались средствами конкретной платформы и для них volatile фактически не требуется. Или, если требуется - то это специфическая платформа.

Цитировать
А, вы про то, что мютекс является барьером памяти?
Я про то что мьютекс - является объектом ядра. Операции над ним - "атомарны" и гарантировано это самим ядром (как правило ядро - это ОС). Про контроллеры я вам ничего не скажу.


Цитировать
А что в стандарте написано? Всегда гарантируется барьер памяти на объектах синхронизации или варьируется от компилятора к компилятору?
В стандарте до ++11 и его использованием std::thread ничего не сказано ни про какие объекты синхронизации, он о них не знает. И потому тот стандарт ничего вам не гарантирует и не требует. Гарантии на работу с такими объектами дает система (библиотека которая обобщает и обворачивает эти вызовы), к которой указанные объекты принадлежат. Начиная с ++11 в стандарте прописана работа с потоками и механизмами синхронизации (std::thread), там же и указаны гарантии/требования к объектам типы которых входят в это пространство имен.

Цитировать
Вот. Абстракция. Следовательно, это нужно только чтобы программист управлял компиляцией и сам себя страховал (отсюда всякие explicit, const и прочие)
Нет, абстракция нужна для того чтобы задача была разделена на определенные уровни облегчающие ее понимание и упрощающие поиск решения задачи. Объявив метод как константный, я не просто прошу компилятор позаботится что бы кто-то случайно не обратился к this как к обычному указателю. Я так же сообщаю пользователям этого метода - что у него есть контракт, который гарантирует возможность вызова этого метода для константных объектов. Вот что я имел ввиду упоминая "абстракцию". Точно так же и с volatile

Цитировать
Я не знаю, что для вас авторитетное, но, например, вот тут так и пишут: http://valera.asf.ru/cpp/book/c03_13.html
Не знаю что за ресурс, но даже там ничего не сказано про массивы (и Ваше ожидание к ним) и не сказано про многопоточность (и Ваше ожидание к ним) - откуда у вас такие ожидания тогда?)

Цитировать
Что касается ссылки на стандарт, я без понятия, где этот стандарт вообще ищется (и есть ли на русском он вообще).
Можно так например https://google.gik-team.com/?q=c%2B%2B+standard+pdf

Цитировать
В примере я нифига не понял, что я должен увидеть. Или его надо откомпилировать и прочитать, что напишет компилятор? А что он напишет? Что volatile_b = volatile_a; не может сделать, потому что оператор "=" не задан для volatile-объектов?
Да, нужно запустить (это возможность я оставил для Вас :) ).
Да именно так, не может присвоить объекты если они были объявлены как волфтильные. Даже если при этом типы простые структуры у которых компилятор встраивает оператор присваивания с побитовым копированием. Другими словами не волатильные объекты присвоить компилятор даст, а волатильные - требует наличие принудительно написанного специального оператора присваивания.
А теперь возвращаясь к Вашим вопросам - у вас волатильный указатель на массив. Почему я так решил? Да потому что иначе Вам бы пришлось для структуры, типы которых вы пытаетесь "скопировать", написать руками "специальный" оператор. Написав такой оператор Вы бы не утверждали что с пользовательскими типами волатильность не работает. Делаю вывод что такого оператора в структуре/типе нет. А раз нет, компилятор ничего не сказал - значит волатильный только указатель. Если компилятор что-либо сказал (подобное что в моей ссылке) и вы делаете преобразование принудительно - то опять же, теряете волатильность для операции присвоения (оставляя для указателя на массив).
Как именно конкретно у Вас объявлен sData_Array - вы не предоставили, поэтому я взял смелость сделать такой вывод (можно как массив C, а можно как указатель, а можно же как std::vector а может и как std::array, а может вообще это тип со своим перегруженным оператором индекса)

Мне кажется мы и так затянули про volatile - так что если возникли вопросы, пишите лично. Более мне тут сказать нечего)


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Марта 19, 2017, 01:24:21 pm
Цитировать
и этого НЕДОСТАТОЧНО для мультипоточности.

Ещё раз. Никто и не писал, что этого ДОСТАТОЧНО. ;)

Цитировать
А механизмы которые полностью удовлетворяют мультипоточности - не требуют volatile

На микроконтроллерах - требуют. Вы ограничиваетесь исключительно теми платформами, которые ваш любимый Си++ 11 поддерживают и отчего-то полагаете, что так везде? Ну так нет.

Цитировать
Я про то что мьютекс - является объектом ядра. Операции над ним - "атомарны" и гарантировано это самим ядром (как правило ядро - это ОС). Про контроллеры я вам ничего не скажу.

А как по-вашему компилятор соображает, что переменная внутри Lock() - Unlock() мютекса и внешняя переменная не volatile типа могут быть связаны? Сам мютекс, как объект, ничего не знает о защищаемых переменных.  
Пример:
Код:
bool Done=false;

pthread_mutex_t mutex_ID;

//первый поток
void Thread_1(void)
{
 sleep(10);
 pthread_mutex_lock(&mutex_ID);//блокируем мьютекс
 Done=true;
 pthread_mutex_unlock(&mutex_ID);//разблокируем мьютекс
}

//второй поток
void Thread_2(void)
{
 pthread_mutex_lock(&mutex_ID);//блокируем мьютекс
 Done=false;
 pthread_mutex_unlock(&mutex_ID);//разблокируем мьютекс
 sleep(1);
 while(1)
 {
  pthread_mutex_lock(&mutex_ID);//блокируем мьютекс
  if (Done==true) break;
  pthread_mutex_unlock(&mutex_ID);//разблокируем мьютекс
 }
 pthread_mutex_unlock(&mutex_ID);//разблокируем мьютекс
}

В этом примере if (Done==true) break; компилятор, основываясь только на мютексе, запросто мог бы выкинуть эту строчку. А что? Он ничего не знает о том, что Done способно измениться внезапно. Так мютекс не просто объект ядра, мютекс реализует вот какой механизм:

acquire_barrier(); - барьер захвата
if (Done==true) break;
release_barrier(); - барьер освобождения

Подробнее тут: http://scrutator.me/post/2015/05/16/memory_barriers.aspx
И вот именно поэтому и не нужен volatile внутри мютекса. И компилятор (а не только ядро) всё это знают.
Но когда вы начнёте писать для микроконтроллеров, там не будет барьеров и всё это вы будете делать сами с помощью этого самого volatile. Поясню примером.
Допустим, вы в прерывании меняете значение Done. А атомарность обеспечите sei и cli. Всё, что между ними - атомарно (прерывание запрещено). Но компилятор похерит if (Done==true) break; с вероятностью 100%.

Цитировать
Я так же сообщаю пользователям этого метода

Чем же это противоречит тому, что я писал? Все эти модификаторы нужно программисту и только ему.

Цитировать
Не знаю что за ресурс, но даже там ничего не сказано про массивы

Как это? "ixa – неустойчивый массив целых, причем каждый элемент такого массива считается неустойчивым. "

Цитировать
Да именно так, не может присвоить объекты если они были объявлены как волфтильные.

Вообще-то, мы с этого и начинали - у меня такая фигня и не сработала с контейнером stl. ;) И я буквально в том же сообщении, где спрашивал, почему там, указал и разгадку по моему предположению (и не ошибся).

Цитировать
Мне кажется мы и так затянули про volatile - так что если возникли вопросы, пишите лично. Более мне тут сказать нечего)

А вот вы зря. Тема эта интересная. И очень полезная. Потому как многопоточность много где применяется


Кстати, я ведь написал, как и что объявлено:

Код:
struct SData
{
 long Data;
};

volatile SData sData_Array[100];

SData sData;
sData.Data=0;
sData_Array[0]=sData; - ошибка, не может выполнить =.

Понимаю, что оператор присваивания вызывался, но по факту-то я просто хочу заместить блок памяти. Что же, через memcpy делать это что ли? Или делать так ((SData*)sData_Array)[0]=sData;

И вот так работает: ((SData*)sData_Array)[0]=sData;



Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: darkelf от Марта 20, 2017, 12:15:24 pm
Цитировать
А механизмы которые полностью удовлетворяют мультипоточности - не требуют volatile
На микроконтроллерах - требуют. Вы ограничиваетесь исключительно теми платформами, которые ваш любимый Си++ 11 поддерживают и отчего-то полагаете, что так везде? Ну так нет.
Сорри, что вмешиваюсь - но микроконтроллеры это своя, очень специфичная область, в которой очень много ньюансов. По моему мнению, volatile, по крайней мере на этапе изобретения, был придуман вовсе не для многопоточности и синхронизации доступа, а для обращения к устройствам, отображенным на память. Для этого он вполне себе подходит, для других вещей, имхо - не очень, если не сказать хуже.

Цитировать
Я про то что мьютекс - является объектом ядра. Операции над ним - "атомарны" и гарантировано это самим ядром (как правило ядро - это ОС). Про контроллеры я вам ничего не скажу.

А как по-вашему компилятор соображает, что переменная внутри Lock() - Unlock() мютекса и внешняя переменная не volatile типа могут быть связаны? Сам мютекс, как объект, ничего не знает о защищаемых переменных.  
Пример:
Код: (C++)
bool Done=false;

pthread_mutex_t mutex_ID;

//первый поток
void Thread_1(void)
{
 sleep(10);
 pthread_mutex_lock(&mutex_ID);//блокируем мьютекс
 Done=true;
 pthread_mutex_unlock(&mutex_ID);//разблокируем мьютекс
}

//второй поток
void Thread_2(void)
{
 pthread_mutex_lock(&mutex_ID);//блокируем мьютекс
 Done=false;
 pthread_mutex_unlock(&mutex_ID);//разблокируем мьютекс
 sleep(1);
 while(1)
 {
  pthread_mutex_lock(&mutex_ID);//блокируем мьютекс
  if (Done==true) break;
  pthread_mutex_unlock(&mutex_ID);//разблокируем мьютекс
 }
 pthread_mutex_unlock(&mutex_ID);//разблокируем мьютекс
}

В этом примере if (Done==true) break; компилятор, основываясь только на мютексе, запросто мог бы выкинуть эту строчку. А что? Он ничего не знает о том, что Done способно измениться внезапно. Так мютекс не просто объект ядра, мютекс реализует вот какой механизм:

acquire_barrier(); - барьер захвата
if (Done==true) break;
release_barrier(); - барьер освобождения

Подробнее тут: http://scrutator.me/post/2015/05/16/memory_barriers.aspx
И вот именно поэтому и не нужен volatile внутри мютекса. И компилятор (а не только ядро) всё это знают.
Сорри, не совсем понял, что должен был выкинуть компилятор?

Ну и кроме того - при обсуждении необходимо смотреть стандарты. Функции семейства pthread* покрывает не стандарт C++, и даже не стандарт C, а POSIX - видимо туда и надо смотреть.

Но когда вы начнёте писать для микроконтроллеров, там не будет барьеров и всё это вы будете делать сами с помощью этого самого volatile. Поясню примером.
Допустим, вы в прерывании меняете значение Done. А атомарность обеспечите sei и cli. Всё, что между ними - атомарно (прерывание запрещено). Но компилятор похерит if (Done==true) break; с вероятностью 100%.
По-моему тут (http://stackoverflow.com/questions/24137964/does-pthread-mutex-lock-contains-memory-fence-instruction) есть объяснение почему в обвёртке pthread_mutex_*() всё работает, а в sie/cli - нет. "Any function whose definition is not available in this translation unit (and is not intrinsic) is a compiler memory barrier. pthread_mutex_lock, pthread_mutex_unlock, pthread_create also issue a hardware memory barrier to prevent the CPU from reordering reads and writes." sie/cli - скорее всего реализованы или через inline-ассемблер, или те самые intrinsic-и, а pthread_mutex_lock()/pthread_mutex_unlock() - через внешние, для данного модуля, библиотеки.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Марта 20, 2017, 02:42:46 pm
Цитировать
Сорри, не совсем понял, что должен был выкинуть компилятор?

Да очень простую вещь:

bool done=false;

while(1)
{
 if (done==true) break;
}

Компилятор выбросит if (done==true) break; так как он видит, что done не меняется. При этом если done будет volatile, то компилятор эту строчку не выбросит. Либо если этот контроль будет внутри заблокированного мютекса.

Цитировать
По-моему тут есть объяснение почему в обвёртке

Вот, я и написал, что мютекс - это барьер памяти. Это совершенно особая вещь для компилятора.

И всё же, хоть надежды и нет (эти люди были тут лет 12 назад), может быть, кто-нибудь знакомый с программированием USB драйверов для QNX сможет мне ответить на мои вопросы? :) У меня работает, но с "нюансами" и вылетами io-usb.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: darkelf от Марта 20, 2017, 03:06:45 pm
Цитировать
Сорри, не совсем понял, что должен был выкинуть компилятор?

Да очень простую вещь:

bool done=false;

while(1)
{
 if (done==true) break;
}

Компилятор выбросит if (done==true) break; так как он видит, что done не меняется. При этом если done будет volatile, то компилятор эту строчку не выбросит. Либо если этот контроль будет внутри заблокированного мютекса.

Цитировать
По-моему тут есть объяснение почему в обвёртке

Вот, я и написал, что мютекс - это барьер памяти. Это совершенно особая вещь для компилятора.
из того, что было написано по той ссылке - барьерами памяти является любой вызов функции, а то, что при этом pthread_mutex_*(), имхо, это дело десятое. Например Вы можете сделать свои функции обвёртки над мутексами и про то, что это были pthread_mutex_*() узнает только линковщик, а если подгрузить библиотеку динамически dlopen()/dladdr(), то и он не узнает.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Марта 20, 2017, 03:07:01 pm
Цитировать
Вы ограничиваетесь исключительно теми платформами, которые ваш любимый Си++ 11 поддерживают и отчего-то полагаете, что так везде?
Вы пишите на языке С++, компилятор гарантирует выполнение конкретных его стандартов (если не гарантирует - то это like C++, и по каким он правилам играет - разбирайтесь отдельно). Компилятор может "расширять/дополнять" поведение языка, если стандарт в этом направлении ему это не запрещает. Но если запрещает явно или четко прописывает поведение - то компилятор не вправе его нарушать. Иначе он не соответствует стандарту.
Почему я "решил" что так "везде"? Да потому, что стандарт на то и стандарт - чтобы унифицировать и зафиксировать поведение.
 

Цитировать
А как по-вашему компилятор соображает, что переменная внутри .. Пример:
Например так (что собственно и должно быть) - переменная ваша статическая (то есть глобальная). Она не принадлежит ни одному из блоков вызова, а значит может/вправе изменятся извне. Такую переменную он не вправе выкинуть (ИМХО). Ну или приведите обратные доказательства этому.
И да, порядок присвоения/чтения без мютекса не гарантирован - но переменная не будет выброшена.

Цитировать
Подробнее тут: http://scrutator.me/post/2015/05/16/memory_barriers.aspx
Ничего там нет про volatile. А знаете почему? Потому что это статья совсем о другом - о memory ordering. При этом volatile на memory ordering не влияет никак (опять же - уверенное ИМХО, хотите убедить в обратном - дайте ссылку).

Цитировать
Так мютекс не просто объект ядра, мютекс реализует вот какой механизм. И вот именно поэтому и не нужен volatile внутри мютекса. И компилятор (а не только ядро) всё это знают.
Возможно я не корректно выразился прошлый раз - да, не только ядро. Но это никак не "улучшает" отношение к volatile.

Цитировать
Но когда вы начнёте писать для микроконтроллеров
В примере (с переменной) нет никакого намека на код для микроконтроллеров. Обсуждать сферического коня в вакууме  неинтересно.

Цитировать
Как это? "ixa – неустойчивый массив целых, причем каждый элемент такого массива считается неустойчивым. "

Тут соглашусь - я не прав, при таком объявлении - да.

Цитировать
Вообще-то, мы с этого и начинали..
Опять же, в этом моменте был я невнимателен - у Вас массив с волатильными данными. Но присваивание - не волатильное.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Марта 20, 2017, 03:48:10 pm
Цитировать
из того, что было написано по той ссылке - барьерами памяти является любой вызов функции, а то, что при этом pthread_mutex_*(), имхо, это дело десятое.

Ну прочтите ж вы ссылку, что я давал! Там написано, какие вообще эти барьеры бывают. Мютекс - это барьер захвата/освобождения.

Цитировать
Например так (что собственно и должно быть) - переменная ваша статическая (то есть глобальная). Она не принадлежит ни одному из блоков вызова, а значит может/вправе изменятся извне. Такую переменную он не вправе выкинуть (ИМХО). Ну или приведите обратные доказательства этому.

А вы откомпилируйте без мютекса на -o3 и гляньте, что будет в асме. :) Думаю, компилятор вас удивит.

Цитировать
Ничего там нет про volatile. А знаете почему? Потому что это статья совсем о другом - о memory ordering. При этом volatile на memory ordering не влияет никак (опять же - уверенное ИМХО, хотите убедить в обратном - дайте ссылку).

Во-первых, статью я давал для показа, что из себя представляет мютекс, а не volatile. А во-вторых, Volatile тоже является барьером памяти. Просто вбейте в гугле "volatile барьер памяти" и всё найдётся сразу же.

Например:

Цитировать
В Visual C++ оно имеет следующие свойства:

1) Уберегает volatile-переменную от агрессивной оптимизации компилятора.
Например, в результате оптимизации компилятор мог бы поместить переменную в
регистр, и тогда ее изменения были бы не видны другим потокам (т.к. у каждого
потока свой набор регистров). volatile защищает от этого, заставляя компилятор
каждый раз при обращении к volatile-переменной генерировать чтение и запись в
соответствующую ячейку памяти, даже если это явно невыгодно и связано с
потерей эффективности. Это называется видимость (visiblilty). Форсирование
чтения и запись в память при обращении к volatile-объекту чрезвычайно важно
для одного из пунктов ниже.

2) Ставит барьер компилятора (compiler barrier).
Компилятор не переупорядочивает обращения к volatile-переменным.
В результате, если мы пишем "int volatile a = 100; int volatile b = 200;",
то можем быть уверенными, что в сгенерированном коде запись в "a"
гарантированно будет находиться до записи в "b". Аналогичного эффекта
можно достичь, применяя специальные макросы - _ReadBarrier, _WriteBarrier и
_ReadWriteBarrier. Подчеркну, что это лишь компиляторный барьер, на
уровне CPU все равно возможно переупорядочивание (см. ниже).

3) Чтение и запись в volatile-переменную ставит неполный барьер памяти на
архитектурах IA-32 и AMD64 (acquire and release semantics). Это, так сказать,
небольшой, но приятный побочный эффект volatile, благодаря которому в некоторых
случаях можно обойтись без установки явных барьеров памяти - mfence, xchg и т.п.
Барьеры памяти, если кто не в курсе, нужны, чтобы гарантировать порядок доступа к
памяти на уровне CPU. Например, на упомянутых архитектурах последовательность
операций write-read может быть выполнена, как read-write, и код, который от этого
порядка зависит, может поломаться. Если интересует, могу дать несколько ссылок.

4) Вопреки распостраненному мнению, volatile не обеспечивает атомарности, он
связан лишь с видимостью.
Например, на 32-битных архитектурах доступ к 64-битной переменной не атомарен, и
volatile здесь не помощник.

Цитировать
В примере (с переменной) нет никакого намека на код для микроконтроллеров. Обсуждать сферического коня в вакууме  неинтересно.

Вы можете такой же код запустить под DOS (там прерывания позволяют с собой работать напрямую по-честному) и всё будет то же самое. Впрочем, под QNX тоже можно проверить.

Цитировать
Возможно я не корректно выразился прошлый раз - да, не только ядро. Но это никак не "улучшает" отношение к volatile.

Всё дело в том, что вы ходите по кругу и меняете отношение к volatile кого-то абстрактного, не меня. :) Я ведь вам уже объяснил, что да как и с volatile и с мютексом, и отношение к атомарным операциям тоже показал. Так кому вы объясняете вашу нелюбовь к volatile? Мне? Зачем? Я и так абсолютно точно знаю, как этот volatile влияет на результат компиляции и точно также я отлично знаю, что даст atomic и мютекс (я ассемблер x86, конечно, за десятилетия подзабыл, но в целом как работает процессор я понимаю). Поэтому давайте просто забудьте про volatile, пока не столкнётесь с аппаратурой (для записи в память устройств, как верно вспомнил darkelf) или при обработке прерываний без поддержки ОС. :)

Новое обновление программы. :)


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Марта 20, 2017, 05:13:06 pm
Цитировать
Всё дело в том, что вы ходите по кругу
Цитировать
Volatile тоже является барьером памяти. Просто вбейте в гугле "volatile барьер памяти" и всё найдётся сразу же.

Вбил и впервых трех ссылках вот вам ответ - ЯЗЫК НЕ ГАРАНТИРУЕТ вам никаких барьеров с volatile. Смотрим тут (http://stackoverflow.com/questions/31944881/volatile-vs-memory-fences) и тут (http://stackoverflow.com/questions/26307071/does-the-c-volatile-keyword-introduce-a-memory-fence) для примера. В вашем компиляторе есть? Поздравляю. Но попробуете пересобрать другим - ваших барьеров может и не быть.

Цитировать
А вы откомпилируйте без мютекса на -o3 и гляньте, что будет в асме. Smiley Думаю, компилятор вас удивит.

Откомпилировал в MSVS2013 с Ox (full optimization) под win8.1 x64 такой код:
Код:
#include "stdafx.h"
#include <iostream>
#include <string>
#include <chrono>
#include <thread>

bool isDone = false;
int Value = 0;


void firstThread()
{
 std::this_thread::sleep_for(std::chrono::seconds(2));
 while (true)
 {
  if (isDone)
  {
   Value = 20;
   break;
  }
 }
}

void secondThread()
{
 std::this_thread::sleep_for(std::chrono::seconds(1));
 isDone = true;
}

int _tmain(int argc, _TCHAR* argv[])
{
 Value = 10;
 std::thread first(firstThread);
 std::thread second(secondThread);
 first.join();
 second.join();

 std::cout << "isDone: " << isDone << std::endl << "Value: " << Value << std::endl;
 return 0;
}


Получил вот такой вывод:
Цитировать
isDone: 1
Value: 20
Что я делаю не так (наблюдения подсказали что переменные не отброшены)? Ассемблер - показывает что проверка и изменение значения присутствуют. Что еще предоставить?)

Цитировать
В Visual C++ оно имеет следующие свойства:
Вот зачем Вы про студию? Ну не надо же было.. А впрочем почитайте на досуге что они сами пишут, чем читать о них от третьих лиц - https://msdn.microsoft.com/ru-ru/library/12a04hfd.aspx
Обратите внимание на настоятельные рекомендации, и то как они по умолчанию работают с volatile (специальные опции для этого у студийного компилятора есть).

Ну и под конец - я не хочу Вас ни в чем разочаровывать. Мне это тоже незачем). Считаете что volatile должен влиять на memory ordering - ну и Бог с Вами!) Мне показалось просто что Вы спрашивали - я же отвечал (подтверждая ссылками).
Я точно знаю что такое поведение не гарантировано, и чтобы обеспечить его я уж лучше буду использовать переносимые, стандартные и гарантированные механизмы нежели "пришитый нитками" к volatile.
Вам же желаю успехов в этом деле!


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Марта 20, 2017, 06:59:41 pm
Что я делаю не так
Тут отвечу сам - забыл указать инструкции относительно предварительного изменение isDone в secondThread(). Вызов конечно он оптимизирует, проверка на if будет выкинута компилятором. В этом Вы правы, хотя откровенно сказать - я не утверждал обратное. Я почему-то понял что вы настаиваете на исключении всей переменной из кода (с этим готов был поспорить).

Но ок - исключается строчка if, volatile все равно не решит вопрос memory order переносимо при использовании в multithreading (только для конкретных наборов компиляторов, под конкретный набор платформ). А это значит что без принудительного (мютекс и т.д.) барьера - он использование volatile может быть опасно (при смене компилятора или платформы), а с доп. барьером - он как минимум избыточен (и в добавок может влиять на производительность).


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Марта 20, 2017, 07:34:27 pm
Цитировать
ЯЗЫК НЕ ГАРАНТИРУЕТ

Язык, надо полагать, много чего вам не гарантирует. Те же атомарные операции далеко не на всех процессорах вообще возможны в полном объёме, указанном в Open MP (интересно, как вы атомарно на Z80 сложите два 32 битных числа :) ). Но на платформе x86 volatile барьер памяти создаёт. Или вам нужно, чтобы создавал везде и всегда? Ну так подобная категоричность вряд ли уместна. На вашем компьютере volatile создаёт барьер памяти и точка.
И, если не трудно, ссылки на английский не надо - я ведь предупредил, что мои знания английского весьма низкие. Я не понимаю, что там написано и что в комментариях ответили. Тем не менее, как минимум барьер компилятора (я их, вообще говоря, с барьерами памяти не разделяю - на любом процессоре, который умеет переставлять инструкции это практически одно и то же) volatile создаёт. Уж не знаю, написано это в стандарте языка или нет, но любой (почти :) AVR GCC, например, этого не знает :) ) компилятор точно знает, что порядок volatile нарушать НЕЛЬЗЯ.

Цитировать
Откомпилировал в MSVS2013 с Ox (full optimization) под win8.1 x64 такой код:

Ну, стало быть, так просто нужного эффекта не добиться. Но рано или поздно компилятор выбросит этот блок. Запустил на VC6. Какую оптимизацию не выбирай, код получается вот таким:
Код:
0040101E loc_40101E:                             ; CODE XREF: _WinMain@16+36j
0040101E                 mov     eax, 1
00401023                 test    eax, eax
00401025                 jz      short loc_401038 - while (1)
00401027                 xor     ecx, ecx
00401029                 mov     cl, byte_4054C0 - переменная done
0040102F                 cmp     ecx, 1
00401032                 jnz     short loc_401036
00401034                 jmp     short loc_401038 - на выход
00401036 loc_401036:                             ; CODE XREF: _WinMain@16+32j
00401036                 jmp     short loc_40101E

Это while(1) {if (done==true) break;}
Но вообще говоря, пример, который я привёл, классический. И он 100% рано или поздно сработает. Только нужно попасть в условия, когда компилятор не будет while(1) заменять на запись 1 в регистр, обнуление и сравнение с 0. Так что даже не сомневайтесь, приведённый пример - классика ошибок в ПО при параллельной работе с данными.

Цитировать
Обратите внимание на настоятельные рекомендации, и то как они по умолчанию работают с volatile (специальные опции для этого у студийного компилятора есть).

От того, что не на x86 барьер памяти не нужен, вовсе не следует, что volatile там будет себя вести как-то иначе. Да, он там не будет барьером памяти (если вы на это надеетесь), но барьер компилятора всё равно остаётся. Всё, что по вашей ссылке написано важного, так это ссылка вот на что:
"/volatile:ms ... Семантика получения и освобождения гарантируется при переменном обращении. Однако этот параметр также заставляет компилятор генерировать барьеры памяти оборудования, которые могут добавлять значительную нагрузку на ARM и другие архитектуры со слабым упорядочиванием памяти."

Цитировать
Я точно знаю что такое поведение не гарантировано,

Какое именно поведение? Отсутствие кэширования переменной? Ну так оно как раз гарантировано.

Цитировать
Вам же желаю успехов в этом деле!

Спасибо. :) Вам тоже успехов в ваших делах.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Марта 20, 2017, 07:38:37 pm
Цитировать
Тут отвечу сам - забыл указать инструкции относительно предварительного изменение

О! Вам удалось! А мне пока не удалось заставить VC6 это оптимизировать...  8) (Да, я люблю именно VC6, как ни странно :) ). :)

Цитировать
использование volatile может быть опасно


Всё верно! Его никто так не использует без атомарности. Просто я на автомате похерил, что мютексу не нужен volatile, вот и вся причина, зачем я его захотел внести во все общие переменные. А потом задумался, почему же я никогда так не делал. И вспомнил, почему. :)


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: darkelf от Марта 20, 2017, 08:32:07 pm
Цитировать
из того, что было написано по той ссылке - барьерами памяти является любой вызов функции, а то, что при этом pthread_mutex_*(), имхо, это дело десятое.

Ну прочтите ж вы ссылку, что я давал! Там написано, какие вообще эти барьеры бывают. Мютекс - это барьер захвата/освобождения.

Во-первых, статью я давал для показа, что из себя представляет мютекс, а не volatile. А во-вторых, Volatile тоже является барьером памяти. Просто вбейте в гугле "volatile барьер памяти" и всё найдётся сразу же.
Например, тут (https://habrahabr.ru/post/155517/#comment_5309295) пишут, что volatile не является барьером памяти. Эта Функциональность, как и сказал lastcross была привязана только в майкрософтовском компиляторе, и очень даже нестандартна.

Вот и в Wikipedia пишут (https://en.wikipedia.org/wiki/Memory_barrier) - "The keyword volatile does not guarantee a memory barrier to enforce cache-consistency.". Кстати, для 86 архитектуры пришлось придумывать команду mfence, чтобы гарантировать барьеры памяти на уровне команд, а в компиляторах сначала приходилось обходиться разными asm volatile("" ::: "memory"), а начиная с C11/C++11 - более цивилизовано - функции atomic_signal_fence(memory_order_acq_rel) и atomic_thread_fence(memory_order_acq_rel) - см здесь (https://en.wikipedia.org/wiki/Memory_ordering).

А вызовы функций, думаю любых, командой call, барьером являются, иначе-бы могли происходить такие интересные вещи как вызов функции до вычисления всех входных её аргументов.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Марта 20, 2017, 09:04:56 pm
Цитировать
Эта Функциональность, как и сказал lastcross была привязана только в майкрософтовском компиляторе,

Ну, я и не обещал повсеместности. :) Вообще говоря, я читал мнение, что volatile как раз обеспечивает привязку аппаратуры к языку - практику к теории. Если Си++ не озаботился жёсткими гарантиями барьера, а без них код конкретного процессора (а x86 имеет для этого море возможностей) может вести себя непредсказуемо, то компилятор будет просто вынужден ставить барьер.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Марта 21, 2017, 02:29:49 pm
Язык, надо полагать, много чего вам не гарантирует. Те же атомарные операции далеко не на всех процессорах вообще возможны в полном объёме, указанном в Open MP (интересно, как вы атомарно на Z80 сложите два 32 битных числа :) ).
Заявление компилятора о поддержке стандарта языка и являются нужными гарантиями. Если у компилятора заявленно поддержка например с++11 в полном объеме и он так же заявил что может собрать под нужную Вам платформу, то использования внутри кода например std::mutex дают Вам полное основания ожидать, что конкретные барьеры памяти будут реализованы внутри исполняемого кода. Потому как это требование языка и компилятор подписался о их поддержке.
Если компилятор не в полной мере поддерживает стандарт (или вообще не поддерживает необходимый для реализации) - вы вправе использовать сторонние библиотеки. Но тогда гарантии возлагаются на библиотеку, которая будет реализовывать необходимый вам функционал. Потому как правило библиотека выпускается с учетом языка/компиляторов/платформ которые будут ее использовать. Пример POSIX-овские thread-ы.

Ну все это очевидно же). Но дело в том что мы обсуждаем volatile - а поведение этого ключевого слова в стандарте не изменялось ну оооочень давно (с 2003 точно, я бы сказал практически неизменно, учитывая что о многопоточности стандарт вообще до ++11 практически ничего не упоминал/гарантировал)

Поэтому, мое лично мнение - вам повезло с компилятором и Ваши ожидания относительно volatile он оправдывает.
Я это все говорю относительно своего опыта. На текущем проекте, который собирается под MSVS2013 в определенном объеме кода раскидано использование volatile. Ну исторически, кто-то решил так "гарантировать" передачу "флага" между потоками. Разумеется студия собирает по умолчанию все это с вставкой барьеров. А при попытке собрать другим компилятором - получаем забавный неуловимый, никому не нужный геморрой времени исполнения.

Цитировать
ссылки на английский не надо

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

Цитировать
Тем не менее, как минимум барьер компилятора volatile создаёт
Обобщенно - компилятор НЕ ОПТИМИЗИРУЕТ переменную/код с модификатором volatile. Барьер вставлять от компилятора никто не требует. Порядок гарантировать от него опять же, никто не требует. А следовательно компилятор МОЖЕТ НЕ вставлять барьер и МОЖЕТ НАРУШАТЬ порядок с волатильностью.

Почему мой ответ имеет "обобщенный" характер - потому как изначально вы спрашивали общее поведение volatile для пользовательских типов. А это жестко прописано в стандарте (использование через volatile методы/наличие своего оператора присваивания и тд). Стандарт жестко в этом ограничивает компилятор (При этом не ограничивает в том как именно это будет реализовано с барьерами или без).
Если обобщенный ответ Вам не интересен - пользуйтесь конкретным поведением конкретного компилятора. А если вдруг решите сменить компилятор - может быть вспомните про "требования" и "гарантии" )


Цитировать
От того, что не на x86 барьер памяти не нужен...

Ох.. Имелось ввиду из ссылки иммено это:
Цитировать
Блок, относящийся к стандарту ISO
Если вы знакомы с ключевым словом C# volatile или разбираетесь в поведении volatile в более ранних версиях Visual C++, вам необходимо учитывать, что в стандарте ISO для языка C++11 ключевое слово volatile отличается. В Visual Studio такое ключевое слово поддерживается, если установлен параметр компилятора /volatile:iso. (Для архитектуры ARM он установлен по умолчанию). Ключевое слово volatile в коде, создаваемом согласно стандарту ISO для языка C ++11, должно использоваться только для аппаратного доступа; его использование для взаимодействия между потоками не допускается. Для взаимодействия между потоками необходимо использовать такие механизмы, как std::atomic<T> из стандартной библиотеки шаблонов C++.

Если Вас интригует что там указано С++11, я могу напрячься и найти за Вас описание и в ранних версиях в стандарте. Но оно будет в оригинале и на английском.

Цитировать
Всё, что по вашей ссылке написано важного, так это ссылка вот на что
А вот это как раз - попытка компилятора "расширить" поведение volatile, так как стандарт не ограничивает его в этом. Любой компилятор может как дополнить любым "разумным" для себя кодом, так и не делать этого вовсе.

И именно замечание (по ссылке) акцентирует внимание на том, что если разработчик создал решение в платформа-независимом стиле, и независимом от компилятора, которое соответствует стандарту (т.е. он сам позаботился о барьерах), то еще одни барьеры вокруг волатильности (принудительно вставленные компилятором, потому что он вдруг такой заботливый, а его никто и не просил) ему явно не нужны - так как это будет сказывается на производительности.

Цитировать
Какое именно поведение? Отсутствие кэширования переменной? Ну так оно как раз гарантировано.
Гарантии языка относительно "автоматических" барьеров памяти, а следовательно и порядка (а не только кэширование/исключение кода) для volatile. Например для std::mutex - они (гарантии) в стандарте прописаны. Для volatile в языке их нет



Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Марта 21, 2017, 03:44:17 pm
Цитировать
Заявление компилятора о поддержке стандарта языка и являются нужными гарантиями.

На практике мы имеем разные трактовки long double у разных компиляторов (Borland и Microsoft, например), неопределённый char (который то знаковый, то беззнаковый, но чаще всего знаковый), массивы нулевой длины, пустые структуры, которые в Си (их там вообще-то не было, но сделали) имеют sizeof()=0, а в Си++ sizeof()=1 (!), ну и на закуску невозможность строить переносимый код в принципе (даже между Unix-системами - то библиотеки названы иначе, то формат функций другой, то в структурах нет полей, которые есть в исходной системе). Поэтому лучше всё же рассматривать связку компилятор-ОС-процессор и забыть о какой-либо полной переносимости. Всё равно она не достигается.
А уж сколько расширений языка Си в компиляторах (практически в любых), думаю, вы представляете? Интересно, для кого они делаются? Наверное, авторы ждут эти расширения в следующем стандарте? Скажем, макросы с переменным количеством аргументов появились в gcc до принятия C99 и Си++ 11. В общем, если всё это есть, значит, оно кому-то нужно.

Цитировать
Потому как правило библиотека выпускается с учетом языка/компиляторов/платформ которые будут ее использовать. Пример POSIX-овские thread-ы.

Угу. Вот ваш POSIX:
Код:
pthread_attr_t pt_attr;
pthread_attr_init(&pt_attr);
pthread_attr_setdetachstate(&pt_attr,PTHREAD_CREATE_DETACHED);
pthread_attr_setinheritsched(&pt_attr,PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&pt_attr,SCHED_RR);
pt_attr.param.sched_priority=15;  - а вот этого поля в Linux нет.
И на практике мы имеем тот же зоопарк.

Цитировать
Если обобщенный ответ Вам не интересен - пользуйтесь конкретным поведением конкретного компилятора. А если вдруг решите сменить компилятор - может быть вспомните про "требования" и "гарантии" )

Положим, я не использую volatile для задания очерёдности действий. Что касается барьера, ну что ж, раз стандарт его не гарантирует, то подождём, когда начнёт гарантировать. Впрочем, большинство компиляторов на этот случай поддерживают вот что: asm volatile ("" ::: «memory»);

Цитировать
Гарантии языка относительно "автоматических" барьеров памяти, а следовательно и порядка (а не только кэширование/исключение кода) для volatile. Например для std::mutex - они (гарантии) в стандарте прописаны. Для volatile в языке их нет

Ну нет, так нет. 8) Язык вообще несовершенен. Выбирайте тот компилятор, где есть и который точно об этом скажет. :) Как я уже выше написал, если есть расширение языка, значит, оно зачем-то нужно, нет?

Что касается правильного использования volatile, я вам его описал в личке ещё вчера.

Кстати, с atomic тоже можно сделать забавные ошибки: https://habrahabr.ru/post/155507/  :)


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Марта 23, 2017, 06:27:49 pm
На практике мы имеем разные трактовки long double у разных компиляторов (Borland и Microsoft, например), неопределённый char (который то знаковый, то без знаковый, но чаще всего знаковый)
Очень много перечислено, по каждому пункту можно дать ответ. Но боюсь что правильно было бы давать вне текущей Вашей темы (топика).
Например, стандарт (насколько мне известно) гарантировал всегда размер только одного типа - это char (байт). Остальное все отдавал на откуп компиляторам. Поэтому как правило, если разрабатывалась некая платформо-независимая библиотека, то она учитывала эти требования языка. С появлением например С++11 (и старше) для таких вещей, где размер типа и знаковость важна есть такое http://en.cppreference.com/w/cpp/types/integer

Цитировать
ну и на закуску невозможность строить переносимый код в принципе (даже между Unix-системами - то библиотеки названы иначе, то формат функций другой, то в структурах нет полей, которые есть в исходной системе).
Задачи бывают разные, и реализация переносимости тоже. С вами не согласится boost (http://"http://www.boost.org/"), QT (http://"https://www.qt.io/ru/"), wxWidgets (http://"https://www.wxwidgets.org/") и еще много чего, openSSL, openSSH, libCurl и тд.
Так что вы не правы - переносимый код уже существует много лет

Цитировать
В общем, если всё это есть, значит, оно кому-то нужно.
Разумеется, оно кому-то нужно, вводят это не просто так - а потому что это потребовалось (для своих целей/библиотек или по требованию пользователей). Но пользуясь этими средствами всегда нужно помнить, что они скорее всего окажутся не переносимы. И хорошо если при переносе другой компилятор Вам скажет - "эй, я не понимаю что это и как мне с этим работать. Для меня это ошибка!". А ведь может например и ничего не сказать (как c volatile) - молча проглотить и не проставить нужный memory_order (ну потому что - никто от него этого и не требует, а значит вправе и не делать лишних движений). И все, и ждите - "какая такая Америка? Ничего не знаю!"


Цитировать
Угу. Вот ваш POSIX:
....
И на практике мы имеем тот же зоопарк.

Ключевое слово как правило. Откровенно, POSIX - давно пользовался, привел его просто в качестве примера переносимости. Допускаю что может иметь специфику работы от платформы к платформы. Но это - нормально, как ни странно Вам может показаться. Решение должно распространятся от общего к частному, от абстракции к имплементации. И если имплементация оказывает сильное влияние на абстракцию - то скорее всего такое решение нельзя назвать общим/переносимым. Другими словами, добиться переносимости кода не получается не потому, что не возможно это сделать в принципе, а потому что для решения не нашли достаточную абстракцию.
Ну нет например сигналов (Unix) в Windows. Зная что Вам нужно переносить решение на эту платформу, Вы скорее всего найдете подходящую абстракцию, реализация которой будет отличатся в сборке в зависимости от платформы, либо же вообще найдете другие переносимые способы взаимодействия.
Ну это вроде и очевидные вещи - то ли дело использовать WinApi ReadFile и мучатся с переносимостью и реализацией на каждой платформе, а то просто использовать std::fstream или fread/fwrite, а может вообще boost::asio или poco - и вообще покрывать большинство решений с файлами, последовательными портами и сокетами.


Цитировать
Выбирайте тот компилятор, где есть и который точно об этом скажет.
Я исхожу сначала из языка (его 100% гарантий) и библиотек, и в последнюю очередь полагаюсь на расширение предлагаемые компилятором.



Цитировать
Кстати, с atomic тоже можно сделать забавные ошибки: https://habrahabr.ru/post/155507/
Забавные ошибки можно сделать в любом месте с любым инструментом. Если человек, например не знает, что любые операции с переалацированием вектора (а это вставка, удаление элементов, ресайз и фактическое копирование) приводят к инвалидации  итераторов этого вектора - то такой человек сам себе злобный доктор. Это не проблемы языка. А в статье - человек показал нарочный пример при не верном использовании memory_order.

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


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Марта 23, 2017, 07:20:32 pm
Цитировать
Очень много перечислено, по каждому пункту можно дать ответ.

Ответ тут очень простой: Си - помойка, которую надстраивают из года в год. И все эти "недосказанности" в стандарте это только подтверждают. "Ой, типы старые плохие, там int разной размерности, давайте срочно исправлять. Придумаем, int32_t! Ой, а библиотечные функции возвращают int - они старые, для них оставим int."  ;D

Цитировать
С вами не согласится boost, QT, wxWidgets и еще много чего, openSSL, openSSH, libCurl и тд.
Так что вы не правы - переносимый код уже существует много лет

О! Qt! В 2005 написал я приложение для Qt. Запускаем в современном Linux -а вот и не работает (нет библиотеки той старой версии в системе). Ладно, какой там Qt у нас? 5? Перекомпилируем. Ой, ошибка на ошибке - нет таких классов, а если есть, то методы названы иначе. Офигенная переносимость.  ;D А чего это для Windows 98, написанное в 2000-м приложение в Windows 10 работает и даже перекомпилируется? Даже не знаю.  ;D

Цитировать
А ведь может например и ничего не сказать (как c volatile) - молча проглотить и не проставить нужный memory_order (ну потому что - никто от него этого и не требует, а значит вправе и не делать лишних движений). И все, и ждите - "какая такая Америка? Ничего не знаю!"

Как я применяю volatile, ошибок не будет нигде и никогда. ;)

Цитировать
Ну нет например сигналов (Unix) в Windows

Возьмём сокеты - они есть и там и там. Возьмём функцию select. Чему там первый-то параметр равен у нас? Максимальный номер сокета+1? Это в Unix. А в Windows он равен 0 и не используется. Так и пишут:
Цитировать
int select(int nfds,                         // Не используется (оставлен для совместимости)
           fd_set FAR *readfds,              // множество сокетов, проверяемых на готовность к чтению
           fd_set FAR *writefds,             // множество сокетов, проверяемых на готовность к отсылке
           fd_set FAR *exceptfds,            // множество сокетов, проверяемых на ошибку/OOB данные
           const struct timeval FAR *timeout // Таймаут проверки
);
Переносим приложение из Windows - и вуаля, не работает в Unix.

Цитировать
Ну это вроде и очевидные вещи - то ли дело использовать WinApi ReadFile и мучиться с переносимостью и реализацией на каждой платформе,

Вот после таких вот "универсальных" библиотек программы весят мегабайты и требуют гигабайты. ;) Если вы пишете под Windows - используйте Win32 API и MFC (тот же ReadFile умеет, например, асинхронное чтение,а CreateFile умеет открывать файл с самыми разными особенностями (что делать, если файл занят другой программой, может ли дескриптор файла наследоваться дочерними процессами и так далее). Это существенно сократит размер программы и увеличит её скорость.

Цитировать
Я исхожу сначала из языка (его 100% гарантий) и библиотек, и в последнюю очередь полагаюсь на расширение предлагаемые компилятором.

Любая платформа имеет особенности, которые раскрываются именно компилятором и его возможностями. Зачем нужны массивы нулевой длины структурах? Очень удобно накладывать на принятые данные переменной длины и с определённым заголовком, особенно, на микроконтроллерах.

Цитировать
А в статье - человек показал нарочный пример при не верном использовании memory_order.

Не вы ли подавали atomic как безопасный к использованию? ;)

Цитировать
Ну и самое главное - я уже в который раз считаю что для подобных "споров" нужно создавать отдельную ветку с дискуссиями. А то Ваше первоначальное сообщение слегка "затерялось".

Ну тогда будем считать, что каждый остался при своём мнении. А холивар мне не нужен.
Что касается исходной темы, вряд ли кто-нибудь найдётся, кто сможет хоть что-то сказать про USBD в QNX. А программу я и так периодически обновляю и выкладывают.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Апреля 05, 2017, 09:20:44 pm
Новая версия! Добавлен расчёт температуры. Так же на некоторых компьютерах не видит устройство - лечится выбором в BIOS USB 1.0 вместо USB 2.0.

Также я написал программу для работы с Flir One Gen 2 из Windows XP (с драйверами - их три для каждого компонента устройства). Вдруг кому пригодится. :)
К сожалению, в других версиях Windows драйвер мне не удалось установить. Поэтому драйвера только для XP. И то, на некоторых компьютерах не на всех USB-портах Windows видит устройство как 3 устройства. С остальными версиями Windows ничего не получается, например, в Windows 7 x86 устройство видится как "Составное USB устройство", и я без понятия, что нужно сделать, чтобы их разделить (XP делит на три устройства с окончаниями на iAP, FileIO, Frame). ::) Поэтому, хотя драйвера я и компилировал и для Windows 7, но как их установить без понятия. В x64 драйвер, как я понял, вообще нельзя установить не подписав его (пишут, что есть способ смухлевать, но я не пробовал).


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Апреля 10, 2017, 08:04:48 pm
Обновлена версия для Windows XP. :) Как ни странно, под Windows получилась самая стабильная работа программы. Если Windows смогла увидеть три устройства и драйвера для них установились, то дружба Windows с тепловизором отныне навсегда. :)
В комплекте нет файла WdfCoInstaller01009.dll ввиду его большого размера. Но его можно найти, например, в WinDDK или в драйвере из предыдущего сообщения.




Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Мая 01, 2017, 02:00:31 pm
Программа для обработки полученных RAW-файлов изображений.



Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Мая 01, 2017, 09:04:49 pm
Откуда столько спама стало валить?  :-[


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Мая 29, 2017, 03:26:40 pm
Хоть и не тепловизор, но тоже связанное с измерением температуры USB-устройство. :)

Валялся на работе китайский термодатчик для USB модели TEMPer V1.4. Почему бы и его не подключить по USB в QNX и заодно писать и температуру вокруг стенда? Подключил. Но датчик тоже с норовом - может с первого раза не запуститься. А на одном компьютере ни в какую не пожелал работать - один интерфейс появляется при подключении, а вот на втором интерфейсе у QNX ошибка usb_attach().
Вот что получилось:


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Июля 07, 2017, 10:51:46 am
Меня на том же хабре часто ругали за неиспользование const вместо #define. Мол, #define вне модуля живёт (почти всегда это, правда, хорошо) и может быть переопределена незаметно в другом модуле. Всё это так, но вот смотрите какую фишку я сегодня словил, использовав именно глобальную переменную модуля (с const) для того, чтобы запереть объявления команд внутри модуля. Есть класс, он существует глобально. В его конструкторе инициализируется вектор списка команд. Вот так:

Код:
#include "cthreadserver.h"

CThreadServer cThreadServer;//серверный поток

//команды серверу
const string Command_Login="LOGIN=";
const string Command_Password="PASSWORD=";
const string Command_Connect="CONNECT";

CThreadServer::CThreadServer(void)
{
//инициализируем список команд
 vector_string_command.push_back(Command_Login);
 vector_string_command.push_back(Command_Password);
 vector_string_command.push_back(Command_Connect);
}

В конструкторе CThreadServer все Command равны пустой строке. :) Если же обменять местами объявления CThreadServer cThreadServer и команд, то инициализация проходит на ура. То есть, компилятор в своём порядке вызывает создание объектов (что логично) и к моменту вызова конструктора CThreadServer остальные объекты нифига не инициализированы. Просто отличный способ прострелить ногу! :)


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Июля 07, 2017, 01:19:24 pm
Просто отличный способ прострелить ногу! :)
Порядок инициализации  - это базовая вещь (которую необходимо знать/помнить) языка.
Хотите уменьшить риски от "прострелов ног" в этой области - перестаньте создавать глобальные переменные!!!
Нужны константные объекты инициализации - переместите их внутрь класса который их будет использовать (при этом их инициализацию можно начиная с С++11 переместить внутрь хедера класса). Не хотите получить оверхед за счет множества таких констант в классе - делайте статические константные объекты класса (а лучше всего статические методы возвращающие статические константные объекты метода, или constexpr методы/значения). Но не глобальные переменные!!! В 2017 году-то ..
Как вариант, так:

Код:
// хедер
class CThreadServer
{
public: // typedefs

  using Command = std::string;
  using CommandVector = std::vector<Command>;

public: // static

  static const CommandVector& getDefaultCommands();
  static const Command & getCommand_Login();
  static const Command & getCommand_Password();
  static const Command & getCommand_Connect();
};

// cpp -модуль
namespace
{
  CThreadServer::CommandVector generateDefaultCommands()
  {
    return CThreadServer::CommandVector {
                 CThreadServer::CommandVector::getCommand_Login(),
                 CThreadServer::CommandVector::getCommand_Password(),
                 CThreadServer::CommandVector::getCommand_Connect(),
             };
  }
};

const CThreadServer::CommandVector& CThreadServer::getDefaultCommands()
{
  static const CommandVector retCommands = generateDefaultCommands();
  return retCommand;
}

const CThreadServer::Command & CThreadServer::getCommand_Login()
{
    static const std::string retCommand("LOGIN=");
    return retCommand;
}

const CThreadServer::Command & CThreadServer::getCommand_Password()
{
    static const std::string retCommand("PASSWORD=");
    return retCommand;
}

const CThreadServer::Command & CThreadServer::getCommand_Connect()
{
    static const std::string retCommand("CONNECT=");
    return retCommand;
}

// конструктор
CThreadServer::CThreadServer()
 : vector_string_command(getDefaultCommands())
{
}

Конечно, нужно учитывать, что статические переменные в  многопоточных приложениях имеют свои проблемы инициализации. Но они тоже решаются - в зависимости от того, какими средства языка вам доступны (и какой стандарт). Но для описанной Вами проблемы (где инициализация объекта в одном потоке происходит раньше чем инициализация используемых глобальных переменных) - вполне себе решением будет.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Июля 07, 2017, 01:43:57 pm
Цитировать
Порядок инициализации  - это базовая вещь (которую необходимо знать/помнить) языка.
Хотите уменьшить риски от "прострелов ног" в этой области - перестаньте создавать глобальные переменные!!!

Порядок инициализации, это конечно, здорово, но только можно просто не заметить, кто должен быть создан первым, а кто вторым и просто внести в программу в произвольном месте.
Откуда глобально созданный сервер? Мне вот надо чтобы класс документа знал о классе сервера, а сервер знал о документе и получал при инициализации   указатель на него. Иными словами, перекрёстные ссылки с взаимным включением в h-файлы h-файлов друг-друга (в объявлении классов каждого объекта требуется знание о другом объекте). Зачем мне так? А потому что запускать сервер должен документ. При этом сервер должен через этот документ и общаться. И всё, что я придумал, так это серверу документ в h-файл подключить, а документу (который динамически создаётся MFC) подключить в его cpp-файле сервер, созданный глобально. В этом случае проблема исчезает, но появляется глобально созданный сервер.

Цитировать
Нужны константные объекты инициализации - переместите их внутрь класса который их будет использовать (при этом их инициализацию можно начиная с С++11 переместить внутрь хедера класса). Не хотите получить оверхед за счет множества таких констант в классе - делайте статические константные объекты класса (а лучше всего статические методы возвращающие статические константные объекты метода, или constexpr методы/значения).

Это очень захламляет класс.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Июля 07, 2017, 02:26:28 pm
Порядок инициализации, это конечно, здорово, но только можно просто не заметить, кто должен быть создан первым, а кто вторым и просто внести в программу в произвольном месте.
Пишите на другом языке (кстати на пример каком, где порядок инициализации может быть "произвольным" ?)

Цитировать
Откуда глобально созданный сервер? Мне вот надо чтобы класс документа знал о классе сервера, а сервер знал о документе и получал при инициализации   указатель на него. Иными словами, перекрёстные ссылки с взаимным включением в h-файлы h-файлов друг-друга (в объявлении классов каждого объекта требуется знание о другом объекте). Зачем мне так? А потому что запускать сервер должен документ. При этом сервер должен через этот документ и общаться. И всё, что я придумал, так это серверу документ в h-файл подключить, а документу (который динамически создаётся MFC) подключить в его cpp-файле сервер, созданный глобально. В этом случае проблема исчезает, но появляется глобально созданный сервер.
Все что гарантируется при инициализации глобальных переменных - это лишь порядок инициализации внутри модлуля который их использует. Остальное - как повезет.
Поэтому - не используйте глобальные переменные, если вы планируете их использовать в разных модулях или собираетесь игнорировать порядок использования от порядка инициализации.
Иначе, давайте еще вспомним кучу возможностей - например не виртуальный деструктор в полиморфном классе. Дефолтный конструктор копирования для объектов которые копировать нельзя вообще (мютексы, потоки и другие содержащие референсы на системыне ресурсы классы) или которые копировать можно, но не поправилам дефолтного.
Так что Ваш аргумент - "константные переменные плохо, потому что если они глобальные то можно написать такоееее" - некоректен. Просто не пишите специально вот "такоеее".

Цитировать
Это очень захламляет класс.

Во первых, это был пример использования (один из вариантов).
Во вторых, никто не обязывает хранить это все внутри одного класса. Создайте отдельный класс комманд и не храните их внутри класса-потока
В третьих, я честное слово - не телепат. Для меня не известно, насколько Вы хотите чтобы команды были доступны для других модулей (являются они видимы за предлом класса или они только для внутренного использования). С моей колокольни - они должны быть видимы и доступны. Но если нет желания захломлять интерфейс класса вообще, команды только для внутреннего использования - то они да, выносятся из класса и  помещаются только в cpp-unit. Вэтом юните, в анонимном неймспейсе, делается один констэкспр метод или метод возвращающий константную ссылку на статический "локальный" объект.
Код:

// cpp-unit
namespace
{
  const Command& getCommand_Login()
  {
      static const std::string retCommand("LOGIN=");
      return retCommand;
  }

// ......

  const CommandVector& getDefaultCommands()
  {
    static const CommandVector retCommands{
                 getCommand_Login(),
                 getCommand_Password(),
                 getCommand_Connect(),
             };
     return retCommands;
  }
};

// конструктор
CThreadServer::CThreadServer()
 : vector_string_command(getDefaultCommands())
{
}
Правда же, и Ваш класс не пострадал )

Для меня захламление вот это вот
Цитировать
ThreadServer::CThreadServer(void)
{
//инициализируем список команд
 vector_string_command.push_back(Command_Login);
 vector_string_command.push_back(Command_Password);
 vector_string_command.push_back(Command_Connect);
}


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Июля 07, 2017, 05:11:05 pm
Цитировать
Пишите на другом языке (кстати на пример каком, где порядок инициализации может быть "произвольным" ?)

Да при чём же тут это? Вся штука в том, что использование const-переменных вместо макросов порождает свои собственные проблемы.

Цитировать
Поэтому - не используйте глобальные переменные, если вы планируете их использовать в разных модулях или собираетесь игнорировать порядок использования от порядка инициализации.

lastcross, я всё это знаю. :) Дело тут в другом. Я-таки решил заменить макросы на константы в своей новой программе (почему бы и не попробовать сделать, как советуют многочисленные апологеты книжек Александреску и ему подобных? Вдруг код и вправду станет гораздо читабельнее и удобнее для работы с ним?) и совершенно вылетело из головы, что они тоже будут инициализироваться в своём порядке. Запустил программу - не проходит авторизация. Опа, а ведь константы же тоже инициализируются не сами по себе. Надо будет это не упустить из виду в будущем.

Цитировать
Так что Ваш аргумент - "константные переменные плохо, потому что если они глобальные то можно написать такоееее" - некоректен. Просто не пишите специально вот "такоеее".

Вся штука в том, что вот именно как глобальные переменные самое очевидное как использовать константы вместо макросов. В модуле может вообще кроме класса ещё какая функция потока быть и ей эта константа очень нужна. Ну спрячете в константу в класс, а поток её как забирать должен тогда? Как дружественная функция что ли? :)

Цитировать
Во вторых, никто не обязывает хранить это все внутри одного класса. Создайте отдельный класс команд и не храните их внутри класса-потока

Так не хочется на каждый чих по классу-то создавать. Оно того не стоит. Используется ровно один раз в функции поиска команды и на этом всё.  :-\

Цитировать
Для меня не известно, насколько Вы хотите чтобы команды были доступны для других модулей

Нет, они только для внутреннего использования классом сервера. С тем, что вы предложили вот в чём проблема - удобно когда список таких настроек сгруппирован и идёт один к одному, например, в начале модуля. Одна строчка - один параметр. В случае отдельных классов так не получается.

Цитировать
Для меня захламление вот это вот

Я так понимаю, вы любите функциональный подход (это я вспомнил, как вы время с помощью лямбда-функции измеряли)? :)
У "этого вот" есть наглядность, тогда как у vector_string_command(getDefaultCommands()) с наглядностью немного сложнее - нужно раскручивать функции одну за другой, чтобы наконец, придти к списку инициализации. И всё это ради одного-единственного места, где всё это инициализируется.

А так - спасибо за идеи; они местами интересны. К сожалению, мой VC6 (да, я всё ещё с него не ушёл) не позволяет инициализировать константы в классе.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Июля 07, 2017, 05:57:51 pm
Да при чём же тут это? Вся штука в том, что использование const-переменных вместо макросов порождает свои собственные проблемы.

Неправда же! Уберите у себя const и получите ровно такой же "выстрел"! Речь не о конст, а порядке инициализации вообще и о правилах инициализации именно глобальных переменных!

Цитировать
Вдруг код и вправду станет гораздо читабельнее и удобнее для работы с ним?) и совершенно вылетело из головы, что они тоже будут инициализироваться в своём порядке. Запустил программу - не проходит авторизация. Опа, а ведь константы же тоже инициализируются не сами по себе. Надо будет это не упустить из виду в будущем.

Еще раз перечитайте то что я написал выше. Да и переход от одного решения к другому, не всегда делается влоб (очевидно).

Цитировать
Вся штука в том, что вот именно как глобальные переменные самое очевидное как использовать константы вместо макросов. В модуле может вообще кроме класса ещё какая функция потока быть и ей эта константа очень нужна. Ну спрячете в константу в класс, а поток её как забирать должен тогда? Как дружественная функция что ли? :)
Реально удививли. Может быть так?
Код:
// хедер команд
namespace command
{
    //
    using CommandName = std::string;
    using CommandVector = std::vector<CommandName>;
    //....
   
    class Server
    {
    public:
        static const CommandVector& getDefaultCommands();
        static const Command & login();
        static const Command & password();
        static const Command & connect();
    }
};

// cpp-unit команд
using namespace command;
#include "command/server.h"

const CommandVector& Server::getDefaultCommands()
{
    static const CommandVector retCommands{
                     login(),
                     password(),
                     connect(),
                };
    return retCommands;   
}
// и дальше по анологии


// хедер класса для потока серверного (девственно чист и не захламлен)
class CThreadServer
{
    //...
};

// cpp -модуль класса для потока серверного
#include "ServerThread.h"
#include "command/server.h"

// конструктор
CThreadServer::CThreadServer()
 : vector_string_command(command::Server::getDefaultCommands())
{
}
Как видите - обошлись без друзей, функции возвращают коснтантные ссылки (то есть объекты не изменяемые), целевой класс не захламлен, а пользователю ненужно думать - глобальная это переменная или нет (И - инкапсуляция)

Цитировать
Так не хочется на каждый чих по классу-то создавать. Оно того не стоит. Используется ровно один раз в функции поиска команды и на этом всё.

Смотрите решение - где команды в cpp-unit-е в отдельном анонимном неймспейсе (его я приводил ранее). Другое дело когда, пользователь класса захочет через интерфейс выполнить следующее (совершенно в третьем cpp-юните)
Код:
#include "ServerThread.h"
#include "command/server.h"

//...
void onConnect()
{
//....
  mServerThread->execute(command::server::connect());
//...
}

Как это будет выглядеть в Вашем случае с глобальной переменной (которая сокрыта и не захломляет ничего) ?) Вы же не планируете прям вбивать строку в вызов execute? )) Например так?
Код:
  mServerThread->execute("CoNect");

Цитировать
С тем, что вы предложили вот в чём проблема - удобно когда список таких настроек сгруппирован и идёт один к одному, например, в начале модуля. Одна строчка - один параметр. В случае отдельных классов так не получается.
Не понял о чем Вы, но уверен что проблемы с этим в моем решении не будет.

Цитировать
Я так понимаю, вы любите функциональный подход (это я вспомнил, как вы время с помощью лямбда-функции измеряли)? :)
У "этого вот" есть наглядность, тогда как у vector_string_command(getDefaultCommands()) с наглядностью немного сложнее - нужно раскручивать функции одну за другой, чтобы наконец, придти к списку инициализации. И всё это ради одного-единственного места, где всё это инициализируется.

Нет, ваше решение заставляет инициализировать вектор дважды: конструирует пустой вектор, а после внутри конструктора добавляет какждый элемент вконец (заставляя вектор переалоцироватся). Мое решение - позволяет инициализировать вектор один раз и алоцировать его всего единожды (так как идет каопирование вставкой, а возможно даже и без копирования если произойдет мовмент). Это так, если касаться производительности.
Кроме того, В случае рефакторинга, когда потребуется добавить новый конструктор с каким-либо параметром, вам придеться и там "заполнять вектор" таким же способом (или каким-либо другим). Это скорее всего приведт к тому - что у вас будут два разных конктруктора с похожим телом, сопровождение которых может привести в дальнейшем к ошибке (набор команд инициализации нужно изменить - этот набор придеться проводить во всех конструкторах класса, у меня же - в одном методе).
Предположу и третий вариант, когда по каким-то причинам вы откажетесь от вектора команд в пользу хеш-контейнера (понадобится быстро находить команду из списка). В таком случае, количество необходимых изменений в коде будет значительным, по сравнению когда инициализация происходит в инициализационном списке конструктора.

А насчет наглядности - это дело зависит от хорошо прдуманого названия метода. Разумеется я не стал вникать какой смысл имеет именно этот список команд. Но его можно назвать как authorizeCommand() вместо getDefaultCommands. Имхо, мне проще понимать что этот метод всегда генерирует набор команд для авторизации, чем вникать почему в конструкторе именно  такой набор команд. А вот когда мне потребуется вникуть какие команды идут на авторизацию - я раскрою метод и увижу. Сегодня это три команды - а завтра 40 (и совсем другие). Зачем мне это видеть в конструкторе?. А если кроме конструктора нужно будет повторить еще раз этот набор?
Цитировать
А так - спасибо за идеи; они местами интересны.
Всегда пожалуйста =)

з.ы.: немного внес исправления в пример кода в именовании методов


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Июля 07, 2017, 06:27:32 pm
Цитировать
Неправда же! Уберите у себя const и получите ровно такой же "выстрел"! Речь не о конст, а порядке инициализации вообще и о правилах инициализации именно глобальных переменных!

Эх, похоже, вам нужно формулировать максимально точно о чём идёт речь, иначе вы обращаете внимание совсем не на то, о чём сообщение, а на какие-либо незначительные детали, относящиеся к частному случаю. :-[ Хорошо. Тогда скажу так: использование любых глобальных переменных вместо макроопределений порождает вышеуказанную проблему последовательности инициализации. Макроопределения такой проблемы не порождают.

Цитировать
Реально удививли. Может быть так?

Так нет же функции потока. :-\
Я вот о чём:
Код:
//функция потока
UINT ThreadServer(LPVOID pParam)
{
 //что-то делаем, и тут нужна константа, спрятанная внутри класса
 return(0);
}

...
//где-то там, внутри класса серверного потока мы запустили поток и передали ему указатель на себя:
void CThreadServer::Start(void)
{
 cWinThread_Thread=AfxBeginThread((AFX_THREADPROC)ThreadServer,this);
 cWinThread_Thread->m_bAutoDelete=FALSE;
}

Цитировать
Как это будет выглядеть в Вашем случае с глобальной переменной (которая сокрыта и не захламляет ничего) ?) Вы же не планируете прям вбивать строку в вызов execute? )) Например так?

Пока - никак. :) В настоящий момент названия команд принимаются по сети и не требуют использования где-то ещё.

Цитировать
Не понял о чем Вы, но уверен что проблемы с этим в моем решении не будет.

Я имею в виду вот что:
Код:
//код устройства
#define DEVICE_ID_BWFKP   7

//количество устройств
#define BWFKP_AMOUNT 1

//коды подтверждения
#define BWFKP_CONFIRM_CODE_OK    1
#define BWFKP_CONFIRM_CODE_ERROR 0

//коды режимов работы БВФКП

//запрос состояния
#define BWFKP_MODE_ID_WORKING 0x01
//включить АКУ
#define BWFKP_MODE_ID_ACU_ON 0x02
//включить ГБ №1
#define BWFKP_MODE_ID_UNIT_1_ON 0x03
//включить ГБ №2
#define BWFKP_MODE_ID_UNIT_2_ON 0x04
//включить ГБ №3
#define BWFKP_MODE_ID_UNIT_3_ON 0x05
//включить акселерометр №1
#define BWFKP_MODE_ID_ACS_1_ON 0x06
//включить акселерометр №2
#define BWFKP_MODE_ID_ACS_2_ON 0x07
//включить акселерометр №3
#define BWFKP_MODE_ID_ACS_3_ON 0x08
//включить акселерометр №4
#define BWFKP_MODE_ID_ACS_4_ON 0x09
//включить акселерометр №5
#define BWFKP_MODE_ID_ACS_5_ON 0x0A
//включить акселерометр №6
#define BWFKP_MODE_ID_ACS_6_ON 0x0B
//включить аккумуляторную батарею
#define BWFKP_MODE_ID_BAT_ON 0x0C
//включить контроль аккумуляторной батареи
#define BWFKP_MODE_ID_BAT_CONTROL_ON 0x0D
//включить ЦВ №1
#define BWFKP_MODE_ID_CW_1_ON 0x0E
//включить ЦВ №2
#define BWFKP_MODE_ID_CW_2_ON 0x0F
//включить ЦВ №3
#define BWFKP_MODE_ID_CW_3_ON 0x10
//включить ЭНЗУ
#define BWFKP_MODE_ID_ENZU_ON 0x11


//отключить АКУ
#define BWFKP_MODE_ID_ACU_OFF 0x12
//отключить ГБ №1
#define BWFKP_MODE_ID_UNIT_1_OFF 0x13
//отключить ГБ №2
#define BWFKP_MODE_ID_UNIT_2_OFF 0x14
//отключить ГБ №3
#define BWFKP_MODE_ID_UNIT_3_OFF 0x15
//отключить акселерометр №1
#define BWFKP_MODE_ID_ACS_1_OFF 0x16
//отключить акселерометр №2
#define BWFKP_MODE_ID_ACS_2_OFF 0x17
//отключить акселерометр №3
#define BWFKP_MODE_ID_ACS_3_OFF 0x18
//отключить акселерометр №4
#define BWFKP_MODE_ID_ACS_4_OFF 0x19
//отключить акселерометр №5
#define BWFKP_MODE_ID_ACS_5_OFF 0x1A
//отключить акселерометр №6
#define BWFKP_MODE_ID_ACS_6_OFF 0x1B
//отключить аккумуляторную батарею
#define BWFKP_MODE_ID_BAT_OFF 0x1C
//отключить контроль аккумуляторной батареи
#define BWFKP_MODE_ID_BAT_CONTROL_OFF 0x1D
//отключить ЦВ №1
#define BWFKP_MODE_ID_CW_1_OFF 0x1E
//отключить ЦВ №2
#define BWFKP_MODE_ID_CW_2_OFF 0x1F
//отключить ЦВ №3
#define BWFKP_MODE_ID_CW_3_OFF 0x20
//отключить ЭНЗУ
#define BWFKP_MODE_ID_ENZU_OFF 0x21
//отключить блокировку отключения ГБ №1
#define BWFKP_MODE_ID_UNIT_1_BLOCK_OFF 0x22
//отключить блокировку отключения ГБ №2
#define BWFKP_MODE_ID_UNIT_2_BLOCK_OFF 0x23
//отключить блокировку отключения ГБ №3
#define BWFKP_MODE_ID_UNIT_3_BLOCK_OFF 0x24

//задать значение константы (без записи во FLASH)
#define BWFKP_MODE_ID_SET_CONST 0x25
//считать текущее значение константы (текущее значение из ОЗУ)
#define BWFKP_MODE_ID_GET_CONST 0x26
//записать константы во FLASH
#define BWFKP_MODE_ID_FLASH_WRITE 0x27

//отключить систему
#define BWFKP_MODE_ID_SHUTDOWN 0x28
//разрешить отключение системы
#define BWFKP_MODE_ID_SHUTDOWN_BLOCK_OFF 0x29
//запретить отключение системы
#define BWFKP_MODE_ID_SHUTDOWN_BLOCK_ON 0x2A


//маски состояние реле
//состояние АКУ
#define BWFKP_RELAY_MASK_ACU_ON (1<<0)
//состояние ГБ №1
#define BWFKP_RELAY_MASK_UNIT_1_ON (1<<1)
//состояние ГБ №2
#define BWFKP_RELAY_MASK_UNIT_2_ON (1<<2)
//состояние ГБ №3
#define BWFKP_RELAY_MASK_UNIT_3_ON (1<<3)
//состояние акселерометра №1
#define BWFKP_RELAY_MASK_ACS_1_ON (1<<4)
//состояние акселерометра №2
#define BWFKP_RELAY_MASK_ACS_2_ON (1<<5)
//состояние акселерометра №3
#define BWFKP_RELAY_MASK_ACS_3_ON (1<<6)
//состояние акселерометра №4
#define BWFKP_RELAY_MASK_ACS_4_ON (1<<7)
//состояние акселерометра №58
#define BWFKP_RELAY_MASK_ACS_5_ON (1<<8)
//состояние акселерометра №6
#define BWFKP_RELAY_MASK_ACS_6_ON (1<<9)
//состояние батареи
#define BWFKP_RELAY_MASK_BAT_ON (1<<10)
//состояние контроля батареи
#define BWFKP_RELAY_MASK_BAT_CONTROL_ON (1<<11)
//состояние ЦВ №1
#define BWFKP_RELAY_MASK_CW_1_ON (1<<12)
//состояние ЦВ №2
#define BWFKP_RELAY_MASK_CW_2_ON (1<<13)
//состояние ЦВ №3
#define BWFKP_RELAY_MASK_CW_3_ON (1<<14)
//состояние ЭНЗУ
#define BWFKP_RELAY_MASK_ENZU_ON (1<<15)
//состояние блокировки отключения гироблока №1
#define BWFKP_RELAY_MASK_UNIT_1_BLOCK_ON (1<<16)
//состояние блокировки отключения гироблока №2
#define BWFKP_RELAY_MASK_UNIT_2_BLOCK_ON (1<<17)
//состояние блокировки отключения гироблока №3
#define BWFKP_RELAY_MASK_UNIT_3_BLOCK_ON (1<<18)
//состояние блокировки отключения системы
#define BWFKP_RELAY_MASK_SHUTDOWN_BLOCK_ON (1<<19)

//маски состояния и допуска
//состояние АКУ
#define BWFKP_STATE_CONTROL_MASK_ACU_I (1<<0)
//состояние ГБ №1
#define BWFKP_STATE_CONTROL_MASK_UNIT_1_I (1<<1)
//состояние ГБ №2
#define BWFKP_STATE_CONTROL_MASK_UNIT_2_I (1<<2)
//состояние ГБ №3
#define BWFKP_STATE_CONTROL_MASK_UNIT_3_I (1<<3)
//состояние акселерометра №1
#define BWFKP_STATE_CONTROL_MASK_ACS_1_I (1<<4)
//состояние акселерометра №2
#define BWFKP_STATE_CONTROL_MASK_ACS_2_I (1<<5)
//состояние акселерометра №3
#define BWFKP_STATE_CONTROL_MASK_ACS_3_I (1<<6)
//состояние акселерометра №4
#define BWFKP_STATE_CONTROL_MASK_ACS_4_I (1<<7)
//состояние акселерометра №58
#define BWFKP_STATE_CONTROL_MASK_ACS_5_I (1<<8)
//состояние акселерометра №6
#define BWFKP_STATE_CONTROL_MASK_ACS_6_I (1<<9)
//состояние батареи
#define BWFKP_STATE_CONTROL_MASK_BAT_U (1<<10)
//состояние ЦВ №1
#define BWFKP_STATE_CONTROL_MASK_CW_1_I (1<<11)
//состояние ЦВ №2
#define BWFKP_STATE_CONTROL_MASK_CW_2_I (1<<12)
//состояние ЦВ №3
#define BWFKP_STATE_CONTROL_MASK_CW_3_I (1<<13)
//состояние ЭНЗУ
#define BWFKP_STATE_CONTROL_MASK_ENZU_I (1<<14)
//состояние ИПК
#define BWFKP_CONTROL_MASK_IPK_U (1<<15)

//множитель для ресурса батареи в БВФКП (перевод в проценты)
#define BWFKP_MULTIPLICATOR_RESOURCE_PROCENTS 1
//множитель для напряжения батареи в БВФКП (перевод в вольты)
#define BWFKP_MULTIPLICATOR_BATTERY_U 0.1
 
//номера устройств для коэффициентов коррекции тока и допусков во FLASH
#define BWFKP_FLASH_ACU 0
#define BWFKP_FLASH_UNIT_1 1
#define BWFKP_FLASH_UNIT_2 2
#define BWFKP_FLASH_UNIT_3 3
#define BWFKP_FLASH_ACS_1 4
#define BWFKP_FLASH_ACS_2 5
#define BWFKP_FLASH_ACS_3 6
#define BWFKP_FLASH_ACS_4 7
#define BWFKP_FLASH_ACS_5 8
#define BWFKP_FLASH_ACS_6 9
#define BWFKP_FLASH_CW_1 10
#define BWFKP_FLASH_CW_2 11
#define BWFKP_FLASH_CW_3 12
#define BWFKP_FLASH_ENZU 13
#define BWFKP_FLASH_BAT_U 14
#define BWFKP_FLASH_BAT_I 15
//#define BWFKP_FLASH_BAT 16

//номера коэффициентов полинома (i_out=a*i^3+b*i^2+c*i+d)
#define BWFKP_FLASH_A 0
#define BWFKP_FLASH_B 1
#define BWFKP_FLASH_C 2
#define BWFKP_FLASH_D 3
//номер допуска на максимальный потребляемый ток
#define BWFKP_FLASH_MAX_VALUE 4
//номер допуска на ток, достаточный чтобы устройство считалось включенным
#define BWFKP_FLASH_ON_VALUE 5
При таком оформлении просто приятно глазом просматривать настройки. А в случае с отдельным классом с методами Get что-то там это выглядит в данном случае не очень.  8)

Цитировать
Нет, ваше решение заставляет инициализировать вектор дважды: конструирует пустой вектор, а после внутри конструктора добавляет какждый элемент вконец (заставляя вектор переалоцироватся).

Но это делается один раз при запуске, поэтому ровно ничего не стоит компьютеру. Вот ползанье по базе данных через ODBC без SQL (я его не знаю, но, возможно, придётся почитать про него и задействовать) - вот это точно будет влиять на скорость. Но тут я посмотрю по фактической работе программы и оценю размер базы. :)

Цитировать
Кроме того, В случае рефакторинга,

Он крайне маловероятен. :) Я бы сказал, он вероятен с вероятностью повторного значения GUID. :)


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Июля 07, 2017, 07:17:56 pm
Цитировать
Эх, похоже, вам нужно формулировать максимально точно о чём идёт речь, иначе вы обращаете внимание совсем не на то, о чём сообщение
Ну этому была причина. Вы акцентировали неоднакратно внимание на инициализации именно const.

Цитировать
Макроопределения такой проблемы не порождают.
Зато легко можно получить невалидную иницализацию при переопределении макроса (без ошибок компилятора), например. И кроме того - я акцентирую внимание на том, что это известное поведение. И если человек решил уйти от "макросов-констант" в сторону константных переменных - то он не будет делать это влоб (через глобальные переменные)

Цитировать
Так нет же функции потока. :-\
Код:
#include "command/server.h"
//функция потока
UINT ThreadServer(LPVOID pParam)
{
 using namespace command;
 // пользуйтесь наздоровье, без френдов и тому подобного
 const CommandName& connect = Server::connect();
 // или так, для с++11
 const auto& connect = Server::connect();
 //...
 return(0);
}

...
Единственный подводный камень для всего этого (действительно явное отличие от макросов в пользу их использования) - это только тот факт, что инициализация объектов которые будут разделяемы между потоками - не совсем потока безопасна. Для этого в С++11 добавлен thread_local. Для ранних версий - прийдеться делать какие либо дополнительные манупуляции (гарантирующие инициализацию до исполнения потока, или же посредством атомарных объектов обеспечить инициализацию). Но опять же все это решается гораздо проще когда у вас есть методы возвращающие констаты и инкапсулирующие инициализацию.

Цитировать
Как это будет выглядеть в Вашем случае с глобальной переменной (которая сокрыта и не захламляет ничего) ?) Вы же не планируете прям вбивать строку в вызов execute? )) Например так?

Здорово! Но не желаю вам поддерживать такой список команд, размазанный по всему коду в виде
Код:
auto currentCommand = extractCommandFromNetwork();
 if (currentCommand == "CONNECT=")
 {
 }


Цитировать
Я имею в виду вот что:
Код:
//код устройства
#define DEVICE_ID_BWFKP   7

//количество устройств
#define BWFKP_AMOUNT 1

//коды подтверждения
#define BWFKP_CONFIRM_CODE_OK    1
#define BWFKP_CONFIRM_CODE_ERROR 0
//....
При таком оформлении просто приятно глазом просматривать настройки. А в случае с отдельным классом с методами Get что-то там это выглядит в данном случае не очень.  8)
Это фломастеры - а они разные на вкус и цвет. Если Вам так удобнее - пользуйтесь макросами. Лично для меня этот список - ужасный набор несгрупированых команд (с точки зрения возможностей С++).

Цитировать
Он крайне маловероятен. :) Я бы сказал, он вероятен с вероятностью повторного значения GUID. :)
Почему Вы вообще паритесь по поводу констант и макросов - если программу всеравно после выбрасывать =) ? Если человек задумывается что лучше использовать - макросы или константы, значит одна из причин - это вопросы возможности сопровождения и модификации программы в дальнейшем.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Июля 07, 2017, 08:18:24 pm
Ну,про недостатки макроопределений это и так понятно. :)

Цитировать
И если человек решил уйти от "макросов-констант"

Я просто решил попробовать под впечатлением от:
Цитировать
И еще. Я ваши статьи узнаю по внешнему виду исходников. Да. По этим ужасным простыням кода низкого качества, в котором отсутствует функциональное деление, использование констант и логичное именование переменных. Ваш код (если на него смотреть как на самостоятельную единицу ценности) годится только на выброс. Ну или в качестве наказания для других разработчиков.

1) у вас имеются длииииннные методы (эмпирическое правило — размер метода должен быть не более одного «экрана» текста — той области, которую можно охватить зрением, не переводя взгляд). Разбивка простыней на части помогает их структурировать.

2) многие данные по смыслу являются векторами. Вот и работайте с ними, как с векторами. Этим вы исключите копипаст и эффект последней строки при копипасте. Не волнуйтесь, компиляторы уже умеют раскручивать циклы. Посмотрите хотя бы на нашу с haqreu библиотеку для векторов и матриц.

3) если вместе с выполнением (2) начать именовать переменные содержательно, программу можно будет вообще читать как сказку, а не продираться через x1; x2; dx; и прочее.

4)константные данные следует помещать в const, а не толкать в дефайны.
Вот я и решил ну сделать просто как идеал в моём понимании. :) Правда, идеал, как обычно, никто никогда не показывает. :) А по поводу разбиения функции на короткие кусочки, я (и не только я) полагаю, что линейная последовательность не нуждается в разбиении. :) По переменным и структурированному коду он (и те, кто в личке написали с советами всё сделать классами (хотя я предупредил, что программа была изначально на чистом С и просто обёрнута классом при портировании)), судя по всему, не понял системы обозначений и разбивки на элементы. И опять-таки, даже самые простые данные почему-то все написавшие хотят видеть как сложные объекты (вектор тут математическое понятие - (x,y)). Ну и как обычно принято на хабре, на мой ответ комментариев, собственно по ответу, не последовало ни в личке, ни в статье (но на карме они отразились в сторону уменьшения, что показывает о сильно несогласных со мной, но боящихся высказаться и получить от кого-либо уменьшение уже своей кармы. Бред, конечно, с этой кармой, но вот трясутся за неё все там.). :)

Цитировать
Но опять же все это решается гораздо проще когда у вас есть методы возвращающие констаты и инкапсулирующие инициализацию.

Если так делать, тогда вообще можно вместо констант поставить не класс, а любую функцию со статической константной переменной. :-\ Но тогда мы просто вернёмся к обычным функциям, возвращающим параметр. Кстати,я не пробовал создать массив от результата такой функции. ::) Типа long Array[GetArraySize()];, если const long& GetArraySize(void) {static const long a=10; return(a)}; Надо проверить будет. :)

Цитировать
Но не желаю вам поддерживать такой список команд, размазанный по всему коду в виде

Нет, там сравнение в цикле. Для того и в вектор помещается.

Цитировать
Если Вам так удобнее - пользуйтесь макросами.

Я отталкивался от категоричного "4)константные данные следует помещать в const, а не толкать в дефайны.", потому и решил попробовать. :)

Цитировать
Почему Вы вообще паритесь по поводу констант и макросов - если программу всеравно после выбрасывать =) ? Если человек задумывается что лучше использовать - макросы или константы, значит одна из причин - это вопросы возможности сопровождения и модификации программы в дальнейшем.

Потому что я хочу получить тот самый идеальный код (который так любит Роберт Мартин), но далеко не со всем согласен из того, что он там рекомендует. Это с одной стороны. С другой появляется мысль попробовать сделать так, как рекомендуется - быть может, я ошибаюсь в ряде положений или не знаю ряд приёмов, при которых описанное тем же Мартином сильно улучшит код. Сопровождаться и модифицироваться этот код будет, а вот меняться идеология этого блока - нет. Это всего лишь сервер, выполняющий ряд операций над двумя базами данных по командам по сети от клиентских программ. Тут ясно, что класс сервера просто вещь в себе и шансов стать чем-то другим у него просто нет.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Июля 07, 2017, 10:51:48 pm
Цитировать
Ну,про недостатки макроопределений это и так понятно. :)
Это скорее не недостаток, это правило использования. У глобальных переменных есть свои правила (константных и не константных).
Недостаток это когда у макроса нельзя взять адрес, или когда он не ограничен областью видимости или когда он не контролируется в преобразовании типа и тд.. Эти ограничения вообще нельзя обойти - так как макросы разворачиваются до компиляции. Вот это и есть их недостатки (хотя и ряд достоинств именно в этом).
Цитировать
Я просто решил попробовать под впечатлением от ..

Все его комментарии важны в случае если программу планируют поддерживать и она не является примером академическим. Иногда у Вас мелькают фразы - что в большинстве случаев такое программам Вашим не светит. Если это не так - то следует прислушаться, потому как через пару лет все ваши алгоритмы никто поддерживать не станет. По причине - затруднений в чтении самой программы при переносе на другие платформы/языки.

Цитировать
А по поводу разбиения функции на короткие кусочки, я (и не только я) полагаю, что линейная последовательность не нуждается в разбиении.
ИМХО - Вы не правы. Даже если в функции есть только линейное исполнение (без циклов и условий), лучше воспринимается не одиночные атомарные команды (завязанные на имплементацию), а сгруппированные и логически связанные наборы команд в ограниченном количестве. Т.е. если функция содержит десяток простыней текста, всегда можно сгруппировать и свести функцию к десятку вызовов логических методов. Вы ранее упоминали слово "захламляет" - тут схожий принцип, большое количество команд, захламляет восприятие всей функции в целом.

Относительно использование классов, и что все хотят видеть в таком роде объекты. Это смотреть нужно в конкретном случае. Но да, очень часто хотят видеть объект типа Point и объект типа Size, а не объекты типа std::pair<int, int> или не дай Бог int x, y (которые размазаны по всему коду). Почему так? Ну понятно же над точкой  можно проделать любые операции характерные точке, а над Size - совсем другие (хотя и тот и другой может быть представлены как два int). Когда какая-то функция принимает в качестве параметра Point в нее будет затруднительно впихнуть Size. Но вообще без проблем это сделать если функция принимает только два int. Люди которые читают подобный код, тратят время не только что бы понять как автор решил проблему, а и на то чтобы понять что именно он передал в метод (например)

Цитировать
Если так делать, тогда вообще можно вместо констант поставить не класс, а любую функцию со статической константной переменной. :-\
Но тогда мы просто вернёмся к обычным функциям, возвращающим параметр. Кстати,я не пробовал создать массив от результата такой функции. ::) Типа long Array[GetArraySize()];, если const long& GetArraySize(void) {static const long a=10; return(a)}; Надо проверить будет. :)
Нет, все не так. Или я Вас не понял что вы поставить хотите вместо классов.

Цитировать
Нет, там сравнение в цикле. Для того и в вектор помещается.
Опять же я, я не понимаю в чем был вопрос о френд-функции в контексте доступа к списку команд (пришедших по сети). В чем заключается такой доступ? Как он будет разделять одну команду от другой если не использует сравнение?)

Цитировать
Потому что я хочу получить тот самый идеальный код (который так любит Роберт Мартин), но далеко не со всем согласен из того, что он там рекомендует. Это с одной стороны. С другой появляется мысль попробовать сделать так, как рекомендуется - быть может, я ошибаюсь в ряде положений или не знаю ряд приёмов, при которых описанное тем же Мартином сильно улучшит код.

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


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Июля 09, 2017, 01:15:21 pm
Цитировать
Если это не так - то следует прислушаться, потому как через пару лет все ваши алгоритмы никто поддерживать не станет. По причине - затруднений в чтении самой программы при переносе на другие платформы/языки.

Указанный комментарий относился к программе пасьянса "Косынка" (аналогичной той, что я здесь для QNX приводил, только перенесённую под Windows и с частично убранными магическими числами (частично - потому что просто лень было количество карт (их 52) задать отдельно. )), которая сама по себе очень простая и там максимум 200 строк отвечают за, собственно, игру. А проблемы с программами, обычно, не в понимании, что и как делает функция (это, как правило, очевидно из комментария или просто по виду функции), а в том, чтобы представить общую концепцию программы.
Скажем, вот функция:
Код:
//----------------------------------------------------------------------------------------------------
//рисование полов
//----------------------------------------------------------------------------------------------------
void CEngine_Base::DrawFloor(long sector_index,const SVisualPlanes &sVisualPlanes_Bottom)
{
 //параметры сектора
 long level=vector_CISectorPtr[sector_index]->GetDown();
 long texture=vector_CISectorPtr[sector_index]->GetCTextureFollow_Down_Ptr()->GetCurrentTexture().TextureIndex;
 long bright=vector_CISectorPtr[sector_index]->GetLighting();
 long z=static_cast<long>((PlayerZ-level)*(WindowHeight/2));

 long screen_min_x;
 long screen_max_x;
 long x;
 long y;
 screen_min_x=sVisualPlanes_Bottom.MinX;
 screen_max_x=sVisualPlanes_Bottom.MaxX;
 if (screen_max_x<screen_min_x) return;
 long y_top=sVisualPlanes_Bottom.TopY[screen_min_x];
 long y_bottom=sVisualPlanes_Bottom.BottomY[screen_min_x];
 for(y=y_top;y<=y_bottom;y++) X_Table[y]=screen_min_x;
 for(x=screen_min_x;x<=screen_max_x;x++)
 {
  long zd;
  long top_plane_y=sVisualPlanes_Bottom.TopY[x];
  long bottom_plane_y=sVisualPlanes_Bottom.BottomY[x];
  if (bottom_plane_y<top_plane_y) continue;//при возможных пропусках точек на границах (а они есть), алгоритм развалится

  //если верхняя линия поднимается
  while(top_plane_y<y_top)
  {
   y_top--;
   X_Table[y_top]=x;
  }
  //если нижняя линия опускается
  while(bottom_plane_y>y_bottom)
  {
   y_bottom++;
   X_Table[y_bottom]=x;
  }

  //если верхняя линия опускается
  zd=(y_top-WindowYCenterWithOffset)+1;
  while(y_top<top_plane_y)
  {
   long dist=z/zd;
   long scale=dist/zd;
   DrawTextureLine(dist,scale,bright,texture,y_top,X_Table[y_top],x-1);
   y_top++;
   zd++;
  }

  //если нижняя линия поднимается
  zd=(y_bottom-WindowYCenterWithOffset)+1;
  while(y_bottom>bottom_plane_y)
  {
   long dist=z/zd;
   long scale=dist/zd;
   DrawTextureLine(dist,scale,bright,texture,y_bottom,X_Table[y_bottom],x-1);
   y_bottom--;
   zd--;
  }
 }
 //заливаем промежуток между top и bottom
 long zd=(y_top-WindowYCenterWithOffset)+1;
 for(y=y_top;y<=y_bottom;y++,zd++)
 {
  long dist=z/zd;
  long scale=dist/zd;
  DrawTextureLine(dist,scale,bright,texture,y,X_Table[y],screen_max_x);
 }
}
Вот не понимая концепции её работы в рамках программы, потребуется позеленеть, чтобы понять, что вообще происходит в этой функции и как она работает. Хотя что делает с переменными сама функция понятно совершенно. Но вот почему она делает именно так останется загадкой. Без объяснения алгоритма это понять по исходному коду будет очень и очень не просто.
Вот есть у меня исходник Quake-1 для PSP и очень непросто (даже со всеми введёнными Кармаком типами) понять, как именно он работает. Тут поможет только описание структуры программы и UML-диаграммы.

Цитировать
ИМХО - Вы не правы.

Ну, я тогда перефразирую, что делать слишком большие функции тоже плохо, ровно как и плодить кучу мелких по принципу, лишь бы в экран влезала каждая. Дело в том, что обычно по ходу функции готовятся данные для финального действия и в случае вызовов мелких функций это требует передачи им этих накопленных данных (что может быть весьма непросто, вплоть до создания самостоятельного объекта этих данных), а кроме того, большое количество функций порождает спагетти, где требуется раскручивать программу по кусочкам и затрудняет понимание. Иными словами, во всём нужна мера. :) Вот тут (ссылка (https://habrahabr.ru/post/324130/#comment_10122358)) есть прекрасный разбор сказки о Курочке Рябе в приёмах деления на функции: :)
Цитировать
Естественно, разбираться в 2000 строчках кода сложнее, чем в 20. Кто бы спорил? Но если 20 умножить на 100, то 2000 не получится. Добавится необходимость помнить, как эта беда складывается в дерево вызовов. То есть помнить, что «Мышка бежала» получает управление через функцию «ЯичкоРазбито», которая стартует после «Снесла она яичко не простое, а золотое» в функции «ПолучениеЯичка» и перед «Не золотое, а простое» в функции «Обещание».

Цитировать
Люди которые читают подобный код, тратят время не только что бы понять как автор решил проблему, а и на то чтобы понять что именно он передал в метод (например)

Это, безусловно, имеет смысл. Но также, в зависимости от ситуации.

Цитировать
Нет, все не так. Или я Вас не понял что вы поставить хотите вместо классов.

Честно говоря, я уже запутался в порядке предложений. Но я имею в виду вот что.
Вот это:
Код:
// хедер команд
namespace command
{
    //
    using CommandName = std::string;
    using CommandVector = std::vector<CommandName>;
    //....
    
    class Server
    {
    public:
        static const CommandVector& getDefaultCommands();
        static const Command & login();
        static const Command & password();
        static const Command & connect();
    }
};
В таком случае не требуется создания класса команд. Точно также можно в пространстве имён Command задать все эти функции без класса в отдельном модуле и при первом же вызове они будут инициализированы. Но вот что ещё плохо при использовании функций, возвращающих константу: я не уверен, что цикл for (long n=0;n<constants::GetMaxItem();n++) не будет любым компилятором вызывать с каждой итерацией цикла функцию (на -o3 может и не будет). А это уже не так хорошо, если цикл большой.

Цитировать
Опять же я, я не понимаю в чем был вопрос о френд-функции в контексте доступа к списку команд (пришедших по сети). В чем заключается такой доступ? Как он будет разделять одну команду от другой если не использует сравнение?)

Про дружественную функцию я не предполагал открывать функции, возвращающие константы всем подряд. Ну и сами функции, полагал, что будут храниться у класса CServerThread как private.
А вот как разделять команды - вот тут я пока думаю. Либо каждой дать идентификатор для удобства, либо (как сейчас сделано) сравнивать содержимое элемента вектора с каждой константой {if (vector_string[n].compare(Command_Login)==0) ... }. Тут, думаю, появится вопрос, а зачем тогда вектор? Да просто прежде чем сравнивать нужно бы проверить, что в буфере принято достаточно байт для команды и параметров. А это удобно сделать унифицированно.

Цитировать
Идеальный код, это такой код который не вызывает вопросов ни у Вас, ни у ваших сотрудников которые будут его сопровождать

В том и фишка, что одному нравится, другому Ад и Израиль. :)
Мне, например, вот такое вообще не нравится ни по оформлению строк,ни по переменным, ни по читабельности:
Код:
template<size_t len,size_t Dim, typename number_t> vec<len,number_t> proj(const vec<Dim,number_t> &v) { //проекция вектора
    vec<len,number_t> ret;
    for (size_t i=len; i--; ret[i]=v[i]);
    return ret;
}
Кстати, это фрагмент от того комментатора, которому мой код не понравился. :)
Ну и ему написали (с чем я согласен и не из вредности  :D ): ссылка (https://habrahabr.ru/post/248909/#comment_8244609)
Цитировать
Ваш трюк с циклом for мне кажется неуместным. Он хорош, когда действительно важно бежать с конца в начало, но, поскольку в ваших функциях вам все равно, то лучше использовать классический

for (size_t i=0; i<Dim; ++i)

Экономить 3 символа это, конечно, хорошо, но в отличие от классического варианта этот при прочтении прямо таки просит взять в руки бумажку и проверить, что тут нигде не налажали. Кроме того, отказ от него позволил бы вам сэкономить целый абзац в статье, раз уж вам это так нравиться.

Кроме того, вносить действие в шапку цикла считается признаком плохого стиля, а когда тело цикла пустое, рекомендуется вместо незаметной ; на той же строке, что и шапка, писать так:

for (size_t i=len; i--; ret=v) {}

или так:

for (size_t i=len; i--; ret=v)
;

Все-таки это материал для новичков, поэтому надо стремиться показывать хороший код.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Июля 10, 2017, 09:56:40 am
А проблемы с программами, обычно, не в понимании, что и как делает функция (это, как правило, очевидно из комментария или просто по виду функции), а в том, чтобы представить общую концепцию программы.
Скажем, вот функция:
Вот не понимая концепции её работы в рамках программы, потребуется позеленеть..

Ответе на вопрос - почему требуется зелень чтобы понять написанный код? Почему метод написан так, что сам код не объясняет решение/подход? Ведь он не делает ничего такого (никаких сложных преобразований)? Это без относительно конкретного примера - если написанный код даже не намекает читающему о используемых концепциях и заставляет читающего постоянно перескакивать между уровнями абстракции (на два вверх или на два вниз), заставляет двигать свой "стек" в голове - код плох. Особенно плох, если это ощущение не у одного читающего (в команде).
Яркий пример - глобальная переменная, которая изменяет интенсивно свое значения на разных уровнях вызова/и в разных модулях и таким же способом участвующая условных проверках. Читающему такой код будет весьма сложно помнить состояние в конкретном месте исполнения.
По конкретному примеру. Да, читается он плохо. Я могу прокомментировать - если бы у автора была нужда услышать мой комментарий, или если бы я участвовал в команде поддерживающий данный код. ИМХО - код явно требует более читаемых модификаций (и комментарии в коде тут играют второстепенную роль) начиная от того что нет нужды разделять объявления локальных переменных и их инициализацию, до того - что циклы с отрисовкой - явные кандидаты на сервис-методы

Цитировать
Ну, я тогда перефразирую, что делать слишком большие функции тоже плохо, ровно как и плодить кучу мелких по принципу, лишь бы в экран влезала каждая.
Разумеется это так. Не будем брать крайности.
Но, в свое время, там где я работаю пришли к важному правилу.
Читаемость кода - в приоритете даже над его производительностью!
Причина тому проста - гораздо проще поднять производительность в читаемом коде после, нежели фиксить ошибки или сопровождать код который нечитаемый.
Поэтому, действительно - код который не помещается (на один/два экрана) - как правило явный кандидат на переработку.

Цитировать
То есть помнить, что «Мышка бежала» получает управление через функцию «ЯичкоРазбито», которая стартует после «Снесла она яичко не простое, а золотое» в функции «ПолучениеЯичка» и перед «Не золотое, а простое» в функции «Обещание».
Еще раз - код который заставляет постоянно держать в голове разного уровня (относительно вызова) логические абстракции - так же плох, как и тот который замешивает эти уровни в единственный монолитный.



Цитировать
Честно говоря, я уже запутался в порядке предложений. Но я имею в виду вот что.
Вот это:
...
В таком случае не требуется создания класса команд. Точно также можно в пространстве имён Command задать все эти функции без класса в отдельном модуле и при первом же вызове они будут инициализированы.
Разумеется! Код был приведен в контексте раскрытия использования инициализации для глобальных-статических переменных. Всего лишь.
Все этом ожно поместить в отдельный неймспейс.
Но я предпочитаю классы. Почему? Да потому что, объект-класса можно создать более контролируемо, как и удалить. Можно так-же использовать ограничения доступа (скрыть сервисные методы) и т.д. Все что можно делать с типом - можно делать и с классом. А это недоступно в пространстве имен. Пробуйте отдать как параметр шаблона пространство имен )))

Цитировать
Но вот что ещё плохо при использовании функций, возвращающих константу: я не уверен, что цикл for (long n=0;n<constants::GetMaxItem();n++) не будет любым компилятором вызывать с каждой итерацией цикла функцию (на -o3 может и не будет). А это уже не так хорошо, если цикл большой.
Вот, все - мне нечего возразить человеку который регулярно игнорирует инициализацию выражением и на следующей строчке присваивает переменной вычисляемое значение.
Ну и так, между прочем - он вернет константную ссылку. Между прочим, метод может быть констэкспр - вычисленный на этапе компиляции. Метод может быть заинлайненый.
Вас не смущает выражение my_vector или array? Там ведь тоже идет операция смещения (указателя) и разименование его. Опять же - дляя таких целей вызовите метод, получите константную ссылку и используйте себе на здоровье.


Цитировать
Про дружественную функцию я не предполагал открывать функции, возвращающие константы всем подряд.
Внезапно, сами ответили почему класс а не пространство имен. Но, опять же - если вам нужна константа доступная только для этого класса - делайте ее членом класса (статическим). Не нравится член класса - делайте отдельный анонимный неймспейс в cpp-unite пользуемого класса (с классом для команд или без - сами решайте).

Цитировать
Либо каждой дать идентификатор для удобства, либо (как сейчас сделано) сравнивать содержимое элемента вектора с каждой константой {if (vector_string[n].compare(Command_Login)==0) ... }. Тут, думаю, появится вопрос, а зачем тогда вектор? Да просто прежде чем сравнивать нужно бы проверить, что в буфере принято достаточно байт для команды и параметров. А это удобно сделать унифицированно.

Мда.. сравнение все таки есть, но его  там.. как вы сказали?))
Цитировать
Нет, там сравнение в цикле. Для того и в вектор помещается.



Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Июля 10, 2017, 12:38:45 pm
Цитировать
Почему метод написан так, что сам код не объясняет решение/подход?

Но этот вопрос сродни вопросу, как имея формулу расчета, понять, как она была выведена? За каждой функцией лежит некий алгоритм. Вот в приведённой функции, например, даже прочитав описание идеи (я эту идею взял из книжки Хонича 1996 года “Как самому написать трёхмерную игру”, описывающую DooM изнутри) не сразу понимаешь, как это работает – придётся посидеть с карандашом и понять идею изменения столбцов экрана при проецировании сегмента на экран.

Цитировать
начиная от того что нет нужды разделять объявления локальных переменных и их инициализацию, 

Ну, это вопрос эстетики. На мой вкус так смотрится лучше. :) Также не люблю в одну строчку объявлять кучу переменных – каждой своя строка – ну не нравится чисто визуально.

Цитировать
до того - что циклы с отрисовкой - явные кандидаты на сервис-методы

Да ну, зачем ещё ради 10 строчек отдельную функцию плодить…

Цитировать
Читаемость кода - в приоритете даже над его производительностью!

Это верно. Однако, тут тоже на всех не угодишь.

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

Это непривычный для меня вариант. :) Может быть, потом понравится. :)

Цитировать
Ну и так, между прочем - он вернет константную ссылку. Между прочим, метод может быть констэкспр - вычисленный на этапе компиляции. Метод может быть заинлайненый.

В случае с классом - да, все эти методы будут inline (если я их не вынесу в cpp-файл). В других случаях с заданным явно inline - как повезёт. В целом, да, использование класса снимет эту проблему. Хотя, возможно, стоит всё же посмотреть IDA что конкретно сделает компилятор и на что будет похож такой код в сравнении с обычным макросом.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Июля 10, 2017, 02:10:21 pm
Но этот вопрос сродни вопросу....
Я вдруг понял - Вам не нужны объяснения). Странно правда, что Вы исренне удивляетесь при этом замечаниям сторонних людей)

Цитировать
стоит всё же посмотреть IDA что конкретно сделает компилятор и на что будет похож такой код в сравнении с обычным макросом.

О! Эта великая экономия на спичках! Вот они преимущества макросов ))


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Июля 10, 2017, 02:28:31 pm
Цитировать
Я вдруг понял - Вам не нужны объяснения). Странно правда, что Вы исренне удивляетесь при этом замечаниям сторонних людей)

Да нет, нужны. :) Просто я их так же анализирую, оцениваю и сравниваю в разных ситуациях. Вот, скажем, сейчас я применил RAII-подход, который вы советовали, для работы с базой данных (класс CRecordset из MFC). Этот класс действительно удобно так обернуть - экономия кучи проверок при подключении к записи базы данных. Но просто так я эти замечания не использую. Почему? Потому что за десятилетия у меня выработалась своя база приёмов и подходов и чтобы её менять нужно очень тщательно разобраться с плюсами и минусами. А следовать слепо приёмам любого автора - это настоящий фанатизм. :) Чего смешного на том же хабре в этих замечаниях? То, что если разбирается код того же Кармака, то находятся 1001 объяснение того, почему он сделал именно так, причём, от тех же людей, что только что ругали то же самое у других.  При этом на каждого критикующего автора найдётся такой же с противоположным мнением. Вот именно поэтому слепо следовать советам тоже не дело. А вот собирать их - отличная идея. :)

Цитировать
О! Эта великая экономия на спичках! Вот они преимущества макросов ))

Так в разных ситуациях по-разному. Есть циклы, которые крайне критичны, и стоит их чуть изменить, как быстродействие упадёт колоссально (или вырастет :) ). И да, я часто работаю с довольно низкоскоростными устройствами и часто вижу, что быстродействия катастрофически не хватает.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Сентября 05, 2017, 09:22:02 pm
А не знает ли кто, как может сложиться следующая ситуация: я подключил этот тепловизор к stm32f4 Discovery к USB-Host. Проект я создал в CubeMX. То есть, вся обвязка USB там точно верная. Проблема в том, что тепловизор в дескрипторе конечной точки передаёт wMaxPacketSize=0x40. И это не управляющие точки. Однако, операционным системам тепловизор по usb -vvv и lsusb -vvv сообщает о конечных точках вот что:


Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass    240
      bInterfaceProtocol      1
      iInterface              6 com.flir.rosebud.fileio
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
       wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x04  EP 4 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               1

То есть, операционным системам тепловизор рапортует о 512 байтах. А микроконтроллеру о 64. Это вообще как так может быть? Я обыскал всю библиотеку USB для STM32 и не нашёл ничего такого, что могло бы так повлиять на решение тепловизора выдавать такие параметры. Там обычная энумерация с получение дескрипторов конфигураций и устройства и задание адреса устройства. Но что-то же заставило тепловизор выдать 64, а не 512 байт? Использую я режим USB Full Speed, 12 МБ/c. Кстати, а никто не знает, запрос SET_DESCRIPTOR позволит изменить параметры конечной точки (и вообще, для чего он применяется? В инете его что-то не используют). Я бы оставил бы это всё без внимания, но на 64 байтах я не успеваю принять все данные, хотя у контроллера аж 168 МГц частота и он вроде бы должен успевать (хотя тут и PC пропускал кадры с радостью под Linux). А конечная точка в 512 байт, думаю, позволила бы пропускать кадры гораздо реже.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Сентября 06, 2017, 03:10:17 pm
Понятно, в чём дело. 64 - максимальный размер пакета для LowSpeed и FullSpeed. А для HeighSpeed максимальны размер 512.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Октября 06, 2017, 10:45:12 pm
Экспериментальная альфа-версия прошивки микроконтроллера STM32F407 платы STM32F4Discovery для работы с тепловизором Flir One Gen 2 через USB: ссылка на репозиторий (https://github.com/da-nie/nightvision)

Из особенностей:
1) Проект создан в Cube MX для Keil 5 - без Cube MX запустить USB с уровня HAL или CMSIS было бы ооочень непросто.
2) Весь объём кода от Cube MX для поддержки процессора в комплекте. Здравствуйте мегабайтные *.h-файлы CMSIS и такого же размера библиотеки HAL в *.c-файлах для работы с периферией.
3) Если три секунды нет картинки, процессор перезагружается, и процесс энумерации начинается заново.

Подключён дисплей 320x240 на контроллере HX8347D.
Через FSMC запуск не удался (возможно, мешают устройства на плате),поэтому контакты дисплея были заново подключены так:
D0-D7 - PE4-PE11
CS - PE12
RD - PE13
RW - PE14
RS - PE15
RST - PE2

А вот так работает: https://youtu.be/-elBzHRjZxo


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Октября 13, 2017, 07:41:36 pm
А вот интересно, я сделал отдельный класс для констант CConsts:

Код:
class CConsts
{
 protected:
 public:  
  //конструктор
  CConsts(void) {};
  //деструктор
  ~CConsts() {};
 public:
  //количество точек сигнала оптических датчиков
  const unsigned long GetSignalPointAmount(void) {static const unsigned long ret=128; return(ret);}
  //время паузы в циклах ожидания данных, нc
  const unsigned long GetPauseTimeNSec(void) {static const unsigned long ret=1000; return(ret);}
  //время добавления точек диаграммы, секунды
  const unsigned long GetDiagramTimeSec(void) {static const unsigned long ret=600; return(ret);}
  //максимальное количество точек диаграммы  
  const unsigned long GetDiagramMaxPointsAmount(void) {static const unsigned long ret=1000; return(ret);}
  //начальное максимальное время записи диаграммы
  const unsigned long GetDiagramInitMaxTimeSec(void) {static const unsigned long ret=10*60*60; return(ret);}
  //рабочая частота  
  const unsigned long GetWorkingFrequencyHZ(void) {static const unsigned long ret=32; return(ret);}
  //количество гироблоков
  const unsigned long GetUnitAmount(void) {static const unsigned long ret=3; return(ret);}
  //сколько тактов допустимо пропустить устройству, чтобы считать его ещё работающим
  const unsigned long GetUnitEnabledCounterMaxValue(void) {static const unsigned long ret=3*GetWorkingFrequencyHZ(); return(ret);}
  //сколько тактов допустимо пропустить АКУ, чтобы считать его ещё работающим
  const unsigned long GetACUEnabledCounterMaxValue(void) {static const unsigned long ret=3*GetWorkingFrequencyHZ(); return(ret);}
  //сколько тактов допустимо пропустить акселерометру, чтобы считать его ещё работающим
  const unsigned long GetAcsEnabledCounterMaxValue(void) {static const unsigned long ret=3*GetWorkingFrequencyHZ(); return(ret);}
  //сколько тактов допустимо пропустить БВФКП, чтобы считать его ещё работающим
  const unsigned long GetBWFKPEnabledCounterMaxValue(void) {static const unsigned long ret=3*GetWorkingFrequencyHZ(); return(ret);}
  //время аварийного торможения в тактах
  const unsigned long GetAlarmCounterMaxValue(void) {static const unsigned long ret=4*60*GetWorkingFrequencyHZ(); return(ret);}
  //сколько тактов отсутствут электропитание, чтобы считать это за аварию
  const unsigned long GetPowerOffCounterMaxValue(void) {static const unsigned long ret=60*GetWorkingFrequencyHZ(); return(ret);}
  //максимальное значение счётчика для принудительного сохранения данных (1 такт - 10 мс)
  const unsigned long GetSaveChacheCounterMaxValue(void) {static const unsigned long ret=100*60; return(ret);}
  //количество точек в графиках сигналов
  const unsigned long GetTrendSignalsPointsAmount(void) {static const unsigned long ret=350; return(ret);}
  //количество точек в графиках углов
  const unsigned long GetTrendAnglesPointsAmount(void) {static const unsigned long ret=350; return(ret);}
  //количество точек в графиках углов в неподвижном основании
  const unsigned long GetTrendStillAnglesPointsAmount(void) {static const unsigned long ret=295; return(ret);}
  //какой такт добавлять в график углов в неподвижном основании
  const unsigned long GetTrendStillVoidCycle(void) {static const unsigned long ret=12; return(ret);}
  //бесконечно малое
  const double GetZeroEPS(void) {static const double ret=0.00000000001; return(ret);}
  //точность задания углов ПАК, угловые секунды
  const float GetPAKAngleSecEPS(void) {static const float ret=10; return(ret);}
};

И использовал этот класс для задания структуры с вложенной структурой внутри, вида
struct SSettings
{
 long Port;//порт сервера
 struct SUnit
 {
  //....что-то тут
 } sUnit[cConst.GetUnitAmount()];// !!!!!Вот в чём проблема!!!!!
 bool EnabledPowerMonitor;//разрешение контроля питания
};

И вот компилятор IDE Momentics теперь ругается - он позволяет задавать Port (sSettings.Port=...), но не позволяет задавать EnabledPowerMonitor. Пишет, что структура переменного размера. Э... Ну, логично - размер ведь задан в другом модуле, а про него компилятор ничего не знает. Только вот как это обойти?  ::)


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Октября 16, 2017, 03:00:14 pm
Цитировать
А вот интересно, я сделал отдельный класс для констант CConsts:
На первый взгляд вам вообще класс не нужен, и объект создавать этого класса вроде бы нет такой необходимости. Достаточно было все эти методы объявить свободными и в
едином неймспейсе. Разумеется, если речь о модульности (хедеры и сипипишники в том же числе), то так же странно - почему реализация методов в "хедере".

С таким классом как ваш (который не содержит данные, а обеспечивает доступ к константам через методы) - достаточно в каждом модуле объявить свой объект cConst.
И лучше это сделать непосредственно в момент вызова (так как создание объекта по сути ничего не стоит - он пустой).
Но опять же, вы из всех путей нашли самый извращенный )) Вы почему-то решили для возвращения конкретного значения создавать статический объект. Возвращаете его по значению (при этом возвращаете как константный объект, а сам метод не константный - но это все дело десятое и к вашему вопросу сейчас мало относится, просто рано или поздно вернетесь к интерфейсу и все равно сделаете правильно)
Так вот, возвращая по значению - вы тем самым уже гарантируете что внутренне значение снаружи изменено быть не может. А значит вполне можно было сделать и так:
Код:
unsigned long CConsts::GetPowerOffCounterMaxValue() const
{
  return 60*GetWorkingFrequencyHZ();
}

Цитировать
И использовал этот класс для задания структуры с вложенной структурой внутри, вида
struct SSettings
{
 long Port;//порт сервера
 struct SUnit
 {
  //....что-то тут
 } sUnit[cConst.GetUnitAmount()];// !!!!!Вот в чём проблема!!!!!
 bool EnabledPowerMonitor;//разрешение контроля питания
};

Конкретно с вашей проблемой - вам необходимо знать размер вашего массива на момент компиляции. Классом который оперирует и вычисляет/возвращает свои значения в рантайме - воспользоватся не получится. Для этого вами придется использовать constexpr методы/значения - они вычисляться на этапе компиляции.
Но я так же бы рекомендовал второй подход. Начните программировать в стиле C++. Все структуры как и классы можно (в большинстве случаев - даже нужно) инициализировать с помощью механизмов конструкторов.
Если тип SSettings может быть инициализирован каким-либо объектом/размером - инициализируйте этим объектом/размером непосредственно в конструкторе, например:
Код:
struct SSettings
{
 long Port;//порт сервера
 struct SUnit
 {
  //....что-то тут
 };
 using SUnitList_t = std::vector<SUnit>;
 SUnitList_t sUnits;  // и никаких проблем

 bool EnabledPowerMonitor;//разрешение контроля питания

//////

 SSettings()
   : SSettings(CConsts {})
 {
 }

 explicit SSettings(const CConsts& initObj)
   : Port (0)
   , sUnits(initObj.GetUnitAmount())  // перенесли инициализацию в рантайм
   , EnabledPowerMonitor(false)
 {
 }
};


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Октября 16, 2017, 06:40:35 pm
Спасибо за ответ. :) Я и не сомневался, кто из присутствующих тут напишет. :)

Цитировать
На первый взгляд вам вообще класс не нужен, и объект создавать этого класса вроде бы нет такой необходимости. Достаточно было все эти методы объявить свободными и в
едином неймспейсе. Разумеется, если речь о модульности (хедеры и сипипишники в том же числе), то так же странно - почему реализация методов в "хедере".

Просто приятно, когда всё собрано в обном месте - в данном случае, в классе. Я, может быть, потом часть констант из констант переведу в загружаемые из файла параметры. С классом удобно. Почему в заголовочном файле - по двум причинам: и лень было реализацию писать отдельно, и наглядно - все описания рядышком, и всё как встраиваемые функции соберётся.

Цитировать
С таким классом как ваш (который не содержит данные, а обеспечивает доступ к константам через методы) - достаточно в каждом модуле объявить свой объект cConst.

Можно, конечно. Только в чём же будет выгода, всесто extern сделать статический объект для каждого модуля?

Цитировать
И лучше это сделать непосредственно в момент вызова (так как создание объекта по сути ничего не стоит - он пустой).

Вы имеете в виду, использовать как временный объект при получении каждой константы? Конечно, можно и так. Просто, а... зачем?  :o

Цитировать
Но опять же, вы из всех путей нашли самый извращенный ))

О, таких путей Си++ предлагает просто множество. :) Грех не воспользоваться. А как приятно видеть в IDE Momentics после автоматической замены во всех файлах макроса на такой объект ( с ошибочной заменой в ряде мест - заменяемая фраза оказалась частью большей) "неизвестная ошибка компилятора номер..." и ссылку на сайт для скидывания информации о том, как мне удалось эту ошибку сделать. ;D Я долго не мог понять, а что произошло-то, пока не обнаружил неверную замену.

Цитировать
при этом возвращаете как константный объект, а сам метод не константный

Да просто с вероятностью 100% не буду я никогда этот объект cConst никуда как константный передавать (он глобальный - у меня всё-таки есть пара глобальных объектов на всю программу). Да и вообще, я просто от нечего делать решил небольшой рефакторинг сделать. Ну и так, из интереса, заменить ряд структур на классы, да и макросы на классы тоже. Всё-таки программу три года я не улучшал, и накопились неудачные на текущий момент решения, мешающие её развивать дальше.

Цитировать
Классом который оперирует и вычисляет/возвращает свои значения в рантайме - воспользоватся не получится. Для этого вами придется использовать constexpr методы/значения - они вычисляться на этапе компиляции.

Увы, это достаточно новый оператор и IDE Momentics с её компилятором его вряд знает.

Цитировать
Так вот, возвращая по значению - вы тем самым уже гарантируете что внутренне значение снаружи изменено быть не может. А значит вполне можно было сделать и так:

Ну да, ну да. Можно было, конечно. Я ещё не решил, что мне нравится больше (да, есть фразы комментариев и текст, который просто вызывает какое-то отторжение, а есть наоборот). :)

Цитировать
Начните программировать в стиле C++. Все структуры как и классы можно (в большинстве случаев - даже нужно) инициализировать с помощью механизмов конструкторов.

Проблема в том, что SSettings не единственный, кому требуются эти самые константы. Его-то можно так инициализировать и вектор ему поставить. А вот есть структуры протоколов обмена - они целиком по сети пойдут один-в-один. А часть кода вообще кусок на чистом Си - эта часть вставляется потом в микроконтроллер один-в-один. А кстати, надо глянуть, не попадёт ли туда часть констант из этого класса.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Октября 16, 2017, 08:04:33 pm
Цитировать
и всё как встраиваемые функции соберётся.
Кстати вообще нет никакой прямой связи что будет собираться встраиваемо а что нет. Метод может быть и встраиваемым даже если его реализация внутри cpp-модуля (яркий пример лямбды)

Цитировать
Можно, конечно. Только в чём же будет выгода, вместо extern сделать статический объект для каждого модуля?
Речь шла скорее о том чтобы создавать переменные CConsts налету, прям перед вызовом. По сути они не должны занимать сколько-либо памяти. С другой стороны - преимущество сгруппированных в пространстве имен методов по сравнению с такими вот классом в том - что для пространства имен не требуется создавать объекты (описывать это явно).
Но да, если вы хотите использовать свой класс как объект который что-либо загружает, хранит состояние - то со свободными функциями все будет посложнее.

Цитировать
Вы имеете в виду, использовать как временный объект при получении каждой константы? Конечно, можно и так. Просто, а... зачем?  :o
Зачем Вам - я не знаю) Но могу предположить, что в упрощенном варианте ваш объект может хранить/ссылаться на разные наборы "констант" (загруженных из файла/полученных по сети). Либо объекты эти могут быть инициализированы частично и иметь возможность объединятся или разделятся (компоноваться в единую всеобъемлющую настройку).
Один словом - состояния набора "констант" может зависеть от условий вызова. Вот тогда такой объект удобно будет формировать непосредственно перед вызовом анализируя некие условия

Цитировать
О, таких путей Си++ предлагает просто множество. :) Грех не воспользоваться. А как приятно видеть в IDE Momentic ...
Проблемы среды разработки - это только проблема среды разработки, а не языка. Имхо - я бы для упрощенного случая сделал бы пространство имен с константами, сделал бы отдельно класс настроек который может быть кем-то заполнен (из файла/сети или другого ввода), а по умолчанию инициализировался бы значениями вызываемых методов (методы бы возвращали по значению неименнованные константы/литералы)

Цитировать
Да просто с вероятностью 100% не буду я никогда этот объект cConst никуда как константный передавать
Я просто отметил - что константность к возвращаемому значению это многословность, которая для читающего Ваш код человека немного вводит в некое заблуждение (не понятно что вы этим хотели сказать - не хотите чтобы возвращаемое значение изменялось, но оно же возвращается по значению, а значит с конст или без - оно никогда не поменяет внутренне состояние объекта. Если же вы хотели обеспечить контракт вызова - то следовало вероятно сделать конст-метод, но вы этого не сделали. Это когда const в конце объявления метода)

Цитировать
Увы, это достаточно новый оператор и IDE Momentics с её компилятором его вряд знает.
Это конечно жаль


Цитировать
А вот есть структуры протоколов обмена - они целиком по сети пойдут один-в-один. А часть кода вообще кусок на чистом Си - эта часть вставляется потом в микроконтроллер один-в-один. А кстати, надо глянуть, не попадёт ли туда часть констант из этого класса.
Ну вы же в курсе что вектор от вашего массива отличатся будет незначительно. У вектора можно запросить указатель на данные которые будут представлены в точь точь как если бы данные хранились в вашем массиве. Вас смущает тот момент что сейчас вы просто берете адрес объекта настроек SSettings и передаете его в сеть, а вот с вектором внутри вам придется помучатся. Но это же правильный подход! По-тому что, в ++ нет гарантий к размеру всех типов (единственные гарантии - это тип char, насколько помню). Кроме того - создав отдельный метод сереализации/десереализации - всегда можно контролировать правильный порядок байт (иногда есть большая разница как данные должны хранится оптимально и как передаваться/сохранятся по протоколу)


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Октября 16, 2017, 08:43:02 pm
Цитировать
Кстати вообще нет никакой прямой связи что будет собираться встраиваемо а что нет. Метод может быть и встраиваемым даже если его реализация внутри cpp-модуля (яркий пример лямбды)

В смысле? Насколько я помню, все методы, реализованные в самом объявлении класса являются inline всегда (и, полагаю, даже если там функция в 1000 строк будет, она всё равно будет встраиваемой). А вот отдельная вынесенная реализация уже будет как удобно компилятору (если указать inline для такой функции, конечно).

Цитировать
Но могу предположить, что в упрощенном варианте ваш объект может хранить/ссылаться на разные наборы "констант"

Нет, это, к счастью, не потребуется. :)

Цитировать
Проблемы среды разработки - это только проблема среды разработки, а не языка.

Не спорю. Но забавно видеть, когда компилятор падает от написанного. :)

Цитировать
Если же вы хотели обеспечить контракт вызова - то следовало вероятно сделать конст-метод. Это когда const в конце объявления метода

Я знаю, как это делается. :) А зачем я возвращаю как const? Да просто это больше информация для компилятора  - так, на всякий случай.

Цитировать
Ну вы же в курсе что вектор от вашего массива отличатся будет незначительно. У вектора можно запросить указатель на данные которые будут представлены в точь точь как если бы данные хранились в вашем массиве.

Касаемо вектора - я где-то прочёл, что это не всегда так (я имею в виду, предложенный вами мне когда-то вариант с &vect[0]). В ранних стандартах порядок хранения в контейнерах не регламентирован. Это только сейчас так. Я понятия не имею, что умеет в QNX 6.3 компилятор и стандартные библиотеки.
А в структуре, которая передаётся как поток байтов, вектора внутри быть не должно - требуется ведь передать его содержимое, а не внутреннюю структуру вектора.
Я имею в виду:
Код:
struct SData
{
 unsigned long Size;
 struct SUnit
 {
  unsigned char Data[128];
 }
 vector<SUnit> vector_SUnit;
} sData;

Translate(&sData,sizeof(SData));

Translate(&sData,sizeof(SData)); - будет передан ведь вовсе не SUnit[заданное количество] внутри.

Цитировать
По-тому что, в ++ нет гарантий к размеру всех типов

По-моему, только int и long double могут иметь машинозависимую длину. Char имеет компиляторозависимый знак. Float мог быть произвольный - в старых компиляторах не по стандарту IEEE. Порядок же кодирования big или little-endian может меняться, это да. Остальные стандартные типы заданы жёстко (ну, может, double ещё схож с float). Про допкод не уверен, но, думаю, что он гарантируется.
Вообще, тут уже действует соглашение на протокол обмена по устройствам, где порядок данных описан как little-endian, допкод для переменных. Все устройства у которых это не так должны сами перекодировать данные.
Исключение составили разработчики из ЛОМО - они у нас отличились и вляпали свои числа в... прямом коде. Говорят, вояки всегда с ним работают. Заставить переделать не удалось. Ну и ладно - хоть один урод в списке аппаратуры комплекса непременно должен быть. :)

Цитировать
Кроме того - создав отдельный метод сереализации/десереализации - всегда можно контролировать правильный порядок байт (иногда есть большая разница как данные должны хранится оптимально и как передаваться/сохранятся по протоколу)

Это будет редкий *дец. :) Структура состояния устройств с подструктурами по типам принятых данных это около 500 строк. Их передача/приём с разложением на байты породит большущий код. Но... зачем?


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Октября 17, 2017, 05:04:43 pm
Цитировать
В смысле? Насколько я помню, все методы, реализованные в самом объявлении класса являются inline всегда
Вероятно я не точно выразился написав "нет никакой прямой связи"..
В том то и дело что НЕ ВСЕГДА. У каждого компилятора существуют свои ограничения и он на свое усмотрение мог взять и не встроить вашу функцию, даже если она реализована в хедере. Просто потому, что она вызывает внутри себя что-то виртуальное, содержит какие-то сложные циклы/условия.
Да, в целом существует требование на встраиваемость функций/методов если такое поведение ожидается получить между разными единицами трансляций - тело методов должно быть в хедерах. Но будет ли метод встроеным - решит компилятор в конечном счете, а не Вы. Вот и получается - что ожидание от inline всегда немного преувеличены.

Цитировать
В ранних стандартах порядок хранения в контейнерах не регламентирован. Это только сейчас так. Я понятия не имею, что умеет в QNX 6.3 компилятор и стандартные библиотеки.
Все то время в течении которого я программирую на с++ - все это время std::vector был последовательным контейнером который предоставлял доступ к единому/цельному куску памяти (аля массив).
Другое дело, что у вектора не всегда был метод data(), но вот с &vect[0] - всегда решал поставленную задачу.

Цитировать
А в структуре, которая передаётся как поток байтов, вектора внутри быть не должно - требуется ведь передать его содержимое, а не внутреннюю структуру вектора.
О том и речь, что ваша структура не только хранит/представляет данные, но она то по сути знает как данные должны передаваться.
Я понимаю что так проще и привычнее. Но до тех пор, когда протокол имеет фиксированную структуру/пакеты, пока нет версионности и пока не окажется что в разных протоколах нужны разные порядки сохранения/чтения данных.
Относительно вашего случая - вы уверены что на этапе компиляции размер настроек известен. А это значит, что пользователь не может ни увеличить ни уменьшить их количество со временем и под конкретную конфигурацию. Вы передаете настройки "на другую сторону", а значит на принимающей стороне работает программа собранная в соответствии и согласованная по размеру этой структуре настроек. Все это до тех пор, пока у вас есть доступ к синхронному обновлению бинарника программ для передатчика и приемника.
Мое замечание относится к тому, что не составляет труда определить методы внутри хотя бы того-же:
Код:
size_t SData::save(char *to);
size_t SData::load(char *from);
Упрощенно, но .. Внутри которых, как хотите так и читаете. И можете дописать/прочитать данные которые в структуре не хранятся явно, а можете предварительно проверить пред- и пост-условия (чтобы понять насколько пришедшие данные вообще cответствуют ожидаемым)

Цитировать
По-моему, только int и long double могут иметь машинозависимую длину.
Вся гарантия размеров типов в С++ сводится к этому
Код:
1 == sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long).
Про это можете почитать тут (http://ru.cppreference.com/w/cpp/language/types)

Цитировать
Это будет редкий *дец. :) Структура состояния ...
...
Но... зачем?
Объяснение дано было выше - представление структуры в памяти программы вообще может не совпадать с представлением данных при передаче. Хотя бы по тому что для передачи данных - нужны протоколы, а протоколы должны быть более устойчивы к изменениям (модификациям программ). Как правило - новые программы должны работать/взаимодействовать со старыми данными (версиями программ). Так принято. Люди не любят терять наработанные данные только потому-что вам вздумалось изменить структуру представления внутри программы


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Октября 17, 2017, 07:29:17 pm
Цитировать
В том то и дело что НЕ ВСЕГДА.

Это я не верно интерпретировал, что у Подбельского написано. Там написано, что они все inline, но фраза несколько неудачно построена, и я решил что ограничения на обычные inline-функции на эти функции не действуют (за исключением рекурсии, конечно).

Цитировать
Я понимаю что так проще и привычнее. Но до тех пор, когда протокол имеет фиксированную структуру/пакеты, пока нет версионности и пока не окажется что в разных протоколах нужны разные порядки сохранения/чтения данных.

Всё вы верно говорите, но случаи бывают разные. В данном случае такое делать просто нецелесообразно. Протокол после отладки больше не меняется никогда. Он жёстко зашивается в устройство и всё.

Цитировать
А это значит, что пользователь не может ни увеличить ни уменьшить их количество со временем и под конкретную конфигурацию.

Нет-нет, я настройки не передаю. Передаётся другое. Просто в этих настройках нужны константы и в другом тоже они же нужны.

Цитировать
Вся гарантия размеров типов в С++ сводится к этому

Отвратительно. Для языка тесно связанного с низкоуровневым программированием (я имею в виду Си) иметь гарантии в стиле "не меньше" просто идиотизм. Жаль. В книжках, вроде как, чётко пишут разрядность чисел, упоминая вот то, что я перечислил. А оказывается, гарантии в стиле "не меньше".

Цитировать
нужны протоколы, а протоколы должны быть более устойчивы к изменениям

Не всегда это нужно. Протоколы работы с оборудованием низкоуровневые и версионности не имеют практически никогда - она бесполезна. Как пример, протокол обмена по USB вот этого вот тепловизора. Всё жёстко задано и для тепловизора следующего поколения протокол другой.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Октября 18, 2017, 12:51:11 am
Цитировать
Отвратительно. Для языка тесно связанного с низкоуровневым программированием (я имею в виду Си) иметь гарантии в стиле "не меньше" просто идиотизм. Жаль. В книжках, вроде как, чётко пишут разрядность чисел, упоминая вот то, что я перечислил. А оказывается, гарантии в стиле "не меньше".
Ну, с 11-го стандарта у языка все стало проще (http://en.cppreference.com/w/cpp/types/integer)

Цитировать
Не всегда это нужно. Протоколы работы с оборудованием низкоуровневые и версионности не имеют практически никогда..

Вот с этим не согласен, но в вашем случае вам же решать.


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Октября 18, 2017, 12:14:47 pm
Цитировать
Ну, с 11-го стандарта у языка все стало проще

Ну, inttype.h уже давно в Си компиляторах типа Keil и IAR лежат. Нет только с плавающей точкой и bool.

Цитировать
Вот с этим не согласен, но в вашем случае вам же решать.

Когда работа идёт с оборудованием, этот протокол чем-то напоминает внутрипрограммный обмен - вы же почти никогда ("почти" - потому что есть случаи, когда передаёте - например, Win32API такое практикует именно по причине разных версий Windows и одних и тех же программ) не передаёте в функцию версию передаваемой структуры/объекта и её размер?

Кстати, спрошу ещё кое-что. Что бывают unit-тесты я давным-давно знаю. Но вот как их делать - без понятия. В статьях, обычно, они как-то как сами собой разумеющиеся идут (моки и стабы там всякие и т.п.). Я, обычно, такими вещами не занимаюсь и делаю всегда интеграционное тестирование (потому что почти 100% ошибки не в конкретных функциях и классах, а в их взаимодействии между собой - скажем, не в том порядке захвачены мюьтексы в разных объектах и произошла взаимоблокировка. Ну или просто не предусмотрена некая ситуация в программе в принципе, а она случилась.). Отсюда вопросы:
1) Unit-тест - это отдельная программа или они встраиваются в основное приложение (что странно, на мой взгляд)?
2) Если это отдельная программа, то делают на каждый тест одну программу или всё тестирует одна программа?
3) Вот есть у меня класс кодирования/декодирования принятых/отправленных данных. Чтобы его протестировать, нужно написать тест, который будет размером в пять таких классов (ведь то же самое кодирование/декодирование потребуется реализовать в программе теста). И нет никакой гарантии, что в тесте не будет ошибки. Да и как, например, убедиться, что все состояния покрыты тестом? Передаваться-то может просто тьма самых различных данных.
4) Ряд вещей вообще непонятно как проверять - например, передачу через сокеты/CAN/USB - там всё завязано на функции ОС и работы с аппаратурой напрямую.
5) Не получится ли тест программы сложнее и объёмнее самой программы раз в 5? По моим прикидкам, так и получится. :)


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: lastcross от Октября 18, 2017, 02:11:36 pm
Цитировать
Ну, inttype.h уже давно в Си компиляторах типа Keil и IAR лежат. Нет только с плавающей точкой и bool.
Речь о том, что теперь не нужно беспокоится будет ли поддерживать конкретный компилятор или нет эти типы. Достаточно знать что компилятор полностью поддерживает с++11 чтобы не беспокоится о том, что где-то нужно что-то подключить, задефайнить или изменить в коде. Т.е. наличие этих типы - гарантированы стандартом языка.
А так, разумеется у каждого компилятора/библиотеки были возможности использовать типы с конкретным размером байт.

Цитировать
Когда работа идёт с оборудованием, этот протокол чем-то напоминает внутрипрограммный обмен - вы же почти никогда ("почти" - потому что есть случаи, когда передаёте - например, Win32API такое практикует именно по причине разных версий Windows и одних и тех же программ) не передаёте в функцию версию передаваемой структуры/объекта и её размер?

До тех пор пока вы планируете использовать это оборудование только совместно со своими программами и игнорировать версионность (развитие программ и оборудования) - до тех пор вы будете правы. Но как только возникнет необходимость использовать старую программу с новым оборудованием или новый софт который должен работать с двумя версиями аппаратуры. Или вы начнете распространять отдельно аппарат и отдельно прогу, предоставляя другим разработчикам на любом удобном языке работать с аппаратурой (считайте вы публикуете свой API к аппарату) - все станет не таким уж и радужным. В упомянутом вами WinAPI размеры передаются/передавались отчасти потому, что этим обеспечивается и версионность, валидация и совместимость с другими языками.

Цитировать
Кстати, спрошу ещё кое-что. Что бывают unit-тесты я давным-давно знаю. Но вот как их делать - без понятия.
Вам вряд-ли понадобятся unit-тесты. Подозреваю что этот подход идет в разрез с вашими представлениями о разработке (если вас не беспокоит версионность протокола, и возможность проверки пост и пред -условий).
Да, они увеличивают время разработки. Как правило требуют наличие несколько разработчиков в команде (над одним проектом).
Они нужны скорее не для выявления ошибок в работе программы. Они для выявления ошибок проектирования. Например, когда программа разрабатывается итерационно, покомпонентно. И с самого начала, с первой итерации, можно описать такие тесты которые бы проверяли корректность основных принципов при проектировании. И по мере добавления/расширения/модификации/исправления модулей эти тесты будут проверять насколько новые модули соответствуют этим тестам(принципам).
И тут даже не вопрос с сокетами/мютексами. Без них есть что тестировать в сложных проектах.
Пример автомобиль:
Запуск системы зажигания не должен приводить к движению автомобиля. Вот это "требование" по сути и является юнит-тестом. Его проверяют каждый раз например когда модифицируется любой из под-модулей системы зажигания. Изменили что-то в системе зажигания, запускаем юнит-тесты, среди которых и проверка на движение про запуск. Проверяем общий результат - даем оценку о том удачное изменение или нет. Unit-тест не отменяет дальнейшее тестирование и не гарантирует отсутствие всех ошибок (все перекрыть не возможно ими)
Цитировать
1) Unit-тест - это отдельная программа или они встраиваются в основное приложение (что странно, на мой взгляд)?

Цитировать
2) Если это отдельная программа, то делают на каждый тест одну программу или всё тестирует одна программа?
Можно так, можно этак зависит от того как у вас построен процесс тестирования

Цитировать
3) Вот есть у меня класс кодирования/декодирования принятых/отправленных данных. Чтобы его протестировать, нужно написать тест, который будет размером в пять таких классов (ведь то же самое кодирование/декодирование потребуется реализовать в программе теста). И нет никакой гарантии, что в тесте не будет ошибки. Да и как, например, убедиться, что все состояния покрыты тестом? Передаваться-то может просто тьма самых различных данных.
Тесты требуют много скучного декларативного кодирования. Классов вам писать может придется и один, но как минимум на каждый метод понадобится отдельный тест-метод (это в лучшем случае). Но с другой стороны - unit-тесты они могут быть расширяемые. Например, проверить только конкретную функциональность, позже другую и т.д.
"Хуже" всего то - что unit-тест жестко привязан к проектированию, ну как хуже - если у вас неудачное проектное решение .. то готовтесь не только перепроектировать решение но и переписать тесты (старые не подойдут)

Цитировать
4) Ряд вещей вообще непонятно как проверять - например, передачу через сокеты/CAN/USB - там всё завязано на функции ОС и работы с аппаратурой напрямую.
Вы можете раздробить проверку - проверять свой код (первично) и взаимодействие со сторонним (вторично). Т.е. остановится на том, что на такие входные данные ваш код формирует выходные данные для отправки API ОС. У вас есть набор заранее просчитанных входных и выходных данных. И как бы вы не меняли в дальнейшем код который трансформирует входные данные к выходным - юнит-тест всегда проверит что на эталонных код работает (а значит с большой долей вероятности можно сказать - трансформация происходит правильно).

Цитировать
5) Не получится ли тест программы сложнее и объёмнее самой программы раз в 5? По моим прикидкам, так и получится. :)
Как правило - кода будет больше, да. Притом значительно. Unit-тесты не имеют никакого значения если вы не планируете развивать код, не подходите к проблеме через проектирование, или же решение проблемы должно быть найдено в кратчайшие сроки (с минимальным количеством людей).


Название: Re: Работа через USB с FlirOne Gen 2
Отправлено: da-nie от Октября 18, 2017, 06:55:23 pm
Цитировать
До тех пор пока вы планируете использовать это оборудование только совместно со своими программами и игнорировать версионность (развитие программ и оборудования) - до тех пор вы будете правы.

А так оно, обычно, и происходит. Покупая оборудование даже одного производителя (я сейчас говорю о высокоточном и сложном оборудовании с ценой из многих нулей - что в массовом сегменте я не знаю), все модификации работают только со своими компонентами и поставляются как единое целое. Заменять компоненты без доработки производителем нельзя. Часто такое оборудование имеет протоколы обмена для дистанционного управления - там часто даже не заморачиваются с проверкой целостности команд/данных. Как пример, приборы фирмы Agilent (а это классные приборы с  часто удивительными возможностями) - обычный COM-порт и текстовый протокол. Если цифра будет искажена, мы об этом не узнаем. Поэтому тут нюанс в том, что есть "как оно было бы универсально" и то, "как это сделано почти всегда на самом деле с учётом не нужной избыточности".

Цитировать
Вам вряд-ли понадобятся unit-тесты. Подозреваю что этот подход идет в разрез с вашими представлениями о разработке (если вас не беспокоит версионность протокола, и возможность проверки пост и пред -условий).

Нет. :) У меня есть версионные протоколы в моих программах. Но не для оборудования, которое мы сами жёстко задаём, а для обмена клиент-серверных приложений между собой, где действительно часты обновления возможностей программ.

Цитировать
Проверяем общий результат - даем оценку о том удачное изменение или нет.

Вот с этим-то у меня и проблема. Например, есть последовательность действий, в которой важны внешние показания датчиков,текущие операции, время операций. Программа написана так, что эта последовательность ей самой и порождается в зависимости от ситуации (и набора ситуаций). Это напоминает, как человек из здравого смысла пытается не по шаблону решить задачу. То есть, самих ситуаций может быть сильно дофига (я их даже не уверен, что смогу все перечислить). Сейчас я не могу проверить, что все возможные случаи программа отрабатывает правильно.
В примерах этих самых тестов всегда тестируют некий примитив с элементарными действиями, но по факту на практике всегда имеется сложная логическая цепочка, которую протестировать будет очень непросто.

Цитировать
Unit-тесты не имеют никакого значения если вы не планируете развивать код, не подходите к проблеме через проектирование, или же решение проблемы должно быть найдено в кратчайшие сроки (с минимальным количеством людей).

Дело в том, что после очередного рефакторинга хорошо бы выполнить проверку в автоматическом режиме, что и позволяют Unit-тесты. :) А том получается так, что я кардинально перестроил отношения классов и теперь не уверен, что не забыл в каком-либо классе о каком-либо нюансе.