Files
src.dualinventive.com/dinet/libdi-php/src/extension.cpp
2024-08-09 12:10:16 +02:00

457 lines
12 KiB
C++

#include <chrono>
#include "libdi-php.h"
#include "socketcan.h"
#include "constants.h"
#include <di/rpc.h>
#include <di/errno.h>
#include <di/version.h>
#include <ifaddrs.h>
#include <ext/standard/info.h>
using namespace std;
static const char *extension_version = APPLICATION_VERSION;
#define le_di_can_ctx_name "di_can_ctx"
static int le_di_can_ctx_resid;
PHP_FUNCTION(di_can_list) {
struct ifaddrs *ifaddr, *ifa;
int n;
if (getifaddrs(&ifaddr) == -1)
RETURN_NULL();
array_init(return_value);
for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
if (strncmp("can", ifa->ifa_name, 3) == 0 ||
strncmp("vcan", ifa->ifa_name, 4) == 0) {
add_next_index_string(return_value, ifa->ifa_name);
}
}
freeifaddrs(ifaddr);
}
PHP_FUNCTION(di_can_open) {
zend_string *iface;
struct php_di_can_ctx *c;
if (zend_parse_parameters(static_cast<int>(ZEND_NUM_ARGS()), "S", &iface) == FAILURE)
RETURN_NULL();
c = php_can_alloc(iface->val);
if (!c)
RETURN_NULL();
di_can_init(&c->ctx);
di_can_set_private_data(&c->ctx, c);
/* Glue can stack recv/send with hardware peripherial */
di_can_callback_set_recv(&c->ctx, socketcan_recv_cb);
di_can_callback_set_send(&c->ctx, socketcan_send_cb);
di_can_frame_init(&c->ctx, c->mm.di_can_frame_pool, DI_CAN_RX_FRAMES);
di_buffer_init_array(c->mm.di_can_msg_buf, c->mm.di_can_msg_buf_data, DI_CAN_MESSAGES, DI_CAN_MSG_SIZE);
di_can_msg_init_array(&c->ctx, c->mm.di_can_msg_pool, DI_CAN_MESSAGES, c->mm.di_can_msg_buf, DI_CAN_MESSAGES);
di_can_msg_init(&c->ctx, c->mm.di_can_msg_pool, DI_CAN_MESSAGES);
c->ctx.frame.timeout = UINT32_MAX;
c->ctx.msg.timeout = UINT32_MAX;
di_can_msg_recv_set_blocking(&c->ctx, true);
di_can_msg_recv_set_timeout(&c->ctx, 10);
di_can_msg_enable_dst_id_filter(&c->ctx, false);
c->running = true;
int rc = pthread_create(&c->thr, NULL, php_can_thread_func, reinterpret_cast<void *>(c));
if (rc) {
php_can_free(&c);
RETURN_NULL();
}
RETURN_RES(zend_register_resource(c, le_di_can_ctx_resid));
}
PHP_FUNCTION(di_can_close) {
zval *zc;
if (zend_parse_parameters(static_cast<int>(ZEND_NUM_ARGS()), "r", &zc) == FAILURE)
RETURN_NULL();
zend_list_delete(Z_RES_P(zc));
RETURN_TRUE;
}
PHP_FUNCTION(di_version) {
array_init(return_value);
add_assoc_string(return_value, "build.version", const_cast<char *>(APPLICATION_VERSION));
add_assoc_string(return_value, "build.commit", const_cast<char *>(APPLICATION_COMMIT));
add_assoc_string(return_value, "build.datetime", const_cast<char *>(APPLICATION_BUILD_DATETIME));
}
/** Convert di_can_msg struct to zval */
static void php_di_can_msg_to_zval(zval *return_value, const struct di_can_msg *msg) {
array_init(return_value);
add_assoc_long(return_value, "canid", msg->canid);
add_assoc_long(return_value, "ttype", DI_CAN_GET_TRANSFERTYPE(msg->canid));
add_assoc_long(return_value, "msgtype", DI_CAN_GET_MSGTYPE(msg->canid));
if (di_can_msg_emflag_rt(msg)) {
add_assoc_bool(return_value, "rt", true);
add_assoc_long(return_value, "rt:seqnr", di_can_msg_read_emdata(msg));
}
add_assoc_long(return_value, "src_id", msg->src_id);
add_assoc_long(return_value, "dst_id", msg->dst_id);
add_assoc_long(return_value, "dtype", DI_CAN_GET_DATATYPE(msg->canid));
add_assoc_long(return_value, "ptype", msg->ptype);
size_t size = 0;
/* We shop of the '\0' character when the payload is a string */
if (msg->ptype == DI_CAN_PTYPE_STRING)
size = msg->size - 1;
else if (msg->size)
size = msg->size;
add_assoc_long(return_value, "size", msg->size);
if (msg->ptype == DI_CAN_PTYPE_I8 || msg->ptype == DI_CAN_PTYPE_U8)
add_assoc_long(return_value, "msg", mpack_load_u8((const char *)msg->msg));
else if (msg->ptype == DI_CAN_PTYPE_I16 || msg->ptype == DI_CAN_PTYPE_U16)
add_assoc_long(return_value, "msg", mpack_load_u16((const char *)msg->msg));
else if (msg->ptype == DI_CAN_PTYPE_I32 || msg->ptype == DI_CAN_PTYPE_U32 || msg->ptype == DI_CAN_PTYPE_DI_ERRNO)
add_assoc_long(return_value, "msg", mpack_load_u32((const char *)msg->msg));
else if (msg->ptype == DI_CAN_PTYPE_I64 || msg->ptype == DI_CAN_PTYPE_U64)
add_assoc_long(return_value, "msg", mpack_load_u64((const char *)msg->msg));
else if (msg->size)
add_assoc_stringl(return_value, "msg", reinterpret_cast<char *>(msg->msg), static_cast<uint>(size));
else
add_assoc_null(return_value, "msg");
}
PHP_FUNCTION(di_can_recv) {
zval *zc;
if (zend_parse_parameters(static_cast<int>(ZEND_NUM_ARGS()), "r", &zc) == FAILURE)
RETURN_NULL();
php_di_can_ctx *c;
c = static_cast<php_di_can_ctx *>(zend_fetch_resource(Z_RES_P(zc), le_di_can_ctx_name, le_di_can_ctx_resid));
if (c == NULL)
RETURN_NULL();
struct di_can_msg *msg;
msg = di_can_recv(&c->ctx, DI_CAN_MSGTYPE_ANY);
if (!msg)
RETURN_NULL();
php_di_can_msg_to_zval(return_value, msg);
di_can_msg_free(&msg);
}
/**
* Queue raw periodic msg with milliseconds
* di_can_send_queue(iface(resource), milliseconds(long), msg(array))
* msg['ttype'] = "pub", "req", "rep"
* msg['dtype'] = 0x123 (code...)
* msg['data'] = "<binary>", or NULL
*/
PHP_FUNCTION(di_can_send_queue) {
uint64_t interval_ms = 0;
struct php_di_can_ctx *c;
zval *zc;
zval *zmsg;
if (zend_parse_parameters(static_cast<int>(ZEND_NUM_ARGS()), "ral", &zc, &zmsg, &interval_ms) == FAILURE)
RETURN_NULL();
c = static_cast<php_di_can_ctx *>(zend_fetch_resource(Z_RES_P(zc), le_di_can_ctx_name, le_di_can_ctx_resid));
if (!c)
RETURN_FALSE;
struct php_can_periodic_item i;
php_di_can_send_args_get(zmsg, &i.msg, &c->ctx);
lock_guard<mutex> lock(*c->lock);
c->periodic.token++;
i.interval = std::chrono::milliseconds(interval_ms);
i.deadline = std::chrono::system_clock::now();
(*c->periodic.queue)[c->periodic.token] = i;
RETURN_LONG(c->periodic.token);
}
/**
* Dequeue periodic msg
*/
PHP_FUNCTION(di_can_send_dequeue) {
uint64_t token = 0;
php_di_can_ctx *c;
zval *zc;
if (zend_parse_parameters(static_cast<int>(ZEND_NUM_ARGS()), "rl", &zc, &token) == FAILURE)
RETURN_NULL();
c = static_cast<php_di_can_ctx *>(zend_fetch_resource(Z_RES_P(zc), le_di_can_ctx_name, le_di_can_ctx_resid));
if (!c)
RETURN_FALSE;
lock_guard<mutex> lock(*c->lock);
auto it = c->periodic.queue->find(static_cast<unsigned int>(token));
if (it == c->periodic.queue->end())
RETURN_FALSE;
php_di_can_send_args_free(&it->second.msg);
c->periodic.queue->erase(it);
RETURN_TRUE;
}
/**
* di_can_send($can0, $msg)
* $can0 = di_can_open("can0")
* msg['ttype'] = "pub", "req", "rep"
* msg['dtype'] = "config:set" etc... (the string is automatic converted to binary dtype id)
* msg['data'] = "<messagepack data>", or NULL
*/
PHP_FUNCTION(di_can_send) {
zval *zc;
zval *zv;
php_di_can_msg msg;
if (zend_parse_parameters(static_cast<int>(ZEND_NUM_ARGS()), "ra", &zc, &zv) == FAILURE)
RETURN_NULL();
struct php_di_can_ctx *c;
c = static_cast<php_di_can_ctx *>(zend_fetch_resource(Z_RES_P(zc), le_di_can_ctx_name, le_di_can_ctx_resid));
if (!c)
RETURN_NULL();
php_di_can_send_args_get(zv, &msg, &c->ctx);
struct di_can_msg *_msg = &msg.msg;
auto ret = di_can_send(&_msg);
php_di_can_send_args_free(&msg);
if (ret != DNOK) {
RETURN_FALSE;
}
RETURN_TRUE;
}
PHP_FUNCTION(di_can_send_empty_reply) {
zval *zc;
zval *zv;
php_di_can_msg msg;
if (zend_parse_parameters(static_cast<int>(ZEND_NUM_ARGS()), "ra", &zc, &zv) == FAILURE)
RETURN_NULL();
struct php_di_can_ctx *c;
c = static_cast<php_di_can_ctx *>(zend_fetch_resource(Z_RES_P(zc), le_di_can_ctx_name, le_di_can_ctx_resid));
if (!c)
RETURN_NULL();
php_di_can_send_args_get(zv, &msg, &c->ctx);
struct di_can_msg *_msg = &msg.msg;
auto ret = di_can_send_empty_reply(_msg);
php_di_can_send_args_free(&msg);
if (ret != DNOK) {
RETURN_FALSE;
}
RETURN_TRUE;
}
/**
* di_can_reqrep($can0, $msg, $timeout_ms)
* $can0 = di_can_open("can0")
* msg['ttype'] = "pub", "req", "rep"
* msg['dtype'] = "config:set" etc... (the string is automatic converted to binary dtype id)
* msg['data'] = "<messagepack data>", or NULL
*/
PHP_FUNCTION(di_can_reqrep) {
zval *zc;
zval *zv;
int64_t timeout_ms;
php_di_can_msg msg;
if (zend_parse_parameters(static_cast<int>(ZEND_NUM_ARGS()), "ral", &zc, &zv, &timeout_ms) == FAILURE)
RETURN_NULL();
struct php_di_can_ctx *c;
c = static_cast<php_di_can_ctx *>(zend_fetch_resource(Z_RES_P(zc), le_di_can_ctx_name, le_di_can_ctx_resid));
if (!c)
RETURN_NULL();
php_di_can_send_args_get(zv, &msg, &c->ctx);
struct di_can_msg *_msg = &msg.msg;
di_errno_t ret = di_can_reqrep(&_msg, static_cast<uint32_t>(timeout_ms));
php_di_can_send_args_free(&msg);
if (ret != DNOK) {
RETURN_NULL();
}
php_di_can_msg_to_zval(return_value, _msg);
di_can_msg_free(&_msg);
}
/**
* di_can_reqrep_with_retry($can0, $msg, $timeout_ms, $retries)
* $can0 = di_can_open("can0")
* msg['ttype'] = "pub", "req", "rep"
* msg['dtype'] = "config:set" etc... (the string is automatic converted to binary dtype id)
* msg['data'] = "<messagepack data>", or NULL
*/
PHP_FUNCTION(di_can_reqrep_with_retry) {
zval *zc;
zval *zv;
int64_t timeout_ms;
int64_t retries;
php_di_can_msg msg;
if (zend_parse_parameters(static_cast<int>(ZEND_NUM_ARGS()), "rall", &zc, &zv, &timeout_ms, &retries) == FAILURE)
RETURN_NULL();
struct php_di_can_ctx *c;
c = static_cast<php_di_can_ctx *>(zend_fetch_resource(Z_RES_P(zc), le_di_can_ctx_name, le_di_can_ctx_resid));
if (!c)
RETURN_NULL();
php_di_can_send_args_get(zv, &msg, &c->ctx);
struct di_can_msg *_msg = &msg.msg;
di_errno_t ret = di_can_reqrep_with_retry(&_msg, static_cast<uint32_t>(timeout_ms), static_cast<size_t>(retries));
php_di_can_send_args_free(&msg);
if (ret != DNOK)
RETURN_NULL();
php_di_can_msg_to_zval(return_value, _msg);
di_can_msg_free(&_msg);
}
static void di_minfo_can_interfaces(void) {
struct ifaddrs *ifaddr, *ifa;
int n;
if (getifaddrs(&ifaddr) == -1)
return;
for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
if (strncmp("can", ifa->ifa_name, 3) == 0 ||
strncmp("vcan", ifa->ifa_name, 4) == 0) {
php_info_print_table_row(2, "Interface", ifa->ifa_name);
}
}
freeifaddrs(ifaddr);
}
ZEND_RSRC_DTOR_FUNC(php_di_can_ctx_dtor) {
php_di_can_ctx *c = reinterpret_cast<php_di_can_ctx *>(res->ptr);
if (c)
php_can_free(&c);
}
PHP_MINFO_FUNCTION(di) {
char *version;
spprintf(&version, 0, "%s", APPLICATION_VERSION);
php_info_print_table_start();
php_info_print_table_row(2, "libdi-php Version", version);
di_minfo_can_interfaces();
php_info_print_table_end();
}
PHP_MINIT_FUNCTION(di) {
le_di_can_ctx_resid = zend_register_list_destructors_ex(php_di_can_ctx_dtor,
NULL, le_di_can_ctx_name, module_number);
constants_register(module_number);
return SUCCESS;
}
PHP_FUNCTION(di_can_set_nodeid) {
zval *zc;
zval *zv;
struct php_di_can_ctx *c;
if (zend_parse_parameters(static_cast<int>(ZEND_NUM_ARGS()), "rz", &zc, &zv) == FAILURE)
RETURN_NULL();
c = static_cast<php_di_can_ctx *>(zend_fetch_resource(Z_RES_P(zc), le_di_can_ctx_name, le_di_can_ctx_resid));
if (!c)
return;
if (Z_TYPE_P(zv) != IS_STRING)
RETURN_FALSE;
di_can_set_nodeid(&c->ctx, Z_STRVAL(*zv));
RETURN_TRUE;
}
static zend_function_entry di_functions[] = {
PHP_FE(di_version, NULL)
PHP_FE(di_can_list, NULL)
PHP_FE(di_can_open, NULL)
PHP_FE(di_can_close, NULL)
PHP_FE(di_can_recv, NULL)
PHP_FE(di_can_send, NULL)
PHP_FE(di_can_send_empty_reply, NULL)
PHP_FE(di_can_reqrep, NULL)
PHP_FE(di_can_reqrep_with_retry, NULL)
PHP_FE(di_can_send_queue, NULL)
PHP_FE(di_can_send_dequeue, NULL)
PHP_FE(di_can_set_nodeid, NULL)
PHP_FE_END
};
static zend_module_entry di_module_entry = {
STANDARD_MODULE_HEADER,
PHP_DI_PHP_EXTNAME,
di_functions,
PHP_MINIT(di),
NULL,
NULL,
NULL,
PHP_MINFO(di),
extension_version,
STANDARD_MODULE_PROPERTIES
};
ZEND_GET_MODULE(di)