Untitled

НикнеймzHz (а также zHz00, zHz01 и так далее)
О чём пишубыт, аниме, программирование, компьютерная техника
(полный список тегов -- что теги обозначают)
Интересненькоесписок моих статей с разбивкой по темам
Где меня ещё читать Telegram
Как со мной связатьсяTelegram, e-mail, Jabber: zhz@jabber.ru,
Discord: zHz#1243
Как дать мне денегBitcoin

Что тут можно и нельзя (читать правила полностью):
1. Комментировать можно всем.
2. Читать всё можно всем.
3. Раскрывать чьи-либо личные данные нельзя.
4. Нарушать правила @дневников и законы РФ нельзя.
5. Если в записи есть ссылки "<<" или ">>" -- то эти ссылки ведут на связанные записи в цепочке.
6. Если навести мышку на зелёный текст, будет сюрприз.
URL
Записи с темой: Говнокод (28)
понедельник, 18 августа 2025
00:47 "Административное" решение проблем в ПО
Я называю "административным" методом две совершенно несвязанные вещи. Наверное, для этих методов есть какие-то официальные названия. Если знаете — сообщите.

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

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

2. Вместо изменения алгоритма делается пометка в руководстве: при возникновении такой-то ситуации пользователь должен делать то-то.

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

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

Эти два метода имеют абсолютно разную направленность, но у них есть сходство: перекладывание ответственности с программиста на других лиц. И если первый метод хорош, потому что он чаще всего улучшает работу ПО (без затрат программиста, лол), то второй метод наоборот — делает ПО менее надёжным за счёт увеличения человеческого фактора. Каждый раз перед применением второго метода надо внимательно думать.

@темы: Программирование, Говнокод

URL
пятница, 06 июня 2025
17:45 Повторный запуск
Может быть, я об этом уже писал? Но повторить (лол) не мешает.

Итак, пилим новую фичу в программе, и вот в первый раз алгоритм отработал без (явных) ошибок. Измерил, что надо, спел, станцевал. Закончена ли работа?

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

Главная причина этого -- изменение стартового состояния. Перед началом выполнения всё, что надо, должно быть занулено, заединичено, очищено или, наоборот, набито нужными данными. Даже если мы следуем школьным правилам инициализации переменных, мы всё равно можем что-нибудь упустить, потому что реальные программы не настолько тривиальны, как int a=0; А ещё -- потому что инициализация может вызываться при старте программы, но не при старте алгоритма -- за этим тоже надо следить.

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

Конечно, этого быть не должно. Поэтому повторные запуски позволяют легко выявить такие ошибки. А дальше можно начинать ловить более мелкие баги.

@темы: Программирование, Говнокод

URL
вторник, 20 мая 2025
02:13 Муляж
Когда заходишь на сайт мошенников, типа фишинга, то там всё выглядит нормально, за исключением нюанса: за декорациями ничего нет. Чем-то похоже на камин Папы Карло.

Ну и вот, у меня две программы, которые обмениваются друг с другом по определённому протоколу. В основном, данные надо отправлять от Алисы к Бобу, но иногда надо и получать. Для отправки существует функция типа SettingsSet, которая получает идентификатор "переменной" и число. Для получения есть несколько функций типа GetA, GetB, GetC, и общая, SettingsGet.

Об'единить все функции было нельзя, т.к. данные разные по смыслу. Ну и пользовался я своими А, БЭ и ЦЭ спокойно, пока не настал момент, когда мне надо было получить что-то через SettingsGet.

Я удивился, получив ноль.

Поковыряв код, я обнаружил:
а) SettingsGet никогда раньше не использовалась. Данные и так надо было получать редко, а когда было надо, я использовал А, БЭ и ЦЭ.
б) она не только не использовалась. Она ещё и не была написана. Вместо неё была заглушка.

А проекту уже лет пять.

В общем, живу я хорошо, всё стабильно. Пришлось срочно доделывать этот кусок.

@темы: Программирование, Говнокод

URL
понедельник, 23 декабря 2024
04:53 Во входном канале никель, в выходном -- пуговица
(никель -- монета в 5 центов)

