Мини мониторинг на базе Odoo

Введение

В прошлой статья я писал про то как быстро сделать websocket на Go. Ниже я объясню как его можно использовать в связке с OpenLayers - популярной библиотеки для работы с картой. Также я покажу как это можно встроить в Odoo - свободную ERP систему.

Таким образом на выходе получим мини систему мониторинга транспорта.

Создание модуля Odoo

Для начала нужно создать новый модуль Odoo. Для этого достаточно запустить команду:

odoo-bin scaffold <имя модуля> <папка с модулем>

Данная команда создаст стуруктуру папок и некоторые файлы.

Теперь в модуле можно завести нужные модели(я для примера создал модели ТС и БНСО) и формы к ним. Процесс описан в документации к Odoo.

Создание виджета карты

Теперь нужно создать виджет карты, для этого в папке static/src/js создать файл map.js следующего содержания:

odoo.define('odoo-socket-map.map', function (require) {
    'use strict';

    var core = require('web.core');
    var Widget = require('web.Widget');

    var MapWidget = Widget.extend({});

    core.action_registry.add('map', MapWidget);
});

После того как виджет создан, нужно сделать для него пункт меню, для этого в файл views/views.xml нужно добавить:

<record id="map_action_window" model="ir.actions.client">
            <field name="name">Map window</field>
            <field name="tag">map</field>
</record>

<menuitem name="Map" id="menu_map_root" sequence="30" action="vehicle_manage.map_action_window"/>        

Теперь когда приготовления закончены, можно подключить openlayers(ol). Для этого в папку static/lib нужно скопировать код ol.

Затем подключим ol к нашему виджету:

var MapWidget = Widget.extend({
    cssLibs: [
            '/odoo-socket-map/static/lib/ol/ol.css'
        ],
        jsLibs: [
            '/odoo-socket-map/static/lib/ol/ol.js'
        ],
    });

И добавим их в загрузку страницы (в файле views/views.xml):

<template id="assets_backend" name="vehicle_manage_assets" inherit_id="web.assets_backend">
            <xpath expr="." position="inside">
                <script type="text/javascript" src="/odoo-socket-map/static/src/js/map.js"/>
                <script type="text/javascript" src="/odoo-socket-map/static/lib/ol/ol.js"/>
                <link rel="stylesheet" href="/odoo-socket-map/static/src/css/map.css"/>
                <link rel="stylesheet" href="/odoo-socket-map/static/lib/ol/ol.css"/>
            </xpath>
        </template>

Тут надо отметить, что файл с виджетом map.js, тоже нужно включить в загрузку.

Теперь добавим к нашему виджету функцию создания карты:

_createMap: function (sourceVector) {
            var mapElement = this.$el[0];
            $(mapElement).attr("id", "map");
            var map = new ol.Map({
                target: mapElement,
                view: new ol.View({
                    projection: 'EPSG:4326',
                    center: [37.62631916049958, 55.749110620880145],
                    zoom: 9,
                    minZoom: 2,
                    maxZoom: 19
                }),
                layers: [
                    new ol.layer.Tile({
                        source: new ol.source.XYZ({
                            url: 'https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png'
                        })
                    }),
                    new ol.layer.Vector({
                        source: sourceVector
                    })
                ]
            });

            return map
        },

Данный код создает объект карты (Map), которая будет выводиться в HTML теге с id="map”. Также заданы параметры масштабирования, система координа и центр. Кроме этого к карте привязываются 2 слоя:

  • Tile - это подложка карты
  • Vector - слой куда будут выводится ТС.

Далее создадим стиль оформления точки, которая будет отображать машину:

markerStyle: function () {
            return new ol.style.Style({
                image: new ol.style.Circle({
                    radius: 7,
                    fill: new ol.style.Fill({color: 'black'}),
                    stroke: new ol.style.Stroke({
                        color: 'white', width: 2
                    })
                })
            });
        }

В нашем случае это будет кружок определенного радиуса.

Когда визуальные компоненты созданы, нужно сделать так, чтобы они отображались при старте виджета. Для этого нужно задать функцию start у объекта с картой:

start: function () {
            var vectorSource = new ol.source.Vector({});

            var map = this._createMap(vectorSource);

            var ws = new WebSocket('ws://localhost:8081/ws');

            ws.onopen = function () {
                console.log("Connected");
                map.render();
            };

            ws.onmessage = function (event) {
                var currentFeature = (new ol.format.GeoJSON()).readFeature(event.data);

                var clientID = currentFeature.get('client');

                var prevFeature = vectorSource.getFeatureById(clientID);

                if (prevFeature == null) {
                    currentFeature.setStyle(this._markerStyle);
                    currentFeature.setId(clientID);
                    vectorSource.addFeature(currentFeature);
                } else {
                    var coord = currentFeature.getGeometry().getCoordinates();
                    prevFeature.getGeometry().setCoordinates(coord);
                }

                console.log(currentFeature.get('client'));
                map.render();
            };
        },

Данный код ицициализирует карту, подключается к websocket, и на каждое принятое сообщение от него рисует точку, причем если она была создана ранее, у нее меняются координаты, таким образом отображая перемещение объекта на карте.

В конце обработки делается перересовка карты для применения изменеий.

Теперь основная часть готова, осталось добавить удобства, чтобы при клике на точку центр карты перемещался на эту машинку. Для этого нужно добавить небольшой кусок кода в конец функции _createMap:

map.on("click", function (e) {
    map.forEachFeatureAtPixel(e.pixel, function (feature, layer) {

        // обработка клика по машинке
        map.getView().setCenter(feature.getGeometry().getCoordinates());
        map.getView().setZoom(14);
    });
});

Теперь виджет можно считать завершенным.

В результате у нас получилось следующее:

карта в Odoo

Исходники модуля доступны на Github.

Заключение

Статья получилось не много сумбурной, но в итоге получилать небольшая система управления траспортом, которую при некоторой доработке можно использовать на небольшом транспортном предприятии. Для полной картины не хватает только сервиса приема телеметрии, о котором я планирую рассказать в следующей статье.

Все исходники находятся на Github: odoo-socket-map

 
comments powered by Disqus