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
a46c3b60
Commit
a46c3b60
authored
Oct 25, 2020
by
Pavel Vainerman
Browse files
Options
Browse Files
Download
Plain Diff
backported to p9 as 2.8-alt14.M90P.15 (with rpmbph script)
parents
3c8466bb
3ee114c9
Show whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
107 additions
and
16 deletions
+107
-16
README.md
README.md
+2
-0
libuniset2.spec
conf/libuniset2.spec
+17
-2
configure.ac
configure.ac
+1
-1
MySQLInterface.h
extensions/DBServer-MySQL/MySQLInterface.h
+1
-0
PostgreSQLInterface.cc
extensions/DBServer-PostgreSQL/PostgreSQLInterface.cc
+9
-5
PostgreSQLInterface.h
extensions/DBServer-PostgreSQL/PostgreSQLInterface.h
+2
-1
db_create_example.sql
extensions/DBServer-PostgreSQL/db_create_example.sql
+11
-0
SQLiteInterface.cc
extensions/DBServer-SQLite/SQLiteInterface.cc
+6
-0
mbslave-tests.at
extensions/ModbusSlave/tests/mbslave-tests.at
+1
-1
SharedMemory.h
extensions/SharedMemory/SharedMemory.h
+1
-1
UNetExchange.cc
extensions/UNetUDP/UNetExchange.cc
+4
-0
UNetExchange.h
extensions/UNetUDP/UNetExchange.h
+7
-0
UNetSender.cc
extensions/UNetUDP/UNetSender.cc
+14
-0
UNetSender.h
extensions/UNetUDP/UNetSender.h
+5
-0
unetudp-tests.at
extensions/UNetUDP/tests/unetudp-tests.at
+1
-1
HourGlass.h
include/HourGlass.h
+2
-0
Pulse.h
include/Pulse.h
+2
-0
Trigger.h
include/Trigger.h
+1
-0
UHelpers.h
include/UHelpers.h
+2
-2
libUniSet2.pc.in
libUniSet2.pc.in
+1
-1
LogSession.cc
src/Log/LogSession.cc
+1
-1
test_utypes.cc
tests/test_utypes.cc
+2
-0
UConnector.h
wrappers/core/UConnector.h
+4
-0
UModbus.h
wrappers/core/UModbus.h
+4
-0
UProxyObject.h
wrappers/core/UProxyObject.h
+3
-0
PyUInterface.h
wrappers/python/lib/pyUniSet/PyUInterface.h
+3
-0
No files found.
README.md
View file @
a46c3b60
...
...
@@ -23,6 +23,8 @@ with open source third-party libraries. UniSet provides the consistent interface
add-on components and third-party libraries. Python wrapper helps in using the library
in python scripts.
libuniset requires minimum C++11
More information:
*
[
RU
]
https://habr.com/post/278535/
*
[
RU
]
https://habr.com/post/171711/
...
...
conf/libuniset2.spec
View file @
a46c3b60
...
...
@@ -26,7 +26,7 @@
Name: libuniset2
Version: 2.8
Release: alt
9.M90P.10
Release: alt
14.M90P.15
Summary: UniSet - library for building distributed industrial control systems
License: LGPL
...
...
@@ -546,9 +546,24 @@ rm -f %buildroot%_docdir/%oname/html/*.md5
# history of current unpublished changes
%changelog
* Sun
Dec 22 2019 Pavel Vainerman <pv@altlinux.ru> 2.8-alt9.M90P.10
* Sun
Oct 25 2020 Pavel Vainerman <pv@altlinux.ru> 2.8-alt14.M90P.15
- backport to ALTLinux p9 (by rpmbph script)
* Sun Oct 25 2020 Pavel Vainerman <pv@altlinux.ru> 2.8-alt15
- minor fixes
* Fri Feb 14 2020 Pavel Vainerman <pv@altlinux.ru> 2.8-alt14
- removed old define -D_GLIBCXX_USE_NANOSLEEP
* Fri Feb 14 2020 Pavel Vainerman <pv@altlinux.ru> 2.8-alt13
- (UNetUDP): added --unet-packsendpause and --unet-packsendpause-factor
* Tue Jan 28 2020 Pavel Vainerman <pv@altlinux.ru> 2.8-alt12
- minor fixes
* Sun Jan 26 2020 Pavel Vainerman <pv@altlinux.ru> 2.8-alt11
- remove std=c++11 cflags
* Thu Dec 19 2019 Pavel Vainerman <pv@altlinux.ru> 2.8-alt10
- (DBServer_PostrgeSQL): fix for check connection
...
...
configure.ac
View file @
a46c3b60
...
...
@@ -454,7 +454,7 @@ CXX_EXTRA_FLAGS="-Wnon-virtual-dtor -Woverloaded-virtual -Woverflow -D_GLIBCXX_U
# export
LDFLAGS="$LDFLAGS ${OMNI_LIBS} ${XML_LIBS} ${SIGC_LIBS} ${COV_LIBS} ${POCO_LIBS} ${EV_LIBS}"
# all developer liked options add to autogen.sh, please
CXXFLAGS="-I\$(top_builddir)/include $CXXFLAGS -funsigned-char -
std=c++11 -
g -DCATCH_VERSION_MAJOR=${CATCH_VERSION_MAJOR} -D_GNU_SOURCE ${REST_API_CFLAGS} ${COMPORT_485F_CFLAGS} ${OMNI_CFLAGS} ${XML_CFLAGS} ${SIGC_CFLAGS} ${COV_CFLAGS} ${POCO_CFLAGS} ${EV_CFLAGS} $CXX_EXTRA_FLAGS"
CXXFLAGS="-I\$(top_builddir)/include $CXXFLAGS -funsigned-char -g -DCATCH_VERSION_MAJOR=${CATCH_VERSION_MAJOR} -D_GNU_SOURCE ${REST_API_CFLAGS} ${COMPORT_485F_CFLAGS} ${OMNI_CFLAGS} ${XML_CFLAGS} ${SIGC_CFLAGS} ${COV_CFLAGS} ${POCO_CFLAGS} ${EV_CFLAGS} $CXX_EXTRA_FLAGS"
AC_SUBST(LDFLAGS)
AC_SUBST(CXXFLAGS)
...
...
extensions/DBServer-MySQL/MySQLInterface.h
View file @
a46c3b60
...
...
@@ -33,6 +33,7 @@
namespace
uniset
{
// ----------------------------------------------------------------------------
// no thread safety
class
MySQLInterface
:
public
DBNetInterface
{
...
...
extensions/DBServer-PostgreSQL/PostgreSQLInterface.cc
View file @
a46c3b60
...
...
@@ -16,7 +16,8 @@
// --------------------------------------------------------------------------
#include <sstream>
#include <cstdio>
#include <UniSetTypes.h>
#include "UniSetTypes.h"
#include "unisetstd.h"
#include "PostgreSQLInterface.h"
// --------------------------------------------------------------------------
using
namespace
std
;
...
...
@@ -52,7 +53,7 @@ bool PostgreSQLInterface::ping() const
try
{
// pqxx doesn't work with unique_ptr (
nontransaction
n
(
*
(
db
.
get
()));
n
.
exec
(
"select 1;"
);
return
true
;
...
...
@@ -89,7 +90,7 @@ bool PostgreSQLInterface::nconnect(const string& host, const string& user, const
try
{
db
=
make_shared
<
pqxx
::
connection
>
(
std
::
move
(
conninfo
.
str
())
);
db
=
unisetstd
::
make_unique
<
pqxx
::
connection
>
(
std
::
move
(
conninfo
.
str
())
);
return
db
->
is_open
();
}
catch
(
const
std
::
exception
&
e
)
...
...
@@ -122,10 +123,11 @@ bool PostgreSQLInterface::copy( const std::string& tblname, const std::vector<st
try
{
// pqxx doesn't work with unique_ptr
work
w
(
*
(
db
.
get
())
);
tablewriter
t
(
w
,
tblname
,
cols
.
begin
(),
cols
.
end
());
t
.
reserve
(
data
.
size
());
// size() не дорогая операция для list?
t
.
reserve
(
data
.
size
());
for
(
const
auto
&
d
:
data
)
t
.
push_back
(
d
.
begin
(),
d
.
end
());
...
...
@@ -153,6 +155,7 @@ bool PostgreSQLInterface::insert( const string& q )
try
{
// pqxx doesn't work with unique_ptr
work
w
(
*
(
db
.
get
())
);
lastQ
=
q
;
w
.
exec
(
q
);
...
...
@@ -180,6 +183,7 @@ bool PostgreSQLInterface::insertAndSaveRowid( const string& q )
try
{
// pqxx doesn't work with unique_ptr
work
w
(
*
(
db
.
get
())
);
lastQ
=
q
;
pqxx
::
result
res
=
w
.
exec
(
qplus
);
...
...
@@ -203,7 +207,7 @@ DBResult PostgreSQLInterface::query( const string& q )
try
{
// pqxx doesn't work with unique_ptr
nontransaction
n
(
*
(
db
.
get
()));
lastQ
=
q
;
/* Execute SQL query */
...
...
extensions/DBServer-PostgreSQL/PostgreSQLInterface.h
View file @
a46c3b60
...
...
@@ -29,6 +29,7 @@
namespace
uniset
{
// ----------------------------------------------------------------------------
// No thread safety!
class
PostgreSQLInterface
:
public
DBNetInterface
{
...
...
@@ -70,7 +71,7 @@ namespace uniset
private
:
DBResult
makeResult
(
const
pqxx
::
result
&
res
);
std
::
shared
_ptr
<
pqxx
::
connection
>
db
;
std
::
unique
_ptr
<
pqxx
::
connection
>
db
;
std
::
string
lastQ
;
std
::
string
lastE
;
double
last_inserted_id
;
...
...
extensions/DBServer-PostgreSQL/db_create_example.sql
0 → 100644
View file @
a46c3b60
CREATE
TABLE
main_history
(
id
PRIMARY
KEY
NOT
NULL
,
date
date
NOT
NULL
,
time
time
NOT
NULL
,
time_usec
int
NOT
NULL
CHECK
(
time_usec
>=
0
),
sensor_id
int
NOT
NULL
,
value
double
precision
NOT
NULL
,
node
int
NOT
NULL
,
confirm
int
DEFAULT
NULL
,
PRIMARY
KEY
(
id
)
);
extensions/DBServer-SQLite/SQLiteInterface.cc
View file @
a46c3b60
...
...
@@ -82,6 +82,12 @@ bool SQLiteInterface::connect( const string& dbfile, bool create, int extra_sqli
// if( !create && !uniset::file_exist(dbfile) )
// return false;
if
(
db
&&
ping
()
)
return
true
;
if
(
db
)
close
();
int
flags
=
create
?
0
:
SQLITE_OPEN_READWRITE
;
if
(
extra_sqlite_flags
)
...
...
extensions/ModbusSlave/tests/mbslave-tests.at
View file @
a46c3b60
...
...
@@ -2,6 +2,6 @@ AT_SETUP([ModbusSlave tests (with SM)])
AT_CHECK([$abs_top_builddir/testsuite/at-test-launch.sh $abs_top_builddir/extensions/ModbusSlave/tests tests_with_sm.sh],[0],[ignore],[ignore])
AT_CLEANUP
AT_SETUP([ModbusSlave tests (
Apart
)])
AT_SETUP([ModbusSlave tests (
separately
)])
AT_CHECK([$abs_top_builddir/testsuite/at-test-launch.sh $abs_top_builddir/extensions/ModbusSlave/tests tests_with_sm_apart.sh],[0],[ignore],[ignore])
AT_CLEANUP
extensions/SharedMemory/SharedMemory.h
View file @
a46c3b60
...
...
@@ -286,7 +286,7 @@ namespace uniset
\section sec_SM_ReservSM Восстановление данных из резервных SM
Для повышения надёжности работы в SharedMemory предусмотрен механизм восстановления текущего состояния (датчиков)
из списка резервных SM. После того, как SM запускается и активизируется, но до того, как она
выдаст exit()=true и с ней можно будет работать, происходит попытка получить значения всех датчиков
выдаст exi
s
t()=true и с ней можно будет работать, происходит попытка получить значения всех датчиков
от резервных SM. Список резервных SM задаётся в секции <ReservList>...</ReservList>.
При этом попытки получить значения идёт в порядке указанном в списке и прекращаются, при первом успешном
доступе.
...
...
extensions/UNetUDP/UNetExchange.cc
View file @
a46c3b60
...
...
@@ -79,6 +79,8 @@ UNetExchange::UNetExchange(uniset::ObjectId objId, uniset::ObjectId shmId, const
int
evrunTimeout
=
conf
->
getArgPInt
(
"--"
+
prefix
+
"-evrun-timeout"
,
it
.
getProp
(
"evrunTimeout"
),
60000
);
int
recvpause
=
conf
->
getArgPInt
(
"--"
+
prefix
+
"-recvpause"
,
it
.
getProp
(
"recvpause"
),
10
);
int
sendpause
=
conf
->
getArgPInt
(
"--"
+
prefix
+
"-sendpause"
,
it
.
getProp
(
"sendpause"
),
100
);
int
packsendpause
=
conf
->
getArgPInt
(
"--"
+
prefix
+
"-packsendpause"
,
it
.
getProp
(
"packsendpause"
),
5
);
int
packsendpauseFactor
=
conf
->
getArgPInt
(
"--"
+
prefix
+
"-packsendpause-factor"
,
it
.
getProp
(
"packsendpauseFactor"
),
0
);
int
updatepause
=
conf
->
getArgPInt
(
"--"
+
prefix
+
"-updatepause"
,
it
.
getProp
(
"updatepause"
),
100
);
int
lostTimeout
=
conf
->
getArgPInt
(
"--"
+
prefix
+
"-lost-timeout"
,
it
.
getProp
(
"lostTimeout"
),
2
*
updatepause
);
steptime
=
conf
->
getArgPInt
(
"--"
+
prefix
+
"-steptime"
,
it
.
getProp
(
"steptime"
),
1000
);
...
...
@@ -166,6 +168,8 @@ UNetExchange::UNetExchange(uniset::ObjectId objId, uniset::ObjectId shmId, const
unetinfo
<<
myname
<<
"(init): init sender.. my node "
<<
n_it
.
getProp
(
"name"
)
<<
endl
;
sender
=
make_shared
<
UNetSender
>
(
h
,
p
,
shm
,
false
,
s_field
,
s_fvalue
,
"unet"
,
prefix
);
sender
->
setSendPause
(
sendpause
);
sender
->
setPackSendPause
(
packsendpause
);
sender
->
setPackSendPauseFactor
(
packsendpauseFactor
);
sender
->
setCheckConnectionPause
(
checkConnectionPause
);
loga
->
add
(
sender
->
getLog
());
...
...
extensions/UNetUDP/UNetExchange.h
View file @
a46c3b60
...
...
@@ -123,6 +123,13 @@ namespace uniset
При этом внутри одной группы датчики разбиваются по пакетам согласно заданному максимальному размеру пакета
(см. конструктор класса UNetSender()).
\section pgUNetUDP_PackSendPause Пауза между посылкой пакетов
Параметр \b --prefix-packsendpause или \b packsendpause в настаройках позволяет задать паузу (в миллисекундах)
между посылками пакетов. Если итоговых пакетов с данными больше чем 1.
При этом параметр \b --prefix-packsendpause-factor или \b packsendpauseFactor позволяет указать,
что необходимо делать паузы не между каждым пакетом, а через каждый N пакет.
По умолчанию \b packsendpause=5 миллисекунд.
\section pgUNetUDP_Stat Статистика работы канала
Для возможности мониторинга работы имеются счётчики, которые можно привязать к датчикам,
задав их для соответствующего узла в секции '<nodes>' конфигурационного файла.
...
...
extensions/UNetUDP/UNetSender.cc
View file @
a46c3b60
...
...
@@ -43,6 +43,7 @@ namespace uniset
saddr
(
_host
,
_port
),
sendpause
(
150
),
packsendpause
(
5
),
packsendpauseFactor
(
1
),
activated
(
false
),
packetnum
(
1
),
lastcrc
(
0
),
...
...
@@ -259,9 +260,20 @@ namespace uniset
break
;
real_send
(
pk
[
i
]);
if
(
packsendpause
>
0
&&
size
>
1
)
{
if
(
packsendpauseFactor
<=
0
)
{
msleep
(
packsendpause
);
}
else
if
(
i
>
0
&&
(
i
%
packsendpauseFactor
)
==
0
)
{
msleep
(
packsendpause
);
}
}
}
}
ncycle
++
;
}
...
...
@@ -565,6 +577,8 @@ namespace uniset
<<
" lastpacknum="
<<
packetnum
<<
" lastcrc="
<<
setw
(
6
)
<<
lastcrc
<<
" items="
<<
items
.
size
()
<<
" maxAData="
<<
getADataSize
()
<<
" maxDData="
<<
getDDataSize
()
<<
" packsendpause[factor="
<<
packsendpauseFactor
<<
"]="
<<
packsendpause
<<
" sendpause="
<<
sendpause
<<
endl
<<
"
\t
packs([sendfactor]=num): "
<<
endl
;
...
...
extensions/UNetUDP/UNetSender.h
View file @
a46c3b60
...
...
@@ -142,6 +142,10 @@ namespace uniset
{
packsendpause
=
msec
;
}
inline
void
setPackSendPauseFactor
(
int
factor
)
{
packsendpauseFactor
=
factor
;
}
void
setCheckConnectionPause
(
int
msec
);
...
...
@@ -204,6 +208,7 @@ namespace uniset
std
::
string
myname
=
{
""
};
timeout_t
sendpause
=
{
150
};
timeout_t
packsendpause
=
{
5
};
int
packsendpauseFactor
=
{
1
};
timeout_t
writeTimeout
=
{
1000
};
// msec
std
::
atomic_bool
activated
=
{
false
};
PassiveTimer
ptCheckConnection
;
...
...
extensions/UNetUDP/tests/unetudp-tests.at
View file @
a46c3b60
...
...
@@ -6,6 +6,6 @@ AT_SETUP([UNetUDP tests (with SM)(evloop)])
AT_CHECK([$abs_top_builddir/testsuite/at-test-launch.sh $abs_top_builddir/extensions/UNetUDP/tests tests_with_sm_evloop.sh],[0],[ignore],[ignore])
AT_CLEANUP
# AT_SETUP([UNetUDP tests (
Apart
)])
# AT_SETUP([UNetUDP tests (
separately
)])
# AT_CHECK([$abs_top_builddir/testsuite/at-test-launch.sh $abs_top_builddir/extensions/UNetUDP/tests tests_with_sm_apart.sh],[0],[ignore],[ignore])
# AT_CLEANUP
include/HourGlass.h
View file @
a46c3b60
...
...
@@ -24,6 +24,8 @@
// --------------------------------------------------------------------------
namespace
uniset
{
// header only
/*! Песочные часы. Класс реализующий логику песочных часов.
Удобен для создания задержек на срабатывание и на отпускание
(как фильтр от кратковременных изменений) с "накоплением времени".
...
...
include/Pulse.h
View file @
a46c3b60
...
...
@@ -23,6 +23,8 @@
// --------------------------------------------------------------------------
namespace
uniset
{
// header only
/*! Класс, реализующий формирование импульсов заданной длительности(t1) и заданных пауз между ними(t0).
Класс пассивный, для работы требует постоянного вызова функции step().
Для получения текущего состояния "выхода" использовать out().
...
...
include/Trigger.h
View file @
a46c3b60
...
...
@@ -24,6 +24,7 @@
//--------------------------------------------------------------------------
namespace
uniset
{
// header only
/*! Триггер, позволяющий красиво засекать изменения во флаге */
class
Trigger
...
...
include/UHelpers.h
View file @
a46c3b60
...
...
@@ -93,7 +93,7 @@ namespace uniset
auto
o
=
uniset
::
make_object
<
T
>
(
idname
,
secname
,
std
::
forward
<
_Args
>
(
__args
)...);
m
->
add
(
o
);
m
->
logAgregator
()
->
add
(
o
->
logAgregator
());
return
std
::
move
(
o
)
;
return
o
;
}
catch
(
const
uniset
::
Exception
&
ex
)
{
...
...
@@ -111,7 +111,7 @@ namespace uniset
auto
o
=
uniset
::
make_object_x
<
T
>
(
root
,
secname
,
std
::
forward
<
_Args
>
(
__args
)...);
m
->
add
(
o
);
m
->
logAgregator
()
->
add
(
o
->
logAgregator
());
return
std
::
move
(
o
)
;
return
o
;
}
catch
(
const
uniset
::
Exception
&
ex
)
{
...
...
libUniSet2.pc.in
View file @
a46c3b60
...
...
@@ -8,4 +8,4 @@ Description: Support library for UniSet
Requires: libxml-2.0 sigc++-2.0 omniORB4 libev
Version: @VERSION@
Libs: -L${libdir} -lUniSet2 -lPocoFoundation -lPocoNet @REST_API_CLIBS@
Cflags: -I${includedir}/@PACKAGE@ -D__OMNIORB4
-std=c++11 -D_GLIBCXX_USE_NANOSLEEP
@REST_API_CFLAGS@
Cflags: -I${includedir}/@PACKAGE@ -D__OMNIORB4 @REST_API_CFLAGS@
src/Log/LogSession.cc
View file @
a46c3b60
...
...
@@ -412,7 +412,7 @@ namespace uniset
err
<<
"BAD MAGICNUM"
;
if
(
ret
!=
sizeof
(
msg
)
)
err
<<
"BAD s
o
ze of message ("
<<
ret
<<
")"
;
err
<<
"BAD s
i
ze of message ("
<<
ret
<<
")"
;
mylog
.
warn
()
<<
err
.
str
()
<<
endl
;
}
...
...
tests/test_utypes.cc
View file @
a46c3b60
...
...
@@ -10,6 +10,8 @@
using
namespace
std
;
using
namespace
uniset
;
// -----------------------------------------------------------------------------
#pragma GCC diagnostic ignored -Wwrite-strings
// -----------------------------------------------------------------------------
TEST_CASE
(
"UniSetTypes: uni_atoi"
,
"[utypes][uni_atoi]"
)
{
SECTION
(
"int"
)
...
...
wrappers/core/UConnector.h
View file @
a46c3b60
...
...
@@ -25,6 +25,10 @@
#include "UExceptions.h"
#include "UniSetActivator.h"
// --------------------------------------------------------------------------
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wdeprecated"
// --------------------------------------------------------------------------
class
UConnector
{
public
:
...
...
wrappers/core/UModbus.h
View file @
a46c3b60
...
...
@@ -27,6 +27,10 @@
#include "UTypes.h"
#include "UExceptions.h"
// --------------------------------------------------------------------------
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wdeprecated"
// --------------------------------------------------------------------------
class
UModbus
{
public
:
...
...
wrappers/core/UProxyObject.h
View file @
a46c3b60
...
...
@@ -22,6 +22,9 @@
#include "UExceptions.h"
#include "UTypes.h"
// --------------------------------------------------------------------------
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wdeprecated"
// --------------------------------------------------------------------------
class
UProxyObject_impl
;
// PIMPL
// --------------------------------------------------------------------------
/*! Интерфейс для взаимодействия с SM (с заказом датчиков).
...
...
wrappers/python/lib/pyUniSet/PyUInterface.h
View file @
a46c3b60
...
...
@@ -21,6 +21,9 @@
#include "UTypes.h"
#include "UExceptions.h"
// --------------------------------------------------------------------------
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wdeprecated"
// --------------------------------------------------------------------------
namespace
pyUInterface
{
void
uniset_init_params
(
UTypes
::
Params
*
p
,
const
std
::
string
&
xmlfile
)
throw
(
UException
);
...
...
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