mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2024-12-02 10:24:11 +01:00
Merge remote-tracking branch 'raid/Brian-PipXtreme-V2' into Brian-PipXtreme-V2
This commit is contained in:
commit
f9c8687645
@ -40,6 +40,9 @@
|
||||
#include <uavtalk_priv.h>
|
||||
#include <pios_rfm22b.h>
|
||||
#include <ecc.h>
|
||||
#if defined(PIOS_INCLUDE_FLASH_EEPROM)
|
||||
#include <pios_eeprom.h>
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
@ -54,7 +57,7 @@
|
||||
#define REQ_TIMEOUT_MS 10
|
||||
#define STATS_UPDATE_PERIOD_MS 2000
|
||||
#define RADIOSTATS_UPDATE_PERIOD_MS 1000
|
||||
#define MAX_LOST_CONTACT_TIME 10
|
||||
#define MAX_LOST_CONTACT_TIME 4
|
||||
#define PACKET_QUEUE_SIZE 10
|
||||
#define MAX_PORT_DELAY 200
|
||||
#define EV_PACKET_RECEIVED 0x20
|
||||
@ -182,6 +185,11 @@ static int32_t RadioComBridgeInitialize(void)
|
||||
// Initialize the UAVObjects that we use
|
||||
GCSReceiverInitialize();
|
||||
PipXStatusInitialize();
|
||||
ObjectPersistenceInitialize();
|
||||
|
||||
// Get the settings.
|
||||
PipXSettingsData pipxSettings;
|
||||
PipXSettingsGet(&pipxSettings);
|
||||
|
||||
// TODO: Get from settings object
|
||||
data->com_port = PIOS_COM_BRIDGE_COM;
|
||||
@ -196,7 +204,8 @@ static int32_t RadioComBridgeInitialize(void)
|
||||
data->objEventQueue = xQueueCreate(PACKET_QUEUE_SIZE, sizeof(UAVObjEvent));
|
||||
|
||||
// Initialize the destination ID
|
||||
data->destination_id = 0xffffffff;
|
||||
data->destination_id = pipxSettings.PairID ? pipxSettings.PairID : 0xffffffff;
|
||||
DEBUG_PRINTF(2, "PairID: %x\n\r", data->destination_id);
|
||||
|
||||
// Initialize the statistics.
|
||||
data->radioTxErrors = 0;
|
||||
@ -296,7 +305,8 @@ static void comUAVTalkTask(void *parameters)
|
||||
|
||||
// Keep reading until we receive a completed packet.
|
||||
UAVTalkRxState state = UAVTalkProcessInputStreamQuiet(data->inUAVTalkCon, rx_byte);
|
||||
UAVTalkInputProcessor *iproc = &(((UAVTalkConnectionData*)(data->inUAVTalkCon))->iproc);
|
||||
UAVTalkConnectionData *connection = (UAVTalkConnectionData*)(data->inUAVTalkCon);
|
||||
UAVTalkInputProcessor *iproc = &(connection->iproc);
|
||||
if (state == UAVTALK_STATE_COMPLETE) {
|
||||
|
||||
// Is this a local UAVObject?
|
||||
@ -305,19 +315,65 @@ static void comUAVTalkTask(void *parameters)
|
||||
// We treat the ObjectPersistance object differently
|
||||
if(iproc->objId == OBJECTPERSISTENCE_OBJID)
|
||||
{
|
||||
// Queue the packet for transmission.
|
||||
xQueueSend(data->sendPacketQueue, &p, MAX_PORT_DELAY);
|
||||
p = NULL;
|
||||
// Unpack object, if the instance does not exist it will be created!
|
||||
UAVObjUnpack(iproc->obj, iproc->instId, connection->rxBuffer);
|
||||
|
||||
// Get the ObjectPersistance object.
|
||||
ObjectPersistenceData obj_per;
|
||||
ObjectPersistenceGet(&obj_per);
|
||||
|
||||
// Is this concerning or setting object?
|
||||
if (obj_per.ObjectID == PIPXSETTINGS_OBJID)
|
||||
{
|
||||
UAVObjEvent ev;
|
||||
ev.obj = iproc->obj;
|
||||
ev.instId = 0;
|
||||
ev.event = EV_SEND_ACK;
|
||||
|
||||
// Is this a save, load, or delete?
|
||||
switch (obj_per.Operation)
|
||||
{
|
||||
case OBJECTPERSISTENCE_OPERATION_LOAD:
|
||||
DEBUG_PRINTF(2, "Load\n\r");
|
||||
break;
|
||||
case OBJECTPERSISTENCE_OPERATION_SAVE:
|
||||
#if defined(PIOS_INCLUDE_FLASH_EEPROM)
|
||||
{
|
||||
// Save the settings.
|
||||
PipXSettingsData pipxSettings;
|
||||
PipXSettingsGet(&pipxSettings);
|
||||
if (PIOS_EEPROM_Save((uint8_t*)&pipxSettings, sizeof(PipXSettingsData)) != 0)
|
||||
ev.event = EV_SEND_NACK;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case OBJECTPERSISTENCE_OPERATION_DELETE:
|
||||
DEBUG_PRINTF(2, "Delete\n\r");
|
||||
break;
|
||||
default:
|
||||
DEBUG_PRINTF(2, "Other\n\r");
|
||||
break;
|
||||
}
|
||||
|
||||
// Queue up the ACK/NACK
|
||||
xQueueSend(data->objEventQueue, &ev, MAX_PORT_DELAY);
|
||||
}
|
||||
else {
|
||||
// Otherwise, queue the packet for transmission.
|
||||
xQueueSend(data->sendPacketQueue, &p, MAX_PORT_DELAY);
|
||||
p = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UAVObjEvent ev;
|
||||
ev.obj = iproc->obj;
|
||||
ev.instId = 0;
|
||||
switch (iproc->type)
|
||||
{
|
||||
case UAVTALK_TYPE_OBJ:
|
||||
// Unpack object, if the instance does not exist it will be created!
|
||||
UAVObjUnpack(iproc->obj, iproc->instId, p->data);
|
||||
UAVObjUnpack(iproc->obj, iproc->instId, connection->rxBuffer);
|
||||
break;
|
||||
case UAVTALK_TYPE_OBJ_REQ:
|
||||
// Queue up an object send request.
|
||||
@ -327,7 +383,7 @@ static void comUAVTalkTask(void *parameters)
|
||||
case UAVTALK_TYPE_OBJ_ACK:
|
||||
// Queue up an ACK
|
||||
ev.event = EV_SEND_ACK;
|
||||
xQueueSend(data->objEventQueue, &ev, MAX_PORT_DELAY);
|
||||
UAVObjUnpack(iproc->obj, iproc->instId, connection->rxBuffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -582,6 +638,7 @@ static void radioStatusTask(void *parameters)
|
||||
{
|
||||
pipxStatus.PairIDs[i] = data->pairStats[i].pairID;
|
||||
pipxStatus.PairSignalStrengths[i] = data->pairStats[i].rssi;
|
||||
data->pairStats[i].lastContact++;
|
||||
}
|
||||
|
||||
// Update the object
|
||||
@ -663,19 +720,21 @@ static void receiveData(uint8_t *buf, uint8_t len)
|
||||
static void StatusHandler(PHPacketHandle status)
|
||||
{
|
||||
uint32_t id = status->header.source_id;
|
||||
|
||||
bool found = false;
|
||||
// Have we seen this device recently?
|
||||
uint8_t id_idx = 0;
|
||||
for ( ; id_idx < PIPXSTATUS_PAIRIDS_NUMELEM; ++id_idx)
|
||||
if(data->pairStats[id_idx].pairID == id)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// If we have seen it, update the RSSI and reset the last contact couter
|
||||
if(id_idx < PIPXSTATUS_PAIRIDS_NUMELEM)
|
||||
if(found)
|
||||
{
|
||||
data->pairStats[id_idx].rssi = status->header.rssi;
|
||||
data->pairStats[id_idx].lastContact = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove any contacts that we haven't seen for a while.
|
||||
@ -690,19 +749,22 @@ static void StatusHandler(PHPacketHandle status)
|
||||
}
|
||||
|
||||
// If we haven't seen it, find a slot to put it in.
|
||||
uint8_t min_idx = 0;
|
||||
int8_t min_rssi = data->pairStats[0].rssi;
|
||||
for (id_idx = 1; id_idx < PIPXSTATUS_PAIRIDS_NUMELEM; ++id_idx)
|
||||
if (!found)
|
||||
{
|
||||
if(data->pairStats[id_idx].rssi < min_rssi)
|
||||
uint8_t min_idx = 0;
|
||||
int8_t min_rssi = data->pairStats[0].rssi;
|
||||
for (id_idx = 1; id_idx < PIPXSTATUS_PAIRIDS_NUMELEM; ++id_idx)
|
||||
{
|
||||
min_rssi = data->pairStats[id_idx].rssi;
|
||||
min_idx = id_idx;
|
||||
if(data->pairStats[id_idx].rssi < min_rssi)
|
||||
{
|
||||
min_rssi = data->pairStats[id_idx].rssi;
|
||||
min_idx = id_idx;
|
||||
}
|
||||
}
|
||||
data->pairStats[min_idx].pairID = id;
|
||||
data->pairStats[min_idx].rssi = status->header.rssi;
|
||||
data->pairStats[min_idx].lastContact = 0;
|
||||
}
|
||||
data->pairStats[min_idx].pairID = id;
|
||||
data->pairStats[min_idx].rssi = status->header.rssi;
|
||||
data->pairStats[min_idx].lastContact = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,7 +152,7 @@ enum pios_rfm22b_dev_magic {
|
||||
|
||||
struct pios_rfm22b_dev {
|
||||
enum pios_rfm22b_dev_magic magic;
|
||||
const struct pios_rfm22b_cfg *cfg;
|
||||
struct pios_rfm22b_cfg cfg;
|
||||
|
||||
uint32_t deviceID;
|
||||
|
||||
@ -448,7 +448,7 @@ int32_t PIOS_RFM22B_Init(uint32_t *rfm22b_id, const struct pios_rfm22b_cfg *cfg)
|
||||
return(-1);
|
||||
|
||||
// Bind the configuration to the device instance
|
||||
rfm22b_dev->cfg = cfg;
|
||||
rfm22b_dev->cfg = *cfg;
|
||||
|
||||
*rfm22b_id = (uint32_t)rfm22b_dev;
|
||||
|
||||
@ -853,7 +853,7 @@ void rfm22_setNominalCarrierFrequency(uint32_t frequency_hz)
|
||||
// *******
|
||||
|
||||
#if defined(RFM22_DEBUG)
|
||||
DEBUG_PRINTF(2, "rf setFreq: %u\n\r", carrier_frequency_hz);
|
||||
//DEBUG_PRINTF(2, "rf setFreq: %u\n\r", carrier_frequency_hz);
|
||||
// DEBUG_PRINTF(2, "rf setFreq frequency_step_size: %0.2f\n\r", frequency_step_size);
|
||||
#endif
|
||||
|
||||
@ -912,13 +912,6 @@ void rfm22_setDatarate(uint32_t datarate_bps, bool data_whitening)
|
||||
|
||||
rf_bandwidth_used = rx_bandwidth[lookup_index];
|
||||
|
||||
// ********************************
|
||||
|
||||
#if defined(RFM22_DEBUG)
|
||||
uint32_t frequency_deviation = freq_deviation[lookup_index]; // Hz
|
||||
uint32_t modulation_bandwidth = datarate_bps + (2 * frequency_deviation);
|
||||
#endif
|
||||
|
||||
// rfm22_if_filter_bandwidth
|
||||
rfm22_write(0x1C, reg_1C[lookup_index]);
|
||||
|
||||
@ -1028,11 +1021,15 @@ void rfm22_setDatarate(uint32_t datarate_bps, bool data_whitening)
|
||||
// ********************************
|
||||
|
||||
#if defined(RFM22_DEBUG)
|
||||
/*
|
||||
DEBUG_PRINTF(2, "rf datarate_bps: %d\n\r", datarate_bps);
|
||||
DEBUG_PRINTF(2, "rf frequency_deviation: %d\n\r", frequency_deviation);
|
||||
uint32_t frequency_deviation = freq_deviation[lookup_index]; // Hz
|
||||
uint32_t modulation_bandwidth = datarate_bps + (2 * frequency_deviation);
|
||||
DEBUG_PRINTF(2, "rf modulation_bandwidth: %u\n\r", modulation_bandwidth);
|
||||
DEBUG_PRINTF(2, "rf_rx_bandwidth[%u]: %u\n\r", lookup_index, rx_bandwidth[lookup_index]);
|
||||
DEBUG_PRINTF(2, "rf est rx sensitivity[%u]: %ddBm\n\r", lookup_index, est_rx_sens_dBm[lookup_index]);
|
||||
*/
|
||||
#endif
|
||||
|
||||
// *******
|
||||
|
@ -140,6 +140,7 @@ ifndef TESTAPP
|
||||
SRC += $(OPUAVSYNTHDIR)/gcsreceiver.c
|
||||
SRC += $(OPUAVSYNTHDIR)/pipxstatus.c
|
||||
SRC += $(OPUAVSYNTHDIR)/pipxsettings.c
|
||||
SRC += $(OPUAVSYNTHDIR)/objectpersistence.c
|
||||
|
||||
endif
|
||||
|
||||
|
@ -214,12 +214,72 @@ void PIOS_Board_Init(void) {
|
||||
PIOS_Assert(0);
|
||||
}
|
||||
}
|
||||
PIOS_COM_SendString(PIOS_COM_DEBUG, "Hello DEBUG\n\r");
|
||||
PIOS_COM_SendString(PIOS_COM_FLEXI, "Hello Flexi\n\r");
|
||||
PIOS_COM_SendString(PIOS_COM_TELEM_SERIAL, "Hello Telem Serial\n\r");
|
||||
PIOS_COM_SendString(PIOS_COM_VCP_USB, "Hello VCP\n\r");
|
||||
|
||||
#if defined(PIOS_INCLUDE_RFM22B)
|
||||
struct pios_rfm22b_cfg pios_rfm22b_cfg = {
|
||||
.frequencyHz = 434000000,
|
||||
.minFrequencyHz = 434000000 - 2000000,
|
||||
.maxFrequencyHz = 434000000 + 2000000,
|
||||
.RFXtalCap = 0x7f,
|
||||
.maxRFBandwidth = 128000,
|
||||
.maxTxPower = RFM22_tx_pwr_txpow_7, // +20dBm .. 100mW
|
||||
};
|
||||
|
||||
/* Retrieve hardware settings. */
|
||||
pios_rfm22b_cfg.frequencyHz = pipxSettings.Frequency;
|
||||
pios_rfm22b_cfg.RFXtalCap = pipxSettings.FrequencyCalibration;
|
||||
switch (pipxSettings.RFSpeed)
|
||||
{
|
||||
case PIPXSETTINGS_RFSPEED_2400:
|
||||
pios_rfm22b_cfg.maxRFBandwidth = 2000;
|
||||
break;
|
||||
case PIPXSETTINGS_RFSPEED_4800:
|
||||
pios_rfm22b_cfg.maxRFBandwidth = 4000;
|
||||
break;
|
||||
case PIPXSETTINGS_RFSPEED_9600:
|
||||
pios_rfm22b_cfg.maxRFBandwidth = 9600;
|
||||
break;
|
||||
case PIPXSETTINGS_RFSPEED_19200:
|
||||
pios_rfm22b_cfg.maxRFBandwidth = 19200;
|
||||
break;
|
||||
case PIPXSETTINGS_RFSPEED_38400:
|
||||
pios_rfm22b_cfg.maxRFBandwidth = 32000;
|
||||
break;
|
||||
case PIPXSETTINGS_RFSPEED_57600:
|
||||
pios_rfm22b_cfg.maxRFBandwidth = 64000;
|
||||
break;
|
||||
case PIPXSETTINGS_RFSPEED_115200:
|
||||
pios_rfm22b_cfg.maxRFBandwidth = 128000;
|
||||
break;
|
||||
}
|
||||
switch (pipxSettings.RFSpeed)
|
||||
{
|
||||
case PIPXSETTINGS_MAXRFPOWER_125:
|
||||
pios_rfm22b_cfg.maxTxPower = RFM22_tx_pwr_txpow_0;
|
||||
break;
|
||||
case PIPXSETTINGS_MAXRFPOWER_16:
|
||||
pios_rfm22b_cfg.maxTxPower = RFM22_tx_pwr_txpow_1;
|
||||
break;
|
||||
case PIPXSETTINGS_MAXRFPOWER_316:
|
||||
pios_rfm22b_cfg.maxTxPower = RFM22_tx_pwr_txpow_2;
|
||||
break;
|
||||
case PIPXSETTINGS_MAXRFPOWER_63:
|
||||
pios_rfm22b_cfg.maxTxPower = RFM22_tx_pwr_txpow_3;
|
||||
break;
|
||||
case PIPXSETTINGS_MAXRFPOWER_126:
|
||||
pios_rfm22b_cfg.maxTxPower = RFM22_tx_pwr_txpow_4;
|
||||
break;
|
||||
case PIPXSETTINGS_MAXRFPOWER_25:
|
||||
pios_rfm22b_cfg.maxTxPower = RFM22_tx_pwr_txpow_5;
|
||||
break;
|
||||
case PIPXSETTINGS_MAXRFPOWER_50:
|
||||
pios_rfm22b_cfg.maxTxPower = RFM22_tx_pwr_txpow_6;
|
||||
break;
|
||||
case PIPXSETTINGS_MAXRFPOWER_100:
|
||||
pios_rfm22b_cfg.maxTxPower = RFM22_tx_pwr_txpow_7;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Initalize the RFM22B radio COM device. */
|
||||
{
|
||||
if (PIOS_RFM22B_Init(&pios_rfm22b_id, &pios_rfm22b_cfg)) {
|
||||
|
@ -494,16 +494,6 @@ const struct pios_eeprom_cfg pios_eeprom_cfg = {
|
||||
#if defined(PIOS_INCLUDE_RFM22B)
|
||||
#include <pios_rfm22b_priv.h>
|
||||
|
||||
const struct pios_rfm22b_cfg pios_rfm22b_cfg = {
|
||||
.frequencyHz = 434000000,
|
||||
.minFrequencyHz = 434000000 - 2000000,
|
||||
.maxFrequencyHz = 434000000 + 2000000,
|
||||
.RFXtalCap = 0x7f,
|
||||
.maxRFBandwidth = 128000,
|
||||
//.maxTxPower = RFM22_tx_pwr_txpow_0, // +1dBm ... 1.25mW
|
||||
.maxTxPower = RFM22_tx_pwr_txpow_7, // +20dBm .. 100mW
|
||||
};
|
||||
|
||||
#endif /* PIOS_INCLUDE_RFM22B */
|
||||
|
||||
#if defined(PIOS_INCLUDE_PACKET_HANDLER)
|
||||
|
37
ground/openpilotgcs/src/plugins/config/configpipxtremewidget.cpp
Normal file → Executable file
37
ground/openpilotgcs/src/plugins/config/configpipxtremewidget.cpp
Normal file → Executable file
@ -46,6 +46,8 @@ ConfigPipXtremeWidget::ConfigPipXtremeWidget(QWidget *parent) : ConfigTaskWidget
|
||||
}
|
||||
|
||||
addApplySaveButtons(m_pipx->Apply, m_pipx->Save);
|
||||
connect(m_pipx->Apply, SIGNAL(clicked()), this, SLOT(applySettings()));
|
||||
connect(m_pipx->Save, SIGNAL(clicked()), this, SLOT(saveSettings()));
|
||||
}
|
||||
|
||||
ConfigPipXtremeWidget::~ConfigPipXtremeWidget()
|
||||
@ -61,6 +63,19 @@ void ConfigPipXtremeWidget::applySettings()
|
||||
{
|
||||
PipXSettings *pipxSettings = PipXSettings::GetInstance(getObjectManager());
|
||||
PipXSettings::DataFields pipxSettingsData = pipxSettings->getData();
|
||||
|
||||
// Get the pair ID.
|
||||
quint32 pairID = 0;
|
||||
bool okay;
|
||||
if (m_pipx->PairSelect1->isChecked())
|
||||
pairID = m_pipx->PairID1->text().toUInt(&okay, 16);
|
||||
else if (m_pipx->PairSelect2->isChecked())
|
||||
pairID = m_pipx->PairID2->text().toUInt(&okay, 16);
|
||||
else if (m_pipx->PairSelect3->isChecked())
|
||||
pairID = m_pipx->PairID3->text().toUInt(&okay, 16);
|
||||
else if (m_pipx->PairSelect4->isChecked())
|
||||
pairID = m_pipx->PairID4->text().toUInt(&okay, 16);
|
||||
pipxSettingsData.PairID = pairID;
|
||||
pipxSettings->setData(pipxSettingsData);
|
||||
}
|
||||
|
||||
@ -76,13 +91,33 @@ void ConfigPipXtremeWidget::saveSettings()
|
||||
*/
|
||||
void ConfigPipXtremeWidget::updateStatus(UAVObject *object) {
|
||||
|
||||
// Get the settings object.
|
||||
PipXSettings *pipxSettings = PipXSettings::GetInstance(getObjectManager());
|
||||
PipXSettings::DataFields pipxSettingsData = pipxSettings->getData();
|
||||
|
||||
// Update the detected devices.
|
||||
UAVObjectField* pairIdField = object->getField("PairIDs");
|
||||
if (pairIdField) {
|
||||
m_pipx->PairID1->setText(QString::number(pairIdField->getValue(0).toUInt(), 16).toUpper());
|
||||
quint32 pairid1 = pairIdField->getValue(0).toUInt();
|
||||
m_pipx->PairID1->setText(QString::number(pairid1, 16).toUpper());
|
||||
m_pipx->PairID1->setEnabled(false);
|
||||
m_pipx->PairSelect1->setChecked(pipxSettingsData.PairID == pairid1);
|
||||
m_pipx->PairSelect1->setEnabled(pairid1);
|
||||
quint32 pairid2 = pairIdField->getValue(1).toUInt();
|
||||
m_pipx->PairID2->setText(QString::number(pairIdField->getValue(1).toUInt(), 16).toUpper());
|
||||
m_pipx->PairID2->setEnabled(false);
|
||||
m_pipx->PairSelect2->setChecked(pipxSettingsData.PairID == pairid2);
|
||||
m_pipx->PairSelect2->setEnabled(pairid2);
|
||||
quint32 pairid3 = pairIdField->getValue(2).toUInt();
|
||||
m_pipx->PairID3->setText(QString::number(pairIdField->getValue(2).toUInt(), 16).toUpper());
|
||||
m_pipx->PairID3->setEnabled(false);
|
||||
m_pipx->PairSelect3->setChecked(pipxSettingsData.PairID == pairid3);
|
||||
m_pipx->PairSelect3->setEnabled(pairid3);
|
||||
quint32 pairid4 = pairIdField->getValue(3).toUInt();
|
||||
m_pipx->PairID4->setText(QString::number(pairIdField->getValue(3).toUInt(), 16).toUpper());
|
||||
m_pipx->PairID4->setEnabled(false);
|
||||
m_pipx->PairSelect4->setChecked(pipxSettingsData.PairID == pairid4);
|
||||
m_pipx->PairSelect4->setEnabled(pairid4);
|
||||
} else {
|
||||
qDebug() << "PipXtremeGadgetWidget: Count not read PairID field.";
|
||||
}
|
||||
|
@ -16,9 +16,9 @@
|
||||
<field name="Frequency" units="" type="uint32" elements="1" defaultvalue="434000000"/>
|
||||
<field name="AESKey" units="" type="uint8" elements="32" defaultvalue="0123456789abcdef0123456789abcdef"/>
|
||||
|
||||
<access gcs="readwrite" flight="readonly"/>
|
||||
<access gcs="readwrite" flight="readwrite"/>
|
||||
<telemetrygcs acked="true" updatemode="onchange" period="0"/>
|
||||
<telemetryflight acked="false" updatemode="manual" period="0"/>
|
||||
<telemetryflight acked="true" updatemode="onchange" period="0"/>
|
||||
<logging updatemode="manual" period="0"/>
|
||||
</object>
|
||||
</xml>
|
||||
|
Loading…
Reference in New Issue
Block a user