reorganize code and added documentation

This commit is contained in:
Dennis Gunia
2025-10-19 19:42:20 +02:00
parent 9a5f18979a
commit e02620c22a
10 changed files with 573 additions and 198 deletions

View File

@@ -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": <device_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": <address>, "x": <x>, "y": <y> }
* response format: { "id": <new_device_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": <number_of_online_devices> }
*
* @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": <device_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": <x>, "y": <y>, "string": <text> }
* 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": <x>, "y": <y>, "flap": <flap_id> }
* 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": <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": <current_address>, "newaddress": <new_address> }
* 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": <address>, "calibration": <calibration_value> }
* 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": <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": <address>, "flap": <flap_id>, "full": <true/false> }
* 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": <address>, "power": <true/false> }
* 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
}

View File

@@ -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");

View File

@@ -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, ...)

View File

@@ -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;
}

View File

@@ -8,7 +8,7 @@
*/
#include "ftdi485.h"
#include "logging/logger.h"
#include "../logging/logger.h"
/*
* Open RS485 Interface (FT232RL)

View File

@@ -8,12 +8,18 @@
*/
#include "sfbus.h"
#include "logging/logger.h"
#include "../logging/logger.h"
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
/*
* 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;

View File

@@ -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);

View File

@@ -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;