-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This sample demonstrates how to run your JavaScript application as performing some action in response to an event, input or stimulus. Since Sming framework itself has an [event-driven architecture](https://sming.readthedocs.io/en/latest/information/events.html) such JavaScript applications will be able to use all the advantages that Sming has in terms of CPU, RAM and power usage.
- Loading branch information
Showing
10 changed files
with
328 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
##################################################################### | ||
#### Please don't change this file. Use component.mk instead #### | ||
##################################################################### | ||
|
||
ifndef SMING_HOME | ||
$(error SMING_HOME is not set: please configure it as an environment variable) | ||
endif | ||
|
||
include $(SMING_HOME)/project.mk |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
Event_Jsvm | ||
========== | ||
|
||
.. highlight:: cpp | ||
|
||
This sample demonstrates how to run your JavaScript application as performing some action | ||
in response to an event, input or stimulus. | ||
Since Sming framework itself has an :doc:`event-driven architecture </information/events>` | ||
such JavaScript applications will be able to use all the advantages that Sming has in terms of CPU, RAM and power usage. | ||
|
||
JavaScript Code | ||
--------------- | ||
|
||
Similar to Sming this sample calls the ``init`` JavaScript function. And in this function | ||
we register the events that we are interested in. As shown below. | ||
|
||
.. code:: javascript | ||
/** | ||
* This is a function that will be called once. | ||
*/ | ||
function init() { | ||
x = 0; | ||
print('Init: X=' + x); | ||
// This is how we register an event listener in JavaScript | ||
addEventListener("EVENT_TEMP", function(event) { | ||
// An event has a name and multiple params | ||
// the params have string keys and string values | ||
print('Event name: ' + event.name + ', value: ' + event.params['temp']); | ||
}); | ||
// ... | ||
} | ||
The ``addEventListener`` mimics the browser `addEventListener <https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener>`_ function. | ||
It accepts two parameters - a unique event name and a callable function. Similar to the browser function | ||
you can add multiple callable functions to one event. | ||
|
||
C/C++ Code | ||
---------- | ||
|
||
The ``addEventListener`` function is not present in the standard JerryScript VM. In this sample we register it as a built-in JavaScript function. | ||
Excerpt from the ``application.cpp`` file:: | ||
|
||
void startJsvm() | ||
{ | ||
/* | ||
* This is how we register a new function in JavaScript | ||
* that will communicate directly with our C/C++ code. | ||
*/ | ||
jsVm.registerFunction("addEventListener", addEventListener); | ||
// ... | ||
|
||
|
||
The actual implementation of the ``addEventListener`` C++ function is in the ``vm_functions.cpp`` file. | ||
There you will find also the ``triggerEvent`` function which is available only in the C/C++ code. | ||
The latter is used to trigger events from the C/C++ code inside the JavaScript application:: | ||
JsEventData params; | ||
params["temp"]="20"; | ||
triggerEvent("EVENT_TEMP", params); | ||
The code above simulates a temperature sensor sending the temperature in Celsius (C). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
#include <SmingCore.h> | ||
#include <Jsvm.h> | ||
#include <vm_functions.h> | ||
|
||
namespace | ||
{ | ||
|
||
Jsvm jsVm; | ||
Timer tempTimer; | ||
|
||
IMPORT_FSTR(main_snap, PROJECT_DIR "/out/jerryscript/main.js.snap") | ||
|
||
void startJsvm() | ||
{ | ||
/* | ||
* This is how we register a new function in JavaScript | ||
* that will communicate directly with our C/C++ code. | ||
*/ | ||
jsVm.registerFunction("addEventListener", addEventListener); | ||
|
||
if(!jsVm.load(main_snap)) { | ||
debug_e("Failed to load snapshot"); | ||
return; | ||
} | ||
|
||
// Now you can initialize your script by calling the init() JavaScript function | ||
if(!jsVm.runFunction("init")) { | ||
debug_e("Failed executing the init function."); | ||
} | ||
|
||
/* | ||
* Here we trigger every 2 seconds an event inside the JavaScript code. | ||
*/ | ||
tempTimer.initializeMs(2000, TimerDelegate([](){ | ||
JsEventData params; | ||
params["temp"]="20"; | ||
triggerEvent("EVENT_TEMP", params); | ||
})).start(); | ||
} | ||
|
||
} // namespace | ||
|
||
void init() | ||
{ | ||
Serial.begin(SERIAL_BAUD_RATE); // 115200 by default | ||
Serial.systemDebugOutput(true); // Enable debug output to serial | ||
|
||
startJsvm(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
COMPONENT_DEPENDS := jerryscript | ||
|
||
COMPONENT_SRCDIRS += src | ||
COMPONENT_INCDIRS += $(COMPONENT_SRCDIRS)/include | ||
|
||
APP_JS_SOURCE_DIR := files | ||
# APP_JS_SNAP_DIR := out/jerryscript | ||
APP_JS_SNAP_UPDATED := touch app/application.cpp | ||
|
||
# We need more heap to run this sample. The default heap is 1Kilobyte, we will use 2K | ||
JERRY_GLOBAL_HEAP_SIZE := 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
var x; | ||
|
||
function thirdListener(event) { | ||
print('Third listener is a normal function!'); | ||
print('-------------' + (++x) + '------------------'); | ||
} | ||
|
||
/** | ||
* This is a function that will be called once. | ||
*/ | ||
function init() { | ||
x = 0; | ||
print('Init: X=' + x); | ||
|
||
// This is how we register an event listener in JavaScript | ||
addEventListener("EVENT_TEMP", function(event) { | ||
// An event has a name and multiple params | ||
// the params have string keys and string values | ||
print('Event name: ' + event.name + ', value: ' + event.params['temp']); | ||
}); | ||
|
||
addEventListener("EVENT_TEMP", function(event) { | ||
// You can register more than one listener to an event | ||
print('Second listener'); | ||
}); | ||
|
||
// You can also run a normal function as callback instead of | ||
// an anonymous function. | ||
addEventListener("EVENT_TEMP", thirdListener); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/**** | ||
* Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. | ||
* Created 2015 by Skurydin Alexey | ||
* http://github.com/SmingHub/Sming | ||
* All files of the Sming Core are provided under the LGPL v3 license. | ||
* | ||
* jsvm-ext.cpp | ||
* | ||
* @author Nov 2021 - Slavey Karadzhov <slav@attachix.com> | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <jerryscript.h> | ||
#include <WHashMap.h> | ||
#include <WString.h> | ||
|
||
using JsEventData = HashMap<String, String>; | ||
|
||
void triggerEvent(const String& name, const JsEventData& data); | ||
|
||
/** | ||
* External functions that are available to JerryScript | ||
*/ | ||
|
||
/** | ||
* @brief Function to register event listeners | ||
* @code {.javascipt} | ||
* | ||
* event = { | ||
* name => "TEMP_CHANGE", | ||
* params = { | ||
* }, | ||
* "origin" => "The\Creator\Of\The\Event" | ||
* }; | ||
* | ||
* addEventListener("TEMP_CHANGE", function(event) { | ||
* console.log("Got Event" + event.name); | ||
* }); | ||
* | ||
* @endcode | ||
* | ||
*/ | ||
jerry_value_t addEventListener(const jerry_value_t function_obj, const jerry_value_t this_val, | ||
const jerry_value_t args_p[], const jerry_length_t args_cnt); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
#include "include/vm_functions.h" | ||
|
||
#include <WVector.h> | ||
#include <debug_progmem.h> | ||
|
||
namespace | ||
{ | ||
using EventList = Vector<jerry_value_t>; | ||
using Events = HashMap<String, EventList>; | ||
|
||
Events events; | ||
|
||
} // namespace | ||
|
||
jerry_value_t addEventListener(const jerry_value_t function_obj, const jerry_value_t this_val, | ||
const jerry_value_t args_p[], const jerry_length_t args_cnt) | ||
{ | ||
(void)function_obj; /* unused */ | ||
(void)this_val; /* unused */ | ||
|
||
if(args_cnt != 2) { | ||
return jerry_create_boolean(false); | ||
} | ||
|
||
// First Parameter - Event Name | ||
jerry_value_t value = jerry_value_to_string(args_p[0]); | ||
|
||
jerry_size_t valueSize = jerry_get_string_size(value); | ||
jerry_char_t eventName[valueSize + 1]; | ||
|
||
jerry_string_to_char_buffer(value, eventName, valueSize); | ||
eventName[valueSize] = '\0'; | ||
|
||
jerry_release_value(value); | ||
|
||
// Second parameter -> callable | ||
auto jsFunction = args_p[1]; | ||
|
||
if(!jerry_value_is_function(jsFunction)) { | ||
debug_e("Second parameter is not a function!"); | ||
|
||
return jerry_create_boolean(false); | ||
} | ||
|
||
events[reinterpret_cast<char*>(eventName)].addElement(jerry_value_to_object(jsFunction)); | ||
|
||
return jerry_create_boolean(true); | ||
} | ||
|
||
void triggerEvent(const String& name, const JsEventData& data) | ||
{ | ||
if(!events.contains(name)) { | ||
return; | ||
} | ||
|
||
/* | ||
* Event = { | ||
* name: "eventName" | ||
* params: { | ||
* "property1": "value1", | ||
* "property2": "value2" | ||
* ... | ||
* } | ||
* } | ||
*/ | ||
|
||
// Build the event object... | ||
jerry_value_t eventObject = jerry_create_object(); | ||
// Name | ||
jerry_value_t namePropertyName = jerry_create_string((const jerry_char_t*)"name"); | ||
jerry_value_t namePropertyValue = jerry_create_string((const jerry_char_t*)name.c_str()); | ||
jerry_value_t nameProperty = jerry_set_property(eventObject, namePropertyName, namePropertyValue); | ||
// Params | ||
jerry_value_t paramsPropertyName = jerry_create_string((const jerry_char_t*)"params"); | ||
jerry_value_t paramsPropertyValue = jerry_create_object(); | ||
jerry_value_t paramsProperty = jerry_set_property(eventObject, paramsPropertyName, paramsPropertyValue); | ||
|
||
// add params... | ||
Vector<jerry_value_t> props; | ||
for(unsigned i = 0; i < data.count(); i++) { | ||
jerry_value_t key = jerry_create_string(reinterpret_cast<const jerry_char_t*>(data.keyAt(i).c_str())); | ||
jerry_value_t value = jerry_create_string(reinterpret_cast<const jerry_char_t*>(data.valueAt(i).c_str())); | ||
jerry_value_t property = jerry_set_property(paramsPropertyValue, key, value); | ||
props.add(key); | ||
props.add(value); | ||
props.add(property); | ||
} | ||
|
||
jerry_value_t globalObject = jerry_get_global_object(); | ||
|
||
EventList listeners = events[name]; | ||
for(unsigned i = 0; i < listeners.count(); i++) { | ||
jerry_value_t res = jerry_call_function(listeners[i], globalObject, &eventObject, 1); | ||
jerry_release_value(res); | ||
} | ||
|
||
jerry_release_value(globalObject); | ||
|
||
for(unsigned i = 0; i < props.count(); i++) { | ||
jerry_release_value(props[i]); | ||
} | ||
|
||
jerry_release_value(namePropertyName); | ||
jerry_release_value(namePropertyValue); | ||
jerry_release_value(nameProperty); | ||
|
||
jerry_release_value(paramsPropertyName); | ||
jerry_release_value(paramsPropertyValue); | ||
jerry_release_value(paramsProperty); | ||
|
||
jerry_release_value(eventObject); | ||
} |