Форум » » C# и Websocket. Нужна помощь. » Ответить

C# и Websocket. Нужна помощь.

Paul_T: В C# пока полный 0, но изучать надо... Решил написать модуль для управления LG телевизора, работающий на Webos. Коммуникация ТВ осуществляется по Websocket на порт 3000. В библиотеках SIMPL# есть класс WebSocketClient, но мне показалось он несколько скуднее на методы, чем такой-же клас в библиотеках .NET. Ну да ладно, как я понимаю, сторонние библиотеки не прокатят и нужно пользоваться только теми, что описаны в SIMPL# (ну, кроме System, видимо). Написал клиентскую часть, соединение с сервером, получение клиентского ключа, сохранение ключа в файле, реализовал несколько команд управления. Работает! громкостью управляет, это уже хорошо, можно развивать дальше, но оказалось все не так просто. После простоя в минуту-две (отсутствие посылок клиент-сервер) сервер перестает отвечать на запросы, при этом метот Send(...) возвращает код WEBSOCKET_CLIENT_SUCCESS. Помогает только переподключение, т.е. нужно разорвать соединение и подключиться заново. За основу своего модуля брал простейший модуль написанный на JS, все сообщения соответствуют этому модулю. JS модуль работает в браузере без ограничения времени, поэтому предположу, что проблема не на стороне телеыизора. Но как ее найти, не понимаю... Может нужно делать асинхронное соединение? Но как это сделать методами SIMPL# так пока и не понял. Может кто сталкивался? Спасибо за любую помощь! PS. Еще заметил, что метод Read() выполняется бесконечно долго при отсутствии ответа от сервера. Может он имеет какие либо таймауты? Но я этого не нашел.

Ответов - 63, стр: 1 2 3 4 All

Вячеслав: 1.Можно попробовать не поддерживать соединение все время, а подключаться только для передачи команды. Если задержка на подключение будет приемлема 2.Подсмотреть WIRESHRK-ом в момент потери связи, что отвечает или не отвечает сервер на ТВ. Либо в дебаг выводить RX с сервера, если это проще чем сниффером.

Paul_T: Вячеслав пишет: 1 - не вариант, так как долго подключается, а после этого нужно делать верификацию ключа итд. Вариант плохой. 2 - не совсем понимаю как ловить пакеты не с компа, на который установлен wireshark, можно ли вообще это сделать? Все данные, что шлются/принимаются websocket клиентом вывожу в консоль, это не помогает, т.к. там нифига не видно, что реально передает сервер, а только то, что получил клиент через сокет без заголовков. т.е. только саму значимую часть... Видимо, чтобы читать все, что исходит от сервера нужно асинхронное соединение, а это пока не понятно как сделать... Понимаю, что нужен какой-то EventHandler, но ввиду отсутствия примеров и скудного крестроновкого хелпа, пока не понимаю, как это реализовать. PS Связь не рвется, статус 'подключено' не меняется, ошибки при отправке сообщения не возникает. Возможно надо слать PING серверу периодично. Но мне кажется, что это должно быть реализовано в самом методе websocket. Ибо на пинг, посланный прогой, сервер тоже не отвечает.

olegny: Никогда не работал с WebSocketClient, но из того, что я вижу: - Не совсем понял почему асинхронное соедитение кажется вам панацеей. По сути оно ничем не отличается от обычного кроме того, что вызовы оных методов не блокируют программу и вы можете что-то еще делать пока ожидаете данных или события в отличии от, например, того же блокируещего метода Receive() (который вы почему-то назваетe Read()?). - асинхронно вы работаете с WebSocketClient или синхронно, от этого серверу (телефизору) ни холодно, ни жарко. - для асинхронной работы нужно задать свои callbacks с помощью ConnectionCallBack, DisconnectCallBack, ReceiveCallBack, SendCallBack пропертей, которые WebSocketClient и позовет когда соотвествующая операция выполнится. - чтобы ловить весь Ethernet трафик между контроллером и телевизором вам понадобится простой hub между ними и подключенный туда же комп с wireshark. Если нет хаба, то можно использоваться управляемый (managed) Ethernet switch на котором надо настроить настроить зеркалирование портов. - Проверьте значение KeepAlive, оно должно быть True - И еще надо убедиться, что обе стороны правильно играют в ping-pong (согласно 5.5.2/5.5.3 RFC 6455) что будет видно в wireshark


Paul_T: olegny пишет: Спасибо за ответ. - Идея асинхронного метода родилась в следствии того, что при запросе пользовательского ключа сначала приходит ответ, что запрос получен, а потом пользователь на телевизоре должен разрешить подключение устройства и приходит еще один ответ от телевизора, собственно, с самим ключем. Т.е. ответ без запроса фактически. - Метод, конечно Receive(). Писал по памяти и ошибся. - В любом случае нужно попробовать асинхронный метод, ибо решил реализовать такой модуль ради практического изучения SIMPL# - Все устройства в одном неуправляемом свитче (самый простой. Свитч он или Хаб даже не знаю ))) , бьюсь, но пока траффика между контроллером и ТВ не видно... Видимо надо бежать за управляемым коммутатором... - KeepAlive - True, так и есть. - Вчера посмотрел Wireshark тот JS модуль, с которого брал пример. Он получает Ping с периодичностью примерно минуту и отвечает. При этом в самой программе этого нет. Т.е. это реализовано в самом подключении(методе?). В JS вроде как нельзая отправить сообщение с Opcode "Ping/Pong", а вот в C# Opcode один из параметров метода Send. И, видимо, не с проста. Но пока не получилось... Буду смотреть Wireshark, шлются ли запросы Ping. Спасибо!

Paul_T: Методом 'научного тыка' удалось понять, что крестроновская библиотека не отвечает PONG на PING телевизора. Поэтому я с периодичностью 30 сек шлю телевизору PONG с пустым телом. И о чудо, он не засыпает... Метод, понимаю, так себе... Но пока другой реализации не вижу.

