added "mode" argument to "getdata" command.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* This file is part of the project AqHome.
|
* This file is part of the project AqHome.
|
||||||
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
|
||||||
*
|
*
|
||||||
* The license for this file can be found in the file COPYING which you
|
* The license for this file can be found in the file COPYING which you
|
||||||
* should have received along with this file.
|
* should have received along with this file.
|
||||||
@@ -33,8 +33,8 @@
|
|||||||
* ------------------------------------------------------------------------------------------------
|
* ------------------------------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES 2048
|
#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES 1024
|
||||||
#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS 1024
|
#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS 512
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
/* ------------------------------------------------------------------------------------------------
|
||||||
@@ -44,13 +44,15 @@
|
|||||||
|
|
||||||
static int _getAndSendDataPoints(AQH_STORAGE *storage, AQH_OBJECT *ep,
|
static int _getAndSendDataPoints(AQH_STORAGE *storage, AQH_OBJECT *ep,
|
||||||
const AQH_VALUE *value,
|
const AQH_VALUE *value,
|
||||||
|
int mode,
|
||||||
uint64_t tsBegin, uint64_t tsEnd, uint64_t num, uint32_t refMsgId);
|
uint64_t tsBegin, uint64_t tsEnd, uint64_t num, uint32_t refMsgId);
|
||||||
static int _getAndSendDataPointsNoNum(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd,
|
static int _getAndSendDataPointsPeriod(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value,
|
||||||
|
uint64_t tsBegin, uint64_t tsEnd, uint64_t num,
|
||||||
uint32_t refMsgId);
|
uint32_t refMsgId);
|
||||||
static int _getAndSendDataPointsWithNum(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint64_t num, uint32_t refMsgId);
|
static int _getAndSendDataPointsFirst(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint64_t num, uint32_t refMsgId);
|
||||||
|
static int _getAndSendDataPointsLast(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint64_t num, uint32_t refMsgId);
|
||||||
static void _sendDataPointsResponse(AQH_OBJECT *ep, const AQH_VALUE *value, const uint64_t *tablePtr,
|
static void _sendDataPointsResponse(AQH_OBJECT *ep, const AQH_VALUE *value, const uint64_t *tablePtr,
|
||||||
uint32_t refMsgId);
|
uint32_t refMsgId);
|
||||||
static void _getAndSendLastDatapoint(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint32_t refMsgId);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -79,16 +81,18 @@ void AqHomeDataServer_HandleGetDataPoints(AQH_OBJECT *o, AQH_OBJECT *ep, const A
|
|||||||
uint64_t tsBegin;
|
uint64_t tsBegin;
|
||||||
uint64_t tsEnd;
|
uint64_t tsEnd;
|
||||||
uint64_t numRequested;
|
uint64_t numRequested;
|
||||||
|
int mode;
|
||||||
|
|
||||||
tsBegin=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_BEGIN, 0);
|
tsBegin=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_BEGIN, 0);
|
||||||
tsEnd=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_END, 0);
|
tsEnd=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_END, 0);
|
||||||
numRequested=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_NUM, 0);
|
numRequested=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_NUM, 0);
|
||||||
|
mode=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_MODE, AQH_MSGDATA_GETDATA_MODE_FIRST);
|
||||||
|
|
||||||
value=AQH_Storage_GetValueByNameForSystem(xo->storage, valueName);
|
value=AQH_Storage_GetValueByNameForSystem(xo->storage, valueName);
|
||||||
if (value) {
|
if (value) {
|
||||||
int resultCode;
|
int resultCode;
|
||||||
|
|
||||||
resultCode=_getAndSendDataPoints(xo->storage, ep, value, tsBegin, tsEnd, numRequested, refMsgId);
|
resultCode=_getAndSendDataPoints(xo->storage, ep, value, mode, tsBegin, tsEnd, numRequested, refMsgId);
|
||||||
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, resultCode);
|
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, resultCode);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -114,30 +118,31 @@ void AqHomeDataServer_HandleGetDataPoints(AQH_OBJECT *o, AQH_OBJECT *ep, const A
|
|||||||
|
|
||||||
int _getAndSendDataPoints(AQH_STORAGE *storage, AQH_OBJECT *ep,
|
int _getAndSendDataPoints(AQH_STORAGE *storage, AQH_OBJECT *ep,
|
||||||
const AQH_VALUE *value,
|
const AQH_VALUE *value,
|
||||||
|
int mode,
|
||||||
uint64_t tsBegin, uint64_t tsEnd, uint64_t num,
|
uint64_t tsBegin, uint64_t tsEnd, uint64_t num,
|
||||||
uint32_t refMsgId)
|
uint32_t refMsgId)
|
||||||
{
|
{
|
||||||
if (num==0)
|
switch(mode) {
|
||||||
return _getAndSendDataPointsNoNum(storage, ep, value, tsBegin, tsEnd, refMsgId);
|
case AQH_MSGDATA_GETDATA_MODE_FIRST: return _getAndSendDataPointsFirst(storage, ep, value, num, refMsgId);
|
||||||
else if (num==1) {
|
case AQH_MSGDATA_GETDATA_MODE_PERIOD: return _getAndSendDataPointsPeriod(storage, ep, value, tsBegin, tsEnd, num, refMsgId);
|
||||||
_getAndSendLastDatapoint(storage, ep, value, refMsgId);
|
default:
|
||||||
return AQH_MSGDATA_RESULT_SUCCESS;
|
case AQH_MSGDATA_GETDATA_MODE_LAST: return _getAndSendDataPointsLast(storage, ep, value, num, refMsgId);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return _getAndSendDataPointsWithNum(storage, ep, value, num, refMsgId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _getAndSendDataPointsNoNum(AQH_STORAGE *storage, AQH_OBJECT *ep,
|
int _getAndSendDataPointsPeriod(AQH_STORAGE *storage, AQH_OBJECT *ep,
|
||||||
const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd,
|
const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd, uint64_t num,
|
||||||
uint32_t refMsgId)
|
uint32_t refMsgId)
|
||||||
{
|
{
|
||||||
uint64_t valueId;
|
uint64_t valueId;
|
||||||
uint64_t *tablePtr;
|
uint64_t *tablePtr;
|
||||||
|
|
||||||
valueId=AQH_Value_GetId(value);
|
valueId=AQH_Value_GetId(value);
|
||||||
tablePtr=AQH_Storage_GetDataPoints(storage, valueId, tsBegin, tsEnd, AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES);
|
if (num==0 || num>AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES)
|
||||||
|
num=AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES;
|
||||||
|
tablePtr=AQH_Storage_GetDataPoints(storage, valueId, tsBegin, tsEnd, num);
|
||||||
if (tablePtr) {
|
if (tablePtr) {
|
||||||
_sendDataPointsResponse(ep, value, tablePtr, refMsgId);
|
_sendDataPointsResponse(ep, value, tablePtr, refMsgId);
|
||||||
free(tablePtr);
|
free(tablePtr);
|
||||||
@@ -151,9 +156,9 @@ int _getAndSendDataPointsNoNum(AQH_STORAGE *storage, AQH_OBJECT *ep,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
int _getAndSendDataPointsWithNum(AQH_STORAGE *storage, AQH_OBJECT *ep,
|
int _getAndSendDataPointsLast(AQH_STORAGE *storage, AQH_OBJECT *ep,
|
||||||
const AQH_VALUE *value, uint64_t num,
|
const AQH_VALUE *value, uint64_t num,
|
||||||
uint32_t refMsgId)
|
uint32_t refMsgId)
|
||||||
{
|
{
|
||||||
uint64_t valueId;
|
uint64_t valueId;
|
||||||
uint64_t *tablePtr;
|
uint64_t *tablePtr;
|
||||||
@@ -175,6 +180,30 @@ int _getAndSendDataPointsWithNum(AQH_STORAGE *storage, AQH_OBJECT *ep,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int _getAndSendDataPointsFirst(AQH_STORAGE *storage, AQH_OBJECT *ep,
|
||||||
|
const AQH_VALUE *value, uint64_t num,
|
||||||
|
uint32_t refMsgId)
|
||||||
|
{
|
||||||
|
uint64_t valueId;
|
||||||
|
uint64_t *tablePtr;
|
||||||
|
|
||||||
|
if (num>AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS)
|
||||||
|
num=AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS;
|
||||||
|
valueId=AQH_Value_GetId(value);
|
||||||
|
tablePtr=AQH_Storage_GetFirstNDataPoints(storage, valueId, num);
|
||||||
|
if (tablePtr) {
|
||||||
|
_sendDataPointsResponse(ep, value, tablePtr, refMsgId);
|
||||||
|
free(tablePtr);
|
||||||
|
return AQH_MSGDATA_RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DBG_INFO(NULL, "No matching datapoints for value \"%s\"", AQH_Value_GetNameForSystem(value));
|
||||||
|
return AQH_MSGDATA_RESULT_ERROR_NODATA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _sendDataPointsResponse(AQH_OBJECT *ep,
|
void _sendDataPointsResponse(AQH_OBJECT *ep,
|
||||||
const AQH_VALUE *value, const uint64_t *tablePtr,
|
const AQH_VALUE *value, const uint64_t *tablePtr,
|
||||||
uint32_t refMsgId)
|
uint32_t refMsgId)
|
||||||
@@ -193,36 +222,3 @@ void _sendDataPointsResponse(AQH_OBJECT *ep,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _getAndSendLastDatapoint(AQH_STORAGE *storage, AQH_OBJECT *ep,
|
|
||||||
const AQH_VALUE *value, uint32_t refMsgId)
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
uint64_t timestamp=0;
|
|
||||||
double data=0.0;
|
|
||||||
|
|
||||||
rv=AQH_Storage_GetLastDataPoint(storage, AQH_Value_GetId(value), ×tamp, &data);
|
|
||||||
if (rv<0) {
|
|
||||||
int resultCode;
|
|
||||||
|
|
||||||
switch(rv) {
|
|
||||||
case GWEN_ERROR_INVALID: resultCode=AQH_MSGDATA_RESULT_ERROR_INVALID; break;
|
|
||||||
case GWEN_ERROR_NO_DATA: resultCode=AQH_MSGDATA_RESULT_ERROR_NODATA; break;
|
|
||||||
default: resultCode=AQH_MSGDATA_RESULT_ERROR_GENERIC; break;
|
|
||||||
}
|
|
||||||
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, resultCode);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
AQH_MESSAGE *outMsg;
|
|
||||||
|
|
||||||
outMsg=AQH_IpcdMessageMultiData_newForOne(AQH_MSGTYPE_IPC_DATA_GETDATA_RSP,
|
|
||||||
AQH_Endpoint_GetNextMessageId(ep), refMsgId,
|
|
||||||
value, timestamp, data);
|
|
||||||
AQH_Endpoint_AddMsgOut(ep, outMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,12 @@ AQH_MESSAGE *_createRequestMessage(GWEN_UNUSED AQH_OBJECT *o, uint32_t msgId)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return AQH_IpcdMessageGetData_new(AQH_MSGTYPE_IPC_DATA_GETDATA_REQ, msgId, 0, valueName, tsBegin, tsEnd, num);
|
// TODO: use "mode" correctly
|
||||||
|
return AQH_IpcdMessageGetData_new(AQH_MSGTYPE_IPC_DATA_GETDATA_REQ,
|
||||||
|
msgId, 0,
|
||||||
|
AQH_MSGDATA_GETDATA_MODE_LAST,
|
||||||
|
valueName,
|
||||||
|
tsBegin, tsEnd, num);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,8 @@
|
|||||||
* ------------------------------------------------------------------------------------------------
|
* ------------------------------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static const char *_modeToChar(int mode);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------
|
/* ------------------------------------------------------------------------------------------------
|
||||||
@@ -37,6 +39,7 @@
|
|||||||
|
|
||||||
AQH_MESSAGE *AQH_IpcdMessageGetData_new(uint16_t code,
|
AQH_MESSAGE *AQH_IpcdMessageGetData_new(uint16_t code,
|
||||||
uint32_t msgId, uint32_t refMsgId,
|
uint32_t msgId, uint32_t refMsgId,
|
||||||
|
int mode,
|
||||||
const char *valueName, uint64_t tsBegin, uint64_t tsEnd, uint64_t num)
|
const char *valueName, uint64_t tsBegin, uint64_t tsEnd, uint64_t num)
|
||||||
{
|
{
|
||||||
AQH_MESSAGE *msg;
|
AQH_MESSAGE *msg;
|
||||||
@@ -49,6 +52,7 @@ AQH_MESSAGE *AQH_IpcdMessageGetData_new(uint16_t code,
|
|||||||
GWEN_Tag16_WriteUint64TagToBuffer(AQH_MSGDATA_GETDATA_TAGS_BEGIN, tsBegin, buf);
|
GWEN_Tag16_WriteUint64TagToBuffer(AQH_MSGDATA_GETDATA_TAGS_BEGIN, tsBegin, buf);
|
||||||
GWEN_Tag16_WriteUint64TagToBuffer(AQH_MSGDATA_GETDATA_TAGS_END, tsEnd, buf);
|
GWEN_Tag16_WriteUint64TagToBuffer(AQH_MSGDATA_GETDATA_TAGS_END, tsEnd, buf);
|
||||||
GWEN_Tag16_WriteUint64TagToBuffer(AQH_MSGDATA_GETDATA_TAGS_NUM, num, buf);
|
GWEN_Tag16_WriteUint64TagToBuffer(AQH_MSGDATA_GETDATA_TAGS_NUM, num, buf);
|
||||||
|
GWEN_Tag16_WriteUint64TagToBuffer(AQH_MSGDATA_GETDATA_TAGS_MODE, mode, buf);
|
||||||
|
|
||||||
msg=AQH_IpcMessage_new(AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION, code, msgId, refMsgId,
|
msg=AQH_IpcMessage_new(AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION, code, msgId, refMsgId,
|
||||||
GWEN_Buffer_GetUsedBytes(buf), (const uint8_t*) GWEN_Buffer_GetStart(buf));
|
GWEN_Buffer_GetUsedBytes(buf), (const uint8_t*) GWEN_Buffer_GetStart(buf));
|
||||||
@@ -64,19 +68,22 @@ void AQH_IpcdMessageGetData_DumpToBuffer(const AQH_MESSAGE *msg, const GWEN_TAG1
|
|||||||
char *valueName;
|
char *valueName;
|
||||||
uint64_t tsBegin;
|
uint64_t tsBegin;
|
||||||
uint64_t tsEnd;
|
uint64_t tsEnd;
|
||||||
|
uint64_t mode;
|
||||||
|
|
||||||
valueName=tagList?AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSGDATA_GETDATA_TAGS_NAME, NULL):NULL;
|
valueName=tagList?AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSGDATA_GETDATA_TAGS_NAME, NULL):NULL;
|
||||||
tsBegin=tagList?AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_BEGIN, 0):0;
|
tsBegin=tagList?AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_BEGIN, 0):0;
|
||||||
tsEnd=tagList?AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_END, 0):0;
|
tsEnd=tagList?AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_END, 0):0;
|
||||||
|
mode=tagList?AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_MODE, 0):0;
|
||||||
|
|
||||||
GWEN_Buffer_AppendArgs(dbuf,
|
GWEN_Buffer_AppendArgs(dbuf,
|
||||||
"GETDATA(%s) %s (code=%d, proto=%d, proto version=%d, name=%s, tsBegin=%lu, tsEnd=%lu)\n",
|
"GETDATA(%s) %s (code=%d, proto=%d, proto version=%d, name=%s, mode=%s, tsBegin=%lu, tsEnd=%lu)\n",
|
||||||
AQH_IpcdMessage_MsgTypeToChar(AQH_IpcMessage_GetCode(msg)),
|
AQH_IpcdMessage_MsgTypeToChar(AQH_IpcMessage_GetCode(msg)),
|
||||||
sText?sText:"",
|
sText?sText:"",
|
||||||
AQH_IpcMessage_GetCode(msg),
|
AQH_IpcMessage_GetCode(msg),
|
||||||
AQH_IpcMessage_GetProtoId(msg),
|
AQH_IpcMessage_GetProtoId(msg),
|
||||||
AQH_IpcMessage_GetProtoVersion(msg),
|
AQH_IpcMessage_GetProtoVersion(msg),
|
||||||
valueName?valueName:"<empty>",
|
valueName?valueName:"<empty>",
|
||||||
|
_modeToChar(mode),
|
||||||
(unsigned long int) tsBegin,
|
(unsigned long int) tsBegin,
|
||||||
(unsigned long int) tsEnd);
|
(unsigned long int) tsEnd);
|
||||||
free(valueName);
|
free(valueName);
|
||||||
@@ -85,3 +92,15 @@ void AQH_IpcdMessageGetData_DumpToBuffer(const AQH_MESSAGE *msg, const GWEN_TAG1
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const char *_modeToChar(int mode)
|
||||||
|
{
|
||||||
|
switch(mode) {
|
||||||
|
case AQH_MSGDATA_GETDATA_MODE_FIRST: return "first";
|
||||||
|
case AQH_MSGDATA_GETDATA_MODE_LAST: return "last";
|
||||||
|
case AQH_MSGDATA_GETDATA_MODE_PERIOD: return "period";
|
||||||
|
default: return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -24,10 +24,20 @@
|
|||||||
#define AQH_MSGDATA_GETDATA_TAGS_BEGIN 0x0020
|
#define AQH_MSGDATA_GETDATA_TAGS_BEGIN 0x0020
|
||||||
#define AQH_MSGDATA_GETDATA_TAGS_END 0x0021
|
#define AQH_MSGDATA_GETDATA_TAGS_END 0x0021
|
||||||
#define AQH_MSGDATA_GETDATA_TAGS_NUM 0x0022
|
#define AQH_MSGDATA_GETDATA_TAGS_NUM 0x0022
|
||||||
|
#define AQH_MSGDATA_GETDATA_TAGS_MODE 0x0023
|
||||||
|
|
||||||
|
|
||||||
|
enum {
|
||||||
|
AQH_MSGDATA_GETDATA_MODE_FIRST=0,
|
||||||
|
AQH_MSGDATA_GETDATA_MODE_LAST,
|
||||||
|
AQH_MSGDATA_GETDATA_MODE_PERIOD
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AQHOME_API AQH_MESSAGE *AQH_IpcdMessageGetData_new(uint16_t code,
|
AQHOME_API AQH_MESSAGE *AQH_IpcdMessageGetData_new(uint16_t code,
|
||||||
uint32_t msgId, uint32_t refMsgId,
|
uint32_t msgId, uint32_t refMsgId,
|
||||||
|
int mode,
|
||||||
const char *valueName, uint64_t tsBegin, uint64_t tsEnd, uint64_t num);
|
const char *valueName, uint64_t tsBegin, uint64_t tsEnd, uint64_t num);
|
||||||
|
|
||||||
AQHOME_API void AQH_IpcdMessageGetData_DumpToBuffer(const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList,
|
AQHOME_API void AQH_IpcdMessageGetData_DumpToBuffer(const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList,
|
||||||
|
|||||||
Reference in New Issue
Block a user