<commit_before>// -*- C++ -*-
/*
 * Copyright (C) 2014 <NAME>.
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */ 

#include <node.h>
#include "helium_wrapper.h"

#include <iostream>
#include <vector>

using namespace v8;
using node::ObjectWrap;

void UV_NOP(uv_work_t* req) { /* No operation */ }

// http://stackoverflow.com/questions/10507323/shortest-way-one-liner-to-get-a-default-argument-out-of-a-v8-function
char *get(v8::Local<v8::Value> value, const char *fallback = "") {
    if (value->IsString()) {
        v8::String::AsciiValue string(value);
        char *str = (char *) malloc(string.length() + 1);
        strcpy(str, *string);
        return str;
    }
    char *str = (char *) malloc(strlen(fallback) + 1);
    strcpy(str, fallback);
    return str;
}

 void hexdump(char *buffer, size_t length)
 {
  int i;
  for (i=0;i<(int)length;i++) {
		printf("%02x ",buffer[i] & 0xff);
   }
  printf("\nASCII: \n");
  for (i=0;i<(int)length;i++)
	{
	if (buffer[i] < 32) printf(".");
	else printf("%c",buffer[i]);
	}
  printf("\n");
 }


Persistent<Function> Helium::constructor;


/**
 * Create the Helium connection object. Just prepares the API
 * using libhelium
 */
Helium::Helium() {
	helium_logging_start();
	// Initialize the connection:
	conn_ = helium_alloc();
	helium_set_context(conn_, this);
	
	std::cout << "Created Helium object\n";

}

/**
 * Opens the connection. Right now hardcodes an ipv4 proxy, but
 * this should be made optional before release.
 */
int Helium::open() {
		return helium_open(conn_, "r01.sjc.helium.io", incoming_msg);
}

/**
 * Close the connection and frees up resources.
 */
void Helium::close() {

	helium_close(conn_);
 	helium_free(conn_);	
}

/**
 * Subscribe to a modem: needs the MAC address and public token
 * for the call to be successful.
 */
int Helium::subscribe(uint64_t mac, char* base64) {

	helium_token_t token;
	helium_base64_token_decode((const unsigned char *)base64, strlen(base64), token);

	return helium_subscribe(conn_, mac, token);

}

/**
 * Unsubscribe from messages from a moden. Only the MAC address
 * is needed there.
 */
int Helium::unsubscribe(uint64_t mac) {

	return helium_unsubscribe(conn_, mac);
}

/**
 * This method is called whenever a message arrives from a modem.
 * It generates a Node callback that is meant to be caught by the Javascript
 * layer of this addon, to dispatch to listeners - the philosophy is that it is
 * considered as best practice to do as little as necessary in C++ and let the JS
 * layer handle as much as possible.
 */
void Helium::incoming_msg(const helium_connection_t *conn, uint64_t mac, char * const message, size_t count)
{

	uv_work_t* uv = new uv_work_t;

  	Helium *self = const_cast<Helium*>(static_cast<const Helium*>(helium_get_context(conn)));

  	// We need to make a copy of the message for use down the line:
	std::cout << "Preparing message\n";
	struct helium_message* req = (struct helium_message *)malloc(sizeof(*req) + sizeof(req->message)*count);

	req->mac = mac;
	req->helium = self;
	req->count = count;
	// memcpy(req->message, message, count);
	std::cout << "Preparing message (2)\n";
	uv->data = req;

	// This will call HandleMessageDone in the nodejs thread, where we ca create V8 objects
	int r = uv_queue_work(uv_default_loop(), uv, UV_NOP, (uv_after_work_cb)HandleMessageDone);
	if (r != 0) {
    	std::cout << "error while queuing helium message callback";
    	delete uv;
  	}
    printf("Connection %p received message from device %" PRIx64 ".\n", (void *)conn, mac);
    hexdump(message,count);

}

/**
 * Callback after incoming_msg, but this one is within the NodeJS thread context, so we have
 * access to our scope, V8 objects, and so on.
 */
void Helium::HandleMessageDone(uv_work_t *req) {

	helium_message* msg = static_cast<helium_message* >(req->data);

	NanScope();

	std::cout << "Preparing javascript callback\n";
	msg->helium->sendCallback(msg->mac, NULL);

  	free(msg);
}

void Helium::sendCallback(uint64_t mac, char* message) {
	Local<Value> args[2] = { String::New("message"), *Undefined()};

	std::cout << "Preparing javascript callback (2)\n";
	//convertMessageToJS(msg->message, args);
	args[1] = String::New("MAC");

  	node::MakeCallback(self, "emit", 2, args);

}