olegny: Да, похоже там лажа с ping/pong в клиенте. Надо им баг открыть на это, ибо - безобразие! Наверное другого способа и нет если сам клиент вам ping от телека не отдает ни синхронно, ни асинхронно... С управляемым свичем еще возиться придется, а старенький хаб где-нить на барахолке можно найти за три копейки я думаю. Вещь в хозяйстве полезная именно для таких случаев! Впрочем крестроновский свитч делает зекралирование портов легко и приятно... ;)

Paul_T: olegny пишет: Похоже на то. Синхронно получить фрейм Ping не получилось, да и это провально, нельзя же минуту ждать пинга и ничего не нажимать на телевизоре. Асинхронный метод пока не потянул. Надо углубиться в изучения. Я много странных вещей вижу в Simpl# может косяки, а может из-за своей некомпетенции. Например свойство WebSocketClient.Connected возвращает 'true' даже если выключить телевизор. А false если разорвать соединение со стороны клиента методом Disconnect. Зачем оно тогда вообще нужно? Потом у меня перестали работать делегаты для передачи состояния в Simpl+. Так и не понял причину, компиляция без ошибок. Один работает, пишешь второй (который никак не связан) перестают работать оба!!! Я собрал все устройства дома, что имеют более 1 разьема RJ45, ничто не заработало. Управляемый свитч найти/купить проще. Ибо я не знаю, как найти старенький хаб, нельзя же скупить все авито в надежде, что что-то заработает )))

olegny: М-да... Я знаю чувака, который все это понаписал для web sockets. Я бы его близко к программированию не допускал! )) Теоретически синхронный Receive тоже запускается в неблокирующем режиме так, что можно было бы использовать его для работы по опросу, но если вы говорите, что он все равно блокируется, то значит нет другого способа узнать есть уже что-то на входе или нет... (( Надо пробовать асинхронные методы! У вас что-то не получается с ними или просто руки пока не доходят? WebSocketClient.Connected это штука зачастую неадекватна реальному состоянию сокета. Например если, как в вашем случае, просто выключить телек, то оный, скорее всего, просто забудет об открытом соединении и ничего не сделает, чтобы его правильно закрыть. Это - типичная ситуация с abandoned TCP connection. В этом случае у другой стороны нет возможности узнать что сервер просто "свалил" непопрощавшись кроме, как послать ему что-нибудь... для этих дел и существуют всякие keepalive, ping/pong и прочие механизмы. Метод Disconnect же делает все правильно и отсюда - false. C Simpl+ помочь врядли смогу ибо надо копать, а времени нет. Если пришлете кусок кода, что могу бысто посмотреть что может быть не так... Я реально имел ввиду купить hub на каком-нибудь радио рынке. Но если на авито написано HUB, а не SWITCH, то скорее всего это оно! )))

Paul_T: Да, метод Receive ,блокирует все выполнение программы на неопределенное время. Даже таймаутов вроде никаких нет (как параметр). С асинхронными методами пока не делал, но и не понимаю, что вообще надо писать ))). Буду изучать, попробую на днях. Да, по WebSocketClient.Connected понятно. Пока не понятно, как отслеживать состояние телевизора. Может обычным Ping'ом? Выключается он полностью и включается только по WOL. Делегаты в Simpl#, чтобы передавать состояние в Simpl+. И загвоздка была в Simpl# как раз. Но может и я, что не так делал. Копать не прошу, сейчас вроде все работает. Ну, попадется, куплю. Но проще в магазине управляемый свитч купить небольшой с port mirroring... Код смогу выложить, как только приведу в более-менее приличное состояние. Там сейчас такой 'быдлокод', стыдно показывть )))

olegny: Ok. Удачи! Обращайтесь если что у Крестрона не так... ;)

Вячеслав: Пожалуй, самый компактный вариант для зеркалирования это какой-нибудь микротик свитч (на ваш выбор, зеркало они думаю все умеют делать) click here Могу подарить HP HUB (в Москве), но он не компактный. А вы скиньте, если не жалко модуль #. Тоже пытаюсь изучать.

Paul_T: Вячеслав пишет: Мне еще микротика не хватало ))). Да обычно зеркалирование указывается в ТТХ. Куплю какой-нибудь... Все таки интересно, что там происходит... Модуль обещаю выложить с основным функционалом, как только приведу в порядок. Все таки 1 опыт C#... Но есть желание получить полностью рабочий инструмент, ибо дома LG ))) Если, кто еще видел API управления для WebOs, буду блаагодарен. А то я все уже облазил, только отрывками а на http://webostv.developer.lge.com описание Luna Servise API, думаю это не совсем то. У меня в модуле команды формата ssap://audio/volumeUp в JSON посылке. Вот пример: {\"type\":\"request\",\"id\":\"volumeup_" + i +"\",\"uri\":\"ssap://audio/volumeUp\"} Но основное все есть. Еще можно разобрать модуль MajorDoMo, на него есть исходники. Ну и GitHub помогает, там и для Crestron можно найти кое что )

Paul_T: Зашел в "дебри". API для "простого" управления довольно скуден. Можно крутить громкость, переключать входы, каналы (вверх/вниз), получить списко каналов, список приложений. Команды управления для передачи кнопок клавиатуры, указателя координат мышки, вверх/вниз/вправо/влево передаются ввиду сообщений по отдельному Сокету. Сокет я получаю от телека ввиде: {"type":"response","payload":{"socketPath":"ws:/resources/c71caedade95e3bcb349163e36b3edcf368a534e/netinput.pointer.sock","returnValue":true}} А, что дальше с ним делать не знаю. Тот пример, что есть в интернетах использует класс MessageWebSocket() из библиотеки Windows.Networking.Sockets. Ничего похожего в библиотеках Simlp# нет. Это тупик?

