diff --git a/software/pc_client/src/console.c b/software/pc_client/src/console.c index 06ad3f2..ffd8ea9 100644 --- a/software/pc_client/src/console.c +++ b/software/pc_client/src/console.c @@ -9,17 +9,39 @@ #include "console.h" -char *device_config_file = "./flapconfig.json"; +char *device_config_file; int fd; -// command handlers +/* +* Command parser for wsserver. +* Parses incoming json commands and executes corresponding functions. +*/ -// dump config/ all devices + +/* +* command: dm_dump +* description: dump all device details as json to websocket +* +* request format: { "command": "dm_dump" } +* response format: { ... all device details ... } +* +* @param req: json request object +* @param res: json response object +*/ void cmd_dm_dump(json_object *req, json_object *res) { devicemgr_printDetailsAll(res); } -// describe single device +/* +* command: dm_describe +* description: describe device with id as json to websocket +* +* request format: { "command": "dm_describe", "id": } +* response format: { ... device details ... } +* +* @param req: json request object +* @param res: json response object +*/ void cmd_dm_describe(json_object *req, json_object *res) { json_object *id; @@ -34,7 +56,16 @@ void cmd_dm_describe(json_object *req, json_object *res) } } -// register new device +/* +* command: dm_register +* description: register new device at address with x,y position +* +* request format: { "command": "dm_register", "address":
, "x": , "y": } +* response format: { "id": } +* +* @param req: json request object +* @param res: json response object +*/ void cmd_dm_register(json_object *req, json_object *res) { json_object *jaddress, *jx, *jy; @@ -65,7 +96,16 @@ void cmd_dm_register(json_object *req, json_object *res) } } -// refresh all devices +/* +* command: dm_refresh +* description: refresh all devices and update their status +* +* request format: { "command": "dm_refresh" } +* response format: { "devices_online": } +* +* @param req: json request object +* @param res: json response object +*/ void cmd_dm_refresh(json_object *req, json_object *res) { int devices_online = devicemgr_refresh(); @@ -73,7 +113,16 @@ void cmd_dm_refresh(json_object *req, json_object *res) } -// remove device +/* +* command: dm_remove +* description: remove device with id from device manager +* +* request format: { "command": "dm_remove", "id": } +* response format: { "ack": true } +* +* @param req: json request object +* @param res: json response object +*/ void cmd_dm_remove(json_object *req, json_object *res) { json_object *jid; @@ -91,14 +140,32 @@ void cmd_dm_remove(json_object *req, json_object *res) } } -// save all devices +/* +* command: dm_save +* description: save all devices to config file +* +* request format: { "command": "dm_save" } +* response format: { "ack": true } +* +* @param req: json request object +* @param res: json response object +*/ void cmd_dm_save(json_object *req, json_object *res) { devicemgr_save(device_config_file); json_object_object_add(res, "ack", json_object_new_boolean(true)); } -// load all devices +/* +* command: dm_load +* description: load all devices from config file +* +* request format: { "command": "dm_load" } +* response format: { "ack": true } +* +* @param req: json request object +* @param res: json response object +*/ void cmd_dm_load(json_object *req, json_object *res) { devicemgr_load(device_config_file); @@ -106,7 +173,16 @@ void cmd_dm_load(json_object *req, json_object *res) } -// print string on display +/* +* command: dm_print +* description: print text to display at position (x,y) +* +* request format: { "command": "dm_print", "x": , "y": , "string": } +* response format: { "ack": true } +* +* @param req: json request object +* @param res: json response object +*/ void cmd_dm_print(json_object *req, json_object *res) { json_object *jx = json_object_object_get(req, "x"); @@ -137,7 +213,16 @@ void cmd_dm_print(json_object *req, json_object *res) } } -// set flap on display +/* +* command: dm_print_single +* description: print single flap to display at position (x,y) +* +* request format: { "command": "dm_print_single", "x": , "y": , "flap": } +* response format: { "ack": true } +* +* @param req: json request object +* @param res: json response object +*/ void cmd_dm_print_single(json_object *req, json_object *res) { json_object *jx = json_object_object_get(req, "x"); @@ -169,16 +254,33 @@ void cmd_dm_print_single(json_object *req, json_object *res) } } -// clear display +/* +* command: dm_clear +* description: clear all displays (set to flap 0) +* +* request format: { "command": "dm_clear" } +* response format: { "ack": true } +* +* @param req: json request object +* @param res: json response object +*/ void cmd_dm_clear(json_object *req, json_object *res) { devicemgr_clearscreen(); json_object_object_add(res, "ack", json_object_new_boolean(true)); - } -// ping device +/* +* command: dr_ping +* description: ping device at address +* +* request format: { "command": "dr_ping", "address":
} +* response format: { "success": true/false } +* +* @param req: json request object +* @param res: json response object +*/ void cmd_dr_ping(json_object *req, json_object *res) { json_object *jaddr = json_object_object_get(req, "address"); @@ -200,7 +302,16 @@ void cmd_dr_ping(json_object *req, json_object *res) } } -// set device address +/* +* command: dr_setaddress +* description: set new address for device +* +* request format: { "command": "dr_setaddress", "address": , "newaddress": } +* response format: { "success": true/false } +* +* @param req: json request object +* @param res: json response object +*/ void cmd_dr_setaddress(json_object *req, json_object *res) { json_object *jaddr = json_object_object_get(req, "address"); @@ -228,6 +339,16 @@ void cmd_dr_setaddress(json_object *req, json_object *res) } } +/* +* command: dr_setcalibration +* description: set calibration value for device +* +* request format: { "command": "dr_setcalibration", "address":
, "calibration": } +* response format: { "success": true/false } +* +* @param req: json request object +* @param res: json response object +*/ void cmd_dr_setcalibration(json_object *req, json_object *res) { json_object *jaddr = json_object_object_get(req, "address"); @@ -255,6 +376,16 @@ void cmd_dr_setcalibration(json_object *req, json_object *res) } } +/* +* command: dr_reset +* description: reset device at address +* +* request format: { "command": "dr_reset", "address":
} +* response format: { "ack": true } +* +* @param req: json request object +* @param res: json response object +*/ void cmd_dr_reset(json_object *req, json_object *res) { json_object *jaddr = json_object_object_get(req, "address"); @@ -270,6 +401,16 @@ void cmd_dr_reset(json_object *req, json_object *res) } } +/* +* command: dr_display +* description: set display flap at address +* +* request format: { "command": "dr_display", "address":
, "flap": , "full": } +* response format: { "ack": true } +* +* @param req: json request object +* @param res: json response object +*/ void cmd_dr_display(json_object *req, json_object *res) { json_object *jaddr = json_object_object_get(req, "address"); @@ -303,6 +444,16 @@ void cmd_dr_display(json_object *req, json_object *res) } } +/* +* command: dr_power +* description: set motor power state at address +* +* request format: { "command": "dr_power", "address":
, "power": } +* response format: { "ack": true } +* +* @param req: json request object +* @param res: json response object +*/ void cmd_dr_power(json_object *req, json_object *res) { json_object *jaddr = json_object_object_get(req, "address"); @@ -332,7 +483,13 @@ void cmd_dr_power(json_object *req, json_object *res) } -// command parser +/* +* Command parser for wsserver. +* Parses incoming json commands and executes corresponding functions. +* +* @param req: json request object +* @return json response object +*/ json_object *parse_command(json_object *req) { json_object *commandObj; @@ -425,12 +582,16 @@ json_object *parse_command(json_object *req) return NULL; } - -void start_console(int _fd) +/* +* Start console with webserver and device manager +* +* @param _fd: rs485 file descriptor +* @param configFile: path to device manager config file +*/ +void start_console(int _fd, char *configFile) { - fd = _fd; - // init device manager - devicemgr_init(fd); - // start server - start_webserver(&parse_command); + device_config_file = configFile; // set config file path + fd = _fd; // set rs485 file descriptor + devicemgr_init(fd); // init device manager + start_webserver(&parse_command); // start webserver with command parser } \ No newline at end of file diff --git a/software/pc_client/src/devicemgr.c b/software/pc_client/src/devicemgr.c index c3a3285..4282f9a 100644 --- a/software/pc_client/src/devicemgr.c +++ b/software/pc_client/src/devicemgr.c @@ -17,57 +17,64 @@ enum SFDEVICE_STATE { - UNALLOCATED, - NEW, - OFFLINE, - ONLINE, - FAILED, - REMOVED + UNALLOCATED, // device slot not allocated + NEW, // device slot allocated, but is not yet refreshed + OFFLINE, // device is allocated, but not reachable + ONLINE, // device is online and reachable + FAILED, // device is online, but in fail-safe mode + REMOVED // device has been removed and can be reallocated }; enum SFDEVICE_POWER { - DISABLED, - ENABLED, - UNKNOWN + DISABLED, // motor power disabled + ENABLED, // motor power enabled + UNKNOWN // power state unknown (device offline) }; struct SFDEVICE { - int pos_x; - int pos_y; - u_int16_t address; - u_int16_t calibration; - int rs485_descriptor; - double reg_voltage; - u_int32_t reg_counter; - u_int8_t reg_status; - u_int8_t current_flap; - enum SFDEVICE_STATE deviceState; - enum SFDEVICE_POWER powerState; + int pos_x; // position in matrix + int pos_y; // position in matrix + u_int16_t address; // device address + u_int16_t calibration; // calibration offset value + int rs485_descriptor; // rs485 file descriptor + double reg_voltage; // last read voltage + u_int32_t reg_counter; // last rotation counter + u_int8_t reg_status; // last status register + u_int8_t current_flap; // current flap position + enum SFDEVICE_STATE deviceState; // device state + enum SFDEVICE_POWER powerState; // power state }; enum { - SFDEVICE_MAXDEV = 128, - SFDEVICE_MAX_X = 20, - SFDEVICE_MAX_Y = 4, - JSON_MAX_LINE_LEN = 256 + SFDEVICE_MAXDEV = 128, // maximum number of devices supported + SFDEVICE_MAX_X = 20, // maximum x size of device matrix + SFDEVICE_MAX_Y = 4, // maximum y size of device matrix + JSON_MAX_LINE_LEN = 256 // maximum length of a line in json file }; -// next free slot to register device -int nextFreeSlot = -1; -int deviceMap[SFDEVICE_MAX_X][SFDEVICE_MAX_Y]; -int deviceFd; -struct SFDEVICE devices[SFDEVICE_MAXDEV]; +int nextFreeSlot = -1; // next free slot in device array +int deviceMap[SFDEVICE_MAX_X][SFDEVICE_MAX_Y]; // device map matrix +int deviceFd; // rs485 file descriptor +struct SFDEVICE devices[SFDEVICE_MAXDEV]; // device array + +// symbol table for flap characters const char *symbols[45] = {" ", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "Ä", "Ö", "Ü", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ".", "-", "?", "!"}; +/* +* Initialize device manager. +* Clear device map and set all devices as unallocated +* +* @param fd rs485 file descriptor +*/ void devicemgr_init(int fd) { - deviceFd = fd; + deviceFd = fd; // store rs485 file descriptor // reserve memory buffer for (int y = 0; y < SFDEVICE_MAX_Y; y++) { @@ -79,10 +86,15 @@ void devicemgr_init(int fd) for (int ix = 0; ix < SFDEVICE_MAXDEV; ix++) { devices[ix].address = 0; // Adress 0 is only used for new units. should never be used for active unit - devices[ix].deviceState = UNALLOCATED; + devices[ix].deviceState = UNALLOCATED; // mark all devices as unallocated } } - +/* +* Read status from device and store it in device struct +* Returns 0 on success, -1 on read error, -2 if device not defined +* +* @param device_id ID of device to read +*/ int devicemgr_readStatus(int device_id) { if (devices[device_id].address > 0) @@ -92,22 +104,22 @@ int devicemgr_readStatus(int device_id) u_int8_t _status = sfbus_read_status(devices[device_id].rs485_descriptor, devices[device_id].address, &_voltage, &_counter); - if (_status == 0xFF) + if (_status == 0xFF) // error reading status { devices[device_id].powerState = UNKNOWN; devices[device_id].deviceState = OFFLINE; return -1; } - devices[device_id].reg_voltage = _voltage; + devices[device_id].reg_voltage = _voltage; // store read values devices[device_id].reg_counter = _counter; devices[device_id].reg_status = _status; devices[device_id].powerState = ~((devices[device_id].reg_status >> 4)) & 0x01; devices[device_id].deviceState = ONLINE; - if ((((devices[device_id].reg_status) >> 5) & 0x01) > 0) + if ((((devices[device_id].reg_status) >> 5) & 0x01) > 0) // fail safe active { devices[device_id].deviceState = FAILED; } - return 0; + return 0; // success } else { @@ -115,6 +127,12 @@ int devicemgr_readStatus(int device_id) } } +/* +* Read calibration data from device and store it in device struct +* Returns 0 on success, -1 on read error, -2 if device not online +* +* @param device_id ID of device to read +*/ int devicemgr_readCalib(int device_id) { if (devices[device_id].deviceState == ONLINE) @@ -139,6 +157,11 @@ int devicemgr_readCalib(int device_id) } } +/* +* Generate json object with device map +* +* @return json object with device map +*/ json_object *devicemgr_printMap() { json_object *rows_array = json_object_new_array(); @@ -154,6 +177,12 @@ json_object *devicemgr_printMap() return rows_array; } +/* +* Generate json object with device details +* +* @param device_id ID of device to print +* @param root json object to add details to +*/ void devicemgr_printDetails(int device_id, json_object *root) { // generate json object with status @@ -172,7 +201,7 @@ void devicemgr_printDetails(int device_id, json_object *root) json_object_object_add(status, "rotations", json_object_new_int(devices[device_id].reg_counter)); json_object_object_add(status, "power", json_object_new_boolean(devices[device_id].powerState)); json_object_object_add(status, "raw", json_object_new_uint64(devices[device_id].reg_status)); - switch (devices[device_id].deviceState) + switch (devices[device_id].deviceState) // device state { case ONLINE: json_object_object_add(status, "device", json_object_new_string("ONLINE")); @@ -193,7 +222,7 @@ void devicemgr_printDetails(int device_id, json_object *root) json_object_object_add(status, "device", json_object_new_string("UNALLOCATED")); break; } - json_object *status_flags = json_object_new_object(); + json_object *status_flags = json_object_new_object(); // status flags json_object_object_add(status_flags, "errorTooBig", json_object_new_boolean(((devices[device_id].reg_status) >> 0) & 0x01)); @@ -219,6 +248,11 @@ void devicemgr_printDetails(int device_id, json_object *root) json_object_object_add(root, "status", status); } +/* +* Generate json object with details for all valid devices +* +* @param root json object to add details to +*/ void devicemgr_printDetailsAll(json_object *root) { json_object_object_add(root, "devices_all", json_object_new_int(nextFreeSlot + 1)); @@ -243,6 +277,12 @@ void devicemgr_printDetailsAll(json_object *root) json_object_object_add(root, "devices_online", json_object_new_int(devices_online)); } +/* +* Set single device to flap character +* +* @param id ID of device to set +* @param flap character to set +*/ void setSingle(int id, char flap) { // first convert char to flap id @@ -260,12 +300,25 @@ void setSingle(int id, char flap) } } -void setSingleRaw(int id, int flap) +/* +* Set single device to raw flap ID +* +* @param id ID of device to set +* @param flap flap ID to set +*/ +void devicemgr_setSingleRaw(int id, int flap) { sfbus_display_full(devices[id].rs485_descriptor, devices[id].address, flap); devices[id].current_flap = flap; } +/* +* Print text to device matrix starting at position (x,y) +* +* @param text text to print +* @param x x position to start printing +* @param y y position to start printing +*/ void devicemgr_printText(const char *text, int x, int y) { for (int i = 0; i < strlen(text); i++) @@ -280,16 +333,25 @@ void devicemgr_printText(const char *text, int x, int y) } } +/* +* Print char to device matrix at position (x,y) +* +* @param flap flap ID to print +* @param x x position to print +* @param y y position to print +*/ void devicemgr_printFlap(int flap, int x, int y) { int this_id = deviceMap[x][y]; if (this_id >= 0) { - setSingleRaw(this_id, flap); + devicemgr_setSingleRaw(this_id, flap); } } -// clears complete screen +/* +* Clear all devices in matrix (set to flap 0) +*/ void devicemgr_clearscreen() { for (int ix = 0; ix < SFDEVICE_MAXDEV; ix++) @@ -298,12 +360,22 @@ void devicemgr_clearscreen() { if (devices[ix].current_flap != 0) { - setSingleRaw(ix, 0); + devicemgr_setSingleRaw(ix, 0); } } } } +/* +* Register new device in device manager and add it to device map +* +* @param rs485_descriptor rs485 file descriptor +* @param address device address +* @param x x position in device matrix +* @param y y position in device matrix +* @param nid optional device ID to use (set to -1 to auto assign) +* @return device ID assigned +*/ int devicemgr_register(int rs485_descriptor, u_int16_t address, int x, int y, int nid) { if (nid < 0) @@ -337,7 +409,11 @@ int devicemgr_register(int rs485_descriptor, u_int16_t address, int x, int y, in return nid; } -// refreshes status of all devices +/* +* Refresh all devices and update their status +* +* @return number of online devices +*/ int devicemgr_refresh() { int devices_online = 0; @@ -355,7 +431,11 @@ int devicemgr_refresh() return devices_online; } -// remove devices from system +/* +* Remove device from device manager +* +* @param id ID of device to remove +*/ int devicemgr_remove(int id) { devices[nextFreeSlot].deviceState = REMOVED; @@ -364,6 +444,12 @@ int devicemgr_remove(int id) return 0; } +/* +* Save device manager configuration to json file +* +* @param file path to json file +* @return 0 on success +*/ int devicemgr_save(char *file) { json_object *root = json_object_new_object(); @@ -388,8 +474,16 @@ int devicemgr_save(char *file) fptr = fopen(file, "w"); fwrite(data, sizeof(char), strlen(data), fptr); fclose(fptr); + return 0; } +/* +* Load device manager configuration from json file and parse contents +* Verify all required keys are present and refresh device status +* +* @param file path to json file +* @return 0 on success, -1 on error +*/ int devicemgr_load(char *file) { FILE *fptr; @@ -465,6 +559,13 @@ int devicemgr_load(char *file) } } +/* +* Load single device from json object and register it +* Verify all required keys are present +* +* @param device_obj json object with device data +* @return 0 on success, -1 on error +*/ int devicemgr_load_single(json_object *device_obj) { json_object *jid = json_object_object_get(device_obj, "id"); diff --git a/software/pc_client/src/logging/logger.c b/software/pc_client/src/logging/logger.c index 3123a9a..e39d8a7 100644 --- a/software/pc_client/src/logging/logger.c +++ b/software/pc_client/src/logging/logger.c @@ -12,6 +12,8 @@ const char *loglevel[] = {"TRACE","DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL void init_logger(int log_level) { log_level_global = log_level; + log_message(LOG_INFO, "Set log level to %s", loglevel[log_level_global]); + } void log_message(int level, const char *message, ...) diff --git a/software/pc_client/src/main.c b/software/pc_client/src/main.c index e66a2ce..696fe99 100644 --- a/software/pc_client/src/main.c +++ b/software/pc_client/src/main.c @@ -32,19 +32,17 @@ void printUsage(char *argv[]) int main(int argc, char *argv[]) { + + // initialize logger init_logger(LOG_TRACE); log_message(LOG_INFO, "Starting split-flap pc client %s", "v1.0.0"); log_message(LOG_INFO, "(c) 2024-2025 GuniaLabs (www.dennisgunia.de)"); + + // parse arguments int opt = ' '; - u_int16_t addr_int = 0; - char *port = malloc(256); - char *command = malloc(16); - char *addr = malloc(16); - char *data = malloc(256); - command = ""; - addr = ""; - data = ""; - while ((opt = getopt(argc, argv, "p:c:a:d:")) != -1) + char *port, *config_file, *log_level; + + while ((opt = getopt(argc, argv, "p:c:l:")) != -1) // check options { switch (opt) { @@ -52,128 +50,78 @@ int main(int argc, char *argv[]) port = optarg; break; case 'c': - command = optarg; + config_file = optarg; break; - case 'a': - addr = optarg; - break; - case 'd': - data = optarg; + case 'l': + log_level = optarg; break; default: printUsage(argv); } } - if (access(port, F_OK) != 0) + + + if (log_level != NULL) // if log level specified { - log_message(LOG_CRITICAL, "Filedescriptor: %s does not exist or cannot be opened\n", port); - printUsage(argv); + if (strlen(log_level) > 0) + { // if log level specified + long inputLogLevel = strtol("", NULL, 10); // parse log level + init_logger(inputLogLevel); // re-init logger with new level + } } - // parse address - if (strlen(addr) == 0) + + if (config_file == NULL) // if config file not specified { - log_message(LOG_CRITICAL, "Please specify address\n"); + log_message(LOG_CRITICAL, "Please specify config file\n"); printUsage(argv); } else { - addr_int = strtol(addr, NULL, 10); + if (strlen(config_file) == 0) // if config file path empty + { + log_message(LOG_CRITICAL, "Please specify config file\n"); + } + else + { + if (access(config_file, F_OK) != 0) // check if config file exists and can be opened + { + log_message(LOG_CRITICAL, "Config file: %s does not exist or cannot be opened\n", config_file); + printUsage(argv); + } + else + { + log_message(LOG_INFO, "Use device configuration at '%s'", config_file); + } + } } - // start program - setvbuf(stdout, NULL, _IONBF, 0); // do not buffer stdout!!!! + if (port == NULL) // if port not specified + { + log_message(LOG_CRITICAL, "Please specify serial port\n"); + printUsage(argv); + } + else + { + if (strlen(port) == 0) // if port path empty + { + log_message(LOG_CRITICAL, "Please specify serial port\n"); + printUsage(argv); + } + else + { + if (access(port, F_OK) != 0) // check if port exists and can be opened + { + log_message(LOG_CRITICAL, "Serial port: %s does not exist or cannot be opened\n", port); + printUsage(argv); + } + else + { + log_message(LOG_INFO, "Use serial port at '%s'", port); + } + } + } - log_message(LOG_INFO,"Open device at %s @ 57600 Baud", port); int fd = rs485_init(port, B57600); // setup rs485 - - if (strcmp(command, "ping") == 0) - { - sfbus_ping(fd, addr_int); - } - else if (strcmp(command, "printf") == 0) - { - devicemgr_printText(data, 0, 0); - } - else if (strcmp(command, "r_eeprom") == 0) - { - char *buffer = malloc(64); - sfbus_read_eeprom(fd, addr_int, buffer); - printf("Read data: 0x"); - print_charHex(buffer, 5); - printf("\n"); - free(buffer); - exit(0); - } - else if (strcmp(command, "w_addr") == 0) - { - int n_addr = strtol(data, NULL, 10); - int ret = sfbusu_write_address(fd, addr_int, n_addr); - exit(ret); - } - else if (strcmp(command, "w_cal") == 0) - { - int n_addr = strtol(data, NULL, 10); - int ret = sfbusu_write_calibration(fd, addr_int, n_addr); - exit(ret); - } - else if (strcmp(command, "status") == 0) - { - double voltage = 0; - u_int32_t counter = 0; - u_int8_t status = sfbus_read_status(fd, addr_int, &voltage, &counter); - printf("=======================\n"); - printf("Status register flags :\n"); - printf(" 00 -> errorTooBig : %i\n", (status >> 0) & 0x01); - printf(" 01 -> noHome : %i\n", (status >> 1) & 0x01); - printf(" 02 -> fuseBlown : %i\n", (status >> 2) & 0x01); - printf(" 03 -> homeSense : %i\n", (status >> 3) & 0x01); - printf(" 04 -> powerDown : %i\n", (status >> 4) & 0x01); - printf(" 05 -> failSafe : %i\n", (status >> 5) & 0x01); - printf(" 06 -> busy : %i\n", (status >> 6) & 0x01); - printf("Driver-Voltage : %.2fV\n", voltage); - printf("Rotations : %i\n", counter); - - exit(0); - } - else if (strcmp(command, "display") == 0) - { - int flap = strtol(data, NULL, 10); - // read current eeprom status - char *buffer_w = malloc(4); - sfbus_display(fd, addr_int, flap); - exit(0); - } - else if (strcmp(command, "display_fr") == 0) - { - int flap = strtol(data, NULL, 10); - // read current eeprom status - char *buffer_w = malloc(4); - sfbus_display_full(fd, addr_int, flap); - exit(0); - } - else if (strcmp(command, "reset") == 0) - { - sfbus_reset_device(fd, addr_int); - exit(0); - } - else if (strcmp(command, "power_on") == 0) - { - sfbus_motor_power(fd, addr_int, 1); - exit(0); - } - else if (strcmp(command, "power_off") == 0) - { - sfbus_motor_power(fd, addr_int, 0); - exit(0); - } - else if (strcmp(command, "server") == 0) - { - start_console(fd); - } - else - { - fprintf(stderr, "Invalid command specified!\n"); - printUsage(argv); - } + start_console(fd, config_file); // start console return 0; } \ No newline at end of file diff --git a/software/pc_client/src/ftdi485.c b/software/pc_client/src/serial/ftdi485.c similarity index 98% rename from software/pc_client/src/ftdi485.c rename to software/pc_client/src/serial/ftdi485.c index bfea5f4..0286fbf 100644 --- a/software/pc_client/src/ftdi485.c +++ b/software/pc_client/src/serial/ftdi485.c @@ -8,7 +8,7 @@ */ #include "ftdi485.h" -#include "logging/logger.h" +#include "../logging/logger.h" /* * Open RS485 Interface (FT232RL) diff --git a/software/pc_client/src/ftdi485.h b/software/pc_client/src/serial/ftdi485.h similarity index 100% rename from software/pc_client/src/ftdi485.h rename to software/pc_client/src/serial/ftdi485.h diff --git a/software/pc_client/src/sfbus.c b/software/pc_client/src/serial/sfbus.c similarity index 69% rename from software/pc_client/src/sfbus.c rename to software/pc_client/src/serial/sfbus.c index 0877f65..6a191cb 100644 --- a/software/pc_client/src/sfbus.c +++ b/software/pc_client/src/serial/sfbus.c @@ -8,12 +8,18 @@ */ #include "sfbus.h" -#include "logging/logger.h" +#include "../logging/logger.h" #include #include #include - +/* +* Print buffer as hex values to stdout +* Used for debugging purposes +* +* @param buffer: buffer to print +* @param length: length of buffer +*/ void print_charHex(char *buffer, int length) { int _tlength = length; @@ -25,26 +31,51 @@ void print_charHex(char *buffer, int length) } } +/* +* Print received buffer as hex values to stdout +* Used for debugging purposes +* +* @param buffer: buffer to print +* @param length: length of buffer +* @param address: device address +*/ void print_bufferHexRx(char *buffer, int length, u_int16_t address) { if (log_message_header(LOG_TRACE) > 0) { - printf("Rx (0x%04X): ", address); + printf("Rx at address: 0x%04X Data: ", address); print_charHex(buffer, length); printf("| %i bytes received\n", length); } } +/* +* Print transmitted buffer as hex values to stdout +* Used for debugging purposes +* +* @param buffer: buffer to print +* @param length: length of buffer +* @param address: device address +*/ void print_bufferHexTx(char *buffer, int length, u_int16_t address) { if (log_message_header(LOG_TRACE) > 0) { - printf("Tx (0x%04X): ", address); + printf("Tx at address: 0x%04X Data: ", address); print_charHex(buffer, length); printf("| %i bytes sent\n", length); } } +/* +* Receive SFBus frame with protocol version 2.0 and wait for valid data +* Fails after 2 unsuccessful attempts +* +* @param fd: rs485 file descriptor +* @param address: device address +* @param buffer: buffer to store received data +* @return length of received data on success, -1 on error +*/ ssize_t sfbus_recv_frame_wait(int fd, u_int16_t address, char *buffer) { ssize_t len = 0; @@ -63,6 +94,15 @@ ssize_t sfbus_recv_frame_wait(int fd, u_int16_t address, char *buffer) return len; } +/* +* Receive SFBus frame with protocol version 2.0 +* Handles start byte, address check and CRC verification +* +* @param fd: rs485 file descriptor +* @param address: device address +* @param buffer: buffer to store received data +* @return length of received data on success, -1 on error +*/ ssize_t sfbus_recv_frame_v2(int fd, u_int16_t address, char *buffer) { @@ -116,14 +156,20 @@ ssize_t sfbus_recv_frame_v2(int fd, u_int16_t address, char *buffer) { print_bufferHexRx(buffer, frm_length - 4, address); } - log_message(LOG_DEBUG,"crc check failed! expected: %04X received: %04X", calc_crc, frm_crc); + log_message(LOG_DEBUG, "crc check failed! expected: %04X received: %04X", calc_crc, frm_crc); return -1; } } /* -* Send SFBus frame with protocol version 2.0 and calculated CRC +* Send SFBus frame with protocol version 2.0 +* Generates frame with start byte, address and crc checksum +* +* @param fd: rs485 file descriptor +* @param address: device address +* @param length: length of payload +* @param buffer: payload buffer to send */ void sfbus_send_frame_v2(int fd, u_int16_t address, u_int8_t length, char *buffer) { @@ -150,8 +196,11 @@ void sfbus_send_frame_v2(int fd, u_int16_t address, u_int8_t length, char *buffe } /* -* Send ping to device at specified address. -* returns 0 on success, else 1. +* Send ping command to device and wait for response +* +* @param fd: rs485 file descriptor +* @param address: device address +* @return 0 on success, 1 on failure */ int sfbus_ping(int fd, u_int16_t address) { @@ -173,6 +222,15 @@ int sfbus_ping(int fd, u_int16_t address) } } +/* +* Read data from device EEPROM +* Verifies received data and checks for ACK byte +* +* @param fd: rs485 file descriptor +* @param address: device address +* @param buffer: buffer to store received data +* @return length of received data on success, -1 on error +*/ int sfbus_read_eeprom(int fd, u_int16_t address, char *buffer) { char *cmd = "\xF0"; @@ -195,6 +253,16 @@ int sfbus_read_eeprom(int fd, u_int16_t address, char *buffer) return len; } +/* +* Write data to device EEPROM +* Verifies received data and checks for ACK byte +* +* @param fd: rs485 file descriptor +* @param address: device address +* @param wbuffer: buffer with data to write +* @param rbuffer: buffer to store received data +* @return length of received data on success, -1 on error +*/ int sfbus_write_eeprom(int fd, u_int16_t address, char *wbuffer, char *rbuffer) { char *cmd = malloc(5); @@ -221,6 +289,14 @@ int sfbus_write_eeprom(int fd, u_int16_t address, char *wbuffer, char *rbuffer) return len; } +/* +* Send display command to device to set flap position +* +* @param fd: rs485 file descriptor +* @param address: device address +* @param flap: flap ID to set +* @return 0 on success +*/ int sfbus_display(int fd, u_int16_t address, u_int8_t flap) { char *cmd = malloc(5); @@ -231,6 +307,14 @@ int sfbus_display(int fd, u_int16_t address, u_int8_t flap) return 0; } +/* +* Send display command to device to set flap position with full rotation +* +* @param fd: rs485 file descriptor +* @param address: device address +* @param flap: flap ID to set +* @return 0 on success +*/ int sfbus_display_full(int fd, u_int16_t address, u_int8_t flap) { char *cmd = malloc(5); @@ -241,6 +325,15 @@ int sfbus_display_full(int fd, u_int16_t address, u_int8_t flap) return 0; } +/* +* Read device status including voltage operation counter and status flags +* +* @param fd: rs485 file descriptor +* @param address: device address +* @param voltage: pointer to store read voltage value +* @param counter: pointer to store read operation counter value +* @return status byte on success, 0xFF on error +*/ u_int8_t sfbus_read_status(int fd, u_int16_t address, double *voltage, u_int32_t *counter) { char *cmd = "\xF8"; @@ -260,17 +353,30 @@ u_int8_t sfbus_read_status(int fd, u_int16_t address, double *voltage, u_int32_t *voltage = __voltage; *counter = (u_int32_t)_counter; - - free(_buffer); - return 0x00; + u_int8_t status = *(_buffer + 0) & 0xFF; // store status byte temporarily + free(_buffer); // free rx buffer to prevent memory leak + return status; } +/* +* Reset device +* +* @param fd: rs485 file descriptor +* @param address: device address +*/ void sfbus_reset_device(int fd, u_int16_t address) { char *cmd = "\x30"; sfbus_send_frame_v2(fd, address, strlen(cmd), cmd); } +/* +* Enable or disable motor power on device +* +* @param fd: rs485 file descriptor +* @param address: device address +* @param state: 0 = disable power, >0 = enable power +*/ void sfbus_motor_power(int fd, u_int16_t address, u_int8_t state) { char *cmd = "\x20"; @@ -281,6 +387,14 @@ void sfbus_motor_power(int fd, u_int16_t address, u_int8_t state) sfbus_send_frame_v2(fd, address, 1, cmd); } +/* +* Utility function to calculate CRC16 checksum +* Used for SFBus protocol v2.0 +* +* @param buffer: buffer to calculate checksum for +* @param len: length of buffer +* @return calculated CRC16 checksum +*/ u_int16_t calc_CRC16(char *buffer, u_int8_t len) { u_int16_t crc16 = 0xFFFF; diff --git a/software/pc_client/src/sfbus.h b/software/pc_client/src/serial/sfbus.h similarity index 100% rename from software/pc_client/src/sfbus.h rename to software/pc_client/src/serial/sfbus.h diff --git a/software/pc_client/src/sfbus-util.c b/software/pc_client/src/sfbus-util.c index 804bdf0..95f486c 100644 --- a/software/pc_client/src/sfbus-util.c +++ b/software/pc_client/src/sfbus-util.c @@ -10,6 +10,14 @@ #include "sfbus-util.h" #include "logging/logger.h" +/* +* Write new address to device +* +* @param fd: rs485 file descriptor +* @param current: current device address +* @param new: new device address +* @return 0 on success, -1 on error +*/ int sfbusu_write_address(int fd, u_int16_t current, u_int16_t new) { log_message(LOG_INFO, "Writing new address 0x%04X to device with current address 0x%04X", new, current); @@ -37,6 +45,14 @@ int sfbusu_write_address(int fd, u_int16_t current, u_int16_t new) return 0; } +/* +* Write new calibration data to device +* +* @param fd: rs485 file descriptor +* @param address: device address +* @param data: calibration data +* @return 0 on success, -1 on error +*/ int sfbusu_write_calibration(int fd, u_int16_t address, u_int16_t data) { log_message(LOG_INFO, "Writing new calibration 0x%04X to device at address 0x%04X", data, address); diff --git a/software/pc_client/src/wsserver.c b/software/pc_client/src/wsserver.c index 9ff10b1..6affbe8 100644 --- a/software/pc_client/src/wsserver.c +++ b/software/pc_client/src/wsserver.c @@ -17,7 +17,11 @@ json_object *(*commandparser_func)(json_object *); // this sections handles ws connections and communications -// called on opening websocket client + +/* +* called on opening websocket client +* @param client: websocket client connection +*/ void ws_opencon(ws_cli_conn_t client) { char *cli; @@ -25,7 +29,10 @@ void ws_opencon(ws_cli_conn_t client) log_message(LOG_DEBUG, "WebSocket connection opened, addr: %s", cli); } -// called on closing websocket client +/* +* called on closing websocket client +* @param client: websocket client connection +*/ void ws_closecon(ws_cli_conn_t client) { char *cli; @@ -33,7 +40,15 @@ void ws_closecon(ws_cli_conn_t client) log_message(LOG_DEBUG, "WebSocket connection closed, addr: %s", cli); } -// called on receiving websocket message +/* +* called on receiving websocket message +* Handles incoming websocket messages, parses json commands and sends responses. +* +* @param client: websocket client connection +* @param msg: received message +* @param size: size of received message +* @param type: type of message +*/ void ws_messagehandler(ws_cli_conn_t client, const unsigned char *msg, uint64_t size, int type) { char *cli = ws_getaddress(client); @@ -76,12 +91,25 @@ void ws_messagehandler(ws_cli_conn_t client, const unsigned char *msg, uint64_t free(tok); // always free tokenizer, to prevent memory leak } +/* +* send json response to websocket client +* +* @param client: websocket client connection +* @param res: json response object +*/ void send_json_response(ws_cli_conn_t client, json_object *res) { const char *message = json_object_to_json_string_ext(res, JSON_C_TO_STRING_PRETTY); ws_sendframe_txt(client, message); } +/* +* send json error to websocket client +* +* @param client: websocket client connection +* @param error: error message +* @param detail: error detail message +*/ void send_json_error(ws_cli_conn_t client, char *error, const char *detail) { json_object *root = json_object_new_object(); @@ -90,7 +118,12 @@ void send_json_error(ws_cli_conn_t client, char *error, const char *detail) send_json_response(client, root); } -// starting webserver +/* +* Start websocket server +* +* @param commandparser_func_ptr: pointer to command parser function +* @return 0 on success +*/ int start_webserver(json_object *(*commandparser_func_ptr)(json_object *)) { commandparser_func = commandparser_func_ptr;