Страницы

Поиск по вопросам

четверг, 11 октября 2018 г.

Как связать несколько ImageView в ScrollView или WebView? - Android

Нужно реализовать, как мне кажется, довольно простую задачу, однако решения, которое меня бы устраивало я не могу найти уже несколько дней. Суть такова. Есть 4 картинки которые должны скролиться вместе. Основная картинка (в нижнем правом углу) должна скролиться во всех направлениях (x, y и по диагонали). Картинка сверху от нее должна скролиться вместе с ней по горизонтали. Картинка слева от нее - вместе с ней по вертикали. Ну и картинка в левом верхнем - просто картинка, которая скролиться не должна. Ниже приведу 2 варианта решения которые я рассматривал, но не смог их реализовать так, что бы меня это устраивало. 1 вариант
Разместить ImageView в ScrollView и связать их перемещение в setOnTouchListener:

//просто картинка

//картинка слева


//картинка сверху



//основная картинка




public class Gost8509 extends Activity implements View.OnTouchListener {
HorizontalScrollView hScroll1, hScroll2; VScrollView vScroll1; ScrollView vScroll2; float mx, my, curX, curY; private boolean started = false;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.gost8509);
hScroll1 = (HorizontalScrollView) findViewById(R.id.scrollHorizontal1); hScroll1.setOnTouchListener(this); hScroll2 = (HorizontalScrollView) findViewById(R.id.scrollHorizontal2); hScroll2.setOnTouchListener(this); vScroll1 = (VScrollView) findViewById(R.id.scrollVertical1); vScroll1.setOnTouchListener(this); vScroll2 = (ScrollView) findViewById(R.id.scrollVertical2); vScroll2.setOnTouchListener(this); vScroll1.sv = hScroll1; }
@Override public boolean onTouch(View v, MotionEvent event) { curX = event.getX(); curY = event.getY(); int x = (int) (mx - curX); int y = (int) (my - curY); switch (event.getAction()) { case MotionEvent.ACTION_MOVE: if (started) { vScroll1.scrollBy(0, y); vScroll2.scrollBy(0, y); hScroll1.scrollBy(x, 0); hScroll2.scrollBy(x, 0); } else { started = true; } mx = curX; my = curY; break; case MotionEvent.ACTION_UP: vScroll1.scrollBy(0, y); vScroll2.scrollBy(0, y); hScroll1.scrollBy(x, 0); hScroll2.scrollBy(x, 0); started = false; break; } return true; } }
При этом способе у меня перестает работать "бросок" (что естественно) и основная загвоздка в том, что если основная картинка очень больших размеров, то она перестает отображаться в эмуляторе.
2 способ Расположить все картинки в WebView:












В этом случае все картинки отображаются без проблем, но если их связать таким образом как в первом варианте, то картинка при скролле уползает за границы экрана) и опять естественно не работает "бросок".
А теперь вопрос ) Как реализовать первый способ, что бы большие картинки отображались на устройстве и работал бросок?
или
Как реализовать второй способ, что бы работал бросок и картинка в WebView не "убегала"?
или
Может есть какой-то более рациональный и логичный способ реализовать то, что мне нужно?
Заранее спасибо за все ответы и за понимание!


Ответ

Нашел кое-какое решение. Может не очень красивое, но мне помогло, может кому пригодится:
relLayout.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() { @Override public void onScrollChanged() { if (webview1.isFocused()) { webview3.setScrollX(webview1.getScrollX()); } if (webview2.isFocused()) { webview3.setScrollY(webview2.getScrollY()); } if (webview3.isFocused()) { webview1.setScrollX(webview3.getScrollX()); webview2.setScrollY(webview3.getScrollY()); } } });
Таким образом получается, что прокрутки всех 3-х WebView связаны между собой.

Скрипт некорректно работает в браузере Internet Explorer. Как исправить?

Скрипт некорректно работает в браузере Internet Explorer. Во время прокрутки происходит сильное дерганье блока то вверх то вниз. Как исправить?
$(function() { var $hor = $("#horizontal"); $("body").css('padding-bottom', $(window).width()*2); var delta = 0; $(window).on('scroll', function () { var top = $(document).scrollTop(); var width = $(window).width(); var lim = $hor.position().top - (delta) - ($(window).height() - $hor.outerHeight()) / 2; delta = Math.min(Math.max(top - lim, 0), width * 2); $(".horizontal:first", $hor).css({left : delta}); $(".horizontal:last", $hor).css({left : -(width*2 - delta)}); $("body").css({'padding-top': delta, 'padding-bottom': width*2 - delta}); }); }); p { height: 500px; } #horizontal { position: relative; overflow: hidden; width: 100%; font-size: 3em; margin: 0; padding: 0; height: 250px; } #horizontal .horizontal { position: absolute; width: 100%; left: -100%; padding: 20px; } #horizontal .horizontal .h_blockquote { position: relative; width: 100%; margin: 0 auto; font-size: 24px; line-height: 1.3em; color: #000; }

test

Скрипт некорректно работает в браузере Internet Explorer. Как исправить? Скрипт некорректно работает в браузере Internet Explorer. Как исправить?
Скрипт некорректно работает в браузере Internet Explorer. Как исправить? Скрипт некорректно работает в браузере Internet Explorer. Как исправить?

test



Ответ

Исправить нельзя, потому что вначале происходит движение по event scroll и только потом срабатывает функция на scroll. (Вначале страница поднемается, а потом добавляется padding к bady). Как вариант, отлавливать прокрутку ролика мышки: (в коде надо только правельно выставить границы)
var boo = true; var delta = 10; $("body").css('height', "6000px"); var $hor = $("#horizontal"); $(document).ready(function() { $('body').bind('mousewheel', function(e) { var width = $(window).width(); if (e.originalEvent.wheelDelta / 120 > 0) { boo = false; $("body").css("overflow", "inherit"); if ($(document).scrollTop() < 250 && !boo && (delta > 0 - $(".h_blockquote_wrap").width())) { $("body").css("overflow", "hidden"); delta -= 10; $(".horizontal").css({ left: delta + "px" }); if (delta < 0 - $(".h_blockquote_wrap").width()) { $("body").css("overflow", "inherit"); boo = true; } } } else { boo = true; $("body").css("overflow", "inherit"); if ($(document).scrollTop() > 250 && boo && (delta < $("#horizontal").width())) { $("body").css("overflow", "hidden"); delta += 10; $(".horizontal").css({ left: delta + "px" }); if (delta > $("#horizontal").width()) { $("body").css("overflow", "inherit"); boo = false; } } } }); }); p { height: 500px; } #horizontal { position: relative; overflow: hidden; width: 100%; font-size: 3em; margin: 0; padding: 0; height: 250px; outline: 2px solid red; } .horizontal { display: block; position: absolute; width: 100%; left: -100%; padding: 20px; float: left; } .h_blockquote { position: relative; width: 100%; margin: 0 auto; font-size: 15px; line-height: 1.3em; color: #000; }

test

Скрипт некорректно работает в браузере Internet Explorer. Как исправить?

test


Что именно разработчики .NET 4.6.2 изменили в SslStream помимо SSL3?

Описание проблемы: пользователи поголовно начали жаловаться на странное поведение программы (которая была написана на .NET 4.5) после автоматического обновления Windows. Начал разбираться, тестировать. Воспроизвел проблему у себя на .NET 4.6.2. Оказывается транспорт SslStream начиная с .NET 4.6 поменял свое поведение. Причем непонятно в каком месте.
Взял сторонний транспорт (Chilkat), сравнил - везде получается одинаковые параметры, TLS 1.0, Cipher - Aes128, Hash - Sha1, одинаковые байты идущие через соединение...
Но! - Chilkat отрабатывает нормально, а SslStream рвет соединение на ReadAsync с исключением: IOException, сбой операции дешифрования. Журналы ошибок Windows пустые.
Вопрос: что конкретно изменилось в реализации?
p.s. Switch.System.Net.DontEnableSchUseStrongCrypto=true в appconfig не помогает, поведение не меняется.

Update:
Посмотрел локальные исключения, вот что всплыло: System.ComponentModel.Win32Exception (0x80004005): Не удается установить связь с локальной системой безопасности. Погуглил, нашел что это связано с лицензией на Windows. Хоть убейте не понимаю чем связана неактивированная тестовая Windows с работоспособностью SslStream. Клиентов ведь не заставишь поголовно покупать лицензии...
Часть синхронного стека вызовов:
System.dll!System.Net.Security._SslStream.ProcessReadErrorCode(System.Net.SecurityStatus errorCode = {неизвестно}, byte[] buffer = {неизвестно}, int offset = {неизвестно}, int count = {неизвестно}, System.Net.AsyncProtocolRequest asyncRequest = {неизвестно}, byte[] extraBuffer = {неизвестно}) System.dll!System.Net.Security._SslStream.ProcessFrameBody(int readBytes = {неизвестно}, byte[] buffer = {неизвестно}, int offset = {неизвестно}, int count = {неизвестно}, System.Net.AsyncProtocolRequest asyncRequest = {неизвестно}) System.dll!System.Net.Security._SslStream.StartFrameHeader(byte[] buffer = {неизвестно}, int offset = {неизвестно}, int count = {неизвестно}, System.Net.AsyncProtocolRequest asyncRequest = {неизвестно}) System.dll!System.Net.Security._SslStream.StartReading(byte[] buffer = {неизвестно}, int offset = {неизвестно}, int count = {неизвестно}, System.Net.AsyncProtocolRequest asyncRequest = {неизвестно}) System.dll!System.Net.Security._SslStream.ProcessRead(byte[] buffer = {неизвестно}, int offset = {неизвестно}, int count = {неизвестно}, System.Net.AsyncProtocolRequest asyncRequest = {неизвестно}) System.dll!System.Net.Security._SslStream.Read(byte[] buffer = {неизвестно}, int offset = {неизвестно}, int count = {неизвестно}) > Application.exe!d__a.MoveNext()

