I am trying to implement event listeners. Node js will subscribe to the events (using C++ function). When the event occurs, C++ should inform node js.
JS code:
pal.subscribeEvent("ONSTATECHANGE", function(a) { console.log("Event received for ONSTATECHAN";});
function kcb (a) {
console.log("KCB .....ONSTATECHAN"+JSON.stringify(a));
}
pal.subscribeEvent("ONSTATECHANGE", kcb);
C++ code:
struct deviceNapi {
Napi::Env env; // to store JS Function Env
Napi::Function jsCallback; // to Store JS function
};
//std::multimap<int, Napi::Function> deviceEventMgmt;
std::multimap<int, struct deviceNapi> deviceEventMgmt;
Napi::Value PAL::subscribeEvent(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
int eventID = info[0].As<Napi::Number>().Int32Value();
deviceNapi obj;
obj.env = env;
obj.jsCallback = info[1].As<Napi::Function>();
deviceEventMgmt.insert(std::make_pair(eventID, obj));
/*Napi::Function jsCallback = info[1].As<Napi::Function>();
deviceEventMgmt.insert(std::make_pair(eventID, jsCallback));*/
}
void ProcessDeviceEvent(int evt, DeviceEvtData data) {
switch(evt) {
case ONSTATECHANGE:
for(auto it = deviceEventMgmt.begin(); it != deviceEventMgmt.end(); it ++) {
if(it->first == ENUM_VERIZON_DEVICE_PENDING_RESET) {
Napi::Env env = it.second.env;
Napi::Object ret = Napi::Object::New(env);
ret.Set("reason", Napi::String::New(env, data.resetReason));
ret.Set("time", Napi::Number::New(env, data.seconds));
it->second.jsCallback.Call(ret);
}
}
/*for(auto it = deviceEventMgmt.begin(); it != deviceEventMgmt.end(); it ++) {
if(it->first == ENUM_VERIZON_DEVICE_PENDING_RESET) {
it->second.Call();
}
}*/
break;
default:
std::cout << "ProcessDeviceEvent : Currently not handling evt : " << evt << std::endl;
break;
}
}
C++ code is throwing below errors while compiling
../PAL.cpp: In member function 'Napi::Value PAL:: subscribeEvent(const Napi::CallbackInfo&)':
../PAL.cpp:1915:13: error: use of deleted function 'deviceNapi::deviceNapi()'
deviceNapi obj;
^~~
../PAL.cpp:1903:8: note: 'deviceNapi::deviceNapi()' is implicitly deleted because the default definition would be ill-formed:
struct deviceNapi {
^~~~~~~~~~
../PAL.cpp:1903:8: error: no matching function for call to 'Napi::Env::Env()'
Can any one please help me to how to fix this problem of storing Node function env into C++ map. I need to save the env so that I can send the response when calling Node callback.
Napi::Env doesn't have a default constructor so your deviceNapi struct's default constructor cannot initialise the env member so the compiler didn't generate it, but you try to call it on this line:
deviceNapi obj;
You can fix this by initialising it like this:
deviceNapi obj = {env, info[1].As<Napi::Function>()};
Related
Assuming I have this proto
message inner_body1{
... // some attr
}
message inner_body2{
... // some attr
}
message body {
oneof inner{
inner_body1 = 1;
inner_body2 = 2;
}
}
message head {
... // some attr
}
message pkg{
head h = 1;
body b = 2;
}
And I design a function like this
void SendPkg(proto::Message& data)
{
pkg p;
auto head = p.mutable_head();
head->fillsomething(); // not important
// My question is, if 'data' is definitely one of the message type defined in 'body'(e.g. 'inner_body1')
// How can I put 'data' into pkg's body field?
}
My question is, if 'data' is definitely one of the message type defined in 'body'(e.g. 'inner_body1')
How can I put 'data' into pkg's body field?
update:
I have tried this way
void SendPkg(proto::Message& data)
{
pkg p;
auto head = p.mutable_head();
head->fillsomething(); // not important
auto body = p.mutable_body();
const Descriptor* desc = data.GetDescriptor();
if (desc.name() == "inner_body1")
{
auto body1 = body->mutable_innerbody1();
body1.CopyFrom(data);
}
else
{
auto body2 = body->mutable_innerbody2();
body2.CopyFrom(data);
}
}
this may works. But the fallback is obviously. I have to maintain this ugly string mapping and it running effienciency is low.
Is there any way could achieve this more elegant?
I have a fairly good template (as in snippet of code) I pull out whenever I need a singleton class. I am now trying to apply it within my project to allow me to control a single instance of a web server. I can make a web server without encasing it in my class. When I try to encase it within the class I'm apparently too unskilled to pull it off.
I've tried the obvious Googling and searching here. I've read relevant posts. I am sure this does not mean I have a unique problem, just that I've not figured out the right way to fix it. Here's what I am working with:
webserver.h:
#include <ESP8266WebServer.h>
#include <FS.h>
class WebServer {
private:
// Singleton Declarations
static bool instanceFlag;
static WebServer *single;
WebServer() {}
// Other Declarations
FS *filesystem;
ESP8266WebServer server();
String getContentType(String);
bool handleFileRead(String);
public:
// Singleton Declarations
static WebServer* getInstance();
~WebServer() {instanceFlag = false;}
// Other Declarations
void initialize(int);
void handleLoop();
};
webserver.cpp:
#include "webserver.h"
bool WebServer::instanceFlag = false;
WebServer* WebServer::single = NULL;
WebServer* WebServer::getInstance() {
if(!instanceFlag) {
single = new WebServer();
instanceFlag = true;
return single;
} else {
return single;
}
}
void WebServer::initialize (int port) {
ESP8266WebServer server(port);
FS *filesystem;
filesystem->begin();
Serial.print("Open: http://");
Serial.print(WiFi.hostname().c_str());
Serial.println(".local");
server.onNotFound([]() {
if (!single->handleFileRead(single->server.uri())) {
single->server.send(404, "text/plain", "404: File not found.");
}
});
server.begin();
Serial.print("HTTP server started on port ");
Serial.print(port);
Serial.println(".");
}
String WebServer::getContentType(String filename) {
if (single->server.hasArg("download")) {
return "application/octet-stream";
} else if (filename.endsWith(".htm")) {
return "text/html";
} else if (filename.endsWith(".html")) {
return "text/html";
} else if (filename.endsWith(".css")) {
return "text/css";
} else if (filename.endsWith(".js")) {
return "application/javascript";
} else if (filename.endsWith(".png")) {
return "image/png";
} else if (filename.endsWith(".gif")) {
return "image/gif";
} else if (filename.endsWith(".jpg")) {
return "image/jpeg";
} else if (filename.endsWith(".ico")) {
return "image/x-icon";
} else if (filename.endsWith(".xml")) {
return "text/xml";
} else if (filename.endsWith(".pdf")) {
return "application/x-pdf";
} else if (filename.endsWith(".zip")) {
return "application/x-zip";
} else if (filename.endsWith(".gz")) {
return "application/x-gzip";
} else {
return "text/plain";
}
}
bool WebServer::handleFileRead(String path) {
Serial.println("handleFileRead: " + path);
if (path.endsWith("/")) {
path += "index.htm";
}
String contentType = getContentType(path);
String pathWithGz = path + ".gz";
if (filesystem->exists(pathWithGz) || filesystem->exists(path)) {
if (filesystem->exists(pathWithGz)) {
path += ".gz";
}
File file = filesystem->open(path, "r");
single->server.streamFile(file, contentType);
file.close();
return true;
}
return false;
}
void WebServer::handleLoop() {
single->server.handleClient();
}
The errors I am getting are all similar to the following:
src\webserver.cpp: In member function 'bool WebServer::handleFileRead(String)':
src\webserver.cpp:81:23: error: 'WebServer::single->WebServer::server' does not have class type
single->server.streamFile(file, contentType);
I get the idea of "does not have a class type", I just have no idea what it means here. In my mind, "single" is a pointer to the class so I'm unclear what that reference is not working.
Obviously, there are ample examples out there how to do a web server without encapsulating it. Other things I need to do for this project lend itself to creating that requirement.
There are some mistake in your code.
In webserver.h:
...
private:
// Singleton Declarations
static bool instanceFlag;
static WebServer *single;
WebServer() {}
// Other Declarations
FS *filesystem;
ESP8266WebServer *server; // <--- remove the parentheses and make it a pointer
String getContentType(String);
bool handleFileRead(String);
...
In webserver.cpp:
In WebServer::initialize I am guessing you want to initialize the class server and filesystem not locals, so it should probably look like this:
void WebServer::initialize (int port) {
server = new ESP8266WebServer(port);
filesystem = new FS();
...
}
And now everywhere you use the server you have to use the -> operator.
For example:
void WebServer::handleLoop() {
single->server->handleClient();
}
Please keep in mind that server and filesystem objects have to be deleted to avoid memory leaks.
EDIT:
You get the new error because FS has no constructor without arguments.
FS's constructor looks like this: FS(FSImplPtr impl) : _impl(impl) { }, here you can see that FSImplPtr is a typedef for std::shared_ptr<FileImpl>, so you need to provide this as a parameter.
It works your way, because SPIFFS's existence is declared here and is of type FS.
If you want to use SPIFFS, you have to use it like this: filesystem = &SPIFFS;, not like you mentioned in the comments (FS* filesystem = &SPIFFS;) because your way creates a new temporary variable named filesystem, and probably you expect to initiate the filesystem in the class, not a local one.
I have one problem. In c++ app, I am using sd-bus and signal does not call my callback function.
I hooked to org.freedesktop.login1, interface is org.freedesktop.DBus.Properties, member is PropertiesChanged and path is /org/freedesktop/login1/seat/seat0
In my connect method I have this:
sd_bus_add_match(m_bus, NULL, "interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path='/org/freedesktop/login1/seat/seat0',type='signal'", on_properties_changed, NULL)
On properties changed method is this:
static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
printf("got some signal");
}
So, when I ran this program, I also ran following command in cmd:ΒΈ
gdbus monitor --system --dest org.freedesktop.login1 --object-path /org/freedesktop/login1/seat/seat0
However, when I switch from userA to userB, I got following line in cmd window:
/org/freedesktop/login1/seat/seat0: org.freedesktop.DBus.Properties.PropertiesChanged ('org.freedesktop.login1.Seat', {'ActiveSession': <('c7', objectpath '/org/freedesktop/login1/session/c7')>}, #as [])
Also when I tried this
busctl --system --match "interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path='/org/freedesktop/login1/seat/seat0',type='signal' monitor
then I also get proper response
Type=signal Endian=l Flags=1 Version=1 Priority=0 Cookie=2281
Sender=:1.0 Path=/org/freedesktop/login1/seat/seat0 Interface=org.freedesktop.DBus.Properties Member=PropertiesChanged
UniqueName=:1.0
MESSAGE "sa{sv}as" {
STRING "org.freedesktop.login1.Seat";
ARRAY "{sv}" {
DICT_ENTRY "sv" {
STRING "ActiveSession";
VARIANT "(so)" {
STRUCT "so" {
STRING "c2";
OBJECT_PATH "/org/freedesktop/login1/session/c2";
};
};
};
};
ARRAY "s" {
};
};
But in c++ that callback function is not called. Any idea why is it not called?
I am using Ubuntu 16.04 and my systemd version is 229.
I found a solution to this problem. Problem was that I wasn't hooked to any event loop.
So I created new function run() and in this function I say this:
while(m_running) {
sd_bus_message *m = NULL;
r = sd_bus_process(m_bus, &m);
if (r < 0) {
//error handling
}
r = sd_bus_wait(m_bus, (uint64_t)-1);
if (r < 0) {
//error handling
}
}
and now I call this function after I connect to signal, and callback function of sd_bus_add_match is normally called
I'm work on next version of v8-profiler and now I want to simplify it.
I see that my class (ProfileNode) don't needs hidden link to internal object (v8::CpuProfileNode), because my class don't uses internal object methods.
Can I delete getter methods and replace it like the example below?
Is it wrong realisation?
Is it hight memory usage?
Or it's OK?
(I'm novice in c++ and v8 library)
(Please ignore version uncompatibility - it's not a part of question)
This is a part of current profile_node.cc
Persistent<ObjectTemplate> ProfileNode::node_template_;
void ProfileNode::Initialize() {
Local<ObjectTemplate> tpl = NanNewLocal<ObjectTemplate>(ObjectTemplate::New());
NanAssignPersistent(ObjectTemplate, node_template_, tpl);
tpl->SetInternalFieldCount(1);
tpl->SetAccessor(String::New("functionName"), ProfileNode::GetFunctionName);
...
}
NAN_GETTER(ProfileNode::GetFunctionName) {
NanScope();
Local<Object> self = args.Holder();
void* ptr = NanGetInternalFieldPointer(self, 0);
Handle<String> fname = static_cast<CpuProfileNode*>(ptr)->GetFunctionName();
NanReturnValue(fname);
}
...
Handle<Value> ProfileNode::New(const CpuProfileNode* node) {
NanScope();
if (node_template_.IsEmpty()) {
ProfileNode::Initialize();
}
if(!node) {
return Undefined();
}
else {
Local<Object> obj = NanPersistentToLocal(node_template_)->NewInstance();
NanSetInternalFieldPointer(obj, 0, const_cast<CpuProfileNode*>(node));
return obj;
}
}
After refactoring
//ProfileNode::Initialize deleted
//ProfileNode::GetFunctionName deleted
Handle<Value> ProfileNode::New(const CpuProfileNode* node) {
NanScope();
if(!node) {
return NanUndefined();
}
else {
//Create new simplest object, instead of new instance of object template
Local<Object> obj = NanNew<Object>();
//Append the value to this object
obj->Set(String::New("functionName"), node->GetFunctionName());
//Delete internal link.
//NanSetInternalFieldPointer(obj, 0, const_cast<CpuProfileNode*>(node));
return obj;
}
}
Ive been staring at my code and I can't figure out why on earth my constructor is not gettign called.
It's just ignoring my constructor completely (i've check with stepping with debugger).
Here's my testapp:
using namespace MyEngine;
int _tmain(int argc, _TCHAR* argv[])
{
TestManager* testMgr = new TestManager();
testMgr->RunAllTests();
delete testMgr;
getchar();
return 0;
}
TestManager.h:
namespace MyEngine
{
class TestManager
{
public:
TestManager();
TestManager(uint64_t RepeatTimes);
~TestManager();
bool RunAllTests();
bool RunMemoryTests();
private:
Engine* mEngine;
ILogManager* mLogger;
MemoryTestManager* mMemTestMgr;
uint64_t mRepeatTimes;
};
}
and TestManager.cpp
namespace MyEngine
{
TestManager::TestManager()
{
TestManager(1);
}
TestManager::TestManager(uint64_t RepeatTimes)
{
if (RepeatTimes>0)
mRepeatTimes = RepeatTimes;
else
{
mRepeatTimes = 1;
}
mEngine = Engine::GetEngine();
mMemTestMgr = new MemoryTestManager();
}
TestManager::~TestManager()
{
}
/* Runs all testing modules */
bool TestManager::RunAllTests()
{
bool res = true;
/* Init Engine */
if(mEngine->Init(0,0,0))
{
res = true;
res && mEngine->GetRenderManager()->Init();
res && mLogger->Init(true,true);
res && mEngine->GetMemoryManager()->Init(false);
}
else
return false;
/* Start Engine */
mEngine->Start();
/* Get logger */
mLogger = mEngine->GetLogger();
/* Run Memory Tests */
res &= RunMemoryTests();
if (res)
mLogger->LogInfo("TEST: TESTING SUCCESSFULL");
else
mLogger->LogError("TEST: TESTING FAILED");
return res;
}
/* Runs all memory tests */
bool TestManager::RunMemoryTests()
{
bool res = true;
res &= mMemTestMgr->AllocateTest();
res &= mMemTestMgr->ReferenceTest();
if (res)
mLogger->LogInfo("TEST: RunMemoryTests SUCCESSFULL");
else
mLogger->LogError("TEST: RunMemoryTests FAILED");
return res;
}
}
You cant call another constructor from the same class. I'd refactor the init code into a separate method and call it from both constructors:
namespace MyEngine
{
TestManager::TestManager()
{
Init(1);
}
TestManager::TestManager(uint64_t RepeatTimes)
{
Init(RepeatTimes);
}
void TestManager::Init(uint64_t RepeatTimes)
{
if (RepeatTimes>0)
mRepeatTimes = RepeatTimes;
else
{
mRepeatTimes = 1;
}
mEngine = Engine::GetEngine();
mMemTestMgr = new MemoryTestManager();
}
}
When you call TestManager(1); inside your TestManager::TestManager() constructor, you're creating another instance of TestManager, using the constructor TestManager::TestManager(uint64_t).
You can't do this on C++, you have to create either a init method, were you set the instance variables to whatever you want, or use optional parameters:
TestManager(uint64_t RepeatTimes = 0);
Then, if you create an instance of TestManager without arguments, you will be calling the TestManager::TestManager(uint64_t)constructor with 0 as the uint64_t argument.
you can't call a default constructor from a overloaded constructor. Why don't you simply create you object like this:
TestManager* testMgr = new TestManager(1);
Using a default argument (as per fontanini's answer) will do what you want in this case.
But if this is a simplified example and you really do want to delegate to another constructor, then that's not possible in C++03 - the line TestManager(1) just constructs a temporary object which goes unused (and the line will probably be optimized out unless the TestManager(uint64_t) constructor has side effects).
However, you can do what you're aiming for here with a C++11 compiler and the following syntax:
TestManager::TestManager() :
TestManager(1)
{
}