/**
 * Converts a Helium message (a char*) into a proper JS "Buffer" object in Node.
 * See http://www.samcday.com.au/blog/2011/03/03/creating-a-proper-buffer-in-a-node-c-addon/
 *
 */
void Helium::convertMessageToJS(char* msg, v8::Local<v8::Value> args[]) {

	int length = strlen(msg);

	// Allocate binary buffer:
	node::Buffer *slowBuffer = node::Buffer::New(length);
	memcpy(node::Buffer::Data(slowBuffer), msg, length);

    //Get "fast" node Buffer constructor
    Local<Function> nodeBufConstructor = Local<Function>::Cast(
      Context::GetCurrent()->Global()->Get(String::New("Buffer") )
    );
    //Construct a new Buffer
    // First argument is the JS object Handle for the SlowBuffer.
	// Second arg is the length of the SlowBuffer.
	// Third arg is the offset in the SlowBuffer we want the "Fast"Buffer to start at.
    Handle<Value> nodeBufferArgs[3] = { slowBuffer->handle_,
    									Integer::New(length),
    									Integer::New(0) };
    Local<Object> buf = nodeBufConstructor->NewInstance(3, nodeBufferArgs);
    args[1] = buf;
}



Helium::~Helium() {
	close();
}

void Helium::Init(Handle<Object> target) {

	HandleScope scope;

  // Prepare constructor template
  Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(Helium::New);
  tpl->SetClassName(String::New("Helium"));

  // How many methods our class has
  tpl->InstanceTemplate()->SetInternalFieldCount(4);

  // Prototypes
  NODE_SET_PROTOTYPE_METHOD(tpl, "open", Open);
  NODE_SET_PROTOTYPE_METHOD(tpl, "close", Close);<commit_msg>First working version, sends MAC and message back in the event.<commit_after>// -*- C++ -*-
/*
 * Copyright (C) 2014 <NAME>.
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */ 

#include <node.h>
#include "helium_wrapper.h"

#include <iostream>
#include <stdio.h>

using namespace v8;
using node::ObjectWrap;

void UV_NOP(uv_work_t* req) { /* No operation */ }

// http://stackoverflow.com/questions/10507323/shortest-way-one-liner-to-get-a-default-argument-out-of-a-v8-function
char *get(v8::Local<v8::Value> value, const char *fallback = "") {
    if (value->IsString()) {
        v8::String::AsciiValue string(value);
        char *str = (char *) malloc(string.length() + 1);
        strcpy(str, *string);
        return str;
    }
    char *str = (char *) malloc(strlen(fallback) + 1);
    strcpy(str, fallback);
    return str;
}

 void hexdump(char *buffer, size_t length)
 {
  int i;
  for (i=0;i<(int)length;i++) {
		printf("%02x ",buffer[i] & 0xff);
   }
  printf("\nASCII: \n");
  for (i=0;i<(int)length;i++)
	{
	if (buffer[i] < 32) printf(".");
	else printf("%c",buffer[i]);
	}
  printf("\n");
 }


Persistent<Function> Helium::constructor;


/**
 * Create the Helium connection object. Just prepares the API
 * using libhelium
 */
Helium::Helium() {
	helium_logging_start();
	// Initialize the connection:
	conn_ = helium_alloc();
	helium_set_context(conn_, this);
	
	std::cout << "Created Helium object\n";

}

/**
 * Opens the connection. Right now hardcodes an ipv4 proxy, but
 * this should be made optional before release.
 */
int Helium::open() {
		return helium_open(conn_, "r01.sjc.helium.io", incoming_msg);
}

/**
 * Close the connection and frees up resources.
 */
void Helium::close() {

	helium_close(conn_);
 	helium_free(conn_);	
}

/**
 * Subscribe to a modem: needs the MAC address and public token
 * for the call to be successful.
 */
int Helium::subscribe(uint64_t mac, char* base64) {

	helium_token_t token;
	helium_base64_token_decode((const unsigned char *)base64, strlen(base64), token);

	return helium_subscribe(conn_, mac, token);

}

/**
 * Unsubscribe from messages from a moden. Only the MAC address
 * is needed there.
 */
int Helium::unsubscribe(uint64_t mac) {

	return helium_unsubscribe(conn_, mac);
}

/**
 * This method is called whenever a message arrives from a modem.
 * It generates a Node callback that is meant to be caught by the Javascript
 * layer of this addon, to dispatch to listeners - the philosophy is that it is
 * considered as best practice to do as little as necessary in C++ and let the JS
 * layer handle as much as possible.
 */
void Helium::incoming_msg(const helium_connection_t *conn, uint64_t mac, char * const message, size_t count)
{

	uv_work_t* uv = new uv_work_t;

  	Helium *self = const_cast<Helium*>(static_cast<const Helium*>(helium_get_context(conn)));

  	// We need to make a copy of the message for use down the line:
	std::cout << "Preparing message\n";
	struct helium_message* req = (struct helium_message *)malloc(sizeof(*req));
	req->mac = mac;
	req->helium = self;
	req->count = count;
	req->message = (char *)malloc(count);
	memcpy(req->message, message, count);
	std::cout << "Preparing message (2)\n";

	uv->data = req;

	// This will call HandleMessageDone in the nodejs thread, where we ca create V8 objects
	int r = uv_queue_work(uv_default_loop(), uv, UV_NOP, (uv_after_work_cb)HandleMessageDone);
	if (r != 0) {
    	std::cout << "error while queuing helium message callback";
    	delete uv;
  	}

}

/**
 * Callback after incoming_msg, but this one is within the NodeJS thread context, so we have
 * access to our scope, V8 objects, and so on.
 */
void Helium::HandleMessageDone(uv_work_t *req) {

	helium_message* msg = static_cast<helium_message* >(req->data);

	NanScope();

	std::cout << "Preparing javascript callback\n";

	msg->helium->sendCallback(msg->mac, msg->message, msg->count);

	free(msg->message);
  	free(msg);
}

void Helium::sendCallback(uint64_t mac, char * const message, size_t count) {
	std::cout << "Preparing javascript callback (2)\n";
    printf("Received message from device %" PRIx64 ".\n", mac);
    hexdump(message, count);

    // Print the MAC into a hex string
    char macstring[17];
    sprintf(macstring, "%016"PRIx64, mac);

	// First argument is the event name ("message")
	Local<Value> args[2] = { String::New("message"), *Undefined()};

	// Create a Javascript object like this:
	// { mac: "HEX String", message: Buffer}
	Local<Object> resp = Object::New();
	resp->Set(String::NewSymbol("mac"), String::New(macstring));
	Local<Value> buf[1];
	convertMessageToJS(message, count, buf);
	resp->Set(String::NewSymbol("message"), buf[0]);
	args[1] = resp;

  	node::MakeCallback(self, "emit", 2, args);

}

/**
 * Converts a Helium message (a char*) into a proper JS "Buffer" object in Node.
 * See http://www.samcday.com.au/blog/2011/03/03/creating-a-proper-buffer-in-a-node-c-addon/
 *
 */
void Helium::convertMessageToJS(char* msg, size_t length, v8::Local<v8::Value> arg[]) {

	// Allocate binary buffer:
	node::Buffer *slowBuffer = node::Buffer::New(length);
	memcpy(node::Buffer::Data(slowBuffer), msg, length);

    //Get "fast" node Buffer constructor
    Local<Function> nodeBufConstructor = Local<Function>::Cast(
      Context::GetCurrent()->Global()->Get(String::New("Buffer") )
    );
    //Construct a new Buffer
    // First argument is the JS object Handle for the SlowBuffer.
	// Second arg is the length of the SlowBuffer.
	// Third arg is the offset in the SlowBuffer we want the "Fast"Buffer to start at.
    Handle<Value> nodeBufferArgs[3] = { slowBuffer->handle_,
    									Integer::New(length),
    									Integer::New(0) };
    Local<Object> buf = nodeBufConstructor->NewInstance(3, nodeBufferArgs);
    arg[0] = buf;
}



Helium::~Helium() {
	close();
}

void Helium::Init(Handle<Object> target) {

	HandleScope scope;

  // Prepare constructor template
  Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(Helium::New);
  tpl->SetClassName(String::New("Helium"));

  // How many methods our class has
  tpl->InstanceTemplate()->SetInternalFieldCount(4);

  // Prototypes
  NODE_SET_PROTOTYPE_METHOD(tpl, "open", Open);
  NODE_SET_PROTOTYPE_METHOD(tpl, "close", Close);
{'fraction_non_alphanumeric': 0.14612097304404997, 'fraction_numerical': 0.012245233399079554, 'mean_word_length': 4.09376308078694, 'pattern_counts': {'":': 0, '<': 71, '<?xml version=': 0, '>': 83, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1729238', 'n_tokens_mistral': 4281, 'n_tokens_neox': 3994, 'n_words': 1154}