Выводил текстовый файл при помощи Си++. Использовал класс ofstream. Неожиданно обнаружил, что файл прерывается досрочно. При этом цикл, который туда пишет, дорабатывает до конца. А в какой момент прерывается вывод?

В общем, у меня был импровизированный ассоциативный массив. Так не делайте, используйте готовый, он в Си++ есть (std::map). Но я сделал свой, с б/дж и ш. Ключ у меня был... целое число. А значение -- текстовая строка (char*, так тоже не делайте, потому что есть std::string). Для поиска по ассоциативному массиву была сделана специальная функция, возвращающая char*.

Проблема была в тех случаях, когда ключ был в массиве не найден. Моя функция возвращала NULL, а точнее, (char*)NULL. Нулевой указатель. Который с удовольствием выводился в файл.

Файловый поток такого издевательства терпеть не мог. Он выставлял сразу биты fail и bad (но не возбуждал исключение!). Я, естественно, не проверял статус выходного потока после каждого вывода. Да и не проверял вообще. А так уж поток устроен, что если ошибку не устранить, весь остальной вывод будет молча с'едаться, что и происходило.

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

@темы: Программирование, Фейлы, Говнокод, Борьба с техникой

URL
вторник, 17 декабря 2024
01:51 Длинные условия
Сколько раз такое было, пишем оператор условия, а там:

if(flag1==True&&flag2==False&&function_call(a,b,c,d)>0)

и что-нибудь ещё.

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

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

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

if(flag1==True&&
flag2==False&&
function_call(a,b,c,d)>0)

А можно написать:

if(flag1==True
&&flag2==False
&&function_call(a,b,c,d)>0)

Я всегда использовал первый вариант, но убедился, что он неудобен. Логическая операция находится в конце строки, её надо постоянно искать глазами. А концы у каждой строки находятся в разном месте. Если расположить оператор в начале, то, во-первых, будет понятнее, что происходит, а во-вторых, можно для удобства дублировать оператор и в конце предыдущей строке, но уже в комментарии, типа //&& .

Но насчёт дублирования я пока не уверен, т.к. никогда так не делал.

3. Есть ещё один метод, назначить каждому условию в выражении отдельную логическую переменную.

bool b1=(flag1==True);
bool b2=(flag2==False);
bool b3=(function_call(a,b,c,d)>0);//скобки необязательны, но пусть будут на всякий случай
if(b1&&b2&&b3)

Какие есть особенности у этого метода?
Во-первых, если удастся дать условиям краткие понятные имена, а не b1, b2, b3, то это действительно упростит читаемость. Если имена будут условными, то упрощение тоже будет условным.
Во-вторых, упрощается отладка, поскольку вы получаете непосредственный доступ к частям логического выражения, а обычно такого доступа нет, т.к. условие выполняется в отладчике как одна строка.

@темы: Программирование, Говнокод

URL
вторник, 03 декабря 2024
01:44 Второй проход
Если при добавлении функционала в программу приходится вносить массовые правки, то у программы плохая архитектура. И тем не менее, массовые правки вносить иногда приходится.

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

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

Одна из проблем, которая может возникнуть -- это поиск всех исправленных мест. Тут есть три варианта:
1. Все места для правок могут быть уже помечены ещё до начала (например, вы исправляете поведение программы при определённом значении какой-либо опции, и сравнение с этим значением есть во всех исправленных местах).
2. Можно вручную помечать все места, куда вносятся правки, но делать это надо было заранее
3. Система контроля версий.

@темы: Программирование, Лайфхак, Говнокод

URL
среда, 06 ноября 2024
05:30 Это не Питон
(но в Питоне тоже можно обосраться похожим образом)

В коде нашёл спящую ошибку. Есть строка типа...

char *str=new char[len];

В эту строку раз за разом копируется разный текст через strcpy

strcpy_s(str,len,src);//src каждый раз разный

Потом эта строка отправляется куда-то на обработку, а после обработки в неё копируется следующая строка. Это происходит не в цикле, а методом китайского кода, т.е. строка за строкой.

Оставим в стороне вопрос о том, почему сразу не отправлять src. Тому есть причина.

Несколько копирований проходит успешно, но во время очередного я получаю access violation. Откуда он там может быть? Я тихо копирую в свою область раз за разом. Может быть, не хватает места?

