some fixes before DE 2025
This commit is contained in:
parent
f966f2df14
commit
58e9a97804
@ -70,7 +70,4 @@ typedef enum batteryType_e
|
||||
extern const char *BatteryString[];
|
||||
extern const size_t BatteryString_Elements;
|
||||
|
||||
#define STARTUP_DELAY 2500
|
||||
#define SHUTDOWN_DELAY_MS 2500
|
||||
|
||||
#endif
|
||||
|
@ -1,9 +1,9 @@
|
||||
/**
|
||||
* @file debugger.h
|
||||
*
|
||||
* @brief Header file for debugging functions and status in the DE-Timer application.
|
||||
* @brief Header file for debugging functions and status in the ChainLube application.
|
||||
*
|
||||
* This file declares functions and status definitions for debugging purposes in the DE-Timer project.
|
||||
* This file declares functions and status definitions for debugging purposes in the ChainLube project.
|
||||
* It includes functions to print system information, WiFi information, format EEPROM data,
|
||||
* handle debug messages, and manage the status of different debug ports.
|
||||
*
|
||||
@ -16,22 +16,6 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "webui.h"
|
||||
const char PROGMEM helpCmd[] = "sysinfo - System Info\n"
|
||||
"reboot - System Reboot\n"
|
||||
"netinfo - WiFi Info\n"
|
||||
"formatPDS - Format Persistence EEPROM Data\n"
|
||||
"formatCFG - Format Configuration EEPROM Data\n"
|
||||
"checkEE - Check EEPROM with checksum\n"
|
||||
"dumpEE1k - dump the first 1kb of EEPROM to Serial\n"
|
||||
"dumpEE - dump the whole EPPROM to Serial\n"
|
||||
"killEE - kill the first 1024 byte of EEPROM\n"
|
||||
"zeroEE - zero the first 1024 byte of EEPROM\n"
|
||||
"resetPageEE - Reset the PersistenceData Page\n"
|
||||
"dumpCFG - print Config struct\n"
|
||||
"dumpPDS - print PersistanceStruct\n"
|
||||
"saveEE - save EE-Data\n"
|
||||
"showdtc - Show all DTCs\n"
|
||||
"dumpGlobals - print globals\n";
|
||||
|
||||
typedef enum DebugStatus_e
|
||||
{
|
||||
@ -52,8 +36,14 @@ const char sDebugPorts[dbg_cntElements][7] = {
|
||||
|
||||
extern DebugStatus_t DebuggerStatus[dbg_cntElements];
|
||||
|
||||
enum LogLevel
|
||||
{
|
||||
LOG_INFO,
|
||||
LOG_WARN,
|
||||
LOG_ERROR
|
||||
};
|
||||
|
||||
void initDebugger();
|
||||
void pushCANDebug(uint32_t id, uint8_t dlc, uint8_t *data);
|
||||
void Debug_pushMessage(const char *format, ...);
|
||||
void SetDebugportStatus(DebugPorts_t port, DebugStatus_t status);
|
||||
void Debug_Process();
|
||||
|
@ -18,6 +18,7 @@ typedef struct Globals_s
|
||||
int loadvoltage_mV = 0;
|
||||
int battery_level = 0;
|
||||
bool timer_disabled = false;
|
||||
bool toggle_wifi = false;
|
||||
} Globals_t;
|
||||
|
||||
extern Globals_t globals; /**< Global variable struct */
|
||||
|
@ -12,22 +12,15 @@
|
||||
*/
|
||||
|
||||
#include "debugger.h"
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include "utilities.h"
|
||||
#include <pgmspace.h>
|
||||
|
||||
DebugStatus_t DebuggerStatus[dbg_cntElements];
|
||||
|
||||
// Funktionszeiger
|
||||
typedef void (*CommandFunction)();
|
||||
|
||||
// Struktur zur Zuordnung von Commands zu Funktionen
|
||||
struct CommandMapping
|
||||
{
|
||||
const char *command;
|
||||
CommandFunction function;
|
||||
};
|
||||
|
||||
void processCmdDebug(const char *command);
|
||||
void processCmdDebug(String command);
|
||||
void Debug_formatCFG();
|
||||
void Debug_formatPersistence();
|
||||
void Debug_printSystemInfo();
|
||||
@ -39,93 +32,9 @@ void Debug_ShowDTCs();
|
||||
void Debug_dumpGlobals();
|
||||
void Debug_printHelp();
|
||||
void Debug_Reboot();
|
||||
void Debug_setBatteryType(int batterytype);
|
||||
const char *uint32_to_binary_string(uint32_t num);
|
||||
|
||||
// Adapter-Functions for Debug-Commands
|
||||
void adapterCheckEEPOM() { Debug_CheckEEPOM(false); }
|
||||
void adapterCheckEEPOMFix() { Debug_CheckEEPOM(true); }
|
||||
void adapterDumpEEPROM1k() { dumpEEPROM(0, 1024); }
|
||||
void adapterDumpEEPROMAll() { dumpEEPROM(0, EEPROM_SIZE_BYTES); }
|
||||
void adapterKillEEPROM() { writeSequentialToEEPROM(0, 1024); }
|
||||
void adapterZeroEEPROM() { writeZeroToEEPROM(0, 1024); }
|
||||
void adapterResetPageEEPROM() { MovePersistencePage_EEPROM(true); }
|
||||
void adapterSaveEEPROM() { globals.requestEEAction = EE_ALL_SAVE; }
|
||||
void adapterSetDebugPort() { SetDebugportStatus(dbg_Serial, enabled); }
|
||||
void adapterCritDTC() { MaintainDTC(DTC_FAKE_DTC_CRIT, true, millis()); }
|
||||
void adapterWarnDTC() { MaintainDTC(DTC_FAKE_DTC_WARN, true, millis()); }
|
||||
void adapterInfoDTC() { MaintainDTC(DTC_FAKE_DTC_INFO, true, millis()); }
|
||||
void adapterNotifyError() { Websocket_PushNotification("Debug Error Notification", error); }
|
||||
void adapterNotifyWarning() { Websocket_PushNotification("Debug Warning Notification", warning); }
|
||||
void adapterNotifySuccess() { Websocket_PushNotification("Debug Success Notification", success); }
|
||||
void adapterNotifyInfo() { Websocket_PushNotification("Debug Info Notification", info); }
|
||||
|
||||
// Definition der Command-Mapping-Tabelle
|
||||
const CommandMapping commandMappings[] = {
|
||||
{"help", Debug_printHelp},
|
||||
{"reboot", Debug_Reboot},
|
||||
{"sysinfo", Debug_printSystemInfo},
|
||||
{"netinfo", Debug_printWifiInfo},
|
||||
{"formatCFG", Debug_formatCFG},
|
||||
{"formatPDS", Debug_formatPersistence},
|
||||
{"checkEE", adapterCheckEEPOM},
|
||||
{"checkEEfix", adapterCheckEEPOMFix},
|
||||
{"dumpEE1k", adapterDumpEEPROM1k},
|
||||
{"dumpEE", adapterDumpEEPROMAll},
|
||||
{"killEE", adapterKillEEPROM},
|
||||
{"zeroEE", adapterZeroEEPROM},
|
||||
{"resetPageEE", adapterResetPageEEPROM},
|
||||
{"dumpCFG", Debug_dumpConfig},
|
||||
{"dumpPDS", Debug_dumpPersistance},
|
||||
{"saveEE", adapterSaveEEPROM},
|
||||
{"dumpGlobals", Debug_dumpGlobals},
|
||||
{"sdbg", adapterSetDebugPort},
|
||||
{"dtc_show", Debug_ShowDTCs},
|
||||
{"dtc_clear", ClearAllDTC},
|
||||
{"dtc_crit", adapterCritDTC},
|
||||
{"dtc_warn", adapterWarnDTC},
|
||||
{"dtc_info", adapterInfoDTC},
|
||||
{"notify_error", adapterNotifyError},
|
||||
{"notify_warning", adapterNotifyWarning},
|
||||
{"notify_success", adapterNotifySuccess},
|
||||
{"notify_info", adapterNotifyInfo},
|
||||
};
|
||||
|
||||
const size_t NUM_COMMANDS = sizeof(commandMappings) / sizeof(commandMappings[0]);
|
||||
|
||||
const char helpText[][64] PROGMEM = {
|
||||
"help - Print this help text",
|
||||
"sysinfo - System Info",
|
||||
"reboot - System Reboot",
|
||||
"netinfo - WiFi Info",
|
||||
"formatPDS - Format Persistence EEPROM Data",
|
||||
"formatCFG - Format Configuration EEPROM Data",
|
||||
"checkEE - Check EEPROM with checksum",
|
||||
"checkEEfix - Check and fix EEPROM with checksum",
|
||||
"dumpEE1k - Dump the first 1kb of EEPROM to Serial",
|
||||
"dumpEE - Dump the whole EEPROM to Serial",
|
||||
"killEE - Kill the first 1024 bytes of EEPROM",
|
||||
"zeroEE - Zero the first 1024 bytes of EEPROM",
|
||||
"resetPageEE - Reset the PersistenceData Page",
|
||||
"dumpCFG - Print Config struct",
|
||||
"dumpPDS - Print PersistenceStruct",
|
||||
"saveEE - Save EE-Data",
|
||||
"dumpGlobals - Print globals",
|
||||
"sdbg - Set debug port status",
|
||||
"dtc_show - Show all DTCs",
|
||||
"dtc_clear - Clear all DTCs",
|
||||
"dtc_crit - Maintain critical DTC",
|
||||
"dtc_warn - Maintain warning DTC",
|
||||
"dtc_info - Maintain info DTC",
|
||||
"notify_error - Send error notification",
|
||||
"notify_warning - Send warning notification",
|
||||
"notify_success - Send success notification",
|
||||
"notify_info - Send info notification"};
|
||||
|
||||
const size_t NUM_HELP_LINES = sizeof(helpText) / sizeof(helpText[0]);
|
||||
|
||||
// Überprüfen, ob die Anzahl der Commands und Hilfetext-Zeilen übereinstimmen
|
||||
static_assert(NUM_COMMANDS == NUM_HELP_LINES, "Number of commands and help text lines do not match!");
|
||||
|
||||
/**
|
||||
* @brief Initializes the debugger by setting the initial status for different debug ports.
|
||||
* Serial debug output is turned off.
|
||||
@ -180,7 +89,7 @@ void Debug_Process()
|
||||
InputProcessed = CMD_ABORT;
|
||||
break;
|
||||
|
||||
case 0x21 ... 0x7E: // it's a real letter or sign and not some control-chars
|
||||
case 0x20 ... 0x7E: // it's a real letter or sign and not some control-chars
|
||||
inputBuffer[inputCnt] = inputChar;
|
||||
inputCnt++;
|
||||
Serial.write(inputChar);
|
||||
@ -191,10 +100,10 @@ void Debug_Process()
|
||||
}
|
||||
|
||||
// Check for input buffer overflow
|
||||
if (inputCnt > sizeof(inputBuffer))
|
||||
if (inputCnt >= sizeof(inputBuffer) - 1)
|
||||
{
|
||||
inputBuffer[sizeof(inputBuffer) - 1] = '\0';
|
||||
inputCnt = 0;
|
||||
inputBuffer[sizeof(inputBuffer) - 1] = 0; // terminate the String
|
||||
InputProcessed = CMD_OVERFLOW;
|
||||
}
|
||||
}
|
||||
@ -207,7 +116,7 @@ void Debug_Process()
|
||||
break;
|
||||
|
||||
case CMD_COMPLETE:
|
||||
processCmdDebug(inputBuffer);
|
||||
processCmdDebug(String(inputBuffer));
|
||||
break;
|
||||
|
||||
case CMD_OVERFLOW:
|
||||
@ -244,6 +153,27 @@ void SetDebugportStatus(DebugPorts_t port, DebugStatus_t status)
|
||||
Debug_pushMessage("Enabled DebugPort %s\n", sDebugPorts[port]);
|
||||
}
|
||||
|
||||
void Debug_log(LogLevel level, const char *format, ...)
|
||||
{
|
||||
if ((DebuggerStatus[dbg_Serial] == enabled) || (DebuggerStatus[dbg_Webui] == enabled))
|
||||
{
|
||||
char buff[128];
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
vsnprintf(buff, sizeof(buff), format, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (DebuggerStatus[dbg_Serial] == enabled)
|
||||
{
|
||||
Serial.print(buff);
|
||||
}
|
||||
if (DebuggerStatus[dbg_Webui] == enabled)
|
||||
{
|
||||
Websocket_PushLiveDebug(String(buff));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pushes a formatted debug message to the enabled debug ports (Serial or WebUI).
|
||||
*
|
||||
@ -277,27 +207,134 @@ void Debug_pushMessage(const char *format, ...)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Processes a debug command and performs corresponding actions.
|
||||
*
|
||||
* @param command The debug command to be processed.
|
||||
*/
|
||||
void processCmdDebug(const char *command)
|
||||
// === splitArgs Helper ===
|
||||
std::vector<String> splitArgs(const String &input)
|
||||
{
|
||||
bool commandFound = false;
|
||||
for (size_t i = 0; i < NUM_COMMANDS; ++i)
|
||||
std::vector<String> tokens;
|
||||
int start = 0, end = 0;
|
||||
while ((end = input.indexOf(' ', start)) != -1)
|
||||
{
|
||||
if (strcmp(command, commandMappings[i].command) == 0)
|
||||
{
|
||||
commandMappings[i].function();
|
||||
commandFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!commandFound)
|
||||
{
|
||||
Debug_pushMessage("unknown Command\n");
|
||||
tokens.push_back(input.substring(start, end));
|
||||
start = end + 1;
|
||||
}
|
||||
if (start < input.length())
|
||||
tokens.push_back(input.substring(start));
|
||||
return tokens;
|
||||
}
|
||||
|
||||
// === getArg helper ===
|
||||
String getArg(const std::vector<String> &args, size_t index, const String &defaultVal = "")
|
||||
{
|
||||
if (index < args.size())
|
||||
return args[index];
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
// === Command Handler Map ===
|
||||
typedef std::function<void(const String &args)> DebugCmdHandler;
|
||||
|
||||
static const std::map<String, DebugCmdHandler> &getCmdMap()
|
||||
{
|
||||
static const std::map<String, DebugCmdHandler> cmdMap = {
|
||||
{"help", [](const String &)
|
||||
{
|
||||
Debug_log(LOG_INFO, "Available commands:\n");
|
||||
for (const auto &entry : getCmdMap())
|
||||
Debug_log(LOG_INFO, " - %s\n", entry.first.c_str());
|
||||
}},
|
||||
{"sysinfo", [](const String &)
|
||||
{ Debug_printSystemInfo(); }},
|
||||
{"netinfo", [](const String &)
|
||||
{ Debug_printWifiInfo(); }},
|
||||
{"formatCFG", [](const String &)
|
||||
{ Debug_formatCFG(); }},
|
||||
{"formatPDS", [](const String &)
|
||||
{ Debug_formatPersistence(); }},
|
||||
{"checkEE", [](const String &)
|
||||
{ Debug_CheckEEPOM(false); }},
|
||||
{"checkEEfix", [](const String &)
|
||||
{ Debug_CheckEEPOM(true); }},
|
||||
{"dumpEE1k", [](const String &)
|
||||
{ dumpEEPROM(0, 1024); }},
|
||||
{"dumpEE", [](const String &args)
|
||||
{
|
||||
int start = 0, len = EEPROM_SIZE_BYTES;
|
||||
auto tokens = splitArgs(args);
|
||||
if (tokens.size() >= 2)
|
||||
{
|
||||
start = tokens[0].toInt();
|
||||
len = tokens[1].toInt();
|
||||
}
|
||||
dumpEEPROM(start, len);
|
||||
}},
|
||||
{"resetPageEE", [](const String &)
|
||||
{ MovePersistencePage_EEPROM(true); }},
|
||||
{"dumpCFG", [](const String &)
|
||||
{ Debug_dumpConfig(); }},
|
||||
{"dumpPDS", [](const String &)
|
||||
{ Debug_dumpPersistance(); }},
|
||||
{"saveEE", [](const String &)
|
||||
{ globals.requestEEAction = EE_ALL_SAVE; }},
|
||||
{"dumpGlobals", [](const String &)
|
||||
{ Debug_dumpGlobals(); }},
|
||||
{"sdbg", [](const String &)
|
||||
{ SetDebugportStatus(dbg_Serial, enabled); }},
|
||||
{"dtc_show", [](const String &)
|
||||
{ Debug_ShowDTCs(); }},
|
||||
{"dtc_clear", [](const String &)
|
||||
{ ClearAllDTC(); }},
|
||||
{"dtc_crit", [](const String &)
|
||||
{ MaintainDTC(DTC_FAKE_DTC_CRIT, true, millis()); }},
|
||||
{"dtc_warn", [](const String &)
|
||||
{ MaintainDTC(DTC_FAKE_DTC_WARN, true, millis()); }},
|
||||
{"dtc_info", [](const String &)
|
||||
{ MaintainDTC(DTC_FAKE_DTC_INFO, true, millis()); }},
|
||||
{"notify_error", [](const String &)
|
||||
{ Websocket_PushNotification("Debug Error Notification", error); }},
|
||||
{"notify_warning", [](const String &)
|
||||
{ Websocket_PushNotification("Debug Warning Notification", warning); }},
|
||||
{"notify_success", [](const String &)
|
||||
{ Websocket_PushNotification("Debug Success Notification", success); }},
|
||||
{"notify_info", [](const String &)
|
||||
{ Websocket_PushNotification("Debug Info Notification", info); }},
|
||||
{"reboot", [](const String &)
|
||||
{ Debug_Reboot(); }},
|
||||
{"toggle_wifi", [](const String &)
|
||||
{ globals.toggle_wifi = true; }},
|
||||
{"dtc_add", [](const String &args)
|
||||
{
|
||||
auto tokens = splitArgs(args);
|
||||
if (!tokens.empty())
|
||||
{
|
||||
int code = tokens[0].toInt();
|
||||
MaintainDTC((DTCNum_t)code, true, millis());
|
||||
}
|
||||
}},
|
||||
{"set_battery", [](const String &args)
|
||||
{
|
||||
auto tokens = splitArgs(args);
|
||||
if (!tokens.empty())
|
||||
{
|
||||
int batteryType = tokens[0].toInt();
|
||||
Debug_setBatteryType(batteryType);
|
||||
}
|
||||
}}};
|
||||
return cmdMap;
|
||||
}
|
||||
|
||||
void processCmdDebug(String input)
|
||||
{
|
||||
input.trim();
|
||||
int splitIndex = input.indexOf(' ');
|
||||
String command = splitIndex == -1 ? input : input.substring(0, splitIndex);
|
||||
String args = splitIndex == -1 ? "" : input.substring(splitIndex + 1);
|
||||
|
||||
auto &cmdMap = getCmdMap();
|
||||
auto it = cmdMap.find(command);
|
||||
if (it != cmdMap.end())
|
||||
it->second(args);
|
||||
else
|
||||
Debug_log(LOG_WARN, "Unknown command: '%s'\n", command.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -409,11 +446,31 @@ void Debug_dumpPersistance()
|
||||
void Debug_printWifiInfo()
|
||||
{
|
||||
char buffer[33];
|
||||
GenerateDeviceSpecificPassword(QUOTE(WIFI_PASS_SEED), buffer, sizeof(buffer));
|
||||
GenerateDeviceSpecificPassword(QUOTE(WIFI_PASS_SEED), buffer, sizeof(buffer));
|
||||
Debug_pushMessage("IP Adress: %s\n", WiFi.localIP().toString().c_str());
|
||||
Debug_pushMessage("WiFi AP Pw: %s\n", buffer);
|
||||
}
|
||||
|
||||
void Debug_setBatteryType(int batterytype)
|
||||
{
|
||||
|
||||
switch (batterytype)
|
||||
{
|
||||
case 2:
|
||||
ConfigData.batteryType = BATTERY_LIPO_2S;
|
||||
break;
|
||||
case 3:
|
||||
ConfigData.batteryType = BATTERY_LIPO_3S;
|
||||
break;
|
||||
default:
|
||||
ConfigData.batteryType = BATTERY_UNDEFINED;
|
||||
break;
|
||||
}
|
||||
|
||||
Debug_pushMessage("Set BatteryType to : %d\n", batterytype);
|
||||
globals.requestEEAction = EE_CFG_SAVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks the EEPROM data integrity by calculating and comparing checksums.
|
||||
* Prints the result to the debug output.
|
||||
@ -471,7 +528,7 @@ void Debug_ShowDTCs()
|
||||
char buff_active[9];
|
||||
|
||||
// Header for the DTC display
|
||||
Debug_pushMessage("\n timestamp | DTC-Nr. | status | debugval\n");
|
||||
Debug_pushMessage("\n timestamp | DTC-Nr. | status | severity\n");
|
||||
|
||||
// Iterate through DTCStorage and display each entry
|
||||
for (uint32_t i = 0; i < MAX_DTC_STORAGE; i++)
|
||||
@ -495,25 +552,11 @@ void Debug_ShowDTCs()
|
||||
strcpy(buff_active, "none");
|
||||
|
||||
// Display DTC information
|
||||
Debug_pushMessage("%s %7d %8s %8d\n", buff_timestamp, DTCStorage[i].Number, buff_active, DTCStorage[i].debugVal);
|
||||
Debug_pushMessage("%s %7d %8s %8d\n", buff_timestamp, DTCStorage[i].Number, buff_active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prints the help information stored in PROGMEM.
|
||||
*/
|
||||
void Debug_printHelp()
|
||||
{
|
||||
char buffer[64];
|
||||
for (size_t i = 0; i < NUM_HELP_LINES; ++i)
|
||||
{
|
||||
strncpy_P(buffer, helpText[i], sizeof(buffer));
|
||||
buffer[sizeof(buffer) - 1] = '\0'; // Sicherstellen, dass der String nullterminiert ist
|
||||
Serial.println(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initiates a system reboot by setting the system status to shutdown.
|
||||
*
|
||||
|
@ -7,4 +7,5 @@ void initGlobals()
|
||||
globals.systemStatus = sysStat_Startup;
|
||||
globals.requestEEAction = EE_IDLE;
|
||||
globals.systemStatustxt[0] = 0;
|
||||
globals.toggle_wifi = false;
|
||||
}
|
||||
|
@ -247,6 +247,13 @@ void loop()
|
||||
DTC_Process();
|
||||
Debug_Process();
|
||||
|
||||
if (globals.toggle_wifi == true)
|
||||
{
|
||||
|
||||
globals.toggle_wifi = false;
|
||||
toggleWiFiAP();
|
||||
}
|
||||
|
||||
#if defined(FEATURE_ENABLE_LORA) || defined(FEATURE_ENABLE_UARTLORA)
|
||||
LoRa_Process();
|
||||
tmrStatusSender.update();
|
||||
@ -269,10 +276,11 @@ void loop()
|
||||
lastStatus = globals.systemStatus;
|
||||
}
|
||||
// Transition to Normal status after startup delay
|
||||
if (millis() > STARTUP_DELAY)
|
||||
if (millis() > STARTUP_DELAY_MS)
|
||||
{
|
||||
globals.systemStatus = sysStat_Normal;
|
||||
globals.resumeStatus = sysStat_Normal;
|
||||
Debug_pushMessage("sysstat = Normal");
|
||||
}
|
||||
break;
|
||||
|
||||
@ -713,7 +721,7 @@ void ProcessKeyCombos(bool *btnState)
|
||||
else if (keyCount_Fac2 == 4 && keyCount_Fac3 == 0)
|
||||
{
|
||||
Debug_pushMessage("KeyCombo: Reset Timer\n");
|
||||
if (globals.systemStatus == sysStat_Startup)
|
||||
if (millis() < STARTUP_DELAY_MS)
|
||||
{
|
||||
OverrideDisplay(5000, "RST ", " ", " ");
|
||||
PersistenceData.faction_1_timer = 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user