mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-08-05 01:18:36 +02:00
Separate MessageDecoder from Packetizer and optimize
This will, in the future, allow overriding of MessageDecoder per device as necessary.
This commit is contained in:
@@ -40,6 +40,12 @@ public:
|
||||
|
||||
void setAlign16Bit(bool enable) { align16bit = enable; }
|
||||
|
||||
class Packet {
|
||||
public:
|
||||
Network network;
|
||||
std::vector<uint8_t> data;
|
||||
};
|
||||
|
||||
protected:
|
||||
std::shared_ptr<ICommunication> impl;
|
||||
static int messageCallbackIDCounter;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef __MESSAGEDECODER_H_
|
||||
#define __MESSAGEDECODER_H_
|
||||
|
||||
#include "communication/include/communication.h"
|
||||
#include "communication/message/include/message.h"
|
||||
#include "communication/message/include/canmessage.h"
|
||||
#include "communication/include/network.h"
|
||||
@@ -12,245 +13,258 @@ namespace icsneo {
|
||||
|
||||
class MessageDecoder {
|
||||
public:
|
||||
bool input(const std::vector<uint8_t>& bytes);
|
||||
std::vector<std::shared_ptr<Message>> output();
|
||||
std::shared_ptr<Message> decodePacket(const std::shared_ptr<Communication::Packet>& message);
|
||||
|
||||
private:
|
||||
enum class ReadState {
|
||||
SearchForHeader,
|
||||
ParseHeader,
|
||||
ParseLongStylePacketHeader,
|
||||
GetData
|
||||
};
|
||||
ReadState state = ReadState::SearchForHeader;
|
||||
|
||||
int currentIndex = 0;
|
||||
int messageLength = 0;
|
||||
int headerSize = 0;
|
||||
bool checksum = false;
|
||||
bool gotGoodMessages = false; // Tracks whether we've ever gotten a good message
|
||||
Message message;
|
||||
std::deque<uint8_t> bytes;
|
||||
|
||||
void processMessage(const Message& message);
|
||||
|
||||
std::vector<std::shared_ptr<Message>> processedMessages;
|
||||
|
||||
typedef uint16_t icscm_bitfield;
|
||||
struct CoreMiniMsg {
|
||||
CANMessage toCANMessage(Network netid);
|
||||
union {
|
||||
uint16_t CxTRB0SID16;
|
||||
struct
|
||||
{
|
||||
icscm_bitfield IDE : 1;
|
||||
icscm_bitfield SRR : 1;
|
||||
icscm_bitfield SID : 11;
|
||||
icscm_bitfield NETWORKINDEX : 3;//DO NOT CLOBBER THIS
|
||||
} CxTRB0SID;
|
||||
struct
|
||||
{
|
||||
icscm_bitfield : 13;
|
||||
icscm_bitfield EDL : 1;
|
||||
icscm_bitfield BRS : 1;
|
||||
icscm_bitfield ESI : 1;
|
||||
} CxTRB0FD;
|
||||
struct
|
||||
{
|
||||
icscm_bitfield ErrRxOnlyBreak : 1;
|
||||
icscm_bitfield ErrRxOnlyBreakSync : 1;
|
||||
icscm_bitfield ID : 11;
|
||||
icscm_bitfield NETWORKINDEX : 3;//DO NOT CLOBBER THIS
|
||||
} CxLIN3;
|
||||
struct
|
||||
{
|
||||
uint8_t D8;
|
||||
uint8_t options : 4;
|
||||
uint8_t TXMSG : 1;
|
||||
uint8_t NETWORKINDEX : 3;//DO NOT CLOBBER THIS
|
||||
} C1xJ1850;
|
||||
struct
|
||||
{
|
||||
uint8_t D8;
|
||||
uint8_t options : 4;
|
||||
uint8_t TXMSG : 1;
|
||||
uint8_t NETWORKINDEX : 3;//DO NOT CLOBBER THIS
|
||||
} C1xISO;
|
||||
struct
|
||||
{
|
||||
uint8_t D8;
|
||||
uint8_t options : 4;
|
||||
uint8_t TXMSG : 1;
|
||||
uint8_t NETWORKINDEX : 3;//DO NOT CLOBBER THIS
|
||||
} C1xJ1708;
|
||||
struct
|
||||
{
|
||||
icscm_bitfield FCS_AVAIL : 1;
|
||||
icscm_bitfield RUNT_FRAME : 1;
|
||||
icscm_bitfield DISABLE_PADDING : 1;
|
||||
icscm_bitfield PREEMPTION_ENABLED : 1;
|
||||
icscm_bitfield MPACKET_TYPE : 4;
|
||||
icscm_bitfield MPACKET_FRAG_CNT : 2;
|
||||
icscm_bitfield : 6;
|
||||
} C1xETH;
|
||||
struct
|
||||
{
|
||||
uint16_t ID : 11;
|
||||
uint16_t STARTUP : 1;
|
||||
uint16_t SYNC : 1;
|
||||
uint16_t NULL_FRAME : 1;
|
||||
uint16_t PAYLOAD_PREAMBLE : 1;
|
||||
uint16_t RESERVED_0 : 1;
|
||||
} C1xFlex;
|
||||
struct
|
||||
{
|
||||
uint8_t daqType;
|
||||
uint8_t ethDaqRes1;
|
||||
} C1xETHDAQ;
|
||||
};
|
||||
union {
|
||||
uint16_t CxTRB0EID16;
|
||||
struct
|
||||
{
|
||||
icscm_bitfield EID : 12;
|
||||
icscm_bitfield TXMSG : 1;
|
||||
icscm_bitfield TXAborted : 1;
|
||||
icscm_bitfield TXLostArb : 1;
|
||||
icscm_bitfield TXError : 1;
|
||||
} CxTRB0EID;
|
||||
struct
|
||||
{
|
||||
uint8_t LINByte9;
|
||||
uint8_t ErrTxRxMismatch : 1;
|
||||
uint8_t TxChkSumEnhanced : 1;
|
||||
uint8_t TXMaster : 1;
|
||||
uint8_t TXSlave : 1;
|
||||
uint8_t ErrRxBreakNot0 : 1;
|
||||
uint8_t ErrRxBreakTooShort : 1;
|
||||
uint8_t ErrRxSyncNot55 : 1;
|
||||
uint8_t ErrRxDataGreater8 : 1;
|
||||
} CxLIN;
|
||||
struct
|
||||
{
|
||||
uint8_t D9;
|
||||
uint8_t D10;
|
||||
} C2xJ1850;
|
||||
struct
|
||||
{
|
||||
uint8_t D9;
|
||||
uint8_t D10;
|
||||
} C2xISO;
|
||||
struct
|
||||
{
|
||||
uint8_t D9;
|
||||
uint8_t D10;
|
||||
} C2xJ1708;
|
||||
struct
|
||||
{
|
||||
uint16_t txlen : 12;
|
||||
uint16_t TXMSG : 1;
|
||||
uint16_t : 3;
|
||||
} C2xETH;
|
||||
struct
|
||||
{
|
||||
uint16_t HDR_CRC_10 : 1;
|
||||
uint16_t PAYLOAD_LEN : 7;
|
||||
uint16_t RESERVED_1 : 4;
|
||||
uint16_t TXMSG : 1;
|
||||
uint16_t RESERVED_2 : 3;
|
||||
} C2xFlex;
|
||||
};
|
||||
union {
|
||||
// For use by CAN
|
||||
uint16_t CxTRB0DLC16;
|
||||
struct
|
||||
{
|
||||
icscm_bitfield DLC : 4;
|
||||
icscm_bitfield RB0 : 1;
|
||||
icscm_bitfield IVRIF : 1;
|
||||
icscm_bitfield HVEnable : 1;// must be cleared before passing into CAN driver
|
||||
icscm_bitfield ExtendedNetworkIndexBit : 1;//DO NOT CLOBBER THIS
|
||||
icscm_bitfield RB1 : 1;
|
||||
icscm_bitfield RTR : 1;
|
||||
icscm_bitfield EID : 6;
|
||||
} CxTRB0DLC;
|
||||
struct
|
||||
{
|
||||
icscm_bitfield len : 4;
|
||||
icscm_bitfield ExtendedNetworkIndexBit2 : 1;//DO NOT CLOBBER THIS
|
||||
icscm_bitfield UpdateSlaveOnce : 1;
|
||||
icscm_bitfield HasUpdatedSlaveOnce : 1;
|
||||
icscm_bitfield ExtendedNetworkIndexBit : 1;//DO NOT CLOBBER THIS
|
||||
icscm_bitfield BusRecovered : 1;
|
||||
icscm_bitfield SyncFerr : 1;//!< We got framing error in our sync byte.
|
||||
icscm_bitfield MidFerr : 1;//!< We got framing error in our message id.
|
||||
icscm_bitfield SlaveByteFerr : 1;//!< We got framing error in one of our slave bytes.
|
||||
icscm_bitfield TxAborted : 1;//!< This transmit was aborted.
|
||||
icscm_bitfield breakOnly : 1;
|
||||
icscm_bitfield : 2;
|
||||
} CxLIN2;
|
||||
// For use by JVPW
|
||||
struct
|
||||
{
|
||||
icscm_bitfield len : 4;
|
||||
icscm_bitfield ExtendedNetworkIndexBit2 : 1;//DO NOT CLOBBER THIS
|
||||
icscm_bitfield just_tx_timestamp : 1;
|
||||
icscm_bitfield first_seg : 1;
|
||||
icscm_bitfield ExtendedNetworkIndexBit : 1;// do not clobber ExtendedNetworkIndexBit
|
||||
icscm_bitfield D11 : 8;
|
||||
} C3xJ1850;
|
||||
// For use by the ISO/KEYWORD
|
||||
struct
|
||||
{
|
||||
icscm_bitfield len : 4;
|
||||
icscm_bitfield ExtendedNetworkIndexBit2 : 1;//DO NOT CLOBBER THIS
|
||||
icscm_bitfield FRM : 1;
|
||||
icscm_bitfield INIT : 1;
|
||||
icscm_bitfield ExtendedNetworkIndexBit : 1;// do not clobber ExtendedNetworkIndexBit
|
||||
icscm_bitfield D11 : 8;
|
||||
} C3xISO;
|
||||
struct
|
||||
{
|
||||
icscm_bitfield len : 4;
|
||||
icscm_bitfield ExtendedNetworkIndexBit2 : 1;//DO NOT CLOBBER THIS
|
||||
icscm_bitfield FRM : 1;
|
||||
icscm_bitfield : 1;
|
||||
icscm_bitfield ExtendedNetworkIndexBit : 1;// do not clobber ExtendedNetworkIndexBit
|
||||
icscm_bitfield pri : 8;
|
||||
} C3xJ1708;
|
||||
struct
|
||||
{
|
||||
uint16_t rsvd;
|
||||
} C3xETH;
|
||||
struct
|
||||
{
|
||||
uint16_t CYCLE : 6;
|
||||
uint16_t HDR_CRC_9_0 : 10;
|
||||
} C3xFlex;
|
||||
};
|
||||
unsigned char CxTRB0Dall[8];
|
||||
union {
|
||||
uint16_t CxTRB0STAT;
|
||||
uint16_t J1850_TX_ID;
|
||||
};
|
||||
union {
|
||||
struct
|
||||
{
|
||||
uint32_t uiTimeStamp10uS;
|
||||
union {
|
||||
uint32_t uiTimeStamp10uSMSB;
|
||||
struct
|
||||
{
|
||||
unsigned : 28;
|
||||
unsigned res_0s : 3;// must be 0!!!
|
||||
unsigned bIsExtended : 1;// always 1 for CoreMiniMsgExtended.
|
||||
};
|
||||
};
|
||||
};
|
||||
int64_t uiTimeStampLarge;
|
||||
uint8_t uiTimeStampBytes[8];
|
||||
};
|
||||
struct HardwareCANPacket {
|
||||
struct {
|
||||
icscm_bitfield IDE : 1;
|
||||
icscm_bitfield SRR : 1;
|
||||
icscm_bitfield SID : 11;
|
||||
icscm_bitfield EDL : 1;
|
||||
icscm_bitfield BRS : 1;
|
||||
icscm_bitfield ESI : 1;
|
||||
} header;
|
||||
struct {
|
||||
icscm_bitfield EID : 12;
|
||||
icscm_bitfield TXMSG : 1;
|
||||
icscm_bitfield TXAborted : 1;
|
||||
icscm_bitfield TXLostArb : 1;
|
||||
icscm_bitfield TXError : 1;
|
||||
} eid;
|
||||
struct {
|
||||
icscm_bitfield DLC : 4;
|
||||
icscm_bitfield RB0 : 1;
|
||||
icscm_bitfield IVRIF : 1;
|
||||
icscm_bitfield HVEnable : 1;// must be cleared before passing into CAN driver
|
||||
icscm_bitfield ExtendedNetworkIndexBit : 1;//DO NOT CLOBBER THIS
|
||||
icscm_bitfield RB1 : 1;
|
||||
icscm_bitfield RTR : 1;
|
||||
icscm_bitfield EID : 6;
|
||||
} dlc;
|
||||
unsigned char data[8];
|
||||
struct {
|
||||
uint64_t TS : 60;
|
||||
uint64_t : 3;
|
||||
uint64_t IsExtended : 1;
|
||||
} timestamp;
|
||||
};
|
||||
|
||||
// struct CoreMiniMsg {
|
||||
// CANMessage toCANMessage(Network netid);
|
||||
// union {
|
||||
// uint16_t CxTRB0SID16;
|
||||
// struct
|
||||
// {
|
||||
// icscm_bitfield IDE : 1;
|
||||
// icscm_bitfield SRR : 1;
|
||||
// icscm_bitfield SID : 11;
|
||||
// icscm_bitfield NETWORKINDEX : 3;//DO NOT CLOBBER THIS
|
||||
// } CxTRB0SID;
|
||||
// struct
|
||||
// {
|
||||
// icscm_bitfield : 13;
|
||||
// icscm_bitfield EDL : 1;
|
||||
// icscm_bitfield BRS : 1;
|
||||
// icscm_bitfield ESI : 1;
|
||||
// } CxTRB0FD;
|
||||
// struct
|
||||
// {
|
||||
// icscm_bitfield ErrRxOnlyBreak : 1;
|
||||
// icscm_bitfield ErrRxOnlyBreakSync : 1;
|
||||
// icscm_bitfield ID : 11;
|
||||
// icscm_bitfield NETWORKINDEX : 3;//DO NOT CLOBBER THIS
|
||||
// } CxLIN3;
|
||||
// struct
|
||||
// {
|
||||
// uint8_t D8;
|
||||
// uint8_t options : 4;
|
||||
// uint8_t TXMSG : 1;
|
||||
// uint8_t NETWORKINDEX : 3;//DO NOT CLOBBER THIS
|
||||
// } C1xJ1850;
|
||||
// struct
|
||||
// {
|
||||
// uint8_t D8;
|
||||
// uint8_t options : 4;
|
||||
// uint8_t TXMSG : 1;
|
||||
// uint8_t NETWORKINDEX : 3;//DO NOT CLOBBER THIS
|
||||
// } C1xISO;
|
||||
// struct
|
||||
// {
|
||||
// uint8_t D8;
|
||||
// uint8_t options : 4;
|
||||
// uint8_t TXMSG : 1;
|
||||
// uint8_t NETWORKINDEX : 3;//DO NOT CLOBBER THIS
|
||||
// } C1xJ1708;
|
||||
// struct
|
||||
// {
|
||||
// icscm_bitfield FCS_AVAIL : 1;
|
||||
// icscm_bitfield RUNT_FRAME : 1;
|
||||
// icscm_bitfield DISABLE_PADDING : 1;
|
||||
// icscm_bitfield PREEMPTION_ENABLED : 1;
|
||||
// icscm_bitfield MPACKET_TYPE : 4;
|
||||
// icscm_bitfield MPACKET_FRAG_CNT : 2;
|
||||
// icscm_bitfield : 6;
|
||||
// } C1xETH;
|
||||
// struct
|
||||
// {
|
||||
// uint16_t ID : 11;
|
||||
// uint16_t STARTUP : 1;
|
||||
// uint16_t SYNC : 1;
|
||||
// uint16_t NULL_FRAME : 1;
|
||||
// uint16_t PAYLOAD_PREAMBLE : 1;
|
||||
// uint16_t RESERVED_0 : 1;
|
||||
// } C1xFlex;
|
||||
// struct
|
||||
// {
|
||||
// uint8_t daqType;
|
||||
// uint8_t ethDaqRes1;
|
||||
// } C1xETHDAQ;
|
||||
// };
|
||||
// union {
|
||||
// uint16_t CxTRB0EID16;
|
||||
// struct
|
||||
// {
|
||||
// icscm_bitfield EID : 12;
|
||||
// icscm_bitfield TXMSG : 1;
|
||||
// icscm_bitfield TXAborted : 1;
|
||||
// icscm_bitfield TXLostArb : 1;
|
||||
// icscm_bitfield TXError : 1;
|
||||
// } CxTRB0EID;
|
||||
// struct
|
||||
// {
|
||||
// uint8_t LINByte9;
|
||||
// uint8_t ErrTxRxMismatch : 1;
|
||||
// uint8_t TxChkSumEnhanced : 1;
|
||||
// uint8_t TXMaster : 1;
|
||||
// uint8_t TXSlave : 1;
|
||||
// uint8_t ErrRxBreakNot0 : 1;
|
||||
// uint8_t ErrRxBreakTooShort : 1;
|
||||
// uint8_t ErrRxSyncNot55 : 1;
|
||||
// uint8_t ErrRxDataGreater8 : 1;
|
||||
// } CxLIN;
|
||||
// struct
|
||||
// {
|
||||
// uint8_t D9;
|
||||
// uint8_t D10;
|
||||
// } C2xJ1850;
|
||||
// struct
|
||||
// {
|
||||
// uint8_t D9;
|
||||
// uint8_t D10;
|
||||
// } C2xISO;
|
||||
// struct
|
||||
// {
|
||||
// uint8_t D9;
|
||||
// uint8_t D10;
|
||||
// } C2xJ1708;
|
||||
// struct
|
||||
// {
|
||||
// uint16_t txlen : 12;
|
||||
// uint16_t TXMSG : 1;
|
||||
// uint16_t : 3;
|
||||
// } C2xETH;
|
||||
// struct
|
||||
// {
|
||||
// uint16_t HDR_CRC_10 : 1;
|
||||
// uint16_t PAYLOAD_LEN : 7;
|
||||
// uint16_t RESERVED_1 : 4;
|
||||
// uint16_t TXMSG : 1;
|
||||
// uint16_t RESERVED_2 : 3;
|
||||
// } C2xFlex;
|
||||
// };
|
||||
// union {
|
||||
// // For use by CAN
|
||||
// uint16_t CxTRB0DLC16;
|
||||
// struct
|
||||
// {
|
||||
// icscm_bitfield DLC : 4;
|
||||
// icscm_bitfield RB0 : 1;
|
||||
// icscm_bitfield IVRIF : 1;
|
||||
// icscm_bitfield HVEnable : 1;// must be cleared before passing into CAN driver
|
||||
// icscm_bitfield ExtendedNetworkIndexBit : 1;//DO NOT CLOBBER THIS
|
||||
// icscm_bitfield RB1 : 1;
|
||||
// icscm_bitfield RTR : 1;
|
||||
// icscm_bitfield EID : 6;
|
||||
// } CxTRB0DLC;
|
||||
// struct
|
||||
// {
|
||||
// icscm_bitfield len : 4;
|
||||
// icscm_bitfield ExtendedNetworkIndexBit2 : 1;//DO NOT CLOBBER THIS
|
||||
// icscm_bitfield UpdateSlaveOnce : 1;
|
||||
// icscm_bitfield HasUpdatedSlaveOnce : 1;
|
||||
// icscm_bitfield ExtendedNetworkIndexBit : 1;//DO NOT CLOBBER THIS
|
||||
// icscm_bitfield BusRecovered : 1;
|
||||
// icscm_bitfield SyncFerr : 1;//!< We got framing error in our sync byte.
|
||||
// icscm_bitfield MidFerr : 1;//!< We got framing error in our message id.
|
||||
// icscm_bitfield SlaveByteFerr : 1;//!< We got framing error in one of our slave bytes.
|
||||
// icscm_bitfield TxAborted : 1;//!< This transmit was aborted.
|
||||
// icscm_bitfield breakOnly : 1;
|
||||
// icscm_bitfield : 2;
|
||||
// } CxLIN2;
|
||||
// // For use by JVPW
|
||||
// struct
|
||||
// {
|
||||
// icscm_bitfield len : 4;
|
||||
// icscm_bitfield ExtendedNetworkIndexBit2 : 1;//DO NOT CLOBBER THIS
|
||||
// icscm_bitfield just_tx_timestamp : 1;
|
||||
// icscm_bitfield first_seg : 1;
|
||||
// icscm_bitfield ExtendedNetworkIndexBit : 1;// do not clobber ExtendedNetworkIndexBit
|
||||
// icscm_bitfield D11 : 8;
|
||||
// } C3xJ1850;
|
||||
// // For use by the ISO/KEYWORD
|
||||
// struct
|
||||
// {
|
||||
// icscm_bitfield len : 4;
|
||||
// icscm_bitfield ExtendedNetworkIndexBit2 : 1;//DO NOT CLOBBER THIS
|
||||
// icscm_bitfield FRM : 1;
|
||||
// icscm_bitfield INIT : 1;
|
||||
// icscm_bitfield ExtendedNetworkIndexBit : 1;// do not clobber ExtendedNetworkIndexBit
|
||||
// icscm_bitfield D11 : 8;
|
||||
// } C3xISO;
|
||||
// struct
|
||||
// {
|
||||
// icscm_bitfield len : 4;
|
||||
// icscm_bitfield ExtendedNetworkIndexBit2 : 1;//DO NOT CLOBBER THIS
|
||||
// icscm_bitfield FRM : 1;
|
||||
// icscm_bitfield : 1;
|
||||
// icscm_bitfield ExtendedNetworkIndexBit : 1;// do not clobber ExtendedNetworkIndexBit
|
||||
// icscm_bitfield pri : 8;
|
||||
// } C3xJ1708;
|
||||
// struct
|
||||
// {
|
||||
// uint16_t rsvd;
|
||||
// } C3xETH;
|
||||
// struct
|
||||
// {
|
||||
// uint16_t CYCLE : 6;
|
||||
// uint16_t HDR_CRC_9_0 : 10;
|
||||
// } C3xFlex;
|
||||
// };
|
||||
// unsigned char CxTRB0Dall[8];
|
||||
// union {
|
||||
// uint16_t CxTRB0STAT;
|
||||
// uint16_t J1850_TX_ID;
|
||||
// };
|
||||
// union {
|
||||
// struct
|
||||
// {
|
||||
// uint32_t uiTimeStamp10uS;
|
||||
// union {
|
||||
// uint32_t uiTimeStamp10uSMSB;
|
||||
// struct
|
||||
// {
|
||||
// unsigned : 28;
|
||||
// unsigned res_0s : 3;// must be 0!!!
|
||||
// unsigned bIsExtended : 1;// always 1 for CoreMiniMsgExtended.
|
||||
// };
|
||||
// };
|
||||
// };
|
||||
// int64_t uiTimeStampLarge;
|
||||
// uint8_t uiTimeStampBytes[8];
|
||||
// };
|
||||
// };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef __PACKETIZER_H_
|
||||
#define __PACKETIZER_H_
|
||||
|
||||
#include "communication/include/communication.h"
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class Packetizer {
|
||||
public:
|
||||
bool input(const std::vector<uint8_t>& bytes);
|
||||
std::vector<std::shared_ptr<Communication::Packet>> output();
|
||||
|
||||
private:
|
||||
enum class ReadState {
|
||||
SearchForHeader,
|
||||
ParseHeader,
|
||||
ParseLongStylePacketHeader,
|
||||
GetData
|
||||
};
|
||||
ReadState state = ReadState::SearchForHeader;
|
||||
|
||||
int currentIndex = 0;
|
||||
int packetLength = 0;
|
||||
int headerSize = 0;
|
||||
bool checksum = false;
|
||||
bool gotGoodPackets = false; // Tracks whether we've ever gotten a good packet
|
||||
Communication::Packet packet;
|
||||
std::deque<uint8_t> bytes;
|
||||
|
||||
std::vector<std::shared_ptr<Communication::Packet>> processedPackets;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user