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
b8688490
Commit
b8688490
authored
Oct 14, 2015
by
Pavel Vainerman
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
(VMonit): сделана сортировка по алфафиту, переписана реализация функций
вывода на экран.
parent
6b5e42ab
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
97 additions
and
110 deletions
+97
-110
ctl-cpp-common.xsl
Utilities/codegen/ctl-cpp-common.xsl
+33
-5
libuniset2.spec
conf/libuniset2.spec
+4
-1
UObject_SK.h
extensions/include/UObject_SK.h
+0
-0
UObject_SK.cc
extensions/lib/UObject_SK.cc
+0
-0
VMonitor.h
include/VMonitor.h
+10
-2
VMonitor.cc
src/Various/VMonitor.cc
+50
-102
No files found.
Utilities/codegen/ctl-cpp-common.xsl
View file @
b8688490
...
@@ -1036,18 +1036,44 @@ std::string <xsl:value-of select="$CLASSNAME"/>_SK::dumpIO()
...
@@ -1036,18 +1036,44 @@ std::string <xsl:value-of select="$CLASSNAME"/>_SK::dumpIO()
ostringstream s;
ostringstream s;
s
<<
myname
<<
": "
<<
endl;
s
<<
myname
<<
": "
<<
endl;
std::
vector
<
std::string
>
v
;
std::
list
<
std::string
>
v_in
;
ostringstream s1;
ostringstream s1;
<xsl:for-each
select=
"//smap/item"
>
<xsl:for-each
select=
"//smap/item"
>
<xsl:sort
select=
"@name"
order=
"ascending"
data-type=
"text"
/>
<xsl:if
test=
"normalize-space(@vartype)='in'"
>
s1.str("");
s1.str("");
s1
<<
" "
<<
setw(30)
<<
std::right
<<
"
<xsl:call-template
name=
"setprefix"
/><xsl:value-of
select=
"@name"
/>
"
s1
<<
" "
<<
setw(30)
<<
std::right
<<
"
<xsl:call-template
name=
"setprefix"
/><xsl:value-of
select=
"@name"
/>
"
<<
"
("
<<
setw(30)
<<
std::left
<<
ORepHelpers::getShortName( uniset_conf()->oind->getMapName(
<xsl:value-of
select=
"@name"
/>
))
<<
"
)"
<<
"
( "
<<
setw(30)
<<
std::left
<<
ORepHelpers::getShortName( uniset_conf()->oind->getMapName(
<xsl:value-of
select=
"@name"
/>
))
<<
"
)"
<<
std::right
<<
" = "
<<
setw(6)
<<
<xsl:call-template
name=
"setprefix"
/><xsl:value-of
select=
"@name"
/>
;
<<
std::right
<<
" = "
<<
setw(6)
<<
<xsl:call-template
name=
"setprefix"
/><xsl:value-of
select=
"@name"
/>
;
v.push_back(s1.str());
v_in.push_back(s1.str());
</xsl:if>
</xsl:for-each>
</xsl:for-each>
std::list
<
std::string
>
v_out;
<xsl:for-each
select=
"//smap/item"
>
<xsl:sort
select=
"@name"
order=
"ascending"
data-type=
"text"
/>
<xsl:if
test=
"normalize-space(@vartype)='out'"
>
s1.str("");
s1
<<
" "
<<
setw(30)
<<
std::right
<<
"
<xsl:call-template
name=
"setprefix"
/><xsl:value-of
select=
"@name"
/>
"
<<
" ( "
<<
setw(30)
<<
std::left
<<
ORepHelpers::getShortName( uniset_conf()->oind->getMapName(
<xsl:value-of
select=
"@name"
/>
))
<<
" )"
<<
std::right
<<
" = "
<<
setw(6)
<<
<xsl:call-template
name=
"setprefix"
/><xsl:value-of
select=
"@name"
/>
;
v_out.push_back(s1.str());
</xsl:if>
</xsl:for-each>
s
<<
endl;
int n = 0;
int n = 0;
for( const auto
&
e: v )
for( const auto
&
e: v_in )
{
s
<<
e;
if( (n++)%2 )
s
<<
std::endl;
}
s
<<
endl;
n = 0;
for( const auto
&
e: v_out )
{
{
s
<<
e;
s
<<
e;
if( (n++)%2 )
if( (n++)%2 )
...
@@ -1354,6 +1380,7 @@ std::string <xsl:value-of select="$CLASSNAME"/>_SK::dumpIO()
...
@@ -1354,6 +1380,7 @@ std::string <xsl:value-of select="$CLASSNAME"/>_SK::dumpIO()
vector
<
std::string
>
v;
vector
<
std::string
>
v;
<xsl:for-each
select=
"//sensors/item/consumers/consumer"
>
<xsl:for-each
select=
"//sensors/item/consumers/consumer"
>
<xsl:sort
select=
"../../@name"
order=
"ascending"
data-type=
"text"
/>
<xsl:if
test=
"normalize-space(../../@msg)!='1'"
>
<xsl:if
test=
"normalize-space(../../@msg)!='1'"
>
<xsl:if
test=
"normalize-space(@name)=$OID"
>
<xsl:if
test=
"normalize-space(@name)=$OID"
>
s1.str("");
s1.str("");
...
@@ -1363,6 +1390,7 @@ std::string <xsl:value-of select="$CLASSNAME"/>_SK::dumpIO()
...
@@ -1363,6 +1390,7 @@ std::string <xsl:value-of select="$CLASSNAME"/>_SK::dumpIO()
</xsl:if>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
std::sort(std::begin(v),std::end(v));
int n=0;
int n=0;
for( const auto
&
e: v )
for( const auto
&
e: v )
{
{
...
@@ -1401,7 +1429,7 @@ std::string <xsl:value-of select="$CLASSNAME"/>_SK::strval( UniSetTypes::Object
...
@@ -1401,7 +1429,7 @@ std::string <xsl:value-of select="$CLASSNAME"/>_SK::strval( UniSetTypes::Object
if( id ==
<xsl:value-of
select=
"../../@name"
/>
)
if( id ==
<xsl:value-of
select=
"../../@name"
/>
)
{
{
s
<<
"
<xsl:call-template
name=
"setprefix"
/><xsl:value-of
select=
"../../@name"
/>
";
s
<<
"
<xsl:call-template
name=
"setprefix"
/><xsl:value-of
select=
"../../@name"
/>
";
if( showLinkName ) s
<<
"
(
<xsl:value-of
select=
"../../@name"
/>
)";
if( showLinkName ) s
<<
"
(
<xsl:value-of
select=
"../../@name"
/>
)";
s
<<
"="
<<
<xsl:call-template
name=
"setprefix"
/><xsl:value-of
select=
"../../@name"
/>
;
s
<<
"="
<<
<xsl:call-template
name=
"setprefix"
/><xsl:value-of
select=
"../../@name"
/>
;
return std::move(s.str());
return std::move(s.str());
}
}
...
...
conf/libuniset2.spec
View file @
b8688490
...
@@ -13,7 +13,7 @@
...
@@ -13,7 +13,7 @@
Name: libuniset2
Name: libuniset2
Version: 2.2
Version: 2.2
Release: alt8
Release: alt8
.1
Summary: UniSet - library for building distributed industrial control systems
Summary: UniSet - library for building distributed industrial control systems
...
@@ -456,6 +456,9 @@ mv -f %buildroot%python_sitelibdir_noarch/* %buildroot%python_sitelibdir/%oname
...
@@ -456,6 +456,9 @@ mv -f %buildroot%python_sitelibdir_noarch/* %buildroot%python_sitelibdir/%oname
# ..
# ..
%changelog
%changelog
* Wed Oct 14 2015 Pavel Vainerman <pv@altlinux.ru> 2.2-alt8.1
- (VMonit): sort output
* Thu Oct 08 2015 Pavel Vainerman <pv@altlinux.ru> 2.2-alt8
* Thu Oct 08 2015 Pavel Vainerman <pv@altlinux.ru> 2.2-alt8
- (uniset-codegen): minor fixes in resetMsg() mechanism
- (uniset-codegen): minor fixes in resetMsg() mechanism
...
...
extensions/include/UObject_SK.h
View file @
b8688490
This diff is collapsed.
Click to expand it.
extensions/lib/UObject_SK.cc
View file @
b8688490
This diff is collapsed.
Click to expand it.
include/VMonitor.h
View file @
b8688490
...
@@ -24,6 +24,7 @@
...
@@ -24,6 +24,7 @@
#define VMonitor_H_
#define VMonitor_H_
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
#include <string>
#include <string>
#include <list>
#include <ostream>
#include <ostream>
#include <unordered_map>
#include <unordered_map>
#include "UniSetTypes.h"
#include "UniSetTypes.h"
...
@@ -120,8 +121,14 @@ class VMonitor
...
@@ -120,8 +121,14 @@ class VMonitor
static
const
int
NameWidth
=
{
30
};
static
const
int
NameWidth
=
{
30
};
static
const
int
ColCount
=
{
2
};
static
const
int
ColCount
=
{
2
};
/*! вывести все элементы в "простом формате" (строки "varname = value") */
std
::
string
str
();
std
::
string
str
();
std
::
string
pretty_str
();
/*! вывести все элементы "с форматированием" (отсортированные по алфавиту)
* \param namewidth - ширина резервируемая под "имя"
* \param colnum - количество столбцов вывода
*/
std
::
string
pretty_str
(
int
namewidth
=
NameWidth
,
int
colnum
=
ColCount
);
// функции добавления..
// функции добавления..
VMON_DEF_FUNC2
(
int
);
VMON_DEF_FUNC2
(
int
);
...
@@ -138,6 +145,8 @@ class VMonitor
...
@@ -138,6 +145,8 @@ class VMonitor
static
const
std
::
string
pretty_str
(
const
std
::
string
&
name
,
const
std
::
string
*
v
,
int
width
=
NameWidth
);
static
const
std
::
string
pretty_str
(
const
std
::
string
&
name
,
const
std
::
string
*
v
,
int
width
=
NameWidth
);
static
const
std
::
string
pretty_str
(
const
std
::
string
&
name
,
const
std
::
string
&
v
,
int
width
=
NameWidth
);
static
const
std
::
string
pretty_str
(
const
std
::
string
&
name
,
const
std
::
string
&
v
,
int
width
=
NameWidth
);
std
::
list
<
std
::
pair
<
std
::
string
,
std
::
string
>>
getList
();
protected
:
protected
:
private
:
private
:
...
@@ -149,7 +158,6 @@ class VMonitor
...
@@ -149,7 +158,6 @@ class VMonitor
VMON_DEF_MAP
(
bool
);
VMON_DEF_MAP
(
bool
);
VMON_DEF_MAP
(
float
);
VMON_DEF_MAP
(
float
);
VMON_DEF_MAP
(
double
);
VMON_DEF_MAP
(
double
);
// VMON_DEF_MAP3(UniSetTypes::ObjectId,ObjectId); // <-- long
VMON_DEF_MAP3
(
std
::
string
,
string
);
VMON_DEF_MAP3
(
std
::
string
,
string
);
};
};
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
...
...
src/Various/VMonitor.cc
View file @
b8688490
...
@@ -3,6 +3,7 @@
...
@@ -3,6 +3,7 @@
#include <sstream>
#include <sstream>
#include <vector>
#include <vector>
#include <iomanip>
#include <iomanip>
#include <algorithm>
#include "VMonitor.h"
#include "VMonitor.h"
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
#define VMON_IMPL_ADD(T) void VMonitor::add( const std::string& name, const T& v ) \
#define VMON_IMPL_ADD(T) void VMonitor::add( const std::string& name, const T& v ) \
...
@@ -13,7 +14,7 @@
...
@@ -13,7 +14,7 @@
const std::string VMonitor::pretty_str( const std::string& name, const T* v, int nwidth ) \
const std::string VMonitor::pretty_str( const std::string& name, const T* v, int nwidth ) \
{ \
{ \
std::ostringstream s; \
std::ostringstream s; \
s << std::right << std::setw(nwidth) << name << std::left << " = " << std::right << std::setw(
6)
<< *(v); \
s << std::right << std::setw(nwidth) << name << std::left << " = " << std::right << std::setw(
10)
<< *(v); \
return std::move(s.str()); \
return std::move(s.str()); \
} \
} \
const std::string VMonitor::pretty_str( const std::string& name, const T& v, int nwidth ) \
const std::string VMonitor::pretty_str( const std::string& name, const T& v, int nwidth ) \
...
@@ -33,13 +34,13 @@
...
@@ -33,13 +34,13 @@
const std::string VMonitor::pretty_str( const std::string& name, const T* v, int nwidth ) \
const std::string VMonitor::pretty_str( const std::string& name, const T* v, int nwidth ) \
{ \
{ \
std::ostringstream s; \
std::ostringstream s; \
s << std::right << std::setw(nwidth) << name << std::left << " = " << std::right << std::setw(
6)
<< *(v); \
s << std::right << std::setw(nwidth) << name << std::left << " = " << std::right << std::setw(
10)
<< *(v); \
return std::move(s.str()); \
return std::move(s.str()); \
} \
} \
const std::string VMonitor::pretty_str( const std::string& name, const unsigned T* v, int nwidth ) \
const std::string VMonitor::pretty_str( const std::string& name, const unsigned T* v, int nwidth ) \
{ \
{ \
std::ostringstream s; \
std::ostringstream s; \
s << std::right << std::setw(nwidth) << name << std::left << " = " << std::right << std::setw(
6)
<< *(v); \
s << std::right << std::setw(nwidth) << name << std::left << " = " << std::right << std::setw(
10)
<< *(v); \
return std::move(s.str()); \
return std::move(s.str()); \
} \
} \
const std::string VMonitor::pretty_str( const std::string& name, const T& v, int nwidth ) \
const std::string VMonitor::pretty_str( const std::string& name, const T& v, int nwidth ) \
...
@@ -58,7 +59,7 @@
...
@@ -58,7 +59,7 @@
const std::string VMonitor::pretty_str( const std::string& name, const T* v, int nwidth ) \
const std::string VMonitor::pretty_str( const std::string& name, const T* v, int nwidth ) \
{ \
{ \
std::ostringstream s; \
std::ostringstream s; \
s << std::right << std::setw(nwidth) << name << std::left << " = " << std::right << std::setw(
6)
<< *(v); \
s << std::right << std::setw(nwidth) << name << std::left << " = " << std::right << std::setw(
10)
<< *(v); \
return std::move(s.str()); \
return std::move(s.str()); \
} \
} \
const std::string VMonitor::pretty_str( const std::string& name, const T& v, int nwidth ) \
const std::string VMonitor::pretty_str( const std::string& name, const T& v, int nwidth ) \
...
@@ -66,102 +67,36 @@
...
@@ -66,102 +67,36 @@
return pretty_str(name,&v,nwidth); \
return pretty_str(name,&v,nwidth); \
}
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
#define VMON_
IMPL_PRN(M,
T) \
#define VMON_
MAKE_PAIR(vlist,
T) \
{\
{\
for( const auto& e: M.m_##T ) \
for( const auto& e: m_##T ) \
os << e.second << "=" << *(e.first) << std::endl;\
vlist.push_back( std::make_pair(e.second, std::to_string(*(e.first))) );\
}
// --------------------------------------------------------------------------
#define VMON_IMPL_PRN2(M,T) \
{\
for( const auto& e: M.m_##T ) \
os << e.second << "=" << *(e.first) << std::endl;\
\
for( const auto& e: M.m_unsigned_##T ) \
os << e.second << "=" << *(e.first) << std::endl;\
}
// --------------------------------------------------------------------------
#define VMON_IMPL_PRN_CHAR(M) \
{\
for( const auto& e: M.m_char ) \
os << e.second << "=" << (int)(*(e.first)) << std::endl;\
\
for( const auto& e: M.m_unsigned_char) \
os << e.second << "=" << (int)(*(e.first)) << std::endl;\
}
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
#define VMON_
IMPL_PRET(
T) \
#define VMON_
MAKE_PAIR_S(vlist,
T) \
{\
{\
std::vector<std::string> v(m_char.size()+m_unsigned_char.size());\
std::ostringstream s;\
for( const auto& e: m_##T ) \
for( const auto& e: m_##T ) \
{\
vlist.push_back( std::make_pair(e.second,*e.first) );\
s.str("");\
s << pretty_str(e.second,e.first);\
v.push_back(s.str()); \
}\
\
int n = 0;\
for( const auto& e: v ) \
{\
os << e; \
if( (n++)%ColCount ) \
os << std::endl; \
} \
}
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
#define VMON_
IMPL_PRET2(
T) \
#define VMON_
MAKE_PAIR2(vlist,
T) \
{\
{\
std::vector<std::string> v(m_char.size()+m_unsigned_char.size());\
std::ostringstream s;\
std::ostringstream s;\
for( const auto& e: m_##T ) \
for( const auto& e: m_##T ) \
{\
vlist.push_back( std::make_pair(e.second, std::to_string(*(e.first))) );\
s.str("");\
s << pretty_str(e.second,e.first);\
v.push_back(s.str());\
}\
\
\
for( const auto& e: m_unsigned_##T ) \
for( const auto& e: m_unsigned_##T ) \
{\
vlist.push_back( std::make_pair(e.second, std::to_string(*(e.first))) );\
s.str("");\
s << pretty_str(e.second,e.first);\
v.push_back(s.str());\
}\
\
int n = 0;\
for( const auto& e: v ) \
{\
os << e; \
if( (n++)%ColCount ) \
os << std::endl; \
} \
}
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
#define VMON_
IMPL_PRET_CHAR
\
#define VMON_
MAKE_PAIR_CHAR(vlist)
\
{\
{\
std::vector<std::string> v(m_char.size()+m_unsigned_char.size());\
std::ostringstream s;\
std::ostringstream s;\
for( const auto& e: m_char ) \
for( const auto& e: m_char ) \
{\
vlist.push_back(std::make_pair(e.second,std::to_string((int)(*(e.first)))) );\
s.str("");\
s << std::right << std::setw(NameWidth) << e.second << std::left << " = " << std::right << std::setw(6) << (int)(*(e.first));\
v.push_back(s.str());\
}\
\
\
for( const auto& e: m_unsigned_char ) \
for( const auto& e: m_unsigned_char ) \
{\
vlist.push_back(std::make_pair(e.second,std::to_string((int)(*(e.first)))) );\
s.str("");\
s << std::right << std::setw(NameWidth) << e.second << std::left << " = " << std::right << std::setw(6) << (int)(*(e.first));\
v.push_back(s.str()); \
}\
\
int n = 0;\
for( const auto& e: v ) \
{\
os << e; \
if( (n++)%ColCount ) \
os << std::endl; \
} \
}
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
VMON_IMPL_ADD2
(
int
)
VMON_IMPL_ADD2
(
int
)
...
@@ -174,21 +109,17 @@ VMON_IMPL_ADD(double)
...
@@ -174,21 +109,17 @@ VMON_IMPL_ADD(double)
VMON_IMPL_ADD3
(
std
::
string
,
string
)
VMON_IMPL_ADD3
(
std
::
string
,
string
)
//VMON_IMPL_ADD3(UniSetTypes::ObjectId,ObjectId)
//VMON_IMPL_ADD3(UniSetTypes::ObjectId,ObjectId)
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
VMonitor
&
m
)
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
VMonitor
&
m
)
{
{
VMON_IMPL_PRN2
(
m
,
int
);
auto
vlist
=
m
.
getList
();
VMON_IMPL_PRN2
(
m
,
long
);
// сортируем по алфавиту
VMON_IMPL_PRN2
(
m
,
short
);
vlist
.
sort
(
[](
const
std
::
pair
<
std
::
string
,
std
::
string
>
&
a
,
const
std
::
pair
<
std
::
string
,
std
::
string
>
&
b
)
{
return
a
.
first
<
b
.
first
;
});
VMON_IMPL_PRN_CHAR
(
m
);
VMON_IMPL_PRN
(
m
,
bool
);
for
(
const
auto
&
e
:
vlist
)
VMON_IMPL_PRN
(
m
,
float
);
os
<<
e
.
first
<<
" = "
<<
e
.
second
;
VMON_IMPL_PRN
(
m
,
double
);
VMON_IMPL_PRN
(
m
,
string
);
// VMON_IMPL_PRN(m,ObjectId);
return
os
;
return
os
;
}
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
std
::
string
VMonitor
::
str
()
std
::
string
VMonitor
::
str
()
{
{
...
@@ -197,18 +128,35 @@ std::string VMonitor::str()
...
@@ -197,18 +128,35 @@ std::string VMonitor::str()
return
std
::
move
(
s
.
str
());
return
std
::
move
(
s
.
str
());
}
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
std
::
string
VMonitor
::
pretty_str
()
std
::
list
<
std
::
pair
<
std
::
string
,
std
::
string
>
>
VMonitor
::
getList
()
{
std
::
list
<
std
::
pair
<
std
::
string
,
std
::
string
>>
vlist
;
VMON_MAKE_PAIR2
(
vlist
,
int
);
VMON_MAKE_PAIR2
(
vlist
,
long
);
VMON_MAKE_PAIR2
(
vlist
,
short
);
VMON_MAKE_PAIR_CHAR
(
vlist
);
VMON_MAKE_PAIR
(
vlist
,
bool
);
VMON_MAKE_PAIR
(
vlist
,
float
);
VMON_MAKE_PAIR
(
vlist
,
double
);
VMON_MAKE_PAIR_S
(
vlist
,
string
);
return
std
::
move
(
vlist
);
}
// --------------------------------------------------------------------------
std
::
string
VMonitor
::
pretty_str
(
int
namewidth
,
int
colnum
)
{
{
auto
vlist
=
getList
();
std
::
ostringstream
os
;
std
::
ostringstream
os
;
VMON_IMPL_PRET2
(
int
);
VMON_IMPL_PRET2
(
long
);
// сортируем по алфавиту
VMON_IMPL_PRET2
(
short
);
vlist
.
sort
(
[](
const
std
::
pair
<
std
::
string
,
std
::
string
>
&
a
,
const
std
::
pair
<
std
::
string
,
std
::
string
>
&
b
)
{
return
a
.
first
<
b
.
first
;
});
VMON_IMPL_PRET_CHAR
;
VMON_IMPL_PRET
(
bool
);
int
n
=
0
;
VMON_IMPL_PRET
(
float
);
for
(
const
auto
&
e
:
vlist
)
VMON_IMPL_PRET
(
double
);
{
VMON_IMPL_PRET
(
string
);
os
<<
std
::
right
<<
std
::
setw
(
namewidth
)
<<
e
.
first
<<
std
::
left
<<
" = "
<<
std
::
right
<<
std
::
setw
(
10
)
<<
e
.
second
;
// VMON_IMPL_PRET(ObjectId);
if
(
(
n
++
)
%
colnum
)
os
<<
std
::
endl
;
}
return
std
::
move
(
os
.
str
());
return
std
::
move
(
os
.
str
());
}
}
...
...
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