Но путём отладки я обнаруживаю, что ошибка происходит при копировании в ПЕРВЫЙ байт. А когда это возможно?

Когда пытаешься копировать в const-область или типа того. Но у меня же не константная область? Точнее, не была константная ещё три строчки назад. Хммм...

Поднимаю глаза на предыдущие строки. А там примерно следующее:

if(very_rare_option)
{
str="Very rare string";
}

Много лет редкий флаг не выставлялся, поэтому строчка не выполнялась. Но я выставил этот флаг. В результате произошло присваивание. Но в Си++ строки таким образом не присваиваются. Тип правого выражения это const char *, а тип левого -- char *. Я присвоил константный указатель на строку и успешно с ним поработал. Но при попытке записи я стал записывать не по старому адресу, на который была выделена память, а по адресу &"Very rare string", который теперь содержался в str. А там запрещённая для записи область...

Естественно, пришлось переписать:

strcpy_s(str,len,"Very rare string");

И проблема исчезла.

@темы: Программирование, Фейлы, Говнокод

URL
воскресенье, 06 октября 2024
07:09 Я еще послушник и могу ругаться сколько захочу
Попробовал библиотеку curses. Широко известно, что если текстовая программа выглядит симпатично, то она была сделана на curses.

Так вот. Я, возможно, не до конца разобрался, но пока я не нашёл в библиотеке встроенных средств для создания кнопок, полей ввода и т.п. То есть, библиотека уступает аналогичному TurboVision (кто помнит такой? я на нём не писал, но результаты его работы видел предостаточно).

Но ладно. Я нашёл способ написать то, что мне надо. А поразил меня следующий момент.

В этой библиотеке повсеместно при задании координат сначала задаётся ИГРЕК, а потом икс!

@темы: Программирование, Говнокод, Борьба с техникой

URL
четверг, 05 сентября 2024
06:13 Невнятные сообщения об ошибках
Примеров таких ошибок я приводил множество, но недавно до меня дошло, что это системная проблема, и даже появилось подозрение, с чем это связано.

Впервые я встретился с описанием такой ошибки в знаменитой книжке П. Нортона Programmer's Guide to the IBM PC. В русском переводе она имела пространное название "Персональный компьютер фирмы IBM и операционная система MS-DOS". В одной из таблиц с кодами ошибок была следующая запись:

(номер кода ошибки): "Неверный формат (а чего, не говорится)".

Книжку читал я в детстве, не имея на руках ещё никакого компьютера. Меня тогда это насмешило.

Сейчас-то мне не очень смешно!

Так вот, кода ошибки недостаточно для понимания происходящего. Часто нужно указание на имя файла, на какое-то конкретное число и т.п.

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

Казалось бы, у нас есть более современный механизм: исключения. Когда возбуждается исключение, можно сделать целый об'ект и засунуть в него всё что пожелаешь. А если об'ект наследован от какого-нибудь базового CException, то там наверняка есть какой-нибудь GetAsStr(), куда можно всё записать.

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

Что делать? Писать логи. В момент передачи кода ошибки/исключения программа точно знает, что пошло не так. Пусть выводит сообщение с контекстом ещё до того, как вернуть код ошибки/возбудить исключение. Пока ещё всё известно.

@темы: Программирование, Говнокод

URL
четверг, 13 июня 2024
04:51 ТЗ нечёткое, но виноват сам
Надо было сделать многократное измерение. Сколько максимум раз может пользователь захотеть его сделать? Неизвестно. Сотня точно. Может и больше.

Но как это тестировать? Это пользователь может мерить по своему желанию. Мне запустить замер на много часов или даже несколько дней -- сложнее.

10 итераций проверил. 50 итераций проверил. 100... не помню, проверил или нет.

Пользователь запустил 200. А потом пишет -- а почему у меня количество измерений отрицательным отображается?

Читаю логи. 126 измерений... 127 измерений... -128 измерений...

Это такая классика, что у меня олдскулы свело. Программа, отображающая ход измерений и программа, непосредственно проводящая измерения -- разные вещи. Они обмениваются пакетами. Формат пакетов задаю я сам.

