Update payload.cpp
copy+paste-error corrected
This commit is contained in:
parent
f5faf31a2a
commit
d6e4ba16e7
468
src/payload.cpp
468
src/payload.cpp
@ -523,474 +523,6 @@ void PayloadConvert::addPM25( float value) {
|
||||
#endif // HAS_SDS011
|
||||
}
|
||||
|
||||
void PayloadConvert::addChars( char * string, int len) {
|
||||
for (int i=0; i < len; i++)
|
||||
addByte(string[i]);
|
||||
}
|
||||
|
||||
buffer[cursor++] = highByte(voltage);
|
||||
buffer[cursor++] = lowByte(voltage);
|
||||
buffer[cursor++] = (byte)((uptime & 0xFF00000000000000) >> 56);
|
||||
buffer[cursor++] = (byte)((uptime & 0x00FF000000000000) >> 48);
|
||||
buffer[cursor++] = (byte)((uptime & 0x0000FF0000000000) >> 40);
|
||||
buffer[cursor++] = (byte)((uptime & 0x000000FF00000000) >> 32);
|
||||
buffer[cursor++] = (byte)((uptime & 0x00000000FF000000) >> 24);
|
||||
buffer[cursor++] = (byte)((uptime & 0x0000000000FF0000) >> 16);
|
||||
buffer[cursor++] = (byte)((uptime & 0x000000000000FF00) >> 8);
|
||||
buffer[cursor++] = (byte)((uptime & 0x00000000000000FF));
|
||||
buffer[cursor++] = (byte)(cputemp);
|
||||
buffer[cursor++] = (byte)((mem & 0xFF000000) >> 24);
|
||||
buffer[cursor++] = (byte)((mem & 0x00FF0000) >> 16);
|
||||
buffer[cursor++] = (byte)((mem & 0x0000FF00) >> 8);
|
||||
buffer[cursor++] = (byte)((mem & 0x000000FF));
|
||||
buffer[cursor++] = (byte)(reset1);
|
||||
buffer[cursor++] = (byte)(reset2);
|
||||
}
|
||||
|
||||
void PayloadConvert::addGPS(gpsStatus_t value) {
|
||||
#if(HAS_GPS)
|
||||
buffer[cursor++] = (byte)((value.latitude & 0xFF000000) >> 24);
|
||||
buffer[cursor++] = (byte)((value.latitude & 0x00FF0000) >> 16);
|
||||
buffer[cursor++] = (byte)((value.latitude & 0x0000FF00) >> 8);
|
||||
buffer[cursor++] = (byte)((value.latitude & 0x000000FF));
|
||||
buffer[cursor++] = (byte)((value.longitude & 0xFF000000) >> 24);
|
||||
buffer[cursor++] = (byte)((value.longitude & 0x00FF0000) >> 16);
|
||||
buffer[cursor++] = (byte)((value.longitude & 0x0000FF00) >> 8);
|
||||
buffer[cursor++] = (byte)((value.longitude & 0x000000FF));
|
||||
#if (!PAYLOAD_OPENSENSEBOX)
|
||||
buffer[cursor++] = value.satellites;
|
||||
buffer[cursor++] = highByte(value.hdop);
|
||||
buffer[cursor++] = lowByte(value.hdop);
|
||||
buffer[cursor++] = highByte(value.altitude);
|
||||
buffer[cursor++] = lowByte(value.altitude);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void PayloadConvert::addSensor(uint8_t buf[]) {
|
||||
#if(HAS_SENSORS)
|
||||
uint8_t length = buf[0];
|
||||
memcpy(buffer, buf + 1, length);
|
||||
cursor += length; // length of buffer
|
||||
#endif
|
||||
}
|
||||
|
||||
void PayloadConvert::addBME(bmeStatus_t value) {
|
||||
#if(HAS_BME)
|
||||
int16_t temperature = (int16_t)(value.temperature); // float -> int
|
||||
uint16_t humidity = (uint16_t)(value.humidity); // float -> int
|
||||
uint16_t pressure = (uint16_t)(value.pressure); // float -> int
|
||||
uint16_t iaq = (uint16_t)(value.iaq); // float -> int
|
||||
buffer[cursor++] = highByte(temperature);
|
||||
buffer[cursor++] = lowByte(temperature);
|
||||
buffer[cursor++] = highByte(pressure);
|
||||
buffer[cursor++] = lowByte(pressure);
|
||||
buffer[cursor++] = highByte(humidity);
|
||||
buffer[cursor++] = lowByte(humidity);
|
||||
buffer[cursor++] = highByte(iaq);
|
||||
buffer[cursor++] = lowByte(iaq);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PayloadConvert::addButton(uint8_t value) {
|
||||
#ifdef HAS_BUTTON
|
||||
buffer[cursor++] = value;
|
||||
#endif
|
||||
}
|
||||
|
||||
void PayloadConvert::addTime(time_t value) {
|
||||
uint32_t time = (uint32_t)value;
|
||||
buffer[cursor++] = (byte)((time & 0xFF000000) >> 24);
|
||||
buffer[cursor++] = (byte)((time & 0x00FF0000) >> 16);
|
||||
buffer[cursor++] = (byte)((time & 0x0000FF00) >> 8);
|
||||
buffer[cursor++] = (byte)((time & 0x000000FF));
|
||||
}
|
||||
|
||||
/* ---------------- packed format with LoRa serialization Encoder ----------
|
||||
*/
|
||||
// derived from
|
||||
// https://github.com/thesolarnomad/lora-serialization/blob/master/src/LoraEncoder.cpp
|
||||
|
||||
#elif (PAYLOAD_ENCODER == 2)
|
||||
|
||||
void PayloadConvert::addByte(uint8_t value) { writeUint8(value); }
|
||||
|
||||
void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) {
|
||||
writeUint16(value);
|
||||
}
|
||||
|
||||
void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) {
|
||||
writeUint8(rssi);
|
||||
writeUint8(msg);
|
||||
}
|
||||
|
||||
void PayloadConvert::addVoltage(uint16_t value) { writeUint16(value); }
|
||||
|
||||
void PayloadConvert::addConfig(configData_t value) {
|
||||
writeUint8(value.loradr);
|
||||
writeUint8(value.txpower);
|
||||
writeUint16(value.rssilimit);
|
||||
writeUint8(value.sendcycle);
|
||||
writeUint8(value.wifichancycle);
|
||||
writeUint8(value.blescantime);
|
||||
writeUint8(value.rgblum);
|
||||
writeBitmap(value.adrmode ? true : false, value.screensaver ? true : false,
|
||||
value.screenon ? true : false, value.countermode ? true : false,
|
||||
value.blescan ? true : false, value.wifiant ? true : false,
|
||||
value.vendorfilter ? true : false,
|
||||
value.monitormode ? true : false);
|
||||
writeBitmap(value.payloadmask && GPS_DATA ? true : false,
|
||||
value.payloadmask && ALARM_DATA ? true : false,
|
||||
value.payloadmask && MEMS_DATA ? true : false,
|
||||
value.payloadmask && COUNT_DATA ? true : false,
|
||||
value.payloadmask && SENSOR1_DATA ? true : false,
|
||||
value.payloadmask && SENSOR2_DATA ? true : false,
|
||||
value.payloadmask && SENSOR3_DATA ? true : false,
|
||||
value.payloadmask && BATT_DATA ? true : false);
|
||||
writeVersion(value.version);
|
||||
}
|
||||
|
||||
void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float cputemp,
|
||||
uint32_t mem, uint8_t reset1, uint8_t reset2) {
|
||||
writeUint16(voltage);
|
||||
writeUptime(uptime);
|
||||
writeUint8((byte)cputemp);
|
||||
writeUint32(mem);
|
||||
writeUint8(reset1);
|
||||
writeUint8(reset2);
|
||||
}
|
||||
|
||||
void PayloadConvert::addGPS(gpsStatus_t value) {
|
||||
#if(HAS_GPS)
|
||||
writeLatLng(value.latitude, value.longitude);
|
||||
#if (!PAYLOAD_OPENSENSEBOX)
|
||||
writeUint8(value.satellites);
|
||||
writeUint16(value.hdop);
|
||||
writeUint16(value.altitude);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void PayloadConvert::addSensor(uint8_t buf[]) {
|
||||
#if(HAS_SENSORS)
|
||||
uint8_t length = buf[0];
|
||||
memcpy(buffer, buf + 1, length);
|
||||
cursor += length; // length of buffer
|
||||
#endif
|
||||
}
|
||||
|
||||
void PayloadConvert::addBME(bmeStatus_t value) {
|
||||
#if(HAS_BME)
|
||||
writeFloat(value.temperature);
|
||||
writePressure(value.pressure);
|
||||
writeUFloat(value.humidity);
|
||||
writeUFloat(value.iaq);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PayloadConvert::addButton(uint8_t value) {
|
||||
#ifdef HAS_BUTTON
|
||||
writeUint8(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PayloadConvert::addTime(time_t value) {
|
||||
uint32_t time = (uint32_t)value;
|
||||
writeUint32(time);
|
||||
}
|
||||
|
||||
void PayloadConvert::uintToBytes(uint64_t value, uint8_t byteSize) {
|
||||
for (uint8_t x = 0; x < byteSize; x++) {
|
||||
byte next = 0;
|
||||
if (sizeof(value) > x) {
|
||||
next = static_cast<byte>((value >> (x * 8)) & 0xFF);
|
||||
}
|
||||
buffer[cursor] = next;
|
||||
++cursor;
|
||||
}
|
||||
}
|
||||
|
||||
void PayloadConvert::writeUptime(uint64_t uptime) {
|
||||
writeUint64(uptime);
|
||||
}
|
||||
|
||||
void PayloadConvert::writeVersion(char *version) {
|
||||
memcpy(buffer + cursor, version, 10);
|
||||
cursor += 10;
|
||||
}
|
||||
|
||||
void PayloadConvert::writeLatLng(double latitude, double longitude) {
|
||||
// Tested to at least work with int32_t, which are processed correctly.
|
||||
writeUint32(latitude);
|
||||
writeUint32(longitude);
|
||||
}
|
||||
|
||||
void PayloadConvert::writeUint64(uint64_t i) { uintToBytes(i, 8); }
|
||||
|
||||
void PayloadConvert::writeUint32(uint32_t i) { uintToBytes(i, 4); }
|
||||
|
||||
void PayloadConvert::writeUint16(uint16_t i) { uintToBytes(i, 2); }
|
||||
|
||||
void PayloadConvert::writeUint8(uint8_t i) { uintToBytes(i, 1); }
|
||||
|
||||
void PayloadConvert::writeUFloat(float value) {
|
||||
writeUint16(value * 100);
|
||||
}
|
||||
|
||||
void PayloadConvert::writePressure(float value) {
|
||||
writeUint16(value * 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses a 16bit two's complement with two decimals, so the range is
|
||||
* -327.68 to +327.67 degrees
|
||||
*/
|
||||
void PayloadConvert::writeFloat(float value) {
|
||||
int16_t t = (int16_t)(value * 100);
|
||||
if (value < 0) {
|
||||
t = ~-t;
|
||||
t = t + 1;
|
||||
}
|
||||
buffer[cursor++] = (byte)((t >> 8) & 0xFF);
|
||||
buffer[cursor++] = (byte)t & 0xFF;
|
||||
}
|
||||
|
||||
void PayloadConvert::writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f,
|
||||
bool g, bool h) {
|
||||
uint8_t bitmap = 0;
|
||||
// LSB first
|
||||
bitmap |= (a & 1) << 7;
|
||||
bitmap |= (b & 1) << 6;
|
||||
bitmap |= (c & 1) << 5;
|
||||
bitmap |= (d & 1) << 4;
|
||||
bitmap |= (e & 1) << 3;
|
||||
bitmap |= (f & 1) << 2;
|
||||
bitmap |= (g & 1) << 1;
|
||||
bitmap |= (h & 1) << 0;
|
||||
writeUint8(bitmap);
|
||||
}
|
||||
|
||||
/* ---------------- Cayenne LPP 2.0 format ---------- */
|
||||
// see specs
|
||||
// http://community.mydevices.com/t/cayenne-lpp-2-0/7510 (LPP 2.0)
|
||||
// https://github.com/myDevicesIoT/cayenne-docs/blob/master/docs/LORA.md
|
||||
// (LPP 1.0) PAYLOAD_ENCODER == 3 -> Dynamic Sensor Payload, using channels ->
|
||||
// FPort 1 PAYLOAD_ENCODER == 4 -> Packed Sensor Payload, not using channels ->
|
||||
// FPort 2
|
||||
|
||||
#elif ((PAYLOAD_ENCODER == 3) || (PAYLOAD_ENCODER == 4))
|
||||
|
||||
void PayloadConvert::addByte(uint8_t value) {
|
||||
/*
|
||||
not implemented
|
||||
*/ }
|
||||
|
||||
void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) {
|
||||
switch (snifftype) {
|
||||
case MAC_SNIFF_WIFI:
|
||||
#if (PAYLOAD_ENCODER == 3)
|
||||
buffer[cursor++] = LPP_COUNT_WIFI_CHANNEL;
|
||||
#endif
|
||||
buffer[cursor++] =
|
||||
LPP_LUMINOSITY; // workaround since cayenne has no data type meter
|
||||
buffer[cursor++] = highByte(value);
|
||||
buffer[cursor++] = lowByte(value);
|
||||
break;
|
||||
case MAC_SNIFF_BLE:
|
||||
#if (PAYLOAD_ENCODER == 3)
|
||||
buffer[cursor++] = LPP_COUNT_BLE_CHANNEL;
|
||||
#endif
|
||||
buffer[cursor++] =
|
||||
LPP_LUMINOSITY; // workaround since cayenne has no data type meter
|
||||
buffer[cursor++] = highByte(value);
|
||||
buffer[cursor++] = lowByte(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) {
|
||||
#if (PAYLOAD_ENCODER == 3)
|
||||
buffer[cursor++] = LPP_ALARM_CHANNEL;
|
||||
#endif
|
||||
buffer[cursor++] = LPP_PRESENCE;
|
||||
buffer[cursor++] = msg;
|
||||
#if (PAYLOAD_ENCODER == 3)
|
||||
buffer[cursor++] = LPP_MSG_CHANNEL;
|
||||
#endif
|
||||
buffer[cursor++] = LPP_ANALOG_INPUT;
|
||||
buffer[cursor++] = rssi;
|
||||
}
|
||||
|
||||
void PayloadConvert::addVoltage(uint16_t value) {
|
||||
uint16_t volt = value / 10;
|
||||
#if (PAYLOAD_ENCODER == 3)
|
||||
buffer[cursor++] = LPP_BATT_CHANNEL;
|
||||
#endif
|
||||
buffer[cursor++] = LPP_ANALOG_INPUT;
|
||||
buffer[cursor++] = highByte(volt);
|
||||
buffer[cursor++] = lowByte(volt);
|
||||
}
|
||||
|
||||
void PayloadConvert::addConfig(configData_t value) {
|
||||
#if (PAYLOAD_ENCODER == 3)
|
||||
buffer[cursor++] = LPP_ADR_CHANNEL;
|
||||
#endif
|
||||
buffer[cursor++] = LPP_DIGITAL_INPUT;
|
||||
buffer[cursor++] = value.adrmode;
|
||||
}
|
||||
|
||||
void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float celsius,
|
||||
uint32_t mem, uint8_t reset1, uint8_t reset2) {
|
||||
uint16_t temp = celsius * 10;
|
||||
uint16_t volt = voltage / 10;
|
||||
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
|
||||
#if (PAYLOAD_ENCODER == 3)
|
||||
buffer[cursor++] = LPP_BATT_CHANNEL;
|
||||
#endif
|
||||
buffer[cursor++] = LPP_ANALOG_INPUT;
|
||||
buffer[cursor++] = highByte(volt);
|
||||
buffer[cursor++] = lowByte(volt);
|
||||
#endif // BAT_MEASURE_ADC
|
||||
|
||||
#if (PAYLOAD_ENCODER == 3)
|
||||
buffer[cursor++] = LPP_TEMPERATURE_CHANNEL;
|
||||
#endif
|
||||
buffer[cursor++] = LPP_TEMPERATURE;
|
||||
buffer[cursor++] = highByte(temp);
|
||||
buffer[cursor++] = lowByte(temp);
|
||||
}
|
||||
|
||||
void PayloadConvert::addGPS(gpsStatus_t value) {
|
||||
#if(HAS_GPS)
|
||||
int32_t lat = value.latitude / 100;
|
||||
int32_t lon = value.longitude / 100;
|
||||
int32_t alt = value.altitude * 100;
|
||||
#if (PAYLOAD_ENCODER == 3)
|
||||
buffer[cursor++] = LPP_GPS_CHANNEL;
|
||||
#endif
|
||||
buffer[cursor++] = LPP_GPS;
|
||||
buffer[cursor++] = (byte)((lat & 0xFF0000) >> 16);
|
||||
buffer[cursor++] = (byte)((lat & 0x00FF00) >> 8);
|
||||
buffer[cursor++] = (byte)((lat & 0x0000FF));
|
||||
buffer[cursor++] = (byte)((lon & 0xFF0000) >> 16);
|
||||
buffer[cursor++] = (byte)((lon & 0x00FF00) >> 8);
|
||||
buffer[cursor++] = (byte)(lon & 0x0000FF);
|
||||
buffer[cursor++] = (byte)((alt & 0xFF0000) >> 16);
|
||||
buffer[cursor++] = (byte)((alt & 0x00FF00) >> 8);
|
||||
buffer[cursor++] = (byte)(alt & 0x0000FF);
|
||||
#endif // HAS_GPS
|
||||
}
|
||||
|
||||
void PayloadConvert::addSensor(uint8_t buf[]) {
|
||||
#if(HAS_SENSORS)
|
||||
// to come
|
||||
/*
|
||||
uint8_t length = buf[0];
|
||||
memcpy(buffer, buf+1, length);
|
||||
cursor += length; // length of buffer
|
||||
*/
|
||||
#endif // HAS_SENSORS
|
||||
}
|
||||
|
||||
void PayloadConvert::addBME(bmeStatus_t value) {
|
||||
#if(HAS_BME)
|
||||
|
||||
// data value conversions to meet cayenne data type definition
|
||||
// 0.1°C per bit => -3276,7 .. +3276,7 °C
|
||||
int16_t temperature = (int16_t)(value.temperature * 10.0);
|
||||
// 0.1 hPa per bit => 0 .. 6553,6 hPa
|
||||
uint16_t pressure = (uint16_t)(value.pressure * 10);
|
||||
// 0.5% per bit => 0 .. 128 %C
|
||||
uint8_t humidity = (uint8_t)(value.humidity * 2.0);
|
||||
int16_t iaq = (int16_t)(value.iaq);
|
||||
|
||||
#if (PAYLOAD_ENCODER == 3)
|
||||
buffer[cursor++] = LPP_TEMPERATURE_CHANNEL;
|
||||
#endif
|
||||
buffer[cursor++] = LPP_TEMPERATURE; // 2 bytes 0.1 °C Signed MSB
|
||||
buffer[cursor++] = highByte(temperature);
|
||||
buffer[cursor++] = lowByte(temperature);
|
||||
#if (PAYLOAD_ENCODER == 3)
|
||||
buffer[cursor++] = LPP_BAROMETER_CHANNEL;
|
||||
#endif
|
||||
buffer[cursor++] = LPP_BAROMETER; // 2 bytes 0.1 hPa Unsigned MSB
|
||||
buffer[cursor++] = highByte(pressure);
|
||||
buffer[cursor++] = lowByte(pressure);
|
||||
#if (PAYLOAD_ENCODER == 3)
|
||||
buffer[cursor++] = LPP_HUMIDITY_CHANNEL;
|
||||
#endif
|
||||
buffer[cursor++] = LPP_HUMIDITY; // 1 byte 0.5 % Unsigned
|
||||
buffer[cursor++] = humidity;
|
||||
#if (PAYLOAD_ENCODER == 3)
|
||||
buffer[cursor++] = LPP_AIR_CHANNEL;
|
||||
#endif
|
||||
buffer[cursor++] = LPP_LUMINOSITY; // 2 bytes, 1.0 unsigned
|
||||
buffer[cursor++] = highByte(iaq);
|
||||
buffer[cursor++] = lowByte(iaq);
|
||||
#endif // HAS_BME
|
||||
}
|
||||
|
||||
void PayloadConvert::addButton(uint8_t value) {
|
||||
#ifdef HAS_BUTTON
|
||||
#if (PAYLOAD_ENCODER == 3)
|
||||
buffer[cursor++] = LPP_BUTTON_CHANNEL;
|
||||
#endif
|
||||
buffer[cursor++] = LPP_DIGITAL_INPUT;
|
||||
buffer[cursor++] = value;
|
||||
#endif // HAS_BUTTON
|
||||
}
|
||||
|
||||
void PayloadConvert::addTime(time_t value) {
|
||||
#if (PAYLOAD_ENCODER == 4)
|
||||
uint32_t t = (uint32_t)value;
|
||||
uint32_t tx_period = (uint32_t)SENDCYCLE * 2;
|
||||
buffer[cursor++] = 0x03; // set config mask to UTCTime + TXPeriod
|
||||
// UTCTime in seconds
|
||||
buffer[cursor++] = (byte)((t & 0xFF000000) >> 24);
|
||||
buffer[cursor++] = (byte)((t & 0x00FF0000) >> 16);
|
||||
buffer[cursor++] = (byte)((t & 0x0000FF00) >> 8);
|
||||
buffer[cursor++] = (byte)((t & 0x000000FF));
|
||||
// TXPeriod in seconds
|
||||
buffer[cursor++] = (byte)((tx_period & 0xFF000000) >> 24);
|
||||
buffer[cursor++] = (byte)((tx_period & 0x00FF0000) >> 16);
|
||||
buffer[cursor++] = (byte)((tx_period & 0x0000FF00) >> 8);
|
||||
buffer[cursor++] = (byte)((tx_period & 0x000000FF));
|
||||
#endif
|
||||
}
|
||||
#endif // PAYLOAD_ENCODER
|
||||
|
||||
void PayloadConvert::addPM10( float value) {
|
||||
#if (HAS_SDS011)
|
||||
#if (PAYLOAD_ENCODER == 1) // plain
|
||||
char tempBuffer[10+1];
|
||||
sprintf( tempBuffer, ",%5.1f", value);
|
||||
addChars(tempBuffer, strlen(tempBuffer));
|
||||
#elif (PAYLOAD_ENCODER == 2 ) // packed
|
||||
writeUint16( (uint16_t) (value*10) );
|
||||
#elif (PAYLOAD_ENCODER == 3 ) // Cayenne LPP dynamic
|
||||
// TODO
|
||||
#elif (PAYLOAD_ENCODER == 4 ) // Cayenne LPP packed
|
||||
// TODO
|
||||
#endif
|
||||
#endif // HAS_SDS011
|
||||
}
|
||||
|
||||
void PayloadConvert::addPM25( float value) {
|
||||
#if (HAS_SDS011)
|
||||
#if (PAYLOAD_ENCODER == 1) // plain
|
||||
char tempBuffer[10+1];
|
||||
sprintf( tempBuffer, ",%5.1f", value);
|
||||
addChars(tempBuffer, strlen(tempBuffer));
|
||||
#elif (PAYLOAD_ENCODER == 2 ) // packed
|
||||
writeUint16( (uint16_t) (value*10) );
|
||||
#elif (PAYLOAD_ENCODER == 3 ) // Cayenne LPP dynamic
|
||||
// TODO
|
||||
#elif (PAYLOAD_ENCODER == 4 ) // Cayenne LPP packed
|
||||
// TODO
|
||||
#endif
|
||||
#endif // HAS_SDS011
|
||||
}
|
||||
|
||||
void PayloadConvert::addChars( char * string, int len) {
|
||||
for (int i=0; i < len; i++)
|
||||
addByte(string[i]);
|
||||
|
Loading…
Reference in New Issue
Block a user