Update2:
Провел ряд тестов в процессе воспроизведения проблемы для публичного обозрения - по предварительным данным виноват Cipher = Aes128, тот же Aes256 - работает нормально.


Ответ

Для .NET 4.+ и x64 разрядных ОС, глобальный вариант:
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319] "SchSendAuxRecord"=dword:00000000
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319] "SchSendAuxRecord"=dword:00000000
Для .NET 2.+ и x64 разрядных ОС, глобальный вариант:
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v2.0.50727] "SchSendAuxRecord"=dword:00000000
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v2.0.50727] "SchSendAuxRecord"=dword:00000000
Действует сразу, перезапуск ОС не требуется.
Использовать только в крайнем случае!!! Выключение этой структуры связано с большими рисками по отношению к старым версиям SSL\TLS транспорта ОС.

Поддержка параллельных алгоритмов стандартной библиотекой

В c++17 должна появиться возможность выполнения стандартных алгоритмов, например, std::sort
template< class ExecutionPolicy, class RandomIt > void sort( ExecutionPolicy&& policy, RandomIt first, RandomIt last );
в параллельном режиме, передавая соответствующий тип политики выполнения. Поддерживает ли на текущий момент какой-либо из доступных online компиляторов данный функционал?
Все попытки подключить пока заканчивались примерно одинаково
fatal error: execution: No such file or directory #include ^~~~~~~~~~~ compilation terminated.


Ответ

The Parallelism TS Should be Standardized [параллельные версии алгоритмов STL]     N4507 - предварительная версия стандарта P0024R2 - спецификация
Статус поддержки данной фичи в Clang: https://libcxx.llvm.org/cxx1z_status.html [не имплементировано на данный момент]
Статус поддержки данной фичи в GCC: https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html#status.iso.201z [не имплементировано на данный момент]
Однако, есть готовые реализации:
Lutz Microsoft NVIDIA

Как правильно воспроизводить звуки на iOS

Использую SpriteKit и все хорошо, если выключить звуки то показывает практически стабильные 60fps. Но стоит включить звуки, как начинаются неприятные просадки на 2-3-4-5 кадров во время воспроизведения звуков, которые особо ничего не портят, но при этом создают неприятные микрофризы, чувствуется что не хватает плавности.
Собственно воспрос: как выводить звуки на iOS/SpriteKit без потери fps?
Изначально я использовал для воспроизведения SKAudioNode без dispatch, и просадки были значительными, потом переделал под следующий вид:
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
SKAudioNode *newAudio = [[SKAudioNode alloc] initWithFileNamed:nameSound]; [newAudio runAction:[SKAction changeVolumeTo:1.0 duration:0]]; newAudio.autoplayLooped = false;
dispatch_async(dispatch_get_main_queue(), ^{ [scene addChild:newAudio]; [newAudio runAction:[SKAction play]]; }); });
После этого изменения стало намного лучше, но все равно, как выше написал, остались неприятные просадки от которых хотелось бы избавиться. Если вообще убрать воспроизведение звуков, то стабильные 60fps.
Пробовал так же воспроизводить звуки через AVAudioPlayer, но получалось хуже чем с SKAudioNode, просадки были еще больше.


Ответ

Я юзаю AVAudioPlayer
Каких-то проседаний фпс именно из-за звука пока не замечал. Раньше юзал SKAction.playSoundFile... и вот там не фпс проседал, а игра фризилась при первом воспроизведении звука из-за того что я создавал экш при каждом создании ноды. Решается простым вынесением экшна в проперти и последующем его применении к объекту.
Через SKAction'ы
Property:
let soundAction: SKAction = SKAction.playSoundFileNamed("nameOfFile.wav", waitForCompletion: false)
В методе, в котором нужно воспроизвести звук:
run(soundAction)
Через AVAudioPlayer:
var musicPlayer: AVAudioPlayer?
let url = Bundle.main.url(forResource: filename, withExtension: nil)
musicPlayer = AVAudioPlayer(contentsOf: url!)
if let player = musicPlayer { player.numberOfLoops = -1 player.prepareToPlay() player.play() }

Чем грозит синглтон для БД?

Избавляясь от глобальных переменных, решил ссылаться на PDO объект через промежуточный статический метод.
Сейчас использование БД выглядит так:
# Установка соединения (срабатывает только в первый раз) DB::set('host','db_name','login','pass'); # Получение PDO объекта для взаимодействия DB::get();
В классах работающих с БД для уменьшения связанности создаю приватную статическую переменную которой присваиваю DB::get(). Рассчитываю на то, что в случае если способ получения PDO объекта изменится, мне придется сменить только значение этой самой переменной в каждом классе работающем с БД.
Чем плох такой подход и чем он грозит при разрастании проекта? Имеет ли смысл заводить реестр если таких объектов станет больше двух-трех?


Ответ

Сложно сказать, чем грозит именно в вашем случае. Многие проблемы, связанные со статическими методами и синглтонами для маленьких проектов носят чисто теоретический характер, нежели практический.
Попробуем потеоретизировать.
1. Что будет, если добавится ещё одна база данных?
Придётся добавить некий новый статический класс DB2 и соответствующие методы DB2::set() / DB2::get(). Соответственно, в классах, которые используют оба подключения, придётся добавлять новую статическую переменную.
2. Что будет, если понадобится писать тесты для компонентов, использующих БД?
Придётся полагаться на то, что где-то там внутри компонентов вызов DB::get() сработает правильно. Мы не будем иметь доступа к инстансу соединения с БД, чтобы заменить его каким-нибудь fake-объектом для отдельного тестового случая.
3. Что будет, если добавится несколько других точек входа в код?
Например, помимо основного сайта появятся: API для мобильных приложений, консольные демоны, админская часть, личный кабинет партнёров. В этом случае придётся при инициализации каждого приложения вызывать DB::set() с параметрами подключения. То есть множество приложений окажутся завязанными на один единый синглтон. Вместо этого хотелось бы иметь конфиг подключения, который независимо используется каждым приложением. Ведь гипотетически прослойка для подключения к БД в них может быть разная.

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

Логирование lua в syslog

Как напрямую писать логи с Openresty Lua в syslog?
При установке библиотеки luasyslog возникает ошибка:
ng-gateway_1 | error loading module 'vendors.lsyslog' from file '/usr/local/openresty/site/lualib/vendors/lsyslog.so': ng-gateway_1 | /usr/local/openresty/site/lualib/vendors/lsyslog.so: undefined symbol: luaopen_vendors_lsyslog
Подскажите как решить проблему, или какую библиотеку использовать. Спасибо!


Ответ

Было принято решение писать в syslog сервера на уровне nginx.
В docker-compose в volumes добавил bind на локальный /dev/log:
- type: bind source: /dev/log target: /dev/log
В nginx.conf, директиве server прокинул 2 unix socket-а:
# Logging configurations error_log syslog:server=unix:/dev/log,tag=ng_trace; access_log syslog:server=unix:/dev/log,tag=ng_trace,nohostname;
Для логирования в lua использую ngx.log(ngx.ERR, 'Log message')
Все логи из докер-контейнера пишутся прямиком в syslog сервера.

Использование STL и фреймворков в C++