И вот почему-то для общего числа итераций я использовал тип int32_t (4 байта), а для номера текущей итерации -- int8_t (1 байт). При этом в микроконтроллере номер итерации хранится 32-битный. Это он только наружу выходит в урезанном виде.

Конечно же, восьмибитное знаковое число будет "оборачиваться" в минус после 127...

@темы: Программирование, Фейлы, Говнокод, Борьба с техникой

URL
суббота, 08 июня 2024
02:05 Для чего, по-вашему, нужна иконка?
Интернет-магазин, товар. Под товаром зелёная галочка. Я такой -- отлично.

Читаю текст рядом с зелёной галочкой: "Нет в наличии".

*волк крутит пальцем у виска*

А если есть в наличии, то какой значок?

Проверил: тоже зелёная галочка.

@темы: Наблюдения, Говнокод

URL
воскресенье, 02 июня 2024
04:04 Ещё раз про знак "меньше" на diary.ru
Пару недель назад я писал о проблемах со знаком "меньше". В том посте я очень гордился тем, что нашёл способ отображения этого знака без проблем. И даже собрал четыре лайка, что очень интересно с учётом того, что я обнаружил позднее.

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

Я не буду описывать процесс расследования, а перейду сразу к выводам.

Правила отображения знака меньше и html-подстановок, о которых я писал в прошлом посте, зависят от настройки: Настройки -- Картинки (аватары, смайлы) -- Показывать или нет графические изображения смайликов.

При этом правила такие.

1. Если у пользователя опция ВКЛЮЧЕНА, т.е. текстовые смайлики заменяются на графические, то HTML-подстановки НЕ ЗАМЕНЯЮТСЯ. Вы можете спокойно писать &lt; в тексте поста. Эта подстановка "как есть" будет передана в браузер, а он её отобразит как знак "<".

2. Если у пользователя опция ВЫКЛЮЧЕНА, то текстовые смайлики заменены не будут. Вместо этого БУДУТ заменены HTML-подстановки! В этом случае писать их напрямую уже нельзя, а надо использовать тот метод, что я описывал в прошлом посте. Иначе движок дайари сам раскроет подстановку и отправит знак "<" в браузер, что недопустимо, и может быть отображено некорректно.

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

У меня, естественно, опция была выключена (вариант №2), поэтому-то я и знал о проблеме. Но проблема эта была только моя. Я уверен, что большинство пользователей, включая анонимов, имеют включённую опцию. Поэтому-то мне и интересно, что за четыре человека поставили лайк. У них, значит, опция тоже отключена, поэтому они увидели текст поста так, как я задумал?

Теперь надо понять, что делать. Я, конечно же, хочу спокойно писать знаки меньше и больше, и чтобы все их видели правильно. Но пока что не понимаю, как это сделать. Возможно, придётся воспользоваться методом CD_Eater'а, который оборачивает эти знаки в тег span, вот так:<span><</span>. Это нарушает правила вёрстки, но даёт предсказуемый результат: знаки отображаются корректно. Теперь надо проверить, что это одинаково работает во всех дизайнах и при всех настройках.

@темы: Говнокод, Борьба с техникой

URL
пятница, 31 мая 2024
03:00 Учебный код и серьёзный код
Все мы когда-то писали кривой код на лабах по программированию. А кто-то такой код пишет до сих пор по работе.

Учебный код должен выполнять то задание, которое написано на листочке, и его он должен выполнять без ошибок. Если при разрешённых входных данных получается чушь, это должно быть исправлено.

Но что делать с запрещёнными входными данными? Я полагаю, что учебные программы имеют право работать некорректно, если пользователь попрыгал по клавиатуре или нажал кнопки не в том порядке. Потому что смысл упражнения не в том, чтобы сделать законченный продукт. Задача лаб в том, чтобы студент понял ту или иную концепцию и применил её на практике.

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

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

Серьёзный код -- это такой, который корректно отрабатывает все разрешённые взаимодействия с пользователем. Программа может падать, если пользователь покопался в настроечных файлах -- это он сам виноват. Но программа не может падать, если пользователь не там пощёлкал мышкой.

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

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

@темы: Программирование, Мысли, Студенты, Говнокод

