Шлюз для получения данных СКТ Глобус через 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"]
}