-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add basic c-node and supporting services #2
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
be91bc0
Add basic c-node and supporting services
abelino 5fd91c6
Register c-node globally and remove no-longer needed services
abelino ad175d4
Add support for handling responses to gen_server calls
abelino 797e765
Bacnet modules doesn't need to be a GenServer
abelino fcbd082
Rename Bacnet module namespace as BACNet and improve style
abelino File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
⛔ (required)
The second arg is the log message as a string, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It currently is returning a string, but nothing stopping us from this being able to return a
term
, i.e. structured data.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅ Oh, well that's cool.