Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
U
uniset2
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
UniSet project repositories
uniset2
Commits
ad34319c
Commit
ad34319c
authored
Dec 05, 2016
by
Pavel Vainerman
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
(REST API): переделал формат json в ответе на запрос заказчиков
по датчику.
parent
9260798b
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
75 additions
and
40 deletions
+75
-40
TODO
TODO
+5
-1
libuniset2.spec
conf/libuniset2.spec
+4
-1
IONotifyController.h
include/IONotifyController.h
+1
-1
UniSetObject.h
include/UniSetObject.h
+5
-0
libUniSet2.pc.in
libUniSet2.pc.in
+1
-1
UTypes.h
python/lib/pyUniSet/UTypes.h
+1
-0
UniSetObject.cc
src/ObjectRepository/UniSetObject.cc
+14
-1
IOController.cc
src/Processes/IOController.cc
+3
-1
IONotifyController.cc
src/Processes/IONotifyController.cc
+39
-32
ujson.cc
src/Various/ujson.cc
+2
-2
No files found.
TODO
View file @
ad34319c
...
@@ -58,7 +58,11 @@ HTTP API:
...
@@ -58,7 +58,11 @@ HTTP API:
= Сделать возможность настраивать параметры httpserver-а из командной строки (количество потоков и т.п.)
= Сделать возможность настраивать параметры httpserver-а из командной строки (количество потоков и т.п.)
- ТЕСТЫ (как вариант поизучать про тестовые фреймворки на питоне (pytest?)
- ТЕСТЫ (как вариант поизучать про тестовые фреймворки на питоне (pytest?)
- /sensors/XXX?config=name,textname,... - запрос полей (из configure.xml)
- /sensors/XXX?config=name,textname,... - запрос полей (из configure.xml)
- /conf/objects?prop1,prop2,prop3,.. - props from condigure.xml
- /conf/nodes?prop1,prop2,prop3,.. - props from condigure.xml
- /conf/controlers?..
- /conf/services?...
- /conf/...
Version 2.5
Version 2.5
============
============
...
...
conf/libuniset2.spec
View file @
ad34319c
...
@@ -16,7 +16,7 @@
...
@@ -16,7 +16,7 @@
Name: libuniset2
Name: libuniset2
Version: 2.6
Version: 2.6
Release: alt3.
2
Release: alt3.
3
Summary: UniSet - library for building distributed industrial control systems
Summary: UniSet - library for building distributed industrial control systems
License: LGPL
License: LGPL
...
@@ -511,6 +511,9 @@ mv -f %buildroot%python_sitelibdir_noarch/* %buildroot%python_sitelibdir/%oname
...
@@ -511,6 +511,9 @@ mv -f %buildroot%python_sitelibdir_noarch/* %buildroot%python_sitelibdir/%oname
# - getInfo() deprecated..
# - getInfo() deprecated..
%changelog
%changelog
* Sun Dec 04 2016 Pavel Vainerman <pv@altlinux.ru> 2.6-alt3.3
- IOC rest api: reformat json reply
* Sat Dec 03 2016 Pavel Vainerman <pv@altlinux.ru> 2.6-alt3.2
* Sat Dec 03 2016 Pavel Vainerman <pv@altlinux.ru> 2.6-alt3.2
- getChangedTime --> getTimeChange
- getChangedTime --> getTimeChange
- getInfo( long param ) --> getInfo( string param )
- getInfo( long param ) --> getInfo( string param )
...
...
include/IONotifyController.h
View file @
ad34319c
...
@@ -328,7 +328,7 @@ class IONotifyController:
...
@@ -328,7 +328,7 @@ class IONotifyController:
// http api
// http api
Poco
::
JSON
::
Object
::
Ptr
request_consumers
(
const
std
::
string
&
req
,
const
Poco
::
URI
::
QueryParameters
&
p
);
Poco
::
JSON
::
Object
::
Ptr
request_consumers
(
const
std
::
string
&
req
,
const
Poco
::
URI
::
QueryParameters
&
p
);
Poco
::
JSON
::
Object
::
Ptr
request_lost
(
const
string
&
req
,
const
Poco
::
URI
::
QueryParameters
&
p
);
Poco
::
JSON
::
Object
::
Ptr
request_lost
(
const
string
&
req
,
const
Poco
::
URI
::
QueryParameters
&
p
);
Poco
::
JSON
::
Object
::
Ptr
getConsumers
(
uniset
::
ObjectId
sid
,
ConsumerListInfo
&
clist
,
bool
no
Empty
=
true
);
Poco
::
JSON
::
Object
::
Ptr
getConsumers
(
uniset
::
ObjectId
sid
,
ConsumerListInfo
&
clist
,
bool
ifNot
Empty
=
true
);
#endif
#endif
private
:
private
:
...
...
include/UniSetObject.h
View file @
ad34319c
...
@@ -202,6 +202,11 @@ class UniSetObject:
...
@@ -202,6 +202,11 @@ class UniSetObject:
/*! false - завершить работу потока обработки сообщений */
/*! false - завершить работу потока обработки сообщений */
void
setActive
(
bool
set
);
void
setActive
(
bool
set
);
#ifndef DISABLE_REST_API
// вспомогательные функции
virtual
Poco
::
JSON
::
Object
::
Ptr
httpGetMyInfo
(
Poco
::
JSON
::
Object
::
Ptr
root
);
#endif
private
:
private
:
friend
class
UniSetManager
;
friend
class
UniSetManager
;
...
...
libUniSet2.pc.in
View file @
ad34319c
...
@@ -7,5 +7,5 @@ Name: libUniSet2
...
@@ -7,5 +7,5 @@ Name: libUniSet2
Description: Support library for UniSet
Description: Support library for UniSet
Requires: libxml-2.0 sigc++-2.0 omniORB4 libev
Requires: libxml-2.0 sigc++-2.0 omniORB4 libev
Version: @VERSION@
Version: @VERSION@
Libs: -L${libdir} -lUniSet2 -lPocoFoundation -lPocoNet
Libs: -L${libdir} -lUniSet2 -lPocoFoundation -lPocoNet
-lPocoJSON
Cflags: -I${includedir}/@PACKAGE@ -D__OMNIORB4 -std=c++11 -D_GLIBCXX_USE_NANOSLEEP @REST_API_CFLAGS@
Cflags: -I${includedir}/@PACKAGE@ -D__OMNIORB4 -std=c++11 -D_GLIBCXX_USE_NANOSLEEP @REST_API_CFLAGS@
python/lib/pyUniSet/UTypes.h
View file @
ad34319c
...
@@ -72,6 +72,7 @@ struct ShortIOInfo
...
@@ -72,6 +72,7 @@ struct ShortIOInfo
unsigned
long
tv_sec
;
unsigned
long
tv_sec
;
unsigned
long
tv_nsec
;
unsigned
long
tv_nsec
;
long
supplier
;
long
supplier
;
long
supplier_node
;
};
};
}
}
...
...
src/ObjectRepository/UniSetObject.cc
View file @
ad34319c
...
@@ -391,7 +391,7 @@ void UniSetObject::push( const TransportMessage& tm )
...
@@ -391,7 +391,7 @@ void UniSetObject::push( const TransportMessage& tm )
Poco
::
JSON
::
Object
::
Ptr
UniSetObject
::
httpGet
(
const
Poco
::
URI
::
QueryParameters
&
p
)
Poco
::
JSON
::
Object
::
Ptr
UniSetObject
::
httpGet
(
const
Poco
::
URI
::
QueryParameters
&
p
)
{
{
Poco
::
JSON
::
Object
::
Ptr
jret
=
new
Poco
::
JSON
::
Object
();
Poco
::
JSON
::
Object
::
Ptr
jret
=
new
Poco
::
JSON
::
Object
();
Poco
::
JSON
::
Object
::
Ptr
jdata
=
uniset
::
json
::
make_child
(
jret
,
myname
);
Poco
::
JSON
::
Object
::
Ptr
jdata
=
uniset
::
json
::
make_child
(
jret
,
"object"
);
jdata
->
set
(
"name"
,
myname
);
jdata
->
set
(
"name"
,
myname
);
jdata
->
set
(
"id"
,
getId
());
jdata
->
set
(
"id"
,
getId
());
...
@@ -408,6 +408,19 @@ Poco::JSON::Object::Ptr UniSetObject::httpHelp( const Poco::URI::QueryParameters
...
@@ -408,6 +408,19 @@ Poco::JSON::Object::Ptr UniSetObject::httpHelp( const Poco::URI::QueryParameters
uniset
::
json
::
help
::
object
myhelp
(
myname
);
uniset
::
json
::
help
::
object
myhelp
(
myname
);
return
myhelp
;
return
myhelp
;
}
}
// ------------------------------------------------------------------------------------------
Poco
::
JSON
::
Object
::
Ptr
UniSetObject
::
httpGetMyInfo
(
Poco
::
JSON
::
Object
::
Ptr
root
)
{
Poco
::
JSON
::
Object
::
Ptr
my
=
uniset
::
json
::
make_child
(
root
,
"object"
);
my
->
set
(
"name"
,
myname
);
my
->
set
(
"id"
,
getId
());
my
->
set
(
"msgCount"
,
countMessages
());
my
->
set
(
"lostMessages"
,
getCountOfLostMessages
());
my
->
set
(
"maxSizeOfMessageQueue"
,
getMaxSizeOfMessageQueue
());
my
->
set
(
"isActive"
,
isActive
());
my
->
set
(
"objectType"
,
getStrType
());
return
my
;
}
#endif
#endif
// ------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------
ObjectPtr
UniSetObject
::
getRef
()
const
ObjectPtr
UniSetObject
::
getRef
()
const
...
...
src/Processes/IOController.cc
View file @
ad34319c
...
@@ -915,6 +915,7 @@ Poco::JSON::Object::Ptr IOController::request_get( const string& req, const Poco
...
@@ -915,6 +915,7 @@ Poco::JSON::Object::Ptr IOController::request_get( const string& req, const Poco
Poco
::
JSON
::
Object
::
Ptr
jdata
=
new
Poco
::
JSON
::
Object
();
Poco
::
JSON
::
Object
::
Ptr
jdata
=
new
Poco
::
JSON
::
Object
();
Poco
::
JSON
::
Array
::
Ptr
jsens
=
new
Poco
::
JSON
::
Array
();
Poco
::
JSON
::
Array
::
Ptr
jsens
=
new
Poco
::
JSON
::
Array
();
jdata
->
set
(
"sensors"
,
jsens
);
jdata
->
set
(
"sensors"
,
jsens
);
auto
my
=
httpGetMyInfo
(
jdata
);
Poco
::
JSON
::
Object
::
Ptr
nullObject
=
new
Poco
::
JSON
::
Object
();
Poco
::
JSON
::
Object
::
Ptr
nullObject
=
new
Poco
::
JSON
::
Object
();
for
(
const
auto
&
s
:
slist
)
for
(
const
auto
&
s
:
slist
)
...
@@ -947,7 +948,7 @@ Poco::JSON::Object::Ptr IOController::request_get( const string& req, const Poco
...
@@ -947,7 +948,7 @@ Poco::JSON::Object::Ptr IOController::request_get( const string& req, const Poco
}
}
}
}
return
uniset
::
json
::
make_object
(
myname
,
jdata
)
;
return
jdata
;
}
}
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void
IOController
::
getSensorInfo
(
Poco
::
JSON
::
Array
::
Ptr
&
jdata
,
std
::
shared_ptr
<
USensorInfo
>&
s
,
bool
shortInfo
)
void
IOController
::
getSensorInfo
(
Poco
::
JSON
::
Array
::
Ptr
&
jdata
,
std
::
shared_ptr
<
USensorInfo
>&
s
,
bool
shortInfo
)
...
@@ -998,6 +999,7 @@ Poco::JSON::Object::Ptr IOController::request_sensors( const string& req, const
...
@@ -998,6 +999,7 @@ Poco::JSON::Object::Ptr IOController::request_sensors( const string& req, const
{
{
Poco
::
JSON
::
Object
::
Ptr
jdata
=
new
Poco
::
JSON
::
Object
();
Poco
::
JSON
::
Object
::
Ptr
jdata
=
new
Poco
::
JSON
::
Object
();
Poco
::
JSON
::
Array
::
Ptr
jsens
=
uniset
::
json
::
make_child_array
(
jdata
,
"sensors"
);
Poco
::
JSON
::
Array
::
Ptr
jsens
=
uniset
::
json
::
make_child_array
(
jdata
,
"sensors"
);
auto
my
=
httpGetMyInfo
(
jdata
);
size_t
num
=
0
;
size_t
num
=
0
;
size_t
offset
=
0
;
size_t
offset
=
0
;
...
...
src/Processes/IONotifyController.cc
View file @
ad34319c
...
@@ -1209,13 +1209,15 @@ Poco::JSON::Object::Ptr IONotifyController::httpRequest( const string& req, cons
...
@@ -1209,13 +1209,15 @@ Poco::JSON::Object::Ptr IONotifyController::httpRequest( const string& req, cons
Poco
::
JSON
::
Object
::
Ptr
IONotifyController
::
request_consumers
(
const
string
&
req
,
const
Poco
::
URI
::
QueryParameters
&
p
)
Poco
::
JSON
::
Object
::
Ptr
IONotifyController
::
request_consumers
(
const
string
&
req
,
const
Poco
::
URI
::
QueryParameters
&
p
)
{
{
Poco
::
JSON
::
Object
::
Ptr
json
=
new
Poco
::
JSON
::
Object
();
Poco
::
JSON
::
Object
::
Ptr
json
=
new
Poco
::
JSON
::
Object
();
Poco
::
JSON
::
Object
::
Ptr
mydata
=
uniset
::
json
::
make_child
(
json
,
myname
);
Poco
::
JSON
::
Array
::
Ptr
jdata
=
uniset
::
json
::
make_child_array
(
json
,
"sensors"
);
Poco
::
JSON
::
Array
::
Ptr
jdata
=
uniset
::
json
::
make_child_array
(
mydata
,
"consumers"
);
auto
my
=
httpGetMyInfo
(
json
);
auto
oind
=
uniset_conf
()
->
oind
;
auto
oind
=
uniset_conf
()
->
oind
;
std
::
list
<
ParamSInfo
>
slist
;
std
::
list
<
ParamSInfo
>
slist
;
ConsumerListInfo
emptyList
;
if
(
p
.
size
()
>
0
)
if
(
p
.
size
()
>
0
)
{
{
if
(
!
p
[
0
].
first
.
empty
()
)
if
(
!
p
[
0
].
first
.
empty
()
)
...
@@ -1234,19 +1236,13 @@ Poco::JSON::Object::Ptr IONotifyController::request_consumers( const string& req
...
@@ -1234,19 +1236,13 @@ Poco::JSON::Object::Ptr IONotifyController::request_consumers( const string& req
// Проход по списку заданных..
// Проход по списку заданных..
if
(
!
slist
.
empty
()
)
if
(
!
slist
.
empty
()
)
{
{
auto
jnotfound
=
uniset
::
json
::
make_child_array
(
mydata
,
"notfound"
);
for
(
const
auto
&
s
:
slist
)
for
(
const
auto
&
s
:
slist
)
{
{
auto
a
=
askIOList
.
find
(
s
.
si
.
id
);
auto
a
=
askIOList
.
find
(
s
.
si
.
id
);
if
(
a
==
askIOList
.
end
()
)
if
(
a
==
askIOList
.
end
()
)
{
jdata
->
add
(
getConsumers
(
s
.
si
.
id
,
emptyList
,
false
)
);
jnotfound
->
add
(
std
::
to_string
(
s
.
si
.
id
));
else
continue
;
}
// Включаем в ответ все, даже если список заказчиков пустой
jdata
->
add
(
getConsumers
(
a
->
first
,
a
->
second
,
false
)
);
jdata
->
add
(
getConsumers
(
a
->
first
,
a
->
second
,
false
)
);
}
}
}
}
...
@@ -1254,9 +1250,8 @@ Poco::JSON::Object::Ptr IONotifyController::request_consumers( const string& req
...
@@ -1254,9 +1250,8 @@ Poco::JSON::Object::Ptr IONotifyController::request_consumers( const string& req
{
{
for
(
auto
&&
a
:
askIOList
)
for
(
auto
&&
a
:
askIOList
)
{
{
// добавляем только датчики с непустым списком заказчиков
// добавляем только датчики
только
с непустым списком заказчиков
auto
jret
=
getConsumers
(
a
.
first
,
a
.
second
,
true
);
auto
jret
=
getConsumers
(
a
.
first
,
a
.
second
,
true
);
if
(
jret
)
if
(
jret
)
jdata
->
add
(
jret
);
jdata
->
add
(
jret
);
}
}
...
@@ -1265,33 +1260,46 @@ Poco::JSON::Object::Ptr IONotifyController::request_consumers( const string& req
...
@@ -1265,33 +1260,46 @@ Poco::JSON::Object::Ptr IONotifyController::request_consumers( const string& req
return
json
;
return
json
;
}
}
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
Poco
::
JSON
::
Object
::
Ptr
IONotifyController
::
getConsumers
(
ObjectId
sid
,
ConsumerListInfo
&
ci
,
bool
no
Empty
)
Poco
::
JSON
::
Object
::
Ptr
IONotifyController
::
getConsumers
(
ObjectId
sid
,
ConsumerListInfo
&
ci
,
bool
ifNot
Empty
)
{
{
/* Создаём json
* { {"sensor":
* {"id": "xxxx"},
* {"name": "xxxx"}
* },
* "consumers": [
* {..consumer1 info },
* {..consumer2 info },
* {..consumer3 info },
* {..consumer4 info }
* ]
* }
*/
Poco
::
JSON
::
Object
::
Ptr
jret
=
new
Poco
::
JSON
::
Object
();
Poco
::
JSON
::
Object
::
Ptr
jret
=
new
Poco
::
JSON
::
Object
();
auto
oind
=
uniset_conf
()
->
oind
;
uniset_rwmutex_rlock
lock
(
ci
.
mut
);
uniset_rwmutex_rlock
lock
(
ci
.
mut
);
if
(
ci
.
clst
.
empty
()
&&
no
Empty
)
if
(
ci
.
clst
.
empty
()
&&
ifNot
Empty
)
return
jret
;
return
jret
;
string
strID
(
std
::
to_string
(
sid
)
);
auto
oind
=
uniset_conf
()
->
oind
;
auto
jsens
=
uniset
::
json
::
make_child
(
jret
,
strID
);
auto
jsens
=
uniset
::
json
::
make_child
(
jret
,
"sensor"
);
jsens
->
set
(
"id"
,
sid
);
jsens
->
set
(
"id"
,
strID
);
jsens
->
set
(
"name"
,
ORepHelpers
::
getShortName
(
oind
->
getMapName
(
sid
)));
jsens
->
set
(
"sensor_name"
,
ORepHelpers
::
getShortName
(
oind
->
getMapName
(
sid
)));
auto
jcons
=
uniset
::
json
::
make_child
(
jsens
,
"consumers"
);
auto
jcons
=
uniset
::
json
::
make_child_array
(
jret
,
"consumers"
);
for
(
const
auto
&
c
:
ci
.
clst
)
for
(
const
auto
&
c
:
ci
.
clst
)
{
{
string
cid
(
std
::
to_string
(
c
.
id
)
);
Poco
::
JSON
::
Object
::
Ptr
consumer
=
new
Poco
::
JSON
::
Object
();
auto
jconsinfo
=
uniset
::
json
::
make_child
(
jcons
,
cid
);
consumer
->
set
(
"id"
,
c
.
id
);
jconsinfo
->
set
(
"id"
,
c
.
id
);
consumer
->
set
(
"name"
,
ORepHelpers
::
getShortName
(
oind
->
getMapName
(
c
.
id
)));
jconsinfo
->
set
(
"name"
,
ORepHelpers
::
getShortName
(
oind
->
getMapName
(
c
.
id
)));
consumer
->
set
(
"node"
,
c
.
node
);
jconsinfo
->
set
(
"lostEvents"
,
c
.
lostEvents
);
consumer
->
set
(
"node_name"
,
oind
->
getNodeName
(
c
.
node
));
jconsinfo
->
set
(
"attempt"
,
c
.
attempt
);
consumer
->
set
(
"lostEvents"
,
c
.
lostEvents
);
jconsinfo
->
set
(
"smCount"
,
c
.
smCount
);
consumer
->
set
(
"attempt"
,
c
.
attempt
);
consumer
->
set
(
"smCount"
,
c
.
smCount
);
jcons
->
add
(
consumer
);
}
}
return
jret
;
return
jret
;
...
@@ -1300,9 +1308,8 @@ Poco::JSON::Object::Ptr IONotifyController::getConsumers( ObjectId sid, Consumer
...
@@ -1300,9 +1308,8 @@ Poco::JSON::Object::Ptr IONotifyController::getConsumers( ObjectId sid, Consumer
Poco
::
JSON
::
Object
::
Ptr
IONotifyController
::
request_lost
(
const
string
&
req
,
const
Poco
::
URI
::
QueryParameters
&
p
)
Poco
::
JSON
::
Object
::
Ptr
IONotifyController
::
request_lost
(
const
string
&
req
,
const
Poco
::
URI
::
QueryParameters
&
p
)
{
{
Poco
::
JSON
::
Object
::
Ptr
json
=
new
Poco
::
JSON
::
Object
();
Poco
::
JSON
::
Object
::
Ptr
json
=
new
Poco
::
JSON
::
Object
();
Poco
::
JSON
::
Array
::
Ptr
jdata
=
uniset
::
json
::
make_child_array
(
json
,
"lost consumers"
);
Poco
::
JSON
::
Object
::
Ptr
mydata
=
uniset
::
json
::
make_child
(
json
,
myname
);
auto
my
=
httpGetMyInfo
(
json
);
Poco
::
JSON
::
Array
::
Ptr
jdata
=
uniset
::
json
::
make_child_array
(
mydata
,
"lost consumers"
);
auto
oind
=
uniset_conf
()
->
oind
;
auto
oind
=
uniset_conf
()
->
oind
;
...
...
src/Various/ujson.cc
View file @
ad34319c
...
@@ -110,14 +110,14 @@ uniset::json::help::object::operator Poco::JSON::Object::Ptr()
...
@@ -110,14 +110,14 @@ uniset::json::help::object::operator Poco::JSON::Object::Ptr()
return
root
;
return
root
;
}
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
Poco
::
JSON
::
Object
::
Ptr
json
::
make_child
(
Poco
::
JSON
::
Object
::
Ptr
&
root
,
const
std
::
__cxx11
::
string
&
key
)
Poco
::
JSON
::
Object
::
Ptr
json
::
make_child
(
Poco
::
JSON
::
Object
::
Ptr
&
root
,
const
std
::
string
&
key
)
{
{
Poco
::
JSON
::
Object
::
Ptr
child
=
new
Poco
::
JSON
::
Object
();
Poco
::
JSON
::
Object
::
Ptr
child
=
new
Poco
::
JSON
::
Object
();
root
->
set
(
key
,
child
);
root
->
set
(
key
,
child
);
return
child
;
return
child
;
}
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
Poco
::
JSON
::
Array
::
Ptr
json
::
make_child_array
(
Poco
::
JSON
::
Object
::
Ptr
&
root
,
const
std
::
__cxx11
::
string
&
key
)
Poco
::
JSON
::
Array
::
Ptr
json
::
make_child_array
(
Poco
::
JSON
::
Object
::
Ptr
&
root
,
const
std
::
string
&
key
)
{
{
Poco
::
JSON
::
Array
::
Ptr
child
=
new
Poco
::
JSON
::
Array
();
Poco
::
JSON
::
Array
::
Ptr
child
=
new
Poco
::
JSON
::
Array
();
root
->
set
(
key
,
child
);
root
->
set
(
key
,
child
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment