Порт сервера на публичный сервер
Этот коммит содержится в:
Коммит
5c026601e1
21
LICENSE
Обычный файл
21
LICENSE
Обычный файл
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 SWD Embedded Systems Ltd
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
30
README.md
Обычный файл
30
README.md
Обычный файл
@ -0,0 +1,30 @@
|
|||||||
|
# gis-map-server
|
||||||
|
Веб-сервис для распространения и тестовый клиент для отображения картографической информации.
|
||||||
|
|
||||||
|
[gis-map-server](src/gis-map-server/) - Python веб-сервер
|
||||||
|
|
||||||
|
[gis-client-render](src/gis-client-render) - Qt клиент для отображения
|
||||||
|
|
||||||
|
Архитектура проекта:
|
||||||
|
![GIS Map Server](src/gis-map-server/docs/architecture.drawio.png)
|
||||||
|
|
||||||
|
Сервис принимает запросы на отрисовку региона средствами [ПК ЦКИ](https://kpda.ru/products/gis/) и возвращает отрисованную карту в виде картинки (поддерживаемые выходные форматы: bmp, png, jpeg).
|
||||||
|
|
||||||
|
Для сборки необходимо:
|
||||||
|
```
|
||||||
|
cd src
|
||||||
|
# Единожды сформировать файл переводов
|
||||||
|
lrelease gis-client-render/*pro
|
||||||
|
# Для сборки под архитектуру _cpu_ можно установить CPULIST=_cpu_, например x86
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
В результате в корне проекта появится папка `.install`, в которую инсталлируются runtime-компоненты
|
||||||
|
|
||||||
|
При запуске веб-сервер `gis-map-server` считывает [конфигурационный файл](src/gis-map-server/gis-map-server.conf).
|
||||||
|
`gis-map-server` предоставляет веб-интерфейс, внешний вид которого показан на рисунке 1.
|
||||||
|
|
||||||
|
![Рисунок 1](src/gis-map-server/docs/web_interface1.png)
|
||||||
|
|
||||||
|
Внешний вид Qt-клиента показан на рисунке 2.
|
||||||
|
|
||||||
|
![Рисунок 2](src/gis-client-render/images/client_interface1.png)
|
2
src/Makefile
Обычный файл
2
src/Makefile
Обычный файл
@ -0,0 +1,2 @@
|
|||||||
|
LIST=LIB
|
||||||
|
include recurse.mk
|
2
src/gis-client-render/Makefile
Обычный файл
2
src/gis-client-render/Makefile
Обычный файл
@ -0,0 +1,2 @@
|
|||||||
|
LIST=OS
|
||||||
|
include recurse.mk
|
21
src/gis-client-render/common.mk
Обычный файл
21
src/gis-client-render/common.mk
Обычный файл
@ -0,0 +1,21 @@
|
|||||||
|
ifndef QCONFIG
|
||||||
|
QCONFIG=qconfig.mk
|
||||||
|
endif
|
||||||
|
include $(QCONFIG)
|
||||||
|
|
||||||
|
COMPILER_DRIVER:=
|
||||||
|
include $(MKFILES_ROOT)/buildlist.mk
|
||||||
|
ifndef OS
|
||||||
|
include $(MKFILES_ROOT)/qmacros.mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
USEFILE=
|
||||||
|
|
||||||
|
# GIS package options
|
||||||
|
ifneq ( $(GIS_INSTALL_ROOT), )
|
||||||
|
DESTDIR:=$(GIS_INSTALL_ROOT)
|
||||||
|
endif
|
||||||
|
export GIS_INSTALL_ROOT=$(DESTDIR)
|
||||||
|
|
||||||
|
include $(MKFILES_ROOT)/qmake.mk
|
||||||
|
include $(MKFILES_ROOT)/qtargets.mk
|
51
src/gis-client-render/gis-client-render.pro
Обычный файл
51
src/gis-client-render/gis-client-render.pro
Обычный файл
@ -0,0 +1,51 @@
|
|||||||
|
QT += core gui network
|
||||||
|
|
||||||
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
|
qnx {
|
||||||
|
# Get install directory for defining command to run
|
||||||
|
INSTALLDIRECTORY=$$(INSTALLDIR)
|
||||||
|
|
||||||
|
!isEmpty(INSTALLDIRECTORY) {
|
||||||
|
target.path = $$(INSTALLDIR)
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmpty(INSTALLDIRECTORY) {
|
||||||
|
INSTALLDIRECTORY = "/opt/gis/bin"
|
||||||
|
target.path = $$INSTALLDIRECTORY
|
||||||
|
message(Manual path setting $$INSTALLDIRECTORY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
INSTALLS += target
|
||||||
|
|
||||||
|
# The following define makes your compiler emit warnings if you use
|
||||||
|
# any feature of Qt which as been marked as deprecated (the exact warnings
|
||||||
|
# depend on your compiler). Please consult the documentation of the
|
||||||
|
# deprecated API in order to know how to port your code away from it.
|
||||||
|
DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
|
|
||||||
|
SOURCES += main.cpp\
|
||||||
|
mainwidget.cpp \
|
||||||
|
mainwindow.cpp \
|
||||||
|
image_viewport.cpp \
|
||||||
|
gismapserver.cpp \
|
||||||
|
mapsender.cpp
|
||||||
|
|
||||||
|
HEADERS += mainwidget.h \
|
||||||
|
mainwindow.h \
|
||||||
|
image_viewport.h \
|
||||||
|
gismapserver.h \
|
||||||
|
mapsender.h
|
||||||
|
|
||||||
|
# If: Cannot find file 'translations/gis-client-render_ru.qm'
|
||||||
|
# then run lupdate, lrelease manually
|
||||||
|
TRANSLATIONS += $$_PRO_FILE_PWD_/translations/$$join(TARGET,,,_ru.ts)
|
||||||
|
|
||||||
|
RESOURCES += \
|
||||||
|
images.qrc \
|
||||||
|
translations.qrc
|
||||||
|
|
||||||
|
QMAKE_CXXFLAGS += -std=gnu++11
|
52
src/gis-client-render/gismapserver.cpp
Обычный файл
52
src/gis-client-render/gismapserver.cpp
Обычный файл
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gismapserver.h"
|
||||||
|
#include "mapsender.h"
|
||||||
|
|
||||||
|
QString gis_map_server_get_error_message( GisHttpCodes statusCode )
|
||||||
|
{
|
||||||
|
switch ( statusCode ) {
|
||||||
|
case GisHttpCodes::IsProcessing :
|
||||||
|
return "Is processing";
|
||||||
|
case GisHttpCodes::InvalidParameters :
|
||||||
|
return "Invalid parameters";
|
||||||
|
case GisHttpCodes::RequestFailed :
|
||||||
|
return "Failed to request";
|
||||||
|
case GisHttpCodes::Timeout :
|
||||||
|
return "Timeout while processing request";
|
||||||
|
case GisHttpCodes::RenderFailed :
|
||||||
|
return "Failed to render request";
|
||||||
|
case GisHttpCodes::Done :
|
||||||
|
return "Request has been already obtained";
|
||||||
|
case GisHttpCodes::Ok :
|
||||||
|
default:
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int gis_map_server_request_map( const char *server_url,
|
||||||
|
double lat,
|
||||||
|
double lon,
|
||||||
|
int scale,
|
||||||
|
int w,
|
||||||
|
int h,
|
||||||
|
const char *format,
|
||||||
|
uint *orderId,
|
||||||
|
char *pinCode )
|
||||||
|
{
|
||||||
|
if ( scale <= 0 || w <= 0 || h <= 0 || !orderId || !format )
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
uint timeout_ms = 30000;
|
||||||
|
MapSender mapSender( QString(server_url), 0, timeout_ms );
|
||||||
|
return mapSender.sendRequest( lat, lon, scale, w, h, format, orderId, pinCode );
|
||||||
|
}
|
||||||
|
|
||||||
|
int gis_map_server_get_image( const char *server_url, int orderId, char *pinCode, QImage *image )
|
||||||
|
{
|
||||||
|
uint timeout_ms = 30000;
|
||||||
|
MapSender mapSender( QString(server_url), orderId, timeout_ms );
|
||||||
|
return mapSender.getImageWithWaiting( pinCode, image );
|
||||||
|
}
|
44
src/gis-client-render/gismapserver.h
Обычный файл
44
src/gis-client-render/gismapserver.h
Обычный файл
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*************************************************/
|
||||||
|
/* GIS Map Server */
|
||||||
|
/* Server interface header */
|
||||||
|
/*************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef GISMAPSENDER_H
|
||||||
|
#define GISMAPSENDER_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
Ok = 200,
|
||||||
|
IsProcessing = 202,
|
||||||
|
InvalidParameters = 400,
|
||||||
|
Timeout = 408,
|
||||||
|
Done = 410,
|
||||||
|
NoMemory = 418,
|
||||||
|
RenderFailed = 500,
|
||||||
|
RequestFailed = 520
|
||||||
|
} GisHttpCodes;
|
||||||
|
|
||||||
|
QString gis_map_server_get_error_message( GisHttpCodes statusCode );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make a request to the gis-map-server
|
||||||
|
*
|
||||||
|
* format -png, jpeg, bmp
|
||||||
|
*/
|
||||||
|
int gis_map_server_request_map(const char *server_url, double lat, double lon, int scale, int w, int h, const char *format, uint* orderId, char *pinCode );
|
||||||
|
|
||||||
|
int gis_map_server_get_image(const char *server_url, int orderId, char *pinCode, QImage *image );
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // GISMAPSENDER_H
|
92
src/gis-client-render/image_viewport.cpp
Обычный файл
92
src/gis-client-render/image_viewport.cpp
Обычный файл
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "image_viewport.h"
|
||||||
|
|
||||||
|
#include <QGraphicsPixmapItem>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QMovie>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
ImageViewport::ImageViewport() : QGraphicsView(),
|
||||||
|
currentMapItem( nullptr )
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Animated earth's GIF setup
|
||||||
|
* Runs until user performs a request
|
||||||
|
*
|
||||||
|
* See: https://forum.qt.io/topic/15658/solved-how-to-play-gif-animation-in-qgraphicsview-widget
|
||||||
|
*/
|
||||||
|
graphics_scene = new QGraphicsScene();
|
||||||
|
graphics_scene->setBackgroundBrush( QColor( 239, 239, 239 ) );
|
||||||
|
QLabel *animation = new QLabel();
|
||||||
|
QMovie *earth_gif = new QMovie( ":/images/earth.gif" );
|
||||||
|
animation->setMovie( earth_gif );
|
||||||
|
earth_gif->setSpeed( 90 );
|
||||||
|
earth_gif->start();
|
||||||
|
proxy = graphics_scene->addWidget( animation );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Placing a circle around GIF to hide its low resolution
|
||||||
|
*/
|
||||||
|
QPen earth_bound( (QColor( Qt::black )) );
|
||||||
|
earth_bound.setWidth( 5 );
|
||||||
|
graphics_scene->addEllipse( QRectF( 0, 0, 257, 257 ),
|
||||||
|
QPen( earth_bound ),
|
||||||
|
QBrush( QColor( 0, 0, 0, 0 ) ) );
|
||||||
|
|
||||||
|
setScene( graphics_scene );
|
||||||
|
setDragMode( QGraphicsView::ScrollHandDrag );
|
||||||
|
setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
|
||||||
|
setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageViewport::fit_image()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Place the center of the image at the center of the viewport
|
||||||
|
*/
|
||||||
|
centerOn( graphics_scene->width() / 2,
|
||||||
|
graphics_scene->height() / 2 );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Keep the whole image inside the viewport, saving the aspect ratio
|
||||||
|
*/
|
||||||
|
fitInView( QRectF( 0, 0,
|
||||||
|
graphics_scene->width(),
|
||||||
|
graphics_scene->height() ),
|
||||||
|
Qt::KeepAspectRatio );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageViewport::wheelEvent( QWheelEvent *event )
|
||||||
|
{
|
||||||
|
if ( event->delta() > 0 )
|
||||||
|
scale( scroll_step, scroll_step );
|
||||||
|
|
||||||
|
if ( event->delta() < 0 )
|
||||||
|
scale( 1 / scroll_step, 1 / scroll_step );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageViewport::show_map_image( const QImage &image )
|
||||||
|
{
|
||||||
|
proxy->hide();
|
||||||
|
if ( currentMapItem )
|
||||||
|
graphics_scene->removeItem( currentMapItem );
|
||||||
|
|
||||||
|
currentMapItem = new QGraphicsPixmapItem( QPixmap::fromImage( image ) );
|
||||||
|
graphics_scene->addItem( currentMapItem );
|
||||||
|
fit_image();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageViewport::clean_map_image()
|
||||||
|
{
|
||||||
|
proxy->show();
|
||||||
|
if ( currentMapItem ) {
|
||||||
|
graphics_scene->removeItem( currentMapItem );
|
||||||
|
currentMapItem = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
fit_image();
|
||||||
|
}
|
34
src/gis-client-render/image_viewport.h
Обычный файл
34
src/gis-client-render/image_viewport.h
Обычный файл
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef IMAGE_VIEWPORT_H
|
||||||
|
#define IMAGE_VIEWPORT_H
|
||||||
|
|
||||||
|
#include <QEvent>
|
||||||
|
#include <QGraphicsScene>
|
||||||
|
#include <QGraphicsView>
|
||||||
|
#include <QWheelEvent>
|
||||||
|
#include <QGraphicsProxyWidget>
|
||||||
|
|
||||||
|
class ImageViewport : public QGraphicsView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ImageViewport();
|
||||||
|
|
||||||
|
void fit_image();
|
||||||
|
protected:
|
||||||
|
virtual void wheelEvent( QWheelEvent *event );
|
||||||
|
private:
|
||||||
|
const double scroll_step = 1.3;
|
||||||
|
QGraphicsScene *graphics_scene;
|
||||||
|
QGraphicsProxyWidget *proxy;
|
||||||
|
QGraphicsPixmapItem *currentMapItem;
|
||||||
|
public slots:
|
||||||
|
void show_map_image( const QImage &img );
|
||||||
|
void clean_map_image();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // IMAGE_VIEWPORT_H
|
5
src/gis-client-render/images.qrc
Обычный файл
5
src/gis-client-render/images.qrc
Обычный файл
@ -0,0 +1,5 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file>images/earth.gif</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
Двоичные данные
src/gis-client-render/images/client_interface1.png
Обычный файл
Двоичные данные
src/gis-client-render/images/client_interface1.png
Обычный файл
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 435 KiB |
Двоичные данные
src/gis-client-render/images/earth.gif
Обычный файл
Двоичные данные
src/gis-client-render/images/earth.gif
Обычный файл
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 739 KiB |
64
src/gis-client-render/main.cpp
Обычный файл
64
src/gis-client-render/main.cpp
Обычный файл
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QTranslator>
|
||||||
|
|
||||||
|
#include "mainwidget.h"
|
||||||
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
int main( int argc, char *argv[] )
|
||||||
|
{
|
||||||
|
double x_pos = -999, y_pos = -999;
|
||||||
|
uint32_t width = 0, height = 0, scale = 0;
|
||||||
|
const char *server_url = nullptr;
|
||||||
|
const char *image_format = nullptr;
|
||||||
|
const char *outputImageName = nullptr;
|
||||||
|
bool consoleMode = false;
|
||||||
|
|
||||||
|
QLocale::setDefault(QLocale::C);
|
||||||
|
|
||||||
|
int opt = 0;
|
||||||
|
extern char *optarg;
|
||||||
|
while ( ( opt = getopt( argc, argv, "x:y:w:h:s:u:f:o:" ) ) != -1 )
|
||||||
|
{
|
||||||
|
switch ( opt )
|
||||||
|
{
|
||||||
|
case 'x': x_pos = atof( optarg ); break;
|
||||||
|
case 'y': y_pos = atof( optarg ); break;
|
||||||
|
case 'w': width = strtoul( optarg, NULL, 0 ); break;
|
||||||
|
case 'h': height = strtoul( optarg, NULL, 0 ); break;
|
||||||
|
case 's': scale = strtoul( optarg, NULL, 0 ); break;
|
||||||
|
case 'u': server_url = strdup( optarg ); break;
|
||||||
|
case 'f': image_format = strdup( optarg ); break;
|
||||||
|
case 'o':
|
||||||
|
{
|
||||||
|
consoleMode = true;
|
||||||
|
outputImageName = strdup( optarg );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '?': printf( "Error: unknown option\n" ); exit(1); break;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
QApplication app( argc, argv );
|
||||||
|
|
||||||
|
QString ablangStr = getenv( "ABLANG" );
|
||||||
|
if ( ablangStr.isEmpty() ) {
|
||||||
|
ablangStr = getenv( "LANG" );
|
||||||
|
}
|
||||||
|
QTranslator myTranslator;
|
||||||
|
myTranslator.load( ":/translations/gis-client-render_" + ablangStr );
|
||||||
|
app.installTranslator( &myTranslator );
|
||||||
|
|
||||||
|
MainWindow window( x_pos, y_pos, width, height, scale, server_url, image_format, outputImageName, consoleMode );
|
||||||
|
window.show();
|
||||||
|
|
||||||
|
return app.exec();
|
||||||
|
}
|
264
src/gis-client-render/mainwidget.cpp
Обычный файл
264
src/gis-client-render/mainwidget.cpp
Обычный файл
@ -0,0 +1,264 @@
|
|||||||
|
/*
|
||||||
|
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mainwidget.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QWaitCondition>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
#include "gismapserver.h"
|
||||||
|
|
||||||
|
MainWidget::MainWidget( double x_pos,
|
||||||
|
double y_pos,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height,
|
||||||
|
uint32_t scale,
|
||||||
|
const char *server_url,
|
||||||
|
const char *image_format,
|
||||||
|
const char *outputImageName,
|
||||||
|
bool consoleMode ) : QWidget(),
|
||||||
|
_consoleMode( consoleMode ),
|
||||||
|
_outputImageName( outputImageName ),
|
||||||
|
_server_url( server_url ),
|
||||||
|
_image_format( image_format ),
|
||||||
|
request_btn_state( true ),
|
||||||
|
field_x_pos( new QLineEdit() ),
|
||||||
|
field_y_pos( new QLineEdit() ),
|
||||||
|
field_width( new QLineEdit() ),
|
||||||
|
field_height( new QLineEdit() ),
|
||||||
|
field_scale( new QLineEdit() ),
|
||||||
|
urlEdit(new QLineEdit()),
|
||||||
|
formatBox(new QComboBox()),
|
||||||
|
request_btn( new QPushButton( tr( "Request map" ) ) )
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Do not rescale left dock panel's widgets and contents.
|
||||||
|
*/
|
||||||
|
QSizePolicy size_policy;
|
||||||
|
size_policy.setVerticalPolicy( QSizePolicy::Fixed );
|
||||||
|
size_policy.setHorizontalPolicy( QSizePolicy::Fixed );
|
||||||
|
setSizePolicy( size_policy );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Placing console parameters into fields, if they are present
|
||||||
|
* If they are not present, use placeholders with input hints.
|
||||||
|
*/
|
||||||
|
if ( width != 0 )
|
||||||
|
field_width->setText( QString::number( width, 10 ) );
|
||||||
|
|
||||||
|
if ( height != 0 )
|
||||||
|
field_height->setText( QString::number( height, 10 ) );
|
||||||
|
|
||||||
|
if ( x_pos != -999 )
|
||||||
|
field_x_pos->setText( QString::number( x_pos, 'f', 6 ) );
|
||||||
|
else
|
||||||
|
field_x_pos->setPlaceholderText( QString( "[-180...180]" ) );
|
||||||
|
|
||||||
|
if ( y_pos != -999 )
|
||||||
|
field_y_pos->setText( QString::number( y_pos, 'f', 6 ) );
|
||||||
|
else
|
||||||
|
field_y_pos->setPlaceholderText( QString( "[-90...90]" ) );
|
||||||
|
|
||||||
|
if ( scale != 0 )
|
||||||
|
field_scale->setText( QString::number( scale, 10 ) );
|
||||||
|
else
|
||||||
|
field_scale->setText( "1000000" );
|
||||||
|
|
||||||
|
QStringList acceptedFormats;
|
||||||
|
acceptedFormats << "bmp" << "png" << "jpg";
|
||||||
|
formatBox->addItems(acceptedFormats);
|
||||||
|
|
||||||
|
int idx = formatBox->findText(image_format);
|
||||||
|
if (idx >= 0)
|
||||||
|
formatBox->setCurrentIndex(idx);
|
||||||
|
|
||||||
|
urlEdit->setText( server_url );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Acceptable longtitude range: [ -180 ... 180 ].
|
||||||
|
*/
|
||||||
|
longtitude_validator = new QDoubleValidator();
|
||||||
|
longtitude_validator->setRange( -180.0, 180.0 );
|
||||||
|
longtitude_validator->setDecimals( 8 );
|
||||||
|
longtitude_validator->setNotation( QDoubleValidator::StandardNotation );
|
||||||
|
field_x_pos->setValidator( longtitude_validator );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Acceptable latitude range: [ -90 ... 90 ].
|
||||||
|
*/
|
||||||
|
latitude_validator = new QDoubleValidator();
|
||||||
|
latitude_validator->setRange( -90.0, 90.0 );
|
||||||
|
latitude_validator->setDecimals( 8 );
|
||||||
|
latitude_validator->setNotation( QDoubleValidator::StandardNotation );
|
||||||
|
field_y_pos->setValidator( latitude_validator );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Acceptable width and height of resulting image in pixels: positive values.
|
||||||
|
*/
|
||||||
|
QIntValidator *pixel_validator = new QIntValidator();
|
||||||
|
pixel_validator->setBottom( 1 );
|
||||||
|
field_width->setValidator( pixel_validator );
|
||||||
|
field_height->setValidator( pixel_validator );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setting up the left dock panel's vertical layout order.
|
||||||
|
*/
|
||||||
|
QVBoxLayout *vlayout = new QVBoxLayout();
|
||||||
|
QHBoxLayout *hlayout = new QHBoxLayout();
|
||||||
|
vlayout->addWidget( new QLabel( tr( "Image width (px):" ) ) );
|
||||||
|
vlayout->addWidget( field_width );
|
||||||
|
vlayout->addWidget( new QLabel( tr( "Image height (px):" ) ) );
|
||||||
|
vlayout->addWidget( field_height );
|
||||||
|
vlayout->addWidget( new QLabel( tr( "Longtitude:" ) ) );
|
||||||
|
vlayout->addWidget( field_x_pos );
|
||||||
|
vlayout->addWidget( new QLabel( tr( "Latitude:" ) ) );
|
||||||
|
vlayout->addWidget( field_y_pos );
|
||||||
|
vlayout->addWidget( new QLabel( tr( "Scale:" ) ) );
|
||||||
|
hlayout->addWidget( new QLabel( "1:" ) );
|
||||||
|
hlayout->addWidget( field_scale );
|
||||||
|
vlayout->addLayout( hlayout );
|
||||||
|
vlayout->addWidget( new QLabel( tr( "URL:" ) ) );
|
||||||
|
vlayout->addWidget( urlEdit );
|
||||||
|
vlayout->addWidget( new QLabel( tr( "Format:" ) ) );
|
||||||
|
vlayout->addWidget(formatBox);
|
||||||
|
vlayout->addWidget( request_btn );
|
||||||
|
setLayout( vlayout );
|
||||||
|
|
||||||
|
connect( request_btn, SIGNAL( released() ),
|
||||||
|
this, SLOT( request_btn_handler() ) );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Go to the next input field if user pressed Enter in the previous one
|
||||||
|
*/
|
||||||
|
connect( field_width, SIGNAL( returnPressed() ),
|
||||||
|
this, SLOT( go_to_field_height() ) );
|
||||||
|
connect( field_height, SIGNAL( returnPressed() ),
|
||||||
|
this, SLOT( go_to_field_x_pos() ) );
|
||||||
|
connect( field_x_pos, SIGNAL( returnPressed() ),
|
||||||
|
this, SLOT( go_to_field_y_pos() ) );
|
||||||
|
connect( field_y_pos, SIGNAL( returnPressed() ),
|
||||||
|
this, SLOT( go_to_field_scale() ) );
|
||||||
|
connect( field_scale, SIGNAL( returnPressed() ),
|
||||||
|
this, SLOT( go_to_request_btn() ) );
|
||||||
|
request_btn->setDefault( true );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Trying to imitate click
|
||||||
|
*/
|
||||||
|
if ( _consoleMode ) {
|
||||||
|
request_btn_handler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWidget::start_processing_request()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Latitudes and longtitudes may have "Intermediate" validation state,
|
||||||
|
* which means that if 180 is the greatest value that is acceptable,
|
||||||
|
* user can't type in 1800, but he can type in 184, which is out of range.
|
||||||
|
* This check prohibits invalid input.
|
||||||
|
*
|
||||||
|
* Width and height validation is omitted due to the fact that only
|
||||||
|
* positive numbers are allowed.
|
||||||
|
*
|
||||||
|
* See: https://doc.qt.io/qt-5/qintvalidator.html#validate
|
||||||
|
*/
|
||||||
|
int cursor = 0;
|
||||||
|
QString temp_x_pos = field_x_pos->text();
|
||||||
|
QString temp_y_pos = field_y_pos->text();
|
||||||
|
if ( longtitude_validator->validate( temp_x_pos, cursor ) != QValidator::Acceptable )
|
||||||
|
{
|
||||||
|
field_x_pos->clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( latitude_validator->validate( temp_y_pos, cursor ) != QValidator::Acceptable )
|
||||||
|
{
|
||||||
|
field_y_pos->clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Request map rendering from server and wait for reply.
|
||||||
|
*/
|
||||||
|
process_request();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWidget::process_request()
|
||||||
|
{
|
||||||
|
double lon = field_x_pos->text().toDouble();
|
||||||
|
double lat = field_y_pos->text().toDouble();
|
||||||
|
uint w = field_width->text().toUInt();
|
||||||
|
uint h = field_height->text().toUInt();
|
||||||
|
uint scale = field_scale->text().toUInt();
|
||||||
|
|
||||||
|
QString format = formatBox->currentText();
|
||||||
|
QString url = urlEdit->text();
|
||||||
|
|
||||||
|
qDebug() << "Sending request: ";
|
||||||
|
qDebug() << "X:" << lon
|
||||||
|
<< "Y:" << lat
|
||||||
|
<< "Width:" << w
|
||||||
|
<< "Height:" << h
|
||||||
|
<< "Scale: 1 :" << scale
|
||||||
|
<< "Format:" << format;
|
||||||
|
|
||||||
|
uint orderId = 0;
|
||||||
|
char pinCode[128];
|
||||||
|
int status = gis_map_server_request_map( url.toStdString().c_str(), lat, lon, scale, w, h, format.toStdString().c_str(), &orderId, pinCode );
|
||||||
|
if ( status != 0 ) {
|
||||||
|
qDebug() << "Failed to make request" << status;
|
||||||
|
if ( _consoleMode ) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Client got id:" << orderId;
|
||||||
|
qDebug() << "Client got pincode:" << pinCode;
|
||||||
|
|
||||||
|
status = gis_map_server_get_image( url.toStdString().c_str(), orderId, pinCode, &image );
|
||||||
|
if ( status != 0 ) {
|
||||||
|
qDebug() << "Failed to get image" << status;
|
||||||
|
if ( _consoleMode ) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qDebug() << "Got image";
|
||||||
|
|
||||||
|
if ( _consoleMode ) {
|
||||||
|
if ( image.save( _outputImageName ) )
|
||||||
|
qDebug() << "Image was saved to current directory as " << _outputImageName;
|
||||||
|
else
|
||||||
|
qDebug() << "Failed to save image as " << _outputImageName;
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit request_completed( image );
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWidget::set_UI_active( bool state )
|
||||||
|
{
|
||||||
|
field_x_pos->setEnabled( state );
|
||||||
|
field_y_pos->setEnabled( state );
|
||||||
|
field_width->setEnabled( state );
|
||||||
|
field_height->setEnabled( state );
|
||||||
|
field_scale->setEnabled( state );
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWidget::request_btn_handler()
|
||||||
|
{
|
||||||
|
set_UI_active( false );
|
||||||
|
start_processing_request();
|
||||||
|
set_UI_active( true );
|
||||||
|
}
|
72
src/gis-client-render/mainwidget.h
Обычный файл
72
src/gis-client-render/mainwidget.h
Обычный файл
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MAINWIDGET_H
|
||||||
|
#define MAINWIDGET_H
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QValidator>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class MainWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit MainWidget( double x_pos,
|
||||||
|
double y_pos,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height,
|
||||||
|
uint32_t scale,
|
||||||
|
const char *server_url,
|
||||||
|
const char *image_format,
|
||||||
|
const char *outputImageName,
|
||||||
|
bool consoleMode );
|
||||||
|
|
||||||
|
QImage image;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const bool _consoleMode;
|
||||||
|
const char *_outputImageName;
|
||||||
|
const char *_server_url;
|
||||||
|
const char *_image_format;
|
||||||
|
|
||||||
|
void process_request();
|
||||||
|
void set_UI_active( bool state );
|
||||||
|
|
||||||
|
bool request_btn_state;
|
||||||
|
|
||||||
|
QDoubleValidator *longtitude_validator;
|
||||||
|
QDoubleValidator *latitude_validator;
|
||||||
|
|
||||||
|
QLineEdit *field_x_pos;
|
||||||
|
QLineEdit *field_y_pos;
|
||||||
|
QLineEdit *field_width;
|
||||||
|
QLineEdit *field_height;
|
||||||
|
QLineEdit *field_scale;
|
||||||
|
QLineEdit *urlEdit;
|
||||||
|
QComboBox *formatBox;
|
||||||
|
QPushButton *request_btn;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void request_completed( const QImage &img );
|
||||||
|
void request_failed();
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
void start_processing_request();
|
||||||
|
void request_btn_handler();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Go to the next input field if user pressed Enter in the previous one
|
||||||
|
*/
|
||||||
|
void go_to_field_height() { field_height->setFocus( Qt::OtherFocusReason ); }
|
||||||
|
void go_to_field_x_pos() { field_x_pos->setFocus( Qt::OtherFocusReason ); }
|
||||||
|
void go_to_field_y_pos() { field_y_pos->setFocus( Qt::OtherFocusReason ); }
|
||||||
|
void go_to_field_scale() { field_scale->setFocus( Qt::OtherFocusReason ); }
|
||||||
|
void go_to_request_btn() { request_btn->setFocus( Qt::OtherFocusReason ); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MAINWIDGET_H
|
63
src/gis-client-render/mainwindow.cpp
Обычный файл
63
src/gis-client-render/mainwindow.cpp
Обычный файл
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "mainwidget.h"
|
||||||
|
|
||||||
|
MainWindow::MainWindow( double x_pos,
|
||||||
|
double y_pos,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height,
|
||||||
|
uint32_t scale,
|
||||||
|
const char *server_url,
|
||||||
|
const char *image_format,
|
||||||
|
const char *outputImageName,
|
||||||
|
bool consoleMode ) : QMainWindow()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Left dock menu panel widget with request parameters
|
||||||
|
*/
|
||||||
|
QDockWidget *dock_widget = new QDockWidget( tr( "Request parameters" ), this );
|
||||||
|
dock_widget->setAllowedAreas( Qt::LeftDockWidgetArea );
|
||||||
|
dock_widget->setFeatures( QDockWidget::NoDockWidgetFeatures );
|
||||||
|
|
||||||
|
MainWidget *menu = new MainWidget( x_pos,
|
||||||
|
y_pos,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
scale,
|
||||||
|
server_url,
|
||||||
|
image_format,
|
||||||
|
outputImageName,
|
||||||
|
consoleMode );
|
||||||
|
dock_widget->setWidget( menu );
|
||||||
|
addDockWidget( Qt::LeftDockWidgetArea, dock_widget );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Resulting image viewport widget
|
||||||
|
*/
|
||||||
|
viewport = new ImageViewport();
|
||||||
|
viewport->show();
|
||||||
|
|
||||||
|
connect( menu, SIGNAL( request_completed( const QImage&) ),
|
||||||
|
viewport, SLOT( show_map_image( const QImage&) ) );
|
||||||
|
|
||||||
|
connect( menu, SIGNAL( request_failed() ),
|
||||||
|
viewport, SLOT( clean_map_image() ) );
|
||||||
|
|
||||||
|
|
||||||
|
setMinimumSize( 700, 400 );
|
||||||
|
setWindowTitle( tr( "Cartographic client" ) );
|
||||||
|
setCentralWidget( viewport );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reimplementation of Qt's resizeEvent() for image fitting
|
||||||
|
* in the viewport after each change of the window's size.
|
||||||
|
*/
|
||||||
|
void MainWindow::resizeEvent( QResizeEvent *event )
|
||||||
|
{
|
||||||
|
Q_UNUSED( event );
|
||||||
|
viewport->fit_image();
|
||||||
|
}
|
35
src/gis-client-render/mainwindow.h
Обычный файл
35
src/gis-client-render/mainwindow.h
Обычный файл
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <QDockWidget>
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QWheelEvent>
|
||||||
|
|
||||||
|
#include "image_viewport.h"
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MainWindow( double x_pos,
|
||||||
|
double y_pos,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height,
|
||||||
|
uint32_t scale,
|
||||||
|
const char *server_url,
|
||||||
|
const char *image_format,
|
||||||
|
const char *outputImageName,
|
||||||
|
bool consoleMode );
|
||||||
|
|
||||||
|
private:
|
||||||
|
ImageViewport *viewport;
|
||||||
|
protected:
|
||||||
|
virtual void resizeEvent( QResizeEvent *event );
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MAINWINDOW_H
|
156
src/gis-client-render/mapsender.cpp
Обычный файл
156
src/gis-client-render/mapsender.cpp
Обычный файл
@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mapsender.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QBuffer>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
|
#include "gismapserver.h"
|
||||||
|
|
||||||
|
int MapSender::sendRequest( double lat,
|
||||||
|
double lon,
|
||||||
|
int scale,
|
||||||
|
int w,
|
||||||
|
int h,
|
||||||
|
const char* format,
|
||||||
|
uint *orderId,
|
||||||
|
char *pinCode )
|
||||||
|
{
|
||||||
|
QString requestUrl = _url + "/?";
|
||||||
|
requestUrl += "&lat=" + QString::number(lat);
|
||||||
|
requestUrl += "&lon=" + QString::number(lon);
|
||||||
|
requestUrl += "&scale=" + QString::number(scale);
|
||||||
|
requestUrl += "&w=" + QString::number(w);
|
||||||
|
requestUrl += "&h=" + QString::number(h);
|
||||||
|
requestUrl += "&format=" + QString(format);
|
||||||
|
|
||||||
|
QUrl url( requestUrl );
|
||||||
|
QNetworkRequest request(url);
|
||||||
|
request.setRawHeader( QByteArray("agent"), QByteArray("gis") );
|
||||||
|
|
||||||
|
QNetworkReply *reply = manager->get(request);
|
||||||
|
waitForReplyFinished( reply, _timeout_ms );
|
||||||
|
|
||||||
|
QVariant statusCodeAttribute = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||||
|
bool ok = false;
|
||||||
|
GisHttpCodes statusCode = static_cast<GisHttpCodes>(statusCodeAttribute.toInt(&ok));
|
||||||
|
if ( !ok || statusCode != GisHttpCodes::Ok ) {
|
||||||
|
qDebug() << gis_map_server_get_error_message( statusCode );
|
||||||
|
return EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString orderId_tag = "orderId=";
|
||||||
|
QString pinCode_tag = "pincode=";
|
||||||
|
|
||||||
|
QByteArray rawData = reply->readAll();
|
||||||
|
QList<QByteArray> rawLines = rawData.split(',');
|
||||||
|
|
||||||
|
QString orderStr( rawLines[0] );
|
||||||
|
if (orderStr.contains( orderId_tag )) {
|
||||||
|
orderStr.remove(orderId_tag);
|
||||||
|
*orderId = orderStr.toUInt( &ok );
|
||||||
|
if ( !ok ) {
|
||||||
|
qDebug() << "Invalid <orderId>";
|
||||||
|
return EFAULT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString pinCodeStr( rawLines[1] );
|
||||||
|
if (pinCodeStr.contains( pinCode_tag )) {
|
||||||
|
pinCodeStr.remove(pinCode_tag);
|
||||||
|
pinCodeStr.remove( " " );
|
||||||
|
strcpy( pinCode, pinCodeStr.toStdString().c_str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MapSender::getImageWithWaiting( char *pinCode, QImage *image )
|
||||||
|
{
|
||||||
|
uint waitMs = 100;
|
||||||
|
QDateTime start = QDateTime::currentDateTimeUtc();
|
||||||
|
QDateTime prevRequestTime = QDateTime::currentDateTimeUtc();
|
||||||
|
QDateTime currentRequestTime = prevRequestTime;
|
||||||
|
|
||||||
|
int status = getImage( pinCode, image );
|
||||||
|
while ( status == EAGAIN ) {
|
||||||
|
qApp->processEvents( QEventLoop::AllEvents, waitMs );
|
||||||
|
|
||||||
|
currentRequestTime = QDateTime::currentDateTimeUtc();
|
||||||
|
qint64 requestdeltaMs = prevRequestTime.msecsTo( currentRequestTime );
|
||||||
|
if ( requestdeltaMs > 500 ) {
|
||||||
|
prevRequestTime = currentRequestTime;
|
||||||
|
status = getImage( pinCode, image );
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime current = QDateTime::currentDateTimeUtc();
|
||||||
|
auto totalDeltaMs = start.msecsTo( current );
|
||||||
|
if ( totalDeltaMs > _timeout_ms )
|
||||||
|
return ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MapSender::getImage( char *pinCode, QImage *image )
|
||||||
|
{
|
||||||
|
QString requestUrl = _url + "/?&orderId=" + QString::number(_orderId) + "&pincode=" + QString(pinCode);
|
||||||
|
QUrl url( requestUrl );
|
||||||
|
QNetworkRequest request(url);
|
||||||
|
|
||||||
|
QNetworkReply *reply = manager->get(request);
|
||||||
|
waitForReplyFinished( reply, _timeout_ms );
|
||||||
|
|
||||||
|
QVariant statusCodeAttribute = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||||
|
bool ok = false;
|
||||||
|
int statusCode = statusCodeAttribute.toInt(&ok);
|
||||||
|
if ( !ok || statusCode != GisHttpCodes::Ok ) {
|
||||||
|
if ( statusCode == GisHttpCodes::IsProcessing ) {
|
||||||
|
return EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "statusCode" << statusCode;
|
||||||
|
return EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !reply->isFinished() )
|
||||||
|
return EFAULT;
|
||||||
|
|
||||||
|
QVariant format_hdr = reply->header( QNetworkRequest::ContentTypeHeader );
|
||||||
|
QString format_str = format_hdr.toString();
|
||||||
|
QString image_format = format_str.remove( "image/" );
|
||||||
|
|
||||||
|
QByteArray ba = reply->readAll();
|
||||||
|
image->loadFromData( ba, image_format.toLocal8Bit().data() );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapSender::waitForReplyFinished( QNetworkReply *reply, uint timeout_ms )
|
||||||
|
{
|
||||||
|
uint waitMs = 100;
|
||||||
|
bool isTimeoutEnabled = false;
|
||||||
|
if ( timeout_ms > 0 )
|
||||||
|
isTimeoutEnabled = true;
|
||||||
|
|
||||||
|
QDateTime start = QDateTime::currentDateTimeUtc();
|
||||||
|
qint64 totalDeltaMs = 0;
|
||||||
|
|
||||||
|
while ( !reply->isFinished() ) {
|
||||||
|
qApp->processEvents( QEventLoop::AllEvents, waitMs );
|
||||||
|
|
||||||
|
if ( isTimeoutEnabled ) {
|
||||||
|
QDateTime current = QDateTime::currentDateTimeUtc();
|
||||||
|
totalDeltaMs = start.msecsTo( current );
|
||||||
|
if ( totalDeltaMs > timeout_ms )
|
||||||
|
{
|
||||||
|
qDebug() << "Timeout" << totalDeltaMs;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
src/gis-client-render/mapsender.h
Обычный файл
37
src/gis-client-render/mapsender.h
Обычный файл
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MAPSENDER_H
|
||||||
|
#define MAPSENDER_H
|
||||||
|
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
|
||||||
|
class MapSender : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit MapSender( const QString &server_address, const int userId, const uint timeout_ms ) :
|
||||||
|
_url( server_address ),
|
||||||
|
_orderId( userId ),
|
||||||
|
_timeout_ms( timeout_ms )
|
||||||
|
{
|
||||||
|
manager = new QNetworkAccessManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Client calls
|
||||||
|
int sendRequest(double lat, double lon, int scale, int w, int h, const char* format, uint* userId , char* pinCode);
|
||||||
|
int getImageWithWaiting( char *pinCode, QImage *image );
|
||||||
|
|
||||||
|
private:
|
||||||
|
int getImage( char *pinCode, QImage *image );
|
||||||
|
void waitForReplyFinished( QNetworkReply *reply, uint timeout_ms );
|
||||||
|
|
||||||
|
const QString _url;
|
||||||
|
const int _orderId;
|
||||||
|
const uint _timeout_ms;
|
||||||
|
QNetworkAccessManager *manager;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MAPSENDER_H
|
8
src/gis-client-render/nto/Makefile
Исполняемый файл
8
src/gis-client-render/nto/Makefile
Исполняемый файл
@ -0,0 +1,8 @@
|
|||||||
|
LIST=CPU
|
||||||
|
ifndef QRECURSE
|
||||||
|
QRECURSE=recurse.mk
|
||||||
|
ifdef QCONFIG
|
||||||
|
QRDIR=$(dir $(QCONFIG))
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
include $(QRDIR)$(QRECURSE)
|
8
src/gis-client-render/nto/arm/Makefile
Исполняемый файл
8
src/gis-client-render/nto/arm/Makefile
Исполняемый файл
@ -0,0 +1,8 @@
|
|||||||
|
LIST=VARIANT
|
||||||
|
ifndef QRECURSE
|
||||||
|
QRECURSE=recurse.mk
|
||||||
|
ifdef QCONFIG
|
||||||
|
QRDIR=$(dir $(QCONFIG))
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
include $(QRDIR)$(QRECURSE)
|
1
src/gis-client-render/nto/arm/o.le.v7/Makefile
Исполняемый файл
1
src/gis-client-render/nto/arm/o.le.v7/Makefile
Исполняемый файл
@ -0,0 +1 @@
|
|||||||
|
include ../../../common.mk
|
8
src/gis-client-render/nto/e2k/Makefile
Исполняемый файл
8
src/gis-client-render/nto/e2k/Makefile
Исполняемый файл
@ -0,0 +1,8 @@
|
|||||||
|
LIST=VARIANT
|
||||||
|
ifndef QRECURSE
|
||||||
|
QRECURSE=recurse.mk
|
||||||
|
ifdef QCONFIG
|
||||||
|
QRDIR=$(dir $(QCONFIG))
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
include $(QRDIR)$(QRECURSE)
|
1
src/gis-client-render/nto/e2k/o.le/Makefile
Исполняемый файл
1
src/gis-client-render/nto/e2k/o.le/Makefile
Исполняемый файл
@ -0,0 +1 @@
|
|||||||
|
include ../../../common.mk
|
8
src/gis-client-render/nto/mips/Makefile
Исполняемый файл
8
src/gis-client-render/nto/mips/Makefile
Исполняемый файл
@ -0,0 +1,8 @@
|
|||||||
|
LIST=VARIANT
|
||||||
|
ifndef QRECURSE
|
||||||
|
QRECURSE=recurse.mk
|
||||||
|
ifdef QCONFIG
|
||||||
|
QRDIR=$(dir $(QCONFIG))
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
include $(QRDIR)$(QRECURSE)
|
1
src/gis-client-render/nto/mips/o.be/Makefile
Исполняемый файл
1
src/gis-client-render/nto/mips/o.be/Makefile
Исполняемый файл
@ -0,0 +1 @@
|
|||||||
|
include ../../../common.mk
|
8
src/gis-client-render/nto/ppc/Makefile
Исполняемый файл
8
src/gis-client-render/nto/ppc/Makefile
Исполняемый файл
@ -0,0 +1,8 @@
|
|||||||
|
LIST=VARIANT
|
||||||
|
ifndef QRECURSE
|
||||||
|
QRECURSE=recurse.mk
|
||||||
|
ifdef QCONFIG
|
||||||
|
QRDIR=$(dir $(QCONFIG))
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
include $(QRDIR)$(QRECURSE)
|
1
src/gis-client-render/nto/ppc/o.be.spe/Makefile
Исполняемый файл
1
src/gis-client-render/nto/ppc/o.be.spe/Makefile
Исполняемый файл
@ -0,0 +1 @@
|
|||||||
|
include ../../../common.mk
|
1
src/gis-client-render/nto/ppc/o.be/Makefile
Исполняемый файл
1
src/gis-client-render/nto/ppc/o.be/Makefile
Исполняемый файл
@ -0,0 +1 @@
|
|||||||
|
include ../../../common.mk
|
8
src/gis-client-render/nto/x86/Makefile
Исполняемый файл
8
src/gis-client-render/nto/x86/Makefile
Исполняемый файл
@ -0,0 +1,8 @@
|
|||||||
|
LIST=VARIANT
|
||||||
|
ifndef QRECURSE
|
||||||
|
QRECURSE=recurse.mk
|
||||||
|
ifdef QCONFIG
|
||||||
|
QRDIR=$(dir $(QCONFIG))
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
include $(QRDIR)$(QRECURSE)
|
1
src/gis-client-render/nto/x86/o/Makefile
Исполняемый файл
1
src/gis-client-render/nto/x86/o/Makefile
Исполняемый файл
@ -0,0 +1 @@
|
|||||||
|
include ../../../common.mk
|
5
src/gis-client-render/translations.qrc
Обычный файл
5
src/gis-client-render/translations.qrc
Обычный файл
@ -0,0 +1,5 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file>translations/gis-client-render_ru.qm</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
56
src/gis-client-render/translations/gis-client-render_ru.ts
Обычный файл
56
src/gis-client-render/translations/gis-client-render_ru.ts
Обычный файл
@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.0" language="ru_RU">
|
||||||
|
<context>
|
||||||
|
<name>MainWidget</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../mainwidget.cpp" line="12"/>
|
||||||
|
<source>Longtitude:</source>
|
||||||
|
<translation>Долгота:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../mainwidget.cpp" line="13"/>
|
||||||
|
<source>Latitude:</source>
|
||||||
|
<translation>Широта:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../mainwidget.cpp" line="14"/>
|
||||||
|
<source>Image width (px):</source>
|
||||||
|
<translation>Ширина изображения в пикс.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../mainwidget.cpp" line="15"/>
|
||||||
|
<source>Image height (px):</source>
|
||||||
|
<translation>Высота изображения в пикс.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../mainwidget.cpp" line="16"/>
|
||||||
|
<source>Scale:</source>
|
||||||
|
<translation>Масштаб:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../mainwidget.cpp" line="23"/>
|
||||||
|
<location filename="../mainwidget.cpp" line="204"/>
|
||||||
|
<source>Request map</source>
|
||||||
|
<translation>Выполнить запрос</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../mainwidget.cpp" line="206"/>
|
||||||
|
<source>Abort request</source>
|
||||||
|
<translation>Прервать запрос</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>MainWindow</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../mainwindow.cpp" line="31"/>
|
||||||
|
<source>Cartographic client</source>
|
||||||
|
<translation>Картографический клиент</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../mainwindow.cpp" line="12"/>
|
||||||
|
<source>Request parameters</source>
|
||||||
|
<translation>Параметры запроса</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
2
src/gis-map-server/Makefile
Обычный файл
2
src/gis-map-server/Makefile
Обычный файл
@ -0,0 +1,2 @@
|
|||||||
|
LIST=OS
|
||||||
|
include recurse.mk
|
6
src/gis-map-server/README.md
Обычный файл
6
src/gis-map-server/README.md
Обычный файл
@ -0,0 +1,6 @@
|
|||||||
|
## gis-map-server - карт-сервер, предназначенный для работы с ПК ЦКИ
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
![GIS Map Server IPC](docs/gis-map-server_IPC.drawio.png)
|
15
src/gis-map-server/START.sh
Исполняемый файл
15
src/gis-map-server/START.sh
Исполняемый файл
@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
if [ -z "$GIS_ROOT" ]; then
|
||||||
|
export GIS_ROOT=/opt/gis/
|
||||||
|
fi
|
||||||
|
export LD_LIBRARY_PATH=$GIS_ROOT/lib:$LD_LIBRARY_PATH
|
||||||
|
export PATH=$GIS_ROOT/bin:$GIS_ROOT/sbin:$PATH
|
||||||
|
export GIS_DEBUG_LEVEL=2
|
||||||
|
export GIS_DISABLE_VERIFIER=y
|
||||||
|
|
||||||
|
slay -f gis-core
|
||||||
|
gis-core -dsxf-local,sync=soft -ds57-local,sync=soft &
|
||||||
|
waitfor /dev/gis_core 300
|
||||||
|
|
||||||
|
echo 'Starting server'
|
||||||
|
$GIS_ROOT/data/resources/gis-map-server/server.py $GIS_ROOT/data/config/gis-map-server.conf
|
20
src/gis-map-server/common.mk
Обычный файл
20
src/gis-map-server/common.mk
Обычный файл
@ -0,0 +1,20 @@
|
|||||||
|
ifndef QCONFIG
|
||||||
|
QCONFIG=qconfig.mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
include $(QCONFIG)
|
||||||
|
|
||||||
|
include $(MKFILES_ROOT)/qmacros.mk
|
||||||
|
|
||||||
|
NAME=$(notdir ${PROJECT_ROOT})
|
||||||
|
|
||||||
|
INSTALLDIR=/opt/gis/sbin/
|
||||||
|
RESOURCESDIR=/opt/gis/data/resources/${NAME}
|
||||||
|
LOGDIR=/opt/gis/data/config
|
||||||
|
|
||||||
|
PRE_INSTALL=$(CP_HOST) ${PROJECT_ROOT}/START.sh ${INSTALL_ROOT_nto}/${CPUVARDIR}/${INSTALLDIR}/${NAME}; $(CP_HOST) ${PROJECT_ROOT}/*.py ${INSTALL_ROOT_nto}/${CPUVARDIR}/${RESOURCESDIR}/
|
||||||
|
POST_INSTALL=$(CP_HOST) -R ${PROJECT_ROOT}/html ${INSTALL_ROOT_nto}/${CPUVARDIR}/${RESOURCESDIR}; $(CP_HOST) ${PROJECT_ROOT}/$(NAME).conf ${INSTALL_ROOT_nto}/${CPUVARDIR}/${LOGDIR}/$(NAME).conf
|
||||||
|
|
||||||
|
ALL_DEPENDENCIES=Makefile
|
||||||
|
|
||||||
|
include $(MKFILES_ROOT)/qtargets.mk
|
1
src/gis-map-server/docs/architecture.drawio
Обычный файл
1
src/gis-map-server/docs/architecture.drawio
Обычный файл
@ -0,0 +1 @@
|
|||||||
|
<mxfile host="app.diagrams.net" modified="2021-12-21T13:21:52.932Z" agent="5.0 (X11)" etag="9BBCW-6gea-xDeGxZfHn" version="16.0.0" type="device"><diagram id="b_8fcSy7Od5qwKq7muho" name="Page-1">7Vhbb5swFP41PG7iEiA8hqTdHjatWnd9qhxwwZvBkXGadL9+tjHGBtpcVLXVNAkhczgc4+/77HNsJ1hW+3cUbMqPJIfY8d187wQrx/e9me874nLz+9Yyd5WhoChXTr3hGv2Byugq6xblsLEcGSGYoY1tzEhdw4xZNkAp2dlutwTbvW5AAUeG6wzgsfU7ylmpRuHHvf09REXZ9exFSfumAp2zGklTgpzsDFNw4QRLSghrW9V+CbEAr8Ol/e7ygbf6xyis2TEffLnJPq9XYEHd5nLbXCf4J/rxRkW5A3irBuysXCdZiHuayvtc3kN5Xzkrz5n78p6qYbH7Dqs7SBni0H0Aa4ivSIMYIjV/tSaMkcoJ0s5hgVEhXjCy4daSVZg/eLzJMdqIYNW+EHJ6uwYNyt5icA8pzG+oJDiV4uL6CFKyZRjVcKm5F0Y1It4V3D8IlacJ4MqFpIKM3nOXfSfS9gulWS8O2+ddrwDPVT6lwX6kbECJrtCRe154Q1FzAk3+mCY/whIMdMebhWgazLmSIbdji1su5D3o7Npn1nHM23HHNG97sr0wFJB0XfIRmL1O/0h62XWiw4Tqd9QHazoMMQz8r8orenX6Cib0NUAf1vlCrKf8qSY1FECBpoS5gtZAWdivAGOQ1tLiu4HAnlHyW6+hHJ2Ukm2dywA9rjAfLccHUTVQCydA62wUYsDQnR1+CknVwxVBvGNNmj+zSQviARkN2dIMqq/MdfhAoCGpDNACslEcyase9flUz/5TfYjqaER1eB7Vw0BR+KxUh4eptonZlYjB6w3IxNsdXyLHbGsVjEneQIr4L0MqIqC6UI5PsGCOEnK3OBp6CKYWTF1GPvmKGR2bkReelfz6WsrMyzy3usfnxemEO7crNN2nmedD1Vub4Y9Lu71EvMMSGaviKehPYpv+rvo286X/nPkyHrMvqA7t8qqlQ1OTGqVWouh4fWDrvKbA1vuXFwN7fuxUS5b2VkW3W/FH1qQ4aW6ZVbJnlLehsR+K7TmnaY+Najsy7POzC+GXF8lsFjzbjPzaQPpp/UtU4L6LRe1vzDhvBLq99LXjwhycku/8IbVPA2pQKUi/AYrAmgMs7Y/KMTGQUfI1uOGYsikClgQT2hdUtwjjgQmoXUvGOYB0YjtToTwX3UwyTrj3LZY1W8n9YP1Eq0EyXawaPM+n8u7pNPNHg+lTjjDcw2WOUdFmGDR81yeJAZSNzQZ155WtvGdZHz72z61fW/8dquBebMfjDTJBeO6OJxoEcp93y+NNnHK9doWERyrEf0mFDA8yguHyfqxC4mAQaFi3n60Q/tgfsbbu/UF1cPEX</diagram></mxfile>
|
Двоичные данные
src/gis-map-server/docs/architecture.drawio.png
Обычный файл
Двоичные данные
src/gis-map-server/docs/architecture.drawio.png
Обычный файл
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 15 KiB |
1
src/gis-map-server/docs/gis-map-server_IPC.drawio
Обычный файл
1
src/gis-map-server/docs/gis-map-server_IPC.drawio
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Двоичные данные
src/gis-map-server/docs/gis-map-server_IPC.drawio.png
Обычный файл
Двоичные данные
src/gis-map-server/docs/gis-map-server_IPC.drawio.png
Обычный файл
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 90 KiB |
Двоичные данные
src/gis-map-server/docs/web_interface1.png
Обычный файл
Двоичные данные
src/gis-map-server/docs/web_interface1.png
Обычный файл
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 216 KiB |
6
src/gis-map-server/gis-map-server.conf
Обычный файл
6
src/gis-map-server/gis-map-server.conf
Обычный файл
@ -0,0 +1,6 @@
|
|||||||
|
SERVER_ADDRESS=localhost
|
||||||
|
SERVER_PORT=8000
|
||||||
|
SLOTS_NUMBER=2
|
||||||
|
STORAGE_MAX_SIZE=6400000
|
||||||
|
HTML_PAGES_PATH=./data/resources/gis-map-server/html/
|
||||||
|
GIS_SHID=770
|
14
src/gis-map-server/html/order_request.html
Обычный файл
14
src/gis-map-server/html/order_request.html
Обычный файл
@ -0,0 +1,14 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
<meta content="">
|
||||||
|
<style></style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<center>
|
||||||
|
<h2>Order is accepted</h2>
|
||||||
|
<p>Order id = ORDERID, pincode = PIN_CODE</p>
|
||||||
|
<a href='http://ADDRESS:PORT/?orderId=ORDERID&pincode=PIN_CODE'> Click here to obtain your order </a>
|
||||||
|
</center>
|
||||||
|
</body>
|
||||||
|
</html>
|
165
src/gis-map-server/html/start_page.html
Обычный файл
165
src/gis-map-server/html/start_page.html
Обычный файл
@ -0,0 +1,165 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
<meta content="">
|
||||||
|
<style>
|
||||||
|
.params_frame {
|
||||||
|
width: 200px;
|
||||||
|
padding: 10px;
|
||||||
|
float: left;
|
||||||
|
border: 1px solid black;
|
||||||
|
margin: 7px;
|
||||||
|
}
|
||||||
|
.results_frame {
|
||||||
|
width: calc(100% - 288px);
|
||||||
|
height: calc(100vh - 58px );
|
||||||
|
float: left;
|
||||||
|
border: 1px solid black;
|
||||||
|
margin: 7px;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
function new_req() {
|
||||||
|
|
||||||
|
var lat = document.getElementById("lat").value;
|
||||||
|
var lon = document.getElementById("lon").value;
|
||||||
|
var lon = document.getElementById("lon").value;
|
||||||
|
var scale = document.getElementById("scale").value;
|
||||||
|
var h = document.getElementById("h").value;
|
||||||
|
var w = document.getElementById("w").value;
|
||||||
|
var format = document.getElementById("format").value;
|
||||||
|
|
||||||
|
document.getElementById('but').disabled = true;
|
||||||
|
|
||||||
|
if (document.contains(document.getElementById("frame_id"))) {
|
||||||
|
document.getElementById("frame_id").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
const iframe = document.createElement('iframe');
|
||||||
|
iframe.id = "frame_id";
|
||||||
|
iframe.scrolling = "no";
|
||||||
|
iframe.style = "border: 0px;"
|
||||||
|
document.getElementById('results').appendChild(iframe);
|
||||||
|
|
||||||
|
res = send_req(lat, lon, scale, w, h, format);
|
||||||
|
|
||||||
|
if (res[2] == '200')
|
||||||
|
{
|
||||||
|
show_content('Processing...')
|
||||||
|
|
||||||
|
counter = 0
|
||||||
|
var interval_poll = window.setInterval(function()
|
||||||
|
{
|
||||||
|
answer = get_ord(res[0], res[1])
|
||||||
|
|
||||||
|
if (answer[1] == 200)
|
||||||
|
{
|
||||||
|
clearInterval(interval_poll);
|
||||||
|
iframe.height = h;
|
||||||
|
iframe.width = w;
|
||||||
|
iframe.src = 'http://ADDRESS:PORT/?orderId=' + res[0] + '&pincode=' + res[1];
|
||||||
|
document.getElementById('but').disabled = false;
|
||||||
|
}
|
||||||
|
else if (answer[1] == 202)
|
||||||
|
{
|
||||||
|
show_content('Processing... ' + answer[1])
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clearInterval(interval_poll);
|
||||||
|
iframe.srcdoc = answer[2];
|
||||||
|
document.getElementById('but').disabled = false;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
show_content('<p> <font color="red"> Error code obtained from server (' + res[2] + ') </font> </p>')
|
||||||
|
document.getElementById('but').disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function show_content(cont){
|
||||||
|
document.getElementById('frame_id').src = "data:text/html;charset=utf-8," + escape(cont);
|
||||||
|
}
|
||||||
|
|
||||||
|
function send_req(lat, lon, scale, w, h, format) {
|
||||||
|
const url = 'http://ADDRESS:PORT/?lat='+ lat + '&lon=' + lon + '&scale=' + scale + '&w=' + w +'&h=' + h + '&format=' + format
|
||||||
|
res = http_get(url)
|
||||||
|
id = res[0].match('Order id = (.*?),')
|
||||||
|
pincode = res[0].match('pincode = (.*?)<')
|
||||||
|
if (id != null && pincode != null)
|
||||||
|
{
|
||||||
|
id = id[1]
|
||||||
|
pincode = pincode[1]
|
||||||
|
}
|
||||||
|
return [id, pincode, res[1]];
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_ord(id, pincode) {
|
||||||
|
const url = 'http://ADDRESS:PORT/?orderId=' + id + '&pincode=' + pincode
|
||||||
|
return http_get(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function http_get(url)
|
||||||
|
{
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open( "GET", url, false);
|
||||||
|
xhr.send( null );
|
||||||
|
return [xhr.responseText, xhr.status, xhr.response]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="params_frame">
|
||||||
|
<form action="">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<label for="lat">Latitude</label>
|
||||||
|
<td>
|
||||||
|
<input name="lat" id="lat" size="7" value="55.4798">
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<label for="lon">Longitude</label>
|
||||||
|
<td>
|
||||||
|
<input name="lon" id="lon" size="7" value="37.754">
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<label for="scale">Scale</label>
|
||||||
|
<td>
|
||||||
|
<input name="scale" id="scale" size="7" value="10700">
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<label for="w">Width</label>
|
||||||
|
<td>
|
||||||
|
<input name="w" id="w" size="7" value="1000">
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<label for="h">Height</label>
|
||||||
|
<td>
|
||||||
|
<input name="h" id="h" size="7" value="1000">
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<label for="format">Format</label>
|
||||||
|
<td>
|
||||||
|
<input name="format" id="format" size="7" value="png">
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<BR>
|
||||||
|
<div>
|
||||||
|
<center> <input type="button" value="Send request" id="but" onclick="new_req()"> </center>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="results_frame" id="results">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
324
src/gis-map-server/net_interface.py
Обычный файл
324
src/gis-map-server/net_interface.py
Обычный файл
@ -0,0 +1,324 @@
|
|||||||
|
###############################################################################
|
||||||
|
# (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
#!/usr/bin/python3 -u
|
||||||
|
import http.server, ssl
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from enum import Enum, unique
|
||||||
|
from utils import Request_Status, get_log_path, get_status_desc
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class Page_Type(Enum):
|
||||||
|
START = 0
|
||||||
|
ORDER_REQUEST = 1
|
||||||
|
BAD_REQUEST = 2
|
||||||
|
|
||||||
|
class Handler(http.server.BaseHTTPRequestHandler):
|
||||||
|
"""
|
||||||
|
A class for http-requests handling. Instance's lifetime is limited by the time of request processing.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
__________
|
||||||
|
pipe_conn : multiprocessing.connection.Connection
|
||||||
|
Pipe connection instance for interacting with Scheduler.
|
||||||
|
buffer : Storage_Class instance
|
||||||
|
Buffer is used to store data from POST-requests obtained by Renderer.
|
||||||
|
html_path : str
|
||||||
|
The path to a folder with html pages that are used to responde clients.
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
________
|
||||||
|
get_html_content(page_type)
|
||||||
|
Obtains the content of html page of specified type and returns it as a string.
|
||||||
|
do_POST()
|
||||||
|
Handles POST requests.
|
||||||
|
do_GET()
|
||||||
|
Handles GET requests.
|
||||||
|
bad_request(code, exc="")
|
||||||
|
Inserts a error message obtained by code and prints an html-page into wfile. Also supports printing an exception string.
|
||||||
|
clear_pipe()
|
||||||
|
Obtains all possible data from Pipe connection if it exists.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, pipe_conn, buffer, html_path, *args):
|
||||||
|
"""
|
||||||
|
Parameters:
|
||||||
|
__________
|
||||||
|
pipe_conn : multiprocessing.connection.Connection
|
||||||
|
Pipe connection instance for interacting with Scheduler.
|
||||||
|
buffer : Storage_Class instance
|
||||||
|
Buffer is used to store data from POST-requests obtained by Renderer.
|
||||||
|
html_path : str
|
||||||
|
The path to a folder with html pages that are used to responde clients.
|
||||||
|
"""
|
||||||
|
self.pipe_conn = pipe_conn
|
||||||
|
self.buffer = buffer
|
||||||
|
self.html_path = html_path
|
||||||
|
http.server.BaseHTTPRequestHandler.__init__(self, *args)
|
||||||
|
|
||||||
|
def get_html_content(self, page_type):
|
||||||
|
"""Obtains the content of html page of specified type and returns it as a string.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
__________
|
||||||
|
page_type : Page_Type Enum instance
|
||||||
|
Type of page which should be read.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
Content of html page
|
||||||
|
"""
|
||||||
|
if page_type == Page_Type.START.value:
|
||||||
|
with open(self.html_path + '/start_page.html') as f:
|
||||||
|
content = f.read()
|
||||||
|
content = content.replace('ADDRESS', str(self.server.server_name))
|
||||||
|
content = content.replace('PORT', str(self.server.server_port))
|
||||||
|
return content
|
||||||
|
elif page_type == Page_Type.ORDER_REQUEST.value:
|
||||||
|
with open(self.html_path + '/order_request.html') as f:
|
||||||
|
content = f.read()
|
||||||
|
content = content.replace('ADDRESS', str(self.server.server_name))
|
||||||
|
content = content.replace('PORT', str(self.server.server_port))
|
||||||
|
return content
|
||||||
|
else:
|
||||||
|
return 'BadPage'
|
||||||
|
|
||||||
|
def do_POST(self):
|
||||||
|
""" Handles POST requests. """
|
||||||
|
try:
|
||||||
|
# trying to get payload
|
||||||
|
logging.debug('Got POST-request')
|
||||||
|
length = int(self.headers['Content-Length'])
|
||||||
|
logging.debug('Got length')
|
||||||
|
orderId = int(self.headers['orderId'])
|
||||||
|
logging.debug('Got orderId')
|
||||||
|
img_format = self.headers['Content-Type']
|
||||||
|
logging.debug('Got img_format')
|
||||||
|
payload = self.rfile.read(length)
|
||||||
|
logging.debug('Got payload')
|
||||||
|
|
||||||
|
status_code, deleted_ids = self.buffer.push(orderId, payload, img_format, length)
|
||||||
|
|
||||||
|
#sending information to scheduler
|
||||||
|
self.clear_pipe()
|
||||||
|
self.pipe_conn.send((2, deleted_ids))
|
||||||
|
if self.pipe_conn.poll(1):
|
||||||
|
logging.debug('net_interface: got answer from scheduler')
|
||||||
|
answer = self.pipe_conn.recv()
|
||||||
|
if answer == False:
|
||||||
|
self.bad_request(Request_Status.REQUEST_FAILED.value, "Scheduler could not delete previous ids from table")
|
||||||
|
else:
|
||||||
|
self.bad_request(Request_Status.TIMEOUT.value, "Scheduler did not responde")
|
||||||
|
|
||||||
|
if status_code == Request_Status.READY.value:
|
||||||
|
logging.debug('Push success')
|
||||||
|
self.send_response(Request_Status.READY.value, "Got payload")
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write('Accepted'.encode())
|
||||||
|
else:
|
||||||
|
logging.debug('Error while pushing {status_code}')
|
||||||
|
self.bad_request(status_code, "Id is busy")
|
||||||
|
|
||||||
|
except Exception as exc:
|
||||||
|
try:
|
||||||
|
self.bad_request(Request_Status.INVALID_PARAM.value, exc)
|
||||||
|
except Exception as exc:
|
||||||
|
logging.debug(f"Bad connection with client {exc}")
|
||||||
|
|
||||||
|
def bad_request(self, code, exc=""):
|
||||||
|
""" Prints an html-page with error code into wfile and inserts there an error message obtained by code. Also supports printing an exception string.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
__________
|
||||||
|
code : Request_Status Enum instance
|
||||||
|
An error code to print.A string of thrown exception to print.
|
||||||
|
exc : str
|
||||||
|
A string of thrown exception to print.
|
||||||
|
"""
|
||||||
|
self.send_response(code)
|
||||||
|
self.send_header("Content-type", "text/html")
|
||||||
|
self.send_header("Access-Control-Allow-Origin", "*")
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write('<html><head><meta charset="utf-8">'.encode())
|
||||||
|
self.wfile.write('<title>Bad request</title></head>'.encode())
|
||||||
|
self.wfile.write(f'<body><p>Bad request: {code}:{get_status_desc(code)} {exc}</p></body></html>'.encode())
|
||||||
|
logging.debug('requested')
|
||||||
|
|
||||||
|
def clear_pipe(self):
|
||||||
|
""" Obtains all possible data from Pipe connection if it exists. """
|
||||||
|
while True:
|
||||||
|
logging.debug('clear pipe start')
|
||||||
|
if self.pipe_conn.poll():
|
||||||
|
self.pipe_conn.recv()
|
||||||
|
continue
|
||||||
|
logging.debug('clear pipe finish')
|
||||||
|
break
|
||||||
|
|
||||||
|
def do_GET(self):
|
||||||
|
""" Handles POST requests. """
|
||||||
|
try:
|
||||||
|
#analyzing agent
|
||||||
|
try:
|
||||||
|
if 'gis' in dict(self.headers)['agent']:
|
||||||
|
logging.debug('Agent: GIS')
|
||||||
|
gis_agent = True
|
||||||
|
except:
|
||||||
|
logging.debug('Agent: Unknown')
|
||||||
|
gis_agent = False
|
||||||
|
|
||||||
|
# analyzing of parameters
|
||||||
|
fields = dict()
|
||||||
|
param_line = re.sub('/[?]*[&]*', '', self.path)
|
||||||
|
|
||||||
|
if '&' in param_line:
|
||||||
|
for p in param_line.split('&'):
|
||||||
|
print(p.split('='))
|
||||||
|
key, val = p.split('=')
|
||||||
|
fields[key] = val
|
||||||
|
logging.debug(f"I've got a GET request, fields = {fields} from {self.path}")
|
||||||
|
|
||||||
|
if len(fields) == 0:
|
||||||
|
# start page request
|
||||||
|
answer = self.get_html_content(Page_Type.START.value)
|
||||||
|
self.send_response(Request_Status.READY.value)
|
||||||
|
self.send_header("Content-type", "text/html")
|
||||||
|
self.send_header("Access-Control-Allow-Origin", "*")
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(answer.encode())
|
||||||
|
|
||||||
|
elif 'orderId' not in fields:
|
||||||
|
# first type of GET-request
|
||||||
|
|
||||||
|
#sending to scheduler new order
|
||||||
|
self.clear_pipe()
|
||||||
|
self.pipe_conn.send((0, fields))
|
||||||
|
|
||||||
|
# obtaining id from scheduler
|
||||||
|
if self.pipe_conn.poll(1):
|
||||||
|
logging.debug('net_interface: got from scheduler')
|
||||||
|
container = self.pipe_conn.recv()
|
||||||
|
logging.debug(f'OBTAINED {container}')
|
||||||
|
id_pin, is_valid = container
|
||||||
|
orderId, pincode = id_pin
|
||||||
|
logging.debug(f'net_interface: got from scheduler {orderId}, {is_valid}, {pincode}')
|
||||||
|
if is_valid:
|
||||||
|
self.send_response(Request_Status.READY.value)
|
||||||
|
logging.debug('net_interface: 0')
|
||||||
|
self.send_header("Content-type", "text/html")
|
||||||
|
self.send_header("Access-Control-Allow-Origin", "*")
|
||||||
|
logging.debug('net_interface: 1')
|
||||||
|
self.end_headers()
|
||||||
|
logging.debug('net_interface: 2')
|
||||||
|
if gis_agent:
|
||||||
|
logging.debug('net_interface: 3')
|
||||||
|
self.wfile.write(f'orderId={orderId}, pincode={pincode}'.encode())
|
||||||
|
else:
|
||||||
|
logging.debug('net_interface: 4')
|
||||||
|
answer = self.get_html_content(Page_Type.ORDER_REQUEST.value)
|
||||||
|
answer = answer.replace('ORDERID', str(orderId))
|
||||||
|
answer = answer.replace('PIN_CODE', pincode)
|
||||||
|
self.wfile.write(answer.encode())
|
||||||
|
else:
|
||||||
|
# internal error, bad request params
|
||||||
|
self.bad_request(Request_Status.INVALID_PARAM.value)
|
||||||
|
else:
|
||||||
|
# timeout error
|
||||||
|
self.bad_request(Request_Status.TIMEOUT.value)
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
# second type of GET-request
|
||||||
|
if 'pincode' not in fields:
|
||||||
|
self.bad_request(Request_Status.INVALID_PARAM.value)
|
||||||
|
else:
|
||||||
|
# asking scheduler about status of order
|
||||||
|
orderId = int(fields['orderId'])
|
||||||
|
pincode = fields['pincode']
|
||||||
|
self.clear_pipe()
|
||||||
|
self.pipe_conn.send((1, (orderId, pincode)))
|
||||||
|
|
||||||
|
# obtaining id from scheduler
|
||||||
|
if self.pipe_conn.poll(2):
|
||||||
|
status = self.pipe_conn.recv()
|
||||||
|
if status == Request_Status.READY.value:
|
||||||
|
output_data, img_format = self.buffer.pop_by_id(orderId)
|
||||||
|
self.send_response(Request_Status.READY.value)
|
||||||
|
self.send_header("Content-type", img_format)
|
||||||
|
self.send_header("Access-Control-Allow-Origin", "*")
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(output_data)
|
||||||
|
else:
|
||||||
|
logging.debug(f'status: {status}')
|
||||||
|
self.bad_request(status)
|
||||||
|
else:
|
||||||
|
self.bad_request(Request_Status.TIMEOUT.value)
|
||||||
|
|
||||||
|
except Exception as exc:
|
||||||
|
logging.debug("Error! {0}".format(exc))
|
||||||
|
self.bad_request(Request_Status.REQUEST_FAILED.value, exc)
|
||||||
|
|
||||||
|
class Net_Interface():
|
||||||
|
"""
|
||||||
|
A class to provide net-interface of map-server. For now it start http server.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
__________
|
||||||
|
host : str
|
||||||
|
IP adress or host name for the net interface module.
|
||||||
|
port : int
|
||||||
|
Connection port for the net interface module.
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
________
|
||||||
|
init_logging(page_type)
|
||||||
|
Starts logging to the file, obtained by get_log_path.
|
||||||
|
start_server()
|
||||||
|
Starts http server using Handler class for requests handling.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, host, port):
|
||||||
|
"""
|
||||||
|
Parameters:
|
||||||
|
__________
|
||||||
|
host : str
|
||||||
|
IP adress or host name for the net interface module.
|
||||||
|
port : int
|
||||||
|
Connection port for the net interface module.
|
||||||
|
"""
|
||||||
|
self.port = port
|
||||||
|
self.host = host
|
||||||
|
self.init_logging()
|
||||||
|
|
||||||
|
def init_logging(self):
|
||||||
|
""" Starts logging to the file, obtained by get_log_path. """
|
||||||
|
log_path = get_log_path()
|
||||||
|
if log_path == None:
|
||||||
|
logging.debug("Could not find GIS_ROOT environment variable")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
log_path += '/server.log'
|
||||||
|
logging.basicConfig(format='%(asctime)s %(levelname)-8s %(message)s', filename=log_path, encoding='utf-8', level=logging.DEBUG)
|
||||||
|
|
||||||
|
def start_server(self, pipe_conn, buffer, html_path):
|
||||||
|
""" Starts http server using Handler class for requests handling.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
__________
|
||||||
|
pipe_conn : multiprocessing.connection.Connection
|
||||||
|
Pipe connection instance for interacting with Scheduler.
|
||||||
|
buffer : Storage_Class instance
|
||||||
|
Buffer is used to store data from POST-requests obtained by Renderer.
|
||||||
|
html_path : str
|
||||||
|
The path to a folder with html pages that are used to responde clients.
|
||||||
|
"""
|
||||||
|
def handler(*args):
|
||||||
|
Handler(pipe_conn, buffer, html_path, *args)
|
||||||
|
|
||||||
|
self.net_server = http.server.HTTPServer((self.host, self.port), handler)
|
||||||
|
logging.debug('net_server Started')
|
||||||
|
self.net_server.serve_forever()
|
8
src/gis-map-server/nto/Makefile
Обычный файл
8
src/gis-map-server/nto/Makefile
Обычный файл
@ -0,0 +1,8 @@
|
|||||||
|
LIST=CPU
|
||||||
|
ifndef QRECURSE
|
||||||
|
QRECURSE=recurse.mk
|
||||||
|
ifdef QCONFIG
|
||||||
|
QRDIR=$(dir $(QCONFIG))
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
include $(QRDIR)$(QRECURSE)
|
8
src/gis-map-server/nto/arm/Makefile
Обычный файл
8
src/gis-map-server/nto/arm/Makefile
Обычный файл
@ -0,0 +1,8 @@
|
|||||||
|
LIST=VARIANT
|
||||||
|
ifndef QRECURSE
|
||||||
|
QRECURSE=recurse.mk
|
||||||
|
ifdef QCONFIG
|
||||||
|
QRDIR=$(dir $(QCONFIG))
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
include $(QRDIR)$(QRECURSE)
|
1
src/gis-map-server/nto/arm/o.le.v7/Makefile
Обычный файл
1
src/gis-map-server/nto/arm/o.le.v7/Makefile
Обычный файл
@ -0,0 +1 @@
|
|||||||
|
include ../../../common.mk
|
8
src/gis-map-server/nto/e2k/Makefile
Обычный файл
8
src/gis-map-server/nto/e2k/Makefile
Обычный файл
@ -0,0 +1,8 @@
|
|||||||
|
LIST=VARIANT
|
||||||
|
ifndef QRECURSE
|
||||||
|
QRECURSE=recurse.mk
|
||||||
|
ifdef QCONFIG
|
||||||
|
QRDIR=$(dir $(QCONFIG))
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
include $(QRDIR)$(QRECURSE)
|
1
src/gis-map-server/nto/e2k/o.le/Makefile
Обычный файл
1
src/gis-map-server/nto/e2k/o.le/Makefile
Обычный файл
@ -0,0 +1 @@
|
|||||||
|
include ../../../common.mk
|
8
src/gis-map-server/nto/mips/Makefile
Исполняемый файл
8
src/gis-map-server/nto/mips/Makefile
Исполняемый файл
@ -0,0 +1,8 @@
|
|||||||
|
LIST=VARIANT
|
||||||
|
ifndef QRECURSE
|
||||||
|
QRECURSE=recurse.mk
|
||||||
|
ifdef QCONFIG
|
||||||
|
QRDIR=$(dir $(QCONFIG))
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
include $(QRDIR)$(QRECURSE)
|
1
src/gis-map-server/nto/mips/o.be/Makefile
Исполняемый файл
1
src/gis-map-server/nto/mips/o.be/Makefile
Исполняемый файл
@ -0,0 +1 @@
|
|||||||
|
include ../../../common.mk
|
8
src/gis-map-server/nto/ppc/Makefile
Обычный файл
8
src/gis-map-server/nto/ppc/Makefile
Обычный файл
@ -0,0 +1,8 @@
|
|||||||
|
LIST=VARIANT
|
||||||
|
ifndef QRECURSE
|
||||||
|
QRECURSE=recurse.mk
|
||||||
|
ifdef QCONFIG
|
||||||
|
QRDIR=$(dir $(QCONFIG))
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
include $(QRDIR)$(QRECURSE)
|
1
src/gis-map-server/nto/ppc/o.be.spe/Makefile
Обычный файл
1
src/gis-map-server/nto/ppc/o.be.spe/Makefile
Обычный файл
@ -0,0 +1 @@
|
|||||||
|
include ../../../common.mk
|
1
src/gis-map-server/nto/ppc/o.be/Makefile
Обычный файл
1
src/gis-map-server/nto/ppc/o.be/Makefile
Обычный файл
@ -0,0 +1 @@
|
|||||||
|
include ../../../common.mk
|
8
src/gis-map-server/nto/x86/Makefile
Обычный файл
8
src/gis-map-server/nto/x86/Makefile
Обычный файл
@ -0,0 +1,8 @@
|
|||||||
|
LIST=VARIANT
|
||||||
|
ifndef QRECURSE
|
||||||
|
QRECURSE=recurse.mk
|
||||||
|
ifdef QCONFIG
|
||||||
|
QRDIR=$(dir $(QCONFIG))
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
include $(QRDIR)$(QRECURSE)
|
1
src/gis-map-server/nto/x86/o/Makefile
Обычный файл
1
src/gis-map-server/nto/x86/o/Makefile
Обычный файл
@ -0,0 +1 @@
|
|||||||
|
include ../../../common.mk
|
317
src/gis-map-server/scheduler.py
Обычный файл
317
src/gis-map-server/scheduler.py
Обычный файл
@ -0,0 +1,317 @@
|
|||||||
|
###############################################################################
|
||||||
|
# (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
#!/usr/bin/python3 -u -B
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import subprocess
|
||||||
|
import string
|
||||||
|
import random
|
||||||
|
|
||||||
|
from utils import Request_Status, get_log_path
|
||||||
|
|
||||||
|
|
||||||
|
class Worker():
|
||||||
|
"""
|
||||||
|
A class for organizing the parallel execution of any processes with required number of slots.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
__________
|
||||||
|
slots_num : int
|
||||||
|
A total number of available slots.
|
||||||
|
slots : array
|
||||||
|
An array of processes.
|
||||||
|
size : int
|
||||||
|
Current number of executing processes.
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
________
|
||||||
|
fill_slot(page_type)
|
||||||
|
Adds new process to the array.
|
||||||
|
free_slot(num)
|
||||||
|
Removes a process from slot with index = num.
|
||||||
|
check_free_slot()
|
||||||
|
Checks if there is at least one free slot in processes array.
|
||||||
|
is_busy()
|
||||||
|
Checks if there is at least one active process.
|
||||||
|
active_slots()
|
||||||
|
Returns a list of elements and its indexes from processes array if they are active.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, slots_num):
|
||||||
|
"""
|
||||||
|
Parameters:
|
||||||
|
__________
|
||||||
|
slots_num : int
|
||||||
|
A total number of available slots.
|
||||||
|
"""
|
||||||
|
self.slots_num = slots_num
|
||||||
|
self.slots = [0] * self.slots_num
|
||||||
|
self.size = 0
|
||||||
|
|
||||||
|
def fill_slot(self, element):
|
||||||
|
""" Adds new process to the array.
|
||||||
|
This function has to be called only after cheking free slot with "check_free_slot".
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
__________
|
||||||
|
element : any type
|
||||||
|
New element to be add into the processes array.
|
||||||
|
"""
|
||||||
|
logging.debug(f'{self.slots}')
|
||||||
|
for i in range(self.slots_num):
|
||||||
|
if self.slots[i] == 0:
|
||||||
|
self.slots[i] = element
|
||||||
|
self.size += 1
|
||||||
|
return
|
||||||
|
raise IndexError
|
||||||
|
|
||||||
|
def free_slot(self, num):
|
||||||
|
""" Removes a process from slot with index = num.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
__________
|
||||||
|
num : int
|
||||||
|
Index of removed slot.
|
||||||
|
"""
|
||||||
|
self.slots[num] = 0
|
||||||
|
self.size -= 1
|
||||||
|
|
||||||
|
def check_free_slot(self):
|
||||||
|
""" Checks if there is at least one free slot in processes array. """
|
||||||
|
if self.size < self.slots_num:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_busy(self):
|
||||||
|
""" Checks if there is at least one active process. """
|
||||||
|
if self.size > 0:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def active_slots(self):
|
||||||
|
""" Returns a list of elements and its indexes from processes array if they are active. """
|
||||||
|
return [ (self.slots[i], i) for i in range(self.slots_num) if self.slots[i] != 0]
|
||||||
|
|
||||||
|
class Scheduler():
|
||||||
|
"""
|
||||||
|
A class for organizing orders execution, id and pin codes generation, saving orders return codes, orders validating.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
__________
|
||||||
|
orders : dict
|
||||||
|
A dictionary of all oders. Key: id, value: [parameters, return codes, pincode]
|
||||||
|
queue : list
|
||||||
|
A queue with new orders that have not been executed.
|
||||||
|
counter : int
|
||||||
|
Total number of orders.
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
________
|
||||||
|
validator(params)
|
||||||
|
Validates parameters of an order.
|
||||||
|
generate_pincode(dictionary, size)
|
||||||
|
Generates new pincode of length = size, using characters from dictionary string.
|
||||||
|
add_order(params)
|
||||||
|
Adds new order with parameters = params to orders and queue.
|
||||||
|
check_order(id)
|
||||||
|
Checks if order with id exists.
|
||||||
|
start_scheduler(pipe_conn, host, port, slots_num)
|
||||||
|
Executes scheduler, starts an infinite loop for listening pipe connection = pipe_conn and organizing orders execution.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.orders = dict()
|
||||||
|
self.cached = dict()
|
||||||
|
self.queue = list()
|
||||||
|
self.counter = 0
|
||||||
|
|
||||||
|
def validator(self, params):
|
||||||
|
""" Validates parameters of an order.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
__________
|
||||||
|
params : list
|
||||||
|
A list of parameters for validating.
|
||||||
|
"""
|
||||||
|
params_list = ['lat', 'lon', 'scale', 'w', 'h', 'format']
|
||||||
|
for par in params_list:
|
||||||
|
if par not in params:
|
||||||
|
logging.debug(f'Bad params: no {par} in {params}')
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
float(params['lon'])
|
||||||
|
float(params['lat'])
|
||||||
|
except ValueError:
|
||||||
|
logging.debug(f'Bad params: lat, lan, w, h or scale is not float in {params}')
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not params['scale'].isdigit() or not params['w'].isdigit() or not params['h'].isdigit():
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def generate_pincode(self, dictionary, size):
|
||||||
|
""" Generates new pincode of length = size, using characters from dictionary string.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
__________
|
||||||
|
dictionary : str
|
||||||
|
A string with characters that is used as a dictionary for pincode creating.
|
||||||
|
size : int
|
||||||
|
A number of characters in picode.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
pincode
|
||||||
|
Generated pincode
|
||||||
|
"""
|
||||||
|
pincode = ''.join(random.choice(dictionary) for x in range(size))
|
||||||
|
return pincode
|
||||||
|
|
||||||
|
def add_order(self, params):
|
||||||
|
""" Adds new order with parameters = params to orders and queue.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
__________
|
||||||
|
params : list
|
||||||
|
A list of parameters of a new order.
|
||||||
|
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
id
|
||||||
|
Id for new order, equal to self.counter value
|
||||||
|
pincode
|
||||||
|
Generated pincode for new order
|
||||||
|
"""
|
||||||
|
self.counter += 1
|
||||||
|
pincode = self.generate_pincode(string.digits + string.ascii_letters, 6)
|
||||||
|
self.orders[self.counter] = [params, Request_Status.PROCESSING.value, pincode]
|
||||||
|
|
||||||
|
self.cached[tuple(params.values())] = self.counter
|
||||||
|
self.queue.append(self.counter)
|
||||||
|
return self.counter, pincode
|
||||||
|
|
||||||
|
def check_order(self, id):
|
||||||
|
""" Checks if order with id exists. """
|
||||||
|
return id in self.orders
|
||||||
|
|
||||||
|
|
||||||
|
def start_scheduler(self, pipe_conn, host, port, slots_num, sharedMemoryId):
|
||||||
|
""" Executes scheduler, starts an infinite loop for listening pipe connection = pipe_conn and organizing orders execution.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
__________
|
||||||
|
pipe_conn : multiprocessing.connection.Connection
|
||||||
|
Pipe connection instance for interacting with Net_Interface.
|
||||||
|
host : str
|
||||||
|
IP adress or host name for net interface.
|
||||||
|
port : int
|
||||||
|
Connection port for net interface.
|
||||||
|
slots_num : int
|
||||||
|
A total number of available slots.
|
||||||
|
"""
|
||||||
|
logging.debug('start')
|
||||||
|
try:
|
||||||
|
gis_path = os.environ['GIS_ROOT']
|
||||||
|
util_path = gis_path + '/sbin/gis-buffer-renderer'
|
||||||
|
logging.debug(f'Renderer path ={util_path}')
|
||||||
|
except Exception as exc:
|
||||||
|
logging.debug('Could not find GIS_ROOT')
|
||||||
|
|
||||||
|
worker = Worker(slots_num)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# checking new data in Pipe
|
||||||
|
if pipe_conn.poll():
|
||||||
|
data = pipe_conn.recv()
|
||||||
|
logging.debug(f'Scheduler got: {data}')
|
||||||
|
|
||||||
|
if data[0] == 0:
|
||||||
|
# request for new order
|
||||||
|
logging.debug(f'Scheduler new order {data[1]}')
|
||||||
|
code = 0
|
||||||
|
orderId = 0
|
||||||
|
pincode = 0
|
||||||
|
|
||||||
|
if self.validator(data[1]):
|
||||||
|
param_tuple = tuple(data[1].values())
|
||||||
|
if param_tuple in self.cached:
|
||||||
|
logging.debug(f'Order exist, cached data is used')
|
||||||
|
orderId = self.cached[param_tuple]
|
||||||
|
pincode = self.orders[self.cached[param_tuple]][2]
|
||||||
|
else:
|
||||||
|
orderId, pincode = self.add_order(data[1])
|
||||||
|
code = 1
|
||||||
|
pipe_conn.send(((orderId, pincode), code))
|
||||||
|
elif data[0] == 1:
|
||||||
|
# request to check order
|
||||||
|
orderId, pincode = data[1]
|
||||||
|
logging.debug(f'Scheduler checking order id={orderId}')
|
||||||
|
if self.check_order(orderId):
|
||||||
|
logging.debug(f'Status: {self.orders[orderId][1]}')
|
||||||
|
if self.orders[orderId][2] == pincode:
|
||||||
|
pipe_conn.send(self.orders[orderId][1])
|
||||||
|
if self.orders[orderId][1] == Request_Status.READY.value:
|
||||||
|
#self.orders[orderId][1] = Request_Status.DONE.value
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
logging.debug(f'Bad pincode {pincode} (not {self.orders[orderId][2]}) for order with id={orderId}')
|
||||||
|
pipe_conn.send(Request_Status.INVALID_PARAM.value)
|
||||||
|
else:
|
||||||
|
logging.debug(f'No order in scheduler with ID {orderId}')
|
||||||
|
pipe_conn.send(Request_Status.INVALID_PARAM.value)
|
||||||
|
elif data[0] == 2:
|
||||||
|
deleted_ids = data[1]
|
||||||
|
logging.debug(f'Scheduler deletes ids={deleted_ids}')
|
||||||
|
for i in deleted_ids:
|
||||||
|
self.cached.pop(tuple(self.orders[i][0].values()))
|
||||||
|
self.orders.pop(i)
|
||||||
|
try:
|
||||||
|
self.queue.remove(i)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
pipe_conn.send(True)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
#print('No data for scheduler')
|
||||||
|
pass
|
||||||
|
|
||||||
|
# checking queue
|
||||||
|
|
||||||
|
#if any of slots are free
|
||||||
|
if worker.check_free_slot():
|
||||||
|
#if current_order == False:
|
||||||
|
if self.queue:
|
||||||
|
current_order_id = self.queue.pop(0)
|
||||||
|
logging.debug(f'Current order id {current_order_id}')
|
||||||
|
params = self.orders[current_order_id][0]
|
||||||
|
logging.debug(f'{params}')
|
||||||
|
child = subprocess.Popen([util_path, f'-uhttp://{host}:{port}', f'-o{current_order_id}',f"-x{params['lon']}", f"-y{params['lat']}",
|
||||||
|
f"-s{params['scale']}", f"-w{params['w']}", f"-h{params['h']}", f"-f{params['format']}", f"-e{Request_Status.RENDER_FAILED.value}", f"-d{sharedMemoryId}"])
|
||||||
|
|
||||||
|
worker.fill_slot((current_order_id, child))
|
||||||
|
|
||||||
|
logging.debug('popen')
|
||||||
|
|
||||||
|
# if some slots are busy
|
||||||
|
# checking if order is ready
|
||||||
|
if worker.is_busy():
|
||||||
|
#if child.poll() != None:
|
||||||
|
for slot, id in worker.active_slots():
|
||||||
|
if slot[1].poll() != None:
|
||||||
|
print('order status ready', Request_Status.READY.value, type(Request_Status.READY.value))
|
||||||
|
logging.debug(f'Scheduler detects process as ready, return code: {slot[1].returncode}, order status ready {Request_Status.READY.value}')
|
||||||
|
if slot[1].returncode == 200:
|
||||||
|
self.orders[slot[0]][1] = Request_Status.READY.value
|
||||||
|
elif slot[1].returncode == Request_Status.NOMEM.value:
|
||||||
|
self.orders[slot[0]][1] = Request_Status.NOMEM.value
|
||||||
|
else:
|
||||||
|
self.orders[slot[0]][1] = Request_Status.RENDER_FAILED.value
|
||||||
|
worker.free_slot(id)
|
||||||
|
time.sleep(0.1)
|
||||||
|
return 2
|
156
src/gis-map-server/server.py
Исполняемый файл
156
src/gis-map-server/server.py
Исполняемый файл
@ -0,0 +1,156 @@
|
|||||||
|
###############################################################################
|
||||||
|
# (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
#!/usr/bin/python3 -uB
|
||||||
|
from net_interface import Net_Interface
|
||||||
|
from scheduler import Scheduler
|
||||||
|
from storage import Storage
|
||||||
|
from utils import get_log_path
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import shutil
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from multiprocessing import Process, Pipe
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
class Server:
|
||||||
|
"""
|
||||||
|
A class used to represent gis-map-server instance consisting of instances of Interface_Class, Storage_Class and Scheduler_Class classes.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
__________
|
||||||
|
host : str
|
||||||
|
IP adress or host name for the net interface module.
|
||||||
|
port : int
|
||||||
|
Connection port for the net interface module.
|
||||||
|
buf_size : int
|
||||||
|
Maximum buffer size in bytes.
|
||||||
|
slots_num : int
|
||||||
|
A total number of available slots.
|
||||||
|
html_path : str
|
||||||
|
The path to a folder with html pages that are used to responde clients.
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
________
|
||||||
|
server_init(Interface_Class, Storage_Class, Scheduler_Class)
|
||||||
|
Initializes server with instances of Interface_Class, Storage_Class and Scheduler_Class classes.
|
||||||
|
start()
|
||||||
|
Starts server execution: executes scheduler as a subprocess, executes Interface_Class listening.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, host, port, slots_num, buf_size, html_path, sharedMemoryId):
|
||||||
|
"""
|
||||||
|
Parameters:
|
||||||
|
__________
|
||||||
|
host : str
|
||||||
|
IP adress or host name for the net interface module.
|
||||||
|
port : int
|
||||||
|
Connection port for the net interface module.
|
||||||
|
buf_size : int
|
||||||
|
Maximum buffer size in bytes.
|
||||||
|
slots_num : int
|
||||||
|
A total number of available slots.
|
||||||
|
html_path : str
|
||||||
|
The path to a folder with html pages that are used to responde clients.
|
||||||
|
"""
|
||||||
|
self.port = port
|
||||||
|
self.host = host
|
||||||
|
self.buf_size = buf_size
|
||||||
|
self.slots_num = slots_num
|
||||||
|
self.html_path = html_path
|
||||||
|
self.sharedMemoryId = sharedMemoryId
|
||||||
|
|
||||||
|
def server_init(self, Interface_Class, Storage_Class, Scheduler_Class):
|
||||||
|
""" Initializes server with instances of Interface_Class, Storage_Class and Scheduler_Class classes.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
__________
|
||||||
|
Interface_Class : class
|
||||||
|
A class link to create net_interface.
|
||||||
|
Storage_Class : class
|
||||||
|
A class link to create storage.
|
||||||
|
Scheduler_Class : class
|
||||||
|
A class link to create scheduler.
|
||||||
|
"""
|
||||||
|
self.net_interface = Interface_Class(self.host, self.port)
|
||||||
|
self.buf_storage = Storage_Class(self.buf_size)
|
||||||
|
self.scheduler = Scheduler_Class()
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
""" Starts server execution: executes scheduler as a subprocess, executes Interface_Class listening. Creates Pipe between two processes."""
|
||||||
|
sched_conn, serv_conn = Pipe()
|
||||||
|
sched = Process(target=self.scheduler.start_scheduler, args=(sched_conn, self.host, self.port, self.slots_num, self.sharedMemoryId))
|
||||||
|
sched.start()
|
||||||
|
|
||||||
|
# Net_Interface start with access to Buf_Storage and Scheduler
|
||||||
|
self.net_interface.start_server(serv_conn, self.buf_storage, self.html_path)
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
""" Function for parsing program arguments """
|
||||||
|
parser = argparse.ArgumentParser(description='gis-map-server description:')
|
||||||
|
parser.add_argument('config_path', type=str,
|
||||||
|
help='a required string positional argument, path to gis-map-server config')
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
def parse_config(path):
|
||||||
|
""" Function parses config and return values of required options """
|
||||||
|
with open(path) as f:
|
||||||
|
content = f.readlines()
|
||||||
|
options = dict()
|
||||||
|
for line in content:
|
||||||
|
key, val = line.split('=')
|
||||||
|
options[key] = val.strip()
|
||||||
|
return options['SERVER_ADDRESS'], int(options['SERVER_PORT']), int(options['SLOTS_NUMBER']), int(options['STORAGE_MAX_SIZE']), options['HTML_PAGES_PATH'], options['GIS_SHID']
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
try:
|
||||||
|
args = parse_args()
|
||||||
|
except Exception as exp:
|
||||||
|
print(f"Arguments parsing error: {exp}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
address, port, slots_num, buf_size, html_path, sharedMemoryId = parse_config(args.config_path)
|
||||||
|
except Exception as exp:
|
||||||
|
print(f"Config parsing error {exp}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# log folder (re)creating
|
||||||
|
log_path = get_log_path()
|
||||||
|
if log_path == None:
|
||||||
|
print("Could not find GIS_ROOT environment variable")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
shutil.rmtree(log_path)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
except Exception as exc:
|
||||||
|
print("Could not get an access to log folder", log_path, exc)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.mkdir(log_path)
|
||||||
|
except Exception as exc:
|
||||||
|
print("Could not create log folder", log_path, exc)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
subprocess.run(["gis-control", f'-s{sharedMemoryId}'])
|
||||||
|
except Exception as exc:
|
||||||
|
print("Could not make data request for sharedMemoryId", sharedMemoryId, exc)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# starting the server
|
||||||
|
try:
|
||||||
|
new_server = Server(address, port, slots_num, buf_size, os.environ['GIS_ROOT'] + '/' + html_path, sharedMemoryId)
|
||||||
|
new_server.server_init(Net_Interface, Storage, Scheduler)
|
||||||
|
new_server.start()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print('Server is closed')
|
||||||
|
sys.exit(0)
|
89
src/gis-map-server/storage.py
Обычный файл
89
src/gis-map-server/storage.py
Обычный файл
@ -0,0 +1,89 @@
|
|||||||
|
###############################################################################
|
||||||
|
# (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
#!/usr/bin/python3 -uB
|
||||||
|
from utils import Request_Status
|
||||||
|
|
||||||
|
class Storage():
|
||||||
|
"""
|
||||||
|
A class used to represent data storage for buffers obtained from Renderer.
|
||||||
|
From the storage data is popped from Iterface class when it is requested by the client.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
__________
|
||||||
|
storage : dict
|
||||||
|
A dictionary used to store buffers. Key: id, val: (data, img_format).
|
||||||
|
current_size : int
|
||||||
|
Current size of buffer in bytes. It counts only size of data, not the dictionary element.
|
||||||
|
buf_size : int
|
||||||
|
Maximum buffer size in bytes.
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
________
|
||||||
|
push(id, data, img_format, img_length)
|
||||||
|
Pushes data to the storage.
|
||||||
|
pop_by_id(id)
|
||||||
|
Pops data by id.
|
||||||
|
"""
|
||||||
|
def __init__(self, buf_size):
|
||||||
|
self.storage = dict()
|
||||||
|
self.current_size = 0
|
||||||
|
self.buf_size = buf_size
|
||||||
|
|
||||||
|
def push(self, id, data, img_format, img_length):
|
||||||
|
"""Pushes data to the storage.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
__________
|
||||||
|
id : int
|
||||||
|
An id of the order, whose data will be stored.
|
||||||
|
data : byte str
|
||||||
|
Data to store in the storage.
|
||||||
|
img_format : str
|
||||||
|
A string which specifies a format of data.
|
||||||
|
img_length : int
|
||||||
|
Data size in bytes.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
status
|
||||||
|
New order status after pushing the data
|
||||||
|
"""
|
||||||
|
if id in self.storage:
|
||||||
|
return Request_Status.INVALID_PARAM.value
|
||||||
|
|
||||||
|
if img_length > self.buf_size:
|
||||||
|
return Request_Status.NOMEM.value
|
||||||
|
|
||||||
|
deleted_ids = list()
|
||||||
|
if self.buf_size < (self.current_size + img_length):
|
||||||
|
keys = list(self.storage.keys())
|
||||||
|
for i in keys:
|
||||||
|
d = self.storage.pop(i)
|
||||||
|
deleted_ids.append(i)
|
||||||
|
self.current_size -= len(d[0])
|
||||||
|
if self.buf_size >= (self.current_size + img_length):
|
||||||
|
break
|
||||||
|
|
||||||
|
self.storage[id] = (data, img_format)
|
||||||
|
self.current_size += img_length
|
||||||
|
return Request_Status.READY.value, deleted_ids
|
||||||
|
|
||||||
|
def pop_by_id(self, id):
|
||||||
|
"""
|
||||||
|
Pops data by id.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
__________
|
||||||
|
id : int
|
||||||
|
An id of the order, whose data is stored.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
data
|
||||||
|
Data popped by id
|
||||||
|
"""
|
||||||
|
data = self.storage[id]
|
||||||
|
#self.current_size -= len(data[0])
|
||||||
|
return data
|
51
src/gis-map-server/utils.py
Обычный файл
51
src/gis-map-server/utils.py
Обычный файл
@ -0,0 +1,51 @@
|
|||||||
|
###############################################################################
|
||||||
|
# (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
#!/usr/bin/python3 -uB
|
||||||
|
from enum import Enum, unique
|
||||||
|
import os
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class Request_Status(Enum):
|
||||||
|
# statuses below are saved inside of Scheduler order table
|
||||||
|
READY = 200
|
||||||
|
PROCESSING = 202
|
||||||
|
INVALID_PARAM = 400
|
||||||
|
DONE = 410
|
||||||
|
NOMEM = 418
|
||||||
|
RENDER_FAILED = 500
|
||||||
|
|
||||||
|
# codes below are used only to responde to the client
|
||||||
|
TIMEOUT = 408
|
||||||
|
REQUEST_FAILED = 520
|
||||||
|
|
||||||
|
def get_status_desc(status):
|
||||||
|
"""Function returns a description for an error code."""
|
||||||
|
if status == Request_Status.READY.value:
|
||||||
|
return 'Request is ready'
|
||||||
|
elif status == Request_Status.PROCESSING.value:
|
||||||
|
return 'Request is processing'
|
||||||
|
elif status == Request_Status.INVALID_PARAM.value:
|
||||||
|
return 'Request has invallid parameters'
|
||||||
|
elif status == Request_Status.DONE.value:
|
||||||
|
return 'Request has been already obtained'
|
||||||
|
elif status == Request_Status.NOMEM.value:
|
||||||
|
return 'Request is not ready - not enough memory on server'
|
||||||
|
elif status == Request_Status.RENDER_FAILED.value:
|
||||||
|
return 'Request is failed - renderer did not finish successfully'
|
||||||
|
elif status == Request_Status.TIMEOUT.value:
|
||||||
|
return 'Request status is unknown, timeout error'
|
||||||
|
elif status == Request_Status.REQUEST_FAILED.value:
|
||||||
|
return 'Request is failed'
|
||||||
|
else:
|
||||||
|
return 'Unknown Error'
|
||||||
|
|
||||||
|
def get_log_path():
|
||||||
|
"""Function returns the log path for gis-map-server."""
|
||||||
|
try:
|
||||||
|
path = os.environ['GIS_ROOT']
|
||||||
|
path += '/data/logs/gis-map-server/'
|
||||||
|
except:
|
||||||
|
path = None
|
||||||
|
return path
|
Загрузка…
Ссылка в новой задаче
Block a user