added webserver

This commit is contained in:
Dennis Gunia
2025-09-18 15:59:15 +02:00
parent cd832083c4
commit d1304224a0
11 changed files with 1167 additions and 137 deletions

View File

@@ -0,0 +1,251 @@
# Websockets Interface documentation
All requests and responses are sent as json objects.
## Request
Every request must conatin at least one value: `command`
```
{
"command": "<command>"
}
```
### Device manager commands
#### Save config `dm_save`
Saves config to ./flapconfig.json.
Request:
```
{
"command": "dm_load"
}
```
Response:
```
{
"ack": true
}
```
#### Load config `dm_load`
Loads config from ./flapconfig.json.
Request:
```
{
"command": "dm_load"
}
```
Response:
```
{
"ack": true
}
```
#### Dump config `dm_dump`
Dumps current config to socket.
Request:
```
{
"command": "dm_dump"
}
```
Response:
```
{
"devices_all": <registered device count>
"devices_online": <online device count>,
"devices": <array of all devices>,
"map": <2D array of device locations>
}
```
#### Describe single device `dm_describe`
Gets all information for specified device id.
Request:
```
{
"command": "dm_describe",
"id": <device id>
}
```
Response:
```
{
...
}
```
#### Register new device `dm_register`
Register device, assign new id and assign a location.
```
{
"command": "dm_describe",
"address": <device nus address>,
"x": <x position on screen>,
"y": <y position on screen>,
}
```
Response:
```
{
"id": <assigned device id>
}
```
#### Remove device from config `dm_remove`
Removes device from config and frees location in screen
Request:
```
{
"command": "dm_remove",
"id": <device id>
}
```
Response:
```
{
"ack": true
}
```
#### Remove device from config `dm_refresh`
Refresh device config
Request:
```
{
"command": "dm_refresh"
}
```
Response:
```
{
"devices_online": <online device count>
}
```
### Device raw commands
#### Ping module `dr_ping`
Checks if a module reponds on the given address.
Request:
```
{
"command": "dr_ping",
"address": <address>
}
```
Response:
```
{
"success": <boolean: if device reponded 'true', else 'false'>
}
```
#### Set module address `dr_setaddress`
Changes the hardware address of an module.
Request:
```
{
"command": "dr_setaddress",
"address": <address>,
"newaddress": <new address>
}
```
Response:
```
{
"success": <boolean: if success 'true', else 'false'>
}
```
#### Set module offset calibration `dr_setcalibration`
Sets the offset calibration for an module.
Request:
```
{
"command": "dr_setcalibration",
"address": <address>,
"calibration": <calibration value (default: 1800)>
}
```
Response:
```
{
"success": <boolean: if success 'true', else 'false'>
}
```
#### Reset module `dr_reset`
Resets the controller of an module
Request:
```
{
"command": "dr_reset",
"address": <address>
}
```
Response:
```
{
"ack": true
}
```
#### Display flap `dr_display`
Sets the module to the specified flap directly or with recalibration.
Request:
```
{
"command": "dr_display",
"address": <address>,
"flap": <flap-number>,
("full": <boolean: full rotation of drum/recalibration>)
}
```
Response:
```
{
"ack": true
}
```
#### Power module on/off `dr_power`
Sets the power-state for the motor of the given module.
Request:
```
{
"command": "dr_power",
"address": <address>,
"power": <boolean: power on>
}
```
Response:
```
{
"ack": true
}
```
## Responses
### Error:
```
{
"error": "<error name>",
"detail": "<more detailed error message>"
}
```
The following responses are valid:
- `parsing error`: the json request is malformated and cannot be parsed
- `format error`: the json object is missing required fields. Check details for more information.
- `internal error`: an internal error occured. This should not happen. Chaeck server logs.

View File

