Шлюз для получения данных СКТ Глобус через websocket
Сервис, который служит для конвертации старого протокола (для настольного клиента СКТ Глобус) в API для доступа к данным о перемещениях тарнспортного средства через Websocket.
Протокол представляет собой посылки данных в формате json. Данные от сервера могут быть как ответами на запрос клиента, так и асинхронными посылками, вызванными приходом данных от устройств при изменении их состояния (положения, датчиков и т.д.)
Любая посылка данных имеет вид json объекта. Тип запроса определяется значением его поля name.
Запросы от клиента к серверу
Авторизация “auth”
После установления соединения c сервером по протоколу websocket клиент должен авторизоваться. Для этого служит один из вариантов запроса.
1.Авторизация при помощи имени пользователя и пароля
Схема:
{
"type": "object",
"properties": {
"name": { "type": "string", "enum": ["auth"] },
"login": { "type": "string" },
"password": { "type": "string" }
}
"required": ["name", "login", "password"]
}
Пример:
{
"name": "auth",
"login": "имя пользователя",
"password": "пароль"
}
2. Авторизация при помощи идентификатора сессии
Этот вариант нужен, когда уже имеется идентификатор сессии, полученный для данного логина и пароля при помощи api авторизации
Схема:
{
"type": "object",
"properties": {
"name": { "type": "string", "enum": ["auth"] },
"sid": {
"type": "string",
"description": "id сессии, полученной путем авторизации через api"
}
},
"required": ["name", "sid"]
}
Пример:
{
"name": "auth",
"sid": "880d1335818669842d086600d97f7ee9b7c69497ddfc3ba9ed8ad42457ced967"
}
Ответ:
В ответ сервер должен прислать сообщение с таким же типом auth
и полем successful
типа boolean, которое будет означать удачную (true
) или неудачную (false
) попытку авторизации.
Схема:
{
"type": "object",
"properties": {
"name": { "type": "string", "enum": ["auth"] },
"successful": { "type": "boolean" },
"sid": {
"type": "string",
"description": "id сессии для использования в запросах к REST API"
}
},
"required": ["name", "successful"]
}
Пример:
{
"name": "auth",
"successful": true
}
После неудачной попытки авторизации сервер закрывает соединение с клиентом. После удачной – клиент может послылать другие запросы серверу, а также сервер может посылать данные клиенту.
Запрос списка устройств “devicesList”
После успешной авторизации клиент может запросить список устройств с сервера.
Схема:
{
"type": "object",
"properties": {
"name": { "type": "string", "enum": ["devicesList"] }
},
"required": ["name"]
}
Пример:
{
"name": "devicesList"
}
Ответ со списком устройств
Схема:
{
"type": "object",
"properties": {
"name": { "type": "string", "enum": ["devicesList"] },
"devices": {
"type": "array",
"items": {
"type": "object",
"properties": {
"regId": {
"type": "integer",
"description": "Уникальный номер устройства в системе"
},
"name": {
"type": "string",
"description": "Произвольное название устройства"
},
"lastKnownTime": {
"type": "string",
"description": "Время последнего получения данных в формате ISO 8601 ( https://ru.wikipedia.org/wiki/ISO_8601 )"
},
"latitude": {
"type": "number"
},
"longitude: {
"type": "number"
},
"speed": {
"type": "number",
"description": "Средняя скорость на последнем участке в метрах в секунду"
},
"group" {
"type": "string",
"description": "Группа, к которой относится это устйроство. Если этого поля нет - то устройство в корневой группе"
},
"reportPeriods" {
"type": "array",
"items": {
"type": "integer"
},
"minItems": 2,
"maxItems": 2,
"description": "Периоды передачи при включенном и выключенном зажигании, соответственно"
},
"ignition" {
"type": "object",
"properties": {
"on": {
"type": "boolean",
"description": "Включено ли зажигание"
},
"used": {
"type": "boolean",
"description": "Используется ли дачик зажигания на данном устройстве"
}
},
"required": [ "on", "used" ]
},
"gpsAntennaStatus": {
"type": "object",
"properties": {
"disconnected": {
"type": "boolean",
"description": "Антенна отключена"
},
"shortCircuit": {
"type": "boolean",
"description": "Антенна замкнута"
}
},
"required": [ "disconnected", "shortCircuit" ],
"description": "Состояние GPS антенны (если этого поля нет - то антенна впорядке)"
},
"sensorsValues": {
"type": "array",
"items": {
"type": "object",
"properties": {
"num": {
"type": "integer",
"description": "Номер датчика"
},
"name": {
"type": "string",
"description": "Название датчика для отображения пользователю"
},
"rawValue": {
"type": "number",
"description": "Исходное значение датчика до применения пересчета на стороне сервера (для отладочного режима)"
},
"scaledValue": {
"type": "number",
"description": "Значение датчика уже в нужных ведичинах"
},
"displayValue": {
"type": "string",
"description": "Отформатированное для показа пользователю значение (с нужным количеством знаков после запятой, единицами измерения и т.д."
},
"flags" {
"type": "object",
"items": {
"type": "object",
"properties": {
"isFuelLevel": {
"type": "boolean",
"description": "Является ли датчиков уровня топлива"
},
"isFuelMeter": {
"type": "boolean",
"description": "Является ли расходомером топлива"
}
},
"required": [ "isFuelLevel", "isFuelMeter" ]
}
}
},
"required": [ "num", "name", "flags" ]
}
}
},
"required": ["regId", "name", "lastKnownTime", "reportPeriods", "ignition"]
}
}
},
"required": ["name", "devices"]
}
Пример:
{
"devices": [
{
"ignition": {
"on": false,
"used": true
},
"name": "\"Мерседес - Бенц АР 7926 СВ\"",
"latitude": 47.41034666666667,
"reportPeriods": [60, 600],
"longitude": 34.820725,
"regId": 52857,
"sensorsValues": [{
"name": "\"БортСеть\"",
"rawValue": 404.0,
"flags": {
"isFuelLevel": false,
"isFuelMeter": false
},
"num": 1,
"displayValue": "\"13,03 В\"",
"scaledValue": 13.032257856000001
}],
"lastKnownTime": "2016-05-10T07:54:06Z",
"speed": 0.0
},
{
"ignition": {
"on": false,
"used": true
},
"name": "\"KAMAZ 4308\"",
"latitude": 47.852666666666664,
"reportPeriods": [30, 300],
"longitude": 35.23113166666667,
"regId": 8908,
"sensorsValues": [
{
"name": "\"Термодатчик 1\"",
"rawValue": 230.0,
"flags": {
"isFuelLevel": false,
"isFuelMeter": false
},
"num": 0,
"displayValue": "\"20,9 ℃\"",
"scaledValue": 20.872131147540983
},
{
"name": "\"Термодатчик 2\"",
"rawValue": 230.0,
"flags": {
"isFuelLevel": false,
"isFuelMeter": false
},
"num": 7,
"displayValue": "\"20,9 ℃\"",
"scaledValue": 20.872131147540983
},
{
"name": "\"Уровень топлива\"",
"rawValue": 876.0,
"flags": {
"isFuelLevel": true,
"isFuelMeter": false
},
"num": 27,
"displayValue": "\"101,36 л\"",
"scaledValue": 101.36363636363637
}
],
"lastKnownTime": "2016-05-13T13:27:45Z",
"speed": 0.0
},
{
"ignition": {
"on": false,
"used": true
},
"name": "\"ГазельАР01-37СМ\"",
"latitude": 47.262431666666664,
"reportPeriods": [30, 600],
"longitude": 35.70977,
"regId": 14041,
"sensorsValues": [],
"lastKnownTime": "2016-05-13T13:24:13Z",
"speed": 0.0
},
{
"ignition": {
"on": true,
"used": false
},
"name": "\"Газель-пропан\"",
"latitude": 48.20518333333333,
"reportPeriods": [30, 600],
"longitude": 34.943553333333334,
"regId": 9533,
"sensorsValues": [],
"lastKnownTime": "2016-05-13T13:30:12Z",
"speed": 0.23
},
{
"ignition": {
"on": false,
"used": true
},
"name": "\"Газель-дизель АР8462СА\"",
"latitude": 47.85151166666667,
"reportPeriods": [30, 600],
"longitude": 35.236088333333335,
"regId": 9421,
"sensorsValues": [{
"name": "\"Уровень топлива\"",
"rawValue": 627.0,
"flags": {
"isFuelLevel": true,
"isFuelMeter": false
},
"num": 27,
"displayValue": "\"6,32 л\"",
"scaledValue": 6.3157894736842195
}],
"lastKnownTime": "2016-05-13T13:22:29Z",
"speed": 0.0
},
{
"ignition": {
"on": false,
"used": true
},
"name": "\"Газель - Метан АР 6451 ВО\"",
"latitude": 47.85129666666667,
"reportPeriods": [60, 600],
"longitude": 35.209718333333335,
"regId": 52858,
"sensorsValues": [{
"name": "\"БортСеть\"",
"rawValue": 401.0,
"flags": {
"isFuelLevel": false,
"isFuelMeter": false
},
"num": 1,
"displayValue": "\"12,94 В\"",
"scaledValue": 12.935483664000001
}],
"lastKnownTime": "2016-05-13T13:23:03Z",
"speed": 0.0
}],
"name": "devicesList"
}
Данные от сервера к клиенту
Кроме описанных выше ответов на запросы, сервер также может передавать данные по мере получения их от устройств. Так как получения этих данных имеет асинхронную природу – то они могут начать приходить сразу же после успешной авторизации, до получения списка устройств. Также следует учесть, что данные от устройства с более новой меткой времени могут поступить до получения списка устройств с данными с более старой меткой времени. Т.е. данные в списке устройств будут старее тех данных от устрйоства, которые пришли ранее.
Положение устройства “location”
Передаются при передаче получении сервером новых данных от устройства.
Схема:
{
"type": "object",
"properties": {
"name": { "type": "string", "enum": ["location"] }
"regId": {
"type": "integer",
"description": "Номер устройства для которого передаются данные"
},
"time": {
"type": "string",
"description": "Время фиксации координат устрйоством в формате ISO 8601"
},
"latitide": { "type": "number" },
"longitude": { "type": "number" },
"speed": {
"type": "number",
"description": "Средняя скорость в метрах в секунду на участке от прошлой точки маршрута"
}
}
"required": [ "name", "regId", "time", "latitude", "longitude", "speed" ]
}
Значение датчиков “sensorValue”
Передаются после передачи положения устрйства как отдельное сообщение. Относятся к предыдущему полученному положению устройствa с тем же номером (т.е. записаны в тот же момент времени, что и предыдущие даные о положении).
Значение датчиков “sensorValue”
Передаются после передачи положения устрйства как отдельное сообщение. Относятся к предыдущему полученному положению устройствa с тем же номером (т.е. записаны в тот же момент времени, что и предыдущие даные о положении).
Схема:
{
"type": "object",
"properties": {
"name": { "type": "string", "enum": ["sensorValue"] },
"regId": {
"type": "integer",
"description": "Номер устройства для которого передаются данные"
},
"num": {
"type": "integer",
"description": "Номер датчика. Такой же, как и в значениях датчика в devicesList"
},
"rawValue": { "type": "number" },
"scaledValue": { "type": "number" },
"displayValue": { "type": "string" },
}
"required": [ "name", "regId", "num", "rawValue", "scaledValue", "displayValue" ]
}
Значения полей – те же, что и в данных о дачиках devicesList, правда название датчика не передается.
В общем случае, данные о датчиках не обязаны приходить непосредственно после данных о положении, между ними могут прийти и другие данные, от другого устрйоства и т.д. Но они всё равно однозначно связываются с предыдущими данными о положении устройства с таким же regId.
Данные от каждого датчика не обязаны присутствовать после каждой посылки location. Данные могут быть не от всех датчиков или вообще отсутствовать, если по каким-то причинам для данной точки фиксации координат они не были измерены.
Изменение состояния зажигания “ignitionChange”
Передаются после данных о положении устройства как отдельное сообщение. Передаются при изменении состояния зажигания на транспортном средстве.
Могут передаваться даже если поле “used” в значениях датчика в списке устройств было помечено как false. В этом случае – можно отображать это значение клиенту для информации, однако действовать как если бы датчика не было. Ну или игорировать эти сообщения.
Схема
{
"type": "object",
"properties": {
"name" { "type": "string", "enum": ["ignitionChange"] },
"regId" { "type": "integer" },
"on" {
"type": "boolean",
"description": "Включено или выключено зажиание"
}
},
"required": [ "name", "regId", "on" ]
}
Посылка сообщения об изменении состояния – это событие, которое произошло в момент времени, указанный в предыдущей посылке location для данного транспортного средства, т.е. с этого момента вемени значения зажигание стало таким, как указано в поле on. До этого момента – оно было в предыдущем состоянии. Т.е. клиент может выбрать показывать, например состояние “стоп, зажигание выключено”, даже если в предыдущей посылке средняя скорость была не 0, так как это средняя скорость за предыдущий интервал, где оно ещё было включено.
Также, в некоторых случаях, событие об изменении состояния зажигания может прийти, когда зажигание уже и так находится в этом же состоянии.
Ошибка “error”
При возникновении каких-либо ошибок при обработке комманд от клиента, либо сообщений от сервера – клиенту передается сообщение с описанием ошибки.
В случае, когда ошибка считается достаточно серьёзной – соединение может быть закрыто сервером. В других случаях – если соединениене было закрыто – можно продолжить обмен данными с сервером.
Схема:
{
"type": "object",
"properties": {
"name": { "type": "string", "enum": ["error"] },
"causedBy": {
"type": "string",
"description": "Имя запроса, который вызвал эту ошибку (если он известен)"
}
"cause": {
"type": "string",
"description": "Описание ошибки в произвольном формате"
}
},
"required": ["name"]
}
Проверка соединения (“ping” – “pong”)
На данный момент для соединений по вебсокету установлен таймаут в 300 секунд. При типичном использовании этого будет достаточно даже для в случае, когда все машины клиента стоят и передают с периодом 300 секунд (стандартный период при стоянке). Для каких-то более редких случаем и, при необходимости, проверке соединения на “живость” – можно использовать пару запрос – ответ из команд ping и pong. Вторая – ответ на первую.
Т.е. клиент может настроить отправку запроса ping через, например, 250 секунд, в течении которых не было передачи данных со стороны сервера. В ответ сервер пришлет команду pong. Можно такие запросы отправлять и чаще, например, для проверки связи мобильным клиентом, в условиях проблемного мобильного интернета.
Также, клиент должен быть готов сам ответить на команду ping ответом pong. В текущей реализации сервер не отправляет таких запросов к коиенту, однако такая возможность должна быть предусмотрена клиентом.
Ping:
{
"type": "object"
"properties": {
"name": { "type": "string", "enum": ["ping"] }
},
"required": ["name"]
}
Pong:
{
"type": "object"
"properties": {
"name": { "type": "string", "enum": ["pong"] }
},
"required": ["name"]
}