olegny: Ну да, у Крестрона этой абстракции (сообщения по вебсокетам) нет, ну и что? Это мало что дает вам! Напишите свой класс для отправки и получения сообщений целиком, а не кусочками - вот и вся суть MessageWebSocket... Тем более, что у вас там json, который не трудно парсить и проверять на синтаксис. Вы же как-то выделили приведенное выше сообщение из потока данных от WebSocketClient! Ну вот так и делайте, только программно и в виде класса... ;) Труднее обстоит дело с самим URI от телека в виде "ws:/..../netinput.pointer.sock". Я не знаю как оно там все устроено у LG, но данный uri выглядит как путь к линуксовому файлу-сокету и что с этим делать - х.з. Вы же к нему не имеете доступа чтобы открыть оный сокет и "поговорить" с телеком? )) Другими словами, ваша проблема не в Крестроне, а в LG. Точнее в описании его API и примерах...

Paul_T: olegny пишет: Да. Просто оказалось все несколько сложнее. Так и есть этот URI путь к сокету. Метод MessageWebSocket.ConnectAsync(Uri) как раз в аргументах имеет такой URI. Как удалось выяснить (я все таки купил управляемы коммутатор) механизм подключения к такому сокету одинаков, за исключением того, что в HTTP заголовке в поле GET передается этот URI. Но опять же у Simpl# WebSocketClient нет такого свойства, которое может записать этот URI в поле GET.

olegny: В смысле этот параметр называется так же? ;) По сути-то это два совсем разных URI. Один - для случая когда ваш клиент сидит на той же самой машине, а другой (который вам нужен) для соединения по сети (<host>:<port>). Говорю же, MessageWebSocket вам здесь совсем не важен и мало что дает - можно обойтись и без этого уровня абстракции.

Paul_T: olegny пишет: Думаю нет. Сервер отдает этот URI именно для подключения по сети. Приложение для LG TV из Microsoft Market делает именно так. Такой-же HTTP запрос на подключение, но в поле GET вписан этот URI. Суть в том, (предположительно) что можно было бы подключиться как обычный CrestronWebSocketClient используя этот URI к Сокету, если бы в заголовке в поле GET можно было бы вписать этот URI. Вот примет HTTP запроса, что шлет это приложение. ¨#þ¶¥X`næ'ìE&ý@@À¨2À¨yô ¸¸>Gª.ËP… GET /resources/dac862b930d3cd8aee47f8653db1faeee201ba60/netinput.pointer.sock HTTP/1.1 Sec-WebSocket-Key: aN/Q7VJaMS0KWvR0gIVN3g== Connection: Upgrade Upgrade: websocket Sec-WebSocket-Version: 13 Host: 192.168.1.121:3000 Cache-Control: no-cache Т.е. Хост и порт тот-же и он там есть. В обычном подключении HTTP запрос такой-же, но поле GET пустое. Для этого, походу, придется писать свой WebSocketClient???

DmitriiP: перед самописанием попробуйте как вариант Mono порт SSharpWebSocketClientLibrary.dll от Neil Colvin, может подойдёт для ваших целей http://www.nivloc.com/downloads/crestron/SSharp/include4.dat%20=%202.10.029%20-%20Plugin%202.x/DLLs/

Paul_T: DmitriiP пишет: Так вот я до сих пор не понимаю, можно ли использовать сторонние библиотеки в проекте библиотеки Simpl#. Не думаю, что прокатит. Иначе зачем включать в Simpl# свой websocket, когда он есть стандартной System.net...

DmitriiP: совсем совсем сторонние C# использовать неполучится, а другие Simpl# библиотеки использовать никто не запрещает да и CrestronWebSocketClient это также не родной MS WebSocketClient а Simpl# обёртка

olegny: Paul_T пишет: Для этого, походу, придется писать свой WebSocketClient??? Зачем? Если вы сделаете как написано ниже, то поле GET в HTTP запросе будет пустое? https://stackoverflow.com/questions/10406930/how-to-construct-a-websocket-uri-relative-to-the-page-uri Например что-то типа: ws://192.168.1.121:3000/resources/c71caedade95e3bcb349163e36b3edcf368a534e/netinput.pointer.sock

Paul_T: В Simpl# URL и Port задается в свойствах. Там не получится написать такую конструкцию. Т.е. мы задаем свойства, а потом вызываем метод Connect() без аргументов... WebSocketClient Client; Client.URL = addressToConnectTo; Client.Port = port; WebSocketClient.WEBSOCKET_RESULT_CODES conn = Client.Connect(); ('string addressToConnectTo' и 'uint Port' я передаю из Simpl)

Paul_T: DmitriiP пишет: да и CrestronWebSocketClient это также не родной MS WebSocketClient а Simpl# обёртка В том то и дело, что приходится использовать Simpl# обертку (библиотеку) а не System.Net.WebSockets

olegny: 1. CrestronWebSocketClient никакая не обертка потому, что в .NET Compact Framework 3.5 нет System.Net.WebSockets. Подождите 4 Series где вас возможно выпустят из песочницы и резвитесь как хотите с полноценным Mono... )) 2. Какая разница как задаются элементы URI если в конечном итоге все это парсится и склеивается если надо. .Port, .Host и .URL в качестве URI это все, что вам нужно. Если поле в GET пустное, то значит ваш URL задан неправильно... Попробуйте задать 3000, 192.168.1.121 и /resources/c71caedade95e3bcb349163e36b3edcf368a534e/netinput.pointer.sock соответственно.

Paul_T: olegny пишет: Так некуда этот netinput.pointer.socket задавать. Я уже по всякому пробовал. Никак не хотет правильный запрос на подключение слать.. Зы. Кстати, крестрон 1 раз шлет Pong от балды, а потом молчит на Ping и сервер рвет соединение... Wireshark, кстати, расшифровывает пакеты websocket с маской. Что радует