@@ -0,0 +1,416 @@
#include "console.h"
const char *device_config_file = "./flapconfig.json";
int fd;
// command handlers
// dump config/ all devices
void cmd_dm_dump(json_object *req, json_object *res)
{
devicemgr_printDetailsAll(res);
}
// describe single device
void cmd_dm_describe(json_object *req, json_object *res)
{
json_object *id;
if (json_object_object_get_ex(req, "id", &id))
{
devicemgr_printDetails(json_object_get_int(id), res);
}
else
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: id"));
}
}
// register new device
void cmd_dm_register(json_object *req, json_object *res)
{
json_object *jaddress, *jx, *jy;
int address, x, y;
if (!json_object_object_get_ex(req, "address", &jaddress))
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: address"));
}
else if (!json_object_object_get_ex(req, "x", &jx))
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: x"));
}
else if (!json_object_object_get_ex(req, "y", &jy))
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: y"));
}
else
{
address = json_object_get_int(jaddress);
x = json_object_get_int(jx);
y = json_object_get_int(jy);
printf("[INFO][console] register new device wit addr %i at (%i,%i)", address, x, y);
int newId = devicemgr_register(fd, address, x, y, -1);
json_object_object_add(res, "id", json_object_new_int(newId));
}
}
// refresh all devices
void cmd_dm_refresh(json_object *req, json_object *res)
{
int devices_online = devicemgr_refresh();
json_object_object_add(res, "devices_online", json_object_new_int(devices_online));
}
// remove device
void cmd_dm_remove(json_object *req, json_object *res)
{
json_object *jid;
int id;
if (!json_object_object_get_ex(req, "id", &jid))
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: id"));
}
else
{
id = json_object_get_int(jid);
devicemgr_remove(id);
json_object_object_add(res, "ack", json_object_new_boolean(true));
}
}
// save all devices
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
void cmd_dm_load(json_object *req, json_object *res)
{
devicemgr_load(device_config_file);
json_object_object_add(res, "ack", json_object_new_boolean(true));
}
// print string on display
void cmd_dm_print(json_object *req, json_object *res)
{
json_object *jx = json_object_object_get(req, "x");
json_object *jy = json_object_object_get(req, "y");
json_object *jstr = json_object_object_get(req, "string");
if (jx == NULL)
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: x"));
}
else if (jy == NULL)
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: y"));
}
else if (jstr == NULL)
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: string"));
}
else
{
int x = json_object_get_int(jx);
int y = json_object_get_int(jy);
char *str = json_object_get_string(jstr);
devicemgr_printText(str, x, y);
json_object_object_add(res, "ack", json_object_new_boolean(true));
}
}
// set flap on display
void cmd_dm_print_single(json_object *req, json_object *res)
{
json_object *jx = json_object_object_get(req, "x");
json_object *jy = json_object_object_get(req, "y");
json_object *jflap = json_object_object_get(req, "flap");
if (jx == NULL)
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: x"));
}
else if (jy == NULL)
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: y"));
}
else if (jflap == NULL)
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: string"));
}
else
{
int x = json_object_get_int(jx);
int y = json_object_get_int(jy);
int flap = json_object_get_int(jflap);
devicemgr_printFlap(flap, x, y);
json_object_object_add(res, "ack", json_object_new_boolean(true));
}
}
// ping device
void cmd_dr_ping(json_object *req, json_object *res)
{
json_object *jaddr = json_object_object_get(req, "address");
if (jaddr == NULL)
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: address"));
}
else
{
if (sfbus_ping(fd, json_object_get_int(jaddr)) == 0)
{
json_object_object_add(res, "success", json_object_new_boolean(true));
}
else
{
json_object_object_add(res, "success", json_object_new_boolean(false));
}
}
}
// set device address
void cmd_dr_setaddress(json_object *req, json_object *res)
{
json_object *jaddr = json_object_object_get(req, "address");
json_object *jaddrn = json_object_object_get(req, "newaddress");
if (jaddr == NULL)
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: address"));
}
else if (jaddrn == NULL)
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: newaddress"));
}
else
{
if (sfbusu_write_address(fd, json_object_get_int(jaddr), json_object_get_int(jaddrn)) == 0)
{
json_object_object_add(res, "success", json_object_new_boolean(true));
}
else
{
json_object_object_add(res, "success", json_object_new_boolean(false));
}
}
}
void cmd_dr_setcalibration(json_object *req, json_object *res)
{
json_object *jaddr = json_object_object_get(req, "address");
json_object *jcal = json_object_object_get(req, "calibration");
if (jaddr == NULL)
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: address"));
}
else if (jcal == NULL)
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: calibration"));
}
else
{
if (sfbusu_write_calibration(fd, json_object_get_int(jaddr), json_object_get_int(jcal)) == 0)
{
json_object_object_add(res, "success", json_object_new_boolean(true));
}
else
{
json_object_object_add(res, "success", json_object_new_boolean(false));
}
}
}
void cmd_dr_reset(json_object *req, json_object *res)
{
json_object *jaddr = json_object_object_get(req, "address");
if (jaddr == NULL)
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: address"));
}
else
{
sfbus_reset_device(fd, json_object_get_int(jaddr));
json_object_object_add(res, "ack", json_object_new_boolean(true));
}
}
void cmd_dr_display(json_object *req, json_object *res)
{
json_object *jaddr = json_object_object_get(req, "address");
json_object *jflap = json_object_object_get(req, "flap");
json_object *jfullrot = json_object_object_get(req, "full");
if (jaddr == NULL)
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: address"));
}
else if (jflap == NULL)
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: flap"));
}
else
{
if (jfullrot == NULL)
{
sfbus_display(fd, json_object_get_int(jaddr), json_object_get_int(jflap));
}
else if (json_object_get_boolean(jfullrot) == false)
{
sfbus_display(fd, json_object_get_int(jaddr), json_object_get_int(jflap));
}
else
{
sfbus_display_full(fd, json_object_get_int(jaddr), json_object_get_int(jflap));
}
json_object_object_add(res, "ack", json_object_new_boolean(true));
}
}
void cmd_dr_power(json_object *req, json_object *res)
{
json_object *jaddr = json_object_object_get(req, "address");
json_object *jpower = json_object_object_get(req, "power");
if (jaddr == NULL)
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: address"));
}
else if (jpower == NULL)
{
json_object_object_add(res, "error", json_object_new_string("format error"));
json_object_object_add(res, "detail", json_object_new_string("missing key: power"));
}
else
{
if (json_object_get_boolean(jpower) == false)
{
sfbus_motor_power(fd, json_object_get_int(jaddr), 0);
}
else
{
sfbus_motor_power(fd, json_object_get_int(jaddr), 1);
}
json_object_object_add(res, "ack", json_object_new_boolean(true));
}
}
// command parser
json_object *parse_command(json_object *req)
{
json_object *commandObj;
json_object *res = json_object_new_object();
json_object_object_get_ex(req, "command", &commandObj);
char *command = json_object_get_string(commandObj);
free(commandObj);
// command 'table'
if (strcmp(command, "dm_dump") == 0)
{
cmd_dm_dump(req, res);
return res;
}
else if (strcmp(command, "dm_describe") == 0)
{
cmd_dm_describe(req, res);
return res;
}
else if (strcmp(command, "dm_register") == 0)
{
cmd_dm_register(req, res);
return res;
}
else if (strcmp(command, "dm_remove") == 0)
{
cmd_dm_remove(req, res);
return res;
}
else if (strcmp(command, "dm_refresh") == 0)
{
cmd_dm_refresh(req, res);
return res;
}
else if (strcmp(command, "dm_save") == 0)
{
cmd_dm_save(req, res);
return res;
}
else if (strcmp(command, "dm_load") == 0)
{
cmd_dm_load(req, res);
return res;
}
else if (strcmp(command, "dm_print") == 0)
{
cmd_dm_print(req, res);
return res;
}
else if (strcmp(command, "dr_ping") == 0)
{
cmd_dr_ping(req, res);
return res;
}
else if (strcmp(command, "dr_setaddress") == 0)
{
cmd_dr_setaddress(req, res);
return res;
}
else if (strcmp(command, "dr_setcalibration") == 0)
{
cmd_dr_setcalibration(req, res);
return res;
}
else if (strcmp(command, "dr_reset") == 0)
{
cmd_dr_reset(req, res);
return res;
}
else if (strcmp(command, "dr_display") == 0)
{
cmd_dr_display(req, res);
return res;
}
else if (strcmp(command, "dr_power") == 0)
{
cmd_dr_power(req, res);
return res;
}
else
{
json_object_object_add(res, "error", json_object_new_string("invalid command"));
json_object_object_add(res, "detail", json_object_new_string(""));
return res;
}
return NULL;
}
void start_console(int _fd)
{
fd = _fd;
// init device manager
devicemgr_init(fd);
// start server
start_webserver(&parse_command);
}