C++ 2 главные библотеки (ИМХО): одна стандартная, одна почти стандартная(Boost). Но ни одна из них не позволяет создавать GUI (с кнопочками, менюшками и т.д.). А потому, есть и фреймворки: Qt, wxWidgets (MFC не в счет -- он не кроссплатформенен), однако, оба "переписывают" (дублируют) Stl (QString, wxString и т.д.**; **вариант: компилировать wxWidgets с использованием stl сами разработчики считают не совсем кошерным, т.к. он, по умолчанию, не работает).
Однако, в более-менее большом приложении обязательно будут сторонние библиотеки, которые используют Stl, да и не обязательно все классы из Stl присутствуют в фреймворке.
Вопросы: что делать с огромным количеством абсолютно бесполезно продублированного кода? Использовать stl классы или классы, предоставляемые фреймворком?
UPD: Что делать с принципом DRY (Don't Repeat Yourself) или это исключение?
UPD2: Как я понял, классы Qt серьёзно отличаются от аналогов в stl, а значит и DRY страдает не сильно. Что про wxWidgets? В wxBook написано:
Классы из wx на 70% похожи на классы из Stl
Я думаю, даже больше. А что с ними и DRY?


Ответ

Лично мое мнение - вопрос надуманный, поскольку концепция DRY (особенно в отношении контейнеров) намного в меньшей степени применима к устоявшимся фреймворкам, которые пережили несколько мажорных версий. Тем более, что, например, если говорить о STL, Qt и Boost, то DRY, за исключением некоторых моментов, не нарушается. Все Qt поддерживают семантику copy-on-write, а иногда вообще не совпадают с контейнерами STL в плане соответствия названия и назначения (например, std::set и QSet - это совершенно разные вещи). boost-specific контейнеры нигде не дублируют функциональность, предоставляемую STL На уровне таких проектов решение о реализации своего набора контейнеров может быть принято чуть ли не исходя из аргументов типа "std::vector плохо смотрится в коде вместе с QString" Или, например, если вы подойдете и спросите у девелоперов EASTL, слышали ли они про DRY и способы уменьшения энтропии в системы, то, мне кажется, вам рассмеются в лицо :) Количество продублированного кода, как вы это называете, не является проблемой до тех пор, пока под кодом подразумевается код фреймворка. Для своих проектов полезно выбрать некоторую стратегию использования контейнеров, например, используем только QXyz или std::xyz Причем понятно, что под использованием здесь имеется ввиду использование в интерфейсах. На уровне реализаций конкретных интерфейсов можете использовать что угодно и как угодно, хоть std::vector, написанный на ассемблере. Но протаскивать его вверх по иерархии, естественно, не стоит - достаточно в местах, где контейнер является частью интерфейса, осуществить соответствующее преобразование. Аргумент про размер выходного файла и время компиляции, тоже, на мой взгляд, несколько оторван от реальности. По своему опыту скажу, что приложение с boost::graph замечательно работает под ARM, а подключение любого хэдера boost::mpl сравнимо с подключением всех контейнеров Qt в плане времени компиляции. На практике, лично с моей точки зрения, 90% промышленных продуктов можно построить, если использовать Qt (фреймворк вообще и пользуясь только их контейнерами), boost::type_traits и При необходимости также можно strip'ать и добавлять в проект какие-либо части boost типа boost::graph, boost::flyweight или boost::heap

Неориентированный граф: оптимальное создание

Здравствуйте! Всем добрый день! Подскажите, пожалуйста, каким образом оптимальнее создавать неориентированный граф, столько разных структур существует в Java
Можно в виде матрицы смежности, создав просто 2-мерный массив (что не очень оптимально, так что сразу был отброшен) Можно переделать тип Iterator в Bag, сделав список смежных вершин (тогда это будет выглядеть примерно так: http://algs4.cs.princeton.edu/13stacks/Bag.java.html, неплохой вариант, в принципе) Можно сделать все это с помощью Map и TreeMap (видел один пример, понравился по компактности, но не знаю, будет ли это приемлемо, или же нет) Также вот увидел вариант с помощью Hashmap
Вот в общем, такие у меня варианты есть. Я не силен в Java, еще только начинаю в нем разбираться. Подскажите, каким образом решить данный вопрос. Спасибо большое заранее!


Ответ

Конкретный язык прграммирования тут ни при чем! Видимо, если Вы не сильны в Java, то пока не сильны и вообще в алгоритмах и структурах данных. Поэтому рекомендую не думать на данном этапе о сверхэффективности Вашего кода. Используйте классические структуры данных для представления графа: матрицу смежности или списки смежности (это 1й и 2й пункты из Вашего вопроса). Возьмите хорошую книгу и реализуйте граф так, как там описано внутри класса граф (в зоне private). Добавьте public-интерфейс, который нужен для конкретной задачи. Реализуйте по книге алгоритмы обхода графа. Запрогрпмируйте какую-нибудь олимпиадную задачу с помощью Вашего класса. Если Вы сделаете обе реализации, сможете сравнить производительность. Успехов!

JavaScript: синтаксис «знак равно + знак больше» (=>)

Что означает синтаксис => в JavaScript?
Пример с JSR (JavaScript.Ru):
let promise = new Promise((resolve, reject) => { setTimeout(() => { resolve('result'); }, 1000); });


Ответ

Это новый синтаксис функций. Кроме того как он выглядит имеет так же несколько особенностей.
Самое главное отличие, что он автоматически использует this из замыкания, где метод определен. Например:
var obj = { foo: function() { return this; }, arr: () => { return this; } };
obj.foo(); // obj obj.arr(); // window
Вторая особенность, что метод имеет краткую лямбда форму, когда выражение после стрелочки является возвращаемым значением.
var а = () => 1 // функция, всегда возвращающая единицу // Эквивалентно (кроме this контекста) var a = function() { return 1; } var b = x => x + 1 // Эквивалентно var b = function(x) { return x + 1; }
В случае нуля или нескольких параметров нужны скобки вокруг аргументов
var c = (x,y,x) => x * y * x
Стоит выделить возврат объекта
var foo = (x) => ({ field: x })
Обратите внимание на круглые скобки вокруг тела функции, это одна из проблем нового синтаксиса, потому как почти такой же код:
var foo = (x) => { field: x }
является валидным, но делает совсем не то, что вы хотите.
В случае когда выражений больше чем одно, то необходимые фигурные скобки вокруг тела функции. При этом значение возвращать надо явно с помощью return.
var foo = (a, b) => { if (a > b) return 1; else return 42; };
Когда применять такие функции? Главных применения два. Первый – сокращенный синтаксис в обратных вызовах. Согласитесь, такое
myStringsArray .map(a => parseFloat(a)) .reduce((b, c) => b + c, 0);
намного луче читается, чем
myStringsArray .map(function (a) { return parseFloat(a)}) .reduce(function(b, c) { return b + c; }, 0);
Второе применение связанно с контекстом, классами и обратными вызовами.
var obj = { data: 1, sendToServer: function() { ajax(this.data, function(newData) { this.data = newData; // this !== obj }); }, sendToServerArrow: function() { ajax(this.data, (newData) => { this.data = newData; // this === obj }); } }
obj.sendToServer() // установит значение в window.data obj.sendToServerArrow() // установит значение в obj.data
Стоит ли использовать такие функции? Нужно помнить, что их поддержка далеко не во всех браузерах. Если вы используете трансплитеры, такие как babel, или пишете под nodejs (>4), то никаких преград нет, кроме возможной потери производительности (нужно мерить).
Для себя я решил, что использую их везде, кроме определения функций в модуле.

разница между flush() и ob_flush()

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


Ответ

Если в простых терминах пояснять, то когда вызывается echo/print, то данные идут на вывод (например браузер). Чтобы при многочисленных echo не гонять по байту существует выходной буфер, куда накапливаются выходные данные до какого то объема и при переполнении данные отправляются на выход. Потом снова происходит накопление данных. flush принудительно выбрасывает содержимое буфера в поток вывода.
Например, есть у вас долгая задача, где нужно постоянно досылать в браузер результаты и вы используете echo "информация". Но они накапливаются в буфере и браузер ничего не получит пока буфер не переполнится. flush после echo принудительно отправляет данные. Также буфер можно отключить, а в режиме cli его вообще нет.
ob_* позволяет же инициализировать свои вложенные буферы, наслаивая их друг на друга. И при очищении данные такого буфера будут попадать в нижележащий вплоть до дефолтного. А можно вообще не делать flush, а просто забрать накопленный результат из буфера и уничтожить его.
ob_start открывает новый буфер и все последующие print/echo будут попадать в него. В любой момент вы можете открыть новый буфер, а можете закрыть и сбросить данный на буфер открытый ранее. То есть это выглядит как слоенный пирог (стек) буферов, где данные пишутся в верхний буфер и только когда все буферы будут сброшены на нижележащие вплоть до дефолтного, только тогда данные уйдут не вывод.
Удобно это использовать в самописном шаблонизаторе. Для сегмента шаблона создаем буфер и потом забираем готовый хтмл и буфер уничтожаем. Или когда вызываем сторонний код который изобилует echo и создает мусор - мы его вызов оборачиваем в ob_* и весь этот мусор оседает там.
то есть flush сбрасывает системный буфер на вывод, а ob_flush сбрасывает последний открытый буфер через ob_start в нижележащий буфер (ob_* или системный)

Использование new(this)

Читаю C++ Super-FAQ. В разделе Constructors натыкаюсь такое высказывание:
BTW do NOT try to achieve this via placement new. Some people think they can say new(this) Foo(x, int(x)+7) within the body of Foo::Foo(char). However that is bad, bad, bad. Please don’t write me and tell me that it seems to work on your particular version of your particular compiler; it’s bad
Речь идет о том, то что так делать категорически нельзя:
class Foo{ public: Foo(char x){ new (this) Foo(x, int(x)+7); } Foo(char x, int y){ //... } };
Может кто-нибудь более подробно объяснить чем грозит такой трюк?
UPD: Подозреваю что в данном примере все будет нормально, и проблемы начнутся при наследовании, динамическом выделении ресурсов и т.п.


Ответ

http://ideone.com/gkgi7S
class Base { public: Base() { ptr = new int[100]; cout << "alloc mem at " << ptr << endl; } ~Base() { delete [] ptr; cout << "free mem at " << ptr << endl; } int * ptr; };
class Derived: public Base { public: Derived(int x, int y):x(x),y(y){} Derived(int x) { new(this) Derived(x,0); } int x, y; };
int main(int argc, const char * argv[]) { Derived d(5); }
Вывод:
alloc mem at 0070EA58 alloc mem at 0070FFE8 free mem at 0070FFE8
Такого примера достаточно?...
Можно и без наследования - суть не меняется:
class Derived { public: Derived(int x, int y):x(x),y(y){} Derived(int x) { new(this) Derived(x,0); } int x, y; Base b; };

Замена цвета элемента SVG туда и обратно при клике

Этот вопрос задавался много раз и есть хорошие решения при помощи использования javascript, например такой код:
var shapeClick = document.getElementById("shape").addEventListener("click", changeColor); var clicks = 0; function changeColor(){ if (shape.style.fill == "rgb(29, 172, 249)") { shape.style.fill = "rgb(255, 0, 0)"; } else { shape.style.fill = "rgb(29, 172, 249)"; } }

>

Всё отлично работает, с помощью скриптов можно сделать очень много.
Внимание вопрос
Как сделать тоже самое на чистом SVG (SMIL), не используя javascript и CSS
Просто сменить цвет по клику не составляет труда:

Но как сменить цвет обратно, повторно кликая по объекту не приходит в голову.


Ответ

if (shape.style.fill == "rgb(29, 172, 249)")
Так лучше не надо. А то кто-нибудь вернёт 1DACF9 вместо rgb(29, 172, 249) и всё.
Как сделать тоже самое на чистом SVG, SMIL не используя - javascript, CSS
label { display: block; } #red { display: none; } svg { width: 250px; display: block; } #shape { fill: green; stroke: black; stroke-width: 2; } #red:checked + svg #shape { fill: red; }
где и как обрабатывается клик по элементу?
В примере выше получился клик по всему svg Чтобы это исправить, можно воспользоваться pointer-events
label { display: block; width: 100%; height: 100%; pointer-events: none; } #red { display: none; } svg { width: 250px; display: block; } foreignObject svg { pointer-events: none; } #shape { fill: green; stroke: black; stroke-width: 2; pointer-events: all; } #red:checked + svg #shape { fill: red; }
Если внутри одного svg надо разместить несколько таких объектов, можно воспользоваться foreignObject, однако следует отметить, что его поддержка браузерами весьма сомнительна
label { display: block; width: 100%; height: 100%; pointer-events: none; } #red { display: none; } svg { width: 250px; display: block; } foreignObject svg { pointer-events: none; } #shape { fill: green; stroke: black; stroke-width: 2; pointer-events: all; } #red:checked + svg #shape { fill: red; }

Из чего складывается общий размер установленного Android приложения?

У меня есть .apk установочник который занимает 3,8 Мб памяти. При установке в телефон приложение занимает 32,4 Мб (если использовать ProGuard то размер уменьшается до 20,4 Мб, но это все равно много). Картинки из интернета или других мест не загружаю. Программа выполняет различные математические преобразования над величинами. Папка с ресурсами (src) проекта весит всего 524 Кб, папка с java кодом занимает 200 Кб. Почему после установки приложение занимает так много места на телефоне? Как определить что именно в проекте может занимать столько места? Спасибо за ответ.


Ответ

APK по сути является ZIP-архивом, то есть, файлы в нем уже подвержены некоторому сжатию. Когда APK устанавливается на телефон, он сохраняется в каталог /data/app/, что занимает как раз те пресловутые 3.8 мб. Из APK извлекается файл classes.dex (с байткодом). Он преобразуется в платформенную библиотеку для выполнения кода на процессоре телефона. Откомпилированная после первого запуска приложения библиотека находится в /data/dalvik-cache. Именно она и занимает большую часть объема (как правило, она минимум в два раза больше APK).
Зачем все это делается? Для скорости работы приложения. Таким образом не приходится компилировать все байткоды, а также запускать исполнительную среду Java при каждом запуске приложения.
P.S: в качестве развлечения можете взять практически любую APK и сравнить ее объем с размером итогового приложения, они гарантированно будут сильно отличаться.

Google+ API. Поле circledByCount отображается не всегда

Когда по запросу people.get пытаюсь получить circlesByCount, у меня это получается не всегда. К примеру, запросы к Gina Trapani и Leo Laporte не дают получить их число подписчиков, а запросы к Kevin Rose и Matt Cutts дают. При этом у них всех на страницах можно увидеть число подписчиков. Почему так происходит? Какие настройки нужно выставить для такого результата / для предотвращения такого результата?


Ответ

В настройках Google+ есть переключатель в разделе Настройки -> Профиль -> [Показывать тех, кто добавил меня в круги]. У тех людей, у которых circlesByCount не выводится при API запросе, этот переключатель в неактивном состоянии.

Для чего ввели U+1160?

В unicode есть симол который похож на пробел, но это не пробел. Его код — U+1160. Выглядит он так вот так: 'ᅠ' (без кавычек). Так какую функцию выполняет этот символ в unicode, для чего он создан?


Ответ

Hangul Jungseong Filler или U+1160 используется для сборки корейских иероглифов. В html можно вызвать как ᅠ

Поиск по минимальному эвклидовому расстоянию между заданным вектором и векторами в БД

В таблице PostgreSQL в поле типа double[] хранятся векторы высокой размерности (128 координат если быть точным).
create table tab ( id integer, name character varying(200) vector double precision[] )
По заданному вектору нужно вернуть из БД одну запись с минимальным эвклидовым расстоянием между этим вектором и вектором в записи таблицы.
Имеется функция которая вычисляет эвклидово расстояние двух векторов по всем известной формуле SQRT((v1[1]-v2[1])^2+(v1[2]-v2[2])^2+....+(v1[128]-v2[128])^2)
CREATE OR REPLACE FUNCTION public.euclidian( arr1 double precision[], arr2 double precision[]) RETURNS double precision AS $BODY$ select sqrt(SUM(tab.v)) as euclidian from (SELECT UNNEST(vec_sub(arr1,arr2)) as v) as tab; $BODY$ LANGUAGE sql IMMUTABLE STRICT
Вспомогательная функция:
CREATE OR REPLACE FUNCTION public.vec_sub( arr1 double precision[], arr2 double precision[]) RETURNS double precision[] AS $BODY$ SELECT array_agg(result) FROM (SELECT (tuple.val1 - tuple.val2)*(tuple.val1 - tuple.val2) AS result FROM (SELECT UNNEST($1) AS val1 ,UNNEST($2) AS val2 ,generate_subscripts($1, 1) AS ix) tuple ORDER BY ix) inn; $BODY$ LANGUAGE sql IMMUTABLE STRICT
Запрос :
select tab.id as tabid, tab.name as tabname, euclidian(target_vector,tab.vector) as eucl from tab order by eulc ASC limit 1
Пока всё отлично работает. Но нетрудно заметить, что запрос может быть выполнен не иначе как полным перебором всех записей в таблице tab. До определенного момента это всех устраивало, но сейчас база данных подрастает и встает вопрос что полный перебор не очень хорош мягко говоря. Сейчас в базе несколько тысяч записей и явных проблем с производительностью пока нет, но десятки и сотни тысяч записей не за горами.
Вопрос - как можно изловчиться и обеспечить индексный поиск в таблице tab при выполнении запроса? Чтобы хотя бы 90% ненужных записей отбрасывалось по индексу, а в оставшихся 10% уже допустимо полным перебором пройти.
P.S. одно из текущих направлений поиска решения: расширение PostGIS позволяет вести поиск и сортировку по расстоянию (ST_3DDistance), фильтр по удаленности (ST_3DWithin) и пр. в 3-х мерном пространстве - делается это быстро по индексам. Может быть как-то кто-то это абстрагировал на случай N-мерного пространства?
P.S.2. данные метеорологических наблюдений:
сделал запрос select max(val), min(val) from (select unnest(vector) as val from tab) as tab1 - 0.485470712185 -0.41735497117 - я точно не знаю, думаю значения координат в теории не превышают 1.0 по модулю при этом векторы не нормализованы, расстояние от (0,0,...0) лежит в пределах от 1.2 до 1.6.
P.S.3. Подобную задачу не первый раз решаю - предыдущий раз это было связано с поиском ближайшей точки в облаке точек (3D), но там у меня было все локально в памяти (база данных iBoxDB), там даже и не рыпался от полного перебора уйти... а тут вся мощь сервера postgresql - из принципа хочется решить).
P.S.4. Исходя из данных наблюдений, максимальное расстояние между точками (на основании имеющейся статистики) составляет примерно 3.2 - расширим на всякий случай до 5, ну или до 10. Мне точно не интересны точки, отдаленные друг от друга более чем на 1.0. Выделим из 128-мерного пространства несколько (сколько не жалко, скажем 10,20) трехмерных проекций - наборов координат (v1,v2,v3), (v4,v5,v6), и т.д. Очевидно, если в одной из трехмерных проекций расстояние превысило лимит (1.0) - то меньшим на полном наборе координат оно точно уже не станет. Далее пытаемся применить то что есть в PostGIS - индексируем каждую их трехмерных проекций векторов и затем при поиске ставим фильтры с помощью ST_3DWithin < 1.0 (количество фильтров будет равно количеству выделенных трехмерных проекций). Стоит попробовать в новогоднюю ночь :-) Очень бы хотелось компетентный комментарий гуру по PostGIS - случится ли чудо в новогоднюю ночь, или можно спокойно веселиться? :-)
P.S.5. В сторону buckets предлагаю больше не копать - во первых их ну ооочень много получится, как не раскидывай - даже если строить на проекции а не на полной размерности, скажем взять первых 64 координаты и раскидать по букетам (<0,>=0) - получится 18446744073709551616 возможных букетов - куда тут... Во вторых, есть проблема границ букетов - точки могут находиться на минимальном расстоянии но попасть в соседние букеты, это нада учитывать в запросах, для этого нужно держать диаграмму соседей для каждого букета... если же применять простейшее разбиение (<0,>=0) - оно в принципе теряет всякий смысл, потому что для одной оси граница только одна и нужно все равно искать и там и там... а если разбивать каждую ось на 4 диапазона - количество букетов уверенно уйдёт в бесконечность... В третьих, не хотелось бы создавать велосипед с помощью напильника - если ГИС-системы решают задачу на трехмерных координатах - либо абстрагировать эти механизмы на большие размерности, либо выделять 3х мерные проекции и пытаться индексировать на них.


Ответ

Есть методики понижения размерности, когда из N измерений делается два или три. Например:
Independent Component Analysis (ICA) Principal Component Analysis (PCA) Nonnegative Matrix Factorization (NMF)
Известны и многие другие способы подойти к проблеме излишнего числа измерений. В том числе можно посмотреть на алгоритмы кластеризации
Не обязательно на ваших данных любой из этих алгоритмов будет работать хорошо, но какой-то может дать приемлемые результаты.
Другое дело что вы далеко не уедете, считая ваши данные непрозрачными. Вам хорошо бы понять, какие координаты что означают. Наверняка ж эти векторы не из случайных чисел. Скорее всего вы берете их из предпоследнего слоя нейронной сети. Значит можно для какой-то известной выборки, которая кардинально отличается по какому-то фактору, можно выяснить какой нейрон соответствует этому фактору. И вот у вас уже одна из координат для трехмерного вектора, по которому можно быстро искать.

Ejb совместно c Spring

Как в spinge использовать EJB классы.
Да EJB не очень, тем более в библиотеке использовать спринг или ejb - это жесть, но что досталось то приходится пока использовать. Есть вопросы
Как спринг дружит с EJB с аннотациями типа stateless, ejb, и даже ConcurrencyManagement Если сервер приложений поднимает эти бины при запуске, как спринг делает их своими бинами и делает ли своими бинами или какая то прокся между бинами управляемые сервером приложений и бинами спринга Как отключить поднятие EJB бинов сервером приложений и чтобы такие бины управлялись спрингом путем сканирования этих пакетов либо же просто через конфигурацию


Ответ

Если коротко, только получать их от сервера приложений через JNDI.
Контейнер Spring и контейнер EJB/CDI - это два разных, хоть и похожих, мира. У бинов Spring и EJB разный жизненный цикл, а у контейнеров разные механизмы обеспечения этого жизненного цикла. Отсюда ответы на ваши вопросы:
Никак не дружит. Эти аннотации анализирует и действует в соответствии с ними CDI-контейнер. Когда Spring встречает поле, в которое нужно внедрить зависимость, он ищет эту зависимость в контексте. А появиться зависимость в контексте может разными способами - в результате вызова конструктора объявленного в конфигурации бина, в результате вызова фабричного метода какого-нибудь класса, в результате JNDI-запроса. Как раз последним способом контекст Spring и получает EJB от сервера приложений. Никак. Spring не сможет самостоятельно управлять EJB.
Так что вам придётся запустить ваше Spring-приложение, включающее EJB-компоненты, на сервере приложений, добавить в конфигурацию контекста что-нибудь вроде


и убедиться в наличии соответствующего сеттера в бине Spring
public class SomeSpringBean { private SomeEJB someEjb;
public void setSomeEjb(SomeEJB someEjb) { this.someEjb = someEjb; } }
Почитать можно здесь

Неявное приведение типов в Java

Учу Java, читаю Шилдта. Уже много раз прочитал, что Java — сильно типизированный язык. Почему следующий код выкидывает ошибку
int i = 10; i = i / 2.5;
А такой код прекрасно работает и присваивает к i значение 4?
int i = 10; i /= 2.5;


Ответ

Так работает оператор присваивания, если переменная типа int, а i / 2.5 типа double, то вы не можете присвоить значение типа double к типу int без явного каста.
То есть проверка типов осуществляется перед присваиванием переменной значения. Во втором случае утверждение является выражением, в котором значение преобразовывается к типу операнда, то есть чтобы можно было осуществить операцию и обратно к типу переменной, где должно хранится значение. То есть
int i = 10; d = i /= 2.5;
тоже будет работать, поскольку нначения int преобразовавыются к double перед присваиванием без потери точности, обратное не верно и вызывает ошибку.

Выбор кроссплатформенного мобильного движка для игр [закрыт]

Цель: разработка кросплатформенных игр на мобильные телефоны. Основной target: iOS (iPhone/iPad) и Android, большим плюсом будет поддержка Windows Phone 7, Symbian, Bada. Задача: максимально быстрая и удобная разработка игр, цена движка роли не играет. Из игр, предположительно 2D аркады, казуалки. Должна быть возможность показа рекламы, поддержка мультиплеера и интеграции с Game Center (iOS) / OpenFeint, шаринг в соц сети. Доступ к Native API будет большим плюсом. Большое количество 3rd Party Tools, плагинов и наработок, ускоряющих процесс разработки тоже крайне желательно. На данный момент есть два претендента: Corona SDK и Unity 3d. У обоих есть как плюсы так и минусы. Слышал разработка 2D игр на Unity требует довольно больших усилий, за то в замен получаем desktop и web версии! В общем дискас. Плюсы, минусы, подводные камни. Возможно есть еще годные аналоги которые я упустил.


Ответ

Остановился на Corona SDK, быстро, удобно. Код пишется на Lua, поддерживаютсю платформы iOS / Android / Kindle Fire / Nook и совсем скоро будет поддержка десктопных Mac App Store приложений. В качестве редактора можно использовать TextMate, Sublime Text 2, Intellij IDEA, VIM. Для них всех есть бандлы добавляющие поддержку короны. Я пока остановился на ST2 Большой плюс Короны, это очень много уроков, примеров кода и библиотек, а так же сторонних программ, типа LevelBuilder, PhysicsHelper, значительно упрощающих разработку. Вот еще пару ссылок на туториалы: Официальные семплы Mobile.Tuts+ Learning Corona – вообще мега сайт, собраны почти все толковые уроки, что есть в сети.

“тонкая синхронизация” в односвязном списке

Добрый день! Недавно подсмотрела в курсах интересный вариант работы со списками в многопоточной среде, обозвали эту модель там "тонкой синхронизацией". Суть состоит в том, что во время работы со списком блокируется доступ только к текущему и предыдущему элементу списка при любых операциях типа удаления/вставки, и при этом коллизий не возникает. Кто-нибудь использовал этот способ синхронизации на реальных задачах? Вот приведенный в курсах пример добавления элемента к связному списку на псевдокоде : Node prev = head; prev.lock(); Node curr = prev.next; curr.lock();
try{ while(curr.key < key){ prev.unlock(); prev = curr; curr = curr.next; curr.lock(); } if(key == curr.key) return false; else{ node = new Node(key,item); node.next = curr; pred.next = node; return true; } } finally{curr.unlock();pred.unlock();} Если переложить этот пример на С, то я должна создать список, элементами которого будут экземпляры некой структуры, содержащей мьютекс. Вот на этом месте у меня возникает вопрос, как реализовать хотя бы первую строку данного кода без риска поймать segfault в случае, когда: первый поток получил указатель на головной элемент списка, управление получил второй поток, и удалил головной элемент списка, управление вернулось к первому потоку, он пытается захватить мьютекс головного элемента списка, которого уже нет в живых... ?


Ответ

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

Возвращение из функции &&

В популярной статье заметил подобный код:
int&& f() { return 2; }
Тут xvalue, но на временную переменную в стеке функции. Он будет не корректен, правильно? Знаю что &&/const& продлевают жизнь (12.2/5), но не в таком контексте же:
const int& f() { return 2; }
Может быть, в первом коде, какой то трюк с литералом и xvalue?
(Этот код должен быть некорректным, но статья популярней меня)


Ответ

Чтобы убедиться, что функция некорректна, можно запустить следующую тестовую программу
#include
struct Int { Int( int x ) : x( x ) { std::cout << "Int::Int( int )" << std::endl; } ~Int() { std::cout << "Int::~Int()" << std::endl; x = 100; } Int( const Int &rhs ) : x( rhs.x ) { std::cout << "Int::Int( const Int & )" << std::endl; } Int( Int &&rhs ) : x( rhs.x ) { std::cout << "Int::Int( Int && )" << std::endl; }
int x; };
std::ostream & operator <<( std::ostream &os, const Int &rhs ) { return os << rhs.x; }
Int && f() { return 2; }
int main() { Int x = f();
std::cout << "x = " << x << std::endl; }
Ее вывод на консоль может выглядеть следующим образом (хотя из-за неопределенного поведения программы вывод может отличаться)
Int::Int( int ) Int::~Int() Int::Int( Int && ) x = 100 Int::~Int()
Как видно в функции main переменная x получила значение уже удаленного объекта.

Qt, Linux, С++. Кто может мешать связи по TCP/IP [решено]?

Написал небольшую библиотечку для синхронизации данных между программами. Есть очередь сообщений. Есть сервер, цепляющийся к очереди, открывающий порт и отвечающий на запросы типа "дай все сообщения", "дай сообщения с такого-то времени", "дай сообщения за последние n мс" и так далее. Есть клиент, цепляющийся к аналогичной очереди, и периодически опрашивающий сервера с указанными адресами/портами, занося новые сообщения в очередь. Подключается, опрашивает и отключается. Написал несколько программ, обменивающихся через такие очереди. Под Windows всё работает в любых версиях от W7 до W10, проверял c версиями Qt 4.7.1, 4.8.6, 5.5.1. Под Linux (Ubuntu 14.04, Qt 5.5.1) тоже работает практически всё. Проблема в слове "практически".
Итак, есть программы A, B, C. B опрашивает несколько источников (A) и формирует выходную очередь, которую опрашивают несколько клиентов (С). Доставка от A к B или от A к C работает всегда. А вот от B к C иногда, если С запущена позже, чем B, связь не налаживается - идут timeout'ы и порча пакетов. При этом, если B и C находятся на одном узле, всё работает. Пробовал для отладки поднимать на ноутбуке виртуальные машины Virtualbox и запускать B и C в разных виртуалках или одну в виртуалке, другую на хосте - всё работает, даже если сетевые задержки сильно увеличивать. Проблемы появляются только при работе между разными физическими компами. Попробовал пересобирать B под Qt 4.8.6 - проблема появляется реже, но всё равно иногда остаётся. Если запускать B в виндовой версии, через Wine - всё тоже работает. Сетевые экраны отключены. Порты (9200-9300), используемые для сервера, никем не заняты, после запуска программ-серверов (A, B) сканер показывает, что они (порты) открыты.
Сейчас собираю на работе тестовый стенд (несколько узлов с Linux) для того, чтобы запустить B под отладчиком. Но в целом ситуация вызывает у меня глубочайшее недоумение, так как:
один и тот же код работает между A и B, между A и C, но не между B и C один и тот же код работает между разными виртуальными машинами, но не между разными физическими ... в сборке для `Windows`, но не в сборке для `Linux`. ... если C запущена раньше, чем B, но не наоборот.
Код проверен Valgrind'ом и cppcheck'ом. Натыкался ли кто-нибудь на похожие "грабли", можете ли предположить хоть одну причину такого странного поведения?
Update: Всё страньше и страньше. Собрал тестовый стенд - и не могу воспроизвести проблему. Те же самые A, B и C цепляются друг к другу во всех возможных комбинациях. Чувствую, придётся запасной switch на объект везти.
Update2: Удалось воспроизвести проблему на стенде. Цепляю к одному B несколько C, через некоторое время часть клиентов (C) перестаёт цепляться. Что удивительно: когда клиент цепляться перестал, я пробую с его машины пинговать машину, на которой развёрнут сервер (B) - и она не пингуется. И начинает пинговаться только после того, как машина B в свою очередь начнёт пинговать клиентскую (С). У меня появилось подозрение, что Ubuntu воспринимает эту кучу запросов, приходящую с клиента, как DOS-атаку, и блокирует хост клиента. Осталось найти, где в настройках системы это прописано, и отключить.
Update3: Ситуация действительно очень похожа на защиту от DDOS-атаки. И такая версия объясняет, почему связь между A и B работает, а между B и C - нет. К одному источнику данных (A) цепляется максимум один промежуточный обработчик (B), а вот к нему может цепляться несколько клиентов C. И именно обращения на один порт с разных узлов, видимо, интерпретируются как DDOS-атака. При этом первый подключившийся клиент остаётся работоспособным, а вот последующие обрубаются так, что даже ping'и от них не проходят.
Update4: найден workaround. Я запустил на машине, на которой запущен B, команду ping до хостов, на которых запущены C. После этого отрубаться эти хосты перестали. Вместе с тем, понять, что же вызывает такую реакцию системы, пока не получается. ufw отключён.
Update5: проблема решена. Сервер не должен рвать соединение, надо дождаться, пока соединение разорвёт клиент (см. Update к комментарию).


Ответ

Причины возникновения проблемы нашлись. Их было две. Одна - в тестовой системе, там серьёзные неполадки с сетью, и даже pingи теряются процентов на 30. Вторая и главная причина - в моём коде, сервер неправильно закрывал соединение. Обработав входящее соединение и сделав socket->write(), он висел на socket->waitForBytesWritten() (таким образом, до завершения отправки поток сервера был заблокирован), а после этого выполнял socket->close(). Видимо, такое принудительное закрытие сокета и не нравилось ОС. Когда я убрал waitForBytesWritten() и вместо close() сделал disconnectFromHost(), всё заработало.
Update: Заработало не всё. На сервере проблема осталась, workaround из Update4 помогает, пока конфигурация остаётся стационарной. Когда мы добавляем мобильный клиент, подключающийся из разных мест, с разных IP, добавить его в пингующий скрипт уже не получается, и он довольно скоро отваливается. На клиентах, даже стационарных, иногда появляются сообщения об обрыве и восстановлении связи. Решение проблемы нашёл только недавно.
Итак: когда сервер стал закрывать сокет более осторожно, через disconnectFromHost, проблема стала менее острой. Однако правильное решение оказалось таким: сервер вообще не должен закрывать сокет самостоятельно! Он должен реагировать на сигналы disconnected и error, по которым вызывает deleteLater()
Когда сервер сам решает закрыть сокет, завершив отправку данных, возникает гонка сигналов. Сигнал об отключении с сервера может прилететь раньше, чем ответ сервера клиенту, и тогда мы на клиенте получаем ситуацию "сервер отключился - сервер подключился". А когда до сервера долетает запрос на отключение от клиента, а сокета уже нет, видимо, возникает аварийная ситуация, и накопление таких ситуаций приводит к блокировке IP клиента (под Linux. Windows такие запросы, похоже, просто игнорирует).
Итог: из сервера просто удалил disconnectFromHost(), и старый клиент работает (завершается неделя тестирования 2 клиента на 1 сервер) без единой потери связи. Timeoutы уменьшил с 400-600 мс до 10мс, и всё равно разрывов нет. Главный вывод: сервер не должен закрывать соединение сам, он должен реагировать на сигналы disconnected и error, возникающие вследствие действий клиента и среды передачи данных.

systemd/udev: запуск демона при подключении модема и остановка демона при отсоединении модема

Возможно ли в systemd/udev реализовать так, чтобы при появлении в системе модема (/dev/ttyACM%i) запускался демон modem@%i.service, и чтобы при отключении модема демон останавливался?
Готовый полный универсальный рабочий рецепт.
/etc/ppp/peers/modem
460800 defaultroute usepeerdns crtscts lock noauth local persist modem nopcomp novjccomp nobsdcomp nodeflate noaccomp ipcp-accept-local ipcp-accept-remote noipdefault connect "/usr/sbin/chat -t10 -f /etc/ppp/chatscripts/modem.chat"
/etc/ppp/chatscripts/modem.chat
В поле точки доступа "internet.mts.ru" или подставьте точку доступа своего провайдера или оставьте такой, если у вас российский МТС. Большинство провайдеров игнорируют это поле, и его вообще можно не указывать, но МТС её требует и без неё не соединяет.
ABORT 'BUSY' ABORT 'NO CARRIER' ABORT 'VOICE' ABORT 'NO DIALTONE' ABORT 'NO DIAL TONE' ABORT 'NO ANSWER' ABORT 'DELAYED' REPORT CONNECT TIMEOUT 5 '' 'ATQ0' 'OK-AT-OK' 'ATZ' TIMEOUT 3 'OK-AT-OK' 'ATI' 'OK' 'ATZ' 'OK' 'ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0' 'OK' 'AT\^SYSCFG=2,2,3fffffff,0,1' 'OK-AT-OK' 'AT+CGDCONT=1,"IP","internet.mts.ru"' 'OK' 'ATDT*99#' CONNECT
/usr/lib/systemd/system/modem@.service
[Unit] Description=Modem /dev/ttyACM%i BindsTo=dev-ttyACM%i.device After=dev-ttyACM%i.device Before=network.target Wants=network.target
[Service] ExecStart=/usr/sbin/pppd call modem /dev/ttyACM%i
[Install] WantedBy=multi-user.target
/etc/udev/rules.d/90-modem.rules
KERNEL=="ttyACM[0-9]*", TAG+="systemd", ENV{SYSTEMD_WANTS}="modem@%n.service"
Затем перезагрузить конфигурацию systemd.
sudo systemctl daemon-reload
И теперь достаточно просто подключить любой телефон, кроме огрызка, с любым оператором в режим не накопителя. (Nokia suite, Телефон, Модем, Синхронизация и т.д.)
Ну, конечно же, желательно убедиться, что у вас определяется модем и его файл устройства, как у меня, /dev/ttyACM0 и udev на него ссылается как dev-ttyACM0.device, а не /dev/ttyUSB0 и dev-ttyUSB0.device или как-то иначе. Цифру, обозначающую порядковый номер устройства (0) в демоне systemd заменяем на %i, а в правилах udev на %n. Это даёт возможность не привязывать демон и правила только к одному модему, а работать с неограниченным количеством модемов и корректно управлять каждым модемом отдельно друг от друга.
systemctl list-units -tdevice -a | grep dev-tty
Телефон теперь всегда подключается к интернету при подключении к компьютеру, даже если компьютер успешно подключён через ethernet или Wi-Fi к интернету. Правда, в этом случае, когда в системе поднят (ifup) хоть 1 интерфейс, трафик идёт через него, а с модемом лишь устанавливает соединение, но передачи данных не происходит.
Чтобы отключить автодозвон при подключении модема нужно просто замаскировать демон sudo systemctl mask modem@0.service, а потом, когда понадобится модем размаскировать sudo systemctl unmask modem@0.service 0 - это порядковый номер модема, если у вас их несколько, то можно отключить ненужные или отключить все.
И самое не приятное, если ethernet и/или Wi-Fi подняты (ifup), то трафик не будет идти через модем, пока все интерфейсы не будут опущены (ifdown) даже если к ним не подключен кабель или нет выхода в интернет. Но это уже особенность модемной маршрутизации.


Ответ

Правильнее будет, чтобы udev запускал/останавливал systemd.service — это его работа.
my.service
[Unit] Description=Modem ppp0 <--> /dev/ttyACM0 BindsTo=dev-ttyACM0.device After=dev-ttyACM0.device Before=network.target Wants=network.target
[Service] ExecStart=/usr/sbin/pppd call modem ExecStop=комманда на стоп KillMode=none Type=oneshot RemainAfterExit=yes
[Install] WantedBy=multi-user.target
Список всех устройств, но со своим ты уже разобрался: systemctl list-units --all --full | grep ".device"
90-my.rules в папку /etc/udev/rules.d/
KERNEL=="ttyACM*", ATTRS{serial}=="<СЕРИЙНИК>", TAG+="systemd", ENV{SYSTEMD_WANTS}="my.service"
Можно добавить SYMLINK=="gsmmodem", тогда появится симлинк /dev/gsmmodem.
А ATTR{serial} или подобные нужны, чтобы не спутать разные устройства.
Получить серийник и прочие аттрибуты: udevadm info -a -n /dev/ttyACM*
UPD
Можно сделать так, чтобы из KERNEL=="ttyACM*" передавался номер устройства (цифра под звёздочкой) в сервис, которая бы потом дополняла бы строку запуска? 2.a Или будет всё же проще и лучше написать 2 демона (modem0.service и modem1.service) для ACM0 и ACM1?
Для этого тебе нужен не просто сервис, а шаблонный сервис. Для этого переименуй my.service в my@.service и запускать их мы будем как my@ttyACM0.service и my@ttyACM1.service, тогда внутри файла тебе будут доступны такие переменные как %i %I (которые и будут именем сервиса, тот что после @). Меняешь все упоминания ttyACMX на %I и наслаждаешься =)
Внутри udev доступны операторы %k and %n, это имя и номер соответственно (ты просил именно %k). Соответственно, исправляешь ENV{SYSTEMD_WANTS}="my@%k.service" и будет стартовать сервис my@ttyACM0.service
По поводу отключения. Тут сложнее, потому что когда устройство отключается, то его параметры становятся недоступны и уже сложно понять что же было отключено, можешь запустить udevadm monitor --environment --udev и выдернуть устройство, должен увидеть что-то типа
#######REMOVE#######
monitor will print the received events for: UDEV - the event which udev sends out after rule processing
UDEV [21800.789239] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/input/input75/mouse1 (input) ACTION=remove DEVLINKS=/dev/input/by-id/usb-Logitech_USB_Receiver-mouse /dev/input/by-path/pci-0000:00:1d.0-usb-0:1.3:1.0-mouse DEVNAME=/dev/input/mouse1 DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/input/input75/mouse1 ID_BUS=usb ID_INPUT=1 ID_INPUT_MOUSE=1 ID_MODEL=USB_Receiver ID_MODEL_ENC=USB\x20Receiver ID_MODEL_ID=c51a ID_PATH=pci-0000:00:1d.0-usb-0:1.3:1.0 ID_PATH_TAG=pci-0000_00_1d_0-usb-0_1_3_1_0 ID_REVISION=4100 ID_SERIAL=Logitech_USB_Receiver ID_TYPE=hid ID_USB_DRIVER=usbhid ID_USB_INTERFACES=:030102:030000: ID_USB_INTERFACE_NUM=00 ID_VENDOR=Logitech ID_VENDOR_ENC=Logitech ID_VENDOR_ID=046d MAJOR=13 MINOR=33 SEQNUM=3178 SUBSYSTEM=input UDEV_LOG=3 USEC_INITIALIZED=21797014959
UDEV [21800.792866] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/input/input75/event6 (input) ACTION=remove DEVLINKS=/dev/input/by-id/usb-Logitech_USB_Receiver-event-mouse /dev/input/by-path/pci-0000:00:1d.0-usb-0:1.3:1.0-event-mouse DEVNAME=/dev/input/event6 DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/input/input75/event6 ID_BUS=usb ID_INPUT=1 ID_INPUT_MOUSE=1 ID_MODEL=USB_Receiver ID_MODEL_ENC=USB\x20Receiver ID_MODEL_ID=c51a ID_PATH=pci-0000:00:1d.0-usb-0:1.3:1.0 ID_PATH_TAG=pci-0000_00_1d_0-usb-0_1_3_1_0 ID_REVISION=4100 ID_SERIAL=Logitech_USB_Receiver ID_TYPE=hid ID_USB_DRIVER=usbhid ID_USB_INTERFACES=:030102:030000: ID_USB_INTERFACE_NUM=00 ID_VENDOR=Logitech ID_VENDOR_ENC=Logitech ID_VENDOR_ID=046d MAJOR=13 MINOR=70 SEQNUM=3179 SUBSYSTEM=input UDEV_LOG=3 USEC_INITIALIZED=21797013896
UDEV [21800.797061] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/input/input75 (input) ACTION=remove DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/input/input75 EV=17 ID_BUS=usb ID_INPUT=1 ID_INPUT_MOUSE=1 ID_MODEL=USB_Receiver ID_MODEL_ENC=USB\x20Receiver ID_MODEL_ID=c51a ID_PATH=pci-0000:00:1d.0-usb-0:1.3:1.0 ID_PATH_TAG=pci-0000_00_1d_0-usb-0_1_3_1_0 ID_REVISION=4100 ID_SERIAL=Logitech_USB_Receiver ID_TYPE=hid ID_USB_DRIVER=usbhid ID_USB_INTERFACES=:030102:030000: ID_USB_INTERFACE_NUM=00 ID_VENDOR=Logitech ID_VENDOR_ENC=Logitech ID_VENDOR_ID=046d KEY=ffff0000 0 0 0 0 0 0 0 0 MODALIAS=input:b0003v046DpC51Ae0111-e0,1,2,4,k110,111,112,113,114,115,116,117,118,119,11A,11B,11C,11D,11E,11F,r0,1,6,8,am4,lsfw MSC=10 NAME="Logitech USB Receiver" PHYS="usb-0000:00:1d.0-1.3/input0" PRODUCT=3/46d/c51a/111 PROP=0 REL=143 SEQNUM=3180 SUBSYSTEM=input UDEV_LOG=3 UNIQ="" USEC_INITIALIZED=21796479085
UDEV [21800.797132] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/0003:046D:C51A.004D/hidraw/hidraw2 (hidraw) ACTION=remove DEVNAME=/dev/hidraw2 DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/0003:046D:C51A.004D/hidraw/hidraw2 MAJOR=251 MINOR=2 SEQNUM=3181 SUBSYSTEM=hidraw UDEV_LOG=3 USEC_INITIALIZED=21800796369
UDEV [21800.797172] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/0003:046D:C51A.004D (hid) ACTION=remove DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/0003:046D:C51A.004D HID_ID=0003:0000046D:0000C51A HID_NAME=Logitech USB Receiver HID_PHYS=usb-0000:00:1d.0-1.3/input0 MODALIAS=hid:b0003g0001v0000046Dp0000C51A SEQNUM=3182 SUBSYSTEM=hid UDEV_LOG=3 USEC_INITIALIZED=21800796501
UDEV [21800.798537] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0 (usb) ACTION=remove DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0 DEVTYPE=usb_interface INTERFACE=3/1/2 MODALIAS=usb:v046DpC51Ad4100dc00dsc00dp00ic03isc01ip02in00 PRODUCT=46d/c51a/4100 SEQNUM=3183 SUBSYSTEM=usb TYPE=0/0/0 UDEV_LOG=3 USEC_INITIALIZED=21800796538
UDEV [21800.814181] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/input/input76/event7 (input) ACTION=remove DEVLINKS=/dev/input/by-id/usb-Logitech_USB_Receiver-event-if01 /dev/input/by-path/pci-0000:00:1d.0-usb-0:1.3:1.1-event DEVNAME=/dev/input/event7 DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/input/input76/event7 ID_BUS=usb ID_INPUT=1 ID_INPUT_KEY=1 ID_MODEL=USB_Receiver ID_MODEL_ENC=USB\x20Receiver ID_MODEL_ID=c51a ID_PATH=pci-0000:00:1d.0-usb-0:1.3:1.1 ID_PATH_TAG=pci-0000_00_1d_0-usb-0_1_3_1_1 ID_REVISION=4100 ID_SERIAL=Logitech_USB_Receiver ID_TYPE=hid ID_USB_DRIVER=usbhid ID_USB_INTERFACES=:030102:030000: ID_USB_INTERFACE_NUM=01 ID_VENDOR=Logitech ID_VENDOR_ENC=Logitech ID_VENDOR_ID=046d MAJOR=13 MINOR=71 SEQNUM=3184 SUBSYSTEM=input UDEV_LOG=3 USEC_INITIALIZED=21797013933 XKBLAYOUT=de XKBMODEL=pc105
UDEV [21800.816765] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/input/input76 (input) ABS=1 0 ACTION=remove DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/input/input76 EV=1f ID_BUS=usb ID_INPUT=1 ID_INPUT_KEY=1 ID_MODEL=USB_Receiver ID_MODEL_ENC=USB\x20Receiver ID_MODEL_ID=c51a ID_PATH=pci-0000:00:1d.0-usb-0:1.3:1.1 ID_PATH_TAG=pci-0000_00_1d_0-usb-0_1_3_1_1 ID_REVISION=4100 ID_SERIAL=Logitech_USB_Receiver ID_TYPE=hid ID_USB_DRIVER=usbhid ID_USB_INTERFACES=:030102:030000: ID_USB_INTERFACE_NUM=01 ID_VENDOR=Logitech ID_VENDOR_ENC=Logitech ID_VENDOR_ID=046d KEY=4837fff 72ff32d bf544446 0 0 1 20f90 8b17c000 677bfa d9415fed 9ed680 4400 0 10000002 MODALIAS=input:b0003v046DpC51Ae0111-e0,1,2,3,4,k71,72,73,74,77,80,82,83,85,86,87,88,89,8A,8B,8C,8E,90,96,98,9B,9C,9E,9F,A1,A3,A4,A5,A6,A7,A8,A9,AB,AC,AD,AE,B0,B1,B2,B5,B6,CE,CF,D0,D1,D2,D4,D8,D9,DB,DF,E4,E7,E8,E9,EA,EB,F1,100,161,162,166,16A,16E,172,174,176,178,179,17A,17B,17C,17D,17F,180,182,183,185,188,189,18C,18D,18E,18F,190,191,192,193,195,198,199,19A,1A0,1A1,1A2,1A3,1A4,1A5,1A6,1A7,1A8,1A9,1AA,1AB,1AC,1AD,1AE,1B0,1B1,1B7,1BA,r6,a20,m4,lsfw MSC=10 NAME="Logitech USB Receiver" PHYS="usb-0000:00:1d.0-1.3/input1" PRODUCT=3/46d/c51a/111 PROP=0 REL=40 SEQNUM=3185 SUBSYSTEM=input UDEV_LOG=3 UNIQ="" USEC_INITIALIZED=21796482192
UDEV [21800.817249] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/usbmisc/hiddev1 (usbmisc) ACTION=remove DEVNAME=/dev/usb/hiddev1 DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/usbmisc/hiddev1 MAJOR=180 MINOR=1 SEQNUM=3186 SUBSYSTEM=usbmisc UDEV_LOG=3 USEC_INITIALIZED=21800816392
UDEV [21800.818490] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/0003:046D:C51A.004E/hidraw/hidraw3 (hidraw) ACTION=remove DEVNAME=/dev/hidraw3 DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/0003:046D:C51A.004E/hidraw/hidraw3 MAJOR=251 MINOR=3 SEQNUM=3187 SUBSYSTEM=hidraw UDEV_LOG=3 USEC_INITIALIZED=21800816472
UDEV [21800.818536] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/0003:046D:C51A.004E (hid) ACTION=remove DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/0003:046D:C51A.004E HID_ID=0003:0000046D:0000C51A HID_NAME=Logitech USB Receiver HID_PHYS=usb-0000:00:1d.0-1.3/input1 MODALIAS=hid:b0003g0001v0000046Dp0000C51A SEQNUM=3188 SUBSYSTEM=hid UDEV_LOG=3 USEC_INITIALIZED=21800816908
UDEV [21800.818580] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1 (usb) ACTION=remove DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1 DEVTYPE=usb_interface INTERFACE=3/0/0 MODALIAS=usb:v046DpC51Ad4100dc00dsc00dp00ic03isc00ip00in01 PRODUCT=46d/c51a/4100 SEQNUM=3189 SUBSYSTEM=usb TYPE=0/0/0 UDEV_LOG=3 USEC_INITIALIZED=21800816945
UDEV [21800.818645] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3 (usb) ACTION=remove BUSNUM=002 DEVNAME=/dev/bus/usb/002/038 DEVNUM=038 DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3 DEVTYPE=usb_device ID_BUS=usb ID_MODEL=USB_Receiver ID_MODEL_ENC=USB\x20Receiver ID_MODEL_ID=c51a ID_REVISION=4100 ID_SERIAL=Logitech_USB_Receiver ID_USB_INTERFACES=:030102:030000: ID_VENDOR=Logitech ID_VENDOR_ENC=Logitech ID_VENDOR_ID=046d MAJOR=189 MINOR=165 PRODUCT=46d/c51a/4100 SEQNUM=3190 SUBSYSTEM=usb TYPE=0/0/0 UDEV_LOG=3 UPOWER_VENDOR=Logitech, Inc. USEC_INITIALIZED=21796488421
и понять что там к чему. Добавить к существующему правилу ACTION=="add" тогда оно будет срабатывать только при подключении устройства. А вот что писать в ACTION=="remove" что бы systemd стопил сервис - я не знаю. Но мне кажется, остановки с помощью BindsTo= вполне достаточно.
Ответ на 3) При выполнении /usr/sbin/pppd call modem он разве не завершается? Там другой процесс порождается демоном, но systemd об этом ничего не знает. Если хочется, то можете попробывать заставить systemd мониторить процесс с помощью Type=forking и, желательно, PIDFile=. Как там работает pppd я не в курсе, может поможет GuessMainPID=yes (хотя если PIDFile пуст, то он по умолчанию вкл).
По управлению udev:
Может быть полезно сгенерировать различные события udev. Например, вы хотите симулировать отключение USB-устройства на удаленной машине. В таких случаях, используйте udevadm trigger: udevadm trigger -v -t subsystems -c remove -s usb -a "idVendor=id_поставщика"
У тебя соответственно -s ttyACM. Хотя тут я не уверен, потому что сам плохо знаю этот момент.

Захват и демонстрация экрана JS

Хочу понять возможности захвата или демонстрации экрана для JS. В сервисе krible.ru реализована следующая возможность:
показывать страницу, которую смотрит оператор видеть страницу, которую смотрит клиент перемещать посетителя к выделенной части страницы можно выделить текст и он будет выделен у клиента
Какими инструментами можно реализовать такие возможности, кто работал с подобным функционалом? Может быть ссылки на какие-то готовые библиотеки или сайты где можно почитать об этом.


Ответ

У меня для вас две новости: хорошая и плохая.
Начну с плохой: полностью готовых открытых решений не существует (как минимум они мне не известны).
А хорошая новость заключается в том, что существующих свободных инструментов достаточно, чтобы построить необходимое вам решение.

Расскажу, как я решал подобную задачу, возможно мой опыт вам чем-то поможет.
Итак, из всех очевидных решений, я предпочел использовать передачу DOM дерева от клиента к оператору и трансляцию сообщений мутаций дерева и пользовательских действий. Этот подход позволяет абстрагироваться от сложности целевого сайта и сделать решение максимально гибким и универсальным.
Всю задачу можно условно разбить на несколько частей:
Получить дерево DOM на клиенте и сериализовать его Передать его оператору Восстановить дерево у оператора Подхватывать мутации дерева у клиента и передавать их оператору (Опционально) Передавать пользовательские события (прокрутка, навигация, нажатие клавиш и прочее) от клиента оператору (Опционально) Передавать выделение текста от оператора клиенту
1. Получение дерева DOM и его сериализация:
Получение самого дерева - тривиальная задача. Для этого можете использовать стандартный JS DOM API. Начать можно с корневого узла:
var root = document;
Обходя дочерние элементы этого узла, и выкидывая потенциально проблемные/бесполезные узлы (вроде