Ich habe eine Anwendung, die eine Reihe von Prozessdaten von einem industriellen Controller liest. Ich möchte diese Daten auf eine Webseite übertragen, wenn sie sich ändert. Zu diesem Zweck habe ich in C++ ein node.js-Addon geschrieben, das die Prozessdaten scannt und versucht, ein Ereignis auszulösen, wenn sich der Datenwert ändert. Alles funktioniert mit dem Addon gut, bis es versucht, ein Ereignis ausgelöst, an welchem Punkt node.js mit dem Fehler beendet:Emittieren eines Ereignisses in Node.js C++ - Addon
undefined:0
TypeError: undefined is not a function
Die CPP, Javascript Shim und Test Javascript unten ist. Alle Einsichten werden sehr geschätzt.
Vielen Dank im Voraus.
node_corelink.cpp
typedef struct CoreLinkValue
{
// pointer to a CTS variant value
CtsVariant* value;
// copy of the last value that was broadcast
CtsVariant lastValue;
} CoreLinkValue;
//
// An event structure for pushing events to node.js
// Requires the javascript shim code in node_corelink.js
//
struct Emitter: ObjectWrap
{
static Handle<Value> New(const Arguments& args);
static Handle<Value> DataChange(const char* topic, CtsVariant* value);
};
//
// Create a message payload based on the variant type and
// initiate sending out on the topic
//
static Handle<Value>
createVariantHandle(CtsVariant* value)
{
Handle<Value> ret;
switch (value->type)
{
case CTSTYPE_BIT:
case CTSTYPE_BYTE:
ret = Integer::New(value->value.byte[0]);
break;
case CTSTYPE_WORD:
ret = Integer::New(value->value.word[0]);
break;
case CTSTYPE_DWORD:
ret = Integer::New(value->value.dword[0]);
break;
case CTSTYPE_WORD64:
ret = Number::New(value->value.word64);
break;
case CTSTYPE_REAL64:
ret = Number::New(value->value.real64);
break;
default:
ret = Undefined();
break;
}
return ret;
}
Handle<Value> Emitter::New(const Arguments& args)
{
HandleScope scope;
assert(args.IsConstructCall());
Emitter* self = new Emitter();
self->Wrap(args.This());
return scope.Close(args.This());
}
// emits DataChange Event
Handle<Value> Emitter::DataChange(const char* topic, CtsVariant* value)
{
HandleScope scope;
Handle<Value> argv[3] = {
String::New("DataChange"), // event name
String::New(topic), // topic argument
createVariantHandle(value) // value argument
};
printf ("C++ Emitting event!\n");
MakeCallback(context_obj_, "emit", 2, argv);
return True();
}
//
// Triggered by the event loop on a regular interval.
// Scans the registered data to see if the latest value has been
// broadcast and does so if needed.
//
void
scan_task(uv_timer_t* timer, int status)
{
std::map<std::string, CoreLinkValue>::iterator it;
bool doUpdate;
for( it = pdos_.begin();
it != pdos_.end();
++it)
{
if (forceRefreshPdos_ == true)
{
//
// An update of this value was requested.
//
doUpdate = true;
}
else if (it->second.value->type != it->second.lastValue.type)
{
//
// If the types don't match, then this variant was obviously
// updated.
//
doUpdate = true;
}
else if (it->second.value->value.word64 != it->second.lastValue.value.word64)
{
//
// Word64 contains all bits of the value. If this value has
// changed, then they've all changed.
//
doUpdate = true;
}
else
{
doUpdate = false;
}
if (doUpdate)
{
it->second.lastValue.value = it->second.value->value;
Emitter::DataChange(it->first.c_str(), it->second.value);
}
}
if (forceRefreshPdos_)
{
forceRefreshPdos_ = false;
printf("Completed refresh all.\n");
}
}
//
// Start the execution of the scan loop
//
int
startScanLoop(void)
{
uv_timer_init(uv_default_loop(), &scanTimer_);
uv_timer_start(
&scanTimer_, // timer instance
&scan_task, // callback function
0, // startup delay (ms)
100); // repeat interval (ms)
return 1;
}
//
// Stop the execution of the scan loop
//
void
stopScanLoop(void)
{
uv_timer_stop(&scanTimer_);
}
//
// Connects to the kernel IPC
//
Handle<Value>
connect(const Arguments& args)
{
HandleScope scope;
...
startScanLoop();
return scope.Close(True());
}
//
// Shuts down the kernel IPC
//
Handle<Value>
close(const Arguments& args)
{
HandleScope scope;
stopScanLoop();
...
return scope.Close(True());
}
//
// Called by node.js to initialize the library.
//
void
init(Handle<Object> target)
{
target->Set(String::NewSymbol("connect"),
FunctionTemplate::New(connect)->GetFunction());
target->Set(String::NewSymbol("close"),
FunctionTemplate::New(close)->GetFunction());
//
// Events interface
//
Local<FunctionTemplate> t = FunctionTemplate::New(Emitter::New);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(String::New("Emitter"));
target->Set(String::NewSymbol("Emitter"), t->GetFunction());
}
NODE_MODULE(node_corelink, init)
node_corelink.js
module.exports = require(__dirname + '/build/Release/node_corelink.node');
var Emitter = require(__dirname + '/build/Release/node_corelink.node').Emitter;
var events = require('events');
inherits(Emitter, events.EventEmitter);
exports.Emitter = Emitter;
// extend prototype
function inherits(target, source) {
for (var k in source.prototype)
target.prototype[k] = source.prototype[k];
}
test.js
process.stdin.resume(); //so the program will not close instantly
process.on('exit', function() {
corelink.close();
console.log('Goodbye!');
});
process.on('SIGINT', function() {
console.log('Got SIGINT.');
process.exit();
});
var corelink = require('./node_corelink');
var Emitter = require('./node_corelink').Emitter;
var e = new Emitter();
e.on('DataChange', function(s) {
console.log('DataChange');
});
corelink.connect();