-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from redwirelabs/setup-cnode-comms
Add basic c-node and supporting services
- Loading branch information
Showing
11 changed files
with
411 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,20 @@ | ||
defmodule Bacnet do | ||
defmodule BACNet do | ||
@moduledoc """ | ||
BACNet client. | ||
""" | ||
|
||
require Logger | ||
|
||
defmodule Device do | ||
@typedoc "Placeholder" | ||
@type t :: term | ||
end | ||
|
||
@spec add_device(device :: Device.t) :: :ok | {:error, term} | ||
def add_device(device) do | ||
GenServer.call({:global, :bacnetd}, {:add_device, device}) | ||
end | ||
|
||
@doc false | ||
defdelegate ei_log(level, term), to: Logger, as: :log | ||
end |
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 |
---|---|---|
@@ -1,15 +1,24 @@ | ||
defmodule Bacnet.Application do | ||
defmodule BACNet.Application do | ||
@moduledoc false | ||
|
||
use Application | ||
|
||
@impl true | ||
def start(_type, _args) do | ||
children = children() | ||
opts = [strategy: :one_for_one, name: Bacnet.Supervisor] | ||
bacnetd_exe = "#{:code.priv_dir(:bacnet)}/bacnetd" | ||
bacnetd_args = [ | ||
"--cookie", Node.get_cookie |> to_string, | ||
"--nodename", Node.self |> to_string, | ||
] | ||
|
||
Supervisor.start_link(children, opts) | ||
end | ||
children = [ | ||
{MuonTrap.Daemon, [bacnetd_exe, bacnetd_args, []]} | ||
] | ||
|
||
defp children(), do: [] | ||
Supervisor.start_link( | ||
children, | ||
strategy: :one_for_one, | ||
name: BACNet.Supervisor | ||
) | ||
end | ||
end |
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 |
---|---|---|
@@ -1,4 +1,4 @@ | ||
defmodule Bacnet.Spec do | ||
defmodule BACNet.Spec do | ||
use ESpec | ||
|
||
specify do: expect true |> to(eq true) | ||
|
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 @@ | ||
#include <getopt.h> | ||
#include <string.h> | ||
|
||
#include "arg.h" | ||
|
||
static const struct option options[] = { | ||
{"nodename", required_argument, 0, 'n'}, | ||
{"cookie", required_argument, 0, 'c'}, | ||
{0, 0, 0, 0}, | ||
}; | ||
|
||
void args_parse(arg_t *args, int argc, char **argv) | ||
{ | ||
int opt; | ||
int index = 0; | ||
|
||
while ((opt = getopt_long(argc, argv, "n:c:", options, &index)) != -1) { | ||
switch (opt) { | ||
case 'n': | ||
strncpy(args->nodename, optarg, sizeof(args->nodename)); | ||
break; | ||
|
||
case 'c': | ||
strncpy(args->cookie, optarg, sizeof(args->cookie)); | ||
break; | ||
|
||
default: | ||
break; | ||
} | ||
} | ||
} |
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,13 @@ | ||
#ifndef ARG_H | ||
#define ARG_H | ||
|
||
#include <ei.h> | ||
|
||
typedef struct { | ||
char nodename[MAXNODELEN + 1]; | ||
char cookie[MAXATOMLEN + 1]; | ||
} arg_t; | ||
|
||
void args_parse(arg_t *args, int argc, char **argv); | ||
|
||
#endif /* ARG_H */ |
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,120 @@ | ||
#include <pthread.h> | ||
#include <stdbool.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
#include "ei_client.h" | ||
|
||
#define OTP_COMPAT_VER 24 | ||
#define CNODE_NAME "bacnetd" | ||
|
||
struct ei_client { | ||
bool ready; | ||
pthread_mutex_t lock; | ||
struct ei_cnode_s cnode; | ||
uint32_t creation; | ||
int port; | ||
int fd; | ||
char nodename[MAXNODELEN + 1]; | ||
char cookie[MAXATOMLEN + 1]; | ||
}; | ||
|
||
static struct ei_client client = { 0 }; | ||
static void ei_free(); | ||
|
||
bool ei_client_config(const char *nodename, const char *cookie) | ||
{ | ||
if (client.ready) | ||
return true; | ||
|
||
if (atexit(ei_free) != 0) | ||
return false; | ||
|
||
strncpy(client.nodename, nodename, sizeof(client.nodename)); | ||
strncpy(client.cookie, cookie, sizeof(client.cookie)); | ||
|
||
ei_init(); | ||
ei_set_compat_rel(OTP_COMPAT_VER); | ||
|
||
pthread_mutex_init(&client.lock, NULL); | ||
client.creation = 0; | ||
|
||
if (ei_connect_init(&client.cnode, CNODE_NAME, cookie, client.creation) < 0) | ||
return false; | ||
|
||
client.creation++; | ||
|
||
client.fd = ei_connect(&client.cnode, client.nodename); | ||
if (client.fd < 0) | ||
return false; | ||
|
||
erlang_pid *pid = ei_self(&client.cnode); | ||
if (ei_global_register(client.fd, CNODE_NAME, pid) == -1) | ||
return false; | ||
|
||
client.ready = true; | ||
|
||
return true; | ||
} | ||
|
||
bool ei_client_send(char *process_name, ei_x_buff *message) | ||
{ | ||
if (!client.ready) | ||
return false; | ||
|
||
pthread_mutex_lock(&client.lock); | ||
int ret = ei_reg_send( | ||
&client.cnode, | ||
client.fd, | ||
process_name, | ||
message->buff, | ||
message->index | ||
); | ||
pthread_mutex_unlock(&client.lock); | ||
|
||
return ret == 0 ? true : false; | ||
} | ||
|
||
bool ei_client_send_to(erlang_pid *pid, ei_x_buff *message) | ||
{ | ||
if (!client.ready) | ||
return false; | ||
|
||
pthread_mutex_lock(&client.lock); | ||
int ret = ei_send(client.fd, pid, message->buff, message->index); | ||
pthread_mutex_unlock(&client.lock); | ||
|
||
return ret == 0 ? true : false; | ||
} | ||
|
||
bool ei_client_call(char *module, char *func, ei_x_buff *args, ei_x_buff *result) | ||
{ | ||
pthread_mutex_lock(&client.lock); | ||
int ret = ei_rpc( | ||
&client.cnode, | ||
client.fd, | ||
module, | ||
func, | ||
args->buff, | ||
args->index, | ||
result | ||
); | ||
pthread_mutex_unlock(&client.lock); | ||
|
||
return ret == -1 ? false : true; | ||
} | ||
|
||
bool ei_client_recv(erlang_msg *meta, ei_x_buff *message) | ||
{ | ||
pthread_mutex_lock(&client.lock); | ||
int ret = ei_xreceive_msg(client.fd, meta, message); | ||
pthread_mutex_unlock(&client.lock); | ||
|
||
return ret == ERL_ERROR ? false : true; | ||
} | ||
|
||
static void ei_free() | ||
{ | ||
if (client.fd > 0) | ||
ei_close_connection(client.fd); | ||
} |
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,12 @@ | ||
#ifndef EI_CLIENT_H | ||
#define EI_CLIENT_H | ||
|
||
#include <ei.h> | ||
|
||
bool ei_client_config(const char *nodename, const char *cookie); | ||
bool ei_client_send(char *process_name, ei_x_buff *message); | ||
bool ei_client_send_to(erlang_pid *pid, ei_x_buff *messageg); | ||
bool ei_client_call(char *module, char *func, ei_x_buff *message, ei_x_buff *out); | ||
bool ei_client_recv(erlang_msg *meta, ei_x_buff *message); | ||
|
||
#endif /* EI_CLIENT_H */ |
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,52 @@ | ||
#include <stdarg.h> | ||
#include <stdbool.h> | ||
|
||
#include "ei_client.h" | ||
#include "ei_log.h" | ||
|
||
#define MAX_LOG_LENGTH 1024 | ||
|
||
static const char *level_to_str(log_level_t level); | ||
|
||
void ei_log(log_level_t level, const char *format, ...) | ||
{ | ||
va_list vargs; | ||
va_start(vargs, format); | ||
|
||
char log_buffer[MAX_LOG_LENGTH]; | ||
int log_length = vsnprintf(log_buffer, sizeof(log_buffer), format, vargs); | ||
|
||
ei_x_buff out; | ||
ei_x_new(&out); | ||
|
||
ei_x_buff args; | ||
ei_x_new(&args); | ||
ei_x_encode_list_header(&args, 2); | ||
ei_x_encode_atom(&args, level_to_str(level)); | ||
ei_x_encode_binary(&args, log_buffer, log_length); | ||
ei_x_encode_empty_list(&args); | ||
|
||
if (!ei_client_call("Elixir.BACNet", "ei_log", &args, &out)) { | ||
char new_format[MAX_LOG_LENGTH]; | ||
snprintf(new_format, sizeof(new_format), "%s\n", format); | ||
vprintf(new_format, vargs); | ||
} | ||
|
||
ei_x_free(&out); | ||
ei_x_free(&args); | ||
va_end(vargs); | ||
} | ||
|
||
static const char *level_to_str(log_level_t level) | ||
{ | ||
switch (level) { | ||
case EMERGENCY: return "emergency"; | ||
case ALERT: return "alert"; | ||
case CRITICAL: return "critical"; | ||
case ERROR: return "error"; | ||
case WARNING: return "warning"; | ||
case NOTICE: return "notice"; | ||
case INFO: return "info"; | ||
case DEBUG: return "debug"; | ||
} | ||
} |
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,26 @@ | ||
#ifndef EI_LOG_H | ||
#define EI_LOG_H | ||
|
||
#define LOG_EMERGENCY(format, ...) ei_log(EMERGENCY, format, ##__VA_ARGS__) | ||
#define LOG_ALERT(format, ...) ei_log(ALERT, format, ##__VA_ARGS__) | ||
#define LOG_CRITICAL(format, ...) ei_log(CRITICAL, format, ##__VA_ARGS__) | ||
#define LOG_ERROR(format, ...) ei_log(ERROR, format, ##__VA_ARGS__) | ||
#define LOG_WARNING(format, ...) ei_log(WARNING, format, ##__VA_ARGS__) | ||
#define LOG_NOTICE(format, ...) ei_log(NOTICE, format, ##__VA_ARGS__) | ||
#define LOG_INFO(format, ...) ei_log(INFO, format, ##__VA_ARGS__) | ||
#define LOG_DEBUG(format, ...) ei_log(DEBUG, format, ##__VA_ARGS__) | ||
|
||
typedef enum { | ||
EMERGENCY, | ||
ALERT, | ||
CRITICAL, | ||
ERROR, | ||
WARNING, | ||
NOTICE, | ||
INFO, | ||
DEBUG, | ||
} log_level_t; | ||
|
||
void ei_log(log_level_t level, const char *format, ...); | ||
|
||
#endif /* EI_LOG_H */ |
Oops, something went wrong.