View File

@@ -0,0 +1,6 @@
#include "devicemgr.h"
#include "sfbus-util.h"
#include "wsserver.h"
#include <string.h>
void start_console();

View File

@@ -3,15 +3,19 @@
#include <string.h>
/*
* This file provides an abstraction layer to access many devices
* simultaneously. by Dennis Gunia - 2025 - wwwm.dennisgunia.de
* This section provides an abstraction layer to access many devices
* simultaneously.
*
* by Dennis Gunia - 2025 - www.dennisgunia.de
*/
enum SFDEVICE_STATE
{
UNALLOCATED,
NEW,
OFFLINE,
ONLINE,
FAILED
FAILED,
REMOVED
};
enum SFDEVICE_POWER
{
@@ -25,6 +29,7 @@ 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;
@@ -38,21 +43,23 @@ enum
{
SFDEVICE_MAXDEV = 128,
SFDEVICE_MAX_X = 20,
SFDEVICE_MAX_Y = 4
SFDEVICE_MAX_Y = 4,
JSON_MAX_LINE_LEN = 256
};
// next free slot to register device
int nextFreeSlot = -1;
int deviceMap[SFDEVICE_MAX_X][SFDEVICE_MAX_Y];
int deviceFd;
struct SFDEVICE devices[SFDEVICE_MAXDEV];
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", ":", ".", "-", "?", "!"};
"O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "Ä", "Ö", "Ü",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ".", "-", "?", "!"};
void devicemgr_init()
void devicemgr_init(int fd)
{
deviceFd = fd;
// reserve memory buffer
for (int y = 0; y < SFDEVICE_MAX_Y; y++)
{
@@ -61,30 +68,66 @@ void devicemgr_init()
deviceMap[x][y] = -1; //all empty slots are -1
}
}
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;
}
}
int devicemgr_readStatus(int device_id)
{
double _voltage = 0;
u_int32_t _counter = 0;
u_int8_t _status =
sfbus_read_status(devices[device_id].rs485_descriptor, devices[device_id].address, &_voltage, &_counter);
if (_status == 0xFF)
{
devices[device_id].powerState = UNKNOWN;
devices[device_id].deviceState = OFFLINE;
return -1;
if (devices[device_id].address > 0)
{ // only if defined
double _voltage = 0;
u_int32_t _counter = 0;
u_int8_t _status =
sfbus_read_status(devices[device_id].rs485_descriptor, devices[device_id].address, &_voltage, &_counter);
if (_status == 0xFF)
{
devices[device_id].powerState = UNKNOWN;
devices[device_id].deviceState = OFFLINE;
return -1;
}
devices[device_id].reg_voltage = _voltage;
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)
{
devices[device_id].deviceState = FAILED;
}
return 0;
}
devices[device_id].reg_voltage = _voltage;
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)
else
{
devices[device_id].deviceState = FAILED;
return -2;
}
}
int devicemgr_readCalib(int device_id)
{
if (devices[device_id].deviceState == ONLINE)
{
char *buffer_r = malloc(256);
if (sfbus_read_eeprom(devices[device_id].rs485_descriptor, devices[device_id].address, buffer_r) > 0)
{
uint16_t calib_data = (*(buffer_r + 2) & 0xFF | ((*(buffer_r + 3) << 8) & 0xFF00));
devices[device_id].calibration = calib_data;
free(buffer_r);
}
else
{
printf("Error reading eeprom from %i\n", device_id);
free(buffer_r);
return -1;
}
}
else
{
return -2;
}
return 0;
}
json_object *devicemgr_printMap()
@@ -102,12 +145,12 @@ json_object *devicemgr_printMap()
return rows_array;
}
json_object *devicemgr_printDetails(int device_id)
void devicemgr_printDetails(int device_id, json_object *root)
{
// generate json object with status
json_object *root = json_object_new_object();
json_object_object_add(root, "id", json_object_new_int(device_id));
json_object_object_add(root, "address", json_object_new_int(devices[device_id].address));
json_object_object_add(root, "calibration", json_object_new_int(devices[device_id].calibration));
json_object_object_add(root, "flapID", json_object_new_int(devices[device_id].current_flap));
json_object_object_add(root, "flapChar", json_object_new_string(symbols[devices[device_id].current_flap]));
json_object *position = json_object_new_object();
@@ -134,6 +177,12 @@ json_object *devicemgr_printDetails(int device_id)
case NEW:
json_object_object_add(status, "device", json_object_new_string("NEW"));
break;
case REMOVED:
json_object_object_add(status, "device", json_object_new_string("REMOVED"));
break;
default:
json_object_object_add(status, "device", json_object_new_string("UNALLOCATED"));
break;
}
json_object *status_flags = json_object_new_object();
json_object_object_add(status_flags,
@@ -159,31 +208,30 @@ json_object *devicemgr_printDetails(int device_id)
json_object_new_boolean(((devices[device_id].reg_status) >> 6) & 0x01));
json_object_object_add(status, "flags", status_flags);
json_object_object_add(root, "status", status);
return root;
}
json_object *devicemgr_printDetailsAll()
void devicemgr_printDetailsAll(json_object *root)
{
json_object *root = json_object_new_object();
json_object_object_add(root, "devices_all", json_object_new_int(nextFreeSlot + 1));
json_object *devices_arr = json_object_new_array();
int devices_online = 0;
for (int i = 0; i < (nextFreeSlot + 1); i++)
{
devicemgr_readStatus(i);
if (devices[i].deviceState == ONLINE)
if (devices[i].address > 0)
{
devices_online++;
devicemgr_readStatus(i);
if (devices[i].deviceState == ONLINE)
{
devices_online++;
}
json_object *device = json_object_new_object();
devicemgr_printDetails(i, device);
json_object_array_add(devices_arr, device);
}
json_object_array_add(devices_arr, devicemgr_printDetails(i));
}
json_object_object_add(root, "map", devicemgr_printMap());
json_object_object_add(root, "devices", devices_arr);
json_object_object_add(root, "devices_online", json_object_new_int(devices_online));
printf("The json representation:\n\n%s\n\n", json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY));
return root;
}
void setSingle(int id, char flap)
@@ -191,9 +239,11 @@ void setSingle(int id, char flap)
// first convert char to flap id
char test_char = toupper(flap);
printf("find char %c\n", test_char);
for (int ix = 0; ix < 45; ix++){
if (*symbols[ix] == test_char){
printf("match char %i %i %i\n",test_char, *symbols[ix], ix);
for (int ix = 0; ix < 45; ix++)
{
if (*symbols[ix] == test_char)
{
printf("match char %i %i %i\n", test_char, *symbols[ix], ix);
sfbus_display_full(devices[id].rs485_descriptor, devices[id].address, ix);
break;
}
@@ -201,42 +251,225 @@ void setSingle(int id, char flap)
devices[nextFreeSlot].current_flap = flap;
}
void printText(char *text, int x, int y)
void setSingleRaw(int id, int flap)
{
sfbus_display_full(devices[id].rs485_descriptor, devices[id].address, flap);
devices[nextFreeSlot].current_flap = flap;
}
void devicemgr_printText(char *text, int x, int y)
{
for (int i = 0; i < strlen(text); i++)
{
int this_id = deviceMap[x + i][y];
if (this_id >= 0)
{
printf("print char %c to %i\n",*(text + i), devices[this_id].address);
printf("print char %c to %i\n", *(text + i), devices[this_id].address);
setSingle(this_id, *(text + i));
usleep(5000);
}
}
}
int devicemgr_register(int rs485_descriptor, u_int16_t address, int x, int y)
void devicemgr_printFlap(int flap, int x, int y)
{
nextFreeSlot++;
devices[nextFreeSlot].pos_x = x;
devices[nextFreeSlot].pos_y = y;
devices[nextFreeSlot].address = address;
devices[nextFreeSlot].rs485_descriptor = rs485_descriptor;
devices[nextFreeSlot].reg_voltage = 0;
devices[nextFreeSlot].reg_counter = 0;
devices[nextFreeSlot].reg_status = 0;
devices[nextFreeSlot].current_flap = 0;
devices[nextFreeSlot].deviceState = NEW;
devices[nextFreeSlot].powerState = DISABLED;
int this_id = deviceMap[x][y];
if (this_id >= 0)
{
setSingleRaw(this_id, flap);
}
}
int devicemgr_register(int rs485_descriptor, u_int16_t address, int x, int y, int nid)
{
if (nid < 0)
{
nextFreeSlot++;
nid = nextFreeSlot;
}
devices[nid].pos_x = x;
devices[nid].pos_y = y;
devices[nid].address = address;
devices[nid].calibration = 0;
devices[nid].rs485_descriptor = rs485_descriptor;
devices[nid].reg_voltage = 0;
devices[nid].reg_counter = 0;
devices[nid].reg_status = 0;
devices[nid].current_flap = 0;
devices[nid].deviceState = NEW;
devices[nid].powerState = DISABLED;
// try to reach device
devicemgr_readStatus(nextFreeSlot);
devicemgr_readStatus(nid);
devicemgr_readCalib(nid);
if (deviceMap[x][y] >= 0)
{ // rest old ones
int old_id = deviceMap[x][y];
devices[old_id].pos_x = -1;
devices[old_id].pos_y = -1;
}
deviceMap[x][y] = nextFreeSlot;
return nextFreeSlot;
deviceMap[x][y] = nid;
return nid;
}
// refreshes status of all devices
int devicemgr_refresh()
{
int devices_online = 0;
for (int ix = 0; ix < SFDEVICE_MAXDEV; ix++)
{
if (devices[ix].address > 0)
{
devicemgr_readStatus(ix);
if (devices[ix].deviceState == ONLINE)
{
devices_online++;
}
}
}
return devices_online;
}
// remove devices from system
int devicemgr_remove(int id)
{
devices[nextFreeSlot].deviceState = REMOVED;
devices[nextFreeSlot].address = 0;
devices[nextFreeSlot].rs485_descriptor = NULL;
return 0;
}
int devicemgr_save(char *file)
{
json_object *root = json_object_new_object();
json_object_object_add(root, "nextFreeSlot", json_object_new_int(nextFreeSlot));
json_object *device_array = json_object_new_array();
for (int ix = 0; ix < SFDEVICE_MAXDEV; ix++)
{
if (devices[ix].address > 0)
{
json_object *device = json_object_new_object();
devicemgr_printDetails(ix, device);
json_object_array_add(device_array, device);
}
}
json_object_object_add(root, "devices", device_array);
char *data = json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY);
printf("[INFO][console] store data to %s\n", file);
FILE *fptr;
fptr = fopen(file, "w");
fwrite(data, sizeof(char), strlen(data), fptr);
fclose(fptr);
}
int devicemgr_load(char *file)
{
FILE *fptr;
const char *line_in_file = malloc(JSON_MAX_LINE_LEN); // maximum of 256 bytes per line;
fptr = fopen(file, "r");
json_tokener *tok = json_tokener_new();
json_object *jobj = NULL;
int stringlen = 0;
enum json_tokener_error jerr;
do
{
int read_ret = fgets(line_in_file, JSON_MAX_LINE_LEN, fptr); // read line from file
stringlen = strlen(line_in_file);
// printf("Read line with chars: %i : %s", stringlen, line_in_file); // only for testing
jobj = json_tokener_parse_ex(tok, line_in_file, stringlen);
if (read_ret == NULL)
{
break;
}
} while ((jerr = json_tokener_get_error(tok)) == json_tokener_continue);
if (jerr != json_tokener_success)
{
free(fptr); //free file pointer
fprintf(stderr, "Error: %s\n", json_tokener_error_desc(jerr));
// Handle errors, as appropriate for your application.
return -1;
}
// cleanup
free(fptr); //free file pointer
free(tok); //free tokenizer
// dump loadad data to terminal ( for tetsting)
// char *data = json_object_to_json_string_ext(jobj, JSON_C_TO_STRING_PRETTY);
// printf("%s",data);
// load data
json_object *next_free;
if (!json_object_object_get_ex(jobj, "nextFreeSlot", &next_free))
{
fprintf(stderr, "Error: %s\n", "Key 'nextFreeSlot' not found.");
return -1;
}
else
{
nextFreeSlot = json_object_get_int(next_free);
free(next_free);
}
// clear config
devicemgr_init(deviceFd);
// load devices
json_object *devices;
if (!json_object_object_get_ex(jobj, "devices", &devices))
{
fprintf(stderr, "Error: %s\n", "Key 'devices' not found.");
return -1;
}
else
{
int arraylen = json_object_array_length(devices);
for (int i = 0; i < arraylen; i++)
{
devicemgr_load_single(json_object_array_get_idx(devices, i));
}
free(devices);
}
}
int devicemgr_load_single(json_object *device_obj)
{
json_object *jid = json_object_object_get(device_obj, "id");
json_object *jaddr = json_object_object_get(device_obj, "address");
json_object *jpos = json_object_object_get(device_obj, "position");
json_object *jposx = json_object_object_get(jpos, "x");
json_object *jposy = json_object_object_get(jpos, "y");
// verify values are present
if (jid == NULL)
{
fprintf(stderr, "Error: Key 'device.%s' not found\n", "id");
return -1;
}
if (jaddr == NULL)
{
fprintf(stderr, "Error: Key 'address.%s' not found\n", "id");
return -1;
}
if (jposx == NULL)
{
fprintf(stderr, "Error: Key 'device.%s' not found\n", "position.x");
return -1;
}
if (jposy == NULL)
{
fprintf(stderr, "Error: Key 'device.%s' not found\n", "position.y");
return -1;
}
// create device
devicemgr_register(deviceFd,
json_object_get_int(jaddr),
json_object_get_int(jposx),
json_object_get_int(jposy),
json_object_get_int(jid));
}

View File

@@ -1,21 +1,26 @@
#include "sfbus.h"
#include <ctype.h>
#include <errno.h> // Error integer and strerror() function
#include <fcntl.h> // Contains file controls like O_RDWR
#include <json-c/json.h>
#include <json-c/json_object.h>
#include <json-c/json_tokener.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <fcntl.h> // Contains file controls like O_RDWR
#include <errno.h> // Error integer and strerror() function
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <termios.h> // Contains POSIX terminal control definitions
#include <unistd.h> // write(), read(), close()
#include <sys/types.h>
#include "sfbus.h"
#include <json-c/json_object.h>
#include <sys/types.h>
#include <json-c/json.h>
#include <ctype.h>
int devicemgr_readStatus(int device_id) ;
json_object * devicemgr_printDetails(int device_id);
json_object * devicemgr_printDetailsAll();
int devicemgr_register(int rs485_descriptor, u_int16_t address, int x,int y);
int devicemgr_readStatus(int device_id);
int devicemgr_readCalib(int device_id);
void devicemgr_printDetails(int device_id, json_object *root);
void devicemgr_printDetailsAll(json_object *root);
int devicemgr_register(int rs485_descriptor, u_int16_t address, int x, int y, int nid);
void devicemgr_init();
int devicemgr_print(char* text);
int devicemgr_print(char *text);
int devicemgr_refresh();
int devicemgr_save(char *file);
void devicemgr_printText(char *text, int x, int y);
void devicemgr_printFlap(int flap, int x, int y);

View File

@@ -12,6 +12,8 @@
#include "ftdi485.h"
#include "sfbus.h"
#include "devicemgr.h"
#include "console.h"
void printUsage(char *argv[]) {
fprintf(stderr, "Usage: %s -p <tty> -c <command> [value]\n", argv[0]);
@@ -65,20 +67,10 @@ int main(int argc, char *argv[]) {
printf("Open device at %s\n", port);
int fd = rs485_init(port, B19200); // setup rs485
// test
devicemgr_init();
devicemgr_register(fd,1,0,0);
devicemgr_register(fd,2,1,0);
devicemgr_register(fd,3,2,0);
devicemgr_register(fd,4,3,0);
devicemgr_printDetailsAll();
//exit(1);
if (strcmp(command, "ping") == 0) {
sfbus_ping(fd, addr_int);
exit(0);
} else if (strcmp(command, "printf") == 0) {
printText(data,0,0);
devicemgr_printText(data,0,0);
} else if (strcmp(command, "r_eeprom") == 0) {
char *buffer = malloc(64);
sfbus_read_eeprom(fd, addr_int, buffer);
@@ -89,44 +81,12 @@ int main(int argc, char *argv[]) {
exit(0);
} else if (strcmp(command, "w_addr") == 0) {
int n_addr = strtol(data, NULL, 10);
if (n_addr < 1) {
printf("Please specify new address > 0 with -d\n");
exit(-1);
}
// read current eeprom status
char *buffer_w = malloc(64);
char *buffer_r = malloc(64);
if (sfbus_read_eeprom(fd, addr_int, buffer_w) < 0) {
fprintf(stderr, "Error reading eeprom\n");
exit(1);
}
// modify current addr
u_int16_t n_addr_16 = n_addr;
memcpy(buffer_w, &n_addr_16, 2);
if (sfbus_write_eeprom(fd, addr_int, buffer_w, buffer_r) < 0) {
fprintf(stderr, "Error writing eeprom\n");
exit(1);
}
exit(0);
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);
// read current eeprom status
char *buffer_w = malloc(64);
char *buffer_r = malloc(64);
if (sfbus_read_eeprom(fd, addr_int, buffer_w) < 0) {
fprintf(stderr, "Error reading eeprom\n");
exit(1);
}
// modify current addr
u_int16_t n_addr_16 = n_addr;
memcpy(buffer_w+2, &n_addr_16, 2);
if (sfbus_write_eeprom(fd, addr_int, buffer_w, buffer_r) < 0) {
fprintf(stderr, "Error writing eeprom\n");
exit(1);
}
exit(0);
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;
@@ -165,20 +125,11 @@ int main(int argc, char *argv[]) {
} 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);
}
char *buffer = malloc(256);
char *cmd = "\xF0";
sfbus_send_frame(fd, 0, strlen(cmd), cmd);
int len = sfbus_recv_frame_wait(fd, 0xFFFF, buffer);
// printf("TEST:%i %s\n", len, buffer);
free(buffer);
return 0;
}

View File

@@ -0,0 +1,48 @@
#include "sfbus-util.h"
int sfbusu_write_address(int fd, u_int16_t current, u_int16_t new)
{
if (new < 1)
{
printf("Please specify new address > 0 with -d\n");
return -1;
}
// read current eeprom status
char *buffer_w = malloc(64);
char *buffer_r = malloc(64);
if (sfbus_read_eeprom(fd, current, buffer_w) < 0)
{
fprintf(stderr, "Error reading eeprom\n");
return 1;
}
// modify current addr
u_int16_t n_addr_16 = new;
memcpy(buffer_w, &n_addr_16, 2);
if (sfbus_write_eeprom(fd, current, buffer_w, buffer_r) < 0)
{
fprintf(stderr, "Error writing eeprom\n");
return 1;
}
return 0;
}
int sfbusu_write_calibration(int fd, u_int16_t address, u_int16_t data)
{
// read current eeprom status
char *buffer_w = malloc(64);
char *buffer_r = malloc(64);
if (sfbus_read_eeprom(fd, address, buffer_w) < 0)
{
fprintf(stderr, "Error reading eeprom\n");
return 1;
}
// modify current calibration
memcpy(buffer_w + 2, &data, 2);
if (sfbus_write_eeprom(fd, address, buffer_w, buffer_r) < 0)
{
fprintf(stderr, "Error writing eeprom\n");
return 1;
}
return 0;
}

View File

@@ -0,0 +1,7 @@
#include "sfbus.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int sfbusu_write_address(int fd, u_int16_t current, u_int16_t new);
int sfbusu_write_calibration(int fd, u_int16_t address, u_int16_t data);

View File

@@ -107,14 +107,10 @@ void sfbus_send_frame(int fd, u_int16_t address, u_int8_t length,
}
*frame = '$'; // startbyte
//rs485_trdir(fd, 0);
int result = write(fd, frame_ptr, frame_size_complete);
print_bufferHexTx(frame_ptr + 5, frame_size_complete - 6, address);
free(frame_ptr);
// tcdrain(fd);
//usleep(470 * (frame_size_complete + 1));
//rs485_trdir(fd, 1);
}
int sfbus_ping(int fd, u_int16_t address) {

View File

@@ -0,0 +1,106 @@
#include "wsserver.h"
/*
* This section provides a web server to controll the
* device manager through web sockets
*
* by Dennis Gunia - 2025 - www.dennisgunia.de
*/
json_object *(*commandparser_func)(json_object *);
// this sections handles ws connections and communications
// called on opening websocket client
void onopen(ws_cli_conn_t client)
{
char *cli;
cli = ws_getaddress(client);
printf("Connection opened, addr: %s\n", cli);
}
// called on closing websocket client
void onclose(ws_cli_conn_t client)
{
char *cli;
cli = ws_getaddress(client);
printf("Connection closed, addr: %s\n", cli);
}
// called on receiving websocket message
void onmessage(ws_cli_conn_t client, const unsigned char *msg, uint64_t size, int type)
{
char *cli = ws_getaddress(client);
printf("received message: %s (%zu), from: %s\n", msg, size, cli);
json_tokener *tok = json_tokener_new();
json_object *req = json_tokener_parse_ex(tok, msg, size);
enum json_tokener_error jerr;
jerr = json_tokener_get_error(tok);
if (jerr != json_tokener_success)
{
// check if request can be parsed, if not retrun error
send_json_error(client, "parsing error", json_tokener_error_desc(jerr));
}
else
{
// if it can be parsed, get command.
json_object *commandObj = json_object_object_get(req, "command");
printf("test");
if (commandObj != NULL)
{
char *command = json_object_to_json_string(commandObj);
// get key
json_object *res = commandparser_func(req);
if (res == NULL)
{
send_json_error(client, "internal error", "");
}
else
{
send_json_response(client, res);
}
free(res);
}
else
{
// if key is missing, send error
send_json_error(client, "format error", "missing key: command");
}
}
free(tok); // always free tokenizer, to prevent memory leak
}
void send_json_response(ws_cli_conn_t client, json_object *res)
{
char *message = json_object_to_json_string_ext(res, JSON_C_TO_STRING_PRETTY);
ws_sendframe_txt(client, message);
}
void send_json_error(ws_cli_conn_t client, char *error, char *detail)
{
json_object *root = json_object_new_object();
json_object_object_add(root, "error", json_object_new_string(error));
json_object_object_add(root, "detail", json_object_new_string(detail));
send_json_response(client, root);
}
// starting webserver
int start_webserver(json_object *(*commandparser_func_ptr)(json_object *))
{
commandparser_func = commandparser_func_ptr;
ws_socket(&(struct ws_server){/*
* Bind host, such as:
* localhost -> localhost/127.0.0.1
* 0.0.0.0 -> global IPv4
* :: -> global IPv4+IPv6 (Dual stack)
*/
.host = WS_SERVER_ADDR,
.port = WS_SERVER_PORT,
.thread_loop = 0,
.timeout_ms = 1000,
.evs.onopen = &onopen,
.evs.onclose = &onclose,
.evs.onmessage = &onmessage});
return (0);
}

View File

@@ -0,0 +1,11 @@
#include <json-c/json_object.h>
#include <json-c/json_tokener.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wsserver/ws.h>
#define WS_SERVER_PORT 8080
#define WS_SERVER_ADDR "localhost"
int start_webserver();