пятница, 1 мая 2020 г.

Запись данных из веб-интерфейса компрессора

По работе появилась необходимость знать как ведет себя компрессор во время работы, а чтобы отслеживать постоянно его состояние, придумали получать данные из веб-интерфеса и передавать их на IBA PDA. По сути таким способом можно записывать данные не только компрессора, но и любого другого устройства имеющего веб-интерфейс. А передавать их можно не только на PDA, но и в базу данных SQL, например, или хоть даже в текстовый документ или эксель

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

На скрине плохо видно, но скрипт GetVar вызывается каждую секунду, и каждую секунду делает специально сформированный запрос к компрессору, и получает в ответ данные, которые потом видно в веб-интерфейсе. Что попросишь, то компрессор и вернет, в своем формате. Если нажать на скрипт GetVar, то можно увидеть и саму структуру запроса, и ответ получаемый от сервиса. Передается запрос методом POST, который можно сформировать таким VBS скриптом:
Dim objHTTP
strToSend = "package_discharge_pressure&sump_pressure"
Set objHTTP = CreateObject("Microsoft.XMLHTTP")
Call objHTTP.Open("POST", "http://10.0.163.51/getVar.cgi", false)
objHTTP.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
objHTTP.Send strToSend
MsgBox(objHTTP.ResponseText)
strToSend - это как раз и есть строка запроса, а в ответ приходят данные в виде чисел, как на скрине в окне MsgBox. Таким же способом удалось получать данные из другого компрессора, только запрос был другого вида: "QUESTION=300201", а в ответ приходит набор байт в HEX формате.

Но это еще не все: во-первых данные нужно принимать как минимум каждые 500 мс, а во-вторых, еще и записывать их. И вот чтобы это сделать, была написана программа на C# которая каждые полсекунды делала запрос, а принимаемые данные пересылала на сервер IBA по UDP протоколу.

Для начала написал код, чтобы можно было посылать запросы и видеть ответы компрессора.
WebClient client = new WebClient();
client.Headers[HttpRequestHeader.ContentType]= "application/x-www-form-urlencoded";
string byteArray = "QUESTION=300201";
string webresponse = client.UploadString("http://10.0.163.50/cgi-bin/mkv.cgi", "POST", byteArray);
Console.WriteLine(webresponse);
После того как в консоли увидел данные, нужно было полученный ответ преобразовать в массив байтов, и переслать этот массив по UDP. Функция передачи по UDP была сделана по примеру UDP чата описываемого в интернете.

Интересно было то, что один из компрессоров напрочь отказался присылать ответ при обращении к нему из C# (из VBS работал отлично). Очень долго бился над этой проблемой и в итоге перепробовав кучу вариантов, нашел один:

WinHttpRequest req = new WinHttpRequest(); try { req.Open("POST", "http://10.0.163.51/getVar.cgi", true); req.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded"); req.Send("sump_pressure"); req.WaitForResponse(); catch(Exception ex) {Console.WriteLine("Error : " + ex.Message.Trim()); Console.WriteLine(req.ResponseText); 

В общем получился такой краткий экскурс как можно сделать нетипичную задачу.  Было довольно интересно поразбираться во всех этих вещах с POST запросами, а потом еще и с расшифровкой присылаемых данных:

1B1B00807FFF00D002D40080000C008000C900800001008000010080000000800001008000000080149A264B00000C3B000003180AA72A13020C2D7F023B535A01B432FF03F74860000000C80000A0630000230D07381393060006100000002C00B6022000010080000100800001008000010080000100800001008000010080230100002302000023030000231700002329000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000XX00000000000000000000000000000064XXXXXXXXXXXXXX00000003006489B700B2A4A4014043B700B2A4A7014043B70000001C

В этой длинной строке из набора байт содержатся данные давления компрессора, температуры, оборотов двигателя и много чего еще. После того как разбил весь запрос по 3 байта, удалось выяснить, что количество отправляемых терхбайтных слов соответствует количеству принимаемых, если разбить каждый из них по 4 байта. Кстати значение может быть зашифровано как в первых двух байтах, так и во вторых двух, и еще необходимы обратныие преобразования в бары, цельсии и %. В общем мне было довольно интересно поковыряться с этим всем. Хорошо помог код JS который удалось выдернуть также из панели инструментов вебразработчика - там удалось проследить и соотнести запросы и ответы на них.


Для разбора помогла такая табличка в EXCEL:


И еще кое-что. Для отправки POST запроса можно использовать не только VBS, код которого был дан выше, но и другие инструменты, например PowerShell:

Invoke-WebRequest -Uri http://10.0.163.51/getVar.cgi -Method POST -Body "package_discharge_pressure" -ContentType "application/x-www-form-urlencoded"

Если powershell будет ругаться на команду, то в интеренете советуют поставить обновление Windows. После утановки обновления у меня заработало.
И еще один инструмент, это программа curl:

curl -i -X POST -H "Content-Type:application/x-www-form-urlencoded" -d "sump_pressure" http://10.0.163.51/getVar.cgi

3 комментария:

  1. Windows Registry Editor Version 5.00

    [HKEY_CLASSES_ROOT\txtfile\shell\Compile]
    "MUIVerb"="Скомпилировать C#"
    "Icon"="shell32.dll,80"

    [HKEY_CLASSES_ROOT\txtfile\shell\Compile\command]
    @="cmd /k C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\csc.exe -nologo \"%1\""

    ОтветитьУдалить
  2. Добавление записи в SQL таблицу при помощи vbs

    Dim cnnMain
    Dim strSQL
    Set cnnMain = CreateObject("ADODB.Connection")
    cnnMain.Open "Provider=SQLOLEDB;Data Source=127.0.0.1;Password=12345;User ID=admin;Initial Catalog=MyDB"
    strSQL="insert into [MyDB].[dbo].[Raw] (test) values (220)"
    cnnMain.BeginTrans
    cnnMain.Execute strSQL
    cnnMain.CommitTrans
    cnnMain.Close
    Set cnnMain = Nothing

    ОтветитьУдалить
  3. Проверка свободного места на диске С и выдача предупреждения если его остается мало

    Set fso = CreateObject("Scripting.FileSystemObject")
    cspace = round(fso.GetDrive("C:").FreeSpace/1073741824,2)
    if cspace < 3 then
    key = msgbox ("Свободного места на диске С:\ осталось всего " & cspace & " Gb" & vbCrLf & "Будешь удалять лишние файлы? ", 4+48 , "Внимание!")

    end if

    ОтветитьУдалить