URL
понедельник, 27 мая 2024
03:22 Хориков Владимир // Принципы юнит-тестирования (2021)
Это, скорее, не обзор на книжку, а нытьё о моих проблемах.

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

Что я делал раньше? Я прогонял все скрипты, а потом сравнивал выходную папку с файлами и "эталонную", полученную с предыдущей версией скриптов. Сравнением занимался WinMerge. Если я видел разницу, я начинал выяснение -- должна была эта разница появиться или это ошибка.

Такой подход довольно затратен.

Исключением был модуль по работе с БД, db.py. Хотя у него и не было "юнит-тестирования", но у него был "генеральный прогон", вызывавший все функции модуля по очереди с тестовыми данными. Данные не проверялись, просто результаты выводились на экран. Я сэкономил немало сил и времени постоянно запуская этот прогон после модификаций. Он быстрее ловил ошибки в SQL и в питон-синтаксисе, чем если бы я каждый раз проводил полное преобразование дневника.

Тестирования модуля БД было мало. Мне была нужна уверенность в конечном результате. Тем не менее, я решил начать именно с модуля БД, превратив "генеральный прогон" в серию тестов. Я написал несколько первых тестов для py.test, разобрался что к чему, а потом понял, что у меня есть философские вопросы о том, как же следует делать тесты. И практические вопросы -- как делать принято?

В интернете есть достаточное количество статей о юнит-тестировании для начинающих. Их об'единяет общая проблема. Они показывают тестирование на примере функции сложения двух чисел. Как же приложить такое тестирование к более сложной системе?

За ответами на этот вопрос я обратился к данной книжке и не ошибся.

Книжка короткая, всего 300 страниц. Информации в ней мало, и она довольно простая. Но подана она систематически. Всего десяток тезисов, каждому из которых дано подробное обоснование, и приведены примеры -- сложнее, чем сложение двух чисел. После чтения у меня в голове образовался каркас знаний по теме. И я могу его применять.

Настолько вменяемых книжек существует совсем немного.

Кроме того, я нашёл в книжке подтверждение догадок, которые у меня появились, когда я пытался написать тесты. А ещё я нашёл ответы на все мои вопросы, которые у меня возникли до чтения.

И главный вопрос у меня был такой: почему для модуля БД тесты написались практически сами собой, а для остальных модулей я даже понять не могу, как к этому подступиться? Почему я не понимаю, куда приложить тесты к коду в проектах по работе?

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

Скрипты по скачиванию дайари проще. Но они всё равно по классификации автора относятся к "переусложнённому коду". Моя 500-строчная функция download() из download.py делает всё. А должна делать только одно действие. Модуль БД, напротив, содержит десятки мелких функций, каждая из которых делает простую вещь. Поэтому-то этот модуль так легко тестируется.

В общем, юнит-тестирование во многом завязано на архитектуру. И архитектурным вопросам автор посвятил, наверное, треть книги.

И, возможно, эта треть важнее, чем сами тесты и тестирование вообще.

@темы: Книги, Программирование, Говнокод

URL
вторник, 09 апреля 2024
02:46 Дайте две
Небольшая разгадка, которую я записываю скорее для себя.

Устройство измеряло данные с частотой 100 Гц, но я на графиках обнаруживал, что фактически значения изменялись с частотой только 5 Гц. Это меня, в принципе, устраивало. Я мог заняться поиском причины, но этот косяк выглядел очень бесперспективно.

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

Однажды я листал код в поисках другого косяка с этим же устройством и решил вспомнить, какой же конкретно запрос устройству я посылаю. И увидел, что я прошу устройство прислать сразу 20 значений.

Одно значение должно приходить 100 раз в секунду, т.е. каждые 0.01 сек. ЕСТЕСТВЕННО, ожидание 20 значений затянется на 0.01*20=0.2 сек. А это и есть 5 Гц.

@темы: Программирование, Говнокод, Борьба с техникой

URL
вторник, 12 марта 2024
02:12 Одно неудобство в яндекс-картах
И это неудобство: режимы плохо совмещаются между собой.

Я включил панорамы. Теперь я не могу кликать на значках организаций. Я не могу включить линейку. Чтобы включить линейку, надо сначала ВЫКЛЮЧИТЬ панорамы.

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

При этом режимы пробок и транспорта с другими совмещаются хорошо.

Я уверен, что эти нестыковки имеют архитектурные причины. Например, панорамы изначально были реализованы как отдельный проект и не предусматривали взаимодействия с об'ектами других слоёв. Но пробки-то предусматривали! И это не изменяет того, что приходится делать лишние переключения слоёв и инструментов.

@темы: Говнокод, Борьба с техникой

URL
суббота, 10 февраля 2024
01:08 Ещё один спящий баг
У наших программ есть окно с настройками. Оно довольно типично для устаревших интерфейсов: слева располагается дерево страниц. В зависимости от того, какой лепесток дерева выбран, формируется страница настроек справа.

По умолчанию, естественно, открывается первый лепесток.

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

ОКАЗАЛОСЬ, что при открытии диалогового окна сначала происходит рендеринг всех настроек, и только потом численные настройки подгружаются из настроечного файла. Таким образом, на странице отображаются устаревшие значения.

Много лет мы этого не замечали. Почему? По двум причинам.

1. На первой странице никогда не было численных полей. Только чекбоксы, комбо-боксы и подобное. А они, в отличие от численных настроек, подгружались ДО рендеринга. Это, конечно, был для меня полный взрыв мозга -- половина настроек подгружается до, а половина после!

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

@темы: Программирование, Говнокод, Борьба с техникой

URL
среда, 10 января 2024
05:11 Надо было кнопки сделать не по порядку
Ещё одно воспоминание из командировки -- посещение местной Ленты. Я не знаю, как дела обстоят в других Лентах, потому что я в них достаточно редко бываю, а когда бываю -- не пользуюсь тем, что покажу ниже.

Короче, терминал взвешивания. Посмотрите, как он сделан там. И больше так не делайте.



Ну кто так делает? Если я сразу знаю номер, то зачем мне выбирать по десяткам, ведь можно сразу ввести трёхзначное число на нумпаде? Может показаться, что экономится один клик. Но эта экономия уходит на поиск нужной кнопки для десятков. (не удивлюсь, если каждая кнопка нарисована вручную, а не сгенерирована в цикле).

@темы: Восприятие, Говнокод

URL
пятница, 15 сентября 2023
04:47 Лишняя строчка
Поступает от пользователя жалоба, что не работает одно из измерений. Открываю исходный код в нужном месте. Вижу там такое:

/*if(module_installed)
{
//...
}*/
if(module_installed)
//так, это я отключаю
if(data_value>data_max)
{
//...
}

Отступы были везде одинаковые. Но это была лишь видимость. Если приглядеться, то становится видно, что иф, который про данные, вложен в иф, который про модуль.

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

Раньше жалоб не было, потому что модуль всегда был установлен, и условие всегда проходило. Но в этот раз модуль был отключён. И вот результат.

Но эта ошибка -- не ошибка выставления отступов и не ошибка пропущенного кода. Наоборот. Покопавшись в репозитории я выяснил, что один из блоков кода был закомментирован за ненадобностью (здесь он приведён первым). Но его первую строчку я почему-то продублировал сразу после комментария.

@темы: Говнокод, Борьба с техникой

URL
четверг, 24 августа 2023
04:38 Ещё пара слов о конечных автоматах
Кто не помнит, что такое конечный автомат, отсылаю сюда: diary.ru/~zHz00/p221557637_vozvrawenie-k-bejsik...

В общем, начальник написал конечный автомат. Но начальник не знал, что это был именно он. Поэтому вместо номера состояния у него был набор флагов. В зависимости от комбинации флагов выполнялся либо один кусок кода, либо другой, либо третий, и т.д.

Ну, во-первых, так делать не надо. Это затрудняет понимание написанного. Пока флагов всего 2-3, и они имеют понятный смысл -- ещё можно разобраться. Но когда флагов хотя бы пять -- уже начинаются проблемы. У меня. Потому что разбирать этот код потом пришлось мне.

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

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

Как же куски кода были размещены у начальника? Они были размещены в обратном порядке. Ушло некоторое время на то, чтобы понять, что первым в тексте указан алгоритм последнего состояния, потом предпоследнего, и т.п.

@темы: Программирование, Говнокод

URL