Telegram Web Link
Всё-таки у Typescript есть большой плюс - от него сразу не ждешь ничего хорошего.
😁37👍6👎2
.
😁38👍3
После недели на typescript
😁32👍4🔥2💩1
наши капчи
😁44👍7🔥1
MS добавил GPT в teams, теперь meeting notes генерятся автоматически.

Работает отлично, по крайней мере для английского языка (причём представьте современный IT митинг, сколько разных акцентов у спикеров).
👍18🔥1
Купил себе планшет для тестирования тач-интерфейсов и вспомнил, почему этот формат устройства мне совершенно не зашёл

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

- до сих пор никто так и не придумал вменяемый способ ввода для планшетов в landscape-режиме

- 8" планшет это чуть больше, чем современный телефон, а 12" - уже почти компьютер

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

Тем не менее, если приличный планшет стоит 200-300$, но лидеры рынка при этом выпускают монстров с кучей ядер и 16гб оперативки за 10х ценник - значит это наверное кому-нибудь нужно?
👍12😁3👎2
Да, напоминаю, что вешать планшеты на стены, встраивать в тумбы и прочее, где оно 24/7 на зарядке - верный способ попытаться получить денег по страховке от пожара.

На питание вешается что-угодно-тачскрин без батареи, или с такой батареей которая по плану умеет работать как ИБП.
🔥30👍6
.
😁56🔥6👍1
Балк-инсерты в базы часто делают следующим образом: открыл транзакцию, наплевал инсертов, закоммитил.

Кроме того, что это медленнее, чем один балк-запрос, это практически не работает, если база и клиент физически далеко друг от друга - пока вы там будете каждый INSERT заворачивать в отдельный TCP и еще ждать, что ответит вам база - уже все вменяемые сроки пройдут.

Идеально - делать один балк-запрос, причем в PostgreSQL это лучше всего делать массивами. Запрос вида

INSERT INTO tbl(names) VALUES (UNNEST(ARRAY(['vasya', 'petya'])));

сделает свое дело, без необходимости городить кучу биндингов со стороны клиента, в Rust, как правило, можно биндить на такое сразу массив или вектор. Бонус: ON CONFLICT в таком запросе решается индивидуально для каждой вставки.

Дополнительно, PostgreSQL конечно же поддерживает и параметры процедур/функций в виде массивов, принцип тот же - биндим вектор на PL/PGSQL функцию, а она уже внутри базы разбирается, что там куда вставить, при необходимости делая данным обработку.
👍22🔥1
После вчерашнего поста о вставках данных ко мне стукнулся коллега (вы наверное догадываетесь, кто), спросил в чём полная задача и предложил некоторые оптимизации.

Опытным путем было установлено следующее (утрированно):

Представьте у нас есть две таблицы tags(id, name) и events(t timestamp, tag_id, value). И некая функция базы push_events(times[], tag_names[], vals[]), которая вставляет события балком. Если тега события нет в tags, то мы его должны сначала вставить. Клиент биндит на функцию сразу векторы.

Вариант изначальный - делаем LOOP по данным, получаем tag_id (находим или вставляем), делаем вставку в events для каждой строчки (мы в транзакции).

Вариант второй - делаем тоже самое, но вставляем сначала всё в temporary table а потом делаем INSERT SELECT. Дает прирост, по сравнению с изначальным от 1 до 30-35%, в зависимости от состояния WAL.

Вариант третий - делаем LOOP, но не вставляем tag_id никуда, а складываем его в отдельный массив. В конце функции делаем INSERT/UNNEST всех массивов. Дает прирост в 40-50% от изначального.

Временная таблица в последнем варианте не требуется, ее использование снижает производительность на 5-10%. INSERT UNNEST с массивами - победил.

Тестировалось на PostgreSQL 12, в качестве клиента был Rust с SQLx 0.7, каждый вариант прошел 10 млн итераций, группами по 10 тысяч событий.
👍33
Продолжаю рассказывать про внутренности Beckhoff TwinCAT 3.

TwinCAT поддерживает два типа строк: STRING - ASCII и WSTRING - UTF16 (little-endian). Оба типа работают по принципу VARCHAR[N], т.е. имеют фиксированную capacity, но динамическую длину. В Structured Text для TwinCAT для них используются одинарные и двойные кавычки, соответственно.

Строка всегда заканчивается NULL-терминатором (UTF16 - двойным). Если вы обьявляете строку как S: STRING(10); по факту на нее будет выделено 11 байт (для WSTRING, соответственно, 22).

Логика чтения и записи строк со стороны ADS-клиента отличается от остальных типов переменных - строка в TwinCAT по сути является массивом из символов, что мы собственно и читаем, но склеиваем пользователю в строчку (или делаем строчке to_bytes(), когда пишем).

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

