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
833a4b73
Commit
833a4b73
authored
Sep 09, 2009
by
Pavel Vainerman
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
initial init for new component - UniNetwork
parent
5a822b8e
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
524 additions
and
6 deletions
+524
-6
libuniset.spec
conf/libuniset.spec
+4
-1
configure.ac
configure.ac
+2
-0
Makefile.am
extensions/Makefile.am
+1
-1
start_tcp_fg.sh
extensions/ModbusSlave/start_tcp_fg.sh
+4
-3
libUniSetSharedMemory.pc.in
extensions/SharedMemory/libUniSetSharedMemory.pc.in
+1
-1
Makefile.am
extensions/UniNetwork/Makefile.am
+26
-0
UniExchange.cc
extensions/UniNetwork/UniExchange.cc
+339
-0
UniExchange.h
extensions/UniNetwork/UniExchange.h
+66
-0
start_fg.sh
extensions/UniNetwork/start_fg.sh
+10
-0
uninet.cc
extensions/UniNetwork/uninet.cc
+71
-0
No files found.
conf/libuniset.spec
View file @
833a4b73
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
Name: libuniset
Name: libuniset
Version: 0.96
Version: 0.96
Release: eter5
6
Release: eter5
7
Summary: UniSet - library for building distributed industrial control systems
Summary: UniSet - library for building distributed industrial control systems
License: GPL
License: GPL
Group: Development/C++
Group: Development/C++
...
@@ -175,6 +175,9 @@ rm -f %buildroot%_libdir/*.la
...
@@ -175,6 +175,9 @@ rm -f %buildroot%_libdir/*.la
%exclude %_pkgconfigdir/libUniSet.pc
%exclude %_pkgconfigdir/libUniSet.pc
%changelog
%changelog
* Wed Sep 09 2009 Pavel Vainerman <pv@altlinux.ru> 0.96-eter56
- new build
* Mon Sep 07 2009 Pavel Vainerman <pv@altlinux.ru> 0.96-eter55
* Mon Sep 07 2009 Pavel Vainerman <pv@altlinux.ru> 0.96-eter55
- new build
- new build
...
...
configure.ac
View file @
833a4b73
...
@@ -197,6 +197,8 @@ AC_CONFIG_FILES([Makefile
...
@@ -197,6 +197,8 @@ AC_CONFIG_FILES([Makefile
extensions/LogicProcessor/Makefile
extensions/LogicProcessor/Makefile
extensions/LogicProcessor/libUniSetLogicProcessor.pc
extensions/LogicProcessor/libUniSetLogicProcessor.pc
extensions/SMViewer/Makefile
extensions/SMViewer/Makefile
extensions/UniNetwork/Makefile
extensions/UniNetwork/libUniSetNetwork.pc
extensions/SharedMemory/Makefile
extensions/SharedMemory/Makefile
extensions/SharedMemory/libUniSetSharedMemory.pc
extensions/SharedMemory/libUniSetSharedMemory.pc
extensions/SharedMemoryPlus/Makefile])
extensions/SharedMemoryPlus/Makefile])
...
...
extensions/Makefile.am
View file @
833a4b73
...
@@ -4,7 +4,7 @@
...
@@ -4,7 +4,7 @@
if
HAVE_EXTENTIONS
if
HAVE_EXTENTIONS
SUBDIRS
=
lib include SharedMemory IOControl RTUExchange LogicProcessor
\
SUBDIRS
=
lib include SharedMemory IOControl RTUExchange LogicProcessor
\
ModbusSlave MBTCPMaster SMViewer
ModbusSlave MBTCPMaster SMViewer
UniNetwork
#SharedMemoryPlus
#SharedMemoryPlus
#UDPExchange
#UDPExchange
...
...
extensions/ModbusSlave/start_tcp_fg.sh
View file @
833a4b73
#!/bin/sh
#!/bin/sh
uniset-start.sh
-f
./uniset-mbslave
--
mbs-name
MBSlave1
--
confile
test.xml
--dlog-add-levels
info,crit,warn
\
uniset-start.sh
-f
./uniset-mbslave
--confile
test.xml
--dlog-add-levels
info,crit,warn
\
--mbs-name
MBSlave1
\
--mbs-name
MBSlave1
\
--mbs-type
TCP
--mbs-inet-addr
127.0.0.1
--mbs-inet-port
2048
--mbs-reg-from-id
1
--mbs-my-addr
0x01
\
--mbs-type
TCP
--mbs-inet-addr
127.0.0.1
--mbs-inet-port
2048
--mbs-reg-from-id
1
--mbs-my-addr
0x01
\
--mbs-askcount-id
SVU_AskCount_AS
--mbs-askcount-id
SVU_AskCount_AS
# --mbs-force 1
# --mbs-force 1
#--mbs-reg-from-id 1 \
#--mbs-reg-from-id 1 \
\ No newline at end of file
#--mbs-filter-field CAN2sender --mbs-filter-value SYSTSNode \
\ No newline at end of file
extensions/SharedMemory/libUniSetSharedMemory.pc.in
View file @
833a4b73
...
@@ -4,7 +4,7 @@ libdir=@libdir@
...
@@ -4,7 +4,7 @@ libdir=@libdir@
includedir=@includedir@
includedir=@includedir@
Name: libUniSetSharedMemory
Name: libUniSetSharedMemory
Description: Support library for UniSet
IOControl
Description: Support library for UniSet
SharedMemory
Requires: libUniSetExtensions
Requires: libUniSetExtensions
Version: @VERSION@
Version: @VERSION@
Libs: -L${libdir} -lUniSetSharedMemory
Libs: -L${libdir} -lUniSetSharedMemory
...
...
extensions/UniNetwork/Makefile.am
0 → 100644
View file @
833a4b73
bin_PROGRAMS
=
@PACKAGE@-network
lib_LTLIBRARIES
=
libUniSetNetwork.la
libUniSetNetwork_la_LIBADD
=
$(SIGC_LIBS)
$(top_builddir)
/lib/libUniSet.la
\
$(top_builddir)
/extensions/lib/libUniSetExtensions.la
\
$(top_builddir)
/extensions/SharedMemory/libUniSetSharedMemory.la
libUniSetNetwork_la_CPPFLAGS
=
$(SIGC_CFLAGS)
\
-I
$(top_builddir)
/extensions/include
\
-I
$(top_builddir)
/extensions/SharedMemory
libUniSetNetwork_la_SOURCES
=
UniExchange.cc
@PACKAGE@
_network_LDADD
=
libUniSetNetwork.la
@PACKAGE@
_network_CPPFLAGS
=
$(SIGC_CFLAGS)
-I
$(top_builddir)
/extensions/include
@PACKAGE@
_network_SOURCES
=
uninet.cc
include
$(top_builddir)/conf/setting.mk
# install
devel_include_HEADERS
=
*
.h
devel_includedir
=
$(pkgincludedir)
/extensions
pkgconfigdir
=
$(libdir)
/pkgconfig
pkgconfig_DATA
=
libUniSetNetwork.pc
all-local
:
ln
-sf
../UniNetwork/
$(devel_include_HEADERS)
../include
extensions/UniNetwork/UniExchange.cc
0 → 100644
View file @
833a4b73
// $Id: NetExchange.cc,v 1.2 2009/04/07 16:11:23 pv Exp $
// --------------------------------------------------------------------------
#include "Configuration.h"
#include "Extensions.h"
#include "Exceptions.h"
#include "UniExchange.h"
// -----------------------------------------------------------------------------
using
namespace
std
;
using
namespace
UniSetTypes
;
using
namespace
UniSetExtensions
;
// --------------------------------------------------------------------------
UniExchange
::
UniExchange
(
UniSetTypes
::
ObjectId
id
,
UniSetTypes
::
ObjectId
shmID
,
SharedMemory
*
ic
,
const
std
::
string
prefix
)
:
IOController
(
id
),
shm
(
0
)
{
cnode
=
conf
->
getNode
(
myname
);
if
(
cnode
==
NULL
)
throw
UniSetTypes
::
SystemError
(
"(UniExchange): Not find conf-node for "
+
myname
);
shm
=
new
SMInterface
(
shmID
,
&
ui
,
id
,
ic
);
UniXML_iterator
it
(
cnode
);
s_field
=
conf
->
getArgParam
(
"--"
+
prefix
+
"-filter-field"
);
s_fvalue
=
conf
->
getArgParam
(
"--"
+
prefix
+
"-filter-value"
);
dlog
[
Debug
::
INFO
]
<<
myname
<<
"(init): read fileter-field='"
<<
s_field
<<
"' filter-value='"
<<
s_fvalue
<<
"'"
<<
endl
;
if
(
it
.
goChildren
()
)
{
UniSetTypes
::
ObjectId
l_id
=
getSharedMemoryID
();
for
(
;
it
.
getCurrent
();
it
.
goNext
()
)
{
UniSetTypes
::
ObjectId
id
;
string
n
(
it
.
getProp
(
"id"
));
if
(
!
n
.
empty
()
)
id
=
it
.
getIntProp
(
"id"
);
else
id
=
conf
->
getControllerID
(
n
);
if
(
id
==
DefaultObjectId
)
throw
SystemError
(
"(UniExchange): Uknown ID for "
+
n
);
UniSetTypes
::
ObjectId
node
;
string
n1
(
it
.
getProp
(
"node_id"
));
if
(
!
n1
.
empty
()
)
node
=
it
.
getIntProp
(
"node_id"
);
else
node
=
conf
->
getNodeID
(
n1
);
if
(
id
==
DefaultObjectId
)
throw
SystemError
(
"(UniExchange): Uknown ID for node="
+
n1
);
NetNodeInfo
ni
;
ni
.
oref
=
CORBA
::
Object
::
_nil
();
ni
.
id
=
id
;
ni
.
node
=
node
;
ni
.
sidConnection
=
conf
->
getSensorID
(
it
.
getProp
(
"sid_connection"
));
dlog
[
Debug
::
INFO
]
<<
myname
<<
": add point "
<<
n
<<
":"
<<
n1
<<
endl
;
nlst
.
push_back
(
ni
);
}
}
if
(
shm
->
isLocalwork
()
)
readConfiguration
();
else
ic
->
addReadItem
(
sigc
::
mem_fun
(
this
,
&
UniExchange
::
readItem
)
);
}
// -----------------------------------------------------------------------------
UniExchange
::~
UniExchange
()
{
delete
shm
;
}
// -----------------------------------------------------------------------------
void
UniExchange
::
execute
()
{
while
(
1
)
{
for
(
NetNodeList
::
iterator
it
=
nlst
.
begin
();
it
!=
nlst
.
end
();
++
it
)
{
try
{
bool
ok
=
false
;;
/*
dlog[Debug::INFO] << myname << ": resolve id=" << it->id
<< " name=" << conf->oind->getNameById(it->id)
<< " node=" << it->node
<< endl;
*/
for
(
unsigned
int
i
=
0
;
i
<
conf
->
getRepeatCount
();
i
++
)
{
try
{
if
(
CORBA
::
is_nil
(
it
->
oref
)
)
{
it
->
oref
=
ui
.
resolve
(
it
->
id
,
it
->
node
);
}
if
(
CORBA
::
is_nil
(
it
->
oref
)
)
continue
;
it
->
shm
=
IONotifyController_i
::
_narrow
(
it
->
oref
);
if
(
CORBA
::
is_nil
(
it
->
shm
)
)
{
it
->
oref
=
CORBA
::
Object
::
_nil
();
msleep
(
conf
->
getRepeatTimeout
());
continue
;
}
if
(
it
->
shm
->
exist
()
)
{
dlog
[
Debug
::
INFO
]
<<
" node="
<<
it
->
node
<<
": resolve OK ***"
<<
endl
;
ok
=
true
;
break
;
}
}
catch
(
CORBA
::
TRANSIENT
){}
catch
(
CORBA
::
OBJECT_NOT_EXIST
){}
catch
(
CORBA
::
SystemException
&
ex
){}
catch
(...){}
it
->
oref
=
CORBA
::
Object
::
_nil
();
msleep
(
conf
->
getRepeatTimeout
());
}
if
(
it
->
sidConnection
!=
DefaultObjectId
)
{
try
{
// shm->saveLocalState( it->sidConnection, ok );
ui
.
saveState
(
it
->
sidConnection
,
ok
,
UniversalIO
::
DigitalInput
,
conf
->
getLocalNode
());
}
catch
(...){
dlog
[
Debug
::
CRIT
]
<<
myname
<<
"(execute): sensor not avalible "
<<
conf
->
oind
->
getNameById
(
it
->
sidConnection
)
<<
endl
;
}
}
if
(
!
ok
)
{
dlog
[
Debug
::
INFO
]
<<
"****** cannot connect with node="
<<
it
->
node
<<
endl
;
continue
;
}
}
catch
(
Exception
&
ex
)
{
cout
<<
myname
<<
"(execute): "
<<
ex
<<
endl
;
}
catch
(
...
)
{
cout
<<
myname
<<
"(execute): catch ..."
<<
endl
;
}
}
msleep
(
200
);
}
}
// -----------------------------------------------------------------------------
void
UniExchange
::
askSensors
(
UniversalIO
::
UIOCommand
cmd
)
{
}
// -----------------------------------------------------------------------------
void
UniExchange
::
processingMessage
(
UniSetTypes
::
VoidMessage
*
msg
)
{
try
{
switch
(
msg
->
type
)
{
case
Message
:
:
SensorInfo
:
{
SensorMessage
sm
(
msg
);
sensorInfo
(
&
sm
);
break
;
}
case
Message
:
:
Timer
:
{
TimerMessage
tm
(
msg
);
timerInfo
(
&
tm
);
break
;
}
case
Message
:
:
SysCommand
:
{
SystemMessage
sm
(
msg
);
sysCommand
(
&
sm
);
break
;
}
default
:
break
;
}
}
catch
(
Exception
&
ex
)
{
cout
<<
myname
<<
"(processingMessage): "
<<
ex
<<
endl
;
}
}
// -----------------------------------------------------------------------------
void
UniExchange
::
sysCommand
(
SystemMessage
*
sm
)
{
switch
(
sm
->
command
)
{
case
SystemMessage
:
:
StartUp
:
{
askSensors
(
UniversalIO
::
UIONotify
);
break
;
}
case
SystemMessage
:
:
FoldUp
:
case
SystemMessage
:
:
Finish
:
askSensors
(
UniversalIO
::
UIODontNotify
);
break
;
case
SystemMessage
:
:
WatchDog
:
askSensors
(
UniversalIO
::
UIONotify
);
break
;
default
:
break
;
}
}
// -----------------------------------------------------------------------------
void
UniExchange
::
sigterm
(
int
signo
)
{
}
// -----------------------------------------------------------------------------
void
UniExchange
::
sensorInfo
(
UniSetTypes
::
SensorMessage
*
sm
)
{
}
// -----------------------------------------------------------------------------
void
UniExchange
::
timerInfo
(
UniSetTypes
::
TimerMessage
*
tm
)
{
}
// -----------------------------------------------------------------------------
UniExchange
*
UniExchange
::
init_exchange
(
int
argc
,
const
char
*
const
*
argv
,
UniSetTypes
::
ObjectId
icID
,
SharedMemory
*
ic
,
const
std
::
string
prefix
)
{
string
nm
(
conf
->
getArgParam
(
"--uniexchange-id"
,
"UniExchange"
));
UniSetTypes
::
ObjectId
ID
=
conf
->
getControllerID
(
nm
);
if
(
ID
==
UniSetTypes
::
DefaultObjectId
)
{
cerr
<<
"(uniexchange): Not found ID for "
<<
nm
<<
" in section "
<<
conf
->
getControllersSection
()
<<
endl
;
return
0
;
}
return
new
UniExchange
(
ID
,
icID
,
ic
,
prefix
);
}
// -----------------------------------------------------------------------------
IOController_i
::
ASensorInfoSeq
*
UniExchange
::
getAnalogSensorsMap
()
{
}
// -----------------------------------------------------------------------------
IOController_i
::
DSensorInfoSeq
*
UniExchange
::
getDigitalSensorsMap
()
{
}
// -----------------------------------------------------------------------------
void
UniExchange
::
readConfiguration
()
{
#warning !!!
// , ...
// readconf_ok = false;
xmlNode
*
root
=
conf
->
getXMLSensorsSection
();
if
(
!
root
)
{
ostringstream
err
;
err
<<
myname
<<
"(readConfiguration): <sensors>"
;
throw
SystemError
(
err
.
str
());
}
UniXML_iterator
it
(
root
);
if
(
!
it
.
goChildren
()
)
{
std
::
cerr
<<
myname
<<
"(readConfiguration): <sensors> ?!!
\n
"
;
return
;
}
for
(
;
it
.
getCurrent
();
it
.
goNext
()
)
{
if
(
check_item
(
it
)
)
initItem
(
it
);
}
// readconf_ok = true;
}
// ------------------------------------------------------------------------------------------
bool
UniExchange
::
check_item
(
UniXML_iterator
&
it
)
{
if
(
s_field
.
empty
()
)
return
true
;
// field
if
(
s_fvalue
.
empty
()
&&
it
.
getProp
(
s_field
).
empty
()
)
return
false
;
// field = value
if
(
!
s_fvalue
.
empty
()
&&
it
.
getProp
(
s_field
)
!=
s_fvalue
)
return
false
;
return
true
;
}
// ------------------------------------------------------------------------------------------
bool
UniExchange
::
readItem
(
UniXML
&
xml
,
UniXML_iterator
&
it
,
xmlNode
*
sec
)
{
if
(
check_item
(
it
)
)
initItem
(
it
);
return
true
;
}
// ------------------------------------------------------------------------------------------
bool
UniExchange
::
initItem
(
UniXML_iterator
&
it
)
{
return
true
;
}
// ------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void
UniExchange
::
help_print
(
int
argc
,
const
char
**
argv
)
{
cout
<<
"--unet-polltime msec - . 200 ."
<<
endl
;
// cout << "--unet-heartbeat-id - heartbeat-." << endl;
// cout << "--unet-heartbeat-max - heartbeat-ޣ . 10." << endl;
cout
<<
"--unet-sm-ready-timeout - SM"
<<
endl
;
}
// -----------------------------------------------------------------------------
extensions/UniNetwork/UniExchange.h
0 → 100644
View file @
833a4b73
// $Id: UniExchange.h,v 1.2 2009/04/07 16:11:23 pv Exp $
// -----------------------------------------------------------------------------
#ifndef UniExchange_H_
#define UniExchange_H_
// -----------------------------------------------------------------------------
#include <list>
#include "UniXML.h"
#include "IOController.h"
#include "SMInterface.h"
#include "SharedMemory.h"
// -----------------------------------------------------------------------------
class
UniExchange
:
public
IOController
{
public
:
UniExchange
(
UniSetTypes
::
ObjectId
id
,
UniSetTypes
::
ObjectId
shmID
,
SharedMemory
*
ic
=
0
,
const
std
::
string
prefix
=
"unet"
);
virtual
~
UniExchange
();
void
execute
();
virtual
IOController_i
::
ASensorInfoSeq
*
getAnalogSensorsMap
();
virtual
IOController_i
::
DSensorInfoSeq
*
getDigitalSensorsMap
();
static
UniExchange
*
init_exchange
(
int
argc
,
const
char
*
const
*
argv
,
UniSetTypes
::
ObjectId
shmID
,
SharedMemory
*
ic
=
0
,
const
std
::
string
prefix
=
"unet"
);
/*! help- */
static
void
help_print
(
int
argc
,
const
char
**
argv
);
protected
:
virtual
void
processingMessage
(
UniSetTypes
::
VoidMessage
*
msg
);
virtual
void
sysCommand
(
UniSetTypes
::
SystemMessage
*
sm
);
virtual
void
askSensors
(
UniversalIO
::
UIOCommand
cmd
);
virtual
void
sensorInfo
(
UniSetTypes
::
SensorMessage
*
sm
);
virtual
void
timerInfo
(
UniSetTypes
::
TimerMessage
*
tm
);
virtual
void
sigterm
(
int
signo
);
xmlNode
*
cnode
;
std
::
string
s_field
;
std
::
string
s_fvalue
;
SMInterface
*
shm
;
struct
NetNodeInfo
{
CORBA
::
Object_var
oref
;
IONotifyController_i_var
shm
;
UniSetTypes
::
ObjectId
id
;
UniSetTypes
::
ObjectId
node
;
UniSetTypes
::
ObjectId
sidConnection
;
/*!< */
};
typedef
std
::
list
<
NetNodeInfo
>
NetNodeList
;
NetNodeList
nlst
;
void
readConfiguration
();
bool
check_item
(
UniXML_iterator
&
it
);
bool
readItem
(
UniXML
&
xml
,
UniXML_iterator
&
it
,
xmlNode
*
sec
);
bool
initItem
(
UniXML_iterator
&
it
);
private
:
};
// -----------------------------------------------------------------------------
#endif // UniExchange_H_
extensions/UniNetwork/start_fg.sh
0 → 100755
View file @
833a4b73
#!/bin/sh
export
LD_LIBRARY_PATH
=
"../../lib/.libs;../lib/.libs"
ulimit
-Sc
10000000000
./uniset-start.sh
-f
./uniset-network
--smemory-id
SharedMemory
\
--unideb-add-levels
info,crit,warn,level9,system
\
--dlog-add-levels
info,crit,warn
extensions/UniNetwork/uninet.cc
0 → 100644
View file @
833a4b73
#include <string>
#include "Debug.h"
#include "ObjectsActivator.h"
#include "UniExchange.h"
#include "Extensions.h"
// --------------------------------------------------------------------------
using
namespace
std
;
using
namespace
UniSetTypes
;
using
namespace
UniSetExtensions
;
// --------------------------------------------------------------------------
int
main
(
int
argc
,
const
char
**
argv
)
{
if
(
argc
>
1
&&
strcmp
(
argv
[
1
],
"--help"
)
==
0
)
{
cout
<<
"--confile - . . configure.xml"
<<
endl
;
UniExchange
::
help_print
(
argc
,
argv
);
return
0
;
}
try
{
string
confile
=
UniSetTypes
::
getArgParam
(
"--confile"
,
argc
,
argv
,
"configure.xml"
);
conf
=
new
Configuration
(
argc
,
argv
,
confile
);
conf
->
initDebug
(
dlog
,
"dlog"
);
string
logfilename
=
conf
->
getArgParam
(
"--logfile"
,
"smemory.log"
);
string
logname
(
conf
->
getLogDir
()
+
logfilename
);
unideb
.
logFile
(
logname
.
c_str
()
);
dlog
.
logFile
(
logname
.
c_str
()
);
ObjectId
shmID
=
DefaultObjectId
;
string
sID
=
conf
->
getArgParam
(
"--smemory-id"
);
if
(
!
sID
.
empty
()
)
shmID
=
conf
->
getControllerID
(
sID
);
else
shmID
=
getSharedMemoryID
();
if
(
shmID
==
DefaultObjectId
)
{
cerr
<<
sID
<<
"? SharedMemoryID not found in "
<<
conf
->
getControllersSection
()
<<
" section"
<<
endl
;
return
1
;
}
UniExchange
*
shm
=
UniExchange
::
init_exchange
(
argc
,
argv
,
shmID
);
if
(
!
shm
)
return
1
;
ObjectsActivator
act
;
act
.
addObject
(
static_cast
<
class
UniSetObject
*>
(
shm
));
SystemMessage
sm
(
SystemMessage
::
StartUp
);
act
.
broadcast
(
sm
.
transport_msg
()
);
act
.
run
(
false
);
return
0
;
}
catch
(
SystemError
&
err
)
{
unideb
[
Debug
::
CRIT
]
<<
"(uninetwork): "
<<
err
<<
endl
;
}
catch
(
Exception
&
ex
)
{
unideb
[
Debug
::
CRIT
]
<<
"(uninetwork): "
<<
ex
<<
endl
;
}
catch
(...)
{
unideb
[
Debug
::
CRIT
]
<<
"(uninetwork): catch(...)"
<<
endl
;
}
return
1
;
}
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