olegny: Как это? Чисто через URL запихнуть не получается? Приведите все значения .Port, .Host и .URL и что получилось в результате в HTTP GET (wireshark?) Я посмотрю что там происходит на самом деле... P.S. Ну с PING/PONG там лажа. Это уже понятно! Надо чтобы кто-то зафалил им багу...

Paul_T: olegny пишет: .URL = "ws://192.168.1.121" .Port = 3000 .Host = тот URI, что приходит с сервера, но без "ws:" в начале ¨#þ¶¥X8¿XEJ@Ù´À¨'À¨yÂ÷ ¸ë=èÙÎP,'Ó GET / HTTP/1.1 Host: /resources/a4c087eb85bd01a0d46ed1e4b77d5e7dd15e7b02/netinput.pointer.sock Upgrade: websocket Connection: Keep-Alive, Upgrade Sec-WebSocket-Key: wngrQIV/pinlN7NXVwc3Pw== Sec-WebSocket-Version: 13 Host пишется отдельным Тэгом, не в GET

olegny: Ну и? Чего вы после этого ожидаете? ;) Попробуйте: .URL = тот URI, что приходит с сервера .Port = 3000 .Host = "192.168.1.121 И при этом выполните команду "SHOWEXTRA ENET ON" и пришлите "ERR SYS" с консоли.

Paul_T: olegny пишет: Да, меня это тоже смущало по началу. Но я соединяюсь с сервером и работают основные команды... Это уже второе подключение для клавиатуры и мышки. Если ws://192.168.1.121 писать в Host, то соединения нет ))) С тем запросом, что выше, соединение тоже устанавливается. Только не туда, куда надо... Из Хелпа "Set/Get Host is used in the Host field of the WebSocket handshake header. Do not add carriage return and line feed."

olegny: Не надо туда ws:// писать! )) В "err sys" все будет видно где ошибка...

Paul_T: olegny пишет: Если делать так: .URL = тот URI, что приходит с сервера .Port = 3000 .Host = "192.168.1.121 то вот: SYSTEM LOG: 1. Exception 'Data Abort' (4): Thread-Id=098401fa(pth=895e31b0), Proc-Id=05d1033a(pprc=86904000) 'splusmanagerapp.exe', VM-active=05d1033a(pprc=86904000) 'splusmanagerapp.exe' 2. PC=001cc9f8(???+0x001cc9f8) RA=00000000(???+0x00000000) SP=028efaa8, BVA=00000000 3. Info: splusmanagerapp.exe # 2019-04-20 12:32:59 # Close connection handle 79084d3e, reason 1000 4. Info: splusmanagerapp.exe # 2019-04-20 12:32:59 # WebSocket Client Error: error occured while closing ret=-1 Total Msgs Logged = 4 Не подключается, выскакивают исключения...

Paul_T: Немного опишу ход программы, а то мы уйдем в дебри. 1. Подключаюсь к серверу с такими параметрами: .URL = "ws://192.168.1.121" .Port = 3000 2. Проверяю ключ, что шлет сервер с тем, что хранится в файле 3. Все команды, что доступны при работе с основным подключением работают (громкость, каналы итд.) 4. Запрашиваю у сервера путь к сокету для 'мышки и клавиатуры". Он как раз присылает этот путь на сокет. До этого момента все работает как надо. Сервер отвечает на любые правильные запросы в JSON формате. Далее нам нужно 2 подключение с этим путем на Сокет. Т.е. фактически это 2-е websocket подключение. Которое пока не получается. Это подключение для отправки сообщений, которые уже идут не как JSON обьекты, а просто текстовые сообщения. Вот пока не получается подключиться как надо.

olegny: Пробуйте второе подключение как я описал выше. Удачи!

Paul_T: Попробовал. Не подключается вообще. Завтра попробую более основательно. Спасибо!!!

olegny: Ну тогда в .URL запихните все: ws://192.168.1.121:3000/resources/c71caedade95e3bcb349163e36b3edcf368a534e/netinput.pointer.sock плюс в .Host чистый 192.168.1.121, а в .Port = 3000

Paul_T: olegny пишет: Что бы я без Вас делал? Все верно! Для основного клиента я прописал: Client.Port = 3000; Client.Host = "192.168.1.121"; Client.ULR = "ws://192.168.1.121"; Таким образом, сервер при запросе URI на сокет возвращает: ws://192.168.1.121/resources/ec462d00ea95ca666f08d1e8043d0af61f6a0b9e/netinput.pointer.sock Соответственно для клиента 'Мышки": Mouse.Port = 3000; Mouse.Host = "192.168.1.121"; Mouse.Url = "ws://192.168.1.121/resources/ec462d00ea95ca666f08d1e8043d0af61f6a0b9e/netinput.pointer.sock" Т.е. ровно то, что вернул сервер. Теперь посылка для подключения к Сокету мышки выглядит так: ¨#þ¶¥X8¿XEq.@ÅÀ¨'À¨yÃB ¸EO©ç5@¸P,!¿ GET /resources/ec462d00ea95ca666f08d1e8043d0af61f6a0b9e/netinput.pointer.sock HTTP/1.1 Host: 192.168.1.121 Upgrade: websocket Connection: Keep-Alive, Upgrade Sec-WebSocket-Key: tm3oWM5IV2T7EIgK6mdWWA== Sec-WebSocket-Version: 13 И, да, ВСЕ работает!!! Осталось только слать PONG в "Сокет Мышки", т.к. сервер разрывает соединение ровно так-же как с основным Сокетом. Спасибо огромное! PS Теперь не совсем понимаю, как парсить ответы от сервера )))

olegny: Да, уж... PONG надо бы поправить им! Но вообще хорошо, а то было уже подумал, что у них там совсем лажа понаписана в клиенте... ;) А что там сервер возвращает-то?

Paul_T: olegny пишет: Кстати, с подключением похоже не все гладко. В логах сохраняется вот такое 1. Info: splusmanagerapp.exe # 2019-04-21 22:23:41 # WebSocketClientOpenSessionfc pURL: ws://192.168.1.121/ 2. Info: splusmanagerapp.exe # 2019-04-21 22:23:41 # WebSocketCopyClientRec: pURL ws://192.168.1.121/ 3. Info: splusmanagerapp.exe # 2019-04-21 22:23:41 # WebSocketURLConversion: ws://192.168.1.121/ 4. Info: splusmanagerapp.exe # 2019-04-21 22:23:41 # host:(null) ipaddr:192.168.1.121 path:/ 5. Info: CIPCommandProcessor.exe # 2019-04-21 22:23:42 # RetrieveSiteIPAddress: Cannot get hostname (ws://192.168.1.121/). Exception (No such host is known ) 6. Info: splusmanagerapp.exe # 2019-04-21 22:23:42 # WebSocketClientOpenSessionfc pURL: ws://192.168.1.121/resources/951f4d3368449969b9df87bf1f011a139744651a/netinput.pointer.sock 7. Info: splusmanagerapp.exe # 2019-04-21 22:23:42 # WebSocketCopyClientRec: pURL ws://192.168.1.121/resources/951f4d3368449969b9df87bf1f011a139744651a/netinput.pointer.sock 8. Info: splusmanagerapp.exe # 2019-04-21 22:23:42 # WebSocketURLConversion: ws://192.168.1.121/resources/951f4d3368449969b9df87bf1f011a139744651a/netinput.pointer.sock 9. Info: splusmanagerapp.exe # 2019-04-21 22:23:42 # host:(null) ipaddr:192.168.1.121 path:/resources/951f4d3368449969b9df87bf1f011a139744651a/netinput.pointer.sock 10. Info: CIPCommandProcessor.exe # 2019-04-21 22:23:43 # RetrieveSiteIPAddress: Cannot get hostname (ws://192.168.1.121/resources/951f4d3368449969b9df87bf1f011a139744651a/netinput.pointer.sock). Exception (No such host is known ) Как, говорил, задаю вот так: Client.URL = "ws://192.168.1.121; Client.Port = 3000; Client.Host = 192.168.1.121; Если URL задавать, как 192.168.1.121 или не задавать, то подключения не происходит. Кажется, что я уже попробовал все варианты, но может, что упустил. Надо еще раз попробовать попереставлять ))) Тем не менее подключения работают. Может, если сделать все правильно и PONG заработает? ))) Да нет, лажи у них нет. Это я просто ламмер ))). Пытаюсь освоить на начальном уровне... Что касается парсинга, я, вроде уже разобрался. Сервер может возвращать список каналов и приложений (среди которых, кстати входы и ТВ) в JSON формате: {"type":"response","payload":{"subscribed":false,"launchPoints":[{...},{...},{...},...,{...}}}; Из этого я получаю массив из JObject (элементы {...}б в которых куча ненужных и нужных элементов "id", "icon", "title" итд) JArray o = (JArray)JObject.Parse(rec_string)["payload"]["launchPoints"]; А, вот дальше мне нужно все это преобразовать в массив обьектов класса, который выглядит вот так: public class App { public string id { get; set; } public string icon { get; set; } public string title { get; set; } public string launchPointId { get; set; } } Получается несколько преобразований. string -> JArray -> поэлементно в string -> .net Object Мне кажется это не эффективно. Еще нарисовалась новая проблема))) В какой-то момент начинает все работать жутко медленно - начиная с подключения к серверу, заканчивая парсингом. Парсинг jArray длится 15 секунд. Почему это возникает пока не понятно. Все по началу вроде работает умеренно быстро, а потом "устает" Ps Пытаюст разобраться с асинхронной передачей, но пока ничего не понимаю. Да и примеров нет, близких к крестроновским методам...

olegny: В логе ничего страшного вроде нет, ибо это все только "info". Хотя, конечно, не понятно с чего бы вдруг CIP захотелось соединяться по указанному URL, но это наверное приколы Simpl+. Насчет неэффективности не знаю. Наверное можно что-то оптимизировать, но если работает и все успевает, то и фиг с ним... )) Парсить-то ведь как-то нужно все равно, но не вручную же! Когда начинает тормозить, посмотрите что там происходит следующими командами с консоли: RAMFREE CPULOAD (несколько раз) TASKSTAT

Paul_T: olegny пишет: Загрузка процессора 100% Причем 100 процентная загрузка возникает сразу после загрузки проекта. Т.е. без подкючения к websocket итд. App Name Proc ID Threads Heap Total/Used NK.EXE 0x00400002 93 3323416/2770104 udevice.exe 0x00FE0006 4 8192/5472 udevice.exe 0x01820006 1 8192/2496 udevice.exe 0x02C40002 1 8192/5056 udevice.exe 0x044C0002 4 36864/20032 udevice.exe 0x05150006 1 8192/2496 explorer.exe 0x051D0006 4 20480/12992 servicesd.exe 0x05830006 11 131072/92288 CrestronDllLoader.exe 0x067A0006 1 8192/1888 ConsoleServiceCE.exe 0x07520006 51 2052492/1910284 SystemCommandProcessor.exe 0x06280016 3 1089828/1000804 CRESLOG.exe 0x0702001A 5 159744/141344 SSHD.exe 0x07BE001A 4 249856/152576 TLDM.exe 0x061A001E 26 265764/234212 RfGateway.exe 0x07D0001E 158 332260/328612 CIPCommandProcessor.exe 0x08240002 14 1700212/1581908 CloudClient.exe 0x092C0006 10 2163076/1928932 CrestronTimerEventEngine.exe 0x087E000A 15 77880/67800 AutoUpdateInterface.exe 0x08CC000A 30 962788/911620 CustomAppManager.exe 0x094D000E 6 1138924/1058380 SimplSharpPro.exe 0x041F0026 25 3248804/3131780 LogicEngine.exe 0x085600B2 17 1726728/1715016 splusmanagerapp.exe 0x099C00E6 9 1745228/1661676 sftp-server.exe 0x084A0082 2 52168/40712 Спасибо огромное. Без Вас я бы еще месяц разбирался. А так фактически у меня все работает. Осталась только рутина ввиде описания всех необходимых команд, приведение всего в нормальный вид. Ну и понять асинхронное программирование ))).

olegny: А 100% все время держится? Надо несколько раз проверить в течении какого-то времени (минуты-другой), ибо может угомониться в конце концов. А без Simpl+ проги все нормально с CPULOAD? Хорошо было бы от вас получить оную апликуху, чтобы на тестовой железке проверить, а то мы тут с чем-то подобным сейчас разбираемся... )) Тем более, что и соединение с телеком устанавливать не надо для этого! Вы к этому асинхронному программированию относитесь как к священной корове, хотя там ничего особенного-то и нет... Делаете прямо вот так: WebSocketClient ws = new WebSocketClient(); ws.ReceiveCallBack = ((data, datalen, opcode, error) => { //DO WHATEVER YOU WANT HERE! return (int)WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS; }); Параметры оной лямбда-функции соответствуют параметрам и описанию делегата: // Summary: // Callback delegate for asynchronous data receive method for user application // // Parameters: // data: // received data // // datalen: // length of received data // // opcode: // opcode of packet type received // // error: // error code returned on callback // // Returns: // return WEBSOCKET_CLIENT_SUCCESS if successful public delegate int WebSocketClientAsyncReceiveCallback(byte[] data, uint datalen, WebSocketClient.WEBSOCKET_PACKET_TYPES opcode, WebSocketClient.WEBSOCKET_RESULT_CODES error); Потом просто зовете ReceiveAsync и забываете про него пока оный не позовет ваш callback, заданный выше, с принятыми данными. Аналогично поступаете и с другими Async методами в соответсвии с их делегатами, но сдается мне, что вам достаточно будет только асинхронного Receive.

Paul_T: olegny пишет: Да. 100% всегда. Но иной раз загрузишь и все работает до какого-то момента. Я еще толком не копался. Есть мысли. Надо попробовать. Проект скину на файлообменник. Но там пока лютый бардак... За пример спасибо. С остальным разберусь )

Viacheslav Alekseev: Где-то у вас там есть бесконечный цикл, в который программа сваливается сразу или через какое-то время. Отсюда и CPU Load 100%. Нередко они возникают в циклах, отвечающих за чтение или переподключение, когда Receive(), например, гоняется по кругу или Connect(). Или что-то подобное. Тут надо весь код целиком видеть.

olegny: Так, вроде, сказано было, что еще до подключения к websocket хотя все может быть конечно...

Paul_T: Все верно. В Simpl+ вкралась ошибка в цикле while. Заморочился на C# и сразу не увидел очевидного, что даже не касается основной программы.

olegny: Ну так неинтересно! Я-то думал это типа "Слушай. Обидно, клянусь! Обидно, ну! Ничего не сделал да— только вошёл..." ))

Paul_T: olegny пишет: Да, у нас так всегда ))) Сделал парсинг ответа с массивом приложений ТВ. Вчера работал, сегодня нет ))) создаю обьект и список обьектов типа App public class App { public string id { get; set; } public string icon { get; set; } public string title { get; set; } } public List<App> appList = new List<App>(); Далее выделяю из ответа "полезную нагрузку" - массив JToken. Далее пытаюсь заполнить список обьектов App значениями из массива JToken var app_array = JObject.Parse(rec_string)["payload"]["launchPoints"]; foreach (JObject item in app_array) { appList.Add(JsonConvert.DeserializeObject<App>(item.ToString())); } Так вот "вчера" работало... А сегодня получаю исключениt (FormatException), что не может преобразовать обьект в тот, что мне нужен... Как обычно, ничего не делал, оно само ))). Дописал просто такую-же часть для списка каналов... и все рухнуло ))) rec_string - строка ответа от сервера. Парсинг в app_array проходит нормально. А вот преобразование в .NET обьект нет... Все поля соответствуют... Продолжаю наблюдение ))) UPD Написал все то-же самое... нажал "Перестроить решение", опять заработало...

Paul_T: Как обещал, выложил все на гугл диск: Ну, если, вдруг, кому интересно. Сильно не пинайте ))). Выглядит пока коряво... Что сделал: - Включается по WOL из Simpl+. (но, если в S+ поставить директиву #ENCODING_UTF16, то перестает. Но включать можно и из Simpl) - Соединяется с сервером, проверяет ключ в файле. Если файла нет или ключ не соответствует, то запрашивается новый и пишется в файл - Реализованы основные команды - навигационные кнопки, цифровые кнопки 0-9, входы, кромкость +/-, каналы +/- . - Пробовал реализовать мышку, но получается коряво. Но особой необходимости в ней нет. - Можно читать список приложений и каналов. Но массивы этих данных пока не передавал в Simpl+ - Громкость можно задавать аналоговым значением, но пока без фидбека. Что нужно сделать: - Описать обработку исключений в C# - Отслеживать состояние ТВ пингом или по питанию на USB - Проработать логику подключения/отключения в зависимости от состояния ТВ (вкл/выкл) - Динамические списки приложений и каналов? - Фидбэк состояния громкости - ...

Paul_T: Забросил на некоторое время модуль, и, вот вернувшись, обнаружил новую ошибку... При определенном простое возникает исключение NullReferenceException. Если программа работает и постоянно шлет/принимает данные, то все работает хорошо. Но после минуты-две простоя "забывает" что-то ))) Если я не ошибасб, то исключение возникает при попытки вызова метода "Receive". Но пока не понимаю, что там может быть не так. WebSocketClient.WEBSOCKET_PACKET_TYPES type; WebSocketClient.WEBSOCKET_RESULT_CODES receive_result = Client.Receive(out receivedata, out type); Лог: 16. Exception 'Data Abort' (0x4): Thread-Id=08b708ca(pth=95111cb0), Proc-Id=0952b5c6(pprc=95137268) 'splusmanagerapp.exe', VM-active=0952b5c6(pprc=95137268) 'splusmanagerapp.exe' 17. PC=0032c9e4(???+0x0032c9e4) RA=0032c9d0(???+0x0032c9d0) SP=02a6fb0c, BVA=00000000 18. Error: splusmanagerapp.exe [App 1] # 2019-06-10 10:10:07 # Module S-3 : LGTV_WebOs_Client at line 167: Unhandled Exception: System.NullReferenceException: NullReferenceException at WebSocketClientLibrary.SimplWebSocketClient.GetResponse() at WebSocketClientLibrary.SimplWebSocketClient.SendCommand(String t 19. Error: splusmanagerapp.exe [App 1] # 2019-06-10 10:10:07 # oSend) at UserModule_LGTV_WEBOS_CLIENT.UserModuleClass_LGTV_WEBOS_CLIENT.VOLUMEDOWN_OnPush_7(Object __EventInfo__) at Amib.Threading.Internal.WorkItem.n() at Amib.Threading.Internal.WorkItem.Execute() at Amib.Threading.SmartThreadPool.f(Wo 20. Error: splusmanagerapp.exe [App 1] # 2019-06-10 10:10:07 # rkItem A_0) at Amib.Threading.SmartThreadPool.r()

DmitriiP: оберните всё что внутри GetResponse в try/catch public void GetResponse() { try{ то что там сейчас } catch(e) { CrestronConsole.Print("Error GetResponse: " +e.toString()); } }

Paul_T: DmitriiP пишет: Сейчас попробую, может получится локализовать 'неисправность' PS Еще возникла такая задача: Телевизор отдает ссылки на иконки каналов, но проблема в том, что с панели эти URL, есстественно, не доступны, т.к. подключение к телеку выполняет контроллер. Каким образом можно транслировать эти URL в панель?

olegny: Да, надо поймать эту null ссылку на объект сначала. Хотя и без этого, 'Data Abort' наводит на грустные мысли... Надеюсь это уйдет вместе с NullReferenceException

Paul_T: olegny пишет: У меня сложилось мнение, что это из разряда PONG... Выполняется: WebSocketClient.WEBSOCKET_RESULT_CODES receive_result = Client.Receive(out receivedata, out type); receive_result возвращает положительный результат выполнения, но массив байтов receivedata похоже пустой... И, когда далее receivedata преобразуется в строку, появляется исключение... Лечится, если 'долбить' клиента постоянными send/receive. После простоя в минуту что-то 'засыпает' ))) После реконнекта опять все работает до определенного 'простоя'.

olegny: Ну это может быть, но подобный exception нельзя допускать все равно. Проверяйте тогда всё возвращаемое из Receive на null всегда... P.S. Надо сфомулировать проблему насчет PING-PONG с куском неработающего кода и я зафайлю им баг - может и починят когда-нибудь... ))

Paul_T: olegny пишет: Кстати, если обрабатывать исключения, то они все равно пишутся в Error Log. Receive в принципе не должен возвращать null. Он блокирует выполнение и ожидает данных. Откуда там null, не понимаю. Сегодня не могу никаким образом получить это Exception. Вчера обновлял весь софт Crestron, возможно с этим связано, но обновлений библиотек Simpl# нет... В Change Log к Crestron Database 78.05.002.00 написано Improved exception handling... Относится это к Simpl# или нет, не знаю. PS По поводу Ping/Pong можно попробовать, но не знаю, что писать... Его просто нет ))) P.P.S Еще я не понимаю, почему 1. Warning: ConsoleServiceCE.exe # 2019-06-11 09:55:32 # SHELL connection being closed from address 192.168.1.50. зажигает на контроллере лампу MSG. Можно как-то сделать, чтобы MSG зажигалась только от Error?

olegny: Paul_T пишет: Кстати, если обрабатывать исключения, то они все равно пишутся в Error Log. Это как это? Если это у вас в коде, вы это поймали и отфильтровали как рекомендовал выше DmitriiP, то в лог попасть ничего не должно. Другое дело если это происходит внутри метода клиента (фирмвари), то да - там они любят сами пофильтровать, запихнуть в лог и отправить (или не отправить) исключение наверх пользователю... Paul_T пишет: Receive в принципе не должен возвращать null. Мало ли кто чего не может или не должен... )) В случае внутренней ошибки очень даже может. Пролема только в том, что ошибку-то вам и не вернули. Доверяй, но проверяй! ;) Paul_T пишет: Вчера обновлял весь софт Crestron, возможно с этим связано, но обновлений библиотек Simpl# нет... В Change Log к Crestron Database 78.05.002.00 написано Improved exception handling... Относится это к Simpl# или нет, не знаю. Сложно сказать. Надо смотреть на конкретные изменения. Библиотеки Simpl# это только надводная часть айсберга. То, с чем вы работаете - чистая фирмарь в Simpl# оболочке. Paul_T пишет: По поводу Ping/Pong можно попробовать, но не знаю, что писать... Его просто нет ))) В смысле его вам не отдают наверх? )) Это да, в прошлый раз это выяснили вроде... но они должны тогда сами это поддерживать. И если в wireshark нет ответа на ping, то сие и есть баг. Paul_T пишет: Можно как-то сделать, чтобы MSG зажигалась только от Error? Наверное нет, но я попробую узнать... Проще попросить спрятать эти Warning по поводу SHELL, но это тогда со следующим релизом только... ))

Paul_T: olegny пишет: Это как это? Если это у вас в коде, вы это поймали и отфильтровали как рекомендовал выше DmitriiP, то в лог попасть ничего не должно. Другое дело если это происходит внутри метода клиента (фирмвари), то да - там они любят сами пофильтровать, запихнуть в лог и отправить (или не отправить) исключение наверх пользователю... Однако, именно так и сделал... try{ rec_string = Encoding.UTF8.GetString(receivedata, 0, receivedata.Length); } catch(Exception e) { CrestronConsole.Print("Error GetResponse: {0}", e.toString()); } В результате получил ошибку в Error логе и то-же самое в консоле... Сейчас уже сложно проверить, т.к. пока все работает без этого исключения. olegny пишет: И если в wireshark нет ответа на ping, то сие и есть баг. Нет, в Wireshark нет. Я про кусок неработающего кода... "чтобы зафайлить им баг..." Все таки никак не понимаю, что делать с иконками... Телевизор отдает URL контроллеру, но с панели я не могу забрать эти иконки. Не сказать, что это очень важно, но просто интересно, каким образом это решается. Выкачивать иконки на контроллер и оттуда отдавать панели? Так себе метод, затратный))) PS Запилил отображение списка каналов и приложений в динамическом списке.

olegny: Paul_T пишет: В результате получил ошибку в Error логе и то-же самое в консоле... Очень странно, ибо CrestronConsole.Print не дублируется в лог. Вы именно что видели там текст, содержащий "Error GetResponse:"? Paul_T пишет: Так себе метод, затратный Нормальный если другого способа нет... ;) Может кэшировать их где-нить для скорости!

Paul_T: В Errorlog было все тоже самое, что без обработки исключений, плюс CrestronConsole.Print печатал содержание обработанного исключения в консоль. Сейчас пытаюсь воспроизвести такую ситуацию, в лог пишется 18. Exception 'Data Abort' (0x4): Thread-Id=08d001d6(pth=95159c08), Proc-Id=088000f6(pprc=95144d70) 'splusmanagerapp.exe', VM-active=088000f6(pprc=95144d70) 'splusmanagerapp.exe' 19. PC=00320690(???+0x00320690) RA=00320684(???+0x00320684) SP=02a7fa7c, BVA=00000000 В консоль, собственно само исключение: System.NullReferenceException: NullReferenceException at WebSocketClientLibrary.SimplWebSocketClient.GetResponse() at WebSocketClientLibrary.SimplWebSocketClient.SendCommand(String toSend) at UserModule_LGTV_WEBOS_CLIENT.UserModuleClass_LGTV_WEBOS_CLIENT.VOLUME_OnChange_0(Object __EventInfo__) at Amib.Threading.Internal.WorkItem.n() at Amib.Threading.Internal.WorkItem.Execute() at Amib.Threading.SmartThreadPool.f(WorkItem A_0) at Amib.Threading.SmartThreadPool.r() Похоже, что все корректно. Я, все-таки, думаю, что это было связано с обновлениями, ибо ранее такой ошибки я не наблюдал, и когда опять вернулся к своей программе был удивлен ее появлению. Сейчас вопрос решился сам после очередного обновления и ошибки нет... (все валим на Crestron ))) С картинками пока не вижу другого варианта. Надо подумать, как их скопиповать на контроллер и отдать в панель... PS Сейчас при перезугрузке контроллера возникает такая ошибка: 4. Warning: LogicEngine.exe # 2019-06-13 09:26:29 # CresStore: calling redisAsyncFree while connected 5. Error: LogicEngine.exe # 2019-06-13 09:26:29 # CresStore: callback with no reply

DmitriiP: а проверить receivedata на null что мешает :)??? public void GetResponse() { byte[] receivedata; string new_ClientKey = ""; WebSocketClient.WEBSOCKET_PACKET_TYPES type; WebSocketClient.WEBSOCKET_RESULT_CODES receive_result = Client.Receive(out receivedata, out type); //Получаем ответ от сервера if (receivedata != null) { try{ string rec_string = Encoding.UTF8.GetString(receivedata, 0, receivedata.Length); CrestronConsole.Print("Received: {0}\r\n", rec_string); //много много букав catch(Exception e) { CrestronConsole.Print("Error GetResponse: {0}", e.toString()); } } } else { CrestronConsole.Print("Received: null!!!\n"); // можно ещё вывести для наглядности receive_result } } Надеюсь вы в try/catch обернули веь код а не только CrestronConsole.Print("Received: {0}\r\n", rec_string);

Paul_T: DmitriiP пишет: а проверить receivedata на null что мешает :)??? Да, собственно ничего не мешает, так и сделал. Сейчас проблема самоустранилась и все стабильно работает уже несколько дней, без исключений и receivedata равной null. Остался только вопрос, откуда там взялся null и, почему это произошло. Но, думаю, мы это не узнаем. Видимо какие-то внутренние процессы забывали ссылку на массив byte[]. Тут даже проблема не в обработке исключений или проверке на null, а в том, что просто в определенный момент переставал работать метод receive, отдавая reveivedata=null. Совсем... и дальнейшая работа над модулем переставала иметь смысл )))

DmitriiP: Paul_T пишет: Тут даже проблема не в обработке исключений или проверке на null, а в том, что просто в определенный момент переставал работать метод receive Проблемма как раз именно в том что вы не обрабатываете исключения. null может приходить по разным причинам. но вы же не проверяете что :) if(receive_result == WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS) может быть другой статус "не зависящий от вашего кода", у которого по факту нету тела (оно null а не byte[] как при WEBSOCKET_CLIENT_SUCCESS) Перестваал работать он из за того что контроллер ловил системную ошибку и "вешал эту функцию". try/catch не прочто так придумали....

Paul_T: DmitriiP пишет: Статус, как раз возвращался WEBSOCKET_CLIENT_SUCCESS, это и смутило... Ваша мысль ясна, буду знать. Пока только отладкой занимаюсь и все статусы и полученные данные пишу в консоль для контроля, видел, что receive выполняется с положительным результатом, однако, да, можно проверять полученные данные на их наличие. Обработку исключений допишу, конечно же ))) Кстати, в модуле к Denon Heos с апликейшн маркета, тоже исключения не все обработаны )



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