Из-за этого даже родные продукты Beckhoff работают с массивами строк с некоторыми ограничениями. Впрочем, если из массивов нужно брать только строки по одинарным индексам, такой проблемы нет, PLC найдет вам нужный элемент сам.
👍3🔥2
Как в TwinCAT работать с массивами (изнутри).

Изнутри с массивами в TwinCAT работать всегда ад. Это вам не Ethernet/IP, который у каждого символа имеет приписку, массив ли он, а если массив то какой. Я по этому поводу долго общался с инженерами Beckhoff и они мне постоянно советовали только один проверенный способ - если переменная массив, то у нее длина больше, чем длина типа, а размер массива соотв. size/type_size. До поры-до времени это работает, пока вам не встречаются многомерные массивы или массив из одного элемента. В последнем случае TwinCAT - опять же не En/IP, педантичные немцы просто не разрешают адресовать var как var[0], если она не массив, а вы имеете кучу проблем, особенно если филд-инженеры пишут код не руками, а используют ПЛК-выгрузку из того же Simulink - такие автоматы очень любят "оптимизировать" код и превращать массивы в одиночные переменные.

Чтобы очень грубо определить, что переменная является массивом, можно просто заюзать функцию ADS API SUMUP_READ_EX (0xF083). Она не вернет вам ничего полезного про переменную, на поле legacy array dimensions даже не надейтесь - оно в TwinCAT 3 на моей памяти всегда имело нули, но зато вернет декларацию переменной, в таком виде, в каком она была в коде, но более-менее стандартизировано - без лишних пробелов и сразу апперкейсом. Тоесть если инженер задал ARRAY [0..5] OF DINT - вы это и получите. Дальше if declaration.startswith("ARRAY" ) - мы хотя бы определили, что это массив, уже хорошо.

