From 8c960a990cabed32141fe5c8ee5c6441d1d356b2 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 20 Oct 2024 16:00:10 +0300 Subject: [PATCH] merge js upds by jamisonderek & Willy-JL --- applications/system/js_app/application.fam | 22 +- applications/system/js_app/js_thread.c | 11 - .../system/js_app/modules/js_gui/js_gui.c | 21 -- applications/system/js_app/modules/js_i2c.c | 280 ++++++++++++++++++ applications/system/js_app/types/global.d.ts | 15 +- .../system/js_app/types/gui/index.d.ts | 5 - .../system/js_app/types/i2c/index.d.ts | 31 ++ 7 files changed, 333 insertions(+), 52 deletions(-) create mode 100644 applications/system/js_app/modules/js_i2c.c create mode 100644 applications/system/js_app/types/i2c/index.d.ts diff --git a/applications/system/js_app/application.fam b/applications/system/js_app/application.fam index fd9f0fe09..f8f1be13f 100644 --- a/applications/system/js_app/application.fam +++ b/applications/system/js_app/application.fam @@ -30,7 +30,7 @@ App( appid="js_gui", apptype=FlipperAppType.PLUGIN, entry_point="js_gui_ep", - requires=["js_app", "js_event_loop"], + requires=["js_app"], sources=["modules/js_gui/js_gui.c", "modules/js_gui/js_gui_api_table.cpp"], ) @@ -38,7 +38,7 @@ App( appid="js_gui__loading", apptype=FlipperAppType.PLUGIN, entry_point="js_view_loading_ep", - requires=["js_app", "js_gui", "js_event_loop"], + requires=["js_app"], sources=["modules/js_gui/loading.c"], ) @@ -46,7 +46,7 @@ App( appid="js_gui__empty_screen", apptype=FlipperAppType.PLUGIN, entry_point="js_view_empty_screen_ep", - requires=["js_app", "js_gui", "js_event_loop"], + requires=["js_app"], sources=["modules/js_gui/empty_screen.c"], ) @@ -54,7 +54,7 @@ App( appid="js_gui__submenu", apptype=FlipperAppType.PLUGIN, entry_point="js_view_submenu_ep", - requires=["js_app", "js_gui"], + requires=["js_app"], sources=["modules/js_gui/submenu.c"], ) @@ -62,7 +62,7 @@ App( appid="js_gui__text_input", apptype=FlipperAppType.PLUGIN, entry_point="js_view_text_input_ep", - requires=["js_app", "js_gui", "js_event_loop"], + requires=["js_app"], sources=["modules/js_gui/text_input.c"], ) @@ -70,7 +70,7 @@ App( appid="js_gui__byte_input", apptype=FlipperAppType.PLUGIN, entry_point="js_view_byte_input_ep", - requires=["js_app", "js_gui", "js_event_loop"], + requires=["js_app"], sources=["modules/js_gui/byte_input.c"], ) @@ -126,7 +126,7 @@ App( appid="js_gpio", apptype=FlipperAppType.PLUGIN, entry_point="js_gpio_ep", - requires=["js_app", "js_event_loop"], + requires=["js_app"], sources=["modules/js_gpio.c"], ) @@ -185,3 +185,11 @@ App( requires=["js_app"], sources=["modules/js_usbdisk/*.c"], ) + +App( + appid="js_i2c", + apptype=FlipperAppType.PLUGIN, + entry_point="js_i2c_ep", + requires=["js_app"], + sources=["modules/js_i2c.c"], +) diff --git a/applications/system/js_app/js_thread.c b/applications/system/js_app/js_thread.c index 31e9f721e..7a774d324 100644 --- a/applications/system/js_app/js_thread.c +++ b/applications/system/js_app/js_thread.c @@ -196,16 +196,6 @@ static void js_require(struct mjs* mjs) { mjs_return(mjs, req_object); } -static void js_global_to_string(struct mjs* mjs) { - int base = 10; - if(mjs_nargs(mjs) > 1) base = mjs_get_int(mjs, mjs_arg(mjs, 1)); - double num = mjs_get_double(mjs, mjs_arg(mjs, 0)); - char tmp_str[] = "-2147483648"; - itoa(num, tmp_str, base); - mjs_val_t ret = mjs_mk_string(mjs, tmp_str, ~0, true); - mjs_return(mjs, ret); -} - static void js_parse_int(struct mjs* mjs) { const char* str; int32_t base = 10; @@ -268,7 +258,6 @@ static int32_t js_thread(void* arg) { } mjs_set(mjs, global, "print", ~0, MJS_MK_FN(js_print)); mjs_set(mjs, global, "delay", ~0, MJS_MK_FN(js_delay)); - mjs_set(mjs, global, "toString", ~0, MJS_MK_FN(js_global_to_string)); mjs_set(mjs, global, "ffi_address", ~0, MJS_MK_FN(js_ffi_address)); mjs_set(mjs, global, "require", ~0, MJS_MK_FN(js_require)); mjs_set(mjs, global, "parseInt", ~0, MJS_MK_FN(js_parse_int)); diff --git a/applications/system/js_app/modules/js_gui/js_gui.c b/applications/system/js_app/modules/js_gui/js_gui.c index cd87e5c4b..4bd4ccc31 100644 --- a/applications/system/js_app/modules/js_gui/js_gui.c +++ b/applications/system/js_app/modules/js_gui/js_gui.c @@ -260,26 +260,6 @@ static void js_gui_view_set(struct mjs* mjs) { mjs_return(mjs, MJS_UNDEFINED); } -/** - * @brief `View.hasProperty` - */ -static void js_gui_view_has_property(struct mjs* mjs) { - const char* name; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&name)); - JsGuiViewData* data = JS_GET_CONTEXT(mjs); - const JsViewDescriptor* descriptor = data->descriptor; - - for(size_t i = 0; i < descriptor->prop_cnt; i++) { - JsViewPropDescriptor prop = descriptor->props[i]; - if(strcmp(prop.name, name) != 0) continue; - - mjs_return(mjs, mjs_mk_boolean(mjs, true)); - return; - } - - mjs_return(mjs, mjs_mk_boolean(mjs, false)); -} - /** * @brief `View` destructor */ @@ -304,7 +284,6 @@ static mjs_val_t js_gui_make_view(struct mjs* mjs, const JsViewDescriptor* descr // generic view API mjs_val_t view_obj = mjs_mk_object(mjs); mjs_set(mjs, view_obj, "set", ~0, MJS_MK_FN(js_gui_view_set)); - mjs_set(mjs, view_obj, "hasProperty", ~0, MJS_MK_FN(js_gui_view_has_property)); // object data JsGuiViewData* data = malloc(sizeof(JsGuiViewData)); diff --git a/applications/system/js_app/modules/js_i2c.c b/applications/system/js_app/modules/js_i2c.c new file mode 100644 index 000000000..6f105142c --- /dev/null +++ b/applications/system/js_app/modules/js_i2c.c @@ -0,0 +1,280 @@ +#include "../js_modules.h" +#include + +static void ret_bad_args(struct mjs* mjs, const char* error) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "%s", error); + mjs_return(mjs, MJS_UNDEFINED); +} + +static bool check_arg_count_range(struct mjs* mjs, size_t min_count, size_t max_count) { + size_t num_args = mjs_nargs(mjs); + if(num_args < min_count || num_args > max_count) { + ret_bad_args(mjs, "Wrong argument count"); + return false; + } + return true; +} + +static void js_i2c_is_device_ready(struct mjs* mjs) { + if(!check_arg_count_range(mjs, 1, 2)) return; + + mjs_val_t addr_arg = mjs_arg(mjs, 0); + if(!mjs_is_number(addr_arg)) { + ret_bad_args(mjs, "Addr must be a number"); + return; + } + uint32_t addr = mjs_get_int32(mjs, addr_arg); + + uint32_t timeout = 1; + if(mjs_nargs(mjs) > 1) { // Timeout is optional argument + mjs_val_t timeout_arg = mjs_arg(mjs, 1); + if(!mjs_is_number(timeout_arg)) { + ret_bad_args(mjs, "Timeout must be a number"); + return; + } + timeout = mjs_get_int32(mjs, timeout_arg); + } + + furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); + bool ready = furi_hal_i2c_is_device_ready(&furi_hal_i2c_handle_external, addr, timeout); + furi_hal_i2c_release(&furi_hal_i2c_handle_external); + + mjs_return(mjs, mjs_mk_boolean(mjs, ready)); +} + +static void js_i2c_write(struct mjs* mjs) { + if(!check_arg_count_range(mjs, 2, 3)) return; + + mjs_val_t addr_arg = mjs_arg(mjs, 0); + if(!mjs_is_number(addr_arg)) { + ret_bad_args(mjs, "Addr must be a number"); + return; + } + uint32_t addr = mjs_get_int32(mjs, addr_arg); + + mjs_val_t tx_buf_arg = mjs_arg(mjs, 1); + bool tx_buf_was_allocated = false; + uint8_t* tx_buf = NULL; + size_t tx_len = 0; + if(mjs_is_array(tx_buf_arg)) { + tx_len = mjs_array_length(mjs, tx_buf_arg); + if(tx_len == 0) { + ret_bad_args(mjs, "Data array must not be empty"); + return; + } + tx_buf = malloc(tx_len); + tx_buf_was_allocated = true; + for(size_t i = 0; i < tx_len; i++) { + mjs_val_t val = mjs_array_get(mjs, tx_buf_arg, i); + if(!mjs_is_number(val)) { + ret_bad_args(mjs, "Data array must contain only numbers"); + free(tx_buf); + return; + } + uint32_t byte_val = mjs_get_int32(mjs, val); + if(byte_val > 0xFF) { + ret_bad_args(mjs, "Data array values must be 0-255"); + free(tx_buf); + return; + } + tx_buf[i] = byte_val; + } + } else if(mjs_is_typed_array(tx_buf_arg)) { + mjs_val_t array_buf = tx_buf_arg; + if(mjs_is_data_view(tx_buf_arg)) { + array_buf = mjs_dataview_get_buf(mjs, tx_buf_arg); + } + tx_buf = (uint8_t*)mjs_array_buf_get_ptr(mjs, array_buf, &tx_len); + if(tx_len == 0) { + ret_bad_args(mjs, "Data array must not be empty"); + return; + } + } else { + ret_bad_args(mjs, "Data must be an array, arraybuf or dataview"); + return; + } + + uint32_t timeout = 1; + if(mjs_nargs(mjs) > 2) { // Timeout is optional argument + mjs_val_t timeout_arg = mjs_arg(mjs, 2); + if(!mjs_is_number(timeout_arg)) { + ret_bad_args(mjs, "Timeout must be a number"); + if(tx_buf_was_allocated) free(tx_buf); + return; + } + timeout = mjs_get_int32(mjs, timeout_arg); + } + + furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); + bool result = furi_hal_i2c_tx(&furi_hal_i2c_handle_external, addr, tx_buf, tx_len, timeout); + furi_hal_i2c_release(&furi_hal_i2c_handle_external); + + if(tx_buf_was_allocated) free(tx_buf); + mjs_return(mjs, mjs_mk_boolean(mjs, result)); +} + +static void js_i2c_read(struct mjs* mjs) { + if(!check_arg_count_range(mjs, 2, 3)) return; + + mjs_val_t addr_arg = mjs_arg(mjs, 0); + if(!mjs_is_number(addr_arg)) { + ret_bad_args(mjs, "Addr must be a number"); + return; + } + uint32_t addr = mjs_get_int32(mjs, addr_arg); + + mjs_val_t rx_len_arg = mjs_arg(mjs, 1); + if(!mjs_is_number(rx_len_arg)) { + ret_bad_args(mjs, "Length must be a number"); + return; + } + size_t rx_len = mjs_get_int32(mjs, rx_len_arg); + if(rx_len == 0) { + ret_bad_args(mjs, "Length must not zero"); + return; + } + uint8_t* rx_buf = malloc(rx_len); + + uint32_t timeout = 1; + if(mjs_nargs(mjs) > 2) { // Timeout is optional argument + mjs_val_t timeout_arg = mjs_arg(mjs, 2); + if(!mjs_is_number(timeout_arg)) { + ret_bad_args(mjs, "Timeout must be a number"); + free(rx_buf); + return; + } + timeout = mjs_get_int32(mjs, timeout_arg); + } + + furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); + bool result = furi_hal_i2c_rx(&furi_hal_i2c_handle_external, addr, rx_buf, rx_len, timeout); + furi_hal_i2c_release(&furi_hal_i2c_handle_external); + + mjs_val_t ret = MJS_UNDEFINED; + if(result) { + ret = mjs_mk_array_buf(mjs, (char*)rx_buf, rx_len); + } + free(rx_buf); + mjs_return(mjs, ret); +} + +static void js_i2c_write_read(struct mjs* mjs) { + if(!check_arg_count_range(mjs, 3, 4)) return; + + mjs_val_t addr_arg = mjs_arg(mjs, 0); + if(!mjs_is_number(addr_arg)) { + ret_bad_args(mjs, "Addr must be a number"); + return; + } + uint32_t addr = mjs_get_int32(mjs, addr_arg); + + mjs_val_t tx_buf_arg = mjs_arg(mjs, 1); + bool tx_buf_was_allocated = false; + uint8_t* tx_buf = NULL; + size_t tx_len = 0; + if(mjs_is_array(tx_buf_arg)) { + tx_len = mjs_array_length(mjs, tx_buf_arg); + if(tx_len == 0) { + ret_bad_args(mjs, "Data array must not be empty"); + return; + } + tx_buf = malloc(tx_len); + tx_buf_was_allocated = true; + for(size_t i = 0; i < tx_len; i++) { + mjs_val_t val = mjs_array_get(mjs, tx_buf_arg, i); + if(!mjs_is_number(val)) { + ret_bad_args(mjs, "Data array must contain only numbers"); + free(tx_buf); + return; + } + uint32_t byte_val = mjs_get_int32(mjs, val); + if(byte_val > 0xFF) { + ret_bad_args(mjs, "Data array values must be 0-255"); + free(tx_buf); + return; + } + tx_buf[i] = byte_val; + } + } else if(mjs_is_typed_array(tx_buf_arg)) { + mjs_val_t array_buf = tx_buf_arg; + if(mjs_is_data_view(tx_buf_arg)) { + array_buf = mjs_dataview_get_buf(mjs, tx_buf_arg); + } + tx_buf = (uint8_t*)mjs_array_buf_get_ptr(mjs, array_buf, &tx_len); + if(tx_len == 0) { + ret_bad_args(mjs, "Data array must not be empty"); + return; + } + } else { + ret_bad_args(mjs, "Data must be an array, arraybuf or dataview"); + return; + } + + mjs_val_t rx_len_arg = mjs_arg(mjs, 2); + if(!mjs_is_number(rx_len_arg)) { + ret_bad_args(mjs, "Length must be a number"); + if(tx_buf_was_allocated) free(tx_buf); + return; + } + size_t rx_len = mjs_get_int32(mjs, rx_len_arg); + if(rx_len == 0) { + ret_bad_args(mjs, "Length must not zero"); + if(tx_buf_was_allocated) free(tx_buf); + return; + } + uint8_t* rx_buf = malloc(rx_len); + + uint32_t timeout = 1; + if(mjs_nargs(mjs) > 3) { // Timeout is optional argument + mjs_val_t timeout_arg = mjs_arg(mjs, 3); + if(!mjs_is_number(timeout_arg)) { + ret_bad_args(mjs, "Timeout must be a number"); + if(tx_buf_was_allocated) free(tx_buf); + free(rx_buf); + return; + } + timeout = mjs_get_int32(mjs, timeout_arg); + } + + furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); + bool result = furi_hal_i2c_trx( + &furi_hal_i2c_handle_external, addr, tx_buf, tx_len, rx_buf, rx_len, timeout); + furi_hal_i2c_release(&furi_hal_i2c_handle_external); + + mjs_val_t ret = MJS_UNDEFINED; + if(result) { + ret = mjs_mk_array_buf(mjs, (char*)rx_buf, rx_len); + } + if(tx_buf_was_allocated) free(tx_buf); + free(rx_buf); + mjs_return(mjs, ret); +} + +static void* js_i2c_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) { + UNUSED(modules); + mjs_val_t i2c_obj = mjs_mk_object(mjs); + mjs_set(mjs, i2c_obj, "isDeviceReady", ~0, MJS_MK_FN(js_i2c_is_device_ready)); + mjs_set(mjs, i2c_obj, "write", ~0, MJS_MK_FN(js_i2c_write)); + mjs_set(mjs, i2c_obj, "read", ~0, MJS_MK_FN(js_i2c_read)); + mjs_set(mjs, i2c_obj, "writeRead", ~0, MJS_MK_FN(js_i2c_write_read)); + *object = i2c_obj; + + return (void*)1; +} + +static const JsModuleDescriptor js_i2c_desc = { + "i2c", + js_i2c_create, + NULL, + NULL, +}; + +static const FlipperAppPluginDescriptor i2c_plugin_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &js_i2c_desc, +}; + +const FlipperAppPluginDescriptor* js_i2c_ep(void) { + return &i2c_plugin_descriptor; +} diff --git a/applications/system/js_app/types/global.d.ts b/applications/system/js_app/types/global.d.ts index 052a2bb6a..d132f89f5 100644 --- a/applications/system/js_app/types/global.d.ts +++ b/applications/system/js_app/types/global.d.ts @@ -11,13 +11,6 @@ declare function delay(ms: number): void; */ declare function print(...args: any[]): void; -/** - * @brief Converts a number to a string - * @param value The number to convert to a string - * @param base Integer base (`2`...`16`), default: 10 - */ -declare function toString(value: number, base?: number): string; - /** * @brief Converts a string to a number * @param text The string to convert to a number @@ -214,7 +207,13 @@ declare class Boolean { } declare class Function { } -declare class Number { } +declare class Number { + /** + * @brief Converts this number to a string + * @param base Integer base (`2`...`16`), default: 10 + */ + toString(base?: number): string; +} declare class Object { } diff --git a/applications/system/js_app/types/gui/index.d.ts b/applications/system/js_app/types/gui/index.d.ts index 5efcfdc03..d4cdc6bc9 100644 --- a/applications/system/js_app/types/gui/index.d.ts +++ b/applications/system/js_app/types/gui/index.d.ts @@ -9,11 +9,6 @@ export declare class View { * @param value Value to assign */ set

(property: P, value: Props[P]): void; - /** - * Check if property is available - * @param name Name of the property - */ - hasProperty(name: string): boolean; } export declare class ViewFactory> { diff --git a/applications/system/js_app/types/i2c/index.d.ts b/applications/system/js_app/types/i2c/index.d.ts new file mode 100644 index 000000000..ea0a640cb --- /dev/null +++ b/applications/system/js_app/types/i2c/index.d.ts @@ -0,0 +1,31 @@ +/** + * @brief Check if there is an I2C device ready on the bus + * @param address The device address to check + * @param timeout Timeout in milliseconds + */ +export declare function isDeviceReady(address: number, timeout?: number): boolean; + +/** + * @brief Write data to I2C device and return success status + * @param address The device address to write to + * @param data The data to write to the device + * @param timeout Timeout in milliseconds + */ +export declare function write(address: number, data: number[] | ArrayBuffer, timeout?: number): boolean; + +/** + * @brief Read data from I2C device or return undefined on failure + * @param address The device address to read from + * @param length How many bytes to read + * @param timeout Timeout in milliseconds + */ +export declare function read(address: number, length: number, timeout?: number): ArrayBuffer | undefined; + +/** + * @brief Write data then read from I2C device or return undefined on failure + * @param address The device address to talk to + * @param writeData The data to write to the device + * @param readLength How many bytes to read + * @param timeout Timeout in milliseconds + */ +export declare function writeRead(address: number, writeData: number[] | ArrayBuffer, readLength: number, timeout?: number): ArrayBuffer | undefined;