Более корректный способ - cгрузить всю таблицу переменных и типов с плк через SYM_UPLOAD_INFO2, SYM_DT_UPLOAD и SYM_UPLOAD (как это делает Rust-клиент https://docs.rs/ads/latest/src/ads/symbol.rs.html#198, правда таблицы у него неудобные, лучше потом сконвертить поля в HashMap/BTreeMap). А типизация в TwinCAT устроена следующим образом:

A: ARRAY [0..5] OF MyType;

создаст в таблице типов две записи (для стандартных типов они тоже есть). Первая - собственно MyType, которая описывает ваш тип, его поля, если это структура и т.д. Вторая - ARRAY [0..5] of MyType, да, для каждого массива компилятор создает отдельный тип. Далее, опять же, вам придётся немножко вручную пропарсить тип переменной, откусив MyType и узнав из таблицы символов что оно вообще такое, а затем - найти в таблице полный тип массива и наконец (ура) найти его четкие размеры.

На этом пиздец с одномерными массивами заканчивается, а с матрицами только начинается. https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/8825255691.html&id= - StructuredText в TwinCAT позволяет задавать матрицы как ARRAY[x..y,x2..y2 ...], в этом случае клиент работает с индексами вида var[x,y]. Так и как "ARRAY [x..y] OF ARRAY [x2..y2] OF ..." - в данном случае элементы адресуются уже как var[x][y]. Во втором случае вы попали чуть больше - вам придётся отдельно прогнать "ARRAY [x..y] OF ARRAY [x2..y2] OF ..." и "ARRAY [x2..y2] OF ..." по таблице символов и наконец определить размеры каждого.

Как это всё решается на практике? Опять же, поскольку производитель сам не знает, что с этим делать, единого стандарта нет. В основном для одномерных массивов используется комбинация ARRAY в definition + вычисление размера руками, а на универсальную мультимерность просто забивают.
👍6💩3
Сегодня будем говорить про аудио-ембед. Потому что постучался ко мне коллега и попросил консультацию - он мол купил студийную звуковуху за 700$ и к ней еще Word Clock от Mutec за 1500, и вот этот Word Clock почему-то нифига ему звук не улучшает, или ему кажется что не улучшает.

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

Вернемся к Word Clock, или как в народе - часы. Если у вас есть центральный юнит (звуковуха) и к ней подключены (все условия схемы - обязательно) цифровые устройства (у аналоговых такой проблемы нет - они условно реалтаймовые), больше одного(!), по протоколам без обратной связи (SPDIF, ADAT, что-угодно), то в генерации звука может случиться рассинхрон, потому что часы у всех участников далеко не атомные. Еще раз - без обратной связи, потому что современные устройства вешаются как правило сразу на USB (еще начиная со старого доброго первого цифрового Access Virus TI, который уже дедушка), а по USB из компа можно пнуть по голове каждого, кто фальшивит, главное чтоб драйвер умел. В качестве примера можно взять известную серию Yamaha Motif, не знаю как сейчас, раньше там была довольно долбанутая схема подключения по USB, где она делала в системе свой непонятный с чем есть usb audio device, поэтому ее от греха все вешали на SPDIF.

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

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

Я понимаю, люди сейчас богатые. Но домашняя студия всё же не Венская опера.
👍17🔥7😁3
Наши будни автоматизации
- вот ваш доступ на наш режимный объект
- я залогинился
- подождите, мы попросим наших админов включить интернет наружу
- я уже сам включил
- ???
- я думал, вы просто днс прописать забыли...
😁47👍1
Проведя несколько недель на нашем фронте, обнаружил следующую типичную ситуацию:

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

- практически полное игнорирование тестирования на реальных тачскринах, зачем если в браузере есть теперь viewport под ипад? в результате на тачскрины изо всех дыр лезет тот же hover, который на них остаётся как минимум до следующего нажатия

- слишком большие надежды на фреймворки, из предыдущего - в том же любимом SASS media(tablet) - это только размер экрана (примерный), а не полное media под устройства с тачем и без мышки

- попытки каждую мелочь делать библиотеками. "зачем тут такой сложный код? такое бывает только в библиотеках". действительно, а библиотеки пишут марсиане

- абсолютное непонимание, как работает JS, почему для реактовского useEffect [1,2,3] и [1,2,3] - два разных состояния, если они находятся в разных переменных

- постоянные попытки убить UX в угоду UI. "ну и что что тут таблица на 100500 столбцов, давайте сделаем побольше padding по бокам, зато будет красиво"

А потом мы это едим.
👍30👎1
На промышленных дашбордах я собаку съел. Вот некоторые моменты по UX.

- Дашборд будут смотреть на компьютере. В редких случаях - на планшете. Почти никогда - на телефоне. Под телефон следует делать, просто чтобы показать что "он есть". Никто и никогда не будет им пользоваться в проде

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

- UX, исходя из предыдущего - дашборд не будут листать. Поэтому банально и просто, если данные горизонтальные - освободите горизонталь. Если вертикальные - вертикаль. Никаких лишних паддингов, менюшек. В горизонтальных - меню сверху. В вертикальных - сбоку

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

- Кастомные дашборды - отдельная тема. Их будут смотреть на нескольких устройствах. Скорее всего вообще на одном. Вы свою графану или жаббикс часто даете другу посмотреть? Вот именно, даже их не даете. Не нужно придумывать велосипеды, посмотрите на больших игроков - они фиксируют viewport гвоздями и ничего резинового там нет в помине. А гвозди позволяют, опять же, засунуть максимум данных на квадратный пиксель
👍32💩1
Таблицы я люблю. Даже был автором довольно популярного под питон крейта (или как там оно называется) rapidtables. К растовскому prettytable у меня претензий (пока) нет.

Формирование таблицы, не зависимо от способа вывода, имеет два алгоритма и там нет никакого rocket science: если данные конечные - два прохода, в первый вычисление лейаута, во второй наполнение. если данные бесконечные (поток) - обработка сугубо построчно (бывает криво, зато бесконечно). можно комбинировать (берем в потоке 10-20к первых элементов и прикидываем лейаут).

В гуях это всё немного отличается тем, что отдав таблице данные мы подразумеваем, что она ведет некий интерактив с пользователем, например позволяет ему произвольно сортировать данные по столбцам. И вот тут у меня большая претензия, особенно к таблицеписакам под веб - отображение данных не всегда совпадает с их весом и сортируются такие таблицы совершенно бредово. Простой пример: уровни лога debug,info,warn,err будут пересортированы как debug,err,info,warn - потому что по алфавиту, а не как положено. С датами тоже - если там ISO, то туда-сюда, а вот если месяц как October, то приехали (то что сравнивать строки намного тяжелее чем timestamps я уже молчу).

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

Неужели это так сложно?
👍16🔥3💩1
Наши будни автоматизации
- мы хотим оптимизировать базу
- у вас и так все хорошо. 8 байт на таймстемп события, 8 байт на float64. миллиард событий у вас занимает 20 гигабайт. значит на одно событие 16 байт и ещё 4 жрёт база
- мы хотим уменьшить
- теоретически у вас можно перейти на float32, с таймстемпом подумать...
- сколько это даст?
- раза в 2 уменьшить попробовать можно
- мы хотим в 10
- это нереально
- ну ты же умный, хоть в 5, а...
😁40👍1💩1
Кто там про сжатие данных упражняется в комментах, сразу обламаю

- этот вольтметр показывает до 100 kV, нам нужно снимать данные до 5 знаков после запятой
- вы что? у него погрешность 5-10 вольт
- надо! есть циркуляр

Само собой, с точки зрения данных это уже генератор случайных чисел
😁22💩4👍2
bun - очень быстрый менеджер пакетов под жявоскрипт (и какой-то там рантайм тоже есть), рекомендую.

Судя по его скорости, написан явно не на JS. Думал на Rust, почти угадал - на Zig.
👍12😁5
2025/09/16 13:10:32
Back to Top